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 {ReserveLogic} from '../libraries/logic/ReserveLogic.sol';
import {IUniswapV2Router02} from '../interfaces/IUniswapV2Router02.sol'; import {IUniswapV2Router02} from '../interfaces/IUniswapV2Router02.sol';
import {IPriceOracleGetter} from '../interfaces/IPriceOracleGetter.sol'; import {IPriceOracleGetter} from '../interfaces/IPriceOracleGetter.sol';
import {IERC20WithPermit} from '../interfaces/IERC20WithPermit.sol';
/** /**
* @title BaseUniswapAdapter * @title BaseUniswapAdapter
@ -25,6 +26,20 @@ contract BaseUniswapAdapter {
enum LeftoverAction {DEPOSIT, TRANSFER} 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 // Max slippage percent allow by param
uint256 public constant MAX_SLIPPAGE_PERCENT = 3000; // 30% uint256 public constant MAX_SLIPPAGE_PERCENT = 3000; // 30%
// Min slippage percent allow by param // Min slippage percent allow by param
@ -221,14 +236,28 @@ contract BaseUniswapAdapter {
* @param reserve address of the asset * @param reserve address of the asset
* @param user address * @param user address
* @param amount of tokens to be transferred to the contract * @param amount of tokens to be transferred to the contract
* @param permitSignature struct containing the permit signature
*/ */
function pullAToken( function pullAToken(
address reserve, address reserve,
address user, address user,
uint256 amount uint256 amount,
PermitSignature memory permitSignature
) internal { ) internal {
address reserveAToken = getAToken(reserve); 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 // transfer from user to adapter
IERC20(reserveAToken).safeTransferFrom(user, address(this), amount); IERC20(reserveAToken).safeTransferFrom(user, address(this), amount);
@ -241,15 +270,28 @@ contract BaseUniswapAdapter {
* @param reserve address of the asset * @param reserve address of the asset
* @param user address * @param user address
* @param flashLoanDebt need to be repaid * @param flashLoanDebt need to be repaid
* @param permitSignature struct containing the permit signature
*/ */
function pullATokenAndRepayFlashLoan( function pullATokenAndRepayFlashLoan(
address reserve, address reserve,
address user, address user,
uint256 flashLoanDebt uint256 flashLoanDebt,
PermitSignature memory permitSignature
) internal { ) internal {
pullAToken(reserve, user, flashLoanDebt); pullAToken(reserve, user, flashLoanDebt, permitSignature);
// Repay flashloan // Repay flashloan
IERC20(reserve).approve(address(POOL), flashLoanDebt); 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 { contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
struct SwapParams {
address[] assetToSwapToList;
uint256 slippage;
PermitParams permitParams;
}
constructor( constructor(
ILendingPoolAddressesProvider addressesProvider, ILendingPoolAddressesProvider addressesProvider,
IUniswapV2Router02 uniswapRouter IUniswapV2Router02 uniswapRouter
@ -35,6 +41,10 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
* @param params Additional variadic field to include extra params. Expected parameters: * @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 * 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 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( function executeOperation(
address[] calldata assets, address[] calldata assets,
@ -45,19 +55,46 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
) external override returns (bool) { ) external override returns (bool) {
require(msg.sender == address(POOL), "CALLER_MUST_BE_LENDING_POOL"); require(msg.sender == address(POOL), "CALLER_MUST_BE_LENDING_POOL");
(address[] memory assetToSwapToList, uint256 slippage) = abi.decode(params, (address[], uint256)); SwapParams memory decodedParams = _decodeParams(params);
require(slippage < MAX_SLIPPAGE_PERCENT && slippage >= MIN_SLIPPAGE_PERCENT, 'SLIPPAGE_OUT_OF_RANGE');
require(assetToSwapToList.length == assets.length, 'INCONSISTENT_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++) { 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 // Deposit new reserve
IERC20(assetToSwapToList[i]).approve(address(POOL), receivedAmount); IERC20(decodedParams.assetToSwapToList[i]).approve(address(POOL), receivedAmount);
POOL.deposit(assetToSwapToList[i], receivedAmount, initiator, 0); POOL.deposit(decodedParams.assetToSwapToList[i], receivedAmount, initiator, 0);
uint256 flashLoanDebt = amounts[i].add(premiums[i]); 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; 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 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
* @param slippage The max slippage percentage allowed for the swap * @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( function swapAndDeposit(
address[] calldata assetToSwapFromList, address[] calldata assetToSwapFromList,
address[] calldata assetToSwapToList, address[] calldata assetToSwapToList,
uint256[] calldata amountToSwapList, uint256[] calldata amountToSwapList,
uint256 slippage uint256 slippage,
PermitSignature[] calldata permitParams
) external { ) external {
require( require(
assetToSwapFromList.length == assetToSwapToList.length && assetToSwapFromList.length == amountToSwapList.length, assetToSwapFromList.length == assetToSwapToList.length
&& assetToSwapFromList.length == amountToSwapList.length
&& assetToSwapFromList.length == permitParams.length,
'INCONSISTENT_PARAMS' 'INCONSISTENT_PARAMS'
); );
for (uint256 i = 0; i < assetToSwapFromList.length; i++) { 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( uint256 receivedAmount = swapExactTokensForTokens(
assetToSwapFromList[i], assetToSwapFromList[i],
@ -99,4 +148,29 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
POOL.deposit(assetToSwapToList[i], receivedAmount, msg.sender, 0); 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; LeftoverAction leftOverAction;
uint256[] repayAmounts; uint256[] repayAmounts;
uint256[] rateModes; uint256[] rateModes;
PermitParams permitParams;
} }
constructor( constructor(
@ -46,6 +47,10 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
* (1) Direct transfer to user * (1) Direct transfer to user
* uint256[] repayAmounts List of amounts of 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 * 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( function executeOperation(
address[] calldata assets, address[] calldata assets,
@ -61,7 +66,11 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
require( require(
assets.length == decodedParams.assetToSwapToList.length assets.length == decodedParams.assetToSwapToList.length
&& assets.length == decodedParams.repayAmounts.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'); 'INCONSISTENT_PARAMS');
for (uint256 i = 0; i < assets.length; i++) { for (uint256 i = 0; i < assets.length; i++) {
@ -73,7 +82,13 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
decodedParams.rateModes[i], decodedParams.rateModes[i],
initiator, initiator,
decodedParams.leftOverAction, 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 initiator Address of the user
* @param leftOverAction enum 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
* @param premium Fee of the flash loan * @param premium Fee of the flash loan
* @param permitSignature struct containing the permit signature
*/ */
function _swapAndRepay( function _swapAndRepay(
address assetFrom, address assetFrom,
@ -100,7 +116,8 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
uint256 rateMode, uint256 rateMode,
address initiator, address initiator,
LeftoverAction leftOverAction, LeftoverAction leftOverAction,
uint256 premium uint256 premium,
PermitSignature memory permitSignature
) internal { ) internal {
swapTokensForExactTokens(assetFrom, assetTo, amount, repayAmount); swapTokensForExactTokens(assetFrom, assetTo, amount, repayAmount);
@ -109,7 +126,7 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
POOL.repay(assetTo, repayAmount, rateMode, initiator); POOL.repay(assetTo, repayAmount, rateMode, initiator);
uint256 flashLoanDebt = amount.add(premium); uint256 flashLoanDebt = amount.add(premium);
pullATokenAndRepayFlashLoan(assetFrom, initiator, flashLoanDebt); pullATokenAndRepayFlashLoan(assetFrom, initiator, flashLoanDebt, permitSignature);
// Take care of reserve leftover from the swap // Take care of reserve leftover from the swap
sendLeftovers(assetFrom, flashLoanDebt, leftOverAction, initiator); sendLeftovers(assetFrom, flashLoanDebt, leftOverAction, initiator);
@ -124,6 +141,10 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
* (1) Direct transfer to user * (1) Direct transfer to user
* uint256[] repayAmounts List of amounts of 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 * 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 * @return RepayParams struct containing decoded params
*/ */
function _decodeParams(bytes memory params) internal returns (RepayParams memory) { function _decodeParams(bytes memory params) internal returns (RepayParams memory) {
@ -131,9 +152,24 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
address[] memory assetToSwapToList, address[] memory assetToSwapToList,
LeftoverAction leftOverAction, LeftoverAction leftOverAction,
uint256[] memory repayAmounts, uint256[] memory repayAmounts,
uint256[] memory rateModes uint256[] memory rateModes,
) = abi.decode(params, (address[], LeftoverAction, uint256[], uint256[])); 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