@marko00/routing-finder-mare 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.idea/copilot.data.migration.agent.xml +6 -0
- package/.idea/copilot.data.migration.ask.xml +6 -0
- package/.idea/copilot.data.migration.ask2agent.xml +6 -0
- package/.idea/copilot.data.migration.edit.xml +6 -0
- package/.idea/modules.xml +8 -0
- package/.idea/php.xml +19 -0
- package/.idea/ratex-sdk.iml +8 -0
- package/.idea/vcs.xml +6 -0
- package/LICENSE-MIT +21 -0
- package/README.md +209 -0
- package/contracts/abi/BalancerHelperAbi.ts +1 -0
- package/contracts/abi/CamelotHelperAbi.ts +1 -0
- package/contracts/abi/RateXAbi.ts +482 -0
- package/contracts/abi/SushiSwapHelperAbi.ts +1 -0
- package/contracts/abi/UniswapHelperAbi.ts +1 -0
- package/contracts/abi/UniswapV2HelperAbi.ts +1 -0
- package/contracts/addresses-arbitrum.ts +8 -0
- package/contracts/addresses-mainnet.ts +7 -0
- package/contracts/addresses-polkadot.ts +9 -0
- package/contracts/addresses-sei.ts +9 -0
- package/contracts/rateX/BalancerHelper.ts +13 -0
- package/contracts/rateX/CamelotHelper.ts +13 -0
- package/contracts/rateX/SushiSwapHelper.ts +13 -0
- package/contracts/rateX/UniswapHelper.ts +19 -0
- package/contracts/rateX/UniswapV2Helper.ts +19 -0
- package/dexes/dexIdsList.ts +9 -0
- package/dexes/graph_queries/BalancerV2.ts +150 -0
- package/dexes/graph_queries/CamelotV2.ts +202 -0
- package/dexes/graph_queries/SushiSwapV2.ts +283 -0
- package/dexes/graph_queries/UniswapV2.ts +252 -0
- package/dexes/graph_queries/UniswapV3.ts +229 -0
- package/dexes/graph_queries/graphQueryFilters.ts +41 -0
- package/dexes/graph_queries/x_template.ts +67 -0
- package/dexes/pools/Balancer/BalancerState.ts +34 -0
- package/dexes/pools/Balancer/BalancerWeightedPool.ts +96 -0
- package/dexes/pools/Camelot.ts +164 -0
- package/dexes/pools/SushiSwapV2.ts +35 -0
- package/dexes/pools/UniswapV2.ts +36 -0
- package/dexes/pools/uniswap/UniswapV3.ts +40 -0
- package/dexes/pools/uniswap/testUniswapOffchainQuoter.ts +169 -0
- package/dexes/pools/uniswap/types.ts +174 -0
- package/dexes/pools/uniswap/uniswapOffchainQuoter.ts +173 -0
- package/dexes/pools/uniswap/uniswapState.ts +56 -0
- package/dexes/pools/uniswap/utils.ts +71 -0
- package/dist/contracts/abi/BalancerHelperAbi.d.ts +25 -0
- package/dist/contracts/abi/BalancerHelperAbi.js +4 -0
- package/dist/contracts/abi/CamelotHelperAbi.d.ts +45 -0
- package/dist/contracts/abi/CamelotHelperAbi.js +4 -0
- package/dist/contracts/abi/RateXAbi.d.ts +71 -0
- package/dist/contracts/abi/RateXAbi.js +485 -0
- package/dist/contracts/abi/SushiSwapHelperAbi.d.ts +45 -0
- package/dist/contracts/abi/SushiSwapHelperAbi.js +4 -0
- package/dist/contracts/abi/UniswapHelperAbi.d.ts +39 -0
- package/dist/contracts/abi/UniswapHelperAbi.js +4 -0
- package/dist/contracts/abi/UniswapV2HelperAbi.d.ts +45 -0
- package/dist/contracts/abi/UniswapV2HelperAbi.js +4 -0
- package/dist/contracts/addresses-arbitrum.d.ts +6 -0
- package/dist/contracts/addresses-arbitrum.js +10 -0
- package/dist/contracts/addresses-mainnet.d.ts +6 -0
- package/dist/contracts/addresses-mainnet.js +10 -0
- package/dist/contracts/addresses-polkadot.d.ts +6 -0
- package/dist/contracts/addresses-polkadot.js +10 -0
- package/dist/contracts/addresses-sei.d.ts +6 -0
- package/dist/contracts/addresses-sei.js +10 -0
- package/dist/contracts/rateX/BalancerHelper.d.ts +26 -0
- package/dist/contracts/rateX/BalancerHelper.js +14 -0
- package/dist/contracts/rateX/CamelotHelper.d.ts +46 -0
- package/dist/contracts/rateX/CamelotHelper.js +14 -0
- package/dist/contracts/rateX/SushiSwapHelper.d.ts +46 -0
- package/dist/contracts/rateX/SushiSwapHelper.js +14 -0
- package/dist/contracts/rateX/UniswapHelper.d.ts +40 -0
- package/dist/contracts/rateX/UniswapHelper.js +22 -0
- package/dist/contracts/rateX/UniswapV2Helper.d.ts +46 -0
- package/dist/contracts/rateX/UniswapV2Helper.js +22 -0
- package/dist/dexes/dexIdsList.d.ts +9 -0
- package/dist/dexes/dexIdsList.js +12 -0
- package/dist/dexes/graph_queries/BalancerV2.d.ts +14 -0
- package/dist/dexes/graph_queries/BalancerV2.js +141 -0
- package/dist/dexes/graph_queries/CamelotV2.d.ts +14 -0
- package/dist/dexes/graph_queries/CamelotV2.js +183 -0
- package/dist/dexes/graph_queries/SushiSwapV2.d.ts +14 -0
- package/dist/dexes/graph_queries/SushiSwapV2.js +263 -0
- package/dist/dexes/graph_queries/UniswapV2.d.ts +14 -0
- package/dist/dexes/graph_queries/UniswapV2.js +217 -0
- package/dist/dexes/graph_queries/UniswapV3.d.ts +14 -0
- package/dist/dexes/graph_queries/UniswapV3.js +198 -0
- package/dist/dexes/graph_queries/graphQueryFilters.d.ts +19 -0
- package/dist/dexes/graph_queries/graphQueryFilters.js +40 -0
- package/dist/dexes/graph_queries/x_template.d.ts +12 -0
- package/dist/dexes/graph_queries/x_template.js +57 -0
- package/dist/dexes/pools/Balancer/BalancerState.d.ts +6 -0
- package/dist/dexes/pools/Balancer/BalancerState.js +32 -0
- package/dist/dexes/pools/Balancer/BalancerWeightedPool.d.ts +12 -0
- package/dist/dexes/pools/Balancer/BalancerWeightedPool.js +109 -0
- package/dist/dexes/pools/Camelot.d.ts +12 -0
- package/dist/dexes/pools/Camelot.js +135 -0
- package/dist/dexes/pools/SushiSwapV2.d.ts +9 -0
- package/dist/dexes/pools/SushiSwapV2.js +34 -0
- package/dist/dexes/pools/UniswapV2.d.ts +9 -0
- package/dist/dexes/pools/UniswapV2.js +34 -0
- package/dist/dexes/pools/uniswap/UniswapV3.d.ts +7 -0
- package/dist/dexes/pools/uniswap/UniswapV3.js +36 -0
- package/dist/dexes/pools/uniswap/types.d.ts +76 -0
- package/dist/dexes/pools/uniswap/types.js +111 -0
- package/dist/dexes/pools/uniswap/uniswapOffchainQuoter.d.ts +13 -0
- package/dist/dexes/pools/uniswap/uniswapOffchainQuoter.js +121 -0
- package/dist/dexes/pools/uniswap/uniswapState.d.ts +14 -0
- package/dist/dexes/pools/uniswap/uniswapState.js +51 -0
- package/dist/dexes/pools/uniswap/utils.d.ts +3 -0
- package/dist/dexes/pools/uniswap/utils.js +41 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +41 -0
- package/dist/routes.d.ts +1 -0
- package/dist/routes.js +20 -0
- package/dist/routing/iterative_spliting/main.d.ts +3 -0
- package/dist/routing/iterative_spliting/main.js +104 -0
- package/dist/routing/iterative_spliting/multiHopSwap.d.ts +4 -0
- package/dist/routing/iterative_spliting/multiHopSwap.js +83 -0
- package/dist/routing/main.d.ts +2 -0
- package/dist/routing/main.js +22 -0
- package/dist/routing/uni_like_algo/algo_config.d.ts +2 -0
- package/dist/routing/uni_like_algo/algo_config.js +8 -0
- package/dist/routing/uni_like_algo/amount_distribution.d.ts +2 -0
- package/dist/routing/uni_like_algo/amount_distribution.js +17 -0
- package/dist/routing/uni_like_algo/compute_routes_backtrack.d.ts +3 -0
- package/dist/routing/uni_like_algo/compute_routes_backtrack.js +44 -0
- package/dist/routing/uni_like_algo/main.d.ts +2 -0
- package/dist/routing/uni_like_algo/main.js +49 -0
- package/dist/routing/uni_like_algo/routes_quoter.d.ts +21 -0
- package/dist/routing/uni_like_algo/routes_quoter.js +53 -0
- package/dist/routing/uni_like_algo/swap_finder.d.ts +25 -0
- package/dist/routing/uni_like_algo/swap_finder.js +154 -0
- package/dist/routing/uni_like_algo/types.d.ts +40 -0
- package/dist/routing/uni_like_algo/types.js +12 -0
- package/dist/swap/graph_communication.d.ts +5 -0
- package/dist/swap/graph_communication.js +187 -0
- package/dist/swap/my_local_storage.d.ts +8 -0
- package/dist/swap/my_local_storage.js +16 -0
- package/dist/utils/addresses.d.ts +24 -0
- package/dist/utils/addresses.js +60 -0
- package/dist/utils/math/fixed-points.d.ts +14 -0
- package/dist/utils/math/fixed-points.js +123 -0
- package/dist/utils/math/log-exp.d.ts +5 -0
- package/dist/utils/math/log-exp.js +385 -0
- package/dist/utils/math/math.d.ts +12 -0
- package/dist/utils/math/math.js +50 -0
- package/dist/utils/types/types.d.ts +51 -0
- package/dist/utils/types/types.js +25 -0
- package/dist/utils/utils.d.ts +20 -0
- package/dist/utils/utils.js +72 -0
- package/images/decenter_logo.png +0 -0
- package/index.ts +50 -0
- package/package.json +39 -0
- package/routes.ts +27 -0
- package/routing/iterative_spliting/main.ts +131 -0
- package/routing/iterative_spliting/multiHopSwap.ts +98 -0
- package/routing/main.ts +22 -0
- package/routing/uni_like_algo/algo_config.ts +7 -0
- package/routing/uni_like_algo/amount_distribution.ts +16 -0
- package/routing/uni_like_algo/compute_routes_backtrack.ts +81 -0
- package/routing/uni_like_algo/main.ts +65 -0
- package/routing/uni_like_algo/routes_quoter.ts +63 -0
- package/routing/uni_like_algo/swap_finder.ts +185 -0
- package/routing/uni_like_algo/types.ts +54 -0
- package/swap/graph_communication.ts +212 -0
- package/swap/my_local_storage.ts +27 -0
- package/tsconfig.json +26 -0
- package/utils/addresses.ts +64 -0
- package/utils/math/fixed-points.ts +88 -0
- package/utils/math/log-exp.ts +469 -0
- package/utils/math/math.ts +46 -0
- package/utils/types/types.ts +100 -0
- package/utils/utils.ts +125 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// Ported from Solidity: https://github.com/balancer/balancer-v2-monorepo/blob/master/pkg/pool-weighted/contracts/WeightedMath.sol
|
|
2
|
+
|
|
3
|
+
import { Token, Pool } from '../../../utils/types/types'
|
|
4
|
+
import BigNumber from 'bignumber.js'
|
|
5
|
+
import * as fp from '../../../utils/math/fixed-points'
|
|
6
|
+
import * as math from '../../../utils/math/math'
|
|
7
|
+
|
|
8
|
+
// Swap limits: amounts swapped may not be larger than this percentage of total balance of the token being swapped
|
|
9
|
+
// Example - if the pool has 100 WETH, we can swap a maximum od 30 WETH
|
|
10
|
+
const _MAX_IN_RATIO = new BigNumber(0.3e18)
|
|
11
|
+
|
|
12
|
+
export class BalancerWeightedPool extends Pool {
|
|
13
|
+
reserves: BigNumber[]
|
|
14
|
+
startingReserves: BigNumber[]
|
|
15
|
+
weights: BigNumber[]
|
|
16
|
+
swapFeePercentage: BigNumber
|
|
17
|
+
|
|
18
|
+
constructor(poolId: string, dexId: string, tokens: Token[], reserves: BigInt[], weights: BigInt[], swapFeePercentage: BigInt) {
|
|
19
|
+
super(poolId, dexId, tokens)
|
|
20
|
+
this.reserves = reserves.map((r: BigInt) => new BigNumber(r.toString()))
|
|
21
|
+
this.startingReserves = [...this.reserves]
|
|
22
|
+
this.weights = weights.map((r: BigInt) => new BigNumber(r.toString()))
|
|
23
|
+
this.swapFeePercentage = new BigNumber(swapFeePercentage.toString())
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
reset(): void {
|
|
27
|
+
this.reserves = [...this.startingReserves]
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
calculateExpectedOutputAmount(tokenIn: string, tokenOut: string, amountIn: bigint): bigint {
|
|
31
|
+
return calculateOutputAmount(this, tokenIn, tokenOut, BigNumber(amountIn.toString()))
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
update(tokenIn: string, tokenOut: string, amountIn: bigint, amountOut: bigint): void {
|
|
35
|
+
// CHECK ???
|
|
36
|
+
const i = this.tokens.findIndex((token) => token._address === tokenIn)
|
|
37
|
+
const j = this.tokens.findIndex((token) => token._address === tokenOut)
|
|
38
|
+
|
|
39
|
+
this.reserves[i] = fp.add(this.reserves[i], BigNumber(amountIn.toString()))
|
|
40
|
+
this.reserves[j] = fp.sub(this.reserves[j], BigNumber(amountOut.toString()))
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function calculateOutputAmount(
|
|
45
|
+
pool: BalancerWeightedPool,
|
|
46
|
+
tokenA: string,
|
|
47
|
+
tokenB: string,
|
|
48
|
+
tokenAmountIn: BigNumber,
|
|
49
|
+
swapFeePercentage?: BigNumber
|
|
50
|
+
): bigint {
|
|
51
|
+
// Subtract the fee from the amount in if requested
|
|
52
|
+
if (swapFeePercentage) tokenAmountIn = fp.sub(tokenAmountIn, fp.mulUp(tokenAmountIn, swapFeePercentage))
|
|
53
|
+
|
|
54
|
+
// Get the index of the token we are swapping from and to
|
|
55
|
+
const i = pool.tokens.findIndex((token) => token._address === tokenA)
|
|
56
|
+
const j = pool.tokens.findIndex((token) => token._address === tokenB)
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
const res = _calcOutGivenIn(pool.reserves[i], pool.weights[i], pool.reserves[j], pool.weights[j], tokenAmountIn)
|
|
60
|
+
return BigInt(res.toFixed())
|
|
61
|
+
} catch (e) {
|
|
62
|
+
return BigInt(0)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/* Computes how many tokens can be taken out of a pool if `amountIn` are sent, given the current balances and weights.
|
|
67
|
+
Amount out, so we round down overall:
|
|
68
|
+
**********************************************************************************************
|
|
69
|
+
// outGivenIn //
|
|
70
|
+
// aO = amountOut //
|
|
71
|
+
// bO = balanceOut //
|
|
72
|
+
// bI = balanceIn / / bI \ (wI / wO) \ //
|
|
73
|
+
// aI = amountIn aO = bO * | 1 - | -------------------------- | ^ | //
|
|
74
|
+
// wI = weightIn \ \ ( bI + aI ) / / //
|
|
75
|
+
// wO = weightOut //
|
|
76
|
+
**********************************************************************************************
|
|
77
|
+
The multiplication rounds down, and the subtrahend (power) runds up (so the base rounds up too).
|
|
78
|
+
Because bI / (bI + aI) <= 1, the exponent rounds down. */
|
|
79
|
+
// Ovde puca!!!!!!!!!!!!!!
|
|
80
|
+
function _calcOutGivenIn(
|
|
81
|
+
balanceIn: BigNumber,
|
|
82
|
+
weightIn: BigNumber,
|
|
83
|
+
balanceOut: BigNumber,
|
|
84
|
+
weightOut: BigNumber,
|
|
85
|
+
amountIn: BigNumber
|
|
86
|
+
): BigNumber {
|
|
87
|
+
// Cannot exceed maximum in ratio (30% of tokenIn balance)
|
|
88
|
+
if (amountIn.gte(fp.mulDown(balanceIn, _MAX_IN_RATIO))) throw new Error('MAX_IN_RATIO')
|
|
89
|
+
|
|
90
|
+
const denominator = math.add(balanceIn, amountIn)
|
|
91
|
+
const base = fp.divUp(balanceIn, denominator)
|
|
92
|
+
const exponent = fp.divDown(weightIn, weightOut)
|
|
93
|
+
const power = fp.powUp(base, exponent)
|
|
94
|
+
|
|
95
|
+
return fp.mulDown(balanceOut, fp.complement(power))
|
|
96
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { Pool, Token } from '../../utils/types/types'
|
|
2
|
+
import BigNumber from "bignumber.js"
|
|
3
|
+
|
|
4
|
+
// Camelot V2 pools have 2 tokens in the pool, and a fee
|
|
5
|
+
// Example contracts of one Camelot V2 pool: https://arbiscan.io/address/0xa6c5c7d189fa4eb5af8ba34e63dcdd3a635d433f#readContract
|
|
6
|
+
|
|
7
|
+
const feeDenominator = new BigNumber(100000)
|
|
8
|
+
|
|
9
|
+
export class CamelotPool extends Pool {
|
|
10
|
+
|
|
11
|
+
fees: BigNumber[]
|
|
12
|
+
reserves: BigNumber[]
|
|
13
|
+
startingReserves: BigNumber[]
|
|
14
|
+
stableSwap: boolean
|
|
15
|
+
|
|
16
|
+
constructor(poolId: string, dexId: string, tokens: Token[], reserves: bigint[], fees: bigint[], stableSwap: boolean) {
|
|
17
|
+
super(poolId, dexId, tokens)
|
|
18
|
+
this.reserves = reserves.map((r) => BigNumber(r.toString()))
|
|
19
|
+
this.startingReserves = [...this.reserves]
|
|
20
|
+
this.fees = fees.map((f) => BigNumber(f.toString()))
|
|
21
|
+
this.stableSwap = stableSwap
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
reset(): void {
|
|
25
|
+
this.reserves = [...this.startingReserves]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// function getAmountOut on Camelot V2 pools smart contracts
|
|
29
|
+
calculateExpectedOutputAmount(tokenIn: string, tokenOut: string, amountIn: bigint): bigint {
|
|
30
|
+
// this function will be called by our routing algorithm which uses bigint values
|
|
31
|
+
const amountInBN = new BigNumber(amountIn.toString())
|
|
32
|
+
|
|
33
|
+
if (this.stableSwap)
|
|
34
|
+
return calculateStableSwap(this, tokenIn, tokenOut, amountInBN)
|
|
35
|
+
else
|
|
36
|
+
return calculateRegularSwap(this, tokenIn, tokenOut, amountInBN)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
update(tokenIn: string, tokenOut: string, amountIn: bigint, amountOut: bigint): void {
|
|
40
|
+
const i = this.tokens.findIndex((token) => token._address === tokenIn)
|
|
41
|
+
const j = this.tokens.findIndex((token) => token._address === tokenOut)
|
|
42
|
+
|
|
43
|
+
this.reserves[i] = this.reserves[i].plus(BigNumber(amountIn.toString()))
|
|
44
|
+
this.reserves[j] = this.reserves[j].minus(BigNumber(amountOut.toString()))
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function calculateStableSwap(pool: CamelotPool, tokenIn: string, tokenOut: string, amountIn: BigNumber): bigint {
|
|
49
|
+
|
|
50
|
+
const feePercent = tokenIn.toLowerCase() === pool.tokens[0]._address.toLowerCase() ? pool.fees[0] : pool.fees[1];
|
|
51
|
+
|
|
52
|
+
let reserve0 = pool.reserves[0]
|
|
53
|
+
let reserve1 = pool.reserves[1]
|
|
54
|
+
const precisionMultiplier0 = new BigNumber(10 ** pool.tokens[0].decimals)
|
|
55
|
+
const precisionMultiplier1 = new BigNumber(10 ** pool.tokens[1].decimals)
|
|
56
|
+
|
|
57
|
+
amountIn = amountIn.minus(amountIn.times(feePercent).div(feeDenominator)) // remove fee from amount received
|
|
58
|
+
const xy: BigNumber = _k(pool);
|
|
59
|
+
reserve0 = reserve0.times(1e18).div(precisionMultiplier0)
|
|
60
|
+
reserve1 = reserve1.times(1e18).div(precisionMultiplier1)
|
|
61
|
+
|
|
62
|
+
const [reserveA, reserveB] = tokenIn.toLowerCase() === pool.tokens[0]._address.toLowerCase()
|
|
63
|
+
? [reserve0, reserve1]
|
|
64
|
+
: [reserve1, reserve0];
|
|
65
|
+
|
|
66
|
+
// amountIn = tokenIn == token0 ? amountIn * 1e18 / precisionMultiplier0 : amountIn * 1e18 / precisionMultiplier1;
|
|
67
|
+
amountIn = tokenIn.toLowerCase() === pool.tokens[0]._address.toLowerCase()
|
|
68
|
+
? amountIn.times(1e18).div(precisionMultiplier0)
|
|
69
|
+
: amountIn.times(1e18).div(precisionMultiplier1);
|
|
70
|
+
|
|
71
|
+
// uint y = reserveB - _get_y(amountIn + reserveA, xy, reserveB);
|
|
72
|
+
const y = reserveB.minus(_get_y(amountIn.plus(reserveA), xy, reserveB))
|
|
73
|
+
|
|
74
|
+
// return y * (tokenIn == token0 ? precisionMultiplier1 : precisionMultiplier0) / 1e18;
|
|
75
|
+
let result = tokenIn.toLowerCase() === pool.tokens[0]._address.toLowerCase()
|
|
76
|
+
? y.times(precisionMultiplier1).div(1e18)
|
|
77
|
+
: y.times(precisionMultiplier0).div(1e18)
|
|
78
|
+
|
|
79
|
+
result = floor(result)
|
|
80
|
+
return BigInt(result.toFixed())
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// named the same as in Camelot V2 pools smart contracts
|
|
84
|
+
function _k(pool: CamelotPool): BigNumber {
|
|
85
|
+
|
|
86
|
+
// for simplicity extract values
|
|
87
|
+
let reserve0 = pool.reserves[0]
|
|
88
|
+
let reserve1 = pool.reserves[1]
|
|
89
|
+
|
|
90
|
+
if (pool.stableSwap) {
|
|
91
|
+
const precisionMultiplier0 = new BigNumber(10 ** pool.tokens[0].decimals)
|
|
92
|
+
const precisionMultiplier1 = new BigNumber(10 ** pool.tokens[1].decimals)
|
|
93
|
+
|
|
94
|
+
const x = reserve0.times(1e18).div(precisionMultiplier0)
|
|
95
|
+
const y = reserve1.times(1e18).div(precisionMultiplier1)
|
|
96
|
+
const a = x.times(y).div(1e18)
|
|
97
|
+
const b = x.times(x).div(1e18).plus(y.times(y).div(1e18))
|
|
98
|
+
let res = a.times(b).div(1e18)
|
|
99
|
+
return floor(res) // x3y+y3x >= k
|
|
100
|
+
}
|
|
101
|
+
return reserve0.times(reserve1)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// named the same as in Camelot V2 pools smart contracts
|
|
105
|
+
function _get_y(x0: BigNumber, xy: BigNumber, y: BigNumber): BigNumber {
|
|
106
|
+
|
|
107
|
+
for (let i = 0; i < 255; i++) {
|
|
108
|
+
const y_prev = y
|
|
109
|
+
const k = _f(x0, y)
|
|
110
|
+
if (k.lt(xy)) {
|
|
111
|
+
let dy = xy.minus(k).times(1e18).dividedToIntegerBy(_d(x0, y))
|
|
112
|
+
y = y.plus(dy)
|
|
113
|
+
} else {
|
|
114
|
+
let dy = k.minus(xy).times(1e18).dividedToIntegerBy(_d(x0, y))
|
|
115
|
+
y = y.minus(dy)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const diff = y.minus(y_prev).abs()
|
|
119
|
+
if (diff.lte(1))
|
|
120
|
+
break
|
|
121
|
+
}
|
|
122
|
+
return floor(y)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function _f(x0: BigNumber, y: BigNumber): BigNumber {
|
|
126
|
+
// return x0 * (y * y / 1e18 * y / 1e18) / 1e18 + (x0 * x0 / 1e18 * x0 / 1e18) * y / 1e18;
|
|
127
|
+
const ySquared = y.times(y).dividedToIntegerBy(1e18)
|
|
128
|
+
const x0Squared = x0.times(x0).dividedToIntegerBy(1e18)
|
|
129
|
+
const term1 = x0.times((ySquared).times(y).dividedToIntegerBy(1e18)).dividedToIntegerBy(1e18)
|
|
130
|
+
const term2 = y.times(x0Squared.times(x0).dividedToIntegerBy(1e18)).dividedToIntegerBy(1e18)
|
|
131
|
+
return term1.plus(term2)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function _d(x0: BigNumber, y: BigNumber): BigNumber {
|
|
135
|
+
// return 3 * x0 * (y * y / 1e18) / 1e18 + (x0 * x0 / 1e18 * x0 / 1e18);
|
|
136
|
+
const ySquared = y.times(y).dividedToIntegerBy(1e18)
|
|
137
|
+
const x0Squared = x0.times(x0).dividedToIntegerBy(1e18)
|
|
138
|
+
const term1 = x0.times(ySquared).times(3).dividedToIntegerBy(1e18)
|
|
139
|
+
const term2 = x0Squared.times(x0).dividedToIntegerBy(1e18)
|
|
140
|
+
return term1.plus(term2)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function calculateRegularSwap(pool: CamelotPool, tokenIn: string, tokenOut: string, amountIn: BigNumber): bigint {
|
|
144
|
+
|
|
145
|
+
const feePercent = tokenIn.toLowerCase() === pool.tokens[0]._address.toLowerCase() ? pool.fees[0] : pool.fees[1];
|
|
146
|
+
|
|
147
|
+
const [reserveA, reserveB] = tokenIn.toLowerCase() === pool.tokens[0]._address.toLowerCase()
|
|
148
|
+
? [pool.reserves[0], pool.reserves[1]]
|
|
149
|
+
: [pool.reserves[1], pool.reserves[0]];
|
|
150
|
+
|
|
151
|
+
amountIn = amountIn.times(feeDenominator.minus(feePercent))
|
|
152
|
+
const numerator = amountIn.times(reserveB)
|
|
153
|
+
const denominator = reserveA.times(feeDenominator).plus(amountIn)
|
|
154
|
+
const result = floor(numerator.div(denominator))
|
|
155
|
+
return BigInt(result.toFixed())
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/* Custom floor function because BigNumber library does not implement it
|
|
159
|
+
@param num: the BigNumber number to be floored
|
|
160
|
+
*/
|
|
161
|
+
function floor(num: BigNumber): BigNumber {
|
|
162
|
+
const whole = num.toFixed().toString().split('.')[0]
|
|
163
|
+
return new BigNumber(whole)
|
|
164
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Pool, Token } from '../../utils/types/types'
|
|
2
|
+
|
|
3
|
+
export class SushiSwapV2Pool extends Pool {
|
|
4
|
+
reserves: bigint[]
|
|
5
|
+
startingReserves: bigint[]
|
|
6
|
+
|
|
7
|
+
constructor(poolId: string, dexId: string, tokens: Token[], reserves: bigint[]) {
|
|
8
|
+
super(poolId, dexId, tokens)
|
|
9
|
+
this.reserves = reserves.slice()
|
|
10
|
+
this.startingReserves = [...this.reserves]
|
|
11
|
+
}
|
|
12
|
+
reset(): void {
|
|
13
|
+
this.reserves = [...this.startingReserves]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
calculateExpectedOutputAmount(tokenIn: string, tokenOut: string, amountIn: bigint): bigint {
|
|
17
|
+
let reserveIn: bigint = this.reserves[0]
|
|
18
|
+
let reserveOut: bigint = this.reserves[1]
|
|
19
|
+
if (tokenIn.toLowerCase() === this.tokens[1]._address.toLowerCase()) {
|
|
20
|
+
reserveIn = this.reserves[1]
|
|
21
|
+
reserveOut = this.reserves[0]
|
|
22
|
+
}
|
|
23
|
+
return (amountIn * BigInt(997) * reserveOut) / (reserveIn * BigInt(1000) + amountIn * BigInt(997))
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
update(tokenIn: string, tokenOut: string, amountIn: bigint, amountOut: bigint): void {
|
|
27
|
+
if (tokenIn.toLowerCase() === this.tokens[0]._address.toLowerCase()) {
|
|
28
|
+
this.reserves[0] += amountIn
|
|
29
|
+
this.reserves[1] -= amountOut
|
|
30
|
+
} else {
|
|
31
|
+
this.reserves[1] += amountIn
|
|
32
|
+
this.reserves[0] -= amountOut
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Pool, Token } from '../../utils/types/types'
|
|
2
|
+
|
|
3
|
+
export class UniswapV2Pool extends Pool {
|
|
4
|
+
reserves: bigint[]
|
|
5
|
+
startingReserves: bigint[]
|
|
6
|
+
|
|
7
|
+
constructor(poolId: string, dexId: string, tokens: Token[], reserves: bigint[]) {
|
|
8
|
+
super(poolId, dexId, tokens)
|
|
9
|
+
this.reserves = reserves.slice()
|
|
10
|
+
this.startingReserves = [...this.reserves]
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
reset(): void {
|
|
14
|
+
this.reserves = [...this.startingReserves]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
calculateExpectedOutputAmount(tokenIn: string, tokenOut: string, amountIn: bigint): bigint {
|
|
18
|
+
let reserveIn: bigint = this.reserves[0]
|
|
19
|
+
let reserveOut: bigint = this.reserves[1]
|
|
20
|
+
if (tokenIn.toLowerCase() === this.tokens[1]._address.toLowerCase()) {
|
|
21
|
+
reserveIn = this.reserves[1]
|
|
22
|
+
reserveOut = this.reserves[0]
|
|
23
|
+
}
|
|
24
|
+
return (amountIn * BigInt(997) * reserveOut) / (reserveIn * BigInt(1000) + amountIn * BigInt(997))
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
update(tokenIn: string, tokenOut: string, amountIn: bigint, amountOut: bigint): void {
|
|
28
|
+
if (tokenIn.toLowerCase() === this.tokens[0]._address.toLowerCase()) {
|
|
29
|
+
this.reserves[0] += amountIn
|
|
30
|
+
this.reserves[1] -= amountOut
|
|
31
|
+
} else {
|
|
32
|
+
this.reserves[1] += amountIn
|
|
33
|
+
this.reserves[0] -= amountOut
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Pool, Token } from '../../../utils/types/types'
|
|
2
|
+
import { UniswapState } from './uniswapState'
|
|
3
|
+
import { PoolState } from './types'
|
|
4
|
+
|
|
5
|
+
export class UniswapV3Pool extends Pool {
|
|
6
|
+
public constructor(poolId: string, dexId: string, tokens: Token[]) {
|
|
7
|
+
super(poolId.toLowerCase(), dexId, tokens)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
calculateExpectedOutputAmount(tokenIn: string, tokenOut: string, amountIn: bigint): bigint {
|
|
11
|
+
const poolData: PoolState | undefined = UniswapState.getPoolState(this.poolId)
|
|
12
|
+
if (!poolData) {
|
|
13
|
+
console.log('ERROR: Data for uni v3 pool: ' + this.poolId + ' not found')
|
|
14
|
+
return BigInt(0)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return UniswapState.quoter.quote(poolData, tokenIn, tokenOut, amountIn)[0]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
reset(): void {
|
|
21
|
+
UniswapState.resetPoolState(this.poolId)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
update(tokenIn: string, tokenOut: string, amountIn: bigint) {
|
|
25
|
+
const poolData: PoolState | undefined = UniswapState.getPoolState(this.poolId)
|
|
26
|
+
if (!poolData) {
|
|
27
|
+
console.log('ERROR: Data for uni v3 pool: ' + this.poolId + ' not found')
|
|
28
|
+
return BigInt(0)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// lastQuote will be stored each time we call quote
|
|
32
|
+
const lastQuote = poolData.lastQuote;
|
|
33
|
+
poolData.data.currentLiquidity = lastQuote.newLiquidity;
|
|
34
|
+
poolData.data.currentSqrtPriceX96 = lastQuote.newSqrtPriceX96;
|
|
35
|
+
poolData.data.currentTickIndex = lastQuote.newTickIndex;
|
|
36
|
+
|
|
37
|
+
// we don't need this, because we don't use amountIn anyway
|
|
38
|
+
return BigInt(0);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* If you want to run this file separately, or any other in ts isolated environment
|
|
3
|
+
* ts-node --esm dexes/pools/uniswap/testUniswapOffchainQuoter.ts
|
|
4
|
+
*
|
|
5
|
+
* remove "type": "module" from sdk package.json
|
|
6
|
+
* And remove rpc function in RPC provider because window object is available only in browser
|
|
7
|
+
*
|
|
8
|
+
* Later if you want to run things in browser, add "type": "module" back to sdk package.json
|
|
9
|
+
*
|
|
10
|
+
* This examples assumes that you have localhost hardhat node running with deployed contracts
|
|
11
|
+
* */
|
|
12
|
+
|
|
13
|
+
import { TradeInfo } from './types'
|
|
14
|
+
import { Token } from '../../../types'
|
|
15
|
+
import { addresses } from '../../../utils/addresses'
|
|
16
|
+
import { UniswapState } from './uniswapState'
|
|
17
|
+
import { UniswapV3Pool } from './UniswapV3'
|
|
18
|
+
import Web3 from 'web3'
|
|
19
|
+
import { IQuoterV2_ABI } from '../../../../contracts/abi/common/IQuoterV2_ABI'
|
|
20
|
+
import { initLocalHardhatProvider } from '../../../../providers/RPCProvider'
|
|
21
|
+
|
|
22
|
+
const web3: Web3 = initLocalHardhatProvider()
|
|
23
|
+
|
|
24
|
+
const POOLS = [
|
|
25
|
+
addresses.univ3_wbtc_eth_pool_0_3,
|
|
26
|
+
addresses.gmx_usdc_pool_0_1,
|
|
27
|
+
addresses.uni_weth_pool,
|
|
28
|
+
addresses.weth_link_pool,
|
|
29
|
+
addresses.dai_usdce_pool_0_0_1,
|
|
30
|
+
]
|
|
31
|
+
const quoterContract = new web3.eth.Contract(IQuoterV2_ABI, addresses.uniQuoterV2)
|
|
32
|
+
|
|
33
|
+
async function testQuote() {
|
|
34
|
+
const startTimestamp = Date.now()
|
|
35
|
+
await UniswapState.initializeFreshPoolsData(POOLS, 1, web3)
|
|
36
|
+
const endTimestamp = Date.now()
|
|
37
|
+
|
|
38
|
+
console.log('Time taken for initialization: ', endTimestamp - startTimestamp)
|
|
39
|
+
|
|
40
|
+
const trades: TradeInfo[] = getTestTrades()
|
|
41
|
+
|
|
42
|
+
for (let tradeInfo of trades) {
|
|
43
|
+
let params = {
|
|
44
|
+
tokenIn: tradeInfo.tokenIn,
|
|
45
|
+
tokenOut: tradeInfo.tokenOut,
|
|
46
|
+
amountIn: tradeInfo.amountIn,
|
|
47
|
+
fee: tradeInfo.fee,
|
|
48
|
+
sqrtPriceLimitX96: 0,
|
|
49
|
+
}
|
|
50
|
+
const tokenIn = { _address: tradeInfo.tokenIn } as Token
|
|
51
|
+
const tokenOut = { _address: tradeInfo.tokenOut } as Token
|
|
52
|
+
//@ts-ignore
|
|
53
|
+
let x: any[] = await quoterContract.methods.quoteExactInputSingle(params).call()
|
|
54
|
+
|
|
55
|
+
const uniV3Pool = new UniswapV3Pool(tradeInfo.pool, 'uniswap', [tokenIn, tokenOut])
|
|
56
|
+
|
|
57
|
+
let y = uniV3Pool.calculateExpectedOutputAmount(tradeInfo.tokenIn, tradeInfo.tokenOut, tradeInfo.amountIn)
|
|
58
|
+
|
|
59
|
+
console.log('-------')
|
|
60
|
+
console.log('Uniswap quote: ', x[0], x[2])
|
|
61
|
+
console.log('Offchain quote: ', y)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function getTestTrades(): TradeInfo[] {
|
|
66
|
+
let trades: TradeInfo[] = []
|
|
67
|
+
|
|
68
|
+
trades.push(
|
|
69
|
+
new TradeInfo(
|
|
70
|
+
addresses.univ3_wbtc_eth_pool_0_3,
|
|
71
|
+
addresses.wethToken,
|
|
72
|
+
addresses.wbtcToken,
|
|
73
|
+
BigInt('100000000000000000000'), // 100 WETH
|
|
74
|
+
BigInt(3000)
|
|
75
|
+
)
|
|
76
|
+
)
|
|
77
|
+
trades.push(
|
|
78
|
+
new TradeInfo(
|
|
79
|
+
addresses.gmx_usdc_pool_0_1,
|
|
80
|
+
addresses.gmxToken,
|
|
81
|
+
addresses.usdcToken,
|
|
82
|
+
BigInt('200000000000000000000'), // 200 GMX
|
|
83
|
+
BigInt(10000)
|
|
84
|
+
)
|
|
85
|
+
)
|
|
86
|
+
trades.push(
|
|
87
|
+
new TradeInfo(
|
|
88
|
+
addresses.uni_weth_pool,
|
|
89
|
+
addresses.wethToken,
|
|
90
|
+
addresses.uniToken,
|
|
91
|
+
BigInt('1000000000000000000'), // 1 WETH
|
|
92
|
+
BigInt(3000)
|
|
93
|
+
)
|
|
94
|
+
)
|
|
95
|
+
trades.push(
|
|
96
|
+
new TradeInfo(
|
|
97
|
+
addresses.weth_link_pool,
|
|
98
|
+
addresses.linkToken,
|
|
99
|
+
addresses.wethToken,
|
|
100
|
+
BigInt('3000000000000000000000'), // 3000 LINK
|
|
101
|
+
BigInt(3000)
|
|
102
|
+
)
|
|
103
|
+
)
|
|
104
|
+
trades.push(
|
|
105
|
+
new TradeInfo(
|
|
106
|
+
addresses.dai_usdce_pool_0_0_1,
|
|
107
|
+
addresses.usdceToken,
|
|
108
|
+
addresses.daiToken,
|
|
109
|
+
BigInt('10000000000'), // 10 000 usdc
|
|
110
|
+
BigInt(100)
|
|
111
|
+
)
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
// ZERO FOR ONE
|
|
115
|
+
|
|
116
|
+
trades.push(
|
|
117
|
+
new TradeInfo(
|
|
118
|
+
addresses.univ3_wbtc_eth_pool_0_3,
|
|
119
|
+
addresses.wbtcToken,
|
|
120
|
+
addresses.wethToken,
|
|
121
|
+
BigInt('1000000000'), // 10 WBTC
|
|
122
|
+
BigInt(3000)
|
|
123
|
+
)
|
|
124
|
+
)
|
|
125
|
+
trades.push(
|
|
126
|
+
new TradeInfo(
|
|
127
|
+
addresses.gmx_usdc_pool_0_1,
|
|
128
|
+
addresses.usdcToken,
|
|
129
|
+
addresses.gmxToken,
|
|
130
|
+
BigInt('1000000000'), // 1000 USDC
|
|
131
|
+
BigInt(10000)
|
|
132
|
+
)
|
|
133
|
+
)
|
|
134
|
+
trades.push(
|
|
135
|
+
new TradeInfo(
|
|
136
|
+
addresses.uni_weth_pool,
|
|
137
|
+
addresses.uniToken,
|
|
138
|
+
addresses.wethToken,
|
|
139
|
+
BigInt('1000000000000000000000'), // 1000 UNI
|
|
140
|
+
BigInt(3000)
|
|
141
|
+
)
|
|
142
|
+
)
|
|
143
|
+
trades.push(
|
|
144
|
+
new TradeInfo(
|
|
145
|
+
addresses.weth_link_pool,
|
|
146
|
+
addresses.wethToken,
|
|
147
|
+
addresses.linkToken,
|
|
148
|
+
BigInt('10000000000000000000'), // 10 WETH
|
|
149
|
+
BigInt(3000)
|
|
150
|
+
)
|
|
151
|
+
)
|
|
152
|
+
trades.push(
|
|
153
|
+
new TradeInfo(
|
|
154
|
+
addresses.dai_usdce_pool_0_0_1,
|
|
155
|
+
addresses.daiToken,
|
|
156
|
+
addresses.usdceToken,
|
|
157
|
+
BigInt('1000000000000000000000'), // 1000 DAI
|
|
158
|
+
BigInt(100)
|
|
159
|
+
)
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
return trades
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async function main() {
|
|
166
|
+
await testQuote()
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
main()
|