Use Xcode to add to the project (File -> Swift Packages) or add this to your Package.swift
file:
.package(url: "https://github.com/argentlabs/web3.swift", from: "1.1.0")
Add web3.swift to your Podfile
:
pod 'web3.swift'
Then run the following command:
$ pod install
Create an instance of EthereumAccount
with a EthereumKeyStorage
provider. This provides a wrapper around your key for web3.swift to use. NOTE We recommend you implement your own KeyStorage provider, instead of relying on the provided EthereumKeyLocalStorage
class. This is provided as an example for conformity to the EthereumSingleKeyStorageProtocol
.
import web3
// This is just an example. EthereumKeyLocalStorage should not be used in production code
let keyStorage = EthereumKeyLocalStorage()
let account = try? EthereumAccount.create(replacing: keyStorage, keystorePassword: "MY_PASSWORD")
Create an instance of EthereumHttpClient
or EthereumWebSocketClient
. This will then provide you access to a set of functions for interacting with the Blockchain.
EthereumHttpClient
guard let clientUrl = URL(string: "https://an-infura-or-similar-url.com/123") else { return }
let client = EthereumHttpClient(url: clientUrl)
OR
EthereumWebSocketClient
guard let clientUrl = URL(string: "wss://goerli.infura.io/ws/v3//123") else { return }
let client = EthereumWebSocketClient(url: clientUrl)
You can then interact with the client methods, such as to get the current gas price:
client.eth_gasPrice { (error, currentPrice) in
print("The current gas price is \(currentPrice)")
}
If using async/await
you can await
on the result
let gasPrice = try await client.eth_gasPrice()
Given a smart contract function ABI like ERC20 transfer
:
function transfer(address recipient, uint256 amount) public returns (bool)
then you can define an ABIFunction
with corresponding encodable Swift types like so:
public struct Transfer: ABIFunction {
public static let name = "transfer"
public let gasPrice: BigUInt? = nil
public let gasLimit: BigUInt? = nil
public var contract: EthereumAddress
public let from: EthereumAddress?
public let to: EthereumAddress
public let value: BigUInt
public init(contract: EthereumAddress,
from: EthereumAddress? = nil,
to: EthereumAddress,
value: BigUInt) {
self.contract = contract
self.from = from
self.to = to
self.value = value
}
public func encode(to encoder: ABIFunctionEncoder) throws {
try encoder.encode(to)
try encoder.encode(value)
}
}
This function can be used to generate contract call transactions to send with the client:
let function = transfer(contract: "0xtokenaddress", from: "0xfrom", to: "0xto", value: 100)
let transaction = try function.transaction()
client.eth_sendRawTransaction(transaction, withAccount: account) { (error, txHash) in
print("TX Hash: \(txHash)")
}
If using async/await
you can await
on the result
let txHash = try await client.eth_sendRawTransaction(transaction, withAccount: account)
The library provides some types and helpers to make interacting with web3 and Ethereum easier.
EthereumAddress
: For representation of addresses, including checksum support.BigInt
andBigUInt
: Using BigInt libraryEthereumBlock
: Represents the block, either number of RPC-specific definitions like 'Earliest' or 'Latest'EthereumTransaction
: Wraps a transaction. Encoders and decoders can work with it to generate properdata
fields.
All extensions are namespaced under ''.web3. So for example, to convert an Int
to a hex string:
let gwei = 100
let hexgwei = gwei.web3.hexString
Supported conversions:
- Convert from hex byte string ("0xabc") to
Data
- Convert from hex byte string ("0xabc") to
Int
- Convert from hex byte string ("0xabc") to
BigUInt
- Convert
String
,Int
,BigUInt
,Data
to a hex byte string ("0xabc") - Add or remove hex prefixes when working with
String
We support querying ERC20 token data via the ERC20
struct. Calls allow to:
- Get the token symbol, name, and decimals
- Get a token balance
- Retrieve
Transfer
events
We support querying ERC721 token data via the ERC721
struct. Including:
- Get the token symbol, name, and decimals
- Get a token balance
- Retrieve
Transfer
events - Decode standard JSON for NFT metadata. Please be aware some smart contracts are not 100% compliant with standard.
The tests will all pass when running against Goerli. You will need to provide a URL for the blockchain proxy (e.g. on Infura), and a key-pair in TestConfig.swift
. Some of the account signing tests will fail, given the signature assertions are against a specific (unprovided) key.
We built web3.swift to be as lightweight as possible. However, given the cryptographic nature of Ethereum, there's a couple of reliable C libraries you will find packaged with this framework:
- keccac-tiny: An implementation of the FIPS-202-defined SHA-3 and SHAKE functions in 120 cloc (156 lines).
- Tiny AES: A small and portable implementation of the AES ECB, CTR and CBC encryption algorithms.
- secp256k1.swift
We also use Apple's own CommonCrypto (via this method) and BigInt via CocoaPod dependency.
There are some features that have yet to be fully implemented! Not every RPC method is currently supported, and here's some other suggestions we would like to see in the future:
- Batch support for JSONRPC interface
- Use a Hex struct for values to be more explicit in expected types
- Use Truffle for running tests
- Bloom Filter support
The initial project was crafted by the team at Argent. However, we encourage anyone to help implement new features and to keep this library up-to-date. For features and fixes, simply submit a pull request to the develop branch. Please follow the contributing guidelines.
For bug reports and feature requests, please open an issue.
Released under the MIT license.