9 个版本 (破坏性更新)
0.8.0 | 2024年1月29日 |
---|---|
0.6.1 | 2023年11月20日 |
0.5.0 | 2023年6月29日 |
0.4.1 | 2023年5月19日 |
0.1.0 | 2023年4月11日 |
#516 in 魔法豆
39KB
744 行
ink-wrapper
ink-wrapper
是一个工具,用于根据该合约的元数据文件(.json
)生成调用 substrate 智能合约的类型安全代码。
安装
从 crates.io 安装此工具
cargo +nightly-2023-04-19 install ink-wrapper --locked --force
注意,这使用的是 nightly-2023-04-19
。您首先需要安装工具链。您可以使用 +nightly
来使用已安装的任何 nightly 版本,但这个特定版本应该可以工作。该包很可能可以在另一个工具链上编译,但在之后可能会生成损坏的代码,请参阅 https://github.com/udoprog/genco/issues/39。
使用方法
关于兼容性的说明。
与 aleph_client
兼容的最后一个版本是 0.6.0
。但请注意,它与 aleph-client
版本 3.0.0
兼容,因为这是在 crates.io 中可用的最后一个版本。我们不保证它与 Testnet 或 Mainnet 兼容,因为它们的运行时可能在某些方面与 aleph_client 3.0.0
不兼容。
当前版本专注于与drink的兼容性。
未来的版本将尝试支持实时链。
设置
给定一些元数据文件,如my_contract.json
,运行工具并将输出保存到您的项目中的一个文件
ink-wrapper -m my_contract.json > src/my_contract.rs
我们对工具的输出仅进行了最小程度的格式化,因此我们建议在(重新)生成时通过格式化器运行它
ink-wrapper -m my_contract.json | rustfmt --edition 2021 > src/my_contract.rs
输出应该无警告地编译,如果您的项目中生成的代码出现任何警告,请创建一个问题。
确保您生成的文件包含在您的模块结构中
mod my_contract;
DRink!
将以下内容添加到您的`Cargo.toml
[dependencies]
drink = "0.8.6"
ink_primitives = "4.3.0"
ink-wrapper-types = { version = "0.7.0", default-feauters = false, features = [ "drink" ] }
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = [ "derive" ] }
生成包装器后,my_contract
将包含一个Instance
结构体,它表示合约的状态。
编写测试的最简单方法是使用drink的单元测试宏,它将设置drink::session::Sessin<MinimalRuntime>
对象
// Auto-generated wrappers need to be added to the crate.
mod my_contract;
// Minimal imports
use ink_wrapper_types::{Connection, ToAccountId};
use drink::{session::Session, AccountId32};
#[drink::test]
fn my_test(mut session: Session) {
// Upload code to DRink! backend
let _code_hash = session.upload_code(my_contract::upload()).expect("Upload to succeed");
// Instantiate the contract.
let address = session.instantiate(my_contract::Instance::new(1000))
.expect("No pallet-contract errors")
.result // AccountId, address, of the new instance
.to_account_id() // Map to ink_primitives type
.into();
// Now we can call contract's methods. They're provided by the trait `my_contract::MyContract` (depends on your actual contract name)
use my_contract::MyContract as _;
// Construct the call object.
let exec_call = address.some_exec_call();
// Execute it.
let res = session.execute(exec_call);
}
有关实际合约包装器的更全面的示例,请参阅tests
目录。
aleph_client
(自0.7.0
起已弃用)
您需要以下依赖项才能使包装器正常工作
ink-wrapper-types = "0.6.0"
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
ink_primitives = "4.2.1"
# You only need this one if you have messages of the form `Trait::message`, like the ones generated by openbrush, for
# example.
async-trait = "0.1.68"
# This one is optional, but you most likely need it as well if you're using the default `aleph_client` implementation
# for actually making calls. Otherwise, you will need to implement `ink_wrapper_types::Connection` and
# `ink_wrapper_types::SignedConnection` yourself.
aleph_client = "3.0.0"
基本用法
有了这些,您就可以在代码中使用包装器了。生成的模块将有一个Instance
结构体,表示您合约的一个实例。您可以通过将account_id
转换为Instance
来与现有的实例进行通信
let account_id: ink_primitives::AccountId = ...;
let instance: my_contract::Instance = account_id.into();
或者(假设合约代码已经被上传),使用生成的构造函数之一创建实例
let instance = conn.instantiate(my_contract::Instance::some_constructor(arg1, arg2)).await?;
然后调用合约上的方法
let result = conn.read(instance.some_getter(arg1, arg2)).await?;
let tx_info = conn.exec(instance.some_mutator(arg1, arg2)).await?;
请注意,任何具有类似Trait::method_name
名称的方法都将被分组到生成的模块中的特性中。如果您使用openbrush,例如,它们的PSP22
实现会生成类似PSP22::balance_of
的方法名称。您需要使用生成的特性来访问这些
use my_contract::PSP22 as _;
conn.read(instance.balance_of(account_id)).await?
在上面的示例中,conn
是实现ink_wrapper_types::Connection
(如果您想使用构造函数或突变者,则还需要ink_wrapper_types::SignedConnection
)的任何内容。在aleph_client
中提供了连接的默认实现。
事件
ink_wrapper_types::Connection
还允许您为给定的TxInfo
检索事件
use ink_wrapper_types::Connection as _;
let tx_info = conn.exec(instance.some_mutator(arg1, arg2)).await?;
let all_events = conn.get_contract_events(tx_info).await?;
let contract_events = all_events.for_contract(instance);
let sub_contract_events = all_events.for_contract(sub_contract);
上面的all_events
对象可能包含来自多个合约的事件,如果合约调用它们。在这种情况下,您可以通过调用for_contract
来过滤和解析这些事件,它有各种您感兴趣的合约。
代码上传
如果您提供了编译时编译的WASM
的路径
ink-wrapper -m my_contract.json --wasm-path ../contracts/target/ink/my_contract.wasm
您还将能够使用生成的包装器上传合约
conn.upload(my_contract::upload()).await
请注意,只要交易成功提交且元数据的代码哈希与上传的代码匹配,upload
函数就会返回 Ok(TxInfo)
。如果代码已在链上存在,则不会返回错误。您可以自己通过查看返回的 TxInfo
中的事件来验证此条件,检查是否包含 CodeStored
事件。
示例
在项目的存储库中查看 tests
以获取更完整的示例。注意,tests/drink
缺少实际的包装器,这些包装器通常在测试时生成。重新生成它们的最简单方法是运行 make all-dockerized
(需要 docker)- 更多信息请参阅 开发。
开发
使用 Makefile
中提供的命令来复制在 CI 上运行的构建过程
make help
最简便的方法是在 docker 中运行一切
make all-dockerized
如果您已在主机上安装了工具并自己启动了节点,您也可以在主机上运行构建
make all
如果在 all-dockerized
中有任何失控的容器,您可以杀死它们
make kill
依赖项
~4.5MB
~91K SLoC