You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 23:49:55