如何在Monad上创建Blinks

本文介绍了如何在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 铸造接口(可在你的网站或社交媒体上分享)。让我们开始吧!

Blink Interface

你将做什么

  • 了解 MonadBlinks
  • 使用 QuickNode 创建一个 Monad 测试网节点端点
  • 使用测试网 MON 代币为你的钱包充值
  • 克隆一个示例仓库并对其进行配置,以便在 Monad 测试网上创建一个 Blink,该 Blink 可以从 ERC-721 智能合约中铸造 NFT

你将需要什么

什么是 Monad?

Monad 由 Monad Labs 于 2022 年推出,专为高吞吐量(高达每秒 10,000 笔交易)和低成本交易(低于 1 美分)而设计。它利用并行执行和优化的共识机制来增强可扩展性,同时保持与以太坊生态系统中流行的工具的兼容性。这使其成为利用区块链的各种类型应用程序(如 NFT 和 DeFi)的理想平台。

什么是 Blinks?

Blinks 由 Dialect Labs 开发,代表 Blockchain Links(区块链链接)。Blinks 是可共享的 URL,它将链上操作(例如,将 NFT 铸造)捆绑到单个可点击的链接中。这消除了复杂集成的需要,允许开发人员将区块链交互嵌入到支持 URL 的任何地方(例如,社交媒体、网站)。对于用户而言,Blinks 通过单击即可简化与区块链的交互,从而直接在其钱包中显示可签名的交易。截至 2025 年 2 月,Blinks 在 Monad 的测试网上可用。

Blinks 深入探讨

Blinks 通过具有以下内容的提供者-客户端架构运行:

  • Blink 提供者(后端):你的服务器,它托管为 Blink 体验提供支持的 API 端点
  • Blink 客户端:内省/解释 Blink URL 并呈现 Blink UI 的软件
  • URL 模式:连接提供者和客户端的标准化格式
  • 高级用例:表单:使用多个输入和高级输入类型构建 blink
  • 高级用例:操作链:例如:在 token 之间来回交换,UI 管理状态更改
  • 高级用例:表单:通过将引用密钥添加到你的 API 调用来跟踪交易转换

Blinks 的核心功能是通过一个两部分的 API 系统:

1. GET 路由

首次访问 Blink URL 时,客户端会向提供者发送 GET 请求。提供者返回有关 Blink 的元数据,例如:

  • 标题和描述
  • 图标 URL
  • 可用操作
  • UI 参数(如输入表单)

此元数据允许客户端为你的 Blink 呈现一致的界面,而无需任何自定义集成。

2. POST 路由

当用户与 Blink 交互时(例如,单击“Mint NFT”按钮):

  • 客户端向提供者发送包含用户输入和钱包地址的 POST 请求
  • 提供者构建区块链交易
  • 交易返回到客户端,客户端将其呈现给用户的钱包
  • 用户签署交易,然后将其广播到区块链

这种关注点分离允许交易逻辑保留在你的服务器上,而客户端处理钱包交互和 UI 呈现。

注意

请注意,本指南中提供的信息基于 Blinks 规范的当前状态。随着技术的发展,可能会引入新的特性和功能。请查看此处或 官方规范 以获取最新信息。

现在,更好地了解了 Blinks 的架构,让我们继续讨论项目先决条件。

项目先决条件

从 QuickNode 多链水龙头获取 MON

为了在链上进行活动,与 Blink 交互的用户需要支付 gas 费用(在本例中为原生 MON 代币)。由于我们使用的是 Monad 测试网,我们可以从 Multi-Chain QuickNode Faucet 获取一些测试 MON。

导航到 Multi-Chain QuickNode Faucet 并连接你的钱包(例如,MetaMask、Coinbase Wallet)或粘贴你的钱包地址以检索测试 MON。请注意,以太坊主网上有 0.001 ETH 的主网余额要求才能使用EVM水龙头。你也可以通过 Twitter 发推文或使用你的 QuickNode 帐户登录来获得奖励!

创建一个 Monad 端点

