合约状态中使用原生u128还是Uint128包装器更合适?
原生u128 vs Uint128包装器:合约状态存储该怎么选?
核心结论
不是必须用Uint128替代原生u128,两者各有适用场景,完全取决于你的实际需求。
原生u128的适用场景
- 合约内部计算高效:原生类型支持直接算术运算(比如示例里的
state.count += 1),无需额外类型转换,代码更简洁,gas开销更低。 - 存储更紧凑:原生u128的序列化/反序列化逻辑更简单,占用链上存储空间更小,进一步节省gas。
你的示例代码用原生u128完全没问题,尤其是当count主要用于合约内部逻辑,且数值不会超过客户端Number类型的精度范围(2^53)时,原生类型是更优选择。
Uint128包装器的核心价值
Uint128的设计初衷主要是解决客户端的数值精度问题:
- JavaScript等前端语言的Number类型无法精确表示大于2^53的整数,Uint128在序列化为JSON时会自动转为字符串,避免精度丢失。
- 封装了安全的数值操作方法(比如
checked_add、checked_sub),可方便处理溢出检查,但原生u128也能通过标准库的u128::checked_add实现同样功能。
什么时候应该用Uint128?
- 当状态中的数值需要直接返回给客户端,且数值可能超过2^53时,用Uint128能确保客户端解析出准确数值。
- 当合约需要接收客户端传入的大整数参数时,用Uint128作为消息结构体的字段,避免解析错误。
代码调整示例
方案1:保留原生u128,处理查询返回
如果继续用原生u128,同时避免客户端精度问题,可在查询时手动将数值转为字符串:
pub fn query(deps: Deps, _env: Env, _msg: Binary) -> StdResult<Binary> { let state: State = singleton_read(deps.storage, CONFIG_KEY).load()?; // 将u128转为字符串后序列化 Ok(to_binary(&serde_json::json!({ "count": state.count.to_string() }))?) }
方案2:改用Uint128
如果直接用Uint128,需要修改State结构体和相关操作:
use cosmwasm_std::{Uint128, ...}; // 导入Uint128 pub struct State { pub count: Uint128, } pub fn instantiate( deps: DepsMut, _env: Env, _info: MessageInfo, _msg: Binary, ) -> StdResult<Response> { let state = State { count: Uint128::new(0) }; singleton(deps.storage, CONFIG_KEY).save(&state)?; Ok(Response::default()) } pub fn execute( deps: DepsMut, _env: Env, _info: MessageInfo, _msg: Binary, ) -> StdResult<Response> { let mut state: State = singleton_read(deps.storage, CONFIG_KEY).load()?; // 使用checked_add避免溢出 state.count = state.count.checked_add(Uint128::new(1))?; singleton(deps.storage, CONFIG_KEY).save(&state)?; Ok(Response::default()) } pub fn query(deps: Deps, _env: Env, _msg: Binary) -> StdResult<Binary> { let state: State = singleton_read(deps.storage, CONFIG_KEY).load()?; // Uint128会自动序列化为字符串 Ok(to_binary(&state)?) }
内容的提问来源于stack exchange,提问作者Pete
相关产品推荐
相关产品推荐

