@swapkit/helpers 4.5.6 → 4.5.8

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.
@@ -2,37 +2,119 @@ import { describe, expect, test } from "bun:test";
2
2
  import { Chain } from "@swapkit/types";
3
3
 
4
4
  import { findAssetBy } from "../asset";
5
- import { getTHORNameCost } from "../others";
5
+ import { getChainIdentifier, getMAYANameCost, getTHORNameCost, warnOnce, wrapWithThrow } from "../others";
6
6
 
7
7
  describe("getTHORNameCost", () => {
8
- describe("for correct values", () => {
9
- const costCases = [
10
- [1, 11],
11
- [2, 12],
12
- [3, 13],
13
- [10, 20],
14
- ];
15
-
16
- for (const [years = 0, expected = 10] of costCases) {
17
- test(`returns correct ${expected} cost for ${years} years`, () => {
18
- const result = getTHORNameCost(years);
19
- expect(result).toBe(expected);
20
- });
21
- }
8
+ test("returns 10 + numberOfYears", () => {
9
+ expect(getTHORNameCost(0)).toBe(10);
10
+ expect(getTHORNameCost(1)).toBe(11);
11
+ expect(getTHORNameCost(5)).toBe(15);
12
+ expect(getTHORNameCost(10)).toBe(20);
22
13
  });
23
14
 
24
- test("throws an error for negative years", () => {
15
+ test("throws for negative years in THORName cost", () => {
25
16
  expect(() => getTHORNameCost(-1)).toThrow("helpers_invalid_number_of_years");
17
+ expect(() => getTHORNameCost(-100)).toThrow("helpers_invalid_number_of_years");
18
+ });
19
+ });
20
+
21
+ describe("getMAYANameCost", () => {
22
+ test("returns 10 + (numberOfYears * 1.0512)", () => {
23
+ expect(getMAYANameCost(0)).toBe(10);
24
+ expect(getMAYANameCost(1)).toBe(11.0512);
25
+ expect(getMAYANameCost(5)).toBeCloseTo(15.256, 3);
26
+ expect(getMAYANameCost(10)).toBeCloseTo(20.512, 3);
27
+ });
28
+
29
+ test("throws for negative years in MAYAName cost", () => {
30
+ expect(() => getMAYANameCost(-1)).toThrow("helpers_invalid_number_of_years");
31
+ });
32
+ });
33
+
34
+ describe("getChainIdentifier", () => {
35
+ test("returns CHAIN.RUNE for THORChain", () => {
36
+ expect(getChainIdentifier(Chain.THORChain)).toBe("THOR.RUNE");
37
+ });
38
+
39
+ test("returns CHAIN.ATOM for Cosmos", () => {
40
+ expect(getChainIdentifier(Chain.Cosmos)).toBe("GAIA.ATOM");
41
+ });
42
+
43
+ test("returns CHAIN only for BinanceSmartChain", () => {
44
+ expect(getChainIdentifier(Chain.BinanceSmartChain)).toBe("BSC");
45
+ });
46
+
47
+ test("returns CHAIN.CHAIN for other chains", () => {
48
+ expect(getChainIdentifier(Chain.Bitcoin)).toBe("BTC.BTC");
49
+ expect(getChainIdentifier(Chain.Ethereum)).toBe("ETH.ETH");
50
+ expect(getChainIdentifier(Chain.Avalanche)).toBe("AVAX.AVAX");
51
+ });
52
+ });
53
+
54
+ describe("wrapWithThrow", () => {
55
+ test("returns function result on success", () => {
56
+ expect(wrapWithThrow(() => 42)).toBe(42);
57
+ expect(wrapWithThrow(() => "hello")).toBe("hello");
58
+ expect(wrapWithThrow(() => ({ a: 1 }))).toEqual({ a: 1 });
59
+ });
60
+
61
+ test("returns undefined on error without errorKey", () => {
62
+ expect(
63
+ wrapWithThrow(() => {
64
+ throw new Error("test");
65
+ }),
66
+ ).toBeUndefined();
67
+ });
68
+
69
+ test("throws SwapKitError when errorKey provided", () => {
70
+ expect(() =>
71
+ wrapWithThrow(() => {
72
+ throw new Error("test");
73
+ }, "helpers_invalid_identifier"),
74
+ ).toThrow("helpers_invalid_identifier");
75
+ });
76
+ });
77
+
78
+ describe("warnOnce", () => {
79
+ // Note: warnOnce skips console.warn in test environment (NODE_ENV=test)
80
+ // We test the behavior by calling the function multiple times and observing
81
+ // that the function doesn't throw and handles repeated calls gracefully
82
+
83
+ test("does not throw when condition is false", () => {
84
+ expect(() => warnOnce({ condition: false, id: "test_false_condition", warning: "should not warn" })).not.toThrow();
85
+ });
86
+
87
+ test("does not throw when condition is true", () => {
88
+ const uniqueId = `test_warns_${Date.now()}`;
89
+ expect(() => warnOnce({ condition: true, id: uniqueId, warning: "test warning message" })).not.toThrow();
90
+ });
91
+
92
+ test("handles repeated calls with same id without throwing", () => {
93
+ const uniqueId = `test_once_${Date.now()}`;
94
+ expect(() => {
95
+ warnOnce({ condition: true, id: uniqueId, warning: "first warn" });
96
+ warnOnce({ condition: true, id: uniqueId, warning: "second warn" });
97
+ warnOnce({ condition: true, id: uniqueId, warning: "third warn" });
98
+ }).not.toThrow();
99
+ });
100
+
101
+ test("handles multiple different ids without throwing", () => {
102
+ const uniqueId1 = `test_different_1_${Date.now()}`;
103
+ const uniqueId2 = `test_different_2_${Date.now()}`;
104
+ expect(() => {
105
+ warnOnce({ condition: true, id: uniqueId1, warning: "warn 1" });
106
+ warnOnce({ condition: true, id: uniqueId2, warning: "warn 2" });
107
+ }).not.toThrow();
26
108
  });
27
109
  });
28
110
 
29
111
  describe("getAssetBy", () => {
30
- test("find asset by identifier", async () => {
112
+ test("find ETH asset by identifier", async () => {
31
113
  const assetByIdentifier = await findAssetBy({ identifier: "ETH.ETH" });
32
114
  expect(assetByIdentifier).toBe("ETH.ETH");
33
115
  });
34
116
 
35
- test("find asset by chain and contract", async () => {
117
+ test("find ETH token by chain and contract", async () => {
36
118
  const assetByChainAndContract = await findAssetBy({
37
119
  chain: Chain.Ethereum,
38
120
  contract: "0XA0B86991C6218B36C1D19D4A2E9EB0CE3606EB48",
@@ -48,12 +130,12 @@ describe("getAssetBy", () => {
48
130
  });
49
131
 
50
132
  describe(Chain.Radix, () => {
51
- test("find asset by identifier", async () => {
133
+ test("find Radix XRD by identifier", async () => {
52
134
  const assetByChainAndContract = await findAssetBy({ identifier: "XRD.XRD" });
53
135
  expect(assetByChainAndContract?.toUpperCase()).toBe("XRD.XRD".toUpperCase());
54
136
  });
55
137
 
56
- test("find asset by chain and contract", async () => {
138
+ test("find Radix token by chain and contract", async () => {
57
139
  const assetByChainAndContract = await findAssetBy({
58
140
  chain: Chain.Radix,
59
141
  contract: "resource_rdx1t580qxc7upat7lww4l2c4jckacafjeudxj5wpjrrct0p3e82sq4y75",
@@ -65,12 +147,12 @@ describe("getAssetBy", () => {
65
147
  });
66
148
 
67
149
  describe(Chain.Solana, () => {
68
- test("find asset by identifier", async () => {
150
+ test("find Solana SOL by identifier", async () => {
69
151
  const assetByChainAndContract = await findAssetBy({ identifier: "SOL.SOL" });
70
152
  expect(assetByChainAndContract?.toUpperCase()).toBe("SOL.SOL".toUpperCase());
71
153
  });
72
154
 
73
- test("find asset by chain and contract", async () => {
155
+ test("find Solana token by chain and contract", async () => {
74
156
  const assetByChainAndContract = await findAssetBy({
75
157
  chain: Chain.Solana,
76
158
  contract: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
@@ -1,24 +1,84 @@
1
1
  import { describe, expect, test } from "bun:test";
2
- import { validateTNS } from "../validators";
2
+ import { validateIdentifier, validateTNS } from "../validators";
3
3
 
4
4
  describe("validateTNS", () => {
5
- const casesWithExpectation: [string, boolean][] = [
6
- ["validname", true],
7
- ["valid-name", true],
8
- ["valid_name", true],
9
- ["valid+name", true],
10
- ["name_with_numbers123", true],
11
- ["UPPER_CASE", true],
12
- ["toolongname123456789012345678901", false],
13
- ["invalid@name", false],
14
- ["invalid!name", false],
15
- ["invalid#name", false],
16
- ];
17
-
18
- for (const [name, expected] of casesWithExpectation) {
19
- test(`returns ${expected} for THORName "${name}"`, () => {
20
- const result = validateTNS(name);
21
- expect(result).toBe(expected);
22
- });
23
- }
5
+ test("valid names with alphanumeric and allowed special chars", () => {
6
+ expect(validateTNS("validname")).toBe(true);
7
+ expect(validateTNS("valid-name")).toBe(true);
8
+ expect(validateTNS("valid_name")).toBe(true);
9
+ expect(validateTNS("valid+name")).toBe(true);
10
+ expect(validateTNS("name123")).toBe(true);
11
+ expect(validateTNS("UPPERCASE")).toBe(true);
12
+ expect(validateTNS("MixedCase123")).toBe(true);
13
+ });
14
+
15
+ test("invalid names exceeding 30 characters", () => {
16
+ expect(validateTNS("toolongname123456789012345678901")).toBe(false);
17
+ expect(validateTNS("a".repeat(31))).toBe(false);
18
+ });
19
+
20
+ test("invalid names with disallowed characters", () => {
21
+ expect(validateTNS("invalid@name")).toBe(false);
22
+ expect(validateTNS("invalid!name")).toBe(false);
23
+ expect(validateTNS("invalid#name")).toBe(false);
24
+ expect(validateTNS("invalid$name")).toBe(false);
25
+ expect(validateTNS("invalid%name")).toBe(false);
26
+ expect(validateTNS("name with space")).toBe(false);
27
+ });
28
+
29
+ test("edge cases", () => {
30
+ expect(validateTNS("a")).toBe(true);
31
+ expect(validateTNS("a".repeat(30))).toBe(true);
32
+ expect(validateTNS("")).toBe(false);
33
+ });
34
+ });
35
+
36
+ describe("validateIdentifier", () => {
37
+ test("valid chain.ticker format", () => {
38
+ expect(validateIdentifier("BTC.BTC")).toBe(true);
39
+ expect(validateIdentifier("ETH.ETH")).toBe(true);
40
+ expect(validateIdentifier("AVAX.USDC-0x123")).toBe(true);
41
+ expect(validateIdentifier("eth.eth")).toBe(true);
42
+ });
43
+
44
+ test("valid synth format with /", () => {
45
+ expect(validateIdentifier("ETH/ETH")).toBe(true);
46
+ expect(validateIdentifier("BTC/BTC")).toBe(true);
47
+ expect(validateIdentifier("THOR.ETH/ETH")).toBe(true);
48
+ });
49
+
50
+ test("valid trade asset format with ~", () => {
51
+ expect(validateIdentifier("THOR.ETH~ETH")).toBe(true);
52
+ expect(validateIdentifier("THOR.BTC~BTC")).toBe(true);
53
+ expect(validateIdentifier("THOR.ETH~USDC-0xa5f2211b9b8170f694421f2046281775e8468044")).toBe(true);
54
+ });
55
+
56
+ test("valid NEAR address formats", () => {
57
+ expect(validateIdentifier("NEAR.wNEAR-wrap.near")).toBe(true);
58
+ expect(validateIdentifier("NEAR.USDC-17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1")).toBe(true);
59
+ expect(validateIdentifier("NEAR.ETH-eth.bridge.near")).toBe(true);
60
+ });
61
+
62
+ test("valid Maya synth format", () => {
63
+ expect(validateIdentifier("MAYA.ETH/ETH")).toBe(true);
64
+ expect(validateIdentifier("MAYA.BTC/BTC")).toBe(true);
65
+ });
66
+
67
+ test("throws for invalid chain", () => {
68
+ expect(() => validateIdentifier("INVALID.TOKEN")).toThrow("helpers_invalid_identifier");
69
+ expect(() => validateIdentifier("XXX.YYY")).toThrow("helpers_invalid_identifier");
70
+ });
71
+
72
+ test("throws for empty or malformed identifier", () => {
73
+ expect(() => validateIdentifier("")).toThrow("helpers_invalid_identifier");
74
+ expect(() => validateIdentifier("NOCHAIN")).toThrow("helpers_invalid_identifier");
75
+ });
76
+
77
+ test("accepts identifier with only chain (validates chain exists)", () => {
78
+ // The validateIdentifier function only checks if the chain part is valid
79
+ // It doesn't enforce the full <Chain>.<Ticker> format
80
+ expect(validateIdentifier("ETH")).toBe(true);
81
+ expect(validateIdentifier("ETH.")).toBe(true);
82
+ expect(validateIdentifier("BTC")).toBe(true);
83
+ });
24
84
  });