本文介绍了如何在 Monad 测试网上使用 Blinks 创建 NFT 交互界面, 简化了 NFT 的铸造过程,用户可以通过点击链接,直接在钱包中签署交易完成 NFT 铸造。文章详细说明了 Blinks 的架构,并提供了在 Monad 链上搭建 NFT 铸造界面的步骤。
Monad 是一个高性能、EVM 兼容的 Layer 1 区块链,旨在提供快速的交易、低廉的费用和可扩展性。 现在,将其与 Blinks 结合起来,Blinks 是与链上交互相关的可嵌入链接,你就拥有了一个强大的组合,可以吸引大众。
在本指南中,我们将引导你在 Monad 测试网上使用 Blinks 创建 NFT 界面。 首先,我们将介绍 Blinks 的架构设置方式,然后向你展示如何更新 Monad scaffold 存储库,以包括你的 NFT 铸造界面(可在你的网站或社交媒体上共享)。 让我们开始吧!
Monad 由 Monad Labs 于 2022 年推出,专为高吞吐量(高达每秒 10,000 笔交易)和低成本交易(低于 1 美分)而设计。 它利用并行执行和优化的共识机制来增强可扩展性,同时保持与生态系统中流行的以太坊工具的兼容性。 这使其成为利用区块链的不同类型应用(如 NFT 和 DeFi)的理想平台。
Blinks 由 Dialect Labs 开发,代表 Blockchain Links。Blinks 是可共享的 URL,它将链上操作(如铸造 NFT)捆绑到一个可点击的链接中。 这消除了复杂集成的需要,允许开发人员在任何支持 URL 的地方(例如,社交媒体、网站)嵌入区块链交互。 对于用户而言,Blinks 通过一次点击简化了与区块链的交互,直接在他们的钱包中呈现一个可签名的交易。 自 2025 年 2 月起,Blinks 已在 Monad 的测试网上可用。
Blinks 通过具有以下功能的提供者-客户端架构运行:
Blinks 的核心功能是通过一个由两部分组成的 API 系统:
当首次访问 Blink URL 时,客户端会向提供者发送 GET
请求。提供者返回有关 Blink 的元数据,例如:
此元数据允许客户端为你的 Blink 呈现一致的界面,而无需任何自定义集成。
当用户与 Blink 交互时(例如,单击“Mint NFT”按钮):
POST
请求发送给提供者这种关注点分离允许交易逻辑保留在你的服务器上,而客户端处理钱包交互和 UI 呈现。
注意
请注意,本指南中提供的信息基于 Blinks 规范的当前状态。随着技术的发展,可能会引入新的功能和能力。请返回此处或查看 官方规范 以获取最新信息。
现在,对 Blinks 架构有了更好的了解,让我们继续讨论项目先决条件。
为了在链上进行活动,与 Blink 交互的用户需要支付 gas 费用(在本例中为本地 MON 代币)。 由于我们使用的是 Monad 测试网,我们可以从 Multi-Chain QuickNode Faucet 获得一些测试 MON。
导航到 Multi-Chain QuickNode Faucet 并连接你的钱包(例如,MetaMask、Coinbase Wallet)或粘贴你的钱包地址以检索测试 MON。 请注意,要在以太坊主网上使用 EVM 水龙头,需要以太坊主网上的主网余额为 0.001 ETH。 你还可以通过 Twitter 或使用你的 QuickNode 帐户登录以获得奖励!
要与 Monad 通信,你需要访问一个节点。 虽然公共节点可用,但 QuickNode 提供的私有端点可提供更快、更稳定的性能。
https://monad-testnet-xxx.quicknode.com/
)并放在手边,因为你稍后需要它。如果你尚未部署 NFT 合约,请查看本指南:如何创建和部署 ERC-721 NFT。
在继续之前,请确保你的 NFT 合约可以执行以下操作,以便与我们的 Blink 完全兼容,否则需要对 SDK 代码进行一些细微更改。
safeMint
函数,该函数接受 2 个参数(to
地址,amount
(# Token))tokenURI
)你可以在 此处 查看完整的源代码示例。
接下来,我们将配置我们的 Monad scaffold 项目。
对于这个项目,我们将使用 Monad Blinks Scaffold 模板。
首先,打开你的终端并导航到你要保存项目的位置。 接下来,运行命令以克隆 repo:
git clone https://github.com/dialectlabs/blink-starter-monad.git
接下来,导航到项目文件夹中并安装依赖项:
cd blink-starter-monad && npm install
现在,让我们更新 .env 文件。 打开它并更新它以包括你在上一步中创建的 QuickNode RPC URL。
MONAD_ENDPOINT_URL=https://api.quicknode.com/YOUR-KEY
现在,让我们更新 scaffold 代码,并使其与 NFT 铸造逻辑对齐。
Monad Blinks scaffold 是一个预构建的项目,它提供了所有必要的组件、配置和样板代码,可以快速开始在 Monad 上构建 Blinks。 在本地使用此项目,我们可以看到我们的 Blink 在启动之前在 playground 环境中的行为方式。 这种内省非常有帮助,因为它允许开发人员在部署到生产环境之前在受控环境中测试和改进其应用程序的行为。
我们需要更新 scaffold 项目中的一些文件,以与 NFT 铸造界面对齐。 首先,我们将处理一些简单的更新,例如前端内容并将图像添加到项目中。
src/app/page.tsx
) 以匹配 此处 的代码。nft-mint.png
) 添加到你的 public
文件夹,该图像将显示在你的 Blink(你可以使用 此处)界面上。接下来,让我们更新 API 路由中的核心逻辑以包括铸造过程。 在 src/app/actions/tip-mon
文件夹中,将文件夹从 tip-mon 重命名为 mint-nft。 然后,在 route.ts
文件中,我们将删除所有现有代码并按顺序包含以下每个代码片段。
import { ActionGetResponse, ActionPostResponse, ActionError } from "@solana/actions";
import { serialize, http } from "wagmi";
import { parseEther, encodeFunctionData, createPublicClient } from "viem";
import { monad } from "@/monad";
const blockchain = "eip155:10143";
const NFT_CONTRACT_ADDRESS = "YOUR_NFT_ADDRESS"; // 输入你的 NFT 合约地址
const MINT_PRICE_MON = "YOUR_NFT_MINT_PRICE"; // 每个 NFT 的价格(以 MON 为单位)(根据你的智能合约进行调整)
const client = createPublicClient({
chain: monad,
transport: http(process.env.MONAD_ENDPOINT_URL),
});
首先,我们导入必要的库并设置我们的常量。 我们使用 CAIP-2 格式作为区块链 ID(即,一种更结构化的方式来表示链 ID),指定我们的 NFT 合约地址,并设置一个 publicClent
实例,以便我们稍后在生成交易时获取网络 gas 费用。
继续之前:确保你使用正确的值更新了 NFT_CONTRACT_ADDRESS
和 MINT_PRICE_MON
变量。
在这里,我们为我们的 NFT 合约的 safeMint 函数定义了 ABI
(应用程序二进制接口),该函数接受一个接收者地址。 我们还设置了 Blink 正常运行所需的 CORS
标头。 OPTIONS
端点是 CORS
预检请求所必需的。 没有它,Blink 将无法正确渲染。
将以下代码放在你的导入和客户端设置下的同一文件中。
const nftAbi = [
{
inputs: [
{ internalType: "address", name: "to", type: "address" },
{ internalType: "uint256", name: "amount", type: "uint256" }
],
name: "safeMint",
outputs: [{ internalType: "uint256[]", name: "", type: "uint256[]" }],
stateMutability: "payable",
type: "function",
},
] as const;
const headers = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
"Access-Control-Allow-Headers":
"Content-Type, x-blockchain-ids, x-action-version",
"Access-Control-Expose-Headers": "x-blockchain-ids, x-action-version",
"Content-Type": "application/json",
"x-blockchain-ids": blockchain,
"x-action-version": "2.4",
};
export const OPTIONS = async () => {
return new Response(null, { headers });
};
在这里,我们为我们的 NFT 合约的 safeMint 函数定义了 ABI
(应用程序二进制接口),该函数接受一个接收者地址。 我们还设置了 Blink 正常运行所需的 CORS
标头。 OPTIONS
端点是 CORS
预检请求所必需的。 没有它,Blink 将无法正确渲染。
估计 gas 很重要,否则用户可能会为他们的交易支付过多的费用,因此我们需要创建一个辅助函数来检查网络 gas 费用并推荐我们用于优先费用的 gas 费用。
将此代码添加到 route.ts
文件中之前的代码(ABI
和 Headers
)代码段下。
async function estimateGasFees() {
const feeData = await client.estimateFeesPerGas();
if (!feeData.maxFeePerGas || !feeData.maxPriorityFeePerGas) {
throw new Error("Failed to retrieve gas fee data from the network");
}
return {
maxFeePerGas: feeData.maxFeePerGas.toString(),
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas.toString(),
};
}
GET
端点为我们的 Blink 定义了 UI - 这是客户端(例如,社交媒体网站)将用于内省和渲染触发指定操作的按钮。 来自 GET 端点的响应使用来自 @solana/actions 的 ActionGetResponse
类型进行构造。 此类型确保返回的元数据包括所有必要的字段,以供 Blink 客户端正确渲染 UI,例如图标、标题、描述和用户可用的操作。 在本例中,我们已将其简化为显示单个“Mint NFT”按钮。
将此代码添加到 route.ts
文件中之前的代码(estimateGasFees
)代码段下。
export const GET = async (req: Request) => {
try {
const response: ActionGetResponse = {
type: "action",
icon: `${new URL("/nft-mint.png", req.url).toString()}`,
label: "",
title: "",
description: `1 ProtoMON = 0.0069420 MON`,
links: {
actions: [
{
type: "transaction",
href: `/api/actions/mint-nft?amount={amount}`,
label: "Mint NFT",
parameters: [
{
name: "amount",
label: `Enter MON amount in wei`,
type: "number",
},
],
}
],
},
};
return new Response(JSON.stringify(response), {
status: 200,
headers,
});
} catch (error) {
console.error("Error in GET request:", error);
const actionError: ActionError = {
message: error instanceof Error ? error.message : "An unknown error occurred"
};
return Response.json(actionError, {
status: 400,
headers,
});
}
};
如果在 GET
请求期间发生错误,则会捕获该错误并使用 ActionError
类型返回该错误。 此类型是一个简单的对象,具有消息属性,确保以标准化格式将错误传达给客户端。
POST
端点包含用于解释用户输入的金额、交易生成和响应回用户的逻辑。 来自 POST
端点的响应使用 ActionPostResponse
类型进行构造。 此类型包括序列化的交易数据和消息,然后将其呈现给用户的钱包以进行签名。
将 POST
代码添加到 route.ts
文件中的 GET
端点逻辑下。
export const POST = async (req: Request) => {
try {
const requestBody = await req.json();
const userAddress = requestBody.account;
const url = new URL(req.url);
const monAmount = url.searchParams.get("amount");
if (!userAddress) {
throw new Error("User address is required");
}
if (!monAmount) {
throw new Error("MON amount is required");
}
const monValue = parseFloat(monAmount.replace(',', '.'));
if (isNaN(monValue) || monValue <= 0) {
throw new Error(`Invalid MON amount: ${monAmount}`);
}
const pricePerNFT = parseFloat(MINT_PRICE_MON);
const numNFTs = Math.floor(monValue / pricePerNFT);
if (numNFTs < 1) {
throw new Error(`Amount too small. Minimum is ${MINT_PRICE_MON} MON for 1 NFT.`);
}
const actualMonAmount = (numNFTs * pricePerNFT).toFixed(8);
console.log(`User entered ${monValue} MON, buying ${numNFTs} NFTs for ${actualMonAmount} MON`);
const weiValue = parseEther(actualMonAmount);
const data = encodeFunctionData({
abi: nftAbi,
functionName: "safeMint",
args: [userAddress, BigInt(numNFTs)],
});
const gasEstimate = await estimateGasFees();
const transaction = {
to: NFT_CONTRACT_ADDRESS,
data,
value: weiValue.toString(),
chainId: "10143", // Monad 测试网
type: "0x2",
maxFeePerGas: gasEstimate.maxFeePerGas,
maxPriorityFeePerGas: gasEstimate.maxPriorityFeePerGas,
};
const transactionJson = serialize(transaction);
const response: ActionPostResponse = {
type: "transaction",
transaction: transactionJson,
message: `Minting ${numNFTs} NFT${numNFTs > 1 ? 's' : ''} for ${actualMonAmount} MON!`,
};
return new Response(JSON.stringify(response), {
status: 200,
headers,
});
} catch (error) {
console.error("Error processing request:", error);
const actionError: ActionError = {
message: error instanceof Error ? error.message : "An unknown error occurred"
};
return Response.json(actionError, {
status: 400,
headers,
});
}
};
与 GET
端点类似,POST
端点中的错误是使用 ActionError
类型处理的。 这确保了任何问题(例如无效的用户输入或 gas 估计失败)都会返回明确的消息,从而允许客户端向用户显示有意义的反馈。
POST 请求的工作方式摘要:
userAddress
) 和输入金额 ( inputAmount
)if statements
)data
)transactionJson
) 以实现钱包兼容性response
)要启动服务器,请在项目根文件夹的终端中运行命令 npm run dev
。 接下来,打开你的浏览器并导航到 http://localhost:3000。 你应该看到 NFT 铸造界面。
尝试输入铸造 NFT 所需的最低金额。 在你的钱包中签署交易后,Blink 界面将使用交易详细信息进行更新:
你完成了! 你现在知道如何使用 Blinks、Wagmi、Viem 等开发人员工具创建自己的 Blink。
让你的 Blink 上线以便与他人共享的下一步是在 Dialect 上注册它。
如果你想继续构建你刚刚学到的知识,请查看这些其他资源。
你完成了! 你刚刚在 Monad 上创建了自己的 Blinks NFT 界面! 凭借现在了解如何使用最佳实践构建 Blink 的核心概念,你可以继续尝试其他类型的 Blinks 集成,例如创建 DeFi 操作(例如交换、staking、借贷或其他用例,例如在市场上展示列出的 NFT、众筹等)
如果你有问题或想法想要分享,请在 Discord 或 Twitter 上给我们留言!
如果你对新主题有任何反馈或要求,请 告诉我们。 我们很乐意收到你的来信。
- 原文链接: quicknode.com/guides/mon...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!