@opendatalabs/vana-sdk 0.1.0-alpha.fd33fc9 → 2.0.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 (351) hide show
  1. package/dist/browser.cjs.map +1 -1
  2. package/dist/browser.d.ts +33 -1
  3. package/dist/browser.js.map +1 -1
  4. package/dist/chains/index.cjs.map +1 -1
  5. package/dist/chains/index.d.ts +30 -1
  6. package/dist/chains/index.js.map +1 -1
  7. package/dist/client/__tests__/enhancedResponse.test.d.ts +1 -0
  8. package/dist/client/enhancedResponse.cjs +164 -0
  9. package/dist/client/enhancedResponse.cjs.map +1 -0
  10. package/dist/client/enhancedResponse.d.ts +120 -0
  11. package/dist/client/enhancedResponse.js +138 -0
  12. package/dist/client/enhancedResponse.js.map +1 -0
  13. package/dist/config/chains.cjs.map +1 -1
  14. package/dist/config/chains.d.ts +99 -0
  15. package/dist/config/chains.js.map +1 -1
  16. package/dist/contracts/contractController.cjs.map +1 -1
  17. package/dist/contracts/contractController.d.ts +66 -10
  18. package/dist/contracts/contractController.js.map +1 -1
  19. package/dist/controllers/__tests__/data-consistency-integration.test.d.ts +7 -0
  20. package/dist/controllers/__tests__/operations.processQueue.test.d.ts +1 -0
  21. package/dist/controllers/base.cjs +33 -0
  22. package/dist/controllers/base.cjs.map +1 -1
  23. package/dist/controllers/base.d.ts +10 -0
  24. package/dist/controllers/base.js +33 -0
  25. package/dist/controllers/base.js.map +1 -1
  26. package/dist/controllers/data.cjs +417 -276
  27. package/dist/controllers/data.cjs.map +1 -1
  28. package/dist/controllers/data.d.ts +246 -193
  29. package/dist/controllers/data.js +430 -279
  30. package/dist/controllers/data.js.map +1 -1
  31. package/dist/controllers/operations.cjs +430 -0
  32. package/dist/controllers/operations.cjs.map +1 -0
  33. package/dist/controllers/operations.d.ts +229 -0
  34. package/dist/controllers/operations.js +406 -0
  35. package/dist/controllers/operations.js.map +1 -0
  36. package/dist/controllers/permissions.cjs +690 -209
  37. package/dist/controllers/permissions.cjs.map +1 -1
  38. package/dist/controllers/permissions.d.ts +196 -68
  39. package/dist/controllers/permissions.js +690 -209
  40. package/dist/controllers/permissions.js.map +1 -1
  41. package/dist/controllers/protocol.cjs.map +1 -1
  42. package/dist/controllers/protocol.d.ts +27 -28
  43. package/dist/controllers/protocol.js.map +1 -1
  44. package/dist/controllers/schemas.cjs +104 -25
  45. package/dist/controllers/schemas.cjs.map +1 -1
  46. package/dist/controllers/schemas.d.ts +88 -40
  47. package/dist/controllers/schemas.js +104 -25
  48. package/dist/controllers/schemas.js.map +1 -1
  49. package/dist/controllers/server.cjs +269 -58
  50. package/dist/controllers/server.cjs.map +1 -1
  51. package/dist/controllers/server.d.ts +157 -52
  52. package/dist/controllers/server.js +269 -58
  53. package/dist/controllers/server.js.map +1 -1
  54. package/dist/core/__tests__/health.test.d.ts +1 -0
  55. package/dist/core/__tests__/inMemoryNonceManager.test.d.ts +1 -0
  56. package/dist/core/__tests__/nonceManager.test.d.ts +1 -0
  57. package/dist/core/__tests__/pollingManager.test.d.ts +4 -0
  58. package/dist/core/apiClient.cjs +53 -3
  59. package/dist/core/apiClient.cjs.map +1 -1
  60. package/dist/core/apiClient.d.ts +132 -7
  61. package/dist/core/apiClient.js +53 -3
  62. package/dist/core/apiClient.js.map +1 -1
  63. package/dist/core/generics.cjs +30 -3
  64. package/dist/core/generics.cjs.map +1 -1
  65. package/dist/core/generics.d.ts +95 -6
  66. package/dist/core/generics.js +30 -3
  67. package/dist/core/generics.js.map +1 -1
  68. package/dist/core/health.cjs +289 -0
  69. package/dist/core/health.cjs.map +1 -0
  70. package/dist/core/health.d.ts +143 -0
  71. package/dist/core/health.js +265 -0
  72. package/dist/core/health.js.map +1 -0
  73. package/dist/core/inMemoryNonceManager.cjs +138 -0
  74. package/dist/core/inMemoryNonceManager.cjs.map +1 -0
  75. package/dist/core/inMemoryNonceManager.d.ts +69 -0
  76. package/dist/core/inMemoryNonceManager.js +114 -0
  77. package/dist/core/inMemoryNonceManager.js.map +1 -0
  78. package/dist/core/nonceManager.cjs +304 -0
  79. package/dist/core/nonceManager.cjs.map +1 -0
  80. package/dist/core/nonceManager.d.ts +116 -0
  81. package/dist/core/nonceManager.js +280 -0
  82. package/dist/core/nonceManager.js.map +1 -0
  83. package/dist/core/pollingManager.cjs +292 -0
  84. package/dist/core/pollingManager.cjs.map +1 -0
  85. package/dist/core/pollingManager.d.ts +120 -0
  86. package/dist/core/pollingManager.js +268 -0
  87. package/dist/core/pollingManager.js.map +1 -0
  88. package/dist/core.cjs +55 -1
  89. package/dist/core.cjs.map +1 -1
  90. package/dist/core.d.ts +54 -3
  91. package/dist/core.js +55 -1
  92. package/dist/core.js.map +1 -1
  93. package/dist/crypto/ecies/base.cjs +16 -3
  94. package/dist/crypto/ecies/base.cjs.map +1 -1
  95. package/dist/crypto/ecies/base.js +16 -3
  96. package/dist/crypto/ecies/base.js.map +1 -1
  97. package/dist/errors.cjs +29 -0
  98. package/dist/errors.cjs.map +1 -1
  99. package/dist/errors.d.ts +64 -0
  100. package/dist/errors.js +28 -0
  101. package/dist/errors.js.map +1 -1
  102. package/dist/generated/abi/ComputeInstructionRegistryImplementation.cjs.map +1 -1
  103. package/dist/generated/abi/ComputeInstructionRegistryImplementation.js.map +1 -1
  104. package/dist/generated/abi/DLPPerformanceImplementation.cjs +42 -0
  105. package/dist/generated/abi/DLPPerformanceImplementation.cjs.map +1 -1
  106. package/dist/generated/abi/DLPPerformanceImplementation.d.ts +32 -0
  107. package/dist/generated/abi/DLPPerformanceImplementation.js +42 -0
  108. package/dist/generated/abi/DLPPerformanceImplementation.js.map +1 -1
  109. package/dist/generated/abi/DLPRegistryImplementation.cjs +5 -5
  110. package/dist/generated/abi/DLPRegistryImplementation.cjs.map +1 -1
  111. package/dist/generated/abi/DLPRegistryImplementation.d.ts +4 -4
  112. package/dist/generated/abi/DLPRegistryImplementation.js +5 -5
  113. package/dist/generated/abi/DLPRegistryImplementation.js.map +1 -1
  114. package/dist/generated/abi/DLPRewardDeployerImplementation.cjs +166 -2
  115. package/dist/generated/abi/DLPRewardDeployerImplementation.cjs.map +1 -1
  116. package/dist/generated/abi/DLPRewardDeployerImplementation.d.ts +129 -2
  117. package/dist/generated/abi/DLPRewardDeployerImplementation.js +166 -2
  118. package/dist/generated/abi/DLPRewardDeployerImplementation.js.map +1 -1
  119. package/dist/generated/abi/DataPortabilityGranteesImplementation.cjs +167 -19
  120. package/dist/generated/abi/DataPortabilityGranteesImplementation.cjs.map +1 -1
  121. package/dist/generated/abi/DataPortabilityGranteesImplementation.d.ts +127 -14
  122. package/dist/generated/abi/DataPortabilityGranteesImplementation.js +167 -19
  123. package/dist/generated/abi/DataPortabilityGranteesImplementation.js.map +1 -1
  124. package/dist/generated/abi/DataPortabilityPermissionsImplementation.cjs +0 -19
  125. package/dist/generated/abi/DataPortabilityPermissionsImplementation.cjs.map +1 -1
  126. package/dist/generated/abi/DataPortabilityPermissionsImplementation.d.ts +0 -14
  127. package/dist/generated/abi/DataPortabilityPermissionsImplementation.js +0 -19
  128. package/dist/generated/abi/DataPortabilityPermissionsImplementation.js.map +1 -1
  129. package/dist/generated/abi/DataPortabilityServersImplementation.cjs +0 -19
  130. package/dist/generated/abi/DataPortabilityServersImplementation.cjs.map +1 -1
  131. package/dist/generated/abi/DataPortabilityServersImplementation.d.ts +0 -14
  132. package/dist/generated/abi/DataPortabilityServersImplementation.js +0 -19
  133. package/dist/generated/abi/DataPortabilityServersImplementation.js.map +1 -1
  134. package/dist/generated/abi/DataRegistryImplementation.cjs +0 -13
  135. package/dist/generated/abi/DataRegistryImplementation.cjs.map +1 -1
  136. package/dist/generated/abi/DataRegistryImplementation.d.ts +0 -10
  137. package/dist/generated/abi/DataRegistryImplementation.js +0 -13
  138. package/dist/generated/abi/DataRegistryImplementation.js.map +1 -1
  139. package/dist/generated/abi/SwapHelperImplementation.cjs +0 -43
  140. package/dist/generated/abi/SwapHelperImplementation.cjs.map +1 -1
  141. package/dist/generated/abi/SwapHelperImplementation.d.ts +0 -35
  142. package/dist/generated/abi/SwapHelperImplementation.js +0 -43
  143. package/dist/generated/abi/SwapHelperImplementation.js.map +1 -1
  144. package/dist/generated/abi/VanaEpochImplementation.cjs +195 -0
  145. package/dist/generated/abi/VanaEpochImplementation.cjs.map +1 -1
  146. package/dist/generated/abi/VanaEpochImplementation.d.ts +151 -0
  147. package/dist/generated/abi/VanaEpochImplementation.js +195 -0
  148. package/dist/generated/abi/VanaEpochImplementation.js.map +1 -1
  149. package/dist/generated/abi/VanaPoolEntityImplementation.cjs +22 -65
  150. package/dist/generated/abi/VanaPoolEntityImplementation.cjs.map +1 -1
  151. package/dist/generated/abi/VanaPoolEntityImplementation.d.ts +17 -51
  152. package/dist/generated/abi/VanaPoolEntityImplementation.js +22 -65
  153. package/dist/generated/abi/VanaPoolEntityImplementation.js.map +1 -1
  154. package/dist/generated/abi/VanaPoolStakingImplementation.cjs +113 -1
  155. package/dist/generated/abi/VanaPoolStakingImplementation.cjs.map +1 -1
  156. package/dist/generated/abi/VanaPoolStakingImplementation.d.ts +85 -1
  157. package/dist/generated/abi/VanaPoolStakingImplementation.js +113 -1
  158. package/dist/generated/abi/VanaPoolStakingImplementation.js.map +1 -1
  159. package/dist/generated/abi/index.d.ts +546 -146
  160. package/dist/generated/event-types.cjs.map +1 -1
  161. package/dist/generated/event-types.d.ts +14 -8
  162. package/dist/generated/eventRegistry.cjs +42 -18
  163. package/dist/generated/eventRegistry.cjs.map +1 -1
  164. package/dist/generated/eventRegistry.js +42 -18
  165. package/dist/generated/eventRegistry.js.map +1 -1
  166. package/dist/generated/server/server-exports.cjs +22 -0
  167. package/dist/generated/server/server-exports.cjs.map +1 -1
  168. package/dist/generated/server/server-exports.d.ts +27 -10
  169. package/dist/generated/server/server-exports.js +17 -0
  170. package/dist/generated/server/server-exports.js.map +1 -1
  171. package/dist/generated/server/server.cjs.map +1 -1
  172. package/dist/generated/server/server.d.ts +771 -402
  173. package/dist/generated/subgraph.cjs +797 -32
  174. package/dist/generated/subgraph.cjs.map +1 -1
  175. package/dist/generated/subgraph.d.ts +135 -0
  176. package/dist/generated/subgraph.js +792 -32
  177. package/dist/generated/subgraph.js.map +1 -1
  178. package/dist/index.browser.d.ts +2 -0
  179. package/dist/index.browser.js +10 -0
  180. package/dist/index.browser.js.map +1 -1
  181. package/dist/index.cjs.map +1 -1
  182. package/dist/index.js.map +1 -1
  183. package/dist/index.node.cjs +26 -0
  184. package/dist/index.node.cjs.map +1 -1
  185. package/dist/index.node.d.ts +49 -5
  186. package/dist/index.node.js +25 -1
  187. package/dist/index.node.js.map +1 -1
  188. package/dist/lib/__tests__/redisAtomicStore.test.d.ts +1 -0
  189. package/dist/lib/redisAtomicStore.cjs +201 -0
  190. package/dist/lib/redisAtomicStore.cjs.map +1 -0
  191. package/dist/lib/redisAtomicStore.d.ts +120 -0
  192. package/dist/lib/redisAtomicStore.js +177 -0
  193. package/dist/lib/redisAtomicStore.js.map +1 -0
  194. package/dist/node.cjs.map +1 -1
  195. package/dist/node.d.ts +39 -1
  196. package/dist/node.js.map +1 -1
  197. package/dist/platform/browser.cjs +160 -2
  198. package/dist/platform/browser.cjs.map +1 -1
  199. package/dist/platform/browser.d.ts +232 -12
  200. package/dist/platform/browser.js +160 -2
  201. package/dist/platform/browser.js.map +1 -1
  202. package/dist/platform/interface.cjs.map +1 -1
  203. package/dist/platform/interface.d.ts +283 -90
  204. package/dist/platform/node.cjs +163 -2
  205. package/dist/platform/node.cjs.map +1 -1
  206. package/dist/platform/node.d.ts +69 -6
  207. package/dist/platform/node.js +163 -2
  208. package/dist/platform/node.js.map +1 -1
  209. package/dist/server/relayerHandler.cjs +315 -81
  210. package/dist/server/relayerHandler.cjs.map +1 -1
  211. package/dist/server/relayerHandler.d.ts +35 -2
  212. package/dist/server/relayerHandler.js +315 -81
  213. package/dist/server/relayerHandler.js.map +1 -1
  214. package/dist/storage/index.cjs +3 -0
  215. package/dist/storage/index.cjs.map +1 -1
  216. package/dist/storage/index.d.ts +1 -0
  217. package/dist/storage/index.js +2 -0
  218. package/dist/storage/index.js.map +1 -1
  219. package/dist/storage/manager.cjs +108 -25
  220. package/dist/storage/manager.cjs.map +1 -1
  221. package/dist/storage/manager.d.ts +119 -25
  222. package/dist/storage/manager.js +108 -25
  223. package/dist/storage/manager.js.map +1 -1
  224. package/dist/storage/providers/callback-storage.cjs +86 -15
  225. package/dist/storage/providers/callback-storage.cjs.map +1 -1
  226. package/dist/storage/providers/callback-storage.d.ts +109 -20
  227. package/dist/storage/providers/callback-storage.js +86 -15
  228. package/dist/storage/providers/callback-storage.js.map +1 -1
  229. package/dist/storage/providers/dropbox.cjs +237 -0
  230. package/dist/storage/providers/dropbox.cjs.map +1 -0
  231. package/dist/storage/providers/dropbox.d.ts +39 -0
  232. package/dist/storage/providers/dropbox.js +215 -0
  233. package/dist/storage/providers/dropbox.js.map +1 -0
  234. package/dist/storage/providers/dropbox.test.d.ts +1 -0
  235. package/dist/storage/providers/pinata.cjs.map +1 -1
  236. package/dist/storage/providers/pinata.d.ts +12 -14
  237. package/dist/storage/providers/pinata.js.map +1 -1
  238. package/dist/tests/data-upload-owner-validation.test.d.ts +1 -0
  239. package/dist/tests/permissions-transaction-options.test.d.ts +1 -0
  240. package/dist/types/atomicStore.cjs +31 -0
  241. package/dist/types/atomicStore.cjs.map +1 -0
  242. package/dist/types/atomicStore.d.ts +236 -0
  243. package/dist/types/atomicStore.js +7 -0
  244. package/dist/types/atomicStore.js.map +1 -0
  245. package/dist/types/blockchain.cjs.map +1 -1
  246. package/dist/types/blockchain.d.ts +39 -11
  247. package/dist/types/chains.cjs.map +1 -1
  248. package/dist/types/chains.d.ts +74 -7
  249. package/dist/types/chains.js.map +1 -1
  250. package/dist/types/config.cjs.map +1 -1
  251. package/dist/types/config.d.ts +38 -4
  252. package/dist/types/config.js.map +1 -1
  253. package/dist/types/contracts.cjs.map +1 -1
  254. package/dist/types/contracts.d.ts +71 -7
  255. package/dist/types/controller-context.cjs.map +1 -1
  256. package/dist/types/controller-context.d.ts +4 -1
  257. package/dist/types/data.cjs.map +1 -1
  258. package/dist/types/data.d.ts +11 -10
  259. package/dist/types/generics.cjs.map +1 -1
  260. package/dist/types/generics.d.ts +81 -10
  261. package/dist/types/index.cjs.map +1 -1
  262. package/dist/types/index.d.ts +31 -3
  263. package/dist/types/index.js.map +1 -1
  264. package/dist/types/operationStore.cjs +17 -0
  265. package/dist/types/operationStore.cjs.map +1 -0
  266. package/dist/types/operationStore.d.ts +171 -0
  267. package/dist/types/operationStore.js +1 -0
  268. package/dist/types/operationStore.js.map +1 -0
  269. package/dist/types/operations.cjs +3 -15
  270. package/dist/types/operations.cjs.map +1 -1
  271. package/dist/types/operations.d.ts +131 -39
  272. package/dist/types/operations.js +2 -13
  273. package/dist/types/operations.js.map +1 -1
  274. package/dist/types/options.cjs +17 -0
  275. package/dist/types/options.cjs.map +1 -0
  276. package/dist/types/options.d.ts +308 -0
  277. package/dist/types/options.js +1 -0
  278. package/dist/types/options.js.map +1 -0
  279. package/dist/types/permissions.cjs.map +1 -1
  280. package/dist/types/permissions.d.ts +19 -20
  281. package/dist/types/personal.cjs.map +1 -1
  282. package/dist/types/personal.d.ts +150 -14
  283. package/dist/types/relayer.cjs.map +1 -1
  284. package/dist/types/relayer.d.ts +145 -24
  285. package/dist/types/storage.cjs.map +1 -1
  286. package/dist/types/storage.d.ts +9 -21
  287. package/dist/types/storage.js.map +1 -1
  288. package/dist/types/utils.cjs.map +1 -1
  289. package/dist/types/utils.d.ts +0 -45
  290. package/dist/utils/__tests__/chainQuery.test.d.ts +1 -0
  291. package/dist/utils/__tests__/subgraphConsistency.test.d.ts +4 -0
  292. package/dist/utils/__tests__/subgraphPagination.test.d.ts +4 -0
  293. package/dist/utils/chainQuery.cjs +107 -0
  294. package/dist/utils/chainQuery.cjs.map +1 -0
  295. package/dist/utils/chainQuery.d.ts +31 -0
  296. package/dist/utils/chainQuery.js +82 -0
  297. package/dist/utils/chainQuery.js.map +1 -0
  298. package/dist/utils/grantFiles.cjs +4 -1
  299. package/dist/utils/grantFiles.cjs.map +1 -1
  300. package/dist/utils/grantFiles.d.ts +10 -20
  301. package/dist/utils/grantFiles.js +4 -1
  302. package/dist/utils/grantFiles.js.map +1 -1
  303. package/dist/utils/grantValidation.cjs.map +1 -1
  304. package/dist/utils/grantValidation.d.ts +95 -16
  305. package/dist/utils/grantValidation.js.map +1 -1
  306. package/dist/utils/grants.cjs.map +1 -1
  307. package/dist/utils/grants.d.ts +93 -12
  308. package/dist/utils/grants.js.map +1 -1
  309. package/dist/utils/ipfs.cjs +2 -4
  310. package/dist/utils/ipfs.cjs.map +1 -1
  311. package/dist/utils/ipfs.d.ts +1 -1
  312. package/dist/utils/ipfs.js +2 -4
  313. package/dist/utils/ipfs.js.map +1 -1
  314. package/dist/utils/lazy-import.cjs.map +1 -1
  315. package/dist/utils/lazy-import.d.ts +32 -7
  316. package/dist/utils/lazy-import.js.map +1 -1
  317. package/dist/utils/signatureCache.cjs +8 -2
  318. package/dist/utils/signatureCache.cjs.map +1 -1
  319. package/dist/utils/signatureCache.d.ts +49 -8
  320. package/dist/utils/signatureCache.js +8 -2
  321. package/dist/utils/signatureCache.js.map +1 -1
  322. package/dist/utils/subgraphConsistency.cjs +184 -0
  323. package/dist/utils/subgraphConsistency.cjs.map +1 -0
  324. package/dist/utils/subgraphConsistency.d.ts +65 -0
  325. package/dist/utils/subgraphConsistency.js +155 -0
  326. package/dist/utils/subgraphConsistency.js.map +1 -0
  327. package/dist/utils/subgraphMetaCache.cjs +101 -0
  328. package/dist/utils/subgraphMetaCache.cjs.map +1 -0
  329. package/dist/utils/subgraphMetaCache.d.ts +56 -0
  330. package/dist/utils/subgraphMetaCache.js +76 -0
  331. package/dist/utils/subgraphMetaCache.js.map +1 -0
  332. package/dist/utils/subgraphPagination.cjs +104 -0
  333. package/dist/utils/subgraphPagination.cjs.map +1 -0
  334. package/dist/utils/subgraphPagination.d.ts +78 -0
  335. package/dist/utils/subgraphPagination.js +78 -0
  336. package/dist/utils/subgraphPagination.js.map +1 -0
  337. package/dist/utils/transactionHelpers.cjs.map +1 -1
  338. package/dist/utils/transactionHelpers.d.ts +12 -12
  339. package/dist/utils/transactionHelpers.js.map +1 -1
  340. package/dist/utils/typedDataConverter.cjs.map +1 -1
  341. package/dist/utils/typedDataConverter.d.ts +39 -3
  342. package/dist/utils/typedDataConverter.js.map +1 -1
  343. package/dist/utils/urlResolver.cjs +7 -0
  344. package/dist/utils/urlResolver.cjs.map +1 -1
  345. package/dist/utils/urlResolver.d.ts +22 -4
  346. package/dist/utils/urlResolver.js +7 -0
  347. package/dist/utils/urlResolver.js.map +1 -1
  348. package/dist/utils/wallet.cjs.map +1 -1
  349. package/dist/utils/wallet.d.ts +78 -16
  350. package/dist/utils/wallet.js.map +1 -1
  351. package/package.json +3 -1
