Skip to content

Conversation

@kaze-cow
Copy link
Contributor

@kaze-cow kaze-cow commented Sep 24, 2025

Description

this introduces the ability for a wrapper contract to be included in the appdata and solutions. A wrapper is a contract which is called with the same parameters as settlement contract settle, but executes a series of operations either/both before or after executing the actual GPv2Settlement contract's settle function. This simplifies integration. see the full info here https://www.notion.so/cownation/Generalized-Wrapper-2798da5f04ca8095a2d4c56b9d17134e

Only approved wrapper contracts will be allowed to call the GPv2Settlement contract (via the current GPv2AllowlistAuthenticator), and it is expected that wrapper contracts will do the necessary validation of solvers on behalf of the settlement contract.

Its expected that generalized wrappers can simplify the implementation of many initiatives, including:

  • "flashloans" integrations (ex. aave, euler)
  • enforcable pre- and post-hooks (users can ensure that post actions must at least be attempted, such as deposit to a vault)
    • ex: with our current CowShed implementation, if cross chain send were to fail due to ex. insufficient fee, funds can be sent back to the user rather than needing to be recovered from the CowShed contract
  • TWAP
    • currently we are implementing this for EOAs, but its very difficult to ensure funds are only pulled from the users wallet when needed because there is no auth. Since the CowShed could effectively become a wrapper contract, it can pull funds from user and immediately call the necessary settle function afterwards to ensure funds are traded.

Implementation Notes

  • wrappers is added to the appData so that the frontend can "hint" the necessary flashloan or other required functionality if needed (similar to current flashloans functionality). More than one wrapper can be specified in a single order. A wrapper is combination of address, the address of the wrapper contract to execute, and data, which is any additional data required for the wrapper to execute
  • wrappers is added to solution so its easy for the driver to encode into a final solution. It is an array in order to accomodate different orders that require different wrappers.
  • additionally a solver can add their own wrapper if they determine its necessary/useful to get the best result from a trade (some already technically do this)

Potentially further changes required/limitations:

  • when quoting, how can the use of a wrapper be effectively included in the calculation?
  • this implements handling in the autopilot and driver, but what will need to happen from the solver side?

How to Test the Wrapper

I have deployed a testing EmptyWrapper to 0x54112E2F481AC239661914691082039d7B05A264. By using this contract as a simple wrapper, created a script to serve as a minimal E2E POC:

This PR can be verified to be working E2E by taking the following steps:

  1. Boot up the playground environment. I used the command: docker compose -f playground/docker-compose.fork.yml up --build
  2. With all the services stabilized and running, open a new terminal and run the provided wrapper test script: ./playground/test_wrapper.sh
  3. In the docker compose logs, observe the order being placed, an auction held, and finally a transaction submitted to chain
  4. The transaction result can be digested with cast:
  • To get the submitted transaction and all its data fields: cast tx <my transaction hash from logs>
  • To get the result of executing the transaction on-chain (the receipt): cast receipt <my transaction hash from logs>
  1. It is also possible to see the order information and the active fields for wrapper and wrapperData by opening up https://localhost:8001 (the CoW Explorer) and putting in the printed order ID at the end of script execution.

Related PRs and Documentation

this introduces the ability for a `wrapper` contract to be included in the appdata and solutions. A `wrapper` is a contract which is called with the same parameters as settlement contract `settle`, but executes a series of operations either/both before or after executing the actual GPv2Settlement contract's `settle` function. This simplifies integration.

Only approved `wrapper` contracts will be allowed to call the GPv2Settlement contract (via the current GPv2Allow), and it is expected that wrapper contracts will do the necessary validation of solvers on behalf of the settlement contract.

Its expected that generalized wrappers can simplify the implementation of many initiatives, including:
* flashloans integrations (ex. aave, euler)
* enforcable pre- and post-hooks (users can ensure that post actions must at least be attempted, such as deposit to a vault)
  * ex: with our current CowShed implementation, if cross chain send were to fail due to ex. insufficient fee, funds can be sent back to the user rather than needing to be recovered from the CowShed contract
* TWAP
  * currently we are implementing this for EOAs, but its very difficult to ensure funds are only pulled from the users wallet when needed because there is no auth. Since the CowShed could effectively become a wrapper contract, it can pull funds from user and immediately call the necessary `settle` function afterwards to ensure funds are traded.

Implementation notes:
* `wrapper` is added  to the `appData` so that the frontend can "hint" the necessary flashloan or other required functionality if needed (similar to current `flashloans` functionality)
* `wrapper` is added to solution so its easy for the driver to encode into a final solution,
* additionally a solver can add their own wrapper if they determine its necessary/useful to get the best result from a trade (some already technically do this)

Potentially further changes required/limitations:
* when quoting, how can the use of a wrapper be effectively included in the calculation?
* what happens if we more than one swap in a batch that requires a wrapper?
* since its not possible to supply additional parameters to `settle` on the wrapper, in some cases it may be required to use a `preHook` to apply settings on a `transient` variable as part of the multicall for execution
unfortunately doesn't quite work because of flaws with cargo and the complexity of managing the dependencies.

rust-lang/cargo#2644
just need to figure out why the app_data is not populating during the solve
@github-actions
Copy link

github-actions bot commented Oct 4, 2025

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.

@github-actions github-actions bot added the stale label Oct 4, 2025
@kaze-cow
Copy link
Contributor Author

kaze-cow commented Oct 4, 2025

The contracts team is taking a bit of extra time to review the design of the wrappers. Some changes will be coming next week but nothing too serious. Afterwards. We'll be requesting reviews and feedback from the back end team 👍

@github-actions github-actions bot removed the stale label Oct 5, 2025
Copy link
Contributor

@fleupold fleupold left a comment

Choose a reason for hiding this comment

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

not a real review, just a comment on the structure of the new field

@kaze-cow kaze-cow force-pushed the feat/generalized-wrapper branch from 10f7352 to 0c5cbb6 Compare October 7, 2025 07:49
@kaze-cow kaze-cow requested a review from MartinquaXD October 24, 2025 06:25

pub type WrapperCalls = HashMap<order::Uid, Vec<(H160, Vec<u8>, bool)>>;

#[allow(clippy::too_many_arguments)]
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@MartinquaXD I aws just peeking through the changes one more time and I just noticed this. For now is it ok to be overriding clippy here?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, that's fine. Although for new overrides let's use #[expect(clippy::too_many_arguments)] (see here for more context).

@kaze-cow kaze-cow requested a review from MartinquaXD October 27, 2025 10:25
Copy link
Contributor

@squadgazzz squadgazzz left a comment

Choose a reason for hiding this comment

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

LGTM, only nits

Copy link
Contributor

@MartinquaXD MartinquaXD left a comment

Choose a reason for hiding this comment

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

Thanks for adding the test. I think after addressing the last nits on the test this is ready to be merged. 🙌

Comment on lines 89 to 90
web3.alloy
.anvil_send_impersonated_transaction_with_config(
Copy link
Contributor

Choose a reason for hiding this comment

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

There is no need to impersonate the trader for this call since we have the PK available. For example the approve call below works just fine without impersonation.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed in 1f356b0

* verify that 2 wrappers nested can be called as expected
* verify that auction id can be retrieved
* dont need to impresonate trader to deposit ETH
* auction seems to be having issues pulling the trace sometimes
* actually check to make sure some expected wrapper calldata is coming through
@kaze-cow kaze-cow enabled auto-merge (squash) October 31, 2025 07:39
@kaze-cow kaze-cow merged commit 91299bd into main Oct 31, 2025
17 checks passed
@kaze-cow kaze-cow deleted the feat/generalized-wrapper branch October 31, 2025 08:03
@github-actions github-actions bot locked and limited conversation to collaborators Oct 31, 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