// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "forge-std/Test.sol"; import "../../src/RailTriggerRegistry.sol"; import "../../src/interfaces/IRailTriggerRegistry.sol"; import "../../src/libraries/RailTypes.sol"; contract RailTriggerFuzzTest is Test { RailTriggerRegistry public registry; address public admin; address public railOperator; address public railAdapter; address public token; function setUp() public { admin = address(0x1); railOperator = address(0x2); railAdapter = address(0x3); token = address(0x100); registry = new RailTriggerRegistry(admin); vm.startPrank(admin); registry.grantRole(registry.RAIL_OPERATOR_ROLE(), railOperator); registry.grantRole(registry.RAIL_ADAPTER_ROLE(), railAdapter); vm.stopPrank(); } function testFuzz_createTrigger( uint8 railValue, bytes32 msgType, bytes32 accountRefId, bytes32 instructionId, uint256 amount ) public { // Bound rail value to valid enum RailTypes.Rail rail = RailTypes.Rail(railValue % 4); // Ensure non-zero values vm.assume(accountRefId != bytes32(0)); vm.assume(instructionId != bytes32(0)); vm.assume(amount > 0); vm.assume(amount < type(uint128).max); // Reasonable bound IRailTriggerRegistry.Trigger memory t = IRailTriggerRegistry.Trigger({ id: 0, rail: rail, msgType: msgType, accountRefId: accountRefId, walletRefId: bytes32(0), token: token, amount: amount, currencyCode: keccak256("USD"), instructionId: instructionId, state: RailTypes.State.CREATED, createdAt: 0, updatedAt: 0 }); vm.prank(railOperator); uint256 id = registry.createTrigger(t); IRailTriggerRegistry.Trigger memory retrieved = registry.getTrigger(id); assertEq(uint8(retrieved.rail), uint8(rail)); assertEq(retrieved.msgType, msgType); assertEq(retrieved.amount, amount); assertEq(retrieved.instructionId, instructionId); assertTrue(registry.instructionIdExists(instructionId)); } function testFuzz_stateTransitions( bytes32 instructionId, uint8 targetStateValue ) public { vm.assume(instructionId != bytes32(0)); // Create trigger IRailTriggerRegistry.Trigger memory t = IRailTriggerRegistry.Trigger({ id: 0, rail: RailTypes.Rail.SWIFT, msgType: keccak256("pacs.008"), accountRefId: keccak256("account1"), walletRefId: bytes32(0), token: token, amount: 1000, currencyCode: keccak256("USD"), instructionId: instructionId, state: RailTypes.State.CREATED, createdAt: 0, updatedAt: 0 }); vm.prank(railOperator); uint256 id = registry.createTrigger(t); // Try valid transitions RailTypes.State targetState = RailTypes.State(targetStateValue % 8); // Valid transitions from CREATED if (targetState == RailTypes.State.VALIDATED || targetState == RailTypes.State.REJECTED || targetState == RailTypes.State.CANCELLED) { vm.prank(railAdapter); registry.updateState(id, targetState, bytes32(0)); IRailTriggerRegistry.Trigger memory trigger = registry.getTrigger(id); assertEq(uint8(trigger.state), uint8(targetState)); } } function testFuzz_duplicateInstructionId( bytes32 instructionId, bytes32 accountRefId1, bytes32 accountRefId2 ) public { vm.assume(instructionId != bytes32(0)); vm.assume(accountRefId1 != bytes32(0)); vm.assume(accountRefId2 != bytes32(0)); vm.assume(accountRefId1 != accountRefId2); IRailTriggerRegistry.Trigger memory t1 = IRailTriggerRegistry.Trigger({ id: 0, rail: RailTypes.Rail.SWIFT, msgType: keccak256("pacs.008"), accountRefId: accountRefId1, walletRefId: bytes32(0), token: token, amount: 1000, currencyCode: keccak256("USD"), instructionId: instructionId, state: RailTypes.State.CREATED, createdAt: 0, updatedAt: 0 }); vm.prank(railOperator); registry.createTrigger(t1); // Try to create another trigger with same instructionId IRailTriggerRegistry.Trigger memory t2 = IRailTriggerRegistry.Trigger({ id: 0, rail: RailTypes.Rail.FEDWIRE, msgType: keccak256("pain.001"), accountRefId: accountRefId2, walletRefId: bytes32(0), token: token, amount: 2000, currencyCode: keccak256("EUR"), instructionId: instructionId, // Same instructionId state: RailTypes.State.CREATED, createdAt: 0, updatedAt: 0 }); vm.prank(railOperator); vm.expectRevert("RailTriggerRegistry: duplicate instructionId"); registry.createTrigger(t2); } }