Swiftpack.co - skywinder/web3swift as Swift Package

Swiftpack.co is a collection of thousands of indexed Swift packages. Search packages.
See all packages published by skywinder.
skywinder/web3swift 3.0.0
Elegant Web3js functionality in Swift. Native ABI parsing and smart contract interactions.
⭐️ 622
🕓 1 week ago
iOS macOS
.package(url: "https://github.com/skywinder/web3swift.git", from: "3.0.0")

web3swift

web3swift is an iOS toolbelt for interaction with the Ethereum network.

Social medias

Join our discord or Telegram if you need a support or want to contribute to web3swift development!

matter-github-swift Web3swift CI Swift Platform CocoaPods Compatible Carthage Compatible License support Stackoverflow


Core features

  • ☑ :zap: Swift implementation of web3.js functionality
  • ☑ :thought_balloon: Interaction with remote node via JSON RPC
  • ☑ 🔐 Local keystore management (geth compatible)
  • ☑ 🤖 Smart-contract ABI parsing
  • ☑ 🔓ABI deconding (V2 is supported with return of structures from public functions. Part of 0.4.22 Solidity compiler)
  • ☑ 🕸Ethereum Name Service (ENS) support - a secure & decentralised way to address resources both on and off the blockchain using simple, human-readable names
  • ☑ :arrows_counterclockwise: Smart contracts interactions (read/write)
  • ☑ ⛩ Infura support
  • ☑ ⚒ Parsing TxPool content into native values (ethereum addresses and transactions) - easy to get pending transactions
  • ☑ 🖇 Event loops functionality
  • ☑ 🕵️‍♂️ Possibility to add or remove "middleware" that intercepts, modifies and even cancel transaction workflow on stages "before assembly", "after assembly"and "before submission"
  • ☑ ✅Literally following the standards (BIP, EIP, etc):
    • BIP32 (HD Wallets), BIP39 (Seed phrases), BIP44 (Key generation prefixes)
  • EIP-20 (Standart interface for tokens - ERC-20), EIP-67 (Standard URI scheme), EIP-155 (Replay attacks protection), EIP-2718 (Typed Transaction Envelope), EIP-1559 (Gas Fee market change)
    • And many others (For details about this EIP's look at Documentation page): EIP-681, EIP-721, EIP-165, EIP-777, EIP-820, EIP-888, EIP-1400, EIP-1410, EIP-1594, EIP-1643, EIP-1644, EIP-1633, EIP-721, EIP-1155, EIP-1376, ST-20
  • RLP encoding
  • ☑ Base58 encoding scheme
  • ☑ Formatting to and from Ethereum Units
  • ☑ Comprehensive Unit and Integration Test Coverage

Installation

CocoaPods

CocoaPods is a dependency manager for Cocoa projects. You can install it with the following command:

$ sudo gem install cocoapods

To integrate web3swift into your Xcode project using CocoaPods, specify it in your Podfile:

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '13.0'

target '<Your Target Name>' do
    use_frameworks!
    pod 'web3swift'
end

Then, run the following command:

$ pod install

Swift Package

The Swift Package Manager is a tool for automating the distribution of Swift code and is integrated into the swift compiler.

Once you have your Swift package set up, adding Alamofire as a dependency is as easy as adding it to the dependencies value of your Package.swift.

dependencies: [
    .package(url: "https://github.com/skywinder/web3swift.git", .upToNextMajor(from: "3.0.0"))
]

Example usage

In the imports section:

import web3swift
import Core

Send Ether

let transaction: CodableTransaction = .emptyTransaction
transaction.from = from ?? transaction.sender // `sender` one is if you have private key of your wallet address, so public key e.g. your wallet address could be infereted
transaction.value = value
transaction.gasLimitPolicy = .manual(78423)
transaction.gasPricePolicy = .manual(20000000000)
web3.eth.send(transaction)

Contract read method

let contract = web3.contract(Web3.Utils.erc20ABI, at: receipt.contractAddress!)!
let readOp = contract.createReadOperation("name")!
readOp.transaction.from = EthereumAddress("0xe22b8979739D724343bd002F9f432F5990879901")
let response = try await readTX.callContractMethod()

Write Transaction and call smart contract method

let abiString = "[]" // some ABI string
let bytecode = Data.fromHex("") // some ABI bite sequence
let contract = web3.contract(abiString, at: nil, abiVersion: 2)!
let parameters = [...] as [AnyObject]
let deployOp = contract.prepareDeploy(bytecode: bytecode, constructor: contract.contract.constructor, parameters: parameters)!
deployOp.transaction.from = "" // your address
deployOp.transaction.gasLimitPolicy = .manual(3000000)
let result = try await deployTx.writeToChain(password: "web3swift")

Sending network request to a node

func feeHistory(blockCount: UInt, block: BlockNumber, percentiles:[Double]) async throws -> Web3.Oracle.FeeHistory {
    let requestCall: APIRequest = .feeHistory(blockCount, block, percentiles)
    let response: APIResponse<Web3.Oracle.FeeHistory> = try await APIRequest.sendRequest(with: web3.provider, for: requestCall) /// explicitly declaring `Result` type is **required**.
    return response.result
}

Build from source

SPM

git clone https://github.com/skywinder/web3swift.git
cd web3swift
swift build

Requirements

  • iOS 13.0 / macOS 10.15
  • Xcode 12.5
  • Swift 5.5

Documentation

Documentation is under construction👷🏻👷🏼‍♀️. We’re trying our best to comment all public API as detailed as we can, but the end it still far to come. But in one of the nearest minor updates we’ll bring DocC support of already done amount of docs. And your PR in such are more than welcome.

Projects that are using web3swift

Please take a look at Our costumers wiki page.

Support

Join our discord and Telegram if you need a support or want to contribute to web3swift development!

Contribute

Want to improve? It's awesome: Then good news for you: We are ready to pay for your contribution via @gitcoin bot!

Contribution

  1. You are more than welcome to participate and get bounty by contributing! Your contribution will be paid via @gitcoin Grant program.
  2. Find or create an issue
  3. You can find open bounties in Gitcoin Bounties list
  4. Commita fix or a new feature in branch, push your changes
  5. Submit a pull request to develop branch
    1. Please, provide detailed description to it to help us proceed it faster.

@skywinder are charged with open-sourсe and do not require money for using web3swift library. We want to continue to do everything we can to move the needle forward.

Credits

Security Disclosure

If you believe you have identified a security vulnerability with web3swift, you should report it as soon as possible via email to [email protected]. Please do not post it to a public issue tracker.

License

web3swift is available under the Apache License 2.0 license. See the LICENSE for details.

GitHub

link
Stars: 622
Last commit: Yesterday
jonrohan Something's broken? Yell at me @ptrpavlik. Praise and feedback (and money) is also welcome.

Release Notes

3.0.0
1 week ago

The whole new way to do Etherum on swift

TLDR

This release changes whole way of iterating with Etherum chain in a terms of creating, preparing, sending transaction and receiving receipt on it. Now it’s:

  • unified — only one struct CodableTransaction to handle
  • consistent — no more transaction to transaction merging, just create one, configure it, and assign it to [Read|WriteOperation].
  • clear — intention of all types and methods are obvious by their names, most of them are generic based, so no more casting to and out of Any.
  • less error prone — we’ve cleared so much legacy code in that pipeline which only God know what it does, so now seeing source of a method of your interess is a breath.

Unified

Previously there were four (!) types that is being used to prepare and send transaction:

  • EthereumTransaction
  • TransactionParameters
  • EthereumParameters
  • TransactionOptions

Now their all gone and there’s only one to have: CodableTransaction, this one covers all previous usecases, because it’s actually just all in one merged yet.

Unfortunately it’s not as clean as i wish it would be, but despite hurting my sense of perfection it’s much, much more better way to work with that it was before.

So now to create transaction that you would send to a node further you sould just do the follow:

let transaction: CodableTransaction = .emptyTransaction
transaction.from = from ?? transaction.sender // `sender` one is if you have private key of your wallet address, so public key e.g. your wallet address could be infereted
transaction.value = value
transaction.gasLimitPolicy = .manual(78423)
transaction.gasPricePolicy = .manual(20000000000)
Web3.eth.send(transaction)

CodableTransaction overview

So let’s take a look at CodableTransaction most important properties closer:

  1. Full access { get set }:
    • from: EthereumAddress? — from property to fill it where it needs to.
    • to: EthereumAddress — adressee of a transaction, the only required property on membervise initnalizaion.
    • type: TransactionType — Type of transaction in Ethereum mainnet mean, 0 — legacy, 1 — post EIP-2930, 2 — post EIP-1559.
    • chainID: BigUInt? — The chainId that transaction is targeted for.
    • value: BigUInt — The native value of a transaction.
    • data: Data — data property of a trancaction, most used whilist contract iterating.
    • callOnBlock: BlockNumber? — Block number to call a given transaction on.
    • accessList: [AccessListEntry]? — access list for contract execution (EIP-2930 and EIP-1559 only).
  2. Indirect properties { get set } (could be changed in release):
    1. Nonce — the nonce for the transaction
      • nonce: BigUInt { get }
      • noncePolicy: NoncePolicy { set }
    2. GasLimit — the max number of gas units allowed to process this transaction
      • gasLimit: BigUInt { get }
      • gasLimitPolicy: GasLimitPolicy { set }
    3. GasPrice — the price per gas unit for the tranaction (Legacy and EIP-2930 only)
      • gasPrice: BigUInt? { get }
      • gasPricePolicy: GasPricePolicy { set }
    4. MaxFeePerGas — the max base fee per gas unit (EIP-1559 only)
      • maxFeePerGas: BigUInt? { get }
      • maxFeePerGasPolicy: FeePerGasPolicy { set }
    5. MaxPriorityFeePerGas — the maximum tip to pay the miner (EIP-1559 only)
      • maxPriorityFeePerGas: BigUInt? { get }
      • maxPriorityFeePerGasPolicy: FeePerGasPolicy { set }
  3. Computed properties { get }:
    • meta: EthereumMetadata? — this one provides some information about already executed transaction, such is blockHash, transactionIndex, gasprice and some related.
    • sender: EthereumAddress? — the address of the sender of the transaction recovered from the signature.
    • hash: Data? — A transaction hash.
    • Sgnature data components:
      • v: BigUInt — signature v component.
      • r: BigUInt — signature r component.
      • s: BigUInt — signature s component.

Consistent

Second big chunk of improvement has happened with [Read|Write]Transaction types. So this is actually the general type to iterate with contracts. While doing so you’ve constantly creating and passing this and there this struct, which is encapsulate in itself all contract related data (contract object, method that you willing to call, transaction to be sent to). So this those structs are now renamed into [Read|Write]Operation respectively, so as their methods. Dispite that all pipeline is kept the same, so to make some read contract call you have to do follow:

let contract = Web3.contract(Web3.Utils.erc20ABI, at: receipt.contractAddress!)!
let readOp = contract.createReadOperation("name")!
readOp.transaction.from = EthereumAddress("0xe22b8979739D724343bd002F9f432F5990879901")
let response = try await readTX.callContractMethod()

and for write call follow:

let abiString = "[]" // some ABI string
let bytecode = Data.fromHex("") // some ABI bite sequence
let contract = Web3.contract(abiString, at: nil, abiVersion: 2)!
let parameters = [...] as [AnyObject]
let deployOp = contract.prepareDeploy(bytecode: bytecode, constructor: contract.contract.constructor, parameters: parameters)!
deployOp.transaction.from = "" // your address
deployOp.transaction.gasLimitPolicy = .manual(3000000)
let result = try await deployTx.writeToChain(password: "web3swift")

Clarity

You haven’t look under the hood before, but i did, and it was scary one. It suddenly appears that resolving and managing nothing, but five objects to send something peace of bite a chain is not as easy as it might be thoughts, so there was a mess in both method intents and their implementation.

Transaction pipeline

Now most of it’s gone, and the rule number one: You should not merge any transaction by yourself.

  1. If you want to prepare you call for a given contract method on a contract pipeline stage there’s CodableTransaction property in it.
  2. If you want to prepare such on a operation stage there’s CodableTransaction property in it too.

Both these properties named transaction and one assigns another, when Contract creates appropriate Operation to be sent.

So thil all leads to rule number two: You should work to any you would like, but only one transaction object. Please pay attention to it, it’s still possible to passing your transaction all around your code while filling it in, but it’s strongly not recomendet. The better way is to choose one place where CodableTransaction instance would be filled and implement utility methods that filled it in with appropriate data, like BigUInt for gasWhatever and BlockNumber for callOnBlock properties.

Network layer

There’s totally new network layer API. It based on generic implementation and on some cascade of protocols. Let’s take a closer look in it.

How to

Using new API is as easy as write three line of a code:

func feeHistory(blockCount: UInt, block: BlockNumber, percentiles:[Double]) async throws -> Web3.Oracle.FeeHistory {
    let requestCall: APIRequest = .feeHistory(blockCount, block, percentiles)
    let response: APIResponse<Web3.Oracle.FeeHistory> = try await APIRequest.sendRequest(with: Web3.provider, for: requestCall) /// explicitly declaring `Result` type is **required**.
    return response.result
}
  1. On the first line you’re creating a request where passing all required and strictly typed parameters.
  2. On a second you’re both declaring expected Result type and making a network request.
  3. On the third one you’re reaching result itself.

And that’s it, you’re done here.

Error prone

Thanks to @mliot`s work it’s under the hood transaction signing/decoding with respect to its type.

So actually you don’t need to feel data property by yourself in most of cases, because it happening itself and it happening on WriteOperation.writeToChain and ReadOperation.callContractMethod e.g. on the very end of the whole pipeline[^1].

Recap

So let’s recap new available API pipelines.

Native transaction send

let transaction: CodableTransaction = .emptyTransaction
transaction.from = from ?? transaction.sender // `sender` one is if you have private key of your wallet address, so public key e.g. your wallet address could be infereted
transaction.value = value
transaction.gasLimitPolicy = .manual(78423)
transaction.gasPricePolicy = .manual(20000000000)
Web3.eth.send(transaction)

Contract read method call

let contract = Web3.contract(Web3.Utils.erc20ABI, at: receipt.contractAddress!)!
let readOp = contract.createReadOperation("name")!
readOp.transaction.from = EthereumAddress("0xe22b8979739D724343bd002F9f432F5990879901")
let response = try await readTX.callContractMethod()

Contract write on chain method call

let abiString = "[]" // some ABI string
let bytecode = Data.fromHex("") // some ABI bite sequence
let contract = Web3.contract(abiString, at: nil, abiVersion: 2)!
let parameters = [...] as [AnyObject]
let deployOp = contract.prepareDeploy(bytecode: bytecode, constructor: contract.contract.constructor, parameters: parameters)!
deployOp.transaction.from = "" // your address
deployOp.transaction.gasLimitPolicy = .manual(3000000)
let result = try await deployTx.writeToChain(password: "web3swift")

Sending network request to a node

func feeHistory(blockCount: UInt, block: BlockNumber, percentiles:[Double]) async throws -> Web3.Oracle.FeeHistory {
    let requestCall: APIRequest = .feeHistory(blockCount, block, percentiles)
    let response: APIResponse<Web3.Oracle.FeeHistory> = try await APIRequest.sendRequest(with: Web3.provider, for: requestCall) /// explicitly declaring `Result` type is **required**.
    return response.result
}

[^1]: This is actually not a whole true, because in that stage it’s just all gas related properties gets resolved.

Swiftpack is being maintained by Petr Pavlik | @ptrpavlik | @swiftpackco | API | Analytics