Skip to content

Conversation

@m-sz
Copy link
Contributor

@m-sz m-sz commented Nov 10, 2025

Sell=buy token initiative

The ability to swap the same token pairs unlocks the potential for CoW to generally become a transaction relay service. This feature will allow users to execute pre- and post- interaction hooks as gasless transactions. The overall implementation has certain caveats:

  • Each settlement has to go through the settlement contract anyway, so the sell=buy token transactions would require users to send X amount of sell token and receive X - cost of the token back. It is not realistic, with the current state of GPv2 contract for the user to just send the cost amount and save on gas this way.
  • The solvers can really compete only by batching to save on gas costs
  • There is little incentive for this feature to be supported by other solvers, but having this support in baseline solver could generally be enough.

Lift restrictions on sell=buy token

Based the changes on https://github.com/cowprotocol/services/pull/2513/files, adapted to current state of the codebase.

  • Adds a command-line argument to the orderbook API to allow for same sell and buy token swaps. The SameBuyAndSell token error is not raised in that case
  • Adds e2e test case that checks if sell=buy quoting and orders are possible
  • Liquidity fetching happens only if the sell and buy tokens are different
  • Relaxes the invariant on Tokens struct to allow for same token pairs
  • Minor tweak to Justfile to allow for custom filtering and extra args to nextest

Based on input from @fhenneke:

  • Adds a short-circuit behavior for solver to return empty route in case sell=buy
  • Returns sell and buy assets directly when solving for empty route

@m-sz m-sz force-pushed the sell=buy branch 2 times, most recently from de09f52 to 858ac73 Compare November 12, 2025 15:54
@m-sz m-sz marked this pull request as ready for review November 12, 2025 15:55
@m-sz m-sz requested a review from a team as a code owner November 12, 2025 15:55
@squadgazzz
Copy link
Contributor

I think this PR needs to be converted to a draft, since more changes are expected. @m-sz , is that correct?

@m-sz
Copy link
Contributor Author

m-sz commented Nov 13, 2025

Yep, had a bunch of 1:1s and will be adding feature flag to the whole change to control it in orderbook.

@m-sz m-sz marked this pull request as draft November 13, 2025 17:10
@m-sz m-sz marked this pull request as ready for review November 14, 2025 16:54
@m-sz
Copy link
Contributor Author

m-sz commented Nov 14, 2025

I have updated the PR with CLI argument to orderbook to allow for same sell and buy tokens.
Changes to baseline solver, to support those transactions will come in a separate PR.

@github-actions
Copy link

This pull request has been marked as stale because it has been inactive a while. Please update this pull request or it will be automatically closed.

@m-sz m-sz marked this pull request as draft December 3, 2025 10:50
@m-sz m-sz force-pushed the sell=buy branch 3 times, most recently from 405acc6 to 6fe22d1 Compare December 5, 2025 14:24
@m-sz m-sz marked this pull request as ready for review December 5, 2025 14:25
m-sz and others added 8 commits December 11, 2025 15:13
Co-authored-by: José Duarte <duarte.gmj@gmail.com>
Co-authored-by: José Duarte <duarte.gmj@gmail.com>
Co-authored-by: José Duarte <duarte.gmj@gmail.com>
Co-authored-by: José Duarte <duarte.gmj@gmail.com>
Co-authored-by: José Duarte <duarte.gmj@gmail.com>
.context("summary buy token is missing")?;

if *sell_token_lost >= sell_token_lost_limit || *buy_token_lost >= buy_token_lost_limit {
if (!sell_token_lost.is_zero() && *sell_token_lost >= sell_token_lost_limit)
Copy link
Contributor

Choose a reason for hiding this comment

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

So in other words "something that used to work now fails". I'd like a more thorough investigation than that. If adding this check is indeed the correct thing to do here there should be a convincing explanation.

Comment on lines +230 to +233
// Check for orders selling wrapped native token for native token.
if &order.sell_token == native_token && order.buy_token == BUY_ETH_ADDRESS {
return Err(PartialValidationError::SameBuyAndSellToken);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this always forbidden? With the new feature this should be allowed when the flag is enabled, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will test it out

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is that a case we want to support? As I understand, right now if a user places such order we are simply unwrapping their WETH for ETH, same goes the other way around.

I tried if it works, and it seems that in order to support that, we would need to plug in native token everywhere where I check for sell=buy (so into the solver for example), and then compare the tokens to native or ETH buy address

input,
output,
interactions: Vec::default(),
gas: eth::Gas(U256::ZERO) + self.solution_gas_offset,
Copy link
Contributor

Choose a reason for hiding this comment

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

Did you check that there is logic somewhere that accounts for the order hooks? I'm a bit worried that the baseline solver will start bleeding money when it doesn't consider the gas costs correctly.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The routing (which is present in the non sell=buy case) takes a Request and max amount of hops.
Request contains wrappers (which I understand are the hooks), and they are present in the solution in both cases.

Routing itself cares about the sell and buy token, computing a path to solve for them, not taking into account wrappers. So I think we should be fine, but I will check it on staging with hooks.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will introduce an E2E test case verifying the hooks gas usage in sell=buy orders in a follow-up PR.

m-sz and others added 5 commits December 12, 2025 12:33
Co-authored-by: Martin Magnus <martin.beckmann@protonmail.com>
Co-authored-by: Martin Magnus <martin.beckmann@protonmail.com>
@m-sz m-sz requested a review from MartinquaXD December 15, 2025 11:55
@m-sz
Copy link
Contributor Author

m-sz commented Dec 16, 2025

I have revisited how we calcualate the before and after balance. This now allows us to avoid the .is_zero checks while ensuring quoting accuracy and seems like a more correct approach.

m-sz added 4 commits December 16, 2025 15:57
Introduce special case to handle sell=buy when calculating the out amounts.
The out_amount had to be extended to I512, to account for negative
outcomes of the previous calculation
@m-sz
Copy link
Contributor Author

m-sz commented Dec 17, 2025

With @MartinquaXD's help, We have also came up with a correct way to account for sell and buy token's out_amount during trade verification steps. The previous could would have worked with current baseline solver (as the input and output amounts of sell=buy are the same), but if any external solver would (more realistically) provide a quote with output amount being smaller (not equal) than the input amount, the solution would break. The new approach uses signed arithmetic and fixes up the calculation if the tokens are the same.

Comment on lines 271 to 272
// It looks like the out_amount is 0 but only because the sell and buy tokens
// are the same.
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this comment can now be dropped in favor of the more specific one below.

@m-sz m-sz enabled auto-merge December 17, 2025 13:17
@m-sz m-sz added this pull request to the merge queue Dec 17, 2025
Merged via the queue into main with commit a1f6c2e Dec 17, 2025
18 checks passed
@m-sz m-sz deleted the sell=buy branch December 17, 2025 13:37
@github-actions github-actions bot locked and limited conversation to collaborators Dec 17, 2025
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.

7 participants