@alcorexchange/alcor-swap-sdk 1.0.26 → 1.0.28

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.
@@ -1,5 +1,7 @@
1
+ /// <reference types="node" />
1
2
  import JSBI from "jsbi";
2
3
  import { Currency } from "../currency";
4
+ import { Token } from "../token";
3
5
  import { Fraction } from "./fraction";
4
6
  import { BigintIsh, Rounding } from "../../internalConstants";
5
7
  export declare class CurrencyAmount<T extends Currency> extends Fraction {
@@ -29,4 +31,8 @@ export declare class CurrencyAmount<T extends Currency> extends Fraction {
29
31
  toAsset(...args: any[]): string;
30
32
  toExtendedAsset(...args: any[]): string;
31
33
  toExtendedAssetObject(...args: any[]): object;
34
+ static toJSON<T extends Currency>(amount: CurrencyAmount<T>): object;
35
+ static fromJSON(json: any): CurrencyAmount<Token>;
36
+ static toBuffer<T extends Currency>(amount: CurrencyAmount<T>): object;
37
+ static fromBuffer(buffer: Buffer): CurrencyAmount<Token>;
32
38
  }
@@ -4,8 +4,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.CurrencyAmount = void 0;
7
+ const msgpack_lite_1 = __importDefault(require("msgpack-lite"));
7
8
  const tiny_invariant_1 = __importDefault(require("tiny-invariant"));
8
9
  const jsbi_1 = __importDefault(require("jsbi"));
10
+ const token_1 = require("../token");
9
11
  const fraction_1 = require("./fraction");
10
12
  const big_js_1 = __importDefault(require("big.js"));
11
13
  const toformat_1 = __importDefault(require("toformat"));
@@ -79,5 +81,30 @@ class CurrencyAmount extends fraction_1.Fraction {
79
81
  toExtendedAssetObject(...args) {
80
82
  return { quantity: `${this.toFixed(...args)} ${this.currency.symbol}`, contract: this.currency.contract };
81
83
  }
84
+ static toJSON(amount) {
85
+ return {
86
+ currency: token_1.Token.toJSON(amount.currency),
87
+ numerator: amount.numerator.toString(),
88
+ denominator: amount.denominator.toString(),
89
+ };
90
+ }
91
+ static fromJSON(json) {
92
+ const currency = token_1.Token.fromJSON(json.currency);
93
+ const numerator = jsbi_1.default.BigInt(json.numerator);
94
+ const denominator = jsbi_1.default.BigInt(json.denominator);
95
+ return new CurrencyAmount(currency, numerator, denominator);
96
+ }
97
+ static toBuffer(amount) {
98
+ const json = {
99
+ currency: token_1.Token.toJSON(amount.currency),
100
+ numerator: amount.numerator.toString(),
101
+ denominator: amount.denominator.toString(),
102
+ };
103
+ return msgpack_lite_1.default.encode(json);
104
+ }
105
+ static fromBuffer(buffer) {
106
+ const json = msgpack_lite_1.default.decode(buffer);
107
+ return this.fromJSON(json);
108
+ }
82
109
  }
83
110
  exports.CurrencyAmount = CurrencyAmount;
@@ -1,3 +1,4 @@
1
+ /// <reference types="node" />
1
2
  import { CurrencyAmount, Price } from "./fractions";
2
3
  import { Token } from "./token";
3
4
  import { BigintIsh, FeeAmount } from "../internalConstants";
@@ -30,6 +31,11 @@ export declare class Pool {
30
31
  readonly feeGrowthGlobalAX64: JSBI;
31
32
  readonly feeGrowthGlobalBX64: JSBI;
32
33
  readonly tickDataProvider: TickDataProvider;
34
+ json?: any;
35
+ buffer?: Buffer;
36
+ bufferHash?: string;
37
+ static hashToPoolMap: Map<string, Pool>;
38
+ static idToPoolMap: Map<number, Pool>;
33
39
  private _tokenAPrice?;
34
40
  private _tokenBPrice?;
35
41
  /**
@@ -69,14 +75,14 @@ export declare class Pool {
69
75
  * @param sqrtPriceLimitX64 The Q64.96 sqrt price limit
70
76
  * @returns The output amount and the pool with updated state
71
77
  */
72
- getOutputAmount(inputAmount: CurrencyAmount<Token>, sqrtPriceLimitX64?: JSBI): [CurrencyAmount<Token>, Pool];
78
+ getOutputAmount(inputAmount: CurrencyAmount<Token>, sqrtPriceLimitX64?: JSBI): CurrencyAmount<Token>;
73
79
  /**
74
80
  * Given a desired output amount of a token, return the computed input amount and a pool with state updated after the trade
75
81
  * @param outputAmount the output amount for which to quote the input amount
76
82
  * @param sqrtPriceLimitX64 The Q64.96 sqrt price limit. If zero for one, the price cannot be less than this value after the swap. If one for zero, the price cannot be greater than this value after the swap
77
83
  * @returns The input amount and the pool with updated state
78
84
  */
79
- getInputAmount(outputAmount: CurrencyAmount<Token>, sqrtPriceLimitX64?: JSBI): [CurrencyAmount<Token>, Pool];
85
+ getInputAmount(outputAmount: CurrencyAmount<Token>, sqrtPriceLimitX64?: JSBI): CurrencyAmount<Token>;
80
86
  /**
81
87
  * Executes a swap
82
88
  * @param zeroForOne Whether the amount in is tokenA or tokenB
@@ -89,4 +95,22 @@ export declare class Pool {
89
95
  */
90
96
  private swap;
91
97
  get tickSpacing(): number;
98
+ static toJSON(pool: Pool): object;
99
+ static fromJSON(json: any): Pool;
100
+ /**
101
+ * Converts the pool to a Buffer using msgpack encoding.
102
+ * @param {Pool} pool - The pool instance to convert.
103
+ * @returns {Buffer} The encoded buffer.
104
+ */
105
+ static toBuffer(pool: Pool): Buffer;
106
+ /**
107
+ * Creates a Pool instance from a Buffer or serialized data.
108
+ * @param {Buffer | any} data - The buffer or serialized data.
109
+ * @returns {Pool} The pool instance.
110
+ */
111
+ static fromBuffer(data: Buffer | any): Pool;
112
+ static fromId(id: number): Pool;
113
+ static createHash(buffer: Buffer, pool?: Pool): string;
114
+ static hashEquals(pool: Pool, hash: string): boolean;
115
+ equals(other: Pool): boolean;
92
116
  }
@@ -4,7 +4,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.Pool = void 0;
7
+ const msgpack_lite_1 = __importDefault(require("msgpack-lite"));
8
+ const crypto_1 = __importDefault(require("crypto"));
7
9
  const fractions_1 = require("./fractions");
10
+ const token_1 = require("./token");
8
11
  const internalConstants_1 = require("../internalConstants");
9
12
  const jsbi_1 = __importDefault(require("jsbi"));
10
13
  const tiny_invariant_1 = __importDefault(require("tiny-invariant"));
@@ -93,23 +96,9 @@ class Pool {
93
96
  getOutputAmount(inputAmount, sqrtPriceLimitX64) {
94
97
  (0, tiny_invariant_1.default)(this.involvesToken(inputAmount.currency), "TOKEN");
95
98
  const zeroForOne = inputAmount.currency.equals(this.tokenA);
96
- const { amountCalculated: outputAmount, sqrtPriceX64, liquidity, tickCurrent, } = this.swap(zeroForOne, inputAmount.quotient, sqrtPriceLimitX64);
99
+ const { amountCalculated: outputAmount, } = this.swap(zeroForOne, inputAmount.quotient, sqrtPriceLimitX64);
97
100
  const outputToken = zeroForOne ? this.tokenB : this.tokenA;
98
- return [
99
- fractions_1.CurrencyAmount.fromRawAmount(outputToken, jsbi_1.default.multiply(outputAmount, internalConstants_2.NEGATIVE_ONE)),
100
- new Pool({
101
- id: this.id,
102
- tokenA: this.tokenA,
103
- tokenB: this.tokenB,
104
- fee: this.fee,
105
- sqrtPriceX64,
106
- liquidity,
107
- tickCurrent,
108
- ticks: this.tickDataProvider,
109
- feeGrowthGlobalAX64: this.feeGrowthGlobalAX64,
110
- feeGrowthGlobalBX64: this.feeGrowthGlobalBX64,
111
- })
112
- ];
101
+ return fractions_1.CurrencyAmount.fromRawAmount(outputToken, jsbi_1.default.multiply(outputAmount, internalConstants_2.NEGATIVE_ONE));
113
102
  }
114
103
  /**
115
104
  * Given a desired output amount of a token, return the computed input amount and a pool with state updated after the trade
@@ -119,23 +108,9 @@ class Pool {
119
108
  */
120
109
  getInputAmount(outputAmount, sqrtPriceLimitX64) {
121
110
  const zeroForOne = outputAmount.currency.equals(this.tokenB);
122
- const { amountCalculated: inputAmount, sqrtPriceX64, liquidity, tickCurrent, } = this.swap(zeroForOne, jsbi_1.default.multiply(outputAmount.quotient, internalConstants_2.NEGATIVE_ONE), sqrtPriceLimitX64);
111
+ const { amountCalculated: inputAmount, } = this.swap(zeroForOne, jsbi_1.default.multiply(outputAmount.quotient, internalConstants_2.NEGATIVE_ONE), sqrtPriceLimitX64);
123
112
  const inputToken = zeroForOne ? this.tokenA : this.tokenB;
124
- return [
125
- fractions_1.CurrencyAmount.fromRawAmount(inputToken, inputAmount),
126
- new Pool({
127
- id: this.id,
128
- tokenA: this.tokenA,
129
- tokenB: this.tokenB,
130
- fee: this.fee,
131
- sqrtPriceX64,
132
- liquidity,
133
- tickCurrent,
134
- ticks: this.tickDataProvider,
135
- feeGrowthGlobalAX64: this.feeGrowthGlobalAX64,
136
- feeGrowthGlobalBX64: this.feeGrowthGlobalBX64,
137
- }),
138
- ];
113
+ return fractions_1.CurrencyAmount.fromRawAmount(inputToken, inputAmount);
139
114
  }
140
115
  /**
141
116
  * Executes a swap
@@ -229,5 +204,111 @@ class Pool {
229
204
  get tickSpacing() {
230
205
  return internalConstants_1.TICK_SPACINGS[this.fee];
231
206
  }
207
+ static toJSON(pool) {
208
+ if (pool.json)
209
+ return pool.json;
210
+ pool.json = {
211
+ id: pool.id,
212
+ tokenA: token_1.Token.toJSON(pool.tokenA),
213
+ tokenB: token_1.Token.toJSON(pool.tokenB),
214
+ fee: pool.fee,
215
+ sqrtPriceX64: pool.sqrtPriceX64.toString(),
216
+ liquidity: pool.liquidity.toString(),
217
+ tickCurrent: pool.tickCurrent,
218
+ feeGrowthGlobalAX64: pool.feeGrowthGlobalAX64.toString(),
219
+ feeGrowthGlobalBX64: pool.feeGrowthGlobalBX64.toString(),
220
+ tickDataProvider: tickListDataProvider_1.TickListDataProvider.toJSON(pool.tickDataProvider.ticks)
221
+ };
222
+ return pool.json;
223
+ }
224
+ static fromJSON(json) {
225
+ return new Pool({
226
+ id: json.id,
227
+ tokenA: token_1.Token.fromJSON(json.tokenA),
228
+ tokenB: token_1.Token.fromJSON(json.tokenB),
229
+ fee: json.fee,
230
+ sqrtPriceX64: jsbi_1.default.BigInt(json.sqrtPriceX64),
231
+ liquidity: jsbi_1.default.BigInt(json.liquidity),
232
+ tickCurrent: json.tickCurrent,
233
+ feeGrowthGlobalAX64: jsbi_1.default.BigInt(json.feeGrowthGlobalAX64),
234
+ feeGrowthGlobalBX64: jsbi_1.default.BigInt(json.feeGrowthGlobalBX64),
235
+ ticks: tickListDataProvider_1.TickListDataProvider.fromJSON(json.tickDataProvider)
236
+ });
237
+ }
238
+ /**
239
+ * Converts the pool to a Buffer using msgpack encoding.
240
+ * @param {Pool} pool - The pool instance to convert.
241
+ * @returns {Buffer} The encoded buffer.
242
+ */
243
+ static toBuffer(pool) {
244
+ if (pool.buffer)
245
+ return pool.buffer;
246
+ const json = Pool.toJSON(pool);
247
+ pool.buffer = msgpack_lite_1.default.encode(json);
248
+ pool.bufferHash = Pool.createHash(pool.buffer);
249
+ return pool.buffer;
250
+ }
251
+ /**
252
+ * Creates a Pool instance from a Buffer or serialized data.
253
+ * @param {Buffer | any} data - The buffer or serialized data.
254
+ * @returns {Pool} The pool instance.
255
+ */
256
+ static fromBuffer(data) {
257
+ const bufferHash = Pool.createHash(data instanceof Buffer ? data : data.buffer);
258
+ if (this.hashToPoolMap.has(bufferHash)) {
259
+ return this.hashToPoolMap.get(bufferHash);
260
+ }
261
+ const json = msgpack_lite_1.default.decode(data instanceof Buffer ? data : data.buffer);
262
+ const pool = Pool.fromJSON(json);
263
+ this.hashToPoolMap.set(bufferHash, pool);
264
+ this.idToPoolMap.set(pool.id, pool);
265
+ return pool;
266
+ }
267
+ static fromId(id) {
268
+ //console.log('fromId', id)
269
+ const pool = Pool.idToPoolMap.get(id);
270
+ if (!pool)
271
+ throw new Error('pool does not exist in idToPoolMap');
272
+ return pool;
273
+ }
274
+ static createHash(buffer, pool) {
275
+ if (pool && pool.bufferHash) {
276
+ return pool.bufferHash;
277
+ }
278
+ const hash = crypto_1.default.createHash('sha256');
279
+ hash.update(buffer);
280
+ const hexHash = hash.digest('hex');
281
+ if (pool) {
282
+ pool.bufferHash = hexHash;
283
+ }
284
+ return hexHash;
285
+ }
286
+ static hashEquals(pool, hash) {
287
+ return pool.bufferHash === hash;
288
+ }
289
+ equals(other) {
290
+ // Сравниваем id пулов
291
+ if (this.id !== other.id)
292
+ return false;
293
+ // Сравниваем fee
294
+ if (this.fee !== other.fee)
295
+ return false;
296
+ // Сравниваем sqrtPriceX64
297
+ if (!jsbi_1.default.equal(this.sqrtPriceX64, other.sqrtPriceX64))
298
+ return false;
299
+ // Сравниваем liquidity
300
+ if (!jsbi_1.default.equal(this.liquidity, other.liquidity))
301
+ return false;
302
+ // Сравниваем tickCurrent
303
+ if (this.tickCurrent !== other.tickCurrent)
304
+ return false;
305
+ // Сравниваем токены (предполагается, что у Token есть метод equals)
306
+ if (!this.tokenA.equals(other.tokenA) || !this.tokenB.equals(other.tokenB))
307
+ return false;
308
+ // Если все проверки прошли, объекты считаются равными
309
+ return true;
310
+ }
232
311
  }
233
312
  exports.Pool = Pool;
313
+ Pool.hashToPoolMap = new Map();
314
+ Pool.idToPoolMap = new Map();
@@ -1,3 +1,4 @@
1
+ /// <reference types="node" />
1
2
  import { Currency } from './currency';
2
3
  import { Price } from './fractions';
3
4
  import { Token } from './token';
@@ -24,4 +25,23 @@ export declare class Route<TInput extends Currency, TOutput extends Currency> {
24
25
  * Returns the mid price of the route
25
26
  */
26
27
  get midPrice(): Price<TInput, TOutput>;
28
+ static toJSON(route: Route<Currency, Currency>, lightWeightVersion?: boolean): {
29
+ pools: (number | Buffer)[];
30
+ input: {
31
+ contract: string;
32
+ decimals: number;
33
+ symbol: string;
34
+ };
35
+ output: {
36
+ contract: string;
37
+ decimals: number;
38
+ symbol: string;
39
+ };
40
+ _midPrice: Price<Token, Token> | null;
41
+ };
42
+ static fromJSON(json: any): Route<Token, Token>;
43
+ static toBuffer(route: Route<Currency, Currency>, lightWeightVersion?: boolean): any;
44
+ static fromBuffer(buffer: Buffer): Route<Token, Token>;
45
+ static toBufferAdvanced(route: Route<Currency, Currency>, pools: any[]): any;
46
+ equals(other: Route<Currency, Currency>): boolean;
27
47
  }
@@ -4,8 +4,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.Route = void 0;
7
+ const msgpack_lite_1 = __importDefault(require("msgpack-lite"));
7
8
  const tiny_invariant_1 = __importDefault(require("tiny-invariant"));
8
9
  const fractions_1 = require("./fractions");
10
+ const token_1 = require("./token");
11
+ const pool_1 = require("./pool");
9
12
  /**
10
13
  * Represents a list of pools through which a swap can occur
11
14
  * @template TInput The input token
@@ -30,8 +33,11 @@ class Route {
30
33
  const tokenPath = [wrappedInput];
31
34
  for (const [i, pool] of pools.entries()) {
32
35
  const currentInputToken = tokenPath[i];
33
- (0, tiny_invariant_1.default)(currentInputToken.equals(pool.tokenA) || currentInputToken.equals(pool.tokenB), 'PATH');
34
- const nextToken = currentInputToken.equals(pool.tokenA) ? pool.tokenB : pool.tokenA;
36
+ (0, tiny_invariant_1.default)(currentInputToken.equals(pool.tokenA) ||
37
+ currentInputToken.equals(pool.tokenB), 'PATH');
38
+ const nextToken = currentInputToken.equals(pool.tokenA)
39
+ ? pool.tokenB
40
+ : pool.tokenA;
35
41
  tokenPath.push(nextToken);
36
42
  }
37
43
  this.pools = pools;
@@ -49,22 +55,83 @@ class Route {
49
55
  return nextInput.equals(pool.tokenA)
50
56
  ? {
51
57
  nextInput: pool.tokenB,
52
- price: price.multiply(pool.tokenAPrice)
58
+ price: price.multiply(pool.tokenAPrice),
53
59
  }
54
60
  : {
55
61
  nextInput: pool.tokenA,
56
- price: price.multiply(pool.tokenBPrice)
62
+ price: price.multiply(pool.tokenBPrice),
57
63
  };
58
64
  }, this.pools[0].tokenA.equals(this.input)
59
65
  ? {
60
66
  nextInput: this.pools[0].tokenB,
61
- price: this.pools[0].tokenAPrice
67
+ price: this.pools[0].tokenAPrice,
62
68
  }
63
69
  : {
64
70
  nextInput: this.pools[0].tokenA,
65
- price: this.pools[0].tokenBPrice
71
+ price: this.pools[0].tokenBPrice,
66
72
  }).price;
67
73
  return (this._midPrice = new fractions_1.Price(this.input, this.output, price.denominator, price.numerator));
68
74
  }
75
+ static toJSON(route, lightWeightVersion = false) {
76
+ return {
77
+ pools: route.pools.map((pool) => {
78
+ if (lightWeightVersion) {
79
+ return pool.id;
80
+ }
81
+ else {
82
+ return pool_1.Pool.toBuffer(pool);
83
+ }
84
+ }),
85
+ input: token_1.Token.toJSON(route.input),
86
+ output: token_1.Token.toJSON(route.output),
87
+ _midPrice: route._midPrice,
88
+ };
89
+ }
90
+ static fromJSON(json) {
91
+ const pools = json.pools.map((pool) => {
92
+ if (typeof pool === 'number') {
93
+ return pool_1.Pool.fromId(pool);
94
+ }
95
+ else {
96
+ return pool_1.Pool.fromBuffer(Buffer.from(pool));
97
+ }
98
+ });
99
+ const input = token_1.Token.fromJSON(json.input);
100
+ const output = token_1.Token.fromJSON(json.output);
101
+ return new Route(pools, input, output);
102
+ }
103
+ static toBuffer(route, lightWeightVersion = false) {
104
+ const json = this.toJSON(route, lightWeightVersion);
105
+ return msgpack_lite_1.default.encode(json);
106
+ }
107
+ static fromBuffer(buffer) {
108
+ const json = msgpack_lite_1.default.decode(buffer);
109
+ return this.fromJSON(json);
110
+ }
111
+ static toBufferAdvanced(route, pools) {
112
+ const json = {
113
+ pools: pools.map((pool) => {
114
+ if (typeof pool === 'number' || Buffer.isBuffer(pool)) {
115
+ return pool;
116
+ }
117
+ else {
118
+ return pool_1.Pool.toBuffer(pool);
119
+ }
120
+ }),
121
+ input: token_1.Token.toJSON(route.input),
122
+ output: token_1.Token.toJSON(route.output),
123
+ _midPrice: route._midPrice,
124
+ };
125
+ return msgpack_lite_1.default.encode(json);
126
+ }
127
+ equals(other) {
128
+ if (this.pools.length !== other.pools.length)
129
+ return false;
130
+ for (let i = 0; i < this.pools.length; i++) {
131
+ if (!this.pools[i].equals(other.pools[i]))
132
+ return false;
133
+ }
134
+ return this.input.equals(other.input) && this.output.equals(other.output);
135
+ }
69
136
  }
70
137
  exports.Route = Route;
@@ -20,4 +20,6 @@ export declare class Tick {
20
20
  readonly secondsOutside: JSBI;
21
21
  readonly secondsPerLiquidityOutsideX64: JSBI;
22
22
  constructor({ id, liquidityGross, liquidityNet, feeGrowthOutsideAX64, feeGrowthOutsideBX64, tickCumulativeOutside, secondsOutside, secondsPerLiquidityOutsideX64, }: TickConstructorArgs);
23
+ static toJSON(tick: Tick): object;
24
+ static fromJSON(json: any): Tick;
23
25
  }
@@ -19,5 +19,29 @@ class Tick {
19
19
  this.secondsOutside = jsbi_1.default.BigInt(secondsOutside);
20
20
  this.secondsPerLiquidityOutsideX64 = jsbi_1.default.BigInt(secondsPerLiquidityOutsideX64);
21
21
  }
22
+ static toJSON(tick) {
23
+ return {
24
+ id: tick.id,
25
+ liquidityGross: tick.liquidityGross.toString(),
26
+ liquidityNet: tick.liquidityNet.toString(),
27
+ feeGrowthOutsideAX64: tick.feeGrowthOutsideAX64.toString(),
28
+ feeGrowthOutsideBX64: tick.feeGrowthOutsideBX64.toString(),
29
+ tickCumulativeOutside: tick.tickCumulativeOutside.toString(),
30
+ secondsOutside: tick.secondsOutside.toString(),
31
+ secondsPerLiquidityOutsideX64: tick.secondsPerLiquidityOutsideX64.toString(),
32
+ };
33
+ }
34
+ static fromJSON(json) {
35
+ return new Tick({
36
+ id: json.id,
37
+ liquidityGross: jsbi_1.default.BigInt(json.liquidityGross),
38
+ liquidityNet: jsbi_1.default.BigInt(json.liquidityNet),
39
+ feeGrowthOutsideAX64: jsbi_1.default.BigInt(json.feeGrowthOutsideAX64),
40
+ feeGrowthOutsideBX64: jsbi_1.default.BigInt(json.feeGrowthOutsideBX64),
41
+ tickCumulativeOutside: jsbi_1.default.BigInt(json.tickCumulativeOutside),
42
+ secondsOutside: jsbi_1.default.BigInt(json.secondsOutside),
43
+ secondsPerLiquidityOutsideX64: jsbi_1.default.BigInt(json.secondsPerLiquidityOutsideX64),
44
+ });
45
+ }
22
46
  }
23
47
  exports.Tick = Tick;
@@ -4,8 +4,10 @@ import { TickDataProvider } from "./tickDataProvider";
4
4
  * A data provider for ticks that is backed by an in-memory array of ticks.
5
5
  */
6
6
  export declare class TickListDataProvider implements TickDataProvider {
7
- private ticks;
7
+ ticks: readonly Tick[];
8
8
  constructor(ticks: (Tick | TickConstructorArgs)[], tickSpacing: number);
9
9
  getTick(tick: number): Tick;
10
10
  nextInitializedTickWithinOneWord(tick: number, lte: boolean, tickSpacing: number): [number, boolean];
11
+ static toJSON(ticks: Tick[]): object;
12
+ static fromJSON(ticksArray: any): TickListDataProvider;
11
13
  }
@@ -18,5 +18,11 @@ class TickListDataProvider {
18
18
  nextInitializedTickWithinOneWord(tick, lte, tickSpacing) {
19
19
  return tickList_1.TickList.nextInitializedTickWithinOneWord(this.ticks, tick, lte, tickSpacing);
20
20
  }
21
+ static toJSON(ticks) {
22
+ return ticks.map((tick) => tick_1.Tick.toJSON(tick));
23
+ }
24
+ static fromJSON(ticksArray) {
25
+ return ticksArray.map(tick_1.Tick.fromJSON);
26
+ }
21
27
  }
22
28
  exports.TickListDataProvider = TickListDataProvider;
@@ -21,4 +21,10 @@ export declare class Token extends BaseCurrency {
21
21
  * @throws if the tokens have the same contract and symbol
22
22
  */
23
23
  sortsBefore(other: Token): boolean;
24
+ static toJSON(token: Token): {
25
+ contract: string;
26
+ decimals: number;
27
+ symbol: string;
28
+ };
29
+ static fromJSON(json: any): Token;
24
30
  }
@@ -48,5 +48,15 @@ class Token extends baseCurrency_1.BaseCurrency {
48
48
  return (0, eos_common_1.name)(this.contract).raw().lt((0, eos_common_1.name)(other.contract).raw());
49
49
  }
50
50
  }
51
+ static toJSON(token) {
52
+ return {
53
+ contract: token.contract,
54
+ decimals: token.decimals,
55
+ symbol: token.symbol,
56
+ };
57
+ }
58
+ static fromJSON(json) {
59
+ return new Token(json.contract, json.decimals, json.symbol);
60
+ }
51
61
  }
52
62
  exports.Token = Token;
@@ -164,7 +164,7 @@ class Trade {
164
164
  amounts[0] = amount;
165
165
  for (let i = 0; i < route.tokenPath.length - 1; i++) {
166
166
  const pool = route.pools[i];
167
- const [outputAmount] = pool.getOutputAmount(amounts[i]);
167
+ const outputAmount = pool.getOutputAmount(amounts[i]);
168
168
  amounts[i + 1] = outputAmount;
169
169
  }
170
170
  inputAmount = fractions_1.CurrencyAmount.fromFractionalAmount(route.input, amount.numerator, amount.denominator);
@@ -175,7 +175,7 @@ class Trade {
175
175
  amounts[amounts.length - 1] = amount;
176
176
  for (let i = route.tokenPath.length - 1; i > 0; i--) {
177
177
  const pool = route.pools[i - 1];
178
- const [inputAmount] = pool.getInputAmount(amounts[i]);
178
+ const inputAmount = pool.getInputAmount(amounts[i]);
179
179
  amounts[i - 1] = inputAmount;
180
180
  }
181
181
  inputAmount = fractions_1.CurrencyAmount.fromFractionalAmount(route.input, amounts[0].numerator, amounts[0].denominator);
@@ -208,7 +208,7 @@ class Trade {
208
208
  amounts[0] = fractions_1.CurrencyAmount.fromFractionalAmount(route.input, amount.numerator, amount.denominator);
209
209
  for (let i = 0; i < route.tokenPath.length - 1; i++) {
210
210
  const pool = route.pools[i];
211
- const [outputAmount] = pool.getOutputAmount(amounts[i]);
211
+ const outputAmount = pool.getOutputAmount(amounts[i]);
212
212
  amounts[i + 1] = outputAmount;
213
213
  }
214
214
  outputAmount = fractions_1.CurrencyAmount.fromFractionalAmount(route.output, amounts[amounts.length - 1].numerator, amounts[amounts.length - 1].denominator);
@@ -219,7 +219,7 @@ class Trade {
219
219
  amounts[amounts.length - 1] = fractions_1.CurrencyAmount.fromFractionalAmount(route.output, amount.numerator, amount.denominator);
220
220
  for (let i = route.tokenPath.length - 1; i > 0; i--) {
221
221
  const pool = route.pools[i - 1];
222
- const [inputAmount] = pool.getInputAmount(amounts[i]);
222
+ const inputAmount = pool.getInputAmount(amounts[i]);
223
223
  amounts[i - 1] = inputAmount;
224
224
  }
225
225
  inputAmount = fractions_1.CurrencyAmount.fromFractionalAmount(route.input, amounts[0].numerator, amounts[0].denominator);
@@ -1,2 +1,5 @@
1
1
  import { Token, Pool, Route } from '../entities';
2
2
  export declare function computeAllRoutes(tokenIn: Token, tokenOut: Token, pools: Pool[], maxHops: number): Route<Token, Token>[];
3
+ export declare function computeAllRoutesFromMap(tokenIn: Token, tokenOut: Token, poolMap: {
4
+ [tokenId: string]: Pool[];
5
+ }, maxHops: number): Route<Token, Token>[];
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.computeAllRoutes = void 0;
3
+ exports.computeAllRoutesFromMap = exports.computeAllRoutes = void 0;
4
4
  const entities_1 = require("../entities");
5
5
  function computeAllRoutes(tokenIn, tokenOut, pools, maxHops) {
6
6
  const poolsUsed = Array(pools.length).fill(false);
@@ -37,3 +37,34 @@ function computeAllRoutes(tokenIn, tokenOut, pools, maxHops) {
37
37
  return routes;
38
38
  }
39
39
  exports.computeAllRoutes = computeAllRoutes;
40
+ function computeAllRoutesFromMap(tokenIn, tokenOut, poolMap, maxHops) {
41
+ const routes = [];
42
+ const computeRoutes = (tokenIn, tokenOut, currentRoute, visitedPools, _previousTokenOut) => {
43
+ if (currentRoute.length > maxHops) {
44
+ return;
45
+ }
46
+ if (currentRoute.length > 0 &&
47
+ currentRoute[currentRoute.length - 1].involvesToken(tokenOut)) {
48
+ routes.push(new entities_1.Route([...currentRoute], tokenIn, tokenOut));
49
+ return;
50
+ }
51
+ const previousTokenOut = _previousTokenOut ? _previousTokenOut : tokenIn;
52
+ const relevantPools = poolMap[previousTokenOut.id] || [];
53
+ for (const curPool of relevantPools) {
54
+ if (visitedPools.has(curPool)) {
55
+ continue;
56
+ }
57
+ const currentTokenOut = curPool.tokenA.equals(previousTokenOut)
58
+ ? curPool.tokenB
59
+ : curPool.tokenA;
60
+ currentRoute.push(curPool);
61
+ visitedPools.add(curPool);
62
+ computeRoutes(tokenIn, tokenOut, currentRoute, visitedPools, currentTokenOut);
63
+ visitedPools.delete(curPool);
64
+ currentRoute.pop();
65
+ }
66
+ };
67
+ computeRoutes(tokenIn, tokenOut, [], new Set());
68
+ return routes;
69
+ }
70
+ exports.computeAllRoutesFromMap = computeAllRoutesFromMap;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alcorexchange/alcor-swap-sdk",
3
- "version": "1.0.26",
3
+ "version": "1.0.28",
4
4
  "description": "",
5
5
  "main": "build/index.js",
6
6
  "scripts": {
@@ -55,6 +55,7 @@
55
55
  "eosjs": "^22.1.0",
56
56
  "eosjs-account-name": "2.3.0",
57
57
  "jsbi": "^4.3.0",
58
+ "msgpack-lite": "^0.1.26",
58
59
  "node-fetch": "2",
59
60
  "tiny-invariant": "^1.1.0",
60
61
  "tiny-warning": "^1.0.3",
@@ -1,3 +1,5 @@
1
+ import msgpack from "msgpack-lite"
2
+
1
3
  import invariant from "tiny-invariant";
2
4
  import JSBI from "jsbi";
3
5
  import { Currency } from "../currency";
@@ -130,4 +132,34 @@ export class CurrencyAmount<T extends Currency> extends Fraction {
130
132
  public toExtendedAssetObject(...args): object {
131
133
  return { quantity: `${this.toFixed(...args)} ${this.currency.symbol}`, contract: this.currency.contract }
132
134
  }
135
+
136
+ static toJSON<T extends Currency>(amount: CurrencyAmount<T>): object {
137
+ return {
138
+ currency: Token.toJSON(amount.currency),
139
+ numerator: amount.numerator.toString(),
140
+ denominator: amount.denominator.toString(),
141
+ };
142
+ }
143
+
144
+ static fromJSON(json: any) {
145
+ const currency = Token.fromJSON(json.currency);
146
+ const numerator = JSBI.BigInt(json.numerator);
147
+ const denominator = JSBI.BigInt(json.denominator);
148
+ return new CurrencyAmount(currency, numerator, denominator);
149
+ }
150
+
151
+ static toBuffer<T extends Currency>(amount: CurrencyAmount<T>): object {
152
+ const json = {
153
+ currency: Token.toJSON(amount.currency),
154
+ numerator: amount.numerator.toString(),
155
+ denominator: amount.denominator.toString(),
156
+ }
157
+
158
+ return msgpack.encode(json);
159
+ }
160
+
161
+ static fromBuffer(buffer: Buffer) {
162
+ const json = msgpack.decode(buffer)
163
+ return this.fromJSON(json)
164
+ }
133
165
  }
@@ -1,3 +1,6 @@
1
+ import msgpack from "msgpack-lite";
2
+ import crypto, {createHash} from 'crypto';
3
+
1
4
  import { CurrencyAmount, Price } from "./fractions";
2
5
  import { Token } from "./token";
3
6
  import { BigintIsh, FeeAmount, TICK_SPACINGS } from "../internalConstants";
@@ -57,6 +60,14 @@ export class Pool {
57
60
  public readonly feeGrowthGlobalBX64: JSBI;
58
61
  public readonly tickDataProvider: TickDataProvider;
59
62
 
63
+ public json?: any
64
+ public buffer?: Buffer
65
+ public bufferHash?: string
66
+
67
+ static hashToPoolMap: Map<string, Pool> = new Map()
68
+ public static idToPoolMap: Map<number, Pool> = new Map()
69
+
70
+
60
71
  private _tokenAPrice?: Price<Token, Token>;
61
72
  private _tokenBPrice?: Price<Token, Token>;
62
73
 
@@ -170,36 +181,19 @@ export class Pool {
170
181
  public getOutputAmount(
171
182
  inputAmount: CurrencyAmount<Token>,
172
183
  sqrtPriceLimitX64?: JSBI
173
- ): [CurrencyAmount<Token>, Pool] {
184
+ ): CurrencyAmount<Token> {
174
185
  invariant(this.involvesToken(inputAmount.currency), "TOKEN");
175
186
 
176
187
  const zeroForOne = inputAmount.currency.equals(this.tokenA);
177
188
 
178
189
  const {
179
190
  amountCalculated: outputAmount,
180
- sqrtPriceX64,
181
- liquidity,
182
- tickCurrent,
183
191
  } = this.swap(zeroForOne, inputAmount.quotient, sqrtPriceLimitX64);
184
192
  const outputToken = zeroForOne ? this.tokenB : this.tokenA;
185
- return [
186
- CurrencyAmount.fromRawAmount(
193
+ return CurrencyAmount.fromRawAmount(
187
194
  outputToken,
188
195
  JSBI.multiply(outputAmount, NEGATIVE_ONE)
189
- ),
190
- new Pool({
191
- id: this.id,
192
- tokenA: this.tokenA,
193
- tokenB: this.tokenB,
194
- fee: this.fee,
195
- sqrtPriceX64,
196
- liquidity,
197
- tickCurrent,
198
- ticks: this.tickDataProvider,
199
- feeGrowthGlobalAX64: this.feeGrowthGlobalAX64,
200
- feeGrowthGlobalBX64: this.feeGrowthGlobalBX64,
201
- })
202
- ];
196
+ )
203
197
  }
204
198
 
205
199
  /**
@@ -211,35 +205,18 @@ export class Pool {
211
205
  public getInputAmount(
212
206
  outputAmount: CurrencyAmount<Token>,
213
207
  sqrtPriceLimitX64?: JSBI
214
- ): [CurrencyAmount<Token>, Pool] {
208
+ ): CurrencyAmount<Token> {
215
209
  const zeroForOne = outputAmount.currency.equals(this.tokenB);
216
210
 
217
211
  const {
218
212
  amountCalculated: inputAmount,
219
- sqrtPriceX64,
220
- liquidity,
221
- tickCurrent,
222
213
  } = this.swap(
223
214
  zeroForOne,
224
215
  JSBI.multiply(outputAmount.quotient, NEGATIVE_ONE),
225
216
  sqrtPriceLimitX64
226
217
  );
227
218
  const inputToken = zeroForOne ? this.tokenA : this.tokenB;
228
- return [
229
- CurrencyAmount.fromRawAmount(inputToken, inputAmount),
230
- new Pool({
231
- id: this.id,
232
- tokenA: this.tokenA,
233
- tokenB: this.tokenB,
234
- fee: this.fee,
235
- sqrtPriceX64,
236
- liquidity,
237
- tickCurrent,
238
- ticks: this.tickDataProvider,
239
- feeGrowthGlobalAX64: this.feeGrowthGlobalAX64,
240
- feeGrowthGlobalBX64: this.feeGrowthGlobalBX64,
241
- }),
242
- ];
219
+ return CurrencyAmount.fromRawAmount(inputToken, inputAmount)
243
220
  }
244
221
 
245
222
  /**
@@ -396,4 +373,118 @@ export class Pool {
396
373
  public get tickSpacing(): number {
397
374
  return TICK_SPACINGS[this.fee];
398
375
  }
376
+
377
+ static toJSON(pool: Pool): object {
378
+ if (pool.json) return pool.json
379
+ pool.json = {
380
+ id: pool.id,
381
+ tokenA: Token.toJSON(pool.tokenA),
382
+ tokenB: Token.toJSON(pool.tokenB),
383
+ fee: pool.fee,
384
+ sqrtPriceX64: pool.sqrtPriceX64.toString(),
385
+ liquidity: pool.liquidity.toString(),
386
+ tickCurrent: pool.tickCurrent,
387
+ feeGrowthGlobalAX64: pool.feeGrowthGlobalAX64.toString(),
388
+ feeGrowthGlobalBX64: pool.feeGrowthGlobalBX64.toString(),
389
+ tickDataProvider: TickListDataProvider.toJSON((pool.tickDataProvider as TickListDataProvider).ticks as Tick[])
390
+ }
391
+
392
+ return pool.json
393
+ }
394
+
395
+ static fromJSON(json: any): Pool {
396
+ return new Pool({
397
+ id: json.id,
398
+ tokenA: Token.fromJSON(json.tokenA),
399
+ tokenB: Token.fromJSON(json.tokenB),
400
+ fee: json.fee,
401
+ sqrtPriceX64: JSBI.BigInt(json.sqrtPriceX64),
402
+ liquidity: JSBI.BigInt(json.liquidity),
403
+ tickCurrent: json.tickCurrent,
404
+ feeGrowthGlobalAX64: JSBI.BigInt(json.feeGrowthGlobalAX64),
405
+ feeGrowthGlobalBX64: JSBI.BigInt(json.feeGrowthGlobalBX64),
406
+ ticks: TickListDataProvider.fromJSON(json.tickDataProvider)
407
+ });
408
+ }
409
+ /**
410
+ * Converts the pool to a Buffer using msgpack encoding.
411
+ * @param {Pool} pool - The pool instance to convert.
412
+ * @returns {Buffer} The encoded buffer.
413
+ */
414
+ static toBuffer(pool: Pool): Buffer {
415
+ if (pool.buffer) return pool.buffer;
416
+
417
+ const json = Pool.toJSON(pool);
418
+ pool.buffer = msgpack.encode(json);
419
+ pool.bufferHash = Pool.createHash(pool.buffer as Buffer);
420
+
421
+ return pool.buffer as Buffer;
422
+ }
423
+
424
+ /**
425
+ * Creates a Pool instance from a Buffer or serialized data.
426
+ * @param {Buffer | any} data - The buffer or serialized data.
427
+ * @returns {Pool} The pool instance.
428
+ */
429
+ static fromBuffer(data: Buffer | any): Pool {
430
+ const bufferHash = Pool.createHash(data instanceof Buffer ? data : data.buffer);
431
+
432
+ if (this.hashToPoolMap.has(bufferHash)) {
433
+ return <Pool>this.hashToPoolMap.get(bufferHash);
434
+ }
435
+
436
+ const json = msgpack.decode(data instanceof Buffer ? data : data.buffer);
437
+ const pool = Pool.fromJSON(json);
438
+
439
+ this.hashToPoolMap.set(bufferHash, pool);
440
+ this.idToPoolMap.set(pool.id, pool);
441
+
442
+ return pool;
443
+ }
444
+
445
+ static fromId(id: number): Pool {
446
+ //console.log('fromId', id)
447
+ const pool = Pool.idToPoolMap.get(id)
448
+ if (!pool) throw new Error('pool does not exist in idToPoolMap')
449
+ return pool;
450
+ }
451
+
452
+ static createHash(buffer: Buffer, pool?: Pool) {
453
+ if (pool && pool.bufferHash) {
454
+ return pool.bufferHash
455
+ }
456
+ const hash = crypto.createHash('sha256');
457
+ hash.update(buffer);
458
+ const hexHash = hash.digest('hex');
459
+
460
+ if (pool) {
461
+ pool.bufferHash = hexHash
462
+ }
463
+ return hexHash
464
+ }
465
+ static hashEquals(pool: Pool, hash: string) {
466
+ return pool.bufferHash === hash
467
+ }
468
+ public equals(other: Pool): boolean {
469
+ // Сравниваем id пулов
470
+ if (this.id !== other.id) return false;
471
+
472
+ // Сравниваем fee
473
+ if (this.fee !== other.fee) return false;
474
+
475
+ // Сравниваем sqrtPriceX64
476
+ if (!JSBI.equal(this.sqrtPriceX64, other.sqrtPriceX64)) return false;
477
+
478
+ // Сравниваем liquidity
479
+ if (!JSBI.equal(this.liquidity, other.liquidity)) return false;
480
+
481
+ // Сравниваем tickCurrent
482
+ if (this.tickCurrent !== other.tickCurrent) return false;
483
+
484
+ // Сравниваем токены (предполагается, что у Token есть метод equals)
485
+ if (!this.tokenA.equals(other.tokenA) || !this.tokenB.equals(other.tokenB)) return false;
486
+
487
+ // Если все проверки прошли, объекты считаются равными
488
+ return true;
489
+ }
399
490
  }
@@ -1,3 +1,4 @@
1
+ import msgpack from "msgpack-lite"
1
2
  import invariant from 'tiny-invariant'
2
3
 
3
4
  import { Currency } from './currency'
@@ -12,12 +13,12 @@ import { Pool } from './pool'
12
13
  * @template TOutput The output token
13
14
  */
14
15
  export class Route<TInput extends Currency, TOutput extends Currency> {
15
- public readonly pools: Pool[]
16
- public readonly tokenPath: Token[]
17
- public readonly input: TInput
18
- public readonly output: TOutput
16
+ public readonly pools: Pool[];
17
+ public readonly tokenPath: Token[];
18
+ public readonly input: TInput;
19
+ public readonly output: TOutput;
19
20
 
20
- private _midPrice: Price<TInput, TOutput> | null = null
21
+ private _midPrice: Price<TInput, TOutput> | null = null;
21
22
 
22
23
  /**
23
24
  * Creates an instance of route.
@@ -26,59 +27,136 @@ export class Route<TInput extends Currency, TOutput extends Currency> {
26
27
  * @param output The output token
27
28
  */
28
29
  public constructor(pools: Pool[], input: TInput, output: TOutput) {
29
- invariant(pools.length > 0, 'POOLS')
30
+ invariant(pools.length > 0, 'POOLS');
30
31
 
31
- const wrappedInput = input
32
- invariant(pools[0].involvesToken(wrappedInput), 'INPUT')
32
+ const wrappedInput = input;
33
+ invariant(pools[0].involvesToken(wrappedInput), 'INPUT');
33
34
 
34
- invariant(pools[pools.length - 1].involvesToken(output), 'OUTPUT')
35
+ invariant(pools[pools.length - 1].involvesToken(output), 'OUTPUT');
35
36
 
36
37
  /**
37
38
  * Normalizes tokenA-tokenB order and selects the next token/fee step to add to the path
38
39
  * */
39
- const tokenPath: Token[] = [wrappedInput]
40
+ const tokenPath: Token[] = [wrappedInput];
40
41
  for (const [i, pool] of pools.entries()) {
41
- const currentInputToken = tokenPath[i]
42
- invariant(currentInputToken.equals(pool.tokenA) || currentInputToken.equals(pool.tokenB), 'PATH')
43
- const nextToken = currentInputToken.equals(pool.tokenA) ? pool.tokenB : pool.tokenA
44
- tokenPath.push(nextToken)
42
+ const currentInputToken = tokenPath[i];
43
+ invariant(
44
+ currentInputToken.equals(pool.tokenA) ||
45
+ currentInputToken.equals(pool.tokenB),
46
+ 'PATH'
47
+ );
48
+ const nextToken = currentInputToken.equals(pool.tokenA)
49
+ ? pool.tokenB
50
+ : pool.tokenA;
51
+ tokenPath.push(nextToken);
45
52
  }
46
53
 
47
- this.pools = pools
48
- this.tokenPath = tokenPath
49
- this.input = input
50
- this.output = output ?? tokenPath[tokenPath.length - 1]
54
+ this.pools = pools;
55
+ this.tokenPath = tokenPath;
56
+ this.input = input;
57
+ this.output = output ?? tokenPath[tokenPath.length - 1];
51
58
  }
52
59
 
53
60
  /**
54
61
  * Returns the mid price of the route
55
62
  */
56
63
  public get midPrice(): Price<TInput, TOutput> {
57
- if (this._midPrice !== null) return this._midPrice
64
+ if (this._midPrice !== null) return this._midPrice;
58
65
 
59
66
  const price = this.pools.slice(1).reduce(
60
67
  ({ nextInput, price }, pool) => {
61
68
  return nextInput.equals(pool.tokenA)
62
69
  ? {
63
70
  nextInput: pool.tokenB,
64
- price: price.multiply(pool.tokenAPrice)
71
+ price: price.multiply(pool.tokenAPrice),
65
72
  }
66
73
  : {
67
74
  nextInput: pool.tokenA,
68
- price: price.multiply(pool.tokenBPrice)
69
- }
75
+ price: price.multiply(pool.tokenBPrice),
76
+ };
70
77
  },
71
78
  this.pools[0].tokenA.equals(this.input)
72
79
  ? {
73
80
  nextInput: this.pools[0].tokenB,
74
- price: this.pools[0].tokenAPrice
81
+ price: this.pools[0].tokenAPrice,
75
82
  }
76
83
  : {
77
84
  nextInput: this.pools[0].tokenA,
78
- price: this.pools[0].tokenBPrice
85
+ price: this.pools[0].tokenBPrice,
79
86
  }
80
- ).price
87
+ ).price;
88
+
89
+ return (this._midPrice = new Price(
90
+ this.input,
91
+ this.output,
92
+ price.denominator,
93
+ price.numerator
94
+ ));
95
+ }
96
+
97
+ static toJSON(route: Route<Currency, Currency>, lightWeightVersion = false) {
98
+ return {
99
+ pools: route.pools.map((pool) => {
100
+ if (lightWeightVersion) {
101
+ return pool.id;
102
+ } else {
103
+ return Pool.toBuffer(pool);
104
+ }
105
+ }),
106
+ input: Token.toJSON(route.input),
107
+ output: Token.toJSON(route.output),
108
+ _midPrice: route._midPrice,
109
+ };
110
+ }
111
+
112
+ static fromJSON(json: any) {
113
+ const pools = json.pools.map((pool) => {
114
+ if (typeof pool === 'number') {
115
+ return Pool.fromId(pool);
116
+ } else {
117
+ return Pool.fromBuffer(Buffer.from(pool));
118
+ }
119
+ });
120
+ const input = Token.fromJSON(json.input);
121
+ const output = Token.fromJSON(json.output);
122
+ return new Route(pools, input, output);
123
+ }
81
124
 
82
- return (this._midPrice = new Price(this.input, this.output, price.denominator, price.numerator))
125
+ static toBuffer(
126
+ route: Route<Currency, Currency>,
127
+ lightWeightVersion = false
128
+ ) {
129
+ const json = this.toJSON(route, lightWeightVersion);
130
+ return msgpack.encode(json);
131
+ }
132
+
133
+ static fromBuffer(buffer: Buffer) {
134
+ const json = msgpack.decode(buffer);
135
+ return this.fromJSON(json);
136
+ }
137
+
138
+ static toBufferAdvanced(route: Route<Currency, Currency>, pools: any[]) {
139
+ const json = {
140
+ pools: pools.map((pool) => {
141
+ if (typeof pool === 'number' || Buffer.isBuffer(pool)) {
142
+ return pool;
143
+ } else {
144
+ return Pool.toBuffer(pool);
145
+ }
146
+ }),
147
+ input: Token.toJSON(route.input),
148
+ output: Token.toJSON(route.output),
149
+ _midPrice: route._midPrice,
150
+ };
151
+ return msgpack.encode(json);
152
+ }
153
+
154
+ public equals(other: Route<Currency, Currency>): boolean {
155
+ if (this.pools.length !== other.pools.length) return false;
156
+
157
+ for (let i = 0; i < this.pools.length; i++) {
158
+ if (!this.pools[i].equals(other.pools[i])) return false;
159
+ }
160
+ return this.input.equals(other.input) && this.output.equals(other.output);
83
161
  }
84
162
  }
@@ -45,4 +45,30 @@ export class Tick {
45
45
  this.secondsOutside = JSBI.BigInt(secondsOutside);
46
46
  this.secondsPerLiquidityOutsideX64 = JSBI.BigInt(secondsPerLiquidityOutsideX64);
47
47
  }
48
+
49
+ static toJSON(tick: Tick): object {
50
+ return {
51
+ id: tick.id,
52
+ liquidityGross: tick.liquidityGross.toString(),
53
+ liquidityNet: tick.liquidityNet.toString(),
54
+ feeGrowthOutsideAX64: tick.feeGrowthOutsideAX64.toString(),
55
+ feeGrowthOutsideBX64: tick.feeGrowthOutsideBX64.toString(),
56
+ tickCumulativeOutside: tick.tickCumulativeOutside.toString(),
57
+ secondsOutside: tick.secondsOutside.toString(),
58
+ secondsPerLiquidityOutsideX64: tick.secondsPerLiquidityOutsideX64.toString(),
59
+ }
60
+ }
61
+
62
+ static fromJSON(json: any): Tick {
63
+ return new Tick({
64
+ id: json.id,
65
+ liquidityGross: JSBI.BigInt(json.liquidityGross),
66
+ liquidityNet: JSBI.BigInt(json.liquidityNet),
67
+ feeGrowthOutsideAX64: JSBI.BigInt(json.feeGrowthOutsideAX64),
68
+ feeGrowthOutsideBX64: JSBI.BigInt(json.feeGrowthOutsideBX64),
69
+ tickCumulativeOutside: JSBI.BigInt(json.tickCumulativeOutside),
70
+ secondsOutside: JSBI.BigInt(json.secondsOutside),
71
+ secondsPerLiquidityOutsideX64: JSBI.BigInt(json.secondsPerLiquidityOutsideX64),
72
+ });
73
+ }
48
74
  }
@@ -6,7 +6,7 @@ import { TickDataProvider } from "./tickDataProvider";
6
6
  * A data provider for ticks that is backed by an in-memory array of ticks.
7
7
  */
8
8
  export class TickListDataProvider implements TickDataProvider {
9
- private ticks: readonly Tick[];
9
+ public ticks: readonly Tick[];
10
10
 
11
11
  constructor(ticks: (Tick | TickConstructorArgs)[], tickSpacing: number) {
12
12
  const ticksMapped: Tick[] = ticks.map((t) =>
@@ -34,4 +34,12 @@ export class TickListDataProvider implements TickDataProvider {
34
34
  tickSpacing
35
35
  );
36
36
  }
37
+
38
+ static toJSON(ticks: Tick[]): object {
39
+ return ticks.map((tick) => Tick.toJSON(tick));
40
+ }
41
+
42
+ static fromJSON(ticksArray: any): TickListDataProvider {
43
+ return ticksArray.map(Tick.fromJSON);
44
+ }
37
45
  }
@@ -51,4 +51,16 @@ export class Token extends BaseCurrency {
51
51
  return name(this.contract).raw().lt(name(other.contract).raw());
52
52
  }
53
53
  }
54
+
55
+ static toJSON(token: Token) {
56
+ return {
57
+ contract: token.contract,
58
+ decimals: token.decimals,
59
+ symbol: token.symbol,
60
+ }
61
+ }
62
+
63
+ static fromJSON(json: any) {
64
+ return new Token(json.contract, json.decimals, json.symbol);
65
+ }
54
66
  }
@@ -240,7 +240,7 @@ export class Trade<TInput extends Currency, TOutput extends Currency, TTradeType
240
240
  amounts[0] = amount
241
241
  for (let i = 0; i < route.tokenPath.length - 1; i++) {
242
242
  const pool = route.pools[i]
243
- const [outputAmount] = pool.getOutputAmount(amounts[i])
243
+ const outputAmount = pool.getOutputAmount(amounts[i])
244
244
  amounts[i + 1] = outputAmount
245
245
  }
246
246
  inputAmount = CurrencyAmount.fromFractionalAmount(route.input, amount.numerator, amount.denominator)
@@ -254,7 +254,7 @@ export class Trade<TInput extends Currency, TOutput extends Currency, TTradeType
254
254
  amounts[amounts.length - 1] = amount
255
255
  for (let i = route.tokenPath.length - 1; i > 0; i--) {
256
256
  const pool = route.pools[i - 1]
257
- const [inputAmount] = pool.getInputAmount(amounts[i])
257
+ const inputAmount = pool.getInputAmount(amounts[i])
258
258
  amounts[i - 1] = inputAmount
259
259
  }
260
260
  inputAmount = CurrencyAmount.fromFractionalAmount(route.input, amounts[0].numerator, amounts[0].denominator)
@@ -302,7 +302,7 @@ export class Trade<TInput extends Currency, TOutput extends Currency, TTradeType
302
302
 
303
303
  for (let i = 0; i < route.tokenPath.length - 1; i++) {
304
304
  const pool = route.pools[i]
305
- const [outputAmount] = pool.getOutputAmount(amounts[i])
305
+ const outputAmount = pool.getOutputAmount(amounts[i])
306
306
  amounts[i + 1] = outputAmount
307
307
  }
308
308
 
@@ -322,7 +322,7 @@ export class Trade<TInput extends Currency, TOutput extends Currency, TTradeType
322
322
 
323
323
  for (let i = route.tokenPath.length - 1; i > 0; i--) {
324
324
  const pool = route.pools[i - 1]
325
- const [inputAmount] = pool.getInputAmount(amounts[i])
325
+ const inputAmount = pool.getInputAmount(amounts[i])
326
326
  amounts[i - 1] = inputAmount
327
327
  }
328
328
 
@@ -62,3 +62,63 @@ export function computeAllRoutes(
62
62
 
63
63
  return routes;
64
64
  }
65
+
66
+ export function computeAllRoutesFromMap(
67
+ tokenIn: Token,
68
+ tokenOut: Token,
69
+ poolMap: { [tokenId: string]: Pool[] },
70
+ maxHops: number
71
+ ): Route<Token , Token>[] {
72
+ const routes: Route<Token, Token>[] = [];
73
+
74
+ const computeRoutes = (
75
+ tokenIn: Token,
76
+ tokenOut: Token,
77
+ currentRoute: Pool[],
78
+ visitedPools: Set<Pool>,
79
+ _previousTokenOut?: Token
80
+ ) => {
81
+ if (currentRoute.length > maxHops) {
82
+ return;
83
+ }
84
+
85
+ if (
86
+ currentRoute.length > 0 &&
87
+ currentRoute[currentRoute.length - 1]!.involvesToken(tokenOut)
88
+ ) {
89
+ routes.push(new Route([...currentRoute], tokenIn, tokenOut));
90
+ return;
91
+ }
92
+
93
+ const previousTokenOut = _previousTokenOut ? _previousTokenOut : tokenIn;
94
+ const relevantPools = poolMap[previousTokenOut.id] || [];
95
+
96
+ for (const curPool of relevantPools) {
97
+ if (visitedPools.has(curPool)) {
98
+ continue;
99
+ }
100
+
101
+ const currentTokenOut = curPool.tokenA.equals(previousTokenOut)
102
+ ? curPool.tokenB
103
+ : curPool.tokenA;
104
+
105
+ currentRoute.push(curPool);
106
+ visitedPools.add(curPool);
107
+
108
+ computeRoutes(
109
+ tokenIn,
110
+ tokenOut,
111
+ currentRoute,
112
+ visitedPools,
113
+ currentTokenOut
114
+ );
115
+
116
+ visitedPools.delete(curPool);
117
+ currentRoute.pop();
118
+ }
119
+ };
120
+
121
+ computeRoutes(tokenIn, tokenOut, [], new Set());
122
+
123
+ return routes;
124
+ }
package/test/pool.test.ts CHANGED
@@ -292,14 +292,14 @@ describe("Pool", () => {
292
292
  describe("#getOutputAmount", () => {
293
293
  it("USDC -> DAI", async () => {
294
294
  const inputAmount = CurrencyAmount.fromRawAmount(USDC, 100);
295
- const [outputAmount] = await pool.getOutputAmount(inputAmount);
295
+ const outputAmount = await pool.getOutputAmount(inputAmount);
296
296
  expect(outputAmount.currency.equals(DAI)).toBe(true);
297
297
  expect(outputAmount.quotient).toEqual(JSBI.BigInt(98));
298
298
  });
299
299
 
300
300
  it("DAI -> USDC", async () => {
301
301
  const inputAmount = CurrencyAmount.fromRawAmount(DAI, 100);
302
- const [outputAmount] = await pool.getOutputAmount(inputAmount);
302
+ const outputAmount = await pool.getOutputAmount(inputAmount);
303
303
  expect(outputAmount.currency.equals(USDC)).toBe(true);
304
304
  expect(outputAmount.quotient).toEqual(JSBI.BigInt(98));
305
305
  });
@@ -308,14 +308,14 @@ describe("Pool", () => {
308
308
  describe("#getInputAmount", () => {
309
309
  it("USDC -> DAI", async () => {
310
310
  const outputAmount = CurrencyAmount.fromRawAmount(DAI, 98);
311
- const [inputAmount] = await pool.getInputAmount(outputAmount);
311
+ const inputAmount = await pool.getInputAmount(outputAmount);
312
312
  expect(inputAmount.currency.equals(USDC)).toBe(true);
313
313
  expect(inputAmount.quotient).toEqual(JSBI.BigInt(100));
314
314
  });
315
315
 
316
316
  it("DAI -> USDC", async () => {
317
317
  const outputAmount = CurrencyAmount.fromRawAmount(USDC, 98);
318
- const [inputAmount] = await pool.getInputAmount(outputAmount);
318
+ const inputAmount = await pool.getInputAmount(outputAmount);
319
319
  expect(inputAmount.currency.equals(DAI)).toBe(true);
320
320
  expect(inputAmount.quotient).toEqual(JSBI.BigInt(100));
321
321
  });
@@ -367,7 +367,7 @@ describe("Pool", () => {
367
367
  });
368
368
  it("correctly handles two BigIntegers", async () => {
369
369
  const inputAmount = CurrencyAmount.fromRawAmount(USDC, 100);
370
- const [outputAmount] = await pool.getOutputAmount(inputAmount);
370
+ const outputAmount = await pool.getOutputAmount(inputAmount);
371
371
  pool.getInputAmount(outputAmount);
372
372
  expect(outputAmount.currency.equals(DAI)).toBe(true);
373
373
  // if output is correct, function has succeeded