GoGoPool contest - IllIllI's results

Liquid staking for Avalanche.

General Information

Platform: Code4rena

Start Date: 15/12/2022

Pot Size: $128,000 USDC

Total HM: 28

Participants: 111

Period: 19 days

Judge: GalloDaSballo

Total Solo HM: 1

Id: 194

League: ETH

GoGoPool

Findings Distribution

Researcher Performance

Rank: 8/111

Findings: 6

Award: $2,375.27

QA:
grade-a
Gas:
grade-a

🌟 Selected for report: 1

šŸš€ Solo Findings: 0

Awards

4.9672 USDC - $4.97

Labels

bug
3 (High Risk)
partial-50
duplicate-213

External Links

Lines of code

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/MinipoolManager.sol#L243-L244

Vulnerability details

When the state is MinipoolStatus.Withdrawable, the recreateMinipool() function expects that it can fetch the data from the old minipool, and re-create the pool using the existing funds, before changing the state to MinipoolStatus.Prelaunch. The same fetching is not done in createMinipool(), where it only clears all of the old data before changing the state.

Impact

Funds not withdrawn using withdrawMinipoolFunds() will be lost

Proof of Concept

If the pool already existed, the state transition allows the minipool data to be reset, and then overwritten with msg.value:

// File: contracts/contract/MinipoolManager.sol : MinipoolManager.createMinipool()   #1

238    		// Create or update a minipool record for nodeID
239    		// If nodeID exists, only allow overwriting if node is finished or canceled
240    		// 		(completed its validation period and all rewards paid and processing is complete)
241    		int256 minipoolIndex = getIndexOf(nodeID);
242    		if (minipoolIndex != -1) {
243 @> 			requireValidStateTransition(minipoolIndex, MinipoolStatus.Prelaunch);
244    			resetMinipoolData(minipoolIndex);
...
261    		setUint(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".avaxNodeOpInitialAmt")), msg.value);
262:   		setUint(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".avaxNodeOpAmt")), msg.value);

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/MinipoolManager.sol#L238-L262

The state transition is allowed from MinipoolStatus.Withdrawable to MinipoolStatus.Prelaunch:

// File: contracts/contract/MinipoolManager.sol : MinipoolManager.requireValidStateTransition()   #2

163    		} else if (currentStatus == MinipoolStatus.Withdrawable || currentStatus == MinipoolStatus.Error) {
164:@> 			isValid = (to == MinipoolStatus.Finished || to == MinipoolStatus.Prelaunch);

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/MinipoolManager.sol#L154-L174

Tools Used

Code inspection

Use recreateMinipool() if the pool is found to already exist

#0 - 0xminty

2023-01-03T23:33:08Z

dupe of #805

#1 - c4-judge

2023-01-10T10:30:43Z

GalloDaSballo marked the issue as duplicate of #213

#2 - c4-judge

2023-02-03T12:33:01Z

GalloDaSballo changed the severity to 2 (Med Risk)

#3 - c4-judge

2023-02-03T19:26:10Z

GalloDaSballo changed the severity to 3 (High Risk)

#4 - c4-judge

2023-02-08T08:26:45Z

GalloDaSballo changed the severity to 2 (Med Risk)

#5 - c4-judge

2023-02-08T08:50:12Z

GalloDaSballo changed the severity to 3 (High Risk)

#6 - c4-judge

2023-02-08T20:23:04Z

GalloDaSballo marked the issue as satisfactory

#7 - c4-judge

2023-02-09T08:22:32Z

GalloDaSballo marked the issue as partial-50

#8 - GalloDaSballo

2023-02-09T08:22:41Z

Valid report but didn't show how this can be as an attack so awarding half

#9 - c4-judge

2023-02-09T08:53:06Z

GalloDaSballo changed the severity to QA (Quality Assurance)

#10 - Simon-Busch

2023-02-09T12:49:56Z

Changed severity back from QA to H as requested by @GalloDaSballo

Awards

14.9051 USDC - $14.91

Labels

bug
3 (High Risk)
satisfactory
duplicate-209

External Links

Lines of code

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/TokenggAVAX.sol#L99

Vulnerability details

An early depositor can break the future minting of shares by minting a very small number of shares, but donating a large amount of AVAX during the reward period, such that each wei of ggAVAX is worth a large amount.

Impact

If one wei of ggAVAX is worth, say, 1 Eth of AVAX, if a user calls deposit() with a value less than this amount, the amount the number of shares they get back round down to zero due to loss of precision and the call will revert, essentially bricking the contract for users with small amounts. If a user instead deposits an amount larger than 1 Eth, the amount over 1 Eth is given to all shareholders due to loss of precision.

Proof of Concept

An attacker can deposit 1 wei of AVAX, then transfer a large amount of AVAX to the TokenggAVAX contract via a selfdestruct(), then call syncRewards(). That function will convert the balance of the contract to lastRewardsAmt...:

// File: contracts/contract/tokens/TokenggAVAX.sol : TokenggAVAX.syncRewards()   #1
99  @> 		uint256 nextRewardsAmt = (asset.balanceOf(address(this)) + stakingTotalAssets_) - totalReleasedAssets_ - lastRewardsAmt_;
100    
101    		// Ensure nextRewardsCycleEnd will be evenly divisible by `rewardsCycleLength`.
102    		uint32 nextRewardsCycleEnd = ((timestamp + rewardsCycleLength) / rewardsCycleLength) * rewardsCycleLength;
103    
104:@> 		lastRewardsAmt = nextRewardsAmt.safeCastTo192();

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/TokenggAVAX.sol#L99-L104

...which is used in totalAssets() either in the next period, or proportionally in the current period, depending on how much time has elapsed...:

// File: contracts/contract/tokens/TokenggAVAX.sol : TokenggAVAX.totalAssets()   #2

120    		if (block.timestamp >= rewardsCycleEnd_) {
121    			// no rewards or rewards are fully unlocked
122    			// entire reward amount is available
123 @> 			return totalReleasedAssets_ + lastRewardsAmt_;
124    		}
125    
126    		// rewards are not fully unlocked
127    		// return unlocked rewards and stored total
128 @> 		uint256 unlockedRewards = (lastRewardsAmt_ * (block.timestamp - lastSync_)) / (rewardsCycleEnd_ - lastSync_);
129    		return totalReleasedAssets_ + unlockedRewards;
130:   	}

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/TokenggAVAX.sol#L120-L130

...and that total is used to determine the worth of every share during minting:

// File: contracts/contract/tokens/upgradeable/ERC4626Upgradeable.sol : ERC4626Upgradeable.convertToShares()   #3

120    	function convertToShares(uint256 assets) public view virtual returns (uint256) {
121    		uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.
122    
123 @> 		return supply == 0 ? assets : assets.mulDivDown(supply, totalAssets());
124:   	}

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/upgradeable/ERC4626Upgradeable.sol#L120-L124

Tools Used

Code inspection

Upon initialization, mint an initial amount of shares to an address that is not able to withdraw them

#0 - c4-judge

2023-01-08T13:11:30Z

GalloDaSballo marked the issue as duplicate of #209

#1 - c4-judge

2023-02-08T09:44:13Z

GalloDaSballo marked the issue as satisfactory

Findings Information

Awards

40.8808 USDC - $40.88

Labels

bug
2 (Med Risk)
satisfactory
duplicate-478

External Links

Lines of code

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/TokenggAVAX.sol#L113-L124

Vulnerability details

Rewards can be deposited to the TokenggAVAX contract, but aren't used in share valuation until syncRewards() is called.

Impact

If a user sees that rewards have been deposited to ggAVAX and decides to withdraw, their withdrawal will not include the reward if it hasn't yet been synced, leading to the user missing out on rewards they should have gotten.

Proof of Concept

totalAssets() is used by ERC4626 to calculate the value of shares. Once a cycle has ended, it will include any previously-synced rewards (lastRewardsAmt), but will not include any rewards that have been deposited since then, e.g. due to an unexpected airdrop:

// File: contracts/contract/tokens/TokenggAVAX.sol : TokenggAVAX.totalAssets()   #1

113    	function totalAssets() public view override returns (uint256) {
114    		// cache global vars
115    		uint256 totalReleasedAssets_ = totalReleasedAssets;
116    		uint192 lastRewardsAmt_ = lastRewardsAmt;
117    		uint32 rewardsCycleEnd_ = rewardsCycleEnd;
118    		uint32 lastSync_ = lastSync;
119    
120    		if (block.timestamp >= rewardsCycleEnd_) {
121    			// no rewards or rewards are fully unlocked
122    			// entire reward amount is available
123 @> 			return totalReleasedAssets_ + lastRewardsAmt_;
124:   		}

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/TokenggAVAX.sol#L113-L124

lastRewardsAmt only gets updated when things are synced:

// File: contracts/contract/tokens/TokenggAVAX.sol : TokenggAVAX.syncRewards()   #2

99  @> 		uint256 nextRewardsAmt = (asset.balanceOf(address(this)) + stakingTotalAssets_) - totalReleasedAssets_ - lastRewardsAmt_;
100    
101    		// Ensure nextRewardsCycleEnd will be evenly divisible by `rewardsCycleLength`.
102    		uint32 nextRewardsCycleEnd = ((timestamp + rewardsCycleLength) / rewardsCycleLength) * rewardsCycleLength;
103    
104:   		lastRewardsAmt = nextRewardsAmt.safeCastTo192();

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/TokenggAVAX.sol#L99-L104

Tools Used

Code inspection

In beforeWithdraw(), call syncRewards() if the contract balance is larger than the total stored amounts

#0 - c4-judge

2023-01-10T09:59:16Z

GalloDaSballo marked the issue as duplicate of #478

#1 - c4-judge

2023-02-08T20:11:44Z

GalloDaSballo marked the issue as satisfactory

Findings Information

Awards

40.8808 USDC - $40.88

Labels

bug
2 (Med Risk)
satisfactory
duplicate-478

External Links

Lines of code

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/TokenggAVAX.sol#L99-L109

Vulnerability details

Reward cycles can start at any time after the end of the cycle, but always end at an exact multiple of the interval.

Impact

An attacker can time the syncing of unsynced rewards such that they only need a large AVAX loan for a single block, in order to capture most of the rewards.

Proof of Concept

The end of a cycle is always a multiple of the cycle length, even if that occurs only one block in the future:

// File: contracts/contract/tokens/TokenggAVAX.sol : TokenggAVAX.syncRewards()   #1

99     		uint256 nextRewardsAmt = (asset.balanceOf(address(this)) + stakingTotalAssets_) - totalReleasedAssets_ - lastRewardsAmt_;
100    
101    		// Ensure nextRewardsCycleEnd will be evenly divisible by `rewardsCycleLength`.
102 @> 		uint32 nextRewardsCycleEnd = ((timestamp + rewardsCycleLength) / rewardsCycleLength) * rewardsCycleLength;
103    
104    		lastRewardsAmt = nextRewardsAmt.safeCastTo192();
105    		lastSync = timestamp;
106    		rewardsCycleEnd = nextRewardsCycleEnd;
107    		totalReleasedAssets = totalReleasedAssets_ + lastRewardsAmt_;
108    		emit NewRewardsCycle(nextRewardsCycleEnd, nextRewardsAmt);
109:   	}

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/TokenggAVAX.sol#L99-L109

An attacker can sync one block before the end of the cycle and take out a very large AVAX loan, mint ggAVAX with it, wait one block, then redeem the shares and repay the loan. The value of the shares will have grown significantly since the totalAssets() will include the full value of unlockedRewards, since the next cycle will have ended after that one block:

// File: contracts/contract/tokens/TokenggAVAX.sol : TokenggAVAX.totalAssets()   #2

126    		// rewards are not fully unlocked
127    		// return unlocked rewards and stored total
128 @> 		uint256 unlockedRewards = (lastRewardsAmt_ * (block.timestamp - lastSync_)) / (rewardsCycleEnd_ - lastSync_);
129    		return totalReleasedAssets_ + unlockedRewards;
130:   	}

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/TokenggAVAX.sol#L126-L130

As long as the rewards are larger than the one-block loan interest, it is worth it for the attack to take place.

Tools Used

Code inspection

Don't allow syncing if the time remaining of the cycle is less than some threshold

#0 - c4-judge

2023-01-10T17:47:35Z

GalloDaSballo marked the issue as duplicate of #478

#1 - c4-judge

2023-02-08T20:11:42Z

GalloDaSballo marked the issue as satisfactory

Findings Information

🌟 Selected for report: 0xbepresent

Also found by: 0xdeadbeef0x, Breeje, Franfran, IllIllI, Matin, Rolezn, SmartSek, btk, ck, datapunk, fs0c, nadin, rvierdiiev, unforgiven

Awards

57.1995 USDC - $57.20

Labels

bug
2 (Med Risk)
satisfactory
duplicate-317

External Links

Lines of code

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/TokenggAVAX.sol#L245

Vulnerability details

totalAssets() is used to calculate the value of shares when doing a withdrawal/redemption, but includes in its value, rewards that are not withdrawable.

Impact

The user won't be able to withdraw their full amount of assets, but if another user deposits the portion of totalAssets() that makes up the reward portion, the first user will be able to withdraw, and the second user will be locked in their position until the rewards are distributed during the next sync. This is possible any time there are only a couple of holders, or when there is a whale that has a very large position in relation to the other holders of ggAVAX.

Proof of Concept

totalAssets() includes "unlocked" rewards on top of the totalReleasedAssets:

// File: contracts/contract/tokens/TokenggAVAX.sol : TokenggAVAX.totalAssets()   #1

126    		// rewards are not fully unlocked
127    		// return unlocked rewards and stored total
128 @> 		uint256 unlockedRewards = (lastRewardsAmt_ * (block.timestamp - lastSync_)) / (rewardsCycleEnd_ - lastSync_);
129    		return totalReleasedAssets_ + unlockedRewards;
130:   	}

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/TokenggAVAX.sol#L126-L130

But the beforeWithdraw() hook will revert if the full total is withdrawn unless it has been topped off by another user:

// File: contracts/contract/tokens/TokenggAVAX.sol : TokenggAVAX.beforeWithdraw()   #2

241    	function beforeWithdraw(
242    		uint256 amount,
243    		uint256 /* shares */
244    	) internal override {
245 @> 		totalReleasedAssets -= amount;
246:   	}

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/TokenggAVAX.sol#L241-L246

The other user won't be able to withdraw until the rewards are synced at the end of the window:

// File: contracts/contract/tokens/TokenggAVAX.sol : TokenggAVAX.syncRewards()   #3

106    		rewardsCycleEnd = nextRewardsCycleEnd;
107:@> 		totalReleasedAssets = totalReleasedAssets_ + lastRewardsAmt_;

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/TokenggAVAX.sol#L106-L107

Tools Used

Code inspection

Update the totalReleasedAssets with the unlocked portion of rewards, if someone does a withdrawal

#0 - c4-judge

2023-01-08T17:01:26Z

GalloDaSballo marked the issue as duplicate of #317

#1 - c4-judge

2023-02-08T10:06:40Z

GalloDaSballo marked the issue as satisfactory

Labels

bug
grade-a
QA (Quality Assurance)
selected for report
edited-by-warden
Q-05

Awards

1538.3717 USDC - $1,538.37

External Links

Summary

Low Risk Issues

IssueInstances
[L‑01]Inflation not locked for four years1
[L‑02]Contract will stop functioning in the year 21061
[L‑03]Lower-level initializations should come first1
[L‑04]Cycle end may be be too soon1
[L‑05]Incorrect percentage conversion1
[L‑06]Missing checks for value of msg.value1
[L‑07]Loss of precision2
[L‑08]Signatures vulnerable to malleability attacks1
[L‑09]require() should be used instead of assert()1
[L‑10]Open TODOs1

Total: 11 instances over 10 issues

Non-critical Issues

IssueInstances
[N‑01]Common code should be refactored1
[N‑02]String constants used in multiple places should be defined as constants1
[N‑03]Constants in comparisons should appear on the left side1
[N‑04]Inconsistent address separator in storage names1
[N‑05]Confusing function name1
[N‑06]Misplaced punctuation1
[N‑07]Upgradeable contract is missing a __gap[50] storage variable to allow for new storage variables in later versions1
[N‑08]Import declarations should import specific identifiers, rather than the whole file13
[N‑09]Missing initializer modifier on constructor1
[N‑10]The nonReentrant modifier should occur before all other modifiers2
[N‑11]override function arguments that are unused should have the variable name removed or commented out to avoid compiler warnings1
[N‑12]constants should be defined rather than using magic numbers2
[N‑13]Missing event and or timelock for critical parameter change1
[N‑14]Events that mark critical parameter changes should contain both the old and the new value2
[N‑15]Use a more recent version of solidity1
[N‑16]Use a more recent version of solidity1
[N‑17]Constant redefined elsewhere2
[N‑18]Lines are too long1
[N‑19]Variable names that consist of all capital letters should be reserved for constant/immutable variables2
[N‑20]Using >/>= without specifying an upper bound is unsafe2
[N‑21]Typos3
[N‑22]File is missing NatSpec3
[N‑23]NatSpec is incomplete27
[N‑24]Not using the named return variables anywhere in the function is confusing1
[N‑25]Contracts should have full test coverage1
[N‑26]Large or complicated code bases should implement fuzzing tests1
[N‑27]Function ordering does not follow the Solidity style guide15
[N‑28]Contract does not follow the Solidity style guide's suggested layout ordering9

Total: 98 instances over 28 issues

Low Risk Issues

[L‑01] Inflation not locked for four years

The litepaper says that there will be no inflation for four years, but there is no code enforcing this

There is 1 instance of this issue:

File: /contracts/contract/ProtocolDAO.sol

