Rust智能合约开发库cw4-group的使用,CosmWasm生态成员组管理与权限控制插件
CW4 Group
这是一个基本实现cw4规范的合约。它满足了规范的所有要求,包括原始查询功能,并设计为作为cw3兼容合约的后端存储使用。
它存储了一组成员以及一个管理员,并允许管理员更新状态。原始查询(用于跨合约查询)可以检查给定的成员地址和总权重。智能查询(设计用于客户端API)可以做同样的事情,还可以查询管理员地址以及分页查询所有成员。
初始化
创建时需要传入成员列表,以及一个可选的admin
(如果你希望它是可变的)。
pub struct InitMsg {
pub admin: Option<HumanAddr>,
pub members: Vec<Member>,
}
pub struct Member {
pub addr: HumanAddr,
pub weight: u64,
}
成员由地址和权重定义。这些会被转换并存储在其CanonicalAddr
下,格式由cw4原始查询定义。
注意0是一个允许的权重。这不会给予任何投票权,但确实定义了该地址是组的一部分。这可用于例如KYC白名单中,表示他们被允许但不能参与决策。
消息
基本更新消息、查询和钩子由cw4规范定义。请参考它获取更多信息。
cw4-group
添加了一个消息来控制组成员:
UpdateMembers{add, remove}
- 接受一个成员差异并添加/更新成员,同时移除任何提供的地址。如果一个地址在两个列表中,它将被移除。如果它在add
中出现多次,只有最后一次出现会被使用。
完整示例代码
use cosmwasm_std::{HumanAddr, Response, StdResult};
use cw4::{Member, MemberListResponse, MemberResponse, TotalWeightResponse};
use cw4_group::msg::{InitMsg, HandleMsg, QueryMsg};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
// 初始化合约
pub fn init(
deps: DepsMut,
_env: Env,
info: MessageInfo,
msg: InitMsg,
) -> StdResult<Response> {
// 验证管理员地址
let admin = msg.admin
.map(|a| deps.api.canonical_address(&a))
.transpose()?;
// 存储管理员
if let Some(admin) = &admin {
ADMIN.set(deps.storage, admin)?;
}
// 存储成员
let members = msg.members
.into_iter()
.map(|m| {
let addr = deps.api.canonical_address(&m.addr)?;
Ok(Member {
addr,
weight: m.weight,
})
})
.collect::<StdResult<Vec<_>>>()?;
MEMBERS.save(deps.storage, &members)?;
Ok(Response::default())
}
// 处理更新成员消息
pub fn handle_update_members(
deps: DepsMut,
info: MessageInfo,
add: Vec<Member>,
remove: Vec<HumanAddr>,
) -> StdResult<Response> {
// 检查调用者是否是管理员
ADMIN.assert_admin(deps.as_ref(), &info.sender)?;
// 加载现有成员
let mut members = MEMBERS.load(deps.storage)?;
// 移除成员
for addr in remove {
let addr = deps.api.canonical_address(&addr)?;
members.retain(|m| m.addr != addr);
}
// 添加/更新成员
for member in add {
let addr = deps.api.canonical_address(&member.addr)?;
// 如果存在则更新,否则添加
if let Some(existing) = members.iter_mut().find(|m| m.addr == addr) {
existing.weight = member.weight;
else {
members.push(Member {
addr,
weight: member.weight,
});
}
}
// 保存更新后的成员列表
MEMBERS.save(deps.storage, &members)?;
Ok(Response::default())
}
// 查询成员列表
pub fn query_members(
deps: Deps,
start_after: Option<HumanAddr>,
limit: Option<u32>,
) -> StdResult<MemberListResponse> {
// 读取所有成员
let members = MEMBERS.load(deps.storage)?;
// 处理分页
let start = start_after
.map(|addr| deps.api.canonical_address(&addr))
.transpose()?;
let members = members
.into_iter()
.filter(|m| start.as_ref().map_or(true, |s| m.addr > *s))
.take(limit.unwrap_or(DEFAULT_LIMIT) as usize)
.map(|m| {
let addr = deps.api.human_address(&m.addr)?;
Ok(Member {
addr,
weight: m.weight,
})
})
.collect::<StdResult<Vec<_>>>()?;
Ok(MemberListResponse { members })
}
// 查询单个成员
pub fn query_member(
deps: Deps,
addr: HumanAddr,
) -> StdResult<MemberResponse> {
let addr = deps.api.canonical_address(&addr)?;
let members = MEMBERS.load(deps.storage)?;
let member = members
.into_iter()
.find(|m| m.addr == addr)
.map(|m| {
let addr = deps.api.human_address(&m.addr)?;
Ok(Member {
addr,
weight: m.weight,
})
})
.transpose()?;
Ok(MemberResponse { weight: member.map(|m| m.weight) })
}
// 查询总权重
pub fn query_total_weight(
deps: Deps,
) -> StdResult<TotalWeightResponse> {
let members = MEMBERS.load(deps.storage)?;
let total = members.into_iter().map(|m| m.weight).sum();
Ok(TotalWeightResponse { weight: total })
}
完整Demo示例
use cosmwasm_std::{testing::{mock_dependencies, mock_env, mock_info}, Addr, HumanAddr};
use cw4::{Member, TotalWeightResponse};
use cw4_group::msg::{InitMsg, HandleMsg, QueryMsg};
#[test]
fn test_cw4_group_workflow() {
// 初始化mock环境
let mut deps = mock_dependencies();
let env = mock_env();
let info = mock_info("creator", &[]);
// 准备初始化消息
let init_msg = InitMsg {
admin: Some(HumanAddr::from("admin")),
members: vec![
Member {
addr: HumanAddr::from("member1"),
weight: 1,
},
Member {
addr: HumanAddr::from("member2"),
weight: 2,
}
],
};
// 初始化合约
let res = init(deps.as_mut(), env.clone(), info, init_msg).unwrap();
assert_eq!(0, res.messages.len());
// 测试查询总权重
let res: TotalWeightResponse =
query_total_weight(deps.as_ref()).unwrap();
assert_eq!(3, res.weight); // 1 + 2 = 3
// 测试查询成员
let res = query_member(
deps.as_ref(),
HumanAddr::from("member1")
).unwrap();
assert_eq!(Some(1), res.weight);
// 准备更新成员的消息
let info = mock_info("admin", &[]);
let handle_msg = HandleMsg::UpdateMembers {
add: vec![
Member {
addr: HumanAddr::from("member1"),
weight: 3, // 更新权重
},
Member {
addr: HumanAddr::from("member3"),
weight: 4, // 新增成员
}
],
remove: vec![HumanAddr::from("member2")], // 移除成员
};
// 执行更新成员操作
let res = handle_update_members(
deps.as_mut(),
info,
handle_msg.add,
handle_msg.remove,
).unwrap();
assert_eq!(0, res.messages.len());
// 验证更新后的总权重
let res: TotalWeightResponse =
query_total_weight(deps.as_ref()).unwrap();
assert_eq!(7, res.weight); // 3 (member1) + 4 (member3) = 7
// 验证分页查询成员
let res = query_members(
deps.as_ref(),
None,
Some(10),
).unwrap();
assert_eq!(2, res.members.len());
assert_eq!(3, res.members[0].weight); // member1
assert_eq!(4, res.members[1].weight); // member3
}
1 回复
Rust智能合约开发库cw4-group的使用:CosmWasm生态成员组管理与权限控制插件
简介
cw4-group是CosmWasm生态系统中一个用于成员组管理和权限控制的智能合约库。它实现了cw4规范,为区块链应用提供了灵活的用户组管理功能,特别适合需要权限控制的去中心化应用场景。
主要功能
- 成员组管理(添加/删除成员)
- 成员权重分配
- 基于权重的投票机制支持
- 与其他cw4兼容合约的互操作性
完整示例代码
下面是一个完整的cw4-group合约实现示例:
use cosmwasm_std::{
Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult, to_binary
};
use cw4_group::{
msg::{InstantiateMsg, ExecuteMsg, QueryMsg, Member, MemberListResponse},
ContractError
};
// 合约初始化
pub fn instantiate(
deps: DepsMut,
_env: Env,
info: MessageInfo,
msg: InstantiateMsg,
) -> Result<Response, ContractError> {
// 设置管理员,如果未指定则使用调用者地址
let admin = msg.admin.unwrap_or_else(|| info.sender.clone());
// 获取初始成员列表,如果没有则为空
let members = msg.members.unwrap_or_default();
// 初始化成员组
cw4_group::initialize(deps.storage, admin, members)?;
Ok(Response::default()
.add_attribute("method", "instantiate")
.add_attribute("admin", info.sender))
}
// 合约执行逻辑
pub fn execute(
deps: DepsMut,
_env: Env,
info: MessageInfo,
msg: ExecuteMsg,
) -> Result<Response, ContractError> {
match msg {
ExecuteMsg::AddMember { addr, weight } => {
// 验证调用者有管理员权限
cw4_group::assert_admin(deps.as_ref(), &info.sender)?;
// 添加新成员
cw4_group::add_member(deps.storage, &addr, weight)?;
Ok(Response::new()
.add_attribute("action", "add_member")
.add_attribute("addr", addr)
.add_attribute("weight", weight.to_string()))
}
ExecuteMsg::RemoveMember { addr } => {
// 验证调用者有管理员权限
cw4_group::assert_admin(deps.as_ref(), &info.sender)?;
// 移除成员
cw4_group::remove_member(deps.storage, &addr)?;
Ok(Response::new()
.add_attribute("action", "remove_member")
.add_attribute("addr", addr))
}
ExecuteMsg::UpdateAdmin { admin } => {
// 验证调用者有管理员权限
cw4_group::assert_admin(deps.as_ref(), &info.sender)?;
// 更新管理员
cw4_group::update_admin(deps.storage, admin)?;
Ok(Response::new()
.add_attribute("action", "update_admin")
.add_attribute("new_admin", admin))
}
}
}
// 合约查询逻辑
pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
match msg {
QueryMsg::Admin {} => {
let admin = cw4_group::query_admin(deps.storage)?;
to_binary(&admin)
}
QueryMsg::Member { addr } => {
let member = cw4_group::query_member(deps.storage, &addr)?;
to_binary(&member)
}
QueryMsg::ListMembers { start_after, limit } => {
let members = cw4_group::list_members(deps.storage, start_after, limit)?;
let resp = MemberListResponse { members };
to_binary(&resp)
}
QueryMsg::TotalWeight {} => {
let weight = cw4_group::query_total_weight(deps.storage)?;
to_binary(&weight)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
use cosmwasm_std::{from_binary, Addr};
#[test]
fn test_instantiate() {
let mut deps = mock_dependencies();
let env = mock_env();
let info = mock_info("creator", &[]);
// 初始化消息
let init_msg = InstantiateMsg {
admin: Some("admin".to_string()),
members: vec![
Member {
addr: "member1".to_string(),
weight: 10,
},
Member {
addr: "member2".to_string(),
weight: 20,
},
],
};
// 执行初始化
let res = instantiate(deps.as_mut(), env, info, init_msg).unwrap();
assert_eq!(0, res.messages.len());
// 查询管理员
let admin_query = QueryMsg::Admin {};
let bin = query(deps.as_ref(), mock_env(), admin_query).unwrap();
let admin: Addr = from_binary(&bin).unwrap();
assert_eq!(admin, Addr::unchecked("admin"));
// 查询成员1
let member_query = QueryMsg::Member { addr: "member1".to_string() };
let bin = query(deps.as_ref(), mock_env(), member_query).unwrap();
let weight: u64 = from_binary(&bin).unwrap();
assert_eq!(weight, 10);
// 查询总权重
let total_query = QueryMsg::TotalWeight {};
let bin = query(deps.as_ref(), mock_env(), total_query).unwrap();
let total: u64 = from_binary(&bin).unwrap();
assert_eq!(total, 30);
}
}
最佳实践
- 权限控制:确保敏感操作(如添加/删除成员)有适当的权限检查
- 权重设计:合理分配成员权重以反映其在组织中的重要性
- 查询优化:对于大型成员组,使用分页查询(start_after和limit参数)
- 事件记录:为成员变更操作添加事件记录以便追踪
注意事项
- cw4-group存储结构简单,不适合超大规模成员组(成千上万成员)
- 成员权重是u64类型,确保不会溢出
- 合约升级需要考虑成员数据的迁移
cw4-group为CosmWasm生态系统提供了一个简单而强大的成员管理基础组件,可以轻松集成到各种需要权限管理的去中心化应用中。