exclamation mark iconReport an issue

If you notice some outdated information please let us know!

close icon
Your message

Badger Finance


Process Quality Review (0.7)

Badger Finance

Final score:85%
Date:21 Sep 2021
Audit Process:version 0.7
Author:Nic of DeFiSafety
PQR Score:85%


Protocol Website:https://badger.com

Hack History

Date:02 Dec 2021
Details: $120 million was drained from BadgerDAO's users' addresses via a malicious script that the hacker implemented in the protocol's website frontend. This hacker's address was then inadvertently approved by users who were then compromised.
Reference Linklink

Scoring Appendix

The final review score is indicated as a percentage. The percentage is calculated as Achieved Points due to MAX Possible Points. For each element the answer can be either Yes/No or a percentage. For a detailed breakdown of the individual weights of each question, please consult this document.

The blockchain used by this protocol

Very simply, the audit looks for the following declarations from the developer's site. With these declarations, it is reasonable to trust the smart contracts.

  • Here is my smart contract on the blockchain
  • You can see it matches a software repository used to develop the code
  • Here is the documentation that explains what my smart contract does
  • Here are the tests I ran to verify my smart contract
  • Here are the audit(s) performed to review my code by third party experts

This report is for informational purposes only and does not constitute investment advice of any kind, nor does it constitute an offer to provide investment advisory or other services. Nothing in this report shall be considered a solicitation or offer to buy or sell any security, token, future, option or other financial instrument or to offer or provide any investment advice or service to any person in any jurisdiction. Nothing contained in this report constitutes investment advice or offers any opinion with respect to the suitability of any security, and the views expressed in this report should not be taken as advice to buy, sell or hold any security. The information in this report should not be relied upon for the purpose of investing. In preparing the information contained in this report, we have not taken into account the investment needs, objectives and financial circumstances of any particular investor. This information has no regard to the specific investment objectives, financial situation and particular needs of any specific recipient of this information and investments discussed may not be suitable for all investors.

Any views expressed in this report by us were prepared based upon the information available to us at the time such views were written. The views expressed within this report are limited to DeFiSafety and the author and do not reflect those of any additional or third party and are strictly based upon DeFiSafety, its authors, interpretations and evaluation of relevant data. Changed or additional information could cause such views to change. All information is subject to possible correction. Information may quickly become unreliable for various reasons, including changes in market conditions or economic circumstances.

This completed report is copyright (c) DeFiSafety 2021. Permission is given to copy in whole, retaining this copyright label.

Code And Team


This section looks at the code deployed on the Mainnet that gets reviewed and its corresponding software repository. The document explaining these questions is here.

1. Are the executing code addresses readily available? (%)

Answer: 100%

They are available at website https://badger.wiki/addresses, as indicated in the Appendix.

Percentage Score Guidance:
Clearly labelled and on website, docs or repo, quick to find
Clearly labelled and on website, docs or repo but takes a bit of looking
Addresses in mainnet.json, in discord or sub graph, etc
Address found but labeling not clear or easy to find
Executing addresses could not be found

2. Is the code actively being used? (%)

Answer: 100%

Activity is 260 transactions a day on contract 0x3472A5A71965499acd81997a54BBA8D852C6E53d, as indicated in the Appendix.

Percentage Score Guidance:
More than 10 transactions a day
More than 10 transactions a week
More than 10 transactions a month
Less than 10 transactions a month
No activity

3. Is there a public software repository? (Y/N)

Answer: Yes

Is there a public software repository with the code at a minimum, but also normally test and scripts. Even if the repository was created just to hold the files and has just 1 transaction, it gets a "Yes". For teams with private repositories, this answer is "No"

Score Guidance:
There is a public software repository with the code at a minimum, but also normally test and scripts. Even if the repository was created just to hold the files and has just 1 transaction.
For teams with private repositories.

4. Is there a development history visible? (%)

Answer: 100%

An astonishing 1059 commits paired with 103 branches make BadgerDAO's development history exceptionally rich.

This metric checks if the software repository demonstrates a strong steady history. This is normally demonstrated by commits, branches and releases in a software repository. A healthy history demonstrates a history of more than a month (at a minimum).

Percentage Score Guidance:
Any one of 100+ commits, 10+branches
Any one of 70+ commits, 7+branches
Any one of 50+ commits, 5+branches
Any one of 30+ commits, 3+branches
Less than 2 branches or less than 30 commits

5. Is the team public (not anonymous)? (Y/N)

Answer: Yes

For a "Yes" in this question, the real names of some team members must be public on the website or other documentation (LinkedIn, etc). If the team is anonymous, then this question is a "No".



This section looks at the software documentation. The document explaining these questions is here.

6. Is there a whitepaper? (Y/N)

Answer: Yes

7. Are the basic software functions documented? (Y/N)

Answer: Yes

All basic software functions are documented under the "Developer Info" subheading.

8. Does the software function documentation fully (100%) cover the deployed contracts? (%)

Answer: 80%

The most important deployed contracts are covered in the "Developer Info" section of their documentation.

Percentage Score Guidance:
All contracts and functions documented
Only the major functions documented
79 - 1%
Estimate of the level of software documentation
No software documentation

9. Are there sufficiently detailed comments for all functions within the deployed contract code (%)

Answer: 30%

Code examples are in the Appendix. As per the SLOC, there is 30% commenting to code (CtC).

The Comments to Code (CtC) ratio is the primary metric for this score.

Percentage Score Guidance:
CtC > 100 Useful comments consistently on all code
90 - 70%
CtC > 70 Useful comment on most code
60 - 20%
CtC > 20 Some useful commenting
CtC < 20 No useful commenting

10. Is it possible to trace from software documentation to the implementation in code (%)

Answer: 60%

Documentation covers some of the BadgerDao code, but almost all functions are non-explicitly traceable to their source code. ​

Percentage Score Guidance:
Clear explicit traceability between code and documentation at a requirement level for all code
Clear association between code and documents via non explicit traceability
Documentation lists all the functions and describes their functions
No connection between documentation and code



11. Full test suite (Covers all the deployed code) (%)

Answer: 100%

Code examples are in the Appendix. As per the SLOC, there is 147% testing to code (TtC).

This score is guided by the Test to Code ratio (TtC). Generally a good test to code ratio is over 100%. However the reviewers best judgement is the final deciding factor.

Percentage Score Guidance:
TtC > 120% Both unit and system test visible
TtC > 80% Both unit and system test visible
TtC < 80% Some tests visible
No tests obvious

12. Code coverage (Covers all the deployed lines of code, or explains misses) (%)

Answer: 55%

An audit conducted in August 2021 found 43.78% of Badger Finance's deployed code to be covered. In additon, the Zokyo audit has performed a code coverage test that returned an average of around 55%.

Percentage Score Guidance:
Documented full coverage
99 - 51%
Value of test coverage from documented results
No indication of code coverage but clearly there is a reasonably complete set of tests
Some tests evident but not complete
No test for coverage seen

13. Scripts and instructions to run the tests? (Y/N)

Answer: Yes

14. Report of the results (%)

Answer: 0%

No test result was found.

Percentage Score Guidance:
Detailed test report as described below
GitHub code coverage report visible
No test report evident

15. Formal Verification test done (%)

Answer: 0%

Badger Finance has not undergone a formal verification test.

16. Stress Testing environment (%)

Answer: 100%

Badger Finance launches new vaults with stringent limits for an initial testing period to allow for bugs to be fixed before these vaults are open to the general public. There is also clear evidence of Rinkeby testnet usage at https://github.com/Badger-Finance/badger-system/blob/master/badger-rinkeby.json.



This section looks at the 3rd party software audits done. It is explained in this document.

17. Did 3rd Party audits take place? (%)

Answer: 100%

Four different audits have taken place in the past year, all of which are public, and have been published pre-mainnet launch.    - The Zokyo audit found that BadgerDAO was very secure and well-written. The only issue that was underlined in the report was an informational language usage flag in the Badger code. Essentially, they use internal functions for modifier roles in some of their contracts, but they should just use modifiers instead. Overall, Zokyo found nothing that could actively pose a risk to the smart contracts' integrity.    - The Haechi audit found several minor and informational issues for the Badger team to work on. Unfortunately, there is no indication as to what the team did to resolve them. The underlined issues include a StakingReward bug where the contract's notifyRewardAmount() function would not check if it received rewards. This could lead to higher rewards for more active stakers, and potentially no rewards for others. In the same contract, another bug includes the notifyRewardAmount() function, where users could potentially be subjected to lower rewards rates. These are the most important findings, and all the other ones touch upon the language use and how it can be optimized.    - The audit performed by Defi Yield did not find any issues. Rather, the report's only recommendations were to change the Controller and Sett contracts' governance addresses to "real" governance addresses. This would imply that both of these contracts are not linked to the actual BadgerDAO governance addresses.    - The Quantstamp audit report unveiled multiple issues. Several of them were of medium risk, and one of them was high risk. The issue is that most of them, including the high-risk one, are not yet resolved. The high risk issue comprises the fact that the Core Badger contract has unbounded trust in its peaks. Peaks, as defined by the Badger documentation, are any third-party integration within the protocol. The issue here is that these peak contracts are telling the Core contract how many tokens to redeem, mint, or burn without limits or any form of verification. This means that any malicious peak contract could completely mess with the overrall Badger token integrity. As this issue is yet unresolved, this poses a serious problem. All other mentionned issues are either medium, low, or of unknown risk, and mostly affect the Core contract.

Percentage Score Guidance:
Multiple Audits performed before deployment and results public and implemented or not required
Single audit performed before deployment and results public and implemented or not required
Audit(s) performed after deployment and no changes required. Audit report is public
Audit(s) performed after deployment and changes needed but not implemented
No audit performed
Audit Performed after deployment, existence is public, report is not public and no improvements deployed OR smart contract address not found, (where question 1 is 0%)
Deduct 25% if code is in a private repo and no note from auditors that audit is applicable to deployed code.

18. Is the bug bounty acceptable high? (%)

Answer: 90%

Badger Finance has a $750k active bug bounty program.

Percentage Score Guidance:
Bounty is 10% TVL or at least $1M AND active program (see below)
Bounty is 5% TVL or at least 500k AND active program
Bounty is 5% TVL or at least 500k
Bounty is 100k or over AND active program
Bounty is 100k or over
Bounty is 50k or over AND active program
Bounty is 50k or over
Bug bounty program bounty is less than 50k
No bug bounty program offered
An active program means that a third party (such as Immunefi) is actively driving hackers to the site. An inactive program would be static mentions on the docs.

Access Controls


This section covers the documentation of special access controls for a DeFi protocol. The admin access controls are the contracts that allow updating contracts or coefficients in the protocol. Since these contracts can allow the protocol admins to "change the rules", complete disclosure of capabilities is vital for user's transparency. It is explained in this document.

19. Can a user clearly and quickly find the status of the access controls (%)

Answer: 100%

The access controls are clearly outlined under the security section of their website.

Percentage Score Guidance:
Clearly labelled and on website, docs or repo, quick to find
Clearly labelled and on website, docs or repo but takes a bit of looking
Access control docs in multiple places and not well labelled
Access control docs in multiple places and not labelled
Admin Control information could not be found

20. Is the information clear and complete (%)

Answer: 80%

a) All contracts are clearly labelled as upgradeable (or not) -- 20% -- all important contracts are clearly labelled as upgradeable in a governance proposal, though not all deployed contracts are covered.    b) The type of ownership is clearly indicated (OnlyOwner / MultiSig / Defined Roles) -- 30% -- the ownership is clearly outlined in both the security section of their website and in the previous governance proposal.    c) The capabilities for change in the contracts are described -- 30% -- contract upgradeability is identified in the security pages.        

Percentage Score Guidance:
All the contracts are immutable -- 100% OR
a) All contracts are clearly labelled as upgradeable (or not) -- 30% AND
b) The type of ownership is clearly indicated (OnlyOwner / MultiSig / Defined Roles) -- 30% AND
c) The capabilities for change in the contracts are described -- 30%

21. Is the information in non-technical terms that pertain to the investments (%)

Answer: 90%

Description of admin controls is in clear and non-technical terms, and relates to user funds' safety.

Percentage Score Guidance:
All the contracts are immutable
Description relates to investments safety and updates in clear, complete non-software language
Description all in software specific language
No admin control information could be found

22. Is there Pause Control documentation including records of tests (%)

Answer: 40%

The documents mentions a "guardian" capable of pausing the protocol, but there is little elaboration.

Percentage Score Guidance:
All the contracts are immutable or no pause control needed and this is explained OR Pause control(s) are clearly documented and there is records of at least one test within 3 months
Pause control(s) explained clearly but no evidence of regular tests
Pause controls mentioned with no detail on capability or tests
Pause control not documented or explained


 The author of this review is Rex of DeFi Safety.

Email: rex@defisafety.com
Twitter: @defisafety

I started with Ethereum just before the DAO and that was a wonderful education.  It showed the importance of code quality. The second Parity hack also showed the importance of good process.  Here my aviation background offers some value. Aerospace knows how to make reliable code using quality processes.
I was coaxed to go to EthDenver 2018 and there I started SecuEth.org with Bryant and Roman. We created guidelines on good processes for blockchain code development. We got EthFoundation funding to assist in their development Process Quality Reviews are an extension of the SecurEth guidelines that will further increase the quality processes in Solidity and Vyper development. DeFiSafety is my full time gig and we are working on funding vehicles for a permanent staff.

1contract BadgerBridgeAdapter is OwnableUpgradeable, ReentrancyGuardUpgradeable {
2    using SafeMathUpgradeable for uint256;
3    using SafeERC20 for IERC20;
45    IERC20 public renBTC;
6    IERC20 public wBTC;
78    // RenVM gateway registry.
9    IGatewayRegistry public registry;
10    // Swap router that handles swap routing optimizations.
11    ISwapStrategyRouter public router;
1213    event RecoverStuck(uint256 amount, uint256 fee);
14    event Mint(uint256 renbtc_minted, uint256 wbtc_swapped, uint256 fee);
15    event Burn(uint256 renbtc_burned, uint256 wbtc_transferred, uint256 fee);
16    event SwapError(bytes error);
1718    address public rewards;
19    address public governance;
2021    uint256 public mintFeeBps;
22    uint256 public burnFeeBps;
23    uint256 private percentageFeeRewardsBps;
24    uint256 private percentageFeeGovernanceBps;
2526    uint256 public constant MAX_BPS = 10000;
2728    mapping(address => bool) public approvedVaults;
2930    // Configurable permissionless curve lp token wrapper.
31    address curveTokenWrapper;
3233    // Make struct for mint args, otherwise too many local vars (stack too deep).
34    struct MintArguments {
35        uint256 _mintAmount;
36        uint256 _mintAmountMinusFee;
37        uint256 _fee;
38        uint256 _slippage;
39        address _vault;
40        address _user;
41        address _token;
42    }
4344    function initialize(
45        address _governance,
46        address _rewards,
47        address _registry,
48        address _router,
49        address _wbtc,
50        uint256[4] memory _feeConfig
51    ) public initializer {
52        __Ownable_init();
53        __ReentrancyGuard_init();
5455        require(_governance != address(0x0), "must set governance address");
56        require(_rewards != address(0x0), "must set rewards address");
57        require(_registry != address(0x0), "must set registry address");
58        require(_router != address(0x0), "must set router address");
59        require(_wbtc != address(0x0), "must set wBTC address");
6061        governance = _governance;
62        rewards = _rewards;
6364        registry = IGatewayRegistry(_registry);
65        router = ISwapStrategyRouter(_router);
66        renBTC = registry.getTokenBySymbol("BTC");
67        wBTC = IERC20(_wbtc);
6869        mintFeeBps = _feeConfig[0];
70        burnFeeBps = _feeConfig[1];
71        percentageFeeRewardsBps = _feeConfig[2];
72        percentageFeeGovernanceBps = _feeConfig[3];
73    }
7475    function version() external pure returns (string memory) {
76        return "1.1";
77    }
7879    // NB: This recovery fn only works for the BTC gateway (hardcoded and only one supported in this adapter).
80    function recoverStuck(
81        // encoded user args
82        bytes calldata encoded,
83        // darkdnode args
84        uint256 _amount,
85        bytes32 _nHash,
86        bytes calldata _sig
87    ) external nonReentrant {
88        // Ensure sender matches sender of original tx.
89        uint256 start = encoded.length - 32;
90        address sender = abi.decode(encoded[start:], (address));
91        require(sender == msg.sender);
9293        bytes32 pHash = keccak256(encoded);
94        uint256 _mintAmount = registry.getGatewayBySymbol("BTC").mint(pHash, _amount, _nHash, _sig);
95        uint256 _fee = _processFee(renBTC, _mintAmount, mintFeeBps);
9697        emit RecoverStuck(_mintAmount, _fee);
9899        renBTC.safeTransfer(msg.sender, _mintAmount.sub(_fee));
100    }
101102    function mint(
103        // user args
104        address _token, // either renBTC or wBTC
105        uint256 _slippage,
106        address _user,
107        address _vault,
108        / darknode args
109        uint256 _amount,
110        bytes32 _nHash,
111        bytes calldata _sig
112    ) external nonReentrant {
113        require(_token == address(renBTC) || _token == address(wBTC), "invalid token address");
114115        / Mint renBTC tokens
116        bytes32 pHash = keccak256(abi.encode(_token, _slippage, _user, _vault));
117        uint256 mintAmount = registry.getGatewayBySymbol("BTC").mint(pHash, _amount, _nHash, _sig);
118119        require(mintAmount > 0, "zero mint amount");
120121        uint256 fee = _processFee(renBTC, mintAmount, mintFeeBps);
122        uint256 mintAmountMinusFee = mintAmount.sub(fee);
123124        MintArguments memory args = MintArguments(mintAmount, mintAmountMinusFee, fee, _slippage, _vault, _user, _token);
125        bool success = mintAdapter(args);
126127        if (!success) {
128            renBTC.safeTransfer(_user, mintAmountMinusFee);
129        }
130    }
131132    function burn(
133        // user args
134        address _token, // either renBTC or wBTC
135        address _vault,
136        uint256 _slippage,
137        bytes calldata _btcDestination,
138        uint256 _amount
139    ) external nonReentrant {
140        require(_token == address(renBTC) || _token == address(wBTC), "invalid token address");
141        require(!(_vault != address(0) && !approvedVaults[_vault]), "Vault not approved");
142143        bool isVault = _vault != address(0);
144        bool isRenBTC = _token == address(renBTC);
145        IERC20 token = isRenBTC ? renBTC : wBTC;
146        uint256 startBalanceRenBTC = renBTC.balanceOf(address(this));
147        uint256 startBalanceWBTC = wBTC.balanceOf(address(this));
148149        // Vaults can require up to two levels of unwrapping.
150        if (isVault) {
151            // First level of unwrapping for sett tokens.
152            IERC20(_vault).safeTransferFrom(msg.sender, address(this), _amount);
153            IERC20 vaultToken = IBridgeVault(_vault).token();
154155            uint256 beforeBalance = vaultToken.balanceOf(address(this));
156            IBridgeVault(_vault).withdraw(IERC20(_vault).balanceOf(address(this)));
157            uint256 balance = vaultToken.balanceOf(address(this)).sub(beforeBalance);
158159            // If the vault token does not match requested burn token, then we need to further unwrap
160            // vault token (e.g. withdrawing from crv sett gets us crv lp tokens which need to be unwrapped to renbtc).
161            if (address(vaultToken) != _token) {
162                vaultToken.safeTransfer(curveTokenWrapper, balance);
163                ICurveTokenWrapper(curveTokenWrapper).unwrap(_vault);
164            }
165        } else {
166            token.safeTransferFrom(msg.sender, address(this), _amount);
167        }
168169        uint256 wbtcTransferred = wBTC.balanceOf(address(this)).sub(startBalanceWBTC);
170171        if (!isRenBTC) {
172            _swapWBTCForRenBTC(wbtcTransferred, _slippage);
173        }
174175        uint256 toBurnAmount = renBTC.balanceOf(address(this)).sub(startBalanceRenBTC);
176        uint256 fee = _processFee(renBTC, toBurnAmount, burnFeeBps);
177178        uint256 burnAmount = registry.getGatewayBySymbol("BTC").burn(_btcDestination, toBurnAmount.sub(fee));
179180        emit Burn(burnAmount, wbtcTransferred, fee);
181    }
182183    function mintAdapter(MintArguments memory args) internal returns (bool) {
184        if (args._vault != address(0) && !approvedVaults[args._vault]) {
185            return false;
186        }
187188        uint256 wbtcExchanged;
189        bool isVault = args._vault != address(0);
190        bool isRenBTC = args._token == address(renBTC);
191        IERC20 token = isRenBTC ? renBTC : wBTC;
193if (!isRenBTC) {
194            // Try and swap and transfer wbtc if token wbtc specified.
195            uint256 startBalance = token.balanceOf(address(this));
196            if (!_swapRenBTCForWBTC(args._mintAmountMinusFee, args._slippage)) {
197                return false;
198            }
199            uint256 endBalance = token.balanceOf(address(this));
200            wbtcExchanged = endBalance.sub(startBalance);
201        }
202203        emit Mint(args._mintAmount, wbtcExchanged, args._fee);
204205        uint256 amount = isRenBTC ? args._mintAmountMinusFee : wbtcExchanged;

Solidity Contracts


Comments to Code: 1413 / 4770 =  30 %

JavaScript Tests


Tests to Code: 7029 / 4770 = 147 %