Skip to content

Conversation

@anxolin
Copy link
Owner

@anxolin anxolin commented Jul 7, 2025

Motivation

Draft script for collateral swap. I aim to use this code for our experimentation with the collateral swap using flash-loans in Sepolia, with the following goals:

  • Study feasibility of the new helper contract, constraints, and blockers in the orderbook API, or solver support
  • Explore ways to simplify the user flow (less transfers? less gas? less approvals?)
  • Allow us to iterarate in the contracts and explore alternative versions for "collateral-swap" and any other flash-loan or AAVE flow (i.e debt swap, re-pay with collateral, etc.)

Case covered

The use case I tried to covered is, collateral swap when you have a debt backed by some collateral (oldCollateral) and you want to keep the debt but swap the collateral for another one (`newCollateral).

The script uses the helper from (WIP):
cowprotocol/flash-loan-router#44

Assets involved

In this case, I set up a user with 10 aETHWeth and a debt of 1,000 GHO (I left simple instructions on how you can set a similar state in your account).

The user wants to swap:

BEFORE SWAP:
   10 aETHWeth
   1,000 GHO debt

AFTER SWAP
   XX aUSDC
   1,000 GHO debt   

, where  XX is the minimum USDC the user agreed to receive + surplus.

These are the relevant addresses:

const TOKENS = {
  oldUnderlying: "0xc558dbdd856501fcd9aaf1e62eae57a9f0629a3c", // WETH
  oldCollateral: "0x5b071b590a59395fe4025a0ccc1fcc931aac1830", // aETHWeth
  debt: "0xc4bf5cbdabe595361438f8c6a187bdc330539c60", // GHO
  newUnderlying: "0x94a9d9ac8a22534e3faca9f4e7f2e2cf85d5e4c8", // USDC
  newCollateral: "0x40d16fc0236f5686f0a7030063ca493c4dd83358", // aUSDC
} as const;

Relevant contracts

const AAVE_POOL_ADDRESS = "0x6Ae43d3271ff6888e7Fc43Fd7321a503ff738951"; // See https://search.onaave.com/?q=sepolia
const COW_AAVE_BORROWER = "0x7d9C4DeE56933151Bc5C909cfe09DEf0d315CB4A"; // See https://github.com/cowprotocol/flash-loan-router/blob/main/networks.json
const COW_AAVE_HELPER_FACTORY = "0xc55098a66d2225c37bf33c1f7b8b9b0abc8fd32f"; // https://sepolia.etherscan.io/address/0xc55098a66d2225c37bf33c1f7b8b9b0abc8fd32f#code

Main logic

  1. Gets the balance of the oldCollateral, will be used for the sell amount
  2. Pre-calculates the address of orderHelper contract based on the order details
  3. Ask for a quote with the following data
    3.1 Sells oldUnderlying for newUnderlying
    3.2 Owner and receiver is the helper contract
    3.3. Includes the flashloan hint (I still don't have clear how to ask the funds to be delivered to the helper, will need to review that part
    3.4 Creates the hook to deploy the helper contract
    3.5 Creates the hook to do the collateral swap
    3.5 Ask for the quote
    * 🚨 currently fails here (keep reading)
    * ⚠️ Also, is the quoting API smart enough to make sure the flash-loan money is available and will be used in the pre-hook making the order feasable?
  4. Post the order
    4.1 Before posting the order, sets the allowance so the oldCollateral can be transferred to the helper.
    * 🚨 We might need to modify the SDK to make it easier to use 1271 orders. When we post the order, we need to include the signature including the order, which should be decodable later GPv2Order.Data memory _order = abi.decode(_signature, (GPv2Order.Data));. Keep in mind the signature will be simpler in a future implementation, because we don't need all the order data (most of them are already constants in the contract)
    * ⚠️UX wise, this approval needs to come after the quote review from the user, this is why I placed it here, but this might be problematic for the Orderbook API because the order can't succeed before this approval is in place
    4.2 Post the order

Current appData

The order sets a pre-hook and a post-hook, so these params are a good summary on how the order looks like:

const advancedSettings: SwapAdvancedSettings = {
    additionalParams: {
      signingScheme: SigningScheme.EIP1271,
    },
    appData: {
      appCode: APP_CODE,
      metadata: {
        // @ts-ignore The flash-loan hint is still not added officially to https://github.com/cowprotocol/app-data
        flashLoan: flashLoanHint,
        hooks: {
          pre: [helperContractDeploymentHook],
          post: [collateralSwapHook],
        },
      },
    },
  };

🚨 Not working yet

On top of the issues pointed out in the "main logic" section, there are some small things to review:

  • Gas estimation fails, probably the deployment of helper is incorrect, although tenderly simulation passes (with un-expected result). Sth to debug --> debug data is logged
  • I set the amount of the order to 1wei, to avoid a cyclic dependency, we should find a better way (left some comments on the matter).
  • NoLiquidity. I ask for the quote, but I get this error because there's no liquidity for WETH-USDC, we could add liquidity in univ2 to make it work

How to test

  1. Create a test account (PK to use in the script)
  2. go to https://app.aave.com, enable sepolia (see image) and supply sepolia ETH
image
  1. Borrow some GHO
  2. Add the private key to the .env file

Env file:

RPC_URL_11155111=your-rpc
PRIVATE_KEY=your-pk

Then run:

yarn
yarn dev

Open questions

  • The design depends on the solvers to include a pre-interaction to send the flas-loaned oldCollateral from the borrower to the helper.
    • Is this already working? (do they know how to do that?)
    • Would it make sense to include a loanReceiver = helperContract
  • What's the simpler way to get the quote from the orderbook API? there's a ciclic dependency: quote determines the minReceived --> The minReceive determines the helper address --> The helper address needs to be part of the hook which presumably gets included in the quote in order for the estimation to succeed
    • Discussed the identified issue of helperAddress being dependent on the quote result, and the quote requiring the helperAddress (trader). Also UX issues with the approval to the helper changing if the quote changes
      • We discuss moving the approve to the factory to keep it constant
      • Making it unlimited (nice, so we do once)
      • We want to include in the 1271 signature payload, a ECDSA signature of the EOA to avoid exploiting the allowances
  • Indexing order, dependencies with backend?
    * [X] We don't see backend dependency issues with indexing the order. The order is born in the orderbook
    * [ ] Our understanding of Backend work is:
    - pre-interaction mentioned above in the driver
    - [good to have] keeping track of the real trader, so we can show it in the explorer
    - We can get by by having the mapping in the UI and linking directly to the order. Improble but not launch blocking
  • How to keep track of the original trader?
    * Because we might add a signature verification, we will need to add the trader in the factory
    * We can emit an event with the orderUid, and trader (ideally), or with the contract address.
    * A service can index the events and link the trader to all their collateral swap orders
  • Discussed about the idea of making the helper to be the borrower.
    * This is not required, so would be just nice to have to make this: simpler, more gas efficient
    * Problem: contract needs to be deployed
    * we could add info in the hint that we need to deploy (calldata might be a security hole for people frontrunning the settlement, but passing the factory params suffice)
    * We agree we can explore this later, is not a must and might require more changes

  • Does the orderbook API accept the 1271 even if the oldCollateral is not yet in the helper? Technically, the order will contain the flash-loan hint and the approval to move the funds from the settlement to the helper, so the funds can be made available at any time by a solver.

Backend requirements

Out of this PR, I would like we comply some expectations for the Orderbook API and driver to make this work. They should be listed here so backend can review them. These items will be know once we answer the questions above.

  • TODO: list of requirements

@anxolin anxolin marked this pull request as draft July 7, 2025 21:32

const orderHelperParams = [
trader, // owner
AAVE_POOL_ADDRESS, // borrower
Copy link
Collaborator

Choose a reason for hiding this comment

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

this is actually COW_AAVE_BORROWER
it's to where the funds need to be sent to.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants