Update getAmountsIn to return prices

This commit is contained in:
Gerardo Nardelli 2020-11-05 11:33:11 -03:00
parent 5d9cd6ebd1
commit 48b9a603a7
2 changed files with 176 additions and 39 deletions

View File

@ -40,6 +40,13 @@ contract BaseUniswapAdapter {
bytes32 s; bytes32 s;
} }
struct AmountCalc {
uint256 calculatedAmount;
uint256 relativePrice;
uint256 amountInUsd;
uint256 amountOutUsd;
}
// Max slippage percent allowed // Max slippage percent allowed
uint256 public constant MAX_SLIPPAGE_PERCENT = 3000; // 30% uint256 public constant MAX_SLIPPAGE_PERCENT = 3000; // 30%
// FLash Loan fee set in lending pool // FLash Loan fee set in lending pool
@ -64,7 +71,7 @@ contract BaseUniswapAdapter {
* @param amountIn Amount of reserveIn * @param amountIn Amount of reserveIn
* @param reserveIn Address of the asset to be swap from * @param reserveIn Address of the asset to be swap from
* @param reserveOut Address of the asset to be swap to * @param reserveOut Address of the asset to be swap to
* @return uint256 Amount out fo the reserveOut * @return uint256 Amount out of the reserveOut
* @return uint256 The price of out amount denominated in the reserveIn currency (18 decimals) * @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 In amount of reserveIn value denominated in USD (8 decimals)
* @return uint256 Out amount of reserveOut value denominated in USD (8 decimals) * @return uint256 Out amount of reserveOut value denominated in USD (8 decimals)
@ -74,46 +81,39 @@ contract BaseUniswapAdapter {
view view
returns (uint256, uint256, uint256, uint256) returns (uint256, uint256, uint256, uint256)
{ {
// Subtract flash loan fee AmountCalc memory results = _getAmountsOut(reserveIn, reserveOut, amountIn);
uint256 finalAmountIn = amountIn.sub(amountIn.mul(FLASHLOAN_PREMIUM_TOTAL).div(10000));
uint256 amountOut = _getAmountsOut(reserveIn, reserveOut, finalAmountIn);
uint256 reserveInDecimals = _getDecimals(reserveIn);
uint256 reserveOutDecimals = _getDecimals(reserveOut);
uint256 outPerInPrice = finalAmountIn
.mul(10**18)
.mul(10**reserveOutDecimals)
.div(amountOut.mul(10**reserveInDecimals));
return ( return (
amountOut, results.calculatedAmount,
outPerInPrice, results.relativePrice,
_calcUsdValue(reserveIn, amountIn, reserveInDecimals), results.amountInUsd,
_calcUsdValue(reserveOut, amountOut, reserveOutDecimals) results.amountOutUsd
); );
} }
/** /**
* @dev Returns the minimum input asset amount required to buy the given output asset amount * @dev Returns the minimum input asset amount required to buy the given output asset amount and the prices
* @param amountOut Amount of reserveOut * @param amountOut Amount of reserveOut
* @param reserveIn Address of the asset to be swap from * @param reserveIn Address of the asset to be swap from
* @param reserveOut Address of the asset to be swap to * @param reserveOut Address of the asset to be swap to
* @return uint256 amountIn * @return uint256 Amount in of the reserveIn
* @return uint256 The price of in amount denominated in the reserveOut 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 getAmountIn(uint256 amountOut, address reserveIn, address reserveOut) function getAmountsIn(uint256 amountOut, address reserveIn, address reserveOut)
external external
view view
returns (uint256) returns (uint256, uint256, uint256, uint256)
{ {
address[] memory path = new address[](2); AmountCalc memory results = _getAmountsIn(reserveIn, reserveOut, amountOut);
path[0] = reserveIn;
path[1] = reserveOut;
uint256[] memory amounts = UNISWAP_ROUTER.getAmountsIn(amountOut, path); return (
results.calculatedAmount,
return amounts[0]; results.relativePrice,
results.amountInUsd,
results.amountOutUsd
);
} }
/** /**
@ -337,15 +337,72 @@ contract BaseUniswapAdapter {
* @param reserveIn Address of the asset to be swap from * @param reserveIn Address of the asset to be swap from
* @param reserveOut Address of the asset to be swap to * @param reserveOut Address of the asset to be swap to
* @param amountIn Amount of reserveIn * @param amountIn Amount of reserveIn
* @return the output amount * @return Struct containing the following information:
* uint256 Amount out of the reserveOut
* uint256 The price of out amount denominated in the reserveIn currency (18 decimals)
* uint256 In amount of reserveIn value denominated in USD (8 decimals)
* uint256 Out amount of reserveOut value denominated in USD (8 decimals)
*/ */
function _getAmountsOut(address reserveIn, address reserveOut, uint256 amountIn) internal view returns (uint256) { function _getAmountsOut(address reserveIn, address reserveOut, uint256 amountIn) internal view returns (AmountCalc memory) {
// Subtract flash loan fee
uint256 finalAmountIn = amountIn.sub(amountIn.mul(FLASHLOAN_PREMIUM_TOTAL).div(10000));
address[] memory path = new address[](2); address[] memory path = new address[](2);
path[0] = reserveIn; path[0] = reserveIn;
path[1] = reserveOut; path[1] = reserveOut;
uint256[] memory amounts = UNISWAP_ROUTER.getAmountsOut(amountIn, path); uint256[] memory amounts = UNISWAP_ROUTER.getAmountsOut(finalAmountIn, path);
return amounts[1]; uint256 reserveInDecimals = _getDecimals(reserveIn);
uint256 reserveOutDecimals = _getDecimals(reserveOut);
uint256 outPerInPrice = finalAmountIn
.mul(10**18)
.mul(10**reserveOutDecimals)
.div(amounts[1].mul(10**reserveInDecimals));
return AmountCalc(
amounts[1],
outPerInPrice,
_calcUsdValue(reserveIn, amountIn, reserveInDecimals),
_calcUsdValue(reserveOut, amounts[1], reserveOutDecimals)
);
}
/**
* @dev Returns the minimum input asset amount required to buy the given output asset amount
* @param reserveIn Address of the asset to be swap from
* @param reserveOut Address of the asset to be swap to
* @param amountOut Amount of reserveOut
* @return Struct containing the following information:
* uint256 Amount in of the reserveIn
* uint256 The price of in amount denominated in the reserveOut currency (18 decimals)
* uint256 In amount of reserveIn value denominated in USD (8 decimals)
* uint256 Out amount of reserveOut value denominated in USD (8 decimals)
*/
function _getAmountsIn(address reserveIn, address reserveOut, uint256 amountOut) internal view returns (AmountCalc memory) {
address[] memory path = new address[](2);
path[0] = reserveIn;
path[1] = reserveOut;
uint256[] memory amounts = UNISWAP_ROUTER.getAmountsIn(amountOut, path);
// Subtract flash loan fee
uint256 finalAmountIn = amounts[0].sub(amounts[0].mul(FLASHLOAN_PREMIUM_TOTAL).div(10000));
uint256 reserveInDecimals = _getDecimals(reserveIn);
uint256 reserveOutDecimals = _getDecimals(reserveOut);
uint256 inPerOutPrice = amountOut
.mul(10**18)
.mul(10**reserveInDecimals)
.div(finalAmountIn.mul(10**reserveOutDecimals));
return AmountCalc(
finalAmountIn,
inPerOutPrice,
_calcUsdValue(reserveIn, finalAmountIn, reserveInDecimals),
_calcUsdValue(reserveOut, amountOut, reserveOutDecimals)
);
} }
} }

View File

@ -109,7 +109,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
const outPerInPrice = amountToSwap const outPerInPrice = amountToSwap
.mul(parseEther('1')) .mul(parseEther('1'))
.mul('1000000') .mul('1000000') // usdc 6 decimals
.div(expectedUSDCAmount.mul(parseEther('1'))); .div(expectedUSDCAmount.mul(parseEther('1')));
const lendUsdValue = amountIn const lendUsdValue = amountIn
@ -120,7 +120,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
const usdcUsdValue = expectedUSDCAmount const usdcUsdValue = expectedUSDCAmount
.mul(usdcPrice) .mul(usdcPrice)
.div('1000000') .div('1000000') // usdc 6 decimals
.mul(usdPrice) .mul(usdPrice)
.div(parseEther('1')); .div(parseEther('1'));
@ -144,17 +144,97 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
}); });
}); });
describe('getAmountIn', () => { describe('getAmountsIn', () => {
it('should return the estimated required amountIn for the asset swap', async () => { it('should return the estimated required amountIn for the asset swap', async () => {
const {weth, dai, uniswapLiquiditySwapAdapter} = testEnv; const {weth, dai, uniswapLiquiditySwapAdapter, oracle} = testEnv;
const amountIn = parseEther('1'); const amountIn = parseEther('1');
const amountOut = parseEther('2'); const flashloanPremium = amountIn.mul(9).div(10000);
const amountToSwap = amountIn.sub(flashloanPremium);
const wethPrice = await oracle.getAssetPrice(weth.address);
const daiPrice = await oracle.getAssetPrice(dai.address);
const usdPrice = await oracle.getAssetPrice(USD_ADDRESS);
const amountOut = await convertToCurrencyDecimals(
dai.address,
new BigNumber(amountIn.toString()).div(daiPrice.toString()).toFixed(0)
);
const inPerOutPrice = amountOut
.mul(parseEther('1'))
.mul(parseEther('1'))
.div(amountToSwap.mul(parseEther('1')));
const ethUsdValue = amountToSwap
.mul(wethPrice)
.div(parseEther('1'))
.mul(usdPrice)
.div(parseEther('1'));
const daiUsdValue = amountOut
.mul(daiPrice)
.div(parseEther('1'))
.mul(usdPrice)
.div(parseEther('1'));
await mockUniswapRouter.setAmountIn(amountOut, weth.address, dai.address, amountIn); await mockUniswapRouter.setAmountIn(amountOut, weth.address, dai.address, amountIn);
expect( const result = await uniswapLiquiditySwapAdapter.getAmountsIn(
await uniswapLiquiditySwapAdapter.getAmountIn(amountOut, weth.address, dai.address) amountOut,
).to.be.eq(amountIn); weth.address,
dai.address
);
expect(result['0']).to.be.eq(amountToSwap);
expect(result['1']).to.be.eq(inPerOutPrice);
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 amountOut = await convertToCurrencyDecimals(
usdc.address,
new BigNumber(amountToSwap.toString()).div(usdcPrice.toString()).toFixed(0)
);
const inPerOutPrice = amountOut
.mul(parseEther('1'))
.mul(parseEther('1'))
.div(amountToSwap.mul('1000000')); // usdc 6 decimals
const lendUsdValue = amountToSwap
.mul(lendPrice)
.div(parseEther('1'))
.mul(usdPrice)
.div(parseEther('1'));
const usdcUsdValue = amountOut
.mul(usdcPrice)
.div('1000000') // usdc 6 decimals
.mul(usdPrice)
.div(parseEther('1'));
await mockUniswapRouter.setAmountIn(amountOut, lend.address, usdc.address, amountIn);
const result = await uniswapLiquiditySwapAdapter.getAmountsIn(
amountOut,
lend.address,
usdc.address
);
expect(result['0']).to.be.eq(amountToSwap);
expect(result['1']).to.be.eq(inPerOutPrice);
expect(result['2']).to.be.eq(lendUsdValue);
expect(result['3']).to.be.eq(usdcUsdValue);
}); });
}); });
}); });