docs(dev4): R20 Task B — mainnet Uniswap swap runbook for Daniel - #460
Conversation
Single-doc runbook for one live mainnet ETH→USDC swap via the
SBO3L policy-guarded `sbo3l uniswap swap --broadcast` flow. The
captured tx hash becomes the live evidence row in the Uniswap
bounty submission.
Steps (Daniel runs):
STEP 0 pre-flight: verify wallet > 0.013 ETH + dry-run
quoter sanity (~$0)
STEP 1 --dry-run to inspect quote + signed receipt + decision
(~$0)
STEP 2 --broadcast with SBO3L_ALLOW_MAINNET_TX=1 (~$8-15 gas)
STEP 3 Etherscan + cast verify
STEP 4 paste tx hash to me; I run 5-min PR adding the row to
etherscan-link-pack.md + bounty narrative + memory
Cost ceiling: 0.005 ETH swap input + ~$8-15 gas = $25 total
budget. Daniel's wallet has 0.014 ETH (~$50) — sufficient for
Task B + STEP 1+2 of Task A's mainnet OR deploy.
Includes troubleshooting matrix (5 common symptoms + fixes) and
explicit irreversibility warning — no rollback for a confirmed
mainnet swap; dry-run is the only mitigation.
Why this matters: Uniswap track jumps from "Sepolia demo" to
"live mainnet activity at <tx-hash>." Pairs with upstream
Universal Router PR (Uniswap/universal-router#477) for the
"proposed the pattern + shipped live" narrative.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4a9181de1c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| --network mainnet \ | ||
| --amount-in 0.005ETH \ | ||
| --token-out USDC \ | ||
| --recipient "$DANIEL_WALLET" \ | ||
| --rpc-url "$MAINNET_RPC_URL" \ | ||
| --dry-run |
There was a problem hiding this comment.
Set mainnet gate before any
--network mainnet step
The pre-flight commands run sbo3l uniswap swap --network mainnet before exporting SBO3L_ALLOW_MAINNET_TX=1, but the CLI rejects all mainnet invocations (including dry-runs) without that env var. In cmd_uniswap_swap, the gate check runs before quoting/envelope generation, so Step 0/1 will fail immediately and the runbook cannot be followed as written.
Useful? React with 👍 / 👎.
| > /tmp/sbo3l-swap-envelope.json | ||
|
|
||
| # Inspect the envelope: | ||
| jq '.quote, .receipt.decision, .receipt.policy_hash' /tmp/sbo3l-swap-envelope.json |
There was a problem hiding this comment.
Emit real envelope JSON before running
jq checks
This step redirects stdout to a .json file and then queries .quote/.receipt.*, but sbo3l uniswap swap prints a human-readable summary to stdout and only writes JSON when --out is provided; additionally, the envelope schema uses top-level fields like amount_in_wei/amount_out_minimum and has no quote or receipt object. As written, the verification command fails (or returns nulls) and can let operators proceed to broadcast without validating the actual safety-critical envelope fields.
Useful? React with 👍 / 👎.
| | `eth_chainId mismatch` | RPC chain id doesn't match `--network mainnet` | Verify `MAINNET_RPC_URL` actually returns `1` (`cast chain-id`) | | ||
| | `insufficient funds for gas * price + value` | Wallet ETH < 0.005 + gas | Top up before retry; pre-flight 0.1 should have caught this | | ||
| | Tx pending > 60s | Gas price too low for current congestion | Wait or bump gas via Etherscan's "Speed Up" UI | | ||
| | Swap output < quote minimum | Slippage on confirm > slippage tolerance | Tx will revert atomically; no funds lost (only gas). Retry with higher `--max-slippage-bps` | |
There was a problem hiding this comment.
Replace unsupported slippage flag with the real CLI option
The troubleshooting guidance tells users to retry with --max-slippage-bps, but the Uniswap swap command exposes --slippage-bps (as documented by the clap args/tests). In a live incident this remediation path will fail at argument parsing, so users cannot apply the intended fix from this runbook.
Useful? React with 👍 / 👎.
…face (#474) Three real bugs in docs/dev4/mainnet-uniswap-swap-runbook.md caught by Codex on PR #460 (after merge). Each would fail Daniel mid-flow if he tried to run the runbook as written. Bug 1 (P1) — mainnet gate fires before quoter, even on --dry-run ================================================================= The CLI rejects EVERY `--network mainnet` invocation (including --dry-run quoter sanity calls) without `SBO3L_ALLOW_MAINNET_TX=1`. The gate check is at uniswap_swap.rs:581, BEFORE the quoter call at line 596+. So my STEP 0.2 + STEP 1 fail immediately at the gate. Fix: export `SBO3L_ALLOW_MAINNET_TX=1` at the top of STEP 0, before any --network mainnet invocation, with an explanatory comment about why it's needed even for dry-runs. Bug 2 (P1) — stdout is human-readable, JSON requires --out ========================================================== `sbo3l uniswap swap` prints a `key: value` summary to stdout via `print_envelope`. JSON output requires `--out <path>` flag (see uniswap_swap.rs:747-752). My runbook redirected stdout (`> /tmp/envelope.json`) and ran jq against it — would fail with "not valid JSON". Also the envelope is FLAT — top-level keys `amount_in_wei`, `expected_amount_out`, `amount_out_minimum`, `slippage_bps`, `quote_source`, `deadline_unix` (see SwapEnvelope struct at uniswap_swap.rs:516). My jq queries against `.quote.input_amount` / `.receipt.decision` / `.receipt.policy_hash` would all return null because no such nested objects exist. Fix: STEP 1 now uses `--out /tmp/sbo3l-swap-envelope.json` explicitly. jq queries against actual top-level fields. Also clarified that the dry-run envelope does NOT include a signed receipt or `decision` — those live at the policy-engine layer (separate flow); the envelope is pure swap calldata + quote metadata. Daniel's policy decision happens by inspecting the envelope and choosing whether to invoke `--broadcast`. Bug 3 (P2) — wrong slippage flag name ====================================== Troubleshooting matrix said to retry with `--max-slippage-bps`, but the CLI flag is `--slippage-bps` (see SwapArgs.slippage_bps at uniswap_swap.rs:106). User in the slippage-revert incident would hit "unknown argument" and be stuck. Fix: renamed to `--slippage-bps` + added concrete bump example ("default 50 = 0.5%; bump to 100 for 1%"). Two trailing prose references ("Token Transferred: USDC ≥ quote.output_amount_minimum", "increased by ≥ quote.output_amount_minimum") also fixed to use the actual field name `amount_out_minimum`. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Single-doc runbook for one live mainnet ETH→USDC swap via the SBO3L policy-guarded
sbo3l uniswap swap --broadcastflow. The captured tx hash becomes the live evidence row in the Uniswap bounty submission.Daniel-side workflow (~10 min)
--dry-runto inspect quote + receipt + decision--broadcastwithSBO3L_ALLOW_MAINNET_TX=1cast txverifyTotal budget: 0.005 ETH swap + ~$15 gas = $25. Wallet has 0.014 ETH (~$50). Sufficient.
Includes
Why this matters
Uniswap track jumps from "Sepolia demo" → "live mainnet activity at
<tx-hash>." Pairs with the upstream Universal Router PR for the "proposed the pattern + shipped live" narrative.🤖 Generated with Claude Code