Use Cases
This section contains use-case-driven integration guides that show how real businesses would integrate with the Oak protocol using the Contracts SDK. Each guide tells a complete business story — from the problem to the on-chain solution — with illustrative code snippets.
These are documentation guides, not runnable scripts. For executable API-reference examples, see API Reference Examples.
Multi-token ERC-20 support
Campaigns are not tied to a single asset like USDC or USDT. GlobalParams owns the canonical currency → ERC-20[] mapping: initialize seeds currencies and tokensPerCurrency at deploy, and the protocol admin can later addTokenToCurrency / removeTokenFromCurrency (emitting TokenAddedToCurrency / TokenRemovedFromCurrency). getTokensForCurrency(currency) returns the full address list for a currency key.
When CampaignInfoFactory.createCampaign runs, it resolves the campaign's campaignData.currency to that list and stores a cached copy on CampaignInfo (with isTokenAccepted for O(1) checks). In the SDK you can read the live list with campaign.getAcceptedTokens() or cross-check globalParams.getTokensForCurrency(currency) against what you passed at creation.
Every pledge or payment specifies pledgeToken / paymentToken; treasuries revert if the token is not accepted. Balances, fees, refunds, and raised-amount aggregates are per token address, in each token's native decimals (normalized where the protocol compares across tokens). The stories below use USDC or USDT as examples; in production, use any address from your campaign's accepted-token list.
Use Cases
| Use Case | Demo | Contract(s) Used | Business Story |
|---|---|---|---|
| Escrow | Healthcare Escrow | CampaignInfoFactory + PaymentTreasury | MedConnect holds patient payments until a doctor confirms service delivery |
| Marketplace | E-Commerce Marketplace | CampaignInfoFactory + PaymentTreasury | CeloMarket locks buyer funds until seller ships; on-chain escrow with line items |
| Prepayment | Automotive Prepayment | CampaignInfoFactory + TimeConstrainedPaymentTreasury | Karma Automotive holds vehicle deposits with time-based expiry; expired funds are swept to the platform/protocol, and end-customer refunds are handled per Karma's policy |
| Flexible Funding | Community Project | CampaignInfoFactory + KeepWhatsRaised | TechForge runs keep-what's-raised campaigns with partial withdrawals, tips, and gateway fees |
| Crowdfunding | Creative Campaign | CampaignInfoFactory + AllOrNothing | ArtFund runs all-or-nothing campaigns with NFT-backed pledges and reward tiers |
How to Read These Demos
Each guide follows the same structure:
- The Business — who is the company and what do they do?
- Why Oak? — what specific problems does Oak solve for them?
- Contracts Used — which Oak smart contracts power the solution
- Roles — who are the actors (platform, buyer, seller, backer)?
- Integration Flow — step-by-step walkthrough with code snippets
- Architecture Diagram — visual flow of interactions
- Key Takeaways — lessons and patterns to apply to your own integration
Contract-to-Use-Case Mapping
Understanding which Oak contract to use for your business:
CampaignInfoFactory + PaymentTreasury
Best for: escrow, marketplace, service payments
Create a CampaignInfo contract first (holds NFT receipts, accepted token list), then deploy a PaymentTreasury via TreasuryFactory. Funds are held until the platform confirms delivery/service. Supports line items, external fees, batch operations, and refund flows.
- Healthcare Escrow — service escrow
- E-Commerce Marketplace — product escrow with line items
CampaignInfoFactory + TimeConstrainedPaymentTreasury
Best for: prepayments, deposits, time-bound commitments
Same setup and interface as PaymentTreasury, but with on-chain time windows. After the campaign deadline plus the platform claim delay, the platform admin can call claimExpiredFunds() to sweep idle balances on-chain (recipients are defined by the contract); align end-customer refunds with your product policy.
- Automotive Prepayment — vehicle deposit with 6-month delivery window
CampaignInfoFactory + KeepWhatsRaised
Best for: flexible funding, hardware startups, ongoing projects
Like AllOrNothing, creates a campaign with goals and deadlines, but the creator keeps whatever is raised. Supports partial withdrawals (with platform approval), tips, payment gateway fees, and configurable refund delays.
- Community Project — hardware startup with partial withdrawals and tips
CampaignInfoFactory + AllOrNothing
Best for: crowdfunding, fundraising, community-driven projects
Creates a campaign with a goal and deadline. Pledges mint NFTs. If the goal is met, the creator withdraws. If not, backers get full refunds. Supports reward tiers with physical/digital items.
- Creative Campaign — indie film funding with reward tiers
Common Patterns Across All Demos
Simulate Before Send
Every write operation should be simulated first to catch errors without spending gas:
await entity.simulate.someMethod(args); // dry run — reverts throw typed errors
const txHash = await entity.someMethod(args); // actual transaction
await oak.waitForReceipt(txHash);
Multicall for Dashboard Reads
Batch multiple reads into a single RPC call:
// PaymentTreasury / KeepWhatsRaised — all three methods available
const [raised, available, refunded] = await oak.multicall([
() => treasury.getRaisedAmount(),
() => treasury.getAvailableRaisedAmount(),
() => treasury.getRefundedAmount(),
]);
// AllOrNothing — uses getRaisedAmount + getLifetimeRaisedAmount (no getAvailableRaisedAmount)
const [raised, lifetime, refunded] = await oak.multicall([
() => aonTreasury.getRaisedAmount(),
() => aonTreasury.getLifetimeRaisedAmount(),
() => aonTreasury.getRefundedAmount(),
]);
Fee Lifecycle
Fees are always disbursed before withdrawal:
await treasury.disburseFees(); // protocol + platform fees distributed
await treasury.withdraw(); // AllOrNothing / PaymentTreasury — sends all remaining funds
KeepWhatsRaised uses a different withdrawal model —
withdraw(token, amount)for partial withdrawals andclaimFund()for the final withdrawal:await kwrTreasury.withdraw(USDC_TOKEN_ADDRESS, 3_000_000000n); // partial withdrawal
await kwrTreasury.claimFund(); // final withdrawal after deadline
Signer Flexibility
The SDK supports three levels of signer configuration for different architectures:
// Client-level (most common for backends)
const oak = createOakContractsClient({ privateKey: PLATFORM_KEY, ... });
// Per-entity (useful for dApps after wallet connect)
const treasury = oak.paymentTreasury(address, { signer: walletClient });
// Per-call (multi-role systems)
await treasury.confirmPayment(id, buyer, { signer: adminWalletClient });