{
  "schemaVersion": "1.0",
  "item": {
    "slug": "nadfunagent",
    "name": "nadfunagent",
    "source": "tencent",
    "type": "skill",
    "category": "开发工具",
    "sourceUrl": "https://clawhub.ai/encipher88/nadfunagent",
    "canonicalUrl": "https://clawhub.ai/encipher88/nadfunagent",
    "targetPlatform": "OpenClaw"
  },
  "install": {
    "downloadMode": "redirect",
    "downloadUrl": "/downloads/nadfunagent",
    "sourceDownloadUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=nadfunagent",
    "sourcePlatform": "tencent",
    "targetPlatform": "OpenClaw",
    "installMethod": "Manual import",
    "extraction": "Extract archive",
    "prerequisites": [
      "OpenClaw"
    ],
    "packageFormat": "ZIP package",
    "includedAssets": [
      ".env",
      "DEPENDENCIES.md",
      "INSTALL.md",
      "package.json",
      "README.md",
      "SKILL.md"
    ],
    "primaryDoc": "SKILL.md",
    "quickSetup": [
      "Download the package from Yavira.",
      "Extract the archive and review SKILL.md first.",
      "Import or place the package into your OpenClaw setup."
    ],
    "agentAssist": {
      "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
      "steps": [
        "Download the package from Yavira.",
        "Extract it into a folder your agent can access.",
        "Paste one of the prompts below and point your agent at the extracted folder."
      ],
      "prompts": [
        {
          "label": "New install",
          "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Then review README.md for any prerequisites, environment setup, or post-install checks. Tell me what you changed and call out any manual steps you could not complete."
        },
        {
          "label": "Upgrade existing",
          "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Then review README.md for any prerequisites, environment setup, or post-install checks. Summarize what changed and any follow-up checks I should run."
        }
      ]
    },
    "sourceHealth": {
      "source": "tencent",
      "status": "healthy",
      "reason": "direct_download_ok",
      "recommendedAction": "download",
      "checkedAt": "2026-05-07T17:22:31.273Z",
      "expiresAt": "2026-05-14T17:22:31.273Z",
      "httpStatus": 200,
      "finalUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=afrexai-annual-report",
      "contentType": "application/zip",
      "probeMethod": "head",
      "details": {
        "probeUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=afrexai-annual-report",
        "contentDisposition": "attachment; filename=\"afrexai-annual-report-1.0.0.zip\"",
        "redirectLocation": null,
        "bodySnippet": null
      },
      "scope": "source",
      "summary": "Source download looks usable.",
      "detail": "Yavira can redirect you to the upstream package for this source.",
      "primaryActionLabel": "Download for OpenClaw",
      "primaryActionHref": "/downloads/nadfunagent"
    },
    "validation": {
      "installChecklist": [
        "Use the Yavira download entry.",
        "Review SKILL.md after the package is downloaded.",
        "Confirm the extracted package contains the expected setup assets."
      ],
      "postInstallChecks": [
        "Confirm the extracted package includes the expected docs or setup files.",
        "Validate the skill or prompts are available in your target agent workspace.",
        "Capture any manual follow-up steps the agent could not complete."
      ]
    },
    "downloadPageUrl": "https://openagent3.xyz/downloads/nadfunagent",
    "agentPageUrl": "https://openagent3.xyz/skills/nadfunagent/agent",
    "manifestUrl": "https://openagent3.xyz/skills/nadfunagent/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/nadfunagent/agent.md"
  },
  "agentAssist": {
    "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
    "steps": [
      "Download the package from Yavira.",
      "Extract it into a folder your agent can access.",
      "Paste one of the prompts below and point your agent at the extracted folder."
    ],
    "prompts": [
      {
        "label": "New install",
        "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Then review README.md for any prerequisites, environment setup, or post-install checks. Tell me what you changed and call out any manual steps you could not complete."
      },
      {
        "label": "Upgrade existing",
        "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Then review README.md for any prerequisites, environment setup, or post-install checks. Summarize what changed and any follow-up checks I should run."
      }
    ]
  },
  "documentation": {
    "source": "clawhub",
    "primaryDoc": "SKILL.md",
    "sections": [
      {
        "title": "Nad.fun Autonomous Trading Agent",
        "body": "Autonomous trading agent that scans Nad.fun markets, analyzes tokens using momentum strategies, executes trades, and distributes profits to MMIND token holders."
      },
      {
        "title": "Prerequisites",
        "body": "monad-development skill installed (for wallet and RPC setup)\nnadfun-trading skill installed (for buy/sell operations), or use the trading/ folder from this repo\nnadfun-indexer skill installed (for querying events)\nnadfun-agent-api skill installed (for market data)\nNetwork configured (mainnet only for this skill)\nMMIND token address configured\n\nPaths (clean install): Config is read from NADFUN_ENV_PATH if set, else $HOME/nadfunagent/.env. Positions report: POSITIONS_REPORT_PATH or $HOME/nadfunagent/positions_report.json. Scripts from nadfun-trading may be at ~/.openclaw/workspace/skills/nadfun-trading or at <this-repo>/trading. See DEPENDENCIES.md."
      },
      {
        "title": "Configuration",
        "body": "CRITICAL: Load environment variables from .env file (default path: $HOME/nadfunagent/.env; override with NADFUN_ENV_PATH):\n\nMMIND_TOKEN_ADDRESS: Address of MMIND token for profit distribution (required)\nMONAD_PRIVATE_KEY: Private key for trading wallet (required)\nMONAD_RPC_URL: RPC endpoint URL (required)\nMONAD_NETWORK: Network type - \"mainnet\" or \"testnet\" (required)\nMAX_POSITION_SIZE: Maximum position size in MON (default: 0.1)\nPROFIT_TARGET_PERCENT: Take-profit threshold (default: 20%)\nSTOP_LOSS_PERCENT: Stop-loss threshold (default: 10%)\nPNL_DISTRIBUTION_PERCENT: % of profit to distribute (default: 30%)\n\nBefore starting trading cycle:\n\nRead .env file (path: NADFUN_ENV_PATH or $HOME/nadfunagent/.env). If the file is missing or any required variable is empty, ask the user to provide MMIND_TOKEN_ADDRESS, MONAD_PRIVATE_KEY, MONAD_RPC_URL, MONAD_NETWORK — do not run trading until config is complete.\nLoad MMIND_TOKEN_ADDRESS - use this for profit distribution\nLoad MONAD_PRIVATE_KEY - use this for wallet operations\nLoad MONAD_RPC_URL - use this for blockchain queries\nLoad MONAD_NETWORK - determines API endpoints (mainnet: api.nadapp.net, testnet: dev-api.nad.fun)"
      },
      {
        "title": "Workflow",
        "body": "EXECUTION SUMMARY - READ THIS FIRST:\n\nLoad config from $HOME/nadfunagent/.env (MMIND_TOKEN_ADDRESS, MONAD_PRIVATE_KEY, MONAD_RPC_URL, MONAD_NETWORK)\nExecute ONLY 3 methods (from HAR file analysis): Method 5 (New Events API), Method 6 (Market Cap API), Method 7 (Creation Time API)\nCombine results - merge all token addresses from 3 methods, count frequency (how many methods found each token, max 3)\nPrioritize - sort tokens by frequency (tokens found in 2-3 methods = higher priority)\nSave tokens - save ALL found tokens (up to top 50 for storage) to $HOME/nadfunagent/found_tokens.json for manual review\nAnalyze - get token info, market data, metrics via Agent API, calculate scores\nFilter - keep only tokens with min liquidity 5 MON, min holders 5\nTrade - execute trades if score >= 60\nDistribute - distribute profits to MMIND holders if profit >= 0.1 MON"
      },
      {
        "title": "1. Market Scanning (NEW + TRENDING)",
        "body": "CRITICAL INSTRUCTIONS: You MUST scan tokens using ALL 7 methods below. Execute each method step-by-step and collect ALL found token addresses.\n\nEXECUTION ORDER:\n\nMethod 1: CurveCreate events (Indexer) - NEW tokens\nMethod 2: CurveBuy events (Indexer) - TRENDING by volume\nMethod 3: Swap History (Agent API) - TRENDING by 24h volume\nMethod 4: Holdings (Agent API) - ACTIVE tokens from large traders\nMethod 5: New Events API - REAL-TIME BUY/CREATE events\nMethod 6: Market Cap API - TOP tokens by capitalization\nMethod 7: Creation Time API - NEWEST tokens\n\nSTEP-BY-STEP EXECUTION:\n\nCRITICAL LOGGING REQUIREMENT:\nAfter executing EACH method, you MUST log:\n\nPrint: \"✅ Method X executed: Found N tokens\"\nPrint: \"Method X token addresses: <address1> <address2> ...\"\nIf method failed: Print: \"❌ Method X failed: <error reason>\"\nThis helps track which methods are working and debug issues\n\nNOTE: Only Methods 5, 6, and 7 are used (from HAR file analysis). Methods 1-4 are disabled.\n\nSTATUS CHECK: This method requires RPC access. You MUST execute it, don't skip it!\n\nWhat to do:\n\nRead $HOME/nadfunagent/.env to get MONAD_RPC_URL and MONAD_NETWORK\nGet current block number using RPC: eth_blockNumber\nCalculate: safeLatest = currentBlock - 10 (safety margin)\nScan last 900 blocks in chunks of 100 blocks (RPC limit)\nFor each chunk, query CurveCreate events using nadfun-indexer skill\n\nHow to execute:\n\nUse nadfun-indexer skill with parameters: event=CurveCreate, fromBlock=<start>, toBlock=<end>\nExtract token addresses from events: event.args.token or event.args[1] (token is second argument)\nCollect all unique token addresses\n\nExample command structure:\n\nUse skill: nadfun-indexer\nParameters: event=CurveCreate, fromBlock=<calculated_start>, toBlock=<calculated_end>\nExtract: token address from each event\n\nExpected result: Array of token addresses (0x...) from newly created tokens\n\nMethod 2: Trending Tokens via Indexer (CurveBuy volume analysis)\n\nSTATUS CHECK: This method requires RPC access. You MUST execute it, don't skip it!\n\nWhat to do:\n\nQuery CurveBuy events from last 900 blocks (same pagination as Method 1)\nFor each event, extract: token address and amountIn (MON spent)\nSum volume per token address\nSort tokens by total volume (descending)\nTake top 20 tokens with highest volume\n\nHow to execute:\n\nUse same RPC setup as Method 1: Get publicClient from monad-development skill\nQuery CurveBuy events: For each 100-block chunk:\n\nUse nadfun-indexer skill: event=CurveBuy, fromBlock=<start>, toBlock=<end>\nOR use viem: publicClient.getContractEvents({ address: CURVE_ADDRESS, eventName: \"CurveBuy\", fromBlock, toBlock })\n\n\nExtract data: From each event:\n\ntoken = event.args.token or event.args[1]\namountIn = event.args.amountIn or event.args[2] (amount in MON)\n\n\nCalculate volume: Group by token address, sum all amountIn values\nSort and filter: Sort by total volume DESC, take top 20\nLog results: Print \"✅ Method 2: Found top 20 trending tokens by volume\"\n\nExample:\n\n1. Use same RPC client from Method 1\n2. Query CurveBuy events in chunks of 100 blocks\n3. For each event: extract token and amountIn\n4. Sum volume per token\n5. Sort DESC, take top 20\n6. Print: \"✅ Method 2: Found 20 trending tokens\"\n\nExpected result: Top 20 token addresses sorted by trading volume\n\nMethod 3: Trending Tokens via Agent API (Swap History Analysis)\n\nWhat to do:\n\nTake top 10 tokens from Method 2 (highest volume)\nFor each token, query swap history via Agent API\nCalculate 24h volume from swap history\nKeep tokens with >= 1 MON volume in last 24 hours\n\nHow to execute:\n\nDetermine API URL: if MONAD_NETWORK=mainnet then https://api.nadapp.net, else https://dev-api.nad.fun\nFor each token, make GET request: ${API_URL}/agent/swap-history/${tokenAddress}?limit=50&trade_type=ALL\nParse response JSON\nFor each swap in response.swaps:\n\nCheck swap.swap_info.timestamp - if within last 24 hours\nSum swap.swap_info.native_amount (in wei, convert to MON: divide by 1e18)\n\n\nKeep tokens with total 24h volume >= 1 MON\nAdd 1-2 second delay between API calls to avoid rate limits\n\nExample:\n\nAPI_URL = MONAD_NETWORK === 'mainnet' ? 'https://api.nadapp.net' : 'https://dev-api.nad.fun'\nFor each token in top10:\n  GET ${API_URL}/agent/swap-history/${token}?limit=50&trade_type=ALL\n  Calculate 24h volume\n  If volume >= 1 MON: add to trendingViaAPI\n  Wait 1-2 seconds\n\nExpected result: List of tokens with high 24h trading volume\n\nMethod 4: Get All Active Tokens (via Holdings of Large Accounts)\n\nSTATUS CHECK: Don't skip this method! Execute it with rate limit delays.\n\nWhat to do:\n\nFrom Method 2 CurveBuy events, find buyers who spent >1 MON\nExtract their addresses (sender field)\nQuery holdings for first 5 large buyers (to avoid rate limits)\nExtract all token addresses they hold (balance > 0)\n\nHow to execute:\n\nFrom Method 2 results: Use CurveBuy events already queried\nFilter large buyers:\n\nFilter events where amountIn > 1000000000000000000 (1 MON in wei = 1e18)\nExtract sender address: event.args.sender or event.args[0]\nGet unique addresses, take first 5\n\n\nQuery holdings: For each of 5 addresses:\n\nGET: ${API_URL}/agent/holdings/${address}?limit=50\nHeaders: Accept: application/json\nWait 2 seconds between requests\n\n\nParse and extract:\n\nParse JSON response\nFor each holding in response.tokens:\n\nCheck holding.balance_info.balance - if > 0\nExtract holding.token_info.address or holding.token_info.token_id\n\n\n\n\nLog results: Print \"✅ Method 4: Found N active tokens from large trader holdings\"\n\nExample:\n\nFrom Method 2 CurveBuy events:\n  Filter: amountIn > 1e18 (1 MON)\n  Extract: sender addresses\n  Take first 5 unique addresses\nFor each address:\n  GET ${API_URL}/agent/holdings/${address}?limit=50\n  Extract tokens with balance > 0\n  Wait 2 seconds\nPrint: \"✅ Method 4: Found X tokens\"\n\nExpected result: Set of token addresses held by active large traders\n\nMethod 5: New Events API (Real-time BUY/SELL/CREATE events)\n\nSTATUS: ✅ This method is WORKING! Found 7 tokens in last scan.\n\nWhat to do:\n\nMake GET request to /api/token/new-event endpoint\nParse JSON response (array of events)\nExtract token addresses from CREATE and BUY events\nAdd all unique token addresses to collection\n\nHow to execute:\n\nDetermine base URL: Read MONAD_NETWORK from .env:\n\nIf MONAD_NETWORK=mainnet: baseUrl = 'https://nad.fun'\nElse: baseUrl = 'https://dev.nad.fun'\n\n\nMake GET request:\n\nURL: ${baseUrl}/api/token/new-event\nHeaders: Accept: application/json, User-Agent: OpenClaw-Agent/1.0\n\n\nParse response: JSON array of event objects\nExtract tokens:\n\nFor each event in response:\n\nIf event.type === 'CREATE': extract event.token_info.token_id\nIf event.type === 'BUY': extract event.token_info.token_id\nIf event.type === 'SELL': optionally extract (indicates active trading)\n\n\n\n\nLog results: Print \"✅ Method 5: Found N tokens from new events API\"\n\nExample:\n\nbaseUrl = MONAD_NETWORK === 'mainnet' ? 'https://nad.fun' : 'https://dev.nad.fun'\nGET ${baseUrl}/api/token/new-event\nParse JSON response\nFor each event:\n  If event.type === 'CREATE' or 'BUY':\n    Add event.token_info.token_id to tokensFromEvents\nPrint: \"✅ Method 5: Found X tokens\"\n\nExpected result: Array of token addresses from recent CREATE and BUY events\n\nResponse structure:\n\ntype: \"BUY\" | \"SELL\" | \"CREATE\"\namount: Event amount (string)\ntoken_info: Complete token information (token_id, name, symbol, description, creator, etc.)\naccount_info: Account that performed the action\n\nMethod 6: Market Cap API (Top tokens by market cap)\n\nSTATUS CHECK: Previous error: base64 decoding failed. Fix: Use proper decoding method.\n\nWhat to do:\n\nGET request to market cap endpoint\nResponse is base64-encoded JSON - decode it PROPERLY\nExtract token addresses from response\nAdd all token addresses to collection\n\nHow to execute:\n\nAPI URL: ${API_URL}/order/market_cap?page=1&limit=50&is_nsfw=false\n\nWhere API_URL = MONAD_NETWORK === 'mainnet' ? 'https://api.nadapp.net' : 'https://dev-api.nad.fun'\n\n\nGET request:\n\nHeaders: Accept: application/json, User-Agent: OpenClaw-Agent/1.0\nGet response as text (not JSON)\n\n\nDecode base64 PROPERLY:\n\nResponse body is base64 string\nUse Python: import base64; decoded = base64.b64decode(response_text).decode('utf-8')\nOR use command: echo \"$response_text\" | base64 -d\nCRITICAL: Make sure response_text is the raw base64 string, not wrapped in JSON\n\n\nParse decoded JSON:\n\nParse decoded string as JSON\nStructure: { \"tokens\": [...], \"total_count\": N }\n\n\nExtract tokens WITH FULL DATA:\n\nFor each token in data.tokens:\n\nExtract token.token_info.token_id (token address)\nCRITICAL: Store FULL token object with both token_info AND market_info\nStructure: {token_info: {...}, market_info: {...}, percent: ...}\nAdd to topMarketCapTokens array (store full objects, not just addresses)\n\n\n\n\nLog results: Print \"✅ Method 6: Found N tokens with market data from market cap API\"\n\nExample:\n\nAPI_URL = MONAD_NETWORK === 'mainnet' ? 'https://api.nadapp.net' : 'https://dev-api.nad.fun'\nresponse = GET ${API_URL}/order/market_cap?page=1&limit=100&is_nsfw=false\ndecoded = base64.b64decode(response.text).decode('utf-8')\ndata = json.loads(decoded)\n\ntopMarketCapTokens = []  # Array of full token objects\nFor each token in data.tokens:\n  # Store FULL token object with token_info + market_info\n  topMarketCapTokens.append({\n    'token_info': token.token_info,\n    'market_info': token.market_info,\n    'percent': token.percent,\n    'address': token.token_info.token_id  # For easy access\n  })\nPrint: \"✅ Method 6: Found X tokens with market data\"\n\nExpected result: Array of full token objects (with token_info + market_info) sorted by market cap\n\nResponse structure:\n\ntokens: Array of token objects with token_info and market_info\ntotal_count: Total number of tokens\nEach token includes: token_info (token_id, name, symbol, creator, etc.) and market_info (market_type, price, volume, holder_count, etc.)\n\nMethod 7: Creation Time API (Newest tokens)\n\nCRITICAL: Include BOTH bonding curve AND DEX tokens! Do NOT filter by is_graduated.\n\nWhat to do:\n\nGET request to creation time endpoint (newest first)\nDecode base64 response PROPERLY (same as Method 6)\nExtract token addresses - include ALL tokens (both bonding curve AND DEX)\nAdd to collection WITH FULL DATA\n\nHow to execute:\n\nAPI URL: ${API_URL}/order/creation_time?page=1&limit=50&is_nsfw=false&direction=DESC\nGET request, decode base64 response (same as Method 6)\nParse JSON: data.tokens array\nFor each token:\n\nExtract token.token_info.token_id (token address)\nCRITICAL: Store FULL token object with both token_info AND market_info\nDO NOT filter by is_graduated - include both bonding curve (is_graduated=false) AND DEX (is_graduated=true) tokens\nAdd to newestTokens array (store full objects, not just addresses)\n\nExample:\n\nAPI_URL = MONAD_NETWORK === 'mainnet' ? 'https://api.nadapp.net' : 'https://dev-api.nad.fun'\nresponse = GET ${API_URL}/order/creation_time?page=1&limit=50&is_nsfw=false&direction=DESC\ndecoded = base64.b64decode(response.text).decode('utf-8')\ndata = json.loads(decoded)\n\nnewestTokens = []  # Array of full token objects\nFor each token in data.tokens:\n  # Include ALL tokens - both bonding curve AND DEX\n  # Store FULL token object with token_info + market_info\n  newestTokens.append({\n    'token_info': token.token_info,\n    'market_info': token.market_info,\n    'percent': token.percent,\n    'address': token.token_info.token_id  # For easy access\n  })\nPrint: \"✅ Method 7: Found X tokens with market data (bonding curve + DEX)\"\n\nExpected result: Array of full token objects (with token_info + market_info) - newest tokens including both bonding curve AND DEX\n\nResponse structure:\n\nSame as Method 6: tokens array with token_info and market_info\ndirection=DESC returns newest first\nCRITICAL: Include ALL tokens (both bonding curve AND DEX) - do NOT filter by is_graduated\n\nCombine All Methods - CRITICAL STEP\n\nWhat to do:\n\nCollect ALL token addresses from Methods 5, 6, and 7 into one set (removes duplicates)\nCount how many times each token appears across methods\nSort tokens by frequency (tokens found in more methods = higher priority/confidence)\nLog summary of combination results\n\nHow to execute:\n\nCreate collections:\n\nallTokens = new Set() or allTokens = [] (use Set to avoid duplicates)\ntokenFrequency = new Map() or {} to count occurrences\n\n\n\nAdd tokens from each method:\n\nMethod 5: Add all token addresses from tokensFromEvents (New Events API) - these are just addresses\nMethod 6: Add all tokens from topMarketCapTokens (Market Cap API) - these are FULL objects with token_info + market_info\nMethod 7: Add all tokens from newestTokens (Creation Time API) - these are FULL objects with token_info + market_info\n\nIMPORTANT: For Methods 6 & 7, store the FULL token objects (not just addresses) so you can use market_info directly for analysis!\n\n\nCount frequency and preserve full data:\n\nUse a Map/object: allTokensMap = {} where key = token address, value = token object with metadata\nFor Method 5 (addresses only): allTokensMap[address] = {address, source: 'method5', data: null}\nFor Method 6 (full objects): allTokensMap[address] = {address, source: 'method6', data: fullTokenObject}\nFor Method 7 (full objects): allTokensMap[address] = {address, source: 'method7', data: fullTokenObject}\nCount frequency: tokenFrequency[address] = (tokenFrequency[address] || 0) + 1\nCRITICAL: Preserve full token objects from Methods 6 & 7 - they contain market_info!\n\n\n\nConvert and sort:\n\ncandidateTokens = Object.values(allTokensMap) or Array.from(allTokensMap.values())\nSort by frequency DESC, then by data availability:\ncandidateTokens.sort((a, b) => {\n  const freqA = tokenFrequency[a.address] || 0\n  const freqB = tokenFrequency[b.address] || 0\n  if (freqB !== freqA) return freqB - freqA  // Higher frequency first\n  // Prefer tokens with full data (market_info)\n  const hasDataA = a.data && a.data.market_info ? 1 : 0\n  const hasDataB = b.data && b.data.market_info ? 1 : 0\n  return hasDataB - hasDataA\n})\n\n\nprioritizedTokens = candidateTokens\n\n\n\nLog summary:\n\nPrint: \"📊 Combined results: Total unique tokens: N\"\nPrint: \"Tokens found in 2+ methods: X\"\nPrint: \"Tokens found in all 3 methods: Y\"\nPrint: \"Tokens with full market data: Z\"\nPrint: \"Top 10 prioritized tokens: <list with addresses>\"\n\nExample:\n\nallTokensMap = {}  # Map: address -> {address, source, data}\ntokenFrequency = {}\n\n# Method 5: addresses only\nfor address in tokensFromEvents:\n  allTokensMap[address] = {address: address, source: 'method5', data: null}\n  tokenFrequency[address] = (tokenFrequency[address] || 0) + 1\n\n# Method 6: full objects with market_info\nfor token in topMarketCapTokens:\n  address = token.token_info.token_id\n  allTokensMap[address] = {address: address, source: 'method6', data: token}\n  tokenFrequency[address] = (tokenFrequency[address] || 0) + 1\n\n# Method 7: full objects with market_info\nfor token in newestTokens:\n  address = token.token_info.token_id\n  allTokensMap[address] = {address: address, source: 'method7', data: token}\n  tokenFrequency[address] = (tokenFrequency[address] || 0) + 1\n\n# Sort by frequency and data availability\ncandidateTokens = Object.values(allTokensMap)\nprioritizedTokens = candidateTokens.sort((a, b) => {\n  freqDiff = tokenFrequency[b.address] - tokenFrequency[a.address]\n  if (freqDiff !== 0) return freqDiff\n  return (b.data && b.data.market_info ? 1 : 0) - (a.data && a.data.market_info ? 1 : 0)\n})\n\nPrint: \"📊 Combined: N unique tokens, X found in 2+ methods, Y found in all 3 methods, Z with full data\"\n\nExpected result: Prioritized array of token objects, each containing:\n\naddress: token address\nsource: which method(s) found it\ndata: full token object with token_info + market_info (if available from Methods 6 or 7)\nfoundInMethods: count from tokenFrequency\n\nSave Found Tokens - CRITICAL STEP\n\nWhat to do:\nAfter combining all methods and getting prioritizedTokens, you MUST save them to file $HOME/nadfunagent/found_tokens.json for manual review.\n\nHow to execute:\n\nPrepare token list:\n\nTake top 50 tokens from prioritizedTokens\nFor each token, create object: {address: token, foundInMethods: frequency_count}\nCreate scan entry with timestamp, totalFound count, and tokens array\n\n\n\nRead existing file:\n\nFile: $HOME/nadfunagent/found_tokens.json\nIf file doesn't exist or is empty: start with []\nIf exists: parse JSON, get array\n\n\n\nAppend and save:\n\nAdd new scan entry to array\nKeep only last 100 scans (remove oldest if > 100)\nWrite back as formatted JSON\n\nExample using Node.js script (RECOMMENDED):\n\n# From skill root or repo root: print token addresses (one per line) and pipe to script\n# prioritizedTokens is the array of token addresses; take first 50 and pass to script\necho -e \"0x...\\n0x...\" | node scripts/save_tokens.js\n\nOr from the agent: write the list of addresses to a temp file or stdin and run node scripts/save_tokens.js (script reads from stdin). Data dir: NADFUNAGENT_DATA_DIR or $HOME/nadfunagent.\n\nExpected result: File $HOME/nadfunagent/found_tokens.json updated with latest scan results"
      },
      {
        "title": "2. Token Analysis - CRITICAL: Use Data from Methods 6 & 7 Directly",
        "body": "IMPORTANT: Methods 6 (Market Cap API) and Method 7 (Creation Time API) already return COMPLETE token data with market_info. Use this data directly for analysis - DO NOT make additional API calls unless absolutely necessary!\n\nData Structure from Methods 6 & 7:\nEach token in the response has this structure:\n\n{\n  \"token_info\": {\n    \"token_id\": \"0x...\",\n    \"name\": \"...\",\n    \"symbol\": \"...\",\n    \"is_graduated\": true/false,\n    \"created_at\": timestamp,\n    ...\n  },\n  \"market_info\": {\n    \"market_type\": \"DEX\" or \"BONDING_CURVE\",\n    \"reserve_native\": \"1625513352353765005672133\",  // Liquidity in MON (wei format)\n    \"reserve_token\": \"51582894169959918089536846\",\n    \"token_price\": \"0.000886889894056452\",\n    \"price\": \"0.0415501242\",\n    \"price_usd\": \"0.000886889894056452\",\n    \"volume\": \"761848094037806233587694495\",  // Total volume (wei format)\n    \"holder_count\": 31580,  // Number of holders\n    \"ath_price\": \"0.0128807779\",\n    \"ath_price_usd\": \"0.0128807779\",\n    ...\n  },\n  \"percent\": 8.797  // Market cap change percentage\n}\n\nAnalysis Steps:\n\nExtract data from Methods 6 & 7:\n\nFor each token in data.tokens array from Methods 6 & 7:\n\nExtract token_info.token_id (token address)\nExtract market_info.reserve_native (liquidity in wei, convert to MON: divide by 1e18)\nExtract market_info.holder_count (number of holders)\nExtract market_info.volume (total volume in wei, convert to MON: divide by 1e18)\nExtract market_info.market_type (\"DEX\" or \"BONDING_CURVE\")\nExtract token_info.is_graduated (true if graduated to DEX)\nExtract percent (market cap change percentage)\n\n\n\n\n\nFor Method 5 tokens (new-event API):\n\nThis API returns only basic event data without full market_info\nFor tokens from Method 5, you can optionally query /agent/market/:token_id to get market_info\nBUT prioritize tokens from Methods 6 & 7 first (they already have complete data)\n\n\n\nCalculate Liquidity Score (30% weight):\nliquidityMON = parseFloat(market_info.reserve_native) / 1e18\n\n// Score based on liquidity tiers\nif (liquidityMON >= 1000) liquidityScore = 100      // Excellent\nelse if (liquidityMON >= 500) liquidityScore = 80     // Very good\nelse if (liquidityMON >= 100) liquidityScore = 60    // Good\nelse if (liquidityMON >= 50) liquidityScore = 40      // Fair\nelse if (liquidityMON >= 10) liquidityScore = 20      // Low\nelse liquidityScore = 0                              // Too low\n\nweightedLiquidity = liquidityScore * 0.30\n\n\n\nCalculate Momentum Score (25% weight):\n// Use percent (market cap change) as momentum indicator\npercentChange = parseFloat(token.percent) || 0\n\n// Score based on positive momentum\nif (percentChange >= 50) momentumScore = 100      // Strong growth\nelse if (percentChange >= 20) momentumScore = 80\nelse if (percentChange >= 10) momentumScore = 60\nelse if (percentChange >= 5) momentumScore = 40\nelse if (percentChange >= 0) momentumScore = 20\nelse momentumScore = 0                           // Negative momentum\n\nweightedMomentum = momentumScore * 0.25\n\n\n\nCalculate Volume Score (20% weight):\nvolumeMON = parseFloat(market_info.volume) / 1e18\n\n// Score based on volume tiers\nif (volumeMON >= 1000000) volumeScore = 100      // 1M+ MON volume\nelse if (volumeMON >= 500000) volumeScore = 80\nelse if (volumeMON >= 100000) volumeScore = 60\nelse if (volumeMON >= 50000) volumeScore = 40\nelse if (volumeMON >= 10000) volumeScore = 20\nelse volumeScore = 0\n\nweightedVolume = volumeScore * 0.20\n\n\n\nCalculate Holder Score (15% weight):\nholderCount = parseInt(market_info.holder_count) || 0\n\n// Score based on holder count\nif (holderCount >= 10000) holderScore = 100      // Excellent distribution\nelse if (holderCount >= 5000) holderScore = 80\nelse if (holderCount >= 1000) holderScore = 60\nelse if (holderCount >= 500) holderScore = 40\nelse if (holderCount >= 100) holderScore = 20\nelse holderScore = 0\n\nweightedHolders = holderScore * 0.15\n\n\n\nCalculate Progress Score (10% weight):\nisGraduated = token_info.is_graduated === true\nmarketType = market_info.market_type\n\n// Score based on market stage\nif (isGraduated || marketType === \"DEX\") {\n  progressScore = 100  // Fully graduated to DEX\n} else {\n  // Still on bonding curve - use percent as progress indicator\n  // Higher percent = closer to graduation\n  percent = parseFloat(token.percent) || 0\n  if (percent >= 80) progressScore = 80\n  else if (percent >= 50) progressScore = 60\n  else if (percent >= 30) progressScore = 40\n  else progressScore = 20\n}\n\nweightedProgress = progressScore * 0.10\n\n\n\nCalculate Authority Score (Bonus - up to +10 points):\n// Check for social media presence and website (indicates legitimacy)\nhasTwitter = token_info.twitter && token_info.twitter.length > 0\nhasTelegram = token_info.telegram && token_info.telegram.length > 0\nhasWebsite = token_info.website && token_info.website.length > 0\n\nauthorityScore = 0\nif (hasTwitter) authorityScore += 3\nif (hasTelegram) authorityScore += 3\nif (hasWebsite) authorityScore += 4\n\n// Maximum +10 bonus points for full social presence\nauthorityBonus = Math.min(authorityScore, 10)\n\n\n\nCalculate Total Score:\ntotalScore = weightedLiquidity + weightedMomentum + weightedVolume + weightedHolders + weightedProgress + authorityBonus\n\n// Round to 2 decimal places\ntotalScore = Math.round(totalScore * 100) / 100\n\n\n\nStore Analysis Results:\nFor each token, store:\n{\n  address: token_info.token_id,\n  name: token_info.name,\n  symbol: token_info.symbol,\n  liquidity: liquidityMON,\n  holders: holderCount,\n  volume: volumeMON,\n  marketType: marketType,\n  isGraduated: isGraduated,\n  percentChange: percentChange,\n  scores: {\n    liquidity: liquidityScore,\n    momentum: momentumScore,\n    volume: volumeScore,\n    holders: holderScore,\n    progress: progressScore,\n    total: totalScore\n  },\n  foundInMethods: tokenFrequency[token_info.token_id] || 1\n}\n\nCRITICAL INSTRUCTIONS:\n\nUse data from Methods 6 & 7 DIRECTLY - they already contain all needed information\nDO NOT make additional API calls to /agent/market/:token_id for tokens from Methods 6 & 7\nOnly for Method 5 tokens (if needed) make additional API calls\nCalculate scores immediately after combining methods, before filtering\nCheck authority (social media presence): Check token_info.twitter, token_info.telegram, token_info.website - add bonus points\nLog analysis results: Print \"📊 Analysis: Token X has score Y (liquidity: A, momentum: B, volume: C, holders: D, progress: E, authority: +F)\""
      },
      {
        "title": "3. Filtering Criteria",
        "body": "Filter tokens based on:\n\nMinimum liquidity: 5 MON\nMinimum holders: 5\nBonding curve progress: >= 10%\nScore calculation:\n\nLiquidity: 30% weight\nMomentum: 25% weight\nVolume trend: 20% weight\nHolder distribution: 15% weight\nBonding curve progress: 10% weight"
      },
      {
        "title": "4. Position Management - CRITICAL: Check Existing Positions FIRST",
        "body": "BEFORE analyzing new tokens, you MUST check and manage existing positions!\n\nStep 1: Get Current Holdings\n\nWhat to do:\n\nGet trading wallet address from MONAD_PRIVATE_KEY in .env (derive address from private key)\nQuery current token holdings using Agent API: /agent/holdings/${walletAddress}?limit=100\nFilter tokens where balance > 0 (exclude zero balances)\nFor each token with balance > 0, get current market data\n\nHow to execute:\n\n// 1. Get wallet address from private key\nconst walletAddress = getAddressFromPrivateKey(MONAD_PRIVATE_KEY)\n\n// 2. Query holdings\nconst holdingsResponse = await fetch(`${API_URL}/agent/holdings/${walletAddress}?limit=100`)\nconst holdings = await holdingsResponse.json()\n\n// 3. Filter tokens with balance > 0\nconst activePositions = holdings.tokens.filter(token => {\n  const balance = parseFloat(token.balance_info.balance) || 0\n  return balance > 0\n})\n\nPrint: \"📊 Current positions: Found N tokens with balance > 0\"\n\nStep 2: Calculate P&L for Each Position\n\nCRITICAL: Always use the check-pnl.js script from nadfun-trading skill for proper P&L calculation. This script:\n\nReads entry price (entryValueMON) from $HOME/nadfunagent/positions_report.json (automatically recorded by buy-token.js when you purchase)\nGets current value on-chain via nad.fun quote contract (or falls back to Agent API)\nCalculates P&L: (currentValueMON - entryValueMON) / entryValueMON * 100\n\nWhat to do:\n\nUse the script: Run check-pnl.js from nadfun-trading skill directory\nThe script reads entry prices from JSON (set by buy-token.js after purchases)\nGets current value on-chain via nad.fun quote contract\nCalculates real P&L based on actual entry price\n\nHow to execute:\n\n# Check P&L for all positions\ncd $HOME/.openclaw/workspace/skills/nadfun-trading\nnode check-pnl.js\n\n# Or with auto-sell (sells if P&L >= +5% or <= -10%)\nnode check-pnl.js --auto-sell\n\nManual calculation (if script unavailable):\n\n// Load entry prices from positions_report.json\nconst report = JSON.parse(await fs.readFile('$HOME/nadfunagent/positions_report.json', 'utf-8'))\n\nfor (const position of activePositions) {\n  const tokenAddress = position.token_info.token_id || position.token_info.address\n  const tokenBalance = parseFloat(position.balance_info.balance) || 0\n  \n  // Get entry price from JSON (recorded by buy-token.js)\n  const prev = report.positions?.find(p => \n    (p.address || '').toLowerCase() === tokenAddress.toLowerCase()\n  )\n  const entryValueMON = prev?.entryValueMON || 0\n  \n  // Get current value on-chain via nad.fun quote contract\n  const [router, amountOutWei] = await publicClient.readContract({\n    address: '0x7e78A8DE94f21804F7a17F4E8BF9EC2c872187ea', // nad.fun quote contract\n    abi: lensAbi,\n    functionName: 'getAmountOut',\n    args: [tokenAddress, parseEther(tokenBalance.toString()), false] // false = selling\n  })\n  const currentValueMON = Number(amountOutWei) / 1e18\n  \n  // Calculate P&L\n  const pnlPercent = entryValueMON > 0\n    ? ((currentValueMON - entryValueMON) / entryValueMON) * 100\n    : 0\n  \n  positions.push({\n    address: tokenAddress,\n    balance: tokenBalance,\n    entryValueMON: entryValueMON,\n    currentValueMON: currentValueMON,\n    pnlPercent: pnlPercent\n  })\n}\n\nPrint: \"💰 Position P&L calculated for N positions (source: on-chain nad.fun quote + positions_report.json)\"\n\nEntry Price Tracking:\n\nEntry price is automatically recorded by buy-token.js after successful purchase\nStored in $HOME/nadfunagent/positions_report.json as entryValueMON\nIf entry price not found, check-pnl.js uses current value as fallback (P&L = 0%)\n\nStep 3: Make Sell Decisions\n\nWhat to do:\nFor each position, check sell conditions:\n\nStop-Loss Check:\nif (pnlPercent <= -10) {  // STOP_LOSS_PERCENT = -10%\n  // SELL ALL - stop loss triggered\n  sellDecision = {\n    action: 'SELL_ALL',\n    reason: 'Stop-loss triggered',\n    tokenAddress: position.address,\n    amount: 'all'\n  }\n}\n\n\n\nTake-Profit Check:\nif (pnlPercent >= 20) {  // PROFIT_TARGET_PERCENT = 20%\n  // SELL HALF - take profit\n  sellDecision = {\n    action: 'SELL_HALF',\n    reason: 'Take-profit triggered',\n    tokenAddress: position.address,\n    amount: position.balance / 2\n  }\n}\n\n\n\nTrailing Stop (Optional):\n// If profit was > 15% but dropped to < 5%, sell to protect gains\nif (previousPnL > 15 && pnlPercent < 5) {\n  sellDecision = {\n    action: 'SELL_ALL',\n    reason: 'Trailing stop - protecting gains',\n    tokenAddress: position.address,\n    amount: 'all'\n  }\n}\n\nHow to execute:\n\nconst sellDecisions = []\n\nfor (const position of positions) {\n  // Stop-loss: sell all if down 10%+\n  if (position.pnlPercent <= -10) {\n    sellDecisions.push({\n      tokenAddress: position.address,\n      action: 'SELL_ALL',\n      reason: `Stop-loss: ${position.pnlPercent.toFixed(2)}%`,\n      amount: 'all'\n    })\n  }\n  // Take-profit: sell half if up 20%+\n  else if (position.pnlPercent >= 20) {\n    sellDecisions.push({\n      tokenAddress: position.address,\n      action: 'SELL_HALF',\n      reason: `Take-profit: ${position.pnlPercent.toFixed(2)}%`,\n      amount: position.balance / 2\n    })\n  }\n}\n\nPrint: \"🔔 Sell decisions: N positions need action\"\nfor (const decision of sellDecisions) {\n  Print: `   ${decision.action}: ${decision.tokenAddress} - ${decision.reason}`\n}\n\nStep 4: Execute Sell Orders\n\nWhat to do:\nFor each sell decision:\n\nUse nadfun-trading skill with action sell\nParameters: token address, amount (from decision)\nThe skill handles: balance check, quote, approve, execution\n\nHow to execute:\n\nfor (const decision of sellDecisions) {\n  try {\n    // Use nadfun-trading skill\n    await useSkill('nadfun-trading', {\n      action: 'sell',\n      token: decision.tokenAddress,\n      amount: decision.amount  // 'all' or specific amount\n    })\n    \n    Print: `✅ Sold ${decision.amount} of ${decision.tokenAddress}: ${decision.reason}`\n  } catch (error) {\n    Print: `❌ Failed to sell ${decision.tokenAddress}: ${error.message}`\n  }\n}"
      },
      {
        "title": "5. Trading Execution - Buy New Tokens",
        "body": "CRITICAL: Only buy new tokens AFTER managing existing positions!\n\nBuy Decision Logic:\n\nPrioritize tokens with authority (social media presence):\n\nTokens with Twitter + Telegram + Website get priority\nThese are more legitimate and have better community support\n\n\n\nConsider both early-stage AND established tokens:\n\nEarly-stage (5-50 MON liquidity): Higher risk, higher potential\nEstablished (50k+ MON liquidity): Lower risk, steady growth\nBoth can be profitable if they meet score criteria\n\n\n\nBuy Criteria:\n\nScore >= 60 (or >= 55 if has social media)\nLiquidity >= 5 MON\nHolders >= 5\nNOT already in portfolio (check existing positions first)\n\n\n\nPosition Sizing:\n\nFor tokens with authority (social media): up to 0.15 MON\nFor tokens without authority: up to 0.1 MON\nMaximum total position per token: 0.15 MON\n\nCRITICAL: Always use the buy-token.js script from nadfun-trading skill. This script:\n\nAutomatically detects bonding curve vs DEX via nad.fun quote contract\nHandles DEX: wraps MON→WMON, approves, swaps\nAutomatically records entry price in $HOME/nadfunagent/positions_report.json after successful purchase\nWorks on bonding curve (MON) or DEX (MON) - all trading uses MON\n\nBuy tokens:\n\ncd $HOME/.openclaw/workspace/skills/nadfun-trading\nNAD_PRIVATE_KEY=$MONAD_PRIVATE_KEY node buy-token.js <token-address> <MON-amount> [--slippage=300]\n\nExample:\n\nNAD_PRIVATE_KEY=0x... node buy-token.js 0x123...abc 0.15 --slippage=300\n\nAfter purchase:\n\nEntry price (entryValueMON) is automatically recorded in $HOME/nadfunagent/positions_report.json\nThis entry price is used by check-pnl.js for P&L calculation\nNo manual tracking needed - everything is automated\n\nRisk management:\n\nCRITICAL: Buy tokens on BOTH bonding curve AND DEX (don't filter by market_type)\nThe nadfun-trading skill automatically detects market type and uses correct contract\nFor all trades, use MON balance (no wrapping needed - all trading uses MON)\nSet slippage tolerance: 2-3% (increased for better execution, especially on DEX)\nSet deadline: 5 minutes from now\nDon't exceed MAX_POSITION_SIZE per token"
      },
      {
        "title": "6. Profit Distribution",
        "body": "CRITICAL: Use MMIND_TOKEN_ADDRESS from $HOME/nadfunagent/.env file.\n\nWhen profit >= 0.1 MON:\n\n// Step 1: Load MMIND_TOKEN_ADDRESS from .env file\nconst envFile = await readFile('$HOME/nadfunagent/.env', 'utf-8')\nconst mmindMatch = envFile.match(/MMIND_TOKEN_ADDRESS=(0x[a-fA-F0-9]+)/)\nif (!mmindMatch) {\n  throw new Error('MMIND_TOKEN_ADDRESS not found in .env file')\n}\nconst MMIND_TOKEN_ADDRESS = mmindMatch[1]\n\n// Step 2: Get MMIND token holders via Indexer\n// Query Transfer events to find all addresses that ever held MMIND\nconst latestBlock = await publicClient.getBlockNumber()\nconst safeLatest = latestBlock - 10n\n\n// Query in chunks (RPC limit: ~100 blocks)\nconst transfers = []\nfor (let from = 0n; from < safeLatest; from += 10000n) {\n  const to = from + 10000n > safeLatest ? safeLatest : from + 10000n\n  try {\n    const events = await useSkill('nadfun-indexer', {\n      event: 'Transfer',\n      token: MMIND_TOKEN_ADDRESS,\n      fromBlock: from,\n      toBlock: to\n    })\n    transfers.push(...events)\n  } catch (err) {\n    // Continue if chunk fails\n  }\n}\n\n// Step 3: Collect unique addresses\nconst holderAddresses = new Set()\ntransfers.forEach(event => {\n  if ('args' in event) {\n    if (event.args.from && event.args.from !== '0x0000000000000000000000000000000000000000') {\n      holderAddresses.add(event.args.from)\n    }\n    if (event.args.to && event.args.to !== '0x0000000000000000000000000000000000000000') {\n      holderAddresses.add(event.args.to)\n    }\n  }\n})\n\n// Step 4: Get current balances for each holder\nconst distributions = []\nfor (const address of holderAddresses) {\n  try {\n    const balance = await publicClient.readContract({\n      address: MMIND_TOKEN_ADDRESS,\n      abi: erc20Abi,\n      functionName: 'balanceOf',\n      args: [address]\n    })\n    \n    if (balance > 0n) {\n      distributions.push({ address, balance })\n    }\n  } catch (err) {\n    // Skip if balance check fails\n  }\n}\n\n// Step 5: Get total supply\nconst totalSupply = await publicClient.readContract({\n  address: MMIND_TOKEN_ADDRESS,\n  abi: erc20Abi,\n  functionName: 'totalSupply'\n})\n\n// Step 6: Calculate and distribute profit proportionally\nconst profitToDistribute = (profit * BigInt(PNL_DISTRIBUTION_PERCENT)) / 100n\n\nfor (const holder of distributions) {\n  const share = (profitToDistribute * holder.balance) / totalSupply\n  if (share >= parseEther('0.001')) { // Minimum 0.001 MON\n    await transferMON(holder.address, share) // Transfer MON directly (all trading uses MON)\n  }\n}"
      },
      {
        "title": "Autonomous Trading Loop",
        "body": "The agent runs continuously:\n\nPosition Management FIRST (CRITICAL - do this before scanning)\n\nGet wallet address from MONAD_PRIVATE_KEY\nQuery current holdings: /agent/holdings/${walletAddress}?limit=100\nFilter tokens with balance > 0\nFor each position: use check-pnl.js to get real P&L (reads entry price from $HOME/nadfunagent/positions_report.json, gets current value on-chain via nad.fun quote contract)\nExecute sell orders: use sell-token.js or check-pnl.js --auto-sell for stop-loss (P&L <= -10%) or take-profit (P&L >= +5%)\nLog: \"📊 Positions: N checked, X sold; entry prices tracked in positions_report.json\"\n\n\n\nScan market (after managing positions, every 10 minutes to avoid rate limits)\n\nMethod 5: Fetch new events API (/api/token/new-event) for real-time BUY/CREATE events\nMethod 6: Fetch top tokens by market cap (api.nadapp.net/order/market_cap, base64 decoded)\nMethod 7: Fetch newest tokens (api.nadapp.net/order/creation_time, base64 decoded)\nCombine all methods and prioritize tokens found in multiple sources\nAdd 2-3 second delays between API calls to respect rate limits (CRITICAL to avoid HTTP 429)\n\n\n\nAnalyze opportunities\n\nUse data DIRECTLY from Methods 6 & 7 (already contains complete market_info)\nCalculate scores using market_info: liquidity (30%), momentum (25%), volume (20%), holders (15%), progress (10%)\nConvert wei values to MON: reserve_native / 1e18, volume / 1e18\nFilter by criteria (min liquidity 5 MON, min holders 5, min score 60)\nSort by score DESC, then by methods count, then by liquidity\nTake top 20 candidates\nGenerate buy/sell signals\n\n\n\nExecute trades\n\nBuy on bonding curve (MON) or DEX (MON) - all trading uses MON\nMonitor positions\nStop-loss and take-profit\n\n\n\nDistribute profits\n\nCalculate total profit\nGet MMIND holders via Indexer (Transfer events)\nDistribute proportionally (30% of profit) in MON"
      },
      {
        "title": "Risk Management",
        "body": "Position sizing: Based on confidence score (max 0.1 MON)\nStop-loss: -10% (automatic sell)\nTake-profit: +20% (sell half position)\nSlippage protection: 1-2% tolerance\nMinimum liquidity: 5 MON required"
      },
      {
        "title": "Usage Examples",
        "body": "Start autonomous trading:\n\n# Via OpenClaw chat\n\"Start autonomous trading agent\"\n\n# Or via cron job (runs every minute). Paths: use NADFUN_ENV_PATH / NADFUNAGENT_DATA_DIR for .env and data; run scripts from nadfun-trading skill directory (clawhub install).\nopenclaw cron add \\\n  --name \"Nad.fun Trading Agent\" \\\n  --cron \"* * * * *\" \\\n  --session isolated \\\n  --message \"Run autonomous trading cycle: 1) Load config from .env (path: NADFUN_ENV_PATH or NADFUNAGENT_DATA_DIR/.env; need MMIND_TOKEN_ADDRESS, MONAD_PRIVATE_KEY, MONAD_RPC_URL, MONAD_NETWORK). 2) From nadfun-trading skill directory run: node execute-bonding-v2.js (uses check-pnl.js for P&L from positions_report.json at POSITIONS_REPORT_PATH or NADFUNAGENT_DATA_DIR, auto-sells at +5% or -10%). 3) If there is positive PnL (profit >= 0.1 MON), distribute profits to MMIND token holders: use MMIND_TOKEN_ADDRESS from .env, get holders via indexer/Transfer events, distribute proportionally (e.g. 30%) in MON. Report output in English.\"\n\nCheck agent status:\n\n# Via OpenClaw chat\n\"Show trading agent status and statistics\"\n\nView found tokens: (data dir: NADFUNAGENT_DATA_DIR, default $HOME/nadfunagent)\n\n# View latest found tokens\ncat \"${NADFUNAGENT_DATA_DIR:-$HOME/nadfunagent}/found_tokens.json\" | jq '.[-1]'\n\n# View all tokens from last scan\ncat \"${NADFUNAGENT_DATA_DIR:-$HOME/nadfunagent}/found_tokens.json\" | jq '.[-1].tokens[] | .address'\n\n# View tokens found in multiple methods (higher confidence)\ncat \"${NADFUNAGENT_DATA_DIR:-$HOME/nadfunagent}/found_tokens.json\" | jq '.[-1].tokens[] | select(.foundInMethods > 1) | .address'\n\n# View summary of last 10 scans\ncat \"${NADFUNAGENT_DATA_DIR:-$HOME/nadfunagent}/found_tokens.json\" | jq '.[-10:] | .[] | {timestamp, totalFound}'\n\nManual trade:\n\n# Via OpenClaw chat\n\"Buy 0.1 MON worth of token 0x...\"\n\"Sell all tokens for 0x...\""
      },
      {
        "title": "Integration with OpenClaw",
        "body": "This skill integrates with:\n\nCron Jobs: Schedule autonomous trading cycles\nSkills: Uses nadfun-trading, nadfun-indexer, nadfun-agent-api\nGateway: Runs via OpenClaw Gateway\nChannels: Can send notifications via Telegram/WhatsApp/etc."
      },
      {
        "title": "Error Handling",
        "body": "All errors are logged but don't stop the agent\nFailed trades are retried with exponential backoff\nAPI failures fall back to Indexer queries\nNetwork errors trigger fallback RPC providers"
      },
      {
        "title": "Rate Limit Handling",
        "body": "CRITICAL: Handle rate limits gracefully to avoid HTTP 429 errors."
      },
      {
        "title": "API Rate Limits",
        "body": "Nad.fun Agent API:\n\nWithout API Key: 10 requests/minute (IP-based)\nWith API Key: 100 requests/minute (Key-based)\nSolution: Add delays between requests (min 2 seconds between calls)\n\nNad.fun Public APIs (nad.fun/api/* and api.nadapp.net/*):\n\nRate limits are not publicly documented but appear to be IP-based\nSolution: Add 2-3 second delays between calls, especially for base64-decoded endpoints\nHandle HTTP 429 errors gracefully with exponential backoff\n\nAnthropic/Claude API (CRITICAL - This is the main source of HTTP 429):\n\nRate limits vary by tier (typically 50-200 requests per minute)\nProblem: Each agent execution makes multiple requests to Claude\nSolution:\n\nCron job runs every 10 minutes (*/10 * * * *) to reduce frequency\nOptimize agent logic to minimize token usage\nBatch operations when possible\nIf still hitting limits, increase to 15 minutes (*/15 * * * *)"
      },
      {
        "title": "Rate Limit Strategy",
        "body": "Add delays between API calls (CRITICAL):\n// Wait 2-3 seconds between Nad.fun API calls\nawait new Promise(resolve => setTimeout(resolve, 2000))\n\n// Wait 1 second between Indexer queries\nawait new Promise(resolve => setTimeout(resolve, 1000))\n\n\n\nLimit parallel requests:\n\nProcess tokens sequentially or in small batches (max 5 at a time)\nAdd delays between batches\nDon't make all 7 methods run simultaneously - stagger them\n\n\n\nHandle 429 errors gracefully:\nasync function fetchWithRetry(url, options, maxRetries = 2) {\n  for (let i = 0; i < maxRetries; i++) {\n    try {\n      const result = await fetch(url, options)\n      if (result.status === 429) {\n        const retryAfter = parseInt(result.headers.get('Retry-After') || '60')\n        console.log(`Rate limited, waiting ${retryAfter} seconds...`)\n        await new Promise(resolve => setTimeout(resolve, retryAfter * 1000))\n        continue\n      }\n      return result\n    } catch (err) {\n      if (i === maxRetries - 1) throw err\n      await new Promise(resolve => setTimeout(resolve, 5000 * (i + 1)))\n    }\n  }\n}\n\n\n\nOptimize agent execution:\n\nUse Indexer methods (1-2) first (they're faster and don't hit API limits)\nOnly use API methods (3-7) if Indexer doesn't find enough tokens\nAnalyze ALL tokens that pass filters (no limit)\nSkip detailed analysis if token doesn't meet basic filters\n\n\n\nCron frequency:\n\nCurrent: every 10 minutes (*/10 * * * *)\nIf still hitting Claude API limits, increase to 15 minutes (*/15 * * * *)"
      }
    ],
    "body": "CRITICAL COMMUNICATION RULES:\n\nLanguage: Always respond in the SAME language as the user's question. If in English, respond in English.\nData Loading: BEFORE executing any operations, FIRST request and load ALL required data from user or configuration files. Use OpenClaw's memory/session storage to save loaded data so you don't need to ask again.\nTelegram Integration:\nSend detailed reports to Telegram after each trading cycle\nIf Telegram bot connection is not established, request user to start conversation with bot first\nInclude position status, P&L, new opportunities found, trades executed\nInitial Setup: When first invoked, ask user for:\nMMIND_TOKEN_ADDRESS (or load from .env)\nMONAD_PRIVATE_KEY (or load from .env)\nMONAD_RPC_URL (or load from .env)\nMONAD_NETWORK (or load from .env)\nTelegram user ID for notifications Save all this data in OpenClaw memory for future use.\nMissing variables: If .env is missing or any required variable (MONAD_PRIVATE_KEY, MONAD_RPC_URL, MMIND_TOKEN_ADDRESS, MONAD_NETWORK) is not set, ask the user to provide it before running trading or scripts. Do not proceed with buy/sell or execute-bonding-v2 until config is complete.\nNad.fun Autonomous Trading Agent\n\nAutonomous trading agent that scans Nad.fun markets, analyzes tokens using momentum strategies, executes trades, and distributes profits to MMIND token holders.\n\nPrerequisites\nmonad-development skill installed (for wallet and RPC setup)\nnadfun-trading skill installed (for buy/sell operations), or use the trading/ folder from this repo\nnadfun-indexer skill installed (for querying events)\nnadfun-agent-api skill installed (for market data)\nNetwork configured (mainnet only for this skill)\nMMIND token address configured\n\nPaths (clean install): Config is read from NADFUN_ENV_PATH if set, else $HOME/nadfunagent/.env. Positions report: POSITIONS_REPORT_PATH or $HOME/nadfunagent/positions_report.json. Scripts from nadfun-trading may be at ~/.openclaw/workspace/skills/nadfun-trading or at <this-repo>/trading. See DEPENDENCIES.md.\n\nConfiguration\n\nCRITICAL: Load environment variables from .env file (default path: $HOME/nadfunagent/.env; override with NADFUN_ENV_PATH):\n\nMMIND_TOKEN_ADDRESS: Address of MMIND token for profit distribution (required)\nMONAD_PRIVATE_KEY: Private key for trading wallet (required)\nMONAD_RPC_URL: RPC endpoint URL (required)\nMONAD_NETWORK: Network type - \"mainnet\" or \"testnet\" (required)\nMAX_POSITION_SIZE: Maximum position size in MON (default: 0.1)\nPROFIT_TARGET_PERCENT: Take-profit threshold (default: 20%)\nSTOP_LOSS_PERCENT: Stop-loss threshold (default: 10%)\nPNL_DISTRIBUTION_PERCENT: % of profit to distribute (default: 30%)\n\nBefore starting trading cycle:\n\nRead .env file (path: NADFUN_ENV_PATH or $HOME/nadfunagent/.env). If the file is missing or any required variable is empty, ask the user to provide MMIND_TOKEN_ADDRESS, MONAD_PRIVATE_KEY, MONAD_RPC_URL, MONAD_NETWORK — do not run trading until config is complete.\nLoad MMIND_TOKEN_ADDRESS - use this for profit distribution\nLoad MONAD_PRIVATE_KEY - use this for wallet operations\nLoad MONAD_RPC_URL - use this for blockchain queries\nLoad MONAD_NETWORK - determines API endpoints (mainnet: api.nadapp.net, testnet: dev-api.nad.fun)\nWorkflow\n\nEXECUTION SUMMARY - READ THIS FIRST:\n\nLoad config from $HOME/nadfunagent/.env (MMIND_TOKEN_ADDRESS, MONAD_PRIVATE_KEY, MONAD_RPC_URL, MONAD_NETWORK)\nExecute ONLY 3 methods (from HAR file analysis): Method 5 (New Events API), Method 6 (Market Cap API), Method 7 (Creation Time API)\nCombine results - merge all token addresses from 3 methods, count frequency (how many methods found each token, max 3)\nPrioritize - sort tokens by frequency (tokens found in 2-3 methods = higher priority)\nSave tokens - save ALL found tokens (up to top 50 for storage) to $HOME/nadfunagent/found_tokens.json for manual review\nAnalyze - get token info, market data, metrics via Agent API, calculate scores\nFilter - keep only tokens with min liquidity 5 MON, min holders 5\nTrade - execute trades if score >= 60\nDistribute - distribute profits to MMIND holders if profit >= 0.1 MON\n1. Market Scanning (NEW + TRENDING)\n\nCRITICAL INSTRUCTIONS: You MUST scan tokens using ALL 7 methods below. Execute each method step-by-step and collect ALL found token addresses.\n\nEXECUTION ORDER:\n\nMethod 1: CurveCreate events (Indexer) - NEW tokens\nMethod 2: CurveBuy events (Indexer) - TRENDING by volume\nMethod 3: Swap History (Agent API) - TRENDING by 24h volume\nMethod 4: Holdings (Agent API) - ACTIVE tokens from large traders\nMethod 5: New Events API - REAL-TIME BUY/CREATE events\nMethod 6: Market Cap API - TOP tokens by capitalization\nMethod 7: Creation Time API - NEWEST tokens\n\nSTEP-BY-STEP EXECUTION:\n\nCRITICAL LOGGING REQUIREMENT: After executing EACH method, you MUST log:\n\nPrint: \"✅ Method X executed: Found N tokens\"\nPrint: \"Method X token addresses: <address1> <address2> ...\"\nIf method failed: Print: \"❌ Method X failed: <error reason>\"\nThis helps track which methods are working and debug issues\n\nNOTE: Only Methods 5, 6, and 7 are used (from HAR file analysis). Methods 1-4 are disabled.\n\nSTATUS CHECK: This method requires RPC access. You MUST execute it, don't skip it!\n\nWhat to do:\n\nRead $HOME/nadfunagent/.env to get MONAD_RPC_URL and MONAD_NETWORK\nGet current block number using RPC: eth_blockNumber\nCalculate: safeLatest = currentBlock - 10 (safety margin)\nScan last 900 blocks in chunks of 100 blocks (RPC limit)\nFor each chunk, query CurveCreate events using nadfun-indexer skill\n\nHow to execute:\n\nUse nadfun-indexer skill with parameters: event=CurveCreate, fromBlock=<start>, toBlock=<end>\nExtract token addresses from events: event.args.token or event.args[1] (token is second argument)\nCollect all unique token addresses\n\nExample command structure:\n\nUse skill: nadfun-indexer\nParameters: event=CurveCreate, fromBlock=<calculated_start>, toBlock=<calculated_end>\nExtract: token address from each event\n\n\nExpected result: Array of token addresses (0x...) from newly created tokens\n\nMethod 2: Trending Tokens via Indexer (CurveBuy volume analysis)\n\nSTATUS CHECK: This method requires RPC access. You MUST execute it, don't skip it!\n\nWhat to do:\n\nQuery CurveBuy events from last 900 blocks (same pagination as Method 1)\nFor each event, extract: token address and amountIn (MON spent)\nSum volume per token address\nSort tokens by total volume (descending)\nTake top 20 tokens with highest volume\n\nHow to execute:\n\nUse same RPC setup as Method 1: Get publicClient from monad-development skill\nQuery CurveBuy events: For each 100-block chunk:\nUse nadfun-indexer skill: event=CurveBuy, fromBlock=<start>, toBlock=<end>\nOR use viem: publicClient.getContractEvents({ address: CURVE_ADDRESS, eventName: \"CurveBuy\", fromBlock, toBlock })\nExtract data: From each event:\ntoken = event.args.token or event.args[1]\namountIn = event.args.amountIn or event.args[2] (amount in MON)\nCalculate volume: Group by token address, sum all amountIn values\nSort and filter: Sort by total volume DESC, take top 20\nLog results: Print \"✅ Method 2: Found top 20 trending tokens by volume\"\n\nExample:\n\n1. Use same RPC client from Method 1\n2. Query CurveBuy events in chunks of 100 blocks\n3. For each event: extract token and amountIn\n4. Sum volume per token\n5. Sort DESC, take top 20\n6. Print: \"✅ Method 2: Found 20 trending tokens\"\n\n\nExpected result: Top 20 token addresses sorted by trading volume\n\nMethod 3: Trending Tokens via Agent API (Swap History Analysis)\n\nWhat to do:\n\nTake top 10 tokens from Method 2 (highest volume)\nFor each token, query swap history via Agent API\nCalculate 24h volume from swap history\nKeep tokens with >= 1 MON volume in last 24 hours\n\nHow to execute:\n\nDetermine API URL: if MONAD_NETWORK=mainnet then https://api.nadapp.net, else https://dev-api.nad.fun\nFor each token, make GET request: ${API_URL}/agent/swap-history/${tokenAddress}?limit=50&trade_type=ALL\nParse response JSON\nFor each swap in response.swaps:\nCheck swap.swap_info.timestamp - if within last 24 hours\nSum swap.swap_info.native_amount (in wei, convert to MON: divide by 1e18)\nKeep tokens with total 24h volume >= 1 MON\nAdd 1-2 second delay between API calls to avoid rate limits\n\nExample:\n\nAPI_URL = MONAD_NETWORK === 'mainnet' ? 'https://api.nadapp.net' : 'https://dev-api.nad.fun'\nFor each token in top10:\n  GET ${API_URL}/agent/swap-history/${token}?limit=50&trade_type=ALL\n  Calculate 24h volume\n  If volume >= 1 MON: add to trendingViaAPI\n  Wait 1-2 seconds\n\n\nExpected result: List of tokens with high 24h trading volume\n\nMethod 4: Get All Active Tokens (via Holdings of Large Accounts)\n\nSTATUS CHECK: Don't skip this method! Execute it with rate limit delays.\n\nWhat to do:\n\nFrom Method 2 CurveBuy events, find buyers who spent >1 MON\nExtract their addresses (sender field)\nQuery holdings for first 5 large buyers (to avoid rate limits)\nExtract all token addresses they hold (balance > 0)\n\nHow to execute:\n\nFrom Method 2 results: Use CurveBuy events already queried\nFilter large buyers:\nFilter events where amountIn > 1000000000000000000 (1 MON in wei = 1e18)\nExtract sender address: event.args.sender or event.args[0]\nGet unique addresses, take first 5\nQuery holdings: For each of 5 addresses:\nGET: ${API_URL}/agent/holdings/${address}?limit=50\nHeaders: Accept: application/json\nWait 2 seconds between requests\nParse and extract:\nParse JSON response\nFor each holding in response.tokens:\nCheck holding.balance_info.balance - if > 0\nExtract holding.token_info.address or holding.token_info.token_id\nLog results: Print \"✅ Method 4: Found N active tokens from large trader holdings\"\n\nExample:\n\nFrom Method 2 CurveBuy events:\n  Filter: amountIn > 1e18 (1 MON)\n  Extract: sender addresses\n  Take first 5 unique addresses\nFor each address:\n  GET ${API_URL}/agent/holdings/${address}?limit=50\n  Extract tokens with balance > 0\n  Wait 2 seconds\nPrint: \"✅ Method 4: Found X tokens\"\n\n\nExpected result: Set of token addresses held by active large traders\n\nMethod 5: New Events API (Real-time BUY/SELL/CREATE events)\n\nSTATUS: ✅ This method is WORKING! Found 7 tokens in last scan.\n\nWhat to do:\n\nMake GET request to /api/token/new-event endpoint\nParse JSON response (array of events)\nExtract token addresses from CREATE and BUY events\nAdd all unique token addresses to collection\n\nHow to execute:\n\nDetermine base URL: Read MONAD_NETWORK from .env:\nIf MONAD_NETWORK=mainnet: baseUrl = 'https://nad.fun'\nElse: baseUrl = 'https://dev.nad.fun'\nMake GET request:\nURL: ${baseUrl}/api/token/new-event\nHeaders: Accept: application/json, User-Agent: OpenClaw-Agent/1.0\nParse response: JSON array of event objects\nExtract tokens:\nFor each event in response:\nIf event.type === 'CREATE': extract event.token_info.token_id\nIf event.type === 'BUY': extract event.token_info.token_id\nIf event.type === 'SELL': optionally extract (indicates active trading)\nLog results: Print \"✅ Method 5: Found N tokens from new events API\"\n\nExample:\n\nbaseUrl = MONAD_NETWORK === 'mainnet' ? 'https://nad.fun' : 'https://dev.nad.fun'\nGET ${baseUrl}/api/token/new-event\nParse JSON response\nFor each event:\n  If event.type === 'CREATE' or 'BUY':\n    Add event.token_info.token_id to tokensFromEvents\nPrint: \"✅ Method 5: Found X tokens\"\n\n\nExpected result: Array of token addresses from recent CREATE and BUY events\n\nResponse structure:\n\ntype: \"BUY\" | \"SELL\" | \"CREATE\"\namount: Event amount (string)\ntoken_info: Complete token information (token_id, name, symbol, description, creator, etc.)\naccount_info: Account that performed the action\nMethod 6: Market Cap API (Top tokens by market cap)\n\nSTATUS CHECK: Previous error: base64 decoding failed. Fix: Use proper decoding method.\n\nWhat to do:\n\nGET request to market cap endpoint\nResponse is base64-encoded JSON - decode it PROPERLY\nExtract token addresses from response\nAdd all token addresses to collection\n\nHow to execute:\n\nAPI URL: ${API_URL}/order/market_cap?page=1&limit=50&is_nsfw=false\nWhere API_URL = MONAD_NETWORK === 'mainnet' ? 'https://api.nadapp.net' : 'https://dev-api.nad.fun'\nGET request:\nHeaders: Accept: application/json, User-Agent: OpenClaw-Agent/1.0\nGet response as text (not JSON)\nDecode base64 PROPERLY:\nResponse body is base64 string\nUse Python: import base64; decoded = base64.b64decode(response_text).decode('utf-8')\nOR use command: echo \"$response_text\" | base64 -d\nCRITICAL: Make sure response_text is the raw base64 string, not wrapped in JSON\nParse decoded JSON:\nParse decoded string as JSON\nStructure: { \"tokens\": [...], \"total_count\": N }\nExtract tokens WITH FULL DATA:\nFor each token in data.tokens:\nExtract token.token_info.token_id (token address)\nCRITICAL: Store FULL token object with both token_info AND market_info\nStructure: {token_info: {...}, market_info: {...}, percent: ...}\nAdd to topMarketCapTokens array (store full objects, not just addresses)\nLog results: Print \"✅ Method 6: Found N tokens with market data from market cap API\"\n\nExample:\n\nAPI_URL = MONAD_NETWORK === 'mainnet' ? 'https://api.nadapp.net' : 'https://dev-api.nad.fun'\nresponse = GET ${API_URL}/order/market_cap?page=1&limit=100&is_nsfw=false\ndecoded = base64.b64decode(response.text).decode('utf-8')\ndata = json.loads(decoded)\n\ntopMarketCapTokens = []  # Array of full token objects\nFor each token in data.tokens:\n  # Store FULL token object with token_info + market_info\n  topMarketCapTokens.append({\n    'token_info': token.token_info,\n    'market_info': token.market_info,\n    'percent': token.percent,\n    'address': token.token_info.token_id  # For easy access\n  })\nPrint: \"✅ Method 6: Found X tokens with market data\"\n\n\nExpected result: Array of full token objects (with token_info + market_info) sorted by market cap\n\nResponse structure:\n\ntokens: Array of token objects with token_info and market_info\ntotal_count: Total number of tokens\nEach token includes: token_info (token_id, name, symbol, creator, etc.) and market_info (market_type, price, volume, holder_count, etc.)\nMethod 7: Creation Time API (Newest tokens)\n\nCRITICAL: Include BOTH bonding curve AND DEX tokens! Do NOT filter by is_graduated.\n\nWhat to do:\n\nGET request to creation time endpoint (newest first)\nDecode base64 response PROPERLY (same as Method 6)\nExtract token addresses - include ALL tokens (both bonding curve AND DEX)\nAdd to collection WITH FULL DATA\n\nHow to execute:\n\nAPI URL: ${API_URL}/order/creation_time?page=1&limit=50&is_nsfw=false&direction=DESC\nGET request, decode base64 response (same as Method 6)\nParse JSON: data.tokens array\nFor each token:\nExtract token.token_info.token_id (token address)\nCRITICAL: Store FULL token object with both token_info AND market_info\nDO NOT filter by is_graduated - include both bonding curve (is_graduated=false) AND DEX (is_graduated=true) tokens\nAdd to newestTokens array (store full objects, not just addresses)\n\nExample:\n\nAPI_URL = MONAD_NETWORK === 'mainnet' ? 'https://api.nadapp.net' : 'https://dev-api.nad.fun'\nresponse = GET ${API_URL}/order/creation_time?page=1&limit=50&is_nsfw=false&direction=DESC\ndecoded = base64.b64decode(response.text).decode('utf-8')\ndata = json.loads(decoded)\n\nnewestTokens = []  # Array of full token objects\nFor each token in data.tokens:\n  # Include ALL tokens - both bonding curve AND DEX\n  # Store FULL token object with token_info + market_info\n  newestTokens.append({\n    'token_info': token.token_info,\n    'market_info': token.market_info,\n    'percent': token.percent,\n    'address': token.token_info.token_id  # For easy access\n  })\nPrint: \"✅ Method 7: Found X tokens with market data (bonding curve + DEX)\"\n\n\nExpected result: Array of full token objects (with token_info + market_info) - newest tokens including both bonding curve AND DEX\n\nResponse structure:\n\nSame as Method 6: tokens array with token_info and market_info\ndirection=DESC returns newest first\nCRITICAL: Include ALL tokens (both bonding curve AND DEX) - do NOT filter by is_graduated\nCombine All Methods - CRITICAL STEP\n\nWhat to do:\n\nCollect ALL token addresses from Methods 5, 6, and 7 into one set (removes duplicates)\nCount how many times each token appears across methods\nSort tokens by frequency (tokens found in more methods = higher priority/confidence)\nLog summary of combination results\n\nHow to execute:\n\nCreate collections:\n\nallTokens = new Set() or allTokens = [] (use Set to avoid duplicates)\ntokenFrequency = new Map() or {} to count occurrences\n\nAdd tokens from each method:\n\nMethod 5: Add all token addresses from tokensFromEvents (New Events API) - these are just addresses\nMethod 6: Add all tokens from topMarketCapTokens (Market Cap API) - these are FULL objects with token_info + market_info\nMethod 7: Add all tokens from newestTokens (Creation Time API) - these are FULL objects with token_info + market_info\n\nIMPORTANT: For Methods 6 & 7, store the FULL token objects (not just addresses) so you can use market_info directly for analysis!\n\nCount frequency and preserve full data:\n\nUse a Map/object: allTokensMap = {} where key = token address, value = token object with metadata\nFor Method 5 (addresses only): allTokensMap[address] = {address, source: 'method5', data: null}\nFor Method 6 (full objects): allTokensMap[address] = {address, source: 'method6', data: fullTokenObject}\nFor Method 7 (full objects): allTokensMap[address] = {address, source: 'method7', data: fullTokenObject}\nCount frequency: tokenFrequency[address] = (tokenFrequency[address] || 0) + 1\nCRITICAL: Preserve full token objects from Methods 6 & 7 - they contain market_info!\n\nConvert and sort:\n\ncandidateTokens = Object.values(allTokensMap) or Array.from(allTokensMap.values())\nSort by frequency DESC, then by data availability:\ncandidateTokens.sort((a, b) => {\n  const freqA = tokenFrequency[a.address] || 0\n  const freqB = tokenFrequency[b.address] || 0\n  if (freqB !== freqA) return freqB - freqA  // Higher frequency first\n  // Prefer tokens with full data (market_info)\n  const hasDataA = a.data && a.data.market_info ? 1 : 0\n  const hasDataB = b.data && b.data.market_info ? 1 : 0\n  return hasDataB - hasDataA\n})\n\nprioritizedTokens = candidateTokens\n\nLog summary:\n\nPrint: \"📊 Combined results: Total unique tokens: N\"\nPrint: \"Tokens found in 2+ methods: X\"\nPrint: \"Tokens found in all 3 methods: Y\"\nPrint: \"Tokens with full market data: Z\"\nPrint: \"Top 10 prioritized tokens: <list with addresses>\"\n\nExample:\n\nallTokensMap = {}  # Map: address -> {address, source, data}\ntokenFrequency = {}\n\n# Method 5: addresses only\nfor address in tokensFromEvents:\n  allTokensMap[address] = {address: address, source: 'method5', data: null}\n  tokenFrequency[address] = (tokenFrequency[address] || 0) + 1\n\n# Method 6: full objects with market_info\nfor token in topMarketCapTokens:\n  address = token.token_info.token_id\n  allTokensMap[address] = {address: address, source: 'method6', data: token}\n  tokenFrequency[address] = (tokenFrequency[address] || 0) + 1\n\n# Method 7: full objects with market_info\nfor token in newestTokens:\n  address = token.token_info.token_id\n  allTokensMap[address] = {address: address, source: 'method7', data: token}\n  tokenFrequency[address] = (tokenFrequency[address] || 0) + 1\n\n# Sort by frequency and data availability\ncandidateTokens = Object.values(allTokensMap)\nprioritizedTokens = candidateTokens.sort((a, b) => {\n  freqDiff = tokenFrequency[b.address] - tokenFrequency[a.address]\n  if (freqDiff !== 0) return freqDiff\n  return (b.data && b.data.market_info ? 1 : 0) - (a.data && a.data.market_info ? 1 : 0)\n})\n\nPrint: \"📊 Combined: N unique tokens, X found in 2+ methods, Y found in all 3 methods, Z with full data\"\n\n\nExpected result: Prioritized array of token objects, each containing:\n\naddress: token address\nsource: which method(s) found it\ndata: full token object with token_info + market_info (if available from Methods 6 or 7)\nfoundInMethods: count from tokenFrequency\nSave Found Tokens - CRITICAL STEP\n\nWhat to do: After combining all methods and getting prioritizedTokens, you MUST save them to file $HOME/nadfunagent/found_tokens.json for manual review.\n\nHow to execute:\n\nPrepare token list:\n\nTake top 50 tokens from prioritizedTokens\nFor each token, create object: {address: token, foundInMethods: frequency_count}\nCreate scan entry with timestamp, totalFound count, and tokens array\n\nRead existing file:\n\nFile: $HOME/nadfunagent/found_tokens.json\nIf file doesn't exist or is empty: start with []\nIf exists: parse JSON, get array\n\nAppend and save:\n\nAdd new scan entry to array\nKeep only last 100 scans (remove oldest if > 100)\nWrite back as formatted JSON\n\nExample using Node.js script (RECOMMENDED):\n\n# From skill root or repo root: print token addresses (one per line) and pipe to script\n# prioritizedTokens is the array of token addresses; take first 50 and pass to script\necho -e \"0x...\\n0x...\" | node scripts/save_tokens.js\n\n\nOr from the agent: write the list of addresses to a temp file or stdin and run node scripts/save_tokens.js (script reads from stdin). Data dir: NADFUNAGENT_DATA_DIR or $HOME/nadfunagent.\n\nExpected result: File $HOME/nadfunagent/found_tokens.json updated with latest scan results\n\n2. Token Analysis - CRITICAL: Use Data from Methods 6 & 7 Directly\n\nIMPORTANT: Methods 6 (Market Cap API) and Method 7 (Creation Time API) already return COMPLETE token data with market_info. Use this data directly for analysis - DO NOT make additional API calls unless absolutely necessary!\n\nData Structure from Methods 6 & 7: Each token in the response has this structure:\n\n{\n  \"token_info\": {\n    \"token_id\": \"0x...\",\n    \"name\": \"...\",\n    \"symbol\": \"...\",\n    \"is_graduated\": true/false,\n    \"created_at\": timestamp,\n    ...\n  },\n  \"market_info\": {\n    \"market_type\": \"DEX\" or \"BONDING_CURVE\",\n    \"reserve_native\": \"1625513352353765005672133\",  // Liquidity in MON (wei format)\n    \"reserve_token\": \"51582894169959918089536846\",\n    \"token_price\": \"0.000886889894056452\",\n    \"price\": \"0.0415501242\",\n    \"price_usd\": \"0.000886889894056452\",\n    \"volume\": \"761848094037806233587694495\",  // Total volume (wei format)\n    \"holder_count\": 31580,  // Number of holders\n    \"ath_price\": \"0.0128807779\",\n    \"ath_price_usd\": \"0.0128807779\",\n    ...\n  },\n  \"percent\": 8.797  // Market cap change percentage\n}\n\n\nAnalysis Steps:\n\nExtract data from Methods 6 & 7:\n\nFor each token in data.tokens array from Methods 6 & 7:\nExtract token_info.token_id (token address)\nExtract market_info.reserve_native (liquidity in wei, convert to MON: divide by 1e18)\nExtract market_info.holder_count (number of holders)\nExtract market_info.volume (total volume in wei, convert to MON: divide by 1e18)\nExtract market_info.market_type (\"DEX\" or \"BONDING_CURVE\")\nExtract token_info.is_graduated (true if graduated to DEX)\nExtract percent (market cap change percentage)\n\nFor Method 5 tokens (new-event API):\n\nThis API returns only basic event data without full market_info\nFor tokens from Method 5, you can optionally query /agent/market/:token_id to get market_info\nBUT prioritize tokens from Methods 6 & 7 first (they already have complete data)\n\nCalculate Liquidity Score (30% weight):\n\nliquidityMON = parseFloat(market_info.reserve_native) / 1e18\n\n// Score based on liquidity tiers\nif (liquidityMON >= 1000) liquidityScore = 100      // Excellent\nelse if (liquidityMON >= 500) liquidityScore = 80     // Very good\nelse if (liquidityMON >= 100) liquidityScore = 60    // Good\nelse if (liquidityMON >= 50) liquidityScore = 40      // Fair\nelse if (liquidityMON >= 10) liquidityScore = 20      // Low\nelse liquidityScore = 0                              // Too low\n\nweightedLiquidity = liquidityScore * 0.30\n\n\nCalculate Momentum Score (25% weight):\n\n// Use percent (market cap change) as momentum indicator\npercentChange = parseFloat(token.percent) || 0\n\n// Score based on positive momentum\nif (percentChange >= 50) momentumScore = 100      // Strong growth\nelse if (percentChange >= 20) momentumScore = 80\nelse if (percentChange >= 10) momentumScore = 60\nelse if (percentChange >= 5) momentumScore = 40\nelse if (percentChange >= 0) momentumScore = 20\nelse momentumScore = 0                           // Negative momentum\n\nweightedMomentum = momentumScore * 0.25\n\n\nCalculate Volume Score (20% weight):\n\nvolumeMON = parseFloat(market_info.volume) / 1e18\n\n// Score based on volume tiers\nif (volumeMON >= 1000000) volumeScore = 100      // 1M+ MON volume\nelse if (volumeMON >= 500000) volumeScore = 80\nelse if (volumeMON >= 100000) volumeScore = 60\nelse if (volumeMON >= 50000) volumeScore = 40\nelse if (volumeMON >= 10000) volumeScore = 20\nelse volumeScore = 0\n\nweightedVolume = volumeScore * 0.20\n\n\nCalculate Holder Score (15% weight):\n\nholderCount = parseInt(market_info.holder_count) || 0\n\n// Score based on holder count\nif (holderCount >= 10000) holderScore = 100      // Excellent distribution\nelse if (holderCount >= 5000) holderScore = 80\nelse if (holderCount >= 1000) holderScore = 60\nelse if (holderCount >= 500) holderScore = 40\nelse if (holderCount >= 100) holderScore = 20\nelse holderScore = 0\n\nweightedHolders = holderScore * 0.15\n\n\nCalculate Progress Score (10% weight):\n\nisGraduated = token_info.is_graduated === true\nmarketType = market_info.market_type\n\n// Score based on market stage\nif (isGraduated || marketType === \"DEX\") {\n  progressScore = 100  // Fully graduated to DEX\n} else {\n  // Still on bonding curve - use percent as progress indicator\n  // Higher percent = closer to graduation\n  percent = parseFloat(token.percent) || 0\n  if (percent >= 80) progressScore = 80\n  else if (percent >= 50) progressScore = 60\n  else if (percent >= 30) progressScore = 40\n  else progressScore = 20\n}\n\nweightedProgress = progressScore * 0.10\n\n\nCalculate Authority Score (Bonus - up to +10 points):\n\n// Check for social media presence and website (indicates legitimacy)\nhasTwitter = token_info.twitter && token_info.twitter.length > 0\nhasTelegram = token_info.telegram && token_info.telegram.length > 0\nhasWebsite = token_info.website && token_info.website.length > 0\n\nauthorityScore = 0\nif (hasTwitter) authorityScore += 3\nif (hasTelegram) authorityScore += 3\nif (hasWebsite) authorityScore += 4\n\n// Maximum +10 bonus points for full social presence\nauthorityBonus = Math.min(authorityScore, 10)\n\n\nCalculate Total Score:\n\ntotalScore = weightedLiquidity + weightedMomentum + weightedVolume + weightedHolders + weightedProgress + authorityBonus\n\n// Round to 2 decimal places\ntotalScore = Math.round(totalScore * 100) / 100\n\n\nStore Analysis Results: For each token, store:\n\n{\n  address: token_info.token_id,\n  name: token_info.name,\n  symbol: token_info.symbol,\n  liquidity: liquidityMON,\n  holders: holderCount,\n  volume: volumeMON,\n  marketType: marketType,\n  isGraduated: isGraduated,\n  percentChange: percentChange,\n  scores: {\n    liquidity: liquidityScore,\n    momentum: momentumScore,\n    volume: volumeScore,\n    holders: holderScore,\n    progress: progressScore,\n    total: totalScore\n  },\n  foundInMethods: tokenFrequency[token_info.token_id] || 1\n}\n\n\nCRITICAL INSTRUCTIONS:\n\nUse data from Methods 6 & 7 DIRECTLY - they already contain all needed information\nDO NOT make additional API calls to /agent/market/:token_id for tokens from Methods 6 & 7\nOnly for Method 5 tokens (if needed) make additional API calls\nCalculate scores immediately after combining methods, before filtering\nCheck authority (social media presence): Check token_info.twitter, token_info.telegram, token_info.website - add bonus points\nLog analysis results: Print \"📊 Analysis: Token X has score Y (liquidity: A, momentum: B, volume: C, holders: D, progress: E, authority: +F)\"\n3. Filtering Criteria\n\nFilter tokens based on:\n\nMinimum liquidity: 5 MON\nMinimum holders: 5\nBonding curve progress: >= 10%\nScore calculation:\nLiquidity: 30% weight\nMomentum: 25% weight\nVolume trend: 20% weight\nHolder distribution: 15% weight\nBonding curve progress: 10% weight\n4. Position Management - CRITICAL: Check Existing Positions FIRST\n\nBEFORE analyzing new tokens, you MUST check and manage existing positions!\n\nStep 1: Get Current Holdings\n\nWhat to do:\n\nGet trading wallet address from MONAD_PRIVATE_KEY in .env (derive address from private key)\nQuery current token holdings using Agent API: /agent/holdings/${walletAddress}?limit=100\nFilter tokens where balance > 0 (exclude zero balances)\nFor each token with balance > 0, get current market data\n\nHow to execute:\n\n// 1. Get wallet address from private key\nconst walletAddress = getAddressFromPrivateKey(MONAD_PRIVATE_KEY)\n\n// 2. Query holdings\nconst holdingsResponse = await fetch(`${API_URL}/agent/holdings/${walletAddress}?limit=100`)\nconst holdings = await holdingsResponse.json()\n\n// 3. Filter tokens with balance > 0\nconst activePositions = holdings.tokens.filter(token => {\n  const balance = parseFloat(token.balance_info.balance) || 0\n  return balance > 0\n})\n\nPrint: \"📊 Current positions: Found N tokens with balance > 0\"\n\nStep 2: Calculate P&L for Each Position\n\nCRITICAL: Always use the check-pnl.js script from nadfun-trading skill for proper P&L calculation. This script:\n\nReads entry price (entryValueMON) from $HOME/nadfunagent/positions_report.json (automatically recorded by buy-token.js when you purchase)\nGets current value on-chain via nad.fun quote contract (or falls back to Agent API)\nCalculates P&L: (currentValueMON - entryValueMON) / entryValueMON * 100\n\nWhat to do:\n\nUse the script: Run check-pnl.js from nadfun-trading skill directory\nThe script reads entry prices from JSON (set by buy-token.js after purchases)\nGets current value on-chain via nad.fun quote contract\nCalculates real P&L based on actual entry price\n\nHow to execute:\n\n# Check P&L for all positions\ncd $HOME/.openclaw/workspace/skills/nadfun-trading\nnode check-pnl.js\n\n# Or with auto-sell (sells if P&L >= +5% or <= -10%)\nnode check-pnl.js --auto-sell\n\n\nManual calculation (if script unavailable):\n\n// Load entry prices from positions_report.json\nconst report = JSON.parse(await fs.readFile('$HOME/nadfunagent/positions_report.json', 'utf-8'))\n\nfor (const position of activePositions) {\n  const tokenAddress = position.token_info.token_id || position.token_info.address\n  const tokenBalance = parseFloat(position.balance_info.balance) || 0\n  \n  // Get entry price from JSON (recorded by buy-token.js)\n  const prev = report.positions?.find(p => \n    (p.address || '').toLowerCase() === tokenAddress.toLowerCase()\n  )\n  const entryValueMON = prev?.entryValueMON || 0\n  \n  // Get current value on-chain via nad.fun quote contract\n  const [router, amountOutWei] = await publicClient.readContract({\n    address: '0x7e78A8DE94f21804F7a17F4E8BF9EC2c872187ea', // nad.fun quote contract\n    abi: lensAbi,\n    functionName: 'getAmountOut',\n    args: [tokenAddress, parseEther(tokenBalance.toString()), false] // false = selling\n  })\n  const currentValueMON = Number(amountOutWei) / 1e18\n  \n  // Calculate P&L\n  const pnlPercent = entryValueMON > 0\n    ? ((currentValueMON - entryValueMON) / entryValueMON) * 100\n    : 0\n  \n  positions.push({\n    address: tokenAddress,\n    balance: tokenBalance,\n    entryValueMON: entryValueMON,\n    currentValueMON: currentValueMON,\n    pnlPercent: pnlPercent\n  })\n}\n\nPrint: \"💰 Position P&L calculated for N positions (source: on-chain nad.fun quote + positions_report.json)\"\n\n\nEntry Price Tracking:\n\nEntry price is automatically recorded by buy-token.js after successful purchase\nStored in $HOME/nadfunagent/positions_report.json as entryValueMON\nIf entry price not found, check-pnl.js uses current value as fallback (P&L = 0%)\nStep 3: Make Sell Decisions\n\nWhat to do: For each position, check sell conditions:\n\nStop-Loss Check:\n\nif (pnlPercent <= -10) {  // STOP_LOSS_PERCENT = -10%\n  // SELL ALL - stop loss triggered\n  sellDecision = {\n    action: 'SELL_ALL',\n    reason: 'Stop-loss triggered',\n    tokenAddress: position.address,\n    amount: 'all'\n  }\n}\n\n\nTake-Profit Check:\n\nif (pnlPercent >= 20) {  // PROFIT_TARGET_PERCENT = 20%\n  // SELL HALF - take profit\n  sellDecision = {\n    action: 'SELL_HALF',\n    reason: 'Take-profit triggered',\n    tokenAddress: position.address,\n    amount: position.balance / 2\n  }\n}\n\n\nTrailing Stop (Optional):\n\n// If profit was > 15% but dropped to < 5%, sell to protect gains\nif (previousPnL > 15 && pnlPercent < 5) {\n  sellDecision = {\n    action: 'SELL_ALL',\n    reason: 'Trailing stop - protecting gains',\n    tokenAddress: position.address,\n    amount: 'all'\n  }\n}\n\n\nHow to execute:\n\nconst sellDecisions = []\n\nfor (const position of positions) {\n  // Stop-loss: sell all if down 10%+\n  if (position.pnlPercent <= -10) {\n    sellDecisions.push({\n      tokenAddress: position.address,\n      action: 'SELL_ALL',\n      reason: `Stop-loss: ${position.pnlPercent.toFixed(2)}%`,\n      amount: 'all'\n    })\n  }\n  // Take-profit: sell half if up 20%+\n  else if (position.pnlPercent >= 20) {\n    sellDecisions.push({\n      tokenAddress: position.address,\n      action: 'SELL_HALF',\n      reason: `Take-profit: ${position.pnlPercent.toFixed(2)}%`,\n      amount: position.balance / 2\n    })\n  }\n}\n\nPrint: \"🔔 Sell decisions: N positions need action\"\nfor (const decision of sellDecisions) {\n  Print: `   ${decision.action}: ${decision.tokenAddress} - ${decision.reason}`\n}\n\nStep 4: Execute Sell Orders\n\nWhat to do: For each sell decision:\n\nUse nadfun-trading skill with action sell\nParameters: token address, amount (from decision)\nThe skill handles: balance check, quote, approve, execution\n\nHow to execute:\n\nfor (const decision of sellDecisions) {\n  try {\n    // Use nadfun-trading skill\n    await useSkill('nadfun-trading', {\n      action: 'sell',\n      token: decision.tokenAddress,\n      amount: decision.amount  // 'all' or specific amount\n    })\n    \n    Print: `✅ Sold ${decision.amount} of ${decision.tokenAddress}: ${decision.reason}`\n  } catch (error) {\n    Print: `❌ Failed to sell ${decision.tokenAddress}: ${error.message}`\n  }\n}\n\n5. Trading Execution - Buy New Tokens\n\nCRITICAL: Only buy new tokens AFTER managing existing positions!\n\nBuy Decision Logic:\n\nPrioritize tokens with authority (social media presence):\n\nTokens with Twitter + Telegram + Website get priority\nThese are more legitimate and have better community support\n\nConsider both early-stage AND established tokens:\n\nEarly-stage (5-50 MON liquidity): Higher risk, higher potential\nEstablished (50k+ MON liquidity): Lower risk, steady growth\nBoth can be profitable if they meet score criteria\n\nBuy Criteria:\n\nScore >= 60 (or >= 55 if has social media)\nLiquidity >= 5 MON\nHolders >= 5\nNOT already in portfolio (check existing positions first)\n\nPosition Sizing:\n\nFor tokens with authority (social media): up to 0.15 MON\nFor tokens without authority: up to 0.1 MON\nMaximum total position per token: 0.15 MON\n\nCRITICAL: Always use the buy-token.js script from nadfun-trading skill. This script:\n\nAutomatically detects bonding curve vs DEX via nad.fun quote contract\nHandles DEX: wraps MON→WMON, approves, swaps\nAutomatically records entry price in $HOME/nadfunagent/positions_report.json after successful purchase\nWorks on bonding curve (MON) or DEX (MON) - all trading uses MON\n\nBuy tokens:\n\ncd $HOME/.openclaw/workspace/skills/nadfun-trading\nNAD_PRIVATE_KEY=$MONAD_PRIVATE_KEY node buy-token.js <token-address> <MON-amount> [--slippage=300]\n\n\nExample:\n\nNAD_PRIVATE_KEY=0x... node buy-token.js 0x123...abc 0.15 --slippage=300\n\n\nAfter purchase:\n\nEntry price (entryValueMON) is automatically recorded in $HOME/nadfunagent/positions_report.json\nThis entry price is used by check-pnl.js for P&L calculation\nNo manual tracking needed - everything is automated\n\nRisk management:\n\nCRITICAL: Buy tokens on BOTH bonding curve AND DEX (don't filter by market_type)\nThe nadfun-trading skill automatically detects market type and uses correct contract\nFor all trades, use MON balance (no wrapping needed - all trading uses MON)\nSet slippage tolerance: 2-3% (increased for better execution, especially on DEX)\nSet deadline: 5 minutes from now\nDon't exceed MAX_POSITION_SIZE per token\n6. Profit Distribution\n\nCRITICAL: Use MMIND_TOKEN_ADDRESS from $HOME/nadfunagent/.env file.\n\nWhen profit >= 0.1 MON:\n\n// Step 1: Load MMIND_TOKEN_ADDRESS from .env file\nconst envFile = await readFile('$HOME/nadfunagent/.env', 'utf-8')\nconst mmindMatch = envFile.match(/MMIND_TOKEN_ADDRESS=(0x[a-fA-F0-9]+)/)\nif (!mmindMatch) {\n  throw new Error('MMIND_TOKEN_ADDRESS not found in .env file')\n}\nconst MMIND_TOKEN_ADDRESS = mmindMatch[1]\n\n// Step 2: Get MMIND token holders via Indexer\n// Query Transfer events to find all addresses that ever held MMIND\nconst latestBlock = await publicClient.getBlockNumber()\nconst safeLatest = latestBlock - 10n\n\n// Query in chunks (RPC limit: ~100 blocks)\nconst transfers = []\nfor (let from = 0n; from < safeLatest; from += 10000n) {\n  const to = from + 10000n > safeLatest ? safeLatest : from + 10000n\n  try {\n    const events = await useSkill('nadfun-indexer', {\n      event: 'Transfer',\n      token: MMIND_TOKEN_ADDRESS,\n      fromBlock: from,\n      toBlock: to\n    })\n    transfers.push(...events)\n  } catch (err) {\n    // Continue if chunk fails\n  }\n}\n\n// Step 3: Collect unique addresses\nconst holderAddresses = new Set()\ntransfers.forEach(event => {\n  if ('args' in event) {\n    if (event.args.from && event.args.from !== '0x0000000000000000000000000000000000000000') {\n      holderAddresses.add(event.args.from)\n    }\n    if (event.args.to && event.args.to !== '0x0000000000000000000000000000000000000000') {\n      holderAddresses.add(event.args.to)\n    }\n  }\n})\n\n// Step 4: Get current balances for each holder\nconst distributions = []\nfor (const address of holderAddresses) {\n  try {\n    const balance = await publicClient.readContract({\n      address: MMIND_TOKEN_ADDRESS,\n      abi: erc20Abi,\n      functionName: 'balanceOf',\n      args: [address]\n    })\n    \n    if (balance > 0n) {\n      distributions.push({ address, balance })\n    }\n  } catch (err) {\n    // Skip if balance check fails\n  }\n}\n\n// Step 5: Get total supply\nconst totalSupply = await publicClient.readContract({\n  address: MMIND_TOKEN_ADDRESS,\n  abi: erc20Abi,\n  functionName: 'totalSupply'\n})\n\n// Step 6: Calculate and distribute profit proportionally\nconst profitToDistribute = (profit * BigInt(PNL_DISTRIBUTION_PERCENT)) / 100n\n\nfor (const holder of distributions) {\n  const share = (profitToDistribute * holder.balance) / totalSupply\n  if (share >= parseEther('0.001')) { // Minimum 0.001 MON\n    await transferMON(holder.address, share) // Transfer MON directly (all trading uses MON)\n  }\n}\n\nAutonomous Trading Loop\n\nThe agent runs continuously:\n\nPosition Management FIRST (CRITICAL - do this before scanning)\n\nGet wallet address from MONAD_PRIVATE_KEY\nQuery current holdings: /agent/holdings/${walletAddress}?limit=100\nFilter tokens with balance > 0\nFor each position: use check-pnl.js to get real P&L (reads entry price from $HOME/nadfunagent/positions_report.json, gets current value on-chain via nad.fun quote contract)\nExecute sell orders: use sell-token.js or check-pnl.js --auto-sell for stop-loss (P&L <= -10%) or take-profit (P&L >= +5%)\nLog: \"📊 Positions: N checked, X sold; entry prices tracked in positions_report.json\"\n\nScan market (after managing positions, every 10 minutes to avoid rate limits)\n\nMethod 5: Fetch new events API (/api/token/new-event) for real-time BUY/CREATE events\nMethod 6: Fetch top tokens by market cap (api.nadapp.net/order/market_cap, base64 decoded)\nMethod 7: Fetch newest tokens (api.nadapp.net/order/creation_time, base64 decoded)\nCombine all methods and prioritize tokens found in multiple sources\nAdd 2-3 second delays between API calls to respect rate limits (CRITICAL to avoid HTTP 429)\n\nAnalyze opportunities\n\nUse data DIRECTLY from Methods 6 & 7 (already contains complete market_info)\nCalculate scores using market_info: liquidity (30%), momentum (25%), volume (20%), holders (15%), progress (10%)\nConvert wei values to MON: reserve_native / 1e18, volume / 1e18\nFilter by criteria (min liquidity 5 MON, min holders 5, min score 60)\nSort by score DESC, then by methods count, then by liquidity\nTake top 20 candidates\nGenerate buy/sell signals\n\nExecute trades\n\nBuy on bonding curve (MON) or DEX (MON) - all trading uses MON\nMonitor positions\nStop-loss and take-profit\n\nDistribute profits\n\nCalculate total profit\nGet MMIND holders via Indexer (Transfer events)\nDistribute proportionally (30% of profit) in MON\nRisk Management\nPosition sizing: Based on confidence score (max 0.1 MON)\nStop-loss: -10% (automatic sell)\nTake-profit: +20% (sell half position)\nSlippage protection: 1-2% tolerance\nMinimum liquidity: 5 MON required\nUsage Examples\n\nStart autonomous trading:\n\n# Via OpenClaw chat\n\"Start autonomous trading agent\"\n\n# Or via cron job (runs every minute). Paths: use NADFUN_ENV_PATH / NADFUNAGENT_DATA_DIR for .env and data; run scripts from nadfun-trading skill directory (clawhub install).\nopenclaw cron add \\\n  --name \"Nad.fun Trading Agent\" \\\n  --cron \"* * * * *\" \\\n  --session isolated \\\n  --message \"Run autonomous trading cycle: 1) Load config from .env (path: NADFUN_ENV_PATH or NADFUNAGENT_DATA_DIR/.env; need MMIND_TOKEN_ADDRESS, MONAD_PRIVATE_KEY, MONAD_RPC_URL, MONAD_NETWORK). 2) From nadfun-trading skill directory run: node execute-bonding-v2.js (uses check-pnl.js for P&L from positions_report.json at POSITIONS_REPORT_PATH or NADFUNAGENT_DATA_DIR, auto-sells at +5% or -10%). 3) If there is positive PnL (profit >= 0.1 MON), distribute profits to MMIND token holders: use MMIND_TOKEN_ADDRESS from .env, get holders via indexer/Transfer events, distribute proportionally (e.g. 30%) in MON. Report output in English.\"\n\n\nCheck agent status:\n\n# Via OpenClaw chat\n\"Show trading agent status and statistics\"\n\n\nView found tokens: (data dir: NADFUNAGENT_DATA_DIR, default $HOME/nadfunagent)\n\n# View latest found tokens\ncat \"${NADFUNAGENT_DATA_DIR:-$HOME/nadfunagent}/found_tokens.json\" | jq '.[-1]'\n\n# View all tokens from last scan\ncat \"${NADFUNAGENT_DATA_DIR:-$HOME/nadfunagent}/found_tokens.json\" | jq '.[-1].tokens[] | .address'\n\n# View tokens found in multiple methods (higher confidence)\ncat \"${NADFUNAGENT_DATA_DIR:-$HOME/nadfunagent}/found_tokens.json\" | jq '.[-1].tokens[] | select(.foundInMethods > 1) | .address'\n\n# View summary of last 10 scans\ncat \"${NADFUNAGENT_DATA_DIR:-$HOME/nadfunagent}/found_tokens.json\" | jq '.[-10:] | .[] | {timestamp, totalFound}'\n\n\nManual trade:\n\n# Via OpenClaw chat\n\"Buy 0.1 MON worth of token 0x...\"\n\"Sell all tokens for 0x...\"\n\nIntegration with OpenClaw\n\nThis skill integrates with:\n\nCron Jobs: Schedule autonomous trading cycles\nSkills: Uses nadfun-trading, nadfun-indexer, nadfun-agent-api\nGateway: Runs via OpenClaw Gateway\nChannels: Can send notifications via Telegram/WhatsApp/etc.\nError Handling\nAll errors are logged but don't stop the agent\nFailed trades are retried with exponential backoff\nAPI failures fall back to Indexer queries\nNetwork errors trigger fallback RPC providers\nRate Limit Handling\n\nCRITICAL: Handle rate limits gracefully to avoid HTTP 429 errors.\n\nAPI Rate Limits\n\nNad.fun Agent API:\n\nWithout API Key: 10 requests/minute (IP-based)\nWith API Key: 100 requests/minute (Key-based)\nSolution: Add delays between requests (min 2 seconds between calls)\n\nNad.fun Public APIs (nad.fun/api/* and api.nadapp.net/*):\n\nRate limits are not publicly documented but appear to be IP-based\nSolution: Add 2-3 second delays between calls, especially for base64-decoded endpoints\nHandle HTTP 429 errors gracefully with exponential backoff\n\nAnthropic/Claude API (CRITICAL - This is the main source of HTTP 429):\n\nRate limits vary by tier (typically 50-200 requests per minute)\nProblem: Each agent execution makes multiple requests to Claude\nSolution:\nCron job runs every 10 minutes (*/10 * * * *) to reduce frequency\nOptimize agent logic to minimize token usage\nBatch operations when possible\nIf still hitting limits, increase to 15 minutes (*/15 * * * *)\nRate Limit Strategy\n\nAdd delays between API calls (CRITICAL):\n\n// Wait 2-3 seconds between Nad.fun API calls\nawait new Promise(resolve => setTimeout(resolve, 2000))\n\n// Wait 1 second between Indexer queries\nawait new Promise(resolve => setTimeout(resolve, 1000))\n\n\nLimit parallel requests:\n\nProcess tokens sequentially or in small batches (max 5 at a time)\nAdd delays between batches\nDon't make all 7 methods run simultaneously - stagger them\n\nHandle 429 errors gracefully:\n\nasync function fetchWithRetry(url, options, maxRetries = 2) {\n  for (let i = 0; i < maxRetries; i++) {\n    try {\n      const result = await fetch(url, options)\n      if (result.status === 429) {\n        const retryAfter = parseInt(result.headers.get('Retry-After') || '60')\n        console.log(`Rate limited, waiting ${retryAfter} seconds...`)\n        await new Promise(resolve => setTimeout(resolve, retryAfter * 1000))\n        continue\n      }\n      return result\n    } catch (err) {\n      if (i === maxRetries - 1) throw err\n      await new Promise(resolve => setTimeout(resolve, 5000 * (i + 1)))\n    }\n  }\n}\n\n\nOptimize agent execution:\n\nUse Indexer methods (1-2) first (they're faster and don't hit API limits)\nOnly use API methods (3-7) if Indexer doesn't find enough tokens\nAnalyze ALL tokens that pass filters (no limit)\nSkip detailed analysis if token doesn't meet basic filters\n\nCron frequency:\n\nCurrent: every 10 minutes (*/10 * * * *)\nIf still hitting Claude API limits, increase to 15 minutes (*/15 * * * *)"
  },
  "trust": {
    "sourceLabel": "tencent",
    "provenanceUrl": "https://clawhub.ai/encipher88/nadfunagent",
    "publisherUrl": "https://clawhub.ai/encipher88/nadfunagent",
    "owner": "encipher88",
    "version": "1.0.0",
    "license": null,
    "verificationStatus": "Indexed source record"
  },
  "links": {
    "detailUrl": "https://openagent3.xyz/skills/nadfunagent",
    "downloadUrl": "https://openagent3.xyz/downloads/nadfunagent",
    "agentUrl": "https://openagent3.xyz/skills/nadfunagent/agent",
    "manifestUrl": "https://openagent3.xyz/skills/nadfunagent/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/nadfunagent/agent.md"
  }
}