结构体类型参数中的 Coin 类型顺序
在之前的市场示例中,我们创建了类型为 Listing<ListedCoin, PaymentCoin>
的市场列表。
然后 buy_coins
函数需要 Listing<ListedCoin, PaymentCoin>
作为参数,以知道买家想要从哪个列表中购买。
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<MyCoin, SUI>
创建了一个列表,但买家弄错了顺序,将列表指定为 Listing<Sui, MyCoin>
。那么会发生什么呢?
当这个交易发送到 Sui 网络时,验证层会报错,因为在提供的相同地址上,Listing<SUI, MyCoin>
是无效的(SUI 是列出的代币,而 MyCoin 是支付代币)。
不必担心这会意外指向另一个 SUI 的列表,因为每个对象的地址是唯一的,并且只能有一种类型。如果另一个卖家确实创建了 Listing<SUI, MyCoin>
,这个列表对象的地址会有所不同。
这是用户尤其是在复杂市场/交易所中常遇到的一个常见问题,因为这些市场/交易所具有许多不同类型的列表,涉及不同的代币对。
与我们上面举的 Listing
示例不同,在 Sui 的大多数交易所中,代币的顺序实际上对核心对象本身并不重要。
public struct Market<phantom Coin1, phantom Coin2> has key {
id: UID,
reserves_1: Balance<Coin1>,
reserves_2: Balance<Coin2>,
}
在这个市场中,Coin1 和 Coin2 具有相似的角色——买家可以用 Coin2 购买 Coin1,或者用 Coin1 购买 Coin2。 在这里,顺序技术上并不重要,但不幸的是,当 Market 对象被创建时,我们仍然需要选择哪个 Coin 是第一个类型(Coin1)。 在后台,对象仍然按照 Market<Coin1, Coin2> 的顺序创建。因此,当用户与这个市场互动时,他们仍然需要按照创建时的正确顺序指定代币。
为了让用户更容易使用,一个解决方案是确保在创建 Market 对象时,Coin1 和 Coin2 也按字母顺序排序。 然而,由于缺乏字符串比较功能,这在 Sui Move 中目前还不容易实现。