Skip to content
Open
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: 13 additions & 0 deletions .changeset/hip-flowers-march.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
"@balancer/sdk": major
---

**WHAT**: Removed wrapped token functionality from the `Token` class and introduced a new `NativeToken` class to handle native tokens (like ETH) separately.

**WHY**: This change improves type safety and separation of concerns by distinguishing between ERC-20 tokens and native tokens, making the API more explicit and preventing confusion about token types.

**HOW**: Update your code by:
- Replace any usage of `Token` for native tokens with the new `NativeToken` class if using the wrapped functionality
- If not using any wrapped functionality, the `Token` can remain as is
- Import `NativeToken` from the SDK: `import { NativeToken } from '@balancer/sdk'`
- Use `NativeToken` for native token operations instead of `Token` with wrapped functionality
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const validateBuildCallInput = (
if (input.wethIsEth) {
if (
!input.amountsIn.some((a) =>
a.token.isUnderlyingEqual(NATIVE_ASSETS[chainId]),
NATIVE_ASSETS[chainId].isWrapped(a.token),
)
) {
throw inputValidationError(
Expand Down
1 change: 1 addition & 0 deletions src/entities/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export * from './removeLiquidityNested/removeLiquidityNestedV3';
export * from './removeLiquidityNested/removeLiquidityNestedV3/types';
export * from './slippage';
export * from './token';
export * from './nativeToken';
export * from './tokenAmount';
export * from './types';
export * from './utils';
Expand Down
33 changes: 33 additions & 0 deletions src/entities/nativeToken.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Address } from 'viem';
import { Token } from './token';

/**
* NativeToken extends BaseToken and adds mandatory wrapped token functionality
* This class is specifically designed for native tokens that always have a wrapped version
*/
export class NativeToken extends Token {
public readonly wrapped: Address;

public constructor(
chainId: number,
address: Address,
decimals: number,
wrapped: Address,
symbol?: string,
name?: string,
) {
// Call parent constructor with core properties
super(chainId, address, decimals, symbol, name);

// Set wrapped address (always mandatory for native tokens)
this.wrapped = wrapped.toLowerCase() as Address;
}

public isWrappedEqual(token: NativeToken) {
return this.chainId === token.chainId && this.wrapped === token.wrapped;
}

public isWrapped(token: Token) {
return this.chainId === token.chainId && this.wrapped === token.address;
}
}
2 changes: 1 addition & 1 deletion src/entities/swap/swaps/v2/auraBalSwaps/joinPool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export function encodeJoinData(
);

const useNativeAsset =
wethIsEth && token.isUnderlyingEqual(NATIVE_ASSETS[ChainId.MAINNET]);
wethIsEth && NATIVE_ASSETS[ChainId.MAINNET].isWrapped(token);

const maxAmountsIn = Array(balWethAssets.length).fill(0n);
maxAmountsIn[tokenInIndex] = inputAmount;
Expand Down
10 changes: 1 addition & 9 deletions src/entities/token.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,32 @@
import { Address } from 'viem';
import { InputToken } from '../types';

export class Token {
public readonly chainId: number;
public readonly address: Address;
public readonly decimals: number;
public readonly symbol?: string;
public readonly name?: string;
public readonly wrapped: Address;

public constructor(
chainId: number,
address: Address,
decimals: number,
symbol?: string,
name?: string,
wrapped?: Address,
) {
this.chainId = chainId;
// Addresses are always lowercased for speed
this.address = address.toLowerCase() as Address;
this.decimals = decimals;
this.symbol = symbol;
this.name = name;
this.wrapped = (
wrapped ? wrapped.toLowerCase() : address.toLowerCase()
) as Address;
}

public isEqual(token: Token) {
return this.chainId === token.chainId && this.address === token.address;
}

public isUnderlyingEqual(token: Token) {
return this.chainId === token.chainId && this.wrapped === token.wrapped;
}

public isSameAddress(address: Address) {
return this.address === address.toLowerCase();
}
Expand Down
2 changes: 1 addition & 1 deletion src/entities/utils/getValue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const getValue = (
if (wethIsEth) {
value =
amountsIn.find((a) =>
a.token.isUnderlyingEqual(NATIVE_ASSETS[a.token.chainId]),
NATIVE_ASSETS[a.token.chainId].isWrapped(a.token),
)?.amount ?? 0n;
}
return value;
Expand Down
2 changes: 1 addition & 1 deletion src/entities/utils/replaceWrapped.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { NATIVE_ASSETS, ZERO_ADDRESS } from '../../utils';

export function replaceWrapped(tokens: Token[], chainId: number): Token[] {
return tokens.map((token) => {
if (token.isUnderlyingEqual(NATIVE_ASSETS[chainId])) {
if (NATIVE_ASSETS[chainId].isWrapped(token)) {
return new Token(chainId, ZERO_ADDRESS, 18);
}
return token;
Expand Down
66 changes: 33 additions & 33 deletions src/utils/constants.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Address, Chain } from 'viem';
import { Token } from '../entities/token';
import { NativeToken } from '../entities/nativeToken';
import {
arbitrum,
avalanche,
Expand Down Expand Up @@ -144,133 +144,133 @@ export const PERMIT2: Record<number, Address> = {
};

export const NATIVE_ASSETS = {
[ChainId.ARBITRUM_ONE]: new Token(
[ChainId.ARBITRUM_ONE]: new NativeToken(
ChainId.ARBITRUM_ONE,
NATIVE_ADDRESS,
18,
'0x82aF49447D8a07e3bd95BD0d56f35241523fBab1',
'ETH',
'Ether',
'0x82aF49447D8a07e3bd95BD0d56f35241523fBab1',
),
[ChainId.BASE]: new Token(
[ChainId.BASE]: new NativeToken(
ChainId.BASE,
NATIVE_ADDRESS,
18,
'0x4200000000000000000000000000000000000006',
'ETH',
'Ether',
'0x4200000000000000000000000000000000000006',
),
[ChainId.FANTOM]: new Token(
[ChainId.FANTOM]: new NativeToken(
ChainId.FANTOM,
NATIVE_ADDRESS,
18,
'0x21be370D5312f44cB42ce377BC9b8a0cEF1A4C83',
'FANTOM',
'Fantom',
'0x21be370D5312f44cB42ce377BC9b8a0cEF1A4C83',
),
[ChainId.FRAXTAL]: new Token(
[ChainId.FRAXTAL]: new NativeToken(
ChainId.FRAXTAL,
NATIVE_ADDRESS,
18,
'0xfc00000000000000000000000000000000000006',
'FRAXTAL',
'Fraxtal',
'0xfc00000000000000000000000000000000000006',
),
[ChainId.GNOSIS_CHAIN]: new Token(
[ChainId.GNOSIS_CHAIN]: new NativeToken(
ChainId.GNOSIS_CHAIN,
NATIVE_ADDRESS,
18,
'0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d',
'xDAI',
'xDAI',
'0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d',
),
[ChainId.GOERLI]: new Token(
[ChainId.GOERLI]: new NativeToken(
ChainId.GOERLI,
NATIVE_ADDRESS,
18,
'0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6',
'ETH',
'Ether',
'0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6',
),
[ChainId.MAINNET]: new Token(
[ChainId.MAINNET]: new NativeToken(
ChainId.MAINNET,
NATIVE_ADDRESS,
18,
'0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
'ETH',
'Ether',
'0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
),
[ChainId.MODE]: new Token(
[ChainId.MODE]: new NativeToken(
ChainId.MODE,
NATIVE_ADDRESS,
18,
'0x4200000000000000000000000000000000000006',
'ETH',
'Ether',
'0x4200000000000000000000000000000000000006',
),
[ChainId.OPTIMISM]: new Token(
[ChainId.OPTIMISM]: new NativeToken(
ChainId.OPTIMISM,
NATIVE_ADDRESS,
18,
'0x4200000000000000000000000000000000000006',
'ETH',
'Ether',
'0x4200000000000000000000000000000000000006',
),
[ChainId.POLYGON]: new Token(
[ChainId.POLYGON]: new NativeToken(
ChainId.POLYGON,
NATIVE_ADDRESS,
18,
'0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270',
'MATIC',
'Matic',
'0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270',
),
[ChainId.SEPOLIA]: new Token(
[ChainId.SEPOLIA]: new NativeToken(
ChainId.SEPOLIA,
NATIVE_ADDRESS,
18,
'0x7b79995e5f793a07bc00c21412e50ecae098e7f9',
'ETH',
'Ether',
'0x7b79995e5f793a07bc00c21412e50ecae098e7f9',
),
[ChainId.AVALANCHE]: new Token(
[ChainId.AVALANCHE]: new NativeToken(
ChainId.AVALANCHE,
NATIVE_ADDRESS,
18,
'0xb31f66aa3c1e785363f0875a1b74e27b85fd66c7',
'AVAX',
'Avax',
'0xb31f66aa3c1e785363f0875a1b74e27b85fd66c7',
),
[ChainId.ZKEVM]: new Token(
[ChainId.ZKEVM]: new NativeToken(
ChainId.ZKEVM,
NATIVE_ADDRESS,
18,
'0xa2036f0538221a77a3937f1379699f44945018d0',
'MATIC',
'Matic',
'0xa2036f0538221a77a3937f1379699f44945018d0',
),
[ChainId.SONIC]: new Token(
[ChainId.SONIC]: new NativeToken(
ChainId.SONIC,
NATIVE_ADDRESS,
18,
'0x039e2fb66102314ce7b64ce5ce3e5183bc94ad38',
'S',
'Sonic',
'0x039e2fb66102314ce7b64ce5ce3e5183bc94ad38',
),
[ChainId.HYPEREVM]: new Token(
[ChainId.HYPEREVM]: new NativeToken(
ChainId.HYPEREVM,
HYPEREVM_NATIVE_ADDRESS,
18,
'0x5555555555555555555555555555555555555555',
'HYPE',
'Hype',
'0x5555555555555555555555555555555555555555',
),
[ChainId.PLASMA]: new Token(
[ChainId.PLASMA]: new NativeToken(
ChainId.PLASMA,
NATIVE_ADDRESS,
18,
'0x6100E367285b01F48D07953803A2d8dCA5D19873',
'XPL',
'Xpl',
'0x6100E367285b01F48D07953803A2d8dCA5D19873',
),
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,17 +142,14 @@ async function testAuraBalSwap(

expect(transactionReceipt.status).to.equal('success');

if (
wethIsEth &&
tokenIn.isUnderlyingEqual(NATIVE_ASSETS[ChainId.MAINNET])
) {
if (wethIsEth && NATIVE_ASSETS[ChainId.MAINNET].isWrapped(tokenIn)) {
expect(queryOutput.inputAmount.amount).to.equal(balanceDeltas[2]);
expect(queryOutput.expectedAmountOut.amount).to.equal(balanceDeltas[1]);
expect(call.value).to.eq(queryOutput.inputAmount.amount);
expect(balanceDeltas[0]).to.eq(0n);
} else if (
wethIsEth &&
tokenOut.isUnderlyingEqual(NATIVE_ASSETS[ChainId.MAINNET])
NATIVE_ASSETS[ChainId.MAINNET].isWrapped(tokenOut)
) {
expect(queryOutput.inputAmount.amount).to.equal(balanceDeltas[0]);
expect(queryOutput.expectedAmountOut.amount).to.equal(balanceDeltas[2]);
Expand Down
2 changes: 1 addition & 1 deletion test/lib/utils/addLiquidityHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ import {
NATIVE_ASSETS,
PoolState,
Slippage,
Token,
TokenAmount,
VAULT_V2,
Permit2Helper,
ChainId,
isSameAddress,
PublicWalletClient,
missingParameterError,
Token,
} from 'src';
import { getTokensForBalanceCheck } from './getTokensForBalanceCheck';
import { TxOutput, sendTransactionGetBalances } from './helper';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import {
RemoveLiquidityUnbalancedInput,
RemoveLiquidityKind,
Slippage,
Token,
PoolState,
RemoveLiquidity,
Address,
Expand All @@ -28,6 +27,7 @@ import {
InputAmount,
PoolType,
RemoveLiquiditySingleTokenExactOutInput,
Token,
} from '../../../src';
import { forkSetup } from '../../lib/utils/helper';
import { RemoveLiquidityTxInput } from '../../lib/utils/types';
Expand Down