Across protocol

Protocol is ERC-7683 order submission for crosschain fills

Protocol is across’s contract-level route for packaging a crosschain transfer as an order, sending it to AcrossOriginSettler, and exposing the resulting deposit to relayers. The caller supplies a fill deadline, an Across-specific type hash, and ABI-encoded order data. AcrossOriginSettler validates and translates that envelope into a SpokePool deposit, while a relayer provides destination-chain liquidity and later receives settlement through Across.

Updated on

Choose open() for contract-level control

Direct open() submission is the right choice when a contract needs deterministic calldata, explicit recipients, and deadline control without a quote-generated transaction.

A prepared ERC-20 allowance leaves one origin transaction: the open call. Without it, the wallet submits two transactions - approve, then open - and pays origin gas for both.

That control matters for smart-account modules, treasury contracts, and applications that already assemble ABI calldata with viem. The deployed entry points on Base, chain ID 8453, and Arbitrum, chain ID 42161, make the choice concrete: the origin contract owns encoding and allowance handling, while Across supplies the destination fill network. A simple browser transfer gains little from this extra surface. A contract that must set its own recipient, message, nonce, or fill policy gains a stable callable boundary. That boundary also keeps recipient selection inside the calling contract’s own logic. The same subject is covered Across protocol overview.

Protocol submission choices and their security layers

Five Across initiation paths serve different workflows, and open() wins when contract composability matters more than API-managed quoting, checks, and encoding.

At a glance: Protocol submission choices and their security layers
Route Workflow Cost driver Security layer
ERC-7683 open() Encode one order tuple and submit it on the origin chain Allowance transaction when needed, plus origin gas and route fees AcrossOriginSettler and SpokePool contract execution
Swap API Request prepared transaction data, then sign the returned origin call Origin gas, destination execution, relayer capital, and LP utilization API-prepared calldata with wallet-authorized on-chain execution
Gasless openFor() Sign an EIP-712 order for a separate submitter Sponsor economics, route fees, and destination execution Permit2 authorization and Across settlement contracts
Direct depositV3() Call the origin SpokePool with the full V3 parameter set Allowance transaction when needed, origin gas, and route fees Direct SpokePool contract execution
Deposit address Transfer the quoted token to a generated origin address Origin transfer gas, route fees, and any required swap impact Counterfactual deposit handling with a designated refund address

Protocol order submission through open() exposes the most fields and the smallest service dependency, but it also leaves quote freshness, ABI selection, and allowance state to the integrator. The Swap API offers three trade types - exactInput, minOutput, and exactOutput - and returns transaction data, checks, and fee components. Gasless submission moves transaction broadcasting to a sponsor after an EIP-712 signature, using Permit2 and openFor() in the deployed lifecycle interface. Direct depositV3() remains attractive when an application already understands every SpokePool parameter.


The three-part open() envelope

The deployed open() ABI takes one three-member OnchainCrossChainOrder tuple: a uint32 fill deadline, a bytes32 type hash, and dynamic bytes order data.

The fill deadline

fillDeadline occupies 32 bits, so its largest representable integer is 4 294 967 295. It holds a Unix timestamp, not a duration. The selected timestamp must remain later than the origin transaction and long enough for a relayer to fill on the destination chain.

The order-data type

orderDataType occupies 32 bytes and prints as 64 hexadecimal digits after the 0x prefix. Across compares it with the Keccak-256 hash of the exact AcrossOrderData type string. Renaming, reordering, adding, or removing one field produces a different hash.

The encoded payload

orderData is dynamic, while the EVM ABI still encodes offsets and lengths in 32-byte words after a 4-byte function selector. Treat a flattened three-argument signature as client helper syntax; the deployed selector represents open((uint32,bytes32,bytes)). The correct payload changes when the deployed decoder’s type string changes.


Move Money Across Chains above the Across wordmark
Move Money Across Chains above the Across wordmark

Ten fields inside AcrossOrderData

Across order data is valid only when its ten fields match the deployed decoder’s types, route, token units, recipient format, and destination expectations.

Token and route values

Five fields describe the economic path: inputToken, inputAmount, outputToken, outputAmount, and destinationChainId. The two token references are 20-byte EVM addresses, while both amounts and the chain ID use uint256. USDC uses 6 decimal places, so 100 USDC becomes 100 000 000 base units before encoding. Base is 8453 and Arbitrum is 42161; neither identifier receives a thousands separator because each names a chain. A route from Base to Arbitrum therefore encodes 2 chain-specific token addresses, not one shared address. The output amount must already reflect the selected route’s fee spread.

Recipient, relayer, and nonce

recipient is a 32-byte value, exclusiveRelayer is a 20-byte EVM address, and depositNonce is a uint256. Across uses the wider recipient representation inside the resolved order. A zero nonce selects the sequential deposit path, while a nonzero nonce selects a deterministically derived deposit ID. The uint32 exclusivityPeriod completes this group.

Message bytes

message is the tenth field and carries optional destination-call data. A seven-value encoding that omits inputToken, inputAmount, and depositNonce doesn’t match this deployed ten-field decoder. Empty bytes suit a plain transfer; the payload grows when destination instructions are required.

How does an order become a destination-chain fill?

An Across order becomes fillable after AcrossOriginSettler escrows the input and records one SpokePool deposit that relayers can evaluate from the origin chain. This is set out Across protocol security.

Origin escrow and deposit

