diff --git a/contracts/src/UntronCore.sol b/contracts/src/UntronCore.sol index a05f99d..858edf7 100644 --- a/contracts/src/UntronCore.sol +++ b/contracts/src/UntronCore.sol @@ -66,6 +66,9 @@ contract UntronCore is Initializable, OwnableUpgradeable, UntronTransfers, Untro external onlyOwner { + + require(_maxOrderSize > 0 && _requiredCollateral > 0 && _orderTtlMillis > 0, "Parameters should be greater than zero"); + maxOrderSize = _maxOrderSize; requiredCollateral = _requiredCollateral; orderTtlMillis = _orderTtlMillis; @@ -137,7 +140,7 @@ contract UntronCore is Initializable, OwnableUpgradeable, UntronTransfers, Untro /// @inheritdoc IUntronCore function createOrder(address provider, bytes21 receiver, uint256 size, uint256 rate, Transfer calldata transfer) - external + external { // collect collateral from the order creator internalTransferFrom(msg.sender, requiredCollateral); @@ -145,6 +148,7 @@ contract UntronCore is Initializable, OwnableUpgradeable, UntronTransfers, Untro // amount is the amount of USDT L2 that will be taken from the provider // based on the order size (which is in USDT Tron) and provider's rate (uint256 amount,) = conversion(size, rate, false, false); + require(amount > 0, "Amount should be greater than zero"); uint256 providerMinDeposit = _providers[provider].minDeposit; if (isReceiverBusy[receiver] != bytes32(0)) { @@ -411,6 +415,9 @@ contract UntronCore is Initializable, OwnableUpgradeable, UntronTransfers, Untro uint256 minDeposit, bytes21[] calldata receivers ) external { + + require(liquidity > 0 && rate > 0 && minOrderSize > 0 && minDeposit > 0, "Parameters should be greater than zero"); + // get provider's current liquidity uint256 currentLiquidity = _providers[msg.sender].liquidity; diff --git a/contracts/src/UntronFees.sol b/contracts/src/UntronFees.sol index 44bd7e9..10aa532 100644 --- a/contracts/src/UntronFees.sol +++ b/contracts/src/UntronFees.sol @@ -17,6 +17,8 @@ abstract contract UntronFees is IUntronFees, UntronTools, OwnableUpgradeable { uint256 public fulfillerFee; // USDT L2 function _setFeesVariables(uint256 _relayerFee, uint256 _fulfillerFee) internal { + require(_relayerFee > 0 && _fulfillerFee > 0, "Relayer fee must be greater than zero"); + relayerFee = _relayerFee; fulfillerFee = _fulfillerFee; } diff --git a/contracts/test/UntronFees.t.sol b/contracts/test/UntronFees.t.sol index b8af4d3..78a845f 100644 --- a/contracts/test/UntronFees.t.sol +++ b/contracts/test/UntronFees.t.sol @@ -51,7 +51,7 @@ contract UntronFeesTest is Test { ERC1967Proxy proxy = new ERC1967Proxy(address(untronFeesImplementation), initData); untronFees = UntronCore(address(proxy)); - untronFees.setFeesVariables(0, 0); + untronFees.setFeesVariables(1, 1); vm.stopPrank(); } @@ -59,8 +59,8 @@ contract UntronFeesTest is Test { function test_setUp() public view { assertEq(untronFees.owner(), admin); - assertEq(untronFees.relayerFee(), 0); - assertEq(untronFees.fulfillerFee(), 0); + assertEq(untronFees.relayerFee(), 1); + assertEq(untronFees.fulfillerFee(), 1); } function test_setUntronFeesVariables_SetVariables() public { @@ -86,8 +86,8 @@ contract UntronFeesTest is Test { } function test_SetExtremeFees(uint256 relayerFee, uint256 fulfillerFee) public { - vm.assume(relayerFee <= 1000000); // Assuming max is 100% in basis points - vm.assume(fulfillerFee <= 1 ether); // Assuming a reasonable max fee in USDT + vm.assume(relayerFee > 0 && relayerFee <= 1000000); // Assuming max is 100% in basis points + vm.assume(fulfillerFee > 0 && fulfillerFee <= 1 ether); // Assuming a reasonable max fee in USDT vm.startPrank(admin); // Ensure we are using the admin address untronFees.setFeesVariables(relayerFee, fulfillerFee); @@ -96,25 +96,21 @@ contract UntronFeesTest is Test { vm.stopPrank(); // Stop prank after setting fees } -function test_SetZeroAndNegativeFees(int256 relayerFee, int256 fulfillerFee) public { - vm.assume(relayerFee < 0 || relayerFee == 0); - vm.assume(fulfillerFee < 0 || fulfillerFee == 0); +function test_setFeesVariables_RevertIf_FeeIsZero() public { + uint256 relayerFee = 0; + uint256 fulfillerFee = 0; -vm.startPrank(admin); - if (relayerFee >= 0 && fulfillerFee >= 0) { - untronFees.setFeesVariables(uint256(relayerFee), uint256(fulfillerFee)); - } else { - vm.expectRevert(); - untronFees.setFeesVariables(uint256(relayerFee), uint256(fulfillerFee)); - } + vm.startPrank(admin); + vm.expectRevert(); + untronFees.setFeesVariables(relayerFee, fulfillerFee); + vm.stopPrank(); -vm.stopPrank(); } function test_BoundaryTestingOnFees(uint256 relayerFee, uint256 fulfillerFee) public { - vm.assume(relayerFee >= 0 && relayerFee <= 1000000); - vm.assume(fulfillerFee >= 0 && fulfillerFee <= 1 ether); + vm.assume(relayerFee > 0 && relayerFee <= 1000000); + vm.assume(fulfillerFee > 0 && fulfillerFee <= 1 ether); vm.startPrank(admin); // Ensure we are using the admin address untronFees.setFeesVariables(relayerFee, fulfillerFee); diff --git a/contracts/test/core/UntronCore.setProvider.t.sol b/contracts/test/core/UntronCore.setProvider.t.sol index f1b319a..9574b35 100644 --- a/contracts/test/core/UntronCore.setProvider.t.sol +++ b/contracts/test/core/UntronCore.setProvider.t.sol @@ -378,41 +378,41 @@ contract SetProviderTest is UntronCoreBase { } /// @notice Test for setting zero liquidity - function test_setProvider_ZeroLiquidity() public { - // Given: A provider with existing liquidity who wants to withdraw all funds - address providerAddress = vm.addr(100); - uint256 initialLiquidity = 1000e6; - uint256 rate = 1e6; - uint256 minOrderSize = 100e6; - uint256 minDeposit = 10e6; - bytes21[] memory receivers = new bytes21[](1); - receivers[0] = addressToBytes21(vm.addr(200)); - - // Initial setup - mintUSDT(providerAddress, initialLiquidity); - approveUSDT(providerAddress, address(untron), initialLiquidity); - - vm.startPrank(providerAddress); - untron.setProvider(initialLiquidity, rate, minOrderSize, minDeposit, receivers); - vm.stopPrank(); - - // When: The provider sets liquidity to zero - vm.expectEmit(true, true, true, true); - emit ProviderUpdated(providerAddress, 0, rate, minOrderSize, minDeposit, receivers); - - vm.startPrank(providerAddress); - untron.setProvider(0, rate, minOrderSize, minDeposit, receivers); - vm.stopPrank(); - - // Then: Verify provider's liquidity is zero and funds are returned - IUntronCore.Provider memory provider = untron.providers(providerAddress); - assertEq(provider.liquidity, 0, "Provider's liquidity should be zero"); - - uint256 providerBalance = usdt.balanceOf(providerAddress); - assertEq(providerBalance, initialLiquidity, "Provider should receive all their liquidity back"); - - uint256 contractBalance = usdt.balanceOf(address(untron)); - assertEq(contractBalance, 0, "Contract's USDT balance should be zero"); + function testSetProvider_RevertIf_ZeroLiquidity() public { + // Given: A provider with existing liquidity who wants to withdraw all funds + address providerAddress = vm.addr(100); + uint256 initialLiquidity = 1000e6; + uint256 rate = 1e6; + uint256 minOrderSize = 100e6; + uint256 minDeposit = 10e6; + bytes21[] memory receivers = new bytes21[](1); + receivers[0] = addressToBytes21(vm.addr(200)); + + // Initial setup + mintUSDT(providerAddress, initialLiquidity); + approveUSDT(providerAddress, address(untron), initialLiquidity); + + vm.startPrank(providerAddress); + untron.setProvider(initialLiquidity, rate, minOrderSize, minDeposit, receivers); + vm.stopPrank(); + + // When: The provider tries to set liquidity to zero (expecting a revert) + vm.startPrank(providerAddress); + vm.expectRevert(); + untron.setProvider(0, rate, minOrderSize, minDeposit, receivers); + vm.stopPrank(); + + // Then: Verify provider's liquidity remains unchanged + IUntronCore.Provider memory provider = untron.providers(providerAddress); + assertEq(provider.liquidity, initialLiquidity, "Provider's liquidity should remain unchanged"); + + // Verify the provider still has their initial balance + uint256 providerBalance = usdt.balanceOf(providerAddress); + assertEq(providerBalance, 0, "Provider should still have their initial liquidity"); + + // Verify contract balance + uint256 contractBalance = usdt.balanceOf(address(untron)); + assertEq(contractBalance, initialLiquidity, "Contract's USDT balance should remain the same"); } /// @notice Test for setting zero receivers @@ -512,41 +512,111 @@ contract SetProviderTest is UntronCoreBase { assertEq(storedOrderId, bytes32(0), "Receiver should no longer be busy due to the expired order"); } - /// @notice Fuzz test: Random valid inputs for setProvider - function testFuzz_setProvider_RandomValidInputs( - uint256 liquidity, - uint256 rate, - uint256 minOrderSize, - uint256 minDeposit - ) public { - // Bound the inputs to reasonable values - liquidity = bound(liquidity, 0, 1e12); // Up to 1,000,000,000 USDT - rate = bound(rate, 1e5, 1e7); // Between 0.1 and 10 - minOrderSize = bound(minOrderSize, 1e6, 1e9); // Between 1 and 1,000 USDT - minDeposit = bound(minDeposit, 0, minOrderSize); + + /// @notice Test for setting provider with zero min deposit + function testSetProvider_RevertIf_ZeroMinDeposit() public { + // Given: A provider address and parameters + address providerAddress = vm.addr(100); + uint256 liquidity = 1000e6; + uint256 rate = 1e6; + uint256 minOrderSize = 100e6; + uint256 minDeposit = 0; // Set minDeposit to zero + bytes21[] memory receivers = new bytes21[](2); + receivers[0] = addressToBytes21(vm.addr(200)); + + // Mint and approve USDT + mintUSDT(providerAddress, liquidity); + approveUSDT(providerAddress, address(untron), liquidity); + + // Store initial provider state + IUntronCore.Provider memory initialProvider = untron.providers(providerAddress); + + // Start the prank as the provider + vm.startPrank(providerAddress); + + // When: Attempting to set provider + vm.expectRevert("Parameters should be greater than zero"); + untron.setProvider(liquidity, rate, minOrderSize, minDeposit, receivers); + vm.stopPrank(); + // Then: Verify that the provider's state remains unchanged + IUntronCore.Provider memory finalProvider = untron.providers(providerAddress); + assertEq(finalProvider.liquidity, initialProvider.liquidity, "Provider's liquidity should remain unchanged"); + assertEq(finalProvider.rate, initialProvider.rate, "Provider's rate should remain unchanged"); + assertEq(finalProvider.minOrderSize, initialProvider.minOrderSize, "Provider's minOrderSize should remain unchanged"); + assertEq(finalProvider.minDeposit, initialProvider.minDeposit, "Provider's minDeposit should remain unchanged"); + } + + /// @notice Test for setting provider with zero min order size + function testSetProvider_RevertIf_ZeroMinOrderSize() public { + // Given: A provider address and parameters address providerAddress = vm.addr(100); + uint256 liquidity = 1000e6; + uint256 rate = 1e6; + uint256 minOrderSize = 0; // Set minOrderSize to zero + uint256 minDeposit = 10e6; bytes21[] memory receivers = new bytes21[](2); receivers[0] = addressToBytes21(vm.addr(200)); - receivers[1] = addressToBytes21(vm.addr(201)); // Mint and approve USDT mintUSDT(providerAddress, liquidity); approveUSDT(providerAddress, address(untron), liquidity); - // When: The provider sets their profile with random valid inputs + // Store initial provider state + IUntronCore.Provider memory initialProvider = untron.providers(providerAddress); + + // Start the prank as the provider vm.startPrank(providerAddress); + + // When: Attempting to set provider + vm.expectRevert("Parameters should be greater than zero"); untron.setProvider(liquidity, rate, minOrderSize, minDeposit, receivers); vm.stopPrank(); - // Then: Verify provider's information - IUntronCore.Provider memory provider = untron.providers(providerAddress); - assertEq(provider.liquidity, liquidity, "Provider's liquidity should match"); - assertEq(provider.rate, rate, "Provider's rate should match"); - assertEq(provider.minOrderSize, minOrderSize, "Provider's minOrderSize should match"); - assertEq(provider.minDeposit, minDeposit, "Provider's minDeposit should match"); + // Then: Verify that the provider's state remains unchanged + IUntronCore.Provider memory finalProvider = untron.providers(providerAddress); + assertEq(finalProvider.liquidity, initialProvider.liquidity, "Provider's liquidity should remain unchanged"); + assertEq(finalProvider.rate, initialProvider.rate, "Provider's rate should remain unchanged"); + assertEq(finalProvider.minOrderSize, initialProvider.minOrderSize, "Provider's minOrderSize should remain unchanged"); + assertEq(finalProvider.minDeposit, initialProvider.minDeposit, "Provider's minDeposit should remain unchanged"); + } + + /// @notice Test for setting provider with zero rate + function testSetProvider_RevertIf_ZeroRate() public { + // Given: A provider address and parameters + address providerAddress = vm.addr(100); + uint256 liquidity = 1000e6; + uint256 rate = 0; // Set rate to zero + uint256 minOrderSize = 100e6; + uint256 minDeposit = 10e6; + bytes21[] memory receivers = new bytes21[](2); + receivers[0] = addressToBytes21(vm.addr(200)); + + // Mint and approve USDT + mintUSDT(providerAddress, liquidity); + approveUSDT(providerAddress, address(untron), liquidity); + + // Store initial provider state + IUntronCore.Provider memory initialProvider = untron.providers(providerAddress); + + // Start the prank as the provider + vm.startPrank(providerAddress); + + // When: Attempting to set provider + vm.expectRevert("Parameters should be greater than zero"); + untron.setProvider(liquidity, rate, minOrderSize, minDeposit, receivers); + vm.stopPrank(); + + // Then: Verify that the provider's state remains unchanged + IUntronCore.Provider memory finalProvider = untron.providers(providerAddress); + assertEq(finalProvider.liquidity, initialProvider.liquidity, "Provider's liquidity should remain unchanged"); + assertEq(finalProvider.rate, initialProvider.rate, "Provider's rate should remain unchanged"); + assertEq(finalProvider.minOrderSize, initialProvider.minOrderSize, "Provider's minOrderSize should remain unchanged"); + assertEq(finalProvider.minDeposit, initialProvider.minDeposit, "Provider's minDeposit should remain unchanged"); } + + /// @notice Invariant test: Provider state consistency after multiple updates function test_invariant_setProvider_ProviderStateConsistency() public { // This test would require setting up a stateful testing environment