Skip to content

Commit

Permalink
Merge pull request #276 from balancer/weth-is-eth-refactor
Browse files Browse the repository at this point in the history
Refactor useNativeAssetAsWrappedAmountIn into wethIsEth
  • Loading branch information
brunoguerios authored Mar 12, 2024
2 parents 7ac10d7 + ed69b5f commit f811425
Show file tree
Hide file tree
Showing 78 changed files with 1,186 additions and 791 deletions.
5 changes: 5 additions & 0 deletions .changeset/curly-shrimps-jog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@balancer/sdk": minor
---

Rename buildCall interface for consistency and clarity
5 changes: 5 additions & 0 deletions .changeset/few-planets-rush.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@balancer/sdk": patch
---

Refactor sender and recipient to exist on v2 only
5 changes: 5 additions & 0 deletions .changeset/hungry-seahorses-rest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@balancer/sdk": minor
---

Refactor useNativeAssetAsWrappedAmountIn adn toNativeAsset into wethIsEth
5 changes: 5 additions & 0 deletions .changeset/mean-numbers-change.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@balancer/sdk": minor
---

Move accountAddress from query to buildCall input
12 changes: 5 additions & 7 deletions examples/addLiquidityNested.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,10 @@ const addLiquidityNested = async () => {
},
];

const useNativeAssetAsWrappedAmountIn = false;

const addLiquidityInput: AddLiquidityNestedInput = {
amountsIn,
chainId,
rpcUrl,
accountAddress,
useNativeAssetAsWrappedAmountIn,
};

// Calculate price impact to ensure it's acceptable
Expand Down Expand Up @@ -100,16 +96,18 @@ const addLiquidityNested = async () => {
client,
);

const wethIsEth = false;

const { call, to, value, minBptOut } = addLiquidityNested.buildCall({
...queryOutput,
slippage,
sender: accountAddress,
recipient: accountAddress,
accountAddress,
relayerApprovalSignature: signature,
wethIsEth,
});

let tokensIn = queryOutput.amountsIn.map((a) => a.token);
if (useNativeAssetAsWrappedAmountIn) {
if (wethIsEth) {
tokensIn = replaceWrapped(tokensIn, chainId);
}

Expand Down
1 change: 0 additions & 1 deletion examples/removeLiquidity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ const removeLiquidity = async () => {
sender: userAccount,
recipient: userAccount,
chainId,
wethIsEth: !!removeLiquidityInput.toNativeAsset,
});

console.log('\nWith slippage applied:');
Expand Down
183 changes: 183 additions & 0 deletions examples/removeLiquidityNested.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
/**
* Example showing how to remove liquidity to a pool.
* (Runs against a local Anvil fork)
*
* Run with:
* pnpm example ./examples/removeLiquidityNested.ts
*/

import { config } from 'dotenv';
config();

import {
Client,
createTestClient,
http,
parseUnits,
PublicActions,
publicActions,
TestActions,
WalletActions,
walletActions,
} from 'viem';

import {
Address,
BALANCER_RELAYER,
BalancerApi,
ChainId,
CHAINS,
NestedPoolState,
PriceImpact,
Relayer,
replaceWrapped,
Slippage,
RemoveLiquidityNested,
RemoveLiquidityNestedSingleTokenInput,
} from '../src';
import { forkSetup, sendTransactionGetBalances } from '../test/lib/utils';
import { ANVIL_NETWORKS, startFork } from '../test/anvil/anvil-global-setup';

const balancerApiUrl = 'https://backend-v3-canary.beets-ftm-node.com/graphql';
const poolId =
'0x08775ccb6674d6bdceb0797c364c2653ed84f3840002000000000000000004f0'; // WETH-3POOL
const chainId = ChainId.MAINNET;

