Install
openclaw skills install pancake-swap-skillsClawHub Security found sensitive or high-impact capabilities. Review the scan results before using.
BSC (Binance Smart Chain) trading on PancakeSwap — wallet creation, token swaps, pair discovery, and balance management.
openclaw skills install pancake-swap-skillsThis skill enables an AI agent to trade tokens on PancakeSwap (BSC mainnet). It covers:
clawchain skill (skill.md or curl_skills.md).~/.config/bsc_agent/ and optionally ~/.config/clawchain/credentials.json (read-only, for EVM key registration only).| File | Access | Purpose |
|---|---|---|
~/.config/bsc_agent/wallet.json | Read/Write (created once) | Stores the agent's BSC private key and address for signing transactions |
~/.config/clawchain/credentials.json | Read-only (optional) | Used only if registering BSC public key on ClawChain for EVM event tracking |
| Endpoint | Purpose |
|---|---|
BSC RPC (BSC_RPC_URL) | Read blockchain state (balances, pair info) and submit signed transactions |
| PancakeSwap Router contract | On-chain calls to discover pairs, get quotes, execute swaps |
Set these environment variables before using the skill. All have sensible defaults for BSC mainnet:
export BSC_RPC_URL="https://bsc-dataseed1.binance.org"
export BSC_CHAIN_ID=56
# PancakeSwap V2 (BSC mainnet)
export PANCAKE_ROUTER="0x10ED43C718714eb63d5aA57B78B54704E256024E"
export WBNB="0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c"
Block explorer: https://bscscan.com
PancakeSwap UI: https://pancakeswap.finance/swap?chain=bsc
For testnet (tBNB, tCHR, etc.), override the defaults:
export BSC_RPC_URL="https://data-seed-prebsc-1-s1.binance.org:8545"
export BSC_CHAIN_ID=97
export PANCAKE_ROUTER="0xD99D1c33F9fC3444f8101754aBC46c52416550D1"
export WBNB="0xae13d989dac2f0debff460ac112a837c89baa7cd"
This skill requires Node.js 18+ and the ethers npm package (v6):
npm install ethers
# or: pnpm add ethers
The agent needs a local wallet file containing a private key and public address so it can sign transactions. This file is created once during initial setup and reused for all subsequent operations.
⚠️ Security: The wallet file contains sensitive key material. See the Security Notes section for encryption and file permission best practices. Only use a dedicated, small-balance wallet — never your main funds.
Run this script to generate a new wallet and write it to a file:
node -e "
const fs = require('fs');
const path = require('path');
const { ethers } = require('ethers');
const wallet = ethers.Wallet.createRandom();
const dir = process.env.HOME + '/.config/bsc_agent';
fs.mkdirSync(dir, { recursive: true });
const file = dir + '/wallet.json';
fs.writeFileSync(file, JSON.stringify({
privateKey: wallet.privateKey,
address: wallet.address,
publicKey: wallet.publicKey
}, null, 2), { mode: 0o600 });
console.log('Wallet saved to ' + file);
console.log('Address: ' + wallet.address);
"
Default file location: ~/.config/bsc_agent/wallet.json
File format (wallet.json):
{
"privateKey": "0x...",
"address": "0x...",
"publicKey": "0x..."
}
chmod 600). Never log or expose this value.To use a different path, set the environment variable:
export BSC_WALLET_FILE="$HOME/.config/bsc_agent/wallet.json"
# Then in the script, write to process.env.BSC_WALLET_FILE
address and publicKey from wallet.json.<address>This step is optional. It links your BSC wallet public key to your ClawChain agent account to enable tracking EVM events on Chromia. Skip this if you don't need on-chain event tracking.
Prerequisite: You must already have a registered and claimed agent on ClawChain. See the
clawchainskill for registration instructions.
wallet.json file with publicKey (required) and optionally address~/.config/clawchain/credentials.json (created by the clawchain skill)These are the same variables used by the clawchain skill — set them if not already configured:
export CLAWCHAIN_BRID="9D728CC635A9D33DAABAC8217AA8131997A8CBF946447ED0B98760245CE5207E"
export CLAWCHAIN_NODE="https://chromia.01node.com:7740"
# Read wallet info
WALLET_FILE="${BSC_WALLET_FILE:-$HOME/.config/bsc_agent/wallet.json}"
PUBLIC_KEY=$(cat $WALLET_FILE | grep -o '"publicKey": "[^"]*' | cut -d'"' -f4)
ADDRESS=$(cat $WALLET_FILE | grep -o '"address": "[^"]*' | cut -d'"' -f4)
# Register public key on Chromia
chr tx register_evm_public_key \
"pancakeswap" \
"$PUBLIC_KEY" \
"BSC" \
56 \
"$ADDRESS" \
--ft-auth \
--secret ~/.config/clawchain/credentials.json \
-brid $CLAWCHAIN_BRID \
--api-url $CLAWCHAIN_NODE \
--await
Arguments: wallet_type public_key chain network_id address
Network IDs:
569715137Note: If you don't have an address, use "" (empty string) for the last parameter.
If you're integrating this into a web application with an authenticated FT4 session:
const fs = require('fs');
const walletFile = process.env.BSC_WALLET_FILE || '~/.config/bsc_agent/wallet.json';
const walletData = JSON.parse(fs.readFileSync(walletFile, 'utf8'));
// Assuming you have a Chromia session object (from @chromia/ft4)
const result = await session.call({
name: 'register_evm_public_key',
args: [
'pancakeswap', // wallet_type
walletData.publicKey, // public_key (required)
'BSC', // chain
56, // network_id (56 for BSC mainnet, 97 for testnet)
walletData.address || "" // address (optional)
]
});
console.log('Public key registered:', result);
To check which public keys are registered for your agent:
# Get all registered public keys for an agent
chr query get_evm_public_keys 'agent_name=your_agent_name' \
-brid $CLAWCHAIN_BRID --api-url $CLAWCHAIN_NODE
# Get specific public key by wallet type
chr query get_evm_public_key_by_type \
'agent_name=your_agent_name' \
'wallet_type=pancakeswap' \
-brid $CLAWCHAIN_BRID --api-url $CLAWCHAIN_NODE
0x and be at least 66 characters (0x + 64 hex chars).The agent does not have an "account" on a server — it has a BSC address (from wallet.json). Fund the agent by sending BNB and tokens to that address:
<address> from wallet.json.<address>. The agent can swap via PancakeSwap if a pair exists (e.g. USDT/BNB, BUSD/BNB).provider.getBalance(address)balanceOf(address) on the token contract.The agent can work with any BEP-20 token and determine which swaps are possible. No fixed token list is required.
symbol() → e.g. "USDT", "WBNB"decimals() → use for amount math and display (e.g. 18 or 6)name() → full name (optional)factory() (read-only) to get the PancakeSwap V2 Factory address.factory.getPair(tokenA, tokenB). If the result is the zero address (0x000...), no pair exists. Otherwise it is the pair contract address.getReserves(). If both reserves are > 0, the pair has liquidity and that swap is available.getAmountsOut(amountIn, path) (read-only). If the call reverts or returns zero for the output amount, the swap is not feasible (no liquidity or invalid path).[tokenA, WBNB, tokenB]. Call getAmountsOut(amountIn, path); if it succeeds and returns a positive amount, the swap is available.The agent uses the private key from wallet.json to sign a swap transaction and sends it to BSC.
| Contract | Address |
|---|---|
| Router | 0x10ED43C718714eb63d5aA57B78B54704E256024E |
| WBNB | 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c |
| Method | Use case |
|---|---|
swapExactETHForTokens | Swap exact BNB for tokens (min amount out) |
swapExactTokensForETH | Swap exact tokens for BNB |
swapExactTokensForTokens | Swap exact token A for token B |
swapTokensForExactTokens | Swap tokens for exact amount of token B |
wallet.json (privateKey + address).new ethers.JsonRpcProvider(BSC_RPC_URL) (mainnet: chain id 56).getAmountsOut(amountIn, path). If it reverts or returns zero, try a multi-hop path (e.g. [tokenA, WBNB, tokenB]) or inform the user the pair has no liquidity.[WBNB, tokenAddress]; token → BNB: [tokenAddress, WBNB]; token → token: [tokenA, WBNB, tokenB] (or direct if pair exists).swapExactETHForTokens, swapExactTokensForETH, or swapExactTokensForTokens.Math.floor(Date.now() / 1000) + 300 (e.g. 5 minutes).getAmountsOut(amountIn, path) then apply slippage (e.g. 1% = amountOut * 0.99).{ value: amountInWei }; for token → BNB or token → token, approve router for the token first, then call the swap.1e18 wei.~/.config/bsc_agent/wallet.json (or BSC_WALLET_FILE).address when telling the user where to top up.privateKey only inside the signing process (ethers Wallet). Never log or print the private key.wallet.json.ethers.Wallet(privateKey, provider).swapExactETHForTokens) with path, deadline, amountOutMin.tx = await router.swapExactETHForTokens(...); await tx.wait().https://bscscan.com/tx/<hash>.getAmountsOut(amountIn, path) (read-only) to get expected amountOut, then compute amountOutMin with slippage.| Symbol | Address | Notes |
|---|---|---|
| WBNB | 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c | Native wrapped; use in paths |
| USDT | 0x55d398326f99059fF775485246999027B3197955 | Stablecoin |
| BUSD | 0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56 | Stablecoin |
Do not limit to these. The agent should accept any BEP-20 address and discover availability via getPair / getReserves and getAmountsOut (see §3). If a pair has no liquidity, add liquidity via the PancakeSwap UI or use another token pair.
mode: 0o600 automatically. Verify with:
chmod 600 ~/.config/bsc_agent/wallet.json
ls -la ~/.config/bsc_agent/wallet.json
# Should show: -rw-------
ethers.Wallet instance for signing. It is never sent to any remote endpoint.Wallet.encrypt()) for production use. The agent would prompt for a password to decrypt at startup.For enhanced security, use ethers.js encrypted wallet format instead of plaintext:
// Create encrypted wallet (during setup)
const encrypted = await wallet.encrypt("your-password");
fs.writeFileSync(file, encrypted, { mode: 0o600 });
// Load encrypted wallet (during operations)
const wallet = await ethers.Wallet.fromEncryptedJson(
fs.readFileSync(file, 'utf8'),
"your-password"
);
| Error | Meaning / action |
|---|---|
| insufficient funds | Not enough BNB for gas or not enough token/BNB for the swap. User should top up. |
| execution reverted: PancakeRouter: INSUFFICIENT_OUTPUT_AMOUNT | Slippage too low; increase or retry. |
| execution reverted: PancakeRouter: EXPIRED | Deadline passed; rebuild tx with new deadline. |
| nonce too low | Reuse of nonce; wait and retry or refresh nonce from chain. |