--- eip: 8352 title: Cross-Chain Event Archive description: Minimal envelope for recording, on an EVM chain, events that occurred on another EVM chain. author: Nicolas Assouad (@nicolasassouad) discussions-to: https://ethereum-magicians.org/t/erc-8352-cross-chain-event-archive/28271 status: Draft type: Standards Track category: ERC created: 2026-07-27 --- ## Abstract This ERC defines a minimal envelope for recording, on an EVM chain, events that occurred on another EVM chain. It standardizes a single `EventArchived` log format carrying a monotonic `version`, an event identifier, and view functions to check whether a given source event has been archived and at which version. Corrections to an erroneous record are expressed as higher-versioned records under the same identifier. One optional extension adds a push-based write function. How archived records are produced, transported, or authorized is out of scope, so a single indexer can ingest cross-chain event history from any conforming contract without knowing its write path. ## Motivation As the EVM ecosystem fragments across L2s and sidechains, protocols increasingly need historical event data from one chain to be accessible on another. Real-world asset protocols, for example, must maintain a complete, auditable history of on-chain events (token transfers, compliance attestations, net asset value updates) across every EVM chain where their assets are issued or traded. There is no standard way to archive events from a source chain onto a destination chain. This ERC defines a minimal envelope that: - Preserves the full provenance of the original event - Requires no knowledge of the source contract's ABI to ingest - Is agnostic to the trust model and transport of the writer ## Specification The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. ### Core interface ```solidity pragma solidity ^0.8.0; interface IEventArchive { event EventArchived( uint256 indexed sourceChainId, bytes32 indexed sourceTxHash, address indexed sourceAddress, uint256 sourceLogIndex, uint256 sourceBlockNumber, uint256 version, bytes payload ); function isArchived( uint256 sourceChainId, bytes32 sourceTxHash, uint256 sourceLogIndex ) external view returns (bool); function latestVersion( uint256 sourceChainId, bytes32 sourceTxHash, uint256 sourceLogIndex ) external view returns (uint256); } ``` ### Event identifier and versioning The identifier of an archived event is the tuple: ``` eventId = (sourceChainId, sourceTxHash, sourceLogIndex) ``` - The first `EventArchived` log emitted for an `eventId` MUST carry `version` 1. - Each subsequent `EventArchived` log for the same `eventId` MUST carry the previous `version` incremented by exactly 1. Versions are strictly monotonic with no gaps, and a record with `version` greater than 1 is a correction of the record at the preceding version. - `isArchived` MUST return true if and only if the contract has emitted at least one `EventArchived` log for the given `eventId`. - `latestVersion` MUST return the highest `version` emitted for the `eventId`, and 0 if the `eventId` has never been archived. The invariant `isArchived(...) == (latestVersion(...) != 0)` MUST hold for the same arguments. The log is append-only; an emitted record is never modified, re-emitted, or deleted. The current payload of an `eventId` is the payload of its highest `version` and MAY be empty. ### Fields - `sourceChainId` is the [EIP-155](./eip-155.md) chain id of the source chain (for example, `1` for Ethereum mainnet, `8453` for Base, `42161` for Arbitrum One). - `sourceTxHash` is the 32-byte hash of the source-chain transaction that emitted the event. - `sourceAddress` is the contract that emitted the event on the source chain. - `sourceLogIndex` is the position of the event within the source transaction, disambiguating multiple events emitted by the same transaction. - `sourceBlockNumber` is the number of the source-chain block containing the transaction. - `version` is the record's revision number for its `eventId`, starting at 1 for the original record and incremented by exactly 1 for each correction. - `payload` is the original event, ABI-encoded (its topics and data). `sourceChainId`, `sourceTxHash`, and `sourceAddress` are indexed, covering the common consumer queries: all events from a chain, a specific transaction, or a specific source contract. ### Write path How records are produced is out of scope for the core interface. A conforming implementation MAY expose the optional writer extension below, MAY emit records from a cross-chain messaging endpoint, MAY require validity proofs against the source chain, or MAY use any other write path, provided the emitted records and the versioning rule conform to this specification. Implementations MUST document their write path and its authorization model. ### Optional extension: push-based write Implementations MAY expose the following interface to support generic relayer tooling: ```solidity interface IEventArchiveWriter { function archiveEvent( uint256 sourceChainId, bytes32 sourceTxHash, address sourceAddress, uint256 sourceLogIndex, uint256 sourceBlockNumber, bytes calldata payload ) external; } ``` - A successful call MUST emit `EventArchived`. If the `eventId` has never been archived, the emitted record MUST carry `version` 1; otherwise it MUST carry `latestVersion` incremented by 1 and is a correction of the current record. - Implementations MAY restrict which callers are authorized to invoke `archiveEvent`. - A repeat call records a correction rather than reverting or acting as a no-op. Relayers that require idempotent submission SHOULD check `isArchived` or `latestVersion` before submitting, to avoid recording spurious corrections. ## Rationale ### Record format, not trust model This ERC standardizes the shape of an archived record, as [ERC-20](./eip-20.md) standardizes a token interface without constraining how the token is administered. An `EventArchived` log is a claim by the emitting contract; its strength depends entirely on the write path, from a single authorized relayer to a proof-verified bridge. ### Core and extensions The mandatory surface is only what consumers depend on: the `EventArchived` event, the versioning rule, `isArchived`, and `latestVersion`. The write path is the sole optional extension because it legitimately differs across implementations (batched submission, proof arguments). ## Backwards Compatibility No backwards compatibility issues found. ## Reference Implementation The normative interfaces ([`IEventArchive`](../assets/eip-8352/interfaces/IEventArchive.sol) and [`IEventArchiveWriter`](../assets/eip-8352/interfaces/IEventArchiveWriter.sol)) and an abstract reference base ([`EventArchive`](../assets/eip-8352/EventArchive.sol)) are released under CC0-1.0 (see [Copyright](#copyright)). `EventArchive` tracks a version per `eventId` and exposes an internal, unguarded `_archiveEvent` that records version 1 on the first write and the next version on each subsequent write. Concrete contracts wrap it with their own write path and authorization model. ## Security Considerations An `EventArchived` record is an assertion by the emitting contract that an event occurred on a source chain. This ERC provides no mechanism to verify that assertion; the trustworthiness of a record is exactly that of the emitting contract's write path. Implementations MUST document who is authorized to archive events and what verification, if any, is performed before a record is emitted. Consumers SHOULD verify archived events against the source chain when trust is critical, and SHOULD treat records from archives with undocumented write paths as untrusted. Consumers MUST NOT assume archived records are correct solely because they exist; erroneous records cannot be deleted, only corrected by a higher-versioned record. The write path is also the correction path: recording version 1 and recording a higher version are the same operation, so restricting who can archive also governs who can rewrite history. A compromised writer can both fabricate records and correct existing ones. Implementations that need stricter control over corrections than over first writes MUST distinguish the two themselves. Consumers displaying archived data SHOULD surface the existence of corrections rather than silently showing the latest payload. Because a repeat write records a correction rather than reverting, a relayer that submits the same `eventId` twice creates a spurious version. Idempotent relayers SHOULD check `isArchived` or `latestVersion` before submitting. The versioning rule orders records within a single conforming contract. It does not prevent two different contracts from archiving the same source event; consumers aggregating across multiple archives MUST deduplicate by `eventId` themselves. ## Copyright Copyright and related rights waived via [CC0](../LICENSE.md).