Repository navigation
Conversation
Adds a two-phase multi-chain treasury skill: drain every token above a floor from one wallet to another via raw transfers (preserving asset identity and cost basis), then liquidate a wallet's whole portfolio into USDC on a single destination chain. Both structural gas bugs found in real runs are fixed rather than shipped: Stage A now reserves gas for the stage-B bridge that follows it. Each swap spends the chain's native token and stage B needs one more transaction to bridge the proceeds out, so sweeping until gas runs out converts sellable tokens into an unsendable pile of USDC/WETH -- worse than not sweeping, since the original assets are gone too. gas.affordable_swaps() caps each chain's sweep at what it can fund while still paying for the bridge, defers surplus rows lowest-value-first, and skips chains that cannot fund even one bridge. Calibrated on an observed ~$0.27/swap: 44 swaps on robinhood ate $12 of ETH and stranded $1,893 of WETH behind them. Gas reserves are now USD-aware. A native-unit reserve is worth cents on cheap chains (1 POL ~ $0.20, 1 S ~ $0.03), so a chain would pass the flat "$5 of balance" test, bridge nearly everything home, and be left unable to broadcast -- stranding its residue until a manual top-up. reserve_for(chain, price) keeps the larger of the native figure and MIN_RESERVE_USD, and stage C applies MIN_BRIDGE_USD to the amount actually leaving rather than the pre-reserve balance. drain.py takes the same fix. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…h deposits Field-tested on the first live run of the gas-aware sweep: a chain was deliberately topped up with native gas, the bridge reported delivered, and eth_getBalance confirmed 132 POL on-chain -- while the positions API still returned $0.00 native, so the affordability check false-skipped the chain. The same lag undersized a bridge read from positions right after a sweep ($3 carried instead of $100). Skip message and gotchas now say to confirm a zero reading via RPC before trusting it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a two-phase multi-chain treasury skill:
They compose (drain A→B, then liquidate B) or run independently.
Why this isn't just a copy of a working script
Two structural gas bugs showed up across real multi-chain runs. Both are fixed here rather than shipped:
Stage A now reserves gas for the stage-B bridge that follows it
Every swap spends the chain's native token, and stage B then needs one more transaction to bridge the proceeds out. Sweeping a chain until its gas runs out converts sellable tokens into an unsendable pile of USDC/WETH — strictly worse than not sweeping, because the original assets are gone too. The bridge fails
not_enough_base_asset_balanceand the value sits there.gas.affordable_swaps()caps each chain's sweep at what it can fund while still paying for the bridge, defers surplus rows lowest-value-first (with a log line, never silently), and skips chains that cannot fund even one bridge — with a top-up hint instead of a confusing downstream failure.Observed twice:
robinhoodspent $12 of ETH (~$0.27 each) and left $1,893 of WETH unbridgeablepolygonsweep attempted 3 swaps while holding $0.00 of POLGas reserves are now USD-aware
A native-unit reserve is worth cents on cheap chains — 1 POL ≈ $0.20, 1 XDAI ≈ $1.00, 1 S ≈ $0.03. Combined with a flat "skip if balance < $5" test, a chain passes the check, bridges nearly everything home, and is left holding a reserve too small to broadcast anything. Its residue is then stranded until someone tops it up by hand.
reserve_for(chain, price)keeps the larger of the per-chain native figure andMIN_RESERVE_USD($2.50). Stage C appliesMIN_BRIDGE_USDto the amount actually leaving (balance minus reserve) rather than the pre-reserve balance. Stage D's native swap anddrain.pytake the same fix.The cost of both guards is a few dollars of gas retained per chain. That's the intended trade: a chain with gas can be retried, a chain without gas cannot.
Calibration note
DEFAULT_SWAP_GAS_USDis rounded up from the observed ~$0.27/swap. An earlier $0.12 estimate would have permitted 96 swaps on the $12 robinhood balance and reproduced the exact failure the cap exists to prevent — so the constant is deliberately conservative. Overestimating leaves a little dust; underestimating strands a whole chain's proceeds.Contents
SKILL.mdreferences/gotchas.mdscripts/preflight.pyscripts/inventory.pyscripts/drain.pyscripts/liquidate.pyscripts/verify.pyscripts/gas.pyTesting
python3 -m py_compile)affordable_swaps()regression-checked against both real failures: robinhood $12 → caps at 38 swaps (below the 44 that broke it); polygon $0.00 → 0, chain skipped up frontScripts are stdlib-only (no new dependencies) and contain no addresses or credentials beyond the public USDC mainnet contract in
verify.py.🤖 Generated with Claude Code