Skip to main content
ERC-7683 is the standard for cross-chain order protocols. Eco Routes implements this standard interface through two core contracts that handle the intent lifecycle across chains.

Architecture

Origin Chain: OriginSettler handles intent creation, signature verification, and fund escrow
Destination Chain: DestinationSettler manages intent fulfillment and proof generation

OriginSettler

Entry point for creating cross-chain intents on source chains. Implements EIP-712 signature verification with domain name “EcoPortal” and version “1”.

open()

Creates an intent directly on-chain with atomic funding.
function open(OnchainCrossChainOrder calldata order) external payable
Process:
  1. Validates orderDataType matches ORDER_DATA_TYPEHASH
  2. Decodes OrderData from the order
  3. Calls _publishAndFund() to create intent and transfer funds
  4. Emits Open event with resolved order details

openFor()

Creates a gasless intent on behalf of a user using EIP-712 signatures.
function openFor(
    GaslessCrossChainOrder calldata order,
    bytes calldata signature,
    bytes calldata /* originFillerData */
) external payable
Security Validations:
  • block.timestamp <= order.openDeadline
  • order.originSettler == address(this)
  • order.originChainId == block.chainid
  • orderDataType matches ORDER_DATA_TYPEHASH
  • Signature verification via _validateOrderSig()
Replay Protection: Built into _publishAndFund() through vault state checking (Initial, Funded, Withdrawn, Refunded).

Resolution Functions

Convert Eco-specific order data into ERC-7683 compliant format:
function resolve(OnchainCrossChainOrder calldata order) 
    public view returns (ResolvedCrossChainOrder memory)

function resolveFor(GaslessCrossChainOrder calldata order, bytes calldata) 
    public view returns (ResolvedCrossChainOrder memory)
Resolution Process:
  1. Converts Reward structure into Output[] array (rewards + native ETH if present)
  2. Calculates routeHash, rewardHash, and intentHash
  3. Creates FillInstruction with encoded (route, rewardHash) as originData
Key Mappings:
  • user: orderData.reward.creator
  • originChainId: block.chainid
  • fillDeadline: orderData.routeDeadline
  • orderId: intentHash
  • minReceived: Reward outputs with origin chain IDs
  • fillInstructions[0].destinationSettler: orderData.routePortal

EIP-712 Signature

GaslessCrossChainOrder TypeHash:
keccak256(
    "GaslessCrossChainOrder(address originSettler,address user,uint256 nonce,uint256 originChainId,uint32 openDeadline,uint32 fillDeadline,bytes32 orderDataType,bytes32 orderDataHash)"
)
Verification recovers signer using ECDSA and compares with order.user.

Abstract Method

function _publishAndFund(
    uint64 destination,
    bytes memory route,
    Reward memory reward,
    bool allowPartial,
    address funder
) internal virtual returns (bytes32 intentHash, address vault);
Implementations handle vault creation, fund transfers, state checking, and excess ETH returns.

DestinationSettler

Handles intent fulfillment on destination chains.

fill()

Executes a cross-chain order on the destination.
function fill(
    bytes32 orderId,
    bytes calldata originData,
    bytes calldata fillerData
) external payable
Parameters:
  • orderId: Intent hash from origin chain
  • originData: Encoded (bytes route, bytes32 rewardHash)
  • fillerData: Encoded (address prover, uint64 source, bytes32 claimant, bytes proverData)
Execution:
  1. Decodes originData to extract route and rewardHash
  2. Emits OrderFilled(orderId, msg.sender)
  3. Decodes fillerData for prover details
  4. Calls fulfillAndProve() with decoded parameters

Abstract Method

function fulfillAndProve(
    bytes32 intentHash,
    Route memory route,
    bytes32 rewardHash,
    bytes32 claimant,
    address prover,
    uint64 source,
    bytes memory data
) public payable virtual returns (bytes[] memory);
Implementations execute route instructions and initiate proof generation atomically.

Data Types

OnchainCrossChainOrder

struct OnchainCrossChainOrder {
    uint32 fillDeadline;
    bytes32 orderDataType;
    bytes orderData;  // ABI-encoded OrderData
}

GaslessCrossChainOrder

struct GaslessCrossChainOrder {
    address originSettler;
    address user;
    uint256 nonce;
    uint256 originChainId;
    uint32 openDeadline;
    uint32 fillDeadline;
    bytes32 orderDataType;
    bytes orderData;  // ABI-encoded OrderData
}

OrderData

struct OrderData {
    uint64 destination;
    bytes route;
    Reward reward;
    uint256 maxSpent;
    address routePortal;
    uint256 routeDeadline;
}

ResolvedCrossChainOrder

struct ResolvedCrossChainOrder {
    address user;
    uint256 originChainId;
    uint32 openDeadline;
    uint32 fillDeadline;
    bytes32 orderId;
    uint256 maxSpent;
    Output[] minReceived;
    FillInstruction[] fillInstructions;
}

Output

struct Output {
    bytes32 token;      // Address as bytes32, zero for native
    uint256 amount;
    bytes32 recipient;  // Zero address in Eco's model
    uint256 chainId;
}

FillInstruction

struct FillInstruction {
    uint64 destinationChainId;
    address destinationSettler;
    bytes originData;  // Encoded (route, rewardHash)
}

Integration Examples

Creating Direct Intent

OnchainCrossChainOrder memory order = OnchainCrossChainOrder({
    fillDeadline: uint32(block.timestamp + 1 hours),
    orderDataType: ORDER_DATA_TYPEHASH,
    orderData: abi.encode(OrderData({
        destination: 42161,
        route: encodedRoute,
        reward: rewardStruct,
        maxSpent: 1000000,
        routePortal: portalAddress,
        routeDeadline: block.timestamp + 1 hours
    }))
});

originSettler.open{value: fundingAmount}(order);

Creating Gasless Intent

// Off-chain: User signs EIP-712 message
bytes32 structHash = keccak256(abi.encode(
    GASLESS_CROSSCHAIN_ORDER_TYPEHASH,
    order.originSettler,
    order.user,
    order.nonce,
    order.originChainId,
    order.openDeadline,
    order.fillDeadline,
    order.orderDataType,
    keccak256(order.orderData)
));

bytes32 digest = keccak256(abi.encodePacked(
    "\x19\x01",
    domainSeparator,
    structHash
));

// On-chain: Solver submits
originSettler.openFor(order, signature, "");

Fulfilling Intent

bytes memory originData = abi.encode(route, rewardHash);
bytes memory fillerData = abi.encode(
    proverAddress,
    sourceChainId,
    claimantBytes32,
    proverSpecificData
);

destinationSettler.fill{value: executionValue}(
    orderId,
    originData,
    fillerData
);

Error Handling

  • TypeSignatureMismatch(): OrderDataType doesn’t match ORDER_DATA_TYPEHASH
  • OpenDeadlinePassed(): Current time exceeds opening deadline
  • InvalidOriginSettler(): Order specifies wrong settler address
  • InvalidOriginChainId(): Order targets different chain
  • InvalidSignature(): Signature verification failed

Eco-Specific Implementation Notes

  1. Recipient Field: Output.recipient always zero address; actual claimant specified in fillerData at fulfillment
  2. ChainId Semantics: minReceived outputs use origin chainId (rewards claimed on source chain)
  3. FillInstruction.originData: Contains (route, rewardHash) tuple instead of complete intent
  4. Single Fill: Each order has exactly one fill instruction
I