@jinn-network/client 0.1.6 → 0.1.7

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 (288) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/deployments/deployment-jinn-mvi-l1-sepolia-fast.json +23 -4
  3. package/deployments/deployment-jinn-mvi-l1-sepolia.json +23 -4
  4. package/deployments/deployment-jinn-mvi-l2-baseSepolia.json +5 -4
  5. package/dist/adapters/mech/adapter.d.ts +38 -1
  6. package/dist/adapters/mech/adapter.js +241 -54
  7. package/dist/adapters/mech/adapter.js.map +1 -1
  8. package/dist/adapters/mech/contracts.d.ts +17 -4
  9. package/dist/adapters/mech/contracts.js +8 -2
  10. package/dist/adapters/mech/contracts.js.map +1 -1
  11. package/dist/adapters/mech/safe-revert.d.ts +20 -0
  12. package/dist/adapters/mech/safe-revert.js +12 -4
  13. package/dist/adapters/mech/safe-revert.js.map +1 -1
  14. package/dist/adapters/mech/safe.d.ts +5 -1
  15. package/dist/adapters/mech/safe.js +27 -8
  16. package/dist/adapters/mech/safe.js.map +1 -1
  17. package/dist/adapters/mech/verdict-code.d.ts +1 -0
  18. package/dist/adapters/mech/verdict-code.js +18 -0
  19. package/dist/adapters/mech/verdict-code.js.map +1 -1
  20. package/dist/api/admin-endpoint.d.ts +15 -3
  21. package/dist/api/admin-endpoint.js +24 -2
  22. package/dist/api/admin-endpoint.js.map +1 -1
  23. package/dist/api/bootstrap-endpoint.js +49 -0
  24. package/dist/api/bootstrap-endpoint.js.map +1 -1
  25. package/dist/api/codex-doctor-endpoint.d.ts +73 -0
  26. package/dist/api/codex-doctor-endpoint.js +177 -0
  27. package/dist/api/codex-doctor-endpoint.js.map +1 -0
  28. package/dist/api/discovery-endpoint.d.ts +1 -0
  29. package/dist/api/discovery-endpoint.js +26 -0
  30. package/dist/api/discovery-endpoint.js.map +1 -1
  31. package/dist/api/fleet-build.d.ts +1 -0
  32. package/dist/api/fleet-build.js +2 -1
  33. package/dist/api/fleet-build.js.map +1 -1
  34. package/dist/api/gather-status.d.ts +11 -0
  35. package/dist/api/gather-status.js +400 -4
  36. package/dist/api/gather-status.js.map +1 -1
  37. package/dist/api/hermes-doctor-endpoint.d.ts +117 -0
  38. package/dist/api/hermes-doctor-endpoint.js +229 -23
  39. package/dist/api/hermes-doctor-endpoint.js.map +1 -1
  40. package/dist/api/launcher-status.d.ts +21 -16
  41. package/dist/api/launcher-status.js +2 -1
  42. package/dist/api/launcher-status.js.map +1 -1
  43. package/dist/api/portfolio-v0-build.d.ts +10 -0
  44. package/dist/api/portfolio-v0-build.js +24 -5
  45. package/dist/api/portfolio-v0-build.js.map +1 -1
  46. package/dist/api/prediction-v1-build.d.ts +10 -0
  47. package/dist/api/prediction-v1-build.js +7 -1
  48. package/dist/api/prediction-v1-build.js.map +1 -1
  49. package/dist/api/server.d.ts +31 -1
  50. package/dist/api/server.js +68 -1
  51. package/dist/api/server.js.map +1 -1
  52. package/dist/api/setup-endpoints.d.ts +16 -0
  53. package/dist/api/setup-endpoints.js +78 -4
  54. package/dist/api/setup-endpoints.js.map +1 -1
  55. package/dist/api/setup-retry-endpoint.d.ts +19 -0
  56. package/dist/api/setup-retry-endpoint.js +32 -0
  57. package/dist/api/setup-retry-endpoint.js.map +1 -0
  58. package/dist/api/solvernets-endpoints.d.ts +8 -0
  59. package/dist/api/solvernets-endpoints.js +71 -43
  60. package/dist/api/solvernets-endpoints.js.map +1 -1
  61. package/dist/api/status-build.d.ts +72 -0
  62. package/dist/api/status-build.js +73 -18
  63. package/dist/api/status-build.js.map +1 -1
  64. package/dist/api/task-run-routing.d.ts +7 -0
  65. package/dist/api/task-run-routing.js +12 -0
  66. package/dist/api/task-run-routing.js.map +1 -0
  67. package/dist/api/task-runs-build.d.ts +21 -0
  68. package/dist/api/task-runs-build.js +14 -1
  69. package/dist/api/task-runs-build.js.map +1 -1
  70. package/dist/build-info.json +4 -4
  71. package/dist/build-meta.json +1 -1
  72. package/dist/chain-read-errors.d.ts +10 -0
  73. package/dist/chain-read-errors.js +15 -0
  74. package/dist/chain-read-errors.js.map +1 -1
  75. package/dist/cli/commands/auth.js +1 -1
  76. package/dist/cli/commands/auth.js.map +1 -1
  77. package/dist/cli/commands/create.js +3 -2
  78. package/dist/cli/commands/create.js.map +1 -1
  79. package/dist/cli/commands/doctor.d.ts +2 -0
  80. package/dist/cli/commands/doctor.js +2 -0
  81. package/dist/cli/commands/doctor.js.map +1 -1
  82. package/dist/cli/commands/rewards.js +11 -7
  83. package/dist/cli/commands/rewards.js.map +1 -1
  84. package/dist/cli/commands/solver-nets.js +24 -9
  85. package/dist/cli/commands/solver-nets.js.map +1 -1
  86. package/dist/cli/commands/status.js +1 -1
  87. package/dist/cli/commands/status.js.map +1 -1
  88. package/dist/cli/commands/tasks.js +86 -9
  89. package/dist/cli/commands/tasks.js.map +1 -1
  90. package/dist/cli/commands/update.d.ts +10 -0
  91. package/dist/cli/commands/update.js +36 -0
  92. package/dist/cli/commands/update.js.map +1 -1
  93. package/dist/cli/introspection-context.js +5 -0
  94. package/dist/cli/introspection-context.js.map +1 -1
  95. package/dist/cli/task-native-readiness.d.ts +3 -1
  96. package/dist/cli/task-native-readiness.js +28 -6
  97. package/dist/cli/task-native-readiness.js.map +1 -1
  98. package/dist/config.d.ts +106 -5
  99. package/dist/config.js +97 -18
  100. package/dist/config.js.map +1 -1
  101. package/dist/daemon/checkpoint-loop.d.ts +48 -0
  102. package/dist/daemon/checkpoint-loop.js +76 -0
  103. package/dist/daemon/checkpoint-loop.js.map +1 -0
  104. package/dist/daemon/creator.d.ts +1 -1
  105. package/dist/daemon/creator.js +7 -3
  106. package/dist/daemon/creator.js.map +1 -1
  107. package/dist/daemon/daemon.d.ts +19 -0
  108. package/dist/daemon/daemon.js +68 -1
  109. package/dist/daemon/daemon.js.map +1 -1
  110. package/dist/daemon/eviction-loop.d.ts +40 -0
  111. package/dist/daemon/eviction-loop.js +67 -0
  112. package/dist/daemon/eviction-loop.js.map +1 -0
  113. package/dist/daemon/jinn-claim-loop-wiring.d.ts +33 -0
  114. package/dist/daemon/jinn-claim-loop-wiring.js +40 -0
  115. package/dist/daemon/jinn-claim-loop-wiring.js.map +1 -0
  116. package/dist/daemon/jinn-claim-loop.d.ts +24 -17
  117. package/dist/daemon/jinn-claim-loop.js +77 -23
  118. package/dist/daemon/jinn-claim-loop.js.map +1 -1
  119. package/dist/daemon/skip-log-dedup.d.ts +69 -0
  120. package/dist/daemon/skip-log-dedup.js +106 -0
  121. package/dist/daemon/skip-log-dedup.js.map +1 -0
  122. package/dist/dashboard/assets/index-BUlE8F3Y.js +330 -0
  123. package/dist/dashboard/assets/index-blqc7eqq.css +32 -0
  124. package/dist/dashboard/index.html +2 -2
  125. package/dist/discovery/factory.d.ts +17 -5
  126. package/dist/discovery/factory.js +46 -18
  127. package/dist/discovery/factory.js.map +1 -1
  128. package/dist/discovery/http.js +142 -3
  129. package/dist/discovery/http.js.map +1 -1
  130. package/dist/discovery/onchain.d.ts +5 -0
  131. package/dist/discovery/onchain.js +407 -15
  132. package/dist/discovery/onchain.js.map +1 -1
  133. package/dist/discovery/types.d.ts +45 -1
  134. package/dist/discovery/types.js +8 -10
  135. package/dist/discovery/types.js.map +1 -1
  136. package/dist/discovery/with-fallback.d.ts +7 -0
  137. package/dist/discovery/with-fallback.js +10 -0
  138. package/dist/discovery/with-fallback.js.map +1 -1
  139. package/dist/earning/bootstrap.d.ts +92 -1
  140. package/dist/earning/bootstrap.js +203 -63
  141. package/dist/earning/bootstrap.js.map +1 -1
  142. package/dist/earning/contracts.d.ts +14 -0
  143. package/dist/earning/contracts.js +17 -5
  144. package/dist/earning/contracts.js.map +1 -1
  145. package/dist/earning/funding-plan.js +27 -18
  146. package/dist/earning/funding-plan.js.map +1 -1
  147. package/dist/earning/jinn-rewards.d.ts +46 -0
  148. package/dist/earning/jinn-rewards.js +32 -0
  149. package/dist/earning/jinn-rewards.js.map +1 -1
  150. package/dist/earning/safe-adapter.d.ts +2 -0
  151. package/dist/earning/safe-adapter.js +26 -12
  152. package/dist/earning/safe-adapter.js.map +1 -1
  153. package/dist/earning/store.d.ts +8 -0
  154. package/dist/earning/store.js.map +1 -1
  155. package/dist/earning/testnet-setup-migration.d.ts +12 -0
  156. package/dist/earning/testnet-setup-migration.js +27 -1
  157. package/dist/earning/testnet-setup-migration.js.map +1 -1
  158. package/dist/earning/types.d.ts +15 -0
  159. package/dist/erc8004/reputation.d.ts +8 -0
  160. package/dist/erc8004/reputation.js +22 -3
  161. package/dist/erc8004/reputation.js.map +1 -1
  162. package/dist/harnesses/cost-estimates.d.ts +145 -0
  163. package/dist/harnesses/cost-estimates.js +297 -0
  164. package/dist/harnesses/cost-estimates.js.map +1 -0
  165. package/dist/harnesses/engine/engine.d.ts +72 -0
  166. package/dist/harnesses/engine/engine.js +105 -8
  167. package/dist/harnesses/engine/engine.js.map +1 -1
  168. package/dist/harnesses/engine/persistence.d.ts +51 -1
  169. package/dist/harnesses/engine/persistence.js +118 -5
  170. package/dist/harnesses/engine/persistence.js.map +1 -1
  171. package/dist/harnesses/engine/work-dir-reaper.d.ts +65 -0
  172. package/dist/harnesses/engine/work-dir-reaper.js +100 -0
  173. package/dist/harnesses/engine/work-dir-reaper.js.map +1 -0
  174. package/dist/harnesses/impls/hermes-agent/adapter.js +40 -0
  175. package/dist/harnesses/impls/hermes-agent/adapter.js.map +1 -1
  176. package/dist/harnesses/impls/hermes-agent/bootstrap.d.ts +20 -0
  177. package/dist/harnesses/impls/hermes-agent/bootstrap.js +40 -6
  178. package/dist/harnesses/impls/hermes-agent/bootstrap.js.map +1 -1
  179. package/dist/harnesses/impls/hermes-agent/harness.d.ts +59 -1
  180. package/dist/harnesses/impls/hermes-agent/harness.js +104 -0
  181. package/dist/harnesses/impls/hermes-agent/harness.js.map +1 -1
  182. package/dist/harnesses/impls/index.d.ts +7 -0
  183. package/dist/harnesses/impls/index.js +16 -1
  184. package/dist/harnesses/impls/index.js.map +1 -1
  185. package/dist/harnesses/impls/learner/harness.d.ts +38 -4
  186. package/dist/harnesses/impls/learner/harness.js +96 -2
  187. package/dist/harnesses/impls/learner/harness.js.map +1 -1
  188. package/dist/harnesses/impls/learner/plugin-path.d.ts +0 -13
  189. package/dist/harnesses/impls/learner/plugin-path.js +35 -15
  190. package/dist/harnesses/impls/learner/plugin-path.js.map +1 -1
  191. package/dist/harnesses/impls/learner/types.d.ts +11 -0
  192. package/dist/harnesses/impls/stub.d.ts +58 -0
  193. package/dist/harnesses/impls/stub.js +89 -0
  194. package/dist/harnesses/impls/stub.js.map +1 -0
  195. package/dist/harnesses/impls/swe-rebench-v2-evaluator/eval-runner.d.ts +69 -50
  196. package/dist/harnesses/impls/swe-rebench-v2-evaluator/eval-runner.js +178 -93
  197. package/dist/harnesses/impls/swe-rebench-v2-evaluator/eval-runner.js.map +1 -1
  198. package/dist/harnesses/impls/swe-rebench-v2-evaluator/harness.d.ts +12 -1
  199. package/dist/harnesses/impls/swe-rebench-v2-evaluator/harness.js +121 -7
  200. package/dist/harnesses/impls/swe-rebench-v2-evaluator/harness.js.map +1 -1
  201. package/dist/harnesses/impls/swe-rebench-v2-evaluator/hf-fetcher.d.ts +15 -0
  202. package/dist/harnesses/impls/swe-rebench-v2-evaluator/hf-fetcher.js +54 -4
  203. package/dist/harnesses/impls/swe-rebench-v2-evaluator/hf-fetcher.js.map +1 -1
  204. package/dist/harnesses/impls/swe-rebench-v2-evaluator/index.d.ts +6 -0
  205. package/dist/harnesses/impls/swe-rebench-v2-evaluator/index.js +1 -1
  206. package/dist/harnesses/impls/swe-rebench-v2-evaluator/index.js.map +1 -1
  207. package/dist/harnesses/readiness-registry.js +9 -1
  208. package/dist/harnesses/readiness-registry.js.map +1 -1
  209. package/dist/main.js +371 -82
  210. package/dist/main.js.map +1 -1
  211. package/dist/observability/emit-event.d.ts +1 -1
  212. package/dist/observability/emit-event.js.map +1 -1
  213. package/dist/operator-errors.d.ts +7 -0
  214. package/dist/operator-errors.js +13 -1
  215. package/dist/operator-errors.js.map +1 -1
  216. package/dist/plugins/learner/.claude-plugin/plugin.json +9 -0
  217. package/dist/plugins/learner/.codex-plugin/plugin.json +39 -0
  218. package/dist/plugins/learner/AGENTS.md +40 -0
  219. package/dist/plugins/learner/CLAUDE.md +33 -0
  220. package/dist/plugins/learner/README.md +59 -0
  221. package/dist/plugins/learner/hooks/hooks.json +16 -0
  222. package/dist/plugins/learner/hooks/session-start +38 -0
  223. package/dist/plugins/learner/skills/learn/SKILL.md +412 -0
  224. package/dist/plugins/learner/skills/learn/analyst-prompt.md +68 -0
  225. package/dist/plugins/learner/skills/learn/consolidator-prompt.md +94 -0
  226. package/dist/plugins/learner/skills/learn/explorer-prompt.md +53 -0
  227. package/dist/plugins/learner/skills/learn/planner-prompt.md +87 -0
  228. package/dist/plugins/learner/skills/learn/promoter-prompt.md +113 -0
  229. package/dist/plugins/learner/skills/learn/step-worker-prompt.md +47 -0
  230. package/dist/plugins/learner/skills/learn/strategist-prompt.md +85 -0
  231. package/dist/restart-daemon.d.ts +90 -0
  232. package/dist/restart-daemon.js +95 -0
  233. package/dist/restart-daemon.js.map +1 -0
  234. package/dist/setup/halt-mode.d.ts +14 -0
  235. package/dist/setup/halt-mode.js +17 -0
  236. package/dist/setup/halt-mode.js.map +1 -0
  237. package/dist/solver-nets/prediction-operator-ux.js +43 -3
  238. package/dist/solver-nets/prediction-operator-ux.js.map +1 -1
  239. package/dist/solver-nets/registry.d.ts +1 -0
  240. package/dist/solver-nets/registry.js +1 -1
  241. package/dist/solver-nets/registry.js.map +1 -1
  242. package/dist/solver-types/_swe-rebench-v2-pool-cache.d.ts +58 -0
  243. package/dist/solver-types/_swe-rebench-v2-pool-cache.js +87 -0
  244. package/dist/solver-types/_swe-rebench-v2-pool-cache.js.map +1 -0
  245. package/dist/solver-types/_swe-rebench-v2-substrate.d.ts +1 -0
  246. package/dist/solver-types/_swe-rebench-v2-substrate.js +10 -0
  247. package/dist/solver-types/_swe-rebench-v2-substrate.js.map +1 -1
  248. package/dist/solver-types/_swe-rebench-v2-validated-pool.d.ts +65 -0
  249. package/dist/solver-types/_swe-rebench-v2-validated-pool.js +243 -26
  250. package/dist/solver-types/_swe-rebench-v2-validated-pool.js.map +1 -1
  251. package/dist/solver-types/swe-rebench-v2-auto.d.ts +22 -7
  252. package/dist/solver-types/swe-rebench-v2-auto.js +45 -20
  253. package/dist/solver-types/swe-rebench-v2-auto.js.map +1 -1
  254. package/dist/solver-types/swe-rebench-v2.d.ts +13 -2
  255. package/dist/solver-types/swe-rebench-v2.js +233 -94
  256. package/dist/solver-types/swe-rebench-v2.js.map +1 -1
  257. package/dist/solvernets/daemon-init.d.ts +10 -2
  258. package/dist/solvernets/daemon-init.js +22 -2
  259. package/dist/solvernets/daemon-init.js.map +1 -1
  260. package/dist/solvernets/launched-record-dispatcher.js +35 -7
  261. package/dist/solvernets/launched-record-dispatcher.js.map +1 -1
  262. package/dist/solvernets/store.d.ts +5 -0
  263. package/dist/solvernets/store.js +1 -0
  264. package/dist/solvernets/store.js.map +1 -1
  265. package/dist/store/store.d.ts +15 -0
  266. package/dist/store/store.js +118 -3
  267. package/dist/store/store.js.map +1 -1
  268. package/dist/tasks/sources.d.ts +18 -1
  269. package/dist/tasks/sources.js +33 -5
  270. package/dist/tasks/sources.js.map +1 -1
  271. package/dist/tx-retry.d.ts +151 -19
  272. package/dist/tx-retry.js +286 -32
  273. package/dist/tx-retry.js.map +1 -1
  274. package/dist/types/payloads/prediction-apy-v0.d.ts +5 -5
  275. package/dist/types/payloads/prediction-v0.d.ts +5 -5
  276. package/dist/types/task-document.d.ts +392 -0
  277. package/dist/types/task-document.js +10 -0
  278. package/dist/types/task-document.js.map +1 -1
  279. package/dist/types/task.d.ts +28 -0
  280. package/dist/util/extract-tx-hash.d.ts +14 -0
  281. package/dist/util/extract-tx-hash.js +19 -0
  282. package/dist/util/extract-tx-hash.js.map +1 -0
  283. package/dist/vendor/@jinn-network/sdk/dist/contracts.js +1 -1
  284. package/dist/vendor/@jinn-network/sdk/dist/solvernets/manifest-schema.d.ts +3 -0
  285. package/dist/vendor/@jinn-network/sdk/dist/solvernets/manifest-schema.js +1 -0
  286. package/package.json +29 -12
  287. package/dist/dashboard/assets/index-DOlzFN8a.css +0 -32
  288. package/dist/dashboard/assets/index-NkZ7CTAT.js +0 -140
