@jinn-network/client 0.1.6 → 0.1.7-canary.17a8ecb8
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/README.md +67 -1
- 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 +268 -57
- 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 +6 -2
- package/dist/adapters/mech/safe.js +32 -11
- package/dist/adapters/mech/safe.js.map +1 -1
- package/dist/adapters/mech/types.d.ts +6 -1
- package/dist/adapters/mech/types.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/activity-events-endpoint.d.ts +14 -0
- package/dist/api/activity-events-endpoint.js +59 -0
- package/dist/api/activity-events-endpoint.js.map +1 -0
- 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.d.ts +1 -2
- package/dist/api/bootstrap-endpoint.js +49 -1
- 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 +14 -0
- package/dist/api/gather-status.js +494 -19
- 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 +22 -17
- package/dist/api/launcher-status.js +13 -11
- package/dist/api/launcher-status.js.map +1 -1
- package/dist/api/launcher-tasks.d.ts +1 -1
- package/dist/api/launcher-tasks.js +12 -8
- package/dist/api/launcher-tasks.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 +72 -1
- package/dist/api/server.js.map +1 -1
- package/dist/api/setup-endpoints.d.ts +16 -0
- package/dist/api/setup-endpoints.js +89 -135
- 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 +112 -0
- package/dist/api/status-build.js +98 -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 +101 -15
- package/dist/cli/commands/solver-nets.js.map +1 -1
- package/dist/cli/commands/solver-plugins-block.d.ts +33 -0
- package/dist/cli/commands/solver-plugins-block.js +118 -0
- package/dist/cli/commands/solver-plugins-block.js.map +1 -0
- package/dist/cli/commands/solver-plugins-feedback.d.ts +72 -0
- package/dist/cli/commands/solver-plugins-feedback.js +262 -0
- package/dist/cli/commands/solver-plugins-feedback.js.map +1 -0
- package/dist/cli/commands/solver-plugins-read.d.ts +54 -0
- package/dist/cli/commands/solver-plugins-read.js +259 -0
- package/dist/cli/commands/solver-plugins-read.js.map +1 -0
- package/dist/cli/commands/solver-plugins.d.ts +35 -0
- package/dist/cli/commands/solver-plugins.js +399 -2
- package/dist/cli/commands/solver-plugins.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 +101 -11
- 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 +10 -1
- package/dist/cli/task-native-readiness.js +30 -6
- package/dist/cli/task-native-readiness.js.map +1 -1
- package/dist/config.d.ts +273 -235
- package/dist/config.js +305 -114
- 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 +22 -0
- package/dist/daemon/daemon.js +156 -23
- 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/gate-logger.d.ts +9 -0
- package/dist/daemon/gate-logger.js +2 -0
- package/dist/daemon/gate-logger.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/readiness-gate.d.ts +1 -4
- package/dist/daemon/readiness-gate.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/daemon/spend-cap-gate.d.ts +40 -0
- package/dist/daemon/spend-cap-gate.js +46 -0
- package/dist/daemon/spend-cap-gate.js.map +1 -0
- package/dist/dashboard/assets/index-8yHQgi7p.js +345 -0
- package/dist/dashboard/assets/index-BOBhJ76-.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 +37 -11
- 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 +21 -6
- package/dist/earning/viem-clients.d.ts +11 -4
- package/dist/earning/viem-clients.js +14 -5
- package/dist/earning/viem-clients.js.map +1 -1
- 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/events/types.d.ts +2 -2
- 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 +118 -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 +88 -4
- package/dist/harnesses/impls/swe-rebench-v2-evaluator/hf-fetcher.js +143 -22
- 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 +413 -111
- package/dist/main.js.map +1 -1
- package/dist/observability/emit-event.d.ts +3 -2
- package/dist/observability/emit-event.js +22 -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/preflight/rpc-network.d.ts +40 -0
- package/dist/preflight/rpc-network.js +67 -1
- package/dist/preflight/rpc-network.js.map +1 -1
- 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/rpc/transport.d.ts +109 -0
- package/dist/rpc/transport.js +220 -0
- package/dist/rpc/transport.js.map +1 -0
- package/dist/scripts/donation-consumption-acceptance.js +7 -28
- package/dist/scripts/donation-consumption-acceptance.js.map +1 -1
- 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.d.ts +1 -2
- package/dist/solver-nets/prediction-operator-ux.js +90 -47
- package/dist/solver-nets/prediction-operator-ux.js.map +1 -1
- package/dist/solver-nets/registry.d.ts +20 -1
- package/dist/solver-nets/registry.js +38 -25
- 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-pool.d.ts +9 -2
- package/dist/solver-types/_swe-rebench-v2-pool.js +15 -20
- package/dist/solver-types/_swe-rebench-v2-pool.js.map +1 -1
- 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 +94 -1
- package/dist/solver-types/_swe-rebench-v2-validated-pool.js +305 -39
- 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 +237 -95
- 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/spend/credential.d.ts +8 -0
- package/dist/spend/credential.js +30 -0
- package/dist/spend/credential.js.map +1 -0
- package/dist/spend/daemon-config.d.ts +13 -0
- package/dist/spend/daemon-config.js +24 -0
- package/dist/spend/daemon-config.js.map +1 -0
- package/dist/spend/pricing.d.ts +16 -0
- package/dist/spend/pricing.js +26 -0
- package/dist/spend/pricing.js.map +1 -0
- package/dist/spend/record.d.ts +13 -0
- package/dist/spend/record.js +30 -0
- package/dist/spend/record.js.map +1 -0
- package/dist/spend/usage.d.ts +27 -0
- package/dist/spend/usage.js +113 -0
- package/dist/spend/usage.js.map +1 -0
- package/dist/store/store.d.ts +43 -0
- package/dist/store/store.js +236 -7
- 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/trajectory/transcript-parsers/types.d.ts +8 -8
- package/dist/tx-retry.d.ts +166 -19
- package/dist/tx-retry.js +310 -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 +30 -12
- package/dist/dashboard/assets/index-DOlzFN8a.css +0 -32
- package/dist/dashboard/assets/index-NkZ7CTAT.js +0 -140
|
@@ -4,11 +4,23 @@ import { privateKeyToAccount } from 'viem/accounts';
|
|
|
4
4
|
import { base, baseSepolia } from 'viem/chains';
|
|
5
5
|
import { PermanentError, parseTask } from '../../types/index.js';
|
|
6
6
|
import { createClients } from './safe.js';
|
|
7
|
+
/**
|
|
8
|
+
* Coalesce a string-or-array RPC input down to the head URL for display in
|
|
9
|
+
* error contexts (`formatRpcError` expects a single host). The adapter
|
|
10
|
+
* accepts the full fallback chain at the type level; this helper exists so
|
|
11
|
+
* the error-formatting call sites can keep their old signature.
|
|
12
|
+
*/
|
|
13
|
+
function rpcUrlForDisplay(rpcUrl) {
|
|
14
|
+
return Array.isArray(rpcUrl) ? rpcUrl[0] : rpcUrl;
|
|
15
|
+
}
|
|
7
16
|
import { buildResultPayload, uploadToIpfs, cidToDigestHex, fetchFromIpfs, fetchSignedTaskFromIpfs, fetchSignedEnvelopeFromIpfs, } from './ipfs.js';
|
|
8
17
|
import { canonicalJson } from '../../harnesses/engine/canonical-json.js';
|
|
9
|
-
import { SignedEnvelopeSchema } from '../../types/envelope.js';
|
|
18
|
+
import { normalizeEnvelopeRole, SignedEnvelopeSchema } from '../../types/envelope.js';
|
|
10
19
|
import { submitTask, claimTask as claimTaskOnchain, claimEvaluation as claimEvaluationOnchain, claimDelivery, getMechDeliveryRate, getTimeoutBounds, decodeTaskCreatedLogs, decodeSolutionDeliveryClaimedLogs, decodeDeliverLogs, findLatestDeliveryDataHexForRequest, getMarketplaceRequestDeliveryMech, getTaskCidDigest, callDeliverToMarketplace, canClaimTask, canClaimEvaluation, } from './contracts.js';
|
|
20
|
+
import { isNonRecoverableInnerRevert } from './safe-revert.js';
|
|
21
|
+
import { verdictCodeFromValue } from './verdict-code.js';
|
|
11
22
|
import { manifestDigestForCid } from './digest.js';
|
|
23
|
+
import { emitStructured } from '../../events/emitter.js';
|
|
12
24
|
import { withRecoverableRetry } from '../../tx-retry.js';
|
|
13
25
|
import { formatRpcError } from '../../rpc-error-context.js';
|
|
14
26
|
import { SOLUTION_ENVELOPE_CID_CONTEXT_KEY, SOLUTION_TASK_CID_CONTEXT_KEY, RESTORATION_TASK_CID_CONTEXT_KEY, } from '../../harnesses/impls/evaluation-context.js';
|
|
@@ -16,6 +28,28 @@ import { signTaskV1 } from '../../tasks/signing.js';
|
|
|
16
28
|
const ROUTER_REQUEST_CURSOR_CONFIG_KEY = 'mech_router_request_block_cursor_v1';
|
|
17
29
|
const PENDING_EVALUATION_SOLUTIONS_CONFIG_KEY = 'mech_pending_evaluation_solutions_v1';
|
|
18
30
|
const DEFAULT_MECH_DELIVER_BACKFILL_LOOKBACK_BLOCKS = 100000n;
|
|
31
|
+
/** Yield to the event loop every N evaluation opportunities so a large retry
|
|
32
|
+
* backlog can't starve the HTTP API mid-cycle. */
|
|
33
|
+
const EVALUATION_RETRY_YIELD_EVERY = 10;
|
|
34
|
+
/**
|
|
35
|
+
* Decide whether a `canClaimEvaluation` failure means the opportunity can NEVER
|
|
36
|
+
* become claimable (terminal, prune it) versus one that could still clear later
|
|
37
|
+
* (transient, keep retrying).
|
|
38
|
+
*
|
|
39
|
+
* Classification is done on the *structured* `revertName` decoded straight from
|
|
40
|
+
* the inner revert data — not by regex-unformatting the operator-facing `reason`
|
|
41
|
+
* string. The format→regex round-trip was fragile: an arg value containing a
|
|
42
|
+
* `(` corrupted the strip, and the `flattenErrorMessage` fallback produced
|
|
43
|
+
* arbitrary text the regex mangled, silently mis-classifying opportunities.
|
|
44
|
+
*
|
|
45
|
+
* A false-keep (re-checking a dead opportunity) only costs one more RPC; a
|
|
46
|
+
* false-prune (dropping a still-claimable opportunity) loses real work — so
|
|
47
|
+
* when in doubt we keep. Anything without a known non-recoverable revert name
|
|
48
|
+
* is treated as transient.
|
|
49
|
+
*/
|
|
50
|
+
function isTerminalEvaluationReason(revertName) {
|
|
51
|
+
return isNonRecoverableInnerRevert(revertName);
|
|
52
|
+
}
|
|
19
53
|
const DEFAULT_ROUTER_LOG_CHUNK_BLOCKS = 9999n;
|
|
20
54
|
/**
|
|
21
55
|
* Floor block for the on-chain TaskCreated backlog scan, per chain.
|
|
@@ -81,6 +115,17 @@ export class MechAdapter {
|
|
|
81
115
|
deliveryBlockCursor = 0n;
|
|
82
116
|
pendingEvaluations = new Map();
|
|
83
117
|
observedTasks = new Map();
|
|
118
|
+
/**
|
|
119
|
+
* Read-through cache for `restorationAnnouncementForTaskId` — the restoration
|
|
120
|
+
* task body looked up *while building an evaluation opportunity*. Kept
|
|
121
|
+
* SEPARATE from `observedTasks` (the `watchForTasks` discovery dedup set) on
|
|
122
|
+
* purpose: writing the restoration body into `observedTasks` made the
|
|
123
|
+
* TaskCreated scan skip that taskId as a *restoration* opportunity just
|
|
124
|
+
* because the daemon had built an *evaluation* opportunity for someone
|
|
125
|
+
* else's attempt on it. That blocked the creator's own daemon from claiming
|
|
126
|
+
* its own attempt on a multi-attempt (`maxClaims > 1`) task it posted.
|
|
127
|
+
*/
|
|
128
|
+
restorationBodyCache = new Map();
|
|
84
129
|
requestKinds = new Map();
|
|
85
130
|
claimedRestorationTaskIds = new Set();
|
|
86
131
|
evaluationOpportunities = new Map();
|
|
@@ -104,7 +149,7 @@ export class MechAdapter {
|
|
|
104
149
|
formatRpcError(message, {
|
|
105
150
|
operation: 'getBlockNumber',
|
|
106
151
|
chain: this.config.chainId === 84532 ? 'base-sepolia' : 'base',
|
|
107
|
-
rpcUrl: this.config.rpcUrl,
|
|
152
|
+
rpcUrl: rpcUrlForDisplay(this.config.rpcUrl),
|
|
108
153
|
}));
|
|
109
154
|
},
|
|
110
155
|
});
|
|
@@ -236,6 +281,35 @@ export class MechAdapter {
|
|
|
236
281
|
return;
|
|
237
282
|
this.persistPendingEvaluationSolutions();
|
|
238
283
|
}
|
|
284
|
+
clearPendingDeliveryRecoveryState(requestId) {
|
|
285
|
+
this.originalStates.delete(requestId);
|
|
286
|
+
this.pendingEvaluations.delete(requestId);
|
|
287
|
+
this.requestKinds.delete(requestId);
|
|
288
|
+
}
|
|
289
|
+
recoveryDeliveryExpirySeconds(requestId) {
|
|
290
|
+
const task = this.originalStates.get(requestId) ?? this.pendingEvaluations.get(requestId);
|
|
291
|
+
const claimPolicy = task?.claimPolicy ?? DEFAULT_MECH_CLAIM_POLICY;
|
|
292
|
+
const normalizeTsToSeconds = (value) => {
|
|
293
|
+
if (value == null)
|
|
294
|
+
return undefined;
|
|
295
|
+
return value > 10_000_000_000 ? Math.floor(value / 1000) : value;
|
|
296
|
+
};
|
|
297
|
+
const submissionDeadlineSeconds = normalizeTsToSeconds(claimPolicy.submissionDeadlineTs);
|
|
298
|
+
if (submissionDeadlineSeconds != null)
|
|
299
|
+
return submissionDeadlineSeconds;
|
|
300
|
+
const claimWindowEndSeconds = normalizeTsToSeconds(claimPolicy.claimWindowEndTs ?? task?.window?.endTs);
|
|
301
|
+
if (claimWindowEndSeconds == null)
|
|
302
|
+
return undefined;
|
|
303
|
+
return claimWindowEndSeconds + claimPolicy.claimLeaseTtlSeconds;
|
|
304
|
+
}
|
|
305
|
+
shouldSkipExpiredRecoveryDelivery(requestId, currentChainTimestampSeconds, recoveryExpirySeconds) {
|
|
306
|
+
if (currentChainTimestampSeconds <= recoveryExpirySeconds)
|
|
307
|
+
return false;
|
|
308
|
+
console.error(`[mech] skipping recovery delivery for ${requestId}: ` +
|
|
309
|
+
`submission deadline expired at ${new Date(recoveryExpirySeconds * 1000).toISOString()}`);
|
|
310
|
+
this.clearPendingDeliveryRecoveryState(requestId);
|
|
311
|
+
return true;
|
|
312
|
+
}
|
|
239
313
|
async postTask(state) {
|
|
240
314
|
const restorationState = {
|
|
241
315
|
...state,
|
|
@@ -264,18 +338,21 @@ export class MechAdapter {
|
|
|
264
338
|
const manifestDigest = keccak256(toBytes(signedTask.solverNetManifestCid));
|
|
265
339
|
const policy = this.contractPolicyForTask(restorationState);
|
|
266
340
|
const taskSubmission = await submitTask(this.publicClient, this.walletClient, this.config.safeAddress, this.config.routerAddress, restorationDataHex, manifestDigest, policy, deliveryRate, deliveryRate, maxTimeout, this.config.evictionRecovery);
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
341
|
+
// Deliberately do NOT seed `observedTasks` with the task we just posted.
|
|
342
|
+
// `observedTasks` is the dedup set for `watchForTasks`: the on-chain
|
|
343
|
+
// TaskCreated scan skips any taskId already in it (so a task is announced
|
|
344
|
+
// to the engine-watcher at most once). Seeding it here marked the
|
|
345
|
+
// creator's own task as "already announced" before it was ever yielded —
|
|
346
|
+
// which permanently prevented the *creator's own daemon* from discovering,
|
|
347
|
+
// claiming, and solving a task it posted. On a multi-attempt task
|
|
348
|
+
// (`maxClaims > 1`) the creator running the solver role is legitimate
|
|
349
|
+
// (the protocol forbids only self-*evaluation*, and that on a per-attempt
|
|
350
|
+
// basis), and on testnet it is the intended single-operator dogfood path
|
|
351
|
+
// — post → claim → solve → grade → settle from one daemon. The dedup is
|
|
352
|
+
// still correct: it now keys only on tasks `watchForTasks` actually
|
|
353
|
+
// yielded. `restorationAnnouncementForTaskId` re-hydrates from chain/IPFS
|
|
354
|
+
// on a cache miss, so dropping the pre-seed costs at most one redundant
|
|
355
|
+
// fetch if the creator later claims its own task.
|
|
279
356
|
return {
|
|
280
357
|
taskId: taskSubmission.taskId,
|
|
281
358
|
taskCid: restorationTaskCid,
|
|
@@ -348,7 +425,14 @@ export class MechAdapter {
|
|
|
348
425
|
maxClaimsPerOperator: claimPolicy.maxClaimsPerOperator,
|
|
349
426
|
policyHook: (claimPolicy.policyHook ?? zeroAddress),
|
|
350
427
|
evaluationPolicy: {
|
|
351
|
-
requiredVerdicts
|
|
428
|
+
// `requiredVerdicts` defaults to 1 but is overridable via the task's
|
|
429
|
+
// claim policy. A value > 1 opens additional verdict claim slots per
|
|
430
|
+
// attempt; combined with the per-evaluator cap below (1), it
|
|
431
|
+
// guarantees an honest evaluator can still claim and deliver a slot
|
|
432
|
+
// even when other evaluators have squatted some — the structural fix
|
|
433
|
+
// for a shared/adversarial testnet where a non-delivering claimer
|
|
434
|
+
// would otherwise permanently lock a single-slot attempt.
|
|
435
|
+
requiredVerdicts: claimPolicy.requiredVerdicts ?? 1,
|
|
352
436
|
passThreshold: 1,
|
|
353
437
|
evaluationDeadline: submissionDeadline + BigInt(claimPolicy.claimLeaseTtlSeconds),
|
|
354
438
|
maxVerdictsPerEvaluator: 1,
|
|
@@ -381,7 +465,11 @@ export class MechAdapter {
|
|
|
381
465
|
};
|
|
382
466
|
}
|
|
383
467
|
async restorationAnnouncementForTaskId(taskId) {
|
|
384
|
-
|
|
468
|
+
// Read-through `restorationBodyCache` — NOT `observedTasks`. This helper is
|
|
469
|
+
// an evaluation-path lookup of a task's restoration body; caching it into
|
|
470
|
+
// the `watchForTasks` discovery dedup set would suppress the creator's own
|
|
471
|
+
// restoration-claim discovery for the same taskId (see field comment).
|
|
472
|
+
const cached = this.restorationBodyCache.get(taskId) ?? this.observedTasks.get(taskId);
|
|
385
473
|
if (cached)
|
|
386
474
|
return cached;
|
|
387
475
|
const taskCidDigest = await getTaskCidDigest(this.publicClient, this.config.routerAddress, taskId);
|
|
@@ -394,7 +482,7 @@ export class MechAdapter {
|
|
|
394
482
|
task,
|
|
395
483
|
taskCid,
|
|
396
484
|
};
|
|
397
|
-
this.
|
|
485
|
+
this.restorationBodyCache.set(taskId, announcement);
|
|
398
486
|
return announcement;
|
|
399
487
|
}
|
|
400
488
|
async restorationAnnouncementFromDigest(params) {
|
|
@@ -465,19 +553,40 @@ export class MechAdapter {
|
|
|
465
553
|
continue;
|
|
466
554
|
}
|
|
467
555
|
try {
|
|
556
|
+
// Yield every hydrated candidate per cycle rather than returning after
|
|
557
|
+
// the first. The engine-watcher (daemon._runEngineWatcherLoop) is the
|
|
558
|
+
// single point of skip-state truth — when its in-flight admission gate
|
|
559
|
+
// fast-skips a candidate (~30s TTL), that skip state never flows back
|
|
560
|
+
// into the adapter's iteration cursor. Yielding only the first
|
|
561
|
+
// candidate per cycle meant a fast-skipped slot starved every
|
|
562
|
+
// subsequent candidate in the round-robin (`fc05f686`) ordering for
|
|
563
|
+
// the duration of the TTL. By driving the full candidate list per
|
|
564
|
+
// cycle we let the engine apply its gate to each one, preserving the
|
|
565
|
+
// round-robin fairness across joined SolverNets. See task 212 live
|
|
566
|
+
// verification in the fix's commit body.
|
|
468
567
|
yield await this.restorationAnnouncementFromDigest({
|
|
469
568
|
taskId: candidate.taskId,
|
|
470
569
|
taskCidDigest: candidate.taskCidDigest,
|
|
471
570
|
transactionHash: candidate.createdAtTx,
|
|
472
571
|
blockNumber: candidate.createdAtBlock,
|
|
473
572
|
});
|
|
474
|
-
return;
|
|
475
573
|
}
|
|
476
574
|
catch (err) {
|
|
477
575
|
console.error(`[mech] failed to hydrate subgraph task ${candidate.taskId}:`, err instanceof Error ? err.message : err);
|
|
478
576
|
}
|
|
479
577
|
}
|
|
480
578
|
}
|
|
579
|
+
/**
|
|
580
|
+
* Look up the Deliver-event envelope CID for a pending evaluation solution.
|
|
581
|
+
*
|
|
582
|
+
* Returns `null` when the Deliver event is not present in the configured
|
|
583
|
+
* lookback window. This is a terminal signal for the caller — re-running the
|
|
584
|
+
* same lookup later is deterministically futile when `solution.blockNumber`
|
|
585
|
+
* is set (toBlock is fixed at the SolutionDeliveryClaimed block), and is
|
|
586
|
+
* monotonically less likely to find the event when toBlock follows chain head
|
|
587
|
+
* (the window slides forward, away from any older Deliver event). Callers
|
|
588
|
+
* should prune the pending solution on `null` rather than retry — see #553.
|
|
589
|
+
*/
|
|
481
590
|
async deliveryEnvelopeCidForSolution(solution) {
|
|
482
591
|
const deliveryMech = await getMarketplaceRequestDeliveryMech(this.publicClient, this.config.mechMarketplaceAddress, solution.requestId);
|
|
483
592
|
const toBlock = solution.blockNumber != null
|
|
@@ -488,8 +597,7 @@ export class MechAdapter {
|
|
|
488
597
|
const fromBlock = toBlock > lookback ? toBlock - lookback : 0n;
|
|
489
598
|
const deliveryDataHex = await findLatestDeliveryDataHexForRequest(this.publicClient, deliveryMech, solution.requestId, fromBlock, toBlock);
|
|
490
599
|
if (!deliveryDataHex) {
|
|
491
|
-
|
|
492
|
-
`between blocks ${fromBlock} and ${toBlock}`);
|
|
600
|
+
return null;
|
|
493
601
|
}
|
|
494
602
|
const digest = deliveryDataHex.startsWith('0x') ? deliveryDataHex.slice(2) : deliveryDataHex;
|
|
495
603
|
return `f01551220${digest}`;
|
|
@@ -500,14 +608,34 @@ export class MechAdapter {
|
|
|
500
608
|
this.forgetPendingEvaluationSolution(solution.requestId);
|
|
501
609
|
return undefined;
|
|
502
610
|
}
|
|
503
|
-
|
|
611
|
+
// Cheap claimability gate FIRST — before the restoration lookup + IPFS
|
|
612
|
+
// fetch. A backlog of terminal opportunities (finalized / evaluation
|
|
613
|
+
// deadline passed / max verdicts reached) must not pay the expensive
|
|
614
|
+
// restoration-announcement cost on every poll cycle. Terminal reasons are
|
|
615
|
+
// pruned from the working set so the loop never re-scans on-chain history;
|
|
616
|
+
// transient reasons are left in place to be retried next cycle.
|
|
504
617
|
const claimable = await canClaimEvaluation(this.publicClient, this.config.safeAddress, this.config.routerAddress, solution.taskId, solution.attemptIndex, this.config.mechContractAddress);
|
|
505
618
|
if (!claimable.ok) {
|
|
506
|
-
|
|
507
|
-
|
|
619
|
+
const terminal = isTerminalEvaluationReason(claimable.revertName);
|
|
620
|
+
console.log(`[mech] skipping evaluation opportunity ${solution.requestId} for task ${solution.taskId}/${solution.attemptIndex}: ${claimable.reason}` +
|
|
621
|
+
(terminal ? ' (terminal — pruned)' : ' (transient — will retry)'));
|
|
622
|
+
if (terminal) {
|
|
623
|
+
this.forgetPendingEvaluationSolution(solution.requestId);
|
|
624
|
+
}
|
|
508
625
|
return undefined;
|
|
509
626
|
}
|
|
627
|
+
const restoration = await this.restorationAnnouncementForTaskId(solution.taskId);
|
|
510
628
|
const solutionEnvelopeCid = await this.deliveryEnvelopeCidForSolution(solution);
|
|
629
|
+
if (solutionEnvelopeCid == null) {
|
|
630
|
+
// #553: Deliver event is not within the configured lookback window. A
|
|
631
|
+
// retry with the same toBlock cannot reach an older event, so this is
|
|
632
|
+
// terminal — prune so the loop never re-pays the canClaimEvaluation +
|
|
633
|
+
// restoration lookup cost on a deterministically-failing opportunity.
|
|
634
|
+
console.log(`[mech] pruning evaluation opportunity ${solution.requestId} for task ${solution.taskId}/${solution.attemptIndex}: ` +
|
|
635
|
+
`no Deliver event found within configured lookback (terminal — pruned)`);
|
|
636
|
+
this.forgetPendingEvaluationSolution(solution.requestId);
|
|
637
|
+
return undefined;
|
|
638
|
+
}
|
|
511
639
|
const resultPayload = await fetchFromIpfs(this.config.ipfsGatewayUrl, solutionEnvelopeCid);
|
|
512
640
|
const resultData = resultPayload.data ?? JSON.stringify(resultPayload);
|
|
513
641
|
const evaluationTask = this.buildEvaluationTask({
|
|
@@ -535,15 +663,21 @@ export class MechAdapter {
|
|
|
535
663
|
return announcement;
|
|
536
664
|
}
|
|
537
665
|
async *retryPendingEvaluationSolutions() {
|
|
666
|
+
let processed = 0;
|
|
538
667
|
for (const [requestId, solution] of Array.from(this.pendingEvaluationSolutions)) {
|
|
668
|
+
// Yield to the event loop periodically so a large backlog of pending
|
|
669
|
+
// evaluation solutions can't starve the HTTP API mid-cycle.
|
|
670
|
+
if (processed > 0 && processed % EVALUATION_RETRY_YIELD_EVERY === 0) {
|
|
671
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
672
|
+
}
|
|
673
|
+
processed++;
|
|
539
674
|
try {
|
|
540
675
|
const announcement = await this.evaluationAnnouncementForSolution(solution);
|
|
541
676
|
if (announcement) {
|
|
542
677
|
yield announcement;
|
|
543
678
|
}
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
}
|
|
679
|
+
// No announcement does NOT mean "forget" — pruning is owned by
|
|
680
|
+
// evaluationAnnouncementForSolution, which only removes terminal cases.
|
|
547
681
|
}
|
|
548
682
|
catch (err) {
|
|
549
683
|
console.error(`[mech] evaluation opportunity retry failed for ${requestId}:`, err);
|
|
@@ -605,7 +739,7 @@ export class MechAdapter {
|
|
|
605
739
|
console.error('[mech] Error polling for tasks:', formatRpcError(err, {
|
|
606
740
|
operation: 'pollTaskCreated',
|
|
607
741
|
chain: this.config.chainId === 84532 ? 'base-sepolia' : 'base',
|
|
608
|
-
rpcUrl: this.config.rpcUrl,
|
|
742
|
+
rpcUrl: rpcUrlForDisplay(this.config.rpcUrl),
|
|
609
743
|
contract: this.config.routerAddress,
|
|
610
744
|
fromBlock: this.requestBlockCursor + 1n,
|
|
611
745
|
}));
|
|
@@ -676,9 +810,13 @@ export class MechAdapter {
|
|
|
676
810
|
async submitVerdictDelivery(requestId, verdictDigest, verdictCode) {
|
|
677
811
|
await claimDelivery(this.publicClient, this.walletClient, this.config.safeAddress, this.config.routerAddress, requestId, { variant: 'v3', kind: 'verdict', evidenceHash: verdictDigest, verdictCode }, this.config.evictionRecovery);
|
|
678
812
|
}
|
|
679
|
-
async
|
|
813
|
+
async deliveryClaimForDelivery(requestId, deliveryDataHex) {
|
|
814
|
+
const fallbackKind = this.requestKinds.get(requestId) ?? 'solution';
|
|
680
815
|
if (this.config.routerClaimDeliveryVariant !== 'v2' && this.config.routerClaimDeliveryVariant !== 'v3') {
|
|
681
|
-
return
|
|
816
|
+
return {
|
|
817
|
+
evidenceHash: undefined,
|
|
818
|
+
kind: fallbackKind,
|
|
819
|
+
};
|
|
682
820
|
}
|
|
683
821
|
const deliveryDigest = deliveryDataHex.startsWith('0x')
|
|
684
822
|
? deliveryDataHex.slice(2)
|
|
@@ -686,11 +824,6 @@ export class MechAdapter {
|
|
|
686
824
|
const envelopeCid = `f01551220${deliveryDigest}`;
|
|
687
825
|
const rawEnvelope = await fetchSignedEnvelopeFromIpfs(this.config.ipfsGatewayUrl, envelopeCid);
|
|
688
826
|
const parsed = SignedEnvelopeSchema.parse(rawEnvelope);
|
|
689
|
-
// Strip signature to recompute the hash over the unsigned body.
|
|
690
|
-
//
|
|
691
|
-
// Important: compute over the fetched wire object, not over the parsed
|
|
692
|
-
// schema result. The schema normalizes some nested objects and may strip
|
|
693
|
-
// extension metadata that was present when the envelope was signed.
|
|
694
827
|
const rawSigned = rawEnvelope;
|
|
695
828
|
const { signature: _rawSignature, ...unsignedBody } = rawSigned;
|
|
696
829
|
const signature = parsed.signature;
|
|
@@ -699,22 +832,36 @@ export class MechAdapter {
|
|
|
699
832
|
if (recomputed !== signature.hash) {
|
|
700
833
|
throw new Error(`recomputed hash ${recomputed} !== envelope.signature.hash ${signature.hash}`);
|
|
701
834
|
}
|
|
702
|
-
|
|
835
|
+
const role = normalizeEnvelopeRole(parsed.role);
|
|
836
|
+
if (role === 'capture') {
|
|
837
|
+
throw new Error(`unsupported delivery envelope role=capture for requestId ${requestId}`);
|
|
838
|
+
}
|
|
839
|
+
const kind = role === 'verdict' ? 'verdict' : 'solution';
|
|
840
|
+
const payload = rawSigned['payload'];
|
|
841
|
+
const rawVerdict = payload != null && typeof payload === 'object'
|
|
842
|
+
? payload['verdict']
|
|
843
|
+
: undefined;
|
|
844
|
+
return {
|
|
845
|
+
evidenceHash: recomputed,
|
|
846
|
+
kind,
|
|
847
|
+
verdictCode: kind === 'verdict' ? verdictCodeFromValue(rawVerdict) : undefined,
|
|
848
|
+
};
|
|
703
849
|
}
|
|
704
850
|
async ensureDeliveryClaimed(requestId, deliveryDataHex) {
|
|
705
|
-
let
|
|
851
|
+
let claimOptions;
|
|
706
852
|
try {
|
|
707
|
-
|
|
853
|
+
claimOptions = await this.deliveryClaimForDelivery(requestId, deliveryDataHex);
|
|
708
854
|
}
|
|
709
855
|
catch (err) {
|
|
710
|
-
console.error(`[mech]
|
|
856
|
+
console.error(`[mech] delivery claim metadata derivation failed for ${requestId} — skipping claim, will retry on next loop:`, err);
|
|
711
857
|
return 'retry';
|
|
712
858
|
}
|
|
713
859
|
try {
|
|
714
860
|
await claimDelivery(this.publicClient, this.walletClient, this.config.safeAddress, this.config.routerAddress, requestId, {
|
|
715
861
|
variant: this.config.routerClaimDeliveryVariant,
|
|
716
|
-
kind:
|
|
717
|
-
evidenceHash,
|
|
862
|
+
kind: claimOptions.kind,
|
|
863
|
+
evidenceHash: claimOptions.evidenceHash,
|
|
864
|
+
verdictCode: claimOptions.verdictCode,
|
|
718
865
|
}, this.config.evictionRecovery);
|
|
719
866
|
return 'claimed';
|
|
720
867
|
}
|
|
@@ -728,22 +875,66 @@ export class MechAdapter {
|
|
|
728
875
|
return 'already-claimed';
|
|
729
876
|
}
|
|
730
877
|
console.error(`[mech] claimDelivery failed for ${requestId}:`, err);
|
|
878
|
+
// Paired SSE signal for the operator-app `claim_failed` notification
|
|
879
|
+
// (OPERATOR-APP-SPEC §2.10). The early-return branches above
|
|
880
|
+
// (`skipped` / `already-claimed`) are not failures and intentionally do not emit.
|
|
881
|
+
emitStructured({
|
|
882
|
+
kind: 'intent',
|
|
883
|
+
message: 'Delivery claim failed',
|
|
884
|
+
requestId,
|
|
885
|
+
errorCode: 'claim_failed',
|
|
886
|
+
details: {
|
|
887
|
+
kind: claimOptions.kind,
|
|
888
|
+
source: 'mech.claimDelivery',
|
|
889
|
+
error: message,
|
|
890
|
+
},
|
|
891
|
+
});
|
|
731
892
|
return 'retry';
|
|
732
893
|
}
|
|
733
894
|
}
|
|
895
|
+
/**
|
|
896
|
+
* Paginate `getLogs` over `[deliveryBlockCursor+1, currentBlock]` chunked by
|
|
897
|
+
* `DEFAULT_ROUTER_LOG_CHUNK_BLOCKS` to honor RPC provider block-range limits
|
|
898
|
+
* (Tenderly base-sepolia caps at 100k; sepolia.base.org ~1k). Advances +
|
|
899
|
+
* persists `deliveryBlockCursor` per chunk so a mid-scan RPC failure on a
|
|
900
|
+
* later chunk does not strand the cursor at the pre-poll value (#552).
|
|
901
|
+
*
|
|
902
|
+
* Yields each chunk's decoded Deliver entries so the consumer can process
|
|
903
|
+
* them with the live "current block" context (needed for the recovery-
|
|
904
|
+
* delivery timestamp cache).
|
|
905
|
+
*/
|
|
906
|
+
async *scanDeliveryLogChunks(currentBlock) {
|
|
907
|
+
while (currentBlock > this.deliveryBlockCursor) {
|
|
908
|
+
const chunkStart = this.deliveryBlockCursor + 1n;
|
|
909
|
+
const chunkEnd = chunkStart + DEFAULT_ROUTER_LOG_CHUNK_BLOCKS > currentBlock
|
|
910
|
+
? currentBlock
|
|
911
|
+
: chunkStart + DEFAULT_ROUTER_LOG_CHUNK_BLOCKS;
|
|
912
|
+
const logs = await this.publicClient.getLogs({
|
|
913
|
+
address: this.config.mechContractAddress,
|
|
914
|
+
fromBlock: chunkStart,
|
|
915
|
+
toBlock: chunkEnd,
|
|
916
|
+
});
|
|
917
|
+
// Advance + persist BEFORE yielding so partial progress is durable even
|
|
918
|
+
// if a downstream throw escapes back through the for-await consumer.
|
|
919
|
+
this.deliveryBlockCursor = chunkEnd;
|
|
920
|
+
if (this.store) {
|
|
921
|
+
this.store.setLastProcessedBlock(this.deliveryBlockCursor);
|
|
922
|
+
}
|
|
923
|
+
yield decodeDeliverLogs(logs);
|
|
924
|
+
}
|
|
925
|
+
}
|
|
734
926
|
async *watchForDeliveries() {
|
|
735
927
|
while (!this.stopped) {
|
|
736
928
|
try {
|
|
737
929
|
const currentBlock = await this.publicClient.getBlockNumber();
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
const
|
|
746
|
-
for (const { requestId, deliveryDataHex, mechAddress } of decoded) {
|
|
930
|
+
// Caches scoped to the whole poll iteration: the "current block"
|
|
931
|
+
// reference does not change across chunks.
|
|
932
|
+
const blockTimestampSecondsByNumber = new Map();
|
|
933
|
+
let currentBlockTimestampSeconds;
|
|
934
|
+
for await (const decoded of this.scanDeliveryLogChunks(currentBlock)) {
|
|
935
|
+
if (this.stopped)
|
|
936
|
+
break;
|
|
937
|
+
for (const { requestId, deliveryDataHex, mechAddress, blockNumber } of decoded) {
|
|
747
938
|
// Two concerns, independent:
|
|
748
939
|
// (a) Did this Safe DELIVER this? → claim it (counter credit goes to msg.sender)
|
|
749
940
|
// The Deliver event's mechAddress is mechServiceMultisig (the Safe that owns
|
|
@@ -753,6 +944,30 @@ export class MechAdapter {
|
|
|
753
944
|
const iCreatedRestoration = this.pendingEvaluations.has(requestId);
|
|
754
945
|
if (!iDelivered && !iCreatedRestoration)
|
|
755
946
|
continue;
|
|
947
|
+
if (iCreatedRestoration) {
|
|
948
|
+
const recoveryExpirySeconds = this.recoveryDeliveryExpirySeconds(requestId);
|
|
949
|
+
if (recoveryExpirySeconds != null) {
|
|
950
|
+
let deliveryTimestampSeconds;
|
|
951
|
+
if (blockNumber != null) {
|
|
952
|
+
deliveryTimestampSeconds = blockTimestampSecondsByNumber.get(blockNumber);
|
|
953
|
+
if (deliveryTimestampSeconds == null) {
|
|
954
|
+
const deliveryBlockData = await this.publicClient.getBlock({ blockNumber });
|
|
955
|
+
deliveryTimestampSeconds = Number(deliveryBlockData.timestamp);
|
|
956
|
+
blockTimestampSecondsByNumber.set(blockNumber, deliveryTimestampSeconds);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
else {
|
|
960
|
+
if (currentBlockTimestampSeconds == null) {
|
|
961
|
+
const currentBlockData = await this.publicClient.getBlock({ blockNumber: currentBlock });
|
|
962
|
+
currentBlockTimestampSeconds = Number(currentBlockData.timestamp);
|
|
963
|
+
}
|
|
964
|
+
deliveryTimestampSeconds = currentBlockTimestampSeconds;
|
|
965
|
+
}
|
|
966
|
+
if (this.shouldSkipExpiredRecoveryDelivery(requestId, deliveryTimestampSeconds, recoveryExpirySeconds)) {
|
|
967
|
+
continue;
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
}
|
|
756
971
|
// (a) Deliverer-side claim path: if this Safe delivered the request,
|
|
757
972
|
// claim it first so router counters credit the deliverer.
|
|
758
973
|
let deliveryClaimStatus;
|
|
@@ -788,9 +1003,7 @@ export class MechAdapter {
|
|
|
788
1003
|
deliveryMechAddress: mechAddress,
|
|
789
1004
|
};
|
|
790
1005
|
// Clean up after yielding
|
|
791
|
-
this.
|
|
792
|
-
this.pendingEvaluations.delete(requestId);
|
|
793
|
-
this.requestKinds.delete(requestId);
|
|
1006
|
+
this.clearPendingDeliveryRecoveryState(requestId);
|
|
794
1007
|
}
|
|
795
1008
|
catch (err) {
|
|
796
1009
|
console.error(`[mech] Failed to parse delivery ${requestId}:`, err);
|
|
@@ -802,15 +1015,13 @@ export class MechAdapter {
|
|
|
802
1015
|
console.error('[mech] Error polling for deliveries:', formatRpcError(err, {
|
|
803
1016
|
operation: 'pollDeliveries',
|
|
804
1017
|
chain: this.config.chainId === 84532 ? 'base-sepolia' : 'base',
|
|
805
|
-
rpcUrl: this.config.rpcUrl,
|
|
1018
|
+
rpcUrl: rpcUrlForDisplay(this.config.rpcUrl),
|
|
806
1019
|
contract: this.config.mechContractAddress,
|
|
807
1020
|
fromBlock: this.deliveryBlockCursor + 1n,
|
|
808
1021
|
}));
|
|
809
1022
|
}
|
|
810
|
-
//
|
|
811
|
-
|
|
812
|
-
this.store.setLastProcessedBlock(this.deliveryBlockCursor);
|
|
813
|
-
}
|
|
1023
|
+
// Cursor persistence is per-chunk inside the loop above (#552). A poll
|
|
1024
|
+
// that did no chunked work has no progress to persist.
|
|
814
1025
|
await new Promise(r => setTimeout(r, this.config.pollIntervalMs));
|
|
815
1026
|
}
|
|
816
1027
|
}
|