Liquid Staking

Stakelink
DeFiHardhatOracle
50,000 USDC
View results
Submission Details
Severity: medium
Invalid

test/core/rebase-controller.test.ts

This code implements unit tests for various smart contracts interacting within a staking, rebase, and insurance system. Below is an extensive review of the code, including potential vulnerabilities, improvements, and suggestions. The review is divided into sections:


1. Potential Security Vulnerabilities

Here are possible areas of concern to ensure the robustness of your contracts:

1.1 Reentrancy Attack Risks

  • StakingPool & InsurancePool:
    Operations like donateTokens(), deposit(), and transfer() involve token interactions. Depending on how these tokens (e.g., ERC677) are implemented, there could be reentrancy risks.

  • Mitigation:

    • Use nonReentrant modifiers on functions that modify the state and interact with external contracts.

    • Ensure you’re following the checks-effects-interactions pattern.

1.2 Unchecked External Contract Calls

  • Functions like simulateSlash() and performUpkeep() rely on external contracts or mocks.

  • Risk: If any of the dependent contracts are compromised, they may manipulate the state unexpectedly.

  • Mitigation: Consider adding onlyOwner() or access control modifiers to critical functions to limit how and who can call certain functionalities.

1.3 Encoding/Decoding with Hardcoded Types

  • Problem:
    The encode and decode logic uses hardcoded types:

    ethers.AbiCoder.defaultAbiCoder().encode(['uint256[]', 'uint256'], data)

    If these structures change over time, it could introduce subtle bugs.

  • Suggestion: Use type-safe wrappers to encode/decode based on dynamic data models.

1.4 Lack of Gas Limits on Upkeep Execution

  • Concern: The performUpkeep() function might execute arbitrarily large workloads (e.g., iterating over many strategies). This could result in out-of-gas exceptions or potential DoS vulnerabilities.

  • Mitigation: Add gas management logic, like batching strategies, or limit the maximum number of strategies updated in a single upkeep.


2. Code Style & Best Practices

Here are some suggestions to improve the quality and maintainability of the code.

2.1 Type Annotations and Stronger Typing

  • Issue: You use any in places like:

    const decode = (data: any) => ethers.AbiCoder.defaultAbiCoder().decode(['uint256[]', 'uint256'], data);
  • Improvement: Replace any with specific types such as:

    const decode = (data: string): [number[], number] =>
    ethers.AbiCoder.defaultAbiCoder().decode(['uint256[]', 'uint256'], data);

2.2 Use of Constants

  • Replace magic numbers like 300, 1000, and 3000 with appropriately named constants:

    const MAX_SLASH = 300;
    const INITIAL_SUPPLY = 1000;
    const REBASE_INTERVAL = 3000;

    This will enhance readability and reduce duplication.

2.3 Structure Test Cases for Clarity

  • You can improve the organization of your tests by using describe blocks for specific functionalities:

    describe('StakingPool interactions', () => { ... });
    describe('RebaseController behavior', () => { ... });

    This makes it easier to isolate which components each test targets.

2.4 Assertions with Messages

  • Many assert calls are missing descriptive failure messages. Add these to improve debugging:

    assert.equal(data[0], true, 'Expected upkeep to be needed after slashing.');

3. Logic Vulnerability Checks and Improvements

There are a few logical improvements that could make the contracts more resilient.

3.1 Handling Unexpected Scenarios in performUpkeep

  • Issue: If someone passes incorrect or malformed encodedData, the contract might behave unexpectedly.

  • Improvement: Add a sanity check or try/catch block to ensure valid encoding and decode failures are handled gracefully:

    try {
    const [strategies, amount] = decode(data);
    // Continue logic...
    } catch (error) {
    revert('InvalidEncodedData()');
    }

3.2 Prevent Overflows and Ensure SafeMath

  • The test code does not demonstrate any protection against overflows. While Solidity >= 0.8.0 has built-in overflow checks, this should also be validated in test logic:

    await expect(strategy1.simulateSlash(toEther(1e18))).to.be.revertedWith('Overflow');

3.3 Edge Case Handling in checkUpkeep()

  • Concern: What happens if multiple strategies are slashed simultaneously? Does checkUpkeep() detect the slashes accurately?

  • Suggestion: Test edge cases with multiple slashes or zero balance strategies to ensure it handles them correctly:

    await strategy1.simulateSlash(toEther(0));
    assert.equal((await rebaseController.checkUpkeep('0x00'))[0], false, 'Should detect zero balance properly.');

4. Additional Testing Suggestions

4.1 Event Emission Testing

  • Why: Ensure that critical state changes emit expected events to improve traceability.

    await expect(rebaseController.performUpkeep(encodedData))
    .to.emit(rebaseController, 'UpkeepPerformed')
    .withArgs(strategyIds, amount);

4.2 Access Control Tests

  • Add tests to verify ownership or role-based access. For example:

    await expect(stakingPool.setPriorityPool(accounts[1]))
    .to.be.revertedWith('Ownable: caller is not the owner');

4.3 Time-based Logic Testing

  • If there are any time-sensitive operations (e.g., checkUpkeep() frequency), use Hardhat's time manipulation tools to simulate time passage:

    await network.provider.send('evm_increaseTime', [3600]); // Advance 1 hour
    await network.provider.send('evm_mine');

5. Gas Optimization Opportunities

5.1 Batch Operations on Strategies

  • Instead of calling addStrategy() multiple times, consider batching strategies to reduce gas usage:

    await stakingPool.addStrategies([strategy1, strategy2, strategy3]);

5.2 Use of immutable for Fixed Addresses

  • Contracts like RebaseController store addresses that don’t change. Marking these variables as immutable can save gas:

    address public immutable stakingPool;

6. Conclusion

This code demonstrates solid practices and comprehensive tests, but here’s a summary of key recommendations:

  1. Security:

    • Add reentrancy guards and access control modifiers where necessary.

    • Validate performUpkeep data carefully to prevent misuse.

  2. Code Improvements:

    • Replace magic numbers with constants.

    • Use stronger type annotations and modularize the test structure.

  3. Testing:

    • Test for gas limits, role-based access, and event emissions.

    • Use Hardhat’s time travel utilities for time-based logic validation.

With these changes, your contracts will be more secure, easier to maintain, and more robust under various conditions.

Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Lack of quality

Support

FAQs

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