mirror of
https://github.com/Instadapp/aave-protocol-v2.git
synced 2024-07-29 21:47:30 +00:00
ParaSwap adapter for collateral swaps
Complete with unit tests (using a mock AugustusSwapper contract). Has similar functionality/tests as for existing Uniswap adapter. Fixed a couple bugs in tests for Uniswap adapters.
This commit is contained in:
parent
2708551bcf
commit
242826ded6
124
contracts/adapters/BaseParaSwapAdapter.sol
Normal file
124
contracts/adapters/BaseParaSwapAdapter.sol
Normal file
|
@ -0,0 +1,124 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity 0.6.12;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
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 {IPriceOracleGetter} from '../interfaces/IPriceOracleGetter.sol';
|
||||
import {IERC20WithPermit} from '../interfaces/IERC20WithPermit.sol';
|
||||
import {FlashLoanReceiverBase} from '../flashloan/base/FlashLoanReceiverBase.sol';
|
||||
|
||||
/**
|
||||
* @title BaseParaSwapAdapter
|
||||
* @notice Utility functions for adapters using ParaSwap
|
||||
* @author Jason Raymond Bell
|
||||
*/
|
||||
abstract contract BaseParaSwapAdapter is FlashLoanReceiverBase, Ownable {
|
||||
using SafeMath for uint256;
|
||||
using SafeERC20 for IERC20;
|
||||
|
||||
struct PermitSignature {
|
||||
uint256 amount;
|
||||
uint256 deadline;
|
||||
uint8 v;
|
||||
bytes32 r;
|
||||
bytes32 s;
|
||||
}
|
||||
|
||||
// Max slippage percent allowed
|
||||
uint256 public constant MAX_SLIPPAGE_PERCENT = 3000; // 30%
|
||||
|
||||
IPriceOracleGetter public immutable ORACLE;
|
||||
|
||||
event Swapped(address indexed fromAsset, address indexed toAsset, uint256 fromAmount, uint256 receivedAmount);
|
||||
|
||||
constructor(
|
||||
ILendingPoolAddressesProvider addressesProvider
|
||||
) public FlashLoanReceiverBase(addressesProvider) {
|
||||
ORACLE = IPriceOracleGetter(addressesProvider.getPriceOracle());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Get the price of the asset from the oracle denominated in eth
|
||||
* @param asset address
|
||||
* @return eth price for the asset
|
||||
*/
|
||||
function _getPrice(address asset) internal view returns (uint256) {
|
||||
return ORACLE.getAssetPrice(asset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Get the decimals of an asset
|
||||
* @return number of decimals of the asset
|
||||
*/
|
||||
function _getDecimals(address asset) internal view returns (uint256) {
|
||||
return IERC20Detailed(asset).decimals();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Get the aToken associated to the asset
|
||||
* @return address of the aToken
|
||||
*/
|
||||
function _getReserveData(address asset) internal view returns (DataTypes.ReserveData memory) {
|
||||
return LENDING_POOL.getReserveData(asset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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(
|
||||
address reserve,
|
||||
address reserveAToken,
|
||||
address user,
|
||||
uint256 amount,
|
||||
PermitSignature memory permitSignature
|
||||
) internal {
|
||||
if (_usePermit(permitSignature)) {
|
||||
IERC20WithPermit(reserveAToken).permit(
|
||||
user,
|
||||
address(this),
|
||||
permitSignature.amount,
|
||||
permitSignature.deadline,
|
||||
permitSignature.v,
|
||||
permitSignature.r,
|
||||
permitSignature.s
|
||||
);
|
||||
}
|
||||
|
||||
// transfer from user to adapter
|
||||
IERC20(reserveAToken).safeTransferFrom(user, address(this), amount);
|
||||
|
||||
// withdraw reserve
|
||||
LENDING_POOL.withdraw(reserve, amount, address(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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)));
|
||||
}
|
||||
}
|
92
contracts/adapters/BaseParaSwapSellAdapter.sol
Normal file
92
contracts/adapters/BaseParaSwapSellAdapter.sol
Normal file
|
@ -0,0 +1,92 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity 0.6.12;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import {BaseParaSwapAdapter} from './BaseParaSwapAdapter.sol';
|
||||
import {PercentageMath} from '../protocol/libraries/math/PercentageMath.sol';
|
||||
import {IParaSwapAugustus} from '../interfaces/IParaSwapAugustus.sol';
|
||||
import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol';
|
||||
import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
|
||||
|
||||
/**
|
||||
* @title BaseParaSwapSellAdapter
|
||||
* @notice Implements the logic for selling tokens on ParaSwap
|
||||
* @author Jason Raymond Bell
|
||||
*/
|
||||
abstract contract BaseParaSwapSellAdapter is BaseParaSwapAdapter {
|
||||
using PercentageMath for uint256;
|
||||
|
||||
constructor(
|
||||
ILendingPoolAddressesProvider addressesProvider
|
||||
) public BaseParaSwapAdapter(addressesProvider) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Swaps a token for another using ParaSwap
|
||||
* @param fromAmountOffset Offset of fromAmount in Augustus calldata if it should be overwritten, otherwise 0
|
||||
* @param swapCalldata Calldata for ParaSwap's AugustusSwapper contract
|
||||
* @param augustus Address of ParaSwap's AugustusSwapper contract
|
||||
* @param assetToSwapFrom Address of the asset to be swapped from
|
||||
* @param assetToSwapTo Address of the asset to be swapped to
|
||||
* @param amountToSwap Amount to be swapped
|
||||
* @param minAmountToReceive Minimum amount to be received from the swap
|
||||
* @return amountReceived The amount received from the swap
|
||||
*/
|
||||
function _sellOnParaSwap(
|
||||
uint256 fromAmountOffset,
|
||||
bytes memory swapCalldata,
|
||||
address augustus,
|
||||
address assetToSwapFrom,
|
||||
address assetToSwapTo,
|
||||
uint256 amountToSwap,
|
||||
uint256 minAmountToReceive
|
||||
) internal returns (uint256 amountReceived) {
|
||||
{
|
||||
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 - MAX_SLIPPAGE_PERCENT);
|
||||
|
||||
require(expectedMinAmountOut <= minAmountToReceive, 'MIN_AMOUNT_EXCEEDS_MAX_SLIPPAGE');
|
||||
}
|
||||
|
||||
uint256 balanceBeforeAssetFrom = IERC20(assetToSwapFrom).balanceOf(address(this));
|
||||
require(balanceBeforeAssetFrom >= amountToSwap, 'INSUFFICIENT_BALANCE_BEFORE_SWAP');
|
||||
uint256 balanceBeforeAssetTo = IERC20(assetToSwapTo).balanceOf(address(this));
|
||||
|
||||
address tokenTransferProxy = IParaSwapAugustus(augustus).getTokenTransferProxy();
|
||||
IERC20(assetToSwapFrom).safeApprove(tokenTransferProxy, 0);
|
||||
IERC20(assetToSwapFrom).safeApprove(tokenTransferProxy, amountToSwap);
|
||||
|
||||
if (fromAmountOffset != 0) {
|
||||
require(fromAmountOffset >= 4 &&
|
||||
fromAmountOffset <= swapCalldata.length.sub(32),
|
||||
'FROM_AMOUNT_OFFSET_OUT_OF_RANGE');
|
||||
assembly {
|
||||
mstore(add(swapCalldata, add(fromAmountOffset, 32)), amountToSwap)
|
||||
}
|
||||
}
|
||||
(bool success,) = augustus.call(swapCalldata);
|
||||
if (!success) {
|
||||
// Copy revert reason from call
|
||||
assembly {
|
||||
let ptr := mload(0x40)
|
||||
let size := returndatasize()
|
||||
returndatacopy(ptr, 0, size)
|
||||
revert(ptr, size)
|
||||
}
|
||||
}
|
||||
require(IERC20(assetToSwapFrom).balanceOf(address(this)) == balanceBeforeAssetFrom - amountToSwap, 'WRONG_BALANCE_AFTER_SWAP');
|
||||
amountReceived = IERC20(assetToSwapTo).balanceOf(address(this)).sub(balanceBeforeAssetTo);
|
||||
require(amountReceived >= minAmountToReceive, 'INSUFFICIENT_AMOUNT_RECEIVED');
|
||||
|
||||
emit Swapped(assetToSwapFrom, assetToSwapTo, amountToSwap, amountReceived);
|
||||
}
|
||||
}
|
191
contracts/adapters/ParaSwapLiquiditySwapAdapter.sol
Normal file
191
contracts/adapters/ParaSwapLiquiditySwapAdapter.sol
Normal file
|
@ -0,0 +1,191 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity 0.6.12;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import {BaseParaSwapSellAdapter} from './BaseParaSwapSellAdapter.sol';
|
||||
import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol';
|
||||
import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
|
||||
|
||||
/**
|
||||
* @title ParaSwapLiquiditySwapAdapter
|
||||
* @notice Adapter to swap liquidity using ParaSwap.
|
||||
* @author Jason Raymond Bell
|
||||
*/
|
||||
contract ParaSwapLiquiditySwapAdapter is BaseParaSwapSellAdapter {
|
||||
constructor(
|
||||
ILendingPoolAddressesProvider addressesProvider
|
||||
) public BaseParaSwapSellAdapter(addressesProvider) {}
|
||||
|
||||
/**
|
||||
* @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 of the underlying asset to be swapped from
|
||||
* @param amounts Amount of the flash loan i.e. maximum amount to swap
|
||||
* @param premiums Fee of the flash loan
|
||||
* @param initiator Account that initiated the flash loan
|
||||
* @param params Additional variadic field to include extra params. Expected parameters:
|
||||
* address assetToSwapTo Address of the underlying asset to be swapped to and deposited
|
||||
* uint256 minAmountToReceive Min amount to be received from the swap
|
||||
* uint256 swapAllBalanceOffset Set to offset of fromAmount in Augustus calldata if wanting to swap all balance, otherwise 0
|
||||
* bytes swapCalldata Calldata for ParaSwap's AugustusSwapper contract
|
||||
* address augustus Address of ParaSwap's AugustusSwapper contract
|
||||
* PermitSignature permitParams Struct containing the permit signatures, set to all zeroes if not used
|
||||
*/
|
||||
function executeOperation(
|
||||
address[] calldata assets,
|
||||
uint256[] calldata amounts,
|
||||
uint256[] calldata premiums,
|
||||
address initiator,
|
||||
bytes calldata params
|
||||
) external override returns (bool) {
|
||||
require(msg.sender == address(LENDING_POOL), 'CALLER_MUST_BE_LENDING_POOL');
|
||||
require(assets.length == 1, 'FLASHLOAN_MULTIPLE_ASSETS_NOT_SUPPORTED');
|
||||
|
||||
uint256 flashLoanAmount = amounts[0];
|
||||
uint256 premium = premiums[0];
|
||||
address initiatorLocal = initiator;
|
||||
address assetToSwapFrom = assets[0];
|
||||
(
|
||||
address assetToSwapTo,
|
||||
uint256 minAmountToReceive,
|
||||
uint256 swapAllBalanceOffset,
|
||||
bytes memory swapCalldata,
|
||||
address augustus,
|
||||
PermitSignature memory permitParams
|
||||
) = abi.decode(params, (address, uint256, uint256, bytes, address, PermitSignature));
|
||||
|
||||
_swapLiquidity(
|
||||
swapAllBalanceOffset,
|
||||
swapCalldata,
|
||||
augustus,
|
||||
permitParams,
|
||||
flashLoanAmount,
|
||||
premium,
|
||||
initiatorLocal,
|
||||
assetToSwapFrom,
|
||||
assetToSwapTo,
|
||||
minAmountToReceive
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 assetToSwapFrom Address of the underlying asset to be swapped from
|
||||
* @param assetToSwapTo Address of the underlying asset to be swapped to and deposited
|
||||
* @param amountToSwap Amount to be swapped, or maximum amount when swapping all balance
|
||||
* @param minAmountToReceive Minimum amount to be received from the swap
|
||||
* @param swapAllBalanceOffset Set to offset of fromAmount in Augustus calldata if wanting to swap all balance, otherwise 0
|
||||
* @param swapCalldata Calldata for ParaSwap's AugustusSwapper contract
|
||||
* @param augustus Address of ParaSwap's AugustusSwapper contract
|
||||
* @param permitParams Struct containing the permit signatures, set to all zeroes if not used
|
||||
*/
|
||||
function swapAndDeposit(
|
||||
address assetToSwapFrom,
|
||||
address assetToSwapTo,
|
||||
uint256 amountToSwap,
|
||||
uint256 minAmountToReceive,
|
||||
uint256 swapAllBalanceOffset,
|
||||
bytes calldata swapCalldata,
|
||||
address augustus,
|
||||
PermitSignature calldata permitParams
|
||||
) external {
|
||||
address aToken = _getReserveData(assetToSwapFrom).aTokenAddress;
|
||||
|
||||
if (swapAllBalanceOffset != 0) {
|
||||
uint256 balance = IERC20(aToken).balanceOf(msg.sender);
|
||||
require(balance <= amountToSwap, 'INSUFFICIENT_AMOUNT_TO_SWAP');
|
||||
amountToSwap = balance;
|
||||
}
|
||||
|
||||
_pullAToken(
|
||||
assetToSwapFrom,
|
||||
aToken,
|
||||
msg.sender,
|
||||
amountToSwap,
|
||||
permitParams
|
||||
);
|
||||
|
||||
uint256 amountReceived = _sellOnParaSwap(
|
||||
swapAllBalanceOffset,
|
||||
swapCalldata,
|
||||
augustus,
|
||||
assetToSwapFrom,
|
||||
assetToSwapTo,
|
||||
amountToSwap,
|
||||
minAmountToReceive
|
||||
);
|
||||
|
||||
IERC20(assetToSwapTo).safeApprove(address(LENDING_POOL), 0);
|
||||
IERC20(assetToSwapTo).safeApprove(address(LENDING_POOL), amountReceived);
|
||||
LENDING_POOL.deposit(assetToSwapTo, amountReceived, msg.sender, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Swaps an amount of an asset to another and deposits the funds on behalf of the initiator.
|
||||
* @param swapAllBalanceOffset Set to offset of fromAmount in Augustus calldata if wanting to swap all balance, otherwise 0
|
||||
* @param swapCalldata Calldata for ParaSwap's AugustusSwapper contract
|
||||
* @param augustus Address of ParaSwap's AugustusSwapper contract
|
||||
* @param permitParams Struct containing the permit signatures, set to all zeroes if not used
|
||||
* @param flashLoanAmount Amount of the flash loan i.e. maximum amount to swap
|
||||
* @param premium Fee of the flash loan
|
||||
* @param initiator Account that initiated the flash loan
|
||||
* @param assetToSwapFrom Address of the underyling asset to be swapped from
|
||||
* @param assetToSwapTo Address of the underlying asset to be swapped to and deposited
|
||||
* @param minAmountToReceive Min amount to be received from the swap
|
||||
*/
|
||||
function _swapLiquidity (
|
||||
uint256 swapAllBalanceOffset,
|
||||
bytes memory swapCalldata,
|
||||
address augustus,
|
||||
PermitSignature memory permitParams,
|
||||
uint256 flashLoanAmount,
|
||||
uint256 premium,
|
||||
address initiator,
|
||||
address assetToSwapFrom,
|
||||
address assetToSwapTo,
|
||||
uint256 minAmountToReceive
|
||||
) internal {
|
||||
address aToken = _getReserveData(assetToSwapFrom).aTokenAddress;
|
||||
uint256 amountToSwap = flashLoanAmount;
|
||||
|
||||
uint256 balance = IERC20(aToken).balanceOf(initiator);
|
||||
if (swapAllBalanceOffset != 0) {
|
||||
uint256 balanceToSwap = balance.sub(premium);
|
||||
require(balanceToSwap <= amountToSwap, 'INSUFFICIENT_AMOUNT_TO_SWAP');
|
||||
amountToSwap = balanceToSwap;
|
||||
} else {
|
||||
require(balance >= amountToSwap.add(premium), 'INSUFFICIENT_ATOKEN_BALANCE');
|
||||
}
|
||||
|
||||
uint256 amountReceived = _sellOnParaSwap(
|
||||
swapAllBalanceOffset,
|
||||
swapCalldata,
|
||||
augustus,
|
||||
assetToSwapFrom,
|
||||
assetToSwapTo,
|
||||
amountToSwap,
|
||||
minAmountToReceive
|
||||
);
|
||||
|
||||
IERC20(assetToSwapTo).safeApprove(address(LENDING_POOL), 0);
|
||||
IERC20(assetToSwapTo).safeApprove(address(LENDING_POOL), amountReceived);
|
||||
LENDING_POOL.deposit(assetToSwapTo, amountReceived, initiator, 0);
|
||||
|
||||
_pullAToken(
|
||||
assetToSwapFrom,
|
||||
aToken,
|
||||
initiator,
|
||||
amountToSwap.add(premium),
|
||||
permitParams
|
||||
);
|
||||
|
||||
// Repay flash loan
|
||||
IERC20(assetToSwapFrom).safeApprove(address(LENDING_POOL), 0);
|
||||
IERC20(assetToSwapFrom).safeApprove(address(LENDING_POOL), flashLoanAmount.add(premium));
|
||||
}
|
||||
}
|
7
contracts/interfaces/IParaSwapAugustus.sol
Normal file
7
contracts/interfaces/IParaSwapAugustus.sol
Normal file
|
@ -0,0 +1,7 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity 0.6.12;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
interface IParaSwapAugustus {
|
||||
function getTokenTransferProxy() external view returns (address);
|
||||
}
|
59
contracts/mocks/swap/MockParaSwapAugustus.sol
Normal file
59
contracts/mocks/swap/MockParaSwapAugustus.sol
Normal file
|
@ -0,0 +1,59 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity 0.6.12;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import {IParaSwapAugustus} from '../../interfaces/IParaSwapAugustus.sol';
|
||||
import {MockParaSwapTokenTransferProxy} from './MockParaSwapTokenTransferProxy.sol';
|
||||
import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol';
|
||||
import {MintableERC20} from '../tokens/MintableERC20.sol';
|
||||
|
||||
contract MockParaSwapAugustus is IParaSwapAugustus {
|
||||
MockParaSwapTokenTransferProxy _tokenTransferProxy;
|
||||
bool _expectingSwap;
|
||||
address _expectedFromToken;
|
||||
address _expectedToToken;
|
||||
uint256 _expectedFromAmountMin;
|
||||
uint256 _expectedFromAmountMax;
|
||||
uint256 _receivedAmount;
|
||||
|
||||
constructor() public {
|
||||
_tokenTransferProxy = new MockParaSwapTokenTransferProxy();
|
||||
}
|
||||
|
||||
function getTokenTransferProxy() external view override returns (address) {
|
||||
return address(_tokenTransferProxy);
|
||||
}
|
||||
|
||||
function expectSwap(
|
||||
address fromToken,
|
||||
address toToken,
|
||||
uint256 fromAmountMin,
|
||||
uint256 fromAmountMax,
|
||||
uint256 receivedAmount
|
||||
) external {
|
||||
_expectingSwap = true;
|
||||
_expectedFromToken = fromToken;
|
||||
_expectedToToken = toToken;
|
||||
_expectedFromAmountMin = fromAmountMin;
|
||||
_expectedFromAmountMax = fromAmountMax;
|
||||
_receivedAmount = receivedAmount;
|
||||
}
|
||||
|
||||
function swap(
|
||||
address fromToken,
|
||||
address toToken,
|
||||
uint256 fromAmount,
|
||||
uint256 toAmount
|
||||
) external returns (uint256) {
|
||||
require(_expectingSwap, 'Not expecting swap');
|
||||
require(fromToken == _expectedFromToken, 'Unexpected from token');
|
||||
require(toToken == _expectedToToken, 'Unexpected to token');
|
||||
require(fromAmount >= _expectedFromAmountMin && fromAmount <= _expectedFromAmountMax, 'From amount out of range');
|
||||
require(_receivedAmount >= toAmount, 'Received amount of tokens are less than expected');
|
||||
_tokenTransferProxy.transferFrom(fromToken, msg.sender, address(this), fromAmount);
|
||||
MintableERC20(toToken).mint(_receivedAmount);
|
||||
IERC20(toToken).transfer(msg.sender, _receivedAmount);
|
||||
_expectingSwap = false;
|
||||
return _receivedAmount;
|
||||
}
|
||||
}
|
17
contracts/mocks/swap/MockParaSwapTokenTransferProxy.sol
Normal file
17
contracts/mocks/swap/MockParaSwapTokenTransferProxy.sol
Normal file
|
@ -0,0 +1,17 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity 0.6.12;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import {Ownable} from '../../dependencies/openzeppelin/contracts/Ownable.sol';
|
||||
import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol';
|
||||
|
||||
contract MockParaSwapTokenTransferProxy is Ownable {
|
||||
function transferFrom(
|
||||
address token,
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount
|
||||
) external onlyOwner {
|
||||
IERC20(token).transferFrom(from, to, amount);
|
||||
}
|
||||
}
|
|
@ -36,9 +36,11 @@ import {
|
|||
MockAggregatorFactory,
|
||||
MockATokenFactory,
|
||||
MockFlashLoanReceiverFactory,
|
||||
MockParaSwapAugustusFactory,
|
||||
MockStableDebtTokenFactory,
|
||||
MockVariableDebtTokenFactory,
|
||||
MockUniswapV2Router02Factory,
|
||||
ParaSwapLiquiditySwapAdapterFactory,
|
||||
PriceOracleFactory,
|
||||
ReserveLogicFactory,
|
||||
SelfdestructTransferFactory,
|
||||
|
@ -537,3 +539,22 @@ export const deployFlashLiquidationAdapter = async (
|
|||
args,
|
||||
verify
|
||||
);
|
||||
|
||||
export const deployMockParaSwapAugustus = async (verify?: boolean) =>
|
||||
withSaveAndVerify(
|
||||
await new MockParaSwapAugustusFactory(await getFirstSigner()).deploy(),
|
||||
eContractid.MockParaSwapAugustus,
|
||||
[],
|
||||
verify
|
||||
);
|
||||
|
||||
export const deployParaSwapLiquiditySwapAdapter = async (
|
||||
args: [tEthereumAddress],
|
||||
verify?: boolean
|
||||
) =>
|
||||
withSaveAndVerify(
|
||||
await new ParaSwapLiquiditySwapAdapterFactory(await getFirstSigner()).deploy(...args),
|
||||
eContractid.ParaSwapLiquiditySwapAdapter,
|
||||
args,
|
||||
verify
|
||||
);
|
||||
|
|
|
@ -18,6 +18,8 @@ import {
|
|||
MockStableDebtTokenFactory,
|
||||
MockVariableDebtTokenFactory,
|
||||
MockUniswapV2Router02Factory,
|
||||
MockParaSwapAugustusFactory,
|
||||
ParaSwapLiquiditySwapAdapterFactory,
|
||||
PriceOracleFactory,
|
||||
ReserveLogicFactory,
|
||||
SelfdestructTransferFactory,
|
||||
|
@ -363,3 +365,19 @@ export const getFlashLiquidationAdapter = async (address?: tEthereumAddress) =>
|
|||
.address,
|
||||
await getFirstSigner()
|
||||
);
|
||||
|
||||
export const getMockParaSwapAugustus = async (address?: tEthereumAddress) =>
|
||||
await MockParaSwapAugustusFactory.connect(
|
||||
address ||
|
||||
(await getDb().get(`${eContractid.MockParaSwapAugustus}.${DRE.network.name}`).value())
|
||||
.address,
|
||||
await getFirstSigner()
|
||||
);
|
||||
|
||||
export const getParaSwapLiquiditySwapAdapter = async (address?: tEthereumAddress) =>
|
||||
await ParaSwapLiquiditySwapAdapterFactory.connect(
|
||||
address ||
|
||||
(await getDb().get(`${eContractid.ParaSwapLiquiditySwapAdapter}.${DRE.network.name}`).value())
|
||||
.address,
|
||||
await getFirstSigner()
|
||||
);
|
||||
|
|
|
@ -301,3 +301,35 @@ export const buildFlashLiquidationAdapterParams = (
|
|||
[collateralAsset, debtAsset, user, debtToCover, useEthPath]
|
||||
);
|
||||
};
|
||||
|
||||
export const buildParaSwapLiquiditySwapParams = (
|
||||
assetToSwapTo: tEthereumAddress,
|
||||
minAmountToReceive: BigNumberish,
|
||||
swapAllBalanceOffset: BigNumberish,
|
||||
swapCalldata: string | Buffer,
|
||||
augustus: tEthereumAddress,
|
||||
permitAmount: BigNumberish,
|
||||
deadline: BigNumberish,
|
||||
v: BigNumberish,
|
||||
r: string | Buffer,
|
||||
s: string | Buffer
|
||||
) => {
|
||||
return ethers.utils.defaultAbiCoder.encode(
|
||||
[
|
||||
'address',
|
||||
'uint256',
|
||||
'uint256',
|
||||
'bytes',
|
||||
'address',
|
||||
'tuple(uint256,uint256,uint8,bytes32,bytes32)',
|
||||
],
|
||||
[
|
||||
assetToSwapTo,
|
||||
minAmountToReceive,
|
||||
swapAllBalanceOffset,
|
||||
swapCalldata,
|
||||
augustus,
|
||||
[permitAmount, deadline, v, r, s],
|
||||
]
|
||||
);
|
||||
};
|
||||
|
|
|
@ -71,6 +71,8 @@ export enum eContractid {
|
|||
UniswapLiquiditySwapAdapter = 'UniswapLiquiditySwapAdapter',
|
||||
UniswapRepayAdapter = 'UniswapRepayAdapter',
|
||||
FlashLiquidationAdapter = 'FlashLiquidationAdapter',
|
||||
MockParaSwapAugustus = 'MockParaSwapAugustus',
|
||||
ParaSwapLiquiditySwapAdapter = 'ParaSwapLiquiditySwapAdapter',
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
"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-paraswap": "hardhat test test/__setup.spec.ts test/paraswapAdapters*.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",
|
||||
|
|
|
@ -26,6 +26,8 @@ import {
|
|||
deployUniswapLiquiditySwapAdapter,
|
||||
deployUniswapRepayAdapter,
|
||||
deployFlashLiquidationAdapter,
|
||||
deployMockParaSwapAugustus,
|
||||
deployParaSwapLiquiditySwapAdapter,
|
||||
} from '../helpers/contracts-deployments';
|
||||
import { Signer } from 'ethers';
|
||||
import { TokenContractId, eContractid, tEthereumAddress, AavePools } from '../helpers/types';
|
||||
|
@ -247,6 +249,10 @@ const buildTestEnv = async (deployer: Signer, secondaryWallet: Signer) => {
|
|||
await deployUniswapRepayAdapter(adapterParams);
|
||||
await deployFlashLiquidationAdapter(adapterParams);
|
||||
|
||||
await deployMockParaSwapAugustus();
|
||||
|
||||
await deployParaSwapLiquiditySwapAdapter([addressesProvider.address]);
|
||||
|
||||
await deployWalletBalancerProvider();
|
||||
|
||||
await deployWETHGateway([mockTokens.WETH.address, lendingPoolAddress]);
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
getUniswapLiquiditySwapAdapter,
|
||||
getUniswapRepayAdapter,
|
||||
getFlashLiquidationAdapter,
|
||||
getParaSwapLiquiditySwapAdapter,
|
||||
} from '../../helpers/contracts-getters';
|
||||
import { eEthereumNetwork, tEthereumAddress } from '../../helpers/types';
|
||||
import { LendingPool } from '../../types/LendingPool';
|
||||
|
@ -32,6 +33,7 @@ import { LendingPoolAddressesProviderRegistry } from '../../types/LendingPoolAdd
|
|||
import { getEthersSigners } from '../../helpers/contracts-helpers';
|
||||
import { UniswapLiquiditySwapAdapter } from '../../types/UniswapLiquiditySwapAdapter';
|
||||
import { UniswapRepayAdapter } from '../../types/UniswapRepayAdapter';
|
||||
import { ParaSwapLiquiditySwapAdapter } from '../../types/ParaSwapLiquiditySwapAdapter';
|
||||
import { getParamPerNetwork } from '../../helpers/contracts-helpers';
|
||||
import { WETH9Mocked } from '../../types/WETH9Mocked';
|
||||
import { WETHGateway } from '../../types/WETHGateway';
|
||||
|
@ -68,6 +70,7 @@ export interface TestEnv {
|
|||
registry: LendingPoolAddressesProviderRegistry;
|
||||
wethGateway: WETHGateway;
|
||||
flashLiquidationAdapter: FlashLiquidationAdapter;
|
||||
paraswapLiquiditySwapAdapter: ParaSwapLiquiditySwapAdapter;
|
||||
}
|
||||
|
||||
let buidlerevmSnapshotId: string = '0x1';
|
||||
|
@ -92,6 +95,7 @@ const testEnv: TestEnv = {
|
|||
uniswapLiquiditySwapAdapter: {} as UniswapLiquiditySwapAdapter,
|
||||
uniswapRepayAdapter: {} as UniswapRepayAdapter,
|
||||
flashLiquidationAdapter: {} as FlashLiquidationAdapter,
|
||||
paraswapLiquiditySwapAdapter: {} as ParaSwapLiquiditySwapAdapter,
|
||||
registry: {} as LendingPoolAddressesProviderRegistry,
|
||||
wethGateway: {} as WETHGateway,
|
||||
} as TestEnv;
|
||||
|
@ -158,6 +162,8 @@ export async function initializeMakeSuite() {
|
|||
testEnv.uniswapLiquiditySwapAdapter = await getUniswapLiquiditySwapAdapter();
|
||||
testEnv.uniswapRepayAdapter = await getUniswapRepayAdapter();
|
||||
testEnv.flashLiquidationAdapter = await getFlashLiquidationAdapter();
|
||||
|
||||
testEnv.paraswapLiquiditySwapAdapter = await getParaSwapLiquiditySwapAdapter();
|
||||
}
|
||||
|
||||
const setSnapshot = async () => {
|
||||
|
|
2332
test/paraswapAdapters.liquiditySwap.spec.ts
Normal file
2332
test/paraswapAdapters.liquiditySwap.spec.ts
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -198,7 +198,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
|||
|
||||
it('should revert if not valid addresses provider', async () => {
|
||||
const { weth } = testEnv;
|
||||
expect(
|
||||
await expect(
|
||||
deployFlashLiquidationAdapter([
|
||||
mockUniswapRouter.address,
|
||||
mockUniswapRouter.address,
|
||||
|
|
|
@ -50,7 +50,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
|||
|
||||
it('should revert if not valid addresses provider', async () => {
|
||||
const { weth } = testEnv;
|
||||
expect(
|
||||
await expect(
|
||||
deployUniswapLiquiditySwapAdapter([
|
||||
mockUniswapRouter.address,
|
||||
mockUniswapRouter.address,
|
||||
|
@ -196,6 +196,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
|||
.div(
|
||||
new BigNumber(daiPrice.toString()).times(new BigNumber(10).pow(collateralDecimals))
|
||||
)
|
||||
.div(new BigNumber(10).pow(principalDecimals))
|
||||
.toFixed(0)
|
||||
);
|
||||
|
||||
|
@ -318,6 +319,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
|||
.div(
|
||||
new BigNumber(daiPrice.toString()).times(new BigNumber(10).pow(collateralDecimals))
|
||||
)
|
||||
.div(new BigNumber(10).pow(principalDecimals))
|
||||
.toFixed(0)
|
||||
);
|
||||
|
||||
|
@ -871,6 +873,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
|||
.div(
|
||||
new BigNumber(daiPrice.toString()).times(new BigNumber(10).pow(collateralDecimals))
|
||||
)
|
||||
.div(new BigNumber(10).pow(principalDecimals))
|
||||
.toFixed(0)
|
||||
);
|
||||
|
||||
|
@ -1493,6 +1496,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
|||
.div(
|
||||
new BigNumber(daiPrice.toString()).times(new BigNumber(10).pow(collateralDecimals))
|
||||
)
|
||||
.div(new BigNumber(10).pow(principalDecimals))
|
||||
.toFixed(0)
|
||||
);
|
||||
|
||||
|
@ -1601,6 +1605,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
|||
.div(
|
||||
new BigNumber(daiPrice.toString()).times(new BigNumber(10).pow(collateralDecimals))
|
||||
)
|
||||
.div(new BigNumber(10).pow(principalDecimals))
|
||||
.toFixed(0)
|
||||
);
|
||||
|
||||
|
|
|
@ -87,7 +87,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
|||
|
||||
it('should revert if not valid addresses provider', async () => {
|
||||
const { weth } = testEnv;
|
||||
expect(
|
||||
await expect(
|
||||
deployUniswapRepayAdapter([
|
||||
mockUniswapRouter.address,
|
||||
mockUniswapRouter.address,
|
||||
|
|
Loading…
Reference in New Issue
Block a user