Skip to main content
Any validation layer can be added as long as it supports validating a payload from the output chain. LI.FI intent supports any oracle that the Open Intents Framework (OIF) supports. An OIF oracle validation layer needs to support the following:
  1. A submission interface where solvers can submit their filled outputs. The submission interface should accept arbitrary packages for validation and then call the associated output settlement contract to check whether the payloads are valid. If submission is automatic by being capable of proving contract calls, events, or storage this requirement can be ignored. Oracle implementations can validate whether an output has been filled through the IAttester interface:
    interface IAttester {
      function hasAttested(
          bytes[] calldata payloads
      ) external view returns (bool);
    }
    
  2. Implement the validation interfaces so attested (or filled) outputs can verify whether outputs have been filled:
      interface IInputOracle {
          function efficientRequireProven(
            bytes calldata proofSeries
        ) external view;
      }
    
Beware of the following when implementing oracle systems:
  • Submission or receive interfaces do not have to be standardized; they need to be documented so solvers can implement it.
  • Oracle systems can either have automatic relaying or manual relaying.
  • Trust assumptions for the oracle system needs to be explicit, so intent issuers and solvers can make informed decisions.
For more documentation refer to the OIF Contributing Guidelines. These requirements specify how a proper OIF compatible oracle system should behave.

Implemented Validation Interfaces

There are two types of validation interfaces:
  1. Self-serve: Validation interfaces where submitting the payload generates an off-chain proof that must be collected and submitted on the input chain.
  2. Automatic: Validation interfaces where submitting the payload automatically delivers the associated proof on the input chain.
Choosing an oracle system is for the intent issuer: Oracle security is directly linked to the payment of users’ inputs to solvers — users are paid directly by solvers. However, when issuing an intent it is important that solvers are capable of understanding how to fill an intent using a specific oracle. In other words, users can freely choose which oracle system they want secure a specific intent but solvers have to implement it for the intent to be solved. When using the LI.FI intent order server, it will only support oracle systems that solvers are quoting for at a specific time.
Speed & Price
Some oracle systems are significantly faster than other oracle systems. While speed does not impact asset delivery for users, it matters for solvers. Choosing an oracle system with fast repayments results in cheaper intents, as solvers can rotate their capital faster.
The following documentation may be outdated if the oracle network updates their documentation and does not notify the LI.FI team. Always refer to the oracle system’s official documentation for the most accurate and up-to-date information.

Polymer

Polymer is an event based system that provides real time proofs at low cost. Security is based on bundled state through Polymer with block headers sourced from Sequencer preconfirmations.
  • Low gas cost.
  • Low latency for quick settlement.
  • Polymer can only prove one output at a time requiring batch proving to be implemented on the solver side to optimise efficiency.
  • Broadcast
The target event for validation is OutputFilled, emitted when an output has been filled.
event OutputFilled(
  bytes32 indexed orderId,
  bytes32 solver,
  uint32 timestamp,
  MandateOutput output,
  uint256 finalAmount
);
Using the Polymer Prove API, a proof of the event can be generated. Once generated, it can be submitted to receivedMessage. For an example of a integration for fetching Polymer proofs, see lintent.org/polymer or the receiveMessage call made.

Wormhole

Wormhole is a messaging based system that allows contracts to broadcast VAAs (Verified Action Approvals). VAAs are messages signed by the Wormhole Guardian network.
  • Security through a decentralized guardian set.
  • Supports batch proving of multiple outputs in a single VAA.
  • Comparatively slow compared to alternatives.
  • Broadcast
The Wormhole implementation is based on the broadcast functionality of Wormhole. Messages must be submitted to the Wormhole Implementation via the submit interface. Messages must be encoded into FillDescriptions and then submitted:
function submit(
  address source,
  bytes[] calldata payloads
) public payable returns (uint256 refund);
This message is then emitted to the Wormhole guardian set. Once the associated proof becomes available, the solver can submit the proof to receiveMessage on the input chain to validate their intents. There are many ways to get Wormhole VAAs like the Wormholescan api or the Wormhole SPY.
The Wormhole implementation uses a significantly more efficient validation algorithm than Wormhole’s Implementation.sol for gas saving purposes.

Hyperlane

Hyperlane is a highly customizable messaging based system that allows contracts to send and receive messages verified through a selected Interchain Security Modules (ISM).
  • Highly customizable security modules.
  • Supports batch proving of multiple outputs in a single message.
  • Strong censorship guarantees.
  • Automatic or manual relaying.
