#blockchain #osmosis #juno #query-response #token-factory

token-bindings

支持 Token Factory 的区块链的 CustomMsg 和 CustomQuery 绑定

5 个版本

0.11.0 2023 年 8 月 22 日
0.10.3 2023 年 8 月 3 日
0.10.2 2023 年 7 月 24 日
0.10.1 2023 年 7 月 22 日
0.10.0 2023 年 7 月 22 日

#4 in #juno

每月 38 次下载
用于 2 crates

Apache-2.0

25KB
255

Juno 网络绑定

从 Osmosis 获取的定制 TokenFactory 功能的 CosmWasm 绑定。

先决条件

开始之前,请确保您已安装 rustup 以及最新的 rustccargo 版本。目前,我们正在测试 1.64+。

您还需要安装 wasm32-unknown-unknown 目标。

您可以通过以下方式检查:

rustc --version
cargo --version
rustup target list --installed
# if wasm32 is not listed above, run this
rustup target add wasm32-unknown-unknown

运行测试

cargo test --locked

编译

cargo build --locked

为 osmosis 合同生成模式

cd contracts/reflect
cargo schema --locked

这将生成 contracts/reflect/schema/ 目录中的模式

构建合同

cd contracts/reflect
cargo wasm

理解测试

主要代码在 src/contract.rs 中,那里的单元测试以纯 rust 运行,这使得它们执行非常快,在失败时提供良好的输出,尤其是如果您执行 RUST_BACKTRACE=1 cargo unit-test

我们认为测试对区块链上的任何事物都至关重要,并建议始终保持测试更新。

生成 JSON 模式

虽然 Wasm 调用(instantiateexecutequery)接受 JSON,但这不足以使用它。我们需要向客户端公开预期消息的模式。您可以通过调用 cargo schema 生成此模式,这将输出 ./schema 中的 4 个文件,对应于合同接受的 3 种消息类型以及内部的 State

这些文件是标准 json-schema 格式,应该可以由各种客户端工具使用,无论是自动生成编解码器还是仅用于根据定义的方案验证传入的 json。

准备生产中的 Wasm 字节码

在我们将其上传到链上之前,我们需要确保输出大小尽可能小,因为这将被包含在交易的主体中。我们还想有一个可重复的构建过程,以便第三方可以验证上传的 Wasm 代码确实来自声明的 Rust 代码。

为了解决这两个问题,我们开发了 rust-optimizer,一个 Docker 镜像,可以以一致的方式产生极小的构建输出。运行它的建议方式是

docker run --rm -v "$(pwd)":/code \
  --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \
  --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \
  cosmwasm/workspace-optimizer:0.12.8

或者,如果你在 arm64 机器上,你应该使用用 arm64 构建的 Docker 镜像。

docker run --rm -v "$(pwd)":/code \
  --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \
  --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \
  cosmwasm/workspace-optimizer-arm64:0.12.8

我们必须将合约代码挂载到 /code。如果你不想首先使用 cd 进入目录,可以使用绝对路径代替 $(pwd)。其他两个卷可以加快速度。特别是将 /code/target 挂载很有用,可以避免 Docker 使用 root 权限覆盖你的本地开发文件。请注意,/code/target 缓存对每个编译的合约是唯一的,以限制干扰,而注册表缓存是全局的。

与本地编译相比,这相当慢,尤其是给定合约的第一个编译。使用两个卷缓存对于加快相同合约的后续编译非常有用。

这会生成一个包含 PROJECT_NAME.wasmartifacts 目录,以及包含 wasm 文件 Sha256 哈希值的 checksums.txt。wasm 文件是确定性地编译的(任何其他人在相同的 git 提交上运行相同的 Docker 都应该得到相同的文件,具有相同的 Sha256 哈希值)。它还被剥离和最小化,以便上传到区块链(我们将在上传过程中将其 gzip,使其变得更小)。

编写与 Osmosis 交互的合约

选择一个网络

要在你的合约中使用这些绑定,你需要将它们部署到测试网或设置本地网。阅读 官方文档 了解如何进行此操作。

请注意,Osmosis 上的 Cosmwasm 是受权限控制的,所以你必须禁用受权限的 Cosmwasm 以便于开发。

使用 Osmosis 类型对输入和结果进行参数化

为了使集成工作,以下在此绑定中定义的类型将需要用作输入/输出的类型,或用于参数化 Cosmwasm 类型

  • OsmosisQuery,它实现了 CustomQuery
  • OsmosisMsg,它实现了 CosmosMsg

具体来说,任何使用 DepsDepsMut 并需要与链交互的函数都需要将它们参数化为 Deps<OsmosisQuery>DepsMut<OsmosisQuery>,并且任何添加要执行的消息或子消息的 Response 都需要参数化为 Response<OsmosisMsg>

执行 Osmosis 查询

如果你想在合约内部执行 Osmosis 查询,你可以这样做,而不需要消息传递(请参阅 Cosmwasm 文档中的 QuerySemantics)。

