Back to Blog
Technicalsecurityaudit

Our Smart Contracts Have Been Audited: A Full Breakdown of the Security Review

Zero critical findings. Two medium issues fixed. Three informational notes. Here's everything the auditors found and what we did about it.

Njangi House EngineeringยทSecurity TeamยทFebruary 20, 2026ยท 9 min read

Why Audits Matter in DeFi

In traditional finance, your bank has regulators, deposit insurance, and legal recourse if something goes wrong. In DeFi, you have code.

Smart contracts are immutable once deployed. A bug in a savings contract isn't a customer support ticket โ€” it can mean permanent, irreversible loss of funds. The history of DeFi is littered with exploits that drained hundreds of millions of dollars from protocols with unaudited or inadequately audited code.

We commissioned an independent security audit of all Njangi House smart contracts before mainnet deployment. This post is a complete, transparent account of what was found.

What Was Audited

The audit covered the complete Njangi House smart contract suite:

  • NkapToken.sol โ€” ERC-20 utility and governance token
  • NjangiHouse.sol โ€” Core ROSCA savings circle logic
  • NjangiFactory.sol โ€” CREATE2 factory for deploying house instances
  • MomoOracle.sol โ€” Mobile money oracle bridge contract
  • NjangiNFT.sol โ€” Dynamic on-chain SVG NFT contract

The auditors also reviewed:

  • All OpenZeppelin dependencies and their usage patterns
  • Access control model (roles and permissions)
  • Oracle trust assumptions
  • Token economics and inflation mechanics

Total lines of Solidity audited: ~1,800 lines (excluding OpenZeppelin) Audit duration: 3 weeks Auditor: Independent security firm (report available on request)

Summary of Findings

| Severity | Count | Status | |---|---|---| | Critical | 0 | โ€” | | High | 0 | โ€” | | Medium | 2 | Fixed | | Low | 1 | Acknowledged | | Informational | 3 | Acknowledged |

Zero critical. Zero high. We're proud of this result, but we take the medium findings seriously.

Medium Finding 1: Oracle Rate Staleness Without Deadline Enforcement

Contract: MomoOracle.sol Issue: The initial implementation updated the exchange rate but did not reject relay operations when the rate was stale. An attacker who could delay rate updates (e.g., via oracle infrastructure disruption) could force payments at an outdated rate.

Our fix: We added a rateNotStale modifier on the registerPayment() function:

modifier rateNotStale() {
    if (block.timestamp - rateUpdatedAt > RATE_STALENESS_THRESHOLD)
        revert RateStale();
    _;
}

RATE_STALENESS_THRESHOLD is set to 4 hours. Cloud Scheduler updates the rate every 30 minutes โ€” providing an 8ร— safety margin. If the rate updater fails, the oracle automatically stops processing payments until the rate is refreshed, preventing any exploitable window.

Medium Finding 2: Contribution Replay via MOMO Reference Collision

Contract: NjangiHouse.sol Issue: The anti-replay mapping usedMomoRefs stored MOMO reference strings per house. A sufficiently short reference string (e.g., "REF001") could theoretically collide across different house contracts if both houses stored state in their own mapping.

Our fix: The anti-replay protection was also added to the MomoOracle.sol contract itself โ€” where the reference is checked globally before any house-level call is made. This creates a two-layer anti-replay system: the oracle rejects any reference it has seen before (globally), and each house contract independently rejects any reference it has seen.

Low Finding: Integer Division Rounding in Fee Calculation

Contract: NjangiHouse.sol Issue: The fee calculation (payoutAmount * feeBps) / 10000 can lose up to 1 wei due to integer division, slightly undercharging on fees.

Our decision: Acknowledged, not fixed. The rounding is always in favor of the user (1 wei โ‰ˆ 0.000001 USDC โ€” negligible). Fixing this with ceiling division would add complexity without meaningful benefit. This is a standard pattern in DeFi fee calculations.

Informational Findings

Info 1: Gas Optimization in getMembers() The getMembers() function copies the entire memberList array to memory on every call. For houses approaching the 20-member maximum, this is slightly inefficient. We acknowledged this โ€” for a maximum 20-element array, gas cost is negligible and the clarity of the implementation is preferred.

Info 2: Missing Event on Enrollment The initial enrollMember() function emitted an event but the event wasn't indexed on the payoutRound field. Auditors recommended indexing for efficient off-chain filtering. We implemented this in the final deployment.

Info 3: Oracle Key Rotation Procedure Auditors noted that while the oracle private key is stored in Secret Manager, there was no documented on-chain procedure for key rotation (replacing the RELAYER_ROLE holder). We've since added a key rotation runbook to our security documentation and ensured the revokeRole/grantRole access control flow is tested.

What We Built Beyond the Audit

The audit was a minimum bar, not a ceiling. We added several additional security features not required by the auditors:

Reentrancy guards: All state-changing functions with external calls use ReentrancyGuard. This prevents a class of attacks where malicious contracts attempt to re-enter functions during execution.

Pausable emergency stop: The house organizer and any admin can pause all state changes in an emergency. This gives users time to assess a situation before more funds flow into a compromised contract.

Custom errors for gas efficiency: All revert conditions use custom errors (revert InvalidAmount()) rather than string messages, reducing gas costs by ~50% on failed transactions.

SafeERC20 for all token transfers: Rather than raw transfer() calls that fail silently on some tokens, all USDC operations use OpenZeppelin's SafeERC20.safeTransfer(), which reverts clearly on failure.

Aave integration with try-catch: Yield integration uses try/catch wrappers around Aave calls. If Aave is unavailable or reverts for any reason, the house continues operating without yield โ€” funds are not stuck.

Ongoing Security Practices

Security is not a one-time event. Our ongoing practices include:

  • Bug bounty program: Up to $10,000 USDC for critical findings (contact: security@njangihouse.io)
  • Monitoring: On-chain event monitoring via The Graph indexes all contract events; anomaly detection alerts the team within minutes
  • Upgrade path: Currently contracts are non-upgradeable by design. Future versions will be deployed as new factory instances, with users having full visibility and choice to migrate
  • Re-audit policy: Any contract modification triggers a new audit before deployment

The Report

The full audit report is available for download on our website. We believe transparency is essential โ€” potential users should be able to read every finding and judge for themselves.

We're proud of the result, but more than that, we're proud of the process. Security in DeFi is earned, not assumed.


Download the full audit report or read the contract source code on GitHub.