Skip to content

fix: use solver-determined fee for BYOS fulfillments#19

Closed
jean-neiverth wants to merge 470 commits into
mainfrom
byos-api
Closed

fix: use solver-determined fee for BYOS fulfillments#19
jean-neiverth wants to merge 470 commits into
mainfrom
byos-api

Conversation

@jean-neiverth

Copy link
Copy Markdown

Summary

  • Fix BYOS /solve handler to use fee: Some(U256::ZERO) instead of fee: None when constructing fulfillment trades
  • fee: None means protocol-determined fee (only valid for market orders), but BYOS orders are limit orders which require the solver to declare a fee
  • fee: Some(U256::ZERO) tells the driver the solver charges 0 fee — the protocol still collects its own fee via fee policies

Test plan

  • cargo check -p byos compiles
  • cargo test -p byos passes
  • Verify limit orders settle correctly with the driver

🤖 Generated with Claude Code

jmg-duarte and others added 30 commits February 20, 2026 18:09
# Description
Migrates the trusted tokens to the main config. I've grouped to be
easier to read/find in config files

# Changes

* Removes the trusted tokens from the CliArgs
* Adds them to the new config file

## How to test
Staging
# Description
The majority of time fetching uni v3 liquidity is spent cloning pool
states.
This makes up 3.6% of the entire CPU time of the driver.
<img width="1920" height="935" alt="Screenshot 2026-02-19 at 22 50 02"
src="https://github.com/user-attachments/assets/a59efa24-60c0-4a4e-9c36-428521ef0bc5"
/>


# Changes
This PR applies 2 optimizations:
* reuse existing allocation with `into_iter().filter().collect()`
* store `PoolInfo` in `Arc`s for cheap clones

I'd like to highlight the use of `Arc::make_mut`. If there is no `Arc`
pointing to the same allocation as our `Arc` we can simply get a mutable
reference. If there are other `Arc`s pointing to our allocation we deep
clone it and point our `Arc` to the new allocation (clone on write).
This is quite cool because together with the fact that `append_events`
takes an exclusive reference to the `pools` we know that there can be no
new `Arc` pointing to a recently updated `PoolInfo` before the function
is over. This ensures that every `PoolInfo` that needs to be updated
will always be cloned at most once.

## How to test
Briefly tested in prod

This yielded surprising results. The amount of time spent inside the uni
v3 liquidity fetching logic remained unchanged and even the time used
for allocations was pretty much unchanged.
But since the cloning of the data was moved to when it gets updated
instead of every time it's needed for a request the time needed to fetch
all the liquidity improved significantly. (still nothing to write home
about, though)
<img width="629" height="302" alt="Screenshot 2026-02-19 at 23 31 05"
src="https://github.com/user-attachments/assets/6dc00c53-4afe-4761-af37-d5848f0da4a0"
/>

Also some other CPU bound pre-processing tasks got faster as well:
<img width="630" height="300" alt="Screenshot 2026-02-19 at 23 31 14"
src="https://github.com/user-attachments/assets/307e4ad1-de24-477d-8a4d-3f5f140233d8"
/>
<img width="625" height="294" alt="Screenshot 2026-02-19 at 23 31 31"
src="https://github.com/user-attachments/assets/31060dcf-1a7b-4c84-8ee1-f3910ef65a8a"
/>
<img width="625" height="302" alt="Screenshot 2026-02-19 at 23 31 23"
src="https://github.com/user-attachments/assets/6c8af19f-942c-44d2-82c2-8b0058d0cf45"
/>
# Description
Serializing the auction and compressing the bytes is CPU bound work and
takes quite long.
That stuff should be spawned on a block_task to not hog one of the
regular tokio runtime threads and cause spikes in tail latency.

# Changes
* spawn blocking task for serializing and compression s3 upload
# Description
Currently the event indexing logic is dominated by 2 calls:
* eth_getBlock
* eth_getLogs (events of fetched block)

This PR eliminates `eth_getLogs` calls.
Since the rest of the indexing logic is not dominated by `eth_getLogs` I
think the only way how we could meaningfully speed up the process
further is by subscribing to log filters and streaming the results as
soon as a new block was processed by the ethereum node.

# Changes
Since we already have a component that keeps track of the latest block I
created a new type that implements `BlockRetrieving` that simply
forwards `get_current_block()` calls to the block stream instead of
sending an RPC request.

Also adds a few tracing spans and replaces the awkward request batching
logic of the event handler with something easier to read.

## How to test
tested on staging mainnet (indexing overhead should be identical to
prod)
the time the autopilot waits for essential processing to be done
decreased from ~65ms to ~45ms

<img width="801" height="150" alt="Screenshot 2026-02-19 at 13 04 48"
src="https://github.com/user-attachments/assets/18ce0eb8-5e8c-4412-a12b-efa4c6b1f313"
/>
<img width="657" height="300" alt="Screenshot 2026-02-19 at 13 03 12"
src="https://github.com/user-attachments/assets/8f823a5c-a1d4-4bd0-ac10-c4c07fed068f"
/>
<img width="1262" height="302" alt="Screenshot 2026-02-19 at 13 05 44"
src="https://github.com/user-attachments/assets/8f007f53-9efb-44a4-b426-cf91d25cb0c4"
/>
# Description
Upgrades prometheus to 0.14 & the metrics macro to 0.6

Closes cowprotocol#3338

# Changes

* Upgrades prometheus to 0.14 & the metrics macro to 0.6
* Changes some callsites to match the new with_label_values signatures

## How to test
Staging
# Description

When investigating CoinGecko usage I noticed our nginx cache is not
being optimally used, it is in fact used very little. The cache can only
trigger when the URL is the same. There are two issues: the batches have
different compositions and the tokens are not in deterministic order.
This PR fixes the second problem.
# Description
Because the insert order_events query tries to avoid duplicating the
last event entry it's quite awkward. That's probably why we didn't
replace it with a bulk request earlier. However, flamegraphs showed that
we spend a ridiculous 49% of our CPU time on inserting orders.

# Changes
Implemented awkward bulk insert query

## How to test
single insert variant was already tested before
1 new unit test for multi insert variant

Reduced CPU usage: **0.55 cores -> 0.2 cores**
<img width="763" height="211" alt="Screenshot 2026-02-19 at 20 28 27"
src="https://github.com/user-attachments/assets/f25e8c66-5480-4b21-a4c2-d4a1fdf654b7"
/>

Faster at handling other CPU bound work
<img width="626" height="298" alt="Screenshot 2026-02-19 at 20 28 55"
src="https://github.com/user-attachments/assets/e27730ca-31cc-4579-832b-d9ccaf2fe140"
/>

Total time spent on that query: **49% -> 3%**
<img width="1920" height="767" alt="Screenshot 2026-02-19 at 20 30 08"
src="https://github.com/user-attachments/assets/28b262ae-dd93-4a30-8122-d28543b7ac71"
/>
<img width="1920" height="636" alt="Screenshot 2026-02-19 at 20 30 17"
src="https://github.com/user-attachments/assets/81a5894f-b2a5-43a7-83c0-33e38b28923a"
/>
# Description
To my surprise do `tracing` macros not lazily evaluate expressions. So
in following code:
```rust
        tracing::trace!(
            path = &url.path(),
            body = %payload.body_to_string(),
            "solver request",
        );
```
`payload.body_to_string()` always gets evaluated but the `Display`
implementation of the resulting `String` gets called only when the log
is enabled.
This leads to the autopilot currently spending ~2% of the time in
`body_to_string()`.

To address this I introduced the `Lazy` type that delays the execution
of the passed in expression to when the `tracing` machinery actually
executed the `Display` or `Debug` functionality.

Additionally flamegraphs showed that the driver is currently burning
2.2% of its CPU time on logging auction deadlines. I downgraded that log
to `trace` since it's not really that useful.

# Changes
used `Lazy` in the autopilot
downgraded expensive log to `trace`

# Test
Manually tested that `body_to_string()` does not get called by injecting
a `panic!()` without causing tests to fail
added unit tests for `Lazy` in `observe` crate
…ile (cowprotocol#4192)

# Description
Move banned_users, order_events_cleanup_interval/threshold, and S3
upload settings from CLI arguments to the TOML config file.

# Changes

- Add config modules: banned_users, order_events_cleanup, s3
- Remove infra::persistence::cli (S3 CLI args)
- Delete corresponding CLI arguments and Display impl entries
- Wire config values through run.rs

## How to test
Staging

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
# Description
The task that writes the current auction to the DB has 2 performance
issues:
* unnecessary conversions: instead of serializing the DTO to a JSON
string we convert it to `JsonValue` and let `sqlx` do the conversion to
string
* conversions on wrong task: serializing data is very slow and blocks an
entire runtime thread if we don't do the sync work in a blocking task
(the entire process often takes more than 1s)

# Changes
* skip 1 conversion by directly serializing the DTO to string and
writing a string to the DB (same pattern already applied by some other
JSON upload)
* serialize the DTO on a blocking task

## How to test
adjusted existing e2e tests, they show that the serialized data can be
read back into the exact JSON value we serialized in the first place
# Description
Follow up to this
[comment](cowprotocol#4191 (comment))
suggesting a different structure for the `observe` crate. This PR goes a
bit further than the comment suggested but I think this setup makes the
most sense.

# Changes
old structure:
```
distributed_tracing
    - request_id.rs
    - trace_id_format.rs
    - tracing_axum.rs
lazy.rs
tracing.rs
tracing_reload_handler.rs
```

new structure:
```
tracing
    - distributed
        - axum.rs
        - headers.rs
        - request_id.rs
        - trace_id_format.rs
    - lazy.rs
    - init.rs
    - reload_handler.rs
```
# Description
The removed structs + impls were here because of incompatibility issues.
We've since fixed them, it's time to remove them 🗑️

# Changes

* Add opentelemetry-http dep
* Remove the HeaderInjector + HeaderExtractor

## How to test
Staging but this should be basically 1:1
# Description

`estimate_prices_and_update_cache` iterates all tracked tokens, most of
which are cache hits that resolve instantly. With .buffered() a pending
cache miss at the front blocks yielding ready results behind it, keeping
all remaining slots occupied and preventing new futures from entering
the stream. This means expired tokens trickle into BufferedRequest too
slowly producing batches of 2-3 tokens instead of the max 20.


https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=63d75fe56901ecfe850ec03875615596
demonstrates this.
# Description
This function gets called hundreds of times within one span leading to a
tempo view that scrolls many pages which makes it hard to get the big
picture of the call.
The caller of the noisy call is also instrumented and measures the
execution time of all sub calls together so we'll not lose much
information.

# Changes
remove instrumentation of frequently called function
# Description
Some solvers reported that `/solve` requests arrive with varying delays.
It's quite hard to pin point where the issues lies. While some
improvements on the autopilot definitely reduced the variance of the
delay greatly there are still new reports about this.
In order to get more data on this (especially from solvers NOT using the
CoW hosted reference driver) this PR tries to approximate the data
transfer times from the autopilot side.
This is accomplished by writing a slim wrapper around the fully
serialized payload `Bytes` that turns it into a stream. That way we can
at least measure the time it takes `hyper` to write the bytes to the
network stack. Since the consumer (external driver) will have
back-pressure on the transfer this measurement should come reasonably
close to the actual data transfer times.

# Changes
- added `BytesStream` that yields `Bytes` in 1MB chunks
- also measures time to start the transfer and the entire transfer time
- logs the result using the original tracing span (which now contains
the auction_id, and driver)

## How to test
added unit test for correctness of the yielded data
ran in e2e test to confirm the logs contain the expected spans

---------

Co-authored-by: José Duarte <duarte.gmj@gmail.com>
…protocol#4190)

# Description
Currently a lot of CPU time (~3.5%) is spent on fetching allowances for
the liquidity sources we index. This is problematic for 2 reasons:
* most liquidity sources are not used so fetching the settlement
contract's allowance is wasted time
* the driver seems to not use the fetched allowance and fetches them
again when encoding the tx (see
[1](https://github.com/cowprotocol/services/blob/main/crates/driver/src/domain/competition/solution/encoding.rs#L175),
[2](https://github.com/cowprotocol/services/blob/main/crates/driver/src/domain/competition/solution/mod.rs#L294-L299),
[3](https://github.com/cowprotocol/services/blob/main/crates/driver/src/domain/competition/solution/mod.rs#L427),
[4](https://github.com/cowprotocol/services/blob/main/crates/driver/src/domain/competition/solution/interaction.rs#L33-L62))

# Changes
deleted `solver::interactions::allowances` and it's usages

## How to test
e2e tests still work
…fig file (cowprotocol#4194)

# Description
Move native price estimator settings (estimators, api_estimators,
results_required, cache_refresh_interval, native_price_prefetch_time)
from CLI arguments to the TOML config file.

# Changes

* Move the autopilot native price estimator settings
* Add tests

# Migration Guide

Autopilot native price estimator CLI arguments have been moved to the
TOML config file under the `[native-price-estimation]` table.

## Estimator format

Previously, estimator variants were specified as `|`-separated strings.
Now they are inline tables with a `type` field:

| Old (CLI) | New (TOML) |
|---|---|
| `CoinGecko` | `{ type = "CoinGecko" }` |
| `OneInchSpotPriceApi` | `{ type = "OneInchSpotPriceApi" }` |
| `Driver\|solver1\|http://localhost:8080` | `{ type = "Driver", name =
"solver1", url = "http://localhost:8080" }` |
| `Forwarder\|http://localhost:12088` | `{ type = "Forwarder", url =
"http://localhost:12088" }` |

Stages (previously separated by `;`) become separate inner arrays.
Estimators within a stage (previously separated by `,`) are elements of
the same inner array.

**Old (CLI):**
```
CoinGecko,OneInchSpotPriceApi;Driver|solver1|http://localhost:8080;Forwarder|http://localhost:12088
```

**New (TOML):**
```toml
[native-price-estimation]
estimators = [
    [{ type = "CoinGecko" }, { type = "OneInchSpotPriceApi" }],
    [{ type = "Driver", name = "solver1", url = "http://localhost:8080" }],
    [{ type = "Forwarder", url = "http://localhost:12088" }],
]
```

## Arguments

- `--native-price-estimators` (CLI) / `NATIVE_PRICE_ESTIMATORS` (env):
```toml
[native-price-estimation]
estimators = []
```

- `--api-native-price-estimators` (CLI) / `API_NATIVE_PRICE_ESTIMATORS`
(env):
```toml
[native-price-estimation]
api-estimators = []
```

- `--native-price-estimation-results-required` (CLI) /
`NATIVE_PRICE_ESTIMATION_RESULTS_REQUIRED` (env):
```toml
[native-price-estimation]
results-required = 2
```

- `--native-price-cache-refresh` (CLI) / `NATIVE_PRICE_CACHE_REFRESH`
(env):
```toml
[native-price-estimation]
cache-refresh-interval = "1s"
```

- `--native-price-prefetch-time` (CLI) / `NATIVE_PRICE_PREFETCH_TIME`
(env):
```toml
[native-price-estimation]
prefetch-time = "80s"
```


## How to test
Staging

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
# Description
gzip is super heavy on the CPU and currently we are compressing data
with the maximum level. However, gzip quickly reaches a level of
diminishing returns so this PR picks a more reasonable gzip compression
level based on a test.

```
Level 1:  2214921 bytes (62ms)
Level 2:  2179485 bytes (62ms)
Level 3:  2149025 bytes (65ms)
Level 4:  1981237 bytes (116ms)
Level 5:  1927338 bytes (133ms)
Level 6:  1883464 bytes (145ms)
Level 7:  1864462 bytes (159ms)
Level 8:  1844328 bytes (225ms)
Level 9:  1840352 bytes (301ms)
```

I picked a recently uploaded auction instance (~11MB) and compressed it
once with each level. This showed that level 3 cuts the runtime by ~80%
while degrading the compressed size by only 15% (from 1.84MB to 2.14MB).

This caused other blocking operations in the autopilot to run faster
(e.g. request serialization goes from ~90ms to ~70ms).
<img width="1400" height="744" alt="Screenshot 2026-02-25 at 07 54 37"
src="https://github.com/user-attachments/assets/59876cda-dd21-422c-830c-9e33850e2a0f"
/>


# Changes
Reduced gzip compression level from max (i.e. 9) to 3.
…otocol#4195)

# Description
Migrate order validation, IPFS, volume fee, and misc orderbook settings
(unsupported tokens, banned users, EIP-1271 validation, gas limits,
same-tokens policy, etc.) from clap CLI arguments to a TOML
configuration file.


# Changes

* Migrates the parameters above
  * Groups IPFS, order validation and volume fee
* Refactors where applicable
* Adds unit tests for (de)serialization
# Orderbook Configuration Migration: CLI to TOML

# Migration Guide

The orderbook no longer accepts certain settings as CLI arguments. They
must now be provided in a TOML config file, passed via the new
**required** `--config` flag.

```bash
orderbook --config=/path/to/orderbook.toml ...
```

## Migrated settings

| Old CLI Argument | TOML Key |
|---|---|
| `--min-order-validity-period` |
`order-validation.min-order-validity-period` |
| `--max-order-validity-period` |
`order-validation.max-order-validity-period` |
| `--max-limit-order-validity-period` |
`order-validation.max-limit-order-validity-period` |
| `--max-limit-orders-per-user` |
`order-validation.max-limit-orders-per-user` |
| `--max-gas-per-order` | `order-validation.max-gas-per-order` |
| `--same-tokens-policy` | `order-validation.same-tokens-policy` |
| `--unsupported-tokens` | `unsupported-tokens` |
| `--banned-users` | `banned-users.addresses` |
| `--banned-users-max-cache-size` | `banned-users.max-cache-size` |
| `--eip1271-skip-creation-validation` |
`eip1271-skip-creation-validation` |
| `--ipfs-gateway` | `ipfs.gateway` |
| `--ipfs-pinata-auth` | `ipfs.auth-token` |
| `--app-data-size-limit` | `app-data-size-limit` |
| `--active-order-competition-threshold` |
`active-order-competition-threshold` |
| `--volume-fee-factor` | `volume-fee.factor` |
| `--volume-fee-effective-timestamp` |
`volume-fee.effective-from-timestamp` |

## Example

Before:

```bash
orderbook \
  --min-order-validity-period=2m \
  --max-order-validity-period=6h \
  --banned-users=0xdead000000000000000000000000000000000000 \
  --ipfs-gateway=https://gateway.pinata.cloud/ipfs/ \
  --ipfs-pinata-auth=my-secret-key \
  --volume-fee-factor=0.0002 \
  --volume-fee-effective-timestamp=2025-06-01T00:00:00Z \
  --same-tokens-policy=allow-sell \
  --bind-address=0.0.0.0:8080
```

After — create `orderbook.toml`:

```toml
same-tokens-policy = "allow-sell"

[order-validation]
min-order-validity-period = "2m"
max-order-validity-period = "6h"

[banned-users]
addresses = ["0xdead000000000000000000000000000000000000"]

[ipfs]
gateway = "https://gateway.pinata.cloud/ipfs/"
pinata-auth = "my-secret-key"

[volume-fee]
factor = 0.0002
effective-from-timestamp = "2025-06-01T00:00:00Z"
```

## How to test
Tests + Staging

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
## Summary

This PR completes **Milestone 3: Documentation** of the [CoW Grants
Program RFP: CoW Protocol Playground Block Explorer
Integration](https://forum.cow.fi/t/grant-application-cow-protocol-playground-block-explorer-integration/3284/1)
proposal by CoBuilders.

### Documentation Strategy

Most of the technical documentation for this integration was delivered
inline with the implementation in M1 and M2:

| Milestone | Documentation Delivered |
|-----------|------------------------|
| **M1** ([cowprotocol#4000](cowprotocol#4000)) |
README sections for Sourcify configuration, contract verification,
component table updates |
| **M2** ([cowprotocol#4077](cowprotocol#4077),
[cowswap#6774](cowprotocol/cowswap#6774)) |
Environment variable documentation in `.env` files, JSDoc comments in
source code |

**This PR** adds the remaining user-facing documentation: a practical
guide on how to use Otterscan for transaction inspection and debugging.

## Changes

Adds a "Using Otterscan" section to the playground README covering:
- How to access and use Otterscan (`http://localhost:8003`)
- Inspecting transactions (overview, traces, logs, gas profiling)
- Debugging failed transactions with trace analysis
- Example workflow: Tracing a CoW Swap settlement

## Milestones

| Milestone | Description | Status |
|-----------|-------------|--------|
| M1 | Otterscan Integration |
[cowprotocol#4000](cowprotocol#4000) ✅ |
| M2 | Frontend Integration |
[cowprotocol#4077](cowprotocol#4077) +
[cowswap#6774](cowprotocol/cowswap#6774) ✅ |
| M3 | Documentation | **This PR** |

---

*Submitted by [CoBuilders](https://cobuilders.xyz) as part of the CoW
Grants Program*

---------

Co-authored-by: Augusto Collerone <collerone.augusto@gmail.com>
Co-authored-by: Ignacio <ignacio@cobuilders.xyz>
# Description
Aave wants to track specific orders in bulk, knowing their ids.

# Changes
Adds POST handler for `v1/orders/lookup` endpoint that requires a list
of order uids and responds with a vector of their data. Has a hardcoded
limit of 128 orders per request (to fit the MAX_JSON_BODY_PAYLOAD size).

## How to test
Test on staging, query multiple orders using this API.

---------

Co-authored-by: José Duarte <duarte.gmj@gmail.com>
Co-authored-by: Martin Magnus <martin.beckmann@protonmail.com>
Co-authored-by: ilya <ilya@cow.fi>
# Description
The playground was left behind amidst the config changes. This PR brings
the playground back up to speed.

# Changes

* Remove unused variables
* Migrate the orderbook migration
* Migrate the autopilot migration

## How to test
Run the playground and execute a trade
# Description
The Dockerfile was compiling the whole workspace which is wasteful for
deployment (it was compiling e2e for example). This PR only compiles the
used packages.

# Changes

* Compiling workspace -> autopilot, driver, orderbook, refunder, solvers
only

## How to test
Build the dockerfile
# Description
Based on @MartinquaXD's comment on Slack, I figured that there would be
places where we can import the specific alloy crate, unlocking earlier
and more parallel compilation. These changes alone seem to save ~5s on a
clean build in my laptop; going down from ~1m to 55s.

There's more gains to take from this, but for that we need to separate
shared further and then split those dependencies.
cowprotocol#4217 would also help a
bunch.

# Changes

* app-data
  * replace alloy with alloy-primitives
* since alloy uses tiny-keccak, replace the tiny-keccak with alloy's
keccak
* chain
  * manually implement thiserror (less 1 dep)
  * replace alloy with alloy-primitives
* model
* replace alloy with alloy-primives, alloy-signer, alloy-signer-local,
alloy-sol-types
* number
  * replace alloy with alloy-primitives
* order-validation
  * replace alloy with alloy-primitives and alloy-contract
  * remove thiserror wrapper (less 1 dep)
* serde-ext
  * replace alloy with alloy-primitives
* testlib
  * replace alloy with alloy-primitives
* winner-selection
  * replace alloy with alloy-primitives


## How to test
Compiler + existing tests
…wprotocol#4213)

# Description
When users provide app data with wrong partner fees, we get less than
good error reporting, for example:
```
{"errorType":"AppDataInvalid","description":"app data has the wrong format: data did not match any variant of untagged enum Helper at line 1 column 232"}
```
This is, not only a pain for us to debug but also for the user:
* For us because the special deserializers are all named Helper
* For the user because of the bad error message

This PR changes the deserialization mechanism in a "non-obvious" way to
provide proper error messages, as untagged enums stand very much against
them.

For example floating point bps are unsupported, they returned the
previous error; after this PR they return:
```
{"errorType":"AppDataInvalid","description":"app data has the wrong format: invalid type: floating point `99.0`, expected u64 at line 1 column 128"}
```

# Changes

* Rename the deserializers
* Refactor the deserializers into weird structs (but better for error
reporting)

## How to test
Unit tests to ensure nothing broke + playground to test the error
```
curl 'http://localhost:8000/api/v1/app_data \
  -X 'PUT' \
  -H 'accept: application/json' \
  -H 'content-type: application/json' \
  --data-raw '{"fullAppData":"{\"appCode\":\"YOUR_APP_CODE\",\"metadata\":{\"partnerFee\":[{\"recipient\":\"0x28c716bC23ed77CAEc27f476A366318ad5F12d58\",\"volumeBps\":99.5},{\"priceImprovementBps\":9900,\"recipient\":\"0x28c716bC23ed77CAEc27f476A366318ad5F12d58\",\"volumeBps\":100}]},\"version\":\"1.14.0\"}"}'
  ```
# Description

I have run a bunch of experiment goal of which was to make CoinGecko
batches bigger (fetch price for more tokens at once) to save on costs.
In a previous experiment I increased `concurrent_requests` on the
`CachingNativePriceEstimator` to 500 and that totally worked and
CoinGecko debounces all these requests nicely, but also means we would
be spamming solvers with a lot of requests at once when we start the
service.

The first issue at hand is that before this PR we would create futures
that would hit the cache and return immediately, which was bad, because
they take up a slot in the queue when using buffered() so fewer useful
futures that actually issue a native price request would get to run. I
changed this to buffered_unordered() and that helped a little bit, but
then the queue gets full and after that new futures get in only as
previous futures finishe, so the execution is spread out in time and our
CoinGecko debouncing can't gather enough tokens to form a full batch.

This PR introduces a solution where we omit non-expired tokens
altogether which solves the issue of futures that hit the cache taking
up a spot in the queue _and_ we just run batches of 19 sequentially. We
wait for each batch to finish (max ~3s, limited by QUERY_TIMEOUT) and
only then issues a new batch. As a tradeoff we get a slower cache warm
up/refresh, but we get to save a lot of money on CoinGecko.

In addition to this change I set the refresh rate to 30s from 1s, so we
can gather more expired tokens at once.

<img width="2103" height="480" alt="Screenshot 2026-03-02 at 11 30 33"
src="https://github.com/user-attachments/assets/190bbefe-ac26-4837-b2c2-05a7c63ebbcb"
/>


# Changes

* [x] Don't try to fetch non-expired prices
* [x] Run batches sequentially 
* [x] NATIVE_PRICE_CACHE_REFRESH set to 30s instead of 1s in service
config

---------

Co-authored-by: Martin Magnus <martin.beckmann@protonmail.com>
# Description
Our current `/solve` request can be heavy on certain chains, and given
the fact that it is expected to eventually switch to a cross-chain
auction, the data will be growing significantly. While it is expected to
implement a delta-sync approach with event-based auction updates, this
is quite involved, and as a first step, we can look into compressing the
request to reduce its size and reduce network latency/delays.

The tests showed that `brotli` with compression level 1 outperforms
`gzip`
cowprotocol#4212 (comment)

# Changes

##Autopilot
- New `--compress-solve-request` boolean CLI flag threaded through
`run_loop::Config`
- `solve::Request` gains a `compressed()` method that br-compresses the
body on a blocking task (CPU-intensive, like the existing serialization)
- On compression failure, falls back to sending the uncompressed body
with an error log
- Compression time is tracked via the existing `auction_overhead` metric
- Sets the `Content-Encoding: br` header only when the body is actually
compressed
- Add a `runloop_solve_request_body_size` histogram metric that records
the size (in bytes) of the JSON body sent in each `/solve` request to
drivers.

## Driver
- Adds `RequestDecompressionLayer` to the axum middleware stack, which
automatically decompresses br-encoded request bodies based on the
`Content-Encoding` header

## How to test
New unit and e2e tests. Deploy manually with disabled compression to
collect some metrics and then with compression enabled.

## Related issues

Fixes cowprotocol#4206
# Description
`shared` is a big ball of spaghetti where dependencies are extremely
unclear.
This PR untangles this somewhat

# Changes
- moves some code out of shared to the only location where it's used
    - `trace_many` into `bad_token::trace_call`
    - `CodeFetching` into `trade_verifier`
    - `BlockRetrieving` into `event-indexing`
    - `tenderly_api` into `trade_verifier`
    - `subgraph` into `sources`
    - `RecentBlockCache` into `sources`
- create separate crates for `balance-overrides`, `event-indexing`,
`account-balances`, `request-sharing`, `token-info`, `liquidity-sources`
- moved `BaseTokens` and `BaselineSolvable` into `liquidity-sources` as
that made the most sense to me

Some of the new crates are super tiny but are necessary to later move
`price-estimation` into a separate crate as well.

This already cut the compilation time of `shard` in half which are gains
we'll often see in incremental builds

<img width="296" height="240" alt="Screenshot 2026-03-03 at 10 31 50"
src="https://github.com/user-attachments/assets/6e69a751-5e7a-435b-b74b-4d51fb5dc1ee"
/>
<img width="277" height="353" alt="Screenshot 2026-03-03 at 10 31 55"
src="https://github.com/user-attachments/assets/2026624c-d3aa-464c-af71-408f2e51c3f1"
/>


## How to test
compiler
# Description
Resolves cowprotocol#3966.

Enable parallel settlement submission from a single solver by using
EIP-7702 delegation. When a solver wins multiple solutions in
overlapping auctions, each settlement can now be submitted concurrently
through independent submission EOAs instead of waiting in a sequential
queue.

The solver EOA delegates its code to a CowSettlementForwarder contract
via EIP-7702. Approved submission EOAs call forward(target, data) on the
solver EOA, which forwards to the actual target (settlement contract,
wrapper, or flashloan router) with msg.sender = solver EOA — preserving
the existing whitelisting.

# Changes

- [x] `CowSettlementForwarder.sol` — New forwarder contract with caller
whitelist. Supports forwarding to any target (settlement contract,
wrappers, flashloan router) via `forward(address target, bytes data)`
- [x] `SubmissionAccountPool` type: a channel-based pool that lends
submission accounts for the duration of a settlement and reclaims them
afterward. In EIP-7702 mode, settle requests are spawned as concurrent
tasks instead of processed sequentially
- [x] `submission_accounts` config that allows specifying accounts that
are whitelisted to submit settlements. If omitted we fallback to
non-delegated submissions directly from the solver EOA

# Requirements before deployment

* Deploy & verify the CowSettlementForwarder contract
* Set up EIP7022 delegation to whitelisted solver EOAs to the newly
deployed contract
* Create new submissions accounts and fund them with eth (or other base
token) & monitor their eth balance
* Whitelist the submissions accounts on the solver EOA
(CowSettlementForwarder code)

Only then can the submissions accounts be configured and deployed. 

## How to test

cargo nextest run -p e2e local_node_parallel_settlement --test-threads 1
--run-ignored ignored-only

<!--
## Related Issues

Fixes #
-->
# Description
Further untangles the `shared` crate.

# Changes
* deletes `bytes.rs` which was forgotten in an old PR
* moved `trade_finding` into `price-estimation`
* move `tenderly_api` arguments from `shared::Arguments` to
`price_estimation::Arguments`
* removed a few dependencies
* downgraded a few dependencies to `dev-dependencies`

new crates split off of `shared`:
	* `gas-price-estimation`
	* `price-estimation`
	* `bad-tokens`

Unfortunately `price-estimation` is relying on a few utils which didn't
make much sense to move into a new crate so I duplicated a few things in
`price_estimation::utils` but I think this does not outweigh the
structure and compile time gained by this PR.

This PR does again not migrate the catch all `alloy` imports to the more
specific sub-crates (e.g. `alloy_primitives`)

## How to test
compiler
ashleychandy and others added 24 commits June 8, 2026 17:59
# Description
Removes the `AlloyU256` alias.

# Changes
* Replace `U256 as AlloyU256` import alias with plain `U256` in
`price-estimation/src/sanitized.rs` and `shared/src/order_quoting.rs`

Fixes cowprotocol#4502
# Description
Adds a barebones `solana-indexer` crate to the repository.

# Changes

Nothing worthy of note, this is just an empty crate. 

Other PRs will follow.
# Description
Currently, claude review only runs when explicitly mentioning claude in
a comment (e.g.
https://github.com/cowprotocol/services/actions/runs/27002342510) , not
on PR creation (e.g.
https://github.com/cowprotocol/services/actions/runs/26947333940/attempts/1)

The reason seems to be that
`github.event.pull_request.author_association` doesn't resolve to Member
(reproed here:
https://github.com/cowprotocol/claude-gate-repro/actions/runs/27007356481/job/79702173516#step:2:16).
Claude says this is due to an inconsistency in github event data
(comment authors contain the correct association). We cannot rely on the
`Contributor` association because people that committed code in the past
but are not part of the organisation would also resolve to that.

This PR changes the check to use repository permissions for the given
author (and automatically reviews for authors that can write)

# Changes

* Add a new step that checks repository permissions for PR or comment
auther
* Remove old gate

## How to test
Verified that both comments as well as new PRs pass the authentication
in the test repo
- Comment:
https://github.com/cowprotocol/claude-gate-repro/actions/runs/27008892569
- PR creation:
https://github.com/cowprotocol/claude-gate-repro/actions/runs/27008147177
…4370)

# Description

The autopilot no longer needs per-solution uniform clearing prices:
scoring uses auction-level native prices, and on-chain settlement
verification reads UCPs straight from the calldata. This PR removes UCPs
from the autopilot's domain model and persistence path while keeping the
wire format intact, so the change is safe under a rolling k8s deploy.

# Changes

* Drop `prices` from the autopilot's domain `Solution` (field,
constructor arg, and `prices()` getter).
* Remove `SolutionError::InvalidPrice` and its `invalid_price` metric
label — price validation is no longer performed when ingesting a
`/solve` response.
* Persist empty arrays into `proposed_solutions.price_tokens` /
`price_values`. The `NOT NULL` columns are retained for now (an empty
array still satisfies the constraint); they can be dropped in a
follow-up.
* Make the autopilot tolerant of drivers that omit `clearingPrices`: add
`#[serde(default)]` to the deserializer and emit a `debug!` log when a
driver still sends a non-empty map, so we can chase down stragglers
before fully removing the field.
* Leave the driver emitting `clearingPrices` on `/solve` responses (with
a deprecation comment) so autopilots running the previous code can still
deserialize during the rolling deploy.
* Mark `clearingPrices` as `deprecated: true` in the driver `/solve`
response schema and in the orderbook `solver_competition_v2` schema,
with a note explaining that recent autopilots will return it empty.

A follow-up PR will remove the driver-side field, the deprecation log,
and (optionally) drop the now-unused `proposed_solutions.price_tokens` /
`price_values` columns.

## How to test

1. `cargo nextest run -p autopilot -p driver` — unit tests, including
`winner_selection` tests updated to construct solutions without prices.
2. Hit `solver_competition_v2` for an auction produced by the new
autopilot and confirm `clearingPrices` is `{}`; for an auction produced
before the change it remains populated.

### Staging

Ran in staging with these orders:
*
https://dev.explorer.cow.fi/orders/0x34ce7c3c6b4c762663d087b2c6ffba30fc966d9bf98039eee98ad1722e79805309fbad1ea29c36dfe4f8f7baa87c5edf85e0d9f369fc62fe
*
https://dev.explorer.cow.fi/orders/0xaf624d224a01e7ecaff3d0be2a04a1e073090f3e9cfdad220cea741748f3e83109fbad1ea29c36dfe4f8f7baa87c5edf85e0d9f369fc66f9

Logs:
* https://victorialogs.dev.cow.fi/goto/eflbzd4yhhy4ga?orgId=1
* https://victorialogs.dev.cow.fi/goto/bflc0jc6wa874b?orgId=1

Context:
https://nomevlabs.slack.com/archives/C036JAGRQ04/p1780923984399839?thread_ts=1775741386.025859&cid=C036JAGRQ04
…tion to 593d7a5 (cowprotocol#4507)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[anthropics/claude-code-action](https://redirect.github.com/anthropics/claude-code-action)
| action | digest | `fbda2eb` → `593d7a5` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the warning logs for
more information.

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - "on monday"
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/cowprotocol/services).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yMDkuNCIsInVwZGF0ZWRJblZlciI6IjQzLjIwOS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
# Description
1. Updated responses of `/api/v1/quote` corresponding to the actual
implementation
2. Updated `PriceEstimationError` enum
3. Added new error codes to `PriceEstimationError` response
corresponding to the changes from
cowprotocol#4268.

The changes were generated via the script:


[sync_quote_errors.py](https://github.com/user-attachments/files/28601528/sync_quote_errors.py)


# Changes
New codes:
```
  - TradingOutsideAllowedWindow
  - TokenTemporarilySuspended
  - InsufficientLiquidity
  - CustomSolverError
```

## How to test

1. Open Swagger
3. Expand `PriceEstimationError`
- [ ] it should display the new error codes

<img width="698" height="632" alt="image"
src="https://github.com/user-attachments/assets/63c1d26c-bcac-42d4-9832-a64e53e8fe3c"
/>
…cowprotocol#4510)

# Description

On some chains (Avalanche especially) the node reports a new block
before its by-hash receipt lookup catches up. Right after our settlement
mines, the driver asks "did my tx land?" by hash, gets "not found", and
re-simulates the tx against the pending state to check it still works.
That state already includes our just-mined (or still-queued) settlement,
so the re-run reverts (`GPv2: order filled` on a full fill, `transfer
amount exceeds balance` on a partial). The driver reads that as a real
revert, cancels (the cancel then fails with `nonce too low`), and
reports the settlement as failed even though it landed. This is the main
source of the Avalanche "too many failing settlements" alert, and the
settlements actually succeed on-chain.

Instead of trusting the lagging by-hash lookup, the driver now leans on
two signals that do not lag:

- Our `Settlement` event in the latest block. The contract emits it only
on a successful settle, and `eth_getLogs` reads block logs directly, so
it sees the event while the receipt-by-hash lookup is still catching up.
If it is there, the tx mined, so report success.
- The signer's pending nonce (`eth_getTransactionCount` with the
`pending` tag). While our tx is still in the mempool the pending nonce
sits above the nonce we submitted with, so the re-simulation revert is
just our own queued tx re-applied, a false positive, and we keep
waiting. Only when the pending nonce shows our tx has left the mempool,
and the re-simulation still reverts, do we cancel.

An earlier version of this PR used `eth_getLogs` on the `pending` block
for the second signal, but measuring our nodes showed `pending` log
queries are rejected on 4 of 11 chains (polygon, bnb, arbitrum, linea,
with "pending logs are not supported"). `eth_getTransactionCount` on
`pending` is accepted on all 11, and since the re-simulation already
runs against the `pending` state, the two signals read the same state.

Old code cancelled whenever the re-simulation reverted, minus a brittle
`GPv2: order filled` string match that only caught full fills. New code
cancels only when the tx is provably gone from the mempool, so a
settlement that is mined-but-receipt-lagging or still queued is never
cancelled.

# Changes

- Added `Ethereum::contains_successful_tx`, which checks for our
`Settlement` event in the latest block via `eth_getLogs`.
- Added `Ethereum::pending_transaction_count`, the signer's nonce
including the mempool.
- Reworked the submission loop: report success when the settlement event
is already in the latest block, and cancel only when the re-simulation
reverts and the pending nonce shows the tx has left the mempool.
Anything else keeps waiting.
- Dropped the old `GPv2: order filled` revert-byte special case.

# How to test

New unit tests cover the cancel decision (`requires_cancellation`): a
revert with the tx gone from the mempool cancels, a revert while the tx
is still queued waits, and an unknown pending nonce never cancels.

The log and nonce lookups and the receipt-lag race itself have no
automated coverage. The e2e harness runs anvil, which returns receipts
immediately, so it cannot reproduce the lag. Verify on Avalanche:
settlements that land on-chain should stop being counted as
`driver_settlements{result="SubmissionError"}` and should stop logging a
cancel right after a re-simulation revert, while still showing up as
on-chain settlements.
…owprotocol#4511)

# Description

I'd like to see how a non-winning, non-colocated solver would have
settled a transaction in case they had won. I believe this is currently
not possible (we only log the transaction when a solver was selected for
winning). This information shouldn't be publicly available (solvers only
want to commit to a solution if they win), but logging it for debugging
purposes should be fine.

I only log the internalized representation, because this is the one that
would make it on-chain. Since we simplified buffer accounting (solver
takes the risk), we probably no longer need to look at uninternalized
calldata.

# Changes
* add a tx field to the simulation log
…owprotocol#4516)

# Description
Before submitting a winning settlement, the driver checks that the
solver has enough ETH to cover gas. It sizes that check against its own
gas price estimate. Since cowprotocol#4299, a solver can override the gas price for
its solution with a higher `maxFeePerGas`, and the settlement is then
submitted at that override. An Ethereum node reserves `gas_limit *
maxFeePerGas` of the sender's balance when it accepts a transaction, so
a solver funded for the driver's cheap estimate but not for its own
higher override passes the check, wins the auction, then gets rejected
at submission with `insufficient funds`.

This happened on base on 2026-06-11. Solver `zurui-solve` set
`maxFeePerGas = 10 gwei`, roughly 1900x the driver's ~0.005 gwei
estimate on base, while holding ~0.0046 ETH. The balance check passed,
the solver won two auctions, and both settlements were rejected by every
mempool (`have 4618974035905571 want 8438180000000000`, where `want =
gas_limit * 10 gwei`). Before cowprotocol#4299 the check price and the submitted
price were the same, so insufficient funds only surfaced at simulation
time on nearly empty wallets.

# Changes
- Size the pre-submission balance check to the `maxFeePerGas` the tx
will actually be submitted with: the solver's gas fee override when set,
otherwise the driver's estimate doubled. This mirrors
`apply_gas_fee_override`, which submits at the override directly when
one is present. Behavior is unchanged when no override is set.

# How to test
New unit test covering the fee selection (override when set, else driver
estimate doubled, including an override below the doubled estimate). An
underfunded solver that sets a high `maxFeePerGas` override is now
rejected at settlement encoding with `SolverAccountInsufficientBalance`
instead of winning and failing to broadcast. The existing
`settle_with_gas_fee_override` integration test still covers the funded
happy path.
cowprotocol#4518)

This PR contains the following updates:

| Package | Type | Update | Change | Pending |
|---|---|---|---|---|
|
[taiki-e/install-action](https://redirect.github.com/taiki-e/install-action)
| action | minor | `2.79.7` → `v2.81.1` | `v2.81.10` (+8) |

# Warnings (1)

Please correct - or verify that you can safely ignore - these warnings
before you merge this PR.

- `taiki-e/install-action`: Could not determine new digest for update
(github-tags package taiki-e/install-action)

---

---

> [!WARNING]
> Some dependencies could not be looked up. Check the warning logs for
more information.

---

### Release Notes

<details>
<summary>taiki-e/install-action (taiki-e/install-action)</summary>

###
[`v2.81.1`](https://redirect.github.com/taiki-e/install-action/releases/tag/v2.81.1):
2.81.1

[Compare
Source](https://redirect.github.com/taiki-e/install-action/compare/v2.81.0...v2.81.1)

- Update `cargo-no-dev-deps@latest` to 0.2.24.

- Update `cargo-hack@latest` to 0.6.45.

###
[`v2.81.0`](https://redirect.github.com/taiki-e/install-action/releases/tag/v2.81.0):
2.81.0

[Compare
Source](https://redirect.github.com/taiki-e/install-action/compare/v2.80.0...v2.81.0)

- Support `convco`.
([#&#8203;1831](https://redirect.github.com/taiki-e/install-action/pull/1831),
thanks [@&#8203;graelo](https://redirect.github.com/graelo))

- Support `docgarden`
([#&#8203;1830](https://redirect.github.com/taiki-e/install-action/pull/1830),
thanks [@&#8203;jesse-black](https://redirect.github.com/jesse-black))

- Update `vacuum@latest` to 0.28.0.

- Update `cargo-binstall@latest` to 1.19.1.

###
[`v2.80.0`](https://redirect.github.com/taiki-e/install-action/releases/tag/v2.80.0):
2.80.0

[Compare
Source](https://redirect.github.com/taiki-e/install-action/compare/v2.79.15...v2.80.0)

- Support `kingfisher`.
([#&#8203;1874](https://redirect.github.com/taiki-e/install-action/pull/1874),
thanks [@&#8203;SAY-5](https://redirect.github.com/SAY-5))

###
[`v2.79.15`](https://redirect.github.com/taiki-e/install-action/releases/tag/v2.79.15):
2.79.15

[Compare
Source](https://redirect.github.com/taiki-e/install-action/compare/v2.79.14...v2.79.15)

- Update `typos@latest` to 1.47.0.

- Update `wasm-tools@latest` to 1.251.0.

- Update `vacuum@latest` to 0.27.2.

- Update `uv@latest` to 0.11.17.

- Update `tombi@latest` to 1.1.1.

- Update `mise@latest` to 2026.5.16.

###
[`v2.79.14`](https://redirect.github.com/taiki-e/install-action/releases/tag/v2.79.14):
2.79.14

[Compare
Source](https://redirect.github.com/taiki-e/install-action/compare/v2.79.13...v2.79.14)

- Update `vacuum@latest` to 0.27.0.

- Update `cargo-deny@latest` to 0.19.8.

###
[`v2.79.13`](https://redirect.github.com/taiki-e/install-action/releases/tag/v2.79.13):
2.79.13

[Compare
Source](https://redirect.github.com/taiki-e/install-action/compare/v2.79.12...v2.79.13)

- Update `gungraun-runner@latest` to 0.19.1.

- Update `biome@latest` to 2.4.16.

###
[`v2.79.12`](https://redirect.github.com/taiki-e/install-action/releases/tag/v2.79.12):
2.79.12

[Compare
Source](https://redirect.github.com/taiki-e/install-action/compare/v2.79.11...v2.79.12)

- Update `prek@latest` to 0.4.3.

- Remove uses of crates.io API, which potentially cases 403 error.

###
[`v2.79.11`](https://redirect.github.com/taiki-e/install-action/releases/tag/v2.79.11):
2.79.11

[Compare
Source](https://redirect.github.com/taiki-e/install-action/compare/v2.79.10...v2.79.11)

- Update `vacuum@latest` to 0.26.8.

- Update `cargo-nextest@latest` to 0.9.137.

###
[`v2.79.10`](https://redirect.github.com/taiki-e/install-action/releases/tag/v2.79.10):
2.79.10

[Compare
Source](https://redirect.github.com/taiki-e/install-action/compare/v2.79.9...v2.79.10)

- Update `tombi@latest` to 1.1.0.

- Update `prek@latest` to 0.4.2.

- Update `editorconfig-checker@latest` to 3.7.0.

###
[`v2.79.9`](https://redirect.github.com/taiki-e/install-action/releases/tag/v2.79.9):
2.79.9

[Compare
Source](https://redirect.github.com/taiki-e/install-action/compare/v2.79.8...v2.79.9)

- Update `vacuum@latest` to 0.26.7.

- Update `tombi@latest` to 1.0.0.

###
[`v2.79.8`](https://redirect.github.com/taiki-e/install-action/releases/tag/v2.79.8):
2.79.8

[Compare
Source](https://redirect.github.com/taiki-e/install-action/compare/v2.79.7...v2.79.8)

- Update `parse-dockerfile@latest` to 0.1.6.

- Update `knope@latest` to 0.23.0.

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - "on monday"
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/cowprotocol/services).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yMTkuMCIsInVwZGF0ZWRJblZlciI6IjQzLjIxOS4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…cowprotocol#4519)

This PR contains the following updates:

| Package | Type | Update | Change | Pending |
|---|---|---|---|---|
|
[tombi-toml/setup-tombi](https://redirect.github.com/tombi-toml/setup-tombi)
| action | minor | `v1.0.11` → `v1.1.1` | `v1.1.3` (+1) |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the warning logs for
more information.

---

### Release Notes

<details>
<summary>tombi-toml/setup-tombi (tombi-toml/setup-tombi)</summary>

###
[`v1.1.1`](https://redirect.github.com/tombi-toml/setup-tombi/releases/tag/v1.1.1)

[Compare
Source](https://redirect.github.com/tombi-toml/setup-tombi/compare/v1.1...v1.1.1)

#### What's Changed

- \[codex] Automate release note generation on version tags by
[@&#8203;ya7010](https://redirect.github.com/ya7010) in
[#&#8203;38](https://redirect.github.com/tombi-toml/setup-tombi/pull/38)

**Full Changelog**:
<tombi-toml/setup-tombi@v1...v1.1.1>

###
[`v1.1.0`](https://redirect.github.com/tombi-toml/setup-tombi/releases/tag/v1.1.0)

[Compare
Source](https://redirect.github.com/tombi-toml/setup-tombi/compare/v1.1...v1.1)

#### What's Changed

Starting with this version, we will synchronize
[tombi](https://redirect.github.com/tombi-toml/tombi) with version
control.

- update: version by
[@&#8203;ya7010](https://redirect.github.com/ya7010) in
[#&#8203;33](https://redirect.github.com/tombi-toml/setup-tombi/pull/33)
- Clarify enable-cache behavior in README by
[@&#8203;ya7010](https://redirect.github.com/ya7010) in
[#&#8203;35](https://redirect.github.com/tombi-toml/setup-tombi/pull/35)
- Sync package version before retagging by
[@&#8203;ya7010](https://redirect.github.com/ya7010) in
[#&#8203;34](https://redirect.github.com/tombi-toml/setup-tombi/pull/34)
- \[codex] Align setup-tombi versioning by
[@&#8203;ya7010](https://redirect.github.com/ya7010) in
[#&#8203;36](https://redirect.github.com/tombi-toml/setup-tombi/pull/36)
- Unify release tag and version sync workflows by
[@&#8203;ya7010](https://redirect.github.com/ya7010) in
[#&#8203;37](https://redirect.github.com/tombi-toml/setup-tombi/pull/37)

**Full Changelog**:
<tombi-toml/setup-tombi@v1.0...v1.1.0>

###
[`v1.1`](https://redirect.github.com/tombi-toml/setup-tombi/compare/v1.0.11...v1.1)

[Compare
Source](https://redirect.github.com/tombi-toml/setup-tombi/compare/v1.0.11...v1.1)

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - "on monday"
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/cowprotocol/services).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yMTkuMCIsInVwZGF0ZWRJblZlciI6IjQzLjIxOS4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…col#4411)

# Description
Remove `Clone` implementation for `RequestSharing`, allow the GC task to
exit once `Arc` is dropped.

# Changes
* Remove clone implementation.
* `spawn_gc` modified to check `Arc` and exit if dropped.

## How to test
cargo nextest run -p request-sharing

---------

Co-authored-by: Martin Magnus <martin.beckmann@protonmail.com>
# Description
In some of the captured heap memory profiles a surprising amount of
memory is allocated for logging the generated cow amm orders. This is
because we currently log all of their fields. This was somewhat useful
when the feature was just introduced and still needed to be debugged but
by now nobody actually looks at all those order details.
This PR changes the code to only log the order_uids. To still allow
debugging the order details when it's necessary the original log was not
deleted but only downgraded to the `trace` level.

<img width="1124" height="818" alt="Screenshot 2026-06-15 at 07 18 42"
src="https://github.com/user-attachments/assets/b37f8ea3-48c1-4f5c-9b37-c27aab017908"
/>
…l#4522)

# Description
The current score handling logic is quite confusing as it first creates
intermediate collections which are unnecessary and splits logic across
multiple iterator variables instead of chaining.

This PR merges all score related handling into 1 (IMO more readable)
iterator chain which avoids allocations as much as possible (no
intermediate `collect_vec`)
…otocol#4521)

# Description
This PR applies 3 optimizations.
First it moves the creation of the auction into a separate function
which cuts down the size of the original function and makes the code
easier to read.
Secondly it avoids `Arc::unwrap_or_clone(auction)` and instead
"manually" constructs the auction. That way we can avoid 1 expensive
re-allocation when we push the cow amm orders into the fully cloned
auction. Since the majority of the memory used by the auctions are used
by orders and `Vec` grows by 2x this should significantly reduce the
total memory footprint since we only have a few cow amm orders anyway.
Lastly it wraps `tokens` and `surplus_capturing_jit_order_owners` into
`Arc`s as this data does not need to be modified by solvers individually
so they can just share the same allocation.
# Description
This PR applies a few optimizations for memory usage while the driver
sends the `/solve` request.

# Changes
First I adjusted the `s3` machinery to allow you to upload already
serialized `Bytes` instead of having it re-serialize the data again.
Since `Bytes` is internally ref-counted and compatible with `reqwest` we
can serialize the request once and then use the same data for the HTTP
request and for the S3 upload.

That also allows us to move the creation of the `DTO` into the scope of
the request serialization so the `DTO` already gets deallocated before
we even send the request (instead of sticking around until we processed
the results).
…#4524)

# Description
The driver can service multiple solvers at once. Therefore when multiple
solvers are connected we somehow need to manage all the duplicate work
the driver is doing for each solver.
This is already happening for the most part but 1 aspect could not be
de-duplicated well yet: finalizing the list of orders for each solver.
This has 2 issues which don't allow us to straight up ref-count the
order array and be done with it:
1. solvers can have different order prioritization strategies, also some
strategies depend on the address of the solver (e.g. prioritize orders
my solver provided the best quote for). That means the list of orders
can have a different order for different solvers.
2. if a user has only funds for a subset of their open orders the driver
allocates funds based on order priority.

There is also the issue that our pipelining was designed that we can
already start processing orders when we haven't already fetched all
appdata JSON documents for each order. Orders where the doc is missing
will have it filled in at a later stage.

Those details means the driver will have to deep clone the orders (which
make up the majority of the auction size) for EACH SOLVER! That
obviously requires a lot more memory than reusing the memory smartly.

# Changes
This PR addresses those issues by splitting the orders into 2 parts:
1. a reference counted struct that contains the immutable data - which
is the majority of the order
2. mutable fields where each solver gets their own instance to modify

To hide those implementation details from the caller the `Order`
implements `Deref`. That way the caller doesn't have to know in which
part of the order the data it needs lives when it's only reading.
…tion to 4d7e1f0 (cowprotocol#4517)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[anthropics/claude-code-action](https://redirect.github.com/anthropics/claude-code-action)
| action | digest | `593d7a5` → `4d7e1f0` |

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - "on monday"
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/cowprotocol/services).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yMTkuMCIsInVwZGF0ZWRJblZlciI6IjQzLjIxOS4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…ol#4422)

# Description
  
Replaces our driver's third-party Uniswap V3 subgraph dependency with
our own pool-indexer service. This is the **subgraph-bootstrap slice**
of the larger cowprotocol#4349, scoped down to keep review focused. Includes a few
fixes surfaced during local testing.

Out of scope (deferred to follow-up PRs):
- Cold-seed-from-genesis (chains without a subgraph)
- Multi-factory-per-network

# Changes
  
**New service: `crates/pool-indexer/`**
- Bootstraps pool / tick state from an existing Uniswap V3 subgraph
- Follows head via `eth_getLogs` and persists incremental state to
Postgres
- Serves four HTTP routes consumed by the driver: `/pools`,
`/pools/by-ids`, `/pools/{addr}/ticks`, `/pools/ticks`

**Driver-side abstraction** (same as original PR, already reviewed)
- New `V3PoolDataSource` trait in
`liquidity-sources/src/uniswap_v3/mod.rs`
- Two impls: existing `UniV3SubgraphClient` (no behavior change) + new
`PoolIndexerClient`
- `build_pool_data_source` selects the impl based on the optional
`pool-indexer-url` driver config; defaults to subgraph

**Database migration: `V110__pool_indexer_uniswap_v3.sql`**
- 4 tables: `pool_indexer_checkpoints`, `uniswap_v3_pools`,
  `uniswap_v3_pool_states`, `uniswap_v3_ticks`
- Partial indexes on `IS NULL` columns for the backfill hot path

**E2E tests** (`crates/e2e/tests/e2e/pool_indexer.rs`)
- `driver_integration` — driver → pool-indexer wiring, asserted via
  per-route request counters on all four endpoints
- `checkpoint_resume` — restart idempotency: pool count, per-pool state,
  and checkpoint advance all survive a stop+start
- `api_errors` — input validation: 400 on unparseable address, 200 +
  empty ticks on valid-but-unknown address
- `pagination` — cursor traversal with `limit=1` across multiple pools,
  no duplicates, terminates on `next_cursor = null`
- Mock V3 factory and pool inlined via `alloy::sol!` with embedded
  compiled bytecode — no additions to the `contracts` crate

## How to test

### 1. e2e tests

Expect 4 passes in ~10s after the build settles. These cover the
driver↔indexer wiring, restart idempotency, the input-validation
surface, and cursor pagination. (Same from original)

### 2. Manual (against a real network with a Uniswap V3 subgraph)

```bash
# tears down + recreates local stack, applies migrations,
# runs the indexer in release mode. Wipes the local DB volume each run.
./crates/pool-indexer/run-local.sh
```

Before running, create `crates/pool-indexer/config.local.toml` (schema =
`Configuration` struct in `src/config.rs`). String fields accept
`%ENV_VAR`, so RPC URLs and subgraph bearer tokens can come from the
environment.

Once the indexer is live, point a local driver at it by setting
`pool-indexer-url = "http://localhost:8080"` in the driver's Uniswap V3
liquidity config (replacing the usual. `graph-url`). Then submit a quote
environment.

Once the indexer is live, point a local driver at it by setting
`pool-indexer-url = "http://localhost:8080"` in the driver's Uniswap V3
liquidity config (replacing the usual `graph-url`). Then submit a quote
and confirm the log line `uniswap v3: using pool-indexer as data source
url=...` appears — that's the driver picking the indexer path.

**Verified manually**: Ink mainnet (chain 57073), Uniswap V3 subgraph
(via bearer auth), USDT0 ↔ WETH quotes in both directions — prices
internally consistent and matched the live market.
# Description
Removes a bunch of "dead code" — follow up to
cowprotocol#4370 (comment)

# Changes

* Removes error paths that are no longer triggered

## How to test
Existing tests
# Description
Our uni v3 liquidity indexing currently works like this:
1. fetch all known pools and most recent state from subgraph
2. spawn background task that continue to update the pool states

Since this approach does not learn about new pools that were created
AFTER fetching the known pools at the process start this logic was
effectively wrapped in an interval which periodically re-runs this logic
(see cowprotocol#1759).

That logic assumed that the cache will get dropped when we reinitialize
the liquidity source. However, because each initialization also spawns a
maintenance background task that has a STRONG reference to the cache
this cache will actually never be freed on re-init.
The result is that we get a full copy of the uni v3 cached states every
12h.
This can be seen nicely by the logs which shows that every 12h we get
more logs with `running maintenance` which results in a visible step
function.
<img width="1855" height="414" alt="Screenshot 2026-06-16 at 10 07 13"
src="https://github.com/user-attachments/assets/6adaf315-82bd-49a8-88fd-04ce74b0153a"
/>

# Changes
I adjusted the `ServiceMaintenance` task to take weak references and
terminate gracefully if none of the original sub-tasks need to run
anymore.

## How to test
added a unit test
# Description
In the heap memory profiles of the driver 10-13% belong to the cache
storing all known uni v3 pools.

# Changes
This PR applies 2 simple optimizations:
1. turn cache from `HashMap<K, HashSet<Address>>` to `HashMap<K,
Vec<Address>>`. HashSets require significantly more memory than vectors
because they have more bookkeeping and also always leave some slots
empty to manage hash collisions. And since we never need random access
of those sets vector will do just fine.
2. call `.shrink_to_fit()` on the cache and all it's values to minimize
the used memory. This cache will never be updated so any memory that is
there to accommodate future growth is just wasted.

<img width="1728" height="507" alt="Screenshot 2026-06-15 at 19 51 08"
src="https://github.com/user-attachments/assets/1dbca7f9-1702-4565-914a-0760690b7970"
/>
Limit orders require the solver to declare a fee (Fee::Dynamic).
Setting fee: None was only valid for market orders (Fee::Static).
Use fee: Some(U256::ZERO) so the solver declares a 0 fee, which is
valid for all order types. The protocol still collects its own fee
via fee policies.
@github-actions

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 9 committers have signed the CLA.
@jmg-duarte
@MartinquaXD
@fafk
@openjarvis2026
@m-sz
@squadgazzz
@AryanGodara
@extrawurst
@felixjff
You can retrigger this bot by commenting recheck in this Pull Request. Posted by the CLA Assistant Lite bot.

Comment thread contracts/Cargo.lock
Comment on lines +1333 to +1343
[[package]]
name = "rustls-webpki"
version = "0.103.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef"
dependencies = [
"aws-lc-rs",
"ring",
"rustls-pki-types",
"untrusted",
]
Comment thread contracts/Cargo.lock
Comment on lines +1333 to +1343
[[package]]
name = "rustls-webpki"
version = "0.103.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef"
dependencies = [
"aws-lc-rs",
"ring",
"rustls-pki-types",
"untrusted",
]
Comment thread contracts/Cargo.lock
Comment on lines +1333 to +1343
[[package]]
name = "rustls-webpki"
version = "0.103.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef"
dependencies = [
"aws-lc-rs",
"ring",
"rustls-pki-types",
"untrusted",
]
Comment thread contracts/Cargo.lock
Comment on lines +1112 to +1121
[[package]]
name = "rand"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
dependencies = [
"rand_chacha",
"rand_core 0.9.5",
"serde",
]
Comment thread contracts/Cargo.lock
Comment on lines +1103 to +1110
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"rand_core 0.6.4",
]
@github-actions

Copy link
Copy Markdown

Reminder: Please update the DB Readme and comment whether migrations are reversible (include rollback scripts if applicable).

  • If creating new tables, update the tables list.
  • When adding a new index, consider using CREATE INDEX CONCURRENTLY for tables involved in the critical execution path.
  • For breaking changes, remember that during rollout k8s starts the new autopilot, runs the Flyway migration, and only then shuts down the old pod. That overlap means the previous version can still be processing requests on the migrated schema, so make it compatible first and ship the breaking DB change in the following release.

Caused by:

@github-actions

Copy link
Copy Markdown

Reminder: Please consider backward compatibility when modifying the API specification.
If breaking changes are unavoidable, ensure:

  • You explicitly pointed out breaking changes.
  • You communicate the changes to affected teams (at least Frontend team and SAFE team).
  • You provide proper versioning and migration mechanisms.

Caused by:

@github-actions github-actions Bot locked and limited conversation to collaborators Jun 18, 2026
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.