feat(chains): add Robinhood Chain and Unichain networks; enable Uniswap v4 routing#665
feat(chains): add Robinhood Chain and Unichain networks; enable Uniswap v4 routing#665fengtality wants to merge 8 commits into
Conversation
…pport Add Robinhood Chain (chainID 4663) and Robinhood Chain Testnet (chainID 46630) as Ethereum networks: - Chain templates with EIP-1559 gas config (Arbitrum Orbit L2, ETH gas) - Mainnet token list with 27 tokens (WETH, USDG, and Robinhood stock/ETF tokens), symbols and decimals verified on-chain - Wrapped native (WETH) mapping and EIP-1559 gating for both networks Wire up Uniswap on robinhood-chain (router, AMM, CLMM): - Add official V2/V3/UniversalRouter deployment addresses - Resolve the Universal Router address from the per-network contracts map instead of the SDK's hardcoded chain map, which throws for chains it doesn't know (e.g. 4663) and has no override mechanism - Fall back to the Universal Router quote in /router/quote-swap when the AlphaRouter SDK doesn't support the network Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Uniswap instance mock was missing the new isAlphaRouterAvailable method, so quoteSwap threw a TypeError and returned 500. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add Unichain (chainID 130, OP-stack L2) as an Ethereum network: - Chain template with EIP-1559 gas config and official public RPC - Token list with 11 major tokens from the Uniswap default token list - Wrapped native (WETH) mapping and EIP-1559 gating - Official Uniswap V2/V3/V4/UniversalRouter deployment addresses Enable Uniswap v4 in /router quotes and swaps: - Include Protocol.V4 in AlphaRouter routing on chains in the SDK's V4_SUPPORTED list; v4 swaps execute through the existing Universal Router + Permit2 flow with no API changes - Override the SDK's on-chain quote provider on Unichain with smaller multicall batches (10x1.2M gas vs the default 80x1.2M), which public Unichain RPCs reject with intermittent ProviderGasError Verified live: Unichain WETH->USDC quotes 3/3 via the official RPC, and mainnet WETH->USDC now returns V3+V4 split routes when v4 wins on price. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t to never Permit2 grants from /approve were hardcoded to expire after 48 hours, so every uniswap/router pair silently required re-approval every 2 days -- no other connector has expiring approvals. Add optional ethereum.permit2ExpirationSeconds chain config; when unset, grants never expire (max uint48), matching plain ERC20 approval semantics on other connectors. Permit2 allowances remain uint160-scoped and revocable, so this keeps its safety advantages without the churn. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Rename the network identifier (and testnet) everywhere: template filenames, config namespaces, token lists, EIP-1559 gates, WETH map, and the Uniswap networks allow-list. The geckoId values and external docs URLs keep their original hyphenated forms since those identifiers are owned by CoinGecko and Uniswap. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Tested against both new chains — nice work. One inconsistency worth a look:
// src/chains/ethereum/routes/estimate-gas.ts
network === 'base' ||
network === 'robinhood-chain' ||
network === 'robinhood-chain-testnet' ||
network === 'unichain';Everything else I checked came back clean:
( |
…red list The EIP-1559 network list was duplicated in three places (twice in ethereum.ts, once in estimate-gas.ts) and the copies drifted: unichain was added to ethereum.ts but not estimate-gas.ts, so GET /chains/ethereum/estimate-gas?network=unichain reported gasType 'legacy'. Extract a single exported EIP1559_NETWORKS constant and use it at all three sites. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The route read lastGasPriceEstimate.isEIP1559 directly, but the cache became keyed by network, so the property was always undefined and the route never returned gasType 'eip1559' for any network. Expose a public getCachedGasPriceEstimate() accessor on Ethereum (replacing the (ethereum as any).constructor reach into a private static) and index the cache correctly. EIP-1559 networks whose estimate fell back to legacy pricing now report gasType 'legacy' instead of omitting it. Adds route tests covering eip1559 output for mainnet/unichain/ robinhoodchain, legacy output for bsc, and the fallback case. The test automock is patched to keep the real EIP1559_NETWORKS constant. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… V3 via QuoterV2 Two fixes to the Universal Router fallback path (used on networks the AlphaRouter SDK doesn't support, e.g. robinhoodchain): - quoteSwap now passes the requested slippagePct through getUniversalRouterQuote, so the min-out/max-in embedded in the swap calldata matches the minAmountOut/maxAmountIn advertised in the quote response. Previously the calldata always used the config slippage while the response reflected the caller's, so executions could revert inside the accepted tolerance or fill below the promised minimum. - findV3Route now quotes every fee tier against the on-chain QuoterV2 and keeps the best one, and getQuote picks the best route across V3/V2 by quoted amount. Previously it simulated locally on fabricated tick data (liquidityNet=0 across +/-300 tick spacings), returned the first fee tier with any liquidity (a dust pool could beat a deep one), and always preferred V3 over V2 regardless of price. Removes tick-data-provider.test.js, which exercised the SDK's tick machinery in support of the fabricated-tick approach. Adds route-level tests for the fallback branch, pinning slippage threading and response mapping. Live-verified on robinhoodchain: SELL 0.1 WETH -> 175.4 USDG and BUY exact-out cross-consistent; decoded calldata min-out tracks requested slippage (1% -> 0.99%, 5% -> 4.76% implied, the SDK's quoted/(1+slip) convention). Unichain AlphaRouter path unaffected. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
pls merge x) |
Summary
Adds Robinhood Chain (docs) as an Ethereum network —
robinhoodchain(chainID4663) androbinhoodchain-testnet(chainID46630) — and wires up Uniswap (router, AMM, CLMM) on mainnet using the official deployment addresses.Also adds Unichain (chainID
130, Uniswap Labs' OP-stack L2), enables Uniswap v4 in/routerquotes and swaps on all chains where the SDK supports it, and makes the Permit2 allowance expiration configurable (default: never expires).New networks
robinhoodchain.yml/robinhoodchain-testnet.ymlwith EIP-1559 gas config (Arbitrum Orbit L2, ETH as native gas token — both RPCs verified to returnbaseFeePerGaswith 0 priority fee)unichain.yml(EIP-1559, official public RPCmainnet.unichain.org)root.yml, the fallback network list, the wrapped-native (WETH) map, and the EIP-1559 gates inethereum.ts/estimate-gas.tsUSO, so the on-chain symbol is used. Testnet token list is empty — no token contracts are documented or deployed there.robinhoodchain), butgeckoIdkeeps CoinGecko's hyphenatedrobinhood-chainidentifierUniswap wiring
robinhoodchainandunichainadded to the Uniswap networks allow-list; official V2, V3 / Universal Router, and (Unichain) V4 deployment addresses added touniswap.contracts.tsuniversal-router.tsnow resolves the Universal Router address from the per-network contracts map instead ofUNIVERSAL_ROUTER_ADDRESS()from@uniswap/universal-router-sdk, which throws for chains not in its hardcoded map (e.g. 4663) and has no override API.SwapRouter.swapCallParametersitself is chain-agnostic (calldata contains only token paths), so only the address lookup needed to change./router/quote-swapfalls back to the Universal Router quote when the AlphaRouter SDK doesn't support the network (@uniswap/smart-order-routeralso hardcodes supported chains). The cached quote shape (trade+methodParameters) is identical, soexecute-quote/execute-swapwork unchanged.Uniswap v4 routing
alpha-router.tsnow includesProtocol.V4in AlphaRouter routing on chains in the SDK'sV4_SUPPORTEDlist (mainnet, arbitrum, optimism, polygon, base, bsc, avalanche, celo, unichain, …); other chains keep V2+V3 only, since the V4 quoter address lookup would throw thereexecute-quotesends the SDK'smethodParameterscalldata as before. The router simply considers v4 pools and picks whichever route prices best.eth_call), which the public Unichain RPCs reject with intermittentProviderGasError.alpha-router.tsoverrides it there with 10-call batches (~12M gas), which succeeds consistently.Permit2 allowance expiration
/approve(used byuniswap/router) were hardcoded to expire after 48 hours, forcing bots to re-approve every uniswap/router pair every 2 days — no other connector has expiring approvalsethereum.permit2ExpirationSecondschain config; when unset, grants never expire (max uint48), matching plain ERC20 approval semantics elsewhere. Permit2 allowances remain uint160-scoped and revocable.Verified live
Robinhood Chain mainnet (4663):
/chains/ethereum/status,/tokens,/estimate-gas(EIP-1559 path) ✅pool-infoon the live WETH/USDG 0.05% pool (~$550k liquidity) ✅pool-infoon the live WETH/USDG V2 pair ✅Unichain (130) + v4:
methodParameters.to= Universal Router, no tx sent) ✅Notes for existing installs
conf/root.ymlfiles do not pick up new template namespaces automatically — runpnpm run setup(or add theethereum-robinhoodchain*/ethereum-unichainnamespaces manually) to enable the networks/pools/finddoes not work for Robinhood Chain because GeckoTerminal does not index it; use an explicitpoolAddressestimate-gasfixes (post-review)Follow-ups from review of this branch (
072aea677,646192f8c):ethereum.ts, once inestimate-gas.ts) and had drifted. Extracted a single exportedEIP1559_NETWORKSconstant used at all three sites.main): the route never returnedgasType: 'eip1559'for any network — it readlastGasPriceEstimate.isEIP1559as a flat object, but the cache is keyed by network, so the check was always undefined. Added a publicgetCachedGasPriceEstimate()accessor (replacing the(ethereum as any).constructorreach into a private static) and fixed the lookup. EIP-1559 networks whose estimate fell back to legacy pricing now reportgasType: 'legacy'instead of omitting the field.estimate-gasreturnseip1559with realmaxFeePerGas/maxPriorityFeePerGason mainnet, unichain, and robinhoodchain; new route tests cover eip1559/legacy/fallback cases (146 tests passing).Universal Router fallback quote fixes (post-review)
From a full-branch code review (
6ff69289d), two fixes to the fallback quote path used where the AlphaRouter SDK is unavailable (robinhoodchain):slippagePctnow reaches the swap calldata. PreviouslygetUniversalRouterQuotealways embedded the config slippage in the Universal Router calldata while the response'sminAmountOut/maxAmountInreflected the caller's requested value — executions could revert inside the accepted tolerance or fill below the promised minimum. The parameter now threads through exactly like the AlphaRouter path.findV3Routepreviously simulated on synthetic ticks (liquidityNet: 0across ±300 spacings), returned the first fee tier with any liquidity (a dust pool could beat a deep one), andgetQuotealways preferred V3 over V2. It now quotes every fee tier via QuoterV2, keeps the best, and selects the best route across V3/V2 by quoted amount.tick-data-provider.test.js(tested the SDK tick machinery supporting the fabricated-tick approach); added route-level tests for the fallback branch pinning slippage threading and response mapping.Test plan
pnpm build,pnpm typecheck,pnpm lint(0 errors; only pre-existing warnings)test/chains+test/connectorssuites pass (146 tests across uniswap + ethereum suites, including new Permit2 expiration and estimate-gas tests)🤖 Generated with Claude Code