multiversx-payment-handling
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseMultiversX Payment Handling — self.call_value()
API Reference
self.call_value()MultiversX 支付处理 — self.call_value()
API 参考文档
self.call_value()Complete reference for receiving and validating payments in MultiversX smart contracts (SDK v0.64+).
MultiversX智能合约中接收和验证支付的完整参考(SDK v0.64+)。
Payment Type Hierarchy
支付类型层级结构
Payment<M> ← v0.64+ preferred, uses NonZeroBigUint (amount guaranteed > 0)
├── token_identifier: TokenId<M> (unified: EGLD + ESDT)
├── token_nonce: u64
└── amount: NonZeroBigUint<M>
EsdtTokenPayment<M> ← ESDT-only (no EGLD), BigUint amount (can be 0 in theory)
├── token_identifier: EsdtTokenIdentifier<M>
├── token_nonce: u64
└── amount: BigUint<M>
EgldOrEsdtTokenPayment<M> ← Legacy mixed type
├── token_identifier: EgldOrEsdtTokenIdentifier<M>
├── token_nonce: u64
└── amount: BigUint<M>
PaymentVec<M> = ManagedVec<M, Payment<M>> ← List of payments
FungiblePayment<M> ← Payment with nonce == 0 guaranteedPayment<M> ← v0.64+ 推荐使用,基于NonZeroBigUint(金额保证>0)
├── token_identifier: TokenId<M> (统一支持:EGLD + ESDT)
├── token_nonce: u64
└── amount: NonZeroBigUint<M>
EsdtTokenPayment<M> ← 仅支持ESDT(不支持EGLD),金额为BigUint(理论上可为0)
├── token_identifier: EsdtTokenIdentifier<M>
├── token_nonce: u64
└── amount: BigUint<M>
EgldOrEsdtTokenPayment<M> ← 旧版混合类型
├── token_identifier: EgldOrEsdtTokenIdentifier<M>
├── token_nonce: u64
└── amount: BigUint<M>
PaymentVec<M> = ManagedVec<M, Payment<M>> ← 支付列表
FungiblePayment<M> ← 保证nonce == 0的支付Payment Methods
支付方法
rust
// Payment<M>
payment.is_fungible() -> bool
payment.fungible_or_panic() -> FungiblePayment<M>
payment.into_tuple() -> (TokenId<M>, u64, NonZeroBigUint<M>)
payment.as_egld_or_esdt_payment() -> &EgldOrEsdtTokenPayment<M>
payment.map_egld_or_esdt(ctx, for_egld, for_esdt) -> U
// NonZeroBigUint<M>
NonZeroBigUint::new(bu: BigUint<M>) -> Option<Self> // None if zero
NonZeroBigUint::new_or_panic(bu: BigUint<M>) -> Self // panics if zero
nzbu.into_big_uint() -> BigUint<M>
nzbu.as_big_uint() -> &BigUint<M>rust
// Payment<M>
payment.is_fungible() -> bool
payment.fungible_or_panic() -> FungiblePayment<M>
payment.into_tuple() -> (TokenId<M>, u64, NonZeroBigUint<M>)
payment.as_egld_or_esdt_payment() -> &EgldOrEsdtTokenPayment<M>
payment.map_egld_or_esdt(ctx, for_egld, for_esdt) -> U
// NonZeroBigUint<M>
NonZeroBigUint::new(bu: BigUint<M>) -> Option<Self> // 若为0则返回None
NonZeroBigUint::new_or_panic(bu: BigUint<M>) -> Self // 若为0则触发panic
nzbu.into_big_uint() -> BigUint<M>
nzbu.as_big_uint() -> &BigUint<M>self.call_value()
Methods
self.call_value()self.call_value()
方法
self.call_value()EGLD-Only
仅支持EGLD
| Method | Returns | Behavior |
|---|---|---|
| | Accepts EGLD only, panics if ESDT sent. Handles both direct and multi-transfer EGLD. |
| | EGLD as 18-decimal |
| | Raw EGLD from VM. Returns 0 even if ESDT was sent. Low-level, rarely needed. |
| 方法 | 返回值 | 行为 |
|---|---|---|
| | 仅接受EGLD,若传入ESDT则触发panic。支持直接转账和多笔转账的EGLD。 |
| | 以18位小数 |
| | 从VM获取原始EGLD金额。即使传入ESDT也返回0。底层方法,很少需要使用。 |
Single Token (Any Type)
单代币(任意类型)
| Method | Returns | Behavior |
|---|---|---|
| | Exactly 1 transfer (EGLD or ESDT). Panics if 0 or 2+. Preferred for v0.64+. |
| | 0 or 1 transfer. Panics if 2+. |
| | Exactly 1 ESDT. Panics if EGLD or count != 1. |
| | Exactly 1 fungible ESDT (nonce == 0). |
| | 0 or 1 transfer. Returns EGLD(0) if nothing sent. |
| | Like above but panics if non-fungible. |
| 方法 | 返回值 | 行为 |
|---|---|---|
| | 恰好1笔转账(EGLD或ESDT)。若为0或2笔及以上则触发panic。v0.64+推荐使用。 |
| | 0或1笔转账。若为2笔及以上则触发panic。 |
| | 恰好1笔ESDT转账。若为EGLD或数量不等于1则触发panic。 |
| | 恰好1笔可 fungible ESDT(nonce == 0)。 |
| | 0或1笔转账。若未传入任何金额则返回EGLD(0)。 |
| | 如上,但如果是非fungible代币则触发panic。 |
Multi-Token
多代币
| Method | Returns | Behavior |
|---|---|---|
| | Recommended. All transfers as |
| | All transfers as legacy type. |
| | ESDT only. Panics if EGLD present in multi-transfer. |
| 方法 | 返回值 | 行为 |
|---|---|---|
| | 推荐使用。 所有转账以 |
| | 所有转账以旧版类型返回。 |
| | 仅支持ESDT。若多笔转账中包含EGLD则触发panic。 |
Fixed-Count Arrays
固定数量数组
| Method | Returns | Behavior |
|---|---|---|
| | Exactly N transfers (any type). Panics if count != N. |
| | Exactly N ESDT transfers. Rejects EGLD. |
| | Exactly N transfers (legacy type). |
| 方法 | 返回值 | 行为 |
|---|---|---|
| | 恰好N笔转账(任意类型)。若数量不等于N则触发panic。 |
| | 恰好N笔ESDT转账。拒绝EGLD。 |
| | 恰好N笔转账(旧版类型)。 |
Deprecated — Do NOT Use
已废弃 — 请勿使用
| Deprecated | Replacement | Since |
|---|---|---|
| | v0.55 — doesn't handle multi-transfer EGLD properly |
| | v0.64 — legacy EGLD-or-multi split no longer meaningful |
| 已废弃方法 | 替代方案 | 废弃版本 |
|---|---|---|
| | v0.55 — 无法正确处理多笔转账的EGLD |
| | v0.64 — 旧版EGLD/多代币拆分已无意义 |
Common Patterns
常见模式
Single Payment Endpoint
单笔支付端点
rust
#[payable]
#[endpoint(deposit)]
fn deposit(&self) {
let payment = self.call_value().single();
// payment.token_identifier, payment.token_nonce, payment.amount (NonZeroBigUint)
self.deposits(&self.blockchain().get_caller())
.update(|total| *total += payment.amount.as_big_uint());
}rust
#[payable]
#[endpoint(deposit)]
fn deposit(&self) {
let payment = self.call_value().single();
// payment.token_identifier, payment.token_nonce, payment.amount (NonZeroBigUint)
self.deposits(&self.blockchain().get_caller())
.update(|total| *total += payment.amount.as_big_uint());
}EGLD-Only Endpoint
仅支持EGLD的端点
rust
#[payable("EGLD")]
#[endpoint(delegate)]
fn delegate(&self) {
let payment = self.call_value().egld().clone_value();
require!(payment >= MIN_EGLD, "Below minimum");
}rust
#[payable("EGLD")]
#[endpoint(delegate)]
fn delegate(&self) {
let payment = self.call_value().egld().clone_value();
require!(payment >= MIN_EGLD, "低于最低金额");
}Multi-Payment with Validation
带验证的多笔支付
rust
#[payable]
#[endpoint(repay)]
fn repay(&self) {
let payments = self.call_value().all();
for payment in payments.iter() {
require!(
self.is_accepted_token(&payment.token_identifier),
"Token not accepted"
);
self.process_repayment(&payment);
}
}rust
#[payable]
#[endpoint(repay)]
fn repay(&self) {
let payments = self.call_value().all();
for payment in payments.iter() {
require!(
self.is_accepted_token(&payment.token_identifier),
"代币不被接受"
);
self.process_repayment(&payment);
}
}Fixed Two-Token Swap
固定双代币兑换
rust
#[payable]
#[endpoint(addLiquidity)]
fn add_liquidity(&self) {
let [token_a, token_b] = self.call_value().array::<2>();
require!(token_a.token_identifier != token_b.token_identifier, "Same token");
}rust
#[payable]
#[endpoint(addLiquidity)]
fn add_liquidity(&self) {
let [token_a, token_b] = self.call_value().array::<2>();
require!(token_a.token_identifier != token_b.token_identifier, "不能是相同代币");
}Optional Payment (Claim or Deposit)
可选支付(领取或存入)
rust
#[payable]
#[endpoint(interact)]
fn interact(&self) {
match self.call_value().single_optional() {
Some(payment) => self.handle_deposit(&payment),
None => self.handle_claim(),
}
}rust
#[payable]
#[endpoint(interact)]
fn interact(&self) {
match self.call_value().single_optional() {
Some(payment) => self.handle_deposit(&payment),
None => self.handle_claim(),
}
}EGLD-or-ESDT with Token Routing
EGLD或ESDT的代币路由
rust
#[payable]
#[endpoint(addRewards)]
fn add_reward(&self) {
let payment = self.call_value().egld_or_single_esdt();
let pool = self.pool_for_token(&payment.token_identifier);
self.tx().to(&pool)
.typed(PoolProxy)
.deposit()
.payment(payment)
.returns(ReturnsResult)
.sync_call();
}rust
#[payable]
#[endpoint(addRewards)]
fn add_reward(&self) {
let payment = self.call_value().egld_or_single_esdt();
let pool = self.pool_for_token(&payment.token_identifier);
self.tx().to(&pool)
.typed(PoolProxy)
.deposit()
.payment(payment)
.returns(ReturnsResult)
.sync_call();
}Payment to Transfer Syntax
支付转转账语法
rust
// Transfer single Payment to caller
let caller = self.blockchain().get_caller();
self.tx().to(&caller).payment(payment.clone()).transfer();
// Transfer EGLD
self.tx().to(&caller).egld(&amount).transfer();rust
// 将单笔Payment转账给调用者
let caller = self.blockchain().get_caller();
self.tx().to(&caller).payment(payment.clone()).transfer();
// 转账EGLD
self.tx().to(&caller).egld(&amount).transfer();Anti-Patterns
反模式
rust
// BAD: using deprecated egld_value — misses EGLD in multi-transfer
let value = self.call_value().egld_value(); // ← DEPRECATED since v0.55
// GOOD: use egld() which handles both direct and multi-transfer
let value = self.call_value().egld();
// BAD: using any_payment — legacy split
let payment = self.call_value().any_payment(); // ← DEPRECATED since v0.64
// GOOD: use all() for uniform handling
let payments = self.call_value().all();
// BAD: not validating token before processing
let payment = self.call_value().single();
self.do_something(&payment); // What if wrong token?
// GOOD: validate token identity
let payment = self.call_value().single();
require!(
payment.token_identifier == self.accepted_token().get(),
"Wrong token"
);rust
// 错误:使用已废弃的egld_value — 会遗漏多笔转账中的EGLD
let value = self.call_value().egld_value(); // ← 自v0.55起已废弃
// 正确:使用egld(),支持直接转账和多笔转账
let value = self.call_value().egld();
// 错误:使用any_payment — 旧版拆分方式
let payment = self.call_value().any_payment(); // ← 自v0.64起已废弃
// 正确:使用all()进行统一处理
let payments = self.call_value().all();
// 错误:处理前未验证代币
let payment = self.call_value().single();
self.do_something(&payment); // 如果是错误代币怎么办?
// 正确:验证代币合法性
let payment = self.call_value().single();
require!(
payment.token_identifier == self.accepted_token().get(),
"错误代币"
);