← Blog
September 5, 2026 · 16 min

ERC-7683 was rewritten around resolvers

Almost every article written about ERC-7683 describes a standard that the ERC no longer specifies. On 2026-05-13 the document was rewritten around a resolver interface, and the order structs everyone quotes were demoted to a section titled "Previous Draft".

ERC-7683 is the cross-chain intents standard. The version that circulated for two years defined two order structs, a canonical resolved form, and two settlement interfaces named IOriginSettler and IDestinationSettler. That is the version in the explainers, the tutorials, and the integration guides.

It is not the version in the ERC. We pulled the current text from the ethereum/ERCs repository and read it against its own history. What follows is an audit of what changed, the four failures the authors now admit, and why the replacement matters to anything that pays for work across chains.

The commit

The file ERCS/erc-7683.md has a short history. It was added on 2024-04-12. Between then and 2025-01-08 it received six edits, all of them typo fixes, comment clarifications and small extensions. Then it sat untouched for sixteen months.

On 2026-05-13 a single commit landed with the message Update ERC-7683: Redesign around resolvers, authored by Francisco Giordano. It changed 350 lines and removed 264. That is not an amendment. That is a replacement of the specification body.

The frontmatter tells the rest of the story:

eip: 7683
title: Cross Chain Intents
description: Programmable solvers for intent protocols.
status: Draft
type: Standards Track
category: ERC
created: 2024-04-11
requires: 7930

Three things to note. The status is still Draft — this standard has never been Final, despite being described as ratified in secondary coverage. The description no longer mentions chains at all; it now reads "programmable solvers for intent protocols", a broader scope than cross-chain value transfer. And there is a new dependency: ERC-7930, interoperable addresses, which we will decode later because it changes every type signature in the document.

The author list also grew to seven: Francisco Giordano, Mark Toda, Matt Rice, Nick Pai, Alexander Lindgren, Mark Gretzke and Chris Cashwell.

What the previous draft standardized

The old design is worth restating precisely, because it is still the mental model most integrators carry.

A user signed a GaslessCrossChainOrder containing originSettler, user, nonce, originChainId, openDeadline, fillDeadline, orderDataType and an opaque orderData blob. An OnchainCrossChainOrder was the same idea with only fillDeadline, orderDataType and orderData.

Either form could be resolved into a ResolvedCrossChainOrder carrying orderId, arrays of Output named maxSpent and minReceived, and an array of FillInstruction. An IOriginSettler exposed open, openFor, resolve and resolveFor; it emitted an Open event carrying the entire resolved order. An IDestinationSettler exposed a single fill(bytes32 orderId, bytes originData, bytes fillerData).

That description is accurate — and it is what erc7683.org, the standard's own documentation site, still publishes as the spec. The canonical ERC and its companion site now describe different systems.

Practical consequence — if you are integrating today, "ERC-7683 compatible" is ambiguous. Ask which draft. The struct-based design and the resolver design share a number and share almost nothing else.

The four failures the ERC now admits

The new Rationale contains a section called "Previous Draft" that is unusually candid for a standards document. It lists four reasons the earlier design did not achieve its goal.

Standardization was superficial. The order structs were parameterized by orderDataType and an implementation-specific orderData payload. In the ERC's own words, orders were "only superficially standardized" — a solver still had to implement support for each protocol's subtypes, "which is not meaningfully different from a situation where each protocol implements an entirely custom interface". The struct was shared. The work was not.

Profitability was unbounded. maxSpent and minReceived were meant to let a solver decide whether an order was worth filling. But they only provided a lower bound on profit. If the bound was loose, profitable orders looked unprofitable and went unfilled. The ERC names the degenerate case directly: protocols sometimes could not provide any maxSpent other than UINT256_MAX. That happens when the user's signature binds only the worst price they will accept, or when the solver's real cost depends on a value chosen during execution, such as priority fees in a gas auction.

Escrow was assumed. The old flow was escrow-first: the user's funds are locked on the origin chain, then the order can be filled. Resource-lock protocols invert this. If user funds sit under a lock the solver already trusts, there is no reason to open anything on the origin chain before filling. The old draft had no room for fill-first designs.

Gas was wasted. Large calldata structs, plus emitting the entire resolved order in the Open event, imposed overhead. The ERC notes this made a standard-compliant interface "less attractive than a protocol-specific interface" — the failure mode that kills a standard quietly.