39   		// GGP Inflation
40   		setUint(keccak256("ProtocolDAO.InflationIntervalSeconds"), 1 days);
41:  		setUint(keccak256("ProtocolDAO.InflationIntervalRate"), 1000133680617113500); // 5% annual calculated on a daily interval - Calculate in js example: let dailyInflation = web3.utils.toBN((1 + 0.05) ** (1 / (365)) * 1e18);

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/ProtocolDAO.sol#L39-L41

[L‑02] Contract will stop functioning in the year 2106

limiting the timestamp to fit in a uint32 will cause the call below to start reverting in 2106

There is 1 instance of this issue:

File: /contracts/contract/tokens/TokenggAVAX.sol

89:  		uint32 timestamp = block.timestamp.safeCastTo32();

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/TokenggAVAX.sol#L89

[L‑03] Lower-level initializations should come first

There may not be an issue now, but if ERC4626 changes to rely on some of the functions BaseUpgradeable provides, things will break

There is 1 instance of this issue:

File: /contracts/contract/tokens/TokenggAVAX.sol

72   	function initialize(Storage storageAddress, ERC20 asset) public initializer {
73   		__ERC4626Upgradeable_init(asset, "GoGoPool Liquid Staking Token", "ggAVAX");
74:  		__BaseUpgradeable_init(storageAddress);

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/TokenggAVAX.sol#L72-L74

[L‑04] Cycle end may be be too soon

The first timestamp should be based on when things are first synced, so that an appropriate duration of the cycle occurs, rather than during deployment

There is 1 instance of this issue:

File: /contracts/contract/tokens/TokenggAVAX.sol

78:  		rewardsCycleEnd = (block.timestamp.safeCastTo32() / rewardsCycleLength) * rewardsCycleLength;

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/TokenggAVAX.sol#L78

[L‑05] Incorrect percentage conversion

0.2 ether should be 20%, not 2%. Other areas use 0.X as X0%

There is 1 instance of this issue:

File: /contracts/contract/MinipoolManager.sol

194: 	/// @param delegationFee Percentage delegation fee in units of ether (2% is 0.2 ether)

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/MinipoolManager.sol#L194

[L‑06] Missing checks for value of msg.value

Other functions in this contract such as recordStakingError() ensure that the correct value is passed, but recordStakingEnd() does not

There is 1 instance of this issue:

File: /contracts/contract/MinipoolManager.sol

385  	function recordStakingEnd(
386  		address nodeID,
387  		uint256 endTime,
388  		uint256 avaxTotalRewardAmt
389: 	) external payable {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/MinipoolManager.sol#L385-L389

[L‑07] Loss of precision

Division by large numbers may result in the result being zero, due to solidity not supporting fractions. Consider requiring a minimum amount for the numerator to ensure that it is always larger than the denominator

There are 2 instances of this issue:

File: contracts/contract/RewardsPool.sol

60:   		return (block.timestamp - startTime) / dao.getInflationIntervalSeconds();

128:  		return (block.timestamp - startTime) / dao.getRewardsCycleSeconds();

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/RewardsPool.sol#L60

[L‑08] Signatures vulnerable to malleability attacks

ecrecover() accepts as valid, two versions of signatures, meaning an attacker can use the same signature twice. Consider adding checks for signature malleability, or using OpenZeppelin's ECDSA library to perform the extra checks necessary in order to prevent this attack.

There is 1 instance of this issue:

File: contracts/contract/tokens/upgradeable/ERC20Upgradeable.sol

132   			address recoveredAddress = ecrecover(
133   				keccak256(
134   					abi.encodePacked(
135   						"\x19\x01",
136   						DOMAIN_SEPARATOR(),
137   						keccak256(
138   							abi.encode(
139   								keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"),
140   								owner,
141   								spender,
142   								value,
143   								nonces[owner]++,
144   								deadline
145   							)
146   						)
147   					)
148   				),
149   				v,
150   				r,
151   				s
152:  			);

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/upgradeable/ERC20Upgradeable.sol#L132-L152

[L‑09] require() should be used instead of assert()

Prior to solidity version 0.8.0, hitting an assert consumes the remainder of the transaction's available gas rather than returning it, as require()/revert() do. assert() should be avoided even past solidity version 0.8.0 as its documentation states that "The assert function creates an error of type Panic(uint256). ... Properly functioning code should never create a Panic, not even on invalid external input. If this happens, then there is a bug in your contract which you should fix".

There is 1 instance of this issue:

File: contracts/contract/tokens/TokenggAVAX.sol

83:   		assert(msg.sender == address(asset));

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/TokenggAVAX.sol#L83

[L‑10] Open TODOs

Code architecture, incentives, and error handling/reporting questions/issues should be resolved before deployment

There is 1 instance of this issue:

File: contracts/contract/MinipoolManager.sol

412:  		// TODO Revisit this logic if we ever allow unequal matched funds

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/MinipoolManager.sol#L412

Non-critical Issues

[N‑01] Common code should be refactored

BaseAbstract performs similar operations, so the common code should be refactored to a function

There is 1 instance of this issue:

File: /contracts/contract/MultisigManager.sol

110: 		addr = getAddress(keccak256(abi.encodePacked("multisig.item", index, ".address")));

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/MultisigManager.sol#L110

[N‑02] String constants used in multiple places should be defined as constants

There is 1 instance of this issue:

File: /contracts/contract/MultisigManager.sol

110: 		addr = getAddress(keccak256(abi.encodePacked("multisig.item", index, ".address")));

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/MultisigManager.sol#L110

[N‑03] Constants in comparisons should appear on the left side

Doing so will prevent typo bugs

There is 1 instance of this issue:

File: /contracts/contract/ClaimNodeOp.sol

92:  		if (ggpRewards == 0) {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/ClaimNodeOp.sol#L92

[N‑04] Inconsistent address separator in storage names

Most addresses in storage names don't separate the prefix from the address with a period, but this one has one

There is 1 instance of this issue:

File: /contracts/contract/ProtocolDAO.sol

102  	function getClaimingContractPct(string memory claimingContract) public view returns (uint256) {
103  		return getUint(keccak256(abi.encodePacked("ProtocolDAO.ClaimingContractPct.", claimingContract)));
104  	}
105  
106  	/// @notice Set the percentage a contract is owed for a rewards cycle
107  	function setClaimingContractPct(string memory claimingContract, uint256 decimal) public onlyGuardian valueNotGreaterThanOne(decimal) {
108  		setUint(keccak256(abi.encodePacked("ProtocolDAO.ClaimingContractPct.", claimingContract)), decimal);
109: 	}

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/ProtocolDAO.sol#L102-L109

[N‑05] Confusing function name

Consider changing the name to stakeGGPAs or stakeGGPFor

There is 1 instance of this issue:

File: /contracts/contract/Staking.sol

328: 	function restakeGGP(address stakerAddr, uint256 amount) public onlySpecificRegisteredContract("ClaimNodeOp", msg.sender) {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Staking.sol#L328

[N‑06] Misplaced punctuation

There's an extra comma - it looks like a find-and-replace error

There is 1 instance of this issue:

File: /contracts/contract/Vault.sol

154  		// Update balances
155  		tokenBalances[contractKey] = tokenBalances[contractKey] - amount;
156  		// Get the toke ERC20 instance
157  		ERC20 tokenContract = ERC20(tokenAddress);
158  		// Withdraw to the withdrawal address, , safeTransfer will revert if it fails
159  		tokenContract.safeTransfer(withdrawalAddress, amount);
160: 	}

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Vault.sol#L154-L160

[N‑07] Upgradeable contract is missing a __gap[50] storage variable to allow for new storage variables in later versions

See this link for a description of this storage variable. While some contracts may not currently be sub-classed, adding the variable now protects against forgetting to add it in the future.

There is 1 instance of this issue:

File: contracts/contract/tokens/TokenggAVAX.sol

24:   contract TokenggAVAX is Initializable, ERC4626Upgradeable, UUPSUpgradeable, BaseUpgradeable {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/TokenggAVAX.sol#L24

[N‑08] Import declarations should import specific identifiers, rather than the whole file

Using import declarations of the form import {<identifier_name>} from "some/file.sol" avoids polluting the symbol namespace making flattened files smaller, and speeds up compilation

There are 13 instances of this issue:

File: contracts/contract/Base.sol

4:    import "./BaseAbstract.sol";

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Base.sol#L4

File: contracts/contract/BaseUpgradeable.sol

4:    import "./BaseAbstract.sol";

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/BaseUpgradeable.sol#L4

File: contracts/contract/ClaimNodeOp.sol

4:    import "./Base.sol";

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/ClaimNodeOp.sol#L4

File: contracts/contract/ClaimProtocolDAO.sol

4:    import "./Base.sol";

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/ClaimProtocolDAO.sol#L4

File: contracts/contract/MinipoolManager.sol

4:    import "./Base.sol";

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/MinipoolManager.sol#L4

File: contracts/contract/MultisigManager.sol

4:    import "./Base.sol";

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/MultisigManager.sol#L4

File: contracts/contract/Ocyticus.sol

4:    import "./Base.sol";

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Ocyticus.sol#L4

File: contracts/contract/Oracle.sol

4:    import "./Base.sol";

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Oracle.sol#L4

File: contracts/contract/ProtocolDAO.sol

4:    import "./Base.sol";

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/ProtocolDAO.sol#L4

File: contracts/contract/RewardsPool.sol

4:    import "./Base.sol";

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/RewardsPool.sol#L4

File: contracts/contract/Staking.sol

4:    import "./Base.sol";

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Staking.sol#L4

File: contracts/contract/tokens/TokenggAVAX.sol

6:    import "../BaseUpgradeable.sol";

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/TokenggAVAX.sol#L6

File: contracts/contract/Vault.sol

4:    import "./Base.sol";

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Vault.sol#L4

[N‑09] Missing initializer modifier on constructor

OpenZeppelin recommends that the initializer modifier be applied to constructors in order to avoid potential griefs, social engineering, or exploits. Ensure that the modifier is applied to the implementation contract. If the default constructor is currently being used, it should be changed to be an explicit one with the modifier applied.

There is 1 instance of this issue:

File: contracts/contract/BaseUpgradeable.sol

9:    contract BaseUpgradeable is Initializable, BaseAbstract {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/BaseUpgradeable.sol#L9

[N‑10] The nonReentrant modifier should occur before all other modifiers

This is a best-practice to protect against reentrancy in other modifiers

There are 2 instances of this issue:

File: contracts/contract/Vault.sol

61:   	function withdrawAVAX(uint256 amount) external onlyRegisteredNetworkContract nonReentrant {

141:  	) external onlyRegisteredNetworkContract nonReentrant {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Vault.sol#L61

[N‑11] override function arguments that are unused should have the variable name removed or commented out to avoid compiler warnings

There is 1 instance of this issue:

File: contracts/contract/tokens/TokenggAVAX.sol

255:  	function _authorizeUpgrade(address newImplementation) internal override onlyGuardian {}

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/TokenggAVAX.sol#L255

[N‑12] constants should be defined rather than using magic numbers

Even assembly can benefit from using readable constants instead of hex/numeric literals

There are 2 instances of this issue:

File: contracts/contract/MinipoolManager.sol

/// @audit 365
560:  		return (avaxAmt.mulWadDown(rate) * duration) / 365 days;

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/MinipoolManager.sol#L560

File: contracts/contract/tokens/TokenggAVAX.sol

/// @audit 14
76:   		rewardsCycleLength = 14 days;

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/TokenggAVAX.sol#L76

[N‑13] Missing event and or timelock for critical parameter change

Events help non-contract tools to track changes, and events prevent users from being surprised by changes

There is 1 instance of this issue:

File: contracts/contract/Storage.sol

41    	function setGuardian(address newAddress) external {
42    		// Check tx comes from current guardian
43    		if (msg.sender != guardian) {
44    			revert MustBeGuardian();
45    		}
46    		// Store new address awaiting confirmation
47    		newGuardian = newAddress;
48:   	}

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Storage.sol#L41-L48

[N‑14] Events that mark critical parameter changes should contain both the old and the new value

This should especially be done if the new value is not required to be different from the old value

There are 2 instances of this issue:

File: contracts/contract/MultisigManager.sol

/// @audit registerMultisig()
50:   		emit RegisteredMultisig(addr, msg.sender);

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/MultisigManager.sol#L50

File: contracts/contract/Oracle.sol

/// @audit setGGPPriceInAVAX()
67:   		emit GGPPriceUpdated(price, timestamp);

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Oracle.sol#L67

[N‑15] Use a more recent version of solidity

Use a solidity version of at least 0.8.13 to get the ability to use using for with a list of free functions

There is 1 instance of this issue:

File: contracts/contract/tokens/upgradeable/ERC4626Upgradeable.sol

2:    pragma solidity >=0.8.0;

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/upgradeable/ERC4626Upgradeable.sol#L2

[N‑16] Use a more recent version of solidity

Use a solidity version of at least 0.8.4 to get bytes.concat() instead of abi.encodePacked(<bytes>,<bytes>) Use a solidity version of at least 0.8.12 to get string.concat() instead of abi.encodePacked(<str>,<str>)

There is 1 instance of this issue:

File: contracts/contract/tokens/upgradeable/ERC20Upgradeable.sol

2:    pragma solidity >=0.8.0;

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/upgradeable/ERC20Upgradeable.sol#L2

[N‑17] Constant redefined elsewhere

Consider defining in only one contract so that values cannot become out of sync when only one location is updated. A cheap way to store constants in a single location is to create an internal constant in a library. If the variable is a local cache of another contract's value, consider making the cache variable internal or private, which will require external users to query the contract with the source of truth, so that callers don't get out of sync.

There are 2 instances of this issue:

File: contracts/contract/MinipoolManager.sol

/// @audit seen in contracts/contract/ClaimNodeOp.sol 
78:   	ERC20 public immutable ggp;

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/MinipoolManager.sol#L78

File: contracts/contract/Staking.sol

/// @audit seen in contracts/contract/MinipoolManager.sol 
58:   	ERC20 public immutable ggp;

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Staking.sol#L58

[N‑18] Lines are too long

Usually lines in source code are limited to 80 characters. Today's screens are much larger so it's reasonable to stretch this in some cases. Since the files will most likely reside in GitHub, and GitHub starts using a scroll bar in all cases when the length is over 164 characters, the lines below should be split when they reach that length

There is 1 instance of this issue:

File: contracts/contract/ProtocolDAO.sol

41:   		setUint(keccak256("ProtocolDAO.InflationIntervalRate"), 1000133680617113500); // 5% annual calculated on a daily interval - Calculate in js example: let dailyInflation = web3.utils.toBN((1 + 0.05) ** (1 / (365)) * 1e18);

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/ProtocolDAO.sol#L41

[N‑19] Variable names that consist of all capital letters should be reserved for constant/immutable variables

If the variable needs to be different based on which class it comes from, a view/pure function should be used instead (e.g. like this).

There are 2 instances of this issue:

File: contracts/contract/tokens/upgradeable/ERC20Upgradeable.sol

43:   	uint256 internal INITIAL_CHAIN_ID;

45:   	bytes32 internal INITIAL_DOMAIN_SEPARATOR;

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/upgradeable/ERC20Upgradeable.sol#L43

[N‑20] Using >/>= without specifying an upper bound is unsafe

There will be breaking changes in future versions of solidity, and at that point your code will no longer be compatable. While you may have the specific version to use in a configuration file, others that include your source files may not.

There are 2 instances of this issue:

File: contracts/contract/tokens/upgradeable/ERC20Upgradeable.sol

2:    pragma solidity >=0.8.0;

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/upgradeable/ERC20Upgradeable.sol#L2

File: contracts/contract/tokens/upgradeable/ERC4626Upgradeable.sol

2:    pragma solidity >=0.8.0;

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/upgradeable/ERC4626Upgradeable.sol#L2

[N‑21] Typos

There are 3 instances of this issue:

File: contracts/contract/MinipoolManager.sol

/// @audit avalanchego
47:   	minipool.item<index>.avaxTotalRewardAmt = Actual total avax rewards paid by avalanchego to the TSS P-chain addr

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/MinipoolManager.sol#L47

File: contracts/contract/tokens/TokenggAVAX.sol

/// @audit exectued
67:   		// The constructor is exectued only when creating implementation contract

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/TokenggAVAX.sol#L67

File: contracts/contract/Vault.sol

/// @audit withdrawl
68:   		// Emit the withdrawl event for that contract

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Vault.sol#L68

[N‑22] File is missing NatSpec

There are 3 instances of this issue:

File: contracts/contract/BaseUpgradeable.sol

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/BaseUpgradeable.sol

File: contracts/contract/tokens/TokenGGP.sol

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/TokenGGP.sol

File: contracts/contract/tokens/upgradeable/ERC4626Upgradeable.sol

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/upgradeable/ERC4626Upgradeable.sol

[N‑23] NatSpec is incomplete

There are 27 instances of this issue:

File: contracts/contract/MinipoolManager.sol

/// @audit Missing: '@return'
544   
545   	/// @notice Calculates how much GGP should be slashed given an expected avaxRewardAmt
546   	/// @param avaxRewardAmt The amount of AVAX that should have been awarded to the validator by Avalanche
547:  	function calculateGGPSlashAmt(uint256 avaxRewardAmt) public view returns (uint256) {

/// @audit Missing: '@return'
569   
570   	/// @notice Gets the minipool information from the node ID
571   	/// @param nodeID 20-byte Avalanche node ID
572:  	function getMinipoolByNodeID(address nodeID) public view returns (Minipool memory mp) {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/MinipoolManager.sol#L544-L547

File: contracts/contract/ProtocolDAO.sol

/// @audit Missing: '@return'
58    
59    	/// @notice Get if a contract is paused
60    	/// @param contractName The contract that is being checked
61:   	function getContractPaused(string memory contractName) public view returns (bool) {

/// @audit Missing: '@param claimingContract'
99    
100   	/// @notice The percentage a contract is owed for a rewards cycle
101   	/// @return uint256 Rewards percentage a contract will receive this cycle
102:  	function getClaimingContractPct(string memory claimingContract) public view returns (uint256) {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/ProtocolDAO.sol#L58-L61

File: contracts/contract/Staking.sol

/// @audit Missing: '@return'
77    
78    	/// @notice The amount of GGP a given staker is staking
79    	/// @param stakerAddr The C-chain address of a GGP staker in the protocol
80:   	function getGGPStake(address stakerAddr) public view returns (uint256) {

/// @audit Missing: '@param amount'
84    
85    	/// @notice Increase the amount of GGP a given staker is staking
86    	/// @param stakerAddr The C-chain address of a GGP staker in the protocol
87:   	function increaseGGPStake(address stakerAddr, uint256 amount) internal {

/// @audit Missing: '@param amount'
91    
92    	/// @notice Decrease the amount of GGP a given staker is staking
93    	/// @param stakerAddr The C-chain address of a GGP staker in the protocol
94:   	function decreaseGGPStake(address stakerAddr, uint256 amount) internal {

/// @audit Missing: '@return'
100   
101   	/// @notice The amount of AVAX a given staker is staking
102   	/// @param stakerAddr The C-chain address of a GGP staker in the protocol
103:  	function getAVAXStake(address stakerAddr) public view returns (uint256) {

/// @audit Missing: '@param amount'
107   
108   	/// @notice Increase the amount of AVAX a given staker is staking
109   	/// @param stakerAddr The C-chain address of a GGP staker in the protocol
110:  	function increaseAVAXStake(address stakerAddr, uint256 amount) public onlySpecificRegisteredContract("MinipoolManager", msg.sender) {

/// @audit Missing: '@param amount'
114   
115   	/// @notice Decrease the amount of AVAX a given staker is staking
116   	/// @param stakerAddr The C-chain address of a GGP staker in the protocol
117:  	function decreaseAVAXStake(address stakerAddr, uint256 amount) public onlySpecificRegisteredContract("MinipoolManager", msg.sender) {

/// @audit Missing: '@return'
123   
124   	/// @notice The amount of AVAX a given staker is assigned by the protocol (for minipool creation)
125   	/// @param stakerAddr The C-chain address of a GGP staker in the protocol
126:  	function getAVAXAssigned(address stakerAddr) public view returns (uint256) {

/// @audit Missing: '@param amount'
130   
131   	/// @notice Increase the amount of AVAX a given staker is assigned by the protocol (for minipool creation)
132   	/// @param stakerAddr The C-chain address of a GGP staker in the protocol
133:  	function increaseAVAXAssigned(address stakerAddr, uint256 amount) public onlySpecificRegisteredContract("MinipoolManager", msg.sender) {

/// @audit Missing: '@param amount'
137   
138   	/// @notice Decrease the amount of AVAX a given staker is assigned by the protocol (for minipool creation)
139   	/// @param stakerAddr The C-chain address of a GGP staker in the protocol
140:  	function decreaseAVAXAssigned(address stakerAddr, uint256 amount) public onlySpecificRegisteredContract("MinipoolManager", msg.sender) {

/// @audit Missing: '@return'
146   
147   	/// @notice Largest total AVAX amt assigned to a staker during a rewards period
148   	/// @param stakerAddr The C-chain address of a GGP staker in the protocol
149:  	function getAVAXAssignedHighWater(address stakerAddr) public view returns (uint256) {

/// @audit Missing: '@param amount'
153   
154   	/// @notice Increase the AVAXAssignedHighWater
155   	/// @param stakerAddr The C-chain address of a GGP staker in the protocol
156:  	function increaseAVAXAssignedHighWater(address stakerAddr, uint256 amount) public onlyRegisteredNetworkContract {

/// @audit Missing: '@return'
170   
171   	/// @notice The number of minipools the given staker has
172   	/// @param stakerAddr The C-chain address of a GGP staker in the protocol
173:  	function getMinipoolCount(address stakerAddr) public view returns (uint256) {

/// @audit Missing: '@return'
193   
194   	/// @notice The timestamp when the staker registered for GGP rewards
195   	/// @param stakerAddr The C-chain address of a GGP staker in the protocol
196:  	function getRewardsStartTime(address stakerAddr) public view returns (uint256) {

/// @audit Missing: '@param time'
200   
201   	/// @notice Set the timestamp when the staker registered for GGP rewards
202   	/// @param stakerAddr The C-chain address of a GGP staker in the protocol
203   	// TODO cant use onlySpecificRegisteredContract("ClaimNodeOp", msg.sender) since we also call from increaseMinipoolCount. Wat do?
204:  	function setRewardsStartTime(address stakerAddr, uint256 time) public onlyRegisteredNetworkContract {

/// @audit Missing: '@return'
214   
215   	/// @notice The amount of GGP rewards the staker has earned and not claimed
216   	/// @param stakerAddr The C-chain address of a GGP staker in the protocol
217:  	function getGGPRewards(address stakerAddr) public view returns (uint256) {

/// @audit Missing: '@param amount'
221   
222   	/// @notice Increase the amount of GGP rewards the staker has earned and not claimed
223   	/// @param stakerAddr The C-chain address of a GGP staker in the protocol
224:  	function increaseGGPRewards(address stakerAddr, uint256 amount) public onlySpecificRegisteredContract("ClaimNodeOp", msg.sender) {

/// @audit Missing: '@param amount'
228   
229   	/// @notice Decrease the amount of GGP rewards the staker has earned and not claimed
230   	/// @param stakerAddr The C-chain address of a GGP staker in the protocol
231:  	function decreaseGGPRewards(address stakerAddr, uint256 amount) public onlySpecificRegisteredContract("ClaimNodeOp", msg.sender) {

/// @audit Missing: '@return'
237   
238   	/// @notice The most recent reward cycle number that the staker has been paid out for
239   	/// @param stakerAddr The C-chain address of a GGP staker in the protocol
240:  	function getLastRewardsCycleCompleted(address stakerAddr) public view returns (uint256) {

/// @audit Missing: '@return'
305   
306   	/// @notice GGP that will count towards rewards this cycle
307   	/// @param stakerAddr The C-chain address of a GGP staker in the protocol
308:  	function getEffectiveGGPStaked(address stakerAddr) external view returns (uint256) {

/// @audit Missing: '@return'
384   
385   	/// @notice Verifying the staker exists in the protocol
386   	/// @param stakerAddr The C-chain address of a GGP staker in the protocol
387:  	function requireValidStaker(address stakerAddr) public view returns (int256) {

/// @audit Missing: '@param stakerAddr'
395   
396   	/// @notice Get index of the staker
397   	/// @return staker index or -1 if the value was not found
398:  	function getIndexOf(address stakerAddr) public view returns (int256) {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Staking.sol#L77-L80

File: contracts/contract/Vault.sol

/// @audit Missing: '@return'
190   
191   	/// @notice Get the AVAX balance held by a network contract
192   	/// @param networkContractName Name of the contract who's AVAX balance is being requested
193:  	function balanceOf(string memory networkContractName) external view returns (uint256) {

/// @audit Missing: '@return'
197   	/// @notice Get the balance of a token held by a network contract
198   	/// @param networkContractName Name of the contract who's token balance is being requested
199   	/// @param tokenAddress address of the ERC20 token
200:  	function balanceOfToken(string memory networkContractName, ERC20 tokenAddress) external view returns (uint256) {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Vault.sol#L190-L193

[N‑24] Not using the named return variables anywhere in the function is confusing

Consider changing the variable to be an unnamed one

There is 1 instance of this issue:

File: contracts/contract/MinipoolManager.sol

/// @audit mp
572:  	function getMinipoolByNodeID(address nodeID) public view returns (Minipool memory mp) {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/MinipoolManager.sol#L572

[N‑25] Contracts should have full test coverage

While 100% code coverage does not guarantee that there are no bugs, it often will catch easy-to-find bugs, and will ensure that there are fewer regressions when the code invariably has to be modified. Furthermore, in order to get full coverage, code authors will often have to re-organize their code so that it is more modular, so that each component can be tested separately, which reduces interdependencies between modules and layers, and makes for code that is easier to reason about and audit.

There is 1 instance of this issue:

File: Various Files

[N‑26] Large or complicated code bases should implement fuzzing tests

Large code bases, or code with lots of inline-assembly, complicated math, or complicated interactions between multiple contracts, should implement fuzzing tests. Fuzzers such as Echidna require the test writer to come up with invariants which should not be violated under any circumstances, and the fuzzer tests various inputs and function calls to ensure that the invariants always hold. Even code with 100% code coverage can still have bugs due to the order of the operations a user performs, and fuzzers, with properly and extensively-written invariants, can close this testing gap significantly.

There is 1 instance of this issue:

File: Various Files

[N‑27] Function ordering does not follow the Solidity style guide

According to the Solidity style guide, functions should be laid out in the following order :constructor(), receive(), fallback(), external, public, internal, private, but the cases below do not follow this pattern

There are 15 instances of this issue:

File: contracts/contract/ClaimNodeOp.sol

/// @audit setRewardsCycleTotal() came earlier
46:   	function isEligible(address stakerAddr) external view returns (bool) {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/ClaimNodeOp.sol#L46

File: contracts/contract/MinipoolManager.sol

/// @audit requireValidStateTransition() came earlier
177   	constructor(
178   		Storage storageAddress,
179   		ERC20 ggp_,
180   		TokenggAVAX ggAVAX_
181:  	) Base(storageAddress) {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/MinipoolManager.sol#L177-L181

File: contracts/contract/ProtocolDAO.sol

/// @audit setClaimingContractPct() came earlier
115:  	function getInflationIntervalRate() external view returns (uint256) {

/// @audit getMinCollateralizationRatio() came earlier
181:  	function getTargetGGAVAXReserveRate() external view returns (uint256) {

/// @audit unregisterContract() came earlier
209   	function upgradeExistingContract(
210   		address newAddr,
211   		string memory newName,
212   		address existingAddr
213:  	) external onlyGuardian {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/ProtocolDAO.sol#L115

File: contracts/contract/RewardsPool.sol

/// @audit inflate() came earlier
105:  	function getRewardsCycleCount() public view returns (uint256) {

/// @audit increaseRewardsCycleCount() came earlier
115:  	function getRewardsCycleStartTime() public view returns (uint256) {

/// @audit canStartRewardsCycle() came earlier
156   	function startRewardsCycle() external {
157:  		if (!canStartRewardsCycle()) {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/RewardsPool.sol#L105

File: contracts/contract/Staking.sol

/// @audit decreaseGGPStake() came earlier
103:  	function getAVAXStake(address stakerAddr) public view returns (uint256) {

/// @audit getEffectiveRewardsRatio() came earlier
308:  	function getEffectiveGGPStaked(address stakerAddr) external view returns (uint256) {

/// @audit _stakeGGP() came earlier
358:  	function withdrawGGP(uint256 amount) external whenNotPaused {

/// @audit getStaker() came earlier
420:  	function getStakers(uint256 offset, uint256 limit) external view returns (Staker[] memory stakers) {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Staking.sol#L103

File: contracts/contract/tokens/TokenggAVAX.sol

/// @audit initialize() came earlier
82    	receive() external payable {
83:   		assert(msg.sender == address(asset));

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/TokenggAVAX.sol#L82-L83

File: contracts/contract/tokens/upgradeable/ERC20Upgradeable.sol

/// @audit __ERC20Upgradeable_init() came earlier
70:   	function approve(address spender, uint256 amount) public virtual returns (bool) {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/upgradeable/ERC20Upgradeable.sol#L70

File: contracts/contract/tokens/upgradeable/ERC4626Upgradeable.sol

/// @audit __ERC4626Upgradeable_init() came earlier
42:   	function deposit(uint256 assets, address receiver) public virtual returns (uint256 shares) {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/upgradeable/ERC4626Upgradeable.sol#L42

[N‑28] Contract does not follow the Solidity style guide's suggested layout ordering

The style guide says that, within a contract, the ordering should be 1) Type declarations, 2) State variables, 3) Events, 4) Modifiers, and 5) Functions, but the contract(s) below do not follow this ordering

There are 9 instances of this issue:

File: contracts/contract/ClaimNodeOp.sol

/// @audit event GGPRewardsClaimed came earlier
27:   	ERC20 public immutable ggp;

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/ClaimNodeOp.sol#L27

File: contracts/contract/MinipoolManager.sol

/// @audit event MinipoolStatusChanged came earlier
78:   	ERC20 public immutable ggp;

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/MinipoolManager.sol#L78

File: contracts/contract/MultisigManager.sol

/// @audit event RegisteredMultisig came earlier
27:   	uint256 public constant MULTISIG_LIMIT = 10;

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/MultisigManager.sol#L27

File: contracts/contract/Staking.sol

/// @audit event GGPWithdrawn came earlier
56:   	uint256 internal constant TENTH = 0.1 ether;

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Staking.sol#L56

File: contracts/contract/Storage.sol

/// @audit event GuardianChanged came earlier
15    	mapping(bytes32 => address) private addressStorage;
16    	mapping(bytes32 => bool) private booleanStorage;
17    	mapping(bytes32 => bytes) private bytesStorage;
18    	mapping(bytes32 => bytes32) private bytes32Storage;
19    	mapping(bytes32 => int256) private intStorage;
20    	mapping(bytes32 => string) private stringStorage;
21:   	mapping(bytes32 => uint256) private uintStorage;

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Storage.sol#L15-L21

File: contracts/contract/tokens/TokenggAVAX.sol

/// @audit event DepositedFromStaking came earlier
41:   	uint32 public lastSync;

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/TokenggAVAX.sol#L41

File: contracts/contract/tokens/upgradeable/ERC20Upgradeable.sol

/// @audit event Approval came earlier
23:   	string public name;

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/upgradeable/ERC20Upgradeable.sol#L23

File: contracts/contract/tokens/upgradeable/ERC4626Upgradeable.sol

/// @audit event Withdraw came earlier
27:   	ERC20 public asset;

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/upgradeable/ERC4626Upgradeable.sol#L27

File: contracts/contract/Vault.sol

/// @audit event TokenWithdrawn came earlier
37:   	mapping(string => uint256) private avaxBalances;

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Vault.sol#L37


Excluded findings

These findings are excluded from awards calculations because there are publicly-available automated tools that find them. The valid ones appear here for completeness

Summary

Low Risk Issues

IssueInstances
[L‑01]Missing checks for address(0x0) when assigning values to address state variables1
[L‑02]abi.encodePacked() should not be used with dynamic types when passing the result to a hash function such as keccak256()155

Total: 156 instances over 2 issues

Non-critical Issues

IssueInstances
[N‑01]Return values of approve() not checked2
[N‑02]public functions not called by the contract should be declared external instead54
[N‑03]Event is missing indexed fields26

Total: 82 instances over 3 issues

Low Risk Issues

[L‑01] Missing checks for address(0x0) when assigning values to address state variables

There is 1 instance of this issue:

File: contracts/contract/Storage.sol

/// @audit (valid but excluded finding)
47:   		newGuardian = newAddress;

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Storage.sol#L47

[L‑02] abi.encodePacked() should not be used with dynamic types when passing the result to a hash function such as keccak256()

Use abi.encode() instead which will pad items to 32 bytes, which will prevent hash collisions (e.g. abi.encodePacked(0x123,0x456) => 0x123456 => abi.encodePacked(0x1,0x23456), but abi.encode(0x123,0x456) => 0x0...1230...456). "Unless there is a compelling reason, abi.encode should be preferred". If there is only one argument to abi.encodePacked() it can often be cast to bytes() or bytes32() instead.

If all arguments are strings and or bytes, bytes.concat() should be used instead

There are 155 instances of this issue:

File: contracts/contract/BaseAbstract.sol

/// @audit (valid but excluded finding)
25:   		if (getBool(keccak256(abi.encodePacked("contract.exists", msg.sender))) == false) {

/// @audit (valid but excluded finding)
33:   		if (contractAddress != getAddress(keccak256(abi.encodePacked("contract.address", contractName)))) {

/// @audit (valid but excluded finding)
41:   		bool isContract = getBool(keccak256(abi.encodePacked("contract.exists", msg.sender)));

/// @audit (valid but excluded finding)
52:   		bool isContract = contractAddress == getAddress(keccak256(abi.encodePacked("contract.address", contractName)));

/// @audit (valid but excluded finding)
71:   		int256 multisigIndex = int256(getUint(keccak256(abi.encodePacked("multisig.index", msg.sender)))) - 1;

/// @audit (valid but excluded finding)
72:   		address addr = getAddress(keccak256(abi.encodePacked("multisig.item", multisigIndex, ".address")));

/// @audit (valid but excluded finding)
73:   		bool enabled = (addr != address(0)) && getBool(keccak256(abi.encodePacked("multisig.item", multisigIndex, ".enabled")));

/// @audit (valid but excluded finding)
83:   		if (getBool(keccak256(abi.encodePacked("contract.paused", contractName)))) {

/// @audit (valid but excluded finding)
91:   		address contractAddress = getAddress(keccak256(abi.encodePacked("contract.address", contractName)));

/// @audit (valid but excluded finding)
100:  		string memory contractName = getString(keccak256(abi.encodePacked("contract.name", contractAddress)));

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/BaseAbstract.sol#L25

File: contracts/contract/MinipoolManager.sol

/// @audit (valid but excluded finding)
116:  		address owner = getAddress(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".owner")));

/// @audit (valid but excluded finding)
130:  		address assignedMultisig = getAddress(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".multisigAddr")));

/// @audit (valid but excluded finding)
153:  		bytes32 statusKey = keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".status"));

/// @audit (valid but excluded finding)
246:  			setUint(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".initialStartTime")), 0);

/// @audit (valid but excluded finding)
250:  			setUint(keccak256(abi.encodePacked("minipool.index", nodeID)), uint256(minipoolIndex + 1));

/// @audit (valid but excluded finding)
251:  			setAddress(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".nodeID")), nodeID);

/// @audit (valid but excluded finding)
256:  		setUint(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".status")), uint256(MinipoolStatus.Prelaunch));

/// @audit (valid but excluded finding)
257:  		setUint(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".duration")), duration);

/// @audit (valid but excluded finding)
258:  		setUint(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".delegationFee")), delegationFee);

/// @audit (valid but excluded finding)
259:  		setAddress(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".owner")), msg.sender);

/// @audit (valid but excluded finding)
260:  		setAddress(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".multisigAddr")), multisig);

/// @audit (valid but excluded finding)
261:  		setUint(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".avaxNodeOpInitialAmt")), msg.value);

/// @audit (valid but excluded finding)
262:  		setUint(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".avaxNodeOpAmt")), msg.value);

/// @audit (valid but excluded finding)
263:  		setUint(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".avaxLiquidStakerAmt")), avaxAssignmentRequest);

/// @audit (valid but excluded finding)
291:  		setUint(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".status")), uint256(MinipoolStatus.Finished));

/// @audit (valid but excluded finding)
293:  		uint256 avaxNodeOpAmt = getUint(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".avaxNodeOpAmt")));

/// @audit (valid but excluded finding)
294:  		uint256 avaxNodeOpRewardAmt = getUint(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".avaxNodeOpRewardAmt")));

/// @audit (valid but excluded finding)
317:  		uint256 avaxLiquidStakerAmt = getUint(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".avaxLiquidStakerAmt")));

/// @audit (valid but excluded finding)
328:  		uint256 avaxNodeOpAmt = getUint(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".avaxNodeOpAmt")));

/// @audit (valid but excluded finding)
329:  		uint256 avaxLiquidStakerAmt = getUint(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".avaxLiquidStakerAmt")));

/// @audit (valid but excluded finding)
335:  		setUint(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".status")), uint256(MinipoolStatus.Launched));

/// @audit (valid but excluded finding)
360:  		setUint(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".status")), uint256(MinipoolStatus.Staking));

/// @audit (valid but excluded finding)
361:  		setBytes32(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".txID")), txID);

/// @audit (valid but excluded finding)
362:  		setUint(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".startTime")), startTime);

/// @audit (valid but excluded finding)
365:  		uint256 initialStartTime = getUint(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".initialStartTime")));

