Update getAmountsOut to return prices

This commit is contained in:
Gerardo Nardelli 2020-11-04 16:51:21 -03:00
parent 847ad3b12a
commit 5d9cd6ebd1
2 changed files with 166 additions and 23 deletions

View File

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

View File

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