From 7c51597282d7ff6f5a4522701f76e12f7e52210f Mon Sep 17 00:00:00 2001 From: Gerardo Nardelli Date: Tue, 27 Oct 2020 09:17:33 -0300 Subject: [PATCH 01/62] Add uniswap adapters for liquidity swap and repay with collateral --- contracts/adapters/BaseUniswapAdapter.sol | 215 ++++++++++++++++++ .../adapters/UniswapLiquiditySwapAdapter.sol | 63 +++++ contracts/adapters/UniswapRepayAdapter.sol | 71 ++++++ 3 files changed, 349 insertions(+) create mode 100644 contracts/adapters/BaseUniswapAdapter.sol create mode 100644 contracts/adapters/UniswapLiquiditySwapAdapter.sol create mode 100644 contracts/adapters/UniswapRepayAdapter.sol diff --git a/contracts/adapters/BaseUniswapAdapter.sol b/contracts/adapters/BaseUniswapAdapter.sol new file mode 100644 index 00000000..79b55df2 --- /dev/null +++ b/contracts/adapters/BaseUniswapAdapter.sol @@ -0,0 +1,215 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity ^0.6.8; +pragma experimental ABIEncoderV2; + +import {PercentageMath} from '../libraries/math/PercentageMath.sol'; +import {SafeMath} from '../dependencies/openzeppelin/contracts/SafeMath.sol'; +import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol'; +import {SafeERC20} from '../dependencies/openzeppelin/contracts/SafeERC20.sol'; +import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol'; +import {ILendingPool} from '../interfaces/ILendingPool.sol'; +import {ReserveLogic} from '../libraries/logic/ReserveLogic.sol'; +import {ReserveConfiguration} from '../libraries/configuration/ReserveConfiguration.sol'; +import {IUniswapV2Router02} from '../interfaces/IUniswapV2Router02.sol'; +import {IPriceOracleGetter} from '../interfaces/IPriceOracleGetter.sol'; + + +/** + * @title BaseUniswapAdapter + * @notice Implements the logic for performing assets swaps in Uniswap V2 + * @author Aave + **/ +contract BaseUniswapAdapter { + using SafeMath for uint256; + using PercentageMath for uint256; + using SafeERC20 for IERC20; + using ReserveConfiguration for ReserveConfiguration.Map; + + // Max slippage percent allow by param + uint256 public constant MAX_SLIPPAGE_PERCENT = 3000; // 30% + // Min slippage percent allow by param + uint256 public constant MIN_SLIPPAGE_PERCENT = 10; // 0,1% + + ILendingPoolAddressesProvider public immutable addressesProvider; + IUniswapV2Router02 public immutable uniswapRouter; + ILendingPool public immutable pool; + + event Swapped(address fromAsset, address toAsset, uint256 fromAmount, uint256 receivedAmount); + + constructor(ILendingPoolAddressesProvider _addressesProvider, IUniswapV2Router02 _uniswapRouter) public { + addressesProvider = _addressesProvider; + pool = ILendingPool(_addressesProvider.getLendingPool()); + uniswapRouter = _uniswapRouter; + } + + /** + * @dev Swaps an `amountToSwap` of an asset to another + * @param assetToSwapFrom Origin asset + * @param assetToSwapTo Destination asset + * @param amountToSwap Exact amount of `assetToSwapFrom` to be swapped + * @param slippage the max slippage percentage allowed for the swap + * @return the amount received from the swap + */ + function swapExactTokensForTokens( + address assetToSwapFrom, + address assetToSwapTo, + uint256 amountToSwap, + uint256 slippage + ) + internal + returns (uint256) + { + uint256 fromAssetDecimals = getDecimals(assetToSwapFrom); + uint256 toAssetDecimals = getDecimals(assetToSwapTo); + + (uint256 fromAssetPrice, uint256 toAssetPrice) = getPrices(assetToSwapFrom, assetToSwapTo); + + uint256 amountOutMin = amountToSwap + .mul(fromAssetPrice.mul(10**toAssetDecimals)) + .div(toAssetPrice.mul(10**fromAssetDecimals)) + .percentMul(PercentageMath.PERCENTAGE_FACTOR.sub(slippage)); + + IERC20(assetToSwapFrom).approve(address(uniswapRouter), amountToSwap); + + address[] memory path = new address[](2); + path[0] = assetToSwapFrom; + path[1] = assetToSwapTo; + uint256[] memory amounts = uniswapRouter.swapExactTokensForTokens(amountToSwap, amountOutMin, path, address(this), block.timestamp); + + require(amounts[1] >= amountOutMin, 'INSUFFICIENT_OUTPUT_AMOUNT'); + + emit Swapped(assetToSwapFrom, assetToSwapTo, amounts[0], amounts[1]); + + return amounts[1]; + } + + /** + * @dev Receive an exact amount `amountToReceive` of `assetToSwapTo` tokens for as few `assetToSwapFrom` tokens as + * possible. + * @param assetToSwapFrom Origin asset + * @param assetToSwapTo Destination asset + * @param maxAmountToSwap Max amount of `assetToSwapFrom` allowed to be swapped + * @param amountToReceive Exact amount of `assetToSwapTo` to receive + * @return the amount received from the swap + */ + function swapTokensForExactTokens( + address assetToSwapFrom, + address assetToSwapTo, + uint256 maxAmountToSwap, + uint256 amountToReceive + ) + internal + returns (uint256) + { + uint256 fromAssetDecimals = getDecimals(assetToSwapFrom); + uint256 toAssetDecimals = getDecimals(assetToSwapTo); + + (uint256 fromAssetPrice, uint256 toAssetPrice) = getPrices(assetToSwapFrom, assetToSwapTo); + + uint256 expectedMaxAmountToSwap = amountToReceive + .mul(toAssetPrice.mul(10**fromAssetDecimals)) + .div(fromAssetPrice.mul(10**toAssetDecimals)) + .percentMul(PercentageMath.PERCENTAGE_FACTOR.add(MAX_SLIPPAGE_PERCENT)); + + require(maxAmountToSwap < expectedMaxAmountToSwap, 'maxAmountToSwap exceed max slippage'); + + IERC20(assetToSwapFrom).approve(address(uniswapRouter), maxAmountToSwap); + + address[] memory path = new address[](2); + path[0] = assetToSwapFrom; + path[1] = assetToSwapTo; + uint256[] memory amounts = uniswapRouter.swapTokensForExactTokens(amountToReceive, maxAmountToSwap, path, address(this), block.timestamp); + + require(amounts[1] >= amountToReceive, 'INSUFFICIENT_OUTPUT_AMOUNT'); + + emit Swapped(assetToSwapFrom, assetToSwapTo, amounts[0], amounts[1]); + + return amounts[1]; + } + + /** + * @dev Get assets prices from the oracle denominated in eth + * @param assetToSwapFrom first asset + * @param assetToSwapTo second asset + * @return fromAssetPrice eth price for the first asset + * @return toAssetPrice eth price for the second asset + */ + function getPrices( + address assetToSwapFrom, + address assetToSwapTo + ) + internal + view + returns (uint256 fromAssetPrice, uint256 toAssetPrice) + { + IPriceOracleGetter oracle = IPriceOracleGetter(addressesProvider.getPriceOracle()); + fromAssetPrice = oracle.getAssetPrice(assetToSwapFrom); + toAssetPrice = oracle.getAssetPrice(assetToSwapTo); + } + + /** + * @dev Get the decimals of an asset + * @return number of decimals of the asset + */ + function getDecimals(address asset) internal view returns (uint256) { + ReserveConfiguration.Map memory configuration = pool.getConfiguration(asset); + (, , , uint256 decimals, ) = configuration.getParamsMemory(); + + return decimals; + } + + /** + * @dev Get the aToken associated to the asset + * @return address of the aToken + */ + function getAToken(address asset) internal view returns (address) { + ReserveLogic.ReserveData memory reserve = pool.getReserveData(asset); + return reserve.aTokenAddress; + } + + /** + * @dev Take action with the swap left overs as configured in the parameters + * @param asset address of the asset + * @param reservedAmount Amount reserved to be used by the contract to repay the flash loan + * @param leftOverAction Flag indicating what to do with the left over balance from the swap: + * (0) Deposit back + * (1) Direct transfer to user + * @param user address + */ + function sendLeftOver(address asset, uint256 reservedAmount, uint256 leftOverAction, address user) internal { + uint256 balance = IERC20(asset).balanceOf(address(this)); + uint256 assetLeftOver = balance.sub(reservedAmount); + + if (assetLeftOver > 0) { + if (leftOverAction == 0) { + IERC20(asset).approve(address(pool), balance); + pool.deposit(asset, assetLeftOver, user, 0); + } else { + IERC20(asset).transfer(user, assetLeftOver); + } + } + } + + /** + * @dev Take action with the swap left overs as configured in the parameters + * @param reserve address of the asset + * @param user address + * @param flashLoanDebt need to be repaid + */ + function pullATokenAndRepayFlashLoan( + address reserve, + address user, + uint256 flashLoanDebt + ) internal { + address reserveAToken = getAToken(reserve); + + // transfer from user to adapter + IERC20(reserveAToken).safeTransferFrom(user, address(this), flashLoanDebt); + + // withdraw reserve + pool.withdraw(reserve, flashLoanDebt); + + // Repay flashloan + IERC20(reserve).approve(address(pool), flashLoanDebt); + } +} diff --git a/contracts/adapters/UniswapLiquiditySwapAdapter.sol b/contracts/adapters/UniswapLiquiditySwapAdapter.sol new file mode 100644 index 00000000..62125264 --- /dev/null +++ b/contracts/adapters/UniswapLiquiditySwapAdapter.sol @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity ^0.6.8; +pragma experimental ABIEncoderV2; + +import {BaseUniswapAdapter} from './BaseUniswapAdapter.sol'; +import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol'; +import {IUniswapV2Router02} from '../interfaces/IUniswapV2Router02.sol'; +import {IFlashLoanReceiver} from '../flashloan/interfaces/IFlashLoanReceiver.sol'; +import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol'; + +/** + * @title UniswapLiquiditySwapAdapter + * @notice Uniswap V2 Adapter to swap liquidity using a flash loan. + * @author Aave + **/ +contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { + + constructor( + ILendingPoolAddressesProvider _addressesProvider, + IUniswapV2Router02 _uniswapRouter + ) + public + BaseUniswapAdapter(_addressesProvider, _uniswapRouter) + {} + + /** + * @dev Swaps the received reserve amount from the flashloan into the asset specified in the params. + * The received funds from the swap are then deposited into the protocol on behalf of the user. + * The user should give this contract allowance to pull the ATokens in order to withdraw the underlying asset and + * repay the flash loan. + * @param reserve Address to be swapped + * @param amount Amount of the reserve to be swapped + * @param fee Fee of the flash loan + * @param params Additional variadic field to include extra params. Expected parameters: + * address assetToSwapTo Address of the reserve to be swapped to and deposited + * address user The address of the user + * uint256 slippage The max slippage percentage allowed for the swap + */ + function executeOperation( + address reserve, + uint256 amount, + uint256 fee, + bytes calldata params + ) external override returns (bool) { + ( + address assetToSwapTo, + address user, + uint256 slippage + ) = abi.decode(params, (address, address, uint256)); + require(slippage < MAX_SLIPPAGE_PERCENT && slippage >= MIN_SLIPPAGE_PERCENT, 'SLIPPAGE_OUT_OF_RANGE'); + + uint256 receivedAmount = swapExactTokensForTokens(reserve, assetToSwapTo, amount, slippage); + + // Deposit new reserve + IERC20(assetToSwapTo).approve(address(pool), receivedAmount); + pool.deposit(assetToSwapTo, receivedAmount, user, 0); + + uint256 flashLoanDebt = amount.add(fee); + pullATokenAndRepayFlashLoan(reserve, user, flashLoanDebt); + + return true; + } +} diff --git a/contracts/adapters/UniswapRepayAdapter.sol b/contracts/adapters/UniswapRepayAdapter.sol new file mode 100644 index 00000000..2bb8a8a8 --- /dev/null +++ b/contracts/adapters/UniswapRepayAdapter.sol @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity ^0.6.8; +pragma experimental ABIEncoderV2; + +import {BaseUniswapAdapter} from './BaseUniswapAdapter.sol'; +import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol'; +import {IUniswapV2Router02} from '../interfaces/IUniswapV2Router02.sol'; +import {IFlashLoanReceiver} from '../flashloan/interfaces/IFlashLoanReceiver.sol'; +import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol'; + +/** + * @title UniswapRepayAdapter + * @notice Uniswap V2 Adapter to perform a repay of a debt using a flash loan. + * @author Aave + **/ +contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { + + constructor( + ILendingPoolAddressesProvider _addressesProvider, + IUniswapV2Router02 _uniswapRouter + ) + public + BaseUniswapAdapter(_addressesProvider, _uniswapRouter) + {} + + /** + * @dev Swaps the received reserve amount into the asset specified in the params. The received funds from the swap are + * then used to repay a debt on the protocol on behalf of the user. + * The user should give this contract allowance to pull the ATokens in order to withdraw the underlying asset and + * repay the flash loan. + * @param reserve Address to be swapped + * @param amount Amount of the reserve to be swapped + * @param fee Fee of the flash loan + * @param params Additional variadic field to include extra params. Expected parameters: + * address assetToSwapTo Address of the reserve to be swapped to and deposited + * address user The address of the user + * uint256 leftOverAction Flag indicating what to do with the left over balance from the swap: + * (0) Deposit back + * (1) Direct transfer to user + * uint256 repayAmount Amount of debt to be repaid + * uint256 rateMode The rate modes of the debt to be repaid + */ + function executeOperation( + address reserve, + uint256 amount, + uint256 fee, + bytes calldata params + ) external override returns (bool) { + ( + address assetToSwapTo, + address user, + uint256 leftOverAction, + uint256 repayAmount, + uint256 rateMode + ) = abi.decode(params, (address, address, uint256, uint256, uint256)); + + swapTokensForExactTokens(reserve, assetToSwapTo, amount, repayAmount); + + // Repay debt + IERC20(assetToSwapTo).approve(address(pool), repayAmount); + pool.repay(assetToSwapTo, repayAmount, rateMode, user); + + uint256 flashLoanDebt = amount.add(fee); + pullATokenAndRepayFlashLoan(reserve, user, flashLoanDebt); + + // Take care of reserve leftover from the swap + sendLeftOver(reserve, flashLoanDebt, leftOverAction, user); + + return true; + } +} From a05b75b467ae08f6417942c469bfa87ab4e45f81 Mon Sep 17 00:00:00 2001 From: Gerardo Nardelli Date: Tue, 27 Oct 2020 09:18:30 -0300 Subject: [PATCH 02/62] Add uniswap adapters unit tests --- contracts/interfaces/IUniswapV2Router02.sol | 20 + .../mocks/swap/MockUniswapV2Router02.sol | 53 ++ helpers/contracts-helpers.ts | 53 +- helpers/types.ts | 4 +- test/__setup.spec.ts | 23 +- test/helpers/make-suite.ts | 15 +- test/pausable-functions.spec.ts | 2 +- test/uniswapAdapters.spec.ts | 805 ++++++++++++++++++ 8 files changed, 958 insertions(+), 17 deletions(-) create mode 100644 contracts/interfaces/IUniswapV2Router02.sol create mode 100644 contracts/mocks/swap/MockUniswapV2Router02.sol create mode 100644 test/uniswapAdapters.spec.ts diff --git a/contracts/interfaces/IUniswapV2Router02.sol b/contracts/interfaces/IUniswapV2Router02.sol new file mode 100644 index 00000000..cb04c269 --- /dev/null +++ b/contracts/interfaces/IUniswapV2Router02.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity ^0.6.8; + +interface IUniswapV2Router02 { + function swapExactTokensForTokens( + uint256 amountIn, + uint256 amountOutMin, + address[] calldata path, + address to, + uint256 deadline + ) external returns (uint256[] memory amounts); + + function swapTokensForExactTokens( + uint amountOut, + uint amountInMax, + address[] calldata path, + address to, + uint deadline + ) external returns (uint256[] memory amounts); +} diff --git a/contracts/mocks/swap/MockUniswapV2Router02.sol b/contracts/mocks/swap/MockUniswapV2Router02.sol new file mode 100644 index 00000000..64cb935f --- /dev/null +++ b/contracts/mocks/swap/MockUniswapV2Router02.sol @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity ^0.6.8; + +import {IUniswapV2Router02} from "../../interfaces/IUniswapV2Router02.sol"; +import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; +import {MintableERC20} from '../tokens/MintableERC20.sol'; + +contract MockUniswapV2Router02 is IUniswapV2Router02 { + uint256 internal _amountToReturn; + uint256 internal _amountToSwap; + + function setAmountToReturn(uint256 amount) public { + _amountToReturn = amount; + } + + function setAmountToSwap(uint256 amount) public { + _amountToSwap = amount; + } + + function swapExactTokensForTokens( + uint256 amountIn, + uint256 /* amountOutMin */, + address[] calldata path, + address to, + uint256 /* deadline */ + ) external override returns (uint256[] memory amounts) { + IERC20(path[0]).transferFrom(msg.sender, address(this), amountIn); + + MintableERC20(path[1]).mint(_amountToReturn); + IERC20(path[1]).transfer(to, _amountToReturn); + + amounts = new uint[](path.length); + amounts[0] = amountIn; + amounts[1] = _amountToReturn; + } + + function swapTokensForExactTokens( + uint amountOut, + uint amountInMax, + address[] calldata path, + address to, + uint /* deadline */ + ) external override returns (uint256[] memory amounts) { + IERC20(path[0]).transferFrom(msg.sender, address(this), _amountToSwap); + + MintableERC20(path[1]).mint(_amountToReturn); + IERC20(path[1]).transfer(to, _amountToReturn); + + amounts = new uint[](path.length); + amounts[0] = _amountToSwap; + amounts[1] = _amountToReturn; + } +} diff --git a/helpers/contracts-helpers.ts b/helpers/contracts-helpers.ts index 77730b04..eaee15ab 100644 --- a/helpers/contracts-helpers.ts +++ b/helpers/contracts-helpers.ts @@ -37,6 +37,9 @@ import BigNumber from 'bignumber.js'; import {Ierc20Detailed} from '../types/Ierc20Detailed'; import {StableDebtToken} from '../types/StableDebtToken'; import {VariableDebtToken} from '../types/VariableDebtToken'; +import {MockUniswapV2Router02} from '../types/MockUniswapV2Router02'; +import {UniswapLiquiditySwapAdapter} from '../types/UniswapLiquiditySwapAdapter'; +import {UniswapRepayAdapter} from '../types/UniswapRepayAdapter'; import {MockContract} from 'ethereum-waffle'; import {getReservesConfigByPool} from './configuration'; import {verifyContract} from './etherscan-verification'; @@ -47,7 +50,6 @@ const { export type MockTokenMap = {[symbol: string]: MintableERC20}; import {ZERO_ADDRESS} from './constants'; -import {MockSwapAdapter} from '../types/MockSwapAdapter'; import {signTypedData_v4, TypedData} from 'eth-sig-util'; import {fromRpcSig, ECDSASignature} from 'ethereumjs-util'; import {SignerWithAddress} from '../test/helpers/make-suite'; @@ -256,6 +258,27 @@ export const deployChainlinkProxyPriceProvider = async ( return instance; }; +export const deployMockUniswapRouter = async () => + await deployContract(eContractid.MockUniswapV2Router02, []); + +export const deployUniswapLiquiditySwapAdapter = async ( + addressesProvider: tEthereumAddress, + uniswapRouter: tEthereumAddress +) => + await deployContract(eContractid.UniswapLiquiditySwapAdapter, [ + addressesProvider, + uniswapRouter, + ]); + +export const deployUniswapRepayAdapter = async ( + addressesProvider: tEthereumAddress, + uniswapRouter: tEthereumAddress +) => + await deployContract(eContractid.UniswapRepayAdapter, [ + addressesProvider, + uniswapRouter, + ]); + export const getChainlingProxyPriceProvider = async (address?: tEthereumAddress) => await getContract( eContractid.ChainlinkProxyPriceProvider, @@ -321,8 +344,6 @@ export const deployWalletBalancerProvider = async ( } return instance; }; -export const deployMockSwapAdapter = async (addressesProvider: tEthereumAddress) => - await deployContract(eContractid.MockSwapAdapter, [addressesProvider]); export const deployAaveProtocolTestHelpers = async ( addressesProvider: tEthereumAddress, @@ -548,11 +569,29 @@ export const getMockFlashLoanReceiver = async (address?: tEthereumAddress) => { ); }; -export const getMockSwapAdapter = async (address?: tEthereumAddress) => { - return await getContract( - eContractid.MockSwapAdapter, +export const getMockUniswapRouter = async (address?: tEthereumAddress) => { + return await getContract( + eContractid.MockUniswapV2Router02, address || - (await getDb().get(`${eContractid.MockSwapAdapter}.${BRE.network.name}`).value()).address + (await getDb().get(`${eContractid.MockUniswapV2Router02}.${BRE.network.name}`).value()) + .address + ); +}; + +export const getUniswapLiquiditySwapAdapter = async (address?: tEthereumAddress) => { + return await getContract( + eContractid.UniswapLiquiditySwapAdapter, + address || + (await getDb().get(`${eContractid.UniswapLiquiditySwapAdapter}.${BRE.network.name}`).value()) + .address + ); +}; + +export const getUniswapRepayAdapter = async (address?: tEthereumAddress) => { + return await getContract( + eContractid.UniswapRepayAdapter, + address || + (await getDb().get(`${eContractid.UniswapRepayAdapter}.${BRE.network.name}`).value()).address ); }; diff --git a/helpers/types.ts b/helpers/types.ts index 26644896..877441ea 100644 --- a/helpers/types.ts +++ b/helpers/types.ts @@ -44,7 +44,6 @@ export enum eContractid { LendingPoolCollateralManager = 'LendingPoolCollateralManager', InitializableAdminUpgradeabilityProxy = 'InitializableAdminUpgradeabilityProxy', MockFlashLoanReceiver = 'MockFlashLoanReceiver', - MockSwapAdapter = 'MockSwapAdapter', WalletBalanceProvider = 'WalletBalanceProvider', AToken = 'AToken', MockAToken = 'MockAToken', @@ -56,6 +55,9 @@ export enum eContractid { VariableDebtToken = 'VariableDebtToken', FeeProvider = 'FeeProvider', TokenDistributor = 'TokenDistributor', + MockUniswapV2Router02 = 'MockUniswapV2Router02', + UniswapLiquiditySwapAdapter = 'UniswapLiquiditySwapAdapter', + UniswapRepayAdapter = 'UniswapRepayAdapter', } export enum ProtocolErrors { diff --git a/test/__setup.spec.ts b/test/__setup.spec.ts index 676ae52a..40ed2ced 100644 --- a/test/__setup.spec.ts +++ b/test/__setup.spec.ts @@ -19,8 +19,10 @@ import { registerContractInJsonDb, getPairsTokenAggregator, initReserves, - deployMockSwapAdapter, deployLendingRateOracle, + deployMockUniswapRouter, + deployUniswapLiquiditySwapAdapter, + deployUniswapRepayAdapter, } from '../helpers/contracts-helpers'; import {Signer} from 'ethers'; import {TokenContractId, eContractid, tEthereumAddress, AavePools} from '../helpers/types'; @@ -239,8 +241,23 @@ const buildTestEnv = async (deployer: Signer, secondaryWallet: Signer) => { const mockFlashLoanReceiver = await deployMockFlashLoanReceiver(addressesProvider.address); await insertContractAddressInDb(eContractid.MockFlashLoanReceiver, mockFlashLoanReceiver.address); - const mockSwapAdapter = await deployMockSwapAdapter(addressesProvider.address); - await insertContractAddressInDb(eContractid.MockSwapAdapter, mockSwapAdapter.address); + const mockUniswapRouter = await deployMockUniswapRouter(); + await insertContractAddressInDb(eContractid.MockUniswapV2Router02, mockUniswapRouter.address); + + const UniswapLiquiditySwapAdapter = await deployUniswapLiquiditySwapAdapter( + addressesProvider.address, + mockUniswapRouter.address + ); + await insertContractAddressInDb( + eContractid.UniswapLiquiditySwapAdapter, + UniswapLiquiditySwapAdapter.address + ); + + const UniswapRepayAdapter = await deployUniswapRepayAdapter( + addressesProvider.address, + mockUniswapRouter.address + ); + await insertContractAddressInDb(eContractid.UniswapRepayAdapter, UniswapRepayAdapter.address); await deployWalletBalancerProvider(addressesProvider.address); diff --git a/test/helpers/make-suite.ts b/test/helpers/make-suite.ts index 5eb8788f..de9cfb13 100644 --- a/test/helpers/make-suite.ts +++ b/test/helpers/make-suite.ts @@ -9,8 +9,9 @@ import { getMintableErc20, getLendingPoolConfiguratorProxy, getPriceOracle, - getMockSwapAdapter, getLendingPoolAddressesProviderRegistry, + getUniswapLiquiditySwapAdapter, + getUniswapRepayAdapter, } from '../../helpers/contracts-helpers'; import {tEthereumAddress} from '../../helpers/types'; import {LendingPool} from '../../types/LendingPool'; @@ -25,8 +26,9 @@ import bignumberChai from 'chai-bignumber'; import {almostEqual} from './almost-equal'; import {PriceOracle} from '../../types/PriceOracle'; import {LendingPoolAddressesProvider} from '../../types/LendingPoolAddressesProvider'; -import {MockSwapAdapter} from '../../types/MockSwapAdapter'; import {LendingPoolAddressesProviderRegistry} from '../../types/LendingPoolAddressesProviderRegistry'; +import {UniswapLiquiditySwapAdapter} from '../../types/UniswapLiquiditySwapAdapter'; +import {UniswapRepayAdapter} from '../../types/UniswapRepayAdapter'; chai.use(bignumberChai()); chai.use(almostEqual()); @@ -48,7 +50,8 @@ export interface TestEnv { usdc: MintableERC20; lend: MintableERC20; addressesProvider: LendingPoolAddressesProvider; - mockSwapAdapter: MockSwapAdapter; + uniswapLiquiditySwapAdapter: UniswapLiquiditySwapAdapter; + uniswapRepayAdapter: UniswapRepayAdapter; registry: LendingPoolAddressesProviderRegistry; } @@ -73,7 +76,8 @@ const testEnv: TestEnv = { usdc: {} as MintableERC20, lend: {} as MintableERC20, addressesProvider: {} as LendingPoolAddressesProvider, - mockSwapAdapter: {} as MockSwapAdapter, + uniswapLiquiditySwapAdapter: {} as UniswapLiquiditySwapAdapter, + uniswapRepayAdapter: {} as UniswapRepayAdapter, registry: {} as LendingPoolAddressesProviderRegistry, } as TestEnv; @@ -135,7 +139,8 @@ export async function initializeMakeSuite() { testEnv.lend = await getMintableErc20(lendAddress); testEnv.weth = await getMintableErc20(wethAddress); - testEnv.mockSwapAdapter = await getMockSwapAdapter(); + testEnv.uniswapLiquiditySwapAdapter = await getUniswapLiquiditySwapAdapter(); + testEnv.uniswapRepayAdapter = await getUniswapRepayAdapter(); } export function makeSuite(name: string, tests: (testEnv: TestEnv) => void) { diff --git a/test/pausable-functions.spec.ts b/test/pausable-functions.spec.ts index 8e7cc2a7..fdfe5341 100644 --- a/test/pausable-functions.spec.ts +++ b/test/pausable-functions.spec.ts @@ -275,7 +275,7 @@ makeSuite('Pausable Pool', (testEnv: TestEnv) => { }); it('SwapBorrowRateMode', async () => { - const {pool, weth, dai, usdc, users, configurator, mockSwapAdapter} = testEnv; + const {pool, weth, dai, usdc, users, configurator} = testEnv; const user = users[1]; const amountWETHToDeposit = parseEther('10'); const amountDAIToDeposit = parseEther('120'); diff --git a/test/uniswapAdapters.spec.ts b/test/uniswapAdapters.spec.ts new file mode 100644 index 00000000..6bf2cbc4 --- /dev/null +++ b/test/uniswapAdapters.spec.ts @@ -0,0 +1,805 @@ +import {makeSuite, TestEnv} from './helpers/make-suite'; +import { + convertToCurrencyDecimals, + deployUniswapLiquiditySwapAdapter, + deployUniswapRepayAdapter, + getContract, + getMockUniswapRouter, +} from '../helpers/contracts-helpers'; +import {MockUniswapV2Router02} from '../types/MockUniswapV2Router02'; +import {Zero} from '@ethersproject/constants'; +import BigNumber from 'bignumber.js'; +import {evmRevert, evmSnapshot} from '../helpers/misc-utils'; +import {ethers} from 'ethers'; +import {eContractid} from '../helpers/types'; +import {AToken} from '../types/AToken'; +import {StableDebtToken} from '../types/StableDebtToken'; +const {parseEther} = ethers.utils; + +const {expect} = require('chai'); + +makeSuite('Uniswap adapters', (testEnv: TestEnv) => { + let mockUniswapRouter: MockUniswapV2Router02; + let evmSnapshotId: string; + + before(async () => { + mockUniswapRouter = await getMockUniswapRouter(); + }); + + beforeEach(async () => { + evmSnapshotId = await evmSnapshot(); + }); + + afterEach(async () => { + await evmRevert(evmSnapshotId); + }); + + describe('UniswapLiquiditySwapAdapter', () => { + describe('constructor', () => { + it('should deploy with correct parameters', async () => { + const {addressesProvider} = testEnv; + await deployUniswapLiquiditySwapAdapter( + addressesProvider.address, + mockUniswapRouter.address + ); + }); + + it('should revert if not valid addresses provider', async () => { + expect( + deployUniswapLiquiditySwapAdapter(mockUniswapRouter.address, mockUniswapRouter.address) + ).to.be.reverted; + }); + }); + + describe('executeOperation', () => { + beforeEach(async () => { + const {users, weth, dai, pool, deployer} = testEnv; + const userAddress = users[0].address; + + // Provide liquidity + await dai.mint(parseEther('20000')); + await dai.approve(pool.address, parseEther('20000')); + await pool.deposit(dai.address, parseEther('20000'), deployer.address, 0); + + // Make a deposit for user + await weth.mint(parseEther('100')); + await weth.approve(pool.address, parseEther('100')); + await pool.deposit(weth.address, parseEther('100'), userAddress, 0); + }); + + it('should correctly swap tokens and deposit the out tokens in the pool', async () => { + const {users, weth, oracle, dai, aDai, aEth, pool, uniswapLiquiditySwapAdapter} = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + await mockUniswapRouter.setAmountToReturn(expectedDaiAmount); + + // User will swap liquidity 10 aEth to aDai + const liquidityToSwap = parseEther('10'); + await aEth.connect(user).approve(uniswapLiquiditySwapAdapter.address, liquidityToSwap); + const userAEthBalanceBefore = await aEth.balanceOf(userAddress); + + // Subtract the FL fee from the amount to be swapped 0,09% + const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); + + // 0,5% slippage + const params = ethers.utils.defaultAbiCoder.encode( + ['address', 'address', 'uint256'], + [dai.address, userAddress, 50] + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapLiquiditySwapAdapter.address, + weth.address, + flashloanAmount.toString(), + 0, + params, + 0 + ) + ) + .to.emit(uniswapLiquiditySwapAdapter, 'Swapped') + .withArgs(weth.address, dai.address, flashloanAmount.toString(), expectedDaiAmount); + + const adapterWethBalance = await weth.balanceOf(uniswapLiquiditySwapAdapter.address); + const adapterDaiBalance = await dai.balanceOf(uniswapLiquiditySwapAdapter.address); + const adapterDaiAllowance = await dai.allowance( + uniswapLiquiditySwapAdapter.address, + userAddress + ); + const userADaiBalance = await aDai.balanceOf(userAddress); + const userAEthBalance = await aEth.balanceOf(userAddress); + + expect(adapterWethBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(adapterDaiAllowance).to.be.eq(Zero); + expect(userADaiBalance).to.be.eq(expectedDaiAmount); + expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); + expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); + }); + + it('should work correctly with tokens of different decimals', async () => { + const { + users, + usdc, + oracle, + dai, + aDai, + uniswapLiquiditySwapAdapter, + pool, + deployer, + } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountUSDCtoSwap = await convertToCurrencyDecimals(usdc.address, '10'); + const liquidity = await convertToCurrencyDecimals(usdc.address, '20000'); + + // Provide liquidity + await usdc.mint(liquidity); + await usdc.approve(pool.address, liquidity); + await pool.deposit(usdc.address, liquidity, deployer.address, 0); + + // Make a deposit for user + await usdc.connect(user).mint(amountUSDCtoSwap); + await usdc.connect(user).approve(pool.address, amountUSDCtoSwap); + await pool.connect(user).deposit(usdc.address, amountUSDCtoSwap, userAddress, 0); + + const usdcPrice = await oracle.getAssetPrice(usdc.address); + const daiPrice = await oracle.getAssetPrice(dai.address); + + // usdc 6 + const collateralDecimals = (await usdc.decimals()).toString(); + const principalDecimals = (await dai.decimals()).toString(); + + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountUSDCtoSwap.toString()) + .times( + new BigNumber(usdcPrice.toString()).times(new BigNumber(10).pow(principalDecimals)) + ) + .div( + new BigNumber(daiPrice.toString()).times(new BigNumber(10).pow(collateralDecimals)) + ) + .toFixed(0) + ); + + await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount); + + const aUsdcData = await pool.getReserveData(usdc.address); + const aUsdc = await getContract(eContractid.AToken, aUsdcData.aTokenAddress); + const aUsdcBalance = await aUsdc.balanceOf(userAddress); + await aUsdc.connect(user).approve(uniswapLiquiditySwapAdapter.address, aUsdcBalance); + // Subtract the FL fee from the amount to be swapped 0,09% + const flashloanAmount = new BigNumber(amountUSDCtoSwap.toString()).div(1.0009).toFixed(0); + + // 0,5% slippage + const params = ethers.utils.defaultAbiCoder.encode( + ['address', 'address', 'uint256'], + [dai.address, userAddress, 50] + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapLiquiditySwapAdapter.address, + usdc.address, + flashloanAmount.toString(), + 0, + params, + 0 + ) + ) + .to.emit(uniswapLiquiditySwapAdapter, 'Swapped') + .withArgs(usdc.address, dai.address, flashloanAmount.toString(), expectedDaiAmount); + + const adapterUsdcBalance = await usdc.balanceOf(uniswapLiquiditySwapAdapter.address); + const adapterDaiBalance = await dai.balanceOf(uniswapLiquiditySwapAdapter.address); + const adapterDaiAllowance = await dai.allowance( + uniswapLiquiditySwapAdapter.address, + userAddress + ); + const aDaiBalance = await aDai.balanceOf(userAddress); + + expect(adapterUsdcBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(adapterDaiAllowance).to.be.eq(Zero); + expect(aDaiBalance).to.be.eq(expectedDaiAmount); + }); + + it('should revert if slippage param is not inside limits', async () => { + const {users, pool, weth, oracle, dai, aEth, uniswapLiquiditySwapAdapter} = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + await weth.connect(user).mint(amountWETHtoSwap); + await weth.connect(user).transfer(uniswapLiquiditySwapAdapter.address, amountWETHtoSwap); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount); + + // User will swap liquidity 10 aEth to aDai + const liquidityToSwap = parseEther('10'); + await aEth.connect(user).approve(uniswapLiquiditySwapAdapter.address, liquidityToSwap); + // Subtract the FL fee from the amount to be swapped 0,09% + const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); + + // 30% slippage + const params1 = ethers.utils.defaultAbiCoder.encode( + ['address', 'address', 'uint256'], + [dai.address, userAddress, 3000] + ); + + // 0,05% slippage + const params2 = ethers.utils.defaultAbiCoder.encode( + ['address', 'address', 'uint256'], + [dai.address, userAddress, 5] + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapLiquiditySwapAdapter.address, + weth.address, + flashloanAmount.toString(), + 0, + params1, + 0 + ) + ).to.be.revertedWith('SLIPPAGE_OUT_OF_RANGE'); + await expect( + pool + .connect(user) + .flashLoan( + uniswapLiquiditySwapAdapter.address, + weth.address, + flashloanAmount.toString(), + 0, + params2, + 0 + ) + ).to.be.revertedWith('SLIPPAGE_OUT_OF_RANGE'); + }); + + it('should revert when swap exceed slippage', async () => { + const {users, weth, oracle, dai, aEth, pool, uniswapLiquiditySwapAdapter} = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + await weth.connect(user).mint(amountWETHtoSwap); + await weth.connect(user).transfer(uniswapLiquiditySwapAdapter.address, amountWETHtoSwap); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + // 1,5% slippage + const returnedDaiAmountWithBigSlippage = new BigNumber(expectedDaiAmount.toString()) + .multipliedBy(0.985) + .toFixed(0); + await mockUniswapRouter.connect(user).setAmountToReturn(returnedDaiAmountWithBigSlippage); + + // User will swap liquidity 10 aEth to aDai + const liquidityToSwap = parseEther('10'); + await aEth.connect(user).approve(uniswapLiquiditySwapAdapter.address, liquidityToSwap); + // Subtract the FL fee from the amount to be swapped 0,09% + const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); + + // 0,5% slippage + const params = ethers.utils.defaultAbiCoder.encode( + ['address', 'address', 'uint256'], + [dai.address, userAddress, 50] + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapLiquiditySwapAdapter.address, + weth.address, + flashloanAmount.toString(), + 0, + params, + 0 + ) + ).to.be.revertedWith('INSUFFICIENT_OUTPUT_AMOUNT'); + }); + }); + }); + + describe('UniswapRepayAdapter', () => { + describe('constructor', () => { + it('should deploy with correct parameters', async () => { + const {addressesProvider} = testEnv; + await deployUniswapRepayAdapter(addressesProvider.address, mockUniswapRouter.address); + }); + + it('should revert if not valid addresses provider', async () => { + expect(deployUniswapRepayAdapter(mockUniswapRouter.address, mockUniswapRouter.address)).to + .be.reverted; + }); + }); + + describe('executeOperation', () => { + beforeEach(async () => { + const {users, weth, dai, pool, deployer} = testEnv; + const userAddress = users[0].address; + + // Provide liquidity + await dai.mint(parseEther('20000')); + await dai.approve(pool.address, parseEther('20000')); + await pool.deposit(dai.address, parseEther('20000'), deployer.address, 0); + + // Make a deposit for user + await weth.mint(parseEther('100')); + await weth.approve(pool.address, parseEther('100')); + await pool.deposit(weth.address, parseEther('100'), userAddress, 0); + }); + + it('should correctly swap tokens and repay debt', async () => { + const { + users, + pool, + weth, + aEth, + oracle, + dai, + uniswapRepayAdapter, + helpersContract, + } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); + + const daiStableDebtTokenAddress = ( + await helpersContract.getReserveTokensAddresses(dai.address) + ).stableDebtTokenAddress; + + const daiStableDebtContract = await getContract( + eContractid.StableDebtToken, + daiStableDebtTokenAddress + ); + + const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); + + const liquidityToSwap = amountWETHtoSwap; + await aEth.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + const userAEthBalanceBefore = await aEth.balanceOf(userAddress); + + // Subtract the FL fee from the amount to be swapped 0,09% + const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); + + await mockUniswapRouter.connect(user).setAmountToSwap(flashloanAmount); + await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount); + + const params = ethers.utils.defaultAbiCoder.encode( + ['address', 'address', 'uint256', 'uint256', 'uint256'], + [dai.address, userAddress, 0, expectedDaiAmount, 1] + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapRepayAdapter.address, + weth.address, + flashloanAmount.toString(), + 0, + params, + 0 + ) + ) + .to.emit(uniswapRepayAdapter, 'Swapped') + .withArgs(weth.address, dai.address, flashloanAmount.toString(), expectedDaiAmount); + + const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); + const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); + const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); + const userAEthBalance = await aEth.balanceOf(userAddress); + + expect(adapterWethBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount); + expect(userDaiStableDebtAmount).to.be.lt(expectedDaiAmount); + expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); + expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); + }); + + it('should revert if there is not debt to repay with the specified rate mode', async () => { + const {users, pool, weth, oracle, dai, uniswapRepayAdapter, aEth} = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + await weth.connect(user).mint(amountWETHtoSwap); + await weth.connect(user).transfer(uniswapRepayAdapter.address, amountWETHtoSwap); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 2, 0, userAddress); + + const liquidityToSwap = amountWETHtoSwap; + await aEth.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + const userAEthBalanceBefore = await aEth.balanceOf(userAddress); + + // Subtract the FL fee from the amount to be swapped 0,09% + const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); + + await mockUniswapRouter.connect(user).setAmountToSwap(flashloanAmount); + await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount); + + const params = ethers.utils.defaultAbiCoder.encode( + ['address', 'address', 'uint256', 'uint256', 'uint256'], + [dai.address, userAddress, 0, expectedDaiAmount, 1] + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapRepayAdapter.address, + weth.address, + flashloanAmount.toString(), + 0, + params, + 0 + ) + ).to.be.reverted; + }); + + it('should revert if there is not debt to repay', async () => { + const {users, pool, weth, oracle, dai, uniswapRepayAdapter, aEth} = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + await weth.connect(user).mint(amountWETHtoSwap); + await weth.connect(user).transfer(uniswapRepayAdapter.address, amountWETHtoSwap); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + const liquidityToSwap = amountWETHtoSwap; + await aEth.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + const userAEthBalanceBefore = await aEth.balanceOf(userAddress); + + // Subtract the FL fee from the amount to be swapped 0,09% + const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); + + await mockUniswapRouter.connect(user).setAmountToSwap(flashloanAmount); + await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount); + + const params = ethers.utils.defaultAbiCoder.encode( + ['address', 'address', 'uint256', 'uint256', 'uint256'], + [dai.address, userAddress, 0, expectedDaiAmount, 1] + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapRepayAdapter.address, + weth.address, + flashloanAmount.toString(), + 0, + params, + 0 + ) + ).to.be.reverted; + }); + + it('should revert when the received amount is less than expected', async () => { + const {users, pool, weth, oracle, dai, aEth, uniswapRepayAdapter, deployer} = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); + + const liquidityToSwap = amountWETHtoSwap; + await aEth.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + + // Subtract the FL fee from the amount to be swapped 0,09% + const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); + + const insufficientOutput = new BigNumber(expectedDaiAmount.toString()) + .multipliedBy(0.985) + .toFixed(0); + + await mockUniswapRouter.connect(user).setAmountToSwap(flashloanAmount); + await mockUniswapRouter.connect(user).setAmountToReturn(insufficientOutput); + + const params = ethers.utils.defaultAbiCoder.encode( + ['address', 'address', 'uint256', 'uint256', 'uint256'], + [dai.address, userAddress, 0, expectedDaiAmount, 1] + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapRepayAdapter.address, + weth.address, + flashloanAmount.toString(), + 0, + params, + 0 + ) + ).to.be.revertedWith('INSUFFICIENT_OUTPUT_AMOUNT'); + }); + + it('should revert when max amount allowed to swap is bigger than max slippage', async () => { + const {users, pool, weth, oracle, dai, aEth, uniswapRepayAdapter} = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); + + const liquidityToSwap = amountWETHtoSwap; + await aEth.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + + // Subtract the FL fee from the amount to be swapped 0,09% + const bigMaxAmountToSwap = amountWETHtoSwap.mul(2); + const flashloanAmount = new BigNumber(bigMaxAmountToSwap.toString()).div(1.0009).toFixed(0); + + await mockUniswapRouter.connect(user).setAmountToSwap(flashloanAmount); + await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount); + + const params = ethers.utils.defaultAbiCoder.encode( + ['address', 'address', 'uint256', 'uint256', 'uint256'], + [dai.address, userAddress, 0, expectedDaiAmount, 1] + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapRepayAdapter.address, + weth.address, + flashloanAmount.toString(), + 0, + params, + 0 + ) + ).to.be.revertedWith('maxAmountToSwap exceed max slippage'); + }); + + it('should swap tokens, repay debt and deposit in pool the left over', async () => { + const { + users, + pool, + weth, + aEth, + oracle, + dai, + uniswapRepayAdapter, + helpersContract, + } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); + + const daiStableDebtTokenAddress = ( + await helpersContract.getReserveTokensAddresses(dai.address) + ).stableDebtTokenAddress; + + const daiStableDebtContract = await getContract( + eContractid.StableDebtToken, + daiStableDebtTokenAddress + ); + + const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); + + const liquidityToSwap = amountWETHtoSwap; + await aEth.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + const userAEthBalanceBefore = await aEth.balanceOf(userAddress); + + // Subtract the FL fee from the amount to be swapped 0,09% + const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); + + const actualWEthSwapped = new BigNumber(flashloanAmount.toString()) + .multipliedBy(0.995) + .toFixed(0); + + const leftOverWeth = new BigNumber(flashloanAmount).minus(actualWEthSwapped); + + await mockUniswapRouter.connect(user).setAmountToSwap(actualWEthSwapped); + await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount); + + const params = ethers.utils.defaultAbiCoder.encode( + ['address', 'address', 'uint256', 'uint256', 'uint256'], + [dai.address, userAddress, 0, expectedDaiAmount, 1] + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapRepayAdapter.address, + weth.address, + flashloanAmount.toString(), + 0, + params, + 0 + ) + ) + .to.emit(uniswapRepayAdapter, 'Swapped') + .withArgs(weth.address, dai.address, actualWEthSwapped.toString(), expectedDaiAmount); + + const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); + const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); + const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); + const userAEthBalance = await aEth.balanceOf(userAddress); + + expect(adapterWethBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount); + expect(userDaiStableDebtAmount).to.be.lt(expectedDaiAmount); + expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); + expect(userAEthBalance).to.be.gt(userAEthBalanceBefore.sub(liquidityToSwap)); + expect(userAEthBalance).to.be.gte( + userAEthBalanceBefore.sub(liquidityToSwap).add(leftOverWeth.toString()) + ); + }); + + it('should swap tokens, repay debt and transfer to user the left over', async () => { + const { + users, + pool, + weth, + aEth, + oracle, + dai, + uniswapRepayAdapter, + helpersContract, + } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); + + const daiStableDebtTokenAddress = ( + await helpersContract.getReserveTokensAddresses(dai.address) + ).stableDebtTokenAddress; + + const daiStableDebtContract = await getContract( + eContractid.StableDebtToken, + daiStableDebtTokenAddress + ); + + const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); + + const liquidityToSwap = amountWETHtoSwap; + await aEth.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + const userAEthBalanceBefore = await aEth.balanceOf(userAddress); + + // Subtract the FL fee from the amount to be swapped 0,09% + const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); + + const actualWEthSwapped = new BigNumber(flashloanAmount.toString()) + .multipliedBy(0.995) + .toFixed(0); + + const leftOverWeth = new BigNumber(flashloanAmount).minus(actualWEthSwapped); + + await mockUniswapRouter.connect(user).setAmountToSwap(actualWEthSwapped); + await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount); + + const wethBalanceBefore = await weth.balanceOf(userAddress); + + const params = ethers.utils.defaultAbiCoder.encode( + ['address', 'address', 'uint256', 'uint256', 'uint256'], + [dai.address, userAddress, 1, expectedDaiAmount, 1] + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapRepayAdapter.address, + weth.address, + flashloanAmount.toString(), + 0, + params, + 0 + ) + ) + .to.emit(uniswapRepayAdapter, 'Swapped') + .withArgs(weth.address, dai.address, actualWEthSwapped.toString(), expectedDaiAmount); + + const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); + const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); + const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); + const userAEthBalance = await aEth.balanceOf(userAddress); + const wethBalance = await weth.balanceOf(userAddress); + + expect(adapterWethBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount); + expect(userDaiStableDebtAmount).to.be.lt(expectedDaiAmount); + expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); + expect(userAEthBalance).to.be.gt(userAEthBalanceBefore.sub(liquidityToSwap)); + expect(wethBalance).to.be.eq(wethBalanceBefore.add(leftOverWeth.toString())); + }); + }); + }); +}); From 96e74cf7079e61ff503c67f37c390b11af51fbbe Mon Sep 17 00:00:00 2001 From: Gerardo Nardelli Date: Tue, 27 Oct 2020 11:22:16 -0300 Subject: [PATCH 03/62] Update adapters to use the new flashloan interface --- .../adapters/UniswapLiquiditySwapAdapter.sol | 18 +++--- contracts/adapters/UniswapRepayAdapter.sol | 20 +++---- test/uniswapAdapters.spec.ts | 55 +++++++++---------- 3 files changed, 45 insertions(+), 48 deletions(-) diff --git a/contracts/adapters/UniswapLiquiditySwapAdapter.sol b/contracts/adapters/UniswapLiquiditySwapAdapter.sol index 62125264..4cfd156a 100644 --- a/contracts/adapters/UniswapLiquiditySwapAdapter.sol +++ b/contracts/adapters/UniswapLiquiditySwapAdapter.sol @@ -28,18 +28,18 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { * The received funds from the swap are then deposited into the protocol on behalf of the user. * The user should give this contract allowance to pull the ATokens in order to withdraw the underlying asset and * repay the flash loan. - * @param reserve Address to be swapped - * @param amount Amount of the reserve to be swapped - * @param fee Fee of the flash loan + * @param assets Address to be swapped + * @param amounts Amount of the reserve to be swapped + * @param premiums Fee of the flash loan * @param params Additional variadic field to include extra params. Expected parameters: * address assetToSwapTo Address of the reserve to be swapped to and deposited * address user The address of the user * uint256 slippage The max slippage percentage allowed for the swap */ function executeOperation( - address reserve, - uint256 amount, - uint256 fee, + address[] calldata assets, + uint256[] calldata amounts, + uint256[] calldata premiums, bytes calldata params ) external override returns (bool) { ( @@ -49,14 +49,14 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { ) = abi.decode(params, (address, address, uint256)); require(slippage < MAX_SLIPPAGE_PERCENT && slippage >= MIN_SLIPPAGE_PERCENT, 'SLIPPAGE_OUT_OF_RANGE'); - uint256 receivedAmount = swapExactTokensForTokens(reserve, assetToSwapTo, amount, slippage); + uint256 receivedAmount = swapExactTokensForTokens(assets[0], assetToSwapTo, amounts[0], slippage); // Deposit new reserve IERC20(assetToSwapTo).approve(address(pool), receivedAmount); pool.deposit(assetToSwapTo, receivedAmount, user, 0); - uint256 flashLoanDebt = amount.add(fee); - pullATokenAndRepayFlashLoan(reserve, user, flashLoanDebt); + uint256 flashLoanDebt = amounts[0].add(premiums[0]); + pullATokenAndRepayFlashLoan(assets[0], user, flashLoanDebt); return true; } diff --git a/contracts/adapters/UniswapRepayAdapter.sol b/contracts/adapters/UniswapRepayAdapter.sol index 2bb8a8a8..70490fa7 100644 --- a/contracts/adapters/UniswapRepayAdapter.sol +++ b/contracts/adapters/UniswapRepayAdapter.sol @@ -28,9 +28,9 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { * then used to repay a debt on the protocol on behalf of the user. * The user should give this contract allowance to pull the ATokens in order to withdraw the underlying asset and * repay the flash loan. - * @param reserve Address to be swapped - * @param amount Amount of the reserve to be swapped - * @param fee Fee of the flash loan + * @param assets Address to be swapped + * @param amounts Amount of the reserve to be swapped + * @param premiums Fee of the flash loan * @param params Additional variadic field to include extra params. Expected parameters: * address assetToSwapTo Address of the reserve to be swapped to and deposited * address user The address of the user @@ -41,9 +41,9 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { * uint256 rateMode The rate modes of the debt to be repaid */ function executeOperation( - address reserve, - uint256 amount, - uint256 fee, + address[] calldata assets, + uint256[] calldata amounts, + uint256[] calldata premiums, bytes calldata params ) external override returns (bool) { ( @@ -54,17 +54,17 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { uint256 rateMode ) = abi.decode(params, (address, address, uint256, uint256, uint256)); - swapTokensForExactTokens(reserve, assetToSwapTo, amount, repayAmount); + swapTokensForExactTokens(assets[0], assetToSwapTo, amounts[0], repayAmount); // Repay debt IERC20(assetToSwapTo).approve(address(pool), repayAmount); pool.repay(assetToSwapTo, repayAmount, rateMode, user); - uint256 flashLoanDebt = amount.add(fee); - pullATokenAndRepayFlashLoan(reserve, user, flashLoanDebt); + uint256 flashLoanDebt = amounts[0].add(premiums[0]); + pullATokenAndRepayFlashLoan(assets[0], user, flashLoanDebt); // Take care of reserve leftover from the swap - sendLeftOver(reserve, flashLoanDebt, leftOverAction, user); + sendLeftOver(assets[0], flashLoanDebt, leftOverAction, user); return true; } diff --git a/test/uniswapAdapters.spec.ts b/test/uniswapAdapters.spec.ts index 6bf2cbc4..18b78be9 100644 --- a/test/uniswapAdapters.spec.ts +++ b/test/uniswapAdapters.spec.ts @@ -101,8 +101,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { .connect(user) .flashLoan( uniswapLiquiditySwapAdapter.address, - weth.address, - flashloanAmount.toString(), + [weth.address], + [flashloanAmount.toString()], 0, params, 0 @@ -194,8 +194,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { .connect(user) .flashLoan( uniswapLiquiditySwapAdapter.address, - usdc.address, - flashloanAmount.toString(), + [usdc.address], + [flashloanAmount.toString()], 0, params, 0 @@ -259,8 +259,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { .connect(user) .flashLoan( uniswapLiquiditySwapAdapter.address, - weth.address, - flashloanAmount.toString(), + [weth.address], + [flashloanAmount.toString()], 0, params1, 0 @@ -271,8 +271,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { .connect(user) .flashLoan( uniswapLiquiditySwapAdapter.address, - weth.address, - flashloanAmount.toString(), + [weth.address], + [flashloanAmount.toString()], 0, params2, 0 @@ -319,8 +319,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { .connect(user) .flashLoan( uniswapLiquiditySwapAdapter.address, - weth.address, - flashloanAmount.toString(), + [weth.address], + [flashloanAmount.toString()], 0, params, 0 @@ -415,8 +415,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { .connect(user) .flashLoan( uniswapRepayAdapter.address, - weth.address, - flashloanAmount.toString(), + [weth.address], + [flashloanAmount.toString()], 0, params, 0 @@ -459,7 +459,6 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const liquidityToSwap = amountWETHtoSwap; await aEth.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); - const userAEthBalanceBefore = await aEth.balanceOf(userAddress); // Subtract the FL fee from the amount to be swapped 0,09% const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); @@ -477,8 +476,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { .connect(user) .flashLoan( uniswapRepayAdapter.address, - weth.address, - flashloanAmount.toString(), + [weth.address], + [flashloanAmount.toString()], 0, params, 0 @@ -504,7 +503,6 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const liquidityToSwap = amountWETHtoSwap; await aEth.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); - const userAEthBalanceBefore = await aEth.balanceOf(userAddress); // Subtract the FL fee from the amount to be swapped 0,09% const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); @@ -522,8 +520,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { .connect(user) .flashLoan( uniswapRepayAdapter.address, - weth.address, - flashloanAmount.toString(), + [weth.address], + [flashloanAmount.toString()], 0, params, 0 @@ -532,7 +530,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { }); it('should revert when the received amount is less than expected', async () => { - const {users, pool, weth, oracle, dai, aEth, uniswapRepayAdapter, deployer} = testEnv; + const {users, pool, weth, oracle, dai, aEth, uniswapRepayAdapter} = testEnv; const user = users[0].signer; const userAddress = users[0].address; @@ -570,8 +568,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { .connect(user) .flashLoan( uniswapRepayAdapter.address, - weth.address, - flashloanAmount.toString(), + [weth.address], + [flashloanAmount.toString()], 0, params, 0 @@ -595,8 +593,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { // Open user Debt await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); - const liquidityToSwap = amountWETHtoSwap; - await aEth.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + await aEth.connect(user).approve(uniswapRepayAdapter.address, amountWETHtoSwap); // Subtract the FL fee from the amount to be swapped 0,09% const bigMaxAmountToSwap = amountWETHtoSwap.mul(2); @@ -615,8 +612,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { .connect(user) .flashLoan( uniswapRepayAdapter.address, - weth.address, - flashloanAmount.toString(), + [weth.address], + [flashloanAmount.toString()], 0, params, 0 @@ -686,8 +683,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { .connect(user) .flashLoan( uniswapRepayAdapter.address, - weth.address, - flashloanAmount.toString(), + [weth.address], + [flashloanAmount.toString()], 0, params, 0 @@ -776,8 +773,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { .connect(user) .flashLoan( uniswapRepayAdapter.address, - weth.address, - flashloanAmount.toString(), + [weth.address], + [flashloanAmount.toString()], 0, params, 0 From e5d37e1a8ce7fb28722d1795b183aea59c247562 Mon Sep 17 00:00:00 2001 From: Gerardo Nardelli Date: Tue, 27 Oct 2020 16:32:09 -0300 Subject: [PATCH 04/62] Add view method to estimate swaps outputs --- contracts/adapters/BaseUniswapAdapter.sol | 72 ++++++++++++++++--- contracts/interfaces/IUniswapV2Router02.sol | 4 ++ .../mocks/swap/MockUniswapV2Router02.sol | 28 +++++++- 3 files changed, 94 insertions(+), 10 deletions(-) diff --git a/contracts/adapters/BaseUniswapAdapter.sol b/contracts/adapters/BaseUniswapAdapter.sol index 79b55df2..eb70d2e4 100644 --- a/contracts/adapters/BaseUniswapAdapter.sol +++ b/contracts/adapters/BaseUniswapAdapter.sol @@ -42,6 +42,48 @@ contract BaseUniswapAdapter { uniswapRouter = _uniswapRouter; } + /** + * @dev Given an input asset amount, returns the maximum output amount of the other asset + * @param amountIn Amount of reserveIn + * @param reserveIn Address of the asset to be swap from + * @param reserveOut Address of the asset to be swap to + * @return uint256 amountOut + */ + function getAmountOut(uint256 amountIn, address reserveIn, address reserveOut) + public + view + returns (uint256) + { + address[] memory path = new address[](2); + path[0] = reserveIn; + path[1] = reserveOut; + + uint256[] memory amounts = uniswapRouter.getAmountsOut(amountIn, path); + + return amounts[1]; + } + + /** + * @dev Returns the minimum input asset amount required to buy the given output asset amount + * @param amountOut Amount of reserveOut + * @param reserveIn Address of the asset to be swap from + * @param reserveOut Address of the asset to be swap to + * @return uint256 amountIn + */ + function getAmountIn(uint256 amountOut, address reserveIn, address reserveOut) + public + view + returns (uint256) + { + address[] memory path = new address[](2); + path[0] = reserveIn; + path[1] = reserveOut; + + uint256[] memory amounts = uniswapRouter.getAmountsIn(amountOut, path); + + return amounts[0]; + } + /** * @dev Swaps an `amountToSwap` of an asset to another * @param assetToSwapFrom Origin asset @@ -191,7 +233,27 @@ contract BaseUniswapAdapter { } /** - * @dev Take action with the swap left overs as configured in the parameters + * @dev Pull the ATokens from the user + * @param reserve address of the asset + * @param user address + * @param amount of tokens to be transferred to the contract + */ + function pullAToken( + address reserve, + address user, + uint256 amount + ) internal { + address reserveAToken = getAToken(reserve); + + // transfer from user to adapter + IERC20(reserveAToken).safeTransferFrom(user, address(this), amount); + + // withdraw reserve + pool.withdraw(reserve, amount); + } + + /** + * @dev Pull the ATokens from the user and use them to repay the flashloan * @param reserve address of the asset * @param user address * @param flashLoanDebt need to be repaid @@ -201,13 +263,7 @@ contract BaseUniswapAdapter { address user, uint256 flashLoanDebt ) internal { - address reserveAToken = getAToken(reserve); - - // transfer from user to adapter - IERC20(reserveAToken).safeTransferFrom(user, address(this), flashLoanDebt); - - // withdraw reserve - pool.withdraw(reserve, flashLoanDebt); + pullAToken(reserve, user, flashLoanDebt); // Repay flashloan IERC20(reserve).approve(address(pool), flashLoanDebt); diff --git a/contracts/interfaces/IUniswapV2Router02.sol b/contracts/interfaces/IUniswapV2Router02.sol index cb04c269..6453e74a 100644 --- a/contracts/interfaces/IUniswapV2Router02.sol +++ b/contracts/interfaces/IUniswapV2Router02.sol @@ -17,4 +17,8 @@ interface IUniswapV2Router02 { address to, uint deadline ) external returns (uint256[] memory amounts); + + function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts); + + function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts); } diff --git a/contracts/mocks/swap/MockUniswapV2Router02.sol b/contracts/mocks/swap/MockUniswapV2Router02.sol index 64cb935f..ef4c22c4 100644 --- a/contracts/mocks/swap/MockUniswapV2Router02.sol +++ b/contracts/mocks/swap/MockUniswapV2Router02.sol @@ -8,6 +8,8 @@ import {MintableERC20} from '../tokens/MintableERC20.sol'; contract MockUniswapV2Router02 is IUniswapV2Router02 { uint256 internal _amountToReturn; uint256 internal _amountToSwap; + mapping(address => mapping(address => mapping(uint256 => uint256))) internal _amountsIn; + mapping(address => mapping(address => mapping(uint256 => uint256))) internal _amountsOut; function setAmountToReturn(uint256 amount) public { _amountToReturn = amount; @@ -35,8 +37,8 @@ contract MockUniswapV2Router02 is IUniswapV2Router02 { } function swapTokensForExactTokens( - uint amountOut, - uint amountInMax, + uint /* amountOut */, + uint /* amountInMax */, address[] calldata path, address to, uint /* deadline */ @@ -50,4 +52,26 @@ contract MockUniswapV2Router02 is IUniswapV2Router02 { amounts[0] = _amountToSwap; amounts[1] = _amountToReturn; } + + function setAmountOut(uint amountIn, address reserveIn, address reserveOut, uint amountOut) public { + _amountsOut[reserveIn][reserveOut][amountIn] = amountOut; + } + + function setAmountIn(uint amountOut, address reserveIn, address reserveOut, uint amountIn) public { + _amountsIn[reserveIn][reserveOut][amountOut] = amountIn; + } + + function getAmountsOut(uint amountIn, address[] calldata path) external view override returns (uint[] memory) { + uint256[] memory amounts = new uint256[](2); + amounts[0] = amountIn; + amounts[1] = _amountsOut[path[0]][path[1]][amountIn]; + return amounts; + } + + function getAmountsIn(uint amountOut, address[] calldata path) external view override returns (uint[] memory) { + uint256[] memory amounts = new uint256[](2); + amounts[0] = _amountsIn[path[0]][path[1]][amountOut]; + amounts[1] = amountOut; + return amounts; + } } From 16a28d6223896998ae444ab0fc795d0587704842 Mon Sep 17 00:00:00 2001 From: Gerardo Nardelli Date: Tue, 27 Oct 2020 16:33:07 -0300 Subject: [PATCH 05/62] Add swapAndDeposit method to use without flashloan --- .../adapters/UniswapLiquiditySwapAdapter.sol | 27 ++++++ test/uniswapAdapters.spec.ts | 96 +++++++++++++++++++ 2 files changed, 123 insertions(+) diff --git a/contracts/adapters/UniswapLiquiditySwapAdapter.sol b/contracts/adapters/UniswapLiquiditySwapAdapter.sol index 4cfd156a..d44f50fd 100644 --- a/contracts/adapters/UniswapLiquiditySwapAdapter.sol +++ b/contracts/adapters/UniswapLiquiditySwapAdapter.sol @@ -60,4 +60,31 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { return true; } + + /** + * @dev Swaps an `amountToSwap` of an asset to another and deposits the funds on behalf of the user without using a flashloan. + * This method can be used when the user has no debts. + * The user should give this contract allowance to pull the ATokens in order to withdraw the underlying asset and + * perform the swap. + * @param assetToSwapFrom Address of the underlying asset to be swap from + * @param assetToSwapTo Address of the underlying asset to be swap to and deposited + * @param amountToSwap How much `assetToSwapFrom` needs to be swapped + * @param user Address that will be pulling the swapped funds + * @param slippage The max slippage percentage allowed for the swap + */ + function swapAndDeposit( + address assetToSwapFrom, + address assetToSwapTo, + uint256 amountToSwap, + address user, + uint256 slippage + ) external { + pullAToken(assetToSwapFrom, user, amountToSwap); + + uint256 receivedAmount = swapExactTokensForTokens(assetToSwapFrom, assetToSwapTo, amountToSwap, slippage); + + // Deposit new reserve + IERC20(assetToSwapTo).approve(address(pool), receivedAmount); + pool.deposit(assetToSwapTo, receivedAmount, user, 0); + } } diff --git a/test/uniswapAdapters.spec.ts b/test/uniswapAdapters.spec.ts index 18b78be9..6fe92b8a 100644 --- a/test/uniswapAdapters.spec.ts +++ b/test/uniswapAdapters.spec.ts @@ -34,6 +34,36 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await evmRevert(evmSnapshotId); }); + describe('BaseUniswapAdapter', () => { + describe('getAmountOut', () => { + it('should return the estimated amountOut for the asset swap', async () => { + const {weth, dai, uniswapLiquiditySwapAdapter} = testEnv; + const amountIn = parseEther('1'); + const amountOut = parseEther('2'); + + await mockUniswapRouter.setAmountOut(amountIn, weth.address, dai.address, amountOut); + + expect( + await uniswapLiquiditySwapAdapter.getAmountOut(amountIn, weth.address, dai.address) + ).to.be.eq(amountOut); + }); + }); + + describe('getAmountIn', () => { + it('should return the estimated required amountIn for the asset swap', async () => { + const {weth, dai, uniswapLiquiditySwapAdapter} = testEnv; + const amountIn = parseEther('1'); + const amountOut = parseEther('2'); + + await mockUniswapRouter.setAmountIn(amountOut, weth.address, dai.address, amountIn); + + expect( + await uniswapLiquiditySwapAdapter.getAmountIn(amountOut, weth.address, dai.address) + ).to.be.eq(amountIn); + }); + }); + }); + describe('UniswapLiquiditySwapAdapter', () => { describe('constructor', () => { it('should deploy with correct parameters', async () => { @@ -328,6 +358,72 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { ).to.be.revertedWith('INSUFFICIENT_OUTPUT_AMOUNT'); }); }); + + describe('swapAndDeposit', () => { + beforeEach(async () => { + const {users, weth, dai, pool, deployer} = testEnv; + const userAddress = users[0].address; + + // Provide liquidity + await dai.mint(parseEther('20000')); + await dai.approve(pool.address, parseEther('20000')); + await pool.deposit(dai.address, parseEther('20000'), deployer.address, 0); + + // Make a deposit for user + await weth.mint(parseEther('100')); + await weth.approve(pool.address, parseEther('100')); + await pool.deposit(weth.address, parseEther('100'), userAddress, 0); + }); + + it('should correctly swap tokens and deposit the out tokens in the pool', async () => { + const {users, weth, oracle, dai, aDai, aEth, uniswapLiquiditySwapAdapter} = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + await mockUniswapRouter.setAmountToReturn(expectedDaiAmount); + + // User will swap liquidity 10 aEth to aDai + const liquidityToSwap = parseEther('10'); + await aEth.connect(user).approve(uniswapLiquiditySwapAdapter.address, liquidityToSwap); + const userAEthBalanceBefore = await aEth.balanceOf(userAddress); + + await expect( + uniswapLiquiditySwapAdapter.swapAndDeposit( + weth.address, + dai.address, + amountWETHtoSwap, + userAddress, + 50 + ) + ) + .to.emit(uniswapLiquiditySwapAdapter, 'Swapped') + .withArgs(weth.address, dai.address, amountWETHtoSwap.toString(), expectedDaiAmount); + + const adapterWethBalance = await weth.balanceOf(uniswapLiquiditySwapAdapter.address); + const adapterDaiBalance = await dai.balanceOf(uniswapLiquiditySwapAdapter.address); + const adapterDaiAllowance = await dai.allowance( + uniswapLiquiditySwapAdapter.address, + userAddress + ); + const userADaiBalance = await aDai.balanceOf(userAddress); + const userAEthBalance = await aEth.balanceOf(userAddress); + + expect(adapterWethBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(adapterDaiAllowance).to.be.eq(Zero); + expect(userADaiBalance).to.be.eq(expectedDaiAmount); + expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); + expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); + }); + }); }); describe('UniswapRepayAdapter', () => { From fc358b7c14edd01ec02eec59513dc4efadcd093f Mon Sep 17 00:00:00 2001 From: Gerardo Nardelli Date: Thu, 29 Oct 2020 17:29:41 -0300 Subject: [PATCH 06/62] merge fixes --- helpers/contracts-deployments.ts | 33 +++++++++++ helpers/contracts-getters.ts | 26 +++++++++ test/__setup.spec.ts | 12 ++-- test/uniswapAdapters.spec.ts | 95 ++++++++++++++++++-------------- 4 files changed, 118 insertions(+), 48 deletions(-) diff --git a/helpers/contracts-deployments.ts b/helpers/contracts-deployments.ts index 82ac2d97..6fa0445d 100644 --- a/helpers/contracts-deployments.ts +++ b/helpers/contracts-deployments.ts @@ -34,9 +34,12 @@ import { MintableErc20Factory, MockAggregatorFactory, MockFlashLoanReceiverFactory, + MockUniswapV2Router02Factory, PriceOracleFactory, ReserveLogicFactory, StableDebtTokenFactory, + UniswapLiquiditySwapAdapterFactory, + UniswapRepayAdapterFactory, VariableDebtTokenFactory, WalletBalanceProviderFactory, } from '../types'; @@ -374,3 +377,33 @@ export const deployATokensAndRatesHelper = async ( args, verify ); + +export const deployMockUniswapRouter = async (verify?: boolean) => + withSaveAndVerify( + await new MockUniswapV2Router02Factory(await getFirstSigner()).deploy(), + eContractid.MockUniswapV2Router02, + [], + verify + ); + +export const deployUniswapLiquiditySwapAdapter = async ( + args: [tEthereumAddress, tEthereumAddress], + verify?: boolean +) => + withSaveAndVerify( + await new UniswapLiquiditySwapAdapterFactory(await getFirstSigner()).deploy(...args), + eContractid.UniswapLiquiditySwapAdapter, + args, + verify + ); + +export const deployUniswapRepayAdapter = async ( + args: [tEthereumAddress, tEthereumAddress], + verify?: boolean +) => + withSaveAndVerify( + await new UniswapRepayAdapterFactory(await getFirstSigner()).deploy(...args), + eContractid.UniswapRepayAdapter, + args, + verify + ); diff --git a/helpers/contracts-getters.ts b/helpers/contracts-getters.ts index 0b03cb63..e9b60e1b 100644 --- a/helpers/contracts-getters.ts +++ b/helpers/contracts-getters.ts @@ -11,10 +11,13 @@ import { LendingRateOracleFactory, MintableErc20Factory, MockFlashLoanReceiverFactory, + MockUniswapV2Router02Factory, PriceOracleFactory, ReserveLogicFactory, StableAndVariableTokensHelperFactory, StableDebtTokenFactory, + UniswapLiquiditySwapAdapterFactory, + UniswapRepayAdapterFactory, VariableDebtTokenFactory, } from '../types'; import {Ierc20DetailedFactory} from '../types/Ierc20DetailedFactory'; @@ -222,3 +225,26 @@ export const getATokensAndRatesHelper = async (address?: tEthereumAddress) => .address, await getFirstSigner() ); + +export const getMockUniswapRouter = async (address?: tEthereumAddress) => + await MockUniswapV2Router02Factory.connect( + address || + (await getDb().get(`${eContractid.MockUniswapV2Router02}.${BRE.network.name}`).value()) + .address, + await getFirstSigner() + ); + +export const getUniswapLiquiditySwapAdapter = async (address?: tEthereumAddress) => + await UniswapLiquiditySwapAdapterFactory.connect( + address || + (await getDb().get(`${eContractid.UniswapLiquiditySwapAdapter}.${BRE.network.name}`).value()) + .address, + await getFirstSigner() + ); + +export const getUniswapRepayAdapter = async (address?: tEthereumAddress) => + await UniswapRepayAdapterFactory.connect( + address || + (await getDb().get(`${eContractid.UniswapRepayAdapter}.${BRE.network.name}`).value()).address, + await getFirstSigner() + ); diff --git a/test/__setup.spec.ts b/test/__setup.spec.ts index f9047181..4a9e7241 100644 --- a/test/__setup.spec.ts +++ b/test/__setup.spec.ts @@ -246,19 +246,19 @@ const buildTestEnv = async (deployer: Signer, secondaryWallet: Signer) => { const mockUniswapRouter = await deployMockUniswapRouter(); await insertContractAddressInDb(eContractid.MockUniswapV2Router02, mockUniswapRouter.address); - const UniswapLiquiditySwapAdapter = await deployUniswapLiquiditySwapAdapter( + const UniswapLiquiditySwapAdapter = await deployUniswapLiquiditySwapAdapter([ addressesProvider.address, - mockUniswapRouter.address - ); + mockUniswapRouter.address, + ]); await insertContractAddressInDb( eContractid.UniswapLiquiditySwapAdapter, UniswapLiquiditySwapAdapter.address ); - const UniswapRepayAdapter = await deployUniswapRepayAdapter( + const UniswapRepayAdapter = await deployUniswapRepayAdapter([ addressesProvider.address, - mockUniswapRouter.address - ); + mockUniswapRouter.address, + ]); await insertContractAddressInDb(eContractid.UniswapRepayAdapter, UniswapRepayAdapter.address); await deployWalletBalancerProvider(addressesProvider.address); diff --git a/test/uniswapAdapters.spec.ts b/test/uniswapAdapters.spec.ts index 6fe92b8a..50007af0 100644 --- a/test/uniswapAdapters.spec.ts +++ b/test/uniswapAdapters.spec.ts @@ -1,11 +1,10 @@ import {makeSuite, TestEnv} from './helpers/make-suite'; +import {convertToCurrencyDecimals, getContract} from '../helpers/contracts-helpers'; +import {getMockUniswapRouter} from '../helpers/contracts-getters'; import { - convertToCurrencyDecimals, deployUniswapLiquiditySwapAdapter, deployUniswapRepayAdapter, - getContract, - getMockUniswapRouter, -} from '../helpers/contracts-helpers'; +} from '../helpers/contracts-deployments'; import {MockUniswapV2Router02} from '../types/MockUniswapV2Router02'; import {Zero} from '@ethersproject/constants'; import BigNumber from 'bignumber.js'; @@ -68,15 +67,15 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { describe('constructor', () => { it('should deploy with correct parameters', async () => { const {addressesProvider} = testEnv; - await deployUniswapLiquiditySwapAdapter( + await deployUniswapLiquiditySwapAdapter([ addressesProvider.address, - mockUniswapRouter.address - ); + mockUniswapRouter.address, + ]); }); it('should revert if not valid addresses provider', async () => { expect( - deployUniswapLiquiditySwapAdapter(mockUniswapRouter.address, mockUniswapRouter.address) + deployUniswapLiquiditySwapAdapter([mockUniswapRouter.address, mockUniswapRouter.address]) ).to.be.reverted; }); }); @@ -98,7 +97,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { }); it('should correctly swap tokens and deposit the out tokens in the pool', async () => { - const {users, weth, oracle, dai, aDai, aEth, pool, uniswapLiquiditySwapAdapter} = testEnv; + const {users, weth, oracle, dai, aDai, aWETH, pool, uniswapLiquiditySwapAdapter} = testEnv; const user = users[0].signer; const userAddress = users[0].address; @@ -114,8 +113,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { // User will swap liquidity 10 aEth to aDai const liquidityToSwap = parseEther('10'); - await aEth.connect(user).approve(uniswapLiquiditySwapAdapter.address, liquidityToSwap); - const userAEthBalanceBefore = await aEth.balanceOf(userAddress); + await aWETH.connect(user).approve(uniswapLiquiditySwapAdapter.address, liquidityToSwap); + const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); // Subtract the FL fee from the amount to be swapped 0,09% const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); @@ -134,6 +133,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { [weth.address], [flashloanAmount.toString()], 0, + userAddress, params, 0 ) @@ -148,7 +148,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { userAddress ); const userADaiBalance = await aDai.balanceOf(userAddress); - const userAEthBalance = await aEth.balanceOf(userAddress); + const userAEthBalance = await aWETH.balanceOf(userAddress); expect(adapterWethBalance).to.be.eq(Zero); expect(adapterDaiBalance).to.be.eq(Zero); @@ -227,6 +227,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { [usdc.address], [flashloanAmount.toString()], 0, + userAddress, params, 0 ) @@ -249,7 +250,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { }); it('should revert if slippage param is not inside limits', async () => { - const {users, pool, weth, oracle, dai, aEth, uniswapLiquiditySwapAdapter} = testEnv; + const {users, pool, weth, oracle, dai, aWETH, uniswapLiquiditySwapAdapter} = testEnv; const user = users[0].signer; const userAddress = users[0].address; @@ -268,7 +269,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { // User will swap liquidity 10 aEth to aDai const liquidityToSwap = parseEther('10'); - await aEth.connect(user).approve(uniswapLiquiditySwapAdapter.address, liquidityToSwap); + await aWETH.connect(user).approve(uniswapLiquiditySwapAdapter.address, liquidityToSwap); // Subtract the FL fee from the amount to be swapped 0,09% const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); @@ -292,6 +293,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { [weth.address], [flashloanAmount.toString()], 0, + userAddress, params1, 0 ) @@ -304,6 +306,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { [weth.address], [flashloanAmount.toString()], 0, + userAddress, params2, 0 ) @@ -311,7 +314,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { }); it('should revert when swap exceed slippage', async () => { - const {users, weth, oracle, dai, aEth, pool, uniswapLiquiditySwapAdapter} = testEnv; + const {users, weth, oracle, dai, aWETH, pool, uniswapLiquiditySwapAdapter} = testEnv; const user = users[0].signer; const userAddress = users[0].address; @@ -334,7 +337,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { // User will swap liquidity 10 aEth to aDai const liquidityToSwap = parseEther('10'); - await aEth.connect(user).approve(uniswapLiquiditySwapAdapter.address, liquidityToSwap); + await aWETH.connect(user).approve(uniswapLiquiditySwapAdapter.address, liquidityToSwap); // Subtract the FL fee from the amount to be swapped 0,09% const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); @@ -352,6 +355,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { [weth.address], [flashloanAmount.toString()], 0, + userAddress, params, 0 ) @@ -376,7 +380,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { }); it('should correctly swap tokens and deposit the out tokens in the pool', async () => { - const {users, weth, oracle, dai, aDai, aEth, uniswapLiquiditySwapAdapter} = testEnv; + const {users, weth, oracle, dai, aDai, aWETH, uniswapLiquiditySwapAdapter} = testEnv; const user = users[0].signer; const userAddress = users[0].address; @@ -392,8 +396,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { // User will swap liquidity 10 aEth to aDai const liquidityToSwap = parseEther('10'); - await aEth.connect(user).approve(uniswapLiquiditySwapAdapter.address, liquidityToSwap); - const userAEthBalanceBefore = await aEth.balanceOf(userAddress); + await aWETH.connect(user).approve(uniswapLiquiditySwapAdapter.address, liquidityToSwap); + const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); await expect( uniswapLiquiditySwapAdapter.swapAndDeposit( @@ -414,7 +418,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { userAddress ); const userADaiBalance = await aDai.balanceOf(userAddress); - const userAEthBalance = await aEth.balanceOf(userAddress); + const userAEthBalance = await aWETH.balanceOf(userAddress); expect(adapterWethBalance).to.be.eq(Zero); expect(adapterDaiBalance).to.be.eq(Zero); @@ -430,11 +434,11 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { describe('constructor', () => { it('should deploy with correct parameters', async () => { const {addressesProvider} = testEnv; - await deployUniswapRepayAdapter(addressesProvider.address, mockUniswapRouter.address); + await deployUniswapRepayAdapter([addressesProvider.address, mockUniswapRouter.address]); }); it('should revert if not valid addresses provider', async () => { - expect(deployUniswapRepayAdapter(mockUniswapRouter.address, mockUniswapRouter.address)).to + expect(deployUniswapRepayAdapter([mockUniswapRouter.address, mockUniswapRouter.address])).to .be.reverted; }); }); @@ -460,7 +464,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { users, pool, weth, - aEth, + aWETH, oracle, dai, uniswapRepayAdapter, @@ -492,8 +496,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); const liquidityToSwap = amountWETHtoSwap; - await aEth.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); - const userAEthBalanceBefore = await aEth.balanceOf(userAddress); + await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); // Subtract the FL fee from the amount to be swapped 0,09% const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); @@ -514,6 +518,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { [weth.address], [flashloanAmount.toString()], 0, + userAddress, params, 0 ) @@ -524,7 +529,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); - const userAEthBalance = await aEth.balanceOf(userAddress); + const userAEthBalance = await aWETH.balanceOf(userAddress); expect(adapterWethBalance).to.be.eq(Zero); expect(adapterDaiBalance).to.be.eq(Zero); @@ -535,7 +540,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { }); it('should revert if there is not debt to repay with the specified rate mode', async () => { - const {users, pool, weth, oracle, dai, uniswapRepayAdapter, aEth} = testEnv; + const {users, pool, weth, oracle, dai, uniswapRepayAdapter, aWETH} = testEnv; const user = users[0].signer; const userAddress = users[0].address; @@ -554,7 +559,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await pool.connect(user).borrow(dai.address, expectedDaiAmount, 2, 0, userAddress); const liquidityToSwap = amountWETHtoSwap; - await aEth.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); // Subtract the FL fee from the amount to be swapped 0,09% const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); @@ -575,6 +580,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { [weth.address], [flashloanAmount.toString()], 0, + userAddress, params, 0 ) @@ -582,7 +588,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { }); it('should revert if there is not debt to repay', async () => { - const {users, pool, weth, oracle, dai, uniswapRepayAdapter, aEth} = testEnv; + const {users, pool, weth, oracle, dai, uniswapRepayAdapter, aWETH} = testEnv; const user = users[0].signer; const userAddress = users[0].address; @@ -598,7 +604,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { ); const liquidityToSwap = amountWETHtoSwap; - await aEth.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); // Subtract the FL fee from the amount to be swapped 0,09% const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); @@ -619,6 +625,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { [weth.address], [flashloanAmount.toString()], 0, + userAddress, params, 0 ) @@ -626,7 +633,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { }); it('should revert when the received amount is less than expected', async () => { - const {users, pool, weth, oracle, dai, aEth, uniswapRepayAdapter} = testEnv; + const {users, pool, weth, oracle, dai, aWETH, uniswapRepayAdapter} = testEnv; const user = users[0].signer; const userAddress = users[0].address; @@ -642,7 +649,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); const liquidityToSwap = amountWETHtoSwap; - await aEth.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); // Subtract the FL fee from the amount to be swapped 0,09% const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); @@ -667,6 +674,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { [weth.address], [flashloanAmount.toString()], 0, + userAddress, params, 0 ) @@ -674,7 +682,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { }); it('should revert when max amount allowed to swap is bigger than max slippage', async () => { - const {users, pool, weth, oracle, dai, aEth, uniswapRepayAdapter} = testEnv; + const {users, pool, weth, oracle, dai, aWETH, uniswapRepayAdapter} = testEnv; const user = users[0].signer; const userAddress = users[0].address; @@ -689,7 +697,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { // Open user Debt await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); - await aEth.connect(user).approve(uniswapRepayAdapter.address, amountWETHtoSwap); + await aWETH.connect(user).approve(uniswapRepayAdapter.address, amountWETHtoSwap); // Subtract the FL fee from the amount to be swapped 0,09% const bigMaxAmountToSwap = amountWETHtoSwap.mul(2); @@ -711,6 +719,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { [weth.address], [flashloanAmount.toString()], 0, + userAddress, params, 0 ) @@ -722,7 +731,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { users, pool, weth, - aEth, + aWETH, oracle, dai, uniswapRepayAdapter, @@ -754,8 +763,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); const liquidityToSwap = amountWETHtoSwap; - await aEth.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); - const userAEthBalanceBefore = await aEth.balanceOf(userAddress); + await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); // Subtract the FL fee from the amount to be swapped 0,09% const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); @@ -782,6 +791,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { [weth.address], [flashloanAmount.toString()], 0, + userAddress, params, 0 ) @@ -792,7 +802,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); - const userAEthBalance = await aEth.balanceOf(userAddress); + const userAEthBalance = await aWETH.balanceOf(userAddress); expect(adapterWethBalance).to.be.eq(Zero); expect(adapterDaiBalance).to.be.eq(Zero); @@ -810,7 +820,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { users, pool, weth, - aEth, + aWETH, oracle, dai, uniswapRepayAdapter, @@ -842,8 +852,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); const liquidityToSwap = amountWETHtoSwap; - await aEth.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); - const userAEthBalanceBefore = await aEth.balanceOf(userAddress); + await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); // Subtract the FL fee from the amount to be swapped 0,09% const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); @@ -872,6 +882,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { [weth.address], [flashloanAmount.toString()], 0, + userAddress, params, 0 ) @@ -882,7 +893,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); - const userAEthBalance = await aEth.balanceOf(userAddress); + const userAEthBalance = await aWETH.balanceOf(userAddress); const wethBalance = await weth.balanceOf(userAddress); expect(adapterWethBalance).to.be.eq(Zero); From 0eddff493354e0015b4e9ed0d9711b750baa2ab4 Mon Sep 17 00:00:00 2001 From: Gerardo Nardelli Date: Fri, 30 Oct 2020 16:59:25 -0300 Subject: [PATCH 07/62] Apply feedback fixes --- contracts/adapters/BaseUniswapAdapter.sol | 94 ++-- .../adapters/UniswapLiquiditySwapAdapter.sol | 70 +-- contracts/adapters/UniswapRepayAdapter.sol | 114 ++++- test/uniswapAdapters.spec.ts | 402 ++++++++++++------ 4 files changed, 438 insertions(+), 242 deletions(-) diff --git a/contracts/adapters/BaseUniswapAdapter.sol b/contracts/adapters/BaseUniswapAdapter.sol index eb70d2e4..26aa60b5 100644 --- a/contracts/adapters/BaseUniswapAdapter.sol +++ b/contracts/adapters/BaseUniswapAdapter.sol @@ -5,15 +5,14 @@ pragma experimental ABIEncoderV2; import {PercentageMath} from '../libraries/math/PercentageMath.sol'; import {SafeMath} from '../dependencies/openzeppelin/contracts/SafeMath.sol'; import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol'; +import {IERC20Detailed} from '../dependencies/openzeppelin/contracts/IERC20Detailed.sol'; import {SafeERC20} from '../dependencies/openzeppelin/contracts/SafeERC20.sol'; import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol'; import {ILendingPool} from '../interfaces/ILendingPool.sol'; import {ReserveLogic} from '../libraries/logic/ReserveLogic.sol'; -import {ReserveConfiguration} from '../libraries/configuration/ReserveConfiguration.sol'; import {IUniswapV2Router02} from '../interfaces/IUniswapV2Router02.sol'; import {IPriceOracleGetter} from '../interfaces/IPriceOracleGetter.sol'; - /** * @title BaseUniswapAdapter * @notice Implements the logic for performing assets swaps in Uniswap V2 @@ -23,23 +22,24 @@ contract BaseUniswapAdapter { using SafeMath for uint256; using PercentageMath for uint256; using SafeERC20 for IERC20; - using ReserveConfiguration for ReserveConfiguration.Map; + + enum LeftoverAction {DEPOSIT, TRANSFER} // Max slippage percent allow by param uint256 public constant MAX_SLIPPAGE_PERCENT = 3000; // 30% // Min slippage percent allow by param uint256 public constant MIN_SLIPPAGE_PERCENT = 10; // 0,1% - ILendingPoolAddressesProvider public immutable addressesProvider; - IUniswapV2Router02 public immutable uniswapRouter; - ILendingPool public immutable pool; + ILendingPool public immutable POOL; + IPriceOracleGetter public immutable ORACLE; + IUniswapV2Router02 public immutable UNISWAP_ROUTER; event Swapped(address fromAsset, address toAsset, uint256 fromAmount, uint256 receivedAmount); - constructor(ILendingPoolAddressesProvider _addressesProvider, IUniswapV2Router02 _uniswapRouter) public { - addressesProvider = _addressesProvider; - pool = ILendingPool(_addressesProvider.getLendingPool()); - uniswapRouter = _uniswapRouter; + constructor(ILendingPoolAddressesProvider addressesProvider, IUniswapV2Router02 uniswapRouter) public { + POOL = ILendingPool(addressesProvider.getLendingPool()); + ORACLE = IPriceOracleGetter(addressesProvider.getPriceOracle()); + UNISWAP_ROUTER = uniswapRouter; } /** @@ -58,7 +58,7 @@ contract BaseUniswapAdapter { path[0] = reserveIn; path[1] = reserveOut; - uint256[] memory amounts = uniswapRouter.getAmountsOut(amountIn, path); + uint256[] memory amounts = UNISWAP_ROUTER.getAmountsOut(amountIn, path); return amounts[1]; } @@ -79,7 +79,7 @@ contract BaseUniswapAdapter { path[0] = reserveIn; path[1] = reserveOut; - uint256[] memory amounts = uniswapRouter.getAmountsIn(amountOut, path); + uint256[] memory amounts = UNISWAP_ROUTER.getAmountsIn(amountOut, path); return amounts[0]; } @@ -101,24 +101,23 @@ contract BaseUniswapAdapter { internal returns (uint256) { - uint256 fromAssetDecimals = getDecimals(assetToSwapFrom); - uint256 toAssetDecimals = getDecimals(assetToSwapTo); + uint256 fromAssetDecimals = _getDecimals(assetToSwapFrom); + uint256 toAssetDecimals = _getDecimals(assetToSwapTo); - (uint256 fromAssetPrice, uint256 toAssetPrice) = getPrices(assetToSwapFrom, assetToSwapTo); + uint256 fromAssetPrice = _getPrice(assetToSwapFrom); + uint256 toAssetPrice = _getPrice(assetToSwapTo); uint256 amountOutMin = amountToSwap .mul(fromAssetPrice.mul(10**toAssetDecimals)) .div(toAssetPrice.mul(10**fromAssetDecimals)) .percentMul(PercentageMath.PERCENTAGE_FACTOR.sub(slippage)); - IERC20(assetToSwapFrom).approve(address(uniswapRouter), amountToSwap); + IERC20(assetToSwapFrom).approve(address(UNISWAP_ROUTER), amountToSwap); address[] memory path = new address[](2); path[0] = assetToSwapFrom; path[1] = assetToSwapTo; - uint256[] memory amounts = uniswapRouter.swapExactTokensForTokens(amountToSwap, amountOutMin, path, address(this), block.timestamp); - - require(amounts[1] >= amountOutMin, 'INSUFFICIENT_OUTPUT_AMOUNT'); + uint256[] memory amounts = UNISWAP_ROUTER.swapExactTokensForTokens(amountToSwap, amountOutMin, path, address(this), block.timestamp); emit Swapped(assetToSwapFrom, assetToSwapTo, amounts[0], amounts[1]); @@ -143,10 +142,11 @@ contract BaseUniswapAdapter { internal returns (uint256) { - uint256 fromAssetDecimals = getDecimals(assetToSwapFrom); - uint256 toAssetDecimals = getDecimals(assetToSwapTo); + uint256 fromAssetDecimals = _getDecimals(assetToSwapFrom); + uint256 toAssetDecimals = _getDecimals(assetToSwapTo); - (uint256 fromAssetPrice, uint256 toAssetPrice) = getPrices(assetToSwapFrom, assetToSwapTo); + uint256 fromAssetPrice = _getPrice(assetToSwapFrom); + uint256 toAssetPrice = _getPrice(assetToSwapTo); uint256 expectedMaxAmountToSwap = amountToReceive .mul(toAssetPrice.mul(10**fromAssetDecimals)) @@ -155,14 +155,12 @@ contract BaseUniswapAdapter { require(maxAmountToSwap < expectedMaxAmountToSwap, 'maxAmountToSwap exceed max slippage'); - IERC20(assetToSwapFrom).approve(address(uniswapRouter), maxAmountToSwap); + IERC20(assetToSwapFrom).approve(address(UNISWAP_ROUTER), maxAmountToSwap); address[] memory path = new address[](2); path[0] = assetToSwapFrom; path[1] = assetToSwapTo; - uint256[] memory amounts = uniswapRouter.swapTokensForExactTokens(amountToReceive, maxAmountToSwap, path, address(this), block.timestamp); - - require(amounts[1] >= amountToReceive, 'INSUFFICIENT_OUTPUT_AMOUNT'); + uint256[] memory amounts = UNISWAP_ROUTER.swapTokensForExactTokens(amountToReceive, maxAmountToSwap, path, address(this), block.timestamp); emit Swapped(assetToSwapFrom, assetToSwapTo, amounts[0], amounts[1]); @@ -170,34 +168,20 @@ contract BaseUniswapAdapter { } /** - * @dev Get assets prices from the oracle denominated in eth - * @param assetToSwapFrom first asset - * @param assetToSwapTo second asset - * @return fromAssetPrice eth price for the first asset - * @return toAssetPrice eth price for the second asset + * @dev Get the price of the asset from the oracle denominated in eth + * @param asset address + * @return eth price for the asset */ - function getPrices( - address assetToSwapFrom, - address assetToSwapTo - ) - internal - view - returns (uint256 fromAssetPrice, uint256 toAssetPrice) - { - IPriceOracleGetter oracle = IPriceOracleGetter(addressesProvider.getPriceOracle()); - fromAssetPrice = oracle.getAssetPrice(assetToSwapFrom); - toAssetPrice = oracle.getAssetPrice(assetToSwapTo); + function _getPrice(address asset) internal view returns (uint256) { + return ORACLE.getAssetPrice(asset); } /** * @dev Get the decimals of an asset * @return number of decimals of the asset */ - function getDecimals(address asset) internal view returns (uint256) { - ReserveConfiguration.Map memory configuration = pool.getConfiguration(asset); - (, , , uint256 decimals, ) = configuration.getParamsMemory(); - - return decimals; + function _getDecimals(address asset) internal view returns (uint256) { + return IERC20Detailed(asset).decimals(); } /** @@ -205,7 +189,7 @@ contract BaseUniswapAdapter { * @return address of the aToken */ function getAToken(address asset) internal view returns (address) { - ReserveLogic.ReserveData memory reserve = pool.getReserveData(asset); + ReserveLogic.ReserveData memory reserve = POOL.getReserveData(asset); return reserve.aTokenAddress; } @@ -213,19 +197,19 @@ contract BaseUniswapAdapter { * @dev Take action with the swap left overs as configured in the parameters * @param asset address of the asset * @param reservedAmount Amount reserved to be used by the contract to repay the flash loan - * @param leftOverAction Flag indicating what to do with the left over balance from the swap: + * @param leftOverAction enum indicating what to do with the left over balance from the swap: * (0) Deposit back * (1) Direct transfer to user * @param user address */ - function sendLeftOver(address asset, uint256 reservedAmount, uint256 leftOverAction, address user) internal { + function sendLeftovers(address asset, uint256 reservedAmount, LeftoverAction leftOverAction, address user) internal { uint256 balance = IERC20(asset).balanceOf(address(this)); uint256 assetLeftOver = balance.sub(reservedAmount); if (assetLeftOver > 0) { - if (leftOverAction == 0) { - IERC20(asset).approve(address(pool), balance); - pool.deposit(asset, assetLeftOver, user, 0); + if (leftOverAction == LeftoverAction.DEPOSIT) { + IERC20(asset).approve(address(POOL), balance); + POOL.deposit(asset, assetLeftOver, user, 0); } else { IERC20(asset).transfer(user, assetLeftOver); } @@ -249,7 +233,7 @@ contract BaseUniswapAdapter { IERC20(reserveAToken).safeTransferFrom(user, address(this), amount); // withdraw reserve - pool.withdraw(reserve, amount); + POOL.withdraw(reserve, amount, address(this)); } /** @@ -266,6 +250,6 @@ contract BaseUniswapAdapter { pullAToken(reserve, user, flashLoanDebt); // Repay flashloan - IERC20(reserve).approve(address(pool), flashLoanDebt); + IERC20(reserve).approve(address(POOL), flashLoanDebt); } } diff --git a/contracts/adapters/UniswapLiquiditySwapAdapter.sol b/contracts/adapters/UniswapLiquiditySwapAdapter.sol index d44f50fd..2a40e0bc 100644 --- a/contracts/adapters/UniswapLiquiditySwapAdapter.sol +++ b/contracts/adapters/UniswapLiquiditySwapAdapter.sol @@ -16,11 +16,11 @@ import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol'; contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { constructor( - ILendingPoolAddressesProvider _addressesProvider, - IUniswapV2Router02 _uniswapRouter + ILendingPoolAddressesProvider addressesProvider, + IUniswapV2Router02 uniswapRouter ) public - BaseUniswapAdapter(_addressesProvider, _uniswapRouter) + BaseUniswapAdapter(addressesProvider, uniswapRouter) {} /** @@ -31,32 +31,34 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { * @param assets Address to be swapped * @param amounts Amount of the reserve to be swapped * @param premiums Fee of the flash loan + * @param initiator Address of the user * @param params Additional variadic field to include extra params. Expected parameters: - * address assetToSwapTo Address of the reserve to be swapped to and deposited - * address user The address of the user + * address[] assetToSwapToList List of the addresses of the reserve to be swapped to and deposited * uint256 slippage The max slippage percentage allowed for the swap */ function executeOperation( address[] calldata assets, uint256[] calldata amounts, uint256[] calldata premiums, + address initiator, bytes calldata params ) external override returns (bool) { - ( - address assetToSwapTo, - address user, - uint256 slippage - ) = abi.decode(params, (address, address, uint256)); + require(msg.sender == address(POOL), "CALLER_MUST_BE_LENDING_POOL"); + + (address[] memory assetToSwapToList, uint256 slippage) = abi.decode(params, (address[], uint256)); require(slippage < MAX_SLIPPAGE_PERCENT && slippage >= MIN_SLIPPAGE_PERCENT, 'SLIPPAGE_OUT_OF_RANGE'); + require(assetToSwapToList.length == assets.length, 'INCONSISTENT_PARAMS'); - uint256 receivedAmount = swapExactTokensForTokens(assets[0], assetToSwapTo, amounts[0], slippage); + for (uint256 i = 0; i < assets.length; i++) { + uint256 receivedAmount = swapExactTokensForTokens(assets[i], assetToSwapToList[i], amounts[i], slippage); - // Deposit new reserve - IERC20(assetToSwapTo).approve(address(pool), receivedAmount); - pool.deposit(assetToSwapTo, receivedAmount, user, 0); + // Deposit new reserve + IERC20(assetToSwapToList[i]).approve(address(POOL), receivedAmount); + POOL.deposit(assetToSwapToList[i], receivedAmount, initiator, 0); - uint256 flashLoanDebt = amounts[0].add(premiums[0]); - pullATokenAndRepayFlashLoan(assets[0], user, flashLoanDebt); + uint256 flashLoanDebt = amounts[i].add(premiums[i]); + pullATokenAndRepayFlashLoan(assets[i], initiator, flashLoanDebt); + } return true; } @@ -66,25 +68,35 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { * This method can be used when the user has no debts. * The user should give this contract allowance to pull the ATokens in order to withdraw the underlying asset and * perform the swap. - * @param assetToSwapFrom Address of the underlying asset to be swap from - * @param assetToSwapTo Address of the underlying asset to be swap to and deposited - * @param amountToSwap How much `assetToSwapFrom` needs to be swapped - * @param user Address that will be pulling the swapped funds + * @param assetToSwapFromList List of addresses of the underlying asset to be swap from + * @param assetToSwapToList List of addresses of the underlying asset to be swap to and deposited + * @param amountToSwapList List of amounts to be swapped * @param slippage The max slippage percentage allowed for the swap */ function swapAndDeposit( - address assetToSwapFrom, - address assetToSwapTo, - uint256 amountToSwap, - address user, + address[] calldata assetToSwapFromList, + address[] calldata assetToSwapToList, + uint256[] calldata amountToSwapList, uint256 slippage ) external { - pullAToken(assetToSwapFrom, user, amountToSwap); + require( + assetToSwapFromList.length == assetToSwapToList.length && assetToSwapFromList.length == amountToSwapList.length, + 'INCONSISTENT_PARAMS' + ); - uint256 receivedAmount = swapExactTokensForTokens(assetToSwapFrom, assetToSwapTo, amountToSwap, slippage); + for (uint256 i = 0; i < assetToSwapFromList.length; i++) { + pullAToken(assetToSwapFromList[i], msg.sender, amountToSwapList[i]); - // Deposit new reserve - IERC20(assetToSwapTo).approve(address(pool), receivedAmount); - pool.deposit(assetToSwapTo, receivedAmount, user, 0); + uint256 receivedAmount = swapExactTokensForTokens( + assetToSwapFromList[i], + assetToSwapToList[i], + amountToSwapList[i], + slippage + ); + + // Deposit new reserve + IERC20(assetToSwapToList[i]).approve(address(POOL), receivedAmount); + POOL.deposit(assetToSwapToList[i], receivedAmount, msg.sender, 0); + } } } diff --git a/contracts/adapters/UniswapRepayAdapter.sol b/contracts/adapters/UniswapRepayAdapter.sol index 70490fa7..755bfb13 100644 --- a/contracts/adapters/UniswapRepayAdapter.sol +++ b/contracts/adapters/UniswapRepayAdapter.sol @@ -15,12 +15,19 @@ import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol'; **/ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { + struct RepayParams { + address[] assetToSwapToList; + LeftoverAction leftOverAction; + uint256[] repayAmounts; + uint256[] rateModes; + } + constructor( - ILendingPoolAddressesProvider _addressesProvider, - IUniswapV2Router02 _uniswapRouter + ILendingPoolAddressesProvider addressesProvider, + IUniswapV2Router02 uniswapRouter ) public - BaseUniswapAdapter(_addressesProvider, _uniswapRouter) + BaseUniswapAdapter(addressesProvider, uniswapRouter) {} /** @@ -31,41 +38,102 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { * @param assets Address to be swapped * @param amounts Amount of the reserve to be swapped * @param premiums Fee of the flash loan + * @param initiator Address of the user * @param params Additional variadic field to include extra params. Expected parameters: - * address assetToSwapTo Address of the reserve to be swapped to and deposited - * address user The address of the user + * address[] assetToSwapToList List of the addresses of the reserve to be swapped to and repay * uint256 leftOverAction Flag indicating what to do with the left over balance from the swap: * (0) Deposit back * (1) Direct transfer to user - * uint256 repayAmount Amount of debt to be repaid - * uint256 rateMode The rate modes of the debt to be repaid + * uint256[] repayAmounts List of amounts of debt to be repaid + * uint256[] rateModes List of the rate modes of the debt to be repaid */ function executeOperation( address[] calldata assets, uint256[] calldata amounts, uint256[] calldata premiums, + address initiator, bytes calldata params ) external override returns (bool) { - ( - address assetToSwapTo, - address user, - uint256 leftOverAction, - uint256 repayAmount, - uint256 rateMode - ) = abi.decode(params, (address, address, uint256, uint256, uint256)); + require(msg.sender == address(POOL), "CALLER_MUST_BE_LENDING_POOL"); - swapTokensForExactTokens(assets[0], assetToSwapTo, amounts[0], repayAmount); + RepayParams memory decodedParams = _decodeParams(params); - // Repay debt - IERC20(assetToSwapTo).approve(address(pool), repayAmount); - pool.repay(assetToSwapTo, repayAmount, rateMode, user); + require( + assets.length == decodedParams.assetToSwapToList.length + && assets.length == decodedParams.repayAmounts.length + && assets.length == decodedParams.rateModes.length, + 'INCONSISTENT_PARAMS'); - uint256 flashLoanDebt = amounts[0].add(premiums[0]); - pullATokenAndRepayFlashLoan(assets[0], user, flashLoanDebt); - - // Take care of reserve leftover from the swap - sendLeftOver(assets[0], flashLoanDebt, leftOverAction, user); + for (uint256 i = 0; i < assets.length; i++) { + _swapAndRepay( + assets[i], + decodedParams.assetToSwapToList[i], + amounts[i], + decodedParams.repayAmounts[i], + decodedParams.rateModes[i], + initiator, + decodedParams.leftOverAction, + premiums[i] + ); + } return true; } + + /** + * @dev Perform the swap, the repay of the debt and send back the left overs + * + * @param assetFrom Address of token to be swapped + * @param assetTo Address of token to be received + * @param amount Amount of the reserve to be swapped + * @param repayAmount Amount of the debt to be repaid + * @param rateMode Rate mode of the debt to be repaid + * @param initiator Address of the user + * @param leftOverAction enum indicating what to do with the left over balance from the swap + * @param premium Fee of the flash loan + */ + function _swapAndRepay( + address assetFrom, + address assetTo, + uint256 amount, + uint256 repayAmount, + uint256 rateMode, + address initiator, + LeftoverAction leftOverAction, + uint256 premium + ) internal { + swapTokensForExactTokens(assetFrom, assetTo, amount, repayAmount); + + // Repay debt + IERC20(assetTo).approve(address(POOL), repayAmount); + POOL.repay(assetTo, repayAmount, rateMode, initiator); + + uint256 flashLoanDebt = amount.add(premium); + pullATokenAndRepayFlashLoan(assetFrom, initiator, flashLoanDebt); + + // Take care of reserve leftover from the swap + sendLeftovers(assetFrom, flashLoanDebt, leftOverAction, initiator); + } + + /** + * @dev Decodes debt information encoded in flashloan params + * @param params Additional variadic field to include extra params. Expected parameters: + * address[] assetToSwapToList List of the addresses of the reserve to be swapped to and repay + * uint256 leftOverAction Flag indicating what to do with the left over balance from the swap: + * (0) Deposit back + * (1) Direct transfer to user + * uint256[] repayAmounts List of amounts of debt to be repaid + * uint256[] rateModes List of the rate modes of the debt to be repaid + * @return RepayParams struct containing decoded params + */ + function _decodeParams(bytes memory params) internal returns (RepayParams memory) { + ( + address[] memory assetToSwapToList, + LeftoverAction leftOverAction, + uint256[] memory repayAmounts, + uint256[] memory rateModes + ) = abi.decode(params, (address[], LeftoverAction, uint256[], uint256[])); + + return RepayParams(assetToSwapToList, leftOverAction, repayAmounts, rateModes); + } } diff --git a/test/uniswapAdapters.spec.ts b/test/uniswapAdapters.spec.ts index 50007af0..555f0ef0 100644 --- a/test/uniswapAdapters.spec.ts +++ b/test/uniswapAdapters.spec.ts @@ -121,8 +121,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { // 0,5% slippage const params = ethers.utils.defaultAbiCoder.encode( - ['address', 'address', 'uint256'], - [dai.address, userAddress, 50] + ['address[]', 'uint256'], + [[dai.address], 50] ); await expect( @@ -132,7 +132,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { uniswapLiquiditySwapAdapter.address, [weth.address], [flashloanAmount.toString()], - 0, + [0], userAddress, params, 0 @@ -158,6 +158,90 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); }); + it('should revert if inconsistent params', async () => { + const {users, weth, oracle, dai, aWETH, pool, uniswapLiquiditySwapAdapter} = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + await mockUniswapRouter.setAmountToReturn(expectedDaiAmount); + + // User will swap liquidity 10 aEth to aDai + const liquidityToSwap = parseEther('10'); + await aWETH.connect(user).approve(uniswapLiquiditySwapAdapter.address, liquidityToSwap); + + // Subtract the FL fee from the amount to be swapped 0,09% + const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); + + // 0,5% slippage + const params = ethers.utils.defaultAbiCoder.encode( + ['address[]', 'uint256'], + [[dai.address, weth.address], 50] + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapLiquiditySwapAdapter.address, + [weth.address], + [flashloanAmount.toString()], + [0], + userAddress, + params, + 0 + ) + ).to.be.revertedWith('INCONSISTENT_PARAMS'); + }); + + it('should revert if caller not lending pool', async () => { + const {users, weth, oracle, dai, aWETH, uniswapLiquiditySwapAdapter} = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + await mockUniswapRouter.setAmountToReturn(expectedDaiAmount); + + // User will swap liquidity 10 aEth to aDai + const liquidityToSwap = parseEther('10'); + await aWETH.connect(user).approve(uniswapLiquiditySwapAdapter.address, liquidityToSwap); + + // Subtract the FL fee from the amount to be swapped 0,09% + const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); + + // 0,5% slippage + const params = ethers.utils.defaultAbiCoder.encode( + ['address[]', 'uint256'], + [[dai.address, weth.address], 50] + ); + + await expect( + uniswapLiquiditySwapAdapter + .connect(user) + .executeOperation( + [weth.address], + [flashloanAmount.toString()], + [0], + userAddress, + params + ) + ).to.be.revertedWith('CALLER_MUST_BE_LENDING_POOL'); + }); + it('should work correctly with tokens of different decimals', async () => { const { users, @@ -215,8 +299,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { // 0,5% slippage const params = ethers.utils.defaultAbiCoder.encode( - ['address', 'address', 'uint256'], - [dai.address, userAddress, 50] + ['address[]', 'uint256'], + [[dai.address], 50] ); await expect( @@ -226,7 +310,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { uniswapLiquiditySwapAdapter.address, [usdc.address], [flashloanAmount.toString()], - 0, + [0], userAddress, params, 0 @@ -275,14 +359,14 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { // 30% slippage const params1 = ethers.utils.defaultAbiCoder.encode( - ['address', 'address', 'uint256'], - [dai.address, userAddress, 3000] + ['address[]', 'uint256'], + [[dai.address], 3000] ); // 0,05% slippage const params2 = ethers.utils.defaultAbiCoder.encode( - ['address', 'address', 'uint256'], - [dai.address, userAddress, 5] + ['address[]', 'uint256'], + [[dai.address], 5] ); await expect( @@ -292,7 +376,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { uniswapLiquiditySwapAdapter.address, [weth.address], [flashloanAmount.toString()], - 0, + [0], userAddress, params1, 0 @@ -305,62 +389,13 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { uniswapLiquiditySwapAdapter.address, [weth.address], [flashloanAmount.toString()], - 0, + [0], userAddress, params2, 0 ) ).to.be.revertedWith('SLIPPAGE_OUT_OF_RANGE'); }); - - it('should revert when swap exceed slippage', async () => { - const {users, weth, oracle, dai, aWETH, pool, uniswapLiquiditySwapAdapter} = testEnv; - const user = users[0].signer; - const userAddress = users[0].address; - - const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); - - await weth.connect(user).mint(amountWETHtoSwap); - await weth.connect(user).transfer(uniswapLiquiditySwapAdapter.address, amountWETHtoSwap); - - const daiPrice = await oracle.getAssetPrice(dai.address); - const expectedDaiAmount = await convertToCurrencyDecimals( - dai.address, - new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) - ); - - // 1,5% slippage - const returnedDaiAmountWithBigSlippage = new BigNumber(expectedDaiAmount.toString()) - .multipliedBy(0.985) - .toFixed(0); - await mockUniswapRouter.connect(user).setAmountToReturn(returnedDaiAmountWithBigSlippage); - - // User will swap liquidity 10 aEth to aDai - const liquidityToSwap = parseEther('10'); - await aWETH.connect(user).approve(uniswapLiquiditySwapAdapter.address, liquidityToSwap); - // Subtract the FL fee from the amount to be swapped 0,09% - const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); - - // 0,5% slippage - const params = ethers.utils.defaultAbiCoder.encode( - ['address', 'address', 'uint256'], - [dai.address, userAddress, 50] - ); - - await expect( - pool - .connect(user) - .flashLoan( - uniswapLiquiditySwapAdapter.address, - [weth.address], - [flashloanAmount.toString()], - 0, - userAddress, - params, - 0 - ) - ).to.be.revertedWith('INSUFFICIENT_OUTPUT_AMOUNT'); - }); }); describe('swapAndDeposit', () => { @@ -400,13 +435,9 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); await expect( - uniswapLiquiditySwapAdapter.swapAndDeposit( - weth.address, - dai.address, - amountWETHtoSwap, - userAddress, - 50 - ) + uniswapLiquiditySwapAdapter + .connect(user) + .swapAndDeposit([weth.address], [dai.address], [amountWETHtoSwap], 50) ) .to.emit(uniswapLiquiditySwapAdapter, 'Swapped') .withArgs(weth.address, dai.address, amountWETHtoSwap.toString(), expectedDaiAmount); @@ -427,6 +458,30 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); }); + it('should revert if inconsistent params', async () => { + const {users, weth, dai, uniswapLiquiditySwapAdapter} = testEnv; + const user = users[0].signer; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + await expect( + uniswapLiquiditySwapAdapter + .connect(user) + .swapAndDeposit([weth.address, dai.address], [dai.address], [amountWETHtoSwap], 50) + ).to.be.revertedWith('INCONSISTENT_PARAMS'); + + await expect( + uniswapLiquiditySwapAdapter + .connect(user) + .swapAndDeposit([weth.address], [dai.address, weth.address], [amountWETHtoSwap], 50) + ).to.be.revertedWith('INCONSISTENT_PARAMS'); + + await expect( + uniswapLiquiditySwapAdapter + .connect(user) + .swapAndDeposit([weth.address], [dai.address], [amountWETHtoSwap, amountWETHtoSwap], 50) + ).to.be.revertedWith('INCONSISTENT_PARAMS'); + }); }); }); @@ -506,8 +561,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount); const params = ethers.utils.defaultAbiCoder.encode( - ['address', 'address', 'uint256', 'uint256', 'uint256'], - [dai.address, userAddress, 0, expectedDaiAmount, 1] + ['address[]', 'uint256', 'uint256[]', 'uint256[]'], + [[dai.address], 0, [expectedDaiAmount], [1]] ); await expect( @@ -517,7 +572,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { uniswapRepayAdapter.address, [weth.address], [flashloanAmount.toString()], - 0, + [0], userAddress, params, 0 @@ -539,6 +594,132 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); }); + it('should revert if inconsistent params', async () => { + const {users, pool, weth, aWETH, oracle, dai, uniswapRepayAdapter} = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); + + const liquidityToSwap = amountWETHtoSwap; + await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + + // Subtract the FL fee from the amount to be swapped 0,09% + const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); + + await mockUniswapRouter.connect(user).setAmountToSwap(flashloanAmount); + await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount); + + const params1 = ethers.utils.defaultAbiCoder.encode( + ['address[]', 'uint256', 'uint256[]', 'uint256[]'], + [[dai.address, weth.address], 0, [expectedDaiAmount], [1]] + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapRepayAdapter.address, + [weth.address], + [flashloanAmount.toString()], + [0], + userAddress, + params1, + 0 + ) + ).to.be.revertedWith('INCONSISTENT_PARAMS'); + + const params2 = ethers.utils.defaultAbiCoder.encode( + ['address[]', 'uint256', 'uint256[]', 'uint256[]'], + [[dai.address], 0, [expectedDaiAmount, expectedDaiAmount], [1]] + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapRepayAdapter.address, + [weth.address], + [flashloanAmount.toString()], + [0], + userAddress, + params2, + 0 + ) + ).to.be.revertedWith('INCONSISTENT_PARAMS'); + + const params3 = ethers.utils.defaultAbiCoder.encode( + ['address[]', 'uint256', 'uint256[]', 'uint256[]'], + [[dai.address], 0, [expectedDaiAmount], [1, 1]] + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapRepayAdapter.address, + [weth.address], + [flashloanAmount.toString()], + [0], + userAddress, + params3, + 0 + ) + ).to.be.revertedWith('INCONSISTENT_PARAMS'); + }); + + it('should revert if caller not lending pool', async () => { + const {users, pool, weth, aWETH, oracle, dai, uniswapRepayAdapter} = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); + + const liquidityToSwap = amountWETHtoSwap; + await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + + // Subtract the FL fee from the amount to be swapped 0,09% + const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); + + await mockUniswapRouter.connect(user).setAmountToSwap(flashloanAmount); + await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount); + + const params = ethers.utils.defaultAbiCoder.encode( + ['address[]', 'uint256', 'uint256[]', 'uint256[]'], + [[dai.address], 0, [expectedDaiAmount], [1]] + ); + + await expect( + uniswapRepayAdapter + .connect(user) + .executeOperation( + [weth.address], + [flashloanAmount.toString()], + [0], + userAddress, + params + ) + ).to.be.revertedWith('CALLER_MUST_BE_LENDING_POOL'); + }); + it('should revert if there is not debt to repay with the specified rate mode', async () => { const {users, pool, weth, oracle, dai, uniswapRepayAdapter, aWETH} = testEnv; const user = users[0].signer; @@ -568,8 +749,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount); const params = ethers.utils.defaultAbiCoder.encode( - ['address', 'address', 'uint256', 'uint256', 'uint256'], - [dai.address, userAddress, 0, expectedDaiAmount, 1] + ['address[]', 'uint256', 'uint256[]', 'uint256[]'], + [[dai.address], 0, [expectedDaiAmount], [1]] ); await expect( @@ -579,7 +760,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { uniswapRepayAdapter.address, [weth.address], [flashloanAmount.toString()], - 0, + [0], userAddress, params, 0 @@ -613,8 +794,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount); const params = ethers.utils.defaultAbiCoder.encode( - ['address', 'address', 'uint256', 'uint256', 'uint256'], - [dai.address, userAddress, 0, expectedDaiAmount, 1] + ['address[]', 'uint256', 'uint256[]', 'uint256[]'], + [[dai.address], 0, [expectedDaiAmount], [1]] ); await expect( @@ -624,7 +805,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { uniswapRepayAdapter.address, [weth.address], [flashloanAmount.toString()], - 0, + [0], userAddress, params, 0 @@ -632,55 +813,6 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { ).to.be.reverted; }); - it('should revert when the received amount is less than expected', async () => { - const {users, pool, weth, oracle, dai, aWETH, uniswapRepayAdapter} = testEnv; - const user = users[0].signer; - const userAddress = users[0].address; - - const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); - - const daiPrice = await oracle.getAssetPrice(dai.address); - const expectedDaiAmount = await convertToCurrencyDecimals( - dai.address, - new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) - ); - - // Open user Debt - await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); - - const liquidityToSwap = amountWETHtoSwap; - await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); - - // Subtract the FL fee from the amount to be swapped 0,09% - const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); - - const insufficientOutput = new BigNumber(expectedDaiAmount.toString()) - .multipliedBy(0.985) - .toFixed(0); - - await mockUniswapRouter.connect(user).setAmountToSwap(flashloanAmount); - await mockUniswapRouter.connect(user).setAmountToReturn(insufficientOutput); - - const params = ethers.utils.defaultAbiCoder.encode( - ['address', 'address', 'uint256', 'uint256', 'uint256'], - [dai.address, userAddress, 0, expectedDaiAmount, 1] - ); - - await expect( - pool - .connect(user) - .flashLoan( - uniswapRepayAdapter.address, - [weth.address], - [flashloanAmount.toString()], - 0, - userAddress, - params, - 0 - ) - ).to.be.revertedWith('INSUFFICIENT_OUTPUT_AMOUNT'); - }); - it('should revert when max amount allowed to swap is bigger than max slippage', async () => { const {users, pool, weth, oracle, dai, aWETH, uniswapRepayAdapter} = testEnv; const user = users[0].signer; @@ -707,8 +839,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount); const params = ethers.utils.defaultAbiCoder.encode( - ['address', 'address', 'uint256', 'uint256', 'uint256'], - [dai.address, userAddress, 0, expectedDaiAmount, 1] + ['address[]', 'uint256', 'uint256[]', 'uint256[]'], + [[dai.address], 0, [expectedDaiAmount], [1]] ); await expect( @@ -718,7 +850,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { uniswapRepayAdapter.address, [weth.address], [flashloanAmount.toString()], - 0, + [0], userAddress, params, 0 @@ -779,8 +911,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount); const params = ethers.utils.defaultAbiCoder.encode( - ['address', 'address', 'uint256', 'uint256', 'uint256'], - [dai.address, userAddress, 0, expectedDaiAmount, 1] + ['address[]', 'uint256', 'uint256[]', 'uint256[]'], + [[dai.address], 0, [expectedDaiAmount], [1]] ); await expect( @@ -790,7 +922,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { uniswapRepayAdapter.address, [weth.address], [flashloanAmount.toString()], - 0, + [0], userAddress, params, 0 @@ -870,8 +1002,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const wethBalanceBefore = await weth.balanceOf(userAddress); const params = ethers.utils.defaultAbiCoder.encode( - ['address', 'address', 'uint256', 'uint256', 'uint256'], - [dai.address, userAddress, 1, expectedDaiAmount, 1] + ['address[]', 'uint256', 'uint256[]', 'uint256[]'], + [[dai.address], 1, [expectedDaiAmount], [1]] ); await expect( @@ -881,7 +1013,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { uniswapRepayAdapter.address, [weth.address], [flashloanAmount.toString()], - 0, + [0], userAddress, params, 0 From fa7fa9f9486f9d217843484bae4a6e611705975b Mon Sep 17 00:00:00 2001 From: Gerardo Nardelli Date: Mon, 2 Nov 2020 17:33:00 -0300 Subject: [PATCH 08/62] Add permit support in swap adapters --- contracts/adapters/BaseUniswapAdapter.sol | 48 +- .../adapters/UniswapLiquiditySwapAdapter.sol | 94 +- contracts/adapters/UniswapRepayAdapter.sol | 50 +- contracts/interfaces/IERC20WithPermit.sol | 16 + test/uniswapAdapters.spec.ts | 902 +++++++++++++++++- 5 files changed, 1052 insertions(+), 58 deletions(-) create mode 100644 contracts/interfaces/IERC20WithPermit.sol diff --git a/contracts/adapters/BaseUniswapAdapter.sol b/contracts/adapters/BaseUniswapAdapter.sol index 26aa60b5..48fe5db9 100644 --- a/contracts/adapters/BaseUniswapAdapter.sol +++ b/contracts/adapters/BaseUniswapAdapter.sol @@ -12,6 +12,7 @@ import {ILendingPool} from '../interfaces/ILendingPool.sol'; import {ReserveLogic} from '../libraries/logic/ReserveLogic.sol'; import {IUniswapV2Router02} from '../interfaces/IUniswapV2Router02.sol'; import {IPriceOracleGetter} from '../interfaces/IPriceOracleGetter.sol'; +import {IERC20WithPermit} from '../interfaces/IERC20WithPermit.sol'; /** * @title BaseUniswapAdapter @@ -25,6 +26,20 @@ contract BaseUniswapAdapter { enum LeftoverAction {DEPOSIT, TRANSFER} + struct PermitParams { + uint256[] deadline; + uint8[] v; + bytes32[] r; + bytes32[] s; + } + + struct PermitSignature { + uint256 deadline; + uint8 v; + bytes32 r; + bytes32 s; + } + // Max slippage percent allow by param uint256 public constant MAX_SLIPPAGE_PERCENT = 3000; // 30% // Min slippage percent allow by param @@ -221,14 +236,28 @@ contract BaseUniswapAdapter { * @param reserve address of the asset * @param user address * @param amount of tokens to be transferred to the contract + * @param permitSignature struct containing the permit signature */ function pullAToken( address reserve, address user, - uint256 amount + uint256 amount, + PermitSignature memory permitSignature ) internal { address reserveAToken = getAToken(reserve); + if (_usePermit(permitSignature)) { + IERC20WithPermit(reserveAToken).permit( + user, + address(this), + amount, + permitSignature.deadline, + permitSignature.v, + permitSignature.r, + permitSignature.s + ); + } + // transfer from user to adapter IERC20(reserveAToken).safeTransferFrom(user, address(this), amount); @@ -241,15 +270,28 @@ contract BaseUniswapAdapter { * @param reserve address of the asset * @param user address * @param flashLoanDebt need to be repaid + * @param permitSignature struct containing the permit signature */ function pullATokenAndRepayFlashLoan( address reserve, address user, - uint256 flashLoanDebt + uint256 flashLoanDebt, + PermitSignature memory permitSignature ) internal { - pullAToken(reserve, user, flashLoanDebt); + pullAToken(reserve, user, flashLoanDebt, permitSignature); // Repay flashloan IERC20(reserve).approve(address(POOL), flashLoanDebt); } + + /** + * @dev Tells if the permit method should be called by inspecting if there is a valid signature. + * If signature params are set to 0, then permit won't be called. + * @param signature struct containing the permit signature + * @return whether or not permit should be called + */ + function _usePermit(PermitSignature memory signature) internal pure returns (bool) { + return !(uint256(signature.deadline) == uint256(signature.v) && + uint256(signature.deadline) == 0); + } } diff --git a/contracts/adapters/UniswapLiquiditySwapAdapter.sol b/contracts/adapters/UniswapLiquiditySwapAdapter.sol index 2a40e0bc..221baada 100644 --- a/contracts/adapters/UniswapLiquiditySwapAdapter.sol +++ b/contracts/adapters/UniswapLiquiditySwapAdapter.sol @@ -15,6 +15,12 @@ import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol'; **/ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { + struct SwapParams { + address[] assetToSwapToList; + uint256 slippage; + PermitParams permitParams; + } + constructor( ILendingPoolAddressesProvider addressesProvider, IUniswapV2Router02 uniswapRouter @@ -35,6 +41,10 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { * @param params Additional variadic field to include extra params. Expected parameters: * address[] assetToSwapToList List of the addresses of the reserve to be swapped to and deposited * uint256 slippage The max slippage percentage allowed for the swap + * uint256[] deadline List of deadlines for the permit signature + * uint8[] v List of v param for the permit signature + * bytes32[] r List of r param for the permit signature + * bytes32[] s List of s param for the permit signature */ function executeOperation( address[] calldata assets, @@ -45,19 +55,46 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { ) external override returns (bool) { require(msg.sender == address(POOL), "CALLER_MUST_BE_LENDING_POOL"); - (address[] memory assetToSwapToList, uint256 slippage) = abi.decode(params, (address[], uint256)); - require(slippage < MAX_SLIPPAGE_PERCENT && slippage >= MIN_SLIPPAGE_PERCENT, 'SLIPPAGE_OUT_OF_RANGE'); - require(assetToSwapToList.length == assets.length, 'INCONSISTENT_PARAMS'); + SwapParams memory decodedParams = _decodeParams(params); + + require( + decodedParams.slippage < MAX_SLIPPAGE_PERCENT && decodedParams.slippage >= MIN_SLIPPAGE_PERCENT, + 'SLIPPAGE_OUT_OF_RANGE' + ); + + require( + decodedParams.assetToSwapToList.length == assets.length + && assets.length == decodedParams.permitParams.deadline.length + && assets.length == decodedParams.permitParams.v.length + && assets.length == decodedParams.permitParams.r.length + && assets.length == decodedParams.permitParams.s.length, + 'INCONSISTENT_PARAMS' + ); for (uint256 i = 0; i < assets.length; i++) { - uint256 receivedAmount = swapExactTokensForTokens(assets[i], assetToSwapToList[i], amounts[i], slippage); + uint256 receivedAmount = swapExactTokensForTokens( + assets[i], + decodedParams.assetToSwapToList[i], + amounts[i], + decodedParams.slippage + ); // Deposit new reserve - IERC20(assetToSwapToList[i]).approve(address(POOL), receivedAmount); - POOL.deposit(assetToSwapToList[i], receivedAmount, initiator, 0); + IERC20(decodedParams.assetToSwapToList[i]).approve(address(POOL), receivedAmount); + POOL.deposit(decodedParams.assetToSwapToList[i], receivedAmount, initiator, 0); uint256 flashLoanDebt = amounts[i].add(premiums[i]); - pullATokenAndRepayFlashLoan(assets[i], initiator, flashLoanDebt); + pullATokenAndRepayFlashLoan( + assets[i], + initiator, + flashLoanDebt, + PermitSignature( + decodedParams.permitParams.deadline[i], + decodedParams.permitParams.v[i], + decodedParams.permitParams.r[i], + decodedParams.permitParams.s[i] + ) + ); } return true; @@ -72,20 +109,32 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { * @param assetToSwapToList List of addresses of the underlying asset to be swap to and deposited * @param amountToSwapList List of amounts to be swapped * @param slippage The max slippage percentage allowed for the swap + * uint256[] deadline List of deadlines for the permit signature + * uint8[] v List of v param for the permit signature + * bytes32[] r List of r param for the permit signature + * bytes32[] s List of s param for the permit signature */ function swapAndDeposit( address[] calldata assetToSwapFromList, address[] calldata assetToSwapToList, uint256[] calldata amountToSwapList, - uint256 slippage + uint256 slippage, + PermitSignature[] calldata permitParams ) external { require( - assetToSwapFromList.length == assetToSwapToList.length && assetToSwapFromList.length == amountToSwapList.length, + assetToSwapFromList.length == assetToSwapToList.length + && assetToSwapFromList.length == amountToSwapList.length + && assetToSwapFromList.length == permitParams.length, 'INCONSISTENT_PARAMS' ); for (uint256 i = 0; i < assetToSwapFromList.length; i++) { - pullAToken(assetToSwapFromList[i], msg.sender, amountToSwapList[i]); + pullAToken( + assetToSwapFromList[i], + msg.sender, + amountToSwapList[i], + permitParams[i] + ); uint256 receivedAmount = swapExactTokensForTokens( assetToSwapFromList[i], @@ -99,4 +148,29 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { POOL.deposit(assetToSwapToList[i], receivedAmount, msg.sender, 0); } } + + /** + * @dev Decodes debt information encoded in flashloan params + * @param params Additional variadic field to include extra params. Expected parameters: + * address[] assetToSwapToList List of the addresses of the reserve to be swapped to and deposited + * uint256 slippage The max slippage percentage allowed for the swap + * uint256[] deadline List of deadlines for the permit signature + * uint256[] deadline List of deadlines for the permit signature + * uint8[] v List of v param for the permit signature + * bytes32[] r List of r param for the permit signature + * bytes32[] s List of s param for the permit signature + * @return SwapParams struct containing decoded params + */ + function _decodeParams(bytes memory params) internal returns (SwapParams memory) { + ( + address[] memory assetToSwapToList, + uint256 slippage, + uint256[] memory deadline, + uint8[] memory v, + bytes32[] memory r, + bytes32[] memory s + ) = abi.decode(params, (address[], uint256, uint256[], uint8[], bytes32[], bytes32[])); + + return SwapParams(assetToSwapToList, slippage, PermitParams(deadline, v, r, s)); + } } diff --git a/contracts/adapters/UniswapRepayAdapter.sol b/contracts/adapters/UniswapRepayAdapter.sol index 755bfb13..d105cb5c 100644 --- a/contracts/adapters/UniswapRepayAdapter.sol +++ b/contracts/adapters/UniswapRepayAdapter.sol @@ -20,6 +20,7 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { LeftoverAction leftOverAction; uint256[] repayAmounts; uint256[] rateModes; + PermitParams permitParams; } constructor( @@ -46,6 +47,10 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { * (1) Direct transfer to user * uint256[] repayAmounts List of amounts of debt to be repaid * uint256[] rateModes List of the rate modes of the debt to be repaid + * uint256[] deadline List of deadlines for the permit signature + * uint8[] v List of v param for the permit signature + * bytes32[] r List of r param for the permit signature + * bytes32[] s List of s param for the permit signature */ function executeOperation( address[] calldata assets, @@ -61,7 +66,11 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { require( assets.length == decodedParams.assetToSwapToList.length && assets.length == decodedParams.repayAmounts.length - && assets.length == decodedParams.rateModes.length, + && assets.length == decodedParams.rateModes.length + && assets.length == decodedParams.permitParams.deadline.length + && assets.length == decodedParams.permitParams.v.length + && assets.length == decodedParams.permitParams.r.length + && assets.length == decodedParams.permitParams.s.length, 'INCONSISTENT_PARAMS'); for (uint256 i = 0; i < assets.length; i++) { @@ -73,7 +82,13 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { decodedParams.rateModes[i], initiator, decodedParams.leftOverAction, - premiums[i] + premiums[i], + PermitSignature( + decodedParams.permitParams.deadline[i], + decodedParams.permitParams.v[i], + decodedParams.permitParams.r[i], + decodedParams.permitParams.s[i] + ) ); } @@ -91,6 +106,7 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { * @param initiator Address of the user * @param leftOverAction enum indicating what to do with the left over balance from the swap * @param premium Fee of the flash loan + * @param permitSignature struct containing the permit signature */ function _swapAndRepay( address assetFrom, @@ -100,7 +116,8 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { uint256 rateMode, address initiator, LeftoverAction leftOverAction, - uint256 premium + uint256 premium, + PermitSignature memory permitSignature ) internal { swapTokensForExactTokens(assetFrom, assetTo, amount, repayAmount); @@ -109,7 +126,7 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { POOL.repay(assetTo, repayAmount, rateMode, initiator); uint256 flashLoanDebt = amount.add(premium); - pullATokenAndRepayFlashLoan(assetFrom, initiator, flashLoanDebt); + pullATokenAndRepayFlashLoan(assetFrom, initiator, flashLoanDebt, permitSignature); // Take care of reserve leftover from the swap sendLeftovers(assetFrom, flashLoanDebt, leftOverAction, initiator); @@ -124,6 +141,10 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { * (1) Direct transfer to user * uint256[] repayAmounts List of amounts of debt to be repaid * uint256[] rateModes List of the rate modes of the debt to be repaid + * uint256[] deadline List of deadlines for the permit signature + * uint8[] v List of v param for the permit signature + * bytes32[] r List of r param for the permit signature + * bytes32[] s List of s param for the permit signature * @return RepayParams struct containing decoded params */ function _decodeParams(bytes memory params) internal returns (RepayParams memory) { @@ -131,9 +152,24 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { address[] memory assetToSwapToList, LeftoverAction leftOverAction, uint256[] memory repayAmounts, - uint256[] memory rateModes - ) = abi.decode(params, (address[], LeftoverAction, uint256[], uint256[])); + uint256[] memory rateModes, + uint256[] memory deadline, + uint8[] memory v, + bytes32[] memory r, + bytes32[] memory s + ) = abi.decode(params, (address[], LeftoverAction, uint256[], uint256[], uint256[], uint8[], bytes32[], bytes32[])); - return RepayParams(assetToSwapToList, leftOverAction, repayAmounts, rateModes); + return RepayParams( + assetToSwapToList, + leftOverAction, + repayAmounts, + rateModes, + PermitParams( + deadline, + v, + r, + s + ) + ); } } diff --git a/contracts/interfaces/IERC20WithPermit.sol b/contracts/interfaces/IERC20WithPermit.sol new file mode 100644 index 00000000..448b383b --- /dev/null +++ b/contracts/interfaces/IERC20WithPermit.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity ^0.6.8; + +import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol'; + +interface IERC20WithPermit is IERC20 { + function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external; +} diff --git a/test/uniswapAdapters.spec.ts b/test/uniswapAdapters.spec.ts index 555f0ef0..3d9e43aa 100644 --- a/test/uniswapAdapters.spec.ts +++ b/test/uniswapAdapters.spec.ts @@ -1,5 +1,10 @@ import {makeSuite, TestEnv} from './helpers/make-suite'; -import {convertToCurrencyDecimals, getContract} from '../helpers/contracts-helpers'; +import { + convertToCurrencyDecimals, + getContract, + buildPermitParams, + getSignatureFromTypedData, +} from '../helpers/contracts-helpers'; import {getMockUniswapRouter} from '../helpers/contracts-getters'; import { deployUniswapLiquiditySwapAdapter, @@ -8,11 +13,13 @@ import { import {MockUniswapV2Router02} from '../types/MockUniswapV2Router02'; import {Zero} from '@ethersproject/constants'; import BigNumber from 'bignumber.js'; -import {evmRevert, evmSnapshot} from '../helpers/misc-utils'; +import {BRE, evmRevert, evmSnapshot} from '../helpers/misc-utils'; import {ethers} from 'ethers'; import {eContractid} from '../helpers/types'; import {AToken} from '../types/AToken'; import {StableDebtToken} from '../types/StableDebtToken'; +import {BUIDLEREVM_CHAINID} from '../helpers/buidler-constants'; +import {MAX_UINT_AMOUNT} from '../helpers/constants'; const {parseEther} = ethers.utils; const {expect} = require('chai'); @@ -121,8 +128,15 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { // 0,5% slippage const params = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256'], - [[dai.address], 50] + ['address[]', 'uint256', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + [ + [dai.address], + 50, + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ] ); await expect( @@ -158,6 +172,102 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); }); + it('should correctly swap tokens with permit', async () => { + const {users, weth, oracle, dai, aDai, aWETH, pool, uniswapLiquiditySwapAdapter} = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + await mockUniswapRouter.setAmountToReturn(expectedDaiAmount); + + // User will swap liquidity 10 aEth to aDai + const liquidityToSwap = parseEther('10'); + const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + + // IMPORTANT: Round down to work equal to solidity to get the correct value for permit call + BigNumber.config({ + ROUNDING_MODE: 1, //round down + }); + + // Subtract the FL fee from the amount to be swapped 0,09% + const flashloanAmountBN = new BigNumber(liquidityToSwap.toString()).div(1.0009); + const flashloanAmount = flashloanAmountBN.toFixed(0); + const flashloanFee = flashloanAmountBN.multipliedBy(9).div(10000); + const amountToPermit = flashloanAmountBN.plus(flashloanFee); + + const chainId = BRE.network.config.chainId || BUIDLEREVM_CHAINID; + const deadline = MAX_UINT_AMOUNT; + const nonce = (await aWETH._nonces(userAddress)).toNumber(); + const msgParams = buildPermitParams( + chainId, + aWETH.address, + '1', + await aWETH.name(), + userAddress, + uniswapLiquiditySwapAdapter.address, + nonce, + deadline, + amountToPermit.toFixed(0).toString() + ); + + const ownerPrivateKey = require('../test-wallets.js').accounts[1].secretKey; + if (!ownerPrivateKey) { + throw new Error('INVALID_OWNER_PK'); + } + + const {v, r, s} = getSignatureFromTypedData(ownerPrivateKey, msgParams); + + // 0,5% slippage + const params = ethers.utils.defaultAbiCoder.encode( + ['address[]', 'uint256', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + [[dai.address], 50, [deadline], [v], [r], [s]] + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapLiquiditySwapAdapter.address, + [weth.address], + [flashloanAmount.toString()], + [0], + userAddress, + params, + 0 + ) + ) + .to.emit(uniswapLiquiditySwapAdapter, 'Swapped') + .withArgs(weth.address, dai.address, flashloanAmount.toString(), expectedDaiAmount); + + const adapterWethBalance = await weth.balanceOf(uniswapLiquiditySwapAdapter.address); + const adapterDaiBalance = await dai.balanceOf(uniswapLiquiditySwapAdapter.address); + const adapterDaiAllowance = await dai.allowance( + uniswapLiquiditySwapAdapter.address, + userAddress + ); + const userADaiBalance = await aDai.balanceOf(userAddress); + const userAEthBalance = await aWETH.balanceOf(userAddress); + + expect(adapterWethBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(adapterDaiAllowance).to.be.eq(Zero); + expect(userADaiBalance).to.be.eq(expectedDaiAmount); + expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); + expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); + + // Restore round up + BigNumber.config({ + ROUNDING_MODE: 0, //round up + }); + }); + it('should revert if inconsistent params', async () => { const {users, weth, oracle, dai, aWETH, pool, uniswapLiquiditySwapAdapter} = testEnv; const user = users[0].signer; @@ -182,8 +292,15 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { // 0,5% slippage const params = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256'], - [[dai.address, weth.address], 50] + ['address[]', 'uint256', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + [ + [dai.address, weth.address], + 50, + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ] ); await expect( @@ -199,6 +316,116 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { 0 ) ).to.be.revertedWith('INCONSISTENT_PARAMS'); + + const params2 = ethers.utils.defaultAbiCoder.encode( + ['address[]', 'uint256', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + [ + [dai.address, weth.address], + 50, + [0, 0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ] + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapLiquiditySwapAdapter.address, + [weth.address], + [flashloanAmount.toString()], + [0], + userAddress, + params2, + 0 + ) + ).to.be.revertedWith('INCONSISTENT_PARAMS'); + + const params3 = ethers.utils.defaultAbiCoder.encode( + ['address[]', 'uint256', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + [ + [dai.address, weth.address], + 50, + [0], + [0, 0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ] + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapLiquiditySwapAdapter.address, + [weth.address], + [flashloanAmount.toString()], + [0], + userAddress, + params3, + 0 + ) + ).to.be.revertedWith('INCONSISTENT_PARAMS'); + + const params4 = ethers.utils.defaultAbiCoder.encode( + ['address[]', 'uint256', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + [ + [dai.address, weth.address], + 50, + [0], + [0], + [ + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000', + ], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ] + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapLiquiditySwapAdapter.address, + [weth.address], + [flashloanAmount.toString()], + [0], + userAddress, + params4, + 0 + ) + ).to.be.revertedWith('INCONSISTENT_PARAMS'); + + const params5 = ethers.utils.defaultAbiCoder.encode( + ['address[]', 'uint256', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + [ + [dai.address, weth.address], + 50, + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + [ + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000', + ], + ] + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapLiquiditySwapAdapter.address, + [weth.address], + [flashloanAmount.toString()], + [0], + userAddress, + params5, + 0 + ) + ).to.be.revertedWith('INCONSISTENT_PARAMS'); }); it('should revert if caller not lending pool', async () => { @@ -225,8 +452,15 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { // 0,5% slippage const params = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256'], - [[dai.address, weth.address], 50] + ['address[]', 'uint256', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + [ + [dai.address], + 50, + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ] ); await expect( @@ -299,8 +533,15 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { // 0,5% slippage const params = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256'], - [[dai.address], 50] + ['address[]', 'uint256', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + [ + [dai.address], + 50, + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ] ); await expect( @@ -359,14 +600,28 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { // 30% slippage const params1 = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256'], - [[dai.address], 3000] + ['address[]', 'uint256', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + [ + [dai.address], + 3000, + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ] ); // 0,05% slippage const params2 = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256'], - [[dai.address], 5] + ['address[]', 'uint256', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + [ + [dai.address], + 5, + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ] ); await expect( @@ -437,7 +692,14 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await expect( uniswapLiquiditySwapAdapter .connect(user) - .swapAndDeposit([weth.address], [dai.address], [amountWETHtoSwap], 50) + .swapAndDeposit([weth.address], [dai.address], [amountWETHtoSwap], 50, [ + { + deadline: 0, + v: 0, + r: '0x0000000000000000000000000000000000000000000000000000000000000000', + s: '0x0000000000000000000000000000000000000000000000000000000000000000', + }, + ]) ) .to.emit(uniswapLiquiditySwapAdapter, 'Swapped') .withArgs(weth.address, dai.address, amountWETHtoSwap.toString(), expectedDaiAmount); @@ -458,6 +720,80 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); }); + + it('should correctly swap tokens using permit', async () => { + const {users, weth, oracle, dai, aDai, aWETH, uniswapLiquiditySwapAdapter} = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + await mockUniswapRouter.setAmountToReturn(expectedDaiAmount); + + // User will swap liquidity 10 aEth to aDai + const liquidityToSwap = parseEther('10'); + const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + + const chainId = BRE.network.config.chainId || BUIDLEREVM_CHAINID; + const deadline = MAX_UINT_AMOUNT; + const nonce = (await aWETH._nonces(userAddress)).toNumber(); + const msgParams = buildPermitParams( + chainId, + aWETH.address, + '1', + await aWETH.name(), + userAddress, + uniswapLiquiditySwapAdapter.address, + nonce, + deadline, + liquidityToSwap.toString() + ); + + const ownerPrivateKey = require('../test-wallets.js').accounts[1].secretKey; + if (!ownerPrivateKey) { + throw new Error('INVALID_OWNER_PK'); + } + + const {v, r, s} = getSignatureFromTypedData(ownerPrivateKey, msgParams); + + await expect( + uniswapLiquiditySwapAdapter + .connect(user) + .swapAndDeposit([weth.address], [dai.address], [amountWETHtoSwap], 50, [ + { + deadline, + v, + r, + s, + }, + ]) + ) + .to.emit(uniswapLiquiditySwapAdapter, 'Swapped') + .withArgs(weth.address, dai.address, amountWETHtoSwap.toString(), expectedDaiAmount); + + const adapterWethBalance = await weth.balanceOf(uniswapLiquiditySwapAdapter.address); + const adapterDaiBalance = await dai.balanceOf(uniswapLiquiditySwapAdapter.address); + const adapterDaiAllowance = await dai.allowance( + uniswapLiquiditySwapAdapter.address, + userAddress + ); + const userADaiBalance = await aDai.balanceOf(userAddress); + const userAEthBalance = await aWETH.balanceOf(userAddress); + + expect(adapterWethBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(adapterDaiAllowance).to.be.eq(Zero); + expect(userADaiBalance).to.be.eq(expectedDaiAmount); + expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); + expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); + }); + it('should revert if inconsistent params', async () => { const {users, weth, dai, uniswapLiquiditySwapAdapter} = testEnv; const user = users[0].signer; @@ -467,19 +803,52 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await expect( uniswapLiquiditySwapAdapter .connect(user) - .swapAndDeposit([weth.address, dai.address], [dai.address], [amountWETHtoSwap], 50) + .swapAndDeposit([weth.address, dai.address], [dai.address], [amountWETHtoSwap], 50, [ + { + deadline: 0, + v: 0, + r: '0x0000000000000000000000000000000000000000000000000000000000000000', + s: '0x0000000000000000000000000000000000000000000000000000000000000000', + }, + ]) ).to.be.revertedWith('INCONSISTENT_PARAMS'); await expect( uniswapLiquiditySwapAdapter .connect(user) - .swapAndDeposit([weth.address], [dai.address, weth.address], [amountWETHtoSwap], 50) + .swapAndDeposit([weth.address], [dai.address, weth.address], [amountWETHtoSwap], 50, [ + { + deadline: 0, + v: 0, + r: '0x0000000000000000000000000000000000000000000000000000000000000000', + s: '0x0000000000000000000000000000000000000000000000000000000000000000', + }, + ]) ).to.be.revertedWith('INCONSISTENT_PARAMS'); await expect( uniswapLiquiditySwapAdapter .connect(user) - .swapAndDeposit([weth.address], [dai.address], [amountWETHtoSwap, amountWETHtoSwap], 50) + .swapAndDeposit( + [weth.address], + [dai.address], + [amountWETHtoSwap, amountWETHtoSwap], + 50, + [ + { + deadline: 0, + v: 0, + r: '0x0000000000000000000000000000000000000000000000000000000000000000', + s: '0x0000000000000000000000000000000000000000000000000000000000000000', + }, + ] + ) + ).to.be.revertedWith('INCONSISTENT_PARAMS'); + + await expect( + uniswapLiquiditySwapAdapter + .connect(user) + .swapAndDeposit([weth.address], [dai.address], [amountWETHtoSwap], 50, []) ).to.be.revertedWith('INCONSISTENT_PARAMS'); }); }); @@ -561,8 +930,26 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount); const params = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256', 'uint256[]', 'uint256[]'], - [[dai.address], 0, [expectedDaiAmount], [1]] + [ + 'address[]', + 'uint256', + 'uint256[]', + 'uint256[]', + 'uint256[]', + 'uint8[]', + 'bytes32[]', + 'bytes32[]', + ], + [ + [dai.address], + 0, + [expectedDaiAmount], + [1], + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ] ); await expect( @@ -594,6 +981,129 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); }); + it('should correctly swap tokens and repay debt with permit', async () => { + const { + users, + pool, + weth, + aWETH, + oracle, + dai, + uniswapRepayAdapter, + helpersContract, + } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); + + const daiStableDebtTokenAddress = ( + await helpersContract.getReserveTokensAddresses(dai.address) + ).stableDebtTokenAddress; + + const daiStableDebtContract = await getContract( + eContractid.StableDebtToken, + daiStableDebtTokenAddress + ); + + const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); + + const liquidityToSwap = amountWETHtoSwap; + const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + + // IMPORTANT: Round down to work equal to solidity to get the correct value for permit call + BigNumber.config({ + ROUNDING_MODE: 1, //round down + }); + + // Subtract the FL fee from the amount to be swapped 0,09% + const flashloanAmountBN = new BigNumber(liquidityToSwap.toString()).div(1.0009); + const flashloanAmount = flashloanAmountBN.toFixed(0); + const flashloanFee = flashloanAmountBN.multipliedBy(9).div(10000); + const amountToPermit = flashloanAmountBN.plus(flashloanFee); + + const chainId = BRE.network.config.chainId || BUIDLEREVM_CHAINID; + const deadline = MAX_UINT_AMOUNT; + const nonce = (await aWETH._nonces(userAddress)).toNumber(); + const msgParams = buildPermitParams( + chainId, + aWETH.address, + '1', + await aWETH.name(), + userAddress, + uniswapRepayAdapter.address, + nonce, + deadline, + amountToPermit.toFixed(0).toString() + ); + + const ownerPrivateKey = require('../test-wallets.js').accounts[1].secretKey; + if (!ownerPrivateKey) { + throw new Error('INVALID_OWNER_PK'); + } + + const {v, r, s} = getSignatureFromTypedData(ownerPrivateKey, msgParams); + + await mockUniswapRouter.connect(user).setAmountToSwap(flashloanAmount); + await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount); + + const params = ethers.utils.defaultAbiCoder.encode( + [ + 'address[]', + 'uint256', + 'uint256[]', + 'uint256[]', + 'uint256[]', + 'uint8[]', + 'bytes32[]', + 'bytes32[]', + ], + [[dai.address], 0, [expectedDaiAmount], [1], [deadline], [v], [r], [s]] + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapRepayAdapter.address, + [weth.address], + [flashloanAmount.toString()], + [0], + userAddress, + params, + 0 + ) + ) + .to.emit(uniswapRepayAdapter, 'Swapped') + .withArgs(weth.address, dai.address, flashloanAmount.toString(), expectedDaiAmount); + + const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); + const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); + const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); + const userAEthBalance = await aWETH.balanceOf(userAddress); + + expect(adapterWethBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount); + expect(userDaiStableDebtAmount).to.be.lt(expectedDaiAmount); + expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); + expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); + + // Restore round up + BigNumber.config({ + ROUNDING_MODE: 0, //round up + }); + }); + it('should revert if inconsistent params', async () => { const {users, pool, weth, aWETH, oracle, dai, uniswapRepayAdapter} = testEnv; const user = users[0].signer; @@ -620,8 +1130,26 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount); const params1 = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256', 'uint256[]', 'uint256[]'], - [[dai.address, weth.address], 0, [expectedDaiAmount], [1]] + [ + 'address[]', + 'uint256', + 'uint256[]', + 'uint256[]', + 'uint256[]', + 'uint8[]', + 'bytes32[]', + 'bytes32[]', + ], + [ + [dai.address, weth.address], + 0, + [expectedDaiAmount], + [1], + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ] ); await expect( @@ -639,8 +1167,26 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { ).to.be.revertedWith('INCONSISTENT_PARAMS'); const params2 = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256', 'uint256[]', 'uint256[]'], - [[dai.address], 0, [expectedDaiAmount, expectedDaiAmount], [1]] + [ + 'address[]', + 'uint256', + 'uint256[]', + 'uint256[]', + 'uint256[]', + 'uint8[]', + 'bytes32[]', + 'bytes32[]', + ], + [ + [dai.address], + 0, + [expectedDaiAmount, expectedDaiAmount], + [1], + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ] ); await expect( @@ -658,8 +1204,26 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { ).to.be.revertedWith('INCONSISTENT_PARAMS'); const params3 = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256', 'uint256[]', 'uint256[]'], - [[dai.address], 0, [expectedDaiAmount], [1, 1]] + [ + 'address[]', + 'uint256', + 'uint256[]', + 'uint256[]', + 'uint256[]', + 'uint8[]', + 'bytes32[]', + 'bytes32[]', + ], + [ + [dai.address], + 0, + [expectedDaiAmount], + [1, 1], + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ] ); await expect( @@ -675,6 +1239,160 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { 0 ) ).to.be.revertedWith('INCONSISTENT_PARAMS'); + + const params4 = ethers.utils.defaultAbiCoder.encode( + [ + 'address[]', + 'uint256', + 'uint256[]', + 'uint256[]', + 'uint256[]', + 'uint8[]', + 'bytes32[]', + 'bytes32[]', + ], + [ + [dai.address], + 0, + [expectedDaiAmount], + [1], + [0, 0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ] + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapRepayAdapter.address, + [weth.address], + [flashloanAmount.toString()], + [0], + userAddress, + params4, + 0 + ) + ).to.be.revertedWith('INCONSISTENT_PARAMS'); + + const params5 = ethers.utils.defaultAbiCoder.encode( + [ + 'address[]', + 'uint256', + 'uint256[]', + 'uint256[]', + 'uint256[]', + 'uint8[]', + 'bytes32[]', + 'bytes32[]', + ], + [ + [dai.address], + 0, + [expectedDaiAmount], + [1], + [0], + [0, 0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ] + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapRepayAdapter.address, + [weth.address], + [flashloanAmount.toString()], + [0], + userAddress, + params5, + 0 + ) + ).to.be.revertedWith('INCONSISTENT_PARAMS'); + + const params6 = ethers.utils.defaultAbiCoder.encode( + [ + 'address[]', + 'uint256', + 'uint256[]', + 'uint256[]', + 'uint256[]', + 'uint8[]', + 'bytes32[]', + 'bytes32[]', + ], + [ + [dai.address], + 0, + [expectedDaiAmount], + [1], + [0], + [0], + [ + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000', + ], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ] + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapRepayAdapter.address, + [weth.address], + [flashloanAmount.toString()], + [0], + userAddress, + params6, + 0 + ) + ).to.be.revertedWith('INCONSISTENT_PARAMS'); + + const params7 = ethers.utils.defaultAbiCoder.encode( + [ + 'address[]', + 'uint256', + 'uint256[]', + 'uint256[]', + 'uint256[]', + 'uint8[]', + 'bytes32[]', + 'bytes32[]', + ], + [ + [dai.address], + 0, + [expectedDaiAmount], + [1], + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + [ + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000', + ], + ] + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapRepayAdapter.address, + [weth.address], + [flashloanAmount.toString()], + [0], + userAddress, + params7, + 0 + ) + ).to.be.revertedWith('INCONSISTENT_PARAMS'); }); it('should revert if caller not lending pool', async () => { @@ -703,8 +1421,26 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount); const params = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256', 'uint256[]', 'uint256[]'], - [[dai.address], 0, [expectedDaiAmount], [1]] + [ + 'address[]', + 'uint256', + 'uint256[]', + 'uint256[]', + 'uint256[]', + 'uint8[]', + 'bytes32[]', + 'bytes32[]', + ], + [ + [dai.address], + 0, + [expectedDaiAmount], + [1], + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ] ); await expect( @@ -749,8 +1485,26 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount); const params = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256', 'uint256[]', 'uint256[]'], - [[dai.address], 0, [expectedDaiAmount], [1]] + [ + 'address[]', + 'uint256', + 'uint256[]', + 'uint256[]', + 'uint256[]', + 'uint8[]', + 'bytes32[]', + 'bytes32[]', + ], + [ + [dai.address], + 0, + [expectedDaiAmount], + [1], + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ] ); await expect( @@ -794,8 +1548,26 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount); const params = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256', 'uint256[]', 'uint256[]'], - [[dai.address], 0, [expectedDaiAmount], [1]] + [ + 'address[]', + 'uint256', + 'uint256[]', + 'uint256[]', + 'uint256[]', + 'uint8[]', + 'bytes32[]', + 'bytes32[]', + ], + [ + [dai.address], + 0, + [expectedDaiAmount], + [1], + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ] ); await expect( @@ -839,8 +1611,26 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount); const params = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256', 'uint256[]', 'uint256[]'], - [[dai.address], 0, [expectedDaiAmount], [1]] + [ + 'address[]', + 'uint256', + 'uint256[]', + 'uint256[]', + 'uint256[]', + 'uint8[]', + 'bytes32[]', + 'bytes32[]', + ], + [ + [dai.address], + 0, + [expectedDaiAmount], + [1], + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ] ); await expect( @@ -911,8 +1701,26 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount); const params = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256', 'uint256[]', 'uint256[]'], - [[dai.address], 0, [expectedDaiAmount], [1]] + [ + 'address[]', + 'uint256', + 'uint256[]', + 'uint256[]', + 'uint256[]', + 'uint8[]', + 'bytes32[]', + 'bytes32[]', + ], + [ + [dai.address], + 0, + [expectedDaiAmount], + [1], + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ] ); await expect( @@ -1002,8 +1810,26 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const wethBalanceBefore = await weth.balanceOf(userAddress); const params = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256', 'uint256[]', 'uint256[]'], - [[dai.address], 1, [expectedDaiAmount], [1]] + [ + 'address[]', + 'uint256', + 'uint256[]', + 'uint256[]', + 'uint256[]', + 'uint8[]', + 'bytes32[]', + 'bytes32[]', + ], + [ + [dai.address], + 1, + [expectedDaiAmount], + [1], + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ] ); await expect( From 94a6a8688cab2c1f0a3033f4dc4defaadd9f493e Mon Sep 17 00:00:00 2001 From: Gerardo Nardelli Date: Tue, 3 Nov 2020 12:22:15 -0300 Subject: [PATCH 09/62] Update liquidity swap adapter params to include min amount to receive --- contracts/adapters/BaseUniswapAdapter.sol | 22 +- .../adapters/UniswapLiquiditySwapAdapter.sol | 30 +-- test/uniswapAdapters.spec.ts | 254 +++++++++++------- 3 files changed, 188 insertions(+), 118 deletions(-) diff --git a/contracts/adapters/BaseUniswapAdapter.sol b/contracts/adapters/BaseUniswapAdapter.sol index 48fe5db9..1b11a8b7 100644 --- a/contracts/adapters/BaseUniswapAdapter.sol +++ b/contracts/adapters/BaseUniswapAdapter.sol @@ -40,10 +40,8 @@ contract BaseUniswapAdapter { bytes32 s; } - // Max slippage percent allow by param + // Max slippage percent allowed uint256 public constant MAX_SLIPPAGE_PERCENT = 3000; // 30% - // Min slippage percent allow by param - uint256 public constant MIN_SLIPPAGE_PERCENT = 10; // 0,1% ILendingPool public immutable POOL; IPriceOracleGetter public immutable ORACLE; @@ -100,18 +98,18 @@ contract BaseUniswapAdapter { } /** - * @dev Swaps an `amountToSwap` of an asset to another + * @dev Swaps an exact `amountToSwap` of an asset to another * @param assetToSwapFrom Origin asset * @param assetToSwapTo Destination asset * @param amountToSwap Exact amount of `assetToSwapFrom` to be swapped - * @param slippage the max slippage percentage allowed for the swap + * @param minAmountOut the min amount of `assetToSwapTo` to be received from the swap * @return the amount received from the swap */ function swapExactTokensForTokens( address assetToSwapFrom, address assetToSwapTo, uint256 amountToSwap, - uint256 slippage + uint256 minAmountOut ) internal returns (uint256) @@ -122,17 +120,19 @@ contract BaseUniswapAdapter { uint256 fromAssetPrice = _getPrice(assetToSwapFrom); uint256 toAssetPrice = _getPrice(assetToSwapTo); - uint256 amountOutMin = amountToSwap + uint256 expectedMinAmountOut = amountToSwap .mul(fromAssetPrice.mul(10**toAssetDecimals)) .div(toAssetPrice.mul(10**fromAssetDecimals)) - .percentMul(PercentageMath.PERCENTAGE_FACTOR.sub(slippage)); + .percentMul(PercentageMath.PERCENTAGE_FACTOR.sub(MAX_SLIPPAGE_PERCENT)); + + require(expectedMinAmountOut < minAmountOut, 'minAmountOut exceed max slippage'); IERC20(assetToSwapFrom).approve(address(UNISWAP_ROUTER), amountToSwap); address[] memory path = new address[](2); path[0] = assetToSwapFrom; path[1] = assetToSwapTo; - uint256[] memory amounts = UNISWAP_ROUTER.swapExactTokensForTokens(amountToSwap, amountOutMin, path, address(this), block.timestamp); + uint256[] memory amounts = UNISWAP_ROUTER.swapExactTokensForTokens(amountToSwap, minAmountOut, path, address(this), block.timestamp); emit Swapped(assetToSwapFrom, assetToSwapTo, amounts[0], amounts[1]); @@ -146,7 +146,7 @@ contract BaseUniswapAdapter { * @param assetToSwapTo Destination asset * @param maxAmountToSwap Max amount of `assetToSwapFrom` allowed to be swapped * @param amountToReceive Exact amount of `assetToSwapTo` to receive - * @return the amount received from the swap + * @return the amount swapped */ function swapTokensForExactTokens( address assetToSwapFrom, @@ -179,7 +179,7 @@ contract BaseUniswapAdapter { emit Swapped(assetToSwapFrom, assetToSwapTo, amounts[0], amounts[1]); - return amounts[1]; + return amounts[0]; } /** diff --git a/contracts/adapters/UniswapLiquiditySwapAdapter.sol b/contracts/adapters/UniswapLiquiditySwapAdapter.sol index 221baada..5291cb85 100644 --- a/contracts/adapters/UniswapLiquiditySwapAdapter.sol +++ b/contracts/adapters/UniswapLiquiditySwapAdapter.sol @@ -17,7 +17,7 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { struct SwapParams { address[] assetToSwapToList; - uint256 slippage; + uint256[] minAmountsToReceive; PermitParams permitParams; } @@ -40,7 +40,7 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { * @param initiator Address of the user * @param params Additional variadic field to include extra params. Expected parameters: * address[] assetToSwapToList List of the addresses of the reserve to be swapped to and deposited - * uint256 slippage The max slippage percentage allowed for the swap + * uint256[] minAmountsToReceive List of min amounts to be received from the swap * uint256[] deadline List of deadlines for the permit signature * uint8[] v List of v param for the permit signature * bytes32[] r List of r param for the permit signature @@ -58,12 +58,8 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { SwapParams memory decodedParams = _decodeParams(params); require( - decodedParams.slippage < MAX_SLIPPAGE_PERCENT && decodedParams.slippage >= MIN_SLIPPAGE_PERCENT, - 'SLIPPAGE_OUT_OF_RANGE' - ); - - require( - decodedParams.assetToSwapToList.length == assets.length + assets.length == decodedParams.assetToSwapToList.length + && assets.length == decodedParams.minAmountsToReceive.length && assets.length == decodedParams.permitParams.deadline.length && assets.length == decodedParams.permitParams.v.length && assets.length == decodedParams.permitParams.r.length @@ -76,7 +72,7 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { assets[i], decodedParams.assetToSwapToList[i], amounts[i], - decodedParams.slippage + decodedParams.minAmountsToReceive[i] ); // Deposit new reserve @@ -108,7 +104,8 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { * @param assetToSwapFromList List of addresses of the underlying asset to be swap from * @param assetToSwapToList List of addresses of the underlying asset to be swap to and deposited * @param amountToSwapList List of amounts to be swapped - * @param slippage The max slippage percentage allowed for the swap + * @param minAmountsToReceive List of min amounts to be received from the swap + * @param permitParams List of struct containing the permit signatures * uint256[] deadline List of deadlines for the permit signature * uint8[] v List of v param for the permit signature * bytes32[] r List of r param for the permit signature @@ -118,12 +115,13 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { address[] calldata assetToSwapFromList, address[] calldata assetToSwapToList, uint256[] calldata amountToSwapList, - uint256 slippage, + uint256[] calldata minAmountsToReceive, PermitSignature[] calldata permitParams ) external { require( assetToSwapFromList.length == assetToSwapToList.length && assetToSwapFromList.length == amountToSwapList.length + && assetToSwapFromList.length == minAmountsToReceive.length && assetToSwapFromList.length == permitParams.length, 'INCONSISTENT_PARAMS' ); @@ -140,7 +138,7 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { assetToSwapFromList[i], assetToSwapToList[i], amountToSwapList[i], - slippage + minAmountsToReceive[i] ); // Deposit new reserve @@ -153,7 +151,7 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { * @dev Decodes debt information encoded in flashloan params * @param params Additional variadic field to include extra params. Expected parameters: * address[] assetToSwapToList List of the addresses of the reserve to be swapped to and deposited - * uint256 slippage The max slippage percentage allowed for the swap + * uint256[] minAmountsToReceive List of min amounts to be received from the swap * uint256[] deadline List of deadlines for the permit signature * uint256[] deadline List of deadlines for the permit signature * uint8[] v List of v param for the permit signature @@ -164,13 +162,13 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { function _decodeParams(bytes memory params) internal returns (SwapParams memory) { ( address[] memory assetToSwapToList, - uint256 slippage, + uint256[] memory minAmountsToReceive, uint256[] memory deadline, uint8[] memory v, bytes32[] memory r, bytes32[] memory s - ) = abi.decode(params, (address[], uint256, uint256[], uint8[], bytes32[], bytes32[])); + ) = abi.decode(params, (address[], uint256[], uint256[], uint8[], bytes32[], bytes32[])); - return SwapParams(assetToSwapToList, slippage, PermitParams(deadline, v, r, s)); + return SwapParams(assetToSwapToList, minAmountsToReceive, PermitParams(deadline, v, r, s)); } } diff --git a/test/uniswapAdapters.spec.ts b/test/uniswapAdapters.spec.ts index 3d9e43aa..259a4ec3 100644 --- a/test/uniswapAdapters.spec.ts +++ b/test/uniswapAdapters.spec.ts @@ -126,12 +126,11 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { // Subtract the FL fee from the amount to be swapped 0,09% const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); - // 0,5% slippage const params = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + ['address[]', 'uint256[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], [ [dai.address], - 50, + [expectedDaiAmount], [0], [0], ['0x0000000000000000000000000000000000000000000000000000000000000000'], @@ -224,10 +223,9 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const {v, r, s} = getSignatureFromTypedData(ownerPrivateKey, msgParams); - // 0,5% slippage const params = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], - [[dai.address], 50, [deadline], [v], [r], [s]] + ['address[]', 'uint256[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + [[dai.address], [expectedDaiAmount], [deadline], [v], [r], [s]] ); await expect( @@ -290,12 +288,11 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { // Subtract the FL fee from the amount to be swapped 0,09% const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); - // 0,5% slippage const params = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + ['address[]', 'uint256[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], [ [dai.address, weth.address], - 50, + [expectedDaiAmount], [0], [0], ['0x0000000000000000000000000000000000000000000000000000000000000000'], @@ -318,10 +315,10 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { ).to.be.revertedWith('INCONSISTENT_PARAMS'); const params2 = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + ['address[]', 'uint256[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], [ [dai.address, weth.address], - 50, + [expectedDaiAmount], [0, 0], [0], ['0x0000000000000000000000000000000000000000000000000000000000000000'], @@ -344,10 +341,10 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { ).to.be.revertedWith('INCONSISTENT_PARAMS'); const params3 = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + ['address[]', 'uint256[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], [ [dai.address, weth.address], - 50, + [expectedDaiAmount], [0], [0, 0], ['0x0000000000000000000000000000000000000000000000000000000000000000'], @@ -370,10 +367,10 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { ).to.be.revertedWith('INCONSISTENT_PARAMS'); const params4 = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + ['address[]', 'uint256[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], [ [dai.address, weth.address], - 50, + [expectedDaiAmount], [0], [0], [ @@ -399,10 +396,10 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { ).to.be.revertedWith('INCONSISTENT_PARAMS'); const params5 = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + ['address[]', 'uint256[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], [ [dai.address, weth.address], - 50, + [expectedDaiAmount], [0], [0], ['0x0000000000000000000000000000000000000000000000000000000000000000'], @@ -426,6 +423,32 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { 0 ) ).to.be.revertedWith('INCONSISTENT_PARAMS'); + + const params6 = ethers.utils.defaultAbiCoder.encode( + ['address[]', 'uint256[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + [ + [dai.address, weth.address], + [expectedDaiAmount, expectedDaiAmount], + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ] + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapLiquiditySwapAdapter.address, + [weth.address], + [flashloanAmount.toString()], + [0], + userAddress, + params6, + 0 + ) + ).to.be.revertedWith('INCONSISTENT_PARAMS'); }); it('should revert if caller not lending pool', async () => { @@ -450,12 +473,11 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { // Subtract the FL fee from the amount to be swapped 0,09% const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); - // 0,5% slippage const params = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + ['address[]', 'uint256[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], [ [dai.address], - 50, + [expectedDaiAmount], [0], [0], ['0x0000000000000000000000000000000000000000000000000000000000000000'], @@ -531,12 +553,11 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { // Subtract the FL fee from the amount to be swapped 0,09% const flashloanAmount = new BigNumber(amountUSDCtoSwap.toString()).div(1.0009).toFixed(0); - // 0,5% slippage const params = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + ['address[]', 'uint256[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], [ [dai.address], - 50, + [expectedDaiAmount], [0], [0], ['0x0000000000000000000000000000000000000000000000000000000000000000'], @@ -574,49 +595,34 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { expect(aDaiBalance).to.be.eq(expectedDaiAmount); }); - it('should revert if slippage param is not inside limits', async () => { - const {users, pool, weth, oracle, dai, aWETH, uniswapLiquiditySwapAdapter} = testEnv; + it('should revert when min amount to receive exceeds the max slippage amount', async () => { + const {users, weth, oracle, dai, aWETH, pool, uniswapLiquiditySwapAdapter} = testEnv; const user = users[0].signer; const userAddress = users[0].address; const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); - await weth.connect(user).mint(amountWETHtoSwap); - await weth.connect(user).transfer(uniswapLiquiditySwapAdapter.address, amountWETHtoSwap); - const daiPrice = await oracle.getAssetPrice(dai.address); const expectedDaiAmount = await convertToCurrencyDecimals( dai.address, new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) ); - await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount); + await mockUniswapRouter.setAmountToReturn(expectedDaiAmount); + const smallExpectedDaiAmount = expectedDaiAmount.div(2); // User will swap liquidity 10 aEth to aDai const liquidityToSwap = parseEther('10'); await aWETH.connect(user).approve(uniswapLiquiditySwapAdapter.address, liquidityToSwap); + // Subtract the FL fee from the amount to be swapped 0,09% const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); - // 30% slippage - const params1 = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + const params = ethers.utils.defaultAbiCoder.encode( + ['address[]', 'uint256[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], [ [dai.address], - 3000, - [0], - [0], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ] - ); - - // 0,05% slippage - const params2 = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], - [ - [dai.address], - 5, + [smallExpectedDaiAmount], [0], [0], ['0x0000000000000000000000000000000000000000000000000000000000000000'], @@ -633,23 +639,10 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { [flashloanAmount.toString()], [0], userAddress, - params1, + params, 0 ) - ).to.be.revertedWith('SLIPPAGE_OUT_OF_RANGE'); - await expect( - pool - .connect(user) - .flashLoan( - uniswapLiquiditySwapAdapter.address, - [weth.address], - [flashloanAmount.toString()], - [0], - userAddress, - params2, - 0 - ) - ).to.be.revertedWith('SLIPPAGE_OUT_OF_RANGE'); + ).to.be.revertedWith('minAmountOut exceed max slippage'); }); }); @@ -690,16 +683,20 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); await expect( - uniswapLiquiditySwapAdapter - .connect(user) - .swapAndDeposit([weth.address], [dai.address], [amountWETHtoSwap], 50, [ + uniswapLiquiditySwapAdapter.connect(user).swapAndDeposit( + [weth.address], + [dai.address], + [amountWETHtoSwap], + [expectedDaiAmount], + [ { deadline: 0, v: 0, r: '0x0000000000000000000000000000000000000000000000000000000000000000', s: '0x0000000000000000000000000000000000000000000000000000000000000000', }, - ]) + ] + ) ) .to.emit(uniswapLiquiditySwapAdapter, 'Swapped') .withArgs(weth.address, dai.address, amountWETHtoSwap.toString(), expectedDaiAmount); @@ -763,16 +760,20 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const {v, r, s} = getSignatureFromTypedData(ownerPrivateKey, msgParams); await expect( - uniswapLiquiditySwapAdapter - .connect(user) - .swapAndDeposit([weth.address], [dai.address], [amountWETHtoSwap], 50, [ + uniswapLiquiditySwapAdapter.connect(user).swapAndDeposit( + [weth.address], + [dai.address], + [amountWETHtoSwap], + [expectedDaiAmount], + [ { deadline, v, r, s, }, - ]) + ] + ) ) .to.emit(uniswapLiquiditySwapAdapter, 'Swapped') .withArgs(weth.address, dai.address, amountWETHtoSwap.toString(), expectedDaiAmount); @@ -795,35 +796,65 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { }); it('should revert if inconsistent params', async () => { - const {users, weth, dai, uniswapLiquiditySwapAdapter} = testEnv; + const {users, weth, dai, uniswapLiquiditySwapAdapter, oracle} = testEnv; const user = users[0].signer; const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); await expect( - uniswapLiquiditySwapAdapter - .connect(user) - .swapAndDeposit([weth.address, dai.address], [dai.address], [amountWETHtoSwap], 50, [ + uniswapLiquiditySwapAdapter.connect(user).swapAndDeposit( + [weth.address, dai.address], + [dai.address], + [amountWETHtoSwap], + [expectedDaiAmount], + [ { deadline: 0, v: 0, r: '0x0000000000000000000000000000000000000000000000000000000000000000', s: '0x0000000000000000000000000000000000000000000000000000000000000000', }, - ]) + ] + ) ).to.be.revertedWith('INCONSISTENT_PARAMS'); await expect( - uniswapLiquiditySwapAdapter - .connect(user) - .swapAndDeposit([weth.address], [dai.address, weth.address], [amountWETHtoSwap], 50, [ + uniswapLiquiditySwapAdapter.connect(user).swapAndDeposit( + [weth.address], + [dai.address, weth.address], + [amountWETHtoSwap], + [expectedDaiAmount], + [ { deadline: 0, v: 0, r: '0x0000000000000000000000000000000000000000000000000000000000000000', s: '0x0000000000000000000000000000000000000000000000000000000000000000', }, - ]) + ] + ) + ).to.be.revertedWith('INCONSISTENT_PARAMS'); + + await expect( + uniswapLiquiditySwapAdapter.connect(user).swapAndDeposit( + [weth.address], + [dai.address], + [amountWETHtoSwap, amountWETHtoSwap], + [expectedDaiAmount], + [ + { + deadline: 0, + v: 0, + r: '0x0000000000000000000000000000000000000000000000000000000000000000', + s: '0x0000000000000000000000000000000000000000000000000000000000000000', + }, + ] + ) ).to.be.revertedWith('INCONSISTENT_PARAMS'); await expect( @@ -832,25 +863,66 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { .swapAndDeposit( [weth.address], [dai.address], - [amountWETHtoSwap, amountWETHtoSwap], - 50, - [ - { - deadline: 0, - v: 0, - r: '0x0000000000000000000000000000000000000000000000000000000000000000', - s: '0x0000000000000000000000000000000000000000000000000000000000000000', - }, - ] + [amountWETHtoSwap], + [expectedDaiAmount], + [] ) ).to.be.revertedWith('INCONSISTENT_PARAMS'); await expect( - uniswapLiquiditySwapAdapter - .connect(user) - .swapAndDeposit([weth.address], [dai.address], [amountWETHtoSwap], 50, []) + uniswapLiquiditySwapAdapter.connect(user).swapAndDeposit( + [weth.address], + [dai.address], + [amountWETHtoSwap], + [expectedDaiAmount, expectedDaiAmount], + [ + { + deadline: 0, + v: 0, + r: '0x0000000000000000000000000000000000000000000000000000000000000000', + s: '0x0000000000000000000000000000000000000000000000000000000000000000', + }, + ] + ) ).to.be.revertedWith('INCONSISTENT_PARAMS'); }); + + it('should revert when min amount to receive exceeds the max slippage amount', async () => { + const {users, weth, oracle, dai, aWETH, uniswapLiquiditySwapAdapter} = testEnv; + const user = users[0].signer; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + await mockUniswapRouter.setAmountToReturn(expectedDaiAmount); + const smallExpectedDaiAmount = expectedDaiAmount.div(2); + + // User will swap liquidity 10 aEth to aDai + const liquidityToSwap = parseEther('10'); + await aWETH.connect(user).approve(uniswapLiquiditySwapAdapter.address, liquidityToSwap); + + await expect( + uniswapLiquiditySwapAdapter.connect(user).swapAndDeposit( + [weth.address], + [dai.address], + [amountWETHtoSwap], + [smallExpectedDaiAmount], + [ + { + deadline: 0, + v: 0, + r: '0x0000000000000000000000000000000000000000000000000000000000000000', + s: '0x0000000000000000000000000000000000000000000000000000000000000000', + }, + ] + ) + ).to.be.revertedWith('minAmountOut exceed max slippage'); + }); }); }); From 847ad3b12a9f008e828530d25e60f6c84482e3f3 Mon Sep 17 00:00:00 2001 From: Gerardo Nardelli Date: Tue, 3 Nov 2020 15:37:06 -0300 Subject: [PATCH 10/62] Add batch swaps unit tests --- .../adapters/UniswapLiquiditySwapAdapter.sol | 2 +- contracts/adapters/UniswapRepayAdapter.sol | 2 +- .../mocks/swap/MockUniswapV2Router02.sol | 30 +- test/uniswapAdapters.spec.ts | 917 +++++++++++++++++- 4 files changed, 905 insertions(+), 46 deletions(-) diff --git a/contracts/adapters/UniswapLiquiditySwapAdapter.sol b/contracts/adapters/UniswapLiquiditySwapAdapter.sol index 5291cb85..365089a8 100644 --- a/contracts/adapters/UniswapLiquiditySwapAdapter.sol +++ b/contracts/adapters/UniswapLiquiditySwapAdapter.sol @@ -159,7 +159,7 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { * bytes32[] s List of s param for the permit signature * @return SwapParams struct containing decoded params */ - function _decodeParams(bytes memory params) internal returns (SwapParams memory) { + function _decodeParams(bytes memory params) internal pure returns (SwapParams memory) { ( address[] memory assetToSwapToList, uint256[] memory minAmountsToReceive, diff --git a/contracts/adapters/UniswapRepayAdapter.sol b/contracts/adapters/UniswapRepayAdapter.sol index d105cb5c..550241ea 100644 --- a/contracts/adapters/UniswapRepayAdapter.sol +++ b/contracts/adapters/UniswapRepayAdapter.sol @@ -147,7 +147,7 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { * bytes32[] s List of s param for the permit signature * @return RepayParams struct containing decoded params */ - function _decodeParams(bytes memory params) internal returns (RepayParams memory) { + function _decodeParams(bytes memory params) internal pure returns (RepayParams memory) { ( address[] memory assetToSwapToList, LeftoverAction leftOverAction, diff --git a/contracts/mocks/swap/MockUniswapV2Router02.sol b/contracts/mocks/swap/MockUniswapV2Router02.sol index ef4c22c4..6d771cda 100644 --- a/contracts/mocks/swap/MockUniswapV2Router02.sol +++ b/contracts/mocks/swap/MockUniswapV2Router02.sol @@ -6,17 +6,17 @@ import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import {MintableERC20} from '../tokens/MintableERC20.sol'; contract MockUniswapV2Router02 is IUniswapV2Router02 { - uint256 internal _amountToReturn; - uint256 internal _amountToSwap; + mapping(address => uint256) internal _amountToReturn; + mapping(address => uint256) internal _amountToSwap; mapping(address => mapping(address => mapping(uint256 => uint256))) internal _amountsIn; mapping(address => mapping(address => mapping(uint256 => uint256))) internal _amountsOut; - function setAmountToReturn(uint256 amount) public { - _amountToReturn = amount; + function setAmountToReturn(address reserve, uint256 amount) public { + _amountToReturn[reserve] = amount; } - function setAmountToSwap(uint256 amount) public { - _amountToSwap = amount; + function setAmountToSwap(address reserve, uint256 amount) public { + _amountToSwap[reserve] = amount; } function swapExactTokensForTokens( @@ -28,29 +28,29 @@ contract MockUniswapV2Router02 is IUniswapV2Router02 { ) external override returns (uint256[] memory amounts) { IERC20(path[0]).transferFrom(msg.sender, address(this), amountIn); - MintableERC20(path[1]).mint(_amountToReturn); - IERC20(path[1]).transfer(to, _amountToReturn); + MintableERC20(path[1]).mint(_amountToReturn[path[0]]); + IERC20(path[1]).transfer(to, _amountToReturn[path[0]]); amounts = new uint[](path.length); amounts[0] = amountIn; - amounts[1] = _amountToReturn; + amounts[1] = _amountToReturn[path[0]]; } function swapTokensForExactTokens( - uint /* amountOut */, + uint amountOut, uint /* amountInMax */, address[] calldata path, address to, uint /* deadline */ ) external override returns (uint256[] memory amounts) { - IERC20(path[0]).transferFrom(msg.sender, address(this), _amountToSwap); + IERC20(path[0]).transferFrom(msg.sender, address(this), _amountToSwap[path[0]]); - MintableERC20(path[1]).mint(_amountToReturn); - IERC20(path[1]).transfer(to, _amountToReturn); + MintableERC20(path[1]).mint(amountOut); + IERC20(path[1]).transfer(to, amountOut); amounts = new uint[](path.length); - amounts[0] = _amountToSwap; - amounts[1] = _amountToReturn; + amounts[0] = _amountToSwap[path[0]]; + amounts[1] = amountOut; } function setAmountOut(uint amountIn, address reserveIn, address reserveOut, uint amountOut) public { diff --git a/test/uniswapAdapters.spec.ts b/test/uniswapAdapters.spec.ts index 259a4ec3..35803467 100644 --- a/test/uniswapAdapters.spec.ts +++ b/test/uniswapAdapters.spec.ts @@ -89,7 +89,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { describe('executeOperation', () => { beforeEach(async () => { - const {users, weth, dai, pool, deployer} = testEnv; + const {users, weth, dai, usdc, pool, deployer} = testEnv; const userAddress = users[0].address; // Provide liquidity @@ -97,6 +97,11 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await dai.approve(pool.address, parseEther('20000')); await pool.deposit(dai.address, parseEther('20000'), deployer.address, 0); + const usdcAmount = await convertToCurrencyDecimals(usdc.address, '10'); + await usdc.mint(usdcAmount); + await usdc.approve(pool.address, usdcAmount); + await pool.deposit(usdc.address, usdcAmount, deployer.address, 0); + // Make a deposit for user await weth.mint(parseEther('100')); await weth.approve(pool.address, parseEther('100')); @@ -116,7 +121,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) ); - await mockUniswapRouter.setAmountToReturn(expectedDaiAmount); + await mockUniswapRouter.setAmountToReturn(weth.address, expectedDaiAmount); // User will swap liquidity 10 aEth to aDai const liquidityToSwap = parseEther('10'); @@ -171,6 +176,281 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); }); + it('should correctly swap and deposit multiple tokens', async () => { + const { + users, + weth, + oracle, + dai, + aDai, + aWETH, + usdc, + pool, + uniswapLiquiditySwapAdapter, + } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmountForEth = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + const amountUSDCtoSwap = await convertToCurrencyDecimals(usdc.address, '10'); + const usdcPrice = await oracle.getAssetPrice(usdc.address); + + const collateralDecimals = (await usdc.decimals()).toString(); + const principalDecimals = (await dai.decimals()).toString(); + + const expectedDaiAmountForUsdc = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountUSDCtoSwap.toString()) + .times( + new BigNumber(usdcPrice.toString()).times(new BigNumber(10).pow(principalDecimals)) + ) + .div( + new BigNumber(daiPrice.toString()).times(new BigNumber(10).pow(collateralDecimals)) + ) + .toFixed(0) + ); + + // Make a deposit for user + await usdc.connect(user).mint(amountUSDCtoSwap); + await usdc.connect(user).approve(pool.address, amountUSDCtoSwap); + await pool.connect(user).deposit(usdc.address, amountUSDCtoSwap, userAddress, 0); + + const aUsdcData = await pool.getReserveData(usdc.address); + const aUsdc = await getContract(eContractid.AToken, aUsdcData.aTokenAddress); + + await mockUniswapRouter.setAmountToReturn(weth.address, expectedDaiAmountForEth); + await mockUniswapRouter.setAmountToReturn(usdc.address, expectedDaiAmountForUsdc); + + await aWETH.connect(user).approve(uniswapLiquiditySwapAdapter.address, amountWETHtoSwap); + const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + await aUsdc.connect(user).approve(uniswapLiquiditySwapAdapter.address, amountUSDCtoSwap); + const userAUsdcBalanceBefore = await aUsdc.balanceOf(userAddress); + + // Subtract the FL fee from the amount to be swapped 0,09% + const wethFlashloanAmount = new BigNumber(amountWETHtoSwap.toString()) + .div(1.0009) + .toFixed(0); + const usdcFlashloanAmount = new BigNumber(amountUSDCtoSwap.toString()) + .div(1.0009) + .toFixed(0); + + const params = ethers.utils.defaultAbiCoder.encode( + ['address[]', 'uint256[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + [ + [dai.address, dai.address], + [expectedDaiAmountForEth, expectedDaiAmountForUsdc], + [0, 0], + [0, 0], + [ + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000', + ], + [ + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000', + ], + ] + ); + + await pool + .connect(user) + .flashLoan( + uniswapLiquiditySwapAdapter.address, + [weth.address, usdc.address], + [wethFlashloanAmount.toString(), usdcFlashloanAmount.toString()], + [0, 0], + userAddress, + params, + 0 + ); + + const adapterWethBalance = await weth.balanceOf(uniswapLiquiditySwapAdapter.address); + const adapterDaiBalance = await dai.balanceOf(uniswapLiquiditySwapAdapter.address); + const adapterDaiAllowance = await dai.allowance( + uniswapLiquiditySwapAdapter.address, + userAddress + ); + const userADaiBalance = await aDai.balanceOf(userAddress); + const userAEthBalance = await aWETH.balanceOf(userAddress); + const userAUsdcBalance = await aUsdc.balanceOf(userAddress); + + expect(adapterWethBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(adapterDaiAllowance).to.be.eq(Zero); + expect(userADaiBalance).to.be.eq(expectedDaiAmountForEth.add(expectedDaiAmountForUsdc)); + expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); + expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(amountWETHtoSwap)); + expect(userAUsdcBalance).to.be.lt(userAUsdcBalanceBefore); + expect(userAUsdcBalance).to.be.gte(userAUsdcBalanceBefore.sub(amountUSDCtoSwap)); + }); + + it('should correctly swap and deposit multiple tokens using permit', async () => { + const { + users, + weth, + oracle, + dai, + aDai, + aWETH, + usdc, + pool, + uniswapLiquiditySwapAdapter, + } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + const chainId = BRE.network.config.chainId || BUIDLEREVM_CHAINID; + const deadline = MAX_UINT_AMOUNT; + + const ownerPrivateKey = require('../test-wallets.js').accounts[1].secretKey; + if (!ownerPrivateKey) { + throw new Error('INVALID_OWNER_PK'); + } + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmountForEth = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + const amountUSDCtoSwap = await convertToCurrencyDecimals(usdc.address, '10'); + const usdcPrice = await oracle.getAssetPrice(usdc.address); + + const collateralDecimals = (await usdc.decimals()).toString(); + const principalDecimals = (await dai.decimals()).toString(); + + const expectedDaiAmountForUsdc = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountUSDCtoSwap.toString()) + .times( + new BigNumber(usdcPrice.toString()).times(new BigNumber(10).pow(principalDecimals)) + ) + .div( + new BigNumber(daiPrice.toString()).times(new BigNumber(10).pow(collateralDecimals)) + ) + .toFixed(0) + ); + + // Make a deposit for user + await usdc.connect(user).mint(amountUSDCtoSwap); + await usdc.connect(user).approve(pool.address, amountUSDCtoSwap); + await pool.connect(user).deposit(usdc.address, amountUSDCtoSwap, userAddress, 0); + + const aUsdcData = await pool.getReserveData(usdc.address); + const aUsdc = await getContract(eContractid.AToken, aUsdcData.aTokenAddress); + + await mockUniswapRouter.setAmountToReturn(weth.address, expectedDaiAmountForEth); + await mockUniswapRouter.setAmountToReturn(usdc.address, expectedDaiAmountForUsdc); + + const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + const userAUsdcBalanceBefore = await aUsdc.balanceOf(userAddress); + + // IMPORTANT: Round down to work equal to solidity to get the correct value for permit call + BigNumber.config({ + ROUNDING_MODE: 1, //round down + }); + + const wethFlashloanAmountBN = new BigNumber(amountWETHtoSwap.toString()).div(1.0009); + const wethFlashloanAmount = wethFlashloanAmountBN.toFixed(0); + const wethFlashloanFee = wethFlashloanAmountBN.multipliedBy(9).div(10000); + const wethAmountToPermit = wethFlashloanAmountBN.plus(wethFlashloanFee).toFixed(0); + + const usdcFlashloanAmountBN = new BigNumber(amountUSDCtoSwap.toString()).div(1.0009); + const usdcFlashloanAmount = usdcFlashloanAmountBN.toFixed(0); + const usdcFlashloanFee = usdcFlashloanAmountBN.multipliedBy(9).div(10000); + const usdcAmountToPermit = usdcFlashloanAmountBN.plus(usdcFlashloanFee).toFixed(0); + + const aWethNonce = (await aWETH._nonces(userAddress)).toNumber(); + const aWethMsgParams = buildPermitParams( + chainId, + aWETH.address, + '1', + await aWETH.name(), + userAddress, + uniswapLiquiditySwapAdapter.address, + aWethNonce, + deadline, + wethAmountToPermit.toString() + ); + const {v: aWETHv, r: aWETHr, s: aWETHs} = getSignatureFromTypedData( + ownerPrivateKey, + aWethMsgParams + ); + + const aUsdcNonce = (await aUsdc._nonces(userAddress)).toNumber(); + const aUsdcMsgParams = buildPermitParams( + chainId, + aUsdc.address, + '1', + await aUsdc.name(), + userAddress, + uniswapLiquiditySwapAdapter.address, + aUsdcNonce, + deadline, + usdcAmountToPermit.toString() + ); + const {v: aUsdcv, r: aUsdcr, s: aUsdcs} = getSignatureFromTypedData( + ownerPrivateKey, + aUsdcMsgParams + ); + + const params = ethers.utils.defaultAbiCoder.encode( + ['address[]', 'uint256[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + [ + [dai.address, dai.address], + [expectedDaiAmountForEth, expectedDaiAmountForUsdc], + [deadline, deadline], + [aWETHv, aUsdcv], + [aWETHr, aUsdcr], + [aWETHs, aUsdcs], + ] + ); + + await pool + .connect(user) + .flashLoan( + uniswapLiquiditySwapAdapter.address, + [weth.address, usdc.address], + [wethFlashloanAmount.toString(), usdcFlashloanAmount.toString()], + [0, 0], + userAddress, + params, + 0 + ); + + const adapterWethBalance = await weth.balanceOf(uniswapLiquiditySwapAdapter.address); + const adapterDaiBalance = await dai.balanceOf(uniswapLiquiditySwapAdapter.address); + const adapterDaiAllowance = await dai.allowance( + uniswapLiquiditySwapAdapter.address, + userAddress + ); + const userADaiBalance = await aDai.balanceOf(userAddress); + const userAEthBalance = await aWETH.balanceOf(userAddress); + const userAUsdcBalance = await aUsdc.balanceOf(userAddress); + + expect(adapterWethBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(adapterDaiAllowance).to.be.eq(Zero); + expect(userADaiBalance).to.be.eq(expectedDaiAmountForEth.add(expectedDaiAmountForUsdc)); + expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); + expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(amountWETHtoSwap)); + expect(userAUsdcBalance).to.be.lt(userAUsdcBalanceBefore); + expect(userAUsdcBalance).to.be.gte(userAUsdcBalanceBefore.sub(amountUSDCtoSwap)); + + // Restore round up + BigNumber.config({ + ROUNDING_MODE: 0, //round up + }); + }); + it('should correctly swap tokens with permit', async () => { const {users, weth, oracle, dai, aDai, aWETH, pool, uniswapLiquiditySwapAdapter} = testEnv; const user = users[0].signer; @@ -184,7 +464,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) ); - await mockUniswapRouter.setAmountToReturn(expectedDaiAmount); + await mockUniswapRouter.setAmountToReturn(weth.address, expectedDaiAmount); // User will swap liquidity 10 aEth to aDai const liquidityToSwap = parseEther('10'); @@ -279,7 +559,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) ); - await mockUniswapRouter.setAmountToReturn(expectedDaiAmount); + await mockUniswapRouter.setAmountToReturn(weth.address, expectedDaiAmount); // User will swap liquidity 10 aEth to aDai const liquidityToSwap = parseEther('10'); @@ -464,7 +744,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) ); - await mockUniswapRouter.setAmountToReturn(expectedDaiAmount); + await mockUniswapRouter.setAmountToReturn(weth.address, expectedDaiAmount); // User will swap liquidity 10 aEth to aDai const liquidityToSwap = parseEther('10'); @@ -544,7 +824,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { .toFixed(0) ); - await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount); + await mockUniswapRouter.connect(user).setAmountToReturn(usdc.address, expectedDaiAmount); const aUsdcData = await pool.getReserveData(usdc.address); const aUsdc = await getContract(eContractid.AToken, aUsdcData.aTokenAddress); @@ -608,7 +888,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) ); - await mockUniswapRouter.setAmountToReturn(expectedDaiAmount); + await mockUniswapRouter.setAmountToReturn(weth.address, expectedDaiAmount); const smallExpectedDaiAmount = expectedDaiAmount.div(2); // User will swap liquidity 10 aEth to aDai @@ -675,7 +955,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) ); - await mockUniswapRouter.setAmountToReturn(expectedDaiAmount); + await mockUniswapRouter.setAmountToReturn(weth.address, expectedDaiAmount); // User will swap liquidity 10 aEth to aDai const liquidityToSwap = parseEther('10'); @@ -731,7 +1011,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) ); - await mockUniswapRouter.setAmountToReturn(expectedDaiAmount); + await mockUniswapRouter.setAmountToReturn(weth.address, expectedDaiAmount); // User will swap liquidity 10 aEth to aDai const liquidityToSwap = parseEther('10'); @@ -899,7 +1179,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) ); - await mockUniswapRouter.setAmountToReturn(expectedDaiAmount); + await mockUniswapRouter.setAmountToReturn(weth.address, expectedDaiAmount); const smallExpectedDaiAmount = expectedDaiAmount.div(2); // User will swap liquidity 10 aEth to aDai @@ -923,6 +1203,241 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { ) ).to.be.revertedWith('minAmountOut exceed max slippage'); }); + + it('should correctly swap tokens and deposit multiple tokens', async () => { + const { + users, + weth, + usdc, + oracle, + dai, + aDai, + aWETH, + uniswapLiquiditySwapAdapter, + pool, + } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmountForEth = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + const amountUSDCtoSwap = await convertToCurrencyDecimals(usdc.address, '10'); + const usdcPrice = await oracle.getAssetPrice(usdc.address); + + const collateralDecimals = (await usdc.decimals()).toString(); + const principalDecimals = (await dai.decimals()).toString(); + + const expectedDaiAmountForUsdc = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountUSDCtoSwap.toString()) + .times( + new BigNumber(usdcPrice.toString()).times(new BigNumber(10).pow(principalDecimals)) + ) + .div( + new BigNumber(daiPrice.toString()).times(new BigNumber(10).pow(collateralDecimals)) + ) + .toFixed(0) + ); + + // Make a deposit for user + await usdc.connect(user).mint(amountUSDCtoSwap); + await usdc.connect(user).approve(pool.address, amountUSDCtoSwap); + await pool.connect(user).deposit(usdc.address, amountUSDCtoSwap, userAddress, 0); + + const aUsdcData = await pool.getReserveData(usdc.address); + const aUsdc = await getContract(eContractid.AToken, aUsdcData.aTokenAddress); + + await mockUniswapRouter.setAmountToReturn(weth.address, expectedDaiAmountForEth); + await mockUniswapRouter.setAmountToReturn(usdc.address, expectedDaiAmountForUsdc); + + await aWETH.connect(user).approve(uniswapLiquiditySwapAdapter.address, amountWETHtoSwap); + const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + await aUsdc.connect(user).approve(uniswapLiquiditySwapAdapter.address, amountUSDCtoSwap); + const userAUsdcBalanceBefore = await aUsdc.balanceOf(userAddress); + + await uniswapLiquiditySwapAdapter.connect(user).swapAndDeposit( + [weth.address, usdc.address], + [dai.address, dai.address], + [amountWETHtoSwap, amountUSDCtoSwap], + [expectedDaiAmountForEth, expectedDaiAmountForUsdc], + [ + { + deadline: 0, + v: 0, + r: '0x0000000000000000000000000000000000000000000000000000000000000000', + s: '0x0000000000000000000000000000000000000000000000000000000000000000', + }, + { + deadline: 0, + v: 0, + r: '0x0000000000000000000000000000000000000000000000000000000000000000', + s: '0x0000000000000000000000000000000000000000000000000000000000000000', + }, + ] + ); + + const adapterWethBalance = await weth.balanceOf(uniswapLiquiditySwapAdapter.address); + const adapterDaiBalance = await dai.balanceOf(uniswapLiquiditySwapAdapter.address); + const adapterDaiAllowance = await dai.allowance( + uniswapLiquiditySwapAdapter.address, + userAddress + ); + const userADaiBalance = await aDai.balanceOf(userAddress); + const userAEthBalance = await aWETH.balanceOf(userAddress); + const userAUsdcBalance = await aUsdc.balanceOf(userAddress); + + expect(adapterWethBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(adapterDaiAllowance).to.be.eq(Zero); + expect(userADaiBalance).to.be.eq(expectedDaiAmountForEth.add(expectedDaiAmountForUsdc)); + expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); + expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(amountWETHtoSwap)); + expect(userAUsdcBalance).to.be.lt(userAUsdcBalanceBefore); + expect(userAUsdcBalance).to.be.gte(userAUsdcBalanceBefore.sub(amountUSDCtoSwap)); + }); + + it('should correctly swap tokens and deposit multiple tokens using permit', async () => { + const { + users, + weth, + usdc, + oracle, + dai, + aDai, + aWETH, + uniswapLiquiditySwapAdapter, + pool, + } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + const chainId = BRE.network.config.chainId || BUIDLEREVM_CHAINID; + const deadline = MAX_UINT_AMOUNT; + + const ownerPrivateKey = require('../test-wallets.js').accounts[1].secretKey; + if (!ownerPrivateKey) { + throw new Error('INVALID_OWNER_PK'); + } + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmountForEth = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + const amountUSDCtoSwap = await convertToCurrencyDecimals(usdc.address, '10'); + const usdcPrice = await oracle.getAssetPrice(usdc.address); + + const collateralDecimals = (await usdc.decimals()).toString(); + const principalDecimals = (await dai.decimals()).toString(); + + const expectedDaiAmountForUsdc = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountUSDCtoSwap.toString()) + .times( + new BigNumber(usdcPrice.toString()).times(new BigNumber(10).pow(principalDecimals)) + ) + .div( + new BigNumber(daiPrice.toString()).times(new BigNumber(10).pow(collateralDecimals)) + ) + .toFixed(0) + ); + + // Make a deposit for user + await usdc.connect(user).mint(amountUSDCtoSwap); + await usdc.connect(user).approve(pool.address, amountUSDCtoSwap); + await pool.connect(user).deposit(usdc.address, amountUSDCtoSwap, userAddress, 0); + + const aUsdcData = await pool.getReserveData(usdc.address); + const aUsdc = await getContract(eContractid.AToken, aUsdcData.aTokenAddress); + + await mockUniswapRouter.setAmountToReturn(weth.address, expectedDaiAmountForEth); + await mockUniswapRouter.setAmountToReturn(usdc.address, expectedDaiAmountForUsdc); + + const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + const userAUsdcBalanceBefore = await aUsdc.balanceOf(userAddress); + + const aWethNonce = (await aWETH._nonces(userAddress)).toNumber(); + const aWethMsgParams = buildPermitParams( + chainId, + aWETH.address, + '1', + await aWETH.name(), + userAddress, + uniswapLiquiditySwapAdapter.address, + aWethNonce, + deadline, + amountWETHtoSwap.toString() + ); + const {v: aWETHv, r: aWETHr, s: aWETHs} = getSignatureFromTypedData( + ownerPrivateKey, + aWethMsgParams + ); + + const aUsdcNonce = (await aUsdc._nonces(userAddress)).toNumber(); + const aUsdcMsgParams = buildPermitParams( + chainId, + aUsdc.address, + '1', + await aUsdc.name(), + userAddress, + uniswapLiquiditySwapAdapter.address, + aUsdcNonce, + deadline, + amountUSDCtoSwap.toString() + ); + const {v: aUsdcv, r: aUsdcr, s: aUsdcs} = getSignatureFromTypedData( + ownerPrivateKey, + aUsdcMsgParams + ); + + await uniswapLiquiditySwapAdapter.connect(user).swapAndDeposit( + [weth.address, usdc.address], + [dai.address, dai.address], + [amountWETHtoSwap, amountUSDCtoSwap], + [expectedDaiAmountForEth, expectedDaiAmountForUsdc], + [ + { + deadline, + v: aWETHv, + r: aWETHr, + s: aWETHs, + }, + { + deadline, + v: aUsdcv, + r: aUsdcr, + s: aUsdcs, + }, + ] + ); + + const adapterWethBalance = await weth.balanceOf(uniswapLiquiditySwapAdapter.address); + const adapterDaiBalance = await dai.balanceOf(uniswapLiquiditySwapAdapter.address); + const adapterDaiAllowance = await dai.allowance( + uniswapLiquiditySwapAdapter.address, + userAddress + ); + const userADaiBalance = await aDai.balanceOf(userAddress); + const userAEthBalance = await aWETH.balanceOf(userAddress); + const userAUsdcBalance = await aUsdc.balanceOf(userAddress); + + expect(adapterWethBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(adapterDaiAllowance).to.be.eq(Zero); + expect(userADaiBalance).to.be.eq(expectedDaiAmountForEth.add(expectedDaiAmountForUsdc)); + expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); + expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(amountWETHtoSwap)); + expect(userAUsdcBalance).to.be.lt(userAUsdcBalanceBefore); + expect(userAUsdcBalance).to.be.gte(userAUsdcBalanceBefore.sub(amountUSDCtoSwap)); + }); }); }); @@ -941,7 +1456,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { describe('executeOperation', () => { beforeEach(async () => { - const {users, weth, dai, pool, deployer} = testEnv; + const {users, weth, dai, usdc, lend, pool, deployer} = testEnv; const userAddress = users[0].address; // Provide liquidity @@ -949,10 +1464,27 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await dai.approve(pool.address, parseEther('20000')); await pool.deposit(dai.address, parseEther('20000'), deployer.address, 0); + const usdcLiquidity = await convertToCurrencyDecimals(usdc.address, '20000'); + await usdc.mint(usdcLiquidity); + await usdc.approve(pool.address, usdcLiquidity); + await pool.deposit(usdc.address, usdcLiquidity, deployer.address, 0); + + await weth.mint(parseEther('100')); + await weth.approve(pool.address, parseEther('100')); + await pool.deposit(weth.address, parseEther('100'), deployer.address, 0); + + await lend.mint(parseEther('1000000')); + await lend.approve(pool.address, parseEther('1000000')); + await pool.deposit(lend.address, parseEther('1000000'), deployer.address, 0); + // Make a deposit for user await weth.mint(parseEther('100')); await weth.approve(pool.address, parseEther('100')); await pool.deposit(weth.address, parseEther('100'), userAddress, 0); + + await lend.mint(parseEther('1000000')); + await lend.approve(pool.address, parseEther('1000000')); + await pool.deposit(lend.address, parseEther('1000000'), userAddress, 0); }); it('should correctly swap tokens and repay debt', async () => { @@ -998,8 +1530,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { // Subtract the FL fee from the amount to be swapped 0,09% const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); - await mockUniswapRouter.connect(user).setAmountToSwap(flashloanAmount); - await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount); + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, flashloanAmount); const params = ethers.utils.defaultAbiCoder.encode( [ @@ -1125,8 +1656,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const {v, r, s} = getSignatureFromTypedData(ownerPrivateKey, msgParams); - await mockUniswapRouter.connect(user).setAmountToSwap(flashloanAmount); - await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount); + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, flashloanAmount); const params = ethers.utils.defaultAbiCoder.encode( [ @@ -1176,6 +1706,342 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { }); }); + it('should correctly swap tokens and repay debt for multiple tokens', async () => { + const { + users, + pool, + weth, + oracle, + dai, + uniswapRepayAdapter, + lend, + usdc, + helpersContract, + aWETH, + } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + const amountLendToSwap = await convertToCurrencyDecimals(lend.address, '1'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmountForEth = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + const lendPrice = await oracle.getAssetPrice(lend.address); + const usdcPrice = await oracle.getAssetPrice(usdc.address); + + const collateralDecimals = (await lend.decimals()).toString(); + const principalDecimals = (await usdc.decimals()).toString(); + + const expectedUsdcAmountForLend = await convertToCurrencyDecimals( + usdc.address, + new BigNumber(amountLendToSwap.toString()) + .times( + new BigNumber(lendPrice.toString()).times(new BigNumber(10).pow(principalDecimals)) + ) + .div( + new BigNumber(usdcPrice.toString()).times(new BigNumber(10).pow(collateralDecimals)) + ) + .toFixed(0) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmountForEth, 1, 0, userAddress); + await pool.connect(user).borrow(usdc.address, expectedUsdcAmountForLend, 1, 0, userAddress); + + const daiStableDebtTokenAddress = ( + await helpersContract.getReserveTokensAddresses(dai.address) + ).stableDebtTokenAddress; + + const daiStableDebtContract = await getContract( + eContractid.StableDebtToken, + daiStableDebtTokenAddress + ); + + const usdcStableDebtTokenAddress = ( + await helpersContract.getReserveTokensAddresses(usdc.address) + ).stableDebtTokenAddress; + + const usdcStableDebtContract = await getContract( + eContractid.StableDebtToken, + usdcStableDebtTokenAddress + ); + + const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); + const userUsdcStableDebtAmountBefore = await usdcStableDebtContract.balanceOf(userAddress); + + // Subtract the FL fee from the amount to be swapped 0,09% + const wethFlashloanAmount = new BigNumber(amountWETHtoSwap.toString()) + .div(1.0009) + .toFixed(0); + const lendFlashloanAmount = new BigNumber(amountLendToSwap.toString()) + .div(1.0009) + .toFixed(0); + + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, wethFlashloanAmount); + await mockUniswapRouter.connect(user).setAmountToSwap(lend.address, lendFlashloanAmount); + + await aWETH.connect(user).approve(uniswapRepayAdapter.address, amountWETHtoSwap); + + const lendData = await pool.getReserveData(lend.address); + const aLend = await getContract(eContractid.AToken, lendData.aTokenAddress); + await aLend.connect(user).approve(uniswapRepayAdapter.address, amountLendToSwap); + + const aWETHBalanceBefore = await aWETH.balanceOf(userAddress); + const aLendBalanceBefore = await aLend.balanceOf(userAddress); + + const params = ethers.utils.defaultAbiCoder.encode( + [ + 'address[]', + 'uint256', + 'uint256[]', + 'uint256[]', + 'uint256[]', + 'uint8[]', + 'bytes32[]', + 'bytes32[]', + ], + [ + [dai.address, usdc.address], + 0, + [expectedDaiAmountForEth, expectedUsdcAmountForLend], + [1, 1], + [0, 0], + [0, 0], + [ + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000', + ], + [ + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000', + ], + ] + ); + + await pool + .connect(user) + .flashLoan( + uniswapRepayAdapter.address, + [weth.address, lend.address], + [wethFlashloanAmount.toString(), lendFlashloanAmount.toString()], + [0, 0], + userAddress, + params, + 0 + ); + + const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); + const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); + const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); + const userUsdcStableDebtAmount = await usdcStableDebtContract.balanceOf(userAddress); + const aWETHBalance = await aWETH.balanceOf(userAddress); + const aLendBalance = await aLend.balanceOf(userAddress); + + expect(adapterWethBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmountForEth); + expect(userDaiStableDebtAmount).to.be.lt(expectedDaiAmountForEth); + expect(userUsdcStableDebtAmountBefore).to.be.gte(expectedUsdcAmountForLend); + expect(userUsdcStableDebtAmount).to.be.lt(expectedUsdcAmountForLend); + expect(aWETHBalance).to.be.lt(aWETHBalanceBefore); + expect(aLendBalance).to.be.lt(aLendBalanceBefore); + }); + + it('should swap tokens and repay debt for multiple tokens using permit', async () => { + const { + users, + pool, + weth, + oracle, + dai, + uniswapRepayAdapter, + lend, + usdc, + helpersContract, + aWETH, + } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + const chainId = BRE.network.config.chainId || BUIDLEREVM_CHAINID; + const deadline = MAX_UINT_AMOUNT; + + const ownerPrivateKey = require('../test-wallets.js').accounts[1].secretKey; + if (!ownerPrivateKey) { + throw new Error('INVALID_OWNER_PK'); + } + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + const amountLendToSwap = await convertToCurrencyDecimals(lend.address, '1'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmountForEth = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + const lendPrice = await oracle.getAssetPrice(lend.address); + const usdcPrice = await oracle.getAssetPrice(usdc.address); + + const collateralDecimals = (await lend.decimals()).toString(); + const principalDecimals = (await usdc.decimals()).toString(); + + const expectedUsdcAmountForLend = await convertToCurrencyDecimals( + usdc.address, + new BigNumber(amountLendToSwap.toString()) + .times( + new BigNumber(lendPrice.toString()).times(new BigNumber(10).pow(principalDecimals)) + ) + .div( + new BigNumber(usdcPrice.toString()).times(new BigNumber(10).pow(collateralDecimals)) + ) + .toFixed(0) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmountForEth, 1, 0, userAddress); + await pool.connect(user).borrow(usdc.address, expectedUsdcAmountForLend, 1, 0, userAddress); + + const daiStableDebtTokenAddress = ( + await helpersContract.getReserveTokensAddresses(dai.address) + ).stableDebtTokenAddress; + + const daiStableDebtContract = await getContract( + eContractid.StableDebtToken, + daiStableDebtTokenAddress + ); + + const usdcStableDebtTokenAddress = ( + await helpersContract.getReserveTokensAddresses(usdc.address) + ).stableDebtTokenAddress; + + const usdcStableDebtContract = await getContract( + eContractid.StableDebtToken, + usdcStableDebtTokenAddress + ); + + const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); + const userUsdcStableDebtAmountBefore = await usdcStableDebtContract.balanceOf(userAddress); + + const lendData = await pool.getReserveData(lend.address); + const aLend = await getContract(eContractid.AToken, lendData.aTokenAddress); + + const aWETHBalanceBefore = await aWETH.balanceOf(userAddress); + const aLendBalanceBefore = await aLend.balanceOf(userAddress); + + // IMPORTANT: Round down to work equal to solidity to get the correct value for permit call + BigNumber.config({ + ROUNDING_MODE: 1, //round down + }); + + const wethFlashloanAmountBN = new BigNumber(amountWETHtoSwap.toString()).div(1.0009); + const wethFlashloanAmount = wethFlashloanAmountBN.toFixed(0); + const wethFlashloanFee = wethFlashloanAmountBN.multipliedBy(9).div(10000); + const wethAmountToPermit = wethFlashloanAmountBN.plus(wethFlashloanFee).toFixed(0); + + const lendFlashloanAmountBN = new BigNumber(amountLendToSwap.toString()).div(1.0009); + const lendFlashloanAmount = lendFlashloanAmountBN.toFixed(0); + const lendFlashloanFee = lendFlashloanAmountBN.multipliedBy(9).div(10000); + const lendAmountToPermit = lendFlashloanAmountBN.plus(lendFlashloanFee).toFixed(0); + + const aWethNonce = (await aWETH._nonces(userAddress)).toNumber(); + const aWethMsgParams = buildPermitParams( + chainId, + aWETH.address, + '1', + await aWETH.name(), + userAddress, + uniswapRepayAdapter.address, + aWethNonce, + deadline, + wethAmountToPermit.toString() + ); + const {v: aWETHv, r: aWETHr, s: aWETHs} = getSignatureFromTypedData( + ownerPrivateKey, + aWethMsgParams + ); + + const aLendNonce = (await aLend._nonces(userAddress)).toNumber(); + const aLendMsgParams = buildPermitParams( + chainId, + aLend.address, + '1', + await aLend.name(), + userAddress, + uniswapRepayAdapter.address, + aLendNonce, + deadline, + lendAmountToPermit.toString() + ); + const {v: aLendv, r: aLendr, s: aLends} = getSignatureFromTypedData( + ownerPrivateKey, + aLendMsgParams + ); + + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, wethFlashloanAmount); + await mockUniswapRouter.connect(user).setAmountToSwap(lend.address, lendFlashloanAmount); + + const params = ethers.utils.defaultAbiCoder.encode( + [ + 'address[]', + 'uint256', + 'uint256[]', + 'uint256[]', + 'uint256[]', + 'uint8[]', + 'bytes32[]', + 'bytes32[]', + ], + [ + [dai.address, usdc.address], + 0, + [expectedDaiAmountForEth, expectedUsdcAmountForLend], + [1, 1], + [deadline, deadline], + [aWETHv, aLendv], + [aWETHr, aLendr], + [aWETHs, aLends], + ] + ); + + await pool + .connect(user) + .flashLoan( + uniswapRepayAdapter.address, + [weth.address, lend.address], + [wethFlashloanAmount.toString(), lendFlashloanAmount.toString()], + [0, 0], + userAddress, + params, + 0 + ); + + const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); + const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); + const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); + const userUsdcStableDebtAmount = await usdcStableDebtContract.balanceOf(userAddress); + const aWETHBalance = await aWETH.balanceOf(userAddress); + const aLendBalance = await aLend.balanceOf(userAddress); + + expect(adapterWethBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmountForEth); + expect(userDaiStableDebtAmount).to.be.lt(expectedDaiAmountForEth); + expect(userUsdcStableDebtAmountBefore).to.be.gte(expectedUsdcAmountForLend); + expect(userUsdcStableDebtAmount).to.be.lt(expectedUsdcAmountForLend); + expect(aWETHBalance).to.be.lt(aWETHBalanceBefore); + expect(aLendBalance).to.be.lt(aLendBalanceBefore); + + // Restore round up + BigNumber.config({ + ROUNDING_MODE: 0, //round up + }); + }); + it('should revert if inconsistent params', async () => { const {users, pool, weth, aWETH, oracle, dai, uniswapRepayAdapter} = testEnv; const user = users[0].signer; @@ -1198,8 +2064,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { // Subtract the FL fee from the amount to be swapped 0,09% const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); - await mockUniswapRouter.connect(user).setAmountToSwap(flashloanAmount); - await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount); + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, flashloanAmount); const params1 = ethers.utils.defaultAbiCoder.encode( [ @@ -1489,8 +2354,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { // Subtract the FL fee from the amount to be swapped 0,09% const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); - await mockUniswapRouter.connect(user).setAmountToSwap(flashloanAmount); - await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount); + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, flashloanAmount); const params = ethers.utils.defaultAbiCoder.encode( [ @@ -1553,8 +2417,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { // Subtract the FL fee from the amount to be swapped 0,09% const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); - await mockUniswapRouter.connect(user).setAmountToSwap(flashloanAmount); - await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount); + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, flashloanAmount); const params = ethers.utils.defaultAbiCoder.encode( [ @@ -1616,8 +2479,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { // Subtract the FL fee from the amount to be swapped 0,09% const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); - await mockUniswapRouter.connect(user).setAmountToSwap(flashloanAmount); - await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount); + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, flashloanAmount); const params = ethers.utils.defaultAbiCoder.encode( [ @@ -1679,8 +2541,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const bigMaxAmountToSwap = amountWETHtoSwap.mul(2); const flashloanAmount = new BigNumber(bigMaxAmountToSwap.toString()).div(1.0009).toFixed(0); - await mockUniswapRouter.connect(user).setAmountToSwap(flashloanAmount); - await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount); + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, flashloanAmount); const params = ethers.utils.defaultAbiCoder.encode( [ @@ -1769,8 +2630,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const leftOverWeth = new BigNumber(flashloanAmount).minus(actualWEthSwapped); - await mockUniswapRouter.connect(user).setAmountToSwap(actualWEthSwapped); - await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount); + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, actualWEthSwapped); const params = ethers.utils.defaultAbiCoder.encode( [ @@ -1876,8 +2736,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const leftOverWeth = new BigNumber(flashloanAmount).minus(actualWEthSwapped); - await mockUniswapRouter.connect(user).setAmountToSwap(actualWEthSwapped); - await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount); + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, actualWEthSwapped); const wethBalanceBefore = await weth.balanceOf(userAddress); From 5d9cd6ebd1ce2dbd96e1c19ab6a17178f9ad8a21 Mon Sep 17 00:00:00 2001 From: Gerardo Nardelli Date: Wed, 4 Nov 2020 16:51:21 -0300 Subject: [PATCH 11/62] Update getAmountsOut to return prices --- contracts/adapters/BaseUniswapAdapter.sol | 82 ++++++++++++++--- test/uniswapAdapters.spec.ts | 107 ++++++++++++++++++++-- 2 files changed, 166 insertions(+), 23 deletions(-) diff --git a/contracts/adapters/BaseUniswapAdapter.sol b/contracts/adapters/BaseUniswapAdapter.sol index 1b11a8b7..2e52742b 100644 --- a/contracts/adapters/BaseUniswapAdapter.sol +++ b/contracts/adapters/BaseUniswapAdapter.sol @@ -42,6 +42,10 @@ contract BaseUniswapAdapter { // Max slippage percent allowed uint256 public constant MAX_SLIPPAGE_PERCENT = 3000; // 30% + // FLash Loan fee set in lending pool + uint256 public constant FLASHLOAN_PREMIUM_TOTAL = 9; + // USD oracle asset address + address public constant USD_ADDRESS = 0x10F7Fc1F91Ba351f9C629c5947AD69bD03C05b96; ILendingPool public immutable POOL; IPriceOracleGetter public immutable ORACLE; @@ -56,24 +60,39 @@ contract BaseUniswapAdapter { } /** - * @dev Given an input asset amount, returns the maximum output amount of the other asset + * @dev Given an input asset amount, returns the maximum output amount of the other asset and the prices * @param amountIn Amount of reserveIn * @param reserveIn Address of the asset to be swap from * @param reserveOut Address of the asset to be swap to - * @return uint256 amountOut + * @return uint256 Amount out fo the reserveOut + * @return uint256 The price of out amount denominated in the reserveIn currency (18 decimals) + * @return uint256 In amount of reserveIn value denominated in USD (8 decimals) + * @return uint256 Out amount of reserveOut value denominated in USD (8 decimals) */ - function getAmountOut(uint256 amountIn, address reserveIn, address reserveOut) - public - view - returns (uint256) + function getAmountsOut(uint256 amountIn, address reserveIn, address reserveOut) + external + view + returns (uint256, uint256, uint256, uint256) { - address[] memory path = new address[](2); - path[0] = reserveIn; - path[1] = reserveOut; + // Subtract flash loan fee + uint256 finalAmountIn = amountIn.sub(amountIn.mul(FLASHLOAN_PREMIUM_TOTAL).div(10000)); - uint256[] memory amounts = UNISWAP_ROUTER.getAmountsOut(amountIn, path); + uint256 amountOut = _getAmountsOut(reserveIn, reserveOut, finalAmountIn); - return amounts[1]; + uint256 reserveInDecimals = _getDecimals(reserveIn); + uint256 reserveOutDecimals = _getDecimals(reserveOut); + + uint256 outPerInPrice = finalAmountIn + .mul(10**18) + .mul(10**reserveOutDecimals) + .div(amountOut.mul(10**reserveInDecimals)); + + return ( + amountOut, + outPerInPrice, + _calcUsdValue(reserveIn, amountIn, reserveInDecimals), + _calcUsdValue(reserveOut, amountOut, reserveOutDecimals) + ); } /** @@ -84,9 +103,9 @@ contract BaseUniswapAdapter { * @return uint256 amountIn */ function getAmountIn(uint256 amountOut, address reserveIn, address reserveOut) - public - view - returns (uint256) + external + view + returns (uint256) { address[] memory path = new address[](2); path[0] = reserveIn; @@ -294,4 +313,39 @@ contract BaseUniswapAdapter { return !(uint256(signature.deadline) == uint256(signature.v) && uint256(signature.deadline) == 0); } + + /** + * @dev Calculates the value denominated in USD + * @param reserve Address of the reserve + * @param amount Amount of the reserve + * @param decimals Decimals of the reserve + * @return whether or not permit should be called + */ + function _calcUsdValue(address reserve, uint256 amount, uint256 decimals) internal view returns (uint256) { + uint256 ethUsdPrice = _getPrice(USD_ADDRESS); + uint256 reservePrice = _getPrice(reserve); + + return amount + .mul(reservePrice) + .div(10**decimals) + .mul(ethUsdPrice) + .div(10**18); + } + + /** + * @dev Given an input asset amount, returns the maximum output amount of the other asset + * @param reserveIn Address of the asset to be swap from + * @param reserveOut Address of the asset to be swap to + * @param amountIn Amount of reserveIn + * @return the output amount + */ + function _getAmountsOut(address reserveIn, address reserveOut, uint256 amountIn) internal view returns (uint256) { + address[] memory path = new address[](2); + path[0] = reserveIn; + path[1] = reserveOut; + + uint256[] memory amounts = UNISWAP_ROUTER.getAmountsOut(amountIn, path); + + return amounts[1]; + } } diff --git a/test/uniswapAdapters.spec.ts b/test/uniswapAdapters.spec.ts index 35803467..1ebc21f8 100644 --- a/test/uniswapAdapters.spec.ts +++ b/test/uniswapAdapters.spec.ts @@ -19,7 +19,7 @@ import {eContractid} from '../helpers/types'; import {AToken} from '../types/AToken'; import {StableDebtToken} from '../types/StableDebtToken'; import {BUIDLEREVM_CHAINID} from '../helpers/buidler-constants'; -import {MAX_UINT_AMOUNT} from '../helpers/constants'; +import {MAX_UINT_AMOUNT, USD_ADDRESS} from '../helpers/constants'; const {parseEther} = ethers.utils; const {expect} = require('chai'); @@ -41,17 +41,106 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { }); describe('BaseUniswapAdapter', () => { - describe('getAmountOut', () => { - it('should return the estimated amountOut for the asset swap', async () => { - const {weth, dai, uniswapLiquiditySwapAdapter} = testEnv; + describe('getAmountsOut', () => { + it('should return the estimated amountOut and prices for the asset swap', async () => { + const {weth, dai, uniswapLiquiditySwapAdapter, oracle} = testEnv; + const amountIn = parseEther('1'); - const amountOut = parseEther('2'); + const flashloanPremium = amountIn.mul(9).div(10000); + const amountToSwap = amountIn.sub(flashloanPremium); - await mockUniswapRouter.setAmountOut(amountIn, weth.address, dai.address, amountOut); + const wethPrice = await oracle.getAssetPrice(weth.address); + const daiPrice = await oracle.getAssetPrice(dai.address); + const usdPrice = await oracle.getAssetPrice(USD_ADDRESS); - expect( - await uniswapLiquiditySwapAdapter.getAmountOut(amountIn, weth.address, dai.address) - ).to.be.eq(amountOut); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountToSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + const outPerInPrice = amountToSwap + .mul(parseEther('1')) + .mul(parseEther('1')) + .div(expectedDaiAmount.mul(parseEther('1'))); + const ethUsdValue = amountIn + .mul(wethPrice) + .div(parseEther('1')) + .mul(usdPrice) + .div(parseEther('1')); + const daiUsdValue = expectedDaiAmount + .mul(daiPrice) + .div(parseEther('1')) + .mul(usdPrice) + .div(parseEther('1')); + + await mockUniswapRouter.setAmountOut( + amountToSwap, + weth.address, + dai.address, + expectedDaiAmount + ); + + const result = await uniswapLiquiditySwapAdapter.getAmountsOut( + amountIn, + weth.address, + dai.address + ); + + expect(result['0']).to.be.eq(expectedDaiAmount); + expect(result['1']).to.be.eq(outPerInPrice); + expect(result['2']).to.be.eq(ethUsdValue); + expect(result['3']).to.be.eq(daiUsdValue); + }); + it('should work correctly with different decimals', async () => { + const {lend, usdc, uniswapLiquiditySwapAdapter, oracle} = testEnv; + + const amountIn = parseEther('10'); + const flashloanPremium = amountIn.mul(9).div(10000); + const amountToSwap = amountIn.sub(flashloanPremium); + + const lendPrice = await oracle.getAssetPrice(lend.address); + const usdcPrice = await oracle.getAssetPrice(usdc.address); + const usdPrice = await oracle.getAssetPrice(USD_ADDRESS); + + const expectedUSDCAmount = await convertToCurrencyDecimals( + usdc.address, + new BigNumber(amountToSwap.toString()).div(usdcPrice.toString()).toFixed(0) + ); + + const outPerInPrice = amountToSwap + .mul(parseEther('1')) + .mul('1000000') + .div(expectedUSDCAmount.mul(parseEther('1'))); + + const lendUsdValue = amountIn + .mul(lendPrice) + .div(parseEther('1')) + .mul(usdPrice) + .div(parseEther('1')); + + const usdcUsdValue = expectedUSDCAmount + .mul(usdcPrice) + .div('1000000') + .mul(usdPrice) + .div(parseEther('1')); + + await mockUniswapRouter.setAmountOut( + amountToSwap, + lend.address, + usdc.address, + expectedUSDCAmount + ); + + const result = await uniswapLiquiditySwapAdapter.getAmountsOut( + amountIn, + lend.address, + usdc.address + ); + + expect(result['0']).to.be.eq(expectedUSDCAmount); + expect(result['1']).to.be.eq(outPerInPrice); + expect(result['2']).to.be.eq(lendUsdValue); + expect(result['3']).to.be.eq(usdcUsdValue); }); }); From 48b9a603a79678f90a30de6bc03ff3370a1dcd42 Mon Sep 17 00:00:00 2001 From: Gerardo Nardelli Date: Thu, 5 Nov 2020 11:33:11 -0300 Subject: [PATCH 12/62] Update getAmountsIn to return prices --- contracts/adapters/BaseUniswapAdapter.sol | 119 ++++++++++++++++------ test/uniswapAdapters.spec.ts | 96 +++++++++++++++-- 2 files changed, 176 insertions(+), 39 deletions(-) diff --git a/contracts/adapters/BaseUniswapAdapter.sol b/contracts/adapters/BaseUniswapAdapter.sol index 2e52742b..9dbdfbdf 100644 --- a/contracts/adapters/BaseUniswapAdapter.sol +++ b/contracts/adapters/BaseUniswapAdapter.sol @@ -40,6 +40,13 @@ contract BaseUniswapAdapter { bytes32 s; } + struct AmountCalc { + uint256 calculatedAmount; + uint256 relativePrice; + uint256 amountInUsd; + uint256 amountOutUsd; + } + // Max slippage percent allowed uint256 public constant MAX_SLIPPAGE_PERCENT = 3000; // 30% // FLash Loan fee set in lending pool @@ -64,7 +71,7 @@ contract BaseUniswapAdapter { * @param amountIn Amount of reserveIn * @param reserveIn Address of the asset to be swap from * @param reserveOut Address of the asset to be swap to - * @return uint256 Amount out fo the reserveOut + * @return uint256 Amount out of the reserveOut * @return uint256 The price of out amount denominated in the reserveIn currency (18 decimals) * @return uint256 In amount of reserveIn value denominated in USD (8 decimals) * @return uint256 Out amount of reserveOut value denominated in USD (8 decimals) @@ -74,46 +81,39 @@ contract BaseUniswapAdapter { view returns (uint256, uint256, uint256, uint256) { - // Subtract flash loan fee - uint256 finalAmountIn = amountIn.sub(amountIn.mul(FLASHLOAN_PREMIUM_TOTAL).div(10000)); - - uint256 amountOut = _getAmountsOut(reserveIn, reserveOut, finalAmountIn); - - uint256 reserveInDecimals = _getDecimals(reserveIn); - uint256 reserveOutDecimals = _getDecimals(reserveOut); - - uint256 outPerInPrice = finalAmountIn - .mul(10**18) - .mul(10**reserveOutDecimals) - .div(amountOut.mul(10**reserveInDecimals)); + AmountCalc memory results = _getAmountsOut(reserveIn, reserveOut, amountIn); return ( - amountOut, - outPerInPrice, - _calcUsdValue(reserveIn, amountIn, reserveInDecimals), - _calcUsdValue(reserveOut, amountOut, reserveOutDecimals) + results.calculatedAmount, + results.relativePrice, + results.amountInUsd, + results.amountOutUsd ); } /** - * @dev Returns the minimum input asset amount required to buy the given output asset amount + * @dev Returns the minimum input asset amount required to buy the given output asset amount and the prices * @param amountOut Amount of reserveOut * @param reserveIn Address of the asset to be swap from * @param reserveOut Address of the asset to be swap to - * @return uint256 amountIn + * @return uint256 Amount in of the reserveIn + * @return uint256 The price of in amount denominated in the reserveOut currency (18 decimals) + * @return uint256 In amount of reserveIn value denominated in USD (8 decimals) + * @return uint256 Out amount of reserveOut value denominated in USD (8 decimals) */ - function getAmountIn(uint256 amountOut, address reserveIn, address reserveOut) + function getAmountsIn(uint256 amountOut, address reserveIn, address reserveOut) external view - returns (uint256) + returns (uint256, uint256, uint256, uint256) { - address[] memory path = new address[](2); - path[0] = reserveIn; - path[1] = reserveOut; + AmountCalc memory results = _getAmountsIn(reserveIn, reserveOut, amountOut); - uint256[] memory amounts = UNISWAP_ROUTER.getAmountsIn(amountOut, path); - - return amounts[0]; + return ( + results.calculatedAmount, + results.relativePrice, + results.amountInUsd, + results.amountOutUsd + ); } /** @@ -337,15 +337,72 @@ contract BaseUniswapAdapter { * @param reserveIn Address of the asset to be swap from * @param reserveOut Address of the asset to be swap to * @param amountIn Amount of reserveIn - * @return the output amount + * @return Struct containing the following information: + * uint256 Amount out of the reserveOut + * uint256 The price of out amount denominated in the reserveIn currency (18 decimals) + * uint256 In amount of reserveIn value denominated in USD (8 decimals) + * uint256 Out amount of reserveOut value denominated in USD (8 decimals) */ - function _getAmountsOut(address reserveIn, address reserveOut, uint256 amountIn) internal view returns (uint256) { + function _getAmountsOut(address reserveIn, address reserveOut, uint256 amountIn) internal view returns (AmountCalc memory) { + // Subtract flash loan fee + uint256 finalAmountIn = amountIn.sub(amountIn.mul(FLASHLOAN_PREMIUM_TOTAL).div(10000)); + address[] memory path = new address[](2); path[0] = reserveIn; path[1] = reserveOut; - uint256[] memory amounts = UNISWAP_ROUTER.getAmountsOut(amountIn, path); + uint256[] memory amounts = UNISWAP_ROUTER.getAmountsOut(finalAmountIn, path); - return amounts[1]; + uint256 reserveInDecimals = _getDecimals(reserveIn); + uint256 reserveOutDecimals = _getDecimals(reserveOut); + + uint256 outPerInPrice = finalAmountIn + .mul(10**18) + .mul(10**reserveOutDecimals) + .div(amounts[1].mul(10**reserveInDecimals)); + + return AmountCalc( + amounts[1], + outPerInPrice, + _calcUsdValue(reserveIn, amountIn, reserveInDecimals), + _calcUsdValue(reserveOut, amounts[1], reserveOutDecimals) + ); + } + + /** + * @dev Returns the minimum input asset amount required to buy the given output asset amount + * @param reserveIn Address of the asset to be swap from + * @param reserveOut Address of the asset to be swap to + * @param amountOut Amount of reserveOut + * @return Struct containing the following information: + * uint256 Amount in of the reserveIn + * uint256 The price of in amount denominated in the reserveOut currency (18 decimals) + * uint256 In amount of reserveIn value denominated in USD (8 decimals) + * uint256 Out amount of reserveOut value denominated in USD (8 decimals) + */ + function _getAmountsIn(address reserveIn, address reserveOut, uint256 amountOut) internal view returns (AmountCalc memory) { + address[] memory path = new address[](2); + path[0] = reserveIn; + path[1] = reserveOut; + + uint256[] memory amounts = UNISWAP_ROUTER.getAmountsIn(amountOut, path); + + // Subtract flash loan fee + uint256 finalAmountIn = amounts[0].sub(amounts[0].mul(FLASHLOAN_PREMIUM_TOTAL).div(10000)); + + uint256 reserveInDecimals = _getDecimals(reserveIn); + uint256 reserveOutDecimals = _getDecimals(reserveOut); + + uint256 inPerOutPrice = amountOut + .mul(10**18) + .mul(10**reserveInDecimals) + .div(finalAmountIn.mul(10**reserveOutDecimals)); + + return AmountCalc( + finalAmountIn, + inPerOutPrice, + _calcUsdValue(reserveIn, finalAmountIn, reserveInDecimals), + _calcUsdValue(reserveOut, amountOut, reserveOutDecimals) + ); } } diff --git a/test/uniswapAdapters.spec.ts b/test/uniswapAdapters.spec.ts index 1ebc21f8..7dc7f03c 100644 --- a/test/uniswapAdapters.spec.ts +++ b/test/uniswapAdapters.spec.ts @@ -109,7 +109,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const outPerInPrice = amountToSwap .mul(parseEther('1')) - .mul('1000000') + .mul('1000000') // usdc 6 decimals .div(expectedUSDCAmount.mul(parseEther('1'))); const lendUsdValue = amountIn @@ -120,7 +120,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const usdcUsdValue = expectedUSDCAmount .mul(usdcPrice) - .div('1000000') + .div('1000000') // usdc 6 decimals .mul(usdPrice) .div(parseEther('1')); @@ -144,17 +144,97 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { }); }); - describe('getAmountIn', () => { + describe('getAmountsIn', () => { it('should return the estimated required amountIn for the asset swap', async () => { - const {weth, dai, uniswapLiquiditySwapAdapter} = testEnv; + const {weth, dai, uniswapLiquiditySwapAdapter, oracle} = testEnv; + const amountIn = parseEther('1'); - const amountOut = parseEther('2'); + const flashloanPremium = amountIn.mul(9).div(10000); + const amountToSwap = amountIn.sub(flashloanPremium); + + const wethPrice = await oracle.getAssetPrice(weth.address); + const daiPrice = await oracle.getAssetPrice(dai.address); + const usdPrice = await oracle.getAssetPrice(USD_ADDRESS); + + const amountOut = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountIn.toString()).div(daiPrice.toString()).toFixed(0) + ); + + const inPerOutPrice = amountOut + .mul(parseEther('1')) + .mul(parseEther('1')) + .div(amountToSwap.mul(parseEther('1'))); + + const ethUsdValue = amountToSwap + .mul(wethPrice) + .div(parseEther('1')) + .mul(usdPrice) + .div(parseEther('1')); + const daiUsdValue = amountOut + .mul(daiPrice) + .div(parseEther('1')) + .mul(usdPrice) + .div(parseEther('1')); await mockUniswapRouter.setAmountIn(amountOut, weth.address, dai.address, amountIn); - expect( - await uniswapLiquiditySwapAdapter.getAmountIn(amountOut, weth.address, dai.address) - ).to.be.eq(amountIn); + const result = await uniswapLiquiditySwapAdapter.getAmountsIn( + amountOut, + weth.address, + dai.address + ); + + expect(result['0']).to.be.eq(amountToSwap); + expect(result['1']).to.be.eq(inPerOutPrice); + expect(result['2']).to.be.eq(ethUsdValue); + expect(result['3']).to.be.eq(daiUsdValue); + }); + it('should work correctly with different decimals', async () => { + const {lend, usdc, uniswapLiquiditySwapAdapter, oracle} = testEnv; + + const amountIn = parseEther('10'); + const flashloanPremium = amountIn.mul(9).div(10000); + const amountToSwap = amountIn.sub(flashloanPremium); + + const lendPrice = await oracle.getAssetPrice(lend.address); + const usdcPrice = await oracle.getAssetPrice(usdc.address); + const usdPrice = await oracle.getAssetPrice(USD_ADDRESS); + + const amountOut = await convertToCurrencyDecimals( + usdc.address, + new BigNumber(amountToSwap.toString()).div(usdcPrice.toString()).toFixed(0) + ); + + const inPerOutPrice = amountOut + .mul(parseEther('1')) + .mul(parseEther('1')) + .div(amountToSwap.mul('1000000')); // usdc 6 decimals + + const lendUsdValue = amountToSwap + .mul(lendPrice) + .div(parseEther('1')) + .mul(usdPrice) + .div(parseEther('1')); + + const usdcUsdValue = amountOut + .mul(usdcPrice) + .div('1000000') // usdc 6 decimals + .mul(usdPrice) + .div(parseEther('1')); + + await mockUniswapRouter.setAmountIn(amountOut, lend.address, usdc.address, amountIn); + + const result = await uniswapLiquiditySwapAdapter.getAmountsIn( + amountOut, + lend.address, + usdc.address + ); + + expect(result['0']).to.be.eq(amountToSwap); + expect(result['1']).to.be.eq(inPerOutPrice); + expect(result['2']).to.be.eq(lendUsdValue); + expect(result['3']).to.be.eq(usdcUsdValue); }); }); }); From 20bbae88d39929a84d2a0374007dea8e8346da40 Mon Sep 17 00:00:00 2001 From: Gerardo Nardelli Date: Thu, 5 Nov 2020 16:49:55 -0300 Subject: [PATCH 13/62] Add swapAllBalance parameter for liquidity swap --- contracts/adapters/BaseUniswapAdapter.sol | 33 +-- .../adapters/UniswapLiquiditySwapAdapter.sol | 114 ++++++++-- contracts/adapters/UniswapRepayAdapter.sol | 44 +++- test/uniswapAdapters.spec.ts | 208 ++++++++++++++++-- 4 files changed, 327 insertions(+), 72 deletions(-) diff --git a/contracts/adapters/BaseUniswapAdapter.sol b/contracts/adapters/BaseUniswapAdapter.sol index 9dbdfbdf..96ad36ae 100644 --- a/contracts/adapters/BaseUniswapAdapter.sol +++ b/contracts/adapters/BaseUniswapAdapter.sol @@ -124,7 +124,7 @@ contract BaseUniswapAdapter { * @param minAmountOut the min amount of `assetToSwapTo` to be received from the swap * @return the amount received from the swap */ - function swapExactTokensForTokens( + function _swapExactTokensForTokens( address assetToSwapFrom, address assetToSwapTo, uint256 amountToSwap, @@ -167,7 +167,7 @@ contract BaseUniswapAdapter { * @param amountToReceive Exact amount of `assetToSwapTo` to receive * @return the amount swapped */ - function swapTokensForExactTokens( + function _swapTokensForExactTokens( address assetToSwapFrom, address assetToSwapTo, uint256 maxAmountToSwap, @@ -222,7 +222,7 @@ contract BaseUniswapAdapter { * @dev Get the aToken associated to the asset * @return address of the aToken */ - function getAToken(address asset) internal view returns (address) { + function _getAToken(address asset) internal view returns (address) { ReserveLogic.ReserveData memory reserve = POOL.getReserveData(asset); return reserve.aTokenAddress; } @@ -236,7 +236,7 @@ contract BaseUniswapAdapter { * (1) Direct transfer to user * @param user address */ - function sendLeftovers(address asset, uint256 reservedAmount, LeftoverAction leftOverAction, address user) internal { + function _sendLeftovers(address asset, uint256 reservedAmount, LeftoverAction leftOverAction, address user) internal { uint256 balance = IERC20(asset).balanceOf(address(this)); uint256 assetLeftOver = balance.sub(reservedAmount); @@ -253,18 +253,18 @@ contract BaseUniswapAdapter { /** * @dev Pull the ATokens from the user * @param reserve address of the asset + * @param reserveAToken address of the aToken of the reserve * @param user address * @param amount of tokens to be transferred to the contract * @param permitSignature struct containing the permit signature */ - function pullAToken( + function _pullAToken( address reserve, + address reserveAToken, address user, uint256 amount, PermitSignature memory permitSignature ) internal { - address reserveAToken = getAToken(reserve); - if (_usePermit(permitSignature)) { IERC20WithPermit(reserveAToken).permit( user, @@ -284,25 +284,6 @@ contract BaseUniswapAdapter { POOL.withdraw(reserve, amount, address(this)); } - /** - * @dev Pull the ATokens from the user and use them to repay the flashloan - * @param reserve address of the asset - * @param user address - * @param flashLoanDebt need to be repaid - * @param permitSignature struct containing the permit signature - */ - function pullATokenAndRepayFlashLoan( - address reserve, - address user, - uint256 flashLoanDebt, - PermitSignature memory permitSignature - ) internal { - pullAToken(reserve, user, flashLoanDebt, permitSignature); - - // Repay flashloan - IERC20(reserve).approve(address(POOL), flashLoanDebt); - } - /** * @dev Tells if the permit method should be called by inspecting if there is a valid signature. * If signature params are set to 0, then permit won't be called. diff --git a/contracts/adapters/UniswapLiquiditySwapAdapter.sol b/contracts/adapters/UniswapLiquiditySwapAdapter.sol index 365089a8..7dd365d6 100644 --- a/contracts/adapters/UniswapLiquiditySwapAdapter.sol +++ b/contracts/adapters/UniswapLiquiditySwapAdapter.sol @@ -18,6 +18,7 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { struct SwapParams { address[] assetToSwapToList; uint256[] minAmountsToReceive; + bool[] swapAllBalance; PermitParams permitParams; } @@ -41,6 +42,7 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { * @param params Additional variadic field to include extra params. Expected parameters: * address[] assetToSwapToList List of the addresses of the reserve to be swapped to and deposited * uint256[] minAmountsToReceive List of min amounts to be received from the swap + * bool[] swapAllBalance Flag indicating if all the user balance should be swapped * uint256[] deadline List of deadlines for the permit signature * uint8[] v List of v param for the permit signature * bytes32[] r List of r param for the permit signature @@ -60,6 +62,7 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { require( assets.length == decodedParams.assetToSwapToList.length && assets.length == decodedParams.minAmountsToReceive.length + && assets.length == decodedParams.swapAllBalance.length && assets.length == decodedParams.permitParams.deadline.length && assets.length == decodedParams.permitParams.v.length && assets.length == decodedParams.permitParams.r.length @@ -68,22 +71,14 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { ); for (uint256 i = 0; i < assets.length; i++) { - uint256 receivedAmount = swapExactTokensForTokens( + _swapLiquidity( assets[i], decodedParams.assetToSwapToList[i], amounts[i], - decodedParams.minAmountsToReceive[i] - ); - - // Deposit new reserve - IERC20(decodedParams.assetToSwapToList[i]).approve(address(POOL), receivedAmount); - POOL.deposit(decodedParams.assetToSwapToList[i], receivedAmount, initiator, 0); - - uint256 flashLoanDebt = amounts[i].add(premiums[i]); - pullATokenAndRepayFlashLoan( - assets[i], + premiums[i], initiator, - flashLoanDebt, + decodedParams.minAmountsToReceive[i], + decodedParams.swapAllBalance[i], PermitSignature( decodedParams.permitParams.deadline[i], decodedParams.permitParams.v[i], @@ -103,7 +98,7 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { * perform the swap. * @param assetToSwapFromList List of addresses of the underlying asset to be swap from * @param assetToSwapToList List of addresses of the underlying asset to be swap to and deposited - * @param amountToSwapList List of amounts to be swapped + * @param amountToSwapList List of amounts to be swapped. If the amount exceeds the balance, the total balance is used for the swap * @param minAmountsToReceive List of min amounts to be received from the swap * @param permitParams List of struct containing the permit signatures * uint256[] deadline List of deadlines for the permit signature @@ -127,17 +122,23 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { ); for (uint256 i = 0; i < assetToSwapFromList.length; i++) { - pullAToken( + address aToken = _getAToken(assetToSwapFromList[i]); + + uint256 aTokenInitiatorBalance = IERC20(aToken).balanceOf(msg.sender); + uint256 amountToSwap = amountToSwapList[i] > aTokenInitiatorBalance ? aTokenInitiatorBalance : amountToSwapList[i]; + + _pullAToken( assetToSwapFromList[i], + aToken, msg.sender, - amountToSwapList[i], + amountToSwap, permitParams[i] ); - uint256 receivedAmount = swapExactTokensForTokens( + uint256 receivedAmount = _swapExactTokensForTokens( assetToSwapFromList[i], assetToSwapToList[i], - amountToSwapList[i], + amountToSwap, minAmountsToReceive[i] ); @@ -147,12 +148,61 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { } } + /** + * @dev Swaps an `amountToSwap` of an asset to another and deposits the funds on behalf of the initiator. + * @param assetFrom Address of the underlying asset to be swap from + * @param assetTo Address of the underlying asset to be swap to and deposited + * @param amount Amount from flashloan + * @param premium Premium of the flashloan + * @param minAmountToReceive Min amount to be received from the swap + * @param swapAllBalance Flag indicating if all the user balance should be swapped + * @param permitSignature List of struct containing the permit signature + */ + function _swapLiquidity( + address assetFrom, + address assetTo, + uint256 amount, + uint256 premium, + address initiator, + uint256 minAmountToReceive, + bool swapAllBalance, + PermitSignature memory permitSignature + ) internal { + address aToken = _getAToken(assetFrom); + + uint256 aTokenInitiatorBalance = IERC20(aToken).balanceOf(initiator); + uint256 amountToSwap = swapAllBalance ? aTokenInitiatorBalance.sub(premium) : amount; + + uint256 receivedAmount = _swapExactTokensForTokens( + assetFrom, + assetTo, + amountToSwap, + minAmountToReceive + ); + + // Deposit new reserve + IERC20(assetTo).approve(address(POOL), receivedAmount); + POOL.deposit(assetTo, receivedAmount, initiator, 0); + + uint256 flashLoanDebt = amount.add(premium); + uint256 amountToPull = swapAllBalance ? aTokenInitiatorBalance : flashLoanDebt; + + _pullATokenAndRepayFlashLoan( + assetFrom, + aToken, + initiator, + amountToPull, + flashLoanDebt, + permitSignature + ); + } + /** * @dev Decodes debt information encoded in flashloan params * @param params Additional variadic field to include extra params. Expected parameters: * address[] assetToSwapToList List of the addresses of the reserve to be swapped to and deposited * uint256[] minAmountsToReceive List of min amounts to be received from the swap - * uint256[] deadline List of deadlines for the permit signature + * bool[] swapAllBalance Flag indicating if all the user balance should be swapped * uint256[] deadline List of deadlines for the permit signature * uint8[] v List of v param for the permit signature * bytes32[] r List of r param for the permit signature @@ -163,12 +213,36 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { ( address[] memory assetToSwapToList, uint256[] memory minAmountsToReceive, + bool[] memory swapAllBalance, uint256[] memory deadline, uint8[] memory v, bytes32[] memory r, bytes32[] memory s - ) = abi.decode(params, (address[], uint256[], uint256[], uint8[], bytes32[], bytes32[])); + ) = abi.decode(params, (address[], uint256[], bool[], uint256[], uint8[], bytes32[], bytes32[])); - return SwapParams(assetToSwapToList, minAmountsToReceive, PermitParams(deadline, v, r, s)); + return SwapParams(assetToSwapToList, minAmountsToReceive, swapAllBalance, PermitParams(deadline, v, r, s)); + } + + /** + * @dev Pull the ATokens from the user and use them to repay the flashloan + * @param reserve address of the asset + * @param reserveAToken address of the aToken of the reserve + * @param user address + * @param amountToPull amount to be pulled from the user + * @param flashLoanDebt need to be repaid + * @param permitSignature struct containing the permit signature + */ + function _pullATokenAndRepayFlashLoan( + address reserve, + address reserveAToken, + address user, + uint256 amountToPull, + uint256 flashLoanDebt, + PermitSignature memory permitSignature + ) internal { + _pullAToken(reserve, reserveAToken, user, amountToPull, permitSignature); + + // Repay flashloan + IERC20(reserve).approve(address(POOL), flashLoanDebt); } } diff --git a/contracts/adapters/UniswapRepayAdapter.sol b/contracts/adapters/UniswapRepayAdapter.sol index 550241ea..59f9c323 100644 --- a/contracts/adapters/UniswapRepayAdapter.sol +++ b/contracts/adapters/UniswapRepayAdapter.sol @@ -119,17 +119,17 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { uint256 premium, PermitSignature memory permitSignature ) internal { - swapTokensForExactTokens(assetFrom, assetTo, amount, repayAmount); + _swapTokensForExactTokens(assetFrom, assetTo, amount, repayAmount); // Repay debt IERC20(assetTo).approve(address(POOL), repayAmount); POOL.repay(assetTo, repayAmount, rateMode, initiator); uint256 flashLoanDebt = amount.add(premium); - pullATokenAndRepayFlashLoan(assetFrom, initiator, flashLoanDebt, permitSignature); + _pullATokenAndRepayFlashLoan(assetFrom, initiator, flashLoanDebt, permitSignature); // Take care of reserve leftover from the swap - sendLeftovers(assetFrom, flashLoanDebt, leftOverAction, initiator); + _sendLeftovers(assetFrom, flashLoanDebt, leftOverAction, initiator); } /** @@ -161,15 +161,35 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { return RepayParams( assetToSwapToList, - leftOverAction, - repayAmounts, - rateModes, - PermitParams( - deadline, - v, - r, - s - ) + leftOverAction, + repayAmounts, + rateModes, + PermitParams( + deadline, + v, + r, + s + ) ); } + + /** + * @dev Pull the ATokens from the user and use them to repay the flashloan + * @param reserve address of the asset + * @param user address + * @param flashLoanDebt need to be repaid + * @param permitSignature struct containing the permit signature + */ + function _pullATokenAndRepayFlashLoan( + address reserve, + address user, + uint256 flashLoanDebt, + PermitSignature memory permitSignature + ) internal { + address reserveAToken = _getAToken(reserve); + _pullAToken(reserve, reserveAToken, user, flashLoanDebt, permitSignature); + + // Repay flashloan + IERC20(reserve).approve(address(POOL), flashLoanDebt); + } } diff --git a/test/uniswapAdapters.spec.ts b/test/uniswapAdapters.spec.ts index 7dc7f03c..a86718b0 100644 --- a/test/uniswapAdapters.spec.ts +++ b/test/uniswapAdapters.spec.ts @@ -301,12 +301,13 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); const params = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + ['address[]', 'uint256[]', 'bool[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], [ [dai.address], [expectedDaiAmount], [0], [0], + [0], ['0x0000000000000000000000000000000000000000000000000000000000000000'], ['0x0000000000000000000000000000000000000000000000000000000000000000'], ] @@ -411,12 +412,13 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { .toFixed(0); const params = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + ['address[]', 'uint256[]', 'bool[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], [ [dai.address, dai.address], [expectedDaiAmountForEth, expectedDaiAmountForUsdc], [0, 0], [0, 0], + [0, 0], [ '0x0000000000000000000000000000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000000000000000000000000000', @@ -572,10 +574,11 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { ); const params = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + ['address[]', 'uint256[]', 'bool[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], [ [dai.address, dai.address], [expectedDaiAmountForEth, expectedDaiAmountForUsdc], + [0, 0], [deadline, deadline], [aWETHv, aUsdcv], [aWETHr, aUsdcr], @@ -673,8 +676,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const {v, r, s} = getSignatureFromTypedData(ownerPrivateKey, msgParams); const params = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], - [[dai.address], [expectedDaiAmount], [deadline], [v], [r], [s]] + ['address[]', 'uint256[]', 'bool[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + [[dai.address], [expectedDaiAmount], [0], [deadline], [v], [r], [s]] ); await expect( @@ -738,12 +741,13 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); const params = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + ['address[]', 'uint256[]', 'bool[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], [ [dai.address, weth.address], [expectedDaiAmount], [0], [0], + [0], ['0x0000000000000000000000000000000000000000000000000000000000000000'], ['0x0000000000000000000000000000000000000000000000000000000000000000'], ] @@ -764,11 +768,12 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { ).to.be.revertedWith('INCONSISTENT_PARAMS'); const params2 = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + ['address[]', 'uint256[]', 'bool[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], [ [dai.address, weth.address], [expectedDaiAmount], [0, 0], + [0, 0], [0], ['0x0000000000000000000000000000000000000000000000000000000000000000'], ['0x0000000000000000000000000000000000000000000000000000000000000000'], @@ -790,10 +795,11 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { ).to.be.revertedWith('INCONSISTENT_PARAMS'); const params3 = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + ['address[]', 'uint256[]', 'bool[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], [ [dai.address, weth.address], [expectedDaiAmount], + [0, 0], [0], [0, 0], ['0x0000000000000000000000000000000000000000000000000000000000000000'], @@ -816,12 +822,13 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { ).to.be.revertedWith('INCONSISTENT_PARAMS'); const params4 = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + ['address[]', 'uint256[]', 'bool[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], [ [dai.address, weth.address], [expectedDaiAmount], [0], [0], + [0], [ '0x0000000000000000000000000000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000000000000000000000000000', @@ -845,12 +852,13 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { ).to.be.revertedWith('INCONSISTENT_PARAMS'); const params5 = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + ['address[]', 'uint256[]', 'bool[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], [ [dai.address, weth.address], [expectedDaiAmount], [0], [0], + [0], ['0x0000000000000000000000000000000000000000000000000000000000000000'], [ '0x0000000000000000000000000000000000000000000000000000000000000000', @@ -874,12 +882,13 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { ).to.be.revertedWith('INCONSISTENT_PARAMS'); const params6 = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + ['address[]', 'uint256[]', 'bool[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], [ [dai.address, weth.address], [expectedDaiAmount, expectedDaiAmount], [0], [0], + [0], ['0x0000000000000000000000000000000000000000000000000000000000000000'], ['0x0000000000000000000000000000000000000000000000000000000000000000'], ] @@ -898,6 +907,33 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { 0 ) ).to.be.revertedWith('INCONSISTENT_PARAMS'); + + const params7 = ethers.utils.defaultAbiCoder.encode( + ['address[]', 'uint256[]', 'bool[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + [ + [dai.address], + [expectedDaiAmount], + [0, 0], + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ] + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapLiquiditySwapAdapter.address, + [weth.address], + [flashloanAmount.toString()], + [0], + userAddress, + params7, + 0 + ) + ).to.be.revertedWith('INCONSISTENT_PARAMS'); }); it('should revert if caller not lending pool', async () => { @@ -923,12 +959,13 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); const params = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + ['address[]', 'uint256[]', 'bool[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], [ [dai.address], [expectedDaiAmount], [0], [0], + [0], ['0x0000000000000000000000000000000000000000000000000000000000000000'], ['0x0000000000000000000000000000000000000000000000000000000000000000'], ] @@ -1003,12 +1040,13 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const flashloanAmount = new BigNumber(amountUSDCtoSwap.toString()).div(1.0009).toFixed(0); const params = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + ['address[]', 'uint256[]', 'bool[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], [ [dai.address], [expectedDaiAmount], [0], [0], + [0], ['0x0000000000000000000000000000000000000000000000000000000000000000'], ['0x0000000000000000000000000000000000000000000000000000000000000000'], ] @@ -1068,12 +1106,13 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); const params = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + ['address[]', 'uint256[]', 'bool[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], [ [dai.address], [smallExpectedDaiAmount], [0], [0], + [0], ['0x0000000000000000000000000000000000000000000000000000000000000000'], ['0x0000000000000000000000000000000000000000000000000000000000000000'], ] @@ -1093,6 +1132,81 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { ) ).to.be.revertedWith('minAmountOut exceed max slippage'); }); + + it('should correctly swap tokens all the balance', async () => { + const {users, weth, oracle, dai, aDai, aWETH, pool, uniswapLiquiditySwapAdapter} = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + await mockUniswapRouter.setAmountToReturn(weth.address, expectedDaiAmount); + + // Remove other balance + await aWETH.connect(user).transfer(users[1].address, parseEther('90')); + const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + + // User will swap liquidity 10 aEth to aDai + const liquidityToSwap = parseEther('10'); + expect(userAEthBalanceBefore).to.be.eq(liquidityToSwap); + await aWETH.connect(user).approve(uniswapLiquiditySwapAdapter.address, liquidityToSwap); + + const params = ethers.utils.defaultAbiCoder.encode( + ['address[]', 'uint256[]', 'bool[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + [ + [dai.address], + [expectedDaiAmount], + [1], + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ] + ); + + // Flashloan + premium > aToken balance. Then it will only swap the balance + const flashloanFee = liquidityToSwap.mul(9).div(10000); + const swappedAmount = liquidityToSwap.sub(flashloanFee); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapLiquiditySwapAdapter.address, + [weth.address], + [liquidityToSwap.toString()], + [0], + userAddress, + params, + 0 + ) + ) + .to.emit(uniswapLiquiditySwapAdapter, 'Swapped') + .withArgs(weth.address, dai.address, swappedAmount.toString(), expectedDaiAmount); + + const adapterWethBalance = await weth.balanceOf(uniswapLiquiditySwapAdapter.address); + const adapterDaiBalance = await dai.balanceOf(uniswapLiquiditySwapAdapter.address); + const adapterDaiAllowance = await dai.allowance( + uniswapLiquiditySwapAdapter.address, + userAddress + ); + const userADaiBalance = await aDai.balanceOf(userAddress); + const userAEthBalance = await aWETH.balanceOf(userAddress); + const adapterAEthBalance = await aWETH.balanceOf(uniswapLiquiditySwapAdapter.address); + + expect(adapterWethBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(adapterDaiAllowance).to.be.eq(Zero); + expect(userADaiBalance).to.be.eq(expectedDaiAmount); + expect(userAEthBalance).to.be.eq(Zero); + expect(adapterAEthBalance).to.be.eq(Zero); + }); }); describe('swapAndDeposit', () => { @@ -1607,6 +1721,72 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { expect(userAUsdcBalance).to.be.lt(userAUsdcBalanceBefore); expect(userAUsdcBalance).to.be.gte(userAUsdcBalanceBefore.sub(amountUSDCtoSwap)); }); + + it('should correctly swap all the balance when using a bigger amount', async () => { + const {users, weth, oracle, dai, aDai, aWETH, uniswapLiquiditySwapAdapter} = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + await mockUniswapRouter.setAmountToReturn(weth.address, expectedDaiAmount); + + // Remove other balance + await aWETH.connect(user).transfer(users[1].address, parseEther('90')); + const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + + // User will swap liquidity 10 aEth to aDai + const liquidityToSwap = parseEther('10'); + expect(userAEthBalanceBefore).to.be.eq(liquidityToSwap); + + // User will swap liquidity 10 aEth to aDai + await aWETH.connect(user).approve(uniswapLiquiditySwapAdapter.address, liquidityToSwap); + + // Only has 10 atokens, so all the balance will be swapped + const bigAmountToSwap = parseEther('100'); + + await expect( + uniswapLiquiditySwapAdapter.connect(user).swapAndDeposit( + [weth.address], + [dai.address], + [bigAmountToSwap], + [expectedDaiAmount], + [ + { + deadline: 0, + v: 0, + r: '0x0000000000000000000000000000000000000000000000000000000000000000', + s: '0x0000000000000000000000000000000000000000000000000000000000000000', + }, + ] + ) + ) + .to.emit(uniswapLiquiditySwapAdapter, 'Swapped') + .withArgs(weth.address, dai.address, amountWETHtoSwap.toString(), expectedDaiAmount); + + const adapterWethBalance = await weth.balanceOf(uniswapLiquiditySwapAdapter.address); + const adapterDaiBalance = await dai.balanceOf(uniswapLiquiditySwapAdapter.address); + const adapterDaiAllowance = await dai.allowance( + uniswapLiquiditySwapAdapter.address, + userAddress + ); + const userADaiBalance = await aDai.balanceOf(userAddress); + const userAEthBalance = await aWETH.balanceOf(userAddress); + const adapterAEthBalance = await aWETH.balanceOf(uniswapLiquiditySwapAdapter.address); + + expect(adapterWethBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(adapterDaiAllowance).to.be.eq(Zero); + expect(userADaiBalance).to.be.eq(expectedDaiAmount); + expect(userAEthBalance).to.be.eq(Zero); + expect(adapterAEthBalance).to.be.eq(Zero); + }); }); }); From 1faffa2c39e86de3ec0e80e1eb84dfa21c2dd696 Mon Sep 17 00:00:00 2001 From: Gerardo Nardelli Date: Fri, 6 Nov 2020 12:12:08 -0300 Subject: [PATCH 14/62] Add permit amount parameter to correctly execute permit call --- contracts/adapters/BaseUniswapAdapter.sol | 4 +- .../adapters/UniswapLiquiditySwapAdapter.sol | 18 +- contracts/adapters/UniswapRepayAdapter.sol | 8 +- test/uniswapAdapters.spec.ts | 1168 ++++++++--------- 4 files changed, 602 insertions(+), 596 deletions(-) diff --git a/contracts/adapters/BaseUniswapAdapter.sol b/contracts/adapters/BaseUniswapAdapter.sol index 96ad36ae..c69fa9a0 100644 --- a/contracts/adapters/BaseUniswapAdapter.sol +++ b/contracts/adapters/BaseUniswapAdapter.sol @@ -27,6 +27,7 @@ contract BaseUniswapAdapter { enum LeftoverAction {DEPOSIT, TRANSFER} struct PermitParams { + uint256[] amount; uint256[] deadline; uint8[] v; bytes32[] r; @@ -34,6 +35,7 @@ contract BaseUniswapAdapter { } struct PermitSignature { + uint256 amount; uint256 deadline; uint8 v; bytes32 r; @@ -269,7 +271,7 @@ contract BaseUniswapAdapter { IERC20WithPermit(reserveAToken).permit( user, address(this), - amount, + permitSignature.amount, permitSignature.deadline, permitSignature.v, permitSignature.r, diff --git a/contracts/adapters/UniswapLiquiditySwapAdapter.sol b/contracts/adapters/UniswapLiquiditySwapAdapter.sol index 7dd365d6..83b334d1 100644 --- a/contracts/adapters/UniswapLiquiditySwapAdapter.sol +++ b/contracts/adapters/UniswapLiquiditySwapAdapter.sol @@ -43,6 +43,7 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { * address[] assetToSwapToList List of the addresses of the reserve to be swapped to and deposited * uint256[] minAmountsToReceive List of min amounts to be received from the swap * bool[] swapAllBalance Flag indicating if all the user balance should be swapped + * uint256[] permitAmount List of amounts for the permit signature * uint256[] deadline List of deadlines for the permit signature * uint8[] v List of v param for the permit signature * bytes32[] r List of r param for the permit signature @@ -63,6 +64,7 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { assets.length == decodedParams.assetToSwapToList.length && assets.length == decodedParams.minAmountsToReceive.length && assets.length == decodedParams.swapAllBalance.length + && assets.length == decodedParams.permitParams.amount.length && assets.length == decodedParams.permitParams.deadline.length && assets.length == decodedParams.permitParams.v.length && assets.length == decodedParams.permitParams.r.length @@ -80,6 +82,7 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { decodedParams.minAmountsToReceive[i], decodedParams.swapAllBalance[i], PermitSignature( + decodedParams.permitParams.amount[i], decodedParams.permitParams.deadline[i], decodedParams.permitParams.v[i], decodedParams.permitParams.r[i], @@ -101,10 +104,11 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { * @param amountToSwapList List of amounts to be swapped. If the amount exceeds the balance, the total balance is used for the swap * @param minAmountsToReceive List of min amounts to be received from the swap * @param permitParams List of struct containing the permit signatures - * uint256[] deadline List of deadlines for the permit signature - * uint8[] v List of v param for the permit signature - * bytes32[] r List of r param for the permit signature - * bytes32[] s List of s param for the permit signature + * uint256 permitAmount Amount for the permit signature + * uint256 deadline Deadline for the permit signature + * uint8 v param for the permit signature + * bytes32 r param for the permit signature + * bytes32 s param for the permit signature */ function swapAndDeposit( address[] calldata assetToSwapFromList, @@ -203,6 +207,7 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { * address[] assetToSwapToList List of the addresses of the reserve to be swapped to and deposited * uint256[] minAmountsToReceive List of min amounts to be received from the swap * bool[] swapAllBalance Flag indicating if all the user balance should be swapped + * uint256[] permitAmount List of amounts for the permit signature * uint256[] deadline List of deadlines for the permit signature * uint8[] v List of v param for the permit signature * bytes32[] r List of r param for the permit signature @@ -214,13 +219,14 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { address[] memory assetToSwapToList, uint256[] memory minAmountsToReceive, bool[] memory swapAllBalance, + uint256[] memory permitAmount, uint256[] memory deadline, uint8[] memory v, bytes32[] memory r, bytes32[] memory s - ) = abi.decode(params, (address[], uint256[], bool[], uint256[], uint8[], bytes32[], bytes32[])); + ) = abi.decode(params, (address[], uint256[], bool[], uint256[], uint256[], uint8[], bytes32[], bytes32[])); - return SwapParams(assetToSwapToList, minAmountsToReceive, swapAllBalance, PermitParams(deadline, v, r, s)); + return SwapParams(assetToSwapToList, minAmountsToReceive, swapAllBalance, PermitParams(permitAmount, deadline, v, r, s)); } /** diff --git a/contracts/adapters/UniswapRepayAdapter.sol b/contracts/adapters/UniswapRepayAdapter.sol index 59f9c323..46d9c5b0 100644 --- a/contracts/adapters/UniswapRepayAdapter.sol +++ b/contracts/adapters/UniswapRepayAdapter.sol @@ -47,6 +47,7 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { * (1) Direct transfer to user * uint256[] repayAmounts List of amounts of debt to be repaid * uint256[] rateModes List of the rate modes of the debt to be repaid + * uint256[] permitAmount List of amounts for the permit signature * uint256[] deadline List of deadlines for the permit signature * uint8[] v List of v param for the permit signature * bytes32[] r List of r param for the permit signature @@ -67,6 +68,7 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { assets.length == decodedParams.assetToSwapToList.length && assets.length == decodedParams.repayAmounts.length && assets.length == decodedParams.rateModes.length + && assets.length == decodedParams.permitParams.amount.length && assets.length == decodedParams.permitParams.deadline.length && assets.length == decodedParams.permitParams.v.length && assets.length == decodedParams.permitParams.r.length @@ -84,6 +86,7 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { decodedParams.leftOverAction, premiums[i], PermitSignature( + decodedParams.permitParams.amount[i], decodedParams.permitParams.deadline[i], decodedParams.permitParams.v[i], decodedParams.permitParams.r[i], @@ -141,6 +144,7 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { * (1) Direct transfer to user * uint256[] repayAmounts List of amounts of debt to be repaid * uint256[] rateModes List of the rate modes of the debt to be repaid + * uint256[] permitAmount List of amounts for the permit signature * uint256[] deadline List of deadlines for the permit signature * uint8[] v List of v param for the permit signature * bytes32[] r List of r param for the permit signature @@ -153,11 +157,12 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { LeftoverAction leftOverAction, uint256[] memory repayAmounts, uint256[] memory rateModes, + uint256[] memory permitAmount, uint256[] memory deadline, uint8[] memory v, bytes32[] memory r, bytes32[] memory s - ) = abi.decode(params, (address[], LeftoverAction, uint256[], uint256[], uint256[], uint8[], bytes32[], bytes32[])); + ) = abi.decode(params, (address[], LeftoverAction, uint256[], uint256[], uint256[], uint256[], uint8[], bytes32[], bytes32[])); return RepayParams( assetToSwapToList, @@ -165,6 +170,7 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { repayAmounts, rateModes, PermitParams( + permitAmount, deadline, v, r, diff --git a/test/uniswapAdapters.spec.ts b/test/uniswapAdapters.spec.ts index a86718b0..2e23f555 100644 --- a/test/uniswapAdapters.spec.ts +++ b/test/uniswapAdapters.spec.ts @@ -4,6 +4,8 @@ import { getContract, buildPermitParams, getSignatureFromTypedData, + buildLiquiditySwapParams, + buildRepayAdapterParams, } from '../helpers/contracts-helpers'; import {getMockUniswapRouter} from '../helpers/contracts-getters'; import { @@ -300,17 +302,15 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { // Subtract the FL fee from the amount to be swapped 0,09% const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); - const params = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256[]', 'bool[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], - [ - [dai.address], - [expectedDaiAmount], - [0], - [0], - [0], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ] + const params = buildLiquiditySwapParams( + [dai.address], + [expectedDaiAmount], + [0], + [0], + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'] ); await expect( @@ -411,22 +411,20 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { .div(1.0009) .toFixed(0); - const params = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256[]', 'bool[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + const params = buildLiquiditySwapParams( + [dai.address, dai.address], + [expectedDaiAmountForEth, expectedDaiAmountForUsdc], + [0, 0], + [0, 0], + [0, 0], + [0, 0], [ - [dai.address, dai.address], - [expectedDaiAmountForEth, expectedDaiAmountForUsdc], - [0, 0], - [0, 0], - [0, 0], - [ - '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000', - ], - [ - '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000', - ], + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000', + ], + [ + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000', ] ); @@ -524,20 +522,13 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); const userAUsdcBalanceBefore = await aUsdc.balanceOf(userAddress); - // IMPORTANT: Round down to work equal to solidity to get the correct value for permit call - BigNumber.config({ - ROUNDING_MODE: 1, //round down - }); + const wethFlashloanAmount = new BigNumber(amountWETHtoSwap.toString()) + .div(1.0009) + .toFixed(0); - const wethFlashloanAmountBN = new BigNumber(amountWETHtoSwap.toString()).div(1.0009); - const wethFlashloanAmount = wethFlashloanAmountBN.toFixed(0); - const wethFlashloanFee = wethFlashloanAmountBN.multipliedBy(9).div(10000); - const wethAmountToPermit = wethFlashloanAmountBN.plus(wethFlashloanFee).toFixed(0); - - const usdcFlashloanAmountBN = new BigNumber(amountUSDCtoSwap.toString()).div(1.0009); - const usdcFlashloanAmount = usdcFlashloanAmountBN.toFixed(0); - const usdcFlashloanFee = usdcFlashloanAmountBN.multipliedBy(9).div(10000); - const usdcAmountToPermit = usdcFlashloanAmountBN.plus(usdcFlashloanFee).toFixed(0); + const usdcFlashloanAmount = new BigNumber(amountUSDCtoSwap.toString()) + .div(1.0009) + .toFixed(0); const aWethNonce = (await aWETH._nonces(userAddress)).toNumber(); const aWethMsgParams = buildPermitParams( @@ -549,7 +540,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { uniswapLiquiditySwapAdapter.address, aWethNonce, deadline, - wethAmountToPermit.toString() + amountWETHtoSwap.toString() ); const {v: aWETHv, r: aWETHr, s: aWETHs} = getSignatureFromTypedData( ownerPrivateKey, @@ -566,24 +557,21 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { uniswapLiquiditySwapAdapter.address, aUsdcNonce, deadline, - usdcAmountToPermit.toString() + amountUSDCtoSwap.toString() ); const {v: aUsdcv, r: aUsdcr, s: aUsdcs} = getSignatureFromTypedData( ownerPrivateKey, aUsdcMsgParams ); - - const params = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256[]', 'bool[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], - [ - [dai.address, dai.address], - [expectedDaiAmountForEth, expectedDaiAmountForUsdc], - [0, 0], - [deadline, deadline], - [aWETHv, aUsdcv], - [aWETHr, aUsdcr], - [aWETHs, aUsdcs], - ] + const params = buildLiquiditySwapParams( + [dai.address, dai.address], + [expectedDaiAmountForEth, expectedDaiAmountForUsdc], + [0, 0], + [amountWETHtoSwap, amountUSDCtoSwap], + [deadline, deadline], + [aWETHv, aUsdcv], + [aWETHr, aUsdcr], + [aWETHs, aUsdcs] ); await pool @@ -616,11 +604,6 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(amountWETHtoSwap)); expect(userAUsdcBalance).to.be.lt(userAUsdcBalanceBefore); expect(userAUsdcBalance).to.be.gte(userAUsdcBalanceBefore.sub(amountUSDCtoSwap)); - - // Restore round up - BigNumber.config({ - ROUNDING_MODE: 0, //round up - }); }); it('should correctly swap tokens with permit', async () => { @@ -642,16 +625,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const liquidityToSwap = parseEther('10'); const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); - // IMPORTANT: Round down to work equal to solidity to get the correct value for permit call - BigNumber.config({ - ROUNDING_MODE: 1, //round down - }); - // Subtract the FL fee from the amount to be swapped 0,09% - const flashloanAmountBN = new BigNumber(liquidityToSwap.toString()).div(1.0009); - const flashloanAmount = flashloanAmountBN.toFixed(0); - const flashloanFee = flashloanAmountBN.multipliedBy(9).div(10000); - const amountToPermit = flashloanAmountBN.plus(flashloanFee); + const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); const chainId = BRE.network.config.chainId || BUIDLEREVM_CHAINID; const deadline = MAX_UINT_AMOUNT; @@ -665,7 +640,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { uniswapLiquiditySwapAdapter.address, nonce, deadline, - amountToPermit.toFixed(0).toString() + liquidityToSwap.toString() ); const ownerPrivateKey = require('../test-wallets.js').accounts[1].secretKey; @@ -675,9 +650,15 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const {v, r, s} = getSignatureFromTypedData(ownerPrivateKey, msgParams); - const params = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256[]', 'bool[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], - [[dai.address], [expectedDaiAmount], [0], [deadline], [v], [r], [s]] + const params = buildLiquiditySwapParams( + [dai.address], + [expectedDaiAmount], + [0], + [liquidityToSwap], + [deadline], + [v], + [r], + [s] ); await expect( @@ -711,11 +692,6 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { expect(userADaiBalance).to.be.eq(expectedDaiAmount); expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); - - // Restore round up - BigNumber.config({ - ROUNDING_MODE: 0, //round up - }); }); it('should revert if inconsistent params', async () => { @@ -740,17 +716,15 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { // Subtract the FL fee from the amount to be swapped 0,09% const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); - const params = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256[]', 'bool[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], - [ - [dai.address, weth.address], - [expectedDaiAmount], - [0], - [0], - [0], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ] + const params = buildLiquiditySwapParams( + [dai.address, weth.address], + [expectedDaiAmount], + [0], + [0], + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'] ); await expect( @@ -767,17 +741,15 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { ) ).to.be.revertedWith('INCONSISTENT_PARAMS'); - const params2 = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256[]', 'bool[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], - [ - [dai.address, weth.address], - [expectedDaiAmount], - [0, 0], - [0, 0], - [0], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ] + const params2 = buildLiquiditySwapParams( + [dai.address, weth.address], + [expectedDaiAmount], + [0, 0], + [0, 0], + [0, 0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'] ); await expect( @@ -794,17 +766,15 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { ) ).to.be.revertedWith('INCONSISTENT_PARAMS'); - const params3 = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256[]', 'bool[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], - [ - [dai.address, weth.address], - [expectedDaiAmount], - [0, 0], - [0], - [0, 0], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ] + const params3 = buildLiquiditySwapParams( + [dai.address, weth.address], + [expectedDaiAmount], + [0, 0], + [0], + [0, 0], + [0, 0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'] ); await expect( @@ -821,20 +791,18 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { ) ).to.be.revertedWith('INCONSISTENT_PARAMS'); - const params4 = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256[]', 'bool[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + const params4 = buildLiquiditySwapParams( + [dai.address, weth.address], + [expectedDaiAmount], + [0], + [0], + [0], + [0], [ - [dai.address, weth.address], - [expectedDaiAmount], - [0], - [0], - [0], - [ - '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000', - ], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ] + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000', + ], + ['0x0000000000000000000000000000000000000000000000000000000000000000'] ); await expect( @@ -851,19 +819,17 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { ) ).to.be.revertedWith('INCONSISTENT_PARAMS'); - const params5 = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256[]', 'bool[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], + const params5 = buildLiquiditySwapParams( + [dai.address, weth.address], + [expectedDaiAmount], + [0], + [0], + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], [ - [dai.address, weth.address], - [expectedDaiAmount], - [0], - [0], - [0], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - [ - '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000', - ], + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000', ] ); @@ -881,17 +847,15 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { ) ).to.be.revertedWith('INCONSISTENT_PARAMS'); - const params6 = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256[]', 'bool[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], - [ - [dai.address, weth.address], - [expectedDaiAmount, expectedDaiAmount], - [0], - [0], - [0], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ] + const params6 = buildLiquiditySwapParams( + [dai.address, weth.address], + [expectedDaiAmount, expectedDaiAmount], + [0], + [0], + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'] ); await expect( @@ -908,17 +872,15 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { ) ).to.be.revertedWith('INCONSISTENT_PARAMS'); - const params7 = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256[]', 'bool[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], - [ - [dai.address], - [expectedDaiAmount], - [0, 0], - [0], - [0], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ] + const params7 = buildLiquiditySwapParams( + [dai.address], + [expectedDaiAmount], + [0, 0], + [0], + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'] ); await expect( @@ -934,6 +896,31 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { 0 ) ).to.be.revertedWith('INCONSISTENT_PARAMS'); + + const params8 = buildLiquiditySwapParams( + [dai.address], + [expectedDaiAmount], + [0], + [0, 0], + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'] + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapLiquiditySwapAdapter.address, + [weth.address], + [flashloanAmount.toString()], + [0], + userAddress, + params8, + 0 + ) + ).to.be.revertedWith('INCONSISTENT_PARAMS'); }); it('should revert if caller not lending pool', async () => { @@ -958,17 +945,15 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { // Subtract the FL fee from the amount to be swapped 0,09% const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); - const params = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256[]', 'bool[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], - [ - [dai.address], - [expectedDaiAmount], - [0], - [0], - [0], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ] + const params = buildLiquiditySwapParams( + [dai.address], + [expectedDaiAmount], + [0], + [0], + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'] ); await expect( @@ -1039,17 +1024,15 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { // Subtract the FL fee from the amount to be swapped 0,09% const flashloanAmount = new BigNumber(amountUSDCtoSwap.toString()).div(1.0009).toFixed(0); - const params = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256[]', 'bool[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], - [ - [dai.address], - [expectedDaiAmount], - [0], - [0], - [0], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ] + const params = buildLiquiditySwapParams( + [dai.address], + [expectedDaiAmount], + [0], + [0], + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'] ); await expect( @@ -1105,17 +1088,15 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { // Subtract the FL fee from the amount to be swapped 0,09% const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); - const params = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256[]', 'bool[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], - [ - [dai.address], - [smallExpectedDaiAmount], - [0], - [0], - [0], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ] + const params = buildLiquiditySwapParams( + [dai.address], + [smallExpectedDaiAmount], + [0], + [0], + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'] ); await expect( @@ -1157,20 +1138,111 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { expect(userAEthBalanceBefore).to.be.eq(liquidityToSwap); await aWETH.connect(user).approve(uniswapLiquiditySwapAdapter.address, liquidityToSwap); - const params = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'uint256[]', 'bool[]', 'uint256[]', 'uint8[]', 'bytes32[]', 'bytes32[]'], - [ - [dai.address], - [expectedDaiAmount], - [1], - [0], - [0], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ] + const params = buildLiquiditySwapParams( + [dai.address], + [expectedDaiAmount], + [1], + [0], + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'] ); - // Flashloan + premium > aToken balance. Then it will only swap the balance + // Flashloan + premium > aToken balance. Then it will only swap the balance - premium + const flashloanFee = liquidityToSwap.mul(9).div(10000); + const swappedAmount = liquidityToSwap.sub(flashloanFee); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapLiquiditySwapAdapter.address, + [weth.address], + [liquidityToSwap.toString()], + [0], + userAddress, + params, + 0 + ) + ) + .to.emit(uniswapLiquiditySwapAdapter, 'Swapped') + .withArgs(weth.address, dai.address, swappedAmount.toString(), expectedDaiAmount); + + const adapterWethBalance = await weth.balanceOf(uniswapLiquiditySwapAdapter.address); + const adapterDaiBalance = await dai.balanceOf(uniswapLiquiditySwapAdapter.address); + const adapterDaiAllowance = await dai.allowance( + uniswapLiquiditySwapAdapter.address, + userAddress + ); + const userADaiBalance = await aDai.balanceOf(userAddress); + const userAEthBalance = await aWETH.balanceOf(userAddress); + const adapterAEthBalance = await aWETH.balanceOf(uniswapLiquiditySwapAdapter.address); + + expect(adapterWethBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(adapterDaiAllowance).to.be.eq(Zero); + expect(userADaiBalance).to.be.eq(expectedDaiAmount); + expect(userAEthBalance).to.be.eq(Zero); + expect(adapterAEthBalance).to.be.eq(Zero); + }); + + it('should correctly swap tokens all the balance using permit', async () => { + const {users, weth, oracle, dai, aDai, aWETH, pool, uniswapLiquiditySwapAdapter} = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + await mockUniswapRouter.setAmountToReturn(weth.address, expectedDaiAmount); + + // Remove other balance + await aWETH.connect(user).transfer(users[1].address, parseEther('90')); + const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + + const liquidityToSwap = parseEther('10'); + expect(userAEthBalanceBefore).to.be.eq(liquidityToSwap); + + const chainId = BRE.network.config.chainId || BUIDLEREVM_CHAINID; + const deadline = MAX_UINT_AMOUNT; + const nonce = (await aWETH._nonces(userAddress)).toNumber(); + const msgParams = buildPermitParams( + chainId, + aWETH.address, + '1', + await aWETH.name(), + userAddress, + uniswapLiquiditySwapAdapter.address, + nonce, + deadline, + liquidityToSwap.toString() + ); + + const ownerPrivateKey = require('../test-wallets.js').accounts[1].secretKey; + if (!ownerPrivateKey) { + throw new Error('INVALID_OWNER_PK'); + } + + const {v, r, s} = getSignatureFromTypedData(ownerPrivateKey, msgParams); + + const params = buildLiquiditySwapParams( + [dai.address], + [expectedDaiAmount], + [1], + [liquidityToSwap], + [deadline], + [v], + [r], + [s] + ); + + // Flashloan + premium > aToken balance. Then it will only swap the balance - premium const flashloanFee = liquidityToSwap.mul(9).div(10000); const swappedAmount = liquidityToSwap.sub(flashloanFee); @@ -1253,6 +1325,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { [expectedDaiAmount], [ { + amount: 0, deadline: 0, v: 0, r: '0x0000000000000000000000000000000000000000000000000000000000000000', @@ -1330,6 +1403,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { [expectedDaiAmount], [ { + amount: liquidityToSwap, deadline, v, r, @@ -1377,6 +1451,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { [expectedDaiAmount], [ { + amount: 0, deadline: 0, v: 0, r: '0x0000000000000000000000000000000000000000000000000000000000000000', @@ -1394,6 +1469,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { [expectedDaiAmount], [ { + amount: 0, deadline: 0, v: 0, r: '0x0000000000000000000000000000000000000000000000000000000000000000', @@ -1411,6 +1487,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { [expectedDaiAmount], [ { + amount: 0, deadline: 0, v: 0, r: '0x0000000000000000000000000000000000000000000000000000000000000000', @@ -1440,6 +1517,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { [expectedDaiAmount, expectedDaiAmount], [ { + amount: 0, deadline: 0, v: 0, r: '0x0000000000000000000000000000000000000000000000000000000000000000', @@ -1477,6 +1555,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { [smallExpectedDaiAmount], [ { + amount: 0, deadline: 0, v: 0, r: '0x0000000000000000000000000000000000000000000000000000000000000000', @@ -1551,12 +1630,14 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { [expectedDaiAmountForEth, expectedDaiAmountForUsdc], [ { + amount: 0, deadline: 0, v: 0, r: '0x0000000000000000000000000000000000000000000000000000000000000000', s: '0x0000000000000000000000000000000000000000000000000000000000000000', }, { + amount: 0, deadline: 0, v: 0, r: '0x0000000000000000000000000000000000000000000000000000000000000000', @@ -1688,12 +1769,14 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { [expectedDaiAmountForEth, expectedDaiAmountForUsdc], [ { + amount: amountWETHtoSwap, deadline, v: aWETHv, r: aWETHr, s: aWETHs, }, { + amount: amountUSDCtoSwap, deadline, v: aUsdcv, r: aUsdcr, @@ -1759,6 +1842,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { [expectedDaiAmount], [ { + amount: 0, deadline: 0, v: 0, r: '0x0000000000000000000000000000000000000000000000000000000000000000', @@ -1787,6 +1871,91 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { expect(userAEthBalance).to.be.eq(Zero); expect(adapterAEthBalance).to.be.eq(Zero); }); + + it('should correctly swap all the balance when using permit', async () => { + const {users, weth, oracle, dai, aDai, aWETH, uniswapLiquiditySwapAdapter} = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + await mockUniswapRouter.setAmountToReturn(weth.address, expectedDaiAmount); + + // Remove other balance + await aWETH.connect(user).transfer(users[1].address, parseEther('90')); + const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + + // User will swap liquidity 10 aEth to aDai + const liquidityToSwap = parseEther('10'); + expect(userAEthBalanceBefore).to.be.eq(liquidityToSwap); + + // Only has 10 atokens, so all the balance will be swapped + const bigAmountToSwap = parseEther('100'); + + const chainId = BRE.network.config.chainId || BUIDLEREVM_CHAINID; + const deadline = MAX_UINT_AMOUNT; + + const ownerPrivateKey = require('../test-wallets.js').accounts[1].secretKey; + if (!ownerPrivateKey) { + throw new Error('INVALID_OWNER_PK'); + } + const aWethNonce = (await aWETH._nonces(userAddress)).toNumber(); + const aWethMsgParams = buildPermitParams( + chainId, + aWETH.address, + '1', + await aWETH.name(), + userAddress, + uniswapLiquiditySwapAdapter.address, + aWethNonce, + deadline, + bigAmountToSwap.toString() + ); + const {v, r, s} = getSignatureFromTypedData(ownerPrivateKey, aWethMsgParams); + + await expect( + uniswapLiquiditySwapAdapter.connect(user).swapAndDeposit( + [weth.address], + [dai.address], + [bigAmountToSwap], + [expectedDaiAmount], + [ + { + amount: bigAmountToSwap, + deadline, + v, + r, + s, + }, + ] + ) + ) + .to.emit(uniswapLiquiditySwapAdapter, 'Swapped') + .withArgs(weth.address, dai.address, amountWETHtoSwap.toString(), expectedDaiAmount); + + const adapterWethBalance = await weth.balanceOf(uniswapLiquiditySwapAdapter.address); + const adapterDaiBalance = await dai.balanceOf(uniswapLiquiditySwapAdapter.address); + const adapterDaiAllowance = await dai.allowance( + uniswapLiquiditySwapAdapter.address, + userAddress + ); + const userADaiBalance = await aDai.balanceOf(userAddress); + const userAEthBalance = await aWETH.balanceOf(userAddress); + const adapterAEthBalance = await aWETH.balanceOf(uniswapLiquiditySwapAdapter.address); + + expect(adapterWethBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(adapterDaiAllowance).to.be.eq(Zero); + expect(userADaiBalance).to.be.eq(expectedDaiAmount); + expect(userAEthBalance).to.be.eq(Zero); + expect(adapterAEthBalance).to.be.eq(Zero); + }); }); }); @@ -1881,27 +2050,16 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, flashloanAmount); - const params = ethers.utils.defaultAbiCoder.encode( - [ - 'address[]', - 'uint256', - 'uint256[]', - 'uint256[]', - 'uint256[]', - 'uint8[]', - 'bytes32[]', - 'bytes32[]', - ], - [ - [dai.address], - 0, - [expectedDaiAmount], - [1], - [0], - [0], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ] + const params = buildRepayAdapterParams( + [dai.address], + 0, + [expectedDaiAmount], + [1], + [0], + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'] ); await expect( @@ -1972,16 +2130,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const liquidityToSwap = amountWETHtoSwap; const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); - // IMPORTANT: Round down to work equal to solidity to get the correct value for permit call - BigNumber.config({ - ROUNDING_MODE: 1, //round down - }); - // Subtract the FL fee from the amount to be swapped 0,09% - const flashloanAmountBN = new BigNumber(liquidityToSwap.toString()).div(1.0009); - const flashloanAmount = flashloanAmountBN.toFixed(0); - const flashloanFee = flashloanAmountBN.multipliedBy(9).div(10000); - const amountToPermit = flashloanAmountBN.plus(flashloanFee); + const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); const chainId = BRE.network.config.chainId || BUIDLEREVM_CHAINID; const deadline = MAX_UINT_AMOUNT; @@ -1995,7 +2145,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { uniswapRepayAdapter.address, nonce, deadline, - amountToPermit.toFixed(0).toString() + liquidityToSwap.toString() ); const ownerPrivateKey = require('../test-wallets.js').accounts[1].secretKey; @@ -2007,18 +2157,16 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, flashloanAmount); - const params = ethers.utils.defaultAbiCoder.encode( - [ - 'address[]', - 'uint256', - 'uint256[]', - 'uint256[]', - 'uint256[]', - 'uint8[]', - 'bytes32[]', - 'bytes32[]', - ], - [[dai.address], 0, [expectedDaiAmount], [1], [deadline], [v], [r], [s]] + const params = buildRepayAdapterParams( + [dai.address], + 0, + [expectedDaiAmount], + [1], + [liquidityToSwap], + [deadline], + [v], + [r], + [s] ); await expect( @@ -2048,11 +2196,6 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { expect(userDaiStableDebtAmount).to.be.lt(expectedDaiAmount); expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); - - // Restore round up - BigNumber.config({ - ROUNDING_MODE: 0, //round up - }); }); it('should correctly swap tokens and repay debt for multiple tokens', async () => { @@ -2143,32 +2286,21 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const aWETHBalanceBefore = await aWETH.balanceOf(userAddress); const aLendBalanceBefore = await aLend.balanceOf(userAddress); - const params = ethers.utils.defaultAbiCoder.encode( + const params = buildRepayAdapterParams( + [dai.address, usdc.address], + 0, + [expectedDaiAmountForEth, expectedUsdcAmountForLend], + [1, 1], + [0, 0], + [0, 0], + [0, 0], [ - 'address[]', - 'uint256', - 'uint256[]', - 'uint256[]', - 'uint256[]', - 'uint8[]', - 'bytes32[]', - 'bytes32[]', + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000', ], [ - [dai.address, usdc.address], - 0, - [expectedDaiAmountForEth, expectedUsdcAmountForLend], - [1, 1], - [0, 0], - [0, 0], - [ - '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000', - ], - [ - '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000', - ], + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000', ] ); @@ -2282,20 +2414,13 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const aWETHBalanceBefore = await aWETH.balanceOf(userAddress); const aLendBalanceBefore = await aLend.balanceOf(userAddress); - // IMPORTANT: Round down to work equal to solidity to get the correct value for permit call - BigNumber.config({ - ROUNDING_MODE: 1, //round down - }); + const wethFlashloanAmount = new BigNumber(amountWETHtoSwap.toString()) + .div(1.0009) + .toFixed(0); - const wethFlashloanAmountBN = new BigNumber(amountWETHtoSwap.toString()).div(1.0009); - const wethFlashloanAmount = wethFlashloanAmountBN.toFixed(0); - const wethFlashloanFee = wethFlashloanAmountBN.multipliedBy(9).div(10000); - const wethAmountToPermit = wethFlashloanAmountBN.plus(wethFlashloanFee).toFixed(0); - - const lendFlashloanAmountBN = new BigNumber(amountLendToSwap.toString()).div(1.0009); - const lendFlashloanAmount = lendFlashloanAmountBN.toFixed(0); - const lendFlashloanFee = lendFlashloanAmountBN.multipliedBy(9).div(10000); - const lendAmountToPermit = lendFlashloanAmountBN.plus(lendFlashloanFee).toFixed(0); + const lendFlashloanAmount = new BigNumber(amountLendToSwap.toString()) + .div(1.0009) + .toFixed(0); const aWethNonce = (await aWETH._nonces(userAddress)).toNumber(); const aWethMsgParams = buildPermitParams( @@ -2307,7 +2432,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { uniswapRepayAdapter.address, aWethNonce, deadline, - wethAmountToPermit.toString() + amountWETHtoSwap.toString() ); const {v: aWETHv, r: aWETHr, s: aWETHs} = getSignatureFromTypedData( ownerPrivateKey, @@ -2324,7 +2449,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { uniswapRepayAdapter.address, aLendNonce, deadline, - lendAmountToPermit.toString() + amountLendToSwap.toString() ); const {v: aLendv, r: aLendr, s: aLends} = getSignatureFromTypedData( ownerPrivateKey, @@ -2334,27 +2459,16 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, wethFlashloanAmount); await mockUniswapRouter.connect(user).setAmountToSwap(lend.address, lendFlashloanAmount); - const params = ethers.utils.defaultAbiCoder.encode( - [ - 'address[]', - 'uint256', - 'uint256[]', - 'uint256[]', - 'uint256[]', - 'uint8[]', - 'bytes32[]', - 'bytes32[]', - ], - [ - [dai.address, usdc.address], - 0, - [expectedDaiAmountForEth, expectedUsdcAmountForLend], - [1, 1], - [deadline, deadline], - [aWETHv, aLendv], - [aWETHr, aLendr], - [aWETHs, aLends], - ] + const params = buildRepayAdapterParams( + [dai.address, usdc.address], + 0, + [expectedDaiAmountForEth, expectedUsdcAmountForLend], + [1, 1], + [amountWETHtoSwap, amountLendToSwap], + [deadline, deadline], + [aWETHv, aLendv], + [aWETHr, aLendr], + [aWETHs, aLends] ); await pool @@ -2384,11 +2498,6 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { expect(userUsdcStableDebtAmount).to.be.lt(expectedUsdcAmountForLend); expect(aWETHBalance).to.be.lt(aWETHBalanceBefore); expect(aLendBalance).to.be.lt(aLendBalanceBefore); - - // Restore round up - BigNumber.config({ - ROUNDING_MODE: 0, //round up - }); }); it('should revert if inconsistent params', async () => { @@ -2415,27 +2524,16 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, flashloanAmount); - const params1 = ethers.utils.defaultAbiCoder.encode( - [ - 'address[]', - 'uint256', - 'uint256[]', - 'uint256[]', - 'uint256[]', - 'uint8[]', - 'bytes32[]', - 'bytes32[]', - ], - [ - [dai.address, weth.address], - 0, - [expectedDaiAmount], - [1], - [0], - [0], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ] + const params1 = buildRepayAdapterParams( + [dai.address, weth.address], + 0, + [expectedDaiAmount], + [1], + [0], + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'] ); await expect( @@ -2452,27 +2550,16 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { ) ).to.be.revertedWith('INCONSISTENT_PARAMS'); - const params2 = ethers.utils.defaultAbiCoder.encode( - [ - 'address[]', - 'uint256', - 'uint256[]', - 'uint256[]', - 'uint256[]', - 'uint8[]', - 'bytes32[]', - 'bytes32[]', - ], - [ - [dai.address], - 0, - [expectedDaiAmount, expectedDaiAmount], - [1], - [0], - [0], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ] + const params2 = buildRepayAdapterParams( + [dai.address], + 0, + [expectedDaiAmount, expectedDaiAmount], + [1], + [0], + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'] ); await expect( @@ -2489,27 +2576,16 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { ) ).to.be.revertedWith('INCONSISTENT_PARAMS'); - const params3 = ethers.utils.defaultAbiCoder.encode( - [ - 'address[]', - 'uint256', - 'uint256[]', - 'uint256[]', - 'uint256[]', - 'uint8[]', - 'bytes32[]', - 'bytes32[]', - ], - [ - [dai.address], - 0, - [expectedDaiAmount], - [1, 1], - [0], - [0], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ] + const params3 = buildRepayAdapterParams( + [dai.address], + 0, + [expectedDaiAmount], + [1, 1], + [0], + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'] ); await expect( @@ -2526,27 +2602,16 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { ) ).to.be.revertedWith('INCONSISTENT_PARAMS'); - const params4 = ethers.utils.defaultAbiCoder.encode( - [ - 'address[]', - 'uint256', - 'uint256[]', - 'uint256[]', - 'uint256[]', - 'uint8[]', - 'bytes32[]', - 'bytes32[]', - ], - [ - [dai.address], - 0, - [expectedDaiAmount], - [1], - [0, 0], - [0], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ] + const params4 = buildRepayAdapterParams( + [dai.address], + 0, + [expectedDaiAmount], + [1], + [0], + [0, 0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'] ); await expect( @@ -2563,27 +2628,16 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { ) ).to.be.revertedWith('INCONSISTENT_PARAMS'); - const params5 = ethers.utils.defaultAbiCoder.encode( - [ - 'address[]', - 'uint256', - 'uint256[]', - 'uint256[]', - 'uint256[]', - 'uint8[]', - 'bytes32[]', - 'bytes32[]', - ], - [ - [dai.address], - 0, - [expectedDaiAmount], - [1], - [0], - [0, 0], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ] + const params5 = buildRepayAdapterParams( + [dai.address], + 0, + [expectedDaiAmount], + [1], + [0], + [0], + [0, 0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'] ); await expect( @@ -2600,30 +2654,19 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { ) ).to.be.revertedWith('INCONSISTENT_PARAMS'); - const params6 = ethers.utils.defaultAbiCoder.encode( + const params6 = buildRepayAdapterParams( + [dai.address], + 0, + [expectedDaiAmount], + [1], + [0], + [0], + [0], [ - 'address[]', - 'uint256', - 'uint256[]', - 'uint256[]', - 'uint256[]', - 'uint8[]', - 'bytes32[]', - 'bytes32[]', + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000', ], - [ - [dai.address], - 0, - [expectedDaiAmount], - [1], - [0], - [0], - [ - '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000', - ], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ] + ['0x0000000000000000000000000000000000000000000000000000000000000000'] ); await expect( @@ -2640,29 +2683,18 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { ) ).to.be.revertedWith('INCONSISTENT_PARAMS'); - const params7 = ethers.utils.defaultAbiCoder.encode( + const params7 = buildRepayAdapterParams( + [dai.address], + 0, + [expectedDaiAmount], + [1], + [0], + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], [ - 'address[]', - 'uint256', - 'uint256[]', - 'uint256[]', - 'uint256[]', - 'uint8[]', - 'bytes32[]', - 'bytes32[]', - ], - [ - [dai.address], - 0, - [expectedDaiAmount], - [1], - [0], - [0], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - [ - '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000', - ], + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000', ] ); @@ -2679,6 +2711,32 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { 0 ) ).to.be.revertedWith('INCONSISTENT_PARAMS'); + + const params8 = buildRepayAdapterParams( + [dai.address], + 0, + [expectedDaiAmount], + [1], + [0, 0], + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'] + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapRepayAdapter.address, + [weth.address], + [flashloanAmount.toString()], + [0], + userAddress, + params8, + 0 + ) + ).to.be.revertedWith('INCONSISTENT_PARAMS'); }); it('should revert if caller not lending pool', async () => { @@ -2705,27 +2763,16 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, flashloanAmount); - const params = ethers.utils.defaultAbiCoder.encode( - [ - 'address[]', - 'uint256', - 'uint256[]', - 'uint256[]', - 'uint256[]', - 'uint8[]', - 'bytes32[]', - 'bytes32[]', - ], - [ - [dai.address], - 0, - [expectedDaiAmount], - [1], - [0], - [0], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ] + const params = buildRepayAdapterParams( + [dai.address], + 0, + [expectedDaiAmount], + [1], + [0], + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'] ); await expect( @@ -2768,27 +2815,16 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, flashloanAmount); - const params = ethers.utils.defaultAbiCoder.encode( - [ - 'address[]', - 'uint256', - 'uint256[]', - 'uint256[]', - 'uint256[]', - 'uint8[]', - 'bytes32[]', - 'bytes32[]', - ], - [ - [dai.address], - 0, - [expectedDaiAmount], - [1], - [0], - [0], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ] + const params = buildRepayAdapterParams( + [dai.address], + 0, + [expectedDaiAmount], + [1], + [0], + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'] ); await expect( @@ -2830,27 +2866,16 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, flashloanAmount); - const params = ethers.utils.defaultAbiCoder.encode( - [ - 'address[]', - 'uint256', - 'uint256[]', - 'uint256[]', - 'uint256[]', - 'uint8[]', - 'bytes32[]', - 'bytes32[]', - ], - [ - [dai.address], - 0, - [expectedDaiAmount], - [1], - [0], - [0], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ] + const params = buildRepayAdapterParams( + [dai.address], + 0, + [expectedDaiAmount], + [1], + [0], + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'] ); await expect( @@ -2892,27 +2917,16 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, flashloanAmount); - const params = ethers.utils.defaultAbiCoder.encode( - [ - 'address[]', - 'uint256', - 'uint256[]', - 'uint256[]', - 'uint256[]', - 'uint8[]', - 'bytes32[]', - 'bytes32[]', - ], - [ - [dai.address], - 0, - [expectedDaiAmount], - [1], - [0], - [0], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ] + const params = buildRepayAdapterParams( + [dai.address], + 0, + [expectedDaiAmount], + [1], + [0], + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'] ); await expect( @@ -2981,27 +2995,16 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, actualWEthSwapped); - const params = ethers.utils.defaultAbiCoder.encode( - [ - 'address[]', - 'uint256', - 'uint256[]', - 'uint256[]', - 'uint256[]', - 'uint8[]', - 'bytes32[]', - 'bytes32[]', - ], - [ - [dai.address], - 0, - [expectedDaiAmount], - [1], - [0], - [0], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ] + const params = buildRepayAdapterParams( + [dai.address], + 0, + [expectedDaiAmount], + [1], + [0], + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'] ); await expect( @@ -3089,27 +3092,16 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const wethBalanceBefore = await weth.balanceOf(userAddress); - const params = ethers.utils.defaultAbiCoder.encode( - [ - 'address[]', - 'uint256', - 'uint256[]', - 'uint256[]', - 'uint256[]', - 'uint8[]', - 'bytes32[]', - 'bytes32[]', - ], - [ - [dai.address], - 1, - [expectedDaiAmount], - [1], - [0], - [0], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ] + const params = buildRepayAdapterParams( + [dai.address], + 1, + [expectedDaiAmount], + [1], + [0], + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'] ); await expect( From 6aeabbe00b05149ab8e9b7d62e857d229e29bf10 Mon Sep 17 00:00:00 2001 From: Gerardo Nardelli Date: Fri, 6 Nov 2020 12:12:40 -0300 Subject: [PATCH 15/62] Add build params unit tests helpers --- helpers/contracts-helpers.ts | 54 +++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/helpers/contracts-helpers.ts b/helpers/contracts-helpers.ts index cbe2b69c..4c67b209 100644 --- a/helpers/contracts-helpers.ts +++ b/helpers/contracts-helpers.ts @@ -1,4 +1,4 @@ -import {Contract, Signer, utils, ethers} from 'ethers'; +import {Contract, Signer, utils, ethers, BigNumberish} from 'ethers'; import {signTypedData_v4} from 'eth-sig-util'; import {fromRpcSig, ECDSASignature} from 'ethereumjs-util'; import BigNumber from 'bignumber.js'; @@ -212,3 +212,55 @@ export const getSignatureFromTypedData = ( }); return fromRpcSig(signature); }; + +export const buildLiquiditySwapParams = ( + assetToSwapToList: tEthereumAddress[], + minAmountsToReceive: BigNumberish[], + swapAllBalances: BigNumberish[], + permitAmounts: BigNumberish[], + deadlines: BigNumberish[], + v: BigNumberish[], + r: (string | Buffer)[], + s: (string | Buffer)[] +) => { + return ethers.utils.defaultAbiCoder.encode( + [ + 'address[]', + 'uint256[]', + 'bool[]', + 'uint256[]', + 'uint256[]', + 'uint8[]', + 'bytes32[]', + 'bytes32[]', + ], + [assetToSwapToList, minAmountsToReceive, swapAllBalances, permitAmounts, deadlines, v, r, s] + ); +}; + +export const buildRepayAdapterParams = ( + assetToSwapToList: tEthereumAddress[], + leftoverAction: BigNumberish, + repayAmounts: BigNumberish[], + rateModes: BigNumberish[], + permitAmounts: BigNumberish[], + deadlines: BigNumberish[], + v: BigNumberish[], + r: (string | Buffer)[], + s: (string | Buffer)[] +) => { + return ethers.utils.defaultAbiCoder.encode( + [ + 'address[]', + 'uint256', + 'uint256[]', + 'uint256[]', + 'uint256[]', + 'uint256[]', + 'uint8[]', + 'bytes32[]', + 'bytes32[]', + ], + [assetToSwapToList, leftoverAction, repayAmounts, rateModes, permitAmounts, deadlines, v, r, s] + ); +}; From d3ef60f01b3ff649ecfc0993dd1569030a134aa7 Mon Sep 17 00:00:00 2001 From: Gerardo Nardelli Date: Fri, 6 Nov 2020 15:21:27 -0300 Subject: [PATCH 16/62] Merge fix --- helpers/contracts-getters.ts | 6 +- test/uniswapAdapters.spec.ts | 178 ++++++++++++++++++----------------- 2 files changed, 94 insertions(+), 90 deletions(-) diff --git a/helpers/contracts-getters.ts b/helpers/contracts-getters.ts index 54d61b9d..03b705ba 100644 --- a/helpers/contracts-getters.ts +++ b/helpers/contracts-getters.ts @@ -278,7 +278,7 @@ export const getSelfdestructTransferMock = async (address?: tEthereumAddress) => export const getMockUniswapRouter = async (address?: tEthereumAddress) => await MockUniswapV2Router02Factory.connect( address || - (await getDb().get(`${eContractid.MockUniswapV2Router02}.${BRE.network.name}`).value()) + (await getDb().get(`${eContractid.MockUniswapV2Router02}.${DRE.network.name}`).value()) .address, await getFirstSigner() ); @@ -286,7 +286,7 @@ export const getMockUniswapRouter = async (address?: tEthereumAddress) => export const getUniswapLiquiditySwapAdapter = async (address?: tEthereumAddress) => await UniswapLiquiditySwapAdapterFactory.connect( address || - (await getDb().get(`${eContractid.UniswapLiquiditySwapAdapter}.${BRE.network.name}`).value()) + (await getDb().get(`${eContractid.UniswapLiquiditySwapAdapter}.${DRE.network.name}`).value()) .address, await getFirstSigner() ); @@ -294,6 +294,6 @@ export const getUniswapLiquiditySwapAdapter = async (address?: tEthereumAddress) export const getUniswapRepayAdapter = async (address?: tEthereumAddress) => await UniswapRepayAdapterFactory.connect( address || - (await getDb().get(`${eContractid.UniswapRepayAdapter}.${BRE.network.name}`).value()).address, + (await getDb().get(`${eContractid.UniswapRepayAdapter}.${DRE.network.name}`).value()).address, await getFirstSigner() ); diff --git a/test/uniswapAdapters.spec.ts b/test/uniswapAdapters.spec.ts index 2e23f555..96ece84e 100644 --- a/test/uniswapAdapters.spec.ts +++ b/test/uniswapAdapters.spec.ts @@ -15,7 +15,7 @@ import { import {MockUniswapV2Router02} from '../types/MockUniswapV2Router02'; import {Zero} from '@ethersproject/constants'; import BigNumber from 'bignumber.js'; -import {BRE, evmRevert, evmSnapshot} from '../helpers/misc-utils'; +import {DRE, evmRevert, evmSnapshot} from '../helpers/misc-utils'; import {ethers} from 'ethers'; import {eContractid} from '../helpers/types'; import {AToken} from '../types/AToken'; @@ -94,13 +94,13 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { expect(result['3']).to.be.eq(daiUsdValue); }); it('should work correctly with different decimals', async () => { - const {lend, usdc, uniswapLiquiditySwapAdapter, oracle} = testEnv; + const {aave, usdc, uniswapLiquiditySwapAdapter, oracle} = testEnv; const amountIn = parseEther('10'); const flashloanPremium = amountIn.mul(9).div(10000); const amountToSwap = amountIn.sub(flashloanPremium); - const lendPrice = await oracle.getAssetPrice(lend.address); + const aavePrice = await oracle.getAssetPrice(aave.address); const usdcPrice = await oracle.getAssetPrice(usdc.address); const usdPrice = await oracle.getAssetPrice(USD_ADDRESS); @@ -114,8 +114,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { .mul('1000000') // usdc 6 decimals .div(expectedUSDCAmount.mul(parseEther('1'))); - const lendUsdValue = amountIn - .mul(lendPrice) + const aaveUsdValue = amountIn + .mul(aavePrice) .div(parseEther('1')) .mul(usdPrice) .div(parseEther('1')); @@ -128,20 +128,20 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await mockUniswapRouter.setAmountOut( amountToSwap, - lend.address, + aave.address, usdc.address, expectedUSDCAmount ); const result = await uniswapLiquiditySwapAdapter.getAmountsOut( amountIn, - lend.address, + aave.address, usdc.address ); expect(result['0']).to.be.eq(expectedUSDCAmount); expect(result['1']).to.be.eq(outPerInPrice); - expect(result['2']).to.be.eq(lendUsdValue); + expect(result['2']).to.be.eq(aaveUsdValue); expect(result['3']).to.be.eq(usdcUsdValue); }); }); @@ -193,13 +193,13 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { expect(result['3']).to.be.eq(daiUsdValue); }); it('should work correctly with different decimals', async () => { - const {lend, usdc, uniswapLiquiditySwapAdapter, oracle} = testEnv; + const {aave, usdc, uniswapLiquiditySwapAdapter, oracle} = testEnv; const amountIn = parseEther('10'); const flashloanPremium = amountIn.mul(9).div(10000); const amountToSwap = amountIn.sub(flashloanPremium); - const lendPrice = await oracle.getAssetPrice(lend.address); + const aavePrice = await oracle.getAssetPrice(aave.address); const usdcPrice = await oracle.getAssetPrice(usdc.address); const usdPrice = await oracle.getAssetPrice(USD_ADDRESS); @@ -213,8 +213,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { .mul(parseEther('1')) .div(amountToSwap.mul('1000000')); // usdc 6 decimals - const lendUsdValue = amountToSwap - .mul(lendPrice) + const aaveUsdValue = amountToSwap + .mul(aavePrice) .div(parseEther('1')) .mul(usdPrice) .div(parseEther('1')); @@ -225,17 +225,17 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { .mul(usdPrice) .div(parseEther('1')); - await mockUniswapRouter.setAmountIn(amountOut, lend.address, usdc.address, amountIn); + await mockUniswapRouter.setAmountIn(amountOut, aave.address, usdc.address, amountIn); const result = await uniswapLiquiditySwapAdapter.getAmountsIn( amountOut, - lend.address, + aave.address, usdc.address ); expect(result['0']).to.be.eq(amountToSwap); expect(result['1']).to.be.eq(inPerOutPrice); - expect(result['2']).to.be.eq(lendUsdValue); + expect(result['2']).to.be.eq(aaveUsdValue); expect(result['3']).to.be.eq(usdcUsdValue); }); }); @@ -474,7 +474,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { } = testEnv; const user = users[0].signer; const userAddress = users[0].address; - const chainId = BRE.network.config.chainId || BUIDLEREVM_CHAINID; + const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID; const deadline = MAX_UINT_AMOUNT; const ownerPrivateKey = require('../test-wallets.js').accounts[1].secretKey; @@ -628,7 +628,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { // Subtract the FL fee from the amount to be swapped 0,09% const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); - const chainId = BRE.network.config.chainId || BUIDLEREVM_CHAINID; + const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID; const deadline = MAX_UINT_AMOUNT; const nonce = (await aWETH._nonces(userAddress)).toNumber(); const msgParams = buildPermitParams( @@ -1209,7 +1209,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const liquidityToSwap = parseEther('10'); expect(userAEthBalanceBefore).to.be.eq(liquidityToSwap); - const chainId = BRE.network.config.chainId || BUIDLEREVM_CHAINID; + const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID; const deadline = MAX_UINT_AMOUNT; const nonce = (await aWETH._nonces(userAddress)).toNumber(); const msgParams = buildPermitParams( @@ -1373,7 +1373,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const liquidityToSwap = parseEther('10'); const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); - const chainId = BRE.network.config.chainId || BUIDLEREVM_CHAINID; + const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID; const deadline = MAX_UINT_AMOUNT; const nonce = (await aWETH._nonces(userAddress)).toNumber(); const msgParams = buildPermitParams( @@ -1680,7 +1680,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { } = testEnv; const user = users[0].signer; const userAddress = users[0].address; - const chainId = BRE.network.config.chainId || BUIDLEREVM_CHAINID; + const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID; const deadline = MAX_UINT_AMOUNT; const ownerPrivateKey = require('../test-wallets.js').accounts[1].secretKey; @@ -1898,7 +1898,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { // Only has 10 atokens, so all the balance will be swapped const bigAmountToSwap = parseEther('100'); - const chainId = BRE.network.config.chainId || BUIDLEREVM_CHAINID; + const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID; const deadline = MAX_UINT_AMOUNT; const ownerPrivateKey = require('../test-wallets.js').accounts[1].secretKey; @@ -1974,7 +1974,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { describe('executeOperation', () => { beforeEach(async () => { - const {users, weth, dai, usdc, lend, pool, deployer} = testEnv; + const {users, weth, dai, usdc, aave, pool, deployer} = testEnv; const userAddress = users[0].address; // Provide liquidity @@ -1982,7 +1982,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await dai.approve(pool.address, parseEther('20000')); await pool.deposit(dai.address, parseEther('20000'), deployer.address, 0); - const usdcLiquidity = await convertToCurrencyDecimals(usdc.address, '20000'); + const usdcLiquidity = await convertToCurrencyDecimals(usdc.address, '2000000'); await usdc.mint(usdcLiquidity); await usdc.approve(pool.address, usdcLiquidity); await pool.deposit(usdc.address, usdcLiquidity, deployer.address, 0); @@ -1991,18 +1991,22 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await weth.approve(pool.address, parseEther('100')); await pool.deposit(weth.address, parseEther('100'), deployer.address, 0); - await lend.mint(parseEther('1000000')); - await lend.approve(pool.address, parseEther('1000000')); - await pool.deposit(lend.address, parseEther('1000000'), deployer.address, 0); + await aave.mint(parseEther('1000000')); + await aave.approve(pool.address, parseEther('1000000')); + await pool.deposit(aave.address, parseEther('1000000'), deployer.address, 0); // Make a deposit for user - await weth.mint(parseEther('100')); - await weth.approve(pool.address, parseEther('100')); - await pool.deposit(weth.address, parseEther('100'), userAddress, 0); + await weth.mint(parseEther('1000')); + await weth.approve(pool.address, parseEther('1000')); + await pool.deposit(weth.address, parseEther('1000'), userAddress, 0); - await lend.mint(parseEther('1000000')); - await lend.approve(pool.address, parseEther('1000000')); - await pool.deposit(lend.address, parseEther('1000000'), userAddress, 0); + await aave.mint(parseEther('1000000')); + await aave.approve(pool.address, parseEther('1000000')); + await pool.deposit(aave.address, parseEther('1000000'), userAddress, 0); + + await usdc.mint(usdcLiquidity); + await usdc.approve(pool.address, usdcLiquidity); + await pool.deposit(usdc.address, usdcLiquidity, userAddress, 0); }); it('should correctly swap tokens and repay debt', async () => { @@ -2133,7 +2137,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { // Subtract the FL fee from the amount to be swapped 0,09% const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); - const chainId = BRE.network.config.chainId || BUIDLEREVM_CHAINID; + const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID; const deadline = MAX_UINT_AMOUNT; const nonce = (await aWETH._nonces(userAddress)).toNumber(); const msgParams = buildPermitParams( @@ -2206,7 +2210,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { oracle, dai, uniswapRepayAdapter, - lend, + aave, usdc, helpersContract, aWETH, @@ -2215,7 +2219,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const userAddress = users[0].address; const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); - const amountLendToSwap = await convertToCurrencyDecimals(lend.address, '1'); + const amountAaveToSwap = parseEther('1'); const daiPrice = await oracle.getAssetPrice(dai.address); const expectedDaiAmountForEth = await convertToCurrencyDecimals( @@ -2223,17 +2227,17 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) ); - const lendPrice = await oracle.getAssetPrice(lend.address); + const aavePrice = await oracle.getAssetPrice(aave.address); const usdcPrice = await oracle.getAssetPrice(usdc.address); - const collateralDecimals = (await lend.decimals()).toString(); + const collateralDecimals = (await aave.decimals()).toString(); const principalDecimals = (await usdc.decimals()).toString(); - const expectedUsdcAmountForLend = await convertToCurrencyDecimals( + const expectedUsdcAmountForAave = await convertToCurrencyDecimals( usdc.address, - new BigNumber(amountLendToSwap.toString()) + new BigNumber(amountAaveToSwap.toString()) .times( - new BigNumber(lendPrice.toString()).times(new BigNumber(10).pow(principalDecimals)) + new BigNumber(aavePrice.toString()).times(new BigNumber(10).pow(principalDecimals)) ) .div( new BigNumber(usdcPrice.toString()).times(new BigNumber(10).pow(collateralDecimals)) @@ -2243,7 +2247,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { // Open user Debt await pool.connect(user).borrow(dai.address, expectedDaiAmountForEth, 1, 0, userAddress); - await pool.connect(user).borrow(usdc.address, expectedUsdcAmountForLend, 1, 0, userAddress); + await pool.connect(user).borrow(usdc.address, expectedUsdcAmountForAave, 1, 0, userAddress); const daiStableDebtTokenAddress = ( await helpersContract.getReserveTokensAddresses(dai.address) @@ -2270,26 +2274,26 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const wethFlashloanAmount = new BigNumber(amountWETHtoSwap.toString()) .div(1.0009) .toFixed(0); - const lendFlashloanAmount = new BigNumber(amountLendToSwap.toString()) + const aaveFlashloanAmount = new BigNumber(amountAaveToSwap.toString()) .div(1.0009) .toFixed(0); await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, wethFlashloanAmount); - await mockUniswapRouter.connect(user).setAmountToSwap(lend.address, lendFlashloanAmount); + await mockUniswapRouter.connect(user).setAmountToSwap(aave.address, aaveFlashloanAmount); await aWETH.connect(user).approve(uniswapRepayAdapter.address, amountWETHtoSwap); - const lendData = await pool.getReserveData(lend.address); - const aLend = await getContract(eContractid.AToken, lendData.aTokenAddress); - await aLend.connect(user).approve(uniswapRepayAdapter.address, amountLendToSwap); + const aaveData = await pool.getReserveData(aave.address); + const aAave = await getContract(eContractid.AToken, aaveData.aTokenAddress); + await aAave.connect(user).approve(uniswapRepayAdapter.address, amountAaveToSwap); const aWETHBalanceBefore = await aWETH.balanceOf(userAddress); - const aLendBalanceBefore = await aLend.balanceOf(userAddress); + const aAaveBalanceBefore = await aAave.balanceOf(userAddress); const params = buildRepayAdapterParams( [dai.address, usdc.address], 0, - [expectedDaiAmountForEth, expectedUsdcAmountForLend], + [expectedDaiAmountForEth, expectedUsdcAmountForAave], [1, 1], [0, 0], [0, 0], @@ -2308,8 +2312,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { .connect(user) .flashLoan( uniswapRepayAdapter.address, - [weth.address, lend.address], - [wethFlashloanAmount.toString(), lendFlashloanAmount.toString()], + [weth.address, aave.address], + [wethFlashloanAmount.toString(), aaveFlashloanAmount.toString()], [0, 0], userAddress, params, @@ -2321,16 +2325,16 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); const userUsdcStableDebtAmount = await usdcStableDebtContract.balanceOf(userAddress); const aWETHBalance = await aWETH.balanceOf(userAddress); - const aLendBalance = await aLend.balanceOf(userAddress); + const aAaveBalance = await aAave.balanceOf(userAddress); expect(adapterWethBalance).to.be.eq(Zero); expect(adapterDaiBalance).to.be.eq(Zero); expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmountForEth); expect(userDaiStableDebtAmount).to.be.lt(expectedDaiAmountForEth); - expect(userUsdcStableDebtAmountBefore).to.be.gte(expectedUsdcAmountForLend); - expect(userUsdcStableDebtAmount).to.be.lt(expectedUsdcAmountForLend); + expect(userUsdcStableDebtAmountBefore).to.be.gte(expectedUsdcAmountForAave); + expect(userUsdcStableDebtAmount).to.be.lt(expectedUsdcAmountForAave); expect(aWETHBalance).to.be.lt(aWETHBalanceBefore); - expect(aLendBalance).to.be.lt(aLendBalanceBefore); + expect(aAaveBalance).to.be.lt(aAaveBalanceBefore); }); it('should swap tokens and repay debt for multiple tokens using permit', async () => { @@ -2341,14 +2345,14 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { oracle, dai, uniswapRepayAdapter, - lend, + aave, usdc, helpersContract, aWETH, } = testEnv; const user = users[0].signer; const userAddress = users[0].address; - const chainId = BRE.network.config.chainId || BUIDLEREVM_CHAINID; + const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID; const deadline = MAX_UINT_AMOUNT; const ownerPrivateKey = require('../test-wallets.js').accounts[1].secretKey; @@ -2357,7 +2361,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { } const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); - const amountLendToSwap = await convertToCurrencyDecimals(lend.address, '1'); + const amountAaveToSwap = await convertToCurrencyDecimals(aave.address, '1'); const daiPrice = await oracle.getAssetPrice(dai.address); const expectedDaiAmountForEth = await convertToCurrencyDecimals( @@ -2365,17 +2369,17 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) ); - const lendPrice = await oracle.getAssetPrice(lend.address); + const aavePrice = await oracle.getAssetPrice(aave.address); const usdcPrice = await oracle.getAssetPrice(usdc.address); - const collateralDecimals = (await lend.decimals()).toString(); + const collateralDecimals = (await aave.decimals()).toString(); const principalDecimals = (await usdc.decimals()).toString(); - const expectedUsdcAmountForLend = await convertToCurrencyDecimals( + const expectedUsdcAmountForAave = await convertToCurrencyDecimals( usdc.address, - new BigNumber(amountLendToSwap.toString()) + new BigNumber(amountAaveToSwap.toString()) .times( - new BigNumber(lendPrice.toString()).times(new BigNumber(10).pow(principalDecimals)) + new BigNumber(aavePrice.toString()).times(new BigNumber(10).pow(principalDecimals)) ) .div( new BigNumber(usdcPrice.toString()).times(new BigNumber(10).pow(collateralDecimals)) @@ -2385,7 +2389,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { // Open user Debt await pool.connect(user).borrow(dai.address, expectedDaiAmountForEth, 1, 0, userAddress); - await pool.connect(user).borrow(usdc.address, expectedUsdcAmountForLend, 1, 0, userAddress); + await pool.connect(user).borrow(usdc.address, expectedUsdcAmountForAave, 1, 0, userAddress); const daiStableDebtTokenAddress = ( await helpersContract.getReserveTokensAddresses(dai.address) @@ -2408,17 +2412,17 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); const userUsdcStableDebtAmountBefore = await usdcStableDebtContract.balanceOf(userAddress); - const lendData = await pool.getReserveData(lend.address); - const aLend = await getContract(eContractid.AToken, lendData.aTokenAddress); + const aaveData = await pool.getReserveData(aave.address); + const aAave = await getContract(eContractid.AToken, aaveData.aTokenAddress); const aWETHBalanceBefore = await aWETH.balanceOf(userAddress); - const aLendBalanceBefore = await aLend.balanceOf(userAddress); + const aAaveBalanceBefore = await aAave.balanceOf(userAddress); const wethFlashloanAmount = new BigNumber(amountWETHtoSwap.toString()) .div(1.0009) .toFixed(0); - const lendFlashloanAmount = new BigNumber(amountLendToSwap.toString()) + const aaveFlashloanAmount = new BigNumber(amountAaveToSwap.toString()) .div(1.0009) .toFixed(0); @@ -2439,44 +2443,44 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { aWethMsgParams ); - const aLendNonce = (await aLend._nonces(userAddress)).toNumber(); - const aLendMsgParams = buildPermitParams( + const aAaveNonce = (await aAave._nonces(userAddress)).toNumber(); + const aAaveMsgParams = buildPermitParams( chainId, - aLend.address, + aAave.address, '1', - await aLend.name(), + await aAave.name(), userAddress, uniswapRepayAdapter.address, - aLendNonce, + aAaveNonce, deadline, - amountLendToSwap.toString() + amountAaveToSwap.toString() ); - const {v: aLendv, r: aLendr, s: aLends} = getSignatureFromTypedData( + const {v: aAavev, r: aAaver, s: aAaves} = getSignatureFromTypedData( ownerPrivateKey, - aLendMsgParams + aAaveMsgParams ); await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, wethFlashloanAmount); - await mockUniswapRouter.connect(user).setAmountToSwap(lend.address, lendFlashloanAmount); + await mockUniswapRouter.connect(user).setAmountToSwap(aave.address, aaveFlashloanAmount); const params = buildRepayAdapterParams( [dai.address, usdc.address], 0, - [expectedDaiAmountForEth, expectedUsdcAmountForLend], + [expectedDaiAmountForEth, expectedUsdcAmountForAave], [1, 1], - [amountWETHtoSwap, amountLendToSwap], + [amountWETHtoSwap, amountAaveToSwap], [deadline, deadline], - [aWETHv, aLendv], - [aWETHr, aLendr], - [aWETHs, aLends] + [aWETHv, aAavev], + [aWETHr, aAaver], + [aWETHs, aAaves] ); await pool .connect(user) .flashLoan( uniswapRepayAdapter.address, - [weth.address, lend.address], - [wethFlashloanAmount.toString(), lendFlashloanAmount.toString()], + [weth.address, aave.address], + [wethFlashloanAmount.toString(), aaveFlashloanAmount.toString()], [0, 0], userAddress, params, @@ -2488,16 +2492,16 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); const userUsdcStableDebtAmount = await usdcStableDebtContract.balanceOf(userAddress); const aWETHBalance = await aWETH.balanceOf(userAddress); - const aLendBalance = await aLend.balanceOf(userAddress); + const aAaveBalance = await aAave.balanceOf(userAddress); expect(adapterWethBalance).to.be.eq(Zero); expect(adapterDaiBalance).to.be.eq(Zero); expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmountForEth); expect(userDaiStableDebtAmount).to.be.lt(expectedDaiAmountForEth); - expect(userUsdcStableDebtAmountBefore).to.be.gte(expectedUsdcAmountForLend); - expect(userUsdcStableDebtAmount).to.be.lt(expectedUsdcAmountForLend); + expect(userUsdcStableDebtAmountBefore).to.be.gte(expectedUsdcAmountForAave); + expect(userUsdcStableDebtAmount).to.be.lt(expectedUsdcAmountForAave); expect(aWETHBalance).to.be.lt(aWETHBalanceBefore); - expect(aLendBalance).to.be.lt(aLendBalanceBefore); + expect(aAaveBalance).to.be.lt(aAaveBalanceBefore); }); it('should revert if inconsistent params', async () => { From 4fb43f7aff489aa170ca380a894157467a69aff2 Mon Sep 17 00:00:00 2001 From: Gerardo Nardelli Date: Mon, 9 Nov 2020 15:27:18 -0300 Subject: [PATCH 17/62] Remove not used batch capabilities on repay adapter --- contracts/adapters/UniswapRepayAdapter.sol | 99 ++- helpers/contracts-helpers.ts | 34 +- test/uniswapAdapters.spec.ts | 671 ++------------------- 3 files changed, 122 insertions(+), 682 deletions(-) diff --git a/contracts/adapters/UniswapRepayAdapter.sol b/contracts/adapters/UniswapRepayAdapter.sol index 46d9c5b0..dba64afd 100644 --- a/contracts/adapters/UniswapRepayAdapter.sol +++ b/contracts/adapters/UniswapRepayAdapter.sol @@ -16,11 +16,11 @@ import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol'; contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { struct RepayParams { - address[] assetToSwapToList; + address assetToSwapTo; LeftoverAction leftOverAction; - uint256[] repayAmounts; - uint256[] rateModes; - PermitParams permitParams; + uint256 repayAmount; + uint256 rateMode; + PermitSignature permitSignature; } constructor( @@ -41,17 +41,17 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { * @param premiums Fee of the flash loan * @param initiator Address of the user * @param params Additional variadic field to include extra params. Expected parameters: - * address[] assetToSwapToList List of the addresses of the reserve to be swapped to and repay + * address Address of the reserve to be swapped to and repay * uint256 leftOverAction Flag indicating what to do with the left over balance from the swap: * (0) Deposit back * (1) Direct transfer to user - * uint256[] repayAmounts List of amounts of debt to be repaid - * uint256[] rateModes List of the rate modes of the debt to be repaid - * uint256[] permitAmount List of amounts for the permit signature - * uint256[] deadline List of deadlines for the permit signature - * uint8[] v List of v param for the permit signature - * bytes32[] r List of r param for the permit signature - * bytes32[] s List of s param for the permit signature + * uint256 repayAmount Amount of debt to be repaid + * uint256 rateMode Rate modes of the debt to be repaid + * uint256 permitAmount Amount for the permit signature + * uint256 deadline Deadline for the permit signature + * uint8 v V param for the permit signature + * bytes32 r R param for the permit signature + * bytes32 s S param for the permit signature */ function executeOperation( address[] calldata assets, @@ -64,36 +64,17 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { RepayParams memory decodedParams = _decodeParams(params); - require( - assets.length == decodedParams.assetToSwapToList.length - && assets.length == decodedParams.repayAmounts.length - && assets.length == decodedParams.rateModes.length - && assets.length == decodedParams.permitParams.amount.length - && assets.length == decodedParams.permitParams.deadline.length - && assets.length == decodedParams.permitParams.v.length - && assets.length == decodedParams.permitParams.r.length - && assets.length == decodedParams.permitParams.s.length, - 'INCONSISTENT_PARAMS'); - - for (uint256 i = 0; i < assets.length; i++) { _swapAndRepay( - assets[i], - decodedParams.assetToSwapToList[i], - amounts[i], - decodedParams.repayAmounts[i], - decodedParams.rateModes[i], + assets[0], + decodedParams.assetToSwapTo, + amounts[0], + decodedParams.repayAmount, + decodedParams.rateMode, initiator, decodedParams.leftOverAction, - premiums[i], - PermitSignature( - decodedParams.permitParams.amount[i], - decodedParams.permitParams.deadline[i], - decodedParams.permitParams.v[i], - decodedParams.permitParams.r[i], - decodedParams.permitParams.s[i] - ) + premiums[0], + decodedParams.permitSignature ); - } return true; } @@ -138,38 +119,38 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { /** * @dev Decodes debt information encoded in flashloan params * @param params Additional variadic field to include extra params. Expected parameters: - * address[] assetToSwapToList List of the addresses of the reserve to be swapped to and repay + * address Address of the reserve to be swapped to and repay * uint256 leftOverAction Flag indicating what to do with the left over balance from the swap: * (0) Deposit back * (1) Direct transfer to user - * uint256[] repayAmounts List of amounts of debt to be repaid - * uint256[] rateModes List of the rate modes of the debt to be repaid - * uint256[] permitAmount List of amounts for the permit signature - * uint256[] deadline List of deadlines for the permit signature - * uint8[] v List of v param for the permit signature - * bytes32[] r List of r param for the permit signature - * bytes32[] s List of s param for the permit signature + * uint256 repayAmount Amount of debt to be repaid + * uint256 rateMode Rate modes of the debt to be repaid + * uint256 permitAmount Amount for the permit signature + * uint256 deadline Deadline for the permit signature + * uint8 v V param for the permit signature + * bytes32 r R param for the permit signature + * bytes32 s S param for the permit signature * @return RepayParams struct containing decoded params */ function _decodeParams(bytes memory params) internal pure returns (RepayParams memory) { ( - address[] memory assetToSwapToList, + address assetToSwapTo, LeftoverAction leftOverAction, - uint256[] memory repayAmounts, - uint256[] memory rateModes, - uint256[] memory permitAmount, - uint256[] memory deadline, - uint8[] memory v, - bytes32[] memory r, - bytes32[] memory s - ) = abi.decode(params, (address[], LeftoverAction, uint256[], uint256[], uint256[], uint256[], uint8[], bytes32[], bytes32[])); + uint256 repayAmount, + uint256 rateMode, + uint256 permitAmount, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) = abi.decode(params, (address, LeftoverAction, uint256, uint256, uint256, uint256, uint8, bytes32, bytes32)); return RepayParams( - assetToSwapToList, + assetToSwapTo, leftOverAction, - repayAmounts, - rateModes, - PermitParams( + repayAmount, + rateMode, + PermitSignature( permitAmount, deadline, v, diff --git a/helpers/contracts-helpers.ts b/helpers/contracts-helpers.ts index d3d226af..e2ccf3c8 100644 --- a/helpers/contracts-helpers.ts +++ b/helpers/contracts-helpers.ts @@ -240,28 +240,28 @@ export const buildLiquiditySwapParams = ( }; export const buildRepayAdapterParams = ( - assetToSwapToList: tEthereumAddress[], + assetToSwapTo: tEthereumAddress, leftoverAction: BigNumberish, - repayAmounts: BigNumberish[], - rateModes: BigNumberish[], - permitAmounts: BigNumberish[], - deadlines: BigNumberish[], - v: BigNumberish[], - r: (string | Buffer)[], - s: (string | Buffer)[] + repayAmount: BigNumberish, + rateMode: BigNumberish, + permitAmount: BigNumberish, + deadline: BigNumberish, + v: BigNumberish, + r: string | Buffer, + s: string | Buffer ) => { return ethers.utils.defaultAbiCoder.encode( [ - 'address[]', + 'address', 'uint256', - 'uint256[]', - 'uint256[]', - 'uint256[]', - 'uint256[]', - 'uint8[]', - 'bytes32[]', - 'bytes32[]', + 'uint256', + 'uint256', + 'uint256', + 'uint256', + 'uint8', + 'bytes32', + 'bytes32', ], - [assetToSwapToList, leftoverAction, repayAmounts, rateModes, permitAmounts, deadlines, v, r, s] + [assetToSwapTo, leftoverAction, repayAmount, rateMode, permitAmount, deadline, v, r, s] ); }; diff --git a/test/uniswapAdapters.spec.ts b/test/uniswapAdapters.spec.ts index 96ece84e..a3ea4dbe 100644 --- a/test/uniswapAdapters.spec.ts +++ b/test/uniswapAdapters.spec.ts @@ -1959,7 +1959,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { }); }); - describe('UniswapRepayAdapter', () => { + describe.only('UniswapRepayAdapter', () => { describe('constructor', () => { it('should deploy with correct parameters', async () => { const {addressesProvider} = testEnv; @@ -2055,15 +2055,15 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, flashloanAmount); const params = buildRepayAdapterParams( - [dai.address], + dai.address, 0, - [expectedDaiAmount], - [1], - [0], - [0], - [0], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'] + expectedDaiAmount, + 1, + 0, + 0, + 0, + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000' ); await expect( @@ -2162,15 +2162,15 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, flashloanAmount); const params = buildRepayAdapterParams( - [dai.address], + dai.address, 0, - [expectedDaiAmount], - [1], - [liquidityToSwap], - [deadline], - [v], - [r], - [s] + expectedDaiAmount, + 1, + liquidityToSwap, + deadline, + v, + r, + s ); await expect( @@ -2202,547 +2202,6 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); }); - it('should correctly swap tokens and repay debt for multiple tokens', async () => { - const { - users, - pool, - weth, - oracle, - dai, - uniswapRepayAdapter, - aave, - usdc, - helpersContract, - aWETH, - } = testEnv; - const user = users[0].signer; - const userAddress = users[0].address; - - const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); - const amountAaveToSwap = parseEther('1'); - - const daiPrice = await oracle.getAssetPrice(dai.address); - const expectedDaiAmountForEth = await convertToCurrencyDecimals( - dai.address, - new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) - ); - - const aavePrice = await oracle.getAssetPrice(aave.address); - const usdcPrice = await oracle.getAssetPrice(usdc.address); - - const collateralDecimals = (await aave.decimals()).toString(); - const principalDecimals = (await usdc.decimals()).toString(); - - const expectedUsdcAmountForAave = await convertToCurrencyDecimals( - usdc.address, - new BigNumber(amountAaveToSwap.toString()) - .times( - new BigNumber(aavePrice.toString()).times(new BigNumber(10).pow(principalDecimals)) - ) - .div( - new BigNumber(usdcPrice.toString()).times(new BigNumber(10).pow(collateralDecimals)) - ) - .toFixed(0) - ); - - // Open user Debt - await pool.connect(user).borrow(dai.address, expectedDaiAmountForEth, 1, 0, userAddress); - await pool.connect(user).borrow(usdc.address, expectedUsdcAmountForAave, 1, 0, userAddress); - - const daiStableDebtTokenAddress = ( - await helpersContract.getReserveTokensAddresses(dai.address) - ).stableDebtTokenAddress; - - const daiStableDebtContract = await getContract( - eContractid.StableDebtToken, - daiStableDebtTokenAddress - ); - - const usdcStableDebtTokenAddress = ( - await helpersContract.getReserveTokensAddresses(usdc.address) - ).stableDebtTokenAddress; - - const usdcStableDebtContract = await getContract( - eContractid.StableDebtToken, - usdcStableDebtTokenAddress - ); - - const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); - const userUsdcStableDebtAmountBefore = await usdcStableDebtContract.balanceOf(userAddress); - - // Subtract the FL fee from the amount to be swapped 0,09% - const wethFlashloanAmount = new BigNumber(amountWETHtoSwap.toString()) - .div(1.0009) - .toFixed(0); - const aaveFlashloanAmount = new BigNumber(amountAaveToSwap.toString()) - .div(1.0009) - .toFixed(0); - - await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, wethFlashloanAmount); - await mockUniswapRouter.connect(user).setAmountToSwap(aave.address, aaveFlashloanAmount); - - await aWETH.connect(user).approve(uniswapRepayAdapter.address, amountWETHtoSwap); - - const aaveData = await pool.getReserveData(aave.address); - const aAave = await getContract(eContractid.AToken, aaveData.aTokenAddress); - await aAave.connect(user).approve(uniswapRepayAdapter.address, amountAaveToSwap); - - const aWETHBalanceBefore = await aWETH.balanceOf(userAddress); - const aAaveBalanceBefore = await aAave.balanceOf(userAddress); - - const params = buildRepayAdapterParams( - [dai.address, usdc.address], - 0, - [expectedDaiAmountForEth, expectedUsdcAmountForAave], - [1, 1], - [0, 0], - [0, 0], - [0, 0], - [ - '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000', - ], - [ - '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000', - ] - ); - - await pool - .connect(user) - .flashLoan( - uniswapRepayAdapter.address, - [weth.address, aave.address], - [wethFlashloanAmount.toString(), aaveFlashloanAmount.toString()], - [0, 0], - userAddress, - params, - 0 - ); - - const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); - const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); - const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); - const userUsdcStableDebtAmount = await usdcStableDebtContract.balanceOf(userAddress); - const aWETHBalance = await aWETH.balanceOf(userAddress); - const aAaveBalance = await aAave.balanceOf(userAddress); - - expect(adapterWethBalance).to.be.eq(Zero); - expect(adapterDaiBalance).to.be.eq(Zero); - expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmountForEth); - expect(userDaiStableDebtAmount).to.be.lt(expectedDaiAmountForEth); - expect(userUsdcStableDebtAmountBefore).to.be.gte(expectedUsdcAmountForAave); - expect(userUsdcStableDebtAmount).to.be.lt(expectedUsdcAmountForAave); - expect(aWETHBalance).to.be.lt(aWETHBalanceBefore); - expect(aAaveBalance).to.be.lt(aAaveBalanceBefore); - }); - - it('should swap tokens and repay debt for multiple tokens using permit', async () => { - const { - users, - pool, - weth, - oracle, - dai, - uniswapRepayAdapter, - aave, - usdc, - helpersContract, - aWETH, - } = testEnv; - const user = users[0].signer; - const userAddress = users[0].address; - const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID; - const deadline = MAX_UINT_AMOUNT; - - const ownerPrivateKey = require('../test-wallets.js').accounts[1].secretKey; - if (!ownerPrivateKey) { - throw new Error('INVALID_OWNER_PK'); - } - - const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); - const amountAaveToSwap = await convertToCurrencyDecimals(aave.address, '1'); - - const daiPrice = await oracle.getAssetPrice(dai.address); - const expectedDaiAmountForEth = await convertToCurrencyDecimals( - dai.address, - new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) - ); - - const aavePrice = await oracle.getAssetPrice(aave.address); - const usdcPrice = await oracle.getAssetPrice(usdc.address); - - const collateralDecimals = (await aave.decimals()).toString(); - const principalDecimals = (await usdc.decimals()).toString(); - - const expectedUsdcAmountForAave = await convertToCurrencyDecimals( - usdc.address, - new BigNumber(amountAaveToSwap.toString()) - .times( - new BigNumber(aavePrice.toString()).times(new BigNumber(10).pow(principalDecimals)) - ) - .div( - new BigNumber(usdcPrice.toString()).times(new BigNumber(10).pow(collateralDecimals)) - ) - .toFixed(0) - ); - - // Open user Debt - await pool.connect(user).borrow(dai.address, expectedDaiAmountForEth, 1, 0, userAddress); - await pool.connect(user).borrow(usdc.address, expectedUsdcAmountForAave, 1, 0, userAddress); - - const daiStableDebtTokenAddress = ( - await helpersContract.getReserveTokensAddresses(dai.address) - ).stableDebtTokenAddress; - - const daiStableDebtContract = await getContract( - eContractid.StableDebtToken, - daiStableDebtTokenAddress - ); - - const usdcStableDebtTokenAddress = ( - await helpersContract.getReserveTokensAddresses(usdc.address) - ).stableDebtTokenAddress; - - const usdcStableDebtContract = await getContract( - eContractid.StableDebtToken, - usdcStableDebtTokenAddress - ); - - const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); - const userUsdcStableDebtAmountBefore = await usdcStableDebtContract.balanceOf(userAddress); - - const aaveData = await pool.getReserveData(aave.address); - const aAave = await getContract(eContractid.AToken, aaveData.aTokenAddress); - - const aWETHBalanceBefore = await aWETH.balanceOf(userAddress); - const aAaveBalanceBefore = await aAave.balanceOf(userAddress); - - const wethFlashloanAmount = new BigNumber(amountWETHtoSwap.toString()) - .div(1.0009) - .toFixed(0); - - const aaveFlashloanAmount = new BigNumber(amountAaveToSwap.toString()) - .div(1.0009) - .toFixed(0); - - const aWethNonce = (await aWETH._nonces(userAddress)).toNumber(); - const aWethMsgParams = buildPermitParams( - chainId, - aWETH.address, - '1', - await aWETH.name(), - userAddress, - uniswapRepayAdapter.address, - aWethNonce, - deadline, - amountWETHtoSwap.toString() - ); - const {v: aWETHv, r: aWETHr, s: aWETHs} = getSignatureFromTypedData( - ownerPrivateKey, - aWethMsgParams - ); - - const aAaveNonce = (await aAave._nonces(userAddress)).toNumber(); - const aAaveMsgParams = buildPermitParams( - chainId, - aAave.address, - '1', - await aAave.name(), - userAddress, - uniswapRepayAdapter.address, - aAaveNonce, - deadline, - amountAaveToSwap.toString() - ); - const {v: aAavev, r: aAaver, s: aAaves} = getSignatureFromTypedData( - ownerPrivateKey, - aAaveMsgParams - ); - - await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, wethFlashloanAmount); - await mockUniswapRouter.connect(user).setAmountToSwap(aave.address, aaveFlashloanAmount); - - const params = buildRepayAdapterParams( - [dai.address, usdc.address], - 0, - [expectedDaiAmountForEth, expectedUsdcAmountForAave], - [1, 1], - [amountWETHtoSwap, amountAaveToSwap], - [deadline, deadline], - [aWETHv, aAavev], - [aWETHr, aAaver], - [aWETHs, aAaves] - ); - - await pool - .connect(user) - .flashLoan( - uniswapRepayAdapter.address, - [weth.address, aave.address], - [wethFlashloanAmount.toString(), aaveFlashloanAmount.toString()], - [0, 0], - userAddress, - params, - 0 - ); - - const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); - const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); - const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); - const userUsdcStableDebtAmount = await usdcStableDebtContract.balanceOf(userAddress); - const aWETHBalance = await aWETH.balanceOf(userAddress); - const aAaveBalance = await aAave.balanceOf(userAddress); - - expect(adapterWethBalance).to.be.eq(Zero); - expect(adapterDaiBalance).to.be.eq(Zero); - expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmountForEth); - expect(userDaiStableDebtAmount).to.be.lt(expectedDaiAmountForEth); - expect(userUsdcStableDebtAmountBefore).to.be.gte(expectedUsdcAmountForAave); - expect(userUsdcStableDebtAmount).to.be.lt(expectedUsdcAmountForAave); - expect(aWETHBalance).to.be.lt(aWETHBalanceBefore); - expect(aAaveBalance).to.be.lt(aAaveBalanceBefore); - }); - - it('should revert if inconsistent params', async () => { - const {users, pool, weth, aWETH, oracle, dai, uniswapRepayAdapter} = testEnv; - const user = users[0].signer; - const userAddress = users[0].address; - - const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); - - const daiPrice = await oracle.getAssetPrice(dai.address); - const expectedDaiAmount = await convertToCurrencyDecimals( - dai.address, - new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) - ); - - // Open user Debt - await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); - - const liquidityToSwap = amountWETHtoSwap; - await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); - - // Subtract the FL fee from the amount to be swapped 0,09% - const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); - - await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, flashloanAmount); - - const params1 = buildRepayAdapterParams( - [dai.address, weth.address], - 0, - [expectedDaiAmount], - [1], - [0], - [0], - [0], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'] - ); - - await expect( - pool - .connect(user) - .flashLoan( - uniswapRepayAdapter.address, - [weth.address], - [flashloanAmount.toString()], - [0], - userAddress, - params1, - 0 - ) - ).to.be.revertedWith('INCONSISTENT_PARAMS'); - - const params2 = buildRepayAdapterParams( - [dai.address], - 0, - [expectedDaiAmount, expectedDaiAmount], - [1], - [0], - [0], - [0], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'] - ); - - await expect( - pool - .connect(user) - .flashLoan( - uniswapRepayAdapter.address, - [weth.address], - [flashloanAmount.toString()], - [0], - userAddress, - params2, - 0 - ) - ).to.be.revertedWith('INCONSISTENT_PARAMS'); - - const params3 = buildRepayAdapterParams( - [dai.address], - 0, - [expectedDaiAmount], - [1, 1], - [0], - [0], - [0], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'] - ); - - await expect( - pool - .connect(user) - .flashLoan( - uniswapRepayAdapter.address, - [weth.address], - [flashloanAmount.toString()], - [0], - userAddress, - params3, - 0 - ) - ).to.be.revertedWith('INCONSISTENT_PARAMS'); - - const params4 = buildRepayAdapterParams( - [dai.address], - 0, - [expectedDaiAmount], - [1], - [0], - [0, 0], - [0], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'] - ); - - await expect( - pool - .connect(user) - .flashLoan( - uniswapRepayAdapter.address, - [weth.address], - [flashloanAmount.toString()], - [0], - userAddress, - params4, - 0 - ) - ).to.be.revertedWith('INCONSISTENT_PARAMS'); - - const params5 = buildRepayAdapterParams( - [dai.address], - 0, - [expectedDaiAmount], - [1], - [0], - [0], - [0, 0], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'] - ); - - await expect( - pool - .connect(user) - .flashLoan( - uniswapRepayAdapter.address, - [weth.address], - [flashloanAmount.toString()], - [0], - userAddress, - params5, - 0 - ) - ).to.be.revertedWith('INCONSISTENT_PARAMS'); - - const params6 = buildRepayAdapterParams( - [dai.address], - 0, - [expectedDaiAmount], - [1], - [0], - [0], - [0], - [ - '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000', - ], - ['0x0000000000000000000000000000000000000000000000000000000000000000'] - ); - - await expect( - pool - .connect(user) - .flashLoan( - uniswapRepayAdapter.address, - [weth.address], - [flashloanAmount.toString()], - [0], - userAddress, - params6, - 0 - ) - ).to.be.revertedWith('INCONSISTENT_PARAMS'); - - const params7 = buildRepayAdapterParams( - [dai.address], - 0, - [expectedDaiAmount], - [1], - [0], - [0], - [0], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - [ - '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000', - ] - ); - - await expect( - pool - .connect(user) - .flashLoan( - uniswapRepayAdapter.address, - [weth.address], - [flashloanAmount.toString()], - [0], - userAddress, - params7, - 0 - ) - ).to.be.revertedWith('INCONSISTENT_PARAMS'); - - const params8 = buildRepayAdapterParams( - [dai.address], - 0, - [expectedDaiAmount], - [1], - [0, 0], - [0], - [0], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'] - ); - - await expect( - pool - .connect(user) - .flashLoan( - uniswapRepayAdapter.address, - [weth.address], - [flashloanAmount.toString()], - [0], - userAddress, - params8, - 0 - ) - ).to.be.revertedWith('INCONSISTENT_PARAMS'); - }); - it('should revert if caller not lending pool', async () => { const {users, pool, weth, aWETH, oracle, dai, uniswapRepayAdapter} = testEnv; const user = users[0].signer; @@ -2768,15 +2227,15 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, flashloanAmount); const params = buildRepayAdapterParams( - [dai.address], + dai.address, 0, - [expectedDaiAmount], - [1], - [0], - [0], - [0], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'] + expectedDaiAmount, + 1, + 0, + 0, + 0, + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000' ); await expect( @@ -2820,15 +2279,15 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, flashloanAmount); const params = buildRepayAdapterParams( - [dai.address], + dai.address, 0, - [expectedDaiAmount], - [1], - [0], - [0], - [0], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'] + expectedDaiAmount, + 1, + 0, + 0, + 0, + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000' ); await expect( @@ -2871,15 +2330,15 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, flashloanAmount); const params = buildRepayAdapterParams( - [dai.address], + dai.address, 0, - [expectedDaiAmount], - [1], - [0], - [0], - [0], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'] + expectedDaiAmount, + 1, + 0, + 0, + 0, + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000' ); await expect( @@ -2922,15 +2381,15 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, flashloanAmount); const params = buildRepayAdapterParams( - [dai.address], + dai.address, 0, - [expectedDaiAmount], - [1], - [0], - [0], - [0], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'] + expectedDaiAmount, + 1, + 0, + 0, + 0, + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000' ); await expect( @@ -3000,15 +2459,15 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, actualWEthSwapped); const params = buildRepayAdapterParams( - [dai.address], + dai.address, 0, - [expectedDaiAmount], - [1], - [0], - [0], - [0], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'] + expectedDaiAmount, + 1, + 0, + 0, + 0, + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000' ); await expect( @@ -3097,15 +2556,15 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const wethBalanceBefore = await weth.balanceOf(userAddress); const params = buildRepayAdapterParams( - [dai.address], + dai.address, 1, - [expectedDaiAmount], - [1], - [0], - [0], - [0], - ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'] + expectedDaiAmount, + 1, + 0, + 0, + 0, + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000' ); await expect( From 6e9defe14c111a02abc5056d9c2f1c8fddb6b412 Mon Sep 17 00:00:00 2001 From: Gerardo Nardelli Date: Mon, 9 Nov 2020 16:53:03 -0300 Subject: [PATCH 18/62] Add repayAllDebt flag to repay the whole debt with collateral --- contracts/adapters/BaseUniswapAdapter.sol | 5 +- .../adapters/UniswapLiquiditySwapAdapter.sol | 4 +- contracts/adapters/UniswapRepayAdapter.sol | 48 ++--- helpers/contracts-helpers.ts | 15 +- test/uniswapAdapters.spec.ts | 186 +++++++++++++++++- 5 files changed, 229 insertions(+), 29 deletions(-) diff --git a/contracts/adapters/BaseUniswapAdapter.sol b/contracts/adapters/BaseUniswapAdapter.sol index c69fa9a0..cb7d32cb 100644 --- a/contracts/adapters/BaseUniswapAdapter.sol +++ b/contracts/adapters/BaseUniswapAdapter.sol @@ -224,9 +224,8 @@ contract BaseUniswapAdapter { * @dev Get the aToken associated to the asset * @return address of the aToken */ - function _getAToken(address asset) internal view returns (address) { - ReserveLogic.ReserveData memory reserve = POOL.getReserveData(asset); - return reserve.aTokenAddress; + function _getReserveData(address asset) internal view returns (ReserveLogic.ReserveData memory) { + return POOL.getReserveData(asset); } /** diff --git a/contracts/adapters/UniswapLiquiditySwapAdapter.sol b/contracts/adapters/UniswapLiquiditySwapAdapter.sol index 83b334d1..a4150ef1 100644 --- a/contracts/adapters/UniswapLiquiditySwapAdapter.sol +++ b/contracts/adapters/UniswapLiquiditySwapAdapter.sol @@ -126,7 +126,7 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { ); for (uint256 i = 0; i < assetToSwapFromList.length; i++) { - address aToken = _getAToken(assetToSwapFromList[i]); + address aToken = _getReserveData(assetToSwapFromList[i]).aTokenAddress; uint256 aTokenInitiatorBalance = IERC20(aToken).balanceOf(msg.sender); uint256 amountToSwap = amountToSwapList[i] > aTokenInitiatorBalance ? aTokenInitiatorBalance : amountToSwapList[i]; @@ -172,7 +172,7 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { bool swapAllBalance, PermitSignature memory permitSignature ) internal { - address aToken = _getAToken(assetFrom); + address aToken = _getReserveData(assetFrom).aTokenAddress; uint256 aTokenInitiatorBalance = IERC20(aToken).balanceOf(initiator); uint256 amountToSwap = swapAllBalance ? aTokenInitiatorBalance.sub(premium) : amount; diff --git a/contracts/adapters/UniswapRepayAdapter.sol b/contracts/adapters/UniswapRepayAdapter.sol index dba64afd..f616765b 100644 --- a/contracts/adapters/UniswapRepayAdapter.sol +++ b/contracts/adapters/UniswapRepayAdapter.sol @@ -7,6 +7,7 @@ import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddresses import {IUniswapV2Router02} from '../interfaces/IUniswapV2Router02.sol'; import {IFlashLoanReceiver} from '../flashloan/interfaces/IFlashLoanReceiver.sol'; import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol'; +import {ReserveLogic} from '../libraries/logic/ReserveLogic.sol'; /** * @title UniswapRepayAdapter @@ -20,6 +21,7 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { LeftoverAction leftOverAction; uint256 repayAmount; uint256 rateMode; + bool repayAllDebt; PermitSignature permitSignature; } @@ -47,6 +49,7 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { * (1) Direct transfer to user * uint256 repayAmount Amount of debt to be repaid * uint256 rateMode Rate modes of the debt to be repaid + * bool repayAllDebt Flag indicating if all the debt should be repaid * uint256 permitAmount Amount for the permit signature * uint256 deadline Deadline for the permit signature * uint8 v V param for the permit signature @@ -72,6 +75,7 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { decodedParams.rateMode, initiator, decodedParams.leftOverAction, + decodedParams.repayAllDebt, premiums[0], decodedParams.permitSignature ); @@ -100,9 +104,21 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { uint256 rateMode, address initiator, LeftoverAction leftOverAction, + bool repayAllDebt, uint256 premium, PermitSignature memory permitSignature ) internal { + + if (repayAllDebt) { + ReserveLogic.ReserveData memory reserveDebtData = _getReserveData(assetTo); + + address debtToken = ReserveLogic.InterestRateMode(rateMode) == ReserveLogic.InterestRateMode.STABLE + ? reserveDebtData.stableDebtTokenAddress + : reserveDebtData.variableDebtTokenAddress; + + repayAmount = IERC20(debtToken).balanceOf(initiator); + } + _swapTokensForExactTokens(assetFrom, assetTo, amount, repayAmount); // Repay debt @@ -110,7 +126,12 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { POOL.repay(assetTo, repayAmount, rateMode, initiator); uint256 flashLoanDebt = amount.add(premium); - _pullATokenAndRepayFlashLoan(assetFrom, initiator, flashLoanDebt, permitSignature); + + ReserveLogic.ReserveData memory reserveData = _getReserveData(assetFrom); + _pullAToken(assetFrom, reserveData.aTokenAddress, initiator, flashLoanDebt, permitSignature); + + // Repay flashloan + IERC20(assetFrom).approve(address(POOL), flashLoanDebt); // Take care of reserve leftover from the swap _sendLeftovers(assetFrom, flashLoanDebt, leftOverAction, initiator); @@ -125,6 +146,7 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { * (1) Direct transfer to user * uint256 repayAmount Amount of debt to be repaid * uint256 rateMode Rate modes of the debt to be repaid + * bool repayAllDebt Flag indicating if all the debt should be repaid * uint256 permitAmount Amount for the permit signature * uint256 deadline Deadline for the permit signature * uint8 v V param for the permit signature @@ -138,18 +160,20 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { LeftoverAction leftOverAction, uint256 repayAmount, uint256 rateMode, + bool repayAllDebt, uint256 permitAmount, uint256 deadline, uint8 v, bytes32 r, bytes32 s - ) = abi.decode(params, (address, LeftoverAction, uint256, uint256, uint256, uint256, uint8, bytes32, bytes32)); + ) = abi.decode(params, (address, LeftoverAction, uint256, uint256, bool, uint256, uint256, uint8, bytes32, bytes32)); return RepayParams( assetToSwapTo, leftOverAction, repayAmount, rateMode, + repayAllDebt, PermitSignature( permitAmount, deadline, @@ -159,24 +183,4 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { ) ); } - - /** - * @dev Pull the ATokens from the user and use them to repay the flashloan - * @param reserve address of the asset - * @param user address - * @param flashLoanDebt need to be repaid - * @param permitSignature struct containing the permit signature - */ - function _pullATokenAndRepayFlashLoan( - address reserve, - address user, - uint256 flashLoanDebt, - PermitSignature memory permitSignature - ) internal { - address reserveAToken = _getAToken(reserve); - _pullAToken(reserve, reserveAToken, user, flashLoanDebt, permitSignature); - - // Repay flashloan - IERC20(reserve).approve(address(POOL), flashLoanDebt); - } } diff --git a/helpers/contracts-helpers.ts b/helpers/contracts-helpers.ts index e2ccf3c8..6eba519e 100644 --- a/helpers/contracts-helpers.ts +++ b/helpers/contracts-helpers.ts @@ -244,6 +244,7 @@ export const buildRepayAdapterParams = ( leftoverAction: BigNumberish, repayAmount: BigNumberish, rateMode: BigNumberish, + repayAllDebt: BigNumberish, permitAmount: BigNumberish, deadline: BigNumberish, v: BigNumberish, @@ -256,12 +257,24 @@ export const buildRepayAdapterParams = ( 'uint256', 'uint256', 'uint256', + 'bool', 'uint256', 'uint256', 'uint8', 'bytes32', 'bytes32', ], - [assetToSwapTo, leftoverAction, repayAmount, rateMode, permitAmount, deadline, v, r, s] + [ + assetToSwapTo, + leftoverAction, + repayAmount, + rateMode, + repayAllDebt, + permitAmount, + deadline, + v, + r, + s, + ] ); }; diff --git a/test/uniswapAdapters.spec.ts b/test/uniswapAdapters.spec.ts index a3ea4dbe..fcb9d81d 100644 --- a/test/uniswapAdapters.spec.ts +++ b/test/uniswapAdapters.spec.ts @@ -1959,7 +1959,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { }); }); - describe.only('UniswapRepayAdapter', () => { + describe('UniswapRepayAdapter', () => { describe('constructor', () => { it('should deploy with correct parameters', async () => { const {addressesProvider} = testEnv; @@ -2062,6 +2062,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { 0, 0, 0, + 0, '0x0000000000000000000000000000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000000000000000000000000000' ); @@ -2166,6 +2167,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { 0, expectedDaiAmount, 1, + 0, liquidityToSwap, deadline, v, @@ -2234,6 +2236,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { 0, 0, 0, + 0, '0x0000000000000000000000000000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000000000000000000000000000' ); @@ -2286,6 +2289,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { 0, 0, 0, + 0, '0x0000000000000000000000000000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000000000000000000000000000' ); @@ -2337,6 +2341,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { 0, 0, 0, + 0, '0x0000000000000000000000000000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000000000000000000000000000' ); @@ -2388,6 +2393,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { 0, 0, 0, + 0, '0x0000000000000000000000000000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000000000000000000000000000' ); @@ -2466,6 +2472,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { 0, 0, 0, + 0, '0x0000000000000000000000000000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000000000000000000000000000' ); @@ -2563,6 +2570,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { 0, 0, 0, + 0, '0x0000000000000000000000000000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000000000000000000000000000' ); @@ -2597,6 +2605,182 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { expect(userAEthBalance).to.be.gt(userAEthBalanceBefore.sub(liquidityToSwap)); expect(wethBalance).to.be.eq(wethBalanceBefore.add(leftOverWeth.toString())); }); + + it('should correctly swap tokens and repay the whole stable debt', async () => { + const { + users, + pool, + weth, + aWETH, + oracle, + dai, + uniswapRepayAdapter, + helpersContract, + } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); + + const daiStableDebtTokenAddress = ( + await helpersContract.getReserveTokensAddresses(dai.address) + ).stableDebtTokenAddress; + + const daiStableDebtContract = await getContract( + eContractid.StableDebtToken, + daiStableDebtTokenAddress + ); + + const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); + + const liquidityToSwap = amountWETHtoSwap; + await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + + // Subtract the FL fee from the amount to be swapped 0,09% + const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); + + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, flashloanAmount); + + // Passed amount to repay is smaller than debt, + // but repayAllDebt flag is enabled so the whole debt should be paid + const amountToRepay = expectedDaiAmount.div(2); + + const params = buildRepayAdapterParams( + dai.address, + 0, + amountToRepay, + 1, + 1, + 0, + 0, + 0, + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000' + ); + + await pool + .connect(user) + .flashLoan( + uniswapRepayAdapter.address, + [weth.address], + [flashloanAmount.toString()], + [0], + userAddress, + params, + 0 + ); + + const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); + const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); + const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); + const userAEthBalance = await aWETH.balanceOf(userAddress); + + expect(adapterWethBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount); + expect(userDaiStableDebtAmount).to.be.eq(Zero); + expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); + expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); + }); + + it('should correctly swap tokens and repay the whole variable debt', async () => { + const { + users, + pool, + weth, + aWETH, + oracle, + dai, + uniswapRepayAdapter, + helpersContract, + } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 2, 0, userAddress); + + const daiStableVariableTokenAddress = ( + await helpersContract.getReserveTokensAddresses(dai.address) + ).variableDebtTokenAddress; + + const daiVariableDebtContract = await getContract( + eContractid.VariableDebtToken, + daiStableVariableTokenAddress + ); + + const userDaiVariableDebtAmountBefore = await daiVariableDebtContract.balanceOf( + userAddress + ); + + const liquidityToSwap = amountWETHtoSwap; + await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + + // Subtract the FL fee from the amount to be swapped 0,09% + const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); + + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, flashloanAmount); + + // Passed amount to repay is smaller than debt, + // but repayAllDebt flag is enabled so the whole debt should be paid + const amountToRepay = expectedDaiAmount.div(2); + + const params = buildRepayAdapterParams( + dai.address, + 0, + amountToRepay, + 2, + 1, + 0, + 0, + 0, + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000' + ); + + await pool + .connect(user) + .flashLoan( + uniswapRepayAdapter.address, + [weth.address], + [flashloanAmount.toString()], + [0], + userAddress, + params, + 0 + ); + + const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); + const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); + const userDaiVariableDebtAmount = await daiVariableDebtContract.balanceOf(userAddress); + const userAEthBalance = await aWETH.balanceOf(userAddress); + + expect(adapterWethBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(userDaiVariableDebtAmountBefore).to.be.gte(expectedDaiAmount); + expect(userDaiVariableDebtAmount).to.be.eq(Zero); + expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); + expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); + }); }); }); }); From cf9c8855c33e72ceba2442c22dcf9130061a4616 Mon Sep 17 00:00:00 2001 From: Gerardo Nardelli Date: Tue, 10 Nov 2020 10:38:24 -0300 Subject: [PATCH 19/62] Pull the correct amount of atokens from user to avoid leftovers in repay adapter --- contracts/adapters/BaseUniswapAdapter.sol | 25 ----- contracts/adapters/UniswapRepayAdapter.sol | 25 +---- helpers/contracts-helpers.ts | 27 +---- test/uniswapAdapters.spec.ts | 114 ++------------------- 4 files changed, 14 insertions(+), 177 deletions(-) diff --git a/contracts/adapters/BaseUniswapAdapter.sol b/contracts/adapters/BaseUniswapAdapter.sol index cb7d32cb..dab04acd 100644 --- a/contracts/adapters/BaseUniswapAdapter.sol +++ b/contracts/adapters/BaseUniswapAdapter.sol @@ -24,8 +24,6 @@ contract BaseUniswapAdapter { using PercentageMath for uint256; using SafeERC20 for IERC20; - enum LeftoverAction {DEPOSIT, TRANSFER} - struct PermitParams { uint256[] amount; uint256[] deadline; @@ -228,29 +226,6 @@ contract BaseUniswapAdapter { return POOL.getReserveData(asset); } - /** - * @dev Take action with the swap left overs as configured in the parameters - * @param asset address of the asset - * @param reservedAmount Amount reserved to be used by the contract to repay the flash loan - * @param leftOverAction enum indicating what to do with the left over balance from the swap: - * (0) Deposit back - * (1) Direct transfer to user - * @param user address - */ - function _sendLeftovers(address asset, uint256 reservedAmount, LeftoverAction leftOverAction, address user) internal { - uint256 balance = IERC20(asset).balanceOf(address(this)); - uint256 assetLeftOver = balance.sub(reservedAmount); - - if (assetLeftOver > 0) { - if (leftOverAction == LeftoverAction.DEPOSIT) { - IERC20(asset).approve(address(POOL), balance); - POOL.deposit(asset, assetLeftOver, user, 0); - } else { - IERC20(asset).transfer(user, assetLeftOver); - } - } - } - /** * @dev Pull the ATokens from the user * @param reserve address of the asset diff --git a/contracts/adapters/UniswapRepayAdapter.sol b/contracts/adapters/UniswapRepayAdapter.sol index f616765b..fb821db3 100644 --- a/contracts/adapters/UniswapRepayAdapter.sol +++ b/contracts/adapters/UniswapRepayAdapter.sol @@ -18,7 +18,6 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { struct RepayParams { address assetToSwapTo; - LeftoverAction leftOverAction; uint256 repayAmount; uint256 rateMode; bool repayAllDebt; @@ -44,9 +43,6 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { * @param initiator Address of the user * @param params Additional variadic field to include extra params. Expected parameters: * address Address of the reserve to be swapped to and repay - * uint256 leftOverAction Flag indicating what to do with the left over balance from the swap: - * (0) Deposit back - * (1) Direct transfer to user * uint256 repayAmount Amount of debt to be repaid * uint256 rateMode Rate modes of the debt to be repaid * bool repayAllDebt Flag indicating if all the debt should be repaid @@ -74,7 +70,6 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { decodedParams.repayAmount, decodedParams.rateMode, initiator, - decodedParams.leftOverAction, decodedParams.repayAllDebt, premiums[0], decodedParams.permitSignature @@ -84,7 +79,7 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { } /** - * @dev Perform the swap, the repay of the debt and send back the left overs + * @dev Perform the swap and the repay of the debt * * @param assetFrom Address of token to be swapped * @param assetTo Address of token to be received @@ -92,7 +87,6 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { * @param repayAmount Amount of the debt to be repaid * @param rateMode Rate mode of the debt to be repaid * @param initiator Address of the user - * @param leftOverAction enum indicating what to do with the left over balance from the swap * @param premium Fee of the flash loan * @param permitSignature struct containing the permit signature */ @@ -103,12 +97,10 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { uint256 repayAmount, uint256 rateMode, address initiator, - LeftoverAction leftOverAction, bool repayAllDebt, uint256 premium, PermitSignature memory permitSignature ) internal { - if (repayAllDebt) { ReserveLogic.ReserveData memory reserveDebtData = _getReserveData(assetTo); @@ -119,31 +111,26 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { repayAmount = IERC20(debtToken).balanceOf(initiator); } - _swapTokensForExactTokens(assetFrom, assetTo, amount, repayAmount); + uint256 amountSwapped = _swapTokensForExactTokens(assetFrom, assetTo, amount, repayAmount); // Repay debt IERC20(assetTo).approve(address(POOL), repayAmount); POOL.repay(assetTo, repayAmount, rateMode, initiator); uint256 flashLoanDebt = amount.add(premium); + uint256 amountToPull = flashLoanDebt.sub(amount.sub(amountSwapped)); ReserveLogic.ReserveData memory reserveData = _getReserveData(assetFrom); - _pullAToken(assetFrom, reserveData.aTokenAddress, initiator, flashLoanDebt, permitSignature); + _pullAToken(assetFrom, reserveData.aTokenAddress, initiator, amountToPull, permitSignature); // Repay flashloan IERC20(assetFrom).approve(address(POOL), flashLoanDebt); - - // Take care of reserve leftover from the swap - _sendLeftovers(assetFrom, flashLoanDebt, leftOverAction, initiator); } /** * @dev Decodes debt information encoded in flashloan params * @param params Additional variadic field to include extra params. Expected parameters: * address Address of the reserve to be swapped to and repay - * uint256 leftOverAction Flag indicating what to do with the left over balance from the swap: - * (0) Deposit back - * (1) Direct transfer to user * uint256 repayAmount Amount of debt to be repaid * uint256 rateMode Rate modes of the debt to be repaid * bool repayAllDebt Flag indicating if all the debt should be repaid @@ -157,7 +144,6 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { function _decodeParams(bytes memory params) internal pure returns (RepayParams memory) { ( address assetToSwapTo, - LeftoverAction leftOverAction, uint256 repayAmount, uint256 rateMode, bool repayAllDebt, @@ -166,11 +152,10 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { uint8 v, bytes32 r, bytes32 s - ) = abi.decode(params, (address, LeftoverAction, uint256, uint256, bool, uint256, uint256, uint8, bytes32, bytes32)); + ) = abi.decode(params, (address, uint256, uint256, bool, uint256, uint256, uint8, bytes32, bytes32)); return RepayParams( assetToSwapTo, - leftOverAction, repayAmount, rateMode, repayAllDebt, diff --git a/helpers/contracts-helpers.ts b/helpers/contracts-helpers.ts index 6eba519e..1b968922 100644 --- a/helpers/contracts-helpers.ts +++ b/helpers/contracts-helpers.ts @@ -241,7 +241,6 @@ export const buildLiquiditySwapParams = ( export const buildRepayAdapterParams = ( assetToSwapTo: tEthereumAddress, - leftoverAction: BigNumberish, repayAmount: BigNumberish, rateMode: BigNumberish, repayAllDebt: BigNumberish, @@ -252,29 +251,7 @@ export const buildRepayAdapterParams = ( s: string | Buffer ) => { return ethers.utils.defaultAbiCoder.encode( - [ - 'address', - 'uint256', - 'uint256', - 'uint256', - 'bool', - 'uint256', - 'uint256', - 'uint8', - 'bytes32', - 'bytes32', - ], - [ - assetToSwapTo, - leftoverAction, - repayAmount, - rateMode, - repayAllDebt, - permitAmount, - deadline, - v, - r, - s, - ] + ['address', 'uint256', 'uint256', 'bool', 'uint256', 'uint256', 'uint8', 'bytes32', 'bytes32'], + [assetToSwapTo, repayAmount, rateMode, repayAllDebt, permitAmount, deadline, v, r, s] ); }; diff --git a/test/uniswapAdapters.spec.ts b/test/uniswapAdapters.spec.ts index fcb9d81d..2a086fb8 100644 --- a/test/uniswapAdapters.spec.ts +++ b/test/uniswapAdapters.spec.ts @@ -2056,7 +2056,6 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const params = buildRepayAdapterParams( dai.address, - 0, expectedDaiAmount, 1, 0, @@ -2164,7 +2163,6 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const params = buildRepayAdapterParams( dai.address, - 0, expectedDaiAmount, 1, 0, @@ -2230,7 +2228,6 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const params = buildRepayAdapterParams( dai.address, - 0, expectedDaiAmount, 1, 0, @@ -2283,7 +2280,6 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const params = buildRepayAdapterParams( dai.address, - 0, expectedDaiAmount, 1, 0, @@ -2335,7 +2331,6 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const params = buildRepayAdapterParams( dai.address, - 0, expectedDaiAmount, 1, 0, @@ -2387,7 +2382,6 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const params = buildRepayAdapterParams( dai.address, - 0, expectedDaiAmount, 1, 0, @@ -2413,7 +2407,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { ).to.be.revertedWith('maxAmountToSwap exceed max slippage'); }); - it('should swap tokens, repay debt and deposit in pool the left over', async () => { + it('should swap, repay debt and pull the needed ATokens leaving no leftovers', async () => { const { users, pool, @@ -2466,7 +2460,6 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const params = buildRepayAdapterParams( dai.address, - 0, expectedDaiAmount, 1, 0, @@ -2497,7 +2490,9 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); const userAEthBalance = await aWETH.balanceOf(userAddress); + const adapterAEthBalance = await aWETH.balanceOf(uniswapRepayAdapter.address); + expect(adapterAEthBalance).to.be.eq(Zero); expect(adapterWethBalance).to.be.eq(Zero); expect(adapterDaiBalance).to.be.eq(Zero); expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount); @@ -2509,103 +2504,6 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { ); }); - it('should swap tokens, repay debt and transfer to user the left over', async () => { - const { - users, - pool, - weth, - aWETH, - oracle, - dai, - uniswapRepayAdapter, - helpersContract, - } = testEnv; - const user = users[0].signer; - const userAddress = users[0].address; - - const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); - - const daiPrice = await oracle.getAssetPrice(dai.address); - const expectedDaiAmount = await convertToCurrencyDecimals( - dai.address, - new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) - ); - - // Open user Debt - await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); - - const daiStableDebtTokenAddress = ( - await helpersContract.getReserveTokensAddresses(dai.address) - ).stableDebtTokenAddress; - - const daiStableDebtContract = await getContract( - eContractid.StableDebtToken, - daiStableDebtTokenAddress - ); - - const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); - - const liquidityToSwap = amountWETHtoSwap; - await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); - const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); - - // Subtract the FL fee from the amount to be swapped 0,09% - const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); - - const actualWEthSwapped = new BigNumber(flashloanAmount.toString()) - .multipliedBy(0.995) - .toFixed(0); - - const leftOverWeth = new BigNumber(flashloanAmount).minus(actualWEthSwapped); - - await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, actualWEthSwapped); - - const wethBalanceBefore = await weth.balanceOf(userAddress); - - const params = buildRepayAdapterParams( - dai.address, - 1, - expectedDaiAmount, - 1, - 0, - 0, - 0, - 0, - '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000' - ); - - await expect( - pool - .connect(user) - .flashLoan( - uniswapRepayAdapter.address, - [weth.address], - [flashloanAmount.toString()], - [0], - userAddress, - params, - 0 - ) - ) - .to.emit(uniswapRepayAdapter, 'Swapped') - .withArgs(weth.address, dai.address, actualWEthSwapped.toString(), expectedDaiAmount); - - const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); - const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); - const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); - const userAEthBalance = await aWETH.balanceOf(userAddress); - const wethBalance = await weth.balanceOf(userAddress); - - expect(adapterWethBalance).to.be.eq(Zero); - expect(adapterDaiBalance).to.be.eq(Zero); - expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount); - expect(userDaiStableDebtAmount).to.be.lt(expectedDaiAmount); - expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); - expect(userAEthBalance).to.be.gt(userAEthBalanceBefore.sub(liquidityToSwap)); - expect(wethBalance).to.be.eq(wethBalanceBefore.add(leftOverWeth.toString())); - }); - it('should correctly swap tokens and repay the whole stable debt', async () => { const { users, @@ -2657,7 +2555,6 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const params = buildRepayAdapterParams( dai.address, - 0, amountToRepay, 1, 1, @@ -2684,7 +2581,9 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); const userAEthBalance = await aWETH.balanceOf(userAddress); + const adapterAEthBalance = await aWETH.balanceOf(uniswapRepayAdapter.address); + expect(adapterAEthBalance).to.be.eq(Zero); expect(adapterWethBalance).to.be.eq(Zero); expect(adapterDaiBalance).to.be.eq(Zero); expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount); @@ -2746,7 +2645,6 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const params = buildRepayAdapterParams( dai.address, - 0, amountToRepay, 2, 1, @@ -2773,7 +2671,9 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); const userDaiVariableDebtAmount = await daiVariableDebtContract.balanceOf(userAddress); const userAEthBalance = await aWETH.balanceOf(userAddress); + const adapterAEthBalance = await aWETH.balanceOf(uniswapRepayAdapter.address); + expect(adapterAEthBalance).to.be.eq(Zero); expect(adapterWethBalance).to.be.eq(Zero); expect(adapterDaiBalance).to.be.eq(Zero); expect(userDaiVariableDebtAmountBefore).to.be.gte(expectedDaiAmount); From b0d9dbe2a7c48c4fd7d31bbd2542e5236ba80f25 Mon Sep 17 00:00:00 2001 From: Gerardo Nardelli Date: Tue, 10 Nov 2020 11:28:19 -0300 Subject: [PATCH 20/62] code improvements --- contracts/adapters/BaseUniswapAdapter.sol | 43 ++++++--------- .../adapters/UniswapLiquiditySwapAdapter.sol | 55 ++++++------------- contracts/adapters/UniswapRepayAdapter.sol | 4 +- 3 files changed, 37 insertions(+), 65 deletions(-) diff --git a/contracts/adapters/BaseUniswapAdapter.sol b/contracts/adapters/BaseUniswapAdapter.sol index dab04acd..e5e779f7 100644 --- a/contracts/adapters/BaseUniswapAdapter.sol +++ b/contracts/adapters/BaseUniswapAdapter.sol @@ -24,14 +24,6 @@ contract BaseUniswapAdapter { using PercentageMath for uint256; using SafeERC20 for IERC20; - struct PermitParams { - uint256[] amount; - uint256[] deadline; - uint8[] v; - bytes32[] r; - bytes32[] s; - } - struct PermitSignature { uint256 amount; uint256 deadline; @@ -140,9 +132,9 @@ contract BaseUniswapAdapter { uint256 toAssetPrice = _getPrice(assetToSwapTo); uint256 expectedMinAmountOut = amountToSwap - .mul(fromAssetPrice.mul(10**toAssetDecimals)) - .div(toAssetPrice.mul(10**fromAssetDecimals)) - .percentMul(PercentageMath.PERCENTAGE_FACTOR.sub(MAX_SLIPPAGE_PERCENT)); + .mul(fromAssetPrice.mul(10**toAssetDecimals)) + .div(toAssetPrice.mul(10**fromAssetDecimals)) + .percentMul(PercentageMath.PERCENTAGE_FACTOR.sub(MAX_SLIPPAGE_PERCENT)); require(expectedMinAmountOut < minAmountOut, 'minAmountOut exceed max slippage'); @@ -183,9 +175,9 @@ contract BaseUniswapAdapter { uint256 toAssetPrice = _getPrice(assetToSwapTo); uint256 expectedMaxAmountToSwap = amountToReceive - .mul(toAssetPrice.mul(10**fromAssetDecimals)) - .div(fromAssetPrice.mul(10**toAssetDecimals)) - .percentMul(PercentageMath.PERCENTAGE_FACTOR.add(MAX_SLIPPAGE_PERCENT)); + .mul(toAssetPrice.mul(10**fromAssetDecimals)) + .div(fromAssetPrice.mul(10**toAssetDecimals)) + .percentMul(PercentageMath.PERCENTAGE_FACTOR.add(MAX_SLIPPAGE_PERCENT)); require(maxAmountToSwap < expectedMaxAmountToSwap, 'maxAmountToSwap exceed max slippage'); @@ -267,8 +259,7 @@ contract BaseUniswapAdapter { * @return whether or not permit should be called */ function _usePermit(PermitSignature memory signature) internal pure returns (bool) { - return !(uint256(signature.deadline) == uint256(signature.v) && - uint256(signature.deadline) == 0); + return !(uint256(signature.deadline) == uint256(signature.v) && uint256(signature.deadline) == 0); } /** @@ -283,10 +274,10 @@ contract BaseUniswapAdapter { uint256 reservePrice = _getPrice(reserve); return amount - .mul(reservePrice) - .div(10**decimals) - .mul(ethUsdPrice) - .div(10**18); + .mul(reservePrice) + .div(10**decimals) + .mul(ethUsdPrice) + .div(10**18); } /** @@ -314,9 +305,9 @@ contract BaseUniswapAdapter { uint256 reserveOutDecimals = _getDecimals(reserveOut); uint256 outPerInPrice = finalAmountIn - .mul(10**18) - .mul(10**reserveOutDecimals) - .div(amounts[1].mul(10**reserveInDecimals)); + .mul(10**18) + .mul(10**reserveOutDecimals) + .div(amounts[1].mul(10**reserveInDecimals)); return AmountCalc( amounts[1], @@ -351,9 +342,9 @@ contract BaseUniswapAdapter { uint256 reserveOutDecimals = _getDecimals(reserveOut); uint256 inPerOutPrice = amountOut - .mul(10**18) - .mul(10**reserveInDecimals) - .div(finalAmountIn.mul(10**reserveOutDecimals)); + .mul(10**18) + .mul(10**reserveInDecimals) + .div(finalAmountIn.mul(10**reserveOutDecimals)); return AmountCalc( finalAmountIn, diff --git a/contracts/adapters/UniswapLiquiditySwapAdapter.sol b/contracts/adapters/UniswapLiquiditySwapAdapter.sol index a4150ef1..c023b1f5 100644 --- a/contracts/adapters/UniswapLiquiditySwapAdapter.sol +++ b/contracts/adapters/UniswapLiquiditySwapAdapter.sol @@ -15,6 +15,14 @@ import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol'; **/ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { + struct PermitParams { + uint256[] amount; + uint256[] deadline; + uint8[] v; + bytes32[] r; + bytes32[] s; + } + struct SwapParams { address[] assetToSwapToList; uint256[] minAmountsToReceive; @@ -26,8 +34,8 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { ILendingPoolAddressesProvider addressesProvider, IUniswapV2Router02 uniswapRouter ) - public - BaseUniswapAdapter(addressesProvider, uniswapRouter) + public + BaseUniswapAdapter(addressesProvider, uniswapRouter) {} /** @@ -191,19 +199,15 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { uint256 flashLoanDebt = amount.add(premium); uint256 amountToPull = swapAllBalance ? aTokenInitiatorBalance : flashLoanDebt; - _pullATokenAndRepayFlashLoan( - assetFrom, - aToken, - initiator, - amountToPull, - flashLoanDebt, - permitSignature - ); + _pullAToken(assetFrom, aToken, initiator, amountToPull, permitSignature); + + // Repay flashloan + IERC20(assetFrom).approve(address(POOL), flashLoanDebt); } /** - * @dev Decodes debt information encoded in flashloan params - * @param params Additional variadic field to include extra params. Expected parameters: + * @dev Decodes debt information encoded in flashloan params + * @param params Additional variadic field to include extra params. Expected parameters: * address[] assetToSwapToList List of the addresses of the reserve to be swapped to and deposited * uint256[] minAmountsToReceive List of min amounts to be received from the swap * bool[] swapAllBalance Flag indicating if all the user balance should be swapped @@ -212,8 +216,8 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { * uint8[] v List of v param for the permit signature * bytes32[] r List of r param for the permit signature * bytes32[] s List of s param for the permit signature - * @return SwapParams struct containing decoded params - */ + * @return SwapParams struct containing decoded params + */ function _decodeParams(bytes memory params) internal pure returns (SwapParams memory) { ( address[] memory assetToSwapToList, @@ -228,27 +232,4 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { return SwapParams(assetToSwapToList, minAmountsToReceive, swapAllBalance, PermitParams(permitAmount, deadline, v, r, s)); } - - /** - * @dev Pull the ATokens from the user and use them to repay the flashloan - * @param reserve address of the asset - * @param reserveAToken address of the aToken of the reserve - * @param user address - * @param amountToPull amount to be pulled from the user - * @param flashLoanDebt need to be repaid - * @param permitSignature struct containing the permit signature - */ - function _pullATokenAndRepayFlashLoan( - address reserve, - address reserveAToken, - address user, - uint256 amountToPull, - uint256 flashLoanDebt, - PermitSignature memory permitSignature - ) internal { - _pullAToken(reserve, reserveAToken, user, amountToPull, permitSignature); - - // Repay flashloan - IERC20(reserve).approve(address(POOL), flashLoanDebt); - } } diff --git a/contracts/adapters/UniswapRepayAdapter.sol b/contracts/adapters/UniswapRepayAdapter.sol index fb821db3..d3db21b0 100644 --- a/contracts/adapters/UniswapRepayAdapter.sol +++ b/contracts/adapters/UniswapRepayAdapter.sol @@ -28,8 +28,8 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { ILendingPoolAddressesProvider addressesProvider, IUniswapV2Router02 uniswapRouter ) - public - BaseUniswapAdapter(addressesProvider, uniswapRouter) + public + BaseUniswapAdapter(addressesProvider, uniswapRouter) {} /** From 4c693d1947268bfd9b9bf714948c0c288d117e57 Mon Sep 17 00:00:00 2001 From: Gerardo Nardelli Date: Fri, 13 Nov 2020 12:27:18 -0300 Subject: [PATCH 21/62] Update amountToSwap calc in liquidity swap adapter --- contracts/adapters/UniswapLiquiditySwapAdapter.sol | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/contracts/adapters/UniswapLiquiditySwapAdapter.sol b/contracts/adapters/UniswapLiquiditySwapAdapter.sol index c023b1f5..059170f9 100644 --- a/contracts/adapters/UniswapLiquiditySwapAdapter.sol +++ b/contracts/adapters/UniswapLiquiditySwapAdapter.sol @@ -183,7 +183,9 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { address aToken = _getReserveData(assetFrom).aTokenAddress; uint256 aTokenInitiatorBalance = IERC20(aToken).balanceOf(initiator); - uint256 amountToSwap = swapAllBalance ? aTokenInitiatorBalance.sub(premium) : amount; + uint256 amountToSwap = swapAllBalance && aTokenInitiatorBalance.sub(premium) <= amount + ? aTokenInitiatorBalance.sub(premium) + : amount; uint256 receivedAmount = _swapExactTokensForTokens( assetFrom, @@ -197,7 +199,7 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { POOL.deposit(assetTo, receivedAmount, initiator, 0); uint256 flashLoanDebt = amount.add(premium); - uint256 amountToPull = swapAllBalance ? aTokenInitiatorBalance : flashLoanDebt; + uint256 amountToPull = amountToSwap.add(premium); _pullAToken(assetFrom, aToken, initiator, amountToPull, permitSignature); From 101c77578975825b3241edde4787394c688365a6 Mon Sep 17 00:00:00 2001 From: Gerardo Nardelli Date: Fri, 13 Nov 2020 12:28:57 -0300 Subject: [PATCH 22/62] Avoid param override in repay adapter --- contracts/adapters/UniswapRepayAdapter.sol | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/contracts/adapters/UniswapRepayAdapter.sol b/contracts/adapters/UniswapRepayAdapter.sol index d3db21b0..2e09f746 100644 --- a/contracts/adapters/UniswapRepayAdapter.sol +++ b/contracts/adapters/UniswapRepayAdapter.sol @@ -101,6 +101,8 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { uint256 premium, PermitSignature memory permitSignature ) internal { + uint256 debtRepayAmount; + if (repayAllDebt) { ReserveLogic.ReserveData memory reserveDebtData = _getReserveData(assetTo); @@ -108,17 +110,19 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { ? reserveDebtData.stableDebtTokenAddress : reserveDebtData.variableDebtTokenAddress; - repayAmount = IERC20(debtToken).balanceOf(initiator); + debtRepayAmount = IERC20(debtToken).balanceOf(initiator); + } else { + debtRepayAmount = repayAmount; } - uint256 amountSwapped = _swapTokensForExactTokens(assetFrom, assetTo, amount, repayAmount); + uint256 amountSwapped = _swapTokensForExactTokens(assetFrom, assetTo, amount, debtRepayAmount); // Repay debt - IERC20(assetTo).approve(address(POOL), repayAmount); - POOL.repay(assetTo, repayAmount, rateMode, initiator); + IERC20(assetTo).approve(address(POOL), debtRepayAmount); + POOL.repay(assetTo, debtRepayAmount, rateMode, initiator); uint256 flashLoanDebt = amount.add(premium); - uint256 amountToPull = flashLoanDebt.sub(amount.sub(amountSwapped)); + uint256 amountToPull = amountSwapped.add(premium); ReserveLogic.ReserveData memory reserveData = _getReserveData(assetFrom); _pullAToken(assetFrom, reserveData.aTokenAddress, initiator, amountToPull, permitSignature); From d0d0e869d0d734238d7c90023d8a876f9df1e390 Mon Sep 17 00:00:00 2001 From: Gerardo Nardelli Date: Fri, 13 Nov 2020 12:30:14 -0300 Subject: [PATCH 23/62] Fix flash loan fee usage in _getAmountsIn --- contracts/adapters/BaseUniswapAdapter.sol | 4 ++-- test/uniswapAdapters.spec.ts | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/contracts/adapters/BaseUniswapAdapter.sol b/contracts/adapters/BaseUniswapAdapter.sol index e5e779f7..7a727b7d 100644 --- a/contracts/adapters/BaseUniswapAdapter.sol +++ b/contracts/adapters/BaseUniswapAdapter.sol @@ -335,8 +335,8 @@ contract BaseUniswapAdapter { uint256[] memory amounts = UNISWAP_ROUTER.getAmountsIn(amountOut, path); - // Subtract flash loan fee - uint256 finalAmountIn = amounts[0].sub(amounts[0].mul(FLASHLOAN_PREMIUM_TOTAL).div(10000)); + // Add flash loan fee + uint256 finalAmountIn = amounts[0].add(amounts[0].mul(FLASHLOAN_PREMIUM_TOTAL).div(10000)); uint256 reserveInDecimals = _getDecimals(reserveIn); uint256 reserveOutDecimals = _getDecimals(reserveOut); diff --git a/test/uniswapAdapters.spec.ts b/test/uniswapAdapters.spec.ts index 2a086fb8..6f40530f 100644 --- a/test/uniswapAdapters.spec.ts +++ b/test/uniswapAdapters.spec.ts @@ -152,7 +152,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const amountIn = parseEther('1'); const flashloanPremium = amountIn.mul(9).div(10000); - const amountToSwap = amountIn.sub(flashloanPremium); + const amountToSwap = amountIn.add(flashloanPremium); const wethPrice = await oracle.getAssetPrice(weth.address); const daiPrice = await oracle.getAssetPrice(dai.address); @@ -197,7 +197,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const amountIn = parseEther('10'); const flashloanPremium = amountIn.mul(9).div(10000); - const amountToSwap = amountIn.sub(flashloanPremium); + const amountToSwap = amountIn.add(flashloanPremium); const aavePrice = await oracle.getAssetPrice(aave.address); const usdcPrice = await oracle.getAssetPrice(usdc.address); @@ -241,7 +241,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { }); }); - describe('UniswapLiquiditySwapAdapter', () => { + describe.only('UniswapLiquiditySwapAdapter', () => { describe('constructor', () => { it('should deploy with correct parameters', async () => { const {addressesProvider} = testEnv; @@ -1959,7 +1959,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { }); }); - describe('UniswapRepayAdapter', () => { + describe.only('UniswapRepayAdapter', () => { describe('constructor', () => { it('should deploy with correct parameters', async () => { const {addressesProvider} = testEnv; From 2e70cc628841c586d63ff2a51c5cfd0a2a6aba07 Mon Sep 17 00:00:00 2001 From: Gerardo Nardelli Date: Tue, 17 Nov 2020 13:31:05 -0300 Subject: [PATCH 24/62] remove .only in tests --- test/uniswapAdapters.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/uniswapAdapters.spec.ts b/test/uniswapAdapters.spec.ts index 6f40530f..146e8320 100644 --- a/test/uniswapAdapters.spec.ts +++ b/test/uniswapAdapters.spec.ts @@ -241,7 +241,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { }); }); - describe.only('UniswapLiquiditySwapAdapter', () => { + describe('UniswapLiquiditySwapAdapter', () => { describe('constructor', () => { it('should deploy with correct parameters', async () => { const {addressesProvider} = testEnv; @@ -1959,7 +1959,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { }); }); - describe.only('UniswapRepayAdapter', () => { + describe('UniswapRepayAdapter', () => { describe('constructor', () => { it('should deploy with correct parameters', async () => { const {addressesProvider} = testEnv; From 8a303c61950130a1a0b9aaae4a767b902aa00840 Mon Sep 17 00:00:00 2001 From: Gerardo Nardelli Date: Thu, 19 Nov 2020 16:39:00 -0300 Subject: [PATCH 25/62] Refactor repayMode in repayAdapter --- contracts/adapters/UniswapRepayAdapter.sol | 25 ++++++++++++++-------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/contracts/adapters/UniswapRepayAdapter.sol b/contracts/adapters/UniswapRepayAdapter.sol index 2e09f746..d8461400 100644 --- a/contracts/adapters/UniswapRepayAdapter.sol +++ b/contracts/adapters/UniswapRepayAdapter.sol @@ -16,11 +16,18 @@ import {ReserveLogic} from '../libraries/logic/ReserveLogic.sol'; **/ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { + /* + * STANDARD: Use the provided amounts parameters + * ALL_DEBT: Repay the whole debt balance + * ALL_COLLATERAL: Use all the collateral balance to repay the max amount of debt + */ + enum RepayMode {STANDARD, ALL_DEBT, ALL_COLLATERAL} + struct RepayParams { address assetToSwapTo; uint256 repayAmount; uint256 rateMode; - bool repayAllDebt; + RepayMode repayMode; PermitSignature permitSignature; } @@ -45,7 +52,7 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { * address Address of the reserve to be swapped to and repay * uint256 repayAmount Amount of debt to be repaid * uint256 rateMode Rate modes of the debt to be repaid - * bool repayAllDebt Flag indicating if all the debt should be repaid + * RepayMode repayMode Enum indicating the repaid mode * uint256 permitAmount Amount for the permit signature * uint256 deadline Deadline for the permit signature * uint8 v V param for the permit signature @@ -70,7 +77,7 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { decodedParams.repayAmount, decodedParams.rateMode, initiator, - decodedParams.repayAllDebt, + decodedParams.repayMode, premiums[0], decodedParams.permitSignature ); @@ -97,13 +104,13 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { uint256 repayAmount, uint256 rateMode, address initiator, - bool repayAllDebt, + RepayMode repayMode, uint256 premium, PermitSignature memory permitSignature ) internal { uint256 debtRepayAmount; - if (repayAllDebt) { + if (repayMode == RepayMode.ALL_DEBT) { ReserveLogic.ReserveData memory reserveDebtData = _getReserveData(assetTo); address debtToken = ReserveLogic.InterestRateMode(rateMode) == ReserveLogic.InterestRateMode.STABLE @@ -137,7 +144,7 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { * address Address of the reserve to be swapped to and repay * uint256 repayAmount Amount of debt to be repaid * uint256 rateMode Rate modes of the debt to be repaid - * bool repayAllDebt Flag indicating if all the debt should be repaid + * RepayMode repayMode Enum indicating the repaid mode * uint256 permitAmount Amount for the permit signature * uint256 deadline Deadline for the permit signature * uint8 v V param for the permit signature @@ -150,19 +157,19 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { address assetToSwapTo, uint256 repayAmount, uint256 rateMode, - bool repayAllDebt, + RepayMode repayMode, uint256 permitAmount, uint256 deadline, uint8 v, bytes32 r, bytes32 s - ) = abi.decode(params, (address, uint256, uint256, bool, uint256, uint256, uint8, bytes32, bytes32)); + ) = abi.decode(params, (address, uint256, uint256, RepayMode, uint256, uint256, uint8, bytes32, bytes32)); return RepayParams( assetToSwapTo, repayAmount, rateMode, - repayAllDebt, + repayMode, PermitSignature( permitAmount, deadline, From b48b50208a160ab3839d5ed9117d5335c8e21c39 Mon Sep 17 00:00:00 2001 From: Gerardo Nardelli Date: Fri, 20 Nov 2020 09:06:28 -0300 Subject: [PATCH 26/62] Support using all the collateral for a debt repay --- contracts/adapters/UniswapRepayAdapter.sol | 44 ++- helpers/contracts-helpers.ts | 16 +- test/uniswapAdapters.spec.ts | 318 +++++++++++++++++---- 3 files changed, 317 insertions(+), 61 deletions(-) diff --git a/contracts/adapters/UniswapRepayAdapter.sol b/contracts/adapters/UniswapRepayAdapter.sol index d8461400..616a6ce1 100644 --- a/contracts/adapters/UniswapRepayAdapter.sol +++ b/contracts/adapters/UniswapRepayAdapter.sol @@ -109,29 +109,40 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { PermitSignature memory permitSignature ) internal { uint256 debtRepayAmount; + uint256 amountSwapped; - if (repayMode == RepayMode.ALL_DEBT) { - ReserveLogic.ReserveData memory reserveDebtData = _getReserveData(assetTo); + ReserveLogic.ReserveData memory reserveData = _getReserveData(assetFrom); - address debtToken = ReserveLogic.InterestRateMode(rateMode) == ReserveLogic.InterestRateMode.STABLE - ? reserveDebtData.stableDebtTokenAddress - : reserveDebtData.variableDebtTokenAddress; + if (repayMode == RepayMode.ALL_COLLATERAL) { + uint256 aTokenInitiatorBalance = IERC20(reserveData.aTokenAddress).balanceOf(initiator); + amountSwapped = aTokenInitiatorBalance.sub(premium); - debtRepayAmount = IERC20(debtToken).balanceOf(initiator); + debtRepayAmount = _swapExactTokensForTokens(assetFrom, assetTo, amountSwapped, repayAmount); } else { - debtRepayAmount = repayAmount; - } + if (repayMode == RepayMode.ALL_DEBT) { + ReserveLogic.ReserveData memory reserveDebtData = _getReserveData(assetTo); - uint256 amountSwapped = _swapTokensForExactTokens(assetFrom, assetTo, amount, debtRepayAmount); + address debtToken = ReserveLogic.InterestRateMode(rateMode) == ReserveLogic.InterestRateMode.STABLE + ? reserveDebtData.stableDebtTokenAddress + : reserveDebtData.variableDebtTokenAddress; + + debtRepayAmount = IERC20(debtToken).balanceOf(initiator); + } else { + debtRepayAmount = repayAmount; + } + + amountSwapped = _swapTokensForExactTokens(assetFrom, assetTo, amount, debtRepayAmount); + } // Repay debt IERC20(assetTo).approve(address(POOL), debtRepayAmount); POOL.repay(assetTo, debtRepayAmount, rateMode, initiator); + // In the case the repay amount provided exceeded the actual debt, send the leftovers to the user + _sendRepayLeftovers(assetTo, initiator); uint256 flashLoanDebt = amount.add(premium); uint256 amountToPull = amountSwapped.add(premium); - ReserveLogic.ReserveData memory reserveData = _getReserveData(assetFrom); _pullAToken(assetFrom, reserveData.aTokenAddress, initiator, amountToPull, permitSignature); // Repay flashloan @@ -179,4 +190,17 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { ) ); } + + /** + * @dev Transfers the balance of the adapter to the user, as there shouldn't be any leftover in the adapter + * @param asset address of the asset + * @param user address + */ + function _sendRepayLeftovers(address asset, address user) internal { + uint256 assetLeftover = IERC20(asset).balanceOf(address(this)); + + if (assetLeftover > 0) { + IERC20(asset).transfer(user, assetLeftover); + } + } } diff --git a/helpers/contracts-helpers.ts b/helpers/contracts-helpers.ts index d420c00e..a055fb32 100644 --- a/helpers/contracts-helpers.ts +++ b/helpers/contracts-helpers.ts @@ -262,7 +262,7 @@ export const buildRepayAdapterParams = ( assetToSwapTo: tEthereumAddress, repayAmount: BigNumberish, rateMode: BigNumberish, - repayAllDebt: BigNumberish, + repayMode: BigNumberish, permitAmount: BigNumberish, deadline: BigNumberish, v: BigNumberish, @@ -270,7 +270,17 @@ export const buildRepayAdapterParams = ( s: string | Buffer ) => { return ethers.utils.defaultAbiCoder.encode( - ['address', 'uint256', 'uint256', 'bool', 'uint256', 'uint256', 'uint8', 'bytes32', 'bytes32'], - [assetToSwapTo, repayAmount, rateMode, repayAllDebt, permitAmount, deadline, v, r, s] + [ + 'address', + 'uint256', + 'uint256', + 'uint256', + 'uint256', + 'uint256', + 'uint8', + 'bytes32', + 'bytes32', + ], + [assetToSwapTo, repayAmount, rateMode, repayMode, permitAmount, deadline, v, r, s] ); }; diff --git a/test/uniswapAdapters.spec.ts b/test/uniswapAdapters.spec.ts index 146e8320..2f0d35c4 100644 --- a/test/uniswapAdapters.spec.ts +++ b/test/uniswapAdapters.spec.ts @@ -1,4 +1,4 @@ -import {makeSuite, TestEnv} from './helpers/make-suite'; +import { makeSuite, TestEnv } from './helpers/make-suite'; import { convertToCurrencyDecimals, getContract, @@ -7,24 +7,24 @@ import { buildLiquiditySwapParams, buildRepayAdapterParams, } from '../helpers/contracts-helpers'; -import {getMockUniswapRouter} from '../helpers/contracts-getters'; +import { getMockUniswapRouter } from '../helpers/contracts-getters'; import { deployUniswapLiquiditySwapAdapter, deployUniswapRepayAdapter, } from '../helpers/contracts-deployments'; -import {MockUniswapV2Router02} from '../types/MockUniswapV2Router02'; -import {Zero} from '@ethersproject/constants'; +import { MockUniswapV2Router02 } from '../types/MockUniswapV2Router02'; +import { Zero } from '@ethersproject/constants'; import BigNumber from 'bignumber.js'; -import {DRE, evmRevert, evmSnapshot} from '../helpers/misc-utils'; -import {ethers} from 'ethers'; -import {eContractid} from '../helpers/types'; -import {AToken} from '../types/AToken'; -import {StableDebtToken} from '../types/StableDebtToken'; -import {BUIDLEREVM_CHAINID} from '../helpers/buidler-constants'; -import {MAX_UINT_AMOUNT, USD_ADDRESS} from '../helpers/constants'; -const {parseEther} = ethers.utils; +import { DRE, evmRevert, evmSnapshot } from '../helpers/misc-utils'; +import { ethers } from 'ethers'; +import { eContractid } from '../helpers/types'; +import { AToken } from '../types/AToken'; +import { StableDebtToken } from '../types/StableDebtToken'; +import { BUIDLEREVM_CHAINID } from '../helpers/buidler-constants'; +import { MAX_UINT_AMOUNT, USD_ADDRESS } from '../helpers/constants'; +const { parseEther } = ethers.utils; -const {expect} = require('chai'); +const { expect } = require('chai'); makeSuite('Uniswap adapters', (testEnv: TestEnv) => { let mockUniswapRouter: MockUniswapV2Router02; @@ -45,7 +45,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { describe('BaseUniswapAdapter', () => { describe('getAmountsOut', () => { it('should return the estimated amountOut and prices for the asset swap', async () => { - const {weth, dai, uniswapLiquiditySwapAdapter, oracle} = testEnv; + const { weth, dai, uniswapLiquiditySwapAdapter, oracle } = testEnv; const amountIn = parseEther('1'); const flashloanPremium = amountIn.mul(9).div(10000); @@ -94,7 +94,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { expect(result['3']).to.be.eq(daiUsdValue); }); it('should work correctly with different decimals', async () => { - const {aave, usdc, uniswapLiquiditySwapAdapter, oracle} = testEnv; + const { aave, usdc, uniswapLiquiditySwapAdapter, oracle } = testEnv; const amountIn = parseEther('10'); const flashloanPremium = amountIn.mul(9).div(10000); @@ -148,7 +148,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { describe('getAmountsIn', () => { it('should return the estimated required amountIn for the asset swap', async () => { - const {weth, dai, uniswapLiquiditySwapAdapter, oracle} = testEnv; + const { weth, dai, uniswapLiquiditySwapAdapter, oracle } = testEnv; const amountIn = parseEther('1'); const flashloanPremium = amountIn.mul(9).div(10000); @@ -193,7 +193,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { expect(result['3']).to.be.eq(daiUsdValue); }); it('should work correctly with different decimals', async () => { - const {aave, usdc, uniswapLiquiditySwapAdapter, oracle} = testEnv; + const { aave, usdc, uniswapLiquiditySwapAdapter, oracle } = testEnv; const amountIn = parseEther('10'); const flashloanPremium = amountIn.mul(9).div(10000); @@ -244,7 +244,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { describe('UniswapLiquiditySwapAdapter', () => { describe('constructor', () => { it('should deploy with correct parameters', async () => { - const {addressesProvider} = testEnv; + const { addressesProvider } = testEnv; await deployUniswapLiquiditySwapAdapter([ addressesProvider.address, mockUniswapRouter.address, @@ -260,7 +260,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { describe('executeOperation', () => { beforeEach(async () => { - const {users, weth, dai, usdc, pool, deployer} = testEnv; + const { users, weth, dai, usdc, pool, deployer } = testEnv; const userAddress = users[0].address; // Provide liquidity @@ -280,7 +280,16 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { }); it('should correctly swap tokens and deposit the out tokens in the pool', async () => { - const {users, weth, oracle, dai, aDai, aWETH, pool, uniswapLiquiditySwapAdapter} = testEnv; + const { + users, + weth, + oracle, + dai, + aDai, + aWETH, + pool, + uniswapLiquiditySwapAdapter, + } = testEnv; const user = users[0].signer; const userAddress = users[0].address; @@ -542,7 +551,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { deadline, amountWETHtoSwap.toString() ); - const {v: aWETHv, r: aWETHr, s: aWETHs} = getSignatureFromTypedData( + const { v: aWETHv, r: aWETHr, s: aWETHs } = getSignatureFromTypedData( ownerPrivateKey, aWethMsgParams ); @@ -559,7 +568,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { deadline, amountUSDCtoSwap.toString() ); - const {v: aUsdcv, r: aUsdcr, s: aUsdcs} = getSignatureFromTypedData( + const { v: aUsdcv, r: aUsdcr, s: aUsdcs } = getSignatureFromTypedData( ownerPrivateKey, aUsdcMsgParams ); @@ -607,7 +616,16 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { }); it('should correctly swap tokens with permit', async () => { - const {users, weth, oracle, dai, aDai, aWETH, pool, uniswapLiquiditySwapAdapter} = testEnv; + const { + users, + weth, + oracle, + dai, + aDai, + aWETH, + pool, + uniswapLiquiditySwapAdapter, + } = testEnv; const user = users[0].signer; const userAddress = users[0].address; @@ -648,7 +666,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { throw new Error('INVALID_OWNER_PK'); } - const {v, r, s} = getSignatureFromTypedData(ownerPrivateKey, msgParams); + const { v, r, s } = getSignatureFromTypedData(ownerPrivateKey, msgParams); const params = buildLiquiditySwapParams( [dai.address], @@ -695,7 +713,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { }); it('should revert if inconsistent params', async () => { - const {users, weth, oracle, dai, aWETH, pool, uniswapLiquiditySwapAdapter} = testEnv; + const { users, weth, oracle, dai, aWETH, pool, uniswapLiquiditySwapAdapter } = testEnv; const user = users[0].signer; const userAddress = users[0].address; @@ -924,7 +942,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { }); it('should revert if caller not lending pool', async () => { - const {users, weth, oracle, dai, aWETH, uniswapLiquiditySwapAdapter} = testEnv; + const { users, weth, oracle, dai, aWETH, uniswapLiquiditySwapAdapter } = testEnv; const user = users[0].signer; const userAddress = users[0].address; @@ -1066,7 +1084,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { }); it('should revert when min amount to receive exceeds the max slippage amount', async () => { - const {users, weth, oracle, dai, aWETH, pool, uniswapLiquiditySwapAdapter} = testEnv; + const { users, weth, oracle, dai, aWETH, pool, uniswapLiquiditySwapAdapter } = testEnv; const user = users[0].signer; const userAddress = users[0].address; @@ -1115,7 +1133,16 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { }); it('should correctly swap tokens all the balance', async () => { - const {users, weth, oracle, dai, aDai, aWETH, pool, uniswapLiquiditySwapAdapter} = testEnv; + const { + users, + weth, + oracle, + dai, + aDai, + aWETH, + pool, + uniswapLiquiditySwapAdapter, + } = testEnv; const user = users[0].signer; const userAddress = users[0].address; @@ -1188,7 +1215,16 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { }); it('should correctly swap tokens all the balance using permit', async () => { - const {users, weth, oracle, dai, aDai, aWETH, pool, uniswapLiquiditySwapAdapter} = testEnv; + const { + users, + weth, + oracle, + dai, + aDai, + aWETH, + pool, + uniswapLiquiditySwapAdapter, + } = testEnv; const user = users[0].signer; const userAddress = users[0].address; @@ -1229,7 +1265,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { throw new Error('INVALID_OWNER_PK'); } - const {v, r, s} = getSignatureFromTypedData(ownerPrivateKey, msgParams); + const { v, r, s } = getSignatureFromTypedData(ownerPrivateKey, msgParams); const params = buildLiquiditySwapParams( [dai.address], @@ -1283,7 +1319,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { describe('swapAndDeposit', () => { beforeEach(async () => { - const {users, weth, dai, pool, deployer} = testEnv; + const { users, weth, dai, pool, deployer } = testEnv; const userAddress = users[0].address; // Provide liquidity @@ -1298,7 +1334,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { }); it('should correctly swap tokens and deposit the out tokens in the pool', async () => { - const {users, weth, oracle, dai, aDai, aWETH, uniswapLiquiditySwapAdapter} = testEnv; + const { users, weth, oracle, dai, aDai, aWETH, uniswapLiquiditySwapAdapter } = testEnv; const user = users[0].signer; const userAddress = users[0].address; @@ -1355,7 +1391,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { }); it('should correctly swap tokens using permit', async () => { - const {users, weth, oracle, dai, aDai, aWETH, uniswapLiquiditySwapAdapter} = testEnv; + const { users, weth, oracle, dai, aDai, aWETH, uniswapLiquiditySwapAdapter } = testEnv; const user = users[0].signer; const userAddress = users[0].address; @@ -1393,7 +1429,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { throw new Error('INVALID_OWNER_PK'); } - const {v, r, s} = getSignatureFromTypedData(ownerPrivateKey, msgParams); + const { v, r, s } = getSignatureFromTypedData(ownerPrivateKey, msgParams); await expect( uniswapLiquiditySwapAdapter.connect(user).swapAndDeposit( @@ -1433,7 +1469,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { }); it('should revert if inconsistent params', async () => { - const {users, weth, dai, uniswapLiquiditySwapAdapter, oracle} = testEnv; + const { users, weth, dai, uniswapLiquiditySwapAdapter, oracle } = testEnv; const user = users[0].signer; const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); @@ -1529,7 +1565,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { }); it('should revert when min amount to receive exceeds the max slippage amount', async () => { - const {users, weth, oracle, dai, aWETH, uniswapLiquiditySwapAdapter} = testEnv; + const { users, weth, oracle, dai, aWETH, uniswapLiquiditySwapAdapter } = testEnv; const user = users[0].signer; const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); @@ -1740,7 +1776,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { deadline, amountWETHtoSwap.toString() ); - const {v: aWETHv, r: aWETHr, s: aWETHs} = getSignatureFromTypedData( + const { v: aWETHv, r: aWETHr, s: aWETHs } = getSignatureFromTypedData( ownerPrivateKey, aWethMsgParams ); @@ -1757,7 +1793,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { deadline, amountUSDCtoSwap.toString() ); - const {v: aUsdcv, r: aUsdcr, s: aUsdcs} = getSignatureFromTypedData( + const { v: aUsdcv, r: aUsdcr, s: aUsdcs } = getSignatureFromTypedData( ownerPrivateKey, aUsdcMsgParams ); @@ -1806,7 +1842,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { }); it('should correctly swap all the balance when using a bigger amount', async () => { - const {users, weth, oracle, dai, aDai, aWETH, uniswapLiquiditySwapAdapter} = testEnv; + const { users, weth, oracle, dai, aDai, aWETH, uniswapLiquiditySwapAdapter } = testEnv; const user = users[0].signer; const userAddress = users[0].address; @@ -1873,7 +1909,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { }); it('should correctly swap all the balance when using permit', async () => { - const {users, weth, oracle, dai, aDai, aWETH, uniswapLiquiditySwapAdapter} = testEnv; + const { users, weth, oracle, dai, aDai, aWETH, uniswapLiquiditySwapAdapter } = testEnv; const user = users[0].signer; const userAddress = users[0].address; @@ -1917,7 +1953,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { deadline, bigAmountToSwap.toString() ); - const {v, r, s} = getSignatureFromTypedData(ownerPrivateKey, aWethMsgParams); + const { v, r, s } = getSignatureFromTypedData(ownerPrivateKey, aWethMsgParams); await expect( uniswapLiquiditySwapAdapter.connect(user).swapAndDeposit( @@ -1962,7 +1998,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { describe('UniswapRepayAdapter', () => { describe('constructor', () => { it('should deploy with correct parameters', async () => { - const {addressesProvider} = testEnv; + const { addressesProvider } = testEnv; await deployUniswapRepayAdapter([addressesProvider.address, mockUniswapRouter.address]); }); @@ -1974,7 +2010,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { describe('executeOperation', () => { beforeEach(async () => { - const {users, weth, dai, usdc, aave, pool, deployer} = testEnv; + const { users, weth, dai, usdc, aave, pool, deployer } = testEnv; const userAddress = users[0].address; // Provide liquidity @@ -2157,7 +2193,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { throw new Error('INVALID_OWNER_PK'); } - const {v, r, s} = getSignatureFromTypedData(ownerPrivateKey, msgParams); + const { v, r, s } = getSignatureFromTypedData(ownerPrivateKey, msgParams); await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, flashloanAmount); @@ -2203,7 +2239,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { }); it('should revert if caller not lending pool', async () => { - const {users, pool, weth, aWETH, oracle, dai, uniswapRepayAdapter} = testEnv; + const { users, pool, weth, aWETH, oracle, dai, uniswapRepayAdapter } = testEnv; const user = users[0].signer; const userAddress = users[0].address; @@ -2252,7 +2288,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { }); it('should revert if there is not debt to repay with the specified rate mode', async () => { - const {users, pool, weth, oracle, dai, uniswapRepayAdapter, aWETH} = testEnv; + const { users, pool, weth, oracle, dai, uniswapRepayAdapter, aWETH } = testEnv; const user = users[0].signer; const userAddress = users[0].address; @@ -2306,7 +2342,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { }); it('should revert if there is not debt to repay', async () => { - const {users, pool, weth, oracle, dai, uniswapRepayAdapter, aWETH} = testEnv; + const { users, pool, weth, oracle, dai, uniswapRepayAdapter, aWETH } = testEnv; const user = users[0].signer; const userAddress = users[0].address; @@ -2357,7 +2393,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { }); it('should revert when max amount allowed to swap is bigger than max slippage', async () => { - const {users, pool, weth, oracle, dai, aWETH, uniswapRepayAdapter} = testEnv; + const { users, pool, weth, oracle, dai, aWETH, uniswapRepayAdapter } = testEnv; const user = users[0].signer; const userAddress = users[0].address; @@ -2681,6 +2717,192 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); }); + + it('should swap and repay debt using all the collateral for a bigger debt', async () => { + const { + users, + pool, + weth, + aWETH, + oracle, + dai, + uniswapRepayAdapter, + helpersContract, + } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + const userDebt = new BigNumber(expectedDaiAmount.toString()).multipliedBy(1.1).toFixed(0); + // Open user Debt + await pool.connect(user).borrow(dai.address, userDebt, 1, 0, userAddress); + + const daiStableDebtTokenAddress = ( + await helpersContract.getReserveTokensAddresses(dai.address) + ).stableDebtTokenAddress; + + const daiStableDebtContract = await getContract( + eContractid.StableDebtToken, + daiStableDebtTokenAddress + ); + + const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); + + const liquidityToSwap = amountWETHtoSwap; + await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + + // Subtract the FL fee from the amount to be swapped 0,09% + const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); + + const actualWEthSwapped = new BigNumber(flashloanAmount.toString()) + .multipliedBy(0.995) + .toFixed(0); + + // Remove other balance + await aWETH + .connect(user) + .transfer(users[1].address, userAEthBalanceBefore.sub(actualWEthSwapped)); + + await mockUniswapRouter.connect(user).setAmountToReturn(weth.address, expectedDaiAmount); + + const params = buildRepayAdapterParams( + dai.address, + expectedDaiAmount, + 1, + 2, + 0, + 0, + 0, + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000' + ); + + await pool + .connect(user) + .flashLoan( + uniswapRepayAdapter.address, + [weth.address], + [flashloanAmount.toString()], + [0], + userAddress, + params, + 0 + ); + + const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); + const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); + const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); + const userAEthBalance = await aWETH.balanceOf(userAddress); + const adapterAEthBalance = await aWETH.balanceOf(uniswapRepayAdapter.address); + + expect(adapterAEthBalance).to.be.eq(Zero); + expect(adapterWethBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount); + expect(userDaiStableDebtAmount).to.be.lt(expectedDaiAmount); + expect(userAEthBalance).to.be.eq(Zero); + }); + + it('should swap and repay debt using all the collateral for a smaller debt', async () => { + const { + users, + pool, + weth, + aWETH, + oracle, + dai, + uniswapRepayAdapter, + helpersContract, + } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + const userDebt = new BigNumber(expectedDaiAmount.toString()).multipliedBy(0.9).toFixed(0); + // Open user Debt + await pool.connect(user).borrow(dai.address, userDebt, 1, 0, userAddress); + + const daiStableDebtTokenAddress = ( + await helpersContract.getReserveTokensAddresses(dai.address) + ).stableDebtTokenAddress; + + const daiStableDebtContract = await getContract( + eContractid.StableDebtToken, + daiStableDebtTokenAddress + ); + + const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); + + const liquidityToSwap = amountWETHtoSwap; + await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + + // Subtract the FL fee from the amount to be swapped 0,09% + const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); + + const actualWEthSwapped = new BigNumber(flashloanAmount.toString()) + .multipliedBy(0.995) + .toFixed(0); + + // Remove other balance + await aWETH + .connect(user) + .transfer(users[1].address, userAEthBalanceBefore.sub(actualWEthSwapped)); + + await mockUniswapRouter.connect(user).setAmountToReturn(weth.address, expectedDaiAmount); + + const params = buildRepayAdapterParams( + dai.address, + expectedDaiAmount, + 1, + 2, + 0, + 0, + 0, + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000' + ); + + await pool + .connect(user) + .flashLoan( + uniswapRepayAdapter.address, + [weth.address], + [flashloanAmount.toString()], + [0], + userAddress, + params, + 0 + ); + + const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); + const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); + const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); + const userAEthBalance = await aWETH.balanceOf(userAddress); + const adapterAEthBalance = await aWETH.balanceOf(uniswapRepayAdapter.address); + + expect(adapterAEthBalance).to.be.eq(Zero); + expect(adapterWethBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); // Validate there are no leftovers + expect(userDaiStableDebtAmountBefore).to.be.gte(userDebt); + expect(userDaiStableDebtAmount).to.be.eq(Zero); + expect(userAEthBalance).to.be.eq(Zero); + }); }); }); }); From 43d05c2bdf846fef7c8f4145988511a36fea3549 Mon Sep 17 00:00:00 2001 From: Gerardo Nardelli Date: Fri, 20 Nov 2020 15:53:50 -0300 Subject: [PATCH 27/62] Refactor repayAdapter to flash loan the debt asset instead of the collateral --- contracts/adapters/UniswapRepayAdapter.sol | 105 +++--- helpers/contracts-helpers.ts | 8 +- test/uniswapAdapters.spec.ts | 358 +++++---------------- 3 files changed, 114 insertions(+), 357 deletions(-) diff --git a/contracts/adapters/UniswapRepayAdapter.sol b/contracts/adapters/UniswapRepayAdapter.sol index 616a6ce1..c42df484 100644 --- a/contracts/adapters/UniswapRepayAdapter.sol +++ b/contracts/adapters/UniswapRepayAdapter.sol @@ -16,18 +16,10 @@ import {ReserveLogic} from '../libraries/logic/ReserveLogic.sol'; **/ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { - /* - * STANDARD: Use the provided amounts parameters - * ALL_DEBT: Repay the whole debt balance - * ALL_COLLATERAL: Use all the collateral balance to repay the max amount of debt - */ - enum RepayMode {STANDARD, ALL_DEBT, ALL_COLLATERAL} - struct RepayParams { - address assetToSwapTo; - uint256 repayAmount; + address collateralAsset; + uint256 collateralAmount; uint256 rateMode; - RepayMode repayMode; PermitSignature permitSignature; } @@ -40,17 +32,17 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { {} /** - * @dev Swaps the received reserve amount into the asset specified in the params. The received funds from the swap are - * then used to repay a debt on the protocol on behalf of the user. - * The user should give this contract allowance to pull the ATokens in order to withdraw the underlying asset and - * repay the flash loan. + * @dev Uses the received funds from the flash loan to repay a debt on the protocol on behalf of the user. Then pulls + * the collateral from the user and swaps it to repay the flash loan. + * The user should give this contract allowance to pull the ATokens in order to withdraw the underlying asset, swap it + * and repay the flash loan. * @param assets Address to be swapped * @param amounts Amount of the reserve to be swapped * @param premiums Fee of the flash loan * @param initiator Address of the user * @param params Additional variadic field to include extra params. Expected parameters: - * address Address of the reserve to be swapped to and repay - * uint256 repayAmount Amount of debt to be repaid + * address collateralAsset Address of the reserve to be swapped + * uint256 collateralAmount Amount of reserve to be swapped * uint256 rateMode Rate modes of the debt to be repaid * RepayMode repayMode Enum indicating the repaid mode * uint256 permitAmount Amount for the permit signature @@ -71,13 +63,12 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { RepayParams memory decodedParams = _decodeParams(params); _swapAndRepay( + decodedParams.collateralAsset, assets[0], - decodedParams.assetToSwapTo, amounts[0], - decodedParams.repayAmount, + decodedParams.collateralAmount, decodedParams.rateMode, initiator, - decodedParams.repayMode, premiums[0], decodedParams.permitSignature ); @@ -86,12 +77,12 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { } /** - * @dev Perform the swap and the repay of the debt + * @dev Perform the repay of the debt, pulls the initiator collateral and swaps to repay the flash loan * * @param assetFrom Address of token to be swapped - * @param assetTo Address of token to be received - * @param amount Amount of the reserve to be swapped - * @param repayAmount Amount of the debt to be repaid + * @param assetTo Address of debt token to be received from the swap + * @param amount Amount of the debt to be repaid + * @param collateralAmount Amount of the reserve to be swapped * @param rateMode Rate mode of the debt to be repaid * @param initiator Address of the user * @param premium Fee of the flash loan @@ -101,61 +92,39 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { address assetFrom, address assetTo, uint256 amount, - uint256 repayAmount, + uint256 collateralAmount, uint256 rateMode, address initiator, - RepayMode repayMode, uint256 premium, PermitSignature memory permitSignature ) internal { - uint256 debtRepayAmount; - uint256 amountSwapped; - - ReserveLogic.ReserveData memory reserveData = _getReserveData(assetFrom); - - if (repayMode == RepayMode.ALL_COLLATERAL) { - uint256 aTokenInitiatorBalance = IERC20(reserveData.aTokenAddress).balanceOf(initiator); - amountSwapped = aTokenInitiatorBalance.sub(premium); - - debtRepayAmount = _swapExactTokensForTokens(assetFrom, assetTo, amountSwapped, repayAmount); - } else { - if (repayMode == RepayMode.ALL_DEBT) { - ReserveLogic.ReserveData memory reserveDebtData = _getReserveData(assetTo); - - address debtToken = ReserveLogic.InterestRateMode(rateMode) == ReserveLogic.InterestRateMode.STABLE - ? reserveDebtData.stableDebtTokenAddress - : reserveDebtData.variableDebtTokenAddress; - - debtRepayAmount = IERC20(debtToken).balanceOf(initiator); - } else { - debtRepayAmount = repayAmount; - } - - amountSwapped = _swapTokensForExactTokens(assetFrom, assetTo, amount, debtRepayAmount); - } - // Repay debt - IERC20(assetTo).approve(address(POOL), debtRepayAmount); - POOL.repay(assetTo, debtRepayAmount, rateMode, initiator); - // In the case the repay amount provided exceeded the actual debt, send the leftovers to the user - _sendRepayLeftovers(assetTo, initiator); + IERC20(assetTo).approve(address(POOL), amount); + POOL.repay(assetTo, amount, rateMode, initiator); + uint256 debtRepayLeftovers = IERC20(assetTo).balanceOf(address(this)); uint256 flashLoanDebt = amount.add(premium); - uint256 amountToPull = amountSwapped.add(premium); + uint256 neededForFlashLoanDebt = flashLoanDebt.sub(debtRepayLeftovers); - _pullAToken(assetFrom, reserveData.aTokenAddress, initiator, amountToPull, permitSignature); + // Pull aTokens from user + ReserveLogic.ReserveData memory reserveData = _getReserveData(assetFrom); + _pullAToken(assetFrom, reserveData.aTokenAddress, initiator, collateralAmount, permitSignature); + + uint256 amountSwapped = _swapTokensForExactTokens(assetFrom, assetTo, collateralAmount, neededForFlashLoanDebt); + + // Send collateral leftovers from swap to the user + _sendLeftovers(assetFrom, initiator); // Repay flashloan - IERC20(assetFrom).approve(address(POOL), flashLoanDebt); + IERC20(assetTo).approve(address(POOL), flashLoanDebt); } /** * @dev Decodes debt information encoded in flashloan params * @param params Additional variadic field to include extra params. Expected parameters: - * address Address of the reserve to be swapped to and repay - * uint256 repayAmount Amount of debt to be repaid + * address collateralAsset Address of the reserve to be swapped + * uint256 collateralAmount Amount of reserve to be swapped * uint256 rateMode Rate modes of the debt to be repaid - * RepayMode repayMode Enum indicating the repaid mode * uint256 permitAmount Amount for the permit signature * uint256 deadline Deadline for the permit signature * uint8 v V param for the permit signature @@ -165,22 +134,20 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { */ function _decodeParams(bytes memory params) internal pure returns (RepayParams memory) { ( - address assetToSwapTo, - uint256 repayAmount, + address collateralAsset, + uint256 collateralAmount, uint256 rateMode, - RepayMode repayMode, uint256 permitAmount, uint256 deadline, uint8 v, bytes32 r, bytes32 s - ) = abi.decode(params, (address, uint256, uint256, RepayMode, uint256, uint256, uint8, bytes32, bytes32)); + ) = abi.decode(params, (address, uint256, uint256, uint256, uint256, uint8, bytes32, bytes32)); return RepayParams( - assetToSwapTo, - repayAmount, + collateralAsset, + collateralAmount, rateMode, - repayMode, PermitSignature( permitAmount, deadline, @@ -196,7 +163,7 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { * @param asset address of the asset * @param user address */ - function _sendRepayLeftovers(address asset, address user) internal { + function _sendLeftovers(address asset, address user) internal { uint256 assetLeftover = IERC20(asset).balanceOf(address(this)); if (assetLeftover > 0) { diff --git a/helpers/contracts-helpers.ts b/helpers/contracts-helpers.ts index a055fb32..832e8ae7 100644 --- a/helpers/contracts-helpers.ts +++ b/helpers/contracts-helpers.ts @@ -259,10 +259,9 @@ export const buildLiquiditySwapParams = ( }; export const buildRepayAdapterParams = ( - assetToSwapTo: tEthereumAddress, - repayAmount: BigNumberish, + collateralAsset: tEthereumAddress, + collateralAmount: BigNumberish, rateMode: BigNumberish, - repayMode: BigNumberish, permitAmount: BigNumberish, deadline: BigNumberish, v: BigNumberish, @@ -276,11 +275,10 @@ export const buildRepayAdapterParams = ( 'uint256', 'uint256', 'uint256', - 'uint256', 'uint8', 'bytes32', 'bytes32', ], - [assetToSwapTo, repayAmount, rateMode, repayMode, permitAmount, deadline, v, r, s] + [collateralAsset, collateralAmount, rateMode, permitAmount, deadline, v, r, s] ); }; diff --git a/test/uniswapAdapters.spec.ts b/test/uniswapAdapters.spec.ts index 2f0d35c4..b821f238 100644 --- a/test/uniswapAdapters.spec.ts +++ b/test/uniswapAdapters.spec.ts @@ -2085,19 +2085,19 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); - // Subtract the FL fee from the amount to be swapped 0,09% - const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap); - await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, flashloanAmount); + const flashLoanDebt = new BigNumber(expectedDaiAmount.toString()) + .multipliedBy(1.0009) + .toFixed(0); const params = buildRepayAdapterParams( - dai.address, - expectedDaiAmount, + weth.address, + liquidityToSwap, 1, 0, 0, 0, - 0, '0x0000000000000000000000000000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000000000000000000000000000' ); @@ -2107,8 +2107,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { .connect(user) .flashLoan( uniswapRepayAdapter.address, - [weth.address], - [flashloanAmount.toString()], + [dai.address], + [expectedDaiAmount.toString()], [0], userAddress, params, @@ -2116,7 +2116,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { ) ) .to.emit(uniswapRepayAdapter, 'Swapped') - .withArgs(weth.address, dai.address, flashloanAmount.toString(), expectedDaiAmount); + .withArgs(weth.address, dai.address, liquidityToSwap.toString(), flashLoanDebt); const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); @@ -2170,9 +2170,6 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const liquidityToSwap = amountWETHtoSwap; const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); - // Subtract the FL fee from the amount to be swapped 0,09% - const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); - const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID; const deadline = MAX_UINT_AMOUNT; const nonce = (await aWETH._nonces(userAddress)).toNumber(); @@ -2195,13 +2192,16 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const { v, r, s } = getSignatureFromTypedData(ownerPrivateKey, msgParams); - await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, flashloanAmount); + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap); + + const flashLoanDebt = new BigNumber(expectedDaiAmount.toString()) + .multipliedBy(1.0009) + .toFixed(0); const params = buildRepayAdapterParams( - dai.address, - expectedDaiAmount, + weth.address, + liquidityToSwap, 1, - 0, liquidityToSwap, deadline, v, @@ -2214,8 +2214,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { .connect(user) .flashLoan( uniswapRepayAdapter.address, - [weth.address], - [flashloanAmount.toString()], + [dai.address], + [expectedDaiAmount.toString()], [0], userAddress, params, @@ -2223,7 +2223,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { ) ) .to.emit(uniswapRepayAdapter, 'Swapped') - .withArgs(weth.address, dai.address, flashloanAmount.toString(), expectedDaiAmount); + .withArgs(weth.address, dai.address, liquidityToSwap.toString(), flashLoanDebt); const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); @@ -2257,19 +2257,15 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const liquidityToSwap = amountWETHtoSwap; await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); - // Subtract the FL fee from the amount to be swapped 0,09% - const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); - - await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, flashloanAmount); + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap); const params = buildRepayAdapterParams( - dai.address, - expectedDaiAmount, + weth.address, + liquidityToSwap, 1, 0, 0, 0, - 0, '0x0000000000000000000000000000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000000000000000000000000000' ); @@ -2278,8 +2274,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { uniswapRepayAdapter .connect(user) .executeOperation( - [weth.address], - [flashloanAmount.toString()], + [dai.address], + [expectedDaiAmount.toString()], [0], userAddress, params @@ -2309,19 +2305,15 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const liquidityToSwap = amountWETHtoSwap; await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); - // Subtract the FL fee from the amount to be swapped 0,09% - const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); - - await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, flashloanAmount); + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap); const params = buildRepayAdapterParams( - dai.address, - expectedDaiAmount, + weth.address, + liquidityToSwap, 1, 0, 0, 0, - 0, '0x0000000000000000000000000000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000000000000000000000000000' ); @@ -2331,8 +2323,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { .connect(user) .flashLoan( uniswapRepayAdapter.address, - [weth.address], - [flashloanAmount.toString()], + [dai.address], + [expectedDaiAmount.toString()], [0], userAddress, params, @@ -2360,19 +2352,15 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const liquidityToSwap = amountWETHtoSwap; await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); - // Subtract the FL fee from the amount to be swapped 0,09% - const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); - - await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, flashloanAmount); + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap); const params = buildRepayAdapterParams( - dai.address, - expectedDaiAmount, + weth.address, + liquidityToSwap, 1, 0, 0, 0, - 0, '0x0000000000000000000000000000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000000000000000000000000000' ); @@ -2382,8 +2370,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { .connect(user) .flashLoan( uniswapRepayAdapter.address, - [weth.address], - [flashloanAmount.toString()], + [dai.address], + [expectedDaiAmount.toString()], [0], userAddress, params, @@ -2408,22 +2396,18 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { // Open user Debt await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); - await aWETH.connect(user).approve(uniswapRepayAdapter.address, amountWETHtoSwap); - - // Subtract the FL fee from the amount to be swapped 0,09% const bigMaxAmountToSwap = amountWETHtoSwap.mul(2); - const flashloanAmount = new BigNumber(bigMaxAmountToSwap.toString()).div(1.0009).toFixed(0); + await aWETH.connect(user).approve(uniswapRepayAdapter.address, bigMaxAmountToSwap); - await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, flashloanAmount); + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, bigMaxAmountToSwap); const params = buildRepayAdapterParams( - dai.address, - expectedDaiAmount, + weth.address, + bigMaxAmountToSwap, 1, 0, 0, 0, - 0, '0x0000000000000000000000000000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000000000000000000000000000' ); @@ -2433,8 +2417,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { .connect(user) .flashLoan( uniswapRepayAdapter.address, - [weth.address], - [flashloanAmount.toString()], + [dai.address], + [expectedDaiAmount.toString()], [0], userAddress, params, @@ -2482,26 +2466,27 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const liquidityToSwap = amountWETHtoSwap; await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + const userWethBalanceBefore = await weth.balanceOf(userAddress); - // Subtract the FL fee from the amount to be swapped 0,09% - const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); - - const actualWEthSwapped = new BigNumber(flashloanAmount.toString()) + const actualWEthSwapped = new BigNumber(liquidityToSwap.toString()) .multipliedBy(0.995) .toFixed(0); - const leftOverWeth = new BigNumber(flashloanAmount).minus(actualWEthSwapped); + const leftOverWeth = new BigNumber(liquidityToSwap.toString()).minus(actualWEthSwapped); await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, actualWEthSwapped); + const flashLoanDebt = new BigNumber(expectedDaiAmount.toString()) + .multipliedBy(1.0009) + .toFixed(0); + const params = buildRepayAdapterParams( - dai.address, - expectedDaiAmount, + weth.address, + liquidityToSwap, 1, 0, 0, 0, - 0, '0x0000000000000000000000000000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000000000000000000000000000' ); @@ -2511,8 +2496,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { .connect(user) .flashLoan( uniswapRepayAdapter.address, - [weth.address], - [flashloanAmount.toString()], + [dai.address], + [expectedDaiAmount.toString()], [0], userAddress, params, @@ -2520,13 +2505,14 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { ) ) .to.emit(uniswapRepayAdapter, 'Swapped') - .withArgs(weth.address, dai.address, actualWEthSwapped.toString(), expectedDaiAmount); + .withArgs(weth.address, dai.address, actualWEthSwapped.toString(), flashLoanDebt); const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); const userAEthBalance = await aWETH.balanceOf(userAddress); const adapterAEthBalance = await aWETH.balanceOf(uniswapRepayAdapter.address); + const userWethBalance = await weth.balanceOf(userAddress); expect(adapterAEthBalance).to.be.eq(Zero); expect(adapterWethBalance).to.be.eq(Zero); @@ -2534,13 +2520,11 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount); expect(userDaiStableDebtAmount).to.be.lt(expectedDaiAmount); expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); - expect(userAEthBalance).to.be.gt(userAEthBalanceBefore.sub(liquidityToSwap)); - expect(userAEthBalance).to.be.gte( - userAEthBalanceBefore.sub(liquidityToSwap).add(leftOverWeth.toString()) - ); + expect(userAEthBalance).to.be.eq(userAEthBalanceBefore.sub(liquidityToSwap)); + expect(userWethBalance).to.be.gte(userWethBalanceBefore.add(leftOverWeth.toString())); }); - it('should correctly swap tokens and repay the whole stable debt', async () => { + it('should correctly swap tokens and repay the whole stable debt with no leftovers', async () => { const { users, pool, @@ -2580,19 +2564,16 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); - // Subtract the FL fee from the amount to be swapped 0,09% - const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); + // Add a % to repay on top of the debt + const amountToRepay = new BigNumber(expectedDaiAmount.toString()) + .multipliedBy(1.1) + .toFixed(0); - await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, flashloanAmount); - - // Passed amount to repay is smaller than debt, - // but repayAllDebt flag is enabled so the whole debt should be paid - const amountToRepay = expectedDaiAmount.div(2); + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap); const params = buildRepayAdapterParams( - dai.address, - amountToRepay, - 1, + weth.address, + liquidityToSwap, 1, 0, 0, @@ -2605,8 +2586,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { .connect(user) .flashLoan( uniswapRepayAdapter.address, - [weth.address], - [flashloanAmount.toString()], + [dai.address], + [amountToRepay.toString()], [0], userAddress, params, @@ -2628,7 +2609,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); }); - it('should correctly swap tokens and repay the whole variable debt', async () => { + it('should correctly swap tokens and repay the whole variable debt with no leftovers', async () => { const { users, pool, @@ -2670,20 +2651,17 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); - // Subtract the FL fee from the amount to be swapped 0,09% - const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); + // Add a % to repay on top of the debt + const amountToRepay = new BigNumber(expectedDaiAmount.toString()) + .multipliedBy(1.1) + .toFixed(0); - await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, flashloanAmount); - - // Passed amount to repay is smaller than debt, - // but repayAllDebt flag is enabled so the whole debt should be paid - const amountToRepay = expectedDaiAmount.div(2); + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap); const params = buildRepayAdapterParams( - dai.address, - amountToRepay, + weth.address, + liquidityToSwap, 2, - 1, 0, 0, 0, @@ -2695,8 +2673,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { .connect(user) .flashLoan( uniswapRepayAdapter.address, - [weth.address], - [flashloanAmount.toString()], + [dai.address], + [amountToRepay.toString()], [0], userAddress, params, @@ -2717,192 +2695,6 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); }); - - it('should swap and repay debt using all the collateral for a bigger debt', async () => { - const { - users, - pool, - weth, - aWETH, - oracle, - dai, - uniswapRepayAdapter, - helpersContract, - } = testEnv; - const user = users[0].signer; - const userAddress = users[0].address; - - const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); - - const daiPrice = await oracle.getAssetPrice(dai.address); - const expectedDaiAmount = await convertToCurrencyDecimals( - dai.address, - new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) - ); - - const userDebt = new BigNumber(expectedDaiAmount.toString()).multipliedBy(1.1).toFixed(0); - // Open user Debt - await pool.connect(user).borrow(dai.address, userDebt, 1, 0, userAddress); - - const daiStableDebtTokenAddress = ( - await helpersContract.getReserveTokensAddresses(dai.address) - ).stableDebtTokenAddress; - - const daiStableDebtContract = await getContract( - eContractid.StableDebtToken, - daiStableDebtTokenAddress - ); - - const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); - - const liquidityToSwap = amountWETHtoSwap; - await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); - const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); - - // Subtract the FL fee from the amount to be swapped 0,09% - const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); - - const actualWEthSwapped = new BigNumber(flashloanAmount.toString()) - .multipliedBy(0.995) - .toFixed(0); - - // Remove other balance - await aWETH - .connect(user) - .transfer(users[1].address, userAEthBalanceBefore.sub(actualWEthSwapped)); - - await mockUniswapRouter.connect(user).setAmountToReturn(weth.address, expectedDaiAmount); - - const params = buildRepayAdapterParams( - dai.address, - expectedDaiAmount, - 1, - 2, - 0, - 0, - 0, - '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000' - ); - - await pool - .connect(user) - .flashLoan( - uniswapRepayAdapter.address, - [weth.address], - [flashloanAmount.toString()], - [0], - userAddress, - params, - 0 - ); - - const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); - const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); - const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); - const userAEthBalance = await aWETH.balanceOf(userAddress); - const adapterAEthBalance = await aWETH.balanceOf(uniswapRepayAdapter.address); - - expect(adapterAEthBalance).to.be.eq(Zero); - expect(adapterWethBalance).to.be.eq(Zero); - expect(adapterDaiBalance).to.be.eq(Zero); - expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount); - expect(userDaiStableDebtAmount).to.be.lt(expectedDaiAmount); - expect(userAEthBalance).to.be.eq(Zero); - }); - - it('should swap and repay debt using all the collateral for a smaller debt', async () => { - const { - users, - pool, - weth, - aWETH, - oracle, - dai, - uniswapRepayAdapter, - helpersContract, - } = testEnv; - const user = users[0].signer; - const userAddress = users[0].address; - - const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); - - const daiPrice = await oracle.getAssetPrice(dai.address); - const expectedDaiAmount = await convertToCurrencyDecimals( - dai.address, - new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) - ); - - const userDebt = new BigNumber(expectedDaiAmount.toString()).multipliedBy(0.9).toFixed(0); - // Open user Debt - await pool.connect(user).borrow(dai.address, userDebt, 1, 0, userAddress); - - const daiStableDebtTokenAddress = ( - await helpersContract.getReserveTokensAddresses(dai.address) - ).stableDebtTokenAddress; - - const daiStableDebtContract = await getContract( - eContractid.StableDebtToken, - daiStableDebtTokenAddress - ); - - const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); - - const liquidityToSwap = amountWETHtoSwap; - await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); - const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); - - // Subtract the FL fee from the amount to be swapped 0,09% - const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0); - - const actualWEthSwapped = new BigNumber(flashloanAmount.toString()) - .multipliedBy(0.995) - .toFixed(0); - - // Remove other balance - await aWETH - .connect(user) - .transfer(users[1].address, userAEthBalanceBefore.sub(actualWEthSwapped)); - - await mockUniswapRouter.connect(user).setAmountToReturn(weth.address, expectedDaiAmount); - - const params = buildRepayAdapterParams( - dai.address, - expectedDaiAmount, - 1, - 2, - 0, - 0, - 0, - '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000' - ); - - await pool - .connect(user) - .flashLoan( - uniswapRepayAdapter.address, - [weth.address], - [flashloanAmount.toString()], - [0], - userAddress, - params, - 0 - ); - - const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); - const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); - const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); - const userAEthBalance = await aWETH.balanceOf(userAddress); - const adapterAEthBalance = await aWETH.balanceOf(uniswapRepayAdapter.address); - - expect(adapterAEthBalance).to.be.eq(Zero); - expect(adapterWethBalance).to.be.eq(Zero); - expect(adapterDaiBalance).to.be.eq(Zero); // Validate there are no leftovers - expect(userDaiStableDebtAmountBefore).to.be.gte(userDebt); - expect(userDaiStableDebtAmount).to.be.eq(Zero); - expect(userAEthBalance).to.be.eq(Zero); - }); }); }); }); From 50e5ea907b9d3977b399ef78ef6ef06011ff1ca2 Mon Sep 17 00:00:00 2001 From: Gerardo Nardelli Date: Tue, 24 Nov 2020 12:11:13 -0300 Subject: [PATCH 28/62] Add first draft of swapAndRepay without using flash loans --- contracts/adapters/BaseUniswapAdapter.sol | 29 ++++++++++++------ contracts/adapters/UniswapRepayAdapter.sol | 34 ++++++++++++++++++++++ 2 files changed, 54 insertions(+), 9 deletions(-) diff --git a/contracts/adapters/BaseUniswapAdapter.sol b/contracts/adapters/BaseUniswapAdapter.sol index 7a727b7d..0e4ca193 100644 --- a/contracts/adapters/BaseUniswapAdapter.sol +++ b/contracts/adapters/BaseUniswapAdapter.sol @@ -73,7 +73,7 @@ contract BaseUniswapAdapter { view returns (uint256, uint256, uint256, uint256) { - AmountCalc memory results = _getAmountsOut(reserveIn, reserveOut, amountIn); + AmountCalc memory results = _getAmountsOutData(reserveIn, reserveOut, amountIn); return ( results.calculatedAmount, @@ -98,7 +98,7 @@ contract BaseUniswapAdapter { view returns (uint256, uint256, uint256, uint256) { - AmountCalc memory results = _getAmountsIn(reserveIn, reserveOut, amountOut); + AmountCalc memory results = _getAmountsInData(reserveIn, reserveOut, amountOut); return ( results.calculatedAmount, @@ -291,7 +291,7 @@ contract BaseUniswapAdapter { * uint256 In amount of reserveIn value denominated in USD (8 decimals) * uint256 Out amount of reserveOut value denominated in USD (8 decimals) */ - function _getAmountsOut(address reserveIn, address reserveOut, uint256 amountIn) internal view returns (AmountCalc memory) { + function _getAmountsOutData(address reserveIn, address reserveOut, uint256 amountIn) internal view returns (AmountCalc memory) { // Subtract flash loan fee uint256 finalAmountIn = amountIn.sub(amountIn.mul(FLASHLOAN_PREMIUM_TOTAL).div(10000)); @@ -328,12 +328,8 @@ contract BaseUniswapAdapter { * uint256 In amount of reserveIn value denominated in USD (8 decimals) * uint256 Out amount of reserveOut value denominated in USD (8 decimals) */ - function _getAmountsIn(address reserveIn, address reserveOut, uint256 amountOut) internal view returns (AmountCalc memory) { - address[] memory path = new address[](2); - path[0] = reserveIn; - path[1] = reserveOut; - - uint256[] memory amounts = UNISWAP_ROUTER.getAmountsIn(amountOut, path); + function _getAmountsInData(address reserveIn, address reserveOut, uint256 amountOut) internal view returns (AmountCalc memory) { + uint256[] memory amounts = _getAmountsIn(reserveIn, reserveOut, amountOut); // Add flash loan fee uint256 finalAmountIn = amounts[0].add(amounts[0].mul(FLASHLOAN_PREMIUM_TOTAL).div(10000)); @@ -353,4 +349,19 @@ contract BaseUniswapAdapter { _calcUsdValue(reserveOut, amountOut, reserveOutDecimals) ); } + + /** + * @dev Calculates the input asset amount required to buy the given output asset amount + * @param reserveIn Address of the asset to be swap from + * @param reserveOut Address of the asset to be swap to + * @param amountOut Amount of reserveOut + * @return uint256[] amounts Array containing the amountIn and amountOut for a swap + */ + function _getAmountsIn(address reserveIn, address reserveOut, uint256 amountOut) internal view returns (uint256[] memory) { + address[] memory path = new address[](2); + path[0] = reserveIn; + path[1] = reserveOut; + + return UNISWAP_ROUTER.getAmountsIn(amountOut, path); + } } diff --git a/contracts/adapters/UniswapRepayAdapter.sol b/contracts/adapters/UniswapRepayAdapter.sol index c42df484..9c741745 100644 --- a/contracts/adapters/UniswapRepayAdapter.sol +++ b/contracts/adapters/UniswapRepayAdapter.sol @@ -76,6 +76,40 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { return true; } + function swapAndRepay( + address collateralAsset, + address debtAsset, + uint256 collateralAmount, + uint256 debtRepayAmount, + uint256 debtRateMode, + PermitSignature calldata permitSignature + ) external { + ReserveLogic.ReserveData memory reserveData = _getReserveData(collateralAsset); + + if (collateralAsset != debtAsset) { + // Get exact collateral needed for the swap to avoid leftovers + uint256[] memory amounts = _getAmountsIn(collateralAsset, debtAsset, debtRepayAmount); + require(amounts[0] <= collateralAmount, 'slippage too high'); + + // Pull aTokens from user + _pullAToken(collateralAsset, reserveData.aTokenAddress, msg.sender, amounts[0], permitSignature); + + // Swap collateral for debt asset + _swapTokensForExactTokens(collateralAsset, debtAsset, amounts[0], debtRepayAmount); + } else { + // Pull aTokens from user + _pullAToken(collateralAsset, reserveData.aTokenAddress, msg.sender, debtRepayAmount, permitSignature); + } + + // Repay debt + IERC20(debtAsset).approve(address(POOL), debtRepayAmount); + POOL.repay(debtAsset, debtRepayAmount, debtRateMode, msg.sender); + + // In the case the repay amount provided exceeded the actual debt, send the leftovers to the user + _sendLeftovers(debtAsset, msg.sender); + } + + /** * @dev Perform the repay of the debt, pulls the initiator collateral and swaps to repay the flash loan * From 4d2d9e8459ca21b435d54a11ce88ca168ec1b92a Mon Sep 17 00:00:00 2001 From: Gerardo Nardelli Date: Tue, 24 Nov 2020 14:06:58 -0300 Subject: [PATCH 29/62] Avoid leftovers on swapAndRepay --- contracts/adapters/UniswapRepayAdapter.sol | 32 ++++++++++++++-------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/contracts/adapters/UniswapRepayAdapter.sol b/contracts/adapters/UniswapRepayAdapter.sol index 9c741745..e8d319bb 100644 --- a/contracts/adapters/UniswapRepayAdapter.sol +++ b/contracts/adapters/UniswapRepayAdapter.sol @@ -84,29 +84,39 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { uint256 debtRateMode, PermitSignature calldata permitSignature ) external { - ReserveLogic.ReserveData memory reserveData = _getReserveData(collateralAsset); + ReserveLogic.ReserveData memory collateralReserveData = _getReserveData(collateralAsset); + ReserveLogic.ReserveData memory debtReserveData = _getReserveData(debtAsset); + + address debtToken = ReserveLogic.InterestRateMode(debtRateMode) == ReserveLogic.InterestRateMode.STABLE + ? debtReserveData.stableDebtTokenAddress + : debtReserveData.variableDebtTokenAddress; + + uint256 currentDebt = IERC20(debtToken).balanceOf(msg.sender); + uint256 amountToRepay = debtRepayAmount <= currentDebt ? debtRepayAmount : currentDebt; if (collateralAsset != debtAsset) { + uint256 maxCollateralToSwap = collateralAmount; + if (amountToRepay < debtRepayAmount) { + maxCollateralToSwap = maxCollateralToSwap.mul(amountToRepay).div(debtRepayAmount); + } + // Get exact collateral needed for the swap to avoid leftovers - uint256[] memory amounts = _getAmountsIn(collateralAsset, debtAsset, debtRepayAmount); - require(amounts[0] <= collateralAmount, 'slippage too high'); + uint256[] memory amounts = _getAmountsIn(collateralAsset, debtAsset, amountToRepay); + require(amounts[0] <= maxCollateralToSwap, 'slippage too high'); // Pull aTokens from user - _pullAToken(collateralAsset, reserveData.aTokenAddress, msg.sender, amounts[0], permitSignature); + _pullAToken(collateralAsset, collateralReserveData.aTokenAddress, msg.sender, amounts[0], permitSignature); // Swap collateral for debt asset - _swapTokensForExactTokens(collateralAsset, debtAsset, amounts[0], debtRepayAmount); + _swapTokensForExactTokens(collateralAsset, debtAsset, amounts[0], amountToRepay); } else { // Pull aTokens from user - _pullAToken(collateralAsset, reserveData.aTokenAddress, msg.sender, debtRepayAmount, permitSignature); + _pullAToken(collateralAsset, collateralReserveData.aTokenAddress, msg.sender, amountToRepay, permitSignature); } // Repay debt - IERC20(debtAsset).approve(address(POOL), debtRepayAmount); - POOL.repay(debtAsset, debtRepayAmount, debtRateMode, msg.sender); - - // In the case the repay amount provided exceeded the actual debt, send the leftovers to the user - _sendLeftovers(debtAsset, msg.sender); + IERC20(debtAsset).approve(address(POOL), amountToRepay); + POOL.repay(debtAsset, amountToRepay, debtRateMode, msg.sender); } From a496be8833cb8413b8cffed391f26f19a898d0e3 Mon Sep 17 00:00:00 2001 From: Gerardo Nardelli Date: Wed, 25 Nov 2020 10:44:50 -0300 Subject: [PATCH 30/62] Refactor to avoid leftovers on _swapAndRepay with flash loan --- contracts/adapters/UniswapRepayAdapter.sol | 68 +++++++++++-------- .../mocks/swap/MockUniswapV2Router02.sol | 9 ++- test/uniswapAdapters.spec.ts | 56 ++++++++++++--- 3 files changed, 94 insertions(+), 39 deletions(-) diff --git a/contracts/adapters/UniswapRepayAdapter.sol b/contracts/adapters/UniswapRepayAdapter.sol index e8d319bb..005228b9 100644 --- a/contracts/adapters/UniswapRepayAdapter.sol +++ b/contracts/adapters/UniswapRepayAdapter.sol @@ -123,8 +123,8 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { /** * @dev Perform the repay of the debt, pulls the initiator collateral and swaps to repay the flash loan * - * @param assetFrom Address of token to be swapped - * @param assetTo Address of debt token to be received from the swap + * @param collateralAsset Address of token to be swapped + * @param debtAsset Address of debt token to be received from the swap * @param amount Amount of the debt to be repaid * @param collateralAmount Amount of the reserve to be swapped * @param rateMode Rate mode of the debt to be repaid @@ -133,8 +133,8 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { * @param permitSignature struct containing the permit signature */ function _swapAndRepay( - address assetFrom, - address assetTo, + address collateralAsset, + address debtAsset, uint256 amount, uint256 collateralAmount, uint256 rateMode, @@ -142,25 +142,48 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { uint256 premium, PermitSignature memory permitSignature ) internal { + ReserveLogic.ReserveData memory collateralReserveData = _getReserveData(collateralAsset); + // Repay debt - IERC20(assetTo).approve(address(POOL), amount); - POOL.repay(assetTo, amount, rateMode, initiator); - uint256 debtRepayLeftovers = IERC20(assetTo).balanceOf(address(this)); + IERC20(debtAsset).approve(address(POOL), amount); + uint256 repaidAmount = IERC20(debtAsset).balanceOf(address(this)); + POOL.repay(debtAsset, amount, rateMode, initiator); + repaidAmount = repaidAmount.sub(IERC20(debtAsset).balanceOf(address(this))); - uint256 flashLoanDebt = amount.add(premium); - uint256 neededForFlashLoanDebt = flashLoanDebt.sub(debtRepayLeftovers); + if (collateralAsset != debtAsset) { + uint256 maxCollateralToSwap = collateralAmount; + if (repaidAmount < amount) { + maxCollateralToSwap = maxCollateralToSwap.mul(repaidAmount).div(amount); + } - // Pull aTokens from user - ReserveLogic.ReserveData memory reserveData = _getReserveData(assetFrom); - _pullAToken(assetFrom, reserveData.aTokenAddress, initiator, collateralAmount, permitSignature); + uint256 neededForFlashLoanDebt = repaidAmount.add(premium); + uint256[] memory amounts = _getAmountsIn(collateralAsset, debtAsset, neededForFlashLoanDebt); + require(amounts[0] <= maxCollateralToSwap, 'slippage too high'); - uint256 amountSwapped = _swapTokensForExactTokens(assetFrom, assetTo, collateralAmount, neededForFlashLoanDebt); + // Pull aTokens from user + _pullAToken( + collateralAsset, + collateralReserveData.aTokenAddress, + initiator, + amounts[0], + permitSignature + ); - // Send collateral leftovers from swap to the user - _sendLeftovers(assetFrom, initiator); + // Swap collateral asset to the debt asset + _swapTokensForExactTokens(collateralAsset, debtAsset, amounts[0], neededForFlashLoanDebt); + } else { + // Pull aTokens from user + _pullAToken( + collateralAsset, + collateralReserveData.aTokenAddress, + initiator, + repaidAmount.add(premium), + permitSignature + ); + } // Repay flashloan - IERC20(assetTo).approve(address(POOL), flashLoanDebt); + IERC20(debtAsset).approve(address(POOL), amount.add(premium)); } /** @@ -201,17 +224,4 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { ) ); } - - /** - * @dev Transfers the balance of the adapter to the user, as there shouldn't be any leftover in the adapter - * @param asset address of the asset - * @param user address - */ - function _sendLeftovers(address asset, address user) internal { - uint256 assetLeftover = IERC20(asset).balanceOf(address(this)); - - if (assetLeftover > 0) { - IERC20(asset).transfer(user, assetLeftover); - } - } } diff --git a/contracts/mocks/swap/MockUniswapV2Router02.sol b/contracts/mocks/swap/MockUniswapV2Router02.sol index 6d771cda..2c94050c 100644 --- a/contracts/mocks/swap/MockUniswapV2Router02.sol +++ b/contracts/mocks/swap/MockUniswapV2Router02.sol @@ -10,6 +10,7 @@ contract MockUniswapV2Router02 is IUniswapV2Router02 { mapping(address => uint256) internal _amountToSwap; mapping(address => mapping(address => mapping(uint256 => uint256))) internal _amountsIn; mapping(address => mapping(address => mapping(uint256 => uint256))) internal _amountsOut; + uint256 internal defaultMockValue; function setAmountToReturn(address reserve, uint256 amount) public { _amountToReturn[reserve] = amount; @@ -61,16 +62,20 @@ contract MockUniswapV2Router02 is IUniswapV2Router02 { _amountsIn[reserveIn][reserveOut][amountOut] = amountIn; } + function setDefaultMockValue(uint value) public { + defaultMockValue = value; + } + function getAmountsOut(uint amountIn, address[] calldata path) external view override returns (uint[] memory) { uint256[] memory amounts = new uint256[](2); amounts[0] = amountIn; - amounts[1] = _amountsOut[path[0]][path[1]][amountIn]; + amounts[1] = _amountsOut[path[0]][path[1]][amountIn] > 0 ? _amountsOut[path[0]][path[1]][amountIn] : defaultMockValue; return amounts; } function getAmountsIn(uint amountOut, address[] calldata path) external view override returns (uint[] memory) { uint256[] memory amounts = new uint256[](2); - amounts[0] = _amountsIn[path[0]][path[1]][amountOut]; + amounts[0] = _amountsIn[path[0]][path[1]][amountOut] > 0 ? _amountsIn[path[0]][path[1]][amountOut] : defaultMockValue; amounts[1] = amountOut; return amounts; } diff --git a/test/uniswapAdapters.spec.ts b/test/uniswapAdapters.spec.ts index b821f238..eb3d6cc5 100644 --- a/test/uniswapAdapters.spec.ts +++ b/test/uniswapAdapters.spec.ts @@ -2091,6 +2091,13 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { .multipliedBy(1.0009) .toFixed(0); + await mockUniswapRouter.setAmountIn( + flashLoanDebt, + weth.address, + dai.address, + liquidityToSwap + ); + const params = buildRepayAdapterParams( weth.address, liquidityToSwap, @@ -2198,6 +2205,13 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { .multipliedBy(1.0009) .toFixed(0); + await mockUniswapRouter.setAmountIn( + flashLoanDebt, + weth.address, + dai.address, + liquidityToSwap + ); + const params = buildRepayAdapterParams( weth.address, liquidityToSwap, @@ -2401,6 +2415,17 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, bigMaxAmountToSwap); + const flashLoanDebt = new BigNumber(expectedDaiAmount.toString()) + .multipliedBy(1.0009) + .toFixed(0); + + await mockUniswapRouter.setAmountIn( + flashLoanDebt, + weth.address, + dai.address, + bigMaxAmountToSwap + ); + const params = buildRepayAdapterParams( weth.address, bigMaxAmountToSwap, @@ -2472,14 +2497,19 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { .multipliedBy(0.995) .toFixed(0); - const leftOverWeth = new BigNumber(liquidityToSwap.toString()).minus(actualWEthSwapped); - await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, actualWEthSwapped); const flashLoanDebt = new BigNumber(expectedDaiAmount.toString()) .multipliedBy(1.0009) .toFixed(0); + await mockUniswapRouter.setAmountIn( + flashLoanDebt, + weth.address, + dai.address, + actualWEthSwapped + ); + const params = buildRepayAdapterParams( weth.address, liquidityToSwap, @@ -2520,8 +2550,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount); expect(userDaiStableDebtAmount).to.be.lt(expectedDaiAmount); expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); - expect(userAEthBalance).to.be.eq(userAEthBalanceBefore.sub(liquidityToSwap)); - expect(userWethBalance).to.be.gte(userWethBalanceBefore.add(leftOverWeth.toString())); + expect(userAEthBalance).to.be.eq(userAEthBalanceBefore.sub(actualWEthSwapped)); + expect(userWethBalance).to.be.eq(userWethBalanceBefore); }); it('should correctly swap tokens and repay the whole stable debt with no leftovers', async () => { @@ -2560,7 +2590,11 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); - const liquidityToSwap = amountWETHtoSwap; + // Add a % to repay on top of the debt + const liquidityToSwap = new BigNumber(amountWETHtoSwap.toString()) + .multipliedBy(1.1) + .toFixed(0); + await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); @@ -2569,7 +2603,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { .multipliedBy(1.1) .toFixed(0); - await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap); + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, amountWETHtoSwap); + await mockUniswapRouter.setDefaultMockValue(amountWETHtoSwap); const params = buildRepayAdapterParams( weth.address, @@ -2647,7 +2682,11 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { userAddress ); - const liquidityToSwap = amountWETHtoSwap; + // Add a % to repay on top of the debt + const liquidityToSwap = new BigNumber(amountWETHtoSwap.toString()) + .multipliedBy(1.1) + .toFixed(0); + await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); @@ -2656,7 +2695,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { .multipliedBy(1.1) .toFixed(0); - await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap); + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, amountWETHtoSwap); + await mockUniswapRouter.setDefaultMockValue(amountWETHtoSwap); const params = buildRepayAdapterParams( weth.address, From cdd922c9084858e7862f37bd4de62bca0e05f3af Mon Sep 17 00:00:00 2001 From: Gerardo Nardelli Date: Wed, 25 Nov 2020 11:07:33 -0300 Subject: [PATCH 31/62] Merge fixes --- contracts/adapters/BaseUniswapAdapter.sol | 8 ++++---- contracts/adapters/UniswapLiquiditySwapAdapter.sol | 2 +- contracts/adapters/UniswapRepayAdapter.sol | 12 ++++++------ contracts/interfaces/IERC20WithPermit.sol | 2 +- contracts/interfaces/IUniswapV2Router02.sol | 2 +- contracts/mocks/swap/MockUniswapV2Router02.sol | 2 +- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/contracts/adapters/BaseUniswapAdapter.sol b/contracts/adapters/BaseUniswapAdapter.sol index 0e4ca193..718b8f24 100644 --- a/contracts/adapters/BaseUniswapAdapter.sol +++ b/contracts/adapters/BaseUniswapAdapter.sol @@ -1,15 +1,15 @@ // SPDX-License-Identifier: agpl-3.0 -pragma solidity ^0.6.8; +pragma solidity 0.6.12; pragma experimental ABIEncoderV2; -import {PercentageMath} from '../libraries/math/PercentageMath.sol'; +import {PercentageMath} from '../protocol/libraries/math/PercentageMath.sol'; import {SafeMath} from '../dependencies/openzeppelin/contracts/SafeMath.sol'; import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol'; import {IERC20Detailed} from '../dependencies/openzeppelin/contracts/IERC20Detailed.sol'; import {SafeERC20} from '../dependencies/openzeppelin/contracts/SafeERC20.sol'; import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol'; import {ILendingPool} from '../interfaces/ILendingPool.sol'; -import {ReserveLogic} from '../libraries/logic/ReserveLogic.sol'; +import {DataTypes} from '../protocol/libraries/types/DataTypes.sol'; import {IUniswapV2Router02} from '../interfaces/IUniswapV2Router02.sol'; import {IPriceOracleGetter} from '../interfaces/IPriceOracleGetter.sol'; import {IERC20WithPermit} from '../interfaces/IERC20WithPermit.sol'; @@ -214,7 +214,7 @@ contract BaseUniswapAdapter { * @dev Get the aToken associated to the asset * @return address of the aToken */ - function _getReserveData(address asset) internal view returns (ReserveLogic.ReserveData memory) { + function _getReserveData(address asset) internal view returns (DataTypes.ReserveData memory) { return POOL.getReserveData(asset); } diff --git a/contracts/adapters/UniswapLiquiditySwapAdapter.sol b/contracts/adapters/UniswapLiquiditySwapAdapter.sol index 059170f9..088fd411 100644 --- a/contracts/adapters/UniswapLiquiditySwapAdapter.sol +++ b/contracts/adapters/UniswapLiquiditySwapAdapter.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: agpl-3.0 -pragma solidity ^0.6.8; +pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import {BaseUniswapAdapter} from './BaseUniswapAdapter.sol'; diff --git a/contracts/adapters/UniswapRepayAdapter.sol b/contracts/adapters/UniswapRepayAdapter.sol index 005228b9..59aff05b 100644 --- a/contracts/adapters/UniswapRepayAdapter.sol +++ b/contracts/adapters/UniswapRepayAdapter.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: agpl-3.0 -pragma solidity ^0.6.8; +pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import {BaseUniswapAdapter} from './BaseUniswapAdapter.sol'; @@ -7,7 +7,7 @@ import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddresses import {IUniswapV2Router02} from '../interfaces/IUniswapV2Router02.sol'; import {IFlashLoanReceiver} from '../flashloan/interfaces/IFlashLoanReceiver.sol'; import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol'; -import {ReserveLogic} from '../libraries/logic/ReserveLogic.sol'; +import {DataTypes} from '../protocol/libraries/types/DataTypes.sol'; /** * @title UniswapRepayAdapter @@ -84,10 +84,10 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { uint256 debtRateMode, PermitSignature calldata permitSignature ) external { - ReserveLogic.ReserveData memory collateralReserveData = _getReserveData(collateralAsset); - ReserveLogic.ReserveData memory debtReserveData = _getReserveData(debtAsset); + DataTypes.ReserveData memory collateralReserveData = _getReserveData(collateralAsset); + DataTypes.ReserveData memory debtReserveData = _getReserveData(debtAsset); - address debtToken = ReserveLogic.InterestRateMode(debtRateMode) == ReserveLogic.InterestRateMode.STABLE + address debtToken = DataTypes.InterestRateMode(debtRateMode) == DataTypes.InterestRateMode.STABLE ? debtReserveData.stableDebtTokenAddress : debtReserveData.variableDebtTokenAddress; @@ -142,7 +142,7 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { uint256 premium, PermitSignature memory permitSignature ) internal { - ReserveLogic.ReserveData memory collateralReserveData = _getReserveData(collateralAsset); + DataTypes.ReserveData memory collateralReserveData = _getReserveData(collateralAsset); // Repay debt IERC20(debtAsset).approve(address(POOL), amount); diff --git a/contracts/interfaces/IERC20WithPermit.sol b/contracts/interfaces/IERC20WithPermit.sol index 448b383b..46466b90 100644 --- a/contracts/interfaces/IERC20WithPermit.sol +++ b/contracts/interfaces/IERC20WithPermit.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: agpl-3.0 -pragma solidity ^0.6.8; +pragma solidity 0.6.12; import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol'; diff --git a/contracts/interfaces/IUniswapV2Router02.sol b/contracts/interfaces/IUniswapV2Router02.sol index 6453e74a..af0f8280 100644 --- a/contracts/interfaces/IUniswapV2Router02.sol +++ b/contracts/interfaces/IUniswapV2Router02.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: agpl-3.0 -pragma solidity ^0.6.8; +pragma solidity 0.6.12; interface IUniswapV2Router02 { function swapExactTokensForTokens( diff --git a/contracts/mocks/swap/MockUniswapV2Router02.sol b/contracts/mocks/swap/MockUniswapV2Router02.sol index 2c94050c..7f38bf92 100644 --- a/contracts/mocks/swap/MockUniswapV2Router02.sol +++ b/contracts/mocks/swap/MockUniswapV2Router02.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: agpl-3.0 -pragma solidity ^0.6.8; +pragma solidity 0.6.12; import {IUniswapV2Router02} from "../../interfaces/IUniswapV2Router02.sol"; import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; From bc969d926e27c6568d1967635529a482b0e5657d Mon Sep 17 00:00:00 2001 From: Gerardo Nardelli Date: Wed, 25 Nov 2020 14:55:46 -0300 Subject: [PATCH 32/62] Add swapAndRepay unit tests --- test/uniswapAdapters.spec.ts | 533 ++++++++++++++++++++++++++++++++--- 1 file changed, 495 insertions(+), 38 deletions(-) diff --git a/test/uniswapAdapters.spec.ts b/test/uniswapAdapters.spec.ts index eb3d6cc5..f65f2402 100644 --- a/test/uniswapAdapters.spec.ts +++ b/test/uniswapAdapters.spec.ts @@ -1996,6 +1996,42 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { }); describe('UniswapRepayAdapter', () => { + beforeEach(async () => { + const { users, weth, dai, usdc, aave, pool, deployer } = testEnv; + const userAddress = users[0].address; + + // Provide liquidity + await dai.mint(parseEther('20000')); + await dai.approve(pool.address, parseEther('20000')); + await pool.deposit(dai.address, parseEther('20000'), deployer.address, 0); + + const usdcLiquidity = await convertToCurrencyDecimals(usdc.address, '2000000'); + await usdc.mint(usdcLiquidity); + await usdc.approve(pool.address, usdcLiquidity); + await pool.deposit(usdc.address, usdcLiquidity, deployer.address, 0); + + await weth.mint(parseEther('100')); + await weth.approve(pool.address, parseEther('100')); + await pool.deposit(weth.address, parseEther('100'), deployer.address, 0); + + await aave.mint(parseEther('1000000')); + await aave.approve(pool.address, parseEther('1000000')); + await pool.deposit(aave.address, parseEther('1000000'), deployer.address, 0); + + // Make a deposit for user + await weth.mint(parseEther('1000')); + await weth.approve(pool.address, parseEther('1000')); + await pool.deposit(weth.address, parseEther('1000'), userAddress, 0); + + await aave.mint(parseEther('1000000')); + await aave.approve(pool.address, parseEther('1000000')); + await pool.deposit(aave.address, parseEther('1000000'), userAddress, 0); + + await usdc.mint(usdcLiquidity); + await usdc.approve(pool.address, usdcLiquidity); + await pool.deposit(usdc.address, usdcLiquidity, userAddress, 0); + }); + describe('constructor', () => { it('should deploy with correct parameters', async () => { const { addressesProvider } = testEnv; @@ -2009,42 +2045,6 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { }); describe('executeOperation', () => { - beforeEach(async () => { - const { users, weth, dai, usdc, aave, pool, deployer } = testEnv; - const userAddress = users[0].address; - - // Provide liquidity - await dai.mint(parseEther('20000')); - await dai.approve(pool.address, parseEther('20000')); - await pool.deposit(dai.address, parseEther('20000'), deployer.address, 0); - - const usdcLiquidity = await convertToCurrencyDecimals(usdc.address, '2000000'); - await usdc.mint(usdcLiquidity); - await usdc.approve(pool.address, usdcLiquidity); - await pool.deposit(usdc.address, usdcLiquidity, deployer.address, 0); - - await weth.mint(parseEther('100')); - await weth.approve(pool.address, parseEther('100')); - await pool.deposit(weth.address, parseEther('100'), deployer.address, 0); - - await aave.mint(parseEther('1000000')); - await aave.approve(pool.address, parseEther('1000000')); - await pool.deposit(aave.address, parseEther('1000000'), deployer.address, 0); - - // Make a deposit for user - await weth.mint(parseEther('1000')); - await weth.approve(pool.address, parseEther('1000')); - await pool.deposit(weth.address, parseEther('1000'), userAddress, 0); - - await aave.mint(parseEther('1000000')); - await aave.approve(pool.address, parseEther('1000000')); - await pool.deposit(aave.address, parseEther('1000000'), userAddress, 0); - - await usdc.mint(usdcLiquidity); - await usdc.approve(pool.address, usdcLiquidity); - await pool.deposit(usdc.address, usdcLiquidity, userAddress, 0); - }); - it('should correctly swap tokens and repay debt', async () => { const { users, @@ -2554,7 +2554,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { expect(userWethBalance).to.be.eq(userWethBalanceBefore); }); - it('should correctly swap tokens and repay the whole stable debt with no leftovers', async () => { + it('should correctly swap tokens and repay the whole stable debt', async () => { const { users, pool, @@ -2644,7 +2644,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); }); - it('should correctly swap tokens and repay the whole variable debt with no leftovers', async () => { + it('should correctly swap tokens and repay the whole variable debt', async () => { const { users, pool, @@ -2736,5 +2736,462 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); }); }); + + describe('swapAndRepay', () => { + it('should correctly swap tokens and repay debt', async () => { + const { + users, + pool, + weth, + aWETH, + oracle, + dai, + uniswapRepayAdapter, + helpersContract, + } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); + + const daiStableDebtTokenAddress = ( + await helpersContract.getReserveTokensAddresses(dai.address) + ).stableDebtTokenAddress; + + const daiStableDebtContract = await getContract( + eContractid.StableDebtToken, + daiStableDebtTokenAddress + ); + + const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); + + const liquidityToSwap = amountWETHtoSwap; + await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + + await mockUniswapRouter.setAmountToSwap(weth.address, liquidityToSwap); + + await mockUniswapRouter.setDefaultMockValue(liquidityToSwap); + + await uniswapRepayAdapter + .connect(user) + .swapAndRepay(weth.address, dai.address, liquidityToSwap, expectedDaiAmount, 1, { + amount: 0, + deadline: 0, + v: 0, + r: '0x0000000000000000000000000000000000000000000000000000000000000000', + s: '0x0000000000000000000000000000000000000000000000000000000000000000', + }); + + const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); + const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); + const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); + const userAEthBalance = await aWETH.balanceOf(userAddress); + + expect(adapterWethBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount); + expect(userDaiStableDebtAmount).to.be.lt(expectedDaiAmount); + expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); + expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); + }); + + it('should correctly swap tokens and repay debt with permit', async () => { + const { + users, + pool, + weth, + aWETH, + oracle, + dai, + uniswapRepayAdapter, + helpersContract, + } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); + + const daiStableDebtTokenAddress = ( + await helpersContract.getReserveTokensAddresses(dai.address) + ).stableDebtTokenAddress; + + const daiStableDebtContract = await getContract( + eContractid.StableDebtToken, + daiStableDebtTokenAddress + ); + + const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); + + const liquidityToSwap = amountWETHtoSwap; + const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + + await mockUniswapRouter.setAmountToSwap(weth.address, liquidityToSwap); + + await mockUniswapRouter.setDefaultMockValue(liquidityToSwap); + + const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID; + const deadline = MAX_UINT_AMOUNT; + const nonce = (await aWETH._nonces(userAddress)).toNumber(); + const msgParams = buildPermitParams( + chainId, + aWETH.address, + '1', + await aWETH.name(), + userAddress, + uniswapRepayAdapter.address, + nonce, + deadline, + liquidityToSwap.toString() + ); + + const ownerPrivateKey = require('../test-wallets.js').accounts[1].secretKey; + if (!ownerPrivateKey) { + throw new Error('INVALID_OWNER_PK'); + } + + const { v, r, s } = getSignatureFromTypedData(ownerPrivateKey, msgParams); + + await uniswapRepayAdapter + .connect(user) + .swapAndRepay(weth.address, dai.address, liquidityToSwap, expectedDaiAmount, 1, { + amount: liquidityToSwap, + deadline, + v, + r, + s, + }); + + const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); + const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); + const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); + const userAEthBalance = await aWETH.balanceOf(userAddress); + + expect(adapterWethBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount); + expect(userDaiStableDebtAmount).to.be.lt(expectedDaiAmount); + expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); + expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); + }); + + it('should revert if there is not debt to repay', async () => { + const { users, weth, aWETH, oracle, dai, uniswapRepayAdapter } = testEnv; + const user = users[0].signer; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + const liquidityToSwap = amountWETHtoSwap; + await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + + await mockUniswapRouter.setAmountToSwap(weth.address, liquidityToSwap); + + await mockUniswapRouter.setDefaultMockValue(liquidityToSwap); + + await expect( + uniswapRepayAdapter + .connect(user) + .swapAndRepay(weth.address, dai.address, liquidityToSwap, expectedDaiAmount, 1, { + amount: 0, + deadline: 0, + v: 0, + r: '0x0000000000000000000000000000000000000000000000000000000000000000', + s: '0x0000000000000000000000000000000000000000000000000000000000000000', + }) + ).to.be.reverted; + }); + + it('should revert when max amount allowed to swap is bigger than max slippage', async () => { + const { users, pool, weth, aWETH, oracle, dai, uniswapRepayAdapter } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); + + const bigMaxAmountToSwap = amountWETHtoSwap.mul(2); + await aWETH.connect(user).approve(uniswapRepayAdapter.address, bigMaxAmountToSwap); + + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, bigMaxAmountToSwap); + + await mockUniswapRouter.setDefaultMockValue(bigMaxAmountToSwap); + + await expect( + uniswapRepayAdapter + .connect(user) + .swapAndRepay(weth.address, dai.address, bigMaxAmountToSwap, expectedDaiAmount, 1, { + amount: 0, + deadline: 0, + v: 0, + r: '0x0000000000000000000000000000000000000000000000000000000000000000', + s: '0x0000000000000000000000000000000000000000000000000000000000000000', + }) + ).to.be.revertedWith('maxAmountToSwap exceed max slippage'); + }); + + it('should swap, repay debt and pull the needed ATokens leaving no leftovers', async () => { + const { + users, + pool, + weth, + aWETH, + oracle, + dai, + uniswapRepayAdapter, + helpersContract, + } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); + + const daiStableDebtTokenAddress = ( + await helpersContract.getReserveTokensAddresses(dai.address) + ).stableDebtTokenAddress; + + const daiStableDebtContract = await getContract( + eContractid.StableDebtToken, + daiStableDebtTokenAddress + ); + + const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); + + const liquidityToSwap = amountWETHtoSwap; + await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + const userWethBalanceBefore = await weth.balanceOf(userAddress); + + const actualWEthSwapped = new BigNumber(liquidityToSwap.toString()) + .multipliedBy(0.995) + .toFixed(0); + + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, actualWEthSwapped); + + await mockUniswapRouter.setDefaultMockValue(actualWEthSwapped); + + await uniswapRepayAdapter + .connect(user) + .swapAndRepay(weth.address, dai.address, liquidityToSwap, expectedDaiAmount, 1, { + amount: 0, + deadline: 0, + v: 0, + r: '0x0000000000000000000000000000000000000000000000000000000000000000', + s: '0x0000000000000000000000000000000000000000000000000000000000000000', + }); + + const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); + const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); + const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); + const userAEthBalance = await aWETH.balanceOf(userAddress); + const adapterAEthBalance = await aWETH.balanceOf(uniswapRepayAdapter.address); + const userWethBalance = await weth.balanceOf(userAddress); + + expect(adapterAEthBalance).to.be.eq(Zero); + expect(adapterWethBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount); + expect(userDaiStableDebtAmount).to.be.lt(expectedDaiAmount); + expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); + expect(userAEthBalance).to.be.eq(userAEthBalanceBefore.sub(actualWEthSwapped)); + expect(userWethBalance).to.be.eq(userWethBalanceBefore); + }); + + it('should correctly swap tokens and repay the whole stable debt', async () => { + const { + users, + pool, + weth, + aWETH, + oracle, + dai, + uniswapRepayAdapter, + helpersContract, + } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); + + const daiStableDebtTokenAddress = ( + await helpersContract.getReserveTokensAddresses(dai.address) + ).stableDebtTokenAddress; + + const daiStableDebtContract = await getContract( + eContractid.StableDebtToken, + daiStableDebtTokenAddress + ); + + const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); + + // Add a % to repay on top of the debt + const liquidityToSwap = new BigNumber(amountWETHtoSwap.toString()) + .multipliedBy(1.1) + .toFixed(0); + + await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + + // Add a % to repay on top of the debt + const amountToRepay = new BigNumber(expectedDaiAmount.toString()) + .multipliedBy(1.1) + .toFixed(0); + + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, amountWETHtoSwap); + await mockUniswapRouter.setDefaultMockValue(amountWETHtoSwap); + + await uniswapRepayAdapter + .connect(user) + .swapAndRepay(weth.address, dai.address, liquidityToSwap, amountToRepay, 1, { + amount: 0, + deadline: 0, + v: 0, + r: '0x0000000000000000000000000000000000000000000000000000000000000000', + s: '0x0000000000000000000000000000000000000000000000000000000000000000', + }); + + const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); + const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); + const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); + const userAEthBalance = await aWETH.balanceOf(userAddress); + const adapterAEthBalance = await aWETH.balanceOf(uniswapRepayAdapter.address); + + expect(adapterAEthBalance).to.be.eq(Zero); + expect(adapterWethBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount); + expect(userDaiStableDebtAmount).to.be.eq(Zero); + expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); + expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); + }); + + it('should correctly swap tokens and repay the whole variable debt', async () => { + const { + users, + pool, + weth, + aWETH, + oracle, + dai, + uniswapRepayAdapter, + helpersContract, + } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 2, 0, userAddress); + + const daiStableVariableTokenAddress = ( + await helpersContract.getReserveTokensAddresses(dai.address) + ).variableDebtTokenAddress; + + const daiVariableDebtContract = await getContract( + eContractid.VariableDebtToken, + daiStableVariableTokenAddress + ); + + const userDaiVariableDebtAmountBefore = await daiVariableDebtContract.balanceOf( + userAddress + ); + + // Add a % to repay on top of the debt + const liquidityToSwap = new BigNumber(amountWETHtoSwap.toString()) + .multipliedBy(1.1) + .toFixed(0); + + await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + + // Add a % to repay on top of the debt + const amountToRepay = new BigNumber(expectedDaiAmount.toString()) + .multipliedBy(1.1) + .toFixed(0); + + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, amountWETHtoSwap); + await mockUniswapRouter.setDefaultMockValue(amountWETHtoSwap); + + await uniswapRepayAdapter + .connect(user) + .swapAndRepay(weth.address, dai.address, liquidityToSwap, amountToRepay, 2, { + amount: 0, + deadline: 0, + v: 0, + r: '0x0000000000000000000000000000000000000000000000000000000000000000', + s: '0x0000000000000000000000000000000000000000000000000000000000000000', + }); + + const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); + const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); + const userDaiVariableDebtAmount = await daiVariableDebtContract.balanceOf(userAddress); + const userAEthBalance = await aWETH.balanceOf(userAddress); + const adapterAEthBalance = await aWETH.balanceOf(uniswapRepayAdapter.address); + + expect(adapterAEthBalance).to.be.eq(Zero); + expect(adapterWethBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(userDaiVariableDebtAmountBefore).to.be.gte(expectedDaiAmount); + expect(userDaiVariableDebtAmount).to.be.eq(Zero); + expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); + expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); + }); + }); }); }); From 3415204216dca7a5a3b0965f9f7d47a25a3db942 Mon Sep 17 00:00:00 2001 From: Gerardo Nardelli Date: Wed, 25 Nov 2020 15:49:11 -0300 Subject: [PATCH 33/62] Update adapter docs --- .../adapters/UniswapLiquiditySwapAdapter.sol | 34 +++++++++++++------ contracts/adapters/UniswapRepayAdapter.sol | 26 ++++++++++---- 2 files changed, 42 insertions(+), 18 deletions(-) diff --git a/contracts/adapters/UniswapLiquiditySwapAdapter.sol b/contracts/adapters/UniswapLiquiditySwapAdapter.sol index 088fd411..8b325499 100644 --- a/contracts/adapters/UniswapLiquiditySwapAdapter.sol +++ b/contracts/adapters/UniswapLiquiditySwapAdapter.sol @@ -10,7 +10,7 @@ import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol'; /** * @title UniswapLiquiditySwapAdapter - * @notice Uniswap V2 Adapter to swap liquidity using a flash loan. + * @notice Uniswap V2 Adapter to swap liquidity. * @author Aave **/ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { @@ -39,12 +39,12 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { {} /** - * @dev Swaps the received reserve amount from the flashloan into the asset specified in the params. + * @dev Swaps the received reserve amount from the flash loan into the asset specified in the params. * The received funds from the swap are then deposited into the protocol on behalf of the user. * The user should give this contract allowance to pull the ATokens in order to withdraw the underlying asset and * repay the flash loan. - * @param assets Address to be swapped - * @param amounts Amount of the reserve to be swapped + * @param assets Address of asset to be swapped + * @param amounts Amount of the asset to be swapped * @param premiums Fee of the flash loan * @param initiator Address of the user * @param params Additional variadic field to include extra params. Expected parameters: @@ -103,8 +103,9 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { } /** - * @dev Swaps an `amountToSwap` of an asset to another and deposits the funds on behalf of the user without using a flashloan. - * This method can be used when the user has no debts. + * @dev Swaps an amount of an asset to another and deposits the new asset amount on behalf of the user without using + * a flash loan. This method can be used when the temporary transfer of the collateral asset to this contract + * does not affect the user position. * The user should give this contract allowance to pull the ATokens in order to withdraw the underlying asset and * perform the swap. * @param assetToSwapFromList List of addresses of the underlying asset to be swap from @@ -164,8 +165,8 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { * @dev Swaps an `amountToSwap` of an asset to another and deposits the funds on behalf of the initiator. * @param assetFrom Address of the underlying asset to be swap from * @param assetTo Address of the underlying asset to be swap to and deposited - * @param amount Amount from flashloan - * @param premium Premium of the flashloan + * @param amount Amount from flash loan + * @param premium Premium of the flash loan * @param minAmountToReceive Min amount to be received from the swap * @param swapAllBalance Flag indicating if all the user balance should be swapped * @param permitSignature List of struct containing the permit signature @@ -203,12 +204,12 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { _pullAToken(assetFrom, aToken, initiator, amountToPull, permitSignature); - // Repay flashloan + // Repay flash loan IERC20(assetFrom).approve(address(POOL), flashLoanDebt); } /** - * @dev Decodes debt information encoded in flashloan params + * @dev Decodes the information encoded in the flash loan params * @param params Additional variadic field to include extra params. Expected parameters: * address[] assetToSwapToList List of the addresses of the reserve to be swapped to and deposited * uint256[] minAmountsToReceive List of min amounts to be received from the swap @@ -232,6 +233,17 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { bytes32[] memory s ) = abi.decode(params, (address[], uint256[], bool[], uint256[], uint256[], uint8[], bytes32[], bytes32[])); - return SwapParams(assetToSwapToList, minAmountsToReceive, swapAllBalance, PermitParams(permitAmount, deadline, v, r, s)); + return SwapParams( + assetToSwapToList, + minAmountsToReceive, + swapAllBalance, + PermitParams( + permitAmount, + deadline, + v, + r, + s + ) + ); } } diff --git a/contracts/adapters/UniswapRepayAdapter.sol b/contracts/adapters/UniswapRepayAdapter.sol index 59aff05b..4945edf0 100644 --- a/contracts/adapters/UniswapRepayAdapter.sol +++ b/contracts/adapters/UniswapRepayAdapter.sol @@ -11,7 +11,7 @@ import {DataTypes} from '../protocol/libraries/types/DataTypes.sol'; /** * @title UniswapRepayAdapter - * @notice Uniswap V2 Adapter to perform a repay of a debt using a flash loan. + * @notice Uniswap V2 Adapter to perform a repay of a debt with collateral. * @author Aave **/ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { @@ -33,18 +33,18 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { /** * @dev Uses the received funds from the flash loan to repay a debt on the protocol on behalf of the user. Then pulls - * the collateral from the user and swaps it to repay the flash loan. + * the collateral from the user and swaps it to the debt asset to repay the flash loan. * The user should give this contract allowance to pull the ATokens in order to withdraw the underlying asset, swap it * and repay the flash loan. - * @param assets Address to be swapped - * @param amounts Amount of the reserve to be swapped + * Supports only one asset on the flash loan. + * @param assets Address of debt asset + * @param amounts Amount of the debt to be repaid * @param premiums Fee of the flash loan * @param initiator Address of the user * @param params Additional variadic field to include extra params. Expected parameters: * address collateralAsset Address of the reserve to be swapped * uint256 collateralAmount Amount of reserve to be swapped * uint256 rateMode Rate modes of the debt to be repaid - * RepayMode repayMode Enum indicating the repaid mode * uint256 permitAmount Amount for the permit signature * uint256 deadline Deadline for the permit signature * uint8 v V param for the permit signature @@ -76,6 +76,18 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { return true; } + /** + * @dev Swaps the user collateral for the debt asset and then repay the debt on the protocol on behalf of the user + * without using flash loans. This method can be used when the temporary transfer of the collateral asset to this + * contract does not affect the user position. + * The user should give this contract allowance to pull the ATokens in order to withdraw the underlying asset + * @param collateralAsset Address of asset to be swapped + * @param debtAsset Address of debt asset + * @param collateralAmount Amount of the collateral to be swapped + * @param debtRepayAmount Amount of the debt to be repaid + * @param debtRateMode Rate mode of the debt to be repaid + * @param permitSignature struct containing the permit signature + */ function swapAndRepay( address collateralAsset, address debtAsset, @@ -182,12 +194,12 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { ); } - // Repay flashloan + // Repay flash loan IERC20(debtAsset).approve(address(POOL), amount.add(premium)); } /** - * @dev Decodes debt information encoded in flashloan params + * @dev Decodes debt information encoded in the flash loan params * @param params Additional variadic field to include extra params. Expected parameters: * address collateralAsset Address of the reserve to be swapped * uint256 collateralAmount Amount of reserve to be swapped From b41ccf744182f3219be31e8a3981dd7187e19dde Mon Sep 17 00:00:00 2001 From: Gerardo Nardelli Date: Thu, 26 Nov 2020 12:42:42 -0300 Subject: [PATCH 34/62] Add repay with same asset unit test --- test/uniswapAdapters.spec.ts | 137 +++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) diff --git a/test/uniswapAdapters.spec.ts b/test/uniswapAdapters.spec.ts index f65f2402..e5dcb7f5 100644 --- a/test/uniswapAdapters.spec.ts +++ b/test/uniswapAdapters.spec.ts @@ -2735,6 +2735,79 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); }); + + it('should correctly repay debt using the same asset as collateral', async () => { + const { users, pool, aDai, dai, uniswapRepayAdapter, helpersContract } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + // Add deposit for user + await dai.mint(parseEther('20')); + await dai.approve(pool.address, parseEther('20')); + await pool.deposit(dai.address, parseEther('20'), userAddress, 0); + + const amountCollateralToSwap = parseEther('10'); + const debtAmount = parseEther('10'); + + // Open user Debt + await pool.connect(user).borrow(dai.address, debtAmount, 1, 0, userAddress); + + const daiStableDebtTokenAddress = ( + await helpersContract.getReserveTokensAddresses(dai.address) + ).stableDebtTokenAddress; + + const daiStableDebtContract = await getContract( + eContractid.StableDebtToken, + daiStableDebtTokenAddress + ); + + const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); + + const flashLoanDebt = new BigNumber(amountCollateralToSwap.toString()) + .multipliedBy(1.0009) + .toFixed(0); + + await aDai.connect(user).approve(uniswapRepayAdapter.address, flashLoanDebt); + const userADaiBalanceBefore = await aDai.balanceOf(userAddress); + const userDaiBalanceBefore = await dai.balanceOf(userAddress); + + const params = buildRepayAdapterParams( + dai.address, + amountCollateralToSwap, + 1, + 0, + 0, + 0, + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000' + ); + + await pool + .connect(user) + .flashLoan( + uniswapRepayAdapter.address, + [dai.address], + [amountCollateralToSwap.toString()], + [0], + userAddress, + params, + 0 + ); + + const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); + const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); + const userADaiBalance = await aDai.balanceOf(userAddress); + const adapterADaiBalance = await aDai.balanceOf(uniswapRepayAdapter.address); + const userDaiBalance = await dai.balanceOf(userAddress); + + expect(adapterADaiBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(userDaiStableDebtAmountBefore).to.be.gte(debtAmount); + expect(userDaiStableDebtAmount).to.be.lt(debtAmount); + expect(userADaiBalance).to.be.lt(userADaiBalanceBefore); + expect(userADaiBalance).to.be.gte(userADaiBalanceBefore.sub(flashLoanDebt)); + expect(userDaiBalance).to.be.eq(userDaiBalanceBefore); + }); }); describe('swapAndRepay', () => { @@ -3192,6 +3265,70 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); }); + + it('should correctly repay debt using the same asset as collateral', async () => { + const { users, pool, dai, uniswapRepayAdapter, helpersContract, aDai } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + // Add deposit for user + await dai.mint(parseEther('20')); + await dai.approve(pool.address, parseEther('20')); + await pool.deposit(dai.address, parseEther('20'), userAddress, 0); + + const amountCollateralToSwap = parseEther('10'); + + const debtAmount = parseEther('10'); + + // Open user Debt + await pool.connect(user).borrow(dai.address, debtAmount, 1, 0, userAddress); + + const daiStableDebtTokenAddress = ( + await helpersContract.getReserveTokensAddresses(dai.address) + ).stableDebtTokenAddress; + + const daiStableDebtContract = await getContract( + eContractid.StableDebtToken, + daiStableDebtTokenAddress + ); + + const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); + + await aDai.connect(user).approve(uniswapRepayAdapter.address, amountCollateralToSwap); + const userADaiBalanceBefore = await aDai.balanceOf(userAddress); + const userDaiBalanceBefore = await dai.balanceOf(userAddress); + + await uniswapRepayAdapter + .connect(user) + .swapAndRepay( + dai.address, + dai.address, + amountCollateralToSwap, + amountCollateralToSwap, + 1, + { + amount: 0, + deadline: 0, + v: 0, + r: '0x0000000000000000000000000000000000000000000000000000000000000000', + s: '0x0000000000000000000000000000000000000000000000000000000000000000', + } + ); + + const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); + const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); + const userADaiBalance = await aDai.balanceOf(userAddress); + const adapterADaiBalance = await aDai.balanceOf(uniswapRepayAdapter.address); + const userDaiBalance = await dai.balanceOf(userAddress); + + expect(adapterADaiBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(userDaiStableDebtAmountBefore).to.be.gte(debtAmount); + expect(userDaiStableDebtAmount).to.be.lt(debtAmount); + expect(userADaiBalance).to.be.lt(userADaiBalanceBefore); + expect(userADaiBalance).to.be.gte(userADaiBalanceBefore.sub(amountCollateralToSwap)); + expect(userDaiBalance).to.be.eq(userDaiBalanceBefore); + }); }); }); }); From a21757d0fc9017f797071c94ff57a95ed2f7dba2 Mon Sep 17 00:00:00 2001 From: andyk Date: Mon, 30 Nov 2020 17:14:29 +0400 Subject: [PATCH 35/62] adoption to the latest --- contracts/adapters/BaseUniswapAdapter.sol | 222 ++++++++++-------- .../adapters/UniswapLiquiditySwapAdapter.sol | 109 ++++----- contracts/adapters/UniswapRepayAdapter.sol | 84 +++---- .../interfaces/IBaseUniswapAdapter.sol | 83 +++++++ hardhat.config.ts | 17 +- package-lock.json | 62 ++--- package.json | 1 + .../deployments/deploy-UiPoolDataProvider.ts | 14 +- .../deployments/deploy-UniswapRepayAdapter.ts | 30 +++ 9 files changed, 370 insertions(+), 252 deletions(-) create mode 100644 contracts/adapters/interfaces/IBaseUniswapAdapter.sol create mode 100644 tasks/deployments/deploy-UniswapRepayAdapter.ts diff --git a/contracts/adapters/BaseUniswapAdapter.sol b/contracts/adapters/BaseUniswapAdapter.sol index 718b8f24..c8d3702a 100644 --- a/contracts/adapters/BaseUniswapAdapter.sol +++ b/contracts/adapters/BaseUniswapAdapter.sol @@ -8,70 +8,65 @@ import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol'; import {IERC20Detailed} from '../dependencies/openzeppelin/contracts/IERC20Detailed.sol'; import {SafeERC20} from '../dependencies/openzeppelin/contracts/SafeERC20.sol'; import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol'; -import {ILendingPool} from '../interfaces/ILendingPool.sol'; import {DataTypes} from '../protocol/libraries/types/DataTypes.sol'; import {IUniswapV2Router02} from '../interfaces/IUniswapV2Router02.sol'; import {IPriceOracleGetter} from '../interfaces/IPriceOracleGetter.sol'; import {IERC20WithPermit} from '../interfaces/IERC20WithPermit.sol'; +import {FlashLoanReceiverBase} from '../flashloan/base/FlashLoanReceiverBase.sol'; +import {IBaseUniswapAdapter} from './interfaces/IBaseUniswapAdapter.sol'; /** * @title BaseUniswapAdapter * @notice Implements the logic for performing assets swaps in Uniswap V2 * @author Aave **/ -contract BaseUniswapAdapter { +abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapter { using SafeMath for uint256; using PercentageMath for uint256; using SafeERC20 for IERC20; - struct PermitSignature { - uint256 amount; - uint256 deadline; - uint8 v; - bytes32 r; - bytes32 s; - } - - struct AmountCalc { - uint256 calculatedAmount; - uint256 relativePrice; - uint256 amountInUsd; - uint256 amountOutUsd; - } - // Max slippage percent allowed - uint256 public constant MAX_SLIPPAGE_PERCENT = 3000; // 30% + uint256 public constant override MAX_SLIPPAGE_PERCENT = 3000; // 30% // FLash Loan fee set in lending pool - uint256 public constant FLASHLOAN_PREMIUM_TOTAL = 9; + uint256 public constant override FLASHLOAN_PREMIUM_TOTAL = 9; // USD oracle asset address - address public constant USD_ADDRESS = 0x10F7Fc1F91Ba351f9C629c5947AD69bD03C05b96; + address public constant override USD_ADDRESS = 0x10F7Fc1F91Ba351f9C629c5947AD69bD03C05b96; - ILendingPool public immutable POOL; - IPriceOracleGetter public immutable ORACLE; - IUniswapV2Router02 public immutable UNISWAP_ROUTER; + IPriceOracleGetter public immutable override ORACLE; + IUniswapV2Router02 public immutable override UNISWAP_ROUTER; - event Swapped(address fromAsset, address toAsset, uint256 fromAmount, uint256 receivedAmount); - - constructor(ILendingPoolAddressesProvider addressesProvider, IUniswapV2Router02 uniswapRouter) public { - POOL = ILendingPool(addressesProvider.getLendingPool()); + constructor(ILendingPoolAddressesProvider addressesProvider, IUniswapV2Router02 uniswapRouter) + public + FlashLoanReceiverBase(addressesProvider) + { ORACLE = IPriceOracleGetter(addressesProvider.getPriceOracle()); UNISWAP_ROUTER = uniswapRouter; } /** - * @dev Given an input asset amount, returns the maximum output amount of the other asset and the prices - * @param amountIn Amount of reserveIn - * @param reserveIn Address of the asset to be swap from - * @param reserveOut Address of the asset to be swap to - * @return uint256 Amount out of the reserveOut - * @return uint256 The price of out amount denominated in the reserveIn currency (18 decimals) - * @return uint256 In amount of reserveIn value denominated in USD (8 decimals) - * @return uint256 Out amount of reserveOut value denominated in USD (8 decimals) - */ - function getAmountsOut(uint256 amountIn, address reserveIn, address reserveOut) + * @dev Given an input asset amount, returns the maximum output amount of the other asset and the prices + * @param amountIn Amount of reserveIn + * @param reserveIn Address of the asset to be swap from + * @param reserveOut Address of the asset to be swap to + * @return uint256 Amount out of the reserveOut + * @return uint256 The price of out amount denominated in the reserveIn currency (18 decimals) + * @return uint256 In amount of reserveIn value denominated in USD (8 decimals) + * @return uint256 Out amount of reserveOut value denominated in USD (8 decimals) + */ + function getAmountsOut( + uint256 amountIn, + address reserveIn, + address reserveOut + ) external view - returns (uint256, uint256, uint256, uint256) + override + returns ( + uint256, + uint256, + uint256, + uint256 + ) { AmountCalc memory results = _getAmountsOutData(reserveIn, reserveOut, amountIn); @@ -93,10 +88,20 @@ contract BaseUniswapAdapter { * @return uint256 In amount of reserveIn value denominated in USD (8 decimals) * @return uint256 Out amount of reserveOut value denominated in USD (8 decimals) */ - function getAmountsIn(uint256 amountOut, address reserveIn, address reserveOut) + function getAmountsIn( + uint256 amountOut, + address reserveIn, + address reserveOut + ) external view - returns (uint256, uint256, uint256, uint256) + override + returns ( + uint256, + uint256, + uint256, + uint256 + ) { AmountCalc memory results = _getAmountsInData(reserveIn, reserveOut, amountOut); @@ -121,20 +126,18 @@ contract BaseUniswapAdapter { address assetToSwapTo, uint256 amountToSwap, uint256 minAmountOut - ) - internal - returns (uint256) - { + ) internal returns (uint256) { uint256 fromAssetDecimals = _getDecimals(assetToSwapFrom); uint256 toAssetDecimals = _getDecimals(assetToSwapTo); uint256 fromAssetPrice = _getPrice(assetToSwapFrom); uint256 toAssetPrice = _getPrice(assetToSwapTo); - uint256 expectedMinAmountOut = amountToSwap - .mul(fromAssetPrice.mul(10**toAssetDecimals)) - .div(toAssetPrice.mul(10**fromAssetDecimals)) - .percentMul(PercentageMath.PERCENTAGE_FACTOR.sub(MAX_SLIPPAGE_PERCENT)); + uint256 expectedMinAmountOut = + amountToSwap + .mul(fromAssetPrice.mul(10**toAssetDecimals)) + .div(toAssetPrice.mul(10**fromAssetDecimals)) + .percentMul(PercentageMath.PERCENTAGE_FACTOR.sub(MAX_SLIPPAGE_PERCENT)); require(expectedMinAmountOut < minAmountOut, 'minAmountOut exceed max slippage'); @@ -143,7 +146,14 @@ contract BaseUniswapAdapter { address[] memory path = new address[](2); path[0] = assetToSwapFrom; path[1] = assetToSwapTo; - uint256[] memory amounts = UNISWAP_ROUTER.swapExactTokensForTokens(amountToSwap, minAmountOut, path, address(this), block.timestamp); + uint256[] memory amounts = + UNISWAP_ROUTER.swapExactTokensForTokens( + amountToSwap, + minAmountOut, + path, + address(this), + block.timestamp + ); emit Swapped(assetToSwapFrom, assetToSwapTo, amounts[0], amounts[1]); @@ -164,20 +174,18 @@ contract BaseUniswapAdapter { address assetToSwapTo, uint256 maxAmountToSwap, uint256 amountToReceive - ) - internal - returns (uint256) - { + ) internal returns (uint256) { uint256 fromAssetDecimals = _getDecimals(assetToSwapFrom); uint256 toAssetDecimals = _getDecimals(assetToSwapTo); uint256 fromAssetPrice = _getPrice(assetToSwapFrom); uint256 toAssetPrice = _getPrice(assetToSwapTo); - uint256 expectedMaxAmountToSwap = amountToReceive - .mul(toAssetPrice.mul(10**fromAssetDecimals)) - .div(fromAssetPrice.mul(10**toAssetDecimals)) - .percentMul(PercentageMath.PERCENTAGE_FACTOR.add(MAX_SLIPPAGE_PERCENT)); + uint256 expectedMaxAmountToSwap = + amountToReceive + .mul(toAssetPrice.mul(10**fromAssetDecimals)) + .div(fromAssetPrice.mul(10**toAssetDecimals)) + .percentMul(PercentageMath.PERCENTAGE_FACTOR.add(MAX_SLIPPAGE_PERCENT)); require(maxAmountToSwap < expectedMaxAmountToSwap, 'maxAmountToSwap exceed max slippage'); @@ -186,7 +194,14 @@ contract BaseUniswapAdapter { address[] memory path = new address[](2); path[0] = assetToSwapFrom; path[1] = assetToSwapTo; - uint256[] memory amounts = UNISWAP_ROUTER.swapTokensForExactTokens(amountToReceive, maxAmountToSwap, path, address(this), block.timestamp); + uint256[] memory amounts = + UNISWAP_ROUTER.swapTokensForExactTokens( + amountToReceive, + maxAmountToSwap, + path, + address(this), + block.timestamp + ); emit Swapped(assetToSwapFrom, assetToSwapTo, amounts[0], amounts[1]); @@ -215,7 +230,7 @@ contract BaseUniswapAdapter { * @return address of the aToken */ function _getReserveData(address asset) internal view returns (DataTypes.ReserveData memory) { - return POOL.getReserveData(asset); + return LENDING_POOL.getReserveData(asset); } /** @@ -249,7 +264,7 @@ contract BaseUniswapAdapter { IERC20(reserveAToken).safeTransferFrom(user, address(this), amount); // withdraw reserve - POOL.withdraw(reserve, amount, address(this)); + LENDING_POOL.withdraw(reserve, amount, address(this)); } /** @@ -259,7 +274,8 @@ contract BaseUniswapAdapter { * @return whether or not permit should be called */ function _usePermit(PermitSignature memory signature) internal pure returns (bool) { - return !(uint256(signature.deadline) == uint256(signature.v) && uint256(signature.deadline) == 0); + return + !(uint256(signature.deadline) == uint256(signature.v) && uint256(signature.deadline) == 0); } /** @@ -269,15 +285,15 @@ contract BaseUniswapAdapter { * @param decimals Decimals of the reserve * @return whether or not permit should be called */ - function _calcUsdValue(address reserve, uint256 amount, uint256 decimals) internal view returns (uint256) { + function _calcUsdValue( + address reserve, + uint256 amount, + uint256 decimals + ) internal view returns (uint256) { uint256 ethUsdPrice = _getPrice(USD_ADDRESS); uint256 reservePrice = _getPrice(reserve); - return amount - .mul(reservePrice) - .div(10**decimals) - .mul(ethUsdPrice) - .div(10**18); + return amount.mul(reservePrice).div(10**decimals).mul(ethUsdPrice).div(10**18); } /** @@ -291,7 +307,11 @@ contract BaseUniswapAdapter { * uint256 In amount of reserveIn value denominated in USD (8 decimals) * uint256 Out amount of reserveOut value denominated in USD (8 decimals) */ - function _getAmountsOutData(address reserveIn, address reserveOut, uint256 amountIn) internal view returns (AmountCalc memory) { + function _getAmountsOutData( + address reserveIn, + address reserveOut, + uint256 amountIn + ) internal view returns (AmountCalc memory) { // Subtract flash loan fee uint256 finalAmountIn = amountIn.sub(amountIn.mul(FLASHLOAN_PREMIUM_TOTAL).div(10000)); @@ -304,17 +324,18 @@ contract BaseUniswapAdapter { uint256 reserveInDecimals = _getDecimals(reserveIn); uint256 reserveOutDecimals = _getDecimals(reserveOut); - uint256 outPerInPrice = finalAmountIn - .mul(10**18) - .mul(10**reserveOutDecimals) - .div(amounts[1].mul(10**reserveInDecimals)); + uint256 outPerInPrice = + finalAmountIn.mul(10**18).mul(10**reserveOutDecimals).div( + amounts[1].mul(10**reserveInDecimals) + ); - return AmountCalc( - amounts[1], - outPerInPrice, - _calcUsdValue(reserveIn, amountIn, reserveInDecimals), - _calcUsdValue(reserveOut, amounts[1], reserveOutDecimals) - ); + return + AmountCalc( + amounts[1], + outPerInPrice, + _calcUsdValue(reserveIn, amountIn, reserveInDecimals), + _calcUsdValue(reserveOut, amounts[1], reserveOutDecimals) + ); } /** @@ -328,7 +349,11 @@ contract BaseUniswapAdapter { * uint256 In amount of reserveIn value denominated in USD (8 decimals) * uint256 Out amount of reserveOut value denominated in USD (8 decimals) */ - function _getAmountsInData(address reserveIn, address reserveOut, uint256 amountOut) internal view returns (AmountCalc memory) { + function _getAmountsInData( + address reserveIn, + address reserveOut, + uint256 amountOut + ) internal view returns (AmountCalc memory) { uint256[] memory amounts = _getAmountsIn(reserveIn, reserveOut, amountOut); // Add flash loan fee @@ -337,27 +362,32 @@ contract BaseUniswapAdapter { uint256 reserveInDecimals = _getDecimals(reserveIn); uint256 reserveOutDecimals = _getDecimals(reserveOut); - uint256 inPerOutPrice = amountOut - .mul(10**18) - .mul(10**reserveInDecimals) - .div(finalAmountIn.mul(10**reserveOutDecimals)); + uint256 inPerOutPrice = + amountOut.mul(10**18).mul(10**reserveInDecimals).div( + finalAmountIn.mul(10**reserveOutDecimals) + ); - return AmountCalc( - finalAmountIn, - inPerOutPrice, - _calcUsdValue(reserveIn, finalAmountIn, reserveInDecimals), - _calcUsdValue(reserveOut, amountOut, reserveOutDecimals) - ); + return + AmountCalc( + finalAmountIn, + inPerOutPrice, + _calcUsdValue(reserveIn, finalAmountIn, reserveInDecimals), + _calcUsdValue(reserveOut, amountOut, reserveOutDecimals) + ); } /** - * @dev Calculates the input asset amount required to buy the given output asset amount - * @param reserveIn Address of the asset to be swap from - * @param reserveOut Address of the asset to be swap to - * @param amountOut Amount of reserveOut - * @return uint256[] amounts Array containing the amountIn and amountOut for a swap - */ - function _getAmountsIn(address reserveIn, address reserveOut, uint256 amountOut) internal view returns (uint256[] memory) { + * @dev Calculates the input asset amount required to buy the given output asset amount + * @param reserveIn Address of the asset to be swap from + * @param reserveOut Address of the asset to be swap to + * @param amountOut Amount of reserveOut + * @return uint256[] amounts Array containing the amountIn and amountOut for a swap + */ + function _getAmountsIn( + address reserveIn, + address reserveOut, + uint256 amountOut + ) internal view returns (uint256[] memory) { address[] memory path = new address[](2); path[0] = reserveIn; path[1] = reserveOut; diff --git a/contracts/adapters/UniswapLiquiditySwapAdapter.sol b/contracts/adapters/UniswapLiquiditySwapAdapter.sol index 8b325499..0adad441 100644 --- a/contracts/adapters/UniswapLiquiditySwapAdapter.sol +++ b/contracts/adapters/UniswapLiquiditySwapAdapter.sol @@ -5,7 +5,6 @@ pragma experimental ABIEncoderV2; import {BaseUniswapAdapter} from './BaseUniswapAdapter.sol'; import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol'; import {IUniswapV2Router02} from '../interfaces/IUniswapV2Router02.sol'; -import {IFlashLoanReceiver} from '../flashloan/interfaces/IFlashLoanReceiver.sol'; import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol'; /** @@ -13,8 +12,7 @@ import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol'; * @notice Uniswap V2 Adapter to swap liquidity. * @author Aave **/ -contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { - +contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter { struct PermitParams { uint256[] amount; uint256[] deadline; @@ -30,10 +28,7 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { PermitParams permitParams; } - constructor( - ILendingPoolAddressesProvider addressesProvider, - IUniswapV2Router02 uniswapRouter - ) + constructor(ILendingPoolAddressesProvider addressesProvider, IUniswapV2Router02 uniswapRouter) public BaseUniswapAdapter(addressesProvider, uniswapRouter) {} @@ -64,19 +59,19 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { address initiator, bytes calldata params ) external override returns (bool) { - require(msg.sender == address(POOL), "CALLER_MUST_BE_LENDING_POOL"); + require(msg.sender == address(LENDING_POOL), 'CALLER_MUST_BE_LENDING_POOL'); SwapParams memory decodedParams = _decodeParams(params); require( - assets.length == decodedParams.assetToSwapToList.length - && assets.length == decodedParams.minAmountsToReceive.length - && assets.length == decodedParams.swapAllBalance.length - && assets.length == decodedParams.permitParams.amount.length - && assets.length == decodedParams.permitParams.deadline.length - && assets.length == decodedParams.permitParams.v.length - && assets.length == decodedParams.permitParams.r.length - && assets.length == decodedParams.permitParams.s.length, + assets.length == decodedParams.assetToSwapToList.length && + assets.length == decodedParams.minAmountsToReceive.length && + assets.length == decodedParams.swapAllBalance.length && + assets.length == decodedParams.permitParams.amount.length && + assets.length == decodedParams.permitParams.deadline.length && + assets.length == decodedParams.permitParams.v.length && + assets.length == decodedParams.permitParams.r.length && + assets.length == decodedParams.permitParams.s.length, 'INCONSISTENT_PARAMS' ); @@ -127,10 +122,10 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { PermitSignature[] calldata permitParams ) external { require( - assetToSwapFromList.length == assetToSwapToList.length - && assetToSwapFromList.length == amountToSwapList.length - && assetToSwapFromList.length == minAmountsToReceive.length - && assetToSwapFromList.length == permitParams.length, + assetToSwapFromList.length == assetToSwapToList.length && + assetToSwapFromList.length == amountToSwapList.length && + assetToSwapFromList.length == minAmountsToReceive.length && + assetToSwapFromList.length == permitParams.length, 'INCONSISTENT_PARAMS' ); @@ -138,26 +133,22 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { address aToken = _getReserveData(assetToSwapFromList[i]).aTokenAddress; uint256 aTokenInitiatorBalance = IERC20(aToken).balanceOf(msg.sender); - uint256 amountToSwap = amountToSwapList[i] > aTokenInitiatorBalance ? aTokenInitiatorBalance : amountToSwapList[i]; + uint256 amountToSwap = + amountToSwapList[i] > aTokenInitiatorBalance ? aTokenInitiatorBalance : amountToSwapList[i]; - _pullAToken( - assetToSwapFromList[i], - aToken, - msg.sender, - amountToSwap, - permitParams[i] - ); + _pullAToken(assetToSwapFromList[i], aToken, msg.sender, amountToSwap, permitParams[i]); - uint256 receivedAmount = _swapExactTokensForTokens( - assetToSwapFromList[i], - assetToSwapToList[i], - amountToSwap, - minAmountsToReceive[i] - ); + uint256 receivedAmount = + _swapExactTokensForTokens( + assetToSwapFromList[i], + assetToSwapToList[i], + amountToSwap, + minAmountsToReceive[i] + ); // Deposit new reserve - IERC20(assetToSwapToList[i]).approve(address(POOL), receivedAmount); - POOL.deposit(assetToSwapToList[i], receivedAmount, msg.sender, 0); + IERC20(assetToSwapToList[i]).approve(address(LENDING_POOL), receivedAmount); + LENDING_POOL.deposit(assetToSwapToList[i], receivedAmount, msg.sender, 0); } } @@ -184,20 +175,17 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { address aToken = _getReserveData(assetFrom).aTokenAddress; uint256 aTokenInitiatorBalance = IERC20(aToken).balanceOf(initiator); - uint256 amountToSwap = swapAllBalance && aTokenInitiatorBalance.sub(premium) <= amount - ? aTokenInitiatorBalance.sub(premium) - : amount; + uint256 amountToSwap = + swapAllBalance && aTokenInitiatorBalance.sub(premium) <= amount + ? aTokenInitiatorBalance.sub(premium) + : amount; - uint256 receivedAmount = _swapExactTokensForTokens( - assetFrom, - assetTo, - amountToSwap, - minAmountToReceive - ); + uint256 receivedAmount = + _swapExactTokensForTokens(assetFrom, assetTo, amountToSwap, minAmountToReceive); // Deposit new reserve - IERC20(assetTo).approve(address(POOL), receivedAmount); - POOL.deposit(assetTo, receivedAmount, initiator, 0); + IERC20(assetTo).approve(address(LENDING_POOL), receivedAmount); + LENDING_POOL.deposit(assetTo, receivedAmount, initiator, 0); uint256 flashLoanDebt = amount.add(premium); uint256 amountToPull = amountToSwap.add(premium); @@ -205,7 +193,7 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { _pullAToken(assetFrom, aToken, initiator, amountToPull, permitSignature); // Repay flash loan - IERC20(assetFrom).approve(address(POOL), flashLoanDebt); + IERC20(assetFrom).approve(address(LENDING_POOL), flashLoanDebt); } /** @@ -231,19 +219,18 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { uint8[] memory v, bytes32[] memory r, bytes32[] memory s - ) = abi.decode(params, (address[], uint256[], bool[], uint256[], uint256[], uint8[], bytes32[], bytes32[])); + ) = + abi.decode( + params, + (address[], uint256[], bool[], uint256[], uint256[], uint8[], bytes32[], bytes32[]) + ); - return SwapParams( - assetToSwapToList, - minAmountsToReceive, - swapAllBalance, - PermitParams( - permitAmount, - deadline, - v, - r, - s - ) - ); + return + SwapParams( + assetToSwapToList, + minAmountsToReceive, + swapAllBalance, + PermitParams(permitAmount, deadline, v, r, s) + ); } } diff --git a/contracts/adapters/UniswapRepayAdapter.sol b/contracts/adapters/UniswapRepayAdapter.sol index 4945edf0..45d998ed 100644 --- a/contracts/adapters/UniswapRepayAdapter.sol +++ b/contracts/adapters/UniswapRepayAdapter.sol @@ -5,7 +5,6 @@ pragma experimental ABIEncoderV2; import {BaseUniswapAdapter} from './BaseUniswapAdapter.sol'; import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol'; import {IUniswapV2Router02} from '../interfaces/IUniswapV2Router02.sol'; -import {IFlashLoanReceiver} from '../flashloan/interfaces/IFlashLoanReceiver.sol'; import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol'; import {DataTypes} from '../protocol/libraries/types/DataTypes.sol'; @@ -14,8 +13,7 @@ import {DataTypes} from '../protocol/libraries/types/DataTypes.sol'; * @notice Uniswap V2 Adapter to perform a repay of a debt with collateral. * @author Aave **/ -contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { - +contract UniswapRepayAdapter is BaseUniswapAdapter { struct RepayParams { address collateralAsset; uint256 collateralAmount; @@ -23,10 +21,7 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { PermitSignature permitSignature; } - constructor( - ILendingPoolAddressesProvider addressesProvider, - IUniswapV2Router02 uniswapRouter - ) + constructor(ILendingPoolAddressesProvider addressesProvider, IUniswapV2Router02 uniswapRouter) public BaseUniswapAdapter(addressesProvider, uniswapRouter) {} @@ -58,20 +53,20 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { address initiator, bytes calldata params ) external override returns (bool) { - require(msg.sender == address(POOL), "CALLER_MUST_BE_LENDING_POOL"); + require(msg.sender == address(LENDING_POOL), 'CALLER_MUST_BE_LENDING_POOL'); RepayParams memory decodedParams = _decodeParams(params); - _swapAndRepay( - decodedParams.collateralAsset, - assets[0], - amounts[0], - decodedParams.collateralAmount, - decodedParams.rateMode, - initiator, - premiums[0], - decodedParams.permitSignature - ); + _swapAndRepay( + decodedParams.collateralAsset, + assets[0], + amounts[0], + decodedParams.collateralAmount, + decodedParams.rateMode, + initiator, + premiums[0], + decodedParams.permitSignature + ); return true; } @@ -99,9 +94,10 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { DataTypes.ReserveData memory collateralReserveData = _getReserveData(collateralAsset); DataTypes.ReserveData memory debtReserveData = _getReserveData(debtAsset); - address debtToken = DataTypes.InterestRateMode(debtRateMode) == DataTypes.InterestRateMode.STABLE - ? debtReserveData.stableDebtTokenAddress - : debtReserveData.variableDebtTokenAddress; + address debtToken = + DataTypes.InterestRateMode(debtRateMode) == DataTypes.InterestRateMode.STABLE + ? debtReserveData.stableDebtTokenAddress + : debtReserveData.variableDebtTokenAddress; uint256 currentDebt = IERC20(debtToken).balanceOf(msg.sender); uint256 amountToRepay = debtRepayAmount <= currentDebt ? debtRepayAmount : currentDebt; @@ -117,21 +113,32 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { require(amounts[0] <= maxCollateralToSwap, 'slippage too high'); // Pull aTokens from user - _pullAToken(collateralAsset, collateralReserveData.aTokenAddress, msg.sender, amounts[0], permitSignature); + _pullAToken( + collateralAsset, + collateralReserveData.aTokenAddress, + msg.sender, + amounts[0], + permitSignature + ); // Swap collateral for debt asset _swapTokensForExactTokens(collateralAsset, debtAsset, amounts[0], amountToRepay); } else { // Pull aTokens from user - _pullAToken(collateralAsset, collateralReserveData.aTokenAddress, msg.sender, amountToRepay, permitSignature); + _pullAToken( + collateralAsset, + collateralReserveData.aTokenAddress, + msg.sender, + amountToRepay, + permitSignature + ); } // Repay debt - IERC20(debtAsset).approve(address(POOL), amountToRepay); - POOL.repay(debtAsset, amountToRepay, debtRateMode, msg.sender); + IERC20(debtAsset).approve(address(LENDING_POOL), amountToRepay); + LENDING_POOL.repay(debtAsset, amountToRepay, debtRateMode, msg.sender); } - /** * @dev Perform the repay of the debt, pulls the initiator collateral and swaps to repay the flash loan * @@ -157,9 +164,9 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { DataTypes.ReserveData memory collateralReserveData = _getReserveData(collateralAsset); // Repay debt - IERC20(debtAsset).approve(address(POOL), amount); + IERC20(debtAsset).approve(address(LENDING_POOL), amount); uint256 repaidAmount = IERC20(debtAsset).balanceOf(address(this)); - POOL.repay(debtAsset, amount, rateMode, initiator); + LENDING_POOL.repay(debtAsset, amount, rateMode, initiator); repaidAmount = repaidAmount.sub(IERC20(debtAsset).balanceOf(address(this))); if (collateralAsset != debtAsset) { @@ -195,7 +202,7 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { } // Repay flash loan - IERC20(debtAsset).approve(address(POOL), amount.add(premium)); + IERC20(debtAsset).approve(address(LENDING_POOL), amount.add(premium)); } /** @@ -223,17 +230,12 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { bytes32 s ) = abi.decode(params, (address, uint256, uint256, uint256, uint256, uint8, bytes32, bytes32)); - return RepayParams( - collateralAsset, - collateralAmount, - rateMode, - PermitSignature( - permitAmount, - deadline, - v, - r, - s - ) - ); + return + RepayParams( + collateralAsset, + collateralAmount, + rateMode, + PermitSignature(permitAmount, deadline, v, r, s) + ); } } diff --git a/contracts/adapters/interfaces/IBaseUniswapAdapter.sol b/contracts/adapters/interfaces/IBaseUniswapAdapter.sol new file mode 100644 index 00000000..2dd6449b --- /dev/null +++ b/contracts/adapters/interfaces/IBaseUniswapAdapter.sol @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; +pragma experimental ABIEncoderV2; + +import {IPriceOracleGetter} from '../../interfaces/IPriceOracleGetter.sol'; +import {IUniswapV2Router02} from '../../interfaces/IUniswapV2Router02.sol'; + +interface IBaseUniswapAdapter { + event Swapped(address fromAsset, address toAsset, uint256 fromAmount, uint256 receivedAmount); + + struct PermitSignature { + uint256 amount; + uint256 deadline; + uint8 v; + bytes32 r; + bytes32 s; + } + + struct AmountCalc { + uint256 calculatedAmount; + uint256 relativePrice; + uint256 amountInUsd; + uint256 amountOutUsd; + } + + function MAX_SLIPPAGE_PERCENT() external returns (uint256); + + function FLASHLOAN_PREMIUM_TOTAL() external returns (uint256); + + function USD_ADDRESS() external returns (address); + + function ORACLE() external returns (IPriceOracleGetter); + + function UNISWAP_ROUTER() external returns (IUniswapV2Router02); + + /** + * @dev Given an input asset amount, returns the maximum output amount of the other asset and the prices + * @param amountIn Amount of reserveIn + * @param reserveIn Address of the asset to be swap from + * @param reserveOut Address of the asset to be swap to + * @return uint256 Amount out of the reserveOut + * @return uint256 The price of out amount denominated in the reserveIn currency (18 decimals) + * @return uint256 In amount of reserveIn value denominated in USD (8 decimals) + * @return uint256 Out amount of reserveOut value denominated in USD (8 decimals) + */ + function getAmountsOut( + uint256 amountIn, + address reserveIn, + address reserveOut + ) + external + view + returns ( + uint256, + uint256, + uint256, + uint256 + ); + + /** + * @dev Returns the minimum input asset amount required to buy the given output asset amount and the prices + * @param amountOut Amount of reserveOut + * @param reserveIn Address of the asset to be swap from + * @param reserveOut Address of the asset to be swap to + * @return uint256 Amount in of the reserveIn + * @return uint256 The price of in amount denominated in the reserveOut currency (18 decimals) + * @return uint256 In amount of reserveIn value denominated in USD (8 decimals) + * @return uint256 Out amount of reserveOut value denominated in USD (8 decimals) + */ + function getAmountsIn( + uint256 amountOut, + address reserveIn, + address reserveOut + ) + external + view + returns ( + uint256, + uint256, + uint256, + uint256 + ); +} diff --git a/hardhat.config.ts b/hardhat.config.ts index fafa0eff..1d1a0e1d 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -1,10 +1,13 @@ import path from 'path'; import fs from 'fs'; -import {HardhatUserConfig} from 'hardhat/types'; +import { HardhatUserConfig } from 'hardhat/types'; + +require('dotenv').config(); + // @ts-ignore -import {accounts} from './test-wallets.js'; -import {eEthereumNetwork} from './helpers/types'; -import {BUIDLEREVM_CHAINID, COVERAGE_CHAINID} from './helpers/buidler-constants'; +import { accounts } from './test-wallets.js'; +import { eEthereumNetwork } from './helpers/types'; +import { BUIDLEREVM_CHAINID, COVERAGE_CHAINID } from './helpers/buidler-constants'; import '@nomiclabs/hardhat-ethers'; import '@nomiclabs/hardhat-waffle'; @@ -27,7 +30,7 @@ const MAINNET_FORK = process.env.MAINNET_FORK === 'true'; // Prevent to load scripts before compilation and typechain if (!SKIP_LOAD) { - ['misc', 'migrations', 'dev', 'full', 'verifications'].forEach((folder) => { + ['misc', 'migrations', 'dev', 'full', 'verifications', 'deployments'].forEach((folder) => { const tasksPath = path.join(__dirname, 'tasks', folder); fs.readdirSync(tasksPath) .filter((pth) => pth.includes('.ts')) @@ -73,7 +76,7 @@ const buidlerConfig: HardhatUserConfig = { solidity: { version: '0.6.12', settings: { - optimizer: {enabled: true, runs: 200}, + optimizer: { enabled: true, runs: 200 }, evmVersion: 'istanbul', }, }, @@ -109,7 +112,7 @@ const buidlerConfig: HardhatUserConfig = { chainId: BUIDLEREVM_CHAINID, throwOnTransactionFailures: true, throwOnCallFailures: true, - accounts: accounts.map(({secretKey, balance}: {secretKey: string; balance: string}) => ({ + accounts: accounts.map(({ secretKey, balance }: { secretKey: string; balance: string }) => ({ privateKey: secretKey, balance, })), diff --git a/package-lock.json b/package-lock.json index 32738e30..3ea8f296 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9504,14 +9504,6 @@ "prr": "~1.0.1", "semver": "~5.4.1", "xtend": "~4.0.0" - }, - "dependencies": { - "semver": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", - "dev": true - } } }, "merkle-patricia-tree": { @@ -9901,14 +9893,6 @@ "prr": "~1.0.1", "semver": "~5.4.1", "xtend": "~4.0.0" - }, - "dependencies": { - "semver": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", - "dev": true - } } }, "merkle-patricia-tree": { @@ -10200,14 +10184,6 @@ "prr": "~1.0.1", "semver": "~5.4.1", "xtend": "~4.0.0" - }, - "dependencies": { - "semver": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", - "dev": true - } } }, "merkle-patricia-tree": { @@ -10459,14 +10435,6 @@ "prr": "~1.0.1", "semver": "~5.4.1", "xtend": "~4.0.0" - }, - "dependencies": { - "semver": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", - "dev": true - } } }, "merkle-patricia-tree": { @@ -12428,6 +12396,17 @@ "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" + }, + "dependencies": { + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } } }, "safe-buffer": { @@ -12436,6 +12415,11 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -14250,6 +14234,12 @@ "integrity": "sha512-O4OZEaNtkMd/K0i6js9SL+gqy0ZCBMgUvlSqHKi4IBdjhe7wB8pwztUk1BbZ1fmrvpwFrPbHzqd2w5pTcJH6LA==", "dev": true }, + "semver": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", + "dev": true + }, "semver-greatest-satisfied-range": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz", @@ -16306,14 +16296,6 @@ "prr": "~1.0.1", "semver": "~5.4.1", "xtend": "~4.0.0" - }, - "dependencies": { - "semver": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", - "dev": true - } } }, "merkle-patricia-tree": { diff --git a/package.json b/package.json index 14dd43c3..f22269f4 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "print-contracts:main": "npm run hardhat:main -- print-contracts", "print-contracts:ropsten": "npm run hardhat:main -- print-contracts", "dev:deployUIProvider": "npm run hardhat:kovan deploy-UiPoolDataProvider", + "dev:deployUniswapRepayAdapter": "npm run hardhat:kovan deploy-UniswapRepayAdapter", "kovan:verify": "npm run hardhat:kovan verify:general -- --all --pool Aave", "ropsten:verify": "npm run hardhat:ropsten verify:general -- --all --pool Aave", "mainnet:verify": "npm run hardhat:main verify:general -- --all --pool Aave", diff --git a/tasks/deployments/deploy-UiPoolDataProvider.ts b/tasks/deployments/deploy-UiPoolDataProvider.ts index b03ca70d..2d696702 100644 --- a/tasks/deployments/deploy-UiPoolDataProvider.ts +++ b/tasks/deployments/deploy-UiPoolDataProvider.ts @@ -1,13 +1,13 @@ -import {task} from '@nomiclabs/buidler/config'; +import { task } from 'hardhat/config'; -import {UiPoolDataProviderFactory} from '../../types'; -import {verifyContract} from '../../helpers/etherscan-verification'; -import {eContractid} from '../../helpers/types'; +import { UiPoolDataProviderFactory } from '../../types'; +import { verifyContract } from '../../helpers/etherscan-verification'; +import { eContractid } from '../../helpers/types'; task(`deploy-${eContractid.UiPoolDataProvider}`, `Deploys the UiPoolDataProvider contract`) .addFlag('verify', 'Verify UiPoolDataProvider contract via Etherscan API.') - .setAction(async ({verify}, localBRE) => { - await localBRE.run('set-bre'); + .setAction(async ({ verify }, localBRE) => { + await localBRE.run('set-DRE'); if (!localBRE.network.config.chainId) { throw new Error('INVALID_CHAIN_ID'); @@ -21,7 +21,7 @@ task(`deploy-${eContractid.UiPoolDataProvider}`, `Deploys the UiPoolDataProvider ).deploy(); await uiPoolDataProvider.deployTransaction.wait(); console.log('uiPoolDataProvider.address', uiPoolDataProvider.address); - await verifyContract(eContractid.UiPoolDataProvider, uiPoolDataProvider.address, []); + await verifyContract(uiPoolDataProvider.address, []); console.log(`\tFinished UiPoolDataProvider proxy and implementation deployment`); }); diff --git a/tasks/deployments/deploy-UniswapRepayAdapter.ts b/tasks/deployments/deploy-UniswapRepayAdapter.ts new file mode 100644 index 00000000..19c77b5d --- /dev/null +++ b/tasks/deployments/deploy-UniswapRepayAdapter.ts @@ -0,0 +1,30 @@ +import { task } from 'hardhat/config'; + +import { UniswapRepayAdapterFactory } from '../../types'; +import { verifyContract } from '../../helpers/etherscan-verification'; + +const CONTRACT_NAME = 'UniswapRepayAdapter'; + +task(`deploy-${CONTRACT_NAME}`, `Deploys the UniswapRepayAdapter contract`) + .addFlag('verify', 'Verify UniswapRepayAdapter contract via Etherscan API.') + .setAction(async ({ verify }, localBRE) => { + await localBRE.run('set-DRE'); + + if (!localBRE.network.config.chainId) { + throw new Error('INVALID_CHAIN_ID'); + } + + console.log(`\n- UniswapRepayAdapter deployment`); + const args = [ + '0x9fe532197ad76c5a68961439604c037eb79681f0', + '0xfcd87315f0e4067070ade8682fcdbc3006631441', + ]; + const uniswapRepayAdapter = await new UniswapRepayAdapterFactory( + await localBRE.ethers.provider.getSigner() + ).deploy(args[0], args[1]); + await uniswapRepayAdapter.deployTransaction.wait(); + console.log('uniswapRepayAdapter.address', uniswapRepayAdapter.address); + await verifyContract(uniswapRepayAdapter.address, args); + + console.log(`\tFinished UiPoolDataProvider proxy and implementation deployment`); + }); From c22533f13980fe719c8fd97989d8dc5510444978 Mon Sep 17 00:00:00 2001 From: David Racero Date: Mon, 30 Nov 2020 14:27:56 +0100 Subject: [PATCH 36/62] Add lending provider kovan address at deploy-UniswapRepayAdapter task --- tasks/deployments/deploy-UniswapRepayAdapter.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/tasks/deployments/deploy-UniswapRepayAdapter.ts b/tasks/deployments/deploy-UniswapRepayAdapter.ts index 19c77b5d..62224a9e 100644 --- a/tasks/deployments/deploy-UniswapRepayAdapter.ts +++ b/tasks/deployments/deploy-UniswapRepayAdapter.ts @@ -2,6 +2,7 @@ import { task } from 'hardhat/config'; import { UniswapRepayAdapterFactory } from '../../types'; import { verifyContract } from '../../helpers/etherscan-verification'; +import { getFirstSigner } from '../../helpers/contracts-getters'; const CONTRACT_NAME = 'UniswapRepayAdapter'; @@ -16,12 +17,15 @@ task(`deploy-${CONTRACT_NAME}`, `Deploys the UniswapRepayAdapter contract`) console.log(`\n- UniswapRepayAdapter deployment`); const args = [ - '0x9fe532197ad76c5a68961439604c037eb79681f0', - '0xfcd87315f0e4067070ade8682fcdbc3006631441', + '0x88757f2f99175387aB4C6a4b3067c77A695b0349', // lending provider kovan address + '0xfcd87315f0e4067070ade8682fcdbc3006631441', // uniswap router address ]; - const uniswapRepayAdapter = await new UniswapRepayAdapterFactory( - await localBRE.ethers.provider.getSigner() - ).deploy(args[0], args[1]); + console.log('before'); + const uniswapRepayAdapter = await new UniswapRepayAdapterFactory(await getFirstSigner()).deploy( + args[0], + args[1] + ); + console.log('afta'); await uniswapRepayAdapter.deployTransaction.wait(); console.log('uniswapRepayAdapter.address', uniswapRepayAdapter.address); await verifyContract(uniswapRepayAdapter.address, args); From a21d82364673e9798e0135b65e59dfc030e7b50f Mon Sep 17 00:00:00 2001 From: David Racero Date: Mon, 30 Nov 2020 14:32:01 +0100 Subject: [PATCH 37/62] Remove console logs --- tasks/deployments/deploy-UniswapRepayAdapter.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/tasks/deployments/deploy-UniswapRepayAdapter.ts b/tasks/deployments/deploy-UniswapRepayAdapter.ts index 62224a9e..15d8cf54 100644 --- a/tasks/deployments/deploy-UniswapRepayAdapter.ts +++ b/tasks/deployments/deploy-UniswapRepayAdapter.ts @@ -20,12 +20,10 @@ task(`deploy-${CONTRACT_NAME}`, `Deploys the UniswapRepayAdapter contract`) '0x88757f2f99175387aB4C6a4b3067c77A695b0349', // lending provider kovan address '0xfcd87315f0e4067070ade8682fcdbc3006631441', // uniswap router address ]; - console.log('before'); const uniswapRepayAdapter = await new UniswapRepayAdapterFactory(await getFirstSigner()).deploy( args[0], args[1] ); - console.log('afta'); await uniswapRepayAdapter.deployTransaction.wait(); console.log('uniswapRepayAdapter.address', uniswapRepayAdapter.address); await verifyContract(uniswapRepayAdapter.address, args); From 6ce019c04a496aa34c89c4b043a375fbc9e4f497 Mon Sep 17 00:00:00 2001 From: andyk Date: Tue, 1 Dec 2020 14:27:57 +0400 Subject: [PATCH 38/62] add deployment task for UniswapRepayAdapter --- package.json | 1 + .../deploy-UniswapLiquiditySwapAdapter.ts | 31 +++++++++++++++++++ .../deployments/deploy-UniswapRepayAdapter.ts | 12 ++++--- 3 files changed, 39 insertions(+), 5 deletions(-) create mode 100644 tasks/deployments/deploy-UniswapLiquiditySwapAdapter.ts diff --git a/package.json b/package.json index f22269f4..8c3622f1 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "print-contracts:ropsten": "npm run hardhat:main -- print-contracts", "dev:deployUIProvider": "npm run hardhat:kovan deploy-UiPoolDataProvider", "dev:deployUniswapRepayAdapter": "npm run hardhat:kovan deploy-UniswapRepayAdapter", + "dev:UniswapLiquiditySwapAdapter": "npm run hardhat:kovan deploy-UniswapLiquiditySwapAdapter", "kovan:verify": "npm run hardhat:kovan verify:general -- --all --pool Aave", "ropsten:verify": "npm run hardhat:ropsten verify:general -- --all --pool Aave", "mainnet:verify": "npm run hardhat:main verify:general -- --all --pool Aave", diff --git a/tasks/deployments/deploy-UniswapLiquiditySwapAdapter.ts b/tasks/deployments/deploy-UniswapLiquiditySwapAdapter.ts new file mode 100644 index 00000000..797ec36e --- /dev/null +++ b/tasks/deployments/deploy-UniswapLiquiditySwapAdapter.ts @@ -0,0 +1,31 @@ +import { task } from 'hardhat/config'; + +import { UniswapLiquiditySwapAdapterFactory } from '../../types'; +import { verifyContract } from '../../helpers/etherscan-verification'; +import { getFirstSigner } from '../../helpers/contracts-getters'; + +const CONTRACT_NAME = 'UniswapLiquiditySwapAdapter'; + +task(`deploy-${CONTRACT_NAME}`, `Deploys the ${CONTRACT_NAME} contract`) + .addFlag('verify', `Verify ${CONTRACT_NAME} contract via Etherscan API.`) + .setAction(async ({ verify }, localBRE) => { + await localBRE.run('set-DRE'); + + if (!localBRE.network.config.chainId) { + throw new Error('INVALID_CHAIN_ID'); + } + + console.log(`\n- ${CONTRACT_NAME} deployment`); + const args = [ + '0x88757f2f99175387aB4C6a4b3067c77A695b0349', // lending provider kovan address + '0xfcd87315f0e4067070ade8682fcdbc3006631441', // uniswap router address + ]; + const uniswapRepayAdapter = await new UniswapLiquiditySwapAdapterFactory( + await getFirstSigner() + ).deploy(args[0], args[1]); + await uniswapRepayAdapter.deployTransaction.wait(); + console.log(`${CONTRACT_NAME}.address`, uniswapRepayAdapter.address); + await verifyContract(uniswapRepayAdapter.address, args); + + console.log(`\tFinished ${CONTRACT_NAME} proxy and implementation deployment`); + }); diff --git a/tasks/deployments/deploy-UniswapRepayAdapter.ts b/tasks/deployments/deploy-UniswapRepayAdapter.ts index 15d8cf54..50bff115 100644 --- a/tasks/deployments/deploy-UniswapRepayAdapter.ts +++ b/tasks/deployments/deploy-UniswapRepayAdapter.ts @@ -6,8 +6,8 @@ import { getFirstSigner } from '../../helpers/contracts-getters'; const CONTRACT_NAME = 'UniswapRepayAdapter'; -task(`deploy-${CONTRACT_NAME}`, `Deploys the UniswapRepayAdapter contract`) - .addFlag('verify', 'Verify UniswapRepayAdapter contract via Etherscan API.') +task(`deploy-${CONTRACT_NAME}`, `Deploys the ${CONTRACT_NAME} contract`) + .addFlag('verify', `Verify ${CONTRACT_NAME} contract via Etherscan API.`) .setAction(async ({ verify }, localBRE) => { await localBRE.run('set-DRE'); @@ -15,7 +15,7 @@ task(`deploy-${CONTRACT_NAME}`, `Deploys the UniswapRepayAdapter contract`) throw new Error('INVALID_CHAIN_ID'); } - console.log(`\n- UniswapRepayAdapter deployment`); + console.log(`\n- ${CONTRACT_NAME} deployment`); const args = [ '0x88757f2f99175387aB4C6a4b3067c77A695b0349', // lending provider kovan address '0xfcd87315f0e4067070ade8682fcdbc3006631441', // uniswap router address @@ -25,8 +25,10 @@ task(`deploy-${CONTRACT_NAME}`, `Deploys the UniswapRepayAdapter contract`) args[1] ); await uniswapRepayAdapter.deployTransaction.wait(); - console.log('uniswapRepayAdapter.address', uniswapRepayAdapter.address); + console.log(`${CONTRACT_NAME}.address`, uniswapRepayAdapter.address); await verifyContract(uniswapRepayAdapter.address, args); - console.log(`\tFinished UiPoolDataProvider proxy and implementation deployment`); + console.log( + `\tFinished ${CONTRACT_NAME}${CONTRACT_NAME}lDataProvider proxy and implementation deployment` + ); }); From 8113cdaef14a31118aaf2a5dede9f496d1829342 Mon Sep 17 00:00:00 2001 From: The3D Date: Wed, 2 Dec 2020 11:18:31 +0100 Subject: [PATCH 39/62] Updated migration scripts to receive external parameters --- hardhat.config.ts | 2 +- package.json | 8 ++++---- .../deployments/deploy-UniswapLiquiditySwapAdapter.ts | 11 +++++++---- tasks/deployments/deploy-UniswapRepayAdapter.ts | 10 ++++++---- 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/hardhat.config.ts b/hardhat.config.ts index be8c4aa9..f4dcaf69 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -16,7 +16,7 @@ import '@tenderly/hardhat-tenderly'; const SKIP_LOAD = process.env.SKIP_LOAD === 'true'; const DEFAULT_BLOCK_GAS_LIMIT = 12450000; const DEFAULT_GAS_MUL = 5; -const DEFAULT_GAS_PRICE = 2000000000; +const DEFAULT_GAS_PRICE = 65000000000; const HARDFORK = 'istanbul'; const INFURA_KEY = process.env.INFURA_KEY || ''; const ALCHEMY_KEY = process.env.ALCHEMY_KEY || ''; diff --git a/package.json b/package.json index 15bcb96f..4c8fdd95 100644 --- a/package.json +++ b/package.json @@ -43,10 +43,10 @@ "print-contracts:main": "npm run hardhat:main -- print-contracts", "print-contracts:ropsten": "npm run hardhat:main -- print-contracts", "dev:deployUIProvider": "npm run hardhat:kovan deploy-UiPoolDataProvider", - "dev:deployUniswapRepayAdapter": "npm run hardhat:kovan deploy-UniswapRepayAdapter", - "dev:UniswapLiquiditySwapAdapter": "npm run hardhat:kovan deploy-UniswapLiquiditySwapAdapter", - "main:deployUniswapRepayAdapter": "npm run hardhat:main deploy-UniswapRepayAdapter", - "main:UniswapLiquiditySwapAdapter": "npm run hardhat:main deploy-UniswapLiquiditySwapAdapter", + "dev:deployUniswapRepayAdapter": "hardhat --network kovan deploy-UniswapRepayAdapter --provider 0x88757f2f99175387aB4C6a4b3067c77A695b0349 --router 0xfcd87315f0e4067070ade8682fcdbc3006631441", + "dev:UniswapLiquiditySwapAdapter": "hardhat --network kovan deploy-UniswapLiquiditySwapAdapter --provider 0x88757f2f99175387aB4C6a4b3067c77A695b0349 --router 0xfcd87315f0e4067070ade8682fcdbc3006631441", + "main:deployUniswapRepayAdapter": "hardhat --network main deploy-UniswapRepayAdapter --provider 0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5 --router 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D", + "main:UniswapLiquiditySwapAdapter": "hardhat --network main deploy-UniswapLiquiditySwapAdapter --provider 0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5 --router 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D", "kovan:verify": "npm run hardhat:kovan verify:general -- --all --pool Aave", "ropsten:verify": "npm run hardhat:ropsten verify:general -- --all --pool Aave", "mainnet:verify": "npm run hardhat:main verify:general -- --all --pool Aave", diff --git a/tasks/deployments/deploy-UniswapLiquiditySwapAdapter.ts b/tasks/deployments/deploy-UniswapLiquiditySwapAdapter.ts index 797ec36e..af9aaa04 100644 --- a/tasks/deployments/deploy-UniswapLiquiditySwapAdapter.ts +++ b/tasks/deployments/deploy-UniswapLiquiditySwapAdapter.ts @@ -7,8 +7,10 @@ import { getFirstSigner } from '../../helpers/contracts-getters'; const CONTRACT_NAME = 'UniswapLiquiditySwapAdapter'; task(`deploy-${CONTRACT_NAME}`, `Deploys the ${CONTRACT_NAME} contract`) + .addParam('provider', 'Address of the LendingPoolAddressesProvider') + .addParam('router', 'Address of the uniswap router') .addFlag('verify', `Verify ${CONTRACT_NAME} contract via Etherscan API.`) - .setAction(async ({ verify }, localBRE) => { + .setAction(async ({ provider, router, verify }, localBRE) => { await localBRE.run('set-DRE'); if (!localBRE.network.config.chainId) { @@ -16,16 +18,17 @@ task(`deploy-${CONTRACT_NAME}`, `Deploys the ${CONTRACT_NAME} contract`) } console.log(`\n- ${CONTRACT_NAME} deployment`); - const args = [ + /*const args = [ '0x88757f2f99175387aB4C6a4b3067c77A695b0349', // lending provider kovan address '0xfcd87315f0e4067070ade8682fcdbc3006631441', // uniswap router address ]; + */ const uniswapRepayAdapter = await new UniswapLiquiditySwapAdapterFactory( await getFirstSigner() - ).deploy(args[0], args[1]); + ).deploy(provider, router); await uniswapRepayAdapter.deployTransaction.wait(); console.log(`${CONTRACT_NAME}.address`, uniswapRepayAdapter.address); - await verifyContract(uniswapRepayAdapter.address, args); + await verifyContract(uniswapRepayAdapter.address, [provider, router]); console.log(`\tFinished ${CONTRACT_NAME} proxy and implementation deployment`); }); diff --git a/tasks/deployments/deploy-UniswapRepayAdapter.ts b/tasks/deployments/deploy-UniswapRepayAdapter.ts index 50bff115..5d52a36c 100644 --- a/tasks/deployments/deploy-UniswapRepayAdapter.ts +++ b/tasks/deployments/deploy-UniswapRepayAdapter.ts @@ -7,8 +7,10 @@ import { getFirstSigner } from '../../helpers/contracts-getters'; const CONTRACT_NAME = 'UniswapRepayAdapter'; task(`deploy-${CONTRACT_NAME}`, `Deploys the ${CONTRACT_NAME} contract`) + .addParam('provider', 'Address of the LendingPoolAddressesProvider') + .addParam('router', 'Address of the uniswap router') .addFlag('verify', `Verify ${CONTRACT_NAME} contract via Etherscan API.`) - .setAction(async ({ verify }, localBRE) => { + .setAction(async ({ provider, router, verify }, localBRE) => { await localBRE.run('set-DRE'); if (!localBRE.network.config.chainId) { @@ -21,12 +23,12 @@ task(`deploy-${CONTRACT_NAME}`, `Deploys the ${CONTRACT_NAME} contract`) '0xfcd87315f0e4067070ade8682fcdbc3006631441', // uniswap router address ]; const uniswapRepayAdapter = await new UniswapRepayAdapterFactory(await getFirstSigner()).deploy( - args[0], - args[1] + provider, + router ); await uniswapRepayAdapter.deployTransaction.wait(); console.log(`${CONTRACT_NAME}.address`, uniswapRepayAdapter.address); - await verifyContract(uniswapRepayAdapter.address, args); + await verifyContract(uniswapRepayAdapter.address, [provider, router]); console.log( `\tFinished ${CONTRACT_NAME}${CONTRACT_NAME}lDataProvider proxy and implementation deployment` From a25bdd92806232ab6defc2370eb1d358bfb7bf8e Mon Sep 17 00:00:00 2001 From: eboado Date: Wed, 2 Dec 2020 14:39:05 +0100 Subject: [PATCH 40/62] - Change path logic on repay adapter and base --- contracts/adapters/BaseUniswapAdapter.sol | 67 ++++++++++++++----- contracts/adapters/UniswapRepayAdapter.sol | 4 +- .../interfaces/IBaseUniswapAdapter.sol | 9 ++- 3 files changed, 60 insertions(+), 20 deletions(-) diff --git a/contracts/adapters/BaseUniswapAdapter.sol b/contracts/adapters/BaseUniswapAdapter.sol index c8d3702a..b0a71942 100644 --- a/contracts/adapters/BaseUniswapAdapter.sol +++ b/contracts/adapters/BaseUniswapAdapter.sol @@ -32,6 +32,8 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt // USD oracle asset address address public constant override USD_ADDRESS = 0x10F7Fc1F91Ba351f9C629c5947AD69bD03C05b96; + address public constant WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; + IPriceOracleGetter public immutable override ORACLE; IUniswapV2Router02 public immutable override UNISWAP_ROUTER; @@ -65,7 +67,8 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt uint256, uint256, uint256, - uint256 + uint256, + address[] memory ) { AmountCalc memory results = _getAmountsOutData(reserveIn, reserveOut, amountIn); @@ -74,7 +77,8 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt results.calculatedAmount, results.relativePrice, results.amountInUsd, - results.amountOutUsd + results.amountOutUsd, + results.path ); } @@ -100,7 +104,8 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt uint256, uint256, uint256, - uint256 + uint256, + address[] memory ) { AmountCalc memory results = _getAmountsInData(reserveIn, reserveOut, amountOut); @@ -109,7 +114,8 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt results.calculatedAmount, results.relativePrice, results.amountInUsd, - results.amountOutUsd + results.amountOutUsd, + results.path ); } @@ -315,11 +321,23 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt // Subtract flash loan fee uint256 finalAmountIn = amountIn.sub(amountIn.mul(FLASHLOAN_PREMIUM_TOTAL).div(10000)); - address[] memory path = new address[](2); - path[0] = reserveIn; - path[1] = reserveOut; + address[] memory simplePath = new address[](2); + simplePath[0] = reserveIn; + simplePath[1] = reserveOut; - uint256[] memory amounts = UNISWAP_ROUTER.getAmountsOut(finalAmountIn, path); + uint256[] memory amounts; + address[] memory pathWithWeth = new address[](3); + try UNISWAP_ROUTER.getAmountsOut(finalAmountIn, simplePath) returns ( + uint256[] memory resultAmounts + ) { + amounts = resultAmounts; + } catch { + pathWithWeth[0] = reserveIn; + pathWithWeth[1] = WETH_ADDRESS; + pathWithWeth[2] = reserveOut; + + amounts = UNISWAP_ROUTER.getAmountsOut(finalAmountIn, pathWithWeth); + } uint256 reserveInDecimals = _getDecimals(reserveIn); uint256 reserveOutDecimals = _getDecimals(reserveOut); @@ -334,7 +352,8 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt amounts[1], outPerInPrice, _calcUsdValue(reserveIn, amountIn, reserveInDecimals), - _calcUsdValue(reserveOut, amounts[1], reserveOutDecimals) + _calcUsdValue(reserveOut, amounts[1], reserveOutDecimals), + (pathWithWeth[0] != address(0) ? pathWithWeth : simplePath) ); } @@ -354,7 +373,8 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt address reserveOut, uint256 amountOut ) internal view returns (AmountCalc memory) { - uint256[] memory amounts = _getAmountsIn(reserveIn, reserveOut, amountOut); + (uint256[] memory amounts, address[] memory path) = + _getAmountsIn(reserveIn, reserveOut, amountOut); // Add flash loan fee uint256 finalAmountIn = amounts[0].add(amounts[0].mul(FLASHLOAN_PREMIUM_TOTAL).div(10000)); @@ -372,7 +392,8 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt finalAmountIn, inPerOutPrice, _calcUsdValue(reserveIn, finalAmountIn, reserveInDecimals), - _calcUsdValue(reserveOut, amountOut, reserveOutDecimals) + _calcUsdValue(reserveOut, amountOut, reserveOutDecimals), + path ); } @@ -387,11 +408,25 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt address reserveIn, address reserveOut, uint256 amountOut - ) internal view returns (uint256[] memory) { - address[] memory path = new address[](2); - path[0] = reserveIn; - path[1] = reserveOut; + ) internal view returns (uint256[] memory, address[] memory) { + address[] memory simplePath = new address[](2); + simplePath[0] = reserveIn; + simplePath[1] = reserveOut; - return UNISWAP_ROUTER.getAmountsIn(amountOut, path); + uint256[] memory amounts; + address[] memory pathWithWeth = new address[](3); + try UNISWAP_ROUTER.getAmountsIn(amountOut, simplePath) returns ( + uint256[] memory resultAmounts + ) { + amounts = resultAmounts; + } catch { + pathWithWeth[0] = reserveIn; + pathWithWeth[1] = WETH_ADDRESS; + pathWithWeth[2] = reserveOut; + + amounts = UNISWAP_ROUTER.getAmountsIn(amountOut, pathWithWeth); + } + + return (amounts, (pathWithWeth[0] != address(0) ? pathWithWeth : simplePath)); } } diff --git a/contracts/adapters/UniswapRepayAdapter.sol b/contracts/adapters/UniswapRepayAdapter.sol index 45d998ed..2fa23d6d 100644 --- a/contracts/adapters/UniswapRepayAdapter.sol +++ b/contracts/adapters/UniswapRepayAdapter.sol @@ -109,7 +109,7 @@ contract UniswapRepayAdapter is BaseUniswapAdapter { } // Get exact collateral needed for the swap to avoid leftovers - uint256[] memory amounts = _getAmountsIn(collateralAsset, debtAsset, amountToRepay); + (uint256[] memory amounts,) = _getAmountsIn(collateralAsset, debtAsset, amountToRepay); require(amounts[0] <= maxCollateralToSwap, 'slippage too high'); // Pull aTokens from user @@ -176,7 +176,7 @@ contract UniswapRepayAdapter is BaseUniswapAdapter { } uint256 neededForFlashLoanDebt = repaidAmount.add(premium); - uint256[] memory amounts = _getAmountsIn(collateralAsset, debtAsset, neededForFlashLoanDebt); + (uint256[] memory amounts,) = _getAmountsIn(collateralAsset, debtAsset, neededForFlashLoanDebt); require(amounts[0] <= maxCollateralToSwap, 'slippage too high'); // Pull aTokens from user diff --git a/contracts/adapters/interfaces/IBaseUniswapAdapter.sol b/contracts/adapters/interfaces/IBaseUniswapAdapter.sol index 2dd6449b..540f9e46 100644 --- a/contracts/adapters/interfaces/IBaseUniswapAdapter.sol +++ b/contracts/adapters/interfaces/IBaseUniswapAdapter.sol @@ -21,6 +21,7 @@ interface IBaseUniswapAdapter { uint256 relativePrice; uint256 amountInUsd; uint256 amountOutUsd; + address[] path; } function MAX_SLIPPAGE_PERCENT() external returns (uint256); @@ -42,6 +43,7 @@ interface IBaseUniswapAdapter { * @return uint256 The price of out amount denominated in the reserveIn currency (18 decimals) * @return uint256 In amount of reserveIn value denominated in USD (8 decimals) * @return uint256 Out amount of reserveOut value denominated in USD (8 decimals) + * @return address[] The exchange path */ function getAmountsOut( uint256 amountIn, @@ -54,7 +56,8 @@ interface IBaseUniswapAdapter { uint256, uint256, uint256, - uint256 + uint256, + address[] memory ); /** @@ -66,6 +69,7 @@ interface IBaseUniswapAdapter { * @return uint256 The price of in amount denominated in the reserveOut currency (18 decimals) * @return uint256 In amount of reserveIn value denominated in USD (8 decimals) * @return uint256 Out amount of reserveOut value denominated in USD (8 decimals) + * @return address[] The exchange path */ function getAmountsIn( uint256 amountOut, @@ -78,6 +82,7 @@ interface IBaseUniswapAdapter { uint256, uint256, uint256, - uint256 + uint256, + address[] memory ); } From d611b1e8dc01459dfc2c30640179a8836fbef900 Mon Sep 17 00:00:00 2001 From: The3D Date: Wed, 2 Dec 2020 15:30:57 +0100 Subject: [PATCH 41/62] Updated to receive theindication on which path to use from outside --- contracts/adapters/BaseUniswapAdapter.sol | 52 ++++++++++++--- .../adapters/UniswapLiquiditySwapAdapter.sol | 63 ++++++++++++------- contracts/adapters/UniswapRepayAdapter.sol | 24 ++++--- 3 files changed, 103 insertions(+), 36 deletions(-) diff --git a/contracts/adapters/BaseUniswapAdapter.sol b/contracts/adapters/BaseUniswapAdapter.sol index b0a71942..5f0851a7 100644 --- a/contracts/adapters/BaseUniswapAdapter.sol +++ b/contracts/adapters/BaseUniswapAdapter.sol @@ -131,7 +131,8 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt address assetToSwapFrom, address assetToSwapTo, uint256 amountToSwap, - uint256 minAmountOut + uint256 minAmountOut, + bool useEthPath ) internal returns (uint256) { uint256 fromAssetDecimals = _getDecimals(assetToSwapFrom); uint256 toAssetDecimals = _getDecimals(assetToSwapTo); @@ -149,9 +150,17 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt IERC20(assetToSwapFrom).approve(address(UNISWAP_ROUTER), amountToSwap); - address[] memory path = new address[](2); - path[0] = assetToSwapFrom; - path[1] = assetToSwapTo; + address[] memory path; + if (useEthPath) { + path = new address[](3); + path[0] = assetToSwapFrom; + path[1] = WETH_ADDRESS; + path[2] = assetToSwapTo; + } else { + path = new address[](2); + path[0] = assetToSwapFrom; + path[1] = assetToSwapTo; + } uint256[] memory amounts = UNISWAP_ROUTER.swapExactTokensForTokens( amountToSwap, @@ -374,7 +383,7 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt uint256 amountOut ) internal view returns (AmountCalc memory) { (uint256[] memory amounts, address[] memory path) = - _getAmountsIn(reserveIn, reserveOut, amountOut); + _getAmountsInAndPath(reserveIn, reserveOut, amountOut); // Add flash loan fee uint256 finalAmountIn = amounts[0].add(amounts[0].mul(FLASHLOAN_PREMIUM_TOTAL).div(10000)); @@ -404,7 +413,7 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt * @param amountOut Amount of reserveOut * @return uint256[] amounts Array containing the amountIn and amountOut for a swap */ - function _getAmountsIn( + function _getAmountsInAndPath( address reserveIn, address reserveOut, uint256 amountOut @@ -419,14 +428,43 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt uint256[] memory resultAmounts ) { amounts = resultAmounts; + return (amounts, simplePath); } catch { pathWithWeth[0] = reserveIn; pathWithWeth[1] = WETH_ADDRESS; pathWithWeth[2] = reserveOut; amounts = UNISWAP_ROUTER.getAmountsIn(amountOut, pathWithWeth); + return (amounts, pathWithWeth); + } + } + + /** + * @dev Calculates the input asset amount required to buy the given output asset amount + * @param reserveIn Address of the asset to be swap from + * @param reserveOut Address of the asset to be swap to + * @param amountOut Amount of reserveOut + * @return uint256[] amounts Array containing the amountIn and amountOut for a swap + */ + function _getAmountsIn( + address reserveIn, + address reserveOut, + uint256 amountOut, + bool useEthPath + ) internal view returns (uint256[] memory) { + address[] memory path; + + if (useEthPath) { + path = new address[](3); + path[0] = reserveIn; + path[1] = WETH_ADDRESS; + path[2] = reserveOut; + } else { + path = new address[](2); + path[0] = reserveIn; + path[1] = reserveOut; } - return (amounts, (pathWithWeth[0] != address(0) ? pathWithWeth : simplePath)); + return UNISWAP_ROUTER.getAmountsIn(amountOut, path); } } diff --git a/contracts/adapters/UniswapLiquiditySwapAdapter.sol b/contracts/adapters/UniswapLiquiditySwapAdapter.sol index 0adad441..b4d01550 100644 --- a/contracts/adapters/UniswapLiquiditySwapAdapter.sol +++ b/contracts/adapters/UniswapLiquiditySwapAdapter.sol @@ -26,6 +26,7 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter { uint256[] minAmountsToReceive; bool[] swapAllBalance; PermitParams permitParams; + bool[] useEthPath; } constructor(ILendingPoolAddressesProvider addressesProvider, IUniswapV2Router02 uniswapRouter) @@ -71,7 +72,8 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter { assets.length == decodedParams.permitParams.deadline.length && assets.length == decodedParams.permitParams.v.length && assets.length == decodedParams.permitParams.r.length && - assets.length == decodedParams.permitParams.s.length, + assets.length == decodedParams.permitParams.s.length && + assets.length == decodedParams.useEthPath.length, 'INCONSISTENT_PARAMS' ); @@ -90,13 +92,22 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter { decodedParams.permitParams.v[i], decodedParams.permitParams.r[i], decodedParams.permitParams.s[i] - ) + ), + decodedParams.useEthPath[i] ); } return true; } + struct SwapAndDepositLocalVars { + uint256 i; + uint256 aTokenInitiatorBalance; + uint256 amountToSwap; + uint256 receivedAmount; + address aToken; + } + /** * @dev Swaps an amount of an asset to another and deposits the new asset amount on behalf of the user without using * a flash loan. This method can be used when the temporary transfer of the collateral asset to this contract @@ -113,13 +124,15 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter { * uint8 v param for the permit signature * bytes32 r param for the permit signature * bytes32 s param for the permit signature + * @param useEthPath true if the swap needs to occur using ETH in the routing, false otherwise */ function swapAndDeposit( address[] calldata assetToSwapFromList, address[] calldata assetToSwapToList, uint256[] calldata amountToSwapList, uint256[] calldata minAmountsToReceive, - PermitSignature[] calldata permitParams + PermitSignature[] calldata permitParams, + bool[] calldata useEthPath ) external { require( assetToSwapFromList.length == assetToSwapToList.length && @@ -129,26 +142,29 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter { 'INCONSISTENT_PARAMS' ); - for (uint256 i = 0; i < assetToSwapFromList.length; i++) { - address aToken = _getReserveData(assetToSwapFromList[i]).aTokenAddress; + SwapAndDepositLocalVars memory vars; - uint256 aTokenInitiatorBalance = IERC20(aToken).balanceOf(msg.sender); - uint256 amountToSwap = - amountToSwapList[i] > aTokenInitiatorBalance ? aTokenInitiatorBalance : amountToSwapList[i]; + for (vars.i = 0; vars.i < assetToSwapFromList.length; vars.i++) { + vars.aToken = _getReserveData(assetToSwapFromList[vars.i]).aTokenAddress; - _pullAToken(assetToSwapFromList[i], aToken, msg.sender, amountToSwap, permitParams[i]); + vars.aTokenInitiatorBalance = IERC20(vars.aToken).balanceOf(msg.sender); + vars.amountToSwap = + amountToSwapList[vars.i] > vars.aTokenInitiatorBalance ? vars.aTokenInitiatorBalance : amountToSwapList[vars.i]; - uint256 receivedAmount = + _pullAToken(assetToSwapFromList[vars.i], vars.aToken, msg.sender, vars.amountToSwap, permitParams[vars.i]); + + vars.receivedAmount = _swapExactTokensForTokens( - assetToSwapFromList[i], - assetToSwapToList[i], - amountToSwap, - minAmountsToReceive[i] + assetToSwapFromList[vars.i], + assetToSwapToList[vars.i], + vars.amountToSwap, + minAmountsToReceive[vars.i], + useEthPath[vars.i] ); // Deposit new reserve - IERC20(assetToSwapToList[i]).approve(address(LENDING_POOL), receivedAmount); - LENDING_POOL.deposit(assetToSwapToList[i], receivedAmount, msg.sender, 0); + IERC20(assetToSwapToList[vars.i]).approve(address(LENDING_POOL), vars.receivedAmount); + LENDING_POOL.deposit(assetToSwapToList[vars.i], vars.receivedAmount, msg.sender, 0); } } @@ -161,6 +177,7 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter { * @param minAmountToReceive Min amount to be received from the swap * @param swapAllBalance Flag indicating if all the user balance should be swapped * @param permitSignature List of struct containing the permit signature + * @param useEthPath true if the swap needs to occur using ETH in the routing, false otherwise */ function _swapLiquidity( address assetFrom, @@ -170,7 +187,8 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter { address initiator, uint256 minAmountToReceive, bool swapAllBalance, - PermitSignature memory permitSignature + PermitSignature memory permitSignature, + bool useEthPath ) internal { address aToken = _getReserveData(assetFrom).aTokenAddress; @@ -181,7 +199,7 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter { : amount; uint256 receivedAmount = - _swapExactTokensForTokens(assetFrom, assetTo, amountToSwap, minAmountToReceive); + _swapExactTokensForTokens(assetFrom, assetTo, amountToSwap, minAmountToReceive, useEthPath); // Deposit new reserve IERC20(assetTo).approve(address(LENDING_POOL), receivedAmount); @@ -207,6 +225,7 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter { * uint8[] v List of v param for the permit signature * bytes32[] r List of r param for the permit signature * bytes32[] s List of s param for the permit signature + * bool[] useEthPath true if the swap needs to occur using ETH in the routing, false otherwise * @return SwapParams struct containing decoded params */ function _decodeParams(bytes memory params) internal pure returns (SwapParams memory) { @@ -218,11 +237,12 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter { uint256[] memory deadline, uint8[] memory v, bytes32[] memory r, - bytes32[] memory s + bytes32[] memory s, + bool[] memory useEthPath ) = abi.decode( params, - (address[], uint256[], bool[], uint256[], uint256[], uint8[], bytes32[], bytes32[]) + (address[], uint256[], bool[], uint256[], uint256[], uint8[], bytes32[], bytes32[], bool[]) ); return @@ -230,7 +250,8 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter { assetToSwapToList, minAmountsToReceive, swapAllBalance, - PermitParams(permitAmount, deadline, v, r, s) + PermitParams(permitAmount, deadline, v, r, s), + useEthPath ); } } diff --git a/contracts/adapters/UniswapRepayAdapter.sol b/contracts/adapters/UniswapRepayAdapter.sol index 2fa23d6d..9e725653 100644 --- a/contracts/adapters/UniswapRepayAdapter.sol +++ b/contracts/adapters/UniswapRepayAdapter.sol @@ -19,6 +19,7 @@ contract UniswapRepayAdapter is BaseUniswapAdapter { uint256 collateralAmount; uint256 rateMode; PermitSignature permitSignature; + bool useEthPath; } constructor(ILendingPoolAddressesProvider addressesProvider, IUniswapV2Router02 uniswapRouter) @@ -65,7 +66,8 @@ contract UniswapRepayAdapter is BaseUniswapAdapter { decodedParams.rateMode, initiator, premiums[0], - decodedParams.permitSignature + decodedParams.permitSignature, + decodedParams.useEthPath ); return true; @@ -82,6 +84,8 @@ contract UniswapRepayAdapter is BaseUniswapAdapter { * @param debtRepayAmount Amount of the debt to be repaid * @param debtRateMode Rate mode of the debt to be repaid * @param permitSignature struct containing the permit signature + * @param useEthPath struct containing the permit signature + */ function swapAndRepay( address collateralAsset, @@ -89,7 +93,8 @@ contract UniswapRepayAdapter is BaseUniswapAdapter { uint256 collateralAmount, uint256 debtRepayAmount, uint256 debtRateMode, - PermitSignature calldata permitSignature + PermitSignature calldata permitSignature, + bool useEthPath ) external { DataTypes.ReserveData memory collateralReserveData = _getReserveData(collateralAsset); DataTypes.ReserveData memory debtReserveData = _getReserveData(debtAsset); @@ -109,7 +114,7 @@ contract UniswapRepayAdapter is BaseUniswapAdapter { } // Get exact collateral needed for the swap to avoid leftovers - (uint256[] memory amounts,) = _getAmountsIn(collateralAsset, debtAsset, amountToRepay); + uint256[] memory amounts = _getAmountsIn(collateralAsset, debtAsset, amountToRepay, useEthPath); require(amounts[0] <= maxCollateralToSwap, 'slippage too high'); // Pull aTokens from user @@ -159,7 +164,8 @@ contract UniswapRepayAdapter is BaseUniswapAdapter { uint256 rateMode, address initiator, uint256 premium, - PermitSignature memory permitSignature + PermitSignature memory permitSignature, + bool useEthPath ) internal { DataTypes.ReserveData memory collateralReserveData = _getReserveData(collateralAsset); @@ -176,7 +182,7 @@ contract UniswapRepayAdapter is BaseUniswapAdapter { } uint256 neededForFlashLoanDebt = repaidAmount.add(premium); - (uint256[] memory amounts,) = _getAmountsIn(collateralAsset, debtAsset, neededForFlashLoanDebt); + uint256[] memory amounts = _getAmountsIn(collateralAsset, debtAsset, neededForFlashLoanDebt, useEthPath); require(amounts[0] <= maxCollateralToSwap, 'slippage too high'); // Pull aTokens from user @@ -227,15 +233,17 @@ contract UniswapRepayAdapter is BaseUniswapAdapter { uint256 deadline, uint8 v, bytes32 r, - bytes32 s - ) = abi.decode(params, (address, uint256, uint256, uint256, uint256, uint8, bytes32, bytes32)); + bytes32 s, + bool useEthPath + ) = abi.decode(params, (address, uint256, uint256, uint256, uint256, uint8, bytes32, bytes32, bool)); return RepayParams( collateralAsset, collateralAmount, rateMode, - PermitSignature(permitAmount, deadline, v, r, s) + PermitSignature(permitAmount, deadline, v, r, s), + useEthPath ); } } From 41efe9e811dcdc2de8ce8c9f348a9aaf0205f33d Mon Sep 17 00:00:00 2001 From: andyk Date: Wed, 2 Dec 2020 19:56:38 +0400 Subject: [PATCH 42/62] add WETH to constructor of swap adapters --- contracts/adapters/BaseUniswapAdapter.sol | 14 ++++--- .../adapters/UniswapLiquiditySwapAdapter.sol | 37 +++++++++++-------- contracts/adapters/UniswapRepayAdapter.sol | 21 +++++++---- .../interfaces/IBaseUniswapAdapter.sol | 2 + hardhat.config.ts | 4 +- helpers/contracts-deployments.ts | 7 ++-- package.json | 8 ++-- .../deploy-UniswapLiquiditySwapAdapter.ts | 7 ++-- .../deployments/deploy-UniswapRepayAdapter.ts | 16 ++++---- 9 files changed, 70 insertions(+), 46 deletions(-) diff --git a/contracts/adapters/BaseUniswapAdapter.sol b/contracts/adapters/BaseUniswapAdapter.sol index 5f0851a7..a10cf825 100644 --- a/contracts/adapters/BaseUniswapAdapter.sol +++ b/contracts/adapters/BaseUniswapAdapter.sol @@ -32,17 +32,21 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt // USD oracle asset address address public constant override USD_ADDRESS = 0x10F7Fc1F91Ba351f9C629c5947AD69bD03C05b96; - address public constant WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; + // address public constant WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; mainnet + // address public constant WETH_ADDRESS = 0xd0a1e359811322d97991e03f863a0c30c2cf029c; kovan + address public immutable override WETH_ADDRESS; IPriceOracleGetter public immutable override ORACLE; IUniswapV2Router02 public immutable override UNISWAP_ROUTER; - constructor(ILendingPoolAddressesProvider addressesProvider, IUniswapV2Router02 uniswapRouter) - public - FlashLoanReceiverBase(addressesProvider) - { + constructor( + ILendingPoolAddressesProvider addressesProvider, + IUniswapV2Router02 uniswapRouter, + address wethAddress + ) public FlashLoanReceiverBase(addressesProvider) { ORACLE = IPriceOracleGetter(addressesProvider.getPriceOracle()); UNISWAP_ROUTER = uniswapRouter; + WETH_ADDRESS = wethAddress; } /** diff --git a/contracts/adapters/UniswapLiquiditySwapAdapter.sol b/contracts/adapters/UniswapLiquiditySwapAdapter.sol index b4d01550..aaf13406 100644 --- a/contracts/adapters/UniswapLiquiditySwapAdapter.sol +++ b/contracts/adapters/UniswapLiquiditySwapAdapter.sol @@ -29,10 +29,11 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter { bool[] useEthPath; } - constructor(ILendingPoolAddressesProvider addressesProvider, IUniswapV2Router02 uniswapRouter) - public - BaseUniswapAdapter(addressesProvider, uniswapRouter) - {} + constructor( + ILendingPoolAddressesProvider addressesProvider, + IUniswapV2Router02 uniswapRouter, + address wethAddress + ) public BaseUniswapAdapter(addressesProvider, uniswapRouter, wethAddress) {} /** * @dev Swaps the received reserve amount from the flash loan into the asset specified in the params. @@ -148,19 +149,25 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter { vars.aToken = _getReserveData(assetToSwapFromList[vars.i]).aTokenAddress; vars.aTokenInitiatorBalance = IERC20(vars.aToken).balanceOf(msg.sender); - vars.amountToSwap = - amountToSwapList[vars.i] > vars.aTokenInitiatorBalance ? vars.aTokenInitiatorBalance : amountToSwapList[vars.i]; + vars.amountToSwap = amountToSwapList[vars.i] > vars.aTokenInitiatorBalance + ? vars.aTokenInitiatorBalance + : amountToSwapList[vars.i]; - _pullAToken(assetToSwapFromList[vars.i], vars.aToken, msg.sender, vars.amountToSwap, permitParams[vars.i]); + _pullAToken( + assetToSwapFromList[vars.i], + vars.aToken, + msg.sender, + vars.amountToSwap, + permitParams[vars.i] + ); - vars.receivedAmount = - _swapExactTokensForTokens( - assetToSwapFromList[vars.i], - assetToSwapToList[vars.i], - vars.amountToSwap, - minAmountsToReceive[vars.i], - useEthPath[vars.i] - ); + vars.receivedAmount = _swapExactTokensForTokens( + assetToSwapFromList[vars.i], + assetToSwapToList[vars.i], + vars.amountToSwap, + minAmountsToReceive[vars.i], + useEthPath[vars.i] + ); // Deposit new reserve IERC20(assetToSwapToList[vars.i]).approve(address(LENDING_POOL), vars.receivedAmount); diff --git a/contracts/adapters/UniswapRepayAdapter.sol b/contracts/adapters/UniswapRepayAdapter.sol index 9e725653..b5ce12bb 100644 --- a/contracts/adapters/UniswapRepayAdapter.sol +++ b/contracts/adapters/UniswapRepayAdapter.sol @@ -22,10 +22,11 @@ contract UniswapRepayAdapter is BaseUniswapAdapter { bool useEthPath; } - constructor(ILendingPoolAddressesProvider addressesProvider, IUniswapV2Router02 uniswapRouter) - public - BaseUniswapAdapter(addressesProvider, uniswapRouter) - {} + constructor( + ILendingPoolAddressesProvider addressesProvider, + IUniswapV2Router02 uniswapRouter, + address wethAddress + ) public BaseUniswapAdapter(addressesProvider, uniswapRouter, wethAddress) {} /** * @dev Uses the received funds from the flash loan to repay a debt on the protocol on behalf of the user. Then pulls @@ -114,7 +115,8 @@ contract UniswapRepayAdapter is BaseUniswapAdapter { } // Get exact collateral needed for the swap to avoid leftovers - uint256[] memory amounts = _getAmountsIn(collateralAsset, debtAsset, amountToRepay, useEthPath); + uint256[] memory amounts = + _getAmountsIn(collateralAsset, debtAsset, amountToRepay, useEthPath); require(amounts[0] <= maxCollateralToSwap, 'slippage too high'); // Pull aTokens from user @@ -182,7 +184,8 @@ contract UniswapRepayAdapter is BaseUniswapAdapter { } uint256 neededForFlashLoanDebt = repaidAmount.add(premium); - uint256[] memory amounts = _getAmountsIn(collateralAsset, debtAsset, neededForFlashLoanDebt, useEthPath); + uint256[] memory amounts = + _getAmountsIn(collateralAsset, debtAsset, neededForFlashLoanDebt, useEthPath); require(amounts[0] <= maxCollateralToSwap, 'slippage too high'); // Pull aTokens from user @@ -235,7 +238,11 @@ contract UniswapRepayAdapter is BaseUniswapAdapter { bytes32 r, bytes32 s, bool useEthPath - ) = abi.decode(params, (address, uint256, uint256, uint256, uint256, uint8, bytes32, bytes32, bool)); + ) = + abi.decode( + params, + (address, uint256, uint256, uint256, uint256, uint8, bytes32, bytes32, bool) + ); return RepayParams( diff --git a/contracts/adapters/interfaces/IBaseUniswapAdapter.sol b/contracts/adapters/interfaces/IBaseUniswapAdapter.sol index 540f9e46..82997b74 100644 --- a/contracts/adapters/interfaces/IBaseUniswapAdapter.sol +++ b/contracts/adapters/interfaces/IBaseUniswapAdapter.sol @@ -24,6 +24,8 @@ interface IBaseUniswapAdapter { address[] path; } + function WETH_ADDRESS() external returns (address); + function MAX_SLIPPAGE_PERCENT() external returns (uint256); function FLASHLOAN_PREMIUM_TOTAL() external returns (uint256); diff --git a/hardhat.config.ts b/hardhat.config.ts index f4dcaf69..74297504 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -6,6 +6,8 @@ import { accounts } from './test-wallets.js'; import { eEthereumNetwork } from './helpers/types'; import { BUIDLEREVM_CHAINID, COVERAGE_CHAINID } from './helpers/buidler-constants'; +require('dotenv').config(); + import '@nomiclabs/hardhat-ethers'; import '@nomiclabs/hardhat-waffle'; import 'temp-hardhat-etherscan'; @@ -45,7 +47,7 @@ const getCommonNetworkConfig = (networkName: eEthereumNetwork, networkId: number ? `https://eth-${ networkName === 'main' ? 'mainnet' : networkName }.alchemyapi.io/v2/${ALCHEMY_KEY}` - : `https://${networkName}.infura.io/v3/${INFURA_KEY}`, + : `https://${networkName === 'main' ? 'mainnet' : networkName}.infura.io/v3/${INFURA_KEY}`, hardfork: HARDFORK, blockGasLimit: DEFAULT_BLOCK_GAS_LIMIT, gasMultiplier: DEFAULT_GAS_MUL, diff --git a/helpers/contracts-deployments.ts b/helpers/contracts-deployments.ts index 7f91cf6a..4d1f0617 100644 --- a/helpers/contracts-deployments.ts +++ b/helpers/contracts-deployments.ts @@ -321,7 +321,7 @@ export const deployVariableDebtToken = async ( ); export const deployGenericAToken = async ( - [poolAddress, underlyingAssetAddress, treasuryAddress, name, symbol,incentivesController]: [ + [poolAddress, underlyingAssetAddress, treasuryAddress, name, symbol, incentivesController]: [ tEthereumAddress, tEthereumAddress, tEthereumAddress, @@ -338,7 +338,6 @@ export const deployGenericAToken = async ( string, tEthereumAddress, tEthereumAddress - ] = [poolAddress, underlyingAssetAddress, treasuryAddress, name, symbol, incentivesController]; return withSaveAndVerify( await new ATokenFactory(await getFirstSigner()).deploy(...args), @@ -506,7 +505,7 @@ export const deployMockUniswapRouter = async (verify?: boolean) => ); export const deployUniswapLiquiditySwapAdapter = async ( - args: [tEthereumAddress, tEthereumAddress], + args: [tEthereumAddress, tEthereumAddress, tEthereumAddress], verify?: boolean ) => withSaveAndVerify( @@ -517,7 +516,7 @@ export const deployUniswapLiquiditySwapAdapter = async ( ); export const deployUniswapRepayAdapter = async ( - args: [tEthereumAddress, tEthereumAddress], + args: [tEthereumAddress, tEthereumAddress, tEthereumAddress], verify?: boolean ) => withSaveAndVerify( diff --git a/package.json b/package.json index 4c8fdd95..5d9bfc75 100644 --- a/package.json +++ b/package.json @@ -43,10 +43,10 @@ "print-contracts:main": "npm run hardhat:main -- print-contracts", "print-contracts:ropsten": "npm run hardhat:main -- print-contracts", "dev:deployUIProvider": "npm run hardhat:kovan deploy-UiPoolDataProvider", - "dev:deployUniswapRepayAdapter": "hardhat --network kovan deploy-UniswapRepayAdapter --provider 0x88757f2f99175387aB4C6a4b3067c77A695b0349 --router 0xfcd87315f0e4067070ade8682fcdbc3006631441", - "dev:UniswapLiquiditySwapAdapter": "hardhat --network kovan deploy-UniswapLiquiditySwapAdapter --provider 0x88757f2f99175387aB4C6a4b3067c77A695b0349 --router 0xfcd87315f0e4067070ade8682fcdbc3006631441", - "main:deployUniswapRepayAdapter": "hardhat --network main deploy-UniswapRepayAdapter --provider 0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5 --router 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D", - "main:UniswapLiquiditySwapAdapter": "hardhat --network main deploy-UniswapLiquiditySwapAdapter --provider 0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5 --router 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D", + "dev:deployUniswapRepayAdapter": "hardhat --network kovan deploy-UniswapRepayAdapter --provider 0x88757f2f99175387aB4C6a4b3067c77A695b0349 --router 0xfcd87315f0e4067070ade8682fcdbc3006631441 --weth 0xd0a1e359811322d97991e03f863a0c30c2cf029c", + "dev:UniswapLiquiditySwapAdapter": "hardhat --network kovan deploy-UniswapLiquiditySwapAdapter --provider 0x88757f2f99175387aB4C6a4b3067c77A695b0349 --router 0xfcd87315f0e4067070ade8682fcdbc3006631441 --weth 0xd0a1e359811322d97991e03f863a0c30c2cf029c", + "main:deployUniswapRepayAdapter": "hardhat --network main deploy-UniswapRepayAdapter --provider 0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5 --router 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D --weth 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "main:UniswapLiquiditySwapAdapter": "hardhat --network main deploy-UniswapLiquiditySwapAdapter --provider 0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5 --router 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D --weth 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", "kovan:verify": "npm run hardhat:kovan verify:general -- --all --pool Aave", "ropsten:verify": "npm run hardhat:ropsten verify:general -- --all --pool Aave", "mainnet:verify": "npm run hardhat:main verify:general -- --all --pool Aave", diff --git a/tasks/deployments/deploy-UniswapLiquiditySwapAdapter.ts b/tasks/deployments/deploy-UniswapLiquiditySwapAdapter.ts index af9aaa04..a41cd975 100644 --- a/tasks/deployments/deploy-UniswapLiquiditySwapAdapter.ts +++ b/tasks/deployments/deploy-UniswapLiquiditySwapAdapter.ts @@ -9,8 +9,9 @@ const CONTRACT_NAME = 'UniswapLiquiditySwapAdapter'; task(`deploy-${CONTRACT_NAME}`, `Deploys the ${CONTRACT_NAME} contract`) .addParam('provider', 'Address of the LendingPoolAddressesProvider') .addParam('router', 'Address of the uniswap router') + .addParam('weth', 'Address of the weth token') .addFlag('verify', `Verify ${CONTRACT_NAME} contract via Etherscan API.`) - .setAction(async ({ provider, router, verify }, localBRE) => { + .setAction(async ({ provider, router, weth, verify }, localBRE) => { await localBRE.run('set-DRE'); if (!localBRE.network.config.chainId) { @@ -25,10 +26,10 @@ task(`deploy-${CONTRACT_NAME}`, `Deploys the ${CONTRACT_NAME} contract`) */ const uniswapRepayAdapter = await new UniswapLiquiditySwapAdapterFactory( await getFirstSigner() - ).deploy(provider, router); + ).deploy(provider, router, weth); await uniswapRepayAdapter.deployTransaction.wait(); console.log(`${CONTRACT_NAME}.address`, uniswapRepayAdapter.address); - await verifyContract(uniswapRepayAdapter.address, [provider, router]); + await verifyContract(uniswapRepayAdapter.address, [provider, router, weth]); console.log(`\tFinished ${CONTRACT_NAME} proxy and implementation deployment`); }); diff --git a/tasks/deployments/deploy-UniswapRepayAdapter.ts b/tasks/deployments/deploy-UniswapRepayAdapter.ts index 5d52a36c..58ba23a1 100644 --- a/tasks/deployments/deploy-UniswapRepayAdapter.ts +++ b/tasks/deployments/deploy-UniswapRepayAdapter.ts @@ -9,8 +9,9 @@ const CONTRACT_NAME = 'UniswapRepayAdapter'; task(`deploy-${CONTRACT_NAME}`, `Deploys the ${CONTRACT_NAME} contract`) .addParam('provider', 'Address of the LendingPoolAddressesProvider') .addParam('router', 'Address of the uniswap router') + .addParam('weth', 'Address of the weth token') .addFlag('verify', `Verify ${CONTRACT_NAME} contract via Etherscan API.`) - .setAction(async ({ provider, router, verify }, localBRE) => { + .setAction(async ({ provider, router, weth, verify }, localBRE) => { await localBRE.run('set-DRE'); if (!localBRE.network.config.chainId) { @@ -18,17 +19,18 @@ task(`deploy-${CONTRACT_NAME}`, `Deploys the ${CONTRACT_NAME} contract`) } console.log(`\n- ${CONTRACT_NAME} deployment`); - const args = [ - '0x88757f2f99175387aB4C6a4b3067c77A695b0349', // lending provider kovan address - '0xfcd87315f0e4067070ade8682fcdbc3006631441', // uniswap router address - ]; + // const args = [ + // '0x88757f2f99175387aB4C6a4b3067c77A695b0349', // lending provider kovan address + // '0xfcd87315f0e4067070ade8682fcdbc3006631441', // uniswap router address + // ]; const uniswapRepayAdapter = await new UniswapRepayAdapterFactory(await getFirstSigner()).deploy( provider, - router + router, + weth ); await uniswapRepayAdapter.deployTransaction.wait(); console.log(`${CONTRACT_NAME}.address`, uniswapRepayAdapter.address); - await verifyContract(uniswapRepayAdapter.address, [provider, router]); + await verifyContract(uniswapRepayAdapter.address, [provider, router, weth]); console.log( `\tFinished ${CONTRACT_NAME}${CONTRACT_NAME}lDataProvider proxy and implementation deployment` From 05dbfeccb3daa2a48f0707b17d6b30379e330453 Mon Sep 17 00:00:00 2001 From: eboado Date: Wed, 2 Dec 2020 17:38:17 +0100 Subject: [PATCH 43/62] - Fixed issue with variadic length of amounts depending on path --- contracts/adapters/BaseUniswapAdapter.sol | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/contracts/adapters/BaseUniswapAdapter.sol b/contracts/adapters/BaseUniswapAdapter.sol index a10cf825..f5f936c6 100644 --- a/contracts/adapters/BaseUniswapAdapter.sol +++ b/contracts/adapters/BaseUniswapAdapter.sol @@ -174,9 +174,9 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt block.timestamp ); - emit Swapped(assetToSwapFrom, assetToSwapTo, amounts[0], amounts[1]); + emit Swapped(assetToSwapFrom, assetToSwapTo, amounts[0], amounts[amounts.length-1]); - return amounts[1]; + return amounts[amounts.length-1]; } /** @@ -222,7 +222,7 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt block.timestamp ); - emit Swapped(assetToSwapFrom, assetToSwapTo, amounts[0], amounts[1]); + emit Swapped(assetToSwapFrom, assetToSwapTo, amounts[0], amounts[amounts.length-1]); return amounts[0]; } @@ -357,15 +357,15 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt uint256 outPerInPrice = finalAmountIn.mul(10**18).mul(10**reserveOutDecimals).div( - amounts[1].mul(10**reserveInDecimals) + amounts[amounts.length-1].mul(10**reserveInDecimals) ); return AmountCalc( - amounts[1], + amounts[amounts.length-1], outPerInPrice, _calcUsdValue(reserveIn, amountIn, reserveInDecimals), - _calcUsdValue(reserveOut, amounts[1], reserveOutDecimals), + _calcUsdValue(reserveOut, amounts[amounts.length-1], reserveOutDecimals), (pathWithWeth[0] != address(0) ? pathWithWeth : simplePath) ); } From 1fec2aa5d1dd1839bfe290ab8084186fa9810e1d Mon Sep 17 00:00:00 2001 From: eboado Date: Wed, 2 Dec 2020 17:59:38 +0100 Subject: [PATCH 44/62] - Added basic selection of optimal path --- contracts/adapters/BaseUniswapAdapter.sol | 27 ++++++++++++++--------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/contracts/adapters/BaseUniswapAdapter.sol b/contracts/adapters/BaseUniswapAdapter.sol index f5f936c6..b9cb639a 100644 --- a/contracts/adapters/BaseUniswapAdapter.sol +++ b/contracts/adapters/BaseUniswapAdapter.sol @@ -174,9 +174,9 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt block.timestamp ); - emit Swapped(assetToSwapFrom, assetToSwapTo, amounts[0], amounts[amounts.length-1]); + emit Swapped(assetToSwapFrom, assetToSwapTo, amounts[0], amounts[amounts.length - 1]); - return amounts[amounts.length-1]; + return amounts[amounts.length - 1]; } /** @@ -222,7 +222,7 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt block.timestamp ); - emit Swapped(assetToSwapFrom, assetToSwapTo, amounts[0], amounts[amounts.length-1]); + emit Swapped(assetToSwapFrom, assetToSwapTo, amounts[0], amounts[amounts.length - 1]); return amounts[0]; } @@ -338,35 +338,42 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt simplePath[0] = reserveIn; simplePath[1] = reserveOut; - uint256[] memory amounts; + uint256[] memory amountsWithoutWeth; + uint256[] memory amountsWithWeth; + address[] memory pathWithWeth = new address[](3); try UNISWAP_ROUTER.getAmountsOut(finalAmountIn, simplePath) returns ( uint256[] memory resultAmounts ) { - amounts = resultAmounts; + amountsWithoutWeth = resultAmounts; } catch { pathWithWeth[0] = reserveIn; pathWithWeth[1] = WETH_ADDRESS; pathWithWeth[2] = reserveOut; - amounts = UNISWAP_ROUTER.getAmountsOut(finalAmountIn, pathWithWeth); + amountsWithWeth = UNISWAP_ROUTER.getAmountsOut(finalAmountIn, pathWithWeth); } + uint256 bestAmountOut = + (amountsWithWeth.length > 0 && amountsWithWeth[2] > amountsWithoutWeth[1]) + ? amountsWithWeth[2] + : amountsWithoutWeth[1]; + uint256 reserveInDecimals = _getDecimals(reserveIn); uint256 reserveOutDecimals = _getDecimals(reserveOut); uint256 outPerInPrice = finalAmountIn.mul(10**18).mul(10**reserveOutDecimals).div( - amounts[amounts.length-1].mul(10**reserveInDecimals) + bestAmountOut.mul(10**reserveInDecimals) ); return AmountCalc( - amounts[amounts.length-1], + bestAmountOut, outPerInPrice, _calcUsdValue(reserveIn, amountIn, reserveInDecimals), - _calcUsdValue(reserveOut, amounts[amounts.length-1], reserveOutDecimals), - (pathWithWeth[0] != address(0) ? pathWithWeth : simplePath) + _calcUsdValue(reserveOut, bestAmountOut, reserveOutDecimals), + (bestAmountOut == amountsWithoutWeth[1]) ? simplePath : pathWithWeth ); } From ab5303bc7cfd66e806eadfae9c6173c6064412cc Mon Sep 17 00:00:00 2001 From: eboado Date: Wed, 2 Dec 2020 18:19:29 +0100 Subject: [PATCH 45/62] - Fixed calculation on _getAmountsOutData(). - Optimizing outcome of _getAmountsInAndPath(). --- contracts/adapters/BaseUniswapAdapter.sol | 43 +++++++++++++---------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/contracts/adapters/BaseUniswapAdapter.sol b/contracts/adapters/BaseUniswapAdapter.sol index b9cb639a..e1c8434f 100644 --- a/contracts/adapters/BaseUniswapAdapter.sol +++ b/contracts/adapters/BaseUniswapAdapter.sol @@ -342,22 +342,23 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt uint256[] memory amountsWithWeth; address[] memory pathWithWeth = new address[](3); + pathWithWeth[0] = reserveIn; + pathWithWeth[1] = WETH_ADDRESS; + pathWithWeth[2] = reserveOut; + amountsWithWeth = UNISWAP_ROUTER.getAmountsOut(finalAmountIn, pathWithWeth); + + uint256 bestAmountOut; try UNISWAP_ROUTER.getAmountsOut(finalAmountIn, simplePath) returns ( uint256[] memory resultAmounts ) { amountsWithoutWeth = resultAmounts; - } catch { - pathWithWeth[0] = reserveIn; - pathWithWeth[1] = WETH_ADDRESS; - pathWithWeth[2] = reserveOut; - amountsWithWeth = UNISWAP_ROUTER.getAmountsOut(finalAmountIn, pathWithWeth); - } - - uint256 bestAmountOut = - (amountsWithWeth.length > 0 && amountsWithWeth[2] > amountsWithoutWeth[1]) + bestAmountOut = (amountsWithWeth[2] > amountsWithoutWeth[1]) ? amountsWithWeth[2] : amountsWithoutWeth[1]; + } catch { + bestAmountOut = amountsWithWeth[2]; + } uint256 reserveInDecimals = _getDecimals(reserveIn); uint256 reserveOutDecimals = _getDecimals(reserveOut); @@ -433,20 +434,26 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt simplePath[0] = reserveIn; simplePath[1] = reserveOut; - uint256[] memory amounts; + uint256[] memory amountsWithoutWeth; + uint256[] memory amountsWithWeth; + address[] memory pathWithWeth = new address[](3); + pathWithWeth[0] = reserveIn; + pathWithWeth[1] = WETH_ADDRESS; + pathWithWeth[2] = reserveOut; + amountsWithWeth = UNISWAP_ROUTER.getAmountsIn(amountOut, pathWithWeth); + try UNISWAP_ROUTER.getAmountsIn(amountOut, simplePath) returns ( uint256[] memory resultAmounts ) { - amounts = resultAmounts; - return (amounts, simplePath); - } catch { - pathWithWeth[0] = reserveIn; - pathWithWeth[1] = WETH_ADDRESS; - pathWithWeth[2] = reserveOut; + amountsWithoutWeth = resultAmounts; - amounts = UNISWAP_ROUTER.getAmountsIn(amountOut, pathWithWeth); - return (amounts, pathWithWeth); + return + (amountsWithWeth[2] > amountsWithoutWeth[1]) + ? (amountsWithWeth, pathWithWeth) + : (amountsWithoutWeth, simplePath); + } catch { + return (amountsWithWeth, pathWithWeth); } } From 84629bca2eec7cf570711764e46c285ab1cfa3ab Mon Sep 17 00:00:00 2001 From: eboado Date: Thu, 3 Dec 2020 11:36:15 +0100 Subject: [PATCH 46/62] - Added fallback condition for price prediction, both In and Out --- contracts/adapters/BaseUniswapAdapter.sol | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/contracts/adapters/BaseUniswapAdapter.sol b/contracts/adapters/BaseUniswapAdapter.sol index e1c8434f..dc51bb3e 100644 --- a/contracts/adapters/BaseUniswapAdapter.sol +++ b/contracts/adapters/BaseUniswapAdapter.sol @@ -345,7 +345,14 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt pathWithWeth[0] = reserveIn; pathWithWeth[1] = WETH_ADDRESS; pathWithWeth[2] = reserveOut; - amountsWithWeth = UNISWAP_ROUTER.getAmountsOut(finalAmountIn, pathWithWeth); + + try UNISWAP_ROUTER.getAmountsOut(finalAmountIn, pathWithWeth) returns ( + uint256[] memory resultsWithWeth + ) { + amountsWithWeth = resultsWithWeth; + } catch { + amountsWithWeth = new uint256[](2); + } uint256 bestAmountOut; try UNISWAP_ROUTER.getAmountsOut(finalAmountIn, simplePath) returns ( @@ -374,7 +381,9 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt outPerInPrice, _calcUsdValue(reserveIn, amountIn, reserveInDecimals), _calcUsdValue(reserveOut, bestAmountOut, reserveOutDecimals), - (bestAmountOut == amountsWithoutWeth[1]) ? simplePath : pathWithWeth + (bestAmountOut == 0) ? new address[](2) : (bestAmountOut == amountsWithoutWeth[1]) + ? simplePath + : pathWithWeth ); } @@ -436,11 +445,19 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt uint256[] memory amountsWithoutWeth; uint256[] memory amountsWithWeth; - address[] memory pathWithWeth = new address[](3); pathWithWeth[0] = reserveIn; pathWithWeth[1] = WETH_ADDRESS; pathWithWeth[2] = reserveOut; + + try UNISWAP_ROUTER.getAmountsIn(amountOut, pathWithWeth) returns ( + uint256[] memory resultsWithWeth + ) { + amountsWithWeth = resultsWithWeth; + } catch { + return (new uint256[](2), new address[](2)); + } + amountsWithWeth = UNISWAP_ROUTER.getAmountsIn(amountOut, pathWithWeth); try UNISWAP_ROUTER.getAmountsIn(amountOut, simplePath) returns ( From 1916426ab1e0c63298932a7bac2eda421c3f7a12 Mon Sep 17 00:00:00 2001 From: eboado Date: Thu, 3 Dec 2020 12:13:06 +0100 Subject: [PATCH 47/62] - Added Ownable to BaseUniswapAdapter. --- contracts/adapters/BaseUniswapAdapter.sol | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/contracts/adapters/BaseUniswapAdapter.sol b/contracts/adapters/BaseUniswapAdapter.sol index dc51bb3e..9edffd43 100644 --- a/contracts/adapters/BaseUniswapAdapter.sol +++ b/contracts/adapters/BaseUniswapAdapter.sol @@ -7,6 +7,7 @@ import {SafeMath} from '../dependencies/openzeppelin/contracts/SafeMath.sol'; import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol'; import {IERC20Detailed} from '../dependencies/openzeppelin/contracts/IERC20Detailed.sol'; import {SafeERC20} from '../dependencies/openzeppelin/contracts/SafeERC20.sol'; +import {Ownable} from '../dependencies/openzeppelin/contracts/Ownable.sol'; import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol'; import {DataTypes} from '../protocol/libraries/types/DataTypes.sol'; import {IUniswapV2Router02} from '../interfaces/IUniswapV2Router02.sol'; @@ -20,7 +21,7 @@ import {IBaseUniswapAdapter} from './interfaces/IBaseUniswapAdapter.sol'; * @notice Implements the logic for performing assets swaps in Uniswap V2 * @author Aave **/ -abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapter { +abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapter, Ownable { using SafeMath for uint256; using PercentageMath for uint256; using SafeERC20 for IERC20; @@ -502,4 +503,13 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt return UNISWAP_ROUTER.getAmountsIn(amountOut, path); } + + /** + * @dev Emergency rescue for token stucked on this contract, as failsafe mechanism + * - Funds should never remain in this contract more time than during transactions + * - Only callable by the owner + **/ + function rescueTokens(IERC20 token) external onlyOwner { + token.transfer(owner(), token.balanceOf(address(this))); + } } From 5fdb4793337f64a1f5ee3724187cb9a871efc18a Mon Sep 17 00:00:00 2001 From: eboado Date: Thu, 3 Dec 2020 16:11:05 +0100 Subject: [PATCH 48/62] - Fixed _getAmountsInAndPath() --- contracts/adapters/BaseUniswapAdapter.sol | 2 -- 1 file changed, 2 deletions(-) diff --git a/contracts/adapters/BaseUniswapAdapter.sol b/contracts/adapters/BaseUniswapAdapter.sol index 9edffd43..dd551a23 100644 --- a/contracts/adapters/BaseUniswapAdapter.sol +++ b/contracts/adapters/BaseUniswapAdapter.sol @@ -459,8 +459,6 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt return (new uint256[](2), new address[](2)); } - amountsWithWeth = UNISWAP_ROUTER.getAmountsIn(amountOut, pathWithWeth); - try UNISWAP_ROUTER.getAmountsIn(amountOut, simplePath) returns ( uint256[] memory resultAmounts ) { From a84cfdabe2a4cbb10d91accc9b62d18afea1e3e0 Mon Sep 17 00:00:00 2001 From: eboado Date: Thu, 3 Dec 2020 16:24:57 +0100 Subject: [PATCH 49/62] - Fixed _getAmountsInAndPath() and _getAmountsOutData() --- contracts/adapters/BaseUniswapAdapter.sol | 45 ++++++++++++++--------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/contracts/adapters/BaseUniswapAdapter.sol b/contracts/adapters/BaseUniswapAdapter.sol index dd551a23..7d7beb8a 100644 --- a/contracts/adapters/BaseUniswapAdapter.sol +++ b/contracts/adapters/BaseUniswapAdapter.sol @@ -343,16 +343,20 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt uint256[] memory amountsWithWeth; address[] memory pathWithWeth = new address[](3); - pathWithWeth[0] = reserveIn; - pathWithWeth[1] = WETH_ADDRESS; - pathWithWeth[2] = reserveOut; + if (reserveIn != WETH_ADDRESS && reserveOut != WETH_ADDRESS) { + pathWithWeth[0] = reserveIn; + pathWithWeth[1] = WETH_ADDRESS; + pathWithWeth[2] = reserveOut; - try UNISWAP_ROUTER.getAmountsOut(finalAmountIn, pathWithWeth) returns ( - uint256[] memory resultsWithWeth - ) { - amountsWithWeth = resultsWithWeth; - } catch { - amountsWithWeth = new uint256[](2); + try UNISWAP_ROUTER.getAmountsOut(finalAmountIn, pathWithWeth) returns ( + uint256[] memory resultsWithWeth + ) { + amountsWithWeth = resultsWithWeth; + } catch { + amountsWithWeth = new uint256[](3); + } + } else { + amountsWithWeth = new uint256[](3); } uint256 bestAmountOut; @@ -447,16 +451,21 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt uint256[] memory amountsWithoutWeth; uint256[] memory amountsWithWeth; address[] memory pathWithWeth = new address[](3); - pathWithWeth[0] = reserveIn; - pathWithWeth[1] = WETH_ADDRESS; - pathWithWeth[2] = reserveOut; - try UNISWAP_ROUTER.getAmountsIn(amountOut, pathWithWeth) returns ( - uint256[] memory resultsWithWeth - ) { - amountsWithWeth = resultsWithWeth; - } catch { - return (new uint256[](2), new address[](2)); + if (reserveIn != WETH_ADDRESS && reserveOut != WETH_ADDRESS) { + pathWithWeth[0] = reserveIn; + pathWithWeth[1] = WETH_ADDRESS; + pathWithWeth[2] = reserveOut; + + try UNISWAP_ROUTER.getAmountsIn(amountOut, pathWithWeth) returns ( + uint256[] memory resultsWithWeth + ) { + amountsWithWeth = resultsWithWeth; + } catch { + return (new uint256[](2), new address[](2)); + } + } else { + amountsWithWeth = new uint256[](3); } try UNISWAP_ROUTER.getAmountsIn(amountOut, simplePath) returns ( From c7bd9d251149a46a829aabb43703ff0d2108c12a Mon Sep 17 00:00:00 2001 From: eboado Date: Thu, 3 Dec 2020 16:35:05 +0100 Subject: [PATCH 50/62] - Fix _getAmountsInAndPath() --- contracts/adapters/BaseUniswapAdapter.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/adapters/BaseUniswapAdapter.sol b/contracts/adapters/BaseUniswapAdapter.sol index 7d7beb8a..b8b41dca 100644 --- a/contracts/adapters/BaseUniswapAdapter.sol +++ b/contracts/adapters/BaseUniswapAdapter.sol @@ -462,7 +462,7 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt ) { amountsWithWeth = resultsWithWeth; } catch { - return (new uint256[](2), new address[](2)); + amountsWithWeth = new uint256[](3); } } else { amountsWithWeth = new uint256[](3); From eadaf066f2cac80767699d3cae1391776d4eba47 Mon Sep 17 00:00:00 2001 From: eboado Date: Thu, 3 Dec 2020 16:51:38 +0100 Subject: [PATCH 51/62] - Fixed _getAmountsOutData() --- contracts/adapters/BaseUniswapAdapter.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/contracts/adapters/BaseUniswapAdapter.sol b/contracts/adapters/BaseUniswapAdapter.sol index b8b41dca..70d4beed 100644 --- a/contracts/adapters/BaseUniswapAdapter.sol +++ b/contracts/adapters/BaseUniswapAdapter.sol @@ -369,6 +369,7 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt ? amountsWithWeth[2] : amountsWithoutWeth[1]; } catch { + amountsWithoutWeth = new uint256[](2); bestAmountOut = amountsWithWeth[2]; } From 3d2a8777704f60c58f3f19fbd78577216f79a680 Mon Sep 17 00:00:00 2001 From: andyk Date: Wed, 9 Dec 2020 13:29:25 +0400 Subject: [PATCH 52/62] add withFlash flage to getAmountIn/Out --- contracts/adapters/BaseUniswapAdapter.sol | 79 +++++++++++-------- .../interfaces/IBaseUniswapAdapter.sol | 6 +- .../deployments/deploy-UniswapRepayAdapter.ts | 4 - 3 files changed, 50 insertions(+), 39 deletions(-) diff --git a/contracts/adapters/BaseUniswapAdapter.sol b/contracts/adapters/BaseUniswapAdapter.sol index 70d4beed..5a163233 100644 --- a/contracts/adapters/BaseUniswapAdapter.sol +++ b/contracts/adapters/BaseUniswapAdapter.sol @@ -63,7 +63,8 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt function getAmountsOut( uint256 amountIn, address reserveIn, - address reserveOut + address reserveOut, + bool withFlash ) external view @@ -76,7 +77,7 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt address[] memory ) { - AmountCalc memory results = _getAmountsOutData(reserveIn, reserveOut, amountIn); + AmountCalc memory results = _getAmountsOutData(reserveIn, reserveOut, amountIn, withFlash); return ( results.calculatedAmount, @@ -100,7 +101,8 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt function getAmountsIn( uint256 amountOut, address reserveIn, - address reserveOut + address reserveOut, + bool withFlash ) external view @@ -113,7 +115,7 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt address[] memory ) { - AmountCalc memory results = _getAmountsInData(reserveIn, reserveOut, amountOut); + AmountCalc memory results = _getAmountsInData(reserveIn, reserveOut, amountOut, withFlash); return ( results.calculatedAmount, @@ -316,6 +318,14 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt return amount.mul(reservePrice).div(10**decimals).mul(ethUsdPrice).div(10**18); } + struct AmountOutVars { + uint256 finalAmountIn; + address[] simplePath; + uint256[] amountsWithoutWeth; + uint256[] amountsWithWeth; + address[] pathWithWeth; + } + /** * @dev Given an input asset amount, returns the maximum output amount of the other asset * @param reserveIn Address of the asset to be swap from @@ -330,54 +340,55 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt function _getAmountsOutData( address reserveIn, address reserveOut, - uint256 amountIn + uint256 amountIn, + bool withFlash ) internal view returns (AmountCalc memory) { + AmountOutVars memory vars; // Subtract flash loan fee - uint256 finalAmountIn = amountIn.sub(amountIn.mul(FLASHLOAN_PREMIUM_TOTAL).div(10000)); + vars.finalAmountIn = amountIn.sub( + withFlash ? amountIn.mul(FLASHLOAN_PREMIUM_TOTAL).div(10000) : 0 + ); - address[] memory simplePath = new address[](2); - simplePath[0] = reserveIn; - simplePath[1] = reserveOut; + vars.simplePath = new address[](2); + vars.simplePath[0] = reserveIn; + vars.simplePath[1] = reserveOut; - uint256[] memory amountsWithoutWeth; - uint256[] memory amountsWithWeth; - - address[] memory pathWithWeth = new address[](3); + vars.pathWithWeth = new address[](3); if (reserveIn != WETH_ADDRESS && reserveOut != WETH_ADDRESS) { - pathWithWeth[0] = reserveIn; - pathWithWeth[1] = WETH_ADDRESS; - pathWithWeth[2] = reserveOut; + vars.pathWithWeth[0] = reserveIn; + vars.pathWithWeth[1] = WETH_ADDRESS; + vars.pathWithWeth[2] = reserveOut; - try UNISWAP_ROUTER.getAmountsOut(finalAmountIn, pathWithWeth) returns ( + try UNISWAP_ROUTER.getAmountsOut(vars.finalAmountIn, vars.pathWithWeth) returns ( uint256[] memory resultsWithWeth ) { - amountsWithWeth = resultsWithWeth; + vars.amountsWithWeth = resultsWithWeth; } catch { - amountsWithWeth = new uint256[](3); + vars.amountsWithWeth = new uint256[](3); } } else { - amountsWithWeth = new uint256[](3); + vars.amountsWithWeth = new uint256[](3); } uint256 bestAmountOut; - try UNISWAP_ROUTER.getAmountsOut(finalAmountIn, simplePath) returns ( + try UNISWAP_ROUTER.getAmountsOut(vars.finalAmountIn, vars.simplePath) returns ( uint256[] memory resultAmounts ) { - amountsWithoutWeth = resultAmounts; + vars.amountsWithoutWeth = resultAmounts; - bestAmountOut = (amountsWithWeth[2] > amountsWithoutWeth[1]) - ? amountsWithWeth[2] - : amountsWithoutWeth[1]; + bestAmountOut = (vars.amountsWithWeth[2] > vars.amountsWithoutWeth[1]) + ? vars.amountsWithWeth[2] + : vars.amountsWithoutWeth[1]; } catch { - amountsWithoutWeth = new uint256[](2); - bestAmountOut = amountsWithWeth[2]; + vars.amountsWithoutWeth = new uint256[](2); + bestAmountOut = vars.amountsWithWeth[2]; } uint256 reserveInDecimals = _getDecimals(reserveIn); uint256 reserveOutDecimals = _getDecimals(reserveOut); uint256 outPerInPrice = - finalAmountIn.mul(10**18).mul(10**reserveOutDecimals).div( + vars.finalAmountIn.mul(10**18).mul(10**reserveOutDecimals).div( bestAmountOut.mul(10**reserveInDecimals) ); @@ -387,9 +398,9 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt outPerInPrice, _calcUsdValue(reserveIn, amountIn, reserveInDecimals), _calcUsdValue(reserveOut, bestAmountOut, reserveOutDecimals), - (bestAmountOut == 0) ? new address[](2) : (bestAmountOut == amountsWithoutWeth[1]) - ? simplePath - : pathWithWeth + (bestAmountOut == 0) ? new address[](2) : (bestAmountOut == vars.amountsWithoutWeth[1]) + ? vars.simplePath + : vars.pathWithWeth ); } @@ -407,13 +418,15 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt function _getAmountsInData( address reserveIn, address reserveOut, - uint256 amountOut + uint256 amountOut, + bool withFlash ) internal view returns (AmountCalc memory) { (uint256[] memory amounts, address[] memory path) = _getAmountsInAndPath(reserveIn, reserveOut, amountOut); // Add flash loan fee - uint256 finalAmountIn = amounts[0].add(amounts[0].mul(FLASHLOAN_PREMIUM_TOTAL).div(10000)); + uint256 finalAmountIn = + amounts[0].add(withFlash ? amounts[0].mul(FLASHLOAN_PREMIUM_TOTAL).div(10000) : 0); uint256 reserveInDecimals = _getDecimals(reserveIn); uint256 reserveOutDecimals = _getDecimals(reserveOut); diff --git a/contracts/adapters/interfaces/IBaseUniswapAdapter.sol b/contracts/adapters/interfaces/IBaseUniswapAdapter.sol index 82997b74..e94727a2 100644 --- a/contracts/adapters/interfaces/IBaseUniswapAdapter.sol +++ b/contracts/adapters/interfaces/IBaseUniswapAdapter.sol @@ -50,7 +50,8 @@ interface IBaseUniswapAdapter { function getAmountsOut( uint256 amountIn, address reserveIn, - address reserveOut + address reserveOut, + bool withFlash ) external view @@ -76,7 +77,8 @@ interface IBaseUniswapAdapter { function getAmountsIn( uint256 amountOut, address reserveIn, - address reserveOut + address reserveOut, + bool withFlash ) external view diff --git a/tasks/deployments/deploy-UniswapRepayAdapter.ts b/tasks/deployments/deploy-UniswapRepayAdapter.ts index 58ba23a1..77550730 100644 --- a/tasks/deployments/deploy-UniswapRepayAdapter.ts +++ b/tasks/deployments/deploy-UniswapRepayAdapter.ts @@ -19,10 +19,6 @@ task(`deploy-${CONTRACT_NAME}`, `Deploys the ${CONTRACT_NAME} contract`) } console.log(`\n- ${CONTRACT_NAME} deployment`); - // const args = [ - // '0x88757f2f99175387aB4C6a4b3067c77A695b0349', // lending provider kovan address - // '0xfcd87315f0e4067070ade8682fcdbc3006631441', // uniswap router address - // ]; const uniswapRepayAdapter = await new UniswapRepayAdapterFactory(await getFirstSigner()).deploy( provider, router, From a3403631be1fcede3970352e99bd921aa026e8bc Mon Sep 17 00:00:00 2001 From: dhadrien Date: Mon, 21 Dec 2020 17:13:51 +0100 Subject: [PATCH 53/62] added task for deploying new asset --- hardhat.config.ts | 2 +- helpers/init-helpers.ts | 2 +- tasks/helpers/deploy-new-asset.ts | 91 +++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 tasks/helpers/deploy-new-asset.ts diff --git a/hardhat.config.ts b/hardhat.config.ts index 98935035..6db6a9f6 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -27,7 +27,7 @@ const MAINNET_FORK = process.env.MAINNET_FORK === 'true'; // Prevent to load scripts before compilation and typechain if (!SKIP_LOAD) { - ['misc', 'migrations', 'dev', 'full', 'verifications'].forEach((folder) => { + ['misc', 'migrations', 'dev', 'full', 'verifications', 'helpers'].forEach((folder) => { const tasksPath = path.join(__dirname, 'tasks', folder); fs.readdirSync(tasksPath) .filter((pth) => pth.includes('.ts')) diff --git a/helpers/init-helpers.ts b/helpers/init-helpers.ts index e80d2f91..55c372a7 100644 --- a/helpers/init-helpers.ts +++ b/helpers/init-helpers.ts @@ -26,7 +26,7 @@ import { import { ZERO_ADDRESS } from './constants'; import { isZeroAddress } from 'ethereumjs-util'; -const chooseATokenDeployment = (id: eContractid) => { +export const chooseATokenDeployment = (id: eContractid) => { switch (id) { case eContractid.AToken: return deployGenericAToken; diff --git a/tasks/helpers/deploy-new-asset.ts b/tasks/helpers/deploy-new-asset.ts new file mode 100644 index 00000000..b466c115 --- /dev/null +++ b/tasks/helpers/deploy-new-asset.ts @@ -0,0 +1,91 @@ +import { task } from 'hardhat/config'; +import { EthereumNetwork } from '../../helpers/types'; +import { getTreasuryAddress } from '../../helpers/configuration'; +import * as marketConfigs from '../../markets/aave'; +import * as reserveConfigs from '../../markets/aave/reservesConfigs'; +import { chooseATokenDeployment } from '../../helpers/init-helpers'; +import { getLendingPoolAddressesProvider } from './../../helpers/contracts-getters'; +import { + deployDefaultReserveInterestRateStrategy, + deployStableDebtToken, + deployVariableDebtToken, +} from './../../helpers/contracts-deployments'; +import { ZERO_ADDRESS } from './../../helpers/constants'; + +const isSymbolValid = (symbol: string, network: EthereumNetwork) => + Object.keys(reserveConfigs).includes('strategy' + symbol) && + marketConfigs.AaveConfig.ReserveAssets[network][symbol] && + marketConfigs.AaveConfig.ReservesConfig[symbol] === reserveConfigs['strategy' + symbol]; + +task('external:deploy-new-asset', 'Deploy A token, Debt Token, Risk Parameters') + .addParam('symbol', `Asset symbol, needs `) + .addFlag('verify', 'Verify contracts at Etherscan') + .setAction(async ({ verify, symbol }, localBRE) => { + if (!isSymbolValid(symbol, localBRE.network.name as EthereumNetwork)) { + throw new Error( + ` +WRONG RESERVE ASSET SETUP: + The symbol ${symbol} has no reserve Config and/or reserve Asset setup. + update /markets/aave/index.ts and add the asset address for ${localBRE.network.name} network + update /markets/aave/reservesConfigs.ts and add parameters for ${symbol} + ` + ); + } + const strategyParams = reserveConfigs['strategy' + symbol]; + const reserveAssetAddress = + marketConfigs.AaveConfig.ReserveAssets[localBRE.network.name][symbol]; + const deployCustomAToken = chooseATokenDeployment(strategyParams.aTokenImpl); + const addressProvider = await getLendingPoolAddressesProvider(); + const poolAddress = await addressProvider.getLendingPool(); + const treasuryAddress = await getTreasuryAddress(marketConfigs.AaveConfig); + const aToken = await deployCustomAToken( + [ + poolAddress, + reserveAssetAddress, + treasuryAddress, + `Aave interest bearing ${symbol}`, + `a${symbol}`, + ZERO_ADDRESS, + ], + verify + ); + const stableDebt = await deployStableDebtToken( + [ + poolAddress, + reserveAssetAddress, + `Aave stable debt bearing ${symbol}`, + `stableDebt${symbol}`, + ZERO_ADDRESS, + ], + verify + ); + const variableDebt = await deployVariableDebtToken( + [ + poolAddress, + reserveAssetAddress, + `Aave variable debt bearing ${symbol}`, + `variableDebt${symbol}`, + ZERO_ADDRESS, + ], + verify + ); + const rates = await deployDefaultReserveInterestRateStrategy( + [ + reserveAssetAddress, + strategyParams.optimalUtilizationRate, + strategyParams.baseVariableBorrowRate, + strategyParams.variableRateSlope1, + strategyParams.variableRateSlope2, + strategyParams.stableRateSlope1, + strategyParams.stableRateSlope2, + ], + verify + ); + console.log(` + New interest bearing asset deployed on ${localBRE.network}: + Interest bearing a${symbol} address: ${aToken.address} + Variable Debt variableDebt${symbol} address: ${variableDebt.address} + Stable Debt stableDebt${symbol} address: ${stableDebt.address} + Strategy Implementation for ${symbol} address: ${rates} + `); + }); From ec77c690b8dd588a2d5d1edd9a69660c68716058 Mon Sep 17 00:00:00 2001 From: dhadrien Date: Tue, 22 Dec 2020 10:15:50 +0100 Subject: [PATCH 54/62] Fixed script, working --- tasks/helpers/deploy-new-asset.ts | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/tasks/helpers/deploy-new-asset.ts b/tasks/helpers/deploy-new-asset.ts index b466c115..50795f64 100644 --- a/tasks/helpers/deploy-new-asset.ts +++ b/tasks/helpers/deploy-new-asset.ts @@ -10,32 +10,42 @@ import { deployStableDebtToken, deployVariableDebtToken, } from './../../helpers/contracts-deployments'; +import { setDRE } from '../../helpers/misc-utils'; import { ZERO_ADDRESS } from './../../helpers/constants'; +const LENDING_POOL_ADDRESS_PROVIDER = { + main: '0xb53c1a33016b2dc2ff3653530bff1848a515c8c5', + kovan: '0x652B2937Efd0B5beA1c8d54293FC1289672AFC6b', +}; + const isSymbolValid = (symbol: string, network: EthereumNetwork) => Object.keys(reserveConfigs).includes('strategy' + symbol) && marketConfigs.AaveConfig.ReserveAssets[network][symbol] && marketConfigs.AaveConfig.ReservesConfig[symbol] === reserveConfigs['strategy' + symbol]; -task('external:deploy-new-asset', 'Deploy A token, Debt Token, Risk Parameters') - .addParam('symbol', `Asset symbol, needs `) +task('external:deploy-new-asset', 'Deploy A token, Debt Tokens, Risk Parameters') + .addParam('symbol', `Asset symbol, needs to have configuration ready`) .addFlag('verify', 'Verify contracts at Etherscan') .setAction(async ({ verify, symbol }, localBRE) => { - if (!isSymbolValid(symbol, localBRE.network.name as EthereumNetwork)) { + const network = localBRE.network.name; + if (!isSymbolValid(symbol, network as EthereumNetwork)) { throw new Error( ` WRONG RESERVE ASSET SETUP: The symbol ${symbol} has no reserve Config and/or reserve Asset setup. - update /markets/aave/index.ts and add the asset address for ${localBRE.network.name} network + update /markets/aave/index.ts and add the asset address for ${network} network update /markets/aave/reservesConfigs.ts and add parameters for ${symbol} ` ); } + setDRE(localBRE); const strategyParams = reserveConfigs['strategy' + symbol]; const reserveAssetAddress = marketConfigs.AaveConfig.ReserveAssets[localBRE.network.name][symbol]; const deployCustomAToken = chooseATokenDeployment(strategyParams.aTokenImpl); - const addressProvider = await getLendingPoolAddressesProvider(); + const addressProvider = await getLendingPoolAddressesProvider( + LENDING_POOL_ADDRESS_PROVIDER[network] + ); const poolAddress = await addressProvider.getLendingPool(); const treasuryAddress = await getTreasuryAddress(marketConfigs.AaveConfig); const aToken = await deployCustomAToken( @@ -82,10 +92,10 @@ WRONG RESERVE ASSET SETUP: verify ); console.log(` - New interest bearing asset deployed on ${localBRE.network}: + New interest bearing asset deployed on ${network}: Interest bearing a${symbol} address: ${aToken.address} Variable Debt variableDebt${symbol} address: ${variableDebt.address} Stable Debt stableDebt${symbol} address: ${stableDebt.address} - Strategy Implementation for ${symbol} address: ${rates} + Strategy Implementation for ${symbol} address: ${rates.address} `); }); From 43df62a9c93400918017af4b2c28c8307de34dc1 Mon Sep 17 00:00:00 2001 From: David Racero Date: Thu, 7 Jan 2021 13:45:18 +0100 Subject: [PATCH 55/62] Fix README.md steps for mainnet fork. Added console fork mode. Fix infura subdomain in mainnet fork mode. --- README.md | 52 +- hardhat.config.ts | 9 +- package-lock.json | 4802 +++++++++++++++------------------------------ package.json | 1 + 4 files changed, 1596 insertions(+), 3268 deletions(-) diff --git a/README.md b/README.md index ae2e7b69..f4d8c1d0 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ ``` - .///. .///. //. .// `/////////////- - `++:++` .++:++` :++` `++: `++:......---.` - `/+: -+/` `++- :+/` /+/ `/+/ `++. - /+/ :+/ /+: /+/ `/+/ /+/` `++. - -::/++::` /+: -::/++::` `/+: `++: :++` `++/:::::::::. - -:+++::-` `/+: --++/---` `++- .++- -++. `++/:::::::::. - -++. .++- -++` .++. .++. .++- `++. - .++- -++. .++. -++. -++``++- `++. - `++: :++` .++- :++` :+//+: `++:----------` - -/: :/- -/: :/. ://: `/////////////- + .///. .///. //. .// `/////////////- + `++:++` .++:++` :++` `++: `++:......---.` + `/+: -+/` `++- :+/` /+/ `/+/ `++. + /+/ :+/ /+: /+/ `/+/ /+/` `++. + -::/++::` /+: -::/++::` `/+: `++: :++` `++/:::::::::. + -:+++::-` `/+: --++/---` `++- .++- -++. `++/:::::::::. + -++. .++- -++` .++. .++. .++- `++. + .++- -++. .++. -++. -++``++- `++. + `++: :++` .++- :++` :+//+: `++:----------` + -/: :/- -/: :/. ://: `/////////////- ``` # Aave Protocol v2 @@ -106,28 +106,34 @@ npm run aave:kovan:full:migration ### Mainnet fork deployment -You can deploy Aave Protocol v2 in a forked Mainnet chain using Hardhat built-in feature: +You can deploy Aave Protocol v2 in a forked Mainnet chain using Hardhat built-in fork feature: ``` -# In one terminal, run a hardhat note with mainnet fork enabled -MAINNET_FORK=true npx hardhat node +docker-compose run contracts-env npm run aave:fork:main +``` -# In another terminal, run docker-compose -docker-compose up +### Mainnet fork - Interact with Aave via Hardhat console -# Open another tab or terminal -docker-compose exec contracts-env bash +You can also deploy Aave into the Hardhat console in fork mode, to interact with the protocol inside the fork or for testing purposes. -# A new Bash terminal is prompted, connected to the container -npm run aave:fork:main +``` +# Run the console in fork mode +docker-compose run contracts-env npm run console:fork -# Contracts are now deployed at Hardhat node with Mainnet fork. +# Deploy the Aave protocol in fork mode +await run('aave:mainnet') -# You can interact with them via Hardhat console -MAINNET_FORK=true npx hardhat console # Or your custom Hardhat task -MAINNET_FORK=true npx hardhat your-custom-task +await run('your-custom-task'); +# After you initialize the Signers via 'set-DRE' task, you can import any TS/JS file +await run('set-DRE'); // Initialize signers + +# Import contract getters to retrieve an Ethers.js Contract instance +const contractGetters = require('./helpers/contracts-getters'); // Import a TS/JS file + +# Lending pool instance +const lendingPool = await contractGetters.getLendingPool("LendingPool address from 'aave:mainnet' task"); ``` ### Mainnet fork - Run the check list diff --git a/hardhat.config.ts b/hardhat.config.ts index 98935035..df6d0758 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -40,12 +40,11 @@ if (!SKIP_LOAD) { require(`${path.join(__dirname, 'tasks/misc')}/set-bre.ts`); const getCommonNetworkConfig = (networkName: eEthereumNetwork, networkId: number) => { + const net = networkName === 'main' ? 'mainnet' : networkName; return { url: ALCHEMY_KEY - ? `https://eth-${ - networkName === 'main' ? 'mainnet' : networkName - }.alchemyapi.io/v2/${ALCHEMY_KEY}` - : `https://${networkName}.infura.io/v3/${INFURA_KEY}`, + ? `https://eth-${net}.alchemyapi.io/v2/${ALCHEMY_KEY}` + : `https://${net}.infura.io/v3/${INFURA_KEY}`, hardfork: HARDFORK, blockGasLimit: DEFAULT_BLOCK_GAS_LIMIT, gasMultiplier: DEFAULT_GAS_MUL, @@ -65,7 +64,7 @@ const mainnetFork = MAINNET_FORK blockNumber: 11366117, url: ALCHEMY_KEY ? `https://eth-mainnet.alchemyapi.io/v2/${ALCHEMY_KEY}` - : `https://main.infura.io/v3/${INFURA_KEY}`, + : `https://mainnet.infura.io/v3/${INFURA_KEY}`, } : undefined; diff --git a/package-lock.json b/package-lock.json index 32738e30..dfdeb1a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2908,15 +2908,6 @@ "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", "dev": true }, - "bufferutil": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.2.tgz", - "integrity": "sha512-AtnG3W6M8B2n4xDQ5R+70EXvOpnXsFYg/AK2yTZd+HQ/oxAdz+GI+DvjmhBw3L0ole+LJ0ngqY4JMbDzkfNzhA==", - "dev": true, - "requires": { - "node-gyp-build": "^4.2.0" - } - }, "buidler-typechain": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/buidler-typechain/-/buidler-typechain-0.1.1.tgz", @@ -3111,33 +3102,6 @@ "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", "dev": true }, - "cids": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/cids/-/cids-0.7.5.tgz", - "integrity": "sha512-zT7mPeghoWAu+ppn8+BS1tQ5qGmbMfB4AregnQjA/qHY3GC1m1ptI9GkWNlgeu38r7CuRdXB47uY2XgAYt6QVA==", - "dev": true, - "optional": true, - "requires": { - "buffer": "^5.5.0", - "class-is": "^1.1.0", - "multibase": "~0.6.0", - "multicodec": "^1.0.0", - "multihashes": "~0.4.15" - }, - "dependencies": { - "multicodec": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-1.0.4.tgz", - "integrity": "sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg==", - "dev": true, - "optional": true, - "requires": { - "buffer": "^5.6.0", - "varint": "^5.0.0" - } - } - } - }, "cipher-base": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", @@ -3148,13 +3112,6 @@ "safe-buffer": "^5.0.1" } }, - "class-is": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/class-is/-/class-is-1.1.0.tgz", - "integrity": "sha512-rhjH9AG1fvabIDoGRVH587413LPjTZgmDF9fOFCbFJQV4yuocX1mHxxvXI4g3cGwbVY9wAYIoKlg1N79frJKQw==", - "dev": true, - "optional": true - }, "class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", @@ -3257,6 +3214,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, "requires": { "color-name": "1.1.3" } @@ -3264,7 +3222,8 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true }, "colors": { "version": "1.4.0", @@ -3398,18 +3357,6 @@ } } }, - "content-hash": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/content-hash/-/content-hash-2.5.2.tgz", - "integrity": "sha512-FvIQKy0S1JaWV10sMsA7TRx8bpU+pqPkhbsfvOJAdjRXvYxEckAwQWGwtRjiaJfh+E0DvcWUGqcdjwMGFjsSdw==", - "dev": true, - "optional": true, - "requires": { - "cids": "^0.7.1", - "multicodec": "^0.5.5", - "multihashes": "^0.4.15" - } - }, "content-type": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", @@ -6352,6 +6299,148 @@ "@ethersproject/strings": ">=5.0.0-beta.130" } }, + "@ethersproject/address": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.0.5.tgz", + "integrity": "sha512-DpkQ6rwk9jTefrRsJzEm6nhRiJd9pvhn1xN0rw5N/jswXG5r7BLk/GVA0mMAVWAsYfvi2xSc5L41FMox43RYEA==", + "dev": true, + "optional": true, + "requires": { + "@ethersproject/bignumber": "^5.0.7", + "@ethersproject/bytes": "^5.0.4", + "@ethersproject/keccak256": "^5.0.3", + "@ethersproject/logger": "^5.0.5", + "@ethersproject/rlp": "^5.0.3", + "bn.js": "^4.4.0" + } + }, + "@ethersproject/bignumber": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.0.8.tgz", + "integrity": "sha512-KXFVAFKS1jdTXYN8BE5Oj+ZfPMh28iRdFeNGBVT6cUFdtiPVqeXqc0ggvBqA3A1VoFFGgM7oAeaagA393aORHA==", + "dev": true, + "optional": true, + "requires": { + "@ethersproject/bytes": "^5.0.4", + "@ethersproject/logger": "^5.0.5", + "bn.js": "^4.4.0" + } + }, + "@ethersproject/bytes": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.0.5.tgz", + "integrity": "sha512-IEj9HpZB+ACS6cZ+QQMTqmu/cnUK2fYNE6ms/PVxjoBjoxc6HCraLpam1KuRvreMy0i523PLmjN8OYeikRdcUQ==", + "dev": true, + "optional": true, + "requires": { + "@ethersproject/logger": "^5.0.5" + } + }, + "@ethersproject/constants": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.0.5.tgz", + "integrity": "sha512-foaQVmxp2+ik9FrLUCtVrLZCj4M3Ibgkqvh+Xw/vFRSerkjVSYePApaVE5essxhoSlF1U9oXfWY09QI2AXtgKA==", + "dev": true, + "optional": true, + "requires": { + "@ethersproject/bignumber": "^5.0.7" + } + }, + "@ethersproject/hash": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.0.5.tgz", + "integrity": "sha512-GpI80/h2HDpfNKpCZoxQJCjOQloGnlD5hM1G+tZe8FQDJhEvFjJoPDuWv+NaYjJfOciKS2Axqc4Q4WamdLoUgg==", + "dev": true, + "optional": true, + "requires": { + "@ethersproject/bytes": "^5.0.4", + "@ethersproject/keccak256": "^5.0.3", + "@ethersproject/logger": "^5.0.5", + "@ethersproject/strings": "^5.0.4" + } + }, + "@ethersproject/keccak256": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.0.4.tgz", + "integrity": "sha512-GNpiOUm9PGUxFNqOxYKDQBM0u68bG9XC9iOulEQ8I0tOx/4qUpgVzvgXL6ugxr0RY554Gz/NQsVqknqPzUcxpQ==", + "dev": true, + "optional": true, + "requires": { + "@ethersproject/bytes": "^5.0.4", + "js-sha3": "0.5.7" + } + }, + "@ethersproject/logger": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.0.6.tgz", + "integrity": "sha512-FrX0Vnb3JZ1md/7GIZfmJ06XOAA8r3q9Uqt9O5orr4ZiksnbpXKlyDzQtlZ5Yv18RS8CAUbiKH9vwidJg1BPmQ==", + "dev": true, + "optional": true + }, + "@ethersproject/properties": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.0.4.tgz", + "integrity": "sha512-UdyX3GqBxFt15B0uSESdDNmhvEbK3ACdDXl2soshoPcneXuTswHDeA0LoPlnaZzhbgk4p6jqb4GMms5C26Qu6A==", + "dev": true, + "optional": true, + "requires": { + "@ethersproject/logger": "^5.0.5" + } + }, + "@ethersproject/rlp": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.0.4.tgz", + "integrity": "sha512-5qrrZad7VTjofxSsm7Zg/7Dr4ZOln4S2CqiDdOuTv6MBKnXj0CiBojXyuDy52M8O3wxH0CyE924hXWTDV1PQWQ==", + "dev": true, + "optional": true, + "requires": { + "@ethersproject/bytes": "^5.0.4", + "@ethersproject/logger": "^5.0.5" + } + }, + "@ethersproject/signing-key": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.0.5.tgz", + "integrity": "sha512-Z1wY7JC1HVO4CvQWY2TyTTuAr8xK3bJijZw1a9G92JEmKdv1j255R/0YLBBcFTl2J65LUjtXynNJ2GbArPGi5g==", + "dev": true, + "optional": true, + "requires": { + "@ethersproject/bytes": "^5.0.4", + "@ethersproject/logger": "^5.0.5", + "@ethersproject/properties": "^5.0.3", + "elliptic": "6.5.3" + } + }, + "@ethersproject/strings": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.0.5.tgz", + "integrity": "sha512-JED6WaIV00xM/gvj8vSnd+0VWtDYdidTmavFRCTQakqfz+4tDo6Jz5LHgG+dd45h7ah7ykCHW0C7ZXWEDROCXQ==", + "dev": true, + "optional": true, + "requires": { + "@ethersproject/bytes": "^5.0.4", + "@ethersproject/constants": "^5.0.4", + "@ethersproject/logger": "^5.0.5" + } + }, + "@ethersproject/transactions": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.0.6.tgz", + "integrity": "sha512-htsFhOD+NMBxx676A8ehSuwVV49iqpSB+CkjPZ02tpNew0K6p8g0CZ46Z1ZP946gIHAU80xQ0NACHYrjIUaCFA==", + "dev": true, + "optional": true, + "requires": { + "@ethersproject/address": "^5.0.4", + "@ethersproject/bignumber": "^5.0.7", + "@ethersproject/bytes": "^5.0.4", + "@ethersproject/constants": "^5.0.4", + "@ethersproject/keccak256": "^5.0.3", + "@ethersproject/logger": "^5.0.5", + "@ethersproject/properties": "^5.0.3", + "@ethersproject/rlp": "^5.0.3", + "@ethersproject/signing-key": "^5.0.4" + } + }, "@sindresorhus/is": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", @@ -6369,14 +6458,6 @@ "defer-to-connect": "^1.0.1" } }, - "@types/bignumber.js": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@types/bignumber.js/-/bignumber.js-5.0.0.tgz", - "integrity": "sha512-0DH7aPGCClywOFaxxjE6UwpN2kQYe9LwuDQMv+zYA97j5GkOMo8e66LYT+a8JYU7jfmUFRZLa9KycxHDsKXJCA==", - "requires": { - "bignumber.js": "*" - } - }, "@types/bn.js": { "version": "4.11.6", "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", @@ -6410,53 +6491,11 @@ "@types/node": "*" } }, - "@web3-js/scrypt-shim": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@web3-js/scrypt-shim/-/scrypt-shim-0.1.0.tgz", - "integrity": "sha512-ZtZeWCc/s0nMcdx/+rZwY1EcuRdemOK9ag21ty9UsHkFxsNb/AaoucUz0iPuyGe0Ku+PFuRmWZG7Z7462p9xPw==", - "requires": { - "scryptsy": "^2.1.0", - "semver": "^6.3.0" - }, - "dependencies": { - "scryptsy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/scryptsy/-/scryptsy-2.1.0.tgz", - "integrity": "sha512-1CdSqHQowJBnMAFyPEBRfqag/YP9OF394FV+4YREIJX4ljD7OxvQRDayyoyyCk+senRjSkP6VnUNQmVQqB6g7w==" - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } - } - }, - "@web3-js/websocket": { - "version": "1.0.30", - "resolved": "https://registry.npmjs.org/@web3-js/websocket/-/websocket-1.0.30.tgz", - "integrity": "sha512-fDwrD47MiDrzcJdSeTLF75aCcxVVt8B1N74rA+vh2XCAvFy4tEWJjtnUtj2QG7/zlQ6g9cQ88bZFBxwd9/FmtA==", - "requires": { - "debug": "^2.2.0", - "es5-ext": "^0.10.50", - "nan": "^2.14.0", - "typedarray-to-buffer": "^3.1.5", - "yaeti": "^0.0.6" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } + "@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true }, "abstract-leveldown": { "version": "3.0.0", @@ -6497,112 +6536,32 @@ "uri-js": "^4.2.2" } }, - "ansi-colors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", - "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", - "requires": { - "ansi-wrap": "^0.1.0" - } - }, - "ansi-gray": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", - "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=", - "requires": { - "ansi-wrap": "0.1.0" - } - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, "requires": { "color-convert": "^1.9.0" } }, - "ansi-wrap": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", - "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=" - }, - "any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" - }, - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - }, - "dependencies": { - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "requires": { - "remove-trailing-separator": "^1.0.1" - } - } - } - }, - "append-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz", - "integrity": "sha1-2CIM9GYIFSXv6lBhTz3mUU36WPE=", - "requires": { - "buffer-equal": "^1.0.0" - } - }, - "archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=" - }, "arr-diff": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" - }, - "arr-filter": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/arr-filter/-/arr-filter-1.1.2.tgz", - "integrity": "sha1-Q/3d0JHo7xGqTEXZzcGOLf8XEe4=", - "requires": { - "make-iterator": "^1.0.0" - } + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true }, "arr-flatten": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" - }, - "arr-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/arr-map/-/arr-map-2.0.2.tgz", - "integrity": "sha1-Onc0X/wc814qkYJWAfnljy4kysQ=", - "requires": { - "make-iterator": "^1.0.0" - } + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true }, "arr-union": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" - }, - "array-each": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", - "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=" + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true }, "array-flatten": { "version": "1.1.1", @@ -6611,63 +6570,11 @@ "dev": true, "optional": true }, - "array-initial": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz", - "integrity": "sha1-L6dLJnOTccOUe9enrcc74zSz15U=", - "requires": { - "array-slice": "^1.0.0", - "is-number": "^4.0.0" - }, - "dependencies": { - "is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==" - } - } - }, - "array-last": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/array-last/-/array-last-1.3.0.tgz", - "integrity": "sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg==", - "requires": { - "is-number": "^4.0.0" - }, - "dependencies": { - "is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==" - } - } - }, - "array-slice": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", - "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==" - }, - "array-sort": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-sort/-/array-sort-1.0.0.tgz", - "integrity": "sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg==", - "requires": { - "default-compare": "^1.0.0", - "get-value": "^2.0.6", - "kind-of": "^5.0.2" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" - } - } - }, "array-unique": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true }, "asn1": { "version": "0.2.4", @@ -6700,7 +6607,8 @@ "assign-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true }, "async": { "version": "2.6.2", @@ -6711,22 +6619,6 @@ "lodash": "^4.17.11" } }, - "async-done": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.2.tgz", - "integrity": "sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.2", - "process-nextick-args": "^2.0.0", - "stream-exhaust": "^1.0.1" - } - }, - "async-each": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", - "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==" - }, "async-eventemitter": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/async-eventemitter/-/async-eventemitter-0.2.4.tgz", @@ -6742,14 +6634,6 @@ "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", "dev": true }, - "async-settle": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz", - "integrity": "sha1-HQqRS7Aldb7IqPOnTlCA9yssDGs=", - "requires": { - "async-done": "^1.2.2" - } - }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -6759,7 +6643,8 @@ "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true }, "aws-sign2": { "version": "0.7.0", @@ -6784,6 +6669,12 @@ "js-tokens": "^3.0.2" }, "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, "ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", @@ -6809,6 +6700,15 @@ "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", "dev": true }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", @@ -6853,16 +6753,22 @@ "ms": "2.0.0" } }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", "dev": true } } @@ -6888,12 +6794,6 @@ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true } } }, @@ -7406,21 +7306,6 @@ "source-map-support": "^0.4.15" }, "dependencies": { - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, "source-map-support": { "version": "0.4.18", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", @@ -7481,6 +7366,12 @@ "ms": "2.0.0" } }, + "globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", + "dev": true + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -7499,6 +7390,14 @@ "esutils": "^2.0.2", "lodash": "^4.17.4", "to-fast-properties": "^1.0.3" + }, + "dependencies": { + "to-fast-properties": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", + "dev": true + } } }, "babelify": { @@ -7517,22 +7416,6 @@ "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", "dev": true }, - "bach": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz", - "integrity": "sha1-Szzpa/JxNPeaG0FKUcFONMO9mIA=", - "requires": { - "arr-filter": "^1.1.1", - "arr-flatten": "^1.0.1", - "arr-map": "^2.0.0", - "array-each": "^1.0.0", - "array-initial": "^1.0.0", - "array-last": "^1.1.1", - "async-done": "^1.2.2", - "async-settle": "^1.0.0", - "now-and-later": "^2.0.0" - } - }, "backoff": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz", @@ -7545,12 +7428,14 @@ "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true }, "base": { "version": "0.11.2", "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, "requires": { "cache-base": "^1.0.1", "class-utils": "^0.3.5", @@ -7565,35 +7450,10 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, "requires": { "is-descriptor": "^1.0.0" } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } } } }, @@ -7601,6 +7461,7 @@ "version": "3.0.8", "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.8.tgz", "integrity": "sha512-Rl/1AWP4J/zRrk54hhlxH4drNxPJXYUaKffODVI53/dAsV4t9fBxyxYKAVPU1XBHxYwOWP9h9H0hM2MVw4YfJA==", + "dev": true, "requires": { "safe-buffer": "^5.0.1" } @@ -7608,7 +7469,8 @@ "base64-js": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", - "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", + "dev": true }, "bcrypt-pbkdf": { "version": "1.0.2", @@ -7630,20 +7492,9 @@ "bignumber.js": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz", - "integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==" - }, - "binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==" - }, - "bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "requires": { - "file-uri-to-path": "1.0.0" - } + "integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==", + "dev": true, + "optional": true }, "bip39": { "version": "2.5.0", @@ -7658,66 +7509,6 @@ "unorm": "^1.3.3" } }, - "bip66": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/bip66/-/bip66-1.1.5.tgz", - "integrity": "sha1-AfqHSHhcpwlV1QESF9GzE5lpyiI=", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "bl": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", - "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", - "requires": { - "readable-stream": "^2.3.5", - "safe-buffer": "^5.1.1" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } - } - } - } - }, "blakejs": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.1.0.tgz", @@ -7734,7 +7525,8 @@ "bn.js": { "version": "4.11.9", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true }, "body-parser": { "version": "1.19.0", @@ -7785,44 +7577,23 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=" - } - } - }, "brorand": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true }, "browserify-aes": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, "requires": { "buffer-xor": "^1.0.3", "cipher-base": "^1.0.0", @@ -7921,6 +7692,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", "integrity": "sha1-vhYedsNU9veIrkBx9j806MTwpCo=", + "dev": true, "requires": { "base-x": "^3.0.2" } @@ -7929,6 +7701,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", + "dev": true, "requires": { "bs58": "^4.0.0", "create-hash": "^1.1.0", @@ -7939,44 +7712,17 @@ "version": "5.6.0", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", + "dev": true, "requires": { "base64-js": "^1.0.2", "ieee754": "^1.1.4" } }, - "buffer-alloc": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", - "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", - "requires": { - "buffer-alloc-unsafe": "^1.1.0", - "buffer-fill": "^1.0.0" - } - }, - "buffer-alloc-unsafe": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", - "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==" - }, - "buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" - }, - "buffer-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz", - "integrity": "sha1-WWFrSYME1Var1GaWayLu2j7KX74=" - }, - "buffer-fill": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", - "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=" - }, "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true }, "buffer-to-arraybuffer": { "version": "0.0.5", @@ -7988,7 +7734,25 @@ "buffer-xor": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "bufferutil": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.1.tgz", + "integrity": "sha512-xowrxvpxojqkagPcWRQVXZl0YXhRhAtBEIq3VoER1NH5Mw1n1o0ojdspp+GS2J//2gCVyrzQDApQ4unGF+QOoA==", + "dev": true, + "requires": { + "node-gyp-build": "~3.7.0" + }, + "dependencies": { + "node-gyp-build": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-3.7.0.tgz", + "integrity": "sha512-L/Eg02Epx6Si2NXmedx+Okg+4UHqmaf3TNcxd50SF9NQGcJaON3AtU++kax69XV7YWz4tUspqZSAsVofhFKG2w==", + "dev": true + } + } }, "bytes": { "version": "3.1.0", @@ -8020,6 +7784,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, "requires": { "collection-visit": "^1.0.0", "component-emitter": "^1.2.1", @@ -8048,16 +7813,6 @@ "responselike": "^1.0.2" }, "dependencies": { - "get-stream": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", - "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", - "dev": true, - "optional": true, - "requires": { - "pump": "^3.0.0" - } - }, "lowercase-keys": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", @@ -8097,11 +7852,6 @@ } } }, - "camelcase": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" - }, "caniuse-lite": { "version": "1.0.30001146", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001146.tgz", @@ -8118,6 +7868,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -8133,24 +7884,6 @@ "functional-red-black-tree": "^1.0.1" } }, - "chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", - "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" - } - }, "chownr": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", @@ -8158,19 +7891,61 @@ "dev": true, "optional": true }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "cids": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/cids/-/cids-0.7.5.tgz", + "integrity": "sha512-zT7mPeghoWAu+ppn8+BS1tQ5qGmbMfB4AregnQjA/qHY3GC1m1ptI9GkWNlgeu38r7CuRdXB47uY2XgAYt6QVA==", + "dev": true, + "optional": true, + "requires": { + "buffer": "^5.5.0", + "class-is": "^1.1.0", + "multibase": "~0.6.0", + "multicodec": "^1.0.0", + "multihashes": "~0.4.15" + }, + "dependencies": { + "multicodec": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-1.0.4.tgz", + "integrity": "sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg==", + "dev": true, + "optional": true, + "requires": { + "buffer": "^5.6.0", + "varint": "^5.0.0" + } + } + } + }, "cipher-base": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, "requires": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" } }, + "class-is": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/class-is/-/class-is-1.1.0.tgz", + "integrity": "sha512-rhjH9AG1fvabIDoGRVH587413LPjTZgmDF9fOFCbFJQV4yuocX1mHxxvXI4g3cGwbVY9wAYIoKlg1N79frJKQw==", + "dev": true, + "optional": true + }, "class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, "requires": { "arr-union": "^3.1.0", "define-property": "^0.2.5", @@ -8181,29 +7956,82 @@ "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=" + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true } } }, - "cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wrap-ansi": "^2.0.0" - } - }, "clone": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=" - }, - "clone-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", - "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=" + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", + "dev": true }, "clone-response": { "version": "1.0.2", @@ -8215,83 +8043,30 @@ "mimic-response": "^1.0.0" } }, - "clone-stats": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", - "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=" - }, - "cloneable-readable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", - "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", - "requires": { - "inherits": "^2.0.1", - "process-nextick-args": "^2.0.0", - "readable-stream": "^2.3.5" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" - }, - "collection-map": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-map/-/collection-map-1.0.0.tgz", - "integrity": "sha1-rqDwb40mx4DCt1SUOFVEsiVa8Yw=", - "requires": { - "arr-map": "^2.0.2", - "for-own": "^1.0.0", - "make-iterator": "^1.0.0" - } - }, "collection-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, "requires": { "map-visit": "^1.0.0", "object-visit": "^1.0.0" } }, - "color-support": { + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true }, "combined-stream": { "version": "1.0.8", @@ -8302,64 +8077,28 @@ "delayed-stream": "~1.0.0" } }, - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" - }, "component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true }, "concat-stream": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, "requires": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^2.2.2", "typedarray": "^0.0.6" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } } }, "content-disposition": { @@ -8381,6 +8120,18 @@ } } }, + "content-hash": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/content-hash/-/content-hash-2.5.2.tgz", + "integrity": "sha512-FvIQKy0S1JaWV10sMsA7TRx8bpU+pqPkhbsfvOJAdjRXvYxEckAwQWGwtRjiaJfh+E0DvcWUGqcdjwMGFjsSdw==", + "dev": true, + "optional": true, + "requires": { + "cids": "^0.7.1", + "multicodec": "^0.5.5", + "multihashes": "^0.4.15" + } + }, "content-type": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", @@ -8392,6 +8143,7 @@ "version": "1.7.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, "requires": { "safe-buffer": "~5.1.1" }, @@ -8399,7 +8151,8 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true } } }, @@ -8427,16 +8180,8 @@ "copy-descriptor": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" - }, - "copy-props": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-2.0.4.tgz", - "integrity": "sha512-7cjuUME+p+S3HZlbllgsn2CDwS+5eCCX16qBgNC4jgSTf49qR1VKy/Zhl400m0IQXl/bPGEVqncgUUMjrr4s8A==", - "requires": { - "each-props": "^1.3.0", - "is-plain-object": "^2.0.1" - } + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true }, "core-js": { "version": "2.6.11", @@ -8453,7 +8198,8 @@ "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true }, "cors": { "version": "2.8.5", @@ -8481,6 +8227,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, "requires": { "cipher-base": "^1.0.1", "inherits": "^2.0.1", @@ -8493,6 +8240,7 @@ "version": "1.1.7", "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, "requires": { "cipher-base": "^1.0.3", "create-hash": "^1.1.0", @@ -8536,6 +8284,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dev": true, "requires": { "es5-ext": "^0.10.50", "type": "^1.0.1" @@ -8559,37 +8308,11 @@ "ms": "^2.1.1" } }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" - }, "decode-uri-component": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" - }, - "decompress": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/decompress/-/decompress-4.2.1.tgz", - "integrity": "sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ==", - "requires": { - "decompress-tar": "^4.0.0", - "decompress-tarbz2": "^4.0.0", - "decompress-targz": "^4.0.0", - "decompress-unzip": "^4.0.1", - "graceful-fs": "^4.1.10", - "make-dir": "^1.0.0", - "pify": "^2.3.0", - "strip-dirs": "^2.0.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" - } - } + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true }, "decompress-response": { "version": "3.3.0", @@ -8601,77 +8324,6 @@ "mimic-response": "^1.0.0" } }, - "decompress-tar": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz", - "integrity": "sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==", - "requires": { - "file-type": "^5.2.0", - "is-stream": "^1.1.0", - "tar-stream": "^1.5.2" - } - }, - "decompress-tarbz2": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz", - "integrity": "sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==", - "requires": { - "decompress-tar": "^4.1.0", - "file-type": "^6.1.0", - "is-stream": "^1.1.0", - "seek-bzip": "^1.0.5", - "unbzip2-stream": "^1.0.9" - }, - "dependencies": { - "file-type": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz", - "integrity": "sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==" - } - } - }, - "decompress-targz": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz", - "integrity": "sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==", - "requires": { - "decompress-tar": "^4.1.1", - "file-type": "^5.2.0", - "is-stream": "^1.1.0" - } - }, - "decompress-unzip": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz", - "integrity": "sha1-3qrM39FK6vhVePczroIQ+bSEj2k=", - "requires": { - "file-type": "^3.8.0", - "get-stream": "^2.2.0", - "pify": "^2.3.0", - "yauzl": "^2.4.2" - }, - "dependencies": { - "file-type": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", - "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=" - }, - "get-stream": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", - "integrity": "sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4=", - "requires": { - "object-assign": "^4.0.1", - "pinkie-promise": "^2.0.0" - } - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" - } - } - }, "deep-equal": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", @@ -8684,36 +8336,8 @@ "object-is": "^1.0.1", "object-keys": "^1.1.1", "regexp.prototype.flags": "^1.2.0" - }, - "dependencies": { - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - } } }, - "default-compare": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz", - "integrity": "sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==", - "requires": { - "kind-of": "^5.0.2" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" - } - } - }, - "default-resolution": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/default-resolution/-/default-resolution-2.0.0.tgz", - "integrity": "sha1-vLgrqnKtebQmp2cy8aga1t8m1oQ=" - }, "defer-to-connect": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", @@ -8725,6 +8349,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-4.0.2.tgz", "integrity": "sha512-5fMC8ek8alH16QiV0lTCis610D1Zt1+LA4MS4d63JgS32lrCjTFDUFz2ao09/j2I4Bqb5jL4FZYwu7Jz0XO1ww==", + "dev": true, "requires": { "abstract-leveldown": "~5.0.0", "inherits": "^2.0.3" @@ -8734,6 +8359,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-5.0.0.tgz", "integrity": "sha512-5mU5P1gXtsMIXg65/rsYGsi93+MlogXZ9FA8JnwKurHQg64bfXwGYVdVdijNTVNOlAsuIiOwHdvFFD5JqCJQ7A==", + "dev": true, "requires": { "xtend": "~4.0.0" } @@ -8744,52 +8370,19 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, "requires": { "object-keys": "^1.0.12" - }, - "dependencies": { - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" - } } }, "define-property": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, "requires": { "is-descriptor": "^1.0.2", "isobject": "^3.0.1" - }, - "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } } }, "defined": { @@ -8829,11 +8422,6 @@ "dev": true, "optional": true }, - "detect-file": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", - "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=" - }, "detect-indent": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", @@ -8870,16 +8458,6 @@ "minimatch": "^3.0.4" } }, - "drbg.js": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/drbg.js/-/drbg.js-1.0.1.tgz", - "integrity": "sha1-Pja2xCs3BDgjzbwzLVjzHiRFSAs=", - "requires": { - "browserify-aes": "^1.0.6", - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4" - } - }, "duplexer3": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", @@ -8887,60 +8465,6 @@ "dev": true, "optional": true }, - "duplexify": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", - "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", - "requires": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "each-props": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/each-props/-/each-props-1.3.2.tgz", - "integrity": "sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA==", - "requires": { - "is-plain-object": "^2.0.1", - "object.defaults": "^1.1.0" - } - }, "ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -8968,6 +8492,7 @@ "version": "6.5.3", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", + "dev": true, "requires": { "bn.js": "^4.4.0", "brorand": "^1.0.1", @@ -9033,6 +8558,7 @@ "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, "requires": { "once": "^1.4.0" } @@ -9041,22 +8567,16 @@ "version": "0.1.7", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "dev": true, "requires": { "prr": "~1.0.1" } }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "requires": { - "is-arrayish": "^0.2.1" - } - }, "es-abstract": { "version": "1.17.7", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", + "dev": true, "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", @@ -9069,19 +8589,13 @@ "object.assign": "^4.1.1", "string.prototype.trimend": "^1.0.1", "string.prototype.trimstart": "^1.0.1" - }, - "dependencies": { - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" - } } }, "es-to-primitive": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, "requires": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", @@ -9092,6 +8606,7 @@ "version": "0.10.53", "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", + "dev": true, "requires": { "es6-iterator": "~2.0.3", "es6-symbol": "~3.1.3", @@ -9102,6 +8617,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "dev": true, "requires": { "d": "1", "es5-ext": "^0.10.35", @@ -9112,22 +8628,12 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "dev": true, "requires": { "d": "^1.0.1", "ext": "^1.1.2" } }, - "es6-weak-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", - "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", - "requires": { - "d": "1", - "es5-ext": "^0.10.46", - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.1" - } - }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -9138,7 +8644,8 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true }, "esutils": { "version": "2.0.3", @@ -9210,15 +8717,6 @@ "requires": { "idna-uts46-hx": "^2.3.1", "js-sha3": "^0.5.7" - }, - "dependencies": { - "js-sha3": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", - "integrity": "sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc=", - "dev": true, - "optional": true - } } }, "eth-json-rpc-infura": { @@ -9272,12 +8770,6 @@ "abstract-leveldown": "~2.6.0" } }, - "ethereum-common": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/ethereum-common/-/ethereum-common-0.2.0.tgz", - "integrity": "sha512-XOnAR/3rntJgbCdGhqdaLIxDLWKLmsZOGhHdBKadEr6gEnJLH52k93Ou+TUdFaPN3hJc3isBZBal3U/XZ15abA==", - "dev": true - }, "ethereumjs-account": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/ethereumjs-account/-/ethereumjs-account-2.0.5.tgz", @@ -9300,6 +8792,14 @@ "ethereumjs-tx": "^1.2.2", "ethereumjs-util": "^5.0.0", "merkle-patricia-tree": "^2.1.2" + }, + "dependencies": { + "ethereum-common": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/ethereum-common/-/ethereum-common-0.2.0.tgz", + "integrity": "sha512-XOnAR/3rntJgbCdGhqdaLIxDLWKLmsZOGhHdBKadEr6gEnJLH52k93Ou+TUdFaPN3hJc3isBZBal3U/XZ15abA==", + "dev": true + } } }, "ethereumjs-tx": { @@ -9310,14 +8810,6 @@ "requires": { "ethereum-common": "^0.0.18", "ethereumjs-util": "^5.0.0" - }, - "dependencies": { - "ethereum-common": { - "version": "0.0.18", - "resolved": "https://registry.npmjs.org/ethereum-common/-/ethereum-common-0.0.18.tgz", - "integrity": "sha1-L9w1dvIykDNYl26znaeDIT/5Uj8=", - "dev": true - } } }, "ethereumjs-util": { @@ -9504,13 +8996,36 @@ "prr": "~1.0.1", "semver": "~5.4.1", "xtend": "~4.0.0" + } + }, + "ltgt": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz", + "integrity": "sha1-81ypHEk/e3PaDgdJUwTxezH4fuU=", + "dev": true + }, + "memdown": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/memdown/-/memdown-1.4.1.tgz", + "integrity": "sha1-tOThkhdGZP+65BNhqlAPMRnv4hU=", + "dev": true, + "requires": { + "abstract-leveldown": "~2.7.1", + "functional-red-black-tree": "^1.0.1", + "immediate": "^3.2.3", + "inherits": "~2.0.1", + "ltgt": "~2.2.0", + "safe-buffer": "~5.1.1" }, "dependencies": { - "semver": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", - "dev": true + "abstract-leveldown": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-2.7.2.tgz", + "integrity": "sha512-+OVvxH2rHVEhWLdbudP6p0+dNMXu8JA1CbhP19T8paTYAcX7oJ4OVjT+ZUVpv7mITxXHqDMej+GdqXBmXkw09w==", + "dev": true, + "requires": { + "xtend": "~4.0.0" + } } } }, @@ -9544,6 +9059,18 @@ "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=", "dev": true }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "semver": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", + "dev": true + }, "string_decoder": { "version": "0.10.31", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", @@ -9669,12 +9196,6 @@ "abstract-leveldown": "~2.6.0" } }, - "ethereum-common": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/ethereum-common/-/ethereum-common-0.2.0.tgz", - "integrity": "sha512-XOnAR/3rntJgbCdGhqdaLIxDLWKLmsZOGhHdBKadEr6gEnJLH52k93Ou+TUdFaPN3hJc3isBZBal3U/XZ15abA==", - "dev": true - }, "ethereumjs-account": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/ethereumjs-account/-/ethereumjs-account-2.0.5.tgz", @@ -9697,6 +9218,14 @@ "ethereumjs-tx": "^1.2.2", "ethereumjs-util": "^5.0.0", "merkle-patricia-tree": "^2.1.2" + }, + "dependencies": { + "ethereum-common": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/ethereum-common/-/ethereum-common-0.2.0.tgz", + "integrity": "sha512-XOnAR/3rntJgbCdGhqdaLIxDLWKLmsZOGhHdBKadEr6gEnJLH52k93Ou+TUdFaPN3hJc3isBZBal3U/XZ15abA==", + "dev": true + } } }, "ethereumjs-tx": { @@ -9707,14 +9236,6 @@ "requires": { "ethereum-common": "^0.0.18", "ethereumjs-util": "^5.0.0" - }, - "dependencies": { - "ethereum-common": { - "version": "0.0.18", - "resolved": "https://registry.npmjs.org/ethereum-common/-/ethereum-common-0.0.18.tgz", - "integrity": "sha1-L9w1dvIykDNYl26znaeDIT/5Uj8=", - "dev": true - } } }, "ethereumjs-util": { @@ -9901,13 +9422,36 @@ "prr": "~1.0.1", "semver": "~5.4.1", "xtend": "~4.0.0" + } + }, + "ltgt": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz", + "integrity": "sha1-81ypHEk/e3PaDgdJUwTxezH4fuU=", + "dev": true + }, + "memdown": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/memdown/-/memdown-1.4.1.tgz", + "integrity": "sha1-tOThkhdGZP+65BNhqlAPMRnv4hU=", + "dev": true, + "requires": { + "abstract-leveldown": "~2.7.1", + "functional-red-black-tree": "^1.0.1", + "immediate": "^3.2.3", + "inherits": "~2.0.1", + "ltgt": "~2.2.0", + "safe-buffer": "~5.1.1" }, "dependencies": { - "semver": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", - "dev": true + "abstract-leveldown": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-2.7.2.tgz", + "integrity": "sha512-+OVvxH2rHVEhWLdbudP6p0+dNMXu8JA1CbhP19T8paTYAcX7oJ4OVjT+ZUVpv7mITxXHqDMej+GdqXBmXkw09w==", + "dev": true, + "requires": { + "xtend": "~4.0.0" + } } } }, @@ -9941,6 +9485,18 @@ "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=", "dev": true }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "semver": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", + "dev": true + }, "string_decoder": { "version": "0.10.31", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", @@ -10200,13 +9756,36 @@ "prr": "~1.0.1", "semver": "~5.4.1", "xtend": "~4.0.0" + } + }, + "ltgt": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz", + "integrity": "sha1-81ypHEk/e3PaDgdJUwTxezH4fuU=", + "dev": true + }, + "memdown": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/memdown/-/memdown-1.4.1.tgz", + "integrity": "sha1-tOThkhdGZP+65BNhqlAPMRnv4hU=", + "dev": true, + "requires": { + "abstract-leveldown": "~2.7.1", + "functional-red-black-tree": "^1.0.1", + "immediate": "^3.2.3", + "inherits": "~2.0.1", + "ltgt": "~2.2.0", + "safe-buffer": "~5.1.1" }, "dependencies": { - "semver": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", - "dev": true + "abstract-leveldown": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-2.7.2.tgz", + "integrity": "sha512-+OVvxH2rHVEhWLdbudP6p0+dNMXu8JA1CbhP19T8paTYAcX7oJ4OVjT+ZUVpv7mITxXHqDMej+GdqXBmXkw09w==", + "dev": true, + "requires": { + "xtend": "~4.0.0" + } } } }, @@ -10240,6 +9819,18 @@ "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=", "dev": true }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "semver": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", + "dev": true + }, "string_decoder": { "version": "0.10.31", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", @@ -10295,34 +9886,6 @@ "ethereum-cryptography": "^0.1.3", "ethjs-util": "0.1.6", "rlp": "^2.2.3" - }, - "dependencies": { - "keccak": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/keccak/-/keccak-2.1.0.tgz", - "integrity": "sha512-m1wbJRTo+gWbctZWay9i26v5fFnYkOn7D5PCxJ3fZUGUEb49dE1Pm4BREUYCt/aoO6di7jeoGmhvqN9Nzylm3Q==", - "requires": { - "bindings": "^1.5.0", - "inherits": "^2.0.4", - "nan": "^2.14.0", - "safe-buffer": "^5.2.0" - } - }, - "secp256k1": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-3.8.0.tgz", - "integrity": "sha512-k5ke5avRZbtl9Tqx/SA7CbY3NF6Ro+Sj9cZxezFzuBlLDmyqPiL8hJJ+EmzD8Ig4LUDByHJ3/iPOVoRixs/hmw==", - "requires": { - "bindings": "^1.5.0", - "bip66": "^1.1.5", - "bn.js": "^4.11.8", - "create-hash": "^1.2.0", - "drbg.js": "^1.0.1", - "elliptic": "^6.5.2", - "nan": "^2.14.0", - "safe-buffer": "^5.1.2" - } - } } }, "ethereumjs-vm": { @@ -10459,13 +10022,36 @@ "prr": "~1.0.1", "semver": "~5.4.1", "xtend": "~4.0.0" + } + }, + "ltgt": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz", + "integrity": "sha1-81ypHEk/e3PaDgdJUwTxezH4fuU=", + "dev": true + }, + "memdown": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/memdown/-/memdown-1.4.1.tgz", + "integrity": "sha1-tOThkhdGZP+65BNhqlAPMRnv4hU=", + "dev": true, + "requires": { + "abstract-leveldown": "~2.7.1", + "functional-red-black-tree": "^1.0.1", + "immediate": "^3.2.3", + "inherits": "~2.0.1", + "ltgt": "~2.2.0", + "safe-buffer": "~5.1.1" }, "dependencies": { - "semver": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", - "dev": true + "abstract-leveldown": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-2.7.2.tgz", + "integrity": "sha512-+OVvxH2rHVEhWLdbudP6p0+dNMXu8JA1CbhP19T8paTYAcX7oJ4OVjT+ZUVpv7mITxXHqDMej+GdqXBmXkw09w==", + "dev": true, + "requires": { + "xtend": "~4.0.0" + } } } }, @@ -10514,6 +10100,18 @@ "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=", "dev": true }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "semver": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", + "dev": true + }, "string_decoder": { "version": "0.10.31", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", @@ -10587,6 +10185,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, "requires": { "md5.js": "^1.3.4", "safe-buffer": "^5.1.1" @@ -10596,6 +10195,7 @@ "version": "2.1.4", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, "requires": { "debug": "^2.3.3", "define-property": "^0.2.5", @@ -10610,6 +10210,7 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, "requires": { "ms": "2.0.0" } @@ -10617,28 +10218,98 @@ "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=" + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } }, "extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=" + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true } } }, - "expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", - "requires": { - "homedir-polyfill": "^1.0.1" - } - }, "express": { "version": "4.17.1", "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", @@ -10715,6 +10386,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", + "dev": true, "requires": { "type": "^2.0.0" }, @@ -10722,38 +10394,32 @@ "type": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz", - "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==" + "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==", + "dev": true } } }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true }, "extend-shallow": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, "requires": { "assign-symbols": "^1.0.0", "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "requires": { - "is-plain-object": "^2.0.4" - } - } } }, "extglob": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, "requires": { "array-unique": "^0.3.2", "define-property": "^1.0.0", @@ -10769,6 +10435,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, "requires": { "is-descriptor": "^1.0.0" } @@ -10776,33 +10443,17 @@ "extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=" - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, "requires": { - "kind-of": "^6.0.0" + "is-extendable": "^0.1.0" } }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true } } }, @@ -10821,17 +10472,6 @@ "checkpoint-store": "^1.1.0" } }, - "fancy-log": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", - "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", - "requires": { - "ansi-gray": "^0.1.1", - "color-support": "^1.1.3", - "parse-node-version": "^1.0.0", - "time-stamp": "^1.0.0" - } - }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -10844,14 +10484,6 @@ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, - "fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", - "requires": { - "pend": "~1.2.0" - } - }, "fetch-ponyfill": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/fetch-ponyfill/-/fetch-ponyfill-4.1.0.tgz", @@ -10861,6 +10493,12 @@ "node-fetch": "~1.7.1" }, "dependencies": { + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, "node-fetch": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", @@ -10873,34 +10511,6 @@ } } }, - "file-type": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", - "integrity": "sha1-LdvqfHP/42No365J3DOMBYwritY=" - }, - "file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=" - } - } - }, "finalhandler": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", @@ -10936,92 +10546,150 @@ } } }, - "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "find-yarn-workspace-root": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-1.2.1.tgz", + "integrity": "sha512-dVtfb0WuQG+8Ag2uWkbG79hOUzEsRrhBzgfn86g2sJPkzmcpGdghbNTfUKGTxymFrY/tLIodDzLoW9nOJ4FY8Q==", + "dev": true, "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" + "fs-extra": "^4.0.3", + "micromatch": "^3.1.4" + }, + "dependencies": { + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fs-extra": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", + "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } } }, - "findup-sync": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", - "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", - "requires": { - "detect-file": "^1.0.0", - "is-glob": "^4.0.0", - "micromatch": "^3.0.4", - "resolve-dir": "^1.0.1" - } - }, - "fined": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz", - "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==", - "requires": { - "expand-tilde": "^2.0.2", - "is-plain-object": "^2.0.3", - "object.defaults": "^1.1.0", - "object.pick": "^1.2.0", - "parse-filepath": "^1.0.1" - } - }, - "flagged-respawn": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", - "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==" - }, "flow-stoplight": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/flow-stoplight/-/flow-stoplight-1.0.0.tgz", "integrity": "sha1-SiksW8/4s5+mzAyxqFPYbyfu/3s=", "dev": true }, - "flush-write-stream": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", - "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", - "requires": { - "inherits": "^2.0.3", - "readable-stream": "^2.3.6" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, "for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -11034,15 +10702,8 @@ "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" - }, - "for-own": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", - "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", - "requires": { - "for-in": "^1.0.1" - } + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true }, "forever-agent": { "version": "0.6.1", @@ -11072,6 +10733,7 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, "requires": { "map-cache": "^0.2.2" } @@ -11083,64 +10745,41 @@ "dev": true, "optional": true }, - "fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" - }, "fs-extra": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, "requires": { "graceful-fs": "^4.1.2", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, - "fs-minipass": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", - "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.6.0" - } - }, - "fs-mkdirp-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz", - "integrity": "sha1-C3gV/DIBxqaeFNuYzgmMFpNSWes=", - "requires": { - "graceful-fs": "^4.1.11", - "through2": "^2.0.3" - } - }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true }, "functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" - }, - "get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true }, "get-stream": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "optional": true, "requires": { "pump": "^3.0.0" } @@ -11148,7 +10787,8 @@ "get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true }, "getpass": { "version": "0.1.7", @@ -11163,6 +10803,7 @@ "version": "7.1.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -11172,90 +10813,6 @@ "path-is-absolute": "^1.0.0" } }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "requires": { - "is-extglob": "^2.1.0" - } - } - } - }, - "glob-stream": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz", - "integrity": "sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ=", - "requires": { - "extend": "^3.0.0", - "glob": "^7.1.1", - "glob-parent": "^3.1.0", - "is-negated-glob": "^1.0.0", - "ordered-read-streams": "^1.0.0", - "pumpify": "^1.3.5", - "readable-stream": "^2.1.5", - "remove-trailing-separator": "^1.0.1", - "to-absolute-glob": "^2.0.0", - "unique-stream": "^2.0.2" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "glob-watcher": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.5.tgz", - "integrity": "sha512-zOZgGGEHPklZNjZQaZ9f41i7F2YwE+tS5ZHrDhbBCk3stwahn5vQxnFmBJZHoYdusR6R1bLSXeGUy/BhctwKzw==", - "requires": { - "anymatch": "^2.0.0", - "async-done": "^1.2.0", - "chokidar": "^2.0.0", - "is-negated-glob": "^1.0.0", - "just-debounce": "^1.0.0", - "normalize-path": "^3.0.0", - "object.defaults": "^1.1.0" - } - }, "global": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz", @@ -11264,42 +10821,14 @@ "requires": { "min-document": "^2.19.0", "process": "~0.5.1" - } - }, - "global-modules": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", - "requires": { - "global-prefix": "^1.0.1", - "is-windows": "^1.0.1", - "resolve-dir": "^1.0.0" - } - }, - "global-prefix": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", - "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", - "requires": { - "expand-tilde": "^2.0.2", - "homedir-polyfill": "^1.0.1", - "ini": "^1.3.4", - "is-windows": "^1.0.1", - "which": "^1.2.14" - } - }, - "globals": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", - "dev": true - }, - "glogg": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz", - "integrity": "sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA==", - "requires": { - "sparkles": "^1.0.0" + }, + "dependencies": { + "process": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz", + "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=", + "dev": true + } } }, "got": { @@ -11337,53 +10866,8 @@ "graceful-fs": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" - }, - "gulp": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.2.tgz", - "integrity": "sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA==", - "requires": { - "glob-watcher": "^5.0.3", - "gulp-cli": "^2.2.0", - "undertaker": "^1.2.1", - "vinyl-fs": "^3.0.0" - }, - "dependencies": { - "gulp-cli": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.3.0.tgz", - "integrity": "sha512-zzGBl5fHo0EKSXsHzjspp3y5CONegCm8ErO5Qh0UzFzk2y4tMvzLWhoDokADbarfZRL2pGpRp7yt6gfJX4ph7A==", - "requires": { - "ansi-colors": "^1.0.1", - "archy": "^1.0.0", - "array-sort": "^1.0.0", - "color-support": "^1.1.3", - "concat-stream": "^1.6.0", - "copy-props": "^2.0.1", - "fancy-log": "^1.3.2", - "gulplog": "^1.0.0", - "interpret": "^1.4.0", - "isobject": "^3.0.1", - "liftoff": "^3.1.0", - "matchdep": "^2.0.0", - "mute-stdout": "^1.0.0", - "pretty-hrtime": "^1.0.0", - "replace-homedir": "^1.0.0", - "semver-greatest-satisfied-range": "^1.1.0", - "v8flags": "^3.2.0", - "yargs": "^7.1.0" - } - } - } - }, - "gulplog": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz", - "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=", - "requires": { - "glogg": "^1.0.0" - } + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true }, "har-schema": { "version": "2.0.0", @@ -11405,6 +10889,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, "requires": { "function-bind": "^1.1.1" } @@ -11416,8 +10901,22 @@ "dev": true, "requires": { "ansi-regex": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + } } }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, "has-symbol-support-x": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", @@ -11428,7 +10927,8 @@ "has-symbols": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true }, "has-to-string-tag-x": { "version": "1.4.1", @@ -11444,6 +10944,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, "requires": { "get-value": "^2.0.6", "has-values": "^1.0.0", @@ -11454,15 +10955,43 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, "requires": { "is-number": "^3.0.0", "kind-of": "^4.0.0" }, "dependencies": { + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, "kind-of": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, "requires": { "is-buffer": "^1.1.5" } @@ -11473,6 +11002,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dev": true, "requires": { "inherits": "^2.0.4", "readable-stream": "^3.6.0", @@ -11483,6 +11013,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -11495,38 +11026,12 @@ "version": "1.1.7", "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, "requires": { "inherits": "^2.0.3", "minimalistic-assert": "^1.0.1" } }, - "hdkey": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/hdkey/-/hdkey-1.1.2.tgz", - "integrity": "sha512-PTQ4VKu0oRnCrYfLp04iQZ7T2Cxz0UsEXYauk2j8eh6PJXCpbXuCFhOmtIFtbET0i3PMWmHN9J11gU8LEgUljQ==", - "requires": { - "bs58check": "^2.1.2", - "safe-buffer": "^5.1.1", - "secp256k1": "^3.0.1" - }, - "dependencies": { - "secp256k1": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-3.8.0.tgz", - "integrity": "sha512-k5ke5avRZbtl9Tqx/SA7CbY3NF6Ro+Sj9cZxezFzuBlLDmyqPiL8hJJ+EmzD8Ig4LUDByHJ3/iPOVoRixs/hmw==", - "requires": { - "bindings": "^1.5.0", - "bip66": "^1.1.5", - "bn.js": "^4.11.8", - "create-hash": "^1.2.0", - "drbg.js": "^1.0.1", - "elliptic": "^6.5.2", - "nan": "^2.14.0", - "safe-buffer": "^5.1.2" - } - } - } - }, "heap": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.6.tgz", @@ -11537,6 +11042,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, "requires": { "hash.js": "^1.0.3", "minimalistic-assert": "^1.0.0", @@ -11553,19 +11059,6 @@ "os-tmpdir": "^1.0.1" } }, - "homedir-polyfill": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", - "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", - "requires": { - "parse-passwd": "^1.0.0" - } - }, - "hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==" - }, "http-cache-semantics": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", @@ -11646,17 +11139,20 @@ "ieee754": { "version": "1.1.13", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", + "dev": true }, "immediate": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.2.3.tgz", - "integrity": "sha1-0UD6j2FGWb1lQSMwl92qwlzdmRw=" + "integrity": "sha1-0UD6j2FGWb1lQSMwl92qwlzdmRw=", + "dev": true }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -11665,17 +11161,8 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" - }, - "interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==" + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true }, "invariant": { "version": "2.2.4", @@ -11686,11 +11173,6 @@ "loose-envify": "^1.0.0" } }, - "invert-kv": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" - }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -11698,28 +11180,13 @@ "dev": true, "optional": true }, - "is-absolute": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", - "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", - "requires": { - "is-relative": "^1.0.0", - "is-windows": "^1.0.1" - } - }, "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } + "dev": true, + "requires": { + "kind-of": "^6.0.0" } }, "is-arguments": { @@ -11728,78 +11195,56 @@ "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", "dev": true }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" - }, - "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "requires": { - "binary-extensions": "^1.0.0" - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, "is-callable": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", - "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==" + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", + "dev": true + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "requires": { + "ci-info": "^2.0.0" + } }, "is-data-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } + "dev": true, + "requires": { + "kind-of": "^6.0.0" } }, "is-date-object": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==" + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", + "dev": true }, "is-descriptor": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" - } + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" } }, "is-extendable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, "requires": { "is-plain-object": "^2.0.4" } }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" - }, "is-finite": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", @@ -11812,61 +11257,23 @@ "integrity": "sha1-lUPV3nvPWwiiLsiiC65uKG1RDYw=", "dev": true }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "requires": { - "number-is-nan": "^1.0.0" - } - }, "is-function": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz", "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==", "dev": true }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "requires": { - "is-extglob": "^2.1.1" - } - }, "is-hex-prefixed": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz", "integrity": "sha1-fY035q135dEnFIkTxXPggtd39VQ=", "dev": true }, - "is-natural-number": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz", - "integrity": "sha1-q5124dtM7VHjXeDHLr7PCfc0zeg=" - }, - "is-negated-glob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", - "integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI=" - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } + "is-negative-zero": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz", + "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=", + "dev": true }, "is-object": { "version": "1.0.1", @@ -11886,6 +11293,7 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, "requires": { "isobject": "^3.0.1" } @@ -11894,18 +11302,11 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "dev": true, "requires": { "has-symbols": "^1.0.1" } }, - "is-relative": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", - "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", - "requires": { - "is-unc-path": "^1.0.0" - } - }, "is-retry-allowed": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", @@ -11913,15 +11314,11 @@ "dev": true, "optional": true }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" - }, "is-symbol": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "dev": true, "requires": { "has-symbols": "^1.0.1" } @@ -11929,30 +11326,14 @@ "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "is-unc-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", - "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", - "requires": { - "unc-path-regex": "^0.1.2" - } - }, - "is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" - }, - "is-valid-glob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-1.0.0.tgz", - "integrity": "sha1-Kb8+/3Ab4tTTFdusw5vDn+j2Aao=" + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true }, "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true }, "isarray": { "version": "1.0.0", @@ -11963,12 +11344,14 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true }, "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true }, "isstream": { "version": "0.1.2", @@ -11990,7 +11373,9 @@ "js-sha3": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", - "integrity": "sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc=" + "integrity": "sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc=", + "dev": true, + "optional": true }, "js-tokens": { "version": "4.0.0", @@ -12004,12 +11389,6 @@ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", "dev": true }, - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true - }, "json-buffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", @@ -12067,27 +11446,17 @@ "jsonify": "~0.0.0" } }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=" - }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", "dev": true }, - "json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", - "dev": true - }, "jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, "requires": { "graceful-fs": "^4.1.6" } @@ -12110,11 +11479,6 @@ "verror": "1.10.0" } }, - "just-debounce": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.0.0.tgz", - "integrity": "sha1-h/zPrv/AtozRnVX2cilD+SnqNeo=" - }, "keccak": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.1.tgz", @@ -12138,73 +11502,16 @@ "kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true }, - "last-run": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/last-run/-/last-run-1.1.1.tgz", - "integrity": "sha1-RblpQsF7HHnHchmCWbqUO+v4yls=", + "klaw-sync": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", + "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==", + "dev": true, "requires": { - "default-resolution": "^2.0.0", - "es6-weak-map": "^2.0.1" - } - }, - "lazystream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", - "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", - "requires": { - "readable-stream": "^2.0.5" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", - "requires": { - "invert-kv": "^1.0.0" - } - }, - "lead": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lead/-/lead-1.0.0.tgz", - "integrity": "sha1-bxT5mje+Op3XhPVJVpDlkDRm7kI=", - "requires": { - "flush-write-stream": "^1.0.2" + "graceful-fs": "^4.1.11" } }, "level-codec": { @@ -12229,34 +11536,11 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/level-iterator-stream/-/level-iterator-stream-2.0.3.tgz", "integrity": "sha512-I6Heg70nfF+e5Y3/qfthJFexhRw/Gi3bIymCoXAlijZdAcLaPuWSJs3KXyTYf23ID6g0o2QF62Yh+grOXY3Rig==", + "dev": true, "requires": { "inherits": "^2.0.1", + "readable-stream": "^2.0.5", "xtend": "^4.0.0" - }, - "dependencies": { - "level-errors": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/level-errors/-/level-errors-1.1.2.tgz", - "integrity": "sha512-Sw/IJwWbPKF5Ai4Wz60B52yj0zYeqzObLh8k1Tk88jVmD51cJSKWSYpRyhVIvFzZdvsPqlH5wfhp/yxdsaQH4w==", - "requires": { - "errno": "~0.1.1" - } - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - } } }, "level-mem": { @@ -12278,12 +11562,6 @@ "xtend": "~4.0.0" } }, - "immediate": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.2.3.tgz", - "integrity": "sha1-0UD6j2FGWb1lQSMwl92qwlzdmRw=", - "dev": true - }, "ltgt": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz", @@ -12347,61 +11625,6 @@ "pull-stream": "^3.6.8", "typewiselite": "~1.0.0", "xtend": "~4.0.0" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "level-iterator-stream": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/level-iterator-stream/-/level-iterator-stream-2.0.3.tgz", - "integrity": "sha512-I6Heg70nfF+e5Y3/qfthJFexhRw/Gi3bIymCoXAlijZdAcLaPuWSJs3KXyTYf23ID6g0o2QF62Yh+grOXY3Rig==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.5", - "xtend": "^4.0.0" - } - }, - "ltgt": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.1.3.tgz", - "integrity": "sha1-EIUaBtmWS5cReEQcI8nlJpjuzjQ=", - "dev": true - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } } }, "level-ws": { @@ -12413,35 +11636,6 @@ "inherits": "^2.0.3", "readable-stream": "^2.2.8", "xtend": "^4.0.1" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true - } } }, "levelup": { @@ -12456,31 +11650,6 @@ "xtend": "~4.0.0" }, "dependencies": { - "abstract-leveldown": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-5.0.0.tgz", - "integrity": "sha512-5mU5P1gXtsMIXg65/rsYGsi93+MlogXZ9FA8JnwKurHQg64bfXwGYVdVdijNTVNOlAsuIiOwHdvFFD5JqCJQ7A==", - "dev": true, - "requires": { - "xtend": "~4.0.0" - } - }, - "deferred-leveldown": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-4.0.2.tgz", - "integrity": "sha512-5fMC8ek8alH16QiV0lTCis610D1Zt1+LA4MS4d63JgS32lrCjTFDUFz2ao09/j2I4Bqb5jL4FZYwu7Jz0XO1ww==", - "dev": true, - "requires": { - "abstract-leveldown": "~5.0.0", - "inherits": "^2.0.3" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, "level-iterator-stream": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/level-iterator-stream/-/level-iterator-stream-3.0.1.tgz", @@ -12491,70 +11660,6 @@ "readable-stream": "^2.3.6", "xtend": "^4.0.0" } - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "liftoff": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz", - "integrity": "sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog==", - "requires": { - "extend": "^3.0.0", - "findup-sync": "^3.0.0", - "fined": "^1.0.1", - "flagged-respawn": "^1.0.0", - "is-plain-object": "^2.0.4", - "object.map": "^1.0.0", - "rechoir": "^0.6.2", - "resolve": "^1.1.7" - } - }, - "load-json-file": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", - "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" } } }, @@ -12601,78 +11706,26 @@ "integrity": "sha1-EIUaBtmWS5cReEQcI8nlJpjuzjQ=", "dev": true }, - "make-dir": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", - "requires": { - "pify": "^3.0.0" - }, - "dependencies": { - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" - } - } - }, - "make-iterator": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", - "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", - "requires": { - "kind-of": "^6.0.2" - } - }, "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true }, "map-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, "requires": { "object-visit": "^1.0.0" } }, - "matchdep": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz", - "integrity": "sha1-xvNINKDY28OzfCfui7yyfHd1WC4=", - "requires": { - "findup-sync": "^2.0.0", - "micromatch": "^3.0.4", - "resolve": "^1.4.0", - "stack-trace": "0.0.10" - }, - "dependencies": { - "findup-sync": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", - "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", - "requires": { - "detect-file": "^1.0.0", - "is-glob": "^3.1.0", - "micromatch": "^3.0.4", - "resolve-dir": "^1.0.1" - } - }, - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "requires": { - "is-extglob": "^2.1.0" - } - } - } - }, "md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, "requires": { "hash-base": "^3.0.0", "inherits": "^2.0.1", @@ -12708,23 +11761,6 @@ "semaphore": ">=1.0.1" }, "dependencies": { - "abstract-leveldown": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-2.7.2.tgz", - "integrity": "sha512-+OVvxH2rHVEhWLdbudP6p0+dNMXu8JA1CbhP19T8paTYAcX7oJ4OVjT+ZUVpv7mITxXHqDMej+GdqXBmXkw09w==", - "requires": { - "xtend": "~4.0.0" - } - }, - "async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", - "dev": true, - "requires": { - "lodash": "^4.17.14" - } - }, "ethereumjs-util": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", @@ -12740,55 +11776,6 @@ "safe-buffer": "^5.1.1" } }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "level-codec": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/level-codec/-/level-codec-7.0.1.tgz", - "integrity": "sha512-Ua/R9B9r3RasXdRmOtd+t9TCOEIIlts+TN/7XTT2unhDaL6sJn83S3rUyljbr6lVtw49N3/yA0HHjpV6Kzb2aQ==" - }, - "level-errors": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/level-errors/-/level-errors-1.0.5.tgz", - "integrity": "sha512-/cLUpQduF6bNrWuAC4pwtUKA5t669pCsCi2XbmojG2tFeOr9j6ShtdDCtFFQO1DRt+EVZhx9gPzP9G2bUaG4ig==", - "requires": { - "errno": "~0.1.1" - } - }, - "levelup": { - "version": "1.3.9", - "resolved": "https://registry.npmjs.org/levelup/-/levelup-1.3.9.tgz", - "integrity": "sha512-VVGHfKIlmw8w1XqpGOAGwq6sZm2WwWLmlDcULkKWQXEA5EopA8OBNJ2Ck2v6bdk8HeEZSbCSEgzXadyQFm76sQ==", - "requires": { - "level-codec": "~7.0.0", - "level-errors": "~1.0.3", - "prr": "~1.0.1", - "semver": "~5.4.1", - "xtend": "~4.0.0" - } - }, - "memdown": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/memdown/-/memdown-1.4.1.tgz", - "integrity": "sha1-tOThkhdGZP+65BNhqlAPMRnv4hU=", - "requires": { - "abstract-leveldown": "~2.7.1", - "functional-red-black-tree": "^1.0.1", - "immediate": "^3.2.3", - "inherits": "~2.0.1", - "safe-buffer": "~5.1.1" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } - } - }, "readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -12798,35 +11785,6 @@ "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } - } - }, - "semver": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - } } } } @@ -12838,26 +11796,6 @@ "dev": true, "optional": true }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, "miller-rabin": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", @@ -12909,17 +11847,20 @@ "minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true }, "minimalistic-crypto-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -12930,17 +11871,6 @@ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true }, - "minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, "minizlib": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", @@ -12949,25 +11879,29 @@ "optional": true, "requires": { "minipass": "^2.9.0" + }, + "dependencies": { + "minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + } } }, "mixin-deep": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, "requires": { "for-in": "^1.0.2", "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "requires": { - "is-plain-object": "^2.0.4" - } - } } }, "mkdirp": { @@ -12975,7 +11909,6 @@ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", "dev": true, - "optional": true, "requires": { "minimist": "^1.2.5" } @@ -13003,15 +11936,51 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "mute-stdout": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz", - "integrity": "sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg==" + "multibase": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/multibase/-/multibase-0.6.1.tgz", + "integrity": "sha512-pFfAwyTjbbQgNc3G7D48JkJxWtoJoBMaR4xQUOuB8RnCgRqaYmWNFeJTTvrJ2w51bjLq2zTby6Rqj9TQ9elSUw==", + "dev": true, + "optional": true, + "requires": { + "base-x": "^3.0.8", + "buffer": "^5.5.0" + } }, - "nan": { - "version": "2.14.1", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", - "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==" + "multicodec": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-0.5.7.tgz", + "integrity": "sha512-PscoRxm3f+88fAtELwUnZxGDkduE2HD9Q6GHUOywQLjOGT/HAdhjLDYNZ1e7VR0s0TP0EwZ16LNUTFpoBGivOA==", + "dev": true, + "optional": true, + "requires": { + "varint": "^5.0.0" + } + }, + "multihashes": { + "version": "0.4.21", + "resolved": "https://registry.npmjs.org/multihashes/-/multihashes-0.4.21.tgz", + "integrity": "sha512-uVSvmeCWf36pU2nB4/1kzYZjsXD9vofZKpgudqkceYY5g2aZZXJ5r9lxuzoRLl1OAp28XljXsEJ/X/85ZsKmKw==", + "dev": true, + "optional": true, + "requires": { + "buffer": "^5.5.0", + "multibase": "^0.7.0", + "varint": "^5.0.0" + }, + "dependencies": { + "multibase": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/multibase/-/multibase-0.7.0.tgz", + "integrity": "sha512-TW8q03O0f6PNFTQDvh3xxH03c8CjGaaYrjkl9UQPG6rz53TQzzxJVCIWVjzcbN/Q5Y53Zd0IBQBMVktVgNx4Fg==", + "dev": true, + "optional": true, + "requires": { + "base-x": "^3.0.8", + "buffer": "^5.5.0" + } + } + } }, "nano-json-stream-parser": { "version": "0.1.2", @@ -13024,6 +11993,7 @@ "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, "requires": { "arr-diff": "^4.0.0", "array-unique": "^0.3.2", @@ -13048,7 +12018,14 @@ "next-tick": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true }, "node-addon-api": { "version": "2.0.2", @@ -13068,29 +12045,6 @@ "integrity": "sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg==", "dev": true }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - } - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" - }, "normalize-url": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", @@ -13098,19 +12052,6 @@ "dev": true, "optional": true }, - "now-and-later": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.1.tgz", - "integrity": "sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ==", - "requires": { - "once": "^1.3.2" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, "number-to-bn": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz", @@ -13140,12 +12081,14 @@ "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true }, "object-copy": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, "requires": { "copy-descriptor": "^0.1.0", "define-property": "^0.2.5", @@ -13155,12 +12098,60 @@ "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=" + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, "requires": { "is-buffer": "^1.1.5" } @@ -13170,7 +12161,8 @@ "object-inspect": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", - "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==" + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", + "dev": true }, "object-is": { "version": "1.1.3", @@ -13214,6 +12206,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, "requires": { "isobject": "^3.0.0" } @@ -13222,6 +12215,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz", "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==", + "dev": true, "requires": { "define-properties": "^1.1.3", "es-abstract": "^1.18.0-next.0", @@ -13233,6 +12227,7 @@ "version": "1.18.0-next.1", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "dev": true, "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", @@ -13247,25 +12242,9 @@ "string.prototype.trimend": "^1.0.1", "string.prototype.trimstart": "^1.0.1" } - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" } } }, - "object.defaults": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", - "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", - "requires": { - "array-each": "^1.0.1", - "array-slice": "^1.0.0", - "for-own": "^1.0.0", - "isobject": "^3.0.0" - } - }, "object.getownpropertydescriptors": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", @@ -13276,32 +12255,15 @@ "es-abstract": "^1.17.0-next.1" } }, - "object.map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", - "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", - "requires": { - "for-own": "^1.0.0", - "make-iterator": "^1.0.0" - } - }, "object.pick": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, "requires": { "isobject": "^3.0.1" } }, - "object.reduce": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object.reduce/-/object.reduce-1.0.1.tgz", - "integrity": "sha1-b+NI8qx/oPlcpiEiZZkJaCW7A60=", - "requires": { - "for-own": "^1.0.0", - "make-iterator": "^1.0.0" - } - }, "oboe": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/oboe/-/oboe-2.1.4.tgz", @@ -13326,66 +12288,17 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, "requires": { "wrappy": "1" } }, - "ordered-read-streams": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz", - "integrity": "sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4=", - "requires": { - "readable-stream": "^2.0.1" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, "os-homedir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true }, - "os-locale": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", - "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", - "requires": { - "lcid": "^1.0.0" - } - }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", @@ -13399,13 +12312,6 @@ "dev": true, "optional": true }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true, - "optional": true - }, "p-timeout": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz", @@ -13414,6 +12320,15 @@ "optional": true, "requires": { "p-finally": "^1.0.0" + }, + "dependencies": { + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true, + "optional": true + } } }, "parse-asn1": { @@ -13430,40 +12345,12 @@ "safe-buffer": "^5.1.1" } }, - "parse-filepath": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", - "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", - "requires": { - "is-absolute": "^1.0.0", - "map-cache": "^0.2.0", - "path-root": "^0.1.1" - } - }, "parse-headers": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.3.tgz", "integrity": "sha512-QhhZ+DCCit2Coi2vmAKbq5RGTRcQUOE2+REgv8vdyu7MnYx2eZztegqtTx99TZ86GTIwqiy3+4nQTWZ2tgmdCA==", "dev": true }, - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "requires": { - "error-ex": "^1.2.0" - } - }, - "parse-node-version": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", - "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==" - }, - "parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=" - }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -13474,43 +12361,106 @@ "pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true }, - "path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=" - }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "patch-package": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-6.2.2.tgz", + "integrity": "sha512-YqScVYkVcClUY0v8fF0kWOjDYopzIM8e3bj/RU1DPeEF14+dCGm6UeOYm4jvCyxqIEQ5/eJzmbWfDWnUleFNMg==", + "dev": true, "requires": { - "pinkie-promise": "^2.0.0" + "@yarnpkg/lockfile": "^1.1.0", + "chalk": "^2.4.2", + "cross-spawn": "^6.0.5", + "find-yarn-workspace-root": "^1.2.1", + "fs-extra": "^7.0.1", + "is-ci": "^2.0.0", + "klaw-sync": "^6.0.0", + "minimist": "^1.2.0", + "rimraf": "^2.6.3", + "semver": "^5.6.0", + "slash": "^2.0.0", + "tmp": "^0.0.33" + }, + "dependencies": { + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } } }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true }, "path-parse": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" - }, - "path-root": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", - "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", - "requires": { - "path-root-regex": "^0.1.0" - } - }, - "path-root-regex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", - "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=" + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true }, "path-to-regexp": { "version": "0.1.7", @@ -13519,27 +12469,11 @@ "dev": true, "optional": true }, - "path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", - "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" - } - } - }, "pbkdf2": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz", "integrity": "sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==", + "dev": true, "requires": { "create-hash": "^1.1.2", "create-hmac": "^1.1.4", @@ -13548,34 +12482,17 @@ "sha.js": "^2.4.8" } }, - "pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" - }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", "dev": true }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "requires": { - "pinkie": "^2.0.0" - } - }, "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true }, "precond": { "version": "0.2.3", @@ -13590,27 +12507,17 @@ "dev": true, "optional": true }, - "pretty-hrtime": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", - "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=" - }, "private": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", "dev": true }, - "process": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz", - "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=", - "dev": true - }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true }, "promise-to-callback": { "version": "1.0.0", @@ -13636,7 +12543,8 @@ "prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true }, "pseudomap": { "version": "1.0.2", @@ -13727,32 +12635,13 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "optional": true, "requires": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, - "pumpify": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", - "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", - "requires": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" - }, - "dependencies": { - "pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - } - } - }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -13762,7 +12651,8 @@ "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true }, "query-string": { "version": "5.1.1", @@ -13816,25 +12706,6 @@ "unpipe": "1.0.0" } }, - "read-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", - "requires": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" - } - }, - "read-pkg-up": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", - "requires": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" - } - }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", @@ -13858,58 +12729,6 @@ } } }, - "readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", - "requires": { - "resolve": "^1.1.6" - } - }, "regenerate": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.1.tgz", @@ -13937,6 +12756,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, "requires": { "extend-shallow": "^3.0.2", "safe-regex": "^1.1.0" @@ -13976,41 +12796,27 @@ "dev": true, "requires": { "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true + } } }, - "remove-bom-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz", - "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==", - "requires": { - "is-buffer": "^1.1.5", - "is-utf8": "^0.2.1" - } - }, - "remove-bom-stream": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz", - "integrity": "sha1-BfGlk/FuQuH7kOv1nejlaVJflSM=", - "requires": { - "remove-bom-buffer": "^3.0.0", - "safe-buffer": "^5.1.0", - "through2": "^2.0.3" - } - }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" - }, "repeat-element": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==" + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true }, "repeat-string": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true }, "repeating": { "version": "2.0.1", @@ -14021,21 +12827,6 @@ "is-finite": "^1.0.0" } }, - "replace-ext": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", - "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==" - }, - "replace-homedir": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/replace-homedir/-/replace-homedir-1.0.0.tgz", - "integrity": "sha1-6H9tUTuSjd6AgmDBK+f+xv9ueYw=", - "requires": { - "homedir-polyfill": "^1.0.1", - "is-absolute": "^1.0.0", - "remove-trailing-separator": "^1.1.0" - } - }, "request": { "version": "2.88.2", "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", @@ -14062,55 +12853,22 @@ "tough-cookie": "~2.5.0", "tunnel-agent": "^0.6.0", "uuid": "^3.3.2" - }, - "dependencies": { - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true - } } }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" - }, - "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" - }, "resolve": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dev": true, "requires": { "path-parse": "^1.0.6" } }, - "resolve-dir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", - "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", - "requires": { - "expand-tilde": "^2.0.0", - "global-modules": "^1.0.0" - } - }, - "resolve-options": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-1.1.0.tgz", - "integrity": "sha1-MrueOcBtZzONyTeMDW1gdFZq0TE=", - "requires": { - "value-or-function": "^3.0.0" - } - }, "resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true }, "responselike": { "version": "1.0.2", @@ -14134,7 +12892,8 @@ "ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true }, "rimraf": { "version": "2.6.3", @@ -14149,6 +12908,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, "requires": { "hash-base": "^3.0.0", "inherits": "^2.0.1" @@ -14172,7 +12932,8 @@ "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true }, "safe-event-emitter": { "version": "1.0.1", @@ -14187,6 +12948,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, "requires": { "ret": "~0.1.10" } @@ -14203,18 +12965,12 @@ "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==", "dev": true }, - "scrypt.js": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/scrypt.js/-/scrypt.js-0.3.0.tgz", - "integrity": "sha512-42LTc1nyFsyv/o0gcHtDztrn+aqpkaCNt5Qh7ATBZfhEZU7IC/0oT/qbBH+uRNoAPvs2fwiOId68FDEoSRA8/A==", - "requires": { - "scryptsy": "^1.2.1" - } - }, "scryptsy": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/scryptsy/-/scryptsy-1.2.1.tgz", "integrity": "sha1-oyJfpLJST4AnAHYeKFW987LZIWM=", + "dev": true, + "optional": true, "requires": { "pbkdf2": "^3.0.3" } @@ -14236,28 +12992,12 @@ "integrity": "sha512-1/02Y/rUeU1CJBAGLebiC5Lbo5FnB22gQbIFFYTLkwvp1xdABZJH1sn4ZT1MzXmPpzv+Rf/Lu2NcsLJiK4rcDg==", "dev": true }, - "seek-bzip": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.6.tgz", - "integrity": "sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ==", - "requires": { - "commander": "^2.8.1" - } - }, "semaphore": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/semaphore/-/semaphore-1.1.0.tgz", "integrity": "sha512-O4OZEaNtkMd/K0i6js9SL+gqy0ZCBMgUvlSqHKi4IBdjhe7wB8pwztUk1BbZ1fmrvpwFrPbHzqd2w5pTcJH6LA==", "dev": true }, - "semver-greatest-satisfied-range": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz", - "integrity": "sha1-E+jCZYq5aRywzXEJMkAoDTb3els=", - "requires": { - "sver-compat": "^1.5.0" - } - }, "send": { "version": "0.17.1", "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", @@ -14335,11 +13075,6 @@ "xhr": "^2.3.3" } }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, "set-immediate-shim": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", @@ -14350,8 +13085,10 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, "requires": { "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", "is-plain-object": "^2.0.3", "split-string": "^3.0.1" }, @@ -14359,7 +13096,17 @@ "extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=" + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true } } }, @@ -14380,6 +13127,7 @@ "version": "2.4.11", "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, "requires": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" @@ -14404,16 +13152,11 @@ "simple-concat": "^1.0.0" } }, - "slash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", - "dev": true - }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, "requires": { "base": "^0.11.1", "debug": "^2.2.0", @@ -14429,6 +13172,7 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, "requires": { "ms": "2.0.0" } @@ -14436,22 +13180,95 @@ "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=" + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } }, "extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=" + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true } } }, @@ -14459,6 +13276,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, "requires": { "define-property": "^1.0.0", "isobject": "^3.0.0", @@ -14469,35 +13287,10 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, "requires": { "is-descriptor": "^1.0.0" } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } } } }, @@ -14505,14 +13298,22 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, "requires": { "kind-of": "^3.2.0" }, "dependencies": { + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, "requires": { "is-buffer": "^1.1.5" } @@ -14522,12 +13323,14 @@ "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true }, "source-map-resolve": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "dev": true, "requires": { "atob": "^2.1.2", "decode-uri-component": "^0.2.0", @@ -14557,45 +13360,14 @@ "source-map-url": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=" - }, - "sparkles": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz", - "integrity": "sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==" - }, - "spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" - }, - "spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", - "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==" + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true }, "split-string": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, "requires": { "extend-shallow": "^3.0.0" } @@ -14625,15 +13397,11 @@ } } }, - "stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" - }, "static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, "requires": { "define-property": "^0.2.5", "object-copy": "^0.1.0" @@ -14642,7 +13410,74 @@ "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=" + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true } } }, @@ -14653,16 +13488,6 @@ "dev": true, "optional": true }, - "stream-exhaust": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz", - "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==" - }, - "stream-shift": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", - "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" - }, "stream-to-pull-stream": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/stream-to-pull-stream/-/stream-to-pull-stream-1.7.3.tgz", @@ -14688,16 +13513,6 @@ "dev": true, "optional": true }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, "string.prototype.trim": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.2.tgz", @@ -14734,6 +13549,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", + "dev": true, "requires": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5" @@ -14743,6 +13559,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", + "dev": true, "requires": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5" @@ -14752,6 +13569,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, "requires": { "safe-buffer": "~5.1.0" }, @@ -14759,34 +13577,11 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true } } }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "requires": { - "is-utf8": "^0.2.0" - } - }, - "strip-dirs": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz", - "integrity": "sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==", - "requires": { - "is-natural-number": "^4.0.1" - } - }, "strip-hex-prefix": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", @@ -14800,19 +13595,11 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, "requires": { "has-flag": "^3.0.0" } }, - "sver-compat": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/sver-compat/-/sver-compat-1.5.0.tgz", - "integrity": "sha1-PPh9/rTQe0o/FIJ7wYaz/QxkXNg=", - "requires": { - "es6-iterator": "^2.0.1", - "es6-symbol": "^3.1.1" - } - }, "swarm-js": { "version": "0.1.40", "resolved": "https://registry.npmjs.org/swarm-js/-/swarm-js-0.1.40.tgz", @@ -14875,6 +13662,13 @@ "url-to-options": "^1.0.1" } }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true, + "optional": true + }, "p-cancelable": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz", @@ -14971,62 +13765,25 @@ "yallist": "^3.0.3" }, "dependencies": { - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "fs-minipass": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", "dev": true, "optional": true, "requires": { - "minimist": "^1.2.5" - } - } - } - }, - "tar-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", - "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", - "requires": { - "bl": "^1.0.0", - "buffer-alloc": "^1.2.0", - "end-of-stream": "^1.0.0", - "fs-constants": "^1.0.0", - "readable-stream": "^2.3.0", - "to-buffer": "^1.1.1", - "xtend": "^4.0.0" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "minipass": "^2.6.0" } }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "dev": true, + "optional": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" } } } @@ -15034,65 +13791,19 @@ "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true }, "through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, "requires": { "readable-stream": "~2.3.6", "xtend": "~4.0.1" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } } }, - "through2-filter": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz", - "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", - "requires": { - "through2": "~2.0.0", - "xtend": "~4.0.0" - } - }, - "time-stamp": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", - "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=" - }, "timed-out": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", @@ -15109,38 +13820,26 @@ "rimraf": "^2.6.3" } }, - "to-absolute-glob": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", - "integrity": "sha1-GGX0PZ50sIItufFFt4z/fQ98hJs=", - "requires": { - "is-absolute": "^1.0.0", - "is-negated-glob": "^1.0.0" - } - }, - "to-buffer": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", - "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==" - }, - "to-fast-properties": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", - "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", - "dev": true - }, "to-object-path": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, "requires": { "kind-of": "^3.0.2" }, "dependencies": { + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, "requires": { "is-buffer": "^1.1.5" } @@ -15158,6 +13857,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, "requires": { "define-property": "^2.0.2", "extend-shallow": "^3.0.2", @@ -15165,23 +13865,6 @@ "safe-regex": "^1.1.0" } }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - }, - "to-through": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-through/-/to-through-2.0.0.tgz", - "integrity": "sha1-/JKtq6ByZHvAtn1rA2ZKoZUJOvY=", - "requires": { - "through2": "^2.0.3" - } - }, "toidentifier": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", @@ -15229,7 +13912,8 @@ "type": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", + "dev": true }, "type-is": { "version": "1.6.18", @@ -15245,12 +13929,14 @@ "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true }, "typedarray-to-buffer": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, "requires": { "is-typedarray": "^1.0.0" } @@ -15283,20 +13969,6 @@ "dev": true, "optional": true }, - "unbzip2-stream": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", - "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", - "requires": { - "buffer": "^5.2.1", - "through": "^2.3.8" - } - }, - "unc-path-regex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", - "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=" - }, "underscore": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz", @@ -15304,50 +13976,31 @@ "dev": true, "optional": true }, - "undertaker": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.2.1.tgz", - "integrity": "sha512-71WxIzDkgYk9ZS+spIB8iZXchFhAdEo2YU8xYqBYJ39DIUIqziK78ftm26eecoIY49X0J2MLhG4hr18Yp6/CMA==", - "requires": { - "arr-flatten": "^1.0.1", - "arr-map": "^2.0.0", - "bach": "^1.0.0", - "collection-map": "^1.0.0", - "es6-weak-map": "^2.0.1", - "last-run": "^1.1.0", - "object.defaults": "^1.0.0", - "object.reduce": "^1.0.0", - "undertaker-registry": "^1.0.0" - } - }, - "undertaker-registry": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-1.0.1.tgz", - "integrity": "sha1-XkvaMI5KiirlhPm5pDWaSZglzFA=" - }, "union-value": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, "requires": { "arr-union": "^3.1.0", "get-value": "^2.0.6", + "is-extendable": "^0.1.1", "set-value": "^2.0.1" - } - }, - "unique-stream": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz", - "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==", - "requires": { - "json-stable-stringify-without-jsonify": "^1.0.1", - "through2-filter": "^3.0.0" + }, + "dependencies": { + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + } } }, "universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true }, "unorm": { "version": "1.6.0", @@ -15366,6 +14019,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, "requires": { "has-value": "^0.3.1", "isobject": "^3.0.0" @@ -15375,6 +14029,7 @@ "version": "0.3.1", "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, "requires": { "get-value": "^2.0.3", "has-values": "^0.1.4", @@ -15385,6 +14040,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, "requires": { "isarray": "1.0.0" } @@ -15394,20 +14050,11 @@ "has-values": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true } } }, - "upath": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==" - }, "uri-js": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", @@ -15420,7 +14067,8 @@ "urix": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true }, "url-parse-lax": { "version": "3.0.0", @@ -15449,7 +14097,25 @@ "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "utf-8-validate": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.2.tgz", + "integrity": "sha512-SwV++i2gTD5qh2XqaPzBnNX88N6HdyhQrNNRykvcS0QKvItV9u3vPEJr+X5Hhfb1JC0r0e1alL0iB09rY8+nmw==", + "dev": true, + "requires": { + "node-gyp-build": "~3.7.0" + }, + "dependencies": { + "node-gyp-build": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-3.7.0.tgz", + "integrity": "sha512-L/Eg02Epx6Si2NXmedx+Okg+4UHqmaf3TNcxd50SF9NQGcJaON3AtU++kax69XV7YWz4tUspqZSAsVofhFKG2w==", + "dev": true + } + } }, "utf8": { "version": "3.0.0", @@ -15461,7 +14127,8 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true }, "util.promisify": { "version": "1.0.1", @@ -15488,27 +14155,12 @@ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", "dev": true }, - "v8flags": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz", - "integrity": "sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg==", - "requires": { - "homedir-polyfill": "^1.0.1" - } - }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "value-or-function": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-3.0.0.tgz", - "integrity": "sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM=" + "varint": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/varint/-/varint-5.0.2.tgz", + "integrity": "sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow==", + "dev": true, + "optional": true }, "vary": { "version": "1.1.2", @@ -15528,101 +14180,6 @@ "extsprintf": "^1.2.0" } }, - "vinyl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz", - "integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==", - "requires": { - "clone": "^2.1.1", - "clone-buffer": "^1.0.0", - "clone-stats": "^1.0.0", - "cloneable-readable": "^1.0.0", - "remove-trailing-separator": "^1.0.1", - "replace-ext": "^1.0.0" - } - }, - "vinyl-fs": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-3.0.3.tgz", - "integrity": "sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng==", - "requires": { - "fs-mkdirp-stream": "^1.0.0", - "glob-stream": "^6.1.0", - "graceful-fs": "^4.0.0", - "is-valid-glob": "^1.0.0", - "lazystream": "^1.0.0", - "lead": "^1.0.0", - "object.assign": "^4.0.4", - "pumpify": "^1.3.5", - "readable-stream": "^2.3.3", - "remove-bom-buffer": "^3.0.0", - "remove-bom-stream": "^1.2.0", - "resolve-options": "^1.1.0", - "through2": "^2.0.0", - "to-through": "^2.0.0", - "value-or-function": "^3.0.0", - "vinyl": "^2.0.0", - "vinyl-sourcemap": "^1.1.0" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "vinyl-sourcemap": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz", - "integrity": "sha1-kqgAWTo4cDqM2xHYswCtS+Y7PhY=", - "requires": { - "append-buffer": "^1.0.2", - "convert-source-map": "^1.5.0", - "graceful-fs": "^4.1.6", - "normalize-path": "^2.1.1", - "now-and-later": "^2.0.0", - "remove-bom-buffer": "^3.0.0", - "vinyl": "^2.0.0" - }, - "dependencies": { - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "requires": { - "remove-trailing-separator": "^1.0.1" - } - } - } - }, "web3": { "version": "1.2.11", "resolved": "https://registry.npmjs.org/web3/-/web3-1.2.11.tgz", @@ -15637,13 +14194,6 @@ "web3-net": "1.2.11", "web3-shh": "1.2.11", "web3-utils": "1.2.11" - }, - "dependencies": { - "@types/node": { - "version": "12.12.53", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.53.tgz", - "integrity": "sha512-51MYTDTyCziHb70wtGNFRwB4l+5JNvdqzFSkbDvpbftEgVUBEE+T5f7pROhWMp/fxp07oNIEQZd5bbfAH22ohQ==" - } } }, "web3-bzz": { @@ -15788,75 +14338,6 @@ "@ethersproject/abi": "5.0.0-beta.153", "underscore": "1.9.1", "web3-utils": "1.2.11" - }, - "dependencies": { - "@types/node": { - "version": "10.17.28", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.28.tgz", - "integrity": "sha512-dzjES1Egb4c1a89C7lKwQh8pwjYmlOAG9dW1pBgxEk57tMrLnssOfEthz8kdkNaBd7lIqQx7APm5+mZ619IiCQ==" - }, - "aes-js": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", - "integrity": "sha1-4h3xCtbCBTKVvLuNq0Cwnb6ofk0=" - }, - "elliptic": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.3.3.tgz", - "integrity": "sha1-VILZZG1UvLif19mU/J4ulWiHbj8=", - "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", - "hash.js": "^1.0.0", - "inherits": "^2.0.1" - } - }, - "ethers": { - "version": "4.0.0-beta.3", - "resolved": "https://registry.npmjs.org/ethers/-/ethers-4.0.0-beta.3.tgz", - "integrity": "sha512-YYPogooSknTwvHg3+Mv71gM/3Wcrx+ZpCzarBj3mqs9njjRkrOo2/eufzhHloOCo3JSoNI4TQJJ6yU5ABm3Uog==", - "requires": { - "@types/node": "^10.3.2", - "aes-js": "3.0.0", - "bn.js": "^4.4.0", - "elliptic": "6.3.3", - "hash.js": "1.1.3", - "js-sha3": "0.5.7", - "scrypt-js": "2.0.3", - "setimmediate": "1.0.4", - "uuid": "2.0.1", - "xmlhttprequest": "1.8.0" - } - }, - "hash.js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", - "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", - "requires": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.0" - } - }, - "js-sha3": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", - "integrity": "sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc=" - }, - "scrypt-js": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-2.0.3.tgz", - "integrity": "sha1-uwBAvgMEPamgEqLOqfyfhSz8h9Q=" - }, - "setimmediate": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.4.tgz", - "integrity": "sha1-IOgd5iLUoCWIzgyNqJc8vPHTE48=" - }, - "uuid": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.1.tgz", - "integrity": "sha1-wqMN7bPlNdcsz4LjQ5QaULqFM6w=" - } } }, "web3-eth-accounts": { @@ -15945,15 +14426,6 @@ "requires": { "bn.js": "^4.11.9", "web3-utils": "1.2.11" - }, - "dependencies": { - "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true, - "optional": true - } } }, "web3-eth-personal": { @@ -16048,12 +14520,6 @@ "ethereumjs-util": "^5.1.1" } }, - "ethereum-common": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/ethereum-common/-/ethereum-common-0.2.0.tgz", - "integrity": "sha512-XOnAR/3rntJgbCdGhqdaLIxDLWKLmsZOGhHdBKadEr6gEnJLH52k93Ou+TUdFaPN3hJc3isBZBal3U/XZ15abA==", - "dev": true - }, "ethereumjs-abi": { "version": "git+https://git@github.com/ethereumjs/ethereumjs-abi.git#1ce6a1d64235fabe2aaf827fd606def55693508f", "from": "git+https://github.com/ethereumjs/ethereumjs-abi.git", @@ -16102,6 +14568,14 @@ "ethereumjs-tx": "^1.2.2", "ethereumjs-util": "^5.0.0", "merkle-patricia-tree": "^2.1.2" + }, + "dependencies": { + "ethereum-common": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/ethereum-common/-/ethereum-common-0.2.0.tgz", + "integrity": "sha512-XOnAR/3rntJgbCdGhqdaLIxDLWKLmsZOGhHdBKadEr6gEnJLH52k93Ou+TUdFaPN3hJc3isBZBal3U/XZ15abA==", + "dev": true + } } }, "ethereumjs-tx": { @@ -16112,14 +14586,6 @@ "requires": { "ethereum-common": "^0.0.18", "ethereumjs-util": "^5.0.0" - }, - "dependencies": { - "ethereum-common": { - "version": "0.0.18", - "resolved": "https://registry.npmjs.org/ethereum-common/-/ethereum-common-0.0.18.tgz", - "integrity": "sha1-L9w1dvIykDNYl26znaeDIT/5Uj8=", - "dev": true - } } }, "ethereumjs-util": { @@ -16306,13 +14772,36 @@ "prr": "~1.0.1", "semver": "~5.4.1", "xtend": "~4.0.0" + } + }, + "ltgt": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz", + "integrity": "sha1-81ypHEk/e3PaDgdJUwTxezH4fuU=", + "dev": true + }, + "memdown": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/memdown/-/memdown-1.4.1.tgz", + "integrity": "sha1-tOThkhdGZP+65BNhqlAPMRnv4hU=", + "dev": true, + "requires": { + "abstract-leveldown": "~2.7.1", + "functional-red-black-tree": "^1.0.1", + "immediate": "^3.2.3", + "inherits": "~2.0.1", + "ltgt": "~2.2.0", + "safe-buffer": "~5.1.1" }, "dependencies": { - "semver": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", - "dev": true + "abstract-leveldown": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-2.7.2.tgz", + "integrity": "sha512-+OVvxH2rHVEhWLdbudP6p0+dNMXu8JA1CbhP19T8paTYAcX7oJ4OVjT+ZUVpv7mITxXHqDMej+GdqXBmXkw09w==", + "dev": true, + "requires": { + "xtend": "~4.0.0" + } } } }, @@ -16346,56 +14835,23 @@ "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=", "dev": true }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "semver": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", + "dev": true }, "string_decoder": { "version": "0.10.31", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } - } + "dev": true }, "ws": { "version": "5.2.2", @@ -16474,13 +14930,6 @@ "utf8": "3.0.0" }, "dependencies": { - "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true, - "optional": true - }, "eth-lib": { "version": "0.2.8", "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.8.tgz", @@ -16532,32 +14981,11 @@ "integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng==", "dev": true }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "requires": { - "isexe": "^2.0.0" - } - }, - "which-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", - "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=" - }, - "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - } - }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true }, "ws": { "version": "3.3.3", @@ -16628,69 +15056,23 @@ "cookiejar": "^2.1.1" } }, - "xmlhttprequest": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz", - "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw=" - }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" - }, - "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true }, "yaeti": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", - "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=" + "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=", + "dev": true }, "yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true - }, - "yargs": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.1.tgz", - "integrity": "sha512-huO4Fr1f9PmiJJdll5kwoS2e4GqzGSsMT3PPMpOwoVkOK8ckqAewMTZyA6LXVQWflleb/Z8oPBEvNsMft0XE+g==", - "requires": { - "camelcase": "^3.0.0", - "cliui": "^3.2.0", - "decamelize": "^1.1.1", - "get-caller-file": "^1.0.1", - "os-locale": "^1.4.0", - "read-pkg-up": "^1.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^1.0.2", - "which-module": "^1.0.0", - "y18n": "^3.2.1", - "yargs-parser": "5.0.0-security.0" - } - }, - "yargs-parser": { - "version": "5.0.0-security.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0-security.0.tgz", - "integrity": "sha512-T69y4Ps64LNesYxeYGYPvfoMTt/7y1XtfpIslUeK4um+9Hu7hlGoRtaDLvdXb7+/tfq4opVa2HRY5xGip022rQ==", - "requires": { - "camelcase": "^3.0.0", - "object.assign": "^4.1.0" - } - }, - "yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", - "requires": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } } } }, @@ -17086,7 +15468,8 @@ "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true }, "has-symbol-support-x": { "version": "1.4.2", @@ -17652,7 +16035,8 @@ "is-negative-zero": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz", - "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=" + "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=", + "dev": true }, "is-number": { "version": "7.0.0", @@ -18963,52 +17347,6 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "multibase": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/multibase/-/multibase-0.6.1.tgz", - "integrity": "sha512-pFfAwyTjbbQgNc3G7D48JkJxWtoJoBMaR4xQUOuB8RnCgRqaYmWNFeJTTvrJ2w51bjLq2zTby6Rqj9TQ9elSUw==", - "dev": true, - "optional": true, - "requires": { - "base-x": "^3.0.8", - "buffer": "^5.5.0" - } - }, - "multicodec": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-0.5.7.tgz", - "integrity": "sha512-PscoRxm3f+88fAtELwUnZxGDkduE2HD9Q6GHUOywQLjOGT/HAdhjLDYNZ1e7VR0s0TP0EwZ16LNUTFpoBGivOA==", - "dev": true, - "optional": true, - "requires": { - "varint": "^5.0.0" - } - }, - "multihashes": { - "version": "0.4.21", - "resolved": "https://registry.npmjs.org/multihashes/-/multihashes-0.4.21.tgz", - "integrity": "sha512-uVSvmeCWf36pU2nB4/1kzYZjsXD9vofZKpgudqkceYY5g2aZZXJ5r9lxuzoRLl1OAp28XljXsEJ/X/85ZsKmKw==", - "dev": true, - "optional": true, - "requires": { - "buffer": "^5.5.0", - "multibase": "^0.7.0", - "varint": "^5.0.0" - }, - "dependencies": { - "multibase": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/multibase/-/multibase-0.7.0.tgz", - "integrity": "sha512-TW8q03O0f6PNFTQDvh3xxH03c8CjGaaYrjkl9UQPG6rz53TQzzxJVCIWVjzcbN/Q5Y53Zd0IBQBMVktVgNx4Fg==", - "dev": true, - "optional": true, - "requires": { - "base-x": "^3.0.8", - "buffer": "^5.5.0" - } - } - } - }, "multimatch": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-4.0.0.tgz", @@ -22138,15 +20476,6 @@ "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", "dev": true }, - "utf-8-validate": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.3.tgz", - "integrity": "sha512-jtJM6fpGv8C1SoH4PtG22pGto6x+Y8uPprW0tw3//gGFhDDTiuksgradgFN6yRayDP4SyZZa6ZMGHLIa17+M8A==", - "dev": true, - "requires": { - "node-gyp-build": "^4.2.0" - } - }, "utf8": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", @@ -22193,13 +20522,6 @@ "spdx-expression-parse": "^3.0.0" } }, - "varint": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/varint/-/varint-5.0.2.tgz", - "integrity": "sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow==", - "dev": true, - "optional": true - }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/package.json b/package.json index d3c73712..5d6c1dbb 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "hardhat:main": "hardhat --network main", "hardhat:docker": "hardhat --network hardhatevm_docker", "compile": "SKIP_LOAD=true hardhat compile", + "console:fork": "MAINNET_FORK=true hardhat console", "test": "TS_NODE_TRANSPILE_ONLY=1 hardhat test ./test/*.spec.ts", "test-scenarios": "npm run test -- test/__setup.spec.ts test/scenario.spec.ts", "test-repay-with-collateral": "hardhat test test/__setup.spec.ts test/repay-with-collateral.spec.ts", From e4f5c0defa9ee52170f3d119ebbd64e5741b871b Mon Sep 17 00:00:00 2001 From: David Racero Date: Thu, 7 Jan 2021 13:51:03 +0100 Subject: [PATCH 56/62] rollback package-lock --- package-lock.json | 4802 ++++++++++++++++++++++++++++++--------------- 1 file changed, 3240 insertions(+), 1562 deletions(-) diff --git a/package-lock.json b/package-lock.json index dfdeb1a5..32738e30 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2908,6 +2908,15 @@ "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", "dev": true }, + "bufferutil": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.2.tgz", + "integrity": "sha512-AtnG3W6M8B2n4xDQ5R+70EXvOpnXsFYg/AK2yTZd+HQ/oxAdz+GI+DvjmhBw3L0ole+LJ0ngqY4JMbDzkfNzhA==", + "dev": true, + "requires": { + "node-gyp-build": "^4.2.0" + } + }, "buidler-typechain": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/buidler-typechain/-/buidler-typechain-0.1.1.tgz", @@ -3102,6 +3111,33 @@ "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", "dev": true }, + "cids": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/cids/-/cids-0.7.5.tgz", + "integrity": "sha512-zT7mPeghoWAu+ppn8+BS1tQ5qGmbMfB4AregnQjA/qHY3GC1m1ptI9GkWNlgeu38r7CuRdXB47uY2XgAYt6QVA==", + "dev": true, + "optional": true, + "requires": { + "buffer": "^5.5.0", + "class-is": "^1.1.0", + "multibase": "~0.6.0", + "multicodec": "^1.0.0", + "multihashes": "~0.4.15" + }, + "dependencies": { + "multicodec": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-1.0.4.tgz", + "integrity": "sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg==", + "dev": true, + "optional": true, + "requires": { + "buffer": "^5.6.0", + "varint": "^5.0.0" + } + } + } + }, "cipher-base": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", @@ -3112,6 +3148,13 @@ "safe-buffer": "^5.0.1" } }, + "class-is": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/class-is/-/class-is-1.1.0.tgz", + "integrity": "sha512-rhjH9AG1fvabIDoGRVH587413LPjTZgmDF9fOFCbFJQV4yuocX1mHxxvXI4g3cGwbVY9wAYIoKlg1N79frJKQw==", + "dev": true, + "optional": true + }, "class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", @@ -3214,7 +3257,6 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "requires": { "color-name": "1.1.3" } @@ -3222,8 +3264,7 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "colors": { "version": "1.4.0", @@ -3357,6 +3398,18 @@ } } }, + "content-hash": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/content-hash/-/content-hash-2.5.2.tgz", + "integrity": "sha512-FvIQKy0S1JaWV10sMsA7TRx8bpU+pqPkhbsfvOJAdjRXvYxEckAwQWGwtRjiaJfh+E0DvcWUGqcdjwMGFjsSdw==", + "dev": true, + "optional": true, + "requires": { + "cids": "^0.7.1", + "multicodec": "^0.5.5", + "multihashes": "^0.4.15" + } + }, "content-type": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", @@ -6299,148 +6352,6 @@ "@ethersproject/strings": ">=5.0.0-beta.130" } }, - "@ethersproject/address": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.0.5.tgz", - "integrity": "sha512-DpkQ6rwk9jTefrRsJzEm6nhRiJd9pvhn1xN0rw5N/jswXG5r7BLk/GVA0mMAVWAsYfvi2xSc5L41FMox43RYEA==", - "dev": true, - "optional": true, - "requires": { - "@ethersproject/bignumber": "^5.0.7", - "@ethersproject/bytes": "^5.0.4", - "@ethersproject/keccak256": "^5.0.3", - "@ethersproject/logger": "^5.0.5", - "@ethersproject/rlp": "^5.0.3", - "bn.js": "^4.4.0" - } - }, - "@ethersproject/bignumber": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.0.8.tgz", - "integrity": "sha512-KXFVAFKS1jdTXYN8BE5Oj+ZfPMh28iRdFeNGBVT6cUFdtiPVqeXqc0ggvBqA3A1VoFFGgM7oAeaagA393aORHA==", - "dev": true, - "optional": true, - "requires": { - "@ethersproject/bytes": "^5.0.4", - "@ethersproject/logger": "^5.0.5", - "bn.js": "^4.4.0" - } - }, - "@ethersproject/bytes": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.0.5.tgz", - "integrity": "sha512-IEj9HpZB+ACS6cZ+QQMTqmu/cnUK2fYNE6ms/PVxjoBjoxc6HCraLpam1KuRvreMy0i523PLmjN8OYeikRdcUQ==", - "dev": true, - "optional": true, - "requires": { - "@ethersproject/logger": "^5.0.5" - } - }, - "@ethersproject/constants": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.0.5.tgz", - "integrity": "sha512-foaQVmxp2+ik9FrLUCtVrLZCj4M3Ibgkqvh+Xw/vFRSerkjVSYePApaVE5essxhoSlF1U9oXfWY09QI2AXtgKA==", - "dev": true, - "optional": true, - "requires": { - "@ethersproject/bignumber": "^5.0.7" - } - }, - "@ethersproject/hash": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.0.5.tgz", - "integrity": "sha512-GpI80/h2HDpfNKpCZoxQJCjOQloGnlD5hM1G+tZe8FQDJhEvFjJoPDuWv+NaYjJfOciKS2Axqc4Q4WamdLoUgg==", - "dev": true, - "optional": true, - "requires": { - "@ethersproject/bytes": "^5.0.4", - "@ethersproject/keccak256": "^5.0.3", - "@ethersproject/logger": "^5.0.5", - "@ethersproject/strings": "^5.0.4" - } - }, - "@ethersproject/keccak256": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.0.4.tgz", - "integrity": "sha512-GNpiOUm9PGUxFNqOxYKDQBM0u68bG9XC9iOulEQ8I0tOx/4qUpgVzvgXL6ugxr0RY554Gz/NQsVqknqPzUcxpQ==", - "dev": true, - "optional": true, - "requires": { - "@ethersproject/bytes": "^5.0.4", - "js-sha3": "0.5.7" - } - }, - "@ethersproject/logger": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.0.6.tgz", - "integrity": "sha512-FrX0Vnb3JZ1md/7GIZfmJ06XOAA8r3q9Uqt9O5orr4ZiksnbpXKlyDzQtlZ5Yv18RS8CAUbiKH9vwidJg1BPmQ==", - "dev": true, - "optional": true - }, - "@ethersproject/properties": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.0.4.tgz", - "integrity": "sha512-UdyX3GqBxFt15B0uSESdDNmhvEbK3ACdDXl2soshoPcneXuTswHDeA0LoPlnaZzhbgk4p6jqb4GMms5C26Qu6A==", - "dev": true, - "optional": true, - "requires": { - "@ethersproject/logger": "^5.0.5" - } - }, - "@ethersproject/rlp": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.0.4.tgz", - "integrity": "sha512-5qrrZad7VTjofxSsm7Zg/7Dr4ZOln4S2CqiDdOuTv6MBKnXj0CiBojXyuDy52M8O3wxH0CyE924hXWTDV1PQWQ==", - "dev": true, - "optional": true, - "requires": { - "@ethersproject/bytes": "^5.0.4", - "@ethersproject/logger": "^5.0.5" - } - }, - "@ethersproject/signing-key": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.0.5.tgz", - "integrity": "sha512-Z1wY7JC1HVO4CvQWY2TyTTuAr8xK3bJijZw1a9G92JEmKdv1j255R/0YLBBcFTl2J65LUjtXynNJ2GbArPGi5g==", - "dev": true, - "optional": true, - "requires": { - "@ethersproject/bytes": "^5.0.4", - "@ethersproject/logger": "^5.0.5", - "@ethersproject/properties": "^5.0.3", - "elliptic": "6.5.3" - } - }, - "@ethersproject/strings": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.0.5.tgz", - "integrity": "sha512-JED6WaIV00xM/gvj8vSnd+0VWtDYdidTmavFRCTQakqfz+4tDo6Jz5LHgG+dd45h7ah7ykCHW0C7ZXWEDROCXQ==", - "dev": true, - "optional": true, - "requires": { - "@ethersproject/bytes": "^5.0.4", - "@ethersproject/constants": "^5.0.4", - "@ethersproject/logger": "^5.0.5" - } - }, - "@ethersproject/transactions": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.0.6.tgz", - "integrity": "sha512-htsFhOD+NMBxx676A8ehSuwVV49iqpSB+CkjPZ02tpNew0K6p8g0CZ46Z1ZP946gIHAU80xQ0NACHYrjIUaCFA==", - "dev": true, - "optional": true, - "requires": { - "@ethersproject/address": "^5.0.4", - "@ethersproject/bignumber": "^5.0.7", - "@ethersproject/bytes": "^5.0.4", - "@ethersproject/constants": "^5.0.4", - "@ethersproject/keccak256": "^5.0.3", - "@ethersproject/logger": "^5.0.5", - "@ethersproject/properties": "^5.0.3", - "@ethersproject/rlp": "^5.0.3", - "@ethersproject/signing-key": "^5.0.4" - } - }, "@sindresorhus/is": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", @@ -6458,6 +6369,14 @@ "defer-to-connect": "^1.0.1" } }, + "@types/bignumber.js": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/bignumber.js/-/bignumber.js-5.0.0.tgz", + "integrity": "sha512-0DH7aPGCClywOFaxxjE6UwpN2kQYe9LwuDQMv+zYA97j5GkOMo8e66LYT+a8JYU7jfmUFRZLa9KycxHDsKXJCA==", + "requires": { + "bignumber.js": "*" + } + }, "@types/bn.js": { "version": "4.11.6", "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", @@ -6491,11 +6410,53 @@ "@types/node": "*" } }, - "@yarnpkg/lockfile": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", - "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", - "dev": true + "@web3-js/scrypt-shim": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@web3-js/scrypt-shim/-/scrypt-shim-0.1.0.tgz", + "integrity": "sha512-ZtZeWCc/s0nMcdx/+rZwY1EcuRdemOK9ag21ty9UsHkFxsNb/AaoucUz0iPuyGe0Ku+PFuRmWZG7Z7462p9xPw==", + "requires": { + "scryptsy": "^2.1.0", + "semver": "^6.3.0" + }, + "dependencies": { + "scryptsy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/scryptsy/-/scryptsy-2.1.0.tgz", + "integrity": "sha512-1CdSqHQowJBnMAFyPEBRfqag/YP9OF394FV+4YREIJX4ljD7OxvQRDayyoyyCk+senRjSkP6VnUNQmVQqB6g7w==" + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "@web3-js/websocket": { + "version": "1.0.30", + "resolved": "https://registry.npmjs.org/@web3-js/websocket/-/websocket-1.0.30.tgz", + "integrity": "sha512-fDwrD47MiDrzcJdSeTLF75aCcxVVt8B1N74rA+vh2XCAvFy4tEWJjtnUtj2QG7/zlQ6g9cQ88bZFBxwd9/FmtA==", + "requires": { + "debug": "^2.2.0", + "es5-ext": "^0.10.50", + "nan": "^2.14.0", + "typedarray-to-buffer": "^3.1.5", + "yaeti": "^0.0.6" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } }, "abstract-leveldown": { "version": "3.0.0", @@ -6536,32 +6497,112 @@ "uri-js": "^4.2.2" } }, + "ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "requires": { + "ansi-wrap": "^0.1.0" + } + }, + "ansi-gray": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", + "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=", + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "requires": { "color-convert": "^1.9.0" } }, + "ansi-wrap": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", + "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=" + }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "append-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz", + "integrity": "sha1-2CIM9GYIFSXv6lBhTz3mUU36WPE=", + "requires": { + "buffer-equal": "^1.0.0" + } + }, + "archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=" + }, "arr-diff": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" + }, + "arr-filter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/arr-filter/-/arr-filter-1.1.2.tgz", + "integrity": "sha1-Q/3d0JHo7xGqTEXZzcGOLf8XEe4=", + "requires": { + "make-iterator": "^1.0.0" + } }, "arr-flatten": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" + }, + "arr-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/arr-map/-/arr-map-2.0.2.tgz", + "integrity": "sha1-Onc0X/wc814qkYJWAfnljy4kysQ=", + "requires": { + "make-iterator": "^1.0.0" + } }, "arr-union": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" + }, + "array-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", + "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=" }, "array-flatten": { "version": "1.1.1", @@ -6570,11 +6611,63 @@ "dev": true, "optional": true }, + "array-initial": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz", + "integrity": "sha1-L6dLJnOTccOUe9enrcc74zSz15U=", + "requires": { + "array-slice": "^1.0.0", + "is-number": "^4.0.0" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==" + } + } + }, + "array-last": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array-last/-/array-last-1.3.0.tgz", + "integrity": "sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg==", + "requires": { + "is-number": "^4.0.0" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==" + } + } + }, + "array-slice": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", + "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==" + }, + "array-sort": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-sort/-/array-sort-1.0.0.tgz", + "integrity": "sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg==", + "requires": { + "default-compare": "^1.0.0", + "get-value": "^2.0.6", + "kind-of": "^5.0.2" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + } + } + }, "array-unique": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" }, "asn1": { "version": "0.2.4", @@ -6607,8 +6700,7 @@ "assign-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" }, "async": { "version": "2.6.2", @@ -6619,6 +6711,22 @@ "lodash": "^4.17.11" } }, + "async-done": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.2.tgz", + "integrity": "sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.2", + "process-nextick-args": "^2.0.0", + "stream-exhaust": "^1.0.1" + } + }, + "async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==" + }, "async-eventemitter": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/async-eventemitter/-/async-eventemitter-0.2.4.tgz", @@ -6634,6 +6742,14 @@ "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", "dev": true }, + "async-settle": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz", + "integrity": "sha1-HQqRS7Aldb7IqPOnTlCA9yssDGs=", + "requires": { + "async-done": "^1.2.2" + } + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -6643,8 +6759,7 @@ "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" }, "aws-sign2": { "version": "0.7.0", @@ -6669,12 +6784,6 @@ "js-tokens": "^3.0.2" }, "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, "ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", @@ -6700,15 +6809,6 @@ "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", "dev": true }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", @@ -6753,22 +6853,16 @@ "ms": "2.0.0" } }, - "json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", - "dev": true - }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, - "slash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "dev": true } } @@ -6794,6 +6888,12 @@ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true } } }, @@ -7306,6 +7406,21 @@ "source-map-support": "^0.4.15" }, "dependencies": { + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, "source-map-support": { "version": "0.4.18", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", @@ -7366,12 +7481,6 @@ "ms": "2.0.0" } }, - "globals": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", - "dev": true - }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -7390,14 +7499,6 @@ "esutils": "^2.0.2", "lodash": "^4.17.4", "to-fast-properties": "^1.0.3" - }, - "dependencies": { - "to-fast-properties": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", - "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", - "dev": true - } } }, "babelify": { @@ -7416,6 +7517,22 @@ "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", "dev": true }, + "bach": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz", + "integrity": "sha1-Szzpa/JxNPeaG0FKUcFONMO9mIA=", + "requires": { + "arr-filter": "^1.1.1", + "arr-flatten": "^1.0.1", + "arr-map": "^2.0.0", + "array-each": "^1.0.0", + "array-initial": "^1.0.0", + "array-last": "^1.1.1", + "async-done": "^1.2.2", + "async-settle": "^1.0.0", + "now-and-later": "^2.0.0" + } + }, "backoff": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz", @@ -7428,14 +7545,12 @@ "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "base": { "version": "0.11.2", "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, "requires": { "cache-base": "^1.0.1", "class-utils": "^0.3.5", @@ -7450,10 +7565,35 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, "requires": { "is-descriptor": "^1.0.0" } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } } } }, @@ -7461,7 +7601,6 @@ "version": "3.0.8", "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.8.tgz", "integrity": "sha512-Rl/1AWP4J/zRrk54hhlxH4drNxPJXYUaKffODVI53/dAsV4t9fBxyxYKAVPU1XBHxYwOWP9h9H0hM2MVw4YfJA==", - "dev": true, "requires": { "safe-buffer": "^5.0.1" } @@ -7469,8 +7608,7 @@ "base64-js": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", - "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", - "dev": true + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" }, "bcrypt-pbkdf": { "version": "1.0.2", @@ -7492,9 +7630,20 @@ "bignumber.js": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz", - "integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==", - "dev": true, - "optional": true + "integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==" + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==" + }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "requires": { + "file-uri-to-path": "1.0.0" + } }, "bip39": { "version": "2.5.0", @@ -7509,6 +7658,66 @@ "unorm": "^1.3.3" } }, + "bip66": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/bip66/-/bip66-1.1.5.tgz", + "integrity": "sha1-AfqHSHhcpwlV1QESF9GzE5lpyiI=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "bl": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", + "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", + "requires": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + } + } + }, "blakejs": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.1.0.tgz", @@ -7525,8 +7734,7 @@ "bn.js": { "version": "4.11.9", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" }, "body-parser": { "version": "1.19.0", @@ -7577,23 +7785,44 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=" + } + } + }, "brorand": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", - "dev": true + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" }, "browserify-aes": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", - "dev": true, "requires": { "buffer-xor": "^1.0.3", "cipher-base": "^1.0.0", @@ -7692,7 +7921,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", "integrity": "sha1-vhYedsNU9veIrkBx9j806MTwpCo=", - "dev": true, "requires": { "base-x": "^3.0.2" } @@ -7701,7 +7929,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", - "dev": true, "requires": { "bs58": "^4.0.0", "create-hash": "^1.1.0", @@ -7712,17 +7939,44 @@ "version": "5.6.0", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", - "dev": true, "requires": { "base64-js": "^1.0.2", "ieee754": "^1.1.4" } }, + "buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "requires": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==" + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" + }, + "buffer-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz", + "integrity": "sha1-WWFrSYME1Var1GaWayLu2j7KX74=" + }, + "buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=" + }, "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" }, "buffer-to-arraybuffer": { "version": "0.0.5", @@ -7734,25 +7988,7 @@ "buffer-xor": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", - "dev": true - }, - "bufferutil": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.1.tgz", - "integrity": "sha512-xowrxvpxojqkagPcWRQVXZl0YXhRhAtBEIq3VoER1NH5Mw1n1o0ojdspp+GS2J//2gCVyrzQDApQ4unGF+QOoA==", - "dev": true, - "requires": { - "node-gyp-build": "~3.7.0" - }, - "dependencies": { - "node-gyp-build": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-3.7.0.tgz", - "integrity": "sha512-L/Eg02Epx6Si2NXmedx+Okg+4UHqmaf3TNcxd50SF9NQGcJaON3AtU++kax69XV7YWz4tUspqZSAsVofhFKG2w==", - "dev": true - } - } + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" }, "bytes": { "version": "3.1.0", @@ -7784,7 +8020,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, "requires": { "collection-visit": "^1.0.0", "component-emitter": "^1.2.1", @@ -7813,6 +8048,16 @@ "responselike": "^1.0.2" }, "dependencies": { + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "dev": true, + "optional": true, + "requires": { + "pump": "^3.0.0" + } + }, "lowercase-keys": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", @@ -7852,6 +8097,11 @@ } } }, + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" + }, "caniuse-lite": { "version": "1.0.30001146", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001146.tgz", @@ -7868,7 +8118,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -7884,6 +8133,24 @@ "functional-red-black-tree": "^1.0.1" } }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, "chownr": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", @@ -7891,61 +8158,19 @@ "dev": true, "optional": true }, - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true - }, - "cids": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/cids/-/cids-0.7.5.tgz", - "integrity": "sha512-zT7mPeghoWAu+ppn8+BS1tQ5qGmbMfB4AregnQjA/qHY3GC1m1ptI9GkWNlgeu38r7CuRdXB47uY2XgAYt6QVA==", - "dev": true, - "optional": true, - "requires": { - "buffer": "^5.5.0", - "class-is": "^1.1.0", - "multibase": "~0.6.0", - "multicodec": "^1.0.0", - "multihashes": "~0.4.15" - }, - "dependencies": { - "multicodec": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-1.0.4.tgz", - "integrity": "sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg==", - "dev": true, - "optional": true, - "requires": { - "buffer": "^5.6.0", - "varint": "^5.0.0" - } - } - } - }, "cipher-base": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "dev": true, "requires": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" } }, - "class-is": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/class-is/-/class-is-1.1.0.tgz", - "integrity": "sha512-rhjH9AG1fvabIDoGRVH587413LPjTZgmDF9fOFCbFJQV4yuocX1mHxxvXI4g3cGwbVY9wAYIoKlg1N79frJKQw==", - "dev": true, - "optional": true - }, "class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, "requires": { "arr-union": "^3.1.0", "define-property": "^0.2.5", @@ -7956,82 +8181,29 @@ "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=" } } }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, "clone": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", - "dev": true + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=" + }, + "clone-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", + "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=" }, "clone-response": { "version": "1.0.2", @@ -8043,30 +8215,83 @@ "mimic-response": "^1.0.0" } }, + "clone-stats": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=" + }, + "cloneable-readable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", + "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", + "requires": { + "inherits": "^2.0.1", + "process-nextick-args": "^2.0.0", + "readable-stream": "^2.3.5" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "collection-map": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-map/-/collection-map-1.0.0.tgz", + "integrity": "sha1-rqDwb40mx4DCt1SUOFVEsiVa8Yw=", + "requires": { + "arr-map": "^2.0.2", + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + } + }, "collection-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "dev": true, "requires": { "map-visit": "^1.0.0", "object-visit": "^1.0.0" } }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { + "color-support": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" }, "combined-stream": { "version": "1.0.8", @@ -8077,28 +8302,64 @@ "delayed-stream": "~1.0.0" } }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, "component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "concat-stream": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, "requires": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^2.2.2", "typedarray": "^0.0.6" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } } }, "content-disposition": { @@ -8120,18 +8381,6 @@ } } }, - "content-hash": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/content-hash/-/content-hash-2.5.2.tgz", - "integrity": "sha512-FvIQKy0S1JaWV10sMsA7TRx8bpU+pqPkhbsfvOJAdjRXvYxEckAwQWGwtRjiaJfh+E0DvcWUGqcdjwMGFjsSdw==", - "dev": true, - "optional": true, - "requires": { - "cids": "^0.7.1", - "multicodec": "^0.5.5", - "multihashes": "^0.4.15" - } - }, "content-type": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", @@ -8143,7 +8392,6 @@ "version": "1.7.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", - "dev": true, "requires": { "safe-buffer": "~5.1.1" }, @@ -8151,8 +8399,7 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } }, @@ -8180,8 +8427,16 @@ "copy-descriptor": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" + }, + "copy-props": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-2.0.4.tgz", + "integrity": "sha512-7cjuUME+p+S3HZlbllgsn2CDwS+5eCCX16qBgNC4jgSTf49qR1VKy/Zhl400m0IQXl/bPGEVqncgUUMjrr4s8A==", + "requires": { + "each-props": "^1.3.0", + "is-plain-object": "^2.0.1" + } }, "core-js": { "version": "2.6.11", @@ -8198,8 +8453,7 @@ "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "cors": { "version": "2.8.5", @@ -8227,7 +8481,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", - "dev": true, "requires": { "cipher-base": "^1.0.1", "inherits": "^2.0.1", @@ -8240,7 +8493,6 @@ "version": "1.1.7", "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", - "dev": true, "requires": { "cipher-base": "^1.0.3", "create-hash": "^1.1.0", @@ -8284,7 +8536,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "dev": true, "requires": { "es5-ext": "^0.10.50", "type": "^1.0.1" @@ -8308,11 +8559,37 @@ "ms": "^2.1.1" } }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, "decode-uri-component": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "dev": true + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" + }, + "decompress": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress/-/decompress-4.2.1.tgz", + "integrity": "sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ==", + "requires": { + "decompress-tar": "^4.0.0", + "decompress-tarbz2": "^4.0.0", + "decompress-targz": "^4.0.0", + "decompress-unzip": "^4.0.1", + "graceful-fs": "^4.1.10", + "make-dir": "^1.0.0", + "pify": "^2.3.0", + "strip-dirs": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + } + } }, "decompress-response": { "version": "3.3.0", @@ -8324,6 +8601,77 @@ "mimic-response": "^1.0.0" } }, + "decompress-tar": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz", + "integrity": "sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==", + "requires": { + "file-type": "^5.2.0", + "is-stream": "^1.1.0", + "tar-stream": "^1.5.2" + } + }, + "decompress-tarbz2": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz", + "integrity": "sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==", + "requires": { + "decompress-tar": "^4.1.0", + "file-type": "^6.1.0", + "is-stream": "^1.1.0", + "seek-bzip": "^1.0.5", + "unbzip2-stream": "^1.0.9" + }, + "dependencies": { + "file-type": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz", + "integrity": "sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==" + } + } + }, + "decompress-targz": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz", + "integrity": "sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==", + "requires": { + "decompress-tar": "^4.1.1", + "file-type": "^5.2.0", + "is-stream": "^1.1.0" + } + }, + "decompress-unzip": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz", + "integrity": "sha1-3qrM39FK6vhVePczroIQ+bSEj2k=", + "requires": { + "file-type": "^3.8.0", + "get-stream": "^2.2.0", + "pify": "^2.3.0", + "yauzl": "^2.4.2" + }, + "dependencies": { + "file-type": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=" + }, + "get-stream": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", + "integrity": "sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4=", + "requires": { + "object-assign": "^4.0.1", + "pinkie-promise": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + } + } + }, "deep-equal": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", @@ -8336,8 +8684,36 @@ "object-is": "^1.0.1", "object-keys": "^1.1.1", "regexp.prototype.flags": "^1.2.0" + }, + "dependencies": { + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + } } }, + "default-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz", + "integrity": "sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==", + "requires": { + "kind-of": "^5.0.2" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + } + } + }, + "default-resolution": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/default-resolution/-/default-resolution-2.0.0.tgz", + "integrity": "sha1-vLgrqnKtebQmp2cy8aga1t8m1oQ=" + }, "defer-to-connect": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", @@ -8349,7 +8725,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-4.0.2.tgz", "integrity": "sha512-5fMC8ek8alH16QiV0lTCis610D1Zt1+LA4MS4d63JgS32lrCjTFDUFz2ao09/j2I4Bqb5jL4FZYwu7Jz0XO1ww==", - "dev": true, "requires": { "abstract-leveldown": "~5.0.0", "inherits": "^2.0.3" @@ -8359,7 +8734,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-5.0.0.tgz", "integrity": "sha512-5mU5P1gXtsMIXg65/rsYGsi93+MlogXZ9FA8JnwKurHQg64bfXwGYVdVdijNTVNOlAsuIiOwHdvFFD5JqCJQ7A==", - "dev": true, "requires": { "xtend": "~4.0.0" } @@ -8370,19 +8744,52 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, "requires": { "object-keys": "^1.0.12" + }, + "dependencies": { + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + } } }, "define-property": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dev": true, "requires": { "is-descriptor": "^1.0.2", "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } } }, "defined": { @@ -8422,6 +8829,11 @@ "dev": true, "optional": true }, + "detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=" + }, "detect-indent": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", @@ -8458,6 +8870,16 @@ "minimatch": "^3.0.4" } }, + "drbg.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/drbg.js/-/drbg.js-1.0.1.tgz", + "integrity": "sha1-Pja2xCs3BDgjzbwzLVjzHiRFSAs=", + "requires": { + "browserify-aes": "^1.0.6", + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4" + } + }, "duplexer3": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", @@ -8465,6 +8887,60 @@ "dev": true, "optional": true }, + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "each-props": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/each-props/-/each-props-1.3.2.tgz", + "integrity": "sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA==", + "requires": { + "is-plain-object": "^2.0.1", + "object.defaults": "^1.1.0" + } + }, "ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -8492,7 +8968,6 @@ "version": "6.5.3", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", - "dev": true, "requires": { "bn.js": "^4.4.0", "brorand": "^1.0.1", @@ -8558,7 +9033,6 @@ "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, "requires": { "once": "^1.4.0" } @@ -8567,16 +9041,22 @@ "version": "0.1.7", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", - "dev": true, "requires": { "prr": "~1.0.1" } }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "requires": { + "is-arrayish": "^0.2.1" + } + }, "es-abstract": { "version": "1.17.7", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", - "dev": true, "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", @@ -8589,13 +9069,19 @@ "object.assign": "^4.1.1", "string.prototype.trimend": "^1.0.1", "string.prototype.trimstart": "^1.0.1" + }, + "dependencies": { + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + } } }, "es-to-primitive": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, "requires": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", @@ -8606,7 +9092,6 @@ "version": "0.10.53", "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", - "dev": true, "requires": { "es6-iterator": "~2.0.3", "es6-symbol": "~3.1.3", @@ -8617,7 +9102,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "dev": true, "requires": { "d": "1", "es5-ext": "^0.10.35", @@ -8628,12 +9112,22 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "dev": true, "requires": { "d": "^1.0.1", "ext": "^1.1.2" } }, + "es6-weak-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", + "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", + "requires": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + } + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -8644,8 +9138,7 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "esutils": { "version": "2.0.3", @@ -8717,6 +9210,15 @@ "requires": { "idna-uts46-hx": "^2.3.1", "js-sha3": "^0.5.7" + }, + "dependencies": { + "js-sha3": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", + "integrity": "sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc=", + "dev": true, + "optional": true + } } }, "eth-json-rpc-infura": { @@ -8770,6 +9272,12 @@ "abstract-leveldown": "~2.6.0" } }, + "ethereum-common": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/ethereum-common/-/ethereum-common-0.2.0.tgz", + "integrity": "sha512-XOnAR/3rntJgbCdGhqdaLIxDLWKLmsZOGhHdBKadEr6gEnJLH52k93Ou+TUdFaPN3hJc3isBZBal3U/XZ15abA==", + "dev": true + }, "ethereumjs-account": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/ethereumjs-account/-/ethereumjs-account-2.0.5.tgz", @@ -8792,14 +9300,6 @@ "ethereumjs-tx": "^1.2.2", "ethereumjs-util": "^5.0.0", "merkle-patricia-tree": "^2.1.2" - }, - "dependencies": { - "ethereum-common": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/ethereum-common/-/ethereum-common-0.2.0.tgz", - "integrity": "sha512-XOnAR/3rntJgbCdGhqdaLIxDLWKLmsZOGhHdBKadEr6gEnJLH52k93Ou+TUdFaPN3hJc3isBZBal3U/XZ15abA==", - "dev": true - } } }, "ethereumjs-tx": { @@ -8810,6 +9310,14 @@ "requires": { "ethereum-common": "^0.0.18", "ethereumjs-util": "^5.0.0" + }, + "dependencies": { + "ethereum-common": { + "version": "0.0.18", + "resolved": "https://registry.npmjs.org/ethereum-common/-/ethereum-common-0.0.18.tgz", + "integrity": "sha1-L9w1dvIykDNYl26znaeDIT/5Uj8=", + "dev": true + } } }, "ethereumjs-util": { @@ -8996,36 +9504,13 @@ "prr": "~1.0.1", "semver": "~5.4.1", "xtend": "~4.0.0" - } - }, - "ltgt": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz", - "integrity": "sha1-81ypHEk/e3PaDgdJUwTxezH4fuU=", - "dev": true - }, - "memdown": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/memdown/-/memdown-1.4.1.tgz", - "integrity": "sha1-tOThkhdGZP+65BNhqlAPMRnv4hU=", - "dev": true, - "requires": { - "abstract-leveldown": "~2.7.1", - "functional-red-black-tree": "^1.0.1", - "immediate": "^3.2.3", - "inherits": "~2.0.1", - "ltgt": "~2.2.0", - "safe-buffer": "~5.1.1" }, "dependencies": { - "abstract-leveldown": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-2.7.2.tgz", - "integrity": "sha512-+OVvxH2rHVEhWLdbudP6p0+dNMXu8JA1CbhP19T8paTYAcX7oJ4OVjT+ZUVpv7mITxXHqDMej+GdqXBmXkw09w==", - "dev": true, - "requires": { - "xtend": "~4.0.0" - } + "semver": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", + "dev": true } } }, @@ -9059,18 +9544,6 @@ "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=", "dev": true }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "semver": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", - "dev": true - }, "string_decoder": { "version": "0.10.31", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", @@ -9196,6 +9669,12 @@ "abstract-leveldown": "~2.6.0" } }, + "ethereum-common": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/ethereum-common/-/ethereum-common-0.2.0.tgz", + "integrity": "sha512-XOnAR/3rntJgbCdGhqdaLIxDLWKLmsZOGhHdBKadEr6gEnJLH52k93Ou+TUdFaPN3hJc3isBZBal3U/XZ15abA==", + "dev": true + }, "ethereumjs-account": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/ethereumjs-account/-/ethereumjs-account-2.0.5.tgz", @@ -9218,14 +9697,6 @@ "ethereumjs-tx": "^1.2.2", "ethereumjs-util": "^5.0.0", "merkle-patricia-tree": "^2.1.2" - }, - "dependencies": { - "ethereum-common": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/ethereum-common/-/ethereum-common-0.2.0.tgz", - "integrity": "sha512-XOnAR/3rntJgbCdGhqdaLIxDLWKLmsZOGhHdBKadEr6gEnJLH52k93Ou+TUdFaPN3hJc3isBZBal3U/XZ15abA==", - "dev": true - } } }, "ethereumjs-tx": { @@ -9236,6 +9707,14 @@ "requires": { "ethereum-common": "^0.0.18", "ethereumjs-util": "^5.0.0" + }, + "dependencies": { + "ethereum-common": { + "version": "0.0.18", + "resolved": "https://registry.npmjs.org/ethereum-common/-/ethereum-common-0.0.18.tgz", + "integrity": "sha1-L9w1dvIykDNYl26znaeDIT/5Uj8=", + "dev": true + } } }, "ethereumjs-util": { @@ -9422,36 +9901,13 @@ "prr": "~1.0.1", "semver": "~5.4.1", "xtend": "~4.0.0" - } - }, - "ltgt": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz", - "integrity": "sha1-81ypHEk/e3PaDgdJUwTxezH4fuU=", - "dev": true - }, - "memdown": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/memdown/-/memdown-1.4.1.tgz", - "integrity": "sha1-tOThkhdGZP+65BNhqlAPMRnv4hU=", - "dev": true, - "requires": { - "abstract-leveldown": "~2.7.1", - "functional-red-black-tree": "^1.0.1", - "immediate": "^3.2.3", - "inherits": "~2.0.1", - "ltgt": "~2.2.0", - "safe-buffer": "~5.1.1" }, "dependencies": { - "abstract-leveldown": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-2.7.2.tgz", - "integrity": "sha512-+OVvxH2rHVEhWLdbudP6p0+dNMXu8JA1CbhP19T8paTYAcX7oJ4OVjT+ZUVpv7mITxXHqDMej+GdqXBmXkw09w==", - "dev": true, - "requires": { - "xtend": "~4.0.0" - } + "semver": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", + "dev": true } } }, @@ -9485,18 +9941,6 @@ "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=", "dev": true }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "semver": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", - "dev": true - }, "string_decoder": { "version": "0.10.31", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", @@ -9756,36 +10200,13 @@ "prr": "~1.0.1", "semver": "~5.4.1", "xtend": "~4.0.0" - } - }, - "ltgt": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz", - "integrity": "sha1-81ypHEk/e3PaDgdJUwTxezH4fuU=", - "dev": true - }, - "memdown": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/memdown/-/memdown-1.4.1.tgz", - "integrity": "sha1-tOThkhdGZP+65BNhqlAPMRnv4hU=", - "dev": true, - "requires": { - "abstract-leveldown": "~2.7.1", - "functional-red-black-tree": "^1.0.1", - "immediate": "^3.2.3", - "inherits": "~2.0.1", - "ltgt": "~2.2.0", - "safe-buffer": "~5.1.1" }, "dependencies": { - "abstract-leveldown": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-2.7.2.tgz", - "integrity": "sha512-+OVvxH2rHVEhWLdbudP6p0+dNMXu8JA1CbhP19T8paTYAcX7oJ4OVjT+ZUVpv7mITxXHqDMej+GdqXBmXkw09w==", - "dev": true, - "requires": { - "xtend": "~4.0.0" - } + "semver": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", + "dev": true } } }, @@ -9819,18 +10240,6 @@ "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=", "dev": true }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "semver": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", - "dev": true - }, "string_decoder": { "version": "0.10.31", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", @@ -9886,6 +10295,34 @@ "ethereum-cryptography": "^0.1.3", "ethjs-util": "0.1.6", "rlp": "^2.2.3" + }, + "dependencies": { + "keccak": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-2.1.0.tgz", + "integrity": "sha512-m1wbJRTo+gWbctZWay9i26v5fFnYkOn7D5PCxJ3fZUGUEb49dE1Pm4BREUYCt/aoO6di7jeoGmhvqN9Nzylm3Q==", + "requires": { + "bindings": "^1.5.0", + "inherits": "^2.0.4", + "nan": "^2.14.0", + "safe-buffer": "^5.2.0" + } + }, + "secp256k1": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-3.8.0.tgz", + "integrity": "sha512-k5ke5avRZbtl9Tqx/SA7CbY3NF6Ro+Sj9cZxezFzuBlLDmyqPiL8hJJ+EmzD8Ig4LUDByHJ3/iPOVoRixs/hmw==", + "requires": { + "bindings": "^1.5.0", + "bip66": "^1.1.5", + "bn.js": "^4.11.8", + "create-hash": "^1.2.0", + "drbg.js": "^1.0.1", + "elliptic": "^6.5.2", + "nan": "^2.14.0", + "safe-buffer": "^5.1.2" + } + } } }, "ethereumjs-vm": { @@ -10022,36 +10459,13 @@ "prr": "~1.0.1", "semver": "~5.4.1", "xtend": "~4.0.0" - } - }, - "ltgt": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz", - "integrity": "sha1-81ypHEk/e3PaDgdJUwTxezH4fuU=", - "dev": true - }, - "memdown": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/memdown/-/memdown-1.4.1.tgz", - "integrity": "sha1-tOThkhdGZP+65BNhqlAPMRnv4hU=", - "dev": true, - "requires": { - "abstract-leveldown": "~2.7.1", - "functional-red-black-tree": "^1.0.1", - "immediate": "^3.2.3", - "inherits": "~2.0.1", - "ltgt": "~2.2.0", - "safe-buffer": "~5.1.1" }, "dependencies": { - "abstract-leveldown": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-2.7.2.tgz", - "integrity": "sha512-+OVvxH2rHVEhWLdbudP6p0+dNMXu8JA1CbhP19T8paTYAcX7oJ4OVjT+ZUVpv7mITxXHqDMej+GdqXBmXkw09w==", - "dev": true, - "requires": { - "xtend": "~4.0.0" - } + "semver": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", + "dev": true } } }, @@ -10100,18 +10514,6 @@ "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=", "dev": true }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "semver": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", - "dev": true - }, "string_decoder": { "version": "0.10.31", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", @@ -10185,7 +10587,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", - "dev": true, "requires": { "md5.js": "^1.3.4", "safe-buffer": "^5.1.1" @@ -10195,7 +10596,6 @@ "version": "2.1.4", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, "requires": { "debug": "^2.3.3", "define-property": "^0.2.5", @@ -10210,7 +10610,6 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, "requires": { "ms": "2.0.0" } @@ -10218,98 +10617,28 @@ "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=" }, "extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=" }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" } } }, + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, "express": { "version": "4.17.1", "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", @@ -10386,7 +10715,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", - "dev": true, "requires": { "type": "^2.0.0" }, @@ -10394,32 +10722,38 @@ "type": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz", - "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==", - "dev": true + "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==" } } }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, "extend-shallow": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dev": true, "requires": { "assign-symbols": "^1.0.0", "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + } } }, "extglob": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, "requires": { "array-unique": "^0.3.2", "define-property": "^1.0.0", @@ -10435,7 +10769,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, "requires": { "is-descriptor": "^1.0.0" } @@ -10443,17 +10776,33 @@ "extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=" + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "requires": { - "is-extendable": "^0.1.0" + "kind-of": "^6.0.0" } }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } } } }, @@ -10472,6 +10821,17 @@ "checkpoint-store": "^1.1.0" } }, + "fancy-log": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", + "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", + "requires": { + "ansi-gray": "^0.1.1", + "color-support": "^1.1.3", + "parse-node-version": "^1.0.0", + "time-stamp": "^1.0.0" + } + }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -10484,6 +10844,14 @@ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "requires": { + "pend": "~1.2.0" + } + }, "fetch-ponyfill": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/fetch-ponyfill/-/fetch-ponyfill-4.1.0.tgz", @@ -10493,12 +10861,6 @@ "node-fetch": "~1.7.1" }, "dependencies": { - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true - }, "node-fetch": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", @@ -10511,6 +10873,34 @@ } } }, + "file-type": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", + "integrity": "sha1-LdvqfHP/42No365J3DOMBYwritY=" + }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=" + } + } + }, "finalhandler": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", @@ -10546,150 +10936,92 @@ } } }, - "find-yarn-workspace-root": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-1.2.1.tgz", - "integrity": "sha512-dVtfb0WuQG+8Ag2uWkbG79hOUzEsRrhBzgfn86g2sJPkzmcpGdghbNTfUKGTxymFrY/tLIodDzLoW9nOJ4FY8Q==", - "dev": true, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "requires": { - "fs-extra": "^4.0.3", - "micromatch": "^3.1.4" - }, - "dependencies": { - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fs-extra": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", - "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - } + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" } }, + "findup-sync": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", + "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", + "requires": { + "detect-file": "^1.0.0", + "is-glob": "^4.0.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + } + }, + "fined": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz", + "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==", + "requires": { + "expand-tilde": "^2.0.2", + "is-plain-object": "^2.0.3", + "object.defaults": "^1.1.0", + "object.pick": "^1.2.0", + "parse-filepath": "^1.0.1" + } + }, + "flagged-respawn": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", + "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==" + }, "flow-stoplight": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/flow-stoplight/-/flow-stoplight-1.0.0.tgz", "integrity": "sha1-SiksW8/4s5+mzAyxqFPYbyfu/3s=", "dev": true }, + "flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "requires": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, "for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -10702,8 +11034,15 @@ "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" + }, + "for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "requires": { + "for-in": "^1.0.1" + } }, "forever-agent": { "version": "0.6.1", @@ -10733,7 +11072,6 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "dev": true, "requires": { "map-cache": "^0.2.2" } @@ -10745,41 +11083,64 @@ "dev": true, "optional": true }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, "fs-extra": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", - "dev": true, "requires": { "graceful-fs": "^4.1.2", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, + "fs-minipass": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.6.0" + } + }, + "fs-mkdirp-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz", + "integrity": "sha1-C3gV/DIBxqaeFNuYzgmMFpNSWes=", + "requires": { + "graceful-fs": "^4.1.11", + "through2": "^2.0.3" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" }, "get-stream": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "optional": true, "requires": { "pump": "^3.0.0" } @@ -10787,8 +11148,7 @@ "get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" }, "getpass": { "version": "0.1.7", @@ -10803,7 +11163,6 @@ "version": "7.1.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -10813,6 +11172,90 @@ "path-is-absolute": "^1.0.0" } }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "glob-stream": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz", + "integrity": "sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ=", + "requires": { + "extend": "^3.0.0", + "glob": "^7.1.1", + "glob-parent": "^3.1.0", + "is-negated-glob": "^1.0.0", + "ordered-read-streams": "^1.0.0", + "pumpify": "^1.3.5", + "readable-stream": "^2.1.5", + "remove-trailing-separator": "^1.0.1", + "to-absolute-glob": "^2.0.0", + "unique-stream": "^2.0.2" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "glob-watcher": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.5.tgz", + "integrity": "sha512-zOZgGGEHPklZNjZQaZ9f41i7F2YwE+tS5ZHrDhbBCk3stwahn5vQxnFmBJZHoYdusR6R1bLSXeGUy/BhctwKzw==", + "requires": { + "anymatch": "^2.0.0", + "async-done": "^1.2.0", + "chokidar": "^2.0.0", + "is-negated-glob": "^1.0.0", + "just-debounce": "^1.0.0", + "normalize-path": "^3.0.0", + "object.defaults": "^1.1.0" + } + }, "global": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz", @@ -10821,14 +11264,42 @@ "requires": { "min-document": "^2.19.0", "process": "~0.5.1" - }, - "dependencies": { - "process": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz", - "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=", - "dev": true - } + } + }, + "global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "requires": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + } + }, + "global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "requires": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + } + }, + "globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", + "dev": true + }, + "glogg": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz", + "integrity": "sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA==", + "requires": { + "sparkles": "^1.0.0" } }, "got": { @@ -10866,8 +11337,53 @@ "graceful-fs": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" + }, + "gulp": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.2.tgz", + "integrity": "sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA==", + "requires": { + "glob-watcher": "^5.0.3", + "gulp-cli": "^2.2.0", + "undertaker": "^1.2.1", + "vinyl-fs": "^3.0.0" + }, + "dependencies": { + "gulp-cli": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.3.0.tgz", + "integrity": "sha512-zzGBl5fHo0EKSXsHzjspp3y5CONegCm8ErO5Qh0UzFzk2y4tMvzLWhoDokADbarfZRL2pGpRp7yt6gfJX4ph7A==", + "requires": { + "ansi-colors": "^1.0.1", + "archy": "^1.0.0", + "array-sort": "^1.0.0", + "color-support": "^1.1.3", + "concat-stream": "^1.6.0", + "copy-props": "^2.0.1", + "fancy-log": "^1.3.2", + "gulplog": "^1.0.0", + "interpret": "^1.4.0", + "isobject": "^3.0.1", + "liftoff": "^3.1.0", + "matchdep": "^2.0.0", + "mute-stdout": "^1.0.0", + "pretty-hrtime": "^1.0.0", + "replace-homedir": "^1.0.0", + "semver-greatest-satisfied-range": "^1.1.0", + "v8flags": "^3.2.0", + "yargs": "^7.1.0" + } + } + } + }, + "gulplog": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz", + "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=", + "requires": { + "glogg": "^1.0.0" + } }, "har-schema": { "version": "2.0.0", @@ -10889,7 +11405,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, "requires": { "function-bind": "^1.1.1" } @@ -10901,22 +11416,8 @@ "dev": true, "requires": { "ansi-regex": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - } } }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, "has-symbol-support-x": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", @@ -10927,8 +11428,7 @@ "has-symbols": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", - "dev": true + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" }, "has-to-string-tag-x": { "version": "1.4.1", @@ -10944,7 +11444,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dev": true, "requires": { "get-value": "^2.0.6", "has-values": "^1.0.0", @@ -10955,43 +11454,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dev": true, "requires": { "is-number": "^3.0.0", "kind-of": "^4.0.0" }, "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, "kind-of": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, "requires": { "is-buffer": "^1.1.5" } @@ -11002,7 +11473,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", - "dev": true, "requires": { "inherits": "^2.0.4", "readable-stream": "^3.6.0", @@ -11013,7 +11483,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -11026,12 +11495,38 @@ "version": "1.1.7", "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "dev": true, "requires": { "inherits": "^2.0.3", "minimalistic-assert": "^1.0.1" } }, + "hdkey": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/hdkey/-/hdkey-1.1.2.tgz", + "integrity": "sha512-PTQ4VKu0oRnCrYfLp04iQZ7T2Cxz0UsEXYauk2j8eh6PJXCpbXuCFhOmtIFtbET0i3PMWmHN9J11gU8LEgUljQ==", + "requires": { + "bs58check": "^2.1.2", + "safe-buffer": "^5.1.1", + "secp256k1": "^3.0.1" + }, + "dependencies": { + "secp256k1": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-3.8.0.tgz", + "integrity": "sha512-k5ke5avRZbtl9Tqx/SA7CbY3NF6Ro+Sj9cZxezFzuBlLDmyqPiL8hJJ+EmzD8Ig4LUDByHJ3/iPOVoRixs/hmw==", + "requires": { + "bindings": "^1.5.0", + "bip66": "^1.1.5", + "bn.js": "^4.11.8", + "create-hash": "^1.2.0", + "drbg.js": "^1.0.1", + "elliptic": "^6.5.2", + "nan": "^2.14.0", + "safe-buffer": "^5.1.2" + } + } + } + }, "heap": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.6.tgz", @@ -11042,7 +11537,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "dev": true, "requires": { "hash.js": "^1.0.3", "minimalistic-assert": "^1.0.0", @@ -11059,6 +11553,19 @@ "os-tmpdir": "^1.0.1" } }, + "homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "requires": { + "parse-passwd": "^1.0.0" + } + }, + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==" + }, "http-cache-semantics": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", @@ -11139,20 +11646,17 @@ "ieee754": { "version": "1.1.13", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", - "dev": true + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" }, "immediate": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.2.3.tgz", - "integrity": "sha1-0UD6j2FGWb1lQSMwl92qwlzdmRw=", - "dev": true + "integrity": "sha1-0UD6j2FGWb1lQSMwl92qwlzdmRw=" }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -11161,8 +11665,17 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" + }, + "interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==" }, "invariant": { "version": "2.2.4", @@ -11173,6 +11686,11 @@ "loose-envify": "^1.0.0" } }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" + }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -11180,13 +11698,28 @@ "dev": true, "optional": true }, + "is-absolute": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", + "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", + "requires": { + "is-relative": "^1.0.0", + "is-windows": "^1.0.1" + } + }, "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } } }, "is-arguments": { @@ -11195,56 +11728,78 @@ "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", "dev": true }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, "is-callable": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", - "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", - "dev": true - }, - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dev": true, - "requires": { - "ci-info": "^2.0.0" - } + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==" }, "is-data-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } } }, "is-date-object": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", - "dev": true + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==" }, "is-descriptor": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-data-descriptor": "^1.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + } } }, "is-extendable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, "requires": { "is-plain-object": "^2.0.4" } }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, "is-finite": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", @@ -11257,23 +11812,61 @@ "integrity": "sha1-lUPV3nvPWwiiLsiiC65uKG1RDYw=", "dev": true }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, "is-function": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz", "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==", "dev": true }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "requires": { + "is-extglob": "^2.1.1" + } + }, "is-hex-prefixed": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz", "integrity": "sha1-fY035q135dEnFIkTxXPggtd39VQ=", "dev": true }, - "is-negative-zero": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz", - "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=", - "dev": true + "is-natural-number": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz", + "integrity": "sha1-q5124dtM7VHjXeDHLr7PCfc0zeg=" + }, + "is-negated-glob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", + "integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI=" + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } }, "is-object": { "version": "1.0.1", @@ -11293,7 +11886,6 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, "requires": { "isobject": "^3.0.1" } @@ -11302,11 +11894,18 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", - "dev": true, "requires": { "has-symbols": "^1.0.1" } }, + "is-relative": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", + "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", + "requires": { + "is-unc-path": "^1.0.0" + } + }, "is-retry-allowed": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", @@ -11314,11 +11913,15 @@ "dev": true, "optional": true }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, "is-symbol": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", - "dev": true, "requires": { "has-symbols": "^1.0.1" } @@ -11326,14 +11929,30 @@ "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "is-unc-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", + "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", + "requires": { + "unc-path-regex": "^0.1.2" + } + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" + }, + "is-valid-glob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-1.0.0.tgz", + "integrity": "sha1-Kb8+/3Ab4tTTFdusw5vDn+j2Aao=" }, "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" }, "isarray": { "version": "1.0.0", @@ -11344,14 +11963,12 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" }, "isstream": { "version": "0.1.2", @@ -11373,9 +11990,7 @@ "js-sha3": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", - "integrity": "sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc=", - "dev": true, - "optional": true + "integrity": "sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc=" }, "js-tokens": { "version": "4.0.0", @@ -11389,6 +12004,12 @@ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", "dev": true }, + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true + }, "json-buffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", @@ -11446,17 +12067,27 @@ "jsonify": "~0.0.0" } }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=" + }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", "dev": true }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true + }, "jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "dev": true, "requires": { "graceful-fs": "^4.1.6" } @@ -11479,6 +12110,11 @@ "verror": "1.10.0" } }, + "just-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.0.0.tgz", + "integrity": "sha1-h/zPrv/AtozRnVX2cilD+SnqNeo=" + }, "keccak": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.1.tgz", @@ -11502,16 +12138,73 @@ "kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" }, - "klaw-sync": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", - "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==", - "dev": true, + "last-run": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/last-run/-/last-run-1.1.1.tgz", + "integrity": "sha1-RblpQsF7HHnHchmCWbqUO+v4yls=", "requires": { - "graceful-fs": "^4.1.11" + "default-resolution": "^2.0.0", + "es6-weak-map": "^2.0.1" + } + }, + "lazystream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", + "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", + "requires": { + "readable-stream": "^2.0.5" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "requires": { + "invert-kv": "^1.0.0" + } + }, + "lead": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lead/-/lead-1.0.0.tgz", + "integrity": "sha1-bxT5mje+Op3XhPVJVpDlkDRm7kI=", + "requires": { + "flush-write-stream": "^1.0.2" } }, "level-codec": { @@ -11536,11 +12229,34 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/level-iterator-stream/-/level-iterator-stream-2.0.3.tgz", "integrity": "sha512-I6Heg70nfF+e5Y3/qfthJFexhRw/Gi3bIymCoXAlijZdAcLaPuWSJs3KXyTYf23ID6g0o2QF62Yh+grOXY3Rig==", - "dev": true, "requires": { "inherits": "^2.0.1", - "readable-stream": "^2.0.5", "xtend": "^4.0.0" + }, + "dependencies": { + "level-errors": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/level-errors/-/level-errors-1.1.2.tgz", + "integrity": "sha512-Sw/IJwWbPKF5Ai4Wz60B52yj0zYeqzObLh8k1Tk88jVmD51cJSKWSYpRyhVIvFzZdvsPqlH5wfhp/yxdsaQH4w==", + "requires": { + "errno": "~0.1.1" + } + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } } }, "level-mem": { @@ -11562,6 +12278,12 @@ "xtend": "~4.0.0" } }, + "immediate": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.2.3.tgz", + "integrity": "sha1-0UD6j2FGWb1lQSMwl92qwlzdmRw=", + "dev": true + }, "ltgt": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz", @@ -11625,6 +12347,61 @@ "pull-stream": "^3.6.8", "typewiselite": "~1.0.0", "xtend": "~4.0.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "level-iterator-stream": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/level-iterator-stream/-/level-iterator-stream-2.0.3.tgz", + "integrity": "sha512-I6Heg70nfF+e5Y3/qfthJFexhRw/Gi3bIymCoXAlijZdAcLaPuWSJs3KXyTYf23ID6g0o2QF62Yh+grOXY3Rig==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.5", + "xtend": "^4.0.0" + } + }, + "ltgt": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.1.3.tgz", + "integrity": "sha1-EIUaBtmWS5cReEQcI8nlJpjuzjQ=", + "dev": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } } }, "level-ws": { @@ -11636,6 +12413,35 @@ "inherits": "^2.0.3", "readable-stream": "^2.2.8", "xtend": "^4.0.1" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + } } }, "levelup": { @@ -11650,6 +12456,31 @@ "xtend": "~4.0.0" }, "dependencies": { + "abstract-leveldown": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-5.0.0.tgz", + "integrity": "sha512-5mU5P1gXtsMIXg65/rsYGsi93+MlogXZ9FA8JnwKurHQg64bfXwGYVdVdijNTVNOlAsuIiOwHdvFFD5JqCJQ7A==", + "dev": true, + "requires": { + "xtend": "~4.0.0" + } + }, + "deferred-leveldown": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-4.0.2.tgz", + "integrity": "sha512-5fMC8ek8alH16QiV0lTCis610D1Zt1+LA4MS4d63JgS32lrCjTFDUFz2ao09/j2I4Bqb5jL4FZYwu7Jz0XO1ww==", + "dev": true, + "requires": { + "abstract-leveldown": "~5.0.0", + "inherits": "^2.0.3" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, "level-iterator-stream": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/level-iterator-stream/-/level-iterator-stream-3.0.1.tgz", @@ -11660,6 +12491,70 @@ "readable-stream": "^2.3.6", "xtend": "^4.0.0" } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "liftoff": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz", + "integrity": "sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog==", + "requires": { + "extend": "^3.0.0", + "findup-sync": "^3.0.0", + "fined": "^1.0.1", + "flagged-respawn": "^1.0.0", + "is-plain-object": "^2.0.4", + "object.map": "^1.0.0", + "rechoir": "^0.6.2", + "resolve": "^1.1.7" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" } } }, @@ -11706,26 +12601,78 @@ "integrity": "sha1-EIUaBtmWS5cReEQcI8nlJpjuzjQ=", "dev": true }, + "make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "requires": { + "pify": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + } + } + }, + "make-iterator": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", + "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", + "requires": { + "kind-of": "^6.0.2" + } + }, "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" }, "map-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "dev": true, "requires": { "object-visit": "^1.0.0" } }, + "matchdep": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz", + "integrity": "sha1-xvNINKDY28OzfCfui7yyfHd1WC4=", + "requires": { + "findup-sync": "^2.0.0", + "micromatch": "^3.0.4", + "resolve": "^1.4.0", + "stack-trace": "0.0.10" + }, + "dependencies": { + "findup-sync": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", + "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", + "requires": { + "detect-file": "^1.0.0", + "is-glob": "^3.1.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + } + }, + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, "md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", - "dev": true, "requires": { "hash-base": "^3.0.0", "inherits": "^2.0.1", @@ -11761,6 +12708,23 @@ "semaphore": ">=1.0.1" }, "dependencies": { + "abstract-leveldown": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-2.7.2.tgz", + "integrity": "sha512-+OVvxH2rHVEhWLdbudP6p0+dNMXu8JA1CbhP19T8paTYAcX7oJ4OVjT+ZUVpv7mITxXHqDMej+GdqXBmXkw09w==", + "requires": { + "xtend": "~4.0.0" + } + }, + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } + }, "ethereumjs-util": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", @@ -11776,6 +12740,55 @@ "safe-buffer": "^5.1.1" } }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "level-codec": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/level-codec/-/level-codec-7.0.1.tgz", + "integrity": "sha512-Ua/R9B9r3RasXdRmOtd+t9TCOEIIlts+TN/7XTT2unhDaL6sJn83S3rUyljbr6lVtw49N3/yA0HHjpV6Kzb2aQ==" + }, + "level-errors": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/level-errors/-/level-errors-1.0.5.tgz", + "integrity": "sha512-/cLUpQduF6bNrWuAC4pwtUKA5t669pCsCi2XbmojG2tFeOr9j6ShtdDCtFFQO1DRt+EVZhx9gPzP9G2bUaG4ig==", + "requires": { + "errno": "~0.1.1" + } + }, + "levelup": { + "version": "1.3.9", + "resolved": "https://registry.npmjs.org/levelup/-/levelup-1.3.9.tgz", + "integrity": "sha512-VVGHfKIlmw8w1XqpGOAGwq6sZm2WwWLmlDcULkKWQXEA5EopA8OBNJ2Ck2v6bdk8HeEZSbCSEgzXadyQFm76sQ==", + "requires": { + "level-codec": "~7.0.0", + "level-errors": "~1.0.3", + "prr": "~1.0.1", + "semver": "~5.4.1", + "xtend": "~4.0.0" + } + }, + "memdown": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/memdown/-/memdown-1.4.1.tgz", + "integrity": "sha1-tOThkhdGZP+65BNhqlAPMRnv4hU=", + "requires": { + "abstract-leveldown": "~2.7.1", + "functional-red-black-tree": "^1.0.1", + "immediate": "^3.2.3", + "inherits": "~2.0.1", + "safe-buffer": "~5.1.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, "readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -11785,6 +12798,35 @@ "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "semver": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } } } } @@ -11796,6 +12838,26 @@ "dev": true, "optional": true }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, "miller-rabin": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", @@ -11847,20 +12909,17 @@ "minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" }, "minimalistic-crypto-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", - "dev": true + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -11871,6 +12930,17 @@ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true }, + "minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, "minizlib": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", @@ -11879,29 +12949,25 @@ "optional": true, "requires": { "minipass": "^2.9.0" - }, - "dependencies": { - "minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - } } }, "mixin-deep": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "dev": true, "requires": { "for-in": "^1.0.2", "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + } } }, "mkdirp": { @@ -11909,6 +12975,7 @@ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", "dev": true, + "optional": true, "requires": { "minimist": "^1.2.5" } @@ -11936,51 +13003,15 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "multibase": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/multibase/-/multibase-0.6.1.tgz", - "integrity": "sha512-pFfAwyTjbbQgNc3G7D48JkJxWtoJoBMaR4xQUOuB8RnCgRqaYmWNFeJTTvrJ2w51bjLq2zTby6Rqj9TQ9elSUw==", - "dev": true, - "optional": true, - "requires": { - "base-x": "^3.0.8", - "buffer": "^5.5.0" - } + "mute-stdout": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz", + "integrity": "sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg==" }, - "multicodec": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-0.5.7.tgz", - "integrity": "sha512-PscoRxm3f+88fAtELwUnZxGDkduE2HD9Q6GHUOywQLjOGT/HAdhjLDYNZ1e7VR0s0TP0EwZ16LNUTFpoBGivOA==", - "dev": true, - "optional": true, - "requires": { - "varint": "^5.0.0" - } - }, - "multihashes": { - "version": "0.4.21", - "resolved": "https://registry.npmjs.org/multihashes/-/multihashes-0.4.21.tgz", - "integrity": "sha512-uVSvmeCWf36pU2nB4/1kzYZjsXD9vofZKpgudqkceYY5g2aZZXJ5r9lxuzoRLl1OAp28XljXsEJ/X/85ZsKmKw==", - "dev": true, - "optional": true, - "requires": { - "buffer": "^5.5.0", - "multibase": "^0.7.0", - "varint": "^5.0.0" - }, - "dependencies": { - "multibase": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/multibase/-/multibase-0.7.0.tgz", - "integrity": "sha512-TW8q03O0f6PNFTQDvh3xxH03c8CjGaaYrjkl9UQPG6rz53TQzzxJVCIWVjzcbN/Q5Y53Zd0IBQBMVktVgNx4Fg==", - "dev": true, - "optional": true, - "requires": { - "base-x": "^3.0.8", - "buffer": "^5.5.0" - } - } - } + "nan": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", + "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==" }, "nano-json-stream-parser": { "version": "0.1.2", @@ -11993,7 +13024,6 @@ "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dev": true, "requires": { "arr-diff": "^4.0.0", "array-unique": "^0.3.2", @@ -12018,14 +13048,7 @@ "next-tick": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", - "dev": true - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" }, "node-addon-api": { "version": "2.0.2", @@ -12045,6 +13068,29 @@ "integrity": "sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg==", "dev": true }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + }, "normalize-url": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", @@ -12052,6 +13098,19 @@ "dev": true, "optional": true }, + "now-and-later": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.1.tgz", + "integrity": "sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ==", + "requires": { + "once": "^1.3.2" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, "number-to-bn": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz", @@ -12081,14 +13140,12 @@ "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, "object-copy": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dev": true, "requires": { "copy-descriptor": "^0.1.0", "define-property": "^0.2.5", @@ -12098,60 +13155,12 @@ "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=" }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, "requires": { "is-buffer": "^1.1.5" } @@ -12161,8 +13170,7 @@ "object-inspect": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", - "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", - "dev": true + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==" }, "object-is": { "version": "1.1.3", @@ -12206,7 +13214,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dev": true, "requires": { "isobject": "^3.0.0" } @@ -12215,7 +13222,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz", "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==", - "dev": true, "requires": { "define-properties": "^1.1.3", "es-abstract": "^1.18.0-next.0", @@ -12227,7 +13233,6 @@ "version": "1.18.0-next.1", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", - "dev": true, "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", @@ -12242,9 +13247,25 @@ "string.prototype.trimend": "^1.0.1", "string.prototype.trimstart": "^1.0.1" } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" } } }, + "object.defaults": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", + "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", + "requires": { + "array-each": "^1.0.1", + "array-slice": "^1.0.0", + "for-own": "^1.0.0", + "isobject": "^3.0.0" + } + }, "object.getownpropertydescriptors": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", @@ -12255,15 +13276,32 @@ "es-abstract": "^1.17.0-next.1" } }, + "object.map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", + "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", + "requires": { + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + } + }, "object.pick": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "dev": true, "requires": { "isobject": "^3.0.1" } }, + "object.reduce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.reduce/-/object.reduce-1.0.1.tgz", + "integrity": "sha1-b+NI8qx/oPlcpiEiZZkJaCW7A60=", + "requires": { + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + } + }, "oboe": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/oboe/-/oboe-2.1.4.tgz", @@ -12288,17 +13326,66 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1" } }, + "ordered-read-streams": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz", + "integrity": "sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4=", + "requires": { + "readable-stream": "^2.0.1" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, "os-homedir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "requires": { + "lcid": "^1.0.0" + } + }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", @@ -12312,6 +13399,13 @@ "dev": true, "optional": true }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true, + "optional": true + }, "p-timeout": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz", @@ -12320,15 +13414,6 @@ "optional": true, "requires": { "p-finally": "^1.0.0" - }, - "dependencies": { - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true, - "optional": true - } } }, "parse-asn1": { @@ -12345,12 +13430,40 @@ "safe-buffer": "^5.1.1" } }, + "parse-filepath": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", + "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", + "requires": { + "is-absolute": "^1.0.0", + "map-cache": "^0.2.0", + "path-root": "^0.1.1" + } + }, "parse-headers": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.3.tgz", "integrity": "sha512-QhhZ+DCCit2Coi2vmAKbq5RGTRcQUOE2+REgv8vdyu7MnYx2eZztegqtTx99TZ86GTIwqiy3+4nQTWZ2tgmdCA==", "dev": true }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "requires": { + "error-ex": "^1.2.0" + } + }, + "parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==" + }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=" + }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -12361,106 +13474,43 @@ "pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" }, - "patch-package": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-6.2.2.tgz", - "integrity": "sha512-YqScVYkVcClUY0v8fF0kWOjDYopzIM8e3bj/RU1DPeEF14+dCGm6UeOYm4jvCyxqIEQ5/eJzmbWfDWnUleFNMg==", - "dev": true, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=" + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", "requires": { - "@yarnpkg/lockfile": "^1.1.0", - "chalk": "^2.4.2", - "cross-spawn": "^6.0.5", - "find-yarn-workspace-root": "^1.2.1", - "fs-extra": "^7.0.1", - "is-ci": "^2.0.0", - "klaw-sync": "^6.0.0", - "minimist": "^1.2.0", - "rimraf": "^2.6.3", - "semver": "^5.6.0", - "slash": "^2.0.0", - "tmp": "^0.0.33" - }, - "dependencies": { - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.2" - } - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } + "pinkie-promise": "^2.0.0" } }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-parse": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", - "dev": true + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + }, + "path-root": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", + "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", + "requires": { + "path-root-regex": "^0.1.0" + } + }, + "path-root-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", + "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=" }, "path-to-regexp": { "version": "0.1.7", @@ -12469,11 +13519,27 @@ "dev": true, "optional": true }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + } + } + }, "pbkdf2": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz", "integrity": "sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==", - "dev": true, "requires": { "create-hash": "^1.1.2", "create-hmac": "^1.1.4", @@ -12482,17 +13548,34 @@ "sha.js": "^2.4.8" } }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" + }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", "dev": true }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "requires": { + "pinkie": "^2.0.0" + } + }, "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" }, "precond": { "version": "0.2.3", @@ -12507,17 +13590,27 @@ "dev": true, "optional": true }, + "pretty-hrtime": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=" + }, "private": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", "dev": true }, + "process": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz", + "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=", + "dev": true + }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, "promise-to-callback": { "version": "1.0.0", @@ -12543,8 +13636,7 @@ "prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", - "dev": true + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" }, "pseudomap": { "version": "1.0.2", @@ -12635,13 +13727,32 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "optional": true, "requires": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -12651,8 +13762,7 @@ "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" }, "query-string": { "version": "5.1.1", @@ -12706,6 +13816,25 @@ "unpipe": "1.0.0" } }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", @@ -12729,6 +13858,58 @@ } } }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "requires": { + "resolve": "^1.1.6" + } + }, "regenerate": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.1.tgz", @@ -12756,7 +13937,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dev": true, "requires": { "extend-shallow": "^3.0.2", "safe-regex": "^1.1.0" @@ -12796,27 +13976,41 @@ "dev": true, "requires": { "jsesc": "~0.5.0" - }, - "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true - } } }, + "remove-bom-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz", + "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==", + "requires": { + "is-buffer": "^1.1.5", + "is-utf8": "^0.2.1" + } + }, + "remove-bom-stream": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz", + "integrity": "sha1-BfGlk/FuQuH7kOv1nejlaVJflSM=", + "requires": { + "remove-bom-buffer": "^3.0.0", + "safe-buffer": "^5.1.0", + "through2": "^2.0.3" + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" + }, "repeat-element": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", - "dev": true + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==" }, "repeat-string": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" }, "repeating": { "version": "2.0.1", @@ -12827,6 +14021,21 @@ "is-finite": "^1.0.0" } }, + "replace-ext": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", + "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==" + }, + "replace-homedir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-homedir/-/replace-homedir-1.0.0.tgz", + "integrity": "sha1-6H9tUTuSjd6AgmDBK+f+xv9ueYw=", + "requires": { + "homedir-polyfill": "^1.0.1", + "is-absolute": "^1.0.0", + "remove-trailing-separator": "^1.1.0" + } + }, "request": { "version": "2.88.2", "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", @@ -12853,22 +14062,55 @@ "tough-cookie": "~2.5.0", "tunnel-agent": "^0.6.0", "uuid": "^3.3.2" + }, + "dependencies": { + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true + } } }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" + }, "resolve": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", - "dev": true, "requires": { "path-parse": "^1.0.6" } }, + "resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "requires": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + } + }, + "resolve-options": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-1.1.0.tgz", + "integrity": "sha1-MrueOcBtZzONyTeMDW1gdFZq0TE=", + "requires": { + "value-or-function": "^3.0.0" + } + }, "resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" }, "responselike": { "version": "1.0.2", @@ -12892,8 +14134,7 @@ "ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" }, "rimraf": { "version": "2.6.3", @@ -12908,7 +14149,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "dev": true, "requires": { "hash-base": "^3.0.0", "inherits": "^2.0.1" @@ -12932,8 +14172,7 @@ "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" }, "safe-event-emitter": { "version": "1.0.1", @@ -12948,7 +14187,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "dev": true, "requires": { "ret": "~0.1.10" } @@ -12965,12 +14203,18 @@ "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==", "dev": true }, + "scrypt.js": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/scrypt.js/-/scrypt.js-0.3.0.tgz", + "integrity": "sha512-42LTc1nyFsyv/o0gcHtDztrn+aqpkaCNt5Qh7ATBZfhEZU7IC/0oT/qbBH+uRNoAPvs2fwiOId68FDEoSRA8/A==", + "requires": { + "scryptsy": "^1.2.1" + } + }, "scryptsy": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/scryptsy/-/scryptsy-1.2.1.tgz", "integrity": "sha1-oyJfpLJST4AnAHYeKFW987LZIWM=", - "dev": true, - "optional": true, "requires": { "pbkdf2": "^3.0.3" } @@ -12992,12 +14236,28 @@ "integrity": "sha512-1/02Y/rUeU1CJBAGLebiC5Lbo5FnB22gQbIFFYTLkwvp1xdABZJH1sn4ZT1MzXmPpzv+Rf/Lu2NcsLJiK4rcDg==", "dev": true }, + "seek-bzip": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.6.tgz", + "integrity": "sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ==", + "requires": { + "commander": "^2.8.1" + } + }, "semaphore": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/semaphore/-/semaphore-1.1.0.tgz", "integrity": "sha512-O4OZEaNtkMd/K0i6js9SL+gqy0ZCBMgUvlSqHKi4IBdjhe7wB8pwztUk1BbZ1fmrvpwFrPbHzqd2w5pTcJH6LA==", "dev": true }, + "semver-greatest-satisfied-range": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz", + "integrity": "sha1-E+jCZYq5aRywzXEJMkAoDTb3els=", + "requires": { + "sver-compat": "^1.5.0" + } + }, "send": { "version": "0.17.1", "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", @@ -13075,6 +14335,11 @@ "xhr": "^2.3.3" } }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, "set-immediate-shim": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", @@ -13085,10 +14350,8 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "dev": true, "requires": { "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", "is-plain-object": "^2.0.3", "split-string": "^3.0.1" }, @@ -13096,17 +14359,7 @@ "extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=" } } }, @@ -13127,7 +14380,6 @@ "version": "2.4.11", "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "dev": true, "requires": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" @@ -13152,11 +14404,16 @@ "simple-concat": "^1.0.0" } }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "dev": true + }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dev": true, "requires": { "base": "^0.11.1", "debug": "^2.2.0", @@ -13172,7 +14429,6 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, "requires": { "ms": "2.0.0" } @@ -13180,95 +14436,22 @@ "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=" }, "extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=" }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" } } }, @@ -13276,7 +14459,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, "requires": { "define-property": "^1.0.0", "isobject": "^3.0.0", @@ -13287,10 +14469,35 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, "requires": { "is-descriptor": "^1.0.0" } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } } } }, @@ -13298,22 +14505,14 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dev": true, "requires": { "kind-of": "^3.2.0" }, "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, "requires": { "is-buffer": "^1.1.5" } @@ -13323,14 +14522,12 @@ "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" }, "source-map-resolve": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", - "dev": true, "requires": { "atob": "^2.1.2", "decode-uri-component": "^0.2.0", @@ -13360,14 +14557,45 @@ "source-map-url": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", - "dev": true + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=" + }, + "sparkles": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz", + "integrity": "sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==" + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==" }, "split-string": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dev": true, "requires": { "extend-shallow": "^3.0.0" } @@ -13397,11 +14625,15 @@ } } }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" + }, "static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "dev": true, "requires": { "define-property": "^0.2.5", "object-copy": "^0.1.0" @@ -13410,74 +14642,7 @@ "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=" } } }, @@ -13488,6 +14653,16 @@ "dev": true, "optional": true }, + "stream-exhaust": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz", + "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==" + }, + "stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" + }, "stream-to-pull-stream": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/stream-to-pull-stream/-/stream-to-pull-stream-1.7.3.tgz", @@ -13513,6 +14688,16 @@ "dev": true, "optional": true }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, "string.prototype.trim": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.2.tgz", @@ -13549,7 +14734,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", - "dev": true, "requires": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5" @@ -13559,7 +14743,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", - "dev": true, "requires": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5" @@ -13569,7 +14752,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, "requires": { "safe-buffer": "~5.1.0" }, @@ -13577,11 +14759,34 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-dirs": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz", + "integrity": "sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==", + "requires": { + "is-natural-number": "^4.0.1" + } + }, "strip-hex-prefix": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", @@ -13595,11 +14800,19 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, "requires": { "has-flag": "^3.0.0" } }, + "sver-compat": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/sver-compat/-/sver-compat-1.5.0.tgz", + "integrity": "sha1-PPh9/rTQe0o/FIJ7wYaz/QxkXNg=", + "requires": { + "es6-iterator": "^2.0.1", + "es6-symbol": "^3.1.1" + } + }, "swarm-js": { "version": "0.1.40", "resolved": "https://registry.npmjs.org/swarm-js/-/swarm-js-0.1.40.tgz", @@ -13662,13 +14875,6 @@ "url-to-options": "^1.0.1" } }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true, - "optional": true - }, "p-cancelable": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz", @@ -13765,25 +14971,62 @@ "yallist": "^3.0.3" }, "dependencies": { - "fs-minipass": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", - "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", "dev": true, "optional": true, "requires": { - "minipass": "^2.6.0" + "minimist": "^1.2.5" + } + } + } + }, + "tar-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", + "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", + "requires": { + "bl": "^1.0.0", + "buffer-alloc": "^1.2.0", + "end-of-stream": "^1.0.0", + "fs-constants": "^1.0.0", + "readable-stream": "^2.3.0", + "to-buffer": "^1.1.1", + "xtend": "^4.0.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, - "minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "dev": true, - "optional": true, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" + "safe-buffer": "~5.1.0" } } } @@ -13791,19 +15034,65 @@ "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" }, "through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, "requires": { "readable-stream": "~2.3.6", "xtend": "~4.0.1" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } } }, + "through2-filter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz", + "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", + "requires": { + "through2": "~2.0.0", + "xtend": "~4.0.0" + } + }, + "time-stamp": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", + "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=" + }, "timed-out": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", @@ -13820,26 +15109,38 @@ "rimraf": "^2.6.3" } }, + "to-absolute-glob": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", + "integrity": "sha1-GGX0PZ50sIItufFFt4z/fQ98hJs=", + "requires": { + "is-absolute": "^1.0.0", + "is-negated-glob": "^1.0.0" + } + }, + "to-buffer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", + "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==" + }, + "to-fast-properties": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", + "dev": true + }, "to-object-path": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "dev": true, "requires": { "kind-of": "^3.0.2" }, "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, "requires": { "is-buffer": "^1.1.5" } @@ -13857,7 +15158,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "dev": true, "requires": { "define-property": "^2.0.2", "extend-shallow": "^3.0.2", @@ -13865,6 +15165,23 @@ "safe-regex": "^1.1.0" } }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "to-through": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-through/-/to-through-2.0.0.tgz", + "integrity": "sha1-/JKtq6ByZHvAtn1rA2ZKoZUJOvY=", + "requires": { + "through2": "^2.0.3" + } + }, "toidentifier": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", @@ -13912,8 +15229,7 @@ "type": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", - "dev": true + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" }, "type-is": { "version": "1.6.18", @@ -13929,14 +15245,12 @@ "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" }, "typedarray-to-buffer": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, "requires": { "is-typedarray": "^1.0.0" } @@ -13969,6 +15283,20 @@ "dev": true, "optional": true }, + "unbzip2-stream": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", + "requires": { + "buffer": "^5.2.1", + "through": "^2.3.8" + } + }, + "unc-path-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", + "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=" + }, "underscore": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz", @@ -13976,31 +15304,50 @@ "dev": true, "optional": true }, + "undertaker": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.2.1.tgz", + "integrity": "sha512-71WxIzDkgYk9ZS+spIB8iZXchFhAdEo2YU8xYqBYJ39DIUIqziK78ftm26eecoIY49X0J2MLhG4hr18Yp6/CMA==", + "requires": { + "arr-flatten": "^1.0.1", + "arr-map": "^2.0.0", + "bach": "^1.0.0", + "collection-map": "^1.0.0", + "es6-weak-map": "^2.0.1", + "last-run": "^1.1.0", + "object.defaults": "^1.0.0", + "object.reduce": "^1.0.0", + "undertaker-registry": "^1.0.0" + } + }, + "undertaker-registry": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-1.0.1.tgz", + "integrity": "sha1-XkvaMI5KiirlhPm5pDWaSZglzFA=" + }, "union-value": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "dev": true, "requires": { "arr-union": "^3.1.0", "get-value": "^2.0.6", - "is-extendable": "^0.1.1", "set-value": "^2.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true - } + } + }, + "unique-stream": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz", + "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==", + "requires": { + "json-stable-stringify-without-jsonify": "^1.0.1", + "through2-filter": "^3.0.0" } }, "universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" }, "unorm": { "version": "1.6.0", @@ -14019,7 +15366,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dev": true, "requires": { "has-value": "^0.3.1", "isobject": "^3.0.0" @@ -14029,7 +15375,6 @@ "version": "0.3.1", "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, "requires": { "get-value": "^2.0.3", "has-values": "^0.1.4", @@ -14040,7 +15385,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, "requires": { "isarray": "1.0.0" } @@ -14050,11 +15394,20 @@ "has-values": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" } } }, + "upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==" + }, "uri-js": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", @@ -14067,8 +15420,7 @@ "urix": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" }, "url-parse-lax": { "version": "3.0.0", @@ -14097,25 +15449,7 @@ "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "dev": true - }, - "utf-8-validate": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.2.tgz", - "integrity": "sha512-SwV++i2gTD5qh2XqaPzBnNX88N6HdyhQrNNRykvcS0QKvItV9u3vPEJr+X5Hhfb1JC0r0e1alL0iB09rY8+nmw==", - "dev": true, - "requires": { - "node-gyp-build": "~3.7.0" - }, - "dependencies": { - "node-gyp-build": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-3.7.0.tgz", - "integrity": "sha512-L/Eg02Epx6Si2NXmedx+Okg+4UHqmaf3TNcxd50SF9NQGcJaON3AtU++kax69XV7YWz4tUspqZSAsVofhFKG2w==", - "dev": true - } - } + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" }, "utf8": { "version": "3.0.0", @@ -14127,8 +15461,7 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "util.promisify": { "version": "1.0.1", @@ -14155,12 +15488,27 @@ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", "dev": true }, - "varint": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/varint/-/varint-5.0.2.tgz", - "integrity": "sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow==", - "dev": true, - "optional": true + "v8flags": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz", + "integrity": "sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg==", + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "value-or-function": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-3.0.0.tgz", + "integrity": "sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM=" }, "vary": { "version": "1.1.2", @@ -14180,6 +15528,101 @@ "extsprintf": "^1.2.0" } }, + "vinyl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz", + "integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==", + "requires": { + "clone": "^2.1.1", + "clone-buffer": "^1.0.0", + "clone-stats": "^1.0.0", + "cloneable-readable": "^1.0.0", + "remove-trailing-separator": "^1.0.1", + "replace-ext": "^1.0.0" + } + }, + "vinyl-fs": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-3.0.3.tgz", + "integrity": "sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng==", + "requires": { + "fs-mkdirp-stream": "^1.0.0", + "glob-stream": "^6.1.0", + "graceful-fs": "^4.0.0", + "is-valid-glob": "^1.0.0", + "lazystream": "^1.0.0", + "lead": "^1.0.0", + "object.assign": "^4.0.4", + "pumpify": "^1.3.5", + "readable-stream": "^2.3.3", + "remove-bom-buffer": "^3.0.0", + "remove-bom-stream": "^1.2.0", + "resolve-options": "^1.1.0", + "through2": "^2.0.0", + "to-through": "^2.0.0", + "value-or-function": "^3.0.0", + "vinyl": "^2.0.0", + "vinyl-sourcemap": "^1.1.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "vinyl-sourcemap": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz", + "integrity": "sha1-kqgAWTo4cDqM2xHYswCtS+Y7PhY=", + "requires": { + "append-buffer": "^1.0.2", + "convert-source-map": "^1.5.0", + "graceful-fs": "^4.1.6", + "normalize-path": "^2.1.1", + "now-and-later": "^2.0.0", + "remove-bom-buffer": "^3.0.0", + "vinyl": "^2.0.0" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, "web3": { "version": "1.2.11", "resolved": "https://registry.npmjs.org/web3/-/web3-1.2.11.tgz", @@ -14194,6 +15637,13 @@ "web3-net": "1.2.11", "web3-shh": "1.2.11", "web3-utils": "1.2.11" + }, + "dependencies": { + "@types/node": { + "version": "12.12.53", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.53.tgz", + "integrity": "sha512-51MYTDTyCziHb70wtGNFRwB4l+5JNvdqzFSkbDvpbftEgVUBEE+T5f7pROhWMp/fxp07oNIEQZd5bbfAH22ohQ==" + } } }, "web3-bzz": { @@ -14338,6 +15788,75 @@ "@ethersproject/abi": "5.0.0-beta.153", "underscore": "1.9.1", "web3-utils": "1.2.11" + }, + "dependencies": { + "@types/node": { + "version": "10.17.28", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.28.tgz", + "integrity": "sha512-dzjES1Egb4c1a89C7lKwQh8pwjYmlOAG9dW1pBgxEk57tMrLnssOfEthz8kdkNaBd7lIqQx7APm5+mZ619IiCQ==" + }, + "aes-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", + "integrity": "sha1-4h3xCtbCBTKVvLuNq0Cwnb6ofk0=" + }, + "elliptic": { + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.3.3.tgz", + "integrity": "sha1-VILZZG1UvLif19mU/J4ulWiHbj8=", + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "inherits": "^2.0.1" + } + }, + "ethers": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-4.0.0-beta.3.tgz", + "integrity": "sha512-YYPogooSknTwvHg3+Mv71gM/3Wcrx+ZpCzarBj3mqs9njjRkrOo2/eufzhHloOCo3JSoNI4TQJJ6yU5ABm3Uog==", + "requires": { + "@types/node": "^10.3.2", + "aes-js": "3.0.0", + "bn.js": "^4.4.0", + "elliptic": "6.3.3", + "hash.js": "1.1.3", + "js-sha3": "0.5.7", + "scrypt-js": "2.0.3", + "setimmediate": "1.0.4", + "uuid": "2.0.1", + "xmlhttprequest": "1.8.0" + } + }, + "hash.js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", + "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.0" + } + }, + "js-sha3": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", + "integrity": "sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc=" + }, + "scrypt-js": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-2.0.3.tgz", + "integrity": "sha1-uwBAvgMEPamgEqLOqfyfhSz8h9Q=" + }, + "setimmediate": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.4.tgz", + "integrity": "sha1-IOgd5iLUoCWIzgyNqJc8vPHTE48=" + }, + "uuid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.1.tgz", + "integrity": "sha1-wqMN7bPlNdcsz4LjQ5QaULqFM6w=" + } } }, "web3-eth-accounts": { @@ -14426,6 +15945,15 @@ "requires": { "bn.js": "^4.11.9", "web3-utils": "1.2.11" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true, + "optional": true + } } }, "web3-eth-personal": { @@ -14520,6 +16048,12 @@ "ethereumjs-util": "^5.1.1" } }, + "ethereum-common": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/ethereum-common/-/ethereum-common-0.2.0.tgz", + "integrity": "sha512-XOnAR/3rntJgbCdGhqdaLIxDLWKLmsZOGhHdBKadEr6gEnJLH52k93Ou+TUdFaPN3hJc3isBZBal3U/XZ15abA==", + "dev": true + }, "ethereumjs-abi": { "version": "git+https://git@github.com/ethereumjs/ethereumjs-abi.git#1ce6a1d64235fabe2aaf827fd606def55693508f", "from": "git+https://github.com/ethereumjs/ethereumjs-abi.git", @@ -14568,14 +16102,6 @@ "ethereumjs-tx": "^1.2.2", "ethereumjs-util": "^5.0.0", "merkle-patricia-tree": "^2.1.2" - }, - "dependencies": { - "ethereum-common": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/ethereum-common/-/ethereum-common-0.2.0.tgz", - "integrity": "sha512-XOnAR/3rntJgbCdGhqdaLIxDLWKLmsZOGhHdBKadEr6gEnJLH52k93Ou+TUdFaPN3hJc3isBZBal3U/XZ15abA==", - "dev": true - } } }, "ethereumjs-tx": { @@ -14586,6 +16112,14 @@ "requires": { "ethereum-common": "^0.0.18", "ethereumjs-util": "^5.0.0" + }, + "dependencies": { + "ethereum-common": { + "version": "0.0.18", + "resolved": "https://registry.npmjs.org/ethereum-common/-/ethereum-common-0.0.18.tgz", + "integrity": "sha1-L9w1dvIykDNYl26znaeDIT/5Uj8=", + "dev": true + } } }, "ethereumjs-util": { @@ -14772,36 +16306,13 @@ "prr": "~1.0.1", "semver": "~5.4.1", "xtend": "~4.0.0" - } - }, - "ltgt": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz", - "integrity": "sha1-81ypHEk/e3PaDgdJUwTxezH4fuU=", - "dev": true - }, - "memdown": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/memdown/-/memdown-1.4.1.tgz", - "integrity": "sha1-tOThkhdGZP+65BNhqlAPMRnv4hU=", - "dev": true, - "requires": { - "abstract-leveldown": "~2.7.1", - "functional-red-black-tree": "^1.0.1", - "immediate": "^3.2.3", - "inherits": "~2.0.1", - "ltgt": "~2.2.0", - "safe-buffer": "~5.1.1" }, "dependencies": { - "abstract-leveldown": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-2.7.2.tgz", - "integrity": "sha512-+OVvxH2rHVEhWLdbudP6p0+dNMXu8JA1CbhP19T8paTYAcX7oJ4OVjT+ZUVpv7mITxXHqDMej+GdqXBmXkw09w==", - "dev": true, - "requires": { - "xtend": "~4.0.0" - } + "semver": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", + "dev": true } } }, @@ -14835,23 +16346,56 @@ "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=", "dev": true }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "semver": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", - "dev": true + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } }, "string_decoder": { "version": "0.10.31", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true + "dev": true, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } }, "ws": { "version": "5.2.2", @@ -14930,6 +16474,13 @@ "utf8": "3.0.0" }, "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true, + "optional": true + }, "eth-lib": { "version": "0.2.8", "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.8.tgz", @@ -14981,11 +16532,32 @@ "integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng==", "dev": true }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=" + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "ws": { "version": "3.3.3", @@ -15056,23 +16628,69 @@ "cookiejar": "^2.1.1" } }, + "xmlhttprequest": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz", + "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw=" + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" }, "yaeti": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", - "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=", - "dev": true + "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=" }, "yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true + }, + "yargs": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.1.tgz", + "integrity": "sha512-huO4Fr1f9PmiJJdll5kwoS2e4GqzGSsMT3PPMpOwoVkOK8ckqAewMTZyA6LXVQWflleb/Z8oPBEvNsMft0XE+g==", + "requires": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "5.0.0-security.0" + } + }, + "yargs-parser": { + "version": "5.0.0-security.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0-security.0.tgz", + "integrity": "sha512-T69y4Ps64LNesYxeYGYPvfoMTt/7y1XtfpIslUeK4um+9Hu7hlGoRtaDLvdXb7+/tfq4opVa2HRY5xGip022rQ==", + "requires": { + "camelcase": "^3.0.0", + "object.assign": "^4.1.0" + } + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } } } }, @@ -15468,8 +17086,7 @@ "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, "has-symbol-support-x": { "version": "1.4.2", @@ -16035,8 +17652,7 @@ "is-negative-zero": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz", - "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=", - "dev": true + "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=" }, "is-number": { "version": "7.0.0", @@ -17347,6 +18963,52 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "multibase": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/multibase/-/multibase-0.6.1.tgz", + "integrity": "sha512-pFfAwyTjbbQgNc3G7D48JkJxWtoJoBMaR4xQUOuB8RnCgRqaYmWNFeJTTvrJ2w51bjLq2zTby6Rqj9TQ9elSUw==", + "dev": true, + "optional": true, + "requires": { + "base-x": "^3.0.8", + "buffer": "^5.5.0" + } + }, + "multicodec": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-0.5.7.tgz", + "integrity": "sha512-PscoRxm3f+88fAtELwUnZxGDkduE2HD9Q6GHUOywQLjOGT/HAdhjLDYNZ1e7VR0s0TP0EwZ16LNUTFpoBGivOA==", + "dev": true, + "optional": true, + "requires": { + "varint": "^5.0.0" + } + }, + "multihashes": { + "version": "0.4.21", + "resolved": "https://registry.npmjs.org/multihashes/-/multihashes-0.4.21.tgz", + "integrity": "sha512-uVSvmeCWf36pU2nB4/1kzYZjsXD9vofZKpgudqkceYY5g2aZZXJ5r9lxuzoRLl1OAp28XljXsEJ/X/85ZsKmKw==", + "dev": true, + "optional": true, + "requires": { + "buffer": "^5.5.0", + "multibase": "^0.7.0", + "varint": "^5.0.0" + }, + "dependencies": { + "multibase": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/multibase/-/multibase-0.7.0.tgz", + "integrity": "sha512-TW8q03O0f6PNFTQDvh3xxH03c8CjGaaYrjkl9UQPG6rz53TQzzxJVCIWVjzcbN/Q5Y53Zd0IBQBMVktVgNx4Fg==", + "dev": true, + "optional": true, + "requires": { + "base-x": "^3.0.8", + "buffer": "^5.5.0" + } + } + } + }, "multimatch": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-4.0.0.tgz", @@ -20476,6 +22138,15 @@ "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", "dev": true }, + "utf-8-validate": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.3.tgz", + "integrity": "sha512-jtJM6fpGv8C1SoH4PtG22pGto6x+Y8uPprW0tw3//gGFhDDTiuksgradgFN6yRayDP4SyZZa6ZMGHLIa17+M8A==", + "dev": true, + "requires": { + "node-gyp-build": "^4.2.0" + } + }, "utf8": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", @@ -20522,6 +22193,13 @@ "spdx-expression-parse": "^3.0.0" } }, + "varint": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/varint/-/varint-5.0.2.tgz", + "integrity": "sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow==", + "dev": true, + "optional": true + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", From 037ac78acb0615197fd88a3dca35a1c279319d75 Mon Sep 17 00:00:00 2001 From: David Racero Date: Thu, 7 Jan 2021 16:17:33 +0100 Subject: [PATCH 57/62] Added small guide to how to interact with Aave in Mainnet with console --- README.md | 76 ++++++++++++++++++++++++++++++++++++----------- hardhat.config.ts | 2 +- 2 files changed, 59 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index f4d8c1d0..aba2356e 100644 --- a/README.md +++ b/README.md @@ -112,41 +112,81 @@ You can deploy Aave Protocol v2 in a forked Mainnet chain using Hardhat built-in docker-compose run contracts-env npm run aave:fork:main ``` -### Mainnet fork - Interact with Aave via Hardhat console +### Deploy Aave into a Mainnet Fork via console -You can also deploy Aave into the Hardhat console in fork mode, to interact with the protocol inside the fork or for testing purposes. +You can deploy Aave into the Hardhat console in fork mode, to interact with the protocol inside the fork or for testing purposes. + +Run the console in Mainnet fork mode: ``` -# Run the console in fork mode docker-compose run contracts-env npm run console:fork +``` -# Deploy the Aave protocol in fork mode +At the Hardhat console, interact with the Aave protocol in Mainnet fork mode: + +``` +// Deploy the Aave protocol in fork mode await run('aave:mainnet') -# Or your custom Hardhat task +// Or your custom Hardhat task await run('your-custom-task'); -# After you initialize the Signers via 'set-DRE' task, you can import any TS/JS file -await run('set-DRE'); // Initialize signers +// After you initialize the HRE via 'set-DRE' task, you can import any TS/JS file +run('set-DRE'); -# Import contract getters to retrieve an Ethers.js Contract instance +// Import contract getters to retrieve an Ethers.js Contract instance const contractGetters = require('./helpers/contracts-getters'); // Import a TS/JS file -# Lending pool instance +// Lending pool instance const lendingPool = await contractGetters.getLendingPool("LendingPool address from 'aave:mainnet' task"); -``` -### Mainnet fork - Run the check list +// You can impersonate any Ethereum address +await network.provider.request({ method: "hardhat_impersonateAccount", params: ["0xb1adceddb2941033a090dd166a462fe1c2029484"]}); -For testing the deployment scripts for Mainnet release, you can run the check-list tests in a Mainnet fork using Hardhat built-in feature: +const signer = await ethers.provider.getSigner("0xb1adceddb2941033a090dd166a462fe1c2029484") + +// ERC20 token DAI Mainnet instance +const DAI = await contractGetters.getIErc20Detailed("0x6B175474E89094C44Da98b954EedeAC495271d0F"); + +// Approve 100 DAI to LendingPool address +await DAI.connect(signer).approve(lendingPool.address, ethers.utils.parseUnits('100')); + +// Deposit 100 DAI +await lendingPool.connect(signer).deposit(DAI.address, ethers.utils.parseUnits('100'), await signer.getAddress(), '0'); ``` -# In another terminal, run docker-compose -docker-compose up -# Open another tab or terminal -docker-compose exec contracts-env bash +## Interact with Aave in Mainnet via console + +You can interact with Aave at Mainnet network using the Hardhat console, in the scenario where the frontend is down or you want to interact directly. You can check the deployed addresses at https://docs.aave.com/developers/deployed-contracts. + +Run the Hardhat console pointing to the Mainnet network: -# A new Bash terminal is prompted, connected to the container -npm run test:main:check-list +``` +docker-compose run contracts-env npx hardhat --network main console +``` + +At the Hardhat console, you can interact with the protocol: + +``` +// Load the HRE into helpers to access signers +run("set-DRE") + +// Import getters to instance any Aave contract +const contractGetters = require('./helpers/contracts-getters'); + +// Load the first signer +const signer = await contractGetters.getFirstSigner(); + +// Lending pool instance +const lendingPool = await contractGetters.getLendingPool("0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9"); + +// ERC20 token DAI Mainnet instance +const DAI = await contractGetters.getIErc20Detailed("0x6B175474E89094C44Da98b954EedeAC495271d0F"); + +// Approve 100 DAI to LendingPool address +await DAI.connect(signer).approve(lendingPool.address, ethers.utils.parseUnits('100')); + +// Deposit 100 DAI +await lendingPool.connect(signer).deposit(DAI.address, ethers.utils.parseUnits('100'), await signer.getAddress(), '0'); ``` diff --git a/hardhat.config.ts b/hardhat.config.ts index df6d0758..adf17664 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -61,7 +61,7 @@ const getCommonNetworkConfig = (networkName: eEthereumNetwork, networkId: number const mainnetFork = MAINNET_FORK ? { - blockNumber: 11366117, + blockNumber: 11608298, url: ALCHEMY_KEY ? `https://eth-mainnet.alchemyapi.io/v2/${ALCHEMY_KEY}` : `https://mainnet.infura.io/v3/${INFURA_KEY}`, From 334718a077b353028db49599cafac9eaa59dbbf9 Mon Sep 17 00:00:00 2001 From: dhadrien Date: Tue, 22 Dec 2020 11:37:08 +0100 Subject: [PATCH 58/62] added package.json script --- package.json | 4 +++- tasks/helpers/deploy-new-asset.ts | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index d3c73712..985fb618 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,9 @@ "print-config:kovan": "hardhat --network kovan print-config --pool Aave --data-provider 0xA1901785c29cBd48bfA74e46b67C736b26054fa4", "main:fork:initialize-tokens": "npm run compile && MAINNET_FORK=true hardhat full:initialize-tokens --pool Aave", "main:initialize-tokens": "npm run compile && hardhat --network main full:initialize-tokens --pool Aave", - "kovan:initialize-tokens": "npm run compile && hardhat --network kovan full:initialize-tokens --pool Aave" + "kovan:initialize-tokens": "npm run compile && hardhat --network kovan full:initialize-tokens --pool Aave", + "external:deploy-assets-kovan": "npm run compile && hardhat --network kovan external:deploy-new-asset --symbol ${SYMBOL} --verify", + "external:deploy-assets-main": "npm run compile && hardhat --network main external:deploy-new-asset --symbol ${SYMBOL} --verify" }, "devDependencies": { "@nomiclabs/buidler": "^1.4.7", diff --git a/tasks/helpers/deploy-new-asset.ts b/tasks/helpers/deploy-new-asset.ts index 50795f64..912ebff1 100644 --- a/tasks/helpers/deploy-new-asset.ts +++ b/tasks/helpers/deploy-new-asset.ts @@ -81,7 +81,7 @@ WRONG RESERVE ASSET SETUP: ); const rates = await deployDefaultReserveInterestRateStrategy( [ - reserveAssetAddress, + addressProvider.address, strategyParams.optimalUtilizationRate, strategyParams.baseVariableBorrowRate, strategyParams.variableRateSlope1, From 63ce8bc2bb6cdf64db08eea39dd66852276aa9b8 Mon Sep 17 00:00:00 2001 From: David Racero Date: Tue, 12 Jan 2021 13:20:32 +0100 Subject: [PATCH 59/62] Revert "add withFlash flage to getAmountIn/Out" This reverts commit 3d2a8777704f60c58f3f19fbd78577216f79a680. --- contracts/adapters/BaseUniswapAdapter.sol | 79 ++++++++----------- .../interfaces/IBaseUniswapAdapter.sol | 6 +- .../deployments/deploy-UniswapRepayAdapter.ts | 4 + 3 files changed, 39 insertions(+), 50 deletions(-) diff --git a/contracts/adapters/BaseUniswapAdapter.sol b/contracts/adapters/BaseUniswapAdapter.sol index 5a163233..70d4beed 100644 --- a/contracts/adapters/BaseUniswapAdapter.sol +++ b/contracts/adapters/BaseUniswapAdapter.sol @@ -63,8 +63,7 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt function getAmountsOut( uint256 amountIn, address reserveIn, - address reserveOut, - bool withFlash + address reserveOut ) external view @@ -77,7 +76,7 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt address[] memory ) { - AmountCalc memory results = _getAmountsOutData(reserveIn, reserveOut, amountIn, withFlash); + AmountCalc memory results = _getAmountsOutData(reserveIn, reserveOut, amountIn); return ( results.calculatedAmount, @@ -101,8 +100,7 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt function getAmountsIn( uint256 amountOut, address reserveIn, - address reserveOut, - bool withFlash + address reserveOut ) external view @@ -115,7 +113,7 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt address[] memory ) { - AmountCalc memory results = _getAmountsInData(reserveIn, reserveOut, amountOut, withFlash); + AmountCalc memory results = _getAmountsInData(reserveIn, reserveOut, amountOut); return ( results.calculatedAmount, @@ -318,14 +316,6 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt return amount.mul(reservePrice).div(10**decimals).mul(ethUsdPrice).div(10**18); } - struct AmountOutVars { - uint256 finalAmountIn; - address[] simplePath; - uint256[] amountsWithoutWeth; - uint256[] amountsWithWeth; - address[] pathWithWeth; - } - /** * @dev Given an input asset amount, returns the maximum output amount of the other asset * @param reserveIn Address of the asset to be swap from @@ -340,55 +330,54 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt function _getAmountsOutData( address reserveIn, address reserveOut, - uint256 amountIn, - bool withFlash + uint256 amountIn ) internal view returns (AmountCalc memory) { - AmountOutVars memory vars; // Subtract flash loan fee - vars.finalAmountIn = amountIn.sub( - withFlash ? amountIn.mul(FLASHLOAN_PREMIUM_TOTAL).div(10000) : 0 - ); + uint256 finalAmountIn = amountIn.sub(amountIn.mul(FLASHLOAN_PREMIUM_TOTAL).div(10000)); - vars.simplePath = new address[](2); - vars.simplePath[0] = reserveIn; - vars.simplePath[1] = reserveOut; + address[] memory simplePath = new address[](2); + simplePath[0] = reserveIn; + simplePath[1] = reserveOut; - vars.pathWithWeth = new address[](3); + uint256[] memory amountsWithoutWeth; + uint256[] memory amountsWithWeth; + + address[] memory pathWithWeth = new address[](3); if (reserveIn != WETH_ADDRESS && reserveOut != WETH_ADDRESS) { - vars.pathWithWeth[0] = reserveIn; - vars.pathWithWeth[1] = WETH_ADDRESS; - vars.pathWithWeth[2] = reserveOut; + pathWithWeth[0] = reserveIn; + pathWithWeth[1] = WETH_ADDRESS; + pathWithWeth[2] = reserveOut; - try UNISWAP_ROUTER.getAmountsOut(vars.finalAmountIn, vars.pathWithWeth) returns ( + try UNISWAP_ROUTER.getAmountsOut(finalAmountIn, pathWithWeth) returns ( uint256[] memory resultsWithWeth ) { - vars.amountsWithWeth = resultsWithWeth; + amountsWithWeth = resultsWithWeth; } catch { - vars.amountsWithWeth = new uint256[](3); + amountsWithWeth = new uint256[](3); } } else { - vars.amountsWithWeth = new uint256[](3); + amountsWithWeth = new uint256[](3); } uint256 bestAmountOut; - try UNISWAP_ROUTER.getAmountsOut(vars.finalAmountIn, vars.simplePath) returns ( + try UNISWAP_ROUTER.getAmountsOut(finalAmountIn, simplePath) returns ( uint256[] memory resultAmounts ) { - vars.amountsWithoutWeth = resultAmounts; + amountsWithoutWeth = resultAmounts; - bestAmountOut = (vars.amountsWithWeth[2] > vars.amountsWithoutWeth[1]) - ? vars.amountsWithWeth[2] - : vars.amountsWithoutWeth[1]; + bestAmountOut = (amountsWithWeth[2] > amountsWithoutWeth[1]) + ? amountsWithWeth[2] + : amountsWithoutWeth[1]; } catch { - vars.amountsWithoutWeth = new uint256[](2); - bestAmountOut = vars.amountsWithWeth[2]; + amountsWithoutWeth = new uint256[](2); + bestAmountOut = amountsWithWeth[2]; } uint256 reserveInDecimals = _getDecimals(reserveIn); uint256 reserveOutDecimals = _getDecimals(reserveOut); uint256 outPerInPrice = - vars.finalAmountIn.mul(10**18).mul(10**reserveOutDecimals).div( + finalAmountIn.mul(10**18).mul(10**reserveOutDecimals).div( bestAmountOut.mul(10**reserveInDecimals) ); @@ -398,9 +387,9 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt outPerInPrice, _calcUsdValue(reserveIn, amountIn, reserveInDecimals), _calcUsdValue(reserveOut, bestAmountOut, reserveOutDecimals), - (bestAmountOut == 0) ? new address[](2) : (bestAmountOut == vars.amountsWithoutWeth[1]) - ? vars.simplePath - : vars.pathWithWeth + (bestAmountOut == 0) ? new address[](2) : (bestAmountOut == amountsWithoutWeth[1]) + ? simplePath + : pathWithWeth ); } @@ -418,15 +407,13 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt function _getAmountsInData( address reserveIn, address reserveOut, - uint256 amountOut, - bool withFlash + uint256 amountOut ) internal view returns (AmountCalc memory) { (uint256[] memory amounts, address[] memory path) = _getAmountsInAndPath(reserveIn, reserveOut, amountOut); // Add flash loan fee - uint256 finalAmountIn = - amounts[0].add(withFlash ? amounts[0].mul(FLASHLOAN_PREMIUM_TOTAL).div(10000) : 0); + uint256 finalAmountIn = amounts[0].add(amounts[0].mul(FLASHLOAN_PREMIUM_TOTAL).div(10000)); uint256 reserveInDecimals = _getDecimals(reserveIn); uint256 reserveOutDecimals = _getDecimals(reserveOut); diff --git a/contracts/adapters/interfaces/IBaseUniswapAdapter.sol b/contracts/adapters/interfaces/IBaseUniswapAdapter.sol index e94727a2..82997b74 100644 --- a/contracts/adapters/interfaces/IBaseUniswapAdapter.sol +++ b/contracts/adapters/interfaces/IBaseUniswapAdapter.sol @@ -50,8 +50,7 @@ interface IBaseUniswapAdapter { function getAmountsOut( uint256 amountIn, address reserveIn, - address reserveOut, - bool withFlash + address reserveOut ) external view @@ -77,8 +76,7 @@ interface IBaseUniswapAdapter { function getAmountsIn( uint256 amountOut, address reserveIn, - address reserveOut, - bool withFlash + address reserveOut ) external view diff --git a/tasks/deployments/deploy-UniswapRepayAdapter.ts b/tasks/deployments/deploy-UniswapRepayAdapter.ts index 77550730..58ba23a1 100644 --- a/tasks/deployments/deploy-UniswapRepayAdapter.ts +++ b/tasks/deployments/deploy-UniswapRepayAdapter.ts @@ -19,6 +19,10 @@ task(`deploy-${CONTRACT_NAME}`, `Deploys the ${CONTRACT_NAME} contract`) } console.log(`\n- ${CONTRACT_NAME} deployment`); + // const args = [ + // '0x88757f2f99175387aB4C6a4b3067c77A695b0349', // lending provider kovan address + // '0xfcd87315f0e4067070ade8682fcdbc3006631441', // uniswap router address + // ]; const uniswapRepayAdapter = await new UniswapRepayAdapterFactory(await getFirstSigner()).deploy( provider, router, From e5e61553c8344e7973e602f44c6031da11ec4077 Mon Sep 17 00:00:00 2001 From: eboado Date: Tue, 12 Jan 2021 14:21:44 +0100 Subject: [PATCH 60/62] - Removed comments from BaseUniswapAdapter --- contracts/adapters/BaseUniswapAdapter.sol | 3 --- 1 file changed, 3 deletions(-) diff --git a/contracts/adapters/BaseUniswapAdapter.sol b/contracts/adapters/BaseUniswapAdapter.sol index 70d4beed..8cd80faf 100644 --- a/contracts/adapters/BaseUniswapAdapter.sol +++ b/contracts/adapters/BaseUniswapAdapter.sol @@ -33,9 +33,6 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt // USD oracle asset address address public constant override USD_ADDRESS = 0x10F7Fc1F91Ba351f9C629c5947AD69bD03C05b96; - // address public constant WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; mainnet - // address public constant WETH_ADDRESS = 0xd0a1e359811322d97991e03f863a0c30c2cf029c; kovan - address public immutable override WETH_ADDRESS; IPriceOracleGetter public immutable override ORACLE; IUniswapV2Router02 public immutable override UNISWAP_ROUTER; From 611605eebf51a0be5fbd02f8a4fced68f0375072 Mon Sep 17 00:00:00 2001 From: David Racero Date: Wed, 13 Jan 2021 15:11:39 +0100 Subject: [PATCH 61/62] Fix mockup base tests. Separate uniswap adapter tests into separate files. --- .../mocks/swap/MockUniswapV2Router02.sol | 56 +- test/__setup.spec.ts | 24 +- test/uniswapAdapters.base.spec.ts | 227 +++ ... => uniswapAdapters.liquiditySwap.spec.ts} | 1591 +---------------- test/uniswapAdapters.repay.spec.ts | 1431 +++++++++++++++ 5 files changed, 1747 insertions(+), 1582 deletions(-) create mode 100644 test/uniswapAdapters.base.spec.ts rename test/{uniswapAdapters.spec.ts => uniswapAdapters.liquiditySwap.spec.ts} (53%) create mode 100644 test/uniswapAdapters.repay.spec.ts diff --git a/contracts/mocks/swap/MockUniswapV2Router02.sol b/contracts/mocks/swap/MockUniswapV2Router02.sol index 7f38bf92..b7fd3f80 100644 --- a/contracts/mocks/swap/MockUniswapV2Router02.sol +++ b/contracts/mocks/swap/MockUniswapV2Router02.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; -import {IUniswapV2Router02} from "../../interfaces/IUniswapV2Router02.sol"; +import {IUniswapV2Router02} from '../../interfaces/IUniswapV2Router02.sol'; import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import {MintableERC20} from '../tokens/MintableERC20.sol'; @@ -22,7 +22,7 @@ contract MockUniswapV2Router02 is IUniswapV2Router02 { function swapExactTokensForTokens( uint256 amountIn, - uint256 /* amountOutMin */, + uint256, /* amountOutMin */ address[] calldata path, address to, uint256 /* deadline */ @@ -32,50 +32,74 @@ contract MockUniswapV2Router02 is IUniswapV2Router02 { MintableERC20(path[1]).mint(_amountToReturn[path[0]]); IERC20(path[1]).transfer(to, _amountToReturn[path[0]]); - amounts = new uint[](path.length); + amounts = new uint256[](path.length); amounts[0] = amountIn; amounts[1] = _amountToReturn[path[0]]; } function swapTokensForExactTokens( - uint amountOut, - uint /* amountInMax */, + uint256 amountOut, + uint256, /* amountInMax */ address[] calldata path, address to, - uint /* deadline */ + uint256 /* deadline */ ) external override returns (uint256[] memory amounts) { IERC20(path[0]).transferFrom(msg.sender, address(this), _amountToSwap[path[0]]); MintableERC20(path[1]).mint(amountOut); IERC20(path[1]).transfer(to, amountOut); - amounts = new uint[](path.length); + amounts = new uint256[](path.length); amounts[0] = _amountToSwap[path[0]]; amounts[1] = amountOut; } - function setAmountOut(uint amountIn, address reserveIn, address reserveOut, uint amountOut) public { + function setAmountOut( + uint256 amountIn, + address reserveIn, + address reserveOut, + uint256 amountOut + ) public { _amountsOut[reserveIn][reserveOut][amountIn] = amountOut; } - function setAmountIn(uint amountOut, address reserveIn, address reserveOut, uint amountIn) public { + function setAmountIn( + uint256 amountOut, + address reserveIn, + address reserveOut, + uint256 amountIn + ) public { _amountsIn[reserveIn][reserveOut][amountOut] = amountIn; } - function setDefaultMockValue(uint value) public { + function setDefaultMockValue(uint256 value) public { defaultMockValue = value; } - function getAmountsOut(uint amountIn, address[] calldata path) external view override returns (uint[] memory) { - uint256[] memory amounts = new uint256[](2); + function getAmountsOut(uint256 amountIn, address[] calldata path) + external + view + override + returns (uint256[] memory) + { + uint256[] memory amounts = new uint256[](path.length); amounts[0] = amountIn; - amounts[1] = _amountsOut[path[0]][path[1]][amountIn] > 0 ? _amountsOut[path[0]][path[1]][amountIn] : defaultMockValue; + amounts[1] = _amountsOut[path[0]][path[1]][amountIn] > 0 + ? _amountsOut[path[0]][path[1]][amountIn] + : defaultMockValue; return amounts; } - function getAmountsIn(uint amountOut, address[] calldata path) external view override returns (uint[] memory) { - uint256[] memory amounts = new uint256[](2); - amounts[0] = _amountsIn[path[0]][path[1]][amountOut] > 0 ? _amountsIn[path[0]][path[1]][amountOut] : defaultMockValue; + function getAmountsIn(uint256 amountOut, address[] calldata path) + external + view + override + returns (uint256[] memory) + { + uint256[] memory amounts = new uint256[](path.length); + amounts[0] = _amountsIn[path[0]][path[1]][amountOut] > 0 + ? _amountsIn[path[0]][path[1]][amountOut] + : defaultMockValue; amounts[1] = amountOut; return amounts; } diff --git a/test/__setup.spec.ts b/test/__setup.spec.ts index 89f535e4..de3478af 100644 --- a/test/__setup.spec.ts +++ b/test/__setup.spec.ts @@ -29,7 +29,12 @@ import { import { Signer } from 'ethers'; import { TokenContractId, eContractid, tEthereumAddress, AavePools } from '../helpers/types'; import { MintableERC20 } from '../types/MintableERC20'; -import { ConfigNames, getReservesConfigByPool, getTreasuryAddress, loadPoolConfig } from '../helpers/configuration'; +import { + ConfigNames, + getReservesConfigByPool, + getTreasuryAddress, + loadPoolConfig, +} from '../helpers/configuration'; import { initializeMakeSuite } from './helpers/make-suite'; import { @@ -38,10 +43,7 @@ import { setInitialMarketRatesInRatesOracleByHelper, } from '../helpers/oracles-helpers'; import { DRE, waitForTx } from '../helpers/misc-utils'; -import { - initReservesByHelper, - configureReservesByHelper, -} from '../helpers/init-helpers'; +import { initReservesByHelper, configureReservesByHelper } from '../helpers/init-helpers'; import AaveConfig from '../markets/aave'; import { ZERO_ADDRESS } from '../helpers/constants'; import { @@ -216,13 +218,15 @@ const buildTestEnv = async (deployer: Signer, secondaryWallet: Signer) => { const treasuryAddress = await getTreasuryAddress(config); - await initReservesByHelper(reservesParams, allReservesAddresses, admin, treasuryAddress, ZERO_ADDRESS, false); - await configureReservesByHelper( + await initReservesByHelper( reservesParams, allReservesAddresses, - testHelpers, - admin + admin, + treasuryAddress, + ZERO_ADDRESS, + false ); + await configureReservesByHelper(reservesParams, allReservesAddresses, testHelpers, admin); const collateralManager = await deployLendingPoolCollateralManager(); await waitForTx( @@ -238,6 +242,7 @@ const buildTestEnv = async (deployer: Signer, secondaryWallet: Signer) => { const UniswapLiquiditySwapAdapter = await deployUniswapLiquiditySwapAdapter([ addressesProvider.address, mockUniswapRouter.address, + mockTokens.WETH.address, ]); await insertContractAddressInDb( eContractid.UniswapLiquiditySwapAdapter, @@ -247,6 +252,7 @@ const buildTestEnv = async (deployer: Signer, secondaryWallet: Signer) => { const UniswapRepayAdapter = await deployUniswapRepayAdapter([ addressesProvider.address, mockUniswapRouter.address, + mockTokens.WETH.address, ]); await insertContractAddressInDb(eContractid.UniswapRepayAdapter, UniswapRepayAdapter.address); diff --git a/test/uniswapAdapters.base.spec.ts b/test/uniswapAdapters.base.spec.ts new file mode 100644 index 00000000..80f76725 --- /dev/null +++ b/test/uniswapAdapters.base.spec.ts @@ -0,0 +1,227 @@ +import { makeSuite, TestEnv } from './helpers/make-suite'; +import { convertToCurrencyDecimals } from '../helpers/contracts-helpers'; +import { getMockUniswapRouter } from '../helpers/contracts-getters'; +import { MockUniswapV2Router02 } from '../types/MockUniswapV2Router02'; +import BigNumber from 'bignumber.js'; +import { evmRevert, evmSnapshot } from '../helpers/misc-utils'; +import { ethers } from 'ethers'; +import { USD_ADDRESS } from '../helpers/constants'; +const { parseEther } = ethers.utils; + +const { expect } = require('chai'); + +makeSuite('Uniswap adapters', (testEnv: TestEnv) => { + let mockUniswapRouter: MockUniswapV2Router02; + let evmSnapshotId: string; + + before(async () => { + mockUniswapRouter = await getMockUniswapRouter(); + }); + + beforeEach(async () => { + evmSnapshotId = await evmSnapshot(); + }); + + afterEach(async () => { + await evmRevert(evmSnapshotId); + }); + + describe('BaseUniswapAdapter', () => { + describe('getAmountsOut', () => { + it('should return the estimated amountOut and prices for the asset swap', async () => { + const { weth, dai, uniswapLiquiditySwapAdapter, oracle } = testEnv; + + const amountIn = parseEther('1'); + const flashloanPremium = amountIn.mul(9).div(10000); + const amountToSwap = amountIn.sub(flashloanPremium); + + const wethPrice = await oracle.getAssetPrice(weth.address); + const daiPrice = await oracle.getAssetPrice(dai.address); + const usdPrice = await oracle.getAssetPrice(USD_ADDRESS); + + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountToSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + const outPerInPrice = amountToSwap + .mul(parseEther('1')) + .mul(parseEther('1')) + .div(expectedDaiAmount.mul(parseEther('1'))); + const ethUsdValue = amountIn + .mul(wethPrice) + .div(parseEther('1')) + .mul(usdPrice) + .div(parseEther('1')); + const daiUsdValue = expectedDaiAmount + .mul(daiPrice) + .div(parseEther('1')) + .mul(usdPrice) + .div(parseEther('1')); + + await mockUniswapRouter.setAmountOut( + amountToSwap, + weth.address, + dai.address, + expectedDaiAmount + ); + + const result = await uniswapLiquiditySwapAdapter.getAmountsOut( + amountIn, + weth.address, + dai.address + ); + + expect(result['0']).to.be.eq(expectedDaiAmount); + expect(result['1']).to.be.eq(outPerInPrice); + expect(result['2']).to.be.eq(ethUsdValue); + expect(result['3']).to.be.eq(daiUsdValue); + }); + it('should work correctly with different decimals', async () => { + const { aave, usdc, uniswapLiquiditySwapAdapter, oracle } = testEnv; + + const amountIn = parseEther('10'); + const flashloanPremium = amountIn.mul(9).div(10000); + const amountToSwap = amountIn.sub(flashloanPremium); + + const aavePrice = await oracle.getAssetPrice(aave.address); + const usdcPrice = await oracle.getAssetPrice(usdc.address); + const usdPrice = await oracle.getAssetPrice(USD_ADDRESS); + + const expectedUSDCAmount = await convertToCurrencyDecimals( + usdc.address, + new BigNumber(amountToSwap.toString()).div(usdcPrice.toString()).toFixed(0) + ); + + const outPerInPrice = amountToSwap + .mul(parseEther('1')) + .mul('1000000') // usdc 6 decimals + .div(expectedUSDCAmount.mul(parseEther('1'))); + + const aaveUsdValue = amountIn + .mul(aavePrice) + .div(parseEther('1')) + .mul(usdPrice) + .div(parseEther('1')); + + const usdcUsdValue = expectedUSDCAmount + .mul(usdcPrice) + .div('1000000') // usdc 6 decimals + .mul(usdPrice) + .div(parseEther('1')); + + await mockUniswapRouter.setAmountOut( + amountToSwap, + aave.address, + usdc.address, + expectedUSDCAmount + ); + + const result = await uniswapLiquiditySwapAdapter.getAmountsOut( + amountIn, + aave.address, + usdc.address + ); + + expect(result['0']).to.be.eq(expectedUSDCAmount); + expect(result['1']).to.be.eq(outPerInPrice); + expect(result['2']).to.be.eq(aaveUsdValue); + expect(result['3']).to.be.eq(usdcUsdValue); + }); + }); + + describe('getAmountsIn', () => { + it('should return the estimated required amountIn for the asset swap', async () => { + const { weth, dai, uniswapLiquiditySwapAdapter, oracle } = testEnv; + + const amountIn = parseEther('1'); + const flashloanPremium = amountIn.mul(9).div(10000); + const amountToSwap = amountIn.add(flashloanPremium); + + const wethPrice = await oracle.getAssetPrice(weth.address); + const daiPrice = await oracle.getAssetPrice(dai.address); + const usdPrice = await oracle.getAssetPrice(USD_ADDRESS); + + const amountOut = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountIn.toString()).div(daiPrice.toString()).toFixed(0) + ); + + const inPerOutPrice = amountOut + .mul(parseEther('1')) + .mul(parseEther('1')) + .div(amountToSwap.mul(parseEther('1'))); + + const ethUsdValue = amountToSwap + .mul(wethPrice) + .div(parseEther('1')) + .mul(usdPrice) + .div(parseEther('1')); + const daiUsdValue = amountOut + .mul(daiPrice) + .div(parseEther('1')) + .mul(usdPrice) + .div(parseEther('1')); + + await mockUniswapRouter.setAmountIn(amountOut, weth.address, dai.address, amountIn); + + const result = await uniswapLiquiditySwapAdapter.getAmountsIn( + amountOut, + weth.address, + dai.address + ); + + expect(result['0']).to.be.eq(amountToSwap); + expect(result['1']).to.be.eq(inPerOutPrice); + expect(result['2']).to.be.eq(ethUsdValue); + expect(result['3']).to.be.eq(daiUsdValue); + }); + it('should work correctly with different decimals', async () => { + const { aave, usdc, uniswapLiquiditySwapAdapter, oracle } = testEnv; + + const amountIn = parseEther('10'); + const flashloanPremium = amountIn.mul(9).div(10000); + const amountToSwap = amountIn.add(flashloanPremium); + + const aavePrice = await oracle.getAssetPrice(aave.address); + const usdcPrice = await oracle.getAssetPrice(usdc.address); + const usdPrice = await oracle.getAssetPrice(USD_ADDRESS); + + const amountOut = await convertToCurrencyDecimals( + usdc.address, + new BigNumber(amountToSwap.toString()).div(usdcPrice.toString()).toFixed(0) + ); + + const inPerOutPrice = amountOut + .mul(parseEther('1')) + .mul(parseEther('1')) + .div(amountToSwap.mul('1000000')); // usdc 6 decimals + + const aaveUsdValue = amountToSwap + .mul(aavePrice) + .div(parseEther('1')) + .mul(usdPrice) + .div(parseEther('1')); + + const usdcUsdValue = amountOut + .mul(usdcPrice) + .div('1000000') // usdc 6 decimals + .mul(usdPrice) + .div(parseEther('1')); + + await mockUniswapRouter.setAmountIn(amountOut, aave.address, usdc.address, amountIn); + + const result = await uniswapLiquiditySwapAdapter.getAmountsIn( + amountOut, + aave.address, + usdc.address + ); + + expect(result['0']).to.be.eq(amountToSwap); + expect(result['1']).to.be.eq(inPerOutPrice); + expect(result['2']).to.be.eq(aaveUsdValue); + expect(result['3']).to.be.eq(usdcUsdValue); + }); + }); + }); +}); diff --git a/test/uniswapAdapters.spec.ts b/test/uniswapAdapters.liquiditySwap.spec.ts similarity index 53% rename from test/uniswapAdapters.spec.ts rename to test/uniswapAdapters.liquiditySwap.spec.ts index e5dcb7f5..65882c0e 100644 --- a/test/uniswapAdapters.spec.ts +++ b/test/uniswapAdapters.liquiditySwap.spec.ts @@ -5,13 +5,9 @@ import { buildPermitParams, getSignatureFromTypedData, buildLiquiditySwapParams, - buildRepayAdapterParams, } from '../helpers/contracts-helpers'; import { getMockUniswapRouter } from '../helpers/contracts-getters'; -import { - deployUniswapLiquiditySwapAdapter, - deployUniswapRepayAdapter, -} from '../helpers/contracts-deployments'; +import { deployUniswapLiquiditySwapAdapter } from '../helpers/contracts-deployments'; import { MockUniswapV2Router02 } from '../types/MockUniswapV2Router02'; import { Zero } from '@ethersproject/constants'; import BigNumber from 'bignumber.js'; @@ -19,9 +15,8 @@ import { DRE, evmRevert, evmSnapshot } from '../helpers/misc-utils'; import { ethers } from 'ethers'; import { eContractid } from '../helpers/types'; import { AToken } from '../types/AToken'; -import { StableDebtToken } from '../types/StableDebtToken'; import { BUIDLEREVM_CHAINID } from '../helpers/buidler-constants'; -import { MAX_UINT_AMOUNT, USD_ADDRESS } from '../helpers/constants'; +import { MAX_UINT_AMOUNT } from '../helpers/constants'; const { parseEther } = ethers.utils; const { expect } = require('chai'); @@ -42,218 +37,25 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await evmRevert(evmSnapshotId); }); - describe('BaseUniswapAdapter', () => { - describe('getAmountsOut', () => { - it('should return the estimated amountOut and prices for the asset swap', async () => { - const { weth, dai, uniswapLiquiditySwapAdapter, oracle } = testEnv; - - const amountIn = parseEther('1'); - const flashloanPremium = amountIn.mul(9).div(10000); - const amountToSwap = amountIn.sub(flashloanPremium); - - const wethPrice = await oracle.getAssetPrice(weth.address); - const daiPrice = await oracle.getAssetPrice(dai.address); - const usdPrice = await oracle.getAssetPrice(USD_ADDRESS); - - const expectedDaiAmount = await convertToCurrencyDecimals( - dai.address, - new BigNumber(amountToSwap.toString()).div(daiPrice.toString()).toFixed(0) - ); - - const outPerInPrice = amountToSwap - .mul(parseEther('1')) - .mul(parseEther('1')) - .div(expectedDaiAmount.mul(parseEther('1'))); - const ethUsdValue = amountIn - .mul(wethPrice) - .div(parseEther('1')) - .mul(usdPrice) - .div(parseEther('1')); - const daiUsdValue = expectedDaiAmount - .mul(daiPrice) - .div(parseEther('1')) - .mul(usdPrice) - .div(parseEther('1')); - - await mockUniswapRouter.setAmountOut( - amountToSwap, - weth.address, - dai.address, - expectedDaiAmount - ); - - const result = await uniswapLiquiditySwapAdapter.getAmountsOut( - amountIn, - weth.address, - dai.address - ); - - expect(result['0']).to.be.eq(expectedDaiAmount); - expect(result['1']).to.be.eq(outPerInPrice); - expect(result['2']).to.be.eq(ethUsdValue); - expect(result['3']).to.be.eq(daiUsdValue); - }); - it('should work correctly with different decimals', async () => { - const { aave, usdc, uniswapLiquiditySwapAdapter, oracle } = testEnv; - - const amountIn = parseEther('10'); - const flashloanPremium = amountIn.mul(9).div(10000); - const amountToSwap = amountIn.sub(flashloanPremium); - - const aavePrice = await oracle.getAssetPrice(aave.address); - const usdcPrice = await oracle.getAssetPrice(usdc.address); - const usdPrice = await oracle.getAssetPrice(USD_ADDRESS); - - const expectedUSDCAmount = await convertToCurrencyDecimals( - usdc.address, - new BigNumber(amountToSwap.toString()).div(usdcPrice.toString()).toFixed(0) - ); - - const outPerInPrice = amountToSwap - .mul(parseEther('1')) - .mul('1000000') // usdc 6 decimals - .div(expectedUSDCAmount.mul(parseEther('1'))); - - const aaveUsdValue = amountIn - .mul(aavePrice) - .div(parseEther('1')) - .mul(usdPrice) - .div(parseEther('1')); - - const usdcUsdValue = expectedUSDCAmount - .mul(usdcPrice) - .div('1000000') // usdc 6 decimals - .mul(usdPrice) - .div(parseEther('1')); - - await mockUniswapRouter.setAmountOut( - amountToSwap, - aave.address, - usdc.address, - expectedUSDCAmount - ); - - const result = await uniswapLiquiditySwapAdapter.getAmountsOut( - amountIn, - aave.address, - usdc.address - ); - - expect(result['0']).to.be.eq(expectedUSDCAmount); - expect(result['1']).to.be.eq(outPerInPrice); - expect(result['2']).to.be.eq(aaveUsdValue); - expect(result['3']).to.be.eq(usdcUsdValue); - }); - }); - - describe('getAmountsIn', () => { - it('should return the estimated required amountIn for the asset swap', async () => { - const { weth, dai, uniswapLiquiditySwapAdapter, oracle } = testEnv; - - const amountIn = parseEther('1'); - const flashloanPremium = amountIn.mul(9).div(10000); - const amountToSwap = amountIn.add(flashloanPremium); - - const wethPrice = await oracle.getAssetPrice(weth.address); - const daiPrice = await oracle.getAssetPrice(dai.address); - const usdPrice = await oracle.getAssetPrice(USD_ADDRESS); - - const amountOut = await convertToCurrencyDecimals( - dai.address, - new BigNumber(amountIn.toString()).div(daiPrice.toString()).toFixed(0) - ); - - const inPerOutPrice = amountOut - .mul(parseEther('1')) - .mul(parseEther('1')) - .div(amountToSwap.mul(parseEther('1'))); - - const ethUsdValue = amountToSwap - .mul(wethPrice) - .div(parseEther('1')) - .mul(usdPrice) - .div(parseEther('1')); - const daiUsdValue = amountOut - .mul(daiPrice) - .div(parseEther('1')) - .mul(usdPrice) - .div(parseEther('1')); - - await mockUniswapRouter.setAmountIn(amountOut, weth.address, dai.address, amountIn); - - const result = await uniswapLiquiditySwapAdapter.getAmountsIn( - amountOut, - weth.address, - dai.address - ); - - expect(result['0']).to.be.eq(amountToSwap); - expect(result['1']).to.be.eq(inPerOutPrice); - expect(result['2']).to.be.eq(ethUsdValue); - expect(result['3']).to.be.eq(daiUsdValue); - }); - it('should work correctly with different decimals', async () => { - const { aave, usdc, uniswapLiquiditySwapAdapter, oracle } = testEnv; - - const amountIn = parseEther('10'); - const flashloanPremium = amountIn.mul(9).div(10000); - const amountToSwap = amountIn.add(flashloanPremium); - - const aavePrice = await oracle.getAssetPrice(aave.address); - const usdcPrice = await oracle.getAssetPrice(usdc.address); - const usdPrice = await oracle.getAssetPrice(USD_ADDRESS); - - const amountOut = await convertToCurrencyDecimals( - usdc.address, - new BigNumber(amountToSwap.toString()).div(usdcPrice.toString()).toFixed(0) - ); - - const inPerOutPrice = amountOut - .mul(parseEther('1')) - .mul(parseEther('1')) - .div(amountToSwap.mul('1000000')); // usdc 6 decimals - - const aaveUsdValue = amountToSwap - .mul(aavePrice) - .div(parseEther('1')) - .mul(usdPrice) - .div(parseEther('1')); - - const usdcUsdValue = amountOut - .mul(usdcPrice) - .div('1000000') // usdc 6 decimals - .mul(usdPrice) - .div(parseEther('1')); - - await mockUniswapRouter.setAmountIn(amountOut, aave.address, usdc.address, amountIn); - - const result = await uniswapLiquiditySwapAdapter.getAmountsIn( - amountOut, - aave.address, - usdc.address - ); - - expect(result['0']).to.be.eq(amountToSwap); - expect(result['1']).to.be.eq(inPerOutPrice); - expect(result['2']).to.be.eq(aaveUsdValue); - expect(result['3']).to.be.eq(usdcUsdValue); - }); - }); - }); - describe('UniswapLiquiditySwapAdapter', () => { describe('constructor', () => { it('should deploy with correct parameters', async () => { - const { addressesProvider } = testEnv; + const { addressesProvider, weth } = testEnv; await deployUniswapLiquiditySwapAdapter([ addressesProvider.address, mockUniswapRouter.address, + weth.address, ]); }); it('should revert if not valid addresses provider', async () => { + const { weth } = testEnv; expect( - deployUniswapLiquiditySwapAdapter([mockUniswapRouter.address, mockUniswapRouter.address]) + deployUniswapLiquiditySwapAdapter([ + mockUniswapRouter.address, + mockUniswapRouter.address, + weth.address, + ]) ).to.be.reverted; }); }); @@ -1367,7 +1169,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { r: '0x0000000000000000000000000000000000000000000000000000000000000000', s: '0x0000000000000000000000000000000000000000000000000000000000000000', }, - ] + ], + [false] ) ) .to.emit(uniswapLiquiditySwapAdapter, 'Swapped') @@ -1445,7 +1248,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { r, s, }, - ] + ], + [false] ) ) .to.emit(uniswapLiquiditySwapAdapter, 'Swapped') @@ -1493,7 +1297,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { r: '0x0000000000000000000000000000000000000000000000000000000000000000', s: '0x0000000000000000000000000000000000000000000000000000000000000000', }, - ] + ], + [false] ) ).to.be.revertedWith('INCONSISTENT_PARAMS'); @@ -1511,7 +1316,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { r: '0x0000000000000000000000000000000000000000000000000000000000000000', s: '0x0000000000000000000000000000000000000000000000000000000000000000', }, - ] + ], + [false] ) ).to.be.revertedWith('INCONSISTENT_PARAMS'); @@ -1529,7 +1335,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { r: '0x0000000000000000000000000000000000000000000000000000000000000000', s: '0x0000000000000000000000000000000000000000000000000000000000000000', }, - ] + ], + [false] ) ).to.be.revertedWith('INCONSISTENT_PARAMS'); @@ -1541,7 +1348,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { [dai.address], [amountWETHtoSwap], [expectedDaiAmount], - [] + [], + [false] ) ).to.be.revertedWith('INCONSISTENT_PARAMS'); @@ -1559,7 +1367,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { r: '0x0000000000000000000000000000000000000000000000000000000000000000', s: '0x0000000000000000000000000000000000000000000000000000000000000000', }, - ] + ], + [false] ) ).to.be.revertedWith('INCONSISTENT_PARAMS'); }); @@ -1597,7 +1406,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { r: '0x0000000000000000000000000000000000000000000000000000000000000000', s: '0x0000000000000000000000000000000000000000000000000000000000000000', }, - ] + ], + [false] ) ).to.be.revertedWith('minAmountOut exceed max slippage'); }); @@ -1679,7 +1489,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { r: '0x0000000000000000000000000000000000000000000000000000000000000000', s: '0x0000000000000000000000000000000000000000000000000000000000000000', }, - ] + ], + [false] ); const adapterWethBalance = await weth.balanceOf(uniswapLiquiditySwapAdapter.address); @@ -1818,7 +1629,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { r: aUsdcr, s: aUsdcs, }, - ] + ], + [false] ); const adapterWethBalance = await weth.balanceOf(uniswapLiquiditySwapAdapter.address); @@ -1884,7 +1696,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { r: '0x0000000000000000000000000000000000000000000000000000000000000000', s: '0x0000000000000000000000000000000000000000000000000000000000000000', }, - ] + ], + [false] ) ) .to.emit(uniswapLiquiditySwapAdapter, 'Swapped') @@ -1969,7 +1782,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { r, s, }, - ] + ], + [false] ) ) .to.emit(uniswapLiquiditySwapAdapter, 'Swapped') @@ -1994,1341 +1808,4 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { }); }); }); - - describe('UniswapRepayAdapter', () => { - beforeEach(async () => { - const { users, weth, dai, usdc, aave, pool, deployer } = testEnv; - const userAddress = users[0].address; - - // Provide liquidity - await dai.mint(parseEther('20000')); - await dai.approve(pool.address, parseEther('20000')); - await pool.deposit(dai.address, parseEther('20000'), deployer.address, 0); - - const usdcLiquidity = await convertToCurrencyDecimals(usdc.address, '2000000'); - await usdc.mint(usdcLiquidity); - await usdc.approve(pool.address, usdcLiquidity); - await pool.deposit(usdc.address, usdcLiquidity, deployer.address, 0); - - await weth.mint(parseEther('100')); - await weth.approve(pool.address, parseEther('100')); - await pool.deposit(weth.address, parseEther('100'), deployer.address, 0); - - await aave.mint(parseEther('1000000')); - await aave.approve(pool.address, parseEther('1000000')); - await pool.deposit(aave.address, parseEther('1000000'), deployer.address, 0); - - // Make a deposit for user - await weth.mint(parseEther('1000')); - await weth.approve(pool.address, parseEther('1000')); - await pool.deposit(weth.address, parseEther('1000'), userAddress, 0); - - await aave.mint(parseEther('1000000')); - await aave.approve(pool.address, parseEther('1000000')); - await pool.deposit(aave.address, parseEther('1000000'), userAddress, 0); - - await usdc.mint(usdcLiquidity); - await usdc.approve(pool.address, usdcLiquidity); - await pool.deposit(usdc.address, usdcLiquidity, userAddress, 0); - }); - - describe('constructor', () => { - it('should deploy with correct parameters', async () => { - const { addressesProvider } = testEnv; - await deployUniswapRepayAdapter([addressesProvider.address, mockUniswapRouter.address]); - }); - - it('should revert if not valid addresses provider', async () => { - expect(deployUniswapRepayAdapter([mockUniswapRouter.address, mockUniswapRouter.address])).to - .be.reverted; - }); - }); - - describe('executeOperation', () => { - it('should correctly swap tokens and repay debt', async () => { - const { - users, - pool, - weth, - aWETH, - oracle, - dai, - uniswapRepayAdapter, - helpersContract, - } = testEnv; - const user = users[0].signer; - const userAddress = users[0].address; - - const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); - - const daiPrice = await oracle.getAssetPrice(dai.address); - const expectedDaiAmount = await convertToCurrencyDecimals( - dai.address, - new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) - ); - - // Open user Debt - await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); - - const daiStableDebtTokenAddress = ( - await helpersContract.getReserveTokensAddresses(dai.address) - ).stableDebtTokenAddress; - - const daiStableDebtContract = await getContract( - eContractid.StableDebtToken, - daiStableDebtTokenAddress - ); - - const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); - - const liquidityToSwap = amountWETHtoSwap; - await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); - const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); - - await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap); - - const flashLoanDebt = new BigNumber(expectedDaiAmount.toString()) - .multipliedBy(1.0009) - .toFixed(0); - - await mockUniswapRouter.setAmountIn( - flashLoanDebt, - weth.address, - dai.address, - liquidityToSwap - ); - - const params = buildRepayAdapterParams( - weth.address, - liquidityToSwap, - 1, - 0, - 0, - 0, - '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000' - ); - - await expect( - pool - .connect(user) - .flashLoan( - uniswapRepayAdapter.address, - [dai.address], - [expectedDaiAmount.toString()], - [0], - userAddress, - params, - 0 - ) - ) - .to.emit(uniswapRepayAdapter, 'Swapped') - .withArgs(weth.address, dai.address, liquidityToSwap.toString(), flashLoanDebt); - - const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); - const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); - const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); - const userAEthBalance = await aWETH.balanceOf(userAddress); - - expect(adapterWethBalance).to.be.eq(Zero); - expect(adapterDaiBalance).to.be.eq(Zero); - expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount); - expect(userDaiStableDebtAmount).to.be.lt(expectedDaiAmount); - expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); - expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); - }); - - it('should correctly swap tokens and repay debt with permit', async () => { - const { - users, - pool, - weth, - aWETH, - oracle, - dai, - uniswapRepayAdapter, - helpersContract, - } = testEnv; - const user = users[0].signer; - const userAddress = users[0].address; - - const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); - - const daiPrice = await oracle.getAssetPrice(dai.address); - const expectedDaiAmount = await convertToCurrencyDecimals( - dai.address, - new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) - ); - - // Open user Debt - await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); - - const daiStableDebtTokenAddress = ( - await helpersContract.getReserveTokensAddresses(dai.address) - ).stableDebtTokenAddress; - - const daiStableDebtContract = await getContract( - eContractid.StableDebtToken, - daiStableDebtTokenAddress - ); - - const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); - - const liquidityToSwap = amountWETHtoSwap; - const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); - - const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID; - const deadline = MAX_UINT_AMOUNT; - const nonce = (await aWETH._nonces(userAddress)).toNumber(); - const msgParams = buildPermitParams( - chainId, - aWETH.address, - '1', - await aWETH.name(), - userAddress, - uniswapRepayAdapter.address, - nonce, - deadline, - liquidityToSwap.toString() - ); - - const ownerPrivateKey = require('../test-wallets.js').accounts[1].secretKey; - if (!ownerPrivateKey) { - throw new Error('INVALID_OWNER_PK'); - } - - const { v, r, s } = getSignatureFromTypedData(ownerPrivateKey, msgParams); - - await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap); - - const flashLoanDebt = new BigNumber(expectedDaiAmount.toString()) - .multipliedBy(1.0009) - .toFixed(0); - - await mockUniswapRouter.setAmountIn( - flashLoanDebt, - weth.address, - dai.address, - liquidityToSwap - ); - - const params = buildRepayAdapterParams( - weth.address, - liquidityToSwap, - 1, - liquidityToSwap, - deadline, - v, - r, - s - ); - - await expect( - pool - .connect(user) - .flashLoan( - uniswapRepayAdapter.address, - [dai.address], - [expectedDaiAmount.toString()], - [0], - userAddress, - params, - 0 - ) - ) - .to.emit(uniswapRepayAdapter, 'Swapped') - .withArgs(weth.address, dai.address, liquidityToSwap.toString(), flashLoanDebt); - - const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); - const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); - const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); - const userAEthBalance = await aWETH.balanceOf(userAddress); - - expect(adapterWethBalance).to.be.eq(Zero); - expect(adapterDaiBalance).to.be.eq(Zero); - expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount); - expect(userDaiStableDebtAmount).to.be.lt(expectedDaiAmount); - expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); - expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); - }); - - it('should revert if caller not lending pool', async () => { - const { users, pool, weth, aWETH, oracle, dai, uniswapRepayAdapter } = testEnv; - const user = users[0].signer; - const userAddress = users[0].address; - - const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); - - const daiPrice = await oracle.getAssetPrice(dai.address); - const expectedDaiAmount = await convertToCurrencyDecimals( - dai.address, - new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) - ); - - // Open user Debt - await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); - - const liquidityToSwap = amountWETHtoSwap; - await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); - - await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap); - - const params = buildRepayAdapterParams( - weth.address, - liquidityToSwap, - 1, - 0, - 0, - 0, - '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000' - ); - - await expect( - uniswapRepayAdapter - .connect(user) - .executeOperation( - [dai.address], - [expectedDaiAmount.toString()], - [0], - userAddress, - params - ) - ).to.be.revertedWith('CALLER_MUST_BE_LENDING_POOL'); - }); - - it('should revert if there is not debt to repay with the specified rate mode', async () => { - const { users, pool, weth, oracle, dai, uniswapRepayAdapter, aWETH } = testEnv; - const user = users[0].signer; - const userAddress = users[0].address; - - const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); - - await weth.connect(user).mint(amountWETHtoSwap); - await weth.connect(user).transfer(uniswapRepayAdapter.address, amountWETHtoSwap); - - const daiPrice = await oracle.getAssetPrice(dai.address); - const expectedDaiAmount = await convertToCurrencyDecimals( - dai.address, - new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) - ); - - // Open user Debt - await pool.connect(user).borrow(dai.address, expectedDaiAmount, 2, 0, userAddress); - - const liquidityToSwap = amountWETHtoSwap; - await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); - - await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap); - - const params = buildRepayAdapterParams( - weth.address, - liquidityToSwap, - 1, - 0, - 0, - 0, - '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000' - ); - - await expect( - pool - .connect(user) - .flashLoan( - uniswapRepayAdapter.address, - [dai.address], - [expectedDaiAmount.toString()], - [0], - userAddress, - params, - 0 - ) - ).to.be.reverted; - }); - - it('should revert if there is not debt to repay', async () => { - const { users, pool, weth, oracle, dai, uniswapRepayAdapter, aWETH } = testEnv; - const user = users[0].signer; - const userAddress = users[0].address; - - const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); - - await weth.connect(user).mint(amountWETHtoSwap); - await weth.connect(user).transfer(uniswapRepayAdapter.address, amountWETHtoSwap); - - const daiPrice = await oracle.getAssetPrice(dai.address); - const expectedDaiAmount = await convertToCurrencyDecimals( - dai.address, - new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) - ); - - const liquidityToSwap = amountWETHtoSwap; - await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); - - await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap); - - const params = buildRepayAdapterParams( - weth.address, - liquidityToSwap, - 1, - 0, - 0, - 0, - '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000' - ); - - await expect( - pool - .connect(user) - .flashLoan( - uniswapRepayAdapter.address, - [dai.address], - [expectedDaiAmount.toString()], - [0], - userAddress, - params, - 0 - ) - ).to.be.reverted; - }); - - it('should revert when max amount allowed to swap is bigger than max slippage', async () => { - const { users, pool, weth, oracle, dai, aWETH, uniswapRepayAdapter } = testEnv; - const user = users[0].signer; - const userAddress = users[0].address; - - const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); - - const daiPrice = await oracle.getAssetPrice(dai.address); - const expectedDaiAmount = await convertToCurrencyDecimals( - dai.address, - new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) - ); - - // Open user Debt - await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); - - const bigMaxAmountToSwap = amountWETHtoSwap.mul(2); - await aWETH.connect(user).approve(uniswapRepayAdapter.address, bigMaxAmountToSwap); - - await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, bigMaxAmountToSwap); - - const flashLoanDebt = new BigNumber(expectedDaiAmount.toString()) - .multipliedBy(1.0009) - .toFixed(0); - - await mockUniswapRouter.setAmountIn( - flashLoanDebt, - weth.address, - dai.address, - bigMaxAmountToSwap - ); - - const params = buildRepayAdapterParams( - weth.address, - bigMaxAmountToSwap, - 1, - 0, - 0, - 0, - '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000' - ); - - await expect( - pool - .connect(user) - .flashLoan( - uniswapRepayAdapter.address, - [dai.address], - [expectedDaiAmount.toString()], - [0], - userAddress, - params, - 0 - ) - ).to.be.revertedWith('maxAmountToSwap exceed max slippage'); - }); - - it('should swap, repay debt and pull the needed ATokens leaving no leftovers', async () => { - const { - users, - pool, - weth, - aWETH, - oracle, - dai, - uniswapRepayAdapter, - helpersContract, - } = testEnv; - const user = users[0].signer; - const userAddress = users[0].address; - - const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); - - const daiPrice = await oracle.getAssetPrice(dai.address); - const expectedDaiAmount = await convertToCurrencyDecimals( - dai.address, - new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) - ); - - // Open user Debt - await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); - - const daiStableDebtTokenAddress = ( - await helpersContract.getReserveTokensAddresses(dai.address) - ).stableDebtTokenAddress; - - const daiStableDebtContract = await getContract( - eContractid.StableDebtToken, - daiStableDebtTokenAddress - ); - - const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); - - const liquidityToSwap = amountWETHtoSwap; - await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); - const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); - const userWethBalanceBefore = await weth.balanceOf(userAddress); - - const actualWEthSwapped = new BigNumber(liquidityToSwap.toString()) - .multipliedBy(0.995) - .toFixed(0); - - await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, actualWEthSwapped); - - const flashLoanDebt = new BigNumber(expectedDaiAmount.toString()) - .multipliedBy(1.0009) - .toFixed(0); - - await mockUniswapRouter.setAmountIn( - flashLoanDebt, - weth.address, - dai.address, - actualWEthSwapped - ); - - const params = buildRepayAdapterParams( - weth.address, - liquidityToSwap, - 1, - 0, - 0, - 0, - '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000' - ); - - await expect( - pool - .connect(user) - .flashLoan( - uniswapRepayAdapter.address, - [dai.address], - [expectedDaiAmount.toString()], - [0], - userAddress, - params, - 0 - ) - ) - .to.emit(uniswapRepayAdapter, 'Swapped') - .withArgs(weth.address, dai.address, actualWEthSwapped.toString(), flashLoanDebt); - - const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); - const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); - const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); - const userAEthBalance = await aWETH.balanceOf(userAddress); - const adapterAEthBalance = await aWETH.balanceOf(uniswapRepayAdapter.address); - const userWethBalance = await weth.balanceOf(userAddress); - - expect(adapterAEthBalance).to.be.eq(Zero); - expect(adapterWethBalance).to.be.eq(Zero); - expect(adapterDaiBalance).to.be.eq(Zero); - expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount); - expect(userDaiStableDebtAmount).to.be.lt(expectedDaiAmount); - expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); - expect(userAEthBalance).to.be.eq(userAEthBalanceBefore.sub(actualWEthSwapped)); - expect(userWethBalance).to.be.eq(userWethBalanceBefore); - }); - - it('should correctly swap tokens and repay the whole stable debt', async () => { - const { - users, - pool, - weth, - aWETH, - oracle, - dai, - uniswapRepayAdapter, - helpersContract, - } = testEnv; - const user = users[0].signer; - const userAddress = users[0].address; - - const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); - - const daiPrice = await oracle.getAssetPrice(dai.address); - const expectedDaiAmount = await convertToCurrencyDecimals( - dai.address, - new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) - ); - - // Open user Debt - await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); - - const daiStableDebtTokenAddress = ( - await helpersContract.getReserveTokensAddresses(dai.address) - ).stableDebtTokenAddress; - - const daiStableDebtContract = await getContract( - eContractid.StableDebtToken, - daiStableDebtTokenAddress - ); - - const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); - - // Add a % to repay on top of the debt - const liquidityToSwap = new BigNumber(amountWETHtoSwap.toString()) - .multipliedBy(1.1) - .toFixed(0); - - await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); - const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); - - // Add a % to repay on top of the debt - const amountToRepay = new BigNumber(expectedDaiAmount.toString()) - .multipliedBy(1.1) - .toFixed(0); - - await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, amountWETHtoSwap); - await mockUniswapRouter.setDefaultMockValue(amountWETHtoSwap); - - const params = buildRepayAdapterParams( - weth.address, - liquidityToSwap, - 1, - 0, - 0, - 0, - '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000' - ); - - await pool - .connect(user) - .flashLoan( - uniswapRepayAdapter.address, - [dai.address], - [amountToRepay.toString()], - [0], - userAddress, - params, - 0 - ); - - const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); - const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); - const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); - const userAEthBalance = await aWETH.balanceOf(userAddress); - const adapterAEthBalance = await aWETH.balanceOf(uniswapRepayAdapter.address); - - expect(adapterAEthBalance).to.be.eq(Zero); - expect(adapterWethBalance).to.be.eq(Zero); - expect(adapterDaiBalance).to.be.eq(Zero); - expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount); - expect(userDaiStableDebtAmount).to.be.eq(Zero); - expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); - expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); - }); - - it('should correctly swap tokens and repay the whole variable debt', async () => { - const { - users, - pool, - weth, - aWETH, - oracle, - dai, - uniswapRepayAdapter, - helpersContract, - } = testEnv; - const user = users[0].signer; - const userAddress = users[0].address; - - const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); - - const daiPrice = await oracle.getAssetPrice(dai.address); - const expectedDaiAmount = await convertToCurrencyDecimals( - dai.address, - new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) - ); - - // Open user Debt - await pool.connect(user).borrow(dai.address, expectedDaiAmount, 2, 0, userAddress); - - const daiStableVariableTokenAddress = ( - await helpersContract.getReserveTokensAddresses(dai.address) - ).variableDebtTokenAddress; - - const daiVariableDebtContract = await getContract( - eContractid.VariableDebtToken, - daiStableVariableTokenAddress - ); - - const userDaiVariableDebtAmountBefore = await daiVariableDebtContract.balanceOf( - userAddress - ); - - // Add a % to repay on top of the debt - const liquidityToSwap = new BigNumber(amountWETHtoSwap.toString()) - .multipliedBy(1.1) - .toFixed(0); - - await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); - const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); - - // Add a % to repay on top of the debt - const amountToRepay = new BigNumber(expectedDaiAmount.toString()) - .multipliedBy(1.1) - .toFixed(0); - - await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, amountWETHtoSwap); - await mockUniswapRouter.setDefaultMockValue(amountWETHtoSwap); - - const params = buildRepayAdapterParams( - weth.address, - liquidityToSwap, - 2, - 0, - 0, - 0, - '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000' - ); - - await pool - .connect(user) - .flashLoan( - uniswapRepayAdapter.address, - [dai.address], - [amountToRepay.toString()], - [0], - userAddress, - params, - 0 - ); - - const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); - const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); - const userDaiVariableDebtAmount = await daiVariableDebtContract.balanceOf(userAddress); - const userAEthBalance = await aWETH.balanceOf(userAddress); - const adapterAEthBalance = await aWETH.balanceOf(uniswapRepayAdapter.address); - - expect(adapterAEthBalance).to.be.eq(Zero); - expect(adapterWethBalance).to.be.eq(Zero); - expect(adapterDaiBalance).to.be.eq(Zero); - expect(userDaiVariableDebtAmountBefore).to.be.gte(expectedDaiAmount); - expect(userDaiVariableDebtAmount).to.be.eq(Zero); - expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); - expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); - }); - - it('should correctly repay debt using the same asset as collateral', async () => { - const { users, pool, aDai, dai, uniswapRepayAdapter, helpersContract } = testEnv; - const user = users[0].signer; - const userAddress = users[0].address; - - // Add deposit for user - await dai.mint(parseEther('20')); - await dai.approve(pool.address, parseEther('20')); - await pool.deposit(dai.address, parseEther('20'), userAddress, 0); - - const amountCollateralToSwap = parseEther('10'); - const debtAmount = parseEther('10'); - - // Open user Debt - await pool.connect(user).borrow(dai.address, debtAmount, 1, 0, userAddress); - - const daiStableDebtTokenAddress = ( - await helpersContract.getReserveTokensAddresses(dai.address) - ).stableDebtTokenAddress; - - const daiStableDebtContract = await getContract( - eContractid.StableDebtToken, - daiStableDebtTokenAddress - ); - - const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); - - const flashLoanDebt = new BigNumber(amountCollateralToSwap.toString()) - .multipliedBy(1.0009) - .toFixed(0); - - await aDai.connect(user).approve(uniswapRepayAdapter.address, flashLoanDebt); - const userADaiBalanceBefore = await aDai.balanceOf(userAddress); - const userDaiBalanceBefore = await dai.balanceOf(userAddress); - - const params = buildRepayAdapterParams( - dai.address, - amountCollateralToSwap, - 1, - 0, - 0, - 0, - '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000' - ); - - await pool - .connect(user) - .flashLoan( - uniswapRepayAdapter.address, - [dai.address], - [amountCollateralToSwap.toString()], - [0], - userAddress, - params, - 0 - ); - - const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); - const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); - const userADaiBalance = await aDai.balanceOf(userAddress); - const adapterADaiBalance = await aDai.balanceOf(uniswapRepayAdapter.address); - const userDaiBalance = await dai.balanceOf(userAddress); - - expect(adapterADaiBalance).to.be.eq(Zero); - expect(adapterDaiBalance).to.be.eq(Zero); - expect(userDaiStableDebtAmountBefore).to.be.gte(debtAmount); - expect(userDaiStableDebtAmount).to.be.lt(debtAmount); - expect(userADaiBalance).to.be.lt(userADaiBalanceBefore); - expect(userADaiBalance).to.be.gte(userADaiBalanceBefore.sub(flashLoanDebt)); - expect(userDaiBalance).to.be.eq(userDaiBalanceBefore); - }); - }); - - describe('swapAndRepay', () => { - it('should correctly swap tokens and repay debt', async () => { - const { - users, - pool, - weth, - aWETH, - oracle, - dai, - uniswapRepayAdapter, - helpersContract, - } = testEnv; - const user = users[0].signer; - const userAddress = users[0].address; - - const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); - - const daiPrice = await oracle.getAssetPrice(dai.address); - const expectedDaiAmount = await convertToCurrencyDecimals( - dai.address, - new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) - ); - - // Open user Debt - await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); - - const daiStableDebtTokenAddress = ( - await helpersContract.getReserveTokensAddresses(dai.address) - ).stableDebtTokenAddress; - - const daiStableDebtContract = await getContract( - eContractid.StableDebtToken, - daiStableDebtTokenAddress - ); - - const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); - - const liquidityToSwap = amountWETHtoSwap; - await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); - const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); - - await mockUniswapRouter.setAmountToSwap(weth.address, liquidityToSwap); - - await mockUniswapRouter.setDefaultMockValue(liquidityToSwap); - - await uniswapRepayAdapter - .connect(user) - .swapAndRepay(weth.address, dai.address, liquidityToSwap, expectedDaiAmount, 1, { - amount: 0, - deadline: 0, - v: 0, - r: '0x0000000000000000000000000000000000000000000000000000000000000000', - s: '0x0000000000000000000000000000000000000000000000000000000000000000', - }); - - const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); - const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); - const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); - const userAEthBalance = await aWETH.balanceOf(userAddress); - - expect(adapterWethBalance).to.be.eq(Zero); - expect(adapterDaiBalance).to.be.eq(Zero); - expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount); - expect(userDaiStableDebtAmount).to.be.lt(expectedDaiAmount); - expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); - expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); - }); - - it('should correctly swap tokens and repay debt with permit', async () => { - const { - users, - pool, - weth, - aWETH, - oracle, - dai, - uniswapRepayAdapter, - helpersContract, - } = testEnv; - const user = users[0].signer; - const userAddress = users[0].address; - - const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); - - const daiPrice = await oracle.getAssetPrice(dai.address); - const expectedDaiAmount = await convertToCurrencyDecimals( - dai.address, - new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) - ); - - // Open user Debt - await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); - - const daiStableDebtTokenAddress = ( - await helpersContract.getReserveTokensAddresses(dai.address) - ).stableDebtTokenAddress; - - const daiStableDebtContract = await getContract( - eContractid.StableDebtToken, - daiStableDebtTokenAddress - ); - - const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); - - const liquidityToSwap = amountWETHtoSwap; - const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); - - await mockUniswapRouter.setAmountToSwap(weth.address, liquidityToSwap); - - await mockUniswapRouter.setDefaultMockValue(liquidityToSwap); - - const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID; - const deadline = MAX_UINT_AMOUNT; - const nonce = (await aWETH._nonces(userAddress)).toNumber(); - const msgParams = buildPermitParams( - chainId, - aWETH.address, - '1', - await aWETH.name(), - userAddress, - uniswapRepayAdapter.address, - nonce, - deadline, - liquidityToSwap.toString() - ); - - const ownerPrivateKey = require('../test-wallets.js').accounts[1].secretKey; - if (!ownerPrivateKey) { - throw new Error('INVALID_OWNER_PK'); - } - - const { v, r, s } = getSignatureFromTypedData(ownerPrivateKey, msgParams); - - await uniswapRepayAdapter - .connect(user) - .swapAndRepay(weth.address, dai.address, liquidityToSwap, expectedDaiAmount, 1, { - amount: liquidityToSwap, - deadline, - v, - r, - s, - }); - - const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); - const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); - const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); - const userAEthBalance = await aWETH.balanceOf(userAddress); - - expect(adapterWethBalance).to.be.eq(Zero); - expect(adapterDaiBalance).to.be.eq(Zero); - expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount); - expect(userDaiStableDebtAmount).to.be.lt(expectedDaiAmount); - expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); - expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); - }); - - it('should revert if there is not debt to repay', async () => { - const { users, weth, aWETH, oracle, dai, uniswapRepayAdapter } = testEnv; - const user = users[0].signer; - - const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); - - const daiPrice = await oracle.getAssetPrice(dai.address); - const expectedDaiAmount = await convertToCurrencyDecimals( - dai.address, - new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) - ); - - const liquidityToSwap = amountWETHtoSwap; - await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); - - await mockUniswapRouter.setAmountToSwap(weth.address, liquidityToSwap); - - await mockUniswapRouter.setDefaultMockValue(liquidityToSwap); - - await expect( - uniswapRepayAdapter - .connect(user) - .swapAndRepay(weth.address, dai.address, liquidityToSwap, expectedDaiAmount, 1, { - amount: 0, - deadline: 0, - v: 0, - r: '0x0000000000000000000000000000000000000000000000000000000000000000', - s: '0x0000000000000000000000000000000000000000000000000000000000000000', - }) - ).to.be.reverted; - }); - - it('should revert when max amount allowed to swap is bigger than max slippage', async () => { - const { users, pool, weth, aWETH, oracle, dai, uniswapRepayAdapter } = testEnv; - const user = users[0].signer; - const userAddress = users[0].address; - - const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); - - const daiPrice = await oracle.getAssetPrice(dai.address); - const expectedDaiAmount = await convertToCurrencyDecimals( - dai.address, - new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) - ); - - // Open user Debt - await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); - - const bigMaxAmountToSwap = amountWETHtoSwap.mul(2); - await aWETH.connect(user).approve(uniswapRepayAdapter.address, bigMaxAmountToSwap); - - await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, bigMaxAmountToSwap); - - await mockUniswapRouter.setDefaultMockValue(bigMaxAmountToSwap); - - await expect( - uniswapRepayAdapter - .connect(user) - .swapAndRepay(weth.address, dai.address, bigMaxAmountToSwap, expectedDaiAmount, 1, { - amount: 0, - deadline: 0, - v: 0, - r: '0x0000000000000000000000000000000000000000000000000000000000000000', - s: '0x0000000000000000000000000000000000000000000000000000000000000000', - }) - ).to.be.revertedWith('maxAmountToSwap exceed max slippage'); - }); - - it('should swap, repay debt and pull the needed ATokens leaving no leftovers', async () => { - const { - users, - pool, - weth, - aWETH, - oracle, - dai, - uniswapRepayAdapter, - helpersContract, - } = testEnv; - const user = users[0].signer; - const userAddress = users[0].address; - - const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); - - const daiPrice = await oracle.getAssetPrice(dai.address); - const expectedDaiAmount = await convertToCurrencyDecimals( - dai.address, - new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) - ); - - // Open user Debt - await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); - - const daiStableDebtTokenAddress = ( - await helpersContract.getReserveTokensAddresses(dai.address) - ).stableDebtTokenAddress; - - const daiStableDebtContract = await getContract( - eContractid.StableDebtToken, - daiStableDebtTokenAddress - ); - - const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); - - const liquidityToSwap = amountWETHtoSwap; - await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); - const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); - const userWethBalanceBefore = await weth.balanceOf(userAddress); - - const actualWEthSwapped = new BigNumber(liquidityToSwap.toString()) - .multipliedBy(0.995) - .toFixed(0); - - await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, actualWEthSwapped); - - await mockUniswapRouter.setDefaultMockValue(actualWEthSwapped); - - await uniswapRepayAdapter - .connect(user) - .swapAndRepay(weth.address, dai.address, liquidityToSwap, expectedDaiAmount, 1, { - amount: 0, - deadline: 0, - v: 0, - r: '0x0000000000000000000000000000000000000000000000000000000000000000', - s: '0x0000000000000000000000000000000000000000000000000000000000000000', - }); - - const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); - const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); - const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); - const userAEthBalance = await aWETH.balanceOf(userAddress); - const adapterAEthBalance = await aWETH.balanceOf(uniswapRepayAdapter.address); - const userWethBalance = await weth.balanceOf(userAddress); - - expect(adapterAEthBalance).to.be.eq(Zero); - expect(adapterWethBalance).to.be.eq(Zero); - expect(adapterDaiBalance).to.be.eq(Zero); - expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount); - expect(userDaiStableDebtAmount).to.be.lt(expectedDaiAmount); - expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); - expect(userAEthBalance).to.be.eq(userAEthBalanceBefore.sub(actualWEthSwapped)); - expect(userWethBalance).to.be.eq(userWethBalanceBefore); - }); - - it('should correctly swap tokens and repay the whole stable debt', async () => { - const { - users, - pool, - weth, - aWETH, - oracle, - dai, - uniswapRepayAdapter, - helpersContract, - } = testEnv; - const user = users[0].signer; - const userAddress = users[0].address; - - const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); - - const daiPrice = await oracle.getAssetPrice(dai.address); - const expectedDaiAmount = await convertToCurrencyDecimals( - dai.address, - new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) - ); - - // Open user Debt - await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); - - const daiStableDebtTokenAddress = ( - await helpersContract.getReserveTokensAddresses(dai.address) - ).stableDebtTokenAddress; - - const daiStableDebtContract = await getContract( - eContractid.StableDebtToken, - daiStableDebtTokenAddress - ); - - const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); - - // Add a % to repay on top of the debt - const liquidityToSwap = new BigNumber(amountWETHtoSwap.toString()) - .multipliedBy(1.1) - .toFixed(0); - - await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); - const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); - - // Add a % to repay on top of the debt - const amountToRepay = new BigNumber(expectedDaiAmount.toString()) - .multipliedBy(1.1) - .toFixed(0); - - await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, amountWETHtoSwap); - await mockUniswapRouter.setDefaultMockValue(amountWETHtoSwap); - - await uniswapRepayAdapter - .connect(user) - .swapAndRepay(weth.address, dai.address, liquidityToSwap, amountToRepay, 1, { - amount: 0, - deadline: 0, - v: 0, - r: '0x0000000000000000000000000000000000000000000000000000000000000000', - s: '0x0000000000000000000000000000000000000000000000000000000000000000', - }); - - const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); - const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); - const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); - const userAEthBalance = await aWETH.balanceOf(userAddress); - const adapterAEthBalance = await aWETH.balanceOf(uniswapRepayAdapter.address); - - expect(adapterAEthBalance).to.be.eq(Zero); - expect(adapterWethBalance).to.be.eq(Zero); - expect(adapterDaiBalance).to.be.eq(Zero); - expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount); - expect(userDaiStableDebtAmount).to.be.eq(Zero); - expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); - expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); - }); - - it('should correctly swap tokens and repay the whole variable debt', async () => { - const { - users, - pool, - weth, - aWETH, - oracle, - dai, - uniswapRepayAdapter, - helpersContract, - } = testEnv; - const user = users[0].signer; - const userAddress = users[0].address; - - const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); - - const daiPrice = await oracle.getAssetPrice(dai.address); - const expectedDaiAmount = await convertToCurrencyDecimals( - dai.address, - new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) - ); - - // Open user Debt - await pool.connect(user).borrow(dai.address, expectedDaiAmount, 2, 0, userAddress); - - const daiStableVariableTokenAddress = ( - await helpersContract.getReserveTokensAddresses(dai.address) - ).variableDebtTokenAddress; - - const daiVariableDebtContract = await getContract( - eContractid.VariableDebtToken, - daiStableVariableTokenAddress - ); - - const userDaiVariableDebtAmountBefore = await daiVariableDebtContract.balanceOf( - userAddress - ); - - // Add a % to repay on top of the debt - const liquidityToSwap = new BigNumber(amountWETHtoSwap.toString()) - .multipliedBy(1.1) - .toFixed(0); - - await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); - const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); - - // Add a % to repay on top of the debt - const amountToRepay = new BigNumber(expectedDaiAmount.toString()) - .multipliedBy(1.1) - .toFixed(0); - - await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, amountWETHtoSwap); - await mockUniswapRouter.setDefaultMockValue(amountWETHtoSwap); - - await uniswapRepayAdapter - .connect(user) - .swapAndRepay(weth.address, dai.address, liquidityToSwap, amountToRepay, 2, { - amount: 0, - deadline: 0, - v: 0, - r: '0x0000000000000000000000000000000000000000000000000000000000000000', - s: '0x0000000000000000000000000000000000000000000000000000000000000000', - }); - - const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); - const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); - const userDaiVariableDebtAmount = await daiVariableDebtContract.balanceOf(userAddress); - const userAEthBalance = await aWETH.balanceOf(userAddress); - const adapterAEthBalance = await aWETH.balanceOf(uniswapRepayAdapter.address); - - expect(adapterAEthBalance).to.be.eq(Zero); - expect(adapterWethBalance).to.be.eq(Zero); - expect(adapterDaiBalance).to.be.eq(Zero); - expect(userDaiVariableDebtAmountBefore).to.be.gte(expectedDaiAmount); - expect(userDaiVariableDebtAmount).to.be.eq(Zero); - expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); - expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); - }); - - it('should correctly repay debt using the same asset as collateral', async () => { - const { users, pool, dai, uniswapRepayAdapter, helpersContract, aDai } = testEnv; - const user = users[0].signer; - const userAddress = users[0].address; - - // Add deposit for user - await dai.mint(parseEther('20')); - await dai.approve(pool.address, parseEther('20')); - await pool.deposit(dai.address, parseEther('20'), userAddress, 0); - - const amountCollateralToSwap = parseEther('10'); - - const debtAmount = parseEther('10'); - - // Open user Debt - await pool.connect(user).borrow(dai.address, debtAmount, 1, 0, userAddress); - - const daiStableDebtTokenAddress = ( - await helpersContract.getReserveTokensAddresses(dai.address) - ).stableDebtTokenAddress; - - const daiStableDebtContract = await getContract( - eContractid.StableDebtToken, - daiStableDebtTokenAddress - ); - - const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); - - await aDai.connect(user).approve(uniswapRepayAdapter.address, amountCollateralToSwap); - const userADaiBalanceBefore = await aDai.balanceOf(userAddress); - const userDaiBalanceBefore = await dai.balanceOf(userAddress); - - await uniswapRepayAdapter - .connect(user) - .swapAndRepay( - dai.address, - dai.address, - amountCollateralToSwap, - amountCollateralToSwap, - 1, - { - amount: 0, - deadline: 0, - v: 0, - r: '0x0000000000000000000000000000000000000000000000000000000000000000', - s: '0x0000000000000000000000000000000000000000000000000000000000000000', - } - ); - - const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); - const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); - const userADaiBalance = await aDai.balanceOf(userAddress); - const adapterADaiBalance = await aDai.balanceOf(uniswapRepayAdapter.address); - const userDaiBalance = await dai.balanceOf(userAddress); - - expect(adapterADaiBalance).to.be.eq(Zero); - expect(adapterDaiBalance).to.be.eq(Zero); - expect(userDaiStableDebtAmountBefore).to.be.gte(debtAmount); - expect(userDaiStableDebtAmount).to.be.lt(debtAmount); - expect(userADaiBalance).to.be.lt(userADaiBalanceBefore); - expect(userADaiBalance).to.be.gte(userADaiBalanceBefore.sub(amountCollateralToSwap)); - expect(userDaiBalance).to.be.eq(userDaiBalanceBefore); - }); - }); - }); }); diff --git a/test/uniswapAdapters.repay.spec.ts b/test/uniswapAdapters.repay.spec.ts new file mode 100644 index 00000000..10b12a04 --- /dev/null +++ b/test/uniswapAdapters.repay.spec.ts @@ -0,0 +1,1431 @@ +import { makeSuite, TestEnv } from './helpers/make-suite'; +import { + convertToCurrencyDecimals, + getContract, + buildPermitParams, + getSignatureFromTypedData, + buildLiquiditySwapParams, + buildRepayAdapterParams, +} from '../helpers/contracts-helpers'; +import { getMockUniswapRouter } from '../helpers/contracts-getters'; +import { + deployUniswapLiquiditySwapAdapter, + deployUniswapRepayAdapter, +} from '../helpers/contracts-deployments'; +import { MockUniswapV2Router02 } from '../types/MockUniswapV2Router02'; +import { Zero } from '@ethersproject/constants'; +import BigNumber from 'bignumber.js'; +import { DRE, evmRevert, evmSnapshot } from '../helpers/misc-utils'; +import { ethers } from 'ethers'; +import { eContractid } from '../helpers/types'; +import { StableDebtToken } from '../types/StableDebtToken'; +import { BUIDLEREVM_CHAINID } from '../helpers/buidler-constants'; +import { MAX_UINT_AMOUNT } from '../helpers/constants'; +const { parseEther } = ethers.utils; + +const { expect } = require('chai'); + +makeSuite('Uniswap adapters', (testEnv: TestEnv) => { + let mockUniswapRouter: MockUniswapV2Router02; + let evmSnapshotId: string; + + before(async () => { + mockUniswapRouter = await getMockUniswapRouter(); + }); + + beforeEach(async () => { + evmSnapshotId = await evmSnapshot(); + }); + + afterEach(async () => { + await evmRevert(evmSnapshotId); + }); + + describe('UniswapRepayAdapter', () => { + beforeEach(async () => { + const { users, weth, dai, usdc, aave, pool, deployer } = testEnv; + const userAddress = users[0].address; + + // Provide liquidity + await dai.mint(parseEther('20000')); + await dai.approve(pool.address, parseEther('20000')); + await pool.deposit(dai.address, parseEther('20000'), deployer.address, 0); + + const usdcLiquidity = await convertToCurrencyDecimals(usdc.address, '2000000'); + await usdc.mint(usdcLiquidity); + await usdc.approve(pool.address, usdcLiquidity); + await pool.deposit(usdc.address, usdcLiquidity, deployer.address, 0); + + await weth.mint(parseEther('100')); + await weth.approve(pool.address, parseEther('100')); + await pool.deposit(weth.address, parseEther('100'), deployer.address, 0); + + await aave.mint(parseEther('1000000')); + await aave.approve(pool.address, parseEther('1000000')); + await pool.deposit(aave.address, parseEther('1000000'), deployer.address, 0); + + // Make a deposit for user + await weth.mint(parseEther('1000')); + await weth.approve(pool.address, parseEther('1000')); + await pool.deposit(weth.address, parseEther('1000'), userAddress, 0); + + await aave.mint(parseEther('1000000')); + await aave.approve(pool.address, parseEther('1000000')); + await pool.deposit(aave.address, parseEther('1000000'), userAddress, 0); + + await usdc.mint(usdcLiquidity); + await usdc.approve(pool.address, usdcLiquidity); + await pool.deposit(usdc.address, usdcLiquidity, userAddress, 0); + }); + + describe('constructor', () => { + it('should deploy with correct parameters', async () => { + const { addressesProvider, weth } = testEnv; + await deployUniswapRepayAdapter([ + addressesProvider.address, + mockUniswapRouter.address, + weth.address, + ]); + }); + + it('should revert if not valid addresses provider', async () => { + const { weth } = testEnv; + expect( + deployUniswapRepayAdapter([ + mockUniswapRouter.address, + mockUniswapRouter.address, + weth.address, + ]) + ).to.be.reverted; + }); + }); + + describe('executeOperation', () => { + it('should correctly swap tokens and repay debt', async () => { + const { + users, + pool, + weth, + aWETH, + oracle, + dai, + uniswapRepayAdapter, + helpersContract, + } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); + + const daiStableDebtTokenAddress = ( + await helpersContract.getReserveTokensAddresses(dai.address) + ).stableDebtTokenAddress; + + const daiStableDebtContract = await getContract( + eContractid.StableDebtToken, + daiStableDebtTokenAddress + ); + + const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); + + const liquidityToSwap = amountWETHtoSwap; + await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap); + + const flashLoanDebt = new BigNumber(expectedDaiAmount.toString()) + .multipliedBy(1.0009) + .toFixed(0); + + await mockUniswapRouter.setAmountIn( + flashLoanDebt, + weth.address, + dai.address, + liquidityToSwap + ); + + const params = buildRepayAdapterParams( + weth.address, + liquidityToSwap, + 1, + 0, + 0, + 0, + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000' + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapRepayAdapter.address, + [dai.address], + [expectedDaiAmount.toString()], + [0], + userAddress, + params, + 0 + ) + ) + .to.emit(uniswapRepayAdapter, 'Swapped') + .withArgs(weth.address, dai.address, liquidityToSwap.toString(), flashLoanDebt); + + const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); + const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); + const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); + const userAEthBalance = await aWETH.balanceOf(userAddress); + + expect(adapterWethBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount); + expect(userDaiStableDebtAmount).to.be.lt(expectedDaiAmount); + expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); + expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); + }); + + it('should correctly swap tokens and repay debt with permit', async () => { + const { + users, + pool, + weth, + aWETH, + oracle, + dai, + uniswapRepayAdapter, + helpersContract, + } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); + + const daiStableDebtTokenAddress = ( + await helpersContract.getReserveTokensAddresses(dai.address) + ).stableDebtTokenAddress; + + const daiStableDebtContract = await getContract( + eContractid.StableDebtToken, + daiStableDebtTokenAddress + ); + + const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); + + const liquidityToSwap = amountWETHtoSwap; + const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + + const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID; + const deadline = MAX_UINT_AMOUNT; + const nonce = (await aWETH._nonces(userAddress)).toNumber(); + const msgParams = buildPermitParams( + chainId, + aWETH.address, + '1', + await aWETH.name(), + userAddress, + uniswapRepayAdapter.address, + nonce, + deadline, + liquidityToSwap.toString() + ); + + const ownerPrivateKey = require('../test-wallets.js').accounts[1].secretKey; + if (!ownerPrivateKey) { + throw new Error('INVALID_OWNER_PK'); + } + + const { v, r, s } = getSignatureFromTypedData(ownerPrivateKey, msgParams); + + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap); + + const flashLoanDebt = new BigNumber(expectedDaiAmount.toString()) + .multipliedBy(1.0009) + .toFixed(0); + + await mockUniswapRouter.setAmountIn( + flashLoanDebt, + weth.address, + dai.address, + liquidityToSwap + ); + + const params = buildRepayAdapterParams( + weth.address, + liquidityToSwap, + 1, + liquidityToSwap, + deadline, + v, + r, + s + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapRepayAdapter.address, + [dai.address], + [expectedDaiAmount.toString()], + [0], + userAddress, + params, + 0 + ) + ) + .to.emit(uniswapRepayAdapter, 'Swapped') + .withArgs(weth.address, dai.address, liquidityToSwap.toString(), flashLoanDebt); + + const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); + const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); + const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); + const userAEthBalance = await aWETH.balanceOf(userAddress); + + expect(adapterWethBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount); + expect(userDaiStableDebtAmount).to.be.lt(expectedDaiAmount); + expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); + expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); + }); + + it('should revert if caller not lending pool', async () => { + const { users, pool, weth, aWETH, oracle, dai, uniswapRepayAdapter } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); + + const liquidityToSwap = amountWETHtoSwap; + await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap); + + const params = buildRepayAdapterParams( + weth.address, + liquidityToSwap, + 1, + 0, + 0, + 0, + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000' + ); + + await expect( + uniswapRepayAdapter + .connect(user) + .executeOperation( + [dai.address], + [expectedDaiAmount.toString()], + [0], + userAddress, + params + ) + ).to.be.revertedWith('CALLER_MUST_BE_LENDING_POOL'); + }); + + it('should revert if there is not debt to repay with the specified rate mode', async () => { + const { users, pool, weth, oracle, dai, uniswapRepayAdapter, aWETH } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + await weth.connect(user).mint(amountWETHtoSwap); + await weth.connect(user).transfer(uniswapRepayAdapter.address, amountWETHtoSwap); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 2, 0, userAddress); + + const liquidityToSwap = amountWETHtoSwap; + await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap); + + const params = buildRepayAdapterParams( + weth.address, + liquidityToSwap, + 1, + 0, + 0, + 0, + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000' + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapRepayAdapter.address, + [dai.address], + [expectedDaiAmount.toString()], + [0], + userAddress, + params, + 0 + ) + ).to.be.reverted; + }); + + it('should revert if there is not debt to repay', async () => { + const { users, pool, weth, oracle, dai, uniswapRepayAdapter, aWETH } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + await weth.connect(user).mint(amountWETHtoSwap); + await weth.connect(user).transfer(uniswapRepayAdapter.address, amountWETHtoSwap); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + const liquidityToSwap = amountWETHtoSwap; + await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, liquidityToSwap); + + const params = buildRepayAdapterParams( + weth.address, + liquidityToSwap, + 1, + 0, + 0, + 0, + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000' + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapRepayAdapter.address, + [dai.address], + [expectedDaiAmount.toString()], + [0], + userAddress, + params, + 0 + ) + ).to.be.reverted; + }); + + it('should revert when max amount allowed to swap is bigger than max slippage', async () => { + const { users, pool, weth, oracle, dai, aWETH, uniswapRepayAdapter } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); + + const bigMaxAmountToSwap = amountWETHtoSwap.mul(2); + await aWETH.connect(user).approve(uniswapRepayAdapter.address, bigMaxAmountToSwap); + + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, bigMaxAmountToSwap); + + const flashLoanDebt = new BigNumber(expectedDaiAmount.toString()) + .multipliedBy(1.0009) + .toFixed(0); + + await mockUniswapRouter.setAmountIn( + flashLoanDebt, + weth.address, + dai.address, + bigMaxAmountToSwap + ); + + const params = buildRepayAdapterParams( + weth.address, + bigMaxAmountToSwap, + 1, + 0, + 0, + 0, + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000' + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapRepayAdapter.address, + [dai.address], + [expectedDaiAmount.toString()], + [0], + userAddress, + params, + 0 + ) + ).to.be.revertedWith('maxAmountToSwap exceed max slippage'); + }); + + it('should swap, repay debt and pull the needed ATokens leaving no leftovers', async () => { + const { + users, + pool, + weth, + aWETH, + oracle, + dai, + uniswapRepayAdapter, + helpersContract, + } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); + + const daiStableDebtTokenAddress = ( + await helpersContract.getReserveTokensAddresses(dai.address) + ).stableDebtTokenAddress; + + const daiStableDebtContract = await getContract( + eContractid.StableDebtToken, + daiStableDebtTokenAddress + ); + + const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); + + const liquidityToSwap = amountWETHtoSwap; + await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + const userWethBalanceBefore = await weth.balanceOf(userAddress); + + const actualWEthSwapped = new BigNumber(liquidityToSwap.toString()) + .multipliedBy(0.995) + .toFixed(0); + + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, actualWEthSwapped); + + const flashLoanDebt = new BigNumber(expectedDaiAmount.toString()) + .multipliedBy(1.0009) + .toFixed(0); + + await mockUniswapRouter.setAmountIn( + flashLoanDebt, + weth.address, + dai.address, + actualWEthSwapped + ); + + const params = buildRepayAdapterParams( + weth.address, + liquidityToSwap, + 1, + 0, + 0, + 0, + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000' + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapRepayAdapter.address, + [dai.address], + [expectedDaiAmount.toString()], + [0], + userAddress, + params, + 0 + ) + ) + .to.emit(uniswapRepayAdapter, 'Swapped') + .withArgs(weth.address, dai.address, actualWEthSwapped.toString(), flashLoanDebt); + + const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); + const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); + const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); + const userAEthBalance = await aWETH.balanceOf(userAddress); + const adapterAEthBalance = await aWETH.balanceOf(uniswapRepayAdapter.address); + const userWethBalance = await weth.balanceOf(userAddress); + + expect(adapterAEthBalance).to.be.eq(Zero); + expect(adapterWethBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount); + expect(userDaiStableDebtAmount).to.be.lt(expectedDaiAmount); + expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); + expect(userAEthBalance).to.be.eq(userAEthBalanceBefore.sub(actualWEthSwapped)); + expect(userWethBalance).to.be.eq(userWethBalanceBefore); + }); + + it('should correctly swap tokens and repay the whole stable debt', async () => { + const { + users, + pool, + weth, + aWETH, + oracle, + dai, + uniswapRepayAdapter, + helpersContract, + } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); + + const daiStableDebtTokenAddress = ( + await helpersContract.getReserveTokensAddresses(dai.address) + ).stableDebtTokenAddress; + + const daiStableDebtContract = await getContract( + eContractid.StableDebtToken, + daiStableDebtTokenAddress + ); + + const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); + + // Add a % to repay on top of the debt + const liquidityToSwap = new BigNumber(amountWETHtoSwap.toString()) + .multipliedBy(1.1) + .toFixed(0); + + await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + + // Add a % to repay on top of the debt + const amountToRepay = new BigNumber(expectedDaiAmount.toString()) + .multipliedBy(1.1) + .toFixed(0); + + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, amountWETHtoSwap); + await mockUniswapRouter.setDefaultMockValue(amountWETHtoSwap); + + const params = buildRepayAdapterParams( + weth.address, + liquidityToSwap, + 1, + 0, + 0, + 0, + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000' + ); + + await pool + .connect(user) + .flashLoan( + uniswapRepayAdapter.address, + [dai.address], + [amountToRepay.toString()], + [0], + userAddress, + params, + 0 + ); + + const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); + const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); + const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); + const userAEthBalance = await aWETH.balanceOf(userAddress); + const adapterAEthBalance = await aWETH.balanceOf(uniswapRepayAdapter.address); + + expect(adapterAEthBalance).to.be.eq(Zero); + expect(adapterWethBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount); + expect(userDaiStableDebtAmount).to.be.eq(Zero); + expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); + expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); + }); + + it('should correctly swap tokens and repay the whole variable debt', async () => { + const { + users, + pool, + weth, + aWETH, + oracle, + dai, + uniswapRepayAdapter, + helpersContract, + } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 2, 0, userAddress); + + const daiStableVariableTokenAddress = ( + await helpersContract.getReserveTokensAddresses(dai.address) + ).variableDebtTokenAddress; + + const daiVariableDebtContract = await getContract( + eContractid.VariableDebtToken, + daiStableVariableTokenAddress + ); + + const userDaiVariableDebtAmountBefore = await daiVariableDebtContract.balanceOf( + userAddress + ); + + // Add a % to repay on top of the debt + const liquidityToSwap = new BigNumber(amountWETHtoSwap.toString()) + .multipliedBy(1.1) + .toFixed(0); + + await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + + // Add a % to repay on top of the debt + const amountToRepay = new BigNumber(expectedDaiAmount.toString()) + .multipliedBy(1.1) + .toFixed(0); + + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, amountWETHtoSwap); + await mockUniswapRouter.setDefaultMockValue(amountWETHtoSwap); + + const params = buildRepayAdapterParams( + weth.address, + liquidityToSwap, + 2, + 0, + 0, + 0, + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000' + ); + + await pool + .connect(user) + .flashLoan( + uniswapRepayAdapter.address, + [dai.address], + [amountToRepay.toString()], + [0], + userAddress, + params, + 0 + ); + + const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); + const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); + const userDaiVariableDebtAmount = await daiVariableDebtContract.balanceOf(userAddress); + const userAEthBalance = await aWETH.balanceOf(userAddress); + const adapterAEthBalance = await aWETH.balanceOf(uniswapRepayAdapter.address); + + expect(adapterAEthBalance).to.be.eq(Zero); + expect(adapterWethBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(userDaiVariableDebtAmountBefore).to.be.gte(expectedDaiAmount); + expect(userDaiVariableDebtAmount).to.be.eq(Zero); + expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); + expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); + }); + + it('should correctly repay debt using the same asset as collateral', async () => { + const { users, pool, aDai, dai, uniswapRepayAdapter, helpersContract } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + // Add deposit for user + await dai.mint(parseEther('20')); + await dai.approve(pool.address, parseEther('20')); + await pool.deposit(dai.address, parseEther('20'), userAddress, 0); + + const amountCollateralToSwap = parseEther('10'); + const debtAmount = parseEther('10'); + + // Open user Debt + await pool.connect(user).borrow(dai.address, debtAmount, 1, 0, userAddress); + + const daiStableDebtTokenAddress = ( + await helpersContract.getReserveTokensAddresses(dai.address) + ).stableDebtTokenAddress; + + const daiStableDebtContract = await getContract( + eContractid.StableDebtToken, + daiStableDebtTokenAddress + ); + + const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); + + const flashLoanDebt = new BigNumber(amountCollateralToSwap.toString()) + .multipliedBy(1.0009) + .toFixed(0); + + await aDai.connect(user).approve(uniswapRepayAdapter.address, flashLoanDebt); + const userADaiBalanceBefore = await aDai.balanceOf(userAddress); + const userDaiBalanceBefore = await dai.balanceOf(userAddress); + + const params = buildRepayAdapterParams( + dai.address, + amountCollateralToSwap, + 1, + 0, + 0, + 0, + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000' + ); + + await pool + .connect(user) + .flashLoan( + uniswapRepayAdapter.address, + [dai.address], + [amountCollateralToSwap.toString()], + [0], + userAddress, + params, + 0 + ); + + const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); + const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); + const userADaiBalance = await aDai.balanceOf(userAddress); + const adapterADaiBalance = await aDai.balanceOf(uniswapRepayAdapter.address); + const userDaiBalance = await dai.balanceOf(userAddress); + + expect(adapterADaiBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(userDaiStableDebtAmountBefore).to.be.gte(debtAmount); + expect(userDaiStableDebtAmount).to.be.lt(debtAmount); + expect(userADaiBalance).to.be.lt(userADaiBalanceBefore); + expect(userADaiBalance).to.be.gte(userADaiBalanceBefore.sub(flashLoanDebt)); + expect(userDaiBalance).to.be.eq(userDaiBalanceBefore); + }); + }); + + describe('swapAndRepay', () => { + it('should correctly swap tokens and repay debt', async () => { + const { + users, + pool, + weth, + aWETH, + oracle, + dai, + uniswapRepayAdapter, + helpersContract, + } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); + + const daiStableDebtTokenAddress = ( + await helpersContract.getReserveTokensAddresses(dai.address) + ).stableDebtTokenAddress; + + const daiStableDebtContract = await getContract( + eContractid.StableDebtToken, + daiStableDebtTokenAddress + ); + + const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); + + const liquidityToSwap = amountWETHtoSwap; + await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + + await mockUniswapRouter.setAmountToSwap(weth.address, liquidityToSwap); + + await mockUniswapRouter.setDefaultMockValue(liquidityToSwap); + + await uniswapRepayAdapter.connect(user).swapAndRepay( + weth.address, + dai.address, + liquidityToSwap, + expectedDaiAmount, + 1, + { + amount: 0, + deadline: 0, + v: 0, + r: '0x0000000000000000000000000000000000000000000000000000000000000000', + s: '0x0000000000000000000000000000000000000000000000000000000000000000', + }, + false + ); + + const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); + const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); + const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); + const userAEthBalance = await aWETH.balanceOf(userAddress); + + expect(adapterWethBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount); + expect(userDaiStableDebtAmount).to.be.lt(expectedDaiAmount); + expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); + expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); + }); + + it('should correctly swap tokens and repay debt with permit', async () => { + const { + users, + pool, + weth, + aWETH, + oracle, + dai, + uniswapRepayAdapter, + helpersContract, + } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); + + const daiStableDebtTokenAddress = ( + await helpersContract.getReserveTokensAddresses(dai.address) + ).stableDebtTokenAddress; + + const daiStableDebtContract = await getContract( + eContractid.StableDebtToken, + daiStableDebtTokenAddress + ); + + const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); + + const liquidityToSwap = amountWETHtoSwap; + const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + + await mockUniswapRouter.setAmountToSwap(weth.address, liquidityToSwap); + + await mockUniswapRouter.setDefaultMockValue(liquidityToSwap); + + const chainId = DRE.network.config.chainId || BUIDLEREVM_CHAINID; + const deadline = MAX_UINT_AMOUNT; + const nonce = (await aWETH._nonces(userAddress)).toNumber(); + const msgParams = buildPermitParams( + chainId, + aWETH.address, + '1', + await aWETH.name(), + userAddress, + uniswapRepayAdapter.address, + nonce, + deadline, + liquidityToSwap.toString() + ); + + const ownerPrivateKey = require('../test-wallets.js').accounts[1].secretKey; + if (!ownerPrivateKey) { + throw new Error('INVALID_OWNER_PK'); + } + + const { v, r, s } = getSignatureFromTypedData(ownerPrivateKey, msgParams); + + await uniswapRepayAdapter.connect(user).swapAndRepay( + weth.address, + dai.address, + liquidityToSwap, + expectedDaiAmount, + 1, + { + amount: liquidityToSwap, + deadline, + v, + r, + s, + }, + false + ); + + const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); + const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); + const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); + const userAEthBalance = await aWETH.balanceOf(userAddress); + + expect(adapterWethBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount); + expect(userDaiStableDebtAmount).to.be.lt(expectedDaiAmount); + expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); + expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); + }); + + it('should revert if there is not debt to repay', async () => { + const { users, weth, aWETH, oracle, dai, uniswapRepayAdapter } = testEnv; + const user = users[0].signer; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + const liquidityToSwap = amountWETHtoSwap; + await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + + await mockUniswapRouter.setAmountToSwap(weth.address, liquidityToSwap); + + await mockUniswapRouter.setDefaultMockValue(liquidityToSwap); + + await expect( + uniswapRepayAdapter.connect(user).swapAndRepay( + weth.address, + dai.address, + liquidityToSwap, + expectedDaiAmount, + 1, + { + amount: 0, + deadline: 0, + v: 0, + r: '0x0000000000000000000000000000000000000000000000000000000000000000', + s: '0x0000000000000000000000000000000000000000000000000000000000000000', + }, + false + ) + ).to.be.reverted; + }); + + it('should revert when max amount allowed to swap is bigger than max slippage', async () => { + const { users, pool, weth, aWETH, oracle, dai, uniswapRepayAdapter } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); + + const bigMaxAmountToSwap = amountWETHtoSwap.mul(2); + await aWETH.connect(user).approve(uniswapRepayAdapter.address, bigMaxAmountToSwap); + + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, bigMaxAmountToSwap); + + await mockUniswapRouter.setDefaultMockValue(bigMaxAmountToSwap); + + await expect( + uniswapRepayAdapter.connect(user).swapAndRepay( + weth.address, + dai.address, + bigMaxAmountToSwap, + expectedDaiAmount, + 1, + { + amount: 0, + deadline: 0, + v: 0, + r: '0x0000000000000000000000000000000000000000000000000000000000000000', + s: '0x0000000000000000000000000000000000000000000000000000000000000000', + }, + false + ) + ).to.be.revertedWith('maxAmountToSwap exceed max slippage'); + }); + + it('should swap, repay debt and pull the needed ATokens leaving no leftovers', async () => { + const { + users, + pool, + weth, + aWETH, + oracle, + dai, + uniswapRepayAdapter, + helpersContract, + } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); + + const daiStableDebtTokenAddress = ( + await helpersContract.getReserveTokensAddresses(dai.address) + ).stableDebtTokenAddress; + + const daiStableDebtContract = await getContract( + eContractid.StableDebtToken, + daiStableDebtTokenAddress + ); + + const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); + + const liquidityToSwap = amountWETHtoSwap; + await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + const userWethBalanceBefore = await weth.balanceOf(userAddress); + + const actualWEthSwapped = new BigNumber(liquidityToSwap.toString()) + .multipliedBy(0.995) + .toFixed(0); + + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, actualWEthSwapped); + + await mockUniswapRouter.setDefaultMockValue(actualWEthSwapped); + + await uniswapRepayAdapter.connect(user).swapAndRepay( + weth.address, + dai.address, + liquidityToSwap, + expectedDaiAmount, + 1, + { + amount: 0, + deadline: 0, + v: 0, + r: '0x0000000000000000000000000000000000000000000000000000000000000000', + s: '0x0000000000000000000000000000000000000000000000000000000000000000', + }, + false + ); + + const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); + const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); + const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); + const userAEthBalance = await aWETH.balanceOf(userAddress); + const adapterAEthBalance = await aWETH.balanceOf(uniswapRepayAdapter.address); + const userWethBalance = await weth.balanceOf(userAddress); + + expect(adapterAEthBalance).to.be.eq(Zero); + expect(adapterWethBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount); + expect(userDaiStableDebtAmount).to.be.lt(expectedDaiAmount); + expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); + expect(userAEthBalance).to.be.eq(userAEthBalanceBefore.sub(actualWEthSwapped)); + expect(userWethBalance).to.be.eq(userWethBalanceBefore); + }); + + it('should correctly swap tokens and repay the whole stable debt', async () => { + const { + users, + pool, + weth, + aWETH, + oracle, + dai, + uniswapRepayAdapter, + helpersContract, + } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress); + + const daiStableDebtTokenAddress = ( + await helpersContract.getReserveTokensAddresses(dai.address) + ).stableDebtTokenAddress; + + const daiStableDebtContract = await getContract( + eContractid.StableDebtToken, + daiStableDebtTokenAddress + ); + + const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); + + // Add a % to repay on top of the debt + const liquidityToSwap = new BigNumber(amountWETHtoSwap.toString()) + .multipliedBy(1.1) + .toFixed(0); + + await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + + // Add a % to repay on top of the debt + const amountToRepay = new BigNumber(expectedDaiAmount.toString()) + .multipliedBy(1.1) + .toFixed(0); + + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, amountWETHtoSwap); + await mockUniswapRouter.setDefaultMockValue(amountWETHtoSwap); + + await uniswapRepayAdapter.connect(user).swapAndRepay( + weth.address, + dai.address, + liquidityToSwap, + amountToRepay, + 1, + { + amount: 0, + deadline: 0, + v: 0, + r: '0x0000000000000000000000000000000000000000000000000000000000000000', + s: '0x0000000000000000000000000000000000000000000000000000000000000000', + }, + false + ); + + const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); + const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); + const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); + const userAEthBalance = await aWETH.balanceOf(userAddress); + const adapterAEthBalance = await aWETH.balanceOf(uniswapRepayAdapter.address); + + expect(adapterAEthBalance).to.be.eq(Zero); + expect(adapterWethBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(userDaiStableDebtAmountBefore).to.be.gte(expectedDaiAmount); + expect(userDaiStableDebtAmount).to.be.eq(Zero); + expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); + expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); + }); + + it('should correctly swap tokens and repay the whole variable debt', async () => { + const { + users, + pool, + weth, + aWETH, + oracle, + dai, + uniswapRepayAdapter, + helpersContract, + } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10'); + + const daiPrice = await oracle.getAssetPrice(dai.address); + const expectedDaiAmount = await convertToCurrencyDecimals( + dai.address, + new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0) + ); + + // Open user Debt + await pool.connect(user).borrow(dai.address, expectedDaiAmount, 2, 0, userAddress); + + const daiStableVariableTokenAddress = ( + await helpersContract.getReserveTokensAddresses(dai.address) + ).variableDebtTokenAddress; + + const daiVariableDebtContract = await getContract( + eContractid.VariableDebtToken, + daiStableVariableTokenAddress + ); + + const userDaiVariableDebtAmountBefore = await daiVariableDebtContract.balanceOf( + userAddress + ); + + // Add a % to repay on top of the debt + const liquidityToSwap = new BigNumber(amountWETHtoSwap.toString()) + .multipliedBy(1.1) + .toFixed(0); + + await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap); + const userAEthBalanceBefore = await aWETH.balanceOf(userAddress); + + // Add a % to repay on top of the debt + const amountToRepay = new BigNumber(expectedDaiAmount.toString()) + .multipliedBy(1.1) + .toFixed(0); + + await mockUniswapRouter.connect(user).setAmountToSwap(weth.address, amountWETHtoSwap); + await mockUniswapRouter.setDefaultMockValue(amountWETHtoSwap); + + await uniswapRepayAdapter.connect(user).swapAndRepay( + weth.address, + dai.address, + liquidityToSwap, + amountToRepay, + 2, + { + amount: 0, + deadline: 0, + v: 0, + r: '0x0000000000000000000000000000000000000000000000000000000000000000', + s: '0x0000000000000000000000000000000000000000000000000000000000000000', + }, + false + ); + + const adapterWethBalance = await weth.balanceOf(uniswapRepayAdapter.address); + const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); + const userDaiVariableDebtAmount = await daiVariableDebtContract.balanceOf(userAddress); + const userAEthBalance = await aWETH.balanceOf(userAddress); + const adapterAEthBalance = await aWETH.balanceOf(uniswapRepayAdapter.address); + + expect(adapterAEthBalance).to.be.eq(Zero); + expect(adapterWethBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(userDaiVariableDebtAmountBefore).to.be.gte(expectedDaiAmount); + expect(userDaiVariableDebtAmount).to.be.eq(Zero); + expect(userAEthBalance).to.be.lt(userAEthBalanceBefore); + expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap)); + }); + + it('should correctly repay debt using the same asset as collateral', async () => { + const { users, pool, dai, uniswapRepayAdapter, helpersContract, aDai } = testEnv; + const user = users[0].signer; + const userAddress = users[0].address; + + // Add deposit for user + await dai.mint(parseEther('20')); + await dai.approve(pool.address, parseEther('20')); + await pool.deposit(dai.address, parseEther('20'), userAddress, 0); + + const amountCollateralToSwap = parseEther('10'); + + const debtAmount = parseEther('10'); + + // Open user Debt + await pool.connect(user).borrow(dai.address, debtAmount, 1, 0, userAddress); + + const daiStableDebtTokenAddress = ( + await helpersContract.getReserveTokensAddresses(dai.address) + ).stableDebtTokenAddress; + + const daiStableDebtContract = await getContract( + eContractid.StableDebtToken, + daiStableDebtTokenAddress + ); + + const userDaiStableDebtAmountBefore = await daiStableDebtContract.balanceOf(userAddress); + + await aDai.connect(user).approve(uniswapRepayAdapter.address, amountCollateralToSwap); + const userADaiBalanceBefore = await aDai.balanceOf(userAddress); + const userDaiBalanceBefore = await dai.balanceOf(userAddress); + + await uniswapRepayAdapter.connect(user).swapAndRepay( + dai.address, + dai.address, + amountCollateralToSwap, + amountCollateralToSwap, + 1, + { + amount: 0, + deadline: 0, + v: 0, + r: '0x0000000000000000000000000000000000000000000000000000000000000000', + s: '0x0000000000000000000000000000000000000000000000000000000000000000', + }, + false + ); + + const adapterDaiBalance = await dai.balanceOf(uniswapRepayAdapter.address); + const userDaiStableDebtAmount = await daiStableDebtContract.balanceOf(userAddress); + const userADaiBalance = await aDai.balanceOf(userAddress); + const adapterADaiBalance = await aDai.balanceOf(uniswapRepayAdapter.address); + const userDaiBalance = await dai.balanceOf(userAddress); + + expect(adapterADaiBalance).to.be.eq(Zero); + expect(adapterDaiBalance).to.be.eq(Zero); + expect(userDaiStableDebtAmountBefore).to.be.gte(debtAmount); + expect(userDaiStableDebtAmount).to.be.lt(debtAmount); + expect(userADaiBalance).to.be.lt(userADaiBalanceBefore); + expect(userADaiBalance).to.be.gte(userADaiBalanceBefore.sub(amountCollateralToSwap)); + expect(userDaiBalance).to.be.eq(userDaiBalanceBefore); + }); + }); + }); +}); From 16a7398574f467145026bec5cd38957c323473bc Mon Sep 17 00:00:00 2001 From: David Racero Date: Thu, 14 Jan 2021 11:53:48 +0100 Subject: [PATCH 62/62] Fixed tests. Add uniswap adapter test to package.json script --- helpers/contracts-helpers.ts | 34 +++++---- package.json | 1 + test/uniswapAdapters.liquiditySwap.spec.ts | 81 +++++++++++++++++----- test/uniswapAdapters.repay.spec.ts | 34 +++++---- 4 files changed, 104 insertions(+), 46 deletions(-) diff --git a/helpers/contracts-helpers.ts b/helpers/contracts-helpers.ts index 832e8ae7..e18de87d 100644 --- a/helpers/contracts-helpers.ts +++ b/helpers/contracts-helpers.ts @@ -1,4 +1,4 @@ -import { Contract, Signer, utils, ethers , BigNumberish} from 'ethers'; +import { Contract, Signer, utils, ethers, BigNumberish } from 'ethers'; import { signTypedData_v4 } from 'eth-sig-util'; import { fromRpcSig, ECDSASignature } from 'ethereumjs-util'; import BigNumber from 'bignumber.js'; @@ -241,7 +241,8 @@ export const buildLiquiditySwapParams = ( deadlines: BigNumberish[], v: BigNumberish[], r: (string | Buffer)[], - s: (string | Buffer)[] + s: (string | Buffer)[], + useEthPath: boolean[] ) => { return ethers.utils.defaultAbiCoder.encode( [ @@ -253,8 +254,19 @@ export const buildLiquiditySwapParams = ( 'uint8[]', 'bytes32[]', 'bytes32[]', + 'bool[]', ], - [assetToSwapToList, minAmountsToReceive, swapAllBalances, permitAmounts, deadlines, v, r, s] + [ + assetToSwapToList, + minAmountsToReceive, + swapAllBalances, + permitAmounts, + deadlines, + v, + r, + s, + useEthPath, + ] ); }; @@ -266,19 +278,11 @@ export const buildRepayAdapterParams = ( deadline: BigNumberish, v: BigNumberish, r: string | Buffer, - s: string | Buffer + s: string | Buffer, + useEthPath: boolean ) => { return ethers.utils.defaultAbiCoder.encode( - [ - 'address', - 'uint256', - 'uint256', - 'uint256', - 'uint256', - 'uint8', - 'bytes32', - 'bytes32', - ], - [collateralAsset, collateralAmount, rateMode, permitAmount, deadline, v, r, s] + ['address', 'uint256', 'uint256', 'uint256', 'uint256', 'uint8', 'bytes32', 'bytes32', 'bool'], + [collateralAsset, collateralAmount, rateMode, permitAmount, deadline, v, r, s, useEthPath] ); }; diff --git a/package.json b/package.json index 49644831..4fefea1b 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "test-stable-and-atokens": "hardhat test test/__setup.spec.ts test/atoken-transfer.spec.ts test/stable-token.spec.ts", "test-subgraph:scenarios": "hardhat --network hardhatevm_docker test test/__setup.spec.ts test/subgraph-scenarios.spec.ts", "test-weth": "hardhat test test/__setup.spec.ts test/weth-gateway.spec.ts", + "test-uniswap": "hardhat test test/__setup.spec.ts test/uniswapAdapters*.spec.ts", "test:main:check-list": "MAINNET_FORK=true TS_NODE_TRANSPILE_ONLY=1 hardhat test test/__setup.spec.ts test/mainnet/check-list.spec.ts", "dev:coverage": "buidler compile --force && buidler coverage --network coverage", "aave:evm:dev:migration": "npm run compile && hardhat aave:dev", diff --git a/test/uniswapAdapters.liquiditySwap.spec.ts b/test/uniswapAdapters.liquiditySwap.spec.ts index 65882c0e..1e30b2b3 100644 --- a/test/uniswapAdapters.liquiditySwap.spec.ts +++ b/test/uniswapAdapters.liquiditySwap.spec.ts @@ -121,7 +121,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { [0], [0], ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'] + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + [false] ); await expect( @@ -236,7 +237,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { [ '0x0000000000000000000000000000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000000000000000000000000000', - ] + ], + [false, false] ); await pool @@ -382,7 +384,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { [deadline, deadline], [aWETHv, aUsdcv], [aWETHr, aUsdcr], - [aWETHs, aUsdcs] + [aWETHs, aUsdcs], + [false, false] ); await pool @@ -478,7 +481,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { [deadline], [v], [r], - [s] + [s], + [false] ); await expect( @@ -544,7 +548,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { [0], [0], ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'] + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + [false] ); await expect( @@ -569,7 +574,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { [0, 0], [0], ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'] + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + [false] ); await expect( @@ -594,7 +600,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { [0, 0], [0, 0], ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'] + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + [false] ); await expect( @@ -622,7 +629,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { '0x0000000000000000000000000000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000000000000000000000000000', ], - ['0x0000000000000000000000000000000000000000000000000000000000000000'] + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + [false] ); await expect( @@ -650,7 +658,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { [ '0x0000000000000000000000000000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000000000000000000000000000', - ] + ], + [false] ); await expect( @@ -675,7 +684,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { [0], [0], ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'] + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + [false] ); await expect( @@ -700,7 +710,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { [0], [0], ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'] + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + [false] ); await expect( @@ -725,7 +736,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { [0], [0], ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'] + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + [false] ); await expect( @@ -741,6 +753,32 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { 0 ) ).to.be.revertedWith('INCONSISTENT_PARAMS'); + + const params9 = buildLiquiditySwapParams( + [dai.address], + [expectedDaiAmount], + [0], + [0], + [0], + [0], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + [false, false] + ); + + await expect( + pool + .connect(user) + .flashLoan( + uniswapLiquiditySwapAdapter.address, + [weth.address], + [flashloanAmount.toString()], + [0], + userAddress, + params9, + 0 + ) + ).to.be.revertedWith('INCONSISTENT_PARAMS'); }); it('should revert if caller not lending pool', async () => { @@ -773,7 +811,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { [0], [0], ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'] + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + [false] ); await expect( @@ -852,7 +891,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { [0], [0], ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'] + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + [false] ); await expect( @@ -916,7 +956,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { [0], [0], ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'] + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + [false] ); await expect( @@ -975,7 +1016,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { [0], [0], ['0x0000000000000000000000000000000000000000000000000000000000000000'], - ['0x0000000000000000000000000000000000000000000000000000000000000000'] + ['0x0000000000000000000000000000000000000000000000000000000000000000'], + [false] ); // Flashloan + premium > aToken balance. Then it will only swap the balance - premium @@ -1077,7 +1119,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { [deadline], [v], [r], - [s] + [s], + [false] ); // Flashloan + premium > aToken balance. Then it will only swap the balance - premium @@ -1490,7 +1533,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { s: '0x0000000000000000000000000000000000000000000000000000000000000000', }, ], - [false] + [false, false] ); const adapterWethBalance = await weth.balanceOf(uniswapLiquiditySwapAdapter.address); @@ -1630,7 +1673,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { s: aUsdcs, }, ], - [false] + [false, false] ); const adapterWethBalance = await weth.balanceOf(uniswapLiquiditySwapAdapter.address); diff --git a/test/uniswapAdapters.repay.spec.ts b/test/uniswapAdapters.repay.spec.ts index 10b12a04..fbae1d00 100644 --- a/test/uniswapAdapters.repay.spec.ts +++ b/test/uniswapAdapters.repay.spec.ts @@ -162,7 +162,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { 0, 0, '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000' + '0x0000000000000000000000000000000000000000000000000000000000000000', + false ); await expect( @@ -276,7 +277,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { deadline, v, r, - s + s, + false ); await expect( @@ -337,7 +339,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { 0, 0, '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000' + '0x0000000000000000000000000000000000000000000000000000000000000000', + false ); await expect( @@ -385,7 +388,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { 0, 0, '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000' + '0x0000000000000000000000000000000000000000000000000000000000000000', + false ); await expect( @@ -432,7 +436,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { 0, 0, '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000' + '0x0000000000000000000000000000000000000000000000000000000000000000', + false ); await expect( @@ -490,7 +495,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { 0, 0, '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000' + '0x0000000000000000000000000000000000000000000000000000000000000000', + false ); await expect( @@ -574,7 +580,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { 0, 0, '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000' + '0x0000000000000000000000000000000000000000000000000000000000000000', + false ); await expect( @@ -670,7 +677,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { 0, 0, '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000' + '0x0000000000000000000000000000000000000000000000000000000000000000', + false ); await pool @@ -762,7 +770,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { 0, 0, '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000' + '0x0000000000000000000000000000000000000000000000000000000000000000', + false ); await pool @@ -835,7 +844,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { 0, 0, '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000000' + '0x0000000000000000000000000000000000000000000000000000000000000000', + false ); await pool @@ -1374,9 +1384,9 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { await dai.approve(pool.address, parseEther('20')); await pool.deposit(dai.address, parseEther('20'), userAddress, 0); - const amountCollateralToSwap = parseEther('10'); + const amountCollateralToSwap = parseEther('4'); - const debtAmount = parseEther('10'); + const debtAmount = parseEther('3'); // Open user Debt await pool.connect(user).borrow(dai.address, debtAmount, 1, 0, userAddress);