Skip to content

Commit 3cfedec

Browse files
committed
design: on-chain quotes
1 parent e50c5ad commit 3cfedec

File tree

2 files changed

+198
-2
lines changed

2 files changed

+198
-2
lines changed

.cspell/custom-dictionary.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,17 @@ hashv
66
idls
77
keccak
88
Linea
9+
Mezo
910
permissionless
1011
permissionlessly
12+
permissionlessness
1113
pubkey
1214
Pubkey
15+
Redoc
1316
Ruleset
1417
Rulesets
1518
runtimes
1619
Solana
1720
struct
1821
structs
1922
Unichain
20-
Redoc
21-
Mezo

design/02_On_Chain_quotes.md

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
# Executor On-Chain Quotes
2+
3+
# Objective
4+
5+
Some would-be Executor integrators may need on-chain quotes as they do not necessarily control the end-user flow or integration API to their contracts. This design proposes a solution which can allow for the on-chain resolution they require without sacrificing the original motivations of the Executor design.
6+
7+
## Runtime Support
8+
9+
- [x] [EVM](./evm/)
10+
- [ ] [SVM](./svm/)
11+
12+
# **Background**
13+
14+
The initial [Executor](../README.md) design requires passing a quote to the on-chain contract which was intended to be passed by the off-chain caller, fetched from an Executor service provider. This quote must comply with a specific [header format](../README.md#off-chain-quote) but may otherwise contain any data specified by the Relay Provider. It was also recommended to perform the gas calculation off-chain. All this was done in service of reducing operational costs and on-chain complexity. Notably, the quote contains an expiry - the Executor on-chain contract enforces that a quote can not be used after its expiry.
15+
16+
This approach worked for use cases where the UI can be changed to accommodate the new requirements. However, some integrators are composing with other protocols with pre-established APIs. They require an approach which only relies on on-chain state.
17+
18+
EVM integrators may be familiar with a common pattern used by other on-chain services of having one `view` function to quote and another `payable` function to execute. For example, [NTT](https://github.com/wormhole-foundation/native-token-transfers) requires this on-chain pattern in its [Transceiver](https://github.com/wormhole-foundation/native-token-transfers/blob/c4db04fcbee08dd474d40c9bc121dbb701b3a535/evm/src/interfaces/ITransceiver.sol#L49-L73) in order to split a `msg.value` across multiple Transceivers.
19+
20+
# **Goals**
21+
22+
- Provide a new mechanism for requesting execution on-chain that is compatible with the existing Executor contract and does not require additional parameters to be passed from off-chain.
23+
- Do not substantially increase the operational cost or complexity of operating an Executor Relay Provider.
24+
- Maintain compatibility with the existing Executor design. This includes the key principles of permissionlessness and immutability.
25+
26+
# Non-Goals
27+
28+
- Support the same on-chain API as another relaying service or a particular EIP.
29+
- Pricing mechanisms for generating quotes or appraising relay costs.
30+
- Supporting non native gas token payments.
31+
32+
# **Overview**
33+
34+
```mermaid
35+
sequenceDiagram
36+
actor OffChain
37+
OffChain->>Integrator: quote(...)
38+
Integrator->>ExecutorQuoterRouter: quote(quoterAddr, ...)
39+
ExecutorQuoterRouter->>ExecutorQuoterRouter: lookupQuoterImplementation
40+
ExecutorQuoterRouter->>ExecutorQuoter: quote(...)
41+
ExecutorQuoter->>ExecutorQuoter: custom logic
42+
ExecutorQuoter-->>ExecutorQuoterRouter: payee, value
43+
ExecutorQuoterRouter-->>Integrator: value
44+
Integrator-->>OffChain: value
45+
OffChain->>Integrator: execute{value}(...)
46+
Integrator->>ExecutorQuoterRouter: requestExecution{value}(quoterAddr, ...)
47+
ExecutorQuoterRouter->>ExecutorQuoterRouter: lookupQuoterImplementation
48+
ExecutorQuoterRouter->>ExecutorQuoter: quote(...)
49+
ExecutorQuoter->>ExecutorQuoter: custom logic
50+
ExecutorQuoter-->>ExecutorQuoterRouter: payee, value
51+
ExecutorQuoterRouter->>ExecutorQuoterRouter: buildQuote
52+
ExecutorQuoterRouter->>Executor: requestExecution{value}
53+
ExecutorQuoterRouter->>ExecutorQuoterRouter: emit OnChainQuote
54+
```
55+
56+
# Detailed Design
57+
58+
The existing Executor contracts are immutable, handle payment in the native gas token, and require a standardized quote header. This design introduces and standardizes the minimum viable approach to form quotes on-chain, allow for permissionless quoter selection, and reuse the rest of the on- and off-chain tooling.
59+
60+
## Technical Details
61+
62+
### EVM
63+
64+
On EVM, two new contracts will be introduced.
65+
66+
1. **ExecutorQuoter** represents the on-chain quoting logic of a particular Quoter / Relay Provider. It may implement any logic desired by the Relay Provider as long as it adheres to this interface. It SHOULD be immutable.
67+
68+
```solidity
69+
interface IExecutorQuoter {
70+
function requestQuote(
71+
uint16 dstChain,
72+
bytes32 dstAddr,
73+
address refundAddr,
74+
bytes calldata requestBytes,
75+
bytes calldata relayInstructions
76+
) external view returns (bytes32 payeeAddress, uint256 requiredPayment);
77+
}
78+
```
79+
80+
2. **ExecutorQuoterRouter** replaces **Executor** as the entry-point for integrators. It MUST be immutable and non-administered / fully permissionless. This provides three critical functionalities.
81+
82+
1. `updateQuoterContract(bytes calldata gov)` allows a Quoter to set their `ExecutorQuoter` contract via signed governance (detailed below). This MUST
83+
1. Verify the chain ID matches the Executor’s `ourChain`.
84+
2. Verify the contract address is an EVM address.
85+
3. Verify the governance has not expired.
86+
4. Verify the signature `ecrecover`s to the quoter address on the governance.
87+
5. Assign the specified contract address to that quoter address.
88+
6. Emit a `QuoterContractUpdate` event.
89+
2. `quoteExecution` allows an integrator to quote the cost of an execution for a given quoter in place of a signed quote. This MUST call `requestQuote` from that Quoter’s registered contract.
90+
3. `requestExecution` allows an integrator to request execution via Executor providing a quoter address in place of a signed quote. This MUST
91+
1. Call `requestQuote` from that Quoter’s registered contract.
92+
2. Enforce the required payment.
93+
3. Refund excess payment.
94+
4. Request execution, forming a `EQ02` quote on-chain.
95+
5. Emit an `OnChainQuote` event.
96+
97+
```solidity
98+
interface IExecutorQuoterRouter {
99+
event OnChainQuote(address implementation);
100+
event QuoterContractUpdate(address indexed quoterAddress, address implementation);
101+
102+
function quoteExecution(
103+
uint16 dstChain,
104+
bytes32 dstAddr,
105+
address refundAddr,
106+
address quoterAddr,
107+
bytes calldata requestBytes,
108+
bytes calldata relayInstructions
109+
) external view returns (uint256);
110+
111+
function requestExecution(
112+
uint16 dstChain,
113+
bytes32 dstAddr,
114+
address refundAddr,
115+
address quoterAddr,
116+
bytes calldata requestBytes,
117+
bytes calldata relayInstructions
118+
) external payable;
119+
}
120+
```
121+
122+
### SVM
123+
124+
The SVM implementation should follow the requirements above relevant to the SVM Executor implementation.
125+
126+
<aside>
127+
⚠️ TODO: The primary additional consideration is how to handle the accounts used for fetching the quote from an ExecutorQuoter in a standardized way.
128+
</aside>
129+
130+
### Other
131+
132+
Other platforms are not in-scope at this time, but similar designs should be achievable.
133+
134+
## Protocol Integration
135+
136+
Relay Providers will need to change their verification for Executor requests. If the prefix is `EQ02`, they MUST check the following event to ensure it is an `OnChainQuote` emitted by the canonical `ExecutorQuoterRouter` on that chain in place of verifying the signature.
137+
138+
<aside>
139+
⚠️ TBD, if the 32 byte body from `EQ01` is added, no additional changes will be required.
140+
</aside>
141+
142+
## **API / database schema**
143+
144+
### Governance
145+
146+
This design introduces a new concept of a Quoter’s on-chain governance
147+
148+
```solidity
149+
bytes4 prefix = "EG01"; // 4-byte prefix for this struct
150+
uint16 sourceChain; // Wormhole Chain ID
151+
address quoterAddress; // The public key of the quoter. Used to identify an execution provider.
152+
bytes32 contractAddress; // UniversalAddress the quote contract to assign.
153+
uint64 expiryTime; // The unix time, in seconds, after which this quote should no longer be considered valid for requesting an execution
154+
[65]byte signature // Quoter's signature of the previous bytes
155+
```
156+
157+
### Quote - Version 2
158+
159+
This introduces a new Quote version to the [Executor spec](../README.md#api--database-schema).
160+
161+
<aside>
162+
⚠️ TBD if the `baseFee`, `destinationGasPrice`, `sourcePrice`, and `destinationPrice` fields should be required akin to `EQ01`. We are assessing the additional cost to implement this or provide an equivalent way to gather this data off-chain.
163+
</aside>
164+
165+
```solidity
166+
Header header // prefix = "EQ02"
167+
```
168+
169+
# **Caveats**
170+
171+
Integrators MAY now choose to construct their relay instructions on-chain. They will need to manage how to handle challenging cross-chain situations, such as calculating the required rent on SVM or gas usage differences across different EVMs.
172+
173+
Unlike the off-chain signed quote, there may be a price update for the on-chain quote between when the client code requests the quote and when the transaction is executed on the source chain. This may cause the transaction to fail if the price increased during that time period and a sufficient buffer was not added to the quote.
174+
175+
# **Alternatives Considered**
176+
177+
## Subscriptions
178+
179+
A separate design where integrators pay for Executor costs via a subscription model was proposed, but this exposes a severe DoS risk where integrators incur arbitrary costs effectively controlled by end users if messaging is permissionless. Instead, this design maintains the costs with end users, keeping the risk equivalent.
180+
181+
## ExecutorV2
182+
183+
It is possible to keep the same or similar interface as Executor in the ExecutorQuoterRouter contract and allow the client to toggle between on- or off- chain quotes based on the first 4 bytes of the quote passed. This is still possible to add an additional wrapper around in the future, though would involve another contract. It is not immediately clear if there are integrators that would desire such flexibility.
184+
185+
## ExecutorQuoter ABI
186+
187+
<aside>
188+
🤔 Further investigation required to determine if the return should change. Document the results here.
189+
</aside>
190+
191+
# **Security Considerations**
192+
193+
The `ExecutorQuoterRouter` remains permissionless and any Quoter can freely register/update and implement their own `ExecutorQuoter` implementation. The only change in the trust assumption for the Relay Provider is that they previously only relied on a given chain’s RPC implementation and their RPC provider in regards to the request for execution event and amount paid. Now they may also trust the resulting quote and required payment, as it is not signed by their Quoter.
194+
195+
The `ExecutorQuoterRouter`, plays a critical role in its emission of an event to ensure to off-chain services that the unsigned `EQ02` quote was formed by the designated Quoter’s registered `ExecutorQuoter` implementation.

0 commit comments

Comments
 (0)