stGov is a convenient liquid token wrapper on top of Staker. Governance token holders can stake their tokens for stGov. stGov automates claiming rewards and delegating governance power.

How it works

  • Stake governance tokens to receive that many stGov.
  • Optionally, delegate the governance token voting power
  • The stGov contract deposits the governance tokens in staker. stGov assigns the voting power to the holder's chosen delegate, if any. Otherwise, it assigns the voting power using the default delegation strategy
  • The delegation strategy can be configured by governance. This keeps the default voting power aligned with the DAO and mitigates capture risk.
  • The stGov contract claims Staker's rewards daily.
  • The rewards are auctioned off for more of the governance token, which is added to each user's staked position. e.g. a balance of 100 stGov might grow to 100.5 stGov. Holders can redeem their stGOV 1:1 for the underlying governance token at any time. For further documentation, see Tally's docs.

Prize distribution and scoring

  • Total Prize Pool: $15,000

  • Additional pay for dedicated Cantina researcher: $10,000

  • Scoring described in the competition scoring page.

  • Findings Severities described in detail on our docs page.

Documentation

Scope

  • Repository: https://github.com/withtally/stGOV
  • Commit: e8a6d3be3a8223d332b911362ebc3820b92176c6
  • Total LOC: 1400
  • Files:
    • Core Contracts:
      • src/GovLst.sol - Main rebasing LST contract
      • src/FixedGovLst.sol - Fixed-balance LST contract
      • src/FixedLstAddressAlias.sol - Helper for fixed LST address mapping
      • src/WithdrawGate.sol - Manages withdrawal delays
      • src/WrappedGovLst.sol - Wrapped version of GovLst
    • Extension Contracts:
      • src/extensions/FixedGovLstOnBehalf.sol - Signature-based operations for FixedGovLst
      • src/extensions/FixedGovLstPermitAndStake.sol - Permit functionality for FixedGovLst
      • src/extensions/GovLstOnBehalf.sol - Signature-based operations for GovLst
      • src/extensions/GovLstPermitAndStake.sol - Permit functionality for GovLst
    • Auto-Delegates:
      • src/auto-delegates/OverwhelmingSupportAutoDelegate.sol - Base auto-delegate functionality
      • src/auto-delegates/extensions/AutoDelegateBravoGovernor.sol - Governor Bravo integration
      • src/auto-delegates/extensions/AutoDelegateOpenZeppelinGovernor.sol - OZ Governor integration
      • src/auto-delegates/extensions/BlockNumberClockMode.sol - Block-based timelock
      • src/auto-delegates/extensions/TimestampClockMode.sol - Timestamp-based timelock
  • stGOV is expected to be deployed on EVM networks such as Ethereum Mainnet, Arbitrum One, ZKsync Era and Rari Chain.
  • The staking token and the reward token are expected to be “normal” ERC20 tokens: non-rebasing, no fee-on-transfer, or hooks that implement weird behavior.
  • stGOV is expected to be deployed for an instance of Staker. Staker is out of scope for this audit, but feel free to review Staker’s audits.
  • stGOV and Staker may have different owners.

Trusted parties

  • The contract relies on trusted actors (owner and delegateeGuardian) who possess privileged powers to modify critical parameters, posing a centralization risk that could potentially lead to fund drainage or contract disablement if these roles are compromised or act maliciously.
  • The owner is trusted to set the reward parameters, the max override tip, and the min qualifying earning power.
  • The owner or delegateeGuardian is trusted to set the default delegate.
  • The Staker contract is trusted to run the underlying staking system. Staker’s owner is generally also the source of the rewards.

Build Instructions

  • Install Foundry:

    curl -L https://foundry.paradigm.xyz | bash foundryup
  • Install dependencies:

    forge install
  • Build the project:

    forge build
  • Run tests:

    forge test
  • Clean build artifacts:

    make clean

Basic POC Test:

  • Default Mandatory POC rule applies for this competition.
  • stGOV/test/contest/POC.t.sol

Out of scope

  • External dependencies including OpenZeppelin contracts
  • The Staker contract itself, which stGOV integrates with
  • Automated findings by LightChaser
  • Known Issues: Issues found in Offbeat Security recently are not yet fixed due to time constraint before starting the audit contest. The following issues are out of scope for this contest:
    • Deposit ID size mismatch may lead updateDeposit denial of service The GovLst contract stores depositIds as uint32 while Staker.DepositIdentifier uses uint256, causing new stakes to revert when deposit identifiers exceed type(uint32).max The GovLst contract stores a depositId in the HolderState struct as a uint32, but Staker.DepositIdentifier is a uint256. An attacker can call stake() or fetchOrInitializeDepositForDelegatee() repeatedly to create new deposit IDs until they exceed type(uint32).max, making it impossible to create or initialize new delegatees, effectively denying service for new delegatee assignments. This attack is only feasible if gas costs are extremely low, making the likelihood of this attack very low.

    • Missing validation in constructor for critical parameters The GovLst constructor does not contain sanity checks against maximum caps that exist in the setter functions, potentially allowing deployment with values exceeding intended limits for minQualifyingEarningPowerBips and maxOverrideTip. The GovLst contract fails to validate the minQualifyingEarningPowerBips and maxOverrideTip parameters in the constructor against their respective maximum caps, while these validations are correctly implemented in the corresponding setter functions. This could allow the contract to be deployed with values exceeding the intended maximum limits.

    • Hardcoded absolute value for max override tip cap is unrealistic for different valued tokens The GovLst contract uses a hardcoded MAX_OVERRIDE_TIP_CAP value (2000e18) which does not scale appropriately across different STAKE_TOKEN implementations with varying values and decimals, potentially allowing excessive tips for valuable tokens or worthless incentives for low-value tokens. The GovLst contract sets a hardcoded MAX_OVERRIDE_TIP_CAP value of 2000e18, which represents the maximum value that can be set for the maxOverrideTip parameter. This absolute value approach fails to account for the varying token values for STAKE_TOKEN. For example if token was as valuable as ETH, 2000e18 would be roughly $40M.

    • Fee extraction through deposit override manipulation: Users can strategically create delegations which are currently or soon-to-be below the minimum earning power thresholds. They could then collect override fees in shares, diluting other shareholders. This effect could potentially be amplified through a sybil attack. The GovLst contract allows any user to manipulate the delegatee override system to extract value from the protocol through self-dealing. An attacker can create multiple delegatee deposits and repeatedly trigger override events to collect tips at the expense of all token holders.

    • Some ERC20 tokens revert on stake contract initialization: The GovLst contract's initialization process creates deposits with zero amounts which may fail deployment with ERC20 tokens that revert on zero-value transfers. The GovLst contract initializes by creating deposits with zero amounts using calls to STAKER.stake(0, _params.initialDefaultDelegatee). However, some ERC20 tokens revert when attempting to transfer zero amounts. Since the staking contract calls the underlying token's transfer function during the staking process, this would cause the initialization to fail for tokens that don't allow zero-value transfers.

    • Inconsistent earning power validation allows staking into suboptimal deposits: The contract allows users to interact with deposits that don't meet the minimum qualifying earning power requirements. The GovLst contract implements a mechanism to prevent users from updating their deposits to ones with insufficient earning power (below minQualifyingEarningPowerBips), but this validation is inconsistently applied across different functions. The validation is missing from _stake(), subsidizeDeposit(), and some conditions within _updateDeposit(), allowing users to stake tokens into or interact with deposits that don't meet the minimum earning power requirements.

    • Unsafe type casting in GovLst contract The contract contains instances of unsafe type downcasting (uint256 to uint128/uint96) in functions that handle token amounts and share calculations, which could lead to silent value truncation and accounting errors when dealing with large numbers. The GovLst contract contains several instances of type downcasting without proper safety checks, which could lead to silent truncation and incorrect accounting. This issue primarily affects the handling of token amounts and share calculations. This issue is specifically prominent in tokens with unusually high supply or low value.

Contact Us

For any issues or concerns regarding this competition, please reach out to the Cantina core team through the Cantina Discord.

Summary

Status

Completed

Total reward:

$15,000

Findings submitted:

136

Start date:

10 Mar 2025 8:00pm (local time)

End date:

17 Mar 2025 8:00pm (local time)