open() first pulls the exact input amount from msg.sender with ERC-20 transferFrom. AcrossOriginSettler then approves its paired SpokePool and makes one origin deposit. A zero depositNonce selects depositV3(); any nonzero value selects unsafeDeposit() and derives a deterministic deposit ID from the settler, depositor, and nonce. The transaction emits the SpokePool deposit record plus an Open event carrying the resolved order. This is documented Across protocol essentials.

Relayer-funded destination execution

The resolver builds one maxSpent entry, one minReceived entry, and one fill instruction. A relayer reads the origin record, commits its own destination-chain tokens, and calls the destination SpokePool fill function. The 32-byte order ID hashes the relay data with the destination chain ID, while the instruction casts that chain ID to uint64. Successful accounting allows each intent to be filled once.

Bundled reimbursement

Later, the Dataworker validates fills and groups repayments through the HubPool on Ethereum. Merkle-root bundles pass through the UMA Optimistic Oracle’s challenge process. The user-facing transfer finishes when FilledV3Relay records the destination fill; the later settlement window matters to relayer economics and route pricing.

Quote values complete the encoded economics

A valid Across encoding still needs a fresh quote because output amount, fill deadline, exclusivity, and fill profitability come from the selected route’s live conditions.

For a same-asset route with matching units, Across expresses the bridge fee as inputAmount minus outputAmount. That spread covers two protocol components: liquidity-provider compensation and relayer compensation. The relayer portion reflects three moving inputs - destination gas, capital opportunity cost, and capital risk - while the liquidity-provider portion follows route utilization and repayment location. Cross-asset orders add swap conversion, so raw integers from tokens with different decimals aren’t a fee calculation. With USDC’s 6 decimals, the encoding remains exact even though the quote changes: 1 USDC always equals 1 000 000 base units.

Before signing, decode all ten fields, compare the allowance with inputAmount, and confirm the destination chain, recipient, token pair, and deadline. Reusing an old output amount or deadline leaves the relayer evaluating stale economics. If any decoded field differs from the accepted quote, rebuild the order before submission.


The ERC-7683 draft boundary for long-lived integrations

Long-lived Across integrations should pin the deployed ABI because today’s ERC-7683 draft puts interoperability at the resolver layer, beyond the earlier open() lifecycle.

The current ERC-7683 draft standardizes a solver-facing resolver rather than the full open, fill, and settlement lifecycle. It uses ERC-7930 interoperable addresses and describes call steps with four core parts: target, 4-byte selector, arguments, and attributes. The deployed AcrossOriginSettler belongs to the earlier lifecycle-shaped interface and exposes open(), openFor(), resolve(), and resolveFor(). Smart-account modules, relayer tooling, and protocol-owned treasuries suit this route when they pin the deployed ABI, derive its type hash, and refresh route data whenever a deployment or draft revision changes.

Questions people ask about Protocol

Does AcrossOriginSettler accept native ETH for an ERC-7683 order?

AcrossOriginSettler’s deployed open() path accepts ERC-20 inputs, not native ETH, because it pulls tokens with transferFrom and the function isn’t payable. The input token must also be enabled for the selected destination route, and the encoded amount must use that token’s own decimal precision. A separate wrapping transaction adds origin gas and changes the asset represented in order data.

Can a Safe account call AcrossOriginSettler directly?

Safe can call AcrossOriginSettler directly because Safe transaction execution supports arbitrary contract calls. The Safe account first grants the ERC-20 allowance from its own address, then executes open() with itself as msg.sender; Across derives the depositor from that caller. A module can bundle preparation logic, although the ABI tuple, token address, amount, and allowance target must all match the deployed origin contract. Signature-based openFor() follows a separate Permit2 and EIP-712 authorization flow with its own nonce for each order.

How long should an Across ERC-7683 fill deadline remain open?

Use a uint32 Unix timestamp for the fill deadline, while choosing the interval from a route quote. The type allows values through 4 294 967 295, but that ceiling isn’t a recommended waiting period. A deadline that is past reverts or expires the order; an overly distant deadline preserves stale economics. Applications should carry forward the quoted deadline rather than inventing a universal duration.

Is a nonzero depositNonce required for deterministic tracking?

A nonzero depositNonce is required when the integration wants a deposit ID derived deterministically from the settler, depositor, and nonce. With zero, AcrossOriginSettler calls depositV3() and uses the SpokePool’s sequential deposit counter. With nonzero, it calls unsafeDeposit() and computes the ID from the supplied values. Uniqueness remains the integrator’s responsibility, so a contract should maintain its own nonce sequence, persist the emitted deposit record, and reconcile that record against the destination fill before updating user-visible status on origin and destination.

Can the exclusive relayer field change what the recipient receives?

Changing exclusiveRelayer doesn’t change the recipient, output token, or output amount, and doesn’t redefine any amount field. It assigns fill rights to one relayer during the encoded exclusivity period. After that period, other relayers can fill an eligible order. The zero address represents no nominated relayer in the on-chain order path. A nonzero address must agree with associated filler data when the signature-based openFor() route is used.

Which event should an indexer follow after calling open()?

V3FundsDeposited identifies the origin SpokePool deposit created by open(), while the settler also emits Open with the resolved order. Index the receipt from the origin transaction and read the deposit ID, depositor, destination chain ID, amounts, deadline, and recipient. Destination completion appears through FilledV3Relay or the deposit-status service. Tracking both origin and destination records prevents an application from treating transaction inclusion as proof that destination liquidity has already arrived for the specified order before reporting the crosschain action as complete in the application interface.