Onchain Agent Memory on Robinhood Chain with Smart Contracts

A trade on Robinhood Chain is final and public in 100 milliseconds. The memory that led to it usually lives in a private database. Anchoring memory onchain closes that gap.
Robinhood Chain calls itself AI native: a permissionless Ethereum Layer 2 on Arbitrum technology, chain ID 4663, with ERC-4337 accounts and session keys built for agents that trade tokenized stocks and DeFi around the clock. That solves what an agent is allowed to do. It says nothing about what the agent knew.
This post shows how to connect Lighthouse Memory to Robinhood Chain smart contracts, so an agent's memory checkpoints and decision records are committed onchain, verifiable by anyone, and usable by the contracts themselves.
What Belongs Onchain, and What Does Not
Memory itself does not belong in contract storage. It is text and vectors, it grows constantly, and paying gas for every byte makes no sense. What belongs onchain is a commitment to the memory: its CID.
| Where it lives | Why | |
|---|---|---|
| Memory content, tags, vectors | Lighthouse Memory (Filecoin, Walrus, S3) | Cheap, durable, searchable |
| Memory CID | Robinhood Chain contract | Tiny, final, timestamped, public |
| Verification | Anyone, offchain | Fetch by CID and re-hash |
Contracts cannot fetch files from IPFS. They do not need to. A contract holding a CID holds a binding commitment to exactly one version of the memory, and anyone can check the memory against it later.

A Useful Detail: Every Memory CID Fits in 32 Bytes
Lighthouse Memory CIDs are always CIDv1, raw codec, sha2-256. Because the version, codec and hash function never change, the only variable part is the 32 byte digest. Store that as a bytes32 and you pay for one storage slot instead of a long string, then rebuild the full CID offchain.
import { CID } from 'multiformats/cid'
import * as raw from 'multiformats/codecs/raw'
import { sha256 } from 'multiformats/hashes/sha2'
import * as Digest from 'multiformats/hashes/digest'
import { toHex, hexToBytes } from 'viem'
export const cidToBytes32 = (cid) => toHex(CID.parse(cid).multihash.digest)
export const bytes32ToCid = (hex) =>
CID.create(1, raw.code, Digest.create(sha256.code, hexToBytes(hex))).toString()
Pattern 1: An Onchain Memory Registry
The simplest pattern is a registry where each agent posts checkpoints, snapshot CIDs of its entire memory, with a block timestamp.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
contract AgentMemoryRegistry {
struct Checkpoint { bytes32 digest; uint64 entries; uint64 at; }
mapping(bytes32 => address) public operatorOf; // agentId => operator
mapping(bytes32 => Checkpoint[]) private checkpoints;
event AgentRegistered(bytes32 indexed agentId, address operator);
event MemoryCheckpoint(bytes32 indexed agentId, bytes32 digest, uint64 entries, uint256 index);
function register(bytes32 agentId) external {
require(operatorOf[agentId] == address(0), "taken");
operatorOf[agentId] = msg.sender;
emit AgentRegistered(agentId, msg.sender);
}
function checkpoint(bytes32 agentId, bytes32 digest, uint64 entries) external {
require(msg.sender == operatorOf[agentId], "not operator");
checkpoints[agentId].push(Checkpoint(digest, entries, uint64(block.timestamp)));
emit MemoryCheckpoint(agentId, digest, entries, checkpoints[agentId].length - 1);
}
function latest(bytes32 agentId) external view returns (Checkpoint memory) {
Checkpoint[] storage c = checkpoints[agentId];
require(c.length > 0, "none");
return c[c.length - 1];
}
}
On the agent side, snapshotIndex() gives one CID for the whole namespace, and the agent commits it:
const snap = await memory.snapshotIndex() // { cid, entries, gatewayUrl }
await walletClient.writeContract({
address: REGISTRY,
abi: registryAbi,
functionName: 'checkpoint',
args: [AGENT_ID, cidToBytes32(snap.cid), BigInt(snap.entries)],
chain: robinhoodChain, // id 4663
})
Restore an agent from the chain
This doubles as an onchain pointer service. A new machine reads the latest checkpoint from the contract and rebuilds memory, with no database and no CID copied by hand:
const { digest } = await publicClient.readContract({
address: REGISTRY, abi: registryAbi, functionName: 'latest', args: [AGENT_ID],
})
await memory.rebuildLocal(bytes32ToCid(digest)) // { added, total }
| Lighthouse pointer service | Onchain registry | |
|---|---|---|
| Stores | Latest snapshot CID per namespace and network | Every checkpoint, forever |
| Auth | Session JWT | Operator wallet |
| History | Latest only (PUT overwrites) | Full, timestamped |
| Readable by | You | Anyone, including contracts |
| Cost | Free with the service | Gas per checkpoint |
Use the pointer service for convenience and the registry when checkpoints need to be public evidence.
Pattern 2: Commit Before Act
The stronger pattern makes memory a precondition for action. The agent must commit the CID of its decision record in the same transaction as the trade. The contract does not judge the reasoning; it guarantees a reasoning record exists, is fixed before execution, and is permanently linked to the trade.
// Illustrative. In production, fold this check into the agent's smart account
// or module so funds never sit in a shared executor.
contract CommittedExecutor {
event DecisionExecuted(address indexed agent, bytes32 decisionDigest, address target, bytes4 selector);
function execute(bytes32 decisionDigest, address target, bytes calldata data) external {
require(decisionDigest != bytes32(0), "decision record required");
(bool ok, ) = target.call(data);
require(ok, "call failed");
emit DecisionExecuted(msg.sender, decisionDigest, target, bytes4(data[:4]));
}
}
The contract cannot confirm the digest points to a real record; it only guarantees a commitment was fixed before the call and is linked to it forever. An auditor later fetches the record and checks it against the digest.
Because Robinhood Chain supports ERC-4337 smart accounts with session keys, the owner can scope the agent's session key so it may call CommittedExecutor.execute and nothing else. Every action the agent takes then carries a memory commitment by construction.
On the agent side, write the decision with flushEvery: 1 so it is uploaded and has a CID before the transaction:
const inputs = await memory.recall('NVDA stock token risk limits and latest signal', { limit: 5 })
const record = await memory.remember(
`Buy 12 NVDA stock tokens. Inputs: ${inputs.map((m) => m.id).join(', ')}. Reason: ${reason}`,
{ tags: ['decision', 'trade'], metadata: { inputs: inputs.map((m) => m.cid) } }
)
// with flushEvery: 1 the result already includes record.cid
await smartAccount.sendUserOperation({
calls: [{ to: EXECUTOR, data: encodeExecute(cidToBytes32(record.cid), POOL, swapCalldata) }],
})
The existing post AI Trading Agents on Robinhood Chain Need Verifiable Memory covers why this matters for trading. This is the contract level version.
Pattern 3: Private Commitments With Memwal
Batched blobs are plaintext, so a CID posted onchain lets anyone read the memory. Sometimes that is the point. Often, strategy memory is sensitive.
The memwal engine gives a clean answer. Memories are SEAL-encrypted on Walrus. With MEMWAL_IPFS_PIN=off, the SDK does not publish a plaintext mirror; it computes each record's CID locally. Post that CID onchain and you have a commitment without disclosure: the chain proves a specific record existed at a specific block, and nobody can read it. When an auditor needs to check, you reveal the record, and verify() or any CID library confirms it matches the committed hash.
MEMWAL_IPFS_PIN=off # local CIDs only; nothing public except the hash you choose to commit
See Encrypted Memory for AI Agents with Memwal and SEAL for exactly what is and is not encrypted.
What You Can Build
Auditable trading agents. Every trade on Robinhood Chain linked to a committed decision record. See How to Audit and Monitor AI Agents with Verifiable Memory.
Agent reputation. A public history of checkpoints and outcomes lets others evaluate an agent before delegating capital to it.
Stock token research agents. Research memory checkpointed onchain alongside the documents it references. See Stock Tokens and RWA Documents on Robinhood Chain with Lighthouse Storage.
Portable agents. Any operator with the registry address can restore an agent's memory from its latest checkpoint.
Frequently Asked Questions
Can smart contracts read AI agent memory? Not directly; contracts cannot fetch offchain files. They store a commitment, the memory's CID, which anyone can verify against the memory itself.
Why not store memory on Robinhood Chain directly? Contract storage is priced per byte and memory grows constantly. Storing the 32 byte CID digest is enough to bind the chain to exact memory content.
How do I restore an agent from an onchain checkpoint?
Read the latest checkpoint from the registry, rebuild the CID from the digest, and call rebuildLocal(cid).
Can onchain memory commitments stay private?
Yes. Use the memwal engine with MEMWAL_IPFS_PIN=off. Records are SEAL-encrypted and only a locally computed CID is committed.
What is Robinhood Chain's chain ID?
Mainnet is 4663. Testnet is 46630.
Get Started
- Lighthouse Memory quick start
- Rebuild and recovery with snapshots
- Memwal engine
- Robinhood Chain developer docs
New to the chain? Read What Is Robinhood Chain and Where Lighthouse Fits In.
Stay in Touch
Learn more at the website, docs, or GitHub. Join the community on Discord, X, Telegram, and LinkedIn.
































































































