From 9530ef333fa7a9c20d244619412cdf583bb1f5df Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Thu, 21 Nov 2024 10:18:35 +0000 Subject: [PATCH] feat: Update Vault and Maths to match deploy10 for removes, weighted & stable. --- testData/config.json | 20 +++++- ...11-5955145-Stable-stataUSDC-stataUSDT.json | 43 ++++++++--- typescript/src/vault/basePoolMath.ts | 71 +++++++++++-------- typescript/test/remove.test.ts | 2 +- 4 files changed, 94 insertions(+), 42 deletions(-) diff --git a/testData/config.json b/testData/config.json index 7175517..e4ebb77 100644 --- a/testData/config.json +++ b/testData/config.json @@ -142,7 +142,25 @@ "decimals": 6 } ], - "removes": [] + "removes": [ + { + "kind": "Proportional", + "bpt": "0x6dbdd7a36d900083a5b86a55583d90021e9f33e8", + "bptInRaw": "1000000000000000000" + }, + { + "kind": "SingleTokenExactIn", + "bpt": "0x6dbdd7a36d900083a5b86a55583d90021e9f33e8", + "bptInRaw": "1000000000000000000", + "token": "0x978206fae13faf5a8d293fb614326b237684b750" + }, + { + "kind": "SingleTokenExactOut", + "token": "0x978206fae13faf5a8d293fb614326b237684b750", + "amountOutRaw": "77000000", + "decimals": 6 + } + ] } ] } \ No newline at end of file diff --git a/testData/testData/11155111-5955145-Stable-stataUSDC-stataUSDT.json b/testData/testData/11155111-5955145-Stable-stataUSDC-stataUSDT.json index 95c6a41..75840a1 100644 --- a/testData/testData/11155111-5955145-Stable-stataUSDC-stataUSDT.json +++ b/testData/testData/11155111-5955145-Stable-stataUSDC-stataUSDT.json @@ -5,14 +5,14 @@ "amountRaw": "10000000", "tokenIn": "0x8a88124522dbbf1e56352ba3de1d9f78c143751e", "tokenOut": "0x978206fae13faf5a8d293fb614326b237684b750", - "outputRaw": "8945672" + "outputRaw": "8945610" }, { "swapKind": 1, "amountRaw": "2000000000", "tokenIn": "0x8a88124522dbbf1e56352ba3de1d9f78c143751e", "tokenOut": "0x978206fae13faf5a8d293fb614326b237684b750", - "outputRaw": "2235851957" + "outputRaw": "2235867430" } ], "adds": [ @@ -22,18 +22,43 @@ "10000000", "10000000" ], - "bptOutRaw": "23950966095277339208" + "bptOutRaw": "23950966194565945549" }, { "kind": "SingleToken", "inputAmountsRaw": [ - "8842467", + "8842500", "0" ], "bptOutRaw": "10000000000000000000" } ], - "removes": [], + "removes": [ + { + "kind": "Proportional", + "amountsOutRaw": [ + "418048", + "417045" + ], + "bptInRaw": "1000000000000000000" + }, + { + "kind": "SingleTokenExactIn", + "amountsOutRaw": [ + "0", + "791015" + ], + "bptInRaw": "1000000000000000000" + }, + { + "kind": "SingleTokenExactOut", + "amountsOutRaw": [ + "0", + "77000000" + ], + "bptInRaw": "97343276713371352126" + } + ], "pool": { "chainId": "11155111", "blockNumber": "5955145", @@ -50,12 +75,12 @@ "swapFee": "1000000000000000", "totalSupply": "84540023180127896688109", "balancesLiveScaled18": [ - "41595408773782450249339", - "46344814851454586344910" + "41596525059357005883820", + "46346379623475808397064" ], "tokenRates": [ - "1176944481304720919", - "1314484588541265655" + "1176976066669194159", + "1314528970393109700" ], "amp": "1000000", "aggregateSwapFee": "0" diff --git a/typescript/src/vault/basePoolMath.ts b/typescript/src/vault/basePoolMath.ts index 16bd0a1..32e6e04 100644 --- a/typescript/src/vault/basePoolMath.ts +++ b/typescript/src/vault/basePoolMath.ts @@ -172,14 +172,12 @@ export function computeProportionalAmountsOut( // bpt = bptTotalSupply // **********************************************************************************************/ - // Since we're computing an amount out, we round down overall. This means rounding down on both the - // multiplication and division. - - const bptRatio = MathSol.divDownFixed(bptAmountIn, bptTotalSupply); - + // Create a new array to hold the amounts of each token to be withdrawn. const amountsOut: bigint[] = []; - for (let i = 0; i < balances.length; i++) { - amountsOut.push(MathSol.mulDownFixed(balances[i], bptRatio)); + for (let i = 0; i < balances.length; ++i) { + // Since we multiply and divide we don't need to use FP math. + // Round down since we're calculating amounts out. + amountsOut.push((balances[i] * bptAmountIn) / bptTotalSupply); } return amountsOut; } @@ -223,24 +221,25 @@ export function computeRemoveLiquiditySingleTokenExactIn( // Compute the amount to be withdrawn from the pool. const amountOut = currentBalances[tokenOutIndex] - newBalance; - // Calculate the non-taxable balance proportionate to the BPT burnt. - const nonTaxableBalance = MathSol.divUpFixed( - MathSol.mulUpFixed(newSupply, currentBalances[tokenOutIndex]), + const newBalanceBeforeTax = MathSol.mulDivUpFixed( + newSupply, + currentBalances[tokenOutIndex], totalSupply, ); - // Compute the taxable amount: the difference between the non-taxable balance and actual withdrawal. - const taxableAmount = nonTaxableBalance - newBalance; + // Compute the taxable amount: the difference between the new proportional and disproportional balances. + const taxableAmount = newBalanceBeforeTax - newBalance; // Calculate the swap fee on the taxable amount. const fee = MathSol.mulUpFixed(taxableAmount, swapFeePercentage); - // Create swap fees amount array and set the single fee we charge + // Create swap fees amount array and set the single fee we charge. const swapFeeAmounts = new Array(currentBalances.length); swapFeeAmounts[tokenOutIndex] = fee; // Return the net amount after subtracting the fee. const amountOutWithFee = amountOut - fee; + return { amountOutWithFee, swapFeeAmounts, @@ -276,7 +275,7 @@ export function computeRemoveLiquiditySingleTokenExactOut( // Copy currentBalances to newBalances for (let index = 0; index < currentBalances.length; index++) { - newBalances[index] = currentBalances[index]; + newBalances[index] = currentBalances[index] - 1n; } // Update the balance of tokenOutIndex with exactAmountOut. newBalances[tokenOutIndex] = newBalances[tokenOutIndex] - exactAmountOut; @@ -284,19 +283,22 @@ export function computeRemoveLiquiditySingleTokenExactOut( // Calculate the invariant using the current balances. const currentInvariant = computeInvariant( currentBalances, - Rounding.ROUND_DOWN, + Rounding.ROUND_UP, ); - // Calculate the new invariant ratio by dividing the new invariant by the current invariant. - // Calculate the taxable amount by subtracting the new balance from the equivalent proportional balance. + // We round invariant ratio up (see reason below). + // This invariant ratio could be rounded up even more by rounding `currentInvariant` down. But since it only + // affects the taxable amount and the fee calculation, whereas `currentInvariant` affects BPT in more directly, + // we use `currentInvariant` rounded up here as well. + const invariantRatio = MathSol.divUpFixed( + computeInvariant(newBalances, Rounding.ROUND_UP), + currentInvariant, + ); + + // Taxable amount is proportional to invariant ratio; a larger taxable amount rounds in the Vault's favor. const taxableAmount = - MathSol.mulUpFixed( - MathSol.divUpFixed( - computeInvariant(newBalances, Rounding.ROUND_DOWN), - currentInvariant, - ), - currentBalances[tokenOutIndex], - ) - newBalances[tokenOutIndex]; + MathSol.mulUpFixed(invariantRatio, currentBalances[tokenOutIndex]) - + newBalances[tokenOutIndex]; const fee = MathSol.divUpFixed( @@ -316,15 +318,22 @@ export function computeRemoveLiquiditySingleTokenExactOut( // Create swap fees amount array and set the single fee we charge const swapFeeAmounts = new Array(numTokens); swapFeeAmounts[tokenOutIndex] = fee; - - // mulUp/divUp maximize the amount of tokens burned for the security reasons - const bptAmountIn = MathSol.divUpFixed( - MathSol.mulUpFixed( - totalSupply, - currentInvariant - invariantWithFeesApplied, - ), + // Calculate the amount of BPT to burn. This is done by multiplying the total supply by the ratio of the + // invariant delta to the current invariant. + // + // Calculating BPT amount in, so we round up. This is the most important result of this function, equivalent to: + // `totalSupply * (1 - invariantWithFeesApplied / currentInvariant)`. + // Then, to round `bptAmountIn` up we use `invariantWithFeesApplied` rounded down and `currentInvariant` + // rounded up. + // + // Since `currentInvariant` is rounded up and `invariantWithFeesApplied` is rounded down, the difference + // should always be positive. The checked math will revert if that is not the case. + const bptAmountIn = MathSol.mulDivUpFixed( + totalSupply, + currentInvariant - invariantWithFeesApplied, currentInvariant, ); + return { bptAmountIn, swapFeeAmounts, diff --git a/typescript/test/remove.test.ts b/typescript/test/remove.test.ts index 789bf2a..2156a42 100644 --- a/typescript/test/remove.test.ts +++ b/typescript/test/remove.test.ts @@ -1,4 +1,4 @@ -// pnpm test -- swaps.test.ts +// pnpm test -- remove.test.ts import { describe, expect, test } from 'vitest'; import { Vault } from '../src'; import { readTestData } from './utils/readTestData';