6 个版本 (破坏性更改)

0.10.0 2023年10月18日
0.9.0 2023年6月4日
0.7.0 2022年12月18日
0.6.0 2022年10月25日
0.2.0 2022年1月16日

#2150 in 魔法豆

Download history 161/week @ 2024-03-11 349/week @ 2024-03-18 102/week @ 2024-03-25 184/week @ 2024-04-01 69/week @ 2024-04-08 124/week @ 2024-04-15 105/week @ 2024-04-22 78/week @ 2024-04-29 73/week @ 2024-05-06 92/week @ 2024-05-13 210/week @ 2024-05-20 136/week @ 2024-05-27 102/week @ 2024-06-03 96/week @ 2024-06-10 132/week @ 2024-06-17 144/week @ 2024-06-24

每月499次下载
4 个crate 中使用

自定义许可

49KB
689

秘密合约开发工具包 - 通用工具

⚠️ 此软件包是 secret-toolkit 软件包的子软件包。请参阅其软件包页面以获取更多上下文。

此软件包包含各种未分类的工具。应将其视为后院的小屋,将不属于其他地方的东西放进去。此软件包中的项目没有总体主题。

目录

  1. 调用模块
  2. 功能切换模块

调用模块

此模块包含用于调用另一个合约的特质。不要忘记为所需的特质添加 use 语句。

use secret_toolkit::utils::{InitCallback, HandleCallback};

此外,别忘了将工具包依赖项添加到你的 Cargo.toml

实例化另一个合约

如果你要实例化另一个合约,你应该首先复制/粘贴该合约的 InitMsg。例如,如果你想在 https://github.com/enigmampc/secret-template

# use secret_toolkit_utils::InitCallback;
# use serde::{Serialize, Deserialize};
# use schemars::JsonSchema;
#
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct CounterInitMsg {
    pub count: i32,
}

impl InitCallback for CounterInitMsg {
    const BLOCK_SIZE: usize = 256;
}

处创建计数器合约的实例,你会复制/粘贴其 InitMsg,并将其重命名,以便不与你自己合约中定义的 InitMsg 冲突。然后,你会实现上述的 InitCallback 特质,将 BLOCK_SIZE 常量设置为你的实例化消息填充到的块大小。

# use secret_toolkit_utils::InitCallback;
# use cosmwasm_std::{StdResult, StdError, Response};
# use serde::{Serialize, Deserialize};
# use schemars::JsonSchema;
#
# #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
# pub struct CounterInitMsg {
#     pub count: i32,
# }
#
# impl InitCallback for CounterInitMsg {
#     const BLOCK_SIZE: usize = 256;
# }
#
# let response: StdResult<Response>;
#
let counter_init_msg = CounterInitMsg {
     count: 100
};

let cosmos_msg = counter_init_msg.to_cosmos_msg(
    None,
    "new_contract_label".to_string(),
    123,
    "CODE_HASH_OF_CONTRACT_YOU_WANT_TO_INSTANTIATE".to_string(),
    None,
)?;

response = Ok(Response::new().add_message(cosmos_msg));
# Ok::<(), StdError>(())

接下来,在将实例化其他合约的init或handle函数中,您将创建一个CounterInitMsg的实例,调用其to_cosmos_msg,并将生成的CosmosMsg放置在您函数返回的InitResponse或HandleResponse的messages Vec中。在本例中,我们假设计数器合约的代码ID为123。在本例中,您没有通过InitMsg发送任何SCRT,但如果您需要发送1 SCRT,您将替换to_cosmos_msg调用中的None为Some(Uint128(1000000))。发送的金额以uscrt为单位。任何放置在messages Vec中的CosmosMsg将在您的合约完成自己的处理之后执行。

调用另一个合约的handle函数

您应该首先复制/粘贴您想要调用的特定HandleMsg(s)。例如,如果您想要重置上面实例化的计数器

# use secret_toolkit_utils::HandleCallback;
# use serde::{Serialize, Deserialize};
# use schemars::JsonSchema;
#
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub enum CounterHandleMsg {
    Reset { count: i32 },
}

impl HandleCallback for CounterHandleMsg {
    const BLOCK_SIZE: usize = 256;
}

您将复制/粘贴其HandleMsg枚举的Reset变体,并将枚举重命名以避免与您为自己的合约定义的HandleMsg枚举冲突。然后您将实现上述HandleCallback特质,将BLOCK_SIZE常量设置为您的Reset消息需要填充的块大小。如果您需要调用多个不同的Handle消息,即使它们是针对不同的合约,您也可以将所有Handle消息作为同一枚举的变体包括在内(不过,您不能在同一枚举中包含两个具有相同名称的变体)。

# use secret_toolkit_utils::HandleCallback;
# use cosmwasm_std::{StdResult, StdError, Response};
# use serde::{Serialize, Deserialize};
# use schemars::JsonSchema;
#
# #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
# pub enum CounterHandleMsg {
#     Reset { count: i32 },
# }
#
# impl HandleCallback for CounterHandleMsg {
#     const BLOCK_SIZE: usize = 256;
# }
#
# let response: StdResult<Response>;
#
let reset_msg = CounterHandleMsg::Reset {
    count: 200,
};

let cosmos_msg = reset_msg.to_cosmos_msg(
    "CODE_HASH_OF_CONTRACT_YOU_WANT_TO_EXECUTE".to_string(),
    "ADDRESS_OF_CONTRACT_YOU_ARE_CALLING".to_string(),
    None,
)?;

response = Ok(Response::new().add_message(cosmos_msg));
# Ok::<(), StdError>(())

接下来,在将调用其他合约的init或handle函数中,您将创建一个CounterHandleMsg::Reset变体的实例,调用其to_cosmos_msg,并将生成的CosmosMsg放置在您函数返回的InitResponse或HandleResponse的messages Vec中。在本例中,您没有通过Reset消息发送任何SCRT,但如果您需要发送1 SCRT,您将替换to_cosmos_msg调用中的None为Some(Uint128(1000000))。发送的金额以uscrt为单位。任何放置在messages Vec中的CosmosMsg将在您的合约完成自己的处理之后执行。

查询另一个合约

您应该首先复制/粘贴您想要调用的特定QueryMsg(s)。例如,如果您想要获取上面实例化的计数器的计数

# use secret_toolkit_utils::Query;
# use serde::{Serialize, Deserialize};
# use schemars::JsonSchema;
#
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum CounterQueryMsg {
    GetCount {},
}

impl Query for CounterQueryMsg {
    const BLOCK_SIZE: usize = 256;
}

您将复制/粘贴其QueryMsg枚举的GetCount变体,并将枚举重命名以避免与您为自己的合约定义的QueryMsg枚举冲突。然后您将实现上述Query特质,将BLOCK_SIZE常量设置为您的查询消息需要填充的块大小。如果您需要执行多个不同的查询,即使它们是针对不同的合约,您也可以将所有Query消息作为同一枚举的变体包括在内(不过,您不能在同一枚举中包含两个具有相同名称的变体)。

# use secret_toolkit_utils::Query;
# use serde::{Serialize, Deserialize};
# use schemars::JsonSchema;
#
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct CountResponse {
    pub count: i32,
}

接下来,您将复制/粘贴查询的响应。如果其他合约使用结构定义其查询响应,您就可以继续了。

然而,如果另一个合约返回一个枚举变体,一种方法是将变体的字段复制并放入一个结构体中。因为枚举变体在序列化时会使用变体的名称,所以你还需要创建一个包装结构体,其唯一字段是变体的名称,类型是你定义的包含变体字段的那个结构体。例如,如果你想查询SNIP20参考实现的token_info,我会推荐使用SNIP20工具包函数,但为了举例,假设你忘记了工具包的存在。

# use secret_toolkit_utils::Query;
# use cosmwasm_std::Uint128;
# use serde::{Serialize, Deserialize};
# use schemars::JsonSchema;
#
#[derive(Serialize, Deserialize, JsonSchema, Debug)]
pub struct TokenInfo {
    pub name: String,
    pub symbol: String,
    pub decimals: u8,
    pub total_supply: Option<Uint128>,
}

#[derive(Serialize, Deserialize, JsonSchema, Debug)]
pub struct TokenInfoResponse {
    pub token_info: TokenInfo,
}

你会复制QueryAnswer::TokenInfo枚举变体,并创建一个包含那些字段的TokenInfo结构体。如果你需要访问这些字段,应该将它们全部设置为公共的。然后你会创建一个TokenInfoResponse包装结构体,它只有一个字段,其名称是QueryAnswer变体的蛇形名称(token_info)。作为提醒,你只有在其他合约中将响应定义为枚举时,才需要这样做。

现在来执行查询

# use secret_toolkit_utils::Query;
# use cosmwasm_std::{StdResult, StdError, Uint128, testing::mock_dependencies};
# use serde::{Serialize, Deserialize};
# use schemars::JsonSchema;
#
# #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
# #[serde(rename_all = "snake_case")]
# pub enum CounterQueryMsg {
#    GetCount {},
# }
#
# impl Query for CounterQueryMsg {
#     const BLOCK_SIZE: usize = 256;
# }
#
# #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
# pub struct CountResponse {
#     pub count: i32,
# }
#
# let deps = mock_dependencies();
#
let get_count = CounterQueryMsg::GetCount {};
let count_response: StdResult<CountResponse> = get_count.query(
    deps.as_ref().querier,
    "CODE_HASH_OF_CONTRACT_YOU_WANT_TO_QUERY".to_string(),
    "ADDRESS_OF_CONTRACT_YOU_ARE_QUERYING".to_string(),
);
#
# assert_eq!(
#     count_response.unwrap_err().to_string(),
#     "Generic error: Querier system error: No such contract: ADDRESS_OF_CONTRACT_YOU_ARE_QUERYING"
# );
# Ok::<(), StdError>(())

你创建CounterQueryMsg::GetCount变体的一个实例,并调用其query函数,将返回值赋给响应类型的变量。如果你正在进行token_info查询,你会写let token_info_resp: TokenInfoResponse = ...。你必须在这里使用显式类型注解。

功能开关

本模块实现了您合约的功能开关。其主要动机是允许暂停/恢复某些操作,而不是整个合约,同时为您提供辅助函数,以将您的代码量降到最低。

功能开关被设计成灵活的,因此您可以选择是否将整个消息放在开关下,或者只是某些代码段等。

初始化功能

通常您希望在instantiate()函数中初始化功能。

# use secret_toolkit_utils::feature_toggle::{FeatureToggle, FeatureStatus, FeatureToggleTrait};
# use cosmwasm_std::{entry_point, DepsMut, Env, MessageInfo, StdResult, Response};
# use serde::{Serialize};
#
# #[derive(Serialize)]
# enum Features {
#     Feature1,
#     Feature2,
# }
#
# struct InstantiateMsg { }
#
#[entry_point]
pub fn instantiate(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
    msg: InstantiateMsg,
) -> StdResult<Response> {
    FeatureToggle::init_features(
        deps.storage,
        vec![
            FeatureStatus {
                feature: Features::Feature1,
                status: Default::default(),
            },
            FeatureStatus {
                feature: Features::Feature2,
                status: Default::default(),
            },
        ],
        vec![info.sender], // Can put more than one pauser
    )?;

    Ok(Response::new())
}

FeatureStatus中的功能字段可以是任何东西,只要它实现了serde::Serialize。在这个例子中是

# use serde::{Serialize};
#
#[derive(Serialize)]
pub enum Features {
    Feature1,
    Feature2,
}

对于status字段,你应该使用内置的FeatureToggle::Status枚举

# use serde::{Serialize, Deserialize};
# use schemars::JsonSchema;
#
#[derive(Serialize, Debug, Deserialize, Clone, JsonSchema, PartialEq)]
pub enum Status {
    NotPaused,
    Paused,
}

Status的默认值是Status::NotPaused

