Add swapAllBalance parameter for liquidity swap

This commit is contained in:
Gerardo Nardelli 2020-11-05 16:49:55 -03:00
parent 48b9a603a7
commit 20bbae88d3
4 changed files with 327 additions and 72 deletions

View File

@ -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.

View File

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

View File

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

View File

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