From 27612179d9e365ef91c76eaefcb95268b085ea79 Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Thu, 5 Dec 2024 20:58:01 +0000 Subject: [PATCH] fix: Stable pool max swap amount, exactOut. --- typescript/src/stable/stablePool.ts | 40 ++++++++++++++----- typescript/src/weighted/weightedPool.ts | 51 ++++++++++++++++--------- typescript/test/stablePool.test.ts | 44 ++++++++++++++++++--- typescript/test/weightedPool.test.ts | 18 ++++++++- 4 files changed, 119 insertions(+), 34 deletions(-) diff --git a/typescript/src/stable/stablePool.ts b/typescript/src/stable/stablePool.ts index 7d77028..86134ba 100644 --- a/typescript/src/stable/stablePool.ts +++ b/typescript/src/stable/stablePool.ts @@ -1,5 +1,6 @@ import { MAX_UINT256, MAX_BALANCE } from '../constants'; import { MathSol } from '../utils/math'; +import { toRawUndoRateRoundDown } from '../vault/utils'; import { MaxSingleTokenRemoveParams, MaxSwapParams, @@ -24,18 +25,39 @@ export class Stable implements PoolBase { } /** - * Returns the max amount that can be swapped (in relation to the amount specified by user). + * Returns the max amount that can be swapped in relation to the swapKind. * @param maxSwapParams - * @returns Returned amount/scaling is respective to the tokenOut because that’s what we’re taking out of the pool and what limits the swap size. + * @returns GivenIn: Returns the max amount in. GivenOut: Returns the max amount out. */ getMaxSwapAmount(maxSwapParams: MaxSwapParams): bigint { - const { balancesLiveScaled18, indexIn, tokenRates, scalingFactors } = - maxSwapParams; - - const diff = MAX_BALANCE - balancesLiveScaled18[indexIn]; - return MathSol.divDownFixed( - diff, - scalingFactors[indexIn] * tokenRates[indexIn], + const { + balancesLiveScaled18, + indexIn, + indexOut, + tokenRates, + scalingFactors, + swapKind, + } = maxSwapParams; + if (swapKind === SwapKind.GivenIn) { + // MAX_BALANCE comes from SC limit and is max pool can hold + const diff = MAX_BALANCE - balancesLiveScaled18[indexIn]; + // Scale to token in (and remove rate) + return toRawUndoRateRoundDown( + diff, + scalingFactors[indexIn], + tokenRates[indexIn], + ); + } + // 99% of token out balance + const max = MathSol.mulDownFixed( + 990000000000000000n, + balancesLiveScaled18[indexOut], + ); + // Scale to token out + return toRawUndoRateRoundDown( + max, + scalingFactors[indexOut], + tokenRates[indexOut], ); } diff --git a/typescript/src/weighted/weightedPool.ts b/typescript/src/weighted/weightedPool.ts index 3a93643..c285526 100644 --- a/typescript/src/weighted/weightedPool.ts +++ b/typescript/src/weighted/weightedPool.ts @@ -1,5 +1,6 @@ import { MAX_UINT256 } from '../constants'; import { MathSol } from '../utils/math'; +import { toRawUndoRateRoundDown } from '../vault/utils'; import { MaxSingleTokenRemoveParams, MaxSwapParams, @@ -26,27 +27,41 @@ export class Weighted implements PoolBase { } /** - * Returns the max amount that can be swapped (in relation to the amount specified by user). + * Returns the max amount that can be swapped in relation to the swapKind. * @param maxSwapParams - * @returns Returned amount/scaling is respective to the tokenOut because that’s what we’re taking out of the pool and what limits the swap size. + * @returns GivenIn: Returns the max amount in. GivenOut: Returns the max amount out. */ - getMaxSwapAmount(swapParams: MaxSwapParams): bigint { - if (swapParams.swapKind === SwapKind.GivenIn) - return MathSol.divDownFixed( - MathSol.mulDownFixed( - swapParams.balancesLiveScaled18[swapParams.indexIn], - _MAX_IN_RATIO, - ), - swapParams.scalingFactors[swapParams.indexIn] * - swapParams.tokenRates[swapParams.indexIn], + getMaxSwapAmount(maxSwapParams: MaxSwapParams): bigint { + const { + balancesLiveScaled18, + indexIn, + indexOut, + tokenRates, + scalingFactors, + swapKind, + } = maxSwapParams; + if (swapKind === SwapKind.GivenIn) { + const max18 = MathSol.mulDownFixed( + balancesLiveScaled18[indexIn], + _MAX_IN_RATIO, ); - return MathSol.divDownFixed( - MathSol.mulDownFixed( - swapParams.balancesLiveScaled18[swapParams.indexOut], - _MAX_OUT_RATIO, - ), - swapParams.scalingFactors[swapParams.indexOut] * - swapParams.tokenRates[swapParams.indexOut], + // Scale to token in (and remove rate) + return toRawUndoRateRoundDown( + max18, + scalingFactors[indexIn], + tokenRates[indexIn], + ); + } + + const max18 = MathSol.mulDownFixed( + balancesLiveScaled18[indexOut], + _MAX_OUT_RATIO, + ); + // Scale to token out + return toRawUndoRateRoundDown( + max18, + scalingFactors[indexOut], + tokenRates[indexOut], ); } diff --git a/typescript/test/stablePool.test.ts b/typescript/test/stablePool.test.ts index 48445bc..f686c57 100644 --- a/typescript/test/stablePool.test.ts +++ b/typescript/test/stablePool.test.ts @@ -9,7 +9,25 @@ describe('stable pool', () => { }); describe('getMaxSwapAmount', () => { describe('no rate', () => { - test('exact in', () => { + test('exact in, 18 decimals', () => { + const swapParams = { + swapKind: SwapKind.GivenIn, + amountGivenScaled18: 0n, + balancesLiveScaled18: [ + 60000000000000000000n, + 40000000000000000000n, + ], + tokenRates: [1000000000000000000n, 1000000000000000000n], + scalingFactors: [1n, 1000000000000n], + indexIn: 0, + indexOut: 1, + }; + const maxSwapAmount = pool.getMaxSwapAmount(swapParams); + expect(maxSwapAmount).to.eq( + 340282366920938463403374607431768211456n, + ); + }); + test('exact in, 6 decimals', () => { const swapParams = { swapKind: SwapKind.GivenIn, amountGivenScaled18: 0n, @@ -25,7 +43,7 @@ describe('stable pool', () => { const maxSwapAmount = pool.getMaxSwapAmount(swapParams); expect(maxSwapAmount).to.eq(340282366920938463403374607n); }); - test('exact out', () => { + test('exact out, 18decimals', () => { const swapParams = { swapKind: SwapKind.GivenOut, amountGivenScaled18: 0n, @@ -39,7 +57,23 @@ describe('stable pool', () => { indexOut: 1, }; const maxSwapAmount = pool.getMaxSwapAmount(swapParams); - expect(maxSwapAmount).to.eq(340282366920938463403374607n); + expect(maxSwapAmount).to.eq(39600000000000000000n); + }); + test('exact out, 6decimals', () => { + const swapParams = { + swapKind: SwapKind.GivenOut, + amountGivenScaled18: 0n, + balancesLiveScaled18: [ + 60000000000000000000n, + 40000000000000000000n, + ], + tokenRates: [1000000000000000000n, 1000000000000000000n], + scalingFactors: [1n, 1000000000000n], + indexIn: 0, + indexOut: 1, + }; + const maxSwapAmount = pool.getMaxSwapAmount(swapParams); + expect(maxSwapAmount).to.eq(39600000n); }); }); describe('with rate', () => { @@ -75,9 +109,7 @@ describe('stable pool', () => { indexOut: 1, }; const maxSwapAmount = pool.getMaxSwapAmount(swapParams); - expect(maxSwapAmount).to.eq( - 170141183460469231701687303715884105728n, - ); + expect(maxSwapAmount).to.eq(9900000n); }); }); }); diff --git a/typescript/test/weightedPool.test.ts b/typescript/test/weightedPool.test.ts index d10d527..fa13896 100644 --- a/typescript/test/weightedPool.test.ts +++ b/typescript/test/weightedPool.test.ts @@ -8,7 +8,7 @@ describe('weighted pool', () => { weights: [60000000000000000000n, 40000000000000000000n], }); describe('getMaxSwapAmount', () => { - test('exact in', () => { + test('exact in, 18 decimals', () => { const swapParams = { swapKind: SwapKind.GivenIn, amountGivenScaled18: 0n, @@ -24,6 +24,22 @@ describe('weighted pool', () => { const maxSwapAmount = pool.getMaxSwapAmount(swapParams); expect(maxSwapAmount).to.eq(18000000000000000000n); }); + test('exact in, 6 decimals', () => { + const swapParams = { + swapKind: SwapKind.GivenIn, + amountGivenScaled18: 0n, + balancesLiveScaled18: [ + 60000000000000000000n, + 40000000000000000000n, + ], + tokenRates: [1000000000000000000n, 1000000000000000000n], + scalingFactors: [1000000000000n, 1n], + indexIn: 0, + indexOut: 1, + }; + const maxSwapAmount = pool.getMaxSwapAmount(swapParams); + expect(maxSwapAmount).to.eq(18000000n); + }); test('exact out', () => { const swapParams = { swapKind: SwapKind.GivenOut,