-
Notifications
You must be signed in to change notification settings - Fork 70
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Frax Convex Strategy with locked Frax Staked Convex LP tokens #1917
Closed
naddison36
wants to merge
44
commits into
nicka/convex-frxeth-weth-libs
from
nicka/convex-frax-locking
Closed
Changes from 32 commits
Commits
Show all changes
44 commits
Select commit
Hold shift + click to select a range
b92b96e
Fix Natspec of old Convex and ThreePool strategies
naddison36 31aa809
WIP ConvexFraxLockingStrategy
naddison36 339d415
Locked Frax Staked Convex deploy script
naddison36 c7b71dc
Merge remote-tracking branch 'origin/nicka/convex-frxeth-weth-libs' i…
naddison36 185bd20
Fix findBalancesSlot for FXS
naddison36 6eb4817
Added FraxConvexStrategy to hot deploy
naddison36 cd60c74
removed Balancer vault, wstETH and Curve frxETH/WETH from WhaleAddresses
naddison36 2f29886
Merge remote-tracking branch 'origin/nicka/convex-frxeth-weth-libs' i…
naddison36 48625da
Refactor of FraxConvexStrategy
naddison36 e8e0806
Fix lint and slither
naddison36 02e986a
Generated new contract diagrams
naddison36 9f3482e
Update comments
naddison36 e78d5ff
Merge remote-tracking branch 'origin/nicka/convex-frxeth-weth-libs' i…
naddison36 7b6dc6f
Updated FraxConvexStrategy code comments
naddison36 8ae4702
Merge remote-tracking branch 'origin/nicka/convex-frxeth-weth-libs' i…
naddison36 fa86a9f
Merge remote-tracking branch 'origin/nicka/convex-frxeth-weth-libs' i…
naddison36 86643d4
Updated Frax Convex deploy file
naddison36 a5ad85e
fix storage variables visibility
naddison36 c2ba94b
Frax Convex Strategy simplification (#1951)
naddison36 04db36e
Added check that all assets have the same number of decimals
naddison36 e77f6e8
Fix slither
naddison36 f0551f4
Generated latest FraxConvexStrategy diagrams
naddison36 71a8bf8
Polish Frax Convex Strategy comments
naddison36 d8d635e
Merge remote-tracking branch 'origin/nicka/convex-frxeth-weth-libs' i…
naddison36 4b90c23
Added Frax Convex deposit
naddison36 caa92ce
Skip Convex frxETH/WETH strategy as its no longer being used
naddison36 6353dd8
Enable FXS harvesting (#1954)
sparrowDom b397370
Merge remote-tracking branch 'origin/nicka/convex-frxeth-weth-libs' i…
naddison36 421320c
Merge remote-tracking branch 'origin/nicka/convex-frax-locking' into …
naddison36 230d15e
Added harvest of FXS to value flow
naddison36 40f4853
add comment regarding stacking of errors
sparrowDom 5870d1d
Merge remote-tracking branch 'refs/remotes/origin/nicka/convex-frax-l…
sparrowDom 3c5fd03
Removed MIN_LOCK_AMOUNT
naddison36 ff38071
Merge remote-tracking branch 'origin/nicka/convex-frax-locking' into …
naddison36 883b06b
Generated new contract diagrams
naddison36 cc4c40a
Small gas optimization
naddison36 dcb83d1
Update value flow diagrams (#1953)
naddison36 34fde41
Merge remote-tracking branch 'origin/master' into nicka/convex-frax-l…
naddison36 1d6e3de
Fix AMO tests (#1977)
shahthepro 7fb738f
Merge remote-tracking branch 'origin/master' into nicka/convex-frax-l…
naddison36 717f72d
Changes from yAudit review (#1976)
naddison36 f0e747b
N-01 Lack of Indexed Event Parameters (#1973)
naddison36 a1bda8e
N-05 Added CURVE_VIRTUAL_PRICE_SCALE constant (#1974)
naddison36 826735c
Merge branch 'nicka/convex-frxeth-weth-libs' into nicka/convex-frax-l…
sparrowDom File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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,107 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.0; | ||
|
||
interface IFraxConvexLocking { | ||
/* ========== EVENTS ========== */ | ||
event LockedAdditional( | ||
address indexed user, | ||
bytes32 kek_id, | ||
uint256 amount | ||
); | ||
event LockedLonger( | ||
address indexed user, | ||
bytes32 kek_id, | ||
uint256 new_secs, | ||
uint256 new_start_ts, | ||
uint256 new_end_ts | ||
); | ||
event StakeLocked( | ||
address indexed user, | ||
uint256 amount, | ||
uint256 secs, | ||
bytes32 kek_id, | ||
address source_address | ||
); | ||
event WithdrawLocked( | ||
address indexed user, | ||
uint256 liquidity, | ||
bytes32 kek_id, | ||
address destination_address | ||
); | ||
|
||
// Struct for the stake | ||
struct LockedStake { | ||
bytes32 kek_id; | ||
uint256 start_timestamp; | ||
uint256 liquidity; | ||
uint256 ending_timestamp; | ||
uint256 lock_multiplier; // 6 decimals of precision. 1x = 1000000 | ||
} | ||
|
||
function curvePool() external view returns (address); | ||
|
||
function curveToken() external view returns (address); | ||
|
||
function earned(address account) | ||
external | ||
view | ||
returns (uint256[] memory new_earned); | ||
|
||
function getAllRewardTokens() external view returns (address[] memory); | ||
|
||
function getReward(address destination_address) | ||
external | ||
returns (uint256[] memory); | ||
|
||
function getReward2(address destination_address, bool claim_extra_too) | ||
external | ||
returns (uint256[] memory); | ||
|
||
function getRewardForDuration() | ||
external | ||
view | ||
returns (uint256[] memory rewards_per_duration_arr); | ||
|
||
function lastRewardClaimTime(address) external view returns (uint256); | ||
|
||
function lastUpdateTime() external view returns (uint256); | ||
|
||
function lockAdditional(bytes32 kek_id, uint256 addl_liq) external; | ||
|
||
function lockLonger(bytes32 kek_id, uint256 new_ending_ts) external; | ||
|
||
function lockMultiplier(uint256 secs) external view returns (uint256); | ||
|
||
function lockedLiquidityOf(address account) external view returns (uint256); | ||
|
||
function lockedStakes(address, uint256) | ||
external | ||
view | ||
returns ( | ||
bytes32 kek_id, | ||
uint256 start_timestamp, | ||
uint256 liquidity, | ||
uint256 ending_timestamp, | ||
uint256 lock_multiplier | ||
); | ||
|
||
function lockedStakesOf(address account) | ||
external | ||
view | ||
returns (LockedStake[] memory); | ||
|
||
function lockedStakesOfLength(address account) | ||
external | ||
view | ||
returns (uint256); | ||
|
||
function stakeLocked(uint256 liquidity, uint256 secs) | ||
external | ||
returns (bytes32); | ||
|
||
function withdrawLocked( | ||
bytes32 kek_id, | ||
address destination_address, | ||
bool claim_rewards | ||
) external returns (uint256); | ||
} |
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,69 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.0; | ||
|
||
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | ||
|
||
interface IFraxConvexStaking is IERC20 { | ||
event Deposited( | ||
address indexed _user, | ||
address indexed _account, | ||
uint256 _amount, | ||
bool _wrapped | ||
); | ||
event Withdrawn(address indexed _user, uint256 _amount, bool _unwrapped); | ||
event RewardInvalidated(address _rewardToken); | ||
event RewardRedirected(address indexed _account, address _forward); | ||
event RewardAdded(address _token); | ||
event Shutdown(); | ||
|
||
struct EarnedData { | ||
address token; | ||
uint256 amount; | ||
} | ||
|
||
function collateralVault() external view returns (address); | ||
|
||
function convexBooster() external view returns (address); | ||
|
||
function convexPool() external view returns (address); | ||
|
||
function convexPoolId() external view returns (uint256); | ||
|
||
function convexToken() external view returns (address); | ||
|
||
function curveToken() external view returns (address); | ||
|
||
function deposit(uint256 _amount, address _to) external; | ||
|
||
function earned(address _account) | ||
external | ||
returns (EarnedData[] memory claimable); | ||
|
||
function getReward(address _account, address _forwardTo) external; | ||
|
||
function getReward(address _account) external; | ||
|
||
function isShutdown() external view returns (bool); | ||
|
||
function rewardLength() external view returns (uint256); | ||
|
||
function rewardRedirect(address) external view returns (address); | ||
|
||
function rewards(uint256) | ||
external | ||
view | ||
returns ( | ||
address reward_token, | ||
address reward_pool, | ||
uint256 reward_integral, | ||
uint256 reward_remaining | ||
); | ||
|
||
function stake(uint256 _amount, address _to) external; | ||
|
||
function totalBalanceOf(address _account) external view returns (uint256); | ||
|
||
function withdraw(uint256 _amount) external; | ||
|
||
function withdrawAndUnwrap(uint256 _amount) external; | ||
} |
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
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
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,139 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.0; | ||
|
||
import { AggregatorV3Interface } from "../interfaces/chainlink/AggregatorV3Interface.sol"; | ||
import { StableMath } from "../utils/StableMath.sol"; | ||
import "@openzeppelin/contracts/utils/math/Math.sol"; | ||
import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol"; | ||
|
||
/** | ||
* @notice Price feed when the feed of 2 oracle prices need combining to achieve desired result. | ||
* | ||
* @dev multiplying oracle pair prices has combining properties. E.g. price FXS/USD multiplied by | ||
* USD/ETH results effectively in FXS/ETH price. Since oracle prices express asset on the left priced | ||
* by the asset on the right, we sometimes need to reverse prices in order to achieve desired results. | ||
* ETH/USD reversed is USD/ETH. | ||
* | ||
* In our first usage of this contract we required FXS/ETH price. It can be derived using FXS/USD and | ||
* ETH/USD prices. Since we need the latter reversed to get the desired result we configure the contract | ||
* by using FXS/USD as feed 0 and USD/ETH (reversed from ETH/USD) as feed 1. | ||
* | ||
* IMPORTANT: It is important to consider that combining 2 Oracle price feeds increases their | ||
* error rate using addition. Meaning if feed 0 has error rate of X and feed 1 error rate of Y | ||
* the resulting error rate is X + Y. E.g. | ||
* - FXS/ETH combines FXS/USD (possible deviation 2%), ETH/USD (possible deviation 0.5%) resulting in | ||
* FXS/USD having possible deviation of 2.5% | ||
*/ | ||
contract PriceFeedPair is AggregatorV3Interface { | ||
using SafeCast for uint256; | ||
using SafeCast for int256; | ||
using StableMath for uint256; | ||
|
||
// Fields to make it compatible with `AggregatorV3Interface` | ||
uint8 public constant override decimals = 18; | ||
string public constant override description = ""; | ||
uint256 public constant override version = 1; | ||
address public immutable addressFeed0; | ||
address public immutable addressFeed1; | ||
bool public immutable reverseFeed0; | ||
bool public immutable reverseFeed1; | ||
uint8 internal immutable decimalsFeed0; | ||
uint8 internal immutable decimalsFeed1; | ||
|
||
error PriceFeedAddressError(address _address); | ||
error PriceFeedsMatchError(); | ||
|
||
constructor( | ||
address _addressFeed0, | ||
address _addressFeed1, | ||
bool _reverseFeed0, | ||
bool _reverseFeed1 | ||
) { | ||
if (_addressFeed0 == address(0)) { | ||
revert PriceFeedAddressError(_addressFeed0); | ||
} | ||
if (_addressFeed1 == address(0)) { | ||
revert PriceFeedAddressError(_addressFeed1); | ||
} | ||
if (_addressFeed0 == _addressFeed1) { | ||
revert PriceFeedsMatchError(); | ||
} | ||
|
||
decimalsFeed0 = AggregatorV3Interface(_addressFeed0).decimals(); | ||
decimalsFeed1 = AggregatorV3Interface(_addressFeed1).decimals(); | ||
addressFeed0 = _addressFeed0; | ||
addressFeed1 = _addressFeed1; | ||
reverseFeed0 = _reverseFeed0; | ||
reverseFeed1 = _reverseFeed1; | ||
} | ||
|
||
function _calculatePrice(int256 priceFeed0, int256 priceFeed1) | ||
internal | ||
view | ||
returns (int256) | ||
{ | ||
uint256 price0 = priceFeed0.toUint256().scaleBy(18, decimalsFeed0); | ||
|
||
if (reverseFeed0) { | ||
price0 = uint256(1e18).divPrecisely(price0); | ||
} | ||
|
||
uint256 price1 = priceFeed1.toUint256().scaleBy(18, decimalsFeed1); | ||
|
||
if (reverseFeed1) { | ||
price1 = uint256(1e18).divPrecisely(price1); | ||
} | ||
|
||
return price0.mulTruncate(price1).toInt256(); | ||
} | ||
|
||
/** | ||
* @notice This function exists to make the contract compatible | ||
* with AggregatorV3Interface (which OETHOracleRouter uses to | ||
* get the price). | ||
**/ | ||
function latestRoundData() | ||
external | ||
view | ||
override | ||
returns ( | ||
uint80, | ||
int256 price, | ||
uint256, | ||
uint256 updatedAt, | ||
uint80 | ||
) | ||
{ | ||
// slither-disable-next-line unused-return | ||
(, int256 _price0, , uint256 updatedAt0, ) = AggregatorV3Interface( | ||
addressFeed0 | ||
).latestRoundData(); | ||
// slither-disable-next-line unused-return | ||
(, int256 _price1, , uint256 updatedAt1, ) = AggregatorV3Interface( | ||
addressFeed1 | ||
).latestRoundData(); | ||
updatedAt = Math.min(updatedAt0, updatedAt1); | ||
price = _calculatePrice(_price0, _price1); | ||
} | ||
|
||
/** | ||
* @notice This function exists to make the contract compatible | ||
* with AggregatorV3Interface. The two oracles don't have rounds | ||
* in sync and for that reason we can not query arbitrary oracle | ||
* round and combine it. | ||
**/ | ||
function getRoundData(uint80) | ||
external | ||
pure | ||
override | ||
returns ( | ||
uint80, | ||
int256 price, | ||
uint256, | ||
uint256 updatedAt, | ||
uint80 | ||
) | ||
{ | ||
revert("No data present"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: can be a custom error |
||
} | ||
} |
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
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sparrowDom @naddison36 Thinking about this, one potential concern I have is that
latestRound
of one feed can be way off from the other feed. Would it make sense to compare theupdatedAt
values and make sure both the updates aren't too far off?Like if FXS<>USD was last updated 24h ago and ETH<>USD 1h ago, I don't think it'd be very reliable to use this price. Especially if there was too much of market movement/price action in the past 24h. More so, since this is a generic contract
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another way to do this for FXS<>ETH pair would be just use Uniswap V3's TWAP (by doing something similar to what we do on AuraPriceFeed): https://blog.uniswap.org/uniswap-v3-oracles
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We've discussed this on discord and figured out that the errors of multiple oracle price feeds stack. E.g. 2% error and 0.5% error price feeds result in 2.5% error price feed. We are OK keeping it like that for now, but still important to consider.