@jinn-network/client 0.1.6 → 0.1.7-canary.d4e4e183
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.
- package/CHANGELOG.md +33 -0
- package/deployments/deployment-jinn-mvi-l1-sepolia-fast.json +23 -4
- package/deployments/deployment-jinn-mvi-l1-sepolia.json +23 -4
- package/deployments/deployment-jinn-mvi-l2-baseSepolia.json +5 -4
- package/dist/adapters/mech/adapter.d.ts +38 -1
- package/dist/adapters/mech/adapter.js +241 -54
- package/dist/adapters/mech/adapter.js.map +1 -1
- package/dist/adapters/mech/contracts.d.ts +17 -4
- package/dist/adapters/mech/contracts.js +8 -2
- package/dist/adapters/mech/contracts.js.map +1 -1
- package/dist/adapters/mech/safe-revert.d.ts +20 -0
- package/dist/adapters/mech/safe-revert.js +12 -4
- package/dist/adapters/mech/safe-revert.js.map +1 -1
- package/dist/adapters/mech/safe.d.ts +5 -1
- package/dist/adapters/mech/safe.js +27 -8
- package/dist/adapters/mech/safe.js.map +1 -1
- package/dist/adapters/mech/verdict-code.d.ts +1 -0
- package/dist/adapters/mech/verdict-code.js +18 -0
- package/dist/adapters/mech/verdict-code.js.map +1 -1
- package/dist/api/admin-endpoint.d.ts +15 -3
- package/dist/api/admin-endpoint.js +24 -2
- package/dist/api/admin-endpoint.js.map +1 -1
- package/dist/api/bootstrap-endpoint.js +49 -0
- package/dist/api/bootstrap-endpoint.js.map +1 -1
- package/dist/api/codex-doctor-endpoint.d.ts +73 -0
- package/dist/api/codex-doctor-endpoint.js +177 -0
- package/dist/api/codex-doctor-endpoint.js.map +1 -0
- package/dist/api/discovery-endpoint.d.ts +1 -0
- package/dist/api/discovery-endpoint.js +26 -0
- package/dist/api/discovery-endpoint.js.map +1 -1
- package/dist/api/fleet-build.d.ts +1 -0
- package/dist/api/fleet-build.js +2 -1
- package/dist/api/fleet-build.js.map +1 -1
- package/dist/api/gather-status.d.ts +11 -0
- package/dist/api/gather-status.js +400 -4
- package/dist/api/gather-status.js.map +1 -1
- package/dist/api/hermes-doctor-endpoint.d.ts +117 -0
- package/dist/api/hermes-doctor-endpoint.js +229 -23
- package/dist/api/hermes-doctor-endpoint.js.map +1 -1
- package/dist/api/launcher-status.d.ts +21 -16
- package/dist/api/launcher-status.js +2 -1
- package/dist/api/launcher-status.js.map +1 -1
- package/dist/api/portfolio-v0-build.d.ts +10 -0
- package/dist/api/portfolio-v0-build.js +24 -5
- package/dist/api/portfolio-v0-build.js.map +1 -1
- package/dist/api/prediction-v1-build.d.ts +10 -0
- package/dist/api/prediction-v1-build.js +7 -1
- package/dist/api/prediction-v1-build.js.map +1 -1
- package/dist/api/server.d.ts +31 -1
- package/dist/api/server.js +68 -1
- package/dist/api/server.js.map +1 -1
- package/dist/api/setup-endpoints.d.ts +16 -0
- package/dist/api/setup-endpoints.js +78 -4
- package/dist/api/setup-endpoints.js.map +1 -1
- package/dist/api/setup-retry-endpoint.d.ts +19 -0
- package/dist/api/setup-retry-endpoint.js +32 -0
- package/dist/api/setup-retry-endpoint.js.map +1 -0
- package/dist/api/solvernets-endpoints.d.ts +8 -0
- package/dist/api/solvernets-endpoints.js +71 -43
- package/dist/api/solvernets-endpoints.js.map +1 -1
- package/dist/api/status-build.d.ts +72 -0
- package/dist/api/status-build.js +73 -18
- package/dist/api/status-build.js.map +1 -1
- package/dist/api/task-run-routing.d.ts +7 -0
- package/dist/api/task-run-routing.js +12 -0
- package/dist/api/task-run-routing.js.map +1 -0
- package/dist/api/task-runs-build.d.ts +21 -0
- package/dist/api/task-runs-build.js +14 -1
- package/dist/api/task-runs-build.js.map +1 -1
- package/dist/build-info.json +4 -4
- package/dist/build-meta.json +1 -1
- package/dist/chain-read-errors.d.ts +10 -0
- package/dist/chain-read-errors.js +15 -0
- package/dist/chain-read-errors.js.map +1 -1
- package/dist/cli/commands/auth.js +1 -1
- package/dist/cli/commands/auth.js.map +1 -1
- package/dist/cli/commands/create.js +3 -2
- package/dist/cli/commands/create.js.map +1 -1
- package/dist/cli/commands/doctor.d.ts +2 -0
- package/dist/cli/commands/doctor.js +2 -0
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/rewards.js +11 -7
- package/dist/cli/commands/rewards.js.map +1 -1
- package/dist/cli/commands/solver-nets.js +24 -9
- package/dist/cli/commands/solver-nets.js.map +1 -1
- package/dist/cli/commands/status.js +1 -1
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/commands/tasks.js +86 -9
- package/dist/cli/commands/tasks.js.map +1 -1
- package/dist/cli/commands/update.d.ts +10 -0
- package/dist/cli/commands/update.js +36 -0
- package/dist/cli/commands/update.js.map +1 -1
- package/dist/cli/introspection-context.js +5 -0
- package/dist/cli/introspection-context.js.map +1 -1
- package/dist/cli/task-native-readiness.d.ts +3 -1
- package/dist/cli/task-native-readiness.js +28 -6
- package/dist/cli/task-native-readiness.js.map +1 -1
- package/dist/config.d.ts +106 -5
- package/dist/config.js +97 -18
- package/dist/config.js.map +1 -1
- package/dist/daemon/checkpoint-loop.d.ts +48 -0
- package/dist/daemon/checkpoint-loop.js +76 -0
- package/dist/daemon/checkpoint-loop.js.map +1 -0
- package/dist/daemon/creator.d.ts +1 -1
- package/dist/daemon/creator.js +7 -3
- package/dist/daemon/creator.js.map +1 -1
- package/dist/daemon/daemon.d.ts +19 -0
- package/dist/daemon/daemon.js +68 -1
- package/dist/daemon/daemon.js.map +1 -1
- package/dist/daemon/eviction-loop.d.ts +40 -0
- package/dist/daemon/eviction-loop.js +67 -0
- package/dist/daemon/eviction-loop.js.map +1 -0
- package/dist/daemon/jinn-claim-loop-wiring.d.ts +33 -0
- package/dist/daemon/jinn-claim-loop-wiring.js +40 -0
- package/dist/daemon/jinn-claim-loop-wiring.js.map +1 -0
- package/dist/daemon/jinn-claim-loop.d.ts +24 -17
- package/dist/daemon/jinn-claim-loop.js +77 -23
- package/dist/daemon/jinn-claim-loop.js.map +1 -1
- package/dist/daemon/skip-log-dedup.d.ts +69 -0
- package/dist/daemon/skip-log-dedup.js +106 -0
- package/dist/daemon/skip-log-dedup.js.map +1 -0
- package/dist/dashboard/assets/index-BUlE8F3Y.js +330 -0
- package/dist/dashboard/assets/index-blqc7eqq.css +32 -0
- package/dist/dashboard/index.html +2 -2
- package/dist/discovery/factory.d.ts +17 -5
- package/dist/discovery/factory.js +46 -18
- package/dist/discovery/factory.js.map +1 -1
- package/dist/discovery/http.js +142 -3
- package/dist/discovery/http.js.map +1 -1
- package/dist/discovery/onchain.d.ts +5 -0
- package/dist/discovery/onchain.js +407 -15
- package/dist/discovery/onchain.js.map +1 -1
- package/dist/discovery/types.d.ts +45 -1
- package/dist/discovery/types.js +8 -10
- package/dist/discovery/types.js.map +1 -1
- package/dist/discovery/with-fallback.d.ts +7 -0
- package/dist/discovery/with-fallback.js +10 -0
- package/dist/discovery/with-fallback.js.map +1 -1
- package/dist/earning/bootstrap.d.ts +92 -1
- package/dist/earning/bootstrap.js +203 -63
- package/dist/earning/bootstrap.js.map +1 -1
- package/dist/earning/contracts.d.ts +14 -0
- package/dist/earning/contracts.js +17 -5
- package/dist/earning/contracts.js.map +1 -1
- package/dist/earning/funding-plan.js +27 -18
- package/dist/earning/funding-plan.js.map +1 -1
- package/dist/earning/jinn-rewards.d.ts +46 -0
- package/dist/earning/jinn-rewards.js +32 -0
- package/dist/earning/jinn-rewards.js.map +1 -1
- package/dist/earning/safe-adapter.d.ts +2 -0
- package/dist/earning/safe-adapter.js +26 -12
- package/dist/earning/safe-adapter.js.map +1 -1
- package/dist/earning/store.d.ts +8 -0
- package/dist/earning/store.js.map +1 -1
- package/dist/earning/testnet-setup-migration.d.ts +12 -0
- package/dist/earning/testnet-setup-migration.js +27 -1
- package/dist/earning/testnet-setup-migration.js.map +1 -1
- package/dist/earning/types.d.ts +15 -0
- package/dist/erc8004/reputation.d.ts +8 -0
- package/dist/erc8004/reputation.js +22 -3
- package/dist/erc8004/reputation.js.map +1 -1
- package/dist/harnesses/cost-estimates.d.ts +145 -0
- package/dist/harnesses/cost-estimates.js +297 -0
- package/dist/harnesses/cost-estimates.js.map +1 -0
- package/dist/harnesses/engine/engine.d.ts +72 -0
- package/dist/harnesses/engine/engine.js +105 -8
- package/dist/harnesses/engine/engine.js.map +1 -1
- package/dist/harnesses/engine/persistence.d.ts +51 -1
- package/dist/harnesses/engine/persistence.js +118 -5
- package/dist/harnesses/engine/persistence.js.map +1 -1
- package/dist/harnesses/engine/work-dir-reaper.d.ts +65 -0
- package/dist/harnesses/engine/work-dir-reaper.js +100 -0
- package/dist/harnesses/engine/work-dir-reaper.js.map +1 -0
- package/dist/harnesses/impls/hermes-agent/adapter.js +40 -0
- package/dist/harnesses/impls/hermes-agent/adapter.js.map +1 -1
- package/dist/harnesses/impls/hermes-agent/bootstrap.d.ts +20 -0
- package/dist/harnesses/impls/hermes-agent/bootstrap.js +40 -6
- package/dist/harnesses/impls/hermes-agent/bootstrap.js.map +1 -1
- package/dist/harnesses/impls/hermes-agent/harness.d.ts +59 -1
- package/dist/harnesses/impls/hermes-agent/harness.js +104 -0
- package/dist/harnesses/impls/hermes-agent/harness.js.map +1 -1
- package/dist/harnesses/impls/index.d.ts +7 -0
- package/dist/harnesses/impls/index.js +16 -1
- package/dist/harnesses/impls/index.js.map +1 -1
- package/dist/harnesses/impls/learner/harness.d.ts +38 -4
- package/dist/harnesses/impls/learner/harness.js +96 -2
- package/dist/harnesses/impls/learner/harness.js.map +1 -1
- package/dist/harnesses/impls/learner/plugin-path.d.ts +0 -13
- package/dist/harnesses/impls/learner/plugin-path.js +35 -15
- package/dist/harnesses/impls/learner/plugin-path.js.map +1 -1
- package/dist/harnesses/impls/learner/types.d.ts +11 -0
- package/dist/harnesses/impls/stub.d.ts +58 -0
- package/dist/harnesses/impls/stub.js +89 -0
- package/dist/harnesses/impls/stub.js.map +1 -0
- package/dist/harnesses/impls/swe-rebench-v2-evaluator/eval-runner.d.ts +69 -50
- package/dist/harnesses/impls/swe-rebench-v2-evaluator/eval-runner.js +178 -93
- package/dist/harnesses/impls/swe-rebench-v2-evaluator/eval-runner.js.map +1 -1
- package/dist/harnesses/impls/swe-rebench-v2-evaluator/harness.d.ts +12 -1
- package/dist/harnesses/impls/swe-rebench-v2-evaluator/harness.js +121 -7
- package/dist/harnesses/impls/swe-rebench-v2-evaluator/harness.js.map +1 -1
- package/dist/harnesses/impls/swe-rebench-v2-evaluator/hf-fetcher.d.ts +15 -0
- package/dist/harnesses/impls/swe-rebench-v2-evaluator/hf-fetcher.js +54 -4
- package/dist/harnesses/impls/swe-rebench-v2-evaluator/hf-fetcher.js.map +1 -1
- package/dist/harnesses/impls/swe-rebench-v2-evaluator/index.d.ts +6 -0
- package/dist/harnesses/impls/swe-rebench-v2-evaluator/index.js +1 -1
- package/dist/harnesses/impls/swe-rebench-v2-evaluator/index.js.map +1 -1
- package/dist/harnesses/readiness-registry.js +9 -1
- package/dist/harnesses/readiness-registry.js.map +1 -1
- package/dist/main.js +371 -82
- package/dist/main.js.map +1 -1
- package/dist/observability/emit-event.d.ts +1 -1
- package/dist/observability/emit-event.js.map +1 -1
- package/dist/operator-errors.d.ts +7 -0
- package/dist/operator-errors.js +13 -1
- package/dist/operator-errors.js.map +1 -1
- package/dist/plugins/learner/.claude-plugin/plugin.json +9 -0
- package/dist/plugins/learner/.codex-plugin/plugin.json +39 -0
- package/dist/plugins/learner/AGENTS.md +40 -0
- package/dist/plugins/learner/CLAUDE.md +33 -0
- package/dist/plugins/learner/README.md +59 -0
- package/dist/plugins/learner/hooks/hooks.json +16 -0
- package/dist/plugins/learner/hooks/session-start +38 -0
- package/dist/plugins/learner/skills/learn/SKILL.md +412 -0
- package/dist/plugins/learner/skills/learn/analyst-prompt.md +68 -0
- package/dist/plugins/learner/skills/learn/consolidator-prompt.md +94 -0
- package/dist/plugins/learner/skills/learn/explorer-prompt.md +53 -0
- package/dist/plugins/learner/skills/learn/planner-prompt.md +87 -0
- package/dist/plugins/learner/skills/learn/promoter-prompt.md +113 -0
- package/dist/plugins/learner/skills/learn/step-worker-prompt.md +47 -0
- package/dist/plugins/learner/skills/learn/strategist-prompt.md +85 -0
- package/dist/restart-daemon.d.ts +90 -0
- package/dist/restart-daemon.js +95 -0
- package/dist/restart-daemon.js.map +1 -0
- package/dist/setup/halt-mode.d.ts +14 -0
- package/dist/setup/halt-mode.js +17 -0
- package/dist/setup/halt-mode.js.map +1 -0
- package/dist/solver-nets/prediction-operator-ux.js +43 -3
- package/dist/solver-nets/prediction-operator-ux.js.map +1 -1
- package/dist/solver-nets/registry.d.ts +1 -0
- package/dist/solver-nets/registry.js +1 -1
- package/dist/solver-nets/registry.js.map +1 -1
- package/dist/solver-types/_swe-rebench-v2-pool-cache.d.ts +58 -0
- package/dist/solver-types/_swe-rebench-v2-pool-cache.js +87 -0
- package/dist/solver-types/_swe-rebench-v2-pool-cache.js.map +1 -0
- package/dist/solver-types/_swe-rebench-v2-substrate.d.ts +1 -0
- package/dist/solver-types/_swe-rebench-v2-substrate.js +10 -0
- package/dist/solver-types/_swe-rebench-v2-substrate.js.map +1 -1
- package/dist/solver-types/_swe-rebench-v2-validated-pool.d.ts +65 -0
- package/dist/solver-types/_swe-rebench-v2-validated-pool.js +243 -26
- package/dist/solver-types/_swe-rebench-v2-validated-pool.js.map +1 -1
- package/dist/solver-types/swe-rebench-v2-auto.d.ts +22 -7
- package/dist/solver-types/swe-rebench-v2-auto.js +45 -20
- package/dist/solver-types/swe-rebench-v2-auto.js.map +1 -1
- package/dist/solver-types/swe-rebench-v2.d.ts +13 -2
- package/dist/solver-types/swe-rebench-v2.js +233 -94
- package/dist/solver-types/swe-rebench-v2.js.map +1 -1
- package/dist/solvernets/daemon-init.d.ts +10 -2
- package/dist/solvernets/daemon-init.js +22 -2
- package/dist/solvernets/daemon-init.js.map +1 -1
- package/dist/solvernets/launched-record-dispatcher.js +35 -7
- package/dist/solvernets/launched-record-dispatcher.js.map +1 -1
- package/dist/solvernets/store.d.ts +5 -0
- package/dist/solvernets/store.js +1 -0
- package/dist/solvernets/store.js.map +1 -1
- package/dist/store/store.d.ts +15 -0
- package/dist/store/store.js +118 -3
- package/dist/store/store.js.map +1 -1
- package/dist/tasks/sources.d.ts +18 -1
- package/dist/tasks/sources.js +33 -5
- package/dist/tasks/sources.js.map +1 -1
- package/dist/tx-retry.d.ts +151 -19
- package/dist/tx-retry.js +286 -32
- package/dist/tx-retry.js.map +1 -1
- package/dist/types/payloads/prediction-apy-v0.d.ts +5 -5
- package/dist/types/payloads/prediction-v0.d.ts +5 -5
- package/dist/types/task-document.d.ts +392 -0
- package/dist/types/task-document.js +10 -0
- package/dist/types/task-document.js.map +1 -1
- package/dist/types/task.d.ts +28 -0
- package/dist/util/extract-tx-hash.d.ts +14 -0
- package/dist/util/extract-tx-hash.js +19 -0
- package/dist/util/extract-tx-hash.js.map +1 -0
- package/dist/vendor/@jinn-network/sdk/dist/contracts.js +1 -1
- package/dist/vendor/@jinn-network/sdk/dist/solvernets/manifest-schema.d.ts +3 -0
- package/dist/vendor/@jinn-network/sdk/dist/solvernets/manifest-schema.js +1 -0
- package/package.json +29 -12
- package/dist/dashboard/assets/index-DOlzFN8a.css +0 -32
- package/dist/dashboard/assets/index-NkZ7CTAT.js +0 -140
package/dist/tasks/sources.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
30
|
-
|
|
31
|
-
|
|
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;
|
|
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"}
|
package/dist/tx-retry.d.ts
CHANGED
|
@@ -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
|
|
47
|
-
|
|
48
|
-
|
|
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
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
241
|
-
|
|
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
|
-
|
|
246
|
-
|
|
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:
|
|
252
|
-
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
|
|
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
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
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
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
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
|
-
|
|
579
|
+
return sendWithRetry();
|
|
326
580
|
}
|
|
327
581
|
//# sourceMappingURL=tx-retry.js.map
|