Skip to content
Solidity 0.8.24 · OpenZeppelin 5.x · BSC

Smart contracts

Every unit of budget lives in a contract you can read. SaTT runs on just two audited Solidity contracts — here is exactly what each one does, who can call it, and the guarantees that keep funds safe.

Single-purpose contracts

There is no central treasury. The factory deploys campaigns; each campaign is its own isolated escrow. One pool can never touch another pool’s money. A separate GigMarketplace handles direct fixed-price deals.

AdpoolFactory
Deployer & registry

A single, permissionless contract. It deploys every campaign as a low-cost EIP-1167 clone of one shared Adpool implementation, pulls the advertiser’s budget straight into it, and indexes it so the app can list pools. It holds no funds itself.

Adpool
Per-campaign escrow

One clone per campaign, all running the same audited logic. It escrows the reward budget, registers creators, lets the Oracle validate performance, splits the protocol fee, and pays creators on-chain. Its economic terms are set once at creation and can never change.

GigMarketplace
Direct fixed-price deals

A single escrow for one-to-one work — a creator offer or an advertiser task. Funds lock when hired and release only when the buyer approves; if they go silent the creator self-releases after a review window, and a neutral arbiter resolves any dispute. Same fee, refunds in full.

AdpoolFactory — how funding works

When an advertiser launches a campaign, the factory clones the shared Adpool implementation, initializes the clone, and — in the same transaction — transfers the budget from the advertiser into that pool. It then checks the pool actually received the full amount before recording it, so a deflationary “fee-on-transfer” token that would secretly under-fund the escrow is rejected, not silently broken. Because the clone is created and initialized atomically, there is never a window for anyone to seize an uninitialized pool.

Configured once, applied to every campaign
  • Canonical SaTT token address (sets the 5% fee tier)
  • Ecosystem treasury — receives the ecosystem share of fees
  • Buy-back vault — receives the buy-back share of non-SaTT fees
  • Default Oracle — the validator used when a campaign doesn’t name its own

Efficient by design

Every campaign is an EIP-1167 minimal-proxy clone of one shared, audited Adpool implementation — not a fresh copy of the whole contract. A clone is just a few hundred bytes that delegate to the same logic, so launching a pool costs a small fraction of a full deployment, while each pool stays a fully isolated escrow.

~80%
Less gas to launch a campaign
1
Shared implementation every pool clones
100%
Isolated escrow, one per campaign

Two ways to reward creators

The mode is chosen at creation and can never change afterwards.

Oracle-signed (bounty)

The Oracle signs an EIP-712 attestation of a creator’s metrics and exact reward. The creator submits that signature and claims. Each signed claim is single-use — it can never be replayed — and expires after its deadline.

Pay a fixed bounty per validated result

Proportional (performance)

The Oracle SIGNS each creator’s validated metrics; the creator submits them on-chain themselves, so the Oracle never needs gas. The contract still derives every score on-chain from those metrics and the campaign’s immutable weights — so the split stays enforced by code. Once the pool is finalized, each creator pulls their pro-rata share.

Split one pool across all creators by performance

The AdPool lifecycle — at a glance

From funding to payout, every step below is enforced by the smart contract — no trust required.

AdvertiserCreatorOracleManager
1.

Create & fund

AdvertisercreateAdpool()

Fund a reward pool with any ERC-20. The budget is escrowed in a fresh contract and the fee tier locks in: SaTT 5%, any other token 15%.

2.

Apply with your post

CreatorsubmitPromotion()

Link your social account once (proves ownership), publish your post, then commit it with a wallet signature.

3.

Oracle validates

Oracle

The Oracle fetches the post’s real performance — views, likes and shares.

The payout depends on the pool type

Bounty pool

Oracle-signed
  1. The Oracle signs your exact reward (EIP-712, single-use, expires).
  2. You redeem it on-chain — paid instantly, net of the fee. claimReward()

Performance pool

Proportional
  1. The Oracle signs your metrics; you submit them on-chain yourself (no Oracle gas). recordMetricsSigned()
  2. Scores freeze at finalize (Oracle anytime; anyone after the end date). finalize()
  3. Each creator pulls their pro-rata share of the pool. claim()
Every payout takes one on-chain fee — SaTT pools: 5% (50% ecosystem · 50% burned). Other tokens: 15% (50% buy-back · 50% ecosystem). The Oracle never receives fees.
4.

End & withdraw

ManagerwithdrawRemaining()

After the end date, the manager reclaims the unspent budget. In performance pools only leftover dust is recoverable — every creator’s share stays claimable forever.

Function reference

Every state-changing entry point, who may call it, and what it does.

Create & fund
createAdpool()AdvertiserAdpoolFactory

Deploys a new Adpool and escrows the budget into it in one transaction. Reverts if the token under-funds the pool.

