Skip to content

Commit

Permalink
Merge pull request #2 from ultrasoundlabs/improve-smart-contract
Browse files Browse the repository at this point in the history
Added Change From Audit
  • Loading branch information
olaoyesalem authored Oct 18, 2024
2 parents 3bd9287 + 8fb5847 commit 66889cc
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 74 deletions.
9 changes: 8 additions & 1 deletion contracts/src/UntronCore.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -137,14 +140,15 @@ 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);

// 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)) {
Expand Down Expand Up @@ -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;

Expand Down
2 changes: 2 additions & 0 deletions contracts/src/UntronFees.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
32 changes: 14 additions & 18 deletions contracts/test/UntronFees.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,16 @@ 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();
}

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 {
Expand All @@ -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);
Expand All @@ -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);
Expand Down
180 changes: 125 additions & 55 deletions contracts/test/core/UntronCore.setProvider.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 66889cc

Please sign in to comment.