diff --git a/LICENSE b/LICENSE index 07f396a..8c4f422 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,117 @@ -Copyright 2022 BGD Labs +Business Source License 1.1 -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +License text copyright (c) 2020 MariaDB Corporation Ab, All Rights Reserved. +“Business Source License” is a trademark of MariaDB Corporation Ab. -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +----------------------------------------------------------------------------- -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +Parameters + +Licensor: Aave DAO, represented by its governance smart contracts + + +Licensed Work: Aave Migration Contracts + The Licensed Work is (c) 2023 Aave DAO, represented by its governance smart contracts + +Additional Use Grant: You are permitted to use, copy, and modify the Licensed Work, subject to + the following conditions: + - Your use of the Licensed Work shall not, directly or indirectly, enable, facilitate, + or assist in any way with the migration of users and/or funds from the Aave ecosystem. + The "Aave ecosystem" is defined in the context of this License as the collection of + software protocols and applications approved by the Aave governance, including all + those produced within compensated service provider engagements with the Aave DAO. + The Aave DAO is able to waive this requirement for one or more third-parties, if and + only if explicitly indicating it on a record 'authorizations' on govv3.aavelicense.eth. + - You are neither an individual nor a direct or indirect participant in any incorporated + organization, DAO, or identifiable group, that has deployed in production any original + or derived software ("fork") of the Aave ecosystem for purposes competitive to Aave, + within the preceding two years. + The Aave DAO is able to waive this requirement for one or more third-parties, if and + only if explicitly indicating it on a record 'authorizations' on govv3.aavelicense.eth. + - You must ensure that the usage of the Licensed Work does not result in any direct or + indirect harm to the Aave ecosystem or the Aave brand. This encompasses, but is not limited to, + reputational damage, omission of proper credit/attribution, or utilization for any malicious + intent. + +Change Date: The earlier of: + - 2027-10-09 + - The date specified in the 'change-date' record on poolmigration.aavelicense.eth + +Change License: MIT + +----------------------------------------------------------------------------- + +Notice + +The Business Source License (this document, or the “License”) is not an Open +Source license. However, the Licensed Work will eventually be made available +under an Open Source License, as stated in this License. + +----------------------------------------------------------------------------- + +Terms + +The Licensor hereby grants you the right to copy, modify, create derivative +works, redistribute, and make non-production use of the Licensed Work. The +Licensor may make an Additional Use Grant, above, permitting limited +production use. + +Effective on the Change Date, or the fourth anniversary of the first publicly +available distribution of a specific version of the Licensed Work under this +License, whichever comes first, the Licensor hereby grants you rights under +the terms of the Change License, and the rights granted in the paragraph +above terminate. + +If your use of the Licensed Work does not comply with the requirements +currently in effect as described in this License, you must purchase a +commercial license from the Licensor, its affiliated entities, or authorized +resellers, or you must refrain from using the Licensed Work. + +All copies of the original and modified Licensed Work, and derivative works +of the Licensed Work, are subject to this License. This License applies +separately for each version of the Licensed Work and the Change Date may vary +for each version of the Licensed Work released by Licensor. + +You must conspicuously display this License on each original or modified copy +of the Licensed Work. If you receive the Licensed Work in original or +modified form from a third party, the terms and conditions set forth in this +License apply to your use of that work. + +Any use of the Licensed Work in violation of this License will automatically +terminate your rights under this License for the current and all other +versions of the Licensed Work. + +This License does not grant you any right in any trademark or logo of +Licensor or its affiliates (provided that you may use a trademark or logo of +Licensor as expressly required by this License). + +TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON +AN “AS IS” BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, +EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND +TITLE. + +MariaDB hereby grants you permission to use this License’s text to license +your works, and to refer to it using the trademark “Business Source License”, +as long as you comply with the Covenants of Licensor below. + +Covenants of Licensor + +In consideration of the right to use this License’s text and the “Business +Source License” name and trademark, Licensor covenants to MariaDB, and to all +other recipients of the licensed work to be provided by Licensor: + +1. To specify as the Change License the GPL Version 2.0 or any later version, + or a license that is compatible with GPL Version 2.0 or a later version, + where “compatible” means that software provided under the Change License can + be included in a program with software provided under GPL Version 2.0 or a + later version. Licensor may specify additional Change Licenses without + limitation. + +2. To either: (a) specify an additional grant of rights to use that does not + impose any additional restriction on the right granted in this License, as + the Additional Use Grant; or (b) insert the text “None”. + +3. To specify a Change Date. + +4. Not to modify this License in any other way. \ No newline at end of file diff --git a/package.json b/package.json index a432623..ea8a1ef 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ }, "keywords": [], "author": "BGD labs", - "license": "MIT", + "license": "BUSL-1.1", "bugs": { "url": "https://github.com/bgd-labs/bgd-forge-template/issues" }, diff --git a/src/contracts/MigrationHelper.sol b/src/contracts/MigrationHelper.sol index c0bd291..1ed4a76 100644 --- a/src/contracts/MigrationHelper.sol +++ b/src/contracts/MigrationHelper.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: MIT +// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.10; import {IERC20WithPermit} from 'solidity-utils/contracts/oz-common/interfaces/IERC20WithPermit.sol'; @@ -7,20 +7,20 @@ import {DataTypes, ILendingPool as IV2Pool} from 'aave-address-book/AaveV2.sol'; import {IPool as IV3Pool} from 'aave-address-book/AaveV3.sol'; import {Ownable} from 'solidity-utils/contracts/oz-common/Ownable.sol'; -import {IMigrationHelper} from '../interfaces/IMigrationHelper.sol'; +import {IMigrationHelperV2V3, IMigrationHelper} from '../interfaces/IMigrationHelperV2V3.sol'; /** * @title MigrationHelper * @author BGD Labs * @dev Contract to migrate positions from Aave v2 to Aave v3 pool */ -contract MigrationHelper is Ownable, IMigrationHelper { +contract MigrationHelper is Ownable, IMigrationHelperV2V3 { using SafeERC20 for IERC20WithPermit; - /// @inheritdoc IMigrationHelper + /// @inheritdoc IMigrationHelperV2V3 IV2Pool public immutable V2_POOL; - /// @inheritdoc IMigrationHelper + /// @inheritdoc IMigrationHelperV2V3 IV3Pool public immutable V3_POOL; mapping(address => IERC20WithPermit) public aTokens; @@ -39,7 +39,7 @@ contract MigrationHelper is Ownable, IMigrationHelper { } /// @inheritdoc IMigrationHelper - function cacheATokens() public virtual { + function cacheATokens() public { DataTypes.ReserveData memory reserveData; address[] memory reserves = _getV2Reserves(); for (uint256 i = 0; i < reserves.length; i++) { diff --git a/src/contracts/MigrationHelperAmm.sol b/src/contracts/MigrationHelperAmm.sol index 7bc9566..9a2e3a0 100644 --- a/src/contracts/MigrationHelperAmm.sol +++ b/src/contracts/MigrationHelperAmm.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: MIT +// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.10; import {IERC20WithPermit} from 'solidity-utils/contracts/oz-common/interfaces/IERC20WithPermit.sol'; diff --git a/src/contracts/MigrationHelperMainnet.sol b/src/contracts/MigrationHelperMainnet.sol index c4e448b..36d3f70 100644 --- a/src/contracts/MigrationHelperMainnet.sol +++ b/src/contracts/MigrationHelperMainnet.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: MIT +// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.10; import {IERC20WithPermit} from 'solidity-utils/contracts/oz-common/interfaces/IERC20WithPermit.sol'; diff --git a/src/contracts/MigrationHelperV3V3.sol b/src/contracts/MigrationHelperV3V3.sol new file mode 100644 index 0000000..59c3c12 --- /dev/null +++ b/src/contracts/MigrationHelperV3V3.sol @@ -0,0 +1,259 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.10; + +import {IERC20WithPermit} from 'solidity-utils/contracts/oz-common/interfaces/IERC20WithPermit.sol'; +import {SafeERC20} from 'solidity-utils/contracts/oz-common/SafeERC20.sol'; +import {IPool as IV3Pool, DataTypes} from 'aave-address-book/AaveV3.sol'; +import {Ownable} from 'solidity-utils/contracts/oz-common/Ownable.sol'; + +import {IMigrationHelperV3V3, IMigrationHelper} from '../interfaces/IMigrationHelperV3V3.sol'; + +/** + * @title MigrationHelperV3V3 + * @author BGD Labs + * @dev Contract to migrate positions from Aave v3 to another Aave v3 pool + */ +contract MigrationHelperV3V3 is Ownable, IMigrationHelperV3V3 { + using SafeERC20 for IERC20WithPermit; + + /// @inheritdoc IMigrationHelperV3V3 + IV3Pool public immutable V3_SOURCE_POOL; + + /// @inheritdoc IMigrationHelperV3V3 + IV3Pool public immutable V3_TARGET_POOL; + + mapping(address => IERC20WithPermit) public aTokens; + mapping(address => IERC20WithPermit) public vTokens; + mapping(address => IERC20WithPermit) public sTokens; + + /** + * @notice Constructor. + * @param v3SourcePool The v3 source pool + * @param v3TargetPool The v3 target pool + */ + constructor(IV3Pool v3SourcePool, IV3Pool v3TargetPool) { + V3_SOURCE_POOL = v3SourcePool; + V3_TARGET_POOL = v3TargetPool; + cacheATokens(); + } + + /// @inheritdoc IMigrationHelper + function cacheATokens() public { + DataTypes.ReserveData memory reserveData; + address[] memory reserves = _getV3Reserves(); + for (uint256 i = 0; i < reserves.length; i++) { + if (address(aTokens[reserves[i]]) == address(0)) { + reserveData = V3_SOURCE_POOL.getReserveData(reserves[i]); + aTokens[reserves[i]] = IERC20WithPermit(reserveData.aTokenAddress); + vTokens[reserves[i]] = IERC20WithPermit(reserveData.variableDebtTokenAddress); + sTokens[reserves[i]] = IERC20WithPermit(reserveData.stableDebtTokenAddress); + + IERC20WithPermit(reserves[i]).safeApprove(address(V3_SOURCE_POOL), type(uint256).max); + IERC20WithPermit(reserves[i]).safeApprove(address(V3_TARGET_POOL), type(uint256).max); + } + } + } + + /// @inheritdoc IMigrationHelper + function migrate( + address[] memory assetsToMigrate, + RepaySimpleInput[] memory positionsToRepay, + PermitInput[] memory permits, + CreditDelegationInput[] memory creditDelegationPermits + ) external { + for (uint256 i = 0; i < permits.length; i++) { + permits[i].aToken.permit( + msg.sender, + address(this), + permits[i].value, + permits[i].deadline, + permits[i].v, + permits[i].r, + permits[i].s + ); + } + + if (positionsToRepay.length == 0) { + _migrationNoBorrow(msg.sender, assetsToMigrate); + } else { + for (uint256 i = 0; i < creditDelegationPermits.length; i++) { + creditDelegationPermits[i].debtToken.delegationWithSig( + msg.sender, + address(this), + creditDelegationPermits[i].value, + creditDelegationPermits[i].deadline, + creditDelegationPermits[i].v, + creditDelegationPermits[i].r, + creditDelegationPermits[i].s + ); + } + + ( + RepayInput[] memory positionsToRepayWithAmounts, + address[] memory assetsToFlash, + uint256[] memory amountsToFlash, + uint256[] memory interestRatesToFlash + ) = _getFlashloanParams(positionsToRepay); + + V3_TARGET_POOL.flashLoan( + address(this), + assetsToFlash, + amountsToFlash, + interestRatesToFlash, + msg.sender, + abi.encode(assetsToMigrate, positionsToRepayWithAmounts, msg.sender), + 6671 + ); + } + } + + /** + * @dev expected structure of the params: + * assetsToMigrate - the list of supplied assets to migrate + * positionsToRepay - the list of borrowed positions, asset address, amount and debt type should be provided + * beneficiary - the user who requested the migration + @inheritdoc IMigrationHelper + */ + function executeOperation( + address[] calldata, + uint256[] calldata, + uint256[] calldata, + address initiator, + bytes calldata params + ) external returns (bool) { + require(msg.sender == address(V3_TARGET_POOL), 'ONLY_V3_POOL_ALLOWED'); + require(initiator == address(this), 'ONLY_INITIATED_BY_MIGRATION_HELPER'); + + (address[] memory assetsToMigrate, RepayInput[] memory positionsToRepay, address user) = abi + .decode(params, (address[], RepayInput[], address)); + + for (uint256 i = 0; i < positionsToRepay.length; i++) { + V3_SOURCE_POOL.repay( + positionsToRepay[i].asset, + positionsToRepay[i].amount, + positionsToRepay[i].rateMode, + user + ); + } + + _migrationNoBorrow(user, assetsToMigrate); + + return true; + } + + /// @inheritdoc IMigrationHelper + function getMigrationSupply( + address asset, + uint256 amount + ) external view virtual returns (address, uint256) { + return (asset, amount); + } + + // helper method to get v3 reserves addresses for migration + // mostly needed to make overrides simpler on specific markets with many available reserves, but few valid + function _getV3Reserves() internal virtual returns (address[] memory) { + return V3_SOURCE_POOL.getReservesList(); + } + + function _migrationNoBorrow(address user, address[] memory assets) internal { + address asset; + IERC20WithPermit aToken; + uint256 aTokenAmountToMigrate; + uint256 aTokenBalanceAfterReceiving; + + for (uint256 i = 0; i < assets.length; i++) { + asset = assets[i]; + aToken = aTokens[asset]; + + require(asset != address(0) && address(aToken) != address(0), 'INVALID_OR_NOT_CACHED_ASSET'); + + aTokenAmountToMigrate = aToken.balanceOf(user); + aToken.safeTransferFrom(user, address(this), aTokenAmountToMigrate); + + // this part of logic needed because of the possible 1-3 wei imprecision after aToken transfer, for example on stETH + aTokenBalanceAfterReceiving = aToken.balanceOf(address(this)); + if ( + aTokenAmountToMigrate != aTokenBalanceAfterReceiving && + aTokenBalanceAfterReceiving <= aTokenAmountToMigrate + 2 + ) { + aTokenAmountToMigrate = aTokenBalanceAfterReceiving; + } + + uint256 withdrawn = V3_SOURCE_POOL.withdraw(asset, aTokenAmountToMigrate, address(this)); + + // there are cases when we transform asset before supplying it to v3 + (address assetToSupply, uint256 amountToSupply) = _preSupply(asset, withdrawn); + + V3_TARGET_POOL.supply(assetToSupply, amountToSupply, user, 6671); + } + } + + function _preSupply(address asset, uint256 amount) internal virtual returns (address, uint256) { + return (asset, amount); + } + + function _getFlashloanParams( + RepaySimpleInput[] memory positionsToRepay + ) + internal + view + returns (RepayInput[] memory, address[] memory, uint256[] memory, uint256[] memory) + { + RepayInput[] memory positionsToRepayWithAmounts = new RepayInput[](positionsToRepay.length); + + uint256 numberOfAssetsToFlash; + address[] memory assetsToFlash = new address[](positionsToRepay.length); + uint256[] memory amountsToFlash = new uint256[](positionsToRepay.length); + uint256[] memory interestRatesToFlash = new uint256[](positionsToRepay.length); + + for (uint256 i = 0; i < positionsToRepay.length; i++) { + IERC20WithPermit debtToken = positionsToRepay[i].rateMode == 2 + ? vTokens[positionsToRepay[i].asset] + : sTokens[positionsToRepay[i].asset]; + require(address(debtToken) != address(0), 'THIS_TYPE_OF_DEBT_NOT_SET'); + + positionsToRepayWithAmounts[i] = RepayInput({ + asset: positionsToRepay[i].asset, + amount: debtToken.balanceOf(msg.sender), + rateMode: positionsToRepay[i].rateMode + }); + + bool amountIncludedIntoFlash; + + // if asset was also borrowed in another mode - add values + for (uint256 j = 0; j < numberOfAssetsToFlash; j++) { + if (assetsToFlash[j] == positionsToRepay[i].asset) { + amountsToFlash[j] += positionsToRepayWithAmounts[i].amount; + amountIncludedIntoFlash = true; + break; + } + } + + // if this is the first ocurance of the asset add it + if (!amountIncludedIntoFlash) { + assetsToFlash[numberOfAssetsToFlash] = positionsToRepayWithAmounts[i].asset; + amountsToFlash[numberOfAssetsToFlash] = positionsToRepayWithAmounts[i].amount; + interestRatesToFlash[numberOfAssetsToFlash] = 2; // @dev variable debt + + ++numberOfAssetsToFlash; + } + } + + // we do not know the length in advance, so we init arrays with the maximum possible length + // and then squeeze the array using mstore + assembly { + mstore(assetsToFlash, numberOfAssetsToFlash) + mstore(amountsToFlash, numberOfAssetsToFlash) + mstore(interestRatesToFlash, numberOfAssetsToFlash) + } + + return (positionsToRepayWithAmounts, assetsToFlash, amountsToFlash, interestRatesToFlash); + } + + /// @inheritdoc IMigrationHelper + function rescueFunds(EmergencyTransferInput[] calldata emergencyInput) external onlyOwner { + for (uint256 i = 0; i < emergencyInput.length; i++) { + emergencyInput[i].asset.safeTransfer(emergencyInput[i].to, emergencyInput[i].amount); + } + } +} diff --git a/src/interfaces/IMigrationHelper.sol b/src/interfaces/IMigrationHelper.sol index 1e3a5a7..1b1a4a9 100644 --- a/src/interfaces/IMigrationHelper.sol +++ b/src/interfaces/IMigrationHelper.sol @@ -99,12 +99,6 @@ interface IMigrationHelper { uint256 amount ) external view returns (address, uint256); - /// @notice The source pool - function V2_POOL() external returns (IV2Pool); - - /// @notice The destination pool - function V3_POOL() external returns (IV3Pool); - /** * @notice Public method for rescue funds in case of a wrong transfer * @param emergencyInput - array of parameters to transfer out funds diff --git a/src/interfaces/IMigrationHelperV2V3.sol b/src/interfaces/IMigrationHelperV2V3.sol new file mode 100644 index 0000000..f81fca0 --- /dev/null +++ b/src/interfaces/IMigrationHelperV2V3.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IERC20WithPermit} from 'solidity-utils/contracts/oz-common/interfaces/IERC20WithPermit.sol'; +import {ILendingPool as IV2Pool} from 'aave-address-book/AaveV2.sol'; +import {IPool as IV3Pool} from 'aave-address-book/AaveV3.sol'; + +import {ICreditDelegationToken} from './ICreditDelegationToken.sol'; +import {IMigrationHelper} from './IMigrationHelper.sol'; + +/** + * @title IMigrationHelper + * @author BGD Labs + * @notice Defines the interface for the contract to migrate positions from Aave v2 to Aave v3 pool + **/ +interface IMigrationHelperV2V3 is IMigrationHelper { + /// @notice The source pool + function V2_POOL() external returns (IV2Pool); + + /// @notice The destination pool + function V3_POOL() external returns (IV3Pool); +} diff --git a/src/interfaces/IMigrationHelperV3V3.sol b/src/interfaces/IMigrationHelperV3V3.sol new file mode 100644 index 0000000..72fb558 --- /dev/null +++ b/src/interfaces/IMigrationHelperV3V3.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IERC20WithPermit} from 'solidity-utils/contracts/oz-common/interfaces/IERC20WithPermit.sol'; +import {IPool as IV3Pool} from 'aave-address-book/AaveV3.sol'; + +import {ICreditDelegationToken} from './ICreditDelegationToken.sol'; +import {IMigrationHelper} from './IMigrationHelper.sol'; + +/** + * @title IMigrationHelperV3V3 + * @author BGD Labs + * @notice Defines the interface for the contract to migrate positions from Aave v3 to another Aave v3 pool + **/ +interface IMigrationHelperV3V3 is IMigrationHelper { + /// @notice The source pool + function V3_SOURCE_POOL() external returns (IV3Pool); + + /// @notice The destination pool + function V3_TARGET_POOL() external returns (IV3Pool); +} diff --git a/tests/MigrationHelperV3V3.t.sol b/tests/MigrationHelperV3V3.t.sol new file mode 100644 index 0000000..b265c05 --- /dev/null +++ b/tests/MigrationHelperV3V3.t.sol @@ -0,0 +1,452 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {Test} from 'forge-std/Test.sol'; +import {console} from 'forge-std/console.sol'; + +import {AaveV3Polygon} from 'aave-address-book/AaveV3Polygon.sol'; + +import {IERC20} from 'solidity-utils/contracts/oz-common/interfaces/IERC20.sol'; +import {ICreditDelegationToken} from '../src/interfaces/ICreditDelegationToken.sol'; +import {IERC20WithATokenCompatibility} from './helpers/IERC20WithATokenCompatibility.sol'; + +import {DataTypes, IAaveProtocolDataProvider as IAaveProtocolDataProviderV3} from 'aave-address-book/AaveV3.sol'; + +import {MigrationHelperV3V3, IMigrationHelperV3V3, IMigrationHelper, IERC20WithPermit} from '../src/contracts/MigrationHelperV3V3.sol'; + +import {SigUtils} from './helpers/SigUtils.sol'; + +contract MigrationHelperTest is Test { + IAaveProtocolDataProviderV3 public v3DataProvider; + MigrationHelperV3V3 public migrationHelper; + SigUtils public sigUtils; + + address public constant DAI = 0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063; + address public constant ETH = 0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619; + + address[] public usersSimple; + address[] public usersWithDebt; + address[] public v3Reserves; + + mapping(address => uint256) private assetsIndex; + + function setUp() public { + vm.createSelectFork(vm.rpcUrl('polygon'), 47967100); + migrationHelper = new MigrationHelperV3V3(AaveV3Polygon.POOL, AaveV3Polygon.POOL); + + v3DataProvider = AaveV3Polygon.AAVE_PROTOCOL_DATA_PROVIDER; + v3Reserves = migrationHelper.V3_SOURCE_POOL().getReservesList(); + + sigUtils = new SigUtils(); + + // @dev users who has only supplied positions, no borrowings + usersSimple = new address[](1); + usersSimple[0] = 0xeE071f4B516F69a1603dA393CdE8e76C40E5Be85; + } + + function testCacheATokens() public { + for (uint256 i = 0; i < v3Reserves.length; i++) { + DataTypes.ReserveData memory reserveData = migrationHelper.V3_SOURCE_POOL().getReserveData( + v3Reserves[i] + ); + assertEq(address(migrationHelper.aTokens(v3Reserves[i])), reserveData.aTokenAddress); + + uint256 allowanceToPoolV3 = IERC20(v3Reserves[i]).allowance( + address(migrationHelper), + address(migrationHelper.V3_SOURCE_POOL()) + ); + assertEq(allowanceToPoolV3, type(uint256).max); + + uint256 allowanceToPool = IERC20(v3Reserves[i]).allowance( + address(migrationHelper), + address(migrationHelper.V3_TARGET_POOL()) + ); + assertEq(allowanceToPool, type(uint256).max); + } + } + + function testMigrationNoBorrowNoPermit() public { + address[] memory suppliedPositions; + uint256[] memory suppliedBalances; + MigrationHelperV3V3.RepayInput[] memory borrowedPositions; + + for (uint256 i = 0; i < usersSimple.length; i++) { + // get positions + (suppliedPositions, suppliedBalances, borrowedPositions) = _getV3UserPosition(usersSimple[i]); + + require( + borrowedPositions.length == 0 && suppliedPositions.length != 0, + 'BAD_USER_FOR_THIS_TEST' + ); + + vm.startPrank(usersSimple[i]); + // approve aTokens to helper + for (uint256 j = 0; j < suppliedPositions.length; j++) { + migrationHelper.aTokens(suppliedPositions[j]).approve( + address(migrationHelper), + type(uint256).max + ); + } + + // migrate positions to V3 + migrationHelper.migrate( + suppliedPositions, + new MigrationHelperV3V3.RepaySimpleInput[](0), + new MigrationHelperV3V3.PermitInput[](0), + new MigrationHelperV3V3.CreditDelegationInput[](0) + ); + + vm.stopPrank(); + + // check that positions were migrated successfully + _checkMigratedSupplies(usersSimple[i], suppliedPositions, suppliedBalances); + } + } + + function testMigrationNoBorrowWithPermit() public { + (address user, uint256 privateKey) = _getUserWithPosition(); + + // get positions + (address[] memory suppliedPositions, uint256[] memory suppliedBalances, ) = _getV3UserPosition( + user + ); + + // calculate permits + MigrationHelperV3V3.PermitInput[] memory permits = _getPermits( + user, + privateKey, + suppliedPositions, + suppliedBalances + ); + + vm.startPrank(user); + + // migrate positions to V3 + migrationHelper.migrate( + suppliedPositions, + new MigrationHelperV3V3.RepaySimpleInput[](0), + permits, + new MigrationHelperV3V3.CreditDelegationInput[](0) + ); + + vm.stopPrank(); + + // check that positions were migrated successfully + _checkMigratedSupplies(user, suppliedPositions, suppliedBalances); + } + + function testMigrationWithCreditDelegation() public { + (address user, uint256 privateKey) = _getUserWithBorrowPosition(); + // get positions + ( + address[] memory suppliedPositions, + uint256[] memory suppliedBalances, + MigrationHelperV3V3.RepayInput[] memory positionsToRepay + ) = _getV3UserPosition(user); + + MigrationHelperV3V3.RepaySimpleInput[] + memory positionsToRepaySimple = _getSimplePositionsToRepay(positionsToRepay); + + // calculate permits + MigrationHelperV3V3.PermitInput[] memory permits = _getPermits( + user, + privateKey, + suppliedPositions, + suppliedBalances + ); + + // calculate credit + MigrationHelperV3V3.CreditDelegationInput[] memory creditDelegations = _getCreditDelegations( + user, + privateKey, + positionsToRepay + ); + + // migrate positions to V3 + vm.startPrank(user); + + migrationHelper.migrate(suppliedPositions, positionsToRepaySimple, permits, creditDelegations); + + vm.stopPrank(); + + // check that positions were migrated successfully + _checkMigratedSupplies(user, suppliedPositions, suppliedBalances); + + _checkMigratedBorrowings(user, positionsToRepay); + } + + function _checkMigratedSupplies( + address user, + address[] memory suppliedPositions, + uint256[] memory suppliedBalances + ) internal { + for (uint256 i = 0; i < suppliedPositions.length; i++) { + (uint256 currentATokenBalance, , , , , , , , ) = v3DataProvider.getUserReserveData( + suppliedPositions[i], + user + ); + + assertTrue(currentATokenBalance >= suppliedBalances[i]); + } + } + + function _checkMigratedBorrowings( + address user, + MigrationHelperV3V3.RepayInput[] memory borrowedPositions + ) internal { + for (uint256 i = 0; i < borrowedPositions.length; i++) { + (, , uint256 currentVariableDebt, , , , , , ) = v3DataProvider.getUserReserveData( + borrowedPositions[i].asset, + user + ); + + assertTrue(currentVariableDebt >= borrowedPositions[i].amount); + } + } + + function _getV3UserPosition( + address user + ) + internal + view + returns (address[] memory, uint256[] memory, MigrationHelperV3V3.RepayInput[] memory) + { + uint256 numberOfSupplied; + uint256 numberOfBorrowed; + address[] memory suppliedPositions = new address[](v3Reserves.length); + uint256[] memory suppliedBalances = new uint256[](v3Reserves.length); + MigrationHelperV3V3.RepayInput[] + memory borrowedPositions = new MigrationHelperV3V3.RepayInput[](v3Reserves.length * 2); + for (uint256 i = 0; i < v3Reserves.length; i++) { + ( + uint256 currentATokenBalance, + uint256 currentStableDebt, + uint256 currentVariableDebt, + , + , + , + , + , + + ) = v3DataProvider.getUserReserveData(v3Reserves[i], user); + if (currentATokenBalance != 0) { + suppliedPositions[numberOfSupplied] = v3Reserves[i]; + suppliedBalances[numberOfSupplied] = currentATokenBalance; + numberOfSupplied++; + } + if (currentStableDebt != 0) { + borrowedPositions[numberOfBorrowed] = IMigrationHelper.RepayInput({ + asset: v3Reserves[i], + amount: currentStableDebt, + rateMode: 1 + }); + numberOfBorrowed++; + } + if (currentVariableDebt != 0) { + borrowedPositions[numberOfBorrowed] = IMigrationHelper.RepayInput({ + asset: v3Reserves[i], + amount: currentVariableDebt, + rateMode: 2 + }); + numberOfBorrowed++; + } + } + + // shrink unused elements of the arrays + assembly { + mstore(suppliedPositions, numberOfSupplied) + mstore(suppliedBalances, numberOfSupplied) + mstore(borrowedPositions, numberOfBorrowed) + } + + return (suppliedPositions, suppliedBalances, borrowedPositions); + } + + function _getSimplePositionsToRepay( + MigrationHelperV3V3.RepayInput[] memory positionsToRepay + ) internal pure returns (MigrationHelperV3V3.RepaySimpleInput[] memory) { + MigrationHelperV3V3.RepaySimpleInput[] + memory positionsToRepaySimple = new MigrationHelperV3V3.RepaySimpleInput[]( + positionsToRepay.length + ); + for (uint256 i; i < positionsToRepay.length; ++i) { + positionsToRepaySimple[i] = IMigrationHelper.RepaySimpleInput({ + asset: positionsToRepay[i].asset, + rateMode: positionsToRepay[i].rateMode + }); + } + + return positionsToRepaySimple; + } + + function _getFlashloanParams( + MigrationHelperV3V3.RepayInput[] memory borrowedPositions + ) internal returns (address[] memory, uint256[] memory, uint256[] memory) { + address[] memory borrowedAssets = new address[](borrowedPositions.length); + uint256[] memory borrowedAmounts = new uint256[](borrowedPositions.length); + uint256[] memory interestRateModes = new uint256[](borrowedPositions.length); + uint256 index = 0; + + for (uint256 i = 0; i < borrowedPositions.length; i++) { + address asset = borrowedPositions[i].asset; + uint256 amount = borrowedPositions[i].amount; + + uint256 existingIndex = assetsIndex[asset]; + + if (existingIndex > 0) { + borrowedAmounts[existingIndex - 1] += amount; + } else { + assetsIndex[asset] = index + 1; + borrowedAssets[index] = asset; + borrowedAmounts[index] = amount; + interestRateModes[index] = 2; + index++; + } + } + + // clean mapping + for (uint256 i = 0; i < borrowedAssets.length; i++) { + delete assetsIndex[borrowedAssets[i]]; + } + + // shrink unused elements of the arrays + assembly { + mstore(borrowedAssets, index) + mstore(borrowedAmounts, index) + mstore(interestRateModes, index) + } + + return (borrowedAssets, borrowedAmounts, interestRateModes); + } + + function _getUserWithPosition() internal returns (address, uint256) { + uint256 ownerPrivateKey = 0xA11CEA; + + address owner = vm.addr(ownerPrivateKey); + deal(DAI, owner, 10000e18); + deal(ETH, owner, 10e18); + + vm.startPrank(owner); + + IERC20(DAI).approve(address(migrationHelper.V3_SOURCE_POOL()), type(uint256).max); + IERC20(ETH).approve(address(migrationHelper.V3_SOURCE_POOL()), type(uint256).max); + + migrationHelper.V3_SOURCE_POOL().deposit(DAI, 10000 ether, owner, 0); + migrationHelper.V3_SOURCE_POOL().deposit(ETH, 10 ether, owner, 0); + + vm.stopPrank(); + + return (owner, ownerPrivateKey); + } + + function _getUserWithBorrowPosition() internal returns (address, uint256) { + uint256 ownerPrivateKey = 0xA11CEB; + + address owner = vm.addr(ownerPrivateKey); + deal(DAI, owner, 10000e18); + deal(ETH, owner, 10e18); + + vm.startPrank(owner); + + IERC20(DAI).approve(address(migrationHelper.V3_SOURCE_POOL()), type(uint256).max); + IERC20(ETH).approve(address(migrationHelper.V3_SOURCE_POOL()), type(uint256).max); + + migrationHelper.V3_SOURCE_POOL().deposit(DAI, 10000 ether, owner, 0); + migrationHelper.V3_SOURCE_POOL().deposit(ETH, 10 ether, owner, 0); + + // migrationHelper.V3_SOURCE_POOL().borrow(ETH, 2 ether, 1, 0, owner); + migrationHelper.V3_SOURCE_POOL().borrow(ETH, 1 ether, 2, 0, owner); + + vm.stopPrank(); + + return (owner, ownerPrivateKey); + } + + function _getPermits( + address user, + uint256 privateKey, + address[] memory suppliedPositions, + uint256[] memory suppliedBalances + ) internal view returns (MigrationHelperV3V3.PermitInput[] memory) { + MigrationHelperV3V3.PermitInput[] memory permits = new MigrationHelperV3V3.PermitInput[]( + suppliedPositions.length + ); + + for (uint256 i = 0; i < suppliedPositions.length; i++) { + IERC20WithPermit token = migrationHelper.aTokens(suppliedPositions[i]); + + SigUtils.Permit memory permit = SigUtils.Permit({ + owner: user, + spender: address(migrationHelper), + value: suppliedBalances[i], + nonce: IERC20WithATokenCompatibility(address(token))._nonces(user), + deadline: type(uint256).max + }); + + bytes32 digest = sigUtils.getPermitTypedDataHash(permit, token.DOMAIN_SEPARATOR()); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest); + + permits[i] = IMigrationHelper.PermitInput({ + aToken: token, + value: suppliedBalances[i], + deadline: type(uint256).max, + v: v, + r: r, + s: s + }); + } + + return permits; + } + + function _getCreditDelegations( + address user, + uint256 privateKey, + IMigrationHelper.RepayInput[] memory positionsToRepay + ) internal returns (IMigrationHelper.CreditDelegationInput[] memory) { + IMigrationHelper.CreditDelegationInput[] + memory creditDelegations = new IMigrationHelper.CreditDelegationInput[]( + positionsToRepay.length + ); + + // calculate params for v3 credit delegation + (address[] memory borrowedAssets, uint256[] memory borrowedAmounts, ) = _getFlashloanParams( + positionsToRepay + ); + + for (uint256 i = 0; i < borrowedAssets.length; i++) { + // get v3 variable debt token + DataTypes.ReserveData memory reserveData = migrationHelper.V3_TARGET_POOL().getReserveData( + borrowedAssets[i] + ); + + IERC20WithPermit token = IERC20WithPermit(reserveData.variableDebtTokenAddress); + + SigUtils.CreditDelegation memory creditDelegation = SigUtils.CreditDelegation({ + delegatee: address(migrationHelper), + value: borrowedAmounts[i], + nonce: token.nonces(user), + deadline: type(uint256).max + }); + + bytes32 digest = sigUtils.getCreditDelegationTypedDataHash( + creditDelegation, + token.DOMAIN_SEPARATOR() + ); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest); + + creditDelegations[i] = IMigrationHelper.CreditDelegationInput({ + debtToken: ICreditDelegationToken(address(token)), + value: borrowedAmounts[i], + deadline: type(uint256).max, + v: v, + r: r, + s: s + }); + } + + return creditDelegations; + } +} diff --git a/yarn.lock b/yarn.lock index 00a7249..62b76c8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9,7 +9,7 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@ethersproject/abi@^5.0.0", "@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== @@ -24,7 +24,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== @@ -37,7 +37,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== @@ -48,7 +48,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== @@ -59,14 +59,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== @@ -74,7 +74,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== @@ -83,14 +83,14 @@ "@ethersproject/logger" "^5.7.0" bn.js "^5.2.1" -"@ethersproject/bytes@^5.0.0", "@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== @@ -113,7 +113,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== @@ -128,7 +128,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== @@ -146,7 +146,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== @@ -165,7 +165,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== @@ -173,19 +173,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.0", "@ethersproject/networks@5.7.1": +"@ethersproject/networks@5.7.1", "@ethersproject/networks@^5.7.0": version "5.7.1" resolved "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.1.tgz" integrity sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ== 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== @@ -193,14 +193,14 @@ "@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== dependencies: "@ethersproject/logger" "^5.7.0" -"@ethersproject/providers@^5.0.0", "@ethersproject/providers@5.7.2": +"@ethersproject/providers@5.7.2": version "5.7.2" resolved "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.7.2.tgz" integrity sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg== @@ -226,7 +226,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== @@ -234,7 +234,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== @@ -242,7 +242,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== @@ -251,7 +251,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== @@ -275,7 +275,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== @@ -284,7 +284,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== @@ -329,7 +329,7 @@ "@ethersproject/transactions" "^5.7.0" "@ethersproject/wordlists" "^5.7.0" -"@ethersproject/web@^5.7.0", "@ethersproject/web@5.7.1": +"@ethersproject/web@5.7.1", "@ethersproject/web@^5.7.0": version "5.7.1" resolved "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.1.tgz" integrity sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w== @@ -340,7 +340,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== @@ -404,11 +404,6 @@ lodash "^4.17.15" ts-essentials "^7.0.1" -"@types/node@*": - version "18.11.18" - resolved "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz" - integrity sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA== - "@types/prettier@^2.1.1": version "2.7.2" resolved "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz" @@ -458,12 +453,7 @@ array-back@^3.0.1, array-back@^3.1.0: resolved "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz" integrity sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q== -array-back@^4.0.1: - version "4.0.2" - resolved "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz" - integrity sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg== - -array-back@^4.0.2: +array-back@^4.0.1, array-back@^4.0.2: version "4.0.2" resolved "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz" integrity sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg== @@ -532,16 +522,16 @@ color-convert@^2.0.1: dependencies: color-name "~1.1.4" -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - color-name@1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + command-line-args@^5.1.1: version "5.2.1" resolved "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz" @@ -607,7 +597,7 @@ escape-string-regexp@^1.0.5: resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== -ethers@^5.1.3, ethers@^5.7.2: +ethers@^5.7.2: version "5.7.2" resolved "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz" integrity sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg== @@ -691,7 +681,7 @@ has-flag@^4.0.0: resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -hash.js@^1.0.0, hash.js@^1.0.3, hash.js@1.1.7: +hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3: version "1.1.7" resolved "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz" integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== @@ -716,12 +706,12 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@^2.0.3, inherits@^2.0.4, inherits@2: +inherits@2, inherits@^2.0.3, inherits@^2.0.4: version "2.0.4" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -js-sha3@^0.8.0, js-sha3@0.8.0: +js-sha3@0.8.0, js-sha3@^0.8.0: version "0.8.0" resolved "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz" integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== @@ -803,7 +793,7 @@ prettier-plugin-solidity@^1.1.1: semver "^7.3.8" solidity-comments-extractor "^0.0.7" -prettier@^2.3.1, prettier@^2.8.3, "prettier@>=2.3.0 || >=3.0.0-alpha.0": +prettier@^2.3.1, prettier@^2.8.3: version "2.8.3" resolved "https://registry.npmjs.org/prettier/-/prettier-2.8.3.tgz" integrity sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw== @@ -909,11 +899,6 @@ typechain@^8.1.1: ts-command-line-args "^2.2.0" ts-essentials "^7.0.1" -typescript@>=2.7, typescript@>=3.7.0, typescript@>=4.3.0: - version "4.9.4" - resolved "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz" - integrity sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg== - typical@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz"