A formal-ish definition for Intents

Hello Skippers and Skippettes,

Intents or preferences (or conditionals, or partials…) have been a hot topic recently as they are appearing in many places: Flashbots, DYDX, Anoma, EIP 4337, Skip?.. Intents are supposed to provide users with an alternative, more general way to transact where instead of specifying the exact message calls which is the current norm, they only specify certain preferences for what they want and allow someone to fulfill those preferences on their behalf. It has been proposed that they may prove useful for a few use cases. A notable, broad one is to move complexity off-chain for various applications. This is because instead of running some combinatorial matching algorithm on-chain, the on-chain logic for intents just needs to verify that the preferences of the user were fulfilled.

Bc of their relevance to a lot projects, @barry, @ksk and I were discussing whether there was a reasonable, more formal definition and thought it’d be nice to move the conversation to here instead in case anyone had differing or complimentary thoughts (or questions!).

The way I have been thinking about them is that an intent is a tuple, \iota = (B, E, T) where:

  • B is a subset of all possible states Q and represents the supported begin states specified by the intent placer (eg, I don’t want to start with less than 100 $ETH)
  • E is a subset of all possible states Q and represents the supported end states specified by the intent placer (eg, I want to end 20 $SHIB)
  • T is a set of “preferred” sequences of transactions that is used to bring the begin state to the end state (eg the user may not want sandwich patterns, or txs coming from a specific address)

The state transition function s: Q \times \mathcal{T} \to Q takes a transaction and begin state as input and outputs the resulting state.

An intent is fulfilled if for some begin state q_0, and sequence of transactions t = t_1, ..., t_n, where q_n = s(s(...s(s(q_0, t_0) , t_1), ...) t_n), is the end state, q_0 \in B, q_n \in E, t \in T

This was pretty similar to @ksk (and I think Tarun’s) definition which used “execution trace” to describe the sequence of state transitions and the preferences over them. Although theirs was (expectedly) more concise and polished.

We can expand this definition to describe Intent Clearing or solving. We know that there is only a valid solution for clearing a set of intents \iota_0, ..., \iota_m iff B_0 \cap ... \cap B_m \neq \emptyset, E_0 \cap ... \cap E_m \neq \emptyset, and T_0 \cap ... \cap T_m \neq \emptyset. This is because, a non-empty intersection means there exist preferences in common. We can also combine intents into a single meta-intent \iota' = (B', E', T') with similar logic B_0 \cap ... \cap B_m = B', E_0 \cap ... \cap E_m = E', and T_0 \cap ... \cap T_m = T'

Of course this doesn’t tell us whether or not the sequence of transactions used to fulfill the intents are favorable to the solver though, just that there exists a possible solution (eg the solver giving away all their $ETH fulfills the meta-intent, but they’d probably never want to actually do that - unless they were super charitable? ).



Adding some more commentary to @PossibilityResult’s definition:

We need the following primitives to define an intent:

  • A state space for the blockchain \Sigma

  • A space of transactions T

  • A set of n users, each of whom pick transactions t_i \in T

  • A state transition function \rho: \Sigma \times T \rightarrow \Sigma that takes current state and transaction to the next state

Given an ordering of user transactions \pi \in S_n, and a set of transactions t = (t_1, \dots, t_n), we have the following definition of the execution trace of the transactions:

E(t, \pi) = \sigma_1, \rho(\sigma_1, t_1), \dots, \rho(\rho(\dots \rho(\sigma_1,t_1), t_n))

Then, a we can define a boolean-valued function \xi_i(E(t,\pi)) over orderings and transactions that evaluates to 1 if the ordering is intent-satisfying for user i and 0 if intent-violating. We call the vector \xi = (\xi_1, \dots, \xi_n) the intent-satisfaction vector. This implicitly induces a preference relation over orderings \succ where an ordering \pi \succ \pi' if and only if \xi > \xi'.

Note that here I am placing a preference over orderings. I like this definition because we can directly compare two orderings via the intents that they satisfy. However, a natural critique is that the intent should only be a function of the user’s output from the ordering (the user doesn’t really care about the ordering itself, but they do care about their own output from the transaction). To collapse this into a preference on the user’s transaction, we can slightly modify the above definition in a restricted setting of DEX swaps:

