可用守卫

允许列表守卫

概述

Allow List 守卫根据预定义的钱包列表验证铸造钱包。如果铸造钱包不在此列表中,铸造将失败。

在此守卫的设置中提供大量钱包列表将需要在区块链上存储大量数据,并且可能需要多个交易来插入它们。因此,Allow List 守卫使用 Merkle Trees 来验证铸造钱包是否属于预配置的钱包列表。

这通过创建一个哈希二叉树来工作,其中所有叶子两两哈希直到我们到达称为 Merkle Root 的最终哈希。这意味着如果任何叶子发生变化,最终的 Merkle Root 将被破坏。

要验证叶子是否属于树的一部分,我们只需要一个所有中间哈希的列表,使我们能够沿着树向上走并重新计算 Merkle Root。我们称这个中间哈希列表为 Merkle Proof。如果计算的 Merkle Root 与存储的 Merkle Root 匹配,我们可以确定该叶子是树的一部分,因此是原始列表的一部分。

因此,Allow List 守卫的设置需要一个 Merkle Root,它作为预配置的允许钱包列表的真实来源。要让钱包证明它在允许列表中,它必须提供有效的 Merkle Proof,允许程序重新计算 Merkle Root 并确保它与守卫的设置匹配。

注意我们的 SDK 提供辅助函数,使为给定的钱包列表创建 Merkle Root 和 Merkle Proof 变得容易。

守卫设置

Allow List 守卫包含以下设置:

  • Merkle Root:代表允许列表的 Merkle Tree 的根。

使用 Allowlist 守卫设置 Candy Machine

为了帮助我们管理 Merkle Trees,Umi 库提供了两个辅助方法 getMerkleRootgetMerkleProof,您可以这样使用。

import {
getMerkleProof,
getMerkleRoot,
} from "@metaplex-foundation/mpl-core-candy-machine";
const allowList = [
"Ur1CbWSGsXCdedknRbJsEk7urwAvu1uddmQv51nAnXB",
"GjwcWFQYzemBtpUoN5fMAP2FZviTtMRWCmrppGuTthJS",
"AT8nPwujHAD14cLojTcB1qdBzA1VXnT6LVGuUd6Y73Cy",
];
const merkleRoot = getMerkleRoot(allowList);
const validMerkleProof = getMerkleProof(
allowList,
"Ur1CbWSGsXCdedknRbJsEk7urwAvu1uddmQv51nAnXB"
);
const invalidMerkleProof = getMerkleProof(allowList, "invalid-address");

一旦我们计算出允许列表的 Merkle Root,我们就可以用它在 Candy Machine 上设置 Allow List 守卫。

import { getMerkleRoot } from "@metaplex-foundation/mpl-core-candy-machine";
const allowList = [
"Ur1CbWSGsXCdedknRbJsEk7urwAvu1uddmQv51nAnXB",
"GjwcWFQYzemBtpUoN5fMAP2FZviTtMRWCmrppGuTthJS",
"AT8nPwujHAD14cLojTcB1qdBzA1VXnT6LVGuUd6Y73Cy",
];
create(umi, {
// ...
guards: {
allowList: some({ merkleRoot: getMerkleRoot(allowList) }),
},
});

API 参考:createAllowList

铸造设置

Allow List 守卫包含以下铸造设置:

  • Merkle Root:代表允许列表的 Merkle Tree 的根。

注意,在能够铸造之前,我们必须通过提供 Merkle Proof 来验证铸造钱包。详情请参阅下面的验证 Merkle Proof

另外注意,如果您计划在没有我们 SDK 帮助的情况下构建指令,您需要将 Allow List Proof PDA 添加到铸造指令的剩余账户中。详情请参阅 Candy Guard 的程序文档

使用 Allow List 守卫铸造

您可以使用 mintArgs 参数传递 Allow List 守卫的铸造设置,如下所示。

import { getMerkleRoot } from "@metaplex-foundation/mpl-core-candy-machine";
const allowList = [
"Ur1CbWSGsXCdedknRbJsEk7urwAvu1uddmQv51nAnXB",
"GjwcWFQYzemBtpUoN5fMAP2FZviTtMRWCmrppGuTthJS",
"AT8nPwujHAD14cLojTcB1qdBzA1VXnT6LVGuUd6Y73Cy",
];
mintV1(umi, {
// ...
mintArgs: {
allowList: some({ merkleRoot: getMerkleRoot(allowList) }),
},
});

API 参考:mintV1AllowListMintArgs

Route 指令

Allow List 的 route 指令支持以下功能。

验证 Merkle Proof

路径:proof

铸造钱包必须使用 Allow List 守卫的 route 指令执行预验证,而不是直接将 Merkle Proof 传递给铸造指令。

此 route 指令将从提供的 Merkle Proof 计算 Merkle Root,如果有效,将创建一个新的 PDA 账户作为铸造钱包属于允许列表的证明。因此,在铸造时,Allow List 守卫只需要检查此 PDA 账户的存在即可授权或拒绝钱包铸造。

那么为什么我们不能直接在铸造指令中验证 Merkle Proof 呢?这仅仅是因为,对于大型允许列表,Merkle Proof 可能会变得相当长。超过一定大小后,就不可能将其包含在已经包含相当数量指令的铸造交易中。通过将验证过程与铸造过程分离,我们使允许列表可以根据需要变大。

此 route 指令路径接受以下参数:

  • Path = proof:选择在 route 指令中执行的路径。
  • Merkle Root:代表允许列表的 Merkle Tree 的根。
  • Merkle Proof:应用于计算 Merkle Root 并验证它与守卫设置中存储的 Merkle Root 匹配的中间哈希列表。
  • Minter(可选):如果铸造者与付款人不同,则作为签名者的铸造者账户。提供时,此账户必须属于允许列表,证明才有效。

预验证钱包

您可以使用 routeArgs 参数传递 Allow List 守卫的 "Proof" Route 设置,如下所示。

import {
getMerkleProof,
getMerkleRoot,
} from "@metaplex-foundation/mpl-core-candy-machine";
import { publicKey } from "@metaplex-foundation/umi";
const allowList = [
"Ur1CbWSGsXCdedknRbJsEk7urwAvu1uddmQv51nAnXB",
"GjwcWFQYzemBtpUoN5fMAP2FZviTtMRWCmrppGuTthJS",
"AT8nPwujHAD14cLojTcB1qdBzA1VXnT6LVGuUd6Y73Cy",
];
await route(umi, {
// ...
guard: "allowList",
routeArgs: {
path: "proof",
merkleRoot: getMerkleRoot(allowList),
merkleProof: getMerkleProof(allowList, publicKey(umi.identity)),
},
}).sendAndConfirm(umi);

umi.identity 钱包现在被允许从 Candy Machine 铸造。

API 参考:routeAllowListRouteArgs

允许列表账户

当使用 Allowlist 守卫时,在运行 route 指令后会创建一个 AllowListProof 账户。当可以获取它时,用户在允许列表上且 route 已运行。出于验证目的,可以这样获取它:

import {
safeFetchAllowListProofFromSeeds,
getMerkleRoot,
} from "@metaplex-foundation/mpl-core-candy-machine";
const allowlist = [
"Tes1zkZkXhgTaMFqVgbgvMsVkRJpq4Y6g54SbDBeKVV",
"GjwcWFQYzemBtpUoN5fMAP2FZviTtMRWCmrppGuTthJS",
"AT8nPwujHAD14cLojTcB1qdBzA1VXnT6LVGuUd6Y73Cy"
];
const allowListProof = await safeFetchAllowListProofFromSeeds(umi, {
candyMachine: candyMachine.publicKey,
// 或者使用您的 CM 地址 candyMachine: publicKey("Address")
candyGuard: candyMachine.mintAuthority,
// 或者使用您的 candyGuard 地址 candyGuard: publicKey("Address")
merkleRoot: getMerkleRoot(allowlist),
user: umi.identity.publicKey,
// 或者 "铸造" 账户的 publicKey
});