Read together, these are the symptoms of standardizing the wrong layer. The old draft tried to standardize the order lifecycle: encoding, publication, escrow, fill. The new draft standardizes only the boundary where a solver reads an order.

The resolver model

The new specification is one interface:

interface IResolver {
    struct ResolvedOrder {
        // Array of `IStep` ABI calldata.
        bytes[] steps;
        // Array of `IVariableRole` ABI calldata.
        bytes[] variables;
        // Array of `IPayment` ABI calldata.
        bytes[] payments;
        Assumption[] assumptions;
    }

    struct Assumption {
        string name;
        bytes data;
    }

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

A protocol publishes orders as opaque payloads and deploys a resolver that decodes them. The solver calls resolve and receives a description of what to do, what it may cost, and what it will be paid.

Two design decisions carry the weight.

First, resolution happens offchain via eth_call, even though the resolver is deployed onchain. The ERC is explicit that because resolution never needs to be included in a transaction, "the payload and translation process are not constrained by onchain gas costs". A resolver can do expensive decoding and validation that would be unthinkable in calldata. This directly answers the fourth failure above.

Second, the resolver is deployed onchain anyway, because it is the point of trust. The stated model is that solver operators whitelist resolver addresses, and once vetted, the instructions those resolvers produce are trusted. Vetting happens "through the usual means, such as security audits, bounties, and lindiness". This is an allowlist with human governance, and it is worth naming as such — the same shape as facilitator trust in the x402 verify/settle model.

The guarantee a resolver makes is strong. A resolver MUST guarantee that an order may only abort as explicitly specified in revert policies. If no abort policy triggers, a solver that begins executing the steps of an order MUST be able to fulfill all requirements and receive all payments. The resolver is not describing an opportunity. It is underwriting one.

Steps, variables, attributes, payments

The resolved order is a small instruction language. It has four parts, and its vocabulary is the most interesting thing in the document.

A step is a Call: a target interoperable address, a 4-byte selector, a list of arguments that are each a constant or a variable reference, and a list of attributes. The solver evaluates the arguments, encodes calldata, and submits a transaction. Steps form a dependency graph that must be acyclic, and execution must proceed in hard-dependency order.

Attributes constrain a step. SpendsERC20 declares that the call may pull up to an amount of a token from the caller via transferFrom, so the solver knows exactly what balance and allowance to stage. SpendsGas declares a gas ceiling. TimingBounds pins block.number or block.timestamp between an optional lower and upper bound. NeedsStep and NeedsVariable declare dependencies the resolver could not otherwise imply.

RevertPolicy is the one to read twice. It takes a policy of either "ignore" or "abort" and an expectedReason byte prefix. If the call reverts with data beginning with that prefix, ignore means the step counts as executed and the solver may safely skip it, while abort means the whole order is off. And then the rule that makes the guarantee real: a call MUST NOT revert without a matching RevertPolicy attribute. Any unannounced revert is a spec violation by the resolver, not bad luck for the solver.

Variables are values the solver decides, each with a declared role. PaymentRecipient and PaymentChain let the solver choose where it gets paid — note that the destination of payment is the solver's choice, expressed as a hole in the order rather than a field the user fills in. StepCaller binds the account used for a given step. ExecutionOutput captures block.number, block.timestamp or receipt.effectiveGasPrice from a completed step, which is how time-dependent pricing gets expressed. Query and QueryEvents instruct the solver to run eth_call or eth_getLogs and bind the result. Witness is the general escape hatch: an identified offchain procedure that produces a value from data and other variables.

Payments close the loop. The single payment type today is ERC20:

function ERC20(
    bytes calldata token,      // interoperable address
    bytes calldata sender,
    bytes calldata amountFormula,
    uint256 recipientVarIdx,  // index of a PaymentRecipient variable
    uint256 onStepIdx,
    uint256 estimatedDelaySeconds
) external;

When the step at onStepIdx executes, at least amountFormula of token must be paid to the address held by the variable at recipientVarIdx, on the chain indicated by the token's interoperable address. The payment SHOULD be delayed by estimatedDelaySeconds with high confidence.

That last field is the quiet fix for the profitability failure. The old draft gave solvers a loose bound on amount and nothing at all on timing. The new one gives an amount formula that can depend on execution outputs, plus an explicit expected settlement delay. A solver can now price capital lockup, not just spread.

Amounts are expressed through IFormula, which currently offers Constant(uint256) and Variable(uint256 varIdx). The spec adds a discipline note: if a formula depends on timing, the amount SHOULD decrease with time, so the solver can compute a tight upper bound. Dutch auctions are the intended shape.

Named assumptions

The most honest part of the redesign is the assumption mechanism. A resolver guarantees safety except for conditions it cannot check itself, which it must surface explicitly as a named, optionally parameterized Assumption. A solver MUST validate the assumption — for example against a whitelist — before fulfilling the order.

The Security Considerations section extends this. Resolvers should document implicit assumptions beyond those the ERC already allows, so solvers and auditors can evaluate them alongside the explicit ones. Only chain liveness and censorship resistance, plus the liveness of tokens named in SpendsERC20 attributes, may be assumed silently.

Audits are told what to look for: whether a solver that correctly follows resolver instructions can be led into an insecure position, whether steps can spend solver assets or create solver obligations that were not disclosed, and whether payment paths can be invalidated after the solver has already incurred cost. That last one is the real risk in any fill-first system, and it is stated plainly.

ERC-7930 underneath

Every address in the new ERC-7683 is bytes, not address. That is because of the requires: 7930 line.

ERC-7930 — "Interoperable Addresses", status Review, created 2025-02-02 — defines a binary format for an address that is specific to one chain. It has a long author list including Nick Johnson, Vitalik Buterin and Sam Wilson. The layout is six fields:

// Version              2 bytes   0x0001 for v1, big-endian
// ChainType            2 bytes   a CASA namespace
// ChainReferenceLength 1 byte    MAY be zero
// ChainReference       variable  per the CAIP-350 profile
// AddressLength        1 byte    MAY be zero
// Address              variable  per the CAIP-350 profile

The spec's own example for an address on Ethereum mainnet decodes cleanly:

0x 0001 0000 01 01 14 d8da6bf26964af9d7eed9e03e53415d37aa96045
   │    │    │  │  │  └─ 20-byte address
   │    │    │  │  └──── AddressLength = 0x14 = 20
   │    │    │  └─────── ChainReference = 1
   │    │    └────────── ChainReferenceLength = 1
   │    └─────────────── ChainType
   └──────────────────── Version = 1

Two properties matter. Both length fields MAY be zero, which means the same format expresses a chain with no address and an address with no chain — one encoding for "this contract on this chain", "this chain", and "this account, chain unspecified". And the serialization rules for both the chain reference and the address come from the CAIP-350 profile for the namespace, so non-EVM chains are first-class. In the spec's examples, the Ethereum case uses ChainType 0x0000 with a one-byte reference, while the Solana case uses ChainType 0x0002 with a 32-byte chain reference and a 32-byte address.

This is why the ERC-7683 rewrite claims cross-ecosystem reach. Its Rationale states the standard is intended to be cross-compatible with other ecosystems, standardizing types on EVM chains while using interoperable addresses so orders can refer to accounts outside the EVM address space, and inviting sibling standards for other ecosystems whose intents can be filled on an EVM chain and vice versa.

Anyone who has dealt with chain identification in payment payloads will recognize the problem being solved. We hit the same ambiguity when mapping x402 network bindings across chains: a string like "base" or "solana" is a naming convention, not a type. ERC-7930 is a type.

Spec drift in production

A standard is what implementations do. So we checked.

erc7683.org/spec, the site that presents itself as the standard's documentation, still documents GaslessCrossChainOrder, OnchainCrossChainOrder, ResolvedCrossChainOrder, Output, FillInstruction, IOriginSettler, IDestinationSettler and the Open event. That is the previous draft, presented as current.

The Open Intents Framework contracts are the reference implementation most often associated with the standard. Reading the repository tree on main, no file path contains "7683". The settlement contracts are named InputSettlerBase.sol, InputSettlerEscrow.sol, InputSettlerCompact.sol and OutputSettlerBase.sol, with types called StandardOrderType and MandateOutputType. Neither the old ERC names nor the new ones.

There is a detail in those filenames worth pausing on. InputSettlerCompact.sol and InputSettlerEscrow.sol sit side by side — a resource-lock path and an escrow path, in one codebase. That is exactly the divergence the ERC's Rationale cites as a reason the escrow-first draft had to go. The implementations had already outgrown the standard before the standard admitted it. There is also an InputSettlerEscrowTron.sol, which tells you how much of this problem is really about addressing across heterogeneous chains.

So the state of play as of today: the canonical ERC specifies a resolver interface that, as far as we can verify, nothing yet implements in production; the documentation site specifies a superseded draft; and the leading implementation uses a third vocabulary. None of that means the redesign is wrong. It means the number "7683" currently carries no information about what a system actually does, and any integration decision has to name a draft.

What it means for LLM4Agents

LLM4Agents routes inference and settles per call in stablecoins over an OpenAI-compatible gateway. ERC-7683 is not on that path today. Three things about it are still directly relevant.

// 1

A resolved order is a machine-readable offer

Strip the cross-chain framing and a ResolvedOrder is this: here are the calls to make, here is what they may cost you, here is what you get paid, here is when, and here is what I cannot promise. That is the same object as an x402 402 response — a priced, machine-negotiable demand that an autonomous buyer can evaluate without a human.

The difference is direction and medium. x402 prices an HTTP request; a resolved order prices onchain execution. An agent that can evaluate both is buying capability with one loop instead of two.

// 2

ERC-7930 is the part to adopt now

The resolver interface is a draft nobody ships. The address format is a Review-status ERC that a payments-adjacent standard already depends on, with a long and serious author list. Any system that records "which chain, which token, which account" — billing rows, receipts, refund destinations, webhook payloads — currently invents its own encoding for that tuple.

Adopting ERC-7930 as the internal canonical form for chain-scoped addresses costs little and removes a class of ambiguity we have already run into across fifteen chain bindings.

// 3

Resolver trust is facilitator trust, again

The security model reduces to a whitelist of resolver addresses vetted by audits and time. We have seen this shape before in x402 facilitators and in cross-chain attestation services. It is workable, and it is a governance surface, not a cryptographic one. Anything an agent trusts because an operator allowlisted it should be treated as an operational dependency with an owner and a revocation path.

The threat, such as it is, is modest and worth stating anyway. If intent-based settlement becomes the normal way value moves between chains, then a gateway that only understands direct transfers on a fixed set of chains is a narrower product than one that can accept payment wherever a solver network can deliver it. That is a several-quarter concern, not a this-quarter one — but it is the same argument that made CCTP V2 burn-and-mint settlement worth studying, and this is the competing mechanism.

Staying on the frontier

Concrete steps, in the order we would take them.

First, adopt ERC-7930 internally. Represent every chain-scoped address in billing records, payment receipts and API payloads in the v1 binary format, with a human-readable rendering at the presentation layer. This is a days-scale change with no external dependency, and it makes every later integration cheaper. It also makes receipts unambiguous, which matters for the audit trails discussed in the receipt integrity audit.

Second, pin the draft in every integration note. Anywhere the platform or its docs say "ERC-7683", say which design. Treat erc7683.org as documenting the previous draft until it says otherwise. This costs nothing and prevents an expensive misunderstanding.

Third, do not build new work on IOriginSettler or IDestinationSettler. They are named in the ERC only as history. If an existing integration depends on them, that is fine — it depends on a protocol, not on the standard.

Fourth, prototype a read-only resolver evaluator. A small service that takes a resolver address and a payload, calls resolve over eth_call, and renders the steps, attributes, assumptions and payment schedule as structured JSON. It commits no capital and takes no risk, and it is the exact component needed later to decide whether an order is worth filling. Building it now against a draft is how you find out whether the instruction language is expressive enough before it matters.

Fifth, model gateway credit as an intent. The natural application is not solving for third parties; it is the reverse. An agent holding USDC on one chain that needs gateway credit denominated on another is expressing an intent, and a solver network is one way to fill it. Specify that order shape — steps, payment, assumptions — even before deciding to publish a resolver. Writing the order is the design exercise.

Sixth, treat resolver allowlists like facilitator allowlists. Same policy, same review cadence, same revocation mechanism. Do not let a second trust registry grow with different rules than the first.

The broader lesson is about reading standards rather than reading about them. ERC-7683 spent sixteen months being cited in one form while the document itself was about to become another. The commit log was public the whole time. For infrastructure that has to settle real value, the primary source is the only source.

Pay per call, in stablecoins, over an OpenAI-compatible API

LLM4Agents gives autonomous agents a gateway they can pay for without a human in the loop.

Register an agent