Skip to main content

Contract

The contract is intentionally minimal (~90 lines): it holds USDC per campaign, releases payouts, and refunds unspent budget. It holds no user data — no Telegram IDs, transcripts, or scores are written on-chain. Verification state lives off-chain in Postgres.

Functions

depositBudget(campaignId, amount)

Advertiser locks USDC into escrow for a campaign. Pulls amount via transferFrom, credits campaignBalance[campaignId], and records the caller as campaignDepositor only if not already set (the first-depositor guard). Anyone can add budget to a campaign; nobody can redirect the refund recipient after the first deposit. Caller: any address (typically the advertiser wallet).

creditDirectDeposit(campaignId, depositor, amount)

Credits USDC that arrived by plain transfer (e.g. a Base Pay payment) to a campaign. Only credits funds that are actually unallocated in the contract (balance − totalHeld). Applies the same first-depositor guard. Caller: Canvas relayer only.

releasePayout(campaignId, to, amount)

Releases amount USDC from a campaign balance to a single recipient. A passing verification produces two releasePayout calls — one for the group owner leg (90%) and one for the Canvas fee leg (10%) — each tracked separately. Caller: Canvas relayer only.

refundUnusedBudget(campaignId, amount)

Returns unspent campaign budget to the recorded campaignDepositor. No lock period. Caller: Canvas relayer only.

withdrawUnallocated(to, amount)

Recovers orphaned USDC not allocated to any campaign (balance − totalHeld). Caller: Canvas relayer only.

setRelayer(newRelayer)

Rotates the authorized relayer address. Caller: current relayer only.

campaignBalance(campaignId) · totalHeld()

Views. Remaining escrowed USDC for a campaign, and the sum of all campaign balances. Invariant: totalHeld == Σ campaignBalance.

Events

Payout split

The split is computed off-chain in payout-batch.ts before the contract calls fire; the contract receives pre-calculated amounts. Per passing verification:
  • Owner leg: 90% of the winning bid price → group owner wallet
  • Fee leg: 10% of the winning bid price → Canvas platform wallet (0x8ddD5c22c38d9641B23fe197869DA843fD209b49)

Security

  • The relayer wallet (0xbD5f911E8621Ec144681d17a8b59DcDd3f9356d9) is the only address authorized to move escrowed funds. Its key is held in the production environment only.
  • The first-depositor guard on campaignDepositor closes a dust-deposit refund-hijack vector.
  • The contract holds USDC only, and advertiser budget is refundable at any time.
  • The contract is not yet audited — an external audit is planned before the permissionless marketplace opens (see Roadmap). It runs a whitelist phase with test-scale amounts in the meantime.

Onchain audit trail

Every passing verification is settled by releasePayout transactions on Base. The owner-leg transaction hash is included in the completion payload delivered to the advertiser, giving a verifiable per-completion settlement record.