⚗️ Web3
Web3.swift是一个用于Ethereum网络中进行交易签名与智能合约交互的Swift库。
它允许您连接到geth或parity以太坊节点(如Infura),发送交易并从智能合约中读取值,无需编写自己的协议实现。
Web3.swift支持iOS、macOS、tvOS和watchOS(通过CocoaPods和Carthage)以及macOS和Linux(通过Swift Package Manager)。
示例
检查下面的用法或查看仓库中的测试。
为什么选择它?
已经有几个用Swift编写的Web3库了。我们知道它们的优缺点,但针对我们的使用场景,它们并没有很好地工作。
Web3.swift
是考虑到模块化、可移植性、速度和效率构建的。
好的,感谢这些关键词。但这实际上意味着什么?
💾 模块化
Web3.swift
被构建为模块化的。如果您安装/使用基本的Web3
subspec/SPM产品,您将获得访问交易签名和与HTTP RPC服务器交互等最基本功能。
如果您想要支持IPC rpc或其他任何功能,您只需创建一个依赖于Web3
的库并实现此确切功能。
如果您想使用为web3调用提供的PromiseKit扩展,您可以使用提供的PromiseKit subspec/SPM产品或创建自己的。
如果您想方便地解析Ethereum智能合约的JSON ABIs,您可以使用提供的ABI解析subspec/SPM产品。
最后,如果您想向Web3.swift
添加尚未提供的功能,您无需等待它合并到版本中并释放。您可以直接在自己的应用程序中扩展/更新功能,因为我们的API是为了易于更改而构建的。
例如,如果您想添加一个由 Web3.swift
(我们只支持由 Infura 支持的方法)尚未提供的方法,您只需要添加一些3行代码(根据方法的输入和输出参数而定)。添加 IPC rpc 支持,只需实现一个协议和回答请求。
就像您看到的,使用 Web3.swift
一切皆有可能。
💻 便携性
我们开始这个项目的主要原因是,我们希望在不同平台上使用它与 CocoaPods 和 Swift Package Manager。
因此,Web3.swift
可通过 CocoaPods、Carthage 和 Swift Package Manager 在 iOS、macOS、tvOS、watchOS(通过 CocoaPods 和 Carthage)以及 macOS 和 Linux(通过 SPM)上使用。
注意:对于 SPM,我们只测试了 macOS 和官方支持的 Linux 发行版(目前为 Ubuntu 14.04 和 16.04),但它应与所有能够编译 Swift 编译器、Foundation 和 Glibc 的小端系统兼容。
⚡️ 速度与效率
我们试图让这个库尽可能快,同时尝试提供一个 API,它可以提高您的开发工作流程,使您能夢单页集中精力构建出色的 DAPPS 而不是担心实现细节。
所有我们的 API 都是线程安全的,并设计用于在高并发应用中使用。
安装
CocoaPods
Web3 通过 CocoaPods 提供。要安装它,只需将以下行添加到您的 Podfile
中
pod 'Web3'
如果您想使用 PromiseKit 扩展,也请将以下行添加到您的 Podfile
中
pod 'Web3/PromiseKit'
如果您想使用 ContractABI 模块,该模块可通过调用其函数和自动解析其输出来简化与智能合约的交互,也请将以下行添加到您的 Podfile
中
pod 'Web3/ContractABI'
Carthage
Web3 与 Carthage 兼容,Carthage 是一种去中心化的依赖管理器,用于构建依赖关系并提供二进制的框架。要安装它,只需将以下行添加到您的 Cartfile
中
github "Boilertalk/Web3.swift"
您还将需要安装依赖关系,这些可以在我们的 Cartfile 中找到。
Swift 包管理器
Web3 与 Swift 包管理器 v4(Swift 4 及以上版本)兼容。只需将其添加到您的 Package.swift
文件中的依赖项。
dependencies: [
.package(url: "https://github.com/Boilertalk/Web3.swift.git", from: "0.4.0")
]
然后将其添加到您的目标依赖项
targets: [
.target(
name: "MyProject",
dependencies: ["Web3", "Web3PromiseKit", "Web3ContractABI"]),
.testTarget(
name: "MyProjectTests",
dependencies: ["MyProject"])
]
注意:
Web3PromiseKit
和Web3ContractABI
是可选的,只有当您想使用它们时,才需要将它们放入您的目标依赖项(并且在稍后导入)。
安装完成后,您可以在您的 .swift
文件中导入 Web3
。
import Web3
使用方法
与以太坊节点的交互
使用 Web3.swift
,您可以在服务器上使用以太坊节点与以太坊进行通信。
您可以使用它发送已签名的事务、读取合约数据、调用合约函数等等。
所有方法的基类是 Web3
。例如,您可以使用 http 提供器实例化它
let web3 = Web3(rpcURL: "https://mainnet.infura.io/<your_infura_id>")
所有 web3_
方法都直接来自 Web3
结构。 net_
方法在 web3
结构的 net
结构中可用。 eth_
方法在 web3
结构的 eth
结构中可用。
请参考下面的示例
注意:要让示例工作,您需要首先导入 Web3 和 PromiseKit
请求 web3_clientVersion
返回当前客户端版本。
参数
无
返回
String
- 当前客户端版本
firstly {
web3.clientVersion()
}.done { version in
print(version)
}.catch { error in
print("Error")
}
请求 net_version
返回当前网络 ID。
参数
无
返回
String
- 当前网络 ID
firstly {
web3.net.version()
}.done { version in
print(version)
}.catch { error in
print("Error")
}
请求 net_PeerCount
返回当前连接到客户端的节点数量。
参数
无
返回
EthereumQuantity
- 连接节点的数量 BigInt。
firstly {
web3.net.peerCount()
}.done { ethereumQuantity in
print(ethereumQuantity.quantity)
}.catch { error in
print("Error")
}
发送原始交易
为签名交易创建新的消息调用交易或合约创建交易。
参数
EthereumTransaction
: 签名交易
返回
EthereumData
, 32 字节 - 交易哈希,如果在交易不可用的情况下为空哈希。
要发送一些 ETH,首先需要获取发送者的当前交易计数 (nonce),创建交易,签名然后发送。
let privateKey = try! EthereumPrivateKey(hexPrivateKey: "0xa26da69ed1df3ba4bb2a231d506b711eace012f1bd2571dfbfff9650b03375af")
firstly {
web3.eth.getTransactionCount(address: privateKey.address, block: .latest)
}.then { nonce in
let tx = try EthereumTransaction(
nonce: nonce,
gasPrice: EthereumQuantity(quantity: 21.gwei),
gas: 21000,
to: EthereumAddress(hex: "0xC0866A1a0ed41e1aa75c932cA3c55fad847fd90D", eip55: true),
value: EthereumQuantity(quantity: 1.eth)
)
return try tx.sign(with: privateKey, chainId: 1).promise
}.then { tx in
web3.eth.sendRawTransaction(transaction: tx)
}.done { hash in
print(hash)
}.catch { error in
print(error)
}
按区块编号请求区块交易计数
firstly {
web3.eth.getBlockTransactionCountByNumber(block: .block(5397389))
}.done { count in
print(count) // 88
}.catch { error in
print(error)
}
更多信息
更多示例,请参阅 我们的测试用例,Web3 结构或官方 Ethereum JSON RPC 文档。
智能合约 ABI 交互
我们提供一个可选模块用于与智能合约交互。使用它,您需要将相应的 subspec 添加到您的 Podfile 中(对于 Cococapods),或者在 Podfile 中添加 Web3ContractABI
到您的目标依赖项中(对于 SPM)。请确保首先查看安装说明。
我们提供两种不同的选项来在Swift中创建合同abi接口。您可以手动定义您的函数和事件(或使用我们提供的接口之一,如ERC20或ERC721)。或者像在web3.js中一样,从JSON ABI表示中解析它们。
静态合约
静态合约是实现StaticContract
类。它们提供一组希望从原始智能合同中使用的函数和事件。以下是我们提供的静态合约(以ERC20或ERC721为例)作为起点。
我们的静态ERC20接口称为GenericERC20Contract
,ERC721合同称为GenericERC721Contract
。它们都可以继承以添加更多自定义合同的功能。
有了这些StaticContract
类型,您可以像以下示例(我们在示例中使用PromiseKit)中那样创建和使用您的合同。
let web3 = Web3(rpcURL: "https://mainnet.infura.io/<your_infura_id>")
let contractAddress = try EthereumAddress(hex: "0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0", eip55: true)
let contract = web3.eth.Contract(type: GenericERC20Contract.self, address: contractAddress)
// Get balance of some address
firstly {
try contract.balanceOf(address: EthereumAddress(hex: "0x3edB3b95DDe29580FFC04b46A68a31dD46106a4a", eip55: true)).call()
}.done { outputs in
print(outputs["_balance"] as? BigUInt)
}.catch { error in
print(error)
}
// Send some tokens to another address (locally signing the transaction)
let myPrivateKey = try EthereumPrivateKey(hexPrivateKey: "...")
firstly {
web3.eth.getTransactionCount(address: myPrivateKey.address, block: .latest)
}.then { nonce in
try contract.transfer(to: EthereumAddress(hex: "0x3edB3b95DDe29580FFC04b46A68a31dD46106a4a", eip55: true), value: 100000).createTransaction(
nonce: nonce,
from: myPrivateKey.address,
value: 0,
gas: 100000,
gasPrice: EthereumQuantity(quantity: 21.gwei)
)!.sign(with: myPrivateKey).promise
}.then { tx in
web3.eth.sendRawTransaction(transaction: tx)
}.done { txHash in
print(txHash)
}.catch { error in
print(error)
}
// Send some tokens to another address (signing will be done by the node)
let myAddress = try EthereumAddress(hex: "0x1f04ef7263804fafb839f0d04e2b5a6a1a57dc60", eip55: true)
firstly {
web3.eth.getTransactionCount(address: myAddress, block: .latest)
}.then { nonce in
try contract.transfer(to: EthereumAddress(hex: "0x3edB3b95DDe29580FFC04b46A68a31dD46106a4a", eip55: true), value: 100000).send(
nonce: nonce,
from: myAddress,
value: 0,
gas: 150000,
gasPrice: EthereumQuantity(quantity: 21.gwei)
)
}.done { txHash in
print(txHash)
}.catch { error in
print(error)
}
通过创建自己的接口,您可以与任何智能合约进行交互!
动态合约
如果您只能访问智能合同的JSON ABI,或者您不想创建静态模板,则可以使用我们的动态合同api将json字符串解析为可用的合同在运行时。请看下面的示例。
let web3 = Web3(rpcURL: "https://mainnet.infura.io/<your_infura_id>")
let contractAddress = try EthereumAddress(hex: "0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0", eip55: true)
let contractJsonABI = "<your contract ABI as a JSON string>".data(using: .utf8)!
// You can optionally pass an abiKey param if the actual abi is nested and not the top level element of the json
let contract = try web3.eth.Contract(json: contractJsonABI, abiKey: nil, address: contractAddress)
print(contract.methods.count)
// Get balance of some address
firstly {
try contract["balanceOf"]!(EthereumAddress(hex: "0x3edB3b95DDe29580FFC04b46A68a31dD46106a4a", eip55: true)).call()
}.done { outputs in
print(outputs["_balance"] as? BigUInt)
}.catch { error in
print(error)
}
// Send some tokens to another address (locally signing the transaction)
let myPrivateKey = try EthereumPrivateKey(hexPrivateKey: "...")
guard let transaction = contract["transfer"]?(EthereumAddress.testAddress, BigUInt(100000)).createTransaction(nonce: 0, from: myPrivateKey.address, value: 0, gas: 150000, gasPrice: EthereumQuantity(quantity: 21.gwei)) else {
return
}
let signedTx = try transaction.sign(with: myPrivateKey)
firstly {
web3.eth.sendRawTransaction(transaction: signedTx)
}.done { txHash in
print(txHash)
}.catch { error in
print(error)
}
使用此API,您可以在以太坊网络中的任何智能合约进行交互!
有关更多信息,包括合同创建(调用构造函数)的示例,请查看我们的测试。
免责声明
直到我们达到1.0.0版本,我们的API在次要版本跳跃之间可能会发生破坏性更改。这是为了保证我们可以在剧烈开发期间集中精力提供最佳实现,而不是尝试维护老旧的代码。
话虽如此,我们会尽量减少破坏性更改。很可能不会有很多。
作者
Boilertalk的精彩团队成员
以及来自社区的更多精彩成员
查看贡献者列表以获取完整列表。
授权
Web3遵循MIT授权协议。更多详情请参阅LICENSE文件。