Custody model
Canvas is non-custodial for users. End users never connect a wallet, sign a transaction, or see crypto during verification. Group owners and advertisers control their own wallets; Canvas never holds their private keys. Advertiser budget lives in the escrow contract, not in a Canvas-controlled wallet. Funds can only move via the contract’s payout and refund functions, and unused budget is refundable at any time.Smart contract
Design choices that reduce risk:
- Single privileged role. Only the relayer address (
0xbD5f911E8621Ec144681d17a8b59DcDd3f9356d9) can release payouts or refunds. Its key is held only in the production environment. - First-depositor guard. The
campaignDepositoris fixed on first deposit, closing a dust-deposit refund-hijack vector. - Balance invariant.
totalHeldalways equals the sum of campaign balances; releases and refunds check the campaign balance before transferring. - Refundable by design.
refundUnusedBudgetandwithdrawUnallocatedreturn unspent USDC to the recorded depositor.
Dashboard authentication
The advertiser and group-owner dashboards are gated by a wallet signature, not a password. The client signs the exact messageCanvas auth: <timestamp> and passes the wallet, timestamp, and signature. The server verifies the signature on-chain (supporting both EOAs and smart-contract wallets via ERC-1271/6492) and rejects signatures outside a 5-minute window to limit replay. Verification failures fail closed.
Verification integrity
- Fail-closed scoring. If the Kimi scorer is unavailable after retries, the user is admitted but no payout is released and the advertiser is not billed — the protocol never pays out on an unscored transcript.
- Rate limiting. Per-user/per-group attempt caps and per-group sequential processing prevent join-flood budget drain. See Anti-gaming.
- Recovery sweeps. Background sweeps detect and recover verifications stranded mid-flow so users are never left permanently muted and payouts are never double-released.
Secrets and infrastructure
- Secrets (bot token, relayer key, database URL, scoring API key) are injected at runtime by the host and are never committed to source.
.envfiles are gitignored. - The bot runs webhook-only with a shared-secret header on the webhook path.
- Payout-moving code paths are covered by tests, and the escrow contract has a Foundry test suite including a first-depositor-guard regression test.