Skip to content

Commit

Permalink
TP-1649: Refactor DEX amounts (#1008)
Browse files Browse the repository at this point in the history
  • Loading branch information
keithbro-imx committed Oct 17, 2023
1 parent 8fcd150 commit 6eba542
Show file tree
Hide file tree
Showing 21 changed files with 348 additions and 308 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import {
formatTokenAmount,
} from './test/utils';
import {
addAmount, Router, SecondaryFee, uniswapTokenToTokenInfo,
addAmount, Router, SecondaryFee,
} from './lib';

jest.mock('@ethersproject/providers');
Expand Down Expand Up @@ -272,7 +272,7 @@ describe('getUnsignedSwapTxFromAmountIn', () => {
newAmountFromString('100', USDC_TEST_TOKEN).value,
);

const tokenIn = { ...uniswapTokenToTokenInfo(USDC_TEST_TOKEN), name: undefined, symbol: undefined };
const tokenIn = { ...USDC_TEST_TOKEN, name: undefined, symbol: undefined };

expect(quote.fees).toEqual([
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { JsonRpcProvider } from '@ethersproject/providers';
import { Contract } from '@ethersproject/contracts';
import { BigNumber } from '@ethersproject/bignumber';
import { SecondaryFee, uniswapTokenToTokenInfo } from 'lib';
import { SecondaryFee } from 'lib';
import { ethers } from 'ethers';
import { ERC20__factory } from 'contracts/types';
import { Exchange } from './exchange';
Expand Down Expand Up @@ -193,7 +193,7 @@ describe('getUnsignedSwapTxFromAmountOut', () => {
newAmountFromString('1000', WETH_TEST_TOKEN).value,
);

const tokenIn = { ...uniswapTokenToTokenInfo(USDC_TEST_TOKEN), name: undefined, symbol: undefined };
const tokenIn = { ...USDC_TEST_TOKEN, name: undefined, symbol: undefined };

expect(quote.fees).toEqual([
{
Expand Down
11 changes: 7 additions & 4 deletions packages/internal/dex/sdk/src/exchange.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ import {
getERC20Decimals, isValidNonZeroAddress, newAmount,
} from './lib/utils';
import {
ExchangeModuleConfiguration, SecondaryFee, TokenInfo, TransactionResponse,
ERC20,
ExchangeModuleConfiguration, SecondaryFee, TransactionResponse,
} from './types';
import { getSwap, prepareSwap } from './lib/transactionUtils/swap';
import { ExchangeConfiguration } from './config';
Expand All @@ -36,7 +37,7 @@ export class Exchange {

private chainId: number;

private nativeToken: TokenInfo;
private nativeToken: ERC20;

private secondaryFees: SecondaryFee[];

Expand Down Expand Up @@ -118,12 +119,14 @@ export class Exchange {
this.getSecondaryFees(),
]);

const tokenIn: TokenInfo = {
const tokenIn: ERC20 = {
type: 'erc20',
address: tokenInAddress,
chainId: this.chainId,
decimals: tokenInDecimals,
};
const tokenOut: TokenInfo = {
const tokenOut: ERC20 = {
type: 'erc20',
address: tokenOutAddress,
chainId: this.chainId,
decimals: tokenOutDecimals,
Expand Down
22 changes: 11 additions & 11 deletions packages/internal/dex/sdk/src/lib/fees.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,29 @@ import { BASIS_POINT_PRECISION } from 'constants/router';
import { BigNumber } from 'ethers';
import {
Amount,
Fee, SecondaryFee, TokenInfo, addAmount, newAmount, subtractAmount,
Fee, SecondaryFee, newAmount, ERC20, subtractERC20Amount, addERC20Amount,
} from 'lib';

export class Fees {
private secondaryFees: SecondaryFee[];

private amount: Amount;
private amount: Amount<ERC20>;

constructor(secondaryFees: SecondaryFee[], token: TokenInfo) {
constructor(secondaryFees: SecondaryFee[], token: ERC20) {
this.secondaryFees = secondaryFees;
this.amount = newAmount(BigNumber.from(0), token);
}

addAmount(amount: Amount): void {
this.amount = addAmount(this.amount, amount);
addAmount(amount: Amount<ERC20>): void {
this.amount = addERC20Amount(this.amount, amount);
}

amountWithFeesApplied(): Amount {
return addAmount(this.amount, this.total());
amountWithFeesApplied(): Amount<ERC20> {
return addERC20Amount(this.amount, this.total());
}

amountLessFees(): Amount {
return subtractAmount(this.amount, this.total());
amountLessFees(): Amount<ERC20> {
return subtractERC20Amount(this.amount, this.total());
}

withAmounts(): Fee[] {
Expand All @@ -40,14 +40,14 @@ export class Fees {
});
}

private total(): Amount {
private total(): Amount<ERC20> {
let totalFees = newAmount(BigNumber.from(0), this.amount.token);

for (const fee of this.secondaryFees) {
const feeAmount = this.amount.value
.mul(fee.basisPoints)
.div(BASIS_POINT_PRECISION);
totalFees = addAmount(totalFees, newAmount(feeAmount, this.amount.token));
totalFees = addERC20Amount(totalFees, newAmount(feeAmount, this.amount.token));
}

return totalFees;
Expand Down
27 changes: 15 additions & 12 deletions packages/internal/dex/sdk/src/lib/getQuotesForRoutes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@ import {
formatAmount,
} from '../test/utils';
import { Multicall__factory } from '../contracts/types';
import { newAmount } from './utils';
import { erc20ToUniswapToken, newAmount } from './utils';

jest.mock('@ethersproject/contracts');

const UNISWAP_IMX = erc20ToUniswapToken(IMX_TEST_TOKEN);
const UNISWAP_WETH = erc20ToUniswapToken(WETH_TEST_TOKEN);

const types = [
'uint256', // amountOut/amountIn
'uint160', // sqrtPrice after
Expand Down Expand Up @@ -57,15 +60,15 @@ describe('getQuotesForRoutes', () => {
// Since we will be mocking the multicall, routes doesn't matter,
// as long as the length is correct.
const pool0 = new Pool(
WETH_TEST_TOKEN,
IMX_TEST_TOKEN,
UNISWAP_WETH,
UNISWAP_IMX,
FeeAmount.HIGH,
sqrtPriceAtTick,
1000,
arbitraryTick,
);
dummyRoutes.push(new Route([pool0], WETH_TEST_TOKEN, IMX_TEST_TOKEN));
dummyRoutes.push(new Route([pool0], WETH_TEST_TOKEN, IMX_TEST_TOKEN));
dummyRoutes.push(new Route([pool0], UNISWAP_WETH, UNISWAP_IMX));
dummyRoutes.push(new Route([pool0], UNISWAP_WETH, UNISWAP_IMX));

const amount = newAmount(BigNumber.from('123123'), WETH_TEST_TOKEN);

Expand Down Expand Up @@ -113,14 +116,14 @@ describe('getQuotesForRoutes', () => {
// Since we will be mocking the multicall, routes doesn't matter,
// as long as the length is correct.
const pool0 = new Pool(
WETH_TEST_TOKEN,
IMX_TEST_TOKEN,
UNISWAP_WETH,
UNISWAP_IMX,
FeeAmount.HIGH,
sqrtPriceAtTick,
1000,
arbitraryTick,
);
dummyRoutes.push(new Route([pool0], WETH_TEST_TOKEN, IMX_TEST_TOKEN));
dummyRoutes.push(new Route([pool0], UNISWAP_WETH, UNISWAP_IMX));

const provider = new providers.JsonRpcProvider(
TEST_RPC_URL,
Expand Down Expand Up @@ -191,15 +194,15 @@ describe('getQuotesForRoutes', () => {
// Since we will be mocking the multicall, routes doesn't matter,
// as long as the length is correct.
const pool0 = new Pool(
WETH_TEST_TOKEN,
IMX_TEST_TOKEN,
UNISWAP_WETH,
UNISWAP_IMX,
FeeAmount.HIGH,
sqrtPriceAtTick,
1000,
arbitraryTick,
);
dummyRoutes.push(new Route([pool0], WETH_TEST_TOKEN, IMX_TEST_TOKEN));
dummyRoutes.push(new Route([pool0], WETH_TEST_TOKEN, IMX_TEST_TOKEN));
dummyRoutes.push(new Route([pool0], UNISWAP_WETH, UNISWAP_IMX));
dummyRoutes.push(new Route([pool0], UNISWAP_WETH, UNISWAP_IMX));

const provider = new providers.JsonRpcProvider(
TEST_RPC_URL,
Expand Down
19 changes: 12 additions & 7 deletions packages/internal/dex/sdk/src/lib/getQuotesForRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import { Route, SwapQuoter } from '@uniswap/v3-sdk';
import { TradeType, Token } from '@uniswap/sdk-core';
import { BigNumber, ethers } from 'ethers';
import { ProviderCallError } from 'errors';
import { Amount } from 'lib';
import { Amount, ERC20 } from 'lib';
import { multicallMultipleCallDataSingContract, MulticallResponse } from './multicall';
import { newAmount, quoteReturnMapping, toCurrencyAmount } from './utils';
import {
newAmount, quoteReturnMapping, toCurrencyAmount, uniswapTokenToERC20,
} from './utils';
import { Multicall } from '../contracts/types';

const amountIndex = 0;
Expand All @@ -13,16 +15,16 @@ const gasEstimateIndex = 3;
export type QuoteResult = {
route: Route<Token, Token>;
gasEstimate: ethers.BigNumber
amountIn: Amount;
amountOut: Amount;
amountIn: Amount<ERC20>;
amountOut: Amount<ERC20>;
tradeType: TradeType;
};

export async function getQuotesForRoutes(
multicallContract: Multicall,
quoterContractAddress: string,
routes: Route<Token, Token>[],
amountSpecified: Amount,
amountSpecified: Amount<ERC20>,
tradeType: TradeType,
): Promise<QuoteResult[]> {
const callData = routes.map(
Expand Down Expand Up @@ -71,10 +73,13 @@ export async function getQuotesForRoutes(
const quoteAmount = decodedQuoteResult[amountIndex];
if (!(quoteAmount instanceof BigNumber)) throw new Error('Expected BigNumber');

const input = uniswapTokenToERC20(routes[i].input);
const output = uniswapTokenToERC20(routes[i].output);

decodedQuoteResults.push({
route: routes[i],
amountIn: tradeType === TradeType.EXACT_INPUT ? amountSpecified : newAmount(quoteAmount, routes[i].input),
amountOut: tradeType === TradeType.EXACT_INPUT ? newAmount(quoteAmount, routes[i].output) : amountSpecified,
amountIn: tradeType === TradeType.EXACT_INPUT ? amountSpecified : newAmount(quoteAmount, input),
amountOut: tradeType === TradeType.EXACT_INPUT ? newAmount(quoteAmount, output) : amountSpecified,
gasEstimate: ethers.BigNumber.from(decodedQuoteResult[gasEstimateIndex]),
tradeType,
});
Expand Down
10 changes: 5 additions & 5 deletions packages/internal/dex/sdk/src/lib/poolUtils/fetchValidPools.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Pool } from '@uniswap/v3-sdk';
import { BigNumber } from 'ethers';
import { ProviderCallError } from 'errors';
import { TokenInfo } from 'types';
import { tokenInfoToUniswapToken } from 'lib';
import { erc20ToUniswapToken } from 'lib/utils';
import { ERC20 } from 'types';
import { MulticallResponse, multicallSingleCallDataMultipleContracts } from '../multicall';
import { generatePossiblePoolsFromERC20Pair } from './generatePossiblePoolsFromERC20Pairs';
import { ERC20Pair } from './generateERC20Pairs';
Expand All @@ -28,7 +28,7 @@ const noDataResult = '0x';
export const fetchValidPools = async (
multicallContract: Multicall,
erc20Pair: ERC20Pair,
commonRoutingERC20s: TokenInfo[],
commonRoutingERC20s: ERC20[],
factoryAddress: string,
): Promise<Pool[]> => {
const poolIDs = generatePossiblePoolsFromERC20Pair(
Expand Down Expand Up @@ -94,8 +94,8 @@ export const fetchValidPools = async (
}

const validPool = new Pool(
tokenInfoToUniswapToken(poolID.erc20Pair[0]),
tokenInfoToUniswapToken(poolID.erc20Pair[1]),
erc20ToUniswapToken(poolID.erc20Pair[0]),
erc20ToUniswapToken(poolID.erc20Pair[1]),
poolID.fee,
poolSlot0.sqrtPriceX96.toString(),
poolLiquidity.toString(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { FeeAmount, Pool, TickMath } from '@uniswap/v3-sdk';
import { Token } from '@uniswap/sdk-core';
import { uniswapTokenToERC20 } from 'lib/utils';
import { generateAllAcyclicPaths } from '../router';

const token0 = new Token(
Expand Down Expand Up @@ -72,13 +73,13 @@ describe('generateAllAcyclicPaths', () => {
);

const routes = generateAllAcyclicPaths(
token0,
token3,
uniswapTokenToERC20(token0),
uniswapTokenToERC20(token3),
pools,
maxHops,
[],
[],
token0,
uniswapTokenToERC20(token0),
);

// There are two routes of maxHops = 3 that go from token0 to token3.
Expand Down Expand Up @@ -155,13 +156,13 @@ describe('generateAllAcyclicPaths', () => {
);

const routes = generateAllAcyclicPaths(
token0,
token3,
uniswapTokenToERC20(token0),
uniswapTokenToERC20(token3),
pools,
maxHops,
[],
[],
token0,
uniswapTokenToERC20(token0),
);

// There is one route of maxHops = 2 that goes from token0 to token3.
Expand Down Expand Up @@ -234,13 +235,13 @@ describe('generateAllAcyclicPaths', () => {
);

const routes = generateAllAcyclicPaths(
token0,
token3,
uniswapTokenToERC20(token0),
uniswapTokenToERC20(token3),
pools,
maxHops,
[],
[],
token0,
uniswapTokenToERC20(token0),
);

// There are five route of maxHops = 4 that goes from token0 to token3.
Expand Down
Loading

0 comments on commit 6eba542

Please sign in to comment.