@tcswap/helpers 4.5.15

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.
Files changed (139) hide show
  1. package/dist/api/index.cjs +4 -0
  2. package/dist/api/index.cjs.map +16 -0
  3. package/dist/api/index.js +4 -0
  4. package/dist/api/index.js.map +16 -0
  5. package/dist/chunk-pfmeq01a.js +5 -0
  6. package/dist/chunk-pfmeq01a.js.map +9 -0
  7. package/dist/chunk-vb4wtm2w.js +4 -0
  8. package/dist/chunk-vb4wtm2w.js.map +9 -0
  9. package/dist/contracts.cjs +4 -0
  10. package/dist/contracts.cjs.map +10 -0
  11. package/dist/contracts.js +4 -0
  12. package/dist/contracts.js.map +10 -0
  13. package/dist/index.cjs +7 -0
  14. package/dist/index.cjs.map +30 -0
  15. package/dist/index.js +7 -0
  16. package/dist/index.js.map +30 -0
  17. package/dist/tokens.cjs +4 -0
  18. package/dist/tokens.cjs.map +10 -0
  19. package/dist/tokens.js +4 -0
  20. package/dist/tokens.js.map +10 -0
  21. package/dist/types/api/index.d.ts +502 -0
  22. package/dist/types/api/index.d.ts.map +1 -0
  23. package/dist/types/api/memoless/endpoints.d.ts +56 -0
  24. package/dist/types/api/memoless/endpoints.d.ts.map +1 -0
  25. package/dist/types/api/memoless/types.d.ts +85 -0
  26. package/dist/types/api/memoless/types.d.ts.map +1 -0
  27. package/dist/types/api/midgard/endpoints.d.ts +80 -0
  28. package/dist/types/api/midgard/endpoints.d.ts.map +1 -0
  29. package/dist/types/api/midgard/types.d.ts +543 -0
  30. package/dist/types/api/midgard/types.d.ts.map +1 -0
  31. package/dist/types/api/thornode/endpoints.d.ts +34 -0
  32. package/dist/types/api/thornode/endpoints.d.ts.map +1 -0
  33. package/dist/types/api/thornode/types.d.ts +264 -0
  34. package/dist/types/api/thornode/types.d.ts.map +1 -0
  35. package/dist/types/api/uswap/endpoints.d.ts +372 -0
  36. package/dist/types/api/uswap/endpoints.d.ts.map +1 -0
  37. package/dist/types/api/uswap/types.d.ts +1487 -0
  38. package/dist/types/api/uswap/types.d.ts.map +1 -0
  39. package/dist/types/contracts.d.ts +2 -0
  40. package/dist/types/contracts.d.ts.map +1 -0
  41. package/dist/types/index.d.ts +32 -0
  42. package/dist/types/index.d.ts.map +1 -0
  43. package/dist/types/modules/assetValue.d.ts +82 -0
  44. package/dist/types/modules/assetValue.d.ts.map +1 -0
  45. package/dist/types/modules/bigIntArithmetics.d.ts +60 -0
  46. package/dist/types/modules/bigIntArithmetics.d.ts.map +1 -0
  47. package/dist/types/modules/feeMultiplier.d.ts +48 -0
  48. package/dist/types/modules/feeMultiplier.d.ts.map +1 -0
  49. package/dist/types/modules/requestClient.d.ts +33 -0
  50. package/dist/types/modules/requestClient.d.ts.map +1 -0
  51. package/dist/types/modules/uSwapConfig.d.ts +249 -0
  52. package/dist/types/modules/uSwapConfig.d.ts.map +1 -0
  53. package/dist/types/modules/uSwapError.d.ts +879 -0
  54. package/dist/types/modules/uSwapError.d.ts.map +1 -0
  55. package/dist/types/modules/uSwapNumber.d.ts +10 -0
  56. package/dist/types/modules/uSwapNumber.d.ts.map +1 -0
  57. package/dist/types/tokens.d.ts +2 -0
  58. package/dist/types/tokens.d.ts.map +1 -0
  59. package/dist/types/types/commonTypes.d.ts +16 -0
  60. package/dist/types/types/commonTypes.d.ts.map +1 -0
  61. package/dist/types/types/derivationPath.d.ts +4 -0
  62. package/dist/types/types/derivationPath.d.ts.map +1 -0
  63. package/dist/types/types/errors/apiV1.d.ts +2 -0
  64. package/dist/types/types/errors/apiV1.d.ts.map +1 -0
  65. package/dist/types/types/index.d.ts +6 -0
  66. package/dist/types/types/index.d.ts.map +1 -0
  67. package/dist/types/types/quotes.d.ts +180 -0
  68. package/dist/types/types/quotes.d.ts.map +1 -0
  69. package/dist/types/types/sdk.d.ts +35 -0
  70. package/dist/types/types/sdk.d.ts.map +1 -0
  71. package/dist/types/types/wallet.d.ts +130 -0
  72. package/dist/types/types/wallet.d.ts.map +1 -0
  73. package/dist/types/utils/asset.d.ts +37 -0
  74. package/dist/types/utils/asset.d.ts.map +1 -0
  75. package/dist/types/utils/chains.d.ts +13 -0
  76. package/dist/types/utils/chains.d.ts.map +1 -0
  77. package/dist/types/utils/derivationPath.d.ts +21 -0
  78. package/dist/types/utils/derivationPath.d.ts.map +1 -0
  79. package/dist/types/utils/explorerUrls.d.ts +10 -0
  80. package/dist/types/utils/explorerUrls.d.ts.map +1 -0
  81. package/dist/types/utils/liquidity.d.ts +62 -0
  82. package/dist/types/utils/liquidity.d.ts.map +1 -0
  83. package/dist/types/utils/memo.d.ts +65 -0
  84. package/dist/types/utils/memo.d.ts.map +1 -0
  85. package/dist/types/utils/others.d.ts +15 -0
  86. package/dist/types/utils/others.d.ts.map +1 -0
  87. package/dist/types/utils/validators.d.ts +6 -0
  88. package/dist/types/utils/validators.d.ts.map +1 -0
  89. package/dist/types/utils/wallets.d.ts +36 -0
  90. package/dist/types/utils/wallets.d.ts.map +1 -0
  91. package/package.json +67 -0
  92. package/src/api/index.ts +15 -0
  93. package/src/api/memoless/endpoints.ts +62 -0
  94. package/src/api/memoless/types.ts +83 -0
  95. package/src/api/midgard/endpoints.ts +352 -0
  96. package/src/api/midgard/types.ts +515 -0
  97. package/src/api/thornode/endpoints.ts +109 -0
  98. package/src/api/thornode/types.ts +247 -0
  99. package/src/api/uswap/endpoints.ts +252 -0
  100. package/src/api/uswap/types.ts +626 -0
  101. package/src/contracts.ts +1 -0
  102. package/src/index.ts +32 -0
  103. package/src/modules/__tests__/assetValue.test.ts +2452 -0
  104. package/src/modules/__tests__/bigIntArithmetics.test.ts +410 -0
  105. package/src/modules/__tests__/feeMultiplier.test.ts +131 -0
  106. package/src/modules/__tests__/uSwapConfig.test.ts +429 -0
  107. package/src/modules/__tests__/uSwapNumber.test.ts +439 -0
  108. package/src/modules/assetValue.ts +536 -0
  109. package/src/modules/bigIntArithmetics.ts +366 -0
  110. package/src/modules/feeMultiplier.ts +84 -0
  111. package/src/modules/requestClient.ts +116 -0
  112. package/src/modules/uSwapConfig.ts +189 -0
  113. package/src/modules/uSwapError.ts +474 -0
  114. package/src/modules/uSwapNumber.ts +17 -0
  115. package/src/tokens.ts +1 -0
  116. package/src/types/commonTypes.ts +10 -0
  117. package/src/types/derivationPath.ts +11 -0
  118. package/src/types/errors/apiV1.ts +0 -0
  119. package/src/types/index.ts +5 -0
  120. package/src/types/quotes.ts +182 -0
  121. package/src/types/sdk.ts +38 -0
  122. package/src/types/wallet.ts +124 -0
  123. package/src/utils/__tests__/asset.test.ts +186 -0
  124. package/src/utils/__tests__/derivationPath.test.ts +142 -0
  125. package/src/utils/__tests__/explorerUrls.test.ts +59 -0
  126. package/src/utils/__tests__/liquidity.test.ts +302 -0
  127. package/src/utils/__tests__/memo.test.ts +99 -0
  128. package/src/utils/__tests__/others.test.ts +169 -0
  129. package/src/utils/__tests__/validators.test.ts +84 -0
  130. package/src/utils/__tests__/wallets.test.ts +625 -0
  131. package/src/utils/asset.ts +399 -0
  132. package/src/utils/chains.ts +104 -0
  133. package/src/utils/derivationPath.ts +101 -0
  134. package/src/utils/explorerUrls.ts +32 -0
  135. package/src/utils/liquidity.ts +154 -0
  136. package/src/utils/memo.ts +102 -0
  137. package/src/utils/others.ts +64 -0
  138. package/src/utils/validators.ts +36 -0
  139. package/src/utils/wallets.ts +238 -0
@@ -0,0 +1,399 @@
1
+ import type { TokenNames } from "@tcswap/tokens";
2
+ import { Chain, type EVMChain, EVMChains, getChainConfig, UTXOChains } from "@tcswap/types";
3
+ import { match } from "ts-pattern";
4
+ import type { AssetValue } from "../modules/assetValue";
5
+ import { RequestClient } from "../modules/requestClient";
6
+ import { getRPCUrl } from "./chains";
7
+
8
+ export type CommonAssetString = (typeof CommonAssetStrings)[number] | Chain;
9
+
10
+ export type ConditionalAssetValueReturn<T extends boolean> = T extends true ? Promise<AssetValue[]> : AssetValue[];
11
+
12
+ export const CommonAssetStrings = [
13
+ `${Chain.Maya}.MAYA`,
14
+ `${Chain.Maya}.CACAO`,
15
+ `${Chain.Ethereum}.THOR`,
16
+ `${Chain.Ethereum}.vTHOR`,
17
+ `${Chain.Kujira}.USK`,
18
+ `${Chain.Ethereum}.FLIP`,
19
+ `${Chain.Radix}.XRD`,
20
+ ] as const;
21
+
22
+ type RadixResourceResponse = {
23
+ at_ledger_state?: any;
24
+ manager: {
25
+ resource_type: string;
26
+ divisibility: { substate_type: string; is_locked: boolean; value: { divisibility: number } };
27
+ };
28
+ owner_role?: any;
29
+ };
30
+ const ethGasChains = [Chain.Arbitrum, Chain.Aurora, Chain.Base, Chain.Ethereum, Chain.Optimism] as const;
31
+
32
+ async function getRadixAssetDecimals(address: string) {
33
+ const { baseDecimal } = getChainConfig(Chain.Radix);
34
+
35
+ try {
36
+ const rpcUrl = await getRPCUrl(Chain.Radix);
37
+
38
+ const { manager } = await RequestClient.post<RadixResourceResponse>(`${rpcUrl}/state/resource`, {
39
+ body: JSON.stringify({ network: "mainnet", resource_address: address }),
40
+ headers: { Accept: "*/*", "Content-Type": "application/json" },
41
+ });
42
+
43
+ return manager?.divisibility?.value?.divisibility;
44
+ } catch (error) {
45
+ const errorMessage = error instanceof Error ? error.message : String(error);
46
+ console.warn(`Failed to fetch Radix asset decimals for ${address}: ${errorMessage}`);
47
+ return baseDecimal;
48
+ }
49
+ }
50
+
51
+ async function getRadixAssetTicker(address: string) {
52
+ try {
53
+ const rpcUrl = await getRPCUrl(Chain.Radix);
54
+
55
+ const response = await RequestClient.post<{
56
+ items: Array<{
57
+ address: string;
58
+ explicit_metadata?: { items: Array<{ key: string; value: { typed: { value: string; type: string } } }> };
59
+ }>;
60
+ }>(`${rpcUrl}/state/entity/details`, {
61
+ body: JSON.stringify({ addresses: [address], opt_ins: { explicit_metadata: ["symbol"] } }),
62
+ headers: { Accept: "*/*", "Content-Type": "application/json" },
63
+ });
64
+
65
+ const symbolMetadata = response.items[0]?.explicit_metadata?.items.find((item) => item.key === "symbol");
66
+ return symbolMetadata?.value.typed.value || undefined;
67
+ } catch (error) {
68
+ const errorMessage = error instanceof Error ? error.message : String(error);
69
+ console.warn(`Failed to fetch Radix asset symbol for ${address}: ${errorMessage}`);
70
+ return "";
71
+ }
72
+ }
73
+
74
+ async function callEVMContract({
75
+ chain,
76
+ address,
77
+ methodHex,
78
+ id,
79
+ }: {
80
+ chain: EVMChain;
81
+ address: string;
82
+ methodHex: string;
83
+ id: number;
84
+ }) {
85
+ const rpcUrl = await getRPCUrl(chain);
86
+ return RequestClient.post<{ result: string }>(rpcUrl, {
87
+ body: JSON.stringify({
88
+ id,
89
+ jsonrpc: "2.0",
90
+ method: "eth_call",
91
+ params: [{ data: methodHex, to: address.toLowerCase() }, "latest"],
92
+ }),
93
+ headers: { accept: "*/*", "cache-control": "no-cache", "content-type": "application/json" },
94
+ });
95
+ }
96
+
97
+ async function decodeABIString(hexResult: string) {
98
+ if (!hexResult || hexResult === "0x") return "UNKNOWN";
99
+
100
+ try {
101
+ const { AbiCoder } = await import("ethers");
102
+ const abiCoder = AbiCoder.defaultAbiCoder();
103
+ const decoded = abiCoder.decode(["string"], hexResult);
104
+ return decoded[0].trim();
105
+ } catch (error) {
106
+ console.warn(`Failed to decode ABI string from ${hexResult}: ${error}`);
107
+ return "UNKNOWN";
108
+ }
109
+ }
110
+
111
+ function decodeABIUint8(hexResult: string, fallback: number) {
112
+ if (!hexResult || hexResult === "0x") return fallback;
113
+
114
+ try {
115
+ return Number(hexResult);
116
+ } catch (error) {
117
+ console.warn(`Failed to decode ABI uint8 from ${hexResult}: ${error}`);
118
+ return fallback;
119
+ }
120
+ }
121
+
122
+ async function getEVMAssetDecimals({ chain, address }: { chain: EVMChain; address: string }) {
123
+ const { baseDecimal } = getChainConfig(chain);
124
+
125
+ const formattedAddress = address.toLowerCase();
126
+
127
+ if (address === "" || !formattedAddress.startsWith("0x")) return baseDecimal;
128
+
129
+ const decimalResponse = await callEVMContract({ address, chain, id: 2, methodHex: "0x313ce567" }).catch(
130
+ (error: Error) => {
131
+ console.warn(`Could not fetch decimals for ${address} on ${chain}: ${error.message}`);
132
+ return { result: "" };
133
+ },
134
+ );
135
+
136
+ const decimals = decodeABIUint8(decimalResponse.result, baseDecimal);
137
+ return decimals;
138
+ }
139
+
140
+ async function getEVMAssetTicker({ chain, address }: { chain: EVMChain; address: string }) {
141
+ const formattedAddress = address.toLowerCase();
142
+
143
+ if (formattedAddress === "" || !formattedAddress.startsWith("0x")) return undefined;
144
+
145
+ const tickerResponse = await callEVMContract({ address, chain, id: 1, methodHex: "0x95d89b41" }).catch(
146
+ (error: Error) => {
147
+ console.warn(`Could not fetch symbol for ${address} on ${chain}: ${error.message}`);
148
+ return { result: "" };
149
+ },
150
+ );
151
+
152
+ const ticker = await decodeABIString(tickerResponse.result);
153
+
154
+ return ticker;
155
+ }
156
+
157
+ export function fetchTokenInfo({ chain, address }: { chain: Chain; address: string }) {
158
+ const { baseDecimal } = getChainConfig(chain);
159
+ const defaultResult = { decimals: baseDecimal, ticker: undefined };
160
+
161
+ return match(chain)
162
+ .with(...EVMChains, async () => {
163
+ try {
164
+ const { isAddress, getAddress } = await import("ethers");
165
+
166
+ if (!isAddress(getAddress(address.replace(/^0X/, "0x")))) return defaultResult;
167
+
168
+ const [ticker, decimals] = await Promise.all([
169
+ getEVMAssetTicker({ address, chain: chain as EVMChain }),
170
+ getEVMAssetDecimals({ address, chain: chain as EVMChain }),
171
+ ]);
172
+
173
+ return { decimals, ticker };
174
+ } catch (error: any) {
175
+ console.warn(`Failed to fetch token info for ${address} on ${chain}: ${error?.code} ${error?.message}`);
176
+ return defaultResult;
177
+ }
178
+ })
179
+ .with(Chain.Solana, async () => {
180
+ if (!address) return defaultResult;
181
+
182
+ try {
183
+ const response = await fetch(`https://lite-api.jup.ag/tokens/v2/search?query=${address}`);
184
+ if (response.ok) {
185
+ const data = await response.json();
186
+ const token = Array.isArray(data) ? data[0] : data;
187
+ if (token) {
188
+ return { decimals: token.decimals ?? baseDecimal, ticker: token.symbol || undefined };
189
+ }
190
+ }
191
+ } catch (error: any) {
192
+ console.warn(`Failed to fetch Solana token info for ${address}: ${error?.code} ${error?.message}`);
193
+ }
194
+ return defaultResult;
195
+ })
196
+ .with(Chain.Tron, async () => {
197
+ if (!address) return defaultResult;
198
+
199
+ try {
200
+ const TW = await import("tronweb");
201
+ const TronWeb = TW.TronWeb ?? TW.default?.TronWeb;
202
+ const rpcUrl = await getRPCUrl(Chain.Tron);
203
+ const tronWeb = new TronWeb({
204
+ fullHost: rpcUrl,
205
+ // Set a default address for read-only calls (required by TronWeb)
206
+ privateKey: "0000000000000000000000000000000000000000000000000000000000000001",
207
+ });
208
+
209
+ const contract = await tronWeb.contract().at(address);
210
+
211
+ const [symbolResult, decimalsResult] = await Promise.all([
212
+ contract
213
+ .symbol()
214
+ .call()
215
+ .catch((error: Error) => {
216
+ console.warn(`Could not fetch symbol for ${address} on Tron:`, error);
217
+ return undefined;
218
+ }),
219
+ contract
220
+ .decimals()
221
+ .call()
222
+ .catch((error: Error) => {
223
+ console.warn(`Could not fetch decimals for ${address} on Tron:`, error);
224
+ return baseDecimal;
225
+ }),
226
+ ]);
227
+
228
+ return {
229
+ decimals: typeof decimalsResult === "number" ? decimalsResult : Number(decimalsResult || baseDecimal),
230
+ ticker: symbolResult || undefined,
231
+ };
232
+ } catch (error) {
233
+ const errorMessage = error instanceof Error ? error.message : String(error);
234
+ console.warn(`Failed to fetch Tron token info for ${address}: ${errorMessage}`);
235
+ return defaultResult;
236
+ }
237
+ })
238
+ .with(Chain.Near, async () => {
239
+ if (!address) return defaultResult;
240
+
241
+ try {
242
+ const { JsonRpcProvider } = await import("@near-js/providers");
243
+ const rpcUrl = await getRPCUrl(Chain.Near);
244
+ const provider = new JsonRpcProvider({ url: rpcUrl });
245
+
246
+ const metadata = await provider.query({
247
+ account_id: address,
248
+ args_base64: Buffer.from("{}").toString("base64"),
249
+ finality: "final",
250
+ method_name: "ft_metadata",
251
+ request_type: "call_function",
252
+ });
253
+
254
+ const result = JSON.parse(Buffer.from((metadata as any).result).toString());
255
+
256
+ return { decimals: result?.decimals || baseDecimal, ticker: result?.symbol };
257
+ } catch (error) {
258
+ const errorMessage = error instanceof Error ? error.message : String(error);
259
+ console.warn(`Failed to fetch Near token info for ${address}: ${errorMessage}`);
260
+ return defaultResult;
261
+ }
262
+ })
263
+ .with(Chain.Radix, async () => {
264
+ if (!address) return defaultResult;
265
+
266
+ try {
267
+ const [ticker, decimals] = await Promise.all([getRadixAssetTicker(address), getRadixAssetDecimals(address)]);
268
+
269
+ return { decimals, ticker };
270
+ } catch (error) {
271
+ const errorMessage = error instanceof Error ? error.message : String(error);
272
+ console.warn(`Failed to fetch Radix token info for ${address}: ${errorMessage}`);
273
+ return defaultResult;
274
+ }
275
+ })
276
+ .otherwise(async () => defaultResult);
277
+ }
278
+
279
+ export function isGasAsset({ chain, symbol }: { chain: Chain; symbol: string }) {
280
+ return match(chain)
281
+ .with(...ethGasChains, () => symbol === "ETH")
282
+ .with(Chain.Avalanche, () => symbol === "AVAX")
283
+ .with(Chain.Berachain, () => symbol === "BERA")
284
+ .with(Chain.BinanceSmartChain, () => symbol === "BNB")
285
+ .with(Chain.Gnosis, () => symbol === "xDAI" || symbol === "XDAI")
286
+ .with(Chain.Monad, () => symbol === "MON")
287
+ .with(Chain.XLayer, () => symbol === "OKB")
288
+ .with(Chain.Maya, () => symbol === "CACAO")
289
+ .with(Chain.Cosmos, () => symbol === "ATOM")
290
+ .with(Chain.THORChain, () => symbol === "RUNE")
291
+ .with(Chain.Tron, () => symbol === "TRX")
292
+ .with(Chain.Radix, () => `${chain}.${symbol}` === getCommonAssetInfo(chain).identifier)
293
+ .otherwise(() => symbol === chain);
294
+ }
295
+
296
+ export const getCommonAssetInfo = (assetString: CommonAssetString) => {
297
+ const { baseDecimal: decimal } = getChainConfig(assetString as Chain);
298
+
299
+ const commonAssetInfo = match(assetString.toUpperCase())
300
+ .with(...ethGasChains, (asset) => ({ decimal, identifier: `${asset}.ETH` }))
301
+ .with(Chain.THORChain, (asset) => ({ decimal, identifier: `${asset}.RUNE` }))
302
+ .with(Chain.Cosmos, (asset) => ({ decimal, identifier: `${asset}.ATOM` }))
303
+ .with(Chain.Maya, (asset) => ({ decimal: 10, identifier: `${asset}.CACAO` }))
304
+ .with(Chain.BinanceSmartChain, (asset) => ({ decimal, identifier: `${asset}.BNB` }))
305
+ .with(Chain.Monad, (asset) => ({ decimal, identifier: `${asset}.MON` }))
306
+ .with(Chain.Avalanche, (asset) => ({ decimal, identifier: `${asset}.AVAX` }))
307
+ .with(Chain.Gnosis, (asset) => ({ decimal, identifier: `${asset}.xDAI` }))
308
+ .with(Chain.XLayer, (asset) => ({ decimal, identifier: `${asset}.OKB` }))
309
+ .with(Chain.Berachain, (asset) => ({ decimal, identifier: `${asset}.BERA` }))
310
+ .with(Chain.Tron, (asset) => ({ decimal, identifier: `${asset}.TRX` }))
311
+ .with(
312
+ Chain.Solana,
313
+ Chain.Chainflip,
314
+ Chain.Kujira,
315
+ Chain.Ripple,
316
+ Chain.Polkadot,
317
+ Chain.Near,
318
+ ...UTXOChains,
319
+ (asset) => ({ decimal, identifier: `${asset}.${asset}` }),
320
+ )
321
+ .with(Chain.Radix, "XRD.XRD", () => ({ decimal, identifier: "XRD.XRD" }))
322
+ .with(Chain.Polygon, "POL.POL", () => ({ decimal, identifier: "POL.POL" }))
323
+ .with("KUJI.USK", (asset) => ({ decimal: 6, identifier: asset }))
324
+ .with("ETH.FLIP", () => ({
325
+ decimal: getChainConfig(Chain.Ethereum).baseDecimal,
326
+ identifier: "ETH.FLIP-0x826180541412D574cf1336d22c0C0a287822678A",
327
+ }))
328
+ .with("ETH.THOR", () => ({
329
+ decimal: getChainConfig(Chain.Ethereum).baseDecimal,
330
+ identifier: "ETH.THOR-0xa5f2211b9b8170f694421f2046281775e8468044",
331
+ }))
332
+ .with("ETH.vTHOR", () => ({
333
+ decimal: getChainConfig(Chain.Ethereum).baseDecimal,
334
+ identifier: "ETH.vTHOR-0x815c23eca83261b6ec689b60cc4a58b54bc24d8d",
335
+ }))
336
+ .with("MAYA.CACAO", (identifier) => ({ decimal: 10, identifier }))
337
+ .with("MAYA.MAYA", (identifier) => ({ decimal: 4, identifier }))
338
+ // Just to be sure that we are not missing any chain
339
+ .otherwise(() => ({ decimal, identifier: assetString }));
340
+
341
+ return commonAssetInfo;
342
+ };
343
+
344
+ export function getAssetType({ chain, symbol }: { chain: Chain; symbol: string }) {
345
+ if (symbol.includes("/")) return "Synth";
346
+ if (symbol.includes("~")) return "Trade";
347
+
348
+ const isNative = match(chain)
349
+ .with(Chain.Radix, () => symbol === Chain.Radix || `${chain}.${symbol}` === getCommonAssetInfo(chain).identifier)
350
+ .with(Chain.Arbitrum, Chain.Optimism, Chain.Base, Chain.Aurora, () => symbol === Chain.Ethereum)
351
+ .with(Chain.Cosmos, () => symbol === "ATOM")
352
+ .with(Chain.BinanceSmartChain, () => symbol === "BNB")
353
+ .with(Chain.Maya, () => symbol === "CACAO")
354
+ .with(Chain.Monad, () => symbol === "MON")
355
+ .with(Chain.THORChain, () => symbol === "RUNE")
356
+ .with(Chain.Tron, () => symbol === "TRX")
357
+ .with(Chain.XLayer, () => symbol === "OKB")
358
+ .otherwise(() => symbol === chain);
359
+
360
+ return isNative ? "Native" : chain;
361
+ }
362
+
363
+ export const assetFromString = (assetString: string) => {
364
+ const [chain, ...symbolArray] = assetString.split(".") as [Chain, ...(string | undefined)[]];
365
+ const synth = assetString.includes("/");
366
+ const symbol = symbolArray.join(".");
367
+ const splitSymbol = symbol?.split("-");
368
+ const ticker = splitSymbol?.length
369
+ ? splitSymbol.length === 1
370
+ ? splitSymbol[0]
371
+ : splitSymbol.slice(0, -1).join("-")
372
+ : undefined;
373
+
374
+ return { chain, symbol, synth, ticker };
375
+ };
376
+
377
+ export async function findAssetBy(params: { chain: Chain; contract: string } | { identifier: `${Chain}.${string}` }) {
378
+ const { loadTokenLists } = await import("../tokens");
379
+ const tokenLists = await loadTokenLists();
380
+
381
+ for (const tokenList of Object.values(tokenLists)) {
382
+ for (const { identifier, chain: tokenChain, ...rest } of tokenList.tokens) {
383
+ if ("identifier" in params && identifier === params.identifier) {
384
+ return identifier as TokenNames;
385
+ }
386
+
387
+ if (
388
+ "address" in rest &&
389
+ "chain" in params &&
390
+ tokenChain === params.chain &&
391
+ rest.address &&
392
+ rest.address.toLowerCase() === params.contract.toLowerCase()
393
+ )
394
+ return identifier as TokenNames;
395
+ }
396
+ }
397
+
398
+ return;
399
+ }
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Modifications © 2025 Horizontal Systems.
3
+ */
4
+
5
+ import { Chain, CosmosChains, EVMChains, type StagenetChain, StagenetChains, UTXOChains } from "@tcswap/types";
6
+ import { match } from "ts-pattern";
7
+ import { USwapConfig } from "../modules/uSwapConfig";
8
+ import { USwapError } from "../modules/uSwapError";
9
+ import { warnOnce } from "./others";
10
+
11
+ function getRpcBody(chain: Chain | StagenetChain) {
12
+ return match(chain)
13
+ .with(...EVMChains, () => ({ id: 1, jsonrpc: "2.0", method: "eth_blockNumber", params: [] }))
14
+ .with(...UTXOChains, () => ({ id: "test", jsonrpc: "1.0", method: "getblockchaininfo", params: [] }))
15
+ .with(...CosmosChains, ...StagenetChains, () => ({ id: 1, jsonrpc: "2.0", method: "status", params: {} }))
16
+ .with(Chain.Polkadot, Chain.Chainflip, () => ({ id: 1, jsonrpc: "2.0", method: "system_health", params: [] }))
17
+ .with(Chain.Solana, () => ({ id: 1, jsonrpc: "2.0", method: "getHealth" }))
18
+ .with(Chain.Sui, () => ({ id: 1, jsonrpc: "2.0", method: "sui_getSystemState", params: [] }))
19
+ .with(Chain.Ton, () => ({ id: 1, jsonrpc: "2.0", method: "getAddressInformation", params: { address: "" } }))
20
+ .with(Chain.Tron, Chain.Radix, () => "")
21
+ .with(Chain.Near, () => ({ id: "dontcare", jsonrpc: "2.0", method: "status", params: [] }))
22
+ .with(Chain.Ripple, () => ({ id: 1, jsonrpc: "2.0", method: "ping", params: [{}] }))
23
+ .otherwise(() => {
24
+ throw new USwapError("helpers_chain_not_supported", { chain });
25
+ });
26
+ }
27
+
28
+ function getChainStatusEndpoint(chain: Chain | StagenetChain) {
29
+ return match(chain)
30
+ .with(Chain.Radix, () => "/status/network-configuration")
31
+ .with(Chain.Tron, () => "/wallet/getnowblock")
32
+ .otherwise(() => "");
33
+ }
34
+
35
+ async function testRPCConnection(chain: Chain | StagenetChain, url: string) {
36
+ try {
37
+ const endpoint = url.startsWith("wss") ? url.replace("wss", "https") : url;
38
+ const response = await fetch(`${endpoint}${getChainStatusEndpoint(chain)}`, {
39
+ body: JSON.stringify(getRpcBody(chain)),
40
+ headers: { "Content-Type": "application/json" },
41
+ method: "POST",
42
+ signal: AbortSignal.timeout(3000),
43
+ });
44
+
45
+ return response.ok;
46
+ } catch {
47
+ return false;
48
+ }
49
+ }
50
+
51
+ const rpcCache = new Map<Chain | StagenetChain, { timestamp: number; url: string }>();
52
+ const rpcCacheTTL = 1000 * 60 * 2; // 2 minutes
53
+
54
+ export async function getRPCUrl(chain: Chain | StagenetChain) {
55
+ const { isStagenet } = USwapConfig.get("envs");
56
+ const [rpcUrl = "", ...fallbackUrls] = USwapConfig.get("rpcUrls")[chain];
57
+
58
+ if (!rpcUrl) {
59
+ warnOnce({
60
+ condition: true,
61
+ id: "helpers_chain_no_public_or_set_rpc_url",
62
+ warning: `No public or set RPC URL found for chain. Please ensure you configured rpcUrls for ${chain}.`,
63
+ });
64
+ throw new USwapError("helpers_chain_no_public_or_set_rpc_url", { chain });
65
+ }
66
+
67
+ if (isStagenet) return rpcUrl;
68
+
69
+ const cached = rpcCache.get(chain);
70
+ if (cached && Date.now() - cached.timestamp < rpcCacheTTL) {
71
+ return cached.url;
72
+ }
73
+
74
+ const primaryIsWorking = await testRPCConnection(chain, rpcUrl);
75
+
76
+ if (!primaryIsWorking) {
77
+ for (const fallbackUrl of fallbackUrls) {
78
+ const fallbackIsWorking = await testRPCConnection(chain, fallbackUrl);
79
+
80
+ if (fallbackIsWorking) {
81
+ rpcCache.set(chain, { timestamp: Date.now(), url: fallbackUrl });
82
+ return fallbackUrl;
83
+ }
84
+ }
85
+ }
86
+
87
+ rpcCache.set(chain, { timestamp: Date.now(), url: rpcUrl });
88
+ return rpcUrl;
89
+ }
90
+
91
+ /**
92
+ * @deprecated
93
+ * RPC URLs are now managed dynamically via USwapConfig.
94
+ * Please use static { rpcUrls, fallbackRpcUrls } USwap init config or dynamic USwapConfig.setRpcUrl/setFallbackRpcUrl to configure RPC endpoints.
95
+ * This function is obsolete and will be removed in a future release.
96
+ */
97
+ export function initializeRPCUrlsWithFallback(_chains: never) {
98
+ warnOnce({
99
+ condition: true,
100
+ id: "initializeRPCUrlsWithFallback",
101
+ warning:
102
+ "initializeRPCUrlsWithFallback is deprecated. Use static { rpcUrls, fallbackRpcUrls } USwap init config or dynamic USwapConfig.setRpcUrl/setFallbackRpcUrl to configure RPC endpoints.",
103
+ });
104
+ }
@@ -0,0 +1,101 @@
1
+ import { Chain, type EVMChain, EVMChains } from "@tcswap/types";
2
+ import { type DerivationPathArray, NetworkDerivationPath } from "../types";
3
+
4
+ type Params = {
5
+ chain: Chain;
6
+ index: number;
7
+ addressIndex?: number;
8
+ type?: "legacy" | "ledgerLive" | "nativeSegwitMiddleAccount" | "segwit" | "account";
9
+ };
10
+
11
+ export function updateDerivationPath(
12
+ path: DerivationPathArray,
13
+ params: { index: number } | { account: number } | { change: number },
14
+ ) {
15
+ if ("index" in params) {
16
+ const newPath = [...path.slice(0, path.length - 1), params.index];
17
+ return newPath as DerivationPathArray;
18
+ }
19
+
20
+ if ("change" in params) {
21
+ const [network, chainId, account, , index] = path;
22
+ return [network, chainId, account, params.change, index] as DerivationPathArray;
23
+ }
24
+
25
+ if ("account" in params) {
26
+ const [network, chainId, , change, index] = path;
27
+ return [network, chainId, params.account, change, index] as DerivationPathArray;
28
+ }
29
+
30
+ return path;
31
+ }
32
+
33
+ export function derivationPathToString([network, chainId, account, change, index]:
34
+ | [number, number, number, number?, number?]
35
+ | [number, number, number, number, number?]
36
+ | DerivationPathArray) {
37
+ const shortPath = typeof index !== "number";
38
+ const accountPath = typeof change !== "number";
39
+
40
+ if (accountPath) {
41
+ return `m/${network}'/${chainId}'/${account}'`;
42
+ }
43
+
44
+ return `m/${network}'/${chainId}'/${account}'/${change}${shortPath ? "" : `/${index}`}`;
45
+ }
46
+
47
+ // TODO @towan - sort this out and make it more readable
48
+ export function getDerivationPathFor({ chain, index, addressIndex = 0, type }: Params) {
49
+ if (EVMChains.includes(chain as EVMChain)) {
50
+ if (type && ["legacy", "account"].includes(type)) {
51
+ return [44, 60, 0, index];
52
+ }
53
+
54
+ if (type === "ledgerLive") {
55
+ return [44, 60, index, 0, addressIndex] as DerivationPathArray;
56
+ }
57
+
58
+ return updateDerivationPath(NetworkDerivationPath[chain], { index });
59
+ }
60
+
61
+ if (chain === Chain.Solana) {
62
+ if (type === "account") return [44, 501, 0, index];
63
+ return updateDerivationPath(NetworkDerivationPath[chain], { index });
64
+ }
65
+
66
+ const chainId = chain === Chain.Litecoin ? 2 : 0;
67
+
68
+ switch (type) {
69
+ case "nativeSegwitMiddleAccount":
70
+ return [84, chainId, index, 0, addressIndex];
71
+ case "segwit":
72
+ return [49, chainId, 0, 0, index];
73
+ case "legacy":
74
+ return [44, chainId, 0, 0, index];
75
+ default:
76
+ return updateDerivationPath(NetworkDerivationPath[chain], { index });
77
+ }
78
+ }
79
+
80
+ export function getWalletFormatFor(path: string) {
81
+ const [_, purpose, chainId] = path.split("/").map((p) => Number.parseInt(p, 10));
82
+
83
+ if (chainId === 145) "cashaddr";
84
+
85
+ switch (purpose) {
86
+ case 44:
87
+ return "legacy";
88
+ case 49:
89
+ return "p2sh";
90
+ default:
91
+ return "bech32";
92
+ }
93
+ }
94
+
95
+ export const DerivationPath: Record<Chain, string> = Object.keys(NetworkDerivationPath).reduce(
96
+ (acc, key) => {
97
+ acc[key as Chain] = derivationPathToString(NetworkDerivationPath[key as Chain]);
98
+ return acc;
99
+ },
100
+ {} as Record<Chain, string>,
101
+ );
@@ -0,0 +1,32 @@
1
+ import { Chain, CosmosChains, EVMChains, getChainConfig, SubstrateChains, UTXOChains } from "@tcswap/types";
2
+ import { match } from "ts-pattern";
3
+
4
+ export function getExplorerTxUrl({ chain, txHash }: { txHash: string; chain: Chain }) {
5
+ const { explorerUrl } = getChainConfig(chain);
6
+
7
+ return match(chain)
8
+ .with(
9
+ ...CosmosChains,
10
+ Chain.Solana,
11
+ () => `${explorerUrl}/tx/${txHash.startsWith("0x") ? txHash.slice(2) : txHash}`,
12
+ )
13
+ .with(
14
+ ...EVMChains,
15
+ ...SubstrateChains,
16
+ () => `${explorerUrl}/tx/${txHash.startsWith("0x") ? txHash : `0x${txHash}`}`,
17
+ )
18
+ .with(...UTXOChains, Chain.Radix, Chain.Tron, () => `${explorerUrl}/transaction/${txHash.toLowerCase()}`)
19
+ .with(Chain.Near, () => `${explorerUrl}/txns/${txHash}`)
20
+ .with(Chain.Ripple, () => `${explorerUrl}/transactions/${txHash}`)
21
+ .with(Chain.Sui, () => `${explorerUrl}/txblock/${txHash}`)
22
+ .with(Chain.Cardano, Chain.Ton, () => `${explorerUrl}/tx/${txHash}`)
23
+ .otherwise(() => "");
24
+ }
25
+
26
+ export function getExplorerAddressUrl({ chain, address }: { address: string; chain: Chain }) {
27
+ const { explorerUrl } = getChainConfig(chain);
28
+
29
+ return match(chain)
30
+ .with(Chain.Solana, Chain.Sui, Chain.Radix, () => `${explorerUrl}/account/${address}`)
31
+ .otherwise(() => `${explorerUrl}/address/${address}`);
32
+ }