Closed Dexaran closed 3 years ago
The report was sent to the developer.
SOY Farming smart contract security audit report performed by Callisto Security Audit Department
Commit b166fee968b6a10f30ab564c146260878b7b73b8
In total, 1 issues were reported, including:
0 high severity issues.
0 medium severity issues.
1 low severity issues.
In total, 6 notes were reported, including:
2 notes.
4 owner privileges.
When a contract read a value in a storage it costs about 500 gas. If in a function you are using the same value in a storage more than once will be gas-effective to assign that value to local variable and use the local variable wherever need.
In the contract GlobalFarm
the storage variable localFarmId[_localFarmAddress]
used many times in many functions:
To reduce gas cost of using those functions should be used local variable uint256 _localFarmId = localFarmId[_localFarmAddress]
instead of storage.
For example:
function removeLocalFarmByAddress(address _localFarmAddress) external onlyOwner {
require (farmExists(_localFarmAddress), "LocalFarm with this address does not exist");
uint256 _localFarmId = localFarmId[_localFarmAddress];
totalMultipliers = totalMultipliers - uint256(localFarms[_localFarmId].multiplier); // update totalMultipliers
delete localFarms[_localFarmId]; // delete LocalFarm structure - automatically assign 0 to each element (reduce transaction gas cost).
localFarmId[_localFarmAddress] = 0;
emit RemoveLocalFarm(_localFarmAddress);
}
In the function mintFarmingReward()
to calculate _reward the nominator 1000 was used to get allocation in the getAllocationX1000()
function. In case of growing numbers of local farms and totalMultipliers
accordingly the accuracy may be lost due to small nominator value.
Also in this function the next_payment()
used many times. To reduce gas usage use uint256 _nextPayment = next_payment();
and use local variable _nextPayment
wherever needed.
Use this fixed function:
function mintFarmingReward(address _localFarmAddress) external {
require (farmExists(_localFarmAddress), "LocalFarm with this address does not exist");
uint256 _localFarmId = localFarmId[_localFarmAddress];
uint256 _nextPayment = next_payment();
// Comparing against 0:00 UTC always
// to enable withdrawals for full days only at any point within 24 hours of a day.
if(localFarms[_localFarmId].lastPayment + paymentDelay > _nextPayment)
{
// Someone is requesting payment for a Local Farm that was paid recently.
// Do nothing.
return;
}
else
{
uint256 _reward = (_nextPayment - localFarms[_localFarmId].lastPayment) * getRewardPerSecond() * localFarms[_localFarmId].multiplier / totalMultipliers;
localFarms[_localFarmId].lastPayment = _nextPayment;
rewardsToken.mint(_localFarmAddress, _reward);
ILocalFarm(_localFarmAddress).notifyRewardAmount(_reward);
}
}
The cross-contracts calls cost additional 2000 gas, to reduce gas usage minimize number of cross-contracts calls.
The functions getRewardPerSecond and getAllocationX1000 calls related functions in the GlobalFarm
contract. But they are used only in pattern uint256 _reward = multiplier * getRewardPerSecond() * getAllocationX1000() / 1000;
here and here.
To reduce transaction gas cost create in the GlobalFarm
contract function:
function getReward(uint256 interval) external view returns(uint256 reward) {
reward = interval * getRewardPerSecond() * localFarms[localFarmId[msg.sender]].multiplier / totalMultipliers;
}
and call it instead of multiplier * getRewardPerSecond() * getAllocationX1000() / 1000;
pattern.
LocalFarm
contractThe LocalFarm
contract owner has right to withdraw all rewards token of inactive farming contract even if users did not claimed their rewards.
Check that all users withdraw their LP tokens adding this requirements:
require(lpToken.balanceOf(address(this) == 0, "Not all users quit");
GlobalFarm
contractThe owner of of GlobalFarm
contract has right to:
CREATE (0xf0)
opcode is assigned following this scheme keccak256(rlp([sender, nonce]))
. Therefore you need to use the same address that was originally used at the main chain to deploy the mock contract at a transaction with the nonce
that matches that on the original chain. Example: If you have deployed your main contract with address 0x010101 at your 2021th transaction then you need to increase your nonce of 0x010101 address to 2020 at the chain where your mock contract will be deployed. Then you can deploy your mock contract with your 2021th transaction, and it will receive the same address as your mainnet contract.The audited smart contract can be deployed. Only low severity issues were found during the audit.
It is recommended to adhere to the security practices described in pt. 4 of this report to ensure the contract's operability and prevent any issues that are not directly related to the code of this smart contract.
Audit request
Formal review of the SOY Farming contract prior to mainnet deployment.
Source code
Disclosure policy
Standard disclosure policy.
Platform
CLO