功能
特殊Guard指令
正如我们在前几页上看到的,guard是自定义Candy Machine铸造过程的强大方式。但您知道guard甚至可以提供自己的自定义指令吗?
Route指令
Candy Guard程序附带一个名为**"Route"指令**的特殊指令。
此指令允许我们从Candy Machine中选择特定的guard并运行特定于此guard的自定义指令。我们称其为"Route"指令,因为它会将我们的请求路由到选定的guard。
此功能使guard更加强大,因为它们可以附带自己的程序逻辑。它使guard能够:
- 将验证过程与铸造过程分离以进行繁重的操作。
- 提供否则需要部署自定义程序的自定义功能。
要调用route指令,我们必须指定要将该指令路由到哪个guard,以及提供它期望的route设置。请注意,如果我们尝试通过选择不支持它的guard来执行"route"指令,交易将失败。
由于每个在Candy Guard程序上注册的guard只能有一个"route"指令,因此通常在route设置中提供Path属性以区分同一guard提供的多个功能。
例如,添加对冻结NFT支持的guard——只有在铸造结束后才能解冻——可以使用其route指令来初始化国库托管账户,并允许任何人在正确条件下解冻铸造的NFT。我们可以通过使用Path属性来区分这两个功能,前者等于"init",后者等于"thaw"。
您将在支持它的每个guard的各自页面上找到route指令及其底层路径的详细说明。
让我们花一分钟通过提供一个示例来说明route指令的工作原理。例如,Allow List guard支持route指令,以验证铸造钱包是否属于预配置的钱包列表。
它使用Merkle树来实现这一点,这意味着我们需要创建整个允许钱包列表的哈希,并将该哈希(称为Merkle根)存储在guard设置中。为了证明钱包在允许列表中,它必须提供一个哈希列表(称为Merkle证明),允许程序计算Merkle根并确保它与guard的设置匹配。
因此,Allow List guard使用其route指令验证给定钱包的Merkle证明,如果成功,将在区块链上创建一个小的PDA账户,作为铸造指令的验证证明。
那么为什么我们不能直接在铸造指令中验证Merkle证明呢?这只是因为,对于大型允许列表,Merkle证明可能会相当冗长。在一定大小之后,将其包含在已经包含大量指令的铸造交易中变得不可能。通过将验证过程与铸造过程分开,我们使允许列表可以根据需要变大。
调用guard的route指令
您可以使用route函数通过Umi库调用guard的route指令。您需要通过guard属性传递guard的名称,并通过routeArgs属性传递其route设置。
以下是使用Allow List guard的示例,该guard在铸造前验证钱包的Merkle证明。
import {
create,
route,
getMerkleProof,
getMerkleRoot,
} from '@metaplex-foundation/mpl-candy-machine'
// 准备允许列表。
// 假设列表上的第一个钱包是Metaplex身份。
const allowList = [
'GjwcWFQYzemBtpUoN5fMAP2FZviTtMRWCmrppGuTthJS',
'2vjCrmEFiN9CLLhiqy8u1JPh48av8Zpzp3kNkdTtirYG',
'AT8nPwujHAD14cLojTcB1qdBzA1VXnT6LVGuUd6Y73Cy',
]
const merkleRoot = getMerkleRoot(allowList)
// 使用Allow List guard创建Candy Machine。
await create(umi, {
// ...
guards: {
allowList: some({ merkleRoot }),
},
}).sendAndConfirm(umi)
// 如果我们现在尝试铸造,它将失败,因为
// 我们没有验证我们的Merkle证明。
// 使用route指令验证Merkle证明。
await route(umi, {
candyMachine: candyMachine.publicKey,
guard: 'allowList',
routeArgs: {
path: 'proof',
merkleRoot,
merkleProof: getMerkleProof(
allowList,
'GjwcWFQYzemBtpUoN5fMAP2FZviTtMRWCmrppGuTthJS'
),
},
}).sendAndConfirm(umi)
// 如果我们现在尝试铸造,它将成功。
API参考:route, DefaultGuardSetRouteArgs
使用组的Route指令
在使用guard组时调用route指令时,重要的是指定我们希望选择的guard的组标签。这是因为我们可能在不同组之间有多个相同类型的guard,程序需要知道应该为route指令使用哪一个。
例如,假设我们在一个组中有一个精选VIP钱包的Allow List,在另一个组中有一个抽奖获胜者的Allow List。然后说我们想验证Allow List guard的Merkle证明是不够的,我们还需要知道应该为哪个组执行该验证。
调用route指令时按组过滤
使用组时,Umi库的route函数接受一个额外的group属性,类型为Option<string>,必须设置为我们想要选择的组的标签。
import {
create,
route,
getMerkleProof,
getMerkleRoot,
} from "@metaplex-foundation/mpl-candy-machine";
import { base58PublicKey, some } from "@metaplex-foundation/umi";
// 准备允许列表。
const allowListA = [...];
const allowListB = [...];
// 使用两个Allow List guard创建Candy Machine。
await create(umi, {
// ...
groups: [
{
label: "listA",
guards: {
allowList: some({ merkleRoot: getMerkleRoot(allowListA) }),
},
},
{
label: "listB",
guards: {
allowList: some({ merkleRoot: getMerkleRoot(allowListB) }),
},
},
],
}).sendAndConfirm(umi);
// 通过指定要选择的组来验证Merkle证明。
await route(umi, {
candyMachine: candyMachine.publicKey,
guard: 'allowList',
group: some('listA'), // <- 我们正在使用"allowListA"进行验证。
routeArgs: {
path: 'proof',
merkleRoot: getMerkleRoot(allowListA),
merkleProof: getMerkleProof(
allowListA,
base58PublicKey(umi.identity),
),
},
}).sendAndConfirm(umi);
API参考:route, DefaultGuardSetRouteArgs
结论
route指令通过允许guard附带自己的自定义程序逻辑,使guard变得更加强大。查看所有可用guard的专用页面,了解每个guard的完整功能集。
现在我们知道了关于设置Candy Machine及其guard的所有内容,是时候谈论铸造了。在下一页见!
