Vyper Vested Claims

First Flight #34
Beginner FriendlyDeFi
100 EXP
View results
Submission Details
Severity: low
Invalid

No Ownership Transfer Mechanism

Summary

The token vesting contract lacks a mechanism for transferring contract ownership to another address. This oversight creates a significant operational risk, as the contract becomes administratively frozen if the owner's private key is compromised or lost. The absence of an ownership transfer function limits the contract's long-term maintainability and creates a single point of failure in the contract's administrative capabilities.

Vulnerability Details

The contract initializes ownership during deployment and maintains a single immutable owner address:

@deploy
def __init__(merkle_root: bytes32, token: address, vesting_start_time: uint256, vesting_end_time: uint256):
"""
@notice Initialize the contract with the merkle root, token address, vesting start and end time
@param merkle_root: bytes32, the merkle root of the vesting list
@param token: address, the address of the token contract
@param vesting_start_time: uint256, the start time of the vesting
@param vesting_end_time: uint256, the end time of the vesting
"""
self.merkle_root = merkle_root
self.token = token
self.vesting_start_time = vesting_start_time
self.vesting_end_time = vesting_end_time
self.owner = msg.sender
log MerkleRootUpdated(merkle_root)

Access control is implemented through the onlyOwner modifier function:

def onlyOwner():
"""
@notice This function is used to restrict access to the owner only
"""
assert msg.sender == self.owner, "Only owner can call this function"

However, the contract does not provide any function to transfer this ownership to another address.

Impact

The lack of an ownership transfer mechanism creates several risks:

  1. Single Point of Failure: If the owner's private key is lost or compromised, critical administrative functions become permanently inaccessible.

  2. Operational Inflexibility: Normal organizational changes (such as staff turnover ) cannot be accommodated in the contract's governance structure.

  3. Reduced Long-term Viability: The contract's ability to adapt to changing circumstances is significantly limited, potentially reducing its useful lifespan.

  4. Emergency Response Limitations: In case of security emergencies, the ability to transfer control to a secure address is absent.

  5. Centralization Risk: The permanent association of control with a single address represents a centralization risk that conflicts with best practices in decentralized systems.

Tools Used

Manual Review

Recommendations

Implement a standard ownership transfer mechanism with a two-step process to prevent accidental transfers:

proposed_owner: public(address)
@external
def propose_owner(new_owner: address):
"""
@notice Proposes a new owner for the contract
@param new_owner: address, the proposed new owner
"""
self.onlyOwner()
assert new_owner != empty(address), "New owner cannot be zero address"
self.proposed_owner = new_owner
@external
def accept_ownership():
"""
@notice Allows the proposed owner to accept ownership
"""
assert msg.sender == self.proposed_owner, "Only proposed owner can accept ownership"
old_owner: address = self.owner
self.owner = self.proposed_owner
self.proposed_owner = empty(address)
# Optional: Emit an event for the ownership transfer
# log OwnershipTransferred(old_owner, self.owner)

Alternatively, consider implementing a more robust governance structure:

  1. Multi-signature Ownership: Replace the single owner with a multi-signature wallet to distribute control.

  2. Role-Based Access Control: Implement more granular access control with different roles for different administrative functions.

Updates

Appeal created

bube Lead Judge 6 months ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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