Skip to content

Repository files navigation

Eco Routes E2E Testing System

End-to-end testing framework for the Eco Routes intent lifecycle on mainnet chains across EVM and Solana networks.

Test Status (100% Pass Rate)

From / To OPT BASE ARB POL INK SOL ETH RON
Optimism - P P P P P P -
Base P - P P P P P P
Arbitrum P P - P P P P -
Polygon P P P - P P P -
Ink P P P P - P P -
Solana P P P P P - P -
Ethereum P P P P P P - P
Ronin - P - - - - P -

P = Pass, F = Fail, - = Not applicable. 46/46 routes passing.

Test Categories

Category Passed Failed Total
Core EVM (5 chains) 20 0 20
Solana to/from EVM 10 0 10
Ethereum to/from EVM 10 0 10
Ethereum to/from Solana 2 0 2
Ethereum to/from Ronin 2 0 2
Base to/from Ronin 2 0 2

About this project

Independent, open-source personal project built to explore cross-chain intent infrastructure using the publicly documented Eco Routes protocol. Not affiliated with, commissioned by, or contracted through Eco — this is self-directed work released under the MIT license.

Overview

This system tests the complete intent lifecycle:

PUBLISHED → FUNDED → FULFILLED → PROVEN → WITHDRAWN

Using real mainnet infrastructure with public solvers - no local solver required.

Quote Providers

The system supports two quote provider modes:

1. Pre-Production API (Default)

QUOTE_PROVIDER=api
QUOTES_API_URL=https://quotes-preprod.eco.com
  • Aggregates quotes from multiple solvers
  • Supports all EVM and Solana routes
  • Best for testing route availability

2. Direct Solver API

QUOTE_PROVIDER=solver
SOLVER_URL=https://solver-magenta.eco.com
  • Connects directly to a specific solver
  • Portal/prover addresses come from solver response
  • Best for testing specific solver behavior

Balance Verification

Every test run verifies that on-chain balances change by the expected amounts. The verification is provider-aware and adapts its strategy depending on the quote mode.

How It Works

STEP 1    Get quote
STEP 1.5  Snapshot user (+ solver in solver mode) balances
STEP 2    Publish and fund intent
STEP 3    Wait for fulfillment on destination chain
STEP 3.5  Verify user received exact destinationAmount (STRICT)
STEP 4    Wait for proof on source chain
STEP 5    Wait for solver withdrawal
STEP 5.5  Verify solver received sourceAmount on source chain

Verification Rules

Assertion Solver mode API mode
User received destinationAmount Strict (0%) Strict (0%)
Solver identity known upfront Yes No
Solver "before" snapshot Yes No
Solver reward delta verified Strict (0%) Informational only
Solver address logged Yes Yes (from claimant)
IntentWithdrawn event verified Yes Yes
  • User verification: Must match the quote's destinationAmount exactly. Any deviation, even 1 wei, fails the test.
  • Solver verification (solver mode): Before/after delta must match the sourceAmount. The solver wallet is known from the /api/v1/blockchain/chains endpoint.
  • Solver verification (API mode): The solver is unknown until fulfillment. The address is discovered from the IntentFulfilled event's claimant field, and a post-withdrawal balance is logged for traceability (no assertion, since there is no "before" snapshot).

Technical Details

  • All balance calculations use BigInt to avoid floating-point precision issues.
  • formatUnits is only used for display, never for comparisons.
  • RPC calls use exponential backoff retry (1s, 2s, 4s) to handle 429 rate limits.
  • Solana balance queries return BigInt(0) for non-existent ATAs instead of throwing.
  • The claimant field in IntentFulfilled is encoded as bytes32; the service extracts the standard 20-byte address from the zero-padded value.

Architecture

┌──────────────────────────────────────────────────────────────────────┐
│                          E2E Test Suite                              │
├──────────────────────────────────────────────────────────────────────┤
│  tests/e2e/                                                          │
│  └── universal.e2e.test.ts     Parameterized test runner             │
│  tests/helpers/                                                      │
│  └── balance-assertions.helper Snapshot + verify balance deltas      │
├──────────────────────────────────────────────────────────────────────┤
│                           Core Services                              │
├──────────────────────────────────────────────────────────────────────┤
│  QuoteService        Get quotes from API or direct solver            │
│  IntentPublisher     Publish & fund intents on-chain                 │
│  EventMonitor        Watch for lifecycle events                      │
│  SvmPublisher        Solana-specific intent publishing               │
│  SolverConfigService Dynamic chain/token config from solver API      │
│  BalanceService      ERC20 + SPL token balance queries with retry    │
│  RouteDiscoveryService  Discover valid routes via quote probing      │
├──────────────────────────────────────────────────────────────────────┤
│                        Blockchain Adapters                            │
├──────────────────────────────────────────────────────────────────────┤
│  EVM: Optimism, Base, Arbitrum, Polygon, Ink, Ethereum, Ronin        │
│  SVM: Solana                                                         │
└──────────────────────────────────────────────────────────────────────┘

Supported Chains

Chain ID Type Token Primary Token Address
Optimism 10 EVM USDC 0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85
Base 8453 EVM USDC 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913
Arbitrum One 42161 EVM USDC 0xaf88d065e77c8cC2239327C5EDb3A432268e5831
Polygon 137 EVM USDC 0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359
Ink 57073 EVM USDG 0xe343167631d89B6Ffc58B88d6b7fB0228795491D
Ethereum 1 EVM USDC 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
Ronin 2020 EVM USDC 0x0B7007c13325C48911F73A2daD5FA5dCBf808aDc
Solana 1399811149 SVM USDC EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v

Prerequisites

  • Node.js 18+
  • Funded wallet with:
    • Native tokens for gas on each chain
    • USDC/stablecoins for testing (~$1-5 per chain)
  • For Solana routes: routes-cli installed

Installation

npm install

Configuration

Copy .env.example to .env and configure:

# Required: EVM wallet
PRIVATE_KEY=0x...
WALLET_ADDRESS=0x...

# Required for Solana routes
SVM_PRIVATE_KEY=...  # base58 format
SOLANA_WALLET=...
SOLANA_RECIPIENT=...

# Quote provider configuration
QUOTE_PROVIDER=solver  # 'api' or 'solver'
QUOTES_API_URL=https://quotes-preprod.eco.com
SOLVER_URL=https://solver-magenta.eco.com

# Chain enable flags
ENABLE_POLYGON=true
ENABLE_INK=true
ENABLE_ETHEREUM=false  # Disabled by default
ENABLE_RONIN=false     # Disabled by default (CCIP issues)

# Optional: Custom RPC URLs
OPTIMISM_RPC=https://optimism.drpc.org
BASE_RPC=https://mainnet.base.org
ARBITRUM_RPC=https://arb1.arbitrum.io/rpc

Running Tests

Single Route Test (Recommended)

Use the universal test runner to test any specific route:

npm run test:route <source> <destination>

Examples:

npm run test:route base eth       # Base → Ethereum
npm run test:route sol arb        # Solana → Arbitrum
npm run test:route eth ron        # Ethereum → Ronin
npm run test:route pol ink        # Polygon → Ink

Valid chain aliases:

  • opt - Optimism
  • base - Base
  • arb - Arbitrum
  • pol - Polygon
  • ink - Ink
  • sol - Solana
  • eth - Ethereum
  • ron - Ronin

Validation: If you mistype a chain name, you'll get a helpful error:

npm run test:route basse eth
# Error: Invalid source chain "basse"
# Valid options: opt, base, arb, sol, pol, ink, eth, ron

Run All Tests

Run all valid route tests sequentially (to avoid nonce collisions):

