Skip to content

Merge/upstream main 2025 10 07 - #17

Merged
xdecentralix merged 113 commits into
mainfrom
merge/upstream-main-2025-10-07
Oct 7, 2025
Merged

Merge/upstream main 2025 10 07#17
xdecentralix merged 113 commits into
mainfrom
merge/upstream-main-2025-10-07

Conversation

@xdecentralix

Copy link
Copy Markdown
Collaborator

Description

Changes

  • ...
  • ...

How to test

squadgazzz and others added 30 commits September 4, 2025 08:49
Replicates cowprotocol#3599 after reverting it in cowprotocol#3627, which caused some memory
issues after updating the lockfile. This version uses downgraded aws lib
versions to avoid updating the lockfile.

Depends on cowprotocol/ethcontract-rs#982

Tested on mainnet-shadow, looks fine.
# Description
Fixes the lints generated
[here](https://github.com/cowprotocol/services/pull/3592/files#diff-dd2c0eb6ea5cfc6c4bd4eac30934e2d5746747af48fef6da689e85b752f39557).

# Changes
- `as` -> `AS`
- remove `CMD` line that only `echo`'s something

## How to test
Run CI and check for these lints
# Description
Migrates 0x SC to `alloy-rs` bindings.

# Changes

- [ ] Binds `IZeroEx` SC using `alloy`.
- [ ] Removes legacy bindings from the `build.rs` file.
- [ ] For e2e tests, a different from the original alloy provider's
signer needs to be used. Currently, alloy doesn't provide a concise way
to achieve that. An issue was opened:
alloy-rs/alloy#2829. This PR contains a
workaround that uses a new provider with the desired wallet for 0x
bindings.
- [ ] Drops GOERLI from the bindings tests.
- [ ] Updates alloy to 1.0.30 since CI fails without it(e.g.
https://github.com/cowprotocol/services/actions/runs/17409474760/job/49422717398)

## How to test
Existing tests

---------

Co-authored-by: MartinquaXD <martin.beckmann@protonmail.com>
…atic to linear time complexity by using a HashMap. (cowprotocol#3621)

## Description

This function is building a vector of fee policies (`fee_policies`) by
looking up orders in an auction. Currently this is doing a linear search
for each order ID. The change I'm proposing is to use a HashMap lookup
for better performance and also I think slightly cleaner logic. The
scale of the performance benefit is a bit context dependent, i.e,
average auction size, but I'll share the theoretical perspective.

## The Theoretical Advantage

The theoretical advantage here is from an algorithmic perspective,
namely O(n²) -> O(n).

The current implementation is
```
for order_id in ranking.ranked().flat_map(...).unique() {  // m iterations
    match auction.orders.iter().find(|order| &order.uid == order_id) {  // O(n) search
        // ...
    }
}
```
m = number of unique order IDs from solutions
n = number of orders in auction
Total complexity: O(m × n) = O(n²) in the worst case (when m ≈ n)

Whereas in this case, we'd be doing

```
let order_lookup: HashMap<_, _> = auction.orders.iter()  // O(n) to build
    .map(|order| (order.uid, order))
    .collect();

let fee_policies: Vec<_> = ranking.ranked()  // m iterations
    .flat_map(...)
    .unique()
    .filter_map(|order_id| {
        match order_lookup.get(order_id) {  // O(1) lookup
            // ...
        }
    })
    .collect();
```
HashMap creation: O(n)
Lookups: O(m × 1) = O(m)
Total complexity: O(n + m) = O(n) when m ≤ n

---------

Co-authored-by: Martin Magnus <martin.beckmann@protonmail.com>
…ks (cowprotocol#3632)

# Description

Some slow nodes time out when there are too many pool IDs when we query
the uniswap v3 subgraph for `Tick`s. This PR adds the option to limit
the number of such items in the query which means running multiple
queries with fewer pool IDs in `where` to achieve the same effect. This
can't be done in parallel as it overloads these slow nodes.

In the end I found a [fast
graph](https://thegraph.com/explorer/subgraphs/FiJDXMFCBv88GP17g2TtPh8BcA8jZozn5WRW7hCN7cUT?view=About&chain=arbitrum-one)
so this change is not needed, but if this one ever stops working we
might be happy to have the option.

# Changes

- [x] Add a new driver param that limits how many pool ids are added to
a single query

## How to test

Start driver with graph-url against a slow subgraph (e.g.
`F85MNzUGYqgSHSHRGgeVMNsdnW1KtZSVgFULumXRZTw2`) and watch it fail. Then
set max pool ids to 10 and see it succeed.

---------

Co-authored-by: Martin Magnus <martin.beckmann@protonmail.com>
Removes the dummy alloy provider since it was overseen that a similar
function already exists in `ethrpc::mock::web()` funciton.
# Description
To continue with the alloy migration, a deployed block info is required
for some of the SC, list GPV2Settlement, BalancerV2.x, others.

# Changes
Extend the bindings macro and InstanceExt interface with that data. Now,
in the bindings! macro, added a way to provide either only address or a
tuple with (address, deployment block).
Adds alloy's BlockId and BlockNumber conversions, which are required for
further migration.
Adds the ability to retrieve SC ABI functions that are required in some
cases to encode some inputs/outputs.

Will be used in some tests in follow-up PRs.
# Description
Currently our axum `/solve` handler immediately parses the JSON request
body into the DTO object. This is very convenient but also pretty
suboptimal. Because the driver was built such that it can support
multiple connected solvers the auction pre processing logic was written
such that it only happens once per auction and the results get shared by
all the solvers.
That means all but 1 `/solve` requests per auction get fully parsed and
then discarded. Also since that happens on the main tokio runtime
(instead of a blocking task) every incoming request blocks the runtime
for a considerable amount of time.

# Changes
- request handler now takes a raw `String`
- parsing into the `dto` representation was moved into the auction
pre-processor
- also parsing happens in a task that is optimized for blocking
- to detect whether a request is duplicated we simply to a string
comparison on the requests (the autopilot sends identical requests to
all drivers). It was determined via a benchmark that this is
considerably faster than just parsing out the auction id from the
request.
- due to not having the `auction_id` available immediately we now init
the tracing info span with `auction_id = field::Empty` and fill in the
value immediately after parsing the request. There should be no
important logs without the auction id present.
- the `deadline` type of the `domain::Auction` needed to be changed
because each solver may have different timeout settings which does not
lend itself nicely to sharing the same auction struct. In fact sharing
the same deadlines for all solvers was actually a bug that also gets
resolved by this PR. Now we just store the auction deadline in the
domain object and each solver can compute their own unique deadlines
individually.

## How to test
I ran a test on shadow mainnet which reduced the CPU time spent
deserializing from `~40%` to `4%`.

Before
<img width="1920" height="827" alt="Screenshot 2025-09-01 at 14 49 11"
src="https://github.com/user-attachments/assets/dbf2bcd6-ca65-4460-9d1b-eb7fc203dc37"
/>

After
<img width="1920" height="955" alt="Screenshot 2025-09-01 at 14 49 24"
src="https://github.com/user-attachments/assets/9fa9761f-b30b-461a-a0a7-95626cb2225e"
/>

Also the metrics show a ~50% reduction in CPU usage of the shadow
mainnet driver
<img width="765" height="273" alt="Screenshot 2025-09-10 at 07 21 03"
src="https://github.com/user-attachments/assets/98db0241-5080-4d7c-8ebf-b7206f47a69e"
/>
# Description
Checking signatures is currently one of the slower auction building
steps. This PR implements a couple optimizations to speed things up.

# Changes
- avoid `Itertools::partition` as that moves around every item even if
we only need to move a few (e.g. in this case we don't need to split ALL
orders into pre-signed and "unknown" - instead we just work with the
orders that are actually relevant for this processing step)
- changed `validate_signatures()` to only handle a single check instead
of multiple - that made it a lot less awkward to figure out for which
order the check failed.
- updated internal helper functions to take an owned `SignatureCheck` so
we don't have to clone the `signature` and `interaction` bytes twice
- used `FuturesUnordered` instead of `join_all` to avoid allocating a
vector for all the results - instead we just await each future
concurrently and push the invalid orders into the result vector

## How to test
updated the existing unit test
unfortunate the performance improvement of this PR can only really be
measured by deploying to mainnet prod as all the staging environments
don't have enough data to matter and the shadow environment does not hit
this code path.
# Description
Migrates `BalancerV2BasePool` bindings to alloy. Also, starts using
`BalancerV2Vault` alloy bindings in the liquidity fetching logic, which
doesn't touch the GPv2Settlement SC stuff. This is required to avoid
non-pretty workaround to provide alloy provider for the
`BalancerV2BasePool` instance here:

https://github.com/cowprotocol/services/pull/3642/files#diff-c3c048247f32ab19c889ca44622f8857e743f2901b2ad2b7b51fb1439835962fR64-R65

`BalancerV2Vault` ethcontract instances remain since they are still used
in the core SCs logic, which will be migrated much later.

## How to test
Existing tests.
# Description
This PR is really stupid but unfortunately necessary at the moment...
Some tokens are weird to route optimally and only few solvers support
them. However, anybody can create a uniswap pool for these tokens which
may cause any good solver to be able to route these tokens
automatically.
This is generally a good thing except in one particular edge case:
If the "good" solver is not able to provide calldata with their quote
that can be verified but the "bad" solver's calldata verifies just fine
we'll currently show the quote from the bad solver despite having very
high confidence that the other solver is actually better and knows how
to route the tokens.

To avoid showing terrible prices in this VERY specific scenario we need
an escape hatch to conditionally bypass the quote verification. I
decided to go for a simple token list. If either the sell or the buy
token show up in the list that we define we'll skip the quote
verification altogether.
That way quote from all solvers (good and bad) will be unverified which
levels the playing field and we again pick the quote based on the
highest out amount.

---------

Co-authored-by: ilya <ilya@cow.fi>
Adds the MutWallet to enable adding signers when needed.

This is a "solution" to alloy-rs/alloy#2829
Since Alloy's Provider is built on layers and in the end it seals them up, it becomes impossible to change the signing wallet. The solution is to share the wallet behind the scenes through the classic Arc + lock (Rw in this case)

The main challenge here was running tokio's locks in "sync" contexts:

    During tests we have "current_thread" so we can't run blocking operations (.blocking_read), we can however, run a separate thread and wait for it (ironic because it blocks the thread anyway — though this is ok because it's for tests)
    Outside tests, we're running the multi-thread runtime, thus spawning an extra thread and waiting is a waste because we're blocked waiting for it anyways, using block_in_place we can signal the runtime that we're in a pickle and we're going to block, evicting other tasks and keeping the system chugging along
# Description
Fetching balances is currently the biggest time sink in the auction
pre-processing step. Interestingly the autopilot is able to it way
faster with many more orders.
So the easiest way to speed things up in the driver is to use the same
component the autopilot uses for that.
Instead of fetching the balances only after we receive the auction we
now have a background task that keeps track of used balances and updates
them on every new block.
If an auction contains a new order that has no balance yet the balance
will be fetched on demand.

# Changes
Use caching account balance fetching in the driver.
Unfortunately the code is a bit ugly because the `shared` code uses
different types than the `driver` code so there are quite a few
conversions needed. By using the same data types we could probably
reduce the times a bit more by avoiding expensive conversions and
clones.

## How to test
ran a test on shadow mainnet and the metrics show significant
improvements.

Total pre-processing time goes from the 750-1000ms bucket to primarily
the 250-500ms bucket
<img width="1196" height="362" alt="Screenshot 2025-09-11 at 12 59 06"
src="https://github.com/user-attachments/assets/0206bc5b-a72b-4fb2-9d88-33a5d5ef4e2d"
/>

balance fetching goes from 500-750ms to 50-100ms
<img width="1189" height="363" alt="Screenshot 2025-09-11 at 12 59 18"
src="https://github.com/user-attachments/assets/794e3f0a-40fa-4d6e-8662-4c52b37f7906"
/>

solver time to compute solutions goes from 9s to 9.5s
<img width="1191" height="340" alt="Screenshot 2025-09-11 at 12 59 25"
src="https://github.com/user-attachments/assets/9774ad6c-f65c-4608-ae1f-0f2f5dd20024"
/>
# Description
Currently saving solver competitions is quite slow (100ms-500ms with
outliers going over 3s!). AFAICS the way we handle uploading the JSON is
very suboptimal:
1. we do `serde_json::to_value()` while `sqlx` later does
`serde_json::to_string()` - we might as well directly do the conversion
to string ourselves
2. we block the runtime while we serialize the solver competition JSON.
Instead we can offload the serialization to a blocking task and let it
already do work while we continue with the other DB queries.

<img width="1190" height="297" alt="Screenshot 2025-09-12 at 22 03 01"
src="https://github.com/user-attachments/assets/963f710e-ac74-4f92-bc68-da4bcad2fd51"
/>


# Changes
Upload the JSON by inserting a JSON string directly.
Offload the serialization to a blocking task and do the solver
competition at the very end so we can already finish all the other DB
queries in the mean time.
This required me to take the `SolverCompetition` as an owned value which
also enabled me to turn some `.iter()` into `.into_iter()` which gives
the optimized more opportunities to avoid cloning memory.

## How to test
updated the `roundtrip` test to work with the string approach
# Description
This is the first prerequisite for the upcoming PRs that generalize the
event handling module, making it possible to integrate with Alloy smart
contract instances. Instead of relying on `ethcontract-rs`–specific
types in the interface, the trait is now generic and can work with
primitives from either `ethcontract-rs` or `alloy`.

All the interfaces in the `event_handling.rs` file don't depend on the
`ethercontract-rs` crate, meaning we can use any underlying
implementation in the future.

Some common logic was extracted to the `EthcontractEventRetrieving`
trait. This is required to remove `ethcontract-rs` crate dependency from
the `EventRetrieving` trait and deduplicate the code.

The change looks pretty straightforward, but you can't imagine how many
failed attempts were made to reach its current state 🤕
# Description
closes cowprotocol#3404
Add RPC request ID logging to HTTP transport layer for improved
observability and debugging.
# Changes
<!-- List of detailed changes (how the change is accomplished) -->

- [ ] ...
- [ ] ...

## How to test
<!--- Include details of how to test your changes, including any
pre-requisites. If no unit tests are included, please explain why and
how to test manually
1.
2.
3.
-->

<!--
## Related Issues

Fixes cowprotocol#3404 
-->

---------

Co-authored-by: Martin Magnus <martin.beckmann@protonmail.com>
Co-authored-by: José Duarte <duarte.gmj@gmail.com>
# Description
This PR adds a helper contract called BalancerQueries to the contracts
crate.
Giving a bit more context, currently the Balancer solver relies on SOR
quotes only, which might be running on slightly out-of-date data from
the Balancer API.
Being able to update and validate amounts with an extra on-chain query
swap step would be ideal.
On Balancer V3 it's possible to query swaps with the BalancerRouter,
which is already present in the contracts crate, but on Balancer V2 the
best way is to use this BalancerQueries helper contract, that is not yet
available.

# Changes
I simply followed the steps to add a new contract provided in the
[contracts readme.md
file](https://github.com/cowprotocol/services/blob/main/crates/contracts/README.md)
> - In vendor.rs extend ARTIFACTS with the package and contract name.
> - Run the vendor binary for example with (cd crates/contracts; cargo
run --bin vendor --features bin). This creates a new json file for this
contract in the artifacts folder.
> - In build.rs add a generate_contract call for the contract. This
creates the ethcontract generated rust code file.
> - In lib.rs add an include! call for the contract. This imports the
rust code into the library.

## How to test
Not sure this addition requires tests to be added. If you think it's
necessary, please share a template or even some example I can follow,
because I couldn't find test examples from the existing contracts.

## Related Issues
Gettin this PR merged and released will unblock
gnosis/solvers#161

---------

Co-authored-by: José Duarte <duarte.gmj@gmail.com>
squadgazzz and others added 26 commits September 30, 2025 14:56
# Description

I accidentally merged the
cowprotocol#3662 PR into the wrong
branch, so this PR resurrects all the changes.

Migrates `UniswapV3Pool` SC bindings to alloy.

## How to test
Existing test + mainnet shadow.

---------

Co-authored-by: José Duarte <duarte.gmj@gmail.com>
# Description

In cowprotocol#3709 we used the wrong
address for the Hooks Trampoline contract on Gnosis.
Co-authored-by: ilya <ilya@cow.fi>
…wprotocol#3727)

This reverts commit adb9dc6.

# Description

After today's deployment we started having issues where transaction
hashes are not present for settlements. This PR is a suspect.

More context:
https://cowservices.slack.com/archives/C0361CDD1FZ/p1759252600242209
Reverts cowprotocol#3707, since the Swapper SC remains in use in the gnosis/solvers
repo:
https://github.com/gnosis/solvers/blob/main/src/infra/dex/simulator.rs#L43
Also, migrates its rust bindings to alloy.

Added a hotfix label to update the gnosis/solvers repo. This hotfix is
not intended for deployment.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…col#3451)

# Description
<!--- Describe your changes to provide context for reviewers, including
why it is needed -->

## Context and why is this needed
Liquorice is an RFQ system that aggregates quotes from PMMs (Private
Market Makers) and provides this liquidity to solvers.

PMMs benefit from knowing that their quotes will be used in settlement
as early as possible because they can hedge earlier, manage their risks
better, and as a result provide better prices.

Right now PMMs can find out that their quote was used only after
settlement already occurred on-chain.
This situation can be improved drastically if the CoW driver were
communicating this information before sending transaction on-chain.

## Solution
PR introduces generic liquidity sources notification logic in CoW driver
and adds [Liquorice](https://liquorice.gitbook.io/liquorice-docs)
liquidity source as the first subscriber.

Before submitting settlement transaction on-chain, driver sends
notification to configured subscribers that the settlement is about to
happen.

In case of Liquroice, driver extracts Liquorice [RFQ
IDS](https://liquorice.gitbook.io/liquorice-docs/for-market-makers/basic-market-making-api#id-3.-receiving-rfq)
from the CoW settlement interactions and sends HTTP request to Liquorice
API.

# Changes

## `crates/contracts`
Added Liquorice [settlement
contract](https://liquorice.gitbook.io/liquorice-docs/links/smart-contracts)
used for interactions matching and calldata decoding

## `crates/driver`
Implemented generic functionality used no notify liquidity sources about
settlement.
 
This functionality perhaps could've been implemented on top of existing
`infra/notify` and `infra/liquidity` modules, however this would require
significant refactoring, so for the sake of simplicity it was added as a
standalone set of modules

- Added new optional configuration for liquidity sources notifiers
- Implemented `notifier` module with `liquorice` notifier as the first
one.
- Added `notifier.settlement(...)` invocation in
`crates/driver/src/domain/competition/mod.rs` before settlement
submitted onchain

## `crates/e2e`
- Added `forked_node_liquidity_source_notification_mainnet` test with
mocked Liquorice API and transaction calldata

## How to test
<!--- Include details of how to test your changes, including any
pre-requisites. If no unit tests are included, please explain why and
how to test manually
1.
2.
3.
-->
1. `FORK_URL_MAINNET="...." cargo test -p e2e
forked_node_liquidity_source_notification_mainnet -- --ignored`
2. Unit tests in
`crates/driver/src/infra/notify/liquidity_sources/liquorice/notifier.rs`

<!--
## Related Issues

Fixes #
-->
## Related Issues
Implements cowprotocol#3452
Depends on cowprotocol#3643
)

# Description
Fixes the liquidity_source_notification e2e test.
# Description

Add BNB and Lens to OpenAPI definitions so it appears in Swagger.
# Description
Migrates BalancerQueries to alloy bindings. This SC is used in the
gnosis solvers repo. That would require some tiny changes in that repo
to support this.
…#3723)

# Description
The recent changes to autopilot and orderbook allow to specify
DB_READ_URL and DB_WRITE_URL. The former is used only for non mutating
database operations. The plan is to point DB_READ_URL to our
read-replica (giving the possibility of slightly outdated data). The
DB_WRITE_URL points to the main database.

# Changes
Added reuse of connection pool in case of DB_READ_URL and DB_WRITE_URL
having the same values.

## How to test
1. Run E2E tests
2. Check the deployment on staging

---------

Co-authored-by: ilya <ilya@cow.fi>
# Description
Migrates ILiquoriceSettlement bindings to alloy.
# Description
Some docs for order validation functions

# Changes
<!-- List of detailed changes (how the change is accomplished) -->

- [ ] custom_interactions
- [ ] validate_app_data
- [ ] simulate
- [ ] validate_signature

## How to test
<!--- Include details of how to test your changes, including any
pre-requisites. If no unit tests are included, please explain why and
how to test manually
1.
2.
3.
-->

<!--
## Related Issues

Fixes #
-->
Co-authored-by: Martin Magnus <martin.beckmann@protonmail.com>
# Description
Drops the `auction_orders` table, which contains duplicated data in the
`competition_auctions` table.
The same data is used there:

https://github.com/cowprotocol/services/blob/979e924dae38ba18bf398447e399092867edaf3f/crates/autopilot/src/infra/persistence/mod.rs#L302-L306

## How to test
Existing tests. No usages apart from this repo:
https://github.com/search?q=org%3Acowprotocol+auction_orders+-repo%3Acowprotocol%2Fservices&type=code

The following query returns 0, which should tell how many
`auction_orders.auction_id` do not exist in `competition_auctions.id`.
```sql
SELECT COUNT(*) 
FROM auction_orders ao
WHERE NOT EXISTS (
  SELECT 1 FROM competition_auctions ca
  WHERE ca.id = ao.auction_id
);
```
@github-actions

github-actions Bot commented Oct 7, 2025

Copy link
Copy Markdown


Thank you for your submission, we really appreciate it. Like many open-source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution. You can sign the CLA by just posting a Pull Request Comment same as the below format.


I have read the CLA Document and I hereby sign the CLA


0 out of 13 committers have signed the CLA.
@squadgazzz
@MartinquaXD
@floor-licker
@fafk
@jmg-duarte
@kaze-cow
@markin-io
@supernovahs
@brunoguerios
@m-sz
@bram-vdberg
@naanselmo
@Olexandr88
You can retrigger this bot by commenting recheck in this Pull Request. Posted by the CLA Assistant Lite bot.

@xdecentralix
xdecentralix merged commit 1dfa221 into main Oct 7, 2025
9 of 13 checks passed
@github-actions github-actions Bot locked and limited conversation to collaborators Oct 7, 2025
@xdecentralix
xdecentralix deleted the merge/upstream-main-2025-10-07 branch October 7, 2025 20:30
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.