NadFun Token Creation
Guide users through uploading an image and metadata, mining a vanity salt, and deploying a token on-chain via BondingCurveRouter in a 4-step flow on Nad.fun.
Description
name: create-token description: Guide for creating a token on Nad.fun. Covers the full 4-step flow - uploading an image, uploading metadata, mining a salt for a vanity address, and deploying the token on-chain via the BondingCurveRouter contract. user-invocable: true argument-hint: [name] [symbol]
Nad.fun Token Creation Flow
Prerequisite: monad-development Skill
Before proceeding, check if the monad-development skill is installed. If it is not available, install it using ClawHub:
clawhub search "monad"
Find and install the monad-development skill from the results. This skill is required for wallet management and on-chain actions in Step 3 and Step 4.
Help the user create a token on Nad.fun by following the 4-step sequential process below. Each step depends on the output of the previous step.
API Base URL
Production: https://api.nadapp.net
Contract Addresses
BondingCurveRouter = "0x6F6B8F1a20703309951a5127c45B49b1CD981A22"
Lens = "0x7e78A8DE94f21804F7a17F4E8BF9EC2c872187ea"
Step 1: Upload Image
POST /agent/token/image
- Content-Type:
image/png,image/jpeg,image/webp, orimage/svg+xml - Body: Raw binary image data (max 5MB)
- Returns:
image_uri(CDN URL) andis_nsfw(boolean)
const imageResponse = await fetch("https://api.nadapp.net/agent/token/image", {
method: "POST",
headers: { "Content-Type": imageFile.type },
body: imageFile,
});
const { image_uri, is_nsfw } = await imageResponse.json();
Error Codes
| Status | Description |
|---|---|
| 400 | Invalid image format or missing image |
| 413 | Image exceeds 5MB limit |
| 500 | NSFW check failed or upload failed |
Step 2: Upload Metadata
POST /agent/token/metadata
- Content-Type:
application/json - Requires:
image_urifrom Step 1
Request Body
Required fields:
| Field | Type | Constraints |
|---|---|---|
image_uri |
string | Must be from https://storage.nadapp.net/ |
name |
string | 1-32 characters |
symbol |
string | 1-10 characters, alphanumeric only (/^[a-zA-Z0-9]+$/) |
Optional fields:
| Field | Type | Constraints |
|---|---|---|
description |
string or null | Max 500 characters |
website |
string or null | Must start with https:// |
twitter |
string or null | Must contain x.com and start with https:// |
telegram |
string or null | Must contain t.me and start with https:// |
const metadataResponse = await fetch("https://api.nadapp.net/agent/token/metadata", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
image_uri,
name: "My Token",
symbol: "MTK",
description: "An awesome token for the Nad.fun",
website: "https://mytoken.com",
twitter: "https://x.com/mytoken",
telegram: "https://t.me/mytoken",
}),
});
const { metadata_uri } = await metadataResponse.json();
Error Codes
| Status | Description |
|---|---|
| 400 | NSFW status unknown, invalid data, or validation failed |
| 500 | Upload to storage or database failed |
Step 3: Mine Salt
POST /agent/salt
- Content-Type:
application/json - Requires:
metadata_urifrom Step 2 - Produces a vanity token address ending in
7777
Request Body
| Field | Type | Description |
|---|---|---|
creator |
string | Creator's wallet address (EVM format) |
name |
string | Token name (must match metadata) |
symbol |
string | Token symbol (must match metadata) |
metadata_uri |
string | Metadata URI from Step 2 |
const saltResponse = await fetch("https://api.nadapp.net/agent/salt", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
creator: walletAddress,
name: "My Token",
symbol: "MTK",
metadata_uri: metadataUri,
}),
});
const { salt, address } = await saltResponse.json();
- Returns:
salt(bytes32 hex) andaddress(token address with7777suffix)
Error Codes
| Status | Description |
|---|---|
| 400 | Invalid parameters |
| 408 | Timeout - max iterations reached |
| 500 | Internal server error |
Step 4: Create Token On-Chain
Call BondingCurveRouter.create() with the data from previous steps.
TokenCreationParams
struct TokenCreationParams {
string name;
string symbol;
string tokenURI; // metadata_uri from Step 2
uint256 amountOut; // 0 for no initial buy, or use Lens.getInitialBuyAmountOut(amountIn)
bytes32 salt; // salt from Step 3
uint8 actionId; // always 1 (graduate to Capricorn V3)
}
function create(TokenCreationParams calldata params) external payable returns (address token, address pool);
Option A: Create Without Initial Buy
Send only the deploy fee as msg.value.
const curve = new ethers.Contract(BONDING_CURVE_ADDRESS, BONDING_CURVE_ABI, signer);
const [deployFee,,] = await curve.feeConfig();
const params = {
name, symbol,
tokenURI: metadata_uri,
amountOut: 0,
salt,
actionId: 1,
};
const tx = await router.create(params, { value: deployFee });
await tx.wait();
Option B: Create With Initial Buy
Send deployFee + amountIn as msg.value. Use Lens.getInitialBuyAmountOut(amountIn) for amountOut.
const lens = new ethers.Contract(LENS_ADDRESS, LENS_ABI, signer);
const expectedAmountOut = await lens.getInitialBuyAmountOut(amountIn);
const [deployFee,,] = await curve.feeConfig();
const params = {
name, symbol,
tokenURI: metadata_uri,
amountOut: expectedAmountOut,
salt,
actionId: 1,
};
const tx = await router.create(params, { value: deployFee + amountIn });
await tx.wait();
Wallet for On-Chain Actions
For Step 3 (salt mining) and Step 4 (on-chain deployment), use the wallet from the monad-development skill. That skill handles all wallet configuration, private key management, and RPC setup. Use the signer and wallet address it provides when calling the salt API (creator field) and when sending the BondingCurveRouter.create() transaction.
Important Rules
- Sequential process - Each step depends on the previous step's output.
- NSFW validation - Images are auto-checked in Step 1; the flag carries into metadata.
- URL validation - All URLs must use HTTPS. Twitter must use
x.com, Telegram must uset.me. - Image domain restriction - Only
https://storage.nadapp.net/image URIs are accepted in metadata. - Salt mining - May timeout if the vanity address pattern can't be found within iteration limits.
- actionId - Always use
1(graduate to Capricorn V3).
When Generating Code
- Use
ethersv6 syntax by default unless the user specifies otherwise. - Always handle errors for each API call before proceeding to the next step.
- The
saltfrom Step 3 andmetadata_urifrom Step 2 are both needed for Step 4. - For initial buy, always query
Lens.getInitialBuyAmountOut()to get the correctamountOut.
Reviews (0)
No reviews yet. Be the first to review!
Comments (0)
No comments yet. Be the first to share your thoughts!