25 个版本 (8 个稳定版)
1.5.1 | 2024 年 7 月 25 日 |
---|---|
1.5.0 | 2024 年 2 月 7 日 |
1.4.3 | 2023 年 8 月 1 日 |
1.4.1 | 2023 年 6 月 16 日 |
0.2.1 | 2022 年 12 月 27 日 |
#2 in #metaplex
5,723 每月下载量
在 73 个软件包中使用 (9 个直接使用)
190KB
3.5K SLoC
Metaplex 令牌授权规则
一个程序,可以创建和执行规则以限制常见的令牌操作,例如转让和出售。
请在此处阅读官方文档 here.
概述
授权规则是实现了 Rule
枚举的 validate()
函数的变体。
有 原始规则 和 组合规则,它们是通过组合一个或多个原始规则创建的。
原始规则 存储任何用于评估的帐户或数据,并在运行时将根据帐户和传递到 validate()
函数的明确定义的 Payload
输出 true 或 false。
组合规则 根据任何或所有原始规则返回 true 或 false。组合规则可以组合成更高级的组合规则,以实现更复杂的布尔逻辑。由于 Rule
枚举的递归定义,对顶级组合规则调用 validate()
将从顶部开始并在每个级别进行验证,直到组件原始规则。
入门
以下软件包可以用于与令牌授权规则 (Token Auth Rules) 程序交互。
TypeScript
npm install @metaplex-foundation/mpl-token-auth-rules
Rust
cargo add mpl-token-auth-rules
构建
从存储库的根目录开始
- 安装所需的软件包
pnpm install
- 构建程序
pnpm programs:build
这将在 <ROOT>/programs/.bin
位置创建程序二进制文件。
测试
令牌认证规则测试包括三组测试:BPF、针对 JS 客户端的 TypeScript 以及基于 Solita 的 JS 客户端的旧版 TypeScript。
BPF
从存储库的根目录开始
pnpm programs:test
TypeScript
从存储库的根目录开始
pnpm validator
这将使用 Amman 启动一个本地验证器。
启动验证器后,进入文件夹 <ROOT>/clients/js
并运行
pnpm install
这将安装测试所需的包。然后,运行
pnpm build && pnpm test
旧版 TypeScript
按照与上述 TypeScript 测试相同的所有步骤进行,包括在根目录中启动验证器,但构建和运行测试时,请导航到文件夹 <ROOT>/clients/js-solita
。
CLI
文件夹 cli
包含一个 TypeScript CLI,用于管理规则集修订
Usage: auth [options] [command]
CLI for managing RuleSet revisions.
Options:
-V, --version output the version number
-h, --help display help for command
Commands:
create [options] creates a new rule set revision
convert [options] converts a rule set revision from V1 to V2
print [options] prints the latest rule set revision as a JSON object
help [command] display help for command
要启动 CLI,导航到文件夹 ./cli
并运行
$ yarn install
然后,CLI 可以用作
$ yarn start <COMMAND>
文件夹 ./examples
包含几个规则集的示例。您需要将 owner
的公钥值替换为您用于运行 CLI 的公钥。
请注意,您需要首先构建 JS SDK。
示例
Rust
注意:可以在 程序/tests 目录中找到额外的 Rust 示例。
use mpl_token_auth_rules::{
instruction::{
builders::{CreateOrUpdateBuilder, ValidateBuilder},
CreateOrUpdateArgs, InstructionBuilder, ValidateArgs,
},
payload::{Payload, PayloadType},
state::{CompareOp, Rule, RuleSetV1},
};
use num_derive::ToPrimitive;
use rmp_serde::Serializer;
use serde::Serialize;
use solana_client::rpc_client::RpcClient;
use solana_sdk::{
instruction::AccountMeta, native_token::LAMPORTS_PER_SOL, signature::Signer,
signer::keypair::Keypair, transaction::Transaction,
};
#[repr(C)]
#[derive(ToPrimitive)]
pub enum Operation {
OwnerTransfer,
Delegate,
SaleTransfer,
}
impl ToString for Operation {
fn to_string(&self) -> String {
match self {
Operation::OwnerTransfer => "OwnerTransfer".to_string(),
Operation::Delegate => "Delegate".to_string(),
Operation::SaleTransfer => "SaleTransfer".to_string(),
}
}
}
fn main() {
let url = "https://api.devnet.solana.com".to_string();
let rpc_client = RpcClient::new(url);
let payer = Keypair::new();
let signature = rpc_client
.request_airdrop(&payer.pubkey(), LAMPORTS_PER_SOL)
.unwrap();
loop {
let confirmed = rpc_client.confirm_transaction(&signature).unwrap();
if confirmed {
break;
}
}
// --------------------------------
// Create RuleSet
// --------------------------------
// Find RuleSet PDA.
let (rule_set_addr, _ruleset_bump) = mpl_token_auth_rules::pda::find_rule_set_address(
payer.pubkey(),
"test rule_set".to_string(),
);
// Additional signer.
let adtl_signer = Keypair::new();
// Create some rules.
let adtl_signer_rule = Rule::AdditionalSigner {
account: adtl_signer.pubkey(),
};
let amount_rule = Rule::Amount {
amount: 1,
operator: CompareOp::LtEq,
field: "Amount".to_string(),
};
let overall_rule = Rule::All {
rules: vec![adtl_signer_rule, amount_rule],
};
// Create a RuleSet.
let mut rule_set = RuleSetV1::new("test rule_set".to_string(), payer.pubkey());
rule_set
.add(Operation::OwnerTransfer.to_string(), overall_rule)
.unwrap();
println!("{:#?}", rule_set);
// Serialize the RuleSet using RMP serde.
let mut serialized_rule_set = Vec::new();
rule_set
.serialize(&mut Serializer::new(&mut serialized_rule_set))
.unwrap();
// Create a `create` instruction.
let create_ix = CreateOrUpdateBuilder::new()
.payer(payer.pubkey())
.rule_set_pda(rule_set_addr)
.build(CreateOrUpdateArgs::V1 {
serialized_rule_set,
})
.unwrap()
.instruction();
// Add it to a transaction.
let latest_blockhash = rpc_client.get_latest_blockhash().unwrap();
let create_tx = Transaction::new_signed_with_payer(
&[create_ix],
Some(&payer.pubkey()),
&[&payer],
latest_blockhash,
);
// Send and confirm transaction.
let signature = rpc_client.send_and_confirm_transaction(&create_tx).unwrap();
println!("Create tx signature: {}", signature);
// --------------------------------
// Validate Operation
// --------------------------------
// Create a Keypair to simulate a token mint address.
let mint = Keypair::new().pubkey();
// Store the payload of data to validate against the rule definition.
let payload = Payload::from([("Amount".to_string(), PayloadType::Number(1))]);
// Create a `validate` instruction with the additional signer.
let validate_ix = ValidateBuilder::new()
.rule_set_pda(rule_set_addr)
.mint(mint)
.additional_rule_accounts(vec![AccountMeta::new_readonly(adtl_signer.pubkey(), true)])
.build(ValidateArgs::V1 {
operation: Operation::OwnerTransfer.to_string(),
payload,
update_rule_state: false,
rule_set_revision: None,
})
.unwrap()
.instruction();
// Add it to a transaction.
let latest_blockhash = rpc_client.get_latest_blockhash().unwrap();
let validate_tx = Transaction::new_signed_with_payer(
&[validate_ix],
Some(&payer.pubkey()),
&[&payer, &adtl_signer],
latest_blockhash,
);
// Send and confirm transaction.
let signature = rpc_client
.send_and_confirm_transaction(&validate_tx)
.unwrap();
println!("Validate tx signature: {}", signature);
}
JavaScript
注意:可以在 /cli/ 源代码中找到额外的 JS 示例,以及 /cli/examples/ 中的示例规则集。
import { createCreateInstruction, createTokenAuthorizationRules, PREFIX, PROGRAM_ID } from './helpers/mpl-token-auth-rules';
import { Keypair, Connection, PublicKey, Transaction, SystemProgram } from '@solana/web3.js';
import {
findRuleSetPDA,
getRuleSetRevisionFromJson,
serializeRuleSetRevision,
} from '@metaplex-foundation/mpl-token-auth-rules';
export const findRuleSetPDA = async (payer: PublicKey, name: string) => {
return await PublicKey.findProgramAddress(
[
Buffer.from(PREFIX),
payer.toBuffer(),
Buffer.from(name),
],
PROGRAM_ID,
);
}
export const createTokenAuthorizationRules = async (
connection: Connection,
payer: Keypair,
name: string,
data: Uint8Array,
) => {
const ruleSetAddress = await findRuleSetPDA(payer.publicKey, name);
let createIX = createCreateOrUpdateInstruction(
{
payer: payer.publicKey,
ruleSetPda: ruleSetAddress[0],
systemProgram: SystemProgram.programId,
},
{
createOrUpdateArgs: {__kind: "V1", serializedRuleSet: data },
},
PROGRAM_ID,
)
const tx = new Transaction().add(createIX);
const { blockhash } = await connection.getLatestBlockhash();
tx.recentBlockhash = blockhash;
tx.feePayer = payer.publicKey;
const sig = await connection.sendTransaction(tx, [payer]);
await connection.confirmTransaction(sig, "finalized");
return ruleSetAddress[0];
}
const connection = new Connection("<YOUR_RPC_HERE>", "finalized");
let payer = Keypair.generate()
const revision = getRuleSetRevisionFromJson(JSON.parse(fs.readFileSync("./examples/v2/pubkey-list-match.json")));
// Create the rule set revision
await createTokenAuthorizationRules(connection, payer, name, serializeRuleSetRevision(revision));
依赖项
~22–31MB
~513K SLoC