Participate
submitPromotion()CreatorAdpool

Registers the caller’s post in the campaign. Requires the caller’s own EIP-712 signature — you can only enter as yourself.

Mode 1 · Oracle-signed
claimReward()CreatorAdpool

Redeems an Oracle-signed reward. Verifies the signature, enforces single-use and the deadline, splits the fee, and pays the net.

Mode 2 · Proportional
recordMetricsSigned()CreatorAdpool

Submit your OWN metrics using the Oracle’s EIP-712 signature — so the Oracle only signs and never needs gas. A strictly-increasing nonce blocks replay of a stale attestation. The score is computed on-chain.

recordMetrics()OracleAdpool

Operator/seeding fallback: the Oracle records a creator’s metrics directly (paying gas). The trustless, gasless path is recordMetricsSigned. Blocked once finalized.

finalize()Anyone (after end)Adpool

Freezes metrics and opens claiming. The Oracle may finalize any time; once the end date passes, anyone can — so claims always open even if the Oracle goes silent.

claim()CreatorAdpool

Pulls the caller’s pro-rata share after finalization. Shares round down and can never exceed the pool.

Manager controls
withdrawRemaining()ManagerAdpool

After the end date, returns the unspent budget. In Proportional mode it can only sweep the unallocated remainder — never a creator’s unclaimed share.

transferManager()ManagerAdpool

Proposes a new manager — step 1 of a two-step handover. The role only moves once the proposed address calls acceptManager, so it can never be lost to a typo.

acceptManager()Proposed managerAdpool

Step 2: the proposed address accepts and becomes the manager.

setOracle()ManagerAdpool

Proposes a new Oracle. The rotation only takes effect once the proposed address calls acceptOracle, proving the new signing key is live first.

acceptOracle()Proposed oracleAdpool

Step 2: the proposed address accepts and becomes the active Oracle.

Fee model, enforced on-chain

A single fee is taken on each payout — nothing else. The Oracle validates performance but is never a fee recipient.

Funded in SaTT → 5% fee
50% to the ecosystem treasury · 50% burned forever
Funded in any other token → 15% fee
50% to buy-back · 50% to the ecosystem treasury

Why the funds are safe

Non-custodial by design

There is no owner and no admin key. Money can only leave a pool as a validated reward or a post-campaign refund. No one — not us, not the manager, not the Oracle — can move it arbitrarily.

Replay-proof claims

Each Oracle-signed claim is hashed and marked used on redemption, so a signature works exactly once and expires after its deadline.

You can only enter as yourself

Joining a campaign requires an EIP-712 signature from your own wallet, so no one can register another person’s address.

Creator shares are untouchable

In Proportional mode each creator’s share is locked at finalization. The manager’s refund can only reclaim genuine rounding dust — never funds owed to a creator.

Reentrancy-guarded payouts

Every function that moves tokens is protected by OpenZeppelin’s ReentrancyGuard and SafeERC20.

Works with any ERC-20, safely

Funding measures the amount actually received, so deflationary or fee-on-transfer tokens that would under-fund the escrow are rejected up front.

Immutable terms

Token, reward mode, dates, pool size and fee tier are fixed at creation. The rules can’t change under participants mid-campaign.

Audited building blocks

Built on OpenZeppelin 5.x (SafeERC20, EIP712, ECDSA, ReentrancyGuard, Clones) and Solidity 0.8.24 with checked arithmetic.

Clones can’t be hijacked

Each pool is initialized exactly once, atomically, in the transaction that creates it — there is no window to seize an uninitialized clone, and the shared implementation is permanently locked so it can never be used directly.

Every cent is accounted for

On each payout the creator’s net plus the fee split add up to exactly the gross reward — the contract conserves funds precisely, and a payout can never exceed the pool.

Multisig & smart-account ready

Every signature is verified with EIP-1271, so the Oracle can be a multisig or DAO-controlled signer, and creators can act from ERC-4337 smart accounts — not just plain wallets.

Two-step role handover

Manager and Oracle changes are proposed, then accepted by the new address — so control can never be lost to a typo or an unreachable address.

Verification status

The contracts ship with a full automated test suite covering the factory, both reward modes, fee splits, the full lifecycle, and security regressions.

37 / 37
Automated tests passing
~92%
Line coverage
0
Admin keys over funds

Tests and internal review are not a substitute for a formal third-party audit. An independent audit is recommended before any mainnet deployment that holds real value.

On-chain reference

SaTT token (BSC)
BEP-20 · the canonical token behind the 5% fee tier
0x448BEE2d93Be708b54eE6353A7CC35C4933F1156View on BscScan

Read it, then launch it

The contracts do exactly what this page says. When you’re ready, spin up a pool.