Intent Lifecycle in Across

Intents as a Building Block

Across is used directly by end-users, but it can also be used by other protocols to ensure their user intents are fulfilled. The Across intent structure and lifecycle described below is general enough to serve as a settlement and communication layer for many use cases.

Initiation

The initiation process involves 3 basic steps:

  1. depositV3 is called on the SpokePool. This call can be made directly by a user, but could also be called on behalf of a user by some other smart contract system. The chain where this call happens is called the origin chain for this intent. The intent specifies the destination chain, which is where the user wants to receive the output.

  2. The user's are pulled into the SpokePool from the caller. These funds are escrowed until the intent is fulfilled, at which point they can be released. Because cross-chain intents are not atomic, user funds must be escrowed before the relayer can safely fulfil the intent.

  3. The SpokePool emits the V3FundsDeposited event. Relayers can subscribe to this event to identify intents that they can fill.

These steps are intended to be a primitive that can fit into almost any system that requires cross-chain transfers. For instance, this design allows for gasless order systems, where the user simply signs the order and the filler brings it on chain. In such a system, the relayer may be preselected in an offchain auction to minimize the user's cost.

Fill

After initiation, a relayer must fulfil the user's intent. This process involves three distinct actions:

  1. fillV3Relay is called on the SpokePool contract deployed on the destination chain. In this call, the relayer specifies on which chain they would like to receive the user's input tokens. The LP Fee that Across charges on input tokens depends on this choice. Generally, if the relayer takes the input tokens on the chain where the user deposited them, the fee is smallest (if not 0).

  2. The intent is marked as filled in the SpokePool. This prevents a second relayer from filling the same intent a second time.

  3. The SpokePool emits the FilledV3Relay event. These events can be used to track the status of intents being settled by the system. They are also used to track

Note: intents can have an exclusivity period whereby a particular relayer address has the sole right to perform the fill during that period.

Slow Fill or Expiration (if no fill)

In the (rare) case where a fill doesn't happen, there are two fallbacks: an expiration or a slow fill.

A slow fill means that the Across system fills the user without requiring a relayer to provide the capital. It's called a slow fill because it requires Across to optimistically verify this fill before executing it, which means the fill happens a few hours after initiation (much longer than a typical fill). A slow fill happens when the following conditions are met:

  • The input token and output token are the same asset.

  • requestV3SlowFill is called on the destination chain before the expiration time for the intent.

  • The slow fill is executed before any relayer fills it or the intent expires.

In cases where a slow fill can't or does not happen and a relayer does not fill the intent, the intent expires. Like a slow fill, this expiry must be be optimistically verified, which takes a few hours. Once this verification is done, the user is then refunded their money on the origin chain.

Settlement

Once an intent has been fulfilled, Across verifies that fulfillment and releases the input tokens to the relayer. Across does this by periodically verifying a bundle of intents. The general process for producing, verifying, and executing a bundle is:

  1. A block range is determined on each chain supported by Across. This block range goes from the end of the previous bundle's range to a recent block chosen by the proposer.

  2. All fill or slow fill request events in the range are validated by ensuring they match some deposit event on the origin chain.

  3. All valid fills, slow fill requests, and intent expirations are combined to determine an aggregated list of payments that need to be made on each chain. Those payments are included in the bundle.

  4. If funds need to be moved between chains to make these payments, those transfer instructions are included in the bundle.

  5. These payments and transfers are organized into a series of data structures called merkle trees whose roots are then proposed on chain to the HubPool along with a bond.

  6. Once this proposal passes the challenge period without being disputed, the bundle execution can begin: these roots are sent from the HubPool to each chain's SpokePool via canonical bridges. Funds are also transferred according to the bundle instructions in this step.

  7. Once these roots arrive, anyone can execute them to make the payments determined in step 3.

Intent Structure in Across

An intent in Across is essentially a struct (set of values) that specifies what the user expects to happen. The user's funds are only released once Across verifies that this intent was satisfied as specified.

The basic fields are:

FieldDescription

recipient

the address that should receive the funds

inputToken

the token that the user supplies

inputAmount

the amount of the inputToken the user supplies

outputToken

the token that the user wants to receive

outputAmount

the amount of the output token the user wants to receive

destinationChainId

where the user wants to receive the output tokens

fillDeadline

deadline for the user to receive the tokens

message

custom data that is sent to the recipient if it's a contract; this allows for custom actions to be executed on the destination chain

Advanced fields:

FieldDescription

exclusiveRelayer

a preselected relayer who is given the exclusive right to fill the user

exclusivityDeadline

deadline for the exclusive relayer to perform the fill before it is opened to other fillers

Last updated