@@ -127,7 +127,7 @@ contract PayNodeRouterTest is Test {
127127
128128 usdc.mint (payer, amount);
129129
130- bytes32 orderId = keccak256 ("fuzz_order " );
130+ bytes32 orderId = keccak256 (abi.encodePacked ( "fuzz_order " , amount, fuzzMerchant) );
131131
132132 uint256 payerBalanceBefore = usdc.balanceOf (payer);
133133 uint256 merchantBalanceBefore = usdc.balanceOf (fuzzMerchant);
@@ -154,7 +154,7 @@ contract PayNodeRouterTest is Test {
154154 // Mint extra to ensure enough balance for high fuzz amounts
155155 usdc.mint (payer, amount);
156156
157- bytes32 orderId = keccak256 ("fuzz_order_permit " );
157+ bytes32 orderId = keccak256 (abi.encodePacked ( "fuzz_order_permit " , amount, fuzzMerchant) );
158158 uint256 deadline = block .timestamp + 1 hours ;
159159
160160 bytes32 permitTypehash =
@@ -172,4 +172,59 @@ contract PayNodeRouterTest is Test {
172172
173173 assertEq (usdc.balanceOf (treasury), treasuryBalanceBefore + (amount / 100 ));
174174 }
175+
176+ // --- Negative Tests ---
177+
178+ function test_RevertWhen_AmountTooLow () public {
179+ uint256 tooLow = 999 ; // Below MIN_PAYMENT_AMOUNT (1000)
180+ bytes32 orderId = keccak256 ("order_dust " );
181+
182+ vm.prank (payer);
183+ usdc.approve (address (router), tooLow);
184+
185+ vm.expectRevert (PayNodeRouter.AmountTooLow.selector );
186+ vm.prank (payer);
187+ router.pay (address (usdc), merchant, tooLow, orderId);
188+ }
189+
190+ function test_RevertWhen_MerchantIsZeroAddress () public {
191+ uint256 amount = 100 * 10 ** 6 ;
192+ bytes32 orderId = keccak256 ("order_zero_merchant " );
193+
194+ vm.prank (payer);
195+ usdc.approve (address (router), amount);
196+
197+ vm.expectRevert (PayNodeRouter.InvalidAddress.selector );
198+ vm.prank (payer);
199+ router.pay (address (usdc), address (0 ), amount, orderId);
200+ }
201+
202+ function test_RevertWhen_TokenIsZeroAddress () public {
203+ uint256 amount = 100 * 10 ** 6 ;
204+ bytes32 orderId = keccak256 ("order_zero_token " );
205+
206+ vm.expectRevert (PayNodeRouter.InvalidAddress.selector );
207+ vm.prank (payer);
208+ router.pay (address (0 ), merchant, amount, orderId);
209+ }
210+
211+ function test_RevertWhen_PayWithPermit_CallerIsNotPayer () public {
212+ uint256 amount = 100 * 10 ** 6 ;
213+ bytes32 orderId = keccak256 ("order_unauthorized " );
214+ uint256 deadline = block .timestamp + 1 hours ;
215+
216+ bytes32 permitTypehash =
217+ keccak256 ("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline) " );
218+ bytes32 structHash =
219+ keccak256 (abi.encode (permitTypehash, payer, address (router), amount, usdc.nonces (payer), deadline));
220+ bytes32 digest = keccak256 (abi.encodePacked ("\x19\x01 " , usdc.DOMAIN_SEPARATOR (), structHash));
221+
222+ (uint8 v , bytes32 r , bytes32 s ) = vm.sign (payerPrivateKey, digest);
223+
224+ // Call from a different address (not payer)
225+ address attacker = address (0xBEEF );
226+ vm.expectRevert (PayNodeRouter.UnauthorizedCaller.selector );
227+ vm.prank (attacker);
228+ router.payWithPermit (payer, address (usdc), merchant, amount, orderId, deadline, v, r, s);
229+ }
175230}
0 commit comments