From 744e1ee2831ae399d126f24d190d9e81a985500a Mon Sep 17 00:00:00 2001 From: miguelmtz <36620902+miguelmtzinf@users.noreply.github.com> Date: Tue, 13 Feb 2024 13:05:30 +0100 Subject: [PATCH 1/4] feat: revamp of paraswap adapters (#12) Co-authored-by: Parth Patel --- .gitignore | 1 + README.md | 237 +++++- foundry.toml | 6 +- lib/aave-address-book | 2 +- package.json | 2 +- scripts/psp.js | 24 +- src/contracts/BaseParaSwapAdapter.sol | 104 --- src/contracts/ParaSwapDebtSwapAdapterV2.sol | 15 +- src/contracts/ParaSwapDebtSwapAdapterV3.sol | 11 +- .../ParaSwapDebtSwapAdapterV3GHO.sol | 9 +- .../ParaSwapLiquiditySwapAdapterV2.sol | 54 ++ .../ParaSwapLiquiditySwapAdapterV3.sol | 55 ++ src/contracts/ParaSwapRepayAdapterV2.sol | 52 ++ src/contracts/ParaSwapRepayAdapterV3.sol | 53 ++ .../ParaSwapWithdrawSwapAdapterV2.sol | 52 ++ .../ParaSwapWithdrawSwapAdapterV3.sol | 52 ++ src/contracts/base/BaseParaSwapAdapter.sol | 123 +++ .../{ => base}/BaseParaSwapBuyAdapter.sol | 34 +- .../base/BaseParaSwapSellAdapter.sol | 125 +++ .../{ => base}/ParaSwapDebtSwapAdapter.sol | 17 +- .../base/ParaSwapLiquiditySwapAdapter.sol | 233 ++++++ src/contracts/base/ParaSwapRepayAdapter.sol | 291 +++++++ .../base/ParaSwapWithdrawSwapAdapter.sol | 77 ++ .../paraswap/AugustusRegistry.sol | 18 + .../paraswap}/IParaSwapAugustus.sol | 0 .../paraswap}/IParaSwapAugustusRegistry.sol | 0 .../interfaces/IAaveFlashLoanReceiver.sol} | 11 +- .../interfaces/IBaseParaSwapAdapter.sol | 70 ++ .../interfaces/IERC3156FlashBorrower.sol | 0 .../interfaces/IERC3156FlashLender.sol | 0 .../interfaces/IParaSwapDebtSwapAdapter.sol | 57 ++ .../IParaSwapLiquiditySwapAdapter.sol | 33 + .../interfaces/IParaSwapRepayAdapter.sol | 34 + .../IParaSwapWithdrawSwapAdapter.sol | 32 + src/interfaces/ICreditDelegationToken.sol | 46 -- src/interfaces/IFlashLoanReceiverBase.sol | 18 - src/interfaces/IFlashLoanSimpleReceiver.sol | 30 - src/interfaces/IParaswapDebtSwapAdapter.sol | 59 -- src/lib/AugustusRegistry.sol | 26 - .../Deploy_ParaSwapDebtSwapAdapter.s.sol | 52 +- .../Deploy_ParaSwapLiquiditySwapAdapter.s.sol | 123 +++ src/script/Deploy_ParaSwapRepayAdapter.s.sol | 123 +++ .../Deploy_ParaSwapWithdrawAdapter.s.sol | 123 +++ .../008cf99bc1ce700e9c5f66631c46785ab3355b0c | 0 .../0206ce28826d6d407b6a6fe1a1b08efea658ab3c | 1 + .../07bde2b1c107aee4017b330bde3d12e12fdca898 | 1 + .../0adb11c7c9b2eaacc253f853e5da606b7b2ee444 | 0 .../0db734c16b5f9ef4e90ff3523aa0594aaa2e9b8d | 0 .../0e0c5f8c55ff556fb635bfc5627c9abd9d0717fe | 0 .../0ebfa91ab79dfb0c8479fd458c8106f65c5b57aa | 1 + .../0ed0b110e587dbe355a7506f0f20f9d47970748c | 0 .../143933b8f3afbeb3654d8f41766a25855620197e | 0 .../16cca61e9a629120ceba0286bef37b750d8dffbe | 1 + .../17a7aa803ac5cbbdbef0c1a211f625eac136b884 | 1 + .../17eddcf2c497766999734d4f623ebf3fad6e2df1 | 1 + .../1bc8f403e290d1db8b2d51b2e76e1ab3b86879e8 | 1 + .../25bced76cd1532466727f7db257159a3bfeee7d1 | 0 .../2c9a59aa8dad846bc87b7277f335a51cddd6338a | 1 + .../2f9b0a370e8129042ff1ec12781c4cffecd24e3b | 1 + .../30a8a8befa9059a15374576bbd0d1030efd2566d | 0 .../31766d47cce8df104f81dcaa07ccb425b3efcc49 | 0 .../31f27fac12203c5177c7b12ab7a8dea95ffbc4d4 | 0 .../353c9f4637609a68600e1c46526d0c98f02ebf8a | 1 + .../37e7d88e75b27f030554977403995e6b8844b8c4 | 0 .../387d830ca6c3a001f3e3ef3668e985422df6ef71 | 1 + .../408543a431121a276c7278389e90263d874716d9 | 0 .../40e07574cf4b2adb2bcb86f11d1a1e4ca669f0cd | 1 + .../417ad23e21f840318b273b89c74fce83c215e2a0 | 1 + .../44afa50aac5288b0a63acf59aea34e79451986c7 | 0 .../4e7e1050c698ff528ca44c20251f5ea28b985fcc | 1 + .../524f30f5ea6116dbfac241bb8902ccfad60870b5 | 1 + .../539f20ad6d391844ef555ae37fe5720c8740c1f4 | 0 .../552c346313cf331377fcd0f51acab6c10f83d92d | 1 + .../554d008100c7b2336f60691acaa14e09c3b45370 | 0 .../55ec4a627ff9237531012c862bb00125e59ebebf | 0 .../5c70c2c29330e126ee4b32020e6aac1e5ac3a15b | 0 .../600f4fe1f97cbb9f835fceefc42a81c370973289 | 1 + .../69de66cc45ecb2b7888d38946a452d20342f0957 | 1 + .../6f04a16df882a3cb3172dc587bf388de121190a1 | 0 .../718870dfd36fb8767847d23bb356a4877634bbcf | 0 .../74a5b04eaf45250d4ad9f7ea2d646e3053d49b1f | 0 .../7843a847c808fafab02c4297592d06c9f70ccbf1 | 0 .../7ebc0ac3529428069322536b5ba677df6d147b59 | 1 + .../84c67c9990ebacbf2be5eb98dbc32069d4853fb7 | 1 + .../85867c9f069a103e4a7a578ec2ffbd52a4e9d4be | 0 .../8a9b0d05f6bd39b23fc63439f2f6aabcfc9316ea | 1 + .../8e35a8ee7df87f06ab9dc951bb8c088fc4cb2372 | 0 .../8f9caeba4101137ce6c14c14786904022ca1b683 | 1 + .../92446b25dafc5e93c7fae0be44bdbda56ffca7b6 | 1 + .../93d59e0680372f2fe5812f1658fce4cba2e4be8f | 1 + .../9723578eb7e58aca760e512d8591f9d05a1e61b0 | 0 .../9d7baacfacfa1b80390e1f950f5154ace3eed0d8 | 0 .../9e4692fc8c083de96468a9163b813b6c6dcf697a | 0 .../a3b419338b1be2384900c4351554503d82bb1375 | 1 + .../ab2992a23bc48dbae8ec248c8569e5f287cae5c0 | 0 .../bc5da0ef3ec5deea3c5e83620c15c8cbfeeca26b | 1 + .../bd30bc9192e72f67a1f61278186d2dbe21c8fec7 | 0 .../be8894d1d3ab9f23a068465d94d5ecce2f4b7819 | 1 + .../c59d4514d1e765e278f1e95421519d5fe27cbf5b | 1 + .../d1e37909777498809c8471e6aa9f0bc35e5ca979 | 1 + .../d1f9d42a184020562218c50a52e9222fe45991a0 | 0 .../d28769bfb9bb8244aab5cd85f4a7e6beaa4b1c88 | 1 + .../d2e88d719754fcdf7dc63f12513a667cc4345b2b | 1 + .../ddd091cbf567959daf3ad05ab2dd97ce28dcb6d9 | 1 + .../e2243940589331811ab9386f31299da274cb39ba | 1 + .../e2c45010b23135f7bd9a10341609f62329e91737 | 1 + .../eb4af82cce5b603457a2d55eefaceea68829c9a5 | 0 .../f23915ac73cd97a3c7ba5d80d9a2665f989b6913 | 0 .../f3c54b14ff21ecf96e8cd50e168db3088aded86c | 1 + src/tests/BuyAdapterFuzz.t.sol | 100 +++ {tests => src/tests}/DebtSwapV2.t.sol | 55 +- {tests => src/tests}/DebtSwapV3.t.sol | 65 +- {tests => src/tests}/DebtSwapV3GHO.t.sol | 51 +- src/tests/LiquiditySwapV2.t.sol | 660 +++++++++++++++ src/tests/LiquiditySwapV3.t.sol | 669 +++++++++++++++ src/tests/PSRouteFuzz.t.sol | 30 + src/tests/RepayAdapterV2.t.sol | 522 ++++++++++++ src/tests/RepayAdapterV3.t.sol | 526 ++++++++++++ src/tests/SellAdapterFuzz.t.sol | 92 +++ src/tests/WithdrawSwapV2.t.sol | 353 ++++++++ src/tests/WithdrawSwapV3.t.sol | 355 ++++++++ .../harness/ParaSwapBuyAdapterHarness.sol | 71 ++ .../harness/ParaSwapSellAdapterHarness.sol | 71 ++ {tests => src/tests}/utils/BaseTest.sol | 60 +- {tests => src/tests}/utils/SigUtils.sol | 2 - yarn.lock | 775 +++++++++--------- 126 files changed, 6120 insertions(+), 856 deletions(-) delete mode 100644 src/contracts/BaseParaSwapAdapter.sol create mode 100644 src/contracts/ParaSwapLiquiditySwapAdapterV2.sol create mode 100644 src/contracts/ParaSwapLiquiditySwapAdapterV3.sol create mode 100644 src/contracts/ParaSwapRepayAdapterV2.sol create mode 100644 src/contracts/ParaSwapRepayAdapterV3.sol create mode 100644 src/contracts/ParaSwapWithdrawSwapAdapterV2.sol create mode 100644 src/contracts/ParaSwapWithdrawSwapAdapterV3.sol create mode 100644 src/contracts/base/BaseParaSwapAdapter.sol rename src/contracts/{ => base}/BaseParaSwapBuyAdapter.sol (76%) create mode 100644 src/contracts/base/BaseParaSwapSellAdapter.sol rename src/contracts/{ => base}/ParaSwapDebtSwapAdapter.sol (95%) create mode 100644 src/contracts/base/ParaSwapLiquiditySwapAdapter.sol create mode 100644 src/contracts/base/ParaSwapRepayAdapter.sol create mode 100644 src/contracts/base/ParaSwapWithdrawSwapAdapter.sol create mode 100644 src/contracts/dependencies/paraswap/AugustusRegistry.sol rename src/{interfaces => contracts/dependencies/paraswap}/IParaSwapAugustus.sol (100%) rename src/{interfaces => contracts/dependencies/paraswap}/IParaSwapAugustusRegistry.sol (100%) rename src/{interfaces/IFlashLoanReceiver.sol => contracts/interfaces/IAaveFlashLoanReceiver.sol} (74%) create mode 100644 src/contracts/interfaces/IBaseParaSwapAdapter.sol rename src/{ => contracts}/interfaces/IERC3156FlashBorrower.sol (100%) rename src/{ => contracts}/interfaces/IERC3156FlashLender.sol (100%) create mode 100644 src/contracts/interfaces/IParaSwapDebtSwapAdapter.sol create mode 100644 src/contracts/interfaces/IParaSwapLiquiditySwapAdapter.sol create mode 100644 src/contracts/interfaces/IParaSwapRepayAdapter.sol create mode 100644 src/contracts/interfaces/IParaSwapWithdrawSwapAdapter.sol delete mode 100644 src/interfaces/ICreditDelegationToken.sol delete mode 100644 src/interfaces/IFlashLoanReceiverBase.sol delete mode 100644 src/interfaces/IFlashLoanSimpleReceiver.sol delete mode 100644 src/interfaces/IParaswapDebtSwapAdapter.sol delete mode 100644 src/lib/AugustusRegistry.sol rename scripts/Deploy.s.sol => src/script/Deploy_ParaSwapDebtSwapAdapter.s.sol (62%) create mode 100644 src/script/Deploy_ParaSwapLiquiditySwapAdapter.s.sol create mode 100644 src/script/Deploy_ParaSwapRepayAdapter.s.sol create mode 100644 src/script/Deploy_ParaSwapWithdrawAdapter.s.sol rename {tests/pspcache => src/tests/.pspcache}/008cf99bc1ce700e9c5f66631c46785ab3355b0c (100%) create mode 100644 src/tests/.pspcache/0206ce28826d6d407b6a6fe1a1b08efea658ab3c create mode 100644 src/tests/.pspcache/07bde2b1c107aee4017b330bde3d12e12fdca898 rename {tests/pspcache => src/tests/.pspcache}/0adb11c7c9b2eaacc253f853e5da606b7b2ee444 (100%) rename {tests/pspcache => src/tests/.pspcache}/0db734c16b5f9ef4e90ff3523aa0594aaa2e9b8d (100%) rename {tests/pspcache => src/tests/.pspcache}/0e0c5f8c55ff556fb635bfc5627c9abd9d0717fe (100%) create mode 100644 src/tests/.pspcache/0ebfa91ab79dfb0c8479fd458c8106f65c5b57aa rename {tests/pspcache => src/tests/.pspcache}/0ed0b110e587dbe355a7506f0f20f9d47970748c (100%) rename {tests/pspcache => src/tests/.pspcache}/143933b8f3afbeb3654d8f41766a25855620197e (100%) create mode 100644 src/tests/.pspcache/16cca61e9a629120ceba0286bef37b750d8dffbe create mode 100644 src/tests/.pspcache/17a7aa803ac5cbbdbef0c1a211f625eac136b884 create mode 100644 src/tests/.pspcache/17eddcf2c497766999734d4f623ebf3fad6e2df1 create mode 100644 src/tests/.pspcache/1bc8f403e290d1db8b2d51b2e76e1ab3b86879e8 rename {tests/pspcache => src/tests/.pspcache}/25bced76cd1532466727f7db257159a3bfeee7d1 (100%) create mode 100644 src/tests/.pspcache/2c9a59aa8dad846bc87b7277f335a51cddd6338a create mode 100644 src/tests/.pspcache/2f9b0a370e8129042ff1ec12781c4cffecd24e3b rename {tests/pspcache => src/tests/.pspcache}/30a8a8befa9059a15374576bbd0d1030efd2566d (100%) rename {tests/pspcache => src/tests/.pspcache}/31766d47cce8df104f81dcaa07ccb425b3efcc49 (100%) rename {tests/pspcache => src/tests/.pspcache}/31f27fac12203c5177c7b12ab7a8dea95ffbc4d4 (100%) create mode 100644 src/tests/.pspcache/353c9f4637609a68600e1c46526d0c98f02ebf8a rename {tests/pspcache => src/tests/.pspcache}/37e7d88e75b27f030554977403995e6b8844b8c4 (100%) create mode 100644 src/tests/.pspcache/387d830ca6c3a001f3e3ef3668e985422df6ef71 rename {tests/pspcache => src/tests/.pspcache}/408543a431121a276c7278389e90263d874716d9 (100%) create mode 100644 src/tests/.pspcache/40e07574cf4b2adb2bcb86f11d1a1e4ca669f0cd create mode 100644 src/tests/.pspcache/417ad23e21f840318b273b89c74fce83c215e2a0 rename {tests/pspcache => src/tests/.pspcache}/44afa50aac5288b0a63acf59aea34e79451986c7 (100%) create mode 100644 src/tests/.pspcache/4e7e1050c698ff528ca44c20251f5ea28b985fcc create mode 100644 src/tests/.pspcache/524f30f5ea6116dbfac241bb8902ccfad60870b5 rename {tests/pspcache => src/tests/.pspcache}/539f20ad6d391844ef555ae37fe5720c8740c1f4 (100%) create mode 100644 src/tests/.pspcache/552c346313cf331377fcd0f51acab6c10f83d92d rename {tests/pspcache => src/tests/.pspcache}/554d008100c7b2336f60691acaa14e09c3b45370 (100%) rename {tests/pspcache => src/tests/.pspcache}/55ec4a627ff9237531012c862bb00125e59ebebf (100%) rename {tests/pspcache => src/tests/.pspcache}/5c70c2c29330e126ee4b32020e6aac1e5ac3a15b (100%) create mode 100644 src/tests/.pspcache/600f4fe1f97cbb9f835fceefc42a81c370973289 create mode 100644 src/tests/.pspcache/69de66cc45ecb2b7888d38946a452d20342f0957 rename {tests/pspcache => src/tests/.pspcache}/6f04a16df882a3cb3172dc587bf388de121190a1 (100%) rename {tests/pspcache => src/tests/.pspcache}/718870dfd36fb8767847d23bb356a4877634bbcf (100%) rename {tests/pspcache => src/tests/.pspcache}/74a5b04eaf45250d4ad9f7ea2d646e3053d49b1f (100%) rename {tests/pspcache => src/tests/.pspcache}/7843a847c808fafab02c4297592d06c9f70ccbf1 (100%) create mode 100644 src/tests/.pspcache/7ebc0ac3529428069322536b5ba677df6d147b59 create mode 100644 src/tests/.pspcache/84c67c9990ebacbf2be5eb98dbc32069d4853fb7 rename {tests/pspcache => src/tests/.pspcache}/85867c9f069a103e4a7a578ec2ffbd52a4e9d4be (100%) create mode 100644 src/tests/.pspcache/8a9b0d05f6bd39b23fc63439f2f6aabcfc9316ea rename {tests/pspcache => src/tests/.pspcache}/8e35a8ee7df87f06ab9dc951bb8c088fc4cb2372 (100%) create mode 100644 src/tests/.pspcache/8f9caeba4101137ce6c14c14786904022ca1b683 create mode 100644 src/tests/.pspcache/92446b25dafc5e93c7fae0be44bdbda56ffca7b6 create mode 100644 src/tests/.pspcache/93d59e0680372f2fe5812f1658fce4cba2e4be8f rename {tests/pspcache => src/tests/.pspcache}/9723578eb7e58aca760e512d8591f9d05a1e61b0 (100%) rename {tests/pspcache => src/tests/.pspcache}/9d7baacfacfa1b80390e1f950f5154ace3eed0d8 (100%) rename {tests/pspcache => src/tests/.pspcache}/9e4692fc8c083de96468a9163b813b6c6dcf697a (100%) create mode 100644 src/tests/.pspcache/a3b419338b1be2384900c4351554503d82bb1375 rename {tests/pspcache => src/tests/.pspcache}/ab2992a23bc48dbae8ec248c8569e5f287cae5c0 (100%) create mode 100644 src/tests/.pspcache/bc5da0ef3ec5deea3c5e83620c15c8cbfeeca26b rename {tests/pspcache => src/tests/.pspcache}/bd30bc9192e72f67a1f61278186d2dbe21c8fec7 (100%) create mode 100644 src/tests/.pspcache/be8894d1d3ab9f23a068465d94d5ecce2f4b7819 create mode 100644 src/tests/.pspcache/c59d4514d1e765e278f1e95421519d5fe27cbf5b create mode 100644 src/tests/.pspcache/d1e37909777498809c8471e6aa9f0bc35e5ca979 rename {tests/pspcache => src/tests/.pspcache}/d1f9d42a184020562218c50a52e9222fe45991a0 (100%) create mode 100644 src/tests/.pspcache/d28769bfb9bb8244aab5cd85f4a7e6beaa4b1c88 create mode 100644 src/tests/.pspcache/d2e88d719754fcdf7dc63f12513a667cc4345b2b create mode 100644 src/tests/.pspcache/ddd091cbf567959daf3ad05ab2dd97ce28dcb6d9 create mode 100644 src/tests/.pspcache/e2243940589331811ab9386f31299da274cb39ba create mode 100644 src/tests/.pspcache/e2c45010b23135f7bd9a10341609f62329e91737 rename {tests/pspcache => src/tests/.pspcache}/eb4af82cce5b603457a2d55eefaceea68829c9a5 (100%) rename {tests/pspcache => src/tests/.pspcache}/f23915ac73cd97a3c7ba5d80d9a2665f989b6913 (100%) create mode 100644 src/tests/.pspcache/f3c54b14ff21ecf96e8cd50e168db3088aded86c create mode 100644 src/tests/BuyAdapterFuzz.t.sol rename {tests => src/tests}/DebtSwapV2.t.sol (88%) rename {tests => src/tests}/DebtSwapV3.t.sol (88%) rename {tests => src/tests}/DebtSwapV3GHO.t.sol (87%) create mode 100644 src/tests/LiquiditySwapV2.t.sol create mode 100644 src/tests/LiquiditySwapV3.t.sol create mode 100644 src/tests/PSRouteFuzz.t.sol create mode 100644 src/tests/RepayAdapterV2.t.sol create mode 100644 src/tests/RepayAdapterV3.t.sol create mode 100644 src/tests/SellAdapterFuzz.t.sol create mode 100644 src/tests/WithdrawSwapV2.t.sol create mode 100644 src/tests/WithdrawSwapV3.t.sol create mode 100644 src/tests/harness/ParaSwapBuyAdapterHarness.sol create mode 100644 src/tests/harness/ParaSwapSellAdapterHarness.sol rename {tests => src/tests}/utils/BaseTest.sol (60%) rename {tests => src/tests}/utils/SigUtils.sol (96%) diff --git a/.gitignore b/.gitignore index d95519b..57e7b2d 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ node_modules # ignore foundry deploy artifacts broadcast/ + diff --git a/README.md b/README.md index 18ccc9c..b29baeb 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,23 @@ -# BGD labs <> Aave Debt Swap Adapter +# Aave ParaSwap Adapters -This repository contains the [ParaSwapDebtSwapAdapter](./src/contracts/ParaSwapDebtSwapAdapter.sol), which aims to allow users to arbitrage borrow APY and exit illiquid debt positions. +This repository contains adapter contracts for ParaSwap: + +- [ParaSwapDebtSwapAdapter](./src/contracts/ParaSwapDebtSwapAdapter.sol) +- [ParaSwapLiquidityAdapter](./src/contracts/ParaSwapLiquidityAdapter.sol) +- [ParaSwapRepayAdapter](./src/contracts/ParaSwapRepayAdapter.sol) +- [ParaSwapWithdrawAdapter](./src/contracts/ParaSwapWithdrawAdapter.sol) + +## ParaSwapDebtSwapAdapter + +ParaSwapDebtSwapAdapter aims to allow users to arbitrage borrow APY and exit illiquid debt positions. Therefore, this contract is able to swap one debt position to another debt position - either partially or completely. You could for example swap your `1000 BUSD` debt to `max(1010 USDC)` debt. In order to perform this task, `swapDebt`: 1. Creates a flashLoan with variable debt mode with the **target debt**(`1010 USDC`) on behalf of the user - - On aave v2 you need to approve the debtSwapAdapter for credit delegation - - On aave v3 you can also pass a credit delegation permit + - On Aave V2 you need to approve the debtSwapAdapter for credit delegation + - On Aave V3 you can also pass a credit delegation permit 2. It then swaps the flashed assets to the underlying of the **current debt**(`1000 BUSD`) via exact out swap (meaning it will receive `1000 BUSD`, but might only need `1000.1 USDC` for the swap) 3. Repays the **current debt** (`1000 BUSD`) 4. Uses potential (`9.9 USDC`) to repay parts of the newly created **target debt** @@ -17,25 +26,27 @@ The user has now payed off his `1000 BUSD` debt position, and created a new `100 In situations where a user's real loan-to-value (LTV) is higher than their maximum LTV but lower than their liquidation threshold (LT), extra collateral is needed to "wrap" around the flashloan-and-swap outlined above. The flow would then look like this: -1. Create a standard, repayable flashloan with the specified extra collateral asset and amount -2. Supply the flashed collateral on behalf of the user -3. Create the variable debt flashloan with the **target debt**(`1010 USDC`) on behalf of the user -4. Swap the flashloaned target debt asset to the underlying of the **current debt**(`1000 BUSD`), needing only `1000.1 USDC` -5. Repay the **current debt** (`1000 BUSD`) -6. Repay the flashloaned collateral asset and premium if needed (requires `aToken` approval) -7. Use the remaining new debt asset (`9.9 USDC`) to repay parts of the newly created **target debt** +1. Creates a standard, repayable flashloan with the specified extra collateral asset and amount +2. Supplies the flashed collateral on behalf of the user +3. Creates the variable debt flashloan with the **target debt**(`1010 USDC`) on behalf of the user +4. Swaps the flashloaned target debt asset to the underlying of the **current debt**(`1000 BUSD`), needing only `1000.1 USDC` +5. Repays the **current debt** (`1000 BUSD`) +6. Repays the flashloaned collateral asset and premium if needed (requires `aToken` approval) +7. Uses the remaining new debt asset (`9.9 USDC`) to repay parts of the newly created **target debt** Notice how steps 3, 4, 5, and 7 are the same four steps from the collateral-less flow. The guidelines for selecting a proper extra collateral asset are as follows: For Aave V3: + 1. Ensure that the potential asset's LTV is nonzero. 2. Ensure that the potential asset's LT is nonzero. 3. Ensure that the potential asset's Supply Cap has sufficient capacity. -4. If the user is in isolation mode, ensure the asset is the same as the isolated collateral asset. +4. If the user is in isolation mode, ensure the asset is the same as the isolated collateral asset. For Aave V2: + 1. Ensure that the potential asset's LTV is nonzero. 2. Ensure that the potential asset's LT is nonzero. 3. Ensure that the extra collateral asset is the same as the new debt asset. @@ -100,29 +111,211 @@ struct PermitInput { bytes32 r; bytes32 s; } + +``` + +## ParaSwapLiquidityAdapter + +ParaSwapLiquidityAdapter aims to allow users to arbitrage supply APY. +Therefore, this contract is able to swap one collateral position to another collateral position - either partially or completely. + +You could for example swap your `1000 BUSD` collateral to `min(995 USDC)` collateral. In order to perform this task, `swapLiquidity`: + +1. Pulls the `1000 aBUSD` token from user and withdraws `1000 BUSD` from pool. (requires `aToken` approval) +2. It then swaps the collateral asset to the new collateral asset via exact in swap (meaning it will send `1000 BUSD` for the swap and receive at least `995 USDC`) +3. Supplies the received `995 USDC` to the pool on behalf of user and user receives `995 aUSDC`. + +The user has now swapped off his `1000 BUSD` collateral position, and created a new `995 USDC` collateral position. + +In situations where a user's real loan-to-value (LTV) is higher than their maximum LTV but lower than their liquidation threshold (LT), extra collateral is needed in the steps outlined above. The flow would then look like this(assuming flashloan premium as `0.09%`): + +1. Creates a standard, repayable flashloan with the collateral asset(`BUSD`) and amount equals to the collateral to swap(`1000`). +2. Swaps the collateral asset via exact in with amount excluding the flashloan premium(`1000 BUSD` - flashloan premium = `999.1 BUSD`) to the new collateral asset(`USDC`). Flashloan premium stays in the contract so repayment is guaranteed. +3. Supplies the `USDC` received in step 2 as a collateral in the pool on behalf of user. +4. Pulls the `1000 aBUSD` from the user and withdraws `1000 BUSD` from the pool. (requires `aToken` approval) +5. Repays `1000 BUSD` flashloan and `0.9 BUSD` premium. + +The `function swapLiquidity(LiquiditySwapParams memory liquiditySwapParams, PermitInput memory collateralATokenPermit)` expects three parameters. + +The first one describes the swap: + +```solidity +struct LiquiditySwapParams { + address collateralAsset; // the asset to swap collateral from + uint256 collateralAmountToSwap; // the amount of asset to swap from + address newCollateralAsset; // the asset to swap collateral to + uint256 newCollateralAmount; // the minimum amount of new collateral asset to receive + uint256 offset; // offset in sell calldata in case of swapping all collateral, otherwise 0 + address user; // the address of user + bool withFlashLoan; // true if flashloan is needed to swap collateral, otherwise false + bytes paraswapData; // encoded paraswap data +} + +``` + +The second one describes the (optional) collateral aToken permit: + +```solidity +struct PermitInput { + IERC20WithPermit aToken; + uint256 value; + uint256 deadline; + uint8 v; + bytes32 r; + bytes32 s; +} + +``` + +## ParaSwapRepayAdapter + +ParaSwapRepayAdapter aims to allow users to repay debt using collateral position. +Therefore, this contract is able to swap one collateral position to repay borrow position - either partially or completely. + +You could for example repay `1000 USDC` borrow position by swapping your `max(1005 BUSD)` that are supplied as collateral. In order to perform this task, `repayWithCollateral`: + +1. Pulls the `1005 aBUSD` token from user and withdraws `1005 BUSD` from pool. (requires `aToken` approval) +2. It then swaps the collateral asset to the borrow asset via exact out swap (meaning it will send `max(1005 BUSD)` for the swap but receive exact `1000 USDC`) +3. Repays the borrow position with received `1000 USDC` on behalf of user. + +The user has now repaid a `1000 USDC` borrow position by swapping off his `1005 BUSD` collateral position. + +In situations where a user's real loan-to-value (LTV) is higher than their maximum LTV but lower than their liquidation threshold (LT), extra collateral is needed in the steps outlined above. The flow would then look like this(assuming flashloan premium as `0.09%`): + +1. Creates a standard, repayable flashloan with the collateral asset(`BUSD`) with value equivalent to the value of collateral asset to be used for the repayment. +2. Swaps the flashed assets to the borrow asset(`USDC`) via exact out. +3. Repays the borrow position with received `USDC` in step 2 on behalf of user. +4. Pull the `aBUSD` from the user equivalent to the value of (flashloan + premium - unutilized flashloan asset in step 2). (requires `aToken` approval) +5. Repays the flashloan along with premium. + +The `function repayWithCollateral(RepayParams memory repayParams, FlashParams memory flashParams, PermitInput memory collateralATokenPermit)` expects three parameters. + +The first one describes the repay params: + +```solidity +struct RepayParams { + address collateralAsset; // the asset you want to swap collateral from + uint256 maxCollateralAmountToSwap; // the max amount you want to swap from + address debtRepayAsset; // the asset you want to repay the debt + uint256 debtRepayAmount; // the amount of debt to repay + uint256 debtRepayMode; // debt interest rate mode (1 for stable, 2 for variable) + uint256 offset; // offset in buy calldata in case of swapping all collateral, otherwise 0 + bool withFlashLoan; // true if flashloan is needed to repay the debt, otherwise false + address user; // the address of user + bytes paraswapData; // encoded paraswap data +} + +``` + +The second one describes the (optional) collateral aToken permit: + +```solidity +struct PermitInput { + IERC20WithPermit aToken; + uint256 value; + uint256 deadline; + uint8 v; + bytes32 r; + bytes32 s; +} + +``` + +## ParaSwapWithdrawSwapAdapter + +ParaSwapRepayAdapter aims to allow users to withdraw their collateral and swap it to other asset. + +You could for example withdraw your `1000 BUSD` collateral and convert the received collateral to `min(995 USDC)`. In order to perform this task, `withdrawAndSwap`: + +1. Pulls the `1000 aBUSD` token from user and withdraws `1000 BUSD` from pool. (requires `aToken` approval) +2. It then swaps the BUSD to the USDC via exact in swap (meaning it will send `1000 BUSD` for the swap and receive at least `995 USDC`). +3. Transfers the new asset amount `995 USDC` to the user. + +The user has now withdraw a 1000 BUSD collateral position and swapped it off to 995 USDC. + +The `function withdrawAndSwap(WithdrawSwapParams memory withdrawSwapParams, PermitInput memory permitInput)` expects two parameters. + +The first one describes the withdraw params: + +```solidity +struct WithdrawSwapParams { + address oldAsset; // the asset to withdraw and swap from + uint256 oldAssetAmount; // the amount to withdraw + address newAsset; // the asset to swap to + uint256 minAmountToReceive; // the minimum amount of new asset to receive + uint256 allBalanceOffset; // offset in sell calldata in case of swapping all collateral, otherwise 0 + address user; // the address of user + bytes paraswapData; // encoded paraswap data +} + +``` + +The second one describes the (optional) collateral aToken permit: + +```solidity +struct PermitInput { + IERC20WithPermit aToken; + uint256 value; + uint256 deadline; + uint8 v; + bytes32 r; + bytes32 s; +} + ``` For usage examples please check the [tests](./tests/). ## Security -- This contract is a extra layer on top of [BaseParaswapBuyAdapter](./src/contracts/BaseParaSwapBuyAdapter.sol) which is used in production for [ParaSwapRepayAdapter](https://github.com/aave/aave-v3-periphery/blob/master/contracts/adapters/paraswap/ParaSwapRepayAdapter.sol). It uses the exact same mechanism for exact out swap. +Security considerations around the ParaSwap adapter contracts: -- In contrast to ParaSwapRepayAdapter the ParaSwapDebtSwapAdapter will always repay on the pool on behalf of the user. So instead of having approvals per transaction the adapter will approve `type(uint256).max` once to reduce gas consumption. +- The adapter contracts are built on top of [BaseParaswapBuyAdapter](./src/contracts/BaseParaSwapBuyAdapter.sol) and [BaseParaswapSellAdapter](./src/contracts/BaseParaSwapSellAdapter.sol) which has been used in production for the previous version of these adapters ([Aave Protocol](https://github.com/Aave/Aave-V3-periphery/blob/master/contracts/adapters/paraswap)). + +- The adapter contracts always act on behalf of the user. So instead of having approvals per transaction the adapter will approve `type(uint256).max` once to reduce gas consumption. - The Aave `POOL` is considered a trustable entity for allowance purposes. -- The contract only interact with `msg.sender` and therefore ensures isolation between users. +- Contracts only interact with 1 single user per action, ensuring isolation between users. -- The contract is not upgradable. +- Contracts are not upgradable. -- The contract is ownable and will be owned by governance, so the governance will be the only entity able to call `tokenRescue`. +- Contracts are ownable and will be owned by governance, so the governance will be the only entity able to call `tokenRescue`. - The approach with credit delegation and borrow-mode flashLoans is very similar to what is done on [V2-V3 Migration helper](https://github.com/bgd-labs/V2-V3-migration-helpers) -- The contract inherits the security and limitations of Aave v2/v3. The contract itself does not validate for frozen/inactive reserves and also does not consider isolation/eMode or borrowCaps. It is the responsibility of the interface integrating this contract to correctly handle all user position compositions and pool configurations. +- Contracts inherit the security and limitations of Aave V2/V3. Contracts themselves do not validate for frozen/inactive reserves and also do not consider isolation/eMode or borrowCaps. It is the responsibility of the interface integrating these contracts to correctly handle all user position compositions and pool configurations. + +- Contracts implement an upper bound of 30% price impact, which would revert any swap. The slippage has to be properly configured in incorporated into: + + - `DebtSwapParams.maxNewDebt` parameter for `ParaSwapDebtSwapAdapter` + - `LiquiditySwapParams.newCollateralAmount` parameter for `ParaSwapLiquiditySwapAdapter` + - `RepayParams.maxCollateralAmountToSwap` parameter for `ParaSwapRepayAdapter` + - `WithdrawSwapParams.minAmountToReceive` parameter for `ParaSwapWithdrawSwapAdapter` -- The contract implements an upper bound of 30% price impact, which would revert any swap. The slippage has to be properly configured in incorporated into the `DebtSwapParams.maxNewDebt` parameter. +- Contracts are using SELL and BUY operations of ParaSwap for swaps, which allows to designate exact input or output for swaps: + + - A SELL action of X means the adapter contract will spend X exactly for the swap. In case of receiving more than X, is considered dust and automatically donated to the contract. + - A BUY action of Y means the adapter contract will receive Y exactly as a result of the swap. In case of receiving more than Y, is considered dust and automatically donated to the contract. + +- Contracts support Aave V2 and V3. There are contracts specifically designed for each version, as well as for working with GHO. + +- Using full balance of the user for actions supported by these adapter contracts require manipulating the ParaSwap calldata passed to the ParaSwap Augustus contract, and some of the swap routes do not support it. Therefore, in the case where full balance is used, the ParaSwap API call be made using a preferred set of methods. + - Excluding the following routes when calculating a swap, using `excludeContractMethods` option: + - BUY: `simpleBuy`, `directUniV3Buy` and `directBalancerV2GivenOutSwap`. + - SELL: `simpleSwap`, `directUniV3Swap`, `directBalancerV2GivenInSwap`, `directBalancerV2GivenOutSwap`, `directCurveV1Swap` and `directCurveV2Swap`. + - Including only these route when calculating a swap, using `includeContractMethods` option: + - BUY: `buy`. + - SELL: `multiSwap` and `megaSwap`. + +- ParaSwap extracts token surplus if ParaSwap positive slippage happens: the trade ends up with a positive result favoring the user (e.g. receiving more assets than expected in a BUY, receiving more assets than expected for same amount of assets in exchange in a SELL). + - Positive slippage means the trade was more efficient than expected, so user is not impacted by the surplus extraction theoretically (e.g. they will get as much tokens as expected). However, a misconfiguration or bad integration with these contracts can lead to artificially create positive slippage. + - Using full balance of users position for an action is a bit problematic and could lead to positive slippage if transaction swap amounts highly differ from amounts used for the ParaSwap API Call. Highly recommended to estimate properly the full balance the user will have at the transaction execution time. + - When using full balance, the ParaSwap `offset` is set to non-zero value (depends on the action) the `amount` is set to a high value (higher than the current balance of the user) so contracts override the amount with the last updated value of the user position. This could artificially create positive slippage if the amount used for the ParaSwap API call highly differs from the amount that is finally used on the transaction execution (the actual one). + - Example for `BUY`: API call of `buy(x,y)` and swap transaction of `buy(x',y')`. If `y'>y` then `x'>x`, so positive slippage happens as user is receiving more assets than expected. + - Example for `SELL`: API call of `sell(x,y)` and swap transaction of `sell(x',y')`. If `x'>x` then `y'>y`, so positive slippage happens as user is receiving more assets than expected. + - In `ParaSwapLiquditySwapAdapter`, fetching of ParaSwap route for swapping from collateral asset to another asset with flashloan enabled should take `flashloanFee` into consideration. As `ParaSwapLiquditySwapAdapter` swaps `(collateralAmountToSwap - flashloanFee)` to guarantee that `flashloanFee` is paid, generating routes with `(collateralAmountToSwap - flashloanFee) `is recommended. + - Example: User wants to swap `1000 BUSD` collateral to `min(995 USDC)` collateral and with flashloan enabled. The ParaSwap route should be generated for selling (1000 BUSD - 0.9 BUSD) to buy USDC assuming (0.09% flashloan fee). Thus, ParaSwapLiquditySwapAdapter will flashloan (1000 BUSD) but will sell (1000 BUSD - 0.9 BUSD) to ensure that 0.9 BUSD stays in the contract to pay flashloan premium. ## Install @@ -148,8 +341,6 @@ forge test ## References -This code is based on [the existing aave paraswap adapters](https://github.com/aave/aave-v3-periphery/tree/master/contracts/adapters/paraswap) for v3. - -The [BaseParaSwapAdapter.sol](./src/contracts/BaseParaSwapAdapter.sol) was slightly adjusted to receive the POOL via constructor instead of fetching it. +This code is a fork of [Aave Debt Swap Adapter](https://github.com/bgd-labs/Aave-debt-swap) contract by [BGD Labs](https://github.com/bgd-labs). Intention is to create a modern version of [the existing Aave paraswap adapters](https://github.com/Aave/Aave-V3-periphery/tree/master/contracts/adapters/paraswap) for V3, by extending the code of the Aave Debt Swap Adapter. -This makes the code agnostic for v2 and v3, as the only methods used are unchanged between the two versions. +Furthermore, these contracts are heavily inspired by the [ParaSwap adapter contracts of Aave Protocol V2](https://github.com/Aave/protocol-V2/tree/master/contracts/adapters) written by ParaSwap team. diff --git a/foundry.toml b/foundry.toml index ec8deb5..6aa64f1 100644 --- a/foundry.toml +++ b/foundry.toml @@ -1,7 +1,7 @@ [profile.default] -src = 'src' -test = 'tests' -script = 'scripts' +src = 'src/contracts' +test = 'src/tests' +script = 'src/script' out = 'out' libs = ['lib'] remappings = [ diff --git a/lib/aave-address-book b/lib/aave-address-book index 9f5c33d..7fce6c0 160000 --- a/lib/aave-address-book +++ b/lib/aave-address-book @@ -1 +1 @@ -Subproject commit 9f5c33da4813dbd885b9f888c1cf6234340f71e7 +Subproject commit 7fce6c033431fc4984c740c676195cade3191f1b diff --git a/package.json b/package.json index 8117f9b..f7c1d13 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "prettier-plugin-solidity": "^1.0.0-beta.19" }, "dependencies": { - "@paraswap/sdk": "^6.2.1", + "@paraswap/sdk": "^6.4.0", "axios": "^1.1.3", "ethers": "^5.7.2", "object-hash": "^3.0.0" diff --git a/scripts/psp.js b/scripts/psp.js index d214c53..c5956a2 100644 --- a/scripts/psp.js +++ b/scripts/psp.js @@ -24,6 +24,7 @@ const FROM_DECIMALS = Number(args[8]); const TO_DECIMALS = Number(args[9]); // param only needed for the hash // const BLOCK_NUMBER = Number(args[10]); +const UPDATE_PSP_CACHE = args[11] !== "false"; // generate a hash for input parameters to cache response and not spam psp sdk const hash = objectHash(args); @@ -60,11 +61,11 @@ function augustusFromAmountOffsetFromCalldata(calldata) { case "0x19fc5be0": // directBalancerV2GivenOutSwap return 68; // 4 + 2 * 32 case "0x3865bde6": // directCurveV1Swap - return 68; // 4 + 2 * 32 + return 132; // 4 + 4 * 32 case "0x58f15100": // directCurveV2Swap return 68; // 4 + 2 * 32 - case "0xa6866da9": // directUniV3Swap - return 68; // 4 + 2 * 32 + case "0xa6886da9": // directUniV3Swap + return 132; // 4 + 4 * 32 default: throw new Error("Unrecognized function selector for Augustus"); } @@ -91,17 +92,17 @@ const augustusToAmountOffsetFromCalldata = (calldata) => { async function main(from, to, method, amount, user) { // check cache and return cache if available - const filePath = path.join(process.cwd(), "tests/pspcache", hash); + const filePath = path.join(process.cwd(), "src/tests/.pspcache", hash); if (fs.existsSync(filePath)) { const file = fs.readFileSync(filePath); process.stdout.write(file); return; } - // distinguish between exactOut and exactInoutdMethod - const excludedMethod = + // distinguish between exactOut and exactInOutMethod + const preferredMethods = method === "SELL" - ? [ContractMethod.simpleSwap] - : [ContractMethod.simpleBuy, ContractMethod.directUniV3Buy]; + ? [ContractMethod.multiSwap, ContractMethod.megaSwap] + : [ContractMethod.buy]; const priceRoute = await paraSwapMin.swap.getRate({ srcToken: from, srcDecimals: FROM_DECIMALS, @@ -112,7 +113,7 @@ async function main(from, to, method, amount, user) { ...(MAX ? { options: { - excludeContractMethods: [...excludedMethod], + includeContractMethods: [...preferredMethods], }, } : {}), @@ -158,8 +159,9 @@ async function main(from, to, method, amount, user) { ["(address,bytes,uint256,uint256,uint256)"], [[txParams.to, txParams.data, srcAmount, destAmount, offset]] ); - - fs.writeFileSync(filePath, encodedData); + if (UPDATE_PSP_CACHE) { + fs.writeFileSync(filePath, encodedData); + } process.stdout.write(encodedData); } main(FROM, TO, METHOD, AMOUNT, USER_ADDRESS); diff --git a/src/contracts/BaseParaSwapAdapter.sol b/src/contracts/BaseParaSwapAdapter.sol deleted file mode 100644 index 932a5b1..0000000 --- a/src/contracts/BaseParaSwapAdapter.sol +++ /dev/null @@ -1,104 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.10; - -import {DataTypes} from '@aave/core-v3/contracts/protocol/libraries/types/DataTypes.sol'; -import {IERC20} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/IERC20.sol'; -import {IERC20Detailed} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/IERC20Detailed.sol'; -import {IERC20WithPermit} from '@aave/core-v3/contracts/interfaces/IERC20WithPermit.sol'; -import {IPoolAddressesProvider} from '@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol'; -import {IPool} from '@aave/core-v3/contracts/interfaces/IPool.sol'; -import {IPriceOracleGetter} from '@aave/core-v3/contracts/interfaces/IPriceOracleGetter.sol'; -import {SafeERC20} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/SafeERC20.sol'; -import {Ownable} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/Ownable.sol'; -import {IFlashLoanReceiverBase} from '../interfaces/IFlashLoanReceiverBase.sol'; - -/** - * @title BaseParaSwapAdapter - * @notice Utility functions for adapters using ParaSwap - * @author Jason Raymond Bell - */ -abstract contract BaseParaSwapAdapter is IFlashLoanReceiverBase, Ownable { - using SafeERC20 for IERC20; - using SafeERC20 for IERC20Detailed; - using SafeERC20 for IERC20WithPermit; - - struct PermitSignature { - uint256 amount; - uint256 deadline; - uint8 v; - bytes32 r; - bytes32 s; - } - - // Max slippage percent allowed - uint256 public constant MAX_SLIPPAGE_PERCENT = 3000; // 30% - - IPriceOracleGetter public immutable ORACLE; - IPoolAddressesProvider public immutable ADDRESSES_PROVIDER; - IPool public immutable POOL; - - event Swapped( - address indexed fromAsset, - address indexed toAsset, - uint256 fromAmount, - uint256 receivedAmount - ); - event Bought( - address indexed fromAsset, - address indexed toAsset, - uint256 amountSold, - uint256 receivedAmount - ); - - constructor(IPoolAddressesProvider addressesProvider, address pool) { - ORACLE = IPriceOracleGetter(addressesProvider.getPriceOracle()); - ADDRESSES_PROVIDER = addressesProvider; - POOL = IPool(pool); - } - - /** - * @dev Get the price of the asset from the oracle denominated in eth - * @param asset address - * @return eth price for the asset - */ - function _getPrice(address asset) internal view returns (uint256) { - return ORACLE.getAssetPrice(asset); - } - - /** - * @dev Get the decimals of an asset - * @return number of decimals of the asset - */ - function _getDecimals(IERC20Detailed asset) internal view returns (uint8) { - uint8 decimals = asset.decimals(); - // Ensure 10**decimals won't overflow a uint256 - require(decimals <= 77, 'TOO_MANY_DECIMALS_ON_TOKEN'); - return decimals; - } - - /** - * @dev Get the vToken, sToken associated to the asset - * @return address of the vToken - * @return address of the sToken - * @return address of the aToken - */ - function _getReserveData(address asset) internal view virtual returns (address, address, address); - - /** - * @dev Supply "amount" of "asset" to Aave - * @param asset Address of the asset to be supplied - * @param amount Amount of the asset to be supplied - * @param to Address receiving the aTokens - * @param referralCode Referral code to pass to Aave - */ - function _supply(address asset, uint256 amount, address to, uint16 referralCode) internal virtual; - - /** - * @dev Emergency rescue for token stucked on this contract, as failsafe mechanism - * - Funds should never remain in this contract more time than during transactions - * - Only callable by the owner - */ - function rescueTokens(IERC20 token) external onlyOwner { - token.safeTransfer(owner(), token.balanceOf(address(this))); - } -} diff --git a/src/contracts/ParaSwapDebtSwapAdapterV2.sol b/src/contracts/ParaSwapDebtSwapAdapterV2.sol index 2a6caea..c76134b 100644 --- a/src/contracts/ParaSwapDebtSwapAdapterV2.sol +++ b/src/contracts/ParaSwapDebtSwapAdapterV2.sol @@ -1,10 +1,11 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.10; -import {ParaSwapDebtSwapAdapter} from './ParaSwapDebtSwapAdapter.sol'; import {IPoolAddressesProvider} from '@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol'; -import {IParaSwapAugustusRegistry} from '../interfaces/IParaSwapAugustusRegistry.sol'; import {DataTypes, ILendingPool} from 'aave-address-book/AaveV2.sol'; +import {IParaSwapAugustusRegistry} from './dependencies/paraswap/IParaSwapAugustusRegistry.sol'; +import {BaseParaSwapAdapter} from './base/BaseParaSwapAdapter.sol'; +import {ParaSwapDebtSwapAdapter} from './base/ParaSwapDebtSwapAdapter.sol'; /** * @title ParaSwapDebtSwapAdapter @@ -17,9 +18,14 @@ contract ParaSwapDebtSwapAdapterV2 is ParaSwapDebtSwapAdapter { address pool, IParaSwapAugustusRegistry augustusRegistry, address owner - ) ParaSwapDebtSwapAdapter(addressesProvider, pool, augustusRegistry, owner) {} + ) ParaSwapDebtSwapAdapter(addressesProvider, pool, augustusRegistry, owner) { + // Intentionally left blank + } - function _getReserveData(address asset) internal view override returns (address, address, address) { + /// @inheritdoc BaseParaSwapAdapter + function _getReserveData( + address asset + ) internal view override returns (address, address, address) { DataTypes.ReserveData memory reserveData = ILendingPool(address(POOL)).getReserveData(asset); return ( reserveData.variableDebtTokenAddress, @@ -28,6 +34,7 @@ contract ParaSwapDebtSwapAdapterV2 is ParaSwapDebtSwapAdapter { ); } + /// @inheritdoc BaseParaSwapAdapter function _supply( address asset, uint256 amount, diff --git a/src/contracts/ParaSwapDebtSwapAdapterV3.sol b/src/contracts/ParaSwapDebtSwapAdapterV3.sol index 9ae344c..9d76de2 100644 --- a/src/contracts/ParaSwapDebtSwapAdapterV3.sol +++ b/src/contracts/ParaSwapDebtSwapAdapterV3.sol @@ -1,11 +1,12 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.10; -import {ParaSwapDebtSwapAdapter} from './ParaSwapDebtSwapAdapter.sol'; import {IPoolAddressesProvider} from '@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol'; -import {IParaSwapAugustusRegistry} from '../interfaces/IParaSwapAugustusRegistry.sol'; import {IPool} from '@aave/core-v3/contracts/interfaces/IPool.sol'; import {DataTypes} from '@aave/core-v3/contracts/protocol/libraries/types/DataTypes.sol'; +import {IParaSwapAugustusRegistry} from './dependencies/paraswap/IParaSwapAugustusRegistry.sol'; +import {BaseParaSwapAdapter} from './base/BaseParaSwapAdapter.sol'; +import {ParaSwapDebtSwapAdapter} from './base/ParaSwapDebtSwapAdapter.sol'; /** * @title ParaSwapDebtSwapAdapter @@ -18,8 +19,11 @@ contract ParaSwapDebtSwapAdapterV3 is ParaSwapDebtSwapAdapter { address pool, IParaSwapAugustusRegistry augustusRegistry, address owner - ) ParaSwapDebtSwapAdapter(addressesProvider, pool, augustusRegistry, owner) {} + ) ParaSwapDebtSwapAdapter(addressesProvider, pool, augustusRegistry, owner) { + // Intentionally left blank + } + /// @inheritdoc BaseParaSwapAdapter function _getReserveData( address asset ) internal view override returns (address, address, address) { @@ -31,6 +35,7 @@ contract ParaSwapDebtSwapAdapterV3 is ParaSwapDebtSwapAdapter { ); } + /// @inheritdoc BaseParaSwapAdapter function _supply( address asset, uint256 amount, diff --git a/src/contracts/ParaSwapDebtSwapAdapterV3GHO.sol b/src/contracts/ParaSwapDebtSwapAdapterV3GHO.sol index a1da142..db0b216 100644 --- a/src/contracts/ParaSwapDebtSwapAdapterV3GHO.sol +++ b/src/contracts/ParaSwapDebtSwapAdapterV3GHO.sol @@ -1,15 +1,16 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.10; -import {ParaSwapDebtSwapAdapterV3} from './ParaSwapDebtSwapAdapterV3.sol'; import {IPoolAddressesProvider} from '@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol'; import {IERC20Detailed} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/IERC20Detailed.sol'; import {IPool} from '@aave/core-v3/contracts/interfaces/IPool.sol'; import {DataTypes} from '@aave/core-v3/contracts/protocol/libraries/types/DataTypes.sol'; import {IERC20} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/IERC20.sol'; -import {IParaSwapAugustusRegistry} from '../interfaces/IParaSwapAugustusRegistry.sol'; -import {IERC3156FlashBorrower} from '../interfaces/IERC3156FlashBorrower.sol'; -import {IERC3156FlashLender} from '../interfaces/IERC3156FlashLender.sol'; +import {IParaSwapAugustusRegistry} from './dependencies/paraswap/IParaSwapAugustusRegistry.sol'; +import {IERC3156FlashBorrower} from './interfaces/IERC3156FlashBorrower.sol'; +import {IERC3156FlashLender} from './interfaces/IERC3156FlashLender.sol'; +import {BaseParaSwapAdapter} from './base/BaseParaSwapAdapter.sol'; +import {ParaSwapDebtSwapAdapterV3} from './ParaSwapDebtSwapAdapterV3.sol'; // send collateral if needed via params /** diff --git a/src/contracts/ParaSwapLiquiditySwapAdapterV2.sol b/src/contracts/ParaSwapLiquiditySwapAdapterV2.sol new file mode 100644 index 0000000..2645992 --- /dev/null +++ b/src/contracts/ParaSwapLiquiditySwapAdapterV2.sol @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.10; + +import {IPoolAddressesProvider} from '@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol'; +import {DataTypes, ILendingPool} from 'aave-address-book/AaveV2.sol'; +import {IParaSwapAugustusRegistry} from './dependencies/paraswap/IParaSwapAugustusRegistry.sol'; +import {BaseParaSwapAdapter} from './base/BaseParaSwapAdapter.sol'; +import {ParaSwapLiquiditySwapAdapter} from './base/ParaSwapLiquiditySwapAdapter.sol'; + +/** + * @title ParaSwapLiquiditySwapAdapterV2 + * @notice ParaSwap Adapter to perform a swap of collateral from one asset to another. + * @dev It is specifically designed for Aave V2 + * @author Aave Labs + **/ +contract ParaSwapLiquiditySwapAdapterV2 is ParaSwapLiquiditySwapAdapter { + /** + * @dev Constructor + * @param addressesProvider The address of the Aave PoolAddressesProvider contract + * @param pool The address of the Aave Pool contract + * @param augustusRegistry The address of the Paraswap AugustusRegistry contract + * @param owner The address of the owner + */ + constructor( + IPoolAddressesProvider addressesProvider, + address pool, + IParaSwapAugustusRegistry augustusRegistry, + address owner + ) ParaSwapLiquiditySwapAdapter(addressesProvider, pool, augustusRegistry, owner) { + // Intentionally left blank + } + + /// @inheritdoc BaseParaSwapAdapter + function _getReserveData( + address asset + ) internal view override returns (address, address, address) { + DataTypes.ReserveData memory reserveData = ILendingPool(address(POOL)).getReserveData(asset); + return ( + reserveData.variableDebtTokenAddress, + reserveData.stableDebtTokenAddress, + reserveData.aTokenAddress + ); + } + + /// @inheritdoc BaseParaSwapAdapter + function _supply( + address asset, + uint256 amount, + address to, + uint16 referralCode + ) internal override { + ILendingPool(address(POOL)).deposit(asset, amount, to, referralCode); + } +} diff --git a/src/contracts/ParaSwapLiquiditySwapAdapterV3.sol b/src/contracts/ParaSwapLiquiditySwapAdapterV3.sol new file mode 100644 index 0000000..727b6c9 --- /dev/null +++ b/src/contracts/ParaSwapLiquiditySwapAdapterV3.sol @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.10; + +import {IPoolAddressesProvider} from '@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol'; +import {IPool} from '@aave/core-v3/contracts/interfaces/IPool.sol'; +import {DataTypes} from '@aave/core-v3/contracts/protocol/libraries/types/DataTypes.sol'; +import {IParaSwapAugustusRegistry} from './dependencies/paraswap/IParaSwapAugustusRegistry.sol'; +import {BaseParaSwapAdapter} from './base/BaseParaSwapAdapter.sol'; +import {ParaSwapLiquiditySwapAdapter} from './base/ParaSwapLiquiditySwapAdapter.sol'; + +/** + * @title ParaSwapLiquiditySwapAdapterV3 + * @notice ParaSwap Adapter to perform a swap of collateral from one asset to another. + * @dev It is specifically designed for Aave V3 + * @author Aave Labs + **/ +contract ParaSwapLiquiditySwapAdapterV3 is ParaSwapLiquiditySwapAdapter { + /** + * @dev Constructor + * @param addressesProvider The address of the Aave PoolAddressesProvider contract + * @param pool The address of the Aave Pool contract + * @param augustusRegistry The address of the Paraswap AugustusRegistry contract + * @param owner The address of the owner + */ + constructor( + IPoolAddressesProvider addressesProvider, + address pool, + IParaSwapAugustusRegistry augustusRegistry, + address owner + ) ParaSwapLiquiditySwapAdapter(addressesProvider, pool, augustusRegistry, owner) { + // Intentionally left blank + } + + /// @inheritdoc BaseParaSwapAdapter + function _getReserveData( + address asset + ) internal view override returns (address, address, address) { + DataTypes.ReserveData memory reserveData = POOL.getReserveData(asset); + return ( + reserveData.variableDebtTokenAddress, + reserveData.stableDebtTokenAddress, + reserveData.aTokenAddress + ); + } + + /// @inheritdoc BaseParaSwapAdapter + function _supply( + address asset, + uint256 amount, + address to, + uint16 referralCode + ) internal override { + POOL.supply(asset, amount, to, referralCode); + } +} diff --git a/src/contracts/ParaSwapRepayAdapterV2.sol b/src/contracts/ParaSwapRepayAdapterV2.sol new file mode 100644 index 0000000..cad982c --- /dev/null +++ b/src/contracts/ParaSwapRepayAdapterV2.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.10; + +import {IPoolAddressesProvider} from '@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol'; +import {DataTypes, ILendingPool} from 'aave-address-book/AaveV2.sol'; +import {IParaSwapAugustusRegistry} from './dependencies/paraswap/IParaSwapAugustusRegistry.sol'; +import {BaseParaSwapAdapter} from './base/BaseParaSwapAdapter.sol'; +import {ParaSwapRepayAdapter} from './base/ParaSwapRepayAdapter.sol'; + +/** + * @title ParaSwapRepayAdapterV2 + * @notice ParaSwap Adapter to repay debt with collateral. + * @dev It is specifically designed for Aave V2 + * @author Aave Labs + **/ +contract ParaSwapRepayAdapterV2 is ParaSwapRepayAdapter { + /** + * @dev Constructor + * @param addressesProvider The address of the Aave PoolAddressesProvider contract + * @param pool The address of the Aave Pool contract + * @param augustusRegistry The address of the Paraswap AugustusRegistry contract + * @param owner The address of the owner + */ + constructor( + IPoolAddressesProvider addressesProvider, + address pool, + IParaSwapAugustusRegistry augustusRegistry, + address owner + ) ParaSwapRepayAdapter(addressesProvider, pool, augustusRegistry, owner) {} + + /// @inheritdoc BaseParaSwapAdapter + function _getReserveData( + address asset + ) internal view override returns (address, address, address) { + DataTypes.ReserveData memory reserveData = ILendingPool(address(POOL)).getReserveData(asset); + return ( + reserveData.variableDebtTokenAddress, + reserveData.stableDebtTokenAddress, + reserveData.aTokenAddress + ); + } + + /// @inheritdoc BaseParaSwapAdapter + function _supply( + address asset, + uint256 amount, + address to, + uint16 referralCode + ) internal override { + ILendingPool(address(POOL)).deposit(asset, amount, to, referralCode); + } +} diff --git a/src/contracts/ParaSwapRepayAdapterV3.sol b/src/contracts/ParaSwapRepayAdapterV3.sol new file mode 100644 index 0000000..2570dda --- /dev/null +++ b/src/contracts/ParaSwapRepayAdapterV3.sol @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.10; + +import {IPoolAddressesProvider} from '@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol'; +import {IParaSwapAugustusRegistry} from './dependencies/paraswap/IParaSwapAugustusRegistry.sol'; +import {IPool} from '@aave/core-v3/contracts/interfaces/IPool.sol'; +import {DataTypes} from '@aave/core-v3/contracts/protocol/libraries/types/DataTypes.sol'; +import {BaseParaSwapAdapter} from './base/BaseParaSwapAdapter.sol'; +import {ParaSwapRepayAdapter} from './base/ParaSwapRepayAdapter.sol'; + +/** + * @title ParaSwapRepayAdapterV3 + * @notice ParaSwap Adapter to repay debt with collateral. + * @dev It is specifically designed for Aave V3 + * @author Aave Labs + **/ +contract ParaSwapRepayAdapterV3 is ParaSwapRepayAdapter { + /** + * @dev Constructor + * @param addressesProvider The address of the Aave PoolAddressesProvider contract + * @param pool The address of the Aave Pool contract + * @param augustusRegistry The address of the Paraswap AugustusRegistry contract + * @param owner The address of the owner + */ + constructor( + IPoolAddressesProvider addressesProvider, + address pool, + IParaSwapAugustusRegistry augustusRegistry, + address owner + ) ParaSwapRepayAdapter(addressesProvider, pool, augustusRegistry, owner) {} + + /// @inheritdoc BaseParaSwapAdapter + function _getReserveData( + address asset + ) internal view override returns (address, address, address) { + DataTypes.ReserveData memory reserveData = POOL.getReserveData(asset); + return ( + reserveData.variableDebtTokenAddress, + reserveData.stableDebtTokenAddress, + reserveData.aTokenAddress + ); + } + + /// @inheritdoc BaseParaSwapAdapter + function _supply( + address asset, + uint256 amount, + address to, + uint16 referralCode + ) internal override { + POOL.supply(asset, amount, to, referralCode); + } +} diff --git a/src/contracts/ParaSwapWithdrawSwapAdapterV2.sol b/src/contracts/ParaSwapWithdrawSwapAdapterV2.sol new file mode 100644 index 0000000..f2147a5 --- /dev/null +++ b/src/contracts/ParaSwapWithdrawSwapAdapterV2.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.10; + +import {IPoolAddressesProvider} from '@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol'; +import {DataTypes, ILendingPool} from 'aave-address-book/AaveV2.sol'; +import {IParaSwapAugustusRegistry} from './dependencies/paraswap/IParaSwapAugustusRegistry.sol'; +import {BaseParaSwapAdapter} from './base/BaseParaSwapAdapter.sol'; +import {ParaSwapWithdrawSwapAdapter} from './base/ParaSwapWithdrawSwapAdapter.sol'; + +/** + * @title ParaSwapWithdrawSwapAdapterV2 + * @notice ParaSwap Adapter to withdraw and swap. + * @dev It is specifically designed for Aave V2 + * @author Aave Labs + **/ +contract ParaSwapWithdrawSwapAdapterV2 is ParaSwapWithdrawSwapAdapter { + /** + * @dev Constructor + * @param addressesProvider The address of the Aave PoolAddressesProvider contract + * @param pool The address of the Aave Pool contract + * @param augustusRegistry The address of the Paraswap AugustusRegistry contract + * @param owner The address of the owner + */ + constructor( + IPoolAddressesProvider addressesProvider, + address pool, + IParaSwapAugustusRegistry augustusRegistry, + address owner + ) ParaSwapWithdrawSwapAdapter(addressesProvider, pool, augustusRegistry, owner) {} + + /// @inheritdoc BaseParaSwapAdapter + function _getReserveData( + address asset + ) internal view override returns (address, address, address) { + DataTypes.ReserveData memory reserveData = ILendingPool(address(POOL)).getReserveData(asset); + return ( + reserveData.variableDebtTokenAddress, + reserveData.stableDebtTokenAddress, + reserveData.aTokenAddress + ); + } + + /// @inheritdoc BaseParaSwapAdapter + function _supply( + address asset, + uint256 amount, + address to, + uint16 referralCode + ) internal override { + ILendingPool(address(POOL)).deposit(asset, amount, to, referralCode); + } +} diff --git a/src/contracts/ParaSwapWithdrawSwapAdapterV3.sol b/src/contracts/ParaSwapWithdrawSwapAdapterV3.sol new file mode 100644 index 0000000..31aa206 --- /dev/null +++ b/src/contracts/ParaSwapWithdrawSwapAdapterV3.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.10; + +import {IPoolAddressesProvider} from '@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol'; +import {DataTypes} from '@aave/core-v3/contracts/protocol/libraries/types/DataTypes.sol'; +import {IParaSwapAugustusRegistry} from './dependencies/paraswap/IParaSwapAugustusRegistry.sol'; +import {BaseParaSwapAdapter} from './base/BaseParaSwapAdapter.sol'; +import {ParaSwapWithdrawSwapAdapter} from './base/ParaSwapWithdrawSwapAdapter.sol'; + +/** + * @title ParaSwapWithdrawSwapAdapterV3 + * @notice ParaSwap Adapter to withdraw and swap. + * @dev It is specifically designed for Aave V3 + * @author Aave Labs + **/ +contract ParaSwapWithdrawSwapAdapterV3 is ParaSwapWithdrawSwapAdapter { + /** + * @dev Constructor + * @param addressesProvider The address of the Aave PoolAddressesProvider contract + * @param pool The address of the Aave Pool contract + * @param augustusRegistry The address of the Paraswap AugustusRegistry contract + * @param owner The address of the owner + */ + constructor( + IPoolAddressesProvider addressesProvider, + address pool, + IParaSwapAugustusRegistry augustusRegistry, + address owner + ) ParaSwapWithdrawSwapAdapter(addressesProvider, pool, augustusRegistry, owner) {} + + /// @inheritdoc BaseParaSwapAdapter + function _getReserveData( + address asset + ) internal view override returns (address, address, address) { + DataTypes.ReserveData memory reserveData = POOL.getReserveData(asset); + return ( + reserveData.variableDebtTokenAddress, + reserveData.stableDebtTokenAddress, + reserveData.aTokenAddress + ); + } + + /// @inheritdoc BaseParaSwapAdapter + function _supply( + address asset, + uint256 amount, + address to, + uint16 referralCode + ) internal override { + POOL.supply(asset, amount, to, referralCode); + } +} diff --git a/src/contracts/base/BaseParaSwapAdapter.sol b/src/contracts/base/BaseParaSwapAdapter.sol new file mode 100644 index 0000000..51057da --- /dev/null +++ b/src/contracts/base/BaseParaSwapAdapter.sol @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.10; + +import {IERC20} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/IERC20.sol'; +import {IERC20Detailed} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/IERC20Detailed.sol'; +import {IPoolAddressesProvider} from '@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol'; +import {IPool} from '@aave/core-v3/contracts/interfaces/IPool.sol'; +import {IPriceOracleGetter} from '@aave/core-v3/contracts/interfaces/IPriceOracleGetter.sol'; +import {SafeERC20} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/SafeERC20.sol'; +import {Ownable} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/Ownable.sol'; +import {IBaseParaSwapAdapter} from '../interfaces/IBaseParaSwapAdapter.sol'; + +/** + * @title BaseParaSwapAdapter + * @notice Utility functions for adapters using ParaSwap + * @author Jason Raymond Bell + */ +abstract contract BaseParaSwapAdapter is Ownable, IBaseParaSwapAdapter { + using SafeERC20 for IERC20; + + // @inheritdoc IBaseParaSwapAdapter + uint256 public constant MAX_SLIPPAGE_PERCENT = 0.3e4; // 30.00% + + // @inheritdoc IBaseParaSwapAdapter + IPriceOracleGetter public immutable ORACLE; + + /// The address of the Aave PoolAddressesProvider contract + IPoolAddressesProvider public immutable ADDRESSES_PROVIDER; + + /// The address of the Aave Pool contract + IPool public immutable POOL; + + /** + * @dev Constructor + * @param addressesProvider The address of the Aave PoolAddressesProvider contract + * @param pool The address of the Aave Pool contract + */ + constructor(IPoolAddressesProvider addressesProvider, address pool) { + ORACLE = IPriceOracleGetter(addressesProvider.getPriceOracle()); + ADDRESSES_PROVIDER = addressesProvider; + POOL = IPool(pool); + } + + /// @inheritdoc IBaseParaSwapAdapter + function rescueTokens(IERC20 token) external onlyOwner { + token.safeTransfer(owner(), token.balanceOf(address(this))); + } + + /** + * @dev Get the price of the asset from the oracle + * @param asset The address of the asset + * @return The price of the asset, based on the oracle denomination units + */ + function _getPrice(address asset) internal view returns (uint256) { + return ORACLE.getAssetPrice(asset); + } + + /** + * @dev Get the decimals of an asset + * @param asset The address of the asset + * @return number of decimals of the asset + */ + function _getDecimals(IERC20Detailed asset) internal view returns (uint8) { + uint8 decimals = asset.decimals(); + // Ensure 10**decimals won't overflow a uint256 + require(decimals <= 77, 'TOO_MANY_DECIMALS_ON_TOKEN'); + return decimals; + } + + /** + * @dev Get the vToken, sToken and aToken associated to the asset + * @param asset The address of the asset + * @return address The address of the VariableDebtToken, vToken + * @return address The address of the StableDebtToken, sToken + * @return address The address of the aToken + */ + function _getReserveData(address asset) internal view virtual returns (address, address, address); + + /** + * @dev Supply an amount of asset to the Aave Pool + * @param asset The address of the asset to be supplied + * @param amount The amount of the asset to be supplied + * @param to The address receiving the aTokens + * @param referralCode The referral code to pass to Aave + */ + function _supply(address asset, uint256 amount, address to, uint16 referralCode) internal virtual; + + /** + * @dev Pull the ATokens from the user and withdraws the underlying asset from the Aave Pool + * @param reserve The address of the asset + * @param user The address of the user to pull aTokens from + * @param amount The amount of tokens to be pulled and withdrawn + * @param permitInput struct containing the permit signature + */ + function _pullATokenAndWithdraw( + address reserve, + address user, + uint256 amount, + PermitInput memory permitInput + ) internal returns (uint256) { + // If deadline is set to zero, assume there is no signature for permit + if (permitInput.deadline != 0) { + permitInput.aToken.permit( + user, + address(this), + permitInput.value, + permitInput.deadline, + permitInput.v, + permitInput.r, + permitInput.s + ); + } + + (, , address aToken) = _getReserveData(reserve); + + uint256 aTokenBalanceBefore = IERC20(aToken).balanceOf(address(this)); + IERC20(aToken).safeTransferFrom(user, address(this), amount); + uint256 aTokenBalanceDiff = IERC20(aToken).balanceOf(address(this)) - aTokenBalanceBefore; + + POOL.withdraw(reserve, aTokenBalanceDiff, address(this)); + return aTokenBalanceDiff; + } +} diff --git a/src/contracts/BaseParaSwapBuyAdapter.sol b/src/contracts/base/BaseParaSwapBuyAdapter.sol similarity index 76% rename from src/contracts/BaseParaSwapBuyAdapter.sol rename to src/contracts/base/BaseParaSwapBuyAdapter.sol index 52e1089..f8afed2 100644 --- a/src/contracts/BaseParaSwapBuyAdapter.sol +++ b/src/contracts/base/BaseParaSwapBuyAdapter.sol @@ -5,20 +5,27 @@ import {PercentageMath} from '@aave/core-v3/contracts/protocol/libraries/math/Pe import {IPoolAddressesProvider} from '@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol'; import {IERC20Detailed} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/IERC20Detailed.sol'; import {SafeERC20} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/SafeERC20.sol'; -import {IParaSwapAugustus} from '../interfaces/IParaSwapAugustus.sol'; -import {IParaSwapAugustusRegistry} from '../interfaces/IParaSwapAugustusRegistry.sol'; +import {IParaSwapAugustus} from '../dependencies/paraswap/IParaSwapAugustus.sol'; +import {IParaSwapAugustusRegistry} from '../dependencies/paraswap/IParaSwapAugustusRegistry.sol'; import {BaseParaSwapAdapter} from './BaseParaSwapAdapter.sol'; /** * @title BaseParaSwapBuyAdapter - * @notice Implements the logic for buying tokens on ParaSwap + * @notice Implements logic for buying an asset using ParaSwap (exact-out swap) */ abstract contract BaseParaSwapBuyAdapter is BaseParaSwapAdapter { using SafeERC20 for IERC20Detailed; using PercentageMath for uint256; + /// @notice The address of the Paraswap Augustus Registry IParaSwapAugustusRegistry public immutable AUGUSTUS_REGISTRY; + /** + * @dev Constructor + * @param addressesProvider The address of the Aave PoolAddressesProvider contract + * @param pool The address of the Aave Pool contract + * @param augustusRegistry The address of the Paraswap AugustusRegistry contract + */ constructor( IPoolAddressesProvider addressesProvider, address pool, @@ -30,14 +37,15 @@ abstract contract BaseParaSwapBuyAdapter is BaseParaSwapAdapter { } /** - * @dev Swaps a token for another using ParaSwap + * @dev Swaps a token for another using ParaSwap (exact out) + * @dev In case the swap output is higher than the designated amount to buy, the excess remains in the contract * @param toAmountOffset Offset of toAmount in Augustus calldata if it should be overwritten, otherwise 0 * @param paraswapData Data for Paraswap Adapter - * @param assetToSwapFrom Address of the asset to be swapped from - * @param assetToSwapTo Address of the asset to be swapped to - * @param maxAmountToSwap Max amount to be swapped - * @param amountToReceive Amount to be received from the swap - * @return amountSold The amount sold during the swap + * @param assetToSwapFrom The address of the asset to swap from + * @param assetToSwapTo The address of the asset to swap to + * @param maxAmountToSwap The maximum amount of asset to swap from + * @param amountToReceive The amount of asset to receive + * @return amountSold The amount of asset sold */ function _buyOnParaSwap( uint256 toAmountOffset, @@ -51,7 +59,6 @@ abstract contract BaseParaSwapBuyAdapter is BaseParaSwapAdapter { paraswapData, (bytes, IParaSwapAugustus) ); - require(AUGUSTUS_REGISTRY.isValidAugustus(address(augustus)), 'INVALID_AUGUSTUS'); { @@ -65,11 +72,13 @@ abstract contract BaseParaSwapBuyAdapter is BaseParaSwapAdapter { (toAssetPrice * (10 ** fromAssetDecimals))) / (fromAssetPrice * (10 ** toAssetDecimals))) .percentMul(PercentageMath.PERCENTAGE_FACTOR + MAX_SLIPPAGE_PERCENT); - require(maxAmountToSwap <= expectedMaxAmountToSwap, 'maxAmountToSwap exceed max slippage'); + // Sanity check for `maxAmountToSwap` to ensure it is within slippage bounds + require(maxAmountToSwap <= expectedMaxAmountToSwap, 'maxAmountToSwap exceeds max slippage'); } uint256 balanceBeforeAssetFrom = assetToSwapFrom.balanceOf(address(this)); require(balanceBeforeAssetFrom >= maxAmountToSwap, 'INSUFFICIENT_BALANCE_BEFORE_SWAP'); + uint256 balanceBeforeAssetTo = assetToSwapTo.balanceOf(address(this)); address tokenTransferProxy = augustus.getTokenTransferProxy(); @@ -99,9 +108,12 @@ abstract contract BaseParaSwapBuyAdapter is BaseParaSwapAdapter { } } + // Amount provided should be less or equal than `maxAmountToSwap` uint256 balanceAfterAssetFrom = assetToSwapFrom.balanceOf(address(this)); amountSold = balanceBeforeAssetFrom - balanceAfterAssetFrom; require(amountSold <= maxAmountToSwap, 'WRONG_BALANCE_AFTER_SWAP'); + + // Amount received should be equal (or even higher) than `amountToReceive` uint256 amountReceived = assetToSwapTo.balanceOf(address(this)) - balanceBeforeAssetTo; require(amountReceived >= amountToReceive, 'INSUFFICIENT_AMOUNT_RECEIVED'); diff --git a/src/contracts/base/BaseParaSwapSellAdapter.sol b/src/contracts/base/BaseParaSwapSellAdapter.sol new file mode 100644 index 0000000..07265a0 --- /dev/null +++ b/src/contracts/base/BaseParaSwapSellAdapter.sol @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.10; + +import {PercentageMath} from '@aave/core-v3/contracts/protocol/libraries/math/PercentageMath.sol'; +import {IPoolAddressesProvider} from '@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol'; +import {IERC20Detailed} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/IERC20Detailed.sol'; +import {SafeERC20} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/SafeERC20.sol'; +import {IParaSwapAugustus} from '../dependencies/paraswap/IParaSwapAugustus.sol'; +import {IParaSwapAugustusRegistry} from '../dependencies/paraswap/IParaSwapAugustusRegistry.sol'; +import {BaseParaSwapAdapter} from './BaseParaSwapAdapter.sol'; + +/** + * @title BaseParaSwapSellAdapter + * @notice Implements logic for selling an asset using ParaSwap (exact-in swap) + */ +abstract contract BaseParaSwapSellAdapter is BaseParaSwapAdapter { + using SafeERC20 for IERC20Detailed; + using PercentageMath for uint256; + + /// @notice The address of the Paraswap Augustus Registry + IParaSwapAugustusRegistry public immutable AUGUSTUS_REGISTRY; + + /** + * @dev Constructor + * @param addressesProvider The address of the Aave PoolAddressesProvider contract + * @param pool The address of the Aave Pool contract + * @param augustusRegistry The address of the Paraswap AugustusRegistry contract + */ + constructor( + IPoolAddressesProvider addressesProvider, + address pool, + IParaSwapAugustusRegistry augustusRegistry + ) BaseParaSwapAdapter(addressesProvider, pool) { + // Do something on Augustus registry to check the right contract was passed + require(!augustusRegistry.isValidAugustus(address(0)), 'Not a valid Augustus address'); + AUGUSTUS_REGISTRY = augustusRegistry; + } + + /** + * @dev Swaps a token for another using ParaSwap (exact in) + * @dev In case the swap input is less than the designated amount to sell, the excess remains in the contract + * @param fromAmountOffset Offset of fromAmount in Augustus calldata if it should be overwritten, otherwise 0 + * @param paraswapData Data for Paraswap Adapter + * @param assetToSwapFrom The address of the asset to swap from + * @param assetToSwapTo The address of the asset to swap to + * @param amountToSwap The amount of asset to swap from + * @param minAmountToReceive The minimum amount to receive + * @return amountReceived The amount of asset bought + */ + function _sellOnParaSwap( + uint256 fromAmountOffset, + bytes memory paraswapData, + IERC20Detailed assetToSwapFrom, + IERC20Detailed assetToSwapTo, + uint256 amountToSwap, + uint256 minAmountToReceive + ) internal returns (uint256 amountReceived) { + (bytes memory swapCalldata, IParaSwapAugustus augustus) = abi.decode( + paraswapData, + (bytes, IParaSwapAugustus) + ); + require(AUGUSTUS_REGISTRY.isValidAugustus(address(augustus)), 'INVALID_AUGUSTUS'); + + { + uint256 fromAssetDecimals = _getDecimals(assetToSwapFrom); + uint256 toAssetDecimals = _getDecimals(assetToSwapTo); + + uint256 fromAssetPrice = _getPrice(address(assetToSwapFrom)); + uint256 toAssetPrice = _getPrice(address(assetToSwapTo)); + + uint256 expectedMinAmountOut = ((amountToSwap * (fromAssetPrice * (10 ** toAssetDecimals))) / + (toAssetPrice * (10 ** fromAssetDecimals))).percentMul( + PercentageMath.PERCENTAGE_FACTOR - MAX_SLIPPAGE_PERCENT + ); + + // Sanity check for `minAmountToReceive` to ensure it is within slippage bounds + require( + expectedMinAmountOut <= minAmountToReceive, + 'minAmountToReceive exceeds max slippage' + ); + } + + uint256 balanceBeforeAssetFrom = assetToSwapFrom.balanceOf(address(this)); + require(balanceBeforeAssetFrom >= amountToSwap, 'INSUFFICIENT_BALANCE_BEFORE_SWAP'); + + uint256 balanceBeforeAssetTo = assetToSwapTo.balanceOf(address(this)); + + address tokenTransferProxy = augustus.getTokenTransferProxy(); + assetToSwapFrom.safeApprove(tokenTransferProxy, 0); + assetToSwapFrom.safeApprove(tokenTransferProxy, amountToSwap); + + if (fromAmountOffset != 0) { + // Ensure 256 bit (32 bytes) fromAmountOffset value is within bounds of the + // calldata, not overlapping with the first 4 bytes (function selector). + require( + fromAmountOffset >= 4 && fromAmountOffset <= swapCalldata.length - 32, + 'FROM_AMOUNT_OFFSET_OUT_OF_RANGE' + ); + // Overwrite the fromAmount with the correct amount for the swap. + // In memory, swapCalldata consists of a 256 bit length field, followed by + // the actual bytes data, that is why 32 is added to the byte offset. + assembly { + mstore(add(swapCalldata, add(fromAmountOffset, 32)), amountToSwap) + } + } + (bool success, ) = address(augustus).call(swapCalldata); + if (!success) { + // Copy revert reason from call + assembly { + returndatacopy(0, 0, returndatasize()) + revert(0, returndatasize()) + } + } + + // Amount provided should be equal (or even less) than `amountToSwap` + uint256 amountSold = balanceBeforeAssetFrom - assetToSwapFrom.balanceOf(address(this)); + require(amountToSwap <= amountSold, 'WRONG_BALANCE_AFTER_SWAP'); + + // Amount received should be higher or equal `minAmountToReceive` + amountReceived = assetToSwapTo.balanceOf(address(this)) - balanceBeforeAssetTo; + require(amountReceived >= minAmountToReceive, 'INSUFFICIENT_AMOUNT_RECEIVED'); + + emit Swapped(address(assetToSwapFrom), address(assetToSwapTo), amountSold, amountReceived); + } +} diff --git a/src/contracts/ParaSwapDebtSwapAdapter.sol b/src/contracts/base/ParaSwapDebtSwapAdapter.sol similarity index 95% rename from src/contracts/ParaSwapDebtSwapAdapter.sol rename to src/contracts/base/ParaSwapDebtSwapAdapter.sol index 79eadc6..a0e716b 100644 --- a/src/contracts/ParaSwapDebtSwapAdapter.sol +++ b/src/contracts/base/ParaSwapDebtSwapAdapter.sol @@ -4,16 +4,15 @@ pragma solidity ^0.8.10; import {DataTypes} from '@aave/core-v3/contracts/protocol/libraries/types/DataTypes.sol'; import {IERC20Detailed} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/IERC20Detailed.sol'; import {IERC20} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/IERC20.sol'; -import {IERC20WithPermit} from 'solidity-utils/contracts/oz-common/interfaces/IERC20WithPermit.sol'; import {IPoolAddressesProvider} from '@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol'; +import {ICreditDelegationToken} from '@aave/core-v3/contracts/interfaces/ICreditDelegationToken.sol'; import {ReentrancyGuard} from 'aave-v3-periphery/contracts/dependencies/openzeppelin/ReentrancyGuard.sol'; -import {BaseParaSwapBuyAdapter} from './BaseParaSwapBuyAdapter.sol'; -import {IParaSwapAugustusRegistry} from '../interfaces/IParaSwapAugustusRegistry.sol'; -import {IParaSwapAugustus} from '../interfaces/IParaSwapAugustus.sol'; -import {IFlashLoanReceiver} from '../interfaces/IFlashLoanReceiver.sol'; -import {ICreditDelegationToken} from '../interfaces/ICreditDelegationToken.sol'; +import {IERC20WithPermit} from 'solidity-utils/contracts/oz-common/interfaces/IERC20WithPermit.sol'; import {SafeERC20} from 'solidity-utils/contracts/oz-common/SafeERC20.sol'; -import {IParaswapDebtSwapAdapter} from '../interfaces/IParaswapDebtSwapAdapter.sol'; +import {IParaSwapAugustusRegistry} from '../dependencies/paraswap/IParaSwapAugustusRegistry.sol'; +import {IAaveFlashLoanReceiver} from '../interfaces/IAaveFlashLoanReceiver.sol'; +import {IParaSwapDebtSwapAdapter} from '../interfaces/IParaSwapDebtSwapAdapter.sol'; +import {BaseParaSwapBuyAdapter} from './BaseParaSwapBuyAdapter.sol'; /** * @title ParaSwapDebtSwapAdapter @@ -23,8 +22,8 @@ import {IParaswapDebtSwapAdapter} from '../interfaces/IParaswapDebtSwapAdapter.s abstract contract ParaSwapDebtSwapAdapter is BaseParaSwapBuyAdapter, ReentrancyGuard, - IFlashLoanReceiver, - IParaswapDebtSwapAdapter + IAaveFlashLoanReceiver, + IParaSwapDebtSwapAdapter { using SafeERC20 for IERC20WithPermit; diff --git a/src/contracts/base/ParaSwapLiquiditySwapAdapter.sol b/src/contracts/base/ParaSwapLiquiditySwapAdapter.sol new file mode 100644 index 0000000..d20f548 --- /dev/null +++ b/src/contracts/base/ParaSwapLiquiditySwapAdapter.sol @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.10; + +import {IERC20Detailed} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/IERC20Detailed.sol'; +import {IPoolAddressesProvider} from '@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol'; +import {IPool} from '@aave/core-v3/contracts/interfaces/IPool.sol'; +import {ReentrancyGuard} from 'aave-v3-periphery/contracts/dependencies/openzeppelin/ReentrancyGuard.sol'; +import {IERC20} from 'solidity-utils/contracts/oz-common/interfaces/IERC20.sol'; +import {SafeERC20} from 'solidity-utils/contracts/oz-common/SafeERC20.sol'; +import {IParaSwapAugustusRegistry} from '../dependencies/paraswap/IParaSwapAugustusRegistry.sol'; +import {IAaveFlashLoanReceiver} from '../interfaces/IAaveFlashLoanReceiver.sol'; +import {IParaSwapLiquiditySwapAdapter} from '../interfaces/IParaSwapLiquiditySwapAdapter.sol'; +import {BaseParaSwapSellAdapter} from './BaseParaSwapSellAdapter.sol'; + +/** + * @title ParaSwapLiquiditySwapAdapter + * @notice ParaSwap Adapter to perform a swap of collateral from one asset to another. + * @dev Swaps the existing collateral asset to another asset. It flash-borrows assets from the Aave Pool in case the + * user position does not remain collateralized during the operation. + * @author Aave Labs + **/ +abstract contract ParaSwapLiquiditySwapAdapter is + BaseParaSwapSellAdapter, + ReentrancyGuard, + IAaveFlashLoanReceiver, + IParaSwapLiquiditySwapAdapter +{ + using SafeERC20 for IERC20; + + // unique identifier to track usage via flashloan events + uint16 public constant REFERRER = 43980; // uint16(uint256(keccak256(abi.encode('liquidity-swap-adapter'))) / type(uint16).max) + + /** + * @dev Constructor + * @param addressesProvider The address of the Aave PoolAddressesProvider contract + * @param pool The address of the Aave Pool contract + * @param augustusRegistry The address of the Paraswap AugustusRegistry contract + * @param owner The address of the owner + */ + constructor( + IPoolAddressesProvider addressesProvider, + address pool, + IParaSwapAugustusRegistry augustusRegistry, + address owner + ) BaseParaSwapSellAdapter(addressesProvider, pool, augustusRegistry) { + transferOwnership(owner); + // set initial approval for all reserves + address[] memory reserves = POOL.getReservesList(); + for (uint256 i = 0; i < reserves.length; i++) { + IERC20(reserves[i]).safeApprove(address(POOL), type(uint256).max); + } + } + + /** + * @notice Renews the asset allowance to the Aave Pool + * @param reserve The address of the asset + */ + function renewAllowance(address reserve) public { + IERC20(reserve).safeApprove(address(POOL), 0); + IERC20(reserve).safeApprove(address(POOL), type(uint256).max); + } + + /// @inheritdoc IParaSwapLiquiditySwapAdapter + function swapLiquidity( + LiquiditySwapParams memory liquiditySwapParams, + PermitInput memory collateralATokenPermit + ) external nonReentrant { + // Offset in August calldata if wanting to swap all balance, otherwise 0 + if (liquiditySwapParams.offset != 0) { + (, , address aToken) = _getReserveData(liquiditySwapParams.collateralAsset); + uint256 balance = IERC20(aToken).balanceOf(liquiditySwapParams.user); + require(balance <= liquiditySwapParams.collateralAmountToSwap, 'INSUFFICIENT_AMOUNT_TO_SWAP'); + liquiditySwapParams.collateralAmountToSwap = balance; + } + + // true if flashloan is needed to swap liquidity + if (!liquiditySwapParams.withFlashLoan) { + _swapAndDeposit(liquiditySwapParams, collateralATokenPermit); + } else { + // flashloan of the current collateral asset + _flash(liquiditySwapParams, collateralATokenPermit); + } + } + + /** + * @dev Executes the collateral swap after receiving the flash-borrowed assets + * @dev Workflow: + * 1. Sell flash-borrowed asset for new collateral asset + * 2. Supply new collateral asset + * 3. Pull aToken collateral from user and withdraw from Pool + * 4. Repay flashloan + * @param assets The addresses of the flash-borrowed assets + * @param amounts The amounts of the flash-borrowed assets + * @param premiums The premiums of the flash-borrowed assets + * @param initiator The address of the flashloan initiator + * @param params The byte-encoded params passed when initiating the flashloan + * @return True if the execution of the operation succeeds, false otherwise + */ + function executeOperation( + address[] calldata assets, + uint256[] calldata amounts, + uint256[] calldata premiums, + address initiator, + bytes calldata params + ) external returns (bool) { + require(msg.sender == address(POOL), 'CALLER_MUST_BE_POOL'); + require(initiator == address(this), 'INITIATOR_MUST_BE_THIS'); + + ( + LiquiditySwapParams memory liquiditySwapParams, + PermitInput memory collateralATokenPermit + ) = abi.decode(params, (LiquiditySwapParams, PermitInput)); + + address flashLoanAsset = assets[0]; + uint256 flashLoanAmount = amounts[0]; + uint256 flashLoanPremium = premiums[0]; + + // sell the flashLoanAmount minus the premium, so flashloan repayment is guaranteed + // flashLoan premium stays in the contract + uint256 amountReceived = _sellOnParaSwap( + liquiditySwapParams.offset, + liquiditySwapParams.paraswapData, + IERC20Detailed(flashLoanAsset), + IERC20Detailed(liquiditySwapParams.newCollateralAsset), + flashLoanAmount - flashLoanPremium, + liquiditySwapParams.newCollateralAmount + ); + + // supplies the received asset(newCollateralAsset) from swap to Aave Pool + _conditionalRenewAllowance(liquiditySwapParams.newCollateralAsset, amountReceived); + _supply( + liquiditySwapParams.newCollateralAsset, + amountReceived, + liquiditySwapParams.user, + REFERRER + ); + + // pulls flashLoanAmount amount of flash-borrowed asset from the user + _pullATokenAndWithdraw( + flashLoanAsset, + liquiditySwapParams.user, + flashLoanAmount, + collateralATokenPermit + ); + + // flashloan repayment + _conditionalRenewAllowance(flashLoanAsset, flashLoanAmount + flashLoanPremium); + return true; + } + + /** + * @dev Swaps the collateral asset and supplies the received asset to the Aave Pool + * @dev Workflow: + * 1. Pull aToken collateral from user and withdraw from Pool + * 2. Sell asset for new collateral asset + * 3. Supply new collateral asset + * @param liquiditySwapParams struct describing the liquidity swap + * @param collateralATokenPermit Permit for aToken corresponding to old collateral asset from the user + * @return The amount received from the swap of new collateral asset, that is now supplied to the Aave Pool + */ + function _swapAndDeposit( + LiquiditySwapParams memory liquiditySwapParams, + PermitInput memory collateralATokenPermit + ) internal returns (uint256) { + uint256 collateralAmountReceived = _pullATokenAndWithdraw( + liquiditySwapParams.collateralAsset, + liquiditySwapParams.user, + liquiditySwapParams.collateralAmountToSwap, + collateralATokenPermit + ); + + // sell(exact in) old collateral asset to new collateral asset + uint256 amountReceived = _sellOnParaSwap( + liquiditySwapParams.offset, + liquiditySwapParams.paraswapData, + IERC20Detailed(liquiditySwapParams.collateralAsset), + IERC20Detailed(liquiditySwapParams.newCollateralAsset), + collateralAmountReceived, + liquiditySwapParams.newCollateralAmount + ); + + // supply the received asset(newCollateralAsset) from swap to the Aave Pool + _conditionalRenewAllowance(liquiditySwapParams.newCollateralAsset, amountReceived); + _supply( + liquiditySwapParams.newCollateralAsset, + amountReceived, + liquiditySwapParams.user, + REFERRER + ); + + return amountReceived; + } + + /** + * @dev Renews the asset allowance in case the current allowance is below a given threshold + * @param asset The address of the asset + * @param minAmount The minimum required allowance to the Aave Pool + */ + function _conditionalRenewAllowance(address asset, uint256 minAmount) internal { + uint256 allowance = IERC20(asset).allowance(address(this), address(POOL)); + if (allowance < minAmount) { + renewAllowance(asset); + } + } + + /** + * @dev Triggers the flashloan passing encoded params for the collateral swap + * @param liquiditySwapParams struct describing the liquidity swap + * @param collateralATokenPermit optional permit for old collateral's aToken + */ + function _flash( + LiquiditySwapParams memory liquiditySwapParams, + PermitInput memory collateralATokenPermit + ) internal virtual { + bytes memory params = abi.encode(liquiditySwapParams, collateralATokenPermit); + address[] memory assets = new address[](1); + assets[0] = liquiditySwapParams.collateralAsset; + uint256[] memory amounts = new uint256[](1); + amounts[0] = liquiditySwapParams.collateralAmountToSwap; + uint256[] memory interestRateModes = new uint256[](1); + interestRateModes[0] = 0; + + POOL.flashLoan( + address(this), + assets, + amounts, + interestRateModes, + address(this), + params, + REFERRER + ); + } +} diff --git a/src/contracts/base/ParaSwapRepayAdapter.sol b/src/contracts/base/ParaSwapRepayAdapter.sol new file mode 100644 index 0000000..da4b414 --- /dev/null +++ b/src/contracts/base/ParaSwapRepayAdapter.sol @@ -0,0 +1,291 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.10; + +import {DataTypes} from '@aave/core-v3/contracts/protocol/libraries/types/DataTypes.sol'; +import {IERC20Detailed} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/IERC20Detailed.sol'; +import {IPoolAddressesProvider} from '@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol'; +import {IPool} from '@aave/core-v3/contracts/interfaces/IPool.sol'; +import {ReentrancyGuard} from 'aave-v3-periphery/contracts/dependencies/openzeppelin/ReentrancyGuard.sol'; +import {IERC20} from 'solidity-utils/contracts/oz-common/interfaces/IERC20.sol'; +import {SafeERC20} from 'solidity-utils/contracts/oz-common/SafeERC20.sol'; +import {IParaSwapAugustusRegistry} from '../dependencies/paraswap/IParaSwapAugustusRegistry.sol'; +import {IParaSwapAugustus} from '../dependencies/paraswap/IParaSwapAugustus.sol'; +import {IAaveFlashLoanReceiver} from '../interfaces/IAaveFlashLoanReceiver.sol'; +import {IParaSwapRepayAdapter} from '../interfaces/IParaSwapRepayAdapter.sol'; +import {BaseParaSwapBuyAdapter} from './BaseParaSwapBuyAdapter.sol'; + +/** + * @title ParaSwapRepayAdapter + * @notice ParaSwap Adapter to repay debt with collateral. + * @dev Swaps the existing collateral asset to debt asset in order to repay the debt. It flash-borrows assets from the Aave Pool in case the + * user position does not remain collateralized during the operation. + * @author Aave Labs + **/ +abstract contract ParaSwapRepayAdapter is + BaseParaSwapBuyAdapter, + ReentrancyGuard, + IAaveFlashLoanReceiver, + IParaSwapRepayAdapter +{ + using SafeERC20 for IERC20; + + // unique identifier to track usage via flashloan events + uint16 public constant REFERRER = 13410; // uint16(uint256(keccak256(abi.encode('repay-swap-adapter'))) / type(uint16).max) + + /** + * @dev Constructor + * @param addressesProvider The address of the Aave PoolAddressesProvider contract + * @param pool The address of the Aave Pool contract + * @param augustusRegistry The address of the Paraswap AugustusRegistry contract + * @param owner The address of the owner + */ + constructor( + IPoolAddressesProvider addressesProvider, + address pool, + IParaSwapAugustusRegistry augustusRegistry, + address owner + ) BaseParaSwapBuyAdapter(addressesProvider, pool, augustusRegistry) { + transferOwnership(owner); + // set initial approval for all reserves + address[] memory reserves = POOL.getReservesList(); + for (uint256 i = 0; i < reserves.length; i++) { + IERC20(reserves[i]).safeApprove(address(POOL), type(uint256).max); + } + } + + /** + * @notice Renews the asset allowance to the Aave Pool + * @param reserve The address of the asset + */ + function renewAllowance(address reserve) public { + IERC20(reserve).safeApprove(address(POOL), 0); + IERC20(reserve).safeApprove(address(POOL), type(uint256).max); + } + + /// @inheritdoc IParaSwapRepayAdapter + function repayWithCollateral( + RepayParams memory repayParams, + PermitInput memory collateralATokenPermit + ) external nonReentrant { + // Refresh the debt amount to repay + repayParams.debtRepayAmount = _getDebtRepayAmount( + IERC20(repayParams.debtRepayAsset), + repayParams.debtRepayMode, + repayParams.offset, + repayParams.debtRepayAmount, + repayParams.user + ); + + // true if flashloan is needed to repay the debt + if (!repayParams.withFlashLoan) { + uint256 collateralBalanceBefore = IERC20(repayParams.collateralAsset).balanceOf( + address(this) + ); + _swapAndRepay(repayParams, collateralATokenPermit); + + // Supply on behalf of the user in case of excess of collateral asset after the swap + uint256 collateralBalanceAfter = IERC20(repayParams.collateralAsset).balanceOf(address(this)); + uint256 collateralExcess = collateralBalanceAfter > collateralBalanceBefore + ? collateralBalanceAfter - collateralBalanceBefore + : 0; + if (collateralExcess > 0) { + _conditionalRenewAllowance(repayParams.collateralAsset, collateralExcess); + _supply(repayParams.collateralAsset, collateralExcess, repayParams.user, REFERRER); + } + } else { + // flashloan of the current collateral asset to use for repayment + _flash(repayParams, collateralATokenPermit); + } + } + + /** + * @dev Executes the repay with collateral after receiving the flash-borrowed assets + * @dev Workflow: + * 1. Buy debt asset by providing the flash-borrowed assets in exchange + * 2. Repay debt + * 3. Pull aToken collateral from user and withdraw from Pool + * 4. Repay flashloan + * @param assets The addresses of the flash-borrowed assets + * @param amounts The amounts of the flash-borrowed assets + * @param premiums The premiums of the flash-borrowed assets + * @param initiator The address of the flashloan initiator + * @param params The byte-encoded params passed when initiating the flashloan + * @return True if the execution of the operation succeeds, false otherwise + */ + function executeOperation( + address[] calldata assets, + uint256[] calldata amounts, + uint256[] calldata premiums, + address initiator, + bytes calldata params + ) external returns (bool) { + require(msg.sender == address(POOL), 'CALLER_MUST_BE_POOL'); + require(initiator == address(this), 'INITIATOR_MUST_BE_THIS'); + + (RepayParams memory repayParams, PermitInput memory collateralATokenPermit) = abi.decode( + params, + (RepayParams, PermitInput) + ); + + address flashLoanAsset = assets[0]; + uint256 flashLoanAmount = amounts[0]; + uint256 flashLoanPremium = premiums[0]; + + // buys the debt asset by providing the flashloanAsset + uint256 amountSold = _buyOnParaSwap( + repayParams.offset, + repayParams.paraswapData, + IERC20Detailed(flashLoanAsset), + IERC20Detailed(repayParams.debtRepayAsset), + flashLoanAmount, + repayParams.debtRepayAmount + ); + + // repays debt + _conditionalRenewAllowance(repayParams.debtRepayAsset, repayParams.debtRepayAmount); + POOL.repay( + repayParams.debtRepayAsset, + repayParams.debtRepayAmount, + repayParams.debtRepayMode, + repayParams.user + ); + + // pulls only the amount needed from the user for the flashloan repayment + // flashLoanAmount - amountSold = excess in the contract from swap + // flashLoanAmount + flashLoanPremium = flashloan repayment + // the amount needed is: + // flashLoanAmount + flashLoanPremium - (flashLoanAmount - amountSold) + // equivalent to + // flashLoanPremium + amountSold + _pullATokenAndWithdraw( + flashLoanAsset, + repayParams.user, + flashLoanPremium + amountSold, + collateralATokenPermit + ); + + // flashloan repayment + _conditionalRenewAllowance(flashLoanAsset, flashLoanAmount + flashLoanPremium); + return true; + } + + /** + * @dev Swaps the collateral asset and repays the debt of received asset from swap + * @dev Workflow: + * 1. Pull aToken collateral from user and withdraw from Pool + * 2. Buy debt asset by providing the withdrawn collateral in exchange + * 3. Repay debt + * @param repayParams struct describing the debt swap + * @param collateralATokenPermit Permit for withdrawing collateral token from the pool + * @return The amount of withdrawn collateral sold in the swap + */ + function _swapAndRepay( + RepayParams memory repayParams, + PermitInput memory collateralATokenPermit + ) internal returns (uint256) { + uint256 collateralAmountReceived = _pullATokenAndWithdraw( + repayParams.collateralAsset, + repayParams.user, + repayParams.maxCollateralAmountToSwap, + collateralATokenPermit + ); + + // buy(exact out) of debt asset by providing the withdrawn collateral in exchange + uint256 amountSold = _buyOnParaSwap( + repayParams.offset, + repayParams.paraswapData, + IERC20Detailed(repayParams.collateralAsset), + IERC20Detailed(repayParams.debtRepayAsset), + collateralAmountReceived, + repayParams.debtRepayAmount + ); + + // repay the debt with the bought asset (debtRepayAsset) from the swap + _conditionalRenewAllowance(repayParams.debtRepayAsset, repayParams.debtRepayAmount); + POOL.repay( + repayParams.debtRepayAsset, + repayParams.debtRepayAmount, + repayParams.debtRepayMode, + repayParams.user + ); + + return amountSold; + } + + /** + * @dev Triggers the flashloan passing encoded params for the repay with collateral + * @param repayParams struct describing the repay swap + * @param collateralATokenPermit optional permit for old collateral's aToken + */ + function _flash( + RepayParams memory repayParams, + PermitInput memory collateralATokenPermit + ) internal virtual { + bytes memory params = abi.encode(repayParams, collateralATokenPermit); + address[] memory assets = new address[](1); + assets[0] = repayParams.collateralAsset; + uint256[] memory amounts = new uint256[](1); + amounts[0] = repayParams.maxCollateralAmountToSwap; + uint256[] memory interestRateModes = new uint256[](1); + interestRateModes[0] = 0; + + POOL.flashLoan( + address(this), + assets, + amounts, + interestRateModes, + address(this), + params, + REFERRER + ); + } + + /** + * @dev Renews the asset allowance in case the current allowance is below a given threshold + * @param asset The address of the asset + * @param minAmount The minimum required allowance to the Aave Pool + */ + function _conditionalRenewAllowance(address asset, uint256 minAmount) internal { + uint256 allowance = IERC20(asset).allowance(address(this), address(POOL)); + if (allowance < minAmount) { + renewAllowance(asset); + } + } + + /** + * @dev Returns the amount of debt to repay for the user + * @param debtAsset The address of the asset to repay the debt + * @param rateMode The interest rate mode of the debt (e.g. STABLE or VARIABLE) + * @param buyAllBalanceOffset offset in calldata in case all debt is repaid, otherwise 0 + * @param debtRepayAmount The amount of debt to repay + * @param user The address user for whom the debt is repaid + * @return The amount of debt to be repaid + */ + function _getDebtRepayAmount( + IERC20 debtAsset, + uint256 rateMode, + uint256 buyAllBalanceOffset, + uint256 debtRepayAmount, + address user + ) internal view returns (uint256) { + (address vDebtToken, address sDebtToken, ) = _getReserveData(address(debtAsset)); + + address debtToken = DataTypes.InterestRateMode(rateMode) == DataTypes.InterestRateMode.STABLE + ? sDebtToken + : vDebtToken; + uint256 currentDebt = IERC20(debtToken).balanceOf(user); + + if (buyAllBalanceOffset != 0) { + // Sanity check to ensure the passed value `debtRepayAmount` is higher than the current debt + // when repaying all debt. + require(currentDebt <= debtRepayAmount, 'INSUFFICIENT_AMOUNT_TO_REPAY'); + debtRepayAmount = currentDebt; + } else { + // Sanity check to ensure the passed value `debtRepayAmount` is less than the current debt + // when repaying the exact amount + require(debtRepayAmount <= currentDebt, 'INVALID_DEBT_REPAY_AMOUNT'); + } + + return debtRepayAmount; + } +} diff --git a/src/contracts/base/ParaSwapWithdrawSwapAdapter.sol b/src/contracts/base/ParaSwapWithdrawSwapAdapter.sol new file mode 100644 index 0000000..7dfc174 --- /dev/null +++ b/src/contracts/base/ParaSwapWithdrawSwapAdapter.sol @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.10; + +import {IERC20Detailed} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/IERC20Detailed.sol'; +import {IPoolAddressesProvider} from '@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol'; +import {ReentrancyGuard} from 'aave-v3-periphery/contracts/dependencies/openzeppelin/ReentrancyGuard.sol'; +import {IERC20} from 'solidity-utils/contracts/oz-common/interfaces/IERC20.sol'; +import {SafeERC20} from 'solidity-utils/contracts/oz-common/SafeERC20.sol'; +import {IParaSwapAugustusRegistry} from '../dependencies/paraswap/IParaSwapAugustusRegistry.sol'; +import {IParaSwapWithdrawSwapAdapter} from '../interfaces/IParaSwapWithdrawSwapAdapter.sol'; +import {BaseParaSwapSellAdapter} from './BaseParaSwapSellAdapter.sol'; + +/** + * @title ParaSwapWithdrawSwapAdapter + * @notice ParaSwap Adapter to withdraw and swap. + * @dev Withdraws the asset from the Aave Pool and swaps(exact in) it to another asset + * @author Aave Labs + **/ +abstract contract ParaSwapWithdrawSwapAdapter is + BaseParaSwapSellAdapter, + ReentrancyGuard, + IParaSwapWithdrawSwapAdapter +{ + using SafeERC20 for IERC20; + + /** + * @dev Constructor + * @param addressesProvider The address of the Aave PoolAddressesProvider contract + * @param pool The address of the Aave Pool contract + * @param augustusRegistry The address of the Paraswap AugustusRegistry contract + * @param owner The address of the owner + */ + constructor( + IPoolAddressesProvider addressesProvider, + address pool, + IParaSwapAugustusRegistry augustusRegistry, + address owner + ) BaseParaSwapSellAdapter(addressesProvider, pool, augustusRegistry) { + transferOwnership(owner); + } + + /// @inheritdoc IParaSwapWithdrawSwapAdapter + function withdrawAndSwap( + WithdrawSwapParams memory withdrawSwapParams, + PermitInput memory permitInput + ) external nonReentrant { + (, , address aToken) = _getReserveData(withdrawSwapParams.oldAsset); + + // Offset in August calldata if wanting to swap all balance, otherwise 0 + if (withdrawSwapParams.allBalanceOffset != 0) { + uint256 balance = IERC20(aToken).balanceOf(withdrawSwapParams.user); + require(balance <= withdrawSwapParams.oldAssetAmount, 'INSUFFICIENT_AMOUNT_TO_SWAP'); + withdrawSwapParams.oldAssetAmount = balance; + } + + // pulls liquidity asset from the user and withdraw + _pullATokenAndWithdraw( + withdrawSwapParams.oldAsset, + withdrawSwapParams.user, + withdrawSwapParams.oldAssetAmount, + permitInput + ); + + // sell(exact in) withdrawn asset from Aave Pool to new asset + uint256 amountReceived = _sellOnParaSwap( + withdrawSwapParams.allBalanceOffset, + withdrawSwapParams.paraswapData, + IERC20Detailed(withdrawSwapParams.oldAsset), + IERC20Detailed(withdrawSwapParams.newAsset), + withdrawSwapParams.oldAssetAmount, + withdrawSwapParams.minAmountToReceive + ); + + // transfer new asset to the user + IERC20(withdrawSwapParams.newAsset).safeTransfer(withdrawSwapParams.user, amountReceived); + } +} diff --git a/src/contracts/dependencies/paraswap/AugustusRegistry.sol b/src/contracts/dependencies/paraswap/AugustusRegistry.sol new file mode 100644 index 0000000..10b1136 --- /dev/null +++ b/src/contracts/dependencies/paraswap/AugustusRegistry.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +library AugustusRegistry { + address public constant ETHEREUM = 0xa68bEA62Dc4034A689AA0F58A76681433caCa663; + + address public constant POLYGON = 0xca35a4866747Ff7A604EF7a2A7F246bb870f3ca1; + + address public constant AVALANCHE = 0xfD1E5821F07F1aF812bB7F3102Bfd9fFb279513a; + + address public constant ARBITRUM = 0xdC6E2b14260F972ad4e5a31c68294Fba7E720701; + + address public constant OPTIMISM = 0x6e7bE86000dF697facF4396efD2aE2C322165dC3; + + address public constant BSC = 0x05b4486f643914a818eD93Afc07457e9074be211; + + address public constant BASE = 0x7E31B336F9E8bA52ba3c4ac861b033Ba90900bb3; +} diff --git a/src/interfaces/IParaSwapAugustus.sol b/src/contracts/dependencies/paraswap/IParaSwapAugustus.sol similarity index 100% rename from src/interfaces/IParaSwapAugustus.sol rename to src/contracts/dependencies/paraswap/IParaSwapAugustus.sol diff --git a/src/interfaces/IParaSwapAugustusRegistry.sol b/src/contracts/dependencies/paraswap/IParaSwapAugustusRegistry.sol similarity index 100% rename from src/interfaces/IParaSwapAugustusRegistry.sol rename to src/contracts/dependencies/paraswap/IParaSwapAugustusRegistry.sol diff --git a/src/interfaces/IFlashLoanReceiver.sol b/src/contracts/interfaces/IAaveFlashLoanReceiver.sol similarity index 74% rename from src/interfaces/IFlashLoanReceiver.sol rename to src/contracts/interfaces/IAaveFlashLoanReceiver.sol index f624646..7fb17d2 100644 --- a/src/interfaces/IFlashLoanReceiver.sol +++ b/src/contracts/interfaces/IAaveFlashLoanReceiver.sol @@ -2,13 +2,12 @@ pragma solidity ^0.8.0; /** - * @dev altered version removing immutables, for easier inheritance - * @title IFlashLoanReceiver - * @author Aave - * @notice Defines the basic interface of a flashloan-receiver contract. - * @dev Implement this interface to develop a flashloan-compatible flashLoanReceiver contract + * @title IAaveFlashLoanReceiver + * @author Aave Labs + * @notice Defines the basic interface of an Aave flashloan-receiver contract. + * @dev Altered version of the official Aave Interface IFlashLoanReceiver, keeping the minimal functionality to receive the flashloan execution **/ -interface IFlashLoanReceiver { +interface IAaveFlashLoanReceiver { /** * @notice Executes an operation after receiving the flash-borrowed assets * @dev Ensure that the contract can return the debt + premium, e.g., has diff --git a/src/contracts/interfaces/IBaseParaSwapAdapter.sol b/src/contracts/interfaces/IBaseParaSwapAdapter.sol new file mode 100644 index 0000000..c115695 --- /dev/null +++ b/src/contracts/interfaces/IBaseParaSwapAdapter.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.10; + +import {IERC20} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/IERC20.sol'; +import {IERC20WithPermit} from 'solidity-utils/contracts/oz-common/interfaces/IERC20WithPermit.sol'; +import {IPriceOracleGetter} from '@aave/core-v3/contracts/interfaces/IPriceOracleGetter.sol'; + +/** + * @title IBaseParaSwapAdapter + * @notice Defines the basic interface of ParaSwap adapter + * @dev Implement this interface to provide functionality of swapping one asset to another asset + **/ +interface IBaseParaSwapAdapter { + struct PermitInput { + IERC20WithPermit aToken; // the asset to give allowance for + uint256 value; // the amount of asset for the allowance + uint256 deadline; // expiration unix timestamp + uint8 v; // sig v + bytes32 r; // sig r + bytes32 s; // sig s + } + + /** + * @dev Emitted after a sell of an asset is made + * @param fromAsset The address of the asset sold + * @param toAsset The address of the asset received in exchange + * @param fromAmount The amount of asset sold + * @param receivedAmount The amount received from the sell + */ + event Swapped( + address indexed fromAsset, + address indexed toAsset, + uint256 fromAmount, + uint256 receivedAmount + ); + + /** + * @dev Emitted after a buy of an asset is made + * @param fromAsset The address of the asset provided in exchange + * @param toAsset The address of the asset bought + * @param amountSold The amount of asset provided for the buy + * @param receivedAmount The amount of asset bought + */ + event Bought( + address indexed fromAsset, + address indexed toAsset, + uint256 amountSold, + uint256 receivedAmount + ); + + /** + * @notice Emergency rescue for token stucked on this contract, as failsafe mechanism + * @dev Funds should never remain in this contract more time than during transactions + * @dev Only callable by the owner + * @param token The address of the stucked token to rescue + */ + function rescueTokens(IERC20 token) external; + + /** + * @notice Returns the maximum slippage percent allowed for swapping one asset to another + * @return The maximum allowed slippage percent, in bps + */ + function MAX_SLIPPAGE_PERCENT() external view returns (uint256); + + /** + * @notice Returns the Aave Price Oracle contract + * @return The address of the AaveOracle + */ + function ORACLE() external view returns (IPriceOracleGetter); +} diff --git a/src/interfaces/IERC3156FlashBorrower.sol b/src/contracts/interfaces/IERC3156FlashBorrower.sol similarity index 100% rename from src/interfaces/IERC3156FlashBorrower.sol rename to src/contracts/interfaces/IERC3156FlashBorrower.sol diff --git a/src/interfaces/IERC3156FlashLender.sol b/src/contracts/interfaces/IERC3156FlashLender.sol similarity index 100% rename from src/interfaces/IERC3156FlashLender.sol rename to src/contracts/interfaces/IERC3156FlashLender.sol diff --git a/src/contracts/interfaces/IParaSwapDebtSwapAdapter.sol b/src/contracts/interfaces/IParaSwapDebtSwapAdapter.sol new file mode 100644 index 0000000..b23cab6 --- /dev/null +++ b/src/contracts/interfaces/IParaSwapDebtSwapAdapter.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.10; + +import {ICreditDelegationToken} from '@aave/core-v3/contracts/interfaces/ICreditDelegationToken.sol'; +import {IBaseParaSwapAdapter} from './IBaseParaSwapAdapter.sol'; + +/** + * @title IParaSwapDebtSwapAdapter + * @notice Defines the basic interface for ParaSwapDebtSwapAdapter + * @dev Implement this interface to provide functionality of swapping one debt asset to another debt asset + * @author BGD labs + **/ +interface IParaSwapDebtSwapAdapter is IBaseParaSwapAdapter { + struct FlashParams { + address debtAsset; // the asset to swap debt from + uint256 debtRepayAmount; // the amount of asset to swap from + uint256 debtRateMode; // debt interest rate mode (1 for stable, 2 for variable) + address nestedFlashloanDebtAsset; // 0 if no need of extra collateral. Otherwise internally used for new debt asset + uint256 nestedFlashloanDebtAmount; // internally used for the amount of new debt asset in case extra collateral + bytes paraswapData; // encoded paraswap data + uint256 offset; // offset in buy calldata in case of swapping all debt, otherwise 0 + address user; // the address of user + } + + struct DebtSwapParams { + address debtAsset; // the asset to repay the debt + uint256 debtRepayAmount; // the amount of debt to repay + uint256 debtRateMode; // debt interest rate mode (1 for stable, 2 for variable) + address newDebtAsset; // the asset of the new debt + uint256 maxNewDebtAmount; // the maximum amount of asset to swap from + address extraCollateralAsset; // the asset of extra collateral to use (if needed) + uint256 extraCollateralAmount; // the amount of extra collateral to use (if needed) + uint256 offset; // offset in buy calldata in case of swapping all debt, otherwise 0 + bytes paraswapData; // encoded paraswap data + } + + struct CreditDelegationInput { + ICreditDelegationToken debtToken; // the debt asset to delegate credit for + uint256 value; // the amount of credit to delegate + uint256 deadline; // expiration unix timestamp + uint8 v; // sig v + bytes32 r; // sig r + bytes32 s; // sig s + } + + /** + * @notice Swaps debt from one asset to another + * @param debtSwapParams struct describing the debt swap + * @param creditDelegationPermit optional permit for credit delegation + * @param collateralATokenPermit optional permit for collateral aToken + */ + function swapDebt( + DebtSwapParams memory debtSwapParams, + CreditDelegationInput memory creditDelegationPermit, + PermitInput memory collateralATokenPermit + ) external; +} diff --git a/src/contracts/interfaces/IParaSwapLiquiditySwapAdapter.sol b/src/contracts/interfaces/IParaSwapLiquiditySwapAdapter.sol new file mode 100644 index 0000000..061b4c1 --- /dev/null +++ b/src/contracts/interfaces/IParaSwapLiquiditySwapAdapter.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.10; + +import {IBaseParaSwapAdapter} from './IBaseParaSwapAdapter.sol'; + +/** + * @title IParaSwapLiquiditySwapAdapter + * @notice Defines the basic interface for ParaSwapLiquiditySwapAdapter + * @dev Implement this interface to provide functionality of swapping one collateral asset to another collateral asset + * @author Aave Labs + **/ +interface IParaSwapLiquiditySwapAdapter is IBaseParaSwapAdapter { + struct LiquiditySwapParams { + address collateralAsset; // the asset to swap collateral from + uint256 collateralAmountToSwap; // the amount of asset to swap from + address newCollateralAsset; // the asset to swap collateral to + uint256 newCollateralAmount; // the minimum amount of new collateral asset to receive + uint256 offset; // offset in sell calldata in case of swapping all collateral, otherwise 0 + address user; // the address of user + bool withFlashLoan; // true if flashloan is needed to swap collateral, otherwise false + bytes paraswapData; // encoded paraswap data + } + + /** + * @notice Swaps liquidity(collateral) from one asset to another + * @param liquiditySwapParams struct describing the liquidity swap + * @param collateralATokenPermit optional permit for collateral aToken + */ + function swapLiquidity( + LiquiditySwapParams memory liquiditySwapParams, + PermitInput memory collateralATokenPermit + ) external; +} diff --git a/src/contracts/interfaces/IParaSwapRepayAdapter.sol b/src/contracts/interfaces/IParaSwapRepayAdapter.sol new file mode 100644 index 0000000..6887951 --- /dev/null +++ b/src/contracts/interfaces/IParaSwapRepayAdapter.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.10; + +import {IBaseParaSwapAdapter} from './IBaseParaSwapAdapter.sol'; + +/** + * @title IParaSwapRepayAdapter + * @notice Defines the basic interface for ParaSwapRepayAdapter + * @dev Implement this interface to provide functionality of repaying debt with collateral + * @author Aave Labs + **/ +interface IParaSwapRepayAdapter is IBaseParaSwapAdapter { + struct RepayParams { + address collateralAsset; // the asset you want to swap collateral from + uint256 maxCollateralAmountToSwap; // the max amount you want to swap from + address debtRepayAsset; // the asset you want to repay the debt + uint256 debtRepayAmount; // the amount of debt to repay + uint256 debtRepayMode; // debt interest rate mode (1 for stable, 2 for variable) + uint256 offset; // offset in buy calldata in case of swapping all collateral, otherwise 0 + bool withFlashLoan; // true if flashloan is needed to repay the debt, otherwise false + address user; // the address of user + bytes paraswapData; // encoded paraswap data + } + + /** + * @notice Repays with collateral by swapping the collateral asset to debt asset + * @param repayParams struct describing the repay with collateral swap + * @param collateralATokenPermit optional permit for collateral aToken + */ + function repayWithCollateral( + RepayParams memory repayParams, + PermitInput memory collateralATokenPermit + ) external; +} diff --git a/src/contracts/interfaces/IParaSwapWithdrawSwapAdapter.sol b/src/contracts/interfaces/IParaSwapWithdrawSwapAdapter.sol new file mode 100644 index 0000000..b5e4197 --- /dev/null +++ b/src/contracts/interfaces/IParaSwapWithdrawSwapAdapter.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.10; + +import {IBaseParaSwapAdapter} from './IBaseParaSwapAdapter.sol'; + +/** + * @title IParaSwapWithdrawSwapAdapter + * @notice Defines the basic interface for ParaSwapWithdrawSwapAdapter + * @dev Implement this interface to provide functionality of withdrawing from the Aave Pool and swapping to another asset + * @author Aave Labs + **/ +interface IParaSwapWithdrawSwapAdapter is IBaseParaSwapAdapter { + struct WithdrawSwapParams { + address oldAsset; // the asset to withdraw and swap from + uint256 oldAssetAmount; // the amount to withdraw + address newAsset; // the asset to swap to + uint256 minAmountToReceive; // the minimum amount of new asset to receive + uint256 allBalanceOffset; // offset in sell calldata in case of swapping all collateral, otherwise 0 + address user; // the address of user + bytes paraswapData; // encoded paraswap data + } + + /** + * @notice Withdraws and swaps an asset that is supplied to the Aave Pool + * @param withdrawSwapParams struct describing the withdraw swap + * @param permitInput optional permit for collateral aToken + */ + function withdrawAndSwap( + WithdrawSwapParams memory withdrawSwapParams, + PermitInput memory permitInput + ) external; +} diff --git a/src/interfaces/ICreditDelegationToken.sol b/src/interfaces/ICreditDelegationToken.sol deleted file mode 100644 index 1de14cf..0000000 --- a/src/interfaces/ICreditDelegationToken.sol +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -/** - * @title ICreditDelegationToken - * @author Aave - * @notice Defines the basic interface for a token supporting credit delegation. - **/ -interface ICreditDelegationToken { - /** - * @notice Delegates borrowing power to a user on the specific debt token. - * Delegation will still respect the liquidation constraints (even if delegated, a - * delegatee cannot force a delegator HF to go below 1) - * @param delegatee The address receiving the delegated borrowing power - * @param amount The maximum amount being delegated. - **/ - function approveDelegation(address delegatee, uint256 amount) external; - - /** - * @notice Returns the borrow allowance of the user - * @param fromUser The user to giving allowance - * @param toUser The user to give allowance to - * @return The current allowance of `toUser` - **/ - function borrowAllowance(address fromUser, address toUser) external view returns (uint256); - - /** - * @notice Delegates borrowing power to a user on the specific debt token via ERC712 signature - * @param delegator The delegator of the credit - * @param delegatee The delegatee that can use the credit - * @param value The amount to be delegated - * @param deadline The deadline timestamp, type(uint256).max for max deadline - * @param v The V signature param - * @param s The S signature param - * @param r The R signature param - */ - function delegationWithSig( - address delegator, - address delegatee, - uint256 value, - uint256 deadline, - uint8 v, - bytes32 r, - bytes32 s - ) external; -} diff --git a/src/interfaces/IFlashLoanReceiverBase.sol b/src/interfaces/IFlashLoanReceiverBase.sol deleted file mode 100644 index 1fb833a..0000000 --- a/src/interfaces/IFlashLoanReceiverBase.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import {IPoolAddressesProvider} from '@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol'; -import {IPool} from '@aave/core-v3/contracts/interfaces/IPool.sol'; - -/** - * @dev altered version removing immutables, for easier inheritance - * @title IFlashLoanReceiver - * @author Aave - * @notice Defines the basic interface of a flashloan-receiver contract. - * @dev Implement this interface to develop a flashloan-compatible flashLoanReceiver contract - **/ -interface IFlashLoanReceiverBase { - function ADDRESSES_PROVIDER() external view returns (IPoolAddressesProvider); - - function POOL() external view returns (IPool); -} diff --git a/src/interfaces/IFlashLoanSimpleReceiver.sol b/src/interfaces/IFlashLoanSimpleReceiver.sol deleted file mode 100644 index f1ec74f..0000000 --- a/src/interfaces/IFlashLoanSimpleReceiver.sol +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -/** - * @dev altered version removing immutables, for easier inheritance - * @title IFlashLoanSimpleReceiver - * @author Aave - * @notice Defines the basic interface of a flashloan-receiver contract. - * @dev Implement this interface to develop a flashloan-compatible flashLoanReceiver contract - **/ -interface IFlashLoanSimpleReceiver { - /** - * @notice Executes an operation after receiving the flash-borrowed asset - * @dev 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 - * @param asset The address of the flash-borrowed asset - * @param amount The amount of the flash-borrowed asset - * @param premium The fee of the flash-borrowed asset - * @param initiator The address of the flashloan initiator - * @param params The byte-encoded params passed when initiating the flashloan - * @return True if the execution of the operation succeeds, false otherwise - */ - function executeOperation( - address asset, - uint256 amount, - uint256 premium, - address initiator, - bytes calldata params - ) external returns (bool); -} diff --git a/src/interfaces/IParaswapDebtSwapAdapter.sol b/src/interfaces/IParaswapDebtSwapAdapter.sol deleted file mode 100644 index c187636..0000000 --- a/src/interfaces/IParaswapDebtSwapAdapter.sol +++ /dev/null @@ -1,59 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.10; -import {ICreditDelegationToken} from './ICreditDelegationToken.sol'; -import {IERC20WithPermit} from 'solidity-utils/contracts/oz-common/interfaces/IERC20WithPermit.sol'; - -interface IParaswapDebtSwapAdapter { - struct FlashParams { - address debtAsset; - uint256 debtRepayAmount; - uint256 debtRateMode; - address nestedFlashloanDebtAsset; - uint256 nestedFlashloanDebtAmount; - bytes paraswapData; - uint256 offset; - address user; - } - - struct DebtSwapParams { - address debtAsset; - uint256 debtRepayAmount; - uint256 debtRateMode; - address newDebtAsset; - uint256 maxNewDebtAmount; - address extraCollateralAsset; - uint256 extraCollateralAmount; - uint256 offset; - bytes paraswapData; - } - - struct CreditDelegationInput { - ICreditDelegationToken debtToken; - uint256 value; - uint256 deadline; - uint8 v; - bytes32 r; - bytes32 s; - } - - struct PermitInput { - IERC20WithPermit aToken; - uint256 value; - uint256 deadline; - uint8 v; - bytes32 r; - bytes32 s; - } - - /** - * @dev swaps debt from one asset to another - * @param debtSwapParams struct describing the debt swap - * @param creditDelegationPermit optional permit for credit delegation - * @param collateralATokenPermit optional permit for collateral aToken - */ - function swapDebt( - DebtSwapParams memory debtSwapParams, - CreditDelegationInput memory creditDelegationPermit, - PermitInput memory collateralATokenPermit - ) external; -} diff --git a/src/lib/AugustusRegistry.sol b/src/lib/AugustusRegistry.sol deleted file mode 100644 index 226f568..0000000 --- a/src/lib/AugustusRegistry.sol +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; -import {IParaSwapAugustusRegistry} from '../interfaces/IParaSwapAugustusRegistry.sol'; - -library AugustusRegistry { - IParaSwapAugustusRegistry public constant ETHEREUM = - IParaSwapAugustusRegistry(0xa68bEA62Dc4034A689AA0F58A76681433caCa663); - - IParaSwapAugustusRegistry public constant POLYGON = - IParaSwapAugustusRegistry(0xca35a4866747Ff7A604EF7a2A7F246bb870f3ca1); - - IParaSwapAugustusRegistry public constant AVALANCHE = - IParaSwapAugustusRegistry(0xfD1E5821F07F1aF812bB7F3102Bfd9fFb279513a); - - IParaSwapAugustusRegistry public constant ARBITRUM = - IParaSwapAugustusRegistry(0xdC6E2b14260F972ad4e5a31c68294Fba7E720701); - - IParaSwapAugustusRegistry public constant OPTIMISM = - IParaSwapAugustusRegistry(0x6e7bE86000dF697facF4396efD2aE2C322165dC3); - - IParaSwapAugustusRegistry public constant BSC = - IParaSwapAugustusRegistry(0x05b4486f643914a818eD93Afc07457e9074be211); - - IParaSwapAugustusRegistry public constant BASE = - IParaSwapAugustusRegistry(0x7E31B336F9E8bA52ba3c4ac861b033Ba90900bb3); -} diff --git a/scripts/Deploy.s.sol b/src/script/Deploy_ParaSwapDebtSwapAdapter.s.sol similarity index 62% rename from scripts/Deploy.s.sol rename to src/script/Deploy_ParaSwapDebtSwapAdapter.s.sol index e619a31..f0b63db 100644 --- a/scripts/Deploy.s.sol +++ b/src/script/Deploy_ParaSwapDebtSwapAdapter.s.sol @@ -3,7 +3,12 @@ pragma solidity ^0.8.0; import {IPoolAddressesProvider} from '@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol'; import {ArbitrumScript, EthereumScript, PolygonScript, AvalancheScript, OptimismScript, BaseScript} from 'aave-helpers/ScriptUtils.sol'; -import {AaveGovernanceV2} from 'aave-address-book/AaveGovernanceV2.sol'; +import {GovernanceV3Ethereum} from 'aave-address-book/GovernanceV3Ethereum.sol'; +import {GovernanceV3Polygon} from 'aave-address-book/GovernanceV3Polygon.sol'; +import {GovernanceV3Avalanche} from 'aave-address-book/GovernanceV3Avalanche.sol'; +import {GovernanceV3Arbitrum} from 'aave-address-book/GovernanceV3Arbitrum.sol'; +import {GovernanceV3Optimism} from 'aave-address-book/GovernanceV3Optimism.sol'; +import {GovernanceV3Base} from 'aave-address-book/GovernanceV3Base.sol'; import {AaveV2Ethereum} from 'aave-address-book/AaveV2Ethereum.sol'; import {AaveV3Ethereum} from 'aave-address-book/AaveV3Ethereum.sol'; import {AaveV2Polygon} from 'aave-address-book/AaveV2Polygon.sol'; @@ -13,18 +18,19 @@ import {AaveV3Avalanche} from 'aave-address-book/AaveV3Avalanche.sol'; import {AaveV3Optimism} from 'aave-address-book/AaveV3Optimism.sol'; import {AaveV3Arbitrum} from 'aave-address-book/AaveV3Arbitrum.sol'; import {AaveV3Base} from 'aave-address-book/AaveV3Base.sol'; -import {ParaSwapDebtSwapAdapterV3} from '../src/contracts/ParaSwapDebtSwapAdapterV3.sol'; -import {ParaSwapDebtSwapAdapterV3GHO} from '../src/contracts/ParaSwapDebtSwapAdapterV3GHO.sol'; -import {ParaSwapDebtSwapAdapterV2} from '../src/contracts/ParaSwapDebtSwapAdapterV2.sol'; -import {AugustusRegistry} from '../src/lib/AugustusRegistry.sol'; +import {ParaSwapDebtSwapAdapterV3} from 'src/contracts/ParaSwapDebtSwapAdapterV3.sol'; +import {ParaSwapDebtSwapAdapterV3GHO} from 'src/contracts/ParaSwapDebtSwapAdapterV3GHO.sol'; +import {ParaSwapDebtSwapAdapterV2} from 'src/contracts/ParaSwapDebtSwapAdapterV2.sol'; +import {IParaSwapAugustusRegistry} from 'src/contracts/dependencies/paraswap/IParaSwapAugustusRegistry.sol'; +import {AugustusRegistry} from 'src/contracts/dependencies/paraswap/AugustusRegistry.sol'; contract EthereumV2 is EthereumScript { function run() external broadcast { new ParaSwapDebtSwapAdapterV2( IPoolAddressesProvider(address(AaveV2Ethereum.POOL_ADDRESSES_PROVIDER)), address(AaveV2Ethereum.POOL), - AugustusRegistry.ETHEREUM, - AaveGovernanceV2.SHORT_EXECUTOR + IParaSwapAugustusRegistry(AugustusRegistry.ETHEREUM), + GovernanceV3Ethereum.EXECUTOR_LVL_1 ); } } @@ -34,8 +40,8 @@ contract EthereumV3 is EthereumScript { new ParaSwapDebtSwapAdapterV3GHO( IPoolAddressesProvider(address(AaveV3Ethereum.POOL_ADDRESSES_PROVIDER)), address(AaveV3Ethereum.POOL), - AugustusRegistry.ETHEREUM, - AaveGovernanceV2.SHORT_EXECUTOR + IParaSwapAugustusRegistry(AugustusRegistry.ETHEREUM), + GovernanceV3Ethereum.EXECUTOR_LVL_1 ); } } @@ -45,8 +51,8 @@ contract PolygonV2 is PolygonScript { new ParaSwapDebtSwapAdapterV2( IPoolAddressesProvider(address(AaveV2Polygon.POOL_ADDRESSES_PROVIDER)), address(AaveV2Polygon.POOL), - AugustusRegistry.POLYGON, - AaveGovernanceV2.POLYGON_BRIDGE_EXECUTOR + IParaSwapAugustusRegistry(AugustusRegistry.POLYGON), + GovernanceV3Polygon.EXECUTOR_LVL_1 ); } } @@ -56,8 +62,8 @@ contract PolygonV3 is PolygonScript { new ParaSwapDebtSwapAdapterV3( IPoolAddressesProvider(address(AaveV3Polygon.POOL_ADDRESSES_PROVIDER)), address(AaveV3Polygon.POOL), - AugustusRegistry.POLYGON, - AaveGovernanceV2.POLYGON_BRIDGE_EXECUTOR + IParaSwapAugustusRegistry(AugustusRegistry.POLYGON), + GovernanceV3Polygon.EXECUTOR_LVL_1 ); } } @@ -67,8 +73,8 @@ contract AvalancheV2 is AvalancheScript { new ParaSwapDebtSwapAdapterV2( IPoolAddressesProvider(address(AaveV2Avalanche.POOL_ADDRESSES_PROVIDER)), address(AaveV2Avalanche.POOL), - AugustusRegistry.AVALANCHE, - 0xa35b76E4935449E33C56aB24b23fcd3246f13470 // guardian + IParaSwapAugustusRegistry(AugustusRegistry.AVALANCHE), + GovernanceV3Avalanche.EXECUTOR_LVL_1 ); } } @@ -78,8 +84,8 @@ contract AvalancheV3 is AvalancheScript { new ParaSwapDebtSwapAdapterV3( IPoolAddressesProvider(address(AaveV3Avalanche.POOL_ADDRESSES_PROVIDER)), address(AaveV3Avalanche.POOL), - AugustusRegistry.AVALANCHE, - 0xa35b76E4935449E33C56aB24b23fcd3246f13470 // guardian + IParaSwapAugustusRegistry(AugustusRegistry.AVALANCHE), + GovernanceV3Avalanche.EXECUTOR_LVL_1 ); } } @@ -89,8 +95,8 @@ contract ArbitrumV3 is ArbitrumScript { new ParaSwapDebtSwapAdapterV3( IPoolAddressesProvider(address(AaveV3Arbitrum.POOL_ADDRESSES_PROVIDER)), address(AaveV3Arbitrum.POOL), - AugustusRegistry.ARBITRUM, - AaveGovernanceV2.ARBITRUM_BRIDGE_EXECUTOR + IParaSwapAugustusRegistry(AugustusRegistry.ARBITRUM), + GovernanceV3Arbitrum.EXECUTOR_LVL_1 ); } } @@ -100,8 +106,8 @@ contract OptimismV3 is OptimismScript { new ParaSwapDebtSwapAdapterV3( IPoolAddressesProvider(address(AaveV3Optimism.POOL_ADDRESSES_PROVIDER)), address(AaveV3Optimism.POOL), - AugustusRegistry.OPTIMISM, - AaveGovernanceV2.OPTIMISM_BRIDGE_EXECUTOR + IParaSwapAugustusRegistry(AugustusRegistry.OPTIMISM), + GovernanceV3Optimism.EXECUTOR_LVL_1 ); } } @@ -111,8 +117,8 @@ contract BaseV3 is BaseScript { new ParaSwapDebtSwapAdapterV3( IPoolAddressesProvider(address(AaveV3Base.POOL_ADDRESSES_PROVIDER)), address(AaveV3Base.POOL), - AugustusRegistry.BASE, - AaveGovernanceV2.BASE_BRIDGE_EXECUTOR + IParaSwapAugustusRegistry(AugustusRegistry.BASE), + GovernanceV3Base.EXECUTOR_LVL_1 ); } } diff --git a/src/script/Deploy_ParaSwapLiquiditySwapAdapter.s.sol b/src/script/Deploy_ParaSwapLiquiditySwapAdapter.s.sol new file mode 100644 index 0000000..b682e15 --- /dev/null +++ b/src/script/Deploy_ParaSwapLiquiditySwapAdapter.s.sol @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IPoolAddressesProvider} from '@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol'; +import {ArbitrumScript, EthereumScript, PolygonScript, AvalancheScript, OptimismScript, BaseScript} from 'aave-helpers/ScriptUtils.sol'; +import {GovernanceV3Ethereum} from 'aave-address-book/GovernanceV3Ethereum.sol'; +import {GovernanceV3Polygon} from 'aave-address-book/GovernanceV3Polygon.sol'; +import {GovernanceV3Avalanche} from 'aave-address-book/GovernanceV3Avalanche.sol'; +import {GovernanceV3Arbitrum} from 'aave-address-book/GovernanceV3Arbitrum.sol'; +import {GovernanceV3Optimism} from 'aave-address-book/GovernanceV3Optimism.sol'; +import {GovernanceV3Base} from 'aave-address-book/GovernanceV3Base.sol'; +import {AaveV2Ethereum} from 'aave-address-book/AaveV2Ethereum.sol'; +import {AaveV3Ethereum} from 'aave-address-book/AaveV3Ethereum.sol'; +import {AaveV2Polygon} from 'aave-address-book/AaveV2Polygon.sol'; +import {AaveV3Polygon} from 'aave-address-book/AaveV3Polygon.sol'; +import {AaveV2Avalanche} from 'aave-address-book/AaveV2Avalanche.sol'; +import {AaveV3Avalanche} from 'aave-address-book/AaveV3Avalanche.sol'; +import {AaveV3Optimism} from 'aave-address-book/AaveV3Optimism.sol'; +import {AaveV3Arbitrum} from 'aave-address-book/AaveV3Arbitrum.sol'; +import {AaveV3Base} from 'aave-address-book/AaveV3Base.sol'; +import {ParaSwapLiquiditySwapAdapterV2} from 'src/contracts/ParaSwapLiquiditySwapAdapterV2.sol'; +import {ParaSwapLiquiditySwapAdapterV3} from 'src/contracts/ParaSwapLiquiditySwapAdapterV3.sol'; +import {IParaSwapAugustusRegistry} from 'src/contracts/dependencies/paraswap/IParaSwapAugustusRegistry.sol'; +import {AugustusRegistry} from 'src/contracts/dependencies/paraswap/AugustusRegistry.sol'; + +contract EthereumV2 is EthereumScript { + function run() external broadcast { + new ParaSwapLiquiditySwapAdapterV2( + IPoolAddressesProvider(address(AaveV2Ethereum.POOL_ADDRESSES_PROVIDER)), + address(AaveV2Ethereum.POOL), + IParaSwapAugustusRegistry(AugustusRegistry.ETHEREUM), + GovernanceV3Ethereum.EXECUTOR_LVL_1 + ); + } +} + +contract EthereumV3 is EthereumScript { + function run() external broadcast { + new ParaSwapLiquiditySwapAdapterV3( + IPoolAddressesProvider(address(AaveV3Ethereum.POOL_ADDRESSES_PROVIDER)), + address(AaveV3Ethereum.POOL), + IParaSwapAugustusRegistry(AugustusRegistry.ETHEREUM), + GovernanceV3Ethereum.EXECUTOR_LVL_1 + ); + } +} + +contract PolygonV2 is PolygonScript { + function run() external broadcast { + new ParaSwapLiquiditySwapAdapterV2( + IPoolAddressesProvider(address(AaveV2Polygon.POOL_ADDRESSES_PROVIDER)), + address(AaveV2Polygon.POOL), + IParaSwapAugustusRegistry(AugustusRegistry.POLYGON), + GovernanceV3Polygon.EXECUTOR_LVL_1 + ); + } +} + +contract PolygonV3 is PolygonScript { + function run() external broadcast { + new ParaSwapLiquiditySwapAdapterV3( + IPoolAddressesProvider(address(AaveV3Polygon.POOL_ADDRESSES_PROVIDER)), + address(AaveV3Polygon.POOL), + IParaSwapAugustusRegistry(AugustusRegistry.POLYGON), + GovernanceV3Polygon.EXECUTOR_LVL_1 + ); + } +} + +contract AvalancheV2 is AvalancheScript { + function run() external broadcast { + new ParaSwapLiquiditySwapAdapterV2( + IPoolAddressesProvider(address(AaveV2Avalanche.POOL_ADDRESSES_PROVIDER)), + address(AaveV2Avalanche.POOL), + IParaSwapAugustusRegistry(AugustusRegistry.AVALANCHE), + GovernanceV3Avalanche.EXECUTOR_LVL_1 + ); + } +} + +contract AvalancheV3 is AvalancheScript { + function run() external broadcast { + new ParaSwapLiquiditySwapAdapterV3( + IPoolAddressesProvider(address(AaveV3Avalanche.POOL_ADDRESSES_PROVIDER)), + address(AaveV3Avalanche.POOL), + IParaSwapAugustusRegistry(AugustusRegistry.AVALANCHE), + GovernanceV3Avalanche.EXECUTOR_LVL_1 + ); + } +} + +contract ArbitrumV3 is ArbitrumScript { + function run() external broadcast { + new ParaSwapLiquiditySwapAdapterV3( + IPoolAddressesProvider(address(AaveV3Arbitrum.POOL_ADDRESSES_PROVIDER)), + address(AaveV3Arbitrum.POOL), + IParaSwapAugustusRegistry(AugustusRegistry.ARBITRUM), + GovernanceV3Arbitrum.EXECUTOR_LVL_1 + ); + } +} + +contract OptimismV3 is OptimismScript { + function run() external broadcast { + new ParaSwapLiquiditySwapAdapterV3( + IPoolAddressesProvider(address(AaveV3Optimism.POOL_ADDRESSES_PROVIDER)), + address(AaveV3Optimism.POOL), + IParaSwapAugustusRegistry(AugustusRegistry.OPTIMISM), + GovernanceV3Optimism.EXECUTOR_LVL_1 + ); + } +} + +contract BaseV3 is BaseScript { + function run() external broadcast { + new ParaSwapLiquiditySwapAdapterV3( + IPoolAddressesProvider(address(AaveV3Base.POOL_ADDRESSES_PROVIDER)), + address(AaveV3Base.POOL), + IParaSwapAugustusRegistry(AugustusRegistry.BASE), + GovernanceV3Base.EXECUTOR_LVL_1 + ); + } +} diff --git a/src/script/Deploy_ParaSwapRepayAdapter.s.sol b/src/script/Deploy_ParaSwapRepayAdapter.s.sol new file mode 100644 index 0000000..791b3f3 --- /dev/null +++ b/src/script/Deploy_ParaSwapRepayAdapter.s.sol @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IPoolAddressesProvider} from '@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol'; +import {ArbitrumScript, EthereumScript, PolygonScript, AvalancheScript, OptimismScript, BaseScript} from 'aave-helpers/ScriptUtils.sol'; +import {GovernanceV3Ethereum} from 'aave-address-book/GovernanceV3Ethereum.sol'; +import {GovernanceV3Polygon} from 'aave-address-book/GovernanceV3Polygon.sol'; +import {GovernanceV3Avalanche} from 'aave-address-book/GovernanceV3Avalanche.sol'; +import {GovernanceV3Arbitrum} from 'aave-address-book/GovernanceV3Arbitrum.sol'; +import {GovernanceV3Optimism} from 'aave-address-book/GovernanceV3Optimism.sol'; +import {GovernanceV3Base} from 'aave-address-book/GovernanceV3Base.sol'; +import {AaveV2Ethereum} from 'aave-address-book/AaveV2Ethereum.sol'; +import {AaveV3Ethereum} from 'aave-address-book/AaveV3Ethereum.sol'; +import {AaveV2Polygon} from 'aave-address-book/AaveV2Polygon.sol'; +import {AaveV3Polygon} from 'aave-address-book/AaveV3Polygon.sol'; +import {AaveV2Avalanche} from 'aave-address-book/AaveV2Avalanche.sol'; +import {AaveV3Avalanche} from 'aave-address-book/AaveV3Avalanche.sol'; +import {AaveV3Optimism} from 'aave-address-book/AaveV3Optimism.sol'; +import {AaveV3Arbitrum} from 'aave-address-book/AaveV3Arbitrum.sol'; +import {AaveV3Base} from 'aave-address-book/AaveV3Base.sol'; +import {ParaSwapRepayAdapterV2} from 'src/contracts/ParaSwapRepayAdapterV2.sol'; +import {ParaSwapRepayAdapterV3} from 'src/contracts/ParaSwapRepayAdapterV3.sol'; +import {IParaSwapAugustusRegistry} from 'src/contracts/dependencies/paraswap/IParaSwapAugustusRegistry.sol'; +import {AugustusRegistry} from 'src/contracts/dependencies/paraswap/AugustusRegistry.sol'; + +contract EthereumV2 is EthereumScript { + function run() external broadcast { + new ParaSwapRepayAdapterV2( + IPoolAddressesProvider(address(AaveV2Ethereum.POOL_ADDRESSES_PROVIDER)), + address(AaveV2Ethereum.POOL), + IParaSwapAugustusRegistry(AugustusRegistry.ETHEREUM), + GovernanceV3Ethereum.EXECUTOR_LVL_1 + ); + } +} + +contract EthereumV3 is EthereumScript { + function run() external broadcast { + new ParaSwapRepayAdapterV3( + IPoolAddressesProvider(address(AaveV3Ethereum.POOL_ADDRESSES_PROVIDER)), + address(AaveV3Ethereum.POOL), + IParaSwapAugustusRegistry(AugustusRegistry.ETHEREUM), + GovernanceV3Ethereum.EXECUTOR_LVL_1 + ); + } +} + +contract PolygonV2 is PolygonScript { + function run() external broadcast { + new ParaSwapRepayAdapterV2( + IPoolAddressesProvider(address(AaveV2Polygon.POOL_ADDRESSES_PROVIDER)), + address(AaveV2Polygon.POOL), + IParaSwapAugustusRegistry(AugustusRegistry.POLYGON), + GovernanceV3Polygon.EXECUTOR_LVL_1 + ); + } +} + +contract PolygonV3 is PolygonScript { + function run() external broadcast { + new ParaSwapRepayAdapterV3( + IPoolAddressesProvider(address(AaveV3Polygon.POOL_ADDRESSES_PROVIDER)), + address(AaveV3Polygon.POOL), + IParaSwapAugustusRegistry(AugustusRegistry.POLYGON), + GovernanceV3Polygon.EXECUTOR_LVL_1 + ); + } +} + +contract AvalancheV2 is AvalancheScript { + function run() external broadcast { + new ParaSwapRepayAdapterV2( + IPoolAddressesProvider(address(AaveV2Avalanche.POOL_ADDRESSES_PROVIDER)), + address(AaveV2Avalanche.POOL), + IParaSwapAugustusRegistry(AugustusRegistry.AVALANCHE), + GovernanceV3Avalanche.EXECUTOR_LVL_1 + ); + } +} + +contract AvalancheV3 is AvalancheScript { + function run() external broadcast { + new ParaSwapRepayAdapterV3( + IPoolAddressesProvider(address(AaveV3Avalanche.POOL_ADDRESSES_PROVIDER)), + address(AaveV3Avalanche.POOL), + IParaSwapAugustusRegistry(AugustusRegistry.AVALANCHE), + GovernanceV3Avalanche.EXECUTOR_LVL_1 + ); + } +} + +contract ArbitrumV3 is ArbitrumScript { + function run() external broadcast { + new ParaSwapRepayAdapterV3( + IPoolAddressesProvider(address(AaveV3Arbitrum.POOL_ADDRESSES_PROVIDER)), + address(AaveV3Arbitrum.POOL), + IParaSwapAugustusRegistry(AugustusRegistry.ARBITRUM), + GovernanceV3Arbitrum.EXECUTOR_LVL_1 + ); + } +} + +contract OptimismV3 is OptimismScript { + function run() external broadcast { + new ParaSwapRepayAdapterV3( + IPoolAddressesProvider(address(AaveV3Optimism.POOL_ADDRESSES_PROVIDER)), + address(AaveV3Optimism.POOL), + IParaSwapAugustusRegistry(AugustusRegistry.OPTIMISM), + GovernanceV3Optimism.EXECUTOR_LVL_1 + ); + } +} + +contract BaseV3 is BaseScript { + function run() external broadcast { + new ParaSwapRepayAdapterV3( + IPoolAddressesProvider(address(AaveV3Base.POOL_ADDRESSES_PROVIDER)), + address(AaveV3Base.POOL), + IParaSwapAugustusRegistry(AugustusRegistry.BASE), + GovernanceV3Base.EXECUTOR_LVL_1 + ); + } +} diff --git a/src/script/Deploy_ParaSwapWithdrawAdapter.s.sol b/src/script/Deploy_ParaSwapWithdrawAdapter.s.sol new file mode 100644 index 0000000..2ef1c48 --- /dev/null +++ b/src/script/Deploy_ParaSwapWithdrawAdapter.s.sol @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IPoolAddressesProvider} from '@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol'; +import {ArbitrumScript, EthereumScript, PolygonScript, AvalancheScript, OptimismScript, BaseScript} from 'aave-helpers/ScriptUtils.sol'; +import {GovernanceV3Ethereum} from 'aave-address-book/GovernanceV3Ethereum.sol'; +import {GovernanceV3Polygon} from 'aave-address-book/GovernanceV3Polygon.sol'; +import {GovernanceV3Avalanche} from 'aave-address-book/GovernanceV3Avalanche.sol'; +import {GovernanceV3Arbitrum} from 'aave-address-book/GovernanceV3Arbitrum.sol'; +import {GovernanceV3Optimism} from 'aave-address-book/GovernanceV3Optimism.sol'; +import {GovernanceV3Base} from 'aave-address-book/GovernanceV3Base.sol'; +import {AaveV2Ethereum} from 'aave-address-book/AaveV2Ethereum.sol'; +import {AaveV3Ethereum} from 'aave-address-book/AaveV3Ethereum.sol'; +import {AaveV2Polygon} from 'aave-address-book/AaveV2Polygon.sol'; +import {AaveV3Polygon} from 'aave-address-book/AaveV3Polygon.sol'; +import {AaveV2Avalanche} from 'aave-address-book/AaveV2Avalanche.sol'; +import {AaveV3Avalanche} from 'aave-address-book/AaveV3Avalanche.sol'; +import {AaveV3Optimism} from 'aave-address-book/AaveV3Optimism.sol'; +import {AaveV3Arbitrum} from 'aave-address-book/AaveV3Arbitrum.sol'; +import {AaveV3Base} from 'aave-address-book/AaveV3Base.sol'; +import {ParaSwapWithdrawSwapAdapterV2} from 'src/contracts/ParaSwapWithdrawSwapAdapterV2.sol'; +import {ParaSwapWithdrawSwapAdapterV3} from 'src/contracts/ParaSwapWithdrawSwapAdapterV3.sol'; +import {IParaSwapAugustusRegistry} from 'src/contracts/dependencies/paraswap/IParaSwapAugustusRegistry.sol'; +import {AugustusRegistry} from 'src/contracts/dependencies/paraswap/AugustusRegistry.sol'; + +contract EthereumV2 is EthereumScript { + function run() external broadcast { + new ParaSwapWithdrawSwapAdapterV2( + IPoolAddressesProvider(address(AaveV2Ethereum.POOL_ADDRESSES_PROVIDER)), + address(AaveV2Ethereum.POOL), + IParaSwapAugustusRegistry(AugustusRegistry.ETHEREUM), + GovernanceV3Ethereum.EXECUTOR_LVL_1 + ); + } +} + +contract EthereumV3 is EthereumScript { + function run() external broadcast { + new ParaSwapWithdrawSwapAdapterV3( + IPoolAddressesProvider(address(AaveV3Ethereum.POOL_ADDRESSES_PROVIDER)), + address(AaveV3Ethereum.POOL), + IParaSwapAugustusRegistry(AugustusRegistry.ETHEREUM), + GovernanceV3Ethereum.EXECUTOR_LVL_1 + ); + } +} + +contract PolygonV2 is PolygonScript { + function run() external broadcast { + new ParaSwapWithdrawSwapAdapterV2( + IPoolAddressesProvider(address(AaveV2Polygon.POOL_ADDRESSES_PROVIDER)), + address(AaveV2Polygon.POOL), + IParaSwapAugustusRegistry(AugustusRegistry.POLYGON), + GovernanceV3Polygon.EXECUTOR_LVL_1 + ); + } +} + +contract PolygonV3 is PolygonScript { + function run() external broadcast { + new ParaSwapWithdrawSwapAdapterV3( + IPoolAddressesProvider(address(AaveV3Polygon.POOL_ADDRESSES_PROVIDER)), + address(AaveV3Polygon.POOL), + IParaSwapAugustusRegistry(AugustusRegistry.POLYGON), + GovernanceV3Polygon.EXECUTOR_LVL_1 + ); + } +} + +contract AvalancheV2 is AvalancheScript { + function run() external broadcast { + new ParaSwapWithdrawSwapAdapterV2( + IPoolAddressesProvider(address(AaveV2Avalanche.POOL_ADDRESSES_PROVIDER)), + address(AaveV2Avalanche.POOL), + IParaSwapAugustusRegistry(AugustusRegistry.AVALANCHE), + GovernanceV3Avalanche.EXECUTOR_LVL_1 + ); + } +} + +contract AvalancheV3 is AvalancheScript { + function run() external broadcast { + new ParaSwapWithdrawSwapAdapterV3( + IPoolAddressesProvider(address(AaveV3Avalanche.POOL_ADDRESSES_PROVIDER)), + address(AaveV3Avalanche.POOL), + IParaSwapAugustusRegistry(AugustusRegistry.AVALANCHE), + GovernanceV3Avalanche.EXECUTOR_LVL_1 + ); + } +} + +contract ArbitrumV3 is ArbitrumScript { + function run() external broadcast { + new ParaSwapWithdrawSwapAdapterV3( + IPoolAddressesProvider(address(AaveV3Arbitrum.POOL_ADDRESSES_PROVIDER)), + address(AaveV3Arbitrum.POOL), + IParaSwapAugustusRegistry(AugustusRegistry.ARBITRUM), + GovernanceV3Arbitrum.EXECUTOR_LVL_1 + ); + } +} + +contract OptimismV3 is OptimismScript { + function run() external broadcast { + new ParaSwapWithdrawSwapAdapterV3( + IPoolAddressesProvider(address(AaveV3Optimism.POOL_ADDRESSES_PROVIDER)), + address(AaveV3Optimism.POOL), + IParaSwapAugustusRegistry(AugustusRegistry.OPTIMISM), + GovernanceV3Optimism.EXECUTOR_LVL_1 + ); + } +} + +contract BaseV3 is BaseScript { + function run() external broadcast { + new ParaSwapWithdrawSwapAdapterV3( + IPoolAddressesProvider(address(AaveV3Base.POOL_ADDRESSES_PROVIDER)), + address(AaveV3Base.POOL), + IParaSwapAugustusRegistry(AugustusRegistry.BASE), + GovernanceV3Base.EXECUTOR_LVL_1 + ); + } +} diff --git a/tests/pspcache/008cf99bc1ce700e9c5f66631c46785ab3355b0c b/src/tests/.pspcache/008cf99bc1ce700e9c5f66631c46785ab3355b0c similarity index 100% rename from tests/pspcache/008cf99bc1ce700e9c5f66631c46785ab3355b0c rename to src/tests/.pspcache/008cf99bc1ce700e9c5f66631c46785ab3355b0c diff --git a/src/tests/.pspcache/0206ce28826d6d407b6a6fe1a1b08efea658ab3c b/src/tests/.pspcache/0206ce28826d6d407b6a6fe1a1b08efea658ab3c new file mode 100644 index 0000000..703cbe7 --- /dev/null +++ b/src/tests/.pspcache/0206ce28826d6d407b6a6fe1a1b08efea658ab3c @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000068155a43676e000000000000000000000000000000000000000000000000000065a2f7a8b45a3959e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001040b86a4c10000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000068155a43676e000000000000000000000000000000000000000000000000000065a2f7a8b45a3959e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000004de4a478c2975ab1ea89e8196811f51a7b7ade33eb11000000000000000000004de5f20ef17b889b437c151eb5ba15a47bfc62bff46900000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/src/tests/.pspcache/07bde2b1c107aee4017b330bde3d12e12fdca898 b/src/tests/.pspcache/07bde2b1c107aee4017b330bde3d12e12fdca898 new file mode 100644 index 0000000..570eb0a --- /dev/null +++ b/src/tests/.pspcache/07bde2b1c107aee4017b330bde3d12e12fdca898 @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000386a7729bf068118ac000000000000000000000000000000000000000000000036c090d0ca6888000000000000000000000000000000000000000000000000000000000000000000a400000000000000000000000000000000000000000000000000000000000003a435326910000000000000000000000000000000000000000000000000000000000000002000000000000000000000000084bef12c9931ce12662cc9f2366b6a5029e4bd290000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba00000000000000000000000000000000000000000000000386a7729bf068118ac000000000000000000000000000000000000000000000036c090d0ca68880000000000000000000000000000000000000000000000000036c5cfa7499ded2e53000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000009abf798f5314bfd793a9e57a654bed35af4a1d60010000000000000000000000000000000000000000000000000000000003138800000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000065bd214bb9424a22c5f844c89305bfbbd19cb24c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000e592427a0aece92de3edee1f18e0157c05861564000000000000000000000000000000000000000000000000000000000000271000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000065c6076b000000000000000000000000000000000000000000000000000000000000002b5f98805a4e8be255a32880fdec7f6728c6568ba00001f46b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/pspcache/0adb11c7c9b2eaacc253f853e5da606b7b2ee444 b/src/tests/.pspcache/0adb11c7c9b2eaacc253f853e5da606b7b2ee444 similarity index 100% rename from tests/pspcache/0adb11c7c9b2eaacc253f853e5da606b7b2ee444 rename to src/tests/.pspcache/0adb11c7c9b2eaacc253f853e5da606b7b2ee444 diff --git a/tests/pspcache/0db734c16b5f9ef4e90ff3523aa0594aaa2e9b8d b/src/tests/.pspcache/0db734c16b5f9ef4e90ff3523aa0594aaa2e9b8d similarity index 100% rename from tests/pspcache/0db734c16b5f9ef4e90ff3523aa0594aaa2e9b8d rename to src/tests/.pspcache/0db734c16b5f9ef4e90ff3523aa0594aaa2e9b8d diff --git a/tests/pspcache/0e0c5f8c55ff556fb635bfc5627c9abd9d0717fe b/src/tests/.pspcache/0e0c5f8c55ff556fb635bfc5627c9abd9d0717fe similarity index 100% rename from tests/pspcache/0e0c5f8c55ff556fb635bfc5627c9abd9d0717fe rename to src/tests/.pspcache/0e0c5f8c55ff556fb635bfc5627c9abd9d0717fe diff --git a/src/tests/.pspcache/0ebfa91ab79dfb0c8479fd458c8106f65c5b57aa b/src/tests/.pspcache/0ebfa91ab79dfb0c8479fd458c8106f65c5b57aa new file mode 100644 index 0000000..09e7388 --- /dev/null +++ b/src/tests/.pspcache/0ebfa91ab79dfb0c8479fd458c8106f65c5b57aa @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000124bc0ddd92e560000000000000000000000000000000000000000000000000000000000001383589800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000038454e3f31b00000000000000000000000000000000000000000000000000000000000000200000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000124bc0ddd92e560000000000000000000000000000000000000000000000000000000000001383589800000000000000000000000000000000000000000000000000000000141dd760000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002a0000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009abf798f5314bfd793a9e57a654bed35af4a1d60010000000000000000000000000000000000000000000000000000000003138800000000000000000000000000000000000000000000000000000000000003400000000000000000000000000000000000000000000000000000000065bae13d1cdc1000539145d49a97be46cde5dbe800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000089b78cfa322f6c5de0abceecab66aee45393cc5a00000000000000000000000000000000000000000000000000000000000000448d7ef9bb000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee570000000000000000000000000000000000000000000000000000000141dd76000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004400000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/pspcache/0ed0b110e587dbe355a7506f0f20f9d47970748c b/src/tests/.pspcache/0ed0b110e587dbe355a7506f0f20f9d47970748c similarity index 100% rename from tests/pspcache/0ed0b110e587dbe355a7506f0f20f9d47970748c rename to src/tests/.pspcache/0ed0b110e587dbe355a7506f0f20f9d47970748c diff --git a/tests/pspcache/143933b8f3afbeb3654d8f41766a25855620197e b/src/tests/.pspcache/143933b8f3afbeb3654d8f41766a25855620197e similarity index 100% rename from tests/pspcache/143933b8f3afbeb3654d8f41766a25855620197e rename to src/tests/.pspcache/143933b8f3afbeb3654d8f41766a25855620197e diff --git a/src/tests/.pspcache/16cca61e9a629120ceba0286bef37b750d8dffbe b/src/tests/.pspcache/16cca61e9a629120ceba0286bef37b750d8dffbe new file mode 100644 index 0000000..6802a92 --- /dev/null +++ b/src/tests/.pspcache/16cca61e9a629120ceba0286bef37b750d8dffbe @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000022e9d589429c28c09c600000000000000000000000000000000000000000000021e19e0c9bab240000000000000000000000000000000000000000000000000000000000000000000a400000000000000000000000000000000000000000000000000000000000003c435326910000000000000000000000000000000000000000000000000000000000000002000000000000000000000000084bef12c9931ce12662cc9f2366b6a5029e4bd290000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba000000000000000000000000000000000000000000000022e9d589429c28c09c600000000000000000000000000000000000000000000021e19e0c9bab240000000000000000000000000000000000000000000000000021e58244a4165e3ebaa000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000009abf798f5314bfd793a9e57a654bed35af4a1d60010000000000000000000000000000000000000000000000000000000003138800000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000065bbaa66db65607733da45a29b876575d8f5dd5e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000e592427a0aece92de3edee1f18e0157c05861564000000000000000000000000000000000000000000000000000000000000271000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000065c4908500000000000000000000000000000000000000000000000000000000000000425f98805a4e8be255a32880fdec7f6728c6568ba00001f4a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000646b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/src/tests/.pspcache/17a7aa803ac5cbbdbef0c1a211f625eac136b884 b/src/tests/.pspcache/17a7aa803ac5cbbdbef0c1a211f625eac136b884 new file mode 100644 index 0000000..6588079 --- /dev/null +++ b/src/tests/.pspcache/17a7aa803ac5cbbdbef0c1a211f625eac136b884 @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000001f6b6bcb9371facf3ba0000000000000000000000000000000000000000000001e7e4171bf4d3a0000000000000000000000000000000000000000000000000000000000000000000a400000000000000000000000000000000000000000000000000000000000003a435326910000000000000000000000000000000000000000000000000000000000000002000000000000000000000000084bef12c9931ce12662cc9f2366b6a5029e4bd290000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba00000000000000000000000000000000000000000000001f6b6bcb9371facf3ba0000000000000000000000000000000000000000000001e7e4171bf4d3a000000000000000000000000000000000000000000000000001e81258c7b446851e57000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000009abf798f5314bfd793a9e57a654bed35af4a1d60010000000000000000000000000000000000000000000000000000000003138800000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000065baf343ffd2e9acc23746beb078a3921ab6e9f900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000e592427a0aece92de3edee1f18e0157c05861564000000000000000000000000000000000000000000000000000000000000271000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000065c3d962000000000000000000000000000000000000000000000000000000000000002b5f98805a4e8be255a32880fdec7f6728c6568ba00001f46b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/src/tests/.pspcache/17eddcf2c497766999734d4f623ebf3fad6e2df1 b/src/tests/.pspcache/17eddcf2c497766999734d4f623ebf3fad6e2df1 new file mode 100644 index 0000000..750324d --- /dev/null +++ b/src/tests/.pspcache/17eddcf2c497766999734d4f623ebf3fad6e2df1 @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000fe485cf4e174b95bf30000000000000000000000000000000000000000000000f6a7ef3d111b58000000000000000000000000000000000000000000000000000000000000000000a400000000000000000000000000000000000000000000000000000000000003a435326910000000000000000000000000000000000000000000000000000000000000002000000000000000000000000084bef12c9931ce12662cc9f2366b6a5029e4bd290000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba00000000000000000000000000000000000000000000000fe485cf4e174b95bf30000000000000000000000000000000000000000000000f6a7ef3d111b5800000000000000000000000000000000000000000000000000f6e05a3fc4853533fe000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000009abf798f5314bfd793a9e57a654bed35af4a1d60010000000000000000000000000000000000000000000000000000000003138800000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000065bbb7755220ea043f4a4005aaad53bca698feb500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000e592427a0aece92de3edee1f18e0157c05861564000000000000000000000000000000000000000000000000000000000000271000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000065c49d94000000000000000000000000000000000000000000000000000000000000002b5f98805a4e8be255a32880fdec7f6728c6568ba00001f46b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/src/tests/.pspcache/1bc8f403e290d1db8b2d51b2e76e1ab3b86879e8 b/src/tests/.pspcache/1bc8f403e290d1db8b2d51b2e76e1ab3b86879e8 new file mode 100644 index 0000000..1a28325 --- /dev/null +++ b/src/tests/.pspcache/1bc8f403e290d1db8b2d51b2e76e1ab3b86879e8 @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000068155a43676e000000000000000000000000000000000000000000000000000064dcfc03a2b1f1598000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000000000000000000000000000000001040b86a4c10000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000068155a43676e000000000000000000000000000000000000000000000000000064dcfc03a2b1f1598000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000004de4a478c2975ab1ea89e8196811f51a7b7ade33eb11000000000000000000004de5f20ef17b889b437c151eb5ba15a47bfc62bff46900000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/pspcache/25bced76cd1532466727f7db257159a3bfeee7d1 b/src/tests/.pspcache/25bced76cd1532466727f7db257159a3bfeee7d1 similarity index 100% rename from tests/pspcache/25bced76cd1532466727f7db257159a3bfeee7d1 rename to src/tests/.pspcache/25bced76cd1532466727f7db257159a3bfeee7d1 diff --git a/src/tests/.pspcache/2c9a59aa8dad846bc87b7277f335a51cddd6338a b/src/tests/.pspcache/2c9a59aa8dad846bc87b7277f335a51cddd6338a new file mode 100644 index 0000000..38211df --- /dev/null +++ b/src/tests/.pspcache/2c9a59aa8dad846bc87b7277f335a51cddd6338a @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000037e1fc1acf92d7eaae00000000000000000000000000000000000000000000003635c9adc5dea0000000000000000000000000000000000000000000000000000000000000000000a400000000000000000000000000000000000000000000000000000000000003a435326910000000000000000000000000000000000000000000000000000000000000002000000000000000000000000084bef12c9931ce12662cc9f2366b6a5029e4bd290000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba0000000000000000000000000000000000000000000000037e1fc1acf92d7eaae00000000000000000000000000000000000000000000003635c9adc5dea00000000000000000000000000000000000000000000000000036414e3cd378328f57000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000009abf798f5314bfd793a9e57a654bed35af4a1d60010000000000000000000000000000000000000000000000000000000003138800000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000065bba719a7aabf91d4ca4f3c88f8784d7b361be600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000e592427a0aece92de3edee1f18e0157c05861564000000000000000000000000000000000000000000000000000000000000271000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000065c48d38000000000000000000000000000000000000000000000000000000000000002b5f98805a4e8be255a32880fdec7f6728c6568ba00001f46b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/src/tests/.pspcache/2f9b0a370e8129042ff1ec12781c4cffecd24e3b b/src/tests/.pspcache/2f9b0a370e8129042ff1ec12781c4cffecd24e3b new file mode 100644 index 0000000..039cede --- /dev/null +++ b/src/tests/.pspcache/2f9b0a370e8129042ff1ec12781c4cffecd24e3b @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000340aad21b3b700000000000000000000000000000000000000000000000000003219ccdc670ef049b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001040b86a4c10000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000340aad21b3b700000000000000000000000000000000000000000000000000003219ccdc670ef049b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000004de4a478c2975ab1ea89e8196811f51a7b7ade33eb11000000000000000000004de5f20ef17b889b437c151eb5ba15a47bfc62bff46900000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/pspcache/30a8a8befa9059a15374576bbd0d1030efd2566d b/src/tests/.pspcache/30a8a8befa9059a15374576bbd0d1030efd2566d similarity index 100% rename from tests/pspcache/30a8a8befa9059a15374576bbd0d1030efd2566d rename to src/tests/.pspcache/30a8a8befa9059a15374576bbd0d1030efd2566d diff --git a/tests/pspcache/31766d47cce8df104f81dcaa07ccb425b3efcc49 b/src/tests/.pspcache/31766d47cce8df104f81dcaa07ccb425b3efcc49 similarity index 100% rename from tests/pspcache/31766d47cce8df104f81dcaa07ccb425b3efcc49 rename to src/tests/.pspcache/31766d47cce8df104f81dcaa07ccb425b3efcc49 diff --git a/tests/pspcache/31f27fac12203c5177c7b12ab7a8dea95ffbc4d4 b/src/tests/.pspcache/31f27fac12203c5177c7b12ab7a8dea95ffbc4d4 similarity index 100% rename from tests/pspcache/31f27fac12203c5177c7b12ab7a8dea95ffbc4d4 rename to src/tests/.pspcache/31f27fac12203c5177c7b12ab7a8dea95ffbc4d4 diff --git a/src/tests/.pspcache/353c9f4637609a68600e1c46526d0c98f02ebf8a b/src/tests/.pspcache/353c9f4637609a68600e1c46526d0c98f02ebf8a new file mode 100644 index 0000000..71871ca --- /dev/null +++ b/src/tests/.pspcache/353c9f4637609a68600e1c46526d0c98f02ebf8a @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000032d26d12e980b6000000000000000000000000000000000000000000000000003146b880b87e7aa3d8d00000000000000000000000000000000000000000000000000000000000000840000000000000000000000000000000000000000000000000000000000000264a6886da900000000000000000000000000000000000000000000000000000000000000200000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba0000000000000000000000000e592427a0aece92de3edee1f18e0157c0586156400000000000000000000000000000000000000000000032d26d12e980b6000000000000000000000000000000000000000000000000003146b880b87e7aa3d8d00000000000000000000000000000000000000000000032ccdde112a756d86b701000000000000000000000000000000000000000000000000000000000313880000000000000000000000000000000000000000000000000000000065baac550000000000000000000000009abf798f5314bfd793a9e57a654bed35af4a1d600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000002206e10e9a993414a3fae553fb55926b48d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b6b175474e89094c44da98b954eedeac495271d0f0001f45f98805a4e8be255a32880fdec7f6728c6568ba0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/pspcache/37e7d88e75b27f030554977403995e6b8844b8c4 b/src/tests/.pspcache/37e7d88e75b27f030554977403995e6b8844b8c4 similarity index 100% rename from tests/pspcache/37e7d88e75b27f030554977403995e6b8844b8c4 rename to src/tests/.pspcache/37e7d88e75b27f030554977403995e6b8844b8c4 diff --git a/src/tests/.pspcache/387d830ca6c3a001f3e3ef3668e985422df6ef71 b/src/tests/.pspcache/387d830ca6c3a001f3e3ef3668e985422df6ef71 new file mode 100644 index 0000000..e4c02b5 --- /dev/null +++ b/src/tests/.pspcache/387d830ca6c3a001f3e3ef3668e985422df6ef71 @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000037e1fc1acf92d7eaae00000000000000000000000000000000000000000000003635c9adc5dea00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004642298207a00000000000000000000000000000000000000000000000000000000000000200000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba0000000000000000000000000000000000000000000000037e1fc1acf92d7eaae00000000000000000000000000000000000000000000003635c9adc5dea00000000000000000000000000000000000000000000000000036414e3cd378328f5700000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009abf798f5314bfd793a9e57a654bed35af4a1d60010000000000000000000000000000000000000000000000000000000003138800000000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000000000000000000000065bba83b26edff12dec7402abb09dae8a80d3373000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000e592427a0aece92de3edee1f18e0157c058615640000000000000000000000000000000000000000000000000000000000000124f28c0498000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee570000000000000000000000000000000000000000000000000000000065c48e5b00000000000000000000000000000000000000000000003635c9adc5dea00000000000000000000000000000000000000000000000000037e1fc1acf92d7eaac000000000000000000000000000000000000000000000000000000000000002b5f98805a4e8be255a32880fdec7f6728c6568ba00001f46b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012400000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/pspcache/408543a431121a276c7278389e90263d874716d9 b/src/tests/.pspcache/408543a431121a276c7278389e90263d874716d9 similarity index 100% rename from tests/pspcache/408543a431121a276c7278389e90263d874716d9 rename to src/tests/.pspcache/408543a431121a276c7278389e90263d874716d9 diff --git a/src/tests/.pspcache/40e07574cf4b2adb2bcb86f11d1a1e4ca669f0cd b/src/tests/.pspcache/40e07574cf4b2adb2bcb86f11d1a1e4ca669f0cd new file mode 100644 index 0000000..284082c --- /dev/null +++ b/src/tests/.pspcache/40e07574cf4b2adb2bcb86f11d1a1e4ca669f0cd @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000006fb3407031f8ae53bb00000000000000000000000000000000000000000000006c6b935b8bbd400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004642298207a00000000000000000000000000000000000000000000000000000000000000200000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba000000000000000000000000000000000000000000000006fb3407031f8ae53bb00000000000000000000000000000000000000000000006c6b935b8bbd40000000000000000000000000000000000000000000000000006c72615b878197d9fe00000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009abf798f5314bfd793a9e57a654bed35af4a1d60010000000000000000000000000000000000000000000000000000000003138800000000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000000000000000000000065baef91fcbcb05d1cf64ae1b2af891b77f9fac3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000e592427a0aece92de3edee1f18e0157c058615640000000000000000000000000000000000000000000000000000000000000124f28c0498000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee570000000000000000000000000000000000000000000000000000000065c3d5b000000000000000000000000000000000000000000000006c6b935b8bbd40000000000000000000000000000000000000000000000000006fb3407031f8ae53b9000000000000000000000000000000000000000000000000000000000000002b5f98805a4e8be255a32880fdec7f6728c6568ba00001f46b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012400000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/src/tests/.pspcache/417ad23e21f840318b273b89c74fce83c215e2a0 b/src/tests/.pspcache/417ad23e21f840318b273b89c74fce83c215e2a0 new file mode 100644 index 0000000..2712733 --- /dev/null +++ b/src/tests/.pspcache/417ad23e21f840318b273b89c74fce83c215e2a0 @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000001beea1040445f622f9e0000000000000000000000000000000000000000000001b1ae4d6e2ef500000000000000000000000000000000000000000000000000000000000000000000a400000000000000000000000000000000000000000000000000000000000003a435326910000000000000000000000000000000000000000000000000000000000000002000000000000000000000000084bef12c9931ce12662cc9f2366b6a5029e4bd290000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba00000000000000000000000000000000000000000000001beea1040445f622f9e0000000000000000000000000000000000000000000001b1ae4d6e2ef50000000000000000000000000000000000000000000000000001b1e5bb45d9fe28a588000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000009abf798f5314bfd793a9e57a654bed35af4a1d60010000000000000000000000000000000000000000000000000000000003138800000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000065bd21e459e839d00d704ce09697be3d5075909d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000e592427a0aece92de3edee1f18e0157c05861564000000000000000000000000000000000000000000000000000000000000271000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000065c60803000000000000000000000000000000000000000000000000000000000000002b5f98805a4e8be255a32880fdec7f6728c6568ba00001f46b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/pspcache/44afa50aac5288b0a63acf59aea34e79451986c7 b/src/tests/.pspcache/44afa50aac5288b0a63acf59aea34e79451986c7 similarity index 100% rename from tests/pspcache/44afa50aac5288b0a63acf59aea34e79451986c7 rename to src/tests/.pspcache/44afa50aac5288b0a63acf59aea34e79451986c7 diff --git a/src/tests/.pspcache/4e7e1050c698ff528ca44c20251f5ea28b985fcc b/src/tests/.pspcache/4e7e1050c698ff528ca44c20251f5ea28b985fcc new file mode 100644 index 0000000..0dd195b --- /dev/null +++ b/src/tests/.pspcache/4e7e1050c698ff528ca44c20251f5ea28b985fcc @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000bdbc41e0348b3000000000000000000000000000000000000000000000000000b800a2f16dec4771fa00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000264a6886da900000000000000000000000000000000000000000000000000000000000000200000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba0000000000000000000000000e592427a0aece92de3edee1f18e0157c058615640000000000000000000000000000000000000000000000bdbc41e0348b3000000000000000000000000000000000000000000000000000b800a2f16dec4771fa0000000000000000000000000000000000000000000000bdb17b1dd8401f6d9601000000000000000000000000000000000000000000000000000000000313880000000000000000000000000000000000000000000000000000000065badced0000000000000000000000009abf798f5314bfd793a9e57a654bed35af4a1d600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000002203a7516849ee34c3585327cf395a0650f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b6b175474e89094c44da98b954eedeac495271d0f0001f45f98805a4e8be255a32880fdec7f6728c6568ba0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/src/tests/.pspcache/524f30f5ea6116dbfac241bb8902ccfad60870b5 b/src/tests/.pspcache/524f30f5ea6116dbfac241bb8902ccfad60870b5 new file mode 100644 index 0000000..d5b4e94 --- /dev/null +++ b/src/tests/.pspcache/524f30f5ea6116dbfac241bb8902ccfad60870b5 @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000f3f20b8dfa69d0000000000000000000000000000000000000000000000000000000000001042c9d400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000038454e3f31b00000000000000000000000000000000000000000000000000000000000000200000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000f3f20b8dfa69d0000000000000000000000000000000000000000000000000000000000001042c9d40000000000000000000000000000000000000000000000000000000010c388d0000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002a0000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009abf798f5314bfd793a9e57a654bed35af4a1d60010000000000000000000000000000000000000000000000000000000003138800000000000000000000000000000000000000000000000000000000000003400000000000000000000000000000000000000000000000000000000065bacc60cf79cf7c829f48ee98674b1e851cea7c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000089b78cfa322f6c5de0abceecab66aee45393cc5a00000000000000000000000000000000000000000000000000000000000000448d7ef9bb000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee57000000000000000000000000000000000000000000000000000000010c388d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004400000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/pspcache/539f20ad6d391844ef555ae37fe5720c8740c1f4 b/src/tests/.pspcache/539f20ad6d391844ef555ae37fe5720c8740c1f4 similarity index 100% rename from tests/pspcache/539f20ad6d391844ef555ae37fe5720c8740c1f4 rename to src/tests/.pspcache/539f20ad6d391844ef555ae37fe5720c8740c1f4 diff --git a/src/tests/.pspcache/552c346313cf331377fcd0f51acab6c10f83d92d b/src/tests/.pspcache/552c346313cf331377fcd0f51acab6c10f83d92d new file mode 100644 index 0000000..fb6d597 --- /dev/null +++ b/src/tests/.pspcache/552c346313cf331377fcd0f51acab6c10f83d92d @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000001bf0edae23c2bc940200000000000000000000000000000000000000000000001b1ae4d6e2ef500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004642298207a00000000000000000000000000000000000000000000000000000000000000200000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba000000000000000000000000000000000000000000000001bf0edae23c2bc940200000000000000000000000000000000000000000000001b1ae4d6e2ef50000000000000000000000000000000000000000000000000001b20973939168a590500000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009abf798f5314bfd793a9e57a654bed35af4a1d60010000000000000000000000000000000000000000000000000000000003138800000000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000000000000000000000065bba96dae11aa06082e4e57b985b271905ac79b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000e592427a0aece92de3edee1f18e0157c058615640000000000000000000000000000000000000000000000000000000000000124f28c0498000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee570000000000000000000000000000000000000000000000000000000065c48f8d00000000000000000000000000000000000000000000001b1ae4d6e2ef50000000000000000000000000000000000000000000000000001bf0edae23c2bc9400000000000000000000000000000000000000000000000000000000000000002b5f98805a4e8be255a32880fdec7f6728c6568ba00001f46b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012400000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/pspcache/554d008100c7b2336f60691acaa14e09c3b45370 b/src/tests/.pspcache/554d008100c7b2336f60691acaa14e09c3b45370 similarity index 100% rename from tests/pspcache/554d008100c7b2336f60691acaa14e09c3b45370 rename to src/tests/.pspcache/554d008100c7b2336f60691acaa14e09c3b45370 diff --git a/tests/pspcache/55ec4a627ff9237531012c862bb00125e59ebebf b/src/tests/.pspcache/55ec4a627ff9237531012c862bb00125e59ebebf similarity index 100% rename from tests/pspcache/55ec4a627ff9237531012c862bb00125e59ebebf rename to src/tests/.pspcache/55ec4a627ff9237531012c862bb00125e59ebebf diff --git a/tests/pspcache/5c70c2c29330e126ee4b32020e6aac1e5ac3a15b b/src/tests/.pspcache/5c70c2c29330e126ee4b32020e6aac1e5ac3a15b similarity index 100% rename from tests/pspcache/5c70c2c29330e126ee4b32020e6aac1e5ac3a15b rename to src/tests/.pspcache/5c70c2c29330e126ee4b32020e6aac1e5ac3a15b diff --git a/src/tests/.pspcache/600f4fe1f97cbb9f835fceefc42a81c370973289 b/src/tests/.pspcache/600f4fe1f97cbb9f835fceefc42a81c370973289 new file mode 100644 index 0000000..24708f0 --- /dev/null +++ b/src/tests/.pspcache/600f4fe1f97cbb9f835fceefc42a81c370973289 @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000015af1d78b58c400000000000000000000000000000000000000000000000000014dd0486d9b5d8281000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001040b86a4c10000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000015af1d78b58c400000000000000000000000000000000000000000000000000014dd0486d9b5d8281000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000004de4a478c2975ab1ea89e8196811f51a7b7ade33eb11000000000000000000004de5f20ef17b889b437c151eb5ba15a47bfc62bff46900000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/src/tests/.pspcache/69de66cc45ecb2b7888d38946a452d20342f0957 b/src/tests/.pspcache/69de66cc45ecb2b7888d38946a452d20342f0957 new file mode 100644 index 0000000..e677a13 --- /dev/null +++ b/src/tests/.pspcache/69de66cc45ecb2b7888d38946a452d20342f0957 @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000006fc47b30a5994183b600000000000000000000000000000000000000000000006c6b935b8bbd400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004642298207a00000000000000000000000000000000000000000000000000000000000000200000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba000000000000000000000000000000000000000000000006fc47b30a5994183b600000000000000000000000000000000000000000000006c6b935b8bbd40000000000000000000000000000000000000000000000000006c831ba40ba630b19600000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009abf798f5314bfd793a9e57a654bed35af4a1d60010000000000000000000000000000000000000000000000000000000003138800000000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000000000000000000000065bba71a77c3f3c282be49a1abe1a5849e579e1e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000e592427a0aece92de3edee1f18e0157c058615640000000000000000000000000000000000000000000000000000000000000124f28c0498000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee570000000000000000000000000000000000000000000000000000000065c48d3a00000000000000000000000000000000000000000000006c6b935b8bbd40000000000000000000000000000000000000000000000000006fc47b30a5994183b4000000000000000000000000000000000000000000000000000000000000002b5f98805a4e8be255a32880fdec7f6728c6568ba00001f46b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012400000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/pspcache/6f04a16df882a3cb3172dc587bf388de121190a1 b/src/tests/.pspcache/6f04a16df882a3cb3172dc587bf388de121190a1 similarity index 100% rename from tests/pspcache/6f04a16df882a3cb3172dc587bf388de121190a1 rename to src/tests/.pspcache/6f04a16df882a3cb3172dc587bf388de121190a1 diff --git a/tests/pspcache/718870dfd36fb8767847d23bb356a4877634bbcf b/src/tests/.pspcache/718870dfd36fb8767847d23bb356a4877634bbcf similarity index 100% rename from tests/pspcache/718870dfd36fb8767847d23bb356a4877634bbcf rename to src/tests/.pspcache/718870dfd36fb8767847d23bb356a4877634bbcf diff --git a/tests/pspcache/74a5b04eaf45250d4ad9f7ea2d646e3053d49b1f b/src/tests/.pspcache/74a5b04eaf45250d4ad9f7ea2d646e3053d49b1f similarity index 100% rename from tests/pspcache/74a5b04eaf45250d4ad9f7ea2d646e3053d49b1f rename to src/tests/.pspcache/74a5b04eaf45250d4ad9f7ea2d646e3053d49b1f diff --git a/tests/pspcache/7843a847c808fafab02c4297592d06c9f70ccbf1 b/src/tests/.pspcache/7843a847c808fafab02c4297592d06c9f70ccbf1 similarity index 100% rename from tests/pspcache/7843a847c808fafab02c4297592d06c9f70ccbf1 rename to src/tests/.pspcache/7843a847c808fafab02c4297592d06c9f70ccbf1 diff --git a/src/tests/.pspcache/7ebc0ac3529428069322536b5ba677df6d147b59 b/src/tests/.pspcache/7ebc0ac3529428069322536b5ba677df6d147b59 new file mode 100644 index 0000000..34fb5b8 --- /dev/null +++ b/src/tests/.pspcache/7ebc0ac3529428069322536b5ba677df6d147b59 @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000a7a77d434e94af3a990000000000000000000000000000000000000000000000a2a15d09519be00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004642298207a00000000000000000000000000000000000000000000000000000000000000200000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba00000000000000000000000000000000000000000000000a7a77d434e94af3a990000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000000000000000000000000000a2c5683767a1c07e7c00000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009abf798f5314bfd793a9e57a654bed35af4a1d60010000000000000000000000000000000000000000000000000000000003138800000000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000000000000000000000065bba71bffda03040f8e47189e52077a90320ae2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000e592427a0aece92de3edee1f18e0157c058615640000000000000000000000000000000000000000000000000000000000000124f28c0498000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee570000000000000000000000000000000000000000000000000000000065c48d3a0000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000000000000000000000000000a7a77d434e94af3a97000000000000000000000000000000000000000000000000000000000000002b5f98805a4e8be255a32880fdec7f6728c6568ba00001f46b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012400000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/src/tests/.pspcache/84c67c9990ebacbf2be5eb98dbc32069d4853fb7 b/src/tests/.pspcache/84c67c9990ebacbf2be5eb98dbc32069d4853fb7 new file mode 100644 index 0000000..05fd150 --- /dev/null +++ b/src/tests/.pspcache/84c67c9990ebacbf2be5eb98dbc32069d4853fb7 @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000028a77936e92c81c000000000000000000000000000000000000000000000000027697dcce2ce9d680be00000000000000000000000000000000000000000000000000000000000000840000000000000000000000000000000000000000000000000000000000000264a6886da900000000000000000000000000000000000000000000000000000000000000200000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba0000000000000000000000000e592427a0aece92de3edee1f18e0157c0586156400000000000000000000000000000000000000000000028a77936e92c81c000000000000000000000000000000000000000000000000027697dcce2ce9d680be00000000000000000000000000000000000000000000028a1899bccca72c4ff101000000000000000000000000000000000000000000000000000000000313880000000000000000000000000000000000000000000000000000000065bd1e6d0000000000000000000000009abf798f5314bfd793a9e57a654bed35af4a1d600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000220f86b38a72f6144d380915a3b9c02b17100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b6b175474e89094c44da98b954eedeac495271d0f0001f45f98805a4e8be255a32880fdec7f6728c6568ba0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/pspcache/85867c9f069a103e4a7a578ec2ffbd52a4e9d4be b/src/tests/.pspcache/85867c9f069a103e4a7a578ec2ffbd52a4e9d4be similarity index 100% rename from tests/pspcache/85867c9f069a103e4a7a578ec2ffbd52a4e9d4be rename to src/tests/.pspcache/85867c9f069a103e4a7a578ec2ffbd52a4e9d4be diff --git a/src/tests/.pspcache/8a9b0d05f6bd39b23fc63439f2f6aabcfc9316ea b/src/tests/.pspcache/8a9b0d05f6bd39b23fc63439f2f6aabcfc9316ea new file mode 100644 index 0000000..83b73da --- /dev/null +++ b/src/tests/.pspcache/8a9b0d05f6bd39b23fc63439f2f6aabcfc9316ea @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000a78da4f53427fec12b0000000000000000000000000000000000000000000000a2a15d09519be00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004642298207a00000000000000000000000000000000000000000000000000000000000000200000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba00000000000000000000000000000000000000000000000a78da4f53427fec12b0000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000000000000000000000000000a2ac509e872458443e00000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009abf798f5314bfd793a9e57a654bed35af4a1d60010000000000000000000000000000000000000000000000000000000003138800000000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000000000000000000000065baecc70b0ffedcbaa240c1a8aacc3400eadb38000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000e592427a0aece92de3edee1f18e0157c058615640000000000000000000000000000000000000000000000000000000000000124f28c0498000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee570000000000000000000000000000000000000000000000000000000065c3d2e60000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000000000000000000000000000a78da4f53427fec129000000000000000000000000000000000000000000000000000000000000002b5f98805a4e8be255a32880fdec7f6728c6568ba00001f46b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012400000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/pspcache/8e35a8ee7df87f06ab9dc951bb8c088fc4cb2372 b/src/tests/.pspcache/8e35a8ee7df87f06ab9dc951bb8c088fc4cb2372 similarity index 100% rename from tests/pspcache/8e35a8ee7df87f06ab9dc951bb8c088fc4cb2372 rename to src/tests/.pspcache/8e35a8ee7df87f06ab9dc951bb8c088fc4cb2372 diff --git a/src/tests/.pspcache/8f9caeba4101137ce6c14c14786904022ca1b683 b/src/tests/.pspcache/8f9caeba4101137ce6c14c14786904022ca1b683 new file mode 100644 index 0000000..6d3a2c3 --- /dev/null +++ b/src/tests/.pspcache/8f9caeba4101137ce6c14c14786904022ca1b683 @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000021e19e0c9bab240000000000000000000000000000000000000000000000000000000000002422a41000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000038454e3f31b00000000000000000000000000000000000000000000000000000000000000200000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000021e19e0c9bab240000000000000000000000000000000000000000000000000000000000002422a410000000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002a0000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009abf798f5314bfd793a9e57a654bed35af4a1d60010000000000000000000000000000000000000000000000000000000003138800000000000000000000000000000000000000000000000000000000000003400000000000000000000000000000000000000000000000000000000065bae0bb0a814c83530b4db9b3fab1554c67574700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000089b78cfa322f6c5de0abceecab66aee45393cc5a00000000000000000000000000000000000000000000000000000000000000448d7ef9bb000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000002540be4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004400000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/src/tests/.pspcache/92446b25dafc5e93c7fae0be44bdbda56ffca7b6 b/src/tests/.pspcache/92446b25dafc5e93c7fae0be44bdbda56ffca7b6 new file mode 100644 index 0000000..578bb09 --- /dev/null +++ b/src/tests/.pspcache/92446b25dafc5e93c7fae0be44bdbda56ffca7b6 @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000010f0cf064dd59200000000000000000000000000000000000000000000000000106876d7722aef8647200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000264a6886da900000000000000000000000000000000000000000000000000000000000000200000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba0000000000000000000000000e592427a0aece92de3edee1f18e0157c0586156400000000000000000000000000000000000000000000010f0cf064dd59200000000000000000000000000000000000000000000000000106876d7722aef8647200000000000000000000000000000000000000000000010ea602016b038e970f01000000000000000000000000000000000000000000000000000000000313880000000000000000000000000000000000000000000000000000000065ba7eb70000000000000000000000009abf798f5314bfd793a9e57a654bed35af4a1d600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000220a049af4f8b274f93a886657ff862bc0800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b6b175474e89094c44da98b954eedeac495271d0f0001f45f98805a4e8be255a32880fdec7f6728c6568ba0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/src/tests/.pspcache/93d59e0680372f2fe5812f1658fce4cba2e4be8f b/src/tests/.pspcache/93d59e0680372f2fe5812f1658fce4cba2e4be8f new file mode 100644 index 0000000..9b5335a --- /dev/null +++ b/src/tests/.pspcache/93d59e0680372f2fe5812f1658fce4cba2e4be8f @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000022385a827e815500000000000000000000000000000000000000000000000000212cfc07a5f3bbd613f00000000000000000000000000000000000000000000000000000000000000840000000000000000000000000000000000000000000000000000000000000264a6886da900000000000000000000000000000000000000000000000000000000000000200000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba0000000000000000000000000e592427a0aece92de3edee1f18e0157c0586156400000000000000000000000000000000000000000000022385a827e815500000000000000000000000000000000000000000000000000212cfc07a5f3bbd613f0000000000000000000000000000000000000000000002233a774174a727869101000000000000000000000000000000000000000000000000000000000313880000000000000000000000000000000000000000000000000000000065bd24f40000000000000000000000009abf798f5314bfd793a9e57a654bed35af4a1d600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000002200d9661dd73014fcea2a55ca1bde4c40300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b6b175474e89094c44da98b954eedeac495271d0f0001f45f98805a4e8be255a32880fdec7f6728c6568ba0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/pspcache/9723578eb7e58aca760e512d8591f9d05a1e61b0 b/src/tests/.pspcache/9723578eb7e58aca760e512d8591f9d05a1e61b0 similarity index 100% rename from tests/pspcache/9723578eb7e58aca760e512d8591f9d05a1e61b0 rename to src/tests/.pspcache/9723578eb7e58aca760e512d8591f9d05a1e61b0 diff --git a/tests/pspcache/9d7baacfacfa1b80390e1f950f5154ace3eed0d8 b/src/tests/.pspcache/9d7baacfacfa1b80390e1f950f5154ace3eed0d8 similarity index 100% rename from tests/pspcache/9d7baacfacfa1b80390e1f950f5154ace3eed0d8 rename to src/tests/.pspcache/9d7baacfacfa1b80390e1f950f5154ace3eed0d8 diff --git a/tests/pspcache/9e4692fc8c083de96468a9163b813b6c6dcf697a b/src/tests/.pspcache/9e4692fc8c083de96468a9163b813b6c6dcf697a similarity index 100% rename from tests/pspcache/9e4692fc8c083de96468a9163b813b6c6dcf697a rename to src/tests/.pspcache/9e4692fc8c083de96468a9163b813b6c6dcf697a diff --git a/src/tests/.pspcache/a3b419338b1be2384900c4351554503d82bb1375 b/src/tests/.pspcache/a3b419338b1be2384900c4351554503d82bb1375 new file mode 100644 index 0000000..eaa37e1 --- /dev/null +++ b/src/tests/.pspcache/a3b419338b1be2384900c4351554503d82bb1375 @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000cdff97fabcb46000000000000000000000000000000000000000000000000000c7c5e70ed218c7dc8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000264a6886da900000000000000000000000000000000000000000000000000000000000000200000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba0000000000000000000000000e592427a0aece92de3edee1f18e0157c058615640000000000000000000000000000000000000000000000cdff97fabcb46000000000000000000000000000000000000000000000000000c7c5e70ed218c7dc800000000000000000000000000000000000000000000000cdf39c63bb904f5cb901000000000000000000000000000000000000000000000000000000000313880000000000000000000000000000000000000000000000000000000065badd860000000000000000000000009abf798f5314bfd793a9e57a654bed35af4a1d600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000220ca63751b31be4c8b909c5a1a41a2025800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b6b175474e89094c44da98b954eedeac495271d0f0001f45f98805a4e8be255a32880fdec7f6728c6568ba0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/pspcache/ab2992a23bc48dbae8ec248c8569e5f287cae5c0 b/src/tests/.pspcache/ab2992a23bc48dbae8ec248c8569e5f287cae5c0 similarity index 100% rename from tests/pspcache/ab2992a23bc48dbae8ec248c8569e5f287cae5c0 rename to src/tests/.pspcache/ab2992a23bc48dbae8ec248c8569e5f287cae5c0 diff --git a/src/tests/.pspcache/bc5da0ef3ec5deea3c5e83620c15c8cbfeeca26b b/src/tests/.pspcache/bc5da0ef3ec5deea3c5e83620c15c8cbfeeca26b new file mode 100644 index 0000000..8fea991 --- /dev/null +++ b/src/tests/.pspcache/bc5da0ef3ec5deea3c5e83620c15c8cbfeeca26b @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000006c6b935b8bbd400000000000000000000000000000000000000000000000000068261a83cc33690a4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001040b86a4c10000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000006c6b935b8bbd400000000000000000000000000000000000000000000000000068261a83cc33690a4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000004de4a478c2975ab1ea89e8196811f51a7b7ade33eb11000000000000000000004de5f20ef17b889b437c151eb5ba15a47bfc62bff46900000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/pspcache/bd30bc9192e72f67a1f61278186d2dbe21c8fec7 b/src/tests/.pspcache/bd30bc9192e72f67a1f61278186d2dbe21c8fec7 similarity index 100% rename from tests/pspcache/bd30bc9192e72f67a1f61278186d2dbe21c8fec7 rename to src/tests/.pspcache/bd30bc9192e72f67a1f61278186d2dbe21c8fec7 diff --git a/src/tests/.pspcache/be8894d1d3ab9f23a068465d94d5ecce2f4b7819 b/src/tests/.pspcache/be8894d1d3ab9f23a068465d94d5ecce2f4b7819 new file mode 100644 index 0000000..b0a75e0 --- /dev/null +++ b/src/tests/.pspcache/be8894d1d3ab9f23a068465d94d5ecce2f4b7819 @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000065a4da25d3016c0000000000000000000000000000000000000000000000000000000000006c67ec30000000000000000000000000000000000000000000000000000000000000000440000000000000000000000000000000000000000000000000000000000000464a94e78ef00000000000000000000000000000000000000000000000000000000000000200000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000065a4da25d3016c0000000000000000000000000000000000000000000000000000000000006c67ec30000000000000000000000000000000000000000000000000000000006fc23ac00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001600000000000000000000000009abf798f5314bfd793a9e57a654bed35af4a1d60010000000000000000000000000000000000000000000000000000000003138800000000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000000000000000000000065bae21e34914413022c406ab8dc3cec7d7a2df50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000baeeb4540f59d30e567a5b563cc0c4587edd936600000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000900000000000000000000000089b78cfa322f6c5de0abceecab66aee45393cc5a000000000000000000000000000000000000000000000000000000000000271000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000a59649758aa4d66e25f08dd01271e891fe521990000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e8d4a51000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/src/tests/.pspcache/c59d4514d1e765e278f1e95421519d5fe27cbf5b b/src/tests/.pspcache/c59d4514d1e765e278f1e95421519d5fe27cbf5b new file mode 100644 index 0000000..93d11f0 --- /dev/null +++ b/src/tests/.pspcache/c59d4514d1e765e278f1e95421519d5fe27cbf5b @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000037d95ec9b9e72f9c1200000000000000000000000000000000000000000000003635c9adc5dea0000000000000000000000000000000000000000000000000000000000000000000a400000000000000000000000000000000000000000000000000000000000003a435326910000000000000000000000000000000000000000000000000000000000000002000000000000000000000000084bef12c9931ce12662cc9f2366b6a5029e4bd290000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba0000000000000000000000000000000000000000000000037d95ec9b9e72f9c1200000000000000000000000000000000000000000000003635c9adc5dea0000000000000000000000000000000000000000000000000003638f12744a4cd4a7a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000009abf798f5314bfd793a9e57a654bed35af4a1d60010000000000000000000000000000000000000000000000000000000003138800000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000065baf0fd47a8f662a3eb426594eb6106a53cbbbc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000e592427a0aece92de3edee1f18e0157c05861564000000000000000000000000000000000000000000000000000000000000271000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000065c3d71d000000000000000000000000000000000000000000000000000000000000002b5f98805a4e8be255a32880fdec7f6728c6568ba00001f46b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/src/tests/.pspcache/d1e37909777498809c8471e6aa9f0bc35e5ca979 b/src/tests/.pspcache/d1e37909777498809c8471e6aa9f0bc35e5ca979 new file mode 100644 index 0000000..36235cc --- /dev/null +++ b/src/tests/.pspcache/d1e37909777498809c8471e6aa9f0bc35e5ca979 @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000021e19e0c9bab240000000000000000000000000000000000000000000000000020d02dd0a2e41fd226700000000000000000000000000000000000000000000000000000000000000840000000000000000000000000000000000000000000000000000000000000264a6886da900000000000000000000000000000000000000000000000000000000000000200000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba0000000000000000000000000e592427a0aece92de3edee1f18e0157c0586156400000000000000000000000000000000000000000000021e19e0c9bab240000000000000000000000000000000000000000000000000020d02dd0a2e41fd226700000000000000000000000000000000000000000000021d3fa72cce09f7c47501000000000000000000000000000000000000000000000000000000000313880000000000000000000000000000000000000000000000000000000065ba83b00000000000000000000000009abf798f5314bfd793a9e57a654bed35af4a1d600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000002209b67591e15134528a15a8e7a0da17adc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b6b175474e89094c44da98b954eedeac495271d0f0001f45f98805a4e8be255a32880fdec7f6728c6568ba0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/pspcache/d1f9d42a184020562218c50a52e9222fe45991a0 b/src/tests/.pspcache/d1f9d42a184020562218c50a52e9222fe45991a0 similarity index 100% rename from tests/pspcache/d1f9d42a184020562218c50a52e9222fe45991a0 rename to src/tests/.pspcache/d1f9d42a184020562218c50a52e9222fe45991a0 diff --git a/src/tests/.pspcache/d28769bfb9bb8244aab5cd85f4a7e6beaa4b1c88 b/src/tests/.pspcache/d28769bfb9bb8244aab5cd85f4a7e6beaa4b1c88 new file mode 100644 index 0000000..2b98e5f --- /dev/null +++ b/src/tests/.pspcache/d28769bfb9bb8244aab5cd85f4a7e6beaa4b1c88 @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000003635c9adc5dea000000000000000000000000000000000000000000000000000348272157dbc3c875a00000000000000000000000000000000000000000000000000000000000000840000000000000000000000000000000000000000000000000000000000000264a6886da900000000000000000000000000000000000000000000000000000000000000200000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba0000000000000000000000000e592427a0aece92de3edee1f18e0157c0586156400000000000000000000000000000000000000000000003635c9adc5dea000000000000000000000000000000000000000000000000000348272157dbc3c875a0000000000000000000000000000000000000000000000362230fe673b75d2cc01000000000000000000000000000000000000000000000000000000000313880000000000000000000000000000000000000000000000000000000065ba97e80000000000000000000000009abf798f5314bfd793a9e57a654bed35af4a1d600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000220af4d6416540b4f7aa268e0af0f0bc5d200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b6b175474e89094c44da98b954eedeac495271d0f0001f45f98805a4e8be255a32880fdec7f6728c6568ba0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/src/tests/.pspcache/d2e88d719754fcdf7dc63f12513a667cc4345b2b b/src/tests/.pspcache/d2e88d719754fcdf7dc63f12513a667cc4345b2b new file mode 100644 index 0000000..f01c7ce --- /dev/null +++ b/src/tests/.pspcache/d2e88d719754fcdf7dc63f12513a667cc4345b2b @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000a2a15d09519be0000000000000000000000000000000000000000000000000009db7c374ea95bdb21c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000264a6886da900000000000000000000000000000000000000000000000000000000000000200000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba0000000000000000000000000e592427a0aece92de3edee1f18e0157c058615640000000000000000000000000000000000000000000000a2a15d09519be0000000000000000000000000000000000000000000000000009db7c374ea95bdb21c0000000000000000000000000000000000000000000000a2987f9ad772c8d74a01000000000000000000000000000000000000000000000000000000000313880000000000000000000000000000000000000000000000000000000065baa9100000000000000000000000009abf798f5314bfd793a9e57a654bed35af4a1d600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000220fd17f1a6f4984444858ed890ca60d60b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b6b175474e89094c44da98b954eedeac495271d0f0001f45f98805a4e8be255a32880fdec7f6728c6568ba0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/src/tests/.pspcache/ddd091cbf567959daf3ad05ab2dd97ce28dcb6d9 b/src/tests/.pspcache/ddd091cbf567959daf3ad05ab2dd97ce28dcb6d9 new file mode 100644 index 0000000..a98a13d --- /dev/null +++ b/src/tests/.pspcache/ddd091cbf567959daf3ad05ab2dd97ce28dcb6d9 @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000001fc4ce94fea638c6c640000000000000000000000000000000000000000000001ed4fde7a2236b0000000000000000000000000000000000000000000000000000000000000000000a400000000000000000000000000000000000000000000000000000000000003a435326910000000000000000000000000000000000000000000000000000000000000002000000000000000000000000084bef12c9931ce12662cc9f2366b6a5029e4bd290000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba00000000000000000000000000000000000000000000001fc4ce94fea638c6c640000000000000000000000000000000000000000000001ed4fde7a2236b000000000000000000000000000000000000000000000000001ed7edd8bb94f404178000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000009abf798f5314bfd793a9e57a654bed35af4a1d60010000000000000000000000000000000000000000000000000000000003138800000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000065baf405634feacd53dd4f00b38278ed7a11604b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000e592427a0aece92de3edee1f18e0157c05861564000000000000000000000000000000000000000000000000000000000000271000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000065c3da24000000000000000000000000000000000000000000000000000000000000002b5f98805a4e8be255a32880fdec7f6728c6568ba00001f46b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/src/tests/.pspcache/e2243940589331811ab9386f31299da274cb39ba b/src/tests/.pspcache/e2243940589331811ab9386f31299da274cb39ba new file mode 100644 index 0000000..c6cc980 --- /dev/null +++ b/src/tests/.pspcache/e2243940589331811ab9386f31299da274cb39ba @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000003cee75c41be75ec0000000000000000000000000000000000000000000000000000000000040fc2ae3000000000000000000000000000000000000000000000000000000000000000440000000000000000000000000000000000000000000000000000000000000464a94e78ef00000000000000000000000000000000000000000000000000000000000000200000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000003cee75c41be75ec0000000000000000000000000000000000000000000000000000000000040fc2ae30000000000000000000000000000000000000000000000000000000042feb02c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001600000000000000000000000009abf798f5314bfd793a9e57a654bed35af4a1d60010000000000000000000000000000000000000000000000000000000003138800000000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000000000000000000000065bacbb5b4bde644bfd74e93a90e9b65915e879b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000baeeb4540f59d30e567a5b563cc0c4587edd936600000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000900000000000000000000000089b78cfa322f6c5de0abceecab66aee45393cc5a000000000000000000000000000000000000000000000000000000000000271000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000a59649758aa4d66e25f08dd01271e891fe521990000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e8d4a51000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/src/tests/.pspcache/e2c45010b23135f7bd9a10341609f62329e91737 b/src/tests/.pspcache/e2c45010b23135f7bd9a10341609f62329e91737 new file mode 100644 index 0000000..15e0f66 --- /dev/null +++ b/src/tests/.pspcache/e2c45010b23135f7bd9a10341609f62329e91737 @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000003643aa6479860400000000000000000000000000000000000000000000000000349de084053d5f239600000000000000000000000000000000000000000000000000000000000000840000000000000000000000000000000000000000000000000000000000000264a6886da900000000000000000000000000000000000000000000000000000000000000200000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba0000000000000000000000000e592427a0aece92de3edee1f18e0157c0586156400000000000000000000000000000000000000000000003643aa6479860400000000000000000000000000000000000000000000000000349de084053d5f23960000000000000000000000000000000000000000000000363e789d378bce49a301000000000000000000000000000000000000000000000000000000000313880000000000000000000000000000000000000000000000000000000065bd264a0000000000000000000000009abf798f5314bfd793a9e57a654bed35af4a1d600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000022091537c91410c48c6845dd2e6fd351b2500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b6b175474e89094c44da98b954eedeac495271d0f0001f45f98805a4e8be255a32880fdec7f6728c6568ba0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/pspcache/eb4af82cce5b603457a2d55eefaceea68829c9a5 b/src/tests/.pspcache/eb4af82cce5b603457a2d55eefaceea68829c9a5 similarity index 100% rename from tests/pspcache/eb4af82cce5b603457a2d55eefaceea68829c9a5 rename to src/tests/.pspcache/eb4af82cce5b603457a2d55eefaceea68829c9a5 diff --git a/tests/pspcache/f23915ac73cd97a3c7ba5d80d9a2665f989b6913 b/src/tests/.pspcache/f23915ac73cd97a3c7ba5d80d9a2665f989b6913 similarity index 100% rename from tests/pspcache/f23915ac73cd97a3c7ba5d80d9a2665f989b6913 rename to src/tests/.pspcache/f23915ac73cd97a3c7ba5d80d9a2665f989b6913 diff --git a/src/tests/.pspcache/f3c54b14ff21ecf96e8cd50e168db3088aded86c b/src/tests/.pspcache/f3c54b14ff21ecf96e8cd50e168db3088aded86c new file mode 100644 index 0000000..f831b30 --- /dev/null +++ b/src/tests/.pspcache/f3c54b14ff21ecf96e8cd50e168db3088aded86c @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000010ece7d2eb4e7de00000000000000000000000000000000000000000000000000000000000120d285b80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000038454e3f31b00000000000000000000000000000000000000000000000000000000000000200000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000010ece7d2eb4e7de00000000000000000000000000000000000000000000000000000000000120d285b80000000000000000000000000000000000000000000000000000000129c147e000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002a0000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009abf798f5314bfd793a9e57a654bed35af4a1d60010000000000000000000000000000000000000000000000000000000003138800000000000000000000000000000000000000000000000000000000000003400000000000000000000000000000000000000000000000000000000065bacc261ba1aac89cb7409681ee3e0894ab81eb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000089b78cfa322f6c5de0abceecab66aee45393cc5a00000000000000000000000000000000000000000000000000000000000000448d7ef9bb000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee570000000000000000000000000000000000000000000000000000000129c147e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004400000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/src/tests/BuyAdapterFuzz.t.sol b/src/tests/BuyAdapterFuzz.t.sol new file mode 100644 index 0000000..907c7fd --- /dev/null +++ b/src/tests/BuyAdapterFuzz.t.sol @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IERC20Detailed} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/IERC20Detailed.sol'; +import {IPoolAddressesProvider} from '@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol'; +import {AaveV3Ethereum, AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol'; +import {IParaSwapAugustusRegistry} from 'src/contracts/dependencies/paraswap/IParaSwapAugustusRegistry.sol'; +import {IBaseParaSwapAdapter} from 'src/contracts/interfaces/IBaseParaSwapAdapter.sol'; +import {IParaSwapAugustusRegistry} from 'src/contracts/dependencies/paraswap/IParaSwapAugustusRegistry.sol'; +import {AugustusRegistry} from 'src/contracts/dependencies/paraswap/AugustusRegistry.sol'; +import {ParaSwapBuyAdapterHarness} from './harness/ParaSwapBuyAdapterHarness.sol'; +import {BaseTest} from './utils/BaseTest.sol'; + +contract BuyAdapterFuzzTest is BaseTest { + ParaSwapBuyAdapterHarness internal buyAdapter; + address[] internal aaveV3EthereumAssets; + + event Bought( + address indexed fromAsset, + address indexed toAsset, + uint256 amountSold, + uint256 receivedAmount + ); + + function setUp() public override { + super.setUp(); + vm.createSelectFork(vm.rpcUrl('mainnet')); + + buyAdapter = new ParaSwapBuyAdapterHarness( + IPoolAddressesProvider(address(AaveV3Ethereum.POOL_ADDRESSES_PROVIDER)), + address(AaveV3Ethereum.POOL), + IParaSwapAugustusRegistry(AugustusRegistry.ETHEREUM) + ); + aaveV3EthereumAssets = [ + AaveV3EthereumAssets.DAI_UNDERLYING, + AaveV3EthereumAssets.LINK_UNDERLYING, + AaveV3EthereumAssets.LUSD_UNDERLYING + ]; + } + + // limiting fuzz runs due to ParaSwap API rate limit + /// forge-config: default.fuzz.runs = 50 + function test_fuzz_buyOnParaSwap( + uint256 fromAssetIndex, + uint256 toAssetIndex, + uint256 amountToBuy, + bool swapAll + ) public { + uint256 totalAssets = aaveV3EthereumAssets.length; + fromAssetIndex = bound(fromAssetIndex, 0, totalAssets - 1); + toAssetIndex = bound(toAssetIndex, 0, totalAssets - 1); + if (fromAssetIndex == toAssetIndex) { + toAssetIndex = (toAssetIndex + 1) % totalAssets; + } + amountToBuy = bound(amountToBuy, 1e15, 4_000 ether); + address assetToSwapFrom = aaveV3EthereumAssets[fromAssetIndex]; + address assetToSwapTo = aaveV3EthereumAssets[toAssetIndex]; + PsPResponse memory psp = _fetchPSPRouteWithoutPspCacheUpdate( + assetToSwapFrom, + assetToSwapTo, + amountToBuy, + user, + false, + swapAll + ); + if (swapAll) { + _checkAmountInParaSwapCalldata(psp.offset, amountToBuy, psp.swapCalldata); + } + deal(assetToSwapFrom, address(buyAdapter), psp.srcAmount); + uint256 buyAdapterAssetFromBalanceBefore = IERC20Detailed(assetToSwapFrom).balanceOf( + address(buyAdapter) + ); + + vm.expectEmit(true, true, false, false, address(buyAdapter)); + emit Bought(assetToSwapFrom, assetToSwapTo, psp.srcAmount, psp.destAmount); + buyAdapter.buyOnParaSwap( + psp.offset, + abi.encode(psp.swapCalldata, psp.augustus), + IERC20Detailed(assetToSwapFrom), + IERC20Detailed(assetToSwapTo), + psp.srcAmount, + amountToBuy + ); + + uint256 buyAdapterAssetFromBalanceAfter = IERC20Detailed(assetToSwapFrom).balanceOf( + address(buyAdapter) + ); + assertGe( + psp.srcAmount, + buyAdapterAssetFromBalanceBefore - buyAdapterAssetFromBalanceAfter, + 'consumed more balance than expected' + ); + assertGt(psp.destAmount, 0, 'route quoted zero destAmount'); + assertGe( + IERC20Detailed(assetToSwapTo).balanceOf(address(buyAdapter)), + amountToBuy, + 'received less amount than quoted' + ); + } +} diff --git a/tests/DebtSwapV2.t.sol b/src/tests/DebtSwapV2.t.sol similarity index 88% rename from tests/DebtSwapV2.t.sol rename to src/tests/DebtSwapV2.t.sol index 0ddb259..e534497 100644 --- a/tests/DebtSwapV2.t.sol +++ b/src/tests/DebtSwapV2.t.sol @@ -3,15 +3,16 @@ pragma solidity ^0.8.0; import {IERC20Detailed} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/IERC20Detailed.sol'; import {IPoolAddressesProvider} from '@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol'; +import {ICreditDelegationToken} from '@aave/core-v3/contracts/interfaces/ICreditDelegationToken.sol'; import {AaveGovernanceV2} from 'aave-address-book/AaveGovernanceV2.sol'; import {Errors} from 'aave-address-book/AaveV2.sol'; import {AaveV2Ethereum, AaveV2EthereumAssets, ILendingPool} from 'aave-address-book/AaveV2Ethereum.sol'; +import {IParaSwapDebtSwapAdapter} from 'src/contracts/interfaces/IParaSwapDebtSwapAdapter.sol'; +import {ParaSwapDebtSwapAdapter} from 'src/contracts/base/ParaSwapDebtSwapAdapter.sol'; +import {ParaSwapDebtSwapAdapterV2} from 'src/contracts/ParaSwapDebtSwapAdapterV2.sol'; +import {IParaSwapAugustusRegistry} from 'src/contracts/dependencies/paraswap/IParaSwapAugustusRegistry.sol'; +import {AugustusRegistry} from 'src/contracts/dependencies/paraswap/AugustusRegistry.sol'; import {BaseTest} from './utils/BaseTest.sol'; -import {ICreditDelegationToken} from '../src/interfaces/ICreditDelegationToken.sol'; -import {IParaswapDebtSwapAdapter} from '../src/interfaces/IParaswapDebtSwapAdapter.sol'; -import {ParaSwapDebtSwapAdapter} from '../src/contracts/ParaSwapDebtSwapAdapter.sol'; -import {ParaSwapDebtSwapAdapterV2} from '../src/contracts/ParaSwapDebtSwapAdapterV2.sol'; -import {AugustusRegistry} from '../src/lib/AugustusRegistry.sol'; contract DebtSwapV2Test is BaseTest { ParaSwapDebtSwapAdapterV2 internal debtSwapAdapter; @@ -23,7 +24,7 @@ contract DebtSwapV2Test is BaseTest { debtSwapAdapter = new ParaSwapDebtSwapAdapterV2( IPoolAddressesProvider(address(AaveV2Ethereum.POOL_ADDRESSES_PROVIDER)), address(AaveV2Ethereum.POOL), - AugustusRegistry.ETHEREUM, + IParaSwapAugustusRegistry(AugustusRegistry.ETHEREUM), AaveGovernanceV2.SHORT_EXECUTOR ); } @@ -99,7 +100,7 @@ contract DebtSwapV2Test is BaseTest { ICreditDelegationToken(newDebtToken).approveDelegation(address(debtSwapAdapter), psp.srcAmount); IERC20Detailed(aToken).approve(address(debtSwapAdapter), supplyAmount); - IParaswapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaswapDebtSwapAdapter + IParaSwapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaSwapDebtSwapAdapter .DebtSwapParams({ debtAsset: debtAsset, debtRepayAmount: type(uint256).max, @@ -112,8 +113,8 @@ contract DebtSwapV2Test is BaseTest { paraswapData: abi.encode(psp.swapCalldata, psp.augustus) }); - IParaswapDebtSwapAdapter.CreditDelegationInput memory cd; - IParaswapDebtSwapAdapter.PermitInput memory collateralATokenPermit; + IParaSwapDebtSwapAdapter.CreditDelegationInput memory cd; + IParaSwapDebtSwapAdapter.PermitInput memory collateralATokenPermit; vm.expectRevert(bytes(Errors.VL_COLLATERAL_CANNOT_COVER_NEW_BORROW)); debtSwapAdapter.swapDebt(debtSwapParams, cd, collateralATokenPermit); @@ -149,7 +150,7 @@ contract DebtSwapV2Test is BaseTest { ICreditDelegationToken(newDebtToken).approveDelegation(address(debtSwapAdapter), psp.srcAmount); - IParaswapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaswapDebtSwapAdapter + IParaSwapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaSwapDebtSwapAdapter .DebtSwapParams({ debtAsset: debtAsset, debtRepayAmount: repayAmount, @@ -164,8 +165,8 @@ contract DebtSwapV2Test is BaseTest { uint256 vDEBT_TOKENBalanceBefore = IERC20Detailed(debtToken).balanceOf(user); - IParaswapDebtSwapAdapter.CreditDelegationInput memory cd; - IParaswapDebtSwapAdapter.PermitInput memory collateralATokenPermit; + IParaSwapDebtSwapAdapter.CreditDelegationInput memory cd; + IParaSwapDebtSwapAdapter.PermitInput memory collateralATokenPermit; debtSwapAdapter.swapDebt(debtSwapParams, cd, collateralATokenPermit); @@ -204,7 +205,7 @@ contract DebtSwapV2Test is BaseTest { ICreditDelegationToken(newDebtToken).approveDelegation(address(debtSwapAdapter), psp.srcAmount); - IParaswapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaswapDebtSwapAdapter + IParaSwapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaSwapDebtSwapAdapter .DebtSwapParams({ debtAsset: debtAsset, debtRepayAmount: type(uint256).max, @@ -217,8 +218,8 @@ contract DebtSwapV2Test is BaseTest { paraswapData: abi.encode(psp.swapCalldata, psp.augustus) }); - IParaswapDebtSwapAdapter.CreditDelegationInput memory cd; - IParaswapDebtSwapAdapter.PermitInput memory collateralATokenPermit; + IParaSwapDebtSwapAdapter.CreditDelegationInput memory cd; + IParaSwapDebtSwapAdapter.PermitInput memory collateralATokenPermit; debtSwapAdapter.swapDebt(debtSwapParams, cd, collateralATokenPermit); @@ -254,7 +255,7 @@ contract DebtSwapV2Test is BaseTest { ICreditDelegationToken(newDebtToken).approveDelegation(address(debtSwapAdapter), psp.srcAmount); - IParaswapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaswapDebtSwapAdapter + IParaSwapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaSwapDebtSwapAdapter .DebtSwapParams({ debtAsset: debtAsset, debtRepayAmount: type(uint256).max, @@ -267,8 +268,8 @@ contract DebtSwapV2Test is BaseTest { paraswapData: abi.encode(psp.swapCalldata, psp.augustus) }); - IParaswapDebtSwapAdapter.CreditDelegationInput memory cd; - IParaswapDebtSwapAdapter.PermitInput memory collateralATokenPermit; + IParaSwapDebtSwapAdapter.CreditDelegationInput memory cd; + IParaSwapDebtSwapAdapter.PermitInput memory collateralATokenPermit; debtSwapAdapter.swapDebt(debtSwapParams, cd, collateralATokenPermit); @@ -326,7 +327,7 @@ contract DebtSwapV2Test is BaseTest { extraCollateralAmount + 1 ); - IParaswapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaswapDebtSwapAdapter + IParaSwapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaSwapDebtSwapAdapter .DebtSwapParams({ debtAsset: debtAsset, debtRepayAmount: type(uint256).max, @@ -339,8 +340,8 @@ contract DebtSwapV2Test is BaseTest { paraswapData: abi.encode(psp.swapCalldata, psp.augustus) }); - IParaswapDebtSwapAdapter.CreditDelegationInput memory cd; - IParaswapDebtSwapAdapter.PermitInput memory collateralATokenPermit; + IParaSwapDebtSwapAdapter.CreditDelegationInput memory cd; + IParaSwapDebtSwapAdapter.PermitInput memory collateralATokenPermit; debtSwapAdapter.swapDebt(debtSwapParams, cd, collateralATokenPermit); } @@ -388,7 +389,7 @@ contract DebtSwapV2Test is BaseTest { ICreditDelegationToken(newDebtToken).approveDelegation(address(debtSwapAdapter), psp.srcAmount); - IParaswapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaswapDebtSwapAdapter + IParaSwapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaSwapDebtSwapAdapter .DebtSwapParams({ debtAsset: debtAsset, debtRepayAmount: type(uint256).max, @@ -401,8 +402,8 @@ contract DebtSwapV2Test is BaseTest { paraswapData: abi.encode(psp.swapCalldata, psp.augustus) }); - IParaswapDebtSwapAdapter.CreditDelegationInput memory cd; - IParaswapDebtSwapAdapter.PermitInput memory collateralATokenPermit = _getPermit( + IParaSwapDebtSwapAdapter.CreditDelegationInput memory cd; + IParaSwapDebtSwapAdapter.PermitInput memory collateralATokenPermit = _getPermit( extraCollateralAToken, address(debtSwapAdapter), extraCollateralAmount + 1 @@ -459,7 +460,7 @@ contract DebtSwapV2Test is BaseTest { extraCollateralAmount + 1 ); - IParaswapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaswapDebtSwapAdapter + IParaSwapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaSwapDebtSwapAdapter .DebtSwapParams({ debtAsset: debtAsset, debtRepayAmount: type(uint256).max, @@ -472,8 +473,8 @@ contract DebtSwapV2Test is BaseTest { paraswapData: abi.encode(psp.swapCalldata, psp.augustus) }); - IParaswapDebtSwapAdapter.CreditDelegationInput memory cd; - IParaswapDebtSwapAdapter.PermitInput memory collateralATokenPermit; + IParaSwapDebtSwapAdapter.CreditDelegationInput memory cd; + IParaSwapDebtSwapAdapter.PermitInput memory collateralATokenPermit; debtSwapAdapter.swapDebt(debtSwapParams, cd, collateralATokenPermit); } diff --git a/tests/DebtSwapV3.t.sol b/src/tests/DebtSwapV3.t.sol similarity index 88% rename from tests/DebtSwapV3.t.sol rename to src/tests/DebtSwapV3.t.sol index bc10416..f361687 100644 --- a/tests/DebtSwapV3.t.sol +++ b/src/tests/DebtSwapV3.t.sol @@ -5,14 +5,15 @@ import {IERC20Detailed} from '@aave/core-v3/contracts/dependencies/openzeppelin/ import {IERC20WithPermit} from 'solidity-utils/contracts/oz-common/interfaces/IERC20WithPermit.sol'; import {IPoolAddressesProvider} from '@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol'; import {Errors} from '@aave/core-v3/contracts/protocol/libraries/helpers/Errors.sol'; +import {ICreditDelegationToken} from '@aave/core-v3/contracts/interfaces/ICreditDelegationToken.sol'; import {AaveGovernanceV2} from 'aave-address-book/AaveGovernanceV2.sol'; import {AaveV3Ethereum, AaveV3EthereumAssets, IPool} from 'aave-address-book/AaveV3Ethereum.sol'; +import {IParaSwapDebtSwapAdapter} from 'src/contracts/interfaces/IParaSwapDebtSwapAdapter.sol'; +import {ParaSwapDebtSwapAdapter} from 'src/contracts/base/ParaSwapDebtSwapAdapter.sol'; +import {ParaSwapDebtSwapAdapterV3} from 'src/contracts/ParaSwapDebtSwapAdapterV3.sol'; +import {IParaSwapAugustusRegistry} from 'src/contracts/dependencies/paraswap/IParaSwapAugustusRegistry.sol'; +import {AugustusRegistry} from 'src/contracts/dependencies/paraswap/AugustusRegistry.sol'; import {BaseTest} from './utils/BaseTest.sol'; -import {ICreditDelegationToken} from '../src/interfaces/ICreditDelegationToken.sol'; -import {IParaswapDebtSwapAdapter} from '../src/interfaces/IParaswapDebtSwapAdapter.sol'; -import {ParaSwapDebtSwapAdapter} from '../src/contracts/ParaSwapDebtSwapAdapter.sol'; -import {ParaSwapDebtSwapAdapterV3} from '../src/contracts/ParaSwapDebtSwapAdapterV3.sol'; -import {AugustusRegistry} from '../src/lib/AugustusRegistry.sol'; import {SigUtils} from './utils/SigUtils.sol'; contract DebtSwapV3Test is BaseTest { @@ -25,7 +26,7 @@ contract DebtSwapV3Test is BaseTest { debtSwapAdapter = new ParaSwapDebtSwapAdapterV3( IPoolAddressesProvider(address(AaveV3Ethereum.POOL_ADDRESSES_PROVIDER)), address(AaveV3Ethereum.POOL), - AugustusRegistry.ETHEREUM, + IParaSwapAugustusRegistry(AugustusRegistry.ETHEREUM), AaveGovernanceV2.SHORT_EXECUTOR ); } @@ -101,7 +102,7 @@ contract DebtSwapV3Test is BaseTest { ICreditDelegationToken(newDebtToken).approveDelegation(address(debtSwapAdapter), psp.srcAmount); IERC20Detailed(aToken).approve(address(debtSwapAdapter), supplyAmount); - IParaswapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaswapDebtSwapAdapter + IParaSwapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaSwapDebtSwapAdapter .DebtSwapParams({ debtAsset: debtAsset, debtRepayAmount: type(uint256).max, @@ -114,8 +115,8 @@ contract DebtSwapV3Test is BaseTest { paraswapData: abi.encode(psp.swapCalldata, psp.augustus) }); - IParaswapDebtSwapAdapter.CreditDelegationInput memory cd; - IParaswapDebtSwapAdapter.PermitInput memory collateralATokenPermit; + IParaSwapDebtSwapAdapter.CreditDelegationInput memory cd; + IParaSwapDebtSwapAdapter.PermitInput memory collateralATokenPermit; vm.expectRevert(bytes(Errors.COLLATERAL_CANNOT_COVER_NEW_BORROW)); debtSwapAdapter.swapDebt(debtSwapParams, cd, collateralATokenPermit); @@ -151,7 +152,7 @@ contract DebtSwapV3Test is BaseTest { ICreditDelegationToken(newDebtToken).approveDelegation(address(debtSwapAdapter), psp.srcAmount); - IParaswapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaswapDebtSwapAdapter + IParaSwapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaSwapDebtSwapAdapter .DebtSwapParams({ debtAsset: debtAsset, debtRepayAmount: repayAmount, @@ -166,8 +167,8 @@ contract DebtSwapV3Test is BaseTest { uint256 vDEBT_TOKENBalanceBefore = IERC20Detailed(debtToken).balanceOf(user); - IParaswapDebtSwapAdapter.CreditDelegationInput memory cd; - IParaswapDebtSwapAdapter.PermitInput memory collateralATokenPermit; + IParaSwapDebtSwapAdapter.CreditDelegationInput memory cd; + IParaSwapDebtSwapAdapter.PermitInput memory collateralATokenPermit; debtSwapAdapter.swapDebt(debtSwapParams, cd, collateralATokenPermit); @@ -206,7 +207,7 @@ contract DebtSwapV3Test is BaseTest { ICreditDelegationToken(newDebtToken).approveDelegation(address(debtSwapAdapter), psp.srcAmount); - IParaswapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaswapDebtSwapAdapter + IParaSwapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaSwapDebtSwapAdapter .DebtSwapParams({ debtAsset: debtAsset, debtRepayAmount: type(uint256).max, @@ -219,8 +220,8 @@ contract DebtSwapV3Test is BaseTest { paraswapData: abi.encode(psp.swapCalldata, psp.augustus) }); - IParaswapDebtSwapAdapter.CreditDelegationInput memory cd; - IParaswapDebtSwapAdapter.PermitInput memory collateralATokenPermit; + IParaSwapDebtSwapAdapter.CreditDelegationInput memory cd; + IParaSwapDebtSwapAdapter.PermitInput memory collateralATokenPermit; debtSwapAdapter.swapDebt(debtSwapParams, cd, collateralATokenPermit); @@ -284,7 +285,7 @@ contract DebtSwapV3Test is BaseTest { ICreditDelegationToken(newDebtToken).approveDelegation(address(debtSwapAdapter), psp.srcAmount); - IParaswapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaswapDebtSwapAdapter + IParaSwapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaSwapDebtSwapAdapter .DebtSwapParams({ debtAsset: debtAsset, debtRepayAmount: type(uint256).max, @@ -297,8 +298,8 @@ contract DebtSwapV3Test is BaseTest { paraswapData: abi.encode(psp.swapCalldata, psp.augustus) }); - IParaswapDebtSwapAdapter.CreditDelegationInput memory cd; - IParaswapDebtSwapAdapter.PermitInput memory collateralATokenPermit; + IParaSwapDebtSwapAdapter.CreditDelegationInput memory cd; + IParaSwapDebtSwapAdapter.PermitInput memory collateralATokenPermit; debtSwapAdapter.swapDebt(debtSwapParams, cd, collateralATokenPermit); @@ -335,7 +336,7 @@ contract DebtSwapV3Test is BaseTest { skip(1 hours); - IParaswapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaswapDebtSwapAdapter + IParaSwapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaSwapDebtSwapAdapter .DebtSwapParams({ debtAsset: debtAsset, debtRepayAmount: type(uint256).max, @@ -348,11 +349,11 @@ contract DebtSwapV3Test is BaseTest { paraswapData: abi.encode(psp.swapCalldata, psp.augustus) }); - IParaswapDebtSwapAdapter.CreditDelegationInput memory cd = _getCDPermit( + IParaSwapDebtSwapAdapter.CreditDelegationInput memory cd = _getCDPermit( psp.srcAmount, newDebtToken ); - IParaswapDebtSwapAdapter.PermitInput memory collateralATokenPermit; + IParaSwapDebtSwapAdapter.PermitInput memory collateralATokenPermit; debtSwapAdapter.swapDebt(debtSwapParams, cd, collateralATokenPermit); @@ -410,7 +411,7 @@ contract DebtSwapV3Test is BaseTest { extraCollateralAmount + 1 ); - IParaswapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaswapDebtSwapAdapter + IParaSwapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaSwapDebtSwapAdapter .DebtSwapParams({ debtAsset: debtAsset, debtRepayAmount: type(uint256).max, @@ -423,8 +424,8 @@ contract DebtSwapV3Test is BaseTest { paraswapData: abi.encode(psp.swapCalldata, psp.augustus) }); - IParaswapDebtSwapAdapter.CreditDelegationInput memory cd; - IParaswapDebtSwapAdapter.PermitInput memory collateralATokenPermit; + IParaSwapDebtSwapAdapter.CreditDelegationInput memory cd; + IParaSwapDebtSwapAdapter.PermitInput memory collateralATokenPermit; debtSwapAdapter.swapDebt(debtSwapParams, cd, collateralATokenPermit); } @@ -472,7 +473,7 @@ contract DebtSwapV3Test is BaseTest { ICreditDelegationToken(newDebtToken).approveDelegation(address(debtSwapAdapter), psp.srcAmount); - IParaswapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaswapDebtSwapAdapter + IParaSwapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaSwapDebtSwapAdapter .DebtSwapParams({ debtAsset: debtAsset, debtRepayAmount: type(uint256).max, @@ -485,8 +486,8 @@ contract DebtSwapV3Test is BaseTest { paraswapData: abi.encode(psp.swapCalldata, psp.augustus) }); - IParaswapDebtSwapAdapter.CreditDelegationInput memory cd; - IParaswapDebtSwapAdapter.PermitInput memory collateralATokenPermit = _getPermit( + IParaSwapDebtSwapAdapter.CreditDelegationInput memory cd; + IParaSwapDebtSwapAdapter.PermitInput memory collateralATokenPermit = _getPermit( extraCollateralAToken, address(debtSwapAdapter), extraCollateralAmount + 1 @@ -543,7 +544,7 @@ contract DebtSwapV3Test is BaseTest { extraCollateralAmount + 1 ); - IParaswapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaswapDebtSwapAdapter + IParaSwapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaSwapDebtSwapAdapter .DebtSwapParams({ debtAsset: debtAsset, debtRepayAmount: type(uint256).max, @@ -556,8 +557,8 @@ contract DebtSwapV3Test is BaseTest { paraswapData: abi.encode(psp.swapCalldata, psp.augustus) }); - IParaswapDebtSwapAdapter.CreditDelegationInput memory cd; - IParaswapDebtSwapAdapter.PermitInput memory collateralATokenPermit; + IParaSwapDebtSwapAdapter.CreditDelegationInput memory cd; + IParaSwapDebtSwapAdapter.PermitInput memory collateralATokenPermit; debtSwapAdapter.swapDebt(debtSwapParams, cd, collateralATokenPermit); } @@ -565,7 +566,7 @@ contract DebtSwapV3Test is BaseTest { function _getCDPermit( uint256 amount, address debtToken - ) internal view returns (IParaswapDebtSwapAdapter.CreditDelegationInput memory) { + ) internal view returns (IParaSwapDebtSwapAdapter.CreditDelegationInput memory) { IERC20WithPermit token = IERC20WithPermit(debtToken); SigUtils.CreditDelegation memory creditDelegation = SigUtils.CreditDelegation({ delegatee: address(debtSwapAdapter), @@ -582,7 +583,7 @@ contract DebtSwapV3Test is BaseTest { (uint8 v, bytes32 r, bytes32 s) = vm.sign(userPrivateKey, digest); return - IParaswapDebtSwapAdapter.CreditDelegationInput({ + IParaSwapDebtSwapAdapter.CreditDelegationInput({ debtToken: ICreditDelegationToken(address(token)), value: amount, deadline: type(uint256).max, diff --git a/tests/DebtSwapV3GHO.t.sol b/src/tests/DebtSwapV3GHO.t.sol similarity index 87% rename from tests/DebtSwapV3GHO.t.sol rename to src/tests/DebtSwapV3GHO.t.sol index e634d11..2acc397 100644 --- a/tests/DebtSwapV3GHO.t.sol +++ b/src/tests/DebtSwapV3GHO.t.sol @@ -2,17 +2,18 @@ pragma solidity ^0.8.0; import {IERC20Detailed} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/IERC20Detailed.sol'; -import {IERC20WithPermit} from 'solidity-utils/contracts/oz-common/interfaces/IERC20WithPermit.sol'; -import {IPoolAddressesProvider} from '@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol'; import {Errors} from '@aave/core-v3/contracts/protocol/libraries/helpers/Errors.sol'; +import {IPoolAddressesProvider} from '@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol'; +import {ICreditDelegationToken} from '@aave/core-v3/contracts/interfaces/ICreditDelegationToken.sol'; +import {IERC20WithPermit} from 'solidity-utils/contracts/oz-common/interfaces/IERC20WithPermit.sol'; import {AaveGovernanceV2} from 'aave-address-book/AaveGovernanceV2.sol'; import {AaveV3Ethereum, AaveV3EthereumAssets, IPool} from 'aave-address-book/AaveV3Ethereum.sol'; +import {IParaSwapDebtSwapAdapter} from 'src/contracts/interfaces/IParaSwapDebtSwapAdapter.sol'; +import {ParaSwapDebtSwapAdapter} from 'src/contracts/base/ParaSwapDebtSwapAdapter.sol'; +import {ParaSwapDebtSwapAdapterV3GHO} from 'src/contracts/ParaSwapDebtSwapAdapterV3GHO.sol'; +import {IParaSwapAugustusRegistry} from 'src/contracts/dependencies/paraswap/IParaSwapAugustusRegistry.sol'; +import {AugustusRegistry} from 'src/contracts/dependencies/paraswap/AugustusRegistry.sol'; import {BaseTest} from './utils/BaseTest.sol'; -import {ICreditDelegationToken} from '../src/interfaces/ICreditDelegationToken.sol'; -import {IParaswapDebtSwapAdapter} from '../src/interfaces/IParaswapDebtSwapAdapter.sol'; -import {ParaSwapDebtSwapAdapter} from '../src/contracts/ParaSwapDebtSwapAdapter.sol'; -import {ParaSwapDebtSwapAdapterV3GHO} from '../src/contracts/ParaSwapDebtSwapAdapterV3GHO.sol'; -import {AugustusRegistry} from '../src/lib/AugustusRegistry.sol'; import {SigUtils} from './utils/SigUtils.sol'; contract DebtSwapV3GHOTest is BaseTest { @@ -25,7 +26,7 @@ contract DebtSwapV3GHOTest is BaseTest { debtSwapAdapter = new ParaSwapDebtSwapAdapterV3GHO( IPoolAddressesProvider(address(AaveV3Ethereum.POOL_ADDRESSES_PROVIDER)), address(AaveV3Ethereum.POOL), - AugustusRegistry.ETHEREUM, + IParaSwapAugustusRegistry(AugustusRegistry.ETHEREUM), AaveGovernanceV2.SHORT_EXECUTOR ); } @@ -72,7 +73,7 @@ contract DebtSwapV3GHOTest is BaseTest { ICreditDelegationToken(newDebtToken).approveDelegation(address(debtSwapAdapter), psp.srcAmount); IERC20Detailed(aToken).approve(address(debtSwapAdapter), supplyAmount); - IParaswapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaswapDebtSwapAdapter + IParaSwapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaSwapDebtSwapAdapter .DebtSwapParams({ debtAsset: debtAsset, debtRepayAmount: type(uint256).max, @@ -85,8 +86,8 @@ contract DebtSwapV3GHOTest is BaseTest { paraswapData: abi.encode(psp.swapCalldata, psp.augustus) }); - IParaswapDebtSwapAdapter.CreditDelegationInput memory cd; - IParaswapDebtSwapAdapter.PermitInput memory collateralATokenPermit; + IParaSwapDebtSwapAdapter.CreditDelegationInput memory cd; + IParaSwapDebtSwapAdapter.PermitInput memory collateralATokenPermit; vm.expectRevert(bytes(Errors.COLLATERAL_CANNOT_COVER_NEW_BORROW)); debtSwapAdapter.swapDebt(debtSwapParams, cd, collateralATokenPermit); @@ -141,7 +142,7 @@ contract DebtSwapV3GHOTest is BaseTest { ICreditDelegationToken(newDebtToken).approveDelegation(address(debtSwapAdapter), psp.srcAmount); - IParaswapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaswapDebtSwapAdapter + IParaSwapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaSwapDebtSwapAdapter .DebtSwapParams({ debtAsset: debtAsset, debtRepayAmount: repayAmount, @@ -156,8 +157,8 @@ contract DebtSwapV3GHOTest is BaseTest { uint256 vDEBT_TOKENBalanceBefore = IERC20Detailed(debtToken).balanceOf(user); - IParaswapDebtSwapAdapter.CreditDelegationInput memory cd; - IParaswapDebtSwapAdapter.PermitInput memory collateralATokenPermit; + IParaSwapDebtSwapAdapter.CreditDelegationInput memory cd; + IParaSwapDebtSwapAdapter.PermitInput memory collateralATokenPermit; debtSwapAdapter.swapDebt(debtSwapParams, cd, collateralATokenPermit); uint256 vDEBT_TOKENBalanceAfter = IERC20Detailed(debtToken).balanceOf(user); @@ -195,7 +196,7 @@ contract DebtSwapV3GHOTest is BaseTest { ICreditDelegationToken(newDebtToken).approveDelegation(address(debtSwapAdapter), psp.srcAmount); - IParaswapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaswapDebtSwapAdapter + IParaSwapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaSwapDebtSwapAdapter .DebtSwapParams({ debtAsset: debtAsset, debtRepayAmount: type(uint256).max, @@ -208,8 +209,8 @@ contract DebtSwapV3GHOTest is BaseTest { paraswapData: abi.encode(psp.swapCalldata, psp.augustus) }); - IParaswapDebtSwapAdapter.CreditDelegationInput memory cd; - IParaswapDebtSwapAdapter.PermitInput memory collateralATokenPermit; + IParaSwapDebtSwapAdapter.CreditDelegationInput memory cd; + IParaSwapDebtSwapAdapter.PermitInput memory collateralATokenPermit; debtSwapAdapter.swapDebt(debtSwapParams, cd, collateralATokenPermit); @@ -264,7 +265,7 @@ contract DebtSwapV3GHOTest is BaseTest { ICreditDelegationToken(newDebtToken).approveDelegation(address(debtSwapAdapter), psp.srcAmount); IERC20Detailed(extraCollateralAToken).approve(address(debtSwapAdapter), extraCollateralAmount); - IParaswapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaswapDebtSwapAdapter + IParaSwapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaSwapDebtSwapAdapter .DebtSwapParams({ debtAsset: debtAsset, debtRepayAmount: type(uint256).max, @@ -277,8 +278,8 @@ contract DebtSwapV3GHOTest is BaseTest { paraswapData: abi.encode(psp.swapCalldata, psp.augustus) }); - IParaswapDebtSwapAdapter.CreditDelegationInput memory cd; - IParaswapDebtSwapAdapter.PermitInput memory collateralATokenPermit; + IParaSwapDebtSwapAdapter.CreditDelegationInput memory cd; + IParaSwapDebtSwapAdapter.PermitInput memory collateralATokenPermit; debtSwapAdapter.swapDebt(debtSwapParams, cd, collateralATokenPermit); } @@ -326,7 +327,7 @@ contract DebtSwapV3GHOTest is BaseTest { ICreditDelegationToken(newDebtToken).approveDelegation(address(debtSwapAdapter), psp.srcAmount); - IParaswapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaswapDebtSwapAdapter + IParaSwapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaSwapDebtSwapAdapter .DebtSwapParams({ debtAsset: debtAsset, debtRepayAmount: type(uint256).max, @@ -339,8 +340,8 @@ contract DebtSwapV3GHOTest is BaseTest { paraswapData: abi.encode(psp.swapCalldata, psp.augustus) }); - IParaswapDebtSwapAdapter.CreditDelegationInput memory cd; - IParaswapDebtSwapAdapter.PermitInput memory collateralATokenPermit = _getPermit( + IParaSwapDebtSwapAdapter.CreditDelegationInput memory cd; + IParaSwapDebtSwapAdapter.PermitInput memory collateralATokenPermit = _getPermit( extraCollateralAToken, address(debtSwapAdapter), extraCollateralAmount + 1 @@ -352,7 +353,7 @@ contract DebtSwapV3GHOTest is BaseTest { function _getCDPermit( uint256 amount, address debtToken - ) internal view returns (IParaswapDebtSwapAdapter.CreditDelegationInput memory) { + ) internal view returns (IParaSwapDebtSwapAdapter.CreditDelegationInput memory) { IERC20WithPermit token = IERC20WithPermit(debtToken); SigUtils.CreditDelegation memory creditDelegation = SigUtils.CreditDelegation({ delegatee: address(debtSwapAdapter), @@ -369,7 +370,7 @@ contract DebtSwapV3GHOTest is BaseTest { (uint8 v, bytes32 r, bytes32 s) = vm.sign(userPrivateKey, digest); return - IParaswapDebtSwapAdapter.CreditDelegationInput({ + IParaSwapDebtSwapAdapter.CreditDelegationInput({ debtToken: ICreditDelegationToken(address(token)), value: amount, deadline: type(uint256).max, diff --git a/src/tests/LiquiditySwapV2.t.sol b/src/tests/LiquiditySwapV2.t.sol new file mode 100644 index 0000000..1e9e741 --- /dev/null +++ b/src/tests/LiquiditySwapV2.t.sol @@ -0,0 +1,660 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IERC20Detailed} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/IERC20Detailed.sol'; +import {IPoolAddressesProvider} from '@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol'; +import {AaveGovernanceV2} from 'aave-address-book/AaveGovernanceV2.sol'; +import {Errors} from 'aave-address-book/AaveV2.sol'; +import {AaveV2Ethereum, AaveV2EthereumAssets, ILendingPool} from 'aave-address-book/AaveV2Ethereum.sol'; +import {ParaSwapLiquiditySwapAdapterV2} from 'src/contracts/ParaSwapLiquiditySwapAdapterV2.sol'; +import {IParaSwapAugustusRegistry} from 'src/contracts/dependencies/paraswap/IParaSwapAugustusRegistry.sol'; +import {IParaSwapLiquiditySwapAdapter} from 'src/contracts/interfaces/IParaSwapLiquiditySwapAdapter.sol'; +import {AugustusRegistry} from 'src/contracts/dependencies/paraswap/AugustusRegistry.sol'; +import {BaseTest} from './utils/BaseTest.sol'; + +contract LiquiditySwapAdapterV2Test is BaseTest { + ParaSwapLiquiditySwapAdapterV2 internal liquiditySwapAdapter; + + function setUp() public override { + super.setUp(); + vm.createSelectFork(vm.rpcUrl('mainnet'), 19125717); + + liquiditySwapAdapter = new ParaSwapLiquiditySwapAdapterV2( + IPoolAddressesProvider(address(AaveV2Ethereum.POOL_ADDRESSES_PROVIDER)), + address(AaveV2Ethereum.POOL), + IParaSwapAugustusRegistry(AugustusRegistry.ETHEREUM), + AaveGovernanceV2.SHORT_EXECUTOR + ); + vm.stopPrank(); + } + + function test_revert_executeOperation_not_pool() public { + address[] memory mockAddresses = new address[](0); + uint256[] memory mockAmounts = new uint256[](0); + + vm.expectRevert(bytes('CALLER_MUST_BE_POOL')); + liquiditySwapAdapter.executeOperation( + mockAddresses, + mockAmounts, + mockAmounts, + address(0), + abi.encode('') + ); + } + + function test_revert_executeOperation_wrong_initiator() public { + vm.prank(address(AaveV2Ethereum.POOL)); + address[] memory mockAddresses = new address[](0); + uint256[] memory mockAmounts = new uint256[](0); + + vm.expectRevert(bytes('INITIATOR_MUST_BE_THIS')); + liquiditySwapAdapter.executeOperation( + mockAddresses, + mockAmounts, + mockAmounts, + address(0), + abi.encode('') + ); + } + + function test_revert_liquiditySwap_without_extra_collateral() public { + address collateralAssetAToken = AaveV2EthereumAssets.DAI_A_TOKEN; + address collateralAsset = AaveV2EthereumAssets.DAI_UNDERLYING; + address newCollateralAsset = AaveV2EthereumAssets.LUSD_UNDERLYING; + + uint256 supplyAmount = 120 ether; + uint256 borrowAmount = 80 ether; + + // We want to end with LT > utilisation > LTV, so we pump up the utilisation to 75% by withdrawing (80 > 75 > 67). + uint256 withdrawAmount = supplyAmount - (borrowAmount * 100) / 75; + + vm.startPrank(user); + + _supply(AaveV2Ethereum.POOL, supplyAmount, collateralAsset); + _borrow(AaveV2Ethereum.POOL, borrowAmount, collateralAsset); + + _withdraw(AaveV2Ethereum.POOL, withdrawAmount, collateralAsset); + + vm.expectRevert(bytes(Errors.VL_TRANSFER_NOT_ALLOWED)); + _withdraw(AaveV2Ethereum.POOL, 25 ether, collateralAsset); + + // Swap liquidity(collateral) + uint256 collateralAmountToSwap = 25 ether; + uint256 expectedAmount = 20 ether; + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + newCollateralAsset, + collateralAmountToSwap, + user, + true, + false + ); + + IERC20Detailed(collateralAssetAToken).approve( + address(liquiditySwapAdapter), + collateralAmountToSwap + ); + + IParaSwapLiquiditySwapAdapter.LiquiditySwapParams + memory liquiditySwapParams = IParaSwapLiquiditySwapAdapter.LiquiditySwapParams({ + collateralAsset: collateralAsset, + collateralAmountToSwap: collateralAmountToSwap, + newCollateralAsset: newCollateralAsset, + newCollateralAmount: expectedAmount, + offset: psp.offset, + user: user, + withFlashLoan: false, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IParaSwapLiquiditySwapAdapter.PermitInput memory collateralATokenPermit; + + vm.expectRevert(bytes(Errors.VL_TRANSFER_NOT_ALLOWED)); + liquiditySwapAdapter.swapLiquidity(liquiditySwapParams, collateralATokenPermit); + } + + function test_revert_due_to_insufficient_amount() public { + uint256 supplyAmount = 12_000 ether; + uint256 borrowAmount = 1_000 ether; + + address collateralAsset = AaveV2EthereumAssets.DAI_UNDERLYING; + address collateralAssetAToken = AaveV2EthereumAssets.DAI_A_TOKEN; + address newCollateralAsset = AaveV2EthereumAssets.LUSD_UNDERLYING; + + vm.startPrank(user); + _supply(AaveV2Ethereum.POOL, supplyAmount, collateralAsset); + _borrow(AaveV2Ethereum.POOL, borrowAmount, collateralAsset); + + skip(1 hours); + + uint256 collateralAmountToSwap = 11_999 ether; + uint256 expectedAmount = 11_800 ether; + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + newCollateralAsset, + collateralAmountToSwap, + user, + true, + true + ); + + IERC20Detailed(collateralAssetAToken).approve( + address(liquiditySwapAdapter), + collateralAmountToSwap + ); + + IParaSwapLiquiditySwapAdapter.LiquiditySwapParams + memory liquiditySwapParams = IParaSwapLiquiditySwapAdapter.LiquiditySwapParams({ + collateralAsset: collateralAsset, + collateralAmountToSwap: collateralAmountToSwap, + newCollateralAsset: newCollateralAsset, + newCollateralAmount: expectedAmount, + offset: psp.offset, + user: user, + withFlashLoan: false, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IParaSwapLiquiditySwapAdapter.PermitInput memory collateralATokenPermit; + + vm.expectRevert(bytes('INSUFFICIENT_AMOUNT_TO_SWAP')); + liquiditySwapAdapter.swapLiquidity(liquiditySwapParams, collateralATokenPermit); + } + + function test_revert_wrong_paraswap_route() public { + uint256 supplyAmount = 12_000 ether; + uint256 borrowAmount = 9_000 ether; + + address collateralAsset = AaveV2EthereumAssets.DAI_UNDERLYING; + address collateralAssetAToken = AaveV2EthereumAssets.DAI_A_TOKEN; + address newCollateralAsset = AaveV2EthereumAssets.LUSD_UNDERLYING; + + vm.startPrank(user); + _supply(AaveV2Ethereum.POOL, supplyAmount, collateralAsset); + _borrow(AaveV2Ethereum.POOL, borrowAmount, collateralAsset); + + skip(1 hours); + + uint256 collateralAmountToSwap = 250 ether; + uint256 expectedAmount = 220 ether; + // generating the paraswap route for half of collateralAmountToSwap and executing swap with collateralAmountToSwap + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + newCollateralAsset, + collateralAmountToSwap / 2, + user, + true, + false + ); + + IERC20Detailed(collateralAssetAToken).approve( + address(liquiditySwapAdapter), + collateralAmountToSwap + ); + + IParaSwapLiquiditySwapAdapter.LiquiditySwapParams + memory liquiditySwapParams = IParaSwapLiquiditySwapAdapter.LiquiditySwapParams({ + collateralAsset: collateralAsset, + collateralAmountToSwap: collateralAmountToSwap, + newCollateralAsset: newCollateralAsset, + newCollateralAmount: expectedAmount, + offset: psp.offset, + user: user, + withFlashLoan: false, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IParaSwapLiquiditySwapAdapter.PermitInput memory collateralATokenPermit; + + vm.expectRevert(bytes('WRONG_BALANCE_AFTER_SWAP')); + liquiditySwapAdapter.swapLiquidity(liquiditySwapParams, collateralATokenPermit); + } + + function test_liquiditySwap_without_extra_collateral() public { + address collateralAssetAToken = AaveV2EthereumAssets.DAI_A_TOKEN; + address collateralAsset = AaveV2EthereumAssets.DAI_UNDERLYING; + address newCollateralAsset = AaveV2EthereumAssets.LUSD_UNDERLYING; + address newCollateralAssetAToken = AaveV2EthereumAssets.LUSD_A_TOKEN; + + uint256 supplyAmount = 12000 ether; + uint256 borrowAmount = 1000 ether; + + vm.startPrank(user); + + _supply(AaveV2Ethereum.POOL, supplyAmount, collateralAsset); + _borrow(AaveV2Ethereum.POOL, borrowAmount, collateralAsset); + + uint256 oldCollateralAssetATokenBalanceBefore = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + uint256 newCollateralAssetATokenBalanceBefore = IERC20Detailed(newCollateralAssetAToken) + .balanceOf(user); + + uint256 collateralAmountToSwap = 3000 ether; + uint256 expectedAmount = 2800 ether; + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + newCollateralAsset, + collateralAmountToSwap, + user, + true, + false + ); + + IERC20Detailed(collateralAssetAToken).approve( + address(liquiditySwapAdapter), + collateralAmountToSwap + ); + + IParaSwapLiquiditySwapAdapter.LiquiditySwapParams + memory liquiditySwapParams = IParaSwapLiquiditySwapAdapter.LiquiditySwapParams({ + collateralAsset: collateralAsset, + collateralAmountToSwap: collateralAmountToSwap, + newCollateralAsset: newCollateralAsset, + newCollateralAmount: expectedAmount, + offset: psp.offset, + user: user, + withFlashLoan: false, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IParaSwapLiquiditySwapAdapter.PermitInput memory collateralATokenPermit; + + liquiditySwapAdapter.swapLiquidity(liquiditySwapParams, collateralATokenPermit); + + _invariant(address(liquiditySwapAdapter), collateralAsset, newCollateralAsset); + uint256 oldCollateralAssetATokenBalanceAfter = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + uint256 newCollateralAssetATokenBalanceAfter = IERC20Detailed(newCollateralAssetAToken) + .balanceOf(user); + assertGt( + newCollateralAssetATokenBalanceAfter - newCollateralAssetATokenBalanceBefore, + expectedAmount, + 'invalid amount received' + ); + assertTrue( + _withinRange( + oldCollateralAssetATokenBalanceBefore - oldCollateralAssetATokenBalanceAfter, + collateralAmountToSwap, + 2 + ) + ); + } + + function test_liquiditySwap_permit_without_extra_collateral() public { + address collateralAssetAToken = AaveV2EthereumAssets.DAI_A_TOKEN; + address collateralAsset = AaveV2EthereumAssets.DAI_UNDERLYING; + address newCollateralAsset = AaveV2EthereumAssets.LUSD_UNDERLYING; + address newCollateralAssetAToken = AaveV2EthereumAssets.LUSD_A_TOKEN; + + uint256 supplyAmount = 10_000 ether; + uint256 borrowAmount = 1000 ether; + + vm.startPrank(user); + + _supply(AaveV2Ethereum.POOL, supplyAmount, collateralAsset); + _borrow(AaveV2Ethereum.POOL, borrowAmount, collateralAsset); + + uint256 oldCollateralAssetATokenBalanceBefore = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + uint256 newCollateralAssetATokenBalanceBefore = IERC20Detailed(newCollateralAssetAToken) + .balanceOf(user); + + uint256 collateralAmountToSwap = 5000 ether; + uint256 expectedAmount = 4800 ether; + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + newCollateralAsset, + collateralAmountToSwap, + user, + true, + false + ); + + IParaSwapLiquiditySwapAdapter.LiquiditySwapParams + memory liquiditySwapParams = IParaSwapLiquiditySwapAdapter.LiquiditySwapParams({ + collateralAsset: collateralAsset, + collateralAmountToSwap: collateralAmountToSwap, + newCollateralAsset: newCollateralAsset, + newCollateralAmount: expectedAmount, + offset: psp.offset, + user: user, + withFlashLoan: false, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IParaSwapLiquiditySwapAdapter.PermitInput memory collateralATokenPermit = _getPermit( + collateralAssetAToken, + address(liquiditySwapAdapter), + collateralAmountToSwap + ); + + liquiditySwapAdapter.swapLiquidity(liquiditySwapParams, collateralATokenPermit); + + _invariant(address(liquiditySwapAdapter), collateralAsset, newCollateralAsset); + uint256 oldCollateralAssetATokenBalanceAfter = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + uint256 newCollateralAssetATokenBalanceAfter = IERC20Detailed(newCollateralAssetAToken) + .balanceOf(user); + assertTrue( + _withinRange( + oldCollateralAssetATokenBalanceBefore - oldCollateralAssetATokenBalanceAfter, + collateralAmountToSwap, + 2 + ) + ); + assertGt( + newCollateralAssetATokenBalanceAfter - newCollateralAssetATokenBalanceBefore, + expectedAmount, + 'invalid amount received' + ); + } + + function test_revert_liquiditySwap_wrong_permit() public { + address collateralAssetAToken = AaveV2EthereumAssets.DAI_A_TOKEN; + address collateralAsset = AaveV2EthereumAssets.DAI_UNDERLYING; + address newCollateralAsset = AaveV2EthereumAssets.LUSD_UNDERLYING; + + uint256 supplyAmount = 10_000 ether; + uint256 borrowAmount = 1000 ether; + + vm.startPrank(user); + + _supply(AaveV2Ethereum.POOL, supplyAmount, collateralAsset); + _borrow(AaveV2Ethereum.POOL, borrowAmount, collateralAsset); + + uint256 collateralAmountToSwap = 5000 ether; + uint256 expectedAmount = 4800 ether; + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + newCollateralAsset, + collateralAmountToSwap, + user, + true, + false + ); + + IParaSwapLiquiditySwapAdapter.LiquiditySwapParams + memory liquiditySwapParams = IParaSwapLiquiditySwapAdapter.LiquiditySwapParams({ + collateralAsset: collateralAsset, + collateralAmountToSwap: collateralAmountToSwap, + newCollateralAsset: newCollateralAsset, + newCollateralAmount: expectedAmount, + offset: psp.offset, + user: user, + withFlashLoan: false, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IParaSwapLiquiditySwapAdapter.PermitInput memory collateralATokenPermit = _getPermit( + collateralAssetAToken, + address(liquiditySwapAdapter), + collateralAmountToSwap - 1 + ); + + vm.expectRevert(bytes('ERC20: transfer amount exceeds allowance')); + liquiditySwapAdapter.swapLiquidity(liquiditySwapParams, collateralATokenPermit); + } + + function test_liquiditySwapFull_without_extra_collateral() public { + uint256 supplyAmount = 15_000 ether; + uint256 borrowAmount = 1000 ether; + + address anotherCollateralAsset = AaveV2EthereumAssets.USDC_UNDERLYING; + address collateralAssetAToken = AaveV2EthereumAssets.DAI_A_TOKEN; + address collateralAsset = AaveV2EthereumAssets.DAI_UNDERLYING; + address newCollateralAsset = AaveV2EthereumAssets.LUSD_UNDERLYING; + address newCollateralAssetAToken = AaveV2EthereumAssets.LUSD_A_TOKEN; + + vm.startPrank(user); + + _supply(AaveV2Ethereum.POOL, supplyAmount, collateralAsset); + //supplying extra collateral so that all dai collateral can be swapped without flashloan + _supply(AaveV2Ethereum.POOL, 15000e6, anotherCollateralAsset); + _borrow(AaveV2Ethereum.POOL, borrowAmount, collateralAsset); + + uint256 newCollateralAssetATokenBalanceBefore = IERC20Detailed(newCollateralAssetAToken) + .balanceOf(user); + + uint256 collateralAmountToSwap = 15_000 ether; // equals to supplyAmount + uint256 expectedAmount = 14_800 ether; + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + newCollateralAsset, + collateralAmountToSwap, + user, + true, + true + ); + + IERC20Detailed(collateralAssetAToken).approve( + address(liquiditySwapAdapter), + collateralAmountToSwap + ); + + IParaSwapLiquiditySwapAdapter.LiquiditySwapParams + memory liquiditySwapParams = IParaSwapLiquiditySwapAdapter.LiquiditySwapParams({ + collateralAsset: collateralAsset, + collateralAmountToSwap: collateralAmountToSwap, + newCollateralAsset: newCollateralAsset, + newCollateralAmount: expectedAmount, + offset: psp.offset, + user: user, + withFlashLoan: false, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IParaSwapLiquiditySwapAdapter.PermitInput memory collateralATokenPermit; + + liquiditySwapAdapter.swapLiquidity(liquiditySwapParams, collateralATokenPermit); + + uint256 oldCollateralAssetATokenBalanceAfter = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + uint256 newCollateralAssetATokenBalanceAfter = IERC20Detailed(newCollateralAssetAToken) + .balanceOf(user); + assertEq(oldCollateralAssetATokenBalanceAfter, 0); + _invariant(address(liquiditySwapAdapter), newCollateralAsset, collateralAsset); + assertGt( + newCollateralAssetATokenBalanceAfter - newCollateralAssetATokenBalanceBefore, + expectedAmount, + 'invalid amount received' + ); + } + + function test_liquiditySwap_half_with_flashloan() public { + uint256 supplyAmount = 18_000 ether; + uint256 borrowAmount = 12_000 ether; + + address collateralAssetAToken = AaveV2EthereumAssets.DAI_A_TOKEN; + address collateralAsset = AaveV2EthereumAssets.DAI_UNDERLYING; + address newCollateralAsset = AaveV2EthereumAssets.USDC_UNDERLYING; + address newCollateralAssetAToken = AaveV2EthereumAssets.USDC_A_TOKEN; + vm.startPrank(user); + + _supply(AaveV2Ethereum.POOL, supplyAmount, collateralAsset); + _borrow(AaveV2Ethereum.POOL, borrowAmount, collateralAsset); + + uint256 oldCollateralAssetATokenBalanceBefore = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + uint256 newCollateralAssetATokenBalanceBefore = IERC20Detailed(newCollateralAssetAToken) + .balanceOf(user); + + uint256 collateralAmountToSwap = 5000 ether; + uint256 expectedAmount = 4800e6; + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + newCollateralAsset, + (collateralAmountToSwap * 9991) / 10_000, //taking flashloan premium(0.09%) into account + user, + true, + false + ); + + IERC20Detailed(collateralAssetAToken).approve( + address(liquiditySwapAdapter), + collateralAmountToSwap + ); + + IParaSwapLiquiditySwapAdapter.LiquiditySwapParams + memory liquiditySwapParams = IParaSwapLiquiditySwapAdapter.LiquiditySwapParams({ + collateralAsset: collateralAsset, + collateralAmountToSwap: collateralAmountToSwap, + newCollateralAsset: newCollateralAsset, + newCollateralAmount: expectedAmount, + offset: psp.offset, + user: user, + withFlashLoan: true, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IParaSwapLiquiditySwapAdapter.PermitInput memory collateralATokenPermit; + + liquiditySwapAdapter.swapLiquidity(liquiditySwapParams, collateralATokenPermit); + uint256 oldCollateralAssetATokenBalanceAfter = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + uint256 newCollateralAssetATokenBalanceAfter = IERC20Detailed(newCollateralAssetAToken) + .balanceOf(user); + assertTrue( + _withinRange( + oldCollateralAssetATokenBalanceBefore - oldCollateralAssetATokenBalanceAfter, + collateralAmountToSwap, + 1e15 //flashloan premium is added to the pool and indexes are updated. So, relaxed this condition + ) + ); + assertGt( + newCollateralAssetATokenBalanceAfter - newCollateralAssetATokenBalanceBefore, + expectedAmount, + 'invalid amount received' + ); + _invariant(address(liquiditySwapAdapter), collateralAsset, newCollateralAsset); + } + + function test_revert_liquiditySwap_half_without_flashloan() public { + uint256 supplyAmount = 18_000 ether; + uint256 borrowAmount = 12_000 ether; + + address collateralAssetAToken = AaveV2EthereumAssets.DAI_A_TOKEN; + address collateralAsset = AaveV2EthereumAssets.DAI_UNDERLYING; + address newCollateralAsset = AaveV2EthereumAssets.USDC_UNDERLYING; + + vm.startPrank(user); + + _supply(AaveV2Ethereum.POOL, supplyAmount, collateralAsset); + _borrow(AaveV2Ethereum.POOL, borrowAmount, collateralAsset); + + uint256 collateralAmountToSwap = 4500 ether; + uint256 expectedAmount = 4000e6; + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + newCollateralAsset, + collateralAmountToSwap, + user, + true, + false + ); + + IERC20Detailed(collateralAssetAToken).approve( + address(liquiditySwapAdapter), + collateralAmountToSwap + ); + + IParaSwapLiquiditySwapAdapter.LiquiditySwapParams + memory liquiditySwapParams = IParaSwapLiquiditySwapAdapter.LiquiditySwapParams({ + collateralAsset: collateralAsset, + collateralAmountToSwap: collateralAmountToSwap, + newCollateralAsset: newCollateralAsset, + newCollateralAmount: expectedAmount, + offset: psp.offset, + user: user, + withFlashLoan: false, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IParaSwapLiquiditySwapAdapter.PermitInput memory collateralATokenPermit; + + vm.expectRevert(bytes(Errors.VL_TRANSFER_NOT_ALLOWED)); + liquiditySwapAdapter.swapLiquidity(liquiditySwapParams, collateralATokenPermit); + + _invariant(address(liquiditySwapAdapter), collateralAsset, newCollateralAsset); + } + + function test_liquiditySwap_full_with_flashloan_and_permit() public { + uint256 supplyAmount = 18_000 ether; + uint256 borrowAmount = 10_000 ether; + + address collateralAssetAToken = AaveV2EthereumAssets.DAI_A_TOKEN; + address collateralAsset = AaveV2EthereumAssets.DAI_UNDERLYING; + address newCollateralAsset = AaveV2EthereumAssets.USDC_UNDERLYING; + address newCollateralAssetAToken = AaveV2EthereumAssets.USDC_A_TOKEN; + vm.startPrank(user); + + _supply(AaveV2Ethereum.POOL, supplyAmount, collateralAsset); + _borrow(AaveV2Ethereum.POOL, borrowAmount, collateralAsset); + + uint256 newCollateralAssetATokenBalanceBefore = IERC20Detailed(newCollateralAssetAToken) + .balanceOf(user); + + uint256 collateralAmountToSwap = 18_000 ether; // supplyAmount + uint256 expectedAmount = 17_500e6; + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + newCollateralAsset, + (collateralAmountToSwap * 9991) / 10_000, //taking flashloan premium(0.09%) into account + user, + true, + true + ); + + IParaSwapLiquiditySwapAdapter.LiquiditySwapParams + memory liquiditySwapParams = IParaSwapLiquiditySwapAdapter.LiquiditySwapParams({ + collateralAsset: collateralAsset, + collateralAmountToSwap: collateralAmountToSwap, + newCollateralAsset: newCollateralAsset, + newCollateralAmount: expectedAmount, + offset: psp.offset, + user: user, + withFlashLoan: true, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + IParaSwapLiquiditySwapAdapter.PermitInput memory collateralATokenPermit = _getPermit( + collateralAssetAToken, + address(liquiditySwapAdapter), + collateralAmountToSwap + ); + + liquiditySwapAdapter.swapLiquidity(liquiditySwapParams, collateralATokenPermit); + uint256 oldCollateralAssetATokenBalanceAfter = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + uint256 newCollateralAssetATokenBalanceAfter = IERC20Detailed(newCollateralAssetAToken) + .balanceOf(user); + assertEq(oldCollateralAssetATokenBalanceAfter, 0, 'swap with all collateral failed'); + assertGt( + newCollateralAssetATokenBalanceAfter - newCollateralAssetATokenBalanceBefore, + expectedAmount, + 'invalid amount received' + ); + _invariant(address(liquiditySwapAdapter), collateralAsset, newCollateralAsset); + _invariant(address(liquiditySwapAdapter), collateralAssetAToken, newCollateralAssetAToken); + } + + function _supply(ILendingPool pool, uint256 amount, address asset) internal { + deal(asset, user, amount); + IERC20Detailed(asset).approve(address(pool), amount); + pool.deposit(asset, amount, user, 0); + } + + function _borrow(ILendingPool pool, uint256 amount, address asset) internal { + pool.borrow(asset, amount, 2, 0, user); + } + + function _withdraw(ILendingPool pool, uint256 amount, address asset) internal { + pool.withdraw(asset, amount, user); + } +} diff --git a/src/tests/LiquiditySwapV3.t.sol b/src/tests/LiquiditySwapV3.t.sol new file mode 100644 index 0000000..4a9bbeb --- /dev/null +++ b/src/tests/LiquiditySwapV3.t.sol @@ -0,0 +1,669 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IERC20Detailed} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/IERC20Detailed.sol'; +import {IACLManager} from '@aave/core-v3/contracts/interfaces/IACLManager.sol'; +import {IPoolAddressesProvider} from '@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol'; +import {AaveGovernanceV2} from 'aave-address-book/AaveGovernanceV2.sol'; +import {Errors} from 'aave-address-book/AaveV3.sol'; +import {AaveV3Ethereum, AaveV3EthereumAssets, IPool} from 'aave-address-book/AaveV3Ethereum.sol'; +import {ParaSwapLiquiditySwapAdapterV3} from 'src/contracts/ParaSwapLiquiditySwapAdapterV3.sol'; +import {IParaSwapAugustusRegistry} from 'src/contracts/dependencies/paraswap/IParaSwapAugustusRegistry.sol'; +import {AugustusRegistry} from 'src/contracts/dependencies/paraswap/AugustusRegistry.sol'; +import {IParaSwapLiquiditySwapAdapter} from 'src/contracts/interfaces/IParaSwapLiquiditySwapAdapter.sol'; +import {BaseTest} from './utils/BaseTest.sol'; + +contract LiquiditySwapAdapterV3Test is BaseTest { + ParaSwapLiquiditySwapAdapterV3 internal liquiditySwapAdapter; + + function setUp() public override { + super.setUp(); + vm.createSelectFork(vm.rpcUrl('mainnet'), 19125717); + + liquiditySwapAdapter = new ParaSwapLiquiditySwapAdapterV3( + IPoolAddressesProvider(address(AaveV3Ethereum.POOL_ADDRESSES_PROVIDER)), + address(AaveV3Ethereum.POOL), + IParaSwapAugustusRegistry(AugustusRegistry.ETHEREUM), + AaveGovernanceV2.SHORT_EXECUTOR + ); + vm.startPrank(AaveV3Ethereum.ACL_ADMIN); + IACLManager(address(AaveV3Ethereum.ACL_MANAGER)).addFlashBorrower( + address(liquiditySwapAdapter) + ); + vm.stopPrank(); + } + + function test_revert_executeOperation_not_pool() public { + address[] memory mockAddresses = new address[](0); + uint256[] memory mockAmounts = new uint256[](0); + + vm.expectRevert(bytes('CALLER_MUST_BE_POOL')); + liquiditySwapAdapter.executeOperation( + mockAddresses, + mockAmounts, + mockAmounts, + address(0), + abi.encode('') + ); + } + + function test_revert_executeOperation_wrong_initiator() public { + vm.prank(address(AaveV3Ethereum.POOL)); + address[] memory mockAddresses = new address[](0); + uint256[] memory mockAmounts = new uint256[](0); + + vm.expectRevert(bytes('INITIATOR_MUST_BE_THIS')); + liquiditySwapAdapter.executeOperation( + mockAddresses, + mockAmounts, + mockAmounts, + address(0), + abi.encode('') + ); + } + + function test_revert_due_to_insufficient_amount() public { + uint256 supplyAmount = 12_000 ether; + uint256 borrowAmount = 1_000 ether; + + address collateralAsset = AaveV3EthereumAssets.DAI_UNDERLYING; + address collateralAssetAToken = AaveV3EthereumAssets.DAI_A_TOKEN; + address newCollateralAsset = AaveV3EthereumAssets.LUSD_UNDERLYING; + + vm.startPrank(user); + _supply(AaveV3Ethereum.POOL, supplyAmount, collateralAsset); + _borrow(AaveV3Ethereum.POOL, borrowAmount, collateralAsset); + + skip(1 hours); + + uint256 collateralAmountToSwap = 11_999 ether; + uint256 expectedAmount = 11_800 ether; + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + newCollateralAsset, + collateralAmountToSwap, + user, + true, + true + ); + + IERC20Detailed(collateralAssetAToken).approve( + address(liquiditySwapAdapter), + collateralAmountToSwap + ); + + IParaSwapLiquiditySwapAdapter.LiquiditySwapParams + memory liquiditySwapParams = IParaSwapLiquiditySwapAdapter.LiquiditySwapParams({ + collateralAsset: collateralAsset, + collateralAmountToSwap: collateralAmountToSwap, + newCollateralAsset: newCollateralAsset, + newCollateralAmount: expectedAmount, + offset: psp.offset, + user: user, + withFlashLoan: false, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IParaSwapLiquiditySwapAdapter.PermitInput memory collateralATokenPermit; + + vm.expectRevert(bytes('INSUFFICIENT_AMOUNT_TO_SWAP')); + liquiditySwapAdapter.swapLiquidity(liquiditySwapParams, collateralATokenPermit); + } + + function test_revert_liquiditySwap_without_extra_collateral() public { + address collateralAssetAToken = AaveV3EthereumAssets.DAI_A_TOKEN; + address collateralAsset = AaveV3EthereumAssets.DAI_UNDERLYING; + address newCollateralAsset = AaveV3EthereumAssets.LUSD_UNDERLYING; + + uint256 supplyAmount = 120 ether; + uint256 borrowAmount = 80 ether; + + // We want to end with LT > utilisation > LTV, so we pump up the utilisation to 75% by withdrawing (80 > 75 > 67). + uint256 withdrawAmount = supplyAmount - (borrowAmount * 100) / 75; + + vm.startPrank(user); + + _supply(AaveV3Ethereum.POOL, supplyAmount, collateralAsset); + _borrow(AaveV3Ethereum.POOL, borrowAmount, collateralAsset); + + _withdraw(AaveV3Ethereum.POOL, withdrawAmount, collateralAsset); + + vm.expectRevert(bytes(Errors.HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD)); + _withdraw(AaveV3Ethereum.POOL, 25e18, collateralAsset); + + uint256 collateralAmountToSwap = 25 ether; + uint256 expectedAmount = 20 ether; + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + newCollateralAsset, + collateralAmountToSwap, + user, + true, + false + ); + + skip(1 hours); + + IERC20Detailed(collateralAssetAToken).approve( + address(liquiditySwapAdapter), + collateralAmountToSwap + ); + + IParaSwapLiquiditySwapAdapter.LiquiditySwapParams + memory liquiditySwapParams = IParaSwapLiquiditySwapAdapter.LiquiditySwapParams({ + collateralAsset: collateralAsset, + collateralAmountToSwap: collateralAmountToSwap, + newCollateralAsset: newCollateralAsset, + newCollateralAmount: expectedAmount, + offset: psp.offset, + user: user, + withFlashLoan: false, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IParaSwapLiquiditySwapAdapter.PermitInput memory collateralATokenPermit; + + vm.expectRevert(bytes(Errors.HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD)); + liquiditySwapAdapter.swapLiquidity(liquiditySwapParams, collateralATokenPermit); + } + + function test_revert_wrong_paraswap_route() public { + uint256 supplyAmount = 12_000 ether; + uint256 borrowAmount = 9_000 ether; + + address collateralAsset = AaveV3EthereumAssets.DAI_UNDERLYING; + address collateralAssetAToken = AaveV3EthereumAssets.DAI_A_TOKEN; + address newCollateralAsset = AaveV3EthereumAssets.LUSD_UNDERLYING; + + vm.startPrank(user); + _supply(AaveV3Ethereum.POOL, supplyAmount, collateralAsset); + _borrow(AaveV3Ethereum.POOL, borrowAmount, collateralAsset); + + skip(1 hours); + + uint256 collateralAmountToSwap = 250 ether; + uint256 expectedAmount = 220 ether; + // generating the paraswap route for half of collateralAmountToSwap and executing swap with collateralAmountToSwap + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + newCollateralAsset, + collateralAmountToSwap / 2, + user, + true, + false + ); + + IERC20Detailed(collateralAssetAToken).approve( + address(liquiditySwapAdapter), + collateralAmountToSwap + ); + + IParaSwapLiquiditySwapAdapter.LiquiditySwapParams + memory liquiditySwapParams = IParaSwapLiquiditySwapAdapter.LiquiditySwapParams({ + collateralAsset: collateralAsset, + collateralAmountToSwap: collateralAmountToSwap, + newCollateralAsset: newCollateralAsset, + newCollateralAmount: expectedAmount, + offset: psp.offset, + user: user, + withFlashLoan: false, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IParaSwapLiquiditySwapAdapter.PermitInput memory collateralATokenPermit; + + vm.expectRevert(bytes('WRONG_BALANCE_AFTER_SWAP')); + liquiditySwapAdapter.swapLiquidity(liquiditySwapParams, collateralATokenPermit); + } + + function test_liquiditySwap_without_extra_collateral() public { + address collateralAssetAToken = AaveV3EthereumAssets.DAI_A_TOKEN; + address collateralAsset = AaveV3EthereumAssets.DAI_UNDERLYING; + address newCollateralAsset = AaveV3EthereumAssets.LUSD_UNDERLYING; + address newCollateralAssetAToken = AaveV3EthereumAssets.LUSD_A_TOKEN; + + uint256 supplyAmount = 12000 ether; + uint256 borrowAmount = 1000 ether; + + vm.startPrank(user); + + _supply(AaveV3Ethereum.POOL, supplyAmount, collateralAsset); + _borrow(AaveV3Ethereum.POOL, borrowAmount, collateralAsset); + + uint256 oldCollateralAssetATokenBalanceBefore = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + uint256 newCollateralAssetATokenBalanceBefore = IERC20Detailed(newCollateralAssetAToken) + .balanceOf(user); + + uint256 collateralAmountToSwap = 3500 ether; + uint256 expectedAmount = 3300 ether; + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + newCollateralAsset, + collateralAmountToSwap, + user, + true, + false + ); + + IERC20Detailed(collateralAssetAToken).approve( + address(liquiditySwapAdapter), + collateralAmountToSwap + ); + + IParaSwapLiquiditySwapAdapter.LiquiditySwapParams + memory liquiditySwapParams = IParaSwapLiquiditySwapAdapter.LiquiditySwapParams({ + collateralAsset: collateralAsset, + collateralAmountToSwap: collateralAmountToSwap, + newCollateralAsset: newCollateralAsset, + newCollateralAmount: expectedAmount, + offset: psp.offset, + user: user, + withFlashLoan: false, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IParaSwapLiquiditySwapAdapter.PermitInput memory collateralATokenPermit; + + liquiditySwapAdapter.swapLiquidity(liquiditySwapParams, collateralATokenPermit); + + uint256 oldCollateralAssetATokenBalanceAfter = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + uint256 newCollateralAssetATokenBalanceAfter = IERC20Detailed(newCollateralAssetAToken) + .balanceOf(user); + assertGt( + newCollateralAssetATokenBalanceAfter - newCollateralAssetATokenBalanceBefore, + expectedAmount, + 'invalid amount received' + ); + assertTrue( + _withinRange( + oldCollateralAssetATokenBalanceBefore - oldCollateralAssetATokenBalanceAfter, + collateralAmountToSwap, + 2 + ) + ); + _invariant(address(liquiditySwapAdapter), collateralAsset, newCollateralAsset); + } + + function test_liquiditySwap_permit_without_extra_collateral() public { + address collateralAssetAToken = AaveV3EthereumAssets.DAI_A_TOKEN; + address collateralAsset = AaveV3EthereumAssets.DAI_UNDERLYING; + address newCollateralAsset = AaveV3EthereumAssets.LUSD_UNDERLYING; + address newCollateralAssetAToken = AaveV3EthereumAssets.LUSD_A_TOKEN; + + uint256 supplyAmount = 12000 ether; + uint256 borrowAmount = 1000 ether; + + vm.startPrank(user); + + _supply(AaveV3Ethereum.POOL, supplyAmount, collateralAsset); + _borrow(AaveV3Ethereum.POOL, borrowAmount, collateralAsset); + + uint256 oldCollateralAssetATokenBalanceBefore = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + uint256 newCollateralAssetATokenBalanceBefore = IERC20Detailed(newCollateralAssetAToken) + .balanceOf(user); + uint256 collateralAmountToSwap = 3800 ether; + uint256 expectedAmount = 3500 ether; + + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + newCollateralAsset, + collateralAmountToSwap, + user, + true, + false + ); + + IParaSwapLiquiditySwapAdapter.LiquiditySwapParams + memory liquiditySwapParams = IParaSwapLiquiditySwapAdapter.LiquiditySwapParams({ + collateralAsset: collateralAsset, + collateralAmountToSwap: collateralAmountToSwap, + newCollateralAsset: newCollateralAsset, + newCollateralAmount: expectedAmount, + offset: psp.offset, + user: user, + withFlashLoan: false, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IParaSwapLiquiditySwapAdapter.PermitInput memory collateralATokenPermit = _getPermit( + collateralAssetAToken, + address(liquiditySwapAdapter), + collateralAmountToSwap + ); + + liquiditySwapAdapter.swapLiquidity(liquiditySwapParams, collateralATokenPermit); + + uint256 oldCollateralAssetATokenBalanceAfter = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + uint256 newCollateralAssetATokenBalanceAfter = IERC20Detailed(newCollateralAssetAToken) + .balanceOf(user); + assertGt( + newCollateralAssetATokenBalanceAfter - newCollateralAssetATokenBalanceBefore, + expectedAmount, + 'invalid amount received' + ); + assertTrue( + _withinRange( + oldCollateralAssetATokenBalanceBefore - oldCollateralAssetATokenBalanceAfter, + collateralAmountToSwap, + 2 + ) + ); + _invariant(address(liquiditySwapAdapter), collateralAsset, newCollateralAsset); + } + + function test_revert_liquiditySwap_wrong_permit() public { + address collateralAssetAToken = AaveV3EthereumAssets.DAI_A_TOKEN; + address collateralAsset = AaveV3EthereumAssets.DAI_UNDERLYING; + address newCollateralAsset = AaveV3EthereumAssets.LUSD_UNDERLYING; + + uint256 supplyAmount = 10_000 ether; + uint256 borrowAmount = 1000 ether; + + vm.startPrank(user); + + _supply(AaveV3Ethereum.POOL, supplyAmount, collateralAsset); + _borrow(AaveV3Ethereum.POOL, borrowAmount, collateralAsset); + + uint256 collateralAmountToSwap = 5000 ether; + uint256 expectedAmount = 4800 ether; + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + newCollateralAsset, + collateralAmountToSwap, + user, + true, + false + ); + + IParaSwapLiquiditySwapAdapter.LiquiditySwapParams + memory liquiditySwapParams = IParaSwapLiquiditySwapAdapter.LiquiditySwapParams({ + collateralAsset: collateralAsset, + collateralAmountToSwap: collateralAmountToSwap, + newCollateralAsset: newCollateralAsset, + newCollateralAmount: expectedAmount, + offset: psp.offset, + user: user, + withFlashLoan: false, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IParaSwapLiquiditySwapAdapter.PermitInput memory collateralATokenPermit = _getPermit( + collateralAssetAToken, + address(liquiditySwapAdapter), + collateralAmountToSwap - 1 + ); + + vm.expectRevert(); + liquiditySwapAdapter.swapLiquidity(liquiditySwapParams, collateralATokenPermit); + } + + function test_liquiditySwapFull_without_extra_collateral() public { + uint256 supplyAmount = 15_000 ether; + uint256 borrowAmount = 1000 ether; + + address anotherCollateralAsset = AaveV3EthereumAssets.USDC_UNDERLYING; + address collateralAssetAToken = AaveV3EthereumAssets.DAI_A_TOKEN; + address collateralAsset = AaveV3EthereumAssets.DAI_UNDERLYING; + address newCollateralAsset = AaveV3EthereumAssets.LUSD_UNDERLYING; + address newCollateralAssetAToken = AaveV3EthereumAssets.LUSD_A_TOKEN; + + vm.startPrank(user); + + _supply(AaveV3Ethereum.POOL, supplyAmount, collateralAsset); + //supplying extra collateral so that all dai collateral can be swapped without flashloan + _supply(AaveV3Ethereum.POOL, 15000e6, anotherCollateralAsset); + _borrow(AaveV3Ethereum.POOL, borrowAmount, collateralAsset); + + uint256 newCollateralAssetATokenBalanceBefore = IERC20Detailed(newCollateralAssetAToken) + .balanceOf(user); + + uint256 collateralAmountToSwap = 15_000 ether; // equals to supplyAmount + uint256 expectedAmount = 14_800 ether; + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + newCollateralAsset, + collateralAmountToSwap, + user, + true, + true + ); + + IERC20Detailed(collateralAssetAToken).approve( + address(liquiditySwapAdapter), + collateralAmountToSwap + ); + + IParaSwapLiquiditySwapAdapter.LiquiditySwapParams + memory liquiditySwapParams = IParaSwapLiquiditySwapAdapter.LiquiditySwapParams({ + collateralAsset: collateralAsset, + collateralAmountToSwap: collateralAmountToSwap, + newCollateralAsset: newCollateralAsset, + newCollateralAmount: expectedAmount, + offset: psp.offset, + user: user, + withFlashLoan: false, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IParaSwapLiquiditySwapAdapter.PermitInput memory collateralATokenPermit; + + liquiditySwapAdapter.swapLiquidity(liquiditySwapParams, collateralATokenPermit); + + uint256 oldCollateralAssetATokenBalanceAfter = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + uint256 newCollateralAssetATokenBalanceAfter = IERC20Detailed(newCollateralAssetAToken) + .balanceOf(user); + assertEq(oldCollateralAssetATokenBalanceAfter, 0); + _invariant(address(liquiditySwapAdapter), newCollateralAsset, collateralAsset); + assertGt( + newCollateralAssetATokenBalanceAfter - newCollateralAssetATokenBalanceBefore, + expectedAmount, + 'invalid amount received' + ); + } + + function test_liquiditySwap_half_with_flashloan() public { + uint256 supplyAmount = 20_000 ether; + uint256 borrowAmount = 13_000 ether; + + address collateralAssetAToken = AaveV3EthereumAssets.DAI_A_TOKEN; + address collateralAsset = AaveV3EthereumAssets.DAI_UNDERLYING; + address newCollateralAsset = AaveV3EthereumAssets.USDC_UNDERLYING; + address newCollateralAssetAToken = AaveV3EthereumAssets.USDC_A_TOKEN; + vm.startPrank(user); + + _supply(AaveV3Ethereum.POOL, supplyAmount, collateralAsset); + _borrow(AaveV3Ethereum.POOL, borrowAmount, collateralAsset); + + uint256 oldCollateralAssetATokenBalanceBefore = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + uint256 newCollateralAssetATokenBalanceBefore = IERC20Detailed(newCollateralAssetAToken) + .balanceOf(user); + + uint256 collateralAmountToSwap = 10_000 ether; + uint256 expectedAmount = 9600e6; + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + newCollateralAsset, + collateralAmountToSwap, + user, + true, + false + ); + + IERC20Detailed(collateralAssetAToken).approve( + address(liquiditySwapAdapter), + collateralAmountToSwap + ); + + IParaSwapLiquiditySwapAdapter.LiquiditySwapParams + memory liquiditySwapParams = IParaSwapLiquiditySwapAdapter.LiquiditySwapParams({ + collateralAsset: collateralAsset, + collateralAmountToSwap: collateralAmountToSwap, + newCollateralAsset: newCollateralAsset, + newCollateralAmount: expectedAmount, + offset: psp.offset, + user: user, + withFlashLoan: true, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IParaSwapLiquiditySwapAdapter.PermitInput memory collateralATokenPermit; + + liquiditySwapAdapter.swapLiquidity(liquiditySwapParams, collateralATokenPermit); + uint256 oldCollateralAssetATokenBalanceAfter = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + uint256 newCollateralAssetATokenBalanceAfter = IERC20Detailed(newCollateralAssetAToken) + .balanceOf(user); + assertTrue( + _withinRange( + oldCollateralAssetATokenBalanceBefore - oldCollateralAssetATokenBalanceAfter, + collateralAmountToSwap, + 2 + ) + ); + assertGt( + newCollateralAssetATokenBalanceAfter - newCollateralAssetATokenBalanceBefore, + expectedAmount, + 'invalid amount received' + ); + _invariant(address(liquiditySwapAdapter), collateralAsset, newCollateralAsset); + } + + function test_revert_liquiditySwap_half_without_flashloan() public { + uint256 supplyAmount = 18_000 ether; + uint256 borrowAmount = 12_000 ether; + + address collateralAssetAToken = AaveV3EthereumAssets.DAI_A_TOKEN; + address collateralAsset = AaveV3EthereumAssets.DAI_UNDERLYING; + address newCollateralAsset = AaveV3EthereumAssets.USDC_UNDERLYING; + vm.startPrank(user); + + _supply(AaveV3Ethereum.POOL, supplyAmount, collateralAsset); + _borrow(AaveV3Ethereum.POOL, borrowAmount, collateralAsset); + + uint256 collateralAmountToSwap = 5400 ether; + uint256 expectedAmount = 5000e6; + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + newCollateralAsset, + collateralAmountToSwap, + user, + true, + false + ); + + IERC20Detailed(collateralAssetAToken).approve( + address(liquiditySwapAdapter), + collateralAmountToSwap + ); + + IParaSwapLiquiditySwapAdapter.LiquiditySwapParams + memory liquiditySwapParams = IParaSwapLiquiditySwapAdapter.LiquiditySwapParams({ + collateralAsset: collateralAsset, + collateralAmountToSwap: collateralAmountToSwap, + newCollateralAsset: newCollateralAsset, + newCollateralAmount: expectedAmount, + offset: psp.offset, + user: user, + withFlashLoan: false, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IParaSwapLiquiditySwapAdapter.PermitInput memory collateralATokenPermit; + + vm.expectRevert(bytes(Errors.HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD)); + liquiditySwapAdapter.swapLiquidity(liquiditySwapParams, collateralATokenPermit); + + _invariant(address(liquiditySwapAdapter), collateralAsset, newCollateralAsset); + } + + function test_liquiditySwap_full_with_flashloan_and_permit() public { + uint256 supplyAmount = 30_000 ether; + uint256 borrowAmount = 20_000 ether; + + address collateralAssetAToken = AaveV3EthereumAssets.DAI_A_TOKEN; + address collateralAsset = AaveV3EthereumAssets.DAI_UNDERLYING; + address newCollateralAsset = AaveV3EthereumAssets.USDC_UNDERLYING; + address newCollateralAssetAToken = AaveV3EthereumAssets.USDC_A_TOKEN; + vm.startPrank(user); + + _supply(AaveV3Ethereum.POOL, supplyAmount, collateralAsset); + _borrow(AaveV3Ethereum.POOL, borrowAmount, collateralAsset); + + uint256 oldCollateralAssetATokenBalanceBefore = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + uint256 newCollateralAssetATokenBalanceBefore = IERC20Detailed(newCollateralAssetAToken) + .balanceOf(user); + + uint256 collateralAmountToSwap = 30_000 ether; // supplyAmount + uint256 expectedAmount = 29_000e6; + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + newCollateralAsset, + collateralAmountToSwap, + user, + true, + true + ); + + IParaSwapLiquiditySwapAdapter.LiquiditySwapParams + memory liquiditySwapParams = IParaSwapLiquiditySwapAdapter.LiquiditySwapParams({ + collateralAsset: collateralAsset, + collateralAmountToSwap: collateralAmountToSwap, + newCollateralAsset: newCollateralAsset, + newCollateralAmount: expectedAmount, + offset: psp.offset, + user: user, + withFlashLoan: true, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + IParaSwapLiquiditySwapAdapter.PermitInput memory collateralATokenPermit = _getPermit( + collateralAssetAToken, + address(liquiditySwapAdapter), + oldCollateralAssetATokenBalanceBefore + ); + + liquiditySwapAdapter.swapLiquidity(liquiditySwapParams, collateralATokenPermit); + uint256 oldCollateralAssetATokenBalanceAfter = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + uint256 newCollateralAssetATokenBalanceAfter = IERC20Detailed(newCollateralAssetAToken) + .balanceOf(user); + assertEq(oldCollateralAssetATokenBalanceAfter, 0, 'swap with all collateral failed'); + assertGt( + newCollateralAssetATokenBalanceAfter - newCollateralAssetATokenBalanceBefore, + expectedAmount, + 'invalid amount received' + ); + _invariant(address(liquiditySwapAdapter), collateralAsset, newCollateralAsset); + _invariant(address(liquiditySwapAdapter), collateralAssetAToken, newCollateralAssetAToken); + } + + function _supply(IPool pool, uint256 amount, address asset) internal { + deal(asset, user, amount); + IERC20Detailed(asset).approve(address(pool), amount); + pool.deposit(asset, amount, user, 0); + } + + function _borrow(IPool pool, uint256 amount, address asset) internal { + pool.borrow(asset, amount, 2, 0, user); + } + + function _withdraw(IPool pool, uint256 amount, address asset) internal { + pool.withdraw(asset, amount, user); + } + +} diff --git a/src/tests/PSRouteFuzz.t.sol b/src/tests/PSRouteFuzz.t.sol new file mode 100644 index 0000000..ee55742 --- /dev/null +++ b/src/tests/PSRouteFuzz.t.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol'; +import {BaseTest} from './utils/BaseTest.sol'; + +contract PSRouteFuzzTest is BaseTest { + function setUp() public override { + super.setUp(); + vm.createSelectFork(vm.rpcUrl('mainnet'), 17706839); + } + + // limiting fuzz runs due to ParaSwap API rate limit + /// forge-config: default.fuzz.runs = 50 + function test_fuzz_correct_offset(uint256 amount, bool sell) public { + amount = bound(amount, 1e9, 1_000_000 ether); + address fromAsset = AaveV3EthereumAssets.DAI_UNDERLYING; + address toAsset = AaveV3EthereumAssets.LUSD_UNDERLYING; + + PsPResponse memory psp = _fetchPSPRouteWithoutPspCacheUpdate( + fromAsset, + toAsset, + amount, + user, + sell, + true + ); + _checkAmountInParaSwapCalldata(psp.offset, amount, psp.swapCalldata); + } +} diff --git a/src/tests/RepayAdapterV2.t.sol b/src/tests/RepayAdapterV2.t.sol new file mode 100644 index 0000000..373a679 --- /dev/null +++ b/src/tests/RepayAdapterV2.t.sol @@ -0,0 +1,522 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IERC20Detailed} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/IERC20Detailed.sol'; +import {IPoolAddressesProvider} from '@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol'; +import {AaveGovernanceV2} from 'aave-address-book/AaveGovernanceV2.sol'; +import {Errors} from 'aave-address-book/AaveV2.sol'; +import {AaveV2Ethereum, AaveV2EthereumAssets, ILendingPool} from 'aave-address-book/AaveV2Ethereum.sol'; +import {ParaSwapRepayAdapterV2} from 'src/contracts/ParaSwapRepayAdapterV2.sol'; +import {IParaSwapAugustusRegistry} from 'src/contracts/dependencies/paraswap/IParaSwapAugustusRegistry.sol'; +import {AugustusRegistry} from 'src/contracts/dependencies/paraswap/AugustusRegistry.sol'; +import {IParaSwapRepayAdapter} from 'src/contracts/interfaces/IParaSwapRepayAdapter.sol'; +import {BaseTest} from './utils/BaseTest.sol'; + +contract RepayAdapterV2Test is BaseTest { + ParaSwapRepayAdapterV2 internal repayAdapter; + + function setUp() public override { + super.setUp(); + vm.createSelectFork(vm.rpcUrl('mainnet'), 18883410); + + repayAdapter = new ParaSwapRepayAdapterV2( + IPoolAddressesProvider(address(AaveV2Ethereum.POOL_ADDRESSES_PROVIDER)), + address(AaveV2Ethereum.POOL), + IParaSwapAugustusRegistry(AugustusRegistry.ETHEREUM), + AaveGovernanceV2.SHORT_EXECUTOR + ); + vm.stopPrank(); + } + + function test_revert_executeOperation_not_pool() public { + address[] memory mockAddresses = new address[](0); + uint256[] memory mockAmounts = new uint256[](0); + + vm.expectRevert(bytes('CALLER_MUST_BE_POOL')); + repayAdapter.executeOperation( + mockAddresses, + mockAmounts, + mockAmounts, + address(0), + abi.encode('') + ); + } + + function test_revert_executeOperation_wrong_initiator() public { + vm.prank(address(AaveV2Ethereum.POOL)); + address[] memory mockAddresses = new address[](0); + uint256[] memory mockAmounts = new uint256[](0); + + vm.expectRevert(bytes('INITIATOR_MUST_BE_THIS')); + repayAdapter.executeOperation( + mockAddresses, + mockAmounts, + mockAmounts, + address(0), + abi.encode('') + ); + } + + function test_repay_partial() public { + uint256 supplyAmount = 12_000 ether; + uint256 borrowAmount = 6_000 ether; + address collateralAsset = AaveV2EthereumAssets.DAI_UNDERLYING; + address collateralAssetAToken = AaveV2EthereumAssets.DAI_A_TOKEN; + address debtAsset = AaveV2EthereumAssets.LUSD_UNDERLYING; + address debtAssetVToken = AaveV2EthereumAssets.LUSD_V_TOKEN; + + vm.startPrank(user); + + _supply(AaveV2Ethereum.POOL, supplyAmount, collateralAsset); + _borrow(AaveV2Ethereum.POOL, borrowAmount, debtAsset); + + vm.expectRevert(bytes(Errors.VL_COLLATERAL_CANNOT_COVER_NEW_BORROW)); + _borrow(AaveV2Ethereum.POOL, 5_000 ether, debtAsset); + + uint256 maxCollateralAmountToSwap = 3300 ether; + uint256 debtRepayAmount = 3_000 ether; + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + debtAsset, + debtRepayAmount, + user, + false, + false + ); + + skip(1 hours); + + IERC20Detailed(collateralAssetAToken).approve(address(repayAdapter), maxCollateralAmountToSwap); + IParaSwapRepayAdapter.RepayParams memory repayParams = IParaSwapRepayAdapter.RepayParams({ + collateralAsset: collateralAsset, + maxCollateralAmountToSwap: maxCollateralAmountToSwap, + debtRepayAsset: debtAsset, + debtRepayAmount: debtRepayAmount, + debtRepayMode: 2, + offset: psp.offset, + withFlashLoan: false, + user: user, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IParaSwapRepayAdapter.PermitInput memory collateralATokenPermit; + + uint256 debtTokenBalanceBefore = IERC20Detailed(debtAssetVToken).balanceOf(user); + uint256 collateralAssetATokenBalanceBefore = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + + repayAdapter.repayWithCollateral(repayParams, collateralATokenPermit); + + uint256 debtTokenBalanceAfter = IERC20Detailed(debtAssetVToken).balanceOf(user); + uint256 collateralAssetATokenBalanceAfter = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + assertTrue(_withinRange(debtTokenBalanceBefore - debtTokenBalanceAfter, debtRepayAmount, 2)); + assertTrue( + _withinRange( + collateralAssetATokenBalanceBefore - collateralAssetATokenBalanceAfter, + maxCollateralAmountToSwap, + 300 ether + ) + ); + assertGt(collateralAssetATokenBalanceBefore, collateralAssetATokenBalanceAfter); + _invariant(address(repayAdapter), collateralAsset, debtAsset); + } + + function test_repay_partial_with_permit() public { + uint256 supplyAmount = 20_000 ether; + uint256 borrowAmount = 8_000 ether; + + address collateralAsset = AaveV2EthereumAssets.DAI_UNDERLYING; + address collateralAssetAToken = AaveV2EthereumAssets.DAI_A_TOKEN; + address debtAsset = AaveV2EthereumAssets.LUSD_UNDERLYING; + address debtAssetVToken = AaveV2EthereumAssets.LUSD_V_TOKEN; + + vm.startPrank(user); + + _supply(AaveV2Ethereum.POOL, supplyAmount, collateralAsset); + _borrow(AaveV2Ethereum.POOL, borrowAmount, debtAsset); + + vm.expectRevert(bytes(Errors.VL_COLLATERAL_CANNOT_COVER_NEW_BORROW)); + _borrow(AaveV2Ethereum.POOL, 10_000 ether, debtAsset); + + uint256 maxCollateralAmountToSwap = 2200 ether; + uint256 debtRepayAmount = 2_000 ether; + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + debtAsset, + debtRepayAmount, + user, + false, + false + ); + + skip(1 hours); + + IParaSwapRepayAdapter.RepayParams memory repayParams = IParaSwapRepayAdapter.RepayParams({ + collateralAsset: collateralAsset, + maxCollateralAmountToSwap: maxCollateralAmountToSwap, + debtRepayAsset: debtAsset, + debtRepayAmount: debtRepayAmount, + debtRepayMode: 2, + offset: psp.offset, + withFlashLoan: false, + user: user, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IParaSwapRepayAdapter.PermitInput memory collateralATokenPermit = _getPermit( + collateralAssetAToken, + address(repayAdapter), + maxCollateralAmountToSwap + ); + + uint256 debtTokenBalanceBefore = IERC20Detailed(debtAssetVToken).balanceOf(user); + uint256 collateralAssetATokenBalanceBefore = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + + repayAdapter.repayWithCollateral(repayParams, collateralATokenPermit); + + uint256 debtTokenBalanceAfter = IERC20Detailed(debtAssetVToken).balanceOf(user); + uint256 collateralAssetATokenBalanceAfter = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + assertTrue(_withinRange(debtTokenBalanceBefore - debtTokenBalanceAfter, debtRepayAmount, 2)); + assertTrue( + _withinRange( + collateralAssetATokenBalanceBefore - collateralAssetATokenBalanceAfter, + maxCollateralAmountToSwap, + 300 ether + ) + ); + assertGt(collateralAssetATokenBalanceBefore, collateralAssetATokenBalanceAfter); + _invariant(address(repayAdapter), collateralAsset, debtAsset); + } + + function test_repay_full_without_flashLoan() public { + uint256 supplyAmount = 20_000 ether; + uint256 borrowAmount = 7_900 ether; + + address collateralAsset = AaveV2EthereumAssets.DAI_UNDERLYING; + address collateralAssetAToken = AaveV2EthereumAssets.DAI_A_TOKEN; + address debtAsset = AaveV2EthereumAssets.LUSD_UNDERLYING; + address debtAssetVToken = AaveV2EthereumAssets.LUSD_V_TOKEN; + + vm.startPrank(user); + + _supply(AaveV2Ethereum.POOL, supplyAmount, collateralAsset); + _borrow(AaveV2Ethereum.POOL, borrowAmount, debtAsset); + + skip(1 hours); + + uint256 maxCollateralAssetToSwap = 8500 ether; + uint256 debtRepayAmount = 8000 ether; + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + debtAsset, + debtRepayAmount, + user, + false, + true + ); + + IERC20Detailed(collateralAssetAToken).approve(address(repayAdapter), maxCollateralAssetToSwap); + IParaSwapRepayAdapter.RepayParams memory repayParams = IParaSwapRepayAdapter.RepayParams({ + collateralAsset: collateralAsset, + maxCollateralAmountToSwap: maxCollateralAssetToSwap, + debtRepayAsset: debtAsset, + debtRepayAmount: debtRepayAmount, + debtRepayMode: 2, + offset: psp.offset, + withFlashLoan: false, + user: user, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IParaSwapRepayAdapter.PermitInput memory collateralATokenPermit; + + uint256 debtTokenBalanceBefore = IERC20Detailed(debtAssetVToken).balanceOf(user); + uint256 collateralAssetATokenBalanceBefore = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + + repayAdapter.repayWithCollateral(repayParams, collateralATokenPermit); + + uint256 debtTokenBalanceAfter = IERC20Detailed(debtAssetVToken).balanceOf(user); + uint256 collateralAssetATokenBalanceAfter = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + assertTrue(debtTokenBalanceAfter == 0, 'FULL_DEBT_NOT_REPAID'); + assertTrue( + _withinRange(debtTokenBalanceBefore - debtTokenBalanceAfter, debtRepayAmount, 100 ether) + ); + assertGt(collateralAssetATokenBalanceBefore, collateralAssetATokenBalanceAfter); + _invariant(address(repayAdapter), collateralAsset, debtAsset); + } + + function test_repay_full_with_flashloan() public { + uint256 supplyAmount = 12_000 ether; + uint256 borrowAmount = 9_000 ether; + + address collateralAsset = AaveV2EthereumAssets.DAI_UNDERLYING; + address collateralAssetAToken = AaveV2EthereumAssets.DAI_A_TOKEN; + address debtAsset = AaveV2EthereumAssets.LUSD_UNDERLYING; + address debtAssetVToken = AaveV2EthereumAssets.LUSD_V_TOKEN; + + vm.startPrank(user); + _supply(AaveV2Ethereum.POOL, supplyAmount, collateralAsset); + _borrow(AaveV2Ethereum.POOL, borrowAmount, debtAsset); + + skip(1 hours); + + uint256 debtRepayAmount = 9100 ether; + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + debtAsset, + debtRepayAmount, + user, + false, + true + ); + + IERC20Detailed(collateralAssetAToken).approve(address(repayAdapter), psp.srcAmount); + IParaSwapRepayAdapter.RepayParams memory repayParams = IParaSwapRepayAdapter.RepayParams({ + collateralAsset: collateralAsset, + maxCollateralAmountToSwap: psp.srcAmount, + debtRepayAsset: debtAsset, + debtRepayAmount: debtRepayAmount, + debtRepayMode: 2, + offset: psp.offset, + withFlashLoan: true, + user: user, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + IParaSwapRepayAdapter.PermitInput memory collateralATokenPermit; + + uint256 debtTokenBalanceBefore = IERC20Detailed(debtAssetVToken).balanceOf(user); + uint256 collateralAssetATokenBalanceBefore = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + + repayAdapter.repayWithCollateral(repayParams, collateralATokenPermit); + + uint256 debtTokenBalanceAfter = IERC20Detailed(debtAssetVToken).balanceOf(user); + uint256 collateralAssetATokenBalanceAfter = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + assertTrue( + _withinRange(debtTokenBalanceBefore - debtTokenBalanceAfter, debtRepayAmount, 100 ether) + ); + assertTrue(debtTokenBalanceAfter == 0); + assertGt(collateralAssetATokenBalanceBefore, collateralAssetATokenBalanceAfter); + _invariant(address(repayAdapter), collateralAsset, debtAsset); + } + + function test_revert_with_invalid_flashloan_input() public { + uint256 supplyAmount = 12_000 ether; + uint256 borrowAmount = 9_000 ether; + + address collateralAsset = AaveV2EthereumAssets.DAI_UNDERLYING; + address collateralAssetAToken = AaveV2EthereumAssets.DAI_A_TOKEN; + address debtAsset = AaveV2EthereumAssets.LUSD_UNDERLYING; + + vm.startPrank(user); + _supply(AaveV2Ethereum.POOL, supplyAmount, collateralAsset); + _borrow(AaveV2Ethereum.POOL, borrowAmount, debtAsset); + + skip(1 hours); + + uint256 debtRepayAmount = 9100 ether; + uint256 maxCollateralAmountToSwap = 9500 ether; + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + debtAsset, + debtRepayAmount, + user, + false, + true + ); + + IERC20Detailed(collateralAssetAToken).approve(address(repayAdapter), maxCollateralAmountToSwap); + IParaSwapRepayAdapter.RepayParams memory repayParams = IParaSwapRepayAdapter.RepayParams({ + collateralAsset: collateralAsset, + maxCollateralAmountToSwap: psp.srcAmount - 1, // not passing enough amount for flashloan + debtRepayAsset: debtAsset, + debtRepayAmount: debtRepayAmount, + debtRepayMode: 2, + offset: psp.offset, + withFlashLoan: false, + user: user, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + IParaSwapRepayAdapter.PermitInput memory collateralATokenPermit; + + vm.expectRevert(); + repayAdapter.repayWithCollateral(repayParams, collateralATokenPermit); + } + + function test_revert_wrong_paraswap_route() public { + uint256 supplyAmount = 12_000 ether; + uint256 borrowAmount = 9_000 ether; + + address collateralAsset = AaveV2EthereumAssets.DAI_UNDERLYING; + address collateralAssetAToken = AaveV2EthereumAssets.DAI_A_TOKEN; + address debtAsset = AaveV2EthereumAssets.LUSD_UNDERLYING; + + vm.startPrank(user); + _supply(AaveV2Ethereum.POOL, supplyAmount, collateralAsset); + _borrow(AaveV2Ethereum.POOL, borrowAmount, debtAsset); + + skip(1 hours); + + uint256 debtRepayAmount = 9100 ether; + uint256 maxCollateralAmountToSwap = 9500 ether; + // generating the paraswap route for half of debtRepayAmount and calling repayAdapter with debtRepayAmount + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + debtAsset, + debtRepayAmount / 2, + user, + false, + true + ); + + IERC20Detailed(collateralAssetAToken).approve(address(repayAdapter), maxCollateralAmountToSwap); + IParaSwapRepayAdapter.RepayParams memory repayParams = IParaSwapRepayAdapter.RepayParams({ + collateralAsset: collateralAsset, + maxCollateralAmountToSwap: psp.srcAmount, + debtRepayAsset: debtAsset, + debtRepayAmount: debtRepayAmount, // debtRepayAmount will be more than route generated + debtRepayMode: 2, + offset: psp.offset, + withFlashLoan: false, + user: user, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + IParaSwapRepayAdapter.PermitInput memory collateralATokenPermit; + + vm.expectRevert(); + repayAdapter.repayWithCollateral(repayParams, collateralATokenPermit); + } + + function test_revert_due_to_insufficient_amount() public { + uint256 supplyAmount = 12_000 ether; + uint256 borrowAmount = 9_000 ether; + + address collateralAsset = AaveV2EthereumAssets.DAI_UNDERLYING; + address collateralAssetAToken = AaveV2EthereumAssets.DAI_A_TOKEN; + address debtAsset = AaveV2EthereumAssets.LUSD_UNDERLYING; + + vm.startPrank(user); + _supply(AaveV2Ethereum.POOL, supplyAmount, collateralAsset); + _borrow(AaveV2Ethereum.POOL, borrowAmount, debtAsset); + + skip(1 hours); + + uint256 debtRepayAmount = 9000 ether; + uint256 maxCollateralAmountToSwap = 9500 ether; + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + debtAsset, + debtRepayAmount, + user, + false, + true + ); + + IERC20Detailed(collateralAssetAToken).approve(address(repayAdapter), maxCollateralAmountToSwap); + IParaSwapRepayAdapter.RepayParams memory repayParams = IParaSwapRepayAdapter.RepayParams({ + collateralAsset: collateralAsset, + maxCollateralAmountToSwap: psp.srcAmount, + debtRepayAsset: debtAsset, + debtRepayAmount: debtRepayAmount, + debtRepayMode: 2, + offset: psp.offset, + withFlashLoan: false, + user: user, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + IParaSwapRepayAdapter.PermitInput memory collateralATokenPermit; + + vm.expectRevert(bytes('INSUFFICIENT_AMOUNT_TO_REPAY')); + repayAdapter.repayWithCollateral(repayParams, collateralATokenPermit); + } + + function test_repay_full_with_flashloan_with_permit() public { + uint256 supplyAmount = 12_000 ether; + uint256 borrowAmount = 9_000 ether; + + address collateralAsset = AaveV2EthereumAssets.DAI_UNDERLYING; + address collateralAssetAToken = AaveV2EthereumAssets.DAI_A_TOKEN; + address debtAsset = AaveV2EthereumAssets.LUSD_UNDERLYING; + address debtAssetVToken = AaveV2EthereumAssets.LUSD_V_TOKEN; + + vm.startPrank(user); + _supply(AaveV2Ethereum.POOL, supplyAmount, collateralAsset); + _borrow(AaveV2Ethereum.POOL, borrowAmount, debtAsset); + + skip(1 hours); + + uint256 debtRepayAmount = 9100 ether; + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + debtAsset, + debtRepayAmount, + user, + false, + true + ); + + IParaSwapRepayAdapter.RepayParams memory repayParams = IParaSwapRepayAdapter.RepayParams({ + collateralAsset: collateralAsset, + maxCollateralAmountToSwap: psp.srcAmount + 1000, // passing extra to check whether there is no dust of collateral asset + debtRepayAsset: debtAsset, + debtRepayAmount: debtRepayAmount, + debtRepayMode: 2, + offset: psp.offset, + withFlashLoan: true, + user: user, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + IParaSwapRepayAdapter.PermitInput memory collateralATokenPermit = _getPermit( + collateralAssetAToken, + address(repayAdapter), + psp.srcAmount + 1000 + ); + + uint256 debtTokenBalanceBefore = IERC20Detailed(debtAssetVToken).balanceOf(user); + uint256 collateralAssetATokenBalanceBefore = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + + repayAdapter.repayWithCollateral(repayParams, collateralATokenPermit); + + uint256 debtTokenBalanceAfter = IERC20Detailed(debtAssetVToken).balanceOf(user); + uint256 collateralAssetATokenBalanceAfter = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + assertTrue( + _withinRange(debtTokenBalanceBefore - debtTokenBalanceAfter, debtRepayAmount, 100 ether) + ); + assertTrue(debtTokenBalanceAfter == 0); + assertGt(collateralAssetATokenBalanceBefore, collateralAssetATokenBalanceAfter); + _invariant(address(repayAdapter), collateralAsset, debtAsset); + } + + function _supply(ILendingPool pool, uint256 amount, address asset) internal { + deal(asset, user, amount); + IERC20Detailed(asset).approve(address(pool), amount); + pool.deposit(asset, amount, user, 0); + } + + function _borrow(ILendingPool pool, uint256 amount, address asset) internal { + pool.borrow(asset, amount, 2, 0, user); + } + + function _withdraw(ILendingPool pool, uint256 amount, address asset) internal { + pool.withdraw(asset, amount, user); + } + +} diff --git a/src/tests/RepayAdapterV3.t.sol b/src/tests/RepayAdapterV3.t.sol new file mode 100644 index 0000000..b39aa14 --- /dev/null +++ b/src/tests/RepayAdapterV3.t.sol @@ -0,0 +1,526 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IERC20Detailed} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/IERC20Detailed.sol'; +import {IACLManager} from '@aave/core-v3/contracts/interfaces/IACLManager.sol'; +import {IPoolAddressesProvider} from '@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol'; +import {AaveGovernanceV2} from 'aave-address-book/AaveGovernanceV2.sol'; +import {Errors} from 'aave-address-book/AaveV3.sol'; +import {AaveV3Ethereum, AaveV3EthereumAssets, IPool} from 'aave-address-book/AaveV3Ethereum.sol'; +import {ParaSwapRepayAdapterV3} from 'src/contracts/ParaSwapRepayAdapterV3.sol'; +import {IParaSwapAugustusRegistry} from 'src/contracts/dependencies/paraswap/IParaSwapAugustusRegistry.sol'; +import {AugustusRegistry} from 'src/contracts/dependencies/paraswap/AugustusRegistry.sol'; +import {IParaSwapRepayAdapter} from 'src/contracts/interfaces/IParaSwapRepayAdapter.sol'; +import {BaseTest} from './utils/BaseTest.sol'; + +contract RepayAdapterV3Test is BaseTest { + ParaSwapRepayAdapterV3 internal repayAdapter; + + function setUp() public override { + super.setUp(); + vm.createSelectFork(vm.rpcUrl('mainnet'), 18883410); + + repayAdapter = new ParaSwapRepayAdapterV3( + IPoolAddressesProvider(address(AaveV3Ethereum.POOL_ADDRESSES_PROVIDER)), + address(AaveV3Ethereum.POOL), + IParaSwapAugustusRegistry(AugustusRegistry.ETHEREUM), + AaveGovernanceV2.SHORT_EXECUTOR + ); + vm.startPrank(AaveV3Ethereum.ACL_ADMIN); + IACLManager(address(AaveV3Ethereum.ACL_MANAGER)).addFlashBorrower(address(repayAdapter)); + vm.stopPrank(); + } + + function test_revert_executeOperation_not_pool() public { + address[] memory mockAddresses = new address[](0); + uint256[] memory mockAmounts = new uint256[](0); + + vm.expectRevert(bytes('CALLER_MUST_BE_POOL')); + repayAdapter.executeOperation( + mockAddresses, + mockAmounts, + mockAmounts, + address(0), + abi.encode('') + ); + } + + function test_revert_executeOperation_wrong_initiator() public { + vm.prank(address(AaveV3Ethereum.POOL)); + address[] memory mockAddresses = new address[](0); + uint256[] memory mockAmounts = new uint256[](0); + + vm.expectRevert(bytes('INITIATOR_MUST_BE_THIS')); + repayAdapter.executeOperation( + mockAddresses, + mockAmounts, + mockAmounts, + address(0), + abi.encode('') + ); + } + + function test_repay_partial() public { + uint256 supplyAmount = 7_000 ether; + uint256 borrowAmount = 3_000 ether; + address collateralAsset = AaveV3EthereumAssets.DAI_UNDERLYING; + address collateralAssetAToken = AaveV3EthereumAssets.DAI_A_TOKEN; + address debtAsset = AaveV3EthereumAssets.LUSD_UNDERLYING; + address debtAssetVToken = AaveV3EthereumAssets.LUSD_V_TOKEN; + + vm.startPrank(user); + + _supply(AaveV3Ethereum.POOL, supplyAmount, collateralAsset); + _borrow(AaveV3Ethereum.POOL, borrowAmount, debtAsset); + + vm.expectRevert(bytes(Errors.COLLATERAL_CANNOT_COVER_NEW_BORROW)); + _borrow(AaveV3Ethereum.POOL, 5_000 ether, debtAsset); + + uint256 maxCollateralAmountToSwap = 1100 ether; + uint256 debtRepayAmount = 1_000 ether; + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + debtAsset, + debtRepayAmount, + user, + false, + false + ); + + skip(1 hours); + + IERC20Detailed(collateralAssetAToken).approve(address(repayAdapter), maxCollateralAmountToSwap); + IParaSwapRepayAdapter.RepayParams memory repayParams = IParaSwapRepayAdapter.RepayParams({ + collateralAsset: collateralAsset, + maxCollateralAmountToSwap: maxCollateralAmountToSwap, + debtRepayAsset: debtAsset, + debtRepayAmount: debtRepayAmount, + debtRepayMode: 2, + offset: psp.offset, + withFlashLoan: false, + user: user, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IParaSwapRepayAdapter.PermitInput memory collateralATokenPermit; + + uint256 debtTokenBalanceBefore = IERC20Detailed(debtAssetVToken).balanceOf(user); + uint256 collateralAssetATokenBalanceBefore = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + + repayAdapter.repayWithCollateral(repayParams, collateralATokenPermit); + + uint256 debtTokenBalanceAfter = IERC20Detailed(debtAssetVToken).balanceOf(user); + uint256 collateralAssetATokenBalanceAfter = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + assertTrue(_withinRange(debtTokenBalanceBefore - debtTokenBalanceAfter, debtRepayAmount, 2)); + assertTrue( + _withinRange( + collateralAssetATokenBalanceBefore - collateralAssetATokenBalanceAfter, + maxCollateralAmountToSwap, + 100 ether + ) + ); + assertGt(collateralAssetATokenBalanceBefore, collateralAssetATokenBalanceAfter); + _invariant(address(repayAdapter), collateralAsset, debtAsset); + } + + function test_revert_due_to_insufficient_amount() public { + uint256 supplyAmount = 12_000 ether; + uint256 borrowAmount = 9_000 ether; + + address collateralAsset = AaveV3EthereumAssets.DAI_UNDERLYING; + address collateralAssetAToken = AaveV3EthereumAssets.DAI_A_TOKEN; + address debtAsset = AaveV3EthereumAssets.LUSD_UNDERLYING; + + vm.startPrank(user); + _supply(AaveV3Ethereum.POOL, supplyAmount, collateralAsset); + _borrow(AaveV3Ethereum.POOL, borrowAmount, debtAsset); + + skip(1 hours); + + uint256 debtRepayAmount = 9000 ether; + uint256 maxCollateralAmountToSwap = 9500 ether; + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + debtAsset, + debtRepayAmount, + user, + false, + true + ); + + IERC20Detailed(collateralAssetAToken).approve(address(repayAdapter), maxCollateralAmountToSwap); + IParaSwapRepayAdapter.RepayParams memory repayParams = IParaSwapRepayAdapter.RepayParams({ + collateralAsset: collateralAsset, + maxCollateralAmountToSwap: psp.srcAmount, + debtRepayAsset: debtAsset, + debtRepayAmount: debtRepayAmount, + debtRepayMode: 2, + offset: psp.offset, + withFlashLoan: false, + user: user, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + IParaSwapRepayAdapter.PermitInput memory collateralATokenPermit; + + vm.expectRevert(bytes('INSUFFICIENT_AMOUNT_TO_REPAY')); + repayAdapter.repayWithCollateral(repayParams, collateralATokenPermit); + } + + function test_repay_partial_with_permit() public { + uint256 supplyAmount = 3_000 ether; + uint256 borrowAmount = 500 ether; + + address collateralAsset = AaveV3EthereumAssets.DAI_UNDERLYING; + address collateralAssetAToken = AaveV3EthereumAssets.DAI_A_TOKEN; + address debtAsset = AaveV3EthereumAssets.LUSD_UNDERLYING; + address debtAssetVToken = AaveV3EthereumAssets.LUSD_V_TOKEN; + + vm.startPrank(user); + + _supply(AaveV3Ethereum.POOL, supplyAmount, collateralAsset); + _borrow(AaveV3Ethereum.POOL, borrowAmount, debtAsset); + + vm.expectRevert(bytes(Errors.COLLATERAL_CANNOT_COVER_NEW_BORROW)); + _borrow(AaveV3Ethereum.POOL, 2000 ether, debtAsset); + + uint256 maxCollateralAmountToSwap = 550 ether; + uint256 debtRepayAmount = 500 ether; + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + debtAsset, + debtRepayAmount, + user, + false, + false + ); + + skip(1 hours); + + IParaSwapRepayAdapter.RepayParams memory repayParams = IParaSwapRepayAdapter.RepayParams({ + collateralAsset: collateralAsset, + maxCollateralAmountToSwap: maxCollateralAmountToSwap, + debtRepayAsset: debtAsset, + debtRepayAmount: debtRepayAmount, + debtRepayMode: 2, + offset: psp.offset, + withFlashLoan: false, + user: user, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IParaSwapRepayAdapter.PermitInput memory collateralATokenPermit = _getPermit( + collateralAssetAToken, + address(repayAdapter), + maxCollateralAmountToSwap + ); + + uint256 debtTokenBalanceBefore = IERC20Detailed(debtAssetVToken).balanceOf(user); + uint256 collateralAssetATokenBalanceBefore = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + + repayAdapter.repayWithCollateral(repayParams, collateralATokenPermit); + + uint256 debtTokenBalanceAfter = IERC20Detailed(debtAssetVToken).balanceOf(user); + uint256 collateralAssetATokenBalanceAfter = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + assertTrue(_withinRange(debtTokenBalanceBefore - debtTokenBalanceAfter, debtRepayAmount, 2)); + assertTrue( + _withinRange( + collateralAssetATokenBalanceBefore - collateralAssetATokenBalanceAfter, + maxCollateralAmountToSwap, + 50 ether + ) + ); + assertGt(collateralAssetATokenBalanceBefore, collateralAssetATokenBalanceAfter); + _invariant(address(repayAdapter), collateralAsset, debtAsset); + } + + function test_repay_full_without_flashLoan() public { + uint256 supplyAmount = 25_000 ether; + uint256 borrowAmount = 9900 ether; + + address collateralAsset = AaveV3EthereumAssets.DAI_UNDERLYING; + address collateralAssetAToken = AaveV3EthereumAssets.DAI_A_TOKEN; + address debtAsset = AaveV3EthereumAssets.LUSD_UNDERLYING; + address debtAssetVToken = AaveV3EthereumAssets.LUSD_V_TOKEN; + + vm.startPrank(user); + + _supply(AaveV3Ethereum.POOL, supplyAmount, collateralAsset); + _borrow(AaveV3Ethereum.POOL, borrowAmount, debtAsset); + + skip(1 hours); + + uint256 maxCollateralAssetToSwap = 10_500 ether; + uint256 debtRepayAmount = 10_000 ether; + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + debtAsset, + debtRepayAmount, + user, + false, + true + ); + + IERC20Detailed(collateralAssetAToken).approve(address(repayAdapter), maxCollateralAssetToSwap); + IParaSwapRepayAdapter.RepayParams memory repayParams = IParaSwapRepayAdapter.RepayParams({ + collateralAsset: collateralAsset, + maxCollateralAmountToSwap: maxCollateralAssetToSwap, + debtRepayAsset: debtAsset, + debtRepayAmount: debtRepayAmount, + debtRepayMode: 2, + offset: psp.offset, + withFlashLoan: false, + user: user, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IParaSwapRepayAdapter.PermitInput memory collateralATokenPermit; + + uint256 debtTokenBalanceBefore = IERC20Detailed(debtAssetVToken).balanceOf(user); + uint256 collateralAssetATokenBalanceBefore = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + + repayAdapter.repayWithCollateral(repayParams, collateralATokenPermit); + + uint256 debtTokenBalanceAfter = IERC20Detailed(debtAssetVToken).balanceOf(user); + uint256 collateralAssetATokenBalanceAfter = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + assertTrue(debtTokenBalanceAfter == 0, 'FULL_DEBT_NOT_REPAID'); + assertTrue( + _withinRange(debtTokenBalanceBefore - debtTokenBalanceAfter, debtRepayAmount, 100 ether) + ); + assertGt(collateralAssetATokenBalanceBefore, collateralAssetATokenBalanceAfter); + _invariant(address(repayAdapter), collateralAsset, debtAsset); + } + + function test_repay_full_with_flashLoan() public { + uint256 supplyAmount = 22_000 ether; + uint256 borrowAmount = 9900 ether; + + address collateralAsset = AaveV3EthereumAssets.DAI_UNDERLYING; + address collateralAssetAToken = AaveV3EthereumAssets.DAI_A_TOKEN; + address debtAsset = AaveV3EthereumAssets.LUSD_UNDERLYING; + address debtAssetVToken = AaveV3EthereumAssets.LUSD_V_TOKEN; + + vm.startPrank(user); + + _supply(AaveV3Ethereum.POOL, supplyAmount, collateralAsset); + _borrow(AaveV3Ethereum.POOL, borrowAmount, debtAsset); + + skip(1 hours); + + uint256 debtRepayAmount = 10_000 ether; //borrowAmount + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + debtAsset, + debtRepayAmount, + user, + false, + true + ); + + IERC20Detailed(collateralAssetAToken).approve(address(repayAdapter), psp.srcAmount); + IParaSwapRepayAdapter.RepayParams memory repayParams = IParaSwapRepayAdapter.RepayParams({ + collateralAsset: collateralAsset, + maxCollateralAmountToSwap: psp.srcAmount, + debtRepayAsset: debtAsset, + debtRepayAmount: debtRepayAmount, + debtRepayMode: 2, + offset: psp.offset, + withFlashLoan: true, + user: user, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IParaSwapRepayAdapter.PermitInput memory collateralATokenPermit; + + uint256 debtTokenBalanceBefore = IERC20Detailed(debtAssetVToken).balanceOf(user); + uint256 collateralAssetATokenBalanceBefore = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + + repayAdapter.repayWithCollateral(repayParams, collateralATokenPermit); + + uint256 debtTokenBalanceAfter = IERC20Detailed(debtAssetVToken).balanceOf(user); + uint256 collateralAssetATokenBalanceAfter = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + assertTrue(debtTokenBalanceAfter == 0, 'FULL_DEBT_NOT_REPAID'); + assertTrue( + _withinRange(debtTokenBalanceBefore - debtTokenBalanceAfter, debtRepayAmount, 100 ether) + ); + assertGt(collateralAssetATokenBalanceBefore, collateralAssetATokenBalanceAfter); + _invariant(address(repayAdapter), collateralAsset, debtAsset); + } + + function test_revert_with_invalid_flashloan_input() public { + uint256 supplyAmount = 12_000 ether; + uint256 borrowAmount = 9_000 ether; + + address collateralAsset = AaveV3EthereumAssets.DAI_UNDERLYING; + address collateralAssetAToken = AaveV3EthereumAssets.DAI_A_TOKEN; + address debtAsset = AaveV3EthereumAssets.LUSD_UNDERLYING; + + vm.startPrank(user); + _supply(AaveV3Ethereum.POOL, supplyAmount, collateralAsset); + _borrow(AaveV3Ethereum.POOL, borrowAmount, debtAsset); + + skip(1 hours); + + uint256 debtRepayAmount = 9100 ether; + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + debtAsset, + debtRepayAmount, + user, + false, + true + ); + + IERC20Detailed(collateralAssetAToken).approve(address(repayAdapter), psp.srcAmount - 1); + IParaSwapRepayAdapter.RepayParams memory repayParams = IParaSwapRepayAdapter.RepayParams({ + collateralAsset: collateralAsset, + maxCollateralAmountToSwap: psp.srcAmount - 1, // not passing enough amount for flashloan + debtRepayAsset: debtAsset, + debtRepayAmount: debtRepayAmount, + debtRepayMode: 2, + offset: psp.offset, + withFlashLoan: true, + user: user, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + IParaSwapRepayAdapter.PermitInput memory collateralATokenPermit; + + vm.expectRevert(); + repayAdapter.repayWithCollateral(repayParams, collateralATokenPermit); + } + + function test_revert_wrong_paraswap_route() public { + uint256 supplyAmount = 12_000 ether; + uint256 borrowAmount = 9_000 ether; + + address collateralAsset = AaveV3EthereumAssets.DAI_UNDERLYING; + address collateralAssetAToken = AaveV3EthereumAssets.DAI_A_TOKEN; + address debtAsset = AaveV3EthereumAssets.LUSD_UNDERLYING; + + vm.startPrank(user); + _supply(AaveV3Ethereum.POOL, supplyAmount, collateralAsset); + _borrow(AaveV3Ethereum.POOL, borrowAmount, debtAsset); + + skip(1 hours); + + uint256 debtRepayAmount = 9100 ether; + uint256 maxCollateralAmountToSwap = 9500 ether; + // generating the paraswap route for half of debtRepayAmount and calling repayAdapter with debtRepayAmount + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + debtAsset, + debtRepayAmount / 2, + user, + false, + true + ); + + IERC20Detailed(collateralAssetAToken).approve(address(repayAdapter), maxCollateralAmountToSwap); + IParaSwapRepayAdapter.RepayParams memory repayParams = IParaSwapRepayAdapter.RepayParams({ + collateralAsset: collateralAsset, + maxCollateralAmountToSwap: psp.srcAmount, + debtRepayAsset: debtAsset, + debtRepayAmount: debtRepayAmount, + debtRepayMode: 2, + offset: psp.offset, + withFlashLoan: false, + user: user, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + IParaSwapRepayAdapter.PermitInput memory collateralATokenPermit; + + vm.expectRevert(); + repayAdapter.repayWithCollateral(repayParams, collateralATokenPermit); + } + + function test_repay_full_with_flashloan_with_permit() public { + uint256 supplyAmount = 12_000 ether; + uint256 borrowAmount = 9_000 ether; + + address collateralAsset = AaveV3EthereumAssets.DAI_UNDERLYING; + address collateralAssetAToken = AaveV3EthereumAssets.DAI_A_TOKEN; + address debtAsset = AaveV3EthereumAssets.LUSD_UNDERLYING; + address debtAssetVToken = AaveV3EthereumAssets.LUSD_V_TOKEN; + + vm.startPrank(user); + _supply(AaveV3Ethereum.POOL, supplyAmount, collateralAsset); + _borrow(AaveV3Ethereum.POOL, borrowAmount, debtAsset); + + skip(1 hours); + + uint256 debtRepayAmount = 9100 ether; + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + debtAsset, + debtRepayAmount, + user, + false, + true + ); + + IParaSwapRepayAdapter.RepayParams memory repayParams = IParaSwapRepayAdapter.RepayParams({ + collateralAsset: collateralAsset, + maxCollateralAmountToSwap: psp.srcAmount, + debtRepayAsset: debtAsset, + debtRepayAmount: debtRepayAmount, + debtRepayMode: 2, + offset: psp.offset, + withFlashLoan: true, + user: user, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + IParaSwapRepayAdapter.PermitInput memory collateralATokenPermit = _getPermit( + collateralAssetAToken, + address(repayAdapter), + psp.srcAmount + ); + + uint256 debtTokenBalanceBefore = IERC20Detailed(debtAssetVToken).balanceOf(user); + uint256 collateralAssetATokenBalanceBefore = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + + repayAdapter.repayWithCollateral(repayParams, collateralATokenPermit); + + uint256 debtTokenBalanceAfter = IERC20Detailed(debtAssetVToken).balanceOf(user); + uint256 collateralAssetATokenBalanceAfter = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + assertTrue( + _withinRange(debtTokenBalanceBefore - debtTokenBalanceAfter, debtRepayAmount, 100 ether) + ); + assertTrue(debtTokenBalanceAfter == 0); + assertGt(collateralAssetATokenBalanceBefore, collateralAssetATokenBalanceAfter); + _invariant(address(repayAdapter), collateralAsset, debtAsset); + } + + function _supply(IPool pool, uint256 amount, address asset) internal { + deal(asset, user, amount); + IERC20Detailed(asset).approve(address(pool), amount); + pool.deposit(asset, amount, user, 0); + } + + function _borrow(IPool pool, uint256 amount, address asset) internal { + pool.borrow(asset, amount, 2, 0, user); + } + + function _withdraw(IPool pool, uint256 amount, address asset) internal { + pool.withdraw(asset, amount, user); + } + +} diff --git a/src/tests/SellAdapterFuzz.t.sol b/src/tests/SellAdapterFuzz.t.sol new file mode 100644 index 0000000..400ccb7 --- /dev/null +++ b/src/tests/SellAdapterFuzz.t.sol @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IERC20Detailed} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/IERC20Detailed.sol'; +import {IPoolAddressesProvider} from '@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol'; +import {AaveV3Ethereum, AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol'; +import {IParaSwapAugustusRegistry} from 'src/contracts/dependencies/paraswap/IParaSwapAugustusRegistry.sol'; +import {AugustusRegistry} from 'src/contracts/dependencies/paraswap/AugustusRegistry.sol'; +import {ParaSwapSellAdapterHarness} from './harness/ParaSwapSellAdapterHarness.sol'; +import {BaseTest} from './utils/BaseTest.sol'; + +contract SellAdapterFuzzTest is BaseTest { + ParaSwapSellAdapterHarness internal sellAdapter; + address[] internal aaveV3EthereumAssets; + + event Swapped( + address indexed fromAsset, + address indexed toAsset, + uint256 fromAmount, + uint256 receivedAmount + ); + + function setUp() public override { + super.setUp(); + vm.createSelectFork(vm.rpcUrl('mainnet')); + + sellAdapter = new ParaSwapSellAdapterHarness( + IPoolAddressesProvider(address(AaveV3Ethereum.POOL_ADDRESSES_PROVIDER)), + address(AaveV3Ethereum.POOL), + IParaSwapAugustusRegistry(AugustusRegistry.ETHEREUM) + ); + aaveV3EthereumAssets = [ + AaveV3EthereumAssets.DAI_UNDERLYING, + AaveV3EthereumAssets.LINK_UNDERLYING, + AaveV3EthereumAssets.LUSD_UNDERLYING + ]; + } + + // limiting fuzz runs due to ParaSwap API rate limit + /// forge-config: default.fuzz.runs = 50 + function test_fuzz_sell_on_paraswap( + uint256 fromAssetIndex, + uint256 toAssetIndex, + uint256 amountToSwap, + bool swapAll + ) public { + uint256 totalAssets = aaveV3EthereumAssets.length; + fromAssetIndex = bound(fromAssetIndex, 0, totalAssets - 1); + toAssetIndex = bound(toAssetIndex, 0, totalAssets - 1); + if (fromAssetIndex == toAssetIndex) { + toAssetIndex = (toAssetIndex + 1) % totalAssets; + } + amountToSwap = bound(amountToSwap, 1e15, 10_000 ether); + address assetToSwapFrom = aaveV3EthereumAssets[fromAssetIndex]; + address assetToSwapTo = aaveV3EthereumAssets[toAssetIndex]; + PsPResponse memory psp = _fetchPSPRouteWithoutPspCacheUpdate( + assetToSwapFrom, + assetToSwapTo, + amountToSwap, + user, + true, + swapAll + ); + if (swapAll) { + _checkAmountInParaSwapCalldata(psp.offset, amountToSwap, psp.swapCalldata); + } + deal(assetToSwapFrom, address(sellAdapter), amountToSwap); + + vm.expectEmit(true, true, false, false, address(sellAdapter)); + emit Swapped(assetToSwapFrom, assetToSwapTo, psp.srcAmount, psp.destAmount); + sellAdapter.sellOnParaSwap( + psp.offset, + abi.encode(psp.swapCalldata, psp.augustus), + IERC20Detailed(assetToSwapFrom), + IERC20Detailed(assetToSwapTo), + amountToSwap, + psp.destAmount + ); + + assertEq( + IERC20Detailed(assetToSwapFrom).balanceOf(address(sellAdapter)), + 0, + 'sell adapter not performing exact-in swap' + ); + assertGt(psp.destAmount, 0, 'received zero srcAmount'); + assertGe( + IERC20Detailed(assetToSwapTo).balanceOf(address(sellAdapter)), + psp.destAmount, + 'received less amount than quoted' + ); + } +} diff --git a/src/tests/WithdrawSwapV2.t.sol b/src/tests/WithdrawSwapV2.t.sol new file mode 100644 index 0000000..2c21342 --- /dev/null +++ b/src/tests/WithdrawSwapV2.t.sol @@ -0,0 +1,353 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IERC20Detailed} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/IERC20Detailed.sol'; +import {IPoolAddressesProvider} from '@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol'; +import {AaveGovernanceV2} from 'aave-address-book/AaveGovernanceV2.sol'; +import {AaveV2Ethereum, AaveV2EthereumAssets, ILendingPool} from 'aave-address-book/AaveV2Ethereum.sol'; +import {ParaSwapWithdrawSwapAdapterV2} from 'src/contracts/ParaSwapWithdrawSwapAdapterV2.sol'; +import {IParaSwapAugustusRegistry} from 'src/contracts/dependencies/paraswap/IParaSwapAugustusRegistry.sol'; +import {AugustusRegistry} from 'src/contracts/dependencies/paraswap/AugustusRegistry.sol'; +import {IParaSwapWithdrawSwapAdapter} from 'src/contracts/interfaces/IParaSwapWithdrawSwapAdapter.sol'; +import {IBaseParaSwapAdapter} from 'src/contracts/interfaces/IBaseParaSwapAdapter.sol'; +import {BaseTest} from './utils/BaseTest.sol'; + +contract WithdrawSwapAdapterV2Test is BaseTest { + ParaSwapWithdrawSwapAdapterV2 internal withdrawSwapAdapter; + + function setUp() public override { + super.setUp(); + vm.createSelectFork(vm.rpcUrl('mainnet'), 19125717); + + withdrawSwapAdapter = new ParaSwapWithdrawSwapAdapterV2( + IPoolAddressesProvider(address(AaveV2Ethereum.POOL_ADDRESSES_PROVIDER)), + address(AaveV2Ethereum.POOL), + IParaSwapAugustusRegistry(AugustusRegistry.ETHEREUM), + AaveGovernanceV2.SHORT_EXECUTOR + ); + } + + function test_revert_due_to_slippage_withdrawSwap() public { + address daiAToken = AaveV2EthereumAssets.DAI_A_TOKEN; + address collateralAsset = AaveV2EthereumAssets.DAI_UNDERLYING; + address newAsset = AaveV2EthereumAssets.LUSD_UNDERLYING; + + uint256 supplyAmount = 120 ether; + uint256 withdrawAmount = 120 ether; + uint256 expectedAmount = 10 ether; + + vm.startPrank(user); + _supply(AaveV2Ethereum.POOL, supplyAmount, collateralAsset); + + skip(1 hours); + + IERC20Detailed(daiAToken).approve(address(withdrawSwapAdapter), withdrawAmount); + + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + newAsset, + withdrawAmount, + user, + true, + false + ); + IParaSwapWithdrawSwapAdapter.WithdrawSwapParams + memory withdrawSwapParams = IParaSwapWithdrawSwapAdapter.WithdrawSwapParams({ + oldAsset: collateralAsset, + oldAssetAmount: withdrawAmount, + newAsset: newAsset, + minAmountToReceive: expectedAmount, + allBalanceOffset: psp.offset, + user: user, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IBaseParaSwapAdapter.PermitInput memory collateralATokenPermit; + + vm.expectRevert(bytes('minAmountToReceive exceeds max slippage')); + withdrawSwapAdapter.withdrawAndSwap(withdrawSwapParams, collateralATokenPermit); + } + + function test_revert_wrong_paraswap_route() public { + address daiAToken = AaveV2EthereumAssets.DAI_A_TOKEN; + address collateralAsset = AaveV2EthereumAssets.DAI_UNDERLYING; + address newAsset = AaveV2EthereumAssets.LUSD_UNDERLYING; + + uint256 supplyAmount = 120 ether; + uint256 withdrawAmount = 120 ether; + uint256 expectedAmount = 100 ether; + + vm.startPrank(user); + _supply(AaveV2Ethereum.POOL, supplyAmount, collateralAsset); + + skip(1 hours); + + IERC20Detailed(daiAToken).approve(address(withdrawSwapAdapter), withdrawAmount); + // generating the paraswap route for half of withdrawAmount and executing swap with withdrawAmount + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + newAsset, + withdrawAmount / 2, + user, + true, + false + ); + IParaSwapWithdrawSwapAdapter.WithdrawSwapParams + memory withdrawSwapParams = IParaSwapWithdrawSwapAdapter.WithdrawSwapParams({ + oldAsset: collateralAsset, + oldAssetAmount: withdrawAmount, + newAsset: newAsset, + minAmountToReceive: expectedAmount, + allBalanceOffset: psp.offset, + user: user, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IBaseParaSwapAdapter.PermitInput memory collateralATokenPermit; + + vm.expectRevert(); + withdrawSwapAdapter.withdrawAndSwap(withdrawSwapParams, collateralATokenPermit); + } + + function test_revert_withdrawSwap_max_collateral() public { + address collateralAsset = AaveV2EthereumAssets.DAI_UNDERLYING; + address newAsset = AaveV2EthereumAssets.LUSD_UNDERLYING; + + uint256 supplyAmount = 120 ether; + uint256 withdrawAmount = 120 ether; + uint256 expectedAmount = 115 ether; + + vm.startPrank(user); + + _supply(AaveV2Ethereum.POOL, supplyAmount, collateralAsset); + + skip(1 hours); + + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + newAsset, + withdrawAmount, + user, + true, + true + ); + IParaSwapWithdrawSwapAdapter.WithdrawSwapParams + memory withdrawSwapParams = IParaSwapWithdrawSwapAdapter.WithdrawSwapParams({ + oldAsset: collateralAsset, + oldAssetAmount: withdrawAmount, + newAsset: newAsset, + minAmountToReceive: expectedAmount, + allBalanceOffset: psp.offset, + user: user, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IBaseParaSwapAdapter.PermitInput memory collateralATokenPermit; + + vm.expectRevert(bytes('INSUFFICIENT_AMOUNT_TO_SWAP')); + withdrawSwapAdapter.withdrawAndSwap(withdrawSwapParams, collateralATokenPermit); + } + + function test_withdrawSwap_swapHalf() public { + vm.startPrank(user); + address collateralAssetAToken = AaveV2EthereumAssets.DAI_A_TOKEN; + address collateralAsset = AaveV2EthereumAssets.DAI_UNDERLYING; + address newAsset = AaveV2EthereumAssets.LUSD_UNDERLYING; + + uint256 supplyAmount = 10_000 ether; + + _supply(AaveV2Ethereum.POOL, supplyAmount, collateralAsset); + + uint256 swapAmount = 5_000 ether; // supplyAmount/2 + uint256 expectedAmount = 4500 ether; + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + newAsset, + swapAmount, + user, + true, + false + ); + IERC20Detailed(collateralAssetAToken).approve(address(withdrawSwapAdapter), swapAmount); + + IParaSwapWithdrawSwapAdapter.WithdrawSwapParams + memory withdrawSwapParams = IParaSwapWithdrawSwapAdapter.WithdrawSwapParams({ + oldAsset: collateralAsset, + oldAssetAmount: swapAmount, + newAsset: newAsset, + minAmountToReceive: expectedAmount, + allBalanceOffset: psp.offset, + user: user, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + IBaseParaSwapAdapter.PermitInput memory tokenPermit; + + uint256 collateratAssetATokenBalanceBefore = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + uint256 newAssetBalanceBefore = IERC20Detailed(newAsset).balanceOf(user); + + withdrawSwapAdapter.withdrawAndSwap(withdrawSwapParams, tokenPermit); + + uint256 collateratAssetATokenBalanceAfter = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + uint256 newAssetBalanceAfter = IERC20Detailed(newAsset).balanceOf(user); + assertEq( + _withinRange( + collateratAssetATokenBalanceAfter, + collateratAssetATokenBalanceBefore, + swapAmount + 1 + ), + true, + 'INVALID_ATOKEN_AMOUNT_AFTER_WITHDRAW_SWAP' + ); + assertGt( + newAssetBalanceAfter - newAssetBalanceBefore, + expectedAmount, + 'invalid amount received' + ); + _invariant(address(withdrawSwapAdapter), collateralAsset, newAsset); + } + + function test_withdrawSwap_swapHalf_with_permit() public { + vm.startPrank(user); + address collateralAsset = AaveV2EthereumAssets.DAI_UNDERLYING; + address collateralAssetAToken = AaveV2EthereumAssets.DAI_A_TOKEN; + address newAsset = AaveV2EthereumAssets.LUSD_UNDERLYING; + + uint256 supplyAmount = 10_000 ether; + + _supply(AaveV2Ethereum.POOL, supplyAmount, collateralAsset); + + uint256 swapAmount = 5_000 ether; // supplyAmount/2 + uint256 expectedAmount = 4500 ether; + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + newAsset, + swapAmount, + user, + true, + false + ); + + IParaSwapWithdrawSwapAdapter.WithdrawSwapParams + memory withdrawSwapParams = IParaSwapWithdrawSwapAdapter.WithdrawSwapParams({ + oldAsset: collateralAsset, + oldAssetAmount: swapAmount, + newAsset: newAsset, + minAmountToReceive: expectedAmount, + allBalanceOffset: psp.offset, + user: user, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IBaseParaSwapAdapter.PermitInput memory tokenPermit = _getPermit( + collateralAssetAToken, + address(withdrawSwapAdapter), + swapAmount + ); + + uint256 collateralAssetATokenBalanceBefore = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + uint256 newAssetBalanceBefore = IERC20Detailed(newAsset).balanceOf(user); + + withdrawSwapAdapter.withdrawAndSwap(withdrawSwapParams, tokenPermit); + + uint256 collateratAssetATokenBalanceAfter = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + uint256 newAssetBalanceAfter = IERC20Detailed(newAsset).balanceOf(user); + + assertEq( + _withinRange( + collateratAssetATokenBalanceAfter, + collateralAssetATokenBalanceBefore, + swapAmount + 1 + ), + true, + 'INVALID_ATOKEN_AMOUNT_AFTER_WITHDRAW_SWAP' + ); + assertGt( + newAssetBalanceAfter - newAssetBalanceBefore, + expectedAmount, + 'invalid amount received' + ); + _invariant(address(withdrawSwapAdapter), collateralAsset, newAsset); + } + + function test_withdrawSwap_swapFull() public { + vm.startPrank(user); + address collateralAsset = AaveV2EthereumAssets.DAI_UNDERLYING; + address collateralAssetAToken = AaveV2EthereumAssets.DAI_A_TOKEN; + address newAsset = AaveV2EthereumAssets.LUSD_UNDERLYING; + + uint256 supplyAmount = 10_000 ether; + + _supply(AaveV2Ethereum.POOL, supplyAmount, collateralAsset); + + skip(1 hours); + + uint256 swapAmount = 10_100 ether; + uint256 expectedAmount = 9500 ether; + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + newAsset, + swapAmount, + user, + true, + true + ); + + IParaSwapWithdrawSwapAdapter.WithdrawSwapParams + memory withdrawSwapParams = IParaSwapWithdrawSwapAdapter.WithdrawSwapParams({ + oldAsset: collateralAsset, + oldAssetAmount: swapAmount, + newAsset: newAsset, + minAmountToReceive: expectedAmount, + allBalanceOffset: psp.offset, + user: user, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + IBaseParaSwapAdapter.PermitInput memory tokenPermit; + + uint256 collateralAssetATokenBalanceBefore = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + uint256 newAssetBalanceBefore = IERC20Detailed(newAsset).balanceOf(user); + + IERC20Detailed(collateralAssetAToken).approve( + address(withdrawSwapAdapter), + collateralAssetATokenBalanceBefore + ); + + withdrawSwapAdapter.withdrawAndSwap(withdrawSwapParams, tokenPermit); + + uint256 collateralAssetATokenBalanceAfter = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + uint256 newAssetBalanceAfter = IERC20Detailed(newAsset).balanceOf(user); + assertEq(collateralAssetATokenBalanceAfter, 0, 'NON_ZERO_ATOKEN_BALANCE_AFTER_WITHDRAW_SWAP'); + assertGt( + newAssetBalanceAfter - newAssetBalanceBefore, + expectedAmount, + 'invalid amount received' + ); + _invariant(address(withdrawSwapAdapter), collateralAsset, newAsset); + } + + function _supply(ILendingPool pool, uint256 amount, address asset) internal { + deal(asset, user, amount); + IERC20Detailed(asset).approve(address(pool), amount); + pool.deposit(asset, amount, user, 0); + } + + function _borrow(ILendingPool pool, uint256 amount, address asset) internal { + pool.borrow(asset, amount, 2, 0, user); + } + + function _withdraw(ILendingPool pool, uint256 amount, address asset) internal { + pool.withdraw(asset, amount, user); + } +} diff --git a/src/tests/WithdrawSwapV3.t.sol b/src/tests/WithdrawSwapV3.t.sol new file mode 100644 index 0000000..5eb2f37 --- /dev/null +++ b/src/tests/WithdrawSwapV3.t.sol @@ -0,0 +1,355 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IERC20Detailed} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/IERC20Detailed.sol'; +import {IPoolAddressesProvider} from '@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol'; +import {AaveGovernanceV2} from 'aave-address-book/AaveGovernanceV2.sol'; +import {Errors} from 'aave-address-book/AaveV3.sol'; +import {AaveV3Ethereum, AaveV3EthereumAssets, IPool} from 'aave-address-book/AaveV3Ethereum.sol'; +import {ParaSwapWithdrawSwapAdapterV3} from 'src/contracts/ParaSwapWithdrawSwapAdapterV3.sol'; +import {IParaSwapAugustusRegistry} from 'src/contracts/dependencies/paraswap/IParaSwapAugustusRegistry.sol'; +import {AugustusRegistry} from 'src/contracts/dependencies/paraswap/AugustusRegistry.sol'; +import {IParaSwapWithdrawSwapAdapter} from 'src/contracts/interfaces/IParaSwapWithdrawSwapAdapter.sol'; +import {IBaseParaSwapAdapter} from 'src/contracts/interfaces/IBaseParaSwapAdapter.sol'; +import {BaseTest} from './utils/BaseTest.sol'; + +contract WithdrawSwapAdapterV3Test is BaseTest { + ParaSwapWithdrawSwapAdapterV3 internal withdrawSwapAdapter; + + function setUp() public override { + super.setUp(); + vm.createSelectFork(vm.rpcUrl('mainnet'), 19125717); + + withdrawSwapAdapter = new ParaSwapWithdrawSwapAdapterV3( + IPoolAddressesProvider(address(AaveV3Ethereum.POOL_ADDRESSES_PROVIDER)), + address(AaveV3Ethereum.POOL), + IParaSwapAugustusRegistry(AugustusRegistry.ETHEREUM), + AaveGovernanceV2.SHORT_EXECUTOR + ); + } + + function test_revert_due_to_slippage_withdrawSwap() public { + address collateralAssetAToken = AaveV3EthereumAssets.DAI_A_TOKEN; + address collateralAsset = AaveV3EthereumAssets.DAI_UNDERLYING; + address newAsset = AaveV3EthereumAssets.LUSD_UNDERLYING; + + uint256 supplyAmount = 120 ether; + uint256 withdrawAmount = 120 ether; + uint256 expectedAmount = 10 ether; + + vm.startPrank(user); + + _supply(AaveV3Ethereum.POOL, supplyAmount, collateralAsset); + + IERC20Detailed(collateralAssetAToken).approve(address(withdrawSwapAdapter), withdrawAmount); + + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + newAsset, + withdrawAmount, + user, + true, + true + ); + IParaSwapWithdrawSwapAdapter.WithdrawSwapParams + memory withdrawSwapParams = IParaSwapWithdrawSwapAdapter.WithdrawSwapParams({ + oldAsset: collateralAsset, + oldAssetAmount: withdrawAmount, + newAsset: newAsset, + minAmountToReceive: expectedAmount, + allBalanceOffset: psp.offset, + user: user, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IBaseParaSwapAdapter.PermitInput memory collateralATokenPermit; + + vm.expectRevert(bytes('minAmountToReceive exceeds max slippage')); + withdrawSwapAdapter.withdrawAndSwap(withdrawSwapParams, collateralATokenPermit); + } + + function test_revert_wrong_paraswap_route() public { + address daiAToken = AaveV3EthereumAssets.DAI_A_TOKEN; + address collateralAsset = AaveV3EthereumAssets.DAI_UNDERLYING; + address newAsset = AaveV3EthereumAssets.LUSD_UNDERLYING; + + uint256 supplyAmount = 120 ether; + uint256 withdrawAmount = 120 ether; + uint256 expectedAmount = 100 ether; + + vm.startPrank(user); + _supply(AaveV3Ethereum.POOL, supplyAmount, collateralAsset); + + skip(1 hours); + + IERC20Detailed(daiAToken).approve(address(withdrawSwapAdapter), withdrawAmount); + // generating the paraswap route for half of withdrawAmount and executing swap with withdrawAmount + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + newAsset, + withdrawAmount / 2, + user, + true, + false + ); + IParaSwapWithdrawSwapAdapter.WithdrawSwapParams + memory withdrawSwapParams = IParaSwapWithdrawSwapAdapter.WithdrawSwapParams({ + oldAsset: collateralAsset, + oldAssetAmount: withdrawAmount, + newAsset: newAsset, + minAmountToReceive: expectedAmount, + allBalanceOffset: psp.offset, + user: user, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IBaseParaSwapAdapter.PermitInput memory collateralATokenPermit; + + vm.expectRevert(); + withdrawSwapAdapter.withdrawAndSwap(withdrawSwapParams, collateralATokenPermit); + } + + function test_revert_withdrawSwap_max_collateral() public { + address collateralAsset = AaveV3EthereumAssets.DAI_UNDERLYING; + address newAsset = AaveV3EthereumAssets.LUSD_UNDERLYING; + + uint256 supplyAmount = 120 ether; + uint256 withdrawAmount = 120 ether; + uint256 expectedAmount = 115 ether; + + vm.startPrank(user); + + _supply(AaveV3Ethereum.POOL, supplyAmount, collateralAsset); + + skip(1 hours); + + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + newAsset, + withdrawAmount, + user, + true, + true + ); + IParaSwapWithdrawSwapAdapter.WithdrawSwapParams + memory withdrawSwapParams = IParaSwapWithdrawSwapAdapter.WithdrawSwapParams({ + oldAsset: collateralAsset, + oldAssetAmount: withdrawAmount, + newAsset: newAsset, + minAmountToReceive: expectedAmount, + allBalanceOffset: psp.offset, + user: user, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IBaseParaSwapAdapter.PermitInput memory collateralATokenPermit; + + vm.expectRevert(bytes('INSUFFICIENT_AMOUNT_TO_SWAP')); + withdrawSwapAdapter.withdrawAndSwap(withdrawSwapParams, collateralATokenPermit); + } + + function test_withdrawSwap_swapHalf() public { + vm.startPrank(user); + address collateralAssetAToken = AaveV3EthereumAssets.DAI_A_TOKEN; + address collateralAsset = AaveV3EthereumAssets.DAI_UNDERLYING; + address newAsset = AaveV3EthereumAssets.LUSD_UNDERLYING; + + uint256 supplyAmount = 10_000 ether; + + _supply(AaveV3Ethereum.POOL, supplyAmount, collateralAsset); + + uint256 swapAmount = 5_000 ether; // supplyAmount/2 + uint256 expectedAmount = 4500 ether; + + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + newAsset, + swapAmount, + user, + true, + false + ); + IERC20Detailed(collateralAssetAToken).approve(address(withdrawSwapAdapter), swapAmount); + + IParaSwapWithdrawSwapAdapter.WithdrawSwapParams + memory withdrawSwapParams = IParaSwapWithdrawSwapAdapter.WithdrawSwapParams({ + oldAsset: collateralAsset, + oldAssetAmount: swapAmount, + newAsset: newAsset, + minAmountToReceive: 4000 ether, + allBalanceOffset: psp.offset, + user: user, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + IBaseParaSwapAdapter.PermitInput memory tokenPermit; + + uint256 collateralAssetATokenBalanceBefore = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + uint256 newAssetBalanceBefore = IERC20Detailed(newAsset).balanceOf(user); + + withdrawSwapAdapter.withdrawAndSwap(withdrawSwapParams, tokenPermit); + + uint256 collateralAssetATokenBalanceAfter = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + uint256 newAssetBalanceAfter = IERC20Detailed(newAsset).balanceOf(user); + + assertEq( + _withinRange( + collateralAssetATokenBalanceAfter, + collateralAssetATokenBalanceBefore, + swapAmount + 1 + ), + true, + 'INVALID_ATOKEN_AMOUNT_AFTER_WITHDRAW_SWAP' + ); + assertGt( + newAssetBalanceAfter - newAssetBalanceBefore, + expectedAmount, + 'invalid amount received' + ); + _invariant(address(withdrawSwapAdapter), collateralAsset, newAsset); + } + + function test_withdrawSwap_swapHalf_with_permit() public { + vm.startPrank(user); + address collateralAssetAToken = AaveV3EthereumAssets.DAI_A_TOKEN; + address collateralAsset = AaveV3EthereumAssets.DAI_UNDERLYING; + address newAsset = AaveV3EthereumAssets.LUSD_UNDERLYING; + + uint256 supplyAmount = 10_000 ether; + + _supply(AaveV3Ethereum.POOL, supplyAmount, collateralAsset); + + uint256 swapAmount = 5_000 ether; // supplyAmount/2 + uint256 expectedAmount = 4500 ether; + + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + newAsset, + swapAmount, + user, + true, + false + ); + + IParaSwapWithdrawSwapAdapter.WithdrawSwapParams + memory withdrawSwapParams = IParaSwapWithdrawSwapAdapter.WithdrawSwapParams({ + oldAsset: collateralAsset, + oldAssetAmount: swapAmount, + newAsset: newAsset, + minAmountToReceive: expectedAmount, + allBalanceOffset: psp.offset, + user: user, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IBaseParaSwapAdapter.PermitInput memory tokenPermit = _getPermit( + collateralAssetAToken, + address(withdrawSwapAdapter), + swapAmount + ); + + uint256 collateralAssetATokenBalanceBefore = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + uint256 newAssetBalanceBefore = IERC20Detailed(newAsset).balanceOf(user); + + withdrawSwapAdapter.withdrawAndSwap(withdrawSwapParams, tokenPermit); + + uint256 collateralAssetATokenBalanceAfter = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + uint256 newAssetBalanceAfter = IERC20Detailed(newAsset).balanceOf(user); + assertEq( + _withinRange( + collateralAssetATokenBalanceAfter, + collateralAssetATokenBalanceBefore, + swapAmount + 1 + ), + true, + 'INVALID_ATOKEN_AMOUNT_AFTER_WITHDRAW_SWAP' + ); + assertGt( + newAssetBalanceAfter - newAssetBalanceBefore, + expectedAmount, + 'invalid amount received' + ); + _invariant(address(withdrawSwapAdapter), collateralAsset, newAsset); + } + + function test_withdrawSwap_swapFull() public { + vm.startPrank(user); + address collateralAsset = AaveV3EthereumAssets.DAI_UNDERLYING; + address collateralAssetAToken = AaveV3EthereumAssets.DAI_A_TOKEN; + address newAsset = AaveV3EthereumAssets.LUSD_UNDERLYING; + + uint256 supplyAmount = 1000 ether; + + _supply(AaveV3Ethereum.POOL, supplyAmount, collateralAsset); + + skip(1 hours); + + uint256 swapAmount = 1001 ether; + uint256 expectedAmount = 950 ether; + PsPResponse memory psp = _fetchPSPRoute( + collateralAsset, + newAsset, + swapAmount, + user, + true, + true + ); + + IParaSwapWithdrawSwapAdapter.WithdrawSwapParams + memory withdrawSwapParams = IParaSwapWithdrawSwapAdapter.WithdrawSwapParams({ + oldAsset: collateralAsset, + oldAssetAmount: swapAmount, + newAsset: newAsset, + minAmountToReceive: expectedAmount, + allBalanceOffset: psp.offset, + user: user, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IBaseParaSwapAdapter.PermitInput memory tokenPermit; + + uint256 collateralAssetATokenBalanceBefore = IERC20Detailed(collateralAssetAToken).balanceOf( + user + ); + uint256 newAssetBalanceBefore = IERC20Detailed(newAsset).balanceOf(user); + + IERC20Detailed(collateralAssetAToken).approve( + address(withdrawSwapAdapter), + collateralAssetATokenBalanceBefore + ); + + withdrawSwapAdapter.withdrawAndSwap(withdrawSwapParams, tokenPermit); + + uint256 collateralATokenBalanceAfter = IERC20Detailed(collateralAssetAToken).balanceOf(user); + uint256 newAssetBalanceAfter = IERC20Detailed(newAsset).balanceOf(user); + assertEq(collateralATokenBalanceAfter, 0, 'NON_ZERO_ATOKEN_BALANCE_AFTER_WITHDRAW_SWAP'); + assertGt( + newAssetBalanceAfter - newAssetBalanceBefore, + expectedAmount, + 'invalid amount received' + ); + _invariant(address(withdrawSwapAdapter), collateralAsset, newAsset); + } + + + function _supply(IPool pool, uint256 amount, address asset) internal { + deal(asset, user, amount); + IERC20Detailed(asset).approve(address(pool), amount); + pool.deposit(asset, amount, user, 0); + } + + function _borrow(IPool pool, uint256 amount, address asset) internal { + pool.borrow(asset, amount, 2, 0, user); + } + + function _withdraw(IPool pool, uint256 amount, address asset) internal { + pool.withdraw(asset, amount, user); + } +} diff --git a/src/tests/harness/ParaSwapBuyAdapterHarness.sol b/src/tests/harness/ParaSwapBuyAdapterHarness.sol new file mode 100644 index 0000000..355b53f --- /dev/null +++ b/src/tests/harness/ParaSwapBuyAdapterHarness.sol @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.10; + +import {IERC20Detailed} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/IERC20Detailed.sol'; +import {IPoolAddressesProvider} from '@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol'; +import {IParaSwapAugustusRegistry} from 'src/contracts/dependencies/paraswap/IParaSwapAugustusRegistry.sol'; +import {BaseParaSwapAdapter} from 'src/contracts/base/BaseParaSwapAdapter.sol'; +import {BaseParaSwapBuyAdapter} from 'src/contracts/base/BaseParaSwapBuyAdapter.sol'; + +/** + * @title ParaSwapBuyAdapterHarness + * @notice Harness contract for BaseParaSwapBuyAdapter + */ +contract ParaSwapBuyAdapterHarness is BaseParaSwapBuyAdapter { + /** + * @dev Constructor + * @param addressesProvider The address of the Aave PoolAddressesProvider contract + * @param pool The address of the Aave Pool contract + * @param augustusRegistry The address of the Paraswap AugustusRegistry contract + */ + constructor( + IPoolAddressesProvider addressesProvider, + address pool, + IParaSwapAugustusRegistry augustusRegistry + ) BaseParaSwapBuyAdapter(addressesProvider, pool, augustusRegistry) { + // intentionally left blank + } + + /** + * @dev Swaps a token for another using ParaSwap (exact out) + * @dev In case the swap output is higher than the designated amount to buy, the excess remains in the contract + * @param toAmountOffset Offset of toAmount in Augustus calldata if it should be overwritten, otherwise 0 + * @param paraswapData Data for Paraswap Adapter + * @param assetToSwapFrom The address of the asset to swap from + * @param assetToSwapTo The address of the asset to swap to + * @param maxAmountToSwap The maximum amount of asset to swap from + * @param amountToReceive The amount of asset to receive + * @return amountSold The amount of asset sold + */ + function buyOnParaSwap( + uint256 toAmountOffset, + bytes memory paraswapData, + IERC20Detailed assetToSwapFrom, + IERC20Detailed assetToSwapTo, + uint256 maxAmountToSwap, + uint256 amountToReceive + ) external returns (uint256 amountSold) { + return + _buyOnParaSwap( + toAmountOffset, + paraswapData, + assetToSwapFrom, + assetToSwapTo, + maxAmountToSwap, + amountToReceive + ); + } + + /// @inheritdoc BaseParaSwapAdapter + function _getReserveData( + address asset + ) internal view virtual override returns (address, address, address) {} + + /// @inheritdoc BaseParaSwapAdapter + function _supply( + address asset, + uint256 amount, + address to, + uint16 referralCode + ) internal virtual override {} +} diff --git a/src/tests/harness/ParaSwapSellAdapterHarness.sol b/src/tests/harness/ParaSwapSellAdapterHarness.sol new file mode 100644 index 0000000..350abd1 --- /dev/null +++ b/src/tests/harness/ParaSwapSellAdapterHarness.sol @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.10; + +import {IERC20Detailed} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/IERC20Detailed.sol'; +import {IPoolAddressesProvider} from '@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol'; +import {IParaSwapAugustusRegistry} from 'src/contracts/dependencies/paraswap/IParaSwapAugustusRegistry.sol'; +import {BaseParaSwapAdapter} from 'src/contracts/base/BaseParaSwapAdapter.sol'; +import {BaseParaSwapSellAdapter} from 'src/contracts/base/BaseParaSwapSellAdapter.sol'; + +/** + * @title ParaSwapSellAdapterHarness + * @notice Harness contract for BaseParaSwapSellAdapter + */ +contract ParaSwapSellAdapterHarness is BaseParaSwapSellAdapter { + /** + * @dev Constructor + * @param addressesProvider The address of the Aave PoolAddressesProvider contract + * @param pool The address of the Aave Pool contract + * @param augustusRegistry The address of the Paraswap AugustusRegistry contract + */ + constructor( + IPoolAddressesProvider addressesProvider, + address pool, + IParaSwapAugustusRegistry augustusRegistry + ) BaseParaSwapSellAdapter(addressesProvider, pool, augustusRegistry) { + // intentionally left blank + } + + /** + * @dev Swaps a token for another using ParaSwap (exact in) + * @dev In case the swap input is less than the designated amount to sell, the excess remains in the contract + * @param fromAmountOffset Offset of fromAmount in Augustus calldata if it should be overwritten, otherwise 0 + * @param paraswapData Data for Paraswap Adapter + * @param assetToSwapFrom The address of the asset to swap from + * @param assetToSwapTo The address of the asset to swap to + * @param amountToSwap The amount of asset to swap from + * @param minAmountToReceive The minimum amount to receive + * @return amountReceived The amount of asset bought + */ + function sellOnParaSwap( + uint256 fromAmountOffset, + bytes memory paraswapData, + IERC20Detailed assetToSwapFrom, + IERC20Detailed assetToSwapTo, + uint256 amountToSwap, + uint256 minAmountToReceive + ) external returns (uint256 amountReceived) { + return + _sellOnParaSwap( + fromAmountOffset, + paraswapData, + assetToSwapFrom, + assetToSwapTo, + amountToSwap, + minAmountToReceive + ); + } + + /// @inheritdoc BaseParaSwapAdapter + function _getReserveData( + address asset + ) internal view virtual override returns (address, address, address) {} + + /// @inheritdoc BaseParaSwapAdapter + function _supply( + address asset, + uint256 amount, + address to, + uint16 referralCode + ) internal virtual override {} +} diff --git a/tests/utils/BaseTest.sol b/src/tests/utils/BaseTest.sol similarity index 60% rename from tests/utils/BaseTest.sol rename to src/tests/utils/BaseTest.sol index 706b83e..851b1bf 100644 --- a/tests/utils/BaseTest.sol +++ b/src/tests/utils/BaseTest.sol @@ -2,13 +2,14 @@ pragma solidity ^0.8.0; import 'forge-std/Test.sol'; +import {stdMath} from 'forge-std/StdMath.sol'; import {AaveV3Polygon} from 'aave-address-book/AaveV3Polygon.sol'; import {DataTypes} from 'aave-address-book/AaveV3.sol'; import {IPoolAddressesProvider} from '@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol'; -import {IParaswapDebtSwapAdapter} from '../../src/interfaces/IParaswapDebtSwapAdapter.sol'; import {IERC20WithPermit} from 'solidity-utils/contracts/oz-common/interfaces/IERC20WithPermit.sol'; import {IERC20Detailed} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/IERC20Detailed.sol'; import {IPool} from '@aave/core-v3/contracts/interfaces/IPool.sol'; +import {IBaseParaSwapAdapter} from 'src/contracts/interfaces/IBaseParaSwapAdapter.sol'; import {SigUtils} from './SigUtils.sol'; contract BaseTest is Test { @@ -57,6 +58,34 @@ contract BaseTest is Test { return abi.decode(res, (PsPResponse)); } + function _fetchPSPRouteWithoutPspCacheUpdate( + address from, + address to, + uint256 amount, + address userAddress, + bool sell, + bool max + ) internal returns (PsPResponse memory) { + string[] memory inputs = new string[](14); + inputs[0] = 'node'; + inputs[1] = './scripts/psp.js'; + inputs[2] = vm.toString(block.chainid); + inputs[3] = vm.toString(from); + inputs[4] = vm.toString(to); + inputs[5] = vm.toString(amount); + inputs[6] = vm.toString(userAddress); + inputs[7] = sell ? 'SELL' : 'BUY'; + inputs[8] = vm.toString(MAX_SLIPPAGE); + inputs[9] = vm.toString(max); + inputs[10] = vm.toString(IERC20Detailed(from).decimals()); + inputs[11] = vm.toString(IERC20Detailed(to).decimals()); + inputs[12] = vm.toString(block.number); + inputs[13] = 'false'; + + bytes memory res = vm.ffi(inputs); + return abi.decode(res, (PsPResponse)); + } + /** * @dev Ensure balances are 0 on the adapter itself */ @@ -69,11 +98,36 @@ contract BaseTest is Test { ); } + function _withinRange(uint256 a, uint256 b, uint256 diff) internal pure returns (bool) { + return stdMath.delta(a, b) <= diff; + } + + /** + * @dev Ensure the amount offset in ParaSwap calldata is correct + */ + function _checkAmountInParaSwapCalldata( + uint256 offset, + uint256 amount, + bytes memory swapCalldata + ) internal { + uint256 amountAtOffset; + // Ensure 256 bit (32 bytes) offset value is within bounds of the + // calldata, not overlapping with the first 4 bytes (function selector). + assertTrue(offset >= 4 && offset <= swapCalldata.length - 32, 'offset out of range'); + // In memory, swapCalldata consists of a 256 bit length field, followed by + // the actual bytes data, that is why 32 is added to the byte offset. + assembly { + amountAtOffset := mload(add(swapCalldata, add(offset, 32))) + } + + assertEq(amountAtOffset, amount, 'wrong offset'); + } + function _getPermit( address permitToken, address debtSwapAdapter, uint256 amount - ) internal view returns (IParaswapDebtSwapAdapter.PermitInput memory) { + ) internal view returns (IBaseParaSwapAdapter.PermitInput memory) { IERC20WithPermit token = IERC20WithPermit(permitToken); uint256 nonce; try IERC20WithPermit(token).nonces(user) returns (uint256 res) { @@ -95,7 +149,7 @@ contract BaseTest is Test { (uint8 v, bytes32 r, bytes32 s) = vm.sign(userPrivateKey, digest); return - IParaswapDebtSwapAdapter.PermitInput({ + IBaseParaSwapAdapter.PermitInput({ aToken: token, value: amount, deadline: type(uint256).max, diff --git a/tests/utils/SigUtils.sol b/src/tests/utils/SigUtils.sol similarity index 96% rename from tests/utils/SigUtils.sol rename to src/tests/utils/SigUtils.sol index ebf7157..4052d76 100644 --- a/tests/utils/SigUtils.sol +++ b/src/tests/utils/SigUtils.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.10; -import {BaseParaSwapAdapter} from '../../src/contracts/BaseParaSwapAdapter.sol'; - library SigUtils { bytes32 public constant PERMIT_TYPEHASH = keccak256('Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)'); diff --git a/yarn.lock b/yarn.lock index 853b51f..5fe87e4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.7.0": - version "5.7.0" - resolved "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz" - integrity sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA== +"@ethersproject/abi@^5.7.0", "@ethersproject/abi@5.7.0": + "integrity" "sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==" + "resolved" "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz" + "version" "5.7.0" dependencies: "@ethersproject/address" "^5.7.0" "@ethersproject/bignumber" "^5.7.0" @@ -17,10 +17,10 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" -"@ethersproject/abstract-provider@5.7.0", "@ethersproject/abstract-provider@^5.7.0": - version "5.7.0" - resolved "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz" - integrity sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw== +"@ethersproject/abstract-provider@^5.7.0", "@ethersproject/abstract-provider@5.7.0": + "integrity" "sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==" + "resolved" "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz" + "version" "5.7.0" dependencies: "@ethersproject/bignumber" "^5.7.0" "@ethersproject/bytes" "^5.7.0" @@ -30,10 +30,10 @@ "@ethersproject/transactions" "^5.7.0" "@ethersproject/web" "^5.7.0" -"@ethersproject/abstract-signer@5.7.0", "@ethersproject/abstract-signer@^5.7.0": - version "5.7.0" - resolved "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz" - integrity sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ== +"@ethersproject/abstract-signer@^5.7.0", "@ethersproject/abstract-signer@5.7.0": + "integrity" "sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ==" + "resolved" "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz" + "version" "5.7.0" dependencies: "@ethersproject/abstract-provider" "^5.7.0" "@ethersproject/bignumber" "^5.7.0" @@ -41,10 +41,10 @@ "@ethersproject/logger" "^5.7.0" "@ethersproject/properties" "^5.7.0" -"@ethersproject/address@5.7.0", "@ethersproject/address@^5.7.0": - version "5.7.0" - resolved "https://registry.npmjs.org/@ethersproject/address/-/address-5.7.0.tgz" - integrity sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA== +"@ethersproject/address@^5.7.0", "@ethersproject/address@5.7.0": + "integrity" "sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==" + "resolved" "https://registry.npmjs.org/@ethersproject/address/-/address-5.7.0.tgz" + "version" "5.7.0" dependencies: "@ethersproject/bignumber" "^5.7.0" "@ethersproject/bytes" "^5.7.0" @@ -52,48 +52,48 @@ "@ethersproject/logger" "^5.7.0" "@ethersproject/rlp" "^5.7.0" -"@ethersproject/base64@5.7.0", "@ethersproject/base64@^5.7.0": - version "5.7.0" - resolved "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.7.0.tgz" - integrity sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ== +"@ethersproject/base64@^5.7.0", "@ethersproject/base64@5.7.0": + "integrity" "sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ==" + "resolved" "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.7.0.tgz" + "version" "5.7.0" dependencies: "@ethersproject/bytes" "^5.7.0" -"@ethersproject/basex@5.7.0", "@ethersproject/basex@^5.7.0": - version "5.7.0" - resolved "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.7.0.tgz" - integrity sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw== +"@ethersproject/basex@^5.7.0", "@ethersproject/basex@5.7.0": + "integrity" "sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw==" + "resolved" "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.7.0.tgz" + "version" "5.7.0" dependencies: "@ethersproject/bytes" "^5.7.0" "@ethersproject/properties" "^5.7.0" -"@ethersproject/bignumber@5.7.0", "@ethersproject/bignumber@^5.7.0": - version "5.7.0" - resolved "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.7.0.tgz" - integrity sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw== +"@ethersproject/bignumber@^5.7.0", "@ethersproject/bignumber@5.7.0": + "integrity" "sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==" + "resolved" "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.7.0.tgz" + "version" "5.7.0" dependencies: "@ethersproject/bytes" "^5.7.0" "@ethersproject/logger" "^5.7.0" - bn.js "^5.2.1" + "bn.js" "^5.2.1" -"@ethersproject/bytes@5.7.0", "@ethersproject/bytes@^5.7.0": - version "5.7.0" - resolved "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.7.0.tgz" - integrity sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A== +"@ethersproject/bytes@^5.7.0", "@ethersproject/bytes@5.7.0": + "integrity" "sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==" + "resolved" "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.7.0.tgz" + "version" "5.7.0" dependencies: "@ethersproject/logger" "^5.7.0" -"@ethersproject/constants@5.7.0", "@ethersproject/constants@^5.7.0": - version "5.7.0" - resolved "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.7.0.tgz" - integrity sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA== +"@ethersproject/constants@^5.7.0", "@ethersproject/constants@5.7.0": + "integrity" "sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==" + "resolved" "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.7.0.tgz" + "version" "5.7.0" dependencies: "@ethersproject/bignumber" "^5.7.0" "@ethersproject/contracts@5.7.0": - version "5.7.0" - resolved "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.7.0.tgz" - integrity sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg== + "integrity" "sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg==" + "resolved" "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.7.0.tgz" + "version" "5.7.0" dependencies: "@ethersproject/abi" "^5.7.0" "@ethersproject/abstract-provider" "^5.7.0" @@ -106,10 +106,10 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/transactions" "^5.7.0" -"@ethersproject/hash@5.7.0", "@ethersproject/hash@^5.7.0": - version "5.7.0" - resolved "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.7.0.tgz" - integrity sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g== +"@ethersproject/hash@^5.7.0", "@ethersproject/hash@5.7.0": + "integrity" "sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==" + "resolved" "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.7.0.tgz" + "version" "5.7.0" dependencies: "@ethersproject/abstract-signer" "^5.7.0" "@ethersproject/address" "^5.7.0" @@ -121,10 +121,10 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" -"@ethersproject/hdnode@5.7.0", "@ethersproject/hdnode@^5.7.0": - version "5.7.0" - resolved "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.7.0.tgz" - integrity sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg== +"@ethersproject/hdnode@^5.7.0", "@ethersproject/hdnode@5.7.0": + "integrity" "sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg==" + "resolved" "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.7.0.tgz" + "version" "5.7.0" dependencies: "@ethersproject/abstract-signer" "^5.7.0" "@ethersproject/basex" "^5.7.0" @@ -139,10 +139,10 @@ "@ethersproject/transactions" "^5.7.0" "@ethersproject/wordlists" "^5.7.0" -"@ethersproject/json-wallets@5.7.0", "@ethersproject/json-wallets@^5.7.0": - version "5.7.0" - resolved "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.7.0.tgz" - integrity sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g== +"@ethersproject/json-wallets@^5.7.0", "@ethersproject/json-wallets@5.7.0": + "integrity" "sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g==" + "resolved" "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.7.0.tgz" + "version" "5.7.0" dependencies: "@ethersproject/abstract-signer" "^5.7.0" "@ethersproject/address" "^5.7.0" @@ -155,48 +155,48 @@ "@ethersproject/random" "^5.7.0" "@ethersproject/strings" "^5.7.0" "@ethersproject/transactions" "^5.7.0" - aes-js "3.0.0" - scrypt-js "3.0.1" + "aes-js" "3.0.0" + "scrypt-js" "3.0.1" -"@ethersproject/keccak256@5.7.0", "@ethersproject/keccak256@^5.7.0": - version "5.7.0" - resolved "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.7.0.tgz" - integrity sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg== +"@ethersproject/keccak256@^5.7.0", "@ethersproject/keccak256@5.7.0": + "integrity" "sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==" + "resolved" "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.7.0.tgz" + "version" "5.7.0" dependencies: "@ethersproject/bytes" "^5.7.0" - js-sha3 "0.8.0" + "js-sha3" "0.8.0" -"@ethersproject/logger@5.7.0", "@ethersproject/logger@^5.7.0": - version "5.7.0" - resolved "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.7.0.tgz" - integrity sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig== +"@ethersproject/logger@^5.7.0", "@ethersproject/logger@5.7.0": + "integrity" "sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==" + "resolved" "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.7.0.tgz" + "version" "5.7.0" -"@ethersproject/networks@5.7.1", "@ethersproject/networks@^5.7.0": - version "5.7.1" - resolved "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.1.tgz" - integrity sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ== +"@ethersproject/networks@^5.7.0", "@ethersproject/networks@5.7.1": + "integrity" "sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==" + "resolved" "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.1.tgz" + "version" "5.7.1" dependencies: "@ethersproject/logger" "^5.7.0" -"@ethersproject/pbkdf2@5.7.0", "@ethersproject/pbkdf2@^5.7.0": - version "5.7.0" - resolved "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz" - integrity sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw== +"@ethersproject/pbkdf2@^5.7.0", "@ethersproject/pbkdf2@5.7.0": + "integrity" "sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw==" + "resolved" "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz" + "version" "5.7.0" dependencies: "@ethersproject/bytes" "^5.7.0" "@ethersproject/sha2" "^5.7.0" -"@ethersproject/properties@5.7.0", "@ethersproject/properties@^5.7.0": - version "5.7.0" - resolved "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.7.0.tgz" - integrity sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw== +"@ethersproject/properties@^5.7.0", "@ethersproject/properties@5.7.0": + "integrity" "sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==" + "resolved" "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.7.0.tgz" + "version" "5.7.0" dependencies: "@ethersproject/logger" "^5.7.0" "@ethersproject/providers@5.7.2": - version "5.7.2" - resolved "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.7.2.tgz" - integrity sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg== + "integrity" "sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg==" + "resolved" "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.7.2.tgz" + "version" "5.7.2" dependencies: "@ethersproject/abstract-provider" "^5.7.0" "@ethersproject/abstract-signer" "^5.7.0" @@ -216,50 +216,50 @@ "@ethersproject/strings" "^5.7.0" "@ethersproject/transactions" "^5.7.0" "@ethersproject/web" "^5.7.0" - bech32 "1.1.4" - ws "7.4.6" + "bech32" "1.1.4" + "ws" "7.4.6" -"@ethersproject/random@5.7.0", "@ethersproject/random@^5.7.0": - version "5.7.0" - resolved "https://registry.npmjs.org/@ethersproject/random/-/random-5.7.0.tgz" - integrity sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ== +"@ethersproject/random@^5.7.0", "@ethersproject/random@5.7.0": + "integrity" "sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ==" + "resolved" "https://registry.npmjs.org/@ethersproject/random/-/random-5.7.0.tgz" + "version" "5.7.0" dependencies: "@ethersproject/bytes" "^5.7.0" "@ethersproject/logger" "^5.7.0" -"@ethersproject/rlp@5.7.0", "@ethersproject/rlp@^5.7.0": - version "5.7.0" - resolved "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.7.0.tgz" - integrity sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w== +"@ethersproject/rlp@^5.7.0", "@ethersproject/rlp@5.7.0": + "integrity" "sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==" + "resolved" "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.7.0.tgz" + "version" "5.7.0" dependencies: "@ethersproject/bytes" "^5.7.0" "@ethersproject/logger" "^5.7.0" -"@ethersproject/sha2@5.7.0", "@ethersproject/sha2@^5.7.0": - version "5.7.0" - resolved "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.7.0.tgz" - integrity sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw== +"@ethersproject/sha2@^5.7.0", "@ethersproject/sha2@5.7.0": + "integrity" "sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw==" + "resolved" "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.7.0.tgz" + "version" "5.7.0" dependencies: "@ethersproject/bytes" "^5.7.0" "@ethersproject/logger" "^5.7.0" - hash.js "1.1.7" + "hash.js" "1.1.7" -"@ethersproject/signing-key@5.7.0", "@ethersproject/signing-key@^5.7.0": - version "5.7.0" - resolved "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.7.0.tgz" - integrity sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q== +"@ethersproject/signing-key@^5.7.0", "@ethersproject/signing-key@5.7.0": + "integrity" "sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==" + "resolved" "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.7.0.tgz" + "version" "5.7.0" dependencies: "@ethersproject/bytes" "^5.7.0" "@ethersproject/logger" "^5.7.0" "@ethersproject/properties" "^5.7.0" - bn.js "^5.2.1" - elliptic "6.5.4" - hash.js "1.1.7" + "bn.js" "^5.2.1" + "elliptic" "6.5.4" + "hash.js" "1.1.7" "@ethersproject/solidity@5.7.0": - version "5.7.0" - resolved "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.7.0.tgz" - integrity sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA== + "integrity" "sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA==" + "resolved" "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.7.0.tgz" + "version" "5.7.0" dependencies: "@ethersproject/bignumber" "^5.7.0" "@ethersproject/bytes" "^5.7.0" @@ -268,19 +268,19 @@ "@ethersproject/sha2" "^5.7.0" "@ethersproject/strings" "^5.7.0" -"@ethersproject/strings@5.7.0", "@ethersproject/strings@^5.7.0": - version "5.7.0" - resolved "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.7.0.tgz" - integrity sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg== +"@ethersproject/strings@^5.7.0", "@ethersproject/strings@5.7.0": + "integrity" "sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==" + "resolved" "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.7.0.tgz" + "version" "5.7.0" dependencies: "@ethersproject/bytes" "^5.7.0" "@ethersproject/constants" "^5.7.0" "@ethersproject/logger" "^5.7.0" -"@ethersproject/transactions@5.7.0", "@ethersproject/transactions@^5.7.0": - version "5.7.0" - resolved "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.7.0.tgz" - integrity sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ== +"@ethersproject/transactions@^5.7.0", "@ethersproject/transactions@5.7.0": + "integrity" "sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==" + "resolved" "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.7.0.tgz" + "version" "5.7.0" dependencies: "@ethersproject/address" "^5.7.0" "@ethersproject/bignumber" "^5.7.0" @@ -293,18 +293,18 @@ "@ethersproject/signing-key" "^5.7.0" "@ethersproject/units@5.7.0": - version "5.7.0" - resolved "https://registry.npmjs.org/@ethersproject/units/-/units-5.7.0.tgz" - integrity sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg== + "integrity" "sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg==" + "resolved" "https://registry.npmjs.org/@ethersproject/units/-/units-5.7.0.tgz" + "version" "5.7.0" dependencies: "@ethersproject/bignumber" "^5.7.0" "@ethersproject/constants" "^5.7.0" "@ethersproject/logger" "^5.7.0" "@ethersproject/wallet@5.7.0": - version "5.7.0" - resolved "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.7.0.tgz" - integrity sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA== + "integrity" "sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA==" + "resolved" "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.7.0.tgz" + "version" "5.7.0" dependencies: "@ethersproject/abstract-provider" "^5.7.0" "@ethersproject/abstract-signer" "^5.7.0" @@ -322,10 +322,10 @@ "@ethersproject/transactions" "^5.7.0" "@ethersproject/wordlists" "^5.7.0" -"@ethersproject/web@5.7.1", "@ethersproject/web@^5.7.0": - version "5.7.1" - resolved "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.1.tgz" - integrity sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w== +"@ethersproject/web@^5.7.0", "@ethersproject/web@5.7.1": + "integrity" "sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==" + "resolved" "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.1.tgz" + "version" "5.7.1" dependencies: "@ethersproject/base64" "^5.7.0" "@ethersproject/bytes" "^5.7.0" @@ -333,10 +333,10 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" -"@ethersproject/wordlists@5.7.0", "@ethersproject/wordlists@^5.7.0": - version "5.7.0" - resolved "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.7.0.tgz" - integrity sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA== +"@ethersproject/wordlists@^5.7.0", "@ethersproject/wordlists@5.7.0": + "integrity" "sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA==" + "resolved" "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.7.0.tgz" + "version" "5.7.0" dependencies: "@ethersproject/bytes" "^5.7.0" "@ethersproject/hash" "^5.7.0" @@ -345,124 +345,124 @@ "@ethersproject/strings" "^5.7.0" "@paraswap/core@1.1.0": - version "1.1.0" - resolved "https://registry.npmjs.org/@paraswap/core/-/core-1.1.0.tgz" - integrity sha512-ecnX8ezlhYWFwolZxYEz+K+RfLr8xaxQqiJKlxJ8Yf00tXTGxDGn6/Acy00t4+9Kv0apewd7++J33eJt9yNfwg== + "integrity" "sha512-ecnX8ezlhYWFwolZxYEz+K+RfLr8xaxQqiJKlxJ8Yf00tXTGxDGn6/Acy00t4+9Kv0apewd7++J33eJt9yNfwg==" + "resolved" "https://registry.npmjs.org/@paraswap/core/-/core-1.1.0.tgz" + "version" "1.1.0" -"@paraswap/sdk@^6.2.1": - version "6.2.1" - resolved "https://registry.npmjs.org/@paraswap/sdk/-/sdk-6.2.1.tgz" - integrity sha512-PW0pw676Oyy6129gqLKU/o7JNFVurCnA6AJ3NAKmZ6zAbPrYLWJV+zl/pWqja5MZkst2QzbDhd40gGjIeEKaPg== +"@paraswap/sdk@^6.4.0": + "integrity" "sha512-fZjJRTmZAoUOYzTIFG11NTiKLU9YdKU7Yc7YjTUARQNBxR8zl1K13FqvvoN//WZGNpVsqxVcubv5Av4p/8Xcuw==" + "resolved" "https://registry.npmjs.org/@paraswap/sdk/-/sdk-6.4.0.tgz" + "version" "6.4.0" dependencies: "@paraswap/core" "1.1.0" - bignumber.js "^9.0.2" - ts-essentials "^9.1.2" + "bignumber.js" "^9.0.2" + "ts-essentials" "^9.1.2" "@solidity-parser/parser@^0.14.5": - version "0.14.5" - resolved "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.14.5.tgz" - integrity sha512-6dKnHZn7fg/iQATVEzqyUOyEidbn05q7YA2mQ9hC0MMXhhV3/JrsxmFSYZAcr7j1yUP700LLhTruvJ3MiQmjJg== - dependencies: - antlr4ts "^0.5.0-alpha.4" - -aes-js@3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz" - integrity sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw== - -ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== - -antlr4ts@^0.5.0-alpha.4: - version "0.5.0-alpha.4" - resolved "https://registry.npmjs.org/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz" - integrity sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ== - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" - integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== - -axios@^1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/axios/-/axios-1.1.3.tgz" - integrity sha512-00tXVRwKx/FZr/IDVFt4C+f9FYairX517WoGCL6dpOntqLkZofjhu43F/Xl44UOpqa+9sLFDrG/XAnFsUYgkDA== - dependencies: - follow-redirects "^1.15.0" - form-data "^4.0.0" - proxy-from-env "^1.1.0" - -bech32@1.1.4: - version "1.1.4" - resolved "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz" - integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== - -bignumber.js@^9.0.2: - version "9.1.0" - resolved "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.0.tgz" - integrity sha512-4LwHK4nfDOraBCtst+wOWIHbu1vhvAPJK8g8nROd4iuc3PSEjWif/qwbkh8jwCJz6yDBvtU4KPynETgrfh7y3A== - -bn.js@^4.11.9: - version "4.12.0" - resolved "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz" - integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== - -bn.js@^5.2.1: - version "5.2.1" - resolved "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz" - integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== - -brorand@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz" - integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== - -combined-stream@^1.0.8: - version "1.0.8" - resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" - integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== - -elliptic@6.5.4: - version "6.5.4" - resolved "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz" - integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== - dependencies: - bn.js "^4.11.9" - brorand "^1.1.0" - hash.js "^1.0.0" - hmac-drbg "^1.0.1" - inherits "^2.0.4" - minimalistic-assert "^1.0.1" - minimalistic-crypto-utils "^1.0.1" - -emoji-regex@^10.1.0: - version "10.2.1" - resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.2.1.tgz" - integrity sha512-97g6QgOk8zlDRdgq1WxwgTMgEWGVAQvB5Fdpgc1MkNy56la5SKP9GsMXKDOdqwn90/41a8yPwIGk1Y6WVbeMQA== - -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - -escape-string-regexp@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - -ethers@^5.7.2: - version "5.7.2" - resolved "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz" - integrity sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg== + "integrity" "sha512-6dKnHZn7fg/iQATVEzqyUOyEidbn05q7YA2mQ9hC0MMXhhV3/JrsxmFSYZAcr7j1yUP700LLhTruvJ3MiQmjJg==" + "resolved" "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.14.5.tgz" + "version" "0.14.5" + dependencies: + "antlr4ts" "^0.5.0-alpha.4" + +"aes-js@3.0.0": + "integrity" "sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==" + "resolved" "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz" + "version" "3.0.0" + +"ansi-regex@^5.0.1": + "integrity" "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + "resolved" "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" + "version" "5.0.1" + +"antlr4ts@^0.5.0-alpha.4": + "integrity" "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==" + "resolved" "https://registry.npmjs.org/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz" + "version" "0.5.0-alpha.4" + +"asynckit@^0.4.0": + "integrity" "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + "resolved" "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" + "version" "0.4.0" + +"axios@^1.1.3", "axios@>=0.25.0 <2.0.0": + "integrity" "sha512-00tXVRwKx/FZr/IDVFt4C+f9FYairX517WoGCL6dpOntqLkZofjhu43F/Xl44UOpqa+9sLFDrG/XAnFsUYgkDA==" + "resolved" "https://registry.npmjs.org/axios/-/axios-1.1.3.tgz" + "version" "1.1.3" + dependencies: + "follow-redirects" "^1.15.0" + "form-data" "^4.0.0" + "proxy-from-env" "^1.1.0" + +"bech32@1.1.4": + "integrity" "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==" + "resolved" "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz" + "version" "1.1.4" + +"bignumber.js@^9.0.2": + "integrity" "sha512-4LwHK4nfDOraBCtst+wOWIHbu1vhvAPJK8g8nROd4iuc3PSEjWif/qwbkh8jwCJz6yDBvtU4KPynETgrfh7y3A==" + "resolved" "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.0.tgz" + "version" "9.1.0" + +"bn.js@^4.11.9": + "integrity" "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + "resolved" "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz" + "version" "4.12.0" + +"bn.js@^5.2.1": + "integrity" "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + "resolved" "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz" + "version" "5.2.1" + +"brorand@^1.1.0": + "integrity" "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==" + "resolved" "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz" + "version" "1.1.0" + +"combined-stream@^1.0.8": + "integrity" "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==" + "resolved" "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" + "version" "1.0.8" + dependencies: + "delayed-stream" "~1.0.0" + +"delayed-stream@~1.0.0": + "integrity" "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + "resolved" "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" + "version" "1.0.0" + +"elliptic@6.5.4": + "integrity" "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==" + "resolved" "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz" + "version" "6.5.4" + dependencies: + "bn.js" "^4.11.9" + "brorand" "^1.1.0" + "hash.js" "^1.0.0" + "hmac-drbg" "^1.0.1" + "inherits" "^2.0.4" + "minimalistic-assert" "^1.0.1" + "minimalistic-crypto-utils" "^1.0.1" + +"emoji-regex@^10.1.0": + "integrity" "sha512-97g6QgOk8zlDRdgq1WxwgTMgEWGVAQvB5Fdpgc1MkNy56la5SKP9GsMXKDOdqwn90/41a8yPwIGk1Y6WVbeMQA==" + "resolved" "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.2.1.tgz" + "version" "10.2.1" + +"emoji-regex@^8.0.0": + "integrity" "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "resolved" "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" + "version" "8.0.0" + +"escape-string-regexp@^4.0.0": + "integrity" "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" + "resolved" "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" + "version" "4.0.0" + +"ethers@^5.5.0", "ethers@^5.7.2": + "integrity" "sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==" + "resolved" "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz" + "version" "5.7.2" dependencies: "@ethersproject/abi" "5.7.0" "@ethersproject/abstract-provider" "5.7.0" @@ -495,152 +495,157 @@ ethers@^5.7.2: "@ethersproject/web" "5.7.1" "@ethersproject/wordlists" "5.7.0" -follow-redirects@^1.15.0: - version "1.15.2" - resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz" - integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== - -form-data@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz" - integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - -hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3: - version "1.1.7" - resolved "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz" - integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== - dependencies: - inherits "^2.0.3" - minimalistic-assert "^1.0.1" - -hmac-drbg@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz" - integrity sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg== - dependencies: - hash.js "^1.0.3" - minimalistic-assert "^1.0.0" - minimalistic-crypto-utils "^1.0.1" - -inherits@^2.0.3, inherits@^2.0.4: - version "2.0.4" - resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - -js-sha3@0.8.0: - version "0.8.0" - resolved "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz" - integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== - -lru-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== - dependencies: - yallist "^4.0.0" - -mime-db@1.52.0: - version "1.52.0" - resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" - integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== - -mime-types@^2.1.12: - version "2.1.35" - resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" - integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== - dependencies: - mime-db "1.52.0" - -minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz" - integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== - -minimalistic-crypto-utils@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz" - integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== - -object-hash@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz" - integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== - -prettier-plugin-solidity@^1.0.0-beta.19: - version "1.0.0-rc.1" - resolved "https://registry.npmjs.org/prettier-plugin-solidity/-/prettier-plugin-solidity-1.0.0-rc.1.tgz" - integrity sha512-horUGyCBbfNHWvJ44UVEcsfVySEoG2gxGs7TcBfTZWNvD4VU6rjzwAkrUtKV6VvRZWn9dh01XZ2UhhB3eVnMXQ== +"follow-redirects@^1.15.0": + "integrity" "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" + "resolved" "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz" + "version" "1.15.2" + +"form-data@^4.0.0": + "integrity" "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==" + "resolved" "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz" + "version" "4.0.0" + dependencies: + "asynckit" "^0.4.0" + "combined-stream" "^1.0.8" + "mime-types" "^2.1.12" + +"hash.js@^1.0.0", "hash.js@^1.0.3", "hash.js@1.1.7": + "integrity" "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==" + "resolved" "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz" + "version" "1.1.7" + dependencies: + "inherits" "^2.0.3" + "minimalistic-assert" "^1.0.1" + +"hmac-drbg@^1.0.1": + "integrity" "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==" + "resolved" "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz" + "version" "1.0.1" + dependencies: + "hash.js" "^1.0.3" + "minimalistic-assert" "^1.0.0" + "minimalistic-crypto-utils" "^1.0.1" + +"inherits@^2.0.3", "inherits@^2.0.4": + "integrity" "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "resolved" "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" + "version" "2.0.4" + +"is-fullwidth-code-point@^3.0.0": + "integrity" "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + "resolved" "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" + "version" "3.0.0" + +"js-sha3@0.8.0": + "integrity" "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" + "resolved" "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz" + "version" "0.8.0" + +"lru-cache@^6.0.0": + "integrity" "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==" + "resolved" "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz" + "version" "6.0.0" + dependencies: + "yallist" "^4.0.0" + +"mime-db@1.52.0": + "integrity" "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + "resolved" "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" + "version" "1.52.0" + +"mime-types@^2.1.12": + "integrity" "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==" + "resolved" "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" + "version" "2.1.35" + dependencies: + "mime-db" "1.52.0" + +"minimalistic-assert@^1.0.0", "minimalistic-assert@^1.0.1": + "integrity" "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + "resolved" "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz" + "version" "1.0.1" + +"minimalistic-crypto-utils@^1.0.1": + "integrity" "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==" + "resolved" "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz" + "version" "1.0.1" + +"object-hash@^3.0.0": + "integrity" "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==" + "resolved" "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz" + "version" "3.0.0" + +"prettier-plugin-solidity@^1.0.0-beta.19": + "integrity" "sha512-horUGyCBbfNHWvJ44UVEcsfVySEoG2gxGs7TcBfTZWNvD4VU6rjzwAkrUtKV6VvRZWn9dh01XZ2UhhB3eVnMXQ==" + "resolved" "https://registry.npmjs.org/prettier-plugin-solidity/-/prettier-plugin-solidity-1.0.0-rc.1.tgz" + "version" "1.0.0-rc.1" dependencies: "@solidity-parser/parser" "^0.14.5" - emoji-regex "^10.1.0" - escape-string-regexp "^4.0.0" - semver "^7.3.7" - solidity-comments-extractor "^0.0.7" - string-width "^4.2.3" - -prettier@^2.7.1: - version "2.7.1" - resolved "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz" - integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g== - -proxy-from-env@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz" - integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== - -scrypt-js@3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz" - integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== - -semver@^7.3.7: - version "7.3.8" - resolved "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz" - integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== - dependencies: - lru-cache "^6.0.0" - -solidity-comments-extractor@^0.0.7: - version "0.0.7" - resolved "https://registry.npmjs.org/solidity-comments-extractor/-/solidity-comments-extractor-0.0.7.tgz" - integrity sha512-wciNMLg/Irp8OKGrh3S2tfvZiZ0NEyILfcRCXCD4mp7SgK/i9gzLfhY2hY7VMCQJ3kH9UB9BzNdibIVMchzyYw== - -string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -ts-essentials@^9.1.2: - version "9.3.0" - resolved "https://registry.npmjs.org/ts-essentials/-/ts-essentials-9.3.0.tgz" - integrity sha512-XeiCboEyBG8UqXZtXl59bWEi4ZgOqRsogFDI6WDGIF1LmzbYiAkIwjkXN6zZWWl4re/lsOqMlYfe8KA0XiiEPw== - -ws@7.4.6: - version "7.4.6" - resolved "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz" - integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== - -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + "emoji-regex" "^10.1.0" + "escape-string-regexp" "^4.0.0" + "semver" "^7.3.7" + "solidity-comments-extractor" "^0.0.7" + "string-width" "^4.2.3" + +"prettier@^2.3.0", "prettier@^2.7.1": + "integrity" "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==" + "resolved" "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz" + "version" "2.7.1" + +"proxy-from-env@^1.1.0": + "integrity" "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + "resolved" "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz" + "version" "1.1.0" + +"scrypt-js@3.0.1": + "integrity" "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==" + "resolved" "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz" + "version" "3.0.1" + +"semver@^7.3.7": + "integrity" "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==" + "resolved" "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz" + "version" "7.3.8" + dependencies: + "lru-cache" "^6.0.0" + +"solidity-comments-extractor@^0.0.7": + "integrity" "sha512-wciNMLg/Irp8OKGrh3S2tfvZiZ0NEyILfcRCXCD4mp7SgK/i9gzLfhY2hY7VMCQJ3kH9UB9BzNdibIVMchzyYw==" + "resolved" "https://registry.npmjs.org/solidity-comments-extractor/-/solidity-comments-extractor-0.0.7.tgz" + "version" "0.0.7" + +"string-width@^4.2.3": + "integrity" "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==" + "resolved" "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" + "version" "4.2.3" + dependencies: + "emoji-regex" "^8.0.0" + "is-fullwidth-code-point" "^3.0.0" + "strip-ansi" "^6.0.1" + +"strip-ansi@^6.0.1": + "integrity" "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==" + "resolved" "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" + "version" "6.0.1" + dependencies: + "ansi-regex" "^5.0.1" + +"ts-essentials@^9.1.2": + "integrity" "sha512-XeiCboEyBG8UqXZtXl59bWEi4ZgOqRsogFDI6WDGIF1LmzbYiAkIwjkXN6zZWWl4re/lsOqMlYfe8KA0XiiEPw==" + "resolved" "https://registry.npmjs.org/ts-essentials/-/ts-essentials-9.3.0.tgz" + "version" "9.3.0" + +"typescript@>=4.1.0": + "integrity" "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==" + "resolved" "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz" + "version" "5.2.2" + +"ws@7.4.6": + "integrity" "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==" + "resolved" "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz" + "version" "7.4.6" + +"yallist@^4.0.0": + "integrity" "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "resolved" "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" + "version" "4.0.0" From ac087c4016ad845da23f33ecbb79bb72995969f6 Mon Sep 17 00:00:00 2001 From: Parth Patel Date: Thu, 7 Mar 2024 18:41:03 +0530 Subject: [PATCH 2/4] fix: rename variable in psp.js (#14) --- scripts/psp.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/psp.js b/scripts/psp.js index c5956a2..e12d7a1 100644 --- a/scripts/psp.js +++ b/scripts/psp.js @@ -99,7 +99,7 @@ async function main(from, to, method, amount, user) { return; } // distinguish between exactOut and exactInOutMethod - const preferredMethods = + const includeContractMethods = method === "SELL" ? [ContractMethod.multiSwap, ContractMethod.megaSwap] : [ContractMethod.buy]; @@ -113,7 +113,7 @@ async function main(from, to, method, amount, user) { ...(MAX ? { options: { - includeContractMethods: [...preferredMethods], + includeContractMethods: [...includeContractMethods], }, } : {}), From cb77b7fa85fe6ed30897649b2429ba928973ea65 Mon Sep 17 00:00:00 2001 From: Parth Patel Date: Thu, 7 Mar 2024 18:41:15 +0530 Subject: [PATCH 3/4] fix: move common function to base contract (#13) * fix: move common function to base contract * fix: make allowance function internal * remove redundant function --- src/contracts/base/BaseParaSwapAdapter.sol | 13 ++++++++++++ .../base/ParaSwapDebtSwapAdapter.sol | 12 ----------- .../base/ParaSwapLiquiditySwapAdapter.sol | 21 ------------------- src/contracts/base/ParaSwapRepayAdapter.sol | 21 ------------------- 4 files changed, 13 insertions(+), 54 deletions(-) diff --git a/src/contracts/base/BaseParaSwapAdapter.sol b/src/contracts/base/BaseParaSwapAdapter.sol index 51057da..b08a268 100644 --- a/src/contracts/base/BaseParaSwapAdapter.sol +++ b/src/contracts/base/BaseParaSwapAdapter.sol @@ -120,4 +120,17 @@ abstract contract BaseParaSwapAdapter is Ownable, IBaseParaSwapAdapter { POOL.withdraw(reserve, aTokenBalanceDiff, address(this)); return aTokenBalanceDiff; } + + /** + * @dev Renews the asset allowance in case the current allowance is below a given threshold + * @param asset The address of the asset + * @param minAmount The minimum required allowance to the Aave Pool + */ + function _conditionalRenewAllowance(address asset, uint256 minAmount) internal { + uint256 allowance = IERC20(asset).allowance(address(this), address(POOL)); + if (allowance < minAmount) { + IERC20(asset).safeApprove(address(POOL), 0); + IERC20(asset).safeApprove(address(POOL), type(uint256).max); + } + } } diff --git a/src/contracts/base/ParaSwapDebtSwapAdapter.sol b/src/contracts/base/ParaSwapDebtSwapAdapter.sol index a0e716b..a4cdab7 100644 --- a/src/contracts/base/ParaSwapDebtSwapAdapter.sol +++ b/src/contracts/base/ParaSwapDebtSwapAdapter.sol @@ -44,11 +44,6 @@ abstract contract ParaSwapDebtSwapAdapter is } } - function renewAllowance(address reserve) public { - IERC20WithPermit(reserve).safeApprove(address(POOL), 0); - IERC20WithPermit(reserve).safeApprove(address(POOL), type(uint256).max); - } - /** * @dev Swaps one type of debt to another. Therefore this methods performs the following actions in order: * 1. Delegate credit in new debt @@ -235,11 +230,4 @@ abstract contract ParaSwapDebtSwapAdapter is ); return amountSold; } - - function _conditionalRenewAllowance(address asset, uint256 minAmount) internal { - uint256 allowance = IERC20(asset).allowance(address(this), address(POOL)); - if (allowance < minAmount) { - renewAllowance(asset); - } - } } diff --git a/src/contracts/base/ParaSwapLiquiditySwapAdapter.sol b/src/contracts/base/ParaSwapLiquiditySwapAdapter.sol index d20f548..bce99c9 100644 --- a/src/contracts/base/ParaSwapLiquiditySwapAdapter.sol +++ b/src/contracts/base/ParaSwapLiquiditySwapAdapter.sol @@ -51,15 +51,6 @@ abstract contract ParaSwapLiquiditySwapAdapter is } } - /** - * @notice Renews the asset allowance to the Aave Pool - * @param reserve The address of the asset - */ - function renewAllowance(address reserve) public { - IERC20(reserve).safeApprove(address(POOL), 0); - IERC20(reserve).safeApprove(address(POOL), type(uint256).max); - } - /// @inheritdoc IParaSwapLiquiditySwapAdapter function swapLiquidity( LiquiditySwapParams memory liquiditySwapParams, @@ -191,18 +182,6 @@ abstract contract ParaSwapLiquiditySwapAdapter is return amountReceived; } - /** - * @dev Renews the asset allowance in case the current allowance is below a given threshold - * @param asset The address of the asset - * @param minAmount The minimum required allowance to the Aave Pool - */ - function _conditionalRenewAllowance(address asset, uint256 minAmount) internal { - uint256 allowance = IERC20(asset).allowance(address(this), address(POOL)); - if (allowance < minAmount) { - renewAllowance(asset); - } - } - /** * @dev Triggers the flashloan passing encoded params for the collateral swap * @param liquiditySwapParams struct describing the liquidity swap diff --git a/src/contracts/base/ParaSwapRepayAdapter.sol b/src/contracts/base/ParaSwapRepayAdapter.sol index da4b414..4ab2583 100644 --- a/src/contracts/base/ParaSwapRepayAdapter.sol +++ b/src/contracts/base/ParaSwapRepayAdapter.sol @@ -53,15 +53,6 @@ abstract contract ParaSwapRepayAdapter is } } - /** - * @notice Renews the asset allowance to the Aave Pool - * @param reserve The address of the asset - */ - function renewAllowance(address reserve) public { - IERC20(reserve).safeApprove(address(POOL), 0); - IERC20(reserve).safeApprove(address(POOL), type(uint256).max); - } - /// @inheritdoc IParaSwapRepayAdapter function repayWithCollateral( RepayParams memory repayParams, @@ -240,18 +231,6 @@ abstract contract ParaSwapRepayAdapter is ); } - /** - * @dev Renews the asset allowance in case the current allowance is below a given threshold - * @param asset The address of the asset - * @param minAmount The minimum required allowance to the Aave Pool - */ - function _conditionalRenewAllowance(address asset, uint256 minAmount) internal { - uint256 allowance = IERC20(asset).allowance(address(this), address(POOL)); - if (allowance < minAmount) { - renewAllowance(asset); - } - } - /** * @dev Returns the amount of debt to repay for the user * @param debtAsset The address of the asset to repay the debt From 820705a5693cb6d3b22b47e1422f233bd28dcf3a Mon Sep 17 00:00:00 2001 From: miguelmtz <36620902+miguelmtzinf@users.noreply.github.com> Date: Fri, 8 Mar 2024 11:18:19 +0100 Subject: [PATCH 4/4] fix: Resolve conflicts agains BGD upstream remote main chain (#15) * chore: bnb deploy script (#25) * fix: resolve conflicts --- Makefile | 1 + foundry.toml | 2 ++ lib/aave-helpers | 2 +- .../dependencies/paraswap/AugustusRegistry.sol | 2 ++ src/script/Deploy_ParaSwapDebtSwapAdapter.s.sol | 16 +++++++++++++++- .../Deploy_ParaSwapLiquiditySwapAdapter.s.sol | 15 ++++++++++++++- src/script/Deploy_ParaSwapRepayAdapter.s.sol | 15 ++++++++++++++- src/script/Deploy_ParaSwapWithdrawAdapter.s.sol | 15 ++++++++++++++- 8 files changed, 63 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 44247e7..5cae5a1 100644 --- a/Makefile +++ b/Makefile @@ -26,3 +26,4 @@ ethereumv2 :; make deploy-ledger contract=scripts/Deploy.s.sol:EthereumV2 chain= ethereumv3 :; make deploy-ledger contract=scripts/Deploy.s.sol:EthereumV3 chain=mainnet basev3 :; make deploy-ledger contract=scripts/Deploy.s.sol:BaseV3 chain=base +bnbv3 :; make deploy-ledger contract=scripts/Deploy.s.sol:BNBV3 chain=bnb diff --git a/foundry.toml b/foundry.toml index 6aa64f1..f42d481 100644 --- a/foundry.toml +++ b/foundry.toml @@ -17,6 +17,7 @@ arbitrum = "${RPC_ARBITRUM}" fantom = "${RPC_FANTOM}" harmony = "${RPC_HARMONY}" base = "${RPC_BASE}" +bnb = "${RPC_BNB}" [etherscan] mainnet={key="${ETHERSCAN_API_KEY_MAINNET}",chainId=1} @@ -26,5 +27,6 @@ polygon={key="${ETHERSCAN_API_KEY_POLYGON}",chainId=137} arbitrum={key="${ETHERSCAN_API_KEY_ARBITRUM}",chainId=42161} fantom={key="${ETHERSCAN_API_KEY_FANTOM}",chainId=250} base={key="${ETHERSCAN_API_KEY_BASE}",chainId=8453} +bnb={key="${ETHERSCAN_API_KEY_BNB}",chainId=56,url='https://api.bscscan.com/api'} # See more config options https://github.com/gakonst/foundry/tree/master/config diff --git a/lib/aave-helpers b/lib/aave-helpers index dc6e80f..428dc96 160000 --- a/lib/aave-helpers +++ b/lib/aave-helpers @@ -1 +1 @@ -Subproject commit dc6e80ffafd43565376ba63cff43659e6fc249d4 +Subproject commit 428dc96fb23d5858e3c5ecb6bd5e24d236613efc diff --git a/src/contracts/dependencies/paraswap/AugustusRegistry.sol b/src/contracts/dependencies/paraswap/AugustusRegistry.sol index 10b1136..a84b190 100644 --- a/src/contracts/dependencies/paraswap/AugustusRegistry.sol +++ b/src/contracts/dependencies/paraswap/AugustusRegistry.sol @@ -14,5 +14,7 @@ library AugustusRegistry { address public constant BSC = 0x05b4486f643914a818eD93Afc07457e9074be211; + address public constant BNB = 0x05b4486f643914a818eD93Afc07457e9074be211; + address public constant BASE = 0x7E31B336F9E8bA52ba3c4ac861b033Ba90900bb3; } diff --git a/src/script/Deploy_ParaSwapDebtSwapAdapter.s.sol b/src/script/Deploy_ParaSwapDebtSwapAdapter.s.sol index f0b63db..2bd8415 100644 --- a/src/script/Deploy_ParaSwapDebtSwapAdapter.s.sol +++ b/src/script/Deploy_ParaSwapDebtSwapAdapter.s.sol @@ -2,13 +2,14 @@ pragma solidity ^0.8.0; import {IPoolAddressesProvider} from '@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol'; -import {ArbitrumScript, EthereumScript, PolygonScript, AvalancheScript, OptimismScript, BaseScript} from 'aave-helpers/ScriptUtils.sol'; +import {ArbitrumScript, EthereumScript, PolygonScript, AvalancheScript, OptimismScript, BaseScript, BNBScript} from 'aave-helpers/ScriptUtils.sol'; import {GovernanceV3Ethereum} from 'aave-address-book/GovernanceV3Ethereum.sol'; import {GovernanceV3Polygon} from 'aave-address-book/GovernanceV3Polygon.sol'; import {GovernanceV3Avalanche} from 'aave-address-book/GovernanceV3Avalanche.sol'; import {GovernanceV3Arbitrum} from 'aave-address-book/GovernanceV3Arbitrum.sol'; import {GovernanceV3Optimism} from 'aave-address-book/GovernanceV3Optimism.sol'; import {GovernanceV3Base} from 'aave-address-book/GovernanceV3Base.sol'; +import {GovernanceV3BNB} from 'aave-address-book/GovernanceV3BNB.sol'; import {AaveV2Ethereum} from 'aave-address-book/AaveV2Ethereum.sol'; import {AaveV3Ethereum} from 'aave-address-book/AaveV3Ethereum.sol'; import {AaveV2Polygon} from 'aave-address-book/AaveV2Polygon.sol'; @@ -18,6 +19,7 @@ import {AaveV3Avalanche} from 'aave-address-book/AaveV3Avalanche.sol'; import {AaveV3Optimism} from 'aave-address-book/AaveV3Optimism.sol'; import {AaveV3Arbitrum} from 'aave-address-book/AaveV3Arbitrum.sol'; import {AaveV3Base} from 'aave-address-book/AaveV3Base.sol'; +import {AaveV3BNB} from 'aave-address-book/AaveV3BNB.sol'; import {ParaSwapDebtSwapAdapterV3} from 'src/contracts/ParaSwapDebtSwapAdapterV3.sol'; import {ParaSwapDebtSwapAdapterV3GHO} from 'src/contracts/ParaSwapDebtSwapAdapterV3GHO.sol'; import {ParaSwapDebtSwapAdapterV2} from 'src/contracts/ParaSwapDebtSwapAdapterV2.sol'; @@ -122,3 +124,15 @@ contract BaseV3 is BaseScript { ); } } + +contract BNBV3 is BNBScript { + function run() external broadcast { + new ParaSwapDebtSwapAdapterV3( + IPoolAddressesProvider(address(AaveV3BNB.POOL_ADDRESSES_PROVIDER)), + address(AaveV3BNB.POOL), + IParaSwapAugustusRegistry(AugustusRegistry.BNB), + GovernanceV3BNB.EXECUTOR_LVL_1 + ); + } +} + diff --git a/src/script/Deploy_ParaSwapLiquiditySwapAdapter.s.sol b/src/script/Deploy_ParaSwapLiquiditySwapAdapter.s.sol index b682e15..c984862 100644 --- a/src/script/Deploy_ParaSwapLiquiditySwapAdapter.s.sol +++ b/src/script/Deploy_ParaSwapLiquiditySwapAdapter.s.sol @@ -2,13 +2,14 @@ pragma solidity ^0.8.0; import {IPoolAddressesProvider} from '@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol'; -import {ArbitrumScript, EthereumScript, PolygonScript, AvalancheScript, OptimismScript, BaseScript} from 'aave-helpers/ScriptUtils.sol'; +import {ArbitrumScript, EthereumScript, PolygonScript, AvalancheScript, OptimismScript, BaseScript, BNBScript} from 'aave-helpers/ScriptUtils.sol'; import {GovernanceV3Ethereum} from 'aave-address-book/GovernanceV3Ethereum.sol'; import {GovernanceV3Polygon} from 'aave-address-book/GovernanceV3Polygon.sol'; import {GovernanceV3Avalanche} from 'aave-address-book/GovernanceV3Avalanche.sol'; import {GovernanceV3Arbitrum} from 'aave-address-book/GovernanceV3Arbitrum.sol'; import {GovernanceV3Optimism} from 'aave-address-book/GovernanceV3Optimism.sol'; import {GovernanceV3Base} from 'aave-address-book/GovernanceV3Base.sol'; +import {GovernanceV3BNB} from 'aave-address-book/GovernanceV3BNB.sol'; import {AaveV2Ethereum} from 'aave-address-book/AaveV2Ethereum.sol'; import {AaveV3Ethereum} from 'aave-address-book/AaveV3Ethereum.sol'; import {AaveV2Polygon} from 'aave-address-book/AaveV2Polygon.sol'; @@ -18,6 +19,7 @@ import {AaveV3Avalanche} from 'aave-address-book/AaveV3Avalanche.sol'; import {AaveV3Optimism} from 'aave-address-book/AaveV3Optimism.sol'; import {AaveV3Arbitrum} from 'aave-address-book/AaveV3Arbitrum.sol'; import {AaveV3Base} from 'aave-address-book/AaveV3Base.sol'; +import {AaveV3BNB} from 'aave-address-book/AaveV3BNB.sol'; import {ParaSwapLiquiditySwapAdapterV2} from 'src/contracts/ParaSwapLiquiditySwapAdapterV2.sol'; import {ParaSwapLiquiditySwapAdapterV3} from 'src/contracts/ParaSwapLiquiditySwapAdapterV3.sol'; import {IParaSwapAugustusRegistry} from 'src/contracts/dependencies/paraswap/IParaSwapAugustusRegistry.sol'; @@ -121,3 +123,14 @@ contract BaseV3 is BaseScript { ); } } + +contract BNBV3 is BNBScript { + function run() external broadcast { + new ParaSwapLiquiditySwapAdapterV3( + IPoolAddressesProvider(address(AaveV3BNB.POOL_ADDRESSES_PROVIDER)), + address(AaveV3BNB.POOL), + IParaSwapAugustusRegistry(AugustusRegistry.BNB), + GovernanceV3BNB.EXECUTOR_LVL_1 + ); + } +} diff --git a/src/script/Deploy_ParaSwapRepayAdapter.s.sol b/src/script/Deploy_ParaSwapRepayAdapter.s.sol index 791b3f3..0a2b12f 100644 --- a/src/script/Deploy_ParaSwapRepayAdapter.s.sol +++ b/src/script/Deploy_ParaSwapRepayAdapter.s.sol @@ -2,13 +2,14 @@ pragma solidity ^0.8.0; import {IPoolAddressesProvider} from '@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol'; -import {ArbitrumScript, EthereumScript, PolygonScript, AvalancheScript, OptimismScript, BaseScript} from 'aave-helpers/ScriptUtils.sol'; +import {ArbitrumScript, EthereumScript, PolygonScript, AvalancheScript, OptimismScript, BaseScript, BNBScript} from 'aave-helpers/ScriptUtils.sol'; import {GovernanceV3Ethereum} from 'aave-address-book/GovernanceV3Ethereum.sol'; import {GovernanceV3Polygon} from 'aave-address-book/GovernanceV3Polygon.sol'; import {GovernanceV3Avalanche} from 'aave-address-book/GovernanceV3Avalanche.sol'; import {GovernanceV3Arbitrum} from 'aave-address-book/GovernanceV3Arbitrum.sol'; import {GovernanceV3Optimism} from 'aave-address-book/GovernanceV3Optimism.sol'; import {GovernanceV3Base} from 'aave-address-book/GovernanceV3Base.sol'; +import {GovernanceV3BNB} from 'aave-address-book/GovernanceV3BNB.sol'; import {AaveV2Ethereum} from 'aave-address-book/AaveV2Ethereum.sol'; import {AaveV3Ethereum} from 'aave-address-book/AaveV3Ethereum.sol'; import {AaveV2Polygon} from 'aave-address-book/AaveV2Polygon.sol'; @@ -18,6 +19,7 @@ import {AaveV3Avalanche} from 'aave-address-book/AaveV3Avalanche.sol'; import {AaveV3Optimism} from 'aave-address-book/AaveV3Optimism.sol'; import {AaveV3Arbitrum} from 'aave-address-book/AaveV3Arbitrum.sol'; import {AaveV3Base} from 'aave-address-book/AaveV3Base.sol'; +import {AaveV3BNB} from 'aave-address-book/AaveV3BNB.sol'; import {ParaSwapRepayAdapterV2} from 'src/contracts/ParaSwapRepayAdapterV2.sol'; import {ParaSwapRepayAdapterV3} from 'src/contracts/ParaSwapRepayAdapterV3.sol'; import {IParaSwapAugustusRegistry} from 'src/contracts/dependencies/paraswap/IParaSwapAugustusRegistry.sol'; @@ -121,3 +123,14 @@ contract BaseV3 is BaseScript { ); } } + +contract BNBV3 is BNBScript { + function run() external broadcast { + new ParaSwapRepayAdapterV3( + IPoolAddressesProvider(address(AaveV3BNB.POOL_ADDRESSES_PROVIDER)), + address(AaveV3BNB.POOL), + IParaSwapAugustusRegistry(AugustusRegistry.BNB), + GovernanceV3BNB.EXECUTOR_LVL_1 + ); + } +} \ No newline at end of file diff --git a/src/script/Deploy_ParaSwapWithdrawAdapter.s.sol b/src/script/Deploy_ParaSwapWithdrawAdapter.s.sol index 2ef1c48..0e69467 100644 --- a/src/script/Deploy_ParaSwapWithdrawAdapter.s.sol +++ b/src/script/Deploy_ParaSwapWithdrawAdapter.s.sol @@ -2,13 +2,14 @@ pragma solidity ^0.8.0; import {IPoolAddressesProvider} from '@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol'; -import {ArbitrumScript, EthereumScript, PolygonScript, AvalancheScript, OptimismScript, BaseScript} from 'aave-helpers/ScriptUtils.sol'; +import {ArbitrumScript, EthereumScript, PolygonScript, AvalancheScript, OptimismScript, BaseScript, BNBScript} from 'aave-helpers/ScriptUtils.sol'; import {GovernanceV3Ethereum} from 'aave-address-book/GovernanceV3Ethereum.sol'; import {GovernanceV3Polygon} from 'aave-address-book/GovernanceV3Polygon.sol'; import {GovernanceV3Avalanche} from 'aave-address-book/GovernanceV3Avalanche.sol'; import {GovernanceV3Arbitrum} from 'aave-address-book/GovernanceV3Arbitrum.sol'; import {GovernanceV3Optimism} from 'aave-address-book/GovernanceV3Optimism.sol'; import {GovernanceV3Base} from 'aave-address-book/GovernanceV3Base.sol'; +import {GovernanceV3BNB} from 'aave-address-book/GovernanceV3BNB.sol'; import {AaveV2Ethereum} from 'aave-address-book/AaveV2Ethereum.sol'; import {AaveV3Ethereum} from 'aave-address-book/AaveV3Ethereum.sol'; import {AaveV2Polygon} from 'aave-address-book/AaveV2Polygon.sol'; @@ -18,6 +19,7 @@ import {AaveV3Avalanche} from 'aave-address-book/AaveV3Avalanche.sol'; import {AaveV3Optimism} from 'aave-address-book/AaveV3Optimism.sol'; import {AaveV3Arbitrum} from 'aave-address-book/AaveV3Arbitrum.sol'; import {AaveV3Base} from 'aave-address-book/AaveV3Base.sol'; +import {AaveV3BNB} from 'aave-address-book/AaveV3BNB.sol'; import {ParaSwapWithdrawSwapAdapterV2} from 'src/contracts/ParaSwapWithdrawSwapAdapterV2.sol'; import {ParaSwapWithdrawSwapAdapterV3} from 'src/contracts/ParaSwapWithdrawSwapAdapterV3.sol'; import {IParaSwapAugustusRegistry} from 'src/contracts/dependencies/paraswap/IParaSwapAugustusRegistry.sol'; @@ -121,3 +123,14 @@ contract BaseV3 is BaseScript { ); } } + +contract BNBV3 is BNBScript { + function run() external broadcast { + new ParaSwapWithdrawSwapAdapterV3( + IPoolAddressesProvider(address(AaveV3BNB.POOL_ADDRESSES_PROVIDER)), + address(AaveV3BNB.POOL), + IParaSwapAugustusRegistry(AugustusRegistry.BNB), + GovernanceV3BNB.EXECUTOR_LVL_1 + ); + } +}