Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Observe multiple contracts for ETH flow #3249

Open
wants to merge 9 commits into
base: main
Choose a base branch
from

Conversation

sunce86
Copy link
Contributor

@sunce86 sunce86 commented Jan 21, 2025

Description

Implements the idea explained here: #3248 (comment)

The Ethflow contract of interest: https://etherscan.io/address/0x40a50cf069e992aa4536211b23f286ef88752187#code (probably useful to better review and understand refunder code changes)

This solution has a limitation: we can't specify a block number to start indexing the new contract. Upon autopilot restart, indexing resumes from the latest block recorded in the database. This approach assumes synchronized and atomic insertion of events for both the old and new contracts.
As a result, new contract events will only be indexed from the point when the new contract address is added to the autopilot configuration.

This is acceptable under the following assumptions:

  • The configuration to read from the new contract is applied first.
  • The new contract is officially published, and orders are created afterward.

Changes

  • EthFlowRefundRetriever and CoWSwapOnchainOrdersContract are now observing multiple ethflow contracts at the same time.
  • Refunder service adjusted to also detect the ethflow contract for which the refund should be done (so far this was irrelevant since there was only one contract)
  • Updated E2E test for refunder.

How to test

Existing e2e tests.
Updated refunder e2e test to simulate two orders being created on two different ethflow contracts and being properly refunded.
I also manually verified (through observing logs on e2e tests) that indexing from multiple contracts works properly.

@sunce86 sunce86 self-assigned this Jan 27, 2025
@sunce86 sunce86 marked this pull request as ready for review January 29, 2025 10:09
@sunce86 sunce86 requested a review from a team as a code owner January 29, 2025 10:09
@sunce86 sunce86 changed the title [DRAFT] Observe multiple contracts for ETH flow Observe multiple contracts for ETH flow Jan 29, 2025
Copy link
Contributor

@MartinquaXD MartinquaXD left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approach looks like it should work but I think the code in the refunder could be simplified quite a lot.

Comment on lines +116 to +118
// Theoretically, multiple orders with the same order_hash can exist in multiple
// contracts at the same time. That's why we need to return a list of contracts
// from which the order should be refunded.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The entire order owner is part of the order_uid so since the ethflow contract is technically the order owner the owner bytes of the 2 order uids would be different for identical orders placed on multiple ethflow contracts.

Copy link
Contributor Author

@sunce86 sunce86 Jan 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

contract is technically the order owner

I assumed the same but apparently this is not true for PreSign orders (although I couldn't find the right example to double check):
https://github.com/cowprotocol/services/blob/main/crates/autopilot/src/database/onchain_order_events/mod.rs#L642

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now I see this: https://github.com/cowprotocol/ethflowcontract/blob/f466b7a8d5df80b593aeb05488e1c27afc7f2704/src/CoWSwapEthFlow.sol#L105
This means that all orders are 1271 orders, I guess, meaning I can leverage the fact that owner is always the EthFlowContract

.map(|contract| {
contract
.orders(ethcontract::tokens::Bytes(order_hash))
.batch_call(&mut batch)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The refunder uses our auto batching logic under the hood. If make use of this this function would probably end up being a lot easier to read.

Comment on lines +129 to +134
tracing::error!(
"Error while getting the currentonchain status of orderhash \
{:?}, {:?}",
H256(order_hash),
err
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Let's apply the usual pattern for logs while we change the code anyway.

Suggested change
tracing::error!(
"Error while getting the currentonchain status of orderhash \
{:?}, {:?}",
H256(order_hash),
err
);
tracing::error!(
uid =? H256(order_hash),
?err,
"Error while getting the current onchain status ot the order"
);

.context(format!("uid {uid:?}"))

// Go over all order uids and group them by ethflow contract address
let mut uids_by_contract: HashMap<CoWSwapEthFlowAddress, Vec<OrderUid>> = HashMap::new();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could probably be replace with Itertools::group_by

@@ -98,63 +93,55 @@ impl RefundService {
async fn identify_uids_refunding_status_via_web3_calls(
&self,
refundable_order_uids: Vec<EthOrderPlacement>,
) -> Result<Vec<OrderUid>> {
) -> Vec<(OrderUid, Vec<CoWSwapEthFlowAddress>)> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that an OrderUid can only belong to a single contract we can ditch the vector. And I would suggest to simply copy the contract type instead of messing around with the address to make the code easier to read. The refunder is doing barely any work anyway so I think we should optimize for readability over performance here.

Suggested change
) -> Vec<(OrderUid, Vec<CoWSwapEthFlowAddress>)> {
) -> Vec<(OrderUid, CoWSwapEthFlow)> {

Comment on lines +43 to +46
let mut events =
AllEventsBuilder::new(self.web3.clone(), *self.addresses.first().unwrap(), None);
// We want to observe multiple addresses for events.
events.filter = events.filter.address(self.addresses.clone());
Copy link
Contributor

@squadgazzz squadgazzz Jan 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the future, it probably makes sense to adjust the new function signature in the ethcontract crate to allow for the collection of addresses.

Copy link
Contributor

@squadgazzz squadgazzz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, assuming all the comments above will be addressed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants