@swapkit/helpers 4.4.5 → 4.5.2

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 (69) hide show
  1. package/dist/api/index.cjs +2 -2
  2. package/dist/api/index.cjs.map +4 -4
  3. package/dist/api/index.js +2 -2
  4. package/dist/api/index.js.map +4 -4
  5. package/dist/index.cjs +3 -3
  6. package/dist/index.cjs.map +10 -10
  7. package/dist/index.js +3 -3
  8. package/dist/index.js.map +10 -10
  9. package/dist/types/api/index.d.ts +247 -0
  10. package/dist/types/api/index.d.ts.map +1 -1
  11. package/dist/types/api/swapkitApi/endpoints.d.ts +247 -0
  12. package/dist/types/api/swapkitApi/endpoints.d.ts.map +1 -1
  13. package/dist/types/api/swapkitApi/types.d.ts +3 -1
  14. package/dist/types/api/swapkitApi/types.d.ts.map +1 -1
  15. package/dist/types/modules/assetValue.d.ts +6 -0
  16. package/dist/types/modules/assetValue.d.ts.map +1 -1
  17. package/dist/types/modules/bigIntArithmetics.d.ts.map +1 -1
  18. package/dist/types/modules/swapKitConfig.d.ts +63 -36
  19. package/dist/types/modules/swapKitConfig.d.ts.map +1 -1
  20. package/dist/types/modules/swapKitError.d.ts +20 -24
  21. package/dist/types/modules/swapKitError.d.ts.map +1 -1
  22. package/dist/types/types/wallet.d.ts +5 -1
  23. package/dist/types/types/wallet.d.ts.map +1 -1
  24. package/dist/types/utils/asset.d.ts +1 -1
  25. package/dist/types/utils/asset.d.ts.map +1 -1
  26. package/package.json +24 -12
  27. package/src/api/index.ts +9 -0
  28. package/src/api/midgard/endpoints.ts +348 -0
  29. package/src/api/midgard/types.ts +515 -0
  30. package/src/api/swapkitApi/endpoints.ts +242 -0
  31. package/src/api/swapkitApi/types.ts +624 -0
  32. package/src/api/thornode/endpoints.ts +105 -0
  33. package/src/api/thornode/types.ts +247 -0
  34. package/src/contracts.ts +1 -0
  35. package/src/index.ts +28 -0
  36. package/src/modules/__tests__/assetValue.test.ts +1637 -0
  37. package/src/modules/__tests__/bigIntArithmetics.test.ts +383 -0
  38. package/src/modules/__tests__/swapKitConfig.test.ts +425 -0
  39. package/src/modules/__tests__/swapKitNumber.test.ts +535 -0
  40. package/src/modules/assetValue.ts +532 -0
  41. package/src/modules/bigIntArithmetics.ts +363 -0
  42. package/src/modules/feeMultiplier.ts +80 -0
  43. package/src/modules/requestClient.ts +110 -0
  44. package/src/modules/swapKitConfig.ts +174 -0
  45. package/src/modules/swapKitError.ts +470 -0
  46. package/src/modules/swapKitNumber.ts +13 -0
  47. package/src/tokens.ts +1 -0
  48. package/src/types/commonTypes.ts +10 -0
  49. package/src/types/derivationPath.ts +11 -0
  50. package/src/types/errors/apiV1.ts +0 -0
  51. package/src/types/index.ts +5 -0
  52. package/src/types/quotes.ts +174 -0
  53. package/src/types/sdk.ts +38 -0
  54. package/src/types/wallet.ts +124 -0
  55. package/src/utils/__tests__/asset.test.ts +185 -0
  56. package/src/utils/__tests__/derivationPath.test.ts +16 -0
  57. package/src/utils/__tests__/explorerUrls.test.ts +59 -0
  58. package/src/utils/__tests__/memo.test.ts +99 -0
  59. package/src/utils/__tests__/others.test.ts +83 -0
  60. package/src/utils/__tests__/validators.test.ts +24 -0
  61. package/src/utils/asset.ts +395 -0
  62. package/src/utils/chains.ts +100 -0
  63. package/src/utils/derivationPath.ts +101 -0
  64. package/src/utils/explorerUrls.ts +32 -0
  65. package/src/utils/liquidity.ts +150 -0
  66. package/src/utils/memo.ts +102 -0
  67. package/src/utils/others.ts +62 -0
  68. package/src/utils/validators.ts +32 -0
  69. package/src/utils/wallets.ts +237 -0
@@ -0,0 +1,1637 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import { Chain, getChainConfig } from "@swapkit/types";
4
+ import { AssetValue, getMinAmountByChain } from "../assetValue";
5
+
6
+ describe("AssetValue", () => {
7
+ describe("creation", () => {
8
+ test("regres cases", () => {
9
+ const arbWeth = AssetValue.from({ asset: "ARB.WETH-0x82aF49447D8a07e3bd95BD0d56f35241523fBab1" });
10
+ expect(arbWeth.toString()).toBe("ARB.WETH-0x82aF49447D8a07e3bd95BD0d56f35241523fBab1");
11
+
12
+ const baseAssetFromString = AssetValue.from({ asset: "BASE.USDC-0x833589fcd6edb6e08f4c7c32d4f71b54bda02913" });
13
+ expect(baseAssetFromString.toString()).toBe("BASE.USDC-0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913");
14
+
15
+ const avaxSolanaAsset = AssetValue.from({ asset: "AVAX.SOL-0XFE6B19286885A4F7F55ADAD09C3CD1F906D2478F" });
16
+ expect(avaxSolanaAsset.toString()).toBe("AVAX.SOL-0xFE6B19286885a4F7F55AdAD09C3Cd1f906D2478F");
17
+ });
18
+
19
+ test("returns asset ticker with value", () => {
20
+ const fakeAvaxUSDCAsset = new AssetValue({
21
+ chain: Chain.Avalanche,
22
+ decimal: 6,
23
+ symbol: "USDC-0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e",
24
+ value: 1234567890,
25
+ });
26
+ expect(fakeAvaxUSDCAsset.toString()).toBe("AVAX.USDC-0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E");
27
+
28
+ const ethSynth = new AssetValue({ chain: Chain.THORChain, decimal: 8, symbol: "ETH/ETH", value: 1234567890 });
29
+
30
+ expect(ethSynth.toString()).toBe("ETH/ETH");
31
+ expect(ethSynth.mul(21.37).getValue("string")).toBe("26382715809.3");
32
+
33
+ const ethTrade = new AssetValue({ chain: Chain.THORChain, decimal: 8, symbol: "ETH~ETH", value: 1234567890 });
34
+
35
+ expect(ethTrade.toString()).toBe("ETH~ETH");
36
+ expect(ethTrade.mul(21.37).getValue("string")).toBe("26382715809.3");
37
+
38
+ const ethThorTrade = new AssetValue({
39
+ chain: Chain.THORChain,
40
+ decimal: 8,
41
+ symbol: "ETH~THOR-0xa5f2211b9b8170f694421f2046281775e8468044",
42
+ value: 1234567890,
43
+ });
44
+
45
+ expect(ethThorTrade.toString()).toBe("ETH~THOR-0xa5f2211b9b8170f694421f2046281775e8468044");
46
+ expect(ethThorTrade.chain).toBe(Chain.THORChain);
47
+
48
+ const ethThorSynth = new AssetValue({
49
+ chain: Chain.THORChain,
50
+ decimal: 8,
51
+ symbol: "ETH/THOR-0xa5f2211b9b8170f694421f2046281775e8468044",
52
+ value: 1234567890,
53
+ });
54
+ expect(ethThorSynth.toString()).toBe("ETH/THOR-0xa5f2211b9b8170f694421f2046281775e8468044");
55
+ expect(ethThorSynth.chain).toBe(Chain.THORChain);
56
+
57
+ const mayaEthSynth = new AssetValue({ chain: Chain.Maya, decimal: 8, symbol: "ETH/ETH", value: 1234567890 });
58
+ expect(mayaEthSynth.toString()).toBe("ETH/ETH");
59
+ expect(mayaEthSynth.chain).toBe(Chain.Maya);
60
+
61
+ const mayaEthSynthFrom = AssetValue.from({ asset: "MAYA.ETH/ETH", value: 12.3456789 });
62
+ expect(mayaEthSynthFrom.toString()).toBe("ETH/ETH");
63
+ expect(mayaEthSynthFrom.chain).toBe(Chain.Maya);
64
+
65
+ const atomDerived = new AssetValue({ decimal: 6, identifier: "THOR.ATOM", value: 123456789 });
66
+
67
+ expect(atomDerived.toString()).toBe("THOR.ATOM");
68
+
69
+ const value = 10;
70
+ const mayaCacao = AssetValue.from({ asset: "MAYA.CACAO", value });
71
+
72
+ expect(mayaCacao.toString()).toBe("MAYA.CACAO");
73
+ const expectedValue = value * 10_000_000_000;
74
+ expect(mayaCacao.getBaseValue("string")).toBe(expectedValue.toString());
75
+
76
+ const ethMayaSynth = AssetValue.from({ asset: "MAYA.ETH/ETH", value: 10 });
77
+
78
+ expect(ethMayaSynth.toString()).toBe("ETH/ETH");
79
+ expect(ethMayaSynth.toString({ includeSynthProtocol: true })).toBe("MAYA.ETH/ETH");
80
+ expect(ethMayaSynth.getBaseValue("string")).toBe("1000000000");
81
+
82
+ const ethTCSynthFallback = AssetValue.from({ asset: "ETH/ETH", value: 10 });
83
+
84
+ expect(ethTCSynthFallback.toString()).toBe("ETH/ETH");
85
+ expect(ethTCSynthFallback.toString({ includeSynthProtocol: true })).toBe("THOR.ETH/ETH");
86
+ expect(ethTCSynthFallback.getBaseValue("string")).toBe("1000000000");
87
+
88
+ const solFromString = AssetValue.from({ asset: "SOL.SOL" });
89
+ expect(solFromString.toString()).toBe("SOL.SOL");
90
+ });
91
+
92
+ describe("NEAR", () => {
93
+ test("assets creation", () => {
94
+ // Standard NEAR token formats
95
+ const wNear = AssetValue.from({ asset: "NEAR.wNEAR-wrap.near" });
96
+ expect(wNear.toString()).toBe("NEAR.wNEAR-wrap.near");
97
+ expect(wNear).toMatchObject({
98
+ address: "wrap.near",
99
+ chain: Chain.Near,
100
+ symbol: "wNEAR-wrap.near",
101
+ ticker: "wNEAR",
102
+ });
103
+
104
+ const ethBridge = AssetValue.from({ asset: "NEAR.ETH-eth.bridge.near" });
105
+ expect(ethBridge.toString()).toBe("NEAR.ETH-eth.bridge.near");
106
+ expect(ethBridge).toMatchObject({
107
+ address: "eth.bridge.near",
108
+ chain: Chain.Near,
109
+ symbol: "ETH-eth.bridge.near",
110
+ ticker: "ETH",
111
+ });
112
+ const usdc = AssetValue.from({
113
+ asset: "NEAR.USDC-17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1",
114
+ });
115
+ expect(usdc.toString()).toBe("NEAR.USDC-17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1");
116
+ expect(usdc).toMatchObject({
117
+ address: "17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1",
118
+ chain: Chain.Near,
119
+ symbol: "USDC-17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1",
120
+ ticker: "USDC",
121
+ });
122
+
123
+ const usdt = AssetValue.from({ asset: "NEAR.USDT-usdt.tether-token.near" });
124
+ expect(usdt.toString()).toBe("NEAR.USDT-usdt.tether-token.near");
125
+ expect(usdt).toMatchObject({
126
+ address: "usdt.tether-token.near",
127
+ chain: Chain.Near,
128
+ symbol: "USDT-usdt.tether-token.near",
129
+ ticker: "USDT",
130
+ });
131
+
132
+ // Factory bridge format
133
+ const frax = AssetValue.from({
134
+ asset: "NEAR.FRAX-853d955acef822db058eb8505911ed77f175b99e.factory.bridge.near",
135
+ });
136
+ expect(frax.toString()).toBe("NEAR.FRAX-853d955acef822db058eb8505911ed77f175b99e.factory.bridge.near");
137
+ expect(frax).toMatchObject({
138
+ address: "853d955acef822db058eb8505911ed77f175b99e.factory.bridge.near",
139
+ chain: Chain.Near,
140
+ symbol: "FRAX-853d955acef822db058eb8505911ed77f175b99e.factory.bridge.near",
141
+ ticker: "FRAX",
142
+ });
143
+
144
+ const aurora = AssetValue.from({
145
+ asset: "NEAR.AURORA-aaaaaa20d9e0e2461697782ef11675f668207961.factory.bridge.near",
146
+ });
147
+ expect(aurora.toString()).toBe("NEAR.AURORA-aaaaaa20d9e0e2461697782ef11675f668207961.factory.bridge.near");
148
+ expect(aurora).toMatchObject({
149
+ address: "aaaaaa20d9e0e2461697782ef11675f668207961.factory.bridge.near",
150
+ chain: Chain.Near,
151
+ symbol: "AURORA-aaaaaa20d9e0e2461697782ef11675f668207961.factory.bridge.near",
152
+ ticker: "AURORA",
153
+ });
154
+
155
+ const wBTC = AssetValue.from({
156
+ asset: "NEAR.wBTC-2260fac5e5542a773aa44fbcfedf7c193bc2c599.factory.bridge.near",
157
+ });
158
+ expect(wBTC.toString()).toBe("NEAR.wBTC-2260fac5e5542a773aa44fbcfedf7c193bc2c599.factory.bridge.near");
159
+ expect(wBTC).toMatchObject({
160
+ address: "2260fac5e5542a773aa44fbcfedf7c193bc2c599.factory.bridge.near",
161
+ chain: Chain.Near,
162
+ symbol: "wBTC-2260fac5e5542a773aa44fbcfedf7c193bc2c599.factory.bridge.near",
163
+ ticker: "wBTC",
164
+ });
165
+
166
+ const blackdragon = AssetValue.from({ asset: "NEAR.BLACKDRAGON-blackdragon.tkn.near" });
167
+ expect(blackdragon.toString()).toBe("NEAR.BLACKDRAGON-blackdragon.tkn.near");
168
+ expect(blackdragon).toMatchObject({
169
+ address: "blackdragon.tkn.near",
170
+ chain: Chain.Near,
171
+ symbol: "BLACKDRAGON-blackdragon.tkn.near",
172
+ ticker: "BLACKDRAGON",
173
+ });
174
+
175
+ const shitzu = AssetValue.from({ asset: "NEAR.SHITZU-token.0xshitzu.near" });
176
+ expect(shitzu.toString()).toBe("NEAR.SHITZU-token.0xshitzu.near");
177
+ expect(shitzu).toMatchObject({
178
+ address: "token.0xshitzu.near",
179
+ chain: Chain.Near,
180
+ symbol: "SHITZU-token.0xshitzu.near",
181
+ ticker: "SHITZU",
182
+ });
183
+
184
+ const abg = AssetValue.from({ asset: "NEAR.ABG-abg-966.meme-cooking.near" });
185
+ expect(abg.toString()).toBe("NEAR.ABG-abg-966.meme-cooking.near");
186
+ expect(abg).toMatchObject({
187
+ address: "abg-966.meme-cooking.near",
188
+ chain: Chain.Near,
189
+ symbol: "ABG-abg-966.meme-cooking.near",
190
+ ticker: "ABG",
191
+ });
192
+ const noear = AssetValue.from({ asset: "NEAR.NOEAR-noear-324.meme-cooking.near" });
193
+ expect(noear.toString()).toBe("NEAR.NOEAR-noear-324.meme-cooking.near");
194
+ expect(noear).toMatchObject({
195
+ address: "noear-324.meme-cooking.near",
196
+ chain: Chain.Near,
197
+ symbol: "NOEAR-noear-324.meme-cooking.near",
198
+ ticker: "NOEAR",
199
+ });
200
+
201
+ const jambo = AssetValue.from({ asset: "NEAR.JAMBO-jambo-1679.meme-cooking.near" });
202
+ expect(jambo.toString()).toBe("NEAR.JAMBO-jambo-1679.meme-cooking.near");
203
+ expect(jambo).toMatchObject({
204
+ address: "jambo-1679.meme-cooking.near",
205
+ chain: Chain.Near,
206
+ symbol: "JAMBO-jambo-1679.meme-cooking.near",
207
+ ticker: "JAMBO",
208
+ });
209
+
210
+ const gnear = AssetValue.from({ asset: "NEAR.GNEAR-gnear-229.meme-cooking.near" });
211
+ expect(gnear.toString()).toBe("NEAR.GNEAR-gnear-229.meme-cooking.near");
212
+ expect(gnear).toMatchObject({
213
+ address: "gnear-229.meme-cooking.near",
214
+ chain: Chain.Near,
215
+ symbol: "GNEAR-gnear-229.meme-cooking.near",
216
+ ticker: "GNEAR",
217
+ });
218
+
219
+ const purge = AssetValue.from({ asset: "NEAR.PURGE-purge-558.meme-cooking.near" });
220
+ expect(purge.toString()).toBe("NEAR.PURGE-purge-558.meme-cooking.near");
221
+ expect(purge).toMatchObject({
222
+ address: "purge-558.meme-cooking.near",
223
+ chain: Chain.Near,
224
+ symbol: "PURGE-purge-558.meme-cooking.near",
225
+ ticker: "PURGE",
226
+ });
227
+
228
+ // Various token formats
229
+ const mpDAO = AssetValue.from({ asset: "NEAR.mpDAO-mpdao-token.near" });
230
+ expect(mpDAO.toString()).toBe("NEAR.mpDAO-mpdao-token.near");
231
+ expect(mpDAO).toMatchObject({
232
+ address: "mpdao-token.near",
233
+ chain: Chain.Near,
234
+ symbol: "mpDAO-mpdao-token.near",
235
+ ticker: "mpDAO",
236
+ });
237
+
238
+ const nearKat = AssetValue.from({ asset: "NEAR.NearKat-kat.token0.near" });
239
+ expect(nearKat.toString()).toBe("NEAR.NearKat-kat.token0.near");
240
+ expect(nearKat).toMatchObject({
241
+ address: "kat.token0.near",
242
+ chain: Chain.Near,
243
+ symbol: "NearKat-kat.token0.near",
244
+ ticker: "NearKat",
245
+ });
246
+
247
+ const testNebula = AssetValue.from({ asset: "NEAR.TESTNEBULA-test-token.highdome3013.near" });
248
+ expect(testNebula.toString()).toBe("NEAR.TESTNEBULA-test-token.highdome3013.near");
249
+ expect(testNebula).toMatchObject({
250
+ address: "test-token.highdome3013.near",
251
+ chain: Chain.Near,
252
+ symbol: "TESTNEBULA-test-token.highdome3013.near",
253
+ ticker: "TESTNEBULA",
254
+ });
255
+
256
+ const sweat = AssetValue.from({ asset: "NEAR.SWEAT-token.sweat" });
257
+ expect(sweat.toString()).toBe("NEAR.SWEAT-token.sweat");
258
+ expect(sweat).toMatchObject({
259
+ address: "token.sweat",
260
+ chain: Chain.Near,
261
+ symbol: "SWEAT-token.sweat",
262
+ ticker: "SWEAT",
263
+ });
264
+ const rhea = AssetValue.from({ asset: "NEAR.RHEA-token.rhealab.near" });
265
+ expect(rhea.toString()).toBe("NEAR.RHEA-token.rhealab.near");
266
+ expect(rhea).toMatchObject({
267
+ address: "token.rhealab.near",
268
+ chain: Chain.Near,
269
+ symbol: "RHEA-token.rhealab.near",
270
+ ticker: "RHEA",
271
+ });
272
+
273
+ const publicToken = AssetValue.from({ asset: "NEAR.PUBLIC-token.publicailab.near" });
274
+ expect(publicToken.toString()).toBe("NEAR.PUBLIC-token.publicailab.near");
275
+ expect(publicToken).toMatchObject({
276
+ address: "token.publicailab.near",
277
+ chain: Chain.Near,
278
+ symbol: "PUBLIC-token.publicailab.near",
279
+ ticker: "PUBLIC",
280
+ });
281
+
282
+ const hapi = AssetValue.from({
283
+ asset: "NEAR.HAPI-d9c2d319cd7e6177336b0a9c93c21cb48d84fb54.factory.bridge.near",
284
+ });
285
+ expect(hapi.toString()).toBe("NEAR.HAPI-d9c2d319cd7e6177336b0a9c93c21cb48d84fb54.factory.bridge.near");
286
+ expect(hapi).toMatchObject({
287
+ address: "d9c2d319cd7e6177336b0a9c93c21cb48d84fb54.factory.bridge.near",
288
+ chain: Chain.Near,
289
+ symbol: "HAPI-d9c2d319cd7e6177336b0a9c93c21cb48d84fb54.factory.bridge.near",
290
+ ticker: "HAPI",
291
+ });
292
+
293
+ const btc = AssetValue.from({ asset: "NEAR.BTC-nbtc.bridge.near" });
294
+ expect(btc.toString()).toBe("NEAR.BTC-nbtc.bridge.near");
295
+ expect(btc).toMatchObject({
296
+ address: "nbtc.bridge.near",
297
+ chain: Chain.Near,
298
+ symbol: "BTC-nbtc.bridge.near",
299
+ ticker: "BTC",
300
+ });
301
+
302
+ const turbo = AssetValue.from({
303
+ asset: "NEAR.TURBO-a35923162c49cf95e6bf26623385eb431ad920d3.factory.bridge.near",
304
+ });
305
+ expect(turbo.toString()).toBe("NEAR.TURBO-a35923162c49cf95e6bf26623385eb431ad920d3.factory.bridge.near");
306
+ expect(turbo).toMatchObject({
307
+ address: "a35923162c49cf95e6bf26623385eb431ad920d3.factory.bridge.near",
308
+ chain: Chain.Near,
309
+ symbol: "TURBO-a35923162c49cf95e6bf26623385eb431ad920d3.factory.bridge.near",
310
+ ticker: "TURBO",
311
+ });
312
+
313
+ const near = AssetValue.from({ asset: "NEAR.NEAR" });
314
+ expect(near.toString()).toBe("NEAR.NEAR");
315
+ expect(near).toMatchObject({ address: undefined, chain: Chain.Near, symbol: "NEAR", ticker: "NEAR" });
316
+ });
317
+
318
+ test("assets with values", () => {
319
+ const wNearWithValue = AssetValue.from({ asset: "NEAR.wNEAR-wrap.near", value: 10.5 });
320
+ expect(wNearWithValue.toString()).toBe("NEAR.wNEAR-wrap.near");
321
+ expect(wNearWithValue.getValue("string")).toBe("10.5");
322
+ expect(wNearWithValue.chain).toBe(Chain.Near);
323
+
324
+ const usdcWithValue = AssetValue.from({
325
+ asset: "NEAR.USDC-17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1",
326
+ value: 1000.123456,
327
+ });
328
+ expect(usdcWithValue.toString()).toBe(
329
+ "NEAR.USDC-17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1",
330
+ );
331
+ expect(usdcWithValue.getValue("string")).toBe("1000.123456");
332
+ expect(usdcWithValue.chain).toBe(Chain.Near);
333
+
334
+ const nearWithValue = AssetValue.from({ asset: "NEAR.NEAR", value: 0.000001 });
335
+ expect(nearWithValue.toString()).toBe("NEAR.NEAR");
336
+ expect(nearWithValue.getValue("string")).toBe("0.000001");
337
+ expect(nearWithValue.chain).toBe(Chain.Near);
338
+ });
339
+ });
340
+
341
+ describe("SUI", () => {
342
+ test("assets creation", () => {
343
+ const sui = AssetValue.from({ asset: "SUI.SUI" });
344
+ expect(sui.toString()).toBe("SUI.SUI");
345
+ expect(sui).toMatchObject({ address: undefined, chain: Chain.Sui, symbol: "SUI", ticker: "SUI" });
346
+ });
347
+
348
+ test("custom token assets creation", () => {
349
+ const suiUsdc = AssetValue.from({
350
+ asset: "SUI.USDC-0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC",
351
+ });
352
+ expect(suiUsdc.toString()).toBe(
353
+ "SUI.USDC-0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC",
354
+ );
355
+ expect(suiUsdc).toMatchObject({
356
+ address: "0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC",
357
+ chain: Chain.Sui,
358
+ symbol: "USDC-0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC",
359
+ ticker: "USDC",
360
+ });
361
+ });
362
+ });
363
+ });
364
+ });
365
+
366
+ describe("set", () => {
367
+ test("get a copy of an assetValue with a new value", () => {
368
+ const btc = AssetValue.from({ asset: "BTC.BTC" });
369
+
370
+ const btc2 = btc.set(10);
371
+
372
+ expect(btc2).toEqual(
373
+ expect.objectContaining({
374
+ chain: Chain.Bitcoin,
375
+ decimal: 8,
376
+ isGasAsset: true,
377
+ isSynthetic: false,
378
+ symbol: "BTC",
379
+ ticker: "BTC",
380
+ }),
381
+ );
382
+
383
+ expect(btc.getValue("string")).toBe("0");
384
+ expect(btc2.getValue("string")).toBe("10");
385
+ });
386
+
387
+ test("get a copy of a synth assetValue with a new value", () => {
388
+ const synthAvax = AssetValue.from({ asset: "THOR.AVAX/AVAX", value: 1 });
389
+
390
+ const synthAvax2 = synthAvax.set(10);
391
+
392
+ expect(synthAvax2).toBeDefined();
393
+ expect(synthAvax2).toEqual(
394
+ expect.objectContaining({
395
+ chain: Chain.THORChain,
396
+ decimal: 8,
397
+ isGasAsset: false,
398
+ isSynthetic: true,
399
+ symbol: "AVAX/AVAX",
400
+ ticker: "AVAX",
401
+ }),
402
+ );
403
+
404
+ expect(synthAvax.getValue("string")).toBe("1");
405
+ expect(synthAvax2.getValue("string")).toBe("10");
406
+ expect(synthAvax.toString({ includeSynthProtocol: true })).toBe("THOR.AVAX/AVAX");
407
+ expect(synthAvax2.toString({ includeSynthProtocol: true })).toBe("THOR.AVAX/AVAX");
408
+ });
409
+ });
410
+
411
+ describe("toUrl", () => {
412
+ test("returns asset compliance with url", () => {
413
+ const fakeAvaxUSDCAsset = new AssetValue({
414
+ chain: Chain.Avalanche,
415
+ decimal: 6,
416
+ symbol: "USDC-0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e",
417
+ value: 1234567890,
418
+ });
419
+ expect(fakeAvaxUSDCAsset.toUrl()).toBe("AVAX.USDC-0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E");
420
+
421
+ const thor = AssetValue.from({ asset: "ETH.THOR" });
422
+ expect(thor.toUrl()).toBe("ETH.THOR-0xa5f2211B9b8170F694421f2046281775E8468044");
423
+
424
+ const ethSynth = new AssetValue({ chain: Chain.THORChain, decimal: 8, symbol: "ETH/ETH", value: 1234567890 });
425
+ expect(ethSynth.toUrl()).toBe("THOR.ETH.ETH");
426
+
427
+ const ethThorSynth = new AssetValue({
428
+ chain: Chain.THORChain,
429
+ decimal: 8,
430
+ symbol: "ETH/THOR-0xa5f2211b9b8170f694421f2046281775e8468044",
431
+ value: 1234567890,
432
+ });
433
+ expect(ethThorSynth.toUrl()).toBe("THOR.ETH.THOR-0xa5f2211b9b8170f694421f2046281775e8468044");
434
+
435
+ const ethThorTrade = new AssetValue({
436
+ chain: Chain.THORChain,
437
+ decimal: 8,
438
+ symbol: "ETH~THOR-0xa5f2211b9b8170f694421f2046281775e8468044",
439
+ value: 1234567890,
440
+ });
441
+ expect(ethThorTrade.toUrl()).toBe("THOR.ETH..THOR-0xa5f2211b9b8170f694421f2046281775e8468044");
442
+ });
443
+ });
444
+
445
+ describe("eq", () => {
446
+ test("checks if assets are same chain and symbol", () => {
447
+ const firstThor = AssetValue.from({ asset: "ETH.THOR" });
448
+ const secondThor = AssetValue.from({ asset: "ETH.THOR" });
449
+ const vThor = AssetValue.from({ asset: "ETH.vTHOR" });
450
+ const firstUsdc = new AssetValue({
451
+ chain: Chain.Avalanche,
452
+ decimal: 6,
453
+ symbol: "USDC-0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e",
454
+ value: 1234567890,
455
+ });
456
+ const secondUsdc = new AssetValue({
457
+ chain: Chain.Avalanche,
458
+ decimal: 6,
459
+ symbol: "USDC-0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e",
460
+ value: 1234,
461
+ });
462
+
463
+ expect(firstThor.eqAsset(firstThor)).toBe(true);
464
+ expect(firstThor.eqAsset(secondThor)).toBe(true);
465
+ expect(firstThor.eqAsset(vThor)).toBe(false);
466
+ expect(firstThor.eqAsset(firstUsdc)).toBe(false);
467
+ expect(firstThor.eqAsset(secondUsdc)).toBe(false);
468
+
469
+ expect(firstUsdc.eqAsset(firstThor)).toBe(false);
470
+ expect(firstUsdc.eqAsset(secondThor)).toBe(false);
471
+ expect(firstUsdc.eqAsset(vThor)).toBe(false);
472
+ expect(firstUsdc.eqAsset(firstUsdc)).toBe(true);
473
+ expect(firstUsdc.eqAsset(secondUsdc)).toBe(true);
474
+ });
475
+
476
+ test("check if assets have same value, even if not same asset", () => {
477
+ const firstThor = AssetValue.from({ asset: "ETH.THOR", value: "20" });
478
+ const secondThor = AssetValue.from({ asset: "ETH.THOR", value: "35" });
479
+ const thirdThor = AssetValue.from({ asset: "ETH.THOR", value: "35" });
480
+ const vThor = AssetValue.from({ asset: "ETH.vTHOR", value: "20" });
481
+
482
+ expect(firstThor.eqValue(firstThor)).toBe(true);
483
+ expect(firstThor.eqValue(secondThor)).toBe(false);
484
+ expect(secondThor.eqValue(thirdThor)).toBe(true);
485
+ expect(firstThor.eqValue(vThor)).toBe(true);
486
+ });
487
+
488
+ test("check if assets have identical asset and value", () => {
489
+ const firstThor = AssetValue.from({ asset: "ETH.THOR", value: "20" });
490
+ const secondThor = AssetValue.from({ asset: "ETH.THOR", value: "35" });
491
+ const thirdThor = AssetValue.from({ asset: "ETH.THOR", value: "35" });
492
+ const vThor = AssetValue.from({ asset: "ETH.vTHOR", value: "20" });
493
+
494
+ expect(firstThor.eq(firstThor)).toBe(true);
495
+ expect(firstThor.eq(secondThor)).toBe(false);
496
+ expect(secondThor.eq(thirdThor)).toBe(true);
497
+ expect(firstThor.eq(vThor)).toBe(false);
498
+ });
499
+ });
500
+
501
+ describe("from bigint", () => {
502
+ test("returns asset value with correct decimal", async () => {
503
+ const avaxUSDCAsset = await AssetValue.from({
504
+ asset: `${Chain.Avalanche}.USDC-0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e`,
505
+ asyncTokenLookup: true,
506
+ value: 1234567800n,
507
+ });
508
+ expect(avaxUSDCAsset.getValue("string")).toBe("1234.5678");
509
+ });
510
+ });
511
+
512
+ describe("toString", () => {
513
+ test("returns asset value string/identifier", async () => {
514
+ const avaxUSDCAsset = new AssetValue({
515
+ chain: Chain.Avalanche,
516
+ decimal: 6,
517
+ symbol: "USDC-0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e",
518
+ value: 1234567890,
519
+ });
520
+ expect(avaxUSDCAsset.toString()).toBe("AVAX.USDC-0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E");
521
+
522
+ const thor = AssetValue.from({ asset: "ETH.THOR" });
523
+ expect(thor.toString()).toBe("ETH.THOR-0xa5f2211B9b8170F694421f2046281775E8468044");
524
+
525
+ const ethSynth = await AssetValue.from({ asset: "ETH/ETH", asyncTokenLookup: true });
526
+ expect(ethSynth.toString()).toBe("ETH/ETH");
527
+
528
+ const eth = await AssetValue.from({ asset: "eth.eth" });
529
+ expect(eth.toString()).toBe("ETH.ETH");
530
+
531
+ const ethFromChain = await AssetValue.from({ chain: Chain.Ethereum });
532
+ expect(ethFromChain.toString()).toBe("ETH.ETH");
533
+ });
534
+ });
535
+
536
+ describe("fromIdentifier", () => {
537
+ test("creates AssetValue from string", async () => {
538
+ const avaxUSDCAsset = await AssetValue.from({
539
+ asset: "AVAX.USDC-0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e",
540
+ asyncTokenLookup: true,
541
+ });
542
+
543
+ expect(avaxUSDCAsset).toEqual(
544
+ expect.objectContaining({
545
+ address: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
546
+ chain: Chain.Avalanche,
547
+ decimal: 6,
548
+ isGasAsset: false,
549
+ isSynthetic: false,
550
+ symbol: "USDC-0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
551
+ ticker: "USDC",
552
+ }),
553
+ );
554
+ });
555
+
556
+ test("creates AssetValue from string with multiple dashes", async () => {
557
+ const ethPendleLptAsset = await AssetValue.from({ asset: "ETH.PENDLE-LPT-0x1234", asyncTokenLookup: true });
558
+
559
+ expect(ethPendleLptAsset).toEqual(
560
+ expect.objectContaining({
561
+ address: "0x1234",
562
+ chain: Chain.Ethereum,
563
+ decimal: 18,
564
+ isGasAsset: false,
565
+ isSynthetic: false,
566
+ symbol: "PENDLE-LPT-0x1234",
567
+ ticker: "PENDLE-LPT",
568
+ }),
569
+ );
570
+ });
571
+ });
572
+
573
+ describe("fromString", () => {
574
+ test("creates AssetValue from string", async () => {
575
+ const fakeAvaxAssetString = "AVAX.ASDF-1234";
576
+ const fakeAvaxAsset = await AssetValue.from({ asset: fakeAvaxAssetString, asyncTokenLookup: true });
577
+
578
+ expect(fakeAvaxAsset).toEqual(
579
+ expect.objectContaining({
580
+ address: "1234",
581
+ chain: Chain.Avalanche,
582
+ decimal: 18,
583
+ isGasAsset: false,
584
+ isSynthetic: false,
585
+ symbol: "ASDF-1234",
586
+ ticker: "ASDF",
587
+ }),
588
+ );
589
+ });
590
+
591
+ test("creates AssetValue from string with multiple dashes", async () => {
592
+ const fakeAvaxAssetString = "AVAX.ASDF-LP-1234";
593
+ const fakeAvaxAsset = await AssetValue.from({ asset: fakeAvaxAssetString, asyncTokenLookup: true });
594
+
595
+ expect(fakeAvaxAsset).toEqual(
596
+ expect.objectContaining({
597
+ address: "1234",
598
+ chain: Chain.Avalanche,
599
+ decimal: 18,
600
+ isGasAsset: false,
601
+ isSynthetic: false,
602
+ symbol: "ASDF-LP-1234",
603
+ ticker: "ASDF-LP",
604
+ }),
605
+ );
606
+ });
607
+
608
+ test.todo("creates AssetValue with _ symbol", async () => {
609
+ const radixXWBTC = await AssetValue.from({
610
+ asset: "XRD.XWBTC-resource_rdx1t580qxc7upat7lww4l2c4jckacafjeudxj5wpjrrct0p3e82sq4y75",
611
+ asyncTokenLookup: true,
612
+ });
613
+
614
+ expect(radixXWBTC).toEqual(
615
+ expect.objectContaining({
616
+ address: "resource_rdx1t580qxc7upat7lww4l2c4jckacafjeudxj5wpjrrct0p3e82sq4y75",
617
+ chain: Chain.Radix,
618
+ decimal: 8,
619
+ isGasAsset: false,
620
+ isSynthetic: false,
621
+ symbol: "XWBTC-resource_rdx1t580qxc7upat7lww4l2c4jckacafjeudxj5wpjrrct0p3e82sq4y75",
622
+ ticker: "XWBTC",
623
+ }),
624
+ );
625
+ });
626
+ });
627
+
628
+ describe("fromStringWithBase", () => {
629
+ test("creates AssetValue from string with base", async () => {
630
+ const fakeAvaxAssetString = "AVAX.ASDF-1234";
631
+ const fakeAvaxAsset = await AssetValue.from({
632
+ asset: fakeAvaxAssetString,
633
+ asyncTokenLookup: true,
634
+ fromBaseDecimal: 8,
635
+ value: 1,
636
+ });
637
+
638
+ expect(fakeAvaxAsset).toEqual(
639
+ expect.objectContaining({
640
+ address: "1234",
641
+ chain: Chain.Avalanche,
642
+ decimal: 18,
643
+ isGasAsset: false,
644
+ isSynthetic: false,
645
+ symbol: "ASDF-1234",
646
+ ticker: "ASDF",
647
+ }),
648
+ );
649
+ expect(fakeAvaxAsset.getValue("string")).toBe("0.00000001");
650
+ expect(fakeAvaxAsset.getBaseValue("string")).toBe("10000000000");
651
+ });
652
+ });
653
+
654
+ describe("fromUrl", () => {
655
+ test("creates AssetValue from url like format", () => {
656
+ const synthETHString = "THOR.ETH.ETH";
657
+ const ethString = "ETH.ETH";
658
+ const thorString = "ETH.THOR-0xa5f2211b9b8170f694421f2046281775e8468044";
659
+ const synthThorString = "THOR.ETH.THOR-0xa5f2211b9b8170f694421f2046281775e8468044";
660
+ const synthDashesString = "THOR.ETH.PENDLE-LPT-0x1234";
661
+
662
+ const synthETH = AssetValue.fromUrl(synthETHString);
663
+ const eth = AssetValue.fromUrl(ethString);
664
+ const thor = AssetValue.fromUrl(thorString);
665
+ const synthThor = AssetValue.fromUrl(synthThorString);
666
+ const synthDashes = AssetValue.fromUrl(synthDashesString);
667
+
668
+ expect(synthETH.toString()).toBe("ETH/ETH");
669
+ expect(eth.toString()).toBe("ETH.ETH");
670
+ expect(thor.toString()).toBe("ETH.THOR-0xa5f2211B9b8170F694421f2046281775E8468044");
671
+ expect(synthThor.toString()).toBe("ETH/THOR-0xa5f2211b9b8170f694421f2046281775e8468044");
672
+ expect(synthDashes.toString()).toBe("ETH/PENDLE-LPT-0x1234");
673
+ });
674
+
675
+ test("fromUrl and toUrl roundtrip for all chains", () => {
676
+ // Native gas assets
677
+ const testCases = [
678
+ // EVM chains
679
+ { asset: "ETH.ETH", url: "ETH.ETH" },
680
+ { asset: "AVAX.AVAX", url: "AVAX.AVAX" },
681
+ { asset: "BSC.BNB", url: "BSC.BNB" },
682
+ { asset: "ARB.ETH", url: "ARB.ETH" },
683
+ { asset: "OP.ETH", url: "OP.ETH" },
684
+ { asset: "POL.POL", url: "POL.POL" },
685
+ { asset: "BASE.ETH", url: "BASE.ETH" },
686
+ { asset: "GNO.XDAI", url: "GNO.XDAI" }, // Gnosis uses XDAI in URLs
687
+ { asset: "AURORA.ETH", url: "AURORA.ETH" },
688
+ { asset: "BERA.BERA", url: "BERA.BERA" },
689
+
690
+ // UTXO chains
691
+ { asset: "BTC.BTC", url: "BTC.BTC" },
692
+ { asset: "BCH.BCH", url: "BCH.BCH" },
693
+ { asset: "LTC.LTC", url: "LTC.LTC" },
694
+ { asset: "DOGE.DOGE", url: "DOGE.DOGE" },
695
+ { asset: "DASH.DASH", url: "DASH.DASH" },
696
+ { asset: "ZEC.ZEC", url: "ZEC.ZEC" },
697
+
698
+ // Cosmos chains
699
+ { asset: "THOR.RUNE", url: "THOR.RUNE" },
700
+ { asset: "MAYA.CACAO", url: "MAYA.CACAO" },
701
+ { asset: "GAIA.ATOM", url: "GAIA.ATOM" },
702
+ { asset: "KUJI.KUJI", url: "KUJI.KUJI" },
703
+ { asset: "NOBLE.USDC", url: "NOBLE.USDC" },
704
+
705
+ // Other chains
706
+ { asset: "SOL.SOL", url: "SOL.SOL" },
707
+ { asset: "TRON.TRX", url: "TRON.TRX" },
708
+ { asset: "XRD.XRD", url: "XRD.XRD" },
709
+ { asset: "XRP.XRP", url: "XRP.XRP" },
710
+ { asset: "DOT.DOT", url: "DOT.DOT" },
711
+ { asset: "FLIP.FLIP", url: "FLIP.FLIP" },
712
+ { asset: "NEAR.NEAR", url: "NEAR.NEAR" },
713
+
714
+ // Tokens with addresses
715
+ {
716
+ asset: "ETH.USDC-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
717
+ url: "ETH.USDC-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
718
+ },
719
+ {
720
+ asset: "BSC.USDT-0x55d398326f99059fF775485246999027B3197955",
721
+ url: "BSC.USDT-0x55d398326f99059fF775485246999027B3197955",
722
+ },
723
+ {
724
+ asset: "AVAX.USDC.e-0xA7D7079b0FEaD91F3e65f86E8915Cb59c1a4C664",
725
+ url: "AVAX.USDC__e-0xA7D7079b0FEaD91F3e65f86E8915Cb59c1a4C664",
726
+ },
727
+ {
728
+ asset: "ARB.WETH-0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
729
+ url: "ARB.WETH-0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
730
+ },
731
+ {
732
+ asset: "POL.USDC-0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
733
+ url: "POL.USDC-0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
734
+ },
735
+ {
736
+ asset: "BASE.USDC-0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
737
+ url: "BASE.USDC-0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
738
+ },
739
+
740
+ // Near tokens - dots are encoded as __
741
+ { asset: "NEAR.wNEAR-wrap.near", url: "NEAR.wNEAR-wrap__near" },
742
+ { asset: "NEAR.ETH-eth.bridge.near", url: "NEAR.ETH-eth__bridge__near" },
743
+ { asset: "NEAR.USDT-usdt.tether-token.near", url: "NEAR.USDT-usdt__tether-token__near" },
744
+ { asset: "NEAR.BLACKDRAGON-blackdragon.tkn.near", url: "NEAR.BLACKDRAGON-blackdragon__tkn__near" },
745
+ { asset: "NEAR.SHITZU-token.0xshitzu.near", url: "NEAR.SHITZU-token__0xshitzu__near" },
746
+ { asset: "NEAR.BTC-nbtc.bridge.near", url: "NEAR.BTC-nbtc__bridge__near" },
747
+ {
748
+ asset: "NEAR.AURORA-aaaaaa20d9e0e2461697782ef11675f668207961.factory.bridge.near",
749
+ url: "NEAR.AURORA-aaaaaa20d9e0e2461697782ef11675f668207961__factory__bridge__near",
750
+ },
751
+ {
752
+ asset: "NEAR.FRAX-853d955acef822db058eb8505911ed77f175b99e.factory.bridge.near",
753
+ url: "NEAR.FRAX-853d955acef822db058eb8505911ed77f175b99e__factory__bridge__near",
754
+ },
755
+ {
756
+ asset: "NEAR.wBTC-2260fac5e5542a773aa44fbcfedf7c193bc2c599.factory.bridge.near",
757
+ url: "NEAR.wBTC-2260fac5e5542a773aa44fbcfedf7c193bc2c599__factory__bridge__near",
758
+ },
759
+ {
760
+ asset: "NEAR.HAPI-d9c2d319cd7e6177336b0a9c93c21cb48d84fb54.factory.bridge.near",
761
+ url: "NEAR.HAPI-d9c2d319cd7e6177336b0a9c93c21cb48d84fb54__factory__bridge__near",
762
+ },
763
+ { asset: "NEAR.PURGE-purge-558.meme-cooking.near", url: "NEAR.PURGE-purge-558__meme-cooking__near" },
764
+ { asset: "NEAR.ABG-abg-966.meme-cooking.near", url: "NEAR.ABG-abg-966__meme-cooking__near" },
765
+ { asset: "NEAR.NOEAR-noear-324.meme-cooking.near", url: "NEAR.NOEAR-noear-324__meme-cooking__near" },
766
+ { asset: "NEAR.JAMBO-jambo-1679.meme-cooking.near", url: "NEAR.JAMBO-jambo-1679__meme-cooking__near" },
767
+ { asset: "NEAR.GNEAR-gnear-229.meme-cooking.near", url: "NEAR.GNEAR-gnear-229__meme-cooking__near" },
768
+ { asset: "NEAR.mpDAO-mpdao-token.near", url: "NEAR.mpDAO-mpdao-token__near" },
769
+ { asset: "NEAR.NearKat-kat.token0.near", url: "NEAR.NearKat-kat__token0__near" },
770
+ { asset: "NEAR.TESTNEBULA-test-token.highdome3013.near", url: "NEAR.TESTNEBULA-test-token__highdome3013__near" },
771
+ { asset: "NEAR.RHEA-token.rhealab.near", url: "NEAR.RHEA-token__rhealab__near" },
772
+ { asset: "NEAR.PUBLIC-token.publicailab.near", url: "NEAR.PUBLIC-token__publicailab__near" },
773
+ { asset: "NEAR.SWEAT-token.sweat", url: "NEAR.SWEAT-token__sweat" },
774
+ {
775
+ asset: "NEAR.USDC-17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1",
776
+ url: "NEAR.USDC-17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1",
777
+ },
778
+
779
+ // THORChain Synths - THOR prefix for chain, then chain.symbol
780
+ { asset: "THOR.ETH/ETH", url: "THOR.ETH.ETH" },
781
+ { asset: "THOR.BTC/BTC", url: "THOR.BTC.BTC" },
782
+ { asset: "THOR.AVAX/AVAX", url: "THOR.AVAX.AVAX" },
783
+ { asset: "THOR.BSC/BNB", url: "THOR.BSC.BNB" },
784
+ { asset: "THOR.GAIA/ATOM", url: "THOR.GAIA.ATOM" },
785
+
786
+ // THORChain Trade assets - THOR prefix, then chain..symbol (double dot)
787
+ { asset: "THOR.ETH~ETH", url: "THOR.ETH..ETH" },
788
+ { asset: "THOR.BTC~BTC", url: "THOR.BTC..BTC" },
789
+ { asset: "THOR.AVAX~AVAX", url: "THOR.AVAX..AVAX" },
790
+
791
+ // Maya synths - MAYA prefix for chain, then chain.symbol
792
+ { asset: "MAYA.ETH/ETH", url: "MAYA.ETH.ETH" },
793
+ { asset: "MAYA.BTC/BTC", url: "MAYA.BTC.BTC" },
794
+
795
+ // Synths with tokens
796
+ {
797
+ asset: "MAYA.ETH/USDC-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
798
+ url: "MAYA.ETH.USDC-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
799
+ },
800
+ {
801
+ asset: "THOR.BSC/USDT-0x55d398326f99059fF775485246999027B3197955",
802
+ url: "THOR.BSC.USDT-0x55d398326f99059fF775485246999027B3197955",
803
+ },
804
+
805
+ // Trade assets with tokens
806
+ {
807
+ asset: "THOR.ETH~USDC-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
808
+ url: "THOR.ETH..USDC-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
809
+ },
810
+ {
811
+ asset: "THOR.BSC~USDT-0x55d398326f99059fF775485246999027B3197955",
812
+ url: "THOR.BSC..USDT-0x55d398326f99059fF775485246999027B3197955",
813
+ },
814
+ ];
815
+
816
+ for (const { asset, url } of testCases) {
817
+ const assetValue = AssetValue.from({ asset });
818
+ expect(assetValue.toUrl()).toBe(url);
819
+
820
+ const fromUrl = AssetValue.fromUrl(url);
821
+ // Synths and trade assets need special handling with includeSynthProtocol
822
+ if ((asset.startsWith("MAYA.") || asset.startsWith("THOR.")) && (asset.includes("/") || asset.includes("~"))) {
823
+ expect(fromUrl.toString({ includeSynthProtocol: true })).toBe(asset);
824
+ } else {
825
+ expect(fromUrl.toString()).toBe(asset);
826
+ }
827
+ }
828
+ });
829
+
830
+ test("handles complex token symbols in URLs", () => {
831
+ // Tokens with multiple dashes in symbol
832
+ const pendle = AssetValue.from({ asset: "ETH.PENDLE-LPT-0x1234567890abcdef" });
833
+ expect(pendle.toUrl()).toBe("ETH.PENDLE-LPT-0x1234567890abcdef");
834
+ expect(pendle.ticker).toBe("PENDLE-LPT");
835
+ expect(pendle.address).toBe("0x1234567890abcdef");
836
+
837
+ const fromUrlPendle = AssetValue.fromUrl("ETH.PENDLE-LPT-0x1234567890abcdef");
838
+ expect(fromUrlPendle.toString()).toBe("ETH.PENDLE-LPT-0x1234567890abcdef");
839
+ expect(fromUrlPendle.ticker).toBe("PENDLE-LPT");
840
+ expect(fromUrlPendle.address).toBe("0x1234567890abcdef");
841
+
842
+ // Synth with complex symbol
843
+ const synthPendle = AssetValue.from({ asset: "THOR.ETH/PENDLE-LPT-0x1234567890abcdef" });
844
+ expect(synthPendle.toUrl()).toBe("THOR.ETH.PENDLE-LPT-0x1234567890abcdef");
845
+
846
+ const fromUrlSynthPendle = AssetValue.fromUrl("THOR.ETH.PENDLE-LPT-0x1234567890abcdef");
847
+ expect(fromUrlSynthPendle.toString({ includeSynthProtocol: true })).toBe("THOR.ETH/PENDLE-LPT-0x1234567890abcdef");
848
+ expect(fromUrlSynthPendle.ticker).toBe("PENDLE-LPT");
849
+ expect(fromUrlSynthPendle.address).toBe("0x1234567890abcdef");
850
+
851
+ // Trade asset with complex symbol
852
+ const tradePendle = AssetValue.from({ asset: "THOR.ETH~PENDLE-LPT-0x1234567890abcdef" });
853
+ expect(tradePendle.toUrl()).toBe("THOR.ETH..PENDLE-LPT-0x1234567890abcdef");
854
+
855
+ const fromUrlTradePendle = AssetValue.fromUrl("THOR.ETH..PENDLE-LPT-0x1234567890abcdef");
856
+ expect(fromUrlTradePendle.toString({ includeSynthProtocol: true })).toBe("THOR.ETH~PENDLE-LPT-0x1234567890abcdef");
857
+ expect(fromUrlTradePendle.ticker).toBe("PENDLE-LPT");
858
+ expect(fromUrlTradePendle.address).toBe("0x1234567890abcdef");
859
+ });
860
+
861
+ test("handles special chain cases in URLs", () => {
862
+ // Gnosis chain special case (GNO -> XDAI)
863
+ const gnosis = AssetValue.from({ asset: "GNO.XDAI" });
864
+ expect(gnosis.toUrl()).toBe("GNO.XDAI");
865
+
866
+ const fromUrlGnosis = AssetValue.fromUrl("GNO.XDAI");
867
+ expect(fromUrlGnosis.toString()).toBe("GNO.XDAI");
868
+ expect(fromUrlGnosis.ticker).toBe("XDAI");
869
+ expect(fromUrlGnosis.isGasAsset).toBe(true);
870
+
871
+ // Tron TRX
872
+ const tron = AssetValue.from({ asset: "TRON.TRX" });
873
+ expect(tron.toUrl()).toBe("TRON.TRX");
874
+
875
+ const fromUrlTron = AssetValue.fromUrl("TRON.TRX");
876
+ expect(fromUrlTron.toString()).toBe("TRON.TRX");
877
+ expect(fromUrlTron.ticker).toBe("TRX");
878
+ expect(fromUrlTron.isGasAsset).toBe(true);
879
+ });
880
+ });
881
+
882
+ describe("fromIdentifierSync", () => {
883
+ test("(same as fromIdentifier) - creates AssetValue from string via `@swapkit/tokens` lists", async () => {
884
+ await AssetValue.loadStaticAssets();
885
+ const thor = AssetValue.from({ asset: "ARB.USDT-0XFD086BC7CD5C481DCC9C85EBE478A1C0B69FCBB9" });
886
+
887
+ expect(thor).toBeDefined();
888
+ expect(thor).toEqual(
889
+ expect.objectContaining({
890
+ address: "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9",
891
+ chain: Chain.Arbitrum,
892
+ decimal: 6,
893
+ isGasAsset: false,
894
+ isSynthetic: false,
895
+ symbol: "USDT-0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9",
896
+ ticker: "USDT",
897
+ }),
898
+ );
899
+ });
900
+ });
901
+
902
+ describe("fromStringSync", () => {
903
+ test("creates AssetValue from string via `@swapkit/tokens` lists", async () => {
904
+ await AssetValue.loadStaticAssets();
905
+ const thor = AssetValue.from({ asset: "ETH.THOR-0xa5f2211b9b8170f694421f2046281775e8468044" });
906
+
907
+ expect(thor).toBeDefined();
908
+ expect(thor).toEqual(
909
+ expect.objectContaining({
910
+ address: "0xa5f2211B9b8170F694421f2046281775E8468044",
911
+ chain: Chain.Ethereum,
912
+ decimal: 18,
913
+ isGasAsset: false,
914
+ isSynthetic: false,
915
+ symbol: "THOR-0xa5f2211B9b8170F694421f2046281775E8468044",
916
+ ticker: "THOR",
917
+ }),
918
+ );
919
+
920
+ const usdc = AssetValue.from({ asset: "ETH.USDC-0XA0B86991C6218B36C1D19D4A2E9EB0CE3606EB48" });
921
+ expect(usdc).toBeDefined();
922
+ expect(usdc).toEqual(
923
+ expect.objectContaining({
924
+ address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
925
+ chain: Chain.Ethereum,
926
+ decimal: 6,
927
+ isGasAsset: false,
928
+ isSynthetic: false,
929
+ symbol: "USDC-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
930
+ ticker: "USDC",
931
+ }),
932
+ );
933
+ });
934
+
935
+ test("returns safe decimals if string is not in `@swapkit/tokens` lists", async () => {
936
+ await AssetValue.loadStaticAssets();
937
+ const fakeAvaxUSDCAssetString = "AVAX.USDC-1234";
938
+ const fakeAvaxUSDCAsset = AssetValue.from({ asset: fakeAvaxUSDCAssetString });
939
+
940
+ expect(fakeAvaxUSDCAsset).toBeDefined();
941
+ expect(fakeAvaxUSDCAsset).toEqual(
942
+ expect.objectContaining({
943
+ address: "1234",
944
+ chain: Chain.Avalanche,
945
+ decimal: 18,
946
+ isGasAsset: false,
947
+ isSynthetic: false,
948
+ symbol: "USDC-1234",
949
+ ticker: "USDC",
950
+ }),
951
+ );
952
+ });
953
+
954
+ test("returns safe decimals if string is not in `@swapkit/tokens` lists with multiple dashes", async () => {
955
+ await AssetValue.loadStaticAssets();
956
+ const fakeAvaxUSDCAssetString = "AVAX.USDC-LPT-1234";
957
+ const fakeAvaxUSDCAsset2 = AssetValue.from({ asset: fakeAvaxUSDCAssetString });
958
+
959
+ expect(fakeAvaxUSDCAsset2).toBeDefined();
960
+ expect(fakeAvaxUSDCAsset2).toEqual(
961
+ expect.objectContaining({
962
+ address: "1234",
963
+ chain: Chain.Avalanche,
964
+ decimal: 18,
965
+ isGasAsset: false,
966
+ isSynthetic: false,
967
+ symbol: "USDC-LPT-1234",
968
+ ticker: "USDC-LPT",
969
+ }),
970
+ );
971
+ });
972
+
973
+ test("returns proper avax string with address from `@swapkit/tokens` lists", async () => {
974
+ await AssetValue.loadStaticAssets();
975
+ const avaxBTCb = "AVAX.BTC.b-0x152b9d0fdc40c096757f570a51e494bd4b943e50";
976
+ const AvaxBTCb = AssetValue.from({ asset: avaxBTCb });
977
+
978
+ expect(AvaxBTCb).toBeDefined();
979
+ expect(AvaxBTCb).toEqual(
980
+ expect.objectContaining({
981
+ address: "0x152b9d0FdC40C096757F570A51E494bd4b943E50",
982
+ chain: Chain.Avalanche,
983
+ decimal: 8,
984
+ isGasAsset: false,
985
+ isSynthetic: false,
986
+ symbol: "BTC.B-0x152b9d0FdC40C096757F570A51E494bd4b943E50",
987
+ ticker: "BTC.B",
988
+ }),
989
+ );
990
+ });
991
+ });
992
+
993
+ describe("fromStringWithBaseSync", () => {
994
+ test("creates AssetValue from string with base decimals via `@swapkit/tokens` lists", async () => {
995
+ await AssetValue.loadStaticAssets();
996
+ const btc = AssetValue.from({ asset: "BTC.BTC", fromBaseDecimal: 8, value: 5200000000000 });
997
+
998
+ expect(btc).toBeDefined();
999
+ expect(btc).toEqual(
1000
+ expect.objectContaining({
1001
+ chain: Chain.Bitcoin,
1002
+ decimal: 8,
1003
+ isGasAsset: true,
1004
+ isSynthetic: false,
1005
+ symbol: "BTC",
1006
+ ticker: "BTC",
1007
+ }),
1008
+ );
1009
+
1010
+ expect(btc.getValue("string")).toBe("52000");
1011
+ expect(btc.getBaseValue("string")).toBe("5200000000000");
1012
+ });
1013
+
1014
+ test("returns safe decimals if string is not in `@swapkit/tokens` lists", async () => {
1015
+ await AssetValue.loadStaticAssets();
1016
+ const fakeAvaxUSDCAssetString = "AVAX.USDC-1234";
1017
+ const fakeAvaxUSDCAsset = AssetValue.from({ asset: fakeAvaxUSDCAssetString, fromBaseDecimal: 8, value: 1 });
1018
+
1019
+ expect(fakeAvaxUSDCAsset).toBeDefined();
1020
+ expect(fakeAvaxUSDCAsset).toEqual(
1021
+ expect.objectContaining({
1022
+ address: "1234",
1023
+ chain: Chain.Avalanche,
1024
+ decimal: 18,
1025
+ isGasAsset: false,
1026
+ isSynthetic: false,
1027
+ symbol: "USDC-1234",
1028
+ ticker: "USDC",
1029
+ }),
1030
+ );
1031
+
1032
+ expect(fakeAvaxUSDCAsset.getValue("string")).toBe("0.00000001");
1033
+ expect(fakeAvaxUSDCAsset.getBaseValue("string")).toBe("10000000000");
1034
+ });
1035
+
1036
+ test("returns proper avax string with address from `@swapkit/tokens` lists", async () => {
1037
+ await AssetValue.loadStaticAssets();
1038
+ const avaxUSDC = "AVAX.USDC-0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e";
1039
+ const AvaxUSDC = AssetValue.from({ asset: avaxUSDC, fromBaseDecimal: 8, value: 100000000 });
1040
+
1041
+ expect(AvaxUSDC).toBeDefined();
1042
+ expect(AvaxUSDC).toEqual(
1043
+ expect.objectContaining({
1044
+ address: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
1045
+ chain: Chain.Avalanche,
1046
+ decimal: 6,
1047
+ isGasAsset: false,
1048
+ isSynthetic: false,
1049
+ symbol: "USDC-0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
1050
+ ticker: "USDC",
1051
+ }),
1052
+ );
1053
+
1054
+ expect(AvaxUSDC.getValue("string")).toBe("1");
1055
+ expect(AvaxUSDC.getBaseValue("string")).toBe("1000000");
1056
+ });
1057
+ });
1058
+
1059
+ describe("fromChainOrSignature", () => {
1060
+ test("creates AssetValue from common asset string or chain", () => {
1061
+ const cosmosAsset = AssetValue.from({ chain: Chain.Cosmos });
1062
+ const { baseDecimal: gaiaDecimal } = getChainConfig(Chain.Cosmos);
1063
+ expect(cosmosAsset).toEqual(
1064
+ expect.objectContaining({
1065
+ address: undefined,
1066
+ chain: Chain.Cosmos,
1067
+ decimal: gaiaDecimal,
1068
+ isGasAsset: true,
1069
+ isSynthetic: false,
1070
+ symbol: "ATOM",
1071
+ ticker: "ATOM",
1072
+ type: "Native",
1073
+ }),
1074
+ );
1075
+
1076
+ const bscAsset = AssetValue.from({ chain: Chain.BinanceSmartChain });
1077
+ const { baseDecimal: bscDecimal } = getChainConfig(Chain.BinanceSmartChain);
1078
+ expect(bscAsset).toEqual(
1079
+ expect.objectContaining({
1080
+ address: undefined,
1081
+ chain: Chain.BinanceSmartChain,
1082
+ decimal: bscDecimal,
1083
+ isGasAsset: true,
1084
+ isSynthetic: false,
1085
+ symbol: "BNB",
1086
+ ticker: "BNB",
1087
+ type: "Native",
1088
+ }),
1089
+ );
1090
+
1091
+ const thorAsset = AssetValue.from({ chain: Chain.THORChain });
1092
+ const { baseDecimal: thorDecimal } = getChainConfig(Chain.THORChain);
1093
+ expect(thorAsset).toEqual(
1094
+ expect.objectContaining({
1095
+ address: undefined,
1096
+ chain: Chain.THORChain,
1097
+ decimal: thorDecimal,
1098
+ isGasAsset: true,
1099
+ isSynthetic: false,
1100
+ symbol: "RUNE",
1101
+ ticker: "RUNE",
1102
+ type: "Native",
1103
+ }),
1104
+ );
1105
+
1106
+ const cacaoAsset = AssetValue.from({ chain: Chain.Maya });
1107
+ expect(cacaoAsset).toEqual(
1108
+ expect.objectContaining({
1109
+ address: undefined,
1110
+ chain: Chain.Maya,
1111
+ decimal: 10,
1112
+ isGasAsset: true,
1113
+ isSynthetic: false,
1114
+ symbol: "CACAO",
1115
+ ticker: "CACAO",
1116
+ type: "Native",
1117
+ }),
1118
+ );
1119
+
1120
+ const thor = AssetValue.from({ asset: "ETH.THOR" });
1121
+ expect(thor).toEqual(
1122
+ expect.objectContaining({
1123
+ address: "0xa5f2211B9b8170F694421f2046281775E8468044",
1124
+ chain: Chain.Ethereum,
1125
+ decimal: 18,
1126
+ isGasAsset: false,
1127
+ isSynthetic: false,
1128
+ symbol: "THOR-0xa5f2211B9b8170F694421f2046281775E8468044",
1129
+ ticker: "THOR",
1130
+ }),
1131
+ );
1132
+
1133
+ // FIXME: just some casing? is it safe to change
1134
+ // const vthor = AssetValue.from({ asset: "ETH.vTHOR" });
1135
+ // expect(vthor).toEqual(
1136
+ // expect.objectContaining({
1137
+ // address: "0x815c23eca83261b6ec689b60cc4a58b54bc24d8d",
1138
+ // chain: Chain.Ethereum,
1139
+ // decimal: 18,
1140
+ // isGasAsset: false,
1141
+ // isSynthetic: false,
1142
+ // symbol: "vTHOR-0x815c23eca83261b6ec689b60cc4a58b54bc24d8d",
1143
+ // ticker: "vTHOR",
1144
+ // }),
1145
+ // );
1146
+
1147
+ const arbAsset = AssetValue.from({ chain: Chain.Arbitrum });
1148
+ const { baseDecimal: arbDecimal } = getChainConfig(Chain.Arbitrum);
1149
+ expect(arbAsset).toEqual(
1150
+ expect.objectContaining({
1151
+ address: undefined,
1152
+ chain: Chain.Arbitrum,
1153
+ decimal: arbDecimal,
1154
+ isGasAsset: true,
1155
+ isSynthetic: false,
1156
+ symbol: "ETH",
1157
+ ticker: "ETH",
1158
+ type: "Native",
1159
+ }),
1160
+ );
1161
+
1162
+ const opAsset = AssetValue.from({ chain: Chain.Optimism });
1163
+ const { baseDecimal: opDecimal } = getChainConfig(Chain.Optimism);
1164
+ expect(opAsset).toEqual(
1165
+ expect.objectContaining({
1166
+ address: undefined,
1167
+ chain: Chain.Optimism,
1168
+ decimal: opDecimal,
1169
+ isGasAsset: true,
1170
+ isSynthetic: false,
1171
+ symbol: "ETH",
1172
+ ticker: "ETH",
1173
+ type: "Native",
1174
+ }),
1175
+ );
1176
+
1177
+ const xrdAsset = AssetValue.from({ chain: Chain.Radix });
1178
+ const { baseDecimal: xrdDecimal } = getChainConfig(Chain.Radix);
1179
+ expect(xrdAsset).toEqual(
1180
+ expect.objectContaining({
1181
+ chain: Chain.Radix,
1182
+ decimal: xrdDecimal,
1183
+ isGasAsset: true,
1184
+ isSynthetic: false,
1185
+ ticker: "XRD",
1186
+ type: "Native",
1187
+ }),
1188
+ );
1189
+
1190
+ const trxAsset = AssetValue.from({ chain: Chain.Tron });
1191
+ const { baseDecimal: trxDecimal } = getChainConfig(Chain.Tron);
1192
+ expect(trxAsset).toEqual(
1193
+ expect.objectContaining({
1194
+ chain: Chain.Tron,
1195
+ decimal: trxDecimal,
1196
+ isGasAsset: true,
1197
+ isSynthetic: false,
1198
+ symbol: "TRX",
1199
+ ticker: "TRX",
1200
+ type: "Native",
1201
+ }),
1202
+ );
1203
+
1204
+ const trxAssetFromString = AssetValue.from({ asset: "TRON.TRX" });
1205
+ const { baseDecimal: trxDecimalFromString } = getChainConfig(Chain.Tron);
1206
+ expect(trxAssetFromString).toEqual(
1207
+ expect.objectContaining({
1208
+ chain: Chain.Tron,
1209
+ decimal: trxDecimalFromString,
1210
+ isGasAsset: true,
1211
+ isSynthetic: false,
1212
+ symbol: "TRX",
1213
+ ticker: "TRX",
1214
+ type: "Native",
1215
+ }),
1216
+ );
1217
+ });
1218
+
1219
+ test("keep SOL address casing", () => {
1220
+ const solUsdc = AssetValue.from({ asset: "SOL.USDC-EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" });
1221
+ expect(solUsdc).toEqual(
1222
+ expect.objectContaining({
1223
+ address: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
1224
+ chain: Chain.Solana,
1225
+ isGasAsset: false,
1226
+ isSynthetic: false,
1227
+ symbol: "USDC-EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
1228
+ ticker: "USDC",
1229
+ }),
1230
+ );
1231
+ });
1232
+
1233
+ test("TRC20 tokens are not marked as gas assets", () => {
1234
+ const tronUsdt = AssetValue.from({ asset: "TRON.USDT-TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t" });
1235
+
1236
+ expect(tronUsdt).toEqual(
1237
+ expect.objectContaining({
1238
+ address: "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
1239
+ chain: Chain.Tron,
1240
+ isGasAsset: false,
1241
+ isSynthetic: false,
1242
+ symbol: "USDT-TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
1243
+ ticker: "USDT",
1244
+ }),
1245
+ );
1246
+ });
1247
+ });
1248
+
1249
+ describe("getMinAmountByChain", () => {
1250
+ test("returns min amount for chain", () => {
1251
+ expect(getMinAmountByChain(Chain.THORChain).getValue("string")).toBe("0");
1252
+ expect(getMinAmountByChain(Chain.Maya).getValue("string")).toBe("0");
1253
+ expect(getMinAmountByChain(Chain.Cosmos).getValue("string")).toBe("0.000001");
1254
+
1255
+ expect(getMinAmountByChain(Chain.Bitcoin).getValue("string")).toBe("0.00010001");
1256
+ expect(getMinAmountByChain(Chain.Litecoin).getValue("string")).toBe("0.00010001");
1257
+ expect(getMinAmountByChain(Chain.BitcoinCash).getValue("string")).toBe("0.00010001");
1258
+ expect(getMinAmountByChain(Chain.Dogecoin).getValue("string")).toBe("1.00000001");
1259
+
1260
+ expect(getMinAmountByChain(Chain.BinanceSmartChain).getValue("string")).toBe("0.00000001");
1261
+ expect(getMinAmountByChain(Chain.Ethereum).getValue("string")).toBe("0.00000001");
1262
+ expect(getMinAmountByChain(Chain.Avalanche).getValue("string")).toBe("0.00000001");
1263
+ expect(getMinAmountByChain(Chain.Arbitrum).getValue("string")).toBe("0.00000001");
1264
+ expect(getMinAmountByChain(Chain.Optimism).getValue("string")).toBe("0.00000001");
1265
+ });
1266
+ });
1267
+
1268
+ describe("asyncTokenLookup", () => {
1269
+ describe("EVM chains with asyncTokenLookup", () => {
1270
+ test("fetches Ethereum USDC token info with chain and address only", async () => {
1271
+ const assetValue = await AssetValue.from({
1272
+ address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
1273
+ asyncTokenLookup: true,
1274
+ chain: Chain.Ethereum,
1275
+ value: 1000,
1276
+ });
1277
+
1278
+ expect(assetValue.decimal).toBe(6); // USDC has 6 decimals
1279
+ expect(assetValue.symbol).toContain("USDC");
1280
+ expect(assetValue.getValue("string")).toBe("1000");
1281
+ });
1282
+
1283
+ test("fetches Ethereum WETH token info with asset string and asyncTokenLookup", async () => {
1284
+ const assetValue = await AssetValue.from({
1285
+ asset: "ETH.WETH-0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
1286
+ asyncTokenLookup: true,
1287
+ value: 0.5,
1288
+ });
1289
+
1290
+ expect(assetValue.decimal).toBe(18);
1291
+ expect(assetValue.symbol).toBe("WETH-0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2");
1292
+ expect(assetValue.ticker).toBe("WETH");
1293
+ expect(assetValue.getValue("string")).toBe("0.5");
1294
+ });
1295
+
1296
+ test("fetches Avalanche USDC.e with chain and address", async () => {
1297
+ const assetValue = await AssetValue.from({
1298
+ address: "0xA7D7079b0FEaD91F3e65f86E8915Cb59c1a4C664",
1299
+ asyncTokenLookup: true,
1300
+ chain: Chain.Avalanche,
1301
+ value: 100,
1302
+ });
1303
+
1304
+ expect(assetValue.decimal).toBe(6);
1305
+ expect(assetValue.symbol).toContain("USDC");
1306
+ expect(assetValue.getValue("string")).toBe("100");
1307
+ });
1308
+
1309
+ test("fetches BSC BUSD with full asset string", async () => {
1310
+ const assetValue = await AssetValue.from({
1311
+ asset: "BSC.BUSD-0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56",
1312
+ asyncTokenLookup: true,
1313
+ value: 50.25,
1314
+ });
1315
+
1316
+ expect(assetValue.decimal).toBe(18);
1317
+ expect(assetValue.symbol).toBe("BUSD-0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56");
1318
+ expect(assetValue.ticker).toBe("BUSD");
1319
+ expect(assetValue.getValue("string")).toBe("50.25");
1320
+ });
1321
+
1322
+ test("fetches Arbitrum USDC native with chain and address", async () => {
1323
+ const assetValue = await AssetValue.from({
1324
+ address: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
1325
+ asyncTokenLookup: true,
1326
+ chain: Chain.Arbitrum,
1327
+ fromBaseDecimal: 6,
1328
+ value: 1000000,
1329
+ });
1330
+
1331
+ expect(assetValue.decimal).toBe(6);
1332
+ expect(assetValue.getBaseValue("string")).toBe("1000000");
1333
+ expect(assetValue.getValue("string")).toBe("1");
1334
+ expect(assetValue.toString()).toBe("ARB.USDC-0xaf88d065e77c8cC2239327C5EDb3A432268e5831");
1335
+ });
1336
+ });
1337
+
1338
+ describe("Solana with asyncTokenLookup", () => {
1339
+ test("fetches Solana USDC with chain and address", async () => {
1340
+ const assetValue = await AssetValue.from({
1341
+ address: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
1342
+ asyncTokenLookup: true,
1343
+ chain: Chain.Solana,
1344
+ value: 250.75,
1345
+ });
1346
+
1347
+ expect(assetValue.decimal).toBe(6);
1348
+ expect(assetValue.symbol).toContain("USDC");
1349
+ expect(assetValue.getValue("string")).toBe("250.75");
1350
+ });
1351
+
1352
+ test("fetches Solana token with asset string", async () => {
1353
+ const assetValue = await AssetValue.from({
1354
+ asset: "SOL.USDT-Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB",
1355
+ asyncTokenLookup: true,
1356
+ value: 100,
1357
+ });
1358
+
1359
+ expect(assetValue.decimal).toBe(6);
1360
+ expect(assetValue.symbol).toBe("USDT-Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB");
1361
+ expect(assetValue.ticker).toBe("USDT");
1362
+ });
1363
+ });
1364
+
1365
+ describe("Tron with asyncTokenLookup", () => {
1366
+ test("fetches Tron USDT with chain and address", async () => {
1367
+ const assetValue = await AssetValue.from({
1368
+ address: "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
1369
+ asyncTokenLookup: true,
1370
+ chain: Chain.Tron,
1371
+ value: 500,
1372
+ });
1373
+
1374
+ expect(assetValue.decimal).toBe(6);
1375
+ expect(assetValue.symbol).toContain("USDT");
1376
+ expect(assetValue.getValue("string")).toBe("500");
1377
+ });
1378
+
1379
+ test("fetches Tron token with asset string", async () => {
1380
+ const assetValue = await AssetValue.from({
1381
+ asset: "TRON.USDC-TEkxiTehnzSmSe2XqrBj4w32RUN966rdz8",
1382
+ asyncTokenLookup: true,
1383
+ value: 1234.56,
1384
+ });
1385
+
1386
+ expect(assetValue.decimal).toBe(6);
1387
+ expect(assetValue.symbol).toBe("USDC-TEkxiTehnzSmSe2XqrBj4w32RUN966rdz8");
1388
+ expect(assetValue.getValue("string")).toBe("1234.56");
1389
+ });
1390
+ });
1391
+
1392
+ describe("Near with asyncTokenLookup", () => {
1393
+ test("fetches Near wNEAR with chain and address", async () => {
1394
+ const assetValue = await AssetValue.from({
1395
+ address: "wrap.near",
1396
+ asyncTokenLookup: true,
1397
+ chain: Chain.Near,
1398
+ value: 10,
1399
+ });
1400
+
1401
+ expect(assetValue.decimal).toBe(24);
1402
+ expect(assetValue.symbol).toContain("wNEAR");
1403
+ expect(assetValue.getValue("string")).toBe("10");
1404
+ });
1405
+
1406
+ test("fetches Near USDC with asset string", async () => {
1407
+ const assetValue = await AssetValue.from({
1408
+ asset: "NEAR.USDC-17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1",
1409
+ asyncTokenLookup: true,
1410
+ value: 999.99,
1411
+ });
1412
+
1413
+ expect(assetValue.decimal).toBe(6);
1414
+ expect(assetValue.symbol).toBe("USDC-17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1");
1415
+ expect(assetValue.ticker).toBe("USDC");
1416
+ expect(assetValue.getValue("string")).toBe("999.99");
1417
+ });
1418
+
1419
+ test("fetches Near token with .near address format", async () => {
1420
+ const assetValue = await AssetValue.from({
1421
+ address: "usdt.tether-token.near",
1422
+ asyncTokenLookup: true,
1423
+ chain: Chain.Near,
1424
+ value: 2500,
1425
+ });
1426
+
1427
+ expect(assetValue.decimal).toBe(6);
1428
+ expect(assetValue.toString()).toBe("NEAR.USDT-usdt.tether-token.near");
1429
+ expect(assetValue.getValue("string")).toBe("2500");
1430
+ });
1431
+ });
1432
+
1433
+ describe("Edge cases and caching", () => {
1434
+ test("handles invalid token address gracefully", async () => {
1435
+ const assetValue = await AssetValue.from({
1436
+ address: "0xinvalidaddress",
1437
+ asyncTokenLookup: true,
1438
+ chain: Chain.Ethereum,
1439
+ value: 10,
1440
+ });
1441
+
1442
+ expect(assetValue.decimal).toBe(18);
1443
+ expect(assetValue.getValue("string")).toBe("10");
1444
+ });
1445
+
1446
+ test("works with fromBaseDecimal parameter", async () => {
1447
+ const assetValue = await AssetValue.from({
1448
+ address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
1449
+ asyncTokenLookup: true,
1450
+ chain: Chain.Ethereum,
1451
+ fromBaseDecimal: 6,
1452
+ value: 1000000,
1453
+ });
1454
+
1455
+ expect(assetValue.decimal).toBe(6);
1456
+ expect(assetValue.getBaseValue("string")).toBe("1000000");
1457
+ expect(assetValue.getValue("string")).toBe("1");
1458
+ });
1459
+
1460
+ test("synchronous call without asyncTokenLookup", async () => {
1461
+ await AssetValue.loadStaticAssets();
1462
+
1463
+ const assetValue = AssetValue.from({
1464
+ address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
1465
+ chain: Chain.Ethereum,
1466
+ value: 100,
1467
+ });
1468
+
1469
+ expect(assetValue.decimal).toBe(6);
1470
+ expect(assetValue.getValue("string")).toBe("100");
1471
+ expect(assetValue.symbol).toBe("USDC-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48");
1472
+
1473
+ const assetValueWrongAddress = AssetValue.from({ address: "0xVERYWRONG", chain: Chain.Ethereum, value: 100 });
1474
+
1475
+ expect(assetValueWrongAddress.decimal).toBe(18);
1476
+ expect(assetValueWrongAddress.getValue("string")).toBe("100");
1477
+ expect(assetValueWrongAddress.toString()).toBe("ETH.UNKNOWN-0xverywrong");
1478
+ });
1479
+
1480
+ test("handles Radix with symbol lookup", async () => {
1481
+ const assetValue = await AssetValue.from({
1482
+ asyncTokenLookup: true,
1483
+ chain: Chain.Radix,
1484
+ symbol: "XRD",
1485
+ value: 50,
1486
+ });
1487
+
1488
+ expect(assetValue.decimal).toBe(18);
1489
+ expect(assetValue.symbol).toBe("XRD");
1490
+ expect(assetValue.getValue("string")).toBe("50");
1491
+ });
1492
+ });
1493
+
1494
+ describe("Mixed symbol and address scenarios", () => {
1495
+ test("uses on-chain data when both symbol and address provided", async () => {
1496
+ const assetValue = await AssetValue.from({
1497
+ asset: "ETH.WRONG-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
1498
+ asyncTokenLookup: true,
1499
+ value: 100,
1500
+ });
1501
+
1502
+ expect(assetValue.decimal).toBe(6);
1503
+ expect(assetValue.symbol).toContain("USDC");
1504
+ expect(assetValue.address).toBe("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48");
1505
+ });
1506
+ });
1507
+
1508
+ describe("getIconUrl", () => {
1509
+ test("returns logoURI when token is in static map", async () => {
1510
+ await AssetValue.loadStaticAssets();
1511
+ const asset = AssetValue.from({ asset: "BTC.BTC" });
1512
+
1513
+ expect(asset.getIconUrl()).toBeString();
1514
+ expect(asset.getIconUrl()?.length).toBeGreaterThan(0);
1515
+ });
1516
+
1517
+ test("returns undefined for custom token not in map", () => {
1518
+ AssetValue.setStaticAssets(new Map());
1519
+
1520
+ const asset = AssetValue.from({ asset: "BTC.BTC" });
1521
+ expect(asset.getIconUrl()).toBeUndefined();
1522
+
1523
+ void AssetValue.loadStaticAssets();
1524
+ });
1525
+ });
1526
+
1527
+ describe("setStaticAssets", () => {
1528
+ test("sets custom static assets map", () => {
1529
+ const customMap = new Map();
1530
+
1531
+ customMap.set("ETH.CUSTOMTOKEN-0X123", {
1532
+ address: "0x123",
1533
+ chain: Chain.Ethereum,
1534
+ decimal: 18,
1535
+ identifier: "ETH.CUSTOMTOKEN-0x123",
1536
+ tax: undefined,
1537
+ });
1538
+
1539
+ const result = AssetValue.setStaticAssets(customMap);
1540
+ expect(result).toBe(true);
1541
+
1542
+ const asset = AssetValue.from({ asset: "ETH.CUSTOMTOKEN-0X123" });
1543
+ expect(asset.decimal).toBe(18);
1544
+ expect(asset.toString()).toBe("ETH.CUSTOMTOKEN-0x123");
1545
+ });
1546
+
1547
+ test("clears existing static assets when setting new ones", () => {
1548
+ const map1 = new Map();
1549
+ map1.set("BTC.TOKEN1", { chain: Chain.Bitcoin, decimal: 8, identifier: "BTC.TOKEN1" });
1550
+
1551
+ AssetValue.setStaticAssets(map1);
1552
+
1553
+ const map2 = new Map();
1554
+ map2.set("ETH.TOKEN2-0xABC", {
1555
+ address: "0xABC",
1556
+ chain: Chain.Ethereum,
1557
+ decimal: 18,
1558
+ identifier: "ETH.TOKEN2-0xABC",
1559
+ });
1560
+
1561
+ AssetValue.setStaticAssets(map2);
1562
+
1563
+ // TOKEN2 should exist
1564
+ const asset2 = AssetValue.from({ asset: "ETH.TOKEN2-0xABC" });
1565
+ expect(asset2.decimal).toBe(18);
1566
+ });
1567
+
1568
+ test("handles token with decimals property", () => {
1569
+ const customMap = new Map();
1570
+
1571
+ customMap.set("AVAX.CUSTOMUSDC-0X456", {
1572
+ address: "0x456",
1573
+ chain: Chain.Avalanche,
1574
+ decimals: 6,
1575
+ identifier: "AVAX.CUSTOMUSDC-0x456",
1576
+ });
1577
+
1578
+ AssetValue.setStaticAssets(customMap);
1579
+
1580
+ const asset = AssetValue.from({ asset: "AVAX.CUSTOMUSDC-0X456" });
1581
+ expect(asset.decimal).toBe(6);
1582
+ });
1583
+
1584
+ test("handles case sensitive chains correctly", () => {
1585
+ const customMap = new Map();
1586
+
1587
+ // SOL is case sensitive
1588
+ customMap.set("SOL.CUSTOMTOKEN-ADDRESS123", {
1589
+ address: "ADDRESS123",
1590
+ chain: Chain.Solana,
1591
+ decimal: 9,
1592
+ identifier: "SOL.CUSTOMTOKEN-ADDRESS123",
1593
+ });
1594
+
1595
+ AssetValue.setStaticAssets(customMap);
1596
+
1597
+ const asset = AssetValue.from({ asset: "SOL.CUSTOMTOKEN-ADDRESS123" });
1598
+ expect(asset.decimal).toBe(9);
1599
+ expect(asset.address).toBe("ADDRESS123");
1600
+ });
1601
+
1602
+ test("populates chain:address map for lookups", () => {
1603
+ const customMap = new Map();
1604
+
1605
+ customMap.set("BSC.TOKEN-0XDEF", {
1606
+ address: "0xDEF",
1607
+ chain: Chain.BinanceSmartChain,
1608
+ decimal: 18,
1609
+ identifier: "BSC.TOKEN-0xDEF",
1610
+ });
1611
+
1612
+ AssetValue.setStaticAssets(customMap);
1613
+
1614
+ // Should be able to find by chain:address
1615
+ const asset = AssetValue.from({ address: "0XDEF", chain: Chain.BinanceSmartChain });
1616
+ expect(asset.decimal).toBe(18);
1617
+ });
1618
+
1619
+ test("handles tokens with tax property", () => {
1620
+ const customMap = new Map();
1621
+
1622
+ const tax = { buy: 0.1, sell: 0.2 };
1623
+ customMap.set("ETH.TAXTOKEN-0X789", {
1624
+ address: "0x789",
1625
+ chain: Chain.Ethereum,
1626
+ decimal: 18,
1627
+ identifier: "ETH.TAXTOKEN-0x789",
1628
+ tax,
1629
+ });
1630
+
1631
+ AssetValue.setStaticAssets(customMap);
1632
+
1633
+ const asset = AssetValue.from({ asset: "ETH.TAXTOKEN-0X789" });
1634
+ expect(asset.tax).toEqual(tax);
1635
+ });
1636
+ });
1637
+ });