@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
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Modifications © 2025 Horizontal Systems.
3
+ */
4
+
5
+ import { USwapError } from "@tcswap/helpers";
6
+ import base58check from "bs58check";
7
+ // @ts-expect-error
8
+ import cashaddr from "cashaddrjs";
9
+
10
+ enum Format {
11
+ Legacy = "legacy",
12
+ Bitpay = "bitpay",
13
+ Cashaddr = "cashaddr",
14
+ }
15
+ enum UtxoNetwork {
16
+ Mainnet = "mainnet",
17
+ Testnet = "testnet",
18
+ }
19
+ enum Type {
20
+ P2PKH = "p2pkh",
21
+ P2SH = "p2sh",
22
+ }
23
+
24
+ const VERSION_BYTE = {
25
+ [Format.Legacy]: {
26
+ [UtxoNetwork.Mainnet]: { [Type.P2PKH]: 0, [Type.P2SH]: 5 },
27
+ [UtxoNetwork.Testnet]: { [Type.P2PKH]: 111, [Type.P2SH]: 196 },
28
+ },
29
+ [Format.Bitpay]: {
30
+ [UtxoNetwork.Mainnet]: { [Type.P2PKH]: 28, [Type.P2SH]: 40 },
31
+ [UtxoNetwork.Testnet]: { [Type.P2PKH]: 111, [Type.P2SH]: 196 },
32
+ },
33
+ };
34
+
35
+ type DecodedType = { format: Format; network: UtxoNetwork; type: Type; hash: any };
36
+
37
+ function isValidAddress(input: any) {
38
+ try {
39
+ decodeAddress(input);
40
+ return true;
41
+ } catch {
42
+ return false;
43
+ }
44
+ }
45
+
46
+ function detectAddressNetwork(address: string) {
47
+ return decodeAddress(address)?.network;
48
+ }
49
+
50
+ function toLegacyAddress(address: string): string {
51
+ const decoded = decodeAddress(address);
52
+ if (decoded?.format === Format.Legacy) {
53
+ return address;
54
+ }
55
+ return encodeAsLegacy(decoded);
56
+ }
57
+
58
+ function toCashAddress(address: string): string {
59
+ const decoded = decodeAddress(address);
60
+ return encodeAsCashaddr(decoded);
61
+ }
62
+
63
+ function decodeAddress(address: string) {
64
+ try {
65
+ const decoded = decodeBase58Address(address);
66
+ if (decoded) {
67
+ return decoded;
68
+ }
69
+ } catch {
70
+ // Try to decode as cashaddr if base58 decoding fails.
71
+ }
72
+ try {
73
+ const decoded = decodeCashAddress(address);
74
+ if (decoded) {
75
+ return decoded;
76
+ }
77
+ } catch {
78
+ // Try to decode as bitpay if cashaddr decoding fails.
79
+ }
80
+ throw new USwapError("toolbox_utxo_invalid_address", { address });
81
+ }
82
+
83
+ function decodeBase58Address(address: string) {
84
+ try {
85
+ const payload = base58check.decode(address);
86
+
87
+ // BASE_58_CHECK_PAYLOAD_LENGTH
88
+ if (payload.length !== 21) throw new USwapError("toolbox_utxo_invalid_address", { address });
89
+ const versionByte = payload[0];
90
+ const hash = Array.prototype.slice.call(payload, 1);
91
+
92
+ switch (versionByte) {
93
+ case VERSION_BYTE[Format.Legacy][UtxoNetwork.Mainnet][Type.P2PKH]:
94
+ return { format: Format.Legacy, hash, network: UtxoNetwork.Mainnet, type: Type.P2PKH };
95
+
96
+ case VERSION_BYTE[Format.Legacy][UtxoNetwork.Mainnet][Type.P2SH]:
97
+ return { format: Format.Legacy, hash, network: UtxoNetwork.Mainnet, type: Type.P2SH };
98
+
99
+ case VERSION_BYTE[Format.Legacy][UtxoNetwork.Testnet][Type.P2PKH]:
100
+ return { format: Format.Legacy, hash, network: UtxoNetwork.Testnet, type: Type.P2PKH };
101
+
102
+ case VERSION_BYTE[Format.Legacy][UtxoNetwork.Testnet][Type.P2SH]:
103
+ return { format: Format.Legacy, hash, network: UtxoNetwork.Testnet, type: Type.P2SH };
104
+
105
+ case VERSION_BYTE[Format.Bitpay][UtxoNetwork.Mainnet][Type.P2PKH]:
106
+ return { format: Format.Bitpay, hash, network: UtxoNetwork.Mainnet, type: Type.P2PKH };
107
+
108
+ case VERSION_BYTE[Format.Bitpay][UtxoNetwork.Mainnet][Type.P2SH]:
109
+ return { format: Format.Bitpay, hash, network: UtxoNetwork.Mainnet, type: Type.P2SH };
110
+
111
+ default:
112
+ return;
113
+ }
114
+ } catch {
115
+ return;
116
+ }
117
+ }
118
+
119
+ function decodeCashAddress(address: string) {
120
+ if (address.indexOf(":") !== -1) {
121
+ try {
122
+ return decodeCashAddressWithPrefix(address);
123
+ } catch {
124
+ // Try to decode as legacy if cashaddr decoding fails.
125
+ }
126
+ } else {
127
+ const prefixes = ["bitcoincash", "bchtest", "bchreg"];
128
+ for (const prefix of prefixes) {
129
+ try {
130
+ return decodeCashAddressWithPrefix(`${prefix}:${address}`);
131
+ } catch {
132
+ // Try next prefix if decoding fails.
133
+ }
134
+ }
135
+ }
136
+
137
+ return;
138
+ }
139
+
140
+ function decodeCashAddressWithPrefix(address: string) {
141
+ try {
142
+ const { hash, prefix, type } = cashaddr.decode(address);
143
+
144
+ return {
145
+ format: Format.Cashaddr,
146
+ hash: Array.prototype.slice.call(hash, 0),
147
+ network: prefix === "bitcoincash" ? UtxoNetwork.Mainnet : UtxoNetwork.Testnet,
148
+ type: type === "P2PKH" ? Type.P2PKH : Type.P2SH,
149
+ };
150
+ } catch {
151
+ return;
152
+ }
153
+ }
154
+
155
+ function encodeAsLegacy(decoded: DecodedType) {
156
+ const versionByte = VERSION_BYTE[Format.Legacy][decoded.network][decoded.type];
157
+ const buffer = Buffer.alloc(1 + decoded.hash.length);
158
+ buffer[0] = versionByte;
159
+ buffer.set(decoded.hash, 1);
160
+ return base58check.encode(buffer);
161
+ }
162
+
163
+ function encodeAsCashaddr(decoded: DecodedType) {
164
+ const prefix = decoded.network === UtxoNetwork.Mainnet ? "bitcoincash" : "bchtest";
165
+ const type = decoded.type === Type.P2PKH ? "P2PKH" : "P2SH";
166
+ const hash = new Uint8Array(decoded.hash);
167
+ return cashaddr.encode(prefix, type, hash);
168
+ }
169
+
170
+ export { detectAddressNetwork, isValidAddress, UtxoNetwork, toCashAddress, toLegacyAddress };
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Modifications © 2025 Horizontal Systems.
3
+ */
4
+
5
+ import { Chain, USwapError, type UTXOChain } from "@tcswap/helpers";
6
+
7
+ import {
8
+ calculateTxSize,
9
+ getInputSize,
10
+ getOutputSize,
11
+ getScriptTypeForAddress,
12
+ TX_OVERHEAD,
13
+ UTXOScriptType,
14
+ } from "../helpers/txSize";
15
+ import type { TargetOutput, UTXOCalculateTxSizeParams, UTXOType } from "../types";
16
+
17
+ export function getDustThreshold(chain: UTXOChain) {
18
+ switch (chain) {
19
+ case Chain.Bitcoin:
20
+ case Chain.BitcoinCash:
21
+ return 550;
22
+ case Chain.Dash:
23
+ case Chain.Litecoin:
24
+ return 5500;
25
+ case Chain.Dogecoin:
26
+ return 100000;
27
+ case Chain.Zcash:
28
+ return 546;
29
+ default:
30
+ throw new USwapError("toolbox_utxo_not_supported", { chain });
31
+ }
32
+ }
33
+
34
+ export function accumulative({
35
+ inputs,
36
+ outputs,
37
+ feeRate: initialFeeRate = 1,
38
+ chain = Chain.Bitcoin,
39
+ changeAddress = "",
40
+ }: UTXOCalculateTxSizeParams & { outputs: TargetOutput[]; chain: UTXOChain; changeAddress?: string }) {
41
+ const feeRate = Math.ceil(initialFeeRate);
42
+
43
+ const newTxType =
44
+ inputs[0] && "address" in inputs[0] && inputs[0].address
45
+ ? getScriptTypeForAddress(inputs[0].address)
46
+ : UTXOScriptType.P2PKH;
47
+ // skip input if adding it would cost more than input is worth
48
+ const filteredInputs = inputs.filter((input) => getInputSize(input) * feeRate <= input.value);
49
+
50
+ const txSizeWithoutInputs =
51
+ TX_OVERHEAD + outputs.reduce((total, output) => total + getOutputSize(output, newTxType), 0);
52
+
53
+ const amountToSend = outputs.reduce((total, output) => total + output.value, 0);
54
+
55
+ let fees = txSizeWithoutInputs * feeRate;
56
+ let inputsValue = 0;
57
+ const inputsToUse: typeof inputs = [];
58
+
59
+ for (const input of filteredInputs) {
60
+ const inputSize = getInputSize(input);
61
+ const inputFee = feeRate * inputSize;
62
+
63
+ fees += inputFee;
64
+ inputsValue += input.value;
65
+
66
+ inputsToUse.push(input);
67
+
68
+ const totalCost = fees + amountToSend;
69
+
70
+ // we need more inputs
71
+ if (inputsValue < totalCost) continue;
72
+
73
+ const remainder = inputsValue - totalCost;
74
+
75
+ const feeForExtraOutput = feeRate * getOutputSize({ address: "", value: 0 }, newTxType);
76
+
77
+ // potential change address
78
+ if (remainder > feeForExtraOutput) {
79
+ const feeAfterExtraOutput = feeForExtraOutput + fees;
80
+ const remainderAfterExtraOutput = inputsValue - (amountToSend + feeAfterExtraOutput);
81
+
82
+ // is it worth a change output aka can we send it in the future?
83
+ if (remainderAfterExtraOutput > Math.max(getInputSize({} as UTXOType) * feeRate, getDustThreshold(chain))) {
84
+ return {
85
+ fee: feeAfterExtraOutput,
86
+ inputs: inputsToUse,
87
+ outputs: outputs.concat({ address: changeAddress, value: remainderAfterExtraOutput }),
88
+ };
89
+ }
90
+ }
91
+ return { fee: fees, inputs: inputsToUse, outputs };
92
+ }
93
+
94
+ // We don't have enough inputs, let's calculate transaction fee accrude to the last input
95
+ return { fee: feeRate * calculateTxSize({ feeRate, inputs, outputs }) };
96
+ }
@@ -0,0 +1,4 @@
1
+ export * from "./api";
2
+ export * from "./bchaddrjs";
3
+ export * from "./coinselect";
4
+ export * from "./txSize";
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Modifications © 2025 Horizontal Systems.
3
+ */
4
+
5
+ import { USwapError } from "@tcswap/helpers";
6
+ import { opcodes, script } from "bitcoinjs-lib";
7
+ import type { TargetOutput, UTXOCalculateTxSizeParams, UTXOInputWithScriptType, UTXOType } from "../types";
8
+
9
+ /**
10
+ * Minimum transaction fee
11
+ * 1000 satoshi/kB (similar to current `minrelaytxfee`)
12
+ * @see https://github.com/bitcoin/bitcoin/blob/db88db47278d2e7208c50d16ab10cb355067d071/src/validation.h#L56
13
+ */
14
+ export const MIN_TX_FEE = 1000;
15
+ export const TX_OVERHEAD = 4 + 1 + 1 + 4; //10
16
+ export const OP_RETURN_OVERHEAD = 1 + 8 + 1; //10
17
+ const TX_INPUT_BASE = 32 + 4 + 1 + 4; // 41
18
+ const TX_INPUT_PUBKEYHASH = 107;
19
+
20
+ export function compileMemo(memo: string) {
21
+ const data = Buffer.from(memo, "utf8"); // converts MEMO to buffer
22
+ return script.compile([opcodes.OP_RETURN as number, data]); // Compile OP_RETURN script
23
+ }
24
+
25
+ export enum UTXOScriptType {
26
+ P2PKH = "P2PKH", // legacy
27
+ // P2SH = 'P2SH', // multisig
28
+ P2WPKH = "P2WPKH", // bech32 - native segwit
29
+ // P2TR = "P2TR", // taproot
30
+ }
31
+
32
+ export const InputSizes: Record<UTXOScriptType, number> = {
33
+ [UTXOScriptType.P2PKH]: 148,
34
+ // [UTXOScriptType.P2SH]: 91,
35
+ [UTXOScriptType.P2WPKH]: 68,
36
+ };
37
+
38
+ export const OutputSizes: Record<UTXOScriptType, number> = {
39
+ [UTXOScriptType.P2PKH]: 34,
40
+ // [UTXOScriptType.P2SH]: 91,
41
+ [UTXOScriptType.P2WPKH]: 31,
42
+ };
43
+
44
+ export const getScriptTypeForAddress = (address: string) => {
45
+ // Native SegWit (Bech32) addresses - P2WPKH
46
+ // Bitcoin: bc1 (mainnet), tb1 (testnet)
47
+ // Litecoin: ltc1 (mainnet), tltc1 (testnet)
48
+ if (
49
+ address.startsWith("bc1") ||
50
+ address.startsWith("tb1") ||
51
+ address.startsWith("ltc1") ||
52
+ address.startsWith("tltc1")
53
+ ) {
54
+ // Note: bc1p/tb1p are Taproot (P2TR) addresses, but we're treating them as P2WPKH for now
55
+ // since P2TR is not yet implemented
56
+ return UTXOScriptType.P2WPKH;
57
+ }
58
+
59
+ // P2SH addresses (Pay-to-Script-Hash) - Currently commented out but kept for future use
60
+ // Bitcoin: 3 (mainnet), 2 (testnet)
61
+ // Litecoin: M or 2 (mainnet), Q (testnet)
62
+ // Dash: 7 (mainnet)
63
+ // if (address.startsWith('3') || address.startsWith('2') ||
64
+ // address.startsWith('M') || address.startsWith('Q') ||
65
+ // address.startsWith('7')) {
66
+ // return UTXOScriptType.P2SH;
67
+ // }
68
+
69
+ // Legacy P2PKH addresses
70
+ // Bitcoin: 1 (mainnet), m/n (testnet)
71
+ // Bitcoin Cash: bitcoincash:q (CashAddr format), q (legacy), 1 (legacy)
72
+ // Litecoin: L (mainnet), m/n (testnet)
73
+ // Dogecoin: D (mainnet), n (testnet)
74
+ // Dash: X (mainnet), y (testnet)
75
+ // Zcash: t1 (transparent mainnet), tm (testnet)
76
+ if (
77
+ // Bitcoin legacy
78
+ address.startsWith("1") ||
79
+ address.startsWith("m") ||
80
+ address.startsWith("n") ||
81
+ // Bitcoin Cash
82
+ address.startsWith("bitcoincash:q") ||
83
+ address.startsWith("bitcoincash:p") ||
84
+ address.startsWith("q") ||
85
+ address.startsWith("p") ||
86
+ // Litecoin legacy (also uses 3 for P2SH but treating as P2PKH for now)
87
+ address.startsWith("L") ||
88
+ address.startsWith("M") ||
89
+ address.startsWith("3") ||
90
+ // Dogecoin
91
+ address.startsWith("D") ||
92
+ address.startsWith("A") ||
93
+ address.startsWith("9") ||
94
+ // Dash
95
+ address.startsWith("X") ||
96
+ address.startsWith("7") ||
97
+ address.startsWith("y") ||
98
+ // Zcash transparent addresses
99
+ address.startsWith("t1") ||
100
+ address.startsWith("t3") ||
101
+ address.startsWith("tm")
102
+ ) {
103
+ return UTXOScriptType.P2PKH;
104
+ }
105
+
106
+ throw new USwapError("toolbox_utxo_invalid_address", { address });
107
+ };
108
+
109
+ export const calculateTxSize = ({ inputs, outputs, feeRate }: UTXOCalculateTxSizeParams) => {
110
+ const newTxType =
111
+ inputs[0] && "address" in inputs[0] && inputs[0].address
112
+ ? getScriptTypeForAddress(inputs[0].address)
113
+ : UTXOScriptType.P2PKH;
114
+ const inputSize = inputs
115
+ .filter((utxo) => utxo.value >= InputSizes["type" in utxo ? utxo.type : UTXOScriptType.P2PKH] * Math.ceil(feeRate))
116
+ .reduce((total, utxo) => total + getInputSize(utxo), 0);
117
+
118
+ const outputSize = outputs?.reduce((total, output) => total + getOutputSize(output), 0) || OutputSizes[newTxType];
119
+
120
+ return TX_OVERHEAD + inputSize + outputSize;
121
+ };
122
+
123
+ export const getInputSize = (input: UTXOInputWithScriptType | UTXOType) => {
124
+ if ("type" in input) {
125
+ return InputSizes[input.type];
126
+ }
127
+ if ("address" in input && input.address) {
128
+ return InputSizes[getScriptTypeForAddress(input.address as string)];
129
+ }
130
+ return TX_INPUT_BASE + TX_INPUT_PUBKEYHASH;
131
+ };
132
+
133
+ export const getOutputSize = (output: TargetOutput, scriptType?: UTXOScriptType) => {
134
+ if (output?.script) {
135
+ return OP_RETURN_OVERHEAD + output.script.length + (output.script.length >= 74 ? 2 : 1);
136
+ }
137
+ if (scriptType) {
138
+ return OutputSizes[scriptType];
139
+ }
140
+ return OutputSizes[UTXOScriptType.P2PKH];
141
+ };
@@ -0,0 +1,6 @@
1
+ export * from "./helpers";
2
+ export * from "./toolbox";
3
+ export * from "./toolbox/bitcoinCash";
4
+ export * from "./toolbox/utxo";
5
+ export * from "./toolbox/zcash";
6
+ export * from "./types";
@@ -0,0 +1,247 @@
1
+ /**
2
+ * Modifications © 2025 Horizontal Systems.
3
+ */
4
+
5
+ import {
6
+ address as bchAddress,
7
+ Transaction,
8
+ TransactionBuilder,
9
+ // @ts-expect-error
10
+ } from "@psf/bitcoincashjs-lib";
11
+ import {
12
+ Chain,
13
+ type ChainSigner,
14
+ type DerivationPathArray,
15
+ derivationPathToString,
16
+ FeeOption,
17
+ NetworkDerivationPath,
18
+ USwapError,
19
+ updateDerivationPath,
20
+ } from "@tcswap/helpers";
21
+ import { Psbt } from "bitcoinjs-lib";
22
+ import { accumulative, compileMemo, getUtxoApi, getUtxoNetwork, toCashAddress, toLegacyAddress } from "../helpers";
23
+ import type {
24
+ BchECPair,
25
+ TargetOutput,
26
+ TransactionBuilderType,
27
+ TransactionType,
28
+ UTXOBuildTxParams,
29
+ UTXOTransferParams,
30
+ UTXOType,
31
+ } from "../types";
32
+ import type { UtxoToolboxParams } from "./params";
33
+ import { createUTXOToolbox, getCreateKeysForPath } from "./utxo";
34
+ import { bchValidateAddress, stripPrefix } from "./validators";
35
+
36
+ const chain = Chain.BitcoinCash;
37
+
38
+ export function stripToCashAddress(address: string) {
39
+ return stripPrefix(toCashAddress(address));
40
+ }
41
+
42
+ function createSignerWithKeys(keys: BchECPair) {
43
+ function signTransaction({ builder, utxos }: { builder: TransactionBuilderType; utxos: UTXOType[] }) {
44
+ utxos.forEach((utxo, index) => {
45
+ builder.sign(index, keys, undefined, 0x41, utxo.witnessUtxo?.value);
46
+ });
47
+
48
+ return builder.build();
49
+ }
50
+
51
+ const getAddress = () => {
52
+ const address = keys.getAddress(0);
53
+ return Promise.resolve(stripToCashAddress(address));
54
+ };
55
+
56
+ return { getAddress, signTransaction };
57
+ }
58
+
59
+ export async function createBCHToolbox<T extends typeof Chain.BitcoinCash>(
60
+ toolboxParams: UtxoToolboxParams[T] | { phrase?: string; derivationPath?: DerivationPathArray; index?: number },
61
+ ) {
62
+ const phrase = "phrase" in toolboxParams ? toolboxParams.phrase : undefined;
63
+
64
+ const index = "index" in toolboxParams ? toolboxParams.index || 0 : 0;
65
+
66
+ const derivationPath = derivationPathToString(
67
+ "derivationPath" in toolboxParams && toolboxParams.derivationPath
68
+ ? toolboxParams.derivationPath
69
+ : updateDerivationPath(NetworkDerivationPath[chain], { index }),
70
+ );
71
+
72
+ const keys = phrase ? (await getCreateKeysForPath(chain))({ derivationPath, phrase }) : undefined;
73
+
74
+ const signer = keys ? createSignerWithKeys(keys) : "signer" in toolboxParams ? toolboxParams.signer : undefined;
75
+
76
+ function getAddress() {
77
+ return Promise.resolve(signer?.getAddress());
78
+ }
79
+
80
+ const { getBalance, getFeeRates, broadcastTx, ...toolbox } = await createUTXOToolbox({ chain });
81
+
82
+ function handleGetBalance(address: string, _scamFilter = true) {
83
+ return getBalance(stripPrefix(toCashAddress(address)));
84
+ }
85
+
86
+ return {
87
+ ...toolbox,
88
+ broadcastTx,
89
+ buildTx,
90
+ createTransaction,
91
+ getAddress,
92
+ getAddressFromKeys,
93
+ getBalance: handleGetBalance,
94
+ getFeeRates,
95
+ stripPrefix,
96
+ stripToCashAddress,
97
+ transfer: transfer({ broadcastTx, getFeeRates, signer }),
98
+ validateAddress: bchValidateAddress,
99
+ };
100
+ }
101
+
102
+ async function createTransaction({ assetValue, recipient, memo, feeRate, sender }: UTXOBuildTxParams) {
103
+ if (!bchValidateAddress(recipient)) throw new USwapError("toolbox_utxo_invalid_address", { address: recipient });
104
+
105
+ // Overestimate by 7500 byte * feeRate to ensure we have enough UTXOs for fees and change
106
+ const targetValue = Math.ceil(assetValue.getBaseValue("number") + feeRate * 7500);
107
+
108
+ const utxos = await getUtxoApi(chain).getUtxos({
109
+ address: stripToCashAddress(sender),
110
+ fetchTxHex: true,
111
+ targetValue,
112
+ });
113
+
114
+ const compiledMemo = memo ? await compileMemo(memo) : null;
115
+
116
+ const targetOutputs: TargetOutput[] = [];
117
+ // output to recipient
118
+ targetOutputs.push({ address: recipient, value: assetValue.getBaseValue("number") });
119
+ const { inputs, outputs } = accumulative({ chain, feeRate, inputs: utxos, outputs: targetOutputs });
120
+
121
+ // .inputs and .outputs will be undefined if no solution was found
122
+ if (!(inputs && outputs)) throw new USwapError("toolbox_utxo_insufficient_balance", { assetValue, sender });
123
+ const getNetwork = await getUtxoNetwork();
124
+ const builder = new TransactionBuilder(getNetwork(chain)) as TransactionBuilderType;
125
+
126
+ await Promise.all(
127
+ inputs.map(async (utxo: UTXOType) => {
128
+ const txHex = await getUtxoApi(chain).getRawTx(utxo.hash);
129
+
130
+ builder.addInput(Transaction.fromBuffer(Buffer.from(txHex, "hex")), utxo.index);
131
+ }),
132
+ );
133
+
134
+ for (const output of outputs) {
135
+ const address = "address" in output && output.address ? output.address : toLegacyAddress(sender);
136
+ const getNetwork = await getUtxoNetwork();
137
+ const outputScript = bchAddress.toOutputScript(toLegacyAddress(address), getNetwork(chain));
138
+
139
+ builder.addOutput(outputScript, output.value);
140
+ }
141
+
142
+ // add output for memo
143
+ if (compiledMemo) {
144
+ builder.addOutput(compiledMemo, 0); // Add OP_RETURN {script, value}
145
+ }
146
+
147
+ return { builder, utxos: inputs };
148
+ }
149
+
150
+ function transfer({
151
+ broadcastTx,
152
+ getFeeRates,
153
+ signer,
154
+ }: {
155
+ broadcastTx: (txHash: string) => Promise<string>;
156
+ getFeeRates: () => Promise<Record<FeeOption, number>>;
157
+ signer?: ChainSigner<{ builder: TransactionBuilderType; utxos: UTXOType[] }, TransactionType>;
158
+ }) {
159
+ return async function transfer({
160
+ recipient,
161
+ assetValue,
162
+ feeOptionKey = FeeOption.Fast,
163
+ ...rest
164
+ }: UTXOTransferParams) {
165
+ const from = await signer?.getAddress();
166
+ if (!(signer && from)) throw new USwapError("toolbox_utxo_no_signer");
167
+ if (!recipient)
168
+ throw new USwapError("toolbox_utxo_invalid_params", { error: "Recipient address must be provided" });
169
+
170
+ const feeRate = rest.feeRate || (await getFeeRates())[feeOptionKey];
171
+
172
+ // try out if psbt tx is faster/better/nicer
173
+ const { builder, utxos } = await createTransaction({ ...rest, assetValue, feeRate, recipient, sender: from });
174
+
175
+ const tx = await signer.signTransaction({ builder, utxos });
176
+ const txHex = tx.toHex();
177
+
178
+ return broadcastTx(txHex);
179
+ };
180
+ }
181
+
182
+ async function buildTx({
183
+ assetValue,
184
+ recipient,
185
+ memo,
186
+ feeRate,
187
+ sender,
188
+ setSigHashType,
189
+ }: UTXOBuildTxParams & { setSigHashType?: boolean }) {
190
+ const recipientCashAddress = toCashAddress(recipient);
191
+ if (!bchValidateAddress(recipientCashAddress))
192
+ throw new USwapError("toolbox_utxo_invalid_address", { address: recipientCashAddress });
193
+
194
+ // Overestimate by 7500 byte * feeRate to ensure we have enough UTXOs for fees and change
195
+ const targetValue = Math.ceil(assetValue.getBaseValue("number") + feeRate * 7500);
196
+
197
+ const utxos = await getUtxoApi(chain).getUtxos({
198
+ address: stripToCashAddress(sender),
199
+ fetchTxHex: false,
200
+ targetValue,
201
+ });
202
+
203
+ const feeRateWhole = Number(feeRate.toFixed(0));
204
+ const compiledMemo = memo ? await compileMemo(memo) : null;
205
+
206
+ const targetOutputs = [] as TargetOutput[];
207
+
208
+ // output to recipient
209
+ targetOutputs.push({ address: toLegacyAddress(recipient), value: assetValue.getBaseValue("number") });
210
+
211
+ //2. add output memo to targets (optional)
212
+ if (compiledMemo) {
213
+ targetOutputs.push({ script: compiledMemo, value: 0 });
214
+ }
215
+
216
+ const { inputs, outputs } = accumulative({ chain, feeRate: feeRateWhole, inputs: utxos, outputs: targetOutputs });
217
+
218
+ // .inputs and .outputs will be undefined if no solution was found
219
+ if (!(inputs && outputs)) throw new USwapError("toolbox_utxo_insufficient_balance", { assetValue, sender });
220
+ const getNetwork = await getUtxoNetwork();
221
+ const psbt = new Psbt({ network: getNetwork(chain) }); // Network-specific
222
+
223
+ for (const { hash, index, witnessUtxo } of inputs) {
224
+ psbt.addInput({ hash, index, sighashType: setSigHashType ? 0x41 : undefined, witnessUtxo });
225
+ }
226
+
227
+ // Outputs
228
+ for (const output of outputs) {
229
+ const address = "address" in output && output.address ? output.address : toLegacyAddress(sender);
230
+ const params = output.script
231
+ ? compiledMemo
232
+ ? { script: compiledMemo, value: 0 }
233
+ : undefined
234
+ : { address, value: output.value };
235
+
236
+ if (params) {
237
+ psbt.addOutput(params);
238
+ }
239
+ }
240
+
241
+ return { inputs: inputs as UTXOType[], psbt, utxos };
242
+ }
243
+
244
+ function getAddressFromKeys(keys: { getAddress: (index?: number) => string }) {
245
+ const address = keys.getAddress(0);
246
+ return stripToCashAddress(address);
247
+ }