#区块链 #ink #智能合约 #合约 #wasm #客户端 #json文件

应用程序 ink-wrapper

用于根据合约元数据文件生成类型安全代码的工具,以便调用 ink 智能合约

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 魔法豆

Apache-2.0

39KB
744

ink-wrapper

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