Overview

All Ankara Chain contracts are written in Solidity ^0.8.24 and use OpenZeppelin upgradeable contracts (v5). They follow the UUPS proxy pattern — each deployed token is a lightweight ERC1967Proxy that delegates to a shared implementation.
This page covers the ERC-20 base layer and the shared UUPS/factory patterns that every other layer follows. See NFT Layer, Multi-Token Layer, and Escrow for the layer-specific details.

AnkaraChainBaseToken

Abstract base contract inherited by all six templates. Provides:
  • ERC20Upgradeable — standard fungible token
  • ERC20PausableUpgradeable — halt transfers
  • ERC20BurnableUpgradeable — token burning
  • AccessControlUpgradeable — role-based permissions
  • UUPSUpgradeable — upgrade authorization

Storage slots (base)

Roles

All roles are granted to admin_ at initialization. The admin also holds DEFAULT_ADMIN_ROLE and can delegate roles to other addresses.

Transfer Hook

Every mint, burn, and transfer passes through _update(), which checks the identity verifier for sender and receiver (when set):

UUPS Proxy Pattern

Each token deploy creates a new ERC1967Proxy pointing to a shared implementation contract. This means:
  1. Gas efficient — only one implementation bytecode on-chain, shared across all tokens of the same template
  2. Upgradeable — the admin can upgrade the implementation to fix bugs without migrating token holders
  3. Isolated state — each proxy has its own storage (balances, metadata, status)

Upgrade flow


TokenFactory

The factory stores implementation addresses and deploys proxies:
The factory charges a deploymentFee in native tokens (covers deployment gas overhead and protocol revenue). The fee is configurable by the factory admin. Deploy event:

IIdentityVerifier

Pluggable KYC interface:
Implement this interface with any KYC provider (Smile Identity, Persona, BVN/NIN, Ghana Card) and pass the contract address at deploy time.

WhitelistVerifier (reference implementation)

A simple on-chain whitelist ships with the contracts for development and testing:

Running the Contracts Locally


Security Notes

  • All contracts use OpenZeppelin’s audited upgradeable library (v5)
  • Storage gaps (uint256[50] private __gap) are reserved in both the base and each template for future storage additions without slot collisions
  • this.setStatus(...) is never called internally — templates use the internal _setStatus() to avoid RBAC bypass via external self-calls
  • Protocol fee is collected on deployment only — no ongoing fees on transfers