Badger DAO Protocol Adapter
The Badger adapter enables the Yieldinator Facet to integrate with Badger DAO's yield-generating vaults, allowing users to earn yield on Bitcoin-related assets through Badger's Sett vaults and farming strategies.
Overview
Badger DAO is a decentralized autonomous organization focused on bringing Bitcoin to DeFi. The protocol offers Sett vaults (similar to Yearn vaults) that automatically compound yield from various farming strategies for BTC-related assets like WBTC, renBTC, and various tokenized Bitcoin derivatives. The Badger adapter facilitates deposits into these Sett vaults, manages vault positions, and harvests yield including BADGER token rewards.
Implementation Details
Contract: BadgerAdapter.sol
BadgerAdapter.solThe adapter implements the standard YieldinatorAdapter interface with Badger-specific functionality:
contract BadgerAdapter is YieldinatorAdapter {
using SafeERC20 for IERC20;
// Badger contracts
address public badgerToken; // BADGER governance token
address public bBadgerToken; // Staked BADGER token
IBadgerSett public bBadgerSett; // BADGER staking Sett
// Vault configuration
struct VaultConfig {
address sett; // Badger Sett (vault) address
bool stakeBadger; // Whether to stake BADGER rewards
bool active; // Whether the vault is active
}
// Mapping from token to vault configuration
mapping(address => VaultConfig) public tokenToVault;
// Deposited amounts per token
mapping(address => uint256) public depositedAmount;
// Constructor
constructor(
address _admin,
address _badgerToken,
address _bBadgerToken,
address _bBadgerSett
) YieldinatorAdapter("Badger DAO", _admin) {
require(_badgerToken != address(0), "BadgerAdapter: BADGER token cannot be zero address");
require(_bBadgerToken != address(0), "BadgerAdapter: bBADGER token cannot be zero address");
require(_bBadgerSett != address(0), "BadgerAdapter: bBADGER Sett cannot be zero address");
badgerToken = _badgerToken;
bBadgerToken = _bBadgerToken;
bBadgerSett = IBadgerSett(_bBadgerSett);
}
}Key Functions
Vault Registration
Before using the adapter for a specific token, the Sett vault must be registered:
function registerVault(
address _token,
address _sett,
bool _stakeBadger
) external onlyRole(ADMIN_ROLE) {
require(_token != address(0), "BadgerAdapter: token cannot be zero address");
require(_sett != address(0), "BadgerAdapter: Sett cannot be zero address");
// Verify the token is accepted by the Sett
address settToken = IBadgerSett(_sett).token();
require(settToken == _token, "BadgerAdapter: token mismatch with Sett");
// Create and store vault configuration
VaultConfig memory config = VaultConfig({
sett: _sett,
stakeBadger: _stakeBadger,
active: true
});
tokenToVault[_token] = config;
emit VaultRegistered(_token, _sett);
}Deposit
The deposit function handles depositing assets into Badger Sett vaults:
function deposit(address _token, uint256 _amount)
external
override
onlyRole(YIELDINATOR_ROLE)
nonReentrant
returns (bool)
{
require(_amount > 0, "BadgerAdapter: amount must be greater than 0");
VaultConfig memory config = tokenToVault[_token];
require(config.active, "BadgerAdapter: vault not active for token");
// Transfer token from caller
IERC20(_token).safeTransferFrom(msg.sender, address(this), _amount);
// Approve Sett to use tokens
IERC20(_token).safeApprove(config.sett, _amount);
// Deposit into Badger Sett
uint256 balanceBefore = IBadgerSett(config.sett).balanceOf(address(this));
IBadgerSett(config.sett).deposit(_amount);
uint256 balanceAfter = IBadgerSett(config.sett).balanceOf(address(this));
// Calculate Sett tokens received
uint256 settTokensReceived = balanceAfter - balanceBefore;
require(settTokensReceived > 0, "BadgerAdapter: no Sett tokens received");
// Update deposited amount
depositedAmount[_token] += _amount;
emit Deposited(_token, _amount);
return true;
}Withdraw
The withdraw function handles withdrawing assets from Badger Sett vaults:
function withdraw(address _token, uint256 _amount)
external
override
onlyRole(YIELDINATOR_ROLE)
nonReentrant
returns (uint256)
{
require(_amount > 0, "BadgerAdapter: amount must be greater than 0");
require(_amount <= depositedAmount[_token], "BadgerAdapter: insufficient balance");
VaultConfig memory config = tokenToVault[_token];
require(config.active, "BadgerAdapter: vault not active for token");
// Calculate proportion of Sett tokens to redeem
uint256 settBalance = IBadgerSett(config.sett).balanceOf(address(this));
uint256 settToRedeem = settBalance * _amount / depositedAmount[_token];
// Withdraw from Badger Sett
uint256 balanceBefore = IERC20(_token).balanceOf(address(this));
IBadgerSett(config.sett).withdraw(settToRedeem);
uint256 balanceAfter = IERC20(_token).balanceOf(address(this));
// Calculate amount received
uint256 amountReceived = balanceAfter - balanceBefore;
require(amountReceived > 0, "BadgerAdapter: no tokens received");
// Transfer withdrawn tokens to caller
IERC20(_token).safeTransfer(msg.sender, amountReceived);
// Update deposited amount
depositedAmount[_token] -= _amount;
// Harvest any BADGER rewards
_harvestBadgerRewards(config);
emit Withdrawn(_token, amountReceived);
return amountReceived;
}Harvest Yield
The harvestYield function collects BADGER rewards and optionally stakes them:
function harvestYield(address _token)
external
override
onlyRole(YIELDINATOR_ROLE)
nonReentrant
returns (uint256)
{
VaultConfig memory config = tokenToVault[_token];
require(config.active, "BadgerAdapter: vault not active for token");
// Claim BADGER rewards if available
// Note: Some Badger Setts automatically distribute rewards, others require explicit claiming
IBadgerSett sett = IBadgerSett(config.sett);
if (address(sett) != address(0) && _canClaimRewards(sett)) {
sett.claim();
}
return _harvestBadgerRewards(config);
}
function _harvestBadgerRewards(VaultConfig memory config) internal returns (uint256) {
uint256 badgerBalance = IERC20(badgerToken).balanceOf(address(this));
// If staking BADGER rewards is enabled, stake in bBADGER Sett
if (config.stakeBadger && badgerBalance > 0) {
IERC20(badgerToken).safeApprove(address(bBadgerSett), badgerBalance);
bBadgerSett.deposit(badgerBalance);
// Calculate bBADGER received
uint256 bBadgerBalance = IERC20(bBadgerToken).balanceOf(address(this));
emit YieldHarvested(badgerToken, badgerBalance, bBadgerToken, bBadgerBalance);
return bBadgerBalance;
} else if (badgerBalance > 0) {
// Transfer BADGER rewards to caller
IERC20(badgerToken).safeTransfer(msg.sender, badgerBalance);
emit YieldHarvested(badgerToken, badgerBalance, address(0), 0);
return badgerBalance;
}
return 0;
}
function _canClaimRewards(IBadgerSett sett) internal view returns (bool) {
// Check if the Sett supports claiming rewards
// Implementation depends on Badger's specific reward distribution mechanism
try sett.canClaim(address(this)) returns (bool canClaim) {
return canClaim;
} catch {
return false;
}
}Emergency Withdraw
The emergencyWithdraw function provides a safety mechanism to withdraw all funds:
function emergencyWithdraw(address _token)
external
override
onlyRole(ADMIN_ROLE)
nonReentrant
returns (uint256)
{
VaultConfig memory config = tokenToVault[_token];
require(config.active, "BadgerAdapter: vault not active for token");
// Withdraw all Sett tokens from Badger Sett
uint256 settBalance = IBadgerSett(config.sett).balanceOf(address(this));
if (settBalance > 0) {
IBadgerSett(config.sett).withdraw(settBalance);
}
// Transfer all token balance to caller
uint256 tokenBalance = IERC20(_token).balanceOf(address(this));
if (tokenBalance > 0) {
IERC20(_token).safeTransfer(msg.sender, tokenBalance);
}
// Transfer all BADGER and bBADGER to caller
uint256 badgerBalance = IERC20(badgerToken).balanceOf(address(this));
if (badgerBalance > 0) {
IERC20(badgerToken).safeTransfer(msg.sender, badgerBalance);
}
uint256 bBadgerBalance = IERC20(bBadgerToken).balanceOf(address(this));
if (bBadgerBalance > 0) {
IERC20(bBadgerToken).safeTransfer(msg.sender, bBadgerBalance);
}
// Reset deposited amount
depositedAmount[_token] = 0;
emit EmergencyWithdrawn(_token, tokenBalance);
return tokenBalance;
}APY Calculation
The getCurrentAPY function calculates the current APY for a token in the Badger Sett:
function getCurrentAPY(address _token)
external
view
override
returns (uint256)
{
VaultConfig memory config = tokenToVault[_token];
if (!config.active) return 0;
// Get base APY from Badger Sett
uint256 baseAPY = _calculateSettAPY(config.sett);
// Calculate BADGER rewards APY
uint256 badgerRewardsAPY = _calculateBadgerRewardsAPY(config.sett);
// Add additional APY from staking BADGER if enabled
uint256 stakingAPY = config.stakeBadger ? _calculateStakingAPY() : 0;
// Combine APYs
return baseAPY + badgerRewardsAPY + stakingAPY;
}
function _calculateSettAPY(address _sett) internal view returns (uint256) {
// Calculate Sett APY based on performance fee, harvest frequency, and strategy returns
// Implementation depends on Badger's specific APY calculation method
return IBadgerSett(_sett).getPricePerFullShare() * 100 / 1e18;
}
function _calculateBadgerRewardsAPY(address _sett) internal view returns (uint256) {
// Calculate BADGER rewards APY based on Sett's reward rate and total value locked
// Implementation depends on Badger's specific reward distribution mechanism
return IBadgerSett(_sett).getRewardsAPY();
}
function _calculateStakingAPY() internal view returns (uint256) {
// Calculate staking APY based on bBADGER growth rate
return bBadgerSett.getPricePerFullShare() * 100 / 1e18;
}Get Total Deposited
The getTotalDeposited function returns the total amount of tokens deposited:
function getTotalDeposited(address _token)
external
view
override
returns (uint256)
{
return depositedAmount[_token];
}Usage Example
Here's an example of how to use the Badger adapter with the Yieldinator Facet:
// Register the Badger adapter with the Yieldinator Facet
address badgerAdapter = address(new BadgerAdapter(
admin,
badgerToken,
bBadgerToken,
bBadgerSett
));
yieldinatorFacet.registerAdapter("Badger DAO", badgerAdapter);
// Register a vault for WBTC
BadgerAdapter(badgerAdapter).registerVault(
WBTC,
WBTC_SETT,
true // Stake BADGER rewards
);
// Add a yield strategy using the Badger adapter
yieldinatorFacet.addYieldStrategy(
"Badger WBTC Sett",
badgerAdapter,
WBTC,
2, // Medium risk level
1500 // 15.00% APY
);Security Considerations
Strategy Risk: Badger Sett vaults employ complex yield farming strategies that may expose users to additional risks from the underlying protocols.
Smart Contract Risk: The adapter interacts with multiple external contracts which may have vulnerabilities.
Impermanent Loss: Some Badger strategies involve providing liquidity to AMMs, which can lead to impermanent loss.
Reward Distribution: Badger's reward distribution mechanisms may change over time, affecting yield calculations.
Risk Mitigation
The adapter implements strict access controls to prevent unauthorized access.
Emergency withdrawal functionality is available to recover funds in case of critical issues.
The adapter validates all inputs and handles edge cases to prevent unexpected behavior.
Integration with Badger's risk assessment framework to evaluate strategy risks.
Gas Optimization
The adapter minimizes gas usage by batching operations when possible.
For harvesting rewards, users can choose whether to stake BADGER rewards based on gas costs and expected returns.
The adapter avoids unnecessary approvals by only approving tokens when needed.
Future Improvements
Support for Badger's governance system for participating in protocol decisions
Integration with Badger's Digg rebasing token strategies
Support for Badger's cross-chain vaults as they become available
Automated reinvestment of harvested rewards for maximum yield
Integration with Badger's ibBTC (interest-bearing BTC) product