// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import {Test} from "forge-std/Test.sol"; import {RailEscrowVault} from "@emoney/RailEscrowVault.sol"; import {IRailEscrowVault} from "@emoney/interfaces/IRailEscrowVault.sol"; import {RailTypes} from "@emoney/libraries/RailTypes.sol"; import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract MockERC20 is ERC20 { constructor() ERC20("Mock Token", "MOCK") { _mint(msg.sender, 1000000 * 10**18); } function mint(address to, uint256 amount) external { _mint(to, amount); } } contract RailEscrowVaultTest is Test { RailEscrowVault public vault; MockERC20 public token; address public admin; address public settlementOperator; address public user; function setUp() public { admin = address(0x1); settlementOperator = address(0x2); user = address(0x10); vault = new RailEscrowVault(admin); token = new MockERC20(); vm.startPrank(admin); vault.grantRole(vault.SETTLEMENT_OPERATOR_ROLE(), settlementOperator); vm.stopPrank(); // Give user some tokens token.mint(user, 10000 * 10**18); } function test_lock() public { uint256 amount = 1000 * 10**18; uint256 triggerId = 1; vm.startPrank(user); token.approve(address(vault), amount); vm.stopPrank(); vm.expectEmit(true, true, false, true); emit Locked(address(token), user, amount, triggerId, uint8(RailTypes.Rail.SWIFT)); vm.prank(settlementOperator); vault.lock(address(token), user, amount, triggerId, RailTypes.Rail.SWIFT); assertEq(vault.getEscrowAmount(address(token), triggerId), amount); assertEq(vault.getTotalEscrow(address(token)), amount); assertEq(token.balanceOf(address(vault)), amount); } function test_release() public { uint256 amount = 1000 * 10**18; uint256 triggerId = 1; address recipient = address(0x20); vm.startPrank(user); token.approve(address(vault), amount); vm.stopPrank(); vm.prank(settlementOperator); vault.lock(address(token), user, amount, triggerId, RailTypes.Rail.SWIFT); uint256 recipientBalanceBefore = token.balanceOf(recipient); vm.expectEmit(true, true, false, true); emit Released(address(token), recipient, amount, triggerId); vm.prank(settlementOperator); vault.release(address(token), recipient, amount, triggerId); assertEq(vault.getEscrowAmount(address(token), triggerId), 0); assertEq(vault.getTotalEscrow(address(token)), 0); assertEq(token.balanceOf(recipient), recipientBalanceBefore + amount); } function test_release_insufficientEscrow() public { uint256 amount = 1000 * 10**18; uint256 triggerId = 1; vm.startPrank(user); token.approve(address(vault), amount); vm.stopPrank(); vm.prank(settlementOperator); vault.lock(address(token), user, amount, triggerId, RailTypes.Rail.SWIFT); vm.prank(settlementOperator); vm.expectRevert("RailEscrowVault: insufficient escrow"); vault.release(address(token), address(0x20), amount + 1, triggerId); } // Helper events for testing (match IRailEscrowVault events) event Locked(address indexed token, address indexed account, uint256 amount, uint256 indexed triggerId, uint8 rail); event Released(address indexed token, address indexed recipient, uint256 amount, uint256 indexed triggerId); }