Custom Guards
Generating Custom Guard Client
Once you have written your custom guard for the Candy Machine Guard program you'll need to generate a Kinobi client that works with the Umi SDK to for example be able to use your guard in a frontend.
Generate IDL and Initial Client
Configuring Shankjs
Shankjs is a IDL generator that works on both Anchor and non Anchor programs. You want to configure this with your new custom Candy Guard deployment key to properly generate a working client. Edit the file located at /configs/shank.cjs
in the mpl-candy-machine repo.
/configs/shank.cjs
generateIdl({
generator: "anchor",
programName: "candy_guard",
programId: "Guard1JwRhJkVH6XZhzoYxeBVQe872VH6QggF4BWmS9g", // Your custom Candy Guard deployed program key.
idlDir,
binaryInstallDir,
programDir: path.join(programDir, "candy-guard", "program"),
});
If you are generating using anchor 28 you will need to add a fallback in to Shankjs idl generator to anchor 27 due to a missing crates.io crate.
/configs/shank.cjs
generateIdl({
generator: "anchor",
programName: "candy_guard",
programId: "Guard1JwRhJkVH6XZhzoYxeBVQe872VH6QggF4BWmS9g", // Your custom Candy Guard deployed program key.
idlDir,
binaryInstallDir,
programDir: path.join(programDir, "candy-guard", "program"),
rustbin: {
locked: true,
versionRangeFallback: "0.27.0",
},
});
Generate IDL and Client
Now you should be able to generate the IDL and the initial client. From the root of the project run
pnpm run generate
this will in turn execute both scripts pnpm generate:idls
and pnpm generate:clients
and build out the intial clients. If you need to run these seperately for what ever reason you are able to do so.
Adding Guard(s) to the client
Create Guard File
Once a sucessful generation of the intial client is made navigate to /clients/js/src
.
The first step would be to add you new guard into the /clients/js/src/defaultGuards
folder.
Below is a template you could use and adjust to your needs based on the type of guard you have created. You can name your guard what ever you want but I'm going to name my example customGuard.ts
import { PublicKey } from '@metaplex-foundation/umi'
import {
getCustomGuardSerializer,
CustomGuard,
CustomGuardArgs,
} from '../generated'
import { GuardManifest, noopParser } from '../guards'
export const customGuardManifest: GuardManifest<
CustomGuardArgs,
CustomGuard,
CustomGuardMintArgs
> = {
name: 'customGuard',
serializer: getCustomGuardSerializer,
mintParser: (context, mintContext, args) => {
const { publicKeyArg1, arg1 } = args
return {
data: new Uint8Array(),
// Pass in any accounts needed for your custom guard from your mint args.
// Your guard may or may not need remaining accounts.
remainingAccounts: [
{ publicKey: publicKeyArg1, isWritable: true },
{ publicKey: publicKeyArg2, isWritable: false },
],
}
},
routeParser: noopParser,
}
// Here you would fill out any custom Mint args needed for your guard to operate.
// Your guard may or may not need MintArgs.
export type CustomGuardMintArgs = {
/**
* Custom Guard Mint Arg 1
*/
publicKeyArg1: PublicKey
/**
* Custom Guard Mint Arg 2
*/
publicKeyArg2: PublicKey
/**
* Custom Guard Mint Arg 3.
*/
arg3: Number
}
Add Guard to Existing Files
From here you need to add your new guard to some existing files.
Export your new guard from /clients/js/src/defaultGuards.index.ts
...
export * from './tokenGate';
export * from './tokenPayment';
export * from './token2022Payment';
// add your guard to the list
export * from './customGuard';
Within /clients/js/src/defaultGuards.defaults.ts
add your guard to these locations;
import { CustomGuardArgs } from "../generated"
export type DefaultGuardSetArgs = GuardSetArgs & {
...
// add your guard to the list
customGuard: OptionOrNullable<CustomGuardArgs>;
}
import { customGuard } from "../generated"
export type DefaultGuardSet = GuardSet & {
...
// add your guard to the list
customGuard: Option<CustomGuard>
}
import { CustomGuardMintArgs } from "./defaultGuards/customGuard.ts"
export type DefaultGuardSetMintArgs = GuardSetMintArgs & {
...
// add your guard to the list
customGuard: OptionOrNullable<CustomGuardMintArgs>
}
export const defaultCandyGuardNames: string[] = [
...// add your guard to the list
'customGuard',
]
Finally you need to add the exported customGuardManifest to the plugin file located at /clients/js/src/plugin.ts
import {customGuardManifest} from "./defaultGuards"
umi.guards.add(
...// add your guard manifest to the list
customGuardManifest
)
From this point you can build and upload your client package to npm or link/move it to your project folder where you would like to access the new guard client.
It is worth using the built in testing suite of AVA to write some tests that fully test your guard in multiple scenarios. Examples of tests can be found in /clients/js/tests
.