Kiosk 标准 - 转移策略

Kiosk 有一个非常强大的功能,允许 NFT 创建者制定其 NFT 交易的规则,例如我们在之前课程中看到的版税:转移策略 (TransferPolicy)。 在前面的课程中,我们看到购买 NFT 首先会返回一个不能丢弃的 TransferRequest:

public fun purchase<T: key + store>(
    self: &mut Kiosk, id: ID, payment: Coin<SUI>
): (T, TransferRequest<T>) {...}

为了处理这个 TransferRequest,用户需要调用 sui::transfer_policy::confirm_request

/// 允许类型 `T` 的 `TransferRequest`。该调用受类型约束保护,因为只有 `T` 的发布者才能获得 `TransferPolicy<T>`。  
///注意:除非 `T` 有允许转移的策略,否则 Kiosk 交易将无法进行。

public fun confirm_request<T>(
    self: &TransferPolicy<T>, request: TransferRequest<T>
): (ID, u64, ID) {
    let TransferRequest { item, paid, from, receipts } = request;
    let completed = vec_set::into_keys(receipts);
    let total = vector::length(&completed);

    assert!(total == vec_set::size(&self.rules), EPolicyNotSatisfied);

    while (total > 0) {
        let rule_type = vector::pop_back(&mut completed);
        assert!(vec_set::contains(&self.rules, &rule_type), EIllegalRule);
        total = total - 1;
    };

    (item, paid, from)
}

如你所见,transfer_policy::confirm_request 会检查请求中是否满足所有“规则”。 请注意,confirm_request 需要一个 TransferPolicy,而这个 TransferPolicy 只能通过调用 transfer_policy::new 来获得,正如我们在之前的版税课程中看到的那样:

use nft_protocol::royalty;
use nft_protocol::royalty_strategy_bps;
use ob_permissions::witness;
use std::string::{Self, String};
use sui::package;

/// 可用于创建后授权其他操作。至关重要的是,这个结构体不能随意提供给任何合约,因为它充当授权令牌。
public struct Witness has drop {}

fun init(otw: KITE, ctx: &mut TxContext) {
    let (collection, mint_cap) =
        collection::create_with_mint_cap<KITE, KiteNFT>(&otw, option::none(), ctx);
    let delegated_witness = witness::from_witness(Witness {});
     collection::add_domain(
        delegated_witness,
        &mut collection,
        display_info::new(
            string::utf8(b"Kites"),
            string::utf8(b"A NFT collection of Kites on Sui"),
        ),
    );
 
    // 定义版税
    let shares = vector[100];
    let creator = tx_context::sender(ctx);
    let shares = utils::from_vec_to_map(creator, shares);
    // 100 BPS (Basis points) == 1%
    royalty_strategy_bps::create_domain_and_add_strategy(
        delegated_witness, &mut collection, royalty::from_shares(shares, ctx), 100, ctx,
    );    

    // 确保版税能轻易被执行
    let publisher = package::claim(otw, ctx);
    let (transfer_policy, transfer_policy_cap) =
        transfer_request::init_policy<KiteNFT>(&publisher, ctx);
    royalty_strategy_bps::enforce(&mut transfer_policy, &transfer_policy_cap);
}

这意味着 TransferPolicy 只能由创建者创建,因此创建者可以将其存储在某个地方,并添加一个在检查版税已支付后确认/解决 TransferRequest 的函数。 他们还可以通过调用 transfer_policy::add_rule 添加更多规则。

public struct TransferPolicyHolder<phantom T> has key {
    id: UID,
    transfer_policy: TransferPolicy<T>,
}

public fun confirm_request(holder: &TransferPolicyHolder, request: TransferRequest<T>) {
    // Verify our rules and add receipts for each confirmed rule to the request.
    
    transfer_policy::confirm_request(&holder.transfer_policy, request);
}