Web3.swift 是一个 Swift 库,用于在以太坊网络中签名交易并与智能合约交互。
它允许您连接到 geth 或 erigon 以太坊节点(例如 Chainnodes),从而发送交易并从智能合约读取值,而无需编写您自己的协议实现。
Web3.swift 通过 Swift Package Manager 支持 iOS、macOS、tvOS、watchOS 和 Linux。
请查看下面的用法或浏览存储库的测试。
目前已经有一些用 Swift 编写的 Web3 库。 我们知道它们的优点和缺点,但就我们的使用场景而言,它们并不能满足需求。
Web3.swift
的构建考虑了模块化、可移植性、速度和效率。
好的,谢谢你的流行语。 但这实际上意味着什么?
Web3.swift
的构建是模块化的。 如果您安装/使用基本的 Web3
SPM 产品,您将可以访问最基本的功能,例如交易签名以及与 http rpc 服务器的交互。
如果您想添加对 IPC rpc 或其他内容的支持,您可以简单地创建一个依赖于 Web3
并实现此确切功能的库。 稍后会详细介绍。
如果您想为 web3 调用使用 PromiseKit 扩展,您可以选择使用提供的 PromiseKit SPM 产品或创建自己的产品。
如果您想方便地解析以太坊智能合约的 JSON ABI,您可以使用提供的 ABI 解析 SPM 产品。
最后,如果您想向 Web3.swift
添加尚未提供的功能,您不必等到它合并并在版本更新中发布。 您可以简单地在您自己的应用程序中扩展/更新功能,因为我们的 API 被设计为对更改非常开放。
例如,如果您想添加 Web3.swift
尚未提供的 web3 方法(我们仅支持 Infura 支持的方法),您只需添加 大约 3 行代码(取决于该方法的输入和输出参数)。 添加 IPC rpc 支持只需实现一个协议并响应请求即可。
就像您可以看到的那样,使用 Web3.swift
一切皆有可能。
我们开始从事这个项目的主要原因之一是因为我们想在不同的平台上使用 Swift Package Manager。
因此,可以通过 Swift Package Manager 在 iOS、macOS、tvOS、watchOS 和 Linux 上使用 Web3.swift
。
注意:对于 SPM,我们仅测试 iOS、macOS 和官方支持的 Linux 发行版(目前为 Ubuntu 16.04 和 20.04),但它应该与所有能够编译 Swift Compiler、Foundation 和 Glibc 的小端系统兼容。
我们尝试使此库尽可能快,同时尝试提供一个可以提高您的开发工作流程的 API,以便您可以专注于构建出色的 DAPP,而不必担心实现细节。
我们所有的 API 都是线程安全的,并且设计用于高度并发的应用程序。
Web3 兼容 Swift Package Manager v5(Swift 5 及以上版本)。 只需将其添加到您的 Package.swift
中的依赖项即可。
dependencies: [
.package(url: "https://github.com/Boilertalk/Web3.swift.git", from: "0.6.0")
]
然后将其添加到您的目标依赖项中
targets: [
.target(
name: "MyProject",
dependencies: [
.product(name: "Web3", package: "Web3.swift"),
.product(name: "Web3PromiseKit", package: "Web3.swift"),
.product(name: "Web3ContractABI", package: "Web3.swift"),
]
),
.testTarget(
name: "MyProjectTests",
dependencies: ["MyProject"])
]
注意:
Web3PromiseKit
和Web3ContractABI
是可选的,只有在您想使用它们时才需要将它们放入您的目标依赖项中(并在之后导入它们)。
安装完成后,您可以在 .swift
文件中导入 Web3
。
import Web3
// Optional
import Web3PromiseKit
import Web3ContractABI
由于内部决策,从 0.5.0 版本开始,我们停止支持 SPM 以外的任何 Package Manager。
为了详细说明这个决定:使用 XCode 11 和 Swift 5.1,我们通过 Swift Package Manager 达到了一个地步,它慢慢地开始使其他包管理器变得无关紧要。 您已经可以使用 Swift Package Manager 在 XCode 项目中加载所有依赖项。
随着更多更新,它变得更加普遍。 Cocoapods 和 Carthage 维护者对他们的项目失去了兴趣,并停止维护它。 存在许多未解决的问题,尤其对于使用 Cocoapods 的库开发人员而言存在许多问题。
付出这么多努力却没有任何实际收益。 用户已经可以将支持 SPM 的依赖项放入他们的 XCode 项目中。 那么何必麻烦呢?
答案很简单。 某些人仍在使用 XCode < 11,并且某些库开发人员在其自己的 Pod/Carthage 中依赖于 Web3。
该决定很艰难,并且花费了一些时间。 但是在看到最后一个版本非常稳定并且已经在许多生产应用程序中使用之后,我们决定现在开始采取这一举措。
XCode 10 已经有 2 年多的历史了。 大多数项目已经升级,那些没有升级的项目遇到的问题远不止 Web3.swift 没有为 Cocoapods 制作更新...
鼓励依赖 Web3.swift 的库所有者也放弃 Cocoapods 和 Carthage 支持。
SPM 是未来。 对于所有使用 Cocoapods 和 Carthage 的用户,因为许多库尚未切换到 SPM:您仍然可以将 Web3.swift 作为 SPM 产品添加到您的 .xcworkspace
或 .xcodeproj
中,并将所有其他依赖项保留在 Cocoapods/Carthage 中。 但仍然。 我们鼓励您尽可能多地切换到 SPM。 宜早不宜迟。 请参阅下一节,了解如何执行此操作。
使用 XCode 11 或更高版本(对于 iOS、macOS 或其他 Apple 平台),您可以非常轻松地添加 SPM 包。
在 Xcode 中,选择您的项目,从下拉菜单中选择项目,而不是单个目标,在选项卡中选择 Swift Packages
。
然后您可以单击 + 图标并放入此存储库的 URL (https://github.com/Boilertalk/Web3.swift)。
现在您可以选择所有产品,然后单击“下一步”直到添加了依赖项。
就这样。 如果您推送您的更改,即使您的 CI 也不会出现任何问题。 不必为过时的规范存储库而烦恼,也不必为仅在某些时候/某些依赖项中才会发生的一些奇怪的链接器错误而烦恼。
如果您需要进一步的指导,请加入我们的 Telegram 群组,我们将为您提供帮助。 https://t.me/web3_swift
使用 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
返回当前客户端版本。
参数
无
返回
String
- 当前客户端版本
firstly {
web3.clientVersion()
}.done { version in
print(version)
}.catch { error in
print("Error")
}
返回当前网络 ID。
参数
无
返回
String
- 当前网络 ID
firstly {
web3.net.version()
}.done { version in
print(version)
}.catch { error in
print("Error")
}
返回当前连接到客户端的对等节点数。
参数
无
返回
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 结构或 官方以太坊 JSON RPC 文档。
我们正在提供一个可选模块用于与智能合约交互。 要使用它,您必须将 Web3ContractABI
添加到 Podfile(对于 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,
gasPrice: EthereumQuantity(quantity: 21.gwei),
maxFeePerGas: nil,
maxPriorityFeePerGas: nil,
gasLimit: 100000,
from: myPrivateKey.address,
value: 0,
accessList: [:],
transactionType: .legacy
)!.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,
gasPrice: EthereumQuantity(quantity: 21.gwei),
maxFeePerGas: nil,
maxPriorityFeePerGas: nil,
gasLimit: 150000,
from: myAddress,
value: 0,
accessList: [:],
transactionType: .legacy
)
}.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,
gasPrice: EthereumQuantity(quantity: 21.gwei),
maxFeePerGas: nil,
maxPriorityFeePerGas: nil,
gasLimit: 150000,
from: myPrivateKey.address,
value: 0,
accessList: [:],
transactionType: .legacy
)) 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,您可以与以太坊网络中的任何智能合约进行交互!
有关更多示例,包括合约创建(构造函数调用),请查看我们的 测试。
如果您在从 json 解析 ABI 时收到此错误,可能是因为您的合约有一个回退函数。 要解决此问题,请删除 ABI 中包含有关回退函数信息的片段。 您应该删除的部分可能如下所示
{
"payable": false,
"stateMutability": "nonpayable",
"type": "fallback"
},
在我们达到 1.0.0 版本之前,我们的 API 会在小版本跳跃之间发生重大更改。 这是为了确保我们能够专注于提供最佳实现,同时我们正在进行大量开发,而不是尝试维护一些已弃用的内容。
话虽如此,我们将尽量减少重大更改。 当然不会有很多。
Boilertalk 的那些很棒的家伙 ⚗️
...还有来自社区的更棒的成员 💜
查看 贡献者列表 以获取完整列表。
Web3 在 MIT 许可证下可用。 有关更多信息,请参见 LICENSE 文件。