为此,你可以创建一个 OsmosisQuery 和一个 QueryRequest<OsmosisQuery>,由查询器分发。

以下代码创建了一个 PoolState 变体,并将其传递给查询器

    let pool_query = OsmosisQuery::PoolState { id: 1 };
    let query = QueryRequest::from(pool_query);
    let pool_info: PoolStateResponse = deps.querier.query(&query)?;

以下查询两种面额的现货价格,但使用 spot_price 函数简化了 OsmosisQuery 的查询创建

    let spot_price = OsmosisQuery::spot_price(1, &denom1, &denom2);
    let query = QueryRequest::from(spot_price);
    let response: SpotPriceResponse = deps.querier.query(&query)?;

请注意,这两个查询中使用的 deps 需要是 Deps<OsmosisQuery>(或 DepsMut<OsmosisQuery>)的类型。否则,默认实现将假设一个 Empty 自定义查询

以(子)消息的形式执行交易

要将 osmosis 交易作为您的合同执行响应的一部分执行,您可以为您的合同响应创建 OsmosisMsg 并将其提供。

以下是一个如何在其中执行交换的示例

有关如何执行和如何处理回复的更多详细信息,请参阅 Cosmwasm 文档中的 子消息

fn execute_swap(
    _deps: DepsMut,
    _info: MessageInfo,
    input: u128,
    min_output: u128,
) -> Result<Response<OsmosisMsg>, ContractError> {
    let swap = OsmosisMsg::simple_swap(
        1,
        "uosmo",
        "uion",
        SwapAmountWithLimit::ExactIn {
            input: Uint128::from(input),
            min_output: Uint128::from(min_output),
        },
    );
    let msgs = vec![SubMsg::new(swap)];

    Ok(Response::new()
        .add_attribute("action", "execute_swap")
        .add_submessages(msgs))
}

请注意,DepsMut 在该函数中未参数化,因为它没有使用它(因此默认的 DepsMut<Empty> 就足够了)。然而,如果您的合同在别处使用 deps,那么您可能希望统一合同中的类型,因此将输入参数设为 DepsMut<OsmosisQuery> 可能是一个好主意。

有关上面使用的 OsmosisMsg 参数的更多信息,请参阅 Osmosis Cosmwasm API 文档(待定)。

执行自定义交易

如果您想执行的交易不是由该 API 提供的,您仍然可以使用 CustomMsg 来执行它。

待办事项:添加示例。

编写模拟 Osmosis 响应的集成测试

要能够编写依赖于与 Osmosis 交互的函数的测试,您需要模拟链,以便它可以处理您的合同请求并给出正确的响应。

为此,您需要

创建并初始化一个应用程序

        let mut app = OsmosisApp::new();
        //... setup your variables here
        app.init_modules(|router, _, storage| {
            router.custom.set_pool(storage, 1, &pool).unwrap();
            router
                .bank
                .init_balance(storage, &owner, init_funds)
                .unwrap();
            router
                .bank
                .init_balance(storage, &borrower, pool_funds.clone())
                .unwrap();
        });

为您的合同定义一个包装器

    pub fn contract<C, Q>() -> Box<dyn Contract<C, Q>>
    where
        C: Clone + Debug + PartialEq + JsonSchema + DeserializeOwned + 'static,
        Q: CustomQuery + DeserializeOwned + 'static,
        ContractWrapper<
            ExecuteMsg,
            InstantiateMsg,
            QueryMsg,
            ContractError,
            ContractError,
            cosmwasm_std::StdError,
            OsmosisMsg,
            OsmosisQuery,
        >: Contract<C, Q>,
    {
        let contract = ContractWrapper::new(execute, instantiate, query); //.with_reply(reply);
        Box::new(contract)
    }

实例化您的合同


        let contract: Box<dyn Contract<OsmosisMsg, OsmosisQuery>> = contract();
        let code_id = app.store_code(contract);

        // Instantiate the contract
        let msg = InstantiateMsg {
            admin: None,
            funds_denom: "usdc".to_string(),
            collateral_denom: "gamm/pool/1".to_string(),
        };
        let contract_addr = app
            .instantiate_contract(code_id, owner.clone(), &msg, &[], "shark", None)
            .unwrap();

执行您的消息并测试结果

        let balance = app.wrap().query_balance(&borrower, "usdc").unwrap();
        assert_eq!(balance.amount, Uint128::new(0));

        let amount = 6;
        let msg = ExecuteMsg::Borrow { amount };  // This is a message defined by your contract

        let wasm_msg = CosmosMsg::Wasm(WasmMsg::Execute {
            contract_addr: contract_addr.into(),
            msg: to_binary(&&inner_msg).unwrap(),
            funds: vec![],
        });

        app.execute(borrower.clone(), wasm_msg).unwrap();

        let balance = app.wrap().query_balance(&borrower, "usdc").unwrap();
        assert_eq!(balance.amount, Uint128::new(amount));

依赖项

~3.5–5.5MB
~114K SLoC