DeFiHardhatOracleProxyUpdates
100,000 USDC
View results
Submission Details
Severity: low
Invalid

Multiple Calls to init Function

Summary

The init function in the InitBipSeedGauge contract is intended to initialize the contract's state, including setting gauge points, updating BDV values, and modifying whitelist statuses. If this function can be called more than once, it could lead to unintended consequences, such as repeatedly altering state variables and emitting events that should only occur once during the contract's lifecycle.

function init() external {
// Update milestone season decimal precision
// prior to dewhitelisting Bean3CRV.
s.ss[C.CURVE_BEAN_METAPOOL].milestoneStem =
int96(s.ss[C.CURVE_BEAN_METAPOOL].milestoneStem.mul(1e6));
addWhitelistStatuses(true);
LibWhitelist.dewhitelistToken(C.CURVE_BEAN_METAPOOL);
// set s.stemScaleSeason for silo v3.1.
s.season.stemScaleSeason = uint16(s.season.current);
// Update depositedBDV for bean, bean3crv, urBean, and urBeanETH.
LibTokenSilo.incrementTotalDepositedBdv(
C.BEAN,
BEAN_UNMIGRATED_BDV - s.migratedBdvs[C.BEAN] + SILO_V2_EARNED_BEANS
);
LibTokenSilo.incrementTotalDepositedBdv(
C.CURVE_BEAN_METAPOOL,
BEAN_3CRV_UNMIGRATED_BDV - s.migratedBdvs[C.CURVE_BEAN_METAPOOL]
);
LibTokenSilo.incrementTotalDepositedBdv(
C.UNRIPE_BEAN,
UNRIPE_BEAN_UNMIGRATED_BDV - s.migratedBdvs[C.UNRIPE_BEAN]
);
LibTokenSilo.incrementTotalDepositedBdv(
C.UNRIPE_LP,
UNRIPE_LP_UNMIGRATED_BDV - s.migratedBdvs[C.UNRIPE_LP]
);
address[] memory siloTokens = LibWhitelistedTokens.getWhitelistedTokens();
bytes4 gpSelector = IGaugePointFacet.defaultGaugePointFunction.selector;
bytes4 lwSelector = ILiquidityWeightFacet.maxWeight.selector;
bytes4[4] memory gpSelectors = [bytes4(0), gpSelector, 0, 0];
bytes4[4] memory lwSelectors = [bytes4(0), lwSelector, 0, 0];
uint128[4] memory gaugePoints = [uint128(0), BEAN_ETH_INITIAL_GAUGE_POINTS, 0, 0];
uint64[4] memory optimalPercentDepositedBdv = [uint64(0), 100e6, 0, 0];
uint128 totalBdv;
for (uint i = 0; i < siloTokens.length; i++) {
// previously, the milestone stem was stored truncated. The seed gauge system now stores
// the value untruncated, and thus needs to update all previous milestone stems.
// This is a one time update, and will not be needed in the future.
s.ss[siloTokens[i]].milestoneStem = int96(s.ss[siloTokens[i]].milestoneStem.mul(1e6));
// update gpSelector, lwSelector, gaugePoint,and optimalPercentDepositedBdv
s.ss[siloTokens[i]].gpSelector = gpSelectors[i];
s.ss[siloTokens[i]].lwSelector = lwSelectors[i];
s.ss[siloTokens[i]].gaugePoints = gaugePoints[i];
s.ss[siloTokens[i]].optimalPercentDepositedBdv = optimalPercentDepositedBdv[i];
// if the silo token has a stalkEarnedPerSeason of 0,
// update to 1.
if (s.ss[siloTokens[i]].stalkEarnedPerSeason == 0) {
LibWhitelist.updateStalkPerBdvPerSeasonForToken(siloTokens[i], 1);
}
// get depositedBDV to use later:
totalBdv += s.siloBalances[siloTokens[i]].depositedBdv;
// emit event
emit LibWhitelist.UpdateGaugeSettings(
siloTokens[i],
gpSelectors[i],
lwSelectors[i],
optimalPercentDepositedBdv[i]
);
}
// initalize seed gauge and emit events.
s.seedGauge.beanToMaxLpGpPerBdvRatio = 33_333_333_333_333_333_333; // 33% (50% + 50%* (1/3) = 66%)
s.seedGauge.averageGrownStalkPerBdvPerSeason = initializeAverageGrownStalkPerBdv(totalBdv);
emit BeanToMaxLpGpPerBdvRatioChange(
s.season.current,
type(uint256).max,
int80(s.seedGauge.beanToMaxLpGpPerBdvRatio)
);
emit LibGauge.UpdateAverageStalkPerBdvPerSeason(
s.seedGauge.averageGrownStalkPerBdvPerSeason
);
// initalize s.usdTokenPrice for the bean eth well.
s.usdTokenPrice[C.BEAN_ETH_WELL] = 1;
// set s.twaReserves for the bean eth well, and the bean:3crv pool.
s.twaReserves[C.BEAN_ETH_WELL].reserve0 = 1;
s.twaReserves[C.BEAN_ETH_WELL].reserve1 = 1;
// initalize V2 cases.
LibCases.setCasesV2();
}

Impact

the init function does not have proper access controls, it can be called by any user, potentially leading to the following

  1. Duplicate updates to the milestoneStem for whitelisted tokens.

  2. Repeated dewhitelisting of the C.CURVE_BEAN_METAPOOL token.

  3. Multiple increments to totalDepositedBdv for certain tokens, inflating their values.

  4. Repeatedly setting gauge-related parameters, which could disrupt the staking and reward mechanisms.

  5. Emitting initialization events multiple times, causing confusion and potential off-chain integration issues.

Tools Used

manual review

Recommendations

Implement access controls to ensure that the init function can only be called once. This can be achieved by the following

  1. Adding a modifier that checks a state variable, such as bool private initialized, which is set to true after the first successful execution of init.

  2. Using the constructor for initialization logic, ensuring it's only executed once when the contract is deployed.

  3. Implementing role-based access control (RBAC) to restrict the execution of init to only authorized addresses, such as the contract deployer or a governance mechanism.
    an example implementation is

bool private initialized;
modifier initializer() {
require(!initialized, "Init function can only be called once");
_;
initialized = true;
}
function init() external initializer {
// Initialization logic...
}
Updates

Lead Judging Commences

giovannidisiena Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Design choice
Assigned finding tags:

Informational/Invalid

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.