├── .env ├── token.png ├── token.json ├── package.json ├── src ├── index.js └── createToken.js ├── readme.md └── README.md /.env: -------------------------------------------------------------------------------- 1 | RPC_URL= 2 | PRIVATE_KEY= 3 | -------------------------------------------------------------------------------- /token.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rashadkhan2/pumpfun-api/HEAD/token.png -------------------------------------------------------------------------------- /token.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SolanaPortal", 3 | "symbol": "SPA", 4 | "description": "Testing SolanaPortal API for pumpfun token creation", 5 | "image": "./token.png", 6 | "twitter": "https://docs.solanaportal.io", 7 | "telegram": "https://docs.solanaportal.io", 8 | "website": "https://docs.solanaportal.io", 9 | "showName": true, 10 | "amount": 0.01, 11 | "slippage": 100, 12 | "tip": 0.0005 13 | } 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "solanaportal-bot", 3 | "version": "1.1.0", 4 | "type": "module", 5 | "scripts": { 6 | "start": "node src/index.js", 7 | "create-token": "node src/createToken.js" 8 | }, 9 | "dependencies": { 10 | "@project-serum/anchor": "^0.26.0", 11 | "@solana/web3.js": "^1.98.2", 12 | "bs58": "^6.0.0", 13 | "dotenv": "^16.5.0", 14 | "node-fetch": "^3.3.2" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import { bs58 } from "@project-serum/anchor/dist/cjs/utils/bytes/index.js"; 2 | import { Keypair, VersionedTransaction } from "@solana/web3.js"; 3 | import fetch from "node-fetch"; 4 | import { config as configDotenv } from "dotenv"; 5 | import readline from "readline/promises"; 6 | import { stdin, stdout } from "process"; 7 | 8 | // If you're running in ESM mode, ensure your package.json includes 9 | // { "type": "module" } or rename to .mjs 10 | 11 | // Load environment variables 12 | configDotenv(); 13 | const PRIVATE_KEY = process.env.PRIVATE_KEY || ""; 14 | const RPC_URL = process.env.RPC_URL || ""; 15 | 16 | async function main() { 17 | const rl = readline.createInterface({ input: stdin, output: stdout }); 18 | 19 | console.log("=== SolanaPortal BOT API ==="); 20 | const actionInput = await rl.question("Do you want to buy or sell? (buy/sell): "); 21 | const action = actionInput.trim().toLowerCase(); 22 | if (action !== "buy" && action !== "sell") { 23 | console.error("Invalid action. Please enter 'buy' or 'sell'."); 24 | rl.close(); 25 | return; 26 | } 27 | 28 | const mint = await rl.question( 29 | action === "buy" 30 | ? "Enter MINT CA of the token you want to buy: " 31 | : "Enter MINT CA of the token you want to sell: " 32 | ); 33 | 34 | const amountInput = await rl.question( 35 | action === "buy" 36 | ? "Enter amount in SOL to spend: " 37 | : "Enter amount in tokens to sell: " 38 | ); 39 | const amount = parseFloat(amountInput); 40 | if (isNaN(amount)) { 41 | console.error("Invalid amount."); 42 | rl.close(); 43 | return; 44 | } 45 | 46 | const slippageInput = await rl.question("Provide slippage tolerance (1-100): "); 47 | const slippage = parseInt(slippageInput, 10); 48 | if (isNaN(slippage) || slippage < 1 || slippage > 100) { 49 | console.error("Invalid slippage."); 50 | rl.close(); 51 | return; 52 | } 53 | 54 | const tipInput = await rl.question("Enter Jito TIP amount (e.g. 0.0001): "); 55 | const tip = parseFloat(tipInput); 56 | if (isNaN(tip) || tip <= 0) { 57 | console.error("Invalid tip amount."); 58 | rl.close(); 59 | return; 60 | } 61 | 62 | rl.close(); 63 | 64 | try { 65 | const wallet = Keypair.fromSecretKey(bs58.decode(PRIVATE_KEY)); 66 | console.log("Wallet:", wallet.publicKey.toBase58()); 67 | 68 | const params = { 69 | wallet_address: wallet.publicKey.toBase58(), 70 | action, 71 | dex: "pumpfun", 72 | mint: mint.trim(), 73 | amount, 74 | slippage, 75 | tip, 76 | type: "jito" 77 | }; 78 | 79 | const tradeResponse = await fetch("https://api.solanaportal.io/api/trading", { 80 | method: "POST", 81 | headers: { "Content-Type": "application/json" }, 82 | body: JSON.stringify(params) 83 | }); 84 | 85 | if (tradeResponse.status !== 200) { 86 | console.error("Trading API error:", tradeResponse.statusText); 87 | return; 88 | } 89 | 90 | const txBase64 = await tradeResponse.text(); 91 | const txBuffer = Buffer.from(txBase64, "base64"); 92 | const tx = VersionedTransaction.deserialize(txBuffer); 93 | tx.sign([wallet]); 94 | const signedTx = bs58.encode(tx.serialize()); 95 | 96 | const jitoResponse = await fetch( 97 | "https://tokyo.mainnet.block-engine.jito.wtf/api/v1/transactions", 98 | { 99 | method: "POST", 100 | headers: { "Content-Type": "application/json" }, 101 | body: JSON.stringify({ 102 | jsonrpc: "2.0", 103 | id: 1, 104 | method: "sendTransaction", 105 | params: [signedTx] 106 | }) 107 | } 108 | ); 109 | 110 | if (jitoResponse.status === 200) { 111 | const { result: signature } = await jitoResponse.json(); 112 | console.log( 113 | `Transaction to ${action} ${mint.trim()} for ${amount} successful: https://solscan.io/tx/${signature}` 114 | ); 115 | } else { 116 | console.error("Transaction failed, please try again", await jitoResponse.text()); 117 | } 118 | } catch (err) { 119 | console.error("Error:", err.message || err); 120 | } 121 | } 122 | 123 | main(); 124 | -------------------------------------------------------------------------------- /src/createToken.js: -------------------------------------------------------------------------------- 1 | // src/createToken.js 2 | import 'dotenv/config'; 3 | import { Keypair, VersionedTransaction } from '@solana/web3.js'; 4 | import { bs58 } from '@project-serum/anchor/dist/cjs/utils/bytes/index.js'; 5 | import fetch, { FormData, Blob } from 'node-fetch'; 6 | import fs from 'fs'; 7 | import path from 'path'; 8 | 9 | async function main() { 10 | try { 11 | // ─── 1. Load your wallet ─────────────────────────────────────────────────── 12 | const pk = process.env.PRIVATE_KEY; 13 | if (!pk) throw new Error('🛑 PRIVATE_KEY not set in .env'); 14 | const wallet = Keypair.fromSecretKey(bs58.decode(pk)); 15 | console.log('🔑 Wallet:', wallet.publicKey.toBase58()); 16 | 17 | // ─── 2. Read token.json ─────────────────────────────────────────────────── 18 | const tokenJsonPath = path.resolve(process.cwd(), 'token.json'); 19 | const raw = await fs.promises.readFile(tokenJsonPath, 'utf8'); 20 | const token = JSON.parse(raw); 21 | 22 | // ─── 3. Upload metadata to IPFS ────────────────────────────────────────── 23 | // Create a Blob from your image file (mimics fs.openAsBlob) 24 | const imagePath = path.resolve(process.cwd(), token.image); 25 | if (!fs.existsSync(imagePath)) { 26 | throw new Error(`🛑 Image not found at ${imagePath}`); 27 | } 28 | const fileBuffer = await fs.promises.readFile(imagePath); 29 | const blob = new Blob([fileBuffer]); 30 | 31 | // Build the FormData exactly like your example 32 | const formData = new FormData(); 33 | formData.append('file', blob, path.basename(imagePath)); // Image 34 | formData.append('name', token.name); 35 | formData.append('symbol', token.symbol); 36 | formData.append('description', token.description); 37 | if (token.twitter) formData.append('twitter', token.twitter); 38 | if (token.telegram) formData.append('telegram', token.telegram); 39 | if (token.website) formData.append('website', token.website); 40 | formData.append('showName', token.showName ? 'true' : 'false'); 41 | 42 | console.log('☁️ Uploading metadata to IPFS…'); 43 | const ipfsRes = await fetch('https://pump.fun/api/ipfs', { 44 | method: 'POST', 45 | body: formData 46 | }); 47 | if (!ipfsRes.ok) { 48 | const errText = await ipfsRes.text(); 49 | throw new Error(`IPFS upload failed (${ipfsRes.status}): ${errText}`); 50 | } 51 | const ipfsJson = await ipfsRes.json(); 52 | console.log('✔️ IPFS response:', ipfsJson); 53 | 54 | // ─── 4. Prepare create-token params ──────────────────────────────────────── 55 | const { metadataUri, metadata } = ipfsJson; 56 | const param = { 57 | wallet_address: wallet.publicKey.toBase58(), 58 | name: metadata.name, 59 | symbol: metadata.symbol, 60 | metadataUri, 61 | amount: token.amount, 62 | slippage: token.slippage, 63 | tip: token.tip, 64 | type: 'jito' 65 | }; 66 | console.log('📦 Create-token payload:', param); 67 | 68 | // ─── 5. Request the base64 transaction ──────────────────────────────────── 69 | const createUrl = 'https://api.solanaportal.io/api/create/token/pumpfun'; 70 | console.log('🔗 POST →', createUrl); 71 | const createRes = await fetch(createUrl, { 72 | method: 'POST', 73 | headers: { 'Content-Type': 'application/json' }, 74 | body: JSON.stringify(param) 75 | }); 76 | if (!createRes.ok) { 77 | const errText = await createRes.text(); 78 | throw new Error(`Create-token failed (${createRes.status}): ${errText}`); 79 | } 80 | const b64 = await createRes.text(); 81 | console.log('✔️ Received base64 Tx'); 82 | 83 | // ─── 6. Decode, sign & re-encode ────────────────────────────────────────── 84 | const buffer = Buffer.from(b64, 'base64'); 85 | const txn = VersionedTransaction.deserialize(buffer); 86 | txn.sign([wallet]); 87 | const signed = bs58.encode(txn.serialize()); 88 | 89 | // ─── 7. Send signed transaction via Jito ────────────────────────────────── 90 | console.log('🚀 Sending signed transaction to Jito…'); 91 | const jitoRes = await fetch( 92 | 'https://tokyo.mainnet.block-engine.jito.wtf/api/v1/transactions', 93 | { 94 | method: 'POST', 95 | headers: { 'Content-Type': 'application/json' }, 96 | body: JSON.stringify({ 97 | jsonrpc: '2.0', 98 | id: 1, 99 | method: 'sendTransaction', 100 | params: [signed] 101 | }) 102 | } 103 | ); 104 | const jitoJson = await jitoRes.json(); 105 | if (jitoRes.ok) { 106 | console.log(`✅ Transaction successful! https://solscan.io/tx/${jitoJson.result}`); 107 | } else { 108 | console.error('❌ Jito submission failed:', jitoJson); 109 | } 110 | 111 | } catch (err) { 112 | console.error('🛑 Error:', err.message); 113 | } 114 | } 115 | 116 | main(); 117 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # SolanaPortal PumpFun Bot 2 | 3 | This repository contains a simple Node.js CLI bot that uses the SolanaPortal PumpFun API to buy and sell SPL tokens on Solana via Jito’s RPC endpoint. 4 | 5 | --- 6 | 7 | ## 🚀 How the PumpFun API Works 8 | 9 | SolanaPortal’s `/api/trading` endpoint builds and returns a base64-encoded transaction for a PumpFun swap. You submit this payload along with your parameters, sign it locally, and then send it to Solana via Jito. 10 | 11 | ### Endpoint 12 | 13 | ``` 14 | POST https://api.solanaportal.io/api/trading 15 | ``` 16 | 17 | ### Required Parameters 18 | 19 | | Field | Type | Description | 20 | | ---------------- | -------- | -------------------------------------------------------------------------------- | 21 | | `wallet_address` | `string` | Your Solana wallet public key (Base58). | 22 | | `action` | `string` | Either `"buy"` or `"sell"`. | 23 | | `dex` | `string` | Always `"pumpfun"` for this script. | 24 | | `mint` | `string` | The mint address of the token to trade (Base58). | 25 | | `amount` | `number` | If buying: the amount of SOL to spend. If selling: the amount of tokens to sell. | 26 | | `slippage` | `number` | Maximum price impact tolerance as a percent (integer between 1 and 100). | 27 | | `tip` | `number` | Jito tip in SOL (e.g. `0.0001`). | 28 | | `type` | `string` | Always `"jito"` in this implementation. | 29 | 30 | ### Response 31 | 32 | * **200 OK**: Returns a base64 string representing a VersionedTransaction. 33 | * **Error**: Non-200 response with error text in the body. 34 | 35 | --- 36 | 37 | ## 📝 Signing & Submitting Transactions 38 | 39 | 1. Decode base64 payload to a `VersionedTransaction`. 40 | 41 | 2. Sign locally using your `PRIVATE_KEY` (loaded from `.env`). 42 | 43 | 3. Encode the signed transaction to Base58. 44 | 45 | 4. Submit via Jito’s RPC endpoint: 46 | 47 | ```http 48 | POST https://tokyo.mainnet.block-engine.jito.wtf/api/v1/transactions 49 | Content-Type: application/json 50 | 51 | { 52 | "jsonrpc": "2.0", 53 | "id": 1, 54 | "method": "sendTransaction", 55 | "params": [ "" ] 56 | } 57 | ``` 58 | 59 | 5. On success, Jito returns a transaction signature you can view on Solscan. 60 | 61 | --- 62 | 63 | ## 💻 Code Overview 64 | 65 | The main script is located at `src/index.js`: 66 | 67 | ```js 68 | import { bs58 } from "@project-serum/anchor/dist/cjs/utils/bytes/index.js"; 69 | import { Keypair, VersionedTransaction } from "@solana/web3.js"; 70 | import fetch from "node-fetch"; 71 | import { config as configDotenv } from "dotenv"; 72 | import readline from "readline/promises"; 73 | import { stdin, stdout } from "process"; 74 | 75 | configDotenv(); 76 | 77 | async function main() { 78 | // 1. Prompt user: buy/sell 79 | // 2. Prompt mint, amount, slippage, tip 80 | // 3. Call SolanaPortal API 81 | // 4. Deserialize, sign, and serialize transaction 82 | // 5. Submit via Jito 83 | // 6. Log success or error 84 | } 85 | 86 | main(); 87 | ``` 88 | 89 | Key points: 90 | 91 | * Uses `dotenv` to load `PRIVATE_KEY` and `RPC_URL`. 92 | * Uses Node’s `readline/promises` for a simple CLI. 93 | * Ensures ESM mode (`"type": "module"` in `package.json`). 94 | 95 | --- 96 | 97 | ## 📦 Installation & Running 98 | 99 | 1. **Clone** this repository: 100 | 101 | ```bash 102 | git clone https://github.com/yourusername/solanaportal-pumpfun-bot.git 103 | cd solanaportal-pumpfun-bot 104 | ``` 105 | 106 | 2. **Install** dependencies: 107 | 108 | ```bash 109 | npm install 110 | ``` 111 | 112 | 3. **Configure** environment variables in a `.env` file at the project root: 113 | 114 | ```dotenv 115 | PRIVATE_KEY= 116 | RPC_URL=https://tokyo.mainnet.block-engine.jito.wtf/api/v1/transactions 117 | ``` 118 | 119 | 4. **Run** the bot: 120 | 121 | ```bash 122 | npm start 123 | ``` 124 | 125 | 5. **Follow prompts** to buy or sell tokens. 126 | 127 | --- 128 | 129 | ## 💸 Buying & Selling 130 | 131 | 1. **Action**: Choose `buy` or `sell`. 132 | 2. **Mint**: Provide the token’s mint address. 133 | 3. **Amount**: 134 | 135 | * If `buy`: enter amount of SOL. 136 | * If `sell`: enter amount of tokens. 137 | 4. **Slippage**: Enter tolerance (1–100%). 138 | 5. **Tip**: Enter Jito tip in SOL (e.g. `0.0001`). 139 | 140 | After you confirm, the bot: 141 | 142 | * Builds and signs a transaction. 143 | * Sends it through Jito’s RPC. 144 | * Prints a Solscan link on success. 145 | 146 | --- 147 | 148 | ## 📚 Further Reading 149 | 150 | For full SolanaPortal PumpFun API docs, visit: 151 | 152 | > [https://docs.solanaportal.io](https://docs.solanaportal.io) 153 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SolanaPortal PumpFun API 2 | 3 | A simple Node.js CLI bot that uses SolanaPortal’s PumpFun API to: 4 | 5 | - **Swap SPL tokens** (buy/sell) via Jito’s RPC 6 | - **Create your own PumpFun mint** (metadata upload + on-chain mint) 7 | 8 | Everything runs locally in ESM mode, with transactions built by SolanaPortal, signed in your wallet, and sent through Jito for max speed. 9 | 10 | --- 11 | 12 | ## 🚀 How the Trading (Buy/Sell) API Works 13 | 14 | ### Endpoint 15 | 16 | ``` 17 | POST https://api.solanaportal.io/api/trading 18 | ``` 19 | 20 | ### Request Parameters 21 | 22 | | Field | Type | Description | 23 | | ---------------- | -------- | -------------------------------------------------------------------------------- | 24 | | `wallet_address` | `string` | Your Solana wallet public key (Base58). | 25 | | `action` | `string` | `"buy"` or `"sell"`. | 26 | | `dex` | `string` | `"pumpfun"`. | 27 | | `mint` | `string` | The token mint address (Base58). | 28 | | `amount` | `number` | SOL amount to spend (buy) or token amount to sell. | 29 | | `slippage` | `number` | Max price impact tolerance (%) between 1–100. | 30 | | `tip` | `number` | Jito tip in SOL (e.g. `0.0001`). | 31 | | `type` | `string` | `"jito"` (this implementation). | 32 | 33 | ### Response 34 | 35 | - **200 OK** 36 | Returns a base64-encoded `VersionedTransaction` for you to decode/sign/submit. 37 | - **Error** 38 | Non-200 status with error details in the response body. 39 | 40 | --- 41 | 42 | ## 📚 Token Creation (Mint) API 43 | 44 | PumpFun lets you deploy your own SPL token + first liquidity in one command: 45 | 46 | 1. **Save** your token info in `token.json` (name, symbol, description, image path, and other metadata). 47 | 2. **Upload** Metadata + Image to IPFS via PumpFun’s IPFS endpoint. 48 | 3. **Request** a “create token” transaction from SolanaPortal. 49 | 4. **Sign** it locally. 50 | 5. **Send** via Jito RPC. 51 | 6. **Log** the Solscan URL on success. 52 | 53 | > **Note:** The script reads your metadata from `token.json` first, so be sure to configure it before running. 54 | 55 | ### 1. IPFS Metadata Upload 56 | 57 | ``` 58 | POST https://pump.fun/api/ipfs 59 | ``` 60 | 61 | **FormData fields**: 62 | 63 | | Field | Type | Description | 64 | | ------------- | ------------ | ----------------------------- | 65 | | `file` | Binary image | PNG/JPG token logo | 66 | | `name` | `string` | Token name (e.g. “My Token”) | 67 | | `symbol` | `string` | Token symbol (e.g. “MTK”) | 68 | | `description` | `string` | Token description | 69 | | `twitter` | `string` | (optional) Twitter URL | 70 | | `telegram` | `string` | (optional) Telegram URL | 71 | | `website` | `string` | (optional) Website URL | 72 | | `showName` | `string` | `"true"` to display on UI | 73 | 74 | **Response**: 75 | 76 | ```json 77 | { 78 | "metadataUri": "https://…/metadata.json", 79 | "metadata": { 80 | "name": "My Token", 81 | "symbol": "MTK", 82 | "…": "…" 83 | } 84 | } 85 | ``` 86 | 87 | ### 2. Create-Token API 88 | 89 | ``` 90 | POST https://api.solanaportal.io/api/create/token/pumpfun 91 | ``` 92 | 93 | **JSON body**: 94 | 95 | | Field | Type | Description | 96 | | ---------------- | -------- | ------------------------------------------------------ | 97 | | `wallet_address` | `string` | Your wallet public key. | 98 | | `name` | `string` | Must match `metadata.name`. | 99 | | `symbol` | `string` | Must match `metadata.symbol`. | 100 | | `metadataUri` | `string` | URI returned from IPFS step. | 101 | | `amount` | `number` | SOL to pay for initial mint (e.g. `0.01`). | 102 | | `slippage` | `number` | % slippage tolerance (1–100). | 103 | | `tip` | `number` | Jito tip in SOL (e.g. `0.0005`). | 104 | | `type` | `string` | `"jito"`. | 105 | 106 | **Response**: 107 | 108 | - **200 OK**: 109 | Returns a base64 string (the unsigned transaction). 110 | - **Post-Sign**: 111 | After signing and sending via Jito, your script will log: 112 | 113 | ``` 114 | txn succeed: https://solscan.io/tx/ 115 | ``` 116 | 117 | --- 118 | 119 | ## 💻 Code Overview 120 | 121 | ### `src/index.js` – Trading CLI 122 | 123 | 1. Prompts: **buy/sell** → **mint** → **amount** → **slippage** → **tip** 124 | 2. Calls `/api/trading` → deserializes & signs → sends via Jito 125 | 3. Prints Solscan link on success 126 | 127 | ### `src/createToken.js` – Token Creation 128 | 129 | 1. Reads **`token.json`** for metadata & local image path 130 | 2. Uploads logo + metadata to `https://pump.fun/api/ipfs` 131 | 3. Calls SolanaPortal create-token endpoint 132 | 4. Signs & sends via Jito 133 | 5. Logs: 134 | 135 | ``` 136 | txn succeed: https://solscan.io/tx/ 137 | ``` 138 | 139 | --- 140 | 141 | ## 📦 Installation 142 | 143 | ```bash 144 | git clone https://github.com/Rashadkhan2/pumpfun-api.git 145 | cd solanaportal-pumpfun-api 146 | npm install 147 | ``` 148 | 149 | ### Environment Variables 150 | 151 | Create a `.env` in project root: 152 | 153 | ```dotenv 154 | PRIVATE_KEY= 155 | RPC_URL=https://tokyo.mainnet.block-engine.jito.wtf/api/v1/transactions 156 | ``` 157 | 158 | --- 159 | 160 | ## 🚀 Running 161 | 162 | - **Trading Bot** 163 | ```bash 164 | npm start 165 | ``` 166 | 167 | - **Create Token** 168 | ```bash 169 | npm run create-token 170 | ``` 171 | 172 | --- 173 | 174 | ## 🔧 Configuration Files 175 | 176 | ### `token.json` 177 | 178 | Place alongside `/src` and your `.env`. Example: 179 | 180 | ```json 181 | { 182 | "name": "SolanaPortal", 183 | "symbol": "SPA", 184 | "description": "Testing SolanaPortal API for pumpfun token creation", 185 | "image": "./token.png", 186 | "twitter": "https://docs.solanaportal.io", 187 | "telegram": "https://docs.solanaportal.io", 188 | "website": "https://docs.solanaportal.io", 189 | "showName": true, 190 | "amount": 0.01, 191 | "slippage": 100, 192 | "tip": 0.0005 193 | } 194 | ``` 195 | 196 | - **`image`**: Path to your PNG/JPG logo 197 | - **`amount`**: SOL to fund the mint 198 | - **`slippage`**/**`tip`**: As in the tables above 199 | 200 | --- 201 | 202 | ## 💸 Usage 203 | 204 | 1. **`npm start`** → follow prompts to **buy** or **sell**. 205 | 2. **`npm run create-token`** → mints your own token with initial SOL. 206 | 207 | --- 208 | 209 | ## 📚 Further Reading 210 | 211 | Full SolanaPortal PumpFun docs: 212 | 👉 https://docs.solanaportal.io 213 | --------------------------------------------------------------------------------