Writer is an onchain writing platform. Content is permanently stored on Optimism through smart contracts.
WriterFactory:0x28c7721ECff2246a9277CAd46ab2124f69Efd88E
ColorRegistry:0x7Bf5B616f5431725bCE61E397173cd6FbFaAC6F1

Factory contract that deploys Writer + WriterStorage pairs using CREATE2 for deterministic addresses.
create(title, admin, managers, salt)§Deploy a new Writer and WriterStorage contract pair.
Parameters
Returns
(address writerAddress, address storeAddress)Events
WriterCreated(writerAddress, storeAddress, admin, title, managers)computeWriterStorageAddress(salt)§Pre-compute the address a WriterStorage would be deployed to with the given salt.
Access: View
Parameters
Returns
addresscomputeWriterAddress(title, admin, managers, salt)§Pre-compute the address a Writer would be deployed to with the given parameters.
Access: View
Parameters
Returns
address
Main logic contract for managing entries with role-based access control.
getEntryCount()§Returns the total number of entries.
Access: View
Returns
uint256getEntryIds()§Returns an array of all entry IDs.
Access: View
Returns
uint256[]getEntry(id)§Returns the full entry struct including chunks, author, and timestamps.
Access: View
Parameters
Returns
Entry { createdAtBlock, updatedAtBlock, chunks[], totalChunks, receivedChunks, author }getEntryContent(id)§Returns the concatenated content of all chunks for an entry.
Access: View
Parameters
Returns
stringgetEntryChunk(id, index)§Returns a specific chunk's content.
Access: View
Parameters
Returns
stringcreateWithChunk(chunkCount, content)§Create a new entry with the first chunk of content. Caller becomes the entry author.
Access: WRITER_ROLE
Parameters
Returns
(uint256 entryId, Entry entry)Events
EntryCreated(id, author)ChunkReceived(author, id, index, content)createWithChunkWithSig(signature, nonce, chunkCount, content)§Create a new entry with the first chunk via EIP-712 signature. Signer becomes the entry author.
Access: Signer must have WRITER_ROLE
Parameters
Returns
(uint256 entryId, Entry entry)Events
EntryCreated(id, author)ChunkReceived(author, id, index, content)addChunk(id, index, content)§Add a chunk to an existing entry at a specific index.
Access: Author + WRITER_ROLE
Parameters
Returns
EntryEvents
ChunkReceived(author, id, index, content)addChunkWithSig(signature, nonce, id, index, content)§Add a chunk to an existing entry via EIP-712 signature.
Access: Signer must be author + WRITER_ROLE
Parameters
Returns
EntryEvents
ChunkReceived(author, id, index, content)update(id, totalChunks, content)§Replace an entry's content. Clears all previous chunks and sets new content.
Access: Author + WRITER_ROLE
Parameters
Returns
EntryEvents
EntryUpdated(id, author)ChunkReceived(author, id, index, content)updateWithSig(signature, nonce, id, totalChunks, content)§Replace an entry's content via EIP-712 signature.
Access: Signer must be author + WRITER_ROLE
Parameters
Events
EntryUpdated(id, author)ChunkReceived(author, id, index, content)remove(id)§Delete an entry.
Access: Author + WRITER_ROLE
Parameters
Events
EntryRemoved(id, author)removeWithSig(signature, nonce, id)§Delete an entry via EIP-712 signature.
Access: Signer must be author + WRITER_ROLE
Parameters
Events
EntryRemoved(id, author)setTitle(newTitle)§Update the writer's title.
Access: DEFAULT_ADMIN_ROLE
Parameters
Events
TitleSet(title)setTitleWithSig(signature, nonce, newTitle)§Update the writer's title via EIP-712 signature.
Access: Signer must have DEFAULT_ADMIN_ROLE
Parameters
Events
TitleSet(title)replaceAdmin(newAdmin)§Transfer admin role to a new address. Revokes admin from the caller.
Access: DEFAULT_ADMIN_ROLE
Parameters

Storage contract that holds all entry data. Only the Writer logic contract can modify state, enforced by the onlyLogic modifier.
Entry struct§The data structure for each entry stored onchain.
Parameters

Simple registry mapping user addresses to their chosen hex color.
setHex(hexColor)§Set your color directly.
Parameters
Events
HexSet(user, hexColor)setHexWithSig(signature, nonce, hexColor)§Set your color via EIP-712 signature.
Parameters
Events
HexSet(user, hexColor)getPrimary(user)§Get a user's hex color.
Access: View
Parameters
Returns
bytes32Public read endpoints. Write endpoints exist but are restricted to authenticated frontend clients (Privy bearer token required) and are intentionally not documented here.
https://api.writer.place
/writer/public§List all public writers.
Response
{ writers: Writer[] }/writer/:address§Get a specific writer and all its entries.
Parameters
Response
{ writer: Writer }/manager/:address§Get all writers managed by an address.
Parameters
Response
{ writers: Writer[] }
/writer/:address/entry/:id§Get a confirmed entry by its onchain ID.
Parameters
Response
{ entry: Entry }/writer/:address/entry/pending/:id§Get a pending entry before onchain confirmation.
Parameters
Response
{ entry: Entry }
/me/:address§Get user data for an address.
Parameters
Response
{ user: User }Entry content goes through a multi-step encoding pipeline before being stored onchain. The content & chunkContent fields in API requests contain the final encoded string, not raw markdown.
markdown → compress → encrypt (optional) → prefix → store
The version prefix at the start of the stored content string indicates how to decode it.
br:Public entry. Brotli compressed, Base64 encoded. No encryption.
br:GxoAAI2pVgqN...
enc:v3:br:Private entry, current format. AES-GCM encrypted with v3 key, Brotli compressed.
enc:v3:br:A7f3kQ9x...
enc:v2:br:Deprecated
enc:br:Deprecated

All content is compressed with Brotli at quality level 11 (maximum), then Base64 encoded. This reduces onchain storage costs.
markdown → UTF-8 encode → brotli compress (quality 11) → base64 encode

Private entries are encrypted after compression using AES-GCM with a 128-bit key and 12-byte random IV.
compressed content → AES-GCM encrypt → prepend IV → base64 encode
The encryption key is deterministically derived from a wallet signature:
personal_signThe key never leaves the client. Only the entry author can decrypt their private entries — the server and contract store opaque ciphertext.
V1, V2, and V3 keys differ only in the message signed during key derivation. V3 is the current default and includes a security warning to only sign on writer.place. V1 and V2 are supported for backward compatibility with older entries. A migration tool is available in the app to re-encrypt legacy entries with the V3 key.

To read an entry, reverse the pipeline based on the prefix.
br:strip prefix → base64 decode → brotli decompress
enc:v3:br:strip prefix → base64 decode → AES-GCM decrypt (v3 key) → brotli decompress
enc:v2:br:Deprecated
enc:br:Deprecated
Public entries are decoded server-side and returned as plaintext in the decompressed field. Private entries are returned as the raw encoded string and decrypted client-side using the author's wallet.