操作请求
Token 和 Coin 的主要区别在于,默认情况下,Token 不允许转账、兑换或支出。但是,有一种授权机制允许执行这些操作。这种机制称为 ActionRequest
。Token 的创建者可以选择允许/禁止任何操作(参见 请求确认)。
受保护的操作
Token 有 4 个受保护的操作,它们会创建一个 ActionRequest
:
Function | Action Name | Description | Special Fields in ActionRequest |
---|---|---|---|
token::from_coin | "from_coin" | Convert a Coin into a Token | - |
token::to_coin | "to_coin" | Convert a Token into a Coin | - |
token::transfer | "transfer" | Transfer a Token to a recipient | Contains "recipient" field |
token::spend | "spend" | Spend a Token | Contains "spent_balance" field |
ActionRequest 结构
ActionRequest 在 sui::token
模块中定义,包含以下字段:
name
- 执行的操作的名称,标准操作有 "transfer"、"spend"、"to_coin" 和 "from_coin",还可以创建自定义操作。amount
- 正在转账、支出、兑换等的 Token 数量。sender
- 启动操作的账户。recipient
- 在 "transfer" 操作中接收 Token 的账户(可以用于自定义操作)。spent_balance
- 在 "spend" 操作 中花费的 Token 的余额(可以在自定义操作中使用)。
这些字段可以由所谓的 "Rules" 使用,以确定是否应允许该操作。规则是实现限制逻辑的自定义模块。有关更多详细信息,请参见 "Rules" 部分。
创建 ActionRequest
的函数示例:
// module: sui::token
public fun transfer<T>(
t: Token<T>, recipient: address, ctx: &mut TxContext
): ActionRequest<T>;
Request Confirmation
有 3 种确认 ActionRequest
的方法:
- 使用
TreasuryCap
- Token 创建者(或存储 TreasuryCap 的应用程序)可以通过调用token::confirm_with_treasury_cap
函数来确认任何请求。此方法适用于存储 TreasuryCap 并实现自定义逻辑的应用程序;它还允许 Token 创建者绕过限制进行mint
和transfer
。 - 使用
TokenPolicy
- Token 创建者可以创建共享的TokenPolicy
并设置每个操作的允许操作和要求。这样,任何应用程序或钱包都会知道哪些操作可以被视为 "public" 并且可以执行它们。 - 使用
TokenPolicyCap
- 管理TokenPolicy
的能力也可以用来确认请求。这对于包装了 TreasuryCap 并且无法访问的应用程序以及需要由 Token 创建者授权的一些管理员操作非常有用。
TokenPolicyCap 不能用于确认 "spend" 请求(参见 Spending)
使用 TreasuryCap 确认
TreasuryCap 可以用于确认 Token 的任何 ActionRequest。它适用于管理员操作(例如 mint 和 transfer),以及对 TreasuryCap 进行包装并成为主对象的简单应用程序。
token::confirm_with_treasury_cap
函数的方法签名如下:
// module: sui::token
public fun confirm_with_treasury_cap<T>(
treasury_cap: &mut TreasuryCap<T>,
request: ActionRequest<T>,
ctx: &mut TxContext
): (String, u64, address, Option<address>);
以下是使用 TypeScript 和 sui.js 实现的一个示例交易,使用 TreasuryCap 确认一个 ActionRequest
。在这里,TreasuryCap 属于管理员账户,并用于铸币以及确认 Token 的转账请求:
let tx = new TransactionBlock();
let tokenType = '0x....::my_token::MY_TOKEN';
let treasuryCapArg = tx.object('0x....');
// mint a 10 tokens using the TreasuryCap
let token = tx.moveCall({
target: '0x2::token::mint',
arguments: [ treasuryCapArg, tx.pure.u64(10) ],
typeArguments: [ tokenType ],
});
// transfer the token to a recipient; receive an `ActionRequest`
let request = tx.moveCall({
target: '0x2::token::transfer',
arguments: [ token, tx.pure.address('0x...') ],
typeArguments: [ tokenType ],
});
// confirm the request with the TreasuryCap
tx.moveCall({
target: '0x2::token::confirm_with_treasury_cap',
arguments: [ treasuryCapArg, request ],
typeArguments: [ tokenType ],
});
// submit the transaction
// ...
使用 TokenPolicy 确认
TokenPolicy 是在整个网络上启用某些操作的一种方式。一旦共享,TokenPolicy
就对所有人都可用。因此,它可以被钱包或任何其他客户端用来确认允许的操作。
token::confirm_request
函数的方法签名如下:
// module: sui::token
public fun confirm_request<T>(
treasury_cap: &TokenPolicy<T>,
request: ActionRequest<T>,
ctx: &mut TxContext
): (String, u64, address, Option<address>);
如果是 "spend" 请求,请使用 confirm_request_mut
函数。
JavaScript 中一个客户端转账请求确认的示例:
let tx = new TransactionBlock();
let tokenType = '0x....::my_token::MY_TOKEN';
let myTokenArg = tx.object('0x...token_object');
let receiverArg = tx.pure.address('0x...receiver');
let tokenPolicyArg = tx.object('0x...token_policy');
let request = tx.moveCall({
target: '0x2::token::transfer',
arguments: [ myTokenArg, receiverArg ],
typeArguments: [ tokenType ],
});
// expecting the `TokenPolicy` to have the `transfer` operation allowed
tx.moveCall({
target: '0x2::token::confirm_request',
arguments: [ tokenPolicyArg, request ],
typeArguments: [ tokenType ],
});
// submit the transaction
// ...
使用 TokenPolicyCap 确认
TokenPolicyCap 也可以用于确认操作请求。当 TreasuryCap
被包装在另一个对象中,并且 TokenPolicy
不允许某些操作或具有使默认确认方式无法进行的规则时,这种方式可能会很方便。
TokenPolicyCap 不能用于确认 "spend" 请求(参见 Spending)
// module: sui::token
public fun confirm_with_policy_cap<T>(
token_policy_cap: &TokenPolicyCap<T>,
request: ActionRequest<T>,
ctx: &mut TxContext
): (String, u64, address, Option<address>);
JavaScript 中一个客户端转账请求确认的示例:
let tx = new TransactionBlock();
let tokenType = '0x....::my_token::MY_TOKEN';
let myTokenArg = tx.object('0x...token_object');
let receiverArg = tx.pure.address('0x...receiver');
let tokenPolicyCapArg = tx.object('0x...token_policy_cap');
let request = tx.moveCall({
target: '0x2::token::transfer',
arguments: [ myTokenArg, receiverArg ],
typeArguments: [ tokenType ],
});
// confirming the request with the TokenPolicyCap
tx.moveCall({
target: '0x2::token::confirm_with_policy_cap',
arguments: [ tokenPolicyCapArg, request ],
typeArguments: [ tokenType ],
});
// submit the transaction
// ...
批准操作
ActionRequest
可以收集 "批准" - 应用程序或 Rules 提供的见证印章。它们携带了一个确定的模块或规则已经批准了该操作的确认。这个机制允许在特定要求后面设定操作门槛。
token::add_approval
函数的签名如下:
// module: sui::token
public fun add_approval<T, W: drop>(
_t: W, request: &mut ActionRequest<T>, _ctx: &mut TxContext
);
大多数情况下,批准主要用于 Rules。然而,它们可以携带来自任何模块的确认。
创建自定义请求
任何人都可以使用 token::new_request
函数创建新的 ActionRequest
。它可用于创建与 Token
本身无关的自定义操作和规则。
由于 ActionRequest
可以自由地为任何类型 T 创建,它们不能作为操作的 "证明" 使用。它们的目的是 授权,而不是证明。
token::new_request
函数的方法签名如下:
public fun new_request<T>(
name: vector<u8>,
amount: u64,
recipient: option<address>,
spent_balance: option<Balance<T>>,
ctx: &mut TxContext
): ActionRequest<T>;