在消息上设置开关

在消息(或您选择的任何代码段)上设置开关就像调用FeatureToggle::require_not_paused()一样简单。例如,如果我们有一个Redeem消息在我们的合约中,并且我们初始化了功能为Features::Redeem

# use cosmwasm_std::{DepsMut, Env, StdResult, Response};
# use secret_toolkit_utils::feature_toggle::{FeatureToggle, FeatureToggleTrait};
# use serde::{Serialize};
#
# #[derive(Serialize)]
# pub enum Features {
#     Redeem,
# }
#
fn redeem(
    deps: DepsMut,
    env: Env,
    amount: Option<u128>,
) -> StdResult<Response> {
    FeatureToggle::require_not_paused(deps.as_ref().storage, vec![Features::Redeem])?;

    // Continue with function's operation
    Ok(Response::new())
}

如果Features::Redeem功能的状祝是Paused,合约将出错并停止操作。

暂停/恢复功能

首先,我们需要在我们的HandleMsg枚举中添加PauseUnpause消息。我们可以简单地使用FeatureToggle::FeatureToggleHandleMsg - 它是一个枚举,它包含默认消息,而FeatureToggle也为其提供了默认实现

# use cosmwasm_std::Uint128;
# use secret_toolkit_utils::feature_toggle::FeatureToggleHandleMsg;
# use serde::{Serialize, Deserialize};
#
# #[derive(Serialize, Deserialize)]
# pub enum Features {
#     Redeem,
# }
#
pub enum ExecuteMsg {
    // Contract messages
    Redeem {
        amount: Option<Uint128>,
    },
    Etc {}, //..

    // Feature toggle
    Features(FeatureToggleHandleMsg::<Features>),
}

FeatureToggle 结构体包含了一个用于触发(暂停/恢复)功能的默认实现,因此您可以直接从您的 execute() 函数中调用它。

# use secret_toolkit_utils::feature_toggle::{FeatureToggle, FeatureToggleTrait, FeatureToggleHandleMsg};
# use cosmwasm_std::{entry_point, DepsMut, Env, MessageInfo, StdResult, Response, Uint128, Addr};
# use serde::{Serialize, Deserialize};
#
# #[derive(Serialize, Deserialize)]
# enum Features {
#     Feature1,
#     Feature2,
# }
#
# pub enum ExecuteMsg {
#     // Contract messages
#     Redeem {
#         amount: Option<Uint128>,
#     },
#     Etc {}, //..
#     // Feature toggle
#     Features(FeatureToggleHandleMsg::<Features>),
# }
#
# fn redeem(_deps: DepsMut, _env: Env, _amount: Option<Uint128>) -> StdResult<Response> { Ok(Response::new()) }
# fn etc(_deps: DepsMut, _env: Env) -> StdResult<Response> { Ok(Response::new()) }
#
#[entry_point]
pub fn execute(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    msg: ExecuteMsg,
) -> StdResult<Response> {
    match msg {
        ExecuteMsg::Redeem { amount } => redeem(deps, env, amount),
        ExecuteMsg::Etc {} => etc(deps, env),
        ExecuteMsg::Features(m) => match m {
            FeatureToggleHandleMsg::Pause { features } => FeatureToggle::handle_pause(deps, &info, features),
            FeatureToggleHandleMsg::Unpause { features } => FeatureToggle::handle_unpause(deps, &info, features),
            // `SetPauser` and `RemovePauser` go here too
            # _ => Ok(Response::new())
        },
    }
}

注意:FeatureToggle::pause()FeatureToggle::unpause() 需要 info.sender 是一个暂停者!

添加/删除暂停者

与上面的部分类似,将 FeatureToggleHandleMsg 添加到您的 HandleMsg

注意:您只需将 Features(FeatureToggleHandleMsg) 添加到 HandleMsg 枚举一次,它将添加所有受支持的消息。

FeatureToggle 也为这些提供了默认实现,但您可以用自己的逻辑包装它,例如要求调用者是管理员等。

# use cosmwasm_std::{entry_point, DepsMut, Env, MessageInfo, StdResult, Response, Uint128, Addr, StdError};
# use secret_toolkit_utils::feature_toggle::{FeatureToggle, FeatureToggleTrait, FeatureToggleHandleMsg};
# use serde::{Serialize, Deserialize};
#
# #[derive(Serialize, Deserialize)]
# enum Features {
#     Feature1,
#     Feature2,
# }
#
# pub enum ExecuteMsg {
#     // Contract messages
#     Redeem {
#         amount: Option<Uint128>,
#     },
#     // Feature toggle
#     Features(FeatureToggleHandleMsg::<Features>),
# }
#
# fn redeem(_deps: DepsMut, _env: Env, _amount: Option<Uint128>) -> StdResult<Response> { Ok(Response::new()) }
# fn get_admin() -> StdResult<Addr> { Ok(Addr::unchecked("admin")) }
#
#[entry_point]
pub fn execute(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    msg: ExecuteMsg,
) -> StdResult<Response> {
    // This is the same `match` clause from the section above
    match msg {
        ExecuteMsg::Redeem { amount } => redeem(deps, env, amount),
        ExecuteMsg::Features(m) => match m {
            FeatureToggleHandleMsg::SetPauser { address } => set_pauser(deps, info, address),
            FeatureToggleHandleMsg::RemovePauser { address } => remove_pauser(deps, info, address),
            // `Pause` and `Unpause` go here too
            # _ => Ok(Response::new())
        },
    }
}

fn set_pauser(
    deps: DepsMut,
    info: MessageInfo,
    address: String,
) -> StdResult<Response> {
    let admin = get_admin()?;
    if admin != info.sender {
        return Err(StdError::generic_err("Unauthorized"));
    }

    let address = deps.api.addr_validate(&address)?;
    FeatureToggle::handle_set_pauser(deps, address)
}

fn remove_pauser(
    deps: DepsMut,
    info: MessageInfo,
    address: String,
) -> StdResult<Response> {
    let admin = get_admin()?;
    if admin != info.sender {
        return Err(StdError::generic_err("Unauthorized"));
    }

    let address = deps.api.addr_validate(&address)?;
    FeatureToggle::handle_remove_pauser(deps, address)
}

注意:默认情况下,set_pauserremove_pauser 是无需权限的。

覆盖默认实现

如果您不喜欢默认实现或出于任何其他原因(例如,使用不同的存储命名空间)想要覆盖它,可以通过定义自己的结构体并为它实现 FeatureToggleTrait 来实现。

# use cosmwasm_std::{Storage, StdResult};
# use secret_toolkit_utils::feature_toggle::{FeatureToggleTrait, Status};
# use serde::Serialize;
#
struct TrollFeatureToggle {}

impl FeatureToggleTrait for TrollFeatureToggle {
    // This is mandatory
    const STORAGE_KEY: &'static [u8] = b"custom_and_super_cool_key";

    // This is optional
    fn pause<T: Serialize>(storage: &mut dyn Storage, features: Vec<T>) -> StdResult<()> {
        for f in features {
            Self::set_feature_status(storage, &f, Status::NotPaused)?;
        }

        Ok(())
    }

    // This is optional
    fn unpause<T: Serialize>(storage: &mut dyn Storage, features: Vec<T>) -> StdResult<()> {
        for f in features {
            Self::set_feature_status(storage, &f, Status::Paused)?;
        }

        Ok(())
    }
}

查询

FeatureToggleHandleMsg 类似,查询消息(和默认实现)也是提供的。

# use serde::{Serialize, Deserialize, de::DeserializeOwned};
# use schemars::JsonSchema;
#
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum FeatureToggleQueryMsg<T: Serialize + DeserializeOwned> {
    # #[serde(bound = "")]
    Status {
        features: Vec<T>,
    },
    IsPauser {
        address: String,
    },
}

您可以使用它们在您的 query() 中以与使用 FeatureToggleHandleMsg 相同的方式使用。

依赖

~2–3.5MB
~80K SLoC