本文介绍了如何在Monad测试网上使用Blinks创建一个NFT minting界面。结合Monad的高性能和Blinks的可嵌入链接特性,简化了用户与区块链NFT的交互流程。文章详细阐述了Blinks的架构,并指导开发者如何配置Monad scaffold项目,将NFT minting逻辑集成到Blink中,最终实现通过一个可分享的URL直接在用户的钱包中完成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。请注意,以太坊主网上有 0.001 ETH 的主网余额要求才能使用EVM水龙头。你也可以通过 Twitter 发推文或使用你的 QuickNode 帐户登录来获得奖励!
要与 Monad 通信,你需要访问一个节点。虽然可以使用公共节点,但来自 QuickNode 的私有端点可提供更快、更稳定的性能。
https://monad-testnet-xxx.quicknode.com/
)并将其放在手边,因为你稍后会用到它。如果你还没有部署 NFT 合约,请查看本指南:How to Create and Deploy an ERC-721 NFT。
在继续之前,请确保你的 NFT 合约可以执行以下操作,以便与我们的 Blink 完全兼容,否则需要对 SDK 代码进行一些小的更改。
safeMint
函数(to
地址,amount
(# token))tokenURI
)你可以查看完整的源代码示例 here。
接下来,我们将配置我们的 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。在本地使用这个项目,我们可以在 playground 环境中看到我们的 Blink 在启动之前的行为。这种自省非常有用,因为它允许开发人员在将应用程序部署到生产环境之前,在受控设置中测试和改进其应用程序的行为。
我们将需要更新 scaffold 项目中的一些文件,以使其与 NFT 铸造界面保持一致。首先,我们将处理一些简单的更新,例如前端内容和向项目中添加图像。
src/app/page.tsx
) 以匹配 here 的代码。nft-mint.png
) 添加到你的 public
文件夹中,该图像将显示在你的 Blink 上(你可以使用 here 的示例)界面。接下来,让我们更新 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"; // Input your NFT contract address
const MINT_PRICE_MON = "YOUR_NFT_MINT_PRICE"; // Price per NFT in MON (adjust as configured by your smart contract)
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
类型进行结构化。此类型可确保返回的元数据包含正确呈现 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
类型返回。此类型是一个带有 message 属性的简单对象,可确保以标准化格式将错误传递给客户端。
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 testnet
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 操作(例如交换、质押、借贷或其他用例,例如在市场上展示已上市的 NFT、众筹等)
如果你有想要分享的问题或想法,请在 Discord 或 Twitter 上给我们留言!
如果你对新主题有任何反馈或要求,请 Let us know。我们很乐意听取你的意见。
- 原文链接: quicknode.com/guides/oth...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!