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:
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
Install Yarn
Install Yarn package manager:
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.
Clone the repository
git clone https://github.com/eco/eco-routes.git
cd eco-routes
Install the published package to use the TypeScript utilities: npm install @eco-foundation/routes
The npm package includes TypeScript utilities for encoding intents, hashing, and vault address calculation. To deploy contracts, you’ll need to build from source.
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:
Destination : The target chain ID where the intent will be executed
Route : The execution instructions (calls to make, tokens needed, deadline)
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.