-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: cross chain reward distribution base deployment
- Loading branch information
Showing
167 changed files
with
45,959 additions
and
51,100 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,133 @@ | ||
//SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.10; | ||
|
||
import '@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol'; | ||
|
||
import './interfaces/AerodromeInterfaces.sol'; | ||
import './interfaces/SonneMerkleDistributorInterfaces.sol'; | ||
import './libraries/SafeToken.sol'; | ||
|
||
struct OpPoolState { | ||
bytes32 sMerkleRoot; | ||
bytes32 uMerkleRoot; | ||
uint256 sSupply; | ||
uint256 uSupply; | ||
uint256 blockNumber; | ||
uint256 withdrawUnlockTime; | ||
} | ||
|
||
contract BaseRewardManager is AccessControlUpgradeable { | ||
using SafeToken for address; | ||
|
||
bytes32 public constant MANAGER_ROLE = keccak256('MANAGER_ROLE'); | ||
|
||
ISonneMerkleDistributor public constant sSonneDistributor = | ||
ISonneMerkleDistributor(0x071B00ef6AD31Fb716955a1d70Ab26D47290120a); | ||
ISonneMerkleDistributor public constant uUsdcDistributor = | ||
ISonneMerkleDistributor(0x6cbDABf17c4634fA4F9A0C4A5e73Fd869F9a9be8); | ||
ISonneMerkleDistributor public constant sAeroDistributor = | ||
ISonneMerkleDistributor(0xA1918c958963DC7E710F5736A7fbCa7C32Bf501d); | ||
ISonneMerkleDistributor public constant uAeroDistributor = | ||
ISonneMerkleDistributor(0xcA31a8173AB19c3fa006D0Adc2883882F189797b); | ||
|
||
address public constant sonne = 0x22a2488fE295047Ba13BD8cCCdBC8361DBD8cf7c; | ||
address public constant usdc = 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913; | ||
address public constant usdbc = 0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA; | ||
address public constant aero = 0x940181a94A35A4569E4529A3CDfB74e38FD98631; | ||
|
||
IRouter public constant router = IRouter(0xcF77a3Ba9A5CA399B7c97c74d54e5b1Beb874E43); | ||
|
||
function initialize() public initializer { | ||
__AccessControl_init(); | ||
|
||
_grantRole(DEFAULT_ADMIN_ROLE, _msgSender()); | ||
_grantRole(MANAGER_ROLE, _msgSender()); | ||
} | ||
|
||
function addRewards( | ||
uint256 usdcAmount, | ||
uint256 aeroAmount, | ||
OpPoolState calldata state | ||
) public onlyRole(MANAGER_ROLE) { | ||
uint256 totalSupply = state.sSupply + state.uSupply; | ||
|
||
// add velo | ||
if (aeroAmount > 0) { | ||
pullTokenInternal(aero, aeroAmount); | ||
uint256 amount = aero.balanceOf(address(this)); | ||
|
||
uint256 uAmount = (amount * state.uSupply) / totalSupply; | ||
uint256 sAmount = amount - uAmount; | ||
|
||
// add for uSonne | ||
aero.safeApprove(address(uAeroDistributor), uAmount); | ||
uAeroDistributor.addReward( | ||
uAmount, | ||
state.uMerkleRoot, | ||
state.blockNumber, | ||
state.withdrawUnlockTime, | ||
state.sSupply | ||
); | ||
|
||
// add for sSonne | ||
aero.safeApprove(address(sAeroDistributor), sAmount); | ||
sAeroDistributor.addReward( | ||
sAmount, | ||
state.sMerkleRoot, | ||
state.blockNumber, | ||
state.withdrawUnlockTime, | ||
state.sSupply | ||
); | ||
} | ||
|
||
// add usdc | ||
if (usdcAmount > 0) { | ||
pullTokenInternal(usdc, usdcAmount); | ||
uint256 amount = usdc.balanceOf(address(this)); | ||
|
||
uint256 uUSDCAmount = (amount * state.uSupply) / totalSupply; | ||
uint256 sUSDCAmount = amount - uUSDCAmount; | ||
|
||
// add usdc for uSonne | ||
usdc.safeApprove(address(uUsdcDistributor), uUSDCAmount); | ||
uUsdcDistributor.addReward( | ||
uUSDCAmount, | ||
state.uMerkleRoot, | ||
state.blockNumber, | ||
state.withdrawUnlockTime, | ||
state.uSupply | ||
); | ||
|
||
// swap usdc to sonne | ||
swapUSDCtoSonneInternal(sUSDCAmount); | ||
|
||
// add sonne for sSonne | ||
uint256 sonneAmount = sonne.balanceOf(address(this)); | ||
sonne.safeApprove(address(sSonneDistributor), sonneAmount); | ||
sSonneDistributor.addReward( | ||
sonneAmount, | ||
state.sMerkleRoot, | ||
state.blockNumber, | ||
state.withdrawUnlockTime, | ||
state.sSupply | ||
); | ||
} | ||
} | ||
|
||
function pullTokenInternal(address token, uint256 amount) internal { | ||
if (amount == type(uint256).max) { | ||
amount = token.balanceOf(msg.sender); | ||
} | ||
|
||
token.safeTransferFrom(msg.sender, address(this), amount); | ||
} | ||
|
||
function swapUSDCtoSonneInternal(uint256 usdcAmount) internal { | ||
IRouter.Route[] memory path = new IRouter.Route[](2); | ||
path[0] = IRouter.Route({from: usdc, to: usdbc, stable: true, factory: address(0)}); | ||
path[1] = IRouter.Route({from: usdbc, to: sonne, stable: false, factory: address(0)}); | ||
|
||
usdc.safeApprove(address(router), usdcAmount); | ||
router.swapExactTokensForTokens(usdcAmount, 0, path, address(this), block.timestamp); | ||
} | ||
} |
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,129 @@ | ||
//SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.10; | ||
|
||
import '@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol'; | ||
|
||
import './interfaces/LendingInterfaces.sol'; | ||
import './interfaces/VelodromeInterfaces.sol'; | ||
import './libraries/SafeToken.sol'; | ||
|
||
import './RewardManager.sol'; | ||
|
||
contract ReserveManager is AccessControlUpgradeable { | ||
using SafeToken for address; | ||
|
||
bytes32 public constant DISTRIBUTOR_ROLE = keccak256('DISTRIBUTOR_ROLE'); | ||
|
||
/* Tokens */ | ||
address public constant usdc = 0x7F5c764cBc14f9669B88837ca1490cCa17c31607; | ||
address public constant velo = 0x9560e827aF36c94D2Ac33a39bCE1Fe78631088Db; | ||
|
||
/* OpenOcean */ | ||
address public constant OORouter = 0x6352a56caadC4F1E25CD6c75970Fa768A3304e64; | ||
|
||
/* Velodrome */ | ||
address public constant voter = 0x09236cfF45047DBee6B921e00704bed6D6B8Cf7e; | ||
|
||
/* Distribution */ | ||
address public constant rewardManager = 0xC5ba10B609E8500c04884e1bcfc935B2c22654cd; | ||
address public constant sonneTimelock = 0x5b22BD2fC485afe2DEAf1Ac9e2fAd316dDE163B0; | ||
|
||
function initialize() public initializer { | ||
__AccessControl_init(); | ||
|
||
_grantRole(DEFAULT_ADMIN_ROLE, _msgSender()); | ||
} | ||
|
||
/* Guarded Distribution Functions */ | ||
function distributeReserves( | ||
IMarket usdcMarket, | ||
uint56 usdcAmount, | ||
IMarket[] calldata markets, | ||
uint256[] calldata amounts, | ||
bytes[] calldata swapQuoteData | ||
) external onlyRole(DISTRIBUTOR_ROLE) { | ||
require( | ||
markets.length == amounts.length && markets.length == swapQuoteData.length, | ||
'ReserveManager: INVALID_INPUT' | ||
); | ||
|
||
reduceReserveInternal(usdcMarket, usdcAmount); | ||
|
||
for (uint256 i = 0; i < markets.length; i++) { | ||
reduceReserveInternal(markets[i], amounts[i]); | ||
address underlying = markets[i].underlying(); | ||
swapToBaseInternal(underlying, amounts[i], swapQuoteData[i]); | ||
} | ||
|
||
uint256 distAmount = usdc.myBalance(); | ||
|
||
usdc.safeApprove(rewardManager, distAmount); | ||
RewardManager(rewardManager).addRewards(distAmount, 0, 0); | ||
} | ||
|
||
function distributeVelo(address pair) external onlyRole(DISTRIBUTOR_ROLE) { | ||
claimVeloInternal(pair); | ||
|
||
uint256 distAmount = velo.myBalance(); | ||
|
||
velo.safeApprove(rewardManager, distAmount); | ||
RewardManager(rewardManager).addRewards(0, distAmount, 0); | ||
} | ||
|
||
/* Guarded Velodrome Management Function */ | ||
function _stakeLP(address pair, uint256 amount) public onlyRole(DEFAULT_ADMIN_ROLE) { | ||
if (amount == type(uint256).max) { | ||
amount = pair.balanceOf(msg.sender); | ||
} | ||
|
||
pair.safeTransferFrom(msg.sender, address(this), amount); | ||
|
||
stakeLPInternal(pair); | ||
} | ||
|
||
function _unstakeLP(address pair, address to) public onlyRole(DEFAULT_ADMIN_ROLE) { | ||
unstakeLPInternal(pair); | ||
|
||
uint256 amount = pair.myBalance(); | ||
pair.safeTransfer(to, amount); | ||
} | ||
|
||
/* Internal Market Management Functions */ | ||
function reduceReserveInternal(IMarket market, uint256 amount) internal { | ||
market.accrueInterest(); | ||
|
||
require(market.getCash() >= amount, 'ReserveManager: NOT_ENOUGH_CASH'); | ||
require(market.totalReserves() >= amount, 'ReserveManager: NOT_ENOUGH_RESERVE'); | ||
|
||
ISonneTimelock(sonneTimelock)._reduceReserves(address(market), amount, address(this)); | ||
} | ||
|
||
function swapToBaseInternal(address underlying, uint256 amount, bytes memory swapQuoteDatum) internal { | ||
underlying.safeApprove(OORouter, amount); | ||
|
||
(bool success, bytes memory result) = OORouter.call{value: 0}(swapQuoteDatum); | ||
require(success, 'ReserveManager: OO_API_SWAP_FAILED'); | ||
} | ||
|
||
/* Internal Velodrome Management Functions */ | ||
function stakeLPInternal(address pair) internal { | ||
address gauge = IVoter(voter).gauges(pair); | ||
|
||
uint256 amountPair = pair.myBalance(); | ||
pair.safeApprove(gauge, amountPair); | ||
IGauge(gauge).deposit(amountPair, 0); | ||
} | ||
|
||
function unstakeLPInternal(address pair) internal { | ||
address gauge = IVoter(voter).gauges(pair); | ||
IGauge(gauge).withdrawAll(); | ||
} | ||
|
||
function claimVeloInternal(address pair) internal { | ||
address[] memory tokens = new address[](1); | ||
tokens[0] = velo; | ||
|
||
address gauge = IVoter(voter).gauges(pair); | ||
IGauge(gauge).getReward(address(this), tokens); | ||
} | ||
} |
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,104 @@ | ||
//SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.10; | ||
|
||
import '@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol'; | ||
|
||
import './interfaces/VelodromeInterfaces.sol'; | ||
import './libraries/SafeToken.sol'; | ||
|
||
import './StakedDistributor.sol'; | ||
|
||
contract RewardManager is OwnableUpgradeable { | ||
using SafeToken for address; | ||
|
||
StakedDistributor public constant sSonne = StakedDistributor(0xDC05d85069Dc4ABa65954008ff99f2D73FF12618); | ||
StakedDistributor public constant uSonne = StakedDistributor(0x41279e29586EB20f9a4f65e031Af09fced171166); | ||
|
||
address public constant sonne = 0x1DB2466d9F5e10D7090E7152B68d62703a2245F0; | ||
address public constant usdc = 0x7F5c764cBc14f9669B88837ca1490cCa17c31607; | ||
address public constant velo = 0x9560e827aF36c94D2Ac33a39bCE1Fe78631088Db; | ||
address public constant op = 0x4200000000000000000000000000000000000042; | ||
|
||
IRouter public constant router = IRouter(0x9c12939390052919aF3155f41Bf4160Fd3666A6f); | ||
|
||
function initialize() public initializer { | ||
__Ownable_init(); | ||
} | ||
|
||
function addRewards(uint256 usdcAmount, uint256 veloAmount, uint256 opAmount) public { | ||
// calculate supplies | ||
uint256 sSupply = sSonne.totalSupply(); | ||
uint256 uSupply = uSonne.totalSupply(); | ||
uint256 totalSupply = sSupply + uSupply; | ||
|
||
// add velo | ||
if (veloAmount > 0) { | ||
pullTokenInternal(velo, veloAmount); | ||
addTokenRewardInternal(velo, uSupply, totalSupply); | ||
} | ||
|
||
// add op | ||
if (opAmount > 0) { | ||
pullTokenInternal(op, opAmount); | ||
addTokenRewardInternal(op, uSupply, totalSupply); | ||
} | ||
|
||
// add usdc | ||
if (usdcAmount > 0) { | ||
pullTokenInternal(usdc, usdcAmount); | ||
addUSDCRewardInternal(uSupply, totalSupply); | ||
} | ||
} | ||
|
||
function pullTokenInternal(address token, uint256 amount) internal { | ||
if (amount == type(uint256).max) { | ||
amount = token.balanceOf(msg.sender); | ||
} | ||
|
||
token.safeTransferFrom(msg.sender, address(this), amount); | ||
} | ||
|
||
function addTokenRewardInternal(address token, uint256 uSupply, uint256 totalSupply) internal { | ||
uint256 amount = token.balanceOf(address(this)); | ||
if (amount == 0) return; | ||
|
||
uint256 uAmount = (amount * uSupply) / totalSupply; | ||
uint256 sAmount = amount - uAmount; | ||
|
||
// add to uSonne | ||
token.safeApprove(address(uSonne), uAmount); | ||
uSonne.addReward(token, uAmount); | ||
|
||
// add to sSonne | ||
token.safeApprove(address(sSonne), sAmount); | ||
sSonne.addReward(token, sAmount); | ||
} | ||
|
||
function addUSDCRewardInternal(uint256 uSupply, uint256 totalSupply) internal { | ||
uint256 amount = usdc.balanceOf(address(this)); | ||
if (amount == 0) return; | ||
|
||
uint256 uUSDCAmount = (amount * uSupply) / totalSupply; | ||
uint256 sUSDCAmount = amount - uUSDCAmount; | ||
|
||
// add to uSonne | ||
usdc.safeApprove(address(uSonne), uUSDCAmount); | ||
uSonne.addReward(usdc, uUSDCAmount); | ||
|
||
// swap usdc to sonne | ||
swapUSDCtoSonneInternal(sUSDCAmount); | ||
|
||
// add to sMare | ||
uint256 sonneAmount = sonne.balanceOf(address(this)); | ||
sonne.safeApprove(address(sSonne), sonneAmount); | ||
sSonne.addReward(sonne, sonneAmount); | ||
} | ||
|
||
function swapUSDCtoSonneInternal(uint256 usdcAmount) internal { | ||
IRouter.route[] memory path = new IRouter.route[](1); | ||
path[0] = IRouter.route({from: usdc, to: sonne, stable: false}); | ||
|
||
usdc.safeApprove(address(router), usdcAmount); | ||
router.swapExactTokensForTokens(usdcAmount, 0, path, address(this), block.timestamp); | ||
} | ||
} |
Oops, something went wrong.