const removeLiquidityNested = async () => {
// User approve vault to spend their tokens and update user balance
const { client, accountAddress, nestedPoolState, rpcUrl } =
await exampleSetup();

// setup remove liquidity helper
const removeLiquidityNested = new RemoveLiquidityNested();

const bptAmountIn = parseUnits('1', 18);
const tokenOut = '0x6b175474e89094c44da98b954eedeac495271d0f' as Address; // DAI

const removeLiquidityInput: RemoveLiquidityNestedSingleTokenInput = {
bptAmountIn,
chainId,
rpcUrl,
tokenOut,
};

// Calculate price impact to ensure it's acceptable
const priceImpact = await PriceImpact.removeLiquidityNested(
removeLiquidityInput,
nestedPoolState,
);
console.log(`\nPrice Impact: ${priceImpact.percentage.toFixed(2)}%`);

const queryOutput = await removeLiquidityNested.query(
removeLiquidityInput,
nestedPoolState,
);

console.log('\nRemove Liquidity Query Output:');
console.table({
tokensOut: queryOutput.amountsOut.map((a) => a.token.address),
amountsOut: queryOutput.amountsOut.map((a) => a.amount),
});
console.log(`BPT In: ${queryOutput.bptAmountIn.amount.toString()}`);

// build remove liquidity nested call with expected minAmountsOut based on slippage
const slippage = Slippage.fromPercentage('1'); // 1%

const signature = await Relayer.signRelayerApproval(
BALANCER_RELAYER[chainId],
accountAddress,
client,
);

const wethIsEth = false;

const { call, to, minAmountsOut } = removeLiquidityNested.buildCall({
...queryOutput,
slippage,
accountAddress,
relayerApprovalSignature: signature,
wethIsEth,
});

let tokensOut = queryOutput.amountsOut.map((a) => a.token);
if (wethIsEth) {
tokensOut = replaceWrapped(tokensOut, chainId);
}

console.log('\nWith slippage applied:');
console.table({
tokensOut: minAmountsOut.map((a) => a.token.address),
minAmountsOut: minAmountsOut.map((a) => a.amount),
});

const tokens = [
...tokensOut.map((t) => t.address),
queryOutput.bptAmountIn.token.address,
];

// send remove liquidity nested transaction and check balance changes
const { transactionReceipt, balanceDeltas } =
await sendTransactionGetBalances(
tokens,
client,
accountAddress,
to,
call,
);
console.log(`\nTransaction status: ${transactionReceipt.status}`);
console.log('Token balance deltas:');
console.table({
tokens,
balanceDeltas,
});
};

const exampleSetup = async (): Promise<{
client: Client & PublicActions & TestActions & WalletActions;
accountAddress: Address;
nestedPoolState: NestedPoolState;
rpcUrl: string;
}> => {
const { rpcUrl } = await startFork(ANVIL_NETWORKS[ChainId[chainId]]);
const balancerApi = new BalancerApi(balancerApiUrl, chainId);
const nestedPoolState =
await balancerApi.nestedPools.fetchNestedPoolState(poolId);

const client: Client & PublicActions & TestActions & WalletActions =
createTestClient({
mode: 'anvil',
chain: CHAINS[chainId],
transport: http(rpcUrl),
})
.extend(publicActions)
.extend(walletActions);

const accountAddress = (await client.getAddresses())[0];

const rootPool = nestedPoolState.pools.sort((a, b) => b.level - a.level)[0];

const testTokens = [
{
address: rootPool.address,
balance: parseUnits('1000', 18),
slot: 0,
},
];

await forkSetup(
client,
accountAddress,
testTokens.map((t) => t.address),
testTokens.map((t) => t.slot),
testTokens.map((t) => t.balance),
);

return {
client,
accountAddress,
nestedPoolState,
rpcUrl,
};
};

