ID指针
ID指针是一种将主要数据(对象)与其访问器/功能分离的技术,通过将后者与原始数据链接起来。有几种不同的方式可以使用这种模式:
- 发行共享对象的可转让功能(例如,更改共享对象的'所有者'字段的TransferCap)
- 拆分动态数据和静态数据(例如,NFT及其集合信息)
- 在通用应用程序中避免不必要的类型链接(和见证要求)(LiquidityPool的LP代币)
以下示例在Sui上实现了基本的Lock
和Key
机制,其中Lock<T>
是一个共享对象,可以包含任何对象,而Key
是一个拥有的对象,需要获取对锁内容的访问权限。
Key
通过一个ID
字段与其Lock
关联。这种检查允许在链下发现目标,同时拆分动态可转让的功能和“静态”内容。这种方法的另一个好处是目标资产始终是可发现的,同时你可以将其Key
包装到另一个对象中(例如市场上的列表)。
module examples::lock_and_key {
use sui::object::{Self, ID, UID};
use sui::transfer;
use sui::tx_context::{Self, TxContext};
use std::option::{Self, Option};
/// Lock is empty, nothing to take.
const ELockIsEmpty: u64 = 0;
/// Key does not match the Lock.
const EKeyMismatch: u64 = 1;
/// Lock already contains something.
const ELockIsFull: u64 = 2;
/// Lock that stores any content inside it.
struct Lock<T: store + key> has key {
id: UID,
locked: Option<T>
}
/// A key that is created with a Lock; is transferable
/// and contains all the needed information to open the Lock.
struct Key<phantom T: store + key> has key, store {
id: UID,
for: ID,
}
/// Returns an ID of a Lock for a given Key.
public fun key_for<T: store + key>(key: &Key<T>): ID {
key.for
}
/// Lock some content inside a shared object. A Key is created and is
/// sent to the transaction sender. For example, we could turn the
/// lock into a treasure chest by locking some `Coin<SUI>` inside.
///
/// Sender gets the `Key` to this `Lock`.
public fun create<T: store + key>(obj: T, ctx: &mut TxContext) {
let id = object::new(ctx);
let for = object::uid_to_inner(&id);
transfer::share_object(Lock<T> {
id,
locked: option::some(obj),
});
transfer::transfer(Key<T> {
for,
id: object::new(ctx)
}, tx_context::sender(ctx));
}
/// Lock something inside a shared object using a Key. Aborts if
/// lock is not empty or if key doesn't match the lock.
public fun lock<T: store + key>(
obj: T,
lock: &mut Lock<T>,
key: &Key<T>,
) {
assert!(option::is_none(&lock.locked), ELockIsFull);
assert!(&key.for == object::borrow_id(lock), EKeyMismatch);
option::fill(&mut lock.locked, obj);
}
/// Unlock the Lock with a Key and access its contents.
/// Can only be called if both conditions are met:
/// - key matches the lock
/// - lock is not empty
public fun unlock<T: store + key>(
lock: &mut Lock<T>,
key: &Key<T>,
): T {
assert!(option::is_some(&lock.locked), ELockIsEmpty);
assert!(&key.for == object::borrow_id(lock), EKeyMismatch);
option::extract(&mut lock.locked)
}
/// Unlock the Lock and transfer its contents to the transaction sender.
public fun take<T: store + key>(
lock: &mut Lock<T>,
key: &Key<T>,
ctx: &mut TxContext,
) {
transfer::public_transfer(unlock(lock, key), tx_context::sender(ctx))
}
}
这个模式在以下示例中使用: