📦 nadfun — 分布式Monad代币发起平台
v1.0.0NadFun是一个基于Monad区块链的去中心化代币发起平台,提供了具有债券曲线交易、代币创建、实时事件流和历史数据查询等功能。通过纯viem调用,支持代币交易、创建、监控和分析。
详细分析 ▾
运行时依赖
版本
nadfun 1.0.0 - 初始发布,包含模块化指南、设置流程、API访问流程、Monad网络端点、代码示例和核心概念解释。
安装命令
点击复制技能文档
基于绑定曲线的 Monad 代币launchpad。交易代币、发行代币、监控事件——全部使用纯 viem。
什么是 NadFun?
NadFun 是 Monad 区块链上的去中心化代币 launchpad。主要特性:
- 绑定曲线交易:代币从绑定曲线开始。随着更多人购买,价格上涨。
- 毕业机制:当曲线达到目标储备时,代币毕业到 DEX(Uniswap V3)。
- 代币创建:任何人都可以发行带有图像、元数据和可选初始购买的代币。
- 实时事件:实时流式传输代币交易、创建和 DEX 交换。
- 历史数据:查询过去事件用于分析和监控。
技能文档
本技能指南分为多个模块。从概述开始,然后深入特定指南:
| 模块 | 用途 | 受众 |
|---|---|---|
| SKILL.md(本文档) | 架构、常量、设置 | 所有人 |
| QUOTE.md | 价格报价、曲线状态 | 交易者、分析师 |
| TRADING.md | 购买、出售、许可签名 | 交易者、机器人 |
| TOKEN.md | 余额、元数据、转账 | 应用开发者 |
| CREATE.md | 代币创建、图像上传 | 代币创建者 |
| INDEXER.md | 历史事件查询 | 分析、仪表盘 |
| AGENT-API.md | 交易数据、代币信息的 REST API | AI 智能体、机器人 |
注意: 要获取 API 密钥,您必须首先通过钱包签名登录 nad.fun。请参阅下面的 认证 部分了解登录流程,然后使用会话 cookie 通过 POST /api-key 创建 API 密钥。技能文件
| 文件 | URL |
|---|---|
| ABI.md | https://nad.fun/abi.md |
| AGENT-API.md | https://nad.fun/agent-api.md |
| CREATE.md | https://nad.fun/create.md |
| INDEXER.md | https://nad.fun/indexer.md |
| QUOTE.md | https://nad.fun/quote.md |
| TOKEN.md | https://nad.fun/token.md |
| TRADING.md | https://nad.fun/trading.md |
| WALLET.md | https://nad.fun/wallet.md |
mkdir -p ~/.nadfun/skills
curl -s https://nad.fun/skill.md > ~/.nadfun/skills/SKILL.md
curl -s https://nad.fun/abi.md > ~/.nadfun/skills/ABI.md
curl -s https://nad.fun/agent-api.md > ~/.nadfun/skills/AGENT-API.md
curl -s https://nad.fun/create.md > ~/.nadfun/skills/CREATE.md
curl -s https://nad.fun/indexer.md > ~/.nadfun/skills/INDEXER.md
curl -s https://nad.fun/quote.md > ~/.nadfun/skills/QUOTE.md
curl -s https://nad.fun/token.md > ~/.nadfun/skills/TOKEN.md
curl -s https://nad.fun/trading.md > ~/.nadfun/skills/TRADING.md
curl -s https://nad.fun/wallet.md > ~/.nadfun/skills/WALLET.md
快速信息
- 网络:测试网(链 10143)或主网(链 143)
- 语言:TypeScript/JavaScript
- 纯 viem:所有示例使用 viem 直接调用合约
- 费用:通过 REST 端点检查 API 获取部署费用
网络常量
所有地址和端点都在这里。网络变化时更新此配置。
const NETWORK = "testnet" // 'testnet' | 'mainnet'const CONFIG = { testnet: { chainId: 10143, rpcUrl: "https://monad-testnet.drpc.org", apiUrl: "https://dev-api.nad.fun", // For token creation
// Contract addresses DEX_ROUTER: "0x5D4a4f430cA3B1b2dB86B9cFE48a5316800F5fb2", BONDING_CURVE_ROUTER: "0x865054F0F6A288adaAc30261731361EA7E908003", LENS: "0xB056d79CA5257589692699a46623F901a3BB76f1", CURVE: "0x1228b0dc9481C11D3071E7A924B794CfB038994e", WMON: "0x5a4E0bFDeF88C9032CB4d24338C5EB3d3870BfDd", V3_FACTORY: "0xd0a37cf728CE2902eB8d4F6f2afc76854048253b", CREATOR_TREASURY: "0x24dFf9B68fA36f8400302e2babC3e049eA19459E", }, mainnet: { chainId: 143, rpcUrl: "https://monad-mainnet.drpc.org", apiUrl: "https://api.nadapp.net",
// Contract addresses DEX_ROUTER: "0x0B79d71AE99528D1dB24A4148b5f4F865cc2b137", BONDING_CURVE_ROUTER: "0x6F6B8F1a20703309951a5127c45B49b1CD981A22", LENS: "0x7e78A8DE94f21804F7a17F4E8BF9EC2c872187ea", CURVE: "0xA7283d07812a02AFB7C09B60f8896bCEA3F90aCE", WMON: "0x3bd359C1119dA7Da1D913D1C4D2B7c461115433A", V3_FACTORY: "0x6B5F564339DbAD6b780249827f2198a841FEB7F3", CREATOR_TREASURY: "0x42e75B4B96d7000E7Da1e0c729Cec8d2049B9731", }, }[NETWORK]
基本设置
每个技能指南都假设您从 viem 开始。以下是基础设置:
import { createPublicClient, createWalletClient, http, privateKeyToAccount } from "viem"const NETWORK = "testnet" const CONFIG = { / from above / }[NETWORK]
// Create clients const publicClient = createPublicClient({ chain: { id: CONFIG.chainId, name: "Monad", nativeCurrency: { name: "MON", symbol: "MON", decimals: 18 }, rpcUrls: { default: { http: [CONFIG.rpcUrl] } }, }, transport: http(CONFIG.rpcUrl), })
const account = privateKeyToAccount("0x...") const walletClient = createWalletClient({ account, chain: publicClient.chain, transport: http(CONFIG.rpcUrl), })
这是您的基础。所有其他模块都构建在此之上。
认证(登录流程)
要访问受会话保护的端点(API 密钥管理、账户设置等),您需要通过钱包签名进行认证。
登录流程
import { createWalletClient, http, privateKeyToAccount } from "viem"const account = privateKeyToAccount("0x...") const walletClient = createWalletClient({ account, chain: { id: CONFIG.chainId, name: "Monad", nativeCurrency: { name: "MON", symbol: "MON", decimals: 18 }, rpcUrls: { default: { http: [CONFIG.rpcUrl] } }, }, transport: http(CONFIG.rpcUrl), })
// Step 1: Request nonce const nonceRes = await fetch(
${CONFIG.apiUrl}/auth/nonce, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ address: account.address }), }) const { nonce } = await nonceRes.json()// Step 2: Sign the nonce const signature = await walletClient.signMessage({ message: nonce })
// Step 3: Create session const sessionRes = await fetch(
${CONFIG.apiUrl}/auth/session, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ signature, nonce, chain_id: CONFIG.chainId, }), })// Extract session cookie from response headers const sessionCookie = sessionRes.headers.get("set-cookie") const { account_info } = await sessionRes.json()
console.log("Logged in as:", account_info.account_id)
使用会话 Cookie
// Use session cookie for authenticated requests const headers = { Cookie: sessionCookie }
// Example: Create API Key (requires session) const apiKeyRes = await fetch(${CONFIG.apiUrl}/api-key, { method: "POST", headers: { ...headers, "Content-Type": "application/json" }, body: JSON.stringify({ name: "My Bot", expires_in_days: 365 }), }) const { api_key } = await apiKeyRes.json() console.log("API Key:", api_key) // Store this securely!
登出
await fetch(${CONFIG.apiUrl}/auth/delete_session, {
method: "DELETE",
headers: { Cookie: sessionCookie },
})
TypeScript 接口
interface AuthNonceRequest { address: string }interface AuthNonceResponse { nonce: string }
interface AuthSessionRequest { signature: string nonce: string chain_id: number wallet_address?: string // Optional }
interface AuthSessionResponse { account_info: { account_id: string nickname: string bio: string image_uri: string } }
核心概念
绑定曲线
NadFun 上的代币从绑定曲线开始。曲线定义价格和可用性:
- 真实储备:池中实际的 MON 和代币
- 虚拟储备:曲线计算的初始伪储备
- K 常数:维持 xy=k 价格公式
- 目标:需要卖出多少代币才能毕业
使用 getCurveState(token) 获取曲线状态 → 参见 QUOTE.md
毕业
当足够多的代币被购买时:
- 曲线达到目标储备
- 流动性转移到 Uniswap V3 池
- 代币从绑定曲线过渡到 DEX
isGraduated(token)返回 true
使用 getProgress(token) 检查进度(0-10000 = 0-100%)
动作 ID
代币创建中的 actionId 参数标识动作类型:
| actionId | 描述 |
|---|---|
| 1 | 代币创建 |
create 函数时始终使用 actionId: 1。许可签名
EIP-2612 允许钱包签署批准交易,而不是发送单独的 approve() 调用:
- 生成签名:
generatePermitSignature(token, spender, amount, deadline) - 在交易中使用:
sellPermit({ ...params, ...signature }) - 无需单独批准——节省 gas
详情请参阅 TRADING.md。
常见模式
模式 1:简单交易
// Get quote (3 args: token, amountIn, isBuy) const [router, amountOut] = await publicClient.readContract({ address: LENS, abi: lensAbi, functionName: "getAmountOut", args: [token, amountIn, true], // true = buy })
// Buy with slippage protection (1 tuple arg) const minOut = (amountOut BigInt(995)) / BigInt(1000) // 0.5% slippage const deadline = BigInt(Math.floor(Date.now() / 1000) + 300) const tx = await walletClient.writeContract({ address: router, // Use router returned from getAmountOut abi: bondingCurveRouterAbi, functionName: "buy", args: [ { amountOutMin: minOut, token: token, to: account.address, deadline: deadline, }, ], value: amountIn, })
完整示例请参阅 TRADING.md。
模式 2:发行代币
import { bondingCurveRouterAbi } from "./abis/router"// Requires API_KEY and deployFeeAmount (see CREATE.md for feeConfig())
// 1. Upload image to Agent API (raw binary, NOT formData) const imageRes = await fetch(
${CONFIG.apiUrl}/agent/token/image, { method: "POST", headers: { "X-API-Key": API_KEY, "Content-Type": "image/png", // or 'image/jpeg', 'image/webp', 'image/svg+xml' }, body: imageBuffer, // raw binary data (Buffer, Blob, or ArrayBuffer) }) const { image_uri: imageUri } = await imageRes.json()// 2. Upload metadata to Agent API const metadataRes = await fetch(
${CONFIG.apiUrl}/agent/token/metadata, { method: "POST", headers: { "Content-Type": "application/json", "X-API-Key": API_KEY }, body: JSON.stringify({ name: "My Token", symbol: "MYT", description: "A test token", image: imageUri, }), }) const { metadata_uri: metadataUri } = await metadataRes.json()
// 3. Deploy token via contract const tx = await walletClient.writeContract({ address: BONDING_CURVE_ROUTER, abi: bondingCurveRouterAbi, functionName: "create", args: [ { actionId: 1, // Token creation name: "My Token", symbol: "MYT", metadataUri: metadataUri, // ... other params }, ], value: deployFeeAmount, })
完整示例请参阅 CREATE.md。
模式 3:监听事件
import { parseAbiItem } from "viem"// Subscribe to new token creations const unsubscribe = publicClient.watchContractEvent({ address: BONDING_CURVE_ROUTER, abi: [parseAbiItem("event TokenCreated(address indexed token, address creator)")], onLogs: (logs) => { for (const log of logs) { console.log("New token created:", log.args.token) console.log("Creator:", log.args.creator) } }, })
// Later, cleanup await unsubscribe()
类型安全
viem 返回精确的类型。这是一个关键优势:
// result type is precisely bigint (not bigint | undefined)
使用 viem 类型助手进行类型安全转换:
import { Address, Hex } from "viem" import { parseEther, formatEther } from "viem"// Amounts const wei = parseEther("1") // string -> bigint const eth = formatEther(1000000000000000000n) // bigint -> string
// Addresses const addr: Address = "0x..." // Validated address type
// Signatures const sig: Hex = "0x..." // Hex string type
常见 ABI 错误
与智能合约交互时,您可能会遇到特定错误。以下是常见 ABI 相关错误及其处理方法:
| 错误 | 含义 |
|---|---|
InsufficientAmount | 输出小于 amountOutMin |
InsufficientAmountOut | 输出金额不足 |
DeadlineExpired | 截止时间已过 |
Unauthorized | 调用者未授权 |
AlreadyGraduated | 代币已毕业到 DEX |
BondingCurveLocked | 曲线在毕业期间锁定 |
InvalidProof | Merkle 证明验证失败(特定于索赔) |
AlreadyClaimed | 此证明的金额已领取(特定于索赔) |
NotClaimable | 代币尚不符合索赔条件(特定于索赔) |
InsufficientBalance | 国库 MON 余额不足(特定于索赔) |
import { ContractFunctionExecutionError } from 'viem'
try { await contract.write.buy([...]) // or any contract interaction } catch (error) { if (error instanceof ContractFunctionExecutionError) { console.log(error.shortMessage) // "InsufficientAmount", etc. } }