Dafi V2 Super Staking Audit Report

Table Of Content

Share:

INTRODUCTION

BlockApex (Auditor) was contracted by Dafi Protocol (Client) for the purpose of conducting a Smart Contract Audit/Code Review. This document presents the findings of our analysis which took place from 16th Dec 2021 to 14th Jan 2022. 

Name: Dafi Super Staking V2
Auditor: Moazzam Arif | Muhammad Jarir Uddin
Platform: Ethereum/Solidity
Type of review: Staking, Mathematics, Oracle feeds
Methods: Architecture Review, Functional Testing, Computer-Aided Verification, Manual Review
Git repository: https://github.com/DAFIProtocol/dDAFI/tree/testing
White paper/ Documentation: https://docs.dafiprotocol.io/super-staking/overview-super-staking
Document log: Initial Audit: 31st December 2021 (complete)
Formal verification using property-based testing: 15th January 2022
Final Audit: 17th January 2022.

Scope

The git-repository shared was checked for common code violations along with vulnerability-specific probing to detect major issues/ vulnerabilities. Some specific checks are as follows:

Code reviewFunctional review
Reentrancy Unchecked external callBusiness Logics Review
Ownership TakeoverERC20 API violationFunctionality Checks
Timestamp DependenceUnchecked mathAccess Control & Authorization
Gas Limit and LoopsUnsafe type inferenceEscrow manipulation
DoS with (Unexpected) ThrowImplicit visibility levelToken Supply manipulation
DoS with Block Gas LimitDeployment ConsistencyAsset’s integrity
Transaction-Ordering DependenceRepository ConsistencyUser Balances manipulation
Style guide violationData ConsistencyKill-Switch Mechanism
Costly LoopOperation Trails & Event Generation

Project Overview

A comprehensive explanation of Staking module V2 for the Dafi Protocol is present in the official documentation and can be viewed at: https://docs.dafiprotocol.io/super-staking/super-staking-v2

Dafi Protocol Super Staking V2 is an update to the V1 module released earlier this year (2021) in the month of July. Super Staking V2 claims to offer a more stable APY rate and enhanced distribution of dDAFI rewards by modifying the math behind reward calculation to rather depend only on accumulated amounts of reward every time demand factor changes to calculating the rewards of users as the sum of all rewards in the past adjusted to the latest demand factor. 

Aside from the new reward formula, V2 also holds a couple of security improvements where demand factor is now enumerated using the latest price and is fortified by introduction of a delay i.e. a variance tolerance mechanism to ultimately prevent a sudden change in price. This new demand factor is supported by a TWAP calculation to make the price curve less steeper. Although strict monitoring is required, in cases of hackable oracle feeds, the protocol is able to recover from any kind of exploits by updating to an entirely new oracle service.

System Architecture

Codebase:

The system consists of 5 main smart contracts (namely: Staking Manager V2, Staking Database, Rebase Engine, Network Demand, Token Pool) and supplied with external data through 2 more contracts of Price Feeds and TVL Feeds.

Note: All of the contracts mentioned above contain onlyOwner modifiers to add, set and/or update configurations.

SHA-256 fingerprints of Contracts included:

  • Staking Manager V2 : ./StakingManagerV2.sol
    759bb0b2eb3f3d18b1d835f1b1b89733613ff6d1  
  • Staking Database : ./StakingDatabase.sol
    e4bb62c5ac3cb28c6f875af06158c4bff448d7410f84e147e8de08fcd72667d0  
  • Rebase Engine : ./rebase engine/RebaseEngine.sol
    61ab614f761737a42134cdaef991a0f7d5dd7ca1244993b2ba8bb277217432c4  
  • TokenPool : ./TokenPool.sol
    62c376c91256035f1601be81f9ce07d069acd1ccae3c00f330507f8618aac986  
  • Network Demand : ./network demand/NetworkDemand.sol
    bf71534344959ba076b03b0ac83f34a0ac20fa2ed2986bd7d85889e2b56fbb30  
  • Price Feeds : ./network demand/PriceFeeds.sol
    75b13b5e14cd516aaf65ac82526e7a1c317eebb57d306d6a0bbff7033317c5c7  
  • TVL Feeds : ./network demand/TVLFeeds.sol
    119130bd33ef19f8e901e7c81f6d3089d7c8df8f98586c2c1881c71021dcded2  

Static-Analysis summary

Methodology & Scope

Audit log

Manual Review: The audit launched with a recon phase where a manual code review was conducted to clarify the layers of understanding of the complexities and the general flow of the program. We started by reviewing the two main contracts against common solidity flaws. After the reconnaissance phase we wrote unit-test cases to ensure that the functions are performing their intended behavior. Then we began with the line-by-line manual code review.

Property Testing: From the reconnaissance, a handful of properties were also extracted and labeled as Invariants. In the following days of the audit procedure, the invariants were thoroughly tested against a setup flow of Dafi Staking V2 to ensure each of them held its proper definition.

AUDIT REPORT

Executive Summary

The analysis indicates that some of the functionalities in the contracts audited are poorly-secured. 

Our team performed a technique called “Filtered Audit”, where the contract was separately audited by two individuals. After their thorough and rigorous process of manual testing, an automated review was carried out using Mythril, MythX, and Slither. All the flags raised were manually reviewed and re-tested. 

Our team found: 

# of issues Severity of the risk
0Critical Risk issue(s)
3High-Risk issue(s)
0Medium Risk issue(s)
1Low-Risk issue(s)
1Informatory issue(s)


Findings

Below is the summary of our findings from the complete audit. This includes any flags raised from the manual/automated code review, behavioral/scenario testing and the properties tested for formal verification. 

S#Properties/FindingsRisk/ImpactStatus
CR-1.Oracle integrityCriticalPassed
HR-1.MAX_DAFI always greater than totaldDafiDistributedHighFailed
HR-2.RewardPool should not have balance if the Program is ended and all users have unstakedHighFailed
HR-3.If a program has ended, users should not be able to stakeHighFailed
LR-1.Reward is claimable even after program has endedLowPassed
LR-2.Unit tests in the testing and main branch of repo at /test/v2.ts should passLowFailed
IR-1.Memory optimization by using uint8 in place of true/ falseInformatory-

Critical-risk

CR-1. Oracle Integrity

Status: Passed

Description

In recent events, Oracles and external data sources have been manipulated to the adversary's benefit. We started off with our focus on the integrity of Oracles (Chainlink in this case) in order to make sure that the data imported into the contracts are not compromised.

The Chainlink oracle library method was tested against the following parameters of a standard audit technique.

  • Third-party (data source) function calls
  • Data type intact for functions’ return values
  • Access modifiers and state variables (if any)
function getThePrice() public view override returns (uint) {
        (
        ,
        int price,
        ,
        ,
        ) = priceFeed.latestRoundData();
        return uint(price);
    }

Suggestion: It is advised that a more tested and bonafide data source of TWAPs be considered to make Chainlink’s oracle integrity and reliability as the most optimal.

High-risk

HR-1. MAX_DAFI is always greater than totaldDafiDistributed

Status: Failed

Description

If a program is marked as “ended” before the preset ending duration, that is to say, the elapsed time of the program is less than the program duration, users get revert transactions for staking into the ended program (as seen in the testnet event and confirmed over a fuzzing test scenario with the reason of MAX_DAFI being calculated less than total DAFI distributed till the ending timestamp). This property does not hold at fuzzed inputs;

function test_ConfirmMaxDafiOverflowFuzz(
        uint256 md,
        uint32 pd,
        uint32 bn
    ) public {
        SetupContracts caller = dapping.users(0).setupContract();

        caller.wrapSEtStakingParams(1, md + 1, pd + 1);

        hevm.warp(block.number + bn);

        caller.wrapRebasePool();
        caller.database().getPool();

        uint256 MaxDafi = caller.rebaseEngine().MAX_DAFI();
        uint256 TdDafidist = caller.rebaseEngine().totaldDAFIDistributed();
        
        assertGt(MaxDafi, TdDafidist, 'maxDafi < tdDafiDist; program ended; dDafi overflows');
        
        emit log_named_uint('value of MaxDafi', MaxDafi);
        emit log_named_uint('value of TdDafiDist', TdDafiDist);

    }

Suggestion: It is advised that program duration should always be maintained greater than the time elapsed by supplying checks that assure it.

        if (database.isprogramEnded() && pool.lastUpdatedon > database.getProgramEndedAt()) {
            return;
        } else if (database.isProgramEnded() && pool.lastUpdatedon < database.getProgramEndedAt()) {
            maxTimestampForcalc = database.getProgramEndedAt();
        } else {
            maxTimestampForCalc = block.timestamp;
        }

HR-2. Reward Pool should not have balance if the program has ended and all users have unstaked

Status: Failed

Description

This property claims that the reward pool should be empty after the program has ended and that all the users have unstaked. 

HR-2(a). “markProgramEnded()” by owner’s mistake can lock funds of the reward pool

In case of human error if the program is marked ended the reward pool retains an amount of tokens unclaimed against the percentage of tokens staked in first place.

function test_RewardpoolUnemptyAfterProgramEnded public {
        SetupContracts caller = dapping.users().setupContract();
        hevm.warp (block. timestamp + 5000);
        
        
        caller.wrapSetStakingParams (1, 1000 ether, 12);
        
        uint256 distPoolBalanceBefore = caller.stakingManager().distributionPool(). balance();
        
        hevm.warp(block.timestamp + 99000);
        caller.wrapstake ( 100);
        
        caller.wrapMarkProgramEnded();
        
        hevm warp (block timestamp + 99000);
        caller.wrapunstake(100);
        
        uint256 distPool BalanceAfter = caller.stakingManager().distributionPool(). balance();
        
        assertGt(distPool BalanceBefore, distPoolBalanceAfter);
        
        emit Log named uint('pool Before', distPoolBalanceBefore);
        emit Log named uint('poolAfter', distPoolBalanceAfter);
    }

Suggestion: It is advised to have a refund mechanism if program duration is ended. The refund amount of tokens should be equal to MAX_DAFI - (MDI*totalElapsedTime).