/// @audit (valid but excluded finding)
367:  			setUint(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".initialStartTime")), startTime);

/// @audit (valid but excluded finding)
370:  		address owner = getAddress(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".owner")));

/// @audit (valid but excluded finding)
371:  		uint256 avaxLiquidStakerAmt = getUint(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".avaxLiquidStakerAmt")));

/// @audit (valid but excluded finding)
393:  		uint256 startTime = getUint(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".startTime")));

/// @audit (valid but excluded finding)
398:  		uint256 avaxNodeOpAmt = getUint(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".avaxNodeOpAmt")));

/// @audit (valid but excluded finding)
399:  		uint256 avaxLiquidStakerAmt = getUint(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".avaxLiquidStakerAmt")));

/// @audit (valid but excluded finding)
405:  		address owner = getAddress(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".owner")));

/// @audit (valid but excluded finding)
407:  		setUint(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".status")), uint256(MinipoolStatus.Withdrawable));

/// @audit (valid but excluded finding)
408:  		setUint(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".endTime")), endTime);

/// @audit (valid but excluded finding)
409:  		setUint(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".avaxTotalRewardAmt")), avaxTotalRewardAmt);

/// @audit (valid but excluded finding)
420:  		setUint(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".avaxNodeOpRewardAmt")), avaxNodeOpRewardAmt);

/// @audit (valid but excluded finding)
421:  		setUint(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".avaxLiquidStakerRewardAmt")), avaxLiquidStakerRewardAmt);

/// @audit (valid but excluded finding)
451:  		setUint(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".avaxNodeOpAmt")), compoundedAvaxNodeOpAmt);

/// @audit (valid but excluded finding)
452:  		setUint(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".avaxLiquidStakerAmt")), compoundedAvaxNodeOpAmt);

/// @audit (valid but excluded finding)
475:  		setUint(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".status")), uint256(MinipoolStatus.Prelaunch));

/// @audit (valid but excluded finding)
488:  		address owner = getAddress(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".owner")));

/// @audit (valid but excluded finding)
489:  		uint256 avaxNodeOpAmt = getUint(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".avaxNodeOpAmt")));

/// @audit (valid but excluded finding)
490:  		uint256 avaxLiquidStakerAmt = getUint(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".avaxLiquidStakerAmt")));

/// @audit (valid but excluded finding)
496:  		setBytes32(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".errorCode")), errorCode);

/// @audit (valid but excluded finding)
497:  		setUint(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".status")), uint256(MinipoolStatus.Error));

/// @audit (valid but excluded finding)
498:  		setUint(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".avaxTotalRewardAmt")), 0);

/// @audit (valid but excluded finding)
499:  		setUint(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".avaxNodeOpRewardAmt")), 0);

/// @audit (valid but excluded finding)
500:  		setUint(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".avaxLiquidStakerRewardAmt")), 0);

/// @audit (valid but excluded finding)
522:  		setBytes32(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".errorCode")), errorCode);

/// @audit (valid but excluded finding)
531:  		setUint(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".status")), uint256(MinipoolStatus.Finished));

/// @audit (valid but excluded finding)
567:  		return int256(getUint(keccak256(abi.encodePacked("minipool.index", nodeID)))) - 1;

/// @audit (valid but excluded finding)
582:  		mp.nodeID = getAddress(keccak256(abi.encodePacked("minipool.item", index, ".nodeID")));

/// @audit (valid but excluded finding)
583:  		mp.status = getUint(keccak256(abi.encodePacked("minipool.item", index, ".status")));

/// @audit (valid but excluded finding)
584:  		mp.duration = getUint(keccak256(abi.encodePacked("minipool.item", index, ".duration")));

/// @audit (valid but excluded finding)
585:  		mp.delegationFee = getUint(keccak256(abi.encodePacked("minipool.item", index, ".delegationFee")));

/// @audit (valid but excluded finding)
586:  		mp.owner = getAddress(keccak256(abi.encodePacked("minipool.item", index, ".owner")));

/// @audit (valid but excluded finding)
587:  		mp.multisigAddr = getAddress(keccak256(abi.encodePacked("minipool.item", index, ".multisigAddr")));

/// @audit (valid but excluded finding)
588:  		mp.avaxNodeOpAmt = getUint(keccak256(abi.encodePacked("minipool.item", index, ".avaxNodeOpAmt")));

/// @audit (valid but excluded finding)
589:  		mp.avaxLiquidStakerAmt = getUint(keccak256(abi.encodePacked("minipool.item", index, ".avaxLiquidStakerAmt")));

/// @audit (valid but excluded finding)
590:  		mp.txID = getBytes32(keccak256(abi.encodePacked("minipool.item", index, ".txID")));

/// @audit (valid but excluded finding)
591:  		mp.initialStartTime = getUint(keccak256(abi.encodePacked("minipool.item", index, ".initialStartTime")));

/// @audit (valid but excluded finding)
592:  		mp.startTime = getUint(keccak256(abi.encodePacked("minipool.item", index, ".startTime")));

/// @audit (valid but excluded finding)
593:  		mp.endTime = getUint(keccak256(abi.encodePacked("minipool.item", index, ".endTime")));

/// @audit (valid but excluded finding)
594:  		mp.avaxTotalRewardAmt = getUint(keccak256(abi.encodePacked("minipool.item", index, ".avaxTotalRewardAmt")));

/// @audit (valid but excluded finding)
595:  		mp.errorCode = getBytes32(keccak256(abi.encodePacked("minipool.item", index, ".errorCode")));

/// @audit (valid but excluded finding)
596:  		mp.avaxNodeOpInitialAmt = getUint(keccak256(abi.encodePacked("minipool.item", index, ".avaxNodeOpInitialAmt")));

/// @audit (valid but excluded finding)
597:  		mp.avaxNodeOpRewardAmt = getUint(keccak256(abi.encodePacked("minipool.item", index, ".avaxNodeOpRewardAmt")));

/// @audit (valid but excluded finding)
598:  		mp.avaxLiquidStakerRewardAmt = getUint(keccak256(abi.encodePacked("minipool.item", index, ".avaxLiquidStakerRewardAmt")));

/// @audit (valid but excluded finding)
599:  		mp.ggpSlashAmt = getUint(keccak256(abi.encodePacked("minipool.item", index, ".ggpSlashAmt")));

/// @audit (valid but excluded finding)
648:  		setUint(keccak256(abi.encodePacked("minipool.item", index, ".status")), uint256(MinipoolStatus.Canceled));

/// @audit (valid but excluded finding)
650:  		address owner = getAddress(keccak256(abi.encodePacked("minipool.item", index, ".owner")));

/// @audit (valid but excluded finding)
651:  		uint256 avaxNodeOpAmt = getUint(keccak256(abi.encodePacked("minipool.item", index, ".avaxNodeOpAmt")));

/// @audit (valid but excluded finding)
652:  		uint256 avaxLiquidStakerAmt = getUint(keccak256(abi.encodePacked("minipool.item", index, ".avaxLiquidStakerAmt")));

/// @audit (valid but excluded finding)
671:  		address nodeID = getAddress(keccak256(abi.encodePacked("minipool.item", index, ".nodeID")));

/// @audit (valid but excluded finding)
672:  		address owner = getAddress(keccak256(abi.encodePacked("minipool.item", index, ".owner")));

/// @audit (valid but excluded finding)
673:  		uint256 duration = getUint(keccak256(abi.encodePacked("minipool.item", index, ".duration")));

/// @audit (valid but excluded finding)
674:  		uint256 avaxLiquidStakerAmt = getUint(keccak256(abi.encodePacked("minipool.item", index, ".avaxLiquidStakerAmt")));

/// @audit (valid but excluded finding)
677:  		setUint(keccak256(abi.encodePacked("minipool.item", index, ".ggpSlashAmt")), slashGGPAmt);

/// @audit (valid but excluded finding)
688:  		setBytes32(keccak256(abi.encodePacked("minipool.item", index, ".txID")), 0);

/// @audit (valid but excluded finding)
689:  		setUint(keccak256(abi.encodePacked("minipool.item", index, ".startTime")), 0);

/// @audit (valid but excluded finding)
690:  		setUint(keccak256(abi.encodePacked("minipool.item", index, ".endTime")), 0);

/// @audit (valid but excluded finding)
691:  		setUint(keccak256(abi.encodePacked("minipool.item", index, ".avaxTotalRewardAmt")), 0);

/// @audit (valid but excluded finding)
692:  		setUint(keccak256(abi.encodePacked("minipool.item", index, ".avaxNodeOpRewardAmt")), 0);

/// @audit (valid but excluded finding)
693:  		setUint(keccak256(abi.encodePacked("minipool.item", index, ".avaxLiquidStakerRewardAmt")), 0);

/// @audit (valid but excluded finding)
694:  		setUint(keccak256(abi.encodePacked("minipool.item", index, ".ggpSlashAmt")), 0);

/// @audit (valid but excluded finding)
695:  		setBytes32(keccak256(abi.encodePacked("minipool.item", index, ".errorCode")), 0);

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/MinipoolManager.sol#L116

File: contracts/contract/MultisigManager.sol

/// @audit (valid but excluded finding)
45:   		setAddress(keccak256(abi.encodePacked("multisig.item", index, ".address")), addr);

/// @audit (valid but excluded finding)
48:   		setUint(keccak256(abi.encodePacked("multisig.index", addr)), index + 1);

/// @audit (valid but excluded finding)
61:   		setBool(keccak256(abi.encodePacked("multisig.item", multisigIndex, ".enabled")), true);

/// @audit (valid but excluded finding)
74:   		setBool(keccak256(abi.encodePacked("multisig.item", multisigIndex, ".enabled")), false);

/// @audit (valid but excluded finding)
97:   		return int256(getUint(keccak256(abi.encodePacked("multisig.index", addr)))) - 1;

/// @audit (valid but excluded finding)
110:  		addr = getAddress(keccak256(abi.encodePacked("multisig.item", index, ".address")));

/// @audit (valid but excluded finding)
111:  		enabled = (addr != address(0)) && getBool(keccak256(abi.encodePacked("multisig.item", index, ".enabled")));

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/MultisigManager.sol#L45

File: contracts/contract/ProtocolDAO.sol

/// @audit (valid but excluded finding)
62:   		return getBool(keccak256(abi.encodePacked("contract.paused", contractName)));

/// @audit (valid but excluded finding)
68:   		setBool(keccak256(abi.encodePacked("contract.paused", contractName)), true);

/// @audit (valid but excluded finding)
74:   		setBool(keccak256(abi.encodePacked("contract.paused", contractName)), false);

/// @audit (valid but excluded finding)
103:  		return getUint(keccak256(abi.encodePacked("ProtocolDAO.ClaimingContractPct.", claimingContract)));

/// @audit (valid but excluded finding)
108:  		setUint(keccak256(abi.encodePacked("ProtocolDAO.ClaimingContractPct.", claimingContract)), decimal);

/// @audit (valid but excluded finding)
191:  		setBool(keccak256(abi.encodePacked("contract.exists", addr)), true);

/// @audit (valid but excluded finding)
192:  		setAddress(keccak256(abi.encodePacked("contract.address", name)), addr);

/// @audit (valid but excluded finding)
193:  		setString(keccak256(abi.encodePacked("contract.name", addr)), name);

/// @audit (valid but excluded finding)
200:  		deleteBool(keccak256(abi.encodePacked("contract.exists", addr)));

/// @audit (valid but excluded finding)
201:  		deleteAddress(keccak256(abi.encodePacked("contract.address", name)));

/// @audit (valid but excluded finding)
202:  		deleteString(keccak256(abi.encodePacked("contract.name", addr)));

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/ProtocolDAO.sol#L62

File: contracts/contract/Staking.sol

/// @audit (valid but excluded finding)
82:   		return getUint(keccak256(abi.encodePacked("staker.item", stakerIndex, ".ggpStaked")));

/// @audit (valid but excluded finding)
89:   		addUint(keccak256(abi.encodePacked("staker.item", stakerIndex, ".ggpStaked")), amount);

/// @audit (valid but excluded finding)
96:   		subUint(keccak256(abi.encodePacked("staker.item", stakerIndex, ".ggpStaked")), amount);

/// @audit (valid but excluded finding)
105:  		return getUint(keccak256(abi.encodePacked("staker.item", stakerIndex, ".avaxStaked")));

/// @audit (valid but excluded finding)
112:  		addUint(keccak256(abi.encodePacked("staker.item", stakerIndex, ".avaxStaked")), amount);

/// @audit (valid but excluded finding)
119:  		subUint(keccak256(abi.encodePacked("staker.item", stakerIndex, ".avaxStaked")), amount);

/// @audit (valid but excluded finding)
128:  		return getUint(keccak256(abi.encodePacked("staker.item", stakerIndex, ".avaxAssigned")));

/// @audit (valid but excluded finding)
135:  		addUint(keccak256(abi.encodePacked("staker.item", stakerIndex, ".avaxAssigned")), amount);

/// @audit (valid but excluded finding)
142:  		subUint(keccak256(abi.encodePacked("staker.item", stakerIndex, ".avaxAssigned")), amount);

/// @audit (valid but excluded finding)
151:  		return getUint(keccak256(abi.encodePacked("staker.item", stakerIndex, ".avaxAssignedHighWater")));

/// @audit (valid but excluded finding)
158:  		addUint(keccak256(abi.encodePacked("staker.item", stakerIndex, ".avaxAssignedHighWater")), amount);

/// @audit (valid but excluded finding)
165:  		uint256 currAVAXAssigned = getUint(keccak256(abi.encodePacked("staker.item", stakerIndex, ".avaxAssigned")));

/// @audit (valid but excluded finding)
166:  		setUint(keccak256(abi.encodePacked("staker.item", stakerIndex, ".avaxAssignedHighWater")), currAVAXAssigned);

/// @audit (valid but excluded finding)
175:  		return getUint(keccak256(abi.encodePacked("staker.item", stakerIndex, ".minipoolCount")));

/// @audit (valid but excluded finding)
182:  		addUint(keccak256(abi.encodePacked("staker.item", stakerIndex, ".minipoolCount")), 1);

/// @audit (valid but excluded finding)
189:  		subUint(keccak256(abi.encodePacked("staker.item", stakerIndex, ".minipoolCount")), 1);

/// @audit (valid but excluded finding)
198:  		return getUint(keccak256(abi.encodePacked("staker.item", stakerIndex, ".rewardsStartTime")));

/// @audit (valid but excluded finding)
210:  		setUint(keccak256(abi.encodePacked("staker.item", stakerIndex, ".rewardsStartTime")), time);

/// @audit (valid but excluded finding)
219:  		return getUint(keccak256(abi.encodePacked("staker.item", stakerIndex, ".ggpRewards")));

/// @audit (valid but excluded finding)
226:  		addUint(keccak256(abi.encodePacked("staker.item", stakerIndex, ".ggpRewards")), amount);

/// @audit (valid but excluded finding)
233:  		subUint(keccak256(abi.encodePacked("staker.item", stakerIndex, ".ggpRewards")), amount);

/// @audit (valid but excluded finding)
242:  		return getUint(keccak256(abi.encodePacked("staker.item", stakerIndex, ".lastRewardsCycleCompleted")));

/// @audit (valid but excluded finding)
250:  		setUint(keccak256(abi.encodePacked("staker.item", stakerIndex, ".lastRewardsCycleCompleted")), cycleNumber);

/// @audit (valid but excluded finding)
350:  			setUint(keccak256(abi.encodePacked("staker.index", stakerAddr)), uint256(stakerIndex + 1));

