Skip to content

Commit 19376a0

Browse files
committed
Fix token switching
1 parent dbf2111 commit 19376a0

3 files changed

Lines changed: 85 additions & 18 deletions

File tree

packages/arb-token-bridge-ui/src/components/TransferPanel/hooks/useNetworkSwitchSelectedTokenAddress.test.ts

Lines changed: 65 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ describe('useNetworkSwitchSelectedTokenAddress', () => {
2727

2828
beforeEach(() => {
2929
vi.clearAllMocks();
30-
3130
mockedUseIsSwapTransfer.mockReturnValue(false);
3231
mockedUseSelectedToken.mockReturnValue([
3332
{
@@ -50,10 +49,10 @@ describe('useNetworkSwitchSelectedTokenAddress', () => {
5049
});
5150
});
5251

53-
it('returns undefined (keeps the current token) when the resolved destination token is the same logical token', () => {
54-
// Ethereum (PYUSD) to ArbitrumOne (PYUSD OFT)
55-
// Different addresses but same "logical" token
56-
// Returns undefined to keep the current token
52+
it('returns the resolved destination token address when the source token changes across a network switch', () => {
53+
// Ethereum -> Arbitrum One:
54+
// the selected source token is L1 PYUSD, but after switching networks
55+
// the new source token must become the resolved Arbitrum-side token.
5756
mockedUseSelectedToken.mockReturnValue([
5857
{
5958
type: TokenType.ERC20,
@@ -76,14 +75,70 @@ describe('useNetworkSwitchSelectedTokenAddress', () => {
7675
listIds: new Set(['1']),
7776
});
7877

78+
const { result } = renderHook(useNetworkSwitchSelectedTokenAddress);
79+
expect(result.current).toBe(CommonAddress.ArbitrumOne.PYUSDOFT);
80+
});
81+
82+
it('returns the L1 PYUSD address when switching away from Arbitrum One PYUSD OFT', () => {
83+
// Arbitrum One -> Ethereum:
84+
// the current source token is Arbitrum PYUSD OFT, and after switching
85+
// networks the new source token must become Ethereum PYUSD.
86+
mockedUseSelectedToken.mockReturnValue([
87+
{
88+
type: TokenType.ERC20,
89+
address: CommonAddress.ArbitrumOne.PYUSDOFT,
90+
importLookupAddress: CommonAddress.Ethereum.PYUSD,
91+
name: 'PYUSD OFT',
92+
symbol: 'PYUSD',
93+
decimals: 6,
94+
listIds: new Set(['1']),
95+
},
96+
vi.fn(),
97+
]);
98+
mockedUseDestinationToken.mockReturnValue({
99+
type: TokenType.ERC20,
100+
address: CommonAddress.Ethereum.PYUSD,
101+
importLookupAddress: CommonAddress.Ethereum.PYUSD,
102+
name: 'PYUSD',
103+
symbol: 'PYUSD',
104+
decimals: 6,
105+
listIds: new Set(['1']),
106+
});
107+
108+
const { result } = renderHook(useNetworkSwitchSelectedTokenAddress);
109+
expect(result.current).toBe(CommonAddress.Ethereum.PYUSD);
110+
});
111+
112+
it('returns undefined when the resolved destination token keeps the same address', () => {
113+
// If switching networks would keep the exact same token address,
114+
// the hook should leave query state untouched.
115+
mockedUseSelectedToken.mockReturnValue([
116+
{
117+
type: TokenType.ERC20,
118+
address: CommonAddress.Ethereum.WETH,
119+
name: 'Wrapped Ether',
120+
symbol: 'WETH',
121+
decimals: 18,
122+
listIds: new Set(['1']),
123+
},
124+
vi.fn(),
125+
]);
126+
mockedUseDestinationToken.mockReturnValue({
127+
type: TokenType.ERC20,
128+
address: CommonAddress.Ethereum.WETH,
129+
name: 'Wrapped Ether',
130+
symbol: 'WETH',
131+
decimals: 18,
132+
listIds: new Set(['1']),
133+
});
134+
79135
const { result } = renderHook(useNetworkSwitchSelectedTokenAddress);
80136
expect(result.current).toBeUndefined();
81137
});
82138

83139
it('returns the resolved destination token address for swaps', () => {
84-
// LiFi swap from Ethereum (PYUSD) to ArbitrumOne (PYUSD OFT)
85-
// Different addresses but same "logical" token
86-
// Returns the destination token (PYUSD OFT)
140+
// In swap mode, the selected token should always follow the resolved
141+
// destination token instead of trying to preserve the current source token.
87142
mockedUseIsSwapTransfer.mockReturnValue(true);
88143
mockedUseDestinationToken.mockReturnValue({
89144
type: TokenType.ERC20,
@@ -100,8 +155,8 @@ describe('useNetworkSwitchSelectedTokenAddress', () => {
100155
});
101156

102157
it('returns null for native swaps without a destination token', () => {
103-
// LiFi swaps to native currency
104-
// Returns null to clear the selected token
158+
// For swaps that resolve to the native asset, there is no ERC20 token
159+
// address to keep selected, so the hook clears the token query param.
105160
mockedUseIsSwapTransfer.mockReturnValue(true);
106161
mockedUseDestinationToken.mockReturnValue(null);
107162

packages/arb-token-bridge-ui/src/components/TransferPanel/hooks/useNetworkSwitchSelectedTokenAddress.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { useMemo } from 'react';
22

33
import { useDestinationToken } from '../../../hooks/useDestinationToken';
44
import { useSelectedToken } from '../../../hooks/useSelectedToken';
5-
import { areEquivalentBridgeTokens } from '../../../util/BridgeTokenAddressUtils';
5+
import { addressesEqual } from '../../../util/AddressUtils';
66
import { useIsSwapTransfer } from './useIsSwapTransfer';
77

88
/**
@@ -23,7 +23,7 @@ export function useNetworkSwitchSelectedTokenAddress() {
2323
return destinationToken?.address ?? null;
2424
}
2525

26-
if (!destinationToken || areEquivalentBridgeTokens(destinationToken, selectedToken)) {
26+
if (!destinationToken || addressesEqual(destinationToken.address, selectedToken?.address)) {
2727
return undefined;
2828
}
2929

packages/arb-token-bridge-ui/src/hooks/__tests__/useDestinationToken.test.ts

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ describe.sequential('useDestinationToken', () => {
161161
expect(result.current).toBeNull();
162162
});
163163

164-
it('maps Ethereum PYUSD deposits to Arbitrum One PYUSD OFT', () => {
164+
it('maps Ethereum PYUSD deposits to Arbitrum One PYUSD OFT when both query params are the L1 address', () => {
165165
mockedUseSelectedToken.mockReturnValue([
166166
getEthereumPyusdToken({
167167
priceUSD: 1,
@@ -171,7 +171,11 @@ describe.sequential('useDestinationToken', () => {
171171
]);
172172

173173
mockedUseArbQueryParams.mockReturnValue([
174-
{ ...defaultQueryParams, destinationToken: CommonAddress.Ethereum.PYUSD },
174+
{
175+
...defaultQueryParams,
176+
token: CommonAddress.Ethereum.PYUSD,
177+
destinationToken: CommonAddress.Ethereum.PYUSD,
178+
},
175179
vi.fn(),
176180
]);
177181

@@ -202,7 +206,7 @@ describe.sequential('useDestinationToken', () => {
202206
expect(result.current?.logoURI).toBe(ETHEREUM_PYUSD_LOGO_URI);
203207
});
204208

205-
it('maps PYUSD OFT withdrawals back to Ethereum PYUSD with metadata', () => {
209+
it('maps PYUSD OFT withdrawals back to Ethereum PYUSD when both query params are the OFT address', () => {
206210
mockedUseNetworks.mockReturnValue([
207211
makeNetworksState(ChainId.ArbitrumOne, ChainId.Ethereum),
208212
vi.fn(),
@@ -217,7 +221,11 @@ describe.sequential('useDestinationToken', () => {
217221
]);
218222

219223
mockedUseArbQueryParams.mockReturnValue([
220-
{ ...defaultQueryParams, destinationToken: CommonAddress.ArbitrumOne.PYUSDOFT },
224+
{
225+
...defaultQueryParams,
226+
token: CommonAddress.ArbitrumOne.PYUSDOFT,
227+
destinationToken: CommonAddress.ArbitrumOne.PYUSDOFT,
228+
},
221229
vi.fn(),
222230
]);
223231
mockedGetTokenOverride.mockImplementation(({ fromToken }) =>
@@ -245,7 +253,7 @@ describe.sequential('useDestinationToken', () => {
245253
);
246254
});
247255

248-
it('maps PYUSD OFT withdrawals safely before bridgeTokens hydrate', () => {
256+
it('maps PYUSD OFT withdrawals safely before bridgeTokens hydrate when both query params are the OFT address', () => {
249257
mockedUseNetworks.mockReturnValue([
250258
makeNetworksState(ChainId.ArbitrumOne, ChainId.Ethereum),
251259
vi.fn(),
@@ -268,7 +276,11 @@ describe.sequential('useDestinationToken', () => {
268276
]);
269277

270278
mockedUseArbQueryParams.mockReturnValue([
271-
{ ...defaultQueryParams, destinationToken: CommonAddress.ArbitrumOne.PYUSDOFT },
279+
{
280+
...defaultQueryParams,
281+
token: CommonAddress.ArbitrumOne.PYUSDOFT,
282+
destinationToken: CommonAddress.ArbitrumOne.PYUSDOFT,
283+
},
272284
vi.fn(),
273285
]);
274286
mockedGetTokenOverride.mockImplementation(({ fromToken }) =>

0 commit comments

Comments
 (0)