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 · Rust 包仓库 1914/week @ 2024-04-29 · Rust 包仓库 1844/week @ 2024-05-06 · Rust 包仓库 1925/week @ 2024-05-13 · Rust 包仓库 2098/week @ 2024-05-20 · Rust 包仓库 2017/week @ 2024-05-27 · Rust 包仓库 1857/week @ 2024-06-03 · Rust 包仓库 1712/week @ 2024-06-10 · Rust 包仓库 2052/week @ 2024-06-17 · Rust 包仓库 1940/week @ 2024-06-24 · Rust 包仓库 668/week @ 2024-07-01 · Rust 包仓库 882/week @ 2024-07-08 · Rust 包仓库 1598/week @ 2024-07-15 · Rust 包仓库 1526/week @ 2024-07-22 · Rust 包仓库 1328/week @ 2024-07-29 · Rust 包仓库 1327/week @ 2024-08-05 · Rust 包仓库 1352/week @ 2024-08-12 · Rust 包仓库

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