Skip to main content

Quickstart

This guide will help you set up your development environment and create your first cross-chain intent with Eco Routes.

Prerequisites

Before you begin, ensure you have the following installed:
1

Install Node.js

Install Node Version Manager (nvm) and Node.js v18.20.3:
# Install nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash

# Install and use Node.js v18.20.3
nvm install v18.20.3
nvm use v18.20.3
2

Install Yarn

Install Yarn package manager:
npm install -g [email protected]
3

Install Foundry

Install Foundry for smart contract development:
curl -L https://foundry.paradigm.xyz | bash
foundryup
Hardhat is also supported as an alternative to Foundry.

Installation

There are two ways to use Eco Routes: from source or as an npm package.

Create your first intent

Let’s create a simple cross-chain intent that transfers tokens from one chain to another.

Understanding the intent structure

An intent consists of three main components:
  1. Destination: The target chain ID where the intent will be executed
  2. Route: The execution instructions (calls to make, tokens needed, deadline)
  3. Reward: The incentives for solvers (reward tokens, deadline, prover address)

Example: Token transfer intent

Here’s how to create and fund an intent:
import {Portal} from "./contracts/Portal.sol";
import {Intent, Route, Reward, TokenAmount, Call} from "./contracts/types/Intent.sol";

// Initialize the Portal contract
Portal portal = Portal(portalAddress);

// Define the route - what should happen on the destination chain
Route memory route = Route({
    salt: keccak256("unique-intent-id"),
    deadline: uint64(block.timestamp + 1 hours),
    portal: destinationPortalAddress,
    nativeAmount: 0.1 ether,
    tokens: new TokenAmount[](1),
    calls: new Call[](1)
});

// Specify input tokens needed on destination chain
route.tokens[0] = TokenAmount({
    token: address(usdcToken),
    amount: 1000e6  // 1000 USDC
});

// Define the call to execute on destination chain
route.calls[0] = Call({
    target: recipientAddress,
    data: abi.encodeWithSignature(
        "transfer(address,uint256)",
        recipientAddress,
        1000e6
    ),
    value: 0
});

// Define the reward for the solver
Reward memory reward = Reward({
    deadline: uint64(block.timestamp + 1 hours),
    creator: msg.sender,
    prover: hyperProverAddress,
    nativeAmount: 0.01 ether,  // 0.01 ETH reward
    tokens: new TokenAmount[](0)
});

// Create the complete intent
Intent memory intent = Intent({
    destination: 42161,  // Arbitrum chain ID
    route: route,
    reward: reward
});

// Publish and fund the intent in one transaction
(bytes32 intentHash, address vault) = portal.publishAndFund{value: 0.11 ether}(
    intent,
    false  // allowPartial = false (must be fully funded)
);

Checking intent status

You can check if an intent is funded:
bool isFunded = portal.isIntentFunded(intent);
Get the vault address for an intent:
address vault = portal.intentVaultAddress(intent);
Check the reward status:
IIntentSource.Status status = portal.getRewardStatus(intentHash);
// Status can be: Initial, Funded, Withdrawn, or Refunded

Fulfilling an intent (Solver perspective)

Solvers execute intents on the destination chain:
// On the destination chain
Portal destinationPortal = Portal(destinationPortalAddress);

// Approve tokens for the portal
usdc.approve(address(destinationPortal), 1000e6);

// Fulfill the intent
bytes32 claimant = bytes32(uint256(uint160(solverAddress)));

bytes[] memory results = destinationPortal.fulfill{value: 0.1 ether}(
    intentHash,
    route,
    rewardHash,
    claimant
);
The claimant is a bytes32 representation of the address that will claim rewards. This format enables cross-VM compatibility.

Proving and withdrawing rewards

After fulfillment, the intent must be proven on the source chain:
// The prover sends proof back to source chain
// This is typically handled automatically by prover infrastructure

// Once proven, solver withdraws rewards on source chain
portal.withdraw(
    intent.destination,
    routeHash,
    reward
);

Testing your integration

Run the test suite to verify your setup:
# Run all tests
yarn test

# Run specific test file
forge test --match-contract Portal

# Run with verbose output
forge test -vvv

Next steps

Architecture

Learn how the Portal, Inbox, and Provers work together

Core concepts

Understand intents, vaults, and the protocol lifecycle
Always test your intents on testnet before deploying to mainnet. Ensure you understand the security implications of the calls you’re making.

Build docs developers (and LLMs) love