Skip to content

Add ETH as a launch spoke (SOL<->ETH swaps) - #613

Open
anderdc wants to merge 2 commits into
testfrom
feat/eth-spoke
Open

Add ETH as a launch spoke (SOL<->ETH swaps)#613
anderdc wants to merge 2 commits into
testfrom
feat/eth-spoke

Conversation

@anderdc

@anderdc anderdc commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

What

Adds Ethereum as the third spoke: LAUNCH_SPOKES = ('btc', 'tao', 'eth'), so SOL↔ETH swaps launch with direction pools, miner quote flags, and CLI network config all derived from the registry. No contract changes — chains are opaque strings on-chain and leg amounts were already u128 (wei-scale).

Provider design (allways/chain_providers/ethereum.py)

  • Lean deps: eth-account + raw JSON-RPC over requests with a narrated failover ladder (publicnode → drpc defaults, ETH_RPC_URLS override) — same shape as the BTC provider's Esplora client, no web3.py.
  • Inclusion ≠ settlement: a mined tx must have receipt status == 1 (a reverted tx still decodes intact to/value). Mirrors the TAO provider's Balances.Transfer-event rule.
  • Unknown ≠ absent: a mined tx whose receipt can't be read raises ProviderUnreachableError instead of returning None — on a dest leg, None is a slash-eligible verdict.
  • Plain EOA value transfers only — internal/contract-mediated transfers don't populate a provable from/to/value and the sender-pin defense needs a provable EOA sender.
  • check_connection verifies eth_chainId against ETH_NETWORK, so a wrong-network RPC fails at startup instead of verifying the wrong chain.
  • Double-send guard: broadcasted_txids + an incremental head-cursor find_recent_outgoing (TAO-style; plain RPC has no address index).

Base change

New ChainProvider.normalize_address hook (identity default), used by verify_transaction's sender-mismatch and self-transfer checks. ETH overrides with lowercase: EIP-55 is display-only casing, and a checksummed reservation address vs a lowercase RPC sender must not false-reject a real deposit.

ChainDefinition choices

field value why
decimals 18 first spoke with more decimals than the hub; integer rate paths already handle negative decimal_diff (tests added)
min_confirmations 32 ~1 beacon epoch; post-merge reorgs deeper than 2 blocks are vanishingly rare
min_onchain_amount 5e13 wei no protocol dust floor; economic floor ≈ a few × a 21k-gas transfer fee
replay_grace_secs 0 block timestamps strictly increase (no BTC median-time lag)

Tests / verification

  • 41 new offline provider tests (RPC layer stubbed): revert rejection, receipt-unavailable → unreachable, pending/reorg/underpay/wrong-recipient, EIP-55 casing across verify_transaction, failover ladder ("null result is authoritative"), send guards, scanner cursor.
  • eth_utils 5+ is_address accepts unprefixed hex and stopped validating EIP-55 checksums — address validation is hand-rolled and tested.
  • Scoring tests now derive pool floors from len(LAUNCH_SPOKES) instead of a hardcoded 2-pair world; the unsupported-chain sentinel moved 'eth''doge'.
  • Full suite: 886 passed. Live read-only mainnet smoke: real transfer verified through verify_transaction with an uppercased recipient + pinned sender; chain-id check; balance read.

Notes for rollout

  • Miners quoting ETH need ETH_PRIVATE_KEY + a funded wallet; validators only need RPC reachability (public defaults work).
  • Third pair dilutes each pair's zero-volume emission floor from (1−α)/2 to (1−α)/3 of the miner pool — expected consequence of the volume-blend design.

anderdc added 2 commits August 3, 2026 11:52
- EthereumProvider: eth-account + raw JSON-RPC with endpoint failover
  (publicnode -> drpc defaults, ETH_RPC_URLS override); plain EOA value
  transfers only; receipt status==1 required (inclusion != settlement);
  a mined tx with an unreadable receipt raises ProviderUnreachableError
  (unknown != absent, never a false slash verdict)
- New ChainProvider.normalize_address hook (identity default) used by the
  base sender-pin and self-transfer checks; ETH lowercases so EIP-55
  display casing can never false-reject a real deposit
- CHAIN_ETH: 18 decimals, 32 confirmations (~1 epoch), 5e13 wei economic
  dust floor, no replay grace (timestamps strictly increase)
- eth-network CLI config (mainnet/sepolia) mirroring btc-network, wired
  into the env bundles; miner quote flags derive from LAUNCH_SPOKES
- Hand-rolled address validation: eth_utils 5+ is_address accepts
  unprefixed hex and no longer validates EIP-55 checksums
- Scoring tests now derive pool floors from len(LAUNCH_SPOKES); the
  unsupported-chain test sentinel moved from 'eth' to 'doge'
A mined tx's receipt and its block timestamp are immutable per block
hash, so fetch them once and key the cache by the blockHash the per-pass
getTransactionByHash already returns — hash change (reorg) misses and
refetches. Pending/absent/reverted reads are never cached. Brings the
ETH re-verify path to parity with BTC's single /tx call (~3x fewer ETH
RPCs at steady load); same shape as the shipped BTC/SOL tip hoists.
@anderdc

anderdc commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator Author

Added c6c34a9 — settled-tx read cache, prompted by the RPC budget review against RPC_OPTIMIZATION.md's 30-concurrent-swap average model:

The original implementation spent 3 RPCs per re-verify (getTransactionByHash + getTransactionReceipt + getBlockByNumber) every 12s pass, vs BTC's single /tx. But a mined tx's receipt and its block's timestamp are immutable per block hash — so they're now fetched once and cached keyed by the blockHash the per-pass tx fetch already returns. Hash change (reorg) = miss = full refetch; pending/absent/reverted reads are never cached (the unknown≠absent and settlement rules are untouched). Same pattern family as the shipped SOL/BTC tip hoists (#542#544).

Effect at the doc's average model (30 concurrent, all-ETH worst case): ~655k → ~225k req/day per validator; at the realistic 15-leg mix ~110k/day — inside dRPC's free keyed tier with 6× headroom. Covered by 4 new unit tests (call-count assertion, reorg invalidation, never-cache pending/reverted, bound). 890 tests green.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant