From 56044ce032c8ca5064f300b8112403e313dc3e79 Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Fri, 12 Jul 2024 12:10:16 +0100 Subject: [PATCH] feat: Add maxSwapAmount. --- typescript/src/stable/stablePool.ts | 23 +++++++- typescript/src/vault/types.ts | 9 +++ typescript/src/weighted/weightedPool.ts | 23 +++++++- typescript/test/stablePool.test.ts | 75 +++++++++++++++++++++++++ typescript/test/utils/readTestData.ts | 8 ++- typescript/test/weightedPool.test.ts | 41 ++++++++++++++ 6 files changed, 175 insertions(+), 4 deletions(-) create mode 100644 typescript/test/stablePool.test.ts create mode 100644 typescript/test/weightedPool.test.ts diff --git a/typescript/src/stable/stablePool.ts b/typescript/src/stable/stablePool.ts index 0feb596..19ce919 100644 --- a/typescript/src/stable/stablePool.ts +++ b/typescript/src/stable/stablePool.ts @@ -1,5 +1,10 @@ import { MathSol } from '../utils/math'; -import { type PoolBase, SwapKind, type SwapParams } from '../vault/types'; +import { + MaxSwapParams, + type PoolBase, + SwapKind, + type SwapParams, +} from '../vault/types'; import { _computeOutGivenExactIn, _computeInGivenExactOut, @@ -16,6 +21,22 @@ export class Stable implements PoolBase { this.amp = poolState.amp; } + getMaxSwapAmount(maxSwapParams: MaxSwapParams): bigint { + const { + swapKind, + balancesLiveScaled18, + indexIn, + indexOut, + tokenRates, + } = maxSwapParams; + if (swapKind === SwapKind.GivenIn) + return MathSol.mulDownFixed( + balancesLiveScaled18[indexOut], + MathSol.divDownFixed(tokenRates[indexOut], tokenRates[indexIn]), + ); + return balancesLiveScaled18[indexOut]; + } + onSwap(swapParams: SwapParams): bigint { const { swapKind, diff --git a/typescript/src/vault/types.ts b/typescript/src/vault/types.ts index 4db37e2..4cce946 100644 --- a/typescript/src/vault/types.ts +++ b/typescript/src/vault/types.ts @@ -4,6 +4,7 @@ export enum SwapKind { } export interface PoolBase { + getMaxSwapAmount(maxSwapParams: MaxSwapParams): bigint; onSwap(swapParams: SwapParams): bigint; computeInvariant(balancesLiveScaled18: bigint[]): bigint; computeBalance( @@ -13,6 +14,14 @@ export interface PoolBase { ): bigint; } +export type MaxSwapParams = { + swapKind: SwapKind; + balancesLiveScaled18: bigint[]; + tokenRates: bigint[]; + indexIn: number; + indexOut: number; +}; + export type SwapParams = { swapKind: SwapKind; amountGivenScaled18: bigint; diff --git a/typescript/src/weighted/weightedPool.ts b/typescript/src/weighted/weightedPool.ts index 985b1d8..578f79a 100644 --- a/typescript/src/weighted/weightedPool.ts +++ b/typescript/src/weighted/weightedPool.ts @@ -1,4 +1,10 @@ -import { type PoolBase, SwapKind, type SwapParams } from '../vault/types'; +import { MathSol } from '../utils/math'; +import { + MaxSwapParams, + type PoolBase, + SwapKind, + type SwapParams, +} from '../vault/types'; import { _computeOutGivenExactIn, _computeInGivenExactOut, @@ -8,6 +14,9 @@ import { export class Weighted implements PoolBase { public normalizedWeights: bigint[]; + // Swap limits: amounts swapped may not be larger than this percentage of the total balance. + public _MAX_IN_RATIO = 300000000000000000n; + public _MAX_OUT_RATIO = 300000000000000000n; constructor(poolState: { weights: bigint[]; @@ -15,6 +24,18 @@ export class Weighted implements PoolBase { this.normalizedWeights = poolState.weights; } + getMaxSwapAmount(swapParams: MaxSwapParams): bigint { + if (swapParams.swapKind === SwapKind.GivenIn) + return MathSol.mulDownFixed( + swapParams.balancesLiveScaled18[swapParams.indexIn], + this._MAX_IN_RATIO, + ); + return MathSol.mulDownFixed( + swapParams.balancesLiveScaled18[swapParams.indexOut], + this._MAX_OUT_RATIO, + ); + } + onSwap(swapParams: SwapParams): bigint { const { swapKind, diff --git a/typescript/test/stablePool.test.ts b/typescript/test/stablePool.test.ts new file mode 100644 index 0000000..1079f36 --- /dev/null +++ b/typescript/test/stablePool.test.ts @@ -0,0 +1,75 @@ +import { describe, expect, test } from 'vitest'; +import { SwapKind } from '../src/index'; +import { Stable } from '../src/stable'; + +describe('stable pool', () => { + const pool = new Stable({ + amp: 60000000000000000000n, + }); + describe('getMaxSwapAmount', () => { + describe('no rate', () => { + test('exact in', () => { + const swapParams = { + swapKind: SwapKind.GivenIn, + amountGivenScaled18: 0n, + balancesLiveScaled18: [ + 60000000000000000000n, + 40000000000000000000n, + ], + tokenRates: [1000000000000000000n, 1000000000000000000n], + indexIn: 0, + indexOut: 1, + }; + const maxSwapAmount = pool.getMaxSwapAmount(swapParams); + expect(maxSwapAmount).to.eq(40000000000000000000n); + }); + test('exact out', () => { + const swapParams = { + swapKind: SwapKind.GivenOut, + amountGivenScaled18: 0n, + balancesLiveScaled18: [ + 60000000000000000000n, + 40000000000000000000n, + ], + tokenRates: [1000000000000000000n, 1000000000000000000n], + indexIn: 0, + indexOut: 1, + }; + const maxSwapAmount = pool.getMaxSwapAmount(swapParams); + expect(maxSwapAmount).to.eq(40000000000000000000n); + }); + }); + describe('with rate', () => { + test('exact in', () => { + const swapParams = { + swapKind: SwapKind.GivenIn, + amountGivenScaled18: 0n, + balancesLiveScaled18: [ + 60000000000000000000n, + 40000000000000000000n, + ], + tokenRates: [2000000000000000000n, 4000000000000000000n], + indexIn: 0, + indexOut: 1, + }; + const maxSwapAmount = pool.getMaxSwapAmount(swapParams); + expect(maxSwapAmount).to.eq(80000000000000000000n); + }); + test('exact out', () => { + const swapParams = { + swapKind: SwapKind.GivenOut, + amountGivenScaled18: 0n, + balancesLiveScaled18: [ + 60000000000000000000n, + 40000000000000000000n, + ], + tokenRates: [2000000000000000000n, 4000000000000000000n], + indexIn: 0, + indexOut: 1, + }; + const maxSwapAmount = pool.getMaxSwapAmount(swapParams); + expect(maxSwapAmount).to.eq(40000000000000000000n); + }); + }); + }); +}); diff --git a/typescript/test/utils/readTestData.ts b/typescript/test/utils/readTestData.ts index 6711d94..833e553 100644 --- a/typescript/test/utils/readTestData.ts +++ b/typescript/test/utils/readTestData.ts @@ -143,7 +143,9 @@ function mapPool( ...pool, scalingFactors: pool.scalingFactors.map((sf) => BigInt(sf)), swapFee: BigInt(pool.swapFee), - balancesLiveScaled18: pool.balancesLiveScaled18.map((b) => BigInt(b)), + balancesLiveScaled18: pool.balancesLiveScaled18.map((b) => + BigInt(b), + ), tokenRates: pool.tokenRates.map((r) => BigInt(r)), totalSupply: BigInt(pool.totalSupply), weights: ( @@ -156,7 +158,9 @@ function mapPool( ...pool, scalingFactors: pool.scalingFactors.map((sf) => BigInt(sf)), swapFee: BigInt(pool.swapFee), - balancesLiveScaled18: pool.balancesLiveScaled18.map((b) => BigInt(b)), + balancesLiveScaled18: pool.balancesLiveScaled18.map((b) => + BigInt(b), + ), tokenRates: pool.tokenRates.map((r) => BigInt(r)), totalSupply: BigInt(pool.totalSupply), amp: BigInt((pool as TransformBigintToString).amp), diff --git a/typescript/test/weightedPool.test.ts b/typescript/test/weightedPool.test.ts new file mode 100644 index 0000000..59e19f1 --- /dev/null +++ b/typescript/test/weightedPool.test.ts @@ -0,0 +1,41 @@ +import { describe, expect, test } from 'vitest'; +import { SwapKind } from '../src/index'; +import { Weighted } from '../src/weighted'; + +describe('weighted pool', () => { + const pool = new Weighted({ + weights: [60000000000000000000n, 40000000000000000000n], + }); + describe('getMaxSwapAmount', () => { + test('exact in', () => { + const swapParams = { + swapKind: SwapKind.GivenIn, + amountGivenScaled18: 0n, + balancesLiveScaled18: [ + 60000000000000000000n, + 40000000000000000000n, + ], + tokenRates: [1000000000000000000n, 1000000000000000000n], + indexIn: 0, + indexOut: 1, + }; + const maxSwapAmount = pool.getMaxSwapAmount(swapParams); + expect(maxSwapAmount).to.eq(18000000000000000000n); + }); + test('exact out', () => { + const swapParams = { + swapKind: SwapKind.GivenOut, + amountGivenScaled18: 0n, + balancesLiveScaled18: [ + 60000000000000000000n, + 40000000000000000000n, + ], + tokenRates: [1000000000000000000n, 1000000000000000000n], + indexIn: 0, + indexOut: 1, + }; + const maxSwapAmount = pool.getMaxSwapAmount(swapParams); + expect(maxSwapAmount).to.eq(12000000000000000000n); + }); + }); +});