npm run test:all           # Run all 46 valid routes
npm run test:all:safe      # Skip Ronin-as-source routes (44 routes)

Route Restrictions

Ronin routes are restricted:

  • Ronin can only route to/from: eth, base
  • Other Ronin combinations (e.g., ron opt) will error with an explanation

Invalid route example:

npm run test:route ron arb
# Error: Ronin can only route to: eth, base. Got: arb

Intent Lifecycle

Standard Flow (HyperProver)

1. Get Quote      → Portal/Prover addresses from API
2. Publish Intent → Portal.publish(encodedRoute)
3. Fund Intent    → Portal.fund() or publishAndFund()
4. Fulfilled      → Solver fulfills on destination chain
5. Proven         → HyperProver submits proof on source chain
6. Withdrawn      → Solver withdraws reward

CCIP Flow (Ronin routes)

1. Get Quote      → Portal/Prover addresses from API
2. Publish Intent → Portal.publish(encodedRoute)
3. Fund Intent    → Portal.fund() or publishAndFund()
4. Fulfilled      → Solver fulfills on destination chain
5. Withdrawn      → CCIP proves automatically, solver withdraws

Note: Ronin routes use Chainlink CCIP for cross-chain proving instead of HyperProver.

Portal Events

Event Topic Hash
IntentPublished 0x43974895be1bcec7344337863fa7de24a0d1c315c0a994f663fe0ee220ddc8e4
IntentFunded 0xc1ed05721d27ad6b2555d61388ac393b120f5cc0e6009e53230e02c68e60064a
IntentFulfilled 0xc471de166a60c0b81727dfa2f57d4fc3ad1b45b057c1f034b7058365613bde8d
IntentProven 0xe6d8040a8a6bc519f4e5a42fb2677067c929ddbf2cca9287a44b23fb617a6f00
IntentWithdrawn 0xbb062c23e818de8ea9c157514eb098052cf36904bbe431cd50d4ec92264ca3ac

Test Reports

Reports are automatically generated in ./reports/ after each test run:

  • e2e-report-{timestamp}.json - Detailed JSON report with full lifecycle data
  • e2e-report-{timestamp}.md - Human-readable Markdown report
  • jest-report.html - Interactive HTML report (generated by Jest)

Report Contents:

  • Test summary (total, successful, failed, partial)
  • Success rate and average durations
  • Phase-by-phase timings (publish → fund → fulfill → prove → withdraw)
  • Individual intent details with transaction hashes
  • Error messages and failure points

Viewing Reports:

# After running tests, check the reports directory
ls -lt reports/

# View the latest Markdown report
cat reports/e2e-report-*.md | tail -100

Reports are automatically cleaned up after generation - temporary data is not persisted between runs.

Timeouts

  • Single intent test: 10 minutes
  • Parallel tests: 15 minutes
  • Solana routes: Extended timeout for cross-chain bridge delays

Troubleshooting

"Invalid EVM address" Error

The solver API requires lowercase EVM addresses. The system automatically normalizes addresses, but if you see this error, verify your .env addresses are correct.

"No quotes available"

  • Check wallet has sufficient token balance
  • Verify token addresses are correct
  • Minimum amount is typically 0.05 USDC (50000 in 6 decimals)

Ronin Routes

Ronin uses the CCIP prover instead of HyperProver. All four Ronin routes (ETH→RON, BASE→RON, RON→ETH, RON→BASE) pass. CCIP proving can take longer than HyperProver, so these routes have extended timeouts.

Solana Routes Timing Out

  • Ensure routes-cli is properly configured
  • Check Solana RPC connectivity
  • Verify SPL token accounts are initialized

Development

# Type check
npm run typecheck

# Lint
npm run lint

# Build
npm run build

Links

License

MIT

About

E2E testing framework for the Eco Routes cross-chain intent lifecycle on mainnet — EVM and Solana, 8 chains, 46 routes.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages