Docs
How it works, exactly.
The rules below are the ones the contracts enforce. Where a detail is easy to get wrong, it is spelled out.
Overview
NairaFlow has two products that share one security model. Savings circles are on-chain Ajo or Esusu for a group. Goal vaults are a personal lock. Both are non-custodial: money sits in a contract, and no person or company can move it outside that contract's rules.
An optional agent can close due circle rounds and release vault allowances you approved. It can never go beyond limits you set, and you can revoke it at any time.
A savings score shows how reliably any wallet pays into circles, read live from public events.
Savings circles
Each circle is its own contract with its own address, created from a factory. Funds are never pooled between circles.
Setting one up
| Stablecoin | Any token on the allowed list for the network. |
| Contribution | The amount every member pays each round. |
| Round length | How long each round lasts. The form is in days. The contract requires at least one hour. |
| Members | From 2 to 20. |
| Security deposit | A multiple of one contribution, locked when you join. Zero is allowed. |
The creator joins automatically as the first member and posts the deposit. When the last seat fills, the circle starts by itself and the first round deadline is set from that moment.
Each round
- Every member contributes before the round deadline.
- After the deadline, anyone can close the round. The agent does it automatically when it is running.
- A member who did not pay loses their deposit into that round's pot and is marked defaulted. They are skipped for the rest of the circle.
- The next member in order is credited the whole pot. They collect it with a Withdraw button.
Order of payouts
Members are paid in join order, with no randomness. If someone leaves before the circle is full, the last member to join takes their place in the order. Once the circle is full the order is fixed.
Leaving, finishing and getting stuck
- You can leave while the circle is still filling and your deposit comes straight back.
- If a circle never fills within 30 days, any member can cancel it and reclaim their deposit.
- When it finishes, deposits of members who never defaulted are returned.
- If every member defaulted, whatever is left is split between all members rather than locked forever.
Goal vaults
A vault belongs to one owner and holds one stablecoin. It is created with a destination address, an unlock date, and optionally a recurring allowance.
| Deposits | Anyone can top a vault up, for example a relative funding your goal. |
| Before the unlock date | Nothing can leave, unless you set an allowance. Then up to the allowance per period. |
| After the unlock date | The owner can withdraw everything. |
| Allowance periods | Fixed windows counted from the Unix epoch, so a one day period resets at midnight UTC. They are not rolling windows. |
| Destination | Where agent releases go. The owner can change it, after which the agent policy must be set again. |
The agent
The agent is an off-chain service. It watches the chain and can call exactly two functions on a contract named AgentExecutor: close a due circle round, and release an allowance from a vault. It holds no funds and no token allowance.
The policy you grant
| Allowed destination | Must match the vault's destination. |
| Per transaction limit | No single release can exceed it. |
| Per period limit | Total released in a period cannot exceed it. |
| Period length | The size of that period. |
| Expiry | Optional. After it, the policy no longer works. |
Every call is checked against the policy on chain, and the vault checks its own unlock and allowance rules separately. Revoke the policy and the agent's next attempt reverts with policy inactive.
Gas and the activity log
For vault releases the agent may wait up to four minutes for a lower gas price, then acts anyway so your schedule is honored. Every action emits an event with the gas price at that moment, and the Activity page reads those events back from the chain with a link to each transaction.
Who runs it
The agent is a program in the repository. It is set up to run on GitHub's servers on a schedule, with its own key that holds only testnet gas and the agent role, and it is also run directly by the project maintainers. GitHub treats schedules as best effort and can delay or skip runs, so a due round may wait, sometimes for hours. Nothing depends on it: any member can close a due round by hand, and a vault owner can always withdraw within the vault's own rules.
Savings score
The Score page reads four public events for a wallet: joined, contributed, defaulted and paid out. It shows contributions paid, rounds missed, payouts received and an on-time rate.
on-time rate = contributions paid / (contributions paid + rounds missed)Reliable is 90% or more, Mixed is 60% to 89%, and At risk is below 60%. It only counts NairaFlow circles on the network you are viewing. There is no stored score and no extra contract, so nobody can edit it.
Networks and tokens
| Arbitrum Sepolia | Chain id 421614. Circle's official test USDC plus a mock mUSDG. |
| Robinhood Chain Testnet | Chain id 46630. Mock mUSDC and mUSDG, because no official ones exist on this testnet. |
| Robinhood Chain mainnet | Chain id 4663. NairaFlow is deployed here with the real USDG (0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168) as the only token. No mocks. |
Mock tokens carry an m in their own on-chain symbol and have a public mint, so they can never be mistaken for the real ones. A Need test tokens card on the create pages mints them in one click.
Contracts
Every address below comes from a real deployment broadcast, and every contract is source-verified.
Arbitrum Sepolia
| StablecoinRegistry | 0x36A7e4945aa7b22F5E61Bc0055013D9Dbdd71c03 |
| PolicyManager | 0x28D4f7C79A26C7C8f23e39Bd57CB6e895bd59B69 |
| AgentExecutor | 0x2f00B3d6392D1D98069Ff6E1B355f812948E93B7 |
| SavingsCircleFactory | 0xAaB3F8C973b16207821C6C93c595fCaCa7D92842 |
| GoalVaultFactory | 0x5863AF77A5e0978B7cF140F2D18081E08de2bB0d |
Robinhood Chain Testnet
| StablecoinRegistry | 0x38036Fe3a1F7053d1195E56ac1EC00e003BE724e |
| PolicyManager | 0xa806b5984FF3C55E5B4960c4f275f7B277a63AcC |
| AgentExecutor | 0x3b0f0dAb4C018B9e994382Ec940d79cB462eB00F |
| SavingsCircleFactory | 0x8EdeeD5342BC4088E5F05e730a64B8b6a82047aa |
| GoalVaultFactory | 0x7609Ad496606693a29121260EAD066CBA4e0Bf63 |
Robinhood Chain (mainnet)
| StablecoinRegistry | 0xAbEA0b38214B1A5FDAc725E63eb1c7EC6b381637 |
| PolicyManager | 0x52A0D9b9d96A03F318c8D07aC8068Aed5f2016c1 |
| AgentExecutor | 0x42ABF69d81425CAbd853a55170B2bAd86b8290Ca |
| SavingsCircleFactory | 0xb29501e7D28a3dDB5c2c84D10916a628A5dD3514 |
| GoalVaultFactory | 0x0Dfe72134CCa08Bf346820F16e3467eaB03aa6C0 |
Security
- 22 unit tests and a funds-conservation invariant test that runs 128,000 random calls.
- Slither static analysis: no High or Medium findings. The 16 Low results are triaged in the security notes.
- Payouts are pull based, and state-changing functions are guarded against reentrancy.
- A platform admin can pause a circle or vault. It cannot move funds.
- Not audited by a professional firm. The mainnet deployment holds real USDG, so use small amounts.
Three real bugs were found by operating and reviewing the deployed system and are written up in the deployments document. Full notes are in SECURITY.md.
Run it yourself
git clone https://github.com/angelraph/nairaflow
cd nairaflow/contracts
npm ci
git clone --depth 1 https://github.com/foundry-rs/forge-std lib/forge-std
forge test
cd ../frontend
npm ci
npm run devThe agent lives in the agent folder. Copy .env.example to .env, give it a funded testnet key that holds the agent role, then run npm start.