Instead of picking transactions t_i \in T, the user picks a pair (t_i^{in}, t_{i}^{out, desired}) as in Elijah’s model, where t_{i}^{in} is the amount the user provides to a swap, say, and t_{i}^{out, desired} is the amount they want to receive.

Then, for any ordering \pi of the transactions, the final state \sigma_n of the DEX after the input transactions have been executed, give rise to payouts t_i^{out,real}. The ordering \pi is intent satisfying for user i if t_i^{out,real} \geq t_i^{out, desired} (that is, \xi_i(E(t, \pi)) = 1 if the above inequality is satisfied).

The point is that putting a preference relation over the execution trace is sufficient to specify an intent. However, this is pretty suboptimal from a UX perspective, as the user doesn’t care about the entire trace.

Some questions to think about:

  1. What are natural intents that users may want to express (as an example, the swap one)?
  2. What are simple ways to express them that do not involve providing unreasonably large preferences?
  3. Are some kinds of intents actually suboptimal to express from a welfare perspective? What if everyone expressed that their slippage limit should be zero on a DEX, for example (that is, they were not willing to tolerate any price impact)? This would deadlock the system I would imagine.

Couple scenarios where the actor creating the intent may care about the exact execution trace:

  • Some risk is incurred along the way that cannot be shifted to the executor, eg. moving an NFT through an unaudited bridge.
  • There are intermediating entities that for moral or legal reasons the intent creator wants to avoid, eg. touching a sanctioned address.

That said, if we wave this away the requirements on our proof system are simplified substantially.

1 Like

I’m definitely of the opinion is that execution trace inclusion in the definition is a nice to have – or maybe that it’s the right way to define it.

But that it’s not super important in the first version of these systems.

As we’ve been thinking about intents internally, I think a big question is what the intersection looks like between:

  1. Intents that are useful for end users to express
  2. Feasible for searchers to discover and fulfill
  3. provide better UX for end users as intents than as fully specified transactions inside some purpose built protocol

A very simple intent that many end users seem to want is the easy swap: User holds x units of token A and wants to swap them for at least y units of token B.

Let’s formalize how a matching market for such an intent might function. There is a universe of n tokens, and m users who submit transactions of the form (x_{ij}, y_{ik}) where i= 1,\dots, m represents the users and j, k \in [n] \times [n] represent the token pairs. This is to be interpreted as: User i is requesting to provide x_{ij} units of token j for at least y_{ik} units of token k. For notational ease, we can therefore define two matrices: X \in \mathbb{R}^{m \times n} and Y \in \mathbb{R}^{m \times n} which are the input-output pairs that the users are requesting.

On the other side of the market are S solvers. These agents are responsible for filling the intents that the users satisfy. The way they do this is by engaging in transactions in a set of markets: decentralized exchanges, centralized exchanges, and other off-chain liquidity they might find. Each of these agents s =1,\dots, S posts an amount of liquidity \ell_{jk}^s(x) that they are willing to satisfy in each pair of tokens j,k. This is to be interpreted as the maximum amount of token k that solver s is willing to provide for receiving x units of token j.

We now define the matching matrix M \in \{0,1\}^{m \times S}, where M_{ij} = 1 if solver i is matched with user j and 0 otherwise. Given any matching, the amount of token k that user i receives over all solvers is given by \sum_{s=1}^{S} \ell_{jk}^s(x_{ij}) M_{is}

This allows us to set up the following optimization problem:

\begin{align*} \text{maximize } & u(M) \\ \text{subject to } & \sum_{s= 1}^{S} \ell_{jk}^s(x_{ij}) M_{is} \geq y_{ik} \text{ for all i} \\ \text{subject to } & 0 \leq \sum_{i=1}^{m} M_{is} \leq 1 \end{align*}

where the problem data are (X,Y) and \ell_{jk}^s(x), and the decision variable is the matching M, and the final constraint is that each solver is matched to at most one user. The utility function u(\cdot) could characterize any excess value that is transferred to the users above what their desired intent was. Note that we have converted the integer constraint M \in \{0,1\}^{m \times S} to final inequality constraint. Note that when x_{ij} =0, we want \ell_{jk}^s(x_{ij}) = 0, and therefore, the constraint is satisfied trivially.

Under certain conditions on the utility function, this problem may be cast as a convex optimization problem that can be efficiently solved, and whose optimality may be certified for any (X,Y).