From 169cb46fa9b7fba3012029a88fa01e47fad22386 Mon Sep 17 00:00:00 2001 From: Dusan Stanivukovic Date: Fri, 7 Mar 2025 13:31:00 +0100 Subject: [PATCH 1/3] Single flashloan encoding (#3279) # Description Tackles https://github.com/cowprotocol/services/issues/3218 Implements on flavor of the "repay debt with collateral" idea an a e2e test. The trader is a gnosis safe to make it easier to do all the necessary pre-interactions. The basic flow is like this: 1. make trader a safe 2. deposit 50K USDC 3. borrow 1 WETH 4. build appdata * flashloan 1 WETH (`flashloan` hint) * repay 1 WETH (1st pre-hook, tx pre-signed by safe) * withdraw 50K USDC (2nd pre-hook, tx pre-signed by safe) 5. place order with that appdata like: ```json { "from": "", "receiver": "", "sell": "50_000 USDC", "buy": "1 WETH + flashloanFee", "kind": "buy", } ``` 6. assert that trade got executed, indexed and that changed balances make sense This flavor of "repay debt with collateral" leaves trace amounts of debt in the AAVE pool. The alternative would be either over paying slightly or a bunch of very complex accounting logic using another helper contract. Both are more involved and not necessary to proof the concept. # Changes some bug fixes / workarounds that were necessary to make it work: * introduce exceptions in filtering logic based on balances in autopilot and driver to prevent the order from getting filtered out * forked e2e test setup currently does some things that will not needed once the flashloan contracts are actually deployed: * deploy contracts * allow list them as solvers in the settlement contract * code to pipe the flashloan data around # Things not addressed by this PR * proper configuration options in the driver * more convenience for computing flashloan amounts that need fees * currently the fee gets added on top of the flashloan amount which means the user needs to be aware that their `buyAmount` needs to be `appdata.flashloan.amount + fee`. Ideally the driver would reduce the flashloan amount such that `loanedAmount + fee = totalSignedAmount`. * error handling * supporting multiple flashloans in the same settlement ## How to test added new forked e2e test adjusted unit test for solvable orders filtering exception for flashloan orders --------- Co-authored-by: MartinquaXD --- Cargo.lock | 1 + crates/app-data/Cargo.toml | 1 + crates/app-data/src/app_data.rs | 4 + crates/autopilot/src/run.rs | 1 + crates/autopilot/src/solvable_orders.rs | 38 ++- crates/contracts/artifacts/IAavePool.json | 3 +- crates/driver/example.toml | 2 + .../driver/src/domain/competition/auction.rs | 14 +- .../src/domain/competition/order/app_data.rs | 15 +- .../domain/competition/solution/encoding.rs | 132 +++++++- .../src/domain/competition/solution/mod.rs | 7 +- crates/driver/src/domain/eth/mod.rs | 10 +- .../driver/src/infra/blockchain/contracts.rs | 23 ++ crates/driver/src/infra/config/file/load.rs | 6 + crates/driver/src/infra/config/file/mod.rs | 5 + .../driver/src/infra/solver/dto/solution.rs | 11 +- crates/driver/src/tests/setup/blockchain.rs | 10 + crates/driver/src/tests/setup/driver.rs | 4 +- crates/driver/src/tests/setup/orderbook.rs | 8 +- crates/driver/src/tests/setup/solver.rs | 1 + crates/e2e/src/setup/colocation.rs | 4 + crates/e2e/src/setup/deploy.rs | 23 ++ .../e2e/src/setup/onchain_components/mod.rs | 17 ++ .../e2e/src/setup/onchain_components/safe.rs | 5 + crates/e2e/tests/e2e/flashloans.rs | 284 ++++++++++++++++++ crates/e2e/tests/e2e/main.rs | 1 + 26 files changed, 607 insertions(+), 23 deletions(-) create mode 100644 crates/e2e/tests/e2e/flashloans.rs diff --git a/Cargo.lock b/Cargo.lock index e4665ee611..6179f23802 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -168,6 +168,7 @@ dependencies = [ "ethcontract", "hex", "hex-literal", + "number", "primitive-types", "serde", "serde_json", diff --git a/crates/app-data/Cargo.toml b/crates/app-data/Cargo.toml index e7f46f64b2..83f6626078 100644 --- a/crates/app-data/Cargo.toml +++ b/crates/app-data/Cargo.toml @@ -15,6 +15,7 @@ serde_with = { workspace = true } primitive-types = { workspace = true } hex = { workspace = true } hex-literal = { workspace = true } +number = { path = "../number" } [dev-dependencies] ethcontract = { workspace = true } diff --git a/crates/app-data/src/app_data.rs b/crates/app-data/src/app_data.rs index f474a1c3df..290d35675e 100644 --- a/crates/app-data/src/app_data.rs +++ b/crates/app-data/src/app_data.rs @@ -1,8 +1,10 @@ use { crate::{AppDataHash, Hooks, app_data_hash::hash_full_app_data}, anyhow::{Context, Result, anyhow}, + number::serialization::HexOrDecimalU256, primitive_types::{H160, U256}, serde::{Deserialize, Deserializer, Serialize, Serializer, de}, + serde_with::serde_as, std::{fmt, fmt::Display}, }; @@ -32,6 +34,7 @@ pub struct ProtocolAppData { /// use of flashloans to settle the associated order. /// Since using flashloans introduces a bunch of complexities /// all these hints are not binding for the solver. +#[serde_as] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq)] #[cfg_attr(any(test, feature = "test_helpers"), derive(Serialize))] pub struct Flashloan { @@ -43,6 +46,7 @@ pub struct Flashloan { /// Which token to flashloan. pub token: H160, /// How much of the token to flashloan. + #[serde_as(as = "HexOrDecimalU256")] pub amount: U256, } diff --git a/crates/autopilot/src/run.rs b/crates/autopilot/src/run.rs index 60614c5e81..e03bd1f65b 100644 --- a/crates/autopilot/src/run.rs +++ b/crates/autopilot/src/run.rs @@ -468,6 +468,7 @@ pub async fn run(args: Arguments) { domain::ProtocolFees::new(&args.fee_policies, args.fee_policy_max_partner_fee), cow_amm_registry.clone(), args.run_loop_native_price_timeout, + eth.contracts().settlement().address(), ); let liveness = Arc::new(Liveness::new(args.max_auction_age)); diff --git a/crates/autopilot/src/solvable_orders.rs b/crates/autopilot/src/solvable_orders.rs index f741ea51b9..cd43ff2883 100644 --- a/crates/autopilot/src/solvable_orders.rs +++ b/crates/autopilot/src/solvable_orders.rs @@ -99,6 +99,7 @@ pub struct SolvableOrdersCache { protocol_fees: domain::ProtocolFees, cow_amm_registry: cow_amm::Registry, native_price_timeout: Duration, + settlement_contract: H160, } type Balances = HashMap; @@ -123,6 +124,7 @@ impl SolvableOrdersCache { protocol_fees: domain::ProtocolFees, cow_amm_registry: cow_amm::Registry, native_price_timeout: Duration, + settlement_contract: H160, ) -> Arc { let self_ = Arc::new(Self { min_order_validity_period, @@ -139,6 +141,7 @@ impl SolvableOrdersCache { protocol_fees, cow_amm_registry, native_price_timeout, + settlement_contract, }); self_ } @@ -181,7 +184,7 @@ impl SolvableOrdersCache { ) }; - let orders = orders_with_balance(orders, &balances); + let orders = orders_with_balance(orders, &balances, self.settlement_contract); let removed = counter.checkpoint("insufficient_balance", &orders); invalid_order_uids.extend(removed); @@ -507,10 +510,23 @@ async fn find_invalid_signature_orders( /// Removes orders that can't possibly be settled because there isn't enough /// balance. -fn orders_with_balance(mut orders: Vec, balances: &Balances) -> Vec { +fn orders_with_balance( + mut orders: Vec, + balances: &Balances, + settlement_contract: H160, +) -> Vec { // Prefer newer orders over older ones. orders.sort_by_key(|order| std::cmp::Reverse(order.metadata.creation_date)); orders.retain(|order| { + if order.data.receiver.as_ref() == Some(&settlement_contract) { + // TODO: replace with proper detection logic + // for now we assume that all orders with the settlement contract + // as the receiver are flashloan orders which unlock the necessary + // funds via a pre-interaction that can't succeed in our balance + // fetching simulation logic. + return true; + } + let balance = match balances.get(&Query::from_order(order)) { None => return false, Some(balance) => *balance, @@ -1351,6 +1367,7 @@ mod tests { #[test] fn orders_with_balance_() { + let settlement_contract = H160([1; 20]); let orders = vec![ // enough balance for sell and fee Order { @@ -1396,18 +1413,31 @@ mod tests { }, ..Default::default() }, + // considered flashloan order because of special receiver + Order { + data: OrderData { + sell_token: H160::from_low_u64_be(6), + sell_amount: 200.into(), + fee_amount: 0.into(), + partially_fillable: true, + receiver: Some(settlement_contract), + ..Default::default() + }, + ..Default::default() + }, ]; let balances = [ (Query::from_order(&orders[0]), 2.into()), (Query::from_order(&orders[1]), 1.into()), (Query::from_order(&orders[2]), 1.into()), (Query::from_order(&orders[3]), 0.into()), + (Query::from_order(&orders[4]), 0.into()), ] .into_iter() .collect(); - let expected = &[0, 2]; + let expected = &[0, 2, 4]; - let filtered = orders_with_balance(orders.clone(), &balances); + let filtered = orders_with_balance(orders.clone(), &balances, settlement_contract); assert_eq!(filtered.len(), expected.len()); for index in expected { let found = filtered.iter().any(|o| o.data == orders[*index].data); diff --git a/crates/contracts/artifacts/IAavePool.json b/crates/contracts/artifacts/IAavePool.json index 75a64d560a..555e7935e7 100644 --- a/crates/contracts/artifacts/IAavePool.json +++ b/crates/contracts/artifacts/IAavePool.json @@ -1 +1,2 @@ -{"abi":[{"type":"function","name":"FLASHLOAN_PREMIUM_TOTAL","inputs":[],"outputs":[{"name":"","type":"uint128","internalType":"uint128"}],"stateMutability":"view"},{"type":"function","name":"flashLoan","inputs":[{"name":"receiverAddress","type":"address","internalType":"address"},{"name":"assets","type":"address[]","internalType":"address[]"},{"name":"amounts","type":"uint256[]","internalType":"uint256[]"},{"name":"interestRateModes","type":"uint256[]","internalType":"uint256[]"},{"name":"onBehalfOf","type":"address","internalType":"address"},{"name":"params","type":"bytes","internalType":"bytes"},{"name":"referralCode","type":"uint16","internalType":"uint16"}],"outputs":[],"stateMutability":"nonpayable"}],"bytecode":"0x","deployedBytecode":"0x","methodIdentifiers":{"FLASHLOAN_PREMIUM_TOTAL()":"074b2e43","flashLoan(address,address[],uint256[],uint256[],address,bytes,uint16)":"ab9c4b5d"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.28+commit.7893614a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"FLASHLOAN_PREMIUM_TOTAL\",\"outputs\":[{\"internalType\":\"uint128\",\"name\":\"\",\"type\":\"uint128\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiverAddress\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"assets\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"amounts\",\"type\":\"uint256[]\"},{\"internalType\":\"uint256[]\",\"name\":\"interestRateModes\",\"type\":\"uint256[]\"},{\"internalType\":\"address\",\"name\":\"onBehalfOf\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"params\",\"type\":\"bytes\"},{\"internalType\":\"uint16\",\"name\":\"referralCode\",\"type\":\"uint16\"}],\"name\":\"flashLoan\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"author\":\"Aave\",\"kind\":\"dev\",\"methods\":{\"FLASHLOAN_PREMIUM_TOTAL()\":{\"returns\":{\"_0\":\"The total fee on flashloans\"}},\"flashLoan(address,address[],uint256[],uint256[],address,bytes,uint16)\":{\"details\":\"IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept into consideration. For further details please visit https://docs.aave.com/developers/\",\"params\":{\"amounts\":\"The amounts of the assets being flash-borrowed\",\"assets\":\"The addresses of the assets being flash-borrowed\",\"interestRateModes\":\"Types of the debt to open if the flash loan is not returned: 0 -> Don't open any debt, just revert if funds can't be transferred from the receiver 1 -> Open debt at stable rate for the value of the amount flash-borrowed to the `onBehalfOf` address 2 -> Open debt at variable rate for the value of the amount flash-borrowed to the `onBehalfOf` address\",\"onBehalfOf\":\"The address that will receive the debt in the case of using on `modes` 1 or 2\",\"params\":\"Variadic packed params to pass to the receiver as extra information\",\"receiverAddress\":\"The address of the contract receiving the funds, implementing IFlashLoanReceiver interface\",\"referralCode\":\"The code used to register the integrator originating the operation, for potential rewards. 0 if the action is executed directly by the user, without any middle-man\"}}},\"title\":\"IPool\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"FLASHLOAN_PREMIUM_TOTAL()\":{\"notice\":\"Returns the total fee on flash loans\"},\"flashLoan(address,address[],uint256[],uint256[],address,bytes,uint16)\":{\"notice\":\"Allows smartcontracts to access the liquidity of the pool within one transaction, as long as the amount taken plus a fee is returned.\"}},\"notice\":\"Defines the basic interface for an Aave Pool.\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/vendored/IAavePool.sol\":\"IAavePool\"},\"evmVersion\":\"cancun\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":1000000},\"remappings\":[\":forge-std/=lib/forge-std/src/\"]},\"sources\":{\"src/vendored/IAavePool.sol\":{\"keccak256\":\"0x2086e04514230a6759d9c41147d4f7e9ee4810e3a2c700ef06a17459c916eeb8\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://a94216a796da8e1cbebd19e3ca69bc509c395157ef369a60ca4063c060bfe4c5\",\"dweb:/ipfs/QmW2ZDANxiBqYaaptspF4EHqskpDPAzFasea2uTtbHH5ZD\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.28+commit.7893614a"},"language":"Solidity","output":{"abi":[{"inputs":[],"stateMutability":"view","type":"function","name":"FLASHLOAN_PREMIUM_TOTAL","outputs":[{"internalType":"uint128","name":"","type":"uint128"}]},{"inputs":[{"internalType":"address","name":"receiverAddress","type":"address"},{"internalType":"address[]","name":"assets","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"uint256[]","name":"interestRateModes","type":"uint256[]"},{"internalType":"address","name":"onBehalfOf","type":"address"},{"internalType":"bytes","name":"params","type":"bytes"},{"internalType":"uint16","name":"referralCode","type":"uint16"}],"stateMutability":"nonpayable","type":"function","name":"flashLoan"}],"devdoc":{"kind":"dev","methods":{"FLASHLOAN_PREMIUM_TOTAL()":{"returns":{"_0":"The total fee on flashloans"}},"flashLoan(address,address[],uint256[],uint256[],address,bytes,uint16)":{"details":"IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept into consideration. For further details please visit https://docs.aave.com/developers/","params":{"amounts":"The amounts of the assets being flash-borrowed","assets":"The addresses of the assets being flash-borrowed","interestRateModes":"Types of the debt to open if the flash loan is not returned: 0 -> Don't open any debt, just revert if funds can't be transferred from the receiver 1 -> Open debt at stable rate for the value of the amount flash-borrowed to the `onBehalfOf` address 2 -> Open debt at variable rate for the value of the amount flash-borrowed to the `onBehalfOf` address","onBehalfOf":"The address that will receive the debt in the case of using on `modes` 1 or 2","params":"Variadic packed params to pass to the receiver as extra information","receiverAddress":"The address of the contract receiving the funds, implementing IFlashLoanReceiver interface","referralCode":"The code used to register the integrator originating the operation, for potential rewards. 0 if the action is executed directly by the user, without any middle-man"}}},"version":1},"userdoc":{"kind":"user","methods":{"FLASHLOAN_PREMIUM_TOTAL()":{"notice":"Returns the total fee on flash loans"},"flashLoan(address,address[],uint256[],uint256[],address,bytes,uint16)":{"notice":"Allows smartcontracts to access the liquidity of the pool within one transaction, as long as the amount taken plus a fee is returned."}},"version":1}},"settings":{"remappings":["forge-std/=lib/forge-std/src/"],"optimizer":{"enabled":true,"runs":1000000},"metadata":{"bytecodeHash":"ipfs"},"compilationTarget":{"src/vendored/IAavePool.sol":"IAavePool"},"evmVersion":"cancun","libraries":{}},"sources":{"src/vendored/IAavePool.sol":{"keccak256":"0x2086e04514230a6759d9c41147d4f7e9ee4810e3a2c700ef06a17459c916eeb8","urls":["bzz-raw://a94216a796da8e1cbebd19e3ca69bc509c395157ef369a60ca4063c060bfe4c5","dweb:/ipfs/QmW2ZDANxiBqYaaptspF4EHqskpDPAzFasea2uTtbHH5ZD"],"license":"MIT"}},"version":1},"id":32} \ No newline at end of file + +{"abi":[{"type":"function","name":"ADDRESSES_PROVIDER","inputs":[],"outputs":[{"name":"","type":"address","internalType":"contract IPoolAddressesProvider"}],"stateMutability":"view"},{"type":"function","name":"BRIDGE_PROTOCOL_FEE","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"FLASHLOAN_PREMIUM_TOTAL","inputs":[],"outputs":[{"name":"","type":"uint128","internalType":"uint128"}],"stateMutability":"view"},{"type":"function","name":"FLASHLOAN_PREMIUM_TO_PROTOCOL","inputs":[],"outputs":[{"name":"","type":"uint128","internalType":"uint128"}],"stateMutability":"view"},{"type":"function","name":"MAX_NUMBER_RESERVES","inputs":[],"outputs":[{"name":"","type":"uint16","internalType":"uint16"}],"stateMutability":"view"},{"type":"function","name":"backUnbacked","inputs":[{"name":"asset","type":"address","internalType":"address"},{"name":"amount","type":"uint256","internalType":"uint256"},{"name":"fee","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"nonpayable"},{"type":"function","name":"borrow","inputs":[{"name":"asset","type":"address","internalType":"address"},{"name":"amount","type":"uint256","internalType":"uint256"},{"name":"interestRateMode","type":"uint256","internalType":"uint256"},{"name":"referralCode","type":"uint16","internalType":"uint16"},{"name":"onBehalfOf","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"configureEModeCategory","inputs":[{"name":"id","type":"uint8","internalType":"uint8"},{"name":"config","type":"tuple","internalType":"struct DataTypes.EModeCategoryBaseConfiguration","components":[{"name":"ltv","type":"uint16","internalType":"uint16"},{"name":"liquidationThreshold","type":"uint16","internalType":"uint16"},{"name":"liquidationBonus","type":"uint16","internalType":"uint16"},{"name":"label","type":"string","internalType":"string"}]}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"configureEModeCategoryBorrowableBitmap","inputs":[{"name":"id","type":"uint8","internalType":"uint8"},{"name":"borrowableBitmap","type":"uint128","internalType":"uint128"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"configureEModeCategoryCollateralBitmap","inputs":[{"name":"id","type":"uint8","internalType":"uint8"},{"name":"collateralBitmap","type":"uint128","internalType":"uint128"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"deposit","inputs":[{"name":"asset","type":"address","internalType":"address"},{"name":"amount","type":"uint256","internalType":"uint256"},{"name":"onBehalfOf","type":"address","internalType":"address"},{"name":"referralCode","type":"uint16","internalType":"uint16"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"dropReserve","inputs":[{"name":"asset","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"eliminateReserveDeficit","inputs":[{"name":"asset","type":"address","internalType":"address"},{"name":"amount","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"finalizeTransfer","inputs":[{"name":"asset","type":"address","internalType":"address"},{"name":"from","type":"address","internalType":"address"},{"name":"to","type":"address","internalType":"address"},{"name":"amount","type":"uint256","internalType":"uint256"},{"name":"balanceFromBefore","type":"uint256","internalType":"uint256"},{"name":"balanceToBefore","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"flashLoan","inputs":[{"name":"receiverAddress","type":"address","internalType":"address"},{"name":"assets","type":"address[]","internalType":"address[]"},{"name":"amounts","type":"uint256[]","internalType":"uint256[]"},{"name":"interestRateModes","type":"uint256[]","internalType":"uint256[]"},{"name":"onBehalfOf","type":"address","internalType":"address"},{"name":"params","type":"bytes","internalType":"bytes"},{"name":"referralCode","type":"uint16","internalType":"uint16"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"flashLoanSimple","inputs":[{"name":"receiverAddress","type":"address","internalType":"address"},{"name":"asset","type":"address","internalType":"address"},{"name":"amount","type":"uint256","internalType":"uint256"},{"name":"params","type":"bytes","internalType":"bytes"},{"name":"referralCode","type":"uint16","internalType":"uint16"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"getBorrowLogic","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"getBridgeLogic","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"getConfiguration","inputs":[{"name":"asset","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"tuple","internalType":"struct DataTypes.ReserveConfigurationMap","components":[{"name":"data","type":"uint256","internalType":"uint256"}]}],"stateMutability":"view"},{"type":"function","name":"getEModeCategoryBorrowableBitmap","inputs":[{"name":"id","type":"uint8","internalType":"uint8"}],"outputs":[{"name":"","type":"uint128","internalType":"uint128"}],"stateMutability":"view"},{"type":"function","name":"getEModeCategoryCollateralBitmap","inputs":[{"name":"id","type":"uint8","internalType":"uint8"}],"outputs":[{"name":"","type":"uint128","internalType":"uint128"}],"stateMutability":"view"},{"type":"function","name":"getEModeCategoryCollateralConfig","inputs":[{"name":"id","type":"uint8","internalType":"uint8"}],"outputs":[{"name":"","type":"tuple","internalType":"struct DataTypes.CollateralConfig","components":[{"name":"ltv","type":"uint16","internalType":"uint16"},{"name":"liquidationThreshold","type":"uint16","internalType":"uint16"},{"name":"liquidationBonus","type":"uint16","internalType":"uint16"}]}],"stateMutability":"view"},{"type":"function","name":"getEModeCategoryData","inputs":[{"name":"id","type":"uint8","internalType":"uint8"}],"outputs":[{"name":"","type":"tuple","internalType":"struct DataTypes.EModeCategoryLegacy","components":[{"name":"ltv","type":"uint16","internalType":"uint16"},{"name":"liquidationThreshold","type":"uint16","internalType":"uint16"},{"name":"liquidationBonus","type":"uint16","internalType":"uint16"},{"name":"priceSource","type":"address","internalType":"address"},{"name":"label","type":"string","internalType":"string"}]}],"stateMutability":"view"},{"type":"function","name":"getEModeCategoryLabel","inputs":[{"name":"id","type":"uint8","internalType":"uint8"}],"outputs":[{"name":"","type":"string","internalType":"string"}],"stateMutability":"view"},{"type":"function","name":"getEModeLogic","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"getFlashLoanLogic","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"getLiquidationGracePeriod","inputs":[{"name":"asset","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"uint40","internalType":"uint40"}],"stateMutability":"view"},{"type":"function","name":"getLiquidationLogic","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"getPoolLogic","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"getReserveAToken","inputs":[{"name":"asset","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"getReserveAddressById","inputs":[{"name":"id","type":"uint16","internalType":"uint16"}],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"getReserveData","inputs":[{"name":"asset","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"tuple","internalType":"struct DataTypes.ReserveDataLegacy","components":[{"name":"configuration","type":"tuple","internalType":"struct DataTypes.ReserveConfigurationMap","components":[{"name":"data","type":"uint256","internalType":"uint256"}]},{"name":"liquidityIndex","type":"uint128","internalType":"uint128"},{"name":"currentLiquidityRate","type":"uint128","internalType":"uint128"},{"name":"variableBorrowIndex","type":"uint128","internalType":"uint128"},{"name":"currentVariableBorrowRate","type":"uint128","internalType":"uint128"},{"name":"currentStableBorrowRate","type":"uint128","internalType":"uint128"},{"name":"lastUpdateTimestamp","type":"uint40","internalType":"uint40"},{"name":"id","type":"uint16","internalType":"uint16"},{"name":"aTokenAddress","type":"address","internalType":"address"},{"name":"stableDebtTokenAddress","type":"address","internalType":"address"},{"name":"variableDebtTokenAddress","type":"address","internalType":"address"},{"name":"interestRateStrategyAddress","type":"address","internalType":"address"},{"name":"accruedToTreasury","type":"uint128","internalType":"uint128"},{"name":"unbacked","type":"uint128","internalType":"uint128"},{"name":"isolationModeTotalDebt","type":"uint128","internalType":"uint128"}]}],"stateMutability":"view"},{"type":"function","name":"getReserveDeficit","inputs":[{"name":"asset","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"getReserveNormalizedIncome","inputs":[{"name":"asset","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"getReserveNormalizedVariableDebt","inputs":[{"name":"asset","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"getReserveVariableDebtToken","inputs":[{"name":"asset","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"getReservesCount","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"getReservesList","inputs":[],"outputs":[{"name":"","type":"address[]","internalType":"address[]"}],"stateMutability":"view"},{"type":"function","name":"getSupplyLogic","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"getUserAccountData","inputs":[{"name":"user","type":"address","internalType":"address"}],"outputs":[{"name":"totalCollateralBase","type":"uint256","internalType":"uint256"},{"name":"totalDebtBase","type":"uint256","internalType":"uint256"},{"name":"availableBorrowsBase","type":"uint256","internalType":"uint256"},{"name":"currentLiquidationThreshold","type":"uint256","internalType":"uint256"},{"name":"ltv","type":"uint256","internalType":"uint256"},{"name":"healthFactor","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"getUserConfiguration","inputs":[{"name":"user","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"tuple","internalType":"struct DataTypes.UserConfigurationMap","components":[{"name":"data","type":"uint256","internalType":"uint256"}]}],"stateMutability":"view"},{"type":"function","name":"getUserEMode","inputs":[{"name":"user","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"getVirtualUnderlyingBalance","inputs":[{"name":"asset","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"uint128","internalType":"uint128"}],"stateMutability":"view"},{"type":"function","name":"initReserve","inputs":[{"name":"asset","type":"address","internalType":"address"},{"name":"aTokenAddress","type":"address","internalType":"address"},{"name":"variableDebtAddress","type":"address","internalType":"address"},{"name":"interestRateStrategyAddress","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"liquidationCall","inputs":[{"name":"collateralAsset","type":"address","internalType":"address"},{"name":"debtAsset","type":"address","internalType":"address"},{"name":"user","type":"address","internalType":"address"},{"name":"debtToCover","type":"uint256","internalType":"uint256"},{"name":"receiveAToken","type":"bool","internalType":"bool"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"mintToTreasury","inputs":[{"name":"assets","type":"address[]","internalType":"address[]"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"mintUnbacked","inputs":[{"name":"asset","type":"address","internalType":"address"},{"name":"amount","type":"uint256","internalType":"uint256"},{"name":"onBehalfOf","type":"address","internalType":"address"},{"name":"referralCode","type":"uint16","internalType":"uint16"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"repay","inputs":[{"name":"asset","type":"address","internalType":"address"},{"name":"amount","type":"uint256","internalType":"uint256"},{"name":"interestRateMode","type":"uint256","internalType":"uint256"},{"name":"onBehalfOf","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"nonpayable"},{"type":"function","name":"repayWithATokens","inputs":[{"name":"asset","type":"address","internalType":"address"},{"name":"amount","type":"uint256","internalType":"uint256"},{"name":"interestRateMode","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"nonpayable"},{"type":"function","name":"repayWithPermit","inputs":[{"name":"asset","type":"address","internalType":"address"},{"name":"amount","type":"uint256","internalType":"uint256"},{"name":"interestRateMode","type":"uint256","internalType":"uint256"},{"name":"onBehalfOf","type":"address","internalType":"address"},{"name":"deadline","type":"uint256","internalType":"uint256"},{"name":"permitV","type":"uint8","internalType":"uint8"},{"name":"permitR","type":"bytes32","internalType":"bytes32"},{"name":"permitS","type":"bytes32","internalType":"bytes32"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"nonpayable"},{"type":"function","name":"rescueTokens","inputs":[{"name":"token","type":"address","internalType":"address"},{"name":"to","type":"address","internalType":"address"},{"name":"amount","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"resetIsolationModeTotalDebt","inputs":[{"name":"asset","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"setConfiguration","inputs":[{"name":"asset","type":"address","internalType":"address"},{"name":"configuration","type":"tuple","internalType":"struct DataTypes.ReserveConfigurationMap","components":[{"name":"data","type":"uint256","internalType":"uint256"}]}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"setLiquidationGracePeriod","inputs":[{"name":"asset","type":"address","internalType":"address"},{"name":"until","type":"uint40","internalType":"uint40"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"setReserveInterestRateStrategyAddress","inputs":[{"name":"asset","type":"address","internalType":"address"},{"name":"rateStrategyAddress","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"setUserEMode","inputs":[{"name":"categoryId","type":"uint8","internalType":"uint8"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"setUserUseReserveAsCollateral","inputs":[{"name":"asset","type":"address","internalType":"address"},{"name":"useAsCollateral","type":"bool","internalType":"bool"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"supply","inputs":[{"name":"asset","type":"address","internalType":"address"},{"name":"amount","type":"uint256","internalType":"uint256"},{"name":"onBehalfOf","type":"address","internalType":"address"},{"name":"referralCode","type":"uint16","internalType":"uint16"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"supplyWithPermit","inputs":[{"name":"asset","type":"address","internalType":"address"},{"name":"amount","type":"uint256","internalType":"uint256"},{"name":"onBehalfOf","type":"address","internalType":"address"},{"name":"referralCode","type":"uint16","internalType":"uint16"},{"name":"deadline","type":"uint256","internalType":"uint256"},{"name":"permitV","type":"uint8","internalType":"uint8"},{"name":"permitR","type":"bytes32","internalType":"bytes32"},{"name":"permitS","type":"bytes32","internalType":"bytes32"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"syncIndexesState","inputs":[{"name":"asset","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"syncRatesState","inputs":[{"name":"asset","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"updateBridgeProtocolFee","inputs":[{"name":"bridgeProtocolFee","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"updateFlashloanPremiums","inputs":[{"name":"flashLoanPremiumTotal","type":"uint128","internalType":"uint128"},{"name":"flashLoanPremiumToProtocol","type":"uint128","internalType":"uint128"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"withdraw","inputs":[{"name":"asset","type":"address","internalType":"address"},{"name":"amount","type":"uint256","internalType":"uint256"},{"name":"to","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"nonpayable"},{"type":"event","name":"BackUnbacked","inputs":[{"name":"reserve","type":"address","indexed":true,"internalType":"address"},{"name":"backer","type":"address","indexed":true,"internalType":"address"},{"name":"amount","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"fee","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"type":"event","name":"Borrow","inputs":[{"name":"reserve","type":"address","indexed":true,"internalType":"address"},{"name":"user","type":"address","indexed":false,"internalType":"address"},{"name":"onBehalfOf","type":"address","indexed":true,"internalType":"address"},{"name":"amount","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"interestRateMode","type":"uint8","indexed":false,"internalType":"enum DataTypes.InterestRateMode"},{"name":"borrowRate","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"referralCode","type":"uint16","indexed":true,"internalType":"uint16"}],"anonymous":false},{"type":"event","name":"DeficitCovered","inputs":[{"name":"reserve","type":"address","indexed":true,"internalType":"address"},{"name":"caller","type":"address","indexed":false,"internalType":"address"},{"name":"amountCovered","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"type":"event","name":"DeficitCreated","inputs":[{"name":"user","type":"address","indexed":true,"internalType":"address"},{"name":"debtAsset","type":"address","indexed":true,"internalType":"address"},{"name":"amountCreated","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"type":"event","name":"FlashLoan","inputs":[{"name":"target","type":"address","indexed":true,"internalType":"address"},{"name":"initiator","type":"address","indexed":false,"internalType":"address"},{"name":"asset","type":"address","indexed":true,"internalType":"address"},{"name":"amount","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"interestRateMode","type":"uint8","indexed":false,"internalType":"enum DataTypes.InterestRateMode"},{"name":"premium","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"referralCode","type":"uint16","indexed":true,"internalType":"uint16"}],"anonymous":false},{"type":"event","name":"IsolationModeTotalDebtUpdated","inputs":[{"name":"asset","type":"address","indexed":true,"internalType":"address"},{"name":"totalDebt","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"type":"event","name":"LiquidationCall","inputs":[{"name":"collateralAsset","type":"address","indexed":true,"internalType":"address"},{"name":"debtAsset","type":"address","indexed":true,"internalType":"address"},{"name":"user","type":"address","indexed":true,"internalType":"address"},{"name":"debtToCover","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"liquidatedCollateralAmount","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"liquidator","type":"address","indexed":false,"internalType":"address"},{"name":"receiveAToken","type":"bool","indexed":false,"internalType":"bool"}],"anonymous":false},{"type":"event","name":"MintUnbacked","inputs":[{"name":"reserve","type":"address","indexed":true,"internalType":"address"},{"name":"user","type":"address","indexed":false,"internalType":"address"},{"name":"onBehalfOf","type":"address","indexed":true,"internalType":"address"},{"name":"amount","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"referralCode","type":"uint16","indexed":true,"internalType":"uint16"}],"anonymous":false},{"type":"event","name":"MintedToTreasury","inputs":[{"name":"reserve","type":"address","indexed":true,"internalType":"address"},{"name":"amountMinted","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"type":"event","name":"Repay","inputs":[{"name":"reserve","type":"address","indexed":true,"internalType":"address"},{"name":"user","type":"address","indexed":true,"internalType":"address"},{"name":"repayer","type":"address","indexed":true,"internalType":"address"},{"name":"amount","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"useATokens","type":"bool","indexed":false,"internalType":"bool"}],"anonymous":false},{"type":"event","name":"ReserveDataUpdated","inputs":[{"name":"reserve","type":"address","indexed":true,"internalType":"address"},{"name":"liquidityRate","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"stableBorrowRate","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"variableBorrowRate","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"liquidityIndex","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"variableBorrowIndex","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"type":"event","name":"ReserveUsedAsCollateralDisabled","inputs":[{"name":"reserve","type":"address","indexed":true,"internalType":"address"},{"name":"user","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"event","name":"ReserveUsedAsCollateralEnabled","inputs":[{"name":"reserve","type":"address","indexed":true,"internalType":"address"},{"name":"user","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"event","name":"Supply","inputs":[{"name":"reserve","type":"address","indexed":true,"internalType":"address"},{"name":"user","type":"address","indexed":false,"internalType":"address"},{"name":"onBehalfOf","type":"address","indexed":true,"internalType":"address"},{"name":"amount","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"referralCode","type":"uint16","indexed":true,"internalType":"uint16"}],"anonymous":false},{"type":"event","name":"UserEModeSet","inputs":[{"name":"user","type":"address","indexed":true,"internalType":"address"},{"name":"categoryId","type":"uint8","indexed":false,"internalType":"uint8"}],"anonymous":false},{"type":"event","name":"Withdraw","inputs":[{"name":"reserve","type":"address","indexed":true,"internalType":"address"},{"name":"user","type":"address","indexed":true,"internalType":"address"},{"name":"to","type":"address","indexed":true,"internalType":"address"},{"name":"amount","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false}],"bytecode":"0x","deployedBytecode":"0x","methodIdentifiers":{"ADDRESSES_PROVIDER()":"0542975c","BRIDGE_PROTOCOL_FEE()":"272d9072","FLASHLOAN_PREMIUM_TOTAL()":"074b2e43","FLASHLOAN_PREMIUM_TO_PROTOCOL()":"6a99c036","MAX_NUMBER_RESERVES()":"f8119d51","backUnbacked(address,uint256,uint256)":"d65dc7a1","borrow(address,uint256,uint256,uint16,address)":"a415bcad","configureEModeCategory(uint8,(uint16,uint16,uint16,string))":"7b75d7f4","configureEModeCategoryBorrowableBitmap(uint8,uint128)":"ff72158a","configureEModeCategoryCollateralBitmap(uint8,uint128)":"92380ecb","deposit(address,uint256,address,uint16)":"e8eda9df","dropReserve(address)":"63c9b860","eliminateReserveDeficit(address,uint256)":"a1d2f3c4","finalizeTransfer(address,address,address,uint256,uint256,uint256)":"d5ed3933","flashLoan(address,address[],uint256[],uint256[],address,bytes,uint16)":"ab9c4b5d","flashLoanSimple(address,address,uint256,bytes,uint16)":"42b0b77c","getBorrowLogic()":"2be29fa7","getBridgeLogic()":"df374c36","getConfiguration(address)":"c44b11f7","getEModeCategoryBorrowableBitmap(uint8)":"903a2c71","getEModeCategoryCollateralBitmap(uint8)":"b0771dba","getEModeCategoryCollateralConfig(uint8)":"b286f467","getEModeCategoryData(uint8)":"6c6f6ae1","getEModeCategoryLabel(uint8)":"2083e183","getEModeLogic()":"f32b9a73","getFlashLoanLogic()":"348fde0f","getLiquidationGracePeriod(address)":"5c9a8b18","getLiquidationLogic()":"911a3413","getPoolLogic()":"d3350155","getReserveAToken(address)":"cff027d9","getReserveAddressById(uint16)":"52751797","getReserveData(address)":"35ea6a75","getReserveDeficit(address)":"c952485d","getReserveNormalizedIncome(address)":"d15e0053","getReserveNormalizedVariableDebt(address)":"386497fd","getReserveVariableDebtToken(address)":"365090a0","getReservesCount()":"72218d04","getReservesList()":"d1946dbc","getSupplyLogic()":"870e7744","getUserAccountData(address)":"bf92857c","getUserConfiguration(address)":"4417a583","getUserEMode(address)":"eddf1b79","getVirtualUnderlyingBalance(address)":"6fb07f96","initReserve(address,address,address,address)":"8bd25677","liquidationCall(address,address,address,uint256,bool)":"00a718a9","mintToTreasury(address[])":"9cd19996","mintUnbacked(address,uint256,address,uint16)":"69a933a5","repay(address,uint256,uint256,address)":"573ade81","repayWithATokens(address,uint256,uint256)":"2dad97d4","repayWithPermit(address,uint256,uint256,address,uint256,uint8,bytes32,bytes32)":"ee3e210b","rescueTokens(address,address,uint256)":"cea9d26f","resetIsolationModeTotalDebt(address)":"e43e88a1","setConfiguration(address,(uint256))":"f51e435b","setLiquidationGracePeriod(address,uint40)":"b1a99e26","setReserveInterestRateStrategyAddress(address,address)":"1d2118f9","setUserEMode(uint8)":"28530a47","setUserUseReserveAsCollateral(address,bool)":"5a3b74b9","supply(address,uint256,address,uint16)":"617ba037","supplyWithPermit(address,uint256,address,uint16,uint256,uint8,bytes32,bytes32)":"02c205f0","syncIndexesState(address)":"ab2b51f6","syncRatesState(address)":"98c7da4e","updateBridgeProtocolFee(uint256)":"3036b439","updateFlashloanPremiums(uint128,uint128)":"bcb6e522","withdraw(address,uint256,address)":"69328dec"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.22+commit.4fc1097e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"reserve\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"backer\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"}],\"name\":\"BackUnbacked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"reserve\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"onBehalfOf\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"enum DataTypes.InterestRateMode\",\"name\":\"interestRateMode\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"borrowRate\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint16\",\"name\":\"referralCode\",\"type\":\"uint16\"}],\"name\":\"Borrow\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"reserve\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amountCovered\",\"type\":\"uint256\"}],\"name\":\"DeficitCovered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"debtAsset\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amountCreated\",\"type\":\"uint256\"}],\"name\":\"DeficitCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"initiator\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"enum DataTypes.InterestRateMode\",\"name\":\"interestRateMode\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"premium\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint16\",\"name\":\"referralCode\",\"type\":\"uint16\"}],\"name\":\"FlashLoan\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"totalDebt\",\"type\":\"uint256\"}],\"name\":\"IsolationModeTotalDebtUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"collateralAsset\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"debtAsset\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"debtToCover\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"liquidatedCollateralAmount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"liquidator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"receiveAToken\",\"type\":\"bool\"}],\"name\":\"LiquidationCall\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"reserve\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"onBehalfOf\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint16\",\"name\":\"referralCode\",\"type\":\"uint16\"}],\"name\":\"MintUnbacked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"reserve\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amountMinted\",\"type\":\"uint256\"}],\"name\":\"MintedToTreasury\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"reserve\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"repayer\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"useATokens\",\"type\":\"bool\"}],\"name\":\"Repay\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"reserve\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"liquidityRate\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"stableBorrowRate\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"variableBorrowRate\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"liquidityIndex\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"variableBorrowIndex\",\"type\":\"uint256\"}],\"name\":\"ReserveDataUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"reserve\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"}],\"name\":\"ReserveUsedAsCollateralDisabled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"reserve\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"}],\"name\":\"ReserveUsedAsCollateralEnabled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"reserve\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"onBehalfOf\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint16\",\"name\":\"referralCode\",\"type\":\"uint16\"}],\"name\":\"Supply\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"categoryId\",\"type\":\"uint8\"}],\"name\":\"UserEModeSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"reserve\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Withdraw\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"ADDRESSES_PROVIDER\",\"outputs\":[{\"internalType\":\"contract IPoolAddressesProvider\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"BRIDGE_PROTOCOL_FEE\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"FLASHLOAN_PREMIUM_TOTAL\",\"outputs\":[{\"internalType\":\"uint128\",\"name\":\"\",\"type\":\"uint128\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"FLASHLOAN_PREMIUM_TO_PROTOCOL\",\"outputs\":[{\"internalType\":\"uint128\",\"name\":\"\",\"type\":\"uint128\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_NUMBER_RESERVES\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"}],\"name\":\"backUnbacked\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"interestRateMode\",\"type\":\"uint256\"},{\"internalType\":\"uint16\",\"name\":\"referralCode\",\"type\":\"uint16\"},{\"internalType\":\"address\",\"name\":\"onBehalfOf\",\"type\":\"address\"}],\"name\":\"borrow\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"id\",\"type\":\"uint8\"},{\"components\":[{\"internalType\":\"uint16\",\"name\":\"ltv\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"liquidationThreshold\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"liquidationBonus\",\"type\":\"uint16\"},{\"internalType\":\"string\",\"name\":\"label\",\"type\":\"string\"}],\"internalType\":\"struct DataTypes.EModeCategoryBaseConfiguration\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"configureEModeCategory\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"id\",\"type\":\"uint8\"},{\"internalType\":\"uint128\",\"name\":\"borrowableBitmap\",\"type\":\"uint128\"}],\"name\":\"configureEModeCategoryBorrowableBitmap\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"id\",\"type\":\"uint8\"},{\"internalType\":\"uint128\",\"name\":\"collateralBitmap\",\"type\":\"uint128\"}],\"name\":\"configureEModeCategoryCollateralBitmap\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"onBehalfOf\",\"type\":\"address\"},{\"internalType\":\"uint16\",\"name\":\"referralCode\",\"type\":\"uint16\"}],\"name\":\"deposit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"}],\"name\":\"dropReserve\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"eliminateReserveDeficit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"balanceFromBefore\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"balanceToBefore\",\"type\":\"uint256\"}],\"name\":\"finalizeTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiverAddress\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"assets\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"amounts\",\"type\":\"uint256[]\"},{\"internalType\":\"uint256[]\",\"name\":\"interestRateModes\",\"type\":\"uint256[]\"},{\"internalType\":\"address\",\"name\":\"onBehalfOf\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"params\",\"type\":\"bytes\"},{\"internalType\":\"uint16\",\"name\":\"referralCode\",\"type\":\"uint16\"}],\"name\":\"flashLoan\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiverAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"params\",\"type\":\"bytes\"},{\"internalType\":\"uint16\",\"name\":\"referralCode\",\"type\":\"uint16\"}],\"name\":\"flashLoanSimple\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getBorrowLogic\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getBridgeLogic\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"}],\"name\":\"getConfiguration\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"data\",\"type\":\"uint256\"}],\"internalType\":\"struct DataTypes.ReserveConfigurationMap\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"id\",\"type\":\"uint8\"}],\"name\":\"getEModeCategoryBorrowableBitmap\",\"outputs\":[{\"internalType\":\"uint128\",\"name\":\"\",\"type\":\"uint128\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"id\",\"type\":\"uint8\"}],\"name\":\"getEModeCategoryCollateralBitmap\",\"outputs\":[{\"internalType\":\"uint128\",\"name\":\"\",\"type\":\"uint128\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"id\",\"type\":\"uint8\"}],\"name\":\"getEModeCategoryCollateralConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"uint16\",\"name\":\"ltv\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"liquidationThreshold\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"liquidationBonus\",\"type\":\"uint16\"}],\"internalType\":\"struct DataTypes.CollateralConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"id\",\"type\":\"uint8\"}],\"name\":\"getEModeCategoryData\",\"outputs\":[{\"components\":[{\"internalType\":\"uint16\",\"name\":\"ltv\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"liquidationThreshold\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"liquidationBonus\",\"type\":\"uint16\"},{\"internalType\":\"address\",\"name\":\"priceSource\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"label\",\"type\":\"string\"}],\"internalType\":\"struct DataTypes.EModeCategoryLegacy\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"id\",\"type\":\"uint8\"}],\"name\":\"getEModeCategoryLabel\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getEModeLogic\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getFlashLoanLogic\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"}],\"name\":\"getLiquidationGracePeriod\",\"outputs\":[{\"internalType\":\"uint40\",\"name\":\"\",\"type\":\"uint40\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLiquidationLogic\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getPoolLogic\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"}],\"name\":\"getReserveAToken\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"id\",\"type\":\"uint16\"}],\"name\":\"getReserveAddressById\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"}],\"name\":\"getReserveData\",\"outputs\":[{\"components\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"data\",\"type\":\"uint256\"}],\"internalType\":\"struct DataTypes.ReserveConfigurationMap\",\"name\":\"configuration\",\"type\":\"tuple\"},{\"internalType\":\"uint128\",\"name\":\"liquidityIndex\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"currentLiquidityRate\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"variableBorrowIndex\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"currentVariableBorrowRate\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"currentStableBorrowRate\",\"type\":\"uint128\"},{\"internalType\":\"uint40\",\"name\":\"lastUpdateTimestamp\",\"type\":\"uint40\"},{\"internalType\":\"uint16\",\"name\":\"id\",\"type\":\"uint16\"},{\"internalType\":\"address\",\"name\":\"aTokenAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"stableDebtTokenAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"variableDebtTokenAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"interestRateStrategyAddress\",\"type\":\"address\"},{\"internalType\":\"uint128\",\"name\":\"accruedToTreasury\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"unbacked\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"isolationModeTotalDebt\",\"type\":\"uint128\"}],\"internalType\":\"struct DataTypes.ReserveDataLegacy\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"}],\"name\":\"getReserveDeficit\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"}],\"name\":\"getReserveNormalizedIncome\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"}],\"name\":\"getReserveNormalizedVariableDebt\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"}],\"name\":\"getReserveVariableDebtToken\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getReservesCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getReservesList\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getSupplyLogic\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"}],\"name\":\"getUserAccountData\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"totalCollateralBase\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"totalDebtBase\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"availableBorrowsBase\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"currentLiquidationThreshold\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"ltv\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"healthFactor\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"}],\"name\":\"getUserConfiguration\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"data\",\"type\":\"uint256\"}],\"internalType\":\"struct DataTypes.UserConfigurationMap\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"}],\"name\":\"getUserEMode\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"}],\"name\":\"getVirtualUnderlyingBalance\",\"outputs\":[{\"internalType\":\"uint128\",\"name\":\"\",\"type\":\"uint128\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"aTokenAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"variableDebtAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"interestRateStrategyAddress\",\"type\":\"address\"}],\"name\":\"initReserve\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"collateralAsset\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"debtAsset\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"debtToCover\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"receiveAToken\",\"type\":\"bool\"}],\"name\":\"liquidationCall\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"assets\",\"type\":\"address[]\"}],\"name\":\"mintToTreasury\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"onBehalfOf\",\"type\":\"address\"},{\"internalType\":\"uint16\",\"name\":\"referralCode\",\"type\":\"uint16\"}],\"name\":\"mintUnbacked\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"interestRateMode\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"onBehalfOf\",\"type\":\"address\"}],\"name\":\"repay\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"interestRateMode\",\"type\":\"uint256\"}],\"name\":\"repayWithATokens\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"interestRateMode\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"onBehalfOf\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"deadline\",\"type\":\"uint256\"},{\"internalType\":\"uint8\",\"name\":\"permitV\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"permitR\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"permitS\",\"type\":\"bytes32\"}],\"name\":\"repayWithPermit\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"rescueTokens\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"}],\"name\":\"resetIsolationModeTotalDebt\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"data\",\"type\":\"uint256\"}],\"internalType\":\"struct DataTypes.ReserveConfigurationMap\",\"name\":\"configuration\",\"type\":\"tuple\"}],\"name\":\"setConfiguration\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"internalType\":\"uint40\",\"name\":\"until\",\"type\":\"uint40\"}],\"name\":\"setLiquidationGracePeriod\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"rateStrategyAddress\",\"type\":\"address\"}],\"name\":\"setReserveInterestRateStrategyAddress\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"categoryId\",\"type\":\"uint8\"}],\"name\":\"setUserEMode\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"useAsCollateral\",\"type\":\"bool\"}],\"name\":\"setUserUseReserveAsCollateral\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"onBehalfOf\",\"type\":\"address\"},{\"internalType\":\"uint16\",\"name\":\"referralCode\",\"type\":\"uint16\"}],\"name\":\"supply\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"onBehalfOf\",\"type\":\"address\"},{\"internalType\":\"uint16\",\"name\":\"referralCode\",\"type\":\"uint16\"},{\"internalType\":\"uint256\",\"name\":\"deadline\",\"type\":\"uint256\"},{\"internalType\":\"uint8\",\"name\":\"permitV\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"permitR\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"permitS\",\"type\":\"bytes32\"}],\"name\":\"supplyWithPermit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"}],\"name\":\"syncIndexesState\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"}],\"name\":\"syncRatesState\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"bridgeProtocolFee\",\"type\":\"uint256\"}],\"name\":\"updateBridgeProtocolFee\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint128\",\"name\":\"flashLoanPremiumTotal\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"flashLoanPremiumToProtocol\",\"type\":\"uint128\"}],\"name\":\"updateFlashloanPremiums\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"withdraw\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"author\":\"Aave\",\"events\":{\"BackUnbacked(address,address,uint256,uint256)\":{\"details\":\"Emitted on backUnbacked()\",\"params\":{\"amount\":\"The amount added as backing\",\"backer\":\"The address paying for the backing\",\"fee\":\"The amount paid in fees\",\"reserve\":\"The address of the underlying asset of the reserve\"}},\"Borrow(address,address,address,uint256,uint8,uint256,uint16)\":{\"details\":\"Emitted on borrow() and flashLoan() when debt needs to be opened\",\"params\":{\"amount\":\"The amount borrowed out\",\"borrowRate\":\"The numeric rate at which the user has borrowed, expressed in ray\",\"interestRateMode\":\"The rate mode: 2 for Variable, 1 is deprecated (changed on v3.2.0)\",\"onBehalfOf\":\"The address that will be getting the debt\",\"referralCode\":\"The referral code used\",\"reserve\":\"The address of the underlying asset being borrowed\",\"user\":\"The address of the user initiating the borrow(), receiving the funds on borrow() or just initiator of the transaction on flashLoan()\"}},\"DeficitCovered(address,address,uint256)\":{\"details\":\"Emitted when the deficit of a reserve is covered.\",\"params\":{\"amountCovered\":\"The amount of deficit covered\",\"caller\":\"The caller that triggered the DeficitCovered event\",\"reserve\":\"The address of the underlying asset of the reserve\"}},\"DeficitCreated(address,address,uint256)\":{\"details\":\"Emitted when deficit is realized on a liquidation.\",\"params\":{\"amountCreated\":\"The amount of deficit created\",\"debtAsset\":\"The address of the underlying borrowed asset to be burned\",\"user\":\"The user address where the bad debt will be burned\"}},\"FlashLoan(address,address,address,uint256,uint8,uint256,uint16)\":{\"details\":\"Emitted on flashLoan()\",\"params\":{\"amount\":\"The amount flash borrowed\",\"asset\":\"The address of the asset being flash borrowed\",\"initiator\":\"The address initiating the flash loan\",\"interestRateMode\":\"The flashloan mode: 0 for regular flashloan, 1 for Stable (Deprecated on v3.2.0), 2 for Variable\",\"premium\":\"The fee flash borrowed\",\"referralCode\":\"The referral code used\",\"target\":\"The address of the flash loan receiver contract\"}},\"IsolationModeTotalDebtUpdated(address,uint256)\":{\"details\":\"Emitted on borrow(), repay() and liquidationCall() when using isolated assets\",\"params\":{\"asset\":\"The address of the underlying asset of the reserve\",\"totalDebt\":\"The total isolation mode debt for the reserve\"}},\"LiquidationCall(address,address,address,uint256,uint256,address,bool)\":{\"details\":\"Emitted when a borrower is liquidated.\",\"params\":{\"collateralAsset\":\"The address of the underlying asset used as collateral, to receive as result of the liquidation\",\"debtAsset\":\"The address of the underlying borrowed asset to be repaid with the liquidation\",\"debtToCover\":\"The debt amount of borrowed `asset` the liquidator wants to cover\",\"liquidatedCollateralAmount\":\"The amount of collateral received by the liquidator\",\"liquidator\":\"The address of the liquidator\",\"receiveAToken\":\"True if the liquidators wants to receive the collateral aTokens, `false` if he wants to receive the underlying collateral asset directly\",\"user\":\"The address of the borrower getting liquidated\"}},\"MintUnbacked(address,address,address,uint256,uint16)\":{\"details\":\"Emitted on mintUnbacked()\",\"params\":{\"amount\":\"The amount of supplied assets\",\"onBehalfOf\":\"The beneficiary of the supplied assets, receiving the aTokens\",\"referralCode\":\"The referral code used\",\"reserve\":\"The address of the underlying asset of the reserve\",\"user\":\"The address initiating the supply\"}},\"MintedToTreasury(address,uint256)\":{\"details\":\"Emitted when the protocol treasury receives minted aTokens from the accrued interest.\",\"params\":{\"amountMinted\":\"The amount minted to the treasury\",\"reserve\":\"The address of the reserve\"}},\"Repay(address,address,address,uint256,bool)\":{\"details\":\"Emitted on repay()\",\"params\":{\"amount\":\"The amount repaid\",\"repayer\":\"The address of the user initiating the repay(), providing the funds\",\"reserve\":\"The address of the underlying asset of the reserve\",\"useATokens\":\"True if the repayment is done using aTokens, `false` if done with underlying asset directly\",\"user\":\"The beneficiary of the repayment, getting his debt reduced\"}},\"ReserveDataUpdated(address,uint256,uint256,uint256,uint256,uint256)\":{\"details\":\"Emitted when the state of a reserve is updated.\",\"params\":{\"liquidityIndex\":\"The next liquidity index\",\"liquidityRate\":\"The next liquidity rate\",\"reserve\":\"The address of the underlying asset of the reserve\",\"stableBorrowRate\":\"The next stable borrow rate @note deprecated on v3.2.0\",\"variableBorrowIndex\":\"The next variable borrow index\",\"variableBorrowRate\":\"The next variable borrow rate\"}},\"ReserveUsedAsCollateralDisabled(address,address)\":{\"details\":\"Emitted on setUserUseReserveAsCollateral()\",\"params\":{\"reserve\":\"The address of the underlying asset of the reserve\",\"user\":\"The address of the user enabling the usage as collateral\"}},\"ReserveUsedAsCollateralEnabled(address,address)\":{\"details\":\"Emitted on setUserUseReserveAsCollateral()\",\"params\":{\"reserve\":\"The address of the underlying asset of the reserve\",\"user\":\"The address of the user enabling the usage as collateral\"}},\"Supply(address,address,address,uint256,uint16)\":{\"details\":\"Emitted on supply()\",\"params\":{\"amount\":\"The amount supplied\",\"onBehalfOf\":\"The beneficiary of the supply, receiving the aTokens\",\"referralCode\":\"The referral code used\",\"reserve\":\"The address of the underlying asset of the reserve\",\"user\":\"The address initiating the supply\"}},\"UserEModeSet(address,uint8)\":{\"details\":\"Emitted when the user selects a certain asset category for eMode\",\"params\":{\"categoryId\":\"The category id\",\"user\":\"The address of the user\"}},\"Withdraw(address,address,address,uint256)\":{\"details\":\"Emitted on withdraw()\",\"params\":{\"amount\":\"The amount to be withdrawn\",\"reserve\":\"The address of the underlying asset being withdrawn\",\"to\":\"The address that will receive the underlying\",\"user\":\"The address initiating the withdrawal, owner of aTokens\"}}},\"kind\":\"dev\",\"methods\":{\"ADDRESSES_PROVIDER()\":{\"returns\":{\"_0\":\"The address of the PoolAddressesProvider\"}},\"BRIDGE_PROTOCOL_FEE()\":{\"returns\":{\"_0\":\"The bridge fee sent to the protocol treasury\"}},\"FLASHLOAN_PREMIUM_TOTAL()\":{\"returns\":{\"_0\":\"The total fee on flashloans\"}},\"FLASHLOAN_PREMIUM_TO_PROTOCOL()\":{\"returns\":{\"_0\":\"The flashloan fee sent to the protocol treasury\"}},\"MAX_NUMBER_RESERVES()\":{\"returns\":{\"_0\":\"The maximum number of reserves supported\"}},\"backUnbacked(address,uint256,uint256)\":{\"params\":{\"amount\":\"The amount to back\",\"asset\":\"The address of the underlying asset to back\",\"fee\":\"The amount paid in fees\"},\"returns\":{\"_0\":\"The backed amount\"}},\"borrow(address,uint256,uint256,uint16,address)\":{\"params\":{\"amount\":\"The amount to be borrowed\",\"asset\":\"The address of the underlying asset to borrow\",\"interestRateMode\":\"2 for Variable, 1 is deprecated on v3.2.0\",\"onBehalfOf\":\"The address of the user who will receive the debt. Should be the address of the borrower itself calling the function if he wants to borrow against his own collateral, or the address of the credit delegator if he has been given credit delegation allowance\",\"referralCode\":\"The code used to register the integrator originating the operation, for potential rewards. 0 if the action is executed directly by the user, without any middle-man\"}},\"configureEModeCategory(uint8,(uint16,uint16,uint16,string))\":{\"details\":\"In eMode, the protocol allows very high borrowing power to borrow assets of the same category. The category 0 is reserved as it's the default for volatile assets\",\"params\":{\"config\":\"The configuration of the category\",\"id\":\"The id of the category\"}},\"configureEModeCategoryBorrowableBitmap(uint8,uint128)\":{\"params\":{\"borrowableBitmap\":\"The borrowableBitmap of the category\",\"id\":\"The id of the category\"}},\"configureEModeCategoryCollateralBitmap(uint8,uint128)\":{\"params\":{\"collateralBitmap\":\"The collateralBitmap of the category\",\"id\":\"The id of the category\"}},\"deposit(address,uint256,address,uint16)\":{\"details\":\"Deprecated: Use the `supply` function instead\",\"params\":{\"amount\":\"The amount to be supplied\",\"asset\":\"The address of the underlying asset to supply\",\"onBehalfOf\":\"The address that will receive the aTokens, same as msg.sender if the user wants to receive them on his own wallet, or a different address if the beneficiary of aTokens is a different wallet\",\"referralCode\":\"Code used to register the integrator originating the operation, for potential rewards. 0 if the action is executed directly by the user, without any middle-man\"}},\"dropReserve(address)\":{\"details\":\"Only callable by the PoolConfigurator contractDoes not reset eMode flags, which must be considered when reusing the same reserve id for a different reserve.\",\"params\":{\"asset\":\"The address of the underlying asset of the reserve\"}},\"eliminateReserveDeficit(address,uint256)\":{\"details\":\"The deficit of a reserve can occur due to situations where borrowed assets are not repaid, leading to bad debt.\",\"params\":{\"amount\":\"The amount to be covered, in aToken or underlying on non-virtual accounted assets\",\"asset\":\"The address of the underlying asset to cover the deficit.\"}},\"finalizeTransfer(address,address,address,uint256,uint256,uint256)\":{\"details\":\"Only callable by the overlying aToken of the `asset`\",\"params\":{\"amount\":\"The amount being transferred/withdrawn\",\"asset\":\"The address of the underlying asset of the aToken\",\"balanceFromBefore\":\"The aToken balance of the `from` user before the transfer\",\"balanceToBefore\":\"The aToken balance of the `to` user before the transfer\",\"from\":\"The user from which the aTokens are transferred\",\"to\":\"The user receiving the aTokens\"}},\"flashLoan(address,address[],uint256[],uint256[],address,bytes,uint16)\":{\"details\":\"IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept into consideration. For further details please visit https://docs.aave.com/developers/\",\"params\":{\"amounts\":\"The amounts of the assets being flash-borrowed\",\"assets\":\"The addresses of the assets being flash-borrowed\",\"interestRateModes\":\"Types of the debt to open if the flash loan is not returned: 0 -> Don't open any debt, just revert if funds can't be transferred from the receiver 1 -> Deprecated on v3.2.0 2 -> Open debt at variable rate for the value of the amount flash-borrowed to the `onBehalfOf` address\",\"onBehalfOf\":\"The address that will receive the debt in the case of using 2 on `modes`\",\"params\":\"Variadic packed params to pass to the receiver as extra information\",\"receiverAddress\":\"The address of the contract receiving the funds, implementing IFlashLoanReceiver interface\",\"referralCode\":\"The code used to register the integrator originating the operation, for potential rewards. 0 if the action is executed directly by the user, without any middle-man\"}},\"flashLoanSimple(address,address,uint256,bytes,uint16)\":{\"details\":\"IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept into consideration. For further details please visit https://docs.aave.com/developers/\",\"params\":{\"amount\":\"The amount of the asset being flash-borrowed\",\"asset\":\"The address of the asset being flash-borrowed\",\"params\":\"Variadic packed params to pass to the receiver as extra information\",\"receiverAddress\":\"The address of the contract receiving the funds, implementing IFlashLoanSimpleReceiver interface\",\"referralCode\":\"The code used to register the integrator originating the operation, for potential rewards. 0 if the action is executed directly by the user, without any middle-man\"}},\"getConfiguration(address)\":{\"params\":{\"asset\":\"The address of the underlying asset of the reserve\"},\"returns\":{\"_0\":\"The configuration of the reserve\"}},\"getEModeCategoryBorrowableBitmap(uint8)\":{\"params\":{\"id\":\"The id of the category\"},\"returns\":{\"_0\":\"The borrowableBitmap of the category\"}},\"getEModeCategoryCollateralBitmap(uint8)\":{\"params\":{\"id\":\"The id of the category\"},\"returns\":{\"_0\":\"The collateralBitmap of the category\"}},\"getEModeCategoryCollateralConfig(uint8)\":{\"params\":{\"id\":\"The id of the category\"},\"returns\":{\"_0\":\"The ltv,lt,lb of the category\"}},\"getEModeCategoryData(uint8)\":{\"details\":\"DEPRECATED use independent getters instead\",\"params\":{\"id\":\"The id of the category\"},\"returns\":{\"_0\":\"The configuration data of the category\"}},\"getEModeCategoryLabel(uint8)\":{\"params\":{\"id\":\"The id of the category\"},\"returns\":{\"_0\":\"The label of the category\"}},\"getLiquidationGracePeriod(address)\":{\"params\":{\"asset\":\"The address of the underlying asset\"},\"returns\":{\"_0\":\"Timestamp when the liquidation grace period will end*\"}},\"getReserveAToken(address)\":{\"params\":{\"asset\":\"The address of the underlying asset of the reserve\"},\"returns\":{\"_0\":\"The address of the aToken\"}},\"getReserveAddressById(uint16)\":{\"params\":{\"id\":\"The id of the reserve as stored in the DataTypes.ReserveData struct\"},\"returns\":{\"_0\":\"The address of the reserve associated with id\"}},\"getReserveData(address)\":{\"params\":{\"asset\":\"The address of the underlying asset of the reserve\"},\"returns\":{\"_0\":\"The state and configuration data of the reserve\"}},\"getReserveDeficit(address)\":{\"params\":{\"asset\":\"The address of the underlying asset of the reserve\"},\"returns\":{\"_0\":\"The current deficit of the reserve\"}},\"getReserveNormalizedIncome(address)\":{\"params\":{\"asset\":\"The address of the underlying asset of the reserve\"},\"returns\":{\"_0\":\"The reserve's normalized income\"}},\"getReserveNormalizedVariableDebt(address)\":{\"details\":\"WARNING: This function is intended to be used primarily by the protocol itself to get a \\\"dynamic\\\" variable index based on time, current stored index and virtual rate at the current moment (approx. a borrower would get if opening a position). This means that is always used in combination with variable debt supply/balances. If using this function externally, consider that is possible to have an increasing normalized variable debt that is not equivalent to how the variable debt index would be updated in storage (e.g. only updates with non-zero variable debt supply)\",\"params\":{\"asset\":\"The address of the underlying asset of the reserve\"},\"returns\":{\"_0\":\"The reserve normalized variable debt\"}},\"getReserveVariableDebtToken(address)\":{\"params\":{\"asset\":\"The address of the underlying asset of the reserve\"},\"returns\":{\"_0\":\"The address of the variableDebtToken\"}},\"getReservesCount()\":{\"details\":\"It includes dropped reserves\",\"returns\":{\"_0\":\"The count\"}},\"getReservesList()\":{\"details\":\"It does not include dropped reserves\",\"returns\":{\"_0\":\"The addresses of the underlying assets of the initialized reserves\"}},\"getUserAccountData(address)\":{\"params\":{\"user\":\"The address of the user\"},\"returns\":{\"availableBorrowsBase\":\"The borrowing power left of the user in the base currency used by the price feed\",\"currentLiquidationThreshold\":\"The liquidation threshold of the user\",\"healthFactor\":\"The current health factor of the user\",\"ltv\":\"The loan to value of The user\",\"totalCollateralBase\":\"The total collateral of the user in the base currency used by the price feed\",\"totalDebtBase\":\"The total debt of the user in the base currency used by the price feed\"}},\"getUserConfiguration(address)\":{\"params\":{\"user\":\"The user address\"},\"returns\":{\"_0\":\"The configuration of the user\"}},\"getUserEMode(address)\":{\"params\":{\"user\":\"The address of the user\"},\"returns\":{\"_0\":\"The eMode id\"}},\"getVirtualUnderlyingBalance(address)\":{\"params\":{\"asset\":\"The address of the underlying asset of the reserve\"},\"returns\":{\"_0\":\"The reserve virtual underlying balance\"}},\"initReserve(address,address,address,address)\":{\"details\":\"Only callable by the PoolConfigurator contract\",\"params\":{\"aTokenAddress\":\"The address of the aToken that will be assigned to the reserve\",\"asset\":\"The address of the underlying asset of the reserve\",\"interestRateStrategyAddress\":\"The address of the interest rate strategy contract\",\"variableDebtAddress\":\"The address of the VariableDebtToken that will be assigned to the reserve\"}},\"liquidationCall(address,address,address,uint256,bool)\":{\"params\":{\"collateralAsset\":\"The address of the underlying asset used as collateral, to receive as result of the liquidation\",\"debtAsset\":\"The address of the underlying borrowed asset to be repaid with the liquidation\",\"debtToCover\":\"The debt amount of borrowed `asset` the liquidator wants to cover\",\"receiveAToken\":\"True if the liquidators wants to receive the collateral aTokens, `false` if he wants to receive the underlying collateral asset directly\",\"user\":\"The address of the borrower getting liquidated\"}},\"mintToTreasury(address[])\":{\"params\":{\"assets\":\"The list of reserves for which the minting needs to be executed\"}},\"mintUnbacked(address,uint256,address,uint16)\":{\"params\":{\"amount\":\"The amount to mint\",\"asset\":\"The address of the underlying asset to mint\",\"onBehalfOf\":\"The address that will receive the aTokens\",\"referralCode\":\"Code used to register the integrator originating the operation, for potential rewards. 0 if the action is executed directly by the user, without any middle-man\"}},\"repay(address,uint256,uint256,address)\":{\"params\":{\"amount\":\"The amount to repay - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode`\",\"asset\":\"The address of the borrowed underlying asset previously borrowed\",\"interestRateMode\":\"2 for Variable, 1 is deprecated on v3.2.0\",\"onBehalfOf\":\"The address of the user who will get his debt reduced/removed. Should be the address of the user calling the function if he wants to reduce/remove his own debt, or the address of any other other borrower whose debt should be removed\"},\"returns\":{\"_0\":\"The final amount repaid\"}},\"repayWithATokens(address,uint256,uint256)\":{\"details\":\"Passing uint256.max as amount will clean up any residual aToken dust balance, if the user aToken balance is not enough to cover the whole debt\",\"params\":{\"amount\":\"The amount to repay - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode`\",\"asset\":\"The address of the borrowed underlying asset previously borrowed\",\"interestRateMode\":\"DEPRECATED in v3.2.0\"},\"returns\":{\"_0\":\"The final amount repaid\"}},\"repayWithPermit(address,uint256,uint256,address,uint256,uint8,bytes32,bytes32)\":{\"params\":{\"amount\":\"The amount to repay - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode`\",\"asset\":\"The address of the borrowed underlying asset previously borrowed\",\"deadline\":\"The deadline timestamp that the permit is valid\",\"interestRateMode\":\"2 for Variable, 1 is deprecated on v3.2.0\",\"onBehalfOf\":\"Address of the user who will get his debt reduced/removed. Should be the address of the user calling the function if he wants to reduce/remove his own debt, or the address of any other other borrower whose debt should be removed\",\"permitR\":\"The R parameter of ERC712 permit sig\",\"permitS\":\"The S parameter of ERC712 permit sig\",\"permitV\":\"The V parameter of ERC712 permit sig\"},\"returns\":{\"_0\":\"The final amount repaid\"}},\"rescueTokens(address,address,uint256)\":{\"params\":{\"amount\":\"The amount of token to transfer\",\"to\":\"The address of the recipient\",\"token\":\"The address of the token\"}},\"resetIsolationModeTotalDebt(address)\":{\"details\":\"It requires the given asset has zero debt ceiling\",\"params\":{\"asset\":\"The address of the underlying asset to reset the isolationModeTotalDebt\"}},\"setConfiguration(address,(uint256))\":{\"details\":\"Only callable by the PoolConfigurator contract\",\"params\":{\"asset\":\"The address of the underlying asset of the reserve\",\"configuration\":\"The new configuration bitmap\"}},\"setLiquidationGracePeriod(address,uint40)\":{\"details\":\"To enable a liquidation grace period, a timestamp in the future should be set, To disable a liquidation grace period, any timestamp in the past works, like 0\",\"params\":{\"asset\":\"The address of the underlying asset to set the liquidationGracePeriod\",\"until\":\"Timestamp when the liquidation grace period will end*\"}},\"setReserveInterestRateStrategyAddress(address,address)\":{\"details\":\"Only callable by the PoolConfigurator contract\",\"params\":{\"asset\":\"The address of the underlying asset of the reserve\",\"rateStrategyAddress\":\"The address of the interest rate strategy contract\"}},\"setUserEMode(uint8)\":{\"params\":{\"categoryId\":\"The id of the category\"}},\"setUserUseReserveAsCollateral(address,bool)\":{\"params\":{\"asset\":\"The address of the underlying asset supplied\",\"useAsCollateral\":\"True if the user wants to use the supply as collateral, false otherwise\"}},\"supply(address,uint256,address,uint16)\":{\"params\":{\"amount\":\"The amount to be supplied\",\"asset\":\"The address of the underlying asset to supply\",\"onBehalfOf\":\"The address that will receive the aTokens, same as msg.sender if the user wants to receive them on his own wallet, or a different address if the beneficiary of aTokens is a different wallet\",\"referralCode\":\"Code used to register the integrator originating the operation, for potential rewards. 0 if the action is executed directly by the user, without any middle-man\"}},\"supplyWithPermit(address,uint256,address,uint16,uint256,uint8,bytes32,bytes32)\":{\"params\":{\"amount\":\"The amount to be supplied\",\"asset\":\"The address of the underlying asset to supply\",\"deadline\":\"The deadline timestamp that the permit is valid\",\"onBehalfOf\":\"The address that will receive the aTokens, same as msg.sender if the user wants to receive them on his own wallet, or a different address if the beneficiary of aTokens is a different wallet\",\"permitR\":\"The R parameter of ERC712 permit sig\",\"permitS\":\"The S parameter of ERC712 permit sig\",\"permitV\":\"The V parameter of ERC712 permit sig\",\"referralCode\":\"Code used to register the integrator originating the operation, for potential rewards. 0 if the action is executed directly by the user, without any middle-man\"}},\"syncIndexesState(address)\":{\"details\":\"Only callable by the PoolConfigurator contractTo be used when required by the configurator, for example when updating interest rates strategy data\",\"params\":{\"asset\":\"The address of the underlying asset of the reserve\"}},\"syncRatesState(address)\":{\"details\":\"Only callable by the PoolConfigurator contractTo be used when required by the configurator, for example when updating interest rates strategy data\",\"params\":{\"asset\":\"The address of the underlying asset of the reserve\"}},\"updateBridgeProtocolFee(uint256)\":{\"params\":{\"bridgeProtocolFee\":\"The part of the premium sent to the protocol treasury\"}},\"updateFlashloanPremiums(uint128,uint128)\":{\"details\":\"The total premium is calculated on the total borrowed amountThe premium to protocol is calculated on the total premium, being a percentage of `flashLoanPremiumTotal`Only callable by the PoolConfigurator contract\",\"params\":{\"flashLoanPremiumToProtocol\":\"The part of the premium sent to the protocol treasury, expressed in bps\",\"flashLoanPremiumTotal\":\"The total premium, expressed in bps\"}},\"withdraw(address,uint256,address)\":{\"params\":{\"amount\":\"The underlying amount to be withdrawn - Send the value type(uint256).max in order to withdraw the whole aToken balance\",\"asset\":\"The address of the underlying asset to withdraw\",\"to\":\"The address that will receive the underlying, same as msg.sender if the user wants to receive it on his own wallet, or a different address if the beneficiary is a different wallet\"},\"returns\":{\"_0\":\"The final amount withdrawn\"}}},\"title\":\"IPool\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"ADDRESSES_PROVIDER()\":{\"notice\":\"Returns the PoolAddressesProvider connected to this contract\"},\"BRIDGE_PROTOCOL_FEE()\":{\"notice\":\"Returns the part of the bridge fees sent to protocol\"},\"FLASHLOAN_PREMIUM_TOTAL()\":{\"notice\":\"Returns the total fee on flash loans\"},\"FLASHLOAN_PREMIUM_TO_PROTOCOL()\":{\"notice\":\"Returns the part of the flashloan fees sent to protocol\"},\"MAX_NUMBER_RESERVES()\":{\"notice\":\"Returns the maximum number of reserves supported to be listed in this Pool\"},\"backUnbacked(address,uint256,uint256)\":{\"notice\":\"Back the current unbacked underlying with `amount` and pay `fee`.\"},\"borrow(address,uint256,uint256,uint16,address)\":{\"notice\":\"Allows users to borrow a specific `amount` of the reserve underlying asset, provided that the borrower already supplied enough collateral, or he was given enough allowance by a credit delegator on the VariableDebtToken - E.g. User borrows 100 USDC passing as `onBehalfOf` his own address, receiving the 100 USDC in his wallet and 100 variable debt tokens\"},\"configureEModeCategory(uint8,(uint16,uint16,uint16,string))\":{\"notice\":\"Configures a new or alters an existing collateral configuration of an eMode.\"},\"configureEModeCategoryBorrowableBitmap(uint8,uint128)\":{\"notice\":\"Replaces the current eMode borrowableBitmap.\"},\"configureEModeCategoryCollateralBitmap(uint8,uint128)\":{\"notice\":\"Replaces the current eMode collateralBitmap.\"},\"deposit(address,uint256,address,uint16)\":{\"notice\":\"Supplies an `amount` of underlying asset into the reserve, receiving in return overlying aTokens. - E.g. User supplies 100 USDC and gets in return 100 aUSDC\"},\"dropReserve(address)\":{\"notice\":\"Drop a reserve\"},\"eliminateReserveDeficit(address,uint256)\":{\"notice\":\"It covers the deficit of a specified reserve by burning: - the equivalent aToken `amount` for assets with virtual accounting enabled - the equivalent `amount` of underlying for assets with virtual accounting disabled (e.g. GHO)\"},\"finalizeTransfer(address,address,address,uint256,uint256,uint256)\":{\"notice\":\"Validates and finalizes an aToken transfer\"},\"flashLoan(address,address[],uint256[],uint256[],address,bytes,uint16)\":{\"notice\":\"Allows smartcontracts to access the liquidity of the pool within one transaction, as long as the amount taken plus a fee is returned.\"},\"flashLoanSimple(address,address,uint256,bytes,uint16)\":{\"notice\":\"Allows smartcontracts to access the liquidity of the pool within one transaction, as long as the amount taken plus a fee is returned.\"},\"getBorrowLogic()\":{\"notice\":\"Gets the address of the external BorrowLogic\"},\"getBridgeLogic()\":{\"notice\":\"Gets the address of the external BridgeLogic\"},\"getConfiguration(address)\":{\"notice\":\"Returns the configuration of the reserve\"},\"getEModeCategoryBorrowableBitmap(uint8)\":{\"notice\":\"Returns the borrowableBitmap of an eMode category\"},\"getEModeCategoryCollateralBitmap(uint8)\":{\"notice\":\"Returns the collateralBitmap of an eMode category\"},\"getEModeCategoryCollateralConfig(uint8)\":{\"notice\":\"Returns the collateral config of an eMode category\"},\"getEModeCategoryData(uint8)\":{\"notice\":\"Returns the data of an eMode category\"},\"getEModeCategoryLabel(uint8)\":{\"notice\":\"Returns the label of an eMode category\"},\"getEModeLogic()\":{\"notice\":\"Gets the address of the external EModeLogic\"},\"getFlashLoanLogic()\":{\"notice\":\"Gets the address of the external FlashLoanLogic\"},\"getLiquidationGracePeriod(address)\":{\"notice\":\"Returns the liquidation grace period of the given asset\"},\"getLiquidationLogic()\":{\"notice\":\"Gets the address of the external LiquidationLogic\"},\"getPoolLogic()\":{\"notice\":\"Gets the address of the external PoolLogic\"},\"getReserveAToken(address)\":{\"notice\":\"Returns the aToken address of a reserve.\"},\"getReserveAddressById(uint16)\":{\"notice\":\"Returns the address of the underlying asset of a reserve by the reserve id as stored in the DataTypes.ReserveData struct\"},\"getReserveData(address)\":{\"notice\":\"Returns the state and configuration of the reserve\"},\"getReserveDeficit(address)\":{\"notice\":\"Returns the current deficit of a reserve.\"},\"getReserveNormalizedIncome(address)\":{\"notice\":\"Returns the normalized income of the reserve\"},\"getReserveNormalizedVariableDebt(address)\":{\"notice\":\"Returns the normalized variable debt per unit of asset\"},\"getReserveVariableDebtToken(address)\":{\"notice\":\"Returns the variableDebtToken address of a reserve.\"},\"getReservesCount()\":{\"notice\":\"Returns the number of initialized reserves\"},\"getReservesList()\":{\"notice\":\"Returns the list of the underlying assets of all the initialized reserves\"},\"getSupplyLogic()\":{\"notice\":\"Gets the address of the external SupplyLogic\"},\"getUserAccountData(address)\":{\"notice\":\"Returns the user account data across all the reserves\"},\"getUserConfiguration(address)\":{\"notice\":\"Returns the configuration of the user across all the reserves\"},\"getUserEMode(address)\":{\"notice\":\"Returns the eMode the user is using\"},\"getVirtualUnderlyingBalance(address)\":{\"notice\":\"Returns the virtual underlying balance of the reserve\"},\"initReserve(address,address,address,address)\":{\"notice\":\"Initializes a reserve, activating it, assigning an aToken and debt tokens and an interest rate strategy\"},\"liquidationCall(address,address,address,uint256,bool)\":{\"notice\":\"Function to liquidate a non-healthy position collateral-wise, with Health Factor below 1 - The caller (liquidator) covers `debtToCover` amount of debt of the user getting liquidated, and receives a proportionally amount of the `collateralAsset` plus a bonus to cover market risk\"},\"mintToTreasury(address[])\":{\"notice\":\"Mints the assets accrued through the reserve factor to the treasury in the form of aTokens\"},\"mintUnbacked(address,uint256,address,uint16)\":{\"notice\":\"Mints an `amount` of aTokens to the `onBehalfOf`\"},\"repay(address,uint256,uint256,address)\":{\"notice\":\"Repays a borrowed `amount` on a specific reserve, burning the equivalent debt tokens owned - E.g. User repays 100 USDC, burning 100 variable debt tokens of the `onBehalfOf` address\"},\"repayWithATokens(address,uint256,uint256)\":{\"notice\":\"Repays a borrowed `amount` on a specific reserve using the reserve aTokens, burning the equivalent debt tokens - E.g. User repays 100 USDC using 100 aUSDC, burning 100 variable debt tokens\"},\"repayWithPermit(address,uint256,uint256,address,uint256,uint8,bytes32,bytes32)\":{\"notice\":\"Repay with transfer approval of asset to be repaid done via permit function see: https://eips.ethereum.org/EIPS/eip-2612 and https://eips.ethereum.org/EIPS/eip-713\"},\"rescueTokens(address,address,uint256)\":{\"notice\":\"Rescue and transfer tokens locked in this contract\"},\"resetIsolationModeTotalDebt(address)\":{\"notice\":\"Resets the isolation mode total debt of the given asset to zero\"},\"setConfiguration(address,(uint256))\":{\"notice\":\"Sets the configuration bitmap of the reserve as a whole\"},\"setLiquidationGracePeriod(address,uint40)\":{\"notice\":\"Sets the liquidation grace period of the given asset\"},\"setReserveInterestRateStrategyAddress(address,address)\":{\"notice\":\"Updates the address of the interest rate strategy contract\"},\"setUserEMode(uint8)\":{\"notice\":\"Allows a user to use the protocol in eMode\"},\"setUserUseReserveAsCollateral(address,bool)\":{\"notice\":\"Allows suppliers to enable/disable a specific supplied asset as collateral\"},\"supply(address,uint256,address,uint16)\":{\"notice\":\"Supplies an `amount` of underlying asset into the reserve, receiving in return overlying aTokens. - E.g. User supplies 100 USDC and gets in return 100 aUSDC\"},\"supplyWithPermit(address,uint256,address,uint16,uint256,uint8,bytes32,bytes32)\":{\"notice\":\"Supply with transfer approval of asset to be supplied done via permit function see: https://eips.ethereum.org/EIPS/eip-2612 and https://eips.ethereum.org/EIPS/eip-713\"},\"syncIndexesState(address)\":{\"notice\":\"Accumulates interest to all indexes of the reserve\"},\"syncRatesState(address)\":{\"notice\":\"Updates interest rates on the reserve data\"},\"updateBridgeProtocolFee(uint256)\":{\"notice\":\"Updates the protocol fee on the bridging\"},\"updateFlashloanPremiums(uint128,uint128)\":{\"notice\":\"Updates flash loan premiums. Flash loan premium consists of two parts: - A part is sent to aToken holders as extra, one time accumulated interest - A part is collected by the protocol treasury\"},\"withdraw(address,uint256,address)\":{\"notice\":\"Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC\"}},\"notice\":\"Defines the basic interface for an Aave Pool.\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/contracts/interfaces/IPool.sol\":\"IPool\"},\"evmVersion\":\"shanghai\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"none\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[\":@openzeppelin/contracts-upgradeable/=lib/solidity-utils/lib/openzeppelin-contracts-upgradeable/contracts/\",\":@openzeppelin/contracts/=lib/solidity-utils/lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/\",\":ds-test/=lib/forge-std/lib/ds-test/src/\",\":erc4626-tests/=lib/solidity-utils/lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/\",\":forge-std/=lib/forge-std/src/\",\":halmos-cheatcodes/=lib/solidity-utils/lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/\",\":openzeppelin-contracts-upgradeable/=lib/solidity-utils/lib/openzeppelin-contracts-upgradeable/\",\":openzeppelin-contracts/=lib/solidity-utils/lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/\",\":solidity-utils/=lib/solidity-utils/src/\"]},\"sources\":{\"src/contracts/interfaces/IPool.sol\":{\"keccak256\":\"0xc22ca9af1e717264fb146ec5cc05e3284c5744db77f8dc7b31ab9d8b34812ae5\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://386c0e01a9223b35ba49dc5c2c7cdd61eb55632f9262630a251b8b103c20140c\",\"dweb:/ipfs/QmWfmWxzC26w67BjXnyqu9vNXcE5gWsL8VkWD88wGnExdc\"]},\"src/contracts/interfaces/IPoolAddressesProvider.sol\":{\"keccak256\":\"0xa60921cf54e91ca8db038effeffc876089b2e72dbf01d68a10ff461770d345e5\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://0de2fd03bf5cd6ae8718a486e5d615c67b260d0608b9fb7076888a4bfaa0e4b4\",\"dweb:/ipfs/QmXpdmxMugyJcBFAGJEruB78bkkuTwrH9esYRaS6wiQoMs\"]},\"src/contracts/protocol/libraries/types/DataTypes.sol\":{\"keccak256\":\"0xddac0f87cd5196a7df4bc75480ddb1823a8905f483473c8ece1f84651bf7070b\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://d2303b8550e016f4adc0658c06356222ba6ee8b5603e2fc2910bfe4ed14b3a71\",\"dweb:/ipfs/QmdoiTc5R6ZPsaehszSBuHv4f8A6DTosAMoMD157jegWra\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.22+commit.4fc1097e"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"address","name":"reserve","type":"address","indexed":true},{"internalType":"address","name":"backer","type":"address","indexed":true},{"internalType":"uint256","name":"amount","type":"uint256","indexed":false},{"internalType":"uint256","name":"fee","type":"uint256","indexed":false}],"type":"event","name":"BackUnbacked","anonymous":false},{"inputs":[{"internalType":"address","name":"reserve","type":"address","indexed":true},{"internalType":"address","name":"user","type":"address","indexed":false},{"internalType":"address","name":"onBehalfOf","type":"address","indexed":true},{"internalType":"uint256","name":"amount","type":"uint256","indexed":false},{"internalType":"enum DataTypes.InterestRateMode","name":"interestRateMode","type":"uint8","indexed":false},{"internalType":"uint256","name":"borrowRate","type":"uint256","indexed":false},{"internalType":"uint16","name":"referralCode","type":"uint16","indexed":true}],"type":"event","name":"Borrow","anonymous":false},{"inputs":[{"internalType":"address","name":"reserve","type":"address","indexed":true},{"internalType":"address","name":"caller","type":"address","indexed":false},{"internalType":"uint256","name":"amountCovered","type":"uint256","indexed":false}],"type":"event","name":"DeficitCovered","anonymous":false},{"inputs":[{"internalType":"address","name":"user","type":"address","indexed":true},{"internalType":"address","name":"debtAsset","type":"address","indexed":true},{"internalType":"uint256","name":"amountCreated","type":"uint256","indexed":false}],"type":"event","name":"DeficitCreated","anonymous":false},{"inputs":[{"internalType":"address","name":"target","type":"address","indexed":true},{"internalType":"address","name":"initiator","type":"address","indexed":false},{"internalType":"address","name":"asset","type":"address","indexed":true},{"internalType":"uint256","name":"amount","type":"uint256","indexed":false},{"internalType":"enum DataTypes.InterestRateMode","name":"interestRateMode","type":"uint8","indexed":false},{"internalType":"uint256","name":"premium","type":"uint256","indexed":false},{"internalType":"uint16","name":"referralCode","type":"uint16","indexed":true}],"type":"event","name":"FlashLoan","anonymous":false},{"inputs":[{"internalType":"address","name":"asset","type":"address","indexed":true},{"internalType":"uint256","name":"totalDebt","type":"uint256","indexed":false}],"type":"event","name":"IsolationModeTotalDebtUpdated","anonymous":false},{"inputs":[{"internalType":"address","name":"collateralAsset","type":"address","indexed":true},{"internalType":"address","name":"debtAsset","type":"address","indexed":true},{"internalType":"address","name":"user","type":"address","indexed":true},{"internalType":"uint256","name":"debtToCover","type":"uint256","indexed":false},{"internalType":"uint256","name":"liquidatedCollateralAmount","type":"uint256","indexed":false},{"internalType":"address","name":"liquidator","type":"address","indexed":false},{"internalType":"bool","name":"receiveAToken","type":"bool","indexed":false}],"type":"event","name":"LiquidationCall","anonymous":false},{"inputs":[{"internalType":"address","name":"reserve","type":"address","indexed":true},{"internalType":"address","name":"user","type":"address","indexed":false},{"internalType":"address","name":"onBehalfOf","type":"address","indexed":true},{"internalType":"uint256","name":"amount","type":"uint256","indexed":false},{"internalType":"uint16","name":"referralCode","type":"uint16","indexed":true}],"type":"event","name":"MintUnbacked","anonymous":false},{"inputs":[{"internalType":"address","name":"reserve","type":"address","indexed":true},{"internalType":"uint256","name":"amountMinted","type":"uint256","indexed":false}],"type":"event","name":"MintedToTreasury","anonymous":false},{"inputs":[{"internalType":"address","name":"reserve","type":"address","indexed":true},{"internalType":"address","name":"user","type":"address","indexed":true},{"internalType":"address","name":"repayer","type":"address","indexed":true},{"internalType":"uint256","name":"amount","type":"uint256","indexed":false},{"internalType":"bool","name":"useATokens","type":"bool","indexed":false}],"type":"event","name":"Repay","anonymous":false},{"inputs":[{"internalType":"address","name":"reserve","type":"address","indexed":true},{"internalType":"uint256","name":"liquidityRate","type":"uint256","indexed":false},{"internalType":"uint256","name":"stableBorrowRate","type":"uint256","indexed":false},{"internalType":"uint256","name":"variableBorrowRate","type":"uint256","indexed":false},{"internalType":"uint256","name":"liquidityIndex","type":"uint256","indexed":false},{"internalType":"uint256","name":"variableBorrowIndex","type":"uint256","indexed":false}],"type":"event","name":"ReserveDataUpdated","anonymous":false},{"inputs":[{"internalType":"address","name":"reserve","type":"address","indexed":true},{"internalType":"address","name":"user","type":"address","indexed":true}],"type":"event","name":"ReserveUsedAsCollateralDisabled","anonymous":false},{"inputs":[{"internalType":"address","name":"reserve","type":"address","indexed":true},{"internalType":"address","name":"user","type":"address","indexed":true}],"type":"event","name":"ReserveUsedAsCollateralEnabled","anonymous":false},{"inputs":[{"internalType":"address","name":"reserve","type":"address","indexed":true},{"internalType":"address","name":"user","type":"address","indexed":false},{"internalType":"address","name":"onBehalfOf","type":"address","indexed":true},{"internalType":"uint256","name":"amount","type":"uint256","indexed":false},{"internalType":"uint16","name":"referralCode","type":"uint16","indexed":true}],"type":"event","name":"Supply","anonymous":false},{"inputs":[{"internalType":"address","name":"user","type":"address","indexed":true},{"internalType":"uint8","name":"categoryId","type":"uint8","indexed":false}],"type":"event","name":"UserEModeSet","anonymous":false},{"inputs":[{"internalType":"address","name":"reserve","type":"address","indexed":true},{"internalType":"address","name":"user","type":"address","indexed":true},{"internalType":"address","name":"to","type":"address","indexed":true},{"internalType":"uint256","name":"amount","type":"uint256","indexed":false}],"type":"event","name":"Withdraw","anonymous":false},{"inputs":[],"stateMutability":"view","type":"function","name":"ADDRESSES_PROVIDER","outputs":[{"internalType":"contract IPoolAddressesProvider","name":"","type":"address"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"BRIDGE_PROTOCOL_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"FLASHLOAN_PREMIUM_TOTAL","outputs":[{"internalType":"uint128","name":"","type":"uint128"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"FLASHLOAN_PREMIUM_TO_PROTOCOL","outputs":[{"internalType":"uint128","name":"","type":"uint128"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"MAX_NUMBER_RESERVES","outputs":[{"internalType":"uint16","name":"","type":"uint16"}]},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"fee","type":"uint256"}],"stateMutability":"nonpayable","type":"function","name":"backUnbacked","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"interestRateMode","type":"uint256"},{"internalType":"uint16","name":"referralCode","type":"uint16"},{"internalType":"address","name":"onBehalfOf","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"borrow"},{"inputs":[{"internalType":"uint8","name":"id","type":"uint8"},{"internalType":"struct DataTypes.EModeCategoryBaseConfiguration","name":"config","type":"tuple","components":[{"internalType":"uint16","name":"ltv","type":"uint16"},{"internalType":"uint16","name":"liquidationThreshold","type":"uint16"},{"internalType":"uint16","name":"liquidationBonus","type":"uint16"},{"internalType":"string","name":"label","type":"string"}]}],"stateMutability":"nonpayable","type":"function","name":"configureEModeCategory"},{"inputs":[{"internalType":"uint8","name":"id","type":"uint8"},{"internalType":"uint128","name":"borrowableBitmap","type":"uint128"}],"stateMutability":"nonpayable","type":"function","name":"configureEModeCategoryBorrowableBitmap"},{"inputs":[{"internalType":"uint8","name":"id","type":"uint8"},{"internalType":"uint128","name":"collateralBitmap","type":"uint128"}],"stateMutability":"nonpayable","type":"function","name":"configureEModeCategoryCollateralBitmap"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"onBehalfOf","type":"address"},{"internalType":"uint16","name":"referralCode","type":"uint16"}],"stateMutability":"nonpayable","type":"function","name":"deposit"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"dropReserve"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"nonpayable","type":"function","name":"eliminateReserveDeficit"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"balanceFromBefore","type":"uint256"},{"internalType":"uint256","name":"balanceToBefore","type":"uint256"}],"stateMutability":"nonpayable","type":"function","name":"finalizeTransfer"},{"inputs":[{"internalType":"address","name":"receiverAddress","type":"address"},{"internalType":"address[]","name":"assets","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"uint256[]","name":"interestRateModes","type":"uint256[]"},{"internalType":"address","name":"onBehalfOf","type":"address"},{"internalType":"bytes","name":"params","type":"bytes"},{"internalType":"uint16","name":"referralCode","type":"uint16"}],"stateMutability":"nonpayable","type":"function","name":"flashLoan"},{"inputs":[{"internalType":"address","name":"receiverAddress","type":"address"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"params","type":"bytes"},{"internalType":"uint16","name":"referralCode","type":"uint16"}],"stateMutability":"nonpayable","type":"function","name":"flashLoanSimple"},{"inputs":[],"stateMutability":"view","type":"function","name":"getBorrowLogic","outputs":[{"internalType":"address","name":"","type":"address"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"getBridgeLogic","outputs":[{"internalType":"address","name":"","type":"address"}]},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"stateMutability":"view","type":"function","name":"getConfiguration","outputs":[{"internalType":"struct DataTypes.ReserveConfigurationMap","name":"","type":"tuple","components":[{"internalType":"uint256","name":"data","type":"uint256"}]}]},{"inputs":[{"internalType":"uint8","name":"id","type":"uint8"}],"stateMutability":"view","type":"function","name":"getEModeCategoryBorrowableBitmap","outputs":[{"internalType":"uint128","name":"","type":"uint128"}]},{"inputs":[{"internalType":"uint8","name":"id","type":"uint8"}],"stateMutability":"view","type":"function","name":"getEModeCategoryCollateralBitmap","outputs":[{"internalType":"uint128","name":"","type":"uint128"}]},{"inputs":[{"internalType":"uint8","name":"id","type":"uint8"}],"stateMutability":"view","type":"function","name":"getEModeCategoryCollateralConfig","outputs":[{"internalType":"struct DataTypes.CollateralConfig","name":"","type":"tuple","components":[{"internalType":"uint16","name":"ltv","type":"uint16"},{"internalType":"uint16","name":"liquidationThreshold","type":"uint16"},{"internalType":"uint16","name":"liquidationBonus","type":"uint16"}]}]},{"inputs":[{"internalType":"uint8","name":"id","type":"uint8"}],"stateMutability":"view","type":"function","name":"getEModeCategoryData","outputs":[{"internalType":"struct DataTypes.EModeCategoryLegacy","name":"","type":"tuple","components":[{"internalType":"uint16","name":"ltv","type":"uint16"},{"internalType":"uint16","name":"liquidationThreshold","type":"uint16"},{"internalType":"uint16","name":"liquidationBonus","type":"uint16"},{"internalType":"address","name":"priceSource","type":"address"},{"internalType":"string","name":"label","type":"string"}]}]},{"inputs":[{"internalType":"uint8","name":"id","type":"uint8"}],"stateMutability":"view","type":"function","name":"getEModeCategoryLabel","outputs":[{"internalType":"string","name":"","type":"string"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"getEModeLogic","outputs":[{"internalType":"address","name":"","type":"address"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"getFlashLoanLogic","outputs":[{"internalType":"address","name":"","type":"address"}]},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"stateMutability":"view","type":"function","name":"getLiquidationGracePeriod","outputs":[{"internalType":"uint40","name":"","type":"uint40"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"getLiquidationLogic","outputs":[{"internalType":"address","name":"","type":"address"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"getPoolLogic","outputs":[{"internalType":"address","name":"","type":"address"}]},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"stateMutability":"view","type":"function","name":"getReserveAToken","outputs":[{"internalType":"address","name":"","type":"address"}]},{"inputs":[{"internalType":"uint16","name":"id","type":"uint16"}],"stateMutability":"view","type":"function","name":"getReserveAddressById","outputs":[{"internalType":"address","name":"","type":"address"}]},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"stateMutability":"view","type":"function","name":"getReserveData","outputs":[{"internalType":"struct DataTypes.ReserveDataLegacy","name":"","type":"tuple","components":[{"internalType":"struct DataTypes.ReserveConfigurationMap","name":"configuration","type":"tuple","components":[{"internalType":"uint256","name":"data","type":"uint256"}]},{"internalType":"uint128","name":"liquidityIndex","type":"uint128"},{"internalType":"uint128","name":"currentLiquidityRate","type":"uint128"},{"internalType":"uint128","name":"variableBorrowIndex","type":"uint128"},{"internalType":"uint128","name":"currentVariableBorrowRate","type":"uint128"},{"internalType":"uint128","name":"currentStableBorrowRate","type":"uint128"},{"internalType":"uint40","name":"lastUpdateTimestamp","type":"uint40"},{"internalType":"uint16","name":"id","type":"uint16"},{"internalType":"address","name":"aTokenAddress","type":"address"},{"internalType":"address","name":"stableDebtTokenAddress","type":"address"},{"internalType":"address","name":"variableDebtTokenAddress","type":"address"},{"internalType":"address","name":"interestRateStrategyAddress","type":"address"},{"internalType":"uint128","name":"accruedToTreasury","type":"uint128"},{"internalType":"uint128","name":"unbacked","type":"uint128"},{"internalType":"uint128","name":"isolationModeTotalDebt","type":"uint128"}]}]},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"stateMutability":"view","type":"function","name":"getReserveDeficit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"stateMutability":"view","type":"function","name":"getReserveNormalizedIncome","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"stateMutability":"view","type":"function","name":"getReserveNormalizedVariableDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"stateMutability":"view","type":"function","name":"getReserveVariableDebtToken","outputs":[{"internalType":"address","name":"","type":"address"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"getReservesCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"getReservesList","outputs":[{"internalType":"address[]","name":"","type":"address[]"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"getSupplyLogic","outputs":[{"internalType":"address","name":"","type":"address"}]},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"stateMutability":"view","type":"function","name":"getUserAccountData","outputs":[{"internalType":"uint256","name":"totalCollateralBase","type":"uint256"},{"internalType":"uint256","name":"totalDebtBase","type":"uint256"},{"internalType":"uint256","name":"availableBorrowsBase","type":"uint256"},{"internalType":"uint256","name":"currentLiquidationThreshold","type":"uint256"},{"internalType":"uint256","name":"ltv","type":"uint256"},{"internalType":"uint256","name":"healthFactor","type":"uint256"}]},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"stateMutability":"view","type":"function","name":"getUserConfiguration","outputs":[{"internalType":"struct DataTypes.UserConfigurationMap","name":"","type":"tuple","components":[{"internalType":"uint256","name":"data","type":"uint256"}]}]},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"stateMutability":"view","type":"function","name":"getUserEMode","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"stateMutability":"view","type":"function","name":"getVirtualUnderlyingBalance","outputs":[{"internalType":"uint128","name":"","type":"uint128"}]},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"aTokenAddress","type":"address"},{"internalType":"address","name":"variableDebtAddress","type":"address"},{"internalType":"address","name":"interestRateStrategyAddress","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"initReserve"},{"inputs":[{"internalType":"address","name":"collateralAsset","type":"address"},{"internalType":"address","name":"debtAsset","type":"address"},{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"debtToCover","type":"uint256"},{"internalType":"bool","name":"receiveAToken","type":"bool"}],"stateMutability":"nonpayable","type":"function","name":"liquidationCall"},{"inputs":[{"internalType":"address[]","name":"assets","type":"address[]"}],"stateMutability":"nonpayable","type":"function","name":"mintToTreasury"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"onBehalfOf","type":"address"},{"internalType":"uint16","name":"referralCode","type":"uint16"}],"stateMutability":"nonpayable","type":"function","name":"mintUnbacked"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"interestRateMode","type":"uint256"},{"internalType":"address","name":"onBehalfOf","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"repay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"interestRateMode","type":"uint256"}],"stateMutability":"nonpayable","type":"function","name":"repayWithATokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"interestRateMode","type":"uint256"},{"internalType":"address","name":"onBehalfOf","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"permitV","type":"uint8"},{"internalType":"bytes32","name":"permitR","type":"bytes32"},{"internalType":"bytes32","name":"permitS","type":"bytes32"}],"stateMutability":"nonpayable","type":"function","name":"repayWithPermit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"nonpayable","type":"function","name":"rescueTokens"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"resetIsolationModeTotalDebt"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"struct DataTypes.ReserveConfigurationMap","name":"configuration","type":"tuple","components":[{"internalType":"uint256","name":"data","type":"uint256"}]}],"stateMutability":"nonpayable","type":"function","name":"setConfiguration"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint40","name":"until","type":"uint40"}],"stateMutability":"nonpayable","type":"function","name":"setLiquidationGracePeriod"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"rateStrategyAddress","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"setReserveInterestRateStrategyAddress"},{"inputs":[{"internalType":"uint8","name":"categoryId","type":"uint8"}],"stateMutability":"nonpayable","type":"function","name":"setUserEMode"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"bool","name":"useAsCollateral","type":"bool"}],"stateMutability":"nonpayable","type":"function","name":"setUserUseReserveAsCollateral"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"onBehalfOf","type":"address"},{"internalType":"uint16","name":"referralCode","type":"uint16"}],"stateMutability":"nonpayable","type":"function","name":"supply"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"onBehalfOf","type":"address"},{"internalType":"uint16","name":"referralCode","type":"uint16"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"permitV","type":"uint8"},{"internalType":"bytes32","name":"permitR","type":"bytes32"},{"internalType":"bytes32","name":"permitS","type":"bytes32"}],"stateMutability":"nonpayable","type":"function","name":"supplyWithPermit"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"syncIndexesState"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"syncRatesState"},{"inputs":[{"internalType":"uint256","name":"bridgeProtocolFee","type":"uint256"}],"stateMutability":"nonpayable","type":"function","name":"updateBridgeProtocolFee"},{"inputs":[{"internalType":"uint128","name":"flashLoanPremiumTotal","type":"uint128"},{"internalType":"uint128","name":"flashLoanPremiumToProtocol","type":"uint128"}],"stateMutability":"nonpayable","type":"function","name":"updateFlashloanPremiums"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"withdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]}],"devdoc":{"kind":"dev","methods":{"ADDRESSES_PROVIDER()":{"returns":{"_0":"The address of the PoolAddressesProvider"}},"BRIDGE_PROTOCOL_FEE()":{"returns":{"_0":"The bridge fee sent to the protocol treasury"}},"FLASHLOAN_PREMIUM_TOTAL()":{"returns":{"_0":"The total fee on flashloans"}},"FLASHLOAN_PREMIUM_TO_PROTOCOL()":{"returns":{"_0":"The flashloan fee sent to the protocol treasury"}},"MAX_NUMBER_RESERVES()":{"returns":{"_0":"The maximum number of reserves supported"}},"backUnbacked(address,uint256,uint256)":{"params":{"amount":"The amount to back","asset":"The address of the underlying asset to back","fee":"The amount paid in fees"},"returns":{"_0":"The backed amount"}},"borrow(address,uint256,uint256,uint16,address)":{"params":{"amount":"The amount to be borrowed","asset":"The address of the underlying asset to borrow","interestRateMode":"2 for Variable, 1 is deprecated on v3.2.0","onBehalfOf":"The address of the user who will receive the debt. Should be the address of the borrower itself calling the function if he wants to borrow against his own collateral, or the address of the credit delegator if he has been given credit delegation allowance","referralCode":"The code used to register the integrator originating the operation, for potential rewards. 0 if the action is executed directly by the user, without any middle-man"}},"configureEModeCategory(uint8,(uint16,uint16,uint16,string))":{"details":"In eMode, the protocol allows very high borrowing power to borrow assets of the same category. The category 0 is reserved as it's the default for volatile assets","params":{"config":"The configuration of the category","id":"The id of the category"}},"configureEModeCategoryBorrowableBitmap(uint8,uint128)":{"params":{"borrowableBitmap":"The borrowableBitmap of the category","id":"The id of the category"}},"configureEModeCategoryCollateralBitmap(uint8,uint128)":{"params":{"collateralBitmap":"The collateralBitmap of the category","id":"The id of the category"}},"deposit(address,uint256,address,uint16)":{"details":"Deprecated: Use the `supply` function instead","params":{"amount":"The amount to be supplied","asset":"The address of the underlying asset to supply","onBehalfOf":"The address that will receive the aTokens, same as msg.sender if the user wants to receive them on his own wallet, or a different address if the beneficiary of aTokens is a different wallet","referralCode":"Code used to register the integrator originating the operation, for potential rewards. 0 if the action is executed directly by the user, without any middle-man"}},"dropReserve(address)":{"details":"Only callable by the PoolConfigurator contractDoes not reset eMode flags, which must be considered when reusing the same reserve id for a different reserve.","params":{"asset":"The address of the underlying asset of the reserve"}},"eliminateReserveDeficit(address,uint256)":{"details":"The deficit of a reserve can occur due to situations where borrowed assets are not repaid, leading to bad debt.","params":{"amount":"The amount to be covered, in aToken or underlying on non-virtual accounted assets","asset":"The address of the underlying asset to cover the deficit."}},"finalizeTransfer(address,address,address,uint256,uint256,uint256)":{"details":"Only callable by the overlying aToken of the `asset`","params":{"amount":"The amount being transferred/withdrawn","asset":"The address of the underlying asset of the aToken","balanceFromBefore":"The aToken balance of the `from` user before the transfer","balanceToBefore":"The aToken balance of the `to` user before the transfer","from":"The user from which the aTokens are transferred","to":"The user receiving the aTokens"}},"flashLoan(address,address[],uint256[],uint256[],address,bytes,uint16)":{"details":"IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept into consideration. For further details please visit https://docs.aave.com/developers/","params":{"amounts":"The amounts of the assets being flash-borrowed","assets":"The addresses of the assets being flash-borrowed","interestRateModes":"Types of the debt to open if the flash loan is not returned: 0 -> Don't open any debt, just revert if funds can't be transferred from the receiver 1 -> Deprecated on v3.2.0 2 -> Open debt at variable rate for the value of the amount flash-borrowed to the `onBehalfOf` address","onBehalfOf":"The address that will receive the debt in the case of using 2 on `modes`","params":"Variadic packed params to pass to the receiver as extra information","receiverAddress":"The address of the contract receiving the funds, implementing IFlashLoanReceiver interface","referralCode":"The code used to register the integrator originating the operation, for potential rewards. 0 if the action is executed directly by the user, without any middle-man"}},"flashLoanSimple(address,address,uint256,bytes,uint16)":{"details":"IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept into consideration. For further details please visit https://docs.aave.com/developers/","params":{"amount":"The amount of the asset being flash-borrowed","asset":"The address of the asset being flash-borrowed","params":"Variadic packed params to pass to the receiver as extra information","receiverAddress":"The address of the contract receiving the funds, implementing IFlashLoanSimpleReceiver interface","referralCode":"The code used to register the integrator originating the operation, for potential rewards. 0 if the action is executed directly by the user, without any middle-man"}},"getConfiguration(address)":{"params":{"asset":"The address of the underlying asset of the reserve"},"returns":{"_0":"The configuration of the reserve"}},"getEModeCategoryBorrowableBitmap(uint8)":{"params":{"id":"The id of the category"},"returns":{"_0":"The borrowableBitmap of the category"}},"getEModeCategoryCollateralBitmap(uint8)":{"params":{"id":"The id of the category"},"returns":{"_0":"The collateralBitmap of the category"}},"getEModeCategoryCollateralConfig(uint8)":{"params":{"id":"The id of the category"},"returns":{"_0":"The ltv,lt,lb of the category"}},"getEModeCategoryData(uint8)":{"details":"DEPRECATED use independent getters instead","params":{"id":"The id of the category"},"returns":{"_0":"The configuration data of the category"}},"getEModeCategoryLabel(uint8)":{"params":{"id":"The id of the category"},"returns":{"_0":"The label of the category"}},"getLiquidationGracePeriod(address)":{"params":{"asset":"The address of the underlying asset"},"returns":{"_0":"Timestamp when the liquidation grace period will end*"}},"getReserveAToken(address)":{"params":{"asset":"The address of the underlying asset of the reserve"},"returns":{"_0":"The address of the aToken"}},"getReserveAddressById(uint16)":{"params":{"id":"The id of the reserve as stored in the DataTypes.ReserveData struct"},"returns":{"_0":"The address of the reserve associated with id"}},"getReserveData(address)":{"params":{"asset":"The address of the underlying asset of the reserve"},"returns":{"_0":"The state and configuration data of the reserve"}},"getReserveDeficit(address)":{"params":{"asset":"The address of the underlying asset of the reserve"},"returns":{"_0":"The current deficit of the reserve"}},"getReserveNormalizedIncome(address)":{"params":{"asset":"The address of the underlying asset of the reserve"},"returns":{"_0":"The reserve's normalized income"}},"getReserveNormalizedVariableDebt(address)":{"details":"WARNING: This function is intended to be used primarily by the protocol itself to get a \"dynamic\" variable index based on time, current stored index and virtual rate at the current moment (approx. a borrower would get if opening a position). This means that is always used in combination with variable debt supply/balances. If using this function externally, consider that is possible to have an increasing normalized variable debt that is not equivalent to how the variable debt index would be updated in storage (e.g. only updates with non-zero variable debt supply)","params":{"asset":"The address of the underlying asset of the reserve"},"returns":{"_0":"The reserve normalized variable debt"}},"getReserveVariableDebtToken(address)":{"params":{"asset":"The address of the underlying asset of the reserve"},"returns":{"_0":"The address of the variableDebtToken"}},"getReservesCount()":{"details":"It includes dropped reserves","returns":{"_0":"The count"}},"getReservesList()":{"details":"It does not include dropped reserves","returns":{"_0":"The addresses of the underlying assets of the initialized reserves"}},"getUserAccountData(address)":{"params":{"user":"The address of the user"},"returns":{"availableBorrowsBase":"The borrowing power left of the user in the base currency used by the price feed","currentLiquidationThreshold":"The liquidation threshold of the user","healthFactor":"The current health factor of the user","ltv":"The loan to value of The user","totalCollateralBase":"The total collateral of the user in the base currency used by the price feed","totalDebtBase":"The total debt of the user in the base currency used by the price feed"}},"getUserConfiguration(address)":{"params":{"user":"The user address"},"returns":{"_0":"The configuration of the user"}},"getUserEMode(address)":{"params":{"user":"The address of the user"},"returns":{"_0":"The eMode id"}},"getVirtualUnderlyingBalance(address)":{"params":{"asset":"The address of the underlying asset of the reserve"},"returns":{"_0":"The reserve virtual underlying balance"}},"initReserve(address,address,address,address)":{"details":"Only callable by the PoolConfigurator contract","params":{"aTokenAddress":"The address of the aToken that will be assigned to the reserve","asset":"The address of the underlying asset of the reserve","interestRateStrategyAddress":"The address of the interest rate strategy contract","variableDebtAddress":"The address of the VariableDebtToken that will be assigned to the reserve"}},"liquidationCall(address,address,address,uint256,bool)":{"params":{"collateralAsset":"The address of the underlying asset used as collateral, to receive as result of the liquidation","debtAsset":"The address of the underlying borrowed asset to be repaid with the liquidation","debtToCover":"The debt amount of borrowed `asset` the liquidator wants to cover","receiveAToken":"True if the liquidators wants to receive the collateral aTokens, `false` if he wants to receive the underlying collateral asset directly","user":"The address of the borrower getting liquidated"}},"mintToTreasury(address[])":{"params":{"assets":"The list of reserves for which the minting needs to be executed"}},"mintUnbacked(address,uint256,address,uint16)":{"params":{"amount":"The amount to mint","asset":"The address of the underlying asset to mint","onBehalfOf":"The address that will receive the aTokens","referralCode":"Code used to register the integrator originating the operation, for potential rewards. 0 if the action is executed directly by the user, without any middle-man"}},"repay(address,uint256,uint256,address)":{"params":{"amount":"The amount to repay - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode`","asset":"The address of the borrowed underlying asset previously borrowed","interestRateMode":"2 for Variable, 1 is deprecated on v3.2.0","onBehalfOf":"The address of the user who will get his debt reduced/removed. Should be the address of the user calling the function if he wants to reduce/remove his own debt, or the address of any other other borrower whose debt should be removed"},"returns":{"_0":"The final amount repaid"}},"repayWithATokens(address,uint256,uint256)":{"details":"Passing uint256.max as amount will clean up any residual aToken dust balance, if the user aToken balance is not enough to cover the whole debt","params":{"amount":"The amount to repay - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode`","asset":"The address of the borrowed underlying asset previously borrowed","interestRateMode":"DEPRECATED in v3.2.0"},"returns":{"_0":"The final amount repaid"}},"repayWithPermit(address,uint256,uint256,address,uint256,uint8,bytes32,bytes32)":{"params":{"amount":"The amount to repay - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode`","asset":"The address of the borrowed underlying asset previously borrowed","deadline":"The deadline timestamp that the permit is valid","interestRateMode":"2 for Variable, 1 is deprecated on v3.2.0","onBehalfOf":"Address of the user who will get his debt reduced/removed. Should be the address of the user calling the function if he wants to reduce/remove his own debt, or the address of any other other borrower whose debt should be removed","permitR":"The R parameter of ERC712 permit sig","permitS":"The S parameter of ERC712 permit sig","permitV":"The V parameter of ERC712 permit sig"},"returns":{"_0":"The final amount repaid"}},"rescueTokens(address,address,uint256)":{"params":{"amount":"The amount of token to transfer","to":"The address of the recipient","token":"The address of the token"}},"resetIsolationModeTotalDebt(address)":{"details":"It requires the given asset has zero debt ceiling","params":{"asset":"The address of the underlying asset to reset the isolationModeTotalDebt"}},"setConfiguration(address,(uint256))":{"details":"Only callable by the PoolConfigurator contract","params":{"asset":"The address of the underlying asset of the reserve","configuration":"The new configuration bitmap"}},"setLiquidationGracePeriod(address,uint40)":{"details":"To enable a liquidation grace period, a timestamp in the future should be set, To disable a liquidation grace period, any timestamp in the past works, like 0","params":{"asset":"The address of the underlying asset to set the liquidationGracePeriod","until":"Timestamp when the liquidation grace period will end*"}},"setReserveInterestRateStrategyAddress(address,address)":{"details":"Only callable by the PoolConfigurator contract","params":{"asset":"The address of the underlying asset of the reserve","rateStrategyAddress":"The address of the interest rate strategy contract"}},"setUserEMode(uint8)":{"params":{"categoryId":"The id of the category"}},"setUserUseReserveAsCollateral(address,bool)":{"params":{"asset":"The address of the underlying asset supplied","useAsCollateral":"True if the user wants to use the supply as collateral, false otherwise"}},"supply(address,uint256,address,uint16)":{"params":{"amount":"The amount to be supplied","asset":"The address of the underlying asset to supply","onBehalfOf":"The address that will receive the aTokens, same as msg.sender if the user wants to receive them on his own wallet, or a different address if the beneficiary of aTokens is a different wallet","referralCode":"Code used to register the integrator originating the operation, for potential rewards. 0 if the action is executed directly by the user, without any middle-man"}},"supplyWithPermit(address,uint256,address,uint16,uint256,uint8,bytes32,bytes32)":{"params":{"amount":"The amount to be supplied","asset":"The address of the underlying asset to supply","deadline":"The deadline timestamp that the permit is valid","onBehalfOf":"The address that will receive the aTokens, same as msg.sender if the user wants to receive them on his own wallet, or a different address if the beneficiary of aTokens is a different wallet","permitR":"The R parameter of ERC712 permit sig","permitS":"The S parameter of ERC712 permit sig","permitV":"The V parameter of ERC712 permit sig","referralCode":"Code used to register the integrator originating the operation, for potential rewards. 0 if the action is executed directly by the user, without any middle-man"}},"syncIndexesState(address)":{"details":"Only callable by the PoolConfigurator contractTo be used when required by the configurator, for example when updating interest rates strategy data","params":{"asset":"The address of the underlying asset of the reserve"}},"syncRatesState(address)":{"details":"Only callable by the PoolConfigurator contractTo be used when required by the configurator, for example when updating interest rates strategy data","params":{"asset":"The address of the underlying asset of the reserve"}},"updateBridgeProtocolFee(uint256)":{"params":{"bridgeProtocolFee":"The part of the premium sent to the protocol treasury"}},"updateFlashloanPremiums(uint128,uint128)":{"details":"The total premium is calculated on the total borrowed amountThe premium to protocol is calculated on the total premium, being a percentage of `flashLoanPremiumTotal`Only callable by the PoolConfigurator contract","params":{"flashLoanPremiumToProtocol":"The part of the premium sent to the protocol treasury, expressed in bps","flashLoanPremiumTotal":"The total premium, expressed in bps"}},"withdraw(address,uint256,address)":{"params":{"amount":"The underlying amount to be withdrawn - Send the value type(uint256).max in order to withdraw the whole aToken balance","asset":"The address of the underlying asset to withdraw","to":"The address that will receive the underlying, same as msg.sender if the user wants to receive it on his own wallet, or a different address if the beneficiary is a different wallet"},"returns":{"_0":"The final amount withdrawn"}}},"version":1},"userdoc":{"kind":"user","methods":{"ADDRESSES_PROVIDER()":{"notice":"Returns the PoolAddressesProvider connected to this contract"},"BRIDGE_PROTOCOL_FEE()":{"notice":"Returns the part of the bridge fees sent to protocol"},"FLASHLOAN_PREMIUM_TOTAL()":{"notice":"Returns the total fee on flash loans"},"FLASHLOAN_PREMIUM_TO_PROTOCOL()":{"notice":"Returns the part of the flashloan fees sent to protocol"},"MAX_NUMBER_RESERVES()":{"notice":"Returns the maximum number of reserves supported to be listed in this Pool"},"backUnbacked(address,uint256,uint256)":{"notice":"Back the current unbacked underlying with `amount` and pay `fee`."},"borrow(address,uint256,uint256,uint16,address)":{"notice":"Allows users to borrow a specific `amount` of the reserve underlying asset, provided that the borrower already supplied enough collateral, or he was given enough allowance by a credit delegator on the VariableDebtToken - E.g. User borrows 100 USDC passing as `onBehalfOf` his own address, receiving the 100 USDC in his wallet and 100 variable debt tokens"},"configureEModeCategory(uint8,(uint16,uint16,uint16,string))":{"notice":"Configures a new or alters an existing collateral configuration of an eMode."},"configureEModeCategoryBorrowableBitmap(uint8,uint128)":{"notice":"Replaces the current eMode borrowableBitmap."},"configureEModeCategoryCollateralBitmap(uint8,uint128)":{"notice":"Replaces the current eMode collateralBitmap."},"deposit(address,uint256,address,uint16)":{"notice":"Supplies an `amount` of underlying asset into the reserve, receiving in return overlying aTokens. - E.g. User supplies 100 USDC and gets in return 100 aUSDC"},"dropReserve(address)":{"notice":"Drop a reserve"},"eliminateReserveDeficit(address,uint256)":{"notice":"It covers the deficit of a specified reserve by burning: - the equivalent aToken `amount` for assets with virtual accounting enabled - the equivalent `amount` of underlying for assets with virtual accounting disabled (e.g. GHO)"},"finalizeTransfer(address,address,address,uint256,uint256,uint256)":{"notice":"Validates and finalizes an aToken transfer"},"flashLoan(address,address[],uint256[],uint256[],address,bytes,uint16)":{"notice":"Allows smartcontracts to access the liquidity of the pool within one transaction, as long as the amount taken plus a fee is returned."},"flashLoanSimple(address,address,uint256,bytes,uint16)":{"notice":"Allows smartcontracts to access the liquidity of the pool within one transaction, as long as the amount taken plus a fee is returned."},"getBorrowLogic()":{"notice":"Gets the address of the external BorrowLogic"},"getBridgeLogic()":{"notice":"Gets the address of the external BridgeLogic"},"getConfiguration(address)":{"notice":"Returns the configuration of the reserve"},"getEModeCategoryBorrowableBitmap(uint8)":{"notice":"Returns the borrowableBitmap of an eMode category"},"getEModeCategoryCollateralBitmap(uint8)":{"notice":"Returns the collateralBitmap of an eMode category"},"getEModeCategoryCollateralConfig(uint8)":{"notice":"Returns the collateral config of an eMode category"},"getEModeCategoryData(uint8)":{"notice":"Returns the data of an eMode category"},"getEModeCategoryLabel(uint8)":{"notice":"Returns the label of an eMode category"},"getEModeLogic()":{"notice":"Gets the address of the external EModeLogic"},"getFlashLoanLogic()":{"notice":"Gets the address of the external FlashLoanLogic"},"getLiquidationGracePeriod(address)":{"notice":"Returns the liquidation grace period of the given asset"},"getLiquidationLogic()":{"notice":"Gets the address of the external LiquidationLogic"},"getPoolLogic()":{"notice":"Gets the address of the external PoolLogic"},"getReserveAToken(address)":{"notice":"Returns the aToken address of a reserve."},"getReserveAddressById(uint16)":{"notice":"Returns the address of the underlying asset of a reserve by the reserve id as stored in the DataTypes.ReserveData struct"},"getReserveData(address)":{"notice":"Returns the state and configuration of the reserve"},"getReserveDeficit(address)":{"notice":"Returns the current deficit of a reserve."},"getReserveNormalizedIncome(address)":{"notice":"Returns the normalized income of the reserve"},"getReserveNormalizedVariableDebt(address)":{"notice":"Returns the normalized variable debt per unit of asset"},"getReserveVariableDebtToken(address)":{"notice":"Returns the variableDebtToken address of a reserve."},"getReservesCount()":{"notice":"Returns the number of initialized reserves"},"getReservesList()":{"notice":"Returns the list of the underlying assets of all the initialized reserves"},"getSupplyLogic()":{"notice":"Gets the address of the external SupplyLogic"},"getUserAccountData(address)":{"notice":"Returns the user account data across all the reserves"},"getUserConfiguration(address)":{"notice":"Returns the configuration of the user across all the reserves"},"getUserEMode(address)":{"notice":"Returns the eMode the user is using"},"getVirtualUnderlyingBalance(address)":{"notice":"Returns the virtual underlying balance of the reserve"},"initReserve(address,address,address,address)":{"notice":"Initializes a reserve, activating it, assigning an aToken and debt tokens and an interest rate strategy"},"liquidationCall(address,address,address,uint256,bool)":{"notice":"Function to liquidate a non-healthy position collateral-wise, with Health Factor below 1 - The caller (liquidator) covers `debtToCover` amount of debt of the user getting liquidated, and receives a proportionally amount of the `collateralAsset` plus a bonus to cover market risk"},"mintToTreasury(address[])":{"notice":"Mints the assets accrued through the reserve factor to the treasury in the form of aTokens"},"mintUnbacked(address,uint256,address,uint16)":{"notice":"Mints an `amount` of aTokens to the `onBehalfOf`"},"repay(address,uint256,uint256,address)":{"notice":"Repays a borrowed `amount` on a specific reserve, burning the equivalent debt tokens owned - E.g. User repays 100 USDC, burning 100 variable debt tokens of the `onBehalfOf` address"},"repayWithATokens(address,uint256,uint256)":{"notice":"Repays a borrowed `amount` on a specific reserve using the reserve aTokens, burning the equivalent debt tokens - E.g. User repays 100 USDC using 100 aUSDC, burning 100 variable debt tokens"},"repayWithPermit(address,uint256,uint256,address,uint256,uint8,bytes32,bytes32)":{"notice":"Repay with transfer approval of asset to be repaid done via permit function see: https://eips.ethereum.org/EIPS/eip-2612 and https://eips.ethereum.org/EIPS/eip-713"},"rescueTokens(address,address,uint256)":{"notice":"Rescue and transfer tokens locked in this contract"},"resetIsolationModeTotalDebt(address)":{"notice":"Resets the isolation mode total debt of the given asset to zero"},"setConfiguration(address,(uint256))":{"notice":"Sets the configuration bitmap of the reserve as a whole"},"setLiquidationGracePeriod(address,uint40)":{"notice":"Sets the liquidation grace period of the given asset"},"setReserveInterestRateStrategyAddress(address,address)":{"notice":"Updates the address of the interest rate strategy contract"},"setUserEMode(uint8)":{"notice":"Allows a user to use the protocol in eMode"},"setUserUseReserveAsCollateral(address,bool)":{"notice":"Allows suppliers to enable/disable a specific supplied asset as collateral"},"supply(address,uint256,address,uint16)":{"notice":"Supplies an `amount` of underlying asset into the reserve, receiving in return overlying aTokens. - E.g. User supplies 100 USDC and gets in return 100 aUSDC"},"supplyWithPermit(address,uint256,address,uint16,uint256,uint8,bytes32,bytes32)":{"notice":"Supply with transfer approval of asset to be supplied done via permit function see: https://eips.ethereum.org/EIPS/eip-2612 and https://eips.ethereum.org/EIPS/eip-713"},"syncIndexesState(address)":{"notice":"Accumulates interest to all indexes of the reserve"},"syncRatesState(address)":{"notice":"Updates interest rates on the reserve data"},"updateBridgeProtocolFee(uint256)":{"notice":"Updates the protocol fee on the bridging"},"updateFlashloanPremiums(uint128,uint128)":{"notice":"Updates flash loan premiums. Flash loan premium consists of two parts: - A part is sent to aToken holders as extra, one time accumulated interest - A part is collected by the protocol treasury"},"withdraw(address,uint256,address)":{"notice":"Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC"}},"version":1}},"settings":{"remappings":["@openzeppelin/contracts-upgradeable/=lib/solidity-utils/lib/openzeppelin-contracts-upgradeable/contracts/","@openzeppelin/contracts/=lib/solidity-utils/lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/","ds-test/=lib/forge-std/lib/ds-test/src/","erc4626-tests/=lib/solidity-utils/lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/","forge-std/=lib/forge-std/src/","halmos-cheatcodes/=lib/solidity-utils/lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/","openzeppelin-contracts-upgradeable/=lib/solidity-utils/lib/openzeppelin-contracts-upgradeable/","openzeppelin-contracts/=lib/solidity-utils/lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/","solidity-utils/=lib/solidity-utils/src/"],"optimizer":{"enabled":true,"runs":200},"metadata":{"bytecodeHash":"none"},"compilationTarget":{"src/contracts/interfaces/IPool.sol":"IPool"},"evmVersion":"shanghai","libraries":{}},"sources":{"src/contracts/interfaces/IPool.sol":{"keccak256":"0xc22ca9af1e717264fb146ec5cc05e3284c5744db77f8dc7b31ab9d8b34812ae5","urls":["bzz-raw://386c0e01a9223b35ba49dc5c2c7cdd61eb55632f9262630a251b8b103c20140c","dweb:/ipfs/QmWfmWxzC26w67BjXnyqu9vNXcE5gWsL8VkWD88wGnExdc"],"license":"MIT"},"src/contracts/interfaces/IPoolAddressesProvider.sol":{"keccak256":"0xa60921cf54e91ca8db038effeffc876089b2e72dbf01d68a10ff461770d345e5","urls":["bzz-raw://0de2fd03bf5cd6ae8718a486e5d615c67b260d0608b9fb7076888a4bfaa0e4b4","dweb:/ipfs/QmXpdmxMugyJcBFAGJEruB78bkkuTwrH9esYRaS6wiQoMs"],"license":"MIT"},"src/contracts/protocol/libraries/types/DataTypes.sol":{"keccak256":"0xddac0f87cd5196a7df4bc75480ddb1823a8905f483473c8ece1f84651bf7070b","urls":["bzz-raw://d2303b8550e016f4adc0658c06356222ba6ee8b5603e2fc2910bfe4ed14b3a71","dweb:/ipfs/QmdoiTc5R6ZPsaehszSBuHv4f8A6DTosAMoMD157jegWra"],"license":"MIT"}},"version":1},"id":161} diff --git a/crates/driver/example.toml b/crates/driver/example.toml index 926c84ae08..095cb9cff3 100644 --- a/crates/driver/example.toml +++ b/crates/driver/example.toml @@ -34,6 +34,8 @@ use-soft-cancellations = true [contracts] # Optionally override the contract addresses, necessary on less popular blockchains gp-v2-settlement = "0x9008D19f58AAbD9eD0D60971565AA8510560ab41" weth = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" +# TODO replace with the actual address when the contract actually gets replaced +flashloan-wrappers = ["0x0000000000000000000000000000000000000000"] [[contracts.cow-amms]] # address of factory creating new CoW AMMs factory = "0x86f3df416979136cb4fdea2c0886301b911c163b" diff --git a/crates/driver/src/domain/competition/auction.rs b/crates/driver/src/domain/competition/auction.rs index 66abb027fd..971f143522 100644 --- a/crates/driver/src/domain/competition/auction.rs +++ b/crates/driver/src/domain/competition/auction.rs @@ -294,6 +294,15 @@ impl AuctionProcessor { // down in case the available user balance is only enough to partially // cover the rest of the order. orders.retain_mut(|order| { + // Update order app data if it was fetched. + if let Some(fetched_app_data) = app_data_by_hash.get(&order.app_data.hash()) { + order.app_data = fetched_app_data.clone().into(); + if order.app_data.flashloan().is_some() { + // assume all the necessary tokens will come from the flashloan + return true; + } + } + let remaining_balance = match balances.get_mut(&( order.trader(), order.sell.token, @@ -350,11 +359,6 @@ impl AuctionProcessor { remaining_balance.0 -= allocated_balance.0; - // Update order app data if it was fetched. - if let Some(fetched_app_data) = app_data_by_hash.get(&order.app_data.hash()) { - order.app_data = fetched_app_data.clone().into(); - } - true }); } diff --git a/crates/driver/src/domain/competition/order/app_data.rs b/crates/driver/src/domain/competition/order/app_data.rs index 5d5e05bf34..c23aefa7be 100644 --- a/crates/driver/src/domain/competition/order/app_data.rs +++ b/crates/driver/src/domain/competition/order/app_data.rs @@ -1,5 +1,7 @@ use { crate::util::Bytes, + anyhow::Context, + app_data::AppDataDocument, derive_more::From, futures::FutureExt, moka::future::Cache, @@ -62,13 +64,20 @@ impl AppDataRetriever { let url = self_ .0 .base_url - .join(&format!("v1/app_data/{:?}", app_data.0))?; + .join(&format!("api/v1/app_data/{:?}", app_data.0))?; let response = self_.0.client.get(url).send().await?; let validated_app_data = match response.status() { StatusCode::NOT_FOUND => None, _ => { - let bytes = response.error_for_status()?.bytes().await?; - Some(self_.0.app_data_validator.validate(&bytes)?) + let appdata: AppDataDocument = + serde_json::from_str(&response.text().await?) + .context("invalid app data document")?; + (appdata.full_app_data != app_data::EMPTY).then_some( + self_ + .0 + .app_data_validator + .validate(&appdata.full_app_data.into_bytes())?, + ) } }; diff --git a/crates/driver/src/domain/competition/solution/encoding.rs b/crates/driver/src/domain/competition/solution/encoding.rs index d1e74de7b5..cfce2d74bd 100644 --- a/crates/driver/src/domain/competition/solution/encoding.rs +++ b/crates/driver/src/domain/competition/solution/encoding.rs @@ -10,10 +10,11 @@ use { liquidity, }, infra::{self, solver::ManageNativeToken}, - util::Bytes, + util::{Bytes, conv::u256::U256Ext}, }, allowance::Allowance, itertools::Itertools, + shared::addr, }; #[derive(Debug, thiserror::Error)] @@ -173,6 +174,97 @@ pub fn tx( min: None, prices: auction.prices().clone(), }; + + // Add all interactions needed to move flash loaned tokens around + // These interactions are executed before all other pre-interactions + let encoded_flashloans: Vec<_> = solution + .flashloans + .iter() + // Necessary pre-interactions get prepended to the settlement. So to initiate + // the loans in the desired order we need to add them in reverse order. + .rev() + .map(|flashloan| { + // TODO add configuration options + // Hardcoded configuration for now + let maker_lender = addr!("60744434d6339a6B27d73d9Eda62b6F66a0a04FA"); + let aave_lender = addr!("87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2"); + + let (flashloan_wrapper, flash_fee_bps) = if flashloan.lender.0 == maker_lender { + (&contracts.flashloan_wrappers()[0], eth::U256::zero()) // MAKER + } else if flashloan.lender.0 == aave_lender { + // TODO: ask AAVE to waive the current 5 BPS fee for our helper contract + (&contracts.flashloan_wrappers()[1], eth::U256::from(5)) // AAVE + } else { + // TODO remove this together with configuration options + (&contracts.flashloan_wrappers()[0], eth::U256::zero()) // for driver tests to pass + }; + + // Allow settlement contract to pull borrowed tokens from flashloan wrapper + pre_interactions.insert( + 0, + approve_flashloan( + flashloan.token, + flashloan.amount, + contracts.settlement().address().into(), + flashloan_wrapper, + ), + ); + + // Transfer tokens from flashloan wrapper to user (i.e. borrower) to later allow + // settlement contract to pull in all the necessary sell tokens from the user. + let tx = contracts::ERC20::at( + &contracts.settlement().raw_instance().web3(), + flashloan.token.into(), + ) + .transfer_from( + flashloan_wrapper.address(), + flashloan.borrower.into(), + flashloan.amount.0, + ) + .into_inner(); + pre_interactions.insert( + 1, + eth::Interaction { + target: tx.to.unwrap().into(), + value: eth::U256::zero().into(), + call_data: tx.data.unwrap().0.into(), + }, + ); + + // Repayment amount needs to be increased by flash fee + let fee_amount = (flashloan.amount.0 * flash_fee_bps).ceil_div(&10_000.into()); + let repayment_amount = flashloan.amount.0 + fee_amount; + + // Since the order receiver is expected to be the setttlement contract, we need + // to transfer tokens from the settlement contract to the flashloan wrapper + let tx = contracts::ERC20::at( + &contracts.settlement().raw_instance().web3(), + flashloan.token.into(), + ) + .transfer_from( + contracts.settlement().address(), + flashloan_wrapper.address(), + repayment_amount, + ) + .into_inner(); + post_interactions.push(eth::Interaction { + target: tx.to.unwrap().into(), + value: eth::U256::zero().into(), + call_data: tx.data.unwrap().0.into(), + }); + + // Allow flash loan lender to take tokens from wrapper contract + post_interactions.push(approve_flashloan( + flashloan.token, + repayment_amount.into(), + flashloan.lender, + flashloan_wrapper, + )); + + (flashloan, flashloan_wrapper) + }) + .collect(); + for interaction in solution.interactions() { if matches!(internalization, settlement::Internalization::Enable) && interaction.internalize() @@ -215,9 +307,29 @@ pub fn tx( let mut calldata = tx.data.unwrap().0; calldata.extend(auction.id().ok_or(Error::MissingAuctionId)?.to_be_bytes()); + // TODO: support multiple flashloans in 1 settlement + // Target and calldata depend on whether a flashloan is used + let (to, calldata) = match encoded_flashloans.first() { + Some((flashloan, flashloan_wrapper)) => { + let calldata = flashloan_wrapper + .flash_loan_and_settle( + flashloan.lender.into(), + (flashloan.token.into(), flashloan.amount.into()), + ethcontract::Bytes(calldata), + ) + .tx + .data + .unwrap() + .0; + + (flashloan_wrapper.address().into(), calldata) + } + None => (contracts.settlement().address().into(), calldata), + }; + Ok(eth::Tx { from: solution.solver().address(), - to: contracts.settlement().address().into(), + to, input: calldata.into(), value: Ether(0.into()), access_list: Default::default(), @@ -274,6 +386,22 @@ pub fn approve(allowance: &Allowance) -> eth::Interaction { } } +fn approve_flashloan( + token: eth::TokenAddress, + amount: eth::TokenAmount, + spender: eth::ContractAddress, + flashloan_wrapper: &contracts::IFlashLoanSolverWrapper, +) -> eth::Interaction { + let tx = flashloan_wrapper + .approve(token.into(), spender.into(), amount.0) + .into_inner(); + eth::Interaction { + target: tx.to.unwrap().into(), + value: eth::U256::zero().into(), + call_data: tx.data.unwrap().0.into(), + } +} + fn unwrap(amount: eth::TokenAmount, weth: &contracts::WETH9) -> eth::Interaction { let tx = weth.withdraw(amount.into()).into_inner(); eth::Interaction { diff --git a/crates/driver/src/domain/competition/solution/mod.rs b/crates/driver/src/domain/competition/solution/mod.rs index 5b73b6b8df..cefe4fa5e3 100644 --- a/crates/driver/src/domain/competition/solution/mod.rs +++ b/crates/driver/src/domain/competition/solution/mod.rs @@ -5,7 +5,7 @@ use { boundary, domain::{ competition::{self, order}, - eth::{self, TokenAddress}, + eth::{self, Flashloan, TokenAddress}, }, infra::{ Simulator, @@ -19,7 +19,6 @@ use { futures::future::try_join_all, itertools::Itertools, num::{BigRational, One}, - solvers_dto::solution::Flashloan, std::{ collections::{BTreeSet, HashMap, HashSet, hash_map::Entry}, sync::atomic::{AtomicU64, Ordering}, @@ -360,7 +359,7 @@ impl Solution { (None, Some(gas)) => Some(gas), (None, None) => None, }, - flashloans: self.flashloans.clone(), + flashloans: [self.flashloans.clone(), other.flashloans.clone()].concat(), }) } @@ -494,6 +493,8 @@ impl std::fmt::Debug for Solution { .field("interactions", &self.interactions) .field("post_interactions", &self.post_interactions) .field("solver", &self.solver.name()) + .field("gas", &self.gas) + .field("flashloans", &self.flashloans) .finish() } } diff --git a/crates/driver/src/domain/eth/mod.rs b/crates/driver/src/domain/eth/mod.rs index 12dfe34d20..8c366c413f 100644 --- a/crates/driver/src/domain/eth/mod.rs +++ b/crates/driver/src/domain/eth/mod.rs @@ -17,7 +17,7 @@ pub use { eip712::DomainSeparator, gas::{EffectiveGasPrice, FeePerGas, Gas, GasPrice}, number::nonzero::U256 as NonZeroU256, - primitive_types::{H160, H256, U256}, + primitive_types::{H160, H256, U256, U512}, }; // TODO This module is getting a little hectic with all kinds of different @@ -411,3 +411,11 @@ impl From for [u8; 32] { value.0.0 } } + +#[derive(Debug, Clone)] +pub struct Flashloan { + pub lender: ContractAddress, + pub borrower: Address, + pub token: TokenAddress, + pub amount: TokenAmount, +} diff --git a/crates/driver/src/infra/blockchain/contracts.rs b/crates/driver/src/infra/blockchain/contracts.rs index 1909894dff..ca03e79f5d 100644 --- a/crates/driver/src/infra/blockchain/contracts.rs +++ b/crates/driver/src/infra/blockchain/contracts.rs @@ -17,6 +17,9 @@ pub struct Contracts { /// The domain separator for settlement contract used for signing orders. settlement_domain_separator: eth::DomainSeparator, cow_amm_registry: cow_amm::Registry, + + /// Each lender potentially has different solver wrapper. + flashloan_wrappers: Vec, } #[derive(Debug, Default, Clone)] @@ -24,6 +27,7 @@ pub struct Addresses { pub settlement: Option, pub weth: Option, pub cow_amms: Vec, + pub flashloan_wrappers: Vec, } impl Contracts { @@ -78,6 +82,20 @@ impl Contracts { } cow_amm_registry.spawn_maintenance_task(block_stream); + let flashloan_wrappers = addresses + .flashloan_wrappers + .iter() + .map(|address| { + contracts::IFlashLoanSolverWrapper::at( + web3, + address_for( + contracts::IFlashLoanSolverWrapper::raw_contract(), + Some(*address), + ), + ) + }) + .collect(); + Ok(Self { settlement, vault_relayer, @@ -85,6 +103,7 @@ impl Contracts { weth, settlement_domain_separator, cow_amm_registry, + flashloan_wrappers, }) } @@ -115,6 +134,10 @@ impl Contracts { pub fn cow_amm_registry(&self) -> &cow_amm::Registry { &self.cow_amm_registry } + + pub fn flashloan_wrappers(&self) -> &[contracts::IFlashLoanSolverWrapper] { + &self.flashloan_wrappers + } } #[derive(Debug, Clone)] diff --git a/crates/driver/src/infra/config/file/load.rs b/crates/driver/src/infra/config/file/load.rs index f17712b566..d6a26bd67d 100644 --- a/crates/driver/src/infra/config/file/load.rs +++ b/crates/driver/src/infra/config/file/load.rs @@ -375,6 +375,12 @@ pub async fn load(chain: Chain, path: &Path) -> infra::Config { helper: cfg.helper, }) .collect(), + flashloan_wrappers: config + .contracts + .flashloan_wrappers + .into_iter() + .map(Into::into) + .collect(), }, disable_access_list_simulation: config.disable_access_list_simulation, disable_gas_simulation: config.disable_gas_simulation.map(Into::into), diff --git a/crates/driver/src/infra/config/file/mod.rs b/crates/driver/src/infra/config/file/mod.rs index 5839636dcf..6052837443 100644 --- a/crates/driver/src/infra/config/file/mod.rs +++ b/crates/driver/src/infra/config/file/mod.rs @@ -370,6 +370,11 @@ struct ContractsConfig { /// rebalancing orders for. #[serde(default)] cow_amms: Vec, + + /// Flashloan wrapper addresses. Note that each lender has it's own wrapper. + /// Currently Maker and Aave lenders are supported. + #[serde(default)] + flashloan_wrappers: Vec, } #[derive(Debug, Clone, Deserialize)] diff --git a/crates/driver/src/infra/solver/dto/solution.rs b/crates/driver/src/infra/solver/dto/solution.rs index 57eab44d51..5e7039b440 100644 --- a/crates/driver/src/infra/solver/dto/solution.rs +++ b/crates/driver/src/infra/solver/dto/solution.rs @@ -208,7 +208,16 @@ impl Solutions { solution.gas.map(|gas| eth::Gas(gas.into())), solver_config.fee_handler, auction.surplus_capturing_jit_order_owners(), - solution.flashloans, + solution + .flashloans + .into_iter() + .map(|flashloan| eth::Flashloan { + lender: flashloan.lender.into(), + borrower: flashloan.borrower.into(), + token: flashloan.token.into(), + amount: flashloan.amount.into(), + }) + .collect(), ) .map_err(|err| match err { competition::solution::error::Solution::InvalidClearingPrices => { diff --git a/crates/driver/src/tests/setup/blockchain.rs b/crates/driver/src/tests/setup/blockchain.rs index 85953a78d7..ce45402acf 100644 --- a/crates/driver/src/tests/setup/blockchain.rs +++ b/crates/driver/src/tests/setup/blockchain.rs @@ -35,6 +35,7 @@ pub struct Blockchain { pub tokens: HashMap<&'static str, contracts::ERC20Mintable>, pub weth: contracts::WETH9, pub settlement: contracts::GPv2Settlement, + pub flashloan_wrapper: contracts::ERC3156FlashLoanSolverWrapper, pub ethflow: Option, pub domain_separator: boundary::DomainSeparator, pub node: Node, @@ -322,6 +323,14 @@ impl Blockchain { ) .await .unwrap(); + let flashloan_wrapper = wait_for( + &web3, + contracts::ERC3156FlashLoanSolverWrapper::builder(&web3, settlement.address()) + .from(main_trader_account.clone()) + .deploy(), + ) + .await + .unwrap(); let mut trader_accounts = Vec::new(); for config in config.solvers { @@ -630,6 +639,7 @@ impl Blockchain { web3_url: node.url(), node, pairs, + flashloan_wrapper, } } diff --git a/crates/driver/src/tests/setup/driver.rs b/crates/driver/src/tests/setup/driver.rs index 509a3c093c..669e8f964c 100644 --- a/crates/driver/src/tests/setup/driver.rs +++ b/crates/driver/src/tests/setup/driver.rs @@ -221,12 +221,14 @@ async fn create_config_file( r#"[contracts] gp-v2-settlement = "{}" weth = "{}" + flashloan-wrappers = ["{}"] [submission] gas-price-cap = "1000000000000" "#, hex_address(blockchain.settlement.address()), - hex_address(blockchain.weth.address()) + hex_address(blockchain.weth.address()), + hex_address(blockchain.flashloan_wrapper.address()), ) .unwrap(); diff --git a/crates/driver/src/tests/setup/orderbook.rs b/crates/driver/src/tests/setup/orderbook.rs index 8ffb314aff..440b6ec9cf 100644 --- a/crates/driver/src/tests/setup/orderbook.rs +++ b/crates/driver/src/tests/setup/orderbook.rs @@ -1,5 +1,6 @@ use { crate::{domain::competition::order::app_data::AppData, tests::setup::Order}, + app_data::AppDataDocument, axum::{ Extension, Json, @@ -42,7 +43,7 @@ impl Orderbook { .collect::>(); let app = Router::new() - .route("/v1/app_data/:app_data", get(Self::app_data_handler)) + .route("/api/v1/app_data/:app_data", get(Self::app_data_handler)) .layer(Extension(app_data_storage)); let server = axum::Server::bind(&"0.0.0.0:0".parse().unwrap()).serve(app.into_make_service()); @@ -73,7 +74,10 @@ impl Orderbook { }; if let Some(full_app_data) = app_data_storage.get(&app_data_hash) { - return Json(full_app_data.clone()).into_response(); + let response = AppDataDocument { + full_app_data: serde_json::to_string(full_app_data).unwrap(), + }; + return Json(response).into_response(); } StatusCode::NOT_FOUND.into_response() diff --git a/crates/driver/src/tests/setup/solver.rs b/crates/driver/src/tests/setup/solver.rs index f4f3e5f174..f2e05f405a 100644 --- a/crates/driver/src/tests/setup/solver.rs +++ b/crates/driver/src/tests/setup/solver.rs @@ -449,6 +449,7 @@ impl Solver { settlement: Some(config.blockchain.settlement.address().into()), weth: Some(config.blockchain.weth.address().into()), cow_amms: vec![], + flashloan_wrappers: vec![config.blockchain.flashloan_wrapper.address().into()], }, gas, None, diff --git a/crates/e2e/src/setup/colocation.rs b/crates/e2e/src/setup/colocation.rs index afdc0c1c66..57adf387f9 100644 --- a/crates/e2e/src/setup/colocation.rs +++ b/crates/e2e/src/setup/colocation.rs @@ -164,10 +164,12 @@ factory = "{:?}" r#" app-data-fetching-enabled = true orderbook-url = "http://localhost:8080" +flashloans-enabled = true [contracts] gp-v2-settlement = "{:?}" weth = "{:?}" +flashloan-wrappers = ["{:?}","{:?}"] # Maker, Aave {cow_amms} {solvers} @@ -185,6 +187,8 @@ mempool = "public" "#, contracts.gp_settlement.address(), contracts.weth.address(), + contracts.flashloan_wrapper_maker.address(), + contracts.flashloan_wrapper_aave.address(), )); let args = vec![ "driver".to_string(), diff --git a/crates/e2e/src/setup/deploy.rs b/crates/e2e/src/setup/deploy.rs index 1127710169..81c1ea6494 100644 --- a/crates/e2e/src/setup/deploy.rs +++ b/crates/e2e/src/setup/deploy.rs @@ -1,9 +1,11 @@ use { contracts::{ + AaveFlashLoanSolverWrapper, BalancerV2Authorizer, BalancerV2Vault, CoWSwapEthFlow, CowAmmLegacyHelper, + ERC3156FlashLoanSolverWrapper, GPv2AllowListAuthentication, GPv2Settlement, HooksTrampoline, @@ -29,6 +31,8 @@ pub struct Contracts { pub ethflows: Vec, pub hooks: HooksTrampoline, pub cow_amm_helper: Option, + pub flashloan_wrapper_maker: ERC3156FlashLoanSolverWrapper, + pub flashloan_wrapper_aave: AaveFlashLoanSolverWrapper, } impl Contracts { @@ -48,6 +52,18 @@ impl Contracts { Ok(contract) => Some(contract), }; + // TODO: use Contract::deployed() after contracts got deployed on mainnet + let flashloan_wrapper_aave = + AaveFlashLoanSolverWrapper::builder(web3, gp_settlement.address()) + .deploy() + .await + .unwrap(); + let flashloan_wrapper_maker = + ERC3156FlashLoanSolverWrapper::builder(web3, gp_settlement.address()) + .deploy() + .await + .unwrap(); + Self { chain_id: network_id .parse() @@ -74,6 +90,8 @@ impl Contracts { hooks: HooksTrampoline::deployed(web3).await.unwrap(), gp_settlement, cow_amm_helper, + flashloan_wrapper_maker, + flashloan_wrapper_aave, } } @@ -159,6 +177,9 @@ impl Contracts { let ethflow = deploy!(CoWSwapEthFlow(gp_settlement.address(), weth.address())); let ethflow_secondary = deploy!(CoWSwapEthFlow(gp_settlement.address(), weth.address())); let hooks = deploy!(HooksTrampoline(gp_settlement.address())); + let flashloan_wrapper_maker = + deploy!(ERC3156FlashLoanSolverWrapper(gp_settlement.address())); + let flashloan_wrapper_aave = deploy!(AaveFlashLoanSolverWrapper(gp_settlement.address())); Self { chain_id: network_id @@ -176,6 +197,8 @@ impl Contracts { hooks, // Current helper contract only works in forked tests cow_amm_helper: None, + flashloan_wrapper_maker, + flashloan_wrapper_aave, } } diff --git a/crates/e2e/src/setup/onchain_components/mod.rs b/crates/e2e/src/setup/onchain_components/mod.rs index 68d609ba32..aabb91eacb 100644 --- a/crates/e2e/src/setup/onchain_components/mod.rs +++ b/crates/e2e/src/setup/onchain_components/mod.rs @@ -303,6 +303,23 @@ impl OnchainComponents { .expect("failed to add solver"); } + // TODO: remove when contracts are actually deployed + // flashloan wrapper also needs to be authorized + self.contracts + .gp_authenticator + .add_solver(self.contracts.flashloan_wrapper_maker.address()) + .from(auth_manager.clone()) + .send() + .await + .expect("failed to add flashloan wrapper"); + self.contracts + .gp_authenticator + .add_solver(self.contracts.flashloan_wrapper_aave.address()) + .from(auth_manager) + .send() + .await + .expect("failed to add flashloan wrapper"); + solvers } diff --git a/crates/e2e/src/setup/onchain_components/safe.rs b/crates/e2e/src/setup/onchain_components/safe.rs index efac9c9127..95ce981315 100644 --- a/crates/e2e/src/setup/onchain_components/safe.rs +++ b/crates/e2e/src/setup/onchain_components/safe.rs @@ -136,6 +136,11 @@ impl Safe { self.contract.address() } + /// Returns the next nonce to be used. + pub async fn nonce(&self) -> U256 { + self.contract.nonce().call().await.unwrap() + } + /// Returns a signed transaction ready for execution. pub fn sign_transaction( &self, diff --git a/crates/e2e/tests/e2e/flashloans.rs b/crates/e2e/tests/e2e/flashloans.rs new file mode 100644 index 0000000000..53583bfcf6 --- /dev/null +++ b/crates/e2e/tests/e2e/flashloans.rs @@ -0,0 +1,284 @@ +use { + contracts::{ERC20, IAavePool}, + e2e::{ + nodes::forked_node::ForkedNodeApi, + setup::{ + OnchainComponents, + Services, + TIMEOUT, + run_forked_test_with_block_number, + safe::Safe, + to_wei, + to_wei_with_exp, + wait_for_condition, + }, + tx, + }, + ethcontract::{H160, U256}, + ethrpc::Web3, + model::{ + order::{OrderCreation, OrderCreationAppData, OrderKind}, + signature::{Signature, hashed_eip712_message}, + }, + shared::{addr, conversions::U256Ext}, + std::time::Duration, +}; + +#[tokio::test] +#[ignore] +async fn forked_node_mainnet_repay_debt_with_collateral_of_safe() { + run_forked_test_with_block_number( + forked_mainnet_repay_debt_with_collateral_of_safe, + std::env::var("FORK_URL_MAINNET") + .expect("FORK_URL_MAINNET must be set to run forked tests"), + 21874126, + ) + .await; +} + +// Tests the rough flow of how a safe that took out a loan on AAVE +// could repay it using its own collateral fronted by a flashloan. +async fn forked_mainnet_repay_debt_with_collateral_of_safe(web3: Web3) { + let mut onchain = OnchainComponents::deployed(web3.clone()).await; + let forked_node_api = web3.api::>(); + + let [solver] = onchain.make_solvers_forked(to_wei(1)).await; + let [trader] = onchain.make_accounts(to_wei(1)).await; + let trader = Safe::deploy(trader.clone(), &web3).await; + + // AAVE token tracking how much USDC is deposited by a user + let ausdc = addr!("98c23e9d8f34fefb1b7bd6a91b7ff122f4e16f5c"); + let weth = &onchain.contracts().weth; + let settlement = &onchain.contracts().gp_settlement; + + // transfer some USDC from a whale to our trader + let usdc = ERC20::at(&web3, addr!("a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48")); + let usdc_whale_mainnet = addr!("28c6c06298d514db089934071355e5743bf21d60"); + let usdc_whale = forked_node_api + .impersonate(&usdc_whale_mainnet) + .await + .unwrap(); + + // transfer $50K + 1 atom to the safe (50K for the test and 1 atom for passing + // the minimum balance test before placing the order) + let collateral_amount = to_wei_with_exp(50_000, 6); + tx!( + usdc_whale, + usdc.transfer(trader.address(), collateral_amount + U256::from(1)) + ); + // approve vault relayer to take safe's sell tokens + trader + .exec_call(usdc.approve(onchain.contracts().allowance, collateral_amount)) + .await; + + // Approve AAVE to take the collateral + let aave_pool = IAavePool::at(&web3, addr!("87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2")); + trader + .exec_call(usdc.approve(aave_pool.address(), collateral_amount)) + .await; + + // Deposit collateral + trader + .exec_call(aave_pool.supply( + usdc.address(), // token + collateral_amount, // amount + trader.address(), // on_behalf + 0, // referral code + )) + .await; + assert!(balance(&web3, trader.address(), ausdc).await >= collateral_amount); + + tracing::info!("wait a bit to make `borrow()` call work"); + tokio::time::sleep(Duration::from_secs(1)).await; + + // Borrow 1 WETH against the collateral + let flashloan_amount = to_wei(1); + trader + .exec_call(aave_pool.borrow( + onchain.contracts().weth.address(), // borrowed token + flashloan_amount, // borrowed amount + 2.into(), // variable interest rate mode + 0, // referral code + trader.address(), // on_behalf + )) + .await; + + // allow aave pool to take back borrowed WETH on `repay()` + // could be replaced with `permit` pre-hook or `repayWithPermit()` for + // borrowed tokens that support `permit` + trader + .exec_call( + onchain + .contracts() + .weth + .approve(aave_pool.address(), to_wei(1)), + ) + .await; + + let current_safe_nonce = trader.nonce().await; + + // Build appdata that does: + // 1. take out a 1 WETH flashloan for the trader (flashloan hint) + // 2. repay the 1 WETH debt to unlock trader's collateral (1st pre-hook) + // 3. withdraw the collateral it can be sold for WETH (2nd pre-hook) + let app_data = { + let repay_tx = trader.sign_transaction( + aave_pool.address(), + aave_pool + .repay( + onchain.contracts().weth.address(), + to_wei(1), + 2.into(), + trader.address(), + ) + .tx + .data + .unwrap() + .0 + .clone(), + current_safe_nonce, + ); + let withdraw_tx = trader.sign_transaction( + aave_pool.address(), + aave_pool + .withdraw(usdc.address(), collateral_amount, trader.address()) + .tx + .data + .unwrap() + .0 + .clone(), + current_safe_nonce + U256::from(1), + ); + let app_data = format!( + r#"{{ + "metadata": {{ + "flashloan": {{ + "lender": "0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2", + "token": "{:?}", + "amount": "{:?}" + }}, + "hooks": {{ + "pre": [ + {{ + "target": "{:?}", + "value": "0", + "callData": "0x{}", + "gasLimit": "1000000" + }}, + {{ + "target": "{:?}", + "value": "0", + "callData": "0x{}", + "gasLimit": "1000000" + }} + ], + "post": [] + }}, + "signer": "{:?}" + }} + }}"#, + // flashloan + onchain.contracts().weth.address(), + // take out a loan that's bigger than we originally borrowed + flashloan_amount, + // 1st pre-hook + trader.address(), + hex::encode(&repay_tx.tx.data.unwrap().0), + // 2nd pre-hook + // ~200K gas + trader.address(), + hex::encode(&withdraw_tx.tx.data.unwrap().0), + // signer + trader.address(), + ); + + OrderCreationAppData::Full { + full: app_data.to_string(), + } + }; + + // pay some extra for the flashloan fee + let fee_bps = aave_pool.flashloan_premium_total().call().await.unwrap(); + let flashloan_fee = (flashloan_amount * U256::from(fee_bps)).ceil_div(&10_000.into()); + let mut order = OrderCreation { + sell_token: usdc.address(), + sell_amount: collateral_amount, + buy_token: onchain.contracts().weth.address(), + buy_amount: flashloan_amount + flashloan_fee, + valid_to: model::time::now_in_epoch_seconds() + 300, + kind: OrderKind::Buy, + app_data, + partially_fillable: false, + // receiver is always the settlement contract because the driver takes + // funds from the settlement contract to pay back the loan + receiver: Some(onchain.contracts().gp_settlement.address()), + ..Default::default() + }; + order.signature = Signature::Eip1271(trader.sign_message(&hashed_eip712_message( + &onchain.contracts().domain_separator, + &order.data().hash_struct(), + ))); + + tracing::info!("Removing all USDC and WETH from settlement contract for easier accounting"); + { + let settlement = forked_node_api + .impersonate(&settlement.address()) + .await + .unwrap(); + let amount = balance(&web3, settlement.address(), weth.address()).await; + tx!(settlement, weth.transfer(H160([1; 20]), amount,)); + let amount = balance(&web3, settlement.address(), weth.address()).await; + assert_eq!(amount, 0.into()); + + let amount = balance(&web3, settlement.address(), usdc.address()).await; + tx!(settlement, usdc.transfer(H160([1; 20]), amount,)); + let amount = balance(&web3, settlement.address(), weth.address()).await; + assert_eq!(amount, 0.into()); + } + + let services = Services::new(&onchain).await; + services.start_protocol(solver).await; + + assert_eq!( + balance(&web3, trader.address(), usdc.address()).await, + 1.into() + ); + tracing::info!("trader just has 1 atom of USDC before placing order"); + + let uid = services.create_order(&order).await.unwrap(); + tracing::info!(?uid, "placed order"); + + // Drive solution + tracing::info!("Waiting for trade to get indexed."); + wait_for_condition(TIMEOUT, || async { + onchain.mint_block().await; + let executed_fee = services + .get_order(&uid) + .await + .unwrap() + .metadata + .executed_fee; + executed_fee > 0.into() + }) + .await + .unwrap(); + + // Because the trader sold some of their collateral to repay their debt + // (~3000 USDC for ~1 WETH) they have that much less `USDC` compared to + // the original collateral. + let trader_usdc = balance(&web3, trader.address(), usdc.address()).await; + assert!(trader_usdc > to_wei_with_exp(47_000, 6)); + tracing::info!("trader got majority of collateral back"); + + let settlement_weth = balance(&web3, settlement.address(), weth.address()).await; + assert!(settlement_weth < 100_000_000u128.into()); + tracing::info!("settlement contract only has dust amounts of WETH"); + + assert!(balance(&web3, trader.address(), ausdc).await < 10_000.into()); + tracing::info!("trader only has dust of aUSDC"); +} + +async fn balance(web3: &Web3, owner: H160, token: H160) -> U256 { + let token = ERC20::at(web3, token); + token.balance_of(owner).call().await.unwrap() +} diff --git a/crates/e2e/tests/e2e/main.rs b/crates/e2e/tests/e2e/main.rs index 5ce4fd3f3f..925a1138e9 100644 --- a/crates/e2e/tests/e2e/main.rs +++ b/crates/e2e/tests/e2e/main.rs @@ -13,6 +13,7 @@ mod database; mod eth_integration; mod eth_safe; mod ethflow; +mod flashloans; mod hooks; mod jit_orders; mod limit_orders; From d9959d9cdd0e89e56bf9442e9c4cc37726c29568 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Strug?= <47604705+mstrug@users.noreply.github.com> Date: Fri, 7 Mar 2025 13:50:21 +0100 Subject: [PATCH 2/3] Added `QuoteMetadata` to `/orders/uid` request (#3222) # Description Exposing `Quote` data in the orderbook API on `/orders/uid` endpoint. # Changes This is a follow up of PR #3124 which added storing in the database of some quote information (like interactions, jit orders and pre interactions) for auctions analysis in the order context. Current PR exposes these information (plus the whole quote data) through API, so any interested party can easily access this data. - Added new type `OrderQuote` - Added new field `quote` to the `OrderMetadata` - Removed `OrderWithQuote` type as the quote is already inside `Order` struct (through `metadata` field) ## How to test Updated two tests which validates if proper quote metadata is returned. Current e2e tests. Tested locally on playground that order quote is returned through API. --------- Co-authored-by: ilya --- crates/database/src/orders.rs | 127 ++++++---- crates/model/Cargo.toml | 2 +- crates/model/src/order.rs | 25 ++ crates/orderbook/src/database/orders.rs | 286 ++++++++-------------- crates/orderbook/src/orderbook.rs | 98 +++----- crates/shared/src/db_order_conversions.rs | 18 ++ crates/shared/src/order_quoting.rs | 20 ++ crates/shared/src/order_validation.rs | 118 ++++++++- 8 files changed, 400 insertions(+), 294 deletions(-) diff --git a/crates/database/src/orders.rs b/crates/database/src/orders.rs index fd0d958ce3..78193facc4 100644 --- a/crates/database/src/orders.rs +++ b/crates/database/src/orders.rs @@ -494,6 +494,17 @@ pub struct FullOrder { pub full_app_data: Option>, } +impl FullOrder { + pub fn valid_to(&self) -> i64 { + if let Some((_, valid_to)) = self.ethflow_data { + // For ethflow orders, we always return the user valid_to, + // as the Eip1271 valid to is u32::max + return valid_to; + } + self.valid_to + } +} + #[derive(Debug, sqlx::FromRow)] pub struct FullOrderWithQuote { #[sqlx(flatten)] @@ -508,14 +519,41 @@ pub struct FullOrderWithQuote { pub solver: Option
, } -impl FullOrder { - pub fn valid_to(&self) -> i64 { - if let Some((_, valid_to)) = self.ethflow_data { - // For ethflow orders, we always return the user valid_to, - // as the Eip1271 valid to is u32::max - return valid_to; - } - self.valid_to +impl FullOrderWithQuote { + pub fn into_order_and_quote(self) -> (FullOrder, Option) { + let quote = match ( + self.quote_buy_amount, + self.quote_sell_amount, + self.quote_gas_amount, + self.quote_gas_price, + self.quote_sell_token_price, + self.quote_verified, + self.quote_metadata, + self.solver, + ) { + ( + Some(buy_amount), + Some(sell_amount), + Some(gas_amount), + Some(gas_price), + Some(sell_token_price), + Some(verified), + Some(metadata), + Some(solver), + ) => Some(Quote { + order_uid: self.full_order.uid, + gas_amount, + gas_price, + sell_token_price, + sell_amount, + buy_amount, + solver, + verified, + metadata, + }), + _ => None, + }; + (self.full_order, quote) } } @@ -574,19 +612,6 @@ COALESCE((SELECT executed_fee_token FROM order_execution oe WHERE oe.order_uid = pub const FROM: &str = "orders o"; -pub async fn single_full_order( - ex: &mut PgConnection, - uid: &OrderUid, -) -> Result, sqlx::Error> { - #[rustfmt::skip] - const QUERY: &str = const_format::concatcp!( -"SELECT ", SELECT, -" FROM ", FROM, -" WHERE o.uid = $1 ", - ); - sqlx::query_as(QUERY).bind(uid).fetch_optional(ex).await -} - pub async fn single_full_order_with_quote( ex: &mut PgConnection, uid: &OrderUid, @@ -861,10 +886,11 @@ mod tests { let order_ = read_order(&mut db, &order.uid).await.unwrap().unwrap(); assert_eq!(order, order_); - let full_order = single_full_order(&mut db, &order.uid) + let full_order = single_full_order_with_quote(&mut db, &order.uid) .await .unwrap() - .unwrap(); + .unwrap() + .full_order; assert_eq!(order.uid, full_order.uid); assert_eq!(order.owner, full_order.owner); @@ -923,10 +949,11 @@ mod tests { .await .unwrap(); insert_order(&mut db, &order).await.unwrap(); - let order_ = single_full_order(&mut db, &order.uid) + let order_ = single_full_order_with_quote(&mut db, &order.uid) .await .unwrap() - .unwrap(); + .unwrap() + .full_order; assert_eq!(Some(sender), order_.onchain_user); } @@ -958,10 +985,11 @@ mod tests { ) .await .unwrap(); - let order_ = single_full_order(&mut db, &order.uid) + let order_ = single_full_order_with_quote(&mut db, &order.uid) .await .unwrap() - .unwrap(); + .unwrap() + .full_order; assert_eq!( Some((Some(Default::default()), user_valid_to)), order_.ethflow_data @@ -994,10 +1022,11 @@ mod tests { insert_or_overwrite_interaction(&mut db, &post_interaction_1, &order.uid) .await .unwrap(); - let order_ = single_full_order(&mut db, &order.uid) + let order_ = single_full_order_with_quote(&mut db, &order.uid) .await .unwrap() - .unwrap(); + .unwrap() + .full_order; assert_eq!( vec![ByteArray::default(), ByteArray([1; 20])], order_ @@ -1084,10 +1113,11 @@ mod tests { insert_or_overwrite_interaction(&mut db, &pre_interaction_1, &order.uid) .await .unwrap(); - let order_ = single_full_order(&mut db, &order.uid) + let order_ = single_full_order_with_quote(&mut db, &order.uid) .await .unwrap() - .unwrap(); + .unwrap() + .full_order; assert_eq!( vec![ByteArray::default(), ByteArray([1; 20])], order_ @@ -1458,10 +1488,11 @@ mod tests { .unwrap(); } - let order = single_full_order(&mut db, &order.uid) + let order = single_full_order_with_quote(&mut db, &order.uid) .await .unwrap() - .unwrap(); + .unwrap() + .full_order; let expected_sell_amount_including_fees: BigInt = sell_amount_including_fee * 16; assert!(expected_sell_amount_including_fees > u256_max); @@ -1558,18 +1589,20 @@ mod tests { ..Default::default() }; insert_order(&mut db, &order).await.unwrap(); - let result = single_full_order(&mut db, &order.uid) + let result = single_full_order_with_quote(&mut db, &order.uid) .await .unwrap() - .unwrap(); + .unwrap() + .full_order; assert!(!result.invalidated); insert_onchain_invalidation(&mut db, &EventIndex::default(), &order.uid) .await .unwrap(); - let result = single_full_order(&mut db, &order.uid) + let result = single_full_order_with_quote(&mut db, &order.uid) .await .unwrap() - .unwrap(); + .unwrap() + .full_order; assert!(result.invalidated); } @@ -1907,10 +1940,11 @@ mod tests { .unwrap(); let fee: BigDecimal = 0.into(); - let order = single_full_order(&mut db, &order_uid) + let order = single_full_order_with_quote(&mut db, &order_uid) .await .unwrap() - .unwrap(); + .unwrap() + .full_order; assert_eq!(order.executed_fee, fee); let fee: BigDecimal = 1.into(); @@ -1928,10 +1962,11 @@ mod tests { .await .unwrap(); - let order = single_full_order(&mut db, &order_uid) + let order = single_full_order_with_quote(&mut db, &order_uid) .await .unwrap() - .unwrap(); + .unwrap() + .full_order; assert_eq!(order.executed_fee, fee); } @@ -1946,19 +1981,21 @@ mod tests { ..Default::default() }; insert_order(&mut db, &order).await.unwrap(); - let full_order = single_full_order(&mut db, &order.uid) + let full_order = single_full_order_with_quote(&mut db, &order.uid) .await .unwrap() - .unwrap(); + .unwrap() + .full_order; assert!(full_order.full_app_data.is_none()); let full_app_data = vec![0u8, 1, 2]; crate::app_data::insert(&mut db, &order.app_data, &full_app_data) .await .unwrap(); - let full_order = single_full_order(&mut db, &order.uid) + let full_order = single_full_order_with_quote(&mut db, &order.uid) .await .unwrap() - .unwrap(); + .unwrap() + .full_order; assert_eq!(full_order.full_app_data, Some(full_app_data)); } diff --git a/crates/model/Cargo.toml b/crates/model/Cargo.toml index ed175fb813..13b1dc670f 100644 --- a/crates/model/Cargo.toml +++ b/crates/model/Cargo.toml @@ -22,12 +22,12 @@ num = { workspace = true } primitive-types = { workspace = true } secp256k1 = { workspace = true } serde = { workspace = true } +serde_json = { workspace = true } serde_with = { workspace = true } strum = { workspace = true } web3 = { workspace = true, features = ["signing"] } [dev-dependencies] -serde_json = { workspace = true } maplit = { workspace = true } testlib = { workspace = true } diff --git a/crates/model/src/order.rs b/crates/model/src/order.rs index 1bd37863c0..b029c9ed0b 100644 --- a/crates/model/src/order.rs +++ b/crates/model/src/order.rs @@ -11,6 +11,7 @@ use { }, anyhow::{Result, anyhow}, app_data::{AppDataHash, hash_full_app_data}, + bigdecimal::BigDecimal, chrono::{DateTime, offset::Utc}, derive_more::Debug as DeriveDebug, hex_literal::hex, @@ -691,6 +692,10 @@ pub struct OrderMetadata { /// Full app data that `OrderData::app_data` is a hash of. Can be None if /// the backend doesn't know about the full app data. pub full_app_data: Option, + /// If the order was created with a quote, then this field contains that + /// quote data for reference. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub quote: Option, } // uid as 56 bytes: 32 for orderDigest, 20 for ownerAddress and 4 for validTo @@ -964,6 +969,26 @@ impl BuyTokenDestination { } } +/// A quote from which particular order was created. +#[serde_as] +#[derive(Eq, PartialEq, Clone, Default, Deserialize, Serialize, DeriveDebug)] +#[serde(rename_all = "camelCase")] +pub struct OrderQuote { + #[serde_as(as = "DisplayFromStr")] + pub gas_amount: BigDecimal, + #[serde_as(as = "DisplayFromStr")] + pub gas_price: BigDecimal, + #[serde_as(as = "DisplayFromStr")] + pub sell_token_price: BigDecimal, + #[serde_as(as = "HexOrDecimalU256")] + pub sell_amount: U256, + #[serde_as(as = "HexOrDecimalU256")] + pub buy_amount: U256, + pub solver: H160, + pub verified: bool, + pub metadata: serde_json::Value, +} + #[cfg(test)] mod tests { use { diff --git a/crates/orderbook/src/database/orders.rs b/crates/orderbook/src/database/orders.rs index 3a577ebc44..e6bf123e16 100644 --- a/crates/orderbook/src/database/orders.rs +++ b/crates/orderbook/src/database/orders.rs @@ -1,9 +1,10 @@ use { super::Postgres, - crate::{dto::TokenMetadata, orderbook::AddOrderError}, + crate::dto::TokenMetadata, anyhow::{Context as _, Result}, app_data::AppDataHash, async_trait::async_trait, + bigdecimal::ToPrimitive, chrono::{DateTime, Utc}, database::{ byte_array::ByteArray, @@ -40,13 +41,13 @@ use { order_class_into, order_kind_from, order_kind_into, + order_quote_into_model, sell_token_source_from, sell_token_source_into, signing_scheme_from, signing_scheme_into, }, fee::FeeParameters, - order_quoting::Quote, order_validation::{Amounts, LimitOrderCounting, is_order_outside_market_price}, }, sqlx::{Connection, PgConnection, types::BigDecimal}, @@ -56,18 +57,15 @@ use { #[cfg_attr(test, mockall::automock)] #[async_trait::async_trait] pub trait OrderStoring: Send + Sync { - async fn insert_order(&self, order: &Order, quote: Option) - -> Result<(), InsertionError>; + async fn insert_order(&self, order: &Order) -> Result<(), InsertionError>; async fn cancel_orders(&self, order_uids: Vec, now: DateTime) -> Result<()>; async fn cancel_order(&self, order_uid: &OrderUid, now: DateTime) -> Result<()>; async fn replace_order( &self, old_order: &OrderUid, new_order: &Order, - new_quote: Option, ) -> Result<(), InsertionError>; async fn orders_for_tx(&self, tx_hash: &H256) -> Result>; - async fn single_order(&self, uid: &OrderUid) -> Result>; /// All orders of a single user ordered by creation date descending (newest /// orders first). async fn user_orders( @@ -77,39 +75,7 @@ pub trait OrderStoring: Send + Sync { limit: Option, ) -> Result>; async fn latest_order_event(&self, order_uid: &OrderUid) -> Result>; - async fn single_order_with_quote(&self, uid: &OrderUid) -> Result>; -} - -pub struct OrderWithQuote { - pub order: Order, - pub quote: Option, -} - -impl OrderWithQuote { - pub fn try_new(order: Order, quote: Option) -> Result { - Ok(Self { - quote: quote - .map(|quote| { - Ok::(orders::Quote { - order_uid: ByteArray(order.metadata.uid.0), - gas_amount: quote.data.fee_parameters.gas_amount, - gas_price: quote.data.fee_parameters.gas_price, - sell_token_price: quote.data.fee_parameters.sell_token_price, - sell_amount: u256_to_big_decimal("e.sell_amount), - buy_amount: u256_to_big_decimal("e.buy_amount), - solver: ByteArray(quote.data.solver.0), - verified: quote.data.verified, - metadata: quote - .data - .metadata - .try_into() - .map_err(AddOrderError::MetadataSerializationFailed)?, - }) - }) - .transpose()?, - order, - }) - } + async fn single_order(&self, uid: &OrderUid) -> Result>; } #[derive(Debug)] @@ -187,7 +153,7 @@ async fn insert_order(order: &Order, ex: &mut PgConnection) -> Result<(), Insert ) .collect::>(); - let order = database::orders::Order { + let db_order = database::orders::Order { uid: order_uid, owner: ByteArray(order.metadata.owner.0), creation_timestamp: order.metadata.creation_date, @@ -210,7 +176,7 @@ async fn insert_order(order: &Order, ex: &mut PgConnection) -> Result<(), Insert cancellation_timestamp: None, }; - database::orders::insert_order(ex, &order) + database::orders::insert_order(ex, &db_order) .await .map_err(|err| { if database::orders::is_duplicate_record_error(&err) { @@ -219,46 +185,34 @@ async fn insert_order(order: &Order, ex: &mut PgConnection) -> Result<(), Insert InsertionError::DbError(err) } })?; - database::orders::insert_interactions(ex, &order.uid, &interactions) + database::orders::insert_interactions(ex, &db_order.uid, &interactions) .await .map_err(InsertionError::DbError)?; - Ok(()) -} + if let Some(quote) = order.metadata.quote.as_ref() { + let db_quote = database::orders::Quote { + order_uid, + // safe to unwrap as these values were converted from f64 previously + gas_amount: quote.gas_amount.to_f64().unwrap(), + gas_price: quote.gas_price.to_f64().unwrap(), + sell_token_price: quote.sell_token_price.to_f64().unwrap(), + sell_amount: u256_to_big_decimal("e.sell_amount), + buy_amount: u256_to_big_decimal("e.buy_amount), + solver: ByteArray(quote.solver.0), + verified: quote.verified, + metadata: quote.metadata.clone(), + }; + database::orders::insert_quote(ex, &db_quote) + .await + .map_err(InsertionError::DbError)?; + } -async fn insert_quote( - uid: &OrderUid, - quote: &Quote, - ex: &mut PgConnection, -) -> Result<(), InsertionError> { - let quote = database::orders::Quote { - order_uid: ByteArray(uid.0), - gas_amount: quote.data.fee_parameters.gas_amount, - gas_price: quote.data.fee_parameters.gas_price, - sell_token_price: quote.data.fee_parameters.sell_token_price, - sell_amount: u256_to_big_decimal("e.sell_amount), - buy_amount: u256_to_big_decimal("e.buy_amount), - solver: ByteArray(quote.data.solver.0), - verified: quote.data.verified, - metadata: quote - .data - .metadata - .clone() - .try_into() - .map_err(InsertionError::MetadataSerializationFailed)?, - }; - database::orders::insert_quote(ex, "e) - .await - .map_err(InsertionError::DbError) + Ok(()) } #[async_trait::async_trait] impl OrderStoring for Postgres { - async fn insert_order( - &self, - order: &Order, - quote: Option, - ) -> Result<(), InsertionError> { + async fn insert_order(&self, order: &Order) -> Result<(), InsertionError> { let _timer = super::Metrics::get() .database_queries .with_label_values(&["insert_order"]) @@ -269,9 +223,6 @@ impl OrderStoring for Postgres { let mut ex = connection.begin().await?; insert_order(&order, &mut ex).await?; - if let Some(quote) = quote { - insert_quote(&order.metadata.uid, "e, &mut ex).await?; - } Self::insert_order_app_data(&order, &mut ex).await?; ex.commit().await?; @@ -309,7 +260,6 @@ impl OrderStoring for Postgres { &self, old_order: &model::order::OrderUid, new_order: &model::order::Order, - new_quote: Option, ) -> anyhow::Result<(), super::orders::InsertionError> { let _timer = super::Metrics::get() .database_queries @@ -329,9 +279,6 @@ impl OrderStoring for Postgres { ) .await?; insert_order(&new_order, ex).await?; - if let Some(quote) = new_quote { - insert_quote(&new_order.metadata.uid, "e, ex).await?; - } Self::insert_order_app_data(&new_order, ex).await?; Ok(()) @@ -348,65 +295,23 @@ impl OrderStoring for Postgres { .start_timer(); let mut ex = self.pool.acquire().await?; - let order = match database::orders::single_full_order(&mut ex, &ByteArray(uid.0)).await? { - Some(order) => Some(order), + + match orders::single_full_order_with_quote(&mut ex, &ByteArray(uid.0)).await? { + Some(order_with_quote) => { + let (order, quote) = order_with_quote.into_order_and_quote(); + Some(full_order_with_quote_into_model_order( + order, + quote.as_ref(), + )) + } None => { // try to find the order in the JIT orders table - database::jit_orders::get_by_id(&mut ex, &ByteArray(uid.0)).await? + database::jit_orders::get_by_id(&mut ex, &ByteArray(uid.0)) + .await? + .map(full_order_into_model_order) } - }; - order.map(full_order_into_model_order).transpose() - } - - async fn single_order_with_quote(&self, uid: &OrderUid) -> Result> { - let _timer = super::Metrics::get() - .database_queries - .with_label_values(&["single_order_with_quote"]) - .start_timer(); - - let mut ex = self.pool.acquire().await?; - let order = orders::single_full_order_with_quote(&mut ex, &ByteArray(uid.0)).await?; - order - .map(|order_with_quote| { - let quote = match ( - order_with_quote.quote_buy_amount, - order_with_quote.quote_sell_amount, - order_with_quote.quote_gas_amount, - order_with_quote.quote_gas_price, - order_with_quote.quote_sell_token_price, - order_with_quote.quote_verified, - order_with_quote.quote_metadata, - order_with_quote.solver, - ) { - ( - Some(buy_amount), - Some(sell_amount), - Some(gas_amount), - Some(gas_price), - Some(sell_token_price), - Some(verified), - Some(metadata), - Some(solver), - ) => Some(orders::Quote { - order_uid: order_with_quote.full_order.uid, - gas_amount, - gas_price, - sell_token_price, - sell_amount, - buy_amount, - solver, - verified, - metadata, - }), - _ => None, - }; - - Ok(OrderWithQuote { - order: full_order_into_model_order(order_with_quote.full_order)?, - quote, - }) - }) - .transpose() + } + .transpose() } async fn orders_for_tx(&self, tx_hash: &H256) -> Result> { @@ -608,6 +513,14 @@ fn calculate_status(order: &FullOrder) -> OrderStatus { } fn full_order_into_model_order(order: FullOrder) -> Result { + full_order_with_quote_into_model_order(order, None) +} + +/// If quote is provided, then it is used to extract quote metadata field value. +fn full_order_with_quote_into_model_order( + order: FullOrder, + quote: Option<&orders::Quote>, +) -> Result { let status = calculate_status(&order); let pre_interactions = extract_interactions(&order, database::orders::ExecutionTime::Pre)?; let post_interactions = extract_interactions(&order, database::orders::ExecutionTime::Post)?; @@ -660,6 +573,7 @@ fn full_order_into_model_order(order: FullOrder) -> Result { .map(String::from_utf8) .transpose() .context("full app data isn't utf-8")?, + quote: quote.map(order_quote_into_model).transpose()?, }; let data = OrderData { sell_token: H160(order.sell_token.0), @@ -726,7 +640,7 @@ mod tests { signature::{Signature, SigningScheme}, }, primitive_types::U256, - shared::order_quoting::{QuoteData, QuoteMetadataV1}, + shared::order_quoting::{Quote, QuoteData, QuoteMetadataV1}, std::sync::atomic::{AtomicI64, Ordering}, }; @@ -963,7 +877,7 @@ mod tests { }, ..Default::default() }; - db.insert_order(&old_order, None).await.unwrap(); + db.insert_order(&old_order).await.unwrap(); let new_order = Order { data: OrderData { @@ -978,7 +892,7 @@ mod tests { }, ..Default::default() }; - db.replace_order(&old_order.metadata.uid, &new_order, None) + db.replace_order(&old_order.metadata.uid, &new_order) .await .unwrap(); @@ -1025,7 +939,7 @@ mod tests { }, ..Default::default() }; - db.insert_order(&old_order, None).await.unwrap(); + db.insert_order(&old_order).await.unwrap(); let new_order = Order { metadata: OrderMetadata { @@ -1036,11 +950,11 @@ mod tests { }, ..Default::default() }; - db.insert_order(&new_order, None).await.unwrap(); + db.insert_order(&new_order).await.unwrap(); // Attempt to replace an old order with one that already exists should fail. let err = db - .replace_order(&old_order.metadata.uid, &new_order, None) + .replace_order(&old_order.metadata.uid, &new_order) .await .unwrap_err(); assert!(matches!(err, InsertionError::DuplicatedRecord)); @@ -1073,7 +987,7 @@ mod tests { signature: Signature::default_with(SigningScheme::PreSign), ..Default::default() }; - db.insert_order(&order, None).await.unwrap(); + db.insert_order(&order).await.unwrap(); let order_status = || async { db.single_order(&order.metadata.uid) @@ -1160,9 +1074,9 @@ mod tests { } }; - db.insert_order(&order(1), None).await.unwrap(); - db.insert_order(&order(2), None).await.unwrap(); - db.insert_order(&order(3), None).await.unwrap(); + db.insert_order(&order(1)).await.unwrap(); + db.insert_order(&order(2)).await.unwrap(); + db.insert_order(&order(3)).await.unwrap(); assert_eq!(order_status(1).await, OrderStatus::Open); assert_eq!(order_status(2).await, OrderStatus::Open); @@ -1189,6 +1103,21 @@ mod tests { call_data: vec![byte; byte as _], }; + let quote = Quote { + id: Some(5), + sell_amount: U256::from(1), + buy_amount: U256::from(2), + data: QuoteData { + fee_parameters: FeeParameters { + sell_token_price: 2.5, + gas_amount: 0.01, + gas_price: 0.003, + }, + ..Default::default() + }, + ..Default::default() + }; + let uid = OrderUid([0x42; 56]); let order = Order { data: OrderData { @@ -1197,6 +1126,7 @@ mod tests { }, metadata: OrderMetadata { uid, + quote: Some(quote.try_to_model_order_quote().unwrap()), ..Default::default() }, interactions: Interactions { @@ -1206,28 +1136,14 @@ mod tests { ..Default::default() }; - let quote = Quote { - id: Some(5), - sell_amount: U256::from(1), - buy_amount: U256::from(2), - ..Default::default() - }; - db.insert_order(&order, Some(quote.clone())).await.unwrap(); - - let interactions = db.single_order(&uid).await.unwrap().unwrap().interactions; - assert_eq!(interactions, order.interactions); + db.insert_order(&order).await.unwrap(); - // Test `single_order_with_quote` - let single_order_with_quote = db.single_order_with_quote(&uid).await.unwrap().unwrap(); - assert_eq!(single_order_with_quote.order, order); + let single_order = db.single_order(&uid).await.unwrap().unwrap(); assert_eq!( - single_order_with_quote.quote.clone().unwrap().sell_amount, - u256_to_big_decimal("e.sell_amount) - ); - assert_eq!( - single_order_with_quote.quote.unwrap().buy_amount, - u256_to_big_decimal("e.buy_amount) + single_order.metadata.quote, + Some(quote.try_to_model_order_quote().unwrap()) ); + assert_eq!(single_order, order); } #[tokio::test] @@ -1236,19 +1152,6 @@ mod tests { let db = Postgres::try_new("postgresql://").unwrap(); database::clear_DANGER(&db.pool).await.unwrap(); - let uid = OrderUid([0x42; 56]); - let order = Order { - data: OrderData { - valid_to: u32::MAX, - ..Default::default() - }, - metadata: OrderMetadata { - uid, - ..Default::default() - }, - ..Default::default() - }; - let quote = Quote { id: Some(5), sell_amount: U256::from(1), @@ -1280,10 +1183,29 @@ mod tests { }, ..Default::default() }; - db.insert_order(&order, Some(quote)).await.unwrap(); - let single_order_with_quote = db.single_order_with_quote(&uid).await.unwrap().unwrap(); - assert_eq!(single_order_with_quote.order, order); - assert!(single_order_with_quote.quote.unwrap().verified); + let uid = OrderUid([0x42; 56]); + let order = Order { + data: OrderData { + valid_to: u32::MAX, + ..Default::default() + }, + metadata: OrderMetadata { + uid, + quote: Some(quote.try_to_model_order_quote().unwrap()), + ..Default::default() + }, + ..Default::default() + }; + + db.insert_order(&order).await.unwrap(); + + let single_order = db.single_order(&uid).await.unwrap().unwrap(); + + assert_eq!( + single_order.metadata.quote, + Some(quote.try_to_model_order_quote().unwrap()) + ); + assert_eq!(single_order, order); } } diff --git a/crates/orderbook/src/orderbook.rs b/crates/orderbook/src/orderbook.rs index b938a33e9a..63ac7c9e11 100644 --- a/crates/orderbook/src/orderbook.rs +++ b/crates/orderbook/src/orderbook.rs @@ -1,7 +1,7 @@ use { crate::{ database::{ - orders::{InsertionError, OrderStoring, OrderWithQuote}, + orders::{InsertionError, OrderStoring}, trades::{TradeFilter, TradeRetrieving}, }, dto, @@ -9,6 +9,7 @@ use { }, anyhow::{Context, Result}, app_data::{AppDataHash, Validator}, + bigdecimal::ToPrimitive, chrono::Utc, database::order_events::OrderEventLabel, ethcontract::H256, @@ -26,12 +27,10 @@ use { quote::QuoteId, solver_competition::{self, SolverCompetitionAPI}, }, - number::conversions::big_decimal_to_u256, observe::metrics::LivenessChecking, primitive_types::H160, shared::{ fee::FeeParameters, - order_quoting::Quote, order_validation::{ Amounts, OrderValidating, @@ -72,26 +71,27 @@ impl Metrics { .expect("unexpected error getting metrics instance") } - fn on_order_operation(order: &OrderWithQuote, operation: OrderOperation) { - let class = if order.quote.as_ref().is_some_and(|quote| { + fn on_order_operation(order: &Order, operation: OrderOperation) { + let class = if order.metadata.quote.as_ref().is_some_and(|quote| { // Check if the order at the submission time was "in market" !is_order_outside_market_price( &Amounts { - sell: order.order.data.sell_amount, - buy: order.order.data.buy_amount, - fee: order.order.data.fee_amount, + sell: order.data.sell_amount, + buy: order.data.buy_amount, + fee: order.data.fee_amount, }, &Amounts { - sell: big_decimal_to_u256("e.sell_amount).unwrap(), - buy: big_decimal_to_u256("e.buy_amount).unwrap(), + sell: quote.sell_amount, + buy: quote.buy_amount, fee: FeeParameters { - gas_amount: quote.gas_amount, - gas_price: quote.gas_price, - sell_token_price: quote.sell_token_price, + // safe to unwrap as these values were converted from f64 previously + gas_amount: quote.gas_amount.to_f64().unwrap(), + gas_price: quote.gas_price.to_f64().unwrap(), + sell_token_price: quote.sell_token_price.to_f64().unwrap(), } .fee(), }, - order.order.data.kind, + order.data.kind, ) }) { OrderClass::Market @@ -239,7 +239,7 @@ impl Orderbook { .get_replaced_order(&payload, full_app_data_override.as_deref()) .await?; - let (order, quote) = self + let (order, quote_id) = self .order_validator .validate_and_construct_order( payload, @@ -249,24 +249,20 @@ impl Orderbook { ) .await?; + let order_uid = order.metadata.uid; + // Check if it has to replace an existing order if let Some(old_order) = replaced_order { - self.replace_order(order, old_order, quote).await + self.replace_order(order, old_order).await? } else { - let quote_id = quote.as_ref().and_then(|quote| quote.id); - let order_uid = order.metadata.uid; - self.database - .insert_order(&order, quote.clone()) + .insert_order(&order) .await .map_err(|err| AddOrderError::from_insertion(err, &order))?; - Metrics::on_order_operation( - &OrderWithQuote::try_new(order, quote)?, - OrderOperation::Created, - ); - - Ok((order_uid, quote_id)) + Metrics::on_order_operation(&order, OrderOperation::Created); } + + Ok((order_uid, quote_id)) } /// Finds an order for cancellation. @@ -275,16 +271,16 @@ impl Orderbook { async fn find_order_for_cancellation( &self, order_uid: &OrderUid, - ) -> Result { + ) -> Result { let order = self .database - .single_order_with_quote(order_uid) + .single_order(order_uid) .await? .ok_or(OrderCancellationError::OrderNotFound)?; - match order.order.metadata.status { + match order.metadata.status { OrderStatus::PresignaturePending => return Err(OrderCancellationError::OnChainOrder), - OrderStatus::Open if !order.order.signature.scheme().is_ecdsa_scheme() => { + OrderStatus::Open if !order.signature.scheme().is_ecdsa_scheme() => { return Err(OrderCancellationError::OnChainOrder); } OrderStatus::Fulfilled => return Err(OrderCancellationError::OrderFullyExecuted), @@ -309,10 +305,7 @@ impl Orderbook { let signer = cancellation .validate(&self.domain_separator) .map_err(|_| OrderCancellationError::InvalidSignature)?; - if orders - .iter() - .any(|order| signer != order.order.metadata.owner) - { + if orders.iter().any(|order| signer != order.metadata.owner) { return Err(OrderCancellationError::WrongOwner); }; @@ -323,7 +316,7 @@ impl Orderbook { .await?; for order in &orders { - tracing::debug!(order_uid =% order.order.metadata.uid, "order cancelled"); + tracing::debug!(order_uid =% order.metadata.uid, "order cancelled"); Metrics::on_order_operation(order, OrderOperation::Cancelled); } @@ -342,17 +335,17 @@ impl Orderbook { let signer = cancellation .validate(&self.domain_separator) .map_err(|_| OrderCancellationError::InvalidSignature)?; - if signer != order.order.metadata.owner { + if signer != order.metadata.owner { return Err(OrderCancellationError::WrongOwner); }; // order is already known to exist in DB at this point, and signer is // known to be correct! self.database - .cancel_order(&order.order.metadata.uid, Utc::now()) + .cancel_order(&order.metadata.uid, Utc::now()) .await?; - tracing::debug!(order_uid =% order.order.metadata.uid, "order cancelled"); + tracing::debug!(order_uid =% order.metadata.uid, "order cancelled"); Metrics::on_order_operation(&order, OrderOperation::Cancelled); Ok(()) @@ -362,7 +355,7 @@ impl Orderbook { &self, new_order: &OrderCreation, app_data_override: Option<&str>, - ) -> Result, AddOrderError> { + ) -> Result, AddOrderError> { let full_app_data = match &new_order.app_data { OrderCreationAppData::Hash { .. } => app_data_override, OrderCreationAppData::Both { full, .. } | OrderCreationAppData::Full { full } => { @@ -389,9 +382,8 @@ impl Orderbook { pub async fn replace_order( &self, validated_new_order: Order, - old_order: OrderWithQuote, - quote: Option, - ) -> Result<(OrderUid, Option), AddOrderError> { + old_order: Order, + ) -> Result<(), AddOrderError> { // Replacement order signatures need to be validated meaning we cannot // accept `PreSign` orders, otherwise anyone can cancel a user order by // submitting a `PreSign` order on someone's behalf. @@ -403,28 +395,18 @@ impl Orderbook { // Verify that the new order is a valid replacement order by checking // that both the old and new orders have the same signer. - if validated_new_order.metadata.owner != old_order.order.metadata.owner { + if validated_new_order.metadata.owner != old_order.metadata.owner { return Err(AddOrderError::InvalidReplacement); } - let quote_id = quote.as_ref().and_then(|quote| quote.id); - let order_uid = validated_new_order.metadata.uid; - self.database - .replace_order( - &old_order.order.metadata.uid, - &validated_new_order, - quote.clone(), - ) + .replace_order(&old_order.metadata.uid, &validated_new_order) .await .map_err(|err| AddOrderError::from_insertion(err, &validated_new_order))?; Metrics::on_order_operation(&old_order, OrderOperation::Cancelled); - Metrics::on_order_operation( - &OrderWithQuote::try_new(validated_new_order, quote)?, - OrderOperation::Created, - ); + Metrics::on_order_operation(&validated_new_order, OrderOperation::Created); - Ok((order_uid, quote_id)) + Ok(()) } pub async fn get_order(&self, uid: &OrderUid) -> Result> { @@ -579,7 +561,7 @@ mod tests { let old_order = old_order.clone(); move |_| Ok(Some(old_order.clone())) }); - database.expect_replace_order().returning(|_, _, _| Ok(())); + database.expect_replace_order().returning(|_, _| Ok(())); let mut order_validator = MockOrderValidating::new(); order_validator @@ -602,7 +584,7 @@ mod tests { let database = crate::database::Postgres::try_new("postgresql://").unwrap(); database::clear_DANGER(&database.pool).await.unwrap(); - database.insert_order(&old_order, None).await.unwrap(); + database.insert_order(&old_order).await.unwrap(); let app_data = Arc::new(crate::app_data::Registry::new( Validator::new(8192), database.clone(), diff --git a/crates/shared/src/db_order_conversions.rs b/crates/shared/src/db_order_conversions.rs index d3b33b104c..704723aba5 100644 --- a/crates/shared/src/db_order_conversions.rs +++ b/crates/shared/src/db_order_conversions.rs @@ -1,6 +1,7 @@ use { anyhow::{Context, Result}, app_data::AppDataHash, + bigdecimal::BigDecimal, database::{ onchain_broadcasted_orders::OnchainOrderPlacementError as DbOnchainOrderPlacementError, orders::{ @@ -27,12 +28,14 @@ use { OrderData, OrderKind, OrderMetadata, + OrderQuote, OrderStatus, OrderUid, SellTokenSource, }, signature::{Signature, SigningScheme}, }, + num::FromPrimitive, number::conversions::{big_decimal_to_big_uint, big_decimal_to_u256}, }; @@ -95,6 +98,7 @@ pub fn full_order_into_model_order(order: database::orders::FullOrder) -> Result .map(String::from_utf8) .transpose() .context("full app data isn't utf-8")?, + quote: None, }; let data = OrderData { sell_token: H160(order.sell_token.0), @@ -123,6 +127,20 @@ pub fn full_order_into_model_order(order: database::orders::FullOrder) -> Result }) } +pub fn order_quote_into_model(quote: &database::orders::Quote) -> Result { + Ok(OrderQuote { + gas_amount: BigDecimal::from_f64(quote.gas_amount).context("gas_amount is not U256")?, + gas_price: BigDecimal::from_f64(quote.gas_price).context("gas_price is not U256")?, + sell_token_price: BigDecimal::from_f64(quote.sell_token_price) + .context("gas_price is not U256")?, + sell_amount: big_decimal_to_u256("e.sell_amount).context("sell_amount is not U256")?, + buy_amount: big_decimal_to_u256("e.buy_amount).context("buy_amount is not U256")?, + solver: H160(quote.solver.0), + verified: quote.verified, + metadata: quote.metadata.clone(), + }) +} + pub fn extract_interactions( order: &FullOrderDb, execution: ExecutionTime, diff --git a/crates/shared/src/order_quoting.rs b/crates/shared/src/order_quoting.rs index c0a1e5852a..8eced6eb3d 100644 --- a/crates/shared/src/order_quoting.rs +++ b/crates/shared/src/order_quoting.rs @@ -24,6 +24,7 @@ use { order::{OrderClass, OrderKind}, quote::{OrderQuoteRequest, OrderQuoteSide, QuoteId, QuoteSigningScheme, SellAmount}, }, + num::FromPrimitive, number::conversions::big_decimal_to_u256, std::sync::Arc, thiserror::Error, @@ -138,6 +139,25 @@ impl Quote { self } + + /// Converts this Quote to model OrderQuote. + pub fn try_to_model_order_quote(&self) -> Result { + Ok(model::order::OrderQuote { + gas_amount: bigdecimal::BigDecimal::from_f64(self.data.fee_parameters.gas_amount) + .context("gas amount is not a valid BigDecimal")?, + gas_price: bigdecimal::BigDecimal::from_f64(self.data.fee_parameters.gas_price) + .context("gas price is not a valid BigDecimal")?, + sell_token_price: bigdecimal::BigDecimal::from_f64( + self.data.fee_parameters.sell_token_price, + ) + .context("sell token price is not a valid BigDecimal")?, + sell_amount: self.sell_amount, + buy_amount: self.buy_amount, + solver: self.data.solver, + verified: self.data.verified, + metadata: serde_json::to_value(&self.data.metadata)?, + }) + } } /// Detailed data for a computed order quote. diff --git a/crates/shared/src/order_validation.rs b/crates/shared/src/order_validation.rs index b853bb1c6e..372d6db982 100644 --- a/crates/shared/src/order_validation.rs +++ b/crates/shared/src/order_validation.rs @@ -37,7 +37,7 @@ use { SellTokenSource, VerificationError, }, - quote::{OrderQuoteSide, QuoteSigningScheme, SellAmount}, + quote::{OrderQuoteSide, QuoteId, QuoteSigningScheme, SellAmount}, signature::{self, Signature, SigningScheme, hashed_eip712_message}, time, }, @@ -94,7 +94,7 @@ pub trait OrderValidating: Send + Sync { domain_separator: &DomainSeparator, settlement_contract: H160, full_app_data_override: Option, - ) -> Result<(Order, Option), ValidationError>; + ) -> Result<(Order, Option), ValidationError>; } #[derive(Debug)] @@ -547,7 +547,7 @@ impl OrderValidating for OrderValidator { domain_separator: &DomainSeparator, settlement_contract: H160, full_app_data_override: Option, - ) -> Result<(Order, Option), ValidationError> { + ) -> Result<(Order, Option), ValidationError> { // Happens before signature verification because a miscalculated app data hash // by the API user would lead to being unable to validate the signature below. let app_data = self.validate_app_data(&order.app_data, &full_app_data_override)?; @@ -747,6 +747,11 @@ impl OrderValidating for OrderValidator { | OrderCreationAppData::Full { full } => Some(full), OrderCreationAppData::Hash { .. } => full_app_data_override, }, + quote: quote + .as_ref() + .map(|q| q.try_to_model_order_quote()) + .transpose() + .map_err(ValidationError::Other)?, ..Default::default() }, signature: order.signature.clone(), @@ -754,7 +759,7 @@ impl OrderValidating for OrderValidator { interactions: app_data.interactions, }; - Ok((order, quote)) + Ok((order, quote.and_then(|q| q.id))) } } @@ -1422,11 +1427,11 @@ mod tests { fee_amount: U256::zero(), ..creation.clone() }; - let (order, quote) = validator + let (order, _) = validator .validate_and_construct_order(creation_, &domain_separator, Default::default(), None) .await .unwrap(); - assert!(quote.is_some()); + assert!(order.metadata.quote.is_some()); assert!(order.metadata.class.is_limit()); let creation_ = OrderCreation { @@ -1437,11 +1442,11 @@ mod tests { }, ..creation }; - let (order, quote) = validator + let (order, _) = validator .validate_and_construct_order(creation_, &domain_separator, Default::default(), None) .await .unwrap(); - assert!(quote.is_some()); + assert!(order.metadata.quote.is_some()); assert!(order.metadata.class.is_limit()); } @@ -2189,4 +2194,101 @@ mod tests { model::order::OrderKind::Sell, )); } + + #[tokio::test] + async fn validate_quote_find_by_id() { + let mut order_quoter = MockOrderQuoting::new(); + let quote_search_parameters = QuoteSearchParameters { + sell_token: H160([1; 20]), + buy_token: H160([2; 20]), + sell_amount: 3.into(), + buy_amount: 4.into(), + fee_amount: 0.into(), + kind: OrderKind::Buy, + signing_scheme: QuoteSigningScheme::Eip1271 { + onchain_order: false, + verification_gas_limit: default_verification_gas_limit(), + }, + additional_gas: 0, + verification: Verification { + from: H160([0xf0; 20]), + receiver: H160([0xf0; 20]), + ..Default::default() + }, + }; + let quote_id = Some(42); + let quote_data = Quote { + id: quote_id, + ..Default::default() + }; + order_quoter + .expect_find_quote() + .with(eq(quote_id), eq(quote_search_parameters.clone())) + .returning(move |_, _| Ok(quote_data.clone())); + + let mut bad_token_detector = MockBadTokenDetecting::new(); + let mut balance_fetcher = MockBalanceFetching::new(); + bad_token_detector + .expect_detect() + .returning(|_| Ok(TokenQuality::Good)); + balance_fetcher + .expect_can_transfer() + .returning(|_, _| Ok(())); + + let mut signature_validating = MockSignatureValidating::new(); + signature_validating + .expect_validate_signature_and_get_additional_gas() + .returning(|_| Ok(default_verification_gas_limit())); + let mut limit_order_counter = MockLimitOrderCounting::new(); + limit_order_counter.expect_count().returning(|_| Ok(0u64)); + + let validator = OrderValidator::new( + dummy_contract!(WETH9, [0xef; 20]), + Arc::new(order_validation::banned::Users::none()), + OrderValidPeriodConfiguration { + min: Duration::from_secs(1), + max_market: Duration::from_secs(100), + max_limit: Duration::from_secs(200), + }, + false, + Arc::new(bad_token_detector), + dummy_contract!(HooksTrampoline, [0xcf; 20]), + Arc::new(order_quoter), + Arc::new(balance_fetcher), + Arc::new(signature_validating), + Arc::new(limit_order_counter), + 0, + Arc::new(MockCodeFetching::new()), + Default::default(), + u64::MAX, + ); + + let creation = OrderCreation { + valid_to: time::now_in_epoch_seconds() + 10, + sell_token: H160([1; 20]), + buy_token: H160([2; 20]), + buy_amount: U256::from(4), + sell_amount: U256::from(3), + fee_amount: U256::from(0), + signature: Signature::Eip1271(vec![1, 2, 3]), + app_data: OrderCreationAppData::Full { + full: "{}".to_string(), + }, + from: Some(H160([0xf0; 20])), + receiver: Some(H160([0xf0; 20])), + quote_id, + ..Default::default() + }; + let (_, returned_quote_id) = validator + .validate_and_construct_order( + creation.clone(), + &Default::default(), + Default::default(), + None, + ) + .await + .unwrap(); + + assert_eq!(quote_id, returned_quote_id); + } } From 30d4c685abfb79b23b20a70e6d3499b55d28b791 Mon Sep 17 00:00:00 2001 From: Martin Magnus Date: Fri, 7 Mar 2025 14:01:06 +0100 Subject: [PATCH 3/3] Multi flashloan support (#3310) # Description https://github.com/cowprotocol/flash-loan-wrapper-solver/pull/9 updated the flashloan contracts to support multiple loans in a single settlement. Based on the included e2e test I updated the driver's encoding logic. Which contracts needs to get approved and where to move the funds remained unchanged. The only real difference is that the router is now always the target of the call and we pass details of each loan it should take out into it's function call together with the underlying `settle()` call. # Changes * updated wrapper ABI files * generated binding for new router contract * updated drivers' encoding logic * added necessary config boilerplate code ## How to test so far I only updated the existing e2e test that takes out a single loan which is good enough for now in a follow up PR we should add another e2e test that takes out 2 flashloans --- .../artifacts/AaveFlashLoanSolverWrapper.json | 3 +- .../ERC3156FlashLoanSolverWrapper.json | 3 +- .../contracts/artifacts/FlashLoanRouter.json | 2 + crates/contracts/build.rs | 1 + crates/contracts/src/lib.rs | 1 + crates/driver/example.toml | 2 + .../driver/src/domain/competition/auction.rs | 11 +++-- .../src/domain/competition/order/app_data.rs | 15 ++++--- .../domain/competition/solution/encoding.rs | 42 +++++++++---------- .../driver/src/infra/blockchain/contracts.rs | 23 ++++++++++ crates/driver/src/infra/config/file/load.rs | 1 + crates/driver/src/infra/config/file/mod.rs | 4 ++ .../driver/src/tests/cases/example_config.rs | 4 +- .../driver/src/tests/cases/flashloan_hints.rs | 9 +++- crates/driver/src/tests/setup/blockchain.rs | 12 +++++- crates/driver/src/tests/setup/driver.rs | 10 ++++- crates/driver/src/tests/setup/mod.rs | 6 +-- crates/driver/src/tests/setup/solver.rs | 4 ++ crates/e2e/src/setup/colocation.rs | 2 + crates/e2e/src/setup/deploy.rs | 18 ++++++-- .../e2e/src/setup/onchain_components/mod.rs | 11 +---- 21 files changed, 129 insertions(+), 55 deletions(-) create mode 100644 crates/contracts/artifacts/FlashLoanRouter.json diff --git a/crates/contracts/artifacts/AaveFlashLoanSolverWrapper.json b/crates/contracts/artifacts/AaveFlashLoanSolverWrapper.json index fd5dba8524..d233e6c053 100644 --- a/crates/contracts/artifacts/AaveFlashLoanSolverWrapper.json +++ b/crates/contracts/artifacts/AaveFlashLoanSolverWrapper.json @@ -1 +1,2 @@ -{"abi":[{"type":"constructor","inputs":[{"name":"_settlementContract","type":"address","internalType":"contract ICowSettlement"}],"stateMutability":"nonpayable"},{"type":"function","name":"approve","inputs":[{"name":"token","type":"address","internalType":"contract IERC20"},{"name":"target","type":"address","internalType":"address"},{"name":"amount","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"executeOperation","inputs":[{"name":"","type":"address[]","internalType":"address[]"},{"name":"","type":"uint256[]","internalType":"uint256[]"},{"name":"","type":"uint256[]","internalType":"uint256[]"},{"name":"","type":"address","internalType":"address"},{"name":"","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"nonpayable"},{"type":"function","name":"flashLoanAndSettle","inputs":[{"name":"lender","type":"address","internalType":"address"},{"name":"loan","type":"tuple","internalType":"struct IFlashLoanSolverWrapper.LoanRequest","components":[{"name":"token","type":"address","internalType":"contract IERC20"},{"name":"amount","type":"uint256","internalType":"uint256"}]},{"name":"settlement","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"settlementAuthentication","inputs":[],"outputs":[{"name":"","type":"address","internalType":"contract ICowAuthentication"}],"stateMutability":"view"},{"type":"function","name":"settlementContract","inputs":[],"outputs":[{"name":"","type":"address","internalType":"contract ICowSettlement"}],"stateMutability":"view"}],"bytecode":"0x60c060405234801561000f575f5ffd5b50604051610ed2380380610ed283398101604081905261002e916100d5565b80806001600160a01b03166080816001600160a01b031681525050806001600160a01b0316632335c76b6040518163ffffffff1660e01b81526004016020604051808303815f875af1158015610086573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906100aa91906100d5565b6001600160a01b031660a052506100f79050565b6001600160a01b03811681146100d2575f5ffd5b50565b5f602082840312156100e5575f5ffd5b81516100f0816100be565b9392505050565b60805160a051610da761012b5f395f8181606d015261031701525f818160f60152818161015c01526105cb0152610da75ff3fe608060405234801561000f575f5ffd5b5060043610610064575f3560e01c8063e1f21c671161004d578063e1f21c67146100dc578063ea42418b146100f1578063f24cc7eb14610118575f5ffd5b806302ebcbea14610068578063920f5c84146100b9575b5f5ffd5b61008f7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b6100cc6100c73660046109ac565b61012b565b60405190151581526020016100b0565b6100ef6100ea366004610a8e565b610144565b005b61008f7f000000000000000000000000000000000000000000000000000000000000000081565b6100ef610126366004610acc565b6102e9565b5f61013461054b565b5060019998505050505050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146101e8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f4f6e6c792063616c6c61626c6520696e206120736574746c656d656e7400000060448201526064015b60405180910390fd5b6040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83811660048301526024820183905284169063095ea7b3906044016020604051808303815f875af115801561025a573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061027e9190610b54565b6102e4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f417070726f76616c206661696c6564000000000000000000000000000000000060448201526064016101df565b505050565b6040517f02cc250d0000000000000000000000000000000000000000000000000000000081523360048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906302cc250d90602401602060405180830381865afa158015610371573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103959190610b54565b6103fb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f4f6e6c792063616c6c61626c65206279206120736f6c7665720000000000000060448201526064016101df565b7f13d79a0b0000000000000000000000000000000000000000000000000000000061042683836106bb565b7fffffffff0000000000000000000000000000000000000000000000000000000016146104af576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f4f6e6c7920736574746c65282920697320616c6c6f776564000000000000000060448201526064016101df565b5f5c15610518576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f50656e64696e6720736574746c656d656e74000000000000000000000000000060448201526064016101df565b61052282826106ce565b61053d846105336020860186610b7a565b85602001356106f3565b61054561087d565b50505050565b5f610554610884565b90505f8151116105c0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f20736574746c656d656e742070656e64696e67000000000000000000000060448201526064016101df565b6105c861087d565b5f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168260405161060e9190610b95565b5f604051808303815f865af19150503d805f8114610647576040519150601f19603f3d011682016040523d82523d5f602084013e61064c565b606091505b50509050806106b7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f536574746c656d656e742072657665727465640000000000000000000000000060448201526064016101df565b5050565b5f600482106106c8575081355b92915050565b80805f5d505f5b818110156102e45780830135602082041981815d50506020016106d5565b60408051600180825281830190925230915f91906020808301908036833701905050905083815f8151811061072a5761072a610bd8565b73ffffffffffffffffffffffffffffffffffffffff92909216602092830291909101909101526040805160018082528183019092525f9181602001602082028036833701905050905083815f8151811061078657610786610bd8565b60209081029190910101526040805160018082528183019092525f918160200160208202803683370190505090505f815f815181106107c7576107c7610bd8565b6020908102919091018101919091526040805191820181525f80835290517fab9c4b5d0000000000000000000000000000000000000000000000000000000081523092919073ffffffffffffffffffffffffffffffffffffffff8b169063ab9c4b5d90610844908a908a908a908a908a908a908a90600401610c8b565b5f604051808303815f87803b15801561085b575f5ffd5b505af115801561086d573d5f5f3e3d5ffd5b5050505050505050505050505050565b5f80805d50565b60605f5c6020601f820181900490810267ffffffffffffffff8111156108ac576108ac610bab565b6040519080825280601f01601f1916602001820160405280156108d6576020820181803683370190505b509250825f5b828110156108f95780195c602060019092019182028301526108dc565b50919091525090565b5f5f83601f840112610912575f5ffd5b50813567ffffffffffffffff811115610929575f5ffd5b6020830191508360208260051b8501011115610943575f5ffd5b9250929050565b73ffffffffffffffffffffffffffffffffffffffff8116811461096b575f5ffd5b50565b5f5f83601f84011261097e575f5ffd5b50813567ffffffffffffffff811115610995575f5ffd5b602083019150836020828501011115610943575f5ffd5b5f5f5f5f5f5f5f5f5f60a08a8c0312156109c4575f5ffd5b893567ffffffffffffffff8111156109da575f5ffd5b6109e68c828d01610902565b909a5098505060208a013567ffffffffffffffff811115610a05575f5ffd5b610a118c828d01610902565b90985096505060408a013567ffffffffffffffff811115610a30575f5ffd5b610a3c8c828d01610902565b90965094505060608a0135610a508161094a565b925060808a013567ffffffffffffffff811115610a6b575f5ffd5b610a778c828d0161096e565b915080935050809150509295985092959850929598565b5f5f5f60608486031215610aa0575f5ffd5b8335610aab8161094a565b92506020840135610abb8161094a565b929592945050506040919091013590565b5f5f5f5f8486036080811215610ae0575f5ffd5b8535610aeb8161094a565b945060407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe082011215610b1c575f5ffd5b50602085019250606085013567ffffffffffffffff811115610b3c575f5ffd5b610b488782880161096e565b95989497509550505050565b5f60208284031215610b64575f5ffd5b81518015158114610b73575f5ffd5b9392505050565b5f60208284031215610b8a575f5ffd5b8135610b738161094a565b5f82518060208501845e5f920191825250919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f8151808452602084019350602083015f5b82811015610c35578151865260209586019590910190600101610c17565b5093949350505050565b5f81518084528060208401602086015e5f6020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b5f60e0820173ffffffffffffffffffffffffffffffffffffffff8a16835260e060208401528089518083526101008501915060208b0192505f5b81811015610cf957835173ffffffffffffffffffffffffffffffffffffffff16835260209384019390920191600101610cc5565b50508381036040850152610d0d818a610c05565b9150508281036060840152610d228188610c05565b73ffffffffffffffffffffffffffffffffffffffff87166080850152905082810360a0840152610d528186610c3f565b915050610d6560c083018461ffff169052565b9897505050505050505056fea264697066735822122000967184e77cc4178c51225cb687e0b45b0326fc3348a895b2948e5f3ded4d5564736f6c634300081c0033","deployedBytecode":"0x608060405234801561000f575f5ffd5b5060043610610064575f3560e01c8063e1f21c671161004d578063e1f21c67146100dc578063ea42418b146100f1578063f24cc7eb14610118575f5ffd5b806302ebcbea14610068578063920f5c84146100b9575b5f5ffd5b61008f7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b6100cc6100c73660046109ac565b61012b565b60405190151581526020016100b0565b6100ef6100ea366004610a8e565b610144565b005b61008f7f000000000000000000000000000000000000000000000000000000000000000081565b6100ef610126366004610acc565b6102e9565b5f61013461054b565b5060019998505050505050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146101e8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f4f6e6c792063616c6c61626c6520696e206120736574746c656d656e7400000060448201526064015b60405180910390fd5b6040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83811660048301526024820183905284169063095ea7b3906044016020604051808303815f875af115801561025a573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061027e9190610b54565b6102e4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f417070726f76616c206661696c6564000000000000000000000000000000000060448201526064016101df565b505050565b6040517f02cc250d0000000000000000000000000000000000000000000000000000000081523360048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906302cc250d90602401602060405180830381865afa158015610371573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103959190610b54565b6103fb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f4f6e6c792063616c6c61626c65206279206120736f6c7665720000000000000060448201526064016101df565b7f13d79a0b0000000000000000000000000000000000000000000000000000000061042683836106bb565b7fffffffff0000000000000000000000000000000000000000000000000000000016146104af576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f4f6e6c7920736574746c65282920697320616c6c6f776564000000000000000060448201526064016101df565b5f5c15610518576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f50656e64696e6720736574746c656d656e74000000000000000000000000000060448201526064016101df565b61052282826106ce565b61053d846105336020860186610b7a565b85602001356106f3565b61054561087d565b50505050565b5f610554610884565b90505f8151116105c0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f20736574746c656d656e742070656e64696e67000000000000000000000060448201526064016101df565b6105c861087d565b5f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168260405161060e9190610b95565b5f604051808303815f865af19150503d805f8114610647576040519150601f19603f3d011682016040523d82523d5f602084013e61064c565b606091505b50509050806106b7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f536574746c656d656e742072657665727465640000000000000000000000000060448201526064016101df565b5050565b5f600482106106c8575081355b92915050565b80805f5d505f5b818110156102e45780830135602082041981815d50506020016106d5565b60408051600180825281830190925230915f91906020808301908036833701905050905083815f8151811061072a5761072a610bd8565b73ffffffffffffffffffffffffffffffffffffffff92909216602092830291909101909101526040805160018082528183019092525f9181602001602082028036833701905050905083815f8151811061078657610786610bd8565b60209081029190910101526040805160018082528183019092525f918160200160208202803683370190505090505f815f815181106107c7576107c7610bd8565b6020908102919091018101919091526040805191820181525f80835290517fab9c4b5d0000000000000000000000000000000000000000000000000000000081523092919073ffffffffffffffffffffffffffffffffffffffff8b169063ab9c4b5d90610844908a908a908a908a908a908a908a90600401610c8b565b5f604051808303815f87803b15801561085b575f5ffd5b505af115801561086d573d5f5f3e3d5ffd5b5050505050505050505050505050565b5f80805d50565b60605f5c6020601f820181900490810267ffffffffffffffff8111156108ac576108ac610bab565b6040519080825280601f01601f1916602001820160405280156108d6576020820181803683370190505b509250825f5b828110156108f95780195c602060019092019182028301526108dc565b50919091525090565b5f5f83601f840112610912575f5ffd5b50813567ffffffffffffffff811115610929575f5ffd5b6020830191508360208260051b8501011115610943575f5ffd5b9250929050565b73ffffffffffffffffffffffffffffffffffffffff8116811461096b575f5ffd5b50565b5f5f83601f84011261097e575f5ffd5b50813567ffffffffffffffff811115610995575f5ffd5b602083019150836020828501011115610943575f5ffd5b5f5f5f5f5f5f5f5f5f60a08a8c0312156109c4575f5ffd5b893567ffffffffffffffff8111156109da575f5ffd5b6109e68c828d01610902565b909a5098505060208a013567ffffffffffffffff811115610a05575f5ffd5b610a118c828d01610902565b90985096505060408a013567ffffffffffffffff811115610a30575f5ffd5b610a3c8c828d01610902565b90965094505060608a0135610a508161094a565b925060808a013567ffffffffffffffff811115610a6b575f5ffd5b610a778c828d0161096e565b915080935050809150509295985092959850929598565b5f5f5f60608486031215610aa0575f5ffd5b8335610aab8161094a565b92506020840135610abb8161094a565b929592945050506040919091013590565b5f5f5f5f8486036080811215610ae0575f5ffd5b8535610aeb8161094a565b945060407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe082011215610b1c575f5ffd5b50602085019250606085013567ffffffffffffffff811115610b3c575f5ffd5b610b488782880161096e565b95989497509550505050565b5f60208284031215610b64575f5ffd5b81518015158114610b73575f5ffd5b9392505050565b5f60208284031215610b8a575f5ffd5b8135610b738161094a565b5f82518060208501845e5f920191825250919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f8151808452602084019350602083015f5b82811015610c35578151865260209586019590910190600101610c17565b5093949350505050565b5f81518084528060208401602086015e5f6020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b5f60e0820173ffffffffffffffffffffffffffffffffffffffff8a16835260e060208401528089518083526101008501915060208b0192505f5b81811015610cf957835173ffffffffffffffffffffffffffffffffffffffff16835260209384019390920191600101610cc5565b50508381036040850152610d0d818a610c05565b9150508281036060840152610d228188610c05565b73ffffffffffffffffffffffffffffffffffffffff87166080850152905082810360a0840152610d528186610c3f565b915050610d6560c083018461ffff169052565b9897505050505050505056fea264697066735822122000967184e77cc4178c51225cb687e0b45b0326fc3348a895b2948e5f3ded4d5564736f6c634300081c0033","methodIdentifiers":{"approve(address,address,uint256)":"e1f21c67","executeOperation(address[],uint256[],uint256[],address,bytes)":"920f5c84","flashLoanAndSettle(address,(address,uint256),bytes)":"f24cc7eb","settlementAuthentication()":"02ebcbea","settlementContract()":"ea42418b"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.28+commit.7893614a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"contract ICowSettlement\",\"name\":\"_settlementContract\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"},{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"name\":\"executeOperation\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"lender\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"struct IFlashLoanSolverWrapper.LoanRequest\",\"name\":\"loan\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"settlement\",\"type\":\"bytes\"}],\"name\":\"flashLoanAndSettle\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"settlementAuthentication\",\"outputs\":[{\"internalType\":\"contract ICowAuthentication\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"settlementContract\",\"outputs\":[{\"internalType\":\"contract ICowSettlement\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{\"approve(address,address,uint256)\":{\"details\":\"In general, the only way to transfer funds out of this contract is through a call to this function and a subsequent call to `transferFrom`. The allowance will be preserved across different transactions.\",\"params\":{\"amount\":\"The amount of tokens to set as the allowance.\",\"target\":\"The address that will be allowed to spend the token.\",\"token\":\"The token to approve for transferring.\"}},\"executeOperation(address[],uint256[],uint256[],address,bytes)\":{\"details\":\"Ensure that the contract can return the debt + premium, e.g., has enough funds to repay and has approved the Pool to pull the total amount\",\"params\":{\"amounts\":\"The amounts of the flash-borrowed assets\",\"assets\":\"The addresses of the flash-borrowed assets\",\"initiator\":\"The address of the flashloan initiator\",\"params\":\"The byte-encoded params passed when initiating the flashloan\",\"premiums\":\"The fee of each flash-borrowed asset\"},\"returns\":{\"_0\":\"True if the execution of the operation succeeds, false otherwise\"}},\"flashLoanAndSettle(address,(address,uint256),bytes)\":{\"details\":\"The repayment of a flash loan is different based on the protocol. For example, some expect to retrieve the funds from this borrower contract through `transferFrom`, while other check the lender balance is as expected after the flash loan has been processed. The executed settlement must be built to cater to the needs of the specified lender.A settlement can be executed at most once in a call. The settlement data cannot change during execution. Only the settle function can be called. All of this is also the case if the lender is untrusted.\",\"params\":{\"lender\":\"The address of the flash-loan lender from which to borrow.\",\"loan\":\"The parameters describing the requested loan.\",\"settlement\":\"The call data for a call to the `settle()` function in the CoW Protocol settlement contract. It fully describes a CoW Protocol settlement.\"}}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"approve(address,address,uint256)\":{\"notice\":\"Approves the target address to spend the specified token on behalf of the flash-loan solver wrapper up to the specified amount.\"},\"executeOperation(address[],uint256[],uint256[],address,bytes)\":{\"notice\":\"Executes an operation after receiving the flash-borrowed assets\"},\"flashLoanAndSettle(address,(address,uint256),bytes)\":{\"notice\":\"Requests a flash loan with the specified parameters from the lender and, once the funds have been received, executes the settlement specified as part of the call. The flash-loan repayment is expected to take place during the settlement.\"},\"settlementAuthentication()\":{\"notice\":\"The contract responsible to determine which address is an authorized solver for CoW Protocol.\"},\"settlementContract()\":{\"notice\":\"The settlement contract that will be called when a settlement is executed after a flash loan.\"}},\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/AaveFlashLoanSolverWrapper.sol\":\"AaveFlashLoanSolverWrapper\"},\"evmVersion\":\"cancun\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":1000000},\"remappings\":[\":forge-std/=lib/forge-std/src/\"]},\"sources\":{\"src/AaveFlashLoanSolverWrapper.sol\":{\"keccak256\":\"0x23a73acc298de366be52d586d39ced342575b88273982bcee51468d54e794c57\",\"license\":\"GPL-3.0-or-later\",\"urls\":[\"bzz-raw://2f746ad2a60140968dfbc922147922578e82d4bb7863617efaa80ce3b9d95596\",\"dweb:/ipfs/Qmby8ugp9n6kMQipiCxfBaEqsK3npVjNRg3ZCbQVeK233R\"]},\"src/interface/ICowSettlement.sol\":{\"keccak256\":\"0x936d8038ce833e625c7c306b5890ba85c4e5bf8adc8b7e41227a680c4039e244\",\"license\":\"GPL-3.0-or-later\",\"urls\":[\"bzz-raw://138b144cac2d5cd0b3bbd4b0f73817b1e611d90854734f64b98b15e709aa7b5e\",\"dweb:/ipfs/Qmf6rcxHBToX7Ym7zHXP8BRaWwD7DLptuZqYq3YDcd7jvJ\"]},\"src/interface/IFlashLoanSolverWrapper.sol\":{\"keccak256\":\"0x28d637bc299e016ffd0ed3e1b0d2a6c57887a1dd95d91355e4de81c5e733e844\",\"license\":\"GPL-3.0-or-later\",\"urls\":[\"bzz-raw://0812acca6ee68fef89d5091ce838c214e1d6da29ea15d1bb611929907c65e378\",\"dweb:/ipfs/QmSwKuoMKWjK8dbJnmagQ2u5nqYwwfFsBFaDNQiQyy5GS1\"]},\"src/mixin/FlashLoanSolverWrapper.sol\":{\"keccak256\":\"0xc5085256e1779663463e15b6dc7c411c20ba5811ca5ed3088823b52a17c5e18f\",\"license\":\"GPL-3.0-or-later\",\"urls\":[\"bzz-raw://970788daf4b538f66877dc1a9ae097227424f8187375034f2f8c800656fd0909\",\"dweb:/ipfs/QmbucPkUkEW4f1V95yeKFRcFD7ukPXQsJmHcP4i7JohvuT\"]},\"src/mixin/TransientStorageArray.sol\":{\"keccak256\":\"0xd3527c2733c6a1131e3c321746405f946cfb36528c032bd84b685b75e2dd9274\",\"license\":\"GPL-3.0-or-later\",\"urls\":[\"bzz-raw://6836bd40e1bcb264d20a0b9bce6dbc58058f2580c133ceb8d328b3ec464c0228\",\"dweb:/ipfs/QmWFXB6HrzsHUvCQFykZoARqhMpeHVSbY442qQzG4UpfUr\"]},\"src/vendored/IAaveFlashLoanReceiver.sol\":{\"keccak256\":\"0x9cb252053b01fd8c223086172a94322e364f3b91f4ff12c1749159828a1e34dc\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://371f289a2b4ff229ddb1ebf605bc05553b0f434d2419cbe2d0141facbc826d7c\",\"dweb:/ipfs/QmdYkSRXCWgUg7uyVW6U89PAQgcwbm4UqPrpsewXF37V4T\"]},\"src/vendored/IAavePool.sol\":{\"keccak256\":\"0x2086e04514230a6759d9c41147d4f7e9ee4810e3a2c700ef06a17459c916eeb8\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://a94216a796da8e1cbebd19e3ca69bc509c395157ef369a60ca4063c060bfe4c5\",\"dweb:/ipfs/QmW2ZDANxiBqYaaptspF4EHqskpDPAzFasea2uTtbHH5ZD\"]},\"src/vendored/ICowAuthentication.sol\":{\"keccak256\":\"0xc0b8ec08ce1e4ed2af4188ab08281b1a894d74b2dc69e045398e789f5a72d140\",\"license\":\"LGPL-3.0-or-later\",\"urls\":[\"bzz-raw://41917d1ba4f6846ddd52d7fe16189a14a836d62b255e3ad07ebbd46f33cdbfa4\",\"dweb:/ipfs/QmXTzjA9qoNX3aHehLJqMyx7qpZKitPE1Un3buB5QtKqfq\"]},\"src/vendored/IERC20.sol\":{\"keccak256\":\"0x1b72641f69f5a2156fc2319a6b08daa0c4b5b224d0a776b85a5fcae428af72c2\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://261d26fbf99877e2d4b5a7e6b2b2defee5ba5cab5bfdfab231fb5f2e2dfb1fdb\",\"dweb:/ipfs/QmSpQuVkTHXzH1CNWmgtnCLz9CNEMD5g5sciiXRkrbYUJt\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.28+commit.7893614a"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"contract ICowSettlement","name":"_settlementContract","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"nonpayable","type":"function","name":"approve"},{"inputs":[{"internalType":"address[]","name":"","type":"address[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"address","name":"","type":"address"},{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"nonpayable","type":"function","name":"executeOperation","outputs":[{"internalType":"bool","name":"","type":"bool"}]},{"inputs":[{"internalType":"address","name":"lender","type":"address"},{"internalType":"struct IFlashLoanSolverWrapper.LoanRequest","name":"loan","type":"tuple","components":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}]},{"internalType":"bytes","name":"settlement","type":"bytes"}],"stateMutability":"nonpayable","type":"function","name":"flashLoanAndSettle"},{"inputs":[],"stateMutability":"view","type":"function","name":"settlementAuthentication","outputs":[{"internalType":"contract ICowAuthentication","name":"","type":"address"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"settlementContract","outputs":[{"internalType":"contract ICowSettlement","name":"","type":"address"}]}],"devdoc":{"kind":"dev","methods":{"approve(address,address,uint256)":{"details":"In general, the only way to transfer funds out of this contract is through a call to this function and a subsequent call to `transferFrom`. The allowance will be preserved across different transactions.","params":{"amount":"The amount of tokens to set as the allowance.","target":"The address that will be allowed to spend the token.","token":"The token to approve for transferring."}},"executeOperation(address[],uint256[],uint256[],address,bytes)":{"details":"Ensure that the contract can return the debt + premium, e.g., has enough funds to repay and has approved the Pool to pull the total amount","params":{"amounts":"The amounts of the flash-borrowed assets","assets":"The addresses of the flash-borrowed assets","initiator":"The address of the flashloan initiator","params":"The byte-encoded params passed when initiating the flashloan","premiums":"The fee of each flash-borrowed asset"},"returns":{"_0":"True if the execution of the operation succeeds, false otherwise"}},"flashLoanAndSettle(address,(address,uint256),bytes)":{"details":"The repayment of a flash loan is different based on the protocol. For example, some expect to retrieve the funds from this borrower contract through `transferFrom`, while other check the lender balance is as expected after the flash loan has been processed. The executed settlement must be built to cater to the needs of the specified lender.A settlement can be executed at most once in a call. The settlement data cannot change during execution. Only the settle function can be called. All of this is also the case if the lender is untrusted.","params":{"lender":"The address of the flash-loan lender from which to borrow.","loan":"The parameters describing the requested loan.","settlement":"The call data for a call to the `settle()` function in the CoW Protocol settlement contract. It fully describes a CoW Protocol settlement."}}},"version":1},"userdoc":{"kind":"user","methods":{"approve(address,address,uint256)":{"notice":"Approves the target address to spend the specified token on behalf of the flash-loan solver wrapper up to the specified amount."},"executeOperation(address[],uint256[],uint256[],address,bytes)":{"notice":"Executes an operation after receiving the flash-borrowed assets"},"flashLoanAndSettle(address,(address,uint256),bytes)":{"notice":"Requests a flash loan with the specified parameters from the lender and, once the funds have been received, executes the settlement specified as part of the call. The flash-loan repayment is expected to take place during the settlement."},"settlementAuthentication()":{"notice":"The contract responsible to determine which address is an authorized solver for CoW Protocol."},"settlementContract()":{"notice":"The settlement contract that will be called when a settlement is executed after a flash loan."}},"version":1}},"settings":{"remappings":["forge-std/=lib/forge-std/src/"],"optimizer":{"enabled":true,"runs":1000000},"metadata":{"bytecodeHash":"ipfs"},"compilationTarget":{"src/AaveFlashLoanSolverWrapper.sol":"AaveFlashLoanSolverWrapper"},"evmVersion":"cancun","libraries":{}},"sources":{"src/AaveFlashLoanSolverWrapper.sol":{"keccak256":"0x23a73acc298de366be52d586d39ced342575b88273982bcee51468d54e794c57","urls":["bzz-raw://2f746ad2a60140968dfbc922147922578e82d4bb7863617efaa80ce3b9d95596","dweb:/ipfs/Qmby8ugp9n6kMQipiCxfBaEqsK3npVjNRg3ZCbQVeK233R"],"license":"GPL-3.0-or-later"},"src/interface/ICowSettlement.sol":{"keccak256":"0x936d8038ce833e625c7c306b5890ba85c4e5bf8adc8b7e41227a680c4039e244","urls":["bzz-raw://138b144cac2d5cd0b3bbd4b0f73817b1e611d90854734f64b98b15e709aa7b5e","dweb:/ipfs/Qmf6rcxHBToX7Ym7zHXP8BRaWwD7DLptuZqYq3YDcd7jvJ"],"license":"GPL-3.0-or-later"},"src/interface/IFlashLoanSolverWrapper.sol":{"keccak256":"0x28d637bc299e016ffd0ed3e1b0d2a6c57887a1dd95d91355e4de81c5e733e844","urls":["bzz-raw://0812acca6ee68fef89d5091ce838c214e1d6da29ea15d1bb611929907c65e378","dweb:/ipfs/QmSwKuoMKWjK8dbJnmagQ2u5nqYwwfFsBFaDNQiQyy5GS1"],"license":"GPL-3.0-or-later"},"src/mixin/FlashLoanSolverWrapper.sol":{"keccak256":"0xc5085256e1779663463e15b6dc7c411c20ba5811ca5ed3088823b52a17c5e18f","urls":["bzz-raw://970788daf4b538f66877dc1a9ae097227424f8187375034f2f8c800656fd0909","dweb:/ipfs/QmbucPkUkEW4f1V95yeKFRcFD7ukPXQsJmHcP4i7JohvuT"],"license":"GPL-3.0-or-later"},"src/mixin/TransientStorageArray.sol":{"keccak256":"0xd3527c2733c6a1131e3c321746405f946cfb36528c032bd84b685b75e2dd9274","urls":["bzz-raw://6836bd40e1bcb264d20a0b9bce6dbc58058f2580c133ceb8d328b3ec464c0228","dweb:/ipfs/QmWFXB6HrzsHUvCQFykZoARqhMpeHVSbY442qQzG4UpfUr"],"license":"GPL-3.0-or-later"},"src/vendored/IAaveFlashLoanReceiver.sol":{"keccak256":"0x9cb252053b01fd8c223086172a94322e364f3b91f4ff12c1749159828a1e34dc","urls":["bzz-raw://371f289a2b4ff229ddb1ebf605bc05553b0f434d2419cbe2d0141facbc826d7c","dweb:/ipfs/QmdYkSRXCWgUg7uyVW6U89PAQgcwbm4UqPrpsewXF37V4T"],"license":"MIT"},"src/vendored/IAavePool.sol":{"keccak256":"0x2086e04514230a6759d9c41147d4f7e9ee4810e3a2c700ef06a17459c916eeb8","urls":["bzz-raw://a94216a796da8e1cbebd19e3ca69bc509c395157ef369a60ca4063c060bfe4c5","dweb:/ipfs/QmW2ZDANxiBqYaaptspF4EHqskpDPAzFasea2uTtbHH5ZD"],"license":"MIT"},"src/vendored/ICowAuthentication.sol":{"keccak256":"0xc0b8ec08ce1e4ed2af4188ab08281b1a894d74b2dc69e045398e789f5a72d140","urls":["bzz-raw://41917d1ba4f6846ddd52d7fe16189a14a836d62b255e3ad07ebbd46f33cdbfa4","dweb:/ipfs/QmXTzjA9qoNX3aHehLJqMyx7qpZKitPE1Un3buB5QtKqfq"],"license":"LGPL-3.0-or-later"},"src/vendored/IERC20.sol":{"keccak256":"0x1b72641f69f5a2156fc2319a6b08daa0c4b5b224d0a776b85a5fcae428af72c2","urls":["bzz-raw://261d26fbf99877e2d4b5a7e6b2b2defee5ba5cab5bfdfab231fb5f2e2dfb1fdb","dweb:/ipfs/QmSpQuVkTHXzH1CNWmgtnCLz9CNEMD5g5sciiXRkrbYUJt"],"license":"MIT"}},"version":1},"id":25} \ No newline at end of file +{"abi":[{"type":"constructor","inputs":[{"name":"_router","type":"address","internalType":"contract IFlashLoanRouter"}],"stateMutability":"nonpayable"},{"type":"function","name":"approve","inputs":[{"name":"token","type":"address","internalType":"contract IERC20"},{"name":"target","type":"address","internalType":"address"},{"name":"amount","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"executeOperation","inputs":[{"name":"","type":"address[]","internalType":"address[]"},{"name":"","type":"uint256[]","internalType":"uint256[]"},{"name":"","type":"uint256[]","internalType":"uint256[]"},{"name":"","type":"address","internalType":"address"},{"name":"callbackData","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"nonpayable"},{"type":"function","name":"flashLoanAndCallBack","inputs":[{"name":"lender","type":"address","internalType":"address"},{"name":"loan","type":"tuple","internalType":"struct IFlashLoanSolverWrapper.LoanRequest","components":[{"name":"token","type":"address","internalType":"contract IERC20"},{"name":"amount","type":"uint256","internalType":"uint256"}]},{"name":"_callbackDataHash","type":"bytes32","internalType":"bytes32"},{"name":"callbackData","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"router","inputs":[],"outputs":[{"name":"","type":"address","internalType":"contract IFlashLoanRouter"}],"stateMutability":"view"},{"type":"function","name":"settlementContract","inputs":[],"outputs":[{"name":"","type":"address","internalType":"contract ICowSettlement"}],"stateMutability":"view"}],"bytecode":"0x60c060405234801561000f575f5ffd5b50604051610cca380380610cca83398101604081905261002e916100d5565b80806001600160a01b03166080816001600160a01b031681525050806001600160a01b031663ea42418b6040518163ffffffff1660e01b81526004016020604051808303815f875af1158015610086573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906100aa91906100d5565b6001600160a01b031660a052506100f79050565b6001600160a01b03811681146100d2575f5ffd5b50565b5f602082840312156100e5575f5ffd5b81516100f0816100be565b9392505050565b60805160a051610b9e61012c5f395f818160bd015261030101525f818161010901528181610192015261053b0152610b9e5ff3fe608060405234801561000f575f5ffd5b5060043610610064575f3560e01c8063e1f21c671161004d578063e1f21c67146100a5578063ea42418b146100b8578063f887ea4014610104575f5ffd5b8063920f5c8414610068578063e0bbec7714610090575b5f5ffd5b61007b6100763660046107c7565b61012b565b60405190151581526020015b60405180910390f35b6100a361009e3660046108a9565b61017a565b005b6100a36100b336600461093e565b6102e9565b6100df7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610087565b6100df7f000000000000000000000000000000000000000000000000000000000000000081565b5f61016a83838080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061048992505050565b5060019998505050505050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461021e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f4f6e6c792063616c6c61626c6520696e206120736574746c656d656e7400000060448201526064015b60405180910390fd5b5f5c15610287576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f50656e64696e672063616c6c6261636b000000000000000000000000000000006044820152606401610215565b82805f5d506102dd8561029d602087018761097c565b866020013585858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152506105a092505050565b5f80805d505050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610388576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f4f6e6c792063616c6c61626c6520696e206120736574746c656d656e740000006044820152606401610215565b6040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83811660048301526024820183905284169063095ea7b3906044016020604051808303815f875af11580156103fa573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061041e919061099e565b610484576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f417070726f76616c206661696c656400000000000000000000000000000000006044820152606401610215565b505050565b805160208201205f5c146104f9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f43616c6c6261636b20646174612068617368206e6f74206d61746368696e67006044820152606401610215565b5f80805d506040517f0efb1fb600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690630efb1fb690610570908490600401610a09565b5f604051808303815f87803b158015610587575f5ffd5b505af1158015610599573d5f5f3e3d5ffd5b5050505050565b60408051600180825281830190925230915f91906020808301908036833701905050905084815f815181106105d7576105d7610a1b565b73ffffffffffffffffffffffffffffffffffffffff92909216602092830291909101909101526040805160018082528183019092525f9181602001602082028036833701905050905084815f8151811061063357610633610a1b565b60209081029190910101526040805160018082528183019092525f918160200160208202803683370190505090505f815f8151811061067457610674610a1b565b60209081029190910101526040517fab9c4b5d000000000000000000000000000000000000000000000000000000008152309086905f9073ffffffffffffffffffffffffffffffffffffffff8c169063ab9c4b5d906106e3908a908a908a908a908a908a908a90600401610a82565b5f604051808303815f87803b1580156106fa575f5ffd5b505af115801561070c573d5f5f3e3d5ffd5b505050505050505050505050505050565b5f5f83601f84011261072d575f5ffd5b50813567ffffffffffffffff811115610744575f5ffd5b6020830191508360208260051b850101111561075e575f5ffd5b9250929050565b73ffffffffffffffffffffffffffffffffffffffff81168114610786575f5ffd5b50565b5f5f83601f840112610799575f5ffd5b50813567ffffffffffffffff8111156107b0575f5ffd5b60208301915083602082850101111561075e575f5ffd5b5f5f5f5f5f5f5f5f5f60a08a8c0312156107df575f5ffd5b893567ffffffffffffffff8111156107f5575f5ffd5b6108018c828d0161071d565b909a5098505060208a013567ffffffffffffffff811115610820575f5ffd5b61082c8c828d0161071d565b90985096505060408a013567ffffffffffffffff81111561084b575f5ffd5b6108578c828d0161071d565b90965094505060608a013561086b81610765565b925060808a013567ffffffffffffffff811115610886575f5ffd5b6108928c828d01610789565b915080935050809150509295985092959850929598565b5f5f5f5f5f85870360a08112156108be575f5ffd5b86356108c981610765565b955060407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0820112156108fa575f5ffd5b5060208601935060608601359250608086013567ffffffffffffffff811115610921575f5ffd5b61092d88828901610789565b969995985093965092949392505050565b5f5f5f60608486031215610950575f5ffd5b833561095b81610765565b9250602084013561096b81610765565b929592945050506040919091013590565b5f6020828403121561098c575f5ffd5b813561099781610765565b9392505050565b5f602082840312156109ae575f5ffd5b81518015158114610997575f5ffd5b5f81518084528060208401602086015e5f6020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b602081525f61099760208301846109bd565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f8151808452602084019350602083015f5b82811015610a78578151865260209586019590910190600101610a5a565b5093949350505050565b5f60e0820173ffffffffffffffffffffffffffffffffffffffff8a16835260e060208401528089518083526101008501915060208b0192505f5b81811015610af057835173ffffffffffffffffffffffffffffffffffffffff16835260209384019390920191600101610abc565b50508381036040850152610b04818a610a48565b9150508281036060840152610b198188610a48565b73ffffffffffffffffffffffffffffffffffffffff87166080850152905082810360a0840152610b4981866109bd565b915050610b5c60c083018461ffff169052565b9897505050505050505056fea2646970667358221220f4c671d2669d9f2049b1ea04a2d87b9203fc902f70bbc6a8f462a6bad014288164736f6c634300081c0033","deployedBytecode":"0x608060405234801561000f575f5ffd5b5060043610610064575f3560e01c8063e1f21c671161004d578063e1f21c67146100a5578063ea42418b146100b8578063f887ea4014610104575f5ffd5b8063920f5c8414610068578063e0bbec7714610090575b5f5ffd5b61007b6100763660046107c7565b61012b565b60405190151581526020015b60405180910390f35b6100a361009e3660046108a9565b61017a565b005b6100a36100b336600461093e565b6102e9565b6100df7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610087565b6100df7f000000000000000000000000000000000000000000000000000000000000000081565b5f61016a83838080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061048992505050565b5060019998505050505050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461021e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f4f6e6c792063616c6c61626c6520696e206120736574746c656d656e7400000060448201526064015b60405180910390fd5b5f5c15610287576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f50656e64696e672063616c6c6261636b000000000000000000000000000000006044820152606401610215565b82805f5d506102dd8561029d602087018761097c565b866020013585858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152506105a092505050565b5f80805d505050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610388576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f4f6e6c792063616c6c61626c6520696e206120736574746c656d656e740000006044820152606401610215565b6040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83811660048301526024820183905284169063095ea7b3906044016020604051808303815f875af11580156103fa573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061041e919061099e565b610484576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f417070726f76616c206661696c656400000000000000000000000000000000006044820152606401610215565b505050565b805160208201205f5c146104f9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f43616c6c6261636b20646174612068617368206e6f74206d61746368696e67006044820152606401610215565b5f80805d506040517f0efb1fb600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690630efb1fb690610570908490600401610a09565b5f604051808303815f87803b158015610587575f5ffd5b505af1158015610599573d5f5f3e3d5ffd5b5050505050565b60408051600180825281830190925230915f91906020808301908036833701905050905084815f815181106105d7576105d7610a1b565b73ffffffffffffffffffffffffffffffffffffffff92909216602092830291909101909101526040805160018082528183019092525f9181602001602082028036833701905050905084815f8151811061063357610633610a1b565b60209081029190910101526040805160018082528183019092525f918160200160208202803683370190505090505f815f8151811061067457610674610a1b565b60209081029190910101526040517fab9c4b5d000000000000000000000000000000000000000000000000000000008152309086905f9073ffffffffffffffffffffffffffffffffffffffff8c169063ab9c4b5d906106e3908a908a908a908a908a908a908a90600401610a82565b5f604051808303815f87803b1580156106fa575f5ffd5b505af115801561070c573d5f5f3e3d5ffd5b505050505050505050505050505050565b5f5f83601f84011261072d575f5ffd5b50813567ffffffffffffffff811115610744575f5ffd5b6020830191508360208260051b850101111561075e575f5ffd5b9250929050565b73ffffffffffffffffffffffffffffffffffffffff81168114610786575f5ffd5b50565b5f5f83601f840112610799575f5ffd5b50813567ffffffffffffffff8111156107b0575f5ffd5b60208301915083602082850101111561075e575f5ffd5b5f5f5f5f5f5f5f5f5f60a08a8c0312156107df575f5ffd5b893567ffffffffffffffff8111156107f5575f5ffd5b6108018c828d0161071d565b909a5098505060208a013567ffffffffffffffff811115610820575f5ffd5b61082c8c828d0161071d565b90985096505060408a013567ffffffffffffffff81111561084b575f5ffd5b6108578c828d0161071d565b90965094505060608a013561086b81610765565b925060808a013567ffffffffffffffff811115610886575f5ffd5b6108928c828d01610789565b915080935050809150509295985092959850929598565b5f5f5f5f5f85870360a08112156108be575f5ffd5b86356108c981610765565b955060407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0820112156108fa575f5ffd5b5060208601935060608601359250608086013567ffffffffffffffff811115610921575f5ffd5b61092d88828901610789565b969995985093965092949392505050565b5f5f5f60608486031215610950575f5ffd5b833561095b81610765565b9250602084013561096b81610765565b929592945050506040919091013590565b5f6020828403121561098c575f5ffd5b813561099781610765565b9392505050565b5f602082840312156109ae575f5ffd5b81518015158114610997575f5ffd5b5f81518084528060208401602086015e5f6020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b602081525f61099760208301846109bd565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f8151808452602084019350602083015f5b82811015610a78578151865260209586019590910190600101610a5a565b5093949350505050565b5f60e0820173ffffffffffffffffffffffffffffffffffffffff8a16835260e060208401528089518083526101008501915060208b0192505f5b81811015610af057835173ffffffffffffffffffffffffffffffffffffffff16835260209384019390920191600101610abc565b50508381036040850152610b04818a610a48565b9150508281036060840152610b198188610a48565b73ffffffffffffffffffffffffffffffffffffffff87166080850152905082810360a0840152610b4981866109bd565b915050610b5c60c083018461ffff169052565b9897505050505050505056fea2646970667358221220f4c671d2669d9f2049b1ea04a2d87b9203fc902f70bbc6a8f462a6bad014288164736f6c634300081c0033","methodIdentifiers":{"approve(address,address,uint256)":"e1f21c67","executeOperation(address[],uint256[],uint256[],address,bytes)":"920f5c84","flashLoanAndCallBack(address,(address,uint256),bytes32,bytes)":"e0bbec77","router()":"f887ea40","settlementContract()":"ea42418b"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.28+commit.7893614a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"contract IFlashLoanRouter\",\"name\":\"_router\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"},{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"callbackData\",\"type\":\"bytes\"}],\"name\":\"executeOperation\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"lender\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"struct IFlashLoanSolverWrapper.LoanRequest\",\"name\":\"loan\",\"type\":\"tuple\"},{\"internalType\":\"bytes32\",\"name\":\"_callbackDataHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"callbackData\",\"type\":\"bytes\"}],\"name\":\"flashLoanAndCallBack\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"router\",\"outputs\":[{\"internalType\":\"contract IFlashLoanRouter\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"settlementContract\",\"outputs\":[{\"internalType\":\"contract ICowSettlement\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{\"approve(address,address,uint256)\":{\"details\":\"In general, the only way to transfer funds out of this contract is through a call to this function and a subsequent call to `transferFrom`. The allowance will be preserved across different transactions.\",\"params\":{\"amount\":\"The amount of tokens to set as the allowance.\",\"target\":\"The address that will be allowed to spend the token.\",\"token\":\"The token to approve for transferring.\"}},\"executeOperation(address[],uint256[],uint256[],address,bytes)\":{\"details\":\"Ensure that the contract can return the debt + premium, e.g., has enough funds to repay and has approved the Pool to pull the total amount\",\"params\":{\"amounts\":\"The amounts of the flash-borrowed assets\",\"assets\":\"The addresses of the flash-borrowed assets\",\"initiator\":\"The address of the flashloan initiator\",\"params\":\"The byte-encoded params passed when initiating the flashloan\",\"premiums\":\"The fee of each flash-borrowed asset\"},\"returns\":{\"_0\":\"True if the execution of the operation succeeds, false otherwise\"}},\"flashLoanAndCallBack(address,(address,uint256),bytes32,bytes)\":{\"details\":\"The repayment of a flash loan is different based on the protocol. For example, some expect to retrieve the funds from this borrower contract through `transferFrom`, while other check the lender balance is as expected after the flash loan has been processed. The executed settlement must be built to cater to the needs of the specified lender.A settlement can be executed at most once in a call. The settlement data cannot change during execution. Only the settle function can be called. All of this is also the case if the lender is untrusted.\",\"params\":{\"callbackData\":\"The data to send back when calling the router once the loan is received.\",\"callbackDataHash\":\"The keccak256 hash of the input callback data.\",\"lender\":\"The address of the flash-loan lender from which to borrow.\",\"loan\":\"The parameters describing the requested loan.\"}}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"approve(address,address,uint256)\":{\"notice\":\"Approves the target address to spend the specified token on behalf of the flash-loan solver wrapper up to the specified amount.\"},\"executeOperation(address[],uint256[],uint256[],address,bytes)\":{\"notice\":\"Executes an operation after receiving the flash-borrowed assets\"},\"flashLoanAndCallBack(address,(address,uint256),bytes32,bytes)\":{\"notice\":\"Requests a flash loan with the specified parameters from the lender and, once the funds have been received, executes the settlement specified as part of the call. The flash-loan repayment is expected to take place during the settlement.\"},\"settlementContract()\":{\"notice\":\"The settlement contract that will be called when a settlement is executed after a flash loan.\"}},\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/AaveFlashLoanSolverWrapper.sol\":\"AaveFlashLoanSolverWrapper\"},\"evmVersion\":\"cancun\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":1000000},\"remappings\":[\":forge-std/=lib/forge-std/src/\"]},\"sources\":{\"src/AaveFlashLoanSolverWrapper.sol\":{\"keccak256\":\"0x5a5107c551a6c2559174f3b054eb0295b12b4f80143875b0249146f23f055453\",\"license\":\"GPL-3.0-or-later\",\"urls\":[\"bzz-raw://23b1751736172e684e9b58e038def62d4dc219e65197c727794042a25bc132c7\",\"dweb:/ipfs/QmYsLzMLVGySWEFmNyWC9tRkw95k5J6WYRt178aKi8f2iJ\"]},\"src/interface/ICowSettlement.sol\":{\"keccak256\":\"0x936d8038ce833e625c7c306b5890ba85c4e5bf8adc8b7e41227a680c4039e244\",\"license\":\"GPL-3.0-or-later\",\"urls\":[\"bzz-raw://138b144cac2d5cd0b3bbd4b0f73817b1e611d90854734f64b98b15e709aa7b5e\",\"dweb:/ipfs/Qmf6rcxHBToX7Ym7zHXP8BRaWwD7DLptuZqYq3YDcd7jvJ\"]},\"src/interface/IFlashLoanRouter.sol\":{\"keccak256\":\"0x72bc1e822dbbe15f75cb7e437605242fd5e8421c3f4074ad7fa0d6ba0b7991de\",\"license\":\"GPL-3.0-or-later\",\"urls\":[\"bzz-raw://63b29b2dbfe7b66d6390cd88e768b8bcc1805585eb4926102cc75a92dc2c36d5\",\"dweb:/ipfs/QmTXmGkN5CDbLAu8ykWJegZsHt9YY1MoUFWJBWPWjbYcht\"]},\"src/interface/IFlashLoanSolverWrapper.sol\":{\"keccak256\":\"0xcd22c2a72f4b0cb7a52c75d66ad801d2541a7117367ad7a9621e44aea9d0d051\",\"license\":\"GPL-3.0-or-later\",\"urls\":[\"bzz-raw://76adab1e0be8d955ebde37a752ff1e38767cf64a2fbdcb95f1dd3b8dccd0a2b6\",\"dweb:/ipfs/QmfWgmUg7kCyHq66qYVvhZk4rb16xW7emWJ4otLMqGRQzY\"]},\"src/library/LoansWithSettlement.sol\":{\"keccak256\":\"0x0a2093a67a219184cb02a40217ed9d662da8562ac8625ee5f61b235608990a69\",\"license\":\"GPL-3.0-or-later\",\"urls\":[\"bzz-raw://6f8b14b11ae34e4aa574728fc64acdee55cdef5a222e228362b8d7d941494a66\",\"dweb:/ipfs/QmXsq4jejQGr1YtYppEnjUYRxYEv762b6HWhQ2ohpFhuzM\"]},\"src/mixin/FlashLoanSolverWrapper.sol\":{\"keccak256\":\"0x95ccd3376d6c5716b7fbaace452839889184d08daf000a017e4c2f37f1fba47a\",\"license\":\"GPL-3.0-or-later\",\"urls\":[\"bzz-raw://4750bf1520deb639baab99a8dd4bf32dc02019ba30833117d5eb9200fa120141\",\"dweb:/ipfs/QmYwL4DicSTc43jYu5P5U3xXMXMbMi39xGKHycfmzTn1aL\"]},\"src/vendored/IAaveFlashLoanReceiver.sol\":{\"keccak256\":\"0x9cb252053b01fd8c223086172a94322e364f3b91f4ff12c1749159828a1e34dc\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://371f289a2b4ff229ddb1ebf605bc05553b0f434d2419cbe2d0141facbc826d7c\",\"dweb:/ipfs/QmdYkSRXCWgUg7uyVW6U89PAQgcwbm4UqPrpsewXF37V4T\"]},\"src/vendored/IAavePool.sol\":{\"keccak256\":\"0x2086e04514230a6759d9c41147d4f7e9ee4810e3a2c700ef06a17459c916eeb8\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://a94216a796da8e1cbebd19e3ca69bc509c395157ef369a60ca4063c060bfe4c5\",\"dweb:/ipfs/QmW2ZDANxiBqYaaptspF4EHqskpDPAzFasea2uTtbHH5ZD\"]},\"src/vendored/ICowAuthentication.sol\":{\"keccak256\":\"0xc0b8ec08ce1e4ed2af4188ab08281b1a894d74b2dc69e045398e789f5a72d140\",\"license\":\"LGPL-3.0-or-later\",\"urls\":[\"bzz-raw://41917d1ba4f6846ddd52d7fe16189a14a836d62b255e3ad07ebbd46f33cdbfa4\",\"dweb:/ipfs/QmXTzjA9qoNX3aHehLJqMyx7qpZKitPE1Un3buB5QtKqfq\"]},\"src/vendored/IERC20.sol\":{\"keccak256\":\"0x1b72641f69f5a2156fc2319a6b08daa0c4b5b224d0a776b85a5fcae428af72c2\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://261d26fbf99877e2d4b5a7e6b2b2defee5ba5cab5bfdfab231fb5f2e2dfb1fdb\",\"dweb:/ipfs/QmSpQuVkTHXzH1CNWmgtnCLz9CNEMD5g5sciiXRkrbYUJt\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.28+commit.7893614a"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"contract IFlashLoanRouter","name":"_router","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"nonpayable","type":"function","name":"approve"},{"inputs":[{"internalType":"address[]","name":"","type":"address[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"address","name":"","type":"address"},{"internalType":"bytes","name":"callbackData","type":"bytes"}],"stateMutability":"nonpayable","type":"function","name":"executeOperation","outputs":[{"internalType":"bool","name":"","type":"bool"}]},{"inputs":[{"internalType":"address","name":"lender","type":"address"},{"internalType":"struct IFlashLoanSolverWrapper.LoanRequest","name":"loan","type":"tuple","components":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}]},{"internalType":"bytes32","name":"_callbackDataHash","type":"bytes32"},{"internalType":"bytes","name":"callbackData","type":"bytes"}],"stateMutability":"nonpayable","type":"function","name":"flashLoanAndCallBack"},{"inputs":[],"stateMutability":"view","type":"function","name":"router","outputs":[{"internalType":"contract IFlashLoanRouter","name":"","type":"address"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"settlementContract","outputs":[{"internalType":"contract ICowSettlement","name":"","type":"address"}]}],"devdoc":{"kind":"dev","methods":{"approve(address,address,uint256)":{"details":"In general, the only way to transfer funds out of this contract is through a call to this function and a subsequent call to `transferFrom`. The allowance will be preserved across different transactions.","params":{"amount":"The amount of tokens to set as the allowance.","target":"The address that will be allowed to spend the token.","token":"The token to approve for transferring."}},"executeOperation(address[],uint256[],uint256[],address,bytes)":{"details":"Ensure that the contract can return the debt + premium, e.g., has enough funds to repay and has approved the Pool to pull the total amount","params":{"amounts":"The amounts of the flash-borrowed assets","assets":"The addresses of the flash-borrowed assets","initiator":"The address of the flashloan initiator","params":"The byte-encoded params passed when initiating the flashloan","premiums":"The fee of each flash-borrowed asset"},"returns":{"_0":"True if the execution of the operation succeeds, false otherwise"}},"flashLoanAndCallBack(address,(address,uint256),bytes32,bytes)":{"details":"The repayment of a flash loan is different based on the protocol. For example, some expect to retrieve the funds from this borrower contract through `transferFrom`, while other check the lender balance is as expected after the flash loan has been processed. The executed settlement must be built to cater to the needs of the specified lender.A settlement can be executed at most once in a call. The settlement data cannot change during execution. Only the settle function can be called. All of this is also the case if the lender is untrusted.","params":{"callbackData":"The data to send back when calling the router once the loan is received.","callbackDataHash":"The keccak256 hash of the input callback data.","lender":"The address of the flash-loan lender from which to borrow.","loan":"The parameters describing the requested loan."}}},"version":1},"userdoc":{"kind":"user","methods":{"approve(address,address,uint256)":{"notice":"Approves the target address to spend the specified token on behalf of the flash-loan solver wrapper up to the specified amount."},"executeOperation(address[],uint256[],uint256[],address,bytes)":{"notice":"Executes an operation after receiving the flash-borrowed assets"},"flashLoanAndCallBack(address,(address,uint256),bytes32,bytes)":{"notice":"Requests a flash loan with the specified parameters from the lender and, once the funds have been received, executes the settlement specified as part of the call. The flash-loan repayment is expected to take place during the settlement."},"settlementContract()":{"notice":"The settlement contract that will be called when a settlement is executed after a flash loan."}},"version":1}},"settings":{"remappings":["forge-std/=lib/forge-std/src/"],"optimizer":{"enabled":true,"runs":1000000},"metadata":{"bytecodeHash":"ipfs"},"compilationTarget":{"src/AaveFlashLoanSolverWrapper.sol":"AaveFlashLoanSolverWrapper"},"evmVersion":"cancun","libraries":{}},"sources":{"src/AaveFlashLoanSolverWrapper.sol":{"keccak256":"0x5a5107c551a6c2559174f3b054eb0295b12b4f80143875b0249146f23f055453","urls":["bzz-raw://23b1751736172e684e9b58e038def62d4dc219e65197c727794042a25bc132c7","dweb:/ipfs/QmYsLzMLVGySWEFmNyWC9tRkw95k5J6WYRt178aKi8f2iJ"],"license":"GPL-3.0-or-later"},"src/interface/ICowSettlement.sol":{"keccak256":"0x936d8038ce833e625c7c306b5890ba85c4e5bf8adc8b7e41227a680c4039e244","urls":["bzz-raw://138b144cac2d5cd0b3bbd4b0f73817b1e611d90854734f64b98b15e709aa7b5e","dweb:/ipfs/Qmf6rcxHBToX7Ym7zHXP8BRaWwD7DLptuZqYq3YDcd7jvJ"],"license":"GPL-3.0-or-later"},"src/interface/IFlashLoanRouter.sol":{"keccak256":"0x72bc1e822dbbe15f75cb7e437605242fd5e8421c3f4074ad7fa0d6ba0b7991de","urls":["bzz-raw://63b29b2dbfe7b66d6390cd88e768b8bcc1805585eb4926102cc75a92dc2c36d5","dweb:/ipfs/QmTXmGkN5CDbLAu8ykWJegZsHt9YY1MoUFWJBWPWjbYcht"],"license":"GPL-3.0-or-later"},"src/interface/IFlashLoanSolverWrapper.sol":{"keccak256":"0xcd22c2a72f4b0cb7a52c75d66ad801d2541a7117367ad7a9621e44aea9d0d051","urls":["bzz-raw://76adab1e0be8d955ebde37a752ff1e38767cf64a2fbdcb95f1dd3b8dccd0a2b6","dweb:/ipfs/QmfWgmUg7kCyHq66qYVvhZk4rb16xW7emWJ4otLMqGRQzY"],"license":"GPL-3.0-or-later"},"src/library/LoansWithSettlement.sol":{"keccak256":"0x0a2093a67a219184cb02a40217ed9d662da8562ac8625ee5f61b235608990a69","urls":["bzz-raw://6f8b14b11ae34e4aa574728fc64acdee55cdef5a222e228362b8d7d941494a66","dweb:/ipfs/QmXsq4jejQGr1YtYppEnjUYRxYEv762b6HWhQ2ohpFhuzM"],"license":"GPL-3.0-or-later"},"src/mixin/FlashLoanSolverWrapper.sol":{"keccak256":"0x95ccd3376d6c5716b7fbaace452839889184d08daf000a017e4c2f37f1fba47a","urls":["bzz-raw://4750bf1520deb639baab99a8dd4bf32dc02019ba30833117d5eb9200fa120141","dweb:/ipfs/QmYwL4DicSTc43jYu5P5U3xXMXMbMi39xGKHycfmzTn1aL"],"license":"GPL-3.0-or-later"},"src/vendored/IAaveFlashLoanReceiver.sol":{"keccak256":"0x9cb252053b01fd8c223086172a94322e364f3b91f4ff12c1749159828a1e34dc","urls":["bzz-raw://371f289a2b4ff229ddb1ebf605bc05553b0f434d2419cbe2d0141facbc826d7c","dweb:/ipfs/QmdYkSRXCWgUg7uyVW6U89PAQgcwbm4UqPrpsewXF37V4T"],"license":"MIT"},"src/vendored/IAavePool.sol":{"keccak256":"0x2086e04514230a6759d9c41147d4f7e9ee4810e3a2c700ef06a17459c916eeb8","urls":["bzz-raw://a94216a796da8e1cbebd19e3ca69bc509c395157ef369a60ca4063c060bfe4c5","dweb:/ipfs/QmW2ZDANxiBqYaaptspF4EHqskpDPAzFasea2uTtbHH5ZD"],"license":"MIT"},"src/vendored/ICowAuthentication.sol":{"keccak256":"0xc0b8ec08ce1e4ed2af4188ab08281b1a894d74b2dc69e045398e789f5a72d140","urls":["bzz-raw://41917d1ba4f6846ddd52d7fe16189a14a836d62b255e3ad07ebbd46f33cdbfa4","dweb:/ipfs/QmXTzjA9qoNX3aHehLJqMyx7qpZKitPE1Un3buB5QtKqfq"],"license":"LGPL-3.0-or-later"},"src/vendored/IERC20.sol":{"keccak256":"0x1b72641f69f5a2156fc2319a6b08daa0c4b5b224d0a776b85a5fcae428af72c2","urls":["bzz-raw://261d26fbf99877e2d4b5a7e6b2b2defee5ba5cab5bfdfab231fb5f2e2dfb1fdb","dweb:/ipfs/QmSpQuVkTHXzH1CNWmgtnCLz9CNEMD5g5sciiXRkrbYUJt"],"license":"MIT"}},"version":1},"id":25} + diff --git a/crates/contracts/artifacts/ERC3156FlashLoanSolverWrapper.json b/crates/contracts/artifacts/ERC3156FlashLoanSolverWrapper.json index df96290a8e..c6bb7bfe94 100644 --- a/crates/contracts/artifacts/ERC3156FlashLoanSolverWrapper.json +++ b/crates/contracts/artifacts/ERC3156FlashLoanSolverWrapper.json @@ -1 +1,2 @@ -{"abi":[{"type":"constructor","inputs":[{"name":"_settlementContract","type":"address","internalType":"contract ICowSettlement"}],"stateMutability":"nonpayable"},{"type":"function","name":"approve","inputs":[{"name":"token","type":"address","internalType":"contract IERC20"},{"name":"target","type":"address","internalType":"address"},{"name":"amount","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"flashLoanAndSettle","inputs":[{"name":"lender","type":"address","internalType":"address"},{"name":"loan","type":"tuple","internalType":"struct IFlashLoanSolverWrapper.LoanRequest","components":[{"name":"token","type":"address","internalType":"contract IERC20"},{"name":"amount","type":"uint256","internalType":"uint256"}]},{"name":"settlement","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"onFlashLoan","inputs":[{"name":"","type":"address","internalType":"address"},{"name":"","type":"address","internalType":"address"},{"name":"","type":"uint256","internalType":"uint256"},{"name":"","type":"uint256","internalType":"uint256"},{"name":"","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"nonpayable"},{"type":"function","name":"settlementAuthentication","inputs":[],"outputs":[{"name":"","type":"address","internalType":"contract ICowAuthentication"}],"stateMutability":"view"},{"type":"function","name":"settlementContract","inputs":[],"outputs":[{"name":"","type":"address","internalType":"contract ICowSettlement"}],"stateMutability":"view"}],"bytecode":"0x60c060405234801561000f575f5ffd5b50604051610c33380380610c3383398101604081905261002e916100d5565b80806001600160a01b03166080816001600160a01b031681525050806001600160a01b0316632335c76b6040518163ffffffff1660e01b81526004016020604051808303815f875af1158015610086573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906100aa91906100d5565b6001600160a01b031660a052506100f79050565b6001600160a01b03811681146100d2575f5ffd5b50565b5f602082840312156100e5575f5ffd5b81516100f0816100be565b9392505050565b60805160a051610b0861012b5f395f8181606d015261033101525f818160f40152818161017601526105e50152610b085ff3fe608060405234801561000f575f5ffd5b5060043610610064575f3560e01c8063e1f21c671161004d578063e1f21c67146100da578063ea42418b146100ef578063f24cc7eb14610116575f5ffd5b806302ebcbea1461006857806323e30c8b146100b9575b5f5ffd5b61008f7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b6100cc6100c7366004610911565b610129565b6040519081526020016100b0565b6100ed6100e8366004610988565b61015e565b005b61008f7f000000000000000000000000000000000000000000000000000000000000000081565b6100ed6101243660046109c6565b610303565b5f610132610565565b507f439148f0bbc682ca079e46d6e2c2f0c1e3b820f1a291b069d8882abf8cf18dd99695505050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610202576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f4f6e6c792063616c6c61626c6520696e206120736574746c656d656e7400000060448201526064015b60405180910390fd5b6040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83811660048301526024820183905284169063095ea7b3906044016020604051808303815f875af1158015610274573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906102989190610a4e565b6102fe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f417070726f76616c206661696c6564000000000000000000000000000000000060448201526064016101f9565b505050565b6040517f02cc250d0000000000000000000000000000000000000000000000000000000081523360048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906302cc250d90602401602060405180830381865afa15801561038b573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103af9190610a4e565b610415576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f4f6e6c792063616c6c61626c65206279206120736f6c7665720000000000000060448201526064016101f9565b7f13d79a0b0000000000000000000000000000000000000000000000000000000061044083836106d5565b7fffffffff0000000000000000000000000000000000000000000000000000000016146104c9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f4f6e6c7920736574746c65282920697320616c6c6f776564000000000000000060448201526064016101f9565b5f5c15610532576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f50656e64696e6720736574746c656d656e74000000000000000000000000000060448201526064016101f9565b61053c82826106e8565b6105578461054d6020860186610a74565b856020013561070d565b61055f610823565b50505050565b5f61056e61082a565b90505f8151116105da576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f20736574746c656d656e742070656e64696e67000000000000000000000060448201526064016101f9565b6105e2610823565b5f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16826040516106289190610a8f565b5f604051808303815f865af19150503d805f8114610661576040519150601f19603f3d011682016040523d82523d5f602084013e610666565b606091505b50509050806106d1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f536574746c656d656e742072657665727465640000000000000000000000000060448201526064016101f9565b5050565b5f600482106106e2575081355b92915050565b80805f5d505f5b818110156102fe5780830135602082041981815d50506020016106ef565b6040517f5cffe9de00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff838116602483015260448201839052608060648301525f608483018190529190851690635cffe9de9060a4016020604051808303815f875af1158015610796573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107ba9190610a4e565b90508061055f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f466c617368206c6f616e2077617320756e7375636365737366756c000000000060448201526064016101f9565b5f80805d50565b60605f5c6020601f820181900490810267ffffffffffffffff81111561085257610852610aa5565b6040519080825280601f01601f19166020018201604052801561087c576020820181803683370190505b509250825f5b8281101561089f5780195c60206001909201918202830152610882565b50919091525090565b73ffffffffffffffffffffffffffffffffffffffff811681146108c9575f5ffd5b50565b5f5f83601f8401126108dc575f5ffd5b50813567ffffffffffffffff8111156108f3575f5ffd5b60208301915083602082850101111561090a575f5ffd5b9250929050565b5f5f5f5f5f5f60a08789031215610926575f5ffd5b8635610931816108a8565b95506020870135610941816108a8565b94506040870135935060608701359250608087013567ffffffffffffffff81111561096a575f5ffd5b61097689828a016108cc565b979a9699509497509295939492505050565b5f5f5f6060848603121561099a575f5ffd5b83356109a5816108a8565b925060208401356109b5816108a8565b929592945050506040919091013590565b5f5f5f5f84860360808112156109da575f5ffd5b85356109e5816108a8565b945060407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe082011215610a16575f5ffd5b50602085019250606085013567ffffffffffffffff811115610a36575f5ffd5b610a42878288016108cc565b95989497509550505050565b5f60208284031215610a5e575f5ffd5b81518015158114610a6d575f5ffd5b9392505050565b5f60208284031215610a84575f5ffd5b8135610a6d816108a8565b5f82518060208501845e5f920191825250919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffdfea26469706673582212209a9b5b578f25405602569a917ab66648a6e2e2b3a751353d6099c3eee88fb50464736f6c634300081c0033","deployedBytecode":"0x608060405234801561000f575f5ffd5b5060043610610064575f3560e01c8063e1f21c671161004d578063e1f21c67146100da578063ea42418b146100ef578063f24cc7eb14610116575f5ffd5b806302ebcbea1461006857806323e30c8b146100b9575b5f5ffd5b61008f7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b6100cc6100c7366004610911565b610129565b6040519081526020016100b0565b6100ed6100e8366004610988565b61015e565b005b61008f7f000000000000000000000000000000000000000000000000000000000000000081565b6100ed6101243660046109c6565b610303565b5f610132610565565b507f439148f0bbc682ca079e46d6e2c2f0c1e3b820f1a291b069d8882abf8cf18dd99695505050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610202576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f4f6e6c792063616c6c61626c6520696e206120736574746c656d656e7400000060448201526064015b60405180910390fd5b6040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83811660048301526024820183905284169063095ea7b3906044016020604051808303815f875af1158015610274573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906102989190610a4e565b6102fe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f417070726f76616c206661696c6564000000000000000000000000000000000060448201526064016101f9565b505050565b6040517f02cc250d0000000000000000000000000000000000000000000000000000000081523360048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906302cc250d90602401602060405180830381865afa15801561038b573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103af9190610a4e565b610415576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f4f6e6c792063616c6c61626c65206279206120736f6c7665720000000000000060448201526064016101f9565b7f13d79a0b0000000000000000000000000000000000000000000000000000000061044083836106d5565b7fffffffff0000000000000000000000000000000000000000000000000000000016146104c9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f4f6e6c7920736574746c65282920697320616c6c6f776564000000000000000060448201526064016101f9565b5f5c15610532576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f50656e64696e6720736574746c656d656e74000000000000000000000000000060448201526064016101f9565b61053c82826106e8565b6105578461054d6020860186610a74565b856020013561070d565b61055f610823565b50505050565b5f61056e61082a565b90505f8151116105da576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4e6f20736574746c656d656e742070656e64696e67000000000000000000000060448201526064016101f9565b6105e2610823565b5f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16826040516106289190610a8f565b5f604051808303815f865af19150503d805f8114610661576040519150601f19603f3d011682016040523d82523d5f602084013e610666565b606091505b50509050806106d1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f536574746c656d656e742072657665727465640000000000000000000000000060448201526064016101f9565b5050565b5f600482106106e2575081355b92915050565b80805f5d505f5b818110156102fe5780830135602082041981815d50506020016106ef565b6040517f5cffe9de00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff838116602483015260448201839052608060648301525f608483018190529190851690635cffe9de9060a4016020604051808303815f875af1158015610796573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107ba9190610a4e565b90508061055f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f466c617368206c6f616e2077617320756e7375636365737366756c000000000060448201526064016101f9565b5f80805d50565b60605f5c6020601f820181900490810267ffffffffffffffff81111561085257610852610aa5565b6040519080825280601f01601f19166020018201604052801561087c576020820181803683370190505b509250825f5b8281101561089f5780195c60206001909201918202830152610882565b50919091525090565b73ffffffffffffffffffffffffffffffffffffffff811681146108c9575f5ffd5b50565b5f5f83601f8401126108dc575f5ffd5b50813567ffffffffffffffff8111156108f3575f5ffd5b60208301915083602082850101111561090a575f5ffd5b9250929050565b5f5f5f5f5f5f60a08789031215610926575f5ffd5b8635610931816108a8565b95506020870135610941816108a8565b94506040870135935060608701359250608087013567ffffffffffffffff81111561096a575f5ffd5b61097689828a016108cc565b979a9699509497509295939492505050565b5f5f5f6060848603121561099a575f5ffd5b83356109a5816108a8565b925060208401356109b5816108a8565b929592945050506040919091013590565b5f5f5f5f84860360808112156109da575f5ffd5b85356109e5816108a8565b945060407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe082011215610a16575f5ffd5b50602085019250606085013567ffffffffffffffff811115610a36575f5ffd5b610a42878288016108cc565b95989497509550505050565b5f60208284031215610a5e575f5ffd5b81518015158114610a6d575f5ffd5b9392505050565b5f60208284031215610a84575f5ffd5b8135610a6d816108a8565b5f82518060208501845e5f920191825250919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffdfea26469706673582212209a9b5b578f25405602569a917ab66648a6e2e2b3a751353d6099c3eee88fb50464736f6c634300081c0033","methodIdentifiers":{"approve(address,address,uint256)":"e1f21c67","flashLoanAndSettle(address,(address,uint256),bytes)":"f24cc7eb","onFlashLoan(address,address,uint256,uint256,bytes)":"23e30c8b","settlementAuthentication()":"02ebcbea","settlementContract()":"ea42418b"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.28+commit.7893614a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"contract ICowSettlement\",\"name\":\"_settlementContract\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"lender\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"struct IFlashLoanSolverWrapper.LoanRequest\",\"name\":\"loan\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"settlement\",\"type\":\"bytes\"}],\"name\":\"flashLoanAndSettle\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"name\":\"onFlashLoan\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"settlementAuthentication\",\"outputs\":[{\"internalType\":\"contract ICowAuthentication\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"settlementContract\",\"outputs\":[{\"internalType\":\"contract ICowSettlement\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{\"approve(address,address,uint256)\":{\"details\":\"In general, the only way to transfer funds out of this contract is through a call to this function and a subsequent call to `transferFrom`. The allowance will be preserved across different transactions.\",\"params\":{\"amount\":\"The amount of tokens to set as the allowance.\",\"target\":\"The address that will be allowed to spend the token.\",\"token\":\"The token to approve for transferring.\"}},\"flashLoanAndSettle(address,(address,uint256),bytes)\":{\"details\":\"The repayment of a flash loan is different based on the protocol. For example, some expect to retrieve the funds from this borrower contract through `transferFrom`, while other check the lender balance is as expected after the flash loan has been processed. The executed settlement must be built to cater to the needs of the specified lender.A settlement can be executed at most once in a call. The settlement data cannot change during execution. Only the settle function can be called. All of this is also the case if the lender is untrusted.\",\"params\":{\"lender\":\"The address of the flash-loan lender from which to borrow.\",\"loan\":\"The parameters describing the requested loan.\",\"settlement\":\"The call data for a call to the `settle()` function in the CoW Protocol settlement contract. It fully describes a CoW Protocol settlement.\"}},\"onFlashLoan(address,address,uint256,uint256,bytes)\":{\"details\":\"Receive a flash loan.\",\"params\":{\"amount\":\"The amount of tokens lent.\",\"data\":\"Arbitrary data structure, intended to contain user-defined parameters.\",\"fee\":\"The additional amount of tokens to repay.\",\"initiator\":\"The initiator of the loan.\",\"token\":\"The loan currency.\"},\"returns\":{\"_0\":\"The keccak256 hash of \\\"ERC3156FlashBorrower.onFlashLoan\\\"\"}}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"approve(address,address,uint256)\":{\"notice\":\"Approves the target address to spend the specified token on behalf of the flash-loan solver wrapper up to the specified amount.\"},\"flashLoanAndSettle(address,(address,uint256),bytes)\":{\"notice\":\"Requests a flash loan with the specified parameters from the lender and, once the funds have been received, executes the settlement specified as part of the call. The flash-loan repayment is expected to take place during the settlement.\"},\"settlementAuthentication()\":{\"notice\":\"The contract responsible to determine which address is an authorized solver for CoW Protocol.\"},\"settlementContract()\":{\"notice\":\"The settlement contract that will be called when a settlement is executed after a flash loan.\"}},\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/ERC3156FlashLoanSolverWrapper.sol\":\"ERC3156FlashLoanSolverWrapper\"},\"evmVersion\":\"cancun\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":1000000},\"remappings\":[\":forge-std/=lib/forge-std/src/\"]},\"sources\":{\"src/ERC3156FlashLoanSolverWrapper.sol\":{\"keccak256\":\"0xbd5528a2a3bd1e5d0baa16673077c1ba2f43e2e9e1a4ce4ce60e320af6f4d1a8\",\"license\":\"GPL-3.0-or-later\",\"urls\":[\"bzz-raw://65d64af23e6bf50a4db8f9284989e176bf9c50536795c212bedfcd586975be54\",\"dweb:/ipfs/QmdA9z9j3bQ4TSP2qpGXX2EWGSFqEXybJECHc6267SLuSN\"]},\"src/interface/ICowSettlement.sol\":{\"keccak256\":\"0x936d8038ce833e625c7c306b5890ba85c4e5bf8adc8b7e41227a680c4039e244\",\"license\":\"GPL-3.0-or-later\",\"urls\":[\"bzz-raw://138b144cac2d5cd0b3bbd4b0f73817b1e611d90854734f64b98b15e709aa7b5e\",\"dweb:/ipfs/Qmf6rcxHBToX7Ym7zHXP8BRaWwD7DLptuZqYq3YDcd7jvJ\"]},\"src/interface/IFlashLoanSolverWrapper.sol\":{\"keccak256\":\"0x28d637bc299e016ffd0ed3e1b0d2a6c57887a1dd95d91355e4de81c5e733e844\",\"license\":\"GPL-3.0-or-later\",\"urls\":[\"bzz-raw://0812acca6ee68fef89d5091ce838c214e1d6da29ea15d1bb611929907c65e378\",\"dweb:/ipfs/QmSwKuoMKWjK8dbJnmagQ2u5nqYwwfFsBFaDNQiQyy5GS1\"]},\"src/mixin/FlashLoanSolverWrapper.sol\":{\"keccak256\":\"0xc5085256e1779663463e15b6dc7c411c20ba5811ca5ed3088823b52a17c5e18f\",\"license\":\"GPL-3.0-or-later\",\"urls\":[\"bzz-raw://970788daf4b538f66877dc1a9ae097227424f8187375034f2f8c800656fd0909\",\"dweb:/ipfs/QmbucPkUkEW4f1V95yeKFRcFD7ukPXQsJmHcP4i7JohvuT\"]},\"src/mixin/TransientStorageArray.sol\":{\"keccak256\":\"0xd3527c2733c6a1131e3c321746405f946cfb36528c032bd84b685b75e2dd9274\",\"license\":\"GPL-3.0-or-later\",\"urls\":[\"bzz-raw://6836bd40e1bcb264d20a0b9bce6dbc58058f2580c133ceb8d328b3ec464c0228\",\"dweb:/ipfs/QmWFXB6HrzsHUvCQFykZoARqhMpeHVSbY442qQzG4UpfUr\"]},\"src/vendored/ICowAuthentication.sol\":{\"keccak256\":\"0xc0b8ec08ce1e4ed2af4188ab08281b1a894d74b2dc69e045398e789f5a72d140\",\"license\":\"LGPL-3.0-or-later\",\"urls\":[\"bzz-raw://41917d1ba4f6846ddd52d7fe16189a14a836d62b255e3ad07ebbd46f33cdbfa4\",\"dweb:/ipfs/QmXTzjA9qoNX3aHehLJqMyx7qpZKitPE1Un3buB5QtKqfq\"]},\"src/vendored/IERC20.sol\":{\"keccak256\":\"0x1b72641f69f5a2156fc2319a6b08daa0c4b5b224d0a776b85a5fcae428af72c2\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://261d26fbf99877e2d4b5a7e6b2b2defee5ba5cab5bfdfab231fb5f2e2dfb1fdb\",\"dweb:/ipfs/QmSpQuVkTHXzH1CNWmgtnCLz9CNEMD5g5sciiXRkrbYUJt\"]},\"src/vendored/IERC3156FlashBorrower.sol\":{\"keccak256\":\"0x48d97659a73cb7d14bbae680c75a337f1ea0327d3f2dd0a60c8c84625a62d518\",\"license\":\"CC0-1.0\",\"urls\":[\"bzz-raw://846a4ce9cc9d6aa834b6a38b753edc3593276c3565d8fec833499fcbfcedd067\",\"dweb:/ipfs/QmZFZiD3RcRDktLZ42B1yKNFhGpDBB47HtNnV8VbJETJmL\"]},\"src/vendored/IERC3156FlashLender.sol\":{\"keccak256\":\"0x5b4a9ebac4d00400465d2868697f33a7ba3e66842d8375adb4a1b278d453a71c\",\"license\":\"CC0-1.0\",\"urls\":[\"bzz-raw://063501976d5e34e8cfa860ea62717b7862fb76a70ac92b099d0bdb62c3c684df\",\"dweb:/ipfs/QmRpztSSUsk5H4Bs5m9638f24S2EiboYZrUQFMXdswPHqM\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.28+commit.7893614a"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"contract ICowSettlement","name":"_settlementContract","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"nonpayable","type":"function","name":"approve"},{"inputs":[{"internalType":"address","name":"lender","type":"address"},{"internalType":"struct IFlashLoanSolverWrapper.LoanRequest","name":"loan","type":"tuple","components":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}]},{"internalType":"bytes","name":"settlement","type":"bytes"}],"stateMutability":"nonpayable","type":"function","name":"flashLoanAndSettle"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"nonpayable","type":"function","name":"onFlashLoan","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"settlementAuthentication","outputs":[{"internalType":"contract ICowAuthentication","name":"","type":"address"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"settlementContract","outputs":[{"internalType":"contract ICowSettlement","name":"","type":"address"}]}],"devdoc":{"kind":"dev","methods":{"approve(address,address,uint256)":{"details":"In general, the only way to transfer funds out of this contract is through a call to this function and a subsequent call to `transferFrom`. The allowance will be preserved across different transactions.","params":{"amount":"The amount of tokens to set as the allowance.","target":"The address that will be allowed to spend the token.","token":"The token to approve for transferring."}},"flashLoanAndSettle(address,(address,uint256),bytes)":{"details":"The repayment of a flash loan is different based on the protocol. For example, some expect to retrieve the funds from this borrower contract through `transferFrom`, while other check the lender balance is as expected after the flash loan has been processed. The executed settlement must be built to cater to the needs of the specified lender.A settlement can be executed at most once in a call. The settlement data cannot change during execution. Only the settle function can be called. All of this is also the case if the lender is untrusted.","params":{"lender":"The address of the flash-loan lender from which to borrow.","loan":"The parameters describing the requested loan.","settlement":"The call data for a call to the `settle()` function in the CoW Protocol settlement contract. It fully describes a CoW Protocol settlement."}},"onFlashLoan(address,address,uint256,uint256,bytes)":{"details":"Receive a flash loan.","params":{"amount":"The amount of tokens lent.","data":"Arbitrary data structure, intended to contain user-defined parameters.","fee":"The additional amount of tokens to repay.","initiator":"The initiator of the loan.","token":"The loan currency."},"returns":{"_0":"The keccak256 hash of \"ERC3156FlashBorrower.onFlashLoan\""}}},"version":1},"userdoc":{"kind":"user","methods":{"approve(address,address,uint256)":{"notice":"Approves the target address to spend the specified token on behalf of the flash-loan solver wrapper up to the specified amount."},"flashLoanAndSettle(address,(address,uint256),bytes)":{"notice":"Requests a flash loan with the specified parameters from the lender and, once the funds have been received, executes the settlement specified as part of the call. The flash-loan repayment is expected to take place during the settlement."},"settlementAuthentication()":{"notice":"The contract responsible to determine which address is an authorized solver for CoW Protocol."},"settlementContract()":{"notice":"The settlement contract that will be called when a settlement is executed after a flash loan."}},"version":1}},"settings":{"remappings":["forge-std/=lib/forge-std/src/"],"optimizer":{"enabled":true,"runs":1000000},"metadata":{"bytecodeHash":"ipfs"},"compilationTarget":{"src/ERC3156FlashLoanSolverWrapper.sol":"ERC3156FlashLoanSolverWrapper"},"evmVersion":"cancun","libraries":{}},"sources":{"src/ERC3156FlashLoanSolverWrapper.sol":{"keccak256":"0xbd5528a2a3bd1e5d0baa16673077c1ba2f43e2e9e1a4ce4ce60e320af6f4d1a8","urls":["bzz-raw://65d64af23e6bf50a4db8f9284989e176bf9c50536795c212bedfcd586975be54","dweb:/ipfs/QmdA9z9j3bQ4TSP2qpGXX2EWGSFqEXybJECHc6267SLuSN"],"license":"GPL-3.0-or-later"},"src/interface/ICowSettlement.sol":{"keccak256":"0x936d8038ce833e625c7c306b5890ba85c4e5bf8adc8b7e41227a680c4039e244","urls":["bzz-raw://138b144cac2d5cd0b3bbd4b0f73817b1e611d90854734f64b98b15e709aa7b5e","dweb:/ipfs/Qmf6rcxHBToX7Ym7zHXP8BRaWwD7DLptuZqYq3YDcd7jvJ"],"license":"GPL-3.0-or-later"},"src/interface/IFlashLoanSolverWrapper.sol":{"keccak256":"0x28d637bc299e016ffd0ed3e1b0d2a6c57887a1dd95d91355e4de81c5e733e844","urls":["bzz-raw://0812acca6ee68fef89d5091ce838c214e1d6da29ea15d1bb611929907c65e378","dweb:/ipfs/QmSwKuoMKWjK8dbJnmagQ2u5nqYwwfFsBFaDNQiQyy5GS1"],"license":"GPL-3.0-or-later"},"src/mixin/FlashLoanSolverWrapper.sol":{"keccak256":"0xc5085256e1779663463e15b6dc7c411c20ba5811ca5ed3088823b52a17c5e18f","urls":["bzz-raw://970788daf4b538f66877dc1a9ae097227424f8187375034f2f8c800656fd0909","dweb:/ipfs/QmbucPkUkEW4f1V95yeKFRcFD7ukPXQsJmHcP4i7JohvuT"],"license":"GPL-3.0-or-later"},"src/mixin/TransientStorageArray.sol":{"keccak256":"0xd3527c2733c6a1131e3c321746405f946cfb36528c032bd84b685b75e2dd9274","urls":["bzz-raw://6836bd40e1bcb264d20a0b9bce6dbc58058f2580c133ceb8d328b3ec464c0228","dweb:/ipfs/QmWFXB6HrzsHUvCQFykZoARqhMpeHVSbY442qQzG4UpfUr"],"license":"GPL-3.0-or-later"},"src/vendored/ICowAuthentication.sol":{"keccak256":"0xc0b8ec08ce1e4ed2af4188ab08281b1a894d74b2dc69e045398e789f5a72d140","urls":["bzz-raw://41917d1ba4f6846ddd52d7fe16189a14a836d62b255e3ad07ebbd46f33cdbfa4","dweb:/ipfs/QmXTzjA9qoNX3aHehLJqMyx7qpZKitPE1Un3buB5QtKqfq"],"license":"LGPL-3.0-or-later"},"src/vendored/IERC20.sol":{"keccak256":"0x1b72641f69f5a2156fc2319a6b08daa0c4b5b224d0a776b85a5fcae428af72c2","urls":["bzz-raw://261d26fbf99877e2d4b5a7e6b2b2defee5ba5cab5bfdfab231fb5f2e2dfb1fdb","dweb:/ipfs/QmSpQuVkTHXzH1CNWmgtnCLz9CNEMD5g5sciiXRkrbYUJt"],"license":"MIT"},"src/vendored/IERC3156FlashBorrower.sol":{"keccak256":"0x48d97659a73cb7d14bbae680c75a337f1ea0327d3f2dd0a60c8c84625a62d518","urls":["bzz-raw://846a4ce9cc9d6aa834b6a38b753edc3593276c3565d8fec833499fcbfcedd067","dweb:/ipfs/QmZFZiD3RcRDktLZ42B1yKNFhGpDBB47HtNnV8VbJETJmL"],"license":"CC0-1.0"},"src/vendored/IERC3156FlashLender.sol":{"keccak256":"0x5b4a9ebac4d00400465d2868697f33a7ba3e66842d8375adb4a1b278d453a71c","urls":["bzz-raw://063501976d5e34e8cfa860ea62717b7862fb76a70ac92b099d0bdb62c3c684df","dweb:/ipfs/QmRpztSSUsk5H4Bs5m9638f24S2EiboYZrUQFMXdswPHqM"],"license":"CC0-1.0"}},"version":1},"id":26} \ No newline at end of file +{"abi":[{"type":"constructor","inputs":[{"name":"_router","type":"address","internalType":"contract IFlashLoanRouter"}],"stateMutability":"nonpayable"},{"type":"function","name":"approve","inputs":[{"name":"token","type":"address","internalType":"contract IERC20"},{"name":"target","type":"address","internalType":"address"},{"name":"amount","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"flashLoanAndCallBack","inputs":[{"name":"lender","type":"address","internalType":"address"},{"name":"loan","type":"tuple","internalType":"struct IFlashLoanSolverWrapper.LoanRequest","components":[{"name":"token","type":"address","internalType":"contract IERC20"},{"name":"amount","type":"uint256","internalType":"uint256"}]},{"name":"_callbackDataHash","type":"bytes32","internalType":"bytes32"},{"name":"callbackData","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"onFlashLoan","inputs":[{"name":"","type":"address","internalType":"address"},{"name":"","type":"address","internalType":"address"},{"name":"","type":"uint256","internalType":"uint256"},{"name":"","type":"uint256","internalType":"uint256"},{"name":"callbackData","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"nonpayable"},{"type":"function","name":"router","inputs":[],"outputs":[{"name":"","type":"address","internalType":"contract IFlashLoanRouter"}],"stateMutability":"view"},{"type":"function","name":"settlementContract","inputs":[],"outputs":[{"name":"","type":"address","internalType":"contract ICowSettlement"}],"stateMutability":"view"}],"bytecode":"0x60c060405234801561000f575f5ffd5b50604051610acb380380610acb83398101604081905261002e916100d5565b80806001600160a01b03166080816001600160a01b031681525050806001600160a01b031663ea42418b6040518163ffffffff1660e01b81526004016020604051808303815f875af1158015610086573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906100aa91906100d5565b6001600160a01b031660a052506100f79050565b6001600160a01b03811681146100d2575f5ffd5b50565b5f602082840312156100e5575f5ffd5b81516100f0816100be565b9392505050565b60805160a05161099f61012c5f395f818160bb015261031b01525f8181610107015281816101ac0152610555015261099f5ff3fe608060405234801561000f575f5ffd5b5060043610610064575f3560e01c8063e1f21c671161004d578063e1f21c67146100a3578063ea42418b146100b6578063f887ea4014610102575f5ffd5b806323e30c8b14610068578063e0bbec771461008e575b5f5ffd5b61007b610076366004610726565b610129565b6040519081526020015b60405180910390f35b6100a161009c36600461079d565b610194565b005b6100a16100b1366004610832565b610303565b6100dd7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610085565b6100dd7f000000000000000000000000000000000000000000000000000000000000000081565b5f61016883838080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152506104a392505050565b507f439148f0bbc682ca079e46d6e2c2f0c1e3b820f1a291b069d8882abf8cf18dd99695505050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610238576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f4f6e6c792063616c6c61626c6520696e206120736574746c656d656e7400000060448201526064015b60405180910390fd5b5f5c156102a1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f50656e64696e672063616c6c6261636b00000000000000000000000000000000604482015260640161022f565b82805f5d506102f7856102b76020870187610870565b866020013585858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152506105ba92505050565b5f80805d505050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146103a2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f4f6e6c792063616c6c61626c6520696e206120736574746c656d656e74000000604482015260640161022f565b6040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83811660048301526024820183905284169063095ea7b3906044016020604051808303815f875af1158015610414573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104389190610892565b61049e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f417070726f76616c206661696c65640000000000000000000000000000000000604482015260640161022f565b505050565b805160208201205f5c14610513576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f43616c6c6261636b20646174612068617368206e6f74206d61746368696e6700604482015260640161022f565b5f80805d506040517f0efb1fb600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690630efb1fb69061058a9084906004016108fd565b5f604051808303815f87803b1580156105a1575f5ffd5b505af11580156105b3573d5f5f3e3d5ffd5b5050505050565b6040517f5cffe9de0000000000000000000000000000000000000000000000000000000081525f9073ffffffffffffffffffffffffffffffffffffffff861690635cffe9de9061061490309088908890889060040161090f565b6020604051808303815f875af1158015610630573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106549190610892565b9050806105b3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f466c617368206c6f616e2077617320756e7375636365737366756c0000000000604482015260640161022f565b73ffffffffffffffffffffffffffffffffffffffff811681146106de575f5ffd5b50565b5f5f83601f8401126106f1575f5ffd5b50813567ffffffffffffffff811115610708575f5ffd5b60208301915083602082850101111561071f575f5ffd5b9250929050565b5f5f5f5f5f5f60a0878903121561073b575f5ffd5b8635610746816106bd565b95506020870135610756816106bd565b94506040870135935060608701359250608087013567ffffffffffffffff81111561077f575f5ffd5b61078b89828a016106e1565b979a9699509497509295939492505050565b5f5f5f5f5f85870360a08112156107b2575f5ffd5b86356107bd816106bd565b955060407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0820112156107ee575f5ffd5b5060208601935060608601359250608086013567ffffffffffffffff811115610815575f5ffd5b610821888289016106e1565b969995985093965092949392505050565b5f5f5f60608486031215610844575f5ffd5b833561084f816106bd565b9250602084013561085f816106bd565b929592945050506040919091013590565b5f60208284031215610880575f5ffd5b813561088b816106bd565b9392505050565b5f602082840312156108a2575f5ffd5b8151801515811461088b575f5ffd5b5f81518084528060208401602086015e5f6020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b602081525f61088b60208301846108b1565b73ffffffffffffffffffffffffffffffffffffffff8516815273ffffffffffffffffffffffffffffffffffffffff84166020820152826040820152608060608201525f61095f60808301846108b1565b969550505050505056fea2646970667358221220e855fe1c7830c4f76e257e5627562525e429bdd63b14818ecefabb05dd1e2a3564736f6c634300081c0033","deployedBytecode":"0x608060405234801561000f575f5ffd5b5060043610610064575f3560e01c8063e1f21c671161004d578063e1f21c67146100a3578063ea42418b146100b6578063f887ea4014610102575f5ffd5b806323e30c8b14610068578063e0bbec771461008e575b5f5ffd5b61007b610076366004610726565b610129565b6040519081526020015b60405180910390f35b6100a161009c36600461079d565b610194565b005b6100a16100b1366004610832565b610303565b6100dd7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610085565b6100dd7f000000000000000000000000000000000000000000000000000000000000000081565b5f61016883838080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152506104a392505050565b507f439148f0bbc682ca079e46d6e2c2f0c1e3b820f1a291b069d8882abf8cf18dd99695505050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610238576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f4f6e6c792063616c6c61626c6520696e206120736574746c656d656e7400000060448201526064015b60405180910390fd5b5f5c156102a1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f50656e64696e672063616c6c6261636b00000000000000000000000000000000604482015260640161022f565b82805f5d506102f7856102b76020870187610870565b866020013585858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152506105ba92505050565b5f80805d505050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146103a2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f4f6e6c792063616c6c61626c6520696e206120736574746c656d656e74000000604482015260640161022f565b6040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83811660048301526024820183905284169063095ea7b3906044016020604051808303815f875af1158015610414573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104389190610892565b61049e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f417070726f76616c206661696c65640000000000000000000000000000000000604482015260640161022f565b505050565b805160208201205f5c14610513576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f43616c6c6261636b20646174612068617368206e6f74206d61746368696e6700604482015260640161022f565b5f80805d506040517f0efb1fb600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690630efb1fb69061058a9084906004016108fd565b5f604051808303815f87803b1580156105a1575f5ffd5b505af11580156105b3573d5f5f3e3d5ffd5b5050505050565b6040517f5cffe9de0000000000000000000000000000000000000000000000000000000081525f9073ffffffffffffffffffffffffffffffffffffffff861690635cffe9de9061061490309088908890889060040161090f565b6020604051808303815f875af1158015610630573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106549190610892565b9050806105b3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f466c617368206c6f616e2077617320756e7375636365737366756c0000000000604482015260640161022f565b73ffffffffffffffffffffffffffffffffffffffff811681146106de575f5ffd5b50565b5f5f83601f8401126106f1575f5ffd5b50813567ffffffffffffffff811115610708575f5ffd5b60208301915083602082850101111561071f575f5ffd5b9250929050565b5f5f5f5f5f5f60a0878903121561073b575f5ffd5b8635610746816106bd565b95506020870135610756816106bd565b94506040870135935060608701359250608087013567ffffffffffffffff81111561077f575f5ffd5b61078b89828a016106e1565b979a9699509497509295939492505050565b5f5f5f5f5f85870360a08112156107b2575f5ffd5b86356107bd816106bd565b955060407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0820112156107ee575f5ffd5b5060208601935060608601359250608086013567ffffffffffffffff811115610815575f5ffd5b610821888289016106e1565b969995985093965092949392505050565b5f5f5f60608486031215610844575f5ffd5b833561084f816106bd565b9250602084013561085f816106bd565b929592945050506040919091013590565b5f60208284031215610880575f5ffd5b813561088b816106bd565b9392505050565b5f602082840312156108a2575f5ffd5b8151801515811461088b575f5ffd5b5f81518084528060208401602086015e5f6020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b602081525f61088b60208301846108b1565b73ffffffffffffffffffffffffffffffffffffffff8516815273ffffffffffffffffffffffffffffffffffffffff84166020820152826040820152608060608201525f61095f60808301846108b1565b969550505050505056fea2646970667358221220e855fe1c7830c4f76e257e5627562525e429bdd63b14818ecefabb05dd1e2a3564736f6c634300081c0033","methodIdentifiers":{"approve(address,address,uint256)":"e1f21c67","flashLoanAndCallBack(address,(address,uint256),bytes32,bytes)":"e0bbec77","onFlashLoan(address,address,uint256,uint256,bytes)":"23e30c8b","router()":"f887ea40","settlementContract()":"ea42418b"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.28+commit.7893614a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"contract IFlashLoanRouter\",\"name\":\"_router\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"lender\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"struct IFlashLoanSolverWrapper.LoanRequest\",\"name\":\"loan\",\"type\":\"tuple\"},{\"internalType\":\"bytes32\",\"name\":\"_callbackDataHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"callbackData\",\"type\":\"bytes\"}],\"name\":\"flashLoanAndCallBack\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"callbackData\",\"type\":\"bytes\"}],\"name\":\"onFlashLoan\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"router\",\"outputs\":[{\"internalType\":\"contract IFlashLoanRouter\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"settlementContract\",\"outputs\":[{\"internalType\":\"contract ICowSettlement\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{\"approve(address,address,uint256)\":{\"details\":\"In general, the only way to transfer funds out of this contract is through a call to this function and a subsequent call to `transferFrom`. The allowance will be preserved across different transactions.\",\"params\":{\"amount\":\"The amount of tokens to set as the allowance.\",\"target\":\"The address that will be allowed to spend the token.\",\"token\":\"The token to approve for transferring.\"}},\"flashLoanAndCallBack(address,(address,uint256),bytes32,bytes)\":{\"details\":\"The repayment of a flash loan is different based on the protocol. For example, some expect to retrieve the funds from this borrower contract through `transferFrom`, while other check the lender balance is as expected after the flash loan has been processed. The executed settlement must be built to cater to the needs of the specified lender.A settlement can be executed at most once in a call. The settlement data cannot change during execution. Only the settle function can be called. All of this is also the case if the lender is untrusted.\",\"params\":{\"callbackData\":\"The data to send back when calling the router once the loan is received.\",\"callbackDataHash\":\"The keccak256 hash of the input callback data.\",\"lender\":\"The address of the flash-loan lender from which to borrow.\",\"loan\":\"The parameters describing the requested loan.\"}},\"onFlashLoan(address,address,uint256,uint256,bytes)\":{\"details\":\"Receive a flash loan.\",\"params\":{\"amount\":\"The amount of tokens lent.\",\"data\":\"Arbitrary data structure, intended to contain user-defined parameters.\",\"fee\":\"The additional amount of tokens to repay.\",\"initiator\":\"The initiator of the loan.\",\"token\":\"The loan currency.\"},\"returns\":{\"_0\":\"The keccak256 hash of \\\"ERC3156FlashBorrower.onFlashLoan\\\"\"}}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"approve(address,address,uint256)\":{\"notice\":\"Approves the target address to spend the specified token on behalf of the flash-loan solver wrapper up to the specified amount.\"},\"flashLoanAndCallBack(address,(address,uint256),bytes32,bytes)\":{\"notice\":\"Requests a flash loan with the specified parameters from the lender and, once the funds have been received, executes the settlement specified as part of the call. The flash-loan repayment is expected to take place during the settlement.\"},\"settlementContract()\":{\"notice\":\"The settlement contract that will be called when a settlement is executed after a flash loan.\"}},\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/ERC3156FlashLoanSolverWrapper.sol\":\"ERC3156FlashLoanSolverWrapper\"},\"evmVersion\":\"cancun\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":1000000},\"remappings\":[\":forge-std/=lib/forge-std/src/\"]},\"sources\":{\"src/ERC3156FlashLoanSolverWrapper.sol\":{\"keccak256\":\"0x631b1b0cf61acc007f7bd6a24eaf501d1f77aa0dfea2eb089dc5312413fb8919\",\"license\":\"GPL-3.0-or-later\",\"urls\":[\"bzz-raw://5ea97b6b8c9df6deaa0129ca681882b2407ebc8a274d4195e457c68f64407e63\",\"dweb:/ipfs/QmcDEzoKRRV1waH9L8M7WtgJeKTKVnLDpHWed2sCFhAsJN\"]},\"src/interface/ICowSettlement.sol\":{\"keccak256\":\"0x936d8038ce833e625c7c306b5890ba85c4e5bf8adc8b7e41227a680c4039e244\",\"license\":\"GPL-3.0-or-later\",\"urls\":[\"bzz-raw://138b144cac2d5cd0b3bbd4b0f73817b1e611d90854734f64b98b15e709aa7b5e\",\"dweb:/ipfs/Qmf6rcxHBToX7Ym7zHXP8BRaWwD7DLptuZqYq3YDcd7jvJ\"]},\"src/interface/IFlashLoanRouter.sol\":{\"keccak256\":\"0x72bc1e822dbbe15f75cb7e437605242fd5e8421c3f4074ad7fa0d6ba0b7991de\",\"license\":\"GPL-3.0-or-later\",\"urls\":[\"bzz-raw://63b29b2dbfe7b66d6390cd88e768b8bcc1805585eb4926102cc75a92dc2c36d5\",\"dweb:/ipfs/QmTXmGkN5CDbLAu8ykWJegZsHt9YY1MoUFWJBWPWjbYcht\"]},\"src/interface/IFlashLoanSolverWrapper.sol\":{\"keccak256\":\"0xcd22c2a72f4b0cb7a52c75d66ad801d2541a7117367ad7a9621e44aea9d0d051\",\"license\":\"GPL-3.0-or-later\",\"urls\":[\"bzz-raw://76adab1e0be8d955ebde37a752ff1e38767cf64a2fbdcb95f1dd3b8dccd0a2b6\",\"dweb:/ipfs/QmfWgmUg7kCyHq66qYVvhZk4rb16xW7emWJ4otLMqGRQzY\"]},\"src/library/LoansWithSettlement.sol\":{\"keccak256\":\"0x0a2093a67a219184cb02a40217ed9d662da8562ac8625ee5f61b235608990a69\",\"license\":\"GPL-3.0-or-later\",\"urls\":[\"bzz-raw://6f8b14b11ae34e4aa574728fc64acdee55cdef5a222e228362b8d7d941494a66\",\"dweb:/ipfs/QmXsq4jejQGr1YtYppEnjUYRxYEv762b6HWhQ2ohpFhuzM\"]},\"src/mixin/FlashLoanSolverWrapper.sol\":{\"keccak256\":\"0x95ccd3376d6c5716b7fbaace452839889184d08daf000a017e4c2f37f1fba47a\",\"license\":\"GPL-3.0-or-later\",\"urls\":[\"bzz-raw://4750bf1520deb639baab99a8dd4bf32dc02019ba30833117d5eb9200fa120141\",\"dweb:/ipfs/QmYwL4DicSTc43jYu5P5U3xXMXMbMi39xGKHycfmzTn1aL\"]},\"src/vendored/ICowAuthentication.sol\":{\"keccak256\":\"0xc0b8ec08ce1e4ed2af4188ab08281b1a894d74b2dc69e045398e789f5a72d140\",\"license\":\"LGPL-3.0-or-later\",\"urls\":[\"bzz-raw://41917d1ba4f6846ddd52d7fe16189a14a836d62b255e3ad07ebbd46f33cdbfa4\",\"dweb:/ipfs/QmXTzjA9qoNX3aHehLJqMyx7qpZKitPE1Un3buB5QtKqfq\"]},\"src/vendored/IERC20.sol\":{\"keccak256\":\"0x1b72641f69f5a2156fc2319a6b08daa0c4b5b224d0a776b85a5fcae428af72c2\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://261d26fbf99877e2d4b5a7e6b2b2defee5ba5cab5bfdfab231fb5f2e2dfb1fdb\",\"dweb:/ipfs/QmSpQuVkTHXzH1CNWmgtnCLz9CNEMD5g5sciiXRkrbYUJt\"]},\"src/vendored/IERC3156FlashBorrower.sol\":{\"keccak256\":\"0x48d97659a73cb7d14bbae680c75a337f1ea0327d3f2dd0a60c8c84625a62d518\",\"license\":\"CC0-1.0\",\"urls\":[\"bzz-raw://846a4ce9cc9d6aa834b6a38b753edc3593276c3565d8fec833499fcbfcedd067\",\"dweb:/ipfs/QmZFZiD3RcRDktLZ42B1yKNFhGpDBB47HtNnV8VbJETJmL\"]},\"src/vendored/IERC3156FlashLender.sol\":{\"keccak256\":\"0x5b4a9ebac4d00400465d2868697f33a7ba3e66842d8375adb4a1b278d453a71c\",\"license\":\"CC0-1.0\",\"urls\":[\"bzz-raw://063501976d5e34e8cfa860ea62717b7862fb76a70ac92b099d0bdb62c3c684df\",\"dweb:/ipfs/QmRpztSSUsk5H4Bs5m9638f24S2EiboYZrUQFMXdswPHqM\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.28+commit.7893614a"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"contract IFlashLoanRouter","name":"_router","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"nonpayable","type":"function","name":"approve"},{"inputs":[{"internalType":"address","name":"lender","type":"address"},{"internalType":"struct IFlashLoanSolverWrapper.LoanRequest","name":"loan","type":"tuple","components":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}]},{"internalType":"bytes32","name":"_callbackDataHash","type":"bytes32"},{"internalType":"bytes","name":"callbackData","type":"bytes"}],"stateMutability":"nonpayable","type":"function","name":"flashLoanAndCallBack"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"callbackData","type":"bytes"}],"stateMutability":"nonpayable","type":"function","name":"onFlashLoan","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"router","outputs":[{"internalType":"contract IFlashLoanRouter","name":"","type":"address"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"settlementContract","outputs":[{"internalType":"contract ICowSettlement","name":"","type":"address"}]}],"devdoc":{"kind":"dev","methods":{"approve(address,address,uint256)":{"details":"In general, the only way to transfer funds out of this contract is through a call to this function and a subsequent call to `transferFrom`. The allowance will be preserved across different transactions.","params":{"amount":"The amount of tokens to set as the allowance.","target":"The address that will be allowed to spend the token.","token":"The token to approve for transferring."}},"flashLoanAndCallBack(address,(address,uint256),bytes32,bytes)":{"details":"The repayment of a flash loan is different based on the protocol. For example, some expect to retrieve the funds from this borrower contract through `transferFrom`, while other check the lender balance is as expected after the flash loan has been processed. The executed settlement must be built to cater to the needs of the specified lender.A settlement can be executed at most once in a call. The settlement data cannot change during execution. Only the settle function can be called. All of this is also the case if the lender is untrusted.","params":{"callbackData":"The data to send back when calling the router once the loan is received.","callbackDataHash":"The keccak256 hash of the input callback data.","lender":"The address of the flash-loan lender from which to borrow.","loan":"The parameters describing the requested loan."}},"onFlashLoan(address,address,uint256,uint256,bytes)":{"details":"Receive a flash loan.","params":{"amount":"The amount of tokens lent.","data":"Arbitrary data structure, intended to contain user-defined parameters.","fee":"The additional amount of tokens to repay.","initiator":"The initiator of the loan.","token":"The loan currency."},"returns":{"_0":"The keccak256 hash of \"ERC3156FlashBorrower.onFlashLoan\""}}},"version":1},"userdoc":{"kind":"user","methods":{"approve(address,address,uint256)":{"notice":"Approves the target address to spend the specified token on behalf of the flash-loan solver wrapper up to the specified amount."},"flashLoanAndCallBack(address,(address,uint256),bytes32,bytes)":{"notice":"Requests a flash loan with the specified parameters from the lender and, once the funds have been received, executes the settlement specified as part of the call. The flash-loan repayment is expected to take place during the settlement."},"settlementContract()":{"notice":"The settlement contract that will be called when a settlement is executed after a flash loan."}},"version":1}},"settings":{"remappings":["forge-std/=lib/forge-std/src/"],"optimizer":{"enabled":true,"runs":1000000},"metadata":{"bytecodeHash":"ipfs"},"compilationTarget":{"src/ERC3156FlashLoanSolverWrapper.sol":"ERC3156FlashLoanSolverWrapper"},"evmVersion":"cancun","libraries":{}},"sources":{"src/ERC3156FlashLoanSolverWrapper.sol":{"keccak256":"0x631b1b0cf61acc007f7bd6a24eaf501d1f77aa0dfea2eb089dc5312413fb8919","urls":["bzz-raw://5ea97b6b8c9df6deaa0129ca681882b2407ebc8a274d4195e457c68f64407e63","dweb:/ipfs/QmcDEzoKRRV1waH9L8M7WtgJeKTKVnLDpHWed2sCFhAsJN"],"license":"GPL-3.0-or-later"},"src/interface/ICowSettlement.sol":{"keccak256":"0x936d8038ce833e625c7c306b5890ba85c4e5bf8adc8b7e41227a680c4039e244","urls":["bzz-raw://138b144cac2d5cd0b3bbd4b0f73817b1e611d90854734f64b98b15e709aa7b5e","dweb:/ipfs/Qmf6rcxHBToX7Ym7zHXP8BRaWwD7DLptuZqYq3YDcd7jvJ"],"license":"GPL-3.0-or-later"},"src/interface/IFlashLoanRouter.sol":{"keccak256":"0x72bc1e822dbbe15f75cb7e437605242fd5e8421c3f4074ad7fa0d6ba0b7991de","urls":["bzz-raw://63b29b2dbfe7b66d6390cd88e768b8bcc1805585eb4926102cc75a92dc2c36d5","dweb:/ipfs/QmTXmGkN5CDbLAu8ykWJegZsHt9YY1MoUFWJBWPWjbYcht"],"license":"GPL-3.0-or-later"},"src/interface/IFlashLoanSolverWrapper.sol":{"keccak256":"0xcd22c2a72f4b0cb7a52c75d66ad801d2541a7117367ad7a9621e44aea9d0d051","urls":["bzz-raw://76adab1e0be8d955ebde37a752ff1e38767cf64a2fbdcb95f1dd3b8dccd0a2b6","dweb:/ipfs/QmfWgmUg7kCyHq66qYVvhZk4rb16xW7emWJ4otLMqGRQzY"],"license":"GPL-3.0-or-later"},"src/library/LoansWithSettlement.sol":{"keccak256":"0x0a2093a67a219184cb02a40217ed9d662da8562ac8625ee5f61b235608990a69","urls":["bzz-raw://6f8b14b11ae34e4aa574728fc64acdee55cdef5a222e228362b8d7d941494a66","dweb:/ipfs/QmXsq4jejQGr1YtYppEnjUYRxYEv762b6HWhQ2ohpFhuzM"],"license":"GPL-3.0-or-later"},"src/mixin/FlashLoanSolverWrapper.sol":{"keccak256":"0x95ccd3376d6c5716b7fbaace452839889184d08daf000a017e4c2f37f1fba47a","urls":["bzz-raw://4750bf1520deb639baab99a8dd4bf32dc02019ba30833117d5eb9200fa120141","dweb:/ipfs/QmYwL4DicSTc43jYu5P5U3xXMXMbMi39xGKHycfmzTn1aL"],"license":"GPL-3.0-or-later"},"src/vendored/ICowAuthentication.sol":{"keccak256":"0xc0b8ec08ce1e4ed2af4188ab08281b1a894d74b2dc69e045398e789f5a72d140","urls":["bzz-raw://41917d1ba4f6846ddd52d7fe16189a14a836d62b255e3ad07ebbd46f33cdbfa4","dweb:/ipfs/QmXTzjA9qoNX3aHehLJqMyx7qpZKitPE1Un3buB5QtKqfq"],"license":"LGPL-3.0-or-later"},"src/vendored/IERC20.sol":{"keccak256":"0x1b72641f69f5a2156fc2319a6b08daa0c4b5b224d0a776b85a5fcae428af72c2","urls":["bzz-raw://261d26fbf99877e2d4b5a7e6b2b2defee5ba5cab5bfdfab231fb5f2e2dfb1fdb","dweb:/ipfs/QmSpQuVkTHXzH1CNWmgtnCLz9CNEMD5g5sciiXRkrbYUJt"],"license":"MIT"},"src/vendored/IERC3156FlashBorrower.sol":{"keccak256":"0x48d97659a73cb7d14bbae680c75a337f1ea0327d3f2dd0a60c8c84625a62d518","urls":["bzz-raw://846a4ce9cc9d6aa834b6a38b753edc3593276c3565d8fec833499fcbfcedd067","dweb:/ipfs/QmZFZiD3RcRDktLZ42B1yKNFhGpDBB47HtNnV8VbJETJmL"],"license":"CC0-1.0"},"src/vendored/IERC3156FlashLender.sol":{"keccak256":"0x5b4a9ebac4d00400465d2868697f33a7ba3e66842d8375adb4a1b278d453a71c","urls":["bzz-raw://063501976d5e34e8cfa860ea62717b7862fb76a70ac92b099d0bdb62c3c684df","dweb:/ipfs/QmRpztSSUsk5H4Bs5m9638f24S2EiboYZrUQFMXdswPHqM"],"license":"CC0-1.0"}},"version":1},"id":26} + diff --git a/crates/contracts/artifacts/FlashLoanRouter.json b/crates/contracts/artifacts/FlashLoanRouter.json new file mode 100644 index 0000000000..54780fe2d0 --- /dev/null +++ b/crates/contracts/artifacts/FlashLoanRouter.json @@ -0,0 +1,2 @@ + +{"abi":[{"type":"constructor","inputs":[{"name":"_settlementContract","type":"address","internalType":"contract ICowSettlement"}],"stateMutability":"nonpayable"},{"type":"function","name":"borrowerCallback","inputs":[{"name":"encodedLoansWithSettlement","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"flashLoanAndSettle","inputs":[{"name":"loans","type":"tuple[]","internalType":"struct LoanRequest.Data[]","components":[{"name":"amount","type":"uint256","internalType":"uint256"},{"name":"borrower","type":"address","internalType":"contract IFlashLoanSolverWrapper"},{"name":"lender","type":"address","internalType":"address"},{"name":"token","type":"address","internalType":"contract IERC20"}]},{"name":"settlement","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"settlementAuthentication","inputs":[],"outputs":[{"name":"","type":"address","internalType":"contract ICowAuthentication"}],"stateMutability":"view"},{"type":"function","name":"settlementContract","inputs":[],"outputs":[{"name":"","type":"address","internalType":"contract ICowSettlement"}],"stateMutability":"view"}],"bytecode":"0x60c06040525f80546001600160a01b031916905534801561001e575f5ffd5b50604051610dc0380380610dc083398101604081905261003d916100d3565b6001600160a01b038116608081905260408051632335c76b60e01b81529051632335c76b9160048082019260209290919082900301815f875af1158015610086573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906100aa91906100d3565b6001600160a01b031660a052506100f5565b6001600160a01b03811681146100d0575f5ffd5b50565b5f602082840312156100e3575f5ffd5b81516100ee816100bc565b9392505050565b60805160a051610c9e6101225f395f81816053015261026001525f818160cb01526106410152610c9e5ff3fe608060405234801561000f575f5ffd5b506004361061004a575f3560e01c806302ebcbea1461004e5780630efb1fb61461009e578063e7c438c9146100b3578063ea42418b146100c6575b5f5ffd5b6100757f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b6100b16100ac3660046108b0565b6100ed565b005b6100b16100c13660046109e5565b610232565b6100757f000000000000000000000000000000000000000000000000000000000000000081565b3373ffffffffffffffffffffffffffffffffffffffff5f5c1614610172576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f4f6e6c792063616c6c61626c6520627920626f72726f7765720000000000000060448201526064015b60405180910390fd5b5f805473ffffffffffffffffffffffffffffffffffffffff16907fffffffffffffffffffffffff0000000000000000000000000000000000000000815c168217905d508051602082012060015c14610226576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f42616420646174612066726f6d20626f72726f776572000000000000000000006044820152606401610169565b61022f81610363565b50565b6040517f02cc250d0000000000000000000000000000000000000000000000000000000081523360048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906302cc250d90602401602060405180830381865afa1580156102ba573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906102de9190610a80565b610344576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f4f6e6c792063616c6c61626c65206279206120736f6c766572000000000000006044820152606401610169565b5f6103518585858561048d565b905061035c81610363565b5050505050565b60208101515f0361037f5761022f61037a82610549565b61058b565b5f61038982610731565b60208181015184519185019190912091925090815f805c7fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831617905d50808060015d50604080518082018252606085015173ffffffffffffffffffffffffffffffffffffffff9081168252855160208301528583015192517fe0bbec7700000000000000000000000000000000000000000000000000000000815291929085169163e0bbec779161045991859087908b90600401610aa6565b5f604051808303815f87803b158015610470575f5ffd5b505af1158015610482573d5f5f3e3d5ffd5b505050505050505050565b60606104c461049d605c86610b72565b6104a8846020610b8f565b6104b29190610b8f565b60408051828152918201602001905290565b60208082018681529192506104d99082610b8f565b9050828482376104e98382610b8f565b9050845b801561053f57806104fd81610ba2565b915082905061052c88888481811061051757610517610bd6565b9050608002018261081890919063ffffffff16565b610537605c84610b8f565b9250506104ed565b5050949350505050565b60605f605c610559846020015190565b6105639190610b72565b602084516105719190610c03565b61057b9190610c03565b6020939093019283525090919050565b7f13d79a0b000000000000000000000000000000000000000000000000000000006105b582610867565b7fffffffff00000000000000000000000000000000000000000000000000000000161461063e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f4f6e6c7920736574746c65282920697320616c6c6f77656400000000000000006044820152606401610169565b5f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16826040516106849190610c16565b5f604051808303815f865af19150503d805f81146106bd576040519150601f19603f3d011682016040523d82523d5f602084013e6106c2565b606091505b505090508061072d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f536574746c656d656e74207265766572746564000000000000000000000000006044820152606401610169565b5050565b604080516080810182525f8082526020820181905291810182905260608101829052906001610761846020015190565b61076b9190610c03565b90505f605c845161077c9190610c03565b9050602084015f61078d8383610b8f565b905082865283825261080e81604080516080810182525f80825260208201819052918101829052606081019190915250805160148201516028830151603c909301516040805160808101825293845273ffffffffffffffffffffffffffffffffffffffff928316602085015293821693830193909352909116606082015290565b9695505050505050565b80355f61082b6040840160208501610c4d565b90505f61083e6060850160408601610c4d565b90505f6108516080860160608701610c4d565b603c870152506028850152601484015290915250565b5f80602083019050600483511061087d57805191505b50919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b5f602082840312156108c0575f5ffd5b813567ffffffffffffffff8111156108d6575f5ffd5b8201601f810184136108e6575f5ffd5b803567ffffffffffffffff81111561090057610900610883565b6040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8501160116810181811067ffffffffffffffff8211171561096c5761096c610883565b604052818152828201602001861015610983575f5ffd5b816020840160208301375f91810160200191909152949350505050565b5f5f83601f8401126109b0575f5ffd5b50813567ffffffffffffffff8111156109c7575f5ffd5b6020830191508360208285010111156109de575f5ffd5b9250929050565b5f5f5f5f604085870312156109f8575f5ffd5b843567ffffffffffffffff811115610a0e575f5ffd5b8501601f81018713610a1e575f5ffd5b803567ffffffffffffffff811115610a34575f5ffd5b8760208260071b8401011115610a48575f5ffd5b60209182019550935085013567ffffffffffffffff811115610a68575f5ffd5b610a74878288016109a0565b95989497509550505050565b5f60208284031215610a90575f5ffd5b81518015158114610a9f575f5ffd5b9392505050565b73ffffffffffffffffffffffffffffffffffffffff8516815273ffffffffffffffffffffffffffffffffffffffff84511660208201526020840151604082015282606082015260a060808201525f82518060a0840152806020850160c085015e5f60c0828501015260c07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011684010191505095945050505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b8082028115828204841417610b8957610b89610b45565b92915050565b80820180821115610b8957610b89610b45565b5f81610bb057610bb0610b45565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b81810381811115610b8957610b89610b45565b5f82518060208501845e5f920191825250919050565b73ffffffffffffffffffffffffffffffffffffffff8116811461022f575f5ffd5b5f60208284031215610c5d575f5ffd5b8135610a9f81610c2c56fea264697066735822122040d837bb712363085fa15f0b290e54590843321342f61b2f21d52432d2dc8e7b64736f6c634300081c0033","deployedBytecode":"0x608060405234801561000f575f5ffd5b506004361061004a575f3560e01c806302ebcbea1461004e5780630efb1fb61461009e578063e7c438c9146100b3578063ea42418b146100c6575b5f5ffd5b6100757f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b6100b16100ac3660046108b0565b6100ed565b005b6100b16100c13660046109e5565b610232565b6100757f000000000000000000000000000000000000000000000000000000000000000081565b3373ffffffffffffffffffffffffffffffffffffffff5f5c1614610172576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f4f6e6c792063616c6c61626c6520627920626f72726f7765720000000000000060448201526064015b60405180910390fd5b5f805473ffffffffffffffffffffffffffffffffffffffff16907fffffffffffffffffffffffff0000000000000000000000000000000000000000815c168217905d508051602082012060015c14610226576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f42616420646174612066726f6d20626f72726f776572000000000000000000006044820152606401610169565b61022f81610363565b50565b6040517f02cc250d0000000000000000000000000000000000000000000000000000000081523360048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906302cc250d90602401602060405180830381865afa1580156102ba573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906102de9190610a80565b610344576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f4f6e6c792063616c6c61626c65206279206120736f6c766572000000000000006044820152606401610169565b5f6103518585858561048d565b905061035c81610363565b5050505050565b60208101515f0361037f5761022f61037a82610549565b61058b565b5f61038982610731565b60208181015184519185019190912091925090815f805c7fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831617905d50808060015d50604080518082018252606085015173ffffffffffffffffffffffffffffffffffffffff9081168252855160208301528583015192517fe0bbec7700000000000000000000000000000000000000000000000000000000815291929085169163e0bbec779161045991859087908b90600401610aa6565b5f604051808303815f87803b158015610470575f5ffd5b505af1158015610482573d5f5f3e3d5ffd5b505050505050505050565b60606104c461049d605c86610b72565b6104a8846020610b8f565b6104b29190610b8f565b60408051828152918201602001905290565b60208082018681529192506104d99082610b8f565b9050828482376104e98382610b8f565b9050845b801561053f57806104fd81610ba2565b915082905061052c88888481811061051757610517610bd6565b9050608002018261081890919063ffffffff16565b610537605c84610b8f565b9250506104ed565b5050949350505050565b60605f605c610559846020015190565b6105639190610b72565b602084516105719190610c03565b61057b9190610c03565b6020939093019283525090919050565b7f13d79a0b000000000000000000000000000000000000000000000000000000006105b582610867565b7fffffffff00000000000000000000000000000000000000000000000000000000161461063e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f4f6e6c7920736574746c65282920697320616c6c6f77656400000000000000006044820152606401610169565b5f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16826040516106849190610c16565b5f604051808303815f865af19150503d805f81146106bd576040519150601f19603f3d011682016040523d82523d5f602084013e6106c2565b606091505b505090508061072d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f536574746c656d656e74207265766572746564000000000000000000000000006044820152606401610169565b5050565b604080516080810182525f8082526020820181905291810182905260608101829052906001610761846020015190565b61076b9190610c03565b90505f605c845161077c9190610c03565b9050602084015f61078d8383610b8f565b905082865283825261080e81604080516080810182525f80825260208201819052918101829052606081019190915250805160148201516028830151603c909301516040805160808101825293845273ffffffffffffffffffffffffffffffffffffffff928316602085015293821693830193909352909116606082015290565b9695505050505050565b80355f61082b6040840160208501610c4d565b90505f61083e6060850160408601610c4d565b90505f6108516080860160608701610c4d565b603c870152506028850152601484015290915250565b5f80602083019050600483511061087d57805191505b50919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b5f602082840312156108c0575f5ffd5b813567ffffffffffffffff8111156108d6575f5ffd5b8201601f810184136108e6575f5ffd5b803567ffffffffffffffff81111561090057610900610883565b6040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8501160116810181811067ffffffffffffffff8211171561096c5761096c610883565b604052818152828201602001861015610983575f5ffd5b816020840160208301375f91810160200191909152949350505050565b5f5f83601f8401126109b0575f5ffd5b50813567ffffffffffffffff8111156109c7575f5ffd5b6020830191508360208285010111156109de575f5ffd5b9250929050565b5f5f5f5f604085870312156109f8575f5ffd5b843567ffffffffffffffff811115610a0e575f5ffd5b8501601f81018713610a1e575f5ffd5b803567ffffffffffffffff811115610a34575f5ffd5b8760208260071b8401011115610a48575f5ffd5b60209182019550935085013567ffffffffffffffff811115610a68575f5ffd5b610a74878288016109a0565b95989497509550505050565b5f60208284031215610a90575f5ffd5b81518015158114610a9f575f5ffd5b9392505050565b73ffffffffffffffffffffffffffffffffffffffff8516815273ffffffffffffffffffffffffffffffffffffffff84511660208201526020840151604082015282606082015260a060808201525f82518060a0840152806020850160c085015e5f60c0828501015260c07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011684010191505095945050505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b8082028115828204841417610b8957610b89610b45565b92915050565b80820180821115610b8957610b89610b45565b5f81610bb057610bb0610b45565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b81810381811115610b8957610b89610b45565b5f82518060208501845e5f920191825250919050565b73ffffffffffffffffffffffffffffffffffffffff8116811461022f575f5ffd5b5f60208284031215610c5d575f5ffd5b8135610a9f81610c2c56fea264697066735822122040d837bb712363085fa15f0b290e54590843321342f61b2f21d52432d2dc8e7b64736f6c634300081c0033","methodIdentifiers":{"borrowerCallback(bytes)":"0efb1fb6","flashLoanAndSettle((uint256,address,address,address)[],bytes)":"e7c438c9","settlementAuthentication()":"02ebcbea","settlementContract()":"ea42418b"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.28+commit.7893614a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"contract ICowSettlement\",\"name\":\"_settlementContract\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"encodedLoansWithSettlement\",\"type\":\"bytes\"}],\"name\":\"borrowerCallback\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"contract IFlashLoanSolverWrapper\",\"name\":\"borrower\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"lender\",\"type\":\"address\"},{\"internalType\":\"contract IERC20\",\"name\":\"token\",\"type\":\"address\"}],\"internalType\":\"struct LoanRequest.Data[]\",\"name\":\"loans\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes\",\"name\":\"settlement\",\"type\":\"bytes\"}],\"name\":\"flashLoanAndSettle\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"settlementAuthentication\",\"outputs\":[{\"internalType\":\"contract ICowAuthentication\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"settlementContract\",\"outputs\":[{\"internalType\":\"contract ICowSettlement\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"settlementAuthentication()\":{\"notice\":\"The contract responsible to determine which address is an authorized solver for CoW Protocol.\"},\"settlementContract()\":{\"notice\":\"The settlement contract that will be called when a settlement is executed after a flash loan.\"}},\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/FlashLoanRouter.sol\":\"FlashLoanRouter\"},\"evmVersion\":\"cancun\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":1000000},\"remappings\":[\":forge-std/=lib/forge-std/src/\"]},\"sources\":{\"src/FlashLoanRouter.sol\":{\"keccak256\":\"0xa870f7b1e734d9c224414507ea794284b75fd8ff1a6ebf7083aa59e01a367333\",\"license\":\"GPL-3.0-or-later\",\"urls\":[\"bzz-raw://b062f5f6d48341eefb59897e86423e2d6002dd108e383c8df94e21e6c804693d\",\"dweb:/ipfs/QmUMNqbS4w2oDaS3nHeEjas9bpNE8wR2kiM7JzCmeFoyHo\"]},\"src/interface/ICowSettlement.sol\":{\"keccak256\":\"0x936d8038ce833e625c7c306b5890ba85c4e5bf8adc8b7e41227a680c4039e244\",\"license\":\"GPL-3.0-or-later\",\"urls\":[\"bzz-raw://138b144cac2d5cd0b3bbd4b0f73817b1e611d90854734f64b98b15e709aa7b5e\",\"dweb:/ipfs/Qmf6rcxHBToX7Ym7zHXP8BRaWwD7DLptuZqYq3YDcd7jvJ\"]},\"src/interface/IFlashLoanRouter.sol\":{\"keccak256\":\"0x72bc1e822dbbe15f75cb7e437605242fd5e8421c3f4074ad7fa0d6ba0b7991de\",\"license\":\"GPL-3.0-or-later\",\"urls\":[\"bzz-raw://63b29b2dbfe7b66d6390cd88e768b8bcc1805585eb4926102cc75a92dc2c36d5\",\"dweb:/ipfs/QmTXmGkN5CDbLAu8ykWJegZsHt9YY1MoUFWJBWPWjbYcht\"]},\"src/interface/IFlashLoanSolverWrapper.sol\":{\"keccak256\":\"0xcd22c2a72f4b0cb7a52c75d66ad801d2541a7117367ad7a9621e44aea9d0d051\",\"license\":\"GPL-3.0-or-later\",\"urls\":[\"bzz-raw://76adab1e0be8d955ebde37a752ff1e38767cf64a2fbdcb95f1dd3b8dccd0a2b6\",\"dweb:/ipfs/QmfWgmUg7kCyHq66qYVvhZk4rb16xW7emWJ4otLMqGRQzY\"]},\"src/library/LoansWithSettlement.sol\":{\"keccak256\":\"0x0a2093a67a219184cb02a40217ed9d662da8562ac8625ee5f61b235608990a69\",\"license\":\"GPL-3.0-or-later\",\"urls\":[\"bzz-raw://6f8b14b11ae34e4aa574728fc64acdee55cdef5a222e228362b8d7d941494a66\",\"dweb:/ipfs/QmXsq4jejQGr1YtYppEnjUYRxYEv762b6HWhQ2ohpFhuzM\"]},\"src/vendored/ICowAuthentication.sol\":{\"keccak256\":\"0xc0b8ec08ce1e4ed2af4188ab08281b1a894d74b2dc69e045398e789f5a72d140\",\"license\":\"LGPL-3.0-or-later\",\"urls\":[\"bzz-raw://41917d1ba4f6846ddd52d7fe16189a14a836d62b255e3ad07ebbd46f33cdbfa4\",\"dweb:/ipfs/QmXTzjA9qoNX3aHehLJqMyx7qpZKitPE1Un3buB5QtKqfq\"]},\"src/vendored/IERC20.sol\":{\"keccak256\":\"0x1b72641f69f5a2156fc2319a6b08daa0c4b5b224d0a776b85a5fcae428af72c2\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://261d26fbf99877e2d4b5a7e6b2b2defee5ba5cab5bfdfab231fb5f2e2dfb1fdb\",\"dweb:/ipfs/QmSpQuVkTHXzH1CNWmgtnCLz9CNEMD5g5sciiXRkrbYUJt\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.28+commit.7893614a"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"contract ICowSettlement","name":"_settlementContract","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"bytes","name":"encodedLoansWithSettlement","type":"bytes"}],"stateMutability":"nonpayable","type":"function","name":"borrowerCallback"},{"inputs":[{"internalType":"struct LoanRequest.Data[]","name":"loans","type":"tuple[]","components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"contract IFlashLoanSolverWrapper","name":"borrower","type":"address"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"contract IERC20","name":"token","type":"address"}]},{"internalType":"bytes","name":"settlement","type":"bytes"}],"stateMutability":"nonpayable","type":"function","name":"flashLoanAndSettle"},{"inputs":[],"stateMutability":"view","type":"function","name":"settlementAuthentication","outputs":[{"internalType":"contract ICowAuthentication","name":"","type":"address"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"settlementContract","outputs":[{"internalType":"contract ICowSettlement","name":"","type":"address"}]}],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{"settlementAuthentication()":{"notice":"The contract responsible to determine which address is an authorized solver for CoW Protocol."},"settlementContract()":{"notice":"The settlement contract that will be called when a settlement is executed after a flash loan."}},"version":1}},"settings":{"remappings":["forge-std/=lib/forge-std/src/"],"optimizer":{"enabled":true,"runs":1000000},"metadata":{"bytecodeHash":"ipfs"},"compilationTarget":{"src/FlashLoanRouter.sol":"FlashLoanRouter"},"evmVersion":"cancun","libraries":{}},"sources":{"src/FlashLoanRouter.sol":{"keccak256":"0xa870f7b1e734d9c224414507ea794284b75fd8ff1a6ebf7083aa59e01a367333","urls":["bzz-raw://b062f5f6d48341eefb59897e86423e2d6002dd108e383c8df94e21e6c804693d","dweb:/ipfs/QmUMNqbS4w2oDaS3nHeEjas9bpNE8wR2kiM7JzCmeFoyHo"],"license":"GPL-3.0-or-later"},"src/interface/ICowSettlement.sol":{"keccak256":"0x936d8038ce833e625c7c306b5890ba85c4e5bf8adc8b7e41227a680c4039e244","urls":["bzz-raw://138b144cac2d5cd0b3bbd4b0f73817b1e611d90854734f64b98b15e709aa7b5e","dweb:/ipfs/Qmf6rcxHBToX7Ym7zHXP8BRaWwD7DLptuZqYq3YDcd7jvJ"],"license":"GPL-3.0-or-later"},"src/interface/IFlashLoanRouter.sol":{"keccak256":"0x72bc1e822dbbe15f75cb7e437605242fd5e8421c3f4074ad7fa0d6ba0b7991de","urls":["bzz-raw://63b29b2dbfe7b66d6390cd88e768b8bcc1805585eb4926102cc75a92dc2c36d5","dweb:/ipfs/QmTXmGkN5CDbLAu8ykWJegZsHt9YY1MoUFWJBWPWjbYcht"],"license":"GPL-3.0-or-later"},"src/interface/IFlashLoanSolverWrapper.sol":{"keccak256":"0xcd22c2a72f4b0cb7a52c75d66ad801d2541a7117367ad7a9621e44aea9d0d051","urls":["bzz-raw://76adab1e0be8d955ebde37a752ff1e38767cf64a2fbdcb95f1dd3b8dccd0a2b6","dweb:/ipfs/QmfWgmUg7kCyHq66qYVvhZk4rb16xW7emWJ4otLMqGRQzY"],"license":"GPL-3.0-or-later"},"src/library/LoansWithSettlement.sol":{"keccak256":"0x0a2093a67a219184cb02a40217ed9d662da8562ac8625ee5f61b235608990a69","urls":["bzz-raw://6f8b14b11ae34e4aa574728fc64acdee55cdef5a222e228362b8d7d941494a66","dweb:/ipfs/QmXsq4jejQGr1YtYppEnjUYRxYEv762b6HWhQ2ohpFhuzM"],"license":"GPL-3.0-or-later"},"src/vendored/ICowAuthentication.sol":{"keccak256":"0xc0b8ec08ce1e4ed2af4188ab08281b1a894d74b2dc69e045398e789f5a72d140","urls":["bzz-raw://41917d1ba4f6846ddd52d7fe16189a14a836d62b255e3ad07ebbd46f33cdbfa4","dweb:/ipfs/QmXTzjA9qoNX3aHehLJqMyx7qpZKitPE1Un3buB5QtKqfq"],"license":"LGPL-3.0-or-later"},"src/vendored/IERC20.sol":{"keccak256":"0x1b72641f69f5a2156fc2319a6b08daa0c4b5b224d0a776b85a5fcae428af72c2","urls":["bzz-raw://261d26fbf99877e2d4b5a7e6b2b2defee5ba5cab5bfdfab231fb5f2e2dfb1fdb","dweb:/ipfs/QmSpQuVkTHXzH1CNWmgtnCLz9CNEMD5g5sciiXRkrbYUJt"],"license":"MIT"}},"version":1},"id":27} diff --git a/crates/contracts/build.rs b/crates/contracts/build.rs index f1835f85c0..010dcf355f 100644 --- a/crates/contracts/build.rs +++ b/crates/contracts/build.rs @@ -641,6 +641,7 @@ fn main() { generate_contract("ERC20"); generate_contract("ERC20Mintable"); generate_contract("ERC3156FlashLoanSolverWrapper"); + generate_contract("FlashLoanRouter"); generate_contract_with_config("GPv2AllowListAuthentication", |builder| { builder .contract_mod_override("gpv2_allow_list_authentication") diff --git a/crates/contracts/src/lib.rs b/crates/contracts/src/lib.rs index f3e7aecf28..1cec627a18 100644 --- a/crates/contracts/src/lib.rs +++ b/crates/contracts/src/lib.rs @@ -82,6 +82,7 @@ include_contracts! { ERC20; ERC20Mintable; ERC3156FlashLoanSolverWrapper; + FlashLoanRouter; GPv2AllowListAuthentication; GPv2Settlement; GnosisSafe; diff --git a/crates/driver/example.toml b/crates/driver/example.toml index 095cb9cff3..2bbce23c87 100644 --- a/crates/driver/example.toml +++ b/crates/driver/example.toml @@ -36,6 +36,8 @@ gp-v2-settlement = "0x9008D19f58AAbD9eD0D60971565AA8510560ab41" weth = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" # TODO replace with the actual address when the contract actually gets replaced flashloan-wrappers = ["0x0000000000000000000000000000000000000000"] +flashloan-router = "0x0000000000000000000000000000000000000000" + [[contracts.cow-amms]] # address of factory creating new CoW AMMs factory = "0x86f3df416979136cb4fdea2c0886301b911c163b" diff --git a/crates/driver/src/domain/competition/auction.rs b/crates/driver/src/domain/competition/auction.rs index 971f143522..233022b5d2 100644 --- a/crates/driver/src/domain/competition/auction.rs +++ b/crates/driver/src/domain/competition/auction.rs @@ -215,7 +215,8 @@ impl AuctionProcessor { ) }); - Self::update_orders(&mut balances, &mut app_data_by_hash, &mut orders); + let settlement = eth.contracts().settlement().address().into(); + Self::update_orders(&mut balances, &mut app_data_by_hash, &mut orders, &settlement); tracing::debug!(auction_id = new_id.0, time =? start.elapsed(), "auction preprocessing done"); orders @@ -284,6 +285,7 @@ impl AuctionProcessor { balances: &mut Balances, app_data_by_hash: &mut HashMap, orders: &mut Vec, + settlement: ð::Address, ) { // The auction that we receive from the `autopilot` assumes that there // is sufficient balance to completely cover all the orders. **This is @@ -298,8 +300,11 @@ impl AuctionProcessor { if let Some(fetched_app_data) = app_data_by_hash.get(&order.app_data.hash()) { order.app_data = fetched_app_data.clone().into(); if order.app_data.flashloan().is_some() { - // assume all the necessary tokens will come from the flashloan - return true; + // If an order requires a flashloan we assume all the necessary + // sell tokens will come from there. But the receiver must be the + // settlement contract because that is how the driver expects + // the flashloan to be repaid for now. + return order.receiver.as_ref() == Some(settlement); } } diff --git a/crates/driver/src/domain/competition/order/app_data.rs b/crates/driver/src/domain/competition/order/app_data.rs index c23aefa7be..f57a90f710 100644 --- a/crates/driver/src/domain/competition/order/app_data.rs +++ b/crates/driver/src/domain/competition/order/app_data.rs @@ -72,12 +72,15 @@ impl AppDataRetriever { let appdata: AppDataDocument = serde_json::from_str(&response.text().await?) .context("invalid app data document")?; - (appdata.full_app_data != app_data::EMPTY).then_some( - self_ - .0 - .app_data_validator - .validate(&appdata.full_app_data.into_bytes())?, - ) + match appdata.full_app_data == app_data::EMPTY { + true => None, // empty app data + false => Some( + self_ + .0 + .app_data_validator + .validate(&appdata.full_app_data.into_bytes())?, + ), + } } }; diff --git a/crates/driver/src/domain/competition/solution/encoding.rs b/crates/driver/src/domain/competition/solution/encoding.rs index cfce2d74bd..7be55fa0ba 100644 --- a/crates/driver/src/domain/competition/solution/encoding.rs +++ b/crates/driver/src/domain/competition/solution/encoding.rs @@ -27,6 +27,9 @@ pub enum Error { InvalidClearingPrice(eth::TokenAddress), #[error(transparent)] Math(#[from] Math), + // TODO: remove when contracts are deployed everywhere + #[error("flashloan support disabled")] + FlashloanSupportDisabled, } pub fn tx( @@ -177,7 +180,7 @@ pub fn tx( // Add all interactions needed to move flash loaned tokens around // These interactions are executed before all other pre-interactions - let encoded_flashloans: Vec<_> = solution + let flashloans: Vec<_> = solution .flashloans .iter() // Necessary pre-interactions get prepended to the settlement. So to initiate @@ -261,7 +264,12 @@ pub fn tx( flashloan_wrapper, )); - (flashloan, flashloan_wrapper) + ( + flashloan.amount.0, + flashloan_wrapper.address(), + flashloan.lender.0, + flashloan.token.0.0, + ) }) .collect(); @@ -304,27 +312,19 @@ pub fn tx( .into_inner(); // Encode the auction id into the calldata - let mut calldata = tx.data.unwrap().0; - calldata.extend(auction.id().ok_or(Error::MissingAuctionId)?.to_be_bytes()); + let mut settle_calldata = tx.data.unwrap().0; + settle_calldata.extend(auction.id().ok_or(Error::MissingAuctionId)?.to_be_bytes()); - // TODO: support multiple flashloans in 1 settlement // Target and calldata depend on whether a flashloan is used - let (to, calldata) = match encoded_flashloans.first() { - Some((flashloan, flashloan_wrapper)) => { - let calldata = flashloan_wrapper - .flash_loan_and_settle( - flashloan.lender.into(), - (flashloan.token.into(), flashloan.amount.into()), - ethcontract::Bytes(calldata), - ) - .tx - .data - .unwrap() - .0; - - (flashloan_wrapper.address().into(), calldata) - } - None => (contracts.settlement().address().into(), calldata), + let (to, calldata) = if flashloans.is_empty() { + (contracts.settlement().address().into(), settle_calldata) + } else { + let router = contracts + .flashloan_router() + .ok_or(Error::FlashloanSupportDisabled)?; + let call = router.flash_loan_and_settle(flashloans, ethcontract::Bytes(settle_calldata)); + let calldata = call.tx.data.unwrap().0; + (router.address().into(), calldata) }; Ok(eth::Tx { diff --git a/crates/driver/src/infra/blockchain/contracts.rs b/crates/driver/src/infra/blockchain/contracts.rs index ca03e79f5d..0afbaacdc0 100644 --- a/crates/driver/src/infra/blockchain/contracts.rs +++ b/crates/driver/src/infra/blockchain/contracts.rs @@ -1,6 +1,7 @@ use { crate::{boundary, domain::eth, infra::blockchain::Ethereum}, chain::Chain, + contracts::FlashLoanRouter, ethcontract::dyns::DynWeb3, ethrpc::block_stream::CurrentBlockWatcher, thiserror::Error, @@ -20,6 +21,11 @@ pub struct Contracts { /// Each lender potentially has different solver wrapper. flashloan_wrappers: Vec, + /// Single router that supports multiple flashloans in the + /// same settlement. + // TODO: make this non-optional when contracts are deployed + // everywhere + flashloan_router: Option, } #[derive(Debug, Default, Clone)] @@ -28,6 +34,7 @@ pub struct Addresses { pub weth: Option, pub cow_amms: Vec, pub flashloan_wrappers: Vec, + pub flashloan_router: Option, } impl Contracts { @@ -96,6 +103,17 @@ impl Contracts { }) .collect(); + // TODO: use `address_for()` once contracts are deployed + let flashloan_router = addresses + .flashloan_router + .or_else(|| { + contracts::FlashLoanRouter::raw_contract() + .networks + .get(&chain.id().to_string()) + .map(|deployment| eth::ContractAddress(deployment.address)) + }) + .map(|address| contracts::FlashLoanRouter::at(web3, address.0)); + Ok(Self { settlement, vault_relayer, @@ -104,6 +122,7 @@ impl Contracts { settlement_domain_separator, cow_amm_registry, flashloan_wrappers, + flashloan_router, }) } @@ -138,6 +157,10 @@ impl Contracts { pub fn flashloan_wrappers(&self) -> &[contracts::IFlashLoanSolverWrapper] { &self.flashloan_wrappers } + + pub fn flashloan_router(&self) -> Option<&contracts::FlashLoanRouter> { + self.flashloan_router.as_ref() + } } #[derive(Debug, Clone)] diff --git a/crates/driver/src/infra/config/file/load.rs b/crates/driver/src/infra/config/file/load.rs index d6a26bd67d..d6ea9e52fa 100644 --- a/crates/driver/src/infra/config/file/load.rs +++ b/crates/driver/src/infra/config/file/load.rs @@ -381,6 +381,7 @@ pub async fn load(chain: Chain, path: &Path) -> infra::Config { .into_iter() .map(Into::into) .collect(), + flashloan_router: config.contracts.flashloan_router.map(Into::into), }, disable_access_list_simulation: config.disable_access_list_simulation, disable_gas_simulation: config.disable_gas_simulation.map(Into::into), diff --git a/crates/driver/src/infra/config/file/mod.rs b/crates/driver/src/infra/config/file/mod.rs index 6052837443..405c6fd728 100644 --- a/crates/driver/src/infra/config/file/mod.rs +++ b/crates/driver/src/infra/config/file/mod.rs @@ -375,6 +375,10 @@ struct ContractsConfig { /// Currently Maker and Aave lenders are supported. #[serde(default)] flashloan_wrappers: Vec, + + /// Flashloan router to support taking out multiple flashloans + /// in the same settlement. + flashloan_router: Option, } #[derive(Debug, Clone, Deserialize)] diff --git a/crates/driver/src/tests/cases/example_config.rs b/crates/driver/src/tests/cases/example_config.rs index 6e33e56487..d914503c61 100644 --- a/crates/driver/src/tests/cases/example_config.rs +++ b/crates/driver/src/tests/cases/example_config.rs @@ -1,4 +1,4 @@ -use crate::tests; +use {crate::tests, shared::addr}; /// Test that the example configuration file is valid by checking that the /// driver does not crash when started with this file. @@ -8,7 +8,7 @@ async fn test() { let example_config_file = std::env::current_dir().unwrap().join("example.toml"); tests::setup() .config(example_config_file) - .settlement_address("0x9008D19f58AAbD9eD0D60971565AA8510560ab41") + .settlement_address(&addr!("9008D19f58AAbD9eD0D60971565AA8510560ab41")) .done() .await; } diff --git a/crates/driver/src/tests/cases/flashloan_hints.rs b/crates/driver/src/tests/cases/flashloan_hints.rs index bfa38255b4..c822973d1b 100644 --- a/crates/driver/src/tests/cases/flashloan_hints.rs +++ b/crates/driver/src/tests/cases/flashloan_hints.rs @@ -26,9 +26,12 @@ async fn solutions_with_flashloan() { let app_data = AppData::Full(Box::new(protocol_app_data_into_validated( protocol_app_data, ))); - let order = ab_order().app_data(app_data); + + let settlement = H160([5; 20]); + let order = ab_order().app_data(app_data).receiver(Some(settlement)); let test = setup() + .settlement_address(&settlement) .pool(ab_pool()) .order(order.clone()) .solution(ab_solution().flashloan(flashloan_into_dto(flashloan))) @@ -57,9 +60,11 @@ async fn solutions_without_flashloan() { let app_data = AppData::Full(Box::new(protocol_app_data_into_validated( protocol_app_data, ))); - let order = ab_order().app_data(app_data); + let settlement = H160([5; 20]); + let order = ab_order().app_data(app_data).receiver(Some(settlement)); let test = setup() + .settlement_address(&settlement) .pool(ab_pool()) .order(order.clone()) .solution(ab_solution()) diff --git a/crates/driver/src/tests/setup/blockchain.rs b/crates/driver/src/tests/setup/blockchain.rs index ce45402acf..52dfd4e9e5 100644 --- a/crates/driver/src/tests/setup/blockchain.rs +++ b/crates/driver/src/tests/setup/blockchain.rs @@ -36,6 +36,7 @@ pub struct Blockchain { pub weth: contracts::WETH9, pub settlement: contracts::GPv2Settlement, pub flashloan_wrapper: contracts::ERC3156FlashLoanSolverWrapper, + pub flashloan_router: contracts::FlashLoanRouter, pub ethflow: Option, pub domain_separator: boundary::DomainSeparator, pub node: Node, @@ -323,9 +324,17 @@ impl Blockchain { ) .await .unwrap(); + let flashloan_router = wait_for( + &web3, + contracts::FlashLoanRouter::builder(&web3, settlement.address()) + .from(main_trader_account.clone()) + .deploy(), + ) + .await + .unwrap(); let flashloan_wrapper = wait_for( &web3, - contracts::ERC3156FlashLoanSolverWrapper::builder(&web3, settlement.address()) + contracts::ERC3156FlashLoanSolverWrapper::builder(&web3, flashloan_router.address()) .from(main_trader_account.clone()) .deploy(), ) @@ -640,6 +649,7 @@ impl Blockchain { node, pairs, flashloan_wrapper, + flashloan_router, } } diff --git a/crates/driver/src/tests/setup/driver.rs b/crates/driver/src/tests/setup/driver.rs index 669e8f964c..61bea69ae7 100644 --- a/crates/driver/src/tests/setup/driver.rs +++ b/crates/driver/src/tests/setup/driver.rs @@ -71,7 +71,7 @@ pub fn solve_req(test: &Test) -> serde_json::Value { let mut quotes = test.quoted_orders.clone(); quotes.shuffle(&mut rand::thread_rng()); for quote in quotes.iter() { - orders_json.push(json!({ + let mut order = json!({ "uid": quote.order_uid(&test.blockchain), "sellToken": hex_address(test.blockchain.get_token(quote.order.sell_token)), "buyToken": hex_address(test.blockchain.get_token(quote.order.buy_token)), @@ -111,7 +111,11 @@ pub fn solve_req(test: &Test) -> serde_json::Value { "signingScheme": "eip712", "signature": format!("0x{}", hex::encode(quote.order_signature(&test.blockchain))), "quote": quote.order.quote, - })); + }); + if let Some(receiver) = quote.order.receiver { + order["receiver"] = json!(hex_address(receiver)); + } + orders_json.push(order); } for trade in test.trades.iter() { match trade { @@ -222,6 +226,7 @@ async fn create_config_file( gp-v2-settlement = "{}" weth = "{}" flashloan-wrappers = ["{}"] + flashloan-router = "{}" [submission] gas-price-cap = "1000000000000" @@ -229,6 +234,7 @@ async fn create_config_file( hex_address(blockchain.settlement.address()), hex_address(blockchain.weth.address()), hex_address(blockchain.flashloan_wrapper.address()), + hex_address(blockchain.flashloan_router.address()), ) .unwrap(); diff --git a/crates/driver/src/tests/setup/mod.rs b/crates/driver/src/tests/setup/mod.rs index a6e33bd3a3..d89fc56631 100644 --- a/crates/driver/src/tests/setup/mod.rs +++ b/crates/driver/src/tests/setup/mod.rs @@ -299,7 +299,7 @@ impl Order { } } - fn receiver(self, receiver: Option) -> Self { + pub fn receiver(self, receiver: Option) -> Self { Self { receiver, ..self } } } @@ -842,8 +842,8 @@ impl Setup { } /// Ensure that the settlement contract is deployed to a specific address. - pub fn settlement_address(mut self, address: &str) -> Self { - self.settlement_address = Some(address.parse().unwrap()); + pub fn settlement_address(mut self, address: &H160) -> Self { + self.settlement_address = Some(*address); self } diff --git a/crates/driver/src/tests/setup/solver.rs b/crates/driver/src/tests/setup/solver.rs index f2e05f405a..4970a3d6c3 100644 --- a/crates/driver/src/tests/setup/solver.rs +++ b/crates/driver/src/tests/setup/solver.rs @@ -147,6 +147,9 @@ impl Solver { "signature": if config.quote { "0x".to_string() } else { format!("0x{}", hex::encode(quote.order_signature(config.blockchain))) }, "signingScheme": if config.quote { "eip1271" } else { "eip712" }, }); + if let Some(receiver) = quote.order.receiver { + order["receiver"] = json!(hex_address(receiver)); + } if let Some(flashloan) = quote.order.app_data.flashloan() { order["flashloanHint"] = json!(FlashloanHint { lender: flashloan.lender.unwrap(), @@ -450,6 +453,7 @@ impl Solver { weth: Some(config.blockchain.weth.address().into()), cow_amms: vec![], flashloan_wrappers: vec![config.blockchain.flashloan_wrapper.address().into()], + flashloan_router: Some(config.blockchain.flashloan_wrapper.address().into()), }, gas, None, diff --git a/crates/e2e/src/setup/colocation.rs b/crates/e2e/src/setup/colocation.rs index 57adf387f9..67e353edfd 100644 --- a/crates/e2e/src/setup/colocation.rs +++ b/crates/e2e/src/setup/colocation.rs @@ -170,6 +170,7 @@ flashloans-enabled = true gp-v2-settlement = "{:?}" weth = "{:?}" flashloan-wrappers = ["{:?}","{:?}"] # Maker, Aave +flashloan-router = "{:?}" {cow_amms} {solvers} @@ -189,6 +190,7 @@ mempool = "public" contracts.weth.address(), contracts.flashloan_wrapper_maker.address(), contracts.flashloan_wrapper_aave.address(), + contracts.flashloan_router.address(), )); let args = vec![ "driver".to_string(), diff --git a/crates/e2e/src/setup/deploy.rs b/crates/e2e/src/setup/deploy.rs index 81c1ea6494..f516626007 100644 --- a/crates/e2e/src/setup/deploy.rs +++ b/crates/e2e/src/setup/deploy.rs @@ -6,6 +6,7 @@ use { CoWSwapEthFlow, CowAmmLegacyHelper, ERC3156FlashLoanSolverWrapper, + FlashLoanRouter, GPv2AllowListAuthentication, GPv2Settlement, HooksTrampoline, @@ -33,6 +34,7 @@ pub struct Contracts { pub cow_amm_helper: Option, pub flashloan_wrapper_maker: ERC3156FlashLoanSolverWrapper, pub flashloan_wrapper_aave: AaveFlashLoanSolverWrapper, + pub flashloan_router: FlashLoanRouter, } impl Contracts { @@ -53,13 +55,17 @@ impl Contracts { }; // TODO: use Contract::deployed() after contracts got deployed on mainnet + let flashloan_router = FlashLoanRouter::builder(web3, gp_settlement.address()) + .deploy() + .await + .unwrap(); let flashloan_wrapper_aave = - AaveFlashLoanSolverWrapper::builder(web3, gp_settlement.address()) + AaveFlashLoanSolverWrapper::builder(web3, flashloan_router.address()) .deploy() .await .unwrap(); let flashloan_wrapper_maker = - ERC3156FlashLoanSolverWrapper::builder(web3, gp_settlement.address()) + ERC3156FlashLoanSolverWrapper::builder(web3, flashloan_router.address()) .deploy() .await .unwrap(); @@ -92,6 +98,7 @@ impl Contracts { cow_amm_helper, flashloan_wrapper_maker, flashloan_wrapper_aave, + flashloan_router, } } @@ -177,9 +184,11 @@ impl Contracts { let ethflow = deploy!(CoWSwapEthFlow(gp_settlement.address(), weth.address())); let ethflow_secondary = deploy!(CoWSwapEthFlow(gp_settlement.address(), weth.address())); let hooks = deploy!(HooksTrampoline(gp_settlement.address())); + let flashloan_router = deploy!(FlashLoanRouter(gp_settlement.address())); let flashloan_wrapper_maker = - deploy!(ERC3156FlashLoanSolverWrapper(gp_settlement.address())); - let flashloan_wrapper_aave = deploy!(AaveFlashLoanSolverWrapper(gp_settlement.address())); + deploy!(ERC3156FlashLoanSolverWrapper(flashloan_router.address())); + let flashloan_wrapper_aave = + deploy!(AaveFlashLoanSolverWrapper(flashloan_router.address())); Self { chain_id: network_id @@ -199,6 +208,7 @@ impl Contracts { cow_amm_helper: None, flashloan_wrapper_maker, flashloan_wrapper_aave, + flashloan_router, } } diff --git a/crates/e2e/src/setup/onchain_components/mod.rs b/crates/e2e/src/setup/onchain_components/mod.rs index aabb91eacb..1eec6dc364 100644 --- a/crates/e2e/src/setup/onchain_components/mod.rs +++ b/crates/e2e/src/setup/onchain_components/mod.rs @@ -303,22 +303,15 @@ impl OnchainComponents { .expect("failed to add solver"); } - // TODO: remove when contracts are actually deployed + // TODO: remove when contract is actually deployed // flashloan wrapper also needs to be authorized self.contracts .gp_authenticator - .add_solver(self.contracts.flashloan_wrapper_maker.address()) + .add_solver(self.contracts.flashloan_router.address()) .from(auth_manager.clone()) .send() .await .expect("failed to add flashloan wrapper"); - self.contracts - .gp_authenticator - .add_solver(self.contracts.flashloan_wrapper_aave.address()) - .from(auth_manager) - .send() - .await - .expect("failed to add flashloan wrapper"); solvers }