@tcswap/toolboxes 4.3.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 (275) hide show
  1. package/dist/src/cardano/index.cjs +4 -0
  2. package/dist/src/cardano/index.cjs.map +11 -0
  3. package/dist/src/cardano/index.js +4 -0
  4. package/dist/src/cardano/index.js.map +11 -0
  5. package/dist/src/cosmos/index.cjs +4 -0
  6. package/dist/src/cosmos/index.cjs.map +20 -0
  7. package/dist/src/cosmos/index.js +4 -0
  8. package/dist/src/cosmos/index.js.map +20 -0
  9. package/dist/src/evm/index.cjs +4 -0
  10. package/dist/src/evm/index.cjs.map +20 -0
  11. package/dist/src/evm/index.js +4 -0
  12. package/dist/src/evm/index.js.map +20 -0
  13. package/dist/src/index.cjs +5 -0
  14. package/dist/src/index.cjs.map +67 -0
  15. package/dist/src/index.js +5 -0
  16. package/dist/src/index.js.map +67 -0
  17. package/dist/src/near/index.cjs +4 -0
  18. package/dist/src/near/index.cjs.map +16 -0
  19. package/dist/src/near/index.js +4 -0
  20. package/dist/src/near/index.js.map +16 -0
  21. package/dist/src/radix/index.cjs +4 -0
  22. package/dist/src/radix/index.cjs.map +10 -0
  23. package/dist/src/radix/index.js +4 -0
  24. package/dist/src/radix/index.js.map +10 -0
  25. package/dist/src/ripple/index.cjs +4 -0
  26. package/dist/src/ripple/index.cjs.map +10 -0
  27. package/dist/src/ripple/index.js +4 -0
  28. package/dist/src/ripple/index.js.map +10 -0
  29. package/dist/src/solana/index.cjs +4 -0
  30. package/dist/src/solana/index.cjs.map +11 -0
  31. package/dist/src/solana/index.js +4 -0
  32. package/dist/src/solana/index.js.map +11 -0
  33. package/dist/src/substrate/index.cjs +4 -0
  34. package/dist/src/substrate/index.cjs.map +13 -0
  35. package/dist/src/substrate/index.js +4 -0
  36. package/dist/src/substrate/index.js.map +13 -0
  37. package/dist/src/sui/index.cjs +4 -0
  38. package/dist/src/sui/index.cjs.map +11 -0
  39. package/dist/src/sui/index.js +4 -0
  40. package/dist/src/sui/index.js.map +11 -0
  41. package/dist/src/ton/index.cjs +4 -0
  42. package/dist/src/ton/index.cjs.map +11 -0
  43. package/dist/src/ton/index.js +4 -0
  44. package/dist/src/ton/index.js.map +11 -0
  45. package/dist/src/tron/index.cjs +4 -0
  46. package/dist/src/tron/index.cjs.map +13 -0
  47. package/dist/src/tron/index.js +4 -0
  48. package/dist/src/tron/index.js.map +13 -0
  49. package/dist/src/utxo/index.cjs +5 -0
  50. package/dist/src/utxo/index.cjs.map +21 -0
  51. package/dist/src/utxo/index.js +5 -0
  52. package/dist/src/utxo/index.js.map +21 -0
  53. package/dist/types/cardano/index.d.ts +3 -0
  54. package/dist/types/cardano/index.d.ts.map +1 -0
  55. package/dist/types/cardano/toolbox.d.ts +37 -0
  56. package/dist/types/cardano/toolbox.d.ts.map +1 -0
  57. package/dist/types/cardano/types.d.ts +11 -0
  58. package/dist/types/cardano/types.d.ts.map +1 -0
  59. package/dist/types/cosmos/index.d.ts +5 -0
  60. package/dist/types/cosmos/index.d.ts.map +1 -0
  61. package/dist/types/cosmos/thorchainUtils/addressFormat.d.ts +8 -0
  62. package/dist/types/cosmos/thorchainUtils/addressFormat.d.ts.map +1 -0
  63. package/dist/types/cosmos/thorchainUtils/index.d.ts +5 -0
  64. package/dist/types/cosmos/thorchainUtils/index.d.ts.map +1 -0
  65. package/dist/types/cosmos/thorchainUtils/messages.d.ts +211 -0
  66. package/dist/types/cosmos/thorchainUtils/messages.d.ts.map +1 -0
  67. package/dist/types/cosmos/thorchainUtils/registry.d.ts +4 -0
  68. package/dist/types/cosmos/thorchainUtils/registry.d.ts.map +1 -0
  69. package/dist/types/cosmos/thorchainUtils/types/MsgCompiled.d.ts +2 -0
  70. package/dist/types/cosmos/thorchainUtils/types/MsgCompiled.d.ts.map +1 -0
  71. package/dist/types/cosmos/thorchainUtils/types/client-types.d.ts +63 -0
  72. package/dist/types/cosmos/thorchainUtils/types/client-types.d.ts.map +1 -0
  73. package/dist/types/cosmos/thorchainUtils/types/index.d.ts +2 -0
  74. package/dist/types/cosmos/thorchainUtils/types/index.d.ts.map +1 -0
  75. package/dist/types/cosmos/toolbox/cosmos.d.ts +65 -0
  76. package/dist/types/cosmos/toolbox/cosmos.d.ts.map +1 -0
  77. package/dist/types/cosmos/toolbox/index.d.ts +18 -0
  78. package/dist/types/cosmos/toolbox/index.d.ts.map +1 -0
  79. package/dist/types/cosmos/toolbox/thorchain.d.ts +161 -0
  80. package/dist/types/cosmos/toolbox/thorchain.d.ts.map +1 -0
  81. package/dist/types/cosmos/types.d.ts +49 -0
  82. package/dist/types/cosmos/types.d.ts.map +1 -0
  83. package/dist/types/cosmos/util.d.ts +77 -0
  84. package/dist/types/cosmos/util.d.ts.map +1 -0
  85. package/dist/types/evm/api.d.ts +8 -0
  86. package/dist/types/evm/api.d.ts.map +1 -0
  87. package/dist/types/evm/contracts/eth/multicall.d.ts +36 -0
  88. package/dist/types/evm/contracts/eth/multicall.d.ts.map +1 -0
  89. package/dist/types/evm/contracts/op/gasOracle.d.ts +40 -0
  90. package/dist/types/evm/contracts/op/gasOracle.d.ts.map +1 -0
  91. package/dist/types/evm/helpers.d.ts +6 -0
  92. package/dist/types/evm/helpers.d.ts.map +1 -0
  93. package/dist/types/evm/index.d.ts +5 -0
  94. package/dist/types/evm/index.d.ts.map +1 -0
  95. package/dist/types/evm/toolbox/baseEVMToolbox.d.ts +88 -0
  96. package/dist/types/evm/toolbox/baseEVMToolbox.d.ts.map +1 -0
  97. package/dist/types/evm/toolbox/evm.d.ts +767 -0
  98. package/dist/types/evm/toolbox/evm.d.ts.map +1 -0
  99. package/dist/types/evm/toolbox/index.d.ts +7 -0
  100. package/dist/types/evm/toolbox/index.d.ts.map +1 -0
  101. package/dist/types/evm/toolbox/op.d.ts +67 -0
  102. package/dist/types/evm/toolbox/op.d.ts.map +1 -0
  103. package/dist/types/evm/types.d.ts +108 -0
  104. package/dist/types/evm/types.d.ts.map +1 -0
  105. package/dist/types/index.d.ts +78 -0
  106. package/dist/types/index.d.ts.map +1 -0
  107. package/dist/types/near/helpers/core.d.ts +18 -0
  108. package/dist/types/near/helpers/core.d.ts.map +1 -0
  109. package/dist/types/near/helpers/gasEstimation.d.ts +41 -0
  110. package/dist/types/near/helpers/gasEstimation.d.ts.map +1 -0
  111. package/dist/types/near/helpers/nep141.d.ts +36 -0
  112. package/dist/types/near/helpers/nep141.d.ts.map +1 -0
  113. package/dist/types/near/index.d.ts +10 -0
  114. package/dist/types/near/index.d.ts.map +1 -0
  115. package/dist/types/near/toolbox.d.ts +35 -0
  116. package/dist/types/near/toolbox.d.ts.map +1 -0
  117. package/dist/types/near/types/contract.d.ts +38 -0
  118. package/dist/types/near/types/contract.d.ts.map +1 -0
  119. package/dist/types/near/types/nep141.d.ts +29 -0
  120. package/dist/types/near/types/nep141.d.ts.map +1 -0
  121. package/dist/types/near/types/toolbox.d.ts +51 -0
  122. package/dist/types/near/types/toolbox.d.ts.map +1 -0
  123. package/dist/types/near/types.d.ts +47 -0
  124. package/dist/types/near/types.d.ts.map +1 -0
  125. package/dist/types/radix/index.d.ts +17 -0
  126. package/dist/types/radix/index.d.ts.map +1 -0
  127. package/dist/types/ripple/index.d.ts +49 -0
  128. package/dist/types/ripple/index.d.ts.map +1 -0
  129. package/dist/types/solana/index.d.ts +23 -0
  130. package/dist/types/solana/index.d.ts.map +1 -0
  131. package/dist/types/solana/toolbox.d.ts +54 -0
  132. package/dist/types/solana/toolbox.d.ts.map +1 -0
  133. package/dist/types/substrate/balance.d.ts +20 -0
  134. package/dist/types/substrate/balance.d.ts.map +1 -0
  135. package/dist/types/substrate/index.d.ts +3 -0
  136. package/dist/types/substrate/index.d.ts.map +1 -0
  137. package/dist/types/substrate/substrate.d.ts +151 -0
  138. package/dist/types/substrate/substrate.d.ts.map +1 -0
  139. package/dist/types/substrate/types.d.ts +100 -0
  140. package/dist/types/substrate/types.d.ts.map +1 -0
  141. package/dist/types/sui/index.d.ts +3 -0
  142. package/dist/types/sui/index.d.ts.map +1 -0
  143. package/dist/types/sui/toolbox.d.ts +22 -0
  144. package/dist/types/sui/toolbox.d.ts.map +1 -0
  145. package/dist/types/sui/types.d.ts +16 -0
  146. package/dist/types/sui/types.d.ts.map +1 -0
  147. package/dist/types/ton/index.d.ts +3 -0
  148. package/dist/types/ton/index.d.ts.map +1 -0
  149. package/dist/types/ton/toolbox.d.ts +17 -0
  150. package/dist/types/ton/toolbox.d.ts.map +1 -0
  151. package/dist/types/ton/types.d.ts +22 -0
  152. package/dist/types/ton/types.d.ts.map +1 -0
  153. package/dist/types/tron/helpers/trc20.abi.d.ts +156 -0
  154. package/dist/types/tron/helpers/trc20.abi.d.ts.map +1 -0
  155. package/dist/types/tron/helpers/trongrid.d.ts +11 -0
  156. package/dist/types/tron/helpers/trongrid.d.ts.map +1 -0
  157. package/dist/types/tron/index.d.ts +6 -0
  158. package/dist/types/tron/index.d.ts.map +1 -0
  159. package/dist/types/tron/toolbox.d.ts +29 -0
  160. package/dist/types/tron/toolbox.d.ts.map +1 -0
  161. package/dist/types/tron/types.d.ts +103 -0
  162. package/dist/types/tron/types.d.ts.map +1 -0
  163. package/dist/types/types.d.ts +26 -0
  164. package/dist/types/types.d.ts.map +1 -0
  165. package/dist/types/utils.d.ts +7 -0
  166. package/dist/types/utils.d.ts.map +1 -0
  167. package/dist/types/utxo/helpers/api.d.ts +104 -0
  168. package/dist/types/utxo/helpers/api.d.ts.map +1 -0
  169. package/dist/types/utxo/helpers/bchaddrjs.d.ts +13 -0
  170. package/dist/types/utxo/helpers/bchaddrjs.d.ts.map +1 -0
  171. package/dist/types/utxo/helpers/coinselect.d.ts +20 -0
  172. package/dist/types/utxo/helpers/coinselect.d.ts.map +1 -0
  173. package/dist/types/utxo/helpers/index.d.ts +5 -0
  174. package/dist/types/utxo/helpers/index.d.ts.map +1 -0
  175. package/dist/types/utxo/helpers/txSize.d.ts +24 -0
  176. package/dist/types/utxo/helpers/txSize.d.ts.map +1 -0
  177. package/dist/types/utxo/index.d.ts +7 -0
  178. package/dist/types/utxo/index.d.ts.map +1 -0
  179. package/dist/types/utxo/toolbox/bitcoinCash.d.ts +96 -0
  180. package/dist/types/utxo/toolbox/bitcoinCash.d.ts.map +1 -0
  181. package/dist/types/utxo/toolbox/index.d.ts +31 -0
  182. package/dist/types/utxo/toolbox/index.d.ts.map +1 -0
  183. package/dist/types/utxo/toolbox/params.d.ts +32 -0
  184. package/dist/types/utxo/toolbox/params.d.ts.map +1 -0
  185. package/dist/types/utxo/toolbox/utxo.d.ts +106 -0
  186. package/dist/types/utxo/toolbox/utxo.d.ts.map +1 -0
  187. package/dist/types/utxo/toolbox/validators.d.ts +4 -0
  188. package/dist/types/utxo/toolbox/validators.d.ts.map +1 -0
  189. package/dist/types/utxo/toolbox/zcash.d.ts +75 -0
  190. package/dist/types/utxo/toolbox/zcash.d.ts.map +1 -0
  191. package/dist/types/utxo/types.d.ts +46 -0
  192. package/dist/types/utxo/types.d.ts.map +1 -0
  193. package/package.json +205 -0
  194. package/src/__tests__/address-validation-all-chains.test.ts +162 -0
  195. package/src/__tests__/addressValidator.test.ts +162 -0
  196. package/src/cardano/__tests__/toolbox.test.ts +48 -0
  197. package/src/cardano/index.ts +2 -0
  198. package/src/cardano/toolbox.ts +172 -0
  199. package/src/cardano/types.ts +10 -0
  200. package/src/cosmos/__tests__/toolbox.test.ts +95 -0
  201. package/src/cosmos/index.ts +4 -0
  202. package/src/cosmos/thorchainUtils/addressFormat.ts +26 -0
  203. package/src/cosmos/thorchainUtils/index.ts +4 -0
  204. package/src/cosmos/thorchainUtils/messages.ts +216 -0
  205. package/src/cosmos/thorchainUtils/registry.ts +43 -0
  206. package/src/cosmos/thorchainUtils/types/MsgCompiled.ts +2800 -0
  207. package/src/cosmos/thorchainUtils/types/client-types.ts +54 -0
  208. package/src/cosmos/thorchainUtils/types/index.ts +1 -0
  209. package/src/cosmos/toolbox/cosmos.ts +354 -0
  210. package/src/cosmos/toolbox/index.ts +39 -0
  211. package/src/cosmos/toolbox/thorchain.ts +253 -0
  212. package/src/cosmos/types.ts +48 -0
  213. package/src/cosmos/util.ts +218 -0
  214. package/src/evm/__tests__/address-validation.test.ts +84 -0
  215. package/src/evm/__tests__/ethereum.test.ts +141 -0
  216. package/src/evm/__tests__/signMessage.test.ts +60 -0
  217. package/src/evm/api.ts +10 -0
  218. package/src/evm/contracts/eth/multicall.ts +165 -0
  219. package/src/evm/contracts/op/gasOracle.ts +145 -0
  220. package/src/evm/helpers.ts +71 -0
  221. package/src/evm/index.ts +4 -0
  222. package/src/evm/toolbox/baseEVMToolbox.ts +701 -0
  223. package/src/evm/toolbox/evm.ts +66 -0
  224. package/src/evm/toolbox/index.ts +44 -0
  225. package/src/evm/toolbox/op.ts +147 -0
  226. package/src/evm/types.ts +146 -0
  227. package/src/index.ts +264 -0
  228. package/src/near/__tests__/core.test.ts +70 -0
  229. package/src/near/helpers/core.ts +89 -0
  230. package/src/near/helpers/gasEstimation.ts +96 -0
  231. package/src/near/helpers/nep141.ts +50 -0
  232. package/src/near/index.ts +21 -0
  233. package/src/near/toolbox.ts +425 -0
  234. package/src/near/types/contract.ts +32 -0
  235. package/src/near/types/nep141.ts +34 -0
  236. package/src/near/types/toolbox.ts +55 -0
  237. package/src/near/types.ts +44 -0
  238. package/src/radix/index.ts +142 -0
  239. package/src/ripple/index.ts +180 -0
  240. package/src/solana/index.ts +36 -0
  241. package/src/solana/toolbox.ts +413 -0
  242. package/src/substrate/balance.ts +92 -0
  243. package/src/substrate/index.ts +2 -0
  244. package/src/substrate/substrate.ts +285 -0
  245. package/src/substrate/types.ts +115 -0
  246. package/src/sui/__tests__/toolbox.test.ts +86 -0
  247. package/src/sui/index.ts +2 -0
  248. package/src/sui/toolbox.ts +170 -0
  249. package/src/sui/types.ts +11 -0
  250. package/src/ton/__tests__/toolbox.test.ts +67 -0
  251. package/src/ton/index.ts +2 -0
  252. package/src/ton/toolbox.ts +140 -0
  253. package/src/ton/types.ts +13 -0
  254. package/src/tron/__tests__/toolbox.test.ts +225 -0
  255. package/src/tron/helpers/trc20.abi.ts +107 -0
  256. package/src/tron/helpers/trongrid.ts +57 -0
  257. package/src/tron/index.ts +21 -0
  258. package/src/tron/toolbox.ts +589 -0
  259. package/src/tron/types.ts +83 -0
  260. package/src/types.ts +28 -0
  261. package/src/utils.ts +38 -0
  262. package/src/utxo/__tests__/zcash-integration.test.ts +97 -0
  263. package/src/utxo/helpers/api.ts +475 -0
  264. package/src/utxo/helpers/bchaddrjs.ts +170 -0
  265. package/src/utxo/helpers/coinselect.ts +96 -0
  266. package/src/utxo/helpers/index.ts +4 -0
  267. package/src/utxo/helpers/txSize.ts +141 -0
  268. package/src/utxo/index.ts +6 -0
  269. package/src/utxo/toolbox/bitcoinCash.ts +247 -0
  270. package/src/utxo/toolbox/index.ts +63 -0
  271. package/src/utxo/toolbox/params.ts +18 -0
  272. package/src/utxo/toolbox/utxo.ts +443 -0
  273. package/src/utxo/toolbox/validators.ts +36 -0
  274. package/src/utxo/toolbox/zcash.ts +246 -0
  275. package/src/utxo/types.ts +39 -0
package/src/utils.ts ADDED
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Modifications © 2025 Horizontal Systems.
3
+ */
4
+
5
+ import { AssetValue, type Chain, getChainConfig } from "@tcswap/helpers";
6
+ import { USwapApi } from "@tcswap/helpers/api";
7
+
8
+ const pid = typeof process !== "undefined" && process.pid ? process.pid.toString(36) : "";
9
+
10
+ let last = 0;
11
+ export function uniqid() {
12
+ function now() {
13
+ const time = Date.now();
14
+ const lastTime = last || time;
15
+ last = lastTime;
16
+
17
+ return time > last ? time : lastTime + 1;
18
+ }
19
+
20
+ return pid + now().toString(36);
21
+ }
22
+
23
+ export function getBalance<T extends Chain>(chain: T) {
24
+ return async function getBalance(address: string, scamFilter = true) {
25
+ const balances = await USwapApi.getChainBalance({ address, chain, scamFilter });
26
+ const { baseDecimal } = getChainConfig(chain);
27
+ const assetValues = balances.map(({ identifier, value, decimal }) => {
28
+ return new AssetValue({ decimal: decimal || baseDecimal, identifier, value });
29
+ });
30
+
31
+ const hasNativeAsset = assetValues.some((asset) => asset.isGasAsset);
32
+ if (!hasNativeAsset) {
33
+ return [AssetValue.from({ chain }), ...assetValues];
34
+ }
35
+
36
+ return assetValues;
37
+ };
38
+ }
@@ -0,0 +1,97 @@
1
+ import { describe, expect, it } from "bun:test";
2
+ import { Chain, DerivationPath } from "@tcswap/helpers";
3
+ import { getUtxoToolbox } from "../toolbox";
4
+
5
+ describe("UTXO Toolbox Zcash Integration", () => {
6
+ const testPhrase = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
7
+
8
+ it("should create Zcash toolbox through main UTXO toolbox factory", async () => {
9
+ const toolbox = await getUtxoToolbox(Chain.Zcash);
10
+
11
+ expect(toolbox).toBeDefined();
12
+ expect(typeof toolbox.validateAddress).toBe("function");
13
+ expect(typeof toolbox.getBalance).toBe("function");
14
+ expect(typeof toolbox.getFeeRates).toBe("function");
15
+ expect(typeof toolbox.broadcastTx).toBe("function");
16
+ expect(typeof toolbox.createTransaction).toBe("function");
17
+ expect(typeof toolbox.transfer).toBe("function");
18
+ });
19
+
20
+ it("should create Zcash toolbox with phrase", async () => {
21
+ const toolbox = await getUtxoToolbox(Chain.Zcash, { phrase: testPhrase });
22
+
23
+ expect(toolbox).toBeDefined();
24
+ expect(() => toolbox.getAddress()).not.toThrow();
25
+ });
26
+
27
+ it("should generate valid Zcash addresses", async () => {
28
+ const toolbox = await getUtxoToolbox(Chain.Zcash, { phrase: testPhrase });
29
+
30
+ const address = await toolbox.getAddress();
31
+ expect(address).toBeDefined();
32
+ expect(typeof address).toBe("string");
33
+ expect(address?.startsWith("t1")).toBe(true); // Zcash mainnet addresses start with t1
34
+ expect(toolbox.validateAddress(address || "")).toBe(true);
35
+ });
36
+
37
+ it("should validate Zcash addresses correctly", async () => {
38
+ const toolbox = await getUtxoToolbox(Chain.Zcash);
39
+
40
+ // Valid Zcash mainnet address format
41
+ expect(toolbox.validateAddress("t1XVXWCvpMgBvUaed4XDqWtgQgJSu1Ghz7F")).toBe(true);
42
+
43
+ // Invalid addresses
44
+ expect(toolbox.validateAddress("1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2")).toBe(false); // Bitcoin address
45
+ expect(toolbox.validateAddress("zcash:qr5agtachyxvrwxu76vzszan5pnvuzy8dm")).toBe(false); // Wrong format
46
+ expect(toolbox.validateAddress("")).toBe(false); // Empty string
47
+ expect(toolbox.validateAddress("invalid")).toBe(false); // Invalid string
48
+ });
49
+
50
+ it("should reject shielded addresses", async () => {
51
+ const toolbox = await getUtxoToolbox(Chain.Zcash);
52
+
53
+ // Test z-address (shielded) - should be rejected with warning
54
+ const originalWarn = console.warn;
55
+ let warnCalled = false;
56
+ let warnMessage = "";
57
+
58
+ console.warn = (message: string) => {
59
+ warnCalled = true;
60
+ warnMessage = message;
61
+ };
62
+
63
+ const isValid = toolbox.validateAddress("zs1z7rejlpsa98s2rrrfkwmaxu2xldqmfq5nj2m3hq6s7r8qjq8eqqqq9p4e7x");
64
+
65
+ expect(isValid).toBe(false);
66
+ expect(warnCalled).toBe(true);
67
+ expect(warnMessage).toBe(
68
+ "Shielded Zcash addresses (z-addresses) are not supported. Use transparent addresses (t1/t3) only.",
69
+ );
70
+
71
+ console.warn = originalWarn;
72
+ });
73
+
74
+ it("should create keys for derivation path", async () => {
75
+ const toolbox = await getUtxoToolbox(Chain.Zcash, { phrase: testPhrase });
76
+
77
+ const keys = await toolbox.createKeysForPath({ derivationPath: DerivationPath.ZEC, phrase: testPhrase });
78
+
79
+ expect(keys).toBeDefined();
80
+ expect(keys.publicKey).toBeDefined();
81
+ expect(keys.privateKey).toBeDefined();
82
+ expect(typeof keys.toWIF).toBe("function");
83
+
84
+ const address = await toolbox.getAddress();
85
+ expect(address).toBeDefined();
86
+ expect(address?.startsWith("t1")).toBe(true);
87
+ });
88
+
89
+ it("should get WIF private key from mnemonic", async () => {
90
+ const toolbox = await getUtxoToolbox(Chain.Zcash, { phrase: testPhrase });
91
+
92
+ const wif = await toolbox.getPrivateKeyFromMnemonic({ derivationPath: DerivationPath.ZEC, phrase: testPhrase });
93
+
94
+ expect(typeof wif).toBe("string");
95
+ expect(wif.length).toBeGreaterThan(50); // WIF keys are typically 51-52 characters
96
+ });
97
+ });
@@ -0,0 +1,475 @@
1
+ /**
2
+ * Modifications © 2025 Horizontal Systems.
3
+ */
4
+
5
+ import { networks as zcashNetworks } from "@bitgo/utxo-lib";
6
+ import { Chain, getRPCUrl, RequestClient, USwapConfig, USwapError, type UTXOChain, warnOnce } from "@tcswap/helpers";
7
+ import { networks } from "bitcoinjs-lib";
8
+ // @ts-expect-error
9
+ import coininfo from "coininfo";
10
+ import { uniqid } from "../../utils";
11
+
12
+ type BlockchairParams<T> = T & { chain: Chain; apiKey?: string };
13
+ type BlockchairFetchUnspentUtxoParams = BlockchairParams<{
14
+ offset?: number;
15
+ limit?: number;
16
+ address: string;
17
+ targetValue?: number;
18
+ accumulativeValue?: number;
19
+ }>;
20
+
21
+ async function broadcastUTXOTx({ chain, txHash }: { chain: Chain; txHash: string }) {
22
+ // Use Blockchair's push transaction endpoint (no API key needed)
23
+ const url = `${baseUrl(chain)}/push/transaction`;
24
+ const body = JSON.stringify({ data: txHash });
25
+
26
+ try {
27
+ const response = await RequestClient.post<{
28
+ data: { transaction_hash: string } | null;
29
+ context: { code: number; error?: string };
30
+ }>(url, { body, headers: { "Content-Type": "application/json" } });
31
+
32
+ if (response.context.code !== 200) {
33
+ throw new USwapError("toolbox_utxo_broadcast_failed", {
34
+ error: response.context.error || "Transaction broadcast failed",
35
+ });
36
+ }
37
+
38
+ return response.data?.transaction_hash || txHash;
39
+ } catch (error) {
40
+ // Fallback to RPC if Blockchair fails
41
+ const rpcUrl = await getRPCUrl(chain);
42
+ if (rpcUrl) {
43
+ const rpcBody = JSON.stringify({ id: uniqid(), jsonrpc: "2.0", method: "sendrawtransaction", params: [txHash] });
44
+
45
+ const rpcResponse = await RequestClient.post<{
46
+ id: string;
47
+ result: string;
48
+ error: { message: string; code?: number } | null;
49
+ }>(rpcUrl, { body: rpcBody, headers: { "Content-Type": "application/json" } });
50
+
51
+ if (rpcResponse.error) {
52
+ throw new USwapError("toolbox_utxo_broadcast_failed", { error: rpcResponse.error?.message });
53
+ }
54
+
55
+ if (rpcResponse.result.includes('"code":-26')) {
56
+ throw new USwapError("toolbox_utxo_invalid_transaction", { error: "Transaction amount was too low" });
57
+ }
58
+
59
+ return rpcResponse.result;
60
+ }
61
+
62
+ throw error;
63
+ }
64
+ }
65
+
66
+ function baseUrl(chain: Chain) {
67
+ return `https://api.blockchair.com/${mapChainToBlockchairChain(chain)}`;
68
+ }
69
+
70
+ function getDefaultTxFeeByChain(chain: Chain) {
71
+ switch (chain) {
72
+ case Chain.Bitcoin:
73
+ return 5;
74
+ case Chain.Dogecoin:
75
+ return 10000;
76
+ case Chain.Litecoin:
77
+ return 1;
78
+ case Chain.Zcash:
79
+ return 1;
80
+ default:
81
+ return 2;
82
+ }
83
+ }
84
+
85
+ function mapChainToBlockchairChain(chain: Chain) {
86
+ switch (chain) {
87
+ case Chain.BitcoinCash:
88
+ return "bitcoin-cash";
89
+ case Chain.Litecoin:
90
+ return "litecoin";
91
+ case Chain.Dash:
92
+ return "dash";
93
+ case Chain.Dogecoin:
94
+ return "dogecoin";
95
+ case Chain.Zcash:
96
+ return "zcash";
97
+ case Chain.Polkadot:
98
+ return "polkadot";
99
+ default:
100
+ return "bitcoin";
101
+ }
102
+ }
103
+
104
+ async function getSuggestedTxFee(chain: Chain) {
105
+ try {
106
+ //Use Bitgo API for fee estimation
107
+ //Refer: https://app.bitgo.com/docs/#operation/v2.tx.getfeeestimate
108
+ const { feePerKb } = await RequestClient.get<{
109
+ feePerKb: number;
110
+ cpfpFeePerKb: number;
111
+ numBlocks: number;
112
+ feeByBlockTarget: { 1: number; 3: number };
113
+ }>(`https://app.bitgo.com/api/v2/${chain.toLowerCase()}/tx/fee`);
114
+ const suggestedFee = feePerKb / 1000;
115
+
116
+ return Math.max(suggestedFee, getDefaultTxFeeByChain(chain));
117
+ } catch {
118
+ return getDefaultTxFeeByChain(chain);
119
+ }
120
+ }
121
+
122
+ async function blockchairRequest<T>(url: string, apiKey?: string): Promise<T> {
123
+ const response = await RequestClient.get<BlockchairResponse<T>>(
124
+ `${url}${apiKey ? `${url.includes("?") ? "&" : "?"}key=${apiKey}` : ""}`,
125
+ );
126
+
127
+ if (!response || response.context.code !== 200)
128
+ throw new USwapError("toolbox_utxo_api_error", { error: `Failed to query ${url}` });
129
+
130
+ return response.data as T;
131
+ }
132
+
133
+ async function getAddressData({ address, chain, apiKey }: BlockchairParams<{ address?: string }>) {
134
+ if (!address) throw new USwapError("toolbox_utxo_invalid_params", { error: "Address is required" });
135
+
136
+ try {
137
+ const response = await blockchairRequest<BlockchairAddressResponse>(
138
+ `${baseUrl(chain)}/dashboards/address/${address}?transaction_details=true`,
139
+ apiKey,
140
+ );
141
+
142
+ return response[address];
143
+ } catch {
144
+ return { address: { balance: 0, transaction_count: 0 }, utxo: [] };
145
+ }
146
+ }
147
+
148
+ async function getUnconfirmedBalance({ address, chain, apiKey }: BlockchairParams<{ address?: string }>) {
149
+ const response = await getAddressData({ address, apiKey, chain });
150
+
151
+ return response?.address.balance || 0;
152
+ }
153
+
154
+ async function getRawTx({ chain, apiKey, txHash }: BlockchairParams<{ txHash?: string }>) {
155
+ if (!txHash) throw new USwapError("toolbox_utxo_invalid_params", { error: "TxHash is required" });
156
+
157
+ try {
158
+ const rawTxResponse = await blockchairRequest<BlockchairRawTransactionResponse>(
159
+ `${baseUrl(chain)}/raw/transaction/${txHash}`,
160
+ apiKey,
161
+ );
162
+ return rawTxResponse?.[txHash]?.raw_transaction || "";
163
+ } catch (error) {
164
+ const errorMessage = error instanceof Error ? error.message : String(error);
165
+ console.error(`Failed to fetch raw transaction: ${errorMessage}`);
166
+ return "";
167
+ }
168
+ }
169
+
170
+ async function fetchUtxosBatch({ chain, address, apiKey, offset = 0, limit = 30 }: BlockchairFetchUnspentUtxoParams) {
171
+ // Only fetch the fields we need to reduce payload size
172
+ const fields = "is_spent,transaction_hash,index,value,script_hex,block_id,spending_signature_hex";
173
+
174
+ const response = await blockchairRequest<BlockchairOutputsResponse[]>(
175
+ // TODO - remove max value limit once we updated bitcoinjs-lib to support larger values
176
+ `${baseUrl(chain)}/outputs?q=recipient(${address}),is_spent(false),value(..2000000000000000)&s=value(desc)&fields=${fields}&limit=${limit}&offset=${offset}`,
177
+ apiKey,
178
+ );
179
+
180
+ const txs = response.map(
181
+ ({ is_spent, script_hex, block_id, transaction_hash, index, value, spending_signature_hex }) => ({
182
+ hash: transaction_hash,
183
+ index,
184
+ is_confirmed: block_id !== -1,
185
+ is_spent,
186
+ script_hex,
187
+ txHex: spending_signature_hex,
188
+ value,
189
+ }),
190
+ );
191
+
192
+ return txs;
193
+ }
194
+
195
+ function getTxsValue(txs: Awaited<ReturnType<typeof fetchUtxosBatch>>) {
196
+ return txs.reduce((total, tx) => total + tx.value, 0);
197
+ }
198
+
199
+ function pickMostValuableTxs(
200
+ txs: Awaited<ReturnType<typeof fetchUtxosBatch>>,
201
+ targetValue?: number,
202
+ ): Awaited<ReturnType<typeof fetchUtxosBatch>> {
203
+ const sortedTxs = [...txs].sort((a, b) => b.value - a.value);
204
+
205
+ if (targetValue) {
206
+ const result = [];
207
+ let accumulated = 0;
208
+
209
+ for (const utxo of sortedTxs) {
210
+ result.push(utxo);
211
+ accumulated += utxo.value;
212
+ if (accumulated >= targetValue) break;
213
+ }
214
+
215
+ return result;
216
+ }
217
+
218
+ return sortedTxs;
219
+ }
220
+
221
+ async function getUnspentUtxos({
222
+ chain,
223
+ address,
224
+ apiKey,
225
+ targetValue,
226
+ accumulativeValue = 0,
227
+ offset = 0,
228
+ limit = 30,
229
+ }: BlockchairFetchUnspentUtxoParams): Promise<Awaited<ReturnType<typeof fetchUtxosBatch>>> {
230
+ if (!address) throw new USwapError("toolbox_utxo_invalid_params", { error: "Address is required" });
231
+
232
+ try {
233
+ const utxos = await fetchUtxosBatch({ address, apiKey, chain, limit, offset, targetValue });
234
+ const utxosCount = utxos.length;
235
+ const isComplete = utxosCount < limit;
236
+
237
+ const unspentUtxos = utxos.filter(({ is_spent }) => !is_spent);
238
+
239
+ const unspentUtxosValue = getTxsValue(unspentUtxos);
240
+ const totalCurrentValue = accumulativeValue + unspentUtxosValue;
241
+
242
+ const limitReached = targetValue && totalCurrentValue >= targetValue;
243
+
244
+ if (isComplete || limitReached) {
245
+ return pickMostValuableTxs(unspentUtxos, targetValue);
246
+ }
247
+
248
+ const nextBatch = await getUnspentUtxos({
249
+ accumulativeValue: totalCurrentValue,
250
+ address,
251
+ apiKey,
252
+ chain,
253
+ limit,
254
+ offset: offset + limit,
255
+ targetValue,
256
+ });
257
+
258
+ const allUtxos = [...unspentUtxos, ...nextBatch];
259
+
260
+ return pickMostValuableTxs(allUtxos, targetValue);
261
+ } catch (error) {
262
+ const errorMessage = error instanceof Error ? error.message : String(error);
263
+ console.error(`Failed to fetch unspent UTXOs: ${errorMessage}`);
264
+ return [];
265
+ }
266
+ }
267
+
268
+ async function getUtxos({
269
+ address,
270
+ chain,
271
+ apiKey,
272
+ fetchTxHex = true,
273
+ targetValue,
274
+ }: BlockchairParams<{ address: string; fetchTxHex?: boolean; targetValue?: number }>) {
275
+ const utxos = await getUnspentUtxos({ address, apiKey, chain, targetValue });
276
+
277
+ const results = [];
278
+
279
+ for (const { hash, index, script_hex, value } of utxos) {
280
+ let txHex: string | undefined;
281
+ if (fetchTxHex) {
282
+ txHex = await getRawTx({ apiKey, chain, txHash: hash });
283
+ }
284
+ results.push({
285
+ address,
286
+ hash,
287
+ index,
288
+ txHex,
289
+ value,
290
+ witnessUtxo: { script: Buffer.from(script_hex, "hex"), value },
291
+ });
292
+ }
293
+ return results;
294
+ }
295
+
296
+ export function getUtxoApi(chain: UTXOChain) {
297
+ const apiKey = USwapConfig.get("apiKeys").blockchair || "";
298
+
299
+ warnOnce({
300
+ condition: !apiKey,
301
+ id: "no_blockchair_api_key_warning",
302
+ warning: "No Blockchair API key found. Functionality will be limited.",
303
+ });
304
+
305
+ return {
306
+ broadcastTx: (txHash: string) => broadcastUTXOTx({ chain, txHash }),
307
+ getAddressData: (address: string) => getAddressData({ address, apiKey, chain }),
308
+ getBalance: (address: string) => getUnconfirmedBalance({ address, apiKey, chain }),
309
+ getRawTx: (txHash: string) => getRawTx({ apiKey, chain, txHash }),
310
+ getSuggestedTxFee: () => getSuggestedTxFee(chain),
311
+ getUtxos: (params: { address: string; fetchTxHex?: boolean; targetValue?: number }) =>
312
+ getUtxos({ ...params, apiKey, chain }),
313
+ };
314
+ }
315
+
316
+ /**
317
+ * "Factory" to ensure typing for custom UTXO APIs
318
+ */
319
+ export function createCustomUtxoApi(methods: ReturnType<typeof getUtxoApi>) {
320
+ return methods;
321
+ }
322
+
323
+ export function getUtxoNetwork() {
324
+ return function getNetwork(chain: Chain) {
325
+ switch (chain) {
326
+ case Chain.Bitcoin:
327
+ return networks.bitcoin;
328
+ case Chain.BitcoinCash:
329
+ return coininfo.bitcoincash.main.toBitcoinJS();
330
+ case Chain.Dash:
331
+ return coininfo.dash.main.toBitcoinJS();
332
+ case Chain.Litecoin:
333
+ return coininfo.litecoin.main.toBitcoinJS();
334
+
335
+ case Chain.Dogecoin: {
336
+ const bip32 = { private: 0x04358394, public: 0x043587cf };
337
+ const test = coininfo.dogecoin.test;
338
+ test.versions.bip32 = bip32;
339
+ return coininfo.dogecoin.main.toBitcoinJS();
340
+ }
341
+
342
+ case Chain.Zcash: {
343
+ return zcashNetworks.zcash;
344
+ }
345
+
346
+ default:
347
+ throw new USwapError("toolbox_utxo_not_supported", { chain });
348
+ }
349
+ };
350
+ }
351
+
352
+ interface BlockchairVin {
353
+ txid: string;
354
+ vout: number;
355
+ scriptSig: { asm: string; hex: string };
356
+ sequence: number;
357
+ }
358
+
359
+ interface BlockchairVout {
360
+ value: number;
361
+ n: number;
362
+ scriptPubKey: { asm: string; hex: string; address: string; type: string; addresses: string[]; reqSigs: number };
363
+ }
364
+
365
+ interface BlockchairTransaction {
366
+ block_id: number;
367
+ hash: string;
368
+ time: string;
369
+ balance_change: number;
370
+ }
371
+
372
+ interface BlockchairUtxo {
373
+ block_id: number;
374
+ transaction_hash: string;
375
+ index: number;
376
+ value: number;
377
+ }
378
+
379
+ interface BlockchairAddressCoreData {
380
+ type: string;
381
+ script_hex: string;
382
+ balance: number;
383
+ balance_usd: number;
384
+ received: number;
385
+ received_usd: number;
386
+ spent: number;
387
+ spent_usd: number;
388
+ output_count: number;
389
+ unspent_output_count: number;
390
+ first_seen_receiving: string;
391
+ last_seen_receiving: string;
392
+ first_seen_spending: null | string;
393
+ last_seen_spending: null | string;
394
+ transaction_count: number;
395
+ scripthash_type: null | string;
396
+ }
397
+
398
+ interface BlockchairInputOutputCommonData {
399
+ block_id: number;
400
+ transaction_id: number;
401
+ index: number;
402
+ transaction_hash: string;
403
+ date: string;
404
+ time: string;
405
+ value: number;
406
+ value_usd: number;
407
+ recipient: string;
408
+ type: string;
409
+ script_hex: string;
410
+ is_from_coinbase: boolean;
411
+ is_spendable: boolean | null;
412
+ is_spent: boolean;
413
+ lifespan: number | null;
414
+ cdd: number | null;
415
+ }
416
+
417
+ interface BlockchairSpendingBlockData {
418
+ spending_block_id: number | null;
419
+ spending_transaction_id: number | null;
420
+ spending_index: number | null;
421
+ spending_transaction_hash: string | null;
422
+ spending_date: string | null;
423
+ spending_time: string | null;
424
+ spending_value_usd: number | null;
425
+ spending_sequence: number | null;
426
+ spending_signature_hex: string | null;
427
+ spending_witness: string | null;
428
+ }
429
+
430
+ interface BlockchairAddressResponse {
431
+ [key: string]: { address: BlockchairAddressCoreData; transactions: BlockchairTransaction[]; utxo: BlockchairUtxo[] };
432
+ }
433
+
434
+ interface BlockchairOutputsResponse extends BlockchairSpendingBlockData, BlockchairInputOutputCommonData {}
435
+
436
+ interface BlockchairRawTransactionResponse {
437
+ [key: string]: {
438
+ raw_transaction: string;
439
+ decoded_raw_transaction: {
440
+ txid: string;
441
+ hash: string;
442
+ version: number;
443
+ size: number;
444
+ vsize: number;
445
+ weight: number;
446
+ locktime: number;
447
+ vin: BlockchairVin[];
448
+ vout: BlockchairVout[];
449
+ };
450
+ };
451
+ }
452
+
453
+ interface BlockchairResponse<T> {
454
+ data: T;
455
+ context: {
456
+ code: number;
457
+ source: string;
458
+ results: number;
459
+ state: number;
460
+ market_price_usd: number;
461
+ cache: { live: boolean; duration: number; since: string; until: string; time: any };
462
+ api: {
463
+ version: string;
464
+ last_major_update: string;
465
+ next_major_update: null | string;
466
+ documentation: string;
467
+ notice: string;
468
+ };
469
+ servers: string;
470
+ time: number;
471
+ render_time: number;
472
+ full_time: number;
473
+ request_cost: number;
474
+ };
475
+ }