Skip to content

Commit 13bd2f7

Browse files
committed
Implement GMX_ACCOUNT_PSEUDO_CHAIN_ID and refactor related components to enhance multichain support. Update token handling in various components to utilize the new pseudo chain ID, improving balance management and user experience across the application.
1 parent b765ced commit 13bd2f7

26 files changed

+471
-345
lines changed

sdk/src/configs/chains.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ export type ContractsChainId =
4848
| typeof BOTANIX
4949
| typeof ARBITRUM_SEPOLIA;
5050
export type ContractsChainIdProduction = Exclude<ContractsChainId, typeof AVALANCHE_FUJI | typeof ARBITRUM_SEPOLIA>;
51+
export const GMX_ACCOUNT_PSEUDO_CHAIN_ID = 0;
52+
export type GmxAccountPseudoChainId = typeof GMX_ACCOUNT_PSEUDO_CHAIN_ID;
5153

5254
export type SettlementChainId = typeof ARBITRUM_SEPOLIA | typeof ARBITRUM | typeof AVALANCHE;
5355
export type SourceChainId =

sdk/src/configs/markets.ts

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@
22
This files is used to pre-build data during the build process.
33
Avoid adding client-side code here, as it can break the build process.
44
*/
5+
import { zeroAddress } from "viem";
6+
7+
import type { ERC20Address, NativeTokenSupportedAddress, Token } from "types/tokens";
8+
59
import { ARBITRUM, ARBITRUM_SEPOLIA, AVALANCHE, AVALANCHE_FUJI, BOTANIX, ContractsChainId } from "./chains";
6-
import { getTokenBySymbol } from "./tokens";
10+
import { convertTokenAddress, getToken, getTokenBySymbol } from "./tokens";
711

812
export const SWAP_GRAPH_MAX_MARKETS_PER_TOKEN = 5;
913

@@ -1232,3 +1236,67 @@ export const fixTokenSymbolFromMarketLabel = (chainId: ContractsChainId, symbol:
12321236
}
12331237
return symbol;
12341238
};
1239+
1240+
export function isMarketTokenAddress(chainId: number, marketTokenAddress: string): boolean {
1241+
return Boolean(MARKETS[chainId]?.[marketTokenAddress]);
1242+
}
1243+
1244+
export function getTokenAddressByMarket(
1245+
chainId: number,
1246+
marketTokenAddress: string,
1247+
tokenType: "long" | "short" | "index"
1248+
): ERC20Address | NativeTokenSupportedAddress {
1249+
const market = MARKETS[chainId as ContractsChainId][marketTokenAddress];
1250+
1251+
if (tokenType === "index") {
1252+
return convertTokenAddress(chainId, market.indexTokenAddress, "native")! as
1253+
| ERC20Address
1254+
| NativeTokenSupportedAddress;
1255+
}
1256+
1257+
if (tokenType === "long") {
1258+
return market.longTokenAddress as ERC20Address;
1259+
}
1260+
1261+
return market.shortTokenAddress as ERC20Address;
1262+
}
1263+
1264+
export function getMarketIndexToken(chainId: number, marketTokenAddress: string): Token {
1265+
const indexTokenAddress = getTokenAddressByMarket(chainId, marketTokenAddress, "index");
1266+
1267+
return getToken(chainId, indexTokenAddress);
1268+
}
1269+
1270+
export function getMarketIndexTokenSymbol(chainId: number, marketTokenAddress: string): string {
1271+
const indexToken = getMarketIndexToken(chainId, marketTokenAddress);
1272+
if (!indexToken) {
1273+
return "";
1274+
}
1275+
1276+
return indexToken.symbol;
1277+
}
1278+
1279+
export function getMarketLongTokenSymbol(chainId: number, marketTokenAddress: string): string {
1280+
const longTokenAddress = getTokenAddressByMarket(chainId, marketTokenAddress, "long");
1281+
1282+
return getToken(chainId, convertTokenAddress(chainId, longTokenAddress, "native")).symbol;
1283+
}
1284+
1285+
export function getMarketShortTokenSymbol(chainId: number, marketTokenAddress: string): string {
1286+
const shortTokenAddress = getTokenAddressByMarket(chainId, marketTokenAddress, "short");
1287+
1288+
return getToken(chainId, convertTokenAddress(chainId, shortTokenAddress, "native")).symbol;
1289+
}
1290+
1291+
export function getIsSpotOnlyMarket(chainId: number, marketTokenAddress: string): boolean {
1292+
return MARKETS[chainId as ContractsChainId]?.[marketTokenAddress]?.indexTokenAddress === zeroAddress;
1293+
}
1294+
1295+
export function getMarketIsSameCollaterals(chainId: number, marketTokenAddress: string): boolean {
1296+
const market = MARKETS[chainId]?.[marketTokenAddress];
1297+
if (!market) {
1298+
return false;
1299+
}
1300+
1301+
return market.longTokenAddress === market.shortTokenAddress;
1302+
}

sdk/src/utils/markets.ts

Lines changed: 4 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
1-
import { zeroAddress } from "viem";
2-
3-
import { ContractsChainId } from "configs/chains";
41
import { BASIS_POINTS_DIVISOR } from "configs/factors";
5-
import { MARKETS, MarketConfig } from "configs/markets";
6-
import { convertTokenAddress, getToken, getTokenVisualMultiplier, NATIVE_TOKEN_ADDRESS } from "configs/tokens";
7-
import { ContractMarketPrices, Market, MarketInfo } from "types/markets";
8-
import { NativeTokenSupportedAddress, Token, TokenPrices, TokensData } from "types/tokens";
2+
import type { MarketConfig } from "configs/markets";
3+
import { getTokenVisualMultiplier, NATIVE_TOKEN_ADDRESS } from "configs/tokens";
4+
import type { ContractMarketPrices, Market, MarketInfo } from "types/markets";
5+
import type { Token, TokenPrices, TokensData } from "types/tokens";
96

107
import { applyFactor, PRECISION } from "./numbers";
118
import { getByKey } from "./objects";
@@ -236,79 +233,3 @@ export function getIsMarketAvailableForExpressSwaps(marketInfo: MarketInfo) {
236233
(token) => token.hasPriceFeedProvider
237234
);
238235
}
239-
240-
export function isMarketTokenAddress(chainId: number, marketTokenAddress: string): boolean {
241-
return Boolean(MARKETS[chainId]?.[marketTokenAddress]);
242-
}
243-
244-
export function getMarketIndexTokenAddress(
245-
chainId: number,
246-
marketTokenAddress: string
247-
): NativeTokenSupportedAddress | undefined {
248-
return convertTokenAddress(chainId, MARKETS[chainId]?.[marketTokenAddress]?.indexTokenAddress, "native");
249-
}
250-
251-
export function getMarketIndexToken(chainId: number, marketTokenAddress: string): Token | undefined {
252-
const indexTokenAddress = getMarketIndexTokenAddress(chainId, marketTokenAddress);
253-
if (!indexTokenAddress) {
254-
return undefined;
255-
}
256-
257-
return getToken(chainId, indexTokenAddress);
258-
}
259-
260-
export function getMarketIndexTokenSymbol(chainId: number, marketTokenAddress: string): string {
261-
const indexToken = getMarketIndexToken(chainId, marketTokenAddress);
262-
if (!indexToken) {
263-
return "";
264-
}
265-
266-
return indexToken.symbol;
267-
}
268-
269-
export function getMarketLongTokenAddress(chainId: number, marketTokenAddress: string): string {
270-
return MARKETS[chainId][marketTokenAddress].longTokenAddress;
271-
}
272-
273-
export function getMarketShortTokenAddress(chainId: number, marketTokenAddress: string): string {
274-
return MARKETS[chainId][marketTokenAddress].shortTokenAddress;
275-
}
276-
277-
export function getMarketLongToken(chainId: number, marketTokenAddress: string): Token {
278-
return getToken(chainId, getMarketLongTokenAddress(chainId, marketTokenAddress))!;
279-
}
280-
281-
export function getMarketShortToken(chainId: number, marketTokenAddress: string): Token {
282-
return getToken(chainId, getMarketShortTokenAddress(chainId, marketTokenAddress))!;
283-
}
284-
285-
export function getMarketLongTokenSymbol(chainId: number, marketTokenAddress: string): string {
286-
const longTokenAddress = MARKETS[chainId]?.[marketTokenAddress]?.longTokenAddress;
287-
if (!longTokenAddress) {
288-
return "";
289-
}
290-
291-
return getToken(chainId, convertTokenAddress(chainId, longTokenAddress, "native")).symbol;
292-
}
293-
294-
export function getMarketShortTokenSymbol(chainId: number, marketTokenAddress: string): string {
295-
const shortTokenAddress = MARKETS[chainId]?.[marketTokenAddress]?.shortTokenAddress;
296-
if (!shortTokenAddress) {
297-
return "";
298-
}
299-
300-
return getToken(chainId, convertTokenAddress(chainId, shortTokenAddress, "native")).symbol;
301-
}
302-
303-
export function getIsSpotOnlyMarket(chainId: number, marketTokenAddress: string): boolean {
304-
return MARKETS[chainId as ContractsChainId]?.[marketTokenAddress]?.indexTokenAddress === zeroAddress;
305-
}
306-
307-
export function getMarketIsSameCollaterals(chainId: number, marketTokenAddress: string): boolean {
308-
const market = MARKETS[chainId]?.[marketTokenAddress];
309-
if (!market) {
310-
return false;
311-
}
312-
313-
return market.longTokenAddress === market.shortTokenAddress;
314-
}

src/components/BridgeModal/BridgeInModal.tsx

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
import { t } from "@lingui/macro";
2-
import mapValues from "lodash/mapValues";
3-
import pickBy from "lodash/pickBy";
42
import { ReactNode, useEffect, useMemo, useState } from "react";
53
import { useAccount } from "wagmi";
64

7-
import { AnyChainId, SettlementChainId, SourceChainId } from "config/chains";
5+
import {
6+
type AnyChainId,
7+
GMX_ACCOUNT_PSEUDO_CHAIN_ID,
8+
type GmxAccountPseudoChainId,
9+
type SettlementChainId,
10+
type SourceChainId,
11+
} from "config/chains";
812
import { isSourceChain } from "config/multichain";
913
import { getSourceChainDecimalsMapped } from "config/multichain";
1014
import { useGmxAccountSettlementChainId } from "context/GmxAccountContext/hooks";
@@ -19,6 +23,7 @@ import { convertToUsd, getMidPrice, getTokenData, TokenBalanceType } from "domai
1923
import { useMaxAvailableAmount } from "domain/tokens/useMaxAvailableAmount";
2024
import { useChainId } from "lib/chains";
2125
import { helperToast } from "lib/helperToast";
26+
import { EMPTY_OBJECT } from "lib/objects";
2227
import { getMarketIndexName } from "sdk/utils/markets";
2328
import { adjustForDecimals, formatBalanceAmount, formatUsd, parseValue } from "sdk/utils/numbers";
2429

@@ -84,23 +89,25 @@ export function BridgeInModal({
8489
? multichainMarketTokenBalances?.balances[bridgeInChain]?.balance
8590
: undefined;
8691

87-
const sourceChainBalancesForSelector: Partial<Record<AnyChainId | 0, bigint>> =
92+
const sourceChainBalancesForSelector: Partial<Record<AnyChainId | GmxAccountPseudoChainId, bigint>> =
8893
multichainMarketTokenBalances?.balances
89-
? mapValues(
90-
pickBy(multichainMarketTokenBalances.balances, (data, chainIdStr) => {
91-
const chainIdNum = Number(chainIdStr);
92-
return data && chainIdNum !== chainId && chainIdNum !== 0;
93-
}),
94-
(data) => data.balance
94+
? Object.fromEntries(
95+
Object.entries(multichainMarketTokenBalances.balances)
96+
.filter(([chainIdStr, data]) => {
97+
const chainIdNum = Number(chainIdStr);
98+
return data && chainIdNum !== chainId && chainIdNum !== GMX_ACCOUNT_PSEUDO_CHAIN_ID;
99+
})
100+
.map(([chainIdStr, data]) => [chainIdStr, data.balance])
95101
)
96-
: {};
102+
: EMPTY_OBJECT;
97103

98104
const sourceChainDecimals =
99105
bridgeInChain && glvOrMarketAddress
100106
? getSourceChainDecimalsMapped(chainId, bridgeInChain, glvOrMarketAddress)
101107
: undefined;
102108

103-
const gmxAccountMarketTokenBalance: bigint | undefined = multichainMarketTokenBalances?.balances[0]?.balance;
109+
const gmxAccountMarketTokenBalance: bigint | undefined =
110+
multichainMarketTokenBalances?.balances[GMX_ACCOUNT_PSEUDO_CHAIN_ID]?.balance;
104111

105112
const nextGmxAccountMarketTokenBalance: bigint | undefined =
106113
gmxAccountMarketTokenBalance !== undefined && bridgeInAmount !== undefined
@@ -125,7 +132,11 @@ export function BridgeInModal({
125132

126133
const firstChainWithBalance = Object.entries(multichainMarketTokenBalances.balances).find(([chainIdStr, data]) => {
127134
const chainIdNum = Number(chainIdStr);
128-
if (!isSourceChain(chainIdNum) || (chainIdNum as number) === chainId || (chainIdNum as number) === 0) {
135+
if (
136+
!isSourceChain(chainIdNum) ||
137+
(chainIdNum as number) === chainId ||
138+
(chainIdNum as number) === GMX_ACCOUNT_PSEUDO_CHAIN_ID
139+
) {
129140
return false;
130141
}
131142
return data?.balance !== undefined && data.balance > 0n;

src/components/BridgeModal/BridgeOutModal.tsx

Lines changed: 62 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,21 @@ import Skeleton from "react-loading-skeleton";
44
import { Address, encodeAbiParameters } from "viem";
55
import { useAccount } from "wagmi";
66

7-
import { getChainName, SettlementChainId, SourceChainId } from "config/chains";
7+
import {
8+
ContractsChainId,
9+
getChainName,
10+
GMX_ACCOUNT_PSEUDO_CHAIN_ID,
11+
SettlementChainId,
12+
SourceChainId,
13+
} from "config/chains";
814
import { getChainIcon } from "config/icons";
915
import { getLayerZeroEndpointId, getStargatePoolAddress } from "config/multichain";
1016
import { useGmxAccountSettlementChainId } from "context/GmxAccountContext/hooks";
1117
import { selectMultichainMarketTokenBalances } from "context/PoolsDetailsContext/selectors/selectMultichainMarketTokenBalances";
1218
import { selectDepositMarketTokensData } from "context/SyntheticsStateContext/selectors/globalSelectors";
1319
import { useSelector } from "context/SyntheticsStateContext/utils";
1420
import { useArbitraryError, useArbitraryRelayParamsAndPayload } from "domain/multichain/arbitraryRelayParams";
15-
import { BridgeOutParams } from "domain/multichain/types";
21+
import type { BridgeOutParams } from "domain/multichain/types";
1622
import { buildAndSignBridgeOutTxn } from "domain/synthetics/express/expressOrderUtils";
1723
import { ExpressTransactionBuilder } from "domain/synthetics/express/types";
1824
import { getGlvOrMarketAddress, GlvOrMarketInfo } from "domain/synthetics/markets";
@@ -89,7 +95,8 @@ export function BridgeOutModal({
8995

9096
const networks = useGmxAccountWithdrawNetworks();
9197

92-
const gmxAccountMarketTokenBalance: bigint | undefined = multichainMarketTokenBalances?.balances[0]?.balance;
98+
const gmxAccountMarketTokenBalance: bigint | undefined =
99+
multichainMarketTokenBalances?.balances[GMX_ACCOUNT_PSEUDO_CHAIN_ID]?.balance;
93100

94101
const nextGmxAccountMarketTokenBalance: bigint | undefined =
95102
gmxAccountMarketTokenBalance !== undefined && bridgeOutAmount !== undefined
@@ -107,39 +114,12 @@ export function BridgeOutModal({
107114
tokenBalanceType: TokenBalanceType.GmxAccount,
108115
});
109116

110-
const bridgeOutParams: BridgeOutParams | undefined = useMemo(() => {
111-
if (
112-
bridgeOutChain === undefined ||
113-
glvOrMarketAddress === undefined ||
114-
bridgeOutAmount === undefined ||
115-
bridgeOutAmount <= 0n
116-
) {
117-
return;
118-
}
119-
120-
const dstEid = getLayerZeroEndpointId(bridgeOutChain);
121-
const stargateAddress = getStargatePoolAddress(chainId, glvOrMarketAddress);
122-
123-
if (dstEid === undefined || stargateAddress === undefined) {
124-
return;
125-
}
126-
127-
return {
128-
token: glvOrMarketAddress as Address,
129-
amount: bridgeOutAmount,
130-
minAmountOut: 0n,
131-
data: encodeAbiParameters(
132-
[
133-
{
134-
type: "uint32",
135-
name: "dstEid",
136-
},
137-
],
138-
[dstEid]
139-
),
140-
provider: stargateAddress,
141-
};
142-
}, [bridgeOutChain, glvOrMarketAddress, bridgeOutAmount, chainId]);
117+
const bridgeOutParams = useBridgeOutParams({
118+
bridgeOutChain,
119+
glvOrMarketAddress,
120+
bridgeOutAmount,
121+
chainId,
122+
});
143123

144124
const expressTransactionBuilder: ExpressTransactionBuilder | undefined = useMemo(() => {
145125
if (account === undefined || bridgeOutParams === undefined || bridgeOutChain === undefined) {
@@ -400,3 +380,49 @@ function NetworkItem({ option }: { option: { id: number; name: string } }) {
400380
</div>
401381
);
402382
}
383+
384+
function useBridgeOutParams({
385+
bridgeOutChain,
386+
glvOrMarketAddress,
387+
bridgeOutAmount,
388+
chainId,
389+
}: {
390+
bridgeOutChain: SourceChainId | undefined;
391+
glvOrMarketAddress: string | undefined;
392+
bridgeOutAmount: bigint | undefined;
393+
chainId: ContractsChainId;
394+
}): BridgeOutParams | undefined {
395+
return useMemo(() => {
396+
if (
397+
bridgeOutChain === undefined ||
398+
glvOrMarketAddress === undefined ||
399+
bridgeOutAmount === undefined ||
400+
bridgeOutAmount <= 0n
401+
) {
402+
return;
403+
}
404+
405+
const dstEid = getLayerZeroEndpointId(bridgeOutChain);
406+
const stargateAddress = getStargatePoolAddress(chainId, glvOrMarketAddress);
407+
408+
if (dstEid === undefined || stargateAddress === undefined) {
409+
return;
410+
}
411+
412+
return {
413+
token: glvOrMarketAddress as Address,
414+
amount: bridgeOutAmount,
415+
minAmountOut: 0n,
416+
data: encodeAbiParameters(
417+
[
418+
{
419+
type: "uint32",
420+
name: "dstEid",
421+
},
422+
],
423+
[dstEid]
424+
),
425+
provider: stargateAddress,
426+
};
427+
}, [bridgeOutChain, glvOrMarketAddress, bridgeOutAmount, chainId]);
428+
}

0 commit comments

Comments
 (0)