// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "forge-std/Test.sol"; import "../../src/eMoneyToken.sol"; import "../../src/PolicyManager.sol"; import "../../src/ComplianceRegistry.sol"; import "../../src/DebtRegistry.sol"; import "../../src/errors/TokenErrors.sol"; import "../../src/libraries/ReasonCodes.sol"; import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; contract eMoneyTokenTest is Test { eMoneyToken public token; PolicyManager public policyManager; ComplianceRegistry public complianceRegistry; DebtRegistry public debtRegistry; address public admin; address public issuer; address public enforcement; address public user1; address public user2; function setUp() public { admin = address(0x1); issuer = address(0x2); enforcement = address(0x3); user1 = address(0x10); user2 = address(0x20); complianceRegistry = new ComplianceRegistry(admin); debtRegistry = new DebtRegistry(admin); policyManager = new PolicyManager(admin, address(complianceRegistry), address(debtRegistry)); // Deploy implementation eMoneyToken implementation = new eMoneyToken(); // Deploy proxy bytes memory initData = abi.encodeWithSelector( eMoneyToken.initialize.selector, "Test Token", "TEST", 18, issuer, address(policyManager), address(debtRegistry), address(complianceRegistry) ); ERC1967Proxy proxy = new ERC1967Proxy(address(implementation), initData); token = eMoneyToken(address(proxy)); // Set up roles vm.startPrank(issuer); token.grantRole(token.ENFORCEMENT_ROLE(), enforcement); vm.stopPrank(); // Set up compliance vm.startPrank(admin); complianceRegistry.grantRole(complianceRegistry.COMPLIANCE_ROLE(), admin); complianceRegistry.setCompliance(user1, true, 1, bytes32(0)); complianceRegistry.setCompliance(user2, true, 1, bytes32(0)); complianceRegistry.setCompliance(issuer, true, 1, bytes32(0)); policyManager.grantRole(policyManager.POLICY_OPERATOR_ROLE(), admin); policyManager.setLienMode(address(token), 2); vm.stopPrank(); } function test_mint() public { bytes32 reasonCode = ReasonCodes.OK; vm.prank(issuer); token.mint(user1, 1000, reasonCode); assertEq(token.balanceOf(user1), 1000); } function test_mint_unauthorized() public { vm.expectRevert(); token.mint(user1, 1000, bytes32(0)); } function test_burn() public { vm.prank(issuer); token.mint(user1, 1000, bytes32(0)); vm.prank(issuer); token.burn(user1, 500, ReasonCodes.OK); assertEq(token.balanceOf(user1), 500); } function test_transfer_normal() public { vm.prank(issuer); token.mint(user1, 1000, bytes32(0)); vm.prank(user1); token.transfer(user2, 500); assertEq(token.balanceOf(user1), 500); assertEq(token.balanceOf(user2), 500); } function test_transfer_paused() public { vm.prank(issuer); token.mint(user1, 1000, bytes32(0)); vm.prank(admin); policyManager.setPaused(address(token), true); vm.expectRevert( abi.encodeWithSelector(TransferBlocked.selector, ReasonCodes.PAUSED, user1, user2, 500) ); vm.prank(user1); token.transfer(user2, 500); } function test_transfer_hardFreezeMode() public { vm.startPrank(admin); policyManager.setLienMode(address(token), 1); // Hard freeze debtRegistry.grantRole(debtRegistry.DEBT_AUTHORITY_ROLE(), admin); debtRegistry.placeLien(user1, 100, 0, 1, ReasonCodes.LIEN_BLOCK); vm.stopPrank(); vm.prank(issuer); token.mint(user1, 1000, bytes32(0)); vm.expectRevert( abi.encodeWithSelector(TransferBlocked.selector, ReasonCodes.LIEN_BLOCK, user1, user2, 1) ); vm.prank(user1); token.transfer(user2, 1); } function test_transfer_encumberedMode() public { vm.startPrank(admin); debtRegistry.grantRole(debtRegistry.DEBT_AUTHORITY_ROLE(), admin); debtRegistry.placeLien(user1, 300, 0, 1, ReasonCodes.LIEN_BLOCK); vm.stopPrank(); vm.prank(issuer); token.mint(user1, 1000, bytes32(0)); // freeBalance = 1000 - 300 = 700 assertEq(token.freeBalanceOf(user1), 700); // Transfer 700 should succeed vm.prank(user1); token.transfer(user2, 700); assertEq(token.balanceOf(user1), 300); assertEq(token.balanceOf(user2), 700); // Transfer 1 more should fail vm.expectRevert( abi.encodeWithSelector(TransferBlocked.selector, ReasonCodes.INSUFF_FREE_BAL, user1, user2, 1) ); vm.prank(user1); token.transfer(user2, 1); } function test_freeBalanceOf() public { vm.prank(issuer); token.mint(user1, 1000, bytes32(0)); assertEq(token.freeBalanceOf(user1), 1000); vm.startPrank(admin); debtRegistry.grantRole(debtRegistry.DEBT_AUTHORITY_ROLE(), admin); debtRegistry.placeLien(user1, 300, 0, 1, ReasonCodes.LIEN_BLOCK); vm.stopPrank(); assertEq(token.freeBalanceOf(user1), 700); } function test_clawback() public { vm.prank(issuer); token.mint(user1, 1000, bytes32(0)); vm.startPrank(admin); debtRegistry.grantRole(debtRegistry.DEBT_AUTHORITY_ROLE(), admin); debtRegistry.placeLien(user1, 500, 0, 1, ReasonCodes.LIEN_BLOCK); vm.stopPrank(); // Clawback should bypass liens vm.prank(enforcement); token.clawback(user1, user2, 600, ReasonCodes.UNAUTHORIZED); assertEq(token.balanceOf(user1), 400); assertEq(token.balanceOf(user2), 600); } function test_forceTransfer() public { vm.prank(issuer); token.mint(user1, 1000, bytes32(0)); vm.startPrank(admin); debtRegistry.grantRole(debtRegistry.DEBT_AUTHORITY_ROLE(), admin); debtRegistry.placeLien(user1, 500, 0, 1, ReasonCodes.LIEN_BLOCK); vm.stopPrank(); // ForceTransfer bypasses liens but checks compliance vm.prank(enforcement); token.forceTransfer(user1, user2, 600, ReasonCodes.UNAUTHORIZED); assertEq(token.balanceOf(user1), 400); assertEq(token.balanceOf(user2), 600); } function test_forceTransfer_nonCompliant() public { address nonCompliant = address(0x99); vm.prank(issuer); token.mint(user1, 1000, bytes32(0)); vm.prank(enforcement); vm.expectRevert("eMoneyToken: to not compliant"); token.forceTransfer(user1, nonCompliant, 100, bytes32(0)); } }