export default removeLiquidityNested;
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@ import { VAULT, MAX_UINT256, ZERO_ADDRESS } from '@/utils';
import { vaultV2Abi } from '@/abi';
import {
AddLiquidityBase,
AddLiquidityBuildOutput,
AddLiquidityBuildCallOutput,
AddLiquidityInput,
AddLiquidityKind,
AddLiquidityComposableStableQueryOutput,
AddLiquidityComposableStableCall,
} from '@/entities/addLiquidity/types';
import {
AddLiquidityAmounts as AddLiquidityAmountsBase,
Expand All @@ -22,6 +20,11 @@ import {
parseAddLiquidityArgs,
} from '@/entities/utils';
import { ComposableStableEncoder } from '@/entities/encoders/composableStable';
import { getValue } from '../../../utils/getValue';
import {
AddLiquidityV2ComposableStableBuildCallInput,
AddLiquidityV2ComposableStableQueryOutput,
} from './types';

type AddLiquidityAmounts = AddLiquidityAmountsBase & {
maxAmountsInWithoutBpt: bigint[];
Expand All @@ -31,7 +34,7 @@ export class AddLiquidityComposableStable implements AddLiquidityBase {
public async query(
input: AddLiquidityInput,
poolState: PoolState,
): Promise<AddLiquidityComposableStableQueryOutput> {
): Promise<AddLiquidityV2ComposableStableQueryOutput> {
const sortedTokens = getSortedTokens(poolState.tokens, input.chainId);
const bptIndex = sortedTokens.findIndex(
(t) => t.address === poolState.address,
Expand All @@ -44,16 +47,14 @@ export class AddLiquidityComposableStable implements AddLiquidityBase {
);

const { args, tokensIn } = parseAddLiquidityArgs({
useNativeAssetAsWrappedAmountIn:
!!input.useNativeAssetAsWrappedAmountIn,
chainId: input.chainId,
sortedTokens,
poolId: poolState.id,
sender: ZERO_ADDRESS,
recipient: ZERO_ADDRESS,
maxAmountsIn: amounts.maxAmountsIn,
userData,
fromInternalBalance: input.fromInternalBalance ?? false,
fromInternalBalance: false, // This isn't required for the query
});

const queryOutput = await doAddLiquidityQuery(
Expand All @@ -76,15 +77,15 @@ export class AddLiquidityComposableStable implements AddLiquidityBase {
bptOut,
amountsIn,
tokenInIndex: amounts.tokenInIndex,
fromInternalBalance: !!input.fromInternalBalance,
bptIndex,
chainId: input.chainId,
vaultVersion: 2,
bptIndex,
};
}

public buildCall(
input: AddLiquidityComposableStableCall,
): AddLiquidityBuildOutput {
input: AddLiquidityV2ComposableStableBuildCallInput,
): AddLiquidityBuildCallOutput {
const amounts = this.getAmountsCall(input);

const userData = ComposableStableEncoder.encodeAddLiquidityUserData(
Expand All @@ -97,7 +98,8 @@ export class AddLiquidityComposableStable implements AddLiquidityBase {
sortedTokens: input.amountsIn.map((a) => a.token),
maxAmountsIn: amounts.maxAmountsIn,
userData,
fromInternalBalance: input.fromInternalBalance,
fromInternalBalance: !!input.fromInternalBalance,
wethIsEth: !!input.wethIsEth,
});

const call = encodeFunctionData({
Expand All @@ -106,14 +108,10 @@ export class AddLiquidityComposableStable implements AddLiquidityBase {
args,
});

const value = input.amountsIn.find(
(a) => a.token.address === ZERO_ADDRESS,
)?.amount;

return {
call,
to: VAULT[input.chainId],
value: value === undefined ? 0n : value,
value: getValue(input.amountsIn, !!input.wethIsEth),
minBptOut: TokenAmount.fromRawAmount(
input.bptOut.token,
amounts.minimumBpt,
Expand Down Expand Up @@ -178,7 +176,7 @@ export class AddLiquidityComposableStable implements AddLiquidityBase {
}

private getAmountsCall(
input: AddLiquidityComposableStableCall,
input: AddLiquidityV2ComposableStableBuildCallInput,
): AddLiquidityAmounts {
let addLiquidityAmounts: AddLiquidityAmountsBase;
switch (input.addLiquidityKind) {
Expand Down
13 changes: 13 additions & 0 deletions src/entities/addLiquidity/addLiquidityV2/composableStable/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import {
AddLiquidityV2BaseBuildCallInput,
AddLiquidityV2BaseQueryOutput,
} from '../types';

export type AddLiquidityV2ComposableStableQueryOutput =
AddLiquidityV2BaseQueryOutput & {
bptIndex: number;
};

export type AddLiquidityV2ComposableStableBuildCallInput =
AddLiquidityV2BaseBuildCallInput &
AddLiquidityV2ComposableStableQueryOutput;
Loading

0 comments on commit f811425

Please sign in to comment.