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
Rank: 7/111
Findings: 7
Award: $2,383.63
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: hansfriese
Also found by: Allarious, HollaDieWaldfee, betweenETHlines, chaduke, unforgiven, wagmi
949.1258 USDC - $949.13
https://github.com/code-423n4/2022-12-gogopool/blob/main/contracts/contract/Staking.sol#L165 https://github.com/code-423n4/2022-12-gogopool/blob/main/contracts/contract/ClaimNodeOp.sol#L77
Staking.sol / ClaimNodeOp.sol
resetAvaxAssignedHighWater can be frontrun to increase rewards
Currently, the function resetAvaxAssignedHighWater is called in claimNodeOp during
calculateAndDistributeRewards. This function sets
avaxAssignedHighWaterto the aggregate of all
avaxAssigned`.
A malicious party can therefore recognize the function-call calculateAndDistributeRewards
in the mempool and call createMinipool
with higher gas than the aforementioned call. This will result in avaxAssigned
increased which will then increase avaxAssignedHighWater
in the following call. Moreover, the malicious party can simply call cancelMinipool
immediately afterwards (the grace period can be circumvented if the attacker has other regular minipools) which will result in status quo for the attacker but with increased avaxAssignedHighWater
. We think that this is a valid scenario which is unwanted by the protocol owner because this is an unnatural increase of avaxAssignedHighWater
which will assign more rewards to the attacker than he deserves.
VSCode; manual inspection
While it is hard to mitigate this attack vector, it might make sense to reconsider the whole logic regarding avaxAssignedHighWater
, it might be not ideal to include the assigned avax from all minipools, if they are not currently running.
*This is the first contest i took part, therefore my submissions might not be ideal yet, if there are any questions feel free to contact me directly in discord :-)
#0 - c4-judge
2023-01-09T20:38:42Z
GalloDaSballo marked the issue as duplicate of #401
#1 - c4-judge
2023-01-29T18:48:17Z
GalloDaSballo marked the issue as duplicate of #566
#2 - c4-judge
2023-02-08T09:48:37Z
GalloDaSballo changed the severity to 3 (High Risk)
#3 - c4-judge
2023-02-08T09:48:43Z
GalloDaSballo marked the issue as satisfactory
🌟 Selected for report: gz627
Also found by: AkshaySrivastav, Allarious, Czar102, HE1M, HollaDieWaldfee, KmanOfficial, adriro, ast3ros, betweenETHlines, bin2chen, brgltd, cccz, chaduke, hihen, imare, mookimgo, neumo, nogo, peanuts, unforgiven
21.713 USDC - $21.71
https://github.com/code-423n4/2022-12-gogopool/blob/main/contracts/contract/ProtocolDAO.sol#L193
Two registered addresses can point to the same name which can result in collisions
Currently, the registerContract
function does not check if names attached to addresses are unique, it allows to use the same name for several addresses. While this at this point doesn't really sound like an issue, consider following scenario:
Breaker
Breaker
name -> address
: setAddress(keccak256(abi.encodePacked("contract.address", name)), addr);
setAddress(keccak256(abi.encodePacked("contract.address", name)), addr);
.Now consider following scenario as well:
1)Contract A calls withdrawAvax
in vault.sol
; it will then account the balance change based on the name which is assigned to contract A:
string memory contractName = getContractName(msg.sender);
avaxBalances[contractName] = avaxBalances[contractName] - amount;
This results in contract A and contract B both using the same slot for saving their contract balance, eventually resulting in collisions.
VSCode
Consider only allowing unique names for contracts, reverting the registerContract
function if the name is already occupied.
*This is the first contest i took part, therefore my submissions might not be ideal yet, if there are any questions feel free to contact me directly in discord :-)
#0 - GalloDaSballo
2023-01-10T19:29:15Z
Most likely OOS
#1 - emersoncloud
2023-01-18T13:52:48Z
This makes sense and seems like a good check to add to the register method, verify that the same name can't be used by two different addresses
#2 - 0xju1ie
2023-01-24T13:38:05Z
similar to #742
#3 - c4-judge
2023-01-30T19:39:33Z
GalloDaSballo marked the issue as duplicate of #742
#4 - c4-judge
2023-02-08T20:09:42Z
GalloDaSballo marked the issue as satisfactory
🌟 Selected for report: imare
Also found by: 0Kage, 0xbepresent, AkshaySrivastav, Faith, HollaDieWaldfee, Jeiwan, Saintcode_, betweenETHlines, btk, dic0de, enckrish, gz627, imare, jadezti, kaliberpoziomka8552, nogo, simon135, sk8erboy
17.3743 USDC - $17.37
https://github.com/code-423n4/2022-12-gogopool/blob/main/contracts/contract/MinipoolManager.sol#L260
If a multisig address suddenly turns malicious, there is no quick and easy way to change the assignment to minipools
Currently, a multisig address is automatically assigned to a newly created minipool: https://github.com/code-423n4/2022-12-gogopool/blob/main/contracts/contract/MinipoolManager.sol#L260
This address can eventually misbehave like stealing funds via claimAndInitiateStaking
. However, this fact itself is not the issue, the issue is that the contract cannot easily change the assigned multisig address for the minipool. It is only possible via creating a new contract, registering it as network contract and then call setAddress
with the correct parameters. However, it would make more sense to directly implement a function like that in case of emergency.
VSCode
Consider implementing a function which allows the guardian to change the multisig address assigned to minipools.
#0 - c4-judge
2023-01-08T12:54:48Z
GalloDaSballo marked the issue as duplicate of #618
#1 - c4-judge
2023-02-01T19:57:26Z
GalloDaSballo marked the issue as duplicate of #702
#2 - GalloDaSballo
2023-02-02T11:57:07Z
See #618
#3 - c4-judge
2023-02-02T11:57:10Z
GalloDaSballo marked the issue as partial-50
🌟 Selected for report: hansfriese
Also found by: 0Kage, 0xdeadbeef0x, Allarious, Aymen0909, Ch_301, Franfran, HollaDieWaldfee, Jeiwan, Nyx, RaymondFam, SmartSek, adriro, betweenETHlines, bin2chen, cccz, datapunk, immeas, kaliberpoziomka8552, peritoflores, wagmi
10.8565 USDC - $10.86
https://github.com/code-423n4/2022-12-gogopool/blob/main/contracts/contract/MinipoolManager.sol#L444
Currently, recreateMinipool
and claimAndInitiateStaking
can be called by the multisig, even if the node operator already withdrew his funds.
withdrawMinipoolFunds
and receives his stake + rewards.recreateMinipool
which assigns all necessary variables:setUint(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".avaxNodeOpAmt")), compoundedAvaxNodeOpAmt);
setUint(keccak256(abi.encodePacked("minipool.item", minipoolIndex, ".avaxLiquidStakerAmt")), compoundedAvaxNodeOpAmt);
claimAndInitiateStaking
which withdraws all necessary tokens from ggAvax and the vault and starts the staking process. However, the nodeOp has not even provided any AVAX, since it was already withdrawn within withdrawMinipoolFunds
.Therefore the nodeOp gets a stake granted without even providing the necessary tokens.
VSCode
Consider rethinking about the logic and state transition itself and eventually disallow the state transition from finished -> prelaunch. However, if that is limited, it needs to be careful validated if the contract still works.
*This is the first contest i took part, therefore my submissions might not be ideal yet, if there are any questions feel free to contact me directly in discord :-)
#0 - c4-judge
2023-01-10T18:02:44Z
GalloDaSballo marked the issue as duplicate of #569
#1 - c4-judge
2023-01-31T13:23:01Z
GalloDaSballo marked the issue as duplicate of #213
#2 - c4-judge
2023-02-03T19:26:10Z
GalloDaSballo changed the severity to 3 (High Risk)
#3 - c4-judge
2023-02-08T08:26:45Z
GalloDaSballo changed the severity to 2 (Med Risk)
#4 - c4-judge
2023-02-08T08:50:12Z
GalloDaSballo changed the severity to 3 (High Risk)
#5 - c4-judge
2023-02-09T08:25:58Z
GalloDaSballo marked the issue as not a duplicate
#6 - c4-judge
2023-02-09T08:50:28Z
GalloDaSballo changed the severity to 2 (Med Risk)
#7 - c4-judge
2023-02-09T08:50:47Z
GalloDaSballo marked the issue as duplicate of #569
#8 - c4-judge
2023-02-09T08:50:57Z
GalloDaSballo marked the issue as partial-50
#9 - GalloDaSballo
2023-02-09T08:51:10Z
Not well detailed -> 50%
🌟 Selected for report: caventa
Also found by: 0xdeadbeef0x, Allarious, Franfran, __141345__, betweenETHlines, hansfriese, mert_eren, stealthyz, unforgiven, wagmi
118.8832 USDC - $118.88
https://github.com/code-423n4/2022-12-gogopool/blob/main/contracts/contract/MinipoolManager.sol#L279
The wait period requirement for cancelling minipools can and will be circumvented
Currently, the function cancelMinipool
checks if the getRewardsStartTime
is far enough in the past to meet the cancel moratorium: https://github.com/code-423n4/2022-12-gogopool/blob/main/contracts/contract/MinipoolManager.sol#L279
. However, this check is fundamentally flawed because it only checks the time when the first minipool was created. All future minipools can be immediately cancelled if that time passes for the first minipool.
This vulnerability can be abused to frontrun claimAndDistribute
, increase avaxAssignedHighWater
and immediately cancel the minipool again. However, this issue is described seperately.
VSCode
Consider implementing a separate startTime variable, based on the nodeId and execute the check on this variable.
#0 - c4-judge
2023-01-09T12:40:16Z
GalloDaSballo marked the issue as duplicate of #519
#1 - c4-judge
2023-02-08T10:04:09Z
GalloDaSballo marked the issue as satisfactory
🌟 Selected for report: IllIllI
Also found by: 0xSmartContract, AkshaySrivastav, HollaDieWaldfee, RaymondFam, Rolezn, ast3ros, betweenETHlines, brgltd, chaduke, codeislight, cozzetti, lukris02, pauliax, sces60107
1183.3628 USDC - $1,183.36
https://github.com/code-423n4/2022-12-gogopool/blob/main/contracts/contract/Vault.sol#L26 https://github.com/code-423n4/2022-12-gogopool/blob/main/contracts/contract/Vault.sol#L27 https://github.com/code-423n4/2022-12-gogopool/blob/main/contracts/contract/Vault.sol#L28
Consider removing them.
https://github.com/code-423n4/2022-12-gogopool/blob/main/contracts/contract/Vault.sol#L39
Consider adding them into a enumerableSet and add a view functions.
Throughout the codebase
Example: https://github.com/code-423n4/2022-12-gogopool/blob/main/contracts/contract/Vault.sol#L100
Consider using the += / -= style.
https://github.com/code-423n4/2022-12-gogopool/blob/main/contracts/contract/Vault.sol#L84
Consider removing the unused function as it exposes an unnecessary governance privilege.
Throughout the codebase
Example: https://github.com/code-423n4/2022-12-gogopool/blob/main/contracts/contract/Vault.sol#L128
Consider rewriting the codebase to respect the checks-effects-interactions pattern.
withdrawToken
https://github.com/code-423n4/2022-12-gogopool/blob/main/contracts/contract/Vault.sol#L137
Consider checking that withdrawalAddress
is not the zero address. (It would revert for erc20 standard tokens, but for custom token it might even succeed)
https://github.com/code-423n4/2022-12-gogopool/blob/main/contracts/contract/Storage.sol#L112
https://github.com/code-423n4/2022-12-gogopool/blob/main/contracts/contract/Storage.sol#L120
Consider removing them, if there is no plan to use it in the future.
setGuardian
Currently, the setGuardian
function lacks a non-zero validation
https://github.com/code-423n4/2022-12-gogopool/blob/main/contracts/contract/Storage.sol#L120
We acknowledge that this can not become an issue at any time due to the two-step initialization, however, we always recommend a non-zero validation for setter functions because its considered as best-practice.
Consider validating the input address.
MinipoolManager
https://github.com/code-423n4/2022-12-gogopool/blob/main/contracts/contract/Staking.sol#L5
Consider removing the unused import to keep the contract size reasonable.
https://github.com/code-423n4/2022-12-gogopool/blob/main/contracts/contract/RewardsPool.sol#L82
Currently, the token inflation is not happening via minting, it is just a vesting-like process. However, this might confuse regular users because the totalSupply will always be display with 21_000_000 tokens.
While we assume that this is the intended logic, we still feel like we need to point this out.
Currently, the multisig reward distribution rounds down which eventually results in a very small loss of rewards:
https://github.com/code-423n4/2022-12-gogopool/blob/main/contracts/contract/RewardsPool.sol#L229
Consider adding a multiplier for this arithmetic calculation to decrease the down-rounding impact.
TokenGGP
https://github.com/code-423n4/2022-12-gogopool/blob/main/contracts/contract/ProtocolDAO.sol#L5
Consider removing this import
setOneInch
https://github.com/code-423n4/2022-12-gogopool/blob/main/contracts/contract/Oracle.sol#L28
Consider validating the input.
Currently, there is no easy way for users to inspect the list of Defenders
https://github.com/code-423n4/2022-12-gogopool/blob/main/contracts/contract/Ocyticus.sol#L27
Consider adding an enumerableSet.addressSet for the Defenders.
avaxHalfRewards
is rounded downhttps://github.com/code-423n4/2022-12-gogopool/blob/main/contracts/contract/Ocyticus.sol#L27
the following line of code is rounded down which results in some dust.
Consider executing this operation with an appropiate multiplier.
#0 - GalloDaSballo
2023-01-25T18:38:44Z
Unused error messages: NC
Allowed tokens are not easily inspectable by users NC
Usage of += / -= for cleaner code Disagree due to gas
Unused function `transferAvax`` R
Checks-effects-interaction pattern not respected L
Non-zero validation for withdrawToken L
Byte storage, int storage as well as the assigned setters are unused Disputing, they are used
Non-zero validation missing for setGuardian NC as it cannot be accepted
Unused import MinipoolManager NC
Sub-optimal design for token inflation L
Multisig reward distribution rounds down L
Unused import: TokenGGP NC
Missing non-zero validation for setOneInch L
Users can not easily inspect the list of added Defenders NC
avaxHalfRewards is rounded down 50% of the time you lose 1 wei Disputed as there's subtraction to deal with dust later
5L 1R R 6NC
#1 - GalloDaSballo
2023-02-03T16:35:21Z
3L from dups
8L 1R 6NC
#2 - c4-judge
2023-02-03T22:09:09Z
GalloDaSballo marked the issue as grade-a
🌟 Selected for report: NoamYakov
Also found by: AkshaySrivastav, IllIllI, ast3ros, betweenETHlines, c3phas, camdengrieh, chaduke, fatherOfBlocks, kartkhira, latt1ce, shark
82.3208 USDC - $82.32
rewardsCycleLength
can be made constantSince this variable is initialized and never changed, this can simply be made constant which makes its init redundant and saves gas.
emitGuardianChanged
can be emitted without cachingIf the event is emitted at the beginning of the function it can simply be done using the parameters (guardian, newGuardian). This will save gas because address oldguardian
does not need to be cached into memory.
https://github.com/code-423n4/2022-12-gogopool/blob/main/contracts/contract/Storage.sol#L65
#0 - GalloDaSballo
2023-01-27T15:07:18Z
2.3k
#1 - c4-judge
2023-02-03T19:12:28Z
GalloDaSballo marked the issue as grade-b