HR-3. If a program has ended, users should not be able to stake

Status: Failed

Description

In a simple scenario test, it was confirmed that Staking was allowed even if the program duration is completed and the program is marked as ended which in the understanding of the Auditor team is an (incomprehensible feature) and can be proved to be a loophole of the system in some scenarios.

Suggestion: The modifier stakeCheck should be supplied with some additional check to ensure monitoring of programs marked as ended not to allow staking.

function test_StakeAfterProgramEnded() public {
        SetupContracts caller = dapping.users().setup Contract();
        hevm.warp(block. timestamp + 500);
        
        // _ms, _md, _pd
        uint256 minStakeDays = 1;
        uint256 maxDafi = 1000 ether;
        uint32 progDuration = 12;
        
        caller.wrapsetStakingParams (minStakeDays, maxDafi, progDuration);
        
        // stakin/ unstaing multiple times for seeding
        for (uint256 i = 0; i < 3; i++) {
            hevm warp (block. timestamp + 90000);
            caller.wrapStake ( 1000000000);
            hevm warp(block. timestamp + 90000);
            caller.wrapunstake ( 1000000000);
        }
        
        caller.wrapMarkProgramEnded();
        
        // stakin/ unstaing multiple times for seeding
        for (uint256 i = 0; i < 3; i++)
            hevm.warp (block. timestamp + 90000);
            caller.wrapStake ( 1000000000);
            hevm.warp(block. timestamp + 90000);
            caller.wrapUnstake ( 1000000000);
        }
    
        assertTrue(false);
    }

Low-risk

LR-1. Reward is claimable even after program is ended

Status: Passed

Description

Regardless of the program being ended, users should be able to claim their rewards for their staked dafi tokens. This claim should not be bound by any type of time-related constraints. We checked whether this property would fail in any circumstance but it passed on all fuzzed inputs.

LR-2. Unit tests in the testing and main branch of repo at /test/v2.ts should pass

Status: Failed

Description

Unit tests are critical in proving the developer's expected intention and behavior of the working code hence the set of tests not passing entirely and partially in the testing repository code is slightly questionable.

Informatory issues and Optimizations

IR-1. Memory optimization by using uint8 in place of true/false

Description

Following the best practices and the Solidity design patterns guide and since the client code uses a good number of state variables to manage the switches for staking, unstaking and alike. It is therefore suggested by the auditing team that uint8 type variables can be replaced by the developer in place of true/false on multiple occasions to minimize the memory usage and reduce the code size as an optimization.

DISCLAIMER

The smart contracts provided by the client for audit purposes have been thoroughly analyzed in compliance with the global best practices till date w.r.t cybersecurity vulnerabilities and issues in smart contract code, the details of which are enclosed in this report. 

This report is not an endorsement or indictment of the project or team, and they do not in any way guarantee the security of the particular object in context. This report is not considered, and should not be interpreted as an influence, on the potential economics of the token, its sale or any other aspect of the project. 

Crypto assets/tokens are results of the emerging blockchain technology in the domain of decentralized finance and they carry with them high levels of technical risk and uncertainty. No report provides any warranty or representation to any third-Party in any respect, including regarding the bug-free nature of code, the business model or proprietors of any such business model, and the legal compliance of any such business. No third-party should rely on the reports in any way, including for the purpose of making any decisions to buy or sell any token, product, service or other asset. Specifically, for the avoidance of doubt, this report does not constitute investment advice, is not intended to be relied upon as investment advice, is not an endorsement of this project or team, and it is not a guarantee as to the absolute security of the project.

Smart contracts are deployed and executed on a blockchain. The platform, its programming language, and other software related to the smart contract can have its vulnerabilities that can lead to hacks. The scope of our review is limited to a review of the Solidity code and only the Solidity code we note as being within the scope of our review within this report. The Solidity language itself remains under development and is subject to unknown risks and flaws. The review does not extend to the compiler layer, or any other areas beyond Solidity that could present security risks.

This audit cannot be considered as a sufficient assessment regarding the utility and safety of the code, bug-free status or any other statements of the contract. While we have done our best in conducting the analysis and producing this report, it is important to note that you should not rely on this report only - we recommend proceeding with several independent audits and a public bug bounty program to ensure security of smart contracts.

More Audits

Phase Protocol Audit

Phase Protocol is a NFT Marketplace infrastructure built on Solana Protocol, a reliable and scalable L1 solution. The on-chain Fundraising solution offered by DedMonke provides a crowdfunding experience to DeFi users.

Spin Finance Audit Report

Spin is a DeFi derivative infrastructure built on NEAR Protocol, a reliable and scalable L1 solution. The on-chain order book solution offered by Spin provides a CEX-competitive experience to DeFi users.

Dafi BSC-ETH Bridge (Final Audit)

Dafi’s “dbridge” enables users to bring their ERC-20 $DAFI tokens across from the Ethereum network to Binance Smart Chain, and vice versa, with aims of making $DAFI available on multiple high-speed and low-cost networks.

Designed & Developed by: 
All rights reserved. Copyright 2020-21