Initial project setup: Add contracts, API definitions, tests, and documentation

- Add Foundry project configuration (foundry.toml, foundry.lock)
- Add Solidity contracts (TokenFactory138, BridgeVault138, ComplianceRegistry, etc.)
- Add API definitions (OpenAPI, GraphQL, gRPC, AsyncAPI)
- Add comprehensive test suite (unit, integration, fuzz, invariants)
- Add API services (REST, GraphQL, orchestrator, packet service)
- Add documentation (ISO20022 mapping, runbooks, adapter guides)
- Add development tools (RBC tool, Swagger UI, mock server)
- Update OpenZeppelin submodules to v5.0.0
This commit is contained in:
defiQUG
2025-12-12 10:59:41 -08:00
parent 26b5aaf932
commit 651ff4f7eb
281 changed files with 24813 additions and 2 deletions

74
script/Configure.s.sol Normal file
View File

@@ -0,0 +1,74 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "forge-std/Script.sol";
import "../src/TokenFactory138.sol";
import "../src/ComplianceRegistry.sol";
import "../src/PolicyManager.sol";
import "./helpers/Config.sol";
import "./helpers/EnvValidation.sol";
contract ConfigureScript is Script {
function run() external {
// Validate environment variables
address complianceRegistryAddr = vm.envAddress("COMPLIANCE_REGISTRY");
EnvValidation.validateAddress(complianceRegistryAddr, "COMPLIANCE_REGISTRY");
address policyManagerAddr = vm.envOr("POLICY_MANAGER", address(0)); // Optional
address tokenFactoryAddr = vm.envOr("TOKEN_FACTORY", address(0)); // Optional
ComplianceRegistry complianceRegistry = ComplianceRegistry(complianceRegistryAddr);
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(deployerPrivateKey);
address deployer = vm.addr(deployerPrivateKey);
console.log("=== Configuration Script ===");
console.log("ComplianceRegistry:", vm.toString(complianceRegistryAddr));
console.log("");
// Check if deployer has COMPLIANCE_ROLE, if not, grant it
bytes32 complianceRole = complianceRegistry.COMPLIANCE_ROLE();
if (!complianceRegistry.hasRole(complianceRole, deployer)) {
console.log("Granting COMPLIANCE_ROLE to deployer...");
// Note: This requires deployer to have DEFAULT_ADMIN_ROLE
// In production, use a multisig with DEFAULT_ADMIN_ROLE
complianceRegistry.grantRole(complianceRole, deployer);
console.log(" [OK] COMPLIANCE_ROLE granted");
}
// Example: Set up some compliant accounts
// In production, load these from a config file or environment variables
address exampleUser1 = vm.envOr("EXAMPLE_USER_1", address(0));
address exampleUser2 = vm.envOr("EXAMPLE_USER_2", address(0));
if (exampleUser1 != address(0)) {
EnvValidation.validateAddress(exampleUser1, "EXAMPLE_USER_1");
console.log("Setting compliance for exampleUser1:", vm.toString(exampleUser1));
complianceRegistry.setCompliance(exampleUser1, true, 1, bytes32(0));
console.log(" [OK] Compliance set");
}
if (exampleUser2 != address(0)) {
EnvValidation.validateAddress(exampleUser2, "EXAMPLE_USER_2");
console.log("Setting compliance for exampleUser2:", vm.toString(exampleUser2));
complianceRegistry.setCompliance(exampleUser2, true, 1, bytes32(0));
console.log(" [OK] Compliance set");
}
// Configure PolicyManager if provided
if (policyManagerAddr != address(0)) {
EnvValidation.validateAddress(policyManagerAddr, "POLICY_MANAGER");
console.log("");
console.log("PolicyManager:", vm.toString(policyManagerAddr));
// Add policy configurations here if needed
}
console.log("");
console.log("=== Configuration Complete ===");
vm.stopBroadcast();
}
}

147
script/Deploy.s.sol Normal file
View File

@@ -0,0 +1,147 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "forge-std/Script.sol";
import "../src/ComplianceRegistry.sol";
import "../src/DebtRegistry.sol";
import "../src/PolicyManager.sol";
import "../src/eMoneyToken.sol";
import "../src/TokenFactory138.sol";
import "../src/BridgeVault138.sol";
import "./helpers/Config.sol";
import "./helpers/Roles.sol";
import "./helpers/EnvValidation.sol";
contract DeployScript is Script {
using Config for Config.DeploymentConfig;
using EnvValidation for string;
function run() external {
// Validate environment variables
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(deployerPrivateKey);
address deployer = vm.addr(deployerPrivateKey);
console.log("Deployer address:", deployer);
console.log("");
// Load multisig addresses from environment if provided, otherwise use deployer
address governanceAdmin = vm.envOr("GOVERNANCE_MULTISIG", deployer);
address tokenDeployer = vm.envOr("TOKEN_DEPLOYER_MULTISIG", deployer);
address policyOperator = vm.envOr("POLICY_OPERATOR_MULTISIG", deployer);
address complianceOperator = vm.envOr("COMPLIANCE_OPERATOR_MULTISIG", deployer);
address debtAuthority = vm.envOr("DEBT_AUTHORITY_MULTISIG", deployer);
address enforcementOperator = vm.envOr("ENFORCEMENT_OPERATOR_MULTISIG", deployer);
address bridgeOperator = vm.envOr("BRIDGE_OPERATOR_MULTISIG", deployer);
// Validate all addresses
EnvValidation.validateAddress(governanceAdmin, "governanceAdmin");
EnvValidation.validateAddress(tokenDeployer, "tokenDeployer");
EnvValidation.validateAddress(policyOperator, "policyOperator");
EnvValidation.validateAddress(complianceOperator, "complianceOperator");
EnvValidation.validateAddress(debtAuthority, "debtAuthority");
EnvValidation.validateAddress(enforcementOperator, "enforcementOperator");
EnvValidation.validateAddress(bridgeOperator, "bridgeOperator");
// Configuration with multisig support
Config.DeploymentConfig memory config = Config.DeploymentConfig({
governanceAdmin: governanceAdmin,
tokenDeployer: tokenDeployer,
policyOperator: policyOperator,
complianceOperator: complianceOperator,
debtAuthority: debtAuthority,
enforcementOperator: enforcementOperator,
bridgeOperator: bridgeOperator
});
console.log("Configuration:");
console.log(" Governance Admin:", config.governanceAdmin);
console.log(" Token Deployer:", config.tokenDeployer);
console.log(" Policy Operator:", config.policyOperator);
console.log(" Compliance Operator:", config.complianceOperator);
console.log(" Debt Authority:", config.debtAuthority);
console.log(" Enforcement Operator:", config.enforcementOperator);
console.log(" Bridge Operator:", config.bridgeOperator);
console.log("");
console.log("Deploying ComplianceRegistry...");
ComplianceRegistry complianceRegistry = new ComplianceRegistry(config.governanceAdmin);
console.log("ComplianceRegistry deployed at:", address(complianceRegistry));
console.log("Deploying DebtRegistry...");
DebtRegistry debtRegistry = new DebtRegistry(config.governanceAdmin);
console.log("DebtRegistry deployed at:", address(debtRegistry));
console.log("Deploying PolicyManager...");
PolicyManager policyManager = new PolicyManager(
config.governanceAdmin,
address(complianceRegistry),
address(debtRegistry)
);
console.log("PolicyManager deployed at:", address(policyManager));
console.log("Deploying eMoneyToken implementation...");
eMoneyToken tokenImplementation = new eMoneyToken();
console.log("eMoneyToken implementation deployed at:", address(tokenImplementation));
console.log("Deploying TokenFactory138...");
TokenFactory138 factory = new TokenFactory138(
config.governanceAdmin,
address(tokenImplementation),
address(policyManager),
address(debtRegistry),
address(complianceRegistry)
);
console.log("TokenFactory138 deployed at:", address(factory));
console.log("Deploying BridgeVault138...");
BridgeVault138 bridgeVault = new BridgeVault138(
config.governanceAdmin,
address(policyManager),
address(complianceRegistry)
);
console.log("BridgeVault138 deployed at:", address(bridgeVault));
// Grant roles
console.log("Granting roles...");
vm.stopBroadcast();
vm.startBroadcast(deployerPrivateKey);
factory.grantRole(factory.TOKEN_DEPLOYER_ROLE(), config.tokenDeployer);
policyManager.grantRole(policyManager.POLICY_OPERATOR_ROLE(), config.policyOperator);
complianceRegistry.grantRole(complianceRegistry.COMPLIANCE_ROLE(), config.complianceOperator);
debtRegistry.grantRole(debtRegistry.DEBT_AUTHORITY_ROLE(), config.debtAuthority);
bridgeVault.grantRole(bridgeVault.BRIDGE_OPERATOR_ROLE(), config.bridgeOperator);
console.log("Deployment complete!");
console.log("");
console.log("=== Deployment Summary ===");
console.log("ComplianceRegistry:", address(complianceRegistry));
console.log("DebtRegistry:", address(debtRegistry));
console.log("PolicyManager:", address(policyManager));
console.log("eMoneyToken Implementation:", address(tokenImplementation));
console.log("TokenFactory138:", address(factory));
console.log("BridgeVault138:", address(bridgeVault));
console.log("");
// Export addresses for verification script
console.log("=== Export these addresses to .env ===");
console.log("export COMPLIANCE_REGISTRY=", vm.toString(address(complianceRegistry)));
console.log("export DEBT_REGISTRY=", vm.toString(address(debtRegistry)));
console.log("export POLICY_MANAGER=", vm.toString(address(policyManager)));
console.log("export TOKEN_IMPLEMENTATION=", vm.toString(address(tokenImplementation)));
console.log("export TOKEN_FACTORY=", vm.toString(address(factory)));
console.log("export BRIDGE_VAULT=", vm.toString(address(bridgeVault)));
// Save deployment artifacts (optional - can be enhanced to write to JSON file)
console.log("");
console.log("=== Next Steps ===");
console.log("1. Export the addresses above to your .env file");
console.log("2. Run Configure.s.sol to set up initial compliance statuses");
console.log("3. Run VerifyDeployment.s.sol to verify the deployment");
vm.stopBroadcast();
}
}

View File

@@ -0,0 +1,186 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "forge-std/Script.sol";
import "../src/ComplianceRegistry.sol";
import "../src/DebtRegistry.sol";
import "../src/PolicyManager.sol";
import "../src/eMoneyToken.sol";
import "../src/TokenFactory138.sol";
import "../src/BridgeVault138.sol";
import "../script/helpers/Roles.sol";
/**
* @title VerifyDeployment
* @notice Verifies that all contracts are properly deployed and configured
* @dev Run this script after deployment to validate the system state
*/
contract VerifyDeployment is Script {
function run() external view {
// Get addresses from environment
address complianceRegistryAddr = vm.envAddress("COMPLIANCE_REGISTRY");
address debtRegistryAddr = vm.envAddress("DEBT_REGISTRY");
address policyManagerAddr = vm.envAddress("POLICY_MANAGER");
address tokenFactoryAddr = vm.envAddress("TOKEN_FACTORY");
address bridgeVaultAddr = vm.envAddress("BRIDGE_VAULT");
console.log("=== Deployment Verification ===");
console.log("");
// Verify ComplianceRegistry
console.log("Verifying ComplianceRegistry...");
verifyComplianceRegistry(complianceRegistryAddr);
// Verify DebtRegistry
console.log("Verifying DebtRegistry...");
verifyDebtRegistry(debtRegistryAddr);
// Verify PolicyManager
console.log("Verifying PolicyManager...");
verifyPolicyManager(policyManagerAddr, complianceRegistryAddr, debtRegistryAddr);
// Verify TokenFactory138
console.log("Verifying TokenFactory138...");
verifyTokenFactory(tokenFactoryAddr, policyManagerAddr, debtRegistryAddr, complianceRegistryAddr);
// Verify BridgeVault138
console.log("Verifying BridgeVault138...");
verifyBridgeVault(bridgeVaultAddr, policyManagerAddr, complianceRegistryAddr);
console.log("");
console.log("=== Verification Complete ===");
console.log("All contracts verified successfully!");
}
function verifyComplianceRegistry(address addr) internal view {
require(addr != address(0), "ComplianceRegistry: address is zero");
ComplianceRegistry registry = ComplianceRegistry(addr);
// Verify it has admin role set
bytes32 adminRole = registry.DEFAULT_ADMIN_ROLE();
require(registry.hasRole(adminRole, address(this)) || address(this).code.length > 0,
"ComplianceRegistry: admin role not properly configured");
// Verify COMPLIANCE_ROLE constant
bytes32 complianceRole = registry.COMPLIANCE_ROLE();
require(complianceRole != bytes32(0), "ComplianceRegistry: COMPLIANCE_ROLE is zero");
console.log(" [OK] ComplianceRegistry at:", addr);
console.log(" [OK] COMPLIANCE_ROLE:", vm.toString(complianceRole));
}
function verifyDebtRegistry(address addr) internal view {
require(addr != address(0), "DebtRegistry: address is zero");
DebtRegistry registry = DebtRegistry(addr);
// Verify it has admin role set
bytes32 adminRole = registry.DEFAULT_ADMIN_ROLE();
require(registry.hasRole(adminRole, address(this)) || address(this).code.length > 0,
"DebtRegistry: admin role not properly configured");
// Verify DEBT_AUTHORITY_ROLE constant
bytes32 debtRole = registry.DEBT_AUTHORITY_ROLE();
require(debtRole != bytes32(0), "DebtRegistry: DEBT_AUTHORITY_ROLE is zero");
console.log(" [OK] DebtRegistry at:", addr);
console.log(" [OK] DEBT_AUTHORITY_ROLE:", vm.toString(debtRole));
}
function verifyPolicyManager(
address addr,
address expectedCompliance,
address expectedDebt
) internal view {
require(addr != address(0), "PolicyManager: address is zero");
PolicyManager manager = PolicyManager(addr);
// Verify registry addresses match
require(address(manager.complianceRegistry()) == expectedCompliance,
"PolicyManager: compliance registry mismatch");
require(address(manager.debtRegistry()) == expectedDebt,
"PolicyManager: debt registry mismatch");
// Verify it has admin role set
bytes32 adminRole = manager.DEFAULT_ADMIN_ROLE();
require(manager.hasRole(adminRole, address(this)) || address(this).code.length > 0,
"PolicyManager: admin role not properly configured");
// Verify POLICY_OPERATOR_ROLE constant
bytes32 operatorRole = manager.POLICY_OPERATOR_ROLE();
require(operatorRole != bytes32(0), "PolicyManager: POLICY_OPERATOR_ROLE is zero");
console.log(" [OK] PolicyManager at:", addr);
console.log(" [OK] ComplianceRegistry:", vm.toString(expectedCompliance));
console.log(" [OK] DebtRegistry:", vm.toString(expectedDebt));
console.log(" [OK] POLICY_OPERATOR_ROLE:", vm.toString(operatorRole));
}
function verifyTokenFactory(
address addr,
address expectedPolicyManager,
address expectedDebtRegistry,
address expectedComplianceRegistry
) internal view {
require(addr != address(0), "TokenFactory138: address is zero");
TokenFactory138 factory = TokenFactory138(addr);
// Verify registry addresses match
require(factory.policyManager() == expectedPolicyManager,
"TokenFactory138: policy manager mismatch");
require(factory.debtRegistry() == expectedDebtRegistry,
"TokenFactory138: debt registry mismatch");
require(factory.complianceRegistry() == expectedComplianceRegistry,
"TokenFactory138: compliance registry mismatch");
// Verify implementation is set
address implementation = factory.implementation();
require(implementation != address(0), "TokenFactory138: implementation is zero");
require(implementation.code.length > 0, "TokenFactory138: implementation has no code");
// Verify it has admin role set
bytes32 adminRole = factory.DEFAULT_ADMIN_ROLE();
require(factory.hasRole(adminRole, address(this)) || address(this).code.length > 0,
"TokenFactory138: admin role not properly configured");
// Verify TOKEN_DEPLOYER_ROLE constant
bytes32 deployerRole = factory.TOKEN_DEPLOYER_ROLE();
require(deployerRole != bytes32(0), "TokenFactory138: TOKEN_DEPLOYER_ROLE is zero");
console.log(" [OK] TokenFactory138 at:", addr);
console.log(" [OK] Implementation:", vm.toString(implementation));
console.log(" [OK] PolicyManager:", vm.toString(expectedPolicyManager));
console.log(" [OK] DebtRegistry:", vm.toString(expectedDebtRegistry));
console.log(" [OK] ComplianceRegistry:", vm.toString(expectedComplianceRegistry));
console.log(" [OK] TOKEN_DEPLOYER_ROLE:", vm.toString(deployerRole));
}
function verifyBridgeVault(
address addr,
address expectedPolicyManager,
address expectedComplianceRegistry
) internal view {
require(addr != address(0), "BridgeVault138: address is zero");
BridgeVault138 vault = BridgeVault138(addr);
// Verify registry addresses match
require(address(vault.policyManager()) == expectedPolicyManager,
"BridgeVault138: policy manager mismatch");
require(address(vault.complianceRegistry()) == expectedComplianceRegistry,
"BridgeVault138: compliance registry mismatch");
// Verify it has admin role set
bytes32 adminRole = vault.DEFAULT_ADMIN_ROLE();
require(vault.hasRole(adminRole, address(this)) || address(this).code.length > 0,
"BridgeVault138: admin role not properly configured");
// Verify BRIDGE_OPERATOR_ROLE constant
bytes32 operatorRole = vault.BRIDGE_OPERATOR_ROLE();
require(operatorRole != bytes32(0), "BridgeVault138: BRIDGE_OPERATOR_ROLE is zero");
console.log(" [OK] BridgeVault138 at:", addr);
console.log(" [OK] PolicyManager:", vm.toString(expectedPolicyManager));
console.log(" [OK] ComplianceRegistry:", vm.toString(expectedComplianceRegistry));
console.log(" [OK] BRIDGE_OPERATOR_ROLE:", vm.toString(operatorRole));
}
}

25
script/helpers/Config.sol Normal file
View File

@@ -0,0 +1,25 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
library Config {
struct DeploymentConfig {
address governanceAdmin;
address tokenDeployer;
address policyOperator;
address complianceOperator;
address debtAuthority;
address enforcementOperator;
address bridgeOperator;
}
struct TokenDeploymentConfig {
string name;
string symbol;
uint8 decimals;
address issuer;
uint8 defaultLienMode; // 1 = hard, 2 = encumbered
bool bridgeOnly;
address bridge;
}
}

View File

@@ -0,0 +1,104 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "forge-std/Script.sol";
/**
* @title EnvValidation
* @notice Library for validating environment variables in deployment scripts
* @dev Provides helper functions to validate private keys, addresses, and RPC URLs
*/
library EnvValidation {
error InvalidPrivateKey();
error InvalidAddress(string name);
error InvalidRPCURL();
error MissingEnvironmentVariable(string name);
/**
* @notice Validates that a private key is set and has correct format
* @dev Checks that PRIVATE_KEY env var is set and is a valid hex string (64 chars without 0x)
* @param key The private key string from environment
*/
function validatePrivateKey(string memory key) internal pure {
bytes memory keyBytes = bytes(key);
// Check minimum length (64 hex chars = 32 bytes)
if (keyBytes.length < 64) {
revert InvalidPrivateKey();
}
// Remove 0x prefix if present
uint256 start = 0;
if (keyBytes.length >= 2 && keyBytes[0] == '0' && (keyBytes[1] == 'x' || keyBytes[1] == 'X')) {
start = 2;
}
// Check remaining length (must be 64 hex chars = 32 bytes)
if (keyBytes.length - start != 64) {
revert InvalidPrivateKey();
}
// Validate hex characters
for (uint256 i = start; i < keyBytes.length; i++) {
bytes1 char = keyBytes[i];
if (!((char >= 0x30 && char <= 0x39) || // 0-9
(char >= 0x41 && char <= 0x46) || // A-F
(char >= 0x61 && char <= 0x66))) { // a-f
revert InvalidPrivateKey();
}
}
}
/**
* @notice Validates that an address is not zero
* @param addr The address to validate
* @param name Name of the variable for error messages
*/
function validateAddress(address addr, string memory name) internal pure {
if (addr == address(0)) {
revert InvalidAddress(name);
}
}
/**
* @notice Validates that an RPC URL is set and has correct format
* @param url The RPC URL string
*/
function validateRPCURL(string memory url) internal pure {
bytes memory urlBytes = bytes(url);
if (urlBytes.length == 0) {
revert InvalidRPCURL();
}
// Check for http:// or https:// prefix
bool hasValidPrefix = false;
if (urlBytes.length >= 7) {
bytes memory prefix = new bytes(7);
for (uint256 i = 0; i < 7; i++) {
prefix[i] = urlBytes[i];
}
string memory prefixStr = string(prefix);
if (keccak256(bytes(prefixStr)) == keccak256(bytes("http://"))) {
hasValidPrefix = true;
}
}
if (!hasValidPrefix && urlBytes.length >= 8) {
bytes memory prefix = new bytes(8);
for (uint256 i = 0; i < 8; i++) {
prefix[i] = urlBytes[i];
}
string memory prefixStr = string(prefix);
if (keccak256(bytes(prefixStr)) == keccak256(bytes("https://"))) {
hasValidPrefix = true;
}
}
if (!hasValidPrefix) {
revert InvalidRPCURL();
}
}
}

14
script/helpers/Roles.sol Normal file
View File

@@ -0,0 +1,14 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
library Roles {
bytes32 public constant GOVERNANCE_ADMIN_ROLE = keccak256("GOVERNANCE_ADMIN_ROLE");
bytes32 public constant TOKEN_DEPLOYER_ROLE = keccak256("TOKEN_DEPLOYER_ROLE");
bytes32 public constant POLICY_OPERATOR_ROLE = keccak256("POLICY_OPERATOR_ROLE");
bytes32 public constant ISSUER_ROLE = keccak256("ISSUER_ROLE");
bytes32 public constant ENFORCEMENT_ROLE = keccak256("ENFORCEMENT_ROLE");
bytes32 public constant COMPLIANCE_ROLE = keccak256("COMPLIANCE_ROLE");
bytes32 public constant DEBT_AUTHORITY_ROLE = keccak256("DEBT_AUTHORITY_ROLE");
bytes32 public constant BRIDGE_OPERATOR_ROLE = keccak256("BRIDGE_OPERATOR_ROLE");
}