| name | consolidate-stables |
| description | Sell supported stablecoin balances into canonical USDC on one chain with the Uniswap Trading API. Use when Codex needs to consolidate DAI, USDT, USDC.e, USDbC, and similar curated non-canonical USD stables into canonical USDC, load ETH_PRIVATE_KEY plus ADDRESS from the repo-root dotenvx `.env`, require UNISWAP_API_KEY, resolve chain metadata through `$evm-chains`, prepare Trading API approvals, quotes, and swaps, sign Permit2 typed data with cast when required, and broadcast only after explicit user confirmation. The user must specify the source chain. |
Consolidate Stables
Overview
Sell every supported non-zero stablecoin balance on one chain into that chain's canonical USDC balance. This skill is intentionally conservative:
- only operate on chains listed in references/stablecoin-allowlist.json
- only operate on allowlisted tokens for that chain
- treat the Trading API quote result as the source of truth for whether the swap is actually executable on Uniswap
- skip canonical USDC itself
Use $evm-chains to resolve chain name, chain ID, default public RPC, native currency symbol, and RouteMesh support before any onchain step. RouteMesh routing and ROUTEMESH_API_KEY sourcing are delegated to the globally installed $evm-chains skill; if that skill is unavailable, tell the user to install it with npx skills add evm-chains before proceeding. Use cli-cast only for the cast commands. Read references/uniswap-trading-api.md before calling the Uniswap Trading API. Use scripts/uniswap_swap_utils.py for routing-aware /swap request preparation and pre-broadcast swap validation.
Workflow
1. Load the signing and API environment
Load ETH_PRIVATE_KEY, ADDRESS, UNISWAP_API_KEY, and any other repo-local secrets from the repo-root dotenvx .env or current shell. Run secret-dependent commands through a dotenvx-run subshell, such as dotenvx run --quiet -- sh -c '...'; do not source .env directly because it is encrypted.
If ADDRESS is missing, empty, or otherwise unusable, derive it from ETH_PRIVATE_KEY with cast wallet address and continue. Treat ETH_PRIVATE_KEY as the signing source of truth.
Require both:
ETH_PRIVATE_KEY before any signing step
UNISWAP_API_KEY before any Trading API request
Resolve the selected source chain through $evm-chains before any RPC call. Use the RPC URL returned by $evm-chains; that skill is responsible for using RouteMesh when the chain supports it and its global ROUTEMESH_API_KEY is available, otherwise it falls back to public RPCs.
2. Load the chain allowlist entry
Open references/stablecoin-allowlist.json and resolve the selected chain id.
For the selected chain, extract:
- chain name
- canonical USDC token address and decimals
- the curated
sell_into_canonical_usdc list
If the chain is absent from the allowlist, stop and report that consolidate-stables does not support that chain yet.
3. Discover balances
For each allowlisted token on that chain:
- read
balanceOf(ADDRESS) with cast call
- keep the token's configured decimals from the allowlist
- skip zero balances
Skip the canonical USDC token even if its balance is non-zero. This skill consolidates into canonical USDC; it never sells canonical USDC itself.
If every allowlisted non-canonical stable balance is zero, report that there is nothing to consolidate on that chain and stop.
4. Prepare one swap plan per non-zero token
For each non-zero allowlisted token:
- call
POST /check_approval
- call
POST /quote
- prepare the routing-aware
/swap request body with scripts/uniswap_swap_utils.py
- call
POST /swap
- validate the swap response with the helper before any broadcast
Use these quote defaults unless the user explicitly overrides them:
type=EXACT_INPUT
amount=<full raw token balance>
tokenInChainId=<SOURCE_CHAIN_ID as string>
tokenOutChainId=<SOURCE_CHAIN_ID as string>
tokenOut=<canonical USDC address>
routingPreference=BEST_PRICE
autoSlippage=true
urgency=normal
Treat each candidate independently. Do not batch multiple stablecoins into one Trading API request.
5. Handle approvals and Permit2 correctly
Use /check_approval as the approval source of truth.
If /check_approval returns approval = null, there is no prerequisite approval transaction for that token.
If /check_approval returns an approval object:
- inspect the
approval.to spender
- if it points at the Universal Router, prefer the legacy direct approval path
- if the route or quote requires Permit2, follow the Permit2 path
Legacy direct approval path:
- submit the ERC-20
approve(spender, amount) transaction only when the existing allowance is insufficient
- no typed-data signature is required for the swap body
Permit2 path:
- the quote response includes
permitData
- write
permitData to a temporary JSON file
- sign that file with
cast wallet sign --data --from-file
- pass the resulting signature to the helper's
prepare-request command
Do not send permitData: null to /swap. Do not wrap the quote response inside { "quote": ... }.
6. Build the execution plan table
After every quote and swap-response validation step, build one Markdown table with these columns:
- Token
- Raw Input
- Human Input
- Route Type
- Approval Path
- Approval Needed
- Expected USDC Out
- Status
Render Status as:
ready when /quote, /swap, and swap-response validation all succeeded
blocked when any prerequisite or API step failed
If all rows are blocked, stop before any confirmation prompt.
If only ready rows remain, ask once for explicit confirmation before any approval or swap broadcast.
7. Broadcast sequentially after confirmation
After explicit confirmation:
- for each ready row, submit the approval transaction first when needed
- confirm the approval receipt with
cast receipt
- submit the prepared swap transaction from the Trading API response with
cast send
- confirm the swap receipt with
cast receipt
- stop on the first approval failure, swap failure, or failed receipt
- report which tokens already succeeded and which ones remain unsold
Before each cast send, validate the Trading API response with:
python3 scripts/uniswap_swap_utils.py validate-response --response-file "$SWAP_RESPONSE_JSON"
8. Report the no-op case clearly
If the only non-zero stable balance on the selected chain is canonical USDC, report that there is nothing to consolidate because the wallet already holds canonical USDC and no other allowlisted stablecoin inventory.
Guardrails
- Do not operate on chains missing from the local allowlist.
- Do not sell canonical USDC.
- Do not include tokens outside the chain's allowlisted stablecoin set.
- Do not hardcode API keys in commands or files. Read
UNISWAP_API_KEY from the environment.
- Do not send
permitData: null to /swap.
- Do not wrap the quote response inside
{ "quote": ... }.
- Do not send
permitData to /swap for UniswapX routes such as DUTCH_V2, DUTCH_V3, or PRIORITY.
- Do not broadcast approvals or swaps until the user explicitly confirms the ready plan.
- Do not continue after the first broadcast failure. Report partial completion and stop.
Minimum Inputs
Before executing, make sure you have: