Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions test/entities/swaps/v2/swap.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,13 @@ import {
testsV2,
allTestsHaveSavedDataV2,
} from './swapTestConfig';
import { loadSwapTestData, saveSwapTestData } from 'test/lib/utils';
import {
loadSwapTestData,
saveSwapTestData,
generateJobId,
} from 'test/lib/utils';
import { runSwapTest } from 'test/lib/utils/swapTestRunner';
import { join, dirname, basename } from 'node:path';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';

// Get the directory of the current test file
Expand All @@ -26,10 +30,7 @@ const swapTestDataPath = join(
__dirname,
TEST_CONSTANTS_V2.SWAP_TEST_DATA_FILENAME,
);
const jobId =
basename(__filename)
.split('')
.reduce((sum, char) => sum + char.charCodeAt(0), 0) % 10000;
const jobId = generateJobId(__filename);

// Load existing test data (if file exists)
const savedSwapTestData = loadSwapTestData(swapTestDataPath);
Expand Down
8 changes: 3 additions & 5 deletions test/entities/swaps/v3/swap.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@ import {
loadSwapTestData,
saveSwapTestData,
allTestsHaveSavedData,
generateJobId,
} from 'test/lib/utils';
import {
runSwapTest,
runSwapTestWithSignature,
} from 'test/lib/utils/swapTestRunner';
import { join, dirname, basename } from 'node:path';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';

// Get the directory of the current test file
Expand All @@ -35,10 +36,7 @@ const swapTestDataPath = join(
__dirname,
TEST_CONSTANTS.SWAP_TEST_DATA_FILENAME,
);
const jobId =
basename(__filename)
.split('')
.reduce((sum, char) => sum + char.charCodeAt(0), 0) % 10000;
const jobId = generateJobId(__filename);

// Load existing test data (if file exists)
const savedSwapTestData = loadSwapTestData(swapTestDataPath);
Expand Down
135 changes: 135 additions & 0 deletions test/lib/utils/addLiquidityAssertHelpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import { Address, TestActions } from 'viem';
import {
AddLiquidityQueryOutput,
AddLiquidityBuildCallOutput,
ChainId,
PublicWalletClient,
PoolState,
Slippage,
} from '@/index';
import { expect } from 'vitest';
import { sendTransactionGetBalances, TxOutput } from './helper';
import { getTokensForBalanceCheck } from './getTokensForBalanceCheck';
import {
assertAddLiquidityUnbalanced,
assertAddLiquiditySingleToken,
assertAddLiquidityProportional,
} from './addLiquidityHelper';
import {
AddLiquidityUnbalancedInput,
AddLiquiditySingleTokenInput,
AddLiquidityProportionalInput,
AddLiquidityInput,
AddLiquidityKind,
} from '@/index';

/**
* Runs full integration test assertions for add liquidity.
* Sends the transaction and verifies results using existing assertion helpers.
* @param params - Parameters for the assertion
*/
export async function assertAddLiquidityResultWithForkTest({
addLiquidityInput,
poolState,
chainId,
client,
testAddress,
call,
queryOutput,
slippage,
wethIsEth,
}: {
addLiquidityInput: AddLiquidityInput;
poolState: PoolState;
chainId: ChainId;
client?: PublicWalletClient & TestActions;
testAddress: Address;
call: AddLiquidityBuildCallOutput;
queryOutput: AddLiquidityQueryOutput;
slippage: Slippage;
wethIsEth?: boolean;
}): Promise<void> {
// Run full integration assertions - client must be available
if (!client) {
throw new Error(
'Cannot run integration assertions: client not initialized and no saved test data available. ' +
'Either initialize a test client or ensure saved test data exists for this test case.',
);
}

const tokens = getTokensForBalanceCheck(poolState);

// Send transaction and calculate balance changes
const txOutput: TxOutput = await sendTransactionGetBalances(
tokens,
client,
testAddress,
call.to,
call.callData,
call.value,
);

// Use existing assertion helpers based on add liquidity kind
const addLiquidityOutput = {
addLiquidityQueryOutput: queryOutput,
addLiquidityBuildCallOutput: call,
txOutput,
};

const protocolVersion = poolState.protocolVersion as 2 | 3;

if (addLiquidityInput.kind === AddLiquidityKind.Unbalanced) {
assertAddLiquidityUnbalanced(
poolState,
addLiquidityInput as AddLiquidityUnbalancedInput,
addLiquidityOutput,
slippage,
chainId,
protocolVersion,
wethIsEth,
);
} else if (addLiquidityInput.kind === AddLiquidityKind.SingleToken) {
assertAddLiquiditySingleToken(
poolState,
addLiquidityInput as AddLiquiditySingleTokenInput,
addLiquidityOutput,
slippage,
chainId,
protocolVersion,
wethIsEth,
);
} else {
assertAddLiquidityProportional(
poolState,
addLiquidityInput as AddLiquidityProportionalInput,
addLiquidityOutput,
slippage,
chainId,
protocolVersion,
wethIsEth,
);
}
}

/**
* Compares built call data against saved test data.
* CallData should match even for permit2 signature tests because the permit2 signature
* is saved and reloaded, making the callData deterministic.
* @param builtCall - The built call data
* @param savedData - The saved test data
*/
export function assertAddLiquidityResultWithSavedData(
builtCall: AddLiquidityBuildCallOutput,
savedData: { call: unknown; permit2?: unknown },
) {
const savedCall = savedData.call as {
to: Address;
callData: string;
value: string;
minBptOut: unknown;
maxAmountsIn: unknown[];
};
expect(builtCall.callData).to.deep.equal(savedCall.callData);
expect(builtCall.to).to.equal(savedCall.to);
expect(builtCall.value).to.equal(BigInt(savedCall.value));
}
136 changes: 136 additions & 0 deletions test/lib/utils/addLiquidityBoostedAssertHelpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import { Address, TestActions, zeroAddress } from 'viem';
import {
AddLiquidityBoostedInput,
AddLiquidityBoostedQueryOutput,
AddLiquidityBuildCallOutput,
PublicWalletClient,
Token,
TokenAmount,
AddLiquidityKind,
} from '@/index';
import { expect } from 'vitest';
import { sendTransactionGetBalances, TxOutput } from './helper';
import {
assertAddLiquidityBoostedUnbalanced,
assertAddLiquidityBoostedProportional,
} from './addLiquidityBoostedHelper';

/**
* Runs full integration test assertions for boosted add liquidity.
* Sends the transaction and verifies results using existing assertion helpers.
* @param params - Parameters for the assertion
*/
export async function assertAddLiquidityBoostedResultWithForkTest({
addLiquidityBoostedInput,
client,
testAddress,
call,
queryOutput,
wethIsEth,
}: {
addLiquidityBoostedInput: AddLiquidityBoostedInput;
client?: PublicWalletClient & TestActions;
testAddress: Address;
call: AddLiquidityBuildCallOutput;
queryOutput: AddLiquidityBoostedQueryOutput;
wethIsEth?: boolean;
}): Promise<void> {
// Run full integration assertions - client must be available
if (!client) {
throw new Error(
'Cannot run integration assertions: client not initialized and no saved test data available. ' +
'Either initialize a test client or ensure saved test data exists for this test case.',
);
}

// Build tokenAmountsForBalanceCheck matching doAddLiquidityBoosted pattern:
// - amountsIn tokens (the tokens being added)
// - bptOut token (the BPT being received)
// - zeroAddress (for native token balance check)
const tokenAmountsForBalanceCheck = [
...queryOutput.amountsIn,
queryOutput.bptOut,
// add zero address so we can check for native token balance change
TokenAmount.fromRawAmount(
new Token(queryOutput.chainId, zeroAddress, 18),
0n,
),
];

// Map to addresses for balance checking - ensure we have the expected number
const tokensForBalanceCheck = tokenAmountsForBalanceCheck.map(
(t) => t.token.address,
);

// Validate we have the expected number of tokens
const expectedTokenCount = queryOutput.amountsIn.length + 1 + 1; // amountsIn + bptOut + zeroAddress
if (tokensForBalanceCheck.length !== expectedTokenCount) {
throw new Error(
`Expected ${expectedTokenCount} tokens for balance check, but got ${tokensForBalanceCheck.length}. ` +
`amountsIn.length: ${queryOutput.amountsIn.length}, tokens: ${tokensForBalanceCheck.join(', ')}`,
);
}

// Send transaction and calculate balance changes
const txOutput: TxOutput = await sendTransactionGetBalances(
tokensForBalanceCheck,
client,
testAddress,
call.to,
call.callData,
call.value,
);

// Validate balance deltas have the expected number of elements
if (txOutput.balanceDeltas.length !== expectedTokenCount) {
throw new Error(
`Expected ${expectedTokenCount} balance deltas, but got ${txOutput.balanceDeltas.length}. ` +
`tokens checked: ${tokensForBalanceCheck.join(', ')}, ` +
`balance deltas: ${txOutput.balanceDeltas.map((d) => d.toString()).join(', ')}`,
);
}

// Use existing assertion helpers based on add liquidity kind
const addLiquidityOutput = {
addLiquidityBoostedQueryOutput: queryOutput,
addLiquidityBuildCallOutput: call,
tokenAmountsForBalanceCheck,
txOutput: {
transactionReceipt: txOutput.transactionReceipt,
balanceDeltas: txOutput.balanceDeltas,
},
};

if (addLiquidityBoostedInput.kind === AddLiquidityKind.Unbalanced) {
assertAddLiquidityBoostedUnbalanced(
addLiquidityOutput,
wethIsEth ?? false,
);
} else {
assertAddLiquidityBoostedProportional(
addLiquidityOutput,
wethIsEth ?? false,
);
}
}

/**
* Compares built boosted call data against saved test data.
* @param builtCall - The built call data
* @param savedData - The saved test data
*/
export function assertAddLiquidityBoostedResultWithSavedData(
builtCall: AddLiquidityBuildCallOutput,
savedData: { call: unknown; permit2?: unknown },
) {
const savedCall = savedData.call as {
to: Address;
callData: string;
value: string;
minBptOut: unknown;
maxAmountsIn: unknown[];
};
expect(builtCall.callData).to.deep.equal(savedCall.callData);
expect(builtCall.to).to.equal(savedCall.to);
expect(builtCall.value).to.equal(BigInt(savedCall.value));
}
65 changes: 65 additions & 0 deletions test/lib/utils/addLiquidityBoostedBuildHelpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import {
AddLiquidityBoostedV3,
AddLiquidityBoostedQueryOutput,
AddLiquidityBuildCallOutput,
Slippage,
Permit2,
} from '@/index';
import { serializeBoostedCall } from './addLiquidityTestDataHelpers';

/**
* Builds boosted add liquidity call data and serializes it for comparison/saving.
* @param addLiquidityBoosted - The AddLiquidityBoostedV3 instance
* @param queryOutput - The query output to build the call from
* @param slippage - Slippage tolerance
* @param wethIsEth - Whether WETH should be treated as native ETH
* @returns The built call and its serialized form
*/
export function buildAndSerializeBoostedCall(
addLiquidityBoosted: AddLiquidityBoostedV3,
queryOutput: AddLiquidityBoostedQueryOutput,
slippage: Slippage,
wethIsEth: boolean,
): {
call: AddLiquidityBuildCallOutput;
serializedCall: unknown;
} {
const call = addLiquidityBoosted.buildCall({
...queryOutput,
slippage,
wethIsEth,
});
const serializedCall = serializeBoostedCall(call);
return { call, serializedCall };
}

/**
* Builds boosted add liquidity call data with Permit2 and serializes it for comparison/saving.
* @param addLiquidityBoosted - The AddLiquidityBoostedV3 instance
* @param queryOutput - The query output to build the call from
* @param slippage - Slippage tolerance
* @param wethIsEth - Whether WETH should be treated as native ETH
* @param permit2 - The Permit2 signature
* @returns The built call and its serialized form
*/
export function buildAndSerializeBoostedCallWithPermit2(
addLiquidityBoosted: AddLiquidityBoostedV3,
queryOutput: AddLiquidityBoostedQueryOutput,
slippage: Slippage,
wethIsEth: boolean,
permit2: Permit2,
): {
call: AddLiquidityBuildCallOutput;
serializedCall: unknown;
} {
const call = addLiquidityBoosted.buildCallWithPermit2(
{
...queryOutput,
slippage,
wethIsEth,
},
permit2,
);
const serializedCall = serializeBoostedCall(call);
return { call, serializedCall };
}
Loading
Loading