├── .github └── FUNDING.yml └── algorithm.ts /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [iivanou1208, RAYNingTime] 4 | open_collective: # Replace with a single Open Collective username 5 | ko_fi: # Replace with a single Ko-fi username 6 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 7 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 8 | liberapay: # Replace with a single Liberapay username 9 | issuehunt: # Replace with a single IssueHunt username 10 | otechie: # Replace with a single Otechie username 11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /algorithm.ts: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import BN from "bn.js"; 3 | 4 | type RawPair = { 5 | reserve0: string; 6 | reserve1: string; 7 | fee?: string; 8 | token0?: string; 9 | token1?: string; 10 | }; 11 | 12 | type Pair = { 13 | address: string; 14 | reserve0: BN; 15 | reserve1: BN; 16 | fee?: BN; 17 | token0?: string; 18 | token1?: string; 19 | }; 20 | 21 | let pairs = new Map(); 22 | let cnt = fs.readFileSync("../../_pairs.json", { encoding: "utf-8" }); 23 | let _pairs: { [a: string]: Pair } = JSON.parse(cnt); 24 | 25 | Object.entries(_pairs).forEach(([address, _pair]) => { 26 | let pair: Pair = { 27 | address, 28 | reserve0: new BN(_pair.reserve0), 29 | reserve1: new BN(_pair.reserve1), 30 | }; 31 | if (_pair.fee) pair.fee = new BN(_pair.fee); 32 | 33 | if (_pair.token0) { 34 | pair.token0 = _pair.token0; 35 | pair.token1 = _pair.token1; 36 | } 37 | pairs.set(address, pair); 38 | }); 39 | 40 | 41 | function pairKey(tokenA: string, tokenB: string): string { 42 | return tokenA < tokenB ? tokenA + "," + tokenB : tokenB + "," + tokenA; 43 | } 44 | 45 | let pairMap = new Map(); 46 | pairs.forEach((pair) => { 47 | if (!pair.token0 || !pair.token1) return; 48 | let key = pairKey(pair.token0, pair.token1); 49 | let _pairs = pairMap.get(key); 50 | if (!_pairs) { 51 | _pairs = []; 52 | pairMap.set(key, _pairs); 53 | } 54 | _pairs.push(pair); 55 | }); 56 | 57 | let m: [string, number][] = []; 58 | pairMap.forEach((pairs, key) => { 59 | m.push([key, pairs.length]); 60 | }); 61 | m.sort((a, b) => b[1] - a[1]); 62 | m.forEach((t) => console.log(t.join("=>"))); 63 | 64 | // get number of tokens, connected to token 65 | let t = new Map>(); 66 | pairs.forEach((pair) => { 67 | if (!pair.token0 || !pair.token1) return; 68 | if (!t.has(pair.token0)) t.set(pair.token0, new Set()); 69 | t.get(pair.token0)!.add(pair.token1); 70 | if (!t.has(pair.token1)) t.set(pair.token1, new Set()); 71 | t.get(pair.token1)!.add(pair.token0); 72 | }); 73 | 74 | let c = 0; 75 | t.forEach((v) => (c += +(v.size > 1))); 76 | console.log(t); 77 | console.log(c); 78 | 79 | function getPairs(tokenA: string, tokenB: string): Pair[] { 80 | return pairMap.get(pairKey(tokenA, tokenB) ); 81 | } 82 | 83 | // 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c 84 | // 0x55d398326f99059fF775485246999027B3197955 85 | function isqrt(s: BN): BN { 86 | let x0 = s.divn(2); 87 | if (x0.eqn(0)) return new BN(0); 88 | let x1 = x0.add(s.div(x0)).divn(2); 89 | while (x1.lt(x0)) { 90 | x0 = x1; 91 | x1 = x0.add(s.div(x0)).divn(2); 92 | } 93 | return x0; 94 | } 95 | 96 | function th(a1: BN, b1: BN, a2: BN, b2: BN): BN { 97 | return isqrt(a1.mul(a2).mul(b1).div(b2)).sub(a1); 98 | } 99 | 100 | function getAmountOut(amountIn: BN, reserveIn: BN, reserveOut: BN): BN { 101 | return reserveOut.mul(amountIn).div(reserveIn.add(amountIn)); 102 | } 103 | 104 | function getAmountOutPair(amountIn: BN, tokenIn: string, pair: Pair) { 105 | let { reserve0, reserve1 } = pair; 106 | if (pair.token0 !== tokenIn) { 107 | [reserve0, reserve1] = [reserve1, reserve0]; 108 | } 109 | return getAmountOut(amountIn, reserve0, reserve1); 110 | } 111 | 112 | // function that, given input and output tokens, returns pairs, parts and output amount 113 | function f(amountIn: BN, fromToken: string, toToken: string) { 114 | let pairs = getPairs(fromToken, toToken); 115 | let rev = pairs[0].token0 !== fromToken; 116 | pairs.sort((a, b) => 117 | getAmountOutPair(amountIn, fromToken, b).cmp( 118 | getAmountOutPair(amountIn, fromToken, a) 119 | ) 120 | ); 121 | 122 | let { reserve0: a1, reserve1: b1 } = pairs[0]; 123 | if (rev) { 124 | [a1, b1] = [b1, a1]; 125 | } 126 | let res: { pair: Pair; part: number }[] = []; 127 | for (let i = 1; i < pairs.length; i++) { 128 | let { reserve0: a2, reserve1: b2 } = pairs[i]; 129 | if (rev) { 130 | [a2, b2] = [b2, a2]; 131 | } 132 | let x = th(a1, b1, a2, b2); 133 | if (x.lt(amountIn)) { 134 | res.push({ 135 | pair: pairs[i - 1], 136 | // part is stored as percents 137 | part: x.muln(100).div(amountIn).toNumber(), 138 | }); 139 | a1 = a1.add(a2); 140 | b1 = b1.add(b2); 141 | } 142 | } 143 | return { 144 | amountOut: getAmountOut(amountIn, a1, b1), 145 | res, 146 | }; 147 | } 148 | 149 | 150 | let routeMap = new Map>(); 151 | 152 | type Segment = { parts: { pair: Pair; part: number }[]; outAmount: BN }; 153 | type Route = { segments: Segment[]; outAmount: BN }; 154 | 155 | function getSegment(inAmount: BN, pairs: Pair[]): Segment { 156 | throw Error("not implemented"); 157 | } 158 | 159 | async function getRoute( 160 | inAmount: BN, 161 | inToken: string, 162 | outToken: string 163 | ): Promise { 164 | // after filled, go from outToken to inToken and accumulate path 165 | // we can also accumulate gas here 166 | let m = new Map(); 167 | let p = [inToken]; 168 | // cpu-intense part, here we can controll if new message arrived (recompute route) 169 | while (p.length > 0) { 170 | let token = p.pop(); 171 | // if token is not in m, but in p, it's inToken 172 | let amount = m.get(token)?.seg.outAmount ?? inAmount; 173 | routeMap.get(token).forEach((pairs, nextToken) => { 174 | let seg = getSegment(amount, pairs); 175 | let t = m.get(nextToken); 176 | // to add gas here, check that newGas-prevGas<=toGas(newOut-prevOut) 177 | if (!t || seg.outAmount.gt(t.seg.outAmount)) { 178 | m.set(nextToken, { fromToken: inToken, seg: seg }); 179 | if (nextToken !== outToken) { 180 | p.unshift(nextToken); 181 | } 182 | } 183 | }); 184 | } 185 | let { fromToken, seg } = m.get(outToken)!; 186 | let outAmount = seg.outAmount; 187 | let segments = []; 188 | do { 189 | segments.push(seg); 190 | ({ fromToken, seg } = m.get(fromToken)!); 191 | } while (fromToken !== inToken); 192 | return { segments, outAmount }; 193 | } 194 | --------------------------------------------------------------------------------