struct OnchainCrossChainOrder { uint32 fillDeadline; // Deadline for order fulfillment bytes32 orderDataType; // Type hash for order data bytes orderData; // Encoded OrderData struct}
struct GaslessCrossChainOrder { address originSettler; // Origin settler contract address address user; // User creating the order uint256 nonce; // Unique nonce for replay protection uint256 originChainId; // Source chain ID uint32 openDeadline; // Deadline to open the order uint32 fillDeadline; // Deadline to fill the order bytes32 orderDataType; // Type hash for order data bytes orderData; // Encoded OrderData struct}
struct ResolvedCrossChainOrder { address user; // User address uint256 originChainId; // Origin chain ID uint32 openDeadline; // Open deadline timestamp uint32 fillDeadline; // Fill deadline timestamp bytes32 orderId; // Unique order identifier (intent hash) Output[] maxSpent; // Max tokens spent by user Output[] minReceived; // Min rewards for solver FillInstruction[] fillInstructions; // How to fill on destination}
/** * @notice Opens an Eco intent directly on chain via ERC-7683 interface * @dev Called by the user to create and fund an intent atomically * @param order the OnchainCrossChainOrder containing embedded OrderData */function open(OnchainCrossChainOrder calldata order) external payable;/** * @notice Opens an Eco intent on behalf of a user via ERC-7683 gasless interface * @dev Called by a solver to create an intent for a user using their signature * @param order the GaslessCrossChainOrder containing user signature and OrderData * @param signature the user's EIP-712 signature authorizing the intent creation */function openFor( GaslessCrossChainOrder calldata order, bytes calldata signature, bytes calldata /* originFillerData */) external payable;
// Approve tokensfor (uint256 i = 0; i < reward.tokens.length; i++) { IERC20(reward.tokens[i].token).approve( address(portal), reward.tokens[i].amount );}// Open orderportal.open{value: reward.nativeAmount}(order);// Order is now published and funded!
// Solver receives order + signature from userfunction executeGaslessOrder( GaslessCrossChainOrder calldata order, bytes calldata signature) external { // Verify order is profitable require(isProfitable(order), "Not profitable"); // Submit order on behalf of user // No originFillerData needed for Eco Routes portal.openFor(order, signature, ""); // Order is now created and funded from user's tokens // (user must have approved portal beforehand or // funded the vault directly)}
/** * @notice Fills a single leg of a particular order on the destination chain * @param orderId Unique identifier for the order being filled (intent hash) * @param originData Data emitted on the origin chain (route + rewardHash) * @param fillerData Data provided by the filler (prover, source, claimant, proverData) */function fill( bytes32 orderId, bytes calldata originData, bytes calldata fillerData) external payable;
const filter = portal.filters.Open();portal.on(filter, async (orderId, resolvedOrder) => { // Check if order is for your destination chain const fillInstruction = resolvedOrder.fillInstructions[0]; if (fillInstruction.destinationChainId === myChainId) { await evaluateAndFill(orderId, fillInstruction); }});
2
Prepare Fill Data
Encode the required data for filling:
// originData comes from the Open event's fillInstructions// It contains: abi.encode(route, rewardHash)bytes memory originData = fillInstruction.originData;// fillerData: (prover, source, claimant, proverData)bytes memory fillerData = abi.encode( proverAddress, // Prover on destination chain sourceChainDomainID, // Bridge domain ID for source bytes32(uint256(uint160(msg.sender))), // Your address as claimant proverSpecificData // Bridge-specific parameters);
3
Execute Fill
Call the fill function:
// Approve tokens for portalfor (uint256 i = 0; i < route.tokens.length; i++) { IERC20(route.tokens[i].token).approve( address(portal), route.tokens[i].amount );}// Fill the orderportal.fill{value: totalValue}( orderId, // Intent hash from Open event originData, // Route + rewardHash fillerData // Prover parameters + claimant);
This internally calls fulfillAndProve() to execute and initiate proving.
/** * @notice Resolves an OnchainCrossChainOrder to a ResolvedCrossChainOrder * @param order the OnchainCrossChainOrder to be resolved */function resolve( OnchainCrossChainOrder calldata order) public view returns (ResolvedCrossChainOrder memory);/** * @notice Resolves GaslessCrossChainOrder to a ResolvedCrossChainOrder * @param order the GaslessCrossChainOrder to be resolved */function resolveFor( GaslessCrossChainOrder calldata order, bytes calldata // originFillerData (not used)) public view returns (ResolvedCrossChainOrder memory);
Usage:
// Resolve on-chain orderResolvedCrossChainOrder memory resolved = portal.resolve(order);// Access standardized fieldsaddress user = resolved.user;bytes32 orderId = resolved.orderId; // This is the intent hashOutput[] memory rewards = resolved.minReceived;FillInstruction memory fillInst = resolved.fillInstructions[0];
Use ERC-7683 for Interoperability: If you want your orders to be visible to all ERC-7683 solvers and tools, use the standardized interface.
Use Native Interface for Integration: If you’re building a custom integration and want maximum control and efficiency, use the native Eco Routes interface.
Signature Security: When using gasless orders, ensure users understand what they’re signing. Always display human-readable order details.
Nonce Management: For gasless orders, track nonces carefully to prevent replay attacks. Use timestamps or incrementing counters.