Hello Everyone..
Welcome to another day of exploring Web3 Engineering. In today's article, let us look into the transfer_policy
provided in the sui framework. So, without any further ado, let us get started.
Transfer Policy
As the name suggests, transfer policy is used to impose some rules and procedures while transferring objects in the sui blockchain. And also, if we want to list our assets in the sui kiosk system, our asset must have a transfer policy.
This transfer policies can be applied to the assets by using the transfer_policy
module provided by the sui framework as shown in the following code.
fun init(witness: NFT, ctx: &mut TxContext) {
let publisher = sui::package::claim(witness, ctx);
let (mut policy, policyCap) = sui::transfer_policy::new<Token>(&publisher, ctx);
followers
transfer::public_share_object(policy);
transfer::public_transfer(policyCap, tx_context::sender(ctx));
transfer::public_transfer(publisher, tx_context::sender(ctx));
}
The transfer_policy::new
is used to create a new policy and it requires 2 parameters and 1 type argument:
Reference to the publisher object of the package.
Mutable reference of the transaction context
Type of the asset to apply the policy.
The new function will return the TransferPolicy
object and TransferPolicyCap
objects. Usually the policy is shared and the policy cap is transferred to the package publisher. If the user wants to make any changes to the policy, the publisher have to use TransferPolicyCap
for authentication. By default the new
function will create a empty transfer policy with no rules.
How does the Transfer Policy Work ?
Transfer policy acts as a ticketing system and allocates a
TransferRequest<T>
which then further is used to follow all the rules applied in the policy.For completing a rule specified in the policy, it will attach a Receipt of the Rule.
Once all the receipts are received then we need to call the
confirm_request
function on the module.This function will confirm whether the request has completed all the rules and have receipts attached within or not and will then release our object.
Full code to integrate
/// Module: nft
module nft_transfer_policy::nft {
use std::ascii::String;
use sui::tx_context::sender;
use nft_transfer_policy::royalty;
public struct NFT has drop {}
public struct Token has key, store {
id: UID,
name: String,
}
#[allow(lint(share_owned))]
fun init(witness: NFT, ctx: &mut TxContext) {
let publisher = sui::package::claim(witness, ctx);
let (mut policy, policyCap) = sui::transfer_policy::new<Token>(&publisher,ctx);
royalty::add(&mut policy, &policyCap, 100u64);
transfer::public_share_object(policy);
transfer::public_transfer(policyCap, tx_context::sender(ctx));
transfer::public_transfer(publisher, tx_context::sender(ctx));
}
public entry fun mint(name: String, ctx: &mut TxContext) {
transfer::public_transfer(Token {
id: object::new(ctx),
name
}, sender(ctx));
}
}
In the above example, we have created a new transfer policy and attached a royalty rule to it Now let us look into writing the rules.
/// Module: royalty
module nft_transfer_policy::royalty {
use sui::sui::SUI;
use sui::coin::{Self, Coin};
use sui::transfer_policy::{Self as policy, TransferPolicyCap, TransferPolicy, TransferRequest};
const MAX_BP: u64 = 10_000;
const EInsufficientAmount: u64 = 0;
public struct RoyaltyRule has drop {}
public struct RoyaltyConfig has store, drop {
royalty: u64
}
public fun add<T>(
policy: &mut TransferPolicy<T>,
cap: &TransferPolicyCap<T>,
royalty: u64
) {
policy::add_rule(RoyaltyRule {}, policy, cap, RoyaltyConfig {royalty})
}
public fun pay<T>(
policy: &mut TransferPolicy<T>,
request: &mut TransferRequest<T>,
payment: &mut Coin<SUI>,
ctx: &mut TxContext
) {
let paid = policy::paid(request);
let config: &RoyaltyConfig = policy::get_rule(RoyaltyRule {}, policy);
let amount = (paid * config.royalty) / MAX_BP;
assert!(coin::value(payment) >= amount, EInsufficientAmount);
let fee = coin::split(payment, amount, ctx);
policy::add_to_balance(RoyaltyRule {}, policy, fee);
policy::add_receipt(RoyaltyRule {}, request)
}
}
In the above code,
RoyaltyRule - Type of the rule. It should only have drop ability and nothing else.
RoyaltyConfig - The config parameters to be stored for the policy. In our case, we have the royalty amount.
add() - A utility function that is used to add the rule to the policy object.
pay() - Function to pay the royalty fee.
If we look at the pay
function, it will first call the paid
function to get the amount paid and then it will calculate the amount and add into the policy. Once that is done, it will add the receipt to the request.
Once, all the receipts are added to the request, then we can call 0x2:sui:transfer_policy:confirm_request
to complete our request.