diff --git a/script/Config.s.sol b/script/Config.s.sol index ef34b6a..13ba006 100644 --- a/script/Config.s.sol +++ b/script/Config.s.sol @@ -3,9 +3,11 @@ pragma solidity ^0.8.20; // Generated by scripts/sync-config.py library Config { - address public constant ROUTER_MAINNET = 0x92e20164FC457a2aC35f53D06268168e6352b200; - address public constant ROUTER_SEPOLIA = 0xB587Bc36aaCf65962eCd6Ba59e2DA76f2f575408; + address public constant ROUTER_MAINNET = 0x4A73696ccF76E7381b044cB95127B3784369Ed63; + address public constant ROUTER_SEPOLIA = 0x24cD8b68aaC209217ff5a6ef1Bf55a59f2c8Ca6F; address public constant TREASURY = 0x598bF63F5449876efafa7b36b77Deb2070621C0E; address public constant USDC_MAINNET = 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913; address public constant USDC_SEPOLIA = 0xeAC1f2C7099CdaFfB91Aa3b8Ffd653Ef16935798; + uint256 public constant MIN_PAYMENT_AMOUNT = 1000; + uint256 public constant FEE_BPS = 100; } diff --git a/script/DeploySepolia.s.sol b/script/DeploySepolia.s.sol index efd4ec9..e913b4f 100644 --- a/script/DeploySepolia.s.sol +++ b/script/DeploySepolia.s.sol @@ -3,10 +3,11 @@ pragma solidity ^0.8.20; import {Script, console} from "forge-std/Script.sol"; import {PayNodeRouter} from "../src/PayNodeRouter.sol"; +import {Config} from "./Config.s.sol"; contract DeploySepolia is Script { function run() external { - address treasury = 0x598bF63F5449876efafa7b36b77Deb2070621C0E; + address treasury = Config.TREASURY; vm.startBroadcast(); PayNodeRouter router = new PayNodeRouter(treasury); diff --git a/src/PayNodeRouter.sol b/src/PayNodeRouter.sol index 22582dc..cd79697 100644 --- a/src/PayNodeRouter.sol +++ b/src/PayNodeRouter.sol @@ -25,9 +25,11 @@ contract PayNodeRouter is Ownable2Step, Pausable { // Fixed protocol fee: 1% (100 basis points out of 10000) uint256 public constant PROTOCOL_FEE_BPS = 100; uint256 public constant MAX_BPS = 10000; + uint256 public constant MIN_PAYMENT_AMOUNT = 1000; error InvalidAddress(); - error AmountMustBeGreaterThanZero(); + error AmountTooLow(); + error UnauthorizedCaller(); // Redesigned event to match SDK requirements (indexed orderId, token verification, chainId) event PaymentReceived( @@ -98,6 +100,8 @@ contract PayNodeRouter is Ownable2Step, Pausable { bytes32 r, bytes32 s ) external whenNotPaused { + if (msg.sender != payer) revert UnauthorizedCaller(); + // 1. Consume permit to grant allowance to this router IERC20Permit(token).permit(payer, address(this), amount, deadline, v, r, s); @@ -110,7 +114,7 @@ contract PayNodeRouter is Ownable2Step, Pausable { */ function _processPayment(address payer, address token, address merchant, uint256 amount, bytes32 orderId) internal { if (merchant == address(0) || token == address(0)) revert InvalidAddress(); - if (amount == 0) revert AmountMustBeGreaterThanZero(); + if (amount < MIN_PAYMENT_AMOUNT) revert AmountTooLow(); // Calculate 1% fee uint256 fee = (amount * PROTOCOL_FEE_BPS) / MAX_BPS; diff --git a/test/PayNodeRouter.t.sol b/test/PayNodeRouter.t.sol index 7ed93bc..b62fb30 100644 --- a/test/PayNodeRouter.t.sol +++ b/test/PayNodeRouter.t.sol @@ -113,9 +113,8 @@ contract PayNodeRouterTest is Test { vm.expectEmit(true, true, true, true); emit PaymentReceived(orderId, merchant, payer, address(usdc), paymentAmount, expectedFee, block.chainid); - // We use an agent to send the transaction to test the Relayer functionality properly! - address agent = address(uint160(0x12345)); - vm.prank(agent); + // Use the payer as the caller to prevent MiTM issues as per router security logic + vm.prank(payer); router.payWithPermit(payer, address(usdc), merchant, paymentAmount, orderId, deadline, v, r, s); assertEq(usdc.balanceOf(merchant), 99 * 10 ** 6);