Acknowledgment & Signature System
Canonical token sale acknowledgments, signature verification, and recovery policy.
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)
- Canonical acknowledgments live in
code/php/token_sale_terms.php. /token-sale/renders the checkbox list directly from the canonical text.- The browser signs EIP-712 typed data (or EIP-191 fallback).
- The server verifies, rebuilds canonical payloads, and stores them.
- The UI only trusts server confirmation (localStorage is cache only).
/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
- Checkbox rendering
- The modal checkboxes are generated from
- This prevents drift between what the user checks and what they sign.
getTokenSaleAcknowledgments() in token-sale/index.php.
- Signing
- Implemented in
token-sale/terms.php. - Primary: EIP-712 typed data signature (ethers v6).
- Fallback: EIP-191
signMessage(plain text).
- Client verification step
- The signing flow sends the signature to
- The UI only marks acceptance once the server confirms.
code/php/agreements.php?action=sign.
- 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
- The canonical JSON payload is stored (ignores client-provided message).
- For EIP-191:
- The server rebuilds the expected message with
buildEip712Payload() and verifies the signature.
buildTokenSaleMessage() and verifies exact match.
Storage:
- Table:
agreements(created bycode/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.
- Retrieve the record
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;
- Verify the signature
- EIP-712:
messageis stored as canonical JSON withdomain,types,message.- Verify with a tool like
ethers.verifyTypedData(domain, types, message, signature). - EIP-191:
messageis stored as the exact signed text.- Verify with
ethers.verifyMessage(message, signature).
- Prove the exact text signed
- For EIP-712, the
acknowledgmentsfield in the stored payload is the - The
doc_hashis the keccak256 of the stored message for audit integrity.
canonical string from getTokenSaleAcknowledgments().
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
- Edit
code/php/token_sale_terms.php. - Confirm
/token-sale/modal and/terms-of-sale/render updated text. - Update
TOKEN_SALE_TERMS_VERSIONincode/php/variables.php. - Verify
agreements.php?action=signandaction=checkflow. - Optionally update
docs/tos-signature-system.md.
Smoke Test Checklist
- Open
/token-sale/and confirm checkbox text matches/terms-of-sale/. - Sign and confirm
agreementstable row fordoc_type=token_sale_v1. - Reload and confirm modal does not appear (server confirms signed).
- Attempt to contribute with server offline (should block signing).