diff --git a/apps/dashboard/public/data/flowmemory-dashboard-base-canary-v0.json b/apps/dashboard/public/data/flowmemory-dashboard-base-canary-v0.json new file mode 100644 index 00000000..d6ac2de2 --- /dev/null +++ b/apps/dashboard/public/data/flowmemory-dashboard-base-canary-v0.json @@ -0,0 +1,891 @@ +{ + "metadata": { + "schema": "flowmemory.dashboard.fixture.v0", + "generatedAt": "2026-05-13T20:05:51.625Z", + "mode": "canary", + "description": "Generated Base mainnet canary dashboard data from the guarded FlowPulse reader. It is canary-only and not a production-readiness claim.", + "fixturePath": "fixtures/dashboard/flowmemory-dashboard-base-canary-v0.json", + "runtimeDataPath": "apps/dashboard/public/data/flowmemory-dashboard-base-canary-v0.json", + "canary": { + "deploymentArtifactPath": "fixtures/deployments/base-canary-v0.json", + "indexerStatePath": "fixtures/deployments/base-canary-indexer-state.json", + "checkpointPath": "fixtures/deployments/base-canary-indexer-checkpoint.json", + "docsPath": "docs/DEPLOYMENTS/2026-05-13-base-canary-v0.md", + "productionReady": false, + "sourceVerification": { + "compilerVersion": "v0.8.24+commit.e11b9ed9", + "optimizer": true, + "optimizerRuns": 200, + "chainId": "8453", + "verifier": "etherscan", + "apiKeyEnv": "BASESCAN_API_KEY" + }, + "readWindow": { + "fromBlock": "45955500", + "toBlock": "45955540", + "finalizedBlock": "45955540" + }, + "counts": { + "observations": 4, + "rejectedLogs": 0, + "duplicates": 0, + "contracts": 10 + }, + "contracts": [ + { + "name": "RootfieldRegistry", + "sourceName": "contracts/RootfieldRegistry.sol:RootfieldRegistry", + "address": "0x2a7ADd68a1d45C3251E2F92fFe4926124654a97C", + "deployTx": "0x9ef9cb563646921e8f8dd71ad237054b8ee5b0c8e8ecc0c3b1577661844d6108", + "block": "45955460", + "emitsFlowPulse": true + }, + { + "name": "FlowMemoryHookAdapter", + "sourceName": "contracts/FlowMemoryHookAdapter.sol:FlowMemoryHookAdapter", + "address": "0x179Df6d52e9DeF5D02704583a2E4E5a9FF427245", + "deployTx": "0xab22f1b9a19c090c63d4b3a4e100cf80571f5142ab91620fbc76d739d1f318b9", + "block": "45955461", + "emitsFlowPulse": true + }, + { + "name": "ArtifactRegistry", + "sourceName": "contracts/ArtifactRegistry.sol:ArtifactRegistry", + "address": "0x8F074d0F4e66975b740A4b7a316330c9660a485E", + "deployTx": "0xbd07e7d0280a20344521628b13353152da859e62fdcdb4355bf04b50a4176b0b", + "block": "45955462", + "emitsFlowPulse": false + }, + { + "name": "CursorRegistry", + "sourceName": "contracts/CursorRegistry.sol:CursorRegistry", + "address": "0x3360689009685eade15c876855D24161b05829C1", + "deployTx": "0x7eecbb949a617bff8fc8c0749a3332e1e2d2fe7b17f0cf3d7522155f5675cee9", + "block": "45955463", + "emitsFlowPulse": false + }, + { + "name": "ReceiptVerifier", + "sourceName": "contracts/ReceiptVerifier.sol:ReceiptVerifier", + "address": "0x94ba7aA4562f8F8528C327378F6352350f6ddB5B", + "deployTx": "0x3b29454ebb4104e38a0f886a4a370f26d3e3bc5ab22bbfb921374fa904e38e95", + "block": "45955464", + "emitsFlowPulse": false + }, + { + "name": "WorkerRegistry", + "sourceName": "contracts/WorkerRegistry.sol:WorkerRegistry", + "address": "0xa8c07eF53Eeb4e57297ee35025a9cD5303fCCD29", + "deployTx": "0x213a1ba85d6e998937c907699be83b7cd7db9eba4e43f937f94ad7b62bf3cfde", + "block": "45955465", + "emitsFlowPulse": false + }, + { + "name": "VerifierRegistry", + "sourceName": "contracts/VerifierRegistry.sol:VerifierRegistry", + "address": "0xAf920ca7436Bb72172E27C96E0B716f01dcC5DBd", + "deployTx": "0x87696577de6522f885a67c9df98fd8942387e7e7c735f3215f067b2cbc913d2f", + "block": "45955466", + "emitsFlowPulse": false + }, + { + "name": "WorkReceiptRegistry", + "sourceName": "contracts/WorkReceiptRegistry.sol:WorkReceiptRegistry", + "address": "0x2874cee0D581E4562ac9015BfCf330f1ea58a1F3", + "deployTx": "0xa278992a28c2a1ad9e4c78998dbbfb43399e55182ca93f8aac40e9447b2f854f", + "block": "45955467", + "emitsFlowPulse": false + }, + { + "name": "VerifierReportRegistry", + "sourceName": "contracts/VerifierReportRegistry.sol:VerifierReportRegistry", + "address": "0x95bC7455AdFD60e1B908ba455c25Ae732C1Ef996", + "deployTx": "0x9def1a9338bf65e04bc43fd3f4ef1bb973ebdf1ee4b351a77157c396da6af074", + "block": "45955468", + "emitsFlowPulse": false + }, + { + "name": "WorkDebtScheduler", + "sourceName": "contracts/WorkDebtScheduler.sol:WorkDebtScheduler", + "address": "0xa752e9bC7fAf39f659110D8Cf408E7707db94E34", + "deployTx": "0x139e3b89b0c674c6031d34fbfafb2649e114443ed7b98b9722d59bc19ffb6bdc", + "block": "45955469", + "emitsFlowPulse": false + } + ], + "boundaries": [ + "Canary deployment for V0 testing only.", + "Not a production protocol launch.", + "Not a production Uniswap v4 hook deployment.", + "Not a production verifier network.", + "No token, fee, custody, governance, multisig, or upgrade policy is implied." + ] + }, + "futureGeneratedPaths": { + "indexer": "fixtures/deployments/base-canary-indexer-state.json", + "verifier": "not-applicable-for-canary-v0", + "devnet": "not-applicable-for-canary-v0", + "hardware": "not-applicable-for-canary-v0" + } + }, + "chain": { + "chainId": "8453", + "name": "Base mainnet V0 canary", + "environment": "mainnet", + "settlementContext": "Guarded canary read from deployed V0 FlowPulse surfaces; not production protocol readiness.", + "currentBlock": 45955540, + "finalizedBlock": 45955540, + "source": "live", + "lastUpdated": "2026-05-13T20:05:51.625Z" + }, + "flowPulseObservations": [ + { + "id": "0x7b43ea15b0d77169e56395425bb757d280aa8be2b262fbd5f85e68fb01cb33b9", + "observationId": "0x7b43ea15b0d77169e56395425bb757d280aa8be2b262fbd5f85e68fb01cb33b9", + "pulseId": "0xa62ffb4b36a415032949138edbdcba5005de2e35952df88bdf592d4266184b87", + "rootfieldId": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "eventSignature": "0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43", + "blockNumber": "45955506", + "blockHash": "0xb99c82844b5ea00482b5e36724e167dbaa3276e872de6884c736f715b6acc758", + "txHash": "0x994b98b1cff0c897d75b62cf7c95340f74a59d0c208af68f1dea2d161b80cf00", + "transactionIndex": "37", + "logIndex": "229", + "receiptStatus": "success", + "actor": "0x3a6fba5a78216ba3a8da8d8f501dee2c8186aff9", + "pulseType": "1", + "subject": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "commitment": "0x421370a88b0e8632d6a90aeffa276eda7dd5995bb18cb7ce0fb5d36a553e98dd", + "parentPulseId": "0x0000000000000000000000000000000000000000000000000000000000000000", + "sequence": "1", + "occurredAt": "2026-05-13T19:25:59.000Z", + "uri": "flowmemory://base-canary/rootfield", + "summary": "rootfield registration from Base canary reader", + "status": "finalized", + "lastUpdated": "2026-05-13T20:05:51.625Z", + "provenance": { + "subsystem": "indexer", + "origin": "live", + "chainContext": "base-mainnet-canary", + "fixturePath": "fixtures/deployments/base-canary-indexer-state.json", + "capturedAt": "2026-05-13T20:05:51.625Z", + "localPathHint": "fixtures/deployments/base-canary-indexer-state.json" + } + }, + { + "id": "0xdd701330fee35840b3e02fc8b8154284d883008467936052963eb8ed0b5e9b68", + "observationId": "0xdd701330fee35840b3e02fc8b8154284d883008467936052963eb8ed0b5e9b68", + "pulseId": "0x2d436d766f9777b7f9925d57d8b2d57def3fdfae405017104f21795e20eacef7", + "rootfieldId": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "eventSignature": "0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43", + "blockNumber": "45955507", + "blockHash": "0x80c8eaffc3d5ebaa3eda56ba83ea5fefa1c9c37777dde659c0204c0a04dbe336", + "txHash": "0x5f81dc48c5d172ff3f44a333a33598f23c82be2614f4156d5dd3257a16806cc7", + "transactionIndex": "74", + "logIndex": "274", + "receiptStatus": "success", + "actor": "0x3a6fba5a78216ba3a8da8d8f501dee2c8186aff9", + "pulseType": "4", + "subject": "0x205b396f8fdc1b25ddef7afa02f31f51f1e1048004897b87477569a8e573d434", + "commitment": "0x30055afe075a7c6ea8557ea3a2d3c7012d9d558ebda95803726179355f98ede9", + "parentPulseId": "0x0000000000000000000000000000000000000000000000000000000000000000", + "sequence": "1", + "occurredAt": "2026-05-13T19:26:01.000Z", + "uri": "flowmemory://uniswap-v4/after-swap", + "summary": "swap memory signal from Base canary reader", + "status": "finalized", + "lastUpdated": "2026-05-13T20:05:51.625Z", + "provenance": { + "subsystem": "indexer", + "origin": "live", + "chainContext": "base-mainnet-canary", + "fixturePath": "fixtures/deployments/base-canary-indexer-state.json", + "capturedAt": "2026-05-13T20:05:51.625Z", + "localPathHint": "fixtures/deployments/base-canary-indexer-state.json" + } + }, + { + "id": "0x3711d0558240758a09429d6408eb1e8d699bf2555fb8253ef8192fe5f583f12d", + "observationId": "0x3711d0558240758a09429d6408eb1e8d699bf2555fb8253ef8192fe5f583f12d", + "pulseId": "0x72407268a2ea62659d6b0f62800931936cc6ea7ea5f5b6db91801ba2f8b43eab", + "rootfieldId": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "eventSignature": "0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43", + "blockNumber": "45955533", + "blockHash": "0x7fc3c3aa61c897fc71fdf7d065a1bda685442cb2096c546d2d1dba4da3339b52", + "txHash": "0x24a43789ef489dd6c697567466944a210273e46c333e7be878cda6df9acb8e7a", + "transactionIndex": "131", + "logIndex": "364", + "receiptStatus": "success", + "actor": "0x3a6fba5a78216ba3a8da8d8f501dee2c8186aff9", + "pulseType": "2", + "subject": "0x4a7b8601c06c20bcc7b69c05c51980c12dbd50cbd95a59f460d40555bfc37ce3", + "commitment": "0xe331e74d3287b677c4eee492f00c22efd12f9365070eca9569f78966e499e039", + "parentPulseId": "0xa62ffb4b36a415032949138edbdcba5005de2e35952df88bdf592d4266184b87", + "sequence": "2", + "occurredAt": "2026-05-13T19:26:53.000Z", + "uri": "flowmemory://base-canary/root", + "summary": "root commitment from Base canary reader", + "status": "finalized", + "lastUpdated": "2026-05-13T20:05:51.625Z", + "provenance": { + "subsystem": "indexer", + "origin": "live", + "chainContext": "base-mainnet-canary", + "fixturePath": "fixtures/deployments/base-canary-indexer-state.json", + "capturedAt": "2026-05-13T20:05:51.625Z", + "localPathHint": "fixtures/deployments/base-canary-indexer-state.json" + } + }, + { + "id": "0xf3a1c63cf9eb89e59970b1266f8691142349d2af230a7654abcf91fcccef0da6", + "observationId": "0xf3a1c63cf9eb89e59970b1266f8691142349d2af230a7654abcf91fcccef0da6", + "pulseId": "0x16c2adf5f3e46ee91d16a432d2420c566851b311e767860cab99068dcaca2591", + "rootfieldId": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "eventSignature": "0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43", + "blockNumber": "45955535", + "blockHash": "0x0510d6a627884b7b3677a7223a2720d1e33f5f4e58f2c0c6c7baf27bcec25d7c", + "txHash": "0xaee21f6d0e9df1a45eae0c7714a4f8eae7fb72afbb07dd67b3a1f0ff724a014f", + "transactionIndex": "194", + "logIndex": "1212", + "receiptStatus": "success", + "actor": "0x3a6fba5a78216ba3a8da8d8f501dee2c8186aff9", + "pulseType": "4", + "subject": "0x205b396f8fdc1b25ddef7afa02f31f51f1e1048004897b87477569a8e573d434", + "commitment": "0x30055afe075a7c6ea8557ea3a2d3c7012d9d558ebda95803726179355f98ede9", + "parentPulseId": "0x0000000000000000000000000000000000000000000000000000000000000000", + "sequence": "2", + "occurredAt": "2026-05-13T19:26:57.000Z", + "uri": "flowmemory://uniswap-v4/after-swap", + "summary": "swap memory signal from Base canary reader", + "status": "finalized", + "lastUpdated": "2026-05-13T20:05:51.625Z", + "provenance": { + "subsystem": "indexer", + "origin": "live", + "chainContext": "base-mainnet-canary", + "fixturePath": "fixtures/deployments/base-canary-indexer-state.json", + "capturedAt": "2026-05-13T20:05:51.625Z", + "localPathHint": "fixtures/deployments/base-canary-indexer-state.json" + } + } + ], + "rootfields": [ + { + "id": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "rootfieldId": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "owner": "0x3A6fBA5a78216ba3a8DA8d8F501dee2C8186aFf9", + "schemaHash": "0x0b4537a7fa7cdd45fd6ff2052f1e4f9087a40b09fb6fe06a686ac67ac96fa5c3", + "metadataHash": "0x5f2a82ffa386793a2a67971ab801b2633b76954a95ee873e463730e6442ef90d", + "latestRoot": "0x4a7b8601c06c20bcc7b69c05c51980c12dbd50cbd95a59f460d40555bfc37ce3", + "latestObservationId": "0xf3a1c63cf9eb89e59970b1266f8691142349d2af230a7654abcf91fcccef0da6", + "pulseCount": 4, + "workLaneIds": [ + "MEMORY_REFRESH", + "STEERING_VALIDATION" + ], + "evidenceUri": "docs/DEPLOYMENTS/2026-05-13-base-canary-v0.md", + "status": "finalized", + "lastUpdated": "2026-05-13T20:05:51.625Z", + "provenance": { + "subsystem": "indexer", + "origin": "live", + "chainContext": "base-mainnet-canary", + "fixturePath": "fixtures/deployments/base-canary-indexer-state.json", + "capturedAt": "2026-05-13T20:05:51.625Z", + "localPathHint": "fixtures/deployments/base-canary-indexer-state.json" + } + } + ], + "workLanes": [], + "workReceipts": [], + "verifierReports": [], + "rootflowTransitions": [ + { + "schema": "flowmemory.rootflow_transition.v0", + "transitionId": "0xef6e3d4a6375fdaf3573db4550b7a6c4ac535f6ddbf8a2759014c7e6ee5dc17d", + "rootfieldId": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "observationId": "0x7b43ea15b0d77169e56395425bb757d280aa8be2b262fbd5f85e68fb01cb33b9", + "pulseId": "0xa62ffb4b36a415032949138edbdcba5005de2e35952df88bdf592d4266184b87", + "parentPulseId": "0x0000000000000000000000000000000000000000000000000000000000000000", + "parentTransitionId": null, + "memorySignalId": "0x1d530aa927338513f49fbd12145d05b1428347b5cfa0fb241033a9d070123637", + "memoryReceiptId": null, + "reportId": null, + "previousRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "attemptedRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nextRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "status": "finalized", + "blockNumber": "45955506", + "txHash": "0x994b98b1cff0c897d75b62cf7c95340f74a59d0c208af68f1dea2d161b80cf00", + "sequence": "1", + "reasonCodes": [ + "canary.no_verifier_report" + ], + "contractEventRef": { + "signalId": "0x1d530aa927338513f49fbd12145d05b1428347b5cfa0fb241033a9d070123637", + "eventName": "FlowPulse", + "eventTopic0": "0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43", + "sourceContract": "0x2a7add68a1d45c3251e2f92ffe4926124654a97c", + "pulseTypeId": "1", + "pulseTypeName": "ROOTFIELD_REGISTERED", + "txHash": "0x994b98b1cff0c897d75b62cf7c95340f74a59d0c208af68f1dea2d161b80cf00", + "logIndex": "229" + }, + "id": "0xef6e3d4a6375fdaf3573db4550b7a6c4ac535f6ddbf8a2759014c7e6ee5dc17d", + "lastUpdated": "2026-05-13T20:05:51.625Z", + "provenance": { + "subsystem": "indexer", + "origin": "live", + "chainContext": "base-mainnet-canary", + "fixturePath": "fixtures/deployments/base-canary-indexer-state.json", + "capturedAt": "2026-05-13T20:05:51.625Z", + "localPathHint": "fixtures/deployments/base-canary-indexer-state.json" + } + }, + { + "schema": "flowmemory.rootflow_transition.v0", + "transitionId": "0x7da37f62f68f29fdd92dde90fdb5783bce555c868bfb0181187ae955eb2d8c95", + "rootfieldId": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "observationId": "0xdd701330fee35840b3e02fc8b8154284d883008467936052963eb8ed0b5e9b68", + "pulseId": "0x2d436d766f9777b7f9925d57d8b2d57def3fdfae405017104f21795e20eacef7", + "parentPulseId": "0x0000000000000000000000000000000000000000000000000000000000000000", + "parentTransitionId": null, + "memorySignalId": "0x4dbf295239f26095033ddbc39dbb3d2780a554ca697d3ea89c588bda449939d1", + "memoryReceiptId": null, + "reportId": null, + "previousRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "attemptedRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nextRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "status": "finalized", + "blockNumber": "45955507", + "txHash": "0x5f81dc48c5d172ff3f44a333a33598f23c82be2614f4156d5dd3257a16806cc7", + "sequence": "1", + "reasonCodes": [ + "canary.no_verifier_report" + ], + "contractEventRef": { + "signalId": "0x4dbf295239f26095033ddbc39dbb3d2780a554ca697d3ea89c588bda449939d1", + "eventName": "FlowPulse", + "eventTopic0": "0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43", + "sourceContract": "0x179df6d52e9def5d02704583a2e4e5a9ff427245", + "pulseTypeId": "4", + "pulseTypeName": "SWAP_MEMORY_SIGNAL", + "txHash": "0x5f81dc48c5d172ff3f44a333a33598f23c82be2614f4156d5dd3257a16806cc7", + "logIndex": "274" + }, + "id": "0x7da37f62f68f29fdd92dde90fdb5783bce555c868bfb0181187ae955eb2d8c95", + "lastUpdated": "2026-05-13T20:05:51.625Z", + "provenance": { + "subsystem": "indexer", + "origin": "live", + "chainContext": "base-mainnet-canary", + "fixturePath": "fixtures/deployments/base-canary-indexer-state.json", + "capturedAt": "2026-05-13T20:05:51.625Z", + "localPathHint": "fixtures/deployments/base-canary-indexer-state.json" + } + }, + { + "schema": "flowmemory.rootflow_transition.v0", + "transitionId": "0x508409804fe0dae77e8ced57cb45968877fcbad6f525a99b4f911ce58759eab7", + "rootfieldId": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "observationId": "0x3711d0558240758a09429d6408eb1e8d699bf2555fb8253ef8192fe5f583f12d", + "pulseId": "0x72407268a2ea62659d6b0f62800931936cc6ea7ea5f5b6db91801ba2f8b43eab", + "parentPulseId": "0xa62ffb4b36a415032949138edbdcba5005de2e35952df88bdf592d4266184b87", + "parentTransitionId": "0xef6e3d4a6375fdaf3573db4550b7a6c4ac535f6ddbf8a2759014c7e6ee5dc17d", + "memorySignalId": "0x79640fe5901417b9254f9de76f011cdd54ffe9587aaf564f241f4b0cc2ec82da", + "memoryReceiptId": null, + "reportId": null, + "previousRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "attemptedRoot": "0x4a7b8601c06c20bcc7b69c05c51980c12dbd50cbd95a59f460d40555bfc37ce3", + "nextRoot": "0x4a7b8601c06c20bcc7b69c05c51980c12dbd50cbd95a59f460d40555bfc37ce3", + "status": "finalized", + "blockNumber": "45955533", + "txHash": "0x24a43789ef489dd6c697567466944a210273e46c333e7be878cda6df9acb8e7a", + "sequence": "2", + "reasonCodes": [ + "canary.no_verifier_report" + ], + "contractEventRef": { + "signalId": "0x79640fe5901417b9254f9de76f011cdd54ffe9587aaf564f241f4b0cc2ec82da", + "eventName": "FlowPulse", + "eventTopic0": "0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43", + "sourceContract": "0x2a7add68a1d45c3251e2f92ffe4926124654a97c", + "pulseTypeId": "2", + "pulseTypeName": "ROOT_COMMITTED", + "txHash": "0x24a43789ef489dd6c697567466944a210273e46c333e7be878cda6df9acb8e7a", + "logIndex": "364" + }, + "id": "0x508409804fe0dae77e8ced57cb45968877fcbad6f525a99b4f911ce58759eab7", + "lastUpdated": "2026-05-13T20:05:51.625Z", + "provenance": { + "subsystem": "indexer", + "origin": "live", + "chainContext": "base-mainnet-canary", + "fixturePath": "fixtures/deployments/base-canary-indexer-state.json", + "capturedAt": "2026-05-13T20:05:51.625Z", + "localPathHint": "fixtures/deployments/base-canary-indexer-state.json" + } + }, + { + "schema": "flowmemory.rootflow_transition.v0", + "transitionId": "0x91d0da9bc41fad8cb3fa66b72ba804f6685d34f0c167bd3fbf1ac48f91402188", + "rootfieldId": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "observationId": "0xf3a1c63cf9eb89e59970b1266f8691142349d2af230a7654abcf91fcccef0da6", + "pulseId": "0x16c2adf5f3e46ee91d16a432d2420c566851b311e767860cab99068dcaca2591", + "parentPulseId": "0x0000000000000000000000000000000000000000000000000000000000000000", + "parentTransitionId": null, + "memorySignalId": "0x623c146bc8899c5edadaed215cc86a1292f1f5c97acb245e86c0462190078ca5", + "memoryReceiptId": null, + "reportId": null, + "previousRoot": "0x4a7b8601c06c20bcc7b69c05c51980c12dbd50cbd95a59f460d40555bfc37ce3", + "attemptedRoot": "0x4a7b8601c06c20bcc7b69c05c51980c12dbd50cbd95a59f460d40555bfc37ce3", + "nextRoot": "0x4a7b8601c06c20bcc7b69c05c51980c12dbd50cbd95a59f460d40555bfc37ce3", + "status": "finalized", + "blockNumber": "45955535", + "txHash": "0xaee21f6d0e9df1a45eae0c7714a4f8eae7fb72afbb07dd67b3a1f0ff724a014f", + "sequence": "2", + "reasonCodes": [ + "canary.no_verifier_report" + ], + "contractEventRef": { + "signalId": "0x623c146bc8899c5edadaed215cc86a1292f1f5c97acb245e86c0462190078ca5", + "eventName": "FlowPulse", + "eventTopic0": "0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43", + "sourceContract": "0x179df6d52e9def5d02704583a2e4e5a9ff427245", + "pulseTypeId": "4", + "pulseTypeName": "SWAP_MEMORY_SIGNAL", + "txHash": "0xaee21f6d0e9df1a45eae0c7714a4f8eae7fb72afbb07dd67b3a1f0ff724a014f", + "logIndex": "1212" + }, + "id": "0x91d0da9bc41fad8cb3fa66b72ba804f6685d34f0c167bd3fbf1ac48f91402188", + "lastUpdated": "2026-05-13T20:05:51.625Z", + "provenance": { + "subsystem": "indexer", + "origin": "live", + "chainContext": "base-mainnet-canary", + "fixturePath": "fixtures/deployments/base-canary-indexer-state.json", + "capturedAt": "2026-05-13T20:05:51.625Z", + "localPathHint": "fixtures/deployments/base-canary-indexer-state.json" + } + } + ], + "memorySignals": [ + { + "schema": "flowmemory.memory_signal.v0", + "signalId": "0x1d530aa927338513f49fbd12145d05b1428347b5cfa0fb241033a9d070123637", + "observationId": "0x7b43ea15b0d77169e56395425bb757d280aa8be2b262fbd5f85e68fb01cb33b9", + "pulseId": "0xa62ffb4b36a415032949138edbdcba5005de2e35952df88bdf592d4266184b87", + "rootfieldId": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "signalType": "rootfield_registration", + "status": "finalized", + "chainId": "8453", + "emittingContract": "0x2a7add68a1d45c3251e2f92ffe4926124654a97c", + "blockNumber": "45955506", + "blockHash": "0xb99c82844b5ea00482b5e36724e167dbaa3276e872de6884c736f715b6acc758", + "txHash": "0x994b98b1cff0c897d75b62cf7c95340f74a59d0c208af68f1dea2d161b80cf00", + "transactionIndex": "37", + "logIndex": "229", + "actor": "0x3a6fba5a78216ba3a8da8d8f501dee2c8186aff9", + "subject": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "commitment": "0x421370a88b0e8632d6a90aeffa276eda7dd5995bb18cb7ce0fb5d36a553e98dd", + "parentPulseId": "0x0000000000000000000000000000000000000000000000000000000000000000", + "sequence": "1", + "occurredAt": "2026-05-13T19:25:59.000Z", + "uri": "flowmemory://base-canary/rootfield", + "summary": "rootfield registration canary pulse 1", + "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": "0x2a7add68a1d45c3251e2f92ffe4926124654a97c", + "pulseTypeId": "1", + "pulseTypeName": "ROOTFIELD_REGISTERED", + "indexed": { + "pulseId": "0xa62ffb4b36a415032949138edbdcba5005de2e35952df88bdf592d4266184b87", + "rootfieldId": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "actor": "0x3a6fba5a78216ba3a8da8d8f501dee2c8186aff9" + }, + "payload": { + "subject": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "commitment": "0x421370a88b0e8632d6a90aeffa276eda7dd5995bb18cb7ce0fb5d36a553e98dd", + "parentPulseId": "0x0000000000000000000000000000000000000000000000000000000000000000", + "sequence": "1", + "occurredAt": "2026-05-13T19:25:59.000Z", + "uri": "flowmemory://base-canary/rootfield" + }, + "receiptLocator": { + "chainId": "8453", + "blockNumber": "45955506", + "blockHash": "0xb99c82844b5ea00482b5e36724e167dbaa3276e872de6884c736f715b6acc758", + "txHash": "0x994b98b1cff0c897d75b62cf7c95340f74a59d0c208af68f1dea2d161b80cf00", + "transactionIndex": "37", + "logIndex": "229", + "receiptStatus": "success" + }, + "receiptDerivedFields": [ + "blockHash", + "txHash", + "transactionIndex", + "logIndex", + "receiptStatus" + ] + }, + "id": "0x1d530aa927338513f49fbd12145d05b1428347b5cfa0fb241033a9d070123637", + "lastUpdated": "2026-05-13T20:05:51.625Z", + "provenance": { + "subsystem": "indexer", + "origin": "live", + "chainContext": "base-mainnet-canary", + "fixturePath": "fixtures/deployments/base-canary-indexer-state.json", + "capturedAt": "2026-05-13T20:05:51.625Z", + "localPathHint": "fixtures/deployments/base-canary-indexer-state.json" + } + }, + { + "schema": "flowmemory.memory_signal.v0", + "signalId": "0x4dbf295239f26095033ddbc39dbb3d2780a554ca697d3ea89c588bda449939d1", + "observationId": "0xdd701330fee35840b3e02fc8b8154284d883008467936052963eb8ed0b5e9b68", + "pulseId": "0x2d436d766f9777b7f9925d57d8b2d57def3fdfae405017104f21795e20eacef7", + "rootfieldId": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "signalType": "swap_memory_signal", + "status": "finalized", + "chainId": "8453", + "emittingContract": "0x179df6d52e9def5d02704583a2e4e5a9ff427245", + "blockNumber": "45955507", + "blockHash": "0x80c8eaffc3d5ebaa3eda56ba83ea5fefa1c9c37777dde659c0204c0a04dbe336", + "txHash": "0x5f81dc48c5d172ff3f44a333a33598f23c82be2614f4156d5dd3257a16806cc7", + "transactionIndex": "74", + "logIndex": "274", + "actor": "0x3a6fba5a78216ba3a8da8d8f501dee2c8186aff9", + "subject": "0x205b396f8fdc1b25ddef7afa02f31f51f1e1048004897b87477569a8e573d434", + "commitment": "0x30055afe075a7c6ea8557ea3a2d3c7012d9d558ebda95803726179355f98ede9", + "parentPulseId": "0x0000000000000000000000000000000000000000000000000000000000000000", + "sequence": "1", + "occurredAt": "2026-05-13T19:26:01.000Z", + "uri": "flowmemory://uniswap-v4/after-swap", + "summary": "swap memory signal canary pulse 1", + "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": "0x179df6d52e9def5d02704583a2e4e5a9ff427245", + "pulseTypeId": "4", + "pulseTypeName": "SWAP_MEMORY_SIGNAL", + "indexed": { + "pulseId": "0x2d436d766f9777b7f9925d57d8b2d57def3fdfae405017104f21795e20eacef7", + "rootfieldId": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "actor": "0x3a6fba5a78216ba3a8da8d8f501dee2c8186aff9" + }, + "payload": { + "subject": "0x205b396f8fdc1b25ddef7afa02f31f51f1e1048004897b87477569a8e573d434", + "commitment": "0x30055afe075a7c6ea8557ea3a2d3c7012d9d558ebda95803726179355f98ede9", + "parentPulseId": "0x0000000000000000000000000000000000000000000000000000000000000000", + "sequence": "1", + "occurredAt": "2026-05-13T19:26:01.000Z", + "uri": "flowmemory://uniswap-v4/after-swap" + }, + "receiptLocator": { + "chainId": "8453", + "blockNumber": "45955507", + "blockHash": "0x80c8eaffc3d5ebaa3eda56ba83ea5fefa1c9c37777dde659c0204c0a04dbe336", + "txHash": "0x5f81dc48c5d172ff3f44a333a33598f23c82be2614f4156d5dd3257a16806cc7", + "transactionIndex": "74", + "logIndex": "274", + "receiptStatus": "success" + }, + "receiptDerivedFields": [ + "blockHash", + "txHash", + "transactionIndex", + "logIndex", + "receiptStatus" + ] + }, + "id": "0x4dbf295239f26095033ddbc39dbb3d2780a554ca697d3ea89c588bda449939d1", + "lastUpdated": "2026-05-13T20:05:51.625Z", + "provenance": { + "subsystem": "indexer", + "origin": "live", + "chainContext": "base-mainnet-canary", + "fixturePath": "fixtures/deployments/base-canary-indexer-state.json", + "capturedAt": "2026-05-13T20:05:51.625Z", + "localPathHint": "fixtures/deployments/base-canary-indexer-state.json" + } + }, + { + "schema": "flowmemory.memory_signal.v0", + "signalId": "0x79640fe5901417b9254f9de76f011cdd54ffe9587aaf564f241f4b0cc2ec82da", + "observationId": "0x3711d0558240758a09429d6408eb1e8d699bf2555fb8253ef8192fe5f583f12d", + "pulseId": "0x72407268a2ea62659d6b0f62800931936cc6ea7ea5f5b6db91801ba2f8b43eab", + "rootfieldId": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "signalType": "root_commitment", + "status": "finalized", + "chainId": "8453", + "emittingContract": "0x2a7add68a1d45c3251e2f92ffe4926124654a97c", + "blockNumber": "45955533", + "blockHash": "0x7fc3c3aa61c897fc71fdf7d065a1bda685442cb2096c546d2d1dba4da3339b52", + "txHash": "0x24a43789ef489dd6c697567466944a210273e46c333e7be878cda6df9acb8e7a", + "transactionIndex": "131", + "logIndex": "364", + "actor": "0x3a6fba5a78216ba3a8da8d8f501dee2c8186aff9", + "subject": "0x4a7b8601c06c20bcc7b69c05c51980c12dbd50cbd95a59f460d40555bfc37ce3", + "commitment": "0xe331e74d3287b677c4eee492f00c22efd12f9365070eca9569f78966e499e039", + "parentPulseId": "0xa62ffb4b36a415032949138edbdcba5005de2e35952df88bdf592d4266184b87", + "sequence": "2", + "occurredAt": "2026-05-13T19:26:53.000Z", + "uri": "flowmemory://base-canary/root", + "summary": "root commitment canary pulse 2", + "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": "0x2a7add68a1d45c3251e2f92ffe4926124654a97c", + "pulseTypeId": "2", + "pulseTypeName": "ROOT_COMMITTED", + "indexed": { + "pulseId": "0x72407268a2ea62659d6b0f62800931936cc6ea7ea5f5b6db91801ba2f8b43eab", + "rootfieldId": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "actor": "0x3a6fba5a78216ba3a8da8d8f501dee2c8186aff9" + }, + "payload": { + "subject": "0x4a7b8601c06c20bcc7b69c05c51980c12dbd50cbd95a59f460d40555bfc37ce3", + "commitment": "0xe331e74d3287b677c4eee492f00c22efd12f9365070eca9569f78966e499e039", + "parentPulseId": "0xa62ffb4b36a415032949138edbdcba5005de2e35952df88bdf592d4266184b87", + "sequence": "2", + "occurredAt": "2026-05-13T19:26:53.000Z", + "uri": "flowmemory://base-canary/root" + }, + "receiptLocator": { + "chainId": "8453", + "blockNumber": "45955533", + "blockHash": "0x7fc3c3aa61c897fc71fdf7d065a1bda685442cb2096c546d2d1dba4da3339b52", + "txHash": "0x24a43789ef489dd6c697567466944a210273e46c333e7be878cda6df9acb8e7a", + "transactionIndex": "131", + "logIndex": "364", + "receiptStatus": "success" + }, + "receiptDerivedFields": [ + "blockHash", + "txHash", + "transactionIndex", + "logIndex", + "receiptStatus" + ] + }, + "id": "0x79640fe5901417b9254f9de76f011cdd54ffe9587aaf564f241f4b0cc2ec82da", + "lastUpdated": "2026-05-13T20:05:51.625Z", + "provenance": { + "subsystem": "indexer", + "origin": "live", + "chainContext": "base-mainnet-canary", + "fixturePath": "fixtures/deployments/base-canary-indexer-state.json", + "capturedAt": "2026-05-13T20:05:51.625Z", + "localPathHint": "fixtures/deployments/base-canary-indexer-state.json" + } + }, + { + "schema": "flowmemory.memory_signal.v0", + "signalId": "0x623c146bc8899c5edadaed215cc86a1292f1f5c97acb245e86c0462190078ca5", + "observationId": "0xf3a1c63cf9eb89e59970b1266f8691142349d2af230a7654abcf91fcccef0da6", + "pulseId": "0x16c2adf5f3e46ee91d16a432d2420c566851b311e767860cab99068dcaca2591", + "rootfieldId": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "signalType": "swap_memory_signal", + "status": "finalized", + "chainId": "8453", + "emittingContract": "0x179df6d52e9def5d02704583a2e4e5a9ff427245", + "blockNumber": "45955535", + "blockHash": "0x0510d6a627884b7b3677a7223a2720d1e33f5f4e58f2c0c6c7baf27bcec25d7c", + "txHash": "0xaee21f6d0e9df1a45eae0c7714a4f8eae7fb72afbb07dd67b3a1f0ff724a014f", + "transactionIndex": "194", + "logIndex": "1212", + "actor": "0x3a6fba5a78216ba3a8da8d8f501dee2c8186aff9", + "subject": "0x205b396f8fdc1b25ddef7afa02f31f51f1e1048004897b87477569a8e573d434", + "commitment": "0x30055afe075a7c6ea8557ea3a2d3c7012d9d558ebda95803726179355f98ede9", + "parentPulseId": "0x0000000000000000000000000000000000000000000000000000000000000000", + "sequence": "2", + "occurredAt": "2026-05-13T19:26:57.000Z", + "uri": "flowmemory://uniswap-v4/after-swap", + "summary": "swap memory signal canary pulse 2", + "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": "0x179df6d52e9def5d02704583a2e4e5a9ff427245", + "pulseTypeId": "4", + "pulseTypeName": "SWAP_MEMORY_SIGNAL", + "indexed": { + "pulseId": "0x16c2adf5f3e46ee91d16a432d2420c566851b311e767860cab99068dcaca2591", + "rootfieldId": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "actor": "0x3a6fba5a78216ba3a8da8d8f501dee2c8186aff9" + }, + "payload": { + "subject": "0x205b396f8fdc1b25ddef7afa02f31f51f1e1048004897b87477569a8e573d434", + "commitment": "0x30055afe075a7c6ea8557ea3a2d3c7012d9d558ebda95803726179355f98ede9", + "parentPulseId": "0x0000000000000000000000000000000000000000000000000000000000000000", + "sequence": "2", + "occurredAt": "2026-05-13T19:26:57.000Z", + "uri": "flowmemory://uniswap-v4/after-swap" + }, + "receiptLocator": { + "chainId": "8453", + "blockNumber": "45955535", + "blockHash": "0x0510d6a627884b7b3677a7223a2720d1e33f5f4e58f2c0c6c7baf27bcec25d7c", + "txHash": "0xaee21f6d0e9df1a45eae0c7714a4f8eae7fb72afbb07dd67b3a1f0ff724a014f", + "transactionIndex": "194", + "logIndex": "1212", + "receiptStatus": "success" + }, + "receiptDerivedFields": [ + "blockHash", + "txHash", + "transactionIndex", + "logIndex", + "receiptStatus" + ] + }, + "id": "0x623c146bc8899c5edadaed215cc86a1292f1f5c97acb245e86c0462190078ca5", + "lastUpdated": "2026-05-13T20:05:51.625Z", + "provenance": { + "subsystem": "indexer", + "origin": "live", + "chainContext": "base-mainnet-canary", + "fixturePath": "fixtures/deployments/base-canary-indexer-state.json", + "capturedAt": "2026-05-13T20:05:51.625Z", + "localPathHint": "fixtures/deployments/base-canary-indexer-state.json" + } + } + ], + "memoryReceipts": [], + "rootfieldBundles": [ + { + "schema": "flowmemory.rootfield_bundle.v0", + "id": "0xac66336871730498f32d07d63e2c753cb60dbd4363113b011a81a52cdeafa9be", + "bundleId": "0xac66336871730498f32d07d63e2c753cb60dbd4363113b011a81a52cdeafa9be", + "rootfieldId": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "latestRoot": "0x4a7b8601c06c20bcc7b69c05c51980c12dbd50cbd95a59f460d40555bfc37ce3", + "latestTransitionId": "0x91d0da9bc41fad8cb3fa66b72ba804f6685d34f0c167bd3fbf1ac48f91402188", + "status": "finalized", + "transitionIds": [ + "0xef6e3d4a6375fdaf3573db4550b7a6c4ac535f6ddbf8a2759014c7e6ee5dc17d", + "0x7da37f62f68f29fdd92dde90fdb5783bce555c868bfb0181187ae955eb2d8c95", + "0x508409804fe0dae77e8ced57cb45968877fcbad6f525a99b4f911ce58759eab7", + "0x91d0da9bc41fad8cb3fa66b72ba804f6685d34f0c167bd3fbf1ac48f91402188" + ], + "memorySignalIds": [ + "0x1d530aa927338513f49fbd12145d05b1428347b5cfa0fb241033a9d070123637", + "0x4dbf295239f26095033ddbc39dbb3d2780a554ca697d3ea89c588bda449939d1", + "0x79640fe5901417b9254f9de76f011cdd54ffe9587aaf564f241f4b0cc2ec82da", + "0x623c146bc8899c5edadaed215cc86a1292f1f5c97acb245e86c0462190078ca5" + ], + "memoryReceiptIds": [], + "verifierReportIds": [], + "counts": { + "observations": 4, + "transitions": 4, + "receipts": 0, + "verified": 0, + "failed": 0, + "unresolved": 0, + "unsupported": 0, + "reorged": 0 + }, + "lastUpdated": "2026-05-13T20:05:51.625Z", + "provenance": { + "subsystem": "indexer", + "origin": "live", + "chainContext": "base-mainnet-canary", + "fixturePath": "fixtures/deployments/base-canary-indexer-state.json", + "capturedAt": "2026-05-13T20:05:51.625Z", + "localPathHint": "fixtures/deployments/base-canary-indexer-state.json" + } + } + ], + "agentMemoryViews": [ + { + "schema": "flowmemory.agent_memory_view.v0", + "id": "0x8cf5bf3090421e6040d0a483ab3ea97cd6bb0ae4b728f15e3aa3864ccfa089db", + "viewId": "0x8cf5bf3090421e6040d0a483ab3ea97cd6bb0ae4b728f15e3aa3864ccfa089db", + "rootfieldId": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "status": "finalized", + "latestRoot": "0x4a7b8601c06c20bcc7b69c05c51980c12dbd50cbd95a59f460d40555bfc37ce3", + "latestTransitionId": "0x91d0da9bc41fad8cb3fa66b72ba804f6685d34f0c167bd3fbf1ac48f91402188", + "signalIds": [ + "0x1d530aa927338513f49fbd12145d05b1428347b5cfa0fb241033a9d070123637", + "0x4dbf295239f26095033ddbc39dbb3d2780a554ca697d3ea89c588bda449939d1", + "0x79640fe5901417b9254f9de76f011cdd54ffe9587aaf564f241f4b0cc2ec82da", + "0x623c146bc8899c5edadaed215cc86a1292f1f5c97acb245e86c0462190078ca5" + ], + "receiptIds": [], + "transitionIds": [ + "0xef6e3d4a6375fdaf3573db4550b7a6c4ac535f6ddbf8a2759014c7e6ee5dc17d", + "0x7da37f62f68f29fdd92dde90fdb5783bce555c868bfb0181187ae955eb2d8c95", + "0x508409804fe0dae77e8ced57cb45968877fcbad6f525a99b4f911ce58759eab7", + "0x91d0da9bc41fad8cb3fa66b72ba804f6685d34f0c167bd3fbf1ac48f91402188" + ], + "warnings": [ + "Canary data is live-read but not verifier-backed.", + "Source verification and operator policy must be completed before any production claim." + ], + "localOnly": false, + "lastUpdated": "2026-05-13T20:05:51.625Z", + "provenance": { + "subsystem": "worker", + "origin": "live", + "chainContext": "base-mainnet-canary", + "fixturePath": "fixtures/deployments/base-canary-indexer-state.json", + "capturedAt": "2026-05-13T20:05:51.625Z", + "localPathHint": "fixtures/deployments/base-canary-indexer-state.json" + } + } + ], + "devnetBlocks": [], + "hardwareNodes": [], + "alerts": [ + { + "id": "0xa7ab0032c5ec7ec344b72b60aed3707a9ae590b1d9bac121e78a1463deb473ba", + "incidentId": "0xa7ab0032c5ec7ec344b72b60aed3707a9ae590b1d9bac121e78a1463deb473ba", + "severity": "info", + "title": "Canary mode", + "summary": "Base mainnet canary logs are visible, but verifier reports, source verification, multisig ownership, and production hook wiring are not complete.", + "openedAt": "2026-05-13T20:05:51.625Z", + "linkedObjectIds": [ + "0x2a7ADd68a1d45C3251E2F92fFe4926124654a97C", + "0x179Df6d52e9DeF5D02704583a2E4E5a9FF427245", + "0x8F074d0F4e66975b740A4b7a316330c9660a485E", + "0x3360689009685eade15c876855D24161b05829C1", + "0x94ba7aA4562f8F8528C327378F6352350f6ddB5B", + "0xa8c07eF53Eeb4e57297ee35025a9cD5303fCCD29", + "0xAf920ca7436Bb72172E27C96E0B716f01dcC5DBd", + "0x2874cee0D581E4562ac9015BfCf330f1ea58a1F3", + "0x95bC7455AdFD60e1B908ba455c25Ae732C1Ef996", + "0xa752e9bC7fAf39f659110D8Cf408E7707db94E34" + ], + "recommendedAction": "Use this view for launch demonstrations and operator review only.", + "status": "unresolved", + "lastUpdated": "2026-05-13T20:05:51.625Z", + "provenance": { + "subsystem": "alerts", + "origin": "live", + "chainContext": "base-mainnet-canary", + "fixturePath": "fixtures/deployments/base-canary-indexer-state.json", + "capturedAt": "2026-05-13T20:05:51.625Z", + "localPathHint": "fixtures/deployments/base-canary-v0.json" + } + } + ] +} diff --git a/apps/dashboard/scripts/sync-fixtures.mjs b/apps/dashboard/scripts/sync-fixtures.mjs index 46a84a45..c576d3f1 100644 --- a/apps/dashboard/scripts/sync-fixtures.mjs +++ b/apps/dashboard/scripts/sync-fixtures.mjs @@ -1,14 +1,22 @@ -import { copyFileSync, mkdirSync } from "node:fs"; +import { copyFileSync, existsSync, mkdirSync } from "node:fs"; import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; const scriptDir = dirname(fileURLToPath(import.meta.url)); const repoRoot = resolve(scriptDir, "../../.."); -const source = resolve(repoRoot, "fixtures/dashboard/flowmemory-dashboard-v0.json"); const destinationDir = resolve(repoRoot, "apps/dashboard/public/data"); -const destination = resolve(destinationDir, "flowmemory-dashboard-v0.json"); +const fixtures = [ + "flowmemory-dashboard-v0.json", + "flowmemory-dashboard-base-canary-v0.json", +]; mkdirSync(destinationDir, { recursive: true }); -copyFileSync(source, destination); -console.log(`Synced dashboard fixture: ${destination}`); +for (const fixture of fixtures) { + const source = resolve(repoRoot, "fixtures/dashboard", fixture); + const destination = resolve(destinationDir, fixture); + if (existsSync(source)) { + copyFileSync(source, destination); + console.log(`Synced dashboard fixture: ${destination}`); + } +} diff --git a/apps/dashboard/src/App.tsx b/apps/dashboard/src/App.tsx index 946f5280..aa2be8ab 100644 --- a/apps/dashboard/src/App.tsx +++ b/apps/dashboard/src/App.tsx @@ -2,9 +2,10 @@ import { useEffect, useState } from "react"; import { Route, Routes } from "react-router-dom"; import { AlertTriangle, RefreshCw } from "lucide-react"; import { AppShell } from "./components/AppShell"; -import { fetchDashboardData } from "./data/loadDashboardData"; +import { DEFAULT_CANARY_DASHBOARD_DATA_PATH, fetchDashboardData } from "./data/loadDashboardData"; import type { DashboardData } from "./data/types"; import { AlertsView } from "./views/AlertsView"; +import { CanaryDeploymentView } from "./views/CanaryDeploymentView"; import { DevnetBlocksView } from "./views/DevnetBlocksView"; import { FlowMemoryView } from "./views/FlowMemoryView"; import { FlowPulseStreamView } from "./views/FlowPulseStreamView"; @@ -52,22 +53,27 @@ function ErrorState({ message, onRetry }: { message: string; onRetry: () => void export default function App() { const [data, setData] = useState(null); + const [canaryData, setCanaryData] = useState(null); const [error, setError] = useState(null); const [version, setVersion] = useState(0); useEffect(() => { let cancelled = false; - fetchDashboardData() - .then((nextData) => { + Promise.all([ + fetchDashboardData(), + fetchDashboardData(DEFAULT_CANARY_DASHBOARD_DATA_PATH), + ]) + .then(([nextData, nextCanaryData]) => { if (!cancelled) { setData(nextData); + setCanaryData(nextCanaryData); setError(null); } }) .catch((nextError: unknown) => { if (!cancelled) { - setError(nextError instanceof Error ? nextError.message : "Unknown fixture load error."); + setError(nextError instanceof Error ? nextError.message : "Unknown dashboard data load error."); } }); @@ -80,14 +86,15 @@ export default function App() { return setVersion((current) => current + 1)} />; } - if (data === null) { + if (data === null || canaryData === null) { return ; } return ( - + } /> + } /> } /> } /> } /> diff --git a/apps/dashboard/src/components/AppShell.tsx b/apps/dashboard/src/components/AppShell.tsx index 895b0da1..624d1f2d 100644 --- a/apps/dashboard/src/components/AppShell.tsx +++ b/apps/dashboard/src/components/AppShell.tsx @@ -8,6 +8,7 @@ import { BrainCircuit, Boxes, ClipboardCheck, + RadioReceiver, LayoutDashboard, Network, RadioTower, @@ -18,11 +19,13 @@ import { StatusBadge } from "./StatusBadge"; interface AppShellProps { data: DashboardData; + canaryData?: DashboardData; children: ReactNode; } const NAV_ITEMS = [ { to: "/", label: "Overview", icon: LayoutDashboard }, + { to: "/canary", label: "Base canary", icon: RadioReceiver }, { to: "/flowmemory", label: "Flow Memory", icon: BrainCircuit }, { to: "/flowpulse", label: "FlowPulse", icon: Activity }, { to: "/rootfields", label: "Rootfields", icon: Boxes }, @@ -34,7 +37,7 @@ const NAV_ITEMS = [ { to: "/raw", label: "Raw JSON", icon: Braces }, ]; -export function AppShell({ data, children }: AppShellProps) { +export function AppShell({ data, canaryData, children }: AppShellProps) { return (
diff --git a/apps/dashboard/src/data/loadDashboardData.ts b/apps/dashboard/src/data/loadDashboardData.ts index e976109b..59c6296d 100644 --- a/apps/dashboard/src/data/loadDashboardData.ts +++ b/apps/dashboard/src/data/loadDashboardData.ts @@ -1,6 +1,7 @@ import type { DashboardData } from "./types"; export const DEFAULT_DASHBOARD_DATA_PATH = "/data/flowmemory-dashboard-v0.json"; +export const DEFAULT_CANARY_DASHBOARD_DATA_PATH = "/data/flowmemory-dashboard-base-canary-v0.json"; function assertArray(value: unknown, label: string): void { if (!Array.isArray(value)) { @@ -17,8 +18,8 @@ export function validateDashboardData(payload: unknown): DashboardData { if (candidate.metadata?.schema !== "flowmemory.dashboard.fixture.v0") { throw new Error("Unsupported dashboard fixture schema."); } - if (candidate.metadata.mode !== "fixture") { - throw new Error("Dashboard V0 expects fixture mode data."); + if (candidate.metadata.mode !== "fixture" && candidate.metadata.mode !== "canary") { + throw new Error("Dashboard V0 expects fixture or canary mode data."); } if (candidate.chain === undefined) { throw new Error("Dashboard fixture is missing chain context."); diff --git a/apps/dashboard/src/data/types.ts b/apps/dashboard/src/data/types.ts index 37686c6c..61e190d8 100644 --- a/apps/dashboard/src/data/types.ts +++ b/apps/dashboard/src/data/types.ts @@ -105,10 +105,37 @@ export interface DashboardChainContext { export interface FixtureMetadata { schema: "flowmemory.dashboard.fixture.v0"; generatedAt: string; - mode: "fixture"; + mode: "fixture" | "canary"; description: string; fixturePath: string; runtimeDataPath: string; + canary?: { + deploymentArtifactPath: string; + indexerStatePath: string; + checkpointPath: string; + docsPath: string; + productionReady: false; + readWindow: { + fromBlock: string; + toBlock: string; + finalizedBlock?: string; + }; + counts: { + observations: number; + rejectedLogs: number; + duplicates: number; + contracts: number; + }; + contracts: Array<{ + name: string; + sourceName: string; + address: string; + deployTx: string; + block: string; + emitsFlowPulse: boolean; + }>; + boundaries: string[]; + }; futureGeneratedPaths: { indexer: string; verifier: string; @@ -276,7 +303,7 @@ export interface AgentMemoryView extends ProvenancedRecord { receiptIds: string[]; transitionIds: string[]; warnings: string[]; - localOnly: true; + localOnly: boolean; } export interface DevnetBlock extends ProvenancedRecord { diff --git a/apps/dashboard/src/styles.css b/apps/dashboard/src/styles.css index 93d9e3fc..baac96ed 100644 --- a/apps/dashboard/src/styles.css +++ b/apps/dashboard/src/styles.css @@ -379,6 +379,66 @@ small { background: rgba(251, 252, 248, 0.72); } +.canary-hero { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(300px, 0.36fr); + gap: 14px; + align-items: stretch; + padding: 16px; + border: 1px solid #c9d5ce; + border-radius: 8px; + background: + linear-gradient(135deg, rgba(251, 252, 248, 0.94), rgba(229, 239, 235, 0.92)), + linear-gradient(90deg, rgba(47, 125, 107, 0.18), transparent); + box-shadow: var(--shadow); +} + +.canary-hero h2 { + max-width: 760px; + margin: 0; + font-size: 1.52rem; + line-height: 1.14; +} + +.canary-hero p { + max-width: 84ch; + margin: 10px 0 0; + color: #465047; + line-height: 1.5; +} + +.canary-read-window { + display: grid; + grid-template-columns: 28px 1fr; + gap: 10px; + align-items: start; + padding: 12px; + border: 1px solid #bfd1c7; + border-radius: 8px; + background: rgba(251, 252, 248, 0.78); +} + +.canary-read-window dl { + display: grid; + gap: 9px; + margin: 0; +} + +.canary-boundaries { + display: grid; + gap: 8px; + padding-top: 12px; +} + +.canary-boundaries span { + padding: 9px 10px; + color: #4d564d; + border: 1px solid var(--line); + border-radius: 7px; + background: #f8f9f4; + line-height: 1.35; +} + .metric-tile, .panel, .table-panel, @@ -976,6 +1036,7 @@ dd { } .flowmemory-hero, + .canary-hero, .flowmemory-spine, .contract-event-list dl, .bundle-grid { diff --git a/apps/dashboard/src/test/dashboardData.test.ts b/apps/dashboard/src/test/dashboardData.test.ts index 889eb94a..a7734198 100644 --- a/apps/dashboard/src/test/dashboardData.test.ts +++ b/apps/dashboard/src/test/dashboardData.test.ts @@ -1,4 +1,5 @@ import { describe, expect, it } from "vitest"; +import canaryFixture from "../../../../fixtures/dashboard/flowmemory-dashboard-base-canary-v0.json"; import fixture from "../../../../fixtures/dashboard/flowmemory-dashboard-v0.json"; import { validateDashboardData } from "../data/loadDashboardData"; import { DASHBOARD_STATUSES } from "../data/status"; @@ -7,6 +8,7 @@ import type { DashboardData, ProvenancedRecord } from "../data/types"; describe("dashboard fixture", () => { const data = validateDashboardData(fixture) as DashboardData; + const canaryData = validateDashboardData(canaryFixture) as DashboardData; it("loads the V0 dashboard fixture shape", () => { expect(data.metadata.schema).toBe("flowmemory.dashboard.fixture.v0"); @@ -19,6 +21,18 @@ describe("dashboard fixture", () => { expect(data.rootflowTransitions.every((transition) => transition.contractEventRef.signalId === transition.memorySignalId)).toBe(true); }); + it("loads the Base canary dashboard mode separately from local fixtures", () => { + expect(canaryData.metadata.schema).toBe("flowmemory.dashboard.fixture.v0"); + expect(canaryData.metadata.mode).toBe("canary"); + expect(canaryData.metadata.canary?.productionReady).toBe(false); + expect(canaryData.chain.environment).toBe("mainnet"); + expect(canaryData.chain.source).toBe("live"); + expect(canaryData.flowPulseObservations).toHaveLength(4); + expect(canaryData.verifierReports).toHaveLength(0); + expect(canaryData.memorySignals.some((signal) => signal.signalType === "swap_memory_signal")).toBe(true); + expect(canaryData.agentMemoryViews.every((view) => view.localOnly === false)).toBe(true); + }); + it("covers every required dashboard status", () => { const records: ProvenancedRecord[] = [ ...data.flowPulseObservations, diff --git a/apps/dashboard/src/views/CanaryDeploymentView.tsx b/apps/dashboard/src/views/CanaryDeploymentView.tsx new file mode 100644 index 00000000..bf9478b2 --- /dev/null +++ b/apps/dashboard/src/views/CanaryDeploymentView.tsx @@ -0,0 +1,241 @@ +import { Activity, AlertTriangle, Boxes, Braces, RadioReceiver, ShieldAlert } from "lucide-react"; +import { EmptyState } from "../components/EmptyState"; +import { HashValue } from "../components/HashValue"; +import { ProvenanceLine } from "../components/ProvenanceLine"; +import { SectionHeader } from "../components/SectionHeader"; +import { StatusBadge } from "../components/StatusBadge"; +import { formatDateTime } from "../data/format"; +import type { DashboardData } from "../data/types"; + +function canaryCounts(data: DashboardData) { + const swapSignals = data.memorySignals.filter((signal) => signal.signalType === "swap_memory_signal").length; + return [ + { + label: "Canary pulses", + value: data.flowPulseObservations.length.toString(), + detail: `${swapSignals} swap memory signal(s)`, + status: "finalized" as const, + }, + { + label: "Rootfields", + value: data.rootfields.length.toString(), + detail: `${data.rootflowTransitions.length} Rootflow transitions`, + status: "observed" as const, + }, + { + label: "Rejected logs", + value: String(data.metadata.canary?.counts.rejectedLogs ?? 0), + detail: "guarded reader result", + status: (data.metadata.canary?.counts.rejectedLogs ?? 0) > 0 ? "failed" as const : "verified" as const, + }, + { + label: "Duplicates", + value: String(data.metadata.canary?.counts.duplicates ?? 0), + detail: "reader duplicate policy", + status: (data.metadata.canary?.counts.duplicates ?? 0) > 0 ? "unresolved" as const : "verified" as const, + }, + { + label: "Source verified", + value: "pending", + detail: "automation ready; API key required", + status: "unresolved" as const, + }, + ]; +} + +export function CanaryDeploymentView({ data }: { data: DashboardData }) { + const canary = data.metadata.canary; + const counts = canaryCounts(data); + const flowPulseContracts = canary?.contracts.filter((contract) => contract.emitsFlowPulse) ?? []; + + return ( +
+ + +
+
+ guardrail +

Visible on Base, still gated for production

+

+ The dashboard is reading committed canary artifacts generated by the Base mainnet canary reader. + Verifier reports, source verification completion, multisig ownership, and real Uniswap v4 hook wiring + remain separate launch gates. +

+
+
+
+
+ +
+ {counts.map((metric) => ( +
+ {metric.label} + {metric.value} +
+ + {metric.detail} +
+
+ ))} +
+ +
+
+
+
+
+ {data.flowPulseObservations.length} observed +
+ {data.flowPulseObservations.length > 0 ? ( +
+ {data.flowPulseObservations.map((observation) => ( +
+
+
+ + +
+

{observation.summary}

+ +
+
+
+
block
+
{observation.blockNumber}
+
+
+
type
+
{observation.pulseType}
+
+
+
tx
+
+ +
+
+
+
log
+
{observation.logIndex}
+
+
+
+ ))} +
+ ) : ( + + )} +
+ +
+
+
+
+ {flowPulseContracts.length} sources +
+
+ {flowPulseContracts.map((contract) => ( +
+ +
+ {contract.name} + + + +
+
+ ))} +
+
+ +
+
+
+
+ not production +
+
+ {(canary?.boundaries ?? []).map((boundary) => ( + {boundary} + ))} +
+
+ +
+
+
+
+ {data.rootflowTransitions.length} transitions +
+
+ {data.rootflowTransitions.map((transition) => ( +
+
+ + + transition + +
+
+
+
pulse
+
+ +
+
+
+
next root
+
+ +
+
+
+
reason
+
{transition.reasonCodes.join(", ")}
+
+
+
+ ))} +
+
+
+ +
+
+
+
{JSON.stringify(data, null, 2)}
+
+
+ ); +} diff --git a/contracts/ACCESS_CONTROL_REVIEW.md b/contracts/ACCESS_CONTROL_REVIEW.md index b38f532c..48b6298e 100644 --- a/contracts/ACCESS_CONTROL_REVIEW.md +++ b/contracts/ACCESS_CONTROL_REVIEW.md @@ -58,6 +58,7 @@ Current protections: Launch risk to watch: - deployer is permanent owner in V0; there is no multisig, timelock, or owner transfer. +- see `docs/OPERATIONS/V0_OPERATOR_POLICY.md` for the current canary operator policy and production gates. - allowlists are coordination controls, not decentralized verifier consensus. ## Self-Registration Registries @@ -114,7 +115,7 @@ Current boundary: - `ReceiptVerifier` accepts first-writer receipt-report commitments and does not cryptographically verify receipts. - `WorkDebtScheduler` allows any scheduler to assign work to a nonzero worker and allows scheduler or worker to mark completion. -- `FlowMemoryHookAdapter` validates nonzero inputs and emits an observation event; it is not a production Uniswap v4 hook. +- `FlowMemoryHookAdapter` validates nonzero inputs and emits an observation event. It also exposes a dependency-light Uniswap v4-shaped `afterSwap` callback path, but it is not a production Uniswap v4 hook deployment. Launch risk to watch: diff --git a/contracts/DEPLOYMENT_BOUNDARY.md b/contracts/DEPLOYMENT_BOUNDARY.md index abbbc091..e3b6ce81 100644 --- a/contracts/DEPLOYMENT_BOUNDARY.md +++ b/contracts/DEPLOYMENT_BOUNDARY.md @@ -41,6 +41,7 @@ Private keys must not be committed to the repo, copied into docs, or stored in g npm run deploy:base-sepolia npm run deploy:base-sepolia:broadcast npm run read:base-sepolia -- --rpc-url --address --from-block --to-block +npm run verify:base-canary:sources -- --json ``` `deploy:base-sepolia` requires `BASE_SEPOLIA_RPC_URL` and @@ -48,10 +49,17 @@ npm run read:base-sepolia -- --rpc-url --address uint64 sequence) private _rootfieldSequences; @@ -18,6 +20,7 @@ contract FlowMemoryHookAdapter is IFlowMemoryHookAdapter, IFlowPulse { error ZeroPoolId(); error ZeroRootfieldId(); error ZeroCommitment(); + error EmptyHookData(); error TimestampOverflow(uint256 timestamp); function afterSwap(address sender, bytes32 poolId, bytes32 rootfieldId, bytes32 commitment, bytes calldata hookData) @@ -29,6 +32,64 @@ contract FlowMemoryHookAdapter is IFlowMemoryHookAdapter, IFlowPulse { if (rootfieldId == bytes32(0)) revert ZeroRootfieldId(); if (commitment == bytes32(0)) revert ZeroCommitment(); + _emitSwapMemorySignal(sender, poolId, rootfieldId, commitment, bytes32(0), hookData, msg.sender, "flowmemory://uniswap-v4/after-swap"); + return AFTER_SWAP_SELECTOR; + } + + function afterSwap( + address sender, + IUniswapV4SwapHookLike.PoolKey calldata key, + IUniswapV4SwapHookLike.SwapParams calldata params, + int256 swapDelta, + bytes calldata hookData + ) external returns (bytes4 selector, int128 hookDelta) { + if (sender == address(0)) revert ZeroSender(); + if (hookData.length == 0) revert EmptyHookData(); + + FlowMemorySwapHookData memory decoded = abi.decode(hookData, (FlowMemorySwapHookData)); + bytes32 poolId = _poolIdFor(key); + bytes memory pulseContext = abi.encode(params.zeroForOne, params.amountSpecified, params.sqrtPriceLimitX96, swapDelta, hookData); + _emitSwapMemorySignal( + sender, + poolId, + decoded.rootfieldId, + decoded.commitment, + decoded.parentPulseId, + pulseContext, + msg.sender, + bytes(decoded.uri).length == 0 ? "flowmemory://uniswap-v4/after-swap" : decoded.uri + ); + + return (UNISWAP_V4_AFTER_SWAP_SELECTOR, 0); + } + + function encodeSwapHookData(bytes32 rootfieldId, bytes32 commitment, bytes32 parentPulseId, string calldata uri) + external + pure + returns (bytes memory hookData) + { + return abi.encode(FlowMemorySwapHookData({ + rootfieldId: rootfieldId, + commitment: commitment, + parentPulseId: parentPulseId, + uri: uri + })); + } + + function _emitSwapMemorySignal( + address sender, + bytes32 poolId, + bytes32 rootfieldId, + bytes32 commitment, + bytes32 parentPulseId, + bytes memory hookData, + address caller, + string memory uri + ) private { + if (poolId == bytes32(0)) revert ZeroPoolId(); + if (rootfieldId == bytes32(0)) revert ZeroRootfieldId(); + if (commitment == bytes32(0)) revert ZeroCommitment(); + bytes32 hookDataHash = keccak256(hookData); uint64 sequence = _nextSequence(rootfieldId); uint64 occurredAt = _blockTimestamp(); @@ -37,17 +98,18 @@ contract FlowMemoryHookAdapter is IFlowMemoryHookAdapter, IFlowPulse { FlowPulseTypes.SCHEMA_ID, block.chainid, address(this), - msg.sender, + caller, sender, poolId, rootfieldId, commitment, + parentPulseId, hookDataHash, sequence ) ); - emit AfterSwapObserved(msg.sender, sender, poolId, rootfieldId, commitment, hookDataHash); + emit AfterSwapObserved(caller, sender, poolId, rootfieldId, commitment, hookDataHash); emit FlowPulse( pulseId, rootfieldId, @@ -55,12 +117,15 @@ contract FlowMemoryHookAdapter is IFlowMemoryHookAdapter, IFlowPulse { FlowPulseTypes.SWAP_MEMORY_SIGNAL, poolId, commitment, - bytes32(0), + parentPulseId, sequence, occurredAt, - "flowmemory://uniswap-v4/after-swap" + uri ); - return AFTER_SWAP_SELECTOR; + } + + function _poolIdFor(IUniswapV4SwapHookLike.PoolKey calldata key) private pure returns (bytes32) { + return keccak256(abi.encode(key.currency0, key.currency1, key.fee, key.tickSpacing, key.hooks)); } function _nextSequence(bytes32 rootfieldId) private returns (uint64 sequence) { diff --git a/contracts/interfaces/IFlowMemoryHookAdapter.sol b/contracts/interfaces/IFlowMemoryHookAdapter.sol index bef298a5..fae9356c 100644 --- a/contracts/interfaces/IFlowMemoryHookAdapter.sol +++ b/contracts/interfaces/IFlowMemoryHookAdapter.sol @@ -2,6 +2,13 @@ pragma solidity ^0.8.24; interface IFlowMemoryHookAdapter { + struct FlowMemorySwapHookData { + bytes32 rootfieldId; + bytes32 commitment; + bytes32 parentPulseId; + string uri; + } + event AfterSwapObserved( address indexed caller, address indexed sender, @@ -14,4 +21,9 @@ interface IFlowMemoryHookAdapter { function afterSwap(address sender, bytes32 poolId, bytes32 rootfieldId, bytes32 commitment, bytes calldata hookData) external returns (bytes4 selector); + + function encodeSwapHookData(bytes32 rootfieldId, bytes32 commitment, bytes32 parentPulseId, string calldata uri) + external + pure + returns (bytes memory hookData); } diff --git a/contracts/interfaces/IUniswapV4SwapHookLike.sol b/contracts/interfaces/IUniswapV4SwapHookLike.sol new file mode 100644 index 00000000..fefa5b14 --- /dev/null +++ b/contracts/interfaces/IUniswapV4SwapHookLike.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +/// @notice Minimal ABI-compatible Uniswap v4 swap hook surface used by +/// FlowMemory without vendoring v4-core into the V0 repository. +/// @dev This mirrors the public afterSwap callback shape documented by +/// Uniswap v4. Currency, IHooks, and BalanceDelta are represented by their +/// ABI-compatible primitive forms so this interface remains dependency-light. +interface IUniswapV4SwapHookLike { + struct PoolKey { + address currency0; + address currency1; + uint24 fee; + int24 tickSpacing; + address hooks; + } + + struct SwapParams { + bool zeroForOne; + int256 amountSpecified; + uint160 sqrtPriceLimitX96; + } + + function afterSwap( + address sender, + PoolKey calldata key, + SwapParams calldata params, + int256 swapDelta, + bytes calldata hookData + ) external returns (bytes4 selector, int128 hookDelta); +} diff --git a/docs/CURRENT_STATE.md b/docs/CURRENT_STATE.md index 09838602..837db639 100644 --- a/docs/CURRENT_STATE.md +++ b/docs/CURRENT_STATE.md @@ -58,6 +58,9 @@ Indexer/verifier local package: - `npm run index:base-canary -- --acknowledge-mainnet-canary --rpc-url --address --from-block --to-block ` provides a guarded Base mainnet canary reader path for the documented V0 canary deployment only. - The Base canary reader requires explicit acknowledgement, RPC URL, addresses, and block range; rejects non-Base-mainnet chain ids; refuses scans wider than 5,000 blocks; persists canonical state plus a durable canary checkpoint; and marks the checkpoint as not production-ready. - A live canary read over blocks `45955500` to `45955540` observed 4 FlowPulse logs from the documented `RootfieldRegistry` and `FlowMemoryHookAdapter` canary addresses with 0 rejected logs and 0 duplicates. +- `fixtures/deployments/base-canary-v0.json`, committed canary reader output, and `npm run flowmemory:canary-dashboard` now generate a separate Base canary dashboard dataset. +- The dashboard has a separate Base canary mode at `/canary` that shows live-read canary FlowPulse observations, Rootflow transitions, canary boundaries, and raw canary JSON without replacing local fixture mode. +- `npm run verify:base-canary:sources` produces a dry-run source verification plan for all canary contracts and writes `fixtures/deployments/base-canary-source-verification-plan.json`; `npm run verify:base-canary:sources:submit` submits after `BASESCAN_API_KEY` is configured. - `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. - A Base mainnet V0 canary deployment exists for testing only; deployed addresses and smoke transactions are recorded in `docs/DEPLOYMENTS/2026-05-13-base-canary-v0.md`. @@ -118,8 +121,7 @@ Launch-core specifications: - Production indexer or verifier service runtime. - Production persistence layer, production live RPC reader, production APIs, or hosted services. - Broad Base mainnet reader. -- Dashboard ingestion of the Base mainnet canary deployment. -- Contract source verification automation for the deployed canary contracts. +- Completed explorer source verification for the deployed canary contracts. Automation exists, but actual explorer acceptance requires `BASESCAN_API_KEY`. - Explorer or hardware console implementation. - FlowRouter firmware, manufacturing, final enclosure work, or field deployment. - Real Meshtastic or LoRa device integration. @@ -179,7 +181,7 @@ 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. Exercise the guarded Base canary reader against the documented V0 canary addresses and feed its output into the next dashboard canary-ingestion issue. +2. Keep the guarded Base canary reader and dashboard canary artifacts fresh when canary smoke actions change. 3. Exercise the Base Sepolia deploy/read path on explicit testnet contract addresses only. 4. Continue contracts hardening without production mainnet deployment or token mechanics. 5. Keep dashboard work fixture-backed until a production API is explicitly scoped. diff --git a/docs/DECISIONS/2026-05-13-uniswap-v4-shaped-hook-path.md b/docs/DECISIONS/2026-05-13-uniswap-v4-shaped-hook-path.md new file mode 100644 index 00000000..375e6a73 --- /dev/null +++ b/docs/DECISIONS/2026-05-13-uniswap-v4-shaped-hook-path.md @@ -0,0 +1,59 @@ +# Uniswap V4-Shaped Hook Path + +Date: 2026-05-13 + +## Decision + +FlowMemory V0 now keeps the original dependency-light hook adapter method and +adds a Uniswap v4-shaped `afterSwap` callback surface: + +```text +afterSwap(address sender, PoolKey key, SwapParams params, int256 swapDelta, bytes hookData) + -> (bytes4 selector, int128 hookDelta) +``` + +The callback consumes ABI-encoded `FlowMemorySwapHookData`, derives a pool id +from the v4-compatible pool key fields, emits a `SWAP_MEMORY_SIGNAL` FlowPulse, +and returns zero hook delta. + +## Why + +The previous adapter was useful for local tests but did not give reviewers a +real v4 callback shape. The new path makes the next hook integration work much +less abstract while still avoiding a premature dependency or production +deployment claim. + +## Boundaries + +- This is not a production Uniswap v4 hook deployment. +- The repo still does not vendor `v4-core` or `v4-periphery`. +- The callback does not do custom accounting, token custody, dynamic fees, or + LP fee overrides. +- Hook address permission mining, PoolManager deployment wiring, and production + pool configuration remain gated. +- Contracts still cannot know `txHash` or `logIndex`; indexers derive those + after receipts and logs exist. + +## Production Hook Permission Target + +When this graduates from adapter-first V0 to a real PoolManager hook, the first +permission target should be `afterSwap` only: + +- no `beforeSwap`; +- no custom accounting return delta; +- no dynamic LP fee override; +- no token custody; +- no pool creation or liquidity-position behavior. + +That production hook must be permission-address-mined for Uniswap v4's hook +flags and deployed through a dedicated script before any pool can use it. The +current canary adapter address is not claimed to satisfy those production hook +address flags. + +## Sources Checked + +- [Uniswap v4 docs](https://docs.uniswap.org/contracts/v4/concepts/hooks) + describe `beforeSwap` and `afterSwap` as swap hook callbacks. +- [Uniswap v4 core Hooks library](https://github.com/Uniswap/v4-core/blob/main/src/libraries/Hooks.sol) + calls `IHooks.afterSwap` after swap execution and accounts for hook delta + separately. diff --git a/docs/DEPLOYMENTS/2026-05-13-base-canary-v0.md b/docs/DEPLOYMENTS/2026-05-13-base-canary-v0.md index 7cdf868d..460113ef 100644 --- a/docs/DEPLOYMENTS/2026-05-13-base-canary-v0.md +++ b/docs/DEPLOYMENTS/2026-05-13-base-canary-v0.md @@ -121,16 +121,59 @@ The canary reader refuses non-Base-mainnet RPC endpoints, refuses scans wider than 5,000 blocks, stores no RPC URLs or private keys, and marks checkpoint output as not production-ready. +Committed deployment and dashboard artifacts: + +- `fixtures/deployments/base-canary-v0.json` +- `fixtures/deployments/base-canary-indexer-state.json` +- `fixtures/deployments/base-canary-indexer-checkpoint.json` +- `fixtures/dashboard/flowmemory-dashboard-base-canary-v0.json` +- `apps/dashboard/public/data/flowmemory-dashboard-base-canary-v0.json` + +Regenerate the canary dashboard data after refreshing the guarded reader output: + +```powershell +npm run flowmemory:canary-dashboard +``` + +The dashboard exposes this as a separate Base canary mode. It stays separate +from the local fixture mode so live canary reads do not get mistaken for local +acceptance fixtures or production readiness. + +## Source Verification Automation + +Dry-run the source verification plan: + +```powershell +npm run verify:base-canary:sources -- --json +``` + +Submit to the configured explorer after setting `BASESCAN_API_KEY`: + +```powershell +$env:BASESCAN_API_KEY="" +npm run verify:base-canary:sources:submit +``` + +Optional bytecode check before submitting: + +```powershell +npm run verify:base-canary:sources -- --check-bytecode --rpc-url https://mainnet.base.org +``` + +The script uses `fixtures/deployments/base-canary-v0.json`, requires no private +key, redacts the API key in generated plans, and writes a non-secret report to +`fixtures/deployments/base-canary-source-verification-plan.json` by default. + ## Important Gaps Found -1. The dashboard still consumes generated fixtures. It does not yet ingest a - deployment artifact plus live read output. -2. Contract source verification is not automated for all deployed contracts. -3. `FlowMemoryHookAdapter` is still an adapter scaffold. It is not a production - Uniswap v4 hook wired into PoolManager permissions. -4. Ownership is still direct deployer ownership where applicable. There is no +1. Contract source verification automation exists, but actual submission + requires `BASESCAN_API_KEY` and explorer acceptance. +2. `FlowMemoryHookAdapter` now exposes a dependency-light Uniswap v4-shaped + `afterSwap` callback path, but it is not a production hook deployment wired + into PoolManager permissions. +3. Ownership is still direct deployer ownership where applicable. There is no multisig, governance, recovery, or operational key policy. -5. Verifier and worker registry flows are deployed, but live verifier report +4. Verifier and worker registry flows are deployed, but live verifier report submission, report signing, and verifier economics are not built. ## Notes diff --git a/docs/OPERATIONS/V0_OPERATOR_POLICY.md b/docs/OPERATIONS/V0_OPERATOR_POLICY.md new file mode 100644 index 00000000..6c17302d --- /dev/null +++ b/docs/OPERATIONS/V0_OPERATOR_POLICY.md @@ -0,0 +1,62 @@ +# V0 Operator Policy + +Date: 2026-05-13 + +Status: launch canary policy. This is not a governance, multisig, token, upgrade, +or production-operations policy. + +## Scope + +This policy covers the current V0 contracts documented in: + +- `fixtures/deployments/base-canary-v0.json` +- `docs/DEPLOYMENTS/2026-05-13-base-canary-v0.md` + +It exists so operators and agents do not confuse a live Base canary deployment +with production readiness. + +## Current Operator Model + +- The canary deployer is `0x3A6fBA5a78216ba3a8DA8d8F501dee2C8186aFf9`. +- V0 contracts are independently deployed implementation contracts. +- There are no proxies, upgrade admins, timelocks, token roles, fee roles, or + governance roles in the canary package. +- Registry owner-style permissions remain direct V0 ownership or caller + self-registration as implemented by each contract. +- Worker and verifier authorization lists are local/test operational surfaces, + not a production verifier network. + +## Required Before Any Production Claim + +Production language remains blocked until a later decision records: + +1. source verification completed for every deployed contract; +2. operational owner separation for deployer, worker admin, verifier admin, and + emergency response; +3. multisig or comparable account-control decision; +4. key-rotation and lost-key recovery plan; +5. public incident and rollback/redeploy runbook; +6. verifier/report signing policy; +7. Uniswap v4 hook permission and PoolManager integration review; +8. explicit go/no-go approval in `docs/DECISIONS/`. + +## Agent Rules + +- Do not reuse the deployer key in CI. +- Do not commit RPC URLs, API keys, private keys, mnemonics, or wallet exports. +- Do not add token, fee, custody, upgrade, or governance behavior to close a + canary issue. +- If a script needs a private service credential, read it from an environment + variable and make dry-run output useful without the credential. +- If live chain state differs from a document, update the deployment artifact + and docs with the observed state rather than hiding the difference. + +## Current Gaps + +- Source verification automation exists, but actual submission requires + `BASESCAN_API_KEY`. +- The canary deployer is still a single account. +- There is no multisig, timelock, recovery, or operator separation. +- `FlowMemoryHookAdapter` remains a hook-adjacent adapter, not a production + Uniswap v4 hook wired to PoolManager permissions. +- Live canary dashboard data is not verifier-backed. diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 5e39b264..cbe32080 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -68,6 +68,8 @@ Status: implemented as fixture-first services plus generated launch-core state; - Deterministic persistence exists for fixture state, the constrained Base Sepolia reader checkpoint, and the guarded Base mainnet canary checkpoint. - A Base Sepolia reader path exists for explicit RPC URLs and explicit FlowPulse contract addresses; it rejects non-Base-Sepolia chain ids. - A guarded Base mainnet canary reader exists for explicit RPC URLs, explicit known canary addresses, and small explicit block ranges; it rejects non-Base-mainnet chain ids and marks output as canary-only. +- A separate dashboard canary mode exists for committed Base canary reader output and deployment artifacts. +- Source verification automation exists for the canary contract set, with real submission gated by `BASESCAN_API_KEY`. - Base Sepolia deploy/read commands exist for the current V0 testnet contract set. - A Base mainnet V0 canary deployment has been performed for testing only and is documented under `docs/DEPLOYMENTS/`. - Runtime schema validation and generated fixture drift checks exist for launch-core outputs. @@ -143,8 +145,8 @@ The initial merge sequence has completed for repo OS, contracts foundation, cryp Next merge preference: -1. Deployment-artifact ingestion for the guarded Base canary reader output. -2. Base Sepolia reader soak tests against explicit testnet deployments. -3. Dashboard live/canary mode separation from generated fixtures. -4. Static analysis follow-up findings triaged for any public testnet deployment. +1. Base Sepolia reader soak tests against explicit testnet deployments. +2. Actual source verification submission for Base canary contracts when `BASESCAN_API_KEY` is available. +3. Static analysis follow-up findings triaged for any public testnet deployment. +4. Operator ownership separation and multisig/recovery decision before further live deployments. 5. Production-gated research only after V0 local acceptance stays green. diff --git a/docs/SECURITY_MODEL.md b/docs/SECURITY_MODEL.md index 7edaa4b2..820bd741 100644 --- a/docs/SECURITY_MODEL.md +++ b/docs/SECURITY_MODEL.md @@ -149,6 +149,18 @@ Static analysis preparation: - 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. +### V0 Operator Policy + +The live Base canary deployment uses direct V0 ownership and caller +self-registration surfaces. It is intentionally not a governance, multisig, +upgrade, token, or production-operations model. + +Current operator rules and production-claim blockers live in +`docs/OPERATIONS/V0_OPERATOR_POLICY.md`. In short: source verification, +operator separation, multisig or comparable account-control decisions, +key-rotation/recovery, verifier signing policy, PoolManager hook wiring, and a +recorded go/no-go decision are required before any production claim. + ## PR Security Checklist - Does this change introduce or require secrets? diff --git a/fixtures/dashboard/flowmemory-dashboard-base-canary-v0.json b/fixtures/dashboard/flowmemory-dashboard-base-canary-v0.json new file mode 100644 index 00000000..d6ac2de2 --- /dev/null +++ b/fixtures/dashboard/flowmemory-dashboard-base-canary-v0.json @@ -0,0 +1,891 @@ +{ + "metadata": { + "schema": "flowmemory.dashboard.fixture.v0", + "generatedAt": "2026-05-13T20:05:51.625Z", + "mode": "canary", + "description": "Generated Base mainnet canary dashboard data from the guarded FlowPulse reader. It is canary-only and not a production-readiness claim.", + "fixturePath": "fixtures/dashboard/flowmemory-dashboard-base-canary-v0.json", + "runtimeDataPath": "apps/dashboard/public/data/flowmemory-dashboard-base-canary-v0.json", + "canary": { + "deploymentArtifactPath": "fixtures/deployments/base-canary-v0.json", + "indexerStatePath": "fixtures/deployments/base-canary-indexer-state.json", + "checkpointPath": "fixtures/deployments/base-canary-indexer-checkpoint.json", + "docsPath": "docs/DEPLOYMENTS/2026-05-13-base-canary-v0.md", + "productionReady": false, + "sourceVerification": { + "compilerVersion": "v0.8.24+commit.e11b9ed9", + "optimizer": true, + "optimizerRuns": 200, + "chainId": "8453", + "verifier": "etherscan", + "apiKeyEnv": "BASESCAN_API_KEY" + }, + "readWindow": { + "fromBlock": "45955500", + "toBlock": "45955540", + "finalizedBlock": "45955540" + }, + "counts": { + "observations": 4, + "rejectedLogs": 0, + "duplicates": 0, + "contracts": 10 + }, + "contracts": [ + { + "name": "RootfieldRegistry", + "sourceName": "contracts/RootfieldRegistry.sol:RootfieldRegistry", + "address": "0x2a7ADd68a1d45C3251E2F92fFe4926124654a97C", + "deployTx": "0x9ef9cb563646921e8f8dd71ad237054b8ee5b0c8e8ecc0c3b1577661844d6108", + "block": "45955460", + "emitsFlowPulse": true + }, + { + "name": "FlowMemoryHookAdapter", + "sourceName": "contracts/FlowMemoryHookAdapter.sol:FlowMemoryHookAdapter", + "address": "0x179Df6d52e9DeF5D02704583a2E4E5a9FF427245", + "deployTx": "0xab22f1b9a19c090c63d4b3a4e100cf80571f5142ab91620fbc76d739d1f318b9", + "block": "45955461", + "emitsFlowPulse": true + }, + { + "name": "ArtifactRegistry", + "sourceName": "contracts/ArtifactRegistry.sol:ArtifactRegistry", + "address": "0x8F074d0F4e66975b740A4b7a316330c9660a485E", + "deployTx": "0xbd07e7d0280a20344521628b13353152da859e62fdcdb4355bf04b50a4176b0b", + "block": "45955462", + "emitsFlowPulse": false + }, + { + "name": "CursorRegistry", + "sourceName": "contracts/CursorRegistry.sol:CursorRegistry", + "address": "0x3360689009685eade15c876855D24161b05829C1", + "deployTx": "0x7eecbb949a617bff8fc8c0749a3332e1e2d2fe7b17f0cf3d7522155f5675cee9", + "block": "45955463", + "emitsFlowPulse": false + }, + { + "name": "ReceiptVerifier", + "sourceName": "contracts/ReceiptVerifier.sol:ReceiptVerifier", + "address": "0x94ba7aA4562f8F8528C327378F6352350f6ddB5B", + "deployTx": "0x3b29454ebb4104e38a0f886a4a370f26d3e3bc5ab22bbfb921374fa904e38e95", + "block": "45955464", + "emitsFlowPulse": false + }, + { + "name": "WorkerRegistry", + "sourceName": "contracts/WorkerRegistry.sol:WorkerRegistry", + "address": "0xa8c07eF53Eeb4e57297ee35025a9cD5303fCCD29", + "deployTx": "0x213a1ba85d6e998937c907699be83b7cd7db9eba4e43f937f94ad7b62bf3cfde", + "block": "45955465", + "emitsFlowPulse": false + }, + { + "name": "VerifierRegistry", + "sourceName": "contracts/VerifierRegistry.sol:VerifierRegistry", + "address": "0xAf920ca7436Bb72172E27C96E0B716f01dcC5DBd", + "deployTx": "0x87696577de6522f885a67c9df98fd8942387e7e7c735f3215f067b2cbc913d2f", + "block": "45955466", + "emitsFlowPulse": false + }, + { + "name": "WorkReceiptRegistry", + "sourceName": "contracts/WorkReceiptRegistry.sol:WorkReceiptRegistry", + "address": "0x2874cee0D581E4562ac9015BfCf330f1ea58a1F3", + "deployTx": "0xa278992a28c2a1ad9e4c78998dbbfb43399e55182ca93f8aac40e9447b2f854f", + "block": "45955467", + "emitsFlowPulse": false + }, + { + "name": "VerifierReportRegistry", + "sourceName": "contracts/VerifierReportRegistry.sol:VerifierReportRegistry", + "address": "0x95bC7455AdFD60e1B908ba455c25Ae732C1Ef996", + "deployTx": "0x9def1a9338bf65e04bc43fd3f4ef1bb973ebdf1ee4b351a77157c396da6af074", + "block": "45955468", + "emitsFlowPulse": false + }, + { + "name": "WorkDebtScheduler", + "sourceName": "contracts/WorkDebtScheduler.sol:WorkDebtScheduler", + "address": "0xa752e9bC7fAf39f659110D8Cf408E7707db94E34", + "deployTx": "0x139e3b89b0c674c6031d34fbfafb2649e114443ed7b98b9722d59bc19ffb6bdc", + "block": "45955469", + "emitsFlowPulse": false + } + ], + "boundaries": [ + "Canary deployment for V0 testing only.", + "Not a production protocol launch.", + "Not a production Uniswap v4 hook deployment.", + "Not a production verifier network.", + "No token, fee, custody, governance, multisig, or upgrade policy is implied." + ] + }, + "futureGeneratedPaths": { + "indexer": "fixtures/deployments/base-canary-indexer-state.json", + "verifier": "not-applicable-for-canary-v0", + "devnet": "not-applicable-for-canary-v0", + "hardware": "not-applicable-for-canary-v0" + } + }, + "chain": { + "chainId": "8453", + "name": "Base mainnet V0 canary", + "environment": "mainnet", + "settlementContext": "Guarded canary read from deployed V0 FlowPulse surfaces; not production protocol readiness.", + "currentBlock": 45955540, + "finalizedBlock": 45955540, + "source": "live", + "lastUpdated": "2026-05-13T20:05:51.625Z" + }, + "flowPulseObservations": [ + { + "id": "0x7b43ea15b0d77169e56395425bb757d280aa8be2b262fbd5f85e68fb01cb33b9", + "observationId": "0x7b43ea15b0d77169e56395425bb757d280aa8be2b262fbd5f85e68fb01cb33b9", + "pulseId": "0xa62ffb4b36a415032949138edbdcba5005de2e35952df88bdf592d4266184b87", + "rootfieldId": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "eventSignature": "0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43", + "blockNumber": "45955506", + "blockHash": "0xb99c82844b5ea00482b5e36724e167dbaa3276e872de6884c736f715b6acc758", + "txHash": "0x994b98b1cff0c897d75b62cf7c95340f74a59d0c208af68f1dea2d161b80cf00", + "transactionIndex": "37", + "logIndex": "229", + "receiptStatus": "success", + "actor": "0x3a6fba5a78216ba3a8da8d8f501dee2c8186aff9", + "pulseType": "1", + "subject": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "commitment": "0x421370a88b0e8632d6a90aeffa276eda7dd5995bb18cb7ce0fb5d36a553e98dd", + "parentPulseId": "0x0000000000000000000000000000000000000000000000000000000000000000", + "sequence": "1", + "occurredAt": "2026-05-13T19:25:59.000Z", + "uri": "flowmemory://base-canary/rootfield", + "summary": "rootfield registration from Base canary reader", + "status": "finalized", + "lastUpdated": "2026-05-13T20:05:51.625Z", + "provenance": { + "subsystem": "indexer", + "origin": "live", + "chainContext": "base-mainnet-canary", + "fixturePath": "fixtures/deployments/base-canary-indexer-state.json", + "capturedAt": "2026-05-13T20:05:51.625Z", + "localPathHint": "fixtures/deployments/base-canary-indexer-state.json" + } + }, + { + "id": "0xdd701330fee35840b3e02fc8b8154284d883008467936052963eb8ed0b5e9b68", + "observationId": "0xdd701330fee35840b3e02fc8b8154284d883008467936052963eb8ed0b5e9b68", + "pulseId": "0x2d436d766f9777b7f9925d57d8b2d57def3fdfae405017104f21795e20eacef7", + "rootfieldId": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "eventSignature": "0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43", + "blockNumber": "45955507", + "blockHash": "0x80c8eaffc3d5ebaa3eda56ba83ea5fefa1c9c37777dde659c0204c0a04dbe336", + "txHash": "0x5f81dc48c5d172ff3f44a333a33598f23c82be2614f4156d5dd3257a16806cc7", + "transactionIndex": "74", + "logIndex": "274", + "receiptStatus": "success", + "actor": "0x3a6fba5a78216ba3a8da8d8f501dee2c8186aff9", + "pulseType": "4", + "subject": "0x205b396f8fdc1b25ddef7afa02f31f51f1e1048004897b87477569a8e573d434", + "commitment": "0x30055afe075a7c6ea8557ea3a2d3c7012d9d558ebda95803726179355f98ede9", + "parentPulseId": "0x0000000000000000000000000000000000000000000000000000000000000000", + "sequence": "1", + "occurredAt": "2026-05-13T19:26:01.000Z", + "uri": "flowmemory://uniswap-v4/after-swap", + "summary": "swap memory signal from Base canary reader", + "status": "finalized", + "lastUpdated": "2026-05-13T20:05:51.625Z", + "provenance": { + "subsystem": "indexer", + "origin": "live", + "chainContext": "base-mainnet-canary", + "fixturePath": "fixtures/deployments/base-canary-indexer-state.json", + "capturedAt": "2026-05-13T20:05:51.625Z", + "localPathHint": "fixtures/deployments/base-canary-indexer-state.json" + } + }, + { + "id": "0x3711d0558240758a09429d6408eb1e8d699bf2555fb8253ef8192fe5f583f12d", + "observationId": "0x3711d0558240758a09429d6408eb1e8d699bf2555fb8253ef8192fe5f583f12d", + "pulseId": "0x72407268a2ea62659d6b0f62800931936cc6ea7ea5f5b6db91801ba2f8b43eab", + "rootfieldId": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "eventSignature": "0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43", + "blockNumber": "45955533", + "blockHash": "0x7fc3c3aa61c897fc71fdf7d065a1bda685442cb2096c546d2d1dba4da3339b52", + "txHash": "0x24a43789ef489dd6c697567466944a210273e46c333e7be878cda6df9acb8e7a", + "transactionIndex": "131", + "logIndex": "364", + "receiptStatus": "success", + "actor": "0x3a6fba5a78216ba3a8da8d8f501dee2c8186aff9", + "pulseType": "2", + "subject": "0x4a7b8601c06c20bcc7b69c05c51980c12dbd50cbd95a59f460d40555bfc37ce3", + "commitment": "0xe331e74d3287b677c4eee492f00c22efd12f9365070eca9569f78966e499e039", + "parentPulseId": "0xa62ffb4b36a415032949138edbdcba5005de2e35952df88bdf592d4266184b87", + "sequence": "2", + "occurredAt": "2026-05-13T19:26:53.000Z", + "uri": "flowmemory://base-canary/root", + "summary": "root commitment from Base canary reader", + "status": "finalized", + "lastUpdated": "2026-05-13T20:05:51.625Z", + "provenance": { + "subsystem": "indexer", + "origin": "live", + "chainContext": "base-mainnet-canary", + "fixturePath": "fixtures/deployments/base-canary-indexer-state.json", + "capturedAt": "2026-05-13T20:05:51.625Z", + "localPathHint": "fixtures/deployments/base-canary-indexer-state.json" + } + }, + { + "id": "0xf3a1c63cf9eb89e59970b1266f8691142349d2af230a7654abcf91fcccef0da6", + "observationId": "0xf3a1c63cf9eb89e59970b1266f8691142349d2af230a7654abcf91fcccef0da6", + "pulseId": "0x16c2adf5f3e46ee91d16a432d2420c566851b311e767860cab99068dcaca2591", + "rootfieldId": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "eventSignature": "0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43", + "blockNumber": "45955535", + "blockHash": "0x0510d6a627884b7b3677a7223a2720d1e33f5f4e58f2c0c6c7baf27bcec25d7c", + "txHash": "0xaee21f6d0e9df1a45eae0c7714a4f8eae7fb72afbb07dd67b3a1f0ff724a014f", + "transactionIndex": "194", + "logIndex": "1212", + "receiptStatus": "success", + "actor": "0x3a6fba5a78216ba3a8da8d8f501dee2c8186aff9", + "pulseType": "4", + "subject": "0x205b396f8fdc1b25ddef7afa02f31f51f1e1048004897b87477569a8e573d434", + "commitment": "0x30055afe075a7c6ea8557ea3a2d3c7012d9d558ebda95803726179355f98ede9", + "parentPulseId": "0x0000000000000000000000000000000000000000000000000000000000000000", + "sequence": "2", + "occurredAt": "2026-05-13T19:26:57.000Z", + "uri": "flowmemory://uniswap-v4/after-swap", + "summary": "swap memory signal from Base canary reader", + "status": "finalized", + "lastUpdated": "2026-05-13T20:05:51.625Z", + "provenance": { + "subsystem": "indexer", + "origin": "live", + "chainContext": "base-mainnet-canary", + "fixturePath": "fixtures/deployments/base-canary-indexer-state.json", + "capturedAt": "2026-05-13T20:05:51.625Z", + "localPathHint": "fixtures/deployments/base-canary-indexer-state.json" + } + } + ], + "rootfields": [ + { + "id": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "rootfieldId": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "owner": "0x3A6fBA5a78216ba3a8DA8d8F501dee2C8186aFf9", + "schemaHash": "0x0b4537a7fa7cdd45fd6ff2052f1e4f9087a40b09fb6fe06a686ac67ac96fa5c3", + "metadataHash": "0x5f2a82ffa386793a2a67971ab801b2633b76954a95ee873e463730e6442ef90d", + "latestRoot": "0x4a7b8601c06c20bcc7b69c05c51980c12dbd50cbd95a59f460d40555bfc37ce3", + "latestObservationId": "0xf3a1c63cf9eb89e59970b1266f8691142349d2af230a7654abcf91fcccef0da6", + "pulseCount": 4, + "workLaneIds": [ + "MEMORY_REFRESH", + "STEERING_VALIDATION" + ], + "evidenceUri": "docs/DEPLOYMENTS/2026-05-13-base-canary-v0.md", + "status": "finalized", + "lastUpdated": "2026-05-13T20:05:51.625Z", + "provenance": { + "subsystem": "indexer", + "origin": "live", + "chainContext": "base-mainnet-canary", + "fixturePath": "fixtures/deployments/base-canary-indexer-state.json", + "capturedAt": "2026-05-13T20:05:51.625Z", + "localPathHint": "fixtures/deployments/base-canary-indexer-state.json" + } + } + ], + "workLanes": [], + "workReceipts": [], + "verifierReports": [], + "rootflowTransitions": [ + { + "schema": "flowmemory.rootflow_transition.v0", + "transitionId": "0xef6e3d4a6375fdaf3573db4550b7a6c4ac535f6ddbf8a2759014c7e6ee5dc17d", + "rootfieldId": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "observationId": "0x7b43ea15b0d77169e56395425bb757d280aa8be2b262fbd5f85e68fb01cb33b9", + "pulseId": "0xa62ffb4b36a415032949138edbdcba5005de2e35952df88bdf592d4266184b87", + "parentPulseId": "0x0000000000000000000000000000000000000000000000000000000000000000", + "parentTransitionId": null, + "memorySignalId": "0x1d530aa927338513f49fbd12145d05b1428347b5cfa0fb241033a9d070123637", + "memoryReceiptId": null, + "reportId": null, + "previousRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "attemptedRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nextRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "status": "finalized", + "blockNumber": "45955506", + "txHash": "0x994b98b1cff0c897d75b62cf7c95340f74a59d0c208af68f1dea2d161b80cf00", + "sequence": "1", + "reasonCodes": [ + "canary.no_verifier_report" + ], + "contractEventRef": { + "signalId": "0x1d530aa927338513f49fbd12145d05b1428347b5cfa0fb241033a9d070123637", + "eventName": "FlowPulse", + "eventTopic0": "0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43", + "sourceContract": "0x2a7add68a1d45c3251e2f92ffe4926124654a97c", + "pulseTypeId": "1", + "pulseTypeName": "ROOTFIELD_REGISTERED", + "txHash": "0x994b98b1cff0c897d75b62cf7c95340f74a59d0c208af68f1dea2d161b80cf00", + "logIndex": "229" + }, + "id": "0xef6e3d4a6375fdaf3573db4550b7a6c4ac535f6ddbf8a2759014c7e6ee5dc17d", + "lastUpdated": "2026-05-13T20:05:51.625Z", + "provenance": { + "subsystem": "indexer", + "origin": "live", + "chainContext": "base-mainnet-canary", + "fixturePath": "fixtures/deployments/base-canary-indexer-state.json", + "capturedAt": "2026-05-13T20:05:51.625Z", + "localPathHint": "fixtures/deployments/base-canary-indexer-state.json" + } + }, + { + "schema": "flowmemory.rootflow_transition.v0", + "transitionId": "0x7da37f62f68f29fdd92dde90fdb5783bce555c868bfb0181187ae955eb2d8c95", + "rootfieldId": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "observationId": "0xdd701330fee35840b3e02fc8b8154284d883008467936052963eb8ed0b5e9b68", + "pulseId": "0x2d436d766f9777b7f9925d57d8b2d57def3fdfae405017104f21795e20eacef7", + "parentPulseId": "0x0000000000000000000000000000000000000000000000000000000000000000", + "parentTransitionId": null, + "memorySignalId": "0x4dbf295239f26095033ddbc39dbb3d2780a554ca697d3ea89c588bda449939d1", + "memoryReceiptId": null, + "reportId": null, + "previousRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "attemptedRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nextRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "status": "finalized", + "blockNumber": "45955507", + "txHash": "0x5f81dc48c5d172ff3f44a333a33598f23c82be2614f4156d5dd3257a16806cc7", + "sequence": "1", + "reasonCodes": [ + "canary.no_verifier_report" + ], + "contractEventRef": { + "signalId": "0x4dbf295239f26095033ddbc39dbb3d2780a554ca697d3ea89c588bda449939d1", + "eventName": "FlowPulse", + "eventTopic0": "0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43", + "sourceContract": "0x179df6d52e9def5d02704583a2e4e5a9ff427245", + "pulseTypeId": "4", + "pulseTypeName": "SWAP_MEMORY_SIGNAL", + "txHash": "0x5f81dc48c5d172ff3f44a333a33598f23c82be2614f4156d5dd3257a16806cc7", + "logIndex": "274" + }, + "id": "0x7da37f62f68f29fdd92dde90fdb5783bce555c868bfb0181187ae955eb2d8c95", + "lastUpdated": "2026-05-13T20:05:51.625Z", + "provenance": { + "subsystem": "indexer", + "origin": "live", + "chainContext": "base-mainnet-canary", + "fixturePath": "fixtures/deployments/base-canary-indexer-state.json", + "capturedAt": "2026-05-13T20:05:51.625Z", + "localPathHint": "fixtures/deployments/base-canary-indexer-state.json" + } + }, + { + "schema": "flowmemory.rootflow_transition.v0", + "transitionId": "0x508409804fe0dae77e8ced57cb45968877fcbad6f525a99b4f911ce58759eab7", + "rootfieldId": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "observationId": "0x3711d0558240758a09429d6408eb1e8d699bf2555fb8253ef8192fe5f583f12d", + "pulseId": "0x72407268a2ea62659d6b0f62800931936cc6ea7ea5f5b6db91801ba2f8b43eab", + "parentPulseId": "0xa62ffb4b36a415032949138edbdcba5005de2e35952df88bdf592d4266184b87", + "parentTransitionId": "0xef6e3d4a6375fdaf3573db4550b7a6c4ac535f6ddbf8a2759014c7e6ee5dc17d", + "memorySignalId": "0x79640fe5901417b9254f9de76f011cdd54ffe9587aaf564f241f4b0cc2ec82da", + "memoryReceiptId": null, + "reportId": null, + "previousRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "attemptedRoot": "0x4a7b8601c06c20bcc7b69c05c51980c12dbd50cbd95a59f460d40555bfc37ce3", + "nextRoot": "0x4a7b8601c06c20bcc7b69c05c51980c12dbd50cbd95a59f460d40555bfc37ce3", + "status": "finalized", + "blockNumber": "45955533", + "txHash": "0x24a43789ef489dd6c697567466944a210273e46c333e7be878cda6df9acb8e7a", + "sequence": "2", + "reasonCodes": [ + "canary.no_verifier_report" + ], + "contractEventRef": { + "signalId": "0x79640fe5901417b9254f9de76f011cdd54ffe9587aaf564f241f4b0cc2ec82da", + "eventName": "FlowPulse", + "eventTopic0": "0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43", + "sourceContract": "0x2a7add68a1d45c3251e2f92ffe4926124654a97c", + "pulseTypeId": "2", + "pulseTypeName": "ROOT_COMMITTED", + "txHash": "0x24a43789ef489dd6c697567466944a210273e46c333e7be878cda6df9acb8e7a", + "logIndex": "364" + }, + "id": "0x508409804fe0dae77e8ced57cb45968877fcbad6f525a99b4f911ce58759eab7", + "lastUpdated": "2026-05-13T20:05:51.625Z", + "provenance": { + "subsystem": "indexer", + "origin": "live", + "chainContext": "base-mainnet-canary", + "fixturePath": "fixtures/deployments/base-canary-indexer-state.json", + "capturedAt": "2026-05-13T20:05:51.625Z", + "localPathHint": "fixtures/deployments/base-canary-indexer-state.json" + } + }, + { + "schema": "flowmemory.rootflow_transition.v0", + "transitionId": "0x91d0da9bc41fad8cb3fa66b72ba804f6685d34f0c167bd3fbf1ac48f91402188", + "rootfieldId": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "observationId": "0xf3a1c63cf9eb89e59970b1266f8691142349d2af230a7654abcf91fcccef0da6", + "pulseId": "0x16c2adf5f3e46ee91d16a432d2420c566851b311e767860cab99068dcaca2591", + "parentPulseId": "0x0000000000000000000000000000000000000000000000000000000000000000", + "parentTransitionId": null, + "memorySignalId": "0x623c146bc8899c5edadaed215cc86a1292f1f5c97acb245e86c0462190078ca5", + "memoryReceiptId": null, + "reportId": null, + "previousRoot": "0x4a7b8601c06c20bcc7b69c05c51980c12dbd50cbd95a59f460d40555bfc37ce3", + "attemptedRoot": "0x4a7b8601c06c20bcc7b69c05c51980c12dbd50cbd95a59f460d40555bfc37ce3", + "nextRoot": "0x4a7b8601c06c20bcc7b69c05c51980c12dbd50cbd95a59f460d40555bfc37ce3", + "status": "finalized", + "blockNumber": "45955535", + "txHash": "0xaee21f6d0e9df1a45eae0c7714a4f8eae7fb72afbb07dd67b3a1f0ff724a014f", + "sequence": "2", + "reasonCodes": [ + "canary.no_verifier_report" + ], + "contractEventRef": { + "signalId": "0x623c146bc8899c5edadaed215cc86a1292f1f5c97acb245e86c0462190078ca5", + "eventName": "FlowPulse", + "eventTopic0": "0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43", + "sourceContract": "0x179df6d52e9def5d02704583a2e4e5a9ff427245", + "pulseTypeId": "4", + "pulseTypeName": "SWAP_MEMORY_SIGNAL", + "txHash": "0xaee21f6d0e9df1a45eae0c7714a4f8eae7fb72afbb07dd67b3a1f0ff724a014f", + "logIndex": "1212" + }, + "id": "0x91d0da9bc41fad8cb3fa66b72ba804f6685d34f0c167bd3fbf1ac48f91402188", + "lastUpdated": "2026-05-13T20:05:51.625Z", + "provenance": { + "subsystem": "indexer", + "origin": "live", + "chainContext": "base-mainnet-canary", + "fixturePath": "fixtures/deployments/base-canary-indexer-state.json", + "capturedAt": "2026-05-13T20:05:51.625Z", + "localPathHint": "fixtures/deployments/base-canary-indexer-state.json" + } + } + ], + "memorySignals": [ + { + "schema": "flowmemory.memory_signal.v0", + "signalId": "0x1d530aa927338513f49fbd12145d05b1428347b5cfa0fb241033a9d070123637", + "observationId": "0x7b43ea15b0d77169e56395425bb757d280aa8be2b262fbd5f85e68fb01cb33b9", + "pulseId": "0xa62ffb4b36a415032949138edbdcba5005de2e35952df88bdf592d4266184b87", + "rootfieldId": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "signalType": "rootfield_registration", + "status": "finalized", + "chainId": "8453", + "emittingContract": "0x2a7add68a1d45c3251e2f92ffe4926124654a97c", + "blockNumber": "45955506", + "blockHash": "0xb99c82844b5ea00482b5e36724e167dbaa3276e872de6884c736f715b6acc758", + "txHash": "0x994b98b1cff0c897d75b62cf7c95340f74a59d0c208af68f1dea2d161b80cf00", + "transactionIndex": "37", + "logIndex": "229", + "actor": "0x3a6fba5a78216ba3a8da8d8f501dee2c8186aff9", + "subject": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "commitment": "0x421370a88b0e8632d6a90aeffa276eda7dd5995bb18cb7ce0fb5d36a553e98dd", + "parentPulseId": "0x0000000000000000000000000000000000000000000000000000000000000000", + "sequence": "1", + "occurredAt": "2026-05-13T19:25:59.000Z", + "uri": "flowmemory://base-canary/rootfield", + "summary": "rootfield registration canary pulse 1", + "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": "0x2a7add68a1d45c3251e2f92ffe4926124654a97c", + "pulseTypeId": "1", + "pulseTypeName": "ROOTFIELD_REGISTERED", + "indexed": { + "pulseId": "0xa62ffb4b36a415032949138edbdcba5005de2e35952df88bdf592d4266184b87", + "rootfieldId": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "actor": "0x3a6fba5a78216ba3a8da8d8f501dee2c8186aff9" + }, + "payload": { + "subject": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "commitment": "0x421370a88b0e8632d6a90aeffa276eda7dd5995bb18cb7ce0fb5d36a553e98dd", + "parentPulseId": "0x0000000000000000000000000000000000000000000000000000000000000000", + "sequence": "1", + "occurredAt": "2026-05-13T19:25:59.000Z", + "uri": "flowmemory://base-canary/rootfield" + }, + "receiptLocator": { + "chainId": "8453", + "blockNumber": "45955506", + "blockHash": "0xb99c82844b5ea00482b5e36724e167dbaa3276e872de6884c736f715b6acc758", + "txHash": "0x994b98b1cff0c897d75b62cf7c95340f74a59d0c208af68f1dea2d161b80cf00", + "transactionIndex": "37", + "logIndex": "229", + "receiptStatus": "success" + }, + "receiptDerivedFields": [ + "blockHash", + "txHash", + "transactionIndex", + "logIndex", + "receiptStatus" + ] + }, + "id": "0x1d530aa927338513f49fbd12145d05b1428347b5cfa0fb241033a9d070123637", + "lastUpdated": "2026-05-13T20:05:51.625Z", + "provenance": { + "subsystem": "indexer", + "origin": "live", + "chainContext": "base-mainnet-canary", + "fixturePath": "fixtures/deployments/base-canary-indexer-state.json", + "capturedAt": "2026-05-13T20:05:51.625Z", + "localPathHint": "fixtures/deployments/base-canary-indexer-state.json" + } + }, + { + "schema": "flowmemory.memory_signal.v0", + "signalId": "0x4dbf295239f26095033ddbc39dbb3d2780a554ca697d3ea89c588bda449939d1", + "observationId": "0xdd701330fee35840b3e02fc8b8154284d883008467936052963eb8ed0b5e9b68", + "pulseId": "0x2d436d766f9777b7f9925d57d8b2d57def3fdfae405017104f21795e20eacef7", + "rootfieldId": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "signalType": "swap_memory_signal", + "status": "finalized", + "chainId": "8453", + "emittingContract": "0x179df6d52e9def5d02704583a2e4e5a9ff427245", + "blockNumber": "45955507", + "blockHash": "0x80c8eaffc3d5ebaa3eda56ba83ea5fefa1c9c37777dde659c0204c0a04dbe336", + "txHash": "0x5f81dc48c5d172ff3f44a333a33598f23c82be2614f4156d5dd3257a16806cc7", + "transactionIndex": "74", + "logIndex": "274", + "actor": "0x3a6fba5a78216ba3a8da8d8f501dee2c8186aff9", + "subject": "0x205b396f8fdc1b25ddef7afa02f31f51f1e1048004897b87477569a8e573d434", + "commitment": "0x30055afe075a7c6ea8557ea3a2d3c7012d9d558ebda95803726179355f98ede9", + "parentPulseId": "0x0000000000000000000000000000000000000000000000000000000000000000", + "sequence": "1", + "occurredAt": "2026-05-13T19:26:01.000Z", + "uri": "flowmemory://uniswap-v4/after-swap", + "summary": "swap memory signal canary pulse 1", + "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": "0x179df6d52e9def5d02704583a2e4e5a9ff427245", + "pulseTypeId": "4", + "pulseTypeName": "SWAP_MEMORY_SIGNAL", + "indexed": { + "pulseId": "0x2d436d766f9777b7f9925d57d8b2d57def3fdfae405017104f21795e20eacef7", + "rootfieldId": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "actor": "0x3a6fba5a78216ba3a8da8d8f501dee2c8186aff9" + }, + "payload": { + "subject": "0x205b396f8fdc1b25ddef7afa02f31f51f1e1048004897b87477569a8e573d434", + "commitment": "0x30055afe075a7c6ea8557ea3a2d3c7012d9d558ebda95803726179355f98ede9", + "parentPulseId": "0x0000000000000000000000000000000000000000000000000000000000000000", + "sequence": "1", + "occurredAt": "2026-05-13T19:26:01.000Z", + "uri": "flowmemory://uniswap-v4/after-swap" + }, + "receiptLocator": { + "chainId": "8453", + "blockNumber": "45955507", + "blockHash": "0x80c8eaffc3d5ebaa3eda56ba83ea5fefa1c9c37777dde659c0204c0a04dbe336", + "txHash": "0x5f81dc48c5d172ff3f44a333a33598f23c82be2614f4156d5dd3257a16806cc7", + "transactionIndex": "74", + "logIndex": "274", + "receiptStatus": "success" + }, + "receiptDerivedFields": [ + "blockHash", + "txHash", + "transactionIndex", + "logIndex", + "receiptStatus" + ] + }, + "id": "0x4dbf295239f26095033ddbc39dbb3d2780a554ca697d3ea89c588bda449939d1", + "lastUpdated": "2026-05-13T20:05:51.625Z", + "provenance": { + "subsystem": "indexer", + "origin": "live", + "chainContext": "base-mainnet-canary", + "fixturePath": "fixtures/deployments/base-canary-indexer-state.json", + "capturedAt": "2026-05-13T20:05:51.625Z", + "localPathHint": "fixtures/deployments/base-canary-indexer-state.json" + } + }, + { + "schema": "flowmemory.memory_signal.v0", + "signalId": "0x79640fe5901417b9254f9de76f011cdd54ffe9587aaf564f241f4b0cc2ec82da", + "observationId": "0x3711d0558240758a09429d6408eb1e8d699bf2555fb8253ef8192fe5f583f12d", + "pulseId": "0x72407268a2ea62659d6b0f62800931936cc6ea7ea5f5b6db91801ba2f8b43eab", + "rootfieldId": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "signalType": "root_commitment", + "status": "finalized", + "chainId": "8453", + "emittingContract": "0x2a7add68a1d45c3251e2f92ffe4926124654a97c", + "blockNumber": "45955533", + "blockHash": "0x7fc3c3aa61c897fc71fdf7d065a1bda685442cb2096c546d2d1dba4da3339b52", + "txHash": "0x24a43789ef489dd6c697567466944a210273e46c333e7be878cda6df9acb8e7a", + "transactionIndex": "131", + "logIndex": "364", + "actor": "0x3a6fba5a78216ba3a8da8d8f501dee2c8186aff9", + "subject": "0x4a7b8601c06c20bcc7b69c05c51980c12dbd50cbd95a59f460d40555bfc37ce3", + "commitment": "0xe331e74d3287b677c4eee492f00c22efd12f9365070eca9569f78966e499e039", + "parentPulseId": "0xa62ffb4b36a415032949138edbdcba5005de2e35952df88bdf592d4266184b87", + "sequence": "2", + "occurredAt": "2026-05-13T19:26:53.000Z", + "uri": "flowmemory://base-canary/root", + "summary": "root commitment canary pulse 2", + "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": "0x2a7add68a1d45c3251e2f92ffe4926124654a97c", + "pulseTypeId": "2", + "pulseTypeName": "ROOT_COMMITTED", + "indexed": { + "pulseId": "0x72407268a2ea62659d6b0f62800931936cc6ea7ea5f5b6db91801ba2f8b43eab", + "rootfieldId": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "actor": "0x3a6fba5a78216ba3a8da8d8f501dee2c8186aff9" + }, + "payload": { + "subject": "0x4a7b8601c06c20bcc7b69c05c51980c12dbd50cbd95a59f460d40555bfc37ce3", + "commitment": "0xe331e74d3287b677c4eee492f00c22efd12f9365070eca9569f78966e499e039", + "parentPulseId": "0xa62ffb4b36a415032949138edbdcba5005de2e35952df88bdf592d4266184b87", + "sequence": "2", + "occurredAt": "2026-05-13T19:26:53.000Z", + "uri": "flowmemory://base-canary/root" + }, + "receiptLocator": { + "chainId": "8453", + "blockNumber": "45955533", + "blockHash": "0x7fc3c3aa61c897fc71fdf7d065a1bda685442cb2096c546d2d1dba4da3339b52", + "txHash": "0x24a43789ef489dd6c697567466944a210273e46c333e7be878cda6df9acb8e7a", + "transactionIndex": "131", + "logIndex": "364", + "receiptStatus": "success" + }, + "receiptDerivedFields": [ + "blockHash", + "txHash", + "transactionIndex", + "logIndex", + "receiptStatus" + ] + }, + "id": "0x79640fe5901417b9254f9de76f011cdd54ffe9587aaf564f241f4b0cc2ec82da", + "lastUpdated": "2026-05-13T20:05:51.625Z", + "provenance": { + "subsystem": "indexer", + "origin": "live", + "chainContext": "base-mainnet-canary", + "fixturePath": "fixtures/deployments/base-canary-indexer-state.json", + "capturedAt": "2026-05-13T20:05:51.625Z", + "localPathHint": "fixtures/deployments/base-canary-indexer-state.json" + } + }, + { + "schema": "flowmemory.memory_signal.v0", + "signalId": "0x623c146bc8899c5edadaed215cc86a1292f1f5c97acb245e86c0462190078ca5", + "observationId": "0xf3a1c63cf9eb89e59970b1266f8691142349d2af230a7654abcf91fcccef0da6", + "pulseId": "0x16c2adf5f3e46ee91d16a432d2420c566851b311e767860cab99068dcaca2591", + "rootfieldId": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "signalType": "swap_memory_signal", + "status": "finalized", + "chainId": "8453", + "emittingContract": "0x179df6d52e9def5d02704583a2e4e5a9ff427245", + "blockNumber": "45955535", + "blockHash": "0x0510d6a627884b7b3677a7223a2720d1e33f5f4e58f2c0c6c7baf27bcec25d7c", + "txHash": "0xaee21f6d0e9df1a45eae0c7714a4f8eae7fb72afbb07dd67b3a1f0ff724a014f", + "transactionIndex": "194", + "logIndex": "1212", + "actor": "0x3a6fba5a78216ba3a8da8d8f501dee2c8186aff9", + "subject": "0x205b396f8fdc1b25ddef7afa02f31f51f1e1048004897b87477569a8e573d434", + "commitment": "0x30055afe075a7c6ea8557ea3a2d3c7012d9d558ebda95803726179355f98ede9", + "parentPulseId": "0x0000000000000000000000000000000000000000000000000000000000000000", + "sequence": "2", + "occurredAt": "2026-05-13T19:26:57.000Z", + "uri": "flowmemory://uniswap-v4/after-swap", + "summary": "swap memory signal canary pulse 2", + "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": "0x179df6d52e9def5d02704583a2e4e5a9ff427245", + "pulseTypeId": "4", + "pulseTypeName": "SWAP_MEMORY_SIGNAL", + "indexed": { + "pulseId": "0x16c2adf5f3e46ee91d16a432d2420c566851b311e767860cab99068dcaca2591", + "rootfieldId": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "actor": "0x3a6fba5a78216ba3a8da8d8f501dee2c8186aff9" + }, + "payload": { + "subject": "0x205b396f8fdc1b25ddef7afa02f31f51f1e1048004897b87477569a8e573d434", + "commitment": "0x30055afe075a7c6ea8557ea3a2d3c7012d9d558ebda95803726179355f98ede9", + "parentPulseId": "0x0000000000000000000000000000000000000000000000000000000000000000", + "sequence": "2", + "occurredAt": "2026-05-13T19:26:57.000Z", + "uri": "flowmemory://uniswap-v4/after-swap" + }, + "receiptLocator": { + "chainId": "8453", + "blockNumber": "45955535", + "blockHash": "0x0510d6a627884b7b3677a7223a2720d1e33f5f4e58f2c0c6c7baf27bcec25d7c", + "txHash": "0xaee21f6d0e9df1a45eae0c7714a4f8eae7fb72afbb07dd67b3a1f0ff724a014f", + "transactionIndex": "194", + "logIndex": "1212", + "receiptStatus": "success" + }, + "receiptDerivedFields": [ + "blockHash", + "txHash", + "transactionIndex", + "logIndex", + "receiptStatus" + ] + }, + "id": "0x623c146bc8899c5edadaed215cc86a1292f1f5c97acb245e86c0462190078ca5", + "lastUpdated": "2026-05-13T20:05:51.625Z", + "provenance": { + "subsystem": "indexer", + "origin": "live", + "chainContext": "base-mainnet-canary", + "fixturePath": "fixtures/deployments/base-canary-indexer-state.json", + "capturedAt": "2026-05-13T20:05:51.625Z", + "localPathHint": "fixtures/deployments/base-canary-indexer-state.json" + } + } + ], + "memoryReceipts": [], + "rootfieldBundles": [ + { + "schema": "flowmemory.rootfield_bundle.v0", + "id": "0xac66336871730498f32d07d63e2c753cb60dbd4363113b011a81a52cdeafa9be", + "bundleId": "0xac66336871730498f32d07d63e2c753cb60dbd4363113b011a81a52cdeafa9be", + "rootfieldId": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "latestRoot": "0x4a7b8601c06c20bcc7b69c05c51980c12dbd50cbd95a59f460d40555bfc37ce3", + "latestTransitionId": "0x91d0da9bc41fad8cb3fa66b72ba804f6685d34f0c167bd3fbf1ac48f91402188", + "status": "finalized", + "transitionIds": [ + "0xef6e3d4a6375fdaf3573db4550b7a6c4ac535f6ddbf8a2759014c7e6ee5dc17d", + "0x7da37f62f68f29fdd92dde90fdb5783bce555c868bfb0181187ae955eb2d8c95", + "0x508409804fe0dae77e8ced57cb45968877fcbad6f525a99b4f911ce58759eab7", + "0x91d0da9bc41fad8cb3fa66b72ba804f6685d34f0c167bd3fbf1ac48f91402188" + ], + "memorySignalIds": [ + "0x1d530aa927338513f49fbd12145d05b1428347b5cfa0fb241033a9d070123637", + "0x4dbf295239f26095033ddbc39dbb3d2780a554ca697d3ea89c588bda449939d1", + "0x79640fe5901417b9254f9de76f011cdd54ffe9587aaf564f241f4b0cc2ec82da", + "0x623c146bc8899c5edadaed215cc86a1292f1f5c97acb245e86c0462190078ca5" + ], + "memoryReceiptIds": [], + "verifierReportIds": [], + "counts": { + "observations": 4, + "transitions": 4, + "receipts": 0, + "verified": 0, + "failed": 0, + "unresolved": 0, + "unsupported": 0, + "reorged": 0 + }, + "lastUpdated": "2026-05-13T20:05:51.625Z", + "provenance": { + "subsystem": "indexer", + "origin": "live", + "chainContext": "base-mainnet-canary", + "fixturePath": "fixtures/deployments/base-canary-indexer-state.json", + "capturedAt": "2026-05-13T20:05:51.625Z", + "localPathHint": "fixtures/deployments/base-canary-indexer-state.json" + } + } + ], + "agentMemoryViews": [ + { + "schema": "flowmemory.agent_memory_view.v0", + "id": "0x8cf5bf3090421e6040d0a483ab3ea97cd6bb0ae4b728f15e3aa3864ccfa089db", + "viewId": "0x8cf5bf3090421e6040d0a483ab3ea97cd6bb0ae4b728f15e3aa3864ccfa089db", + "rootfieldId": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "status": "finalized", + "latestRoot": "0x4a7b8601c06c20bcc7b69c05c51980c12dbd50cbd95a59f460d40555bfc37ce3", + "latestTransitionId": "0x91d0da9bc41fad8cb3fa66b72ba804f6685d34f0c167bd3fbf1ac48f91402188", + "signalIds": [ + "0x1d530aa927338513f49fbd12145d05b1428347b5cfa0fb241033a9d070123637", + "0x4dbf295239f26095033ddbc39dbb3d2780a554ca697d3ea89c588bda449939d1", + "0x79640fe5901417b9254f9de76f011cdd54ffe9587aaf564f241f4b0cc2ec82da", + "0x623c146bc8899c5edadaed215cc86a1292f1f5c97acb245e86c0462190078ca5" + ], + "receiptIds": [], + "transitionIds": [ + "0xef6e3d4a6375fdaf3573db4550b7a6c4ac535f6ddbf8a2759014c7e6ee5dc17d", + "0x7da37f62f68f29fdd92dde90fdb5783bce555c868bfb0181187ae955eb2d8c95", + "0x508409804fe0dae77e8ced57cb45968877fcbad6f525a99b4f911ce58759eab7", + "0x91d0da9bc41fad8cb3fa66b72ba804f6685d34f0c167bd3fbf1ac48f91402188" + ], + "warnings": [ + "Canary data is live-read but not verifier-backed.", + "Source verification and operator policy must be completed before any production claim." + ], + "localOnly": false, + "lastUpdated": "2026-05-13T20:05:51.625Z", + "provenance": { + "subsystem": "worker", + "origin": "live", + "chainContext": "base-mainnet-canary", + "fixturePath": "fixtures/deployments/base-canary-indexer-state.json", + "capturedAt": "2026-05-13T20:05:51.625Z", + "localPathHint": "fixtures/deployments/base-canary-indexer-state.json" + } + } + ], + "devnetBlocks": [], + "hardwareNodes": [], + "alerts": [ + { + "id": "0xa7ab0032c5ec7ec344b72b60aed3707a9ae590b1d9bac121e78a1463deb473ba", + "incidentId": "0xa7ab0032c5ec7ec344b72b60aed3707a9ae590b1d9bac121e78a1463deb473ba", + "severity": "info", + "title": "Canary mode", + "summary": "Base mainnet canary logs are visible, but verifier reports, source verification, multisig ownership, and production hook wiring are not complete.", + "openedAt": "2026-05-13T20:05:51.625Z", + "linkedObjectIds": [ + "0x2a7ADd68a1d45C3251E2F92fFe4926124654a97C", + "0x179Df6d52e9DeF5D02704583a2E4E5a9FF427245", + "0x8F074d0F4e66975b740A4b7a316330c9660a485E", + "0x3360689009685eade15c876855D24161b05829C1", + "0x94ba7aA4562f8F8528C327378F6352350f6ddB5B", + "0xa8c07eF53Eeb4e57297ee35025a9cD5303fCCD29", + "0xAf920ca7436Bb72172E27C96E0B716f01dcC5DBd", + "0x2874cee0D581E4562ac9015BfCf330f1ea58a1F3", + "0x95bC7455AdFD60e1B908ba455c25Ae732C1Ef996", + "0xa752e9bC7fAf39f659110D8Cf408E7707db94E34" + ], + "recommendedAction": "Use this view for launch demonstrations and operator review only.", + "status": "unresolved", + "lastUpdated": "2026-05-13T20:05:51.625Z", + "provenance": { + "subsystem": "alerts", + "origin": "live", + "chainContext": "base-mainnet-canary", + "fixturePath": "fixtures/deployments/base-canary-indexer-state.json", + "capturedAt": "2026-05-13T20:05:51.625Z", + "localPathHint": "fixtures/deployments/base-canary-v0.json" + } + } + ] +} diff --git a/fixtures/deployments/base-canary-indexer-checkpoint.json b/fixtures/deployments/base-canary-indexer-checkpoint.json new file mode 100644 index 00000000..00fcd6f1 --- /dev/null +++ b/fixtures/deployments/base-canary-indexer-checkpoint.json @@ -0,0 +1 @@ +{"addresses":["0x179df6d52e9def5d02704583a2e4e5a9ff427245","0x2a7add68a1d45c3251e2f92ffe4926124654a97c"],"chainId":"8453","cursorCount":4,"duplicateCount":0,"finalizedBlockNumber":"45955540","fromBlock":"45955500","generatedAt":"2026-05-13T20:05:51.625Z","lastIndexedBlock":"45955535","network":"base-mainnet-canary","observationCount":4,"rejectedLogCount":0,"safety":{"acknowledgement":"base-mainnet-canary-only","productionReady":false},"schema":"flowmemory.indexer.base_canary_checkpoint.v0","source":"base-mainnet-canary-rpc","statePath":"E:\\FlowMemory\\flowmemory-main\\fixtures\\deployments\\base-canary-indexer-state.json","toBlock":"45955540"} diff --git a/fixtures/deployments/base-canary-indexer-state.json b/fixtures/deployments/base-canary-indexer-state.json new file mode 100644 index 00000000..7526f8eb --- /dev/null +++ b/fixtures/deployments/base-canary-indexer-state.json @@ -0,0 +1 @@ +{"schema":"flowmemory.indexer.persistence.v0","state":{"batches":[{"cursorCount":4,"observationCount":4,"rejectedLogCount":0,"schema":"flowmemory.indexer.batch.v0","source":"base-mainnet-canary-rpc","sourceSetId":"0xef612a85cb90730538fc4862ef9375c4a7b2d5318745f0b36a0248088d5fcaef"}],"cursors":[{"blockHash":"0xb99c82844b5ea00482b5e36724e167dbaa3276e872de6884c736f715b6acc758","blockNumber":"45955506","chainId":"8453","cursorId":"0x3651ab56bfc86d78628f5cda2e02e363eadb68aa986d38c8e6f2bd938b983440","logIndex":"229","sourceSetId":"0xef612a85cb90730538fc4862ef9375c4a7b2d5318745f0b36a0248088d5fcaef","transactionIndex":"37"},{"blockHash":"0x0510d6a627884b7b3677a7223a2720d1e33f5f4e58f2c0c6c7baf27bcec25d7c","blockNumber":"45955535","chainId":"8453","cursorId":"0x49b89d1e961522eac1b018fc62339da5a0f8a3152bde6055417e31eb9adf39ba","logIndex":"1212","sourceSetId":"0xef612a85cb90730538fc4862ef9375c4a7b2d5318745f0b36a0248088d5fcaef","transactionIndex":"194"},{"blockHash":"0x7fc3c3aa61c897fc71fdf7d065a1bda685442cb2096c546d2d1dba4da3339b52","blockNumber":"45955533","chainId":"8453","cursorId":"0x4f03451e8f8f4bda65a8788e0753697d6beb3f5ebc3574c0cd3da5139ef984e2","logIndex":"364","sourceSetId":"0xef612a85cb90730538fc4862ef9375c4a7b2d5318745f0b36a0248088d5fcaef","transactionIndex":"131"},{"blockHash":"0x80c8eaffc3d5ebaa3eda56ba83ea5fefa1c9c37777dde659c0204c0a04dbe336","blockNumber":"45955507","chainId":"8453","cursorId":"0xac5f50cd599ff8fb84ef784bc33bd6eb766c48fbd2ec53348cc8092c20fb60e7","logIndex":"274","sourceSetId":"0xef612a85cb90730538fc4862ef9375c4a7b2d5318745f0b36a0248088d5fcaef","transactionIndex":"74"}],"duplicates":[],"observations":[{"actor":"0x3a6fba5a78216ba3a8da8d8f501dee2c8186aff9","blockHash":"0xb99c82844b5ea00482b5e36724e167dbaa3276e872de6884c736f715b6acc758","blockNumber":"45955506","canonicalObservationJson":"{\"actor\":\"0x3a6fba5a78216ba3a8da8d8f501dee2c8186aff9\",\"blockHash\":\"0xb99c82844b5ea00482b5e36724e167dbaa3276e872de6884c736f715b6acc758\",\"blockNumber\":\"45955506\",\"chainId\":\"8453\",\"commitment\":\"0x421370a88b0e8632d6a90aeffa276eda7dd5995bb18cb7ce0fb5d36a553e98dd\",\"cursorId\":\"0x3651ab56bfc86d78628f5cda2e02e363eadb68aa986d38c8e6f2bd938b983440\",\"emittingContract\":\"0x2a7add68a1d45c3251e2f92ffe4926124654a97c\",\"eventSignature\":\"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43\",\"lifecycleState\":\"finalized\",\"logIndex\":\"229\",\"observationId\":\"0x7b43ea15b0d77169e56395425bb757d280aa8be2b262fbd5f85e68fb01cb33b9\",\"occurredAt\":\"1778700359\",\"parentPulseId\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"pulseId\":\"0xa62ffb4b36a415032949138edbdcba5005de2e35952df88bdf592d4266184b87\",\"pulseType\":\"1\",\"receiptStatus\":\"success\",\"rootfieldId\":\"0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114\",\"sequence\":\"1\",\"subject\":\"0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114\",\"transactionIndex\":\"37\",\"txHash\":\"0x994b98b1cff0c897d75b62cf7c95340f74a59d0c208af68f1dea2d161b80cf00\",\"uri\":\"flowmemory://base-canary/rootfield\"}","chainId":"8453","commitment":"0x421370a88b0e8632d6a90aeffa276eda7dd5995bb18cb7ce0fb5d36a553e98dd","cursorId":"0x3651ab56bfc86d78628f5cda2e02e363eadb68aa986d38c8e6f2bd938b983440","duplicateKind":"unique","emittingContract":"0x2a7add68a1d45c3251e2f92ffe4926124654a97c","eventSignature":"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43","lifecycleState":"finalized","logIndex":"229","observationId":"0x7b43ea15b0d77169e56395425bb757d280aa8be2b262fbd5f85e68fb01cb33b9","occurredAt":"1778700359","parentPulseId":"0x0000000000000000000000000000000000000000000000000000000000000000","pulseId":"0xa62ffb4b36a415032949138edbdcba5005de2e35952df88bdf592d4266184b87","pulseType":"1","receiptStatus":"success","rootfieldId":"0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114","sequence":"1","subject":"0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114","transactionIndex":"37","txHash":"0x994b98b1cff0c897d75b62cf7c95340f74a59d0c208af68f1dea2d161b80cf00","uri":"flowmemory://base-canary/rootfield"},{"actor":"0x3a6fba5a78216ba3a8da8d8f501dee2c8186aff9","blockHash":"0x80c8eaffc3d5ebaa3eda56ba83ea5fefa1c9c37777dde659c0204c0a04dbe336","blockNumber":"45955507","canonicalObservationJson":"{\"actor\":\"0x3a6fba5a78216ba3a8da8d8f501dee2c8186aff9\",\"blockHash\":\"0x80c8eaffc3d5ebaa3eda56ba83ea5fefa1c9c37777dde659c0204c0a04dbe336\",\"blockNumber\":\"45955507\",\"chainId\":\"8453\",\"commitment\":\"0x30055afe075a7c6ea8557ea3a2d3c7012d9d558ebda95803726179355f98ede9\",\"cursorId\":\"0xac5f50cd599ff8fb84ef784bc33bd6eb766c48fbd2ec53348cc8092c20fb60e7\",\"emittingContract\":\"0x179df6d52e9def5d02704583a2e4e5a9ff427245\",\"eventSignature\":\"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43\",\"lifecycleState\":\"finalized\",\"logIndex\":\"274\",\"observationId\":\"0xdd701330fee35840b3e02fc8b8154284d883008467936052963eb8ed0b5e9b68\",\"occurredAt\":\"1778700361\",\"parentPulseId\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"pulseId\":\"0x2d436d766f9777b7f9925d57d8b2d57def3fdfae405017104f21795e20eacef7\",\"pulseType\":\"4\",\"receiptStatus\":\"success\",\"rootfieldId\":\"0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114\",\"sequence\":\"1\",\"subject\":\"0x205b396f8fdc1b25ddef7afa02f31f51f1e1048004897b87477569a8e573d434\",\"transactionIndex\":\"74\",\"txHash\":\"0x5f81dc48c5d172ff3f44a333a33598f23c82be2614f4156d5dd3257a16806cc7\",\"uri\":\"flowmemory://uniswap-v4/after-swap\"}","chainId":"8453","commitment":"0x30055afe075a7c6ea8557ea3a2d3c7012d9d558ebda95803726179355f98ede9","cursorId":"0xac5f50cd599ff8fb84ef784bc33bd6eb766c48fbd2ec53348cc8092c20fb60e7","duplicateKind":"unique","emittingContract":"0x179df6d52e9def5d02704583a2e4e5a9ff427245","eventSignature":"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43","lifecycleState":"finalized","logIndex":"274","observationId":"0xdd701330fee35840b3e02fc8b8154284d883008467936052963eb8ed0b5e9b68","occurredAt":"1778700361","parentPulseId":"0x0000000000000000000000000000000000000000000000000000000000000000","pulseId":"0x2d436d766f9777b7f9925d57d8b2d57def3fdfae405017104f21795e20eacef7","pulseType":"4","receiptStatus":"success","rootfieldId":"0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114","sequence":"1","subject":"0x205b396f8fdc1b25ddef7afa02f31f51f1e1048004897b87477569a8e573d434","transactionIndex":"74","txHash":"0x5f81dc48c5d172ff3f44a333a33598f23c82be2614f4156d5dd3257a16806cc7","uri":"flowmemory://uniswap-v4/after-swap"},{"actor":"0x3a6fba5a78216ba3a8da8d8f501dee2c8186aff9","blockHash":"0x7fc3c3aa61c897fc71fdf7d065a1bda685442cb2096c546d2d1dba4da3339b52","blockNumber":"45955533","canonicalObservationJson":"{\"actor\":\"0x3a6fba5a78216ba3a8da8d8f501dee2c8186aff9\",\"blockHash\":\"0x7fc3c3aa61c897fc71fdf7d065a1bda685442cb2096c546d2d1dba4da3339b52\",\"blockNumber\":\"45955533\",\"chainId\":\"8453\",\"commitment\":\"0xe331e74d3287b677c4eee492f00c22efd12f9365070eca9569f78966e499e039\",\"cursorId\":\"0x4f03451e8f8f4bda65a8788e0753697d6beb3f5ebc3574c0cd3da5139ef984e2\",\"emittingContract\":\"0x2a7add68a1d45c3251e2f92ffe4926124654a97c\",\"eventSignature\":\"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43\",\"lifecycleState\":\"finalized\",\"logIndex\":\"364\",\"observationId\":\"0x3711d0558240758a09429d6408eb1e8d699bf2555fb8253ef8192fe5f583f12d\",\"occurredAt\":\"1778700413\",\"parentPulseId\":\"0xa62ffb4b36a415032949138edbdcba5005de2e35952df88bdf592d4266184b87\",\"pulseId\":\"0x72407268a2ea62659d6b0f62800931936cc6ea7ea5f5b6db91801ba2f8b43eab\",\"pulseType\":\"2\",\"receiptStatus\":\"success\",\"rootfieldId\":\"0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114\",\"sequence\":\"2\",\"subject\":\"0x4a7b8601c06c20bcc7b69c05c51980c12dbd50cbd95a59f460d40555bfc37ce3\",\"transactionIndex\":\"131\",\"txHash\":\"0x24a43789ef489dd6c697567466944a210273e46c333e7be878cda6df9acb8e7a\",\"uri\":\"flowmemory://base-canary/root\"}","chainId":"8453","commitment":"0xe331e74d3287b677c4eee492f00c22efd12f9365070eca9569f78966e499e039","cursorId":"0x4f03451e8f8f4bda65a8788e0753697d6beb3f5ebc3574c0cd3da5139ef984e2","duplicateKind":"unique","emittingContract":"0x2a7add68a1d45c3251e2f92ffe4926124654a97c","eventSignature":"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43","lifecycleState":"finalized","logIndex":"364","observationId":"0x3711d0558240758a09429d6408eb1e8d699bf2555fb8253ef8192fe5f583f12d","occurredAt":"1778700413","parentPulseId":"0xa62ffb4b36a415032949138edbdcba5005de2e35952df88bdf592d4266184b87","pulseId":"0x72407268a2ea62659d6b0f62800931936cc6ea7ea5f5b6db91801ba2f8b43eab","pulseType":"2","receiptStatus":"success","rootfieldId":"0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114","sequence":"2","subject":"0x4a7b8601c06c20bcc7b69c05c51980c12dbd50cbd95a59f460d40555bfc37ce3","transactionIndex":"131","txHash":"0x24a43789ef489dd6c697567466944a210273e46c333e7be878cda6df9acb8e7a","uri":"flowmemory://base-canary/root"},{"actor":"0x3a6fba5a78216ba3a8da8d8f501dee2c8186aff9","blockHash":"0x0510d6a627884b7b3677a7223a2720d1e33f5f4e58f2c0c6c7baf27bcec25d7c","blockNumber":"45955535","canonicalObservationJson":"{\"actor\":\"0x3a6fba5a78216ba3a8da8d8f501dee2c8186aff9\",\"blockHash\":\"0x0510d6a627884b7b3677a7223a2720d1e33f5f4e58f2c0c6c7baf27bcec25d7c\",\"blockNumber\":\"45955535\",\"chainId\":\"8453\",\"commitment\":\"0x30055afe075a7c6ea8557ea3a2d3c7012d9d558ebda95803726179355f98ede9\",\"cursorId\":\"0x49b89d1e961522eac1b018fc62339da5a0f8a3152bde6055417e31eb9adf39ba\",\"emittingContract\":\"0x179df6d52e9def5d02704583a2e4e5a9ff427245\",\"eventSignature\":\"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43\",\"lifecycleState\":\"finalized\",\"logIndex\":\"1212\",\"observationId\":\"0xf3a1c63cf9eb89e59970b1266f8691142349d2af230a7654abcf91fcccef0da6\",\"occurredAt\":\"1778700417\",\"parentPulseId\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"pulseId\":\"0x16c2adf5f3e46ee91d16a432d2420c566851b311e767860cab99068dcaca2591\",\"pulseType\":\"4\",\"receiptStatus\":\"success\",\"rootfieldId\":\"0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114\",\"sequence\":\"2\",\"subject\":\"0x205b396f8fdc1b25ddef7afa02f31f51f1e1048004897b87477569a8e573d434\",\"transactionIndex\":\"194\",\"txHash\":\"0xaee21f6d0e9df1a45eae0c7714a4f8eae7fb72afbb07dd67b3a1f0ff724a014f\",\"uri\":\"flowmemory://uniswap-v4/after-swap\"}","chainId":"8453","commitment":"0x30055afe075a7c6ea8557ea3a2d3c7012d9d558ebda95803726179355f98ede9","cursorId":"0x49b89d1e961522eac1b018fc62339da5a0f8a3152bde6055417e31eb9adf39ba","duplicateKind":"unique","emittingContract":"0x179df6d52e9def5d02704583a2e4e5a9ff427245","eventSignature":"0x5d07190b9ae441b4d7b16259a48424acd451492b12f5f99a29f5bfd992c13e43","lifecycleState":"finalized","logIndex":"1212","observationId":"0xf3a1c63cf9eb89e59970b1266f8691142349d2af230a7654abcf91fcccef0da6","occurredAt":"1778700417","parentPulseId":"0x0000000000000000000000000000000000000000000000000000000000000000","pulseId":"0x16c2adf5f3e46ee91d16a432d2420c566851b311e767860cab99068dcaca2591","pulseType":"4","receiptStatus":"success","rootfieldId":"0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114","sequence":"2","subject":"0x205b396f8fdc1b25ddef7afa02f31f51f1e1048004897b87477569a8e573d434","transactionIndex":"194","txHash":"0xaee21f6d0e9df1a45eae0c7714a4f8eae7fb72afbb07dd67b3a1f0ff724a014f","uri":"flowmemory://uniswap-v4/after-swap"}],"pulses":[{"actor":"0x3a6fba5a78216ba3a8da8d8f501dee2c8186aff9","commitment":"0x421370a88b0e8632d6a90aeffa276eda7dd5995bb18cb7ce0fb5d36a553e98dd","observationId":"0x7b43ea15b0d77169e56395425bb757d280aa8be2b262fbd5f85e68fb01cb33b9","occurredAt":"1778700359","parentPulseId":"0x0000000000000000000000000000000000000000000000000000000000000000","pulseId":"0xa62ffb4b36a415032949138edbdcba5005de2e35952df88bdf592d4266184b87","pulseType":"1","rootfieldId":"0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114","sequence":"1","subject":"0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114","uri":"flowmemory://base-canary/rootfield"},{"actor":"0x3a6fba5a78216ba3a8da8d8f501dee2c8186aff9","commitment":"0x30055afe075a7c6ea8557ea3a2d3c7012d9d558ebda95803726179355f98ede9","observationId":"0xdd701330fee35840b3e02fc8b8154284d883008467936052963eb8ed0b5e9b68","occurredAt":"1778700361","parentPulseId":"0x0000000000000000000000000000000000000000000000000000000000000000","pulseId":"0x2d436d766f9777b7f9925d57d8b2d57def3fdfae405017104f21795e20eacef7","pulseType":"4","rootfieldId":"0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114","sequence":"1","subject":"0x205b396f8fdc1b25ddef7afa02f31f51f1e1048004897b87477569a8e573d434","uri":"flowmemory://uniswap-v4/after-swap"},{"actor":"0x3a6fba5a78216ba3a8da8d8f501dee2c8186aff9","commitment":"0xe331e74d3287b677c4eee492f00c22efd12f9365070eca9569f78966e499e039","observationId":"0x3711d0558240758a09429d6408eb1e8d699bf2555fb8253ef8192fe5f583f12d","occurredAt":"1778700413","parentPulseId":"0xa62ffb4b36a415032949138edbdcba5005de2e35952df88bdf592d4266184b87","pulseId":"0x72407268a2ea62659d6b0f62800931936cc6ea7ea5f5b6db91801ba2f8b43eab","pulseType":"2","rootfieldId":"0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114","sequence":"2","subject":"0x4a7b8601c06c20bcc7b69c05c51980c12dbd50cbd95a59f460d40555bfc37ce3","uri":"flowmemory://base-canary/root"},{"actor":"0x3a6fba5a78216ba3a8da8d8f501dee2c8186aff9","commitment":"0x30055afe075a7c6ea8557ea3a2d3c7012d9d558ebda95803726179355f98ede9","observationId":"0xf3a1c63cf9eb89e59970b1266f8691142349d2af230a7654abcf91fcccef0da6","occurredAt":"1778700417","parentPulseId":"0x0000000000000000000000000000000000000000000000000000000000000000","pulseId":"0x16c2adf5f3e46ee91d16a432d2420c566851b311e767860cab99068dcaca2591","pulseType":"4","rootfieldId":"0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114","sequence":"2","subject":"0x205b396f8fdc1b25ddef7afa02f31f51f1e1048004897b87477569a8e573d434","uri":"flowmemory://uniswap-v4/after-swap"}],"rejectedLogs":[],"rootfields":[{"firstObservationId":"0x7b43ea15b0d77169e56395425bb757d280aa8be2b262fbd5f85e68fb01cb33b9","latestObservationId":"0xf3a1c63cf9eb89e59970b1266f8691142349d2af230a7654abcf91fcccef0da6","pulseCount":4,"rootfieldId":"0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114"}],"schema":"flowmemory.indexer.state.v0","source":"base-mainnet-canary-rpc"}} diff --git a/fixtures/deployments/base-canary-source-verification-plan.json b/fixtures/deployments/base-canary-source-verification-plan.json new file mode 100644 index 00000000..e2057a8e --- /dev/null +++ b/fixtures/deployments/base-canary-source-verification-plan.json @@ -0,0 +1,228 @@ +{ + "schema": "flowmemory.base_canary_source_verification_plan.v0", + "deploymentPath": "fixtures/deployments/base-canary-v0.json", + "reportPath": "fixtures/deployments/base-canary-source-verification-plan.json", + "network": { + "name": "Base mainnet", + "chainId": "8453", + "environment": "mainnet", + "rpcLabel": "public Base RPC" + }, + "productionReady": false, + "submit": false, + "checkBytecode": true, + "apiKeyEnv": "BASESCAN_API_KEY", + "contractCount": 10, + "commands": [ + { + "name": "RootfieldRegistry", + "address": "0x2a7ADd68a1d45C3251E2F92fFe4926124654a97C", + "sourceName": "contracts/RootfieldRegistry.sol:RootfieldRegistry", + "argv": [ + "forge", + "verify-contract", + "0x2a7ADd68a1d45C3251E2F92fFe4926124654a97C", + "contracts/RootfieldRegistry.sol:RootfieldRegistry", + "--chain-id", + "8453", + "--compiler-version", + "v0.8.24+commit.e11b9ed9", + "--num-of-optimizations", + "200", + "--verifier", + "etherscan", + "--etherscan-api-key", + "" + ] + }, + { + "name": "FlowMemoryHookAdapter", + "address": "0x179Df6d52e9DeF5D02704583a2E4E5a9FF427245", + "sourceName": "contracts/FlowMemoryHookAdapter.sol:FlowMemoryHookAdapter", + "argv": [ + "forge", + "verify-contract", + "0x179Df6d52e9DeF5D02704583a2E4E5a9FF427245", + "contracts/FlowMemoryHookAdapter.sol:FlowMemoryHookAdapter", + "--chain-id", + "8453", + "--compiler-version", + "v0.8.24+commit.e11b9ed9", + "--num-of-optimizations", + "200", + "--verifier", + "etherscan", + "--etherscan-api-key", + "" + ] + }, + { + "name": "ArtifactRegistry", + "address": "0x8F074d0F4e66975b740A4b7a316330c9660a485E", + "sourceName": "contracts/ArtifactRegistry.sol:ArtifactRegistry", + "argv": [ + "forge", + "verify-contract", + "0x8F074d0F4e66975b740A4b7a316330c9660a485E", + "contracts/ArtifactRegistry.sol:ArtifactRegistry", + "--chain-id", + "8453", + "--compiler-version", + "v0.8.24+commit.e11b9ed9", + "--num-of-optimizations", + "200", + "--verifier", + "etherscan", + "--etherscan-api-key", + "" + ] + }, + { + "name": "CursorRegistry", + "address": "0x3360689009685eade15c876855D24161b05829C1", + "sourceName": "contracts/CursorRegistry.sol:CursorRegistry", + "argv": [ + "forge", + "verify-contract", + "0x3360689009685eade15c876855D24161b05829C1", + "contracts/CursorRegistry.sol:CursorRegistry", + "--chain-id", + "8453", + "--compiler-version", + "v0.8.24+commit.e11b9ed9", + "--num-of-optimizations", + "200", + "--verifier", + "etherscan", + "--etherscan-api-key", + "" + ] + }, + { + "name": "ReceiptVerifier", + "address": "0x94ba7aA4562f8F8528C327378F6352350f6ddB5B", + "sourceName": "contracts/ReceiptVerifier.sol:ReceiptVerifier", + "argv": [ + "forge", + "verify-contract", + "0x94ba7aA4562f8F8528C327378F6352350f6ddB5B", + "contracts/ReceiptVerifier.sol:ReceiptVerifier", + "--chain-id", + "8453", + "--compiler-version", + "v0.8.24+commit.e11b9ed9", + "--num-of-optimizations", + "200", + "--verifier", + "etherscan", + "--etherscan-api-key", + "" + ] + }, + { + "name": "WorkerRegistry", + "address": "0xa8c07eF53Eeb4e57297ee35025a9cD5303fCCD29", + "sourceName": "contracts/WorkerRegistry.sol:WorkerRegistry", + "argv": [ + "forge", + "verify-contract", + "0xa8c07eF53Eeb4e57297ee35025a9cD5303fCCD29", + "contracts/WorkerRegistry.sol:WorkerRegistry", + "--chain-id", + "8453", + "--compiler-version", + "v0.8.24+commit.e11b9ed9", + "--num-of-optimizations", + "200", + "--verifier", + "etherscan", + "--etherscan-api-key", + "" + ] + }, + { + "name": "VerifierRegistry", + "address": "0xAf920ca7436Bb72172E27C96E0B716f01dcC5DBd", + "sourceName": "contracts/VerifierRegistry.sol:VerifierRegistry", + "argv": [ + "forge", + "verify-contract", + "0xAf920ca7436Bb72172E27C96E0B716f01dcC5DBd", + "contracts/VerifierRegistry.sol:VerifierRegistry", + "--chain-id", + "8453", + "--compiler-version", + "v0.8.24+commit.e11b9ed9", + "--num-of-optimizations", + "200", + "--verifier", + "etherscan", + "--etherscan-api-key", + "" + ] + }, + { + "name": "WorkReceiptRegistry", + "address": "0x2874cee0D581E4562ac9015BfCf330f1ea58a1F3", + "sourceName": "contracts/WorkReceiptRegistry.sol:WorkReceiptRegistry", + "argv": [ + "forge", + "verify-contract", + "0x2874cee0D581E4562ac9015BfCf330f1ea58a1F3", + "contracts/WorkReceiptRegistry.sol:WorkReceiptRegistry", + "--chain-id", + "8453", + "--compiler-version", + "v0.8.24+commit.e11b9ed9", + "--num-of-optimizations", + "200", + "--verifier", + "etherscan", + "--etherscan-api-key", + "" + ] + }, + { + "name": "VerifierReportRegistry", + "address": "0x95bC7455AdFD60e1B908ba455c25Ae732C1Ef996", + "sourceName": "contracts/VerifierReportRegistry.sol:VerifierReportRegistry", + "argv": [ + "forge", + "verify-contract", + "0x95bC7455AdFD60e1B908ba455c25Ae732C1Ef996", + "contracts/VerifierReportRegistry.sol:VerifierReportRegistry", + "--chain-id", + "8453", + "--compiler-version", + "v0.8.24+commit.e11b9ed9", + "--num-of-optimizations", + "200", + "--verifier", + "etherscan", + "--etherscan-api-key", + "" + ] + }, + { + "name": "WorkDebtScheduler", + "address": "0xa752e9bC7fAf39f659110D8Cf408E7707db94E34", + "sourceName": "contracts/WorkDebtScheduler.sol:WorkDebtScheduler", + "argv": [ + "forge", + "verify-contract", + "0xa752e9bC7fAf39f659110D8Cf408E7707db94E34", + "contracts/WorkDebtScheduler.sol:WorkDebtScheduler", + "--chain-id", + "8453", + "--compiler-version", + "v0.8.24+commit.e11b9ed9", + "--num-of-optimizations", + "200", + "--verifier", + "etherscan", + "--etherscan-api-key", + "" + ] + } + ] +} diff --git a/fixtures/deployments/base-canary-v0.json b/fixtures/deployments/base-canary-v0.json new file mode 100644 index 00000000..78721dfd --- /dev/null +++ b/fixtures/deployments/base-canary-v0.json @@ -0,0 +1,124 @@ +{ + "schema": "flowmemory.deployment_artifact.v0", + "name": "base-canary-v0", + "status": "canary-only", + "productionReady": false, + "network": { + "name": "Base mainnet", + "chainId": "8453", + "environment": "mainnet", + "rpcLabel": "public Base RPC" + }, + "deployer": "0x3A6fBA5a78216ba3a8DA8d8F501dee2C8186aFf9", + "deployedAt": "2026-05-13T00:00:00.000Z", + "docsPath": "docs/DEPLOYMENTS/2026-05-13-base-canary-v0.md", + "readWindow": { + "fromBlock": "45955500", + "toBlock": "45955540", + "finalizedBlock": "45955540" + }, + "rootfield": { + "rootfieldId": "0x19c830e926bfd3ce06d71ed0ef2e90ddc73accf4367b0defea835dc1cd3b3114", + "owner": "0x3A6fBA5a78216ba3a8DA8d8F501dee2C8186aFf9", + "schemaHash": "0x0b4537a7fa7cdd45fd6ff2052f1e4f9087a40b09fb6fe06a686ac67ac96fa5c3", + "metadataHash": "0x5f2a82ffa386793a2a67971ab801b2633b76954a95ee873e463730e6442ef90d", + "latestRoot": "0x4a7b8601c06c20bcc7b69c05c51980c12dbd50cbd95a59f460d40555bfc37ce3" + }, + "contracts": [ + { + "name": "RootfieldRegistry", + "sourceName": "contracts/RootfieldRegistry.sol:RootfieldRegistry", + "address": "0x2a7ADd68a1d45C3251E2F92fFe4926124654a97C", + "deployTx": "0x9ef9cb563646921e8f8dd71ad237054b8ee5b0c8e8ecc0c3b1577661844d6108", + "block": "45955460", + "emitsFlowPulse": true + }, + { + "name": "FlowMemoryHookAdapter", + "sourceName": "contracts/FlowMemoryHookAdapter.sol:FlowMemoryHookAdapter", + "address": "0x179Df6d52e9DeF5D02704583a2E4E5a9FF427245", + "deployTx": "0xab22f1b9a19c090c63d4b3a4e100cf80571f5142ab91620fbc76d739d1f318b9", + "block": "45955461", + "emitsFlowPulse": true + }, + { + "name": "ArtifactRegistry", + "sourceName": "contracts/ArtifactRegistry.sol:ArtifactRegistry", + "address": "0x8F074d0F4e66975b740A4b7a316330c9660a485E", + "deployTx": "0xbd07e7d0280a20344521628b13353152da859e62fdcdb4355bf04b50a4176b0b", + "block": "45955462", + "emitsFlowPulse": false + }, + { + "name": "CursorRegistry", + "sourceName": "contracts/CursorRegistry.sol:CursorRegistry", + "address": "0x3360689009685eade15c876855D24161b05829C1", + "deployTx": "0x7eecbb949a617bff8fc8c0749a3332e1e2d2fe7b17f0cf3d7522155f5675cee9", + "block": "45955463", + "emitsFlowPulse": false + }, + { + "name": "ReceiptVerifier", + "sourceName": "contracts/ReceiptVerifier.sol:ReceiptVerifier", + "address": "0x94ba7aA4562f8F8528C327378F6352350f6ddB5B", + "deployTx": "0x3b29454ebb4104e38a0f886a4a370f26d3e3bc5ab22bbfb921374fa904e38e95", + "block": "45955464", + "emitsFlowPulse": false + }, + { + "name": "WorkerRegistry", + "sourceName": "contracts/WorkerRegistry.sol:WorkerRegistry", + "address": "0xa8c07eF53Eeb4e57297ee35025a9cD5303fCCD29", + "deployTx": "0x213a1ba85d6e998937c907699be83b7cd7db9eba4e43f937f94ad7b62bf3cfde", + "block": "45955465", + "emitsFlowPulse": false + }, + { + "name": "VerifierRegistry", + "sourceName": "contracts/VerifierRegistry.sol:VerifierRegistry", + "address": "0xAf920ca7436Bb72172E27C96E0B716f01dcC5DBd", + "deployTx": "0x87696577de6522f885a67c9df98fd8942387e7e7c735f3215f067b2cbc913d2f", + "block": "45955466", + "emitsFlowPulse": false + }, + { + "name": "WorkReceiptRegistry", + "sourceName": "contracts/WorkReceiptRegistry.sol:WorkReceiptRegistry", + "address": "0x2874cee0D581E4562ac9015BfCf330f1ea58a1F3", + "deployTx": "0xa278992a28c2a1ad9e4c78998dbbfb43399e55182ca93f8aac40e9447b2f854f", + "block": "45955467", + "emitsFlowPulse": false + }, + { + "name": "VerifierReportRegistry", + "sourceName": "contracts/VerifierReportRegistry.sol:VerifierReportRegistry", + "address": "0x95bC7455AdFD60e1B908ba455c25Ae732C1Ef996", + "deployTx": "0x9def1a9338bf65e04bc43fd3f4ef1bb973ebdf1ee4b351a77157c396da6af074", + "block": "45955468", + "emitsFlowPulse": false + }, + { + "name": "WorkDebtScheduler", + "sourceName": "contracts/WorkDebtScheduler.sol:WorkDebtScheduler", + "address": "0xa752e9bC7fAf39f659110D8Cf408E7707db94E34", + "deployTx": "0x139e3b89b0c674c6031d34fbfafb2649e114443ed7b98b9722d59bc19ffb6bdc", + "block": "45955469", + "emitsFlowPulse": false + } + ], + "sourceVerification": { + "compilerVersion": "v0.8.24+commit.e11b9ed9", + "optimizer": true, + "optimizerRuns": 200, + "chainId": "8453", + "verifier": "etherscan", + "apiKeyEnv": "BASESCAN_API_KEY" + }, + "boundaries": [ + "Canary deployment for V0 testing only.", + "Not a production protocol launch.", + "Not a production Uniswap v4 hook deployment.", + "Not a production verifier network.", + "No token, fee, custody, governance, multisig, or upgrade policy is implied." + ] +} diff --git a/infra/scripts/verify-base-canary-sources.mjs b/infra/scripts/verify-base-canary-sources.mjs new file mode 100644 index 00000000..65c43437 --- /dev/null +++ b/infra/scripts/verify-base-canary-sources.mjs @@ -0,0 +1,125 @@ +#!/usr/bin/env node +import { mkdirSync, readFileSync, writeFileSync } from "node:fs"; +import { dirname, resolve } from "node:path"; +import { spawnSync } from "node:child_process"; + +const args = process.argv.slice(2); +const flags = new Set(args.filter((arg) => arg.startsWith("--"))); + +function valueAfter(flag, fallback) { + const index = args.indexOf(flag); + if (index === -1) return fallback; + const value = args[index + 1]; + if (value === undefined || value.startsWith("--")) { + throw new Error(`${flag} requires a value`); + } + return value; +} + +const deploymentPath = valueAfter("--deployment", "fixtures/deployments/base-canary-v0.json"); +const reportPath = valueAfter("--report", "fixtures/deployments/base-canary-source-verification-plan.json"); +const rpcUrl = valueAfter("--rpc-url", process.env.BASE_CANARY_RPC_URL ?? "https://mainnet.base.org"); +const submit = flags.has("--submit"); +const json = flags.has("--json"); +const watch = flags.has("--watch"); +const checkBytecode = flags.has("--check-bytecode"); +const artifact = JSON.parse(readFileSync(resolve(deploymentPath), "utf8")); + +if (artifact.schema !== "flowmemory.deployment_artifact.v0") { + throw new Error(`unsupported deployment artifact schema: ${artifact.schema}`); +} +if (artifact.productionReady !== false || artifact.status !== "canary-only") { + throw new Error("source verification script only accepts canary-only deployment artifacts"); +} +if (artifact.network?.chainId !== "8453") { + throw new Error(`expected Base mainnet chain id 8453, received ${artifact.network?.chainId}`); +} + +const apiKeyEnv = artifact.sourceVerification?.apiKeyEnv ?? "BASESCAN_API_KEY"; +const apiKey = process.env[apiKeyEnv]; +if (submit && !apiKey) { + throw new Error(`${apiKeyEnv} is required when --submit is used`); +} + +function verificationArgs(contract, apiKeyValue) { + const sourceVerification = artifact.sourceVerification; + const result = [ + "verify-contract", + contract.address, + contract.sourceName, + "--chain-id", + sourceVerification.chainId, + "--compiler-version", + sourceVerification.compilerVersion, + "--num-of-optimizations", + String(sourceVerification.optimizerRuns), + "--verifier", + sourceVerification.verifier, + "--etherscan-api-key", + apiKeyValue, + ]; + if (watch) { + result.push("--watch"); + } + return result; +} + +function run(command, commandArgs) { + const result = spawnSync(command, commandArgs, { + cwd: process.cwd(), + stdio: "inherit", + }); + if (result.status !== 0) { + throw new Error(`${command} ${commandArgs.join(" ")} failed with exit code ${result.status ?? "unknown"}`); + } +} + +function writeJson(path, value) { + mkdirSync(dirname(resolve(path)), { recursive: true }); + writeFileSync(resolve(path), `${JSON.stringify(value, null, 2)}\n`); +} + +if (checkBytecode) { + for (const contract of artifact.contracts) { + const result = spawnSync("cast", ["code", contract.address, "--rpc-url", rpcUrl], { + cwd: process.cwd(), + encoding: "utf8", + }); + if (result.status !== 0) { + throw new Error(`cast code failed for ${contract.name}: ${result.stderr || result.stdout}`); + } + if (result.stdout.trim() === "0x") { + throw new Error(`no bytecode found for ${contract.name} at ${contract.address}`); + } + } +} + +const plan = { + schema: "flowmemory.base_canary_source_verification_plan.v0", + deploymentPath, + reportPath, + network: artifact.network, + productionReady: false, + submit, + checkBytecode, + apiKeyEnv, + contractCount: artifact.contracts.length, + commands: artifact.contracts.map((contract) => ({ + name: contract.name, + address: contract.address, + sourceName: contract.sourceName, + argv: ["forge", ...verificationArgs(contract, submit ? "" : `<${apiKeyEnv}>`)], + })), +}; + +writeJson(reportPath, plan); + +if (json || !submit) { + console.log(JSON.stringify(plan, null, 2)); +} + +if (submit) { + for (const contract of artifact.contracts) { + run("forge", verificationArgs(contract, apiKey)); + } +} diff --git a/package.json b/package.json index 2e194da5..43d6286f 100644 --- a/package.json +++ b/package.json @@ -18,9 +18,12 @@ "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", + "verify:base-canary:sources": "node infra/scripts/verify-base-canary-sources.mjs --dry-run", + "verify:base-canary:sources:submit": "node infra/scripts/verify-base-canary-sources.mjs --submit --watch", "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", + "flowmemory:canary-dashboard": "npm run generate:canary-dashboard --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", diff --git a/services/flowmemory/package.json b/services/flowmemory/package.json index f65daf1b..f26c8330 100644 --- a/services/flowmemory/package.json +++ b/services/flowmemory/package.json @@ -4,6 +4,7 @@ "type": "module", "scripts": { "generate": "node src/generate-launch-core.ts", + "generate:canary-dashboard": "node src/generate-canary-dashboard.ts", "launch:v0": "node src/launch-v0.ts", "validate": "node src/validate-launch-core.ts", "fixtures:check": "node src/check-fixture-drift.ts", diff --git a/services/flowmemory/src/generate-canary-dashboard.ts b/services/flowmemory/src/generate-canary-dashboard.ts new file mode 100644 index 00000000..17da84b0 --- /dev/null +++ b/services/flowmemory/src/generate-canary-dashboard.ts @@ -0,0 +1,621 @@ +import { mkdirSync, readFileSync, writeFileSync } from "node:fs"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +import { canonicalJson, keccak256Hex } from "../../shared/src/index.ts"; +import { observationLifecycleToFlowMemoryStatus, type FlowMemoryStatus } from "./status.ts"; +import type { + FlowPulseContractEvent, + FlowPulseContractEventRef, + FlowPulseContractTypeName, + MemorySignal, + RootflowTransition, +} from "./types.ts"; + +const ZERO_ROOT = "0x0000000000000000000000000000000000000000000000000000000000000000"; +const REPO_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "../../.."); +const FLOWPULSE_EVENT_SIGNATURE_TEXT = "FlowPulse(bytes32,bytes32,address,uint8,bytes32,bytes32,bytes32,uint64,uint64,string)"; +const FLOWPULSE_EVENT_TOPIC0 = keccak256Hex(new TextEncoder().encode(FLOWPULSE_EVENT_SIGNATURE_TEXT)); +const FLOWPULSE_CONTRACT_TYPE_NAMES: Record = { + "1": "ROOTFIELD_REGISTERED", + "2": "ROOT_COMMITTED", + "3": "ROOTFIELD_STATUS_CHANGED", + "4": "SWAP_MEMORY_SIGNAL", +}; + +type JsonObject = Record; + +interface IndexedObservation { + observationId: string; + cursorId: string; + lifecycleState: string; + duplicateKind: string; + chainId: string; + emittingContract: string; + eventSignature: string; + blockNumber: string; + blockHash: string; + txHash: string; + transactionIndex: string; + logIndex: string; + receiptStatus: string; + pulseId: string; + rootfieldId: string; + actor: string; + pulseType: string; + subject: string; + commitment: string; + parentPulseId: string; + sequence: string; + occurredAt: string; + uri: string; +} + +interface IndexerPersistence { + schema: "flowmemory.indexer.persistence.v0"; + state: { + source: "base-mainnet-canary-rpc"; + observations: IndexedObservation[]; + rootfields: Array<{ + rootfieldId: string; + firstObservationId: string; + latestObservationId: string; + pulseCount: number; + }>; + rejectedLogs: unknown[]; + duplicates: unknown[]; + }; +} + +interface CanaryCheckpoint { + schema: "flowmemory.indexer.base_canary_checkpoint.v0"; + network: "base-mainnet-canary"; + chainId: "8453"; + fromBlock: string; + toBlock: string; + finalizedBlockNumber?: string; + observationCount: number; + rejectedLogCount: number; + duplicateCount: number; + lastIndexedBlock: string; + generatedAt: string; + safety: { + productionReady: false; + }; +} + +interface DeploymentArtifact { + schema: "flowmemory.deployment_artifact.v0"; + name: string; + status: "canary-only"; + productionReady: false; + deployer: string; + docsPath: string; + network: { + name: string; + chainId: "8453"; + environment: "mainnet"; + }; + rootfield: { + rootfieldId: string; + owner: string; + schemaHash: string; + metadataHash: string; + latestRoot: string; + }; + contracts: Array<{ + name: string; + sourceName: string; + address: string; + deployTx: string; + block: string; + emitsFlowPulse: boolean; + }>; + sourceVerification: { + compilerVersion: string; + optimizer: boolean; + optimizerRuns: number; + chainId: string; + verifier: string; + apiKeyEnv: string; + }; + boundaries: string[]; +} + +export interface CanaryDashboardPaths { + deploymentPath: string; + indexerPath: string; + checkpointPath: string; + dashboardOutPath: string; + dashboardRuntimePath: string; +} + +export interface CanaryDashboardData { + metadata: JsonObject; + chain: JsonObject; + flowPulseObservations: JsonObject[]; + rootfields: JsonObject[]; + workLanes: JsonObject[]; + workReceipts: JsonObject[]; + verifierReports: JsonObject[]; + rootflowTransitions: JsonObject[]; + memorySignals: JsonObject[]; + memoryReceipts: JsonObject[]; + rootfieldBundles: JsonObject[]; + agentMemoryViews: JsonObject[]; + devnetBlocks: JsonObject[]; + hardwareNodes: JsonObject[]; + alerts: JsonObject[]; +} + +export const DEFAULT_CANARY_DASHBOARD_PATHS: CanaryDashboardPaths = { + deploymentPath: "fixtures/deployments/base-canary-v0.json", + indexerPath: "fixtures/deployments/base-canary-indexer-state.json", + checkpointPath: "fixtures/deployments/base-canary-indexer-checkpoint.json", + dashboardOutPath: "fixtures/dashboard/flowmemory-dashboard-base-canary-v0.json", + dashboardRuntimePath: "apps/dashboard/public/data/flowmemory-dashboard-base-canary-v0.json", +}; + +function readJson(path: string): T { + return JSON.parse(readFileSync(path, "utf8")) as T; +} + +function writeJson(path: string, value: unknown): void { + mkdirSync(dirname(path), { recursive: true }); + writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`); +} + +function stableId(schema: string, value: unknown): string { + return keccak256Hex(new TextEncoder().encode(canonicalJson({ schema, value } as JsonObject))); +} + +function isoFromUnixSeconds(value: string, fallback: string): string { + const seconds = Number(value); + if (!Number.isFinite(seconds)) { + return fallback; + } + return new Date(seconds * 1000).toISOString(); +} + +function pulseTypeName(pulseType: string): MemorySignal["signalType"] { + if (pulseType === "1") return "rootfield_registration"; + if (pulseType === "2") return "root_commitment"; + if (pulseType === "4") return "swap_memory_signal"; + return "unsupported_pulse"; +} + +function contractPulseTypeName(pulseType: string): FlowPulseContractTypeName { + return FLOWPULSE_CONTRACT_TYPE_NAMES[pulseType] ?? "UNKNOWN_FLOWPULSE_TYPE"; +} + +function sortObservations(observations: IndexedObservation[]): IndexedObservation[] { + return [...observations].sort((left, right) => { + const block = BigInt(left.blockNumber) - BigInt(right.blockNumber); + if (block !== 0n) return block < 0n ? -1 : 1; + const tx = BigInt(left.transactionIndex) - BigInt(right.transactionIndex); + if (tx !== 0n) return tx < 0n ? -1 : 1; + const log = BigInt(left.logIndex) - BigInt(right.logIndex); + if (log !== 0n) return log < 0n ? -1 : 1; + return left.observationId.localeCompare(right.observationId); + }); +} + +function provenance(subsystem: string, localPathHint: string, generatedAt: string): JsonObject { + return { + subsystem, + origin: "live", + chainContext: "base-mainnet-canary", + fixturePath: "fixtures/deployments/base-canary-indexer-state.json", + capturedAt: generatedAt, + localPathHint, + }; +} + +function buildFlowPulseContractEvent(observation: IndexedObservation): FlowPulseContractEvent { + return { + schema: "flowmemory.flowpulse_contract_event.v0", + interfaceName: "IFlowPulse", + eventName: "FlowPulse", + eventSignatureText: FLOWPULSE_EVENT_SIGNATURE_TEXT, + eventTopic0: observation.eventSignature, + expectedTopic0: FLOWPULSE_EVENT_TOPIC0, + topicMatchesContract: observation.eventSignature.toLowerCase() === FLOWPULSE_EVENT_TOPIC0.toLowerCase(), + sourceContract: observation.emittingContract, + pulseTypeId: observation.pulseType, + pulseTypeName: contractPulseTypeName(observation.pulseType), + indexed: { + pulseId: observation.pulseId, + rootfieldId: observation.rootfieldId, + actor: observation.actor, + }, + payload: { + subject: observation.subject, + commitment: observation.commitment, + parentPulseId: observation.parentPulseId, + sequence: observation.sequence, + occurredAt: isoFromUnixSeconds(observation.occurredAt, new Date(0).toISOString()), + uri: observation.uri, + }, + receiptLocator: { + chainId: observation.chainId, + blockNumber: observation.blockNumber, + blockHash: observation.blockHash, + txHash: observation.txHash, + transactionIndex: observation.transactionIndex, + logIndex: observation.logIndex, + receiptStatus: observation.receiptStatus, + }, + receiptDerivedFields: [ + "blockHash", + "txHash", + "transactionIndex", + "logIndex", + "receiptStatus", + ], + }; +} + +function buildContractEventRef(signal: MemorySignal): FlowPulseContractEventRef { + return { + signalId: signal.signalId, + eventName: signal.contractEvent.eventName, + eventTopic0: signal.contractEvent.eventTopic0, + sourceContract: signal.contractEvent.sourceContract, + pulseTypeId: signal.contractEvent.pulseTypeId, + pulseTypeName: signal.contractEvent.pulseTypeName, + txHash: signal.contractEvent.receiptLocator.txHash, + logIndex: signal.contractEvent.receiptLocator.logIndex, + }; +} + +function buildCanaryDashboardData( + deployment: DeploymentArtifact, + indexer: IndexerPersistence, + checkpoint: CanaryCheckpoint, + paths: CanaryDashboardPaths, +): CanaryDashboardData { + if (indexer.state.source !== "base-mainnet-canary-rpc") { + throw new Error(`expected canary indexer source, received ${indexer.state.source}`); + } + if (checkpoint.safety.productionReady !== false || deployment.productionReady !== false) { + throw new Error("canary dashboard refuses production-ready deployment artifacts"); + } + + const generatedAt = checkpoint.generatedAt; + const observations = sortObservations(indexer.state.observations); + const statusByObservation = new Map(observations.map((observation) => [ + observation.observationId, + observationLifecycleToFlowMemoryStatus(observation.lifecycleState), + ])); + const memorySignals: MemorySignal[] = observations.map((observation) => { + const status = statusByObservation.get(observation.observationId) ?? "observed"; + const signalCore = { + observationId: observation.observationId, + pulseId: observation.pulseId, + rootfieldId: observation.rootfieldId, + pulseType: observation.pulseType, + sequence: observation.sequence, + }; + + return { + schema: "flowmemory.memory_signal.v0", + signalId: stableId("flowmemory.canary.memory_signal.v0", signalCore), + observationId: observation.observationId, + pulseId: observation.pulseId, + rootfieldId: observation.rootfieldId, + signalType: pulseTypeName(observation.pulseType), + status, + chainId: observation.chainId, + emittingContract: observation.emittingContract, + blockNumber: observation.blockNumber, + blockHash: observation.blockHash, + txHash: observation.txHash, + transactionIndex: observation.transactionIndex, + logIndex: observation.logIndex, + actor: observation.actor, + subject: observation.subject, + commitment: observation.commitment, + parentPulseId: observation.parentPulseId, + sequence: observation.sequence, + occurredAt: isoFromUnixSeconds(observation.occurredAt, generatedAt), + uri: observation.uri, + summary: `${pulseTypeName(observation.pulseType).replaceAll("_", " ")} canary pulse ${observation.sequence}`, + contractEvent: buildFlowPulseContractEvent(observation), + }; + }); + const signalByObservation = new Map(memorySignals.map((signal) => [signal.observationId, signal])); + const latestTransitionByPulse = new Map(); + const currentRootByRootfield = new Map(); + const rootflowTransitions: RootflowTransition[] = []; + + for (const observation of observations) { + if (observation.duplicateKind === "exactDuplicate") continue; + const signal = signalByObservation.get(observation.observationId); + if (signal === undefined) { + throw new Error(`missing MemorySignal for ${observation.observationId}`); + } + + const previousRoot = currentRootByRootfield.get(observation.rootfieldId) ?? ZERO_ROOT; + const attemptedRoot = observation.pulseType === "2" ? observation.subject : previousRoot; + const status = statusByObservation.get(observation.observationId) ?? "observed"; + const nextRoot = observation.pulseType === "2" && (status === "finalized" || status === "observed") + ? attemptedRoot + : previousRoot; + const transitionCore = { + rootfieldId: observation.rootfieldId, + observationId: observation.observationId, + pulseId: observation.pulseId, + previousRoot, + attemptedRoot, + nextRoot, + status, + sequence: observation.sequence, + }; + const transition: RootflowTransition = { + schema: "flowmemory.rootflow_transition.v0", + transitionId: stableId("flowmemory.canary.rootflow_transition.v0", transitionCore), + rootfieldId: observation.rootfieldId, + observationId: observation.observationId, + pulseId: observation.pulseId, + parentPulseId: observation.parentPulseId, + parentTransitionId: latestTransitionByPulse.get(observation.parentPulseId) ?? null, + memorySignalId: signal.signalId, + memoryReceiptId: null, + reportId: null, + previousRoot, + attemptedRoot, + nextRoot, + status, + blockNumber: observation.blockNumber, + txHash: observation.txHash, + sequence: observation.sequence, + reasonCodes: ["canary.no_verifier_report"], + contractEventRef: buildContractEventRef(signal), + }; + + rootflowTransitions.push(transition); + latestTransitionByPulse.set(observation.pulseId, transition.transitionId); + if (nextRoot !== previousRoot) { + currentRootByRootfield.set(observation.rootfieldId, nextRoot); + } + } + + const rootfieldBundles = indexer.state.rootfields.map((rootfield) => { + const signals = memorySignals.filter((signal) => signal.rootfieldId === rootfield.rootfieldId); + const transitions = rootflowTransitions.filter((transition) => transition.rootfieldId === rootfield.rootfieldId); + const latestTransition = transitions.at(-1) ?? null; + const latestRoot = currentRootByRootfield.get(rootfield.rootfieldId) ?? deployment.rootfield.latestRoot ?? ZERO_ROOT; + const counts = { + observations: signals.length, + transitions: transitions.length, + receipts: 0, + verified: 0, + failed: 0, + unresolved: 0, + unsupported: 0, + reorged: transitions.filter((transition) => transition.status === "reorged").length, + }; + const bundleCore = { + rootfieldId: rootfield.rootfieldId, + latestRoot, + latestTransitionId: latestTransition?.transitionId ?? null, + status: latestTransition?.status ?? "observed", + counts, + }; + + return { + schema: "flowmemory.rootfield_bundle.v0", + id: stableId("flowmemory.canary.rootfield_bundle.v0", bundleCore), + bundleId: stableId("flowmemory.canary.rootfield_bundle.v0", bundleCore), + rootfieldId: rootfield.rootfieldId, + latestRoot, + latestTransitionId: latestTransition?.transitionId ?? null, + status: latestTransition?.status ?? "observed", + transitionIds: transitions.map((transition) => transition.transitionId), + memorySignalIds: signals.map((signal) => signal.signalId), + memoryReceiptIds: [], + verifierReportIds: [], + counts, + lastUpdated: generatedAt, + provenance: provenance("indexer", paths.indexerPath, generatedAt), + }; + }); + + const rootfieldRows = indexer.state.rootfields.map((rootfield) => { + const bundle = rootfieldBundles.find((candidate) => candidate.rootfieldId === rootfield.rootfieldId); + return { + id: rootfield.rootfieldId, + rootfieldId: rootfield.rootfieldId, + owner: deployment.rootfield.owner, + schemaHash: deployment.rootfield.schemaHash, + metadataHash: deployment.rootfield.metadataHash, + latestRoot: bundle?.latestRoot ?? deployment.rootfield.latestRoot, + latestObservationId: rootfield.latestObservationId, + pulseCount: rootfield.pulseCount, + workLaneIds: ["MEMORY_REFRESH", "STEERING_VALIDATION"], + evidenceUri: deployment.docsPath, + status: bundle?.status ?? "observed", + lastUpdated: generatedAt, + provenance: provenance("indexer", paths.indexerPath, generatedAt), + }; + }); + + return { + metadata: { + schema: "flowmemory.dashboard.fixture.v0", + generatedAt, + mode: "canary", + description: "Generated Base mainnet canary dashboard data from the guarded FlowPulse reader. It is canary-only and not a production-readiness claim.", + fixturePath: paths.dashboardOutPath, + runtimeDataPath: paths.dashboardRuntimePath, + canary: { + deploymentArtifactPath: paths.deploymentPath, + indexerStatePath: paths.indexerPath, + checkpointPath: paths.checkpointPath, + docsPath: deployment.docsPath, + productionReady: false, + sourceVerification: deployment.sourceVerification, + readWindow: { + fromBlock: checkpoint.fromBlock, + toBlock: checkpoint.toBlock, + finalizedBlock: checkpoint.finalizedBlockNumber, + }, + counts: { + observations: checkpoint.observationCount, + rejectedLogs: checkpoint.rejectedLogCount, + duplicates: checkpoint.duplicateCount, + contracts: deployment.contracts.length, + }, + contracts: deployment.contracts, + boundaries: deployment.boundaries, + }, + futureGeneratedPaths: { + indexer: paths.indexerPath, + verifier: "not-applicable-for-canary-v0", + devnet: "not-applicable-for-canary-v0", + hardware: "not-applicable-for-canary-v0", + }, + }, + chain: { + chainId: deployment.network.chainId, + name: "Base mainnet V0 canary", + environment: "mainnet", + settlementContext: "Guarded canary read from deployed V0 FlowPulse surfaces; not production protocol readiness.", + currentBlock: Number(checkpoint.toBlock), + finalizedBlock: Number(checkpoint.finalizedBlockNumber ?? checkpoint.lastIndexedBlock), + source: "live", + lastUpdated: generatedAt, + }, + flowPulseObservations: observations.map((observation) => ({ + id: observation.observationId, + observationId: observation.observationId, + pulseId: observation.pulseId, + rootfieldId: observation.rootfieldId, + eventSignature: observation.eventSignature, + blockNumber: observation.blockNumber, + blockHash: observation.blockHash, + txHash: observation.txHash, + transactionIndex: observation.transactionIndex, + logIndex: observation.logIndex, + receiptStatus: observation.receiptStatus, + actor: observation.actor, + pulseType: observation.pulseType, + subject: observation.subject, + commitment: observation.commitment, + parentPulseId: observation.parentPulseId, + sequence: observation.sequence, + occurredAt: isoFromUnixSeconds(observation.occurredAt, generatedAt), + uri: observation.uri, + summary: `${pulseTypeName(observation.pulseType).replaceAll("_", " ")} from Base canary reader`, + status: statusByObservation.get(observation.observationId) ?? "observed", + lastUpdated: generatedAt, + provenance: provenance("indexer", paths.indexerPath, generatedAt), + })), + rootfields: rootfieldRows, + workLanes: [], + workReceipts: [], + verifierReports: [], + rootflowTransitions: rootflowTransitions.map((transition) => ({ + ...transition, + id: transition.transitionId, + lastUpdated: generatedAt, + provenance: provenance("indexer", paths.indexerPath, generatedAt), + })), + memorySignals: memorySignals.map((signal) => ({ + ...signal, + id: signal.signalId, + lastUpdated: generatedAt, + provenance: provenance("indexer", paths.indexerPath, generatedAt), + })), + memoryReceipts: [], + rootfieldBundles, + agentMemoryViews: rootfieldBundles.map((bundle) => ({ + schema: "flowmemory.agent_memory_view.v0", + id: stableId("flowmemory.canary.agent_memory_view.v0", bundle.bundleId), + viewId: stableId("flowmemory.canary.agent_memory_view.v0", bundle.bundleId), + rootfieldId: bundle.rootfieldId, + status: bundle.status, + latestRoot: bundle.latestRoot, + latestTransitionId: bundle.latestTransitionId, + signalIds: bundle.memorySignalIds, + receiptIds: [], + transitionIds: bundle.transitionIds, + warnings: [ + "Canary data is live-read but not verifier-backed.", + "Source verification and operator policy must be completed before any production claim.", + ], + localOnly: false, + lastUpdated: generatedAt, + provenance: provenance("worker", paths.indexerPath, generatedAt), + })), + devnetBlocks: [], + hardwareNodes: [], + alerts: [{ + id: stableId("flowmemory.canary.alert.v0", deployment.name), + incidentId: stableId("flowmemory.canary.alert.v0", deployment.name), + severity: "info", + title: "Canary mode", + summary: "Base mainnet canary logs are visible, but verifier reports, source verification, multisig ownership, and production hook wiring are not complete.", + openedAt: generatedAt, + linkedObjectIds: deployment.contracts.map((contract) => contract.address), + recommendedAction: "Use this view for launch demonstrations and operator review only.", + status: "unresolved", + lastUpdated: generatedAt, + provenance: provenance("alerts", paths.deploymentPath, generatedAt), + }], + }; +} + +export function generateCanaryDashboard(paths: CanaryDashboardPaths = DEFAULT_CANARY_DASHBOARD_PATHS): CanaryDashboardData { + const deployment = readJson(paths.deploymentPath); + const indexer = readJson(paths.indexerPath); + const checkpoint = readJson(paths.checkpointPath); + const dashboard = buildCanaryDashboardData(deployment, indexer, checkpoint, paths); + + writeJson(paths.dashboardOutPath, dashboard); + writeJson(paths.dashboardRuntimePath, dashboard); + + return dashboard; +} + +function parseCliPaths(): CanaryDashboardPaths { + const args = process.argv.slice(2); + const paths = { ...DEFAULT_CANARY_DASHBOARD_PATHS }; + const optionMap: Record = { + "--deployment": "deploymentPath", + "--indexer": "indexerPath", + "--checkpoint": "checkpointPath", + "--dashboard-out": "dashboardOutPath", + "--dashboard-runtime-out": "dashboardRuntimePath", + }; + + for (let index = 0; index < args.length; index += 1) { + const key = optionMap[args[index]]; + if (key !== undefined) { + const value = args[index + 1]; + if (value === undefined) { + throw new Error(`${args[index]} requires a path value`); + } + paths[key] = value; + index += 1; + } + } + + return paths; +} + +if (process.argv[1] === fileURLToPath(import.meta.url)) { + process.chdir(REPO_ROOT); + const paths = parseCliPaths(); + const dashboard = generateCanaryDashboard(paths); + + console.log(JSON.stringify({ + service: "flowmemory-canary-dashboard-v0", + dashboardOutPath: resolve(paths.dashboardOutPath), + dashboardRuntimePath: resolve(paths.dashboardRuntimePath), + observations: dashboard.flowPulseObservations.length, + memorySignals: dashboard.memorySignals.length, + rootflowTransitions: dashboard.rootflowTransitions.length, + productionReady: false, + }, null, 2)); +} diff --git a/services/flowmemory/test/flowmemory.test.ts b/services/flowmemory/test/flowmemory.test.ts index 6d0d9710..fe740c89 100644 --- a/services/flowmemory/test/flowmemory.test.ts +++ b/services/flowmemory/test/flowmemory.test.ts @@ -10,6 +10,10 @@ import { generateLaunchCore, type DashboardData, } from "../src/generate-launch-core.ts"; +import { + DEFAULT_CANARY_DASHBOARD_PATHS, + generateCanaryDashboard, +} from "../src/generate-canary-dashboard.ts"; import { FLOW_MEMORY_STATUSES, verifierStatusToFlowMemoryStatus, @@ -96,6 +100,31 @@ test("generates concrete Rootflow and Flow Memory V0 outputs", () => { } }); +test("generates Base canary dashboard output from committed deployment artifacts", () => { + const dir = mkdtempSync(join(tmpdir(), "flowmemory-canary-dashboard-")); + const paths = { + ...DEFAULT_CANARY_DASHBOARD_PATHS, + dashboardOutPath: join(dir, "flowmemory-dashboard-base-canary-v0.json"), + dashboardRuntimePath: join(dir, "runtime-flowmemory-dashboard-base-canary-v0.json"), + }; + + try { + const dashboard = generateCanaryDashboard(paths); + + assert.equal(dashboard.metadata.schema, "flowmemory.dashboard.fixture.v0"); + assert.equal(dashboard.metadata.mode, "canary"); + assert.equal(dashboard.chain.chainId, "8453"); + assert.equal(dashboard.chain.source, "live"); + assert.equal(dashboard.flowPulseObservations.length, 4); + assert.equal(dashboard.memorySignals.length, 4); + assert.equal(dashboard.rootflowTransitions.length, 4); + assert.equal(dashboard.verifierReports.length, 0); + assert.equal(dashboard.alerts[0].severity, "info"); + } finally { + rmSync(dir, { recursive: true, force: true }); + } +}); + function assertDashboardCoversStatuses(dashboard: DashboardData): void { const records = [ ...dashboard.flowPulseObservations, diff --git a/tests/LiveV0Package.t.sol b/tests/LiveV0Package.t.sol index a8b9f6fb..5e4041d9 100644 --- a/tests/LiveV0Package.t.sol +++ b/tests/LiveV0Package.t.sol @@ -12,6 +12,7 @@ import {WorkDebtScheduler} from "../contracts/WorkDebtScheduler.sol"; import {WorkReceiptRegistry} from "../contracts/WorkReceiptRegistry.sol"; import {IArtifactRegistry} from "../contracts/interfaces/IArtifactRegistry.sol"; import {IReceiptVerifier} from "../contracts/interfaces/IReceiptVerifier.sol"; +import {IUniswapV4SwapHookLike} from "../contracts/interfaces/IUniswapV4SwapHookLike.sol"; import {IVerifierRegistry} from "../contracts/interfaces/IVerifierRegistry.sol"; import {IWorkDebtScheduler} from "../contracts/interfaces/IWorkDebtScheduler.sol"; import {IWorkerRegistry} from "../contracts/interfaces/IWorkerRegistry.sol"; @@ -627,6 +628,46 @@ contract LiveV0PackageTest { _assertTrue(keccak256(bytes(uri)) == keccak256("flowmemory://uniswap-v4/after-swap")); } + function testFlowMemoryHookAdapterExposesUniswapV4AfterSwapShape() public { + FlowMemoryHookAdapter adapter = new FlowMemoryHookAdapter(); + bytes32 rootfieldId = keccak256("rootfield.v4"); + bytes32 commitment = keccak256("hook.commitment.v4"); + bytes32 parentPulseId = keccak256("parent.pulse"); + IUniswapV4SwapHookLike.PoolKey memory key = IUniswapV4SwapHookLike.PoolKey({ + currency0: address(0x1000), + currency1: address(0x2000), + fee: 3000, + tickSpacing: 60, + hooks: address(adapter) + }); + IUniswapV4SwapHookLike.SwapParams memory params = IUniswapV4SwapHookLike.SwapParams({ + zeroForOne: true, + amountSpecified: -1 ether, + sqrtPriceLimitX96: 42 + }); + bytes memory hookData = + adapter.encodeSwapHookData(rootfieldId, commitment, parentPulseId, "flowmemory://uniswap-v4/canary-after-swap"); + + vm.recordLogs(); + (bytes4 selector, int128 hookDelta) = adapter.afterSwap(address(this), key, params, int256(123), hookData); + LiveV0Vm.Log[] memory logs = vm.getRecordedLogs(); + + bytes32 poolId = keccak256(abi.encode(key.currency0, key.currency1, key.fee, key.tickSpacing, key.hooks)); + _assertTrue(selector == adapter.UNISWAP_V4_AFTER_SWAP_SELECTOR()); + _assertTrue(hookDelta == 0); + _assertTrue(logs.length == 2); + _assertTrue(logs[1].topics[0] == FLOWPULSE_SIGNATURE); + _assertTrue(logs[1].topics[2] == rootfieldId); + _assertTrue(logs[1].topics[3] == bytes32(uint256(uint160(address(this))))); + _assertSwapPulseData( + logs[1].data, + poolId, + commitment, + parentPulseId, + "flowmemory://uniswap-v4/canary-after-swap" + ); + } + function testFlowMemoryHookAdapterRejectsZeroCommitment() public { FlowMemoryHookAdapter adapter = new FlowMemoryHookAdapter(); @@ -649,7 +690,52 @@ contract LiveV0PackageTest { adapter.afterSwap(address(this), keccak256("pool.alpha"), bytes32(0), keccak256("commitment"), ""); } + function testFlowMemoryHookAdapterRejectsEmptyUniswapV4HookData() public { + FlowMemoryHookAdapter adapter = new FlowMemoryHookAdapter(); + IUniswapV4SwapHookLike.PoolKey memory key = IUniswapV4SwapHookLike.PoolKey({ + currency0: address(0x1000), + currency1: address(0x2000), + fee: 3000, + tickSpacing: 60, + hooks: address(adapter) + }); + IUniswapV4SwapHookLike.SwapParams memory params = IUniswapV4SwapHookLike.SwapParams({ + zeroForOne: true, + amountSpecified: -1 ether, + sqrtPriceLimitX96: 42 + }); + + vm.expectRevert(FlowMemoryHookAdapter.EmptyHookData.selector); + adapter.afterSwap(address(this), key, params, int256(0), ""); + } + function _assertTrue(bool condition) private pure { if (!condition) revert AssertionFailed(); } + + function _assertSwapPulseData( + bytes memory data, + bytes32 expectedSubject, + bytes32 expectedCommitment, + bytes32 expectedParentPulseId, + string memory expectedUri + ) private pure { + ( + uint8 pulseType, + bytes32 subject, + bytes32 flowPulseCommitment, + bytes32 decodedParentPulseId, + uint64 sequence, + uint64 occurredAt, + string memory uri + ) = abi.decode(data, (uint8, bytes32, bytes32, bytes32, uint64, uint64, string)); + + _assertTrue(pulseType == 4); + _assertTrue(subject == expectedSubject); + _assertTrue(flowPulseCommitment == expectedCommitment); + _assertTrue(decodedParentPulseId == expectedParentPulseId); + _assertTrue(sequence == 1); + _assertTrue(occurredAt > 0); + _assertTrue(keccak256(bytes(uri)) == keccak256(bytes(expectedUri))); + } }