@uswap/toolboxes 4.3.6

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