@0xsquid/react-hooks 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.babelrc +7 -0
- package/.eslintignore +1 -0
- package/.eslintrc.json +44 -0
- package/dist/index.js +6 -0
- package/jest.config.ts +28 -0
- package/package.json +124 -0
- package/scripts/copyAssets.js +27 -0
- package/scripts/removeBuildTestFolder.js +28 -0
- package/scripts/replacePaths.js +37 -0
- package/src/assets/images/icons/wallets/binance.svg +4 -0
- package/src/assets/images/icons/wallets/bitget.svg +43 -0
- package/src/assets/images/icons/wallets/bitkeep.svg +9 -0
- package/src/assets/images/icons/wallets/blockchaindotcom.svg +11 -0
- package/src/assets/images/icons/wallets/brave.svg +18 -0
- package/src/assets/images/icons/wallets/c98.svg +3 -0
- package/src/assets/images/icons/wallets/coinbase.svg +5 -0
- package/src/assets/images/icons/wallets/cosmostation.svg +9 -0
- package/src/assets/images/icons/wallets/defiConnect.svg +25 -0
- package/src/assets/images/icons/wallets/exodus.svg +33 -0
- package/src/assets/images/icons/wallets/fetchai.svg +1 -0
- package/src/assets/images/icons/wallets/index.ts +43 -0
- package/src/assets/images/icons/wallets/keplr.svg +9 -0
- package/src/assets/images/icons/wallets/leap.svg +35 -0
- package/src/assets/images/icons/wallets/metamask.svg +48 -0
- package/src/assets/images/icons/wallets/okx.svg +1 -0
- package/src/assets/images/icons/wallets/rabby.svg +1 -0
- package/src/assets/images/icons/wallets/trustwallet.svg +11 -0
- package/src/assets/images/icons/wallets/walletConnect.svg +11 -0
- package/src/assets/images/icons/wallets/wallet_icon.svg +1 -0
- package/src/assets/images/icons/wallets/xdefi.svg +9 -0
- package/src/assets/images/icons/wallets/zerion.svg +11 -0
- package/src/connectors/ExodusConnector.ts +6 -0
- package/src/connectors/LedgerLiveConnector.ts +226 -0
- package/src/contracts/typechain/ERC20.d.ts +441 -0
- package/src/contracts/typechain/factories/ERC20__factory.d.ts +56 -0
- package/src/contracts/typechain/factories/ERC20__factory.ts +340 -0
- package/src/core/constants.ts +549 -0
- package/src/core/externalLinks.ts +26 -0
- package/src/core/index.ts +1 -0
- package/src/core/multicall3.ts +22 -0
- package/src/core/numbers.ts +44 -0
- package/src/core/providers/CosmosProvider.tsx +55 -0
- package/src/core/queries/queries-keys.ts +156 -0
- package/src/core/queries/react-query-config.ts +13 -0
- package/src/core/types/components.ts +16 -0
- package/src/core/types/config.ts +52 -0
- package/src/core/types/dex.ts +31 -0
- package/src/core/types/error.ts +52 -0
- package/src/core/types/event.ts +16 -0
- package/src/core/types/route.ts +21 -0
- package/src/core/types/swap.ts +8 -0
- package/src/core/types/tokens.ts +30 -0
- package/src/core/types/transaction.ts +84 -0
- package/src/core/types/types.ts +37 -0
- package/src/core/types/wallet.ts +83 -0
- package/src/hooks/chains/useSquidChains.ts +66 -0
- package/src/hooks/cosmos/useCosmos.ts +279 -0
- package/src/hooks/cosmos/useCosmosForChain.ts +33 -0
- package/src/hooks/index.ts +32 -0
- package/src/hooks/navigation/useKeyboardNavigation.ts +88 -0
- package/src/hooks/store/useSquidStore.ts +80 -0
- package/src/hooks/swap/useSwap.ts +359 -0
- package/src/hooks/tokens/useAllTokensWithBalance.ts +157 -0
- package/src/hooks/tokens/useBalance.ts +180 -0
- package/src/hooks/tokens/useMultiChainBalance.ts +59 -0
- package/src/hooks/tokens/usePrices.ts +52 -0
- package/src/hooks/tokens/useSingleTokenPrice.ts +45 -0
- package/src/hooks/tokens/useSquidTokens.ts +48 -0
- package/src/hooks/tokens/useTokensWithBalance.ts +169 -0
- package/src/hooks/transaction/useEstimate.ts +397 -0
- package/src/hooks/transaction/useEstimatePriceImpact.ts +39 -0
- package/src/hooks/transaction/useExecuteTransaction.ts +440 -0
- package/src/hooks/transaction/useExpress.ts +46 -0
- package/src/hooks/transaction/useGetRoute.ts +145 -0
- package/src/hooks/transaction/useSingleTransaction.ts +155 -0
- package/src/hooks/transaction/useTransaction.ts +734 -0
- package/src/hooks/user/useUserParams.ts +63 -0
- package/src/hooks/wallet/useAddToken.ts +53 -0
- package/src/hooks/wallet/useAutoConnect.ts +56 -0
- package/src/hooks/wallet/useGnosisContext.ts +84 -0
- package/src/hooks/wallet/useIntegratorContext.ts +44 -0
- package/src/hooks/wallet/useMultiChainWallet.ts +146 -0
- package/src/hooks/wallet/useWallet.ts +86 -0
- package/src/hooks/webapp-router/useSquidRouter.ts +42 -0
- package/src/index.ts +5 -0
- package/src/provider/index.tsx +105 -0
- package/src/react-app-env.d.ts +2 -0
- package/src/services/external/analyticsService.ts +46 -0
- package/src/services/external/coingeckoService.ts +35 -0
- package/src/services/external/rpcService.ts +196 -0
- package/src/services/external/secretService.ts +178 -0
- package/src/services/index.ts +1 -0
- package/src/services/internal/apiService.ts +6 -0
- package/src/services/internal/assetsService.ts +84 -0
- package/src/services/internal/configService.ts +261 -0
- package/src/services/internal/errorService.ts +124 -0
- package/src/services/internal/eventService.ts +93 -0
- package/src/services/internal/priceService.ts +10 -0
- package/src/services/internal/transactionService.ts +255 -0
- package/src/services/internal/transactionStatusService.ts +293 -0
- package/src/services/internal/walletService.ts +158 -0
- package/src/tests/assetsService.test.ts +176 -0
- package/src/tests/configService.test.ts +486 -0
- package/src/tests/fetchSquidData.ts +43 -0
- package/src/tests/jest-svg-transform.ts +19 -0
- package/src/tests/sample.json +0 -0
- package/src/tests/walletService.test.ts +178 -0
- package/tsconfig.json +30 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import {
|
|
2
|
+
shareSubgraphId,
|
|
3
|
+
sortTokensBySharedSubgraphIds,
|
|
4
|
+
filterChains,
|
|
5
|
+
} from "@/services/internal/assetsService";
|
|
6
|
+
import { OnlineSquidData, fetchSquidData } from "./fetchSquidData";
|
|
7
|
+
|
|
8
|
+
let squid: OnlineSquidData | undefined = undefined;
|
|
9
|
+
let CHAINS: OnlineSquidData["chains"] = {};
|
|
10
|
+
let TOKENS: OnlineSquidData["tokens"] = {};
|
|
11
|
+
|
|
12
|
+
describe("assetsService", () => {
|
|
13
|
+
beforeAll(async () => {
|
|
14
|
+
squid = await fetchSquidData();
|
|
15
|
+
CHAINS = squid?.chains;
|
|
16
|
+
TOKENS = squid?.tokens;
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
describe("shareSubgraphId", () => {
|
|
20
|
+
test("should return false when no token has subgraph ids", () => {
|
|
21
|
+
expect(shareSubgraphId(TOKENS.BASE.USDBC, TOKENS.AVALANCHE.MAI)).toBe(
|
|
22
|
+
false
|
|
23
|
+
);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("should return true when tokens share subgraph ids", () => {
|
|
27
|
+
expect(shareSubgraphId(TOKENS.AVALANCHE.USDC, TOKENS.ETHEREUM.USDC)).toBe(
|
|
28
|
+
true
|
|
29
|
+
);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("should return false when tokens do not share subgraph ids", () => {
|
|
33
|
+
expect(shareSubgraphId(TOKENS.AVALANCHE.AVAX, TOKENS.ETHEREUM.ETH)).toBe(
|
|
34
|
+
false
|
|
35
|
+
);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe("sortTokensBySharedSubgraphIds", () => {
|
|
40
|
+
test("should sort 'a' token first when it shares subgraph ids with the token to check", () => {
|
|
41
|
+
expect(
|
|
42
|
+
sortTokensBySharedSubgraphIds({
|
|
43
|
+
a: TOKENS.AVALANCHE.USDC,
|
|
44
|
+
b: TOKENS.ETHEREUM.WBTC,
|
|
45
|
+
tokenToCheck: TOKENS.ETHEREUM.USDC,
|
|
46
|
+
})
|
|
47
|
+
).toBe(-1);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("should sort 'b' token first when it shares subgraph ids with the token to check", () => {
|
|
51
|
+
expect(
|
|
52
|
+
sortTokensBySharedSubgraphIds({
|
|
53
|
+
a: TOKENS.AVALANCHE.USDC,
|
|
54
|
+
b: TOKENS.ETHEREUM.ETH,
|
|
55
|
+
tokenToCheck: TOKENS.ETHEREUM.WETH,
|
|
56
|
+
})
|
|
57
|
+
).toBe(1);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("should not change order when tokens do not share subgraph ids with the token to check", () => {
|
|
61
|
+
expect(
|
|
62
|
+
sortTokensBySharedSubgraphIds({
|
|
63
|
+
a: TOKENS.AVALANCHE.AVAX,
|
|
64
|
+
b: TOKENS.ETHEREUM.ETH,
|
|
65
|
+
tokenToCheck: TOKENS.ETHEREUM.USDC,
|
|
66
|
+
})
|
|
67
|
+
).toBe(0);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test("should not change the order when both tokens share subgraph ids with the token to check", () => {
|
|
71
|
+
expect(
|
|
72
|
+
sortTokensBySharedSubgraphIds({
|
|
73
|
+
a: TOKENS.ETHEREUM.USDC,
|
|
74
|
+
b: TOKENS.AVALANCHE.axlUSDC,
|
|
75
|
+
tokenToCheck: TOKENS.AVALANCHE.USDC,
|
|
76
|
+
})
|
|
77
|
+
).toBe(0);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe("filterChains", () => {
|
|
82
|
+
test("return chains without changes when no chain is disabled or coming soon", () => {
|
|
83
|
+
const chains = [CHAINS.AVALANCHE, CHAINS.ETHEREUM, CHAINS.NOBLE];
|
|
84
|
+
|
|
85
|
+
expect(
|
|
86
|
+
filterChains({
|
|
87
|
+
chains,
|
|
88
|
+
config: {
|
|
89
|
+
comingSoonChainIds: undefined,
|
|
90
|
+
disabledChains: undefined,
|
|
91
|
+
},
|
|
92
|
+
direction: "from",
|
|
93
|
+
})
|
|
94
|
+
).toEqual(chains);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("sorts disabled chains to the end", () => {
|
|
98
|
+
expect(
|
|
99
|
+
filterChains({
|
|
100
|
+
chains: [CHAINS.MOONBEAM, CHAINS.POLYGON, CHAINS.BINANCE],
|
|
101
|
+
config: {
|
|
102
|
+
comingSoonChainIds: undefined,
|
|
103
|
+
disabledChains: { source: [CHAINS.POLYGON.chainId] },
|
|
104
|
+
},
|
|
105
|
+
direction: "from",
|
|
106
|
+
})
|
|
107
|
+
).toEqual([CHAINS.MOONBEAM, CHAINS.BINANCE, CHAINS.POLYGON]);
|
|
108
|
+
|
|
109
|
+
expect(
|
|
110
|
+
filterChains({
|
|
111
|
+
chains: [CHAINS.POLYGON, CHAINS.CELO, CHAINS.BINANCE],
|
|
112
|
+
config: {
|
|
113
|
+
comingSoonChainIds: undefined,
|
|
114
|
+
disabledChains: { destination: [CHAINS.CELO.chainId] },
|
|
115
|
+
},
|
|
116
|
+
direction: "to",
|
|
117
|
+
})
|
|
118
|
+
).toEqual([CHAINS.POLYGON, CHAINS.BINANCE, CHAINS.CELO]);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test("sorts coming soon chains to the end", () => {
|
|
122
|
+
expect(
|
|
123
|
+
filterChains({
|
|
124
|
+
chains: [CHAINS.LINEA, CHAINS.FANTOM, CHAINS.CELO],
|
|
125
|
+
config: {
|
|
126
|
+
disabledChains: undefined,
|
|
127
|
+
comingSoonChainIds: [CHAINS.LINEA.chainId],
|
|
128
|
+
},
|
|
129
|
+
direction: "from",
|
|
130
|
+
})
|
|
131
|
+
).toEqual([CHAINS.FANTOM, CHAINS.CELO, CHAINS.LINEA]);
|
|
132
|
+
|
|
133
|
+
expect(
|
|
134
|
+
filterChains({
|
|
135
|
+
chains: [CHAINS.NOBLE, CHAINS.OPTIMISM, CHAINS.MANTLE],
|
|
136
|
+
config: {
|
|
137
|
+
disabledChains: undefined,
|
|
138
|
+
comingSoonChainIds: [CHAINS.NOBLE.chainId],
|
|
139
|
+
},
|
|
140
|
+
direction: "to",
|
|
141
|
+
})
|
|
142
|
+
).toEqual([CHAINS.OPTIMISM, CHAINS.MANTLE, CHAINS.NOBLE]);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test("sorts available chains first, then disabled chains, and finally coming soon chains", () => {
|
|
146
|
+
const chains = [
|
|
147
|
+
CHAINS.MOONBEAM,
|
|
148
|
+
CHAINS.POLYGON,
|
|
149
|
+
CHAINS.BINANCE,
|
|
150
|
+
CHAINS.LINEA,
|
|
151
|
+
CHAINS.FANTOM,
|
|
152
|
+
CHAINS.CELO,
|
|
153
|
+
];
|
|
154
|
+
|
|
155
|
+
expect(
|
|
156
|
+
filterChains({
|
|
157
|
+
chains,
|
|
158
|
+
config: {
|
|
159
|
+
disabledChains: {
|
|
160
|
+
source: [CHAINS.MOONBEAM.chainId, CHAINS.LINEA.chainId],
|
|
161
|
+
},
|
|
162
|
+
comingSoonChainIds: [CHAINS.POLYGON.chainId],
|
|
163
|
+
},
|
|
164
|
+
direction: "from",
|
|
165
|
+
})
|
|
166
|
+
).toEqual([
|
|
167
|
+
CHAINS.BINANCE,
|
|
168
|
+
CHAINS.FANTOM,
|
|
169
|
+
CHAINS.CELO,
|
|
170
|
+
CHAINS.MOONBEAM,
|
|
171
|
+
CHAINS.LINEA,
|
|
172
|
+
CHAINS.POLYGON,
|
|
173
|
+
]);
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
});
|
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
import { nativeEvmTokenAddress } from "@/core/constants";
|
|
2
|
+
import {
|
|
3
|
+
filterTokensForDestination,
|
|
4
|
+
getDefaultTokenAddressForChain,
|
|
5
|
+
getFirstAvailableChainId,
|
|
6
|
+
getInitialOrDefaultTokenAddressForChain,
|
|
7
|
+
} from "../services/internal/configService";
|
|
8
|
+
import { OnlineSquidData, fetchSquidData } from "./fetchSquidData";
|
|
9
|
+
|
|
10
|
+
let squid: OnlineSquidData | undefined = undefined;
|
|
11
|
+
let CHAINS: OnlineSquidData["chains"] = {};
|
|
12
|
+
let TOKENS: OnlineSquidData["tokens"] = {};
|
|
13
|
+
|
|
14
|
+
describe("config service", () => {
|
|
15
|
+
beforeAll(async () => {
|
|
16
|
+
squid = await fetchSquidData();
|
|
17
|
+
CHAINS = squid?.chains;
|
|
18
|
+
TOKENS = squid?.tokens;
|
|
19
|
+
});
|
|
20
|
+
describe("getFirstAvailableChainId", () => {
|
|
21
|
+
test("returns chain id of first chain when no initialAssets or comingSoonChainIds are specified", () => {
|
|
22
|
+
expect(
|
|
23
|
+
getFirstAvailableChainId({ integratorId: "" }, "from", [
|
|
24
|
+
CHAINS.ETHEREUM,
|
|
25
|
+
CHAINS.ARBITRUM,
|
|
26
|
+
CHAINS.POLYGON,
|
|
27
|
+
])
|
|
28
|
+
).toBe(CHAINS.ETHEREUM.chainId);
|
|
29
|
+
|
|
30
|
+
expect(
|
|
31
|
+
getFirstAvailableChainId(
|
|
32
|
+
{
|
|
33
|
+
integratorId: "",
|
|
34
|
+
comingSoonChainIds: [],
|
|
35
|
+
initialAssets: {},
|
|
36
|
+
},
|
|
37
|
+
"from",
|
|
38
|
+
[CHAINS.ETHEREUM, CHAINS.ARBITRUM, CHAINS.POLYGON]
|
|
39
|
+
)
|
|
40
|
+
).toBe(CHAINS.ETHEREUM.chainId);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("returns undefined when no chains are provided", () => {
|
|
44
|
+
expect(getFirstAvailableChainId({ integratorId: "" }, "from", [])).toBe(
|
|
45
|
+
undefined
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
expect(
|
|
49
|
+
getFirstAvailableChainId({ integratorId: "" }, "from", undefined)
|
|
50
|
+
).toBe(undefined);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("filters by a single comingSoonChainId", () => {
|
|
54
|
+
expect(
|
|
55
|
+
getFirstAvailableChainId(
|
|
56
|
+
{
|
|
57
|
+
integratorId: "",
|
|
58
|
+
comingSoonChainIds: [CHAINS.CELO.chainId],
|
|
59
|
+
},
|
|
60
|
+
"from",
|
|
61
|
+
[CHAINS.CELO, CHAINS.BASE, CHAINS.AVALANCHE]
|
|
62
|
+
)
|
|
63
|
+
).toBe(CHAINS.BASE.chainId);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("filters by multiple comingSoonChainIds", () => {
|
|
67
|
+
expect(
|
|
68
|
+
getFirstAvailableChainId(
|
|
69
|
+
{
|
|
70
|
+
integratorId: "",
|
|
71
|
+
comingSoonChainIds: [
|
|
72
|
+
CHAINS.AVALANCHE.chainId,
|
|
73
|
+
CHAINS.POLYGON.chainId,
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
"from",
|
|
77
|
+
[
|
|
78
|
+
CHAINS.POLYGON,
|
|
79
|
+
CHAINS.ARBITRUM,
|
|
80
|
+
CHAINS.AVALANCHE,
|
|
81
|
+
CHAINS.OPTIMISM,
|
|
82
|
+
CHAINS.ETHEREUM,
|
|
83
|
+
]
|
|
84
|
+
)
|
|
85
|
+
).toBe(CHAINS.ARBITRUM.chainId);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("returns undefined if all chain ids are also comingSoonChainIds", () => {
|
|
89
|
+
const allChains = Object.values(CHAINS);
|
|
90
|
+
const comingSoonChainIds = allChains.map((chain) => chain.chainId);
|
|
91
|
+
|
|
92
|
+
expect(
|
|
93
|
+
getFirstAvailableChainId(
|
|
94
|
+
{
|
|
95
|
+
integratorId: "",
|
|
96
|
+
comingSoonChainIds,
|
|
97
|
+
},
|
|
98
|
+
"from",
|
|
99
|
+
allChains
|
|
100
|
+
)
|
|
101
|
+
).toBe(undefined);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("returns chain id of initialAssets when its ids are present in chains provided", () => {
|
|
105
|
+
expect(
|
|
106
|
+
getFirstAvailableChainId(
|
|
107
|
+
{
|
|
108
|
+
integratorId: "",
|
|
109
|
+
initialAssets: {
|
|
110
|
+
from: {
|
|
111
|
+
address: nativeEvmTokenAddress,
|
|
112
|
+
chainId: CHAINS.POLYGON.chainId,
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
"from",
|
|
117
|
+
[CHAINS.OPTIMISM, CHAINS.BINANCE, CHAINS.POLYGON, CHAINS.ETHEREUM]
|
|
118
|
+
)
|
|
119
|
+
).toBe(CHAINS.POLYGON.chainId);
|
|
120
|
+
|
|
121
|
+
expect(
|
|
122
|
+
getFirstAvailableChainId(
|
|
123
|
+
{
|
|
124
|
+
integratorId: "",
|
|
125
|
+
initialAssets: {
|
|
126
|
+
to: {
|
|
127
|
+
address: nativeEvmTokenAddress,
|
|
128
|
+
chainId: CHAINS.AVALANCHE.chainId,
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
"to",
|
|
133
|
+
[CHAINS.MANTLE, CHAINS.BASE, CHAINS.AVALANCHE, CHAINS.MOONBEAM]
|
|
134
|
+
)
|
|
135
|
+
).toBe(CHAINS.AVALANCHE.chainId);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test("returns chain id of first chain when initialAssets chain ids are not present in chains provided", () => {
|
|
139
|
+
expect(
|
|
140
|
+
getFirstAvailableChainId(
|
|
141
|
+
{
|
|
142
|
+
integratorId: "",
|
|
143
|
+
initialAssets: {
|
|
144
|
+
from: {
|
|
145
|
+
address: nativeEvmTokenAddress,
|
|
146
|
+
chainId: CHAINS.ARBITRUM.chainId,
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
"from",
|
|
151
|
+
[CHAINS.BINANCE, CHAINS.ETHEREUM, CHAINS.POLYGON]
|
|
152
|
+
)
|
|
153
|
+
).toBe(CHAINS.BINANCE.chainId);
|
|
154
|
+
|
|
155
|
+
expect(
|
|
156
|
+
getFirstAvailableChainId(
|
|
157
|
+
{
|
|
158
|
+
integratorId: "",
|
|
159
|
+
initialAssets: {
|
|
160
|
+
to: {
|
|
161
|
+
address: nativeEvmTokenAddress,
|
|
162
|
+
chainId: CHAINS.MOONBEAM.chainId,
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
"to",
|
|
167
|
+
[CHAINS.ARBITRUM, CHAINS.AVALANCHE, CHAINS.LINEA]
|
|
168
|
+
)
|
|
169
|
+
).toBe(CHAINS.ARBITRUM.chainId);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test("returns chain id of second chain when initial asset chain is a coming soon chain", () => {
|
|
173
|
+
expect(
|
|
174
|
+
getFirstAvailableChainId(
|
|
175
|
+
{
|
|
176
|
+
integratorId: "",
|
|
177
|
+
comingSoonChainIds: [CHAINS.ARBITRUM.chainId],
|
|
178
|
+
initialAssets: {
|
|
179
|
+
from: {
|
|
180
|
+
address: nativeEvmTokenAddress,
|
|
181
|
+
chainId: CHAINS.ARBITRUM.chainId,
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
"from",
|
|
186
|
+
[CHAINS.ARBITRUM, CHAINS.FANTOM, CHAINS.MANTLE]
|
|
187
|
+
)
|
|
188
|
+
).toBe(CHAINS.FANTOM.chainId);
|
|
189
|
+
|
|
190
|
+
expect(
|
|
191
|
+
getFirstAvailableChainId(
|
|
192
|
+
{
|
|
193
|
+
integratorId: "",
|
|
194
|
+
comingSoonChainIds: [CHAINS.POLYGON.chainId],
|
|
195
|
+
initialAssets: {
|
|
196
|
+
to: {
|
|
197
|
+
address: nativeEvmTokenAddress,
|
|
198
|
+
chainId: CHAINS.POLYGON.chainId,
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
"to",
|
|
203
|
+
[CHAINS.POLYGON, CHAINS.BASE, CHAINS.AVALANCHE]
|
|
204
|
+
)
|
|
205
|
+
).toBe(CHAINS.BASE.chainId);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
test("returns chain id of second chain when first chain is disabled", () => {
|
|
209
|
+
expect(
|
|
210
|
+
getFirstAvailableChainId(
|
|
211
|
+
{
|
|
212
|
+
integratorId: "",
|
|
213
|
+
disabledChains: {
|
|
214
|
+
source: [CHAINS.ARBITRUM.chainId],
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
"from",
|
|
218
|
+
[CHAINS.ARBITRUM, CHAINS.FANTOM, CHAINS.MANTLE]
|
|
219
|
+
)
|
|
220
|
+
).toBe(CHAINS.FANTOM.chainId);
|
|
221
|
+
|
|
222
|
+
expect(
|
|
223
|
+
getFirstAvailableChainId(
|
|
224
|
+
{
|
|
225
|
+
integratorId: "",
|
|
226
|
+
disabledChains: {
|
|
227
|
+
destination: [CHAINS.NOBLE.chainId],
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
"to",
|
|
231
|
+
[CHAINS.NOBLE, CHAINS.AVALANCHE, CHAINS.SCROLL]
|
|
232
|
+
)
|
|
233
|
+
).toBe(CHAINS.AVALANCHE.chainId);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test("returns chain id of second chain when initial asset chain is disabled", () => {
|
|
237
|
+
expect(
|
|
238
|
+
getFirstAvailableChainId(
|
|
239
|
+
{
|
|
240
|
+
integratorId: "",
|
|
241
|
+
disabledChains: {
|
|
242
|
+
source: [CHAINS.OPTIMISM.chainId],
|
|
243
|
+
},
|
|
244
|
+
initialAssets: {
|
|
245
|
+
from: {
|
|
246
|
+
address: nativeEvmTokenAddress,
|
|
247
|
+
chainId: CHAINS.OPTIMISM.chainId,
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
"from",
|
|
252
|
+
[CHAINS.OPTIMISM, CHAINS.MOONBEAM, CHAINS.NOBLE]
|
|
253
|
+
)
|
|
254
|
+
).toBe(CHAINS.MOONBEAM.chainId);
|
|
255
|
+
|
|
256
|
+
expect(
|
|
257
|
+
getFirstAvailableChainId(
|
|
258
|
+
{
|
|
259
|
+
integratorId: "",
|
|
260
|
+
disabledChains: {
|
|
261
|
+
destination: [CHAINS.LINEA.chainId],
|
|
262
|
+
},
|
|
263
|
+
initialAssets: {
|
|
264
|
+
to: {
|
|
265
|
+
address: nativeEvmTokenAddress,
|
|
266
|
+
chainId: CHAINS.LINEA.chainId,
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
},
|
|
270
|
+
"to",
|
|
271
|
+
[CHAINS.LINEA, CHAINS.BASE, CHAINS.MANTLE]
|
|
272
|
+
)
|
|
273
|
+
).toBe(CHAINS.BASE.chainId);
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
describe("getDefaultTokenAddressForChain", () => {
|
|
278
|
+
test("returns first token address if no special config is specified", () => {
|
|
279
|
+
expect(
|
|
280
|
+
getDefaultTokenAddressForChain(
|
|
281
|
+
[TOKENS.ETHEREUM.USDT, TOKENS.ETHEREUM.ETH, TOKENS.ETHEREUM.WETH],
|
|
282
|
+
{
|
|
283
|
+
integratorId: "",
|
|
284
|
+
},
|
|
285
|
+
CHAINS.ETHEREUM.chainId
|
|
286
|
+
)
|
|
287
|
+
).toBe(TOKENS.ETHEREUM.USDT.address);
|
|
288
|
+
|
|
289
|
+
expect(
|
|
290
|
+
getDefaultTokenAddressForChain(
|
|
291
|
+
[TOKENS.ETHEREUM.WBTC, TOKENS.ETHEREUM.USDC, TOKENS.ETHEREUM.ETH],
|
|
292
|
+
{
|
|
293
|
+
defaultTokensPerChain: [],
|
|
294
|
+
integratorId: "",
|
|
295
|
+
},
|
|
296
|
+
CHAINS.ETHEREUM.chainId
|
|
297
|
+
)
|
|
298
|
+
).toBe(TOKENS.ETHEREUM.WBTC.address);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
test("returns undefined if no tokens are found for specified chain id", () => {
|
|
302
|
+
expect(
|
|
303
|
+
getDefaultTokenAddressForChain(
|
|
304
|
+
[TOKENS.BASE.USDbC, TOKENS.BASE.axlUSDC, TOKENS.BASE.WETH],
|
|
305
|
+
{
|
|
306
|
+
integratorId: "",
|
|
307
|
+
},
|
|
308
|
+
CHAINS.ETHEREUM.chainId
|
|
309
|
+
)
|
|
310
|
+
).toBe(undefined);
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
test("filters by defaultTokensPerChain config", () => {
|
|
314
|
+
expect(
|
|
315
|
+
getDefaultTokenAddressForChain(
|
|
316
|
+
[TOKENS.BASE.USDC, TOKENS.BASE.WETH, TOKENS.ETHEREUM.USDT],
|
|
317
|
+
{
|
|
318
|
+
defaultTokensPerChain: [
|
|
319
|
+
{
|
|
320
|
+
address: TOKENS.ETHEREUM.USDT.address,
|
|
321
|
+
chainId: TOKENS.ETHEREUM.USDT.chainId,
|
|
322
|
+
},
|
|
323
|
+
],
|
|
324
|
+
integratorId: "",
|
|
325
|
+
},
|
|
326
|
+
CHAINS.ETHEREUM.chainId
|
|
327
|
+
)
|
|
328
|
+
).toBe(TOKENS.ETHEREUM.USDT.address);
|
|
329
|
+
|
|
330
|
+
expect(
|
|
331
|
+
getDefaultTokenAddressForChain(
|
|
332
|
+
[TOKENS.ETHEREUM.WBTC, TOKENS.BASE.axlUSDC, TOKENS.BASE.ETH],
|
|
333
|
+
{
|
|
334
|
+
defaultTokensPerChain: [
|
|
335
|
+
{
|
|
336
|
+
address: TOKENS.BASE.axlUSDC.address,
|
|
337
|
+
chainId: TOKENS.BASE.axlUSDC.chainId,
|
|
338
|
+
},
|
|
339
|
+
],
|
|
340
|
+
integratorId: "",
|
|
341
|
+
},
|
|
342
|
+
CHAINS.BASE.chainId
|
|
343
|
+
)
|
|
344
|
+
).toBe(TOKENS.BASE.axlUSDC.address);
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
test("filters by unavailableTokenAddress", () => {
|
|
348
|
+
expect(
|
|
349
|
+
getDefaultTokenAddressForChain(
|
|
350
|
+
[TOKENS.ETHEREUM.WBTC, TOKENS.BASE.axlUSDC, TOKENS.BASE.USDbC],
|
|
351
|
+
{
|
|
352
|
+
integratorId: "",
|
|
353
|
+
},
|
|
354
|
+
CHAINS.BASE.chainId,
|
|
355
|
+
TOKENS.BASE.axlUSDC.address // unavailableTokenAddress
|
|
356
|
+
)
|
|
357
|
+
).toBe(TOKENS.BASE.USDbC.address);
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
test("filters when unavailableTokenAddress and default token are the same", () => {
|
|
361
|
+
expect(
|
|
362
|
+
getDefaultTokenAddressForChain(
|
|
363
|
+
[
|
|
364
|
+
TOKENS.BASE.WETH,
|
|
365
|
+
TOKENS.ETHEREUM.ETH,
|
|
366
|
+
TOKENS.BASE.USDC,
|
|
367
|
+
TOKENS.ETHEREUM.USDT,
|
|
368
|
+
],
|
|
369
|
+
{
|
|
370
|
+
integratorId: "",
|
|
371
|
+
defaultTokensPerChain: [
|
|
372
|
+
{
|
|
373
|
+
address: TOKENS.ETHEREUM.ETH.address,
|
|
374
|
+
chainId: TOKENS.ETHEREUM.ETH.chainId,
|
|
375
|
+
},
|
|
376
|
+
],
|
|
377
|
+
},
|
|
378
|
+
CHAINS.ETHEREUM.chainId,
|
|
379
|
+
TOKENS.ETHEREUM.ETH.address // unavailableTokenAddress
|
|
380
|
+
)
|
|
381
|
+
).toBe(TOKENS.ETHEREUM.USDT.address);
|
|
382
|
+
});
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
describe("filterTokensForDestination", () => {
|
|
386
|
+
test("returns different tokens than source token when from and to chains are the same", () => {
|
|
387
|
+
expect(
|
|
388
|
+
filterTokensForDestination(
|
|
389
|
+
[TOKENS.ETHEREUM.ETH, TOKENS.ETHEREUM.USDC, TOKENS.ETHEREUM.WETH],
|
|
390
|
+
CHAINS.ETHEREUM, // destination chain
|
|
391
|
+
TOKENS.ETHEREUM.ETH // source token
|
|
392
|
+
)
|
|
393
|
+
).toStrictEqual([TOKENS.ETHEREUM.USDC, TOKENS.ETHEREUM.WETH]);
|
|
394
|
+
|
|
395
|
+
expect(
|
|
396
|
+
filterTokensForDestination(
|
|
397
|
+
[TOKENS.BASE.USDbC, TOKENS.BASE.WETH, TOKENS.BASE.axlUSDC],
|
|
398
|
+
CHAINS.BASE, // destination chain
|
|
399
|
+
TOKENS.BASE.axlUSDC // source token
|
|
400
|
+
)
|
|
401
|
+
).toStrictEqual([TOKENS.BASE.USDbC, TOKENS.BASE.WETH]);
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
test("returns only tokens of same chain as destination chain", () => {
|
|
405
|
+
expect(
|
|
406
|
+
filterTokensForDestination(
|
|
407
|
+
[
|
|
408
|
+
TOKENS.ETHEREUM.USDC,
|
|
409
|
+
TOKENS.BASE.USDbC,
|
|
410
|
+
TOKENS.ETHEREUM.WETH,
|
|
411
|
+
TOKENS.BASE.ETH,
|
|
412
|
+
],
|
|
413
|
+
CHAINS.ETHEREUM, // destination chain
|
|
414
|
+
TOKENS.BASE.USDC // source token
|
|
415
|
+
)
|
|
416
|
+
).toStrictEqual([TOKENS.ETHEREUM.USDC, TOKENS.ETHEREUM.WETH]);
|
|
417
|
+
});
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
// we only test initialAssets here, as the rest of the logic is tested in getDefaultTokenAddressForChain
|
|
421
|
+
describe("getInitialOrDefaultTokenAddressForChain", () => {
|
|
422
|
+
test("returns initial assets when chain id matches", () => {
|
|
423
|
+
const fromInitialAsset = TOKENS.ETHEREUM.WETH;
|
|
424
|
+
|
|
425
|
+
expect(
|
|
426
|
+
getInitialOrDefaultTokenAddressForChain({
|
|
427
|
+
chainId: CHAINS.ETHEREUM.chainId,
|
|
428
|
+
config: {
|
|
429
|
+
integratorId: "",
|
|
430
|
+
initialAssets: {
|
|
431
|
+
from: fromInitialAsset,
|
|
432
|
+
},
|
|
433
|
+
},
|
|
434
|
+
direction: "from",
|
|
435
|
+
tokens: [TOKENS.ETHEREUM.USDT, fromInitialAsset],
|
|
436
|
+
})
|
|
437
|
+
).toBe(fromInitialAsset.address);
|
|
438
|
+
|
|
439
|
+
const toInitialAsset = TOKENS.AVALANCHE.MAI;
|
|
440
|
+
|
|
441
|
+
expect(
|
|
442
|
+
getInitialOrDefaultTokenAddressForChain({
|
|
443
|
+
chainId: CHAINS.AVALANCHE.chainId,
|
|
444
|
+
config: {
|
|
445
|
+
integratorId: "",
|
|
446
|
+
initialAssets: {
|
|
447
|
+
to: toInitialAsset,
|
|
448
|
+
},
|
|
449
|
+
},
|
|
450
|
+
direction: "to",
|
|
451
|
+
tokens: [TOKENS.AVALANCHE.axlUSDC, toInitialAsset],
|
|
452
|
+
})
|
|
453
|
+
).toBe(toInitialAsset.address);
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
test("returns first token when initial asset does not match chain id", () => {
|
|
457
|
+
expect(
|
|
458
|
+
getInitialOrDefaultTokenAddressForChain({
|
|
459
|
+
chainId: CHAINS.ETHEREUM.chainId, // we want to get Ethereum tokens
|
|
460
|
+
config: {
|
|
461
|
+
integratorId: "",
|
|
462
|
+
initialAssets: {
|
|
463
|
+
from: TOKENS.BASE.USDbC, // initial asset is a Base token, but we want to get Ethereum tokens
|
|
464
|
+
},
|
|
465
|
+
},
|
|
466
|
+
direction: "from",
|
|
467
|
+
tokens: [TOKENS.ETHEREUM.USDT, TOKENS.ETHEREUM.WETH],
|
|
468
|
+
})
|
|
469
|
+
).toBe(TOKENS.ETHEREUM.USDT.address);
|
|
470
|
+
|
|
471
|
+
expect(
|
|
472
|
+
getInitialOrDefaultTokenAddressForChain({
|
|
473
|
+
chainId: CHAINS.BASE.chainId, // we want to get Base tokens
|
|
474
|
+
config: {
|
|
475
|
+
integratorId: "",
|
|
476
|
+
initialAssets: {
|
|
477
|
+
to: TOKENS.AVALANCHE.AVAX, // initial asset is an Avalanche token, but we want to get Base tokens
|
|
478
|
+
},
|
|
479
|
+
},
|
|
480
|
+
direction: "from",
|
|
481
|
+
tokens: [TOKENS.BASE.ETH, TOKENS.BASE.axlUSDC],
|
|
482
|
+
})
|
|
483
|
+
).toBe(TOKENS.BASE.ETH.address);
|
|
484
|
+
});
|
|
485
|
+
});
|
|
486
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Squid } from "@0xsquid/sdk";
|
|
2
|
+
import { ChainData, Token } from "@0xsquid/squid-types";
|
|
3
|
+
|
|
4
|
+
const INTEGRATOR_ID = "squid-react-hooks";
|
|
5
|
+
|
|
6
|
+
export interface OnlineSquidData {
|
|
7
|
+
tokens: Record<string, Record<string, Token>>;
|
|
8
|
+
chains: Record<string, ChainData>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const remapSquidData = (squidData: Squid) => {
|
|
12
|
+
const tokens: Record<string, Record<string, Token>> = {};
|
|
13
|
+
const chains: Record<string, ChainData> = {};
|
|
14
|
+
|
|
15
|
+
squidData.chains.forEach((chain) => {
|
|
16
|
+
const chainName = chain.networkIdentifier.toUpperCase();
|
|
17
|
+
|
|
18
|
+
tokens[chainName] = squidData.tokens
|
|
19
|
+
.filter((token) => token.chainId === chain.chainId)
|
|
20
|
+
.reduce(
|
|
21
|
+
(acc, token) => {
|
|
22
|
+
const tokenSymbol = token.symbol;
|
|
23
|
+
acc[tokenSymbol] = token;
|
|
24
|
+
return acc;
|
|
25
|
+
},
|
|
26
|
+
{} as Record<string, Token>
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
chains[chainName] = chain;
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
return { tokens, chains };
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const fetchSquidData = async (): Promise<OnlineSquidData> => {
|
|
36
|
+
const squid = new Squid({
|
|
37
|
+
integratorId: INTEGRATOR_ID,
|
|
38
|
+
baseUrl: "https://v2.api.squidrouter.com",
|
|
39
|
+
});
|
|
40
|
+
await squid.init();
|
|
41
|
+
|
|
42
|
+
return remapSquidData(squid);
|
|
43
|
+
};
|