Background

An intent-based application allows a user to express an intent and encodes it as an order for an underlying protocol. The order is signed by the user, and the signed order is submitted to a network of fillers. The fillers are then able to fill the order on chain, satisfying the user intent, and can claim a payment by providing the signed order and validation of the fill.

Each intent protocol defines its own type of orders with a specific set of parameters and with different requirements and implications for fillers. As a result, before a filler can participate in an intent protocol they have to build out the integration for that specific protocol. The goal of ERC-7683 is to provide a protocol-agnostic order type, and to reduce to a minimum the protocol-specific integration requirements for fillers.

Interface and Types

An ERC-7683 order consists of a payload bytestring and the address of a resolver contract.

A resolver must implement the IOrderResolver interface.

An ERC-7683-compatible filler, upon receiving an order, may use the resolver to decode the payload by performing an eth_call to the resolve function.

// SPDX-License-Identifier: CC0
pragma solidity 0.8.30;

interface IOrderResolver {
		function resolve(bytes calldata payload)
				external view returns (ResolvedOrder memory);
}

struct ResolvedOrder {
		bytes[] steps; // Step[] (Tagged Union Encoding)
		bytes[] variables; // VariableRole[] (Tagged Union Encoding)
		Assumption[] assumptions;
		bytes[] payments;
}

interface Step {
		function Call(
				bytes memory target, // ERC-7930 Address
				bytes4 selector,
				bytes[] memory arguments, // Argument Encoding
				bytes[] memory attributes, // Attribute[] (Tagged Union Encoding)
				uint256[] memory dependencySteps, // Steps by index in resolved order
				bytes[] memory payments // Payment[] (Tagged Union Encoding)
		) external;

		function FromResolver(
				bytes memory target, // ERC-7930 Address
				bytes4 selector,
				bytes[] memory arguments, // Argument Encoding
				uint256[] memory dependencies // Steps by index in resolved order
		) external;
}

interface Attribute {
		function SpendsERC20(
				bytes memory token, // ERC-7930 Address
				bytes memory amountFormula, // Formula (Tagged Union Encoding)
				bytes memory spender // ERC-7930 Address
		) external;

		function OnlyBefore(uint256 deadline) external;
		function OnlyFillerUntil(
				address exclusiveFiller,
				uint256 deadline
		) external;
		function OnlyWhenCallResult(
				bytes memory target, // ERC-7930 Address
				bytes4 selector,
				bytes[] memory arguments, // Argument Encoding
				bytes memory result,
				uint256 maxGasCost
		) external;

		function UnlessRevert(bytes memory reason) external;

		function WithTimestamp(uint256 timestampVarIdx) external;
		function WithBlockNumber(uint256 blockNumberVarIdx) external;
		function WithEffectiveGasPrice(uint256 gasPriceVarIdx) external;
		function WithLog(byte1 mask, uint256[] topicVarIdxs, uint256 dataVarIdx) external;
}

interface Formula {
		function Const(uint256 val) external;
		function VarRef(uint256 varIdx) external;
}

interface Payment {
		function ERC20(
				bytes memory token, // ERC-7930 Address
				bytes memory amountFormula, // Formula (Tagged Union Encoding)
				uint256 recipientVarIdx,
				uint256 estimatedDelaySeconds
		) external;
}

interface VariableRole {
		function PaymentRecipient(uint256 chainId) external;
		function PaymentChain() external;
		function Pricing() external;
		function TxOutput() external;
		function Witness(
				string memory kind,
				bytes memory data,
				uint256[] memory variables
		) external;
		
		function Query(
				bytes memory target, // ERC-7930 Address
				bytes4 selector,
				bytes[] memory arguments, // Argument Encoding
				uint256 blockNumber
		) external;
}

struct Assumption {
		bytes memory trusted; // ERC-7930 Address
		string memory kind;
}

Tagged Union Encoding

Tagged unions are encoded as bytes by ABI encoding function calls from a given interface. The functions in the interface are the variants of the tagged union. The function selector is used as the tag.

interface E {
	function A(uint256 x) external;
	function B(string s) external;
}

bytes a = abi.encodeCall(E.A, (x));
bytes b = abi.encodeCall(E.B, (s));
enum E {
	A(x: U256),
	B(s: String),
}

let a = E::A(x);
let b = E::B(s);

Argument Encoding

An array bytes[] arguments encodes the arguments for a function call as follows:

  1. If arguments[i] has length 32, it must be ABI decoded as a uint256, and this integer k denotes that the ith argument takes the value of the kth variable.
  2. Otherwise arguments[i] is the encoding of a constant value v given by the Solidity expression abi.encode("", v).

See Aggregation for how to encode a function call given these arguments.

Resolved Order Semantics

A resolved order returned by a trusted resolver is a promise that, given a set of choices for variables, if steps are executed within the constraints specified by attributes, any payments described within will be made, provided the trust assumptions hold.