Skip to content

Commit

Permalink
Add generalized approach for unbalanced join
Browse files Browse the repository at this point in the history
  • Loading branch information
brunoguerios committed Nov 21, 2023
1 parent 396aa36 commit cb5a5ff
Show file tree
Hide file tree
Showing 3 changed files with 242 additions and 51 deletions.
6 changes: 6 additions & 0 deletions balancer-js/src/lib/utils/math.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,9 @@ export function formatFromBigInt18(value: bigint): string {
* Like parseEther but for numbers. Converts floating point to BigNumber using 18 decimals
*/
export const bn = (value: number): BigNumber => _parseFixed(`${value}`, 18);

export const min = (values: BigNumber[]): BigNumber =>
values.reduce((a, b) => (a.lt(b) ? a : b));

export const max = (values: BigNumber[]): BigNumber =>
values.reduce((a, b) => (a.gt(b) ? a : b));
167 changes: 116 additions & 51 deletions balancer-js/src/modules/pricing/priceImpact.compare.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,18 @@ import * as fs from 'fs';
import {
Address,
BalancerSDK,
BatchSwapStep,

Check warning on line 10 in balancer-js/src/modules/pricing/priceImpact.compare.spec.ts

View workflow job for this annotation

GitHub Actions / lint

'BatchSwapStep' is defined but never used
Network,
PoolToken,
PoolWithMethods,
SubgraphPoolBase,
SwapType,
max,
min,
} from '@/.';
import { forkSetup, TestPoolHelper } from '@/test/lib/utils';
import { queryBatchSwap } from '../swaps/queryBatchSwap';
import { Zero } from '@ethersproject/constants';

dotenv.config();

Expand All @@ -27,7 +31,7 @@ const { contracts, pricing } = sdk;
const provider = new JsonRpcProvider(rpcUrl, 1);
const signer = provider.getSigner();
const { balancerHelpers, vault } = contracts;
const csvFilePath = '50COIL50USDC_USDC.csv';
const csvFilePath = 'wstETH-rETH-sfrxETH.csv';
// Write the header to the CSV file
const csvLine = 'action,test,spot price,ABA,error abs,error rel,\n';
fs.writeFileSync(csvFilePath, csvLine, { flag: 'w' });
Expand All @@ -37,14 +41,14 @@ const writeNewTable = (header: string) => {

const blockNumber = 18559730;
const testPoolId =
'0x42fbd9f666aacc0026ca1b88c94259519e03dd67000200000000000000000507'; // 80BAL/20WETH
'0x42ed016f826165c2e5976fe5bc3df540c5ad0af700000000000000000000058b'; // 80BAL/20WETH

/**
* When testing pools with phantom BPT (e.g. ComposableStable), indexes should consider pool tokens with BPT
*/

// swap config
const swapAmountFloat = '500';
const swapAmountFloat = '10';
const swapAmountFloats = [
swapAmountFloat,
String(Number(swapAmountFloat) * 2),
Expand All @@ -55,10 +59,10 @@ const swapAmountFloats = [
String(Number(swapAmountFloat) * 100),
];
const assetInIndex = 1;
const assetOutIndex = 0;
const assetOutIndex = 2;

// single token join config
const joinAmountFloat = '500';
const joinAmountFloat = '10';
const tokenIndex = 1;
const singleTokenJoinTests = [
{ amountFloat: joinAmountFloat, tokenIndex },
Expand All @@ -73,11 +77,9 @@ const singleTokenJoinTests = [
// unbalanced join config
// const amountsInFloat = ['0', '200', '100', '10']; // should add value for BPT if present
const unbalancedJoinTests = [
{ amountsInFloat: ['10', '10'] },
{ amountsInFloat: ['10', '100'] },
{ amountsInFloat: ['10', '1000'] },
{ amountsInFloat: ['10', '10000'] },
{ amountsInFloat: ['10', '100000'] },
{ amountsInFloat: ['0', '10', '100', '1000'] },
{ amountsInFloat: ['0', '100', '1000', '10'] },
{ amountsInFloat: ['0', '1000', '100', '10'] },
// Add more test scenarios as needed
];

Expand Down Expand Up @@ -339,7 +341,7 @@ describe('Price impact comparison tests', async () => {
});

unbalancedJoinTests.forEach((test, index) => {
context('unbalanced join - 2 tokens', async () => {
context('unbalanced join - generalized (multi-token)', async () => {
let tokensIn: string[];
let amountsIn: BigNumber[];

Expand Down Expand Up @@ -398,7 +400,7 @@ describe('Price impact comparison tests', async () => {
console.log(`priceImpactSpotPrice: ${priceImpactSpot}`);
});

it('should calculate price impact - ABA method', async () => {
it('should calculate price impact - ABA method - generalized (multi-token)', async () => {
const maxAmountsInByToken = new Map<string, BigNumber>(
amountsIn.map((a, i) => [tokensIn[i], a])
);
Expand All @@ -419,50 +421,113 @@ describe('Price impact comparison tests', async () => {

// diff between unbalanced and proportional amounts for token 1
const diffs = amountsOut.map((a, i) => a.sub(amountsIn[i]));
const excessIndex = diffs.findIndex((a) => a.gt(0)); // token index that has excess amount on proportional compared to unbalanced
const otherIndex = diffs.findIndex((a) => a.lt(0));
const diffExcess = amountsOut[excessIndex].sub(amountsIn[excessIndex]);

// swap that diff to token other (non-excess)
const returnAmounts = await queryBatchSwap(
vault,
SwapType.SwapExactIn,
[
{
poolId: pool.id,
assetInIndex: excessIndex,
assetOutIndex: otherIndex,
amount: diffExcess.toString(),
userData: '0x',
},
],
pool.tokensList
);

// calculate final other token amount (using sub because returnAmounts[0] is negative)
const otherTokenFinal = amountsOut[otherIndex].sub(
BigNumber.from(returnAmounts[otherIndex])
);

// diff between unbalanced and proportional amounts for token 0
const diffOther = amountsIn[otherIndex].sub(otherTokenFinal);

// query join with diffOther in order to get BPT difference between unbalanced and proportional
const diffAmounts = new Map<string, BigNumber>([
[pool.tokensList[otherIndex], diffOther],
]);
const diffBPTs: BigNumber[] = [];
for (let i = 0; i < diffs.length; i++) {
if (diffs[i].eq(Zero)) {
diffBPTs.push(Zero);
} else {
const diffQuery = await balancerHelpers.callStatic.queryJoin(
...pool.buildQueryJoinExactIn({
maxAmountsInByToken: new Map<string, BigNumber>([
[tokensIn[i], diffs[i].abs()],
]),
})
);
const diffBPT = diffQuery.bptOut.mul(diffs[i].gte(0) ? 1 : -1);
diffBPTs.push(diffBPT);
}
}
let minPositiveDiffIndex = 0;
let minNegativeDiffIndex = 1;

const { bptOut: bptOutDiff } =
await balancerHelpers.callStatic.queryJoin(
...pool.buildQueryJoinExactIn({
maxAmountsInByToken: diffAmounts,
})
const nonZeroDiffs = diffs.filter((a) => !a.eq(Zero));
for (let i = 0; i < nonZeroDiffs.length - 1; i++) {
minPositiveDiffIndex = diffBPTs.findIndex((diffBPT) =>
diffBPT.eq(min(diffBPTs.filter((a) => a.gt(0))))
);
minNegativeDiffIndex = diffBPTs.findIndex((diffBPT) =>
diffBPT.eq(max(diffBPTs.filter((a) => a.lt(0))))
);

let returnAmounts: string[];
if (
diffBPTs[minPositiveDiffIndex] <
diffBPTs[minNegativeDiffIndex].abs()
) {
// swap that diff to token other (non-excess)
returnAmounts = await queryBatchSwap(
vault,
SwapType.SwapExactIn,
[
{
poolId: pool.id,
assetInIndex: minPositiveDiffIndex,
assetOutIndex: minNegativeDiffIndex,
amount: diffs[minPositiveDiffIndex].toString(),
userData: '0x',
},
],
pool.tokensList
);
diffs[minPositiveDiffIndex] = Zero;
diffBPTs[minPositiveDiffIndex] = Zero;
diffs[minNegativeDiffIndex] = diffs[minNegativeDiffIndex].sub(
BigNumber.from(returnAmounts[minNegativeDiffIndex])
);
const diffQuery = await balancerHelpers.callStatic.queryJoin(
...pool.buildQueryJoinExactIn({
maxAmountsInByToken: new Map<string, BigNumber>([
[
tokensIn[minNegativeDiffIndex],
diffs[minNegativeDiffIndex].abs(),
],
]),
})
);
diffBPTs[minNegativeDiffIndex] = diffQuery.bptOut.mul(-1);
} else {
returnAmounts = await queryBatchSwap(
vault,
SwapType.SwapExactOut,
[
{
poolId: pool.id,
assetInIndex: minPositiveDiffIndex,
assetOutIndex: minNegativeDiffIndex,
amount: diffs[minNegativeDiffIndex].abs().toString(),
userData: '0x',
},
],
pool.tokensList
);
diffs[minNegativeDiffIndex] = Zero;
diffBPTs[minNegativeDiffIndex] = Zero;
diffs[minPositiveDiffIndex] = diffs[minPositiveDiffIndex].add(
BigNumber.from(returnAmounts[minPositiveDiffIndex])
);
const diffQuery = await balancerHelpers.callStatic.queryJoin(
...pool.buildQueryJoinExactIn({
maxAmountsInByToken: new Map<string, BigNumber>([
[
tokensIn[minPositiveDiffIndex],
diffs[minPositiveDiffIndex].abs(),
],
]),
})
);
diffBPTs[minPositiveDiffIndex] = diffQuery.bptOut;
}
}

const initialBPT = parseFloat(formatFixed(bptOut, 18));
const finalBPT = parseFloat(formatFixed(bptOut.sub(bptOutDiff), 18));
const amountInitial = parseFloat(
amountsIn[minNegativeDiffIndex].toString()
);
const amountDiff = parseFloat(
diffs[minNegativeDiffIndex].abs().toString()
);

priceImpactABA = (initialBPT - finalBPT) / initialBPT / 2;
priceImpactABA = amountDiff / amountInitial / 2;
console.log(`priceImpactABA : ${priceImpactABA}`);
});
});
Expand Down
120 changes: 120 additions & 0 deletions balancer-js/wstETH-rETH-sfrxETH.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
action,test,spot price,ABA,error abs,error rel,
swap,1,0.00040377556888164767,0.00040370224878127914,-7.332010036853161e-8,-0.0001815862722244663
Swap Summary
0x42ed016f826165c2e5976fe5bc3df540c5ad0af7,0,2596148429267391.797360501734824291,0
0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0,10,3049.113080910972561465,0.0032796422220629272
0xac3e018457b222d93114458476f3e3416abbe38f,0,8353.098667955846370862,0
0xae78736cd615f374d3085123a210448e74fc6393,0,1210.62732888482209938,0

swap,2,0.00040752761462463616,0.00040748466224682645,-4.2952377809709914e-8,-0.00010539746576258964
Swap Summary
0x42ed016f826165c2e5976fe5bc3df540c5ad0af7,0,2596148429267391.797360501734824291,0
0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0,20,3049.113080910972561465,0.0065592844441258544
0xac3e018457b222d93114458476f3e3416abbe38f,0,8353.098667955846370862,0
0xae78736cd615f374d3085123a210448e74fc6393,0,1210.62732888482209938,0

swap,3,0.00041865039101878187,0.00041883520209616163,1.848110773797619e-7,0.00044144489374541327
Swap Summary
0x42ed016f826165c2e5976fe5bc3df540c5ad0af7,0,2596148429267391.797360501734824291,0
0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0,50,3049.113080910972561465,0.016398211110314635
0xac3e018457b222d93114458476f3e3416abbe38f,0,8353.098667955846370862,0
0xae78736cd615f374d3085123a210448e74fc6393,0,1210.62732888482209938,0

swap,4,0.0004367569669131873,0.0004377776577118198,0.0000010206907986324491,0.002336976570393961
Swap Summary
0x42ed016f826165c2e5976fe5bc3df540c5ad0af7,0,2596148429267391.797360501734824291,0
0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0,100,3049.113080910972561465,0.03279642222062927
0xac3e018457b222d93114458476f3e3416abbe38f,0,8353.098667955846370862,0
0xae78736cd615f374d3085123a210448e74fc6393,0,1210.62732888482209938,0

swap,5,0.0004880753174956617,0.0004950607027807906,0.000006985385285128876,0.014312105191000499
Swap Summary
0x42ed016f826165c2e5976fe5bc3df540c5ad0af7,0,2596148429267391.797360501734824291,0
0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0,250,3049.113080910972561465,0.08199105555157317
0xac3e018457b222d93114458476f3e3416abbe38f,0,8353.098667955846370862,0
0xae78736cd615f374d3085123a210448e74fc6393,0,1210.62732888482209938,0

swap,6,0.0005649246215843502,0.0005938876298481546,0.000028963008263804368,0.0512688014598773
Swap Summary
0x42ed016f826165c2e5976fe5bc3df540c5ad0af7,0,2596148429267391.797360501734824291,0
0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0,500,3049.113080910972561465,0.16398211110314634
0xac3e018457b222d93114458476f3e3416abbe38f,0,8353.098667955846370862,0
0xae78736cd615f374d3085123a210448e74fc6393,0,1210.62732888482209938,0

swap,7,0.0006937172565830037,0.0008207671867847921,0.00012704993020178838,0.18314367848882648
Swap Summary
0x42ed016f826165c2e5976fe5bc3df540c5ad0af7,0,2596148429267391.797360501734824291,0
0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0,1000,3049.113080910972561465,0.3279642222062927
0xac3e018457b222d93114458476f3e3416abbe38f,0,8353.098667955846370862,0
0xae78736cd615f374d3085123a210448e74fc6393,0,1210.62732888482209938,0

single token join,1,0.000299821876819659,0.0002997818706826472,-4.000613701183068e-8,-0.00013343301508279907
Single-Sided Join Summary
0x42ed016f826165c2e5976fe5bc3df540c5ad0af7,0,2596148429267391.797360501734824291,0
0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0,10,3049.113080910972561465,0.0032796422220629272
0xac3e018457b222d93114458476f3e3416abbe38f,0,8353.098667955846370862,0
0xae78736cd615f374d3085123a210448e74fc6393,0,1210.62732888482209938,0

single token join,2,0.000301646893391609,0.00030162116023273454,-2.573315887444529e-8,-0.00008530888080799017
Single-Sided Join Summary
0x42ed016f826165c2e5976fe5bc3df540c5ad0af7,0,2596148429267391.797360501734824291,0
0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0,20,3049.113080910972561465,0.0065592844441258544
0xac3e018457b222d93114458476f3e3416abbe38f,0,8353.098667955846370862,0
0xae78736cd615f374d3085123a210448e74fc6393,0,1210.62732888482209938,0

single token join,3,0.000307064127222337,0.00030714052265715,7.639543481299067e-8,0.00024879309577466454
Single-Sided Join Summary
0x42ed016f826165c2e5976fe5bc3df540c5ad0af7,0,2596148429267391.797360501734824291,0
0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0,50,3049.113080910972561465,0.016398211110314635
0xac3e018457b222d93114458476f3e3416abbe38f,0,8353.098667955846370862,0
0xae78736cd615f374d3085123a210448e74fc6393,0,1210.62732888482209938,0

single token join,4,0.000315905711879544,0.0003163503571391857,4.446452596417231e-7,0.001407525229588973
Single-Sided Join Summary
0x42ed016f826165c2e5976fe5bc3df540c5ad0af7,0,2596148429267391.797360501734824291,0
0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0,100,3049.113080910972561465,0.03279642222062927
0xac3e018457b222d93114458476f3e3416abbe38f,0,8353.098667955846370862,0
0xae78736cd615f374d3085123a210448e74fc6393,0,1210.62732888482209938,0

single token join,5,0.000341126126129627,0.00034417624561956475,0.0000030501194899377397,0.008941324795447366
Single-Sided Join Summary
0x42ed016f826165c2e5976fe5bc3df540c5ad0af7,0,2596148429267391.797360501734824291,0
0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0,250,3049.113080910972561465,0.08199105555157317
0xac3e018457b222d93114458476f3e3416abbe38f,0,8353.098667955846370862,0
0xae78736cd615f374d3085123a210448e74fc6393,0,1210.62732888482209938,0

single token join,6,0.000379371869200906,0.0003919909344199368,0.000012619065219030814,0.033263049381102285
Single-Sided Join Summary
0x42ed016f826165c2e5976fe5bc3df540c5ad0af7,0,2596148429267391.797360501734824291,0
0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0,500,3049.113080910972561465,0.16398211110314634
0xac3e018457b222d93114458476f3e3416abbe38f,0,8353.098667955846370862,0
0xae78736cd615f374d3085123a210448e74fc6393,0,1210.62732888482209938,0

single token join,7,0.00044486981991085,0.00050014069422582,0.00005527087431496998,0.12424055721749348
Single-Sided Join Summary
0x42ed016f826165c2e5976fe5bc3df540c5ad0af7,0,2596148429267391.797360501734824291,0
0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0,1000,3049.113080910972561465,0.3279642222062927
0xac3e018457b222d93114458476f3e3416abbe38f,0,8353.098667955846370862,0
0xae78736cd615f374d3085123a210448e74fc6393,0,1210.62732888482209938,0

unbalanced join,1,0.001395038034686279,0.002192811273091142,0.000797773238404863,0.5718648657377066
Unbalanced Join Summary
0x42ed016f826165c2e5976fe5bc3df540c5ad0af7,0,2596148429267391.797360501734824291,0
0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0,10,3049.113080910972561465,0.0032796422220629272
0xac3e018457b222d93114458476f3e3416abbe38f,100,8353.098667955846370862,0.011971605265914068
0xae78736cd615f374d3085123a210448e74fc6393,1000,1210.62732888482209938,0.8260180289512851

unbalanced join,2,0.000143047857261436,0.00015267199507975335,0.000009624137818317347,0.0672791470111164
Unbalanced Join Summary
0x42ed016f826165c2e5976fe5bc3df540c5ad0af7,0,2596148429267391.797360501734824291,0
0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0,100,3049.113080910972561465,0.03279642222062927
0xac3e018457b222d93114458476f3e3416abbe38f,1000,8353.098667955846370862,0.11971605265914068
0xae78736cd615f374d3085123a210448e74fc6393,10,1210.62732888482209938,0.00826018028951285

unbalanced join,3,0.00038505679898757,0.00045787494478876696,0.00007281814580119696,0.18911014165353718
Unbalanced Join Summary
0x42ed016f826165c2e5976fe5bc3df540c5ad0af7,0,2596148429267391.797360501734824291,0
0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0,1000,3049.113080910972561465,0.3279642222062927
0xac3e018457b222d93114458476f3e3416abbe38f,100,8353.098667955846370862,0.011971605265914068
0xae78736cd615f374d3085123a210448e74fc6393,10,1210.62732888482209938,0.00826018028951285

0 comments on commit cb5a5ff

Please sign in to comment.