Vanguard

First Flight #56
Beginner FriendlyDeFiFoundry
0 EXP
Submission Details
Impact: medium
Likelihood: low

Missing Owner Controls

Author Revealed upon completion

Root + Impact

Description

  • Normal behavior:
    Per the stated protocol model, an owner deploys the hook and must be able to modify fee parameters and launch configuration via administrative functions. This requires explicit on-chain ownership/access control and setter functions guarded by onlyOwner (or roles) to update launch parameters safely.

  • Specific issue:
    TokenLaunchHook does not implement an owner/admin role or any access-controlled administrative functions. All launch parameters are declared as immutable, making them permanently fixed at deployment. This directly contradicts the protocol specification that the owner has “full administrative control” and “can modify fee parameters,” creating incorrect trust/operational assumptions and eliminating any on-chain remediation path for misconfiguration.

// TokenLaunchHook.sol
contract TokenLaunchHook is BaseHook {
// @> No owner/admin address
// @> No access control (onlyOwner/roles)
// @> No admin functions to modify launch config after deployment
uint256 public immutable phase1Duration; // @> Immutable: cannot be updated
uint256 public immutable phase2Duration; // @> Immutable: cannot be updated
uint256 public immutable phase1LimitBps; // @> Immutable: cannot be updated
uint256 public immutable phase2LimitBps; // @> Immutable: cannot be updated
uint256 public immutable phase1Cooldown; // @> Immutable: cannot be updated
uint256 public immutable phase2Cooldown; // @> Immutable: cannot be updated
uint256 public immutable phase1PenaltyBps; // @> Immutable: cannot be updated
uint256 public immutable phase2PenaltyBps; // @> Immutable: cannot be updated
}

Risk

Likelihood: LOW

  • There is no attacker-driven interaction that can exploit this issue.
    The absence of owner controls is a design/implementation omission present at deployment time and cannot be triggered, amplified, or abused by an external adversary.

Impact: MEDIUM

If manifested, the issue can cause significant operational and governance damage:

  • launch parameters cannot be corrected post-deployment,

  • fees and penalties cannot be adjusted in response to unexpected conditions,

  • misconfigurations may render a launch unrecoverable.

However, the issue does not directly enable fund theft, privilege escalation, or denial-of-service through attacker action.

Proof of Concept

PoC by inspection and interface surface:

  1. Search the contract for any of the following patterns:

    owner state variable

    onlyOwner AccessControl roles

    setter functions (e.g., setPhase*, updateFee*, configureLaunch*)

  2. Observe none exist.

  3. Observe launch parameters are immutable and set only in the constructor.

Therefore, no on-chain actor can modify launch configuration post-deployment,
contradicting the stated protocol model of an owner with administrative control.

Recommended Mitigation

Implement explicit ownership and scoped admin setters (or alternatively redesign governance, but the current specification requires an owner). Below is a minimal, security-first pattern using OpenZeppelin Ownable. Do not add blanket setters; scope changes tightly and consider time-lock / phase-gating.

+ import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
- contract TokenLaunchHook is BaseHook {
+ contract TokenLaunchHook is BaseHook, Ownable {
- uint256 public immutable phase1PenaltyBps;
- uint256 public immutable phase2PenaltyBps;
+ uint256 public phase1PenaltyBps;
+ uint256 public phase2PenaltyBps;
+ // This event enables off-chain indexers, monitoring tools, and governance
+ // processes to track owner-driven configuration changes transparently.
+ event PenaltyBpsUpdated(uint256 phase1PenaltyBps, uint256 phase2PenaltyBps);
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
/* CONSTRUCTOR */
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
constructor(
IPoolManager _poolManager,
+ address _initialOwner,
uint256 _phase1Duration,
uint256 _phase2Duration,
uint256 _phase1LimitBps,
uint256 _phase2LimitBps,
uint256 _phase1Cooldown,
uint256 _phase2Cooldown,
uint256 _phase1PenaltyBps,
uint256 _phase2PenaltyBps
- ) BaseHook(_poolManager) {
+ )
+ BaseHook(_poolManager)
+ Ownable(_initialOwner)
+ {
+ // IMPORTANT:
+ // The owner MUST be passed explicitly by the deploy script.
+ // This should typically be a multisig, timelock, or governance contract,
+ // NOT an implicitly assumed msg.sender.
+
if (_phase1Duration == 0 || _phase2Duration == 0)
revert InvalidConstructorParams();
if (
_phase1LimitBps > 10_000 ||
_phase2LimitBps > 10_000 ||
_phase1PenaltyBps > 10_000 ||
_phase2PenaltyBps > 10_000
) revert InvalidConstructorParams();
phase1Duration = _phase1Duration;
phase2Duration = _phase2Duration;
phase1LimitBps = _phase1LimitBps;
phase2LimitBps = _phase2LimitBps;
phase1Cooldown = _phase1Cooldown;
phase2Cooldown = _phase2Cooldown;
phase1PenaltyBps = _phase1PenaltyBps;
phase2PenaltyBps = _phase2PenaltyBps;
currentPhase = 0;
lastPhaseUpdateBlock = 0;
totalPenaltyFeesCollected = 0;
}
+ /**
+ * @notice Updates penalty fee parameters for the launch phases.
+ * @dev Restricted to the contract owner to prevent unauthorized changes.
+ *
+ * SECURITY CONSIDERATIONS:
+ * - Owner privileges are powerful and assume a trusted operator.
+ * - Consider adding phase-based restrictions or a timelock
+ * for production deployments.
+ */
+ function setPenaltyBps(
+ uint256 newPhase1PenaltyBps,
+ uint256 newPhase2PenaltyBps
+ ) external onlyOwner {
+ // Defensive check: penalty values must not exceed 100%
+ require(
+ newPhase1PenaltyBps <= 10_000 &&
+ newPhase2PenaltyBps <= 10_000,
+ "bps > 100%"
+ );
+
+ phase1PenaltyBps = newPhase1PenaltyBps;
+ phase2PenaltyBps = newPhase2PenaltyBps;
+
+ emit PenaltyBpsUpdated(
+ newPhase1PenaltyBps,
+ newPhase2PenaltyBps
+ );
+ }

Support

FAQs

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

Give us feedback!