高级:处理多种代币类型
在前一课中,我们实现了一个简单的代币市场,卖家列出他们想要出售的代币,买家可以使用 SUI 自动从列表中购买,而无需卖家参与或签署交易。
public struct Listing<phantom CoinType> has key {
id: UID,
seller: address,
listed_coins: Balance<CoinType>,
amount_asked: u64,
}
public fun buy_coins<CoinType>(listing: Listing<CoinType>, payment: Coin<SUI>): Balance<CoinType> {
let Listing<CoinType> { id, seller, listed_coins, amount_asked } = listing;
object::delete(id);
assert!(coin::value(&payment) == amount_asked, EINSUFFICIENT_PAYMENT);
transfer::public_transfer(payment, seller);
listed_coins
}
如果我们希望卖家指定他们接受哪种代币作为支付,而不是总是要求使用 Sui,该怎么办? 这意味着我们必须将两种不同类型的代币作为类型参数用于 Listing 对象和 buy_coins 函数:
public struct Listing<phantom ListedCoin, phantom PaymentCoin> has key {
id: UID,
seller: address,
listed_coins: Balance<ListedCoin>,
amount_asked: u64,
}
public fun buy_coins<ListedCoin, PaymentCoin>(listing: Listing<ListedCoin, PaymentCoin>, payment: Coin<PaymentCoin>): Balance<CoinType> {
let Listing<ListedCoin, PaymentCoin> { id, seller, listed_coins, amount_asked } = listing;
object::delete(id);
assert!(coin::value(&payment) == amount_asked, EINSUFFICIENT_PAYMENT);
transfer::public_transfer(payment, seller);
listed_coins
}
我们做了以下更改:
- Listing 现在有两种单独的(虚拟)类型。我们可以为类型命名,这里我们使名称非常明确,以免混淆类型——ListedCoin 和 PaymentCoin。
- buy_coins 现在也接受两种代币类型。支付现在必须是
Coin<PaymentCoin>
类型,而不是Coin<SUI>
。 - buy_coins 内部的逻辑变化不大。我们不需要明确验证支付是否为正确的类型,因为支付代币的类型已经在
Listing<ListedCoin, PaymentCoin>
中直接指定。买家必须提供正确的支付代币,否则他们根本无法调用此函数! - 类型正确性由 Move 编译器和 Sui VM 在执行交易时保证。
在非常复杂的情况下,例如允许在多种不同代币类型之间进行 3 方或 4 方交易的交易所,您可能会看到非常长的函数,例如:
public fun buy_coins<Coin1, Coin2, Coin3, Coin4>(...) {
}
CoinTypes 必须始终显式声明为类型参数。如果我们希望使用上面的函数来支持 1 种、2 种、3 种或 4 种类型的交易,这可能会导致一些复杂性,因为用户在调用此函数时不能将缺失的类型留空。为了解决这个问题,可以定义一个“Null”代币类型,用户在不需要时可以传递这个类型:
use std::type_name;
public struct Null has drop {}
public fun buy_coins<Coin1, Coin2, Coin3, Coin4>(...) {
if (type_name::into_string<Coin3>() == type_name::into_string<Null>()) {
// Coin3 is not specified and should be ignored
}
}
如上所示,开发人员可以使用 type_name::into_string
来比较类型,以确定哪些类型未被指定。