diff --git a/README.md b/README.md index 0bf44bf..c13b104 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,35 @@ # BGD labs <> Aave Debt Swap Adapter This repository contains the [ParaSwapDebtSwapAdapter](./src/contracts/ParaSwapDebtSwapAdapter.sol), which 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 complete. +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 permit + - 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** The user has now payed off his `1000 BUSD` debt position, and created a new `1000.1 USDC` debt position. -The `function swapDebt( DebtSwapParams memory debtSwapParams, CreditDelegationInput memory creditDelegationPermit )` expects two parameters. +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 (requires `aToken` approval) +7. Use 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. + +In order to select adequate extra collateral asset and amount parameters, consider the extra collateral's LTV and supply cap. Where possible, it is recommended to use the from/to debt asset in order to reduce gas costs. + +The `function swapDebt(DebtSwapParams memory debtSwapParams, CreditDelegationInput memory creditDelegationPermit, PermitInput memory collateralATokenPermit)` expects three parameters. The first one describes the swap: @@ -26,6 +40,8 @@ struct DebtSwapParams { uint256 debtRateMode; // the type of debt (1 for stable, 2 for variable) address newDebtAsset; // the asset you want to swap to uint256 maxNewDebtAmount; // the max amount of debt your're willing to receive in excahnge for repaying debtRepayAmount + address extraCollateralAsset; // The asset to flash and add as collateral if needed + uint256 extraCollateralAmount; // The amount of `extraCollateralAsset` to flash and supply momentarily bytes paraswapData; // encoded exactOut swap } @@ -45,6 +61,19 @@ struct CreditDelegationInput { ``` +The third 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 diff --git a/src/contracts/BaseParaSwapAdapter.sol b/src/contracts/BaseParaSwapAdapter.sol index 406763f..932a5b1 100644 --- a/src/contracts/BaseParaSwapAdapter.sol +++ b/src/contracts/BaseParaSwapAdapter.sol @@ -80,8 +80,18 @@ abstract contract BaseParaSwapAdapter is IFlashLoanReceiverBase, Ownable { * @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); + 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 diff --git a/src/contracts/ParaSwapDebtSwapAdapter.sol b/src/contracts/ParaSwapDebtSwapAdapter.sol index 4f10227..fd20865 100644 --- a/src/contracts/ParaSwapDebtSwapAdapter.sol +++ b/src/contracts/ParaSwapDebtSwapAdapter.sol @@ -58,10 +58,12 @@ abstract contract ParaSwapDebtSwapAdapter is * 4. repay old debt * @param debtSwapParams the parameters describing the swap * @param creditDelegationPermit optional permit for credit delegation + * @param collateralATokenPermit optional permit for collateral aToken */ function swapDebt( DebtSwapParams memory debtSwapParams, - CreditDelegationInput memory creditDelegationPermit + CreditDelegationInput memory creditDelegationPermit, + PermitInput memory collateralATokenPermit ) external { uint256 excessBefore = IERC20Detailed(debtSwapParams.newDebtAsset).balanceOf(address(this)); // delegate credit @@ -76,49 +78,82 @@ abstract contract ParaSwapDebtSwapAdapter is creditDelegationPermit.s ); } - // flash & repay - if (debtSwapParams.debtRepayAmount == type(uint256).max) { - (address vToken, address sToken) = _getReserveData(debtSwapParams.debtAsset); - debtSwapParams.debtRepayAmount = debtSwapParams.debtRateMode == 2 - ? IERC20WithPermit(vToken).balanceOf(msg.sender) - : IERC20WithPermit(sToken).balanceOf(msg.sender); + // Default to the entire debt if an amount greater than it is passed. + (address vToken, address sToken, ) = _getReserveData(debtSwapParams.debtAsset); + uint256 maxDebtRepayAmount = debtSwapParams.debtRateMode == 2 + ? IERC20WithPermit(vToken).balanceOf(msg.sender) + : IERC20WithPermit(sToken).balanceOf(msg.sender); + + if (debtSwapParams.debtRepayAmount > maxDebtRepayAmount) { + debtSwapParams.debtRepayAmount = maxDebtRepayAmount; } FlashParams memory flashParams = FlashParams( debtSwapParams.debtAsset, debtSwapParams.debtRepayAmount, debtSwapParams.debtRateMode, + address(0), + 0, debtSwapParams.paraswapData, debtSwapParams.offset, msg.sender ); - _flash(flashParams, debtSwapParams); + + // If we need extra collateral, execute the flashloan with the collateral asset instead of the debt asset. + if (debtSwapParams.extraCollateralAsset != address(0)) { + // Permit collateral aToken if needed. + if (collateralATokenPermit.deadline != 0) { + collateralATokenPermit.aToken.permit( + msg.sender, + address(this), + collateralATokenPermit.value, + collateralATokenPermit.deadline, + collateralATokenPermit.v, + collateralATokenPermit.r, + collateralATokenPermit.s + ); + } + flashParams.nestedFlashloanDebtAsset = debtSwapParams.newDebtAsset; + flashParams.nestedFlashloanDebtAmount = debtSwapParams.maxNewDebtAmount; + // Execute the flashloan with the extra collateral asset. + _flash( + flashParams, + debtSwapParams.extraCollateralAsset, + debtSwapParams.extraCollateralAmount + ); + } else { + // Execute the flashloan with the debt asset. + _flash(flashParams, debtSwapParams.newDebtAsset, debtSwapParams.maxNewDebtAmount); + } + // use excess to repay parts of flash debt uint256 excessAfter = IERC20Detailed(debtSwapParams.newDebtAsset).balanceOf(address(this)); uint256 excess = excessAfter - excessBefore; if (excess > 0) { - uint256 allowance = IERC20(debtSwapParams.newDebtAsset).allowance( - address(this), - address(POOL) - ); - if (allowance < excess) { - renewAllowance(debtSwapParams.newDebtAsset); - } + _conditionalRenewAllowance(debtSwapParams.newDebtAsset, excess); POOL.repay(debtSwapParams.newDebtAsset, excess, 2, msg.sender); } } - function _flash( - FlashParams memory flashParams, - DebtSwapParams memory debtSwapParams - ) internal virtual { + function _flash(FlashParams memory flashParams, address asset, uint256 amount) internal virtual { bytes memory params = abi.encode(flashParams); + address[] memory assets = new address[](1); - assets[0] = debtSwapParams.newDebtAsset; + assets[0] = asset; uint256[] memory amounts = new uint256[](1); - amounts[0] = debtSwapParams.maxNewDebtAmount; + amounts[0] = amount; uint256[] memory interestRateModes = new uint256[](1); - interestRateModes[0] = 2; - POOL.flashLoan(address(this), assets, amounts, interestRateModes, msg.sender, params, REFERRER); + // This is only true if there is no need for extra collateral. + interestRateModes[0] = flashParams.nestedFlashloanDebtAsset == address(0) ? 2 : 0; + + POOL.flashLoan( + address(this), + assets, + amounts, + interestRateModes, + flashParams.user, + params, + REFERRER + ); } /** @@ -141,9 +176,34 @@ abstract contract ParaSwapDebtSwapAdapter is require(msg.sender == address(POOL), 'CALLER_MUST_BE_POOL'); require(initiator == address(this), 'INITIATOR_MUST_BE_THIS'); - FlashParams memory swapParams = abi.decode(params, (FlashParams)); - _swapAndRepay(swapParams, IERC20Detailed(assets[0]), amounts[0]); + FlashParams memory flashParams = abi.decode(params, (FlashParams)); + + // This is only non-zero if we flashed extra collateral. + if (flashParams.nestedFlashloanDebtAsset != address(0)) { + // Wrap the swap with a supply and withdraw. + address collateralAsset = assets[0]; + uint256 collateralAmount = amounts[0]; + + // Supply + _supply(collateralAsset, collateralAmount, flashParams.user, REFERRER); + // Execute the nested flashloan + address newAsset = flashParams.nestedFlashloanDebtAsset; + flashParams.nestedFlashloanDebtAsset = address(0); + _flash(flashParams, newAsset, flashParams.nestedFlashloanDebtAmount); + + // Fetch and transfer back in the aToken to allow the pool to pull it. + (, , address aToken) = _getReserveData(collateralAsset); + IERC20WithPermit(aToken).safeTransferFrom(flashParams.user, address(this), collateralAmount); // Could be rounding error but it's insignificant + POOL.withdraw(collateralAsset, collateralAmount, address(this)); + _conditionalRenewAllowance(collateralAsset, collateralAmount); + + // Return out of this scope. + return true; + } + + // There is no need for additional collateral, execute the swap. + _swapAndRepay(flashParams, IERC20Detailed(assets[0]), amounts[0]); return true; } @@ -167,10 +227,7 @@ abstract contract ParaSwapDebtSwapAdapter is swapParams.debtRepayAmount ); - uint256 allowance = IERC20(swapParams.debtAsset).allowance(address(this), address(POOL)); - if (allowance < swapParams.debtRepayAmount) { - renewAllowance(address(swapParams.debtAsset)); - } + _conditionalRenewAllowance(swapParams.debtAsset, swapParams.debtRepayAmount); POOL.repay( address(swapParams.debtAsset), @@ -180,4 +237,11 @@ 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/ParaSwapDebtSwapAdapterV2.sol b/src/contracts/ParaSwapDebtSwapAdapterV2.sol index d3d5483..2a6caea 100644 --- a/src/contracts/ParaSwapDebtSwapAdapterV2.sol +++ b/src/contracts/ParaSwapDebtSwapAdapterV2.sol @@ -19,8 +19,21 @@ contract ParaSwapDebtSwapAdapterV2 is ParaSwapDebtSwapAdapter { address owner ) ParaSwapDebtSwapAdapter(addressesProvider, pool, augustusRegistry, owner) {} - function _getReserveData(address asset) internal view override returns (address, address) { + 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); + return ( + reserveData.variableDebtTokenAddress, + reserveData.stableDebtTokenAddress, + reserveData.aTokenAddress + ); + } + + 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/ParaSwapDebtSwapAdapterV3.sol b/src/contracts/ParaSwapDebtSwapAdapterV3.sol index 9b6f7ab..9ae344c 100644 --- a/src/contracts/ParaSwapDebtSwapAdapterV3.sol +++ b/src/contracts/ParaSwapDebtSwapAdapterV3.sol @@ -20,8 +20,23 @@ contract ParaSwapDebtSwapAdapterV3 is ParaSwapDebtSwapAdapter { address owner ) ParaSwapDebtSwapAdapter(addressesProvider, pool, augustusRegistry, owner) {} - function _getReserveData(address asset) internal view override returns (address, address) { + function _getReserveData( + address asset + ) internal view override returns (address, address, address) { DataTypes.ReserveData memory reserveData = POOL.getReserveData(asset); - return (reserveData.variableDebtTokenAddress, reserveData.stableDebtTokenAddress); + return ( + reserveData.variableDebtTokenAddress, + reserveData.stableDebtTokenAddress, + reserveData.aTokenAddress + ); + } + + function _supply( + address asset, + uint256 amount, + address to, + uint16 referralCode + ) internal override { + POOL.supply(asset, amount, to, referralCode); } } diff --git a/src/contracts/ParaSwapDebtSwapAdapterV3GHO.sol b/src/contracts/ParaSwapDebtSwapAdapterV3GHO.sol index 0a85572..a1da142 100644 --- a/src/contracts/ParaSwapDebtSwapAdapterV3GHO.sol +++ b/src/contracts/ParaSwapDebtSwapAdapterV3GHO.sol @@ -11,6 +11,7 @@ import {IParaSwapAugustusRegistry} from '../interfaces/IParaSwapAugustusRegistry import {IERC3156FlashBorrower} from '../interfaces/IERC3156FlashBorrower.sol'; import {IERC3156FlashLender} from '../interfaces/IERC3156FlashLender.sol'; +// send collateral if needed via params /** * @title ParaSwapDebtSwapAdapter * @notice ParaSwap Adapter to perform a swap of debt to another debt. @@ -50,19 +51,16 @@ contract ParaSwapDebtSwapAdapterV3GHO is ParaSwapDebtSwapAdapterV3, IERC3156Flas return keccak256('ERC3156FlashBorrower.onFlashLoan'); } - function _flash( - FlashParams memory flashParams, - DebtSwapParams memory debtSwapParams - ) internal override { - if (debtSwapParams.newDebtAsset == GHO) { + function _flash(FlashParams memory flashParams, address asset, uint256 amount) internal override { + if (asset == GHO) { GHO_FLASH_MINTER.flashLoan( IERC3156FlashBorrower(address(this)), GHO, - debtSwapParams.maxNewDebtAmount, + amount, abi.encode(flashParams) ); } else { - super._flash(flashParams, debtSwapParams); + super._flash(flashParams, asset, amount); } } } diff --git a/src/interfaces/IParaswapDebtSwapAdapter.sol b/src/interfaces/IParaswapDebtSwapAdapter.sol index 2b3a310..c187636 100644 --- a/src/interfaces/IParaswapDebtSwapAdapter.sol +++ b/src/interfaces/IParaswapDebtSwapAdapter.sol @@ -1,12 +1,15 @@ // 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; @@ -18,6 +21,8 @@ interface IParaswapDebtSwapAdapter { uint256 debtRateMode; address newDebtAsset; uint256 maxNewDebtAmount; + address extraCollateralAsset; + uint256 extraCollateralAmount; uint256 offset; bytes paraswapData; } @@ -31,13 +36,24 @@ interface IParaswapDebtSwapAdapter { 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 + CreditDelegationInput memory creditDelegationPermit, + PermitInput memory collateralATokenPermit ) external; } diff --git a/tests/DebtSwapV2.t.sol b/tests/DebtSwapV2.t.sol index d34ba54..f4842da 100644 --- a/tests/DebtSwapV2.t.sol +++ b/tests/DebtSwapV2.t.sol @@ -3,6 +3,7 @@ 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 {Errors} from 'aave-helpers/dependencies/Errors.sol'; import {AaveGovernanceV2} from 'aave-address-book/AaveGovernanceV2.sol'; import {AaveV2Ethereum, AaveV2EthereumAssets, ILendingPool} from 'aave-address-book/AaveV2Ethereum.sol'; import {BaseTest} from './utils/BaseTest.sol'; @@ -27,6 +28,97 @@ contract DebtSwapV2Test is BaseTest { ); } + 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')); + debtSwapAdapter.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')); + debtSwapAdapter.executeOperation( + mockAddresses, + mockAmounts, + mockAmounts, + address(0), + abi.encode('') + ); + } + + function test_revert_debtSwap_without_extra_collateral() public { + address aToken = AaveV2EthereumAssets.DAI_A_TOKEN; + address debtAsset = AaveV2EthereumAssets.DAI_UNDERLYING; + address newDebtAsset = AaveV2EthereumAssets.LUSD_UNDERLYING; + address newDebtToken = AaveV2EthereumAssets.LUSD_V_TOKEN; + + uint256 supplyAmount = 120e18; + uint256 borrowAmount = 80e18; + + // 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; + + // Deal some debtAsset to cover the premium and any 1 wei rounding errors on withdrawal. + deal(debtAsset, address(debtSwapAdapter), 1e18); + + vm.startPrank(user); + + _supply(AaveV2Ethereum.POOL, supplyAmount, debtAsset); + _borrow(AaveV2Ethereum.POOL, borrowAmount, debtAsset); + + _withdraw(AaveV2Ethereum.POOL, withdrawAmount, debtAsset); + + vm.expectRevert(bytes(Errors.VL_COLLATERAL_CANNOT_COVER_NEW_BORROW)); + _borrow(AaveV2Ethereum.POOL, 1, debtAsset); + + // Swap debt + // add some margin to account for accumulated debt + uint256 repayAmount = (borrowAmount * 101) / 100; + PsPResponse memory psp = _fetchPSPRoute( + newDebtAsset, + debtAsset, + repayAmount, + user, + false, + true + ); + + skip(1 hours); + + ICreditDelegationToken(newDebtToken).approveDelegation(address(debtSwapAdapter), psp.srcAmount); + IERC20Detailed(aToken).approve(address(debtSwapAdapter), supplyAmount); + + IParaswapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaswapDebtSwapAdapter + .DebtSwapParams({ + debtAsset: debtAsset, + debtRepayAmount: type(uint256).max, + debtRateMode: 2, + newDebtAsset: newDebtAsset, + maxNewDebtAmount: psp.srcAmount, + extraCollateralAsset: address(0), + extraCollateralAmount: 0, + offset: psp.offset, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IParaswapDebtSwapAdapter.CreditDelegationInput memory cd; + IParaswapDebtSwapAdapter.PermitInput memory collateralATokenPermit; + + vm.expectRevert(bytes(Errors.VL_COLLATERAL_CANNOT_COVER_NEW_BORROW)); + debtSwapAdapter.swapDebt(debtSwapParams, cd, collateralATokenPermit); + } + /** * 1. supply 200000 DAI * 2. borrow 1000 DAI @@ -64,14 +156,18 @@ contract DebtSwapV2Test is BaseTest { debtRateMode: 2, newDebtAsset: newDebtAsset, maxNewDebtAmount: psp.srcAmount, - paraswapData: abi.encode(psp.swapCalldata, psp.augustus), - offset: psp.offset + extraCollateralAsset: address(0), + extraCollateralAmount: 0, + offset: psp.offset, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) }); uint256 vDEBT_TOKENBalanceBefore = IERC20Detailed(debtToken).balanceOf(user); IParaswapDebtSwapAdapter.CreditDelegationInput memory cd; - debtSwapAdapter.swapDebt(debtSwapParams, cd); + IParaswapDebtSwapAdapter.PermitInput memory collateralATokenPermit; + + debtSwapAdapter.swapDebt(debtSwapParams, cd, collateralATokenPermit); uint256 vDEBT_TOKENBalanceAfter = IERC20Detailed(debtToken).balanceOf(user); uint256 vNEWDEBT_TOKENBalanceAfter = IERC20Detailed(newDebtToken).balanceOf(user); @@ -115,12 +211,16 @@ contract DebtSwapV2Test is BaseTest { debtRateMode: 2, newDebtAsset: newDebtAsset, maxNewDebtAmount: psp.srcAmount, - paraswapData: abi.encode(psp.swapCalldata, psp.augustus), - offset: psp.offset + extraCollateralAsset: address(0), + extraCollateralAmount: 0, + offset: psp.offset, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) }); IParaswapDebtSwapAdapter.CreditDelegationInput memory cd; - debtSwapAdapter.swapDebt(debtSwapParams, cd); + IParaswapDebtSwapAdapter.PermitInput memory collateralATokenPermit; + + debtSwapAdapter.swapDebt(debtSwapParams, cd, collateralATokenPermit); uint256 vDEBT_TOKENBalanceAfter = IERC20Detailed(debtToken).balanceOf(user); uint256 vNEWDEBT_TOKENBalanceAfter = IERC20Detailed(newDebtToken).balanceOf(user); @@ -161,12 +261,16 @@ contract DebtSwapV2Test is BaseTest { debtRateMode: 2, newDebtAsset: newDebtAsset, maxNewDebtAmount: psp.srcAmount, - paraswapData: abi.encode(psp.swapCalldata, psp.augustus), - offset: psp.offset + extraCollateralAsset: address(0), + extraCollateralAmount: 0, + offset: psp.offset, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) }); IParaswapDebtSwapAdapter.CreditDelegationInput memory cd; - debtSwapAdapter.swapDebt(debtSwapParams, cd); + IParaswapDebtSwapAdapter.PermitInput memory collateralATokenPermit; + + debtSwapAdapter.swapDebt(debtSwapParams, cd, collateralATokenPermit); uint256 vDEBT_TOKENBalanceAfter = IERC20Detailed(debtToken).balanceOf(vBUSD_WHALE); uint256 vNEWDEBT_TOKENBalanceAfter = IERC20Detailed(newDebtToken).balanceOf(vBUSD_WHALE); @@ -175,6 +279,205 @@ contract DebtSwapV2Test is BaseTest { _invariant(address(debtSwapAdapter), debtAsset, newDebtAsset); } + function test_debtSwap_extra_Collateral() public { + address debtAsset = AaveV2EthereumAssets.DAI_UNDERLYING; + address newDebtAsset = AaveV2EthereumAssets.LUSD_UNDERLYING; + address newDebtToken = AaveV2EthereumAssets.LUSD_V_TOKEN; + address extraCollateralAsset = debtAsset; + address extraCollateralAToken = AaveV2EthereumAssets.DAI_A_TOKEN; + + uint256 supplyAmount = 120e18; + uint256 borrowAmount = 80e18; + uint256 extraCollateralAmount = 1000e18; + + // 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; + + // Deal some debtAsset to cover the premium and any 1 wei rounding errors on withdrawal. + deal(debtAsset, address(debtSwapAdapter), 1e18); + + vm.startPrank(user); + + _supply(AaveV2Ethereum.POOL, supplyAmount, debtAsset); + _borrow(AaveV2Ethereum.POOL, borrowAmount, debtAsset); + + _withdraw(AaveV2Ethereum.POOL, withdrawAmount, debtAsset); + + vm.expectRevert(bytes(Errors.VL_COLLATERAL_CANNOT_COVER_NEW_BORROW)); + _borrow(AaveV2Ethereum.POOL, 1, debtAsset); + + // Swap debt + // add some margin to account for accumulated debt + uint256 repayAmount = (borrowAmount * 101) / 100; + PsPResponse memory psp = _fetchPSPRoute( + newDebtAsset, + debtAsset, + repayAmount, + user, + false, + true + ); + + skip(1 hours); + + ICreditDelegationToken(newDebtToken).approveDelegation(address(debtSwapAdapter), psp.srcAmount); + IERC20Detailed(extraCollateralAToken).approve( + address(debtSwapAdapter), + extraCollateralAmount + 1 + ); + + IParaswapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaswapDebtSwapAdapter + .DebtSwapParams({ + debtAsset: debtAsset, + debtRepayAmount: type(uint256).max, + debtRateMode: 2, + newDebtAsset: newDebtAsset, + maxNewDebtAmount: psp.srcAmount, + extraCollateralAsset: extraCollateralAsset, + extraCollateralAmount: extraCollateralAmount, + offset: psp.offset, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IParaswapDebtSwapAdapter.CreditDelegationInput memory cd; + IParaswapDebtSwapAdapter.PermitInput memory collateralATokenPermit; + + debtSwapAdapter.swapDebt(debtSwapParams, cd, collateralATokenPermit); + } + + function test_debtSwap_extra_Collateral_permit() public { + address debtAsset = AaveV2EthereumAssets.DAI_UNDERLYING; + address newDebtAsset = AaveV2EthereumAssets.LUSD_UNDERLYING; + address newDebtToken = AaveV2EthereumAssets.LUSD_V_TOKEN; + address extraCollateralAsset = debtAsset; + address extraCollateralAToken = AaveV2EthereumAssets.DAI_A_TOKEN; + + uint256 supplyAmount = 120e18; + uint256 borrowAmount = 80e18; + uint256 extraCollateralAmount = 1000e18; + + // 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; + + // Deal some debtAsset to cover the premium and any 1 wei rounding errors on withdrawal. + deal(debtAsset, address(debtSwapAdapter), 1e18); + + vm.startPrank(user); + + _supply(AaveV2Ethereum.POOL, supplyAmount, debtAsset); + _borrow(AaveV2Ethereum.POOL, borrowAmount, debtAsset); + + _withdraw(AaveV2Ethereum.POOL, withdrawAmount, debtAsset); + + vm.expectRevert(bytes(Errors.VL_COLLATERAL_CANNOT_COVER_NEW_BORROW)); + _borrow(AaveV2Ethereum.POOL, 1, debtAsset); + + // Swap debt + // add some margin to account for accumulated debt + uint256 repayAmount = (borrowAmount * 101) / 100; + PsPResponse memory psp = _fetchPSPRoute( + newDebtAsset, + debtAsset, + repayAmount, + user, + false, + true + ); + + skip(1 hours); + + ICreditDelegationToken(newDebtToken).approveDelegation(address(debtSwapAdapter), psp.srcAmount); + + IParaswapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaswapDebtSwapAdapter + .DebtSwapParams({ + debtAsset: debtAsset, + debtRepayAmount: type(uint256).max, + debtRateMode: 2, + newDebtAsset: newDebtAsset, + maxNewDebtAmount: psp.srcAmount, + extraCollateralAsset: extraCollateralAsset, + extraCollateralAmount: extraCollateralAmount, + offset: psp.offset, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IParaswapDebtSwapAdapter.CreditDelegationInput memory cd; + IParaswapDebtSwapAdapter.PermitInput memory collateralATokenPermit = _getPermit( + extraCollateralAToken, + address(debtSwapAdapter), + extraCollateralAmount + 1 + ); + + debtSwapAdapter.swapDebt(debtSwapParams, cd, collateralATokenPermit); + } + + function test_debtSwap_extra_Collateral_same_as_new_debt() public { + // We'll use the debtAsset & supplyAmount as extra collateral too. + address debtAsset = AaveV2EthereumAssets.DAI_UNDERLYING; + address newDebtAsset = AaveV2EthereumAssets.USDC_UNDERLYING; + address newDebtToken = AaveV2EthereumAssets.USDC_V_TOKEN; + address extraCollateralAsset = newDebtAsset; + address extraCollateralAToken = AaveV2EthereumAssets.USDC_A_TOKEN; + + uint256 supplyAmount = 120e18; + uint256 borrowAmount = 80e18; + uint256 extraCollateralAmount = 1000e6; + + // 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; + + // Deal some debtAsset to cover the premium and any 1 wei rounding errors on withdrawal. + deal(debtAsset, address(debtSwapAdapter), 1e18); + + vm.startPrank(user); + + _supply(AaveV2Ethereum.POOL, supplyAmount, debtAsset); + _borrow(AaveV2Ethereum.POOL, borrowAmount, debtAsset); + + _withdraw(AaveV2Ethereum.POOL, withdrawAmount, debtAsset); + + vm.expectRevert(bytes(Errors.VL_COLLATERAL_CANNOT_COVER_NEW_BORROW)); + _borrow(AaveV2Ethereum.POOL, 1, debtAsset); + + // Swap debt + // add some margin to account for accumulated debt + uint256 repayAmount = (borrowAmount * 101) / 100; + PsPResponse memory psp = _fetchPSPRoute( + newDebtAsset, + debtAsset, + repayAmount, + user, + false, + true + ); + + skip(1 hours); + + ICreditDelegationToken(newDebtToken).approveDelegation(address(debtSwapAdapter), psp.srcAmount); + IERC20Detailed(extraCollateralAToken).approve( + address(debtSwapAdapter), + extraCollateralAmount + 1 + ); + + IParaswapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaswapDebtSwapAdapter + .DebtSwapParams({ + debtAsset: debtAsset, + debtRepayAmount: type(uint256).max, + debtRateMode: 2, + newDebtAsset: newDebtAsset, + maxNewDebtAmount: psp.srcAmount, + extraCollateralAsset: extraCollateralAsset, + extraCollateralAmount: extraCollateralAmount, + offset: psp.offset, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IParaswapDebtSwapAdapter.CreditDelegationInput memory cd; + IParaswapDebtSwapAdapter.PermitInput memory collateralATokenPermit; + + debtSwapAdapter.swapDebt(debtSwapParams, cd, collateralATokenPermit); + } + function _supply(ILendingPool pool, uint256 amount, address asset) internal { deal(asset, user, amount); IERC20Detailed(asset).approve(address(pool), amount); @@ -184,4 +487,8 @@ contract DebtSwapV2Test is BaseTest { 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/tests/DebtSwapV3.t.sol b/tests/DebtSwapV3.t.sol index 3106c5a..bc10416 100644 --- a/tests/DebtSwapV3.t.sol +++ b/tests/DebtSwapV3.t.sol @@ -4,6 +4,7 @@ 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 {AaveGovernanceV2} from 'aave-address-book/AaveGovernanceV2.sol'; import {AaveV3Ethereum, AaveV3EthereumAssets, IPool} from 'aave-address-book/AaveV3Ethereum.sol'; import {BaseTest} from './utils/BaseTest.sol'; @@ -19,7 +20,7 @@ contract DebtSwapV3Test is BaseTest { function setUp() public override { super.setUp(); - vm.createSelectFork(vm.rpcUrl('mainnet'), 17706797); + vm.createSelectFork(vm.rpcUrl('mainnet'), 17786869); debtSwapAdapter = new ParaSwapDebtSwapAdapterV3( IPoolAddressesProvider(address(AaveV3Ethereum.POOL_ADDRESSES_PROVIDER)), @@ -29,6 +30,97 @@ contract DebtSwapV3Test is BaseTest { ); } + 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')); + debtSwapAdapter.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')); + debtSwapAdapter.executeOperation( + mockAddresses, + mockAmounts, + mockAmounts, + address(0), + abi.encode('') + ); + } + + function test_revert_debtSwap_without_extra_collateral() public { + address aToken = AaveV3EthereumAssets.DAI_A_TOKEN; + address debtAsset = AaveV3EthereumAssets.DAI_UNDERLYING; + address newDebtAsset = AaveV3EthereumAssets.LUSD_UNDERLYING; + address newDebtToken = AaveV3EthereumAssets.LUSD_V_TOKEN; + + uint256 supplyAmount = 120e18; + uint256 borrowAmount = 80e18; + + // 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; + + // Deal some debtAsset to cover the premium and any 1 wei rounding errors on withdrawal. + deal(debtAsset, address(debtSwapAdapter), 1e18); + + vm.startPrank(user); + + _supply(AaveV3Ethereum.POOL, supplyAmount, debtAsset); + _borrow(AaveV3Ethereum.POOL, borrowAmount, debtAsset); + + _withdraw(AaveV3Ethereum.POOL, withdrawAmount, debtAsset); + + vm.expectRevert(bytes(Errors.COLLATERAL_CANNOT_COVER_NEW_BORROW)); + _borrow(AaveV3Ethereum.POOL, 1, debtAsset); + + // Swap debt + // add some margin to account for accumulated debt + uint256 repayAmount = (borrowAmount * 101) / 100; + PsPResponse memory psp = _fetchPSPRoute( + newDebtAsset, + debtAsset, + repayAmount, + user, + false, + true + ); + + skip(1 hours); + + ICreditDelegationToken(newDebtToken).approveDelegation(address(debtSwapAdapter), psp.srcAmount); + IERC20Detailed(aToken).approve(address(debtSwapAdapter), supplyAmount); + + IParaswapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaswapDebtSwapAdapter + .DebtSwapParams({ + debtAsset: debtAsset, + debtRepayAmount: type(uint256).max, + debtRateMode: 2, + newDebtAsset: newDebtAsset, + maxNewDebtAmount: psp.srcAmount, + extraCollateralAsset: address(0), + extraCollateralAmount: 0, + offset: psp.offset, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IParaswapDebtSwapAdapter.CreditDelegationInput memory cd; + IParaswapDebtSwapAdapter.PermitInput memory collateralATokenPermit; + + vm.expectRevert(bytes(Errors.COLLATERAL_CANNOT_COVER_NEW_BORROW)); + debtSwapAdapter.swapDebt(debtSwapParams, cd, collateralATokenPermit); + } + /** * 1. supply 200000 DAI * 2. borrow 100 DAI @@ -66,14 +158,18 @@ contract DebtSwapV3Test is BaseTest { debtRateMode: 2, newDebtAsset: newDebtAsset, maxNewDebtAmount: psp.srcAmount, - paraswapData: abi.encode(psp.swapCalldata, psp.augustus), - offset: psp.offset + extraCollateralAsset: address(0), + extraCollateralAmount: 0, + offset: psp.offset, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) }); uint256 vDEBT_TOKENBalanceBefore = IERC20Detailed(debtToken).balanceOf(user); IParaswapDebtSwapAdapter.CreditDelegationInput memory cd; - debtSwapAdapter.swapDebt(debtSwapParams, cd); + IParaswapDebtSwapAdapter.PermitInput memory collateralATokenPermit; + + debtSwapAdapter.swapDebt(debtSwapParams, cd, collateralATokenPermit); uint256 vDEBT_TOKENBalanceAfter = IERC20Detailed(debtToken).balanceOf(user); uint256 vNEWDEBT_TOKENBalanceAfter = IERC20Detailed(newDebtToken).balanceOf(user); @@ -117,12 +213,94 @@ contract DebtSwapV3Test is BaseTest { debtRateMode: 2, newDebtAsset: newDebtAsset, maxNewDebtAmount: psp.srcAmount, - paraswapData: abi.encode(psp.swapCalldata, psp.augustus), - offset: psp.offset + extraCollateralAsset: address(0), + extraCollateralAmount: 0, + offset: psp.offset, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) }); IParaswapDebtSwapAdapter.CreditDelegationInput memory cd; - debtSwapAdapter.swapDebt(debtSwapParams, cd); + IParaswapDebtSwapAdapter.PermitInput memory collateralATokenPermit; + + debtSwapAdapter.swapDebt(debtSwapParams, cd, collateralATokenPermit); + + uint256 vDEBT_TOKENBalanceAfter = IERC20Detailed(debtToken).balanceOf(user); + uint256 vNEWDEBT_TOKENBalanceAfter = IERC20Detailed(newDebtToken).balanceOf(user); + assertEq(vDEBT_TOKENBalanceAfter, 0); + assertLe(vNEWDEBT_TOKENBalanceAfter, psp.srcAmount); + _invariant(address(debtSwapAdapter), debtAsset, newDebtAsset); + } + + function test_debtSwap_swapAll_lacking_allowance() public { + vm.startPrank(user); + address debtAsset = AaveV3EthereumAssets.DAI_UNDERLYING; + address debtToken = AaveV3EthereumAssets.DAI_V_TOKEN; + address newDebtAsset = AaveV3EthereumAssets.LUSD_UNDERLYING; + address newDebtToken = AaveV3EthereumAssets.LUSD_V_TOKEN; + + uint256 supplyAmount = 200000 ether; + uint256 borrowAmount = 1000 ether; + + { + // Pre + assertEq( + IERC20WithPermit(debtAsset).allowance( + address(debtSwapAdapter), + address(AaveV3Ethereum.POOL) + ), + type(uint256).max + ); + + vm.record(); + IERC20WithPermit(debtAsset).allowance(address(debtSwapAdapter), address(AaveV3Ethereum.POOL)); + (bytes32[] memory reads, ) = vm.accesses(AaveV3EthereumAssets.DAI_UNDERLYING); + vm.store(address(debtAsset), reads[0], 0); + + // Post + assertEq( + IERC20WithPermit(debtAsset).allowance( + address(debtSwapAdapter), + address(AaveV3Ethereum.POOL) + ), + 0 + ); + } + + _supply(AaveV3Ethereum.POOL, supplyAmount, debtAsset); + _borrow(AaveV3Ethereum.POOL, borrowAmount, debtAsset); + + // add some margin to account for accumulated debt + uint256 repayAmount = (borrowAmount * 101) / 100; + PsPResponse memory psp = _fetchPSPRoute( + newDebtAsset, + debtAsset, + repayAmount, + user, + false, + true + ); + + skip(1 hours); + + ICreditDelegationToken(newDebtToken).approveDelegation(address(debtSwapAdapter), psp.srcAmount); + + IParaswapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaswapDebtSwapAdapter + .DebtSwapParams({ + debtAsset: debtAsset, + debtRepayAmount: type(uint256).max, + debtRateMode: 2, + newDebtAsset: newDebtAsset, + maxNewDebtAmount: psp.srcAmount, + extraCollateralAsset: address(0), + extraCollateralAmount: 0, + offset: psp.offset, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IParaswapDebtSwapAdapter.CreditDelegationInput memory cd; + IParaswapDebtSwapAdapter.PermitInput memory collateralATokenPermit; + + debtSwapAdapter.swapDebt(debtSwapParams, cd, collateralATokenPermit); uint256 vDEBT_TOKENBalanceAfter = IERC20Detailed(debtToken).balanceOf(user); uint256 vNEWDEBT_TOKENBalanceAfter = IERC20Detailed(newDebtToken).balanceOf(user); @@ -164,16 +342,19 @@ contract DebtSwapV3Test is BaseTest { debtRateMode: 2, newDebtAsset: newDebtAsset, maxNewDebtAmount: psp.srcAmount, - paraswapData: abi.encode(psp.swapCalldata, psp.augustus), - offset: psp.offset + extraCollateralAsset: address(0), + extraCollateralAmount: 0, + offset: psp.offset, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) }); IParaswapDebtSwapAdapter.CreditDelegationInput memory cd = _getCDPermit( psp.srcAmount, newDebtToken ); + IParaswapDebtSwapAdapter.PermitInput memory collateralATokenPermit; - debtSwapAdapter.swapDebt(debtSwapParams, cd); + debtSwapAdapter.swapDebt(debtSwapParams, cd, collateralATokenPermit); uint256 vDEBT_TOKENBalanceAfter = IERC20Detailed(debtToken).balanceOf(user); uint256 vNEWDEBT_TOKENBalanceAfter = IERC20Detailed(newDebtToken).balanceOf(user); @@ -182,6 +363,205 @@ contract DebtSwapV3Test is BaseTest { _invariant(address(debtSwapAdapter), debtAsset, newDebtAsset); } + function test_debtSwap_extra_Collateral() public { + address debtAsset = AaveV3EthereumAssets.DAI_UNDERLYING; + address newDebtAsset = AaveV3EthereumAssets.LUSD_UNDERLYING; + address newDebtToken = AaveV3EthereumAssets.LUSD_V_TOKEN; + address extraCollateralAsset = debtAsset; + address extraCollateralAToken = AaveV3EthereumAssets.DAI_A_TOKEN; + + uint256 supplyAmount = 120e18; + uint256 borrowAmount = 80e18; + uint256 extraCollateralAmount = 1000e18; + + // 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; + + // Deal some debtAsset to cover the premium and any 1 wei rounding errors on withdrawal. + deal(debtAsset, address(debtSwapAdapter), 1e18); + + vm.startPrank(user); + + _supply(AaveV3Ethereum.POOL, supplyAmount, debtAsset); + _borrow(AaveV3Ethereum.POOL, borrowAmount, debtAsset); + + _withdraw(AaveV3Ethereum.POOL, withdrawAmount, debtAsset); + + vm.expectRevert(bytes(Errors.COLLATERAL_CANNOT_COVER_NEW_BORROW)); + _borrow(AaveV3Ethereum.POOL, 1, debtAsset); + + // Swap debt + // add some margin to account for accumulated debt + uint256 repayAmount = (borrowAmount * 101) / 100; + PsPResponse memory psp = _fetchPSPRoute( + newDebtAsset, + debtAsset, + repayAmount, + user, + false, + true + ); + + skip(1 hours); + + ICreditDelegationToken(newDebtToken).approveDelegation(address(debtSwapAdapter), psp.srcAmount); + IERC20Detailed(extraCollateralAToken).approve( + address(debtSwapAdapter), + extraCollateralAmount + 1 + ); + + IParaswapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaswapDebtSwapAdapter + .DebtSwapParams({ + debtAsset: debtAsset, + debtRepayAmount: type(uint256).max, + debtRateMode: 2, + newDebtAsset: newDebtAsset, + maxNewDebtAmount: psp.srcAmount, + extraCollateralAsset: extraCollateralAsset, + extraCollateralAmount: extraCollateralAmount, + offset: psp.offset, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IParaswapDebtSwapAdapter.CreditDelegationInput memory cd; + IParaswapDebtSwapAdapter.PermitInput memory collateralATokenPermit; + + debtSwapAdapter.swapDebt(debtSwapParams, cd, collateralATokenPermit); + } + + function test_debtSwap_extra_Collateral_permit() public { + address debtAsset = AaveV3EthereumAssets.DAI_UNDERLYING; + address newDebtAsset = AaveV3EthereumAssets.LUSD_UNDERLYING; + address newDebtToken = AaveV3EthereumAssets.LUSD_V_TOKEN; + address extraCollateralAsset = debtAsset; + address extraCollateralAToken = AaveV3EthereumAssets.DAI_A_TOKEN; + + uint256 supplyAmount = 120e18; + uint256 borrowAmount = 80e18; + uint256 extraCollateralAmount = 1000e18; + + // 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; + + // Deal some debtAsset to cover the premium and any 1 wei rounding errors on withdrawal. + deal(debtAsset, address(debtSwapAdapter), 1e18); + + vm.startPrank(user); + + _supply(AaveV3Ethereum.POOL, supplyAmount, debtAsset); + _borrow(AaveV3Ethereum.POOL, borrowAmount, debtAsset); + + _withdraw(AaveV3Ethereum.POOL, withdrawAmount, debtAsset); + + vm.expectRevert(bytes(Errors.COLLATERAL_CANNOT_COVER_NEW_BORROW)); + _borrow(AaveV3Ethereum.POOL, 1, debtAsset); + + // Swap debt + // add some margin to account for accumulated debt + uint256 repayAmount = (borrowAmount * 101) / 100; + PsPResponse memory psp = _fetchPSPRoute( + newDebtAsset, + debtAsset, + repayAmount, + user, + false, + true + ); + + skip(1 hours); + + ICreditDelegationToken(newDebtToken).approveDelegation(address(debtSwapAdapter), psp.srcAmount); + + IParaswapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaswapDebtSwapAdapter + .DebtSwapParams({ + debtAsset: debtAsset, + debtRepayAmount: type(uint256).max, + debtRateMode: 2, + newDebtAsset: newDebtAsset, + maxNewDebtAmount: psp.srcAmount, + extraCollateralAsset: extraCollateralAsset, + extraCollateralAmount: extraCollateralAmount, + offset: psp.offset, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IParaswapDebtSwapAdapter.CreditDelegationInput memory cd; + IParaswapDebtSwapAdapter.PermitInput memory collateralATokenPermit = _getPermit( + extraCollateralAToken, + address(debtSwapAdapter), + extraCollateralAmount + 1 + ); + + debtSwapAdapter.swapDebt(debtSwapParams, cd, collateralATokenPermit); + } + + function test_debtSwap_extra_Collateral_same_as_new_debt() public { + // We'll use the debtAsset & supplyAmount as extra collateral too. + address debtAsset = AaveV3EthereumAssets.DAI_UNDERLYING; + address newDebtAsset = AaveV3EthereumAssets.USDC_UNDERLYING; + address newDebtToken = AaveV3EthereumAssets.USDC_V_TOKEN; + address extraCollateralAsset = newDebtAsset; + address extraCollateralAToken = AaveV3EthereumAssets.USDC_A_TOKEN; + + uint256 supplyAmount = 120e18; + uint256 borrowAmount = 80e18; + uint256 extraCollateralAmount = 1000e6; + + // 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; + + // Deal some debtAsset to cover the premium and any 1 wei rounding errors on withdrawal. + deal(debtAsset, address(debtSwapAdapter), 1e18); + + vm.startPrank(user); + + _supply(AaveV3Ethereum.POOL, supplyAmount, debtAsset); + _borrow(AaveV3Ethereum.POOL, borrowAmount, debtAsset); + + _withdraw(AaveV3Ethereum.POOL, withdrawAmount, debtAsset); + + vm.expectRevert(bytes(Errors.COLLATERAL_CANNOT_COVER_NEW_BORROW)); + _borrow(AaveV3Ethereum.POOL, 1, debtAsset); + + // Swap debt + // add some margin to account for accumulated debt + uint256 repayAmount = (borrowAmount * 101) / 100; + PsPResponse memory psp = _fetchPSPRoute( + newDebtAsset, + debtAsset, + repayAmount, + user, + false, + true + ); + + skip(1 hours); + + ICreditDelegationToken(newDebtToken).approveDelegation(address(debtSwapAdapter), psp.srcAmount); + IERC20Detailed(extraCollateralAToken).approve( + address(debtSwapAdapter), + extraCollateralAmount + 1 + ); + + IParaswapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaswapDebtSwapAdapter + .DebtSwapParams({ + debtAsset: debtAsset, + debtRepayAmount: type(uint256).max, + debtRateMode: 2, + newDebtAsset: newDebtAsset, + maxNewDebtAmount: psp.srcAmount, + extraCollateralAsset: extraCollateralAsset, + extraCollateralAmount: extraCollateralAmount, + offset: psp.offset, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IParaswapDebtSwapAdapter.CreditDelegationInput memory cd; + IParaswapDebtSwapAdapter.PermitInput memory collateralATokenPermit; + + debtSwapAdapter.swapDebt(debtSwapParams, cd, collateralATokenPermit); + } + function _getCDPermit( uint256 amount, address debtToken @@ -221,4 +601,8 @@ contract DebtSwapV3Test is BaseTest { 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/tests/DebtSwapV3GHO.t.sol b/tests/DebtSwapV3GHO.t.sol index d02bba5..e634d11 100644 --- a/tests/DebtSwapV3GHO.t.sol +++ b/tests/DebtSwapV3GHO.t.sol @@ -4,6 +4,7 @@ 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 {AaveGovernanceV2} from 'aave-address-book/AaveGovernanceV2.sol'; import {AaveV3Ethereum, AaveV3EthereumAssets, IPool} from 'aave-address-book/AaveV3Ethereum.sol'; import {BaseTest} from './utils/BaseTest.sol'; @@ -19,7 +20,7 @@ contract DebtSwapV3GHOTest is BaseTest { function setUp() public override { super.setUp(); - vm.createSelectFork(vm.rpcUrl('mainnet'), 17706797); + vm.createSelectFork(vm.rpcUrl('mainnet'), 17786869); debtSwapAdapter = new ParaSwapDebtSwapAdapterV3GHO( IPoolAddressesProvider(address(AaveV3Ethereum.POOL_ADDRESSES_PROVIDER)), @@ -29,6 +30,87 @@ contract DebtSwapV3GHOTest is BaseTest { ); } + function test_revert_Gho_debtSwap_without_extra_collateral() public { + address aToken = AaveV3EthereumAssets.DAI_A_TOKEN; + address debtAsset = AaveV3EthereumAssets.DAI_UNDERLYING; + address newDebtAsset = debtSwapAdapter.GHO(); + address newDebtToken = 0x786dBff3f1292ae8F92ea68Cf93c30b34B1ed04B; // vDebtGHO + + uint256 supplyAmount = 120e18; + uint256 borrowAmount = 80e18; + + // 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; + + // Deal some debtAsset to cover the premium and any 1 wei rounding errors on withdrawal. + deal(debtAsset, address(debtSwapAdapter), 1e18); + + vm.startPrank(user); + + _supply(AaveV3Ethereum.POOL, supplyAmount, debtAsset); + _borrow(AaveV3Ethereum.POOL, borrowAmount, debtAsset); + + _withdraw(AaveV3Ethereum.POOL, withdrawAmount, debtAsset); + + vm.expectRevert(bytes(Errors.COLLATERAL_CANNOT_COVER_NEW_BORROW)); + _borrow(AaveV3Ethereum.POOL, 1, debtAsset); + + // Swap debt + // add some margin to account for accumulated debt + uint256 repayAmount = (borrowAmount * 101) / 100; + PsPResponse memory psp = _fetchPSPRoute( + newDebtAsset, + debtAsset, + repayAmount, + user, + false, + true + ); + + skip(1 hours); + + ICreditDelegationToken(newDebtToken).approveDelegation(address(debtSwapAdapter), psp.srcAmount); + IERC20Detailed(aToken).approve(address(debtSwapAdapter), supplyAmount); + + IParaswapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaswapDebtSwapAdapter + .DebtSwapParams({ + debtAsset: debtAsset, + debtRepayAmount: type(uint256).max, + debtRateMode: 2, + newDebtAsset: newDebtAsset, + maxNewDebtAmount: psp.srcAmount, + extraCollateralAsset: address(0), + extraCollateralAmount: 0, + offset: psp.offset, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IParaswapDebtSwapAdapter.CreditDelegationInput memory cd; + IParaswapDebtSwapAdapter.PermitInput memory collateralATokenPermit; + + vm.expectRevert(bytes(Errors.COLLATERAL_CANNOT_COVER_NEW_BORROW)); + debtSwapAdapter.swapDebt(debtSwapParams, cd, collateralATokenPermit); + } + + function test_revert_Gho_onFlashloan_not_minter() public { + vm.expectRevert(bytes('SENDER_MUST_BE_MINTER')); + debtSwapAdapter.onFlashLoan(address(0), address(0), 0, 0, ''); + } + + function test_revert_Gho_onFlashloan_not_initiator() public { + vm.prank(0xb639D208Bcf0589D54FaC24E655C79EC529762B8); + + vm.expectRevert(bytes('INITIATOR_MUST_BE_THIS')); + debtSwapAdapter.onFlashLoan(address(0), address(0), 0, 0, ''); + } + + function test_revert_Gho_onFlashloan_token_not_Gho() public { + vm.prank(0xb639D208Bcf0589D54FaC24E655C79EC529762B8); + + vm.expectRevert(bytes('MUST_BE_GHO')); + debtSwapAdapter.onFlashLoan(address(debtSwapAdapter), address(0), 0, 0, ''); + } + /** * 1. supply 200000 DAI * 2. borrow 1000 GHO @@ -66,14 +148,17 @@ contract DebtSwapV3GHOTest is BaseTest { debtRateMode: 2, newDebtAsset: newDebtAsset, maxNewDebtAmount: psp.srcAmount, - paraswapData: abi.encode(psp.swapCalldata, psp.augustus), - offset: psp.offset + extraCollateralAsset: address(0), + extraCollateralAmount: 0, + offset: psp.offset, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) }); uint256 vDEBT_TOKENBalanceBefore = IERC20Detailed(debtToken).balanceOf(user); IParaswapDebtSwapAdapter.CreditDelegationInput memory cd; - debtSwapAdapter.swapDebt(debtSwapParams, cd); + IParaswapDebtSwapAdapter.PermitInput memory collateralATokenPermit; + debtSwapAdapter.swapDebt(debtSwapParams, cd, collateralATokenPermit); uint256 vDEBT_TOKENBalanceAfter = IERC20Detailed(debtToken).balanceOf(user); uint256 vNEWDEBT_TOKENBalanceAfter = IERC20Detailed(newDebtToken).balanceOf(user); @@ -117,12 +202,16 @@ contract DebtSwapV3GHOTest is BaseTest { debtRateMode: 2, newDebtAsset: newDebtAsset, maxNewDebtAmount: psp.srcAmount, - paraswapData: abi.encode(psp.swapCalldata, psp.augustus), - offset: psp.offset + extraCollateralAsset: address(0), + extraCollateralAmount: 0, + offset: psp.offset, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) }); IParaswapDebtSwapAdapter.CreditDelegationInput memory cd; - debtSwapAdapter.swapDebt(debtSwapParams, cd); + IParaswapDebtSwapAdapter.PermitInput memory collateralATokenPermit; + + debtSwapAdapter.swapDebt(debtSwapParams, cd, collateralATokenPermit); uint256 vDEBT_TOKENBalanceAfter = IERC20Detailed(debtToken).balanceOf(user); uint256 vNEWDEBT_TOKENBalanceAfter = IERC20Detailed(newDebtToken).balanceOf(user); @@ -131,6 +220,135 @@ contract DebtSwapV3GHOTest is BaseTest { _invariant(address(debtSwapAdapter), debtAsset, newDebtAsset); } + function test_Gho_debtSwap_extra_collateral() public { + address debtAsset = AaveV3EthereumAssets.DAI_UNDERLYING; + address newDebtAsset = debtSwapAdapter.GHO(); + address newDebtToken = 0x786dBff3f1292ae8F92ea68Cf93c30b34B1ed04B; + address extraCollateralAsset = debtAsset; + address extraCollateralAToken = AaveV3EthereumAssets.DAI_A_TOKEN; + + uint256 supplyAmount = 120e18; + uint256 borrowAmount = 80e18; + uint256 extraCollateralAmount = 1000e18; + + // 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; + + // Deal some debtAsset to cover the premium and any 1 wei rounding errors on withdrawal. + deal(debtAsset, address(debtSwapAdapter), 1e18); + + vm.startPrank(user); + + _supply(AaveV3Ethereum.POOL, supplyAmount, debtAsset); + _borrow(AaveV3Ethereum.POOL, borrowAmount, debtAsset); + + _withdraw(AaveV3Ethereum.POOL, withdrawAmount, debtAsset); + + vm.expectRevert(bytes(Errors.COLLATERAL_CANNOT_COVER_NEW_BORROW)); + _borrow(AaveV3Ethereum.POOL, 1, debtAsset); + + // Swap debt + // add some margin to account for accumulated debt + uint256 repayAmount = (borrowAmount * 101) / 100; + PsPResponse memory psp = _fetchPSPRoute( + newDebtAsset, + debtAsset, + repayAmount, + user, + false, + true + ); + + skip(1 hours); + + ICreditDelegationToken(newDebtToken).approveDelegation(address(debtSwapAdapter), psp.srcAmount); + IERC20Detailed(extraCollateralAToken).approve(address(debtSwapAdapter), extraCollateralAmount); + + IParaswapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaswapDebtSwapAdapter + .DebtSwapParams({ + debtAsset: debtAsset, + debtRepayAmount: type(uint256).max, + debtRateMode: 2, + newDebtAsset: newDebtAsset, + maxNewDebtAmount: psp.srcAmount, + extraCollateralAsset: extraCollateralAsset, + extraCollateralAmount: extraCollateralAmount, + offset: psp.offset, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IParaswapDebtSwapAdapter.CreditDelegationInput memory cd; + IParaswapDebtSwapAdapter.PermitInput memory collateralATokenPermit; + + debtSwapAdapter.swapDebt(debtSwapParams, cd, collateralATokenPermit); + } + + function test_Gho_debtSwap_extra_collateral_permit() public { + address debtAsset = AaveV3EthereumAssets.DAI_UNDERLYING; + address newDebtAsset = debtSwapAdapter.GHO(); + address newDebtToken = 0x786dBff3f1292ae8F92ea68Cf93c30b34B1ed04B; + address extraCollateralAsset = debtAsset; + address extraCollateralAToken = AaveV3EthereumAssets.DAI_A_TOKEN; + + uint256 supplyAmount = 120e18; + uint256 borrowAmount = 80e18; + uint256 extraCollateralAmount = 1000e18; + + // 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; + + // Deal some debtAsset to cover the premium and any 1 wei rounding errors on withdrawal. + deal(debtAsset, address(debtSwapAdapter), 1e18); + + vm.startPrank(user); + + _supply(AaveV3Ethereum.POOL, supplyAmount, debtAsset); + _borrow(AaveV3Ethereum.POOL, borrowAmount, debtAsset); + + _withdraw(AaveV3Ethereum.POOL, withdrawAmount, debtAsset); + + vm.expectRevert(bytes(Errors.COLLATERAL_CANNOT_COVER_NEW_BORROW)); + _borrow(AaveV3Ethereum.POOL, 1, debtAsset); + + // Swap debt + // add some margin to account for accumulated debt + uint256 repayAmount = (borrowAmount * 101) / 100; + PsPResponse memory psp = _fetchPSPRoute( + newDebtAsset, + debtAsset, + repayAmount, + user, + false, + true + ); + + skip(1 hours); + + ICreditDelegationToken(newDebtToken).approveDelegation(address(debtSwapAdapter), psp.srcAmount); + + IParaswapDebtSwapAdapter.DebtSwapParams memory debtSwapParams = IParaswapDebtSwapAdapter + .DebtSwapParams({ + debtAsset: debtAsset, + debtRepayAmount: type(uint256).max, + debtRateMode: 2, + newDebtAsset: newDebtAsset, + maxNewDebtAmount: psp.srcAmount, + extraCollateralAsset: extraCollateralAsset, + extraCollateralAmount: extraCollateralAmount, + offset: psp.offset, + paraswapData: abi.encode(psp.swapCalldata, psp.augustus) + }); + + IParaswapDebtSwapAdapter.CreditDelegationInput memory cd; + IParaswapDebtSwapAdapter.PermitInput memory collateralATokenPermit = _getPermit( + extraCollateralAToken, + address(debtSwapAdapter), + extraCollateralAmount + 1 + ); + + debtSwapAdapter.swapDebt(debtSwapParams, cd, collateralATokenPermit); + } + function _getCDPermit( uint256 amount, address debtToken @@ -170,4 +388,8 @@ contract DebtSwapV3GHOTest is BaseTest { 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/tests/pspcache/008cf99bc1ce700e9c5f66631c46785ab3355b0c b/tests/pspcache/008cf99bc1ce700e9c5f66631c46785ab3355b0c new file mode 100644 index 0000000..c33922c --- /dev/null +++ b/tests/pspcache/008cf99bc1ce700e9c5f66631c46785ab3355b0c @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000387407b1fd1ed7af17000000000000000000000000000000000000000000000036c090d0ca6888000000000000000000000000000000000000000000000000000000000000000000a400000000000000000000000000000000000000000000000000000000000003a4353269100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000dcf4ee5b700e2a5fec458e06b763a4a3e30044940000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba00000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000387407b1fd1ed7af17000000000000000000000000000000000000000000000036c090d0ca68880000000000000000000000000000000000000000000000000036cf18de836afe23c7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000009abf798f5314bfd793a9e57a654bed35af4a1d60010000000000000000000000000000000000000000000000000000000003138800000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000064c32bd8e26aff08996f4cbcbd2d1b672492686500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000e592427a0aece92de3edee1f18e0157c05861564000000000000000000000000000000000000000000000000000000000000271000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000064cc11f7000000000000000000000000000000000000000000000000000000000000002b6b175474e89094c44da98b954eedeac495271d0f0001f45f98805a4e8be255a32880fdec7f6728c6568ba0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/pspcache/0db734c16b5f9ef4e90ff3523aa0594aaa2e9b8d b/tests/pspcache/0db734c16b5f9ef4e90ff3523aa0594aaa2e9b8d new file mode 100644 index 0000000..f9ef0d3 --- /dev/null +++ b/tests/pspcache/0db734c16b5f9ef4e90ff3523aa0594aaa2e9b8d @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000092216c29decbb57c0000000000000000000000000000000000000000000000008c2a687ce772000000000000000000000000000000000000000000000000000000000000000000440000000000000000000000000000000000000000000000000000000000000104b2f1e6db0000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba000000000000000000000000000000000000000000000000092216c29decbb57c0000000000000000000000000000000000000000000000008c2a687ce7720000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000004de4f20ef17b889b437c151eb5ba15a47bfc62bff469000000000000000000004de5a478c2975ab1ea89e8196811f51a7b7ade33eb1100000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/pspcache/0e0c5f8c55ff556fb635bfc5627c9abd9d0717fe b/tests/pspcache/0e0c5f8c55ff556fb635bfc5627c9abd9d0717fe new file mode 100644 index 0000000..198e144 --- /dev/null +++ b/tests/pspcache/0e0c5f8c55ff556fb635bfc5627c9abd9d0717fe @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000036ccd08b2f8f4fc3200000000000000000000000000000000000000000000000348fe72ed6cac000000000000000000000000000000000000000000000000000000000000000000440000000000000000000000000000000000000000000000000000000000000104b2f1e6db0000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba00000000000000000000000000000000000000000000000036ccd08b2f8f4fc3200000000000000000000000000000000000000000000000348fe72ed6cac0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000004de4f20ef17b889b437c151eb5ba15a47bfc62bff469000000000000000000004de5a478c2975ab1ea89e8196811f51a7b7ade33eb1100000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/pspcache/30a8a8befa9059a15374576bbd0d1030efd2566d b/tests/pspcache/30a8a8befa9059a15374576bbd0d1030efd2566d new file mode 100644 index 0000000..ef372ba --- /dev/null +++ b/tests/pspcache/30a8a8befa9059a15374576bbd0d1030efd2566d @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000493b102b6f0228d1b000000000000000000000000000000000000000000000004615343e73b90000000000000000000000000000000000000000000000000000000000000000000a400000000000000000000000000000000000000000000000000000000000003c4353269100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000dcf4ee5b700e2a5fec458e06b763a4a3e300449400000000000000000000000040d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000493b102b6f0228d1b000000000000000000000000000000000000000000000004615343e73b900000000000000000000000000000000000000000000000000004719083e0d541db04000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000009abf798f5314bfd793a9e57a654bed35af4a1d60010000000000000000000000000000000000000000000000000000000003138800000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000064c47c4192ac9a07a68b4544872b4dd73ce50e7500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000e592427a0aece92de3edee1f18e0157c05861564000000000000000000000000000000000000000000000000000000000000271000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000064cd626100000000000000000000000000000000000000000000000000000000000000426b175474e89094c44da98b954eedeac495271d0f000064a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f440d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/pspcache/31f27fac12203c5177c7b12ab7a8dea95ffbc4d4 b/tests/pspcache/31f27fac12203c5177c7b12ab7a8dea95ffbc4d4 new file mode 100644 index 0000000..ff25623 --- /dev/null +++ b/tests/pspcache/31f27fac12203c5177c7b12ab7a8dea95ffbc4d4 @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000003c495a0edd3f76daa0000000000000000000000000000000000000000000000039d17e4d1f78a000000000000000000000000000000000000000000000000000000000000000000440000000000000000000000000000000000000000000000000000000000000104b2f1e6db0000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba0000000000000000000000000000000000000000000000003c495a0edd3f76daa0000000000000000000000000000000000000000000000039d17e4d1f78a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000004de4f20ef17b889b437c151eb5ba15a47bfc62bff469000000000000000000004de5a478c2975ab1ea89e8196811f51a7b7ade33eb1100000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/pspcache/44afa50aac5288b0a63acf59aea34e79451986c7 b/tests/pspcache/44afa50aac5288b0a63acf59aea34e79451986c7 new file mode 100644 index 0000000..1f78f06 --- /dev/null +++ b/tests/pspcache/44afa50aac5288b0a63acf59aea34e79451986c7 @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000004f9a813000000000000000000000000000000000000000000000004615343e73b900000000000000000000000000000000000000000000000000000000000000000004400000000000000000000000000000000000000000000000000000000000000e4b2f1e6db000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000004f9a813000000000000000000000000000000000000000000000004615343e73b900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000004de5ae461ca67b15dc8dc81ce7615e0320da1a9ab8d500000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/pspcache/554d008100c7b2336f60691acaa14e09c3b45370 b/tests/pspcache/554d008100c7b2336f60691acaa14e09c3b45370 new file mode 100644 index 0000000..d48f748 --- /dev/null +++ b/tests/pspcache/554d008100c7b2336f60691acaa14e09c3b45370 @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000004f9a803000000000000000000000000000000000000000000000004615343e73b900000000000000000000000000000000000000000000000000000000000000000004400000000000000000000000000000000000000000000000000000000000000e4b2f1e6db000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000004f9a803000000000000000000000000000000000000000000000004615343e73b900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000004de5ae461ca67b15dc8dc81ce7615e0320da1a9ab8d500000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/pspcache/55ec4a627ff9237531012c862bb00125e59ebebf b/tests/pspcache/55ec4a627ff9237531012c862bb00125e59ebebf new file mode 100644 index 0000000..c8dad07 --- /dev/null +++ b/tests/pspcache/55ec4a627ff9237531012c862bb00125e59ebebf @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000001bf22584d35bdcc24800000000000000000000000000000000000000000000001b1ae4d6e2ef500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004642298207a00000000000000000000000000000000000000000000000000000000000000200000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba00000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000001bf22584d35bdcc24800000000000000000000000000000000000000000000001b1ae4d6e2ef50000000000000000000000000000000000000000000000000001b21c5fabe4a462c7800000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009abf798f5314bfd793a9e57a654bed35af4a1d60010000000000000000000000000000000000000000000000000000000003138800000000000000000000000000000000000000000000000000000000000004200000000000000000000000000000000000000000000000000000000064c32bd8160ed2884cf74747b527cea42423c5b6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000e592427a0aece92de3edee1f18e0157c058615640000000000000000000000000000000000000000000000000000000000000124f28c0498000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee570000000000000000000000000000000000000000000000000000000064cc11f700000000000000000000000000000000000000000000001b1ae4d6e2ef50000000000000000000000000000000000000000000000000001bf22584d35bdcc246000000000000000000000000000000000000000000000000000000000000002b6b175474e89094c44da98b954eedeac495271d0f0001f45f98805a4e8be255a32880fdec7f6728c6568ba00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012400000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/pspcache/5c70c2c29330e126ee4b32020e6aac1e5ac3a15b b/tests/pspcache/5c70c2c29330e126ee4b32020e6aac1e5ac3a15b new file mode 100644 index 0000000..50fdd7e --- /dev/null +++ b/tests/pspcache/5c70c2c29330e126ee4b32020e6aac1e5ac3a15b @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000002db291af048e80229000000000000000000000000000000000000000000000002bcd40a70853a000000000000000000000000000000000000000000000000000000000000000000440000000000000000000000000000000000000000000000000000000000000104b2f1e6db0000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba0000000000000000000000000000000000000000000000002db291af048e80229000000000000000000000000000000000000000000000002bcd40a70853a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000004de4f20ef17b889b437c151eb5ba15a47bfc62bff469000000000000000000004de5a478c2975ab1ea89e8196811f51a7b7ade33eb1100000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/pspcache/6f04a16df882a3cb3172dc587bf388de121190a1 b/tests/pspcache/6f04a16df882a3cb3172dc587bf388de121190a1 new file mode 100644 index 0000000..541397b --- /dev/null +++ b/tests/pspcache/6f04a16df882a3cb3172dc587bf388de121190a1 @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000490b0e10188f608fd000000000000000000000000000000000000000000000004615343e73b90000000000000000000000000000000000000000000000000000000000000000000440000000000000000000000000000000000000000000000000000000000000104b2f1e6db0000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba000000000000000000000000000000000000000000000000490b0e10188f608fd000000000000000000000000000000000000000000000004615343e73b900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000004de4f20ef17b889b437c151eb5ba15a47bfc62bff469000000000000000000004de5a478c2975ab1ea89e8196811f51a7b7ade33eb1100000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/pspcache/718870dfd36fb8767847d23bb356a4877634bbcf b/tests/pspcache/718870dfd36fb8767847d23bb356a4877634bbcf new file mode 100644 index 0000000..946b8ff --- /dev/null +++ b/tests/pspcache/718870dfd36fb8767847d23bb356a4877634bbcf @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000729070e50e27f3473100000000000000000000000000000000000000000000006d8121a194d110000000000000000000000000000000000000000000000000000000000000000000a400000000000000000000000000000000000000000000000000000000000003c4353269100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000dcf4ee5b700e2a5fec458e06b763a4a3e300449400000000000000000000000040d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000729070e50e27f3473100000000000000000000000000000000000000000000006d8121a194d110000000000000000000000000000000000000000000000000006f3a36ed4be131c8d9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000009abf798f5314bfd793a9e57a654bed35af4a1d60010000000000000000000000000000000000000000000000000000000003138800000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000064c82d13da3f85b97a5d445b87ec26ff7276722300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000e592427a0aece92de3edee1f18e0157c05861564000000000000000000000000000000000000000000000000000000000000271000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000064d1133300000000000000000000000000000000000000000000000000000000000000426b175474e89094c44da98b954eedeac495271d0f000064a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f440d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/pspcache/7843a847c808fafab02c4297592d06c9f70ccbf1 b/tests/pspcache/7843a847c808fafab02c4297592d06c9f70ccbf1 new file mode 100644 index 0000000..89db484 --- /dev/null +++ b/tests/pspcache/7843a847c808fafab02c4297592d06c9f70ccbf1 @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000004927493f518001704000000000000000000000000000000000000000000000004615343e73b90000000000000000000000000000000000000000000000000000000000000000000440000000000000000000000000000000000000000000000000000000000000104b2f1e6db0000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba0000000000000000000000000000000000000000000000004927493f518001704000000000000000000000000000000000000000000000004615343e73b900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000004de4f20ef17b889b437c151eb5ba15a47bfc62bff469000000000000000000004de5a478c2975ab1ea89e8196811f51a7b7ade33eb1100000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/pspcache/9723578eb7e58aca760e512d8591f9d05a1e61b0 b/tests/pspcache/9723578eb7e58aca760e512d8591f9d05a1e61b0 new file mode 100644 index 0000000..c695e7b --- /dev/null +++ b/tests/pspcache/9723578eb7e58aca760e512d8591f9d05a1e61b0 @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000038b644644f5aec2d3800000000000000000000000000000000000000000000003635c9adc5dea00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004842298207a000000000000000000000000000000000000000000000000000000000000002000000000000000000000000040d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000038b644644f5aec2d3800000000000000000000000000000000000000000000003635c9adc5dea000000000000000000000000000000000000000000000000000370f67ae6fd708180500000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003a0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009abf798f5314bfd793a9e57a654bed35af4a1d60010000000000000000000000000000000000000000000000000000000003138800000000000000000000000000000000000000000000000000000000000004400000000000000000000000000000000000000000000000000000000064c82d17e534ef7ae4fb496288e42f49090d06ee000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000e592427a0aece92de3edee1f18e0157c058615640000000000000000000000000000000000000000000000000000000000000144f28c0498000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee570000000000000000000000000000000000000000000000000000000064d1133600000000000000000000000000000000000000000000003635c9adc5dea00000000000000000000000000000000000000000000000000038b644644f5aec2d3600000000000000000000000000000000000000000000000000000000000000426b175474e89094c44da98b954eedeac495271d0f000064a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f440d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014400000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/pspcache/9e4692fc8c083de96468a9163b813b6c6dcf697a b/tests/pspcache/9e4692fc8c083de96468a9163b813b6c6dcf697a new file mode 100644 index 0000000..ed16852 --- /dev/null +++ b/tests/pspcache/9e4692fc8c083de96468a9163b813b6c6dcf697a @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000003c8347e56d1c5f9ee000000000000000000000000000000000000000000000003ab1c22780eaf000000000000000000000000000000000000000000000000000000000000000000a400000000000000000000000000000000000000000000000000000000000003a4353269100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000dcf4ee5b700e2a5fec458e06b763a4a3e30044940000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba00000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000003c8347e56d1c5f9ee000000000000000000000000000000000000000000000003ab1c22780eaf0000000000000000000000000000000000000000000000000003ac01417e8b0ac5ea000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000009abf798f5314bfd793a9e57a654bed35af4a1d60010000000000000000000000000000000000000000000000000000000003138800000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000064c329b604c4ec63293842c4bef3a27fd2930fe300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000e592427a0aece92de3edee1f18e0157c05861564000000000000000000000000000000000000000000000000000000000000271000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000064cc0fd5000000000000000000000000000000000000000000000000000000000000002b6b175474e89094c44da98b954eedeac495271d0f0001f45f98805a4e8be255a32880fdec7f6728c6568ba0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/pspcache/ab2992a23bc48dbae8ec248c8569e5f287cae5c0 b/tests/pspcache/ab2992a23bc48dbae8ec248c8569e5f287cae5c0 new file mode 100644 index 0000000..d7bb34a --- /dev/null +++ b/tests/pspcache/ab2992a23bc48dbae8ec248c8569e5f287cae5c0 @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000493b102b6f0228d1b000000000000000000000000000000000000000000000004615343e73b90000000000000000000000000000000000000000000000000000000000000000000a400000000000000000000000000000000000000000000000000000000000003c4353269100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000dcf4ee5b700e2a5fec458e06b763a4a3e300449400000000000000000000000040d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000493b102b6f0228d1b000000000000000000000000000000000000000000000004615343e73b900000000000000000000000000000000000000000000000000004719083e0d541db04000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000009abf798f5314bfd793a9e57a654bed35af4a1d60010000000000000000000000000000000000000000000000000000000003138800000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000064c47c2512b1958212ee420d99bd78513723908c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000e592427a0aece92de3edee1f18e0157c05861564000000000000000000000000000000000000000000000000000000000000271000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000064cd624400000000000000000000000000000000000000000000000000000000000000426b175474e89094c44da98b954eedeac495271d0f000064a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f440d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/pspcache/eb4af82cce5b603457a2d55eefaceea68829c9a5 b/tests/pspcache/eb4af82cce5b603457a2d55eefaceea68829c9a5 new file mode 100644 index 0000000..e611179 --- /dev/null +++ b/tests/pspcache/eb4af82cce5b603457a2d55eefaceea68829c9a5 @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000003e20c8c9bb8fc5a97000000000000000000000000000000000000000000000003ab1c22780eaf000000000000000000000000000000000000000000000000000000000000000000440000000000000000000000000000000000000000000000000000000000000104b2f1e6db0000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba0000000000000000000000000000000000000000000000003e20c8c9bb8fc5a97000000000000000000000000000000000000000000000003ab1c22780eaf0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000004de4f20ef17b889b437c151eb5ba15a47bfc62bff469000000000000000000004de5a478c2975ab1ea89e8196811f51a7b7ade33eb1100000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/pspcache/f23915ac73cd97a3c7ba5d80d9a2665f989b6913 b/tests/pspcache/f23915ac73cd97a3c7ba5d80d9a2665f989b6913 new file mode 100644 index 0000000..7ae3e2f --- /dev/null +++ b/tests/pspcache/f23915ac73cd97a3c7ba5d80d9a2665f989b6913 @@ -0,0 +1 @@ +0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000def171fe48cf0115b1d80b88dc8eab59176fee5700000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000004fb8c830000000000000000000000000000000000000000000000000000000004d0e90000000000000000000000000000000000000000000000000000000000000000440000000000000000000000000000000000000000000000000000000000000104b2f1e6db0000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba00000000000000000000000000000000000000000000000000000000004fb8c830000000000000000000000000000000000000000000000000000000004d0e900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000004de4279ca79d5fb2490721512c8ae4767e249d75f41b000000000000000000004de5c3d03e4f041fd4cd388c549ee2a29a9e5075882f00000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/tests/utils/BaseTest.sol b/tests/utils/BaseTest.sol index 0f83abb..706b83e 100644 --- a/tests/utils/BaseTest.sol +++ b/tests/utils/BaseTest.sol @@ -5,8 +5,11 @@ import 'forge-std/Test.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 {SigUtils} from './SigUtils.sol'; contract BaseTest is Test { struct PsPResponse { @@ -65,4 +68,44 @@ contract BaseTest is Test { 'LEFTOVER_NEW_DEBT_ASSET' ); } + + function _getPermit( + address permitToken, + address debtSwapAdapter, + uint256 amount + ) internal view returns (IParaswapDebtSwapAdapter.PermitInput memory) { + IERC20WithPermit token = IERC20WithPermit(permitToken); + uint256 nonce; + try IERC20WithPermit(token).nonces(user) returns (uint256 res) { + nonce = res; + } catch { + nonce = IATokenV2(address(token))._nonces(user); + } + + SigUtils.Permit memory permit = SigUtils.Permit({ + owner: user, + spender: address(debtSwapAdapter), + value: amount, + nonce: nonce, + deadline: type(uint256).max + }); + + bytes32 digest = SigUtils.getTypedDataHash(permit, token.DOMAIN_SEPARATOR()); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(userPrivateKey, digest); + + return + IParaswapDebtSwapAdapter.PermitInput({ + aToken: token, + value: amount, + deadline: type(uint256).max, + v: v, + r: r, + s: s + }); + } +} + +interface IATokenV2 { + function _nonces(address user) external view returns (uint256); } diff --git a/tests/utils/SigUtils.sol b/tests/utils/SigUtils.sol index b4a1ab6..ebf7157 100644 --- a/tests/utils/SigUtils.sol +++ b/tests/utils/SigUtils.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.10; -import 'forge-std/Test.sol'; -import {IAToken} from '@aave/core-v3/contracts/interfaces/IAToken.sol'; import {BaseParaSwapAdapter} from '../../src/contracts/BaseParaSwapAdapter.sol'; library SigUtils { @@ -46,7 +44,7 @@ library SigUtils { function getPermitTypedDataHash( Permit memory _permit, bytes32 domainSeparator - ) public pure returns (bytes32) { + ) internal pure returns (bytes32) { return keccak256( abi.encodePacked( @@ -69,7 +67,7 @@ library SigUtils { function getCreditDelegationTypedDataHash( CreditDelegation memory _creditDelegation, bytes32 domainSeparator - ) public pure returns (bytes32) { + ) internal pure returns (bytes32) { return keccak256( abi.encodePacked( @@ -92,34 +90,7 @@ library SigUtils { function getTypedDataHash( Permit memory permit, bytes32 domainSeperator - ) public pure returns (bytes32) { + ) internal pure returns (bytes32) { return keccak256(abi.encodePacked('\x19\x01', domainSeperator, getStructHash(permit))); } - - function getPermit( - Vm vm, - address user, - uint256 userPrivateKey, - address spender, - address token, - uint256 value - ) public returns (BaseParaSwapAdapter.PermitSignature memory) { - Permit memory permit = Permit({ - owner: user, - spender: spender, - value: value, - nonce: IAToken(token).nonces(user), - deadline: block.timestamp + 1 days - }); - bytes32 permitDigest = getTypedDataHash(permit, IAToken(token).DOMAIN_SEPARATOR()); - (uint8 pV, bytes32 pR, bytes32 pS) = vm.sign(userPrivateKey, permitDigest); - return - BaseParaSwapAdapter.PermitSignature({ - amount: permit.value, - deadline: permit.deadline, - v: pV, - r: pR, - s: pS - }); - } } diff --git a/yarn.lock b/yarn.lock index 8411266..dc15cbd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -"@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.7.0": +"@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== @@ -17,7 +17,7 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" -"@ethersproject/abstract-provider@5.7.0", "@ethersproject/abstract-provider@^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== @@ -30,7 +30,7 @@ "@ethersproject/transactions" "^5.7.0" "@ethersproject/web" "^5.7.0" -"@ethersproject/abstract-signer@5.7.0", "@ethersproject/abstract-signer@^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== @@ -41,7 +41,7 @@ "@ethersproject/logger" "^5.7.0" "@ethersproject/properties" "^5.7.0" -"@ethersproject/address@5.7.0", "@ethersproject/address@^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== @@ -52,14 +52,14 @@ "@ethersproject/logger" "^5.7.0" "@ethersproject/rlp" "^5.7.0" -"@ethersproject/base64@5.7.0", "@ethersproject/base64@^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== dependencies: "@ethersproject/bytes" "^5.7.0" -"@ethersproject/basex@5.7.0", "@ethersproject/basex@^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== @@ -67,7 +67,7 @@ "@ethersproject/bytes" "^5.7.0" "@ethersproject/properties" "^5.7.0" -"@ethersproject/bignumber@5.7.0", "@ethersproject/bignumber@^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== @@ -76,14 +76,14 @@ "@ethersproject/logger" "^5.7.0" bn.js "^5.2.1" -"@ethersproject/bytes@5.7.0", "@ethersproject/bytes@^5.7.0": +"@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== dependencies: "@ethersproject/logger" "^5.7.0" -"@ethersproject/constants@5.7.0", "@ethersproject/constants@^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== @@ -106,7 +106,7 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/transactions" "^5.7.0" -"@ethersproject/hash@5.7.0", "@ethersproject/hash@^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== @@ -121,7 +121,7 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" -"@ethersproject/hdnode@5.7.0", "@ethersproject/hdnode@^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== @@ -139,7 +139,7 @@ "@ethersproject/transactions" "^5.7.0" "@ethersproject/wordlists" "^5.7.0" -"@ethersproject/json-wallets@5.7.0", "@ethersproject/json-wallets@^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== @@ -158,7 +158,7 @@ aes-js "3.0.0" scrypt-js "3.0.1" -"@ethersproject/keccak256@5.7.0", "@ethersproject/keccak256@^5.7.0": +"@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== @@ -166,19 +166,19 @@ "@ethersproject/bytes" "^5.7.0" js-sha3 "0.8.0" -"@ethersproject/logger@5.7.0", "@ethersproject/logger@^5.7.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/networks@5.7.1", "@ethersproject/networks@^5.7.0": +"@ethersproject/networks@^5.7.0", "@ethersproject/networks@5.7.1": version "5.7.1" resolved "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.1.tgz" integrity sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ== dependencies: "@ethersproject/logger" "^5.7.0" -"@ethersproject/pbkdf2@5.7.0", "@ethersproject/pbkdf2@^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== @@ -186,7 +186,7 @@ "@ethersproject/bytes" "^5.7.0" "@ethersproject/sha2" "^5.7.0" -"@ethersproject/properties@5.7.0", "@ethersproject/properties@^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== @@ -219,7 +219,7 @@ bech32 "1.1.4" ws "7.4.6" -"@ethersproject/random@5.7.0", "@ethersproject/random@^5.7.0": +"@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== @@ -227,7 +227,7 @@ "@ethersproject/bytes" "^5.7.0" "@ethersproject/logger" "^5.7.0" -"@ethersproject/rlp@5.7.0", "@ethersproject/rlp@^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== @@ -235,7 +235,7 @@ "@ethersproject/bytes" "^5.7.0" "@ethersproject/logger" "^5.7.0" -"@ethersproject/sha2@5.7.0", "@ethersproject/sha2@^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== @@ -244,7 +244,7 @@ "@ethersproject/logger" "^5.7.0" hash.js "1.1.7" -"@ethersproject/signing-key@5.7.0", "@ethersproject/signing-key@^5.7.0": +"@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== @@ -268,7 +268,7 @@ "@ethersproject/sha2" "^5.7.0" "@ethersproject/strings" "^5.7.0" -"@ethersproject/strings@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== @@ -277,7 +277,7 @@ "@ethersproject/constants" "^5.7.0" "@ethersproject/logger" "^5.7.0" -"@ethersproject/transactions@5.7.0", "@ethersproject/transactions@^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== @@ -322,7 +322,7 @@ "@ethersproject/transactions" "^5.7.0" "@ethersproject/wordlists" "^5.7.0" -"@ethersproject/web@5.7.1", "@ethersproject/web@^5.7.0": +"@ethersproject/web@^5.7.0", "@ethersproject/web@5.7.1": version "5.7.1" resolved "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.1.tgz" integrity sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w== @@ -333,7 +333,7 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" -"@ethersproject/wordlists@5.7.0", "@ethersproject/wordlists@^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== @@ -346,12 +346,12 @@ "@paraswap/core@1.1.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@paraswap/core/-/core-1.1.0.tgz#5ec7415be69dc657a9d82b0fde4e20f632cca1b6" + resolved "https://registry.npmjs.org/@paraswap/core/-/core-1.1.0.tgz" integrity sha512-ecnX8ezlhYWFwolZxYEz+K+RfLr8xaxQqiJKlxJ8Yf00tXTGxDGn6/Acy00t4+9Kv0apewd7++J33eJt9yNfwg== "@paraswap/sdk@^6.2.1": version "6.2.1" - resolved "https://registry.yarnpkg.com/@paraswap/sdk/-/sdk-6.2.1.tgz#cbac091ae1a8f080edd64234465ef127fdbd21f6" + resolved "https://registry.npmjs.org/@paraswap/sdk/-/sdk-6.2.1.tgz" integrity sha512-PW0pw676Oyy6129gqLKU/o7JNFVurCnA6AJ3NAKmZ6zAbPrYLWJV+zl/pWqja5MZkst2QzbDhd40gGjIeEKaPg== dependencies: "@paraswap/core" "1.1.0" @@ -385,7 +385,7 @@ asynckit@^0.4.0: resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== -axios@^1.1.3: +axios@^1.1.3, "axios@>=0.25.0 <2.0.0": version "1.1.3" resolved "https://registry.npmjs.org/axios/-/axios-1.1.3.tgz" integrity sha512-00tXVRwKx/FZr/IDVFt4C+f9FYairX517WoGCL6dpOntqLkZofjhu43F/Xl44UOpqa+9sLFDrG/XAnFsUYgkDA== @@ -459,7 +459,7 @@ escape-string-regexp@^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: +ethers@^5.5.0, ethers@^5.7.2: version "5.7.2" resolved "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz" integrity sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg== @@ -509,7 +509,7 @@ form-data@^4.0.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: +hash.js@^1.0.0, hash.js@^1.0.3, hash.js@1.1.7: version "1.1.7" resolved "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz" integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== @@ -587,7 +587,7 @@ prettier-plugin-solidity@^1.0.0-beta.19: solidity-comments-extractor "^0.0.7" string-width "^4.2.3" -prettier@^2.7.1: +prettier@^2.3.0, 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== @@ -635,6 +635,11 @@ ts-essentials@^9.1.2: resolved "https://registry.npmjs.org/ts-essentials/-/ts-essentials-9.3.0.tgz" integrity sha512-XeiCboEyBG8UqXZtXl59bWEi4ZgOqRsogFDI6WDGIF1LmzbYiAkIwjkXN6zZWWl4re/lsOqMlYfe8KA0XiiEPw== +typescript@>=4.1.0: + version "5.1.6" + resolved "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz" + integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA== + ws@7.4.6: version "7.4.6" resolved "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz"