diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..a3f91469 --- /dev/null +++ b/.env.example @@ -0,0 +1,16 @@ +# FlowMemory local/test launch configuration. +# Copy values into an untracked .env file or export them in your shell. + +# Base Sepolia JSON-RPC endpoint. The reader rejects non-84532 chains. +BASE_SEPOLIA_RPC_URL= + +# Hex deployer key for Foundry script runs. Keep the real value out of Git. +BASE_SEPOLIA_DEPLOYER_KEY_HEX= + +# Comma-separated FlowPulse-capable contract addresses for testnet reads. +BASE_SEPOLIA_FLOWPULSE_ADDRESSES= +BASE_SEPOLIA_FROM_BLOCK= +BASE_SEPOLIA_TO_BLOCK= +BASE_SEPOLIA_FINALIZED_BLOCK= + +FLOWMEMORY_DASHBOARD_DATA_PATH=apps/dashboard/public/data/flowmemory-dashboard-v0.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3ff0732d..e236baa2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -130,6 +130,12 @@ jobs: - name: Run service e2e run: npm run e2e + - name: Validate launch schemas + run: npm run validate:launch + + - name: Check generated fixture drift + run: npm run fixtures:check + launch-core: name: Launch-core acceptance command runs-on: ubuntu-latest @@ -157,6 +163,9 @@ jobs: - name: Run launch V0 command run: npm run launch:v0 + - name: Validate launch schemas + run: npm run validate:launch + crypto: name: Crypto runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index 3473d28e..1ed7aa60 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,10 @@ node_modules/ dist/ cache/ out/ +.env +.env.* +!.env.example +broadcast/ devnet/local/ crates/flowmemory-devnet/target/ diff --git a/README.md b/README.md index d214adf7..04d8a206 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ FlowMemory is managed as a multi-agent program. The management layer is part of - `docs/MARKETING_CLAIMS_GUARDRAILS.md`: allowed and blocked launch claims for docs and marketing - `infra/scripts/status-report.ps1`: read-only local worktree, PR, and issue status report -Immediate major milestone: build the Rootflow V0 and Flow Memory V0 launch core. This means local contracts/tests, FlowPulse fixtures, Rootflow transitions, Flow Memory schemas, verifier reports, crypto fixtures, dashboard-readable state, and local smoke-test gates. It does not mean production deployment. +Immediate major milestone: keep the Rootflow V0 and Flow Memory V0 launch core green. This means local contracts/tests, FlowPulse fixtures, Uniswap swap-derived memory-signal fixtures, Rootflow transitions, Flow Memory schemas, verifier reports, crypto fixtures, dashboard-readable state, Base Sepolia testnet read/deploy commands, and local smoke-test gates. It does not mean production deployment. Run the local launch-core path: @@ -71,11 +71,33 @@ npm run launch:v0 This regenerates local/test Rootflow and Flow Memory V0 fixtures, including `fixtures/launch-core/flowmemory-launch-v0.json`, `fixtures/launch-core/rootflow-transitions.json`, and the dashboard fixture at `fixtures/dashboard/flowmemory-dashboard-v0.json`. +Run the stricter local launch-candidate gate: + +```powershell +npm run launch:candidate +``` + +That command runs contract hardening, the launch flow, runtime schema validation, fixture drift checks, and claim guardrails. + +Build the dashboard after regenerating launch data: + +```powershell +npm run build:production +``` + +Base Sepolia testnet commands require local environment values from `.env.example`: + +```powershell +npm run deploy:base-sepolia +npm run deploy:base-sepolia:broadcast +npm run read:base-sepolia -- --rpc-url --address --from-block --to-block +``` + ## What Not To Claim -- Do not claim FlowMemory has production contracts or deployment automation. +- Do not claim FlowMemory has production contracts or a mainnet deployment. - Do not claim FlowMemory is production-ready or mainnet-ready. -- Do not claim Uniswap v4 hook integration exists yet. +- Do not claim the current hook adapter is a production Uniswap v4 hook. - Do not claim explorer, hardware console, production FlowRouter hardware, or Meshtastic integration exists yet. - Do not claim cryptographic proof systems, tokenomics, or appchain/L1 implementation exists yet. - Do not claim URI fields enforce off-chain storage. Current URI values are caller-supplied log data. @@ -108,7 +130,10 @@ This regenerates local/test Rootflow and Flow Memory V0 fixtures, including `fix - Foundry tests for the Rootfield registry foundation and live V0 contract package - fixture-first indexer/verifier packages and local launch-core generation - Base Sepolia reader path with explicit RPC URL and durable checkpoint output +- Base Sepolia deployment runner for the current V0 testnet contract set +- FlowMemoryHookAdapter emits a `SWAP_MEMORY_SIGNAL` FlowPulse for the swap-memory fixture path - Flow Memory V0 schemas and generated Rootflow transition fixtures +- runtime schema validation and generated fixture drift checks for launch-core outputs - fixture-backed dashboard V0 - crypto helper package and test vectors - local no-value devnet prototype @@ -117,7 +142,7 @@ This regenerates local/test Rootflow and Flow Memory V0 fixtures, including `fix ## Still Conceptual -- Uniswap v4 hook integration +- Production Uniswap v4 hook integration - Production indexer and verifier services - Production Rootflow runtime implementation - Production Flow Memory runtime implementation diff --git a/apps/dashboard/public/data/flowmemory-dashboard-v0.json b/apps/dashboard/public/data/flowmemory-dashboard-v0.json index 299c9566..23277afe 100644 --- a/apps/dashboard/public/data/flowmemory-dashboard-v0.json +++ b/apps/dashboard/public/data/flowmemory-dashboard-v0.json @@ -88,6 +88,38 @@ "localPathHint": "services/indexer/out/indexer-state.json" } }, + { + "id": "0x62e6ea1899a4dfd0d0a6cf6010ffb98983db20a9755257c8288602d059aa169e", + "observationId": "0x62e6ea1899a4dfd0d0a6cf6010ffb98983db20a9755257c8288602d059aa169e", + "pulseId": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab008", + "rootfieldId": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "eventSignature": "0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43", + "blockNumber": "123457", + "blockHash": "0x2222222222222222222222222222222222222222222222222222222222222230", + "txHash": "0x3333333333333333333333333333333333333333333333333333333333333341", + "transactionIndex": "15", + "logIndex": "10", + "receiptStatus": "success", + "actor": "0x4444444444444444444444444444444444444444", + "pulseType": "4", + "subject": "0x1212121212121212121212121212121212121212121212121212121212121212", + "commitment": "0x8fabbad2f4aae2b67e5dcacc7cd425b7860d4d4453488c6c9770196b5009d795", + "parentPulseId": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab001", + "sequence": "6", + "occurredAt": "2026-05-13T05:00:20.000Z", + "uri": "fixture://swap-memory-valid", + "summary": "swap memory signal from generated indexer output", + "status": "finalized", + "lastUpdated": "2026-05-13T17:02:00.000Z", + "provenance": { + "subsystem": "indexer", + "origin": "fixture", + "chainContext": "flowmemory-local-v0", + "fixturePath": "fixtures/launch-core/flowmemory-launch-v0.json", + "capturedAt": "2026-05-13T17:02:00.000Z", + "localPathHint": "services/indexer/out/indexer-state.json" + } + }, { "id": "0x9d958aadf8bf46f989b51e541709a73d21970e7e79643f939c9a0000b50f9a91", "observationId": "0x9d958aadf8bf46f989b51e541709a73d21970e7e79643f939c9a0000b50f9a91", @@ -251,14 +283,14 @@ ], "rootfields": [ { - "id": "0x31cf738173ed06b94ab86b99f2337c348040e50086a2d2f1f83ff9b0960fdbe6", + "id": "0xc296618c8c57cbd6c4cc67cbf38e2ed31357177b565b4660b76e041c7189d5e1", "rootfieldId": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", "owner": "0x4444444444444444444444444444444444444444", "schemaHash": "0x4c9929a2e216b475bd5bb68ac234d74d70ceadfbb86a948a62535413118371a8", "metadataHash": "0x32ae92d97373a790718b78a4bd0900e33c763de3c2cb43670f5d9b8a7ae7c8d1", "latestRoot": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1", "latestObservationId": "0x5ba8c5a70c814d35482df5f6598e549f83f2d0e27f6e3d25b83eb2a0e1d56a98", - "pulseCount": 7, + "pulseCount": 8, "workLaneIds": [ "MEMORY_REFRESH", "FAILURE_DISCOVERY", @@ -285,7 +317,7 @@ "name": "Memory refresh", "queueDepth": 1, "inflight": 0, - "completed24h": 3, + "completed24h": 4, "p95LatencyMs": 640, "operator": "fixture-worker", "status": "pending", @@ -367,6 +399,29 @@ "localPathHint": "services/verifier/out/reports.json" } }, + { + "id": "0x1706cc0af00148a0633b33e73856690ceea895f6c44bfe02c4d4a9819429dbce", + "receiptId": "0x1706cc0af00148a0633b33e73856690ceea895f6c44bfe02c4d4a9819429dbce", + "laneId": "MEMORY_REFRESH", + "rootfieldId": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "observationId": "0x62e6ea1899a4dfd0d0a6cf6010ffb98983db20a9755257c8288602d059aa169e", + "reportId": "0xe0d47250f60e6d4764173e6ec13b29b06b9279abf461a5c7be4caf45374d8b40", + "workType": "VERIFIER_REPORT_TO_MEMORY_RECEIPT", + "artifactUri": "fixture://swap-memory-valid", + "startedAt": "2026-05-13T17:02:00.000Z", + "completedAt": "2026-05-13T17:02:00.000Z", + "resultHash": "0xe0d47250f60e6d4764173e6ec13b29b06b9279abf461a5c7be4caf45374d8b40", + "status": "verified", + "lastUpdated": "2026-05-13T17:02:00.000Z", + "provenance": { + "subsystem": "verifier", + "origin": "fixture", + "chainContext": "flowmemory-local-v0", + "fixturePath": "fixtures/launch-core/flowmemory-launch-v0.json", + "capturedAt": "2026-05-13T17:02:00.000Z", + "localPathHint": "services/verifier/out/reports.json" + } + }, { "id": "0x48066ab6febdedb523cfcae2f313752db7e80467641d80914ebfe2e079d8c49d", "receiptId": "0x48066ab6febdedb523cfcae2f313752db7e80467641d80914ebfe2e079d8c49d", @@ -527,6 +582,28 @@ "localPathHint": "services/verifier/out/reports.json" } }, + { + "id": "0xe0d47250f60e6d4764173e6ec13b29b06b9279abf461a5c7be4caf45374d8b40", + "reportId": "0xe0d47250f60e6d4764173e6ec13b29b06b9279abf461a5c7be4caf45374d8b40", + "observationId": "0x62e6ea1899a4dfd0d0a6cf6010ffb98983db20a9755257c8288602d059aa169e", + "rootfieldId": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "resolverPolicyId": "flowmemory.resolver.policy.v0.fixture", + "verifierSpecVersion": "0", + "checksPassed": 3, + "checksTotal": 3, + "reasonCodes": [], + "reportHash": "0xe0d47250f60e6d4764173e6ec13b29b06b9279abf461a5c7be4caf45374d8b40", + "status": "verified", + "lastUpdated": "2026-05-13T17:02:00.000Z", + "provenance": { + "subsystem": "verifier", + "origin": "fixture", + "chainContext": "flowmemory-local-v0", + "fixturePath": "fixtures/launch-core/flowmemory-launch-v0.json", + "capturedAt": "2026-05-13T17:02:00.000Z", + "localPathHint": "services/verifier/out/reports.json" + } + }, { "id": "0x03af9720a87f7d72ce2ce95c1d04d50134649da51ace802d88818ad67fb2ebb3", "reportId": "0x03af9720a87f7d72ce2ce95c1d04d50134649da51ace802d88818ad67fb2ebb3", @@ -727,6 +804,46 @@ "localPathHint": "fixtures/launch-core/rootflow-transitions.json" } }, + { + "schema": "flowmemory.rootflow_transition.v0", + "transitionId": "0x2d4473b0c1eff862ed3d0db7caed9b2c4aff1f1c8668ace6683b9b33573d9349", + "rootfieldId": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "observationId": "0x62e6ea1899a4dfd0d0a6cf6010ffb98983db20a9755257c8288602d059aa169e", + "pulseId": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab008", + "parentPulseId": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab001", + "parentTransitionId": "0x5cdf7f5498350650adc19c62828425723a09bcca6571774992ca2d013fdc1068", + "memorySignalId": "0xa0b1c648fb150b661e30a45dbccec410b1c3d510d94a7ac907e59141dc1cb36c", + "memoryReceiptId": "0x1706cc0af00148a0633b33e73856690ceea895f6c44bfe02c4d4a9819429dbce", + "reportId": "0xe0d47250f60e6d4764173e6ec13b29b06b9279abf461a5c7be4caf45374d8b40", + "previousRoot": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1", + "attemptedRoot": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1", + "nextRoot": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1", + "status": "verified", + "blockNumber": "123457", + "txHash": "0x3333333333333333333333333333333333333333333333333333333333333341", + "sequence": "6", + "reasonCodes": [], + "contractEventRef": { + "signalId": "0xa0b1c648fb150b661e30a45dbccec410b1c3d510d94a7ac907e59141dc1cb36c", + "eventName": "FlowPulse", + "eventTopic0": "0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43", + "sourceContract": "0x2222222222222222222222222222222222222222", + "pulseTypeId": "4", + "pulseTypeName": "SWAP_MEMORY_SIGNAL", + "txHash": "0x3333333333333333333333333333333333333333333333333333333333333341", + "logIndex": "10" + }, + "id": "0x2d4473b0c1eff862ed3d0db7caed9b2c4aff1f1c8668ace6683b9b33573d9349", + "lastUpdated": "2026-05-13T17:02:00.000Z", + "provenance": { + "subsystem": "indexer", + "origin": "fixture", + "chainContext": "flowmemory-local-v0", + "fixturePath": "fixtures/launch-core/flowmemory-launch-v0.json", + "capturedAt": "2026-05-13T17:02:00.000Z", + "localPathHint": "fixtures/launch-core/rootflow-transitions.json" + } + }, { "schema": "flowmemory.rootflow_transition.v0", "transitionId": "0xe1b6f858279961c968cdfba0b85d489767c486289e824cab3d017997064fed89", @@ -1122,6 +1239,81 @@ "localPathHint": "fixtures/launch-core/flowmemory-launch-v0.json" } }, + { + "schema": "flowmemory.memory_signal.v0", + "signalId": "0xa0b1c648fb150b661e30a45dbccec410b1c3d510d94a7ac907e59141dc1cb36c", + "observationId": "0x62e6ea1899a4dfd0d0a6cf6010ffb98983db20a9755257c8288602d059aa169e", + "pulseId": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab008", + "rootfieldId": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "signalType": "swap_memory_signal", + "status": "verified", + "chainId": "8453", + "emittingContract": "0x2222222222222222222222222222222222222222", + "blockNumber": "123457", + "blockHash": "0x2222222222222222222222222222222222222222222222222222222222222230", + "txHash": "0x3333333333333333333333333333333333333333333333333333333333333341", + "transactionIndex": "15", + "logIndex": "10", + "actor": "0x4444444444444444444444444444444444444444", + "subject": "0x1212121212121212121212121212121212121212121212121212121212121212", + "commitment": "0x8fabbad2f4aae2b67e5dcacc7cd425b7860d4d4453488c6c9770196b5009d795", + "parentPulseId": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab001", + "sequence": "6", + "occurredAt": "2026-05-13T05:00:20.000Z", + "uri": "fixture://swap-memory-valid", + "summary": "swap memory signal pulse 6", + "contractEvent": { + "schema": "flowmemory.flowpulse_contract_event.v0", + "interfaceName": "IFlowPulse", + "eventName": "FlowPulse", + "eventSignatureText": "FlowPulse(bytes32,bytes32,address,uint8,bytes32,bytes32,bytes32,uint64,uint64,string)", + "eventTopic0": "0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43", + "expectedTopic0": "0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43", + "topicMatchesContract": true, + "sourceContract": "0x2222222222222222222222222222222222222222", + "pulseTypeId": "4", + "pulseTypeName": "SWAP_MEMORY_SIGNAL", + "indexed": { + "pulseId": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab008", + "rootfieldId": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "actor": "0x4444444444444444444444444444444444444444" + }, + "payload": { + "subject": "0x1212121212121212121212121212121212121212121212121212121212121212", + "commitment": "0x8fabbad2f4aae2b67e5dcacc7cd425b7860d4d4453488c6c9770196b5009d795", + "parentPulseId": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab001", + "sequence": "6", + "occurredAt": "2026-05-13T05:00:20.000Z", + "uri": "fixture://swap-memory-valid" + }, + "receiptLocator": { + "chainId": "8453", + "blockNumber": "123457", + "blockHash": "0x2222222222222222222222222222222222222222222222222222222222222230", + "txHash": "0x3333333333333333333333333333333333333333333333333333333333333341", + "transactionIndex": "15", + "logIndex": "10", + "receiptStatus": "success" + }, + "receiptDerivedFields": [ + "blockHash", + "txHash", + "transactionIndex", + "logIndex", + "receiptStatus" + ] + }, + "id": "0xa0b1c648fb150b661e30a45dbccec410b1c3d510d94a7ac907e59141dc1cb36c", + "lastUpdated": "2026-05-13T17:02:00.000Z", + "provenance": { + "subsystem": "indexer", + "origin": "fixture", + "chainContext": "flowmemory-local-v0", + "fixturePath": "fixtures/launch-core/flowmemory-launch-v0.json", + "capturedAt": "2026-05-13T17:02:00.000Z", + "localPathHint": "fixtures/launch-core/flowmemory-launch-v0.json" + } + }, { "schema": "flowmemory.memory_signal.v0", "signalId": "0x4699ec901e15d260417fd6a9a86357c179ac55b631df05f28b29f28d5a5f3b61", @@ -1488,6 +1680,38 @@ "localPathHint": "fixtures/launch-core/flowmemory-launch-v0.json" } }, + { + "schema": "flowmemory.memory_receipt.v0", + "receiptId": "0x1706cc0af00148a0633b33e73856690ceea895f6c44bfe02c4d4a9819429dbce", + "reportId": "0xe0d47250f60e6d4764173e6ec13b29b06b9279abf461a5c7be4caf45374d8b40", + "reportDigest": "0xe0d47250f60e6d4764173e6ec13b29b06b9279abf461a5c7be4caf45374d8b40", + "observationId": "0x62e6ea1899a4dfd0d0a6cf6010ffb98983db20a9755257c8288602d059aa169e", + "rootfieldId": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "verifierStatus": "valid", + "flowMemoryStatus": "verified", + "resolverPolicyId": "flowmemory.resolver.policy.v0.fixture", + "verifierSpecVersion": "0", + "checksPassed": 3, + "checksTotal": 3, + "reasonCodes": [], + "evidenceRefs": [ + { + "kind": "swap-memory-signal", + "uri": "fixture://swap-memory-valid" + } + ], + "id": "0x1706cc0af00148a0633b33e73856690ceea895f6c44bfe02c4d4a9819429dbce", + "status": "verified", + "lastUpdated": "2026-05-13T17:02:00.000Z", + "provenance": { + "subsystem": "verifier", + "origin": "fixture", + "chainContext": "flowmemory-local-v0", + "fixturePath": "fixtures/launch-core/flowmemory-launch-v0.json", + "capturedAt": "2026-05-13T17:02:00.000Z", + "localPathHint": "fixtures/launch-core/flowmemory-launch-v0.json" + } + }, { "schema": "flowmemory.memory_receipt.v0", "receiptId": "0x48066ab6febdedb523cfcae2f313752db7e80467641d80914ebfe2e079d8c49d", @@ -1645,7 +1869,7 @@ "rootfieldBundles": [ { "schema": "flowmemory.rootfield_bundle.v0", - "bundleId": "0x31cf738173ed06b94ab86b99f2337c348040e50086a2d2f1f83ff9b0960fdbe6", + "bundleId": "0xc296618c8c57cbd6c4cc67cbf38e2ed31357177b565b4660b76e041c7189d5e1", "rootfieldId": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", "latestRoot": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1", "latestTransitionId": "0xdc9a57620b3309aceee35e858f62650b565e5f17ec6d9218ffd653ef5a633cdf", @@ -1653,6 +1877,7 @@ "transitionIds": [ "0x29fcee50b38bf403556ea08db7faeda6ea50430c08fc3126cebffad4a7657522", "0x5cdf7f5498350650adc19c62828425723a09bcca6571774992ca2d013fdc1068", + "0x2d4473b0c1eff862ed3d0db7caed9b2c4aff1f1c8668ace6683b9b33573d9349", "0xe1b6f858279961c968cdfba0b85d489767c486289e824cab3d017997064fed89", "0x35db5f8c36e2bc988c4d60f25a2e60d971256bdfdb79a73c92c0b82a41e5a367", "0x31d44377aa98e7fcaa07110256e1600249c4337c69e6d06c14d36e6aad6a79c4", @@ -1662,6 +1887,7 @@ "0xec2a80963190d2fa4d0eae023e735eeaf770cf33496eabdea696f3c7dda071eb", "0xec2a80963190d2fa4d0eae023e735eeaf770cf33496eabdea696f3c7dda071eb", "0xc595baea5e90a5f06ff49d8afeea0996eb39e13b3794903b8142dc9daf3a2df0", + "0xa0b1c648fb150b661e30a45dbccec410b1c3d510d94a7ac907e59141dc1cb36c", "0x4699ec901e15d260417fd6a9a86357c179ac55b631df05f28b29f28d5a5f3b61", "0xe32d6eeeff9c6bafadca56b980d58159afe8ffb29184458fa589c890829b9627", "0xfdc7756ac00683bc3e85bb3a9c2bd01f9719667b2d739818591fe697ee45d6c4", @@ -1670,6 +1896,7 @@ "memoryReceiptIds": [ "0x48066ab6febdedb523cfcae2f313752db7e80467641d80914ebfe2e079d8c49d", "0x7973443bfec1d763a16014d2299ff3e1a3d7dbda5d8a8c1c79638438fdf0f6fa", + "0x1706cc0af00148a0633b33e73856690ceea895f6c44bfe02c4d4a9819429dbce", "0x48066ab6febdedb523cfcae2f313752db7e80467641d80914ebfe2e079d8c49d", "0xdb38d4fa47cc00c6390ba08d3d6b7722291304d025a80981885294dd826843f8", "0x6a155b51ab1f84d1dbb20e47201db33a5a9a4996a2372987ce7559503cf97898", @@ -1679,6 +1906,7 @@ "verifierReportIds": [ "0x03af9720a87f7d72ce2ce95c1d04d50134649da51ace802d88818ad67fb2ebb3", "0xf1dfced6038cfa79928e1888e611da6bb05e7a14393cb20e2d5a5cb90c825c35", + "0xe0d47250f60e6d4764173e6ec13b29b06b9279abf461a5c7be4caf45374d8b40", "0x03af9720a87f7d72ce2ce95c1d04d50134649da51ace802d88818ad67fb2ebb3", "0xb7cef189a48b8c2697603c3704a52bb3a0322b375fdbea68d2bbbadb65a92ec1", "0xabc22e2203a9404c832aa792f7a569e17e599078fc9ebacf2c1307bbf6f1d7bb", @@ -1686,16 +1914,16 @@ "0x61b984c1055654334a7a7a191887779c8f2be937b08590e8a90927ee645042f5" ], "counts": { - "observations": 7, - "transitions": 6, - "receipts": 7, - "verified": 2, + "observations": 8, + "transitions": 7, + "receipts": 8, + "verified": 3, "failed": 1, "unresolved": 1, "unsupported": 1, "reorged": 1 }, - "id": "0x31cf738173ed06b94ab86b99f2337c348040e50086a2d2f1f83ff9b0960fdbe6", + "id": "0xc296618c8c57cbd6c4cc67cbf38e2ed31357177b565b4660b76e041c7189d5e1", "lastUpdated": "2026-05-13T17:02:00.000Z", "provenance": { "subsystem": "indexer", @@ -1719,6 +1947,7 @@ "0xec2a80963190d2fa4d0eae023e735eeaf770cf33496eabdea696f3c7dda071eb", "0xec2a80963190d2fa4d0eae023e735eeaf770cf33496eabdea696f3c7dda071eb", "0xc595baea5e90a5f06ff49d8afeea0996eb39e13b3794903b8142dc9daf3a2df0", + "0xa0b1c648fb150b661e30a45dbccec410b1c3d510d94a7ac907e59141dc1cb36c", "0x4699ec901e15d260417fd6a9a86357c179ac55b631df05f28b29f28d5a5f3b61", "0xe32d6eeeff9c6bafadca56b980d58159afe8ffb29184458fa589c890829b9627", "0xfdc7756ac00683bc3e85bb3a9c2bd01f9719667b2d739818591fe697ee45d6c4", @@ -1727,6 +1956,7 @@ "receiptIds": [ "0x48066ab6febdedb523cfcae2f313752db7e80467641d80914ebfe2e079d8c49d", "0x7973443bfec1d763a16014d2299ff3e1a3d7dbda5d8a8c1c79638438fdf0f6fa", + "0x1706cc0af00148a0633b33e73856690ceea895f6c44bfe02c4d4a9819429dbce", "0x48066ab6febdedb523cfcae2f313752db7e80467641d80914ebfe2e079d8c49d", "0xdb38d4fa47cc00c6390ba08d3d6b7722291304d025a80981885294dd826843f8", "0x6a155b51ab1f84d1dbb20e47201db33a5a9a4996a2372987ce7559503cf97898", @@ -1736,6 +1966,7 @@ "transitionIds": [ "0x29fcee50b38bf403556ea08db7faeda6ea50430c08fc3126cebffad4a7657522", "0x5cdf7f5498350650adc19c62828425723a09bcca6571774992ca2d013fdc1068", + "0x2d4473b0c1eff862ed3d0db7caed9b2c4aff1f1c8668ace6683b9b33573d9349", "0xe1b6f858279961c968cdfba0b85d489767c486289e824cab3d017997064fed89", "0x35db5f8c36e2bc988c4d60f25a2e60d971256bdfdb79a73c92c0b82a41e5a367", "0x31d44377aa98e7fcaa07110256e1600249c4337c69e6d06c14d36e6aad6a79c4", @@ -1769,8 +2000,8 @@ "stateRoot": "0x76ec5260c34184b6bb54ca406a43fc1f9591a47f37f71583a7620ef4a4065aff", "receiptsRoot": "0x6962bd6dbf28c2361c1337c1d33d678a815cc4b961e0e50db5ccb401cc0fe076", "timestamp": "2026-05-13T16:00:00.000Z", - "observationCount": 7, - "reportCount": 7, + "observationCount": 8, + "reportCount": 8, "finalityDistance": 123460, "status": "stale", "lastUpdated": "2026-05-13T17:02:00.000Z", @@ -1791,8 +2022,8 @@ "stateRoot": "0x3e1f5fddd84f9d460ee30a380ff700b17611891b8c03eb320edf1baefe003ef9", "receiptsRoot": "0xa0407b9a8a55106d549e0f19b92fceaa7f7a25697e94ebf8a1fa74af7b9168f4", "timestamp": "2026-05-13T16:00:01.000Z", - "observationCount": 7, - "reportCount": 7, + "observationCount": 8, + "reportCount": 8, "finalityDistance": 123459, "status": "finalized", "lastUpdated": "2026-05-13T17:02:00.000Z", diff --git a/apps/dashboard/src/data/types.ts b/apps/dashboard/src/data/types.ts index 8adc4809..37686c6c 100644 --- a/apps/dashboard/src/data/types.ts +++ b/apps/dashboard/src/data/types.ts @@ -34,6 +34,7 @@ export type FlowPulseContractTypeName = | "ROOTFIELD_REGISTERED" | "ROOT_COMMITTED" | "ROOTFIELD_STATUS_CHANGED" + | "SWAP_MEMORY_SIGNAL" | "UNKNOWN_FLOWPULSE_TYPE"; export interface FlowPulseContractEvent { diff --git a/apps/dashboard/src/test/dashboardData.test.ts b/apps/dashboard/src/test/dashboardData.test.ts index a635ffc9..889eb94a 100644 --- a/apps/dashboard/src/test/dashboardData.test.ts +++ b/apps/dashboard/src/test/dashboardData.test.ts @@ -15,6 +15,7 @@ describe("dashboard fixture", () => { expect(data.verifierReports.length).toBeGreaterThan(0); expect(data.memorySignals.every((signal) => signal.contractEvent.eventName === "FlowPulse")).toBe(true); expect(data.memorySignals.every((signal) => signal.contractEvent.topicMatchesContract)).toBe(true); + expect(data.memorySignals.some((signal) => signal.signalType === "swap_memory_signal")).toBe(true); expect(data.rootflowTransitions.every((transition) => transition.contractEventRef.signalId === transition.memorySignalId)).toBe(true); }); diff --git a/apps/dashboard/src/views/FlowMemoryView.tsx b/apps/dashboard/src/views/FlowMemoryView.tsx index 2464b686..7526cb85 100644 --- a/apps/dashboard/src/views/FlowMemoryView.tsx +++ b/apps/dashboard/src/views/FlowMemoryView.tsx @@ -17,6 +17,7 @@ export function FlowMemoryView({ data }: { data: DashboardData }) { const latestView = data.agentMemoryViews[0]; const latestBundle = data.rootfieldBundles[0]; const contractEventCount = data.memorySignals.filter((signal) => signal.contractEvent.topicMatchesContract).length; + const swapMemorySignalCount = data.memorySignals.filter((signal) => signal.signalType === "swap_memory_signal").length; const statusCounts = useMemo( () => data.rootflowTransitions.reduce>((counts, transition) => { counts[transition.status] = (counts[transition.status] ?? 0) + 1; @@ -112,6 +113,14 @@ export function FlowMemoryView({ data }: { data: DashboardData }) { topic0 matched +
+ Swap memory signals + {swapMemorySignalCount} +
+ 0 ? "verified" : "pending"} compact /> + Uniswap adapter path +
+
diff --git a/contracts/DEPLOYMENT_BOUNDARY.md b/contracts/DEPLOYMENT_BOUNDARY.md index 63d17d70..abbbc091 100644 --- a/contracts/DEPLOYMENT_BOUNDARY.md +++ b/contracts/DEPLOYMENT_BOUNDARY.md @@ -6,7 +6,7 @@ Status: V0 local and Base Sepolia readiness boundary. - Local Foundry tests. - Local fixture generation and indexer/verifier/dashboard flows. -- Base Sepolia deployment preparation for the current V0 contracts. +- Base Sepolia deployment dry runs and explicit broadcasts for the current V0 contracts. - Base Sepolia reads from explicit RPC URLs. - Public docs that describe emitted events, roots, receipts, and off-chain verification paths. @@ -35,6 +35,19 @@ Before a Base Sepolia deployment transaction is sent, the PR or issue must recor Private keys must not be committed to the repo, copied into docs, or stored in generated artifacts. +## Current Commands + +```powershell +npm run deploy:base-sepolia +npm run deploy:base-sepolia:broadcast +npm run read:base-sepolia -- --rpc-url --address --from-block --to-block +``` + +`deploy:base-sepolia` requires `BASE_SEPOLIA_RPC_URL` and +`BASE_SEPOLIA_DEPLOYER_KEY_HEX` from the local shell or an untracked `.env` +loader. The example file is `.env.example`; real key material must stay +outside Git. + ## Current Contract Set - `RootfieldRegistry`: Rootfield namespaces and root commitment pulses. diff --git a/contracts/FLOWPULSE_SCHEMA.md b/contracts/FLOWPULSE_SCHEMA.md index 0c576121..893b4204 100644 --- a/contracts/FLOWPULSE_SCHEMA.md +++ b/contracts/FLOWPULSE_SCHEMA.md @@ -24,8 +24,8 @@ event FlowPulse( - `pulseId`: Domain-separated identifier created by the emitting contract. It is not a replacement for receipt metadata. - `rootfieldId`: Namespace for the committed state stream. - `actor`: Account that caused the pulse. -- `pulseType`: Stable numeric type. Initial reserved values are `1` for rootfield registration, `2` for root commitment, and `3` for rootfield lifecycle/status changes such as deactivation or ownership transfer. -- `subject`: Type-specific subject. For registration and rootfield lifecycle changes this is the rootfield id. For root commitment this is the committed root. +- `pulseType`: Stable numeric type. Current reserved values are `1` for rootfield registration, `2` for root commitment, `3` for rootfield lifecycle/status changes such as deactivation or ownership transfer, and `4` for swap-derived memory signals emitted by the V0 hook adapter. +- `subject`: Type-specific subject. For registration and rootfield lifecycle changes this is the rootfield id. For root commitment this is the committed root. For swap-derived memory signals this is the pool id. - `commitment`: Type-specific hash commitment to off-chain data or metadata. Heavy AI, model, memory, artifact, and media data stays off-chain. - `parentPulseId`: Optional prior pulse reference for chains of work or verification. - `sequence`: Monotonic sequence within the rootfield namespace. @@ -44,3 +44,12 @@ FlowPulse does not include `txHash` or `logIndex`. Those values are not availabl - `uri` values are advisory by convention only. The skeleton does not enforce that they are short, resolvable, or off-chain pointers, so callers and reviewers must treat the off-chain-data boundary as a design convention rather than an enforcement guarantee. - Verifiers must validate any referenced off-chain content against the emitted `commitment`. - Pulse type expansion should happen by reserving new numeric values and documenting their subject and commitment semantics before contracts depend on them. + +## Current Pulse Types + +| Type | Name | Subject | Commitment | +| --- | --- | --- | --- | +| `1` | `ROOTFIELD_REGISTERED` | `rootfieldId` | `keccak256(abi.encode(schemaHash, metadataHash))` | +| `2` | `ROOT_COMMITTED` | committed root | `keccak256(abi.encode(root, artifactCommitment))` | +| `3` | `ROOTFIELD_STATUS_CHANGED` | `rootfieldId` | type-specific status or ownership commitment | +| `4` | `SWAP_MEMORY_SIGNAL` | Uniswap pool id | swap-memory artifact commitment checked by the V0 verifier fixture policy | diff --git a/contracts/FlowMemoryHookAdapter.sol b/contracts/FlowMemoryHookAdapter.sol index 8da77278..bed92948 100644 --- a/contracts/FlowMemoryHookAdapter.sol +++ b/contracts/FlowMemoryHookAdapter.sol @@ -2,19 +2,23 @@ pragma solidity ^0.8.24; import {IFlowMemoryHookAdapter} from "./interfaces/IFlowMemoryHookAdapter.sol"; +import {IFlowPulse, FlowPulseTypes} from "./FlowPulse.sol"; /// @title FlowMemoryHookAdapter /// @notice Dependency-light scaffold for future Uniswap v4 hook integration. /// @dev This is not a production hook. It performs no custom accounting, no /// dynamic fees, no token custody, and no external protocol calls. It cannot /// know txHash or logIndex during execution; indexers derive receipt metadata. -contract FlowMemoryHookAdapter is IFlowMemoryHookAdapter { +contract FlowMemoryHookAdapter is IFlowMemoryHookAdapter, IFlowPulse { bytes4 public constant AFTER_SWAP_SELECTOR = bytes4(keccak256("afterSwap(address,bytes32,bytes32,bytes32,bytes)")); + mapping(bytes32 rootfieldId => uint64 sequence) private _rootfieldSequences; + error ZeroSender(); error ZeroPoolId(); error ZeroRootfieldId(); error ZeroCommitment(); + error TimestampOverflow(uint256 timestamp); function afterSwap(address sender, bytes32 poolId, bytes32 rootfieldId, bytes32 commitment, bytes calldata hookData) external @@ -25,7 +29,47 @@ contract FlowMemoryHookAdapter is IFlowMemoryHookAdapter { if (rootfieldId == bytes32(0)) revert ZeroRootfieldId(); if (commitment == bytes32(0)) revert ZeroCommitment(); - emit AfterSwapObserved(msg.sender, sender, poolId, rootfieldId, commitment, keccak256(hookData)); + bytes32 hookDataHash = keccak256(hookData); + uint64 sequence = _nextSequence(rootfieldId); + uint64 occurredAt = _blockTimestamp(); + bytes32 pulseId = keccak256( + abi.encode( + FlowPulseTypes.SCHEMA_ID, + block.chainid, + address(this), + msg.sender, + sender, + poolId, + rootfieldId, + commitment, + hookDataHash, + sequence + ) + ); + + emit AfterSwapObserved(msg.sender, sender, poolId, rootfieldId, commitment, hookDataHash); + emit FlowPulse( + pulseId, + rootfieldId, + sender, + FlowPulseTypes.SWAP_MEMORY_SIGNAL, + poolId, + commitment, + bytes32(0), + sequence, + occurredAt, + "flowmemory://uniswap-v4/after-swap" + ); return AFTER_SWAP_SELECTOR; } + + function _nextSequence(bytes32 rootfieldId) private returns (uint64 sequence) { + sequence = _rootfieldSequences[rootfieldId] + 1; + _rootfieldSequences[rootfieldId] = sequence; + } + + function _blockTimestamp() private view returns (uint64) { + if (block.timestamp > type(uint64).max) revert TimestampOverflow(block.timestamp); + return uint64(block.timestamp); + } } diff --git a/contracts/FlowPulse.sol b/contracts/FlowPulse.sol index 18d4b998..8f422064 100644 --- a/contracts/FlowPulse.sol +++ b/contracts/FlowPulse.sol @@ -38,4 +38,5 @@ library FlowPulseTypes { uint8 internal constant ROOTFIELD_REGISTERED = 1; uint8 internal constant ROOT_COMMITTED = 2; uint8 internal constant ROOTFIELD_STATUS_CHANGED = 3; + uint8 internal constant SWAP_MEMORY_SIGNAL = 4; } diff --git a/contracts/STATIC_ANALYSIS.md b/contracts/STATIC_ANALYSIS.md index 5fb428b5..f55253f0 100644 --- a/contracts/STATIC_ANALYSIS.md +++ b/contracts/STATIC_ANALYSIS.md @@ -5,10 +5,10 @@ Status: pre-production hardening setup. This repository now has one standard command for contract hardening checks: ```powershell -.\infra\scripts\contracts-static-analysis.ps1 +npm run contracts:hardening ``` -On bash-compatible shells: +The underlying platform scripts remain available: ```bash bash infra/scripts/contracts-static-analysis.sh @@ -33,7 +33,7 @@ CHECK_FORGE_FMT=1 bash infra/scripts/contracts-static-analysis.sh Audit environments should require Slither explicitly: ```powershell -.\infra\scripts\contracts-static-analysis.ps1 -RequireSlither +npm run contracts:hardening:slither ``` ```bash @@ -44,6 +44,9 @@ REQUIRE_SLITHER=1 bash infra/scripts/contracts-static-analysis.sh `.slither.config.json` excludes the `timestamp` detector for V0 because the current contracts use `block.timestamp` only for advisory `registeredAt`, `updatedAt`, `submittedAt`, `scheduledAt`, and FlowPulse `occurredAt` fields plus `uint64` overflow guards. Those timestamps do not drive randomness, rewards, custody, slashing, dynamic fees, or protocol-critical authorization in the current V0 boundary. +Latest local required-Slither pass on 2026-05-13 analyzed 22 contracts with +100 detectors and found 0 results. + ## Current Boundary The contracts are V0 launch foundations for FlowPulse, Rootfield, receipts, workers, verifiers, cursors, and hook-adapter events. They are not a production L1, production verifier network, token system, custody system, fee system, or production Uniswap v4 hook deployment. diff --git a/docs/CURRENT_STATE.md b/docs/CURRENT_STATE.md index 2462b188..d04e206b 100644 --- a/docs/CURRENT_STATE.md +++ b/docs/CURRENT_STATE.md @@ -6,9 +6,9 @@ This file is the beginner-friendly source of truth for what exists in FlowMemory ## Repo Phase -FlowMemory is in foundation hardening. +FlowMemory is in launch-candidate V0 hardening. -The bootstrap repository operating system, contracts V0 foundation, crypto V0 foundation, local indexer/verifier fixture package, dashboard V0, FlowRouter hardware POC, local no-value devnet prototype, launch-core contract-event spine, and pre-production hardening guardrails have merged into `main`. +The bootstrap repository operating system, contracts V0 foundation, crypto V0 foundation, local indexer/verifier fixture package, dashboard V0, FlowRouter hardware POC, local no-value devnet prototype, launch-core contract-event spine, and pre-production hardening guardrails have merged into `main`. The current launch-candidate branch adds swap-derived memory signals, stricter launch validation, and Base Sepolia testnet deploy/read commands. The launch-core V0 stack now has a single runnable local command that connects contract fixtures, local indexing/verifier outputs, crypto schema vocabulary, Rootflow transitions, Flow Memory objects, generated dashboard state, local no-value devnet output, and hardware POC output without production deployment. @@ -31,10 +31,10 @@ Contracts foundation: - `contracts/FlowPulse.sol` defines the FlowPulse v0 event interface and initial pulse type constants. - `contracts/RootfieldRegistry.sol` registers Rootfield namespaces, accepts committed roots, and emits FlowPulse events. -- `contracts/FlowMemoryHookAdapter.sol` is a compileable V0 hook-adapter scaffold. It is not a production Uniswap v4 hook. +- `contracts/FlowMemoryHookAdapter.sol` is a compileable V0 hook-adapter scaffold. It emits `SWAP_MEMORY_SIGNAL` FlowPulse events for the launch fixture path. It is not a production Uniswap v4 hook. - `contracts/ArtifactRegistry.sol`, `CursorRegistry.sol`, `ReceiptVerifier.sol`, `WorkerRegistry.sol`, `VerifierRegistry.sol`, `WorkReceiptRegistry.sol`, `VerifierReportRegistry.sol`, and `WorkDebtScheduler.sol` provide local/test skeleton surfaces for commitments, cursors, work receipts, verifier reports, and work state. - `contracts/FLOWPULSE_SCHEMA.md` documents event fields, receipt boundaries, and URI/log-data limitations. -- `tests/RootfieldRegistry.t.sol` and `tests/LiveV0Package.t.sol` contain 35 passing Foundry tests. +- `tests/RootfieldRegistry.t.sol` and `tests/LiveV0Package.t.sol` contain 36 passing Foundry tests. - `tests/README.md` documents the current test command. - `contracts/STATIC_ANALYSIS.md`, `contracts/DEPLOYMENT_BOUNDARY.md`, and `contracts/ACCESS_CONTROL_REVIEW.md` define the current hardening, deployment, and access-control boundaries. - `infra/scripts/contracts-static-analysis.ps1` and `infra/scripts/contracts-static-analysis.sh` run the contract hardening baseline. Slither is optional by default and required only when explicitly requested. @@ -47,11 +47,13 @@ Crypto foundation: Indexer/verifier local package: - `services/shared/`, `services/indexer/`, and `services/verifier/` contain fixture-first local packages. -- The local services test suite currently has 30 passing tests. -- `npm run e2e` currently indexes 7 observations, writes 6 cursors, rejects 2 logs, tracks 1 duplicate, and produces 7 verifier reports. +- The local services test suite currently has 31 passing tests. +- `npm run e2e` currently indexes 8 observations, writes 7 cursors, rejects 2 logs, tracks 1 duplicate, and produces 8 verifier reports. - The verifier uses local fixture evidence only. It is not a production verifier network. +- The verifier supports local fixture checks for rootfield registration, root commitments, and swap-derived memory-signal commitments. - `npm run index:base-sepolia -- --rpc-url --address --from-block --to-block ` provides a constrained Base Sepolia reader path. - The Base Sepolia reader requires an explicit RPC URL, rejects non-Base-Sepolia chain ids, and persists both canonical state and a durable checkpoint without storing RPC URLs or keys. +- `npm run deploy:base-sepolia` and `npm run deploy:base-sepolia:broadcast` provide Foundry deploy commands for the current V0 Base Sepolia testnet contract set. They require local env values and do not commit credentials. Dashboard V0: @@ -63,6 +65,9 @@ Dashboard V0: Launch-core integration: - `npm run launch:v0` runs the local end-to-end V0 flow. +- `npm run launch:candidate` runs contract hardening, launch generation, runtime schema validation, fixture drift checks, and launch claim guardrails. +- `npm run validate:launch` validates generated MemorySignal, MemoryReceipt, RootflowTransition, RootfieldBundle, and AgentMemoryView objects against canonical JSON schemas. +- `npm run fixtures:check` confirms committed launch and dashboard fixtures match generated output. - `fixtures/launch-core/flowmemory-launch-v0.json` contains generated MemorySignal, MemoryReceipt, RootfieldBundle, AgentMemoryView, and RootflowTransition objects. - `fixtures/launch-core/rootflow-transitions.json` contains concrete generated RootflowTransition output. - `schemas/flowmemory/` contains canonical JSON schemas for MemorySignal, MemoryReceipt, RootflowTransition, RootfieldBundle, and AgentMemoryView. @@ -104,7 +109,6 @@ Launch-core specifications: - Production Rootflow runtime implementation. - Production Flow Memory runtime implementation. - Hosted launch-core services. -- Rich JSON Schema runtime validation with a dedicated validator dependency. - Production indexer or verifier service runtime. - Production persistence layer, production live RPC reader, production APIs, or hosted services. - Base mainnet reader. @@ -167,9 +171,9 @@ Before assigning agents, check for dirty worktrees and avoid overlapping folders ## Current Operator Priorities 1. Keep the generated launch-core command stable in CI. -2. Add runtime schema validation and fixture diff guardrails before live services. -3. Exercise the Base Sepolia reader path on explicit testnet contract addresses only. -4. Continue contracts hardening without production deployment or token mechanics. +2. Exercise the Base Sepolia deploy/read path on explicit testnet contract addresses only. +3. Continue contracts hardening without production mainnet deployment or token mechanics. +4. Keep swap-memory signal semantics narrow until a real Uniswap v4 hook issue scopes the next step. 5. Keep dashboard work fixture-backed until a production API is explicitly scoped. ## Update Rule diff --git a/docs/DECISIONS/2026-05-13-launch-candidate-v0.md b/docs/DECISIONS/2026-05-13-launch-candidate-v0.md new file mode 100644 index 00000000..985946cc --- /dev/null +++ b/docs/DECISIONS/2026-05-13-launch-candidate-v0.md @@ -0,0 +1,58 @@ +# Launch Candidate V0 Boundary + +Date: 2026-05-13 + +## Decision + +FlowMemory V0 now treats the local launch candidate as a complete repeatable +developer loop, not just a set of isolated fixtures. + +The loop is: + +```text +FlowPulse or swap-derived hook-adapter event +-> indexer observation +-> verifier report +-> MemorySignal and MemoryReceipt +-> RootflowTransition +-> RootfieldBundle +-> AgentMemoryView +-> dashboard fixture +``` + +## What Changed + +- `FlowMemoryHookAdapter.afterSwap` emits `SWAP_MEMORY_SIGNAL` FlowPulse events + for the V0 swap-memory path. +- The verifier can validate swap-memory artifacts using `poolId`, + `hookDataHash`, and `memoryRoot` commitments. +- The launch command now generates 8 observations, 8 verifier reports, 8 memory + signals, 8 memory receipts, 7 Rootflow transitions, 1 Rootfield bundle, and 1 + AgentMemoryView. +- `npm run validate:launch` validates generated launch-core objects against + canonical JSON schemas. +- `npm run fixtures:check` catches stale generated launch/dashboard fixtures. +- `npm run launch:candidate` runs the local hardening, launch, validation, + drift, and claim-guardrail path. +- Base Sepolia deploy/read commands exist for the current V0 testnet contract + set without committing credentials. + +## Boundaries + +This decision does not approve Base mainnet deployment, a production L1, +tokenomics, production verifier economics, custody, dynamic fees, or a production +Uniswap v4 hook. + +Contracts still cannot know `txHash` or `logIndex` during execution. The +indexer derives those fields after receipts and logs exist. + +Heavy AI/model/memory/artifact data remains off-chain. On-chain and fixture +surfaces carry roots, commitments, receipts, and status. + +## Required Before Public Testnet Use + +- Run `npm run contracts:hardening:slither`. +- Record deployer address, chain id `84532`, contract addresses, block range, + source verification status, and post-deploy read evidence in the PR or issue. +- Run the Base Sepolia reader over the deployment block range and attach the + generated checkpoint. diff --git a/docs/FLOW_MEMORY_V0.md b/docs/FLOW_MEMORY_V0.md index 00782a40..2493d608 100644 --- a/docs/FLOW_MEMORY_V0.md +++ b/docs/FLOW_MEMORY_V0.md @@ -19,7 +19,7 @@ Flow Memory V0 answers: ## Launch Flow ```text -Uniswap v4 or fixture activity +Uniswap v4 adapter or fixture activity -> FlowPulse -> indexed observation -> MemorySignal @@ -43,7 +43,7 @@ Minimum V0 fields: "signalId": "bytes32-or-hex-string", "pulseId": "bytes32-or-hex-string", "rootfieldId": "bytes32-or-hex-string", - "signalType": "root_commitment|swap_activity|failure|repair|evaluation|hardware_heartbeat|unsupported", + "signalType": "rootfield_registration|root_commitment|swap_memory_signal|unsupported_pulse", "subject": "bytes32-or-hex-string", "commitment": "bytes32-or-hex-string", "contractEvent": { @@ -80,6 +80,12 @@ receipt-derived locator fields. `IFlowPulse` emits `pulseId`, `rootfieldId`, `occurredAt`, and `uri`; the indexer adds `txHash`, `logIndex`, block metadata, and receipt status after receipts/logs exist. +`swap_memory_signal` is the V0 name for a swap-derived signal. The current +contract path is `FlowMemoryHookAdapter.afterSwap`, which emits a FlowPulse with +`pulseType = 4`, `subject = poolId`, and a caller-supplied memory commitment. +The adapter cannot know `txHash` or `logIndex`; those fields are added by the +indexer after receipts/logs exist. + ### MemoryReceipt A `MemoryReceipt` links a signal to evidence, commitments, and verifier output. @@ -210,6 +216,12 @@ The current local/test generator is: npm run launch:v0 ``` +The stricter local launch-candidate gate is: + +```powershell +npm run launch:candidate +``` + It writes generated Flow Memory V0 objects to: ```text diff --git a/docs/PRODUCTION_READINESS_CHECKLIST.md b/docs/PRODUCTION_READINESS_CHECKLIST.md index efab8670..83cd7e57 100644 --- a/docs/PRODUCTION_READINESS_CHECKLIST.md +++ b/docs/PRODUCTION_READINESS_CHECKLIST.md @@ -10,6 +10,8 @@ Current launch target: - fixture-backed dashboard - Base Sepolia reader path for FlowPulse logs - contract hardening baseline +- runtime schema validation and fixture-drift guardrails +- Base Sepolia testnet deploy/read commands - clear claim boundaries Current launch target is not: @@ -30,6 +32,7 @@ Current launch target is not: - `forge build` passes. - Static-analysis baseline script passes. - Slither findings are captured before any public testnet deployment. +- `npm run contracts:hardening:slither` passes in audit environments before public testnet deployment. - Deployment boundary is documented in `contracts/DEPLOYMENT_BOUNDARY.md`. - Access-control boundary is documented in `contracts/ACCESS_CONTROL_REVIEW.md`. - Event tests cover every launch-critical state transition. @@ -48,6 +51,9 @@ Current launch target is not: ## Flow Memory And Rootflow Gates - `npm run launch:v0` passes. +- `npm run launch:candidate` passes. +- `npm run validate:launch` passes. +- `npm run fixtures:check` passes. - MemorySignal, MemoryReceipt, RootfieldBundle, AgentMemoryView, and RootflowTransition schemas are present. - Rootflow transitions preserve parent/child linkage. - Contract-event linkage remains explicit. @@ -58,6 +64,7 @@ Current launch target is not: - Dashboard tests pass. - Dashboard production build passes. +- `npm run build:production` passes before a public demo build is cut. - Dashboard uses generated fixtures until a production API is explicitly scoped. - Dashboard copy does not imply production deployment, production L1, full trustless verification, free storage, or AI running on-chain. diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index e3050099..25713844 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -12,6 +12,7 @@ The launch-core V0 stack means: - Contracts compile and tests run locally. - FlowPulse fixtures can be produced or consumed deterministically. +- The V0 hook adapter can emit a swap-derived `SWAP_MEMORY_SIGNAL` FlowPulse for the launch fixture path. - Rootflow transitions link FlowPulse observations, parent state, receipts, verifier reports, and new roots. - Flow Memory objects expose MemorySignal, MemoryReceipt, RootfieldBundle, and AgentMemoryView shapes. - MemorySignals and RootflowTransitions preserve explicit `IFlowPulse.FlowPulse` contract-event semantics while keeping receipt-only fields indexer-derived. @@ -48,7 +49,7 @@ Status: implemented as a local/test foundation; hardening still active. - Minimal Foundry config and contract tests exist. - `FlowPulse`, `RootfieldRegistry`, hook-adapter scaffold, artifact/cursor/worker/verifier/work registries, receipt verifier, work receipt registry, verifier report registry, and scheduler skeletons exist. -- `forge test` currently runs 35 passing tests. +- `forge test` currently runs 36 passing tests. - FlowPulse v0 and Rootfield URI/log-data decisions are documented. - Static-analysis runner, deployment boundary, and access-control review docs exist. - Define status lifecycle, ownership/recovery, and namespace policy before expanding deployment scope. @@ -66,6 +67,8 @@ Status: implemented as fixture-first services plus generated launch-core state; - Fixture-based parser and reorg-state tests exist in the indexer/verifier packages. - Deterministic persistence exists for fixture state and the constrained Base Sepolia reader checkpoint. - A Base Sepolia reader path exists for explicit RPC URLs and explicit FlowPulse contract addresses; it rejects non-Base-Sepolia chain ids. +- Base Sepolia deploy/read commands exist for the current V0 testnet contract set. +- Runtime schema validation and generated fixture drift checks exist for launch-core outputs. - Local devnet smoke-test gates exist as a no-value Rust prototype, without mainnet or production deployment. ### Phase 3: V0 Review/Audit @@ -75,7 +78,7 @@ Status: active. - Define foundation PR review rules. - Add security reporting guidance. - Enforce claim guardrails in CI for README/docs/marketing surfaces. -- Keep Slither required for audit environments, optional for ordinary local hardening runs until the toolchain is installed. +- Keep Slither required for audit environments and available through `npm run contracts:hardening:slither`. - Enforce allowed-folder and forbidden-folder boundaries. ## Mid-Term Phases @@ -138,8 +141,7 @@ The initial merge sequence has completed for repo OS, contracts foundation, cryp Next merge preference: -1. Runtime schema validation and fixture-diff guardrails. -2. Base Sepolia reader soak tests against explicit testnet deployments. -3. Dashboard polish and explorer/hardware-console separation. -4. Static analysis follow-up with Slither installed and findings triaged. +1. Base Sepolia reader soak tests against explicit testnet deployments. +2. Dashboard polish and explorer/hardware-console separation. +3. Static analysis follow-up findings triaged for any public testnet deployment. 5. Production-gated research only after V0 local acceptance stays green. diff --git a/docs/SECURITY_MODEL.md b/docs/SECURITY_MODEL.md index d1a0e955..7edaa4b2 100644 --- a/docs/SECURITY_MODEL.md +++ b/docs/SECURITY_MODEL.md @@ -145,9 +145,9 @@ Live V0 registries and schedulers are commitment surfaces, not complete trust sy Static analysis preparation: -- Slither was not available in the local PATH during the Live V0 package pass. -- Track setup in GitHub issue #24 before adding a CI gate. -- Candidate command once installed: `slither . --filter-paths "tests|script"`. +- The baseline hardening runner is `npm run contracts:hardening`. +- Audit environments should run `npm run contracts:hardening:slither`. +- Slither findings must be attached to the relevant PR or issue before any public testnet deployment. ## PR Security Checklist diff --git a/docs/V0_LAUNCH_ACCEPTANCE.md b/docs/V0_LAUNCH_ACCEPTANCE.md index 7fa2f54a..6bb82737 100644 --- a/docs/V0_LAUNCH_ACCEPTANCE.md +++ b/docs/V0_LAUNCH_ACCEPTANCE.md @@ -14,6 +14,7 @@ The local/testnet-ready V0 system must support: - Root commitments. - Parent/child memory-state transitions. - FlowPulse linkage. +- Uniswap swap-derived memory-signal linkage. - Receipt linkage. - Verifier statuses. - `pending`, `verified`, `failed`, and `reorged` states. @@ -35,6 +36,7 @@ It must not claim production L1, production mainnet readiness, full trustless ve | Root commitments | Contract or fixture commits a nonzero root and emits/records the update. | Contracts | Implemented for local/test V0 in contracts and generated fixtures. | | Parent/child transitions | `RootflowTransition` includes parent pulse/root and new root. | Indexer + Crypto + Contracts | Implemented in `fixtures/launch-core/rootflow-transitions.json`. | | FlowPulse linkage | Transition and memory signal reference `pulseId`. | Indexer + Dashboard | Implemented in generated MemorySignals and RootflowTransitions. | +| Swap-memory linkage | Hook-adapter or fixture emits `SWAP_MEMORY_SIGNAL` and Flow Memory exposes `swap_memory_signal`. | Contracts + Indexer + Verifier + Dashboard | Implemented in `FlowMemoryHookAdapter`, receipt fixtures, verifier fixtures, generated launch-core data, and dashboard tests. | | Contract event semantics | Generated MemorySignals preserve `IFlowPulse.FlowPulse` event signature, indexed fields, payload fields, pulse type, and receipt-derived locator fields. | Contracts + Indexer + Dashboard | Implemented with `contractEvent` and `contractEventRef` fields in launch-core fixtures and dashboard data. | | Receipt linkage | `MemoryReceipt` links signal, artifact commitment, evidence URI, and verifier report. | Crypto + Indexer | Implemented in `fixtures/launch-core/flowmemory-launch-v0.json`. | | Verifier statuses | Cross-agent status vocabulary exists and verifier reports use it. | Indexer + Crypto | Implemented with explicit adapter in `services/flowmemory/src/status.ts`. | @@ -43,6 +45,8 @@ It must not claim production L1, production mainnet readiness, full trustless ve | Failed state | Fixture/report can show rejected transition. | Indexer + Dashboard | Implemented via `invalid` -> `failed` adapter. | | Reorged state | Fixture/report can show removed/superseded observation. | Indexer | Implemented in generated transition/report/dashboard state. | | Deterministic fixtures | Fixtures run without private RPC keys or secrets. | Indexer + Crypto + Chain | Implemented; `npm run launch:v0` regenerates deterministic local fixtures. | +| Runtime schema validation | Launch-core JSON passes canonical schemas. | Flow Memory | Implemented with `npm run validate:launch`. | +| Fixture drift guard | Generated fixtures match committed dashboard/runtime artifacts. | Flow Memory + Dashboard | Implemented with `npm run fixtures:check`. | | Dashboard-readable state | JSON/API/fixture shape feeds dashboard views. | Dashboard + Indexer | Implemented in generated dashboard fixture. | | Flow Memory schemas | `MemorySignal`, `MemoryReceipt`, `RootflowTransition`, `RootfieldBundle`, and `AgentMemoryView` schemas exist. | Crypto + Indexer + Dashboard | Implemented under `schemas/flowmemory/`. | | Verifier reports | JSON schema and sample reports exist. | Indexer + Crypto | Implemented in verifier package and generated MemoryReceipts. | @@ -63,6 +67,7 @@ A developer must be able to run a local V0 flow: The final acceptance evidence should include: - command: `npm run launch:v0`; +- stricter gate: `npm run launch:candidate`; - fixture paths: `fixtures/launch-core/`, `fixtures/dashboard/flowmemory-dashboard-v0.json`; - output paths: `fixtures/launch-core/flowmemory-launch-v0.json`, `fixtures/launch-core/rootflow-transitions.json`, `apps/dashboard/public/data/flowmemory-dashboard-v0.json`; - test results: services, dashboard, contracts, crypto, devnet, and hardware checks; @@ -76,7 +81,7 @@ Contracts to indexer: - RootfieldRegistry ABI. - local deployment or fixture event output. - contract tests showing root registration and root commitment. -- pulse type semantics for `ROOTFIELD_REGISTERED`, `ROOT_COMMITTED`, and `ROOTFIELD_STATUS_CHANGED`. +- pulse type semantics for `ROOTFIELD_REGISTERED`, `ROOT_COMMITTED`, `ROOTFIELD_STATUS_CHANGED`, and `SWAP_MEMORY_SIGNAL`. Crypto to indexer: diff --git a/fixtures/dashboard/flowmemory-dashboard-v0.json b/fixtures/dashboard/flowmemory-dashboard-v0.json index 299c9566..23277afe 100644 --- a/fixtures/dashboard/flowmemory-dashboard-v0.json +++ b/fixtures/dashboard/flowmemory-dashboard-v0.json @@ -88,6 +88,38 @@ "localPathHint": "services/indexer/out/indexer-state.json" } }, + { + "id": "0x62e6ea1899a4dfd0d0a6cf6010ffb98983db20a9755257c8288602d059aa169e", + "observationId": "0x62e6ea1899a4dfd0d0a6cf6010ffb98983db20a9755257c8288602d059aa169e", + "pulseId": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab008", + "rootfieldId": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "eventSignature": "0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43", + "blockNumber": "123457", + "blockHash": "0x2222222222222222222222222222222222222222222222222222222222222230", + "txHash": "0x3333333333333333333333333333333333333333333333333333333333333341", + "transactionIndex": "15", + "logIndex": "10", + "receiptStatus": "success", + "actor": "0x4444444444444444444444444444444444444444", + "pulseType": "4", + "subject": "0x1212121212121212121212121212121212121212121212121212121212121212", + "commitment": "0x8fabbad2f4aae2b67e5dcacc7cd425b7860d4d4453488c6c9770196b5009d795", + "parentPulseId": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab001", + "sequence": "6", + "occurredAt": "2026-05-13T05:00:20.000Z", + "uri": "fixture://swap-memory-valid", + "summary": "swap memory signal from generated indexer output", + "status": "finalized", + "lastUpdated": "2026-05-13T17:02:00.000Z", + "provenance": { + "subsystem": "indexer", + "origin": "fixture", + "chainContext": "flowmemory-local-v0", + "fixturePath": "fixtures/launch-core/flowmemory-launch-v0.json", + "capturedAt": "2026-05-13T17:02:00.000Z", + "localPathHint": "services/indexer/out/indexer-state.json" + } + }, { "id": "0x9d958aadf8bf46f989b51e541709a73d21970e7e79643f939c9a0000b50f9a91", "observationId": "0x9d958aadf8bf46f989b51e541709a73d21970e7e79643f939c9a0000b50f9a91", @@ -251,14 +283,14 @@ ], "rootfields": [ { - "id": "0x31cf738173ed06b94ab86b99f2337c348040e50086a2d2f1f83ff9b0960fdbe6", + "id": "0xc296618c8c57cbd6c4cc67cbf38e2ed31357177b565b4660b76e041c7189d5e1", "rootfieldId": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", "owner": "0x4444444444444444444444444444444444444444", "schemaHash": "0x4c9929a2e216b475bd5bb68ac234d74d70ceadfbb86a948a62535413118371a8", "metadataHash": "0x32ae92d97373a790718b78a4bd0900e33c763de3c2cb43670f5d9b8a7ae7c8d1", "latestRoot": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1", "latestObservationId": "0x5ba8c5a70c814d35482df5f6598e549f83f2d0e27f6e3d25b83eb2a0e1d56a98", - "pulseCount": 7, + "pulseCount": 8, "workLaneIds": [ "MEMORY_REFRESH", "FAILURE_DISCOVERY", @@ -285,7 +317,7 @@ "name": "Memory refresh", "queueDepth": 1, "inflight": 0, - "completed24h": 3, + "completed24h": 4, "p95LatencyMs": 640, "operator": "fixture-worker", "status": "pending", @@ -367,6 +399,29 @@ "localPathHint": "services/verifier/out/reports.json" } }, + { + "id": "0x1706cc0af00148a0633b33e73856690ceea895f6c44bfe02c4d4a9819429dbce", + "receiptId": "0x1706cc0af00148a0633b33e73856690ceea895f6c44bfe02c4d4a9819429dbce", + "laneId": "MEMORY_REFRESH", + "rootfieldId": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "observationId": "0x62e6ea1899a4dfd0d0a6cf6010ffb98983db20a9755257c8288602d059aa169e", + "reportId": "0xe0d47250f60e6d4764173e6ec13b29b06b9279abf461a5c7be4caf45374d8b40", + "workType": "VERIFIER_REPORT_TO_MEMORY_RECEIPT", + "artifactUri": "fixture://swap-memory-valid", + "startedAt": "2026-05-13T17:02:00.000Z", + "completedAt": "2026-05-13T17:02:00.000Z", + "resultHash": "0xe0d47250f60e6d4764173e6ec13b29b06b9279abf461a5c7be4caf45374d8b40", + "status": "verified", + "lastUpdated": "2026-05-13T17:02:00.000Z", + "provenance": { + "subsystem": "verifier", + "origin": "fixture", + "chainContext": "flowmemory-local-v0", + "fixturePath": "fixtures/launch-core/flowmemory-launch-v0.json", + "capturedAt": "2026-05-13T17:02:00.000Z", + "localPathHint": "services/verifier/out/reports.json" + } + }, { "id": "0x48066ab6febdedb523cfcae2f313752db7e80467641d80914ebfe2e079d8c49d", "receiptId": "0x48066ab6febdedb523cfcae2f313752db7e80467641d80914ebfe2e079d8c49d", @@ -527,6 +582,28 @@ "localPathHint": "services/verifier/out/reports.json" } }, + { + "id": "0xe0d47250f60e6d4764173e6ec13b29b06b9279abf461a5c7be4caf45374d8b40", + "reportId": "0xe0d47250f60e6d4764173e6ec13b29b06b9279abf461a5c7be4caf45374d8b40", + "observationId": "0x62e6ea1899a4dfd0d0a6cf6010ffb98983db20a9755257c8288602d059aa169e", + "rootfieldId": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "resolverPolicyId": "flowmemory.resolver.policy.v0.fixture", + "verifierSpecVersion": "0", + "checksPassed": 3, + "checksTotal": 3, + "reasonCodes": [], + "reportHash": "0xe0d47250f60e6d4764173e6ec13b29b06b9279abf461a5c7be4caf45374d8b40", + "status": "verified", + "lastUpdated": "2026-05-13T17:02:00.000Z", + "provenance": { + "subsystem": "verifier", + "origin": "fixture", + "chainContext": "flowmemory-local-v0", + "fixturePath": "fixtures/launch-core/flowmemory-launch-v0.json", + "capturedAt": "2026-05-13T17:02:00.000Z", + "localPathHint": "services/verifier/out/reports.json" + } + }, { "id": "0x03af9720a87f7d72ce2ce95c1d04d50134649da51ace802d88818ad67fb2ebb3", "reportId": "0x03af9720a87f7d72ce2ce95c1d04d50134649da51ace802d88818ad67fb2ebb3", @@ -727,6 +804,46 @@ "localPathHint": "fixtures/launch-core/rootflow-transitions.json" } }, + { + "schema": "flowmemory.rootflow_transition.v0", + "transitionId": "0x2d4473b0c1eff862ed3d0db7caed9b2c4aff1f1c8668ace6683b9b33573d9349", + "rootfieldId": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "observationId": "0x62e6ea1899a4dfd0d0a6cf6010ffb98983db20a9755257c8288602d059aa169e", + "pulseId": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab008", + "parentPulseId": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab001", + "parentTransitionId": "0x5cdf7f5498350650adc19c62828425723a09bcca6571774992ca2d013fdc1068", + "memorySignalId": "0xa0b1c648fb150b661e30a45dbccec410b1c3d510d94a7ac907e59141dc1cb36c", + "memoryReceiptId": "0x1706cc0af00148a0633b33e73856690ceea895f6c44bfe02c4d4a9819429dbce", + "reportId": "0xe0d47250f60e6d4764173e6ec13b29b06b9279abf461a5c7be4caf45374d8b40", + "previousRoot": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1", + "attemptedRoot": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1", + "nextRoot": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1", + "status": "verified", + "blockNumber": "123457", + "txHash": "0x3333333333333333333333333333333333333333333333333333333333333341", + "sequence": "6", + "reasonCodes": [], + "contractEventRef": { + "signalId": "0xa0b1c648fb150b661e30a45dbccec410b1c3d510d94a7ac907e59141dc1cb36c", + "eventName": "FlowPulse", + "eventTopic0": "0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43", + "sourceContract": "0x2222222222222222222222222222222222222222", + "pulseTypeId": "4", + "pulseTypeName": "SWAP_MEMORY_SIGNAL", + "txHash": "0x3333333333333333333333333333333333333333333333333333333333333341", + "logIndex": "10" + }, + "id": "0x2d4473b0c1eff862ed3d0db7caed9b2c4aff1f1c8668ace6683b9b33573d9349", + "lastUpdated": "2026-05-13T17:02:00.000Z", + "provenance": { + "subsystem": "indexer", + "origin": "fixture", + "chainContext": "flowmemory-local-v0", + "fixturePath": "fixtures/launch-core/flowmemory-launch-v0.json", + "capturedAt": "2026-05-13T17:02:00.000Z", + "localPathHint": "fixtures/launch-core/rootflow-transitions.json" + } + }, { "schema": "flowmemory.rootflow_transition.v0", "transitionId": "0xe1b6f858279961c968cdfba0b85d489767c486289e824cab3d017997064fed89", @@ -1122,6 +1239,81 @@ "localPathHint": "fixtures/launch-core/flowmemory-launch-v0.json" } }, + { + "schema": "flowmemory.memory_signal.v0", + "signalId": "0xa0b1c648fb150b661e30a45dbccec410b1c3d510d94a7ac907e59141dc1cb36c", + "observationId": "0x62e6ea1899a4dfd0d0a6cf6010ffb98983db20a9755257c8288602d059aa169e", + "pulseId": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab008", + "rootfieldId": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "signalType": "swap_memory_signal", + "status": "verified", + "chainId": "8453", + "emittingContract": "0x2222222222222222222222222222222222222222", + "blockNumber": "123457", + "blockHash": "0x2222222222222222222222222222222222222222222222222222222222222230", + "txHash": "0x3333333333333333333333333333333333333333333333333333333333333341", + "transactionIndex": "15", + "logIndex": "10", + "actor": "0x4444444444444444444444444444444444444444", + "subject": "0x1212121212121212121212121212121212121212121212121212121212121212", + "commitment": "0x8fabbad2f4aae2b67e5dcacc7cd425b7860d4d4453488c6c9770196b5009d795", + "parentPulseId": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab001", + "sequence": "6", + "occurredAt": "2026-05-13T05:00:20.000Z", + "uri": "fixture://swap-memory-valid", + "summary": "swap memory signal pulse 6", + "contractEvent": { + "schema": "flowmemory.flowpulse_contract_event.v0", + "interfaceName": "IFlowPulse", + "eventName": "FlowPulse", + "eventSignatureText": "FlowPulse(bytes32,bytes32,address,uint8,bytes32,bytes32,bytes32,uint64,uint64,string)", + "eventTopic0": "0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43", + "expectedTopic0": "0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43", + "topicMatchesContract": true, + "sourceContract": "0x2222222222222222222222222222222222222222", + "pulseTypeId": "4", + "pulseTypeName": "SWAP_MEMORY_SIGNAL", + "indexed": { + "pulseId": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab008", + "rootfieldId": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "actor": "0x4444444444444444444444444444444444444444" + }, + "payload": { + "subject": "0x1212121212121212121212121212121212121212121212121212121212121212", + "commitment": "0x8fabbad2f4aae2b67e5dcacc7cd425b7860d4d4453488c6c9770196b5009d795", + "parentPulseId": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab001", + "sequence": "6", + "occurredAt": "2026-05-13T05:00:20.000Z", + "uri": "fixture://swap-memory-valid" + }, + "receiptLocator": { + "chainId": "8453", + "blockNumber": "123457", + "blockHash": "0x2222222222222222222222222222222222222222222222222222222222222230", + "txHash": "0x3333333333333333333333333333333333333333333333333333333333333341", + "transactionIndex": "15", + "logIndex": "10", + "receiptStatus": "success" + }, + "receiptDerivedFields": [ + "blockHash", + "txHash", + "transactionIndex", + "logIndex", + "receiptStatus" + ] + }, + "id": "0xa0b1c648fb150b661e30a45dbccec410b1c3d510d94a7ac907e59141dc1cb36c", + "lastUpdated": "2026-05-13T17:02:00.000Z", + "provenance": { + "subsystem": "indexer", + "origin": "fixture", + "chainContext": "flowmemory-local-v0", + "fixturePath": "fixtures/launch-core/flowmemory-launch-v0.json", + "capturedAt": "2026-05-13T17:02:00.000Z", + "localPathHint": "fixtures/launch-core/flowmemory-launch-v0.json" + } + }, { "schema": "flowmemory.memory_signal.v0", "signalId": "0x4699ec901e15d260417fd6a9a86357c179ac55b631df05f28b29f28d5a5f3b61", @@ -1488,6 +1680,38 @@ "localPathHint": "fixtures/launch-core/flowmemory-launch-v0.json" } }, + { + "schema": "flowmemory.memory_receipt.v0", + "receiptId": "0x1706cc0af00148a0633b33e73856690ceea895f6c44bfe02c4d4a9819429dbce", + "reportId": "0xe0d47250f60e6d4764173e6ec13b29b06b9279abf461a5c7be4caf45374d8b40", + "reportDigest": "0xe0d47250f60e6d4764173e6ec13b29b06b9279abf461a5c7be4caf45374d8b40", + "observationId": "0x62e6ea1899a4dfd0d0a6cf6010ffb98983db20a9755257c8288602d059aa169e", + "rootfieldId": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "verifierStatus": "valid", + "flowMemoryStatus": "verified", + "resolverPolicyId": "flowmemory.resolver.policy.v0.fixture", + "verifierSpecVersion": "0", + "checksPassed": 3, + "checksTotal": 3, + "reasonCodes": [], + "evidenceRefs": [ + { + "kind": "swap-memory-signal", + "uri": "fixture://swap-memory-valid" + } + ], + "id": "0x1706cc0af00148a0633b33e73856690ceea895f6c44bfe02c4d4a9819429dbce", + "status": "verified", + "lastUpdated": "2026-05-13T17:02:00.000Z", + "provenance": { + "subsystem": "verifier", + "origin": "fixture", + "chainContext": "flowmemory-local-v0", + "fixturePath": "fixtures/launch-core/flowmemory-launch-v0.json", + "capturedAt": "2026-05-13T17:02:00.000Z", + "localPathHint": "fixtures/launch-core/flowmemory-launch-v0.json" + } + }, { "schema": "flowmemory.memory_receipt.v0", "receiptId": "0x48066ab6febdedb523cfcae2f313752db7e80467641d80914ebfe2e079d8c49d", @@ -1645,7 +1869,7 @@ "rootfieldBundles": [ { "schema": "flowmemory.rootfield_bundle.v0", - "bundleId": "0x31cf738173ed06b94ab86b99f2337c348040e50086a2d2f1f83ff9b0960fdbe6", + "bundleId": "0xc296618c8c57cbd6c4cc67cbf38e2ed31357177b565b4660b76e041c7189d5e1", "rootfieldId": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", "latestRoot": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1", "latestTransitionId": "0xdc9a57620b3309aceee35e858f62650b565e5f17ec6d9218ffd653ef5a633cdf", @@ -1653,6 +1877,7 @@ "transitionIds": [ "0x29fcee50b38bf403556ea08db7faeda6ea50430c08fc3126cebffad4a7657522", "0x5cdf7f5498350650adc19c62828425723a09bcca6571774992ca2d013fdc1068", + "0x2d4473b0c1eff862ed3d0db7caed9b2c4aff1f1c8668ace6683b9b33573d9349", "0xe1b6f858279961c968cdfba0b85d489767c486289e824cab3d017997064fed89", "0x35db5f8c36e2bc988c4d60f25a2e60d971256bdfdb79a73c92c0b82a41e5a367", "0x31d44377aa98e7fcaa07110256e1600249c4337c69e6d06c14d36e6aad6a79c4", @@ -1662,6 +1887,7 @@ "0xec2a80963190d2fa4d0eae023e735eeaf770cf33496eabdea696f3c7dda071eb", "0xec2a80963190d2fa4d0eae023e735eeaf770cf33496eabdea696f3c7dda071eb", "0xc595baea5e90a5f06ff49d8afeea0996eb39e13b3794903b8142dc9daf3a2df0", + "0xa0b1c648fb150b661e30a45dbccec410b1c3d510d94a7ac907e59141dc1cb36c", "0x4699ec901e15d260417fd6a9a86357c179ac55b631df05f28b29f28d5a5f3b61", "0xe32d6eeeff9c6bafadca56b980d58159afe8ffb29184458fa589c890829b9627", "0xfdc7756ac00683bc3e85bb3a9c2bd01f9719667b2d739818591fe697ee45d6c4", @@ -1670,6 +1896,7 @@ "memoryReceiptIds": [ "0x48066ab6febdedb523cfcae2f313752db7e80467641d80914ebfe2e079d8c49d", "0x7973443bfec1d763a16014d2299ff3e1a3d7dbda5d8a8c1c79638438fdf0f6fa", + "0x1706cc0af00148a0633b33e73856690ceea895f6c44bfe02c4d4a9819429dbce", "0x48066ab6febdedb523cfcae2f313752db7e80467641d80914ebfe2e079d8c49d", "0xdb38d4fa47cc00c6390ba08d3d6b7722291304d025a80981885294dd826843f8", "0x6a155b51ab1f84d1dbb20e47201db33a5a9a4996a2372987ce7559503cf97898", @@ -1679,6 +1906,7 @@ "verifierReportIds": [ "0x03af9720a87f7d72ce2ce95c1d04d50134649da51ace802d88818ad67fb2ebb3", "0xf1dfced6038cfa79928e1888e611da6bb05e7a14393cb20e2d5a5cb90c825c35", + "0xe0d47250f60e6d4764173e6ec13b29b06b9279abf461a5c7be4caf45374d8b40", "0x03af9720a87f7d72ce2ce95c1d04d50134649da51ace802d88818ad67fb2ebb3", "0xb7cef189a48b8c2697603c3704a52bb3a0322b375fdbea68d2bbbadb65a92ec1", "0xabc22e2203a9404c832aa792f7a569e17e599078fc9ebacf2c1307bbf6f1d7bb", @@ -1686,16 +1914,16 @@ "0x61b984c1055654334a7a7a191887779c8f2be937b08590e8a90927ee645042f5" ], "counts": { - "observations": 7, - "transitions": 6, - "receipts": 7, - "verified": 2, + "observations": 8, + "transitions": 7, + "receipts": 8, + "verified": 3, "failed": 1, "unresolved": 1, "unsupported": 1, "reorged": 1 }, - "id": "0x31cf738173ed06b94ab86b99f2337c348040e50086a2d2f1f83ff9b0960fdbe6", + "id": "0xc296618c8c57cbd6c4cc67cbf38e2ed31357177b565b4660b76e041c7189d5e1", "lastUpdated": "2026-05-13T17:02:00.000Z", "provenance": { "subsystem": "indexer", @@ -1719,6 +1947,7 @@ "0xec2a80963190d2fa4d0eae023e735eeaf770cf33496eabdea696f3c7dda071eb", "0xec2a80963190d2fa4d0eae023e735eeaf770cf33496eabdea696f3c7dda071eb", "0xc595baea5e90a5f06ff49d8afeea0996eb39e13b3794903b8142dc9daf3a2df0", + "0xa0b1c648fb150b661e30a45dbccec410b1c3d510d94a7ac907e59141dc1cb36c", "0x4699ec901e15d260417fd6a9a86357c179ac55b631df05f28b29f28d5a5f3b61", "0xe32d6eeeff9c6bafadca56b980d58159afe8ffb29184458fa589c890829b9627", "0xfdc7756ac00683bc3e85bb3a9c2bd01f9719667b2d739818591fe697ee45d6c4", @@ -1727,6 +1956,7 @@ "receiptIds": [ "0x48066ab6febdedb523cfcae2f313752db7e80467641d80914ebfe2e079d8c49d", "0x7973443bfec1d763a16014d2299ff3e1a3d7dbda5d8a8c1c79638438fdf0f6fa", + "0x1706cc0af00148a0633b33e73856690ceea895f6c44bfe02c4d4a9819429dbce", "0x48066ab6febdedb523cfcae2f313752db7e80467641d80914ebfe2e079d8c49d", "0xdb38d4fa47cc00c6390ba08d3d6b7722291304d025a80981885294dd826843f8", "0x6a155b51ab1f84d1dbb20e47201db33a5a9a4996a2372987ce7559503cf97898", @@ -1736,6 +1966,7 @@ "transitionIds": [ "0x29fcee50b38bf403556ea08db7faeda6ea50430c08fc3126cebffad4a7657522", "0x5cdf7f5498350650adc19c62828425723a09bcca6571774992ca2d013fdc1068", + "0x2d4473b0c1eff862ed3d0db7caed9b2c4aff1f1c8668ace6683b9b33573d9349", "0xe1b6f858279961c968cdfba0b85d489767c486289e824cab3d017997064fed89", "0x35db5f8c36e2bc988c4d60f25a2e60d971256bdfdb79a73c92c0b82a41e5a367", "0x31d44377aa98e7fcaa07110256e1600249c4337c69e6d06c14d36e6aad6a79c4", @@ -1769,8 +2000,8 @@ "stateRoot": "0x76ec5260c34184b6bb54ca406a43fc1f9591a47f37f71583a7620ef4a4065aff", "receiptsRoot": "0x6962bd6dbf28c2361c1337c1d33d678a815cc4b961e0e50db5ccb401cc0fe076", "timestamp": "2026-05-13T16:00:00.000Z", - "observationCount": 7, - "reportCount": 7, + "observationCount": 8, + "reportCount": 8, "finalityDistance": 123460, "status": "stale", "lastUpdated": "2026-05-13T17:02:00.000Z", @@ -1791,8 +2022,8 @@ "stateRoot": "0x3e1f5fddd84f9d460ee30a380ff700b17611891b8c03eb320edf1baefe003ef9", "receiptsRoot": "0xa0407b9a8a55106d549e0f19b92fceaa7f7a25697e94ebf8a1fa74af7b9168f4", "timestamp": "2026-05-13T16:00:01.000Z", - "observationCount": 7, - "reportCount": 7, + "observationCount": 8, + "reportCount": 8, "finalityDistance": 123459, "status": "finalized", "lastUpdated": "2026-05-13T17:02:00.000Z", diff --git a/fixtures/launch-core/flowmemory-launch-v0.json b/fixtures/launch-core/flowmemory-launch-v0.json index f5298b92..7f753802 100644 --- a/fixtures/launch-core/flowmemory-launch-v0.json +++ b/fixtures/launch-core/flowmemory-launch-v0.json @@ -211,6 +211,71 @@ ] } }, + { + "schema": "flowmemory.memory_signal.v0", + "signalId": "0xa0b1c648fb150b661e30a45dbccec410b1c3d510d94a7ac907e59141dc1cb36c", + "observationId": "0x62e6ea1899a4dfd0d0a6cf6010ffb98983db20a9755257c8288602d059aa169e", + "pulseId": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab008", + "rootfieldId": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "signalType": "swap_memory_signal", + "status": "verified", + "chainId": "8453", + "emittingContract": "0x2222222222222222222222222222222222222222", + "blockNumber": "123457", + "blockHash": "0x2222222222222222222222222222222222222222222222222222222222222230", + "txHash": "0x3333333333333333333333333333333333333333333333333333333333333341", + "transactionIndex": "15", + "logIndex": "10", + "actor": "0x4444444444444444444444444444444444444444", + "subject": "0x1212121212121212121212121212121212121212121212121212121212121212", + "commitment": "0x8fabbad2f4aae2b67e5dcacc7cd425b7860d4d4453488c6c9770196b5009d795", + "parentPulseId": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab001", + "sequence": "6", + "occurredAt": "2026-05-13T05:00:20.000Z", + "uri": "fixture://swap-memory-valid", + "summary": "swap memory signal pulse 6", + "contractEvent": { + "schema": "flowmemory.flowpulse_contract_event.v0", + "interfaceName": "IFlowPulse", + "eventName": "FlowPulse", + "eventSignatureText": "FlowPulse(bytes32,bytes32,address,uint8,bytes32,bytes32,bytes32,uint64,uint64,string)", + "eventTopic0": "0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43", + "expectedTopic0": "0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43", + "topicMatchesContract": true, + "sourceContract": "0x2222222222222222222222222222222222222222", + "pulseTypeId": "4", + "pulseTypeName": "SWAP_MEMORY_SIGNAL", + "indexed": { + "pulseId": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab008", + "rootfieldId": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "actor": "0x4444444444444444444444444444444444444444" + }, + "payload": { + "subject": "0x1212121212121212121212121212121212121212121212121212121212121212", + "commitment": "0x8fabbad2f4aae2b67e5dcacc7cd425b7860d4d4453488c6c9770196b5009d795", + "parentPulseId": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab001", + "sequence": "6", + "occurredAt": "2026-05-13T05:00:20.000Z", + "uri": "fixture://swap-memory-valid" + }, + "receiptLocator": { + "chainId": "8453", + "blockNumber": "123457", + "blockHash": "0x2222222222222222222222222222222222222222222222222222222222222230", + "txHash": "0x3333333333333333333333333333333333333333333333333333333333333341", + "transactionIndex": "15", + "logIndex": "10", + "receiptStatus": "success" + }, + "receiptDerivedFields": [ + "blockHash", + "txHash", + "transactionIndex", + "logIndex", + "receiptStatus" + ] + } + }, { "schema": "flowmemory.memory_signal.v0", "signalId": "0x4699ec901e15d260417fd6a9a86357c179ac55b631df05f28b29f28d5a5f3b61", @@ -515,6 +580,27 @@ } ] }, + { + "schema": "flowmemory.memory_receipt.v0", + "receiptId": "0x1706cc0af00148a0633b33e73856690ceea895f6c44bfe02c4d4a9819429dbce", + "reportId": "0xe0d47250f60e6d4764173e6ec13b29b06b9279abf461a5c7be4caf45374d8b40", + "reportDigest": "0xe0d47250f60e6d4764173e6ec13b29b06b9279abf461a5c7be4caf45374d8b40", + "observationId": "0x62e6ea1899a4dfd0d0a6cf6010ffb98983db20a9755257c8288602d059aa169e", + "rootfieldId": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "verifierStatus": "valid", + "flowMemoryStatus": "verified", + "resolverPolicyId": "flowmemory.resolver.policy.v0.fixture", + "verifierSpecVersion": "0", + "checksPassed": 3, + "checksTotal": 3, + "reasonCodes": [], + "evidenceRefs": [ + { + "kind": "swap-memory-signal", + "uri": "fixture://swap-memory-valid" + } + ] + }, { "schema": "flowmemory.memory_receipt.v0", "receiptId": "0x48066ab6febdedb523cfcae2f313752db7e80467641d80914ebfe2e079d8c49d", @@ -675,6 +761,36 @@ "logIndex": "3" } }, + { + "schema": "flowmemory.rootflow_transition.v0", + "transitionId": "0x2d4473b0c1eff862ed3d0db7caed9b2c4aff1f1c8668ace6683b9b33573d9349", + "rootfieldId": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "observationId": "0x62e6ea1899a4dfd0d0a6cf6010ffb98983db20a9755257c8288602d059aa169e", + "pulseId": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab008", + "parentPulseId": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab001", + "parentTransitionId": "0x5cdf7f5498350650adc19c62828425723a09bcca6571774992ca2d013fdc1068", + "memorySignalId": "0xa0b1c648fb150b661e30a45dbccec410b1c3d510d94a7ac907e59141dc1cb36c", + "memoryReceiptId": "0x1706cc0af00148a0633b33e73856690ceea895f6c44bfe02c4d4a9819429dbce", + "reportId": "0xe0d47250f60e6d4764173e6ec13b29b06b9279abf461a5c7be4caf45374d8b40", + "previousRoot": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1", + "attemptedRoot": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1", + "nextRoot": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1", + "status": "verified", + "blockNumber": "123457", + "txHash": "0x3333333333333333333333333333333333333333333333333333333333333341", + "sequence": "6", + "reasonCodes": [], + "contractEventRef": { + "signalId": "0xa0b1c648fb150b661e30a45dbccec410b1c3d510d94a7ac907e59141dc1cb36c", + "eventName": "FlowPulse", + "eventTopic0": "0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43", + "sourceContract": "0x2222222222222222222222222222222222222222", + "pulseTypeId": "4", + "pulseTypeName": "SWAP_MEMORY_SIGNAL", + "txHash": "0x3333333333333333333333333333333333333333333333333333333333333341", + "logIndex": "10" + } + }, { "schema": "flowmemory.rootflow_transition.v0", "transitionId": "0xe1b6f858279961c968cdfba0b85d489767c486289e824cab3d017997064fed89", @@ -807,7 +923,7 @@ "rootfieldBundles": [ { "schema": "flowmemory.rootfield_bundle.v0", - "bundleId": "0x31cf738173ed06b94ab86b99f2337c348040e50086a2d2f1f83ff9b0960fdbe6", + "bundleId": "0xc296618c8c57cbd6c4cc67cbf38e2ed31357177b565b4660b76e041c7189d5e1", "rootfieldId": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", "latestRoot": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1", "latestTransitionId": "0xdc9a57620b3309aceee35e858f62650b565e5f17ec6d9218ffd653ef5a633cdf", @@ -815,6 +931,7 @@ "transitionIds": [ "0x29fcee50b38bf403556ea08db7faeda6ea50430c08fc3126cebffad4a7657522", "0x5cdf7f5498350650adc19c62828425723a09bcca6571774992ca2d013fdc1068", + "0x2d4473b0c1eff862ed3d0db7caed9b2c4aff1f1c8668ace6683b9b33573d9349", "0xe1b6f858279961c968cdfba0b85d489767c486289e824cab3d017997064fed89", "0x35db5f8c36e2bc988c4d60f25a2e60d971256bdfdb79a73c92c0b82a41e5a367", "0x31d44377aa98e7fcaa07110256e1600249c4337c69e6d06c14d36e6aad6a79c4", @@ -824,6 +941,7 @@ "0xec2a80963190d2fa4d0eae023e735eeaf770cf33496eabdea696f3c7dda071eb", "0xec2a80963190d2fa4d0eae023e735eeaf770cf33496eabdea696f3c7dda071eb", "0xc595baea5e90a5f06ff49d8afeea0996eb39e13b3794903b8142dc9daf3a2df0", + "0xa0b1c648fb150b661e30a45dbccec410b1c3d510d94a7ac907e59141dc1cb36c", "0x4699ec901e15d260417fd6a9a86357c179ac55b631df05f28b29f28d5a5f3b61", "0xe32d6eeeff9c6bafadca56b980d58159afe8ffb29184458fa589c890829b9627", "0xfdc7756ac00683bc3e85bb3a9c2bd01f9719667b2d739818591fe697ee45d6c4", @@ -832,6 +950,7 @@ "memoryReceiptIds": [ "0x48066ab6febdedb523cfcae2f313752db7e80467641d80914ebfe2e079d8c49d", "0x7973443bfec1d763a16014d2299ff3e1a3d7dbda5d8a8c1c79638438fdf0f6fa", + "0x1706cc0af00148a0633b33e73856690ceea895f6c44bfe02c4d4a9819429dbce", "0x48066ab6febdedb523cfcae2f313752db7e80467641d80914ebfe2e079d8c49d", "0xdb38d4fa47cc00c6390ba08d3d6b7722291304d025a80981885294dd826843f8", "0x6a155b51ab1f84d1dbb20e47201db33a5a9a4996a2372987ce7559503cf97898", @@ -841,6 +960,7 @@ "verifierReportIds": [ "0x03af9720a87f7d72ce2ce95c1d04d50134649da51ace802d88818ad67fb2ebb3", "0xf1dfced6038cfa79928e1888e611da6bb05e7a14393cb20e2d5a5cb90c825c35", + "0xe0d47250f60e6d4764173e6ec13b29b06b9279abf461a5c7be4caf45374d8b40", "0x03af9720a87f7d72ce2ce95c1d04d50134649da51ace802d88818ad67fb2ebb3", "0xb7cef189a48b8c2697603c3704a52bb3a0322b375fdbea68d2bbbadb65a92ec1", "0xabc22e2203a9404c832aa792f7a569e17e599078fc9ebacf2c1307bbf6f1d7bb", @@ -848,10 +968,10 @@ "0x61b984c1055654334a7a7a191887779c8f2be937b08590e8a90927ee645042f5" ], "counts": { - "observations": 7, - "transitions": 6, - "receipts": 7, - "verified": 2, + "observations": 8, + "transitions": 7, + "receipts": 8, + "verified": 3, "failed": 1, "unresolved": 1, "unsupported": 1, @@ -871,6 +991,7 @@ "0xec2a80963190d2fa4d0eae023e735eeaf770cf33496eabdea696f3c7dda071eb", "0xec2a80963190d2fa4d0eae023e735eeaf770cf33496eabdea696f3c7dda071eb", "0xc595baea5e90a5f06ff49d8afeea0996eb39e13b3794903b8142dc9daf3a2df0", + "0xa0b1c648fb150b661e30a45dbccec410b1c3d510d94a7ac907e59141dc1cb36c", "0x4699ec901e15d260417fd6a9a86357c179ac55b631df05f28b29f28d5a5f3b61", "0xe32d6eeeff9c6bafadca56b980d58159afe8ffb29184458fa589c890829b9627", "0xfdc7756ac00683bc3e85bb3a9c2bd01f9719667b2d739818591fe697ee45d6c4", @@ -879,6 +1000,7 @@ "receiptIds": [ "0x48066ab6febdedb523cfcae2f313752db7e80467641d80914ebfe2e079d8c49d", "0x7973443bfec1d763a16014d2299ff3e1a3d7dbda5d8a8c1c79638438fdf0f6fa", + "0x1706cc0af00148a0633b33e73856690ceea895f6c44bfe02c4d4a9819429dbce", "0x48066ab6febdedb523cfcae2f313752db7e80467641d80914ebfe2e079d8c49d", "0xdb38d4fa47cc00c6390ba08d3d6b7722291304d025a80981885294dd826843f8", "0x6a155b51ab1f84d1dbb20e47201db33a5a9a4996a2372987ce7559503cf97898", @@ -888,6 +1010,7 @@ "transitionIds": [ "0x29fcee50b38bf403556ea08db7faeda6ea50430c08fc3126cebffad4a7657522", "0x5cdf7f5498350650adc19c62828425723a09bcca6571774992ca2d013fdc1068", + "0x2d4473b0c1eff862ed3d0db7caed9b2c4aff1f1c8668ace6683b9b33573d9349", "0xe1b6f858279961c968cdfba0b85d489767c486289e824cab3d017997064fed89", "0x35db5f8c36e2bc988c4d60f25a2e60d971256bdfdb79a73c92c0b82a41e5a367", "0x31d44377aa98e7fcaa07110256e1600249c4337c69e6d06c14d36e6aad6a79c4", @@ -903,10 +1026,10 @@ } ], "acceptance": { - "loadedFlowPulses": 7, - "indexedObservations": 7, - "verifierReports": 7, - "rootflowTransitions": 6, + "loadedFlowPulses": 8, + "indexedObservations": 8, + "verifierReports": 8, + "rootflowTransitions": 7, "dashboardFixtureGenerated": true, "localOnly": true } diff --git a/fixtures/launch-core/rootflow-transitions.json b/fixtures/launch-core/rootflow-transitions.json index a73b43db..cca04308 100644 --- a/fixtures/launch-core/rootflow-transitions.json +++ b/fixtures/launch-core/rootflow-transitions.json @@ -62,6 +62,36 @@ "logIndex": "3" } }, + { + "schema": "flowmemory.rootflow_transition.v0", + "transitionId": "0x2d4473b0c1eff862ed3d0db7caed9b2c4aff1f1c8668ace6683b9b33573d9349", + "rootfieldId": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "observationId": "0x62e6ea1899a4dfd0d0a6cf6010ffb98983db20a9755257c8288602d059aa169e", + "pulseId": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab008", + "parentPulseId": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab001", + "parentTransitionId": "0x5cdf7f5498350650adc19c62828425723a09bcca6571774992ca2d013fdc1068", + "memorySignalId": "0xa0b1c648fb150b661e30a45dbccec410b1c3d510d94a7ac907e59141dc1cb36c", + "memoryReceiptId": "0x1706cc0af00148a0633b33e73856690ceea895f6c44bfe02c4d4a9819429dbce", + "reportId": "0xe0d47250f60e6d4764173e6ec13b29b06b9279abf461a5c7be4caf45374d8b40", + "previousRoot": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1", + "attemptedRoot": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1", + "nextRoot": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1", + "status": "verified", + "blockNumber": "123457", + "txHash": "0x3333333333333333333333333333333333333333333333333333333333333341", + "sequence": "6", + "reasonCodes": [], + "contractEventRef": { + "signalId": "0xa0b1c648fb150b661e30a45dbccec410b1c3d510d94a7ac907e59141dc1cb36c", + "eventName": "FlowPulse", + "eventTopic0": "0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43", + "sourceContract": "0x2222222222222222222222222222222222222222", + "pulseTypeId": "4", + "pulseTypeName": "SWAP_MEMORY_SIGNAL", + "txHash": "0x3333333333333333333333333333333333333333333333333333333333333341", + "logIndex": "10" + } + }, { "schema": "flowmemory.rootflow_transition.v0", "transitionId": "0xe1b6f858279961c968cdfba0b85d489767c486289e824cab3d017997064fed89", diff --git a/foundry.toml b/foundry.toml index b7b8c1bb..97ae8ed0 100644 --- a/foundry.toml +++ b/foundry.toml @@ -7,3 +7,6 @@ libs = [] solc_version = "0.8.24" optimizer = true optimizer_runs = 200 + +[rpc_endpoints] +base_sepolia = "${BASE_SEPOLIA_RPC_URL}" diff --git a/infra/scripts/run-base-sepolia-deploy.mjs b/infra/scripts/run-base-sepolia-deploy.mjs new file mode 100644 index 00000000..05374359 --- /dev/null +++ b/infra/scripts/run-base-sepolia-deploy.mjs @@ -0,0 +1,39 @@ +#!/usr/bin/env node +import { spawnSync } from "node:child_process"; + +const args = new Set(process.argv.slice(2)); +const broadcast = args.has("--broadcast"); + +const rpcUrl = process.env.BASE_SEPOLIA_RPC_URL; +const deployerKey = process.env.BASE_SEPOLIA_DEPLOYER_KEY_HEX; + +if (!rpcUrl) { + throw new Error("BASE_SEPOLIA_RPC_URL is required for Base Sepolia deployment runs"); +} + +if (!deployerKey) { + throw new Error("BASE_SEPOLIA_DEPLOYER_KEY_HEX is required for Base Sepolia deployment runs"); +} + +const forgeArgs = [ + "script", + "script/DeployLaunchCandidate.s.sol:DeployLaunchCandidate", + "--rpc-url", + rpcUrl, + "--private-key", + deployerKey, +]; + +if (broadcast) { + forgeArgs.push("--broadcast", "--slow"); +} + +const result = spawnSync("forge", forgeArgs, { + cwd: process.cwd(), + stdio: "inherit", + shell: process.platform === "win32", +}); + +if (result.status !== 0) { + throw new Error(`Base Sepolia deploy ${broadcast ? "broadcast" : "dry run"} failed with exit code ${result.status ?? "unknown"}`); +} diff --git a/infra/scripts/run-contract-hardening.mjs b/infra/scripts/run-contract-hardening.mjs new file mode 100644 index 00000000..66f2922f --- /dev/null +++ b/infra/scripts/run-contract-hardening.mjs @@ -0,0 +1,42 @@ +#!/usr/bin/env node +import { spawnSync } from "node:child_process"; +import { resolve } from "node:path"; + +const args = new Set(process.argv.slice(2)); +const requireSlither = args.has("--require-slither"); +const checkFormat = args.has("--check-format"); +const repoRoot = process.cwd(); + +function run(command, commandArgs, options = {}) { + const result = spawnSync(command, commandArgs, { + cwd: repoRoot, + stdio: "inherit", + shell: false, + ...options, + }); + + if (result.status !== 0) { + throw new Error(`contract hardening failed with exit code ${result.status ?? "unknown"}`); + } +} + +if (process.platform === "win32") { + const psArgs = [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-File", + resolve(repoRoot, "infra/scripts/contracts-static-analysis.ps1"), + ]; + if (checkFormat) psArgs.push("-CheckFormat"); + if (requireSlither) psArgs.push("-RequireSlither"); + run("powershell.exe", psArgs); +} else { + run("bash", [resolve(repoRoot, "infra/scripts/contracts-static-analysis.sh")], { + env: { + ...process.env, + REQUIRE_SLITHER: requireSlither ? "1" : process.env.REQUIRE_SLITHER ?? "0", + CHECK_FORGE_FMT: checkFormat ? "1" : process.env.CHECK_FORGE_FMT ?? "0", + }, + }); +} diff --git a/package-lock.json b/package-lock.json index 846780fb..55b2c206 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,11 @@ "services/indexer", "services/verifier", "services/flowmemory" - ] + ], + "devDependencies": { + "ajv": "^8.20.0", + "ajv-formats": "^3.0.1" + } }, "node_modules/@flowmemory/indexer-v0": { "resolved": "services/indexer", @@ -28,6 +32,82 @@ "resolved": "services/verifier", "link": true }, + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "services/flowmemory": { "name": "@flowmemory/launch-core-v0" }, diff --git a/package.json b/package.json index a7d432ac..ef7ffe50 100644 --- a/package.json +++ b/package.json @@ -10,13 +10,26 @@ ], "scripts": { "test": "npm test --prefix services/shared && npm test --prefix services/indexer && npm test --prefix services/verifier && npm test --prefix services/flowmemory", + "contracts:hardening": "node infra/scripts/run-contract-hardening.mjs", + "contracts:hardening:slither": "node infra/scripts/run-contract-hardening.mjs --require-slither", "index:base-sepolia": "npm run index:base-sepolia --prefix services/indexer", + "read:base-sepolia": "npm run index:base-sepolia --prefix services/indexer", + "deploy:base-sepolia": "node infra/scripts/run-base-sepolia-deploy.mjs", + "deploy:base-sepolia:broadcast": "node infra/scripts/run-base-sepolia-deploy.mjs --broadcast", "index:fixtures": "npm run index:fixtures --prefix services/indexer", "verify:fixtures": "npm run verify:fixtures --prefix services/verifier", "flowmemory:generate": "npm run generate --prefix services/flowmemory", "launch:v0": "npm run launch:v0 --prefix services/flowmemory", + "validate:launch": "npm run validate --prefix services/flowmemory", + "fixtures:check": "npm run fixtures:check --prefix services/flowmemory", + "launch:candidate": "npm run contracts:hardening && npm run launch:v0 && npm run validate:launch && npm run fixtures:check && node infra/scripts/check-unsafe-claims.mjs", + "build:production": "npm run launch:candidate && npm run build --prefix apps/dashboard", "e2e": "npm run index:fixtures && npm run verify:fixtures && npm run flowmemory:generate", "demo:indexer": "npm run demo --prefix services/indexer", "demo:verifier": "npm run demo --prefix services/verifier" + }, + "devDependencies": { + "ajv": "^8.20.0", + "ajv-formats": "^3.0.1" } } diff --git a/schemas/flowmemory/memory-signal.schema.json b/schemas/flowmemory/memory-signal.schema.json index 1883568e..7e6f69dc 100644 --- a/schemas/flowmemory/memory-signal.schema.json +++ b/schemas/flowmemory/memory-signal.schema.json @@ -35,7 +35,7 @@ "observationId": { "$ref": "#/$defs/hex32" }, "pulseId": { "$ref": "#/$defs/hex32" }, "rootfieldId": { "$ref": "#/$defs/hex32" }, - "signalType": { "enum": ["rootfield_registration", "root_commitment", "unsupported_pulse"] }, + "signalType": { "enum": ["rootfield_registration", "root_commitment", "swap_memory_signal", "unsupported_pulse"] }, "status": { "$ref": "#/$defs/flowMemoryStatus" }, "chainId": { "type": "string", "minLength": 1 }, "emittingContract": { "$ref": "#/$defs/address" }, @@ -58,7 +58,7 @@ "hex32": { "type": "string", "pattern": "^0x[0-9a-fA-F]{64}$" }, "address": { "type": "string", "pattern": "^0x[0-9a-fA-F]{40}$" }, "flowPulseContractTypeName": { - "enum": ["ROOTFIELD_REGISTERED", "ROOT_COMMITTED", "ROOTFIELD_STATUS_CHANGED", "UNKNOWN_FLOWPULSE_TYPE"] + "enum": ["ROOTFIELD_REGISTERED", "ROOT_COMMITTED", "ROOTFIELD_STATUS_CHANGED", "SWAP_MEMORY_SIGNAL", "UNKNOWN_FLOWPULSE_TYPE"] }, "flowPulseContractEvent": { "type": "object", diff --git a/schemas/flowmemory/rootflow-transition.schema.json b/schemas/flowmemory/rootflow-transition.schema.json index 5f5a1a38..8d0b13aa 100644 --- a/schemas/flowmemory/rootflow-transition.schema.json +++ b/schemas/flowmemory/rootflow-transition.schema.json @@ -50,7 +50,7 @@ "hex32": { "type": "string", "pattern": "^0x[0-9a-fA-F]{64}$" }, "address": { "type": "string", "pattern": "^0x[0-9a-fA-F]{40}$" }, "flowPulseContractTypeName": { - "enum": ["ROOTFIELD_REGISTERED", "ROOT_COMMITTED", "ROOTFIELD_STATUS_CHANGED", "UNKNOWN_FLOWPULSE_TYPE"] + "enum": ["ROOTFIELD_REGISTERED", "ROOT_COMMITTED", "ROOTFIELD_STATUS_CHANGED", "SWAP_MEMORY_SIGNAL", "UNKNOWN_FLOWPULSE_TYPE"] }, "flowPulseContractEventRef": { "type": "object", diff --git a/script/DeployLaunchCandidate.s.sol b/script/DeployLaunchCandidate.s.sol new file mode 100644 index 00000000..ac53d41d --- /dev/null +++ b/script/DeployLaunchCandidate.s.sol @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import {ArtifactRegistry} from "../contracts/ArtifactRegistry.sol"; +import {CursorRegistry} from "../contracts/CursorRegistry.sol"; +import {FlowMemoryHookAdapter} from "../contracts/FlowMemoryHookAdapter.sol"; +import {ReceiptVerifier} from "../contracts/ReceiptVerifier.sol"; +import {RootfieldRegistry} from "../contracts/RootfieldRegistry.sol"; +import {VerifierRegistry} from "../contracts/VerifierRegistry.sol"; +import {VerifierReportRegistry} from "../contracts/VerifierReportRegistry.sol"; +import {WorkerRegistry} from "../contracts/WorkerRegistry.sol"; +import {WorkDebtScheduler} from "../contracts/WorkDebtScheduler.sol"; +import {WorkReceiptRegistry} from "../contracts/WorkReceiptRegistry.sol"; + +interface Vm { + function startBroadcast() external; + function stopBroadcast() external; +} + +/// @title DeployLaunchCandidate +/// @notice Foundry deployment script for the current FlowMemory V0 testnet surface. +/// @dev This deploys independent V0 contracts only. It does not configure a production +/// protocol, proxy upgrade path, token, custody system, or verifier network. +contract DeployLaunchCandidate { + Vm private constant VM = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); + + struct Deployment { + address rootfieldRegistry; + address flowMemoryHookAdapter; + address artifactRegistry; + address cursorRegistry; + address receiptVerifier; + address workerRegistry; + address verifierRegistry; + address workReceiptRegistry; + address verifierReportRegistry; + address workDebtScheduler; + } + + event FlowMemoryLaunchCandidateDeployed( + address indexed rootfieldRegistry, + address indexed flowMemoryHookAdapter, + address artifactRegistry, + address cursorRegistry, + address receiptVerifier, + address workerRegistry, + address verifierRegistry, + address workReceiptRegistry, + address verifierReportRegistry, + address workDebtScheduler + ); + + function run() external returns (Deployment memory deployment) { + VM.startBroadcast(); + + deployment = Deployment({ + rootfieldRegistry: address(new RootfieldRegistry()), + flowMemoryHookAdapter: address(new FlowMemoryHookAdapter()), + artifactRegistry: address(new ArtifactRegistry()), + cursorRegistry: address(new CursorRegistry()), + receiptVerifier: address(new ReceiptVerifier()), + workerRegistry: address(new WorkerRegistry()), + verifierRegistry: address(new VerifierRegistry()), + workReceiptRegistry: address(new WorkReceiptRegistry()), + verifierReportRegistry: address(new VerifierReportRegistry()), + workDebtScheduler: address(new WorkDebtScheduler()) + }); + + emit FlowMemoryLaunchCandidateDeployed( + deployment.rootfieldRegistry, + deployment.flowMemoryHookAdapter, + deployment.artifactRegistry, + deployment.cursorRegistry, + deployment.receiptVerifier, + deployment.workerRegistry, + deployment.verifierRegistry, + deployment.workReceiptRegistry, + deployment.verifierReportRegistry, + deployment.workDebtScheduler + ); + + VM.stopBroadcast(); + } +} diff --git a/services/flowmemory/package.json b/services/flowmemory/package.json index 9825b726..f65daf1b 100644 --- a/services/flowmemory/package.json +++ b/services/flowmemory/package.json @@ -5,6 +5,8 @@ "scripts": { "generate": "node src/generate-launch-core.ts", "launch:v0": "node src/launch-v0.ts", + "validate": "node src/validate-launch-core.ts", + "fixtures:check": "node src/check-fixture-drift.ts", "test": "node --test test/*.test.ts" } } diff --git a/services/flowmemory/src/check-fixture-drift.ts b/services/flowmemory/src/check-fixture-drift.ts new file mode 100644 index 00000000..d0bab6cf --- /dev/null +++ b/services/flowmemory/src/check-fixture-drift.ts @@ -0,0 +1,69 @@ +import { mkdirSync, readFileSync, rmSync } from "node:fs"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +import { canonicalJson } from "../../shared/src/index.ts"; +import { DEFAULT_LAUNCH_CORE_PATHS, generateLaunchCore, type LaunchCorePaths } from "./generate-launch-core.ts"; + +const REPO_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "../../.."); +const TEMP_ROOT = "out/flowmemory-fixture-drift"; + +const fixturePairs: Array<[keyof LaunchCorePaths, string]> = [ + ["launchOutPath", DEFAULT_LAUNCH_CORE_PATHS.launchOutPath], + ["transitionsOutPath", DEFAULT_LAUNCH_CORE_PATHS.transitionsOutPath], + ["dashboardOutPath", DEFAULT_LAUNCH_CORE_PATHS.dashboardOutPath], + ["dashboardRuntimePath", DEFAULT_LAUNCH_CORE_PATHS.dashboardRuntimePath], +]; + +function readCanonical(path: string): string { + return canonicalJson(JSON.parse(readFileSync(resolve(REPO_ROOT, path), "utf8"))); +} + +function tempPath(name: keyof LaunchCorePaths): string { + if (name === "launchOutPath") return `${TEMP_ROOT}/flowmemory-launch-v0.json`; + if (name === "transitionsOutPath") return `${TEMP_ROOT}/rootflow-transitions.json`; + if (name === "dashboardOutPath") return `${TEMP_ROOT}/flowmemory-dashboard-v0.json`; + if (name === "dashboardRuntimePath") return `${TEMP_ROOT}/dashboard-runtime/flowmemory-dashboard-v0.json`; + throw new Error(`unsupported generated fixture path key: ${name}`); +} + +export function checkFixtureDrift(): void { + process.chdir(REPO_ROOT); + rmSync(resolve(REPO_ROOT, TEMP_ROOT), { recursive: true, force: true }); + mkdirSync(resolve(REPO_ROOT, TEMP_ROOT), { recursive: true }); + + const tempPaths: LaunchCorePaths = { + ...DEFAULT_LAUNCH_CORE_PATHS, + launchOutPath: tempPath("launchOutPath"), + transitionsOutPath: tempPath("transitionsOutPath"), + dashboardOutPath: tempPath("dashboardOutPath"), + dashboardRuntimePath: tempPath("dashboardRuntimePath"), + }; + + generateLaunchCore(tempPaths); + + const drifted = fixturePairs + .map(([key, committedPath]) => { + const generatedPath = tempPath(key); + return readCanonical(committedPath) === readCanonical(generatedPath) + ? null + : { committedPath, generatedPath }; + }) + .filter((result): result is { committedPath: string; generatedPath: string } => result !== null); + + if (drifted.length > 0) { + throw new Error(`Generated launch fixtures are stale:\n${drifted + .map((result) => `- ${result.committedPath} differs from ${result.generatedPath}`) + .join("\n")}\nRun npm run launch:v0 and commit the regenerated fixtures.`); + } + + console.log(JSON.stringify({ + service: "flowmemory-fixture-drift-check", + checked: fixturePairs.map(([, path]) => path), + status: "clean", + }, null, 2)); +} + +if (process.argv[1] === fileURLToPath(import.meta.url)) { + checkFixtureDrift(); +} diff --git a/services/flowmemory/src/generate-launch-core.ts b/services/flowmemory/src/generate-launch-core.ts index 72d48984..48408559 100644 --- a/services/flowmemory/src/generate-launch-core.ts +++ b/services/flowmemory/src/generate-launch-core.ts @@ -32,6 +32,7 @@ const FLOWPULSE_CONTRACT_TYPE_NAMES: Record = "1": "ROOTFIELD_REGISTERED", "2": "ROOT_COMMITTED", "3": "ROOTFIELD_STATUS_CHANGED", + "4": "SWAP_MEMORY_SIGNAL", }; type JsonObject = Record; @@ -153,6 +154,9 @@ function pulseTypeName(pulseType: string): MemorySignal["signalType"] { if (pulseType === "2") { return "root_commitment"; } + if (pulseType === "4") { + return "swap_memory_signal"; + } return "unsupported_pulse"; } diff --git a/services/flowmemory/src/types.ts b/services/flowmemory/src/types.ts index 7c10fd7c..a3fa7b8b 100644 --- a/services/flowmemory/src/types.ts +++ b/services/flowmemory/src/types.ts @@ -4,6 +4,7 @@ export type FlowPulseContractTypeName = | "ROOTFIELD_REGISTERED" | "ROOT_COMMITTED" | "ROOTFIELD_STATUS_CHANGED" + | "SWAP_MEMORY_SIGNAL" | "UNKNOWN_FLOWPULSE_TYPE"; export interface FlowPulseContractEvent { @@ -59,7 +60,7 @@ export interface MemorySignal { observationId: string; pulseId: string; rootfieldId: string; - signalType: "rootfield_registration" | "root_commitment" | "unsupported_pulse"; + signalType: "rootfield_registration" | "root_commitment" | "swap_memory_signal" | "unsupported_pulse"; status: FlowMemoryStatus; chainId: string; emittingContract: string; diff --git a/services/flowmemory/src/validate-launch-core.ts b/services/flowmemory/src/validate-launch-core.ts new file mode 100644 index 00000000..fd59a72a --- /dev/null +++ b/services/flowmemory/src/validate-launch-core.ts @@ -0,0 +1,150 @@ +import { readFileSync } from "node:fs"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +import Ajv2020, { type ValidateFunction } from "ajv/dist/2020.js"; +import addFormats from "ajv-formats"; + +import type { LaunchCoreOutput } from "./types.ts"; + +const REPO_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "../../.."); + +const schemaFiles = { + memorySignals: "schemas/flowmemory/memory-signal.schema.json", + memoryReceipts: "schemas/flowmemory/memory-receipt.schema.json", + rootflowTransitions: "schemas/flowmemory/rootflow-transition.schema.json", + rootfieldBundles: "schemas/flowmemory/rootfield-bundle.schema.json", + agentMemoryViews: "schemas/flowmemory/agent-memory-view.schema.json", +} as const; + +type LaunchCollectionName = keyof typeof schemaFiles; + +function readJson(path: string): T { + return JSON.parse(readFileSync(resolve(REPO_ROOT, path), "utf8")) as T; +} + +function compileValidators(): Record { + const ajv = new Ajv2020({ allErrors: true, strict: false }); + addFormats(ajv); + + return Object.fromEntries( + Object.entries(schemaFiles).map(([name, path]) => [name, ajv.compile(readJson(path))]), + ) as Record; +} + +function validateCollection( + validators: Record, + launchCore: LaunchCoreOutput, +): string[] { + const failures: string[] = []; + const collections: Record = { + memorySignals: launchCore.memorySignals, + memoryReceipts: launchCore.memoryReceipts, + rootflowTransitions: launchCore.rootflowTransitions, + rootfieldBundles: launchCore.rootfieldBundles, + agentMemoryViews: launchCore.agentMemoryViews, + }; + + for (const [name, records] of Object.entries(collections) as Array<[LaunchCollectionName, unknown[]]>) { + const validate = validators[name]; + records.forEach((record, index) => { + if (!validate(record)) { + failures.push(`${name}[${index}] ${ajvErrorText(validate)}`); + } + }); + } + + return failures; +} + +function ajvErrorText(validate: ValidateFunction): string { + return (validate.errors ?? []) + .map((error) => `${error.instancePath || "/"} ${error.message ?? "failed validation"}`) + .join("; "); +} + +function assertLaunchCandidateInvariants(launchCore: LaunchCoreOutput): string[] { + const failures: string[] = []; + const signalIds = new Set(launchCore.memorySignals.map((signal) => signal.signalId)); + const receiptIds = new Set(launchCore.memoryReceipts.map((receipt) => receipt.receiptId)); + const reportIds = new Set(launchCore.memoryReceipts.map((receipt) => receipt.reportId)); + + if (!launchCore.memorySignals.some((signal) => signal.signalType === "swap_memory_signal")) { + failures.push("launch core must include at least one swap_memory_signal"); + } + + for (const signal of launchCore.memorySignals) { + if (!signal.contractEvent.topicMatchesContract) { + failures.push(`signal ${signal.signalId} has a non-matching FlowPulse topic0`); + } + } + + for (const transition of launchCore.rootflowTransitions) { + if (!signalIds.has(transition.memorySignalId)) { + failures.push(`transition ${transition.transitionId} references missing signal ${transition.memorySignalId}`); + } + if (transition.contractEventRef.signalId !== transition.memorySignalId) { + failures.push(`transition ${transition.transitionId} contractEventRef does not match memorySignalId`); + } + if (transition.memoryReceiptId !== null && !receiptIds.has(transition.memoryReceiptId)) { + failures.push(`transition ${transition.transitionId} references missing receipt ${transition.memoryReceiptId}`); + } + if (transition.reportId !== null && !reportIds.has(transition.reportId)) { + failures.push(`transition ${transition.transitionId} references missing report ${transition.reportId}`); + } + } + + for (const receipt of launchCore.memoryReceipts) { + if (!launchCore.memorySignals.some((signal) => signal.observationId === receipt.observationId)) { + failures.push(`receipt ${receipt.receiptId} references missing observation ${receipt.observationId}`); + } + } + + const transitionIds = new Set(launchCore.rootflowTransitions.map((transition) => transition.transitionId)); + for (const view of launchCore.agentMemoryViews) { + for (const signalId of view.signalIds) { + if (!signalIds.has(signalId)) { + failures.push(`agent view ${view.viewId} references missing signal ${signalId}`); + } + } + for (const receiptId of view.receiptIds) { + if (!receiptIds.has(receiptId)) { + failures.push(`agent view ${view.viewId} references missing receipt ${receiptId}`); + } + } + for (const transitionId of view.transitionIds) { + if (!transitionIds.has(transitionId)) { + failures.push(`agent view ${view.viewId} references missing transition ${transitionId}`); + } + } + } + + return failures; +} + +export function validateLaunchCore(path = "fixtures/launch-core/flowmemory-launch-v0.json"): void { + const launchCore = readJson(path); + const failures = [ + ...validateCollection(compileValidators(), launchCore), + ...assertLaunchCandidateInvariants(launchCore), + ]; + + if (failures.length > 0) { + throw new Error(`FlowMemory launch-core validation failed:\n${failures.map((failure) => `- ${failure}`).join("\n")}`); + } + + console.log(JSON.stringify({ + service: "flowmemory-launch-core-validator", + path, + memorySignals: launchCore.memorySignals.length, + memoryReceipts: launchCore.memoryReceipts.length, + rootflowTransitions: launchCore.rootflowTransitions.length, + rootfieldBundles: launchCore.rootfieldBundles.length, + agentMemoryViews: launchCore.agentMemoryViews.length, + swapMemorySignals: launchCore.memorySignals.filter((signal) => signal.signalType === "swap_memory_signal").length, + }, null, 2)); +} + +if (process.argv[1] === fileURLToPath(import.meta.url)) { + validateLaunchCore(process.argv[2]); +} diff --git a/services/flowmemory/test/flowmemory.test.ts b/services/flowmemory/test/flowmemory.test.ts index 6f29f17f..6d0d9710 100644 --- a/services/flowmemory/test/flowmemory.test.ts +++ b/services/flowmemory/test/flowmemory.test.ts @@ -58,9 +58,9 @@ test("generates concrete Rootflow and Flow Memory V0 outputs", () => { assert.equal(launchCore.schema, "flowmemory.launch_core.v0"); assert.equal(launchCore.statusAdapter.valid, "verified"); assert.equal(launchCore.statusAdapter.invalid, "failed"); - assert.equal(launchCore.memorySignals.length, 7); - assert.equal(launchCore.memoryReceipts.length, 7); - assert.equal(launchCore.rootflowTransitions.length, 6); + assert.equal(launchCore.memorySignals.length, 8); + assert.equal(launchCore.memoryReceipts.length, 8); + assert.equal(launchCore.rootflowTransitions.length, 7); assert.equal(launchCore.rootfieldBundles.length, 1); assert.equal(launchCore.agentMemoryViews.length, 1); @@ -75,6 +75,8 @@ test("generates concrete Rootflow and Flow Memory V0 outputs", () => { const unsupportedSignal = launchCore.memorySignals.find((signal) => signal.contractEvent.pulseTypeId === "99"); assert.equal(unsupportedSignal?.contractEvent.pulseTypeName, "UNKNOWN_FLOWPULSE_TYPE"); + const swapSignal = launchCore.memorySignals.find((signal) => signal.signalType === "swap_memory_signal"); + assert.equal(swapSignal?.contractEvent.pulseTypeName, "SWAP_MEMORY_SIGNAL"); const firstTransition = launchCore.rootflowTransitions[0]; assert.equal(firstTransition.contractEventRef.signalId, firstTransition.memorySignalId); diff --git a/services/indexer/README.md b/services/indexer/README.md index 3903eaba..c75685a1 100644 --- a/services/indexer/README.md +++ b/services/indexer/README.md @@ -50,6 +50,7 @@ The fixture set covers: - valid rootfield registration - valid root commit +- valid swap-derived memory signal - duplicate observation - removed/reorg-style log - invalid commitment input diff --git a/services/indexer/fixtures/flowpulse-receipts.json b/services/indexer/fixtures/flowpulse-receipts.json index 185d25c1..c5d207f3 100644 --- a/services/indexer/fixtures/flowpulse-receipts.json +++ b/services/indexer/fixtures/flowpulse-receipts.json @@ -46,6 +46,28 @@ } ] }, + { + "name": "swap-memory-signal-valid", + "chainId": "8453", + "blockNumber": "123457", + "blockHash": "0x2222222222222222222222222222222222222222222222222222222222222230", + "transactionHash": "0x3333333333333333333333333333333333333333333333333333333333333341", + "transactionIndex": "15", + "status": "success", + "logs": [ + { + "address": "0x2222222222222222222222222222222222222222", + "topics": [ + "0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43", + "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab008", + "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "0x0000000000000000000000004444444444444444444444444444444444444444" + ], + "data": "0x000000000000000000000000000000000000000000000000000000000000000412121212121212121212121212121212121212121212121212121212121212128fabbad2f4aae2b67e5dcacc7cd425b7860d4d4453488c6c9770196b5009d795aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab0010000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000006a04056400000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000001b666978747572653a2f2f737761702d6d656d6f72792d76616c69640000000000", + "logIndex": "10" + } + ] + }, { "name": "duplicate-registration", "chainId": "8453", diff --git a/services/indexer/out/indexer-state.json b/services/indexer/out/indexer-state.json index 001c723c..de430ea3 100644 --- a/services/indexer/out/indexer-state.json +++ b/services/indexer/out/indexer-state.json @@ -1 +1 @@ -{"schema":"flowmemory.indexer.persistence.v0","state":{"batches":[{"cursorCount":6,"observationCount":7,"rejectedLogCount":2,"schema":"flowmemory.indexer.batch.v0","source":"fixture","sourceSetId":"0x95b6a52c8fbf5a0f68120c877461f0e7f75dc191c79a03e7510078f14a55dfe4"}],"cursors":[{"blockHash":"0x2222222222222222222222222222222222222222222222222222222222222222","blockNumber":"123456","chainId":"8453","cursorId":"0x668270c7410fea7c1581c9f4e8b9ba5a5d3eeebb7b5b75ee3a602b096245a21f","logIndex":"2","sourceSetId":"0x95b6a52c8fbf5a0f68120c877461f0e7f75dc191c79a03e7510078f14a55dfe4","transactionIndex":"7"},{"blockHash":"0x2222222222222222222222222222222222222222222222222222222222222224","blockNumber":"123458","chainId":"8453","cursorId":"0x69fddcac0f6148d6193cda1a3bdacde7c15a5bf4589831dfcfc0c90062bd05b4","logIndex":"4","sourceSetId":"0x95b6a52c8fbf5a0f68120c877461f0e7f75dc191c79a03e7510078f14a55dfe4","transactionIndex":"9"},{"blockHash":"0x2222222222222222222222222222222222222222222222222222222222222225","blockNumber":"123459","chainId":"8453","cursorId":"0x9bdea7fca6fd5954e75608037b1b8bb92a2312cd58a10573b40f0312a29285f8","logIndex":"5","sourceSetId":"0x95b6a52c8fbf5a0f68120c877461f0e7f75dc191c79a03e7510078f14a55dfe4","transactionIndex":"10"},{"blockHash":"0x2222222222222222222222222222222222222222222222222222222222222226","blockNumber":"123460","chainId":"8453","cursorId":"0xc7ef88c914177a735c6757f6e85412a8bbcdad6404a4165a458a1a92eafc4686","logIndex":"6","sourceSetId":"0x95b6a52c8fbf5a0f68120c877461f0e7f75dc191c79a03e7510078f14a55dfe4","transactionIndex":"11"},{"blockHash":"0x2222222222222222222222222222222222222222222222222222222222222227","blockNumber":"123461","chainId":"8453","cursorId":"0xe7071042f8c40f0f7a698a510749ff76bc65c683dcf4914eff7e98d9df5974f9","logIndex":"7","sourceSetId":"0x95b6a52c8fbf5a0f68120c877461f0e7f75dc191c79a03e7510078f14a55dfe4","transactionIndex":"12"},{"blockHash":"0x2222222222222222222222222222222222222222222222222222222222222223","blockNumber":"123457","chainId":"8453","cursorId":"0xf5cd45f8e8fb8d3bdedcfe527bcb21f40d01591729e244bdf263f53f1498169f","logIndex":"3","sourceSetId":"0x95b6a52c8fbf5a0f68120c877461f0e7f75dc191c79a03e7510078f14a55dfe4","transactionIndex":"8"}],"duplicates":[{"kind":"exactDuplicate","observationId":"0x9d958aadf8bf46f989b51e541709a73d21970e7e79643f939c9a0000b50f9a91","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}],"observations":[{"actor":"0x4444444444444444444444444444444444444444","blockHash":"0x2222222222222222222222222222222222222222222222222222222222222222","blockNumber":"123456","canonicalObservationJson":"{\"actor\":\"0x4444444444444444444444444444444444444444\",\"blockHash\":\"0x2222222222222222222222222222222222222222222222222222222222222222\",\"blockNumber\":\"123456\",\"chainId\":\"8453\",\"commitment\":\"0x4122209ff672fc04b2ec3af31ab1af79813971f86de000aa6534038cc79de6b5\",\"cursorId\":\"0x668270c7410fea7c1581c9f4e8b9ba5a5d3eeebb7b5b75ee3a602b096245a21f\",\"emittingContract\":\"0x1111111111111111111111111111111111111111\",\"eventSignature\":\"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43\",\"lifecycleState\":\"finalized\",\"logIndex\":\"2\",\"observationId\":\"0x9d958aadf8bf46f989b51e541709a73d21970e7e79643f939c9a0000b50f9a91\",\"occurredAt\":\"1778640000\",\"parentPulseId\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"pulseId\":\"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"pulseType\":\"1\",\"receiptStatus\":\"success\",\"rootfieldId\":\"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"sequence\":\"1\",\"subject\":\"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"transactionIndex\":\"7\",\"txHash\":\"0x3333333333333333333333333333333333333333333333333333333333333333\",\"uri\":\"ipfs://bafy-flowmemory-example\"}","chainId":"8453","commitment":"0x4122209ff672fc04b2ec3af31ab1af79813971f86de000aa6534038cc79de6b5","cursorId":"0x668270c7410fea7c1581c9f4e8b9ba5a5d3eeebb7b5b75ee3a602b096245a21f","duplicateKind":"unique","emittingContract":"0x1111111111111111111111111111111111111111","eventSignature":"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43","lifecycleState":"finalized","logIndex":"2","observationId":"0x9d958aadf8bf46f989b51e541709a73d21970e7e79643f939c9a0000b50f9a91","occurredAt":"1778640000","parentPulseId":"0x0000000000000000000000000000000000000000000000000000000000000000","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","pulseType":"1","receiptStatus":"success","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","sequence":"1","subject":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","transactionIndex":"7","txHash":"0x3333333333333333333333333333333333333333333333333333333333333333","uri":"ipfs://bafy-flowmemory-example"},{"actor":"0x4444444444444444444444444444444444444444","blockHash":"0x2222222222222222222222222222222222222222222222222222222222222223","blockNumber":"123457","canonicalObservationJson":"{\"actor\":\"0x4444444444444444444444444444444444444444\",\"blockHash\":\"0x2222222222222222222222222222222222222222222222222222222222222223\",\"blockNumber\":\"123457\",\"chainId\":\"8453\",\"commitment\":\"0x69a9f953179a6ce2e08724dd759fb71b08b0759a5900fc0399a753ecf0557df5\",\"cursorId\":\"0xf5cd45f8e8fb8d3bdedcfe527bcb21f40d01591729e244bdf263f53f1498169f\",\"emittingContract\":\"0x1111111111111111111111111111111111111111\",\"eventSignature\":\"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43\",\"lifecycleState\":\"finalized\",\"logIndex\":\"3\",\"observationId\":\"0x49c6cd59d1f1916bc5301308be09c14d64127d1566362272dc5aa201ed53bdea\",\"occurredAt\":\"1778640060\",\"parentPulseId\":\"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"pulseId\":\"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab001\",\"pulseType\":\"2\",\"receiptStatus\":\"success\",\"rootfieldId\":\"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"sequence\":\"2\",\"subject\":\"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1\",\"transactionIndex\":\"8\",\"txHash\":\"0x3333333333333333333333333333333333333333333333333333333333333334\",\"uri\":\"fixture://root-commit-valid\"}","chainId":"8453","commitment":"0x69a9f953179a6ce2e08724dd759fb71b08b0759a5900fc0399a753ecf0557df5","cursorId":"0xf5cd45f8e8fb8d3bdedcfe527bcb21f40d01591729e244bdf263f53f1498169f","duplicateKind":"unique","emittingContract":"0x1111111111111111111111111111111111111111","eventSignature":"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43","lifecycleState":"finalized","logIndex":"3","observationId":"0x49c6cd59d1f1916bc5301308be09c14d64127d1566362272dc5aa201ed53bdea","occurredAt":"1778640060","parentPulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab001","pulseType":"2","receiptStatus":"success","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","sequence":"2","subject":"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1","transactionIndex":"8","txHash":"0x3333333333333333333333333333333333333333333333333333333333333334","uri":"fixture://root-commit-valid"},{"actor":"0x4444444444444444444444444444444444444444","blockHash":"0x2222222222222222222222222222222222222222222222222222222222222222","blockNumber":"123456","canonicalObservationJson":"{\"actor\":\"0x4444444444444444444444444444444444444444\",\"blockHash\":\"0x2222222222222222222222222222222222222222222222222222222222222222\",\"blockNumber\":\"123456\",\"chainId\":\"8453\",\"commitment\":\"0x4122209ff672fc04b2ec3af31ab1af79813971f86de000aa6534038cc79de6b5\",\"cursorId\":\"0x668270c7410fea7c1581c9f4e8b9ba5a5d3eeebb7b5b75ee3a602b096245a21f\",\"emittingContract\":\"0x1111111111111111111111111111111111111111\",\"eventSignature\":\"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43\",\"lifecycleState\":\"finalized\",\"logIndex\":\"2\",\"observationId\":\"0x9d958aadf8bf46f989b51e541709a73d21970e7e79643f939c9a0000b50f9a91\",\"occurredAt\":\"1778640000\",\"parentPulseId\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"pulseId\":\"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"pulseType\":\"1\",\"receiptStatus\":\"success\",\"rootfieldId\":\"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"sequence\":\"1\",\"subject\":\"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"transactionIndex\":\"7\",\"txHash\":\"0x3333333333333333333333333333333333333333333333333333333333333333\",\"uri\":\"ipfs://bafy-flowmemory-example\"}","chainId":"8453","commitment":"0x4122209ff672fc04b2ec3af31ab1af79813971f86de000aa6534038cc79de6b5","cursorId":"0x668270c7410fea7c1581c9f4e8b9ba5a5d3eeebb7b5b75ee3a602b096245a21f","duplicateKind":"exactDuplicate","emittingContract":"0x1111111111111111111111111111111111111111","eventSignature":"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43","lifecycleState":"finalized","logIndex":"2","observationId":"0x9d958aadf8bf46f989b51e541709a73d21970e7e79643f939c9a0000b50f9a91","occurredAt":"1778640000","parentPulseId":"0x0000000000000000000000000000000000000000000000000000000000000000","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","pulseType":"1","receiptStatus":"success","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","sequence":"1","subject":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","transactionIndex":"7","txHash":"0x3333333333333333333333333333333333333333333333333333333333333333","uri":"ipfs://bafy-flowmemory-example"},{"actor":"0x4444444444444444444444444444444444444444","blockHash":"0x2222222222222222222222222222222222222222222222222222222222222224","blockNumber":"123458","canonicalObservationJson":"{\"actor\":\"0x4444444444444444444444444444444444444444\",\"blockHash\":\"0x2222222222222222222222222222222222222222222222222222222222222224\",\"blockNumber\":\"123458\",\"chainId\":\"8453\",\"commitment\":\"0x69a9f953179a6ce2e08724dd759fb71b08b0759a5900fc0399a753ecf0557df5\",\"cursorId\":\"0x69fddcac0f6148d6193cda1a3bdacde7c15a5bf4589831dfcfc0c90062bd05b4\",\"emittingContract\":\"0x1111111111111111111111111111111111111111\",\"eventSignature\":\"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43\",\"lifecycleState\":\"removed\",\"logIndex\":\"4\",\"observationId\":\"0x223d74c971f301d800dd69aa30994bc3fa3089b34b0db1b0a7fc9d4e8d114c79\",\"occurredAt\":\"1778640060\",\"parentPulseId\":\"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"pulseId\":\"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab002\",\"pulseType\":\"2\",\"receiptStatus\":\"success\",\"rootfieldId\":\"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"sequence\":\"2\",\"subject\":\"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1\",\"transactionIndex\":\"9\",\"txHash\":\"0x3333333333333333333333333333333333333333333333333333333333333335\",\"uri\":\"fixture://root-commit-valid\"}","chainId":"8453","commitment":"0x69a9f953179a6ce2e08724dd759fb71b08b0759a5900fc0399a753ecf0557df5","cursorId":"0x69fddcac0f6148d6193cda1a3bdacde7c15a5bf4589831dfcfc0c90062bd05b4","duplicateKind":"unique","emittingContract":"0x1111111111111111111111111111111111111111","eventSignature":"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43","lifecycleState":"removed","logIndex":"4","observationId":"0x223d74c971f301d800dd69aa30994bc3fa3089b34b0db1b0a7fc9d4e8d114c79","occurredAt":"1778640060","parentPulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab002","pulseType":"2","receiptStatus":"success","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","sequence":"2","subject":"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1","transactionIndex":"9","txHash":"0x3333333333333333333333333333333333333333333333333333333333333335","uri":"fixture://root-commit-valid"},{"actor":"0x4444444444444444444444444444444444444444","blockHash":"0x2222222222222222222222222222222222222222222222222222222222222225","blockNumber":"123459","canonicalObservationJson":"{\"actor\":\"0x4444444444444444444444444444444444444444\",\"blockHash\":\"0x2222222222222222222222222222222222222222222222222222222222222225\",\"blockNumber\":\"123459\",\"chainId\":\"8453\",\"commitment\":\"0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc\",\"cursorId\":\"0x9bdea7fca6fd5954e75608037b1b8bb92a2312cd58a10573b40f0312a29285f8\",\"emittingContract\":\"0x1111111111111111111111111111111111111111\",\"eventSignature\":\"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43\",\"lifecycleState\":\"pending\",\"logIndex\":\"5\",\"observationId\":\"0xe4a7065f1578c4a232b41f5984942e9b7b07760ce7dfeba77b38eb03173491df\",\"occurredAt\":\"1778640120\",\"parentPulseId\":\"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"pulseId\":\"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab003\",\"pulseType\":\"2\",\"receiptStatus\":\"success\",\"rootfieldId\":\"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"sequence\":\"3\",\"subject\":\"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1\",\"transactionIndex\":\"10\",\"txHash\":\"0x3333333333333333333333333333333333333333333333333333333333333336\",\"uri\":\"fixture://root-commit-valid\"}","chainId":"8453","commitment":"0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc","cursorId":"0x9bdea7fca6fd5954e75608037b1b8bb92a2312cd58a10573b40f0312a29285f8","duplicateKind":"unique","emittingContract":"0x1111111111111111111111111111111111111111","eventSignature":"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43","lifecycleState":"pending","logIndex":"5","observationId":"0xe4a7065f1578c4a232b41f5984942e9b7b07760ce7dfeba77b38eb03173491df","occurredAt":"1778640120","parentPulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab003","pulseType":"2","receiptStatus":"success","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","sequence":"3","subject":"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1","transactionIndex":"10","txHash":"0x3333333333333333333333333333333333333333333333333333333333333336","uri":"fixture://root-commit-valid"},{"actor":"0x4444444444444444444444444444444444444444","blockHash":"0x2222222222222222222222222222222222222222222222222222222222222226","blockNumber":"123460","canonicalObservationJson":"{\"actor\":\"0x4444444444444444444444444444444444444444\",\"blockHash\":\"0x2222222222222222222222222222222222222222222222222222222222222226\",\"blockNumber\":\"123460\",\"chainId\":\"8453\",\"commitment\":\"0x69a9f953179a6ce2e08724dd759fb71b08b0759a5900fc0399a753ecf0557df5\",\"cursorId\":\"0xc7ef88c914177a735c6757f6e85412a8bbcdad6404a4165a458a1a92eafc4686\",\"emittingContract\":\"0x1111111111111111111111111111111111111111\",\"eventSignature\":\"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43\",\"lifecycleState\":\"pending\",\"logIndex\":\"6\",\"observationId\":\"0x92144fc24c81cdd6319598e6e0c58d84e3f18d9ad4a8be33bc956e32c2ae39f4\",\"occurredAt\":\"1778640180\",\"parentPulseId\":\"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"pulseId\":\"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab004\",\"pulseType\":\"2\",\"receiptStatus\":\"success\",\"rootfieldId\":\"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"sequence\":\"4\",\"subject\":\"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1\",\"transactionIndex\":\"11\",\"txHash\":\"0x3333333333333333333333333333333333333333333333333333333333333337\",\"uri\":\"fixture://missing-artifact\"}","chainId":"8453","commitment":"0x69a9f953179a6ce2e08724dd759fb71b08b0759a5900fc0399a753ecf0557df5","cursorId":"0xc7ef88c914177a735c6757f6e85412a8bbcdad6404a4165a458a1a92eafc4686","duplicateKind":"unique","emittingContract":"0x1111111111111111111111111111111111111111","eventSignature":"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43","lifecycleState":"pending","logIndex":"6","observationId":"0x92144fc24c81cdd6319598e6e0c58d84e3f18d9ad4a8be33bc956e32c2ae39f4","occurredAt":"1778640180","parentPulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab004","pulseType":"2","receiptStatus":"success","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","sequence":"4","subject":"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1","transactionIndex":"11","txHash":"0x3333333333333333333333333333333333333333333333333333333333333337","uri":"fixture://missing-artifact"},{"actor":"0x4444444444444444444444444444444444444444","blockHash":"0x2222222222222222222222222222222222222222222222222222222222222227","blockNumber":"123461","canonicalObservationJson":"{\"actor\":\"0x4444444444444444444444444444444444444444\",\"blockHash\":\"0x2222222222222222222222222222222222222222222222222222222222222227\",\"blockNumber\":\"123461\",\"chainId\":\"8453\",\"commitment\":\"0x69a9f953179a6ce2e08724dd759fb71b08b0759a5900fc0399a753ecf0557df5\",\"cursorId\":\"0xe7071042f8c40f0f7a698a510749ff76bc65c683dcf4914eff7e98d9df5974f9\",\"emittingContract\":\"0x1111111111111111111111111111111111111111\",\"eventSignature\":\"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43\",\"lifecycleState\":\"pending\",\"logIndex\":\"7\",\"observationId\":\"0x5ba8c5a70c814d35482df5f6598e549f83f2d0e27f6e3d25b83eb2a0e1d56a98\",\"occurredAt\":\"1778640240\",\"parentPulseId\":\"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"pulseId\":\"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab005\",\"pulseType\":\"99\",\"receiptStatus\":\"success\",\"rootfieldId\":\"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"sequence\":\"5\",\"subject\":\"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1\",\"transactionIndex\":\"12\",\"txHash\":\"0x3333333333333333333333333333333333333333333333333333333333333338\",\"uri\":\"fixture://unsupported\"}","chainId":"8453","commitment":"0x69a9f953179a6ce2e08724dd759fb71b08b0759a5900fc0399a753ecf0557df5","cursorId":"0xe7071042f8c40f0f7a698a510749ff76bc65c683dcf4914eff7e98d9df5974f9","duplicateKind":"unique","emittingContract":"0x1111111111111111111111111111111111111111","eventSignature":"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43","lifecycleState":"pending","logIndex":"7","observationId":"0x5ba8c5a70c814d35482df5f6598e549f83f2d0e27f6e3d25b83eb2a0e1d56a98","occurredAt":"1778640240","parentPulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab005","pulseType":"99","receiptStatus":"success","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","sequence":"5","subject":"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1","transactionIndex":"12","txHash":"0x3333333333333333333333333333333333333333333333333333333333333338","uri":"fixture://unsupported"}],"pulses":[{"actor":"0x4444444444444444444444444444444444444444","commitment":"0x4122209ff672fc04b2ec3af31ab1af79813971f86de000aa6534038cc79de6b5","observationId":"0x9d958aadf8bf46f989b51e541709a73d21970e7e79643f939c9a0000b50f9a91","occurredAt":"1778640000","parentPulseId":"0x0000000000000000000000000000000000000000000000000000000000000000","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","pulseType":"1","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","sequence":"1","subject":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","uri":"ipfs://bafy-flowmemory-example"},{"actor":"0x4444444444444444444444444444444444444444","commitment":"0x69a9f953179a6ce2e08724dd759fb71b08b0759a5900fc0399a753ecf0557df5","observationId":"0x49c6cd59d1f1916bc5301308be09c14d64127d1566362272dc5aa201ed53bdea","occurredAt":"1778640060","parentPulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab001","pulseType":"2","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","sequence":"2","subject":"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1","uri":"fixture://root-commit-valid"},{"actor":"0x4444444444444444444444444444444444444444","commitment":"0x4122209ff672fc04b2ec3af31ab1af79813971f86de000aa6534038cc79de6b5","observationId":"0x9d958aadf8bf46f989b51e541709a73d21970e7e79643f939c9a0000b50f9a91","occurredAt":"1778640000","parentPulseId":"0x0000000000000000000000000000000000000000000000000000000000000000","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","pulseType":"1","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","sequence":"1","subject":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","uri":"ipfs://bafy-flowmemory-example"},{"actor":"0x4444444444444444444444444444444444444444","commitment":"0x69a9f953179a6ce2e08724dd759fb71b08b0759a5900fc0399a753ecf0557df5","observationId":"0x223d74c971f301d800dd69aa30994bc3fa3089b34b0db1b0a7fc9d4e8d114c79","occurredAt":"1778640060","parentPulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab002","pulseType":"2","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","sequence":"2","subject":"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1","uri":"fixture://root-commit-valid"},{"actor":"0x4444444444444444444444444444444444444444","commitment":"0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc","observationId":"0xe4a7065f1578c4a232b41f5984942e9b7b07760ce7dfeba77b38eb03173491df","occurredAt":"1778640120","parentPulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab003","pulseType":"2","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","sequence":"3","subject":"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1","uri":"fixture://root-commit-valid"},{"actor":"0x4444444444444444444444444444444444444444","commitment":"0x69a9f953179a6ce2e08724dd759fb71b08b0759a5900fc0399a753ecf0557df5","observationId":"0x92144fc24c81cdd6319598e6e0c58d84e3f18d9ad4a8be33bc956e32c2ae39f4","occurredAt":"1778640180","parentPulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab004","pulseType":"2","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","sequence":"4","subject":"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1","uri":"fixture://missing-artifact"},{"actor":"0x4444444444444444444444444444444444444444","commitment":"0x69a9f953179a6ce2e08724dd759fb71b08b0759a5900fc0399a753ecf0557df5","observationId":"0x5ba8c5a70c814d35482df5f6598e549f83f2d0e27f6e3d25b83eb2a0e1d56a98","occurredAt":"1778640240","parentPulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab005","pulseType":"99","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","sequence":"5","subject":"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1","uri":"fixture://unsupported"}],"rejectedLogs":[{"blockHash":"0x2222222222222222222222222222222222222222222222222222222222222228","blockNumber":"123462","chainId":"8453","logIndex":"8","message":"receipt status is reverted","reasonCode":"receipt.reverted","transactionIndex":"13","txHash":"0x3333333333333333333333333333333333333333333333333333333333333339"},{"blockHash":"0x2222222222222222222222222222222222222222222222222222222222222229","blockNumber":"123463","chainId":"8453","logIndex":"9","message":"unsupported event signature: 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff","reasonCode":"log.malformed","transactionIndex":"14","txHash":"0x3333333333333333333333333333333333333333333333333333333333333340"}],"rootfields":[{"firstObservationId":"0x9d958aadf8bf46f989b51e541709a73d21970e7e79643f939c9a0000b50f9a91","latestObservationId":"0x5ba8c5a70c814d35482df5f6598e549f83f2d0e27f6e3d25b83eb2a0e1d56a98","pulseCount":6,"rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"}],"schema":"flowmemory.indexer.state.v0","source":"fixture"}} +{"schema":"flowmemory.indexer.persistence.v0","state":{"batches":[{"cursorCount":7,"observationCount":8,"rejectedLogCount":2,"schema":"flowmemory.indexer.batch.v0","source":"fixture","sourceSetId":"0xafd57bb478d919950dce77a66efd7b7a672a2907ba56c4e54810fa554cab8f88"}],"cursors":[{"blockHash":"0x2222222222222222222222222222222222222222222222222222222222222222","blockNumber":"123456","chainId":"8453","cursorId":"0x40aa916686b08058c379bcf17b75e34dd0aeac21410fbcd87eb2616b3e960584","logIndex":"2","sourceSetId":"0xafd57bb478d919950dce77a66efd7b7a672a2907ba56c4e54810fa554cab8f88","transactionIndex":"7"},{"blockHash":"0x2222222222222222222222222222222222222222222222222222222222222226","blockNumber":"123460","chainId":"8453","cursorId":"0x4d24ca0c21db1d275798b2d97af1943227a534953763586ee8247c61eb66a7ec","logIndex":"6","sourceSetId":"0xafd57bb478d919950dce77a66efd7b7a672a2907ba56c4e54810fa554cab8f88","transactionIndex":"11"},{"blockHash":"0x2222222222222222222222222222222222222222222222222222222222222223","blockNumber":"123457","chainId":"8453","cursorId":"0x55101923cb52836ec838db893181593cc123ee59f787a3db43772e87777a023b","logIndex":"3","sourceSetId":"0xafd57bb478d919950dce77a66efd7b7a672a2907ba56c4e54810fa554cab8f88","transactionIndex":"8"},{"blockHash":"0x2222222222222222222222222222222222222222222222222222222222222227","blockNumber":"123461","chainId":"8453","cursorId":"0x900b2701cc193396e418ce0af071bdded26364eac5399946a6e9745decf46e5a","logIndex":"7","sourceSetId":"0xafd57bb478d919950dce77a66efd7b7a672a2907ba56c4e54810fa554cab8f88","transactionIndex":"12"},{"blockHash":"0x2222222222222222222222222222222222222222222222222222222222222225","blockNumber":"123459","chainId":"8453","cursorId":"0xb8db31a7a1ddb0e07d013a615c3ef49b2cba42cdcebe2a2a06f6e4a22eb9fa40","logIndex":"5","sourceSetId":"0xafd57bb478d919950dce77a66efd7b7a672a2907ba56c4e54810fa554cab8f88","transactionIndex":"10"},{"blockHash":"0x2222222222222222222222222222222222222222222222222222222222222224","blockNumber":"123458","chainId":"8453","cursorId":"0xf494cda844fee1ba70247a5c5853b83bc417661fcfc92679311e8ba56708f5f7","logIndex":"4","sourceSetId":"0xafd57bb478d919950dce77a66efd7b7a672a2907ba56c4e54810fa554cab8f88","transactionIndex":"9"},{"blockHash":"0x2222222222222222222222222222222222222222222222222222222222222230","blockNumber":"123457","chainId":"8453","cursorId":"0xfcfa01e3a2654fbc1670757fb640c0510a92173bfde46ab8fba809b31a3de959","logIndex":"10","sourceSetId":"0xafd57bb478d919950dce77a66efd7b7a672a2907ba56c4e54810fa554cab8f88","transactionIndex":"15"}],"duplicates":[{"kind":"exactDuplicate","observationId":"0x9d958aadf8bf46f989b51e541709a73d21970e7e79643f939c9a0000b50f9a91","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}],"observations":[{"actor":"0x4444444444444444444444444444444444444444","blockHash":"0x2222222222222222222222222222222222222222222222222222222222222222","blockNumber":"123456","canonicalObservationJson":"{\"actor\":\"0x4444444444444444444444444444444444444444\",\"blockHash\":\"0x2222222222222222222222222222222222222222222222222222222222222222\",\"blockNumber\":\"123456\",\"chainId\":\"8453\",\"commitment\":\"0x4122209ff672fc04b2ec3af31ab1af79813971f86de000aa6534038cc79de6b5\",\"cursorId\":\"0x40aa916686b08058c379bcf17b75e34dd0aeac21410fbcd87eb2616b3e960584\",\"emittingContract\":\"0x1111111111111111111111111111111111111111\",\"eventSignature\":\"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43\",\"lifecycleState\":\"finalized\",\"logIndex\":\"2\",\"observationId\":\"0x9d958aadf8bf46f989b51e541709a73d21970e7e79643f939c9a0000b50f9a91\",\"occurredAt\":\"1778640000\",\"parentPulseId\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"pulseId\":\"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"pulseType\":\"1\",\"receiptStatus\":\"success\",\"rootfieldId\":\"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"sequence\":\"1\",\"subject\":\"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"transactionIndex\":\"7\",\"txHash\":\"0x3333333333333333333333333333333333333333333333333333333333333333\",\"uri\":\"ipfs://bafy-flowmemory-example\"}","chainId":"8453","commitment":"0x4122209ff672fc04b2ec3af31ab1af79813971f86de000aa6534038cc79de6b5","cursorId":"0x40aa916686b08058c379bcf17b75e34dd0aeac21410fbcd87eb2616b3e960584","duplicateKind":"unique","emittingContract":"0x1111111111111111111111111111111111111111","eventSignature":"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43","lifecycleState":"finalized","logIndex":"2","observationId":"0x9d958aadf8bf46f989b51e541709a73d21970e7e79643f939c9a0000b50f9a91","occurredAt":"1778640000","parentPulseId":"0x0000000000000000000000000000000000000000000000000000000000000000","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","pulseType":"1","receiptStatus":"success","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","sequence":"1","subject":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","transactionIndex":"7","txHash":"0x3333333333333333333333333333333333333333333333333333333333333333","uri":"ipfs://bafy-flowmemory-example"},{"actor":"0x4444444444444444444444444444444444444444","blockHash":"0x2222222222222222222222222222222222222222222222222222222222222223","blockNumber":"123457","canonicalObservationJson":"{\"actor\":\"0x4444444444444444444444444444444444444444\",\"blockHash\":\"0x2222222222222222222222222222222222222222222222222222222222222223\",\"blockNumber\":\"123457\",\"chainId\":\"8453\",\"commitment\":\"0x69a9f953179a6ce2e08724dd759fb71b08b0759a5900fc0399a753ecf0557df5\",\"cursorId\":\"0x55101923cb52836ec838db893181593cc123ee59f787a3db43772e87777a023b\",\"emittingContract\":\"0x1111111111111111111111111111111111111111\",\"eventSignature\":\"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43\",\"lifecycleState\":\"finalized\",\"logIndex\":\"3\",\"observationId\":\"0x49c6cd59d1f1916bc5301308be09c14d64127d1566362272dc5aa201ed53bdea\",\"occurredAt\":\"1778640060\",\"parentPulseId\":\"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"pulseId\":\"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab001\",\"pulseType\":\"2\",\"receiptStatus\":\"success\",\"rootfieldId\":\"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"sequence\":\"2\",\"subject\":\"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1\",\"transactionIndex\":\"8\",\"txHash\":\"0x3333333333333333333333333333333333333333333333333333333333333334\",\"uri\":\"fixture://root-commit-valid\"}","chainId":"8453","commitment":"0x69a9f953179a6ce2e08724dd759fb71b08b0759a5900fc0399a753ecf0557df5","cursorId":"0x55101923cb52836ec838db893181593cc123ee59f787a3db43772e87777a023b","duplicateKind":"unique","emittingContract":"0x1111111111111111111111111111111111111111","eventSignature":"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43","lifecycleState":"finalized","logIndex":"3","observationId":"0x49c6cd59d1f1916bc5301308be09c14d64127d1566362272dc5aa201ed53bdea","occurredAt":"1778640060","parentPulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab001","pulseType":"2","receiptStatus":"success","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","sequence":"2","subject":"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1","transactionIndex":"8","txHash":"0x3333333333333333333333333333333333333333333333333333333333333334","uri":"fixture://root-commit-valid"},{"actor":"0x4444444444444444444444444444444444444444","blockHash":"0x2222222222222222222222222222222222222222222222222222222222222230","blockNumber":"123457","canonicalObservationJson":"{\"actor\":\"0x4444444444444444444444444444444444444444\",\"blockHash\":\"0x2222222222222222222222222222222222222222222222222222222222222230\",\"blockNumber\":\"123457\",\"chainId\":\"8453\",\"commitment\":\"0x8fabbad2f4aae2b67e5dcacc7cd425b7860d4d4453488c6c9770196b5009d795\",\"cursorId\":\"0xfcfa01e3a2654fbc1670757fb640c0510a92173bfde46ab8fba809b31a3de959\",\"emittingContract\":\"0x2222222222222222222222222222222222222222\",\"eventSignature\":\"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43\",\"lifecycleState\":\"finalized\",\"logIndex\":\"10\",\"observationId\":\"0x62e6ea1899a4dfd0d0a6cf6010ffb98983db20a9755257c8288602d059aa169e\",\"occurredAt\":\"1778648420\",\"parentPulseId\":\"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab001\",\"pulseId\":\"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab008\",\"pulseType\":\"4\",\"receiptStatus\":\"success\",\"rootfieldId\":\"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"sequence\":\"6\",\"subject\":\"0x1212121212121212121212121212121212121212121212121212121212121212\",\"transactionIndex\":\"15\",\"txHash\":\"0x3333333333333333333333333333333333333333333333333333333333333341\",\"uri\":\"fixture://swap-memory-valid\"}","chainId":"8453","commitment":"0x8fabbad2f4aae2b67e5dcacc7cd425b7860d4d4453488c6c9770196b5009d795","cursorId":"0xfcfa01e3a2654fbc1670757fb640c0510a92173bfde46ab8fba809b31a3de959","duplicateKind":"unique","emittingContract":"0x2222222222222222222222222222222222222222","eventSignature":"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43","lifecycleState":"finalized","logIndex":"10","observationId":"0x62e6ea1899a4dfd0d0a6cf6010ffb98983db20a9755257c8288602d059aa169e","occurredAt":"1778648420","parentPulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab001","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab008","pulseType":"4","receiptStatus":"success","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","sequence":"6","subject":"0x1212121212121212121212121212121212121212121212121212121212121212","transactionIndex":"15","txHash":"0x3333333333333333333333333333333333333333333333333333333333333341","uri":"fixture://swap-memory-valid"},{"actor":"0x4444444444444444444444444444444444444444","blockHash":"0x2222222222222222222222222222222222222222222222222222222222222222","blockNumber":"123456","canonicalObservationJson":"{\"actor\":\"0x4444444444444444444444444444444444444444\",\"blockHash\":\"0x2222222222222222222222222222222222222222222222222222222222222222\",\"blockNumber\":\"123456\",\"chainId\":\"8453\",\"commitment\":\"0x4122209ff672fc04b2ec3af31ab1af79813971f86de000aa6534038cc79de6b5\",\"cursorId\":\"0x40aa916686b08058c379bcf17b75e34dd0aeac21410fbcd87eb2616b3e960584\",\"emittingContract\":\"0x1111111111111111111111111111111111111111\",\"eventSignature\":\"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43\",\"lifecycleState\":\"finalized\",\"logIndex\":\"2\",\"observationId\":\"0x9d958aadf8bf46f989b51e541709a73d21970e7e79643f939c9a0000b50f9a91\",\"occurredAt\":\"1778640000\",\"parentPulseId\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"pulseId\":\"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"pulseType\":\"1\",\"receiptStatus\":\"success\",\"rootfieldId\":\"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"sequence\":\"1\",\"subject\":\"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"transactionIndex\":\"7\",\"txHash\":\"0x3333333333333333333333333333333333333333333333333333333333333333\",\"uri\":\"ipfs://bafy-flowmemory-example\"}","chainId":"8453","commitment":"0x4122209ff672fc04b2ec3af31ab1af79813971f86de000aa6534038cc79de6b5","cursorId":"0x40aa916686b08058c379bcf17b75e34dd0aeac21410fbcd87eb2616b3e960584","duplicateKind":"exactDuplicate","emittingContract":"0x1111111111111111111111111111111111111111","eventSignature":"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43","lifecycleState":"finalized","logIndex":"2","observationId":"0x9d958aadf8bf46f989b51e541709a73d21970e7e79643f939c9a0000b50f9a91","occurredAt":"1778640000","parentPulseId":"0x0000000000000000000000000000000000000000000000000000000000000000","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","pulseType":"1","receiptStatus":"success","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","sequence":"1","subject":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","transactionIndex":"7","txHash":"0x3333333333333333333333333333333333333333333333333333333333333333","uri":"ipfs://bafy-flowmemory-example"},{"actor":"0x4444444444444444444444444444444444444444","blockHash":"0x2222222222222222222222222222222222222222222222222222222222222224","blockNumber":"123458","canonicalObservationJson":"{\"actor\":\"0x4444444444444444444444444444444444444444\",\"blockHash\":\"0x2222222222222222222222222222222222222222222222222222222222222224\",\"blockNumber\":\"123458\",\"chainId\":\"8453\",\"commitment\":\"0x69a9f953179a6ce2e08724dd759fb71b08b0759a5900fc0399a753ecf0557df5\",\"cursorId\":\"0xf494cda844fee1ba70247a5c5853b83bc417661fcfc92679311e8ba56708f5f7\",\"emittingContract\":\"0x1111111111111111111111111111111111111111\",\"eventSignature\":\"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43\",\"lifecycleState\":\"removed\",\"logIndex\":\"4\",\"observationId\":\"0x223d74c971f301d800dd69aa30994bc3fa3089b34b0db1b0a7fc9d4e8d114c79\",\"occurredAt\":\"1778640060\",\"parentPulseId\":\"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"pulseId\":\"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab002\",\"pulseType\":\"2\",\"receiptStatus\":\"success\",\"rootfieldId\":\"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"sequence\":\"2\",\"subject\":\"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1\",\"transactionIndex\":\"9\",\"txHash\":\"0x3333333333333333333333333333333333333333333333333333333333333335\",\"uri\":\"fixture://root-commit-valid\"}","chainId":"8453","commitment":"0x69a9f953179a6ce2e08724dd759fb71b08b0759a5900fc0399a753ecf0557df5","cursorId":"0xf494cda844fee1ba70247a5c5853b83bc417661fcfc92679311e8ba56708f5f7","duplicateKind":"unique","emittingContract":"0x1111111111111111111111111111111111111111","eventSignature":"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43","lifecycleState":"removed","logIndex":"4","observationId":"0x223d74c971f301d800dd69aa30994bc3fa3089b34b0db1b0a7fc9d4e8d114c79","occurredAt":"1778640060","parentPulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab002","pulseType":"2","receiptStatus":"success","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","sequence":"2","subject":"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1","transactionIndex":"9","txHash":"0x3333333333333333333333333333333333333333333333333333333333333335","uri":"fixture://root-commit-valid"},{"actor":"0x4444444444444444444444444444444444444444","blockHash":"0x2222222222222222222222222222222222222222222222222222222222222225","blockNumber":"123459","canonicalObservationJson":"{\"actor\":\"0x4444444444444444444444444444444444444444\",\"blockHash\":\"0x2222222222222222222222222222222222222222222222222222222222222225\",\"blockNumber\":\"123459\",\"chainId\":\"8453\",\"commitment\":\"0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc\",\"cursorId\":\"0xb8db31a7a1ddb0e07d013a615c3ef49b2cba42cdcebe2a2a06f6e4a22eb9fa40\",\"emittingContract\":\"0x1111111111111111111111111111111111111111\",\"eventSignature\":\"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43\",\"lifecycleState\":\"pending\",\"logIndex\":\"5\",\"observationId\":\"0xe4a7065f1578c4a232b41f5984942e9b7b07760ce7dfeba77b38eb03173491df\",\"occurredAt\":\"1778640120\",\"parentPulseId\":\"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"pulseId\":\"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab003\",\"pulseType\":\"2\",\"receiptStatus\":\"success\",\"rootfieldId\":\"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"sequence\":\"3\",\"subject\":\"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1\",\"transactionIndex\":\"10\",\"txHash\":\"0x3333333333333333333333333333333333333333333333333333333333333336\",\"uri\":\"fixture://root-commit-valid\"}","chainId":"8453","commitment":"0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc","cursorId":"0xb8db31a7a1ddb0e07d013a615c3ef49b2cba42cdcebe2a2a06f6e4a22eb9fa40","duplicateKind":"unique","emittingContract":"0x1111111111111111111111111111111111111111","eventSignature":"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43","lifecycleState":"pending","logIndex":"5","observationId":"0xe4a7065f1578c4a232b41f5984942e9b7b07760ce7dfeba77b38eb03173491df","occurredAt":"1778640120","parentPulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab003","pulseType":"2","receiptStatus":"success","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","sequence":"3","subject":"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1","transactionIndex":"10","txHash":"0x3333333333333333333333333333333333333333333333333333333333333336","uri":"fixture://root-commit-valid"},{"actor":"0x4444444444444444444444444444444444444444","blockHash":"0x2222222222222222222222222222222222222222222222222222222222222226","blockNumber":"123460","canonicalObservationJson":"{\"actor\":\"0x4444444444444444444444444444444444444444\",\"blockHash\":\"0x2222222222222222222222222222222222222222222222222222222222222226\",\"blockNumber\":\"123460\",\"chainId\":\"8453\",\"commitment\":\"0x69a9f953179a6ce2e08724dd759fb71b08b0759a5900fc0399a753ecf0557df5\",\"cursorId\":\"0x4d24ca0c21db1d275798b2d97af1943227a534953763586ee8247c61eb66a7ec\",\"emittingContract\":\"0x1111111111111111111111111111111111111111\",\"eventSignature\":\"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43\",\"lifecycleState\":\"pending\",\"logIndex\":\"6\",\"observationId\":\"0x92144fc24c81cdd6319598e6e0c58d84e3f18d9ad4a8be33bc956e32c2ae39f4\",\"occurredAt\":\"1778640180\",\"parentPulseId\":\"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"pulseId\":\"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab004\",\"pulseType\":\"2\",\"receiptStatus\":\"success\",\"rootfieldId\":\"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"sequence\":\"4\",\"subject\":\"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1\",\"transactionIndex\":\"11\",\"txHash\":\"0x3333333333333333333333333333333333333333333333333333333333333337\",\"uri\":\"fixture://missing-artifact\"}","chainId":"8453","commitment":"0x69a9f953179a6ce2e08724dd759fb71b08b0759a5900fc0399a753ecf0557df5","cursorId":"0x4d24ca0c21db1d275798b2d97af1943227a534953763586ee8247c61eb66a7ec","duplicateKind":"unique","emittingContract":"0x1111111111111111111111111111111111111111","eventSignature":"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43","lifecycleState":"pending","logIndex":"6","observationId":"0x92144fc24c81cdd6319598e6e0c58d84e3f18d9ad4a8be33bc956e32c2ae39f4","occurredAt":"1778640180","parentPulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab004","pulseType":"2","receiptStatus":"success","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","sequence":"4","subject":"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1","transactionIndex":"11","txHash":"0x3333333333333333333333333333333333333333333333333333333333333337","uri":"fixture://missing-artifact"},{"actor":"0x4444444444444444444444444444444444444444","blockHash":"0x2222222222222222222222222222222222222222222222222222222222222227","blockNumber":"123461","canonicalObservationJson":"{\"actor\":\"0x4444444444444444444444444444444444444444\",\"blockHash\":\"0x2222222222222222222222222222222222222222222222222222222222222227\",\"blockNumber\":\"123461\",\"chainId\":\"8453\",\"commitment\":\"0x69a9f953179a6ce2e08724dd759fb71b08b0759a5900fc0399a753ecf0557df5\",\"cursorId\":\"0x900b2701cc193396e418ce0af071bdded26364eac5399946a6e9745decf46e5a\",\"emittingContract\":\"0x1111111111111111111111111111111111111111\",\"eventSignature\":\"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43\",\"lifecycleState\":\"pending\",\"logIndex\":\"7\",\"observationId\":\"0x5ba8c5a70c814d35482df5f6598e549f83f2d0e27f6e3d25b83eb2a0e1d56a98\",\"occurredAt\":\"1778640240\",\"parentPulseId\":\"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"pulseId\":\"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab005\",\"pulseType\":\"99\",\"receiptStatus\":\"success\",\"rootfieldId\":\"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"sequence\":\"5\",\"subject\":\"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1\",\"transactionIndex\":\"12\",\"txHash\":\"0x3333333333333333333333333333333333333333333333333333333333333338\",\"uri\":\"fixture://unsupported\"}","chainId":"8453","commitment":"0x69a9f953179a6ce2e08724dd759fb71b08b0759a5900fc0399a753ecf0557df5","cursorId":"0x900b2701cc193396e418ce0af071bdded26364eac5399946a6e9745decf46e5a","duplicateKind":"unique","emittingContract":"0x1111111111111111111111111111111111111111","eventSignature":"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43","lifecycleState":"pending","logIndex":"7","observationId":"0x5ba8c5a70c814d35482df5f6598e549f83f2d0e27f6e3d25b83eb2a0e1d56a98","occurredAt":"1778640240","parentPulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab005","pulseType":"99","receiptStatus":"success","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","sequence":"5","subject":"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1","transactionIndex":"12","txHash":"0x3333333333333333333333333333333333333333333333333333333333333338","uri":"fixture://unsupported"}],"pulses":[{"actor":"0x4444444444444444444444444444444444444444","commitment":"0x4122209ff672fc04b2ec3af31ab1af79813971f86de000aa6534038cc79de6b5","observationId":"0x9d958aadf8bf46f989b51e541709a73d21970e7e79643f939c9a0000b50f9a91","occurredAt":"1778640000","parentPulseId":"0x0000000000000000000000000000000000000000000000000000000000000000","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","pulseType":"1","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","sequence":"1","subject":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","uri":"ipfs://bafy-flowmemory-example"},{"actor":"0x4444444444444444444444444444444444444444","commitment":"0x69a9f953179a6ce2e08724dd759fb71b08b0759a5900fc0399a753ecf0557df5","observationId":"0x49c6cd59d1f1916bc5301308be09c14d64127d1566362272dc5aa201ed53bdea","occurredAt":"1778640060","parentPulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab001","pulseType":"2","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","sequence":"2","subject":"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1","uri":"fixture://root-commit-valid"},{"actor":"0x4444444444444444444444444444444444444444","commitment":"0x8fabbad2f4aae2b67e5dcacc7cd425b7860d4d4453488c6c9770196b5009d795","observationId":"0x62e6ea1899a4dfd0d0a6cf6010ffb98983db20a9755257c8288602d059aa169e","occurredAt":"1778648420","parentPulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab001","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab008","pulseType":"4","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","sequence":"6","subject":"0x1212121212121212121212121212121212121212121212121212121212121212","uri":"fixture://swap-memory-valid"},{"actor":"0x4444444444444444444444444444444444444444","commitment":"0x4122209ff672fc04b2ec3af31ab1af79813971f86de000aa6534038cc79de6b5","observationId":"0x9d958aadf8bf46f989b51e541709a73d21970e7e79643f939c9a0000b50f9a91","occurredAt":"1778640000","parentPulseId":"0x0000000000000000000000000000000000000000000000000000000000000000","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","pulseType":"1","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","sequence":"1","subject":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","uri":"ipfs://bafy-flowmemory-example"},{"actor":"0x4444444444444444444444444444444444444444","commitment":"0x69a9f953179a6ce2e08724dd759fb71b08b0759a5900fc0399a753ecf0557df5","observationId":"0x223d74c971f301d800dd69aa30994bc3fa3089b34b0db1b0a7fc9d4e8d114c79","occurredAt":"1778640060","parentPulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab002","pulseType":"2","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","sequence":"2","subject":"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1","uri":"fixture://root-commit-valid"},{"actor":"0x4444444444444444444444444444444444444444","commitment":"0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc","observationId":"0xe4a7065f1578c4a232b41f5984942e9b7b07760ce7dfeba77b38eb03173491df","occurredAt":"1778640120","parentPulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab003","pulseType":"2","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","sequence":"3","subject":"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1","uri":"fixture://root-commit-valid"},{"actor":"0x4444444444444444444444444444444444444444","commitment":"0x69a9f953179a6ce2e08724dd759fb71b08b0759a5900fc0399a753ecf0557df5","observationId":"0x92144fc24c81cdd6319598e6e0c58d84e3f18d9ad4a8be33bc956e32c2ae39f4","occurredAt":"1778640180","parentPulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab004","pulseType":"2","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","sequence":"4","subject":"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1","uri":"fixture://missing-artifact"},{"actor":"0x4444444444444444444444444444444444444444","commitment":"0x69a9f953179a6ce2e08724dd759fb71b08b0759a5900fc0399a753ecf0557df5","observationId":"0x5ba8c5a70c814d35482df5f6598e549f83f2d0e27f6e3d25b83eb2a0e1d56a98","occurredAt":"1778640240","parentPulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab005","pulseType":"99","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","sequence":"5","subject":"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1","uri":"fixture://unsupported"}],"rejectedLogs":[{"blockHash":"0x2222222222222222222222222222222222222222222222222222222222222228","blockNumber":"123462","chainId":"8453","logIndex":"8","message":"receipt status is reverted","reasonCode":"receipt.reverted","transactionIndex":"13","txHash":"0x3333333333333333333333333333333333333333333333333333333333333339"},{"blockHash":"0x2222222222222222222222222222222222222222222222222222222222222229","blockNumber":"123463","chainId":"8453","logIndex":"9","message":"unsupported event signature: 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff","reasonCode":"log.malformed","transactionIndex":"14","txHash":"0x3333333333333333333333333333333333333333333333333333333333333340"}],"rootfields":[{"firstObservationId":"0x9d958aadf8bf46f989b51e541709a73d21970e7e79643f939c9a0000b50f9a91","latestObservationId":"0x5ba8c5a70c814d35482df5f6598e549f83f2d0e27f6e3d25b83eb2a0e1d56a98","pulseCount":7,"rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"}],"schema":"flowmemory.indexer.state.v0","source":"fixture"}} diff --git a/services/indexer/test/indexer.test.ts b/services/indexer/test/indexer.test.ts index 5aa046a8..ce331b36 100644 --- a/services/indexer/test/indexer.test.ts +++ b/services/indexer/test/indexer.test.ts @@ -35,10 +35,10 @@ test("ingests receipt fixtures and rejects reverted or malformed logs cleanly", finalizedBlockNumber: "123458", }); - assert.equal(state.observations.length, 7); - assert.equal(state.cursors.length, 6); - assert.equal(state.batches[0].observationCount, 7); - assert.equal(state.batches[0].cursorCount, 6); + assert.equal(state.observations.length, 8); + assert.equal(state.cursors.length, 7); + assert.equal(state.batches[0].observationCount, 8); + assert.equal(state.batches[0].cursorCount, 7); assert.equal(state.rejectedLogs.length, 2); assert.deepEqual(state.rejectedLogs.map((log) => log.reasonCode), ["receipt.reverted", "log.malformed"]); assert.equal(state.duplicates.length, 1); @@ -82,7 +82,7 @@ test("persists deterministic indexer state JSON", () => { assert.equal(firstWrite, secondWrite); assert.equal(persisted.schema, "flowmemory.indexer.persistence.v0"); - assert.equal(persisted.state.observations.length, 7); + assert.equal(persisted.state.observations.length, 8); } finally { rmSync(dir, { recursive: true, force: true }); } diff --git a/services/verifier/README.md b/services/verifier/README.md index a7811c4d..432b9f1e 100644 --- a/services/verifier/README.md +++ b/services/verifier/README.md @@ -88,6 +88,13 @@ subject == root commitment == keccak256(abi.encode(root, artifactCommitment)) ``` +For `SWAP_MEMORY_SIGNAL` (`pulseType = 4`): + +```text +subject == poolId +commitment == keccak256(abi.encode(poolId, hookDataHash, memoryRoot)) +``` + Unsupported pulse types return `unsupported`. Missing artifacts return `unresolved`. Subject or commitment mismatches return `invalid`. ## Reports diff --git a/services/verifier/fixtures/artifacts.json b/services/verifier/fixtures/artifacts.json index 56040f8e..57a62d68 100644 --- a/services/verifier/fixtures/artifacts.json +++ b/services/verifier/fixtures/artifacts.json @@ -10,6 +10,12 @@ "kind": "root-commitment", "root": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1", "artifactCommitment": "0x9999999999999999999999999999999999999999999999999999999999999999" + }, + "fixture://swap-memory-valid": { + "kind": "swap-memory-signal", + "poolId": "0x1212121212121212121212121212121212121212121212121212121212121212", + "hookDataHash": "0x3434343434343434343434343434343434343434343434343434343434343434", + "memoryRoot": "0x5656565656565656565656565656565656565656565656565656565656565656" } } } diff --git a/services/verifier/out/reports.json b/services/verifier/out/reports.json index f813d04d..6af28b74 100644 --- a/services/verifier/out/reports.json +++ b/services/verifier/out/reports.json @@ -1 +1 @@ -{"reports":[{"reportCore":{"checks":[{"id":"observation.decoded","passed":true},{"id":"subject.rootfield_matches","passed":true},{"id":"commitment.rootfield_registration","passed":true}],"evidenceRefs":[{"kind":"rootfield-registration","uri":"ipfs://bafy-flowmemory-example"}],"flowPulse":{"actor":"0x4444444444444444444444444444444444444444","commitment":"0x4122209ff672fc04b2ec3af31ab1af79813971f86de000aa6534038cc79de6b5","occurredAt":"1778640000","parentPulseId":"0x0000000000000000000000000000000000000000000000000000000000000000","pulseType":"1","sequence":"1","subject":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","uri":"ipfs://bafy-flowmemory-example"},"observation":{"blockHash":"0x2222222222222222222222222222222222222222222222222222222222222222","blockNumber":"123456","chainId":"8453","emittingContract":"0x1111111111111111111111111111111111111111","eventSignature":"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43","logIndex":"2","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","transactionIndex":"7","txHash":"0x3333333333333333333333333333333333333333333333333333333333333333"},"observationId":"0x9d958aadf8bf46f989b51e541709a73d21970e7e79643f939c9a0000b50f9a91","reasonCodes":[],"resolverPolicyId":"flowmemory.resolver.policy.v0.fixture","schema":"flowmemory.verifier.report.v0","status":"valid","verifierSpecVersion":"0"},"reportDigest":"0x03af9720a87f7d72ce2ce95c1d04d50134649da51ace802d88818ad67fb2ebb3","reportId":"0x03af9720a87f7d72ce2ce95c1d04d50134649da51ace802d88818ad67fb2ebb3"},{"reportCore":{"checks":[{"id":"observation.decoded","passed":true},{"id":"subject.root_matches","passed":true},{"id":"commitment.root","passed":true}],"evidenceRefs":[{"kind":"root-commitment","uri":"fixture://root-commit-valid"}],"flowPulse":{"actor":"0x4444444444444444444444444444444444444444","commitment":"0x69a9f953179a6ce2e08724dd759fb71b08b0759a5900fc0399a753ecf0557df5","occurredAt":"1778640060","parentPulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","pulseType":"2","sequence":"2","subject":"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1","uri":"fixture://root-commit-valid"},"observation":{"blockHash":"0x2222222222222222222222222222222222222222222222222222222222222223","blockNumber":"123457","chainId":"8453","emittingContract":"0x1111111111111111111111111111111111111111","eventSignature":"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43","logIndex":"3","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab001","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","transactionIndex":"8","txHash":"0x3333333333333333333333333333333333333333333333333333333333333334"},"observationId":"0x49c6cd59d1f1916bc5301308be09c14d64127d1566362272dc5aa201ed53bdea","reasonCodes":[],"resolverPolicyId":"flowmemory.resolver.policy.v0.fixture","schema":"flowmemory.verifier.report.v0","status":"valid","verifierSpecVersion":"0"},"reportDigest":"0xf1dfced6038cfa79928e1888e611da6bb05e7a14393cb20e2d5a5cb90c825c35","reportId":"0xf1dfced6038cfa79928e1888e611da6bb05e7a14393cb20e2d5a5cb90c825c35"},{"reportCore":{"checks":[{"id":"observation.decoded","passed":true},{"id":"subject.rootfield_matches","passed":true},{"id":"commitment.rootfield_registration","passed":true}],"evidenceRefs":[{"kind":"rootfield-registration","uri":"ipfs://bafy-flowmemory-example"}],"flowPulse":{"actor":"0x4444444444444444444444444444444444444444","commitment":"0x4122209ff672fc04b2ec3af31ab1af79813971f86de000aa6534038cc79de6b5","occurredAt":"1778640000","parentPulseId":"0x0000000000000000000000000000000000000000000000000000000000000000","pulseType":"1","sequence":"1","subject":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","uri":"ipfs://bafy-flowmemory-example"},"observation":{"blockHash":"0x2222222222222222222222222222222222222222222222222222222222222222","blockNumber":"123456","chainId":"8453","emittingContract":"0x1111111111111111111111111111111111111111","eventSignature":"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43","logIndex":"2","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","transactionIndex":"7","txHash":"0x3333333333333333333333333333333333333333333333333333333333333333"},"observationId":"0x9d958aadf8bf46f989b51e541709a73d21970e7e79643f939c9a0000b50f9a91","reasonCodes":[],"resolverPolicyId":"flowmemory.resolver.policy.v0.fixture","schema":"flowmemory.verifier.report.v0","status":"valid","verifierSpecVersion":"0"},"reportDigest":"0x03af9720a87f7d72ce2ce95c1d04d50134649da51ace802d88818ad67fb2ebb3","reportId":"0x03af9720a87f7d72ce2ce95c1d04d50134649da51ace802d88818ad67fb2ebb3"},{"reportCore":{"checks":[{"id":"observation.decoded","passed":true}],"evidenceRefs":[],"flowPulse":{"actor":"0x4444444444444444444444444444444444444444","commitment":"0x69a9f953179a6ce2e08724dd759fb71b08b0759a5900fc0399a753ecf0557df5","occurredAt":"1778640060","parentPulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","pulseType":"2","sequence":"2","subject":"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1","uri":"fixture://root-commit-valid"},"observation":{"blockHash":"0x2222222222222222222222222222222222222222222222222222222222222224","blockNumber":"123458","chainId":"8453","emittingContract":"0x1111111111111111111111111111111111111111","eventSignature":"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43","logIndex":"4","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab002","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","transactionIndex":"9","txHash":"0x3333333333333333333333333333333333333333333333333333333333333335"},"observationId":"0x223d74c971f301d800dd69aa30994bc3fa3089b34b0db1b0a7fc9d4e8d114c79","reasonCodes":["observation.reorged"],"resolverPolicyId":"flowmemory.resolver.policy.v0.fixture","schema":"flowmemory.verifier.report.v0","status":"reorged","verifierSpecVersion":"0"},"reportDigest":"0xb7cef189a48b8c2697603c3704a52bb3a0322b375fdbea68d2bbbadb65a92ec1","reportId":"0xb7cef189a48b8c2697603c3704a52bb3a0322b375fdbea68d2bbbadb65a92ec1"},{"reportCore":{"checks":[{"id":"observation.decoded","passed":true},{"id":"subject.root_matches","passed":true},{"id":"commitment.root","passed":false}],"evidenceRefs":[{"kind":"root-commitment","uri":"fixture://root-commit-valid"}],"flowPulse":{"actor":"0x4444444444444444444444444444444444444444","commitment":"0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc","occurredAt":"1778640120","parentPulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","pulseType":"2","sequence":"3","subject":"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1","uri":"fixture://root-commit-valid"},"observation":{"blockHash":"0x2222222222222222222222222222222222222222222222222222222222222225","blockNumber":"123459","chainId":"8453","emittingContract":"0x1111111111111111111111111111111111111111","eventSignature":"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43","logIndex":"5","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab003","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","transactionIndex":"10","txHash":"0x3333333333333333333333333333333333333333333333333333333333333336"},"observationId":"0xe4a7065f1578c4a232b41f5984942e9b7b07760ce7dfeba77b38eb03173491df","reasonCodes":["commitment.mismatch"],"resolverPolicyId":"flowmemory.resolver.policy.v0.fixture","schema":"flowmemory.verifier.report.v0","status":"invalid","verifierSpecVersion":"0"},"reportDigest":"0xabc22e2203a9404c832aa792f7a569e17e599078fc9ebacf2c1307bbf6f1d7bb","reportId":"0xabc22e2203a9404c832aa792f7a569e17e599078fc9ebacf2c1307bbf6f1d7bb"},{"reportCore":{"checks":[{"id":"observation.decoded","passed":true}],"evidenceRefs":[],"flowPulse":{"actor":"0x4444444444444444444444444444444444444444","commitment":"0x69a9f953179a6ce2e08724dd759fb71b08b0759a5900fc0399a753ecf0557df5","occurredAt":"1778640180","parentPulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","pulseType":"2","sequence":"4","subject":"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1","uri":"fixture://missing-artifact"},"observation":{"blockHash":"0x2222222222222222222222222222222222222222222222222222222222222226","blockNumber":"123460","chainId":"8453","emittingContract":"0x1111111111111111111111111111111111111111","eventSignature":"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43","logIndex":"6","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab004","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","transactionIndex":"11","txHash":"0x3333333333333333333333333333333333333333333333333333333333333337"},"observationId":"0x92144fc24c81cdd6319598e6e0c58d84e3f18d9ad4a8be33bc956e32c2ae39f4","reasonCodes":["artifact.unavailable"],"resolverPolicyId":"flowmemory.resolver.policy.v0.fixture","schema":"flowmemory.verifier.report.v0","status":"unresolved","verifierSpecVersion":"0"},"reportDigest":"0xb3798cf34e65a4af4017fe312cd60646a730fd74dbf36ea8d3a5b8b88502e294","reportId":"0xb3798cf34e65a4af4017fe312cd60646a730fd74dbf36ea8d3a5b8b88502e294"},{"reportCore":{"checks":[{"id":"observation.decoded","passed":true}],"evidenceRefs":[],"flowPulse":{"actor":"0x4444444444444444444444444444444444444444","commitment":"0x69a9f953179a6ce2e08724dd759fb71b08b0759a5900fc0399a753ecf0557df5","occurredAt":"1778640240","parentPulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","pulseType":"99","sequence":"5","subject":"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1","uri":"fixture://unsupported"},"observation":{"blockHash":"0x2222222222222222222222222222222222222222222222222222222222222227","blockNumber":"123461","chainId":"8453","emittingContract":"0x1111111111111111111111111111111111111111","eventSignature":"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43","logIndex":"7","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab005","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","transactionIndex":"12","txHash":"0x3333333333333333333333333333333333333333333333333333333333333338"},"observationId":"0x5ba8c5a70c814d35482df5f6598e549f83f2d0e27f6e3d25b83eb2a0e1d56a98","reasonCodes":["pulse.type.unsupported"],"resolverPolicyId":"flowmemory.resolver.policy.v0.fixture","schema":"flowmemory.verifier.report.v0","status":"unsupported","verifierSpecVersion":"0"},"reportDigest":"0x61b984c1055654334a7a7a191887779c8f2be937b08590e8a90927ee645042f5","reportId":"0x61b984c1055654334a7a7a191887779c8f2be937b08590e8a90927ee645042f5"}],"schema":"flowmemory.verifier.persistence.v0"} +{"reports":[{"reportCore":{"checks":[{"id":"observation.decoded","passed":true},{"id":"subject.rootfield_matches","passed":true},{"id":"commitment.rootfield_registration","passed":true}],"evidenceRefs":[{"kind":"rootfield-registration","uri":"ipfs://bafy-flowmemory-example"}],"flowPulse":{"actor":"0x4444444444444444444444444444444444444444","commitment":"0x4122209ff672fc04b2ec3af31ab1af79813971f86de000aa6534038cc79de6b5","occurredAt":"1778640000","parentPulseId":"0x0000000000000000000000000000000000000000000000000000000000000000","pulseType":"1","sequence":"1","subject":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","uri":"ipfs://bafy-flowmemory-example"},"observation":{"blockHash":"0x2222222222222222222222222222222222222222222222222222222222222222","blockNumber":"123456","chainId":"8453","emittingContract":"0x1111111111111111111111111111111111111111","eventSignature":"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43","logIndex":"2","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","transactionIndex":"7","txHash":"0x3333333333333333333333333333333333333333333333333333333333333333"},"observationId":"0x9d958aadf8bf46f989b51e541709a73d21970e7e79643f939c9a0000b50f9a91","reasonCodes":[],"resolverPolicyId":"flowmemory.resolver.policy.v0.fixture","schema":"flowmemory.verifier.report.v0","status":"valid","verifierSpecVersion":"0"},"reportDigest":"0x03af9720a87f7d72ce2ce95c1d04d50134649da51ace802d88818ad67fb2ebb3","reportId":"0x03af9720a87f7d72ce2ce95c1d04d50134649da51ace802d88818ad67fb2ebb3"},{"reportCore":{"checks":[{"id":"observation.decoded","passed":true},{"id":"subject.root_matches","passed":true},{"id":"commitment.root","passed":true}],"evidenceRefs":[{"kind":"root-commitment","uri":"fixture://root-commit-valid"}],"flowPulse":{"actor":"0x4444444444444444444444444444444444444444","commitment":"0x69a9f953179a6ce2e08724dd759fb71b08b0759a5900fc0399a753ecf0557df5","occurredAt":"1778640060","parentPulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","pulseType":"2","sequence":"2","subject":"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1","uri":"fixture://root-commit-valid"},"observation":{"blockHash":"0x2222222222222222222222222222222222222222222222222222222222222223","blockNumber":"123457","chainId":"8453","emittingContract":"0x1111111111111111111111111111111111111111","eventSignature":"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43","logIndex":"3","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab001","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","transactionIndex":"8","txHash":"0x3333333333333333333333333333333333333333333333333333333333333334"},"observationId":"0x49c6cd59d1f1916bc5301308be09c14d64127d1566362272dc5aa201ed53bdea","reasonCodes":[],"resolverPolicyId":"flowmemory.resolver.policy.v0.fixture","schema":"flowmemory.verifier.report.v0","status":"valid","verifierSpecVersion":"0"},"reportDigest":"0xf1dfced6038cfa79928e1888e611da6bb05e7a14393cb20e2d5a5cb90c825c35","reportId":"0xf1dfced6038cfa79928e1888e611da6bb05e7a14393cb20e2d5a5cb90c825c35"},{"reportCore":{"checks":[{"id":"observation.decoded","passed":true},{"id":"subject.pool_matches","passed":true},{"id":"commitment.swap_memory_signal","passed":true}],"evidenceRefs":[{"kind":"swap-memory-signal","uri":"fixture://swap-memory-valid"}],"flowPulse":{"actor":"0x4444444444444444444444444444444444444444","commitment":"0x8fabbad2f4aae2b67e5dcacc7cd425b7860d4d4453488c6c9770196b5009d795","occurredAt":"1778648420","parentPulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab001","pulseType":"4","sequence":"6","subject":"0x1212121212121212121212121212121212121212121212121212121212121212","uri":"fixture://swap-memory-valid"},"observation":{"blockHash":"0x2222222222222222222222222222222222222222222222222222222222222230","blockNumber":"123457","chainId":"8453","emittingContract":"0x2222222222222222222222222222222222222222","eventSignature":"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43","logIndex":"10","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab008","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","transactionIndex":"15","txHash":"0x3333333333333333333333333333333333333333333333333333333333333341"},"observationId":"0x62e6ea1899a4dfd0d0a6cf6010ffb98983db20a9755257c8288602d059aa169e","reasonCodes":[],"resolverPolicyId":"flowmemory.resolver.policy.v0.fixture","schema":"flowmemory.verifier.report.v0","status":"valid","verifierSpecVersion":"0"},"reportDigest":"0xe0d47250f60e6d4764173e6ec13b29b06b9279abf461a5c7be4caf45374d8b40","reportId":"0xe0d47250f60e6d4764173e6ec13b29b06b9279abf461a5c7be4caf45374d8b40"},{"reportCore":{"checks":[{"id":"observation.decoded","passed":true},{"id":"subject.rootfield_matches","passed":true},{"id":"commitment.rootfield_registration","passed":true}],"evidenceRefs":[{"kind":"rootfield-registration","uri":"ipfs://bafy-flowmemory-example"}],"flowPulse":{"actor":"0x4444444444444444444444444444444444444444","commitment":"0x4122209ff672fc04b2ec3af31ab1af79813971f86de000aa6534038cc79de6b5","occurredAt":"1778640000","parentPulseId":"0x0000000000000000000000000000000000000000000000000000000000000000","pulseType":"1","sequence":"1","subject":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","uri":"ipfs://bafy-flowmemory-example"},"observation":{"blockHash":"0x2222222222222222222222222222222222222222222222222222222222222222","blockNumber":"123456","chainId":"8453","emittingContract":"0x1111111111111111111111111111111111111111","eventSignature":"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43","logIndex":"2","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","transactionIndex":"7","txHash":"0x3333333333333333333333333333333333333333333333333333333333333333"},"observationId":"0x9d958aadf8bf46f989b51e541709a73d21970e7e79643f939c9a0000b50f9a91","reasonCodes":[],"resolverPolicyId":"flowmemory.resolver.policy.v0.fixture","schema":"flowmemory.verifier.report.v0","status":"valid","verifierSpecVersion":"0"},"reportDigest":"0x03af9720a87f7d72ce2ce95c1d04d50134649da51ace802d88818ad67fb2ebb3","reportId":"0x03af9720a87f7d72ce2ce95c1d04d50134649da51ace802d88818ad67fb2ebb3"},{"reportCore":{"checks":[{"id":"observation.decoded","passed":true}],"evidenceRefs":[],"flowPulse":{"actor":"0x4444444444444444444444444444444444444444","commitment":"0x69a9f953179a6ce2e08724dd759fb71b08b0759a5900fc0399a753ecf0557df5","occurredAt":"1778640060","parentPulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","pulseType":"2","sequence":"2","subject":"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1","uri":"fixture://root-commit-valid"},"observation":{"blockHash":"0x2222222222222222222222222222222222222222222222222222222222222224","blockNumber":"123458","chainId":"8453","emittingContract":"0x1111111111111111111111111111111111111111","eventSignature":"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43","logIndex":"4","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab002","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","transactionIndex":"9","txHash":"0x3333333333333333333333333333333333333333333333333333333333333335"},"observationId":"0x223d74c971f301d800dd69aa30994bc3fa3089b34b0db1b0a7fc9d4e8d114c79","reasonCodes":["observation.reorged"],"resolverPolicyId":"flowmemory.resolver.policy.v0.fixture","schema":"flowmemory.verifier.report.v0","status":"reorged","verifierSpecVersion":"0"},"reportDigest":"0xb7cef189a48b8c2697603c3704a52bb3a0322b375fdbea68d2bbbadb65a92ec1","reportId":"0xb7cef189a48b8c2697603c3704a52bb3a0322b375fdbea68d2bbbadb65a92ec1"},{"reportCore":{"checks":[{"id":"observation.decoded","passed":true},{"id":"subject.root_matches","passed":true},{"id":"commitment.root","passed":false}],"evidenceRefs":[{"kind":"root-commitment","uri":"fixture://root-commit-valid"}],"flowPulse":{"actor":"0x4444444444444444444444444444444444444444","commitment":"0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc","occurredAt":"1778640120","parentPulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","pulseType":"2","sequence":"3","subject":"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1","uri":"fixture://root-commit-valid"},"observation":{"blockHash":"0x2222222222222222222222222222222222222222222222222222222222222225","blockNumber":"123459","chainId":"8453","emittingContract":"0x1111111111111111111111111111111111111111","eventSignature":"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43","logIndex":"5","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab003","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","transactionIndex":"10","txHash":"0x3333333333333333333333333333333333333333333333333333333333333336"},"observationId":"0xe4a7065f1578c4a232b41f5984942e9b7b07760ce7dfeba77b38eb03173491df","reasonCodes":["commitment.mismatch"],"resolverPolicyId":"flowmemory.resolver.policy.v0.fixture","schema":"flowmemory.verifier.report.v0","status":"invalid","verifierSpecVersion":"0"},"reportDigest":"0xabc22e2203a9404c832aa792f7a569e17e599078fc9ebacf2c1307bbf6f1d7bb","reportId":"0xabc22e2203a9404c832aa792f7a569e17e599078fc9ebacf2c1307bbf6f1d7bb"},{"reportCore":{"checks":[{"id":"observation.decoded","passed":true}],"evidenceRefs":[],"flowPulse":{"actor":"0x4444444444444444444444444444444444444444","commitment":"0x69a9f953179a6ce2e08724dd759fb71b08b0759a5900fc0399a753ecf0557df5","occurredAt":"1778640180","parentPulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","pulseType":"2","sequence":"4","subject":"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1","uri":"fixture://missing-artifact"},"observation":{"blockHash":"0x2222222222222222222222222222222222222222222222222222222222222226","blockNumber":"123460","chainId":"8453","emittingContract":"0x1111111111111111111111111111111111111111","eventSignature":"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43","logIndex":"6","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab004","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","transactionIndex":"11","txHash":"0x3333333333333333333333333333333333333333333333333333333333333337"},"observationId":"0x92144fc24c81cdd6319598e6e0c58d84e3f18d9ad4a8be33bc956e32c2ae39f4","reasonCodes":["artifact.unavailable"],"resolverPolicyId":"flowmemory.resolver.policy.v0.fixture","schema":"flowmemory.verifier.report.v0","status":"unresolved","verifierSpecVersion":"0"},"reportDigest":"0xb3798cf34e65a4af4017fe312cd60646a730fd74dbf36ea8d3a5b8b88502e294","reportId":"0xb3798cf34e65a4af4017fe312cd60646a730fd74dbf36ea8d3a5b8b88502e294"},{"reportCore":{"checks":[{"id":"observation.decoded","passed":true}],"evidenceRefs":[],"flowPulse":{"actor":"0x4444444444444444444444444444444444444444","commitment":"0x69a9f953179a6ce2e08724dd759fb71b08b0759a5900fc0399a753ecf0557df5","occurredAt":"1778640240","parentPulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","pulseType":"99","sequence":"5","subject":"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1","uri":"fixture://unsupported"},"observation":{"blockHash":"0x2222222222222222222222222222222222222222222222222222222222222227","blockNumber":"123461","chainId":"8453","emittingContract":"0x1111111111111111111111111111111111111111","eventSignature":"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43","logIndex":"7","pulseId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab005","rootfieldId":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","transactionIndex":"12","txHash":"0x3333333333333333333333333333333333333333333333333333333333333338"},"observationId":"0x5ba8c5a70c814d35482df5f6598e549f83f2d0e27f6e3d25b83eb2a0e1d56a98","reasonCodes":["pulse.type.unsupported"],"resolverPolicyId":"flowmemory.resolver.policy.v0.fixture","schema":"flowmemory.verifier.report.v0","status":"unsupported","verifierSpecVersion":"0"},"reportDigest":"0x61b984c1055654334a7a7a191887779c8f2be937b08590e8a90927ee645042f5","reportId":"0x61b984c1055654334a7a7a191887779c8f2be937b08590e8a90927ee645042f5"}],"schema":"flowmemory.verifier.persistence.v0"} diff --git a/services/verifier/src/verifier.ts b/services/verifier/src/verifier.ts index 33622a10..e1096748 100644 --- a/services/verifier/src/verifier.ts +++ b/services/verifier/src/verifier.ts @@ -43,7 +43,14 @@ export interface RootCommitmentArtifact { artifactCommitment: string; } -export type VerifierArtifact = RootfieldRegistrationArtifact | RootCommitmentArtifact; +export interface SwapMemorySignalArtifact { + kind: "swap-memory-signal"; + poolId: string; + hookDataHash: string; + memoryRoot: string; +} + +export type VerifierArtifact = RootfieldRegistrationArtifact | RootCommitmentArtifact | SwapMemorySignalArtifact; export interface ArtifactResolverFixture { resolverPolicyId: string; @@ -81,6 +88,14 @@ export function rootCommitment(artifact: RootCommitmentArtifact): `0x${string}` ])); } +export function swapMemorySignalCommitment(artifact: SwapMemorySignalArtifact): `0x${string}` { + return keccak256Hex(concatBytes([ + encodeBytes32(artifact.poolId), + encodeBytes32(artifact.hookDataHash), + encodeBytes32(artifact.memoryRoot), + ])); +} + function baseReportCore( observation: VerifiableObservation, resolverPolicyId: string, @@ -151,7 +166,7 @@ export function verifyObservation( return finalizeReport(baseReportCore(observation, resolver.resolverPolicyId, "reorged", checks, evidenceRefs, reasonCodes)); } - if (observation.pulseType !== "1" && observation.pulseType !== "2") { + if (observation.pulseType !== "1" && observation.pulseType !== "2" && observation.pulseType !== "4") { reasonCodes.push("pulse.type.unsupported"); return finalizeReport(baseReportCore(observation, resolver.resolverPolicyId, "unsupported", checks, evidenceRefs, reasonCodes)); } @@ -226,6 +241,35 @@ export function verifyObservation( )); } + if (observation.pulseType === "4") { + if (artifact.kind !== "swap-memory-signal") { + reasonCodes.push("artifact.schema_mismatch"); + return finalizeReport(baseReportCore(observation, resolver.resolverPolicyId, "invalid", checks, evidenceRefs, reasonCodes)); + } + + const expectedCommitment = swapMemorySignalCommitment(artifact); + const subjectMatches = normalizeBytes32(observation.subject) === normalizeBytes32(artifact.poolId); + const commitmentMatches = normalizeBytes32(observation.commitment) === expectedCommitment; + checks.push({ id: "subject.pool_matches", passed: subjectMatches }); + checks.push({ id: "commitment.swap_memory_signal", passed: commitmentMatches }); + + if (!subjectMatches) { + reasonCodes.push("subject.mismatch"); + } + if (!commitmentMatches) { + reasonCodes.push("commitment.mismatch"); + } + + return finalizeReport(baseReportCore( + observation, + resolver.resolverPolicyId, + reasonCodes.length === 0 ? "valid" : "invalid", + checks, + evidenceRefs, + reasonCodes, + )); + } + reasonCodes.push("pulse.type.unsupported"); return finalizeReport(baseReportCore(observation, resolver.resolverPolicyId, "unsupported", checks, evidenceRefs, reasonCodes)); } diff --git a/services/verifier/test/verifier.test.ts b/services/verifier/test/verifier.test.ts index c42bc401..6195fdd1 100644 --- a/services/verifier/test/verifier.test.ts +++ b/services/verifier/test/verifier.test.ts @@ -8,7 +8,7 @@ import { indexFlowPulseLogs, indexFlowPulseReceipts } from "../../indexer/src/in import { loadIndexerFixtureLogs, loadIndexerFixtureReceipts } from "../../indexer/src/fixtures.ts"; import { loadVerifierArtifactFixture } from "../src/fixtures.ts"; import { readVerifierReports, writeVerifierReports } from "../src/persistence.ts"; -import { verifyObservation, verifyObservations } from "../src/verifier.ts"; +import { swapMemorySignalCommitment, verifyObservation, verifyObservations } from "../src/verifier.ts"; test("generates valid deterministic reports from fixture observations", () => { const indexerState = indexFlowPulseLogs(loadIndexerFixtureLogs()); @@ -74,10 +74,30 @@ test("generates all verifier statuses from receipt fixtures", () => { reorged: 1, unresolved: 1, unsupported: 1, - valid: 3, + valid: 4, }); }); +test("validates swap-derived memory signal commitments", () => { + const indexerState = indexFlowPulseReceipts(loadIndexerFixtureReceipts(), { + finalizedBlockNumber: "123458", + }); + const swapObservation = indexerState.observations.find((observation) => observation.pulseType === "4"); + assert.ok(swapObservation); + + const report = verifyObservation(swapObservation, loadVerifierArtifactFixture()); + assert.equal(report.reportCore.status, "valid"); + assert.deepEqual(report.reportCore.reasonCodes, []); + assert.equal(report.reportCore.checks.some((check) => check.id === "commitment.swap_memory_signal" && check.passed === true), true); + + const resolver = loadVerifierArtifactFixture(); + const artifact = resolver.artifactsByUri["fixture://swap-memory-valid"]; + assert.equal(artifact.kind, "swap-memory-signal"); + if (artifact.kind === "swap-memory-signal") { + assert.equal(swapMemorySignalCommitment(artifact), swapObservation.commitment); + } +}); + test("persists deterministic verifier report JSON", () => { const indexerState = indexFlowPulseReceipts(loadIndexerFixtureReceipts(), { finalizedBlockNumber: "123458", @@ -95,7 +115,7 @@ test("persists deterministic verifier report JSON", () => { assert.equal(firstWrite, secondWrite); assert.equal(persisted.schema, "flowmemory.verifier.persistence.v0"); - assert.equal(persisted.reports.length, 7); + assert.equal(persisted.reports.length, 8); assert.equal("createdAt" in persisted.reports[0].reportCore, false); } finally { rmSync(dir, { recursive: true, force: true }); diff --git a/tests/LiveV0Package.t.sol b/tests/LiveV0Package.t.sol index 747c4f03..a8b9f6fb 100644 --- a/tests/LiveV0Package.t.sol +++ b/tests/LiveV0Package.t.sol @@ -99,6 +99,8 @@ contract WorkDebtSchedulerCaller { contract LiveV0PackageTest { LiveV0Vm private constant vm = LiveV0Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); + bytes32 private constant FLOWPULSE_SIGNATURE = + keccak256("FlowPulse(bytes32,bytes32,address,uint8,bytes32,bytes32,bytes32,uint64,uint64,string)"); error AssertionFailed(); @@ -589,16 +591,40 @@ contract LiveV0PackageTest { function testFlowMemoryHookAdapterEmitsObservationAndReturnsSelector() public { FlowMemoryHookAdapter adapter = new FlowMemoryHookAdapter(); bytes memory hookData = abi.encode(keccak256("artifact.commitment")); + bytes32 poolId = keccak256("pool.alpha"); + bytes32 rootfieldId = keccak256("rootfield.alpha"); + bytes32 commitment = keccak256("hook.commitment"); vm.recordLogs(); - bytes4 selector = adapter.afterSwap( - address(this), keccak256("pool.alpha"), keccak256("rootfield.alpha"), keccak256("hook.commitment"), hookData - ); + bytes4 selector = adapter.afterSwap(address(this), poolId, rootfieldId, commitment, hookData); LiveV0Vm.Log[] memory logs = vm.getRecordedLogs(); _assertTrue(selector == adapter.AFTER_SWAP_SELECTOR()); - _assertTrue(logs.length == 1); + _assertTrue(logs.length == 2); _assertTrue(logs[0].emitter == address(adapter)); + _assertTrue(logs[1].emitter == address(adapter)); + _assertTrue(logs[1].topics.length == 4); + _assertTrue(logs[1].topics[0] == FLOWPULSE_SIGNATURE); + _assertTrue(logs[1].topics[2] == rootfieldId); + _assertTrue(logs[1].topics[3] == bytes32(uint256(uint160(address(this))))); + + ( + uint8 pulseType, + bytes32 subject, + bytes32 flowPulseCommitment, + bytes32 parentPulseId, + uint64 sequence, + uint64 occurredAt, + string memory uri + ) = abi.decode(logs[1].data, (uint8, bytes32, bytes32, bytes32, uint64, uint64, string)); + + _assertTrue(pulseType == 4); + _assertTrue(subject == poolId); + _assertTrue(flowPulseCommitment == commitment); + _assertTrue(parentPulseId == bytes32(0)); + _assertTrue(sequence == 1); + _assertTrue(occurredAt > 0); + _assertTrue(keccak256(bytes(uri)) == keccak256("flowmemory://uniswap-v4/after-swap")); } function testFlowMemoryHookAdapterRejectsZeroCommitment() public { @@ -608,6 +634,21 @@ contract LiveV0PackageTest { adapter.afterSwap(address(this), keccak256("pool.alpha"), keccak256("rootfield.alpha"), bytes32(0), ""); } + function testFlowMemoryHookAdapterRejectsZeroSwapInputs() public { + FlowMemoryHookAdapter adapter = new FlowMemoryHookAdapter(); + + vm.expectRevert(FlowMemoryHookAdapter.ZeroSender.selector); + adapter.afterSwap( + address(0), keccak256("pool.alpha"), keccak256("rootfield.alpha"), keccak256("commitment"), "" + ); + + vm.expectRevert(FlowMemoryHookAdapter.ZeroPoolId.selector); + adapter.afterSwap(address(this), bytes32(0), keccak256("rootfield.alpha"), keccak256("commitment"), ""); + + vm.expectRevert(FlowMemoryHookAdapter.ZeroRootfieldId.selector); + adapter.afterSwap(address(this), keccak256("pool.alpha"), bytes32(0), keccak256("commitment"), ""); + } + function _assertTrue(bool condition) private pure { if (!condition) revert AssertionFailed(); }