대체 가능 토큰 만들기: Currency Standard
Currency Standard를 사용해 만든 asset은 모든 앱에 저장할 수 있는 free-flowing, wrappable, freely transferable 대체 가능 asset이다. Currency Standard는 metadata를 위해 sui::coin_registry module과 Currency object를 사용한다.
Currency<T> type은 open-loop 대체 가능 token을 나타낸다. Currency는 type parameter T로 표시되며, T는 모든 Currency<T> instance에 적용되는 metadata(예: 이름, symbol, decimal precision)와도 연결된다. sui::coin_registry module은 Currency<T>를 fungible로 취급하는 interface를 제공한다. 즉, 전통적인 법정화폐가 동작하는 방식과 유사하게 한 Currency<T> instance에 보관된 T 단위는 다른 어떤 T 단위와도 상호 교환 가능하다.
Coin Registry
Coin Registry system은 sui::coin_registry module을 통해 currency 관리를 위한 중앙화된 접근 방식을 제공한다. Registry는 주소 0xc에 있는 shared object이며, 등록된 모든 coin type의 metadata, supply information, regulatory status를 저장한다.
creation option
Registry는 두 가지 coin creation flow를 지원한다:
- Standard creation (
coin_registry::new_currency): 대부분의 경우 권장된다. coin type을 게시한 뒤 언제든 이 method를 호출할 수 있다. - One-Time Witness creation (
coin_registry::new_currency_with_otw): uniqueness proof에 One-Time Witness를 사용한다. publish와 finalize의 두 단계 process가 필요하다.
두 creation method는 모두 추가 구성을 허용하는 CurrencyInitializer<T>를 반환한다:
- Regulated tokens: deny list capability를 추가해 token을 regulated하게 만든다.
- Supply model: fixed, burn-only, flexible supply 중에서 선택한다.
- Extensions: custom functionality를 위한 추가 field를 포함한다.
/// Hot potato wrapper to enforce registration after "new_currency" data creation.
/// Destroyed in the `finalize` call and either transferred to the `CoinRegistry`
/// (in case of an OTW registration) or shared directly (for dynamically created
/// currencies).
public struct CurrencyInitializer<phantom T> {
currency: Currency<T>,
extra_fields: Bag,
is_otw: bool,
}
standard creation(권장)
coin type이 게시된 뒤 언제든 new_currency function을 사용한다. 이 function은 shared Currency<T> object를 만들며, type T는 key-only type이어야 한다(public struct MyCoin has key { id: UID }):
#[allow(lint(self_transfer))]
public fun new_currency(registry: &mut CoinRegistry, ctx: &mut TxContext): Coin<MyCoin> {
let (mut currency, mut treasury_cap) = coin_registry::new_currency(
registry,
6, // Decimals
b"MyCoin".to_string(), // Symbol
b"My Coin".to_string(), // Name
b"Standard Unregulated Coin".to_string(), // Description
b"https://example.com/my_coin.png".to_string(), // Icon URL
ctx,
);
let total_supply = treasury_cap.mint(TOTAL_SUPPLY, ctx);
currency.make_supply_burn_only(treasury_cap);
let metadata_cap = currency.finalize(ctx);
transfer::public_transfer(metadata_cap, ctx.sender());
total_supply
}
finalize function을 호출하면 currency creation이 완료된다:
#[allow(lint(share_owned))]
/// Finalize the coin initialization, returning `MetadataCap`
public fun finalize<T>(builder: CurrencyInitializer<T>, ctx: &mut TxContext): MetadataCap<T>
One-Time Witness 생성
올바른 생성과 RPC support를 위해서는 currency를 registry로 promote하는 두 번째 transaction이 필요하다.
One-Time Witness creation으로 새 coin을 만드는 과정은 두 단계 process이다. Initialization process는 package publication으로 시작한다:
fun init(witness: MY_COIN_NEW, ctx: &mut TxContext) {
let (builder, treasury_cap) = coin_registry::new_currency_with_otw(
witness,
6, // Decimals
b"MY_COIN".to_string(), // Symbol
b"My Coin".to_string(), // Name
b"Standard Unregulated Coin".to_string(), // Description
b"https://example.com/my_coin.png".to_string(), // Icon URL
ctx,
);
let metadata_cap = builder.finalize(ctx);
transfer::public_transfer(treasury_cap, ctx.sender());
transfer::public_transfer(metadata_cap, ctx.sender());
}
그다음 coin_registry::finalize_registration을 호출해 coin을 registry에 배치한다:
# Requires the ID of the Currency object created during publishing.
# This step is only required for OTW-created currencies.
sui client ptb \
--assign @created_currency_object_id currency_to_promote \
--move-call 0x2::coin_registry::finalize_registration <CURRENCY_TYPE> @0xc currency_to_promote
/// The second step in the "otw" initialization of coin metadata, that takes in
/// the `Currency<T>` that was transferred from init, and transforms it in to a
/// "derived address" shared object.
///
/// Can be performed by anyone.
public fun finalize_registration<T>(
registry: &mut CoinRegistry,
currency: Receiving<Currency<T>>,
_ctx: &mut TxContext,
)