The Hyperlane implementation is based on the dispatch function on the Hyperlane mailbox. To configure Hyperlane relaying, provide the relevant customHook and customHookMetadata when calling submit.
function submit(
  uint32 destinationDomain,
  address recipientOracle,
  uint256 gasLimit,
  bytes calldata customMetadata,
  address source,
  bytes[] calldata payloads
) public payable
For more about dispatch hooks see the Hyperlane documentation. Because Hyperlane is a highly customizable system, the exact required interactions highly varies based on the selected ISM and relaying method. For more information, refer to the Hyperlane documentation. Chainlink CCIP is a messaging based system that allows contracts to send and receive messages verified through Chainlink oracles.
  • Security through Chainlink’s oracle network.
  • Supports batch proving of multiple outputs in a single message.
  • Comparatively slow and expensive compared to alternatives.
Chainlink CCIP implementation is based on the CCIP router ccipSend with an EVM2AnyMessage . To submit filled outputs to the CCIP oracle, first call the quote endpoint: getFee to estimate the messaging fee. Then call submit to send the message to the output chain along with the required fee. If an ERC20 token is used to pay the fee, approve the token transfer first.
function submit(
  uint64 destinationChainSelector,
  bytes32 receiver,
  bytes calldata extraArgs,
  address source,
  bytes[] calldata payloads,
  address feeToken
)
CCIP is automatic and once submitted, Chainlink will relay the message to the input chain.

Axelar

Axelar is a messaging based system that allows contracts to send and receive message verified by Axelar’s Proof of State network.
  • Security through Axelar’s Proof of State network.
  • Supports batch proving of multiple outputs in a single message.
  • Automatic relaying.
Axelar implementation is based on the Axelar Gateway callContract function. To submit filled outputs to the Axelar oracle, first use the gasService to estimate the fees. Then call submit to send the message to the output chain along with the required fee.
submit(
  string calldata destinationChain,
  string calldata destinationAddress,
  address source,
  bytes[] calldata payloads
) public payable
Axelar is automatic and once submitted, Axelar will relay the message to the input chain.

LayerZero

LayerZero is a security customizable messaging based system that allows contracts to send and receive messages verified through a selected set of Decentralized Verifier Networks (DVNs).
  • Customizable security
  • Popular and broad chain compatibility.
  • Supports batch proving of multiple outputs in a single message.
  • Expensive
The LayerZero implementation is based on send on the LayerZero endpoint. To submit filled outputs to the LayerZero oracle, first use the quote function to estimate fees and then call submit with the required fee to send the message to the output chain.
function submit(
  uint32 dstEid,
  address recipientOracle,
  address source,
  bytes[] calldata payloads,
  bytes calldata options
) external payable;
LayerZero is automatic and once submitted, LayerZero will relay the message to the input chain.

Bitcoin

LI.FI Intents has a Bitcoin Simplified Payment Validation (SPV) client implementation. This implementation works both as an Output Settlement implementation and as a validation layer. The Bitcoin SPV client requires constant upkeep—the blockchain must be updated approximately every 10 minutes, or whenever a transaction needs to be proven—to properly validate transactions. To generate a transaction proof, refer to the code below:
import mempoolJS from "@catalabs/mempool.js";
const mainnet: boolean;
const {
  bitcoin: { transactions, blocks },
} = mempoolJS({
  hostname: "mempool.space",
  network: mainnet ? undefined : "testnet4",
});

export async function generateProof(
  txid: string,
): Promise<{ blockHeader: string; proof: Proof; rawTx: string }> {
  const tx = await transactions.getTx({ txid });

  const merkleProof = await transactions.getTxMerkleProof({ txid });
  // TODO: serialization version 1.
  const rawTx = await transactions.getTxHex({ txid });

  const blockHash = await blocks.getBlockHeight({
    height: merkleProof.block_height,
  });

  // Most endpoints provide transactions witness encoded.
  // The following function serves to strip the witness data.
  const rawTxWitnessStripped = removeWitnesses(rawTx);

  const blockHeader = await blocks.getBlockHeader({ hash: blockHash });

  return {
    blockHeader,
    proof: {
      txId: txid,
      txIndex: merkleProof.pos,
      siblings: merkleProof.merkle.reduce(
        (accumulator, currentValue) => accumulator + currentValue,
      ),
    },
    rawTx: rawTxWitnessStripped,
  };
}