Boundary of consent
Suggest an editOn-chain state changes between the moment a user signs and the moment the transaction executes. A swap is signed at one price and runs at another, and a position is signed as liquidatable and is topped up a slot later. The exact result the user expected is stale by the time the transaction arrives, so the user cannot consent to an exact result at all.
The result moves while the transaction waits
Suppose a user swaps SOL for USDC. The client computes the output they will get, and the transaction goes out.
In the moments before it executes, other swaps land in the same pool and move the price. The user gets a little less than computed, or far less if someone saw the transaction coming and traded in front of it.
The program has two bad options. It can check that the output equals the computed one. Then it fails almost every swap, because the exact number is never the same twice. Or it can check nothing and give the user whatever comes out. Then anyone can move the price against them.
The same happens to every action that runs against moving state. An order signed at one price meets another price, and an oracle price that was fresh at signing is a minute old at execution.
Sign the boundary, not the result
The user signs the limits together with the action, and those limits are the boundary of consent: the maximum input, the minimum output, the limit price, the maximum oracle age, the deadline. The program executes inside those limits and fails outside them.
This means the boundary does not predict the result but defines what the user agrees to. For a swap this boundary is called slippage. The user does not promise that the price will stay, but says how far it may move before the swap is no longer what they wanted.
The check has to happen on-chain, in the same transaction as the action, because the state moves after any check the client could do. A limit that the client verified before sending proves nothing by the time the transaction lands.
Jupiter: the user signs the minimum, not the quote
Jupiter is a swap aggregator, and its quote is a prediction.
The client computes the expected output and shows it to the user. But the transaction the user signs carries the minimum output they accept. The swap executes while the result stays above that minimum, and fails when it drops below. A failed swap costs a fee, and the user keeps their tokens. A swap without a minimum would succeed at any price, including the one an attacker arranged.
Drift: an order carries its price and its deadline
Drift is a perpetuals exchange, and an order on Drift is a set of boundaries.
A limit order names the worst price the trader accepts. A market order goes through an auction with a start price and an end price, so even a market order has a range it will not leave. An order can carry a deadline after which it cannot fill. The trader does not know which maker will fill them, in which slot, or at which price inside the range. They signed only the range, and no fill outside it can happen.
Oracles: a price has an age
A program that reads an oracle price gets a value and a timestamp. The value may be fresh or minutes old, depending on when the last update landed.
So the consumer sets a maximum age, and a price older than that is refused, whatever the number says. Drift refuses to fill on a stale price, and Pyth's library takes the maximum age as a parameter of every read. Here the boundary is on the age of the price, not on its value.
Sign what you agree to, not what you expect
The user never knows the state at the moment their transaction runs, so an exact expected result is useless. Every action that runs against moving state gets a boundary signed with it: how much at most, how little at least, how old, how late. Inside the boundary the transaction runs. Outside it the transaction fails, and that is what the user asked for.