Skip to content

Commit

Permalink
feat: add base reserve manager
Browse files Browse the repository at this point in the history
  • Loading branch information
gathonwar committed Apr 14, 2024
1 parent e0d62c5 commit a444bc2
Show file tree
Hide file tree
Showing 12 changed files with 617 additions and 45 deletions.
131 changes: 131 additions & 0 deletions contracts/BaseReserveManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

import '@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol';

import './interfaces/LendingInterfaces.sol';
import './interfaces/AerodromeInterfaces.sol';
import './libraries/SafeToken.sol';

import './BaseRewardManager.sol';

contract BaseReserveManager is AccessControlUpgradeable {
using SafeToken for address;

bytes32 public constant DISTRIBUTOR_ROLE = keccak256('DISTRIBUTOR_ROLE');

/* Tokens */

address public constant usdc = 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913;
address public constant aero = 0x940181a94A35A4569E4529A3CDfB74e38FD98631;

/* OpenOcean */
address public constant OORouter = 0x6352a56caadC4F1E25CD6c75970Fa768A3304e64;

/* Aerodrome */
address public constant voter = 0x16613524e02ad97eDfeF371bC883F2F5d6C480A5;

/* 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,
OpPoolState calldata state
) 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);
BaseRewardManager(rewardManager).addRewards(distAmount, 0, state);
}

function distributeAero(address pair, OpPoolState calldata state) external onlyRole(DISTRIBUTOR_ROLE) {
claimAeroInternal(pair);

uint256 distAmount = aero.myBalance();

aero.safeApprove(rewardManager, distAmount);
BaseRewardManager(rewardManager).addRewards(0, distAmount, state);
}

/* Guarded Aerodrome 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 Aerodrome 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 claimAeroInternal(address pair) internal {
address[] memory tokens = new address[](1);
tokens[0] = aero;

address gauge = IVoter(voter).gauges(pair);
IGauge(gauge).getReward(address(this), tokens);
}
}
12 changes: 9 additions & 3 deletions contracts/BaseRewardManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,13 @@ contract BaseRewardManager is AccessControlUpgradeable {
}
}

function setDelegator(address recipient, address delegator) public onlyRole(MANAGER_ROLE) {
sSonneDistributor.setDelegator(recipient, delegator);
uUsdcDistributor.setDelegator(recipient, delegator);
sAeroDistributor.setDelegator(recipient, delegator);
uAeroDistributor.setDelegator(recipient, delegator);
}

function pullTokenInternal(address token, uint256 amount) internal {
if (amount == type(uint256).max) {
amount = token.balanceOf(msg.sender);
Expand All @@ -123,9 +130,8 @@ contract BaseRewardManager is AccessControlUpgradeable {
}

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)});
IRouter.Route[] memory path = new IRouter.Route[](1);
path[0] = IRouter.Route({from: usdc, to: sonne, stable: false, factory: address(0)});

usdc.safeApprove(address(router), usdcAmount);
router.swapExactTokensForTokens(usdcAmount, 0, path, address(this), block.timestamp);
Expand Down
2 changes: 2 additions & 0 deletions contracts/interfaces/SonneMerkleDistributorInterfaces.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ interface ISonneMerkleDistributor {
uint256 totalStakedBalance
) external;

function setDelegator(address _recipient, address _delegator) external;

// For testing

function grantRole(bytes32 role, address account) external;
Expand Down
218 changes: 218 additions & 0 deletions crosschain-rewards/.openzeppelin/unknown-8453.json
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,224 @@
}
}
}
},
"d19edc677caed8fa880eb82ceaf36c36b4958d76f58e89709ad8c585542278ce": {
"address": "0x21fE76ca291207922132eA66E49fFB5bD10a43BD",
"txHash": "0xcda79a2c5772ea25e6c14c4fed0d0d5d49bebb9c635caedea65f0577ec161c75",
"layout": {
"solcVersion": "0.8.19",
"storage": [
{
"label": "_initialized",
"offset": 0,
"slot": "0",
"type": "t_uint8",
"contract": "Initializable",
"src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:63",
"retypedFrom": "bool"
},
{
"label": "_initializing",
"offset": 1,
"slot": "0",
"type": "t_bool",
"contract": "Initializable",
"src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:68"
},
{
"label": "_status",
"offset": 0,
"slot": "1",
"type": "t_uint256",
"contract": "ReentrancyGuardUpgradeable",
"src": "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:38"
},
{
"label": "__gap",
"offset": 0,
"slot": "2",
"type": "t_array(t_uint256)49_storage",
"contract": "ReentrancyGuardUpgradeable",
"src": "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:88"
},
{
"label": "__gap",
"offset": 0,
"slot": "51",
"type": "t_array(t_uint256)50_storage",
"contract": "ContextUpgradeable",
"src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:36"
},
{
"label": "__gap",
"offset": 0,
"slot": "101",
"type": "t_array(t_uint256)50_storage",
"contract": "ERC165Upgradeable",
"src": "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol:41"
},
{
"label": "_roles",
"offset": 0,
"slot": "151",
"type": "t_mapping(t_bytes32,t_struct(RoleData)1331_storage)",
"contract": "AccessControlUpgradeable",
"src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:62"
},
{
"label": "__gap",
"offset": 0,
"slot": "152",
"type": "t_array(t_uint256)49_storage",
"contract": "AccessControlUpgradeable",
"src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:260"
},
{
"label": "rewardToken",
"offset": 0,
"slot": "201",
"type": "t_contract(IERC20)3664",
"contract": "SonneMerkleDistributor",
"src": "contracts/SonneMerkleDistributor.sol:32"
},
{
"label": "rewards",
"offset": 0,
"slot": "202",
"type": "t_array(t_uint256)dyn_storage",
"contract": "SonneMerkleDistributor",
"src": "contracts/SonneMerkleDistributor.sol:33"
},
{
"label": "Rewards",
"offset": 0,
"slot": "203",
"type": "t_mapping(t_uint256,t_struct(Reward)4453_storage)",
"contract": "SonneMerkleDistributor",
"src": "contracts/SonneMerkleDistributor.sol:35"
},
{
"label": "delegatorAddresses",
"offset": 0,
"slot": "204",
"type": "t_mapping(t_address,t_address)",
"contract": "SonneMerkleDistributor",
"src": "contracts/SonneMerkleDistributor.sol:36"
}
],
"types": {
"t_address": {
"label": "address",
"numberOfBytes": "20"
},
"t_array(t_uint256)49_storage": {
"label": "uint256[49]",
"numberOfBytes": "1568"
},
"t_array(t_uint256)50_storage": {
"label": "uint256[50]",
"numberOfBytes": "1600"
},
"t_array(t_uint256)dyn_storage": {
"label": "uint256[]",
"numberOfBytes": "32"
},
"t_bool": {
"label": "bool",
"numberOfBytes": "1"
},
"t_bytes32": {
"label": "bytes32",
"numberOfBytes": "32"
},
"t_contract(IERC20)3664": {
"label": "contract IERC20",
"numberOfBytes": "20"
},
"t_mapping(t_address,t_address)": {
"label": "mapping(address => address)",
"numberOfBytes": "32"
},
"t_mapping(t_address,t_bool)": {
"label": "mapping(address => bool)",
"numberOfBytes": "32"
},
"t_mapping(t_bytes32,t_bool)": {
"label": "mapping(bytes32 => bool)",
"numberOfBytes": "32"
},
"t_mapping(t_bytes32,t_struct(RoleData)1331_storage)": {
"label": "mapping(bytes32 => struct AccessControlUpgradeable.RoleData)",
"numberOfBytes": "32"
},
"t_mapping(t_uint256,t_struct(Reward)4453_storage)": {
"label": "mapping(uint256 => struct SonneMerkleDistributor.Reward)",
"numberOfBytes": "32"
},
"t_struct(Reward)4453_storage": {
"label": "struct SonneMerkleDistributor.Reward",
"members": [
{
"label": "balance",
"type": "t_uint256",
"offset": 0,
"slot": "0"
},
{
"label": "merkleRoot",
"type": "t_bytes32",
"offset": 0,
"slot": "1"
},
{
"label": "withdrawUnlockTime",
"type": "t_uint256",
"offset": 0,
"slot": "2"
},
{
"label": "ratio",
"type": "t_uint256",
"offset": 0,
"slot": "3"
},
{
"label": "leafClaimed",
"type": "t_mapping(t_bytes32,t_bool)",
"offset": 0,
"slot": "4"
}
],
"numberOfBytes": "160"
},
"t_struct(RoleData)1331_storage": {
"label": "struct AccessControlUpgradeable.RoleData",
"members": [
{
"label": "members",
"type": "t_mapping(t_address,t_bool)",
"offset": 0,
"slot": "0"
},
{
"label": "adminRole",
"type": "t_bytes32",
"offset": 0,
"slot": "1"
}
],
"numberOfBytes": "64"
},
"t_uint256": {
"label": "uint256",
"numberOfBytes": "32"
},
"t_uint8": {
"label": "uint8",
"numberOfBytes": "1"
}
}
}
}
}
}
Loading

0 comments on commit a444bc2

Please sign in to comment.