diff --git a/solidity/src/FlowYieldVaultsRequests.sol b/solidity/src/FlowYieldVaultsRequests.sol index 8cc0f08..6012dbd 100644 --- a/solidity/src/FlowYieldVaultsRequests.sol +++ b/solidity/src/FlowYieldVaultsRequests.sol @@ -99,6 +99,9 @@ contract FlowYieldVaultsRequests is ReentrancyGuard, Ownable2Step { address public constant NATIVE_FLOW = 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF; + /// @notice Default minimum deposit for initially supported tokens + uint256 public constant DEFAULT_MINIMUM_BALANCE = 1 ether; + /// @notice WFLOW (Wrapped FLOW) ERC20 token address /// @dev On Cadence side, WFLOW is automatically unwrapped to native FlowToken by FlowEVMBridge address public immutable WFLOW; @@ -193,6 +196,9 @@ contract FlowYieldVaultsRequests is ReentrancyGuard, Ownable2Step { /// @notice COA address cannot be zero error InvalidCOAAddress(); + /// @notice Minimum balance must be non-zero for supported tokens + error InvalidMinimumBalance(); + /// @notice Address array cannot be empty error EmptyAddressArray(); @@ -489,24 +495,18 @@ contract FlowYieldVaultsRequests is ReentrancyGuard, Ownable2Step { /// @param coaAddress Address of the authorized COA /// @param wflowAddress Address of the WFLOW (Wrapped FLOW) ERC20 token constructor(address coaAddress, address wflowAddress) Ownable(msg.sender) { + if (coaAddress == address(0)) revert InvalidCOAAddress(); + authorizedCOA = coaAddress; WFLOW = wflowAddress; _requestIdCounter = 1; maxPendingRequestsPerUser = 10; - allowedTokens[NATIVE_FLOW] = TokenConfig({ - isSupported: true, - minimumBalance: 1 ether, - isNative: true - }); + _setTokenConfig(NATIVE_FLOW, true, DEFAULT_MINIMUM_BALANCE, true); // WFLOW is treated as ERC20 on EVM side, but unwraps to native FlowToken on Cadence if (wflowAddress != address(0)) { - allowedTokens[WFLOW] = TokenConfig({ - isSupported: true, - minimumBalance: 1 ether, - isNative: false - }); + _setTokenConfig(WFLOW, true, DEFAULT_MINIMUM_BALANCE, false); } } @@ -627,11 +627,7 @@ contract FlowYieldVaultsRequests is ReentrancyGuard, Ownable2Step { uint256 minimumBalance, bool isNative ) external onlyOwner { - allowedTokens[tokenAddress] = TokenConfig({ - isSupported: isSupported, - minimumBalance: minimumBalance, - isNative: isNative - }); + _setTokenConfig(tokenAddress, isSupported, minimumBalance, isNative); emit TokenConfigured( tokenAddress, @@ -1443,6 +1439,22 @@ contract FlowYieldVaultsRequests is ReentrancyGuard, Ownable2Step { // Internal Functions // ============================================ + /// @dev Internal token configuration helper with minimum-balance validation. + function _setTokenConfig( + address tokenAddress, + bool isSupported, + uint256 minimumBalance, + bool isNative + ) internal { + if (isSupported && minimumBalance == 0) revert InvalidMinimumBalance(); + + allowedTokens[tokenAddress] = TokenConfig({ + isSupported: isSupported, + minimumBalance: minimumBalance, + isNative: isNative + }); + } + /** * @dev Validates deposit parameters and transfers tokens to this contract for escrow. * Performs comprehensive validation including amount, token support, and minimum balance checks. diff --git a/solidity/test/FlowYieldVaultsRequests.t.sol b/solidity/test/FlowYieldVaultsRequests.t.sol index a200cb3..e3d9116 100644 --- a/solidity/test/FlowYieldVaultsRequests.t.sol +++ b/solidity/test/FlowYieldVaultsRequests.t.sol @@ -415,6 +415,11 @@ contract FlowYieldVaultsRequestsTest is Test { c.setAuthorizedCOA(address(0)); } + function test_Constructor_RevertZeroCOAAddress() public { + vm.expectRevert(FlowYieldVaultsRequests.InvalidCOAAddress.selector); + new FlowYieldVaultsRequestsTestHelper(address(0), WFLOW); + } + function test_SetTokenConfig() public { address token = makeAddr("token"); @@ -427,6 +432,14 @@ contract FlowYieldVaultsRequestsTest is Test { assertEq(isNative, false); } + function test_SetTokenConfig_RevertZeroMinimumForSupportedToken() public { + address token = makeAddr("token"); + + vm.prank(c.owner()); + vm.expectRevert(FlowYieldVaultsRequests.InvalidMinimumBalance.selector); + c.setTokenConfig(token, true, 0, false); + } + function test_SetMaxPendingRequestsPerUser() public { vm.prank(c.owner()); c.setMaxPendingRequestsPerUser(5);