MultiversXDeFi数学模式Skill multiversx-defi-math

这个技能提供了在 MultiversX 区块链上开发去中心化金融(DeFi)智能合约时使用的数学模式,包括精度管理、舍入策略、安全缩放和百分比计算。适用于构建处理金融计算、费用、利率和代币数学的合约。关键词:MultiversX, DeFi, 智能合约, 金融数学, 精度控制, 舍入方法, 安全计算, 区块链开发。

DeFi 0 次安装 0 次浏览 更新于 3/21/2026

name: multiversx-defi-math description: MultiversX 智能合约的金融数学模式 — 精度管理、半入舍入、安全缩放和百分比计算。在构建处理金融计算、费用、利率或代币数学的任何 DeFi 合约时使用。

MultiversX DeFi 数学模式

在 MultiversX 智能合约中用于金融安全的可重用数学模式。

精度管理

选择精度级别

pub const BPS_PRECISION: usize = 4;       // 基点:10,000 = 100%
pub const BPS: u64 = 10_000;

pub const PPM_PRECISION: usize = 6;       // 百万分比:1,000,000 = 100%
pub const PPM: u64 = 1_000_000;

pub const WAD_PRECISION: usize = 18;      // 标准代币小数:1e18 = 1.0
pub const WAD: u128 = 1_000_000_000_000_000_000;

pub const RAY_PRECISION: usize = 27;      // 高精度:1e27 = 1.0
pub const RAY: u128 = 1_000_000_000_000_000_000_000_000_000;
级别 小数位数 使用时机
BPS (4) 10,000 = 100% 费用、简单百分比、储备因子
PPM (6) 1,000,000 = 100% 细粒度百分比、部分提款
WAD (18) 1e18 = 1.0 代币数量、价格、份额比率
RAY (27) 1e27 = 1.0 利息指数、复利利率、任何需要最小精度损失的数学

经验法则:使用在您的领域中避免舍入错误的最低精度。对于中间计算,始终使用比最终结果更高的精度。

舍入策略

为什么使用半入舍入?

标准的 ManagedDecimal 操作会截断(向零舍入)。经过多次操作,这会导致系统性价值损失。在 DeFi 中,这意味着协议缓慢泄漏价值,攻击者可以利用微小存款进行攻击。

无符号半入乘法

fn mul_half_up(
    &self,
    a: &ManagedDecimal<Self::Api, NumDecimals>,
    b: &ManagedDecimal<Self::Api, NumDecimals>,
    precision: NumDecimals,
) -> ManagedDecimal<Self::Api, NumDecimals> {
    let scaled_a = a.rescale(precision);
    let scaled_b = b.rescale(precision);
    let product = scaled_a.into_raw_units() * scaled_b.into_raw_units();
    let scaled = BigUint::from(10u64).pow(precision as u32);
    let half_scaled = &scaled / &BigUint::from(2u64);
    let rounded_product = (product + half_scaled) / scaled;
    self.to_decimal(rounded_product, precision)
}

无符号半入除法

fn div_half_up(
    &self,
    a: &ManagedDecimal<Self::Api, NumDecimals>,
    b: &ManagedDecimal<Self::Api, NumDecimals>,
    precision: NumDecimals,
) -> ManagedDecimal<Self::Api, NumDecimals> {
    let scaled_a = a.rescale(precision);
    let scaled_b = b.rescale(precision);
    let scaled = BigUint::from(10u64).pow(precision as u32);
    let numerator = scaled_a.into_raw_units() * &scaled;
    let denominator = scaled_b.into_raw_units();
    let half_denominator = denominator / &BigUint::from(2u64);
    let rounded_quotient = (numerator + half_denominator) / denominator;
    self.to_decimal(rounded_quotient, precision)
}

有符号远离零舍入

对于有符号值(例如,损益、价格差),舍入远离零以防止系统性偏差:

fn mul_half_up_signed(
    &self,
    a: &ManagedDecimalSigned<Self::Api, NumDecimals>,
    b: &ManagedDecimalSigned<Self::Api, NumDecimals>,
    precision: NumDecimals,
) -> ManagedDecimalSigned<Self::Api, NumDecimals> {
    let scaled_a = a.rescale(precision);
    let scaled_b = b.rescale(precision);
    let product = scaled_a.into_raw_units() * scaled_b.into_raw_units();
    let scaled = BigInt::from(10i64).pow(precision as u32);
    let half_scaled = &scaled / &BigInt::from(2i64);

    let rounded_product = if product.sign() == Sign::Minus {
        (product - half_scaled) / scaled  // 更负
    } else {
        (product + half_scaled) / scaled  // 更正
    };
    ManagedDecimalSigned::from_raw_units(rounded_product, precision)
}

安全缩放

在精度级别之间转换时使用半入舍入(标准的 rescale 会截断):

fn rescale_half_up(
    &self,
    value: &ManagedDecimal<Self::Api, NumDecimals>,
    new_precision: NumDecimals,
) -> ManagedDecimal<Self::Api, NumDecimals> {
    let old_precision = value.scale();
    match new_precision.cmp(&old_precision) {
        Ordering::Equal => value.clone(),
        Ordering::Less => {
            // 向下缩放 — 舍入重要
            let precision_diff = old_precision - new_precision;
            let factor = BigUint::from(10u64).pow(precision_diff as u32);
            let half_factor = &factor / 2u64;
            let rounded = (value.into_raw_units() + &half_factor) / factor;
            ManagedDecimal::from_raw_units(rounded, new_precision)
        },
        Ordering::Greater => value.rescale(new_precision), // 向上缩放 — 无需舍入
    }
}

百分比计算

框架内置:proportion()

MultiversX 框架提供 BigUint::proportion(part, total) 用于百分比数学。这是首选方法:

// BigUint::proportion(分子, 分母) — 内置框架方法
let fee = amount.proportion(fee_percent, PERCENT_BASE_POINTS);

生产中常用的基点常量:

pub const PERCENT_BASE_POINTS: u64 = 100_000;  // 100% = 100_000(5位精度)
pub const BPS: u64 = 10_000;                     // 100% = 10_000(基点)
pub const PPM: u64 = 1_000_000;                  // 100% = 1_000_000(百万分比)

使用 proportion() 计算费用

multiversx_sc::imports!();

pub const PERCENT_BASE_POINTS: u64 = 100_000;

/// 使用框架的 proportion() 应用百分比费用
fn calculate_fee(&self, amount: &BigUint, fee_percent: u64) -> BigUint {
    amount.proportion(fee_percent, PERCENT_BASE_POINTS)
}

/// 扣除费用后的金额
fn amount_after_fee(&self, amount: &BigUint, fee_percent: u64) -> BigUint {
    amount - &amount.proportion(fee_percent, PERCENT_BASE_POINTS)
}

BPS(基点) — 手动

当需要显式控制计算时:

pub fn apply_bps(amount: &BigUint, bps: u64) -> BigUint {
    require!(bps <= 10_000, "BPS 超过 100%");
    (amount * bps) / 10_000u64
}

PPM(百万分比) — 手动

pub fn apply_ppm(amount: &BigUint, ppm: u32) -> BigUint {
    require!(ppm <= 1_000_000, "PPM 超过 100%");
    (amount * ppm) / 1_000_000u64
}

常见数学模块模板

#![no_std]
multiversx_sc::imports!();

#[multiversx_sc::module]
pub trait SharedMathModule {
    fn mul_half_up(
        &self,
        a: &ManagedDecimal<Self::Api, NumDecimals>,
        b: &ManagedDecimal<Self::Api, NumDecimals>,
        precision: NumDecimals,
    ) -> ManagedDecimal<Self::Api, NumDecimals> {
        // ...(上述实现)
    }

    fn div_half_up(
        &self,
        a: &ManagedDecimal<Self::Api, NumDecimals>,
        b: &ManagedDecimal<Self::Api, NumDecimals>,
        precision: NumDecimals,
    ) -> ManagedDecimal<Self::Api, NumDecimals> {
        // ...(上述实现)
    }

    fn to_decimal(
        &self,
        value: BigUint,
        precision: NumDecimals,
    ) -> ManagedDecimal<Self::Api, NumDecimals> {
        ManagedDecimal::from_raw_units(value, precision)
    }

    fn min(
        &self,
        a: ManagedDecimal<Self::Api, NumDecimals>,
        b: ManagedDecimal<Self::Api, NumDecimals>,
    ) -> ManagedDecimal<Self::Api, NumDecimals> {
        if a < b { a } else { b }
    }
}

舍入攻击向量

攻击 缓解措施
微小存款以窃取舍入 在所有缩放操作中使用半入舍入
重复小操作以耗尽价值 最小金额 + 指数上的半入
跨转换的精度损失 中间计算使用最高所需精度
利用费用计算中的截断 始终将费用向上舍入(有利于协议)

坏/好示例

// 不要:先除后乘 — 损失精度
let shares = (&amount / &total_supply) * &total_shares; // 对小金额截断为 0!

// 做:先乘后除以保存精度
let shares = (&amount * &total_shares) / &total_supply;

// 不要:硬编码小数假设 — 代币可以有 0-18 位小数
let one_token = BigUint::from(10u64).pow(18); // 假设 18 位小数!

// 做:从代币属性获取小数或作为参数传递
let one_token = BigUint::from(10u64).pow(token_decimals as u32);

反模式

1. 混合精度而不缩放

// 错误 — BPS 和 RAY 有不同的比例
let result = bps_value + ray_value;

// 正确 — 先缩放
let bps_as_ray = bps_value.rescale(RAY_PRECISION);
let result = bps_as_ray + ray_value;

2. 使用截断除法计算费用

// 错误 — 截断损失协议价值
let fee = amount / 100u64; // 截断

// 正确 — 向上舍入以利于协议
let fee = (amount + 99u64) / 100u64; // 天花板除法

3. 中间结果在低精度

// 错误 — BPS 精度在中间计算中损失显著数字
let ratio = self.div_half_up(&a, &b, BPS_PRECISION);
let result = self.mul_half_up(&ratio, &c, BPS_PRECISION);

// 正确 — 在 RAY 计算,最后向下缩放
let ratio = self.div_half_up(&a, &b, RAY_PRECISION);
let result = self.mul_half_up(&ratio, &c, RAY_PRECISION);
let final_result = self.rescale_half_up(&result, BPS_PRECISION);

领域应用

这些通用模式在 DeFi 领域中不同使用:

  • 借贷:利率模型、复利(泰勒展开)、利用率比率 — 全部基于 RAY 精度的 mul_half_up/div_half_up
  • DEX/AMM:价格影响计算、LP 份额数学 — WAD 精度带半入舍入
  • 质押:奖励分配、份额到代币比率 — RAY 指数带安全缩放
  • 金库:费用计算、收益累积 — BPS 费用带天花板除法