Add permit support in swap adapters

This commit is contained in:
Gerardo Nardelli 2020-11-02 17:33:00 -03:00
parent e7183536b3
commit fa7fa9f948
5 changed files with 1052 additions and 58 deletions

View File

@ -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);
}
}

View File

@ -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));
}
}

View File

@ -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
)
);
}
}

View File

@ -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;
}

File diff suppressed because it is too large Load Diff