@bigmi/core 0.3.1 → 0.4.0-beta.0

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 (270) hide show
  1. package/dist/cjs/actions/getTransactionFee.d.ts +10 -0
  2. package/dist/cjs/actions/getTransactionFee.js +11 -0
  3. package/dist/cjs/actions/getTransactionFee.js.map +1 -0
  4. package/dist/cjs/errors/transport.d.ts +23 -0
  5. package/dist/cjs/errors/transport.js +21 -1
  6. package/dist/cjs/errors/transport.js.map +1 -1
  7. package/dist/cjs/index.d.ts +8 -6
  8. package/dist/cjs/index.js +15 -10
  9. package/dist/cjs/index.js.map +1 -1
  10. package/dist/cjs/test/utils.d.ts +1 -1
  11. package/dist/cjs/test/utils.js +2 -1
  12. package/dist/cjs/test/utils.js.map +1 -1
  13. package/dist/cjs/transports/__mocks__/getTransactionFee.d.ts +3 -0
  14. package/dist/cjs/transports/__mocks__/getTransactionFee.js +7 -0
  15. package/dist/cjs/transports/__mocks__/getTransactionFee.js.map +1 -0
  16. package/dist/cjs/transports/ankr/__mocks__/getTransactionFee/invalid.json +3 -0
  17. package/dist/cjs/transports/ankr/__mocks__/getTransactionFee/valid.json +51 -0
  18. package/dist/cjs/transports/ankr/ankr.types.d.ts +4 -0
  19. package/dist/cjs/transports/ankr/getTransactionFee.d.ts +2 -0
  20. package/dist/cjs/transports/ankr/getTransactionFee.js +18 -0
  21. package/dist/cjs/transports/ankr/getTransactionFee.js.map +1 -0
  22. package/dist/cjs/transports/ankr/getTransactions.d.ts +0 -3
  23. package/dist/cjs/transports/ankr/getTransactions.js +3 -27
  24. package/dist/cjs/transports/ankr/getTransactions.js.map +1 -1
  25. package/dist/cjs/transports/ankr/methods.js +2 -0
  26. package/dist/cjs/transports/ankr/methods.js.map +1 -1
  27. package/dist/cjs/transports/ankr/utils.d.ts +3 -0
  28. package/dist/cjs/transports/ankr/utils.js +29 -0
  29. package/dist/cjs/transports/ankr/utils.js.map +1 -0
  30. package/dist/cjs/transports/blockchair/__mocks__/getTransactionFee/invalid.json +7 -0
  31. package/dist/cjs/transports/blockchair/__mocks__/getTransactionFee/valid.json +37 -0
  32. package/dist/cjs/transports/blockchair/__mocks__/getUTXOs/rateLimited.json +28 -0
  33. package/dist/cjs/transports/blockchair/blockchair.types.d.ts +61 -0
  34. package/dist/cjs/transports/blockchair/getTransactionFee.d.ts +2 -0
  35. package/dist/cjs/transports/blockchair/getTransactionFee.js +28 -0
  36. package/dist/cjs/transports/blockchair/getTransactionFee.js.map +1 -0
  37. package/dist/cjs/transports/blockchair/methods.js +2 -0
  38. package/dist/cjs/transports/blockchair/methods.js.map +1 -1
  39. package/dist/cjs/transports/blockcypher/__mocks__/getTransactionFee/invalid.json +3 -0
  40. package/dist/cjs/transports/blockcypher/__mocks__/getTransactionFee/valid.json +22 -0
  41. package/dist/cjs/transports/blockcypher/__mocks__/getUTXOs/rateLimited.json +3 -0
  42. package/dist/cjs/transports/blockcypher/blockcypher.types.d.ts +41 -0
  43. package/dist/cjs/transports/blockcypher/getTransactionFee.d.ts +2 -0
  44. package/dist/cjs/transports/blockcypher/getTransactionFee.js +23 -0
  45. package/dist/cjs/transports/blockcypher/getTransactionFee.js.map +1 -0
  46. package/dist/cjs/transports/blockcypher/methods.js +2 -0
  47. package/dist/cjs/transports/blockcypher/methods.js.map +1 -1
  48. package/dist/cjs/transports/fallback.js +16 -3
  49. package/dist/cjs/transports/fallback.js.map +1 -1
  50. package/dist/cjs/transports/mempool/__mocks__/getTransactionFee/invalid.json +1 -0
  51. package/dist/cjs/transports/mempool/__mocks__/getTransactionFee/valid.json +58 -0
  52. package/dist/cjs/transports/mempool/getTransactionFee.d.ts +2 -0
  53. package/dist/cjs/transports/mempool/getTransactionFee.js +15 -0
  54. package/dist/cjs/transports/mempool/getTransactionFee.js.map +1 -0
  55. package/dist/cjs/transports/mempool/getTransactions.js +2 -26
  56. package/dist/cjs/transports/mempool/getTransactions.js.map +1 -1
  57. package/dist/cjs/transports/mempool/methods.js +2 -0
  58. package/dist/cjs/transports/mempool/methods.js.map +1 -1
  59. package/dist/cjs/transports/mempool/utils.d.ts +3 -0
  60. package/dist/cjs/transports/mempool/utils.js +30 -0
  61. package/dist/cjs/transports/mempool/utils.js.map +1 -0
  62. package/dist/cjs/transports/types.d.ts +7 -0
  63. package/dist/cjs/transports/types.js +1 -0
  64. package/dist/cjs/transports/types.js.map +1 -1
  65. package/dist/cjs/types/account.d.ts +3 -3
  66. package/dist/cjs/types/address.d.ts +20 -0
  67. package/dist/cjs/types/address.js +15 -0
  68. package/dist/cjs/types/address.js.map +1 -1
  69. package/dist/cjs/utils/converter.d.ts +1 -0
  70. package/dist/cjs/utils/converter.js +4 -0
  71. package/dist/cjs/utils/converter.js.map +1 -1
  72. package/dist/cjs/utils/getAddressInfo.d.ts +2 -0
  73. package/dist/cjs/utils/{getUTXOAddress.js → getAddressInfo.js} +36 -32
  74. package/dist/cjs/utils/getAddressInfo.js.map +1 -0
  75. package/dist/cjs/utils/isAddress.d.ts +2 -0
  76. package/dist/cjs/utils/isAddress.js +18 -0
  77. package/dist/cjs/utils/isAddress.js.map +1 -0
  78. package/dist/cjs/version.d.ts +1 -1
  79. package/dist/cjs/version.js +1 -1
  80. package/dist/cjs/version.js.map +1 -1
  81. package/dist/esm/actions/getTransactionFee.d.ts +10 -0
  82. package/dist/esm/actions/getTransactionFee.js +8 -0
  83. package/dist/esm/actions/getTransactionFee.js.map +1 -0
  84. package/dist/esm/errors/transport.d.ts +23 -0
  85. package/dist/esm/errors/transport.js +19 -0
  86. package/dist/esm/errors/transport.js.map +1 -1
  87. package/dist/esm/index.d.ts +8 -6
  88. package/dist/esm/index.js +6 -4
  89. package/dist/esm/index.js.map +1 -1
  90. package/dist/esm/test/utils.d.ts +1 -1
  91. package/dist/esm/test/utils.js +2 -1
  92. package/dist/esm/test/utils.js.map +1 -1
  93. package/dist/esm/transports/__mocks__/getTransactionFee.d.ts +3 -0
  94. package/dist/esm/transports/__mocks__/getTransactionFee.js +4 -0
  95. package/dist/esm/transports/__mocks__/getTransactionFee.js.map +1 -0
  96. package/dist/esm/transports/ankr/__mocks__/getTransactionFee/invalid.json +3 -0
  97. package/dist/esm/transports/ankr/__mocks__/getTransactionFee/valid.json +51 -0
  98. package/dist/esm/transports/ankr/ankr.types.d.ts +4 -0
  99. package/dist/esm/transports/ankr/getTransactionFee.d.ts +2 -0
  100. package/dist/esm/transports/ankr/getTransactionFee.js +14 -0
  101. package/dist/esm/transports/ankr/getTransactionFee.js.map +1 -0
  102. package/dist/esm/transports/ankr/getTransactions.d.ts +0 -3
  103. package/dist/esm/transports/ankr/getTransactions.js +1 -24
  104. package/dist/esm/transports/ankr/getTransactions.js.map +1 -1
  105. package/dist/esm/transports/ankr/methods.js +2 -0
  106. package/dist/esm/transports/ankr/methods.js.map +1 -1
  107. package/dist/esm/transports/ankr/utils.d.ts +3 -0
  108. package/dist/esm/transports/ankr/utils.js +25 -0
  109. package/dist/esm/transports/ankr/utils.js.map +1 -0
  110. package/dist/esm/transports/blockchair/__mocks__/getTransactionFee/invalid.json +7 -0
  111. package/dist/esm/transports/blockchair/__mocks__/getTransactionFee/valid.json +37 -0
  112. package/dist/esm/transports/blockchair/__mocks__/getUTXOs/rateLimited.json +28 -0
  113. package/dist/esm/transports/blockchair/blockchair.types.d.ts +61 -0
  114. package/dist/esm/transports/blockchair/getTransactionFee.d.ts +2 -0
  115. package/dist/esm/transports/blockchair/getTransactionFee.js +24 -0
  116. package/dist/esm/transports/blockchair/getTransactionFee.js.map +1 -0
  117. package/dist/esm/transports/blockchair/methods.js +2 -0
  118. package/dist/esm/transports/blockchair/methods.js.map +1 -1
  119. package/dist/esm/transports/blockcypher/__mocks__/getTransactionFee/invalid.json +3 -0
  120. package/dist/esm/transports/blockcypher/__mocks__/getTransactionFee/valid.json +22 -0
  121. package/dist/esm/transports/blockcypher/__mocks__/getUTXOs/rateLimited.json +3 -0
  122. package/dist/esm/transports/blockcypher/blockcypher.types.d.ts +41 -0
  123. package/dist/esm/transports/blockcypher/getTransactionFee.d.ts +2 -0
  124. package/dist/esm/transports/blockcypher/getTransactionFee.js +19 -0
  125. package/dist/esm/transports/blockcypher/getTransactionFee.js.map +1 -0
  126. package/dist/esm/transports/blockcypher/methods.js +2 -0
  127. package/dist/esm/transports/blockcypher/methods.js.map +1 -1
  128. package/dist/esm/transports/fallback.js +19 -5
  129. package/dist/esm/transports/fallback.js.map +1 -1
  130. package/dist/esm/transports/mempool/__mocks__/getTransactionFee/invalid.json +1 -0
  131. package/dist/esm/transports/mempool/__mocks__/getTransactionFee/valid.json +58 -0
  132. package/dist/esm/transports/mempool/getTransactionFee.d.ts +2 -0
  133. package/dist/esm/transports/mempool/getTransactionFee.js +11 -0
  134. package/dist/esm/transports/mempool/getTransactionFee.js.map +1 -0
  135. package/dist/esm/transports/mempool/getTransactions.js +1 -25
  136. package/dist/esm/transports/mempool/getTransactions.js.map +1 -1
  137. package/dist/esm/transports/mempool/methods.js +2 -0
  138. package/dist/esm/transports/mempool/methods.js.map +1 -1
  139. package/dist/esm/transports/mempool/utils.d.ts +3 -0
  140. package/dist/esm/transports/mempool/utils.js +26 -0
  141. package/dist/esm/transports/mempool/utils.js.map +1 -0
  142. package/dist/esm/transports/types.d.ts +7 -0
  143. package/dist/esm/transports/types.js +1 -0
  144. package/dist/esm/transports/types.js.map +1 -1
  145. package/dist/esm/types/account.d.ts +3 -3
  146. package/dist/esm/types/address.d.ts +20 -0
  147. package/dist/esm/types/address.js +14 -1
  148. package/dist/esm/types/address.js.map +1 -1
  149. package/dist/esm/utils/converter.d.ts +1 -0
  150. package/dist/esm/utils/converter.js +3 -0
  151. package/dist/esm/utils/converter.js.map +1 -1
  152. package/dist/esm/utils/getAddressInfo.d.ts +2 -0
  153. package/dist/esm/utils/{getUTXOAddress.js → getAddressInfo.js} +34 -30
  154. package/dist/esm/utils/getAddressInfo.js.map +1 -0
  155. package/dist/esm/utils/isAddress.d.ts +2 -0
  156. package/dist/esm/utils/isAddress.js +14 -0
  157. package/dist/esm/utils/isAddress.js.map +1 -0
  158. package/dist/esm/version.d.ts +1 -1
  159. package/dist/esm/version.js +1 -1
  160. package/dist/esm/version.js.map +1 -1
  161. package/dist/types/actions/getTransactionFee.d.ts +11 -0
  162. package/dist/types/actions/getTransactionFee.d.ts.map +1 -0
  163. package/dist/types/errors/transport.d.ts +23 -0
  164. package/dist/types/errors/transport.d.ts.map +1 -1
  165. package/dist/types/index.d.ts +8 -6
  166. package/dist/types/index.d.ts.map +1 -1
  167. package/dist/types/test/utils.d.ts +1 -1
  168. package/dist/types/test/utils.d.ts.map +1 -1
  169. package/dist/types/transports/__mocks__/getTransactionFee.d.ts +4 -0
  170. package/dist/types/transports/__mocks__/getTransactionFee.d.ts.map +1 -0
  171. package/dist/types/transports/ankr/ankr.types.d.ts +4 -0
  172. package/dist/types/transports/ankr/ankr.types.d.ts.map +1 -1
  173. package/dist/types/transports/ankr/getTransactionFee.d.ts +3 -0
  174. package/dist/types/transports/ankr/getTransactionFee.d.ts.map +1 -0
  175. package/dist/types/transports/ankr/getTransactions.d.ts +0 -3
  176. package/dist/types/transports/ankr/getTransactions.d.ts.map +1 -1
  177. package/dist/types/transports/ankr/methods.d.ts.map +1 -1
  178. package/dist/types/transports/ankr/utils.d.ts +4 -0
  179. package/dist/types/transports/ankr/utils.d.ts.map +1 -0
  180. package/dist/types/transports/blockchair/blockchair.types.d.ts +61 -0
  181. package/dist/types/transports/blockchair/blockchair.types.d.ts.map +1 -1
  182. package/dist/types/transports/blockchair/getTransactionFee.d.ts +3 -0
  183. package/dist/types/transports/blockchair/getTransactionFee.d.ts.map +1 -0
  184. package/dist/types/transports/blockchair/methods.d.ts.map +1 -1
  185. package/dist/types/transports/blockcypher/blockcypher.types.d.ts +41 -0
  186. package/dist/types/transports/blockcypher/blockcypher.types.d.ts.map +1 -1
  187. package/dist/types/transports/blockcypher/getTransactionFee.d.ts +3 -0
  188. package/dist/types/transports/blockcypher/getTransactionFee.d.ts.map +1 -0
  189. package/dist/types/transports/blockcypher/methods.d.ts.map +1 -1
  190. package/dist/types/transports/fallback.d.ts.map +1 -1
  191. package/dist/types/transports/mempool/getTransactionFee.d.ts +3 -0
  192. package/dist/types/transports/mempool/getTransactionFee.d.ts.map +1 -0
  193. package/dist/types/transports/mempool/getTransactions.d.ts.map +1 -1
  194. package/dist/types/transports/mempool/methods.d.ts.map +1 -1
  195. package/dist/types/transports/mempool/utils.d.ts +4 -0
  196. package/dist/types/transports/mempool/utils.d.ts.map +1 -0
  197. package/dist/types/transports/types.d.ts +7 -0
  198. package/dist/types/transports/types.d.ts.map +1 -1
  199. package/dist/types/types/account.d.ts +3 -3
  200. package/dist/types/types/account.d.ts.map +1 -1
  201. package/dist/types/types/address.d.ts +20 -0
  202. package/dist/types/types/address.d.ts.map +1 -1
  203. package/dist/types/utils/converter.d.ts +1 -0
  204. package/dist/types/utils/converter.d.ts.map +1 -1
  205. package/dist/types/utils/getAddressInfo.d.ts +3 -0
  206. package/dist/types/utils/getAddressInfo.d.ts.map +1 -0
  207. package/dist/types/utils/isAddress.d.ts +3 -0
  208. package/dist/types/utils/isAddress.d.ts.map +1 -0
  209. package/dist/types/version.d.ts +1 -1
  210. package/dist/types/version.d.ts.map +1 -1
  211. package/package.json +1 -1
  212. package/package.json.tmp +3 -3
  213. package/src/actions/getTransactionFee.ts +26 -0
  214. package/src/errors/transport.ts +54 -0
  215. package/src/index.ts +25 -9
  216. package/src/test/utils.ts +5 -1
  217. package/src/transports/__mocks__/getTransactionFee.ts +4 -0
  218. package/src/transports/ankr/__mocks__/getTransactionFee/invalid.json +3 -0
  219. package/src/transports/ankr/__mocks__/getTransactionFee/valid.json +51 -0
  220. package/src/transports/ankr/ankr.spec.ts +45 -1
  221. package/src/transports/ankr/ankr.types.ts +5 -0
  222. package/src/transports/ankr/getTransactionFee.ts +21 -0
  223. package/src/transports/ankr/getTransactions.ts +1 -30
  224. package/src/transports/ankr/methods.ts +2 -0
  225. package/src/transports/ankr/utils.ts +29 -0
  226. package/src/transports/blockchair/__mocks__/getTransactionFee/invalid.json +7 -0
  227. package/src/transports/blockchair/__mocks__/getTransactionFee/valid.json +37 -0
  228. package/src/transports/blockchair/__mocks__/getUTXOs/rateLimited.json +28 -0
  229. package/src/transports/blockchair/blockchair.spec.ts +49 -0
  230. package/src/transports/blockchair/blockchair.types.ts +67 -0
  231. package/src/transports/blockchair/getTransactionFee.ts +32 -0
  232. package/src/transports/blockchair/methods.ts +2 -0
  233. package/src/transports/blockcypher/__mocks__/getTransactionFee/invalid.json +3 -0
  234. package/src/transports/blockcypher/__mocks__/getTransactionFee/valid.json +22 -0
  235. package/src/transports/blockcypher/__mocks__/getUTXOs/rateLimited.json +3 -0
  236. package/src/transports/blockcypher/blockcyper.spec.ts +43 -4
  237. package/src/transports/blockcypher/blockcypher.types.ts +45 -0
  238. package/src/transports/blockcypher/getTransactionFee.ts +28 -0
  239. package/src/transports/blockcypher/methods.ts +2 -0
  240. package/src/transports/fallback.spec.ts +103 -0
  241. package/src/transports/fallback.ts +29 -5
  242. package/src/transports/mempool/__mocks__/getTransactionFee/invalid.json +1 -0
  243. package/src/transports/mempool/__mocks__/getTransactionFee/valid.json +58 -0
  244. package/src/transports/mempool/getTransactionFee.ts +19 -0
  245. package/src/transports/mempool/getTransactions.ts +1 -30
  246. package/src/transports/mempool/mempool.spec.ts +41 -0
  247. package/src/transports/mempool/methods.ts +2 -0
  248. package/src/transports/mempool/utils.ts +30 -0
  249. package/src/transports/types.ts +8 -0
  250. package/src/types/account.ts +3 -3
  251. package/src/types/address.ts +24 -0
  252. package/src/utils/converter.ts +6 -0
  253. package/src/utils/{getUTXOAddress.ts → getAddressInfo.ts} +46 -41
  254. package/src/utils/isAddress.ts +16 -0
  255. package/src/version.ts +1 -1
  256. package/dist/cjs/utils/getUTXOAddress.d.ts +0 -20
  257. package/dist/cjs/utils/getUTXOAddress.js.map +0 -1
  258. package/dist/cjs/utils/isUTXOAddress.d.ts +0 -2
  259. package/dist/cjs/utils/isUTXOAddress.js +0 -18
  260. package/dist/cjs/utils/isUTXOAddress.js.map +0 -1
  261. package/dist/esm/utils/getUTXOAddress.d.ts +0 -20
  262. package/dist/esm/utils/getUTXOAddress.js.map +0 -1
  263. package/dist/esm/utils/isUTXOAddress.d.ts +0 -2
  264. package/dist/esm/utils/isUTXOAddress.js +0 -14
  265. package/dist/esm/utils/isUTXOAddress.js.map +0 -1
  266. package/dist/types/utils/getUTXOAddress.d.ts +0 -21
  267. package/dist/types/utils/getUTXOAddress.d.ts.map +0 -1
  268. package/dist/types/utils/isUTXOAddress.d.ts +0 -3
  269. package/dist/types/utils/isUTXOAddress.d.ts.map +0 -1
  270. package/src/utils/isUTXOAddress.ts +0 -19
@@ -0,0 +1,28 @@
1
+ {
2
+ "data": null,
3
+ "context": {
4
+ "code": 429,
5
+ "optimized": false,
6
+ "error": "Rate limit exceeded",
7
+ "market_price_usd": 102926,
8
+ "cache": {
9
+ "live": true,
10
+ "duration": 120,
11
+ "since": "2025-05-19 12:22:44",
12
+ "until": "2025-05-19 12:24:44",
13
+ "time": null
14
+ },
15
+ "api": {
16
+ "version": "2.0.95-ie",
17
+ "last_major_update": "2022-11-07 02:00:00",
18
+ "next_major_update": "2023-11-12 02:00:00",
19
+ "documentation": "https://blockchair.com/api/docs",
20
+ "notice": "Try out our new API v.3: https://3xpl.com/data"
21
+ },
22
+ "servers": "API4,BTC5",
23
+ "time": 1.0055019855499268,
24
+ "render_time": 0.01248788833618164,
25
+ "full_time": 1.0179898738861084,
26
+ "request_cost": 0
27
+ }
28
+ }
@@ -1,14 +1,22 @@
1
1
  import { beforeEach, describe, expect, it, vi } from 'vitest'
2
2
  import { getBalance } from '../../actions/getBalance'
3
+ import { getTransactionFee } from '../../actions/getTransactionFee'
3
4
  import { getUTXOs } from '../../actions/getUTXOs'
4
5
  import { bitcoin } from '../../chains/bitcoin'
5
6
  import { BaseError } from '../../errors/base'
6
7
  import { InsufficientUTXOBalanceError } from '../../errors/utxo'
7
8
  import { createClient, rpcSchema } from '../../factories/createClient'
8
9
  import { createMockResponse } from '../../test/utils'
10
+ import {
11
+ INVALID_TX_ID,
12
+ TX_FEE,
13
+ VALID_TX_ID,
14
+ } from '../__mocks__/getTransactionFee'
9
15
  import type { UTXOSchema } from '../types'
10
16
  import getInvalidBalanceReponse from './__mocks__/getBalance/invalidAddress.json'
11
17
  import getBalanceReponse from './__mocks__/getBalance/valid.json'
18
+ import getTransactionFeeInvalidResponse from './__mocks__/getTransactionFee/invalid.json'
19
+ import getTransactionFeeValidResponse from './__mocks__/getTransactionFee/valid.json'
12
20
  import getUTXOsInvalidResponse from './__mocks__/getUTXOs/invalidAddress.json'
13
21
  import getUTXOsResponse from './__mocks__/getUTXOs/valid.json'
14
22
  import getUTXOsPaginatedResponse from './__mocks__/getUTXOs/validPaginated.json'
@@ -22,6 +30,8 @@ import type {
22
30
  const address = import.meta.env.VITE_TEST_ADDRESS
23
31
  const apiKey = import.meta.env.VITE_TEST_BLOCKCHAIR_KEY
24
32
 
33
+ const USE_MOCK = true
34
+
25
35
  const publicClient = createClient({
26
36
  chain: bitcoin,
27
37
  rpcSchema: rpcSchema<UTXOSchema>(),
@@ -191,4 +201,43 @@ describe('Blockchair Transport', () => {
191
201
  })
192
202
  })
193
203
  })
204
+
205
+ describe('getTransactionFee', () => {
206
+ it('should fetch correct transaction fee for valid transaction', async () => {
207
+ if (USE_MOCK) {
208
+ const mockData = {
209
+ data: {
210
+ [VALID_TX_ID]:
211
+ getTransactionFeeValidResponse.data[
212
+ '8f210660cd99c5e6dc77b6cb09d4d522d3fbc5fd97ad3e65403d49aa7aa5dc23'
213
+ ],
214
+ },
215
+ context: getTransactionFeeValidResponse.context,
216
+ }
217
+
218
+ mockData.data[VALID_TX_ID].transaction.fee = TX_FEE
219
+ vi.spyOn(global, 'fetch').mockResolvedValue(
220
+ createMockResponse(mockData)
221
+ )
222
+ }
223
+
224
+ const result = await getTransactionFee(publicClient, {
225
+ txId: VALID_TX_ID,
226
+ })
227
+ expect(result).toBeDefined()
228
+ expect(result).toBe(BigInt(TX_FEE))
229
+ })
230
+
231
+ it('should throw error for non-existent transaction', async () => {
232
+ if (USE_MOCK) {
233
+ vi.spyOn(global, 'fetch').mockResolvedValue(
234
+ createMockResponse(getTransactionFeeInvalidResponse)
235
+ )
236
+ }
237
+
238
+ await expect(
239
+ getTransactionFee(publicClient, { txId: INVALID_TX_ID })
240
+ ).rejects.toThrow()
241
+ })
242
+ })
194
243
  })
@@ -57,3 +57,70 @@ export type BlockchairResponse<T = any> = {
57
57
  total_rows?: number
58
58
  }
59
59
  }
60
+
61
+ export type BlockchairTransactionResponse = BlockchairResponse<
62
+ Record<string, BlockchairTransaction>
63
+ >
64
+
65
+ export type BlockchairTransaction = {
66
+ transaction: {
67
+ block_id: number
68
+ id: number
69
+ hash: string
70
+ date: string
71
+ time: string
72
+ size: number
73
+ weight: number
74
+ version: number
75
+ lock_time: number
76
+ is_coinbase: boolean
77
+ has_witness: boolean
78
+ input_count: number
79
+ output_count: number
80
+ input_total: number
81
+ input_total_usd: number
82
+ output_total: number
83
+ output_total_usd: number
84
+ fee: number
85
+ fee_usd: number
86
+ fee_per_kb: number
87
+ fee_per_kb_usd: number
88
+ fee_per_kwu: number
89
+ fee_per_kwu_usd: number
90
+ cdd_total: number
91
+ is_rbf: boolean
92
+ }
93
+ inputs: Array<BlockchairVin>
94
+ outputs: Array<BlockchairVout>
95
+ }
96
+
97
+ export type BlockchairVin = {
98
+ block_id: number
99
+ transaction_id: number
100
+ index: number
101
+ transaction_hash: string
102
+ date: string
103
+ time: string
104
+ value: number
105
+ value_usd: number
106
+ recipient: string
107
+ type: string
108
+ script_hex: string
109
+ is_from_coinbase: boolean
110
+ is_spendable: boolean | null
111
+ is_spent: boolean
112
+ spending_block_id: number | null
113
+ spending_transaction_id: number | null
114
+ spending_index: number | null
115
+ spending_transaction_hash: string | null
116
+ spending_date: string | null
117
+ spending_time: string | null
118
+ spending_value_usd: number | null
119
+ spending_sequence: number | null
120
+ spending_signature_hex: string | null
121
+ spending_witness: string | null
122
+ lifespan: number | null
123
+ cdd: number | null
124
+ }
125
+
126
+ export type BlockchairVout = BlockchairVin
@@ -0,0 +1,32 @@
1
+ import { urlWithParams } from '../../utils/url.js'
2
+ import type { RpcMethodHandler } from '../types.js'
3
+ import type { BlockchairTransactionResponse } from './blockchair.types.js'
4
+
5
+ export const getTransactionFee: RpcMethodHandler<'getTransactionFee'> = async (
6
+ client,
7
+ { baseUrl, apiKey },
8
+ { txId }
9
+ ) => {
10
+ const apiUrl = urlWithParams(`${baseUrl}/dashboards/transaction/${txId}`, {
11
+ key: apiKey,
12
+ })
13
+ const response = (await client.request({
14
+ url: apiUrl,
15
+ fetchOptions: {
16
+ method: 'GET',
17
+ },
18
+ })) as unknown as BlockchairTransactionResponse
19
+
20
+ if (response.context?.code !== 200) {
21
+ return {
22
+ error: {
23
+ code: response.context?.code,
24
+ message: response.context?.error,
25
+ },
26
+ }
27
+ }
28
+
29
+ return {
30
+ result: BigInt(response.data[txId].transaction.fee),
31
+ }
32
+ }
@@ -1,8 +1,10 @@
1
1
  import type { RpcMethods } from '../types.js'
2
2
  import { getBalance } from './getBalance.js'
3
+ import { getTransactionFee } from './getTransactionFee.js'
3
4
  import { getUTXOs } from './getUTXOs.js'
4
5
 
5
6
  export const blockchairMethods: RpcMethods = {
6
7
  getBalance,
7
8
  getUTXOs,
9
+ getTransactionFee,
8
10
  }
@@ -0,0 +1,3 @@
1
+ {
2
+ "error": "Transaction not found"
3
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "block_height": 896390,
3
+ "hash": "8f210660cd99c5e6dc77b6cb09d4d522d3fbc5fd97ad3e65403d49aa7aa5dc23",
4
+ "addresses": [
5
+ "bc1qpvk0kjdg0kantu9z78qz7gqk53q643auz33yaw",
6
+ "bc1qjmggdm4r9rqkfq335w72duu47386jhdw6wye0m"
7
+ ],
8
+ "total": 10269001908,
9
+ "fees": 1036,
10
+ "size": 450,
11
+ "vsize": 288,
12
+ "preference": "medium",
13
+ "relayed_by": "127.0.0.1",
14
+ "received": "2024-03-15T12:00:00Z",
15
+ "ver": 1,
16
+ "double_spend": false,
17
+ "vin_sz": 2,
18
+ "vout_sz": 3,
19
+ "confirmations": 1040,
20
+ "inputs": [],
21
+ "outputs": []
22
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "error": "Rate limit exceeded"
3
+ }
@@ -1,14 +1,21 @@
1
- import { beforeEach } from 'node:test'
2
- import { describe, expect, it, vi } from 'vitest'
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest'
3
2
  import { getBalance } from '../../actions/getBalance.js'
3
+ import { getTransactionFee } from '../../actions/getTransactionFee.js'
4
4
  import { getUTXOs } from '../../actions/getUTXOs.js'
5
5
  import { bitcoin } from '../../chains/bitcoin.js'
6
- import { NotEnoughUTXOError } from '../../errors/address.js'
6
+ import { InsufficientUTXOBalanceError } from '../../errors/utxo.js'
7
7
  import { createClient, rpcSchema } from '../../factories/createClient.js'
8
8
  import { createMockResponse } from '../../test/utils.js'
9
+ import {
10
+ INVALID_TX_ID,
11
+ TX_FEE,
12
+ VALID_TX_ID,
13
+ } from '../__mocks__/getTransactionFee'
9
14
  import type { UTXOSchema } from '../types.js'
10
15
  import getBalanceInValidResponse from './__mocks__/getBalance/invalid.json'
11
16
  import getBalanceValidResponse from './__mocks__/getBalance/valid.json'
17
+ import getTransactionFeeInvalidResponse from './__mocks__/getTransactionFee/invalid.json'
18
+ import getTransactionFeeValidResponse from './__mocks__/getTransactionFee/valid.json'
12
19
  import getUTXOsEmptyReponse from './__mocks__/getUTXOs/empty.json'
13
20
  import getUTXOsPaginatedReponse from './__mocks__/getUTXOs/paginated.json'
14
21
  import getUTXOsValidReponse from './__mocks__/getUTXOs/valid.json'
@@ -21,6 +28,8 @@ import type {
21
28
 
22
29
  const apiKey = import.meta.env.VITE_TEST_BLOCKCYPHER_API_KEY
23
30
 
31
+ const USE_MOCK = true
32
+
24
33
  const publicClient = createClient({
25
34
  chain: bitcoin,
26
35
  rpcSchema: rpcSchema<UTXOSchema>(),
@@ -88,7 +97,7 @@ describe('Blockcypher Transport', () => {
88
97
  address,
89
98
  minValue: Number(hugeValue),
90
99
  })
91
- ).rejects.toThrow(NotEnoughUTXOError)
100
+ ).rejects.toThrow(InsufficientUTXOBalanceError)
92
101
  })
93
102
 
94
103
  it('should handle empty UTXO', async () => {
@@ -119,4 +128,34 @@ describe('Blockcypher Transport', () => {
119
128
  expect(totalValue).toBeGreaterThanOrEqual(minValue)
120
129
  })
121
130
  })
131
+
132
+ describe('getTransactionFee', () => {
133
+ it('should fetch correct transaction fee for valid transaction', async () => {
134
+ if (USE_MOCK) {
135
+ getTransactionFeeValidResponse.hash = VALID_TX_ID
136
+ getTransactionFeeValidResponse.fees = TX_FEE
137
+ vi.spyOn(global, 'fetch').mockResolvedValue(
138
+ createMockResponse(getTransactionFeeValidResponse)
139
+ )
140
+ }
141
+
142
+ const result = await getTransactionFee(publicClient, {
143
+ txId: VALID_TX_ID,
144
+ })
145
+ expect(result).toBeDefined()
146
+ expect(result).toBe(BigInt(TX_FEE))
147
+ })
148
+
149
+ it('should throw error for non-existent transaction', async () => {
150
+ if (USE_MOCK) {
151
+ vi.spyOn(global, 'fetch').mockResolvedValue(
152
+ createMockResponse(getTransactionFeeInvalidResponse)
153
+ )
154
+ }
155
+
156
+ await expect(
157
+ getTransactionFee(publicClient, { txId: INVALID_TX_ID })
158
+ ).rejects.toThrow()
159
+ })
160
+ })
122
161
  })
@@ -33,3 +33,48 @@ export type BlockcypherUTXO = {
33
33
  export type BlockcypherUTXOsResponse = BlockcypherBalanceResponse & {
34
34
  txrefs: BlockcypherUTXO[]
35
35
  }
36
+
37
+ export type BlockcypherTransactionInput = {
38
+ prev_hash: string
39
+ output_index: number
40
+ script: string
41
+ output_value: number
42
+ sequence: number
43
+ addresses: string[]
44
+ script_type: string
45
+ }
46
+
47
+ export type BlockcypherTransactionOutput = {
48
+ value: number
49
+ script: string
50
+ spent_by: string
51
+ addresses: string[]
52
+ script_type: string
53
+ }
54
+
55
+ export type BlockcypherTransaction = {
56
+ block_hash: string
57
+ block_height: number
58
+ hash: string
59
+ addresses: string[]
60
+ total: number
61
+ fees: number
62
+ size: number
63
+ vsize: number
64
+ preference: string
65
+ relayed_by: string
66
+ confirmed: string
67
+ received: string
68
+ ver: number
69
+ lock_time: number
70
+ double_spend: boolean
71
+ vin_sz: number
72
+ vout_sz: number
73
+ confirmations: number
74
+ inputs: BlockcypherTransactionInput[]
75
+ outputs: BlockcypherTransactionOutput[]
76
+ }
77
+
78
+ export type BlockcypherTransactionReponse = BlockcypherTransaction & {
79
+ error?: string
80
+ }
@@ -0,0 +1,28 @@
1
+ import { urlWithParams } from '../../utils/url.js'
2
+ import type { RpcMethodHandler } from '../types.js'
3
+ import type { BlockcypherTransactionReponse } from './blockcypher.types.js'
4
+
5
+ export const getTransactionFee: RpcMethodHandler<'getTransactionFee'> = async (
6
+ client,
7
+ { baseUrl, apiKey },
8
+ { txId }
9
+ ) => {
10
+ const apiUrl = urlWithParams(`${baseUrl}/txs/${txId}`, {
11
+ token: apiKey,
12
+ })
13
+
14
+ const response = (await client.request({
15
+ url: apiUrl,
16
+ fetchOptions: { method: 'GET' },
17
+ })) as unknown as BlockcypherTransactionReponse
18
+
19
+ if (response.error) {
20
+ return {
21
+ error: { code: -1, message: response.error },
22
+ }
23
+ }
24
+
25
+ return {
26
+ result: BigInt(response.fees),
27
+ }
28
+ }
@@ -1,8 +1,10 @@
1
1
  import type { RpcMethods } from '../types.js'
2
2
  import { getBalance } from './getBalance.js'
3
+ import { getTransactionFee } from './getTransactionFee.js'
3
4
  import { getUTXOs } from './getUTXOs.js'
4
5
 
5
6
  export const blockcypherMethods: RpcMethods = {
6
7
  getBalance,
7
8
  getUTXOs,
9
+ getTransactionFee,
8
10
  }
@@ -0,0 +1,103 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest'
2
+ import { getUTXOs } from '../actions/getUTXOs'
3
+ import { bitcoin } from '../chains/bitcoin'
4
+ import { AllTransportsFailedError } from '../errors/transport'
5
+ import { InsufficientUTXOBalanceError } from '../errors/utxo'
6
+ import { createClient, rpcSchema } from '../factories/createClient'
7
+ import { createMockResponse } from '../test/utils'
8
+ import blockchairLimitedResponse from './blockchair/__mocks__/getUTXOs/rateLimited.json'
9
+ import blockchairValidResponse from './blockchair/__mocks__/getUTXOs/valid.json'
10
+ import { blockchair } from './blockchair/blockchair'
11
+ import blockcypherLimitedResponse from './blockcypher/__mocks__/getUTXOs/rateLimited.json'
12
+ import blockcypherValidResponse from './blockcypher/__mocks__/getUTXOs/valid.json'
13
+ import { blockcypher } from './blockcypher/blockcypher'
14
+ import { fallback } from './fallback'
15
+ import type { UTXOSchema } from './types'
16
+
17
+ const address = import.meta.env.VITE_TEST_ADDRESS
18
+ const apiKey = import.meta.env.VITE_TEST_BLOCKCHAIR_KEY
19
+ const blockCypherKey = import.meta.env.VITE_TEST_BLOCKCYPHER_API_KEY
20
+
21
+ const publicClient = createClient({
22
+ chain: bitcoin,
23
+ rpcSchema: rpcSchema<UTXOSchema>(),
24
+ transport: fallback([
25
+ blockcypher({ apiKey: blockCypherKey }),
26
+ blockchair({ apiKey }),
27
+ ]),
28
+ })
29
+
30
+ describe('Fallback Transport', () => {
31
+ beforeEach(() => {
32
+ vi.restoreAllMocks()
33
+ })
34
+
35
+ describe('getUTXOs', () => {
36
+ it('should use blockcypher when it succeeds', async () => {
37
+ vi.spyOn(global, 'fetch').mockResolvedValue(
38
+ createMockResponse(blockcypherValidResponse)
39
+ )
40
+
41
+ const utxos = await getUTXOs(publicClient, { address })
42
+ expect(utxos.length).toBeGreaterThan(0)
43
+ })
44
+
45
+ it('should fallback to blockchair when blockcypher fails', async () => {
46
+ vi.spyOn(global, 'fetch').mockImplementation((request) => {
47
+ const url = new URL(request.toString())
48
+ if (url.hostname.includes('blockcypher')) {
49
+ return Promise.resolve(
50
+ createMockResponse(blockcypherLimitedResponse, { status: 429 })
51
+ )
52
+ }
53
+
54
+ return Promise.resolve(createMockResponse(blockchairValidResponse))
55
+ })
56
+
57
+ const utxos = await getUTXOs(publicClient, { address })
58
+ expect(utxos.length).toBeGreaterThan(0)
59
+ })
60
+
61
+ it('should throw error when both transports fail', async () => {
62
+ vi.spyOn(global, 'fetch').mockImplementation((request) => {
63
+ const url = new URL(request.toString())
64
+ if (url.hostname.includes('blockcypher')) {
65
+ return Promise.resolve(
66
+ createMockResponse(blockcypherLimitedResponse, { status: 429 })
67
+ )
68
+ }
69
+ return Promise.resolve(
70
+ createMockResponse(blockchairLimitedResponse, { status: 429 })
71
+ )
72
+ })
73
+
74
+ await expect(getUTXOs(publicClient, { address })).rejects.toThrow(
75
+ AllTransportsFailedError
76
+ )
77
+ })
78
+
79
+ it('should not call blockchair if user has insufficientUTXOs', async () => {
80
+ const blockchairSpy = vi
81
+ .spyOn(global, 'fetch')
82
+ .mockImplementation((request) => {
83
+ const url = new URL(request.toString())
84
+ if (url.hostname.includes('blockcypher')) {
85
+ return Promise.resolve(createMockResponse(blockcypherValidResponse))
86
+ }
87
+
88
+ return Promise.resolve(createMockResponse(blockchairValidResponse))
89
+ })
90
+
91
+ await expect(
92
+ getUTXOs(publicClient, {
93
+ address,
94
+ minValue: 1000000,
95
+ })
96
+ ).rejects.toThrow(InsufficientUTXOBalanceError)
97
+
98
+ expect(blockchairSpy).not.toHaveBeenCalledWith(
99
+ expect.stringContaining('blockchair')
100
+ )
101
+ })
102
+ })
103
+ })
@@ -1,5 +1,8 @@
1
1
  import { RpcErrorCode } from '../errors/rpc.js'
2
- import { TransportMethodNotSupportedError } from '../errors/transport.js'
2
+ import {
3
+ AllTransportsFailedError,
4
+ TransportMethodNotSupportedError,
5
+ } from '../errors/transport.js'
3
6
  import type { ErrorType } from '../errors/utils.js'
4
7
  import { InsufficientUTXOBalanceError } from '../errors/utxo.js'
5
8
  import { createTransport } from '../factories/createTransport.js'
@@ -154,6 +157,12 @@ export function fallback<const transports extends readonly Transport[]>(
154
157
  throw new TransportMethodNotSupportedError({ method })
155
158
  }
156
159
 
160
+ const collectedErrors: Array<{
161
+ transport: string
162
+ error: Error
163
+ attempt: number
164
+ }> = []
165
+
157
166
  const fetch = async (i = 0): Promise<any> => {
158
167
  const transport = supportedTransports[i]
159
168
  try {
@@ -184,9 +193,20 @@ export function fallback<const transports extends readonly Transport[]>(
184
193
  throw err
185
194
  }
186
195
 
187
- // If we've reached the end of the fallbacks, throw the error.
196
+ collectedErrors.push({
197
+ transport: transport.config.name,
198
+ error: err as Error,
199
+ attempt: i + 1,
200
+ })
201
+
202
+ // If we've reached the end of the fallbacks, throw all collected errors.
188
203
  if (i === supportedTransports.length - 1) {
189
- throw err
204
+ throw new AllTransportsFailedError({
205
+ method,
206
+ params,
207
+ errors: collectedErrors,
208
+ totalAttempts: i + 1,
209
+ })
190
210
  }
191
211
 
192
212
  // Otherwise, try the next fallback.
@@ -227,16 +247,20 @@ export function fallback<const transports extends readonly Transport[]>(
227
247
  }
228
248
 
229
249
  export function shouldThrow(error: Error) {
250
+ if (error instanceof InsufficientUTXOBalanceError) {
251
+ return true
252
+ }
253
+
230
254
  if ('code' in error && typeof error.code === 'number') {
231
255
  if (
232
256
  error.code === RpcErrorCode.INTERNAL_ERROR ||
233
257
  error.code === RpcErrorCode.USER_REJECTION ||
234
- error.code === 5000 || // CAIP UserRejectedRequestError
235
- error instanceof InsufficientUTXOBalanceError
258
+ error.code === 5000 // CAIP UserRejectedRequestError
236
259
  ) {
237
260
  return true
238
261
  }
239
262
  }
263
+
240
264
  return false
241
265
  }
242
266
 
@@ -0,0 +1 @@
1
+ "Invalid hex string"
@@ -0,0 +1,58 @@
1
+ {
2
+ "txid": "4b6ee974f1dd179071d027562e1fd1c83965efa4a171ec84f00b6c638e36fa4e",
3
+ "version": 2,
4
+ "locktime": 0,
5
+ "vin": [
6
+ {
7
+ "txid": "e77cbe6d061f43672968834bce324a1984797f5cfbb463697df238bd385917be",
8
+ "vout": 0,
9
+ "prevout": {
10
+ "scriptpubkey": "00140b2cfb49a87dbb35f0a2f1c02f2016a441aac7bc",
11
+ "scriptpubkey_asm": "OP_0 OP_PUSHBYTES_20 0b2cfb49a87dbb35f0a2f1c02f2016a441aac7bc",
12
+ "scriptpubkey_type": "v0_p2wpkh",
13
+ "scriptpubkey_address": "bc1qpvk0kjdg0kantu9z78qz7gqk53q643auz33yaw",
14
+ "value": 47580
15
+ },
16
+ "scriptsig": "",
17
+ "scriptsig_asm": "",
18
+ "witness": [
19
+ "3045022100f7f01395cd1522fa63977f658bc72410a59e576b35cbf2a606dad1ed98e9c01402201d7994777edf3967ea2cf90e9f95eb9b9b5e6635aa934defb0094f7b313609e301",
20
+ "025873f7163097c81372cb414f25e79204a422fbcf43495a18d8043466fd4e7c99"
21
+ ],
22
+ "is_coinbase": false,
23
+ "sequence": 4294967293
24
+ }
25
+ ],
26
+ "vout": [
27
+ {
28
+ "scriptpubkey": "001496d086eea328c1648231a3bca6f395f44fa95dae",
29
+ "scriptpubkey_asm": "OP_0 OP_PUSHBYTES_20 96d086eea328c1648231a3bca6f395f44fa95dae",
30
+ "scriptpubkey_type": "v0_p2wpkh",
31
+ "scriptpubkey_address": "bc1qjmggdm4r9rqkfq335w72duu47386jhdw6wye0m",
32
+ "value": 10000
33
+ },
34
+ {
35
+ "scriptpubkey": "6a473d3a613a3078424435354332463330364339374664316433453741303233663763343833346132463437323833343a3a6c6966692f2d5f3a302f32307c30783134323835626439",
36
+ "scriptpubkey_asm": "OP_RETURN OP_PUSHBYTES_71 3d3a613a3078424435354332463330364339374664316433453741303233663763343833346132463437323833343a3a6c6966692f2d5f3a302f32307c30783134323835626439",
37
+ "scriptpubkey_type": "op_return",
38
+ "value": 0
39
+ },
40
+ {
41
+ "scriptpubkey": "00140b2cfb49a87dbb35f0a2f1c02f2016a441aac7bc",
42
+ "scriptpubkey_asm": "OP_0 OP_PUSHBYTES_20 0b2cfb49a87dbb35f0a2f1c02f2016a441aac7bc",
43
+ "scriptpubkey_type": "v0_p2wpkh",
44
+ "scriptpubkey_address": "bc1qpvk0kjdg0kantu9z78qz7gqk53q643auz33yaw",
45
+ "value": 33206
46
+ }
47
+ ],
48
+ "size": 305,
49
+ "weight": 890,
50
+ "sigops": 1,
51
+ "fee": 4374,
52
+ "status": {
53
+ "confirmed": true,
54
+ "block_height": 897002,
55
+ "block_hash": "00000000000000000000aa525233832b8fcf408b8de79677c994b3a014686134",
56
+ "block_time": 1747423060
57
+ }
58
+ }
@@ -0,0 +1,19 @@
1
+ import type { RpcMethodHandler } from '../types.js'
2
+ import type { MempoolTransaction } from './mempool.types.js'
3
+
4
+ export const getTransactionFee: RpcMethodHandler<'getTransactionFee'> = async (
5
+ client,
6
+ { baseUrl },
7
+ { txId }
8
+ ) => {
9
+ const apiUrl = `${baseUrl}/tx/${txId}`
10
+
11
+ const response = (await client.request({
12
+ url: apiUrl,
13
+ fetchOptions: { method: 'GET' },
14
+ })) as unknown as MempoolTransaction
15
+
16
+ return {
17
+ result: BigInt(response.fee),
18
+ }
19
+ }