Smart Contract
The Logbook protocol is implemented as a Move module — a smart contract written in the Move programming language — on the Sui blockchain.
#What is a Smart Contract?
A smart contract is a program that runs on the blockchain. Unlike traditional software that runs on a company's servers, smart contracts:
- Execute automatically: Once deployed, the code runs exactly as written — no one can change the rules
- Are transparent: Anyone can read the code and verify what it does
- Are trustless: You don't need to trust the developer — you can verify the logic yourself
- Are permanent: Once deployed, the contract exists as long as the blockchain exists
#Why Sui is Different
Most blockchains (Ethereum, Solana, etc.) have significant limitations:
#Traditional Blockchains
- Limited data storage: Storing data is extremely expensive, so most apps store minimal on-chain data
- Simple logic only: Complex programs are costly and slow to execute
- Account-based model: Data is tied to accounts, making it hard to work with individual objects
#Sui's Advantages
Sui was designed from the ground up to solve these problems:
1. Object-Centric Model Every piece of data is an "object" with its own ID. In Logbook:
- Each campaign is an object
- Each response is its own object attached to the campaign
- You can reference any specific piece of data by its ID
- Full question text
- All answer options
- Every individual response
- Vote counts (for campaigns with public answers)
3. Move Language Sui uses Move, a language designed specifically for blockchain with:
- Built-in safety guarantees (no reentrancy attacks, no overflow bugs)
- Resource-oriented programming (assets can't be accidentally destroyed or duplicated)
- Clear ownership model (every object has exactly one owner)
- Campaign creation and question setup happen in one transaction
- Response submission and vote counting happen together (when answers are public)
- Either everything succeeds, or nothing changes
#What This Means for Logbook
Because of Sui's unique features, Logbook can:
- Store every response permanently on-chain (not just a hash)
- Execute voting logic in the smart contract itself
- Let anyone verify public results by reading the blockchain directly, and let everyone allowed to decrypt verify private ones
- Offer a web2-like experience (Google login, sponsored transactions) with web3 guarantees
#Contract Address
Testnet Package ID: 0x3af97244b4f93f68a426a36b6349d78e0d667230b0f060d25d2ae0f49ad63d3d
This is the original package ID: the address of all types and the Seal namespace. The package is upgradeable; the latest version (documents, 2026-09-17) is 0x9d859c72da1f2d030d8f6f87355a7db6e227e706038950dca0b6dfc2f0d10936.
The source code lives in the protocol-v2 directory of the repository.
#Core Objects
#Campaign
The main shared object representing a survey or poll:
- id: Unique identifier (UID)
- version: Contract version the campaign was created with
- creator: Address of the campaign creator
- title, description: Plaintext for open campaigns, encrypted otherwise
- questions: Vector of Question objects (text and options are encrypted for non-open campaigns)
- access: Who can open the campaign — 0 (anyone), 1 (password link), 2 (address list)
- visibility: Who can read answers — 0 (everyone), 1 (everyone with access), 2 (voters), 3 (creator), 4 (after the end date)
- allowlist: Set of addresses for access = 2
- responses: Table of Response objects keyed by respondent address
- response_ids: Response object ids in submission order
- total_responses: Number of responses
- seal_nonce: Random 32 bytes that are part of every Seal identity of this campaign
- created_at, end_time: Timestamps (ms)
- content_key: The campaign content key, encrypted with Seal (released to the address list, or to the creator for recovery)
- link_key: The content key wrapped with the campaign password (password campaigns only)
#Question
Stored within a Campaign:
- question_type: 0 (single), 1 (multiple), 2 (text)
- text: The question text
- required: Whether the question is required
- options: Vector of answer options
- option_votes: Votes per option (counted only when answers are public)
- text_response_count, allow_other, other_votes, other_responses: "Other" and text answer bookkeeping (public answers only)
#Response
A participant's submission, stored as a child object of the campaign so the campaign object stays small:
- respondent: Address of the participant
- timestamp: Submission timestamp
- answers: Plaintext answers (question index → answer), only when answers are visible to everyone
- sealed: Encrypted answers for every other visibility (a Seal encrypted object, or content-key ciphertext for password campaigns)
- key_wrap: Password campaigns only — the content key sealed for this respondent, so they can reopen the campaign on another device
#CampaignRegistry
Global shared registry tracking all campaigns:
- all_campaigns: Vector of campaign IDs
- campaigns_by_creator: Map of creator to their campaigns
#Seal Access Policy
Encrypted answers and content keys are Seal encrypted objects. Their identity encodes the campaign creator, the campaign's seal_nonce, a tag (content or answers) and, for per-respondent answers, the respondent address. Seal key servers only release a key after simulating the contract's seal_approve function for the requesting address:
- Content key: released to the creator, and to allowlisted addresses (address-list campaigns)
- Answers, voters only: released to the creator and to anyone who has responded
- Answers, only creator: each response is encrypted for its respondent; released to that respondent and to the creator
- Answers, after the end date: released to the respondent at any time, and to everyone with access once
end_timehas passed - Answers, everyone with access (address-list campaigns): released to allowlisted addresses
#Entry Functions
#create_campaign
Creates a new campaign on-chain.
Parameters:
- registry: &mut CampaignRegistry
- title, description: String (encrypted client-side unless access = 0)
- question_types, question_texts, question_required, question_allow_other: Vectors
- all_options, options_per_question: Flattened options data
- access: u8
- visibility: u8
- allowlist: vector
- seal_nonce: address
- content_key: vector
(Seal encrypted, empty for open campaigns) - link_key: vector
(password-wrapped key, password campaigns only) - end_time: u64
- clock: &Clock
#submit_response
Records a participant's answers.
Parameters:
- campaign: &mut Campaign
- question_indices: vector
, answers: vector (plaintext, only when visibility = 0) - sealed: vector
(encrypted answers, required for every other visibility) - key_wrap: vector
- clock: &Clock
- Campaign not ended
- Participant hasn't already responded
- Address is on the list for address-list campaigns
- Plaintext answers only when answers are public, encrypted answers otherwise
#add_to_allowlist / remove_from_allowlist
Change the address list of an address-list campaign (creator only). Newly added addresses can decrypt the campaign immediately; removed addresses can no longer obtain keys they have not fetched before.
#update_campaign
Updates campaign details (creator only, only if zero responses).
#delete_campaign
Permanently removes a campaign from the blockchain (creator only, only if zero responses).
#create_campaign_with_documents / set_documents / set_document_storage
Added by the second package upgrade. A campaign can commit to up to 10 files: name, type, size, a SHA-256 commitment to the file, and where the file is stored (Walrus blob id). The commitment is written in the creation transaction; set_documents can replace the documents only while the campaign has no responses. set_document_storage only updates where the file lives and can never change a hash. Open campaigns commit to the plain SHA-256 of the file; private campaigns commit to SHA-256(salt + file) and keep the salt encrypted with the campaign content key.
#attach_report / detach_report
Added by the first package upgrade. The creator records a pointer to a document stored off-chain (Walrus): its kind (AI analysis or results certificate), the Walrus blob id, the SHA-256 of the stored bytes, size and whether it is encrypted. Reports of campaigns with restricted access or visibility must be encrypted. Stored as dynamic fields of the campaign, one per kind.
#seal_approve
Called by Seal key servers (in a simulated transaction) to check whether an address may receive a decryption key. It aborts unless the policy above allows it.
#View Functions
Read-only functions to query campaign data:
creator,access,visibility,end_time,total_responses,seal_nonceresponse_ids,questions,option_voteshas_responded(campaign, address),is_allowlisted(campaign, address)all_campaigns(registry)- Constants:
access_open(),access_link(),access_allowlist(),vis_everyone(),vis_access(),vis_voters(),vis_creator(),vis_after_end()
#Events
The contract emits events for tracking all operations on the blockchain. These events are used for efficient querying and indexing.
#CampaignCreated
Emitted when a new campaign is created.campaign_id: Unique ID of the campaigncreator: Address of the campaign creatoraccess: Who can open the campaignvisibility: Who can read the answersend_time: Campaign end timestamptimestamp: When the event occurred
#CampaignUpdated
Emitted when a campaign is updated (before any responses).campaign_id: Unique ID of the campaigncreator: Address of the campaign creatortimestamp: When the update occurred
#CampaignDeleted
Emitted when a campaign is deleted (only possible with zero responses).campaign_id: Unique ID of the deleted campaigncreator: Address of the campaign creatortimestamp: When the deletion occurred
#ResponseSubmitted
Emitted when a participant submits a response.campaign_id: Campaign that received the responsecreator: Campaign creator's addressrespondent: Address of the participantresponse_index: Index of this response (0-based)timestamp: When the response was submitted