Platform: Code4rena
Start Date: 26/07/2022
Pot Size: $75,000 USDC
Total HM: 29
Participants: 179
Period: 6 days
Judge: LSDan
Total Solo HM: 6
Id: 148
League: ETH
Rank: 58/179
Findings: 3
Award: $188.90
π Selected for report: 0
π Solo Findings: 0
π Selected for report: cloudjunky
Also found by: 0x1f8b, 0x4non, 0x52, 0xDjango, 0xHarry, 0xNazgul, 0xNineDec, 0xf15ers, 0xsanson, 0xsolstars, 8olidity, Bnke0x0, CertoraInc, Chom, Deivitto, Dravee, GalloDaSballo, GimelSec, IllIllI, Jmaxmanblue, JohnSmith, Jujic, Kenshin, Krow10, Lambda, MEP, Noah3o6, RedOneN, Ruhum, StErMi, StyxRave, TomJ, Treasure-Seeker, TrungOre, _Adam, __141345__, arcoun, asutorufos, bardamu, bearonbike, bin2chen, brgltd, bulej93, c3phas, cRat1st0s, carlitox477, cccz, codexploder, cryptonue, cryptphi, cthulhu_cult, dharma09, dipp, djxploit, durianSausage, ellahi, giovannidisiena, hansfriese, horsefacts, hyh, immeas, indijanc, jayjonah8, jayphbee, joestakey, kenzo, kyteg, ladboy233, minhquanym, navinavu, obront, oyc_109, peritoflores, rbserver, reassor, rokinot, rotcivegaf, saian, scaraven, shenwilly, simon135, sseefried, teddav, zzzitron
0.0037 USDC - $0.00
File Name | SHA-1 Hash |
---|---|
2022-07-golom/contracts/vote-escrow/TokenUriHelper.sol | 4cb68851036989e92b4525acfcede317a03c5385 |
2022-07-golom/contracts/vote-escrow/VoteEscrowDelegation.sol | a099c4020b083e43def93cf4196032703761c0af |
2022-07-golom/contracts/vote-escrow/VoteEscrowCore.sol | 8d325a79916b4d01184657f9f875d198287d39be |
2022-07-golom/contracts/rewards/RewardDistributor.sol | 9ed9a8baa18b6f67a2555bf2287968a9cdeb62fb |
2022-07-golom/contracts/governance/GolomToken.sol | f83fbdb554fd1d0f3d970f7c84bc5de5dfc7f5d2 |
2022-07-golom/contracts/core/GolomTrader.sol | 2d1f78c663242498479552a54ec9f20aab459db9 |
call()
should be used instead of transfer()
on an address payableThe use of the deprecated transfer() function for an address will inevitably make the transaction fail when:
Additionally, using higher than 2300 gas might be mandatory for some multisig wallets.
payable(payAddress).transfer(payAmt); // royalty transfer to royaltyaddress
Use call()
instead of transfer()
.
VS Code
#0 - KenzoAgada
2022-08-03T14:03:51Z
Duplicate of #343
π Selected for report: IllIllI
Also found by: 0x1f8b, 0x4non, 0x52, 0xA5DF, 0xDjango, 0xLovesleep, 0xNazgul, 0xNineDec, 0xSmartContract, 0xackermann, 0xc0ffEE, 0xf15ers, 0xmatt, 0xsanson, 0xsolstars, 8olidity, AuditsAreUS, Bahurum, Bnke0x0, CRYP70, CertoraInc, Ch_301, Chom, CryptoMartian, Deivitto, DevABDee, Dravee, ElKu, Franfran, Funen, GalloDaSballo, GimelSec, GiveMeTestEther, Green, JC, Jmaxmanblue, JohnSmith, Jujic, Junnon, Kenshin, Krow10, Kumpa, Lambda, MEP, Maxime, MiloTruck, Mohandes, NoamYakov, Picodes, RedOneN, Rohan16, Rolezn, Ruhum, RustyRabbit, Sm4rty, Soosh, StErMi, StyxRave, Tadashi, TomJ, Treasure-Seeker, TrungOre, Waze, _Adam, __141345__, ajtra, ak1, apostle0x01, arcoun, asutorufos, async, benbaessler, berndartmueller, bin2chen, brgltd, c3phas, cRat1st0s, carlitox477, chatch, codetilda, codexploder, cryptonue, cryptphi, csanuragjain, cthulhu_cult, delfin454000, dipp, dirk_y, djxploit, ellahi, exd0tpy, fatherOfBlocks, giovannidisiena, hansfriese, horsefacts, hyh, idkwhatimdoing, indijanc, jayfromthe13th, jayphbee, joestakey, kenzo, kyteg, lucacez, luckypanda, mics, minhquanym, obront, oyc_109, pedr02b2, rajatbeladiya, rbserver, reassor, robee, rokinot, rotcivegaf, sach1r0, saian, saneryee, sashik_eth, scaraven, shenwilly, simon135, sseefried, supernova, teddav, ych18, zuhaibmohd, zzzitron
167.5763 USDC - $167.58
dailyEmission
rewardToken.totalSupply()
Staking/Trading/Exchange Rewards
File Name | SHA-1 Hash |
---|---|
2022-07-golom/contracts/vote-escrow/TokenUriHelper.sol | 4cb68851036989e92b4525acfcede317a03c5385 |
2022-07-golom/contracts/vote-escrow/VoteEscrowDelegation.sol | a099c4020b083e43def93cf4196032703761c0af |
2022-07-golom/contracts/vote-escrow/VoteEscrowCore.sol | 8d325a79916b4d01184657f9f875d198287d39be |
2022-07-golom/contracts/rewards/RewardDistributor.sol | 9ed9a8baa18b6f67a2555bf2287968a9cdeb62fb |
2022-07-golom/contracts/governance/GolomToken.sol | f83fbdb554fd1d0f3d970f7c84bc5de5dfc7f5d2 |
2022-07-golom/contracts/core/GolomTrader.sol | 2d1f78c663242498479552a54ec9f20aab459db9 |
tokenToEmit
and stakerReward
can be invalid because if there's an error in the rewardToken.totalSupply()
will evaluate to divide by zero creating inconsistencies in the logic.
uint256 tokenToEmit = (dailyEmission * (rewardToken.totalSupply() - rewardToken.balanceOf(address(ve)))) / rewardToken.totalSupply();
uint256 stakerReward = (tokenToEmit * rewardToken.balanceOf(address(ve))) / rewardToken.totalSupply();
Add a check if the value of rewardToken.totalSupply()
is zero.
VS Code
dailyEmission
Once 150M Golom tokens are given out as Airdrop and 62.5M as Genesis Rewards the daily emissions will adjust as a percentage of circulating supply and they will not be constant and equal to 600,000 tokens. So, this is breaking the project's logic for the emissions.
uint256 constant dailyEmission = 600000 * 10**18;
I suppose the docs have the correct formula and so the code must be adjusted to what is written in the docs.
VS Code
rewardToken.totalSupply()
Golom protocol has a native token which has a maximum supply limit of 1,000,000,000 (1 Billion $GOLOM tokens) but in the function addFee
the check rewardToken.totalSupply() > 1000000000 * 10**18
is insufficient. For example, if rewardToken.totalSupply() = 1000000000 * 10**18
it will pass the check and then it will mint more tokens than the maximum supply limit.
function addFee(address[2] memory addr, uint256 fee) public onlyTrader { //console.log(block.timestamp,epoch,fee); if (rewardToken.totalSupply() > 1000000000 * 10**18) { // if supply is greater then a billion dont mint anything, dont add trades return; }
Calculate first the rewards and if totalSupply is exceeding the maximum supply limit them up to 1 Billion.
VS Code
Staking/Trading/Exchange Rewards
$GOLOM Allocation & Distribution for Staking/Trading/Exchange Rewards is 68.75% of the supply (687,500,000 $GOLOM) but in the function addFee
there is no check that makes sure that the allocation will remain as planned.
function addFee(address[2] memory addr, uint256 fee) public onlyTrader {
Add a constant variable (totalRewards) that will be equal to 687,500,000 and in function addFee
there will be a check to make sure that the rewards will not exceed the 68.75% of the supply.
VS Code
WETH address might change if deployed in other chains.
ERC20 WETH = ERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
Consider injecting WETH address via the constructor.
VS Code
None.
diff --git a/contracts/core/GolomTrader.sol b/contracts/core/GolomTrader.sol index 70a18ad..85ebe27 100644 --- a/contracts/core/GolomTrader.sol +++ b/contracts/core/GolomTrader.sol @@ -50,14 +50,14 @@ contract GolomTrader is Ownable, ReentrancyGuard { address signer; // maker of order address uint256 orderType; // 0 if selling nft for eth , 1 if offering weth for nft,2 if offering weth for collection with special criteria root uint256 totalAmt; // price value of the trade // total amt maker is willing to give up per unit of amount - Payment exchange; // payment agreed by maker of the order to pay on succesful filling of trade this amt is subtracted from totalamt - Payment prePayment; // another payment , can be used for royalty, facilating trades + Payment exchange; // payment agreed by maker of the order to pay on successful filling of trade this amt is subtracted from totalamt + Payment prePayment; // another payment , can be used for royalty, facilitating trades bool isERC721; // standard of the collection , if 721 then true , if 1155 then false uint256 tokenAmt; // token amt useful if standard is 1155 if >1 means whole order can be filled tokenAmt times uint256 refererrAmt; // amt to pay to the address that helps in filling your order bytes32 root; // A merkle root derived from each valid tokenId β set to 0 to indicate a collection-level or tokenId-specific order. address reservedAddress; // if not address(0) , only this address can fill the order - uint256 nonce; // nonce of order usefull for cancelling in bulk + uint256 nonce; // nonce of order useful for cancelling in bulk uint256 deadline; // timestamp till order is valid epoch timestamp in secs uint8 v; bytes32 r; @@ -198,7 +198,7 @@ contract GolomTrader is Ownable, ReentrancyGuard { /// @param o the Order struct to be filled must be orderType 0 /// @param amount the amount of times the order is to be filled(useful for ERC1155) /// @param referrer referrer of the order - /// @param p any extra payment that the taker of this order wanna send on succesful execution of order + /// @param p any extra payment that the taker of this order wanna send on successful execution of order /// @param receiver address which will receive the NFT function fillAsk( Order calldata o, @@ -275,7 +275,7 @@ contract GolomTrader is Ownable, ReentrancyGuard { /// @param o the Order struct to be filled must be orderType 1 /// @param amount the amount of times the order is to be filled(useful for ERC1155) /// @param referrer referrer of the order - /// @param p any extra payment that the taker of this order wanna send on succesful execution of order + /// @param p any extra payment that the taker of this order wanna send on successful execution of order function fillBid( Order calldata o, uint256 amount, @@ -285,7 +285,7 @@ contract GolomTrader is Ownable, ReentrancyGuard { require( o.totalAmt * amount > (o.exchange.paymentAmt + o.prePayment.paymentAmt + o.refererrAmt) * amount + p.paymentAmt - ); // cause bidder eth is paying for seller payment p , dont take anything extra from seller + ); // cause bidder eth is paying for seller payment p , don't take anything extra from seller // require eth amt is sufficient if (o.reservedAddress != address(0)) { require(msg.sender == o.reservedAddress); @@ -330,7 +330,7 @@ contract GolomTrader is Ownable, ReentrancyGuard { /// @param o the Order struct to be filled must be orderType 2 /// @param amount the amount of times the order is to be filled(useful for ERC1155) /// @param referrer referrer of the order - /// @param p any extra payment that the taker of this order wanna send on succesful execution of order + /// @param p any extra payment that the taker of this order wanna send on successful execution of order function fillCriteriaBid( Order calldata o, uint256 amount, @@ -367,11 +367,11 @@ contract GolomTrader is Ownable, ReentrancyGuard { _settleBalances(o, amount, referrer, p); } - /// @dev function to settle balances when a bid is filled succesfully + /// @dev function to settle balances when a bid is filled successfully /// @param o the Order struct to be filled must be orderType 1 /// @param amount the amount of times the order is to be filled(useful for ERC1155) /// @param referrer referrer of the order - /// @param p any extra payment that the taker of this order wanna send on succesful execution of order + /// @param p any extra payment that the taker of this order wanna send on successful execution of order function _settleBalances( Order calldata o, uint256 amount, diff --git a/contracts/rewards/RewardDistributor.sol b/contracts/rewards/RewardDistributor.sol index 9e31916..9a919e1 100644 --- a/contracts/rewards/RewardDistributor.sol +++ b/contracts/rewards/RewardDistributor.sol @@ -58,10 +58,10 @@ contract RewardDistributor is Ownable { mapping(address => mapping(uint256 => uint256)) public feesTrader; // fees accumulated by address of trader per epoch mapping(address => mapping(uint256 => uint256)) public feesExchange; // fees accumulated by exchange of trader per epoch mapping(uint256 => uint256) public epochTotalFee; // total fee of epoch - mapping(uint256 => uint256) public rewardTrader; // reward minted each epoc for trader - mapping(uint256 => uint256) public rewardExchange; // reward minted each epoc for exhange - mapping(uint256 => uint256) public rewardLP; // reward minted each epoc for LP - mapping(uint256 => uint256) public rewardStaker; // reward minted each epoc for stakers + mapping(uint256 => uint256) public rewardTrader; // reward minted each epoch for trader + mapping(uint256 => uint256) public rewardExchange; // reward minted each epoch for exchange + mapping(uint256 => uint256) public rewardLP; // reward minted each epoch for LP + mapping(uint256 => uint256) public rewardStaker; // reward minted each epoch for stakers mapping(uint256 => uint256) public epochBeginTime; // what time previous epoch ended mapping(uint256 => uint256) public claimedUpto; // epoch upto which tokenid has claimed mapping(uint256 => mapping(uint256 => uint256)) public claimed; // epoch upto which tokenid has claimed @@ -92,23 +92,23 @@ contract RewardDistributor is Ownable { // at starttime epoch 1 starts , first trade changes epoch from 0 to 1 , emits tokens stores the rewards for epoch 1 , // after 1 day , first trade changes epoch from 1 to 2, changes eth in contract to weth and stores rewardstakedeth , emits tokens stores the rewards for epoch 2 - /// @dev Add fees contributed by the Seller of nft and the exchange/frontend that facilated the trade + /// @dev Add fees contributed by the Seller of nft and the exchange/frontend that facilitated the trade /// @param addr the address that contributed in fees /// @param fee the fee contributed by these addresses function addFee(address[2] memory addr, uint256 fee) public onlyTrader { //console.log(block.timestamp,epoch,fee); if (rewardToken.totalSupply() > 1000000000 * 10**18) { - // if supply is greater then a billion dont mint anything, dont add trades + // if supply is greater then a billion don't mint anything, don't add trades return; } // if 24 hours have passed since last epoch change if (block.timestamp > startTime + (epoch) * secsInDay) { - // this assumes atleast 1 trade is done daily?????? + // this assumes at least 1 trade is done daily?????? // logic to decide how much token to emit // emission = daily * (1 - (balance of locker/ total supply)) full if 0 locked and 0 if all locked // uint256 tokenToEmit = dailyEmission * rewardToken.balanceOf()/ - // emissions is decided by epoch begiining locked/circulating , and amount each nft gets also decided at epoch begining + // emissions is decided by epoch beginning locked/circulating , and amount each nft gets also decided at epoch beginning uint256 tokenToEmit = (dailyEmission * (rewardToken.totalSupply() - rewardToken.balanceOf(address(ve)))) / rewardToken.totalSupply(); uint256 stakerReward = (tokenToEmit * rewardToken.balanceOf(address(ve))) / rewardToken.totalSupply(); @@ -151,7 +151,7 @@ contract RewardDistributor is Ownable { rewardToken.transfer(addr, reward); } - // allows exchange that facilated the nft trades to claim there previous epoch rewards + // allows exchange that facilitated the nft trades to claim there previous epoch rewards function exchangeClaim(address addr, uint256[] memory epochs) public { uint256 reward = 0; for (uint256 index = 0; index < epochs.length; index++) { diff --git a/contracts/vote-escrow/VoteEscrowCore.sol b/contracts/vote-escrow/VoteEscrowCore.sol index 097cd35..997fbcb 100644 --- a/contracts/vote-escrow/VoteEscrowCore.sol +++ b/contracts/vote-escrow/VoteEscrowCore.sol @@ -523,7 +523,7 @@ contract VoteEscrowCore is IERC721, IERC721Metadata { } } - /// @dev Exeute transfer of a NFT. + /// @dev Execute transfer of a NFT. /// Throws unless `msg.sender` is the current owner, an authorized operator, or the approved /// address for this NFT. (NOTE: `msg.sender` not allowed in internal function so pass `_sender`.) /// Throws if `_to` is the zero address. @@ -685,7 +685,7 @@ contract VoteEscrowCore is IERC721, IERC721Metadata { /// @notice Record global and per-user data to checkpoint /// @param _tokenId NFT token ID. No user checkpoint if 0 - /// @param old_locked Pevious locked amount / end lock time for the user + /// @param old_locked Previous locked amount / end lock time for the user /// @param new_locked New locked amount / end lock time for the user function _checkpoint( uint256 _tokenId, diff --git a/contracts/vote-escrow/VoteEscrowDelegation.sol b/contracts/vote-escrow/VoteEscrowDelegation.sol index e68e8aa..053db7b 100644 --- a/contracts/vote-escrow/VoteEscrowDelegation.sol +++ b/contracts/vote-escrow/VoteEscrowDelegation.sol @@ -224,7 +224,7 @@ contract VoteEscrow is VoteEscrowCore, Ownable { // _writeCheckpoint(ownerTokenId, nCheckpoints, checkpoint.delegatedTokenIds); // } - /// @dev Exeute transfer of a NFT. + /// @dev Execute transfer of a NFT. /// Throws unless `msg.sender` is the current owner, an authorized operator, or the approved /// address for this NFT. (NOTE: `msg.sender` not allowed in internal function so pass `_sender`.) /// Throws if `_to` is the zero address.
VS Code
2022-07-golom/contracts/vote-escrow/TokenUriHelper.sol
Choose the appropriate license from the list.
VS Code
NatSpec is incomplete.
function hashPayment(Payment calldata p) private pure returns (bytes32) {
function _hashOrder(Order calldata o) private pure returns (bytes32) {
function _hashOrderinternal(Order calldata o, uint256[2] memory extra) private pure returns (bytes32) {
function payEther(uint256 payAmt, address payAddress) internal {
function cancelOrder(Order calldata o) public nonReentrant {
function fillCriteriaBid(
function traderClaim(address addr, uint256[] memory epochs) public {
function exchangeClaim(address addr, uint256[] memory epochs) public {
function encode(bytes memory data) internal pure returns (string memory) {
function _tokenURI(
function toString(uint256 value) internal pure returns (string memory) {
function _writeCheckpoint(
function _transferFrom(
Add in full NatSpec comments for all functions to have complete code documentation.
VS Code
π Selected for report: JohnSmith
Also found by: 0x1f8b, 0xA5DF, 0xDjango, 0xKitsune, 0xLovesleep, 0xNazgul, 0xSmartContract, 0xmatt, 0xsam, Aymen0909, Bnke0x0, CRYP70, Chandr, Chinmay, CodingNameKiki, Deivitto, Dravee, ElKu, Fitraldys, Funen, GalloDaSballo, Green, IllIllI, JC, Jmaxmanblue, Junnon, Kaiziron, Kenshin, Krow10, Maxime, Migue, MiloTruck, Noah3o6, NoamYakov, Randyyy, RedOneN, ReyAdmirado, Rohan16, Rolezn, Ruhum, Sm4rty, StyxRave, TomJ, Tomio, _Adam, __141345__, ajtra, ak1, apostle0x01, asutorufos, async, benbaessler, brgltd, c3phas, cRat1st0s, carlitox477, delfin454000, djxploit, durianSausage, ellahi, erictee, fatherOfBlocks, gerdusx, gogo, hyh, jayfromthe13th, jayphbee, joestakey, kaden, kenzo, kyteg, ladboy233, lucacez, m_Rassska, mics, minhquanym, oyc_109, pfapostol, rbserver, reassor, rfa, robee, rokinot, sach1r0, saian, samruna, sashik_eth, simon135, supernova, tofunmi, zuhaibmohd
21.3211 USDC - $21.32
!= 0
rather than > 0
for unsigned integers in require()
statements
&&
File Name | SHA-1 Hash |
---|---|
2022-07-golom/contracts/vote-escrow/TokenUriHelper.sol | 4cb68851036989e92b4525acfcede317a03c5385 |
2022-07-golom/contracts/vote-escrow/VoteEscrowDelegation.sol | a099c4020b083e43def93cf4196032703761c0af |
2022-07-golom/contracts/vote-escrow/VoteEscrowCore.sol | 8d325a79916b4d01184657f9f875d198287d39be |
2022-07-golom/contracts/rewards/RewardDistributor.sol | 9ed9a8baa18b6f67a2555bf2287968a9cdeb62fb |
2022-07-golom/contracts/governance/GolomToken.sol | f83fbdb554fd1d0f3d970f7c84bc5de5dfc7f5d2 |
2022-07-golom/contracts/core/GolomTrader.sol | 2d1f78c663242498479552a54ec9f20aab459db9 |
If a variable is not set/initialized, it is assumed to have the default value (0
, false
, 0x0
, etc depending on the data type). If you explicitly initialize it with its default value, you are just wasting gas.
uint256 public epoch = 0;
uint256 reward = 0;
uint256 reward = 0;
uint256 reward = 0;
uint256 rewardEth = 0
uint256 reward = 0;
uint256 rewardEth = 0;
uint256 reward = 0;
uint256 reward = 0;
uint256 block_slope = 0; // dblock/dt
uint256 _min = 0;
uint256 _min = 0;
uint256 d_block = 0;
uint256 d_t = 0;
uint256 dt = 0;
uint256 public MIN_VOTING_POWER_REQUIRED = 0;
uint256 lower = 0;
uint256 votes = 0;
uint256 votes = 0;
Deployments | - | - | - | % of limit | - |
---|---|---|---|---|---|
ERC1155Mock | - | - | 1482781 | 0 % | - |
ERC20Mock | - | - | 496811 | 0 % | - |
ERC721Mock | - | - | 1210377 | 0 % | - |
GolomToken | - | - | 1998674 | 0 % | - |
GolomTrader | - | - | 2013842 | 0 % | - |
RewardDistributor | 2377569 | 2377605 | 2377599 | 0 % | - |
WETH | - | - | 572761 | 0 % | - |
Do not initialize variables with default values.
VS Code
Pre-increments cost less gas compared to post-increments.
for (uint256 i = 0; i < proof.length; i++) {
for (uint256 index = 0; index < epochs.length; index++) {
for (uint256 index = 0; index < epochs.length; index++) {
for (uint256 tindex = 0; tindex < tokenids.length; tindex++) {
for (uint256 index = 0; index < epochs.length; index++) {
for (uint256 index = 0; index < epoch; index++) {
for (uint256 index = 0; index < epoch; index++) {
for (uint256 index = 0; index < epoch; index++) {
digits++;
for (uint256 index = 0; index < delegated.length; index++) {
for (uint256 index = 0; index < delegatednft.length; index++) {
for (uint256 i; i < _array.length; i++) {
Contract | Method | Min | Max | Avg | # calls | eur (avg) |
---|---|---|---|---|---|---|
ERC1155Mock | setApprovalForAll | 46213 | 46225 | 46223 | 20 | - |
ERC721Mock | mint | - | - | 90772 | 20 | - |
ERC721Mock | setApprovalForAll | 46168 | 46180 | 46178 | 20 | - |
GolomToken | executeSetMinter | - | - | 47798 | 9 | - |
GolomToken | mint | - | - | 120177 | 1 | - |
GolomToken | mintAirdrop | - | - | 142282 | 2 | - |
GolomToken | mintGenesisReward | - | - | 142260 | 8 | - |
GolomToken | setMinter | 34134 | 68334 | 54652 | 10 | - |
GolomTrader | fillAsk | 238117 | 241948 | 241392 | 7 | - |
GolomTrader | setDistributor | 46281 | 70449 | 47432 | 21 | - |
RewardDistributor | addVoteEscrow | - | - | 28462 | 6 | - |
RewardDistributor | executeAddVoteEscrow | - | - | 29954 | 6 | - |
Deployments | - | - | - | % of limit | - |
---|---|---|---|---|---|
ERC1155Mock | - | - | 1482781 | 0 % | - |
ERC20Mock | - | - | 496811 | 0 % | - |
ERC721Mock | - | - | 1210377 | 0 % | - |
GolomToken | - | - | 1998674 | 0 % | - |
GolomTrader | - | - | 2013338 | 0 % | - |
RewardDistributor | 2376483 | 2376519 | 2376513 | 0 % | - |
WETH | - | - | 572761 | 0 % | - |
Change i++
to ++i
.
VS Code
In Solidity 0.8+, thereβs a default overflow check on unsigned integers.
for (uint256 i = 0; i < proof.length; i++) {
for (uint256 index = 0; index < epochs.length; index++) {
for (uint256 index = 0; index < epochs.length; index++) {
for (uint256 tindex = 0; tindex < tokenids.length; tindex++) {
for (uint256 index = 0; index < epochs.length; index++) {
for (uint256 index = 0; index < epoch; index++) {
for (uint256 index = 0; index < epoch; index++) {
for (uint256 index = 0; index < epoch; index++) {
digits++;
for (uint256 index = 0; index < delegated.length; index++) {
for (uint256 index = 0; index < delegatednft.length; index++) {
for (uint256 i; i < _array.length; i++) {
Contract | Method | Min | Max | Avg | # calls | eur (avg) |
---|---|---|---|---|---|---|
ERC1155Mock | setApprovalForAll | 46213 | 46225 | 46223 | 20 | - |
ERC721Mock | mint | - | - | 90772 | 20 | - |
ERC721Mock | setApprovalForAll | 46168 | 46180 | 46178 | 20 | - |
GolomToken | executeSetMinter | - | - | 47798 | 9 | - |
GolomToken | mint | - | - | 120177 | 1 | - |
GolomToken | mintAirdrop | - | - | 142282 | 2 | - |
GolomToken | mintGenesisReward | - | - | 142260 | 8 | - |
GolomToken | setMinter | 34134 | 68334 | 54652 | 10 | - |
GolomTrader | fillAsk | 238153 | 241948 | 241401 | 7 | - |
GolomTrader | setDistributor | 46281 | 70449 | 47432 | 21 | - |
RewardDistributor | addVoteEscrow | - | - | 28462 | 6 | - |
RewardDistributor | executeAddVoteEscrow | - | - | 29954 | 6 | - |
Deployments | - | - | - | % of limit | - |
---|---|---|---|---|---|
ERC1155Mock | - | - | 1482781 | 0 % | - |
ERC20Mock | - | - | 496811 | 0 % | - |
ERC721Mock | - | - | 1210377 | 0 % | - |
GolomToken | - | - | 1998674 | 0 % | - |
GolomTrader | - | - | 2011682 | 0 % | - |
RewardDistributor | 2359626 | 2359662 | 2359656 | 0 % | - |
WETH | - | - | 572761 | 0 % | - |
One example is the code would go from:
for (uint256 i = 0; i < proof.length; i++) { bytes32 proofElement = proof[i]; if (computedHash <= proofElement) { // Hash(current computed hash + current element of the proof) computedHash = _efficientHash(computedHash, proofElement); } else { // Hash(current element of the proof + current computed hash) computedHash = _efficientHash(proofElement, computedHash); } } if (computedHash != root) { revert('invalid proof'); }
to:
for (uint256 i = 0; i < proof.length;) { bytes32 proofElement = proof[i]; if (computedHash <= proofElement) { // Hash(current computed hash + current element of the proof) computedHash = _efficientHash(computedHash, proofElement); } else { // Hash(current element of the proof + current computed hash) computedHash = _efficientHash(proofElement, computedHash); } unchecked { ++i; } }
VS Code
If a variable is not set/initialized, it is assumed to have the default value (0
, false
, 0x0
, etc depending on the data type). If you explicitly initialize it with its default value, you are just wasting gas.
for (uint256 i = 0; i < proof.length; i++) {
for (uint256 index = 0; index < epochs.length; index++) {
for (uint256 index = 0; index < epochs.length; index++) {
for (uint256 tindex = 0; tindex < tokenids.length; tindex++) {
for (uint256 index = 0; index < epochs.length; index++) {
for (uint256 index = 0; index < epoch; index++) {
for (uint256 index = 0; index < epoch; index++) {
for (uint256 index = 0; index < epoch; index++) {
for (uint256 i = 0; i < 255; ++i) {
for (uint256 i = 0; i < 128; ++i) {
for (uint256 i = 0; i < 128; ++i) {
for (uint256 i = 0; i < 255; ++i) {
for (uint256 index = 0; index < delegated.length; index++) {
for (uint256 index = 0; index < delegatednft.length; index++) {
Do not initialize variables with default values.
VS Code
!= 0
rather than > 0
for unsigned integers in require()
statementsWhen the optimizer is enabled, gas is wasted by doing a greater-than operation, rather than a not-equals operation inside require()
statements. When using !=,
the optimizer is able to avoid the EQ
, ISZERO
, and associated operations, by relying on the JUMPI
that comes afterwards, which itself checks for zero.
require(_value > 0); // dev: need non-zero value
require(_locked.amount > 0, 'No existing lock found');
require(_value > 0); // dev: need non-zero value
require(_locked.amount > 0, 'No existing lock found');
require(_locked.amount > 0, 'Nothing is locked');
Use != 0
rather than > 0
for unsigned integers in require()
statements.
VS Code
&&
Using multiple require statements can save gas.
require(attachments[_tokenId] == 0 && !voted[_tokenId], 'attached');
require(attachments[_from] == 0 && !voted[_from], 'attached');
require(attachments[_tokenId] == 0 && !voted[_tokenId], 'attached');
Use multiple require statements.
VS Code
Less expensive and able to use dynamic information in them.
Use custom errors.
VS Code