@@ -13,22 +13,50 @@ export class StaticConfiguredTaskSource {
13
13
  }));
14
14
  }
15
15
  }
16
+ /**
17
+ * Filter a config-level tasks[] array to only entries that can be posted via
18
+ * the production adapter (i.e. those with a solverNetManifestCid).
19
+ *
20
+ * This is the guard for issue #415: a tasks[] entry without a manifest CID
21
+ * will always throw PermanentError in signTaskDocument, causing the creator loop
22
+ * to backoff and retry indefinitely. Filtering here prevents the entry from ever
23
+ * entering the posting cycle and emits a one-time warning per dropped entry at
24
+ * startup.
25
+ *
26
+ * Only call this on the config-level tasks[] array (the production path). Do not
27
+ * apply it to Task objects created in tests or via other in-memory sources.
28
+ */
29
+ export function filterBindableTasks(tasks) {
30
+ return tasks.filter((task) => {
31
+ if (!task.solverNetManifestCid) {
32
+ console.warn(`[creator] Skipping configured task "${task.id}": missing solverNetManifestCid. ` +
33
+ `Legacy tasks[] entries without a SolverNet manifest binding can never be posted ` +
34
+ `(spec §14 BINDING rule). Remove this entry from your config or add a solverNetManifestCid.`);
35
+ return false;
36
+ }
37
+ return true;
38
+ });
39
+ }
16
40
  export class GeneratedTaskSource {
17
41
  sourceKey;
18
42
  generator;
19
- constructor(sourceKey, generator) {
43
+ opts;
44
+ constructor(sourceKey, generator, opts = {}) {
20
45
  this.sourceKey = sourceKey;
21
46
  this.generator = generator;
47
+ this.opts = opts;
22
48
  }
23
49
  async collect(_now) {
24
50
  const generated = await this.generator();
25
51
  if (!generated)
26
52
  return [];
27
53
  const tasks = Array.isArray(generated) ? generated : [generated];
28
- return tasks.map((task) => {
29
- const bucketKey = task.window
30
- ? `${task.window.startTs}:${task.window.endTs}`
31
- : task.id;
54
+ return tasks.map((task, index) => {
55
+ const overrideBucketKey = this.opts.bucketKeyForTask?.(task, index);
56
+ const bucketKey = overrideBucketKey
57
+ ?? (task.window
58
+ ? `${task.window.startTs}:${task.window.endTs}`
59
+ : task.id);
32
60
  return {
33
61
  task,
34
62
  sourceKey: this.sourceKey,
@@ -1 +1 @@
1
- {"version":3,"file":"sources.js","sourceRoot":"","sources":["../../src/tasks/sources.ts"],"names":[],"mappings":"AAiCA,MAAM,OAAO,0BAA0B;IAGR;IAFpB,SAAS,GAAG,YAAY,CAAC;IAElC,YAA6B,KAAa;QAAb,UAAK,GAAL,KAAK,CAAQ;IAAG,CAAC;IAE9C,KAAK,CAAC,OAAO,CAAC,IAAU;QACtB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAC/B,IAAI;YACJ,SAAS,EAAE,GAAG,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,EAAE,EAAE;YACzC,aAAa,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE;YACxC,UAAU,EAAE,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,EAAE,YAAY,EAAE;SAChE,CAAC,CAAC,CAAC;IACN,CAAC;CACF;AAED,MAAM,OAAO,mBAAmB;IAEnB;IACQ;IAFnB,YACW,SAAiB,EACT,SAAwB;QADhC,cAAS,GAAT,SAAS,CAAQ;QACT,cAAS,GAAT,SAAS,CAAe;IACxC,CAAC;IAEJ,KAAK,CAAC,OAAO,CAAC,IAAU;QACtB,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;QACzC,IAAI,CAAC,SAAS;YAAE,OAAO,EAAE,CAAC;QAC1B,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QACjE,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;YACxB,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM;gBAC3B,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE;gBAC/C,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;YACZ,OAAO;gBACL,IAAI;gBACJ,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,aAAa,EAAE,EAAE,IAAI,EAAE,iBAA0B,EAAE,SAAS,EAAE;gBAC9D,UAAU,EAAE;oBACV,UAAU,EAAE,IAAI,CAAC,UAAU;oBAC3B,SAAS;oBACT,IAAI,EAAE,WAAW;iBAClB;aACF,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
1
+ {"version":3,"file":"sources.js","sourceRoot":"","sources":["../../src/tasks/sources.ts"],"names":[],"mappings":"AAiCA,MAAM,OAAO,0BAA0B;IAGR;IAFpB,SAAS,GAAG,YAAY,CAAC;IAElC,YAA6B,KAAa;QAAb,UAAK,GAAL,KAAK,CAAQ;IAAG,CAAC;IAE9C,KAAK,CAAC,OAAO,CAAC,IAAU;QACtB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAC/B,IAAI;YACJ,SAAS,EAAE,GAAG,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,EAAE,EAAE;YACzC,aAAa,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE;YACxC,UAAU,EAAE,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,EAAE,YAAY,EAAE;SAChE,CAAC,CAAC,CAAC;IACN,CAAC;CACF;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAa;IAC/C,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE;QAC3B,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE,CAAC;YAC/B,OAAO,CAAC,IAAI,CACV,uCAAuC,IAAI,CAAC,EAAE,mCAAmC;gBACjF,kFAAkF;gBAClF,4FAA4F,CAC7F,CAAC;YACF,OAAO,KAAK,CAAC;QACf,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,OAAO,mBAAmB;IAEnB;IACQ;IACA;IAHnB,YACW,SAAiB,EACT,SAAwB,EACxB,OAEb,EAAE;QAJG,cAAS,GAAT,SAAS,CAAQ;QACT,cAAS,GAAT,SAAS,CAAe;QACxB,SAAI,GAAJ,IAAI,CAEf;IACL,CAAC;IAEJ,KAAK,CAAC,OAAO,CAAC,IAAU;QACtB,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;QACzC,IAAI,CAAC,SAAS;YAAE,OAAO,EAAE,CAAC;QAC1B,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QACjE,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;YAC/B,MAAM,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YACpE,MAAM,SAAS,GAAG,iBAAiB;mBAC9B,CAAC,IAAI,CAAC,MAAM;oBACb,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE;oBAC/C,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACf,OAAO;gBACL,IAAI;gBACJ,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,aAAa,EAAE,EAAE,IAAI,EAAE,iBAA0B,EAAE,SAAS,EAAE;gBAC9D,UAAU,EAAE;oBACV,UAAU,EAAE,IAAI,CAAC,UAAU;oBAC3B,SAAS;oBACT,IAAI,EAAE,WAAW;iBAClB;aACF,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -8,6 +8,9 @@ export declare const TX_RETRY_DEFAULTS: {
8
8
  readonly maxDelayMs: 12000;
9
9
  /** Extra fee bump per retry attempt after the first (basis points, 1500 = +15%) */
10
10
  readonly feeBumpBpsPerAttempt: 1500;
11
+ /** Minimum bump over the previously submitted fee for same-sender nonce replacement. */
12
+ readonly replacementBumpBps: 1500;
13
+ readonly stuckNonceAfterMs: 120000;
11
14
  };
12
15
  export declare function flattenErrorMessage(error: unknown): string;
13
16
  /**
@@ -21,6 +24,9 @@ export interface TxRetryOptions {
21
24
  maxAttempts?: number;
22
25
  baseDelayMs?: number;
23
26
  maxDelayMs?: number;
27
+ ledger?: TxSubmissionLedger;
28
+ logicalTx?: string;
29
+ stuckNonceAfterMs?: number;
24
30
  /** If set, invoked before each retry (attempt >= 1) for logging/metrics */
25
31
  onRetry?: (info: {
26
32
  attempt: number;
@@ -28,6 +34,119 @@ export interface TxRetryOptions {
28
34
  message: string;
29
35
  }) => void;
30
36
  }
37
+ export interface TxFeeSnapshot {
38
+ maxFeePerGas?: bigint;
39
+ maxPriorityFeePerGas?: bigint;
40
+ gasPrice?: bigint;
41
+ }
42
+ export type Eip1559FeeOverrides = {
43
+ maxFeePerGas: bigint;
44
+ maxPriorityFeePerGas: bigint;
45
+ gasPrice?: undefined;
46
+ };
47
+ export type LegacyFeeOverrides = {
48
+ gasPrice: bigint;
49
+ maxFeePerGas?: undefined;
50
+ maxPriorityFeePerGas?: undefined;
51
+ };
52
+ export type EmptyFeeOverrides = {
53
+ maxFeePerGas?: undefined;
54
+ maxPriorityFeePerGas?: undefined;
55
+ gasPrice?: undefined;
56
+ };
57
+ export type TxFeeOverrides = Eip1559FeeOverrides | LegacyFeeOverrides | EmptyFeeOverrides;
58
+ export type TxFeeOverrideResult = {
59
+ kind: 'eip1559';
60
+ overrides: Eip1559FeeOverrides;
61
+ snapshot: TxFeeSnapshot;
62
+ } | {
63
+ kind: 'legacy';
64
+ overrides: LegacyFeeOverrides;
65
+ snapshot: TxFeeSnapshot;
66
+ } | {
67
+ kind: 'none';
68
+ overrides: EmptyFeeOverrides;
69
+ snapshot: TxFeeSnapshot;
70
+ };
71
+ export type TxRecoveryTransactionRequest = TxFeeOverrides & {
72
+ account?: unknown;
73
+ to: Address;
74
+ value: bigint;
75
+ nonce: number;
76
+ };
77
+ export interface TxRecoveryWalletClient {
78
+ account?: unknown;
79
+ sendTransaction(tx: TxRecoveryTransactionRequest): Promise<Hex>;
80
+ }
81
+ export type TxRetryTransactionRequest = TxFeeOverrides & {
82
+ account?: unknown;
83
+ to?: Address;
84
+ data?: Hex;
85
+ value?: bigint;
86
+ gas?: bigint;
87
+ nonce?: number;
88
+ [key: string]: unknown;
89
+ };
90
+ export interface TxRetryWalletClient {
91
+ account?: unknown;
92
+ sendTransaction(tx: TxRetryTransactionRequest): Promise<Hex>;
93
+ }
94
+ export interface TxSubmissionKey {
95
+ chainId: number;
96
+ from: Address;
97
+ nonce: number;
98
+ }
99
+ export interface TxSubmissionLedgerEntry extends TxSubmissionKey {
100
+ hash?: Hex;
101
+ logicalTx?: string;
102
+ submittedAtMs: number;
103
+ fees: TxFeeSnapshot;
104
+ to?: Address;
105
+ value?: bigint;
106
+ data?: Hex;
107
+ resolvedAtMs?: number | null;
108
+ }
109
+ type MaybePromise<T> = T | Promise<T>;
110
+ export interface TxSubmissionLedger {
111
+ getTxSubmission(key: TxSubmissionKey): MaybePromise<TxSubmissionLedgerEntry | null>;
112
+ recordTxSubmission(entry: TxSubmissionLedgerEntry): MaybePromise<void>;
113
+ markTxSubmissionResolved(key: TxSubmissionKey & {
114
+ resolvedAtMs: number;
115
+ }): MaybePromise<void>;
116
+ }
117
+ export interface NonceLedgerSubmission {
118
+ hash?: Hex;
119
+ logicalTx?: string;
120
+ submittedAtMs?: number;
121
+ fees: TxFeeSnapshot;
122
+ to?: Address;
123
+ value?: bigint;
124
+ data?: Hex;
125
+ }
126
+ export interface NonceLedgerContext {
127
+ ledger: TxSubmissionLedger;
128
+ chainId: number;
129
+ from: Address;
130
+ nonce: number;
131
+ feeResultForAttempt(attemptIndex: number, options?: {
132
+ forceEstimate?: boolean;
133
+ }): Promise<TxFeeOverrideResult>;
134
+ recordSubmitted(entry: NonceLedgerSubmission): Promise<void>;
135
+ markResolved(resolvedAtMs?: number): Promise<void>;
136
+ }
137
+ export interface WithNonceLedgerArgs {
138
+ publicClient: PublicClient;
139
+ walletClient?: TxRecoveryWalletClient;
140
+ ledger?: TxSubmissionLedger;
141
+ from: Address;
142
+ nonce?: number;
143
+ chainId?: number;
144
+ recoverStuckNonce?: boolean;
145
+ staleAfterMs?: number;
146
+ }
147
+ export declare function createMemoryTxSubmissionLedger(): TxSubmissionLedger;
148
+ export declare function setDefaultTxSubmissionLedger(ledger: TxSubmissionLedger): void;
149
+ export declare function getDefaultTxSubmissionLedger(): TxSubmissionLedger;
31
150
  export declare function withRecoverableRetry<T>(fn: (attemptIndex: number) => Promise<T>, options?: TxRetryOptions): Promise<T>;
32
151
  /**
33
152
  * Poll publicClient.getCode until bytecode is present at `address`, or give up.
@@ -42,27 +161,40 @@ export declare function waitForContractCode(publicClient: PublicClient, address:
42
161
  baseDelayMs?: number;
43
162
  maxDelayMs?: number;
44
163
  }): Promise<Hex>;
164
+ export declare function mergeFeeEstimateWithPrevious(current: TxFeeSnapshot, previous?: TxFeeSnapshot | null, attemptIndex?: number): TxFeeSnapshot;
165
+ /**
166
+ * EIP-1559 or legacy gas overrides for viem, increasingly aggressive on later attempts.
167
+ *
168
+ * The result carries both the spreadable viem transaction overrides and the
169
+ * ledger snapshot, keeping call sites from recasting the override object.
170
+ */
171
+ export declare function viemFeeOverrideResultForAttempt(publicClient: PublicClient, attemptIndex: number, options?: {
172
+ previousFees?: TxFeeSnapshot | null;
173
+ forceEstimate?: boolean;
174
+ }): Promise<TxFeeOverrideResult>;
45
175
  /** EIP-1559 or legacy gas overrides for viem, increasingly aggressive on later attempts. */
46
- export declare function viemFeeOverridesForAttempt(publicClient: PublicClient, attemptIndex: number): Promise<{
47
- maxFeePerGas: bigint;
48
- maxPriorityFeePerGas: bigint;
49
- } | {
50
- gasPrice: bigint;
51
- } | Record<string, never>>;
176
+ export declare function viemFeeOverridesForAttempt(publicClient: PublicClient, attemptIndex: number, options?: {
177
+ previousFees?: TxFeeSnapshot | null;
178
+ forceEstimate?: boolean;
179
+ }): Promise<TxFeeOverrides>;
52
180
  export declare function waitForTransactionReceiptWithRetry(publicClient: PublicClient, hash: Hex, options?: TxRetryOptions & {
53
181
  pollingInterval?: number;
54
182
  confirmations?: number;
55
183
  }): Promise<TransactionReceipt>;
56
- export declare function viemSendTransactionWithRetry(walletClient: {
57
- sendTransaction: (tx: any) => Promise<Hex>;
58
- }, publicClient: PublicClient, txRequest: {
59
- account: any;
60
- to?: Address;
61
- data?: Hex;
62
- value?: bigint;
63
- gas?: bigint;
64
- maxFeePerGas?: bigint;
65
- maxPriorityFeePerGas?: bigint;
66
- gasPrice?: bigint;
67
- [key: string]: any;
68
- }, options?: TxRetryOptions): Promise<Hex>;
184
+ export declare function removeConflictingLegacyGasPrice<T extends TxFeeSnapshot>(tx: T): void;
185
+ export declare function withNonceLedger<T>(args: WithNonceLedgerArgs, fn: (context: NonceLedgerContext) => Promise<T>): Promise<T>;
186
+ export declare function recoverStuckNonceIfNeeded(args: {
187
+ publicClient: PublicClient;
188
+ walletClient: TxRecoveryWalletClient;
189
+ ledger?: TxSubmissionLedger;
190
+ from: Address;
191
+ chainId?: number;
192
+ staleAfterMs?: number;
193
+ nowMs?: number;
194
+ }): Promise<{
195
+ nonce: number;
196
+ previousHash?: Hex;
197
+ recoveryHash: Hex;
198
+ } | null>;
199
+ export declare function viemSendTransactionWithRetry(walletClient: TxRetryWalletClient, publicClient: PublicClient, txRequest: TxRetryTransactionRequest, options?: TxRetryOptions): Promise<Hex>;
200
+ export {};
package/dist/tx-retry.js CHANGED
@@ -7,6 +7,9 @@ export const TX_RETRY_DEFAULTS = {
7
7
  maxDelayMs: 12_000,
8
8
  /** Extra fee bump per retry attempt after the first (basis points, 1500 = +15%) */
9
9
  feeBumpBpsPerAttempt: 1500,
10
+ /** Minimum bump over the previously submitted fee for same-sender nonce replacement. */
11
+ replacementBumpBps: 1500,
12
+ stuckNonceAfterMs: 120_000,
10
13
  };
11
14
  export function flattenErrorMessage(error) {
12
15
  if (error === null || error === undefined)
@@ -194,6 +197,30 @@ export async function backoffDelay(attemptIndex, baseMs, maxMs) {
194
197
  const jitter = Math.floor(Math.random() * Math.min(250, baseMs));
195
198
  await sleep(exp + jitter);
196
199
  }
200
+ export function createMemoryTxSubmissionLedger() {
201
+ const entries = new Map();
202
+ const keyFor = (key) => `${key.chainId}:${key.from.toLowerCase()}:${key.nonce}`;
203
+ return {
204
+ getTxSubmission(key) {
205
+ return entries.get(keyFor(key)) ?? null;
206
+ },
207
+ recordTxSubmission(entry) {
208
+ entries.set(keyFor(entry), { ...entry, resolvedAtMs: entry.resolvedAtMs ?? null });
209
+ },
210
+ markTxSubmissionResolved(key) {
211
+ const existing = entries.get(keyFor(key));
212
+ if (existing)
213
+ entries.set(keyFor(key), { ...existing, resolvedAtMs: key.resolvedAtMs });
214
+ },
215
+ };
216
+ }
217
+ let defaultTxSubmissionLedger = createMemoryTxSubmissionLedger();
218
+ export function setDefaultTxSubmissionLedger(ledger) {
219
+ defaultTxSubmissionLedger = ledger;
220
+ }
221
+ export function getDefaultTxSubmissionLedger() {
222
+ return defaultTxSubmissionLedger;
223
+ }
197
224
  export async function withRecoverableRetry(fn, options = {}) {
198
225
  const maxAttempts = options.maxAttempts ?? TX_RETRY_DEFAULTS.maxAttempts;
199
226
  const baseDelayMs = options.baseDelayMs ?? TX_RETRY_DEFAULTS.baseDelayMs;
@@ -237,20 +264,71 @@ export async function waitForContractCode(publicClient, address, options = {}) {
237
264
  }
238
265
  throw new Error(`No contract code at ${address} after ${maxAttempts} getCode attempts`);
239
266
  }
240
- /** EIP-1559 or legacy gas overrides for viem, increasingly aggressive on later attempts. */
241
- export async function viemFeeOverridesForAttempt(publicClient, attemptIndex) {
267
+ function mulDivCeil(value, numerator, denominator) {
268
+ return (value * numerator + denominator - 1n) / denominator;
269
+ }
270
+ function maxBigInt(...values) {
271
+ const present = values.filter((value) => value !== undefined);
272
+ if (present.length === 0)
273
+ return undefined;
274
+ return present.reduce((max, value) => (value > max ? value : max), present[0]);
275
+ }
276
+ function replacementBump(value) {
277
+ return mulDivCeil(value, 10000n + BigInt(TX_RETRY_DEFAULTS.replacementBumpBps), 10000n);
278
+ }
279
+ function attemptBump(value, attemptIndex) {
242
280
  if (attemptIndex <= 0)
243
- return {};
281
+ return value;
244
282
  const bps = BigInt(TX_RETRY_DEFAULTS.feeBumpBpsPerAttempt) * BigInt(attemptIndex);
245
- const mult = 10000n + bps;
246
- const apply = (v) => (v * mult) / 10000n;
283
+ return mulDivCeil(value, 10000n + bps, 10000n);
284
+ }
285
+ export function mergeFeeEstimateWithPrevious(current, previous, attemptIndex = 0) {
286
+ if (current.gasPrice !== undefined || previous?.gasPrice !== undefined) {
287
+ const gasPrice = maxBigInt(current.gasPrice === undefined ? undefined : attemptBump(current.gasPrice, attemptIndex), previous?.gasPrice === undefined ? undefined : replacementBump(previous.gasPrice));
288
+ return gasPrice === undefined ? {} : { gasPrice };
289
+ }
290
+ const maxFeePerGas = maxBigInt(current.maxFeePerGas === undefined ? undefined : attemptBump(current.maxFeePerGas, attemptIndex), previous?.maxFeePerGas === undefined ? undefined : replacementBump(previous.maxFeePerGas));
291
+ const maxPriorityFeePerGas = maxBigInt(current.maxPriorityFeePerGas === undefined
292
+ ? undefined
293
+ : attemptBump(current.maxPriorityFeePerGas, attemptIndex), previous?.maxPriorityFeePerGas === undefined
294
+ ? undefined
295
+ : replacementBump(previous.maxPriorityFeePerGas));
296
+ if (maxFeePerGas !== undefined && maxPriorityFeePerGas !== undefined) {
297
+ return { maxFeePerGas, maxPriorityFeePerGas };
298
+ }
299
+ return {};
300
+ }
301
+ function feeOverrideResultFromSnapshot(snapshot) {
302
+ if (snapshot.maxFeePerGas !== undefined && snapshot.maxPriorityFeePerGas !== undefined) {
303
+ const overrides = {
304
+ maxFeePerGas: snapshot.maxFeePerGas,
305
+ maxPriorityFeePerGas: snapshot.maxPriorityFeePerGas,
306
+ };
307
+ return { kind: 'eip1559', overrides, snapshot: overrides };
308
+ }
309
+ if (snapshot.gasPrice !== undefined) {
310
+ const overrides = { gasPrice: snapshot.gasPrice };
311
+ return { kind: 'legacy', overrides, snapshot: overrides };
312
+ }
313
+ return { kind: 'none', overrides: {}, snapshot: {} };
314
+ }
315
+ /**
316
+ * EIP-1559 or legacy gas overrides for viem, increasingly aggressive on later attempts.
317
+ *
318
+ * The result carries both the spreadable viem transaction overrides and the
319
+ * ledger snapshot, keeping call sites from recasting the override object.
320
+ */
321
+ export async function viemFeeOverrideResultForAttempt(publicClient, attemptIndex, options = {}) {
322
+ if (attemptIndex <= 0 && !options.previousFees && !options.forceEstimate) {
323
+ return feeOverrideResultFromSnapshot({});
324
+ }
247
325
  try {
248
326
  const fees = await publicClient.estimateFeesPerGas();
249
327
  if (fees.maxFeePerGas !== undefined && fees.maxPriorityFeePerGas !== undefined) {
250
- return {
251
- maxFeePerGas: apply(fees.maxFeePerGas),
252
- maxPriorityFeePerGas: apply(fees.maxPriorityFeePerGas),
253
- };
328
+ return feeOverrideResultFromSnapshot(mergeFeeEstimateWithPrevious({
329
+ maxFeePerGas: fees.maxFeePerGas,
330
+ maxPriorityFeePerGas: fees.maxPriorityFeePerGas,
331
+ }, options.previousFees, attemptIndex));
254
332
  }
255
333
  }
256
334
  catch {
@@ -258,12 +336,18 @@ export async function viemFeeOverridesForAttempt(publicClient, attemptIndex) {
258
336
  }
259
337
  try {
260
338
  const gasPrice = await publicClient.getGasPrice();
261
- return { gasPrice: apply(gasPrice) };
339
+ return feeOverrideResultFromSnapshot(mergeFeeEstimateWithPrevious({ gasPrice }, options.previousFees, attemptIndex));
262
340
  }
263
341
  catch {
264
- return {};
342
+ return options.previousFees
343
+ ? feeOverrideResultFromSnapshot(mergeFeeEstimateWithPrevious({}, options.previousFees, attemptIndex))
344
+ : feeOverrideResultFromSnapshot({});
265
345
  }
266
346
  }
347
+ /** EIP-1559 or legacy gas overrides for viem, increasingly aggressive on later attempts. */
348
+ export async function viemFeeOverridesForAttempt(publicClient, attemptIndex, options = {}) {
349
+ return (await viemFeeOverrideResultForAttempt(publicClient, attemptIndex, options)).overrides;
350
+ }
267
351
  export async function waitForTransactionReceiptWithRetry(publicClient, hash, options = {}) {
268
352
  const maxAttempts = options.maxAttempts ?? TX_RETRY_DEFAULTS.maxAttempts;
269
353
  const baseDelayMs = options.baseDelayMs ?? TX_RETRY_DEFAULTS.baseDelayMs;
@@ -294,34 +378,204 @@ export async function waitForTransactionReceiptWithRetry(publicClient, hash, opt
294
378
  }
295
379
  throw lastError instanceof Error ? lastError : new Error(String(lastError));
296
380
  }
381
+ async function resolveTxChainId(publicClient) {
382
+ return Number(await publicClient.getChainId());
383
+ }
384
+ async function resolvePendingNonce(publicClient, from) {
385
+ return Number(await publicClient.getTransactionCount({ address: from, blockTag: 'pending' }));
386
+ }
387
+ async function resolveLatestNonce(publicClient, from) {
388
+ return Number(await publicClient.getTransactionCount({ address: from, blockTag: 'latest' }));
389
+ }
390
+ function accountAddress(account) {
391
+ if (account && typeof account === 'object' && typeof account.address === 'string') {
392
+ return account.address;
393
+ }
394
+ return undefined;
395
+ }
396
+ function supportsNonceTracking(publicClient) {
397
+ const client = publicClient;
398
+ return typeof client.getChainId === 'function' && typeof client.getTransactionCount === 'function';
399
+ }
400
+ function isEmptyFees(fees) {
401
+ return fees.maxFeePerGas === undefined
402
+ && fees.maxPriorityFeePerGas === undefined
403
+ && fees.gasPrice === undefined;
404
+ }
405
+ export function removeConflictingLegacyGasPrice(tx) {
406
+ if (tx.maxFeePerGas !== undefined && tx.maxPriorityFeePerGas !== undefined) {
407
+ delete tx.gasPrice;
408
+ }
409
+ }
410
+ function withoutFeeOverrides(tx) {
411
+ const { maxFeePerGas: _maxFeePerGas, maxPriorityFeePerGas: _maxPriorityFeePerGas, gasPrice: _gasPrice, ...baseRequest } = tx;
412
+ return baseRequest;
413
+ }
414
+ export async function withNonceLedger(args, fn) {
415
+ const ledger = args.ledger ?? defaultTxSubmissionLedger;
416
+ const chainId = args.chainId ?? await resolveTxChainId(args.publicClient);
417
+ if (args.recoverStuckNonce) {
418
+ if (!args.walletClient) {
419
+ throw new Error('withNonceLedger recovery requested without a wallet client');
420
+ }
421
+ await recoverStuckNonceIfNeeded({
422
+ publicClient: args.publicClient,
423
+ walletClient: args.walletClient,
424
+ ledger,
425
+ chainId,
426
+ from: args.from,
427
+ staleAfterMs: args.staleAfterMs,
428
+ });
429
+ }
430
+ const nonce = args.nonce ?? await resolvePendingNonce(args.publicClient, args.from);
431
+ const key = { chainId, from: args.from, nonce };
432
+ const context = {
433
+ ledger,
434
+ chainId,
435
+ from: args.from,
436
+ nonce,
437
+ async feeResultForAttempt(attemptIndex, options = {}) {
438
+ const previous = await ledger.getTxSubmission(key);
439
+ return viemFeeOverrideResultForAttempt(args.publicClient, attemptIndex, {
440
+ previousFees: previous?.resolvedAtMs == null ? previous?.fees : undefined,
441
+ forceEstimate: options.forceEstimate,
442
+ });
443
+ },
444
+ async recordSubmitted(entry) {
445
+ await ledger.recordTxSubmission({
446
+ chainId,
447
+ from: args.from,
448
+ nonce,
449
+ hash: entry.hash,
450
+ logicalTx: entry.logicalTx,
451
+ submittedAtMs: entry.submittedAtMs ?? Date.now(),
452
+ fees: entry.fees,
453
+ to: entry.to,
454
+ value: entry.value,
455
+ data: entry.data,
456
+ });
457
+ },
458
+ async markResolved(resolvedAtMs = Date.now()) {
459
+ await ledger.markTxSubmissionResolved({ chainId, from: args.from, nonce, resolvedAtMs });
460
+ },
461
+ };
462
+ return fn(context);
463
+ }
464
+ export async function recoverStuckNonceIfNeeded(args) {
465
+ const ledger = args.ledger ?? defaultTxSubmissionLedger;
466
+ const [chainId, pendingNonce, latestNonce] = await Promise.all([
467
+ args.chainId ?? resolveTxChainId(args.publicClient),
468
+ resolvePendingNonce(args.publicClient, args.from),
469
+ resolveLatestNonce(args.publicClient, args.from),
470
+ ]);
471
+ if (pendingNonce <= latestNonce)
472
+ return null;
473
+ const nowMs = args.nowMs ?? Date.now();
474
+ const staleAfterMs = args.staleAfterMs ?? TX_RETRY_DEFAULTS.stuckNonceAfterMs;
475
+ for (let nonce = latestNonce; nonce < pendingNonce; nonce++) {
476
+ const entry = await ledger.getTxSubmission({ chainId, from: args.from, nonce });
477
+ if (!entry || entry.resolvedAtMs != null)
478
+ continue;
479
+ if (nowMs - entry.submittedAtMs < staleAfterMs)
480
+ continue;
481
+ const feeOverrides = await viemFeeOverridesForAttempt(args.publicClient, 1, {
482
+ previousFees: entry.fees,
483
+ forceEstimate: true,
484
+ });
485
+ const account = args.walletClient.account;
486
+ const tx = {
487
+ account,
488
+ to: args.from,
489
+ value: 0n,
490
+ nonce,
491
+ ...feeOverrides,
492
+ };
493
+ removeConflictingLegacyGasPrice(tx);
494
+ const recoveryHash = await args.walletClient.sendTransaction(tx);
495
+ await ledger.recordTxSubmission({
496
+ chainId,
497
+ from: args.from,
498
+ nonce,
499
+ hash: recoveryHash,
500
+ logicalTx: 'stuck-nonce-recovery',
501
+ submittedAtMs: nowMs,
502
+ fees: feeOverrides,
503
+ to: args.from,
504
+ value: 0n,
505
+ });
506
+ await args.publicClient.waitForTransactionReceipt({ hash: recoveryHash });
507
+ await ledger.markTxSubmissionResolved({ chainId, from: args.from, nonce, resolvedAtMs: nowMs });
508
+ return { nonce, previousHash: entry.hash, recoveryHash };
509
+ }
510
+ return null;
511
+ }
297
512
  export async function viemSendTransactionWithRetry(walletClient, publicClient, txRequest, options = {}) {
298
513
  const maxAttempts = options.maxAttempts ?? TX_RETRY_DEFAULTS.maxAttempts;
299
514
  const baseDelayMs = options.baseDelayMs ?? TX_RETRY_DEFAULTS.baseDelayMs;
300
515
  const maxDelayMs = options.maxDelayMs ?? TX_RETRY_DEFAULTS.maxDelayMs;
301
- let lastError;
302
- for (let attempt = 0; attempt < maxAttempts; attempt++) {
303
- try {
304
- const overrides = await viemFeeOverridesForAttempt(publicClient, attempt);
305
- const req = { ...txRequest, ...overrides };
306
- // If we provided maxFeePerGas, ensure gasPrice is not somehow inherited
307
- if ('maxFeePerGas' in req && 'maxPriorityFeePerGas' in req) {
308
- delete req.gasPrice;
516
+ const ledger = options.ledger ?? defaultTxSubmissionLedger;
517
+ const from = accountAddress(txRequest.account);
518
+ const shouldTrackNonce = from !== undefined && supportsNonceTracking(publicClient);
519
+ const requestedNonce = txRequest.nonce === undefined ? undefined : Number(txRequest.nonce);
520
+ const sendWithRetry = async (nonceLedger) => {
521
+ const pinnedNonce = nonceLedger?.nonce ?? requestedNonce;
522
+ let lastError;
523
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
524
+ try {
525
+ const feeResult = nonceLedger
526
+ ? await nonceLedger.feeResultForAttempt(attempt, { forceEstimate: true })
527
+ : await viemFeeOverrideResultForAttempt(publicClient, attempt);
528
+ const nonceOverride = pinnedNonce !== undefined ? { nonce: pinnedNonce } : {};
529
+ const req = feeResult.kind === 'none'
530
+ ? {
531
+ ...txRequest,
532
+ ...nonceOverride,
533
+ }
534
+ : {
535
+ ...withoutFeeOverrides(txRequest),
536
+ ...nonceOverride,
537
+ ...feeResult.overrides,
538
+ };
539
+ removeConflictingLegacyGasPrice(req);
540
+ const hash = await walletClient.sendTransaction(req);
541
+ if (nonceLedger && !isEmptyFees(feeResult.snapshot)) {
542
+ await nonceLedger.recordSubmitted({
543
+ hash,
544
+ logicalTx: options.logicalTx,
545
+ fees: feeResult.snapshot,
546
+ to: txRequest.to,
547
+ value: txRequest.value,
548
+ data: txRequest.data,
549
+ });
550
+ }
551
+ return hash;
309
552
  }
310
- return await walletClient.sendTransaction(req);
311
- }
312
- catch (err) {
313
- lastError = err;
314
- if (!isRecoverableTransactionError(err) || attempt >= maxAttempts - 1) {
315
- throw err;
553
+ catch (err) {
554
+ lastError = err;
555
+ if (!isRecoverableTransactionError(err) || attempt >= maxAttempts - 1) {
556
+ throw err;
557
+ }
558
+ options.onRetry?.({
559
+ attempt: attempt + 1,
560
+ error: err,
561
+ message: flattenErrorMessage(err),
562
+ });
563
+ await backoffDelay(attempt, baseDelayMs, maxDelayMs);
316
564
  }
317
- options.onRetry?.({
318
- attempt: attempt + 1,
319
- error: err,
320
- message: flattenErrorMessage(err),
321
- });
322
- await backoffDelay(attempt, baseDelayMs, maxDelayMs);
323
565
  }
566
+ throw lastError instanceof Error ? lastError : new Error(String(lastError));
567
+ };
568
+ if (from && shouldTrackNonce) {
569
+ return withNonceLedger({
570
+ publicClient,
571
+ walletClient,
572
+ ledger,
573
+ from,
574
+ nonce: requestedNonce,
575
+ recoverStuckNonce: true,
576
+ staleAfterMs: options.stuckNonceAfterMs,
577
+ }, sendWithRetry);
324
578
  }
325
- throw lastError instanceof Error ? lastError : new Error(String(lastError));
579
+ return sendWithRetry();
326
580
  }
327
581
  //# sourceMappingURL=tx-retry.js.map