I used to think moving money between chains meant running a bridge. Lock it here, mint a wrapped IOU there, check in the morning to see if the relayer ghosted you overnight. ERC-7683 made that mental model obsolete with one move: don't bridge, just sign what you want. A solver fronts you the funds on the other side in seconds and sorts out the plumbing later.
The risk didn't disappear. It got a job.
That's the honest version of "one signature, any chain." Most explainers stop at the magic trick, you sign a message, money appears on another chain, wow. Nobody walks through what's actually priced into that appearance, or who's structurally allowed to be the one making it appear. That's the part worth writing down.
The old bridge model puts you in the risk chain. You lock USDC on Arbitrum, a contract emits an event, a relayer (which might be a multisig, might be a light client, might be some dude's cron job) watches for that event and mints or releases the equivalent on Base. You wait. You pay gas on the destination chain to claim it. If the relayer is slow, compromised, or just offline, you're the one holding a receipt for money that isn't there yet. Bridges made you the risk manager for infrastructure you don't control.
Intents flip who's holding the bag. Under ERC-7683, ratified in early 2025 and now implemented across Across, UniswapX, and Eco, you sign a single off-chain message describing an outcome: "I have USDC on Arbitrum, I want ETH on Base." A solver sees that signed intent, decides it's profitable, and fronts you ETH on Base out of its own inventory, right now, before your funds have even moved on Arbitrum. You never touch the destination chain. You never pay its gas. The solver gets reimbursed later, on origin, after it proves the fill happened.
You went from operating a bridge to signing a wish and trusting a stranger's balance sheet to grant it. That's a genuinely better interface. It is not a genuinely risk-free one, it's a repriced one, and the price is the interesting part.
The standard is smaller than the hype around it. There's one struct the user signs, and two interfaces that move it through the system.
struct GaslessCrossChainOrder {
address originSettler; // contract that escrows input on the origin chain
address user; // who signed this intent
uint256 nonce;
uint256 originChainId;
uint32 openDeadline; // by when the origin must open it
uint32 fillDeadline; // by when a solver must fill on destination
bytes32 orderDataType; // type tag for orderData
bytes orderData; // the actual "what I want" (inputs, outputs, dest chain)
}
interface IOriginSettler {
// user-signed, gasless: solver submits it; escrows inputs, emits Open(...)
function openFor(GaslessCrossChainOrder order, bytes signature, bytes originFillerData) external;
}
interface IDestinationSettler {
// solver fronts the output on the destination chain; emits Filled(...)
function fill(bytes32 orderId, bytes originData, bytes fillerData) external;
}Notice what the user never does. They never call openFor, a solver does. They never touch IDestinationSettler at all, they never even know which solver filled it. The user's entire job is producing a signature over GaslessCrossChainOrder. Here's roughly what that looks like without needing an import statement or a wallet UI, just the shape of it:
// walletClient is an already-connected viem WalletClient
// orderTypes describes GaslessCrossChainOrder as an EIP-712 typed struct
const signature = await walletClient.signTypedData({
domain: { name: 'ERC7683', version: '1', chainId: originChainId },
types: orderTypes,
primaryType: 'GaslessCrossChainOrder',
message: {
originSettler,
user: walletClient.account.address,
nonce,
originChainId,
openDeadline,
fillDeadline,
orderDataType,
orderData, // encodes inputs, outputs, and the destination chain
},
})That's the entire user side of a cross-chain swap. No destination-chain interaction, no gas token juggling, no "bridge and wait." Fifteen lines and you're done. Everything past this point is somebody else's problem, specifically a solver's.
Here's the part the UI hides on purpose because it's genuinely boring to watch, but it's where all the actual work happens.
Once your signed order gets broadcast (usually to a solver mempool or an off-chain relay), it's an open invitation. Some number of solvers, N of them, look at your order and independently price it. One of them submits openFor on the origin chain, escrowing your input and emitting Open. That same solver (or whichever one wins the fill race, implementations vary) immediately calls fill on the destination chain, sending you your ETH on Base out of its own pocket, and emits Filled. From your perspective this all happens in seconds. From the solver's perspective, the clock just started on getting its money back.
The origin settler doesn't reimburse the solver instantly. It reimburses after it's convinced the fill actually happened, via whatever verification path that specific 7683 deployment uses (an optimistic challenge window, a proof relay, a message bridge). That gap between "solver fronts the funds" and "solver gets reimbursed" is where every dollar of risk in this system lives. Call that gap t.
A solver quoting your order isn't quoting the exchange rate. It's quoting the exchange rate plus three things it's eating on your behalf:
quote(V) = c * V * t + rho(V) + mu
where V is the amount the solver fronts you on the destination chain, c is the solver's cost of capital per unit time (what it's giving up by having V locked up instead of deployed somewhere else), t is the reimbursement delay, rho(V) is a bridge-risk premium (roughly p_fail * V, the expected loss if the reimbursement leg never lands, plus a risk-aversion load on top), and mu is straight margin.
The user's effective price is V + quote(V). You're paying for time you no longer wait through and risk you no longer carry, and that's a fair trade, but it is a trade, not a gift.
type Quote = {
carry: number // c * V * t, the cost of tied-up capital
risk: number // rho(V), expected loss from a failed reimbursement
margin: number // mu, pure profit
total: number
}
function solverQuote(V: number, t: number, c: number, pFail: number, mu: number): Quote {
const carry = c * V * t
const risk = pFail * V
return { carry, risk, margin: mu, total: V + carry + risk + mu }
}
function auction(V: number, t: number, pFail: number, solvers: { c: number, mu: number }[]) {
const quotes = solvers.map(s => solverQuote(V, t, s.c, pFail, s.mu))
const winner = quotes.reduce((best, q) => q.total < best.total ? q : best)
return { quotes, winner }
}Run N solvers through auction and something Bertrand-competition-shaped happens: the winning quote gets driven toward the second-cheapest solver's actual cost, c * V * t + rho(V), as N grows. Margin, mu, gets competed toward zero. That's real and it's the genuine consumer surplus intents create over a monopoly bridge operator charging whatever it wants.
But look at what doesn't go to zero as N grows. The carry and the risk premium don't compete away, because they're not margin, they're cost. More solvers racing for your order gets you a cheaper price, it does not get you a cheaper t or a lower p_fail. Crank the reimbursement delay or the bridge failure probability and the quote balloons regardless of how many solvers are bidding. Competition compresses the slice a solver keeps for itself. It does not compress the slice the market has decided this particular risk is worth.
Here's the constraint that makes this a capital business and not a routing business: to fill your order in seconds, a solver has to already hold the destination asset before your order exists. There's no time to go acquire ETH on Base after your intent shows up, the entire value proposition is speed. So a solver competing on chain B needs idle inventory I_B sitting there, earning nothing, just so it can react instantly when an order arrives.
Now multiply across chains. A solver that wants to compete on Arbitrum, Base, Optimism, Polygon, and whatever else adopted the standard needs I_B on each of them, simultaneously, at all times, sized to whatever flow it wants to capture. Total idle capital required is the sum across every chain it plays on, and every dollar of that sum is a dollar not earning c somewhere else. That's not overhead, that's the actual product. A solver is, underneath the intent-fulfillment branding, a market maker running a permanent multi-chain inventory book and getting paid a spread for the privilege.
Say it plainly instead of dancing around it: the interface is permissionless, the participant set is not.
Anyone can technically submit a fill. Almost nobody can profitably submit a fill, because c and rho aren't the same number for every solver. A well-capitalized solver borrows cheaper, so its c is lower. It diversifies bridge exposure across more routes and more volume, so its effective rho per order is lower too, it's amortizing the same fixed monitoring and insurance cost over way more fills. A small solver trying to compete on the same order faces a higher c, a higher rho, and a hard wall it literally cannot clear: the total inventory sum of I_B across every chain it would need to be competitive on. That's a fixed-capital barrier, not a skill gap. You don't out-code your way past not having the money.
So the Bertrand competition that drives mu toward zero is real, but it's competition among the solvers who cleared the capital wall, not among everyone. "Permissionless" describes who can call the function. It doesn't describe who wins the auction, basically ever. What you get is a small number of well-capitalized fillers competing hard on margin while everyone else is priced out of the game before the first bid. That's not a flaw in ERC-7683, that's just what a capital-intensive service business looks like when you strip the crypto branding off it.
If you want to know where the actual trust in this system sits, it's not the user's signature. The signature is just "I authorize this outcome," it's not a promise anyone has to keep to you specifically, the solver already gave you the money before your signature mattered to it at all.
The trust hinge is the reimbursement leg: the origin chain pays the solver back only after it believes the fill happened on the destination chain. How it forms that belief is the whole security model, and different 7683 implementations answer it very differently, an optimistic window where anyone can dispute a false claim, a proof-based verification through a message bridge, a trusted attester. Each of those has a different p_fail, and p_fail is doing real work in the pricing math above. "Trustless cross-chain swap" undersells what's happening, you're trusting the specific verification primitive wired into that settler, and you're trusting it every time, whether you notice it or not.
Intents are the right interface for anything that isn't a human clicking a bridge UI, which increasingly means agents. State the goal, sign it once, let a market of solvers race to execute it, don't make the agent orchestrate a bridge relay itself, that's brittle and slow and it's exactly the kind of deterministic plumbing an agent shouldn't be improvising in real time. At B3 the version of "an agent moves value across chains" that actually works looks like this: sign the intent, walk away, check for the Filled event. That's the correct shape.
But don't let the good interface talk you out of noticing the bad news underneath it. The risk that used to sit with bridge operators and impatient users now sits with a small number of solvers who can afford to hold inventory everywhere and eat bridge risk professionally. That's a healthier place for the risk to live. It is not a decentralized place for it to live, and pretending otherwise is the one thing every intents explainer seems to agree on skipping.
"One signature, trustless" oversells this. What you're trusting is the fill-proof mechanism specific to whichever settler you're routed through, and that mechanism varies enough between implementations that quoting one p_fail number for "ERC-7683" broadly would be a lie.
The pricing model above, c * V * t + rho(V) + mu, is the shape of the price, not a live quote. Real solvers are also pricing MEV, gas on both legs, hedging costs, and rebalancing across chains. I simplified to the three terms that explain the recentralization story, not to give you a spreadsheet.
Adoption claims move fast in this space. Check erc7683.org for the current list before you repeat "Superchain, AggLayer, Elastic Chain" as settled fact in a pitch deck, it was accurate when I wrote this and standards adoption is not static.
Intents are how an agent should move value. Sign the goal, let the market execute it. Just know exactly which market you're handing it to, and who's structurally allowed to show up.
Sources: