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

Download history 1914/week @ 2024-04-29 1844/week @ 2024-05-06 1925/week @ 2024-05-13 2098/week @ 2024-05-20 2017/week @ 2024-05-27 1857/week @ 2024-06-03 1712/week @ 2024-06-10 2052/week @ 2024-06-17 1940/week @ 2024-06-24 668/week @ 2024-07-01 882/week @ 2024-07-08 1598/week @ 2024-07-15 1526/week @ 2024-07-22 1328/week @ 2024-07-29 1327/week @ 2024-08-05 1352/week @ 2024-08-12

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

请参阅 typedoc 文档.

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