-
Notifications
You must be signed in to change notification settings - Fork 54
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* add WombatStrategy * update WombatStrategy * add reservedWom property to WombatVoter
- Loading branch information
1 parent
9a9eaaf
commit 273a3e4
Showing
9 changed files
with
421 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.13; | ||
|
||
import "./../../../interfaces/IERC20.sol"; | ||
import "./interfaces/IWombatVoter.sol"; | ||
import "./interfaces/IBoostedMasterWombat.sol"; | ||
import "./interfaces/IWombatWaddle.sol"; | ||
|
||
interface IWombatStrategy { | ||
function pid() external view returns (uint256); | ||
function masterWombat() external view returns (address); | ||
} | ||
|
||
library SafeProxy { | ||
function safeExecute(IWombatVoter voter, address target, uint256 value, bytes memory data) | ||
internal | ||
returns (bytes memory) | ||
{ | ||
(bool success, bytes memory returnValue) = voter.execute(target, value, data); | ||
if (!success) revert("WombatProxy::safeExecute failed"); | ||
return returnValue; | ||
} | ||
} | ||
|
||
contract WombatProxy { | ||
using SafeProxy for IWombatVoter; | ||
|
||
struct Reward { | ||
address reward; | ||
uint256 amount; | ||
} | ||
|
||
uint256 constant LOCK_DAYS = 1461; | ||
uint256 internal constant BIPS_DIVISOR = 10000; | ||
|
||
address public devAddr; | ||
IWombatVoter public immutable voter; | ||
IWombatWaddle public immutable wombatWaddle; | ||
address immutable WOM; | ||
|
||
// staking contract => pid => strategy | ||
mapping(address => mapping(uint256 => address)) public approvedStrategies; | ||
uint256 boostFeeBips; | ||
uint256 minBoostAmount; | ||
|
||
modifier onlyDev() { | ||
require(msg.sender == devAddr, "WombatProxy::onlyDev"); | ||
_; | ||
} | ||
|
||
modifier onlyStrategy(address _stakingContract, uint256 _pid) { | ||
require(approvedStrategies[_stakingContract][_pid] == msg.sender, "WombatProxy::onlyStrategy"); | ||
_; | ||
} | ||
|
||
constructor( | ||
address _voter, | ||
address _devAddr, | ||
address _wombatWaddle, | ||
uint256 _boostFeeBips, | ||
uint256 _minBoostAmount | ||
) { | ||
require(_devAddr > address(0), "WombatProxy::Invalid dev address provided"); | ||
devAddr = _devAddr; | ||
voter = IWombatVoter(_voter); | ||
wombatWaddle = IWombatWaddle(_wombatWaddle); | ||
WOM = wombatWaddle.wom(); | ||
boostFeeBips = _boostFeeBips; | ||
minBoostAmount = _minBoostAmount; | ||
} | ||
|
||
/** | ||
* @notice Update devAddr | ||
* @param newValue address | ||
*/ | ||
function updateDevAddr(address newValue) external onlyDev { | ||
devAddr = newValue; | ||
} | ||
|
||
/** | ||
* @notice Add an approved strategy | ||
* @dev Very sensitive, restricted to devAddr | ||
* @dev Can only be set once per PID and staking contract (reported by the strategy) | ||
* @param _strategy address | ||
*/ | ||
function approveStrategy(address _strategy) public onlyDev { | ||
uint256 pid = IWombatStrategy(_strategy).pid(); | ||
address stakingContract = IWombatStrategy(_strategy).masterWombat(); | ||
require(approvedStrategies[stakingContract][pid] == address(0), "WombatProxy::Strategy for PID already added"); | ||
approvedStrategies[stakingContract][pid] = _strategy; | ||
} | ||
|
||
/** | ||
* @notice Transfer from _amount WOM from sender and lock for veWOM | ||
* @dev This contract needs approval to transfer _amount WOM before calling this method | ||
* @param _amount WOM amount | ||
*/ | ||
function boost(uint256 _amount) public onlyDev { | ||
IERC20(WOM).transferFrom(msg.sender, address(voter), _amount); | ||
_boost(_amount); | ||
} | ||
|
||
/** | ||
* @notice Update additional/optional boost fee settins | ||
* @param _boostFeeBips Boost fee bips, check BIPS_DIVISOR | ||
* @param _minBoostAmount Minimum amount of WOM to create a new lock position | ||
*/ | ||
function updateBoostFee(uint256 _boostFeeBips, uint256 _minBoostAmount) external onlyDev { | ||
require(_boostFeeBips < BIPS_DIVISOR, "WombatProxy::Invalid boost fee"); | ||
boostFeeBips = _boostFeeBips; | ||
minBoostAmount = _minBoostAmount; | ||
} | ||
|
||
function depositToStakingContract(address _masterWombat, uint256 _pid, address _token, uint256 _amount) | ||
external | ||
onlyStrategy(_masterWombat, _pid) | ||
{ | ||
voter.safeExecute(_token, 0, abi.encodeWithSelector(IERC20.approve.selector, _masterWombat, _amount)); | ||
voter.safeExecute( | ||
_masterWombat, 0, abi.encodeWithSelector(IBoostedMasterWombat.deposit.selector, _pid, _amount) | ||
); | ||
} | ||
|
||
function withdrawFromStakingContract(address _masterWombat, uint256 _pid, address _token, uint256 _amount) | ||
external | ||
onlyStrategy(_masterWombat, _pid) | ||
{ | ||
getRewards(_masterWombat, _pid); | ||
voter.safeExecute( | ||
_masterWombat, 0, abi.encodeWithSelector(IBoostedMasterWombat.withdraw.selector, _pid, _amount) | ||
); | ||
voter.safeExecute(_token, 0, abi.encodeWithSelector(IERC20.transfer.selector, msg.sender, _amount)); | ||
} | ||
|
||
function pendingRewards(address _masterWombat, uint256 _pid) public view returns (Reward[] memory) { | ||
(, address[] memory bonusTokenAddresses,, uint256[] memory pendingBonusRewards) = | ||
IBoostedMasterWombat(_masterWombat).pendingTokens(_pid, address(voter)); | ||
Reward[] memory rewards = new Reward[](bonusTokenAddresses.length); | ||
for (uint256 i = 0; i < bonusTokenAddresses.length; i++) { | ||
uint256 boostFee = bonusTokenAddresses[i] == WOM ? _calculateBoostFee(pendingBonusRewards[i]) : 0; | ||
rewards[i] = Reward({reward: bonusTokenAddresses[i], amount: pendingBonusRewards[i] - boostFee}); | ||
} | ||
return rewards; | ||
} | ||
|
||
function getRewards(address _masterWombat, uint256 _pid) public onlyStrategy(_masterWombat, _pid) { | ||
Reward[] memory rewards = pendingRewards(_masterWombat, _pid); | ||
voter.safeExecute(_masterWombat, 0, abi.encodeWithSelector(IBoostedMasterWombat.deposit.selector, _pid, 0)); | ||
for (uint256 i; i < rewards.length; i++) { | ||
uint256 reward = IERC20(rewards[i].reward).balanceOf(address(voter)); | ||
|
||
if (rewards[i].reward == WOM) { | ||
uint256 reservedWom = voter.reservedWom(); | ||
reservedWom += _calculateBoostFee(reward - reservedWom); | ||
reward -= reservedWom; | ||
if (reservedWom > minBoostAmount) { | ||
_boost(reservedWom); | ||
reservedWom = 0; | ||
} | ||
voter.setReservedWom(reservedWom); | ||
} | ||
|
||
voter.safeExecute( | ||
rewards[i].reward, 0, abi.encodeWithSelector(IERC20.transfer.selector, msg.sender, reward) | ||
); | ||
} | ||
} | ||
|
||
function _calculateBoostFee(uint256 _womAmount) internal view returns (uint256) { | ||
return (_womAmount * boostFeeBips) / BIPS_DIVISOR; | ||
} | ||
|
||
function _boost(uint256 _amount) internal { | ||
voter.safeExecute( | ||
address(WOM), 0, abi.encodeWithSelector(IERC20.approve.selector, address(wombatWaddle), _amount) | ||
); | ||
voter.safeExecute( | ||
address(wombatWaddle), 0, abi.encodeWithSelector(IWombatWaddle.mint.selector, _amount, LOCK_DAYS) | ||
); | ||
} | ||
|
||
function totalDeposits(address _masterWombat, uint256 _pid) external view returns (uint256) { | ||
(uint128 liquidity,,,) = IBoostedMasterWombat(_masterWombat).userInfo(_pid, address(voter)); | ||
return liquidity; | ||
} | ||
|
||
function emergencyWithdraw(address _masterWombat, uint256 _pid, address _token) | ||
external | ||
onlyStrategy(_masterWombat, _pid) | ||
{ | ||
voter.safeExecute( | ||
_masterWombat, 0, abi.encodeWithSelector(IBoostedMasterWombat.emergencyWithdraw.selector, _pid) | ||
); | ||
voter.safeExecute( | ||
_token, | ||
0, | ||
abi.encodeWithSelector(IERC20.transfer.selector, msg.sender, IERC20(_token).balanceOf(address(voter))) | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.13; | ||
|
||
import "../../BaseStrategy.sol"; | ||
import "./interfaces/IBoostedMasterWombat.sol"; | ||
import "./interfaces/IWombatPool.sol"; | ||
import "./interfaces/IWombatAsset.sol"; | ||
import "./interfaces/IWombatProxy.sol"; | ||
|
||
contract WombatStrategy is BaseStrategy { | ||
using SafeERC20 for IERC20; | ||
|
||
IWombatPool public immutable wombatPool; | ||
address public immutable masterWombat; | ||
uint256 public immutable pid; | ||
address public immutable underlyingToken; | ||
|
||
IWombatProxy public proxy; | ||
|
||
constructor( | ||
address _wombatPool, | ||
address _proxy, | ||
BaseStrategySettings memory baseStrategySettings, | ||
StrategySettings memory _strategySettings | ||
) BaseStrategy(baseStrategySettings, _strategySettings) { | ||
wombatPool = IWombatPool(_wombatPool); | ||
masterWombat = wombatPool.masterWombat(); | ||
pid = IBoostedMasterWombat(masterWombat).getAssetPid(address(depositToken)); | ||
proxy = IWombatProxy(_proxy); | ||
underlyingToken = IWombatAsset(address(depositToken)).underlyingToken(); | ||
} | ||
|
||
function setProxy(address _proxy) external onlyDev { | ||
proxy = IWombatProxy(_proxy); | ||
} | ||
|
||
function _depositToStakingContract(uint256 _amount, uint256) internal override { | ||
depositToken.transfer(proxy.voter(), _amount); | ||
proxy.depositToStakingContract(masterWombat, pid, address(depositToken), _amount); | ||
} | ||
|
||
function _withdrawFromStakingContract(uint256 _amount) internal override returns (uint256 _withdrawAmount) { | ||
proxy.withdrawFromStakingContract(masterWombat, pid, address(depositToken), _amount); | ||
return _amount; | ||
} | ||
|
||
function _emergencyWithdraw() internal override { | ||
proxy.emergencyWithdraw(masterWombat, pid, address(depositToken)); | ||
} | ||
|
||
function _pendingRewards() internal view override returns (Reward[] memory) { | ||
return proxy.pendingRewards(masterWombat, pid); | ||
} | ||
|
||
function _getRewards() internal override { | ||
proxy.getRewards(masterWombat, pid); | ||
} | ||
|
||
function _convertRewardTokenToDepositToken(uint256 _fromAmount) internal override returns (uint256) { | ||
if (address(rewardToken) != underlyingToken) { | ||
FormattedOffer memory offer = simpleRouter.query(_fromAmount, address(rewardToken), underlyingToken); | ||
_fromAmount = _swap(offer); | ||
} | ||
if (_fromAmount > 0) { | ||
IERC20(underlyingToken).approve(address(wombatPool), _fromAmount); | ||
wombatPool.deposit(underlyingToken, _fromAmount, 0, address(proxy.voter()), block.timestamp, true); | ||
} | ||
return 0; | ||
} | ||
|
||
function totalDeposits() public view override returns (uint256) { | ||
return proxy.totalDeposits(masterWombat, pid); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.13; | ||
|
||
import "../../../lib/Ownable.sol"; | ||
|
||
import "./interfaces/IWombatVoter.sol"; | ||
|
||
contract WombatVoter is IWombatVoter, Ownable { | ||
address public proxy; | ||
uint256 public reservedWom; | ||
|
||
modifier onlyProxy() { | ||
require(msg.sender == proxy, "WombatVoter::onlyProxy"); | ||
_; | ||
} | ||
|
||
constructor(address _owner) { | ||
transferOwnership(_owner); | ||
} | ||
|
||
function setProxy(address _proxy) external override onlyOwner { | ||
proxy = _proxy; | ||
} | ||
|
||
function setReservedWom(uint256 _reservedWom) external override onlyProxy { | ||
reservedWom = _reservedWom; | ||
} | ||
|
||
function execute(address target, uint256 value, bytes calldata data) | ||
external | ||
override | ||
onlyProxy | ||
returns (bool, bytes memory) | ||
{ | ||
(bool success, bytes memory result) = target.call{value: value}(data); | ||
|
||
return (success, result); | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
contracts/strategies/crosschain/wombat/interfaces/IBoostedMasterWombat.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.13; | ||
|
||
interface IBoostedMasterWombat { | ||
function getAssetPid(address _asset) external view returns (uint256); | ||
|
||
function deposit(uint256 _pid, uint256 _amount) external; | ||
|
||
function withdraw(uint256 _pid, uint256 _amount) external; | ||
|
||
function emergencyWithdraw(uint256 _pid) external; | ||
|
||
function userInfo(uint256 _pid, address _user) external view returns (uint128, uint128, uint128, uint128); | ||
|
||
function pendingTokens(uint256 _pid, address _user) | ||
external | ||
view | ||
returns ( | ||
uint256 pendingRewards, | ||
address[] memory bonusTokenAddresses, | ||
string[] memory bonusTokenSymbols, | ||
uint256[] memory pendingBonusRewards | ||
); | ||
} |
7 changes: 7 additions & 0 deletions
7
contracts/strategies/crosschain/wombat/interfaces/IWombatAsset.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.13; | ||
|
||
interface IWombatAsset { | ||
function pool() external view returns (address); | ||
function underlyingToken() external view returns (address); | ||
} |
26 changes: 26 additions & 0 deletions
26
contracts/strategies/crosschain/wombat/interfaces/IWombatPool.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.13; | ||
|
||
interface IWombatPool { | ||
function addressOfAsset(address token) external view returns (address); | ||
|
||
function masterWombat() external view returns (address); | ||
|
||
function deposit( | ||
address token, | ||
uint256 amount, | ||
uint256 minimumLiquidity, | ||
address to, | ||
uint256 deadline, | ||
bool shouldStake | ||
) external returns (uint256 liquidity); | ||
|
||
function withdraw(address token, uint256 liquidity, uint256 minimumAmount, address to, uint256 deadline) | ||
external | ||
returns (uint256 amount); | ||
|
||
function quotePotentialWithdraw(address token, uint256 liquidity) | ||
external | ||
view | ||
returns (uint256 amount, uint256 fee); | ||
} |
15 changes: 15 additions & 0 deletions
15
contracts/strategies/crosschain/wombat/interfaces/IWombatProxy.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.13; | ||
|
||
import "./../../../BaseStrategy.sol"; | ||
|
||
interface IWombatProxy { | ||
function voter() external view returns (address); | ||
function depositToStakingContract(address _masterWombat, uint256 _pid, address _token, uint256 _amount) external; | ||
function withdrawFromStakingContract(address _masterWombat, uint256 _pid, address _token, uint256 _amount) | ||
external; | ||
function emergencyWithdraw(address _masterWombat, uint256 _pid, address _token) external; | ||
function pendingRewards(address _masterWombat, uint256 _pid) external view returns (BaseStrategy.Reward[] memory); | ||
function getRewards(address _masterWombat, uint256 _pid) external; | ||
function totalDeposits(address _masterWombat, uint256 _pid) external view returns (uint256); | ||
} |
Oops, something went wrong.