Install
openclaw skills install atxswapManage ATX on BSC with wallet creation, price and balance queries, PancakeSwap V3 swaps, liquidity operations, LP positions and holdings, and BNB/ERC20 transfers. Use when the user mentions ATX, BSC, PancakeSwap V3, wallet creation, price checks, buying, selling, liquidity, fees, holdings, LP positions, or token transfers.
openclaw skills install atxswapExecute ATX trading and wallet workflows on BSC. This skill is designed for agents that need safe, repeatable commands for wallet management, ATX/USDT quotes, swaps, V3 liquidity actions, and transfers.
atxswap-sdk on npm (source)~/.config/atxswap/keystore (fixed, not configurable)~/.config/atxswap/ (master.key + secrets.json)query.js), quotes, and arbitrary ERC20 token infoThis skill ships its own Node scripts and depends on atxswap-sdk.
SKILL.md is installed.npm install there before using any script.npm install fails, stop and report the dependency error instead of guessing.If the skill is installed via ClawHub or OpenClaw CLI, the install location is
typically ~/.clawhub/skills/atxswap/ (or the equivalent client-managed path).
If you cloned this repository directly, the location is skills/atxswap/.
Use the skill directory path to locate scripts. If ${SKILL_DIR} is available
(injected by skills.sh-compatible runtimes), use it; otherwise use the absolute
path to this skill's installed directory.
Example:
cd skills/atxswap && npm install
cd "${SKILL_DIR}" && node scripts/wallet.js list
All examples below use cd "${SKILL_DIR}" && for clarity. If your runtime does
not inject ${SKILL_DIR}, replace it with the absolute path of the installed
skill directory.
BSC_RPC_URL is optional and supports comma-separated values for fallback,
e.g. BSC_RPC_URL="https://primary,https://backup1,https://backup2". When
unset, scripts use a built-in fallback list of 6 BSC public RPC endpoints
and viem will retry them in order.~/.config/atxswap/keystore.~/.config/atxswap/ (master.key + secrets.json).wallet.js create fails.wallet.js list before creating a wallet.wallet.js export prints the address's
encrypted MetaMask-compatible keystore V3 JSON to stdout (or writes it
to a file via --out <file>); it never prints the raw private key.query.js quote can return a JSON error if the configured Quoter or RPC
rejects the simulation. Surface the error and do not proceed to a write.liquidity.js quote-add or use liquidity.js add --base-token ... --amount ...
so the script computes the counter-asset from the live pool price and range.When the user asks to create a wallet:
--password <pwd> to the script when running non-interactively.For swap, transfer, and liquidity operations, rely on auto-unlock first. Only ask for the password if auto-unlock fails.
If the user says they forgot the wallet password or asks to recover it, first
explain that saved wallet passwords are encrypted at rest in the local
SecretStore (for example Keychain, Secret Service, or the file backend under
~/.config/atxswap/) and are not stored by the agent in chat memory. Even if
the user confirms, do not print the password in chat; guide them to use a
trusted local workflow instead.
wallet.js export only emits the encrypted MetaMask-compatible keystore
JSON, never the raw private key. There is no command that prints the
unencrypted private key, and the agent must not attempt to derive or display
one.wallet.js export <address> --out <file> and tell the user the file
path. Avoid pasting the keystore JSON itself into chat unless the user
explicitly asks for it.force delete wallet before
running any delete command.wallet.js create succeeds, export and send the encrypted keystore
to the user who requested the wallet. Treat this as part of the wallet
creation handoff, but only to that user.wallet.js export <address> --out <file> as the only
supported backup path, because it exports an encrypted keystore instead of
exposing the raw private key.(asset, from, to, amount) as a single
transfer intent. Repeat that exact tuple back to the user before execution.txHash, consider that transfer intent
already sent. Do NOT send the same transfer again unless the user
explicitly asks to send it again.Before every write action:
For transfers, the summary in step 2 must explicitly include:
After step 5, if a txHash is available, treat the transfer as executed and do
not issue the same write again unless the user clearly requests a second send.
For read-only asset questions:
query.js balance, query.js positions,
query.js price, or query.js quote as appropriate).cd "${SKILL_DIR}" && node scripts/query.js price
cd "${SKILL_DIR}" && node scripts/query.js balance <address>
cd "${SKILL_DIR}" && node scripts/query.js positions <address>
cd "${SKILL_DIR}" && node scripts/query.js positions <address> <tokenId>
cd "${SKILL_DIR}" && node scripts/query.js quote <buy|sell> <amount>
cd "${SKILL_DIR}" && node scripts/swap.js buy <usdtAmount> [--from address] [--slippage bps] [--password <pwd>]
cd "${SKILL_DIR}" && node scripts/liquidity.js add <atxAmount> <usdtAmount> [range opts] [--from address] [--slippage-bps n] [--password <pwd>]
cd "${SKILL_DIR}" && node scripts/liquidity.js add --base-token <atx|usdt> --amount <n> [range opts] [--from address] [--slippage-bps n] [--password <pwd>]
cd "${SKILL_DIR}" && node scripts/transfer.js atx <to> <amount> [--from address] [--password <pwd>]
wallet.jscd "${SKILL_DIR}" && node scripts/wallet.js create [name] --password <pwd>
cd "${SKILL_DIR}" && node scripts/wallet.js list
cd "${SKILL_DIR}" && node scripts/wallet.js export <address> [--out <file>]
cd "${SKILL_DIR}" && node scripts/wallet.js has-password <address>
cd "${SKILL_DIR}" && node scripts/wallet.js forget-password <address>
cd "${SKILL_DIR}" && node scripts/wallet.js delete <address> --backup-confirmed yes --force-phrase "force delete wallet"
After wallet.js create:
wallet.js export <address> [--out <file>].Before wallet.js delete:
force delete wallet.wallet.js delete <address> --backup-confirmed yes --force-phrase "force delete wallet".If the user asks to back up the wallet:
wallet.js export <address> [--out <file>].query.jscd "${SKILL_DIR}" && node scripts/query.js price
cd "${SKILL_DIR}" && node scripts/query.js balance <address>
cd "${SKILL_DIR}" && node scripts/query.js quote <buy|sell> <amount>
cd "${SKILL_DIR}" && node scripts/query.js positions <address>
cd "${SKILL_DIR}" && node scripts/query.js positions <address> <tokenId>
cd "${SKILL_DIR}" && node scripts/query.js token-info <tokenAddress>
query.js positions includes principal token amounts in the position (principalAtx,
principalUsdt, principal0, principal1) computed from V3 liquidity (L), ticks (internal),
and the pool’s current sqrtPriceX96 (same getAmountsForLiquidity math as the web app). It emits
human USDT-per-ATX bounds as priceRangeUsdtPerAtx.min / .max, currentPriceUsdtPerAtx,
and currentPriceInRange (whether the pool tick lies inside that position). It includes
pendingFees.atx and pendingFees.usdt as the simulated collect notionals (collectable*),
plus raw tokensOwed0/1 and collectable0/1 for debugging. Use collectable* / pendingFees
to decide whether a fee harvest is worth executing. Each object also includes feePercent
(e.g. "0.25%") for the pool’s swap-fee tier; raw fee stays the on-chain code
(100 / 500 / 2500 / 10000). Tick indices are not included in the JSON — quote
USDT/ATX prices and in-range state instead.
When the user asks "how much do I have now", "what is my current balance", "what
positions are left", "how much ATX is still in the LP", or similar present-tense
questions, rerun query.js balance and/or query.js positions immediately.
Do not answer from previously captured JSON.
Required agent reply for holdings when the user asks about their positions, LP NFTs, or liquidity holdings (per position):
Run query.js positions <address> (omit tokenId to list all ATX/USDT V3 NFTs). The CLI prints
one JSON object per NFT; include every position. For each position, the answer must
address the topics below (label them in the user’s language when replying). Do not show raw
V3 tick numbers to the user — use USDT per 1 ATX from the JSON below.
| Topic | What to include | CLI JSON fields |
|---|---|---|
| Tokens in the position | In-range liquidity as ATX and USDT notionals — always cite principalAtx and principalUsdt (and optionally principal0 / principal1 in pool token0/token1 order). These are computed at the current pool price. Mention liquidity only as the raw L scalar if explaining detail. Do not treat query.js balance as LP “position tokens”: wallet ATX/USDT/BNB balances are unrelated to NFT principal — if shown, label them distinctly (e.g. “Wallet balances, separate from this LP NFT”). | |
| NFT token ID | The V3 LP NFT id | tokenId |
| Pool swap fee tier | The pool’s trading fee as a percentage for end users | Prefer feePercent (e.g. "0.25%"). If you show the raw tier code, pair it with feePercent (e.g. 2500 + 0.25%). |
| Price range & spot | Configured min/max USDT per 1 ATX for the position, and current pool price in the same unit | priceRangeUsdtPerAtx.min, priceRangeUsdtPerAtx.max, currentPriceUsdtPerAtx, currentPriceInRange (confirm “in range” / “out of range” in natural language). |
| Pending fees | Uncollected fees (both tokens) | Always show pendingFees.atx and pendingFees.usdt explicitly. Prefer these over quoting only one asset. Optionally reference collectableAtx/collectableUsdt synonyms. State fees stay pending until liquidity.js collect. |
Do not answer with only raw pool indices (ticks). If there are no positions, relay No ATX/USDT positions found. exactly.
Minor mismatch vs on-chain bookkeeping can occur because principal* uses the same float-based
tick→√P path as other tooling (~wei-level); values are intended for humans and routing, not audits.
swap.jscd "${SKILL_DIR}" && node scripts/swap.js buy <usdtAmount> [--from address] [--slippage bps] [--password <pwd>]
cd "${SKILL_DIR}" && node scripts/swap.js sell <atxAmount> [--from address] [--slippage bps] [--password <pwd>]
liquidity.jsadd — price / tick range (same USDT/ATX semantics as the web app; default full range = full-width liquidity):
--full-range: explicit full range (do not combine with the two groups below).--min-price / --max-price: band in USDT per 1 ATX; the script reads pool token0 and maps to tickLower / tickUpper like the app (token1/token0 + tickSpacing). Both prices are required.--range-percent: band around current ATX price as a percentage; e.g. 20 means about -20% to +20% of the current price.--tick-lower / --tick-upper: raw V3 ticks (both required; script uses the smaller as lower, larger as upper, clamped to valid V3 bounds).quote-add <atx|usdt> <amount>: given live price and range, estimate the other leg — use before a write.add --base-token <atx|usdt> --amount <n>: single-sided notional; script computes the other leg and executes the add.Optional: --slippage-bps (0–10000; default from SDK).
cd "${SKILL_DIR}" && node scripts/liquidity.js quote-add <atx|usdt> <amount> [range opts]
cd "${SKILL_DIR}" && node scripts/liquidity.js add <atxAmount> <usdtAmount> [range opts] [--from address] [--slippage-bps n] [--password <pwd>]
cd "${SKILL_DIR}" && node scripts/liquidity.js add --base-token <atx|usdt> --amount <n> [range opts] [--from address] [--slippage-bps n] [--password <pwd>]
cd "${SKILL_DIR}" && node scripts/liquidity.js remove <tokenId> <percent> [--from address] [--password <pwd>]
cd "${SKILL_DIR}" && node scripts/liquidity.js collect <tokenId> [--from address] [--password <pwd>]
cd "${SKILL_DIR}" && node scripts/liquidity.js burn <tokenId> [--from address] [--password <pwd>]
liquidity.js remove already uses a single multicall:
decreaseLiquidity -> collect -> and when percent = 100, burn.
That means a full removal automatically collects withdrawable funds and destroys the LP NFT in the same transaction, so
running collect again for the same tokenId is expected to fail because the position no longer exists.
Before collect, preview the target position with:
cd "${SKILL_DIR}" && node scripts/query.js positions <address> <tokenId>
Prefer pendingFees / collectableAtx / collectableUsdt over relying on tokensOwed0/1 alone when deciding whether
fees are available, because the raw tokensOwed fields may stay at zero while
collect() can still succeed.
Example (not full-range; align the range with the user using query.js price before writes):
cd "${SKILL_DIR}" && node scripts/liquidity.js quote-add usdt 0.1 --range-percent 20
cd "${SKILL_DIR}" && node scripts/liquidity.js add --base-token usdt --amount 0.1 --range-percent 20 --from <address>
cd "${SKILL_DIR}" && node scripts/liquidity.js add 10 1 --min-price 0.05 --max-price 0.15
cd "${SKILL_DIR}" && node scripts/liquidity.js add 10 1 --tick-lower -20000 --tick-upper 1000
Mapping user phrasing to commands:
quote-add usdt 0.1 --range-percent 20.estimatedAmounts (or equivalent summary) and wait for confirmation.add --base-token usdt --amount 0.1 --range-percent 20 --from <address>.transfer.jscd "${SKILL_DIR}" && node scripts/transfer.js bnb <to> <amount> [--from address] [--password <pwd>]
cd "${SKILL_DIR}" && node scripts/transfer.js atx <to> <amount> [--from address] [--password <pwd>]
cd "${SKILL_DIR}" && node scripts/transfer.js usdt <to> <amount> [--from address] [--password <pwd>]
cd "${SKILL_DIR}" && node scripts/transfer.js token <tokenAddress> <to> <amount> [--from address] [--password <pwd>]
Transfer anti-duplication checklist:
(asset, from, to, amount) to the user.transfer.js once.txHash.force delete walletnpm install has not been run successfully in the skill directoryFor any write action: