rota (Latin): wheel. Your turn always comes.
The world has always saved in circles β somiti (Bangladesh), chit fund/kameti (India, Pakistan), tanda (Mexico), susu (Ghana, Caribbean), esusu/ajo (Nigeria), hui (China), paluwagan (Philippines), gam'eya (Egypt). Rota puts them all on one set of rails: smart-contract escrowed savings circles in USDC on Arc, Circle's stablecoin-native L1.
Beachhead market: Bangladesh β complete Bangla UI, taka amounts on every screen, somiti-native copy, and diaspora corridors (Dubai/KSA/Malaysia/UK/US β Dhaka). New markets are configuration, not refactoring (see Adding a locale/currency).
| Product | What it is |
|---|---|
| Rota Circle | Classic ROSCA: N members pay a fixed USDC amount per round; each round one member takes the whole pot (fixed or random order). |
| Bid Circle | Chit-fund mode: members who haven't won bid a discount to take the pot early; the discount is redistributed to everyone as a dividend β a market price for liquidity. |
| Goal Pot | Non-rotating group saving toward a target (wedding, tuition, Eid, land). Locks until target/deadline; everyone withdraws exactly their own deposits. |
Cross-cutting: contract escrow (no absconding organizer), deterministic payouts,
collateral + default curing, non-custodial AutoPay, optional Giving round
(labeled Zakat/Sadaqah in bn/ur), and an on-chain Credit Passport reputation
score for thin-file users.
rota/
βββ contracts/ Foundry project (Solidity 0.8.24, OpenZeppelin 5)
β βββ src/ RotaFactory, RotaCircle, GoalPot, ReputationRegistry
β βββ test/ 66 tests: happy paths, bids, autopay, defaults, fuzz, invariants, reentrancy
β βββ script/ DeployLocal / DeployArc (+ script/config/ArcConfig.sol)
βββ app/ Vite + React 18 + TS + Tailwind + wagmi v2/viem + i18next
β βββ src/
β βββ config/ chain.ts (single source of chain truth), currencies.ts
β βββ abi/ AUTO-GENERATED by scripts/abi-sync.mjs
β βββ i18n/ en + bn complete; es/hi/ur/tl scaffolds
βββ genlayer/ news_curator.py β GenLayer Intelligent Contract (AI news desk)
βββ scripts/ abi-sync.mjs, seed-local.mjs
βββ docs/ ARC_NOTES.md (researched chain values), DECISIONS.md, ROADMAP.md
βββ SECURITY.md honest limitations β read it
pnpm install
# terminal 1 β local chain
pnpm chain
# terminal 2 β deploy + wire the app + demo data
pnpm deploy:local # MockUSDC + registry + implementations + factory,
# writes app/src/config/deployments.local.json, syncs ABIs
pnpm seed:local # demo: ROSCA with 2 rounds played, bid circle mid-auction,
# goal pot at 60% β instantly demo-able
pnpm dev # app at http://localhost:5173In MetaMask: add network http://127.0.0.1:8545 (chain id 31337) and import anvil
account #0 (0xac09β¦ff80 private key printed by anvil). Accounts #0β#3 are members
of the seeded circles. The header has a Mint 1,000 test USDC button on anvil.
Run the contract suite:
pnpm test:contracts # forge test β 74 tests, all greenRota is deployed on Arc Testnet (chain id 5042002, redeployed 2026-07-12 with
the audit fixes from SECURITY.md):
| Contract | Address |
|---|---|
| RotaFactory | 0x1ba8Aabc9501d6C68A9eE1387b0d40d53EdCC325 |
| ReputationRegistry | 0xDE51D720ad3b93300e6f8A6F936f62F291c5CE80 |
| RotaCircle implementation | 0x5d5f658706acCB2B25B7f3FE22261C44DCA0DC2F |
| GoalPot implementation | 0x0159536F931Ed1d1DdFEB4f1fc69F9f8429f0161 |
| USDC (canonical ERC-20 interface) | 0x3600000000000000000000000000000000000000 |
Run the app against it with VITE_CHAIN=arc pnpm dev (or set VITE_CHAIN=arc in
.env / .env.local at the repo root β the app reads env from there). Get testnet
USDC for your wallet at https://faucet.circle.com (Arc Testnet); on Arc, USDC is
also the gas token.
The /news page shows the best Arc-ecosystem news and stories from Rota's
domain (ROSCAs, group savings, stablecoin remittances), curated on-chain by a
GenLayer Intelligent Contract
(genlayer/news_curator.py) on the GenLayer Studio
testnet: on each refresh_news() call, validators fetch live news feeds, run an
LLM curation prompt, reach consensus, and store the result as JSON. The app reads
it with genlayer-js β no backend, no API keys.
A curator instance is live on GenLayer studionet:
| Contract | Network | Address |
|---|---|---|
| RotaNewsCurator (Intelligent Contract) | GenLayer studionet | 0xDFEE06117b8d91b5b3e17Bfb09C6C0592ea71690 |
To run your own instance, deploy news_curator.py at
https://studio.genlayer.com and set VITE_GENLAYER_NEWS_CONTRACT=0x⦠in
.env.local (see genlayer/README.md). When unset, /news
shows setup instructions instead. Note: studionet resets occasionally β if reads
fail, redeploy and update the address.
Chain values were researched from docs.arc.io and recorded in
docs/ARC_NOTES.md: chain id 5042002, RPC
https://rpc.testnet.arc.network, explorer https://testnet.arcscan.app, USDC =
native gas token with an ERC-20 interface at 0x3600β¦0000 (6 decimals).
- Fund a deployer wallet with testnet USDC at https://faucet.circle.com (a full deploy costs β 0.12 USDC in gas).
cp .env.example .envand setPRIVATE_KEY=0xβ¦(the0xprefix is required).-
pnpm deploy:arc # sources .env/.env.local, deploys registry/implementations/factory, # writes contracts/deployments/arc.json, syncs ABIs + addresses to the app
stateDiagram-v2
[*] --> OPEN: factory.createCircle (organizer auto-joins)
OPEN --> OPEN: join() β collateral escrowed
OPEN --> ACTIVE: activate() when full (payout order fixed/shuffled)
OPEN --> CANCELLED: cancel() by organizer, or anyone after openDeadline
ACTIVE --> ACTIVE: contribute()/pullContribution() β placeBid() [BID] β settleRound()
ACTIVE --> COMPLETED: settleRound() of round N
COMPLETED --> [*]: withdrawCollateral(), withdrawDividends()
CANCELLED --> [*]: withdrawCollateral()
Per active round: members contribute before the schedule-anchored deadline (AutoPay
lets anyone trigger an opted-in member's exact contribution); in BID mode an auction
runs during the first bidWindowBps of the round; settleRound() slashes defaulter
collateral, pays the optional giving cut, pays the recipient (order-based or winning
bidder minus discount), credits bid dividends pull-withdrawably, and reports
everything to the ReputationRegistry.
stateDiagram-v2
[*] --> LOCKED: factory.createGoalPot
LOCKED --> LOCKED: deposit() (join on first deposit)
LOCKED --> LOCKED: emergencyWithdraw() β haircut shared with stayers
LOCKED --> UNLOCKED: unlock() when total β₯ target OR now > deadline
UNLOCKED --> [*]: withdraw() β exact principal + haircut share β giving cut
Only factory-deployed clones can write. Transparent score:
completionsΓ100 + contributionsΓ10 + curesΓ20 β defaultsΓ50 β earlyExitsΓ15
(floored at 0). The frontend renders a shareable Credit Passport page per address
with QR, history, and client-side signature verification β no backend.
The phased plan behind the βFutureβ column lives in docs/ROADMAP.md.
| Feature | MVP | Future |
|---|---|---|
| ROSCA / bid / goal-pot products | β | |
| Collateral, default slashing, curing | β | multi-round collateral |
| Non-custodial AutoPay | β | protocol keeper network |
| Giving round (Zakat/Sadaqah) | β | |
| Reputation registry + passport page | β | soul-bound token passport |
| en + bn locales, 7 display currencies | β | es/hi/ur/tl translations |
| In-app + browser deadline reminders | β | Telegram/WhatsApp bots |
| Random order via blockhash | β (documented) | Chainlink VRF |
| Local anvil + Arc testnet | β | mainnet (post-audit, post-legal) |
| Fiat ramps (bKash/Nagad, UPI, M-Pesa, GCash) | β | per-market integrations |
| Arc App Kit Unified Balance / Bridge deposits | β | chain-abstracted funding |
| Emergency micro-advances against reputation | β | research |
| AI organizer agent, mobile app | β | research |
New markets are pure configuration:
- Locale: copy
app/src/i18n/en.jsonto<code>.json, translate every key, register it inapp/src/i18n/index.ts(SUPPORTED_LOCALES+resources). Missing keys automatically fall back to English, so partial translations ship safely. Scaffolds fores,hi,ur,tlalready exist. - Display currency: add one entry to
CURRENCIESinapp/src/config/currencies.ts(ISO code, symbol,Intllocale, name key) and, if the new locale should default to it, one line inLOCALE_DEFAULT_CURRENCY. Rates come from the swappableRATE_PROVIDER(free FX API, cached, display-only β on-chain money is always USDC). - Localized product terminology (e.g. the giving label "Zakat/Sadaqah" vs "Giving") lives entirely in the locale file β contracts are locale-agnostic by design.
Read SECURITY.md. Highlights: blockhash randomness is validator-influenceable (bounded impact), collateral can under-cover repeat defaults, AutoPay rests on standard ERC-20 allowances, FX rates are indicative, and there is no KYC/regulatory layer β pooled savings face financial regulation that varies by country, so Rota stays on testnet until reviewed per market.
MIT (contracts and app). Not audited. Not financial advice.