Docs

Acknowledgment & Signature System

Canonical token sale acknowledgments, signature verification, and recovery policy.

Signature & Verification Policy Token Sale

LockItIn Token Sale Acknowledgment System

Purpose

Provide a single, canonical, non-drifting flow for token sale acknowledgments:

users check the exact statements, sign an EIP-712 payload, and the server

stores a verifiable record tied to their wallet.

Overview (High Level)

  1. Canonical acknowledgments live in code/php/token_sale_terms.php.
  2. /token-sale/ renders the checkbox list directly from the canonical text.
  3. The browser signs EIP-712 typed data (or EIP-191 fallback).
  4. The server verifies, rebuilds canonical payloads, and stores them.
  5. The UI only trusts server confirmation (localStorage is cache only).
  6. /terms-of-sale/ is read-only reference and links back to /token-sale/.

Canonical Source of Truth

File: code/php/token_sale_terms.php

  • getTokenSaleTermsVersion() returns the current sale terms version.
  • getTokenSaleAcknowledgments() returns the exact, newline-delimited,
  • bullet-prefixed acknowledgments string that is used everywhere.

Any change to the acknowledgments or version MUST be made here first.

Do not edit text directly in the UI or docs.

Frontend Flow (Token Sale App)

Files: token-sale/index.php, token-sale/terms.php

  1. Checkbox rendering
  • The modal checkboxes are generated from
  • getTokenSaleAcknowledgments() in token-sale/index.php.

  • This prevents drift between what the user checks and what they sign.
  1. Signing
  • Implemented in token-sale/terms.php.
  • Primary: EIP-712 typed data signature (ethers v6).
  • Fallback: EIP-191 signMessage (plain text).
  1. Client verification step
  • The signing flow sends the signature to
  • code/php/agreements.php?action=sign.

  • The UI only marks acceptance once the server confirms.
  1. Cache rules
  • localStorage is only written after server confirmation.
  • checkSaleTermsStatus() fails closed if the server is unavailable.

EIP-712 Payload (Token Sale)

Doc type: token_sale_v1

Domain (server + client must match):

  • name: "LockItIn Token Sale"
  • version: "1"
  • chainId: 8453 (Base mainnet)
  • verifyingContract: LOCKIT_LAUNCHVAULT_ADDRESS

Primary type:

TokenSaleAcknowledgment(address purchaser,string action,string version,uint256 timestamp,string acknowledgments)

Message fields:

  • purchaser: checksummed wallet address
  • action: TOKEN_SALE_ACKNOWLEDGMENT
  • version: TOKEN_SALE_TERMS_VERSION
  • timestamp: current Unix timestamp (seconds)
  • acknowledgments: canonical multiline string from getTokenSaleAcknowledgments()

Backend Verification & Storage

File: code/php/agreements.php

Endpoints:

  • POST agreements.php?action=check
  • Input: { wallet, doc_type }
  • Output: { success, signed, signed_at }
  • POST agreements.php?action=sign
  • Input: { wallet, signature, message, timestamp, doc_type, is_eip712 }
  • Output: { success, doc_hash, signature_type }

Verification rules:

  • For EIP-712:
  • The server reconstructs the canonical payload using
  • buildEip712Payload() and verifies the signature.

  • The canonical JSON payload is stored (ignores client-provided message).
  • For EIP-191:
  • The server rebuilds the expected message with
  • buildTokenSaleMessage() and verifies exact match.

Storage:

  • Table: agreements (created by code/php/db.php).
  • Stored fields: wallet, doc_type, doc_hash, signature, message, signed_at.

Recovery & Proof of Signature

The server stores the exact signed payload plus the signature, so you can

reconstruct and verify consent later.

  1. Retrieve the record
  2. Example SQL:

       SELECT wallet, doc_type, signature, message, signed_at, doc_hash
       FROM agreements
       WHERE wallet = '0x...'
         AND doc_type = 'token_sale_v1'
       ORDER BY signed_at DESC
       LIMIT 1;
    
  1. Verify the signature
  • EIP-712:
  • message is stored as canonical JSON with domain, types, message.
  • Verify with a tool like ethers.verifyTypedData(domain, types, message, signature).
  • EIP-191:
  • message is stored as the exact signed text.
  • Verify with ethers.verifyMessage(message, signature).
  1. Prove the exact text signed
  • For EIP-712, the acknowledgments field in the stored payload is the
  • canonical string from getTokenSaleAcknowledgments().

  • The doc_hash is the keccak256 of the stored message for audit integrity.

This provides a verifiable chain: wallet -> signature -> exact message content

-> canonical acknowledgments and version at time of signing.

UI/Docs Alignment

The full terms page at /terms-of-sale/ (served from

code/html/terms-of-sale.php) renders the exact acknowledgments from

getTokenSaleAcknowledgments() for transparency and review.

Reference agreements (PDFs) are linked from:

  • /token-sale/ (main terms + modal)
  • /terms-of-sale/
  • /docs/legal/

Operational Notes

  • The server is authoritative; if it is down, signing is blocked.
  • Timestamp window is enforced server-side (currently 5 minutes).
  • If acknowledgments or version change, expect all users to re-sign.

Update Checklist

  1. Edit code/php/token_sale_terms.php.
  2. Confirm /token-sale/ modal and /terms-of-sale/ render updated text.
  3. Update TOKEN_SALE_TERMS_VERSION in code/php/variables.php.
  4. Verify agreements.php?action=sign and action=check flow.
  5. Optionally update docs/tos-signature-system.md.

Smoke Test Checklist

  • Open /token-sale/ and confirm checkbox text matches /terms-of-sale/.
  • Sign and confirm agreements table row for doc_type=token_sale_v1.
  • Reload and confirm modal does not appear (server confirms signed).
  • Attempt to contribute with server offline (should block signing).