Skip to content

Commit

Permalink
Merge branch '4.0.0' into usds-plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
tbrent committed Nov 6, 2024
2 parents 5ec338d + a5235dd commit 17dd84b
Show file tree
Hide file tree
Showing 65 changed files with 2,664 additions and 317 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ jobs:
restore-keys: |
hardhat-network-fork-${{ runner.os }}-
hardhat-network-fork-
- run: yarn hardhat test ./test/plugins/individual-collateral/{cbeth,aave-v3,compoundv3,stargate,lido,meta-morpho}/*.test.ts
- run: yarn hardhat test ./test/plugins/individual-collateral/{cbeth,aave-v3,compoundv3,stargate,lido,meta-morpho,aerodrome}/*.test.ts
env:
NODE_OPTIONS: '--max-old-space-size=32768'
TS_NODE_SKIP_IGNORE: true
Expand Down
6 changes: 6 additions & 0 deletions common/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,9 @@ export interface ITokens {
// Sky
USDS?: string
sUSDS?: string

// Aerodrome
AERO?: string
}

export type ITokensKeys = Array<keyof ITokens>
Expand Down Expand Up @@ -149,6 +152,7 @@ export interface IPools {
crvTriCrypto?: string
crvMIM3Pool?: string
sdUSDCUSDCPlus?: string
aeroUSDCeUSD?: string
}

interface INetworkConfig {
Expand Down Expand Up @@ -526,6 +530,7 @@ export const networkConfig: { [key: string]: INetworkConfig } = {
STG: '0xE3B53AF74a4BF62Ae5511055290838050bf764Df',
eUSD: '0xCfA3Ef56d303AE4fAabA0592388F19d7C3399FB4',
meUSD: '0xbb819D845b573B5D7C538F5b85057160cfb5f313',
AERO: '0x940181a94A35A4569E4529A3CDfB74e38FD98631',
},
chainlinkFeeds: {
DAI: '0x591e79239a7d679378ec8c847e5038150364c78f', // 0.3%, 24hr
Expand All @@ -543,6 +548,7 @@ export const networkConfig: { [key: string]: INetworkConfig } = {
ETHUSD: '0x71041dddad3595F9CEd3DcCFBe3D1F4b0a16Bb70', // 0.15%, 20min
wstETHstETH: '0xB88BAc61a4Ca37C43a3725912B1f472c9A5bc061', // 0.5%, 24h
eUSD: '0x9b2C948dbA5952A1f5Ab6fA16101c1392b8da1ab', // 0.5%, 24h
AERO: '0x4EC5970fC728C5f65ba413992CD5fF6FD70fcfF0', // 0.5%, 24h
},
GNOSIS_EASY_AUCTION: '0xb1875Feaeea32Bbb02DE83D81772e07E37A40f02', // mock
COMET_REWARDS: '0x123964802e6ABabBE1Bc9547D72Ef1B69B00A6b1',
Expand Down
5 changes: 5 additions & 0 deletions contracts/interfaces/IRToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,11 @@ interface TestIRToken is IRToken {

function setRedemptionThrottleParams(ThrottleLib.Params calldata) external;

function setThrottleParams(
ThrottleLib.Params calldata issuanceParams,
ThrottleLib.Params calldata redemptionParams
) external;

function issuanceThrottleParams() external view returns (ThrottleLib.Params memory);

function redemptionThrottleParams() external view returns (ThrottleLib.Params memory);
Expand Down
59 changes: 55 additions & 4 deletions contracts/p0/RToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ contract RTokenP0 is ComponentP0, ERC20PermitUpgradeable, IRToken {
uint192 public constant MAX_THROTTLE_PCT_AMT = 1e18; // {qRTok}
uint192 public constant MIN_EXCHANGE_RATE = 1e9; // D18{BU/rTok}
uint192 public constant MAX_EXCHANGE_RATE = 1e27; // D18{BU/rTok}
uint192 public constant MIN_THROTTLE_DELTA = 25e16; // {1} 25%

/// Weakly immutable: expected to be an IPFS link but could be the mandate itself
string public mandate;
Expand All @@ -54,8 +55,7 @@ contract RTokenP0 is ComponentP0, ERC20PermitUpgradeable, IRToken {
__ERC20Permit_init(name_);

mandate = mandate_;
setIssuanceThrottleParams(issuanceThrottleParams_);
setRedemptionThrottleParams(redemptionThrottleParams_);
setThrottleParams(issuanceThrottleParams_, redemptionThrottleParams_);

issuanceThrottle.lastTimestamp = uint48(block.timestamp);
redemptionThrottle.lastTimestamp = uint48(block.timestamp);
Expand Down Expand Up @@ -340,25 +340,76 @@ contract RTokenP0 is ComponentP0, ERC20PermitUpgradeable, IRToken {

/// @custom:governance
function setIssuanceThrottleParams(ThrottleLib.Params calldata params) public governance {
_setIssuanceThrottleParams(params);
require(
isRedemptionThrottleGreaterByDelta(params, redemptionThrottle.params),
"redemption throttle too low"
);
}

/// @custom:governance
function setRedemptionThrottleParams(ThrottleLib.Params calldata params) public governance {
_setRedemptionThrottleParams(params);
require(
isRedemptionThrottleGreaterByDelta(issuanceThrottle.params, params),
"redemption throttle too low"
);
}

function setThrottleParams(
ThrottleLib.Params calldata issuanceParams,
ThrottleLib.Params calldata redemptionParams
) public governance {
_setIssuanceThrottleParams(issuanceParams);
_setRedemptionThrottleParams(redemptionParams);
require(
isRedemptionThrottleGreaterByDelta(issuanceParams, redemptionParams),
"redemption throttle too low"
);
}

// === Private ===

function _setIssuanceThrottleParams(ThrottleLib.Params calldata params) private {
require(params.amtRate >= MIN_THROTTLE_RATE_AMT, "issuance amtRate too small");
require(params.amtRate <= MAX_THROTTLE_RATE_AMT, "issuance amtRate too big");
require(params.pctRate <= MAX_THROTTLE_PCT_AMT, "issuance pctRate too big");
issuanceThrottle.useAvailable(totalSupply(), 0);

emit IssuanceThrottleSet(issuanceThrottle.params, params);
issuanceThrottle.params = params;
}

/// @custom:governance
function setRedemptionThrottleParams(ThrottleLib.Params calldata params) public governance {
function _setRedemptionThrottleParams(ThrottleLib.Params calldata params) private {
require(params.amtRate >= MIN_THROTTLE_RATE_AMT, "redemption amtRate too small");
require(params.amtRate <= MAX_THROTTLE_RATE_AMT, "redemption amtRate too big");
require(params.pctRate <= MAX_THROTTLE_PCT_AMT, "redemption pctRate too big");
redemptionThrottle.useAvailable(totalSupply(), 0);

emit RedemptionThrottleSet(redemptionThrottle.params, params);
redemptionThrottle.params = params;
}

// === Private ===
/// @notice Checks if the redemption throttle is greater than the issuance throttle by the
/// required delta
/// @dev Compares both amtRate and pctRate individually to ensure each meets the minimum
/// delta requirement
/// @param issuance The issuance throttle parameters to compare against
/// @param redemption The redemption throttle parameters to check
/// @return bool True if redemption throttle is greater by at least MIN_THROTTLE_DELTA,
/// false otherwise
function isRedemptionThrottleGreaterByDelta(
ThrottleLib.Params memory issuance,
ThrottleLib.Params memory redemption
) private pure returns (bool) {
uint256 requiredAmtRate = issuance.amtRate +
((issuance.amtRate * MIN_THROTTLE_DELTA) / FIX_ONE);
uint256 requiredPctRate = issuance.pctRate +
((issuance.pctRate * MIN_THROTTLE_DELTA) / FIX_ONE);

return redemption.amtRate >= requiredAmtRate && redemption.pctRate >= requiredPctRate;
}

/// Mint an amount of RToken equivalent to amtBaskets and scale basketsNeeded up
/// @param recipient The address to receive the RTokens
Expand Down
60 changes: 55 additions & 5 deletions contracts/p1/RToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@ contract RTokenP1 is ComponentP1, ERC20PermitUpgradeable, IRToken {
uint192 public constant MAX_THROTTLE_PCT_AMT = 1e18; // {qRTok}
uint192 public constant MIN_EXCHANGE_RATE = 1e9; // D18{BU/rTok}
uint192 public constant MAX_EXCHANGE_RATE = 1e27; // D18{BU/rTok}
uint192 public constant MIN_THROTTLE_DELTA = 25e16; // {1} 25%

/// The mandate describes what goals its governors should try to achieve. By succinctly
/// explaining the RTokens purpose and what the RToken is intended to do, it provides common
/// explaining the RToken's purpose and what the RToken is intended to do, it provides common
/// ground for the governors to decide upon priorities and how to weigh tradeoffs.
///
/// Example Mandates:
Expand Down Expand Up @@ -79,8 +80,7 @@ contract RTokenP1 is ComponentP1, ERC20PermitUpgradeable, IRToken {
furnace = main_.furnace();

mandate = mandate_;
setIssuanceThrottleParams(issuanceThrottleParams_);
setRedemptionThrottleParams(redemptionThrottleParams_);
setThrottleParams(issuanceThrottleParams_, redemptionThrottleParams_);

issuanceThrottle.lastTimestamp = uint48(block.timestamp);
redemptionThrottle.lastTimestamp = uint48(block.timestamp);
Expand Down Expand Up @@ -461,6 +461,38 @@ contract RTokenP1 is ComponentP1, ERC20PermitUpgradeable, IRToken {

/// @custom:governance
function setIssuanceThrottleParams(ThrottleLib.Params calldata params) public governance {
_setIssuanceThrottleParams(params);
require(
isRedemptionThrottleGreaterByDelta(params, redemptionThrottle.params),
"redemption throttle too low"
);
}

/// @custom:governance
function setRedemptionThrottleParams(ThrottleLib.Params calldata params) public governance {
_setRedemptionThrottleParams(params);
require(
isRedemptionThrottleGreaterByDelta(issuanceThrottle.params, params),
"redemption throttle too low"
);
}

/// @custom:governance
function setThrottleParams(
ThrottleLib.Params calldata issuanceParams,
ThrottleLib.Params calldata redemptionParams
) public governance {
_setIssuanceThrottleParams(issuanceParams);
_setRedemptionThrottleParams(redemptionParams);
require(
isRedemptionThrottleGreaterByDelta(issuanceParams, redemptionParams),
"redemption throttle too low"
);
}

// === Private Helpers ===

function _setIssuanceThrottleParams(ThrottleLib.Params calldata params) private {
require(params.amtRate >= MIN_THROTTLE_RATE_AMT, "issuance amtRate too small");
require(params.amtRate <= MAX_THROTTLE_RATE_AMT, "issuance amtRate too big");
require(params.pctRate <= MAX_THROTTLE_PCT_AMT, "issuance pctRate too big");
Expand All @@ -471,7 +503,7 @@ contract RTokenP1 is ComponentP1, ERC20PermitUpgradeable, IRToken {
}

/// @custom:governance
function setRedemptionThrottleParams(ThrottleLib.Params calldata params) public governance {
function _setRedemptionThrottleParams(ThrottleLib.Params calldata params) private {
require(params.amtRate >= MIN_THROTTLE_RATE_AMT, "redemption amtRate too small");
require(params.amtRate <= MAX_THROTTLE_RATE_AMT, "redemption amtRate too big");
require(params.pctRate <= MAX_THROTTLE_PCT_AMT, "redemption pctRate too big");
Expand All @@ -481,7 +513,25 @@ contract RTokenP1 is ComponentP1, ERC20PermitUpgradeable, IRToken {
redemptionThrottle.params = params;
}

// ==== Private ====
/// @notice Checks if the redemption throttle is greater than the issuance throttle by the
/// required delta
/// @dev Compares both amtRate and pctRate individually to ensure each meets the minimum
/// delta requirement
/// @param issuance The issuance throttle parameters to compare against
/// @param redemption The redemption throttle parameters to check
/// @return bool True if redemption throttle is greater by at least MIN_THROTTLE_DELTA,
/// false otherwise
function isRedemptionThrottleGreaterByDelta(
ThrottleLib.Params memory issuance,
ThrottleLib.Params memory redemption
) private pure returns (bool) {
uint256 requiredAmtRate = issuance.amtRate +
((issuance.amtRate * MIN_THROTTLE_DELTA) / FIX_ONE);
uint256 requiredPctRate = issuance.pctRate +
((issuance.pctRate * MIN_THROTTLE_DELTA) / FIX_ONE);

return redemption.amtRate >= requiredAmtRate && redemption.pctRate >= requiredPctRate;
}

/// Mint an amount of RToken equivalent to amtBaskets and scale basketsNeeded up
/// @param recipient The address to receive the RTokens
Expand Down
49 changes: 49 additions & 0 deletions contracts/plugins/assets/aerodrome/AerodromeGaugeWrapper.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity ^0.8.19;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "../erc20/RewardableERC20Wrapper.sol";
import "./vendor/IAeroGauge.sol";

// Note: Only supports AERO rewards.
contract AerodromeGaugeWrapper is RewardableERC20Wrapper {
using SafeERC20 for IERC20;

IAeroGauge public immutable gauge;

/// @param _lpToken The Aerodrome LP token, transferrable
constructor(
ERC20 _lpToken,
string memory _name,
string memory _symbol,
ERC20 _aero,
IAeroGauge _gauge
) RewardableERC20Wrapper(_lpToken, _name, _symbol, _aero) {
require(
address(_aero) != address(0) &&
address(_gauge) != address(0) &&
address(_lpToken) != address(0),
"invalid address"
);

require(address(_aero) == address(_gauge.rewardToken()), "wrong Aero");

gauge = _gauge;
}

// deposit an Aerodrome LP token
function _afterDeposit(uint256 _amount, address) internal override {
underlying.approve(address(gauge), _amount);
gauge.deposit(_amount);
}

// withdraw to Aerodrome LP token
function _beforeWithdraw(uint256 _amount, address) internal override {
gauge.withdraw(_amount);
}

// claim rewards - only supports AERO rewards
function _claimAssetRewards() internal virtual override {
gauge.getReward(address(this));
}
}
Loading

0 comments on commit 17dd84b

Please sign in to comment.