You're testing Logbook Beta on Sui Testnet. Learn more →
Documentation

Learn Logbook

Everything you need to create blockchain-verified surveys, understand the technology, and get the most out of Logbook.

DocsSmart Contract
9 min read

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
Think of it as a vending machine: you put in money, select an item, and the machine gives you exactly what you selected. No human intervention, no exceptions, no "special cases". The rules are the rules.

#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
2. Cheap Storage Sui's storage model makes it economically viable to store real data on-chain. That's why Logbook can store:
  • Full question text
  • All answer options
  • Every individual response
  • Vote counts (for campaigns with public answers)
On Ethereum, this would cost hundreds or thousands of dollars. On Sui, it costs fractions of a cent.

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)
4. Programmable Transactions Sui allows complex multi-step transactions that execute atomically. This means:
  • 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_time has passed
  • Answers, everyone with access (address-list campaigns): released to allowlisted addresses
The contract never sees plaintext: encryption and decryption happen in the browser.

#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
Checks:
  • 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_nonce
  • response_ids, questions, option_votes
  • has_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()
The web app reads objects through the Sui full node (gRPC) and history through the indexer (GraphQL).

#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 campaign
  • creator: Address of the campaign creator
  • access: Who can open the campaign
  • visibility: Who can read the answers
  • end_time: Campaign end timestamp
  • timestamp: When the event occurred

#CampaignUpdated

Emitted when a campaign is updated (before any responses).
  • campaign_id: Unique ID of the campaign
  • creator: Address of the campaign creator
  • timestamp: When the update occurred

#CampaignDeleted

Emitted when a campaign is deleted (only possible with zero responses).
  • campaign_id: Unique ID of the deleted campaign
  • creator: Address of the campaign creator
  • timestamp: When the deletion occurred

#ResponseSubmitted

Emitted when a participant submits a response.
  • campaign_id: Campaign that received the response
  • creator: Campaign creator's address
  • respondent: Address of the participant
  • response_index: Index of this response (0-based)
  • timestamp: When the response was submitted
These events can be monitored by off-chain services for notifications and indexing.