See also: API Reference Examples — executable TypeScript walkthroughs.
The Business
CeloMarket is an online marketplace where independent sellers list physical products (electronics, handmade goods, apparel). Buyers pay using fiat through the platform’s UI, but under the hood, funds flow through crypto rails on the Celo network. Funds are locked until the seller ships the product and the platform confirms delivery, providing buyer protection similar to traditional e-commerce escrow.Why Oak?
CeloMarket needs:- Buyer protection — funds locked until shipment is confirmed
- Multi-line-item orders — product cost, shipping fee, and platform commission as separate line items
- Fee transparency — protocol and platform fees are tracked and disbursed on-chain
- Fiat-to-fiat UX — end users see USD prices; crypto conversion happens behind the scenes
Oak Contracts Used
| Contract | Purpose |
|---|---|
| CampaignInfoFactory | Creates the CampaignInfo contract that holds NFT receipts and the accepted token list |
| TreasuryFactory | Deploys the PaymentTreasury clone linked to the CampaignInfo |
| PaymentTreasury | Holds buyer funds until delivery is confirmed |
Multi-token support
PaymentTreasury is multi-token: each order’spaymentToken must be on the campaign’s accepted-token list (isTokenAccepted). Balances and fee paths are per ERC-20 contract (native decimals). This story uses USDC for pricing clarity; CeloMarket can offer the same UX in “USD” while settling on-chain in any whitelisted stablecoin or other ERC-20 your protocol maps to that currency. GlobalParams.getTokensForCurrency defines the mapping; CampaignInfo.getAcceptedTokens reflects what that campaign was created with.
Roles
| Role | Who | On-Chain Functions |
|---|---|---|
| Platform Admin | CeloMarket backend | createPayment, createPaymentBatch, confirmPayment, confirmPaymentBatch, cancelPayment, claimRefund(paymentId, address) (non-NFT), claimExpiredFunds, claimNonGoalLineItems, pauseTreasury, unpauseTreasury, cancelTreasury |
| Platform Admin or Campaign Owner | CeloMarket or seller | withdraw, cancelTreasury |
| Buyer | End customer | ERC-20 approve, processCryptoPayment, claimRefundSelf(paymentId) (NFT payments) |
| Protocol Admin | Oak protocol | Receives protocol fees (via disburseFees) |
| Any caller | Anyone | disburseFees, all read functions (getPaymentData, getRaisedAmount, paused, etc.) |
Integration Flow
Step 1: Create a CampaignInfo contract
Role: Any caller — createCampaign is permissionless.
Before deploying a PaymentTreasury, CeloMarket needs a CampaignInfo contract. This holds NFT receipts for crypto payments and defines the accepted token list.
Step 2: Deploy the PaymentTreasury
Role: Any caller — deploy on TreasuryFactory is permissionless (the implementation must have been registered and approved during platform onboarding).
CeloMarket deploys a PaymentTreasury linked to the CampaignInfo from Step 1.
Step 3: Buyer places order — two independent payment flows
CeloMarket supports two payment methods. A platform uses one or both depending on its business model — they are not sequential steps.Flow A: Off-chain / fiat payment (createPayment)
Role: Platform Admin — only the platform admin can create payment records.A buyer orders wireless headphones for $79.99. CeloMarket’s backend creates a payment record on-chain. The
createPayment transaction does not pull ERC-20 from the buyer’s wallet — it records the order and pending accounting. The buyer pays through off-chain rails (credit card, bank transfer, etc.). Before confirmPayment, the treasury must hold enough of the payment token on-chain (for example after fiat settlement the platform deposits USDC). The contract checks the treasury balance when confirming.
createPayment, fund the treasury with the agreed token amount before calling confirmPayment (operational path is product-specific).
Confirm after shipment (platform admin)
Role: Platform Admin —The seller uploads a shipping proof (tracking number). After verification and once the treasury holds the required ERC-20, CeloMarket confirms the payment on-chain.confirmPaymentapplies only to payments created withcreatePayment.
Flow B: On-chain crypto payment (processCryptoPayment)
Role: Any caller — processCryptoPayment is permissionless, but the buyer must first approve the treasury to transfer their ERC-20 tokens.
This is a standalone operation — it creates the payment record AND transfers ERC-20 tokens in a single transaction. It does not require or complete a prior createPayment call. An NFT is minted to the buyer as proof of payment.
Before the treasury can pull funds, the buyer must grant an ERC-20 allowance:
Step 4: Read order state — dashboard view
Role: Any caller — all read functions are public.CeloMarket’s admin dashboard reads the payment details and treasury state.
Step 5: Fee disbursement
Role: Any caller — disburseFees is permissionless. Fees are sent to the Protocol Admin and Platform Admin automatically.
Protocol and platform fees are distributed before the seller can withdraw.
Step 6: Seller withdrawal
Role: Platform Admin or Campaign Owner — either party can trigger withdrawal. Funds are always sent to the campaign owner (the seller).The settled amount (product price minus fees) is sent to the seller.
Alternative: Cancellation and refund flows
Three distinct paths exist depending on payment state and type:A) Cancel an unconfirmed off-chain payment (before
confirmPayment):
Role: Platform Admin — cancelPayment works only on unconfirmed, non-expired, non-crypto payments. The transaction drops pending accounting; it does not automatically return ERC-20 already sent to the treasury—recover tokens operationally if needed. Off-chain refunds (credit card reversal, etc.) are handled outside this call.
Role: Platform Admin —claimRefund(paymentId, refundAddress)refunds a confirmed payment where no NFT was minted (confirmPaymentwas called without abuyerAddress, orbuyerAddresswasaddress(0)). The contract verifies the payment is confirmed and hastokenId == 0.
Role: Any caller (NFT owner) —Before callingclaimRefundSelf(paymentId)is for crypto payments (auto-confirmed on creation). The contract looks up the NFT owner, burns the NFT, and sends the refundable amount to that owner. No priorcancelPaymentis needed — crypto payments cannot be cancelled viacancelPayment.
claimRefundSelf, the NFT owner must approve the treasury to manage the NFT. All pledge NFTs live on the CampaignInfo contract (not the treasury itself), so approval uses the CampaignInfo SDK entity:
Claim non-goal line items
Role: Platform Admin — only the platform admin can claim non-goal line items.If the order included line items that don’t count toward the campaign goal (e.g., platform commission, shipping fees configured as non-goal), these accumulate separately. The platform admin can claim them at any time after confirmation.
Pause, unpause, or cancel the treasury
Pause the treasury:Role: Platform Admin — only the platform admin can pause and unpause.If CeloMarket needs to halt operations (e.g., suspected fraud, compliance review):
Role: Platform Admin
Role: Platform Admin or Campaign Owner — either party can cancel.Cancellation is irreversible. After cancellation, buyers can still claim refunds for confirmed NFT payments, but no new payments or withdrawals can happen.
Architecture Diagram
CeloMarket E-Commerce Order FlowKey Takeaways
- ERC-20 approval is required — the buyer must
approvethe treasury contract beforeprocessCryptoPaymentcan transfer tokens createPaymentpath —createPaymentdoes not pull tokens from the buyer; fund the treasury beforeconfirmPaymentprocessCryptoPaymentpath — confirms in one transaction; thendisburseFees/withdraw—do not callconfirmPaymentfor these payments- Multi-token — orders can settle in any accepted
paymentToken; treasury accounting is per token address - Role-based access —
createPayment/confirmPayment/cancelPaymentare platform-admin-only;processCryptoPaymentanddisburseFeesare permissionless;withdrawrequires admin or owner - Three cancellation/refund paths —
cancelPaymentdeletes unconfirmed off-chain records (no on-chain refund);claimRefund(paymentId, address)refunds confirmed non-NFT payments (platform admin);claimRefundSelf(paymentId)refunds crypto/NFT payments directly (NFT owner, no prior cancel needed; requires prior ERC-721 approval on CampaignInfo) - Line items separate product cost, shipping, and commission with configurable goal-counting, fees, and refund rules
- Non-goal line items (e.g., platform commission) can be claimed separately via
claimNonGoalLineItems - Batch operations (
createPaymentBatch,confirmPaymentBatch) enable efficient end-of-day settlement - Pause / cancel controls — platform admin can pause; either admin or owner can permanently cancel
- Fiat-to-fiat for users — buyers and sellers deal in USD; crypto conversion is abstracted away
- Buyer protection — funds stay in the treasury under contract rules until withdrawal; refunds use
cancelPayment,claimRefund, orclaimRefundSelfas applicable