Audit Comp | eBTC
eBTC is a collateralized crypto asset soft pegged to the price of Bitcoin and built on the Ethereum network. It is based on the Liquity protocol and backed exclusively by Staked Ether (stETH). The protocol is designed with an immutable core with minimized counterparty reliance and governance.
Status
All eBTC contracts will be deployed shortly and the assets in the ‘assets in scope’ table above will be updated with the live contracts. These will be fully identical with the code in the github links.
BadgerDAO’s up to date codebase can be found at https://github.com/ebtc-protocol/ebtc/tree/release-0.7
Whitehat Educational Resources & Technical Info
- Read the changelog here to learn the differences between this release and the previous Code4rena contest.
- Primary documentation
- Primary Readme
- In-depth Introductory Video
- eBTC Cheatsheet with additional videos and an up to date list of additional resources
- Website: ebtc.finance
- Twitter: eBTCProtocol
- JohnnyTime eBTC Overview
- eBTC x Immunefi Technical Walkthrough
Is this an upgrade of an existing system? If so, which? And what are the main differences?
No, this is a net new protocol with no relation to past BadgerDAO projects.
Where do you suspect there may be bugs?
We are concerned around core accounting (yield split and debt redistribution), the ability to abuse or significantly DOS the liquidation or redemption mechanics, and significant rounding errors.
Here is our full list of invariants.
Calling out accounting invariants:
-
Cdps have a pending accounting state that is only updated when that Cdp is interacted with. Cdps can stay out of sync for a long time, there is no way to force them to synchronize unless the owner or approved positionManager interacts with the Cdp.
-
For numerical exploits, the total stETH in existence or that could be in existence must be considered.
-
Loss of yield must be considered relative to other changes. For example, if a bunch of positive rebases happen and then there’s a slash, and the system is not updated until that point, those positive rebases will be lost.
-
Max 1 wei of unallocated debt per Cdp per debt redisttribution event is a theoretical maximum that can happen. This does not scale to a reasonable value with real-world values.
-
SortedCdps view functions are not for on-chain users.
What ERC20 / ERC721 / ERC777 / ERC1155 token standards are supported? Which are not?
stETH is supported as collateral, which is a rebasing token. The eBTC token is “standard” as far as these properties go.
What emergency actions may you want to use as a reason to invalidate or downgrade an otherwise valid bug report?
Emergency actions could involve:
- pausing flashloans
- pausing redemptions
- switching between the stETH 1:1 fixed oracle to the stETH/ETH market rate oracle
What Roles are there, and what capacities do they have?
There are a number of roles maintained by the Governor contract.
It has role-based access control that gates function signatures by role, based on solmate RolesAuthority.
The following roles exist in the system:
Role 0: // The Admin, manages the setting of roles and associated properties
- governor.setRoleName()
- governor.setUserRole()
- governor.setRoleCapability()
- governor.setPublicCapability()
- governor.burnCapability()
- governor.transferOwnership()
- governor.setAuthority()
Role 1: // Extensible minter, can mint eBTC tokens at-will. Unused at the start.
- ebtcToken.mint(address,uint256)
Role 2: // Extensible burner, can burn eBTC tokens at will. Unused at the start.
- ebtcToken.burn(address,uint256)
- ebtcToken.burn(uint256)
Role 3: // Redemption and yield split parameter manager
- cdpManager.setStakingRewardSplit(uint256)
- cdpManager.setRedemptionFeeFloor(uint256)
- cdpManager.setMinuteDecayFactor(uint256)
- cdpManager.setBeta(uint256)
- cdpManager.setGracePeriod(uint256)
Role 4: // Emergency pauser
- cdpManager.setRedemptionsPaused(bool)
- activePool.setFlashLoansPaused(bool)
- borrowerOperations.setFlashLoansPaused(bool)
5: // Flashloan fee parameter manager
- borrowerOperations.setFeeBps(uint256)
- activePool.setFeeBps(uint256)
6: // Token sweeper. Note that the sweeping recipient is pre-set and members of this role can’t claim the tokens to themselves.
- activePool.sweepToken(address,uint256)
- collSurplusPool.sweepToken(address,uint256)
7: // Fee recipient sweeper
- activePool.claimFeeRecipientCollShares(uint256)
8: // Primary oracle manager
- ebtcFeed.setPrimaryOracle(address)
9: // Secondary oracle manager
- ebtcFeed.setSecondaryOracle(address)
10: // Price Feed fallback manager
- priceFeed.setFallbackCaller(address)
11: // stETH 1:1 to market rate switcher, coupled with appropriate redemption rate change. Unused initially, but intended to give to a bot with security guardrails around conditions where sETH/ETH feed is appropriate.
- priceFeed.setCollateralFeedSource(bool)
- cdpManager.setRedemptionFeeFloor(uint256)
There are five permissioned contracts
- High Security Timelock
The ultimate governance administrator also holds most permissions. The only permissions it doesn’t have are eBTC minting and burning (which no contract has in this configuration)
Roles: [0, 3, 4, 5, 6, 7, 8, 9, 10]
- Low Security Timelock
A faster timelock that can perform all but the most sensitive operations. It notably doesn’t have admin capabilities on the Governor, or the ability to swap primary oracle.
Roles: [3, 4, 5, 6, 7, 9, 10]
- Security Multisig
The higher security multisig. It can propose transactions into the high security and low security timelocks and call pausing functions directly
Roles: [4]
- Tech Ops Multisig
The lower security multisig. It can propose transactions into the low security timelock and call pausing functions directly.
Roles: [4]
- Fee Recipient Multisig
Manages the yield split fee collections serving as a destination for sweeping tokens.
Roles: [6, 7]
What external dependencies are there?
We’ve got a short list for eBTC.
- Chainlink oracles
- stETH token
Where might whitehats confuse out-of-scope code to be in-scope?
Files in the repo that are not specifically in scope are the most likely source of confusion, such as test files. This project has a low number of external dependencies and doesn’t interact with much.
Assume the PriceFeed.sol will never have the fallback oracle assigned, this will remain at zero. This governance capability can be permanently burned to enable this spec.
Unbounded gas is effectively possible via poor hint selection, this is known and should not constitute an unbounded gas finding
Are there any unusual points about your protocol that may confuse whitehats?
The rebasing nature of the stETH token and conversion between balances and shares.
What is the test suite setup information?
We have several test suites, they can be found here:
- Hardhat test directory
- Foundry test directory
- Several Echdina/Medusa configs
Public Disclosure of Known Issues
Bug reports covering previously-discovered bugs (listed below) are not eligible for a reward within this program. This includes known issues that the project is aware of but has consciously decided not to “fix”, necessary code changes, or any implemented operational mitigating procedures that can lessen potential risk.
-
Account's stETH balance getting lower on 1 or 2 wei due to rounding down integer math: https://github.com/lidofinance/lido-dao/issues/442
-
Steal of shares using transferSharesFrom due to math rounding issues: https://github.com/lidofinance/lido-dao/issues/796
-
Redeem to change partial NICR in order to grief redemption, or, open a cdp that front-runs the redemption to grief the redemption: https://github.com/code-423n4/2023-10-badger-findings/issues/226
-
stETH upgrade risk is considered known
-
Unbounded gas is via poor hint selection
-
Chainlink misbehaviour and single privilege is considered known
-
Known Rounding Behaviour: Debt Redistribution Precision Loss:
- Every time a Cdp updates with one or more pending debt redistribution events, it can possibly lose 1 wei of debt to rounding. This 1 wei of debt will still be accounted for in systemDebt. This is “rounding against the protocol” in the sense that the systemDebt will become 1 wei higher than the sum of all active Cdp debt for each instance of this occuring. It’s unbounded. This leads to a theoretical maximum differential of 1 wei per Cdp per debt redistribution event between the sum of all active Cdp debt and the systemDebt.
-
Known Rounding Behaviour: Collateral Rebase Precision Loss:
- However, this rounding behavior also exists during collateral rebase events. In this case it “rounds against the user”. Each time a Cdp is updated with any pending rebases to apply, it can lose 1 wei of collateral versus what it would have mathematically speaking due to precision loss of division. The systemCollShares becomes 1 wei higher than the sum of all active Cdp collShares in this instance. This leads to a theoretical maximum differential of 1 wei per Cdp per rebase event between the systemCollShares and the sum of all active Cdp collShares.
-
SortedCdps list can get out of order with debt redistribution. This is considered acceptable and does not affect liquidations, only redemption ordering.
Previous Audits
BadgerDAO’s has provided these completed audit review reports for reference. Any unfixed vulnerabilities mentioned in these reports are not eligible for a reward.
- RiskDAO: https://github.com/Risk-DAO/Reports/blob/main/eBTC.pdf
- Trust: https://badger.com/images/uploads/trust-ebtc-audit-report.pdf
- Spearbit: https://badger.com/images/uploads/ebtc-security-review-spearbit.pdf
- Cantina: https://badger.com/images/uploads/ebtc-security-review-cantina.pdf
- Code4rena: https://code4rena.com/reports/2023-10-badger
- Shung: https://gist.github.com/Shungy/ebeb9366e970ecbf4da1eda296581e47
Asset In Scope Policies
Asset Accuracy Assurance
Bugs found on assets incorrectly listed in-scope will be considered valid and be rewarded.
Private Known Issues Reward Policy
Private known issues, meaning known issues that were not publicly disclosed, are considered valid, but will be downgraded by one severity.
Known Issue Assurance
BadgerDAO commits to providing Known Issue Assurance to bug submissions through their program. This means that BadgerDAO will either disclose known issues publicly, or at the very least, privately via a self-reported bug submission.
In a potential scenario of a mediation, this allows for a more objective and streamlined process, in order to prove that an issue is known. Otherwise, assuming the bug report is valid, it would result in the report being considered as in-scope, and due a reward.
Primacy of Impact vs Primacy of Rules
BadgerDAO adheres to the Primacy of Impact for all impacts.
Primacy of Impact means that the impact is prioritized rather than a specific asset. This encourages security researchers to report on all bugs with an in-scope impact, even if the affected assets are not in scope. For more information, please see Best Practices: Primacy of Impact
When submitting a report on Immunefi’s dashboard, the security researcher should select the Primacy of Impact asset placeholder. If the team behind this project has multiple programs, those other programs are not covered under Primacy of Impact for this program. Instead, check if those other projects have a bug bounty program on Immunefi.
If the project has any testnet and/or mock files, those will not be covered under Primacy of Impact.