Skill: portdeveloper/nadfun (version 1.0.0)

# SKILL.md - NadFun Integration Guide **The Monad token launchpad with bonding curves.** Trade tokens, launch your own, monitor events—all with pure viem. ## What is NadFun? NadFun is a decentralized token launchpad on the Monad blockchain. Key features: - **Bonding Curve Trading**: Tokens start on a bonding curve. Price increases as more people buy. - **Graduation**: When curve reaches target reserves, token graduates to DEX (Uniswap V3). - **Token Creation**: Anyone can launch a token with image, metadata, and optional initial buy. - **Real-time Events**: Stream token trades, creations, and DEX swaps as they happen. - **Historical Data**: Query past events for analytics and monitoring. ## Skills Documentation This skill guide is split into focused modules. Start with the overview, then dive into specific guides: | Module | Purpose | Audience | | ------------------------ | ------------------------------------- | --------------------- | | **SKILL.md** (this file) | Architecture, constants, setup | Everyone | | **QUOTE.md** | Price quotes, curve state | Traders, analyzers | | **TRADING.md** | Buy, sell, permit signatures | Traders, bots | | **TOKEN.md** | Balances, metadata, transfers | App developers | | **CREATE.md** | Token creation, image upload | Token creators | | **INDEXER.md** | Historical event querying | Analytics, dashboards | | **AGENT-API.md** | REST API for trading data, token info | AI agents, bots | For API download/endpoints/header usage, see **AGENT-API.md**. > **Note:** To obtain an API key, you must first login to [nad.fun](https://nad.fun) via wallet signature. See the [Authentication](#authentication-login-flow) section below for the login flow, then use the session cookie to create an API key via `POST /api-key`. ## Skill Files | File | 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 ## Quick Facts - **Network**: Testnet (chain 10143) or Mainnet (chain 143) - **Language**: TypeScript/JavaScript - **Pure viem**: All examples use direct contract calls with viem - **Fees**: Check API for deploy fees via REST endpoint ## Network Constants All addresses and endpoints are here. Update this when network changes. ```typescript 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: [REDACTED_SECRET], BONDING_CURVE_ROUTER: [REDACTED_SECRET], LENS: [REDACTED_SECRET], CURVE: [REDACTED_SECRET], WMON: [REDACTED_SECRET], V3_FACTORY: [REDACTED_SECRET], CREATOR_TREASURY: [REDACTED_SECRET], }, mainnet: { chainId: 143, rpcUrl: "https://monad-mainnet.drpc.org", apiUrl: "https://api.nadapp.net", // Contract addresses DEX_ROUTER: [REDACTED_SECRET], BONDING_CURVE_ROUTER: [REDACTED_SECRET], LENS: [REDACTED_SECRET], CURVE: [REDACTED_SECRET], WMON: [REDACTED_SECRET], V3_FACTORY: [REDACTED_SECRET], CREATOR_TREASURY: [REDACTED_SECRET], }, }[NETWORK] ``` ## Basic Setup Every skill guide assumes you start with viem. Here's the foundation: ```typescript 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), }) ``` This is your foundation. All other modules build on top of this. ## Authentication (Login Flow) To access session-protected endpoints (API key management, account settings, etc.), you need to authenticate via wallet signature. ### Login Flow ```typescript 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) ``` ### Using Session Cookie ```typescript // 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! ``` ### Logout ```typescript await fetch(`${CONFIG.apiUrl}/auth/delete_session`, { method: "DELETE", headers: { Cookie: sessionCookie }, }) ``` ### TypeScript Interfaces ```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 } } ``` ## Core Concepts ### Bonding Curve Tokens on NadFun start on a bonding curve. The curve defines price and availability: - **Real reserves**: Actual MON and tokens in the pool - **Virtual reserves**: Initial pseudo-reserves for curve math - **K constant**: Maintains x\*y=k formula for price - **Target**: How many tokens must be sold to graduate Get curve state with `getCurveState(token)` → see **QUOTE.md** ### Graduation When enough tokens are bought: 1. Curve reaches target reserve 2. Liquidity moves to Uniswap V3 pool 3. Token transitions from bonding curve to DEX 4. `isGraduated(token)` returns true Check progress with `getProgress(token)` (0-10000 = 0-100%) ### Action IDs The `actionId` parameter in token creation identifies the type of action: | actionId | Description | | -------- | -------------- | | 1 | Token Creation | Always use `actionId: 1` when calling the `create` function. ### Permit Signatures EIP-2612 lets wallets sign approval transactions instead of sending separate approve() calls: 1. Generate signature: `generatePermitSignature(token, spender, amount, deadline)` 2. Use in trade: `sellPermit({ ...params, ...signature })` 3. No separate approve needed—saves gas See **TRADING.md** for details. ## Common Patterns ### Pattern 1: Simple Trade ```typescript // 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, }) ``` See **TRADING.md** for full examples. ### Pattern 2: Launch Token ```typescript 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: "MTK", description: "My awesome token", image_uri: imageUri, }), }) const { metadata_uri: metadataUri } = await metadataRes.json() // 3. Mine salt via Agent API const saltRes = await fetch(`${CONFIG.apiUrl}/agent/salt`, { method: "POST", headers: { "Content-Type": "application/json", "X-API-Key": API_KEY }, body: JSON.stringify({ creator: account.address, name: "My Token", symbol: "MTK", metadata_uri: metadataUri, }), }) const { salt } = await saltRes.json() // 4. Create token on-chain const tx = await walletClient.writeContract({ address: BONDING_CURVE_ROUTER, abi: bondingCurveRouterAbi, functionName: "create", args: [ { name: "My Token", symbol: "MTK", tokenURI: metadataUri, amountOut: 0n, // Set > 0n for initial buy amount (in tokens) salt: salt, actionId: 1, // Token creation action identifier (always 1 for create) }, ], value: deployFeeAmount, // plus optional initial buy MON if amountOut > 0 gas, // Use estimated gas (see below) }) console.log("Transaction:", tx) // Gas estimation (recommended over hardcoded values) const gasEstimate = await publicClient.estimateContractGas({ address: CONFIG.BONDING_CURVE_ROUTER, abi: bondingCurveRouterAbi, functionName: "create", args: [ { name: "My Token", symbol: "MTK", tokenURI: metadataUri, amountOut: 0n, salt, actionId: 1 }, ], value: deployFeeAmount, account: account.address, }) const gas = (gasEstimate * 120n) / 100n // 20% buffer for safety ``` See **CREATE.md** for step-by-step. ## Prompts for AI Agents Use these prompts to guide your work: ### Trading - "Get a price quote for token X with 0.1 MON" - "Buy 0.1 MON of token X with 1% slippage" - "Sell all tokens with permit signature" - "Check if token has graduated" ### Token Info - "Get metadata for token address X" - "Check my MON balance" - "Get available tokens to buy before graduation" ### Creation - "Launch a token called MyToken (MTK) with this image" - "Mine a vanity address for my token" - "Check token creation fees" ### Analysis - "Calculate graduation progress for token X" - "Get the bonding curve state for token X" - "Track all swaps on the graduated token" - "Analyze token creation rate" ## Required Files For integration, you'll need: ``` abis/ ├── curve.ts # Bonding curve contract ABI ├── lens.ts # Price quote ABI ├── router.ts # DEX router ABI ├── token.ts # ERC20 token ABI └── v3*.ts # Uniswap V3 ABIs constants.ts # Network configs, contract addresses ``` All ABIs are documented in **ABI.md**. ### ABI Import Guide Each ABI can be copied from **ABI.md**. Reference the correct section: | ABI | ABI.md Section | Purpose | | ----------------------- | ------------------------------------------------- | -------------------------------- | | `bondingCurveRouterAbi` | [BondingCurveRouter ABI](#bondingcurverouter-abi) | Token creation, buy/sell | | `lensAbi` | [Lens ABI](#lens-abi) | Price quotes, getAmountOut | | `curveAbi` | [Curve ABI](#curve-abi) | Curve state, graduation check | | `tokenAbi` | [Token ABI](#token-abi) | ERC20 operations, permit signing | Example import pattern: ```typescript // Copy the ABI from ABI.md into your project import { bondingCurveRouterAbi } from "./abis/router" import { lensAbi } from "./abis/lens" ``` ## Installation ```bash npm install viem # or pnpm add viem # or yarn add viem ``` ## Dependencies ```json { "viem": "^2.0.0" } ``` Pure viem. No other blockchain libraries needed. ## Troubleshooting ### "No such contract" You're on the wrong network. Check NETWORK constant matches your setup. ### "Token not graduated yet" Query `getProgress()` via Lens contract. Returns 0-10000. Need 10000 (100%) to graduate. ### "Transaction reverted" 1. Check amountOutMin—slippage might be too high or curve moved 2. Verify deadline—transaction took too long 3. Check allowance—approve token before sell ## Next Steps Pick a guide based on what you need: - **Building a dex bot?** → TRADING.md - **Creating a dashboard?** → INDEXER.md + QUOTE.md - **Launching tokens?** → CREATE.md - **Token info queries?** → TOKEN.md Each guide has complete examples ready to copy. ## General Development Practices & Troubleshooting ### Type Safety Tips Always use the `as const` assertion when working with ABIs in viem. All ABIs in this module are pre-declared with `as const` for full type inference: ```typescript // Types are automatically narrowed const result = await contract.read.getAmountOut([...]) // result type is precisely bigint (not bigint | undefined) ``` Use viem type helpers for type-safe conversions: ```typescript 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 ``` ### Common ABI Errors When interacting with smart contracts, you might encounter specific errors. Here's a table of common ABI-related errors and how to approach them: | Error | Meaning | | ----------------------- | ---------------------------------------------------------- | | `InsufficientAmount` | Output less than amountOutMin | | `InsufficientAmountOut` | Insufficient output amount | | `DeadlineExpired` | Deadline has passed | | `Unauthorized` | Caller not authorized | | `AlreadyGraduated` | Token already graduated to DEX | | `BondingCurveLocked` | Curve locked during graduation | | `InvalidProof` | Merkle proof verification failed (specific to claims) | | `AlreadyClaimed` | Amount already claimed for this proof (specific to claims) | | `NotClaimable` | Token not eligible for claims yet (specific to claims) | | `InsufficientBalance` | Treasury has insufficient MON balance (specific to claims) | Check error types in viem with: ```typescript 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. } } ```


Security scan verdicts — ClawScan: suspicious | static analysis: suspicious | VirusTotal: suspicious | SkillSpector: suspicious (CRITICAL)