@@ -0,0 +1,280 @@
1
+ import { hasNonceSupport } from "../types/atomicStore";
2
+ class DistributedNonceManager {
3
+ store;
4
+ publicClient;
5
+ lockTTL;
6
+ maxLockRetries;
7
+ lockRetryDelay;
8
+ constructor(config) {
9
+ this.store = config.atomicStore;
10
+ this.publicClient = config.publicClient;
11
+ this.lockTTL = config.lockTTL ?? 5;
12
+ this.maxLockRetries = config.maxLockRetries ?? 50;
13
+ this.lockRetryDelay = config.lockRetryDelay ?? 100;
14
+ }
15
+ /**
16
+ * Atomically assigns the next available nonce for an address.
17
+ *
18
+ * @remarks
19
+ * This method uses two strategies depending on the atomic store capabilities:
20
+ * 1. If the store provides atomicAssignNonce (store-specific optimization),
21
+ * it uses that for maximum reliability and performance
22
+ * 2. Otherwise, it falls back to the lock-based approach:
23
+ * - Acquires a distributed lock to prevent race conditions
24
+ * - Queries the blockchain for the current pending nonce
25
+ * - Syncs the stored nonce if blockchain is ahead
26
+ * - Atomically increments and returns the next nonce
27
+ * - Releases the lock
28
+ *
29
+ * @param address - The address to assign a nonce for
30
+ * @param chainId - The chain ID for the network
31
+ * @returns The assigned nonce, or null if lock acquisition fails
32
+ */
33
+ async assignNonce(address, chainId) {
34
+ const lastUsedKey = `nonce:${chainId}:${address}:lastUsed`;
35
+ const lockKey = `nonce:${chainId}:${address}:lock`;
36
+ if (hasNonceSupport(this.store)) {
37
+ try {
38
+ const pendingCount = await this.publicClient.getTransactionCount({
39
+ address,
40
+ blockTag: "pending"
41
+ });
42
+ const nonce = await this.store.atomicAssignNonce(
43
+ lastUsedKey,
44
+ pendingCount
45
+ );
46
+ console.log(
47
+ `[NonceManager] Assigned nonce ${nonce} for ${address} on chain ${chainId} (using store optimization)`
48
+ );
49
+ return nonce;
50
+ } catch (error) {
51
+ console.error(
52
+ "[NonceManager] Error during optimized nonce assignment:",
53
+ error
54
+ );
55
+ throw error;
56
+ }
57
+ }
58
+ let lockAcquired = false;
59
+ let retries = 0;
60
+ let acquiredLockId = null;
61
+ while (!lockAcquired && retries < this.maxLockRetries) {
62
+ acquiredLockId = await this.store.acquireLock(lockKey, this.lockTTL);
63
+ lockAcquired = acquiredLockId !== null;
64
+ if (lockAcquired && acquiredLockId) {
65
+ try {
66
+ const pendingCount = await this.publicClient.getTransactionCount({
67
+ address,
68
+ blockTag: "pending"
69
+ });
70
+ const blockchainPending = pendingCount === 0 ? -1 : pendingCount - 1;
71
+ const currentLastUsedStr = await this.store.get(lastUsedKey);
72
+ const currentLastUsed = currentLastUsedStr ? parseInt(currentLastUsedStr) : -1;
73
+ if (blockchainPending > currentLastUsed) {
74
+ await this.store.set(lastUsedKey, blockchainPending.toString());
75
+ console.log(
76
+ `[NonceManager] Synced nonce for ${address} on chain ${chainId}: ${currentLastUsed} -> ${blockchainPending}`
77
+ );
78
+ }
79
+ const nextNonce = await this.store.incr(lastUsedKey);
80
+ console.log(
81
+ `[NonceManager] Assigned nonce ${nextNonce} for ${address} on chain ${chainId}`
82
+ );
83
+ if (this.store.setWithTTL) {
84
+ const assignmentKey = `nonce:${chainId}:${address}:assignment:${nextNonce}`;
85
+ const metadata = {
86
+ nonce: nextNonce,
87
+ assignedAt: Date.now(),
88
+ address,
89
+ chainId
90
+ };
91
+ await this.store.setWithTTL(
92
+ assignmentKey,
93
+ JSON.stringify(metadata),
94
+ 3600
95
+ );
96
+ }
97
+ return nextNonce;
98
+ } catch (error) {
99
+ console.error("[NonceManager] Error during nonce assignment:", error);
100
+ throw error;
101
+ } finally {
102
+ await this.store.releaseLock(lockKey, acquiredLockId);
103
+ }
104
+ } else {
105
+ retries++;
106
+ if (retries < this.maxLockRetries) {
107
+ const delay = Math.min(
108
+ this.lockRetryDelay * Math.pow(1.5, retries - 1),
109
+ 5e3
110
+ // Cap at 5 seconds
111
+ );
112
+ await new Promise((resolve) => setTimeout(resolve, delay));
113
+ }
114
+ }
115
+ }
116
+ console.error(
117
+ `[NonceManager] Failed to acquire lock for ${address} after ${this.maxLockRetries} attempts`
118
+ );
119
+ return null;
120
+ }
121
+ /**
122
+ * Resets the nonce counter for an address.
123
+ *
124
+ * @remarks
125
+ * Use with caution - should only be called when you're certain
126
+ * no transactions are pending. This is useful for recovery from
127
+ * stuck states.
128
+ *
129
+ * @param address - The address to reset nonce for
130
+ * @param chainId - The chain ID for the network
131
+ */
132
+ async resetNonce(address, chainId) {
133
+ const lastUsedKey = `nonce:${chainId}:${address}:lastUsed`;
134
+ const lockKey = `nonce:${chainId}:${address}:lock`;
135
+ const lockId = await this.acquireLockWithRetry(lockKey);
136
+ if (!lockId) {
137
+ throw new Error("Failed to acquire lock for nonce reset");
138
+ }
139
+ try {
140
+ const confirmedCount = await this.publicClient.getTransactionCount({
141
+ address,
142
+ blockTag: "latest"
143
+ });
144
+ const resetValue = confirmedCount === 0 ? -1 : confirmedCount - 1;
145
+ await this.store.set(lastUsedKey, resetValue.toString());
146
+ console.log(
147
+ `[NonceManager] Reset nonce for ${address} on chain ${chainId} to ${resetValue}`
148
+ );
149
+ } finally {
150
+ await this.store.releaseLock(lockKey, lockId);
151
+ }
152
+ }
153
+ /**
154
+ * Gets the current nonce state for monitoring/debugging.
155
+ *
156
+ * @param address - The address to check
157
+ * @param chainId - The chain ID for the network
158
+ * @returns Current nonce state information
159
+ */
160
+ async getNonceState(address, chainId) {
161
+ const lastUsedKey = `nonce:${chainId}:${address}:lastUsed`;
162
+ const lastUsedStr = await this.store.get(lastUsedKey);
163
+ const lastUsed = lastUsedStr ? parseInt(lastUsedStr) : -1;
164
+ const [pendingCount, confirmedCount] = await Promise.all([
165
+ this.publicClient.getTransactionCount({
166
+ address,
167
+ blockTag: "pending"
168
+ }),
169
+ this.publicClient.getTransactionCount({
170
+ address,
171
+ blockTag: "latest"
172
+ })
173
+ ]);
174
+ return {
175
+ lastUsed,
176
+ blockchainPending: pendingCount === 0 ? -1 : pendingCount - 1,
177
+ blockchainConfirmed: confirmedCount === 0 ? -1 : confirmedCount - 1
178
+ };
179
+ }
180
+ /**
181
+ * Helper to acquire lock with retries.
182
+ *
183
+ * @internal
184
+ */
185
+ async acquireLockWithRetry(lockKey) {
186
+ let retries = 0;
187
+ while (retries < this.maxLockRetries) {
188
+ const lockId = await this.store.acquireLock(lockKey, this.lockTTL);
189
+ if (lockId) {
190
+ return lockId;
191
+ }
192
+ retries++;
193
+ if (retries < this.maxLockRetries) {
194
+ const delay = Math.min(
195
+ this.lockRetryDelay * Math.pow(1.5, retries - 1),
196
+ 5e3
197
+ );
198
+ await new Promise((resolve) => setTimeout(resolve, delay));
199
+ }
200
+ }
201
+ return null;
202
+ }
203
+ /**
204
+ * Burns a stuck nonce by sending a minimal self-transfer with higher gas.
205
+ *
206
+ * @remarks
207
+ * This method implements Vana App's production nonce burning strategy.
208
+ * It sends a zero-value transaction to the same address with elevated gas
209
+ * to replace a stuck transaction and unblock the nonce queue.
210
+ *
211
+ * Ported from apps/web/app/api/relay/route.ts (Vana App production code)
212
+ *
213
+ * @param walletClient - The wallet client to send the burn transaction
214
+ * @param nonceToBurn - The nonce to burn
215
+ * @param address - The address whose nonce to burn
216
+ * @param chainId - The chain ID for the network
217
+ * @param gasMultiplier - Multiplier for gas prices (default: 1.5)
218
+ * @returns The transaction hash of the burn transaction
219
+ */
220
+ async burnNonce(walletClient, nonceToBurn, address, chainId, gasMultiplier = 1.5) {
221
+ try {
222
+ const fees = await this.publicClient.estimateFeesPerGas();
223
+ const bump = (x) => x * BigInt(Math.floor(gasMultiplier * 100)) / 100n;
224
+ const newMaxFee = bump(fees.maxFeePerGas);
225
+ const newMaxPriorityFee = bump(fees.maxPriorityFeePerGas);
226
+ console.log(
227
+ `[NonceManager] Burning stuck nonce ${nonceToBurn} with high gas price: maxFee ${newMaxFee}, maxPriorityFee ${newMaxPriorityFee}`
228
+ );
229
+ const burnTx = await walletClient.sendTransaction({
230
+ account: walletClient.account,
231
+ // WalletClient should have an account
232
+ to: address,
233
+ // Self-transfer
234
+ value: 0n,
235
+ nonce: nonceToBurn,
236
+ gas: 21000n,
237
+ // Minimal gas for transfer
238
+ maxFeePerGas: newMaxFee,
239
+ maxPriorityFeePerGas: newMaxPriorityFee,
240
+ chain: {
241
+ id: chainId,
242
+ name: chainId === 14800 ? "Vana Moksha" : "Vana Mainnet",
243
+ network: chainId === 14800 ? "moksha" : "mainnet",
244
+ nativeCurrency: { name: "VANA", symbol: "VANA", decimals: 18 },
245
+ rpcUrls: {
246
+ default: {
247
+ http: [
248
+ chainId === 14800 ? "https://rpc.moksha.vana.org" : "https://rpc.vana.org"
249
+ ]
250
+ },
251
+ public: {
252
+ http: [
253
+ chainId === 14800 ? "https://rpc.moksha.vana.org" : "https://rpc.vana.org"
254
+ ]
255
+ }
256
+ }
257
+ }
258
+ });
259
+ console.log(
260
+ `[NonceManager] Burn nonce ${nonceToBurn} transaction sent: ${burnTx}`
261
+ );
262
+ return burnTx;
263
+ } catch (error) {
264
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
265
+ if (errorMessage.includes("nonce too low") || errorMessage.includes("underpriced") || errorMessage.includes("already known")) {
266
+ console.log(`[NonceManager] Nonce ${nonceToBurn} was already used`);
267
+ } else {
268
+ console.error(
269
+ `[NonceManager] Error burning nonce ${nonceToBurn}:`,
270
+ error
271
+ );
272
+ }
273
+ throw error;
274
+ }
275
+ }
276
+ }
277
+ export {
278
+ DistributedNonceManager
279
+ };
280
+ //# sourceMappingURL=nonceManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/nonceManager.ts"],"sourcesContent":["/**\n * Internal distributed nonce manager for atomic transaction nonce assignment.\n *\n * @internal\n * @module\n */\n\nimport type { PublicClient, WalletClient, Address, Hash } from \"viem\";\nimport type { IAtomicStore } from \"../types/atomicStore\";\nimport { hasNonceSupport } from \"../types/atomicStore\";\n\n/**\n * Configuration for the distributed nonce manager.\n *\n * @internal\n */\nexport interface NonceManagerConfig {\n /** Atomic store for distributed coordination */\n atomicStore: IAtomicStore;\n /** Public client for blockchain queries */\n publicClient: PublicClient;\n /** TTL for nonce locks in seconds (default: 5) */\n lockTTL?: number;\n /** Maximum retries to acquire lock (default: 50) */\n maxLockRetries?: number;\n /** Delay between lock retries in milliseconds (default: 100) */\n lockRetryDelay?: number;\n}\n\n/**\n * Internal nonce manager that coordinates distributed nonce assignment.\n *\n * @internal\n * @remarks\n * This class implements sophisticated logic for atomic nonce assignment\n * in distributed environments. It uses the provided IAtomicStore to\n * coordinate between multiple instances and prevent nonce conflicts.\n *\n * Key features:\n * - Store-specific optimizations when available (e.g., Redis Lua scripts)\n * - Fallback to distributed locking for generic stores\n * - Syncing with blockchain pending count\n * - Nonce gap prevention\n * - Nonce burning capability for stuck transactions\n */\nexport class DistributedNonceManager {\n private readonly store: IAtomicStore;\n private readonly publicClient: PublicClient;\n private readonly lockTTL: number;\n private readonly maxLockRetries: number;\n private readonly lockRetryDelay: number;\n\n constructor(config: NonceManagerConfig) {\n this.store = config.atomicStore;\n this.publicClient = config.publicClient;\n this.lockTTL = config.lockTTL ?? 5;\n this.maxLockRetries = config.maxLockRetries ?? 50;\n this.lockRetryDelay = config.lockRetryDelay ?? 100;\n }\n\n /**\n * Atomically assigns the next available nonce for an address.\n *\n * @remarks\n * This method uses two strategies depending on the atomic store capabilities:\n * 1. If the store provides atomicAssignNonce (store-specific optimization),\n * it uses that for maximum reliability and performance\n * 2. Otherwise, it falls back to the lock-based approach:\n * - Acquires a distributed lock to prevent race conditions\n * - Queries the blockchain for the current pending nonce\n * - Syncs the stored nonce if blockchain is ahead\n * - Atomically increments and returns the next nonce\n * - Releases the lock\n *\n * @param address - The address to assign a nonce for\n * @param chainId - The chain ID for the network\n * @returns The assigned nonce, or null if lock acquisition fails\n */\n async assignNonce(address: Address, chainId: number): Promise<number | null> {\n const lastUsedKey = `nonce:${chainId}:${address}:lastUsed`;\n const lockKey = `nonce:${chainId}:${address}:lock`;\n\n // Check if the store provides an optimized atomic nonce assignment\n if (hasNonceSupport(this.store)) {\n try {\n // Get pending transaction count from blockchain\n const pendingCount = await this.publicClient.getTransactionCount({\n address,\n blockTag: \"pending\",\n });\n\n // Use store-specific optimized implementation (e.g., Redis Lua script)\n const nonce = await this.store.atomicAssignNonce(\n lastUsedKey,\n pendingCount,\n );\n\n console.log(\n `[NonceManager] Assigned nonce ${nonce} for ${address} on chain ${chainId} (using store optimization)`,\n );\n\n return nonce;\n } catch (error) {\n console.error(\n \"[NonceManager] Error during optimized nonce assignment:\",\n error,\n );\n throw error;\n }\n }\n\n // Try to acquire lock with retries\n let lockAcquired = false;\n let retries = 0;\n let acquiredLockId: string | null = null;\n\n while (!lockAcquired && retries < this.maxLockRetries) {\n // Try to acquire the lock\n acquiredLockId = await this.store.acquireLock(lockKey, this.lockTTL);\n lockAcquired = acquiredLockId !== null;\n\n if (lockAcquired && acquiredLockId) {\n try {\n // Get pending transaction count from blockchain\n const pendingCount = await this.publicClient.getTransactionCount({\n address,\n blockTag: \"pending\",\n });\n\n // Calculate the pending nonce (0-indexed)\n // If no transactions, start at 0\n const blockchainPending = pendingCount === 0 ? -1 : pendingCount - 1;\n\n // Get current lastUsed from store\n const currentLastUsedStr = await this.store.get(lastUsedKey);\n const currentLastUsed = currentLastUsedStr\n ? parseInt(currentLastUsedStr)\n : -1;\n\n // Sync lastUsed with blockchain if blockchain is ahead\n // This handles cases where transactions were sent outside this system\n if (blockchainPending > currentLastUsed) {\n await this.store.set(lastUsedKey, blockchainPending.toString());\n console.log(\n `[NonceManager] Synced nonce for ${address} on chain ${chainId}: ${currentLastUsed} -> ${blockchainPending}`,\n );\n }\n\n // Atomically increment and get next nonce\n const nextNonce = await this.store.incr(lastUsedKey);\n\n console.log(\n `[NonceManager] Assigned nonce ${nextNonce} for ${address} on chain ${chainId}`,\n );\n\n // Store assignment metadata for debugging (optional TTL)\n if (this.store.setWithTTL) {\n const assignmentKey = `nonce:${chainId}:${address}:assignment:${nextNonce}`;\n const metadata = {\n nonce: nextNonce,\n assignedAt: Date.now(),\n address,\n chainId,\n };\n // Store for 1 hour for debugging\n await this.store.setWithTTL(\n assignmentKey,\n JSON.stringify(metadata),\n 3600,\n );\n }\n\n return nextNonce;\n } catch (error) {\n console.error(\"[NonceManager] Error during nonce assignment:\", error);\n throw error;\n } finally {\n // Always release the lock\n await this.store.releaseLock(lockKey, acquiredLockId);\n }\n } else {\n // Failed to acquire lock, wait and retry with exponential backoff\n retries++;\n if (retries < this.maxLockRetries) {\n const delay = Math.min(\n this.lockRetryDelay * Math.pow(1.5, retries - 1),\n 5000, // Cap at 5 seconds\n );\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n }\n\n console.error(\n `[NonceManager] Failed to acquire lock for ${address} after ${this.maxLockRetries} attempts`,\n );\n return null;\n }\n\n /**\n * Resets the nonce counter for an address.\n *\n * @remarks\n * Use with caution - should only be called when you're certain\n * no transactions are pending. This is useful for recovery from\n * stuck states.\n *\n * @param address - The address to reset nonce for\n * @param chainId - The chain ID for the network\n */\n async resetNonce(address: Address, chainId: number): Promise<void> {\n const lastUsedKey = `nonce:${chainId}:${address}:lastUsed`;\n const lockKey = `nonce:${chainId}:${address}:lock`;\n\n // Acquire lock before resetting\n const lockId = await this.acquireLockWithRetry(lockKey);\n if (!lockId) {\n throw new Error(\"Failed to acquire lock for nonce reset\");\n }\n\n try {\n // Get confirmed transaction count from blockchain\n const confirmedCount = await this.publicClient.getTransactionCount({\n address,\n blockTag: \"latest\",\n });\n\n // Reset to blockchain state (confirmed count - 1 since we're 0-indexed)\n const resetValue = confirmedCount === 0 ? -1 : confirmedCount - 1;\n await this.store.set(lastUsedKey, resetValue.toString());\n\n console.log(\n `[NonceManager] Reset nonce for ${address} on chain ${chainId} to ${resetValue}`,\n );\n } finally {\n await this.store.releaseLock(lockKey, lockId);\n }\n }\n\n /**\n * Gets the current nonce state for monitoring/debugging.\n *\n * @param address - The address to check\n * @param chainId - The chain ID for the network\n * @returns Current nonce state information\n */\n async getNonceState(\n address: Address,\n chainId: number,\n ): Promise<{\n lastUsed: number;\n blockchainPending: number;\n blockchainConfirmed: number;\n }> {\n const lastUsedKey = `nonce:${chainId}:${address}:lastUsed`;\n\n // Get stored state\n const lastUsedStr = await this.store.get(lastUsedKey);\n const lastUsed = lastUsedStr ? parseInt(lastUsedStr) : -1;\n\n // Get blockchain state\n const [pendingCount, confirmedCount] = await Promise.all([\n this.publicClient.getTransactionCount({\n address,\n blockTag: \"pending\",\n }),\n this.publicClient.getTransactionCount({\n address,\n blockTag: \"latest\",\n }),\n ]);\n\n return {\n lastUsed,\n blockchainPending: pendingCount === 0 ? -1 : pendingCount - 1,\n blockchainConfirmed: confirmedCount === 0 ? -1 : confirmedCount - 1,\n };\n }\n\n /**\n * Helper to acquire lock with retries.\n *\n * @internal\n */\n private async acquireLockWithRetry(lockKey: string): Promise<string | null> {\n let retries = 0;\n\n while (retries < this.maxLockRetries) {\n const lockId = await this.store.acquireLock(lockKey, this.lockTTL);\n\n if (lockId) {\n return lockId;\n }\n\n retries++;\n if (retries < this.maxLockRetries) {\n const delay = Math.min(\n this.lockRetryDelay * Math.pow(1.5, retries - 1),\n 5000,\n );\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n\n return null;\n }\n\n /**\n * Burns a stuck nonce by sending a minimal self-transfer with higher gas.\n *\n * @remarks\n * This method implements Vana App's production nonce burning strategy.\n * It sends a zero-value transaction to the same address with elevated gas\n * to replace a stuck transaction and unblock the nonce queue.\n *\n * Ported from apps/web/app/api/relay/route.ts (Vana App production code)\n *\n * @param walletClient - The wallet client to send the burn transaction\n * @param nonceToBurn - The nonce to burn\n * @param address - The address whose nonce to burn\n * @param chainId - The chain ID for the network\n * @param gasMultiplier - Multiplier for gas prices (default: 1.5)\n * @returns The transaction hash of the burn transaction\n */\n async burnNonce(\n walletClient: WalletClient,\n nonceToBurn: number,\n address: Address,\n chainId: number,\n gasMultiplier: number = 1.5,\n ): Promise<Hash> {\n try {\n // Get current gas prices\n const fees = await this.publicClient.estimateFeesPerGas();\n\n // Bump gas prices by multiplier (Vana App's exact formula)\n const bump = (x: bigint) =>\n (x * BigInt(Math.floor(gasMultiplier * 100))) / 100n;\n\n const newMaxFee = bump(fees.maxFeePerGas);\n const newMaxPriorityFee = bump(fees.maxPriorityFeePerGas);\n\n console.log(\n `[NonceManager] Burning stuck nonce ${nonceToBurn} with high gas price: maxFee ${newMaxFee}, maxPriorityFee ${newMaxPriorityFee}`,\n );\n\n // Send minimal self-transfer to burn the nonce\n const burnTx = await walletClient.sendTransaction({\n account: walletClient.account!, // WalletClient should have an account\n to: address, // Self-transfer\n value: 0n,\n nonce: nonceToBurn,\n gas: 21000n, // Minimal gas for transfer\n maxFeePerGas: newMaxFee,\n maxPriorityFeePerGas: newMaxPriorityFee,\n chain: {\n id: chainId,\n name: chainId === 14800 ? \"Vana Moksha\" : \"Vana Mainnet\",\n network: chainId === 14800 ? \"moksha\" : \"mainnet\",\n nativeCurrency: { name: \"VANA\", symbol: \"VANA\", decimals: 18 },\n rpcUrls: {\n default: {\n http: [\n chainId === 14800\n ? \"https://rpc.moksha.vana.org\"\n : \"https://rpc.vana.org\",\n ],\n },\n public: {\n http: [\n chainId === 14800\n ? \"https://rpc.moksha.vana.org\"\n : \"https://rpc.vana.org\",\n ],\n },\n },\n },\n });\n\n console.log(\n `[NonceManager] Burn nonce ${nonceToBurn} transaction sent: ${burnTx}`,\n );\n\n return burnTx;\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : \"Unknown error\";\n\n // Check for common errors that indicate nonce was already used\n if (\n errorMessage.includes(\"nonce too low\") ||\n errorMessage.includes(\"underpriced\") ||\n errorMessage.includes(\"already known\")\n ) {\n console.log(`[NonceManager] Nonce ${nonceToBurn} was already used`);\n } else {\n console.error(\n `[NonceManager] Error burning nonce ${nonceToBurn}:`,\n error,\n );\n }\n\n throw error;\n }\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,292 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var pollingManager_exports = {};
20
+ __export(pollingManager_exports, {
21
+ PollingManager: () => PollingManager
22
+ });
23
+ module.exports = __toCommonJS(pollingManager_exports);
24
+ var import_errors = require("../errors");
25
+ const DEFAULT_POLLING_OPTIONS = {
26
+ timeout: 3e5,
27
+ // 5 minutes
28
+ initialInterval: 1e3,
29
+ // 1 second
30
+ maxInterval: 1e4,
31
+ // 10 seconds
32
+ backoffMultiplier: 1.5,
33
+ // 1s -> 1.5s -> 2.25s -> 3.375s -> 5s -> 7.5s -> 10s
34
+ jitter: 0.2
35
+ // 20% randomization
36
+ };
37
+ class PollingManager {
38
+ constructor(relayerCallback) {
39
+ this.relayerCallback = relayerCallback;
40
+ }
41
+ abortController;
42
+ timeoutId;
43
+ pollIntervalId;
44
+ /**
45
+ * Starts polling for an operation's status.
46
+ *
47
+ * @param operationId - The operation ID to poll for
48
+ * @param options - Polling configuration and callbacks
49
+ * @returns Promise that resolves when operation completes or fails
50
+ * @throws TransactionPendingError if polling times out
51
+ * @throws Error if operation fails or is cancelled
52
+ */
53
+ async startPolling(operationId, options = {}) {
54
+ const context = {
55
+ operationId,
56
+ signal: options.signal,
57
+ onStatusUpdate: options.onStatusUpdate,
58
+ relayerCallback: this.relayerCallback,
59
+ options: {
60
+ ...DEFAULT_POLLING_OPTIONS,
61
+ ...options
62
+ }
63
+ };
64
+ this.abortController = new AbortController();
65
+ if (context.signal) {
66
+ context.signal.addEventListener("abort", () => {
67
+ this.abortController?.abort();
68
+ });
69
+ }
70
+ const timeoutPromise = this.createTimeoutPromise(context);
71
+ const pollingPromise = this.poll(context);
72
+ try {
73
+ const result = await Promise.race([pollingPromise, timeoutPromise]);
74
+ this.cleanup();
75
+ return result;
76
+ } catch (error) {
77
+ this.cleanup();
78
+ if (error instanceof Error && !error.message.includes("operationId")) {
79
+ throw new import_errors.TransactionPendingError(
80
+ operationId,
81
+ error.message,
82
+ this.getLastKnownStatus(context)
83
+ );
84
+ }
85
+ throw error;
86
+ }
87
+ }
88
+ /**
89
+ * Main polling loop with exponential backoff.
90
+ *
91
+ * @internal
92
+ */
93
+ async poll(context) {
94
+ let currentInterval = context.options.initialInterval;
95
+ let lastStatus = null;
96
+ let retryCount = 0;
97
+ while (!this.abortController?.signal.aborted) {
98
+ try {
99
+ const response = await this.checkStatus(context);
100
+ const status = this.mapResponseToStatus(response, context.operationId);
101
+ if (this.hasStatusChanged(lastStatus, status)) {
102
+ context.onStatusUpdate?.(status);
103
+ lastStatus = status;
104
+ }
105
+ if (status.type === "confirmed") {
106
+ return {
107
+ hash: status.hash,
108
+ receipt: status.receipt
109
+ };
110
+ }
111
+ if (status.type === "failed") {
112
+ throw new Error(status.error);
113
+ }
114
+ retryCount = 0;
115
+ } catch (error) {
116
+ if (this.isRetryableError(error)) {
117
+ retryCount++;
118
+ if (retryCount > 5) {
119
+ const errorMessage = error instanceof Error ? error.message : String(error);
120
+ throw new Error(
121
+ `Failed to poll after ${retryCount} attempts: ${errorMessage}`
122
+ );
123
+ }
124
+ } else {
125
+ throw error;
126
+ }
127
+ }
128
+ await this.wait(this.calculateNextInterval(currentInterval, context));
129
+ currentInterval = Math.min(
130
+ currentInterval * context.options.backoffMultiplier,
131
+ context.options.maxInterval
132
+ );
133
+ }
134
+ throw new Error("Polling cancelled");
135
+ }
136
+ /**
137
+ * Checks the current status of an operation.
138
+ *
139
+ * @internal
140
+ */
141
+ async checkStatus(context) {
142
+ const request = {
143
+ type: "status_check",
144
+ operationId: context.operationId
145
+ };
146
+ return await context.relayerCallback(request);
147
+ }
148
+ /**
149
+ * Maps relayer response to operation status.
150
+ *
151
+ * @internal
152
+ */
153
+ mapResponseToStatus(response, operationId) {
154
+ switch (response.type) {
155
+ case "pending":
156
+ return { type: "pending", operationId };
157
+ case "submitted":
158
+ return { type: "submitted", hash: response.hash };
159
+ case "confirmed":
160
+ return {
161
+ type: "confirmed",
162
+ hash: response.hash,
163
+ receipt: response.receipt
164
+ };
165
+ case "error":
166
+ return {
167
+ type: "failed",
168
+ error: response.error,
169
+ operationId
170
+ };
171
+ case "direct":
172
+ const result = response.result;
173
+ if (result?.status === "queued") {
174
+ return {
175
+ type: "queued",
176
+ position: result.position,
177
+ estimatedWait: result.estimatedWait
178
+ };
179
+ }
180
+ if (result?.status === "processing") {
181
+ return { type: "processing" };
182
+ }
183
+ return { type: "pending", operationId };
184
+ default:
185
+ return { type: "pending", operationId };
186
+ }
187
+ }
188
+ /**
189
+ * Determines if status has changed meaningfully.
190
+ *
191
+ * @internal
192
+ */
193
+ hasStatusChanged(oldStatus, newStatus) {
194
+ if (!oldStatus) return true;
195
+ if (oldStatus.type !== newStatus.type) return true;
196
+ if (oldStatus.type === "queued" && newStatus.type === "queued" && oldStatus.position !== newStatus.position) {
197
+ return true;
198
+ }
199
+ return false;
200
+ }
201
+ /**
202
+ * Calculates next polling interval with jitter.
203
+ *
204
+ * @internal
205
+ */
206
+ calculateNextInterval(baseInterval, context) {
207
+ const jitterRange = baseInterval * context.options.jitter;
208
+ const jitter = (Math.random() - 0.5) * 2 * jitterRange;
209
+ return Math.max(0, baseInterval + jitter);
210
+ }
211
+ /**
212
+ * Waits for specified duration or until aborted.
213
+ *
214
+ * @internal
215
+ */
216
+ async wait(ms) {
217
+ return new Promise((resolve, reject) => {
218
+ const timeoutId = setTimeout(resolve, ms);
219
+ this.abortController?.signal.addEventListener("abort", () => {
220
+ clearTimeout(timeoutId);
221
+ reject(new Error("Polling cancelled"));
222
+ });
223
+ this.pollIntervalId = timeoutId;
224
+ });
225
+ }
226
+ /**
227
+ * Creates a timeout promise that rejects after configured duration.
228
+ *
229
+ * @internal
230
+ */
231
+ createTimeoutPromise(context) {
232
+ return new Promise((_, reject) => {
233
+ this.timeoutId = setTimeout(() => {
234
+ reject(
235
+ new import_errors.TransactionPendingError(
236
+ context.operationId,
237
+ `Polling timeout after ${context.options.timeout}ms`,
238
+ this.getLastKnownStatus(context)
239
+ )
240
+ );
241
+ }, context.options.timeout);
242
+ });
243
+ }
244
+ /**
245
+ * Determines if an error is retryable.
246
+ *
247
+ * @internal
248
+ */
249
+ isRetryableError(error) {
250
+ if (!(error instanceof Error)) return false;
251
+ const message = error.message.toLowerCase();
252
+ if (message.includes("network") || message.includes("fetch") || message.includes("timeout") || message.includes("econnrefused") || message.includes("enotfound")) {
253
+ return true;
254
+ }
255
+ if (message.includes("500") || message.includes("502") || message.includes("503") || message.includes("504")) {
256
+ return true;
257
+ }
258
+ return false;
259
+ }
260
+ /**
261
+ * Gets last known status for error reporting.
262
+ *
263
+ * @internal
264
+ */
265
+ getLastKnownStatus(context) {
266
+ return { type: "pending", operationId: context.operationId };
267
+ }
268
+ /**
269
+ * Cleans up resources.
270
+ *
271
+ * @internal
272
+ */
273
+ cleanup() {
274
+ if (this.timeoutId) {
275
+ clearTimeout(this.timeoutId);
276
+ this.timeoutId = void 0;
277
+ }
278
+ if (this.pollIntervalId) {
279
+ clearTimeout(this.pollIntervalId);
280
+ this.pollIntervalId = void 0;
281
+ }
282
+ if (this.abortController) {
283
+ this.abortController.abort();
284
+ this.abortController = void 0;
285
+ }
286
+ }
287
+ }
288
+ // Annotate the CommonJS export names for ESM import in node:
289
+ 0 && (module.exports = {
290
+ PollingManager
291
+ });
292
+ //# sourceMappingURL=pollingManager.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/pollingManager.ts"],"sourcesContent":["/**\n * Internal polling manager for asynchronous relayer operations.\n *\n * @internal\n * @remarks\n * This module handles client-side polling for long-running relayer operations\n * with exponential backoff, jitter, and cancellation support.\n */\n\nimport type {\n UnifiedRelayerRequest,\n UnifiedRelayerResponse,\n} from \"../types/relayer\";\nimport type { OperationStatus } from \"../types/options\";\nimport type { Hash, TransactionReceipt } from \"viem\";\nimport { TransactionPendingError } from \"../errors\";\n\n/**\n * Configuration options for polling behavior.\n *\n * @internal\n */\nexport interface PollingOptions {\n /** Total timeout in milliseconds before giving up (default: 300000ms = 5 min) */\n timeout?: number;\n /** Initial polling interval in milliseconds (default: 1000ms) */\n initialInterval?: number;\n /** Maximum polling interval in milliseconds (default: 10000ms) */\n maxInterval?: number;\n /** Backoff multiplier for each retry (default: 1.5) */\n backoffMultiplier?: number;\n /** Jitter factor to prevent thundering herd (default: 0.2 = 20%) */\n jitter?: number;\n}\n\n/**\n * Context for a polling operation.\n *\n * @internal\n */\ninterface PollingContext {\n operationId: string;\n signal?: AbortSignal;\n onStatusUpdate?: (status: OperationStatus) => void;\n relayerCallback: (\n request: UnifiedRelayerRequest,\n ) => Promise<UnifiedRelayerResponse>;\n options: Required<PollingOptions>;\n}\n\n/**\n * Default polling configuration based on production load testing.\n *\n * @internal\n */\nconst DEFAULT_POLLING_OPTIONS: Required<PollingOptions> = {\n timeout: 300000, // 5 minutes\n initialInterval: 1000, // 1 second\n maxInterval: 10000, // 10 seconds\n backoffMultiplier: 1.5, // 1s -> 1.5s -> 2.25s -> 3.375s -> 5s -> 7.5s -> 10s\n jitter: 0.2, // 20% randomization\n};\n\n/**\n * Internal polling manager that handles asynchronous relayer operations.\n *\n * @internal\n * @remarks\n * This class implements exponential backoff with jitter to prevent server\n * overload while maintaining responsive status updates. It's designed to\n * be resilient to network failures and browser tab closures.\n */\nexport class PollingManager {\n private abortController?: AbortController;\n private timeoutId?: NodeJS.Timeout | number;\n private pollIntervalId?: NodeJS.Timeout | number;\n\n constructor(\n private readonly relayerCallback: (\n request: UnifiedRelayerRequest,\n ) => Promise<UnifiedRelayerResponse>,\n ) {}\n\n /**\n * Starts polling for an operation's status.\n *\n * @param operationId - The operation ID to poll for\n * @param options - Polling configuration and callbacks\n * @returns Promise that resolves when operation completes or fails\n * @throws TransactionPendingError if polling times out\n * @throws Error if operation fails or is cancelled\n */\n async startPolling(\n operationId: string,\n options: {\n signal?: AbortSignal;\n onStatusUpdate?: (status: OperationStatus) => void;\n } & Partial<PollingOptions> = {},\n ): Promise<{ hash: Hash; receipt?: TransactionReceipt }> {\n const context: PollingContext = {\n operationId,\n signal: options.signal,\n onStatusUpdate: options.onStatusUpdate,\n relayerCallback: this.relayerCallback,\n options: {\n ...DEFAULT_POLLING_OPTIONS,\n ...options,\n },\n };\n\n // Set up abort handling\n this.abortController = new AbortController();\n if (context.signal) {\n context.signal.addEventListener(\"abort\", () => {\n this.abortController?.abort();\n });\n }\n\n // Set up timeout\n const timeoutPromise = this.createTimeoutPromise(context);\n\n // Start polling\n const pollingPromise = this.poll(context);\n\n try {\n // Race between polling completion and timeout\n const result = await Promise.race([pollingPromise, timeoutPromise]);\n\n // Clean up\n this.cleanup();\n\n return result;\n } catch (error) {\n // Clean up on error\n this.cleanup();\n\n // Preserve operationId in error for recovery\n if (error instanceof Error && !error.message.includes(\"operationId\")) {\n throw new TransactionPendingError(\n operationId,\n error.message,\n this.getLastKnownStatus(context),\n );\n }\n\n throw error;\n }\n }\n\n /**\n * Main polling loop with exponential backoff.\n *\n * @internal\n */\n private async poll(\n context: PollingContext,\n ): Promise<{ hash: Hash; receipt?: TransactionReceipt }> {\n let currentInterval = context.options.initialInterval;\n let lastStatus: OperationStatus | null = null;\n let retryCount = 0;\n\n while (!this.abortController?.signal.aborted) {\n try {\n // Check operation status\n const response = await this.checkStatus(context);\n\n // Process response\n const status = this.mapResponseToStatus(response, context.operationId);\n\n // Notify if status changed\n if (this.hasStatusChanged(lastStatus, status)) {\n context.onStatusUpdate?.(status);\n lastStatus = status;\n }\n\n // Check for terminal states\n if (status.type === \"confirmed\") {\n return {\n hash: status.hash,\n receipt: status.receipt as TransactionReceipt | undefined,\n };\n }\n\n if (status.type === \"failed\") {\n throw new Error(status.error);\n }\n\n // Reset retry count on successful poll\n retryCount = 0;\n } catch (error) {\n // Network errors are retryable\n if (this.isRetryableError(error)) {\n retryCount++;\n if (retryCount > 5) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n throw new Error(\n `Failed to poll after ${retryCount} attempts: ${errorMessage}`,\n );\n }\n } else {\n // Non-retryable errors should bubble up\n throw error;\n }\n }\n\n // Wait before next poll with exponential backoff\n await this.wait(this.calculateNextInterval(currentInterval, context));\n\n // Increase interval for next iteration\n currentInterval = Math.min(\n currentInterval * context.options.backoffMultiplier,\n context.options.maxInterval,\n );\n }\n\n // Aborted\n throw new Error(\"Polling cancelled\");\n }\n\n /**\n * Checks the current status of an operation.\n *\n * @internal\n */\n private async checkStatus(\n context: PollingContext,\n ): Promise<UnifiedRelayerResponse> {\n const request: UnifiedRelayerRequest = {\n type: \"status_check\",\n operationId: context.operationId,\n };\n\n return await context.relayerCallback(request);\n }\n\n /**\n * Maps relayer response to operation status.\n *\n * @internal\n */\n private mapResponseToStatus(\n response: UnifiedRelayerResponse,\n operationId: string,\n ): OperationStatus {\n switch (response.type) {\n case \"pending\":\n return { type: \"pending\", operationId };\n\n case \"submitted\":\n return { type: \"submitted\", hash: response.hash };\n\n case \"confirmed\":\n return {\n type: \"confirmed\",\n hash: response.hash,\n receipt: response.receipt,\n };\n\n case \"error\":\n return {\n type: \"failed\",\n error: response.error,\n operationId,\n };\n\n case \"direct\":\n // Direct responses during polling might include queue info\n const result = response.result as any;\n if (result?.status === \"queued\") {\n return {\n type: \"queued\",\n position: result.position,\n estimatedWait: result.estimatedWait,\n };\n }\n if (result?.status === \"processing\") {\n return { type: \"processing\" };\n }\n // Fallback\n return { type: \"pending\", operationId };\n\n default:\n return { type: \"pending\", operationId };\n }\n }\n\n /**\n * Determines if status has changed meaningfully.\n *\n * @internal\n */\n private hasStatusChanged(\n oldStatus: OperationStatus | null,\n newStatus: OperationStatus,\n ): boolean {\n if (!oldStatus) return true;\n\n // Check type change\n if (oldStatus.type !== newStatus.type) return true;\n\n // Check queue position change\n if (\n oldStatus.type === \"queued\" &&\n newStatus.type === \"queued\" &&\n oldStatus.position !== newStatus.position\n ) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Calculates next polling interval with jitter.\n *\n * @internal\n */\n private calculateNextInterval(\n baseInterval: number,\n context: PollingContext,\n ): number {\n const jitterRange = baseInterval * context.options.jitter;\n const jitter = (Math.random() - 0.5) * 2 * jitterRange;\n return Math.max(0, baseInterval + jitter);\n }\n\n /**\n * Waits for specified duration or until aborted.\n *\n * @internal\n */\n private async wait(ms: number): Promise<void> {\n return new Promise((resolve, reject) => {\n const timeoutId = setTimeout(resolve, ms);\n\n // Handle abort\n this.abortController?.signal.addEventListener(\"abort\", () => {\n clearTimeout(timeoutId);\n reject(new Error(\"Polling cancelled\"));\n });\n\n this.pollIntervalId = timeoutId as any;\n });\n }\n\n /**\n * Creates a timeout promise that rejects after configured duration.\n *\n * @internal\n */\n private createTimeoutPromise(context: PollingContext): Promise<never> {\n return new Promise((_, reject) => {\n this.timeoutId = setTimeout(() => {\n reject(\n new TransactionPendingError(\n context.operationId,\n `Polling timeout after ${context.options.timeout}ms`,\n this.getLastKnownStatus(context),\n ),\n );\n }, context.options.timeout) as any;\n });\n }\n\n /**\n * Determines if an error is retryable.\n *\n * @internal\n */\n private isRetryableError(error: unknown): boolean {\n if (!(error instanceof Error)) return false;\n\n const message = error.message.toLowerCase();\n\n // Network errors\n if (\n message.includes(\"network\") ||\n message.includes(\"fetch\") ||\n message.includes(\"timeout\") ||\n message.includes(\"econnrefused\") ||\n message.includes(\"enotfound\")\n ) {\n return true;\n }\n\n // Server errors (5xx)\n if (\n message.includes(\"500\") ||\n message.includes(\"502\") ||\n message.includes(\"503\") ||\n message.includes(\"504\")\n ) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Gets last known status for error reporting.\n *\n * @internal\n */\n private getLastKnownStatus(\n context: PollingContext,\n ): OperationStatus | undefined {\n // In a real implementation, we'd track this\n return { type: \"pending\", operationId: context.operationId };\n }\n\n /**\n * Cleans up resources.\n *\n * @internal\n */\n private cleanup(): void {\n if (this.timeoutId) {\n clearTimeout(this.timeoutId as any);\n this.timeoutId = undefined;\n }\n\n if (this.pollIntervalId) {\n clearTimeout(this.pollIntervalId as any);\n this.pollIntervalId = undefined;\n }\n\n if (this.abortController) {\n this.abortController.abort();\n this.abortController = undefined;\n }\n }\n}\n"],"mappings":"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","names":[]}