본문으로 건너뛰기

Kiosk join pattern

이 reference implementation은 tokenized asset이 정의된 policy 안에서 동작하도록 Kiosk standard를 사용한다. 이 implementation을 그대로 사용하면 royalty와 commission 같은 rule을 지원하는 marketable tokenized asset을 만들 수 있다.

Kiosk가 requirement가 아니라면 unlock module과 transfer policy 관련 proxy method를 제외할 수 있다.

PTB 안에서 unlock functionality를 여러 step으로 구현하는 대신, asset의 purchase, borrowing, unlocking, joining을 하나의 function에서 수행하는 method를 만들 수 있다. 다음 example은 join operation이 어떤 형태인지 보여준다:

public fun kiosk_join<T>(
kiosk: &mut Kiosk,
kiosk_cap: &KioskOwnerCap,
protected_tp: &ProtectedTP<TokenizedAsset<T>>,
ta1_id: ID,
ta2_id: ID,
ctx: &mut TxContext
) {

kiosk::list<TokenizedAsset<T>>(kiosk, kiosk_cap, ta2_id, 0);
let (ta1, promise_ta1) = kiosk::borrow_val(kiosk, kiosk_cap, ta1_id);
let coin = coin::zero<SUI>(ctx);
let (ta2, request) = kiosk::purchase(kiosk, ta2_id, coin);

let tp_ref = proxy::transfer_policy(protected_tp);
let (_item, _paid, _from) = transfer_policy::confirm_request(
tp_ref,
request
);

tokenized_asset::join(&mut ta1, ta2);

kiosk::return_val(kiosk, ta1, promise_ta1);
}

사용 사례에 맞춘 example 변경

주의

다음 example은 AssetCap<T>를 두 개의 새 object인 Treasury<T>AdminCap<T>로 split하여 사실상 대체한다. 이 변경은 원치 않는 effect를 만들 수 있으므로 original package에 정의된 method 접근 방식을 신중하게 다시 설계해야 한다. 필요한 re-design 전체가 이 example에 모두 포함되어 있지는 않으며, demonstration 목적으로 일부 method만 변경한다.

admin뿐 아니라 user도 asset을 burn할 수 있게 하려는 상황을 가정한다. 이는 여전히 authorized operation이어야 하지만, use case-specific purpose를 위해 tokenized asset을 consume할 수 있는 flexibility를 제공한다(예: 모아 둔 collectible을 모두 burn하여 combine). 이를 위해 admin은 burn이 허용된 asset의 ID를 담은 ticket을 mint할 수 있다. 이 functionality를 지원하려면 smart contract를 redesign하고 admin을 각 asset의 asset treasury에서 분리해야 하며, treasury는 이제 supply-related information만 보유한다. 다음 sample change가 필요하다:

Struct

receiver가 trade할 수 없도록 key ability만 가진 ticket을 생성한다:

struct BurnTicket<phantom T> has key {
id: UID,
tokenized_asset_id: ID // 이 ticket이 burn access를 부여하는 tokenized asset
}

이제 treasury-related information만 보유하는 struct(AssetCap을 split한 결과이며, AssetCap은 더 이상 이 design의 일부가 아님)는 shared object로 생성된다. mint 같은 function이 Treasury object와 AdminCap object를 모두 input으로 받도록 변경한다:

struct Treasury<phantom T> has key, store {
id: UID,
supply: Supply<T>,
total_supply: u64,
}

AssetCap functionality의 나머지 절반은 admin capability와 burnability configuration을 유지한다. 이는 type <T>의 creator에게 전송되는 owned object이다:

struct AdminCap<phantom T> has key, store {
id: UID,
burnable: bool
}

Method signature

여기서 AdminCap은 admin capability이자 type insurance로 동작하며, 이 ticket으로 delete가 허용된 asset type 정보를 encode한다. 이 function은 asset T가 burnable임을 assert하고 BurnTicket<T>를 반환해야 한다:

public fun mint_burn_ticket<T>(
cap: &AdminCap<T>,
tokenized_asset_id: ID,
ctx: &mut TxContext
): BurnTicket

user side에서 burning하려면 shared Treasury object에 access해야 한다. 이 function은 tokenized asset을 burn하고 supply를 줄인다:

public fun burn_with_ticket<T>(
treasury: &mut Treasury<T>,
self: TokenizedAsset<T>,
ticket: BurnTicket<T>)