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 魔法豆
每月499次下载
在 4 个crate 中使用
49KB
689 行
秘密合约开发工具包 - 通用工具
⚠️ 此软件包是 secret-toolkit
软件包的子软件包。请参阅其软件包页面以获取更多上下文。
此软件包包含各种未分类的工具。应将其视为后院的小屋,将不属于其他地方的东西放进去。此软件包中的项目没有总体主题。
目录
调用模块
此模块包含用于调用另一个合约的特质。不要忘记为所需的特质添加 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
枚举中添加Pause
和Unpause
消息。我们可以简单地使用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_pauser
和 remove_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