要与 Monad 通信,你需要访问一个节点。虽然可以使用公共节点,但来自 QuickNode 的私有端点可提供更快、更稳定的性能。

  1. QuickNode 注册一个免费帐户。
  2. 转到 Endpoints 页面,然后单击 Create Endpoint
  3. 选择 Monad 作为链,选择 Testnet 作为网络。
  4. 复制 HTTP Provider URL(例如,https://monad-testnet-xxx.quicknode.com/)并将其放在手边,因为你稍后会用到它。

部署 NFT 合约 (ERC-721)

如果你还没有部署 NFT 合约,请查看本指南:How to Create and Deploy an ERC-721 NFT

在继续之前,请确保你的 NFT 合约可以执行以下操作,以便与我们的 Blink 完全兼容,否则需要对 SDK 代码进行一些小的更改。

  1. 具有接受 2 个参数的 safeMint 函数(to 地址,amount(# token))
  2. 它部署在 Monad 测试网上
  3. 任何人都可以 mint
  4. (推荐)元数据存储在 IPFS + 返回给定 token ID 的元数据 URI 的函数(即,tokenURI
  5. 你有 NFT 合约地址,稍后可以在你的 Blink scaffold 中输入

你可以查看完整的源代码示例 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 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 文件中,我们将删除所有现有代码并按顺序包含下面的每个代码段。

1. 导入和基本设置

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_ADDRESSMINT_PRICE_MON 变量。

2. 合约 ABI 和标头

在这里,我们为 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 将无法正确呈现。

3. 估计 Gas

估计 gas 很重要,否则用户可能会为他们的交易支付过高的费用,因此我们需要创建一个辅助函数来检查网络 gas 费用并推荐我们用于优先级费用的 gas 费用。

将此代码添加到 route.ts 文件中之前的代码(ABIHeaders)片段下方。

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(),
  };
}

4. GET 端点 - 获取用户详细信息

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,
    });
  }
};

使用 ActionError 处理错误

如果在 GET 请求期间发生错误,则会捕获该错误并使用 ActionError 类型返回。此类型是一个带有 message 属性的简单对象,可确保以标准化格式将错误传递给客户端。

5. POST 端点 - 交易创建

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,
    });
  }
};

使用 ActionError 处理错误

GET 端点类似,POST 端点中的错误使用 ActionError 类型处理。这可确保返回任何问题(例如,无效的用户输入或 gas 估算失败),并提供清晰的消息,从而允许客户端向用户显示有意义的反馈。

POST 请求的工作方式摘要:

  • 从请求正文中获取用户地址 (userAddress) 和输入金额 (inputAmount)
  • 确认用户地址和金额都存在 (if statements)
  • 通过编码 NFT 铸造函数调用、估计 gas 费用和构造完整的交易对象来构建交易 (data)
  • 序列化交易 (transactionJson) 以实现钱包兼容性
  • 返回成功消息或详细错误 (response)

在本地运行 Blink

要启动服务器,请在项目根文件夹中的终端中运行命令 npm run dev。接下来,打开你的浏览器并导航到 http://localhost:3000。你应会看到 NFT 铸造界面。

Blink Interface

尝试输入铸造 NFT 所需的最低金额。在你的钱包中签署交易后,Blink 界面将更新并显示交易详细信息:

Minting NFT

你已经成功了!你现在知道如何使用 Blinks、Wagmi、Viem 等开发人员工具创建自己的 Blink。

注册你的 Blink

让你的 Blink 上线以便你可以与他人分享的下一步是在 Dialect 上注册它。

如果你想继续在你刚刚学到的知识的基础上进行构建,请查看以下其他资源。

其他资源

最后的想法

你已经成功了!你刚刚在 Monad 上创建了自己的 Blinks NFT 接口!现在了解了如何使用最佳实践构建 Blink 的核心概念,你可以继续试验其他类型的 Blinks 集成,例如创建 DeFi 操作(例如交换、质押、借贷或其他用例,例如在市场上展示已上市的 NFT、众筹等)

如果你有想要分享的问题或想法,请在 DiscordTwitter 上给我们留言!

我们 ❤️ 反馈!

如果你对新主题有任何反馈或要求,请 Let us know。我们很乐意听取你的意见。

  • 原文链接: quicknode.com/guides/oth...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
QuickNode
QuickNode
江湖只有他的大名,没有他的介绍。