How Collective Memory bridges Firebase with an immutable on-chain ledger on Base
Collective Memory (CM) is a social content platform where users create memories (photos, videos, text) and other users express belief in those memories by staking the platform’s native token. The system bridges a traditional Firebase/Firestore web2 backend with an immutable on-chain ledger on Base (Ethereum L2), creating a dual-layer architecture: fast UX in web2, verifiable truth on-chain.
The unit of value in the system. A standard ERC-20 token deployed on Base.
The entire 1B supply is pre-minted to the deployer and transferred to the Treasury wallet. From there, the LedgerRouter moves pATTN in and out of user/memory vaults as directed by the backend.
A user in web2 is a Firebase Auth UID (e.g. ZY7pZu4txwcXw7nnOE0rmBU14g83). On-chain, each user is identified by bytes32(keccak256(userId)).Each user gets two on-chain entities, deployed lazily on first interaction:
Entity
Contract
Purpose
User Vault
MinimalVaultUpgradeable
Holds the user’s pATTN balance and any Content Tokens received from staking
A memory is user-generated content (image, video, text) stored in Firestore under the memory collection. On-chain: bytes32(keccak256(memoryId)).Each memory gets two on-chain entities, also deployed lazily:
Entity
Contract
Purpose
Memory Vault
MinimalVaultUpgradeable
Holds the pATTN staked into this memory (the memory’s TVL)
Memory Token
ContentERC20Upgradeable
Unique ERC-20 representing belief-shares in this memory. 1M supply, 6 decimals. Minted once, distributed to stakers proportionally
Contract: LedgerRouterUUPS.sol (UUPS-upgradeable proxy) Base Mainnet: 0x8B38...6f02
The Router is the single entry point for all on-chain operations. It owns every vault and token (as their OpenZeppelin Ownable owner), meaning only the Router can move funds. External callers must be the Router’s owner (admin) or an approved operator.
The compute*Address() view functions return addresses at zero gas cost. The Create*IfMissing() functions are idempotent — calling them for an already-deployed entity is a no-op.
Vaults and tokens are BeaconProxy instances pointing to shared implementation beacons. Upgrading a beacon implementation upgrades all proxies simultaneously.
The Router uses an allowlist of operator wallets. Backend Cloud Functions hold operator private keys and sign transactions — this is the bridge between web2 and web3.
When a user redeems, pATTN returns and Memory Tokens are burned.The returned pATTN may be more or less than the original stake, depending on how the memory’s TVL has grown through other stakes and injections.
Sync is the most important low-level operation. It moves either pATTN or Memory Tokens between three locations:Every movement emits a canonical Synced event for full auditability. The higher-level Sync(id, scope, target) variant computes the delta automatically and calls the generic mover.
The original architecture used Uniswap V2 to create an automated market for each memory:
Staking = buying Memory Tokens with ATTN through the pool (price goes up)
Redeeming = selling Memory Tokens back for ATTN (price goes down)
Linking memories = the cmLiquidity contract splits ATTN, buys tokens of two memories, and adds cross-liquidity — creating a value bridge between memories
Each memory had a real-time market price driven by supply and demand via the constant product formula x * y = k.
The production system replaced Uniswap pools with a managed ledger for several reasons:
Gas Efficient
No AMM swap fees or slippage per transaction
Predictable Pricing
Token-to-pATTN ratios controlled by the backend
Batch Settlement
Full pipeline runs off-chain, settles on-chain in batches
Gasless UX
Users never need ETH or wallet interactions
Each memory still gets a unique ERC-20 (1M supply, 6 decimals), and the bonding math (how many tokens per pATTN staked) is computed off-chain by the Firebase backend using the same AMM curve principles. The on-chain Router then executes the pre-calculated movements.
The AMM math still governs pricing — it just runs in the Firebase layer rather than in a Uniswap pool. The on-chain contracts serve as the immutable settlement and custody layer.
Each vault is a simple token-holding contract with two operations:
Function
Direction
Description
pull(token, from, amount, memo)
Inbound
Transfers tokens into the vault (requires prior approval)
push(token, to, amount, memo)
Outbound
Transfers tokens out of the vault
Every movement emits a LedgerMove event with a sequential nonce, token address, direction, counterparty, amount, and memo string — creating a complete, auditable on-chain history.
Only the Router (as owner) can call pull and push. No user or external contract can directly move funds from a vault.
The reconciliation pipeline ensures on-chain state stays consistent with the web2 ledger, even if transactions arrive out of order.This “balance-to-moment, then execute” pattern ensures the on-chain state is always consistent with the web2 ledger.