Core Candy MachineからアセットをミントするためのWebサイトを作成する
SolanaでCore NFTコレクションをローンチする場合、通常はユーザーがアセットを購入できるCandy Machineを使用します。ユーザーフレンドリーなエクスペリエンスを提供するために、Webサイトを用意することをお勧めします。このガイドでは、独自のミント機能を構築する方法に焦点を当てます。また、Candy Machineからデータを取得して、例えばミント可能な残り数量を表示する方法も示します。
このガイドは、完全なWebサイトの実装ではなく、コアとなるCandy Machineの機能と対話に焦点を当てています。Webサイトにボタンを追加したり、ウォレットアダプターと統合したりする方法は含まれていません。代わりに、Core Candy Machineを操作するための重要な情報を提供します。
UIエレメントやウォレット統合を含む完全なWebサイト実装については、metaplex-nextjs-tailwind-templateのようなテンプレートから始めることをお勧めします。このテンプレートには、Wallet Adapterのようなコンポーネントの多くのセットアップ手順が含まれています。
一般的なWeb開発の実践方法や特定のフレームワークの使用方法に関するガイダンスをお探しの場合は、Visual Studio Codeなどのツールが豊富なドキュメントとコミュニティリソースを提供しています。
前提条件
- すでに作成されたCandy Machine。作成方法の詳細はこちらをご覧ください。
- Web開発と選択したフレームワークの基本的な知識。umiとの互換性を最も簡単にするために、Next JSをお勧めします。
必要なパッケージ
選択したテンプレートや実装に関係なく、Core Candy Machineと対話するために以下のパッケージをインストールする必要があります:
npm i @metaplex-foundation/umi @metaplex-foundation/umi-bundle-defaults @metaplex-foundation/mpl-core-candy-machine
オンチェーンデータの取得
環境のセットアップ後、Candy Machineに焦点を当てることができます。ミントUIは通常、以下のようなデータを表示したいと考えます:
- すでにミントされたアセットの数
- Candy Machine内のアセットの数
- ミント開始までの時間
- アセットの価格
- その他
ユーザーには表示されないが、バックグラウンドの計算で使用される追加データを取得することも理にかなっています。たとえば、Redeemed Amount Guardを使用する場合、すでに償還された数量を取得して、ユーザーがさらにミントできるかどうかを確認する必要があります。
Candy Machineデータの取得
Candy Machineアカウントには、利用可能および償還されたアセットの数などのデータが保存されています。また、mintAuthorityも保存されており、これは通常、Candy Guardのアドレスです。
Candy Machineを取得するには、以下のようにfetchCandyMachine関数を使用できます:
Solana Devnetエンドポイントを使用します。
import {
mplCandyMachine,
fetchCandyMachine,
} from "@metaplex-foundation/mpl-core-candy-machine";
import { createUmi } from "@metaplex-foundation/umi-bundle-defaults";
// 次の2行は、以前にumiをセットアップしていない場合のみ必要です
// エンドポイントとしてSolana Devnetを使用します
const umi = createUmi("https://api.devnet.solana.com")
.use(mplCandyMachine());
const candyMachineId = "Ct5CWicvmjETYXarcUVJenfz3CCh2hcrCM3CMiB8x3k9";
const candyMachine = await fetchCandyMachine(umi, publicKey(candyMachineId));
console.log(candyMachine)
これは以下のようなCandy Machineデータを返します:
JSON Result
UI観点から最も重要なフィールドは、itemsRedeemed、itemsAvailable、およびmintAuthorityです。場合によっては、Webサイトにティーザー画像としてitemsのいくつかを表示することも興味深いかもしれません。
残りのアセット数を表示する
13 / 16 Assets mintedのようなセクションを表示するには、以下のようなものを使用します:
const mintedString = `${candyMachine.itemsRedeemed} / ${candyMachine.itemsAvailable} Assets minted`
3 availableのように残りのミント可能なアセットを取得したい場合は、代わりに以下のような計算を実行します:
const availableString = `${candyMachine.itemsAvailable - candyMachine.itemsRedeemed} available`;
Candy Guardデータの取得
Candy Guardには、ミントを許可するために満たさなければならない条件が含まれています。これには、例えばSolまたはTokenの支払い、1つのウォレットがミントできるアセットの数の制限などが含まれます。Candy Guardの詳細については、Candy Guardページをご覧ください。
Candy Machineデータと同様に、guardアカウントを取得することは必須ではありません。そうすることで、Candy GuardのSOL価格を更新するだけで、Webサイトの数値も自動的に更新されるような柔軟性が得られます。
複数のCandy Machineに使用できるより柔軟なUIを構築したい場合、Candy Guardを取得することで、ミント機能の構築と適格性の動的なチェックの両方が可能になります。
次のスニペットは、candyMachineアカウントが以前に取得されていることを前提としています。または、candyMachine.mintAuthorityの代わりにCandy GuardのpublicKeyをハードコーディングすることもできます。
import { safeFetchCandyGuard } from "@metaplex-foundation/mpl-core-candy-machine";
const candyGuard = await safeFetchCandyGuard(umi, candyMachine.mintAuthority);
JSON Result
追加のCandy Machine関連アカウントの取得
実装するGuardの選択により、追加のアカウントを取得する必要がある場合があります。たとえば、ウォレットのミント適格性を確認する予定があり、mintLimit Guardを使用している場合は、mintCounterアカウントを取得する必要があります。このアカウントは、特定のウォレットがその特定のguard下でミントしたNFTの数を記録しています。
MintLimitアカウント
MintLimit guardがアクティブな場合、ユーザーのウォレットのMintCounterアカウントを取得することをお勧めします。これにより、ユーザーがミント制限に達しているか、まだ追加のアイテムをミントできるかどうかを確認できます。
以下のコードスニペットは、MintCounterを取得する方法を示しています。この例は、すでにCandy MachineとCandy Guardデータを取得していることを前提としています:
import { safeFetchMintCounterFromSeeds } from "@metaplex-foundation/mpl-core-candy-machine";
const mintCounter = await safeFetchMintCounterFromSeeds(umi, {
id: 1, // Guard設定で設定したmintLimit id
user: umi.identity.publicKey,
candyMachine: candyMachine.publicKey,
candyGuard: candyMachine.mintAuthority,
});
NftMintLimitアカウント
MintLimit guardと同様に、NftMintLimit guardのNftMintCounterアカウントを取得して適格性を確認することが理にかなっています。
以下のコードスニペットは、NftMintCounterアカウントを取得する方法を示しています。この例は、すでにCandy MachineとCandy Guardデータを取得していることを前提としています:
import {
findNftMintCounterPda,
fetchNftMintCounter
} from "@metaplex-foundation/mpl-core-candy-machine";
const pda = findNftMintCounterPda(umi, {
id: 1, // Guard設定で設定したnftMintLimit id
mint: asset.publicKey, // ユーザーが所有するnftのアドレス
candyGuard: candyMachine.mintAuthority,
candyMachine: candyMachine.publicKey,
});
const nftMintCounter = fetchNftMintCounter(umi, pda)
AssetMintLimitアカウント
NftMintCounter guardと同様に、AssetMintLimit guardのAssetMintCounterアカウントを取得して適格性を確認することが理にかなっています。
以下のコードスニペットは、AssetMintCounterアカウントを取得する方法を示しています。この例は、すでにCandy Machineデータを取得していることを前提としています:
import {
findAssetMintCounterPda,
fetchAssetMintCounter
} from "@metaplex-foundation/mpl-core-candy-machine";
const pda = findAssetMintCounterPda(umi, {
id: 1, // Guard設定で設定したassetMintLimit id
asset: asset.publicKey, // ユーザーが所有するcore nftのアドレス
candyGuard: candyMachine.mintAuthority,
candyMachine: candyMachine.publicKey,
});
const assetMintCounter = fetchAssetMintCounter(umi, pda);
Allocationアカウント
Allocation guardの場合、AllocationTrackerアカウントを取得して、特定のグループから追加のNFTをミントできることを確認することが理にかなっています。
以下のコードスニペットは、AllocationTrackerアカウントを取得する方法を示しています。この例は、すでにCandy Machineデータを取得していることを前提としています:
import {
safeFetchAllocationTrackerFromSeeds,
} from "@metaplex-foundation/mpl-core-candy-machine";
const allocationTracker = await safeFetchAllocationTrackerFromSeeds(umi, {
id: 1, // Guard設定で設定したallocation id
candyMachine: candyMachine.publicKey,
candyGuard: candyMachine.mintAuthority,
});
Allowlistアカウント
Allowlist guardを実装する場合、事前にroute命令を実行することが重要です。この命令は、各ウォレットとCandy Machineの組み合わせに対して一意のアカウントを生成し、ウォレットがミントを承認されていることを効果的にマークします。
UI観点から、このアカウントを照会することは有益です。これにより、route命令を実行する必要があるか、ユーザーがミント命令に直接進めるかを判断できます。
以下のコードスニペットは、このアカウントを取得する方法を示しています。すでにCandy Machineデータを取得していることを前提としていますが、望む場合は、代わりにcandyGuardとcandyMachineのpublicKeyをハードコーディングすることもできます。
import {
safeFetchAllowListProofFromSeeds,
getMerkleRoot,
} from "@metaplex-foundation/mpl-core-candy-machine";
const allowlist = [
"Tes1zkZkXhgTaMFqVgbgvMsVkRJpq4Y6g54SbDBeKVV",
"GjwcWFQYzemBtpUoN5fMAP2FZviTtMRWCmrppGuTthJS",
"AT8nPwujHAD14cLojTcB1qdBzA1VXnT6LVGuUd6Y73Cy"
];
const allowListProof = await safeFetchAllowListProofFromSeeds(umi, {
candyGuard: candyMachine.mintAuthority,
candyMachine: candyMachine.publicKey,
merkleRoot: getMerkleRoot(allowlist),
user: umi.identity.publicKey,
});
ウォレットデータの取得
適格性を検証するために、接続されたウォレットに関する情報を取得することもできます。使用しているGuardに応じて、ウォレット内のSOLの量、ウォレットが所有しているTokenとNFTを知りたい場合があります。
SOL残高を取得するには、組み込みのgetAccount umi関数を使用してウォレットアカウントを取得できます:
const account = await umi.rpc.getAccount(umi.identity.publicKey);
const solBalance = account.lamports;
TokenまたはNFTを必要とするguardのいずれかを使用している場合は、それらも取得することをお勧めします。このためにはDAS APIを使用することをお勧めします。DASは、RPCプロバイダーによって維持されるTokenのインデックスです。これを使用すると、すべての必要な情報を1回の呼び出しで取得できます。UIでは、返されたオブジェクトを使用して、接続されたウォレットが必要なトークンまたはNFTを所有しているかどうかを確認できます。
import { publicKey } from '@metaplex-foundation/umi';
import { dasApi } from '@metaplex-foundation/digital-asset-standard-api';
// 以前のどこかでumiインスタンスを定義するときに、
// すでに`.use(dasApi());`を追加できるので、umiを再度定義する必要はありません。
const umi = createUmi('<ENDPOINT>').use(dasApi());
const assets = await umi.rpc.getAssetsByOwner({
umi.identity.publicKey
});
適格性の検証
すべての必要なデータを取得した後、接続されたウォレットがミントを許可されているかどうかを確認できます。
Candy Machineにグループが添付されている場合、default guardsは作成されたすべてのグループに普遍的に適用されることに注意することが重要です。また、グループが有効になっている場合、defaultグループからミントする機能は無効になり、作成されたグループをミントに使用する必要があります。
したがって、グループが定義されていない場合は、defaultグループのすべてのミント条件が満たされているかどうかを確認する必要があります。グループが定義されている場合は、default guardsと現在のミントグループのguardsの両方を検証する必要があります。
グループを活用していないstartDate、SolPayment、およびmintLimit guardsが添付されたCandy Machineが与えられた場合、ユーザーがミント関数を呼び出すことを許可する前に、次の検証を行う必要があります。candyGuardが以前に取得されており、1つのCore NFTアセットをミントすることを前提としています。
startDateが過去であることを検証します。ここではユーザーのデバイス時間を使用せず、代わりに現在の内部Solanaブロックタイムをフェッチしています。これは、Candy Machineがミント時の検証に使用する時間だからです:
import { unwrapOption } from '@metaplex-foundation/umi';
let allowed = true;
// 現在のスロットを取得してブロックタイムを読み取ります
const slot = await umi.rpc.getSlot();
let solanaTime = await umi.rpc.getBlockTime(slot);
// `default` startDate guardが添付されているかを確認します
const startDate = unwrapOption(candyGuard.guards.startDate);
if (startDate) {
// startTimeが将来にあることを検証します
if (solanaTime < startDate) {
console.info(`StartDate not reached!`);
allowed = false;
}
}
- ウォレットがミントの支払いに十分なSOLを持っているかを確認します。ここでは取引手数料を含めておらず、
SolBalanceが上記のように取得されていると仮定しています。
import { unwrapOption } from '@metaplex-foundation/umi';
const solPayment = unwrapOption(candyGuard.guards.solPayment);
if (solPayment){
if (solPayment.lamports.basisPoints > solBalance){
console.info(`Not enough SOL!`);
allowed = false;
}
}
mintLimitにまだ達していないことを確認します:
import { unwrapOption } from '@metaplex-foundation/umi';
import {
safeFetchMintCounterFromSeeds,
} from "@metaplex-foundation/mpl-core-candy-machine";
const mintLimit = unwrapOption(candyGuard.guards.mintLimit);
if (mintLimit){
const mintCounter = await safeFetchMintCounterFromSeeds(umi, {
id: mintLimit.id,
user: umi.identity.publicKey,
candyMachine: candyMachine.publicKey,
candyGuard: candyMachine.mintAuthority,
});
// mintCounter PDAが存在する(最初のミントではない)
if (mintCounter && mintLimit.limit >= mintCounter.count
) {
allowed = false;
}
}
ウォレットがミント不適格な場合、ミントボタンを無効にして、ミント不適格の理由をユーザーに表示すると役立ちます。例えば、Not enough SOL!メッセージなど。
Guard Routes
特定のGuardは、ミントが発生する前に実行する必要がある特定の命令を必要とします。これらの命令は、データを保存するアカウントを作成するか、ウォレットのミント適格性の証明を提供します。これらの命令の実行頻度は、Guardのタイプによって異なります。
このセクションの対象者
Allocation、FreezeSolPayment、FreezeTokenPayment、またはAllowlist guardを使用していない場合は、このセクションをスキップしても安全です。
一部のGuardは、Candy Machine全体に対して一度だけルートを実行する必要があります。これらについては、UIに関数を含める必要はありませんが、スクリプトを介して一度事前に実行できます:
他のGuardは、個々のウォレットごとにルートを実行する必要があります。これらの場合、ルート命令はミントトランザクションの前に実行する必要があります:
Guardルートの実装方法の例として、Allowlist guardのケースを考えてみましょう。これは、allowListProofが前述のように取得されており、allowlistが適格なウォレットアドレスの配列を表していることを前提としています。以下のコードは、実装でこのシナリオを処理する方法を示しています。
import {
getMerkleRoot,
getMerkleProof,
route
} from "@metaplex-foundation/mpl-core-candy-machine";
import {
publicKey,
} from "@metaplex-foundation/umi";
// 上記で説明したようにAllowListProofを取得したと仮定します
if (allowListProof === null) {
route(umi, {
guard: "allowList",
candyMachine: candyMachine.publicKey,
candyGuard: candyMachine.mintAuthority,
group: "default", // ここにguardラベルを追加します
routeArgs: {
path: "proof",
merkleRoot: getMerkleRoot(allowlist),
merkleProof: getMerkleProof(allowlist, publicKey(umi.identity)),
},
})
}
ミント関数の作成
添付されているすべてのguardの適格性チェックを実装することをお勧めします。グループが添付されている場合、default guardsがすべての追加グループに適用される一方で、同時にdefaultグループが無効になることに注意してください。
これらのチェックが完了し、必要に応じてルート命令が実行された後、ミントトランザクションを構築できます。Guardに応じて、mintArgsを渡す必要がある場合があります。これらは、正しいアカウントとデータを渡すことでミントトランザクションを構築するのに役立つ引数です。たとえば、mintLimit guardはmintCounterアカウントを必要とします。Umi SDKはこれらの詳細を抽象化しますが、トランザクションを正しく構築するためには、いくつかの情報が必要です。
再びstartDate、SolPayment、およびmintLimit Guardsが添付されたCandy Machineを仮定して、mintArgsを構築する方法を見てみましょう。
import { some, unwrapOption } from '@metaplex-foundation/umi';
import {
DefaultGuardSetMintArgs
} from "@metaplex-foundation/mpl-core-candy-machine";
let mintArgs: Partial<DefaultGuardSetMintArgs> = {};
// solPayment mintArgsを追加します
const solPayment = unwrapOption(candyGuard.guards.solPayment)
if (solPayment) {
mintArgs.solPayment = some({
destination: solPayment.destination,
});
}
// mintLimit mintArgsを追加します
const mintLimit = unwrapOption(candyGuard.guards.mintLimit)
if (mintLimit) {
mintArgs.mintLimit = some({ id: mintLimit.id });
}
すべてのGuardが追加のmintArgsを渡す必要があるわけではありません。これが、上記のコードスニペットにstartDateがない理由です。使用しているguardsがmintArgsを渡す必要があるかどうかを理解するには、Developer Hub Guardページを確認することをお勧めします。「Mint Settings」が記述されている場合、このguardに対してmintArgsを渡す必要があります。
mintArgsが構築されたので、ミント関数自体を呼び出す方法を見てみましょう。次のスニペットは、candyMachineとcandyGuardが上記のように取得されていることを前提としています。技術的には、candyMachine、collection、candyGuardのpublicKeyとすべてのmintArgsは、取得したくない場合は手動で渡すこともできます。
// NFTアドレスを生成します
const nftMint = generateSigner(umi);
await mintV1(umi, {
candyMachine: candyMachine.publicKey,
collection: candyMachine.collectionMint,
asset: nftMint,
candyGuard: candyGuard.publicKey,
mintArgs,
}).sendAndConfirm(umi)
console.log(`NFT ${nftMint.publicKey} minted!`)
高度なミントテクニック
これまでに説明した基本的なミント関数はほとんどのケースでうまく機能しますが、ミントプロセスを強化するために使用できる高度なテクニックがいくつかあります。これらのいくつかを探ってみましょう:
1つのトランザクションで複数のNFTをミントする
効率性のために、ユーザーが1つのトランザクションで複数のNFTをミントできるようにすることができます。これを実現する方法は次のとおりです:
特定の設定に応じて、Transaction Buildersを組み合わせることで、1つのトランザクションで複数のNFTをミントできるようにすると役立つ場合があります。
let builder = transactionBuilder()
.add(mintV1(...))
.add(mintV1(...))
トランザクションにmintV1命令を追加しすぎると、Transaction too largeエラーが発生します。関数builder.fitsInOneTransaction(umi)を使用すると、トランザクションを送信する前にこれを確認できるため、送信前にトランザクションを分割できます。分割が必要な場合は、signAllTransactionsを使用することをお勧めします。これにより、Wallet Adapterで承認する必要があるポップアップが1つだけになります。
Guard Groups
Guard groupsは、異なる設定で複数のguardセットを定義できるCore Candy Machineの強力な機能です。以下のようなシナリオで特に役立ちます:
- 階層化されたミント:VIP、早期アクセス、一般販売用の異なるグループ。
- 複数の支払いオプション:SOL支払い、SPLトークン支払いなどのグループ。
- 時間ベースのミント:異なる開始日と終了日を持つグループ。
- Allowlistベースのミント:Allowlistユーザーと一般販売用のグループ。
UIでguard groupsを実装するには、2つの主なアプローチがあります:
複数のボタンアプローチ: 各グループに個別のボタンを作成し、ユーザーが好みのミントオプションを選択できるようにします。
自動グループ選択: ユーザーの適格性と現在の条件に基づいて、ユーザーに最適なグループを決定する関数を実装します。
選択するシナリオまたはアプローチに関係なく、特定のグループで機能するようにmintV1命令を調整する方法は次のとおりです。主な変更は、目的のラベルを指定するgroupパラメーターを含めることです。
// NFTアドレスを生成します
const nftMint = generateSigner(umi);
await mintV1(umi, {
candyMachine: candyMachine.publicKey,
collection: candyMachine.collectionMint,
asset: nftMint,
candyGuard: candyGuard.publicKey,
mintArgs,
group: "group1",
}).sendAndConfirm(umi)
console.log(`NFT ${nftMint.publicKey} minted!`)
次のステップ
フロントエンドでCandy Machineと対話する基本をマスターしたので、プロジェクトをさらに強化および配布するために、次のステップを検討することをお勧めします:
ホスティング:ホスティングプラットフォームにデプロイして、ユーザーがフロントエンドにアクセスできるようにします。開発者の間で人気のあるオプションには次のものがあります:
- Vercel
- Cloudflare Pages
- Netlify
- GitHub Pages
テスト:スムーズなユーザーエクスペリエンスを確保するために、さまざまなデバイスとブラウザでUIを徹底的にテストします。
最適化:特にミントイベント中に高トラフィックが予想される場合は、パフォーマンスのためにフロントエンドを微調整します。
モニタリング:Candy Machine UIのステータスを追跡し、発生する可能性のある問題に迅速に対処するために、監視ツールを設定します。
これらの領域に焦点を当てることで、Core Candy Machineを使用して成功したNFTミントプロジェクトを立ち上げ、維持する準備が整います。
