The Cross Chain Intents Standard is meant to unify off-chain messages and on-chain settlement smart contracts to enable sharing of infrastructure, filler networks, and orders across cross-chain bridging and trading systems.
ERC:
The Cross Chain Intents Standard is meant to unify off-chain messages and on-chain settlement smart contracts to enable sharing of infrastructure, filler networks, and orders across cross-chain bridging and trading systems.
ERC:
Hi @marktoda, cross-chain intents, this is an important space to solve on. Thanks for proposing it!
A few technical questions
hey @xinbenlv i’ll try to chime in here.
settlementContract
in the standard. One of the goals of the standard is to allow intent settlement contracts to be opinionated about how they refund fulfilled intents while also committing to some shared language and feature-set. The standard allows fillers to define who they want to decide whether their fulfillment was valid or not, which helps them to give more accurate pricing to users who submit cross chain intent ordersAgreed, in that case please update the title and summary to reflect this scope
It’s less opinionated than you think: it’s more about future compatibility. If there is an attestation field predefined, anyone (future contract implementing your ERC) who doesn’t care about attestation can just leave it unfilled, contracts can ignore them: the behaves like a naive optimistism without challenging mechanism, pretty much centralized trust is the only way. yet anyone (future contract implementing your ERC) who cares about attestation can fill them with challengable optimistism commitments or zkproofs.
Opens up a lot of future compatibility for you.
That said, this is a great idea to work on. congrats on drafting it!
have we think about making it something more general like a UserOps / general contact call
I think what you are proposing here is done by Anoma (see also Anoma: a unified architecture for full-stack decentralised applications, or the recent ethresear.ch post).
Should we consider not only EVM chains but also NO-EVM chains?
0. orderData is a good design for support no-evm chain
/// @notice Tokens sent by the swapper as inputs to the order
struct Input {
/// @dev The address of the ERC20 token on the origin chain
address token;
/// @dev The amount of the token to be sent
uint256 amount;
}
adress token should not be type address
Hi authors of ERC-7683, this is Victor, an EIP editor and current operator of AllERCDevs.
I like to invite you to our next AllERCDevs meeting (online) to present for 10min of your ERCs if you are interested!
AllERCDevs is a bi-weekly meeting for ERC authors, builders and editors to meet and help the drafting and adoption of an ERC. The next one is 2024-04-30 UTC 2300, let us know if this time works for you, I can put this ERC in the agenda, or you can add a response directly at S2E4 AllERCDevs Agenda 2024-04-30 Tuesday UTC2300 (APEC friendly time) · Issue #22 · ercref/AllERCDevs · GitHub
The value proposition of EIP-7683, which aims to standardize cross-chain trade execution, is indeed compelling. The potential to streamline cross-chain interactions by providing a unified interface is a significant step forward. However, there are several areas where this proposal could be further enhanced:
Title and Scope:
Current Limitations:
Scalability and Security Considerations:
Potential for Expansion:
Generalized Intent Execution:
We at Router Protocol are tirelessly trying to solve for chain-abstraction, which is our key area of focus. We are happy to ideate and collaborate to make web3 more simple and usable. In conclusion, while EIP-7683 lays a solid foundation for cross-chain trade execution, its true potential lies in broadening its scope to encompass a wider range of blockchain activities. Addressing scalability and security for Rollups and enabling more complex intent executions could transform this proposal into a cornerstone of cross-chain interoperability.
Cross-chain execution systems implementing this standard SHOULD create a custom sub-type that can be parsed from the arbitrary
orderData
field.
If this struct is to be signed as EIP-712 data (the ERC doesn’t specify), the orderData
will show up as uninterpreted bytes (i.e. a hex string) to the end-user, resulting in bad security (verifiability) and usability.
This kind of pattern is useful though, and it has come up before in the context of making EIP-1271 signatures account-bound and replay-protected. I believe an EIP-712 extension may be necessary to properly address this.
I’ve proposed an extension of EIP-712 that would enable better support for implementation-specific orderData
:
The current demand in the ecosystem is for more generalized standards that can support a broader range of cross-chain activities. For instance, enabling users to purchase a token on one chain and automatically stake it in a protocol on another chain would provide greater utility
We designed ERC7683 on the assumption that the majority of cross chain user intents are to (1) do some arbitrary action on the origin chain and then (2) swap into a token on the destination or vice versa. We therefore left room in the bytes orderData
to encode arbitrary action. For example, the arbitrary data to execute (2) could be encoded like so into the CrossChainOrder
struct:
// Bytes field from CrossChainOrder struct containing the ERC7683-compliant ResolvedCrossChainOrder data along with extra data.
bytes orderData = order.orderData;
struct DestinationAppData {
address target; // contract I wish to call on the destination chain after my intent is fulfilled
bytes targetData; // calldata I want to execute on target contract on destination chain
}
(DestinationAppData appData, ResolvedCrossChainOrder crossChainOrder) = abi.decode(order.orderData, (ImplementationData));
Now, I can support a user flow where my user submits an intent containing the above target
and targetData
in addition to the intent-specific parameters like {input}/{output}{Token}/{Amount}
, destinationChainId
, originChainId
, initiateDeadline
, depositor
, etc. The user’s chosen intent settlement network would then need to ensure that when a solver fulfills the user’s intent, that the fulfillment triggers a call to target
with targetData
on behalf of the depositor
.
In my opinion, enabling these generalized cross chain user flows is a feature that intent settlement networks can choose to offer or not, and there is room in ERC7683 to support this if the intent principal chooses a supporting settlementContract
. The Across network supports this, for example.
Allowing users to express complex intents and having these intents executed by fillers (entities with the necessary funds and capabilities) can open up a myriad of possibilities. Fillers, essentially acting as service providers, could execute a wide range of instructions, from simple swaps to intricate financial operations, in exchange for fees and potential refunds.
Now, this does not handle the case naturally where someone just wants to send a message, involving no token transfers, cross-chain. But you and I both seem to agree that all cross chain user intents will involve a token swap in some way
So, our long term vision is very much aligned with supporting generalized intent execution but we acknowledge that the biggest use case of intents will be to do (1) and (2) above. Therefore this ERC was written to contain the minimum set of parameters to support this cross-chain swaps plus arbitrary action without being too generalized as to be meaningless.
On the rollup security issue, I do not agree that rollup security is not addressed in the ERC parameters, though it is not obvious. By including the settlementContract
in the CrossChainOrder
struct, the user (or realistically, dApp) has freedom to decide which mechanism will settle their intent and therefore ensures that their intent is fulfilled. In my opinion it is the settlement contract’s responsibility to handle rollup liveness securely. By forcing users to specify the settlement contract, this ERC provides a foundation for a settlement marketplace where settlement protocols compete to abstract rollup security away from users. In my opinion the most successful settlement networks will be the ones who most efficiently remove finality risk while fulfilling user intents and also refund honest solvers.
Hey y’all, small point of feedback - in the resolve
function, why does the return type ResolvedCrossChainOrder
repeat all of the fields on the parameter CrossChainOrder
? is there ever a circumstance where the repeated fields would or should change between the input CrossChainOrder
and the output ResolvedCrossChainOrder
? in practice, copying these fields from the input type to the return type makes developing smart contracts that implement this standard unnecessarily uglier to write.
repeated fields:
/// @dev The contract address that the order is meant to be settled by.
/// Fillers send this order to this contract address on the origin chain
address settlementContract;
/// @dev The address of the user who is initiating the swap,
/// whose input tokens will be taken and escrowed
address swapper;
/// @dev Nonce to be used as replay protection for the order
uint256 nonce;
/// @dev The chainId of the origin chain
uint32 originChainId;
/// @dev The timestamp by which the order must be initiated
uint32 initiateDeadline;
/// @dev The timestamp by which the order must be filled on the destination chain
uint32 fillDeadline;
I would cut the repeated fields from ResolvedCrossChainOrder
to change the type to something like ResolvedOrderData
:
/// @title ResolvedOrderData type
/// @notice An implementation-generic representation of an order
/// @dev Defines all requirements for filling an order by unbundling the implementation-specific orderData.
/// @dev Intended to improve integration generalization by allowing fillers to compute the exact input and output information of any order
struct ResolvedOrderData {
/// @dev The inputs to be taken from the swapper as part of order initiation
Input[] swapperInputs;
/// @dev The outputs to be given to the swapper as part of order fulfillment
Output[] swapperOutputs;
/// @dev The outputs to be given to the filler as part of order settlement
Output[] fillerOutputs;
}
then
/// @notice Resolves a specific CrossChainOrder into ResolvedOrderData
/// @dev Intended to improve standardized integration of various order types and settlement contracts
/// @param order The CrossChainOrder definition
/// @param fillerData Any filler-defined data required by the settler
/// @return ResolvedOrderData hydrated order data including the inputs and outputs of the order
function resolve(CrossChainOrder order, bytes fillerData)
external
view
returns (ResolvedOrderData);
of course, we still need to pass in the entire CrossChainOrder
and fillerData
as parameters (not just the raw orderData
bytes) as ResolvedOrderData
resolution may depend on additional fields
Hey @anna-carroll thanks for taking a look at this, I think this is a very fair response. I think the idea behind ResolvedCrossChainOrder
is that any user of a settlement protocol could glance at this struct in isolation to determine the shape of their intent order with respect to the settlement protocol. The expected user in this case would be a dApp developer or filler.
But I also think its expected that this same user would already know the shape of the CrossChainOrder
, having just created/filled an intent order using it, and then would only want to know the additional new params in ResolvedCrossChainOrder
that are relevant for the specific settlement contract.
Does this standard enforce / assume that the settlement contract has to live on the source chain of the intent?
What about a settlement model where the intent can be settled on the destination chain or maybe an arbitrary chain which acts as the source of truth for both intent order as well as settlement
Thanks for pushing standardization on this very important topic! Here are some of my notes as part of conversations that I had with various folks over the last couple weeks about this topic:
CrossChainOrder
so that the intent has more standardized fields and requires less divergent tooling for parsing?orderData
schema, I was wondering if there is opportunity for standardization without preventing experimentation. I was thinking of a mechanism like in EIP191 that has a version byte that allows the EIP to formalize orderData
like a simple limit order, or an exact order.
orderData
is to have less variability in permit2 witnesses
and type strings, and allow UIs/wallets to optimize the experience for the standard ones.orderData
and settlementContracts
I imagine would be helpful in conveying the power of this standard.function fill(CrossChainOrder order, bytes fillerData)
. I suspect that there are systems where there is no need to change contract state to record the fill which is why it was omitted, but I’d imagine that standardizing this for the settlement systems that do would be still beneficial?Wouldn’t you still need some contract to process the intent on the origin chain since that’s where the user’s tokens reside? The standard only imposes that some contract exists on the origin to process the intent, not that that’s the only point of interaction or validation.
- I agree that arbitrary post-fill actions with the tokens are a highly desired feature for cross-chain intents. What do you think about making it an explicit field on
CrossChainOrder
so that the intent has more standardized fields and requires less divergent tooling for parsing?- While different intent generation and settlement systems could require vastly different
orderData
schema, I was wondering if there is opportunity for standardization without preventing experimentation. I was thinking of a mechanism like in EIP191 that has a version byte that allows the EIP to formalizeorderData
like a simple limit order, or an exact order.
- Another benefit of pushing for more standardization on
orderData
is to have less variability in permit2witnesses
and type strings, and allow UIs/wallets to optimize the experience for the standard ones.
The way the standard is currently designed, the explicit fields are only the ones that would apply to all implementing protocols. It is intentionally minimalist to ease the burden on implementing protocols and fillers. Adding post-fill actions would mean that some protocols might not implement part of the official standard and be forced to reject intents with that field, which might be confusing. Alternatively, if that field was required to be handled by all protocols implementing the standard, it would be limited to protocols that support it.
Jumping off your points, maybe the right path is to have an extension specification, where extensions that take advantage of orderData could be built and encoded in a specific way whereby each extension is, by default, not mutually exclusive with other extensions. For instance, maybe there’s an extension to encode a limit order and another extension to do post-fill actions. The former could be assigned identifier 0x01 and the latter assigned the identifier 0x02. If my protocol supports both, it could parse the order data to identify the data for each extension using the identifier. If my protocol doesn’t support one of them, it could fail during the parsing process. While something like this is doable, it is complex to specify and implement. Maybe it belongs in a separate ERC.
Thoughts? In my opinion minimalist ERCs often have the best chance of gaining adoption and being useful.
- Probably doesn’t need to be in the EIP itself, but the high generality of the standard made it a bit hard to wrap your head around it for folks who are not in the weeds. Pseudo-code examples of
orderData
andsettlementContracts
I imagine would be helpful in conveying the power of this standard.
Totally agree. More can and should be added there.
- I’m curious if you thought about a standard function for filling? I.e. a
function fill(CrossChainOrder order, bytes fillerData)
. I suspect that there are systems where there is no need to change contract state to record the fill which is why it was omitted, but I’d imagine that standardizing this for the settlement systems that do would be still beneficial?
Definitely. It’s something I’m still wrestling with. The issues I see with a fill function:
Thanks for pushing a standard for cross-chain transaction, we (paywithglide.xyz) are looking to adopt this standard to work with our cross-chain execution system
Per the Input struct and signature based initiate function, it seems the standard assumes the input token is always ERC20? Curious if this standard can work with native token as swapper input token?
/// @notice Tokens sent by the swapper as inputs to the order
struct Input {
/// @dev The address of the ERC20 token on the origin chain
address token;
/// @dev The amount of the token to be sent
uint256 amount;
}
I definitely sympathize with the desire in keeping the EIP simple and understand that additional specificity, even if for optional extensions can be counter-productive. I admit that I generally bias towards the technical implementation considerations of standards, and less so the social aspects of them.
Adding post-fill actions would mean that some protocols might not implement part of the official standard and be forced to reject intents with that field, which might be confusing.
Wouldn’t that already be the case if post-fill actions are specified in orderData
, i.e. they have to reject the intent since they do not know about the orderData
schema?
maybe the right path is to have an extension specification
I do rather like the idea. To be honest, it doesn’t even have to be in the official specification, even a loose place for coordination I think would help implementors a lot to be compatible with each other. I do think it’s going to be hard to find a easily composable way of doing so. I.e. how do you express both an intent’s orderData
implements both 0x01 and 0x02? You would probably have to then abi.encode both the identifiers and the data itself which can be a lot of data wrangling. We at Hyperlane had a somewhat similar problem when designing our HookMetadata
which are also arbitrary bytes that different hooks can interpret differently, choosing the right trade-off between simplicity and expressiveness was definitely pretty hard.
Only a fraction of that information is needed at fill time.
Thanks for the context on this one. Now seems obvious in hindsight
You could just add an arbitrary bytes field and leave it up to the filler/protocol to figure all of this out but then that means the standard isn’t really saving the filler/protocol much in that case, while imposing this bytes field on all the protocols that don’t need this contextual info
That’s kind of the case already if this is not specified in the spec, right? The filler will need protocol/settlement contract specific context to know how to fill on the destination chain. This does seem very similar to the above orderData
question. I think in this case, an empty bytes
field seems maybe worth the cost for protocols that don’t need this info, but you are right that the benefit from slightly more structural prescription might still not be worth it.
I think for both cases, maybe enough standardization at the tooling level is sufficient, and does not need to reflect itself in the spec itself.
Why is the chainID not part of the Input struct (in ResolvedCrossChainOrder) and is a top level variable?
In a chain abstracted world user balances will be pulled parallely from several chains for the same order. So it makes sense have different chainIDs available for different inputs.
For context, ERC-5792 is also updating where they will specify chainID on a per-call basis and not use the same chainID for all the batches. Quote from “Update EIP-5792: make atomicBatch chain-specific per-batch, not per-call (both in protocol messages and in normative prose) by bumblefudge · Pull Request #8626 · ethereum/EIPs”