如何在NEAR Rust智能合约中使用ethnum库U256并解决JsonSchema错误
解决NEAR合约中U256的JsonSchema未实现问题并构建可复用U256运算合约
问题根源
NEAR Rust智能合约中,所有需要通过合约接口暴露的类型必须实现JsonSchema trait(用于生成合约ABI),而ethnum库的U256类型默认未提供该实现,因此触发报错。我们可以通过封装U256并手动实现所需trait解决问题,同时封装常用运算方法供其他合约复用。
可复用U256合约实现
1. 配置Cargo依赖
在Cargo.toml中添加必要依赖:
[package] name = "u256-utils" version = "0.1.0" edition = "2021" [dependencies] near-sdk = "4.0.0" ethnum = "1.3.0" schemars = "0.8.12" serde = { version = "1.0", features = ["derive"] }
2. 核心代码实现
创建src/lib.rs,封装U256并实现所需trait与运算方法:
use ethnum::U256; use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; /// 封装ethnum的U256,实现NEAR合约所需的全部trait #[derive( Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, BorshSerialize, BorshDeserialize, Serialize, Deserialize, JsonSchema )] #[serde(transparent)] pub struct NearU256(U256); impl NearU256 { /// 从u64创建NearU256 pub fn from_u64(value: u64) -> Self { Self(U256::from(value)) } /// 从字符串创建NearU256(支持十进制/十六进制) pub fn from_str(s: &str) -> Result<Self, ethnum::ParseIntError> { U256::from_str(s).map(Self) } /// 转换为字符串 pub fn to_string(&self) -> String { self.0.to_string() } // 加法运算 pub fn add(&self, other: &Self) -> Self { Self(self.0 + other.0) } // 减法运算(无溢出检查,如需溢出安全可改用checked_sub) pub fn sub(&self, other: &Self) -> Self { Self(self.0 - other.0) } // 乘法运算 pub fn mul(&self, other: &Self) -> Self { Self(self.0 * other.0) } // 除法运算(无溢出检查,如需溢出安全可改用checked_div) pub fn div(&self, other: &Self) -> Self { Self(self.0 / other.0) } // 溢出安全加法 pub fn checked_add(&self, other: &Self) -> Option<Self> { self.0.checked_add(other.0).map(Self) } // 溢出安全减法 pub fn checked_sub(&self, other: &Self) -> Option<Self> { self.0.checked_sub(other.0).map(Self) } } // 允许直接从U256转换为NearU256 impl From<U256> for NearU256 { fn from(value: U256) -> Self { Self(value) } } // 允许直接从NearU256转换为U256 impl From<NearU256> for U256 { fn from(value: NearU256) -> Self { value.0 } } // 示例合约:展示如何使用NearU256 #[near_sdk::contract] pub mod u256_demo { use super::*; use near_sdk::{near_bindgen, AccountId}; #[near_bindgen] #[derive(BorshDeserialize, BorshSerialize)] pub struct Contract { balances: std::collections::HashMap<AccountId, NearU256>, } impl Default for Contract { fn default() -> Self { Self { balances: Default::default() } } } #[near_bindgen] impl Contract { // 存入余额(加法) pub fn deposit(&mut self, account_id: AccountId, amount: NearU256) { let balance = self.balances.get(&account_id).unwrap_or(&NearU256::from_u64(0)); self.balances.insert(account_id, balance.add(&amount)); } // 取出余额(溢出安全减法) pub fn withdraw(&mut self, account_id: AccountId, amount: NearU256) -> Option<NearU256> { let balance = self.balances.get(&account_id)?; let new_balance = balance.checked_sub(&amount)?; self.balances.insert(account_id, new_balance); Some(amount) } // 查询余额 pub fn get_balance(&self, account_id: AccountId) -> Option<NearU256> { self.balances.get(&account_id).cloned() } } } #[cfg(test)] mod tests { use super::*; use near_sdk::test_utils::VMContextBuilder; use near_sdk::{testing_env, AccountId}; #[test] fn test_add_sub() { let mut context = VMContextBuilder::new(); testing_env!(context.build()); let mut contract = u256_demo::Contract::default(); let account = AccountId::new_unchecked("test.near".to_string()); let amount1 = NearU256::from_u64(100); let amount2 = NearU256::from_u64(50); contract.deposit(account.clone(), amount1); assert_eq!(contract.get_balance(account.clone()), Some(NearU256::from_u64(100))); contract.deposit(account.clone(), amount2); assert_eq!(contract.get_balance(account.clone()), Some(NearU256::from_u64(150))); let withdrawn = contract.withdraw(account.clone(), amount2); assert_eq!(withdrawn, Some(amount2)); assert_eq!(contract.get_balance(account), Some(NearU256::from_u64(100))); } }
关键说明
#[serde(transparent)]:让序列化/反序列化直接透传内部的U256,避免额外嵌套结构- 派生
JsonSchema:通过宏自动生成实现,确保合约ABI能正确识别该类型 - 双版本运算方法:同时提供普通运算和溢出安全运算(
checked_*系列),适配不同业务场景 - 高复用性:将
NearU256放在独立库crate中,其他合约可通过Cargo.toml导入直接使用
内容的提问来源于stack exchange,提问作者Aakash Singh Rajput
相关产品推荐
相关产品推荐

