- Integrated additional Zod validation schemas for improved input validation across various API routes. - Updated existing services to utilize the new validation middleware, ensuring better request integrity. - Improved error handling mechanisms in key services to provide clearer feedback on request failures. - Conducted code cleanup to enhance readability and maintainability of the API services.
eMoney Token Factory (ChainID 138)
A comprehensive ERC-20 eMoney Token Factory system with policy-controlled transfers, lien enforcement, compliance management, and bridge functionality.
Overview
This system enables the deployment and management of restricted ERC-20 tokens on ChainID 138 with the following key features:
- Policy-Controlled Transfers: All transfers are validated through a centralized PolicyManager
- Lien Enforcement: Two modes supported
- Hard Freeze Mode: Any active lien blocks all outbound transfers
- Encumbered Mode: Transfers allowed up to
freeBalance = balance - encumbrance
- Compliance Registry: Track account compliance status, risk tiers, and jurisdiction
- Debt Registry: Multi-lien support with aggregation and priority
- Bridge Vault: Optional public chain bridge with light client verification
- UUPS Upgradable: Token implementations use UUPS proxy pattern for upgradeability
Architecture
Contract Relationships
graph TB
subgraph "Registry Layer"
CR[ComplianceRegistry]
DR[DebtRegistry]
end
subgraph "Policy Layer"
PM[PolicyManager]
end
subgraph "Token Layer"
TF[TokenFactory138]
EMT[eMoneyToken]
IMPL[eMoneyToken<br/>Implementation]
end
subgraph "Bridge Layer"
BV[BridgeVault138]
end
CR -->|checks compliance| PM
DR -->|provides lien info| PM
PM -->|authorizes transfers| EMT
PM -->|authorizes transfers| BV
TF -->|deploys| EMT
IMPL -->|used by| TF
EMT -->|uses| PM
EMT -->|uses| DR
EMT -->|uses| CR
BV -->|uses| PM
BV -->|uses| CR
style CR fill:#e1f5ff
style DR fill:#e1f5ff
style PM fill:#fff4e1
style TF fill:#e8f5e9
style EMT fill:#e8f5e9
style BV fill:#f3e5f5
Transfer Authorization Flow
sequenceDiagram
participant User
participant Token as eMoneyToken
participant PM as PolicyManager
participant CR as ComplianceRegistry
participant DR as DebtRegistry
User->>Token: transfer(to, amount)
Token->>PM: canTransfer(from, to, amount)
alt Token Paused
PM-->>Token: (false, PAUSED)
else Account Frozen
PM->>CR: isFrozen(from/to)
CR-->>PM: true
PM-->>Token: (false, FROM_FROZEN/TO_FROZEN)
else Not Compliant
PM->>CR: isAllowed(from/to)
CR-->>PM: false
PM-->>Token: (false, FROM_NOT_COMPLIANT/TO_NOT_COMPLIANT)
else Bridge Only Mode
PM->>PM: check bridge address
PM-->>Token: (false, BRIDGE_ONLY)
else Lien Check
alt Hard Freeze Mode
Token->>DR: hasActiveLien(from)
DR-->>Token: true
Token-->>User: TransferBlocked(LIEN_BLOCK)
else Encumbered Mode
Token->>DR: activeLienAmount(from)
DR-->>Token: encumbrance
Token->>Token: freeBalance = balance - encumbrance
alt amount > freeBalance
Token-->>User: TransferBlocked(INSUFF_FREE_BAL)
else
Token->>Token: _update(from, to, amount)
Token-->>User: Transfer succeeded
end
end
end
Contracts
Core Contracts
- TokenFactory138: Factory contract for deploying new eMoney tokens as UUPS proxies
- eMoneyToken: Restricted ERC-20 token with transfer hooks and lien enforcement
- PolicyManager: Central rule engine for transfer authorization
- DebtRegistry: Lien management and aggregation engine
- ComplianceRegistry: Compliance status and freeze management
- BridgeVault138: Lock/unlock portal for public chain representation
Installation
Prerequisites
- Foundry (forge, cast, anvil)
- OpenZeppelin Contracts v5
- Node.js 18+ (for API layer)
- pnpm 8+ (package manager for API layer)
Setup
- Clone the repository
- Install Solidity dependencies:
forge install OpenZeppelin/openzeppelin-contracts@v5.0.0
forge install OpenZeppelin/openzeppelin-contracts-upgradeable@v5.0.0
- Install API dependencies (if using API layer):
# Install pnpm (if not installed)
npm install -g pnpm
# Install all API dependencies
cd api
pnpm install
See API Getting Started for detailed API setup instructions.
- Build:
forge build
- Run tests:
forge test
Usage
Environment Variables
Before deploying, you need to set up environment variables. A template file .env.example is provided as a reference.
Required Variables
-
PRIVATE_KEY: Private key for deployment (without 0x prefix)- SECURITY WARNING: This key will have admin access to deployed contracts
- Use a dedicated deployment wallet with minimal funds
- Never commit this key to version control
-
RPC_URL: RPC endpoint URL for ChainID 138
Post-Deployment Variables (Required for Configure.s.sol)
Set these after initial deployment:
COMPLIANCE_REGISTRY: Address of deployed ComplianceRegistry contractPOLICY_MANAGER: Address of deployed PolicyManager contractTOKEN_FACTORY: Address of deployed TokenFactory138 contract
Optional Variables
INFURA_API_KEY: For Infura RPC endpoints (optional)ETHERSCAN_API_KEY: For contract verification (optional)GOVERNANCE_MULTISIG: Multisig address for governance (production)
Setting Up Environment Variables
- Copy the example file:
cp .env.example .env
- Edit
.envand fill in your actual values:
# Edit .env file with your editor
nano .env # or vim, code, etc.
- Alternatively, export variables directly:
export PRIVATE_KEY=<your_private_key>
export RPC_URL=<chain_rpc_url>
Security Best Practices:
- Never commit
.envto version control (it's in.gitignore) - Use different keys for development, staging, and production
- Rotate keys regularly
- Use hardware wallets for production deployments
- Store sensitive values in secure key management services
Deploying the System
-
Set up environment variables (see above)
-
Deploy contracts:
forge script script/Deploy.s.sol:DeployScript --rpc-url $RPC_URL --broadcast --verify
- Configure roles and initial settings:
export COMPLIANCE_REGISTRY=<deployed_address>
export POLICY_MANAGER=<deployed_address>
export TOKEN_FACTORY=<deployed_address>
forge script script/Configure.s.sol:ConfigureScript --rpc-url $RPC_URL --broadcast
Deploying a New Token
TokenFactory138 factory = TokenFactory138(factoryAddress);
ITokenFactory138.TokenConfig memory config = ITokenFactory138.TokenConfig({
issuer: issuerAddress,
decimals: 18,
defaultLienMode: 2, // 1 = hard freeze, 2 = encumbered
bridgeOnly: false,
bridge: bridgeAddress
});
address token = factory.deployToken("My Token", "MTK", config);
Managing Liens
DebtRegistry registry = DebtRegistry(debtRegistryAddress);
// Place a lien
uint256 lienId = registry.placeLien(
debtor,
1000, // amount
0, // expiry (0 = no expiry)
1, // priority
reasonCode
);
// Reduce a lien
registry.reduceLien(lienId, 300); // reduce by 300
// Release a lien
registry.releaseLien(lienId);
Transfer Modes
Mode 1: Hard Freeze
When a token is in hard freeze mode (lienMode = 1), any active lien on an account blocks all outbound transfers.
Mode 2: Encumbered (Recommended)
When a token is in encumbered mode (lienMode = 2), accounts can transfer up to their freeBalance:
freeBalance = balanceOf(account) - activeLienAmount(account)- Transfers exceeding
freeBalanceare blocked withINSUFF_FREE_BALreason code
Roles
GOVERNANCE_ADMIN_ROLE: Root governance (should be multisig)TOKEN_DEPLOYER_ROLE: Deploy new tokens via factoryPOLICY_OPERATOR_ROLE: Configure token policies (pause, bridgeOnly, lienMode)ISSUER_ROLE: Mint/burn tokensENFORCEMENT_ROLE: Clawback and forceTransferCOMPLIANCE_ROLE: Update compliance registryDEBT_AUTHORITY_ROLE: Place/reduce/release liensBRIDGE_OPERATOR_ROLE: Authorize bridge unlocks
Reason Codes
All transfer blocks emit a bytes32 reason code:
OK: Transfer allowedPAUSED: Token is pausedFROM_FROZEN/TO_FROZEN: Account is frozenFROM_NOT_COMPLIANT/TO_NOT_COMPLIANT: Account not compliantLIEN_BLOCK: Hard freeze mode - lien blocks transferINSUFF_FREE_BAL: Encumbered mode - insufficient free balanceBRIDGE_ONLY: Token in bridge-only modeUNAUTHORIZED: Unauthorized operationCONFIG_ERROR: Configuration error
Testing
Run All Tests
forge test
Run Specific Test Suite
forge test --match-contract ComplianceRegistryTest
forge test --match-contract DebtRegistryTest
forge test --match-contract PolicyManagerTest
forge test --match-contract eMoneyTokenTest
forge test --match-contract TokenFactoryTest
Run Invariant Tests
forge test --match-contract DebtRegistryInvariants
forge test --match-contract TransferInvariants
Run Fuzz Tests
forge test --match-contract DebtRegistryFuzz
forge test --match-contract TransferFuzz
Generate Coverage Report
forge coverage
Security Considerations
- Admin Roles: All admin roles should be assigned to multisigs in production
- Timelock: Consider adding timelock for privileged operations
- Audits: External security audit recommended before mainnet deployment
- Upgrades: UUPS upgradeability requires careful governance control
Documentation
See RUNBOOK.md for operational procedures including:
- Role rotation
- Emergency pause procedures
- Lien dispute handling
- Upgrade procedures
- Bridge operator procedures
License
MIT