/// @audit (valid but excluded finding)
351:  			setAddress(keccak256(abi.encodePacked("staker.item", stakerIndex, ".stakerAddr")), stakerAddr);

/// @audit (valid but excluded finding)
399:  		return int256(getUint(keccak256(abi.encodePacked("staker.index", stakerAddr)))) - 1;

/// @audit (valid but excluded finding)
406:  		staker.ggpStaked = getUint(keccak256(abi.encodePacked("staker.item", stakerIndex, ".ggpStaked")));

/// @audit (valid but excluded finding)
407:  		staker.avaxAssigned = getUint(keccak256(abi.encodePacked("staker.item", stakerIndex, ".avaxAssigned")));

/// @audit (valid but excluded finding)
408:  		staker.avaxStaked = getUint(keccak256(abi.encodePacked("staker.item", stakerIndex, ".avaxStaked")));

/// @audit (valid but excluded finding)
409:  		staker.stakerAddr = getAddress(keccak256(abi.encodePacked("staker.item", stakerIndex, ".stakerAddr")));

/// @audit (valid but excluded finding)
410:  		staker.minipoolCount = getUint(keccak256(abi.encodePacked("staker.item", stakerIndex, ".minipoolCount")));

/// @audit (valid but excluded finding)
411:  		staker.rewardsStartTime = getUint(keccak256(abi.encodePacked("staker.item", stakerIndex, ".rewardsStartTime")));

/// @audit (valid but excluded finding)
412:  		staker.ggpRewards = getUint(keccak256(abi.encodePacked("staker.item", stakerIndex, ".ggpRewards")));

/// @audit (valid but excluded finding)
413:  		staker.lastRewardsCycleCompleted = getUint(keccak256(abi.encodePacked("staker.item", stakerIndex, ".lastRewardsCycleCompleted")));

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Staking.sol#L82

File: contracts/contract/Storage.sol

/// @audit (valid but excluded finding)
29:   		if (booleanStorage[keccak256(abi.encodePacked("contract.exists", msg.sender))] == false && msg.sender != guardian) {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Storage.sol#L29

File: contracts/contract/tokens/TokenggAVAX.sol

/// @audit (valid but excluded finding)
59:   		if (amt > 0 && getBool(keccak256(abi.encodePacked("contract.paused", "TokenggAVAX")))) {

/// @audit (valid but excluded finding)
207:  		if (getBool(keccak256(abi.encodePacked("contract.paused", "TokenggAVAX")))) {

/// @audit (valid but excluded finding)
217:  		if (getBool(keccak256(abi.encodePacked("contract.paused", "TokenggAVAX")))) {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/TokenggAVAX.sol#L59

File: contracts/contract/Vault.sol

/// @audit (valid but excluded finding)
124:  		bytes32 contractKey = keccak256(abi.encodePacked(networkContractName, address(tokenContract)));

/// @audit (valid but excluded finding)
179:  		bytes32 contractKeyTo = keccak256(abi.encodePacked(networkContractName, tokenAddress));

/// @audit (valid but excluded finding)
201:  		return tokenBalances[keccak256(abi.encodePacked(networkContractName, tokenAddress))];

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Vault.sol#L124

Non-critical Issues

[N‑01] Return values of approve() not checked

Not all IERC20 implementations revert() when there's a failure in approve(). The function signature has a boolean return value and they indicate errors that way instead. By not checking the return value, operations that should have marked as failed, may potentially go through without actually approving anything

There are 2 instances of this issue:

File: contracts/contract/ClaimNodeOp.sol

/// @audit (valid but excluded finding)
105:  			ggp.approve(address(staking), restakeAmt);

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/ClaimNodeOp.sol#L105

File: contracts/contract/Staking.sol

/// @audit (valid but excluded finding)
342:  		ggp.approve(address(vault), amount);

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Staking.sol#L342

[N‑02] public functions not called by the contract should be declared external instead

Contracts are allowed to override their parents' functions and change the visibility from external to public.

There are 54 instances of this issue:

File: contracts/contract/ClaimNodeOp.sol

/// @audit (valid but excluded finding)
40:   	function setRewardsCycleTotal(uint256 amount) public onlySpecificRegisteredContract("RewardsPool", msg.sender) {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/ClaimNodeOp.sol#L40

File: contracts/contract/MinipoolManager.sol

/// @audit (valid but excluded finding)
541:  	function getTotalAVAXLiquidStakerAmt() public view returns (uint256) {

/// @audit (valid but excluded finding)
572:  	function getMinipoolByNodeID(address nodeID) public view returns (Minipool memory mp) {

/// @audit (valid but excluded finding)
607   	function getMinipools(
608   		MinipoolStatus status,
609   		uint256 offset,
610   		uint256 limit
611:  	) public view returns (Minipool[] memory minipools) {

/// @audit (valid but excluded finding)
634:  	function getMinipoolCount() public view returns (uint256) {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/MinipoolManager.sol#L541

File: contracts/contract/MultisigManager.sol

/// @audit (valid but excluded finding)
102:  	function getCount() public view returns (uint256) {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/MultisigManager.sol#L102

File: contracts/contract/ProtocolDAO.sol

/// @audit (valid but excluded finding)
61:   	function getContractPaused(string memory contractName) public view returns (bool) {

/// @audit (valid but excluded finding)
67:   	function pauseContract(string memory contractName) public onlySpecificRegisteredContract("Ocyticus", msg.sender) {

/// @audit (valid but excluded finding)
73:   	function resumeContract(string memory contractName) public onlySpecificRegisteredContract("Ocyticus", msg.sender) {

/// @audit (valid but excluded finding)
81:   	function getRewardsEligibilityMinSeconds() public view returns (uint256) {

/// @audit (valid but excluded finding)
86:   	function getRewardsCycleSeconds() public view returns (uint256) {

/// @audit (valid but excluded finding)
91:   	function getTotalGGPCirculatingSupply() public view returns (uint256) {

/// @audit (valid but excluded finding)
96:   	function setTotalGGPCirculatingSupply(uint256 amount) public onlySpecificRegisteredContract("RewardsPool", msg.sender) {

/// @audit (valid but excluded finding)
102:  	function getClaimingContractPct(string memory claimingContract) public view returns (uint256) {

/// @audit (valid but excluded finding)
107:  	function setClaimingContractPct(string memory claimingContract, uint256 decimal) public onlyGuardian valueNotGreaterThanOne(decimal) {

/// @audit (valid but excluded finding)
123:  	function getInflationIntervalSeconds() public view returns (uint256) {

/// @audit (valid but excluded finding)
130:  	function getMinipoolMinAVAXStakingAmt() public view returns (uint256) {

/// @audit (valid but excluded finding)
135:  	function getMinipoolNodeCommissionFeePct() public view returns (uint256) {

/// @audit (valid but excluded finding)
140:  	function getMinipoolMaxAVAXAssignment() public view returns (uint256) {

/// @audit (valid but excluded finding)
145:  	function getMinipoolMinAVAXAssignment() public view returns (uint256) {

/// @audit (valid but excluded finding)
150:  	function getMinipoolCancelMoratoriumSeconds() public view returns (uint256) {

/// @audit (valid but excluded finding)
156:  	function setExpectedAVAXRewardsRate(uint256 rate) public onlyMultisig valueNotGreaterThanOne(rate) {

/// @audit (valid but excluded finding)
161:  	function getExpectedAVAXRewardsRate() public view returns (uint256) {

/// @audit (valid but excluded finding)
168:  	function getMaxCollateralizationRatio() public view returns (uint256) {

/// @audit (valid but excluded finding)
173:  	function getMinCollateralizationRatio() public view returns (uint256) {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/ProtocolDAO.sol#L61

File: contracts/contract/RewardsPool.sol

/// @audit (valid but excluded finding)
105:  	function getRewardsCycleCount() public view returns (uint256) {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/RewardsPool.sol#L105

File: contracts/contract/Staking.sol

/// @audit (valid but excluded finding)
66:   	function getTotalGGPStake() public view returns (uint256) {

/// @audit (valid but excluded finding)
103:  	function getAVAXStake(address stakerAddr) public view returns (uint256) {

/// @audit (valid but excluded finding)
110:  	function increaseAVAXStake(address stakerAddr, uint256 amount) public onlySpecificRegisteredContract("MinipoolManager", msg.sender) {

/// @audit (valid but excluded finding)
117:  	function decreaseAVAXStake(address stakerAddr, uint256 amount) public onlySpecificRegisteredContract("MinipoolManager", msg.sender) {

/// @audit (valid but excluded finding)
133:  	function increaseAVAXAssigned(address stakerAddr, uint256 amount) public onlySpecificRegisteredContract("MinipoolManager", msg.sender) {

/// @audit (valid but excluded finding)
140:  	function decreaseAVAXAssigned(address stakerAddr, uint256 amount) public onlySpecificRegisteredContract("MinipoolManager", msg.sender) {

/// @audit (valid but excluded finding)
156:  	function increaseAVAXAssignedHighWater(address stakerAddr, uint256 amount) public onlyRegisteredNetworkContract {

/// @audit (valid but excluded finding)
163:  	function resetAVAXAssignedHighWater(address stakerAddr) public onlyRegisteredNetworkContract {

/// @audit (valid but excluded finding)
173:  	function getMinipoolCount(address stakerAddr) public view returns (uint256) {

/// @audit (valid but excluded finding)
180:  	function increaseMinipoolCount(address stakerAddr) public onlySpecificRegisteredContract("MinipoolManager", msg.sender) {

/// @audit (valid but excluded finding)
187:  	function decreaseMinipoolCount(address stakerAddr) public onlySpecificRegisteredContract("MinipoolManager", msg.sender) {

/// @audit (valid but excluded finding)
196:  	function getRewardsStartTime(address stakerAddr) public view returns (uint256) {

/// @audit (valid but excluded finding)
204:  	function setRewardsStartTime(address stakerAddr, uint256 time) public onlyRegisteredNetworkContract {

/// @audit (valid but excluded finding)
217:  	function getGGPRewards(address stakerAddr) public view returns (uint256) {

/// @audit (valid but excluded finding)
224:  	function increaseGGPRewards(address stakerAddr, uint256 amount) public onlySpecificRegisteredContract("ClaimNodeOp", msg.sender) {

/// @audit (valid but excluded finding)
231:  	function decreaseGGPRewards(address stakerAddr, uint256 amount) public onlySpecificRegisteredContract("ClaimNodeOp", msg.sender) {

/// @audit (valid but excluded finding)
240:  	function getLastRewardsCycleCompleted(address stakerAddr) public view returns (uint256) {

/// @audit (valid but excluded finding)
248:  	function setLastRewardsCycleCompleted(address stakerAddr, uint256 cycleNumber) public onlySpecificRegisteredContract("ClaimNodeOp", msg.sender) {

/// @audit (valid but excluded finding)
256:  	function getMinimumGGPStake(address stakerAddr) public view returns (uint256) {

/// @audit (valid but excluded finding)
328:  	function restakeGGP(address stakerAddr, uint256 amount) public onlySpecificRegisteredContract("ClaimNodeOp", msg.sender) {

/// @audit (valid but excluded finding)
379:  	function slashGGP(address stakerAddr, uint256 ggpAmt) public onlySpecificRegisteredContract("MinipoolManager", msg.sender) {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Staking.sol#L66

File: contracts/contract/tokens/TokenggAVAX.sol

/// @audit (valid but excluded finding)
72:   	function initialize(Storage storageAddress, ERC20 asset) public initializer {

/// @audit (valid but excluded finding)
88    	function syncRewards() public {
89:   		uint32 timestamp = block.timestamp.safeCastTo32();

/// @audit (valid but excluded finding)
142:  	function depositFromStaking(uint256 baseAmt, uint256 rewardAmt) public payable onlySpecificRegisteredContract("MinipoolManager", msg.sender) {

/// @audit (valid but excluded finding)
153:  	function withdrawForStaking(uint256 assets) public onlySpecificRegisteredContract("MinipoolManager", msg.sender) {

/// @audit (valid but excluded finding)
166:  	function depositAVAX() public payable returns (uint256 shares) {

/// @audit (valid but excluded finding)
180:  	function withdrawAVAX(uint256 assets) public returns (uint256 shares) {

/// @audit (valid but excluded finding)
191:  	function redeemAVAX(uint256 shares) public returns (uint256 assets) {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/TokenggAVAX.sol#L72

[N‑03] Event is missing indexed fields

Index event fields make the field more quickly accessible to off-chain tools that parse events. However, note that each index field costs extra gas during emission, so it's not necessarily best to index the maximum allowed per event (three fields). Each event should use three indexed fields if there are three or more fields, and gas usage is not particularly of concern for the events in question. If there are fewer than three fields, all of the fields should be indexed.

There are 26 instances of this issue:

File: contracts/contract/ClaimNodeOp.sol

/// @audit (valid but excluded finding)
25:   	event GGPRewardsClaimed(address indexed to, uint256 amount);

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/ClaimNodeOp.sol#L25

File: contracts/contract/ClaimProtocolDAO.sol

/// @audit (valid but excluded finding)
13:   	event GGPTokensSentByDAOProtocol(string invoiceID, address indexed from, address indexed to, uint256 amount);

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/ClaimProtocolDAO.sol#L13

File: contracts/contract/MinipoolManager.sol

/// @audit (valid but excluded finding)
75:   	event GGPSlashed(address indexed nodeID, uint256 ggp);

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/MinipoolManager.sol#L75

File: contracts/contract/MultisigManager.sol

/// @audit (valid but excluded finding)
23:   	event DisabledMultisig(address indexed multisig, address actor);

/// @audit (valid but excluded finding)
24:   	event EnabledMultisig(address indexed multisig, address actor);

/// @audit (valid but excluded finding)
25:   	event RegisteredMultisig(address indexed multisig, address actor);

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/MultisigManager.sol#L23

File: contracts/contract/Oracle.sol

/// @audit (valid but excluded finding)
21:   	event GGPPriceUpdated(uint256 indexed price, uint256 timestamp);

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Oracle.sol#L21

File: contracts/contract/RewardsPool.sol

/// @audit (valid but excluded finding)
24:   	event GGPInflated(uint256 newTokens);

/// @audit (valid but excluded finding)
25:   	event NewRewardsCycleStarted(uint256 totalRewardsAmt);

/// @audit (valid but excluded finding)
26:   	event ClaimNodeOpRewardsTransfered(uint256 value);

/// @audit (valid but excluded finding)
27:   	event ProtocolDAORewardsTransfered(uint256 value);

/// @audit (valid but excluded finding)
28:   	event MultisigRewardsTransfered(uint256 value);

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/RewardsPool.sol#L24

File: contracts/contract/Staking.sol

/// @audit (valid but excluded finding)
40:   	event GGPStaked(address indexed from, uint256 amount);

/// @audit (valid but excluded finding)
41:   	event GGPWithdrawn(address indexed to, uint256 amount);

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Staking.sol#L40

File: contracts/contract/Storage.sol

/// @audit (valid but excluded finding)
12:   	event GuardianChanged(address oldGuardian, address newGuardian);

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Storage.sol#L12

File: contracts/contract/tokens/TokenggAVAX.sol

/// @audit (valid but excluded finding)
36:   	event NewRewardsCycle(uint256 indexed cycleEnd, uint256 rewardsAmt);

/// @audit (valid but excluded finding)
37:   	event WithdrawnForStaking(address indexed caller, uint256 assets);

/// @audit (valid but excluded finding)
38:   	event DepositedFromStaking(address indexed caller, uint256 baseAmt, uint256 rewardsAmt);

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/TokenggAVAX.sol#L36

File: contracts/contract/tokens/upgradeable/ERC20Upgradeable.sol

/// @audit (valid but excluded finding)
15:   	event Transfer(address indexed from, address indexed to, uint256 amount);

/// @audit (valid but excluded finding)
17:   	event Approval(address indexed owner, address indexed spender, uint256 amount);

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/upgradeable/ERC20Upgradeable.sol#L15

File: contracts/contract/tokens/upgradeable/ERC4626Upgradeable.sol

/// @audit (valid but excluded finding)
19:   	event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares);

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/upgradeable/ERC4626Upgradeable.sol#L19

File: contracts/contract/Vault.sol

/// @audit (valid but excluded finding)
30:   	event AVAXDeposited(string indexed by, uint256 amount);

/// @audit (valid but excluded finding)
31:   	event AVAXTransfer(string indexed from, string indexed to, uint256 amount);

/// @audit (valid but excluded finding)
32:   	event AVAXWithdrawn(string indexed by, uint256 amount);

/// @audit (valid but excluded finding)
33:   	event TokenDeposited(bytes32 indexed by, address indexed tokenAddress, uint256 amount);

/// @audit (valid but excluded finding)
35:   	event TokenWithdrawn(bytes32 indexed by, address indexed tokenAddress, uint256 amount);

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Vault.sol#L30

#0 - GalloDaSballo

2023-01-25T19:14:19Z

| [L‑01] | Inflation not locked for four years | 1 | R Code as is will revert after 4 years, so it's enforced via config

| [L‑02] | Contract will stop functioning in the year 2106 | 1 | L

| [L‑03] | Lower-level initializations should come first | 1 | R in lack of specific risk

| [L‑04] | Cycle end may be be too soon | 1 | I don't think this is valid

| [L‑05] | Incorrect percentage conversion | 1 | L

| [L‑06] | Missing checks for value of msg.value | 1 | Invalid: https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/MinipoolManager.sol#L401

| [L‑07] | Loss of precision | 2 | L

| [L‑08] | Signatures vulnerable to malleability attacks | 1 | L | [L‑09] | require() should be used instead of assert() | 1 | R

| [L‑10] | Open TODOs | 1 | NC

| [N‑01] | Common code should be refactored | 1 | R

| [N‑02] | String constants used in multiple places should be defined as constants | 1 | R

| [N‑03] | Constants in comparisons should appear on the left side | 1 | R

| [N‑04] | Inconsistent address separator in storage names | 1 | NC

| [N‑05] | Confusing function name | 1 | NC

| [N‑06] | Misplaced punctuation | 1 | NC

| [N‑07] | Upgradeable contract is missing a __gap[50] storage variable to allow for new storage variables in later versions | 1 | Disputing for TokenAvax as it's the child contract

| [N‑08] | Import declarations should import specific identifiers, rather than the whole file | 13 | NC

| [N‑09] | Missing initializer modifier on constructor | 1 | R

| [N‑10] | The nonReentrant modifier should occur before all other modifiers | 2 | R

| [N‑11] | override function arguments that are unused should have the variable name removed or commented out to avoid compiler warnings | 1 | NC

| [N‑12] | constants should be defined rather than using magic numbers | 2 | R

| [N‑13] | Missing event and or timelock for critical parameter change | 1 | NC

| [N‑14] | Events that mark critical parameter changes should contain both the old and the new value | 2 | NC

| [N‑15] | Use a more recent version of solidity | 1 | NC

| [N‑16] | Use a more recent version of solidity | 1 | See N-15

| [N‑17] | Constant redefined elsewhere | 2 | R

| [N‑18] | Lines are too long | 1 | NC

| [N‑19] | Variable names that consist of all capital letters should be reserved for constant/immutable variables | 2 | R

| [N‑20] | Using >/>= without specifying an upper bound is unsafe | 2 | R

| [N‑21] | Typos | 3 | NC

| [N‑22] | File is missing NatSpec | 3 | NC

| [N‑23] | NatSpec is incomplete | 27 | NC

| [N‑24] | Not using the named return variables anywhere in the function is confusing | 1 | R

| [N‑25] | Contracts should have full test coverage | 1 | R

| [N‑26] | Large or complicated code bases should implement fuzzing tests | 1 | R

| [N‑27] | Function ordering does not follow the Solidity style guide | 15 | NC

| [N‑28] | Contract does not follow the Solidity style guide's suggested layout ordering | 9 | NC

#1 - GalloDaSballo

2023-02-03T16:33:08Z

2L from dups

4L 15R 15NC

#2 - GalloDaSballo

2023-02-03T16:33:20Z

6L 15R 15NC including dups

#3 - c4-judge

2023-02-03T22:09:06Z

GalloDaSballo marked the issue as grade-a

#4 - c4-judge

2023-02-03T22:09:13Z

GalloDaSballo marked the issue as selected for report

#5 - c4-judge

2023-02-09T09:33:19Z

GalloDaSballo marked the issue as not selected for report

#6 - c4-judge

2023-02-09T09:35:01Z

GalloDaSballo marked the issue as selected for report

#7 - GalloDaSballo

2023-02-09T09:35:35Z

Best report by far, so far I though the second best was the best (this one scores above 100%)

Well played

Findings Information

Labels

bug
G (Gas Optimization)
grade-a
G-03

Awards

718.9381 USDC - $718.94

External Links

Summary

Gas Optimizations

IssueInstancesTotal Gas Saved
[G‑01]Modifier unnecessarily looks up storage variables24200
[G‑02]Batch pause and resume to save gas1600
[G‑03]Cheaper to calculate than to read and update12100
[G‑04]Use 1 and 2 instead of bools to save gas117100
[G‑05]Wastes gas to clear the old newGuardian117100
[G‑06]Cheaper to calculate domain separator every time14200
[G‑07]Contract calculations having to do with msg.value can be unchecked160
[G‑08]State variables should be cached in stack variables rather than re-reading them from storage4388
[G‑09]The result of function calls should be cached rather than re-calling the function2-
[G‑10]<x> += <y> costs more gas than <x> = <x> + <y> for state variables6678
[G‑11]internal functions only called once can be inlined to save gas5100
[G‑12]++i/i++ should be unchecked{++i}/unchecked{i++} when it is not possible for them to overflow, as is the case when used in for- and while-loops7420
[G‑13]keccak256() should only need to be called on a specific string literal once743108
[G‑14]Optimize names to save gas12264
[G‑15]Use a more recent version of solidity2-
[G‑16]String literals passed to abi.encode()/abi.encodePacked() should not be split by commas3-
[G‑17]>= costs less gas than >26
[G‑18]Splitting require() statements that use && saves gas13
[G‑19]Usage of uints/ints smaller than 32 bytes (256 bits) incurs overhead3-
[G‑20]Don't compare boolean expressions to boolean literals327
[G‑21]Stack variable used as a cheaper cache for a state variable is only used once13
[G‑22]Superfluous event fields1-
[G‑23]Functions guaranteed to revert when called by normal users can be marked payable631323

Total: 197 instances over 23 issues with 51680 gas saved

Gas totals use lower bounds of ranges and count two iterations of each for-loop. All values above are runtime, not deployment, values; deployment values are listed in the individual issue descriptions. The table above as well as its gas numbers do not include any of the excluded findings.

Gas Optimizations

[G‑01] Modifier unnecessarily looks up storage variables

If the else-condition isn't required, isContract will have been looked up unnecessarily

There are 2 instances of this issue:

File: /contracts/contract/BaseAbstract.sol

40   	modifier guardianOrRegisteredContract() {
41   		bool isContract = getBool(keccak256(abi.encodePacked("contract.exists", msg.sender)));
42   		bool isGuardian = msg.sender == gogoStorage.getGuardian();
43   
44   		if (!(isGuardian || isContract)) {
45:  			revert MustBeGuardianOrValidContract();

51   	modifier guardianOrSpecificRegisteredContract(string memory contractName, address contractAddress) {
52   		bool isContract = contractAddress == getAddress(keccak256(abi.encodePacked("contract.address", contractName)));
53   		bool isGuardian = msg.sender == gogoStorage.getGuardian();
54   
55   		if (!(isGuardian || isContract)) {
56:  			revert MustBeGuardianOrValidContract();

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/BaseAbstract.sol#L40-L45

[G‑02] Batch pause and resume to save gas

If pause/resumeEverything() are made to be functions on ProtocolDAO, you'll save the overhead of six external calls (100 gas each)

There is 1 instance of this issue:

File: /contracts/contract/Ocyticus.sol

37   	function pauseEverything() external onlyDefender {
38   		ProtocolDAO dao = ProtocolDAO(getContractAddress("ProtocolDAO"));
39   		dao.pauseContract("TokenggAVAX");
40   		dao.pauseContract("MinipoolManager");
41   		dao.pauseContract("Staking");
42   		disableAllMultisigs();
43   	}
44   
45   	/// @notice Reestablish all contract's abilities
46   	/// @dev Multisigs will need to be enabled seperately, we dont know which ones to enable
47   	function resumeEverything() external onlyDefender {
48   		ProtocolDAO dao = ProtocolDAO(getContractAddress("ProtocolDAO"));
49   		dao.resumeContract("TokenggAVAX");
50   		dao.resumeContract("MinipoolManager");
51   		dao.resumeContract("Staking");
52:  	}

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Ocyticus.sol#L37-L52

[G‑03] Cheaper to calculate than to read and update

The new value is known, so it's cheaper to set the new multisig count directly, rather than having addUint() look up the old value, then overwrite it

There is 1 instance of this issue:

File: /contracts/contract/MultisigManager.sol

48   		setUint(keccak256(abi.encodePacked("multisig.index", addr)), index + 1);
49:  		addUint(keccak256("multisig.count"), 1);

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/MultisigManager.sol#L48-L49

[G‑04] Use 1 and 2 instead of bools to save gas

Using 1 and 2 prevents the storage slot from being marked as cleared, which will save gas when later setting the value back to true, since it will already be non-zero and thus a cheaper storage write than if it had been zero

There is 1 instance of this issue:

File: /contracts/contract/Storage.sol

16:  	mapping(bytes32 => bool) private booleanStorage;

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Storage.sol#L16

[G‑05] Wastes gas to clear the old newGuardian

Clearing the value means the next time it's changed to something else, the operation is more expensive than if it had been changed from the old value

There is 1 instance of this issue:

File: /contracts/contract/Storage.sol

64:  		delete newGuardian;

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Storage.sol#L64

[G‑06] Cheaper to calculate domain separator every time

Since INITIAL_CHAIN_ID and INITIAL_DOMAIN_SEPARATOR are no longer immutable, but are state variables, you end up looking up their value every time, which incurs a very large gas penalty. It's cheaper to just calculate it every time, or to use OpenZeppelin's version which is optimized for this case

There is 1 instance of this issue:

File: /contracts/contract/tokens/upgradeable/ERC20Upgradeable.sol

163: 		return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/upgradeable/ERC20Upgradeable.sol#L163

[G‑07] Contract calculations having to do with msg.value can be unchecked

This is because msg.value will never be more than type(uint256).max

There is 1 instance of this issue:

File: /contracts/contract/tokens/TokenggAVAX.sol

143  		uint256 totalAmt = msg.value;
144: 		if (totalAmt != (baseAmt + rewardAmt) || baseAmt > stakingTotalAssets) {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/TokenggAVAX.sol#L143-L144

[G‑08] State variables should be cached in stack variables rather than re-reading them from storage

The instances below point to the second+ access of a state variable within a function. Caching of a state variable replaces each Gwarmaccess (100 gas) with a much cheaper stack read. Other less obvious fixes/optimizations include having local memory caches of state variable structs, or having local caches of state variable contracts/addresses.

There are 4 instances of this issue:

File: contracts/contract/tokens/TokenggAVAX.sol

/// @audit rewardsCycleLength on line 76
/// @audit rewardsCycleLength on line 78
78:   		rewardsCycleEnd = (block.timestamp.safeCastTo32() / rewardsCycleLength) * rewardsCycleLength;

/// @audit rewardsCycleLength on line 102
/// @audit rewardsCycleLength on line 102
102:  		uint32 nextRewardsCycleEnd = ((timestamp + rewardsCycleLength) / rewardsCycleLength) * rewardsCycleLength;

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/TokenggAVAX.sol#L78

[G‑09] The result of function calls should be cached rather than re-calling the function

The instances below point to the second+ call of the function within a single function

There are 2 instances of this issue:

File: contracts/contract/ClaimNodeOp.sol

/// @audit rewardsPool.getRewardsCycleCount() on line 61
65:   		if (staking.getLastRewardsCycleCompleted(stakerAddr) == rewardsPool.getRewardsCycleCount()) {

/// @audit rewardsPool.getRewardsCycleCount() on line 61
68:   		staking.setLastRewardsCycleCompleted(stakerAddr, rewardsPool.getRewardsCycleCount());

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/ClaimNodeOp.sol#L65

[G‑10] <x> += <y> costs more gas than <x> = <x> + <y> for state variables

Using the addition operator instead of plus-equals saves 113 gas

There are 6 instances of this issue:

File: contracts/contract/tokens/TokenggAVAX.sol

149:  		stakingTotalAssets -= baseAmt;

160:  		stakingTotalAssets += assets;

245:  		totalReleasedAssets -= amount;

252:  		totalReleasedAssets += amount;

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/TokenggAVAX.sol#L149

File: contracts/contract/tokens/upgradeable/ERC20Upgradeable.sol

184:  		totalSupply += amount;

201:  			totalSupply -= amount;

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/upgradeable/ERC20Upgradeable.sol#L184

[G‑11] internal functions only called once can be inlined to save gas

Not inlining costs 20 to 40 gas because of two extra JUMP instructions and additional stack operations needed for function calls.

There are 5 instances of this issue:

File: contracts/contract/RewardsPool.sol

82    	function inflate() internal {
83:   		ProtocolDAO dao = ProtocolDAO(getContractAddress("ProtocolDAO"));

110   	function increaseRewardsCycleCount() internal {
111:  		addUint(keccak256("RewardsPool.RewardsCycleCount"), 1);

203   	function distributeMultisigAllotment(
204   		uint256 allotment,
205   		Vault vault,
206:  		TokenGGP ggp

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/RewardsPool.sol#L82-L83

File: contracts/contract/Staking.sol

87:   	function increaseGGPStake(address stakerAddr, uint256 amount) internal {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Staking.sol#L87

File: contracts/contract/tokens/TokenggAVAX.sol

248   	function afterDeposit(
249   		uint256 amount,
250:  		uint256 /* shares */

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/TokenggAVAX.sol#L248-L250

[G‑12] ++i/i++ should be unchecked{++i}/unchecked{i++} when it is not possible for them to overflow, as is the case when used in for- and while-loops

The unchecked keyword is new in solidity version 0.8.0, so this only applies to that version or higher, which these instances are. This saves 30-40 gas per loop

There are 7 instances of this issue:

File: contracts/contract/MinipoolManager.sol

619:  		for (uint256 i = offset; i < max; i++) {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/MinipoolManager.sol#L619

File: contracts/contract/MultisigManager.sol

84:   		for (uint256 i = 0; i < total; i++) {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/MultisigManager.sol#L84

File: contracts/contract/Ocyticus.sol

61:   		for (uint256 i = 0; i < count; i++) {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Ocyticus.sol#L61

File: contracts/contract/RewardsPool.sol

74:   		for (uint256 i = 0; i < inflationIntervalsElapsed; i++) {

215:  		for (uint256 i = 0; i < count; i++) {

230:  		for (uint256 i = 0; i < enabledMultisigs.length; i++) {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/RewardsPool.sol#L74

File: contracts/contract/Staking.sol

428:  		for (uint256 i = offset; i < max; i++) {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Staking.sol#L428

[G‑13] keccak256() should only need to be called on a specific string literal once

It should be saved to an immutable variable, and the variable used instead. If the hash is being used as a part of a function selector, the cast to bytes4 should also only be done once

There are 74 instances of this issue:

File: contracts/contract/ClaimNodeOp.sol

36:   		return getUint(keccak256("NOPClaim.RewardsCycleTotal"));

41:   		setUint(keccak256("NOPClaim.RewardsCycleTotal"), amount);

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/ClaimNodeOp.sol#L36

File: contracts/contract/MinipoolManager.sol

248:  			minipoolIndex = int256(getUint(keccak256("minipool.count")));

252:  			addUint(keccak256("minipool.count"), 1);

333:  		addUint(keccak256("MinipoolManager.TotalAVAXLiquidStakerAmt"), avaxLiquidStakerAmt);

433:  		subUint(keccak256("MinipoolManager.TotalAVAXLiquidStakerAmt"), avaxLiquidStakerAmt);

512:  		subUint(keccak256("MinipoolManager.TotalAVAXLiquidStakerAmt"), avaxLiquidStakerAmt);

542:  		return getUint(keccak256("MinipoolManager.TotalAVAXLiquidStakerAmt"));

612:  		uint256 totalMinipools = getUint(keccak256("minipool.count"));

635:  		return getUint(keccak256("minipool.count"));

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/MinipoolManager.sol#L248

File: contracts/contract/MultisigManager.sol

40:   		uint256 index = getUint(keccak256("multisig.count"));

49:   		addUint(keccak256("multisig.count"), 1);

81:   		uint256 total = getUint(keccak256("multisig.count"));

103:  		return getUint(keccak256("multisig.count"));

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/MultisigManager.sol#L40

File: contracts/contract/Oracle.sol

29:   		setAddress(keccak256("Oracle.OneInch"), addr);

38:   		IOneInch oneinch = IOneInch(getAddress(keccak256("Oracle.OneInch")));

47:   		price = getUint(keccak256("Oracle.GGPPriceInAVAX"));

51:   		timestamp = getUint(keccak256("Oracle.GGPTimestamp"));

58:   		uint256 lastTimestamp = getUint(keccak256("Oracle.GGPTimestamp"));

65:   		setUint(keccak256("Oracle.GGPPriceInAVAX"), price);

66:   		setUint(keccak256("Oracle.GGPTimestamp"), timestamp);

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Oracle.sol#L29

File: contracts/contract/ProtocolDAO.sol

24:   		if (getBool(keccak256("ProtocolDAO.initialized"))) {

27:   		setBool(keccak256("ProtocolDAO.initialized"), true);

30:   		setUint(keccak256("ProtocolDAO.RewardsEligibilityMinSeconds"), 14 days);

33:   		setUint(keccak256("ProtocolDAO.RewardsCycleSeconds"), 28 days); // The time in which a claim period will span in seconds - 28 days by default

34:   		setUint(keccak256("ProtocolDAO.TotalGGPCirculatingSupply"), 18_000_000 ether);

35:   		setUint(keccak256("ProtocolDAO.ClaimingContractPct.ClaimMultisig"), 0.20 ether);

36:   		setUint(keccak256("ProtocolDAO.ClaimingContractPct.ClaimNodeOp"), 0.70 ether);

37:   		setUint(keccak256("ProtocolDAO.ClaimingContractPct.ClaimProtocolDAO"), 0.10 ether);

40:   		setUint(keccak256("ProtocolDAO.InflationIntervalSeconds"), 1 days);

41:   		setUint(keccak256("ProtocolDAO.InflationIntervalRate"), 1000133680617113500); // 5% annual calculated on a daily interval - Calculate in js example: let dailyInflation = web3.utils.toBN((1 + 0.05) ** (1 / (365)) * 1e18);

44:   		setUint(keccak256("ProtocolDAO.TargetGGAVAXReserveRate"), 0.1 ether); // 10% collateral held in reserve

47:   		setUint(keccak256("ProtocolDAO.MinipoolMinAVAXStakingAmt"), 2_000 ether);

48:   		setUint(keccak256("ProtocolDAO.MinipoolNodeCommissionFeePct"), 0.15 ether);

49:   		setUint(keccak256("ProtocolDAO.MinipoolMaxAVAXAssignment"), 10_000 ether);

50:   		setUint(keccak256("ProtocolDAO.MinipoolMinAVAXAssignment"), 1_000 ether);

51:   		setUint(keccak256("ProtocolDAO.ExpectedAVAXRewardsRate"), 0.1 ether); // Annual rate as pct of 1 avax

52:   		setUint(keccak256("ProtocolDAO.MinipoolCancelMoratoriumSeconds"), 5 days);

55:   		setUint(keccak256("ProtocolDAO.MaxCollateralizationRatio"), 1.5 ether);

56:   		setUint(keccak256("ProtocolDAO.MinCollateralizationRatio"), 0.1 ether);

82:   		return getUint(keccak256("ProtocolDAO.RewardsEligibilityMinSeconds"));

87:   		return getUint(keccak256("ProtocolDAO.RewardsCycleSeconds"));

92:   		return getUint(keccak256("ProtocolDAO.TotalGGPCirculatingSupply"));

97:   		return setUint(keccak256("ProtocolDAO.TotalGGPCirculatingSupply"), amount);

117:  		uint256 rate = getUint(keccak256("ProtocolDAO.InflationIntervalRate"));

124:  		return getUint(keccak256("ProtocolDAO.InflationIntervalSeconds"));

131:  		return getUint(keccak256("ProtocolDAO.MinipoolMinAVAXStakingAmt"));

136:  		return getUint(keccak256("ProtocolDAO.MinipoolNodeCommissionFeePct"));

141:  		return getUint(keccak256("ProtocolDAO.MinipoolMaxAVAXAssignment"));

146:  		return getUint(keccak256("ProtocolDAO.MinipoolMinAVAXAssignment"));

151:  		return getUint(keccak256("ProtocolDAO.MinipoolCancelMoratoriumSeconds"));

157:  		setUint(keccak256("ProtocolDAO.ExpectedAVAXRewardsRate"), rate);

162:  		return getUint(keccak256("ProtocolDAO.ExpectedAVAXRewardsRate"));

169:  		return getUint(keccak256("ProtocolDAO.MaxCollateralizationRatio"));

174:  		return getUint(keccak256("ProtocolDAO.MinCollateralizationRatio"));

182:  		return getUint(keccak256("ProtocolDAO.TargetGGAVAXReserveRate"));

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/ProtocolDAO.sol#L24

File: contracts/contract/RewardsPool.sol

35:   		if (getBool(keccak256("RewardsPool.initialized"))) {

38:   		setBool(keccak256("RewardsPool.initialized"), true);

40:   		setUint(keccak256("RewardsPool.RewardsCycleStartTime"), block.timestamp);

41:   		setUint(keccak256("RewardsPool.InflationIntervalStartTime"), block.timestamp);

49:   		return getUint(keccak256("RewardsPool.InflationIntervalStartTime"));

98:   		addUint(keccak256("RewardsPool.InflationIntervalStartTime"), inflationIntervalElapsedSeconds);

99:   		setUint(keccak256("RewardsPool.RewardsCycleTotalAmt"), newTokens);

106:  		return getUint(keccak256("RewardsPool.RewardsCycleCount"));

111:  		addUint(keccak256("RewardsPool.RewardsCycleCount"), 1);

116:  		return getUint(keccak256("RewardsPool.RewardsCycleStartTime"));

121:  		return getUint(keccak256("RewardsPool.RewardsCycleTotalAmt"));

164:  		setUint(keccak256("RewardsPool.RewardsCycleStartTime"), block.timestamp);

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/RewardsPool.sol#L35

File: contracts/contract/Staking.sol

73:   		return getUint(keccak256("staker.count"));

348:  			stakerIndex = int256(getUint(keccak256("staker.count")));

349:  			addUint(keccak256("staker.count"), 1);

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Staking.sol#L73

File: contracts/contract/tokens/upgradeable/ERC20Upgradeable.sol

139:  								keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"),

170:  					keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),

172:  					keccak256("1"),

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/upgradeable/ERC20Upgradeable.sol#L139

[G‑14] Optimize names to save gas

public/external function names and public member variable names can be optimized to save gas. See this link for an example of how it works. Below are the interfaces/abstract contracts that can be optimized so that the most frequently-called functions use the least amount of gas possible during method lookup. Method IDs that have two leading zero bytes can save 128 gas each during deployment, and renaming functions to have lower method IDs will save 22 gas per call, per sorted position shifted

There are 12 instances of this issue:

File: contracts/contract/ClaimNodeOp.sol

/// @audit getRewardsCycleTotal(), setRewardsCycleTotal(), isEligible(), calculateAndDistributeRewards(), claimAndRestake()
17:   contract ClaimNodeOp is Base {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/ClaimNodeOp.sol#L17

File: contracts/contract/ClaimProtocolDAO.sol

/// @audit spend()
10:   contract ClaimProtocolDAO is Base {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/ClaimProtocolDAO.sol#L10

File: contracts/contract/MinipoolManager.sol

/// @audit receiveWithdrawalAVAX(), createMinipool(), cancelMinipool(), withdrawMinipoolFunds(), canClaimAndInitiateStaking(), claimAndInitiateStaking(), recordStakingStart(), recordStakingEnd(), recreateMinipool(), recordStakingError(), cancelMinipoolByMultisig(), finishFailedMinipoolByMultisig(), getTotalAVAXLiquidStakerAmt(), calculateGGPSlashAmt(), getExpectedAVAXRewardsAmt(), getIndexOf(), getMinipoolByNodeID(), getMinipool(), getMinipools(), getMinipoolCount()
57:   contract MinipoolManager is Base, ReentrancyGuard, IWithdrawer {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/MinipoolManager.sol#L57

File: contracts/contract/MultisigManager.sol

/// @audit registerMultisig(), enableMultisig(), disableMultisig(), requireNextActiveMultisig(), getIndexOf(), getCount(), getMultisig()
17:   contract MultisigManager is Base {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/MultisigManager.sol#L17

File: contracts/contract/Ocyticus.sol

/// @audit addDefender(), removeDefender(), pauseEverything(), resumeEverything(), disableAllMultisigs()
10:   contract Ocyticus is Base {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Ocyticus.sol#L10

File: contracts/contract/Oracle.sol

/// @audit setOneInch(), getGGPPriceInAVAXFromOneInch(), getGGPPriceInAVAX(), setGGPPriceInAVAX()
17:   contract Oracle is Base {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Oracle.sol#L17

File: contracts/contract/ProtocolDAO.sol

/// @audit initialize(), getContractPaused(), pauseContract(), resumeContract(), getRewardsEligibilityMinSeconds(), getRewardsCycleSeconds(), getTotalGGPCirculatingSupply(), setTotalGGPCirculatingSupply(), getClaimingContractPct(), setClaimingContractPct(), getInflationIntervalRate(), getInflationIntervalSeconds(), getMinipoolMinAVAXStakingAmt(), getMinipoolNodeCommissionFeePct(), getMinipoolMaxAVAXAssignment(), getMinipoolMinAVAXAssignment(), getMinipoolCancelMoratoriumSeconds(), setExpectedAVAXRewardsRate(), getExpectedAVAXRewardsRate(), getMaxCollateralizationRatio(), getMinCollateralizationRatio(), getTargetGGAVAXReserveRate(), registerContract(), unregisterContract(), upgradeExistingContract()
9:    contract ProtocolDAO is Base {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/ProtocolDAO.sol#L9

File: contracts/contract/RewardsPool.sol

/// @audit initialize(), getInflationIntervalStartTime(), getInflationIntervalsElapsed(), getInflationAmt(), getRewardsCycleCount(), getRewardsCycleStartTime(), getRewardsCycleTotalAmt(), getRewardsCyclesElapsed(), getClaimingContractDistribution(), canStartRewardsCycle(), startRewardsCycle()
15:   contract RewardsPool is Base {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/RewardsPool.sol#L15

File: contracts/contract/Staking.sol

/// @audit getTotalGGPStake(), getStakerCount(), getGGPStake(), getAVAXStake(), increaseAVAXStake(), decreaseAVAXStake(), getAVAXAssigned(), increaseAVAXAssigned(), decreaseAVAXAssigned(), getAVAXAssignedHighWater(), increaseAVAXAssignedHighWater(), resetAVAXAssignedHighWater(), getMinipoolCount(), increaseMinipoolCount(), decreaseMinipoolCount(), getRewardsStartTime(), setRewardsStartTime(), getGGPRewards(), increaseGGPRewards(), decreaseGGPRewards(), getLastRewardsCycleCompleted(), setLastRewardsCycleCompleted(), getMinimumGGPStake(), getCollateralizationRatio(), getEffectiveRewardsRatio(), getEffectiveGGPStaked(), stakeGGP(), restakeGGP(), withdrawGGP(), slashGGP(), requireValidStaker(), getIndexOf(), getStaker(), getStakers()
30:   contract Staking is Base {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Staking.sol#L30

File: contracts/contract/Storage.sol

/// @audit setGuardian(), getGuardian(), confirmGuardian(), getAddress(), getBool(), getBytes(), getBytes32(), getInt(), getString(), getUint(), setAddress(), setBool(), setBytes(), setBytes32(), setInt(), setString(), setUint(), deleteAddress(), deleteBool(), deleteBytes(), deleteBytes32(), deleteInt(), deleteString(), deleteUint(), addUint(), subUint()
7:    contract Storage {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Storage.sol#L7

File: contracts/contract/tokens/TokenggAVAX.sol

/// @audit initialize(), syncRewards(), amountAvailableForStaking(), depositFromStaking(), withdrawForStaking(), depositAVAX(), withdrawAVAX(), redeemAVAX()
24:   contract TokenggAVAX is Initializable, ERC4626Upgradeable, UUPSUpgradeable, BaseUpgradeable {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/TokenggAVAX.sol#L24

File: contracts/contract/Vault.sol

/// @audit depositAVAX(), withdrawAVAX(), transferAVAX(), depositToken(), withdrawToken(), transferToken(), balanceOf(), balanceOfToken(), addAllowedToken(), removeAllowedToken()
19:   contract Vault is Base, ReentrancyGuard {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Vault.sol#L19

[G‑15] Use a more recent version of solidity

Use a solidity version of at least 0.8.2 to get simple compiler automatic inlining Use a solidity version of at least 0.8.3 to get better struct packing and cheaper multiple storage reads Use a solidity version of at least 0.8.4 to get custom errors, which are cheaper at deployment than revert()/require() strings Use a solidity version of at least 0.8.10 to have external calls skip contract existence checks if the external call has a return value

There are 2 instances of this issue:

File: contracts/contract/tokens/upgradeable/ERC20Upgradeable.sol

2:    pragma solidity >=0.8.0;

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/upgradeable/ERC20Upgradeable.sol#L2

File: contracts/contract/tokens/upgradeable/ERC4626Upgradeable.sol

2:    pragma solidity >=0.8.0;

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/upgradeable/ERC4626Upgradeable.sol#L2

[G‑16] String literals passed to abi.encode()/abi.encodePacked() should not be split by commas

String literals can be split into multiple parts and still be considered as a single string literal. Adding commas between each chunk makes it no longer a single string, and instead multiple strings. EACH new comma costs 21 gas due to stack operations and separate MSTOREs

There are 3 instances of this issue:

File: contracts/contract/tokens/TokenggAVAX.sol

/// @audit 1 commas
59:   		if (amt > 0 && getBool(keccak256(abi.encodePacked("contract.paused", "TokenggAVAX")))) {

/// @audit 1 commas
207:  		if (getBool(keccak256(abi.encodePacked("contract.paused", "TokenggAVAX")))) {

/// @audit 1 commas
217:  		if (getBool(keccak256(abi.encodePacked("contract.paused", "TokenggAVAX")))) {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/TokenggAVAX.sol#L59

[G‑17] >= costs less gas than >

The compiler uses opcodes GT and ISZERO for solidity code that uses >, but only requires LT for >=, which saves 3 gas

There are 2 instances of this issue:

File: contracts/contract/tokens/TokenggAVAX.sol

212:  		return assets > avail ? avail : assets;

222:  		return shares > avail ? avail : shares;

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/TokenggAVAX.sol#L212

[G‑18] Splitting require() statements that use && saves gas

See this issue which describes the fact that there is a larger deployment gas cost, but with enough runtime calls, the change ends up being cheaper by 3 gas

There is 1 instance of this issue:

File: contracts/contract/tokens/upgradeable/ERC20Upgradeable.sol

154:  			require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/upgradeable/ERC20Upgradeable.sol#L154

[G‑19] Usage of uints/ints smaller than 32 bytes (256 bits) incurs overhead

When using elements that are smaller than 32 bytes, your contract’s gas usage may be higher. This is because the EVM operates on 32 bytes at a time. Therefore, if the element is smaller than that, the EVM must use more operations in order to reduce the size of the element from 32 bytes to the desired size.

https://docs.soliditylang.org/en/v0.8.11/internals/layout_in_storage.html Each operation involving a uint8 costs an extra 22-28 gas (depending on whether the other operand is also a variable of type uint8) as compared to ones involving uint256, due to the compiler having to clear the higher bits of the memory word before operating on the uint8, as well as the associated stack operations of doing so. Use a larger size then downcast where needed

There are 3 instances of this issue:

File: contracts/contract/tokens/TokenggAVAX.sol

/// @audit uint32 rewardsCycleLength
76:   		rewardsCycleLength = 14 days;

/// @audit uint32 rewardsCycleEnd
78:   		rewardsCycleEnd = (block.timestamp.safeCastTo32() / rewardsCycleLength) * rewardsCycleLength;

/// @audit uint192 lastRewardsAmt
104:  		lastRewardsAmt = nextRewardsAmt.safeCastTo192();

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/TokenggAVAX.sol#L76

[G‑20] Don't compare boolean expressions to boolean literals

if (<x> == true) => if (<x>), if (<x> == false) => if (!<x>)

There are 3 instances of this issue:

File: contracts/contract/BaseAbstract.sol

25:   		if (getBool(keccak256(abi.encodePacked("contract.exists", msg.sender))) == false) {

74:   		if (enabled == false) {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/BaseAbstract.sol#L25

File: contracts/contract/Storage.sol

29:   		if (booleanStorage[keccak256(abi.encodePacked("contract.exists", msg.sender))] == false && msg.sender != guardian) {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Storage.sol#L29

[G‑21] Stack variable used as a cheaper cache for a state variable is only used once

If the variable is only accessed once, it's cheaper to use the state variable directly that one time, and save the 3 gas the extra stack assignment would spend

There is 1 instance of this issue:

File: contracts/contract/tokens/TokenggAVAX.sol

97:   		uint256 stakingTotalAssets_ = stakingTotalAssets;

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/TokenggAVAX.sol#L97

[G‑22] Superfluous event fields

block.timestamp and block.number are added to event information by default so adding them manually wastes gas

There is 1 instance of this issue:

File: contracts/contract/Oracle.sol

21:   	event GGPPriceUpdated(uint256 indexed price, uint256 timestamp);

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Oracle.sol#L21

[G‑23] Functions guaranteed to revert when called by normal users can be marked payable

If a function modifier such as onlyOwner is used, the function will revert if a normal user tries to pay the function. Marking the function as payable will lower the gas cost for legitimate callers because the compiler will not include checks for whether a payment was provided. The extra opcodes avoided are CALLVALUE(2),DUP1(3),ISZERO(3),PUSH2(3),JUMPI(10),PUSH1(3),DUP1(3),REVERT(0),JUMPDEST(1),POP(2), which costs an average of about 21 gas per call to the function, in addition to the extra deployment cost

There are 63 instances of this issue:

File: contracts/contract/BaseUpgradeable.sol

10:   	function __BaseUpgradeable_init(Storage gogoStorageAddress) internal onlyInitializing {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/BaseUpgradeable.sol#L10

File: contracts/contract/ClaimNodeOp.sol

40:   	function setRewardsCycleTotal(uint256 amount) public onlySpecificRegisteredContract("RewardsPool", msg.sender) {

56:   	function calculateAndDistributeRewards(address stakerAddr, uint256 totalEligibleGGPStaked) external onlyMultisig {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/ClaimNodeOp.sol#L40

File: contracts/contract/ClaimProtocolDAO.sol

20    	function spend(
21    		string memory invoiceID,
22    		address recipientAddress,
23    		uint256 amount
24:   	) external onlyGuardian {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/ClaimProtocolDAO.sol#L20-L24

File: contracts/contract/MultisigManager.sol

35:   	function registerMultisig(address addr) external onlyGuardian {

55:   	function enableMultisig(address addr) external onlyGuardian {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/MultisigManager.sol#L35

File: contracts/contract/Ocyticus.sol

27:   	function addDefender(address defender) external onlyGuardian {

32:   	function removeDefender(address defender) external onlyGuardian {

37:   	function pauseEverything() external onlyDefender {

47:   	function resumeEverything() external onlyDefender {

55:   	function disableAllMultisigs() public onlyDefender {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Ocyticus.sol#L27

File: contracts/contract/Oracle.sol

28:   	function setOneInch(address addr) external onlyGuardian {

57:   	function setGGPPriceInAVAX(uint256 price, uint256 timestamp) external onlyMultisig {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Oracle.sol#L28

File: contracts/contract/ProtocolDAO.sol

23:   	function initialize() external onlyGuardian {

67:   	function pauseContract(string memory contractName) public onlySpecificRegisteredContract("Ocyticus", msg.sender) {

73:   	function resumeContract(string memory contractName) public onlySpecificRegisteredContract("Ocyticus", msg.sender) {

96:   	function setTotalGGPCirculatingSupply(uint256 amount) public onlySpecificRegisteredContract("RewardsPool", msg.sender) {

107:  	function setClaimingContractPct(string memory claimingContract, uint256 decimal) public onlyGuardian valueNotGreaterThanOne(decimal) {

156:  	function setExpectedAVAXRewardsRate(uint256 rate) public onlyMultisig valueNotGreaterThanOne(rate) {

190:  	function registerContract(address addr, string memory name) public onlyGuardian {

198:  	function unregisterContract(address addr) public onlyGuardian {

209   	function upgradeExistingContract(
210   		address newAddr,
211   		string memory newName,
212   		address existingAddr
213:  	) external onlyGuardian {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/ProtocolDAO.sol#L23

File: contracts/contract/RewardsPool.sol

34:   	function initialize() external onlyGuardian {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/RewardsPool.sol#L34

File: contracts/contract/Staking.sol

110:  	function increaseAVAXStake(address stakerAddr, uint256 amount) public onlySpecificRegisteredContract("MinipoolManager", msg.sender) {

117:  	function decreaseAVAXStake(address stakerAddr, uint256 amount) public onlySpecificRegisteredContract("MinipoolManager", msg.sender) {

133:  	function increaseAVAXAssigned(address stakerAddr, uint256 amount) public onlySpecificRegisteredContract("MinipoolManager", msg.sender) {

140:  	function decreaseAVAXAssigned(address stakerAddr, uint256 amount) public onlySpecificRegisteredContract("MinipoolManager", msg.sender) {

156:  	function increaseAVAXAssignedHighWater(address stakerAddr, uint256 amount) public onlyRegisteredNetworkContract {

163:  	function resetAVAXAssignedHighWater(address stakerAddr) public onlyRegisteredNetworkContract {

180:  	function increaseMinipoolCount(address stakerAddr) public onlySpecificRegisteredContract("MinipoolManager", msg.sender) {

187:  	function decreaseMinipoolCount(address stakerAddr) public onlySpecificRegisteredContract("MinipoolManager", msg.sender) {

204:  	function setRewardsStartTime(address stakerAddr, uint256 time) public onlyRegisteredNetworkContract {

224:  	function increaseGGPRewards(address stakerAddr, uint256 amount) public onlySpecificRegisteredContract("ClaimNodeOp", msg.sender) {

231:  	function decreaseGGPRewards(address stakerAddr, uint256 amount) public onlySpecificRegisteredContract("ClaimNodeOp", msg.sender) {

248:  	function setLastRewardsCycleCompleted(address stakerAddr, uint256 cycleNumber) public onlySpecificRegisteredContract("ClaimNodeOp", msg.sender) {

328:  	function restakeGGP(address stakerAddr, uint256 amount) public onlySpecificRegisteredContract("ClaimNodeOp", msg.sender) {

379:  	function slashGGP(address stakerAddr, uint256 ggpAmt) public onlySpecificRegisteredContract("MinipoolManager", msg.sender) {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Staking.sol#L110

File: contracts/contract/Storage.sol

104:  	function setAddress(bytes32 key, address value) external onlyRegisteredNetworkContract {

108:  	function setBool(bytes32 key, bool value) external onlyRegisteredNetworkContract {

112:  	function setBytes(bytes32 key, bytes calldata value) external onlyRegisteredNetworkContract {

116:  	function setBytes32(bytes32 key, bytes32 value) external onlyRegisteredNetworkContract {

120:  	function setInt(bytes32 key, int256 value) external onlyRegisteredNetworkContract {

124:  	function setString(bytes32 key, string calldata value) external onlyRegisteredNetworkContract {

128:  	function setUint(bytes32 key, uint256 value) external onlyRegisteredNetworkContract {

136:  	function deleteAddress(bytes32 key) external onlyRegisteredNetworkContract {

140:  	function deleteBool(bytes32 key) external onlyRegisteredNetworkContract {

144:  	function deleteBytes(bytes32 key) external onlyRegisteredNetworkContract {

148:  	function deleteBytes32(bytes32 key) external onlyRegisteredNetworkContract {

152:  	function deleteInt(bytes32 key) external onlyRegisteredNetworkContract {

156:  	function deleteString(bytes32 key) external onlyRegisteredNetworkContract {

160:  	function deleteUint(bytes32 key) external onlyRegisteredNetworkContract {

170:  	function addUint(bytes32 key, uint256 amount) external onlyRegisteredNetworkContract {

176:  	function subUint(bytes32 key, uint256 amount) external onlyRegisteredNetworkContract {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Storage.sol#L104

File: contracts/contract/tokens/TokenggAVAX.sol

153:  	function withdrawForStaking(uint256 assets) public onlySpecificRegisteredContract("MinipoolManager", msg.sender) {

255:  	function _authorizeUpgrade(address newImplementation) internal override onlyGuardian {}

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/TokenggAVAX.sol#L153

File: contracts/contract/tokens/upgradeable/ERC20Upgradeable.sol

53    	function __ERC20Upgradeable_init(
54    		string memory _name,
55    		string memory _symbol,
56    		uint8 _decimals
57:   	) internal onlyInitializing {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/upgradeable/ERC20Upgradeable.sol#L53-L57

File: contracts/contract/tokens/upgradeable/ERC4626Upgradeable.sol

29    	function __ERC4626Upgradeable_init(
30    		ERC20 _asset,
31    		string memory _name,
32    		string memory _symbol
33:   	) internal onlyInitializing {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/upgradeable/ERC4626Upgradeable.sol#L29-L33

File: contracts/contract/Vault.sol

61:   	function withdrawAVAX(uint256 amount) external onlyRegisteredNetworkContract nonReentrant {

84:   	function transferAVAX(string memory toContractName, uint256 amount) external onlyRegisteredNetworkContract {

137   	function withdrawToken(
138   		address withdrawalAddress,
139   		ERC20 tokenAddress,
140   		uint256 amount
141:  	) external onlyRegisteredNetworkContract nonReentrant {

166   	function transferToken(
167   		string memory networkContractName,
168   		ERC20 tokenAddress,
169   		uint256 amount
170:  	) external onlyRegisteredNetworkContract {

204:  	function addAllowedToken(address tokenAddress) external onlyGuardian {

208:  	function removeAllowedToken(address tokenAddress) external onlyGuardian {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Vault.sol#L61


Excluded findings

These findings are excluded from awards calculations because there are publicly-available automated tools that find them. The valid ones appear here for completeness

Summary

Gas Optimizations

IssueInstancesTotal Gas Saved
[G‑01]Using calldata instead of memory for read-only arguments in external functions saves gas121440
[G‑02]State variables should be cached in stack variables rather than re-reading them from storage2194
[G‑03]<array>.length should not be looked up in every loop of a for-loop13
[G‑04]Using bools for storage incurs overhead351300
[G‑05]++i costs less gas than i++, especially when it's used in for-loops (--i/i-- too)1050
[G‑06]Using private rather than public for constants, saves gas1-
[G‑07]Division by two should use bit shifting120
[G‑08]Use custom errors rather than revert()/require() strings to save gas4-

Total: 34 instances over 8 issues with 53007 gas saved

Gas totals use lower bounds of ranges and count two iterations of each for-loop. All values above are runtime, not deployment, values; deployment values are listed in the individual issue descriptions. The table above as well as its gas numbers do not include any of the excluded findings.

Gas Optimizations

[G‑01] Using calldata instead of memory for read-only arguments in external functions saves gas

When a function with a memory array is called externally, the abi.decode() step has to use a for-loop to copy each index of the calldata to the memory index. Each iteration of this for-loop costs at least 60 gas (i.e. 60 * <mem_array>.length). Using calldata directly, obliviates the need for such a loop in the contract code and runtime execution. Note that even if an interface defines a function as having memory arguments, it's still valid for implementation contracs to use calldata arguments instead.

If the array is passed to an internal function which passes the array to another internal function where the array is modified and therefore memory is used in the external call, it's still more gass-efficient to use calldata when the external function uses modifiers, since the modifiers may prevent the internal functions from being called. Structs have the same overhead as an array of length one

Note that I've also flagged instances where the function is public but can be marked as external since it's not called by the contract, and cases where a constructor is involved

There are 12 instances of this issue:

File: contracts/contract/ClaimProtocolDAO.sol

/// @audit invoiceID - (valid but excluded finding)
20    	function spend(
21    		string memory invoiceID,
22    		address recipientAddress,
23    		uint256 amount
24:   	) external onlyGuardian {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/ClaimProtocolDAO.sol#L20-L24

File: contracts/contract/ProtocolDAO.sol

/// @audit contractName - (valid but excluded finding)
61:   	function getContractPaused(string memory contractName) public view returns (bool) {

/// @audit contractName - (valid but excluded finding)
67:   	function pauseContract(string memory contractName) public onlySpecificRegisteredContract("Ocyticus", msg.sender) {

/// @audit contractName - (valid but excluded finding)
73:   	function resumeContract(string memory contractName) public onlySpecificRegisteredContract("Ocyticus", msg.sender) {

/// @audit claimingContract - (valid but excluded finding)
102:  	function getClaimingContractPct(string memory claimingContract) public view returns (uint256) {

/// @audit claimingContract - (valid but excluded finding)
107:  	function setClaimingContractPct(string memory claimingContract, uint256 decimal) public onlyGuardian valueNotGreaterThanOne(decimal) {

/// @audit newName - (valid but excluded finding)
209   	function upgradeExistingContract(
210   		address newAddr,
211   		string memory newName,
212   		address existingAddr
213:  	) external onlyGuardian {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/ProtocolDAO.sol#L61

File: contracts/contract/Vault.sol

/// @audit toContractName - (valid but excluded finding)
84:   	function transferAVAX(string memory toContractName, uint256 amount) external onlyRegisteredNetworkContract {

/// @audit networkContractName - (valid but excluded finding)
108   	function depositToken(
109   		string memory networkContractName,
110   		ERC20 tokenContract,
111   		uint256 amount
112:  	) external guardianOrRegisteredContract {

/// @audit networkContractName - (valid but excluded finding)
166   	function transferToken(
167   		string memory networkContractName,
168   		ERC20 tokenAddress,
169   		uint256 amount
170:  	) external onlyRegisteredNetworkContract {

/// @audit networkContractName - (valid but excluded finding)
193:  	function balanceOf(string memory networkContractName) external view returns (uint256) {

/// @audit networkContractName - (valid but excluded finding)
200:  	function balanceOfToken(string memory networkContractName, ERC20 tokenAddress) external view returns (uint256) {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Vault.sol#L84

[G‑02] State variables should be cached in stack variables rather than re-reading them from storage

The instances below point to the second+ access of a state variable within a function. Caching of a state variable replaces each Gwarmaccess (100 gas) with a much cheaper stack read. Other less obvious fixes/optimizations include having local memory caches of state variable structs, or having local caches of state variable contracts/addresses.

There are 2 instances of this issue:

File: contracts/contract/Storage.sol

/// @audit newGuardian on line 57 - (valid but excluded finding)
63:   		guardian = newGuardian;

/// @audit guardian on line 63 - (valid but excluded finding)
65:   		emit GuardianChanged(oldGuardian, guardian);

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Storage.sol#L63

[G‑03] <array>.length should not be looked up in every loop of a for-loop

The overheads outlined below are PER LOOP, excluding the first loop

  • storage arrays incur a Gwarmaccess (100 gas)
  • memory arrays use MLOAD (3 gas)
  • calldata arrays use CALLDATALOAD (3 gas)

Caching the length changes each of these to a DUP<N> (3 gas), and gets rid of the extra DUP<N> needed to store the stack offset

There is 1 instance of this issue:

File: contracts/contract/RewardsPool.sol

/// @audit (valid but excluded finding)
230:  		for (uint256 i = 0; i < enabledMultisigs.length; i++) {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/RewardsPool.sol#L230

[G‑04] Using bools for storage incurs overhead

    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

https://github.com/OpenZeppelin/openzeppelin-contracts/blob/58f635312aa21f947cae5f8578638a85aa2519f5/contracts/security/ReentrancyGuard.sol#L23-L27 Use uint256(1) and uint256(2) for true/false to avoid a Gwarmaccess (100 gas) for the extra SLOAD, and to avoid Gsset (20000 gas) when changing from false to true, after having been true in the past

There are 3 instances of this issue:

File: contracts/contract/Ocyticus.sol

/// @audit (valid but excluded finding)
13:   	mapping(address => bool) public defenders;

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Ocyticus.sol#L13

File: contracts/contract/Storage.sol

/// @audit (valid but excluded finding)
16:   	mapping(bytes32 => bool) private booleanStorage;

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Storage.sol#L16

File: contracts/contract/Vault.sol

/// @audit (valid but excluded finding)
39:   	mapping(address => bool) private allowedTokens;

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Vault.sol#L39

[G‑05] ++i costs less gas than i++, especially when it's used in for-loops (--i/i-- too)

Saves 5 gas per loop

There are 10 instances of this issue:

File: contracts/contract/MinipoolManager.sol

/// @audit (valid but excluded finding)
619:  		for (uint256 i = offset; i < max; i++) {

/// @audit (valid but excluded finding)
623:  				total++;

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/MinipoolManager.sol#L619

File: contracts/contract/MultisigManager.sol

/// @audit (valid but excluded finding)
84:   		for (uint256 i = 0; i < total; i++) {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/MultisigManager.sol#L84

File: contracts/contract/Ocyticus.sol

/// @audit (valid but excluded finding)
61:   		for (uint256 i = 0; i < count; i++) {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Ocyticus.sol#L61

File: contracts/contract/RewardsPool.sol

/// @audit (valid but excluded finding)
74:   		for (uint256 i = 0; i < inflationIntervalsElapsed; i++) {

/// @audit (valid but excluded finding)
215:  		for (uint256 i = 0; i < count; i++) {

/// @audit (valid but excluded finding)
219:  				enabledCount++;

/// @audit (valid but excluded finding)
230:  		for (uint256 i = 0; i < enabledMultisigs.length; i++) {

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/RewardsPool.sol#L74

File: contracts/contract/Staking.sol

/// @audit (valid but excluded finding)
428:  		for (uint256 i = offset; i < max; i++) {

/// @audit (valid but excluded finding)
431:  			total++;

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/Staking.sol#L428

[G‑06] Using private rather than public for constants, saves gas

If needed, the values can be read from the verified contract source code, or if there are multiple values there can be a single getter function that returns a tuple of the values of all currently-public constants. Saves 3406-3606 gas in deployment gas due to the compiler not having to create non-payable getter functions for deployment calldata, not having to store the bytes of the value outside of where it's used, and not adding another entry to the method ID table

There is 1 instance of this issue:

File: contracts/contract/MultisigManager.sol

/// @audit (valid but excluded finding)
27:   	uint256 public constant MULTISIG_LIMIT = 10;

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/MultisigManager.sol#L27

[G‑07] Division by two should use bit shifting

<x> / 2 is the same as <x> >> 1. While the compiler uses the SHR opcode to accomplish both, the version that uses division incurs an overhead of 20 gas due to JUMPs to and from a compiler utility function that introduces checks which can be avoided by using unchecked {} around the division by two

There is 1 instance of this issue:

File: contracts/contract/MinipoolManager.sol

/// @audit (valid but excluded finding)
413:  		uint256 avaxHalfRewards = avaxTotalRewardAmt / 2;

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/MinipoolManager.sol#L413

[G‑08] Use custom errors rather than revert()/require() strings to save gas

Custom errors are available from solidity version 0.8.4. Custom errors save ~50 gas each time they're hit by avoiding having to allocate and store the revert string. Not defining the strings also save deployment gas

There are 4 instances of this issue:

File: contracts/contract/tokens/upgradeable/ERC20Upgradeable.sol

/// @audit (valid but excluded finding)
127:  		require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");

/// @audit (valid but excluded finding)
154:  			require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/upgradeable/ERC20Upgradeable.sol#L127

File: contracts/contract/tokens/upgradeable/ERC4626Upgradeable.sol

/// @audit (valid but excluded finding)
44:   		require((shares = previewDeposit(assets)) != 0, "ZERO_SHARES");

/// @audit (valid but excluded finding)
103:  		require((assets = previewRedeem(shares)) != 0, "ZERO_ASSETS");

https://github.com/code-423n4/2022-12-gogopool/blob/aec9928d8bdce8a5a4efe45f54c39d4fc7313731/contracts/contract/tokens/upgradeable/ERC4626Upgradeable.sol#L44

#0 - GalloDaSballo

2023-01-25T19:31:44Z

| [G‑01] | Modifier unnecessarily looks up storage variables | 2 | 4200 | 4.2k

| [G‑02] | Batch pause and resume to save gas | 1 | 600 | 600

| [G‑03] | Cheaper to calculate than to read and update | 1 | 2100 | 100 (it's already hot)

| [G‑04] | Use 1 and 2 instead of bools to save gas | 1 | 17100 | Will not award, but it's a great saving for the Sponsor IMO

| [G‑05] | Wastes gas to clear the old newGuardian | 1 | 17100 | See G-04 as well

| [G‑06] | Cheaper to calculate domain separator every time | 1 | 4200 | Let's say 2.1k gas

| [G‑07] | Contract calculations having to do with msg.value can be unchecked | 1 | 60 | 20

| [G‑08] | State variables should be cached in stack variables rather than re-reading them from storage | 4 | 388 | 400

| [G‑09] | The result of function calls should be cached rather than re-calling the function | 2 | - | 340 * 2 680

Rest are marginal, let's say 300 gas

8400

#1 - GalloDaSballo

2023-02-03T18:16:00Z

Btw check these for += it's not that impactful https://gist.github.com/GalloDaSballo/8521d2cb6dcc4eea5523a06957f0ea15

#2 - c4-judge

2023-02-03T19:12:01Z

GalloDaSballo marked the issue as grade-a

#3 - c4-judge

2023-02-03T19:12:11Z

GalloDaSballo marked the issue as selected for report

#4 - c4-judge

2023-02-03T19:12:39Z

GalloDaSballo marked the issue as grade-b

#5 - c4-judge

2023-02-03T19:12:46Z

GalloDaSballo marked the issue as grade-a

#6 - c4-judge

2023-02-08T08:06:52Z

GalloDaSballo marked the issue as not selected for report

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax Ā© 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter