diff --git a/common/configuration.ts b/common/configuration.ts index daae83074..6794a846f 100644 --- a/common/configuration.ts +++ b/common/configuration.ts @@ -121,7 +121,12 @@ export interface ITokens { // Aerodrome AERO?: string + + MOG?: string USDz?: string + cbBTC?: string + WELL?: string + DEGEN?: string // Sky USDS?: string @@ -154,7 +159,12 @@ export interface IPools { crvMIM3Pool?: string sdUSDCUSDCPlus?: string aeroUSDCeUSD?: string + aeroWETHAERO?: string + aeroMOGWETH?: string aeroUSDzUSDC?: string + aeroWETHcbBTC?: string + aeroWETHWELL?: string + aeroWETHDEGEN?: string } interface INetworkConfig { @@ -533,9 +543,13 @@ export const networkConfig: { [key: string]: INetworkConfig } = { wstETH: '0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452', STG: '0xE3B53AF74a4BF62Ae5511055290838050bf764Df', eUSD: '0xCfA3Ef56d303AE4fAabA0592388F19d7C3399FB4', + MOG: '0x2Da56AcB9Ea78330f947bD57C54119Debda7AF71', USDz: '0x04D5ddf5f3a8939889F11E97f8c4BB48317F1938', meUSD: '0xbb819D845b573B5D7C538F5b85057160cfb5f313', AERO: '0x940181a94A35A4569E4529A3CDfB74e38FD98631', + cbBTC: '0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf', + WELL: '0xA88594D404727625A9437C3f886C7643872296AE', + DEGEN: '0x4ed4E862860beD51a9570b96d89aF5E1B0Efefed', }, chainlinkFeeds: { DAI: '0x591e79239a7d679378ec8c847e5038150364c78f', // 0.3%, 24hr @@ -555,6 +569,10 @@ export const networkConfig: { [key: string]: INetworkConfig } = { eUSD: '0x9b2C948dbA5952A1f5Ab6fA16101c1392b8da1ab', // 0.5%, 24h USDz: '0xe25969e2Fa633a0C027fAB8F30Fc9C6A90D60B48', // 0.5%, 24h AERO: '0x4EC5970fC728C5f65ba413992CD5fF6FD70fcfF0', // 0.5%, 24h + MOG: '0x4aeb6D15769EaD32D0c5Be2940F40c7CFf53801d', // 0.5%, 24h + cbBTC: '0x07DA0E54543a844a80ABE69c8A12F22B3aA59f9D', // 0.3%, 20 min + WELL: '0xc15d9944dAefE2dB03e53bef8DDA25a56832C5fe', // 0.5%, 24h + DEGEN: '0xE62BcE5D7CB9d16AB8b4D622538bc0A50A5799c2', // 0.5%, 24h }, GNOSIS_EASY_AUCTION: '0xb1875Feaeea32Bbb02DE83D81772e07E37A40f02', // mock COMET_REWARDS: '0x123964802e6ABabBE1Bc9547D72Ef1B69B00A6b1', diff --git a/contracts/plugins/assets/aerodrome/AerodromePoolTokens.sol b/contracts/plugins/assets/aerodrome/AerodromePoolTokens.sol index 882b32ee3..fa62c625e 100644 --- a/contracts/plugins/assets/aerodrome/AerodromePoolTokens.sol +++ b/contracts/plugins/assets/aerodrome/AerodromePoolTokens.sol @@ -8,7 +8,7 @@ import "contracts/plugins/assets/OracleLib.sol"; import "contracts/libraries/Fixed.sol"; import "./vendor/IAeroPool.sol"; -/// Supports Aerodrome stable pools (2 tokens) +/// Supports Aerodrome stable and volatile pools (2 tokens) contract AerodromePoolTokens { using OracleLib for AggregatorV3Interface; using FixLib for uint192; @@ -20,7 +20,7 @@ contract AerodromePoolTokens { enum AeroPoolType { Stable, - Volatile // not supported in this version + Volatile } // === State (Immutable) === @@ -76,7 +76,10 @@ contract AerodromePoolTokens { // === Tokens === - if (config.poolType != AeroPoolType.Stable || !config.pool.stable()) { + if (config.poolType == AeroPoolType.Stable && !config.pool.stable()) { + revert("invalid poolType"); + } + if (config.poolType == AeroPoolType.Volatile && config.pool.stable()) { revert("invalid poolType"); } @@ -175,8 +178,8 @@ contract AerodromePoolTokens { } /// @param index The index of the token: 0 or 1 - /// @return [{ref_index}] - function tokenReserve(uint8 index) public view virtual returns (uint256) { + /// @return {ref_index} + function tokenReserve(uint8 index) public view virtual returns (uint192) { if (index >= nTokens) revert WrongIndex(nTokens - 1); // Maybe also cache token decimals as immutable? IERC20Metadata tokenInterface = getToken(index); diff --git a/contracts/plugins/assets/aerodrome/AerodromeStableCollateral.sol b/contracts/plugins/assets/aerodrome/AerodromeStableCollateral.sol index aab8b8f75..5c7965702 100644 --- a/contracts/plugins/assets/aerodrome/AerodromeStableCollateral.sol +++ b/contracts/plugins/assets/aerodrome/AerodromeStableCollateral.sol @@ -1,17 +1,7 @@ // SPDX-License-Identifier: BlueOak-1.0.0 pragma solidity 0.8.19; -import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; -import "@openzeppelin/contracts/utils/math/Math.sol"; -import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; -import "contracts/interfaces/IAsset.sol"; -import "contracts/libraries/Fixed.sol"; -import "contracts/plugins/assets/FiatCollateral.sol"; -import "../../../interfaces/IRewardable.sol"; -import "./AerodromePoolTokens.sol"; - -// This plugin only works on Base -IERC20 constant AERO = IERC20(0x940181a94A35A4569E4529A3CDfB74e38FD98631); +import "./AerodromeVolatileCollateral.sol"; /** * @title AerodromeStableCollateral @@ -24,20 +14,17 @@ IERC20 constant AERO = IERC20(0x940181a94A35A4569E4529A3CDfB74e38FD98631); * UoA = USD * */ -contract AerodromeStableCollateral is FiatCollateral, AerodromePoolTokens { +contract AerodromeStableCollateral is AerodromeVolatileCollateral { using OracleLib for AggregatorV3Interface; using FixLib for uint192; /// @dev config Unused members: chainlinkFeed, oracleError, oracleTimeout - /// @dev No revenue hiding (refPerTok() == FIX_ONE) + /// @dev No revenue hiding (refPerTok() is constant) /// @dev config.erc20 should be an AerodromeStakingWrapper constructor(CollateralConfig memory config, APTConfiguration memory aptConfig) - FiatCollateral(config) - AerodromePoolTokens(aptConfig) + AerodromeVolatileCollateral(config, aptConfig) { require(config.defaultThreshold != 0, "defaultThreshold zero"); - assert((token0.decimals() + token1.decimals()) % 2 == 0); - maxOracleTimeout = uint48(Math.max(maxOracleTimeout, maxPoolOracleTimeout())); } /// Can revert, used by other contract functions in order to catch errors @@ -58,105 +45,44 @@ contract AerodromeStableCollateral is FiatCollateral, AerodromePoolTokens { uint192 pegPrice ) { - uint256 r0 = tokenReserve(0); - uint256 r1 = tokenReserve(1); + uint256 r0 = tokenReserve(0); // {ref_0} upcast + uint256 r1 = tokenReserve(1); // {ref_1} upcast // xy^3 + yx^3 >= k for sAMM pools uint256 sqrtReserve = sqrt256(sqrt256(r0 * r1) * sqrt256(r0 * r0 + r1 * r1)); // get token prices - (uint192 p0_low, uint192 p0_high) = tokenPrice(0); - (uint192 p1_low, uint192 p1_high) = tokenPrice(1); + (uint192 p0_low, uint192 p0_high) = tokenPrice(0); // {UoA/ref_0} + (uint192 p1_low, uint192 p1_high) = tokenPrice(1); // {UoA/ref_1} - uint192 totalSupply = shiftl_toFix(pool.totalSupply(), -int8(pool.decimals()), FLOOR); + // all aero pools have 18 decimals + uint192 totalSupply = _safeWrap(pool.totalSupply()); // low { - uint256 ratioLow = ((1e18) * p0_high) / p1_low; + uint256 ratioLow = (1e18 * p0_high) / p1_low; uint256 sqrtPriceLow = sqrt256( - sqrt256((1e18) * ratioLow) * sqrt256(1e36 + ratioLow * ratioLow) + sqrt256(1e18 * ratioLow) * sqrt256(1e36 + ratioLow * ratioLow) ); - low = _safeWrap(((((1e18) * sqrtReserve) / sqrtPriceLow) * p0_low * 2) / totalSupply); + low = _safeWrap((((1e18 * sqrtReserve) / sqrtPriceLow) * p0_low * 2) / totalSupply); } // high { - uint256 ratioHigh = ((1e18) * p0_low) / p1_high; + uint256 ratioHigh = (1e18 * p0_low) / p1_high; uint256 sqrtPriceHigh = sqrt256( - sqrt256((1e18) * ratioHigh) * sqrt256(1e36 + ratioHigh * ratioHigh) + sqrt256(1e18 * ratioHigh) * sqrt256(1e36 + ratioHigh * ratioHigh) ); - high = _safeWrap( - ((((1e18) * sqrtReserve) / sqrtPriceHigh) * p0_high * 2) / totalSupply - ); + high = _safeWrap((((1e18 * sqrtReserve) / sqrtPriceHigh) * p0_high * 2) / totalSupply); } - assert(low <= high); //obviously true just by inspection - - // {target/ref} = {UoA/ref} = {UoA/tok} / ({ref/tok} - // {target/ref} and {UoA/ref} are the same since target == UoA - pegPrice = ((low + high) / 2).div(refPerTok()); - } - - /// Should not revert - /// Refresh exchange rates and update default status. - /// Have to override to add custom default checks - function refresh() public virtual override { - CollateralStatus oldStatus = status(); - - // Check for soft default + save prices - try this.tryPrice() returns (uint192 low, uint192 high, uint192 pegPrice) { - // {UoA/tok}, {UoA/tok}, {UoA/tok} - // (0, 0) is a valid price; (0, FIX_MAX) is unpriced - - // Save prices if priced - if (high != FIX_MAX) { - savedLowPrice = low; - savedHighPrice = high; - savedPegPrice = pegPrice; - lastSave = uint48(block.timestamp); - } else { - // must be unpriced - // untested: - // validated in other plugins, cost to test here is high - assert(low == 0); - } - - // If the price is below the default-threshold price, default eventually - // uint192(+/-) is the same as Fix.plus/minus - if (low == 0 || _anyDepeggedInPool()) { - markStatus(CollateralStatus.IFFY); - } else { - markStatus(CollateralStatus.SOUND); - } - } catch (bytes memory errData) { - // see: docs/solidity-style.md#Catching-Empty-Data - if (errData.length == 0) revert(); // solhint-disable-line reason-string - markStatus(CollateralStatus.IFFY); - } - - CollateralStatus newStatus = status(); - if (oldStatus != newStatus) { - emit CollateralStatusChanged(oldStatus, newStatus); - } - } - - /// Claim rewards earned by holding a balance of the ERC20 token - /// @custom:delegate-call - function claimRewards() external virtual override(Asset, IRewardable) { - uint256 aeroBal = AERO.balanceOf(address(this)); - IRewardable(address(erc20)).claimRewards(); - emit RewardsClaimed(AERO, AERO.balanceOf(address(this)) - aeroBal); - } + assert(low <= high); // not obviously true just by inspection - /// @return {ref/tok} Actual quantity of whole reference units per whole collateral tokens - function refPerTok() public view virtual override returns (uint192) { - int8 shift = 18 - int8((token0.decimals() + token1.decimals()) / 2); - return shiftl_toFix(2, shift, FLOOR); + pegPrice = 0; } // === Internal === - // Override this later to implement non-stable pools - function _anyDepeggedInPool() internal view virtual returns (bool) { + function _anyDepeggedInPool() internal view virtual override returns (bool) { // Check reference token oracles for (uint8 i = 0; i < nTokens; ++i) { try this.tokenPrice(i) returns (uint192 low, uint192 high) { diff --git a/contracts/plugins/assets/aerodrome/AerodromeVolatileCollateral.sol b/contracts/plugins/assets/aerodrome/AerodromeVolatileCollateral.sol new file mode 100644 index 000000000..8b8e8bd9f --- /dev/null +++ b/contracts/plugins/assets/aerodrome/AerodromeVolatileCollateral.sol @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: BlueOak-1.0.0 +pragma solidity 0.8.19; + +import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; +import "@openzeppelin/contracts/utils/math/Math.sol"; +import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; +import "contracts/interfaces/IAsset.sol"; +import "contracts/libraries/Fixed.sol"; +import "contracts/plugins/assets/FiatCollateral.sol"; +import "../../../interfaces/IRewardable.sol"; +import "./AerodromePoolTokens.sol"; + +// This plugin only works on Base +IERC20 constant AERO = IERC20(0x940181a94A35A4569E4529A3CDfB74e38FD98631); + +/** + * @title AerodromeVolatileCollateral + * This plugin contract is designed for Aerodrome volatile pools + * Each token in the pool can have between 1 and 2 oracles per each token. + * + * There is no appreciation, only AERO rewards. + * + * tok = AerodromeStakingWrapper(volatilePool) + * ref = LP token /w shift + * tar = LP token /w shift + * UoA = USD + * + */ +contract AerodromeVolatileCollateral is FiatCollateral, AerodromePoolTokens { + using OracleLib for AggregatorV3Interface; + using FixLib for uint192; + + /// @dev config Unused members: chainlinkFeed, oracleError, oracleTimout + /// @dev No revenue hiding (refPerTok() is constant) + /// @dev config.erc20 should be an AerodromeStakingWrapper + constructor(CollateralConfig memory config, APTConfiguration memory aptConfig) + FiatCollateral(config) + AerodromePoolTokens(aptConfig) + { + assert((token0.decimals() + token1.decimals()) % 2 == 0); + assert(pool.decimals() == 18); + maxOracleTimeout = uint48(Math.max(maxOracleTimeout, maxPoolOracleTimeout())); + } + + /// Can revert, used by other contract functions in order to catch errors + /// Should not return FIX_MAX for low + /// Should only return FIX_MAX for high if low is 0 + /// Should NOT be manipulable by MEV + /// @return low {UoA/tok} The low price estimate + /// @return high {UoA/tok} The high price estimate + /// @return pegPrice {target/ref} The actual price observed in the peg + function tryPrice() + external + view + virtual + override + returns ( + uint192 low, + uint192 high, + uint192 pegPrice + ) + { + uint192 r0 = tokenReserve(0); // {ref_0} + uint192 r1 = tokenReserve(1); // {ref_1} + + // x * y >= k for vAMM pools + uint192 sqrtReserve = r0.mul(r1, ROUND).sqrt(); + + // get token prices + (uint192 p0_low, uint192 p0_high) = tokenPrice(0); // {UoA/ref_0} + (uint192 p1_low, uint192 p1_high) = tokenPrice(1); // {UoA/ref_1} + + // all aero pools have 18 decimals already + uint192 totalSupply = _safeWrap(pool.totalSupply()); + + // low + uint192 sqrtPriceLow = p0_high.div(p1_low, CEIL).sqrt(); + low = sqrtReserve.mulDiv(p0_low, sqrtPriceLow, FLOOR).mulDiv(2e18, totalSupply, FLOOR); + + // high + uint192 sqrtPriceHigh = p0_low.div(p1_high, FLOOR).sqrt(); + high = sqrtReserve.mulDiv(p0_high, sqrtPriceHigh, CEIL).mulDiv(2e18, totalSupply, CEIL); + + assert(low <= high); // not obviously true just by inspection + + pegPrice = 0; // no default checks or issuance premium + } + + /// Should not revert + /// Refresh exchange rates and update default status. + /// Have to override to add custom default checks + function refresh() public virtual override { + CollateralStatus oldStatus = status(); + + // Check for soft default + save prices + try this.tryPrice() returns (uint192 low, uint192 high, uint192 pegPrice) { + // {UoA/tok}, {UoA/tok}, {UoA/tok} + // (0, 0) is a valid price; (0, FIX_MAX) is unpriced + + // Save prices if priced + if (high != FIX_MAX) { + savedLowPrice = low; + savedHighPrice = high; + savedPegPrice = pegPrice; + lastSave = uint48(block.timestamp); + } else { + // must be unpriced + // untested: + // validated in other plugins, cost to test here is high + assert(low == 0); + } + + // If the price is below the default-threshold price, default eventually + // uint192(+/-) is the same as Fix.plus/minus + if (low == 0 || _anyDepeggedInPool()) { + markStatus(CollateralStatus.IFFY); + } else { + markStatus(CollateralStatus.SOUND); + } + } catch (bytes memory errData) { + // see: docs/solidity-style.md#Catching-Empty-Data + if (errData.length == 0) revert(); // solhint-disable-line reason-string + markStatus(CollateralStatus.IFFY); + } + + CollateralStatus newStatus = status(); + if (oldStatus != newStatus) { + emit CollateralStatusChanged(oldStatus, newStatus); + } + } + + /// Claim rewards earned by holding a balance of the ERC20 token + /// @custom:delegate-call + function claimRewards() external virtual override(Asset, IRewardable) { + uint256 aeroBal = AERO.balanceOf(address(this)); + IRewardable(address(erc20)).claimRewards(); + emit RewardsClaimed(AERO, AERO.balanceOf(address(this)) - aeroBal); + } + + /// @return {ref/tok} Actual quantity of whole reference units per whole collateral tokens + function refPerTok() public view virtual override returns (uint192) { + int8 shift = 18 - int8((token0.decimals() + token1.decimals()) / 2); + return shiftl_toFix(2, shift, FLOOR); + } + + // === Internal === + + // Override this later to implement non-stable pools + function _anyDepeggedInPool() internal view virtual returns (bool) { + // TODO maybe expand plugin later to support peg checks, currently not worth it + return false; + } +} diff --git a/contracts/plugins/assets/aerodrome/README.md b/contracts/plugins/assets/aerodrome/README.md index 82cf963aa..0eea2e013 100644 --- a/contracts/plugins/assets/aerodrome/README.md +++ b/contracts/plugins/assets/aerodrome/README.md @@ -1,12 +1,12 @@ -# Aerodrome Stable Collateral Plugin +# Aerodrome Collateral Plugins -[Aerodrome Finance](https://aerodrome.finance) is an AMM designed to serve as Base's central liquidity hub. This plugin enables the use of any Aerodrome Stable LP token as collateral within the Reserve Protocol. +[Aerodrome Finance](https://aerodrome.finance) is an AMM designed to serve as Base's central liquidity hub. This plugin enables the use of any Aerodrome Stable and Volatile LP token as collateral within the Reserve Protocol. Aerodrome Finance offers two different liquidity pool types based on token pair needs, `Stable Pools` and `Volatile Pools`. -Only `Stable Pools` are currently supported. These pools are designed for tokens which have little to no volatility, and use the current formula for pricing tokens: +`Stable Pools` are designed for tokens which have little to no volatility, and use the current formula for pricing tokens: `x³y + y³x ≥ k` -`x³y + y³x ≥ k` +`Volatile Pools` are designed for tokens with high price volatility, and use a generic AMM formula: `x × y ≥ k` ## Usage @@ -36,10 +36,11 @@ Internally, all `tokensPriceFeeds` are stored as multiple separate immutable var ### refPerTok -Aerodrome Stable Pools do not appreciate in value over time, so `refPerTok()` will be constant for these plugins and will not change. This also means there are no hard default checks in place. +Aerodrome Pools do not appreciate in value over time, so `refPerTok()` will be constant for these plugins and will not change. This also means there are no hard default checks in place. ## Implementation -| `tok` | `ref` | `target` | `UoA` | -| :------------------: | :---------------: | :------: | :---: | -| Aero Staking Wrapper | LP token /w shift | USD | USD | +| `tok` | `ref` | `target` | `UoA` | +| :------------------: | :---------------: | :--------------: | :---: | +| Aero Staking Wrapper | LP token /w shift | USD (Stable) | USD | +| Aero Staking Wrapper | LP token /w shift | cpAMM (Volatile) | USD | diff --git a/scripts/addresses/8453-tmp-assets-collateral.json b/scripts/addresses/8453-tmp-assets-collateral.json index 76784c695..33e58ecf6 100644 --- a/scripts/addresses/8453-tmp-assets-collateral.json +++ b/scripts/addresses/8453-tmp-assets-collateral.json @@ -13,9 +13,14 @@ "saBasUSDC": "0xC19f5d60e2Aca1174f3D5Fe189f0A69afaB76f50", "cUSDCv3": "0xf7a9D27c3B60c78c6F6e2c2d6ED6E8B94b352461", "wstETH": "0x8b4374005291B8FCD14C4E947604b2FB3C660A73", - "meUSD": "0x0f1e10871e6a2D3A5Aa696b85b39d61a22A9e8C3", "aeroUSDCeUSD": "0x9216CD5cA133aBBd23cc6F873bB4a95A78032db0", - "aeroUSDzUSDC": "0x8AAdfbea33146e28170F99D031B7747EAa87DDD4" + "aeroWETHAERO": "0xaC23df4289e2FeaFb2851e63958C2AAF41DB4833", + "aeroMOGWETH": "0xfAa0cfc986041a3aF69207C6fCF35316D58fEA6a", + "meUSD": "0x0f1e10871e6a2D3A5Aa696b85b39d61a22A9e8C3", + "aeroUSDzUSDC": "0x8AAdfbea33146e28170F99D031B7747EAa87DDD4", + "aeroWETHcbBTC": "0x5ABe1A8b04A2428589bAeF9486d12688d482D061", + "aeroWETHWELL": "0x7df3541FFeDAA956aE6136E64253f3749b055Be6", + "aeroWETHDEGEN": "0xc8E72288f717Da55b7231365daE17AcD91b59705" }, "erc20s": { "COMP": "0x9e1028F5F1D5eDE59748FFceE5532509976840E0", @@ -28,9 +33,14 @@ "STG": "0xE3B53AF74a4BF62Ae5511055290838050bf764Df", "cUSDCv3": "0x53f1Df4E5591Ae35Bf738742981669c3767241FA", "wstETH": "0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452", - "meUSD": "0xbb819D845b573B5D7C538F5b85057160cfb5f313", "aeroUSDCeUSD": "0xDB5b8cead52f77De0f6B5255f73F348AAf2CBb8D", + "aeroWETHAERO": "0x65f2c1b253a3E45670aDD259C9688Edf1A3b814d", + "aeroMOGWETH": "0xfaAC26b279338dF8cF56B11A572617f674A2F69C", + "meUSD": "0xbb819D845b573B5D7C538F5b85057160cfb5f313", "AERO": "0x940181a94A35A4569E4529A3CDfB74e38FD98631", - "aeroUSDzUSDC": "0x246Df11B856E9fD6120494F168475e1b41321c61" + "aeroUSDzUSDC": "0x246Df11B856E9fD6120494F168475e1b41321c61", + "aeroWETHcbBTC": "0x4BD08a771CdAbA5333CAc6F20322eD7d72b6cBfA", + "aeroWETHWELL": "0x1F599F8657CAA38Ee825e4E2d64F695749E2a161", + "aeroWETHDEGEN": "0xA762F790a31654D9AeF7DE550A473A0F5621E4F1" } -} +} \ No newline at end of file diff --git a/scripts/addresses/8453-tmp-deployments.json b/scripts/addresses/8453-tmp-deployments.json index 7d32446fa..b34350b2b 100644 --- a/scripts/addresses/8453-tmp-deployments.json +++ b/scripts/addresses/8453-tmp-deployments.json @@ -37,4 +37,4 @@ "stRSR": "0x4Cf200D7fA568611DD8B4BD85053ba9419982C7D" } } -} \ No newline at end of file +} diff --git a/scripts/addresses/base-4.0.0/8453-tmp-assets-collateral.json b/scripts/addresses/base-4.0.0/8453-tmp-assets-collateral.json index c72e62328..36f009606 100644 --- a/scripts/addresses/base-4.0.0/8453-tmp-assets-collateral.json +++ b/scripts/addresses/base-4.0.0/8453-tmp-assets-collateral.json @@ -5,12 +5,22 @@ "collateral": { "meUSD": "0x0f1e10871e6a2D3A5Aa696b85b39d61a22A9e8C3", "aeroUSDCeUSD": "0x9216CD5cA133aBBd23cc6F873bB4a95A78032db0", - "aeroUSDzUSDC": "0x8AAdfbea33146e28170F99D031B7747EAa87DDD4" + "aeroUSDzUSDC": "0x8AAdfbea33146e28170F99D031B7747EAa87DDD4", + "aeroWETHAERO": "0xaC23df4289e2FeaFb2851e63958C2AAF41DB4833", + "aeroMOGWETH": "0xfAa0cfc986041a3aF69207C6fCF35316D58fEA6a", + "aeroWETHcbBTC": "0x5ABe1A8b04A2428589bAeF9486d12688d482D061", + "aeroWETHWELL": "0x7df3541FFeDAA956aE6136E64253f3749b055Be6", + "aeroWETHDEGEN": "0xc8E72288f717Da55b7231365daE17AcD91b59705" }, "erc20s": { "meUSD": "0xbb819D845b573B5D7C538F5b85057160cfb5f313", "aeroUSDCeUSD": "0xDB5b8cead52f77De0f6B5255f73F348AAf2CBb8D", "AERO": "0x940181a94A35A4569E4529A3CDfB74e38FD98631", - "aeroUSDzUSDC": "0x246Df11B856E9fD6120494F168475e1b41321c61" + "aeroUSDzUSDC": "0x246Df11B856E9fD6120494F168475e1b41321c61", + "aeroWETHAERO": "0x65f2c1b253a3E45670aDD259C9688Edf1A3b814d", + "aeroMOGWETH": "0xfaAC26b279338dF8cF56B11A572617f674A2F69C", + "aeroWETHcbBTC": "0x4BD08a771CdAbA5333CAc6F20322eD7d72b6cBfA", + "aeroWETHWELL": "0x1F599F8657CAA38Ee825e4E2d64F695749E2a161", + "aeroWETHDEGEN": "0xA762F790a31654D9AeF7DE550A473A0F5621E4F1" } } diff --git a/scripts/deploy.ts b/scripts/deploy.ts index 7123de77b..1d1199fe4 100644 --- a/scripts/deploy.ts +++ b/scripts/deploy.ts @@ -111,9 +111,14 @@ async function main() { 'phase2-assets/collaterals/deploy_lido_wsteth_collateral.ts', 'phase2-assets/collaterals/deploy_cbeth_collateral.ts', 'phase2-assets/assets/deploy_stg.ts', - 'phase2-assets/collaterals/deploy_morphoeUSD.ts', 'phase2-assets/collaterals/deploy_aerodrome_usdc_eusd.ts', - 'phase2-assets/collaterals/deploy_aerodrome_usdz_usdc.ts' + 'phase2-assets/collaterals/deploy_aerodrome_weth_aero.ts', + 'phase2-assets/collaterals/deploy_aerodrome_mog_weth.ts', + 'phase2-assets/collaterals/deploy_aerodrome_usdz_usdc.ts', + 'phase2-assets/collaterals/deploy_aerodrome_weth_cbbtc.ts', + 'phase2-assets/collaterals/deploy_aerodrome_weth_well.ts', + 'phase2-assets/collaterals/deploy_aerodrome_weth_degen.ts', + 'phase2-assets/collaterals/deploy_morphoeUSD.ts' ) } else if (chainId == '42161' || chainId == '421614') { // Arbitrum One diff --git a/scripts/deployment/phase2-assets/collaterals/deploy_aerodrome_mog_weth.ts b/scripts/deployment/phase2-assets/collaterals/deploy_aerodrome_mog_weth.ts new file mode 100644 index 000000000..bafaccd84 --- /dev/null +++ b/scripts/deployment/phase2-assets/collaterals/deploy_aerodrome_mog_weth.ts @@ -0,0 +1,140 @@ +import fs from 'fs' +import hre, { ethers } from 'hardhat' +import { getChainId } from '../../../../common/blockchain-utils' +import { baseL2Chains, networkConfig } from '../../../../common/configuration' +import { expect } from 'chai' +import { CollateralStatus, ONE_ADDRESS } from '../../../../common/constants' +import { + getDeploymentFile, + getAssetCollDeploymentFilename, + IAssetCollDeployments, + getDeploymentFilename, + fileExists, +} from '../../common' +import { + AerodromeVolatileCollateral, + AerodromeGaugeWrapper, + IAeroPool, +} from '../../../../typechain' +import { combinedError } from '../../utils' +import { + AerodromePoolType, + MAX_TRADE_VOL, + PRICE_TIMEOUT, + AERO_MOG_WETH_POOL, + AERO_MOG_WETH_GAUGE, + AERO, + MOG_USD_FEED, + MOG_ORACLE_TIMEOUT, + MOG_ORACLE_ERROR, + ETH_USD_FEED, + ETH_ORACLE_TIMEOUT, + ETH_ORACLE_ERROR, +} from '../../../../test/plugins/individual-collateral/aerodrome/constants' + +// Aerodrome volatile plugin: MOG-WETH vAMM + +async function main() { + // ==== Read Configuration ==== + const [deployer] = await hre.ethers.getSigners() + + const chainId = await getChainId(hre) + + console.log(`Deploying Collateral to network ${hre.network.name} (${chainId}) + with burner account: ${deployer.address}`) + + if (!networkConfig[chainId]) { + throw new Error(`Missing network configuration for ${hre.network.name}`) + } + + // Get phase1 deployment + const phase1File = getDeploymentFilename(chainId) + if (!fileExists(phase1File)) { + throw new Error(`${phase1File} doesn't exist yet. Run phase 1`) + } + // Check previous step completed + const assetCollDeploymentFilename = getAssetCollDeploymentFilename(chainId) + const assetCollDeployments = getDeploymentFile(assetCollDeploymentFilename) + + const deployedCollateral: string[] = [] + + /******** Deploy Aerodrome Volatile Pool for MOG-WETH **************************/ + + let collateral: AerodromeVolatileCollateral + let wMogWeth: AerodromeGaugeWrapper + + // Only for Base + if (baseL2Chains.includes(hre.network.name)) { + const AerodromeVolatileCollateralFactory = await hre.ethers.getContractFactory( + 'AerodromeVolatileCollateral' + ) + const AerodromeGaugeWrapperFactory = await ethers.getContractFactory('AerodromeGaugeWrapper') + + // Deploy gauge wrapper + const pool = await ethers.getContractAt('IAeroPool', AERO_MOG_WETH_POOL) + wMogWeth = ( + await AerodromeGaugeWrapperFactory.deploy( + pool.address, + 'w' + (await pool.name()), + 'w' + (await pool.symbol()), + AERO, + AERO_MOG_WETH_GAUGE + ) + ) + await wMogWeth.deployed() + + console.log( + `Deployed wrapper for Aerodrome Volatile MOG-WETH pool on ${hre.network.name} (${chainId}): ${wMogWeth.address} ` + ) + + const oracleError = combinedError(MOG_ORACLE_ERROR, ETH_ORACLE_ERROR) // 0.5% & 0.15% + + collateral = await AerodromeVolatileCollateralFactory.connect( + deployer + ).deploy( + { + erc20: wMogWeth.address, + targetName: ethers.utils.formatBytes32String('MOG_ETH_AERODROME_cpAMM'), + priceTimeout: PRICE_TIMEOUT, + chainlinkFeed: ONE_ADDRESS, // unused but cannot be zero + oracleError: oracleError.toString(), // unused but cannot be zero + oracleTimeout: MOG_ORACLE_TIMEOUT, // max of oracleTimeouts + maxTradeVolume: MAX_TRADE_VOL, + defaultThreshold: '0', + delayUntilDefault: '86400', // 24h + }, + { + pool: AERO_MOG_WETH_POOL, + poolType: AerodromePoolType.Volatile, + feeds: [[MOG_USD_FEED], [ETH_USD_FEED]], + oracleTimeouts: [[MOG_ORACLE_TIMEOUT], [ETH_ORACLE_TIMEOUT]], + oracleErrors: [[MOG_ORACLE_ERROR], [ETH_ORACLE_ERROR]], + } + ) + } else { + throw new Error(`Unsupported chainId: ${chainId}`) + } + + await collateral.deployed() + await (await collateral.refresh()).wait() + expect(await collateral.status()).to.equal(CollateralStatus.SOUND) + + console.log( + `Deployed Aerodrome Volatile Collateral for MOG-WETH to ${hre.network.name} (${chainId}): ${collateral.address}` + ) + + assetCollDeployments.collateral.aeroMOGWETH = collateral.address + assetCollDeployments.erc20s.aeroMOGWETH = wMogWeth.address + deployedCollateral.push(collateral.address.toString()) + + fs.writeFileSync(assetCollDeploymentFilename, JSON.stringify(assetCollDeployments, null, 2)) + + console.log(`Deployed collateral to ${hre.network.name} (${chainId}) + New deployments: ${deployedCollateral} + Deployment file: ${assetCollDeploymentFilename}`) +} + +main().catch((error) => { + console.error(error) + process.exitCode = 1 +}) diff --git a/scripts/deployment/phase2-assets/collaterals/deploy_aerodrome_weth_aero.ts b/scripts/deployment/phase2-assets/collaterals/deploy_aerodrome_weth_aero.ts new file mode 100644 index 000000000..010446fc9 --- /dev/null +++ b/scripts/deployment/phase2-assets/collaterals/deploy_aerodrome_weth_aero.ts @@ -0,0 +1,140 @@ +import fs from 'fs' +import hre, { ethers } from 'hardhat' +import { getChainId } from '../../../../common/blockchain-utils' +import { baseL2Chains, networkConfig } from '../../../../common/configuration' +import { expect } from 'chai' +import { CollateralStatus, ONE_ADDRESS } from '../../../../common/constants' +import { + getDeploymentFile, + getAssetCollDeploymentFilename, + IAssetCollDeployments, + getDeploymentFilename, + fileExists, +} from '../../common' +import { + AerodromeVolatileCollateral, + AerodromeGaugeWrapper, + IAeroPool, +} from '../../../../typechain' +import { combinedError } from '../../utils' +import { + AerodromePoolType, + MAX_TRADE_VOL, + PRICE_TIMEOUT, + AERO_WETH_AERO_POOL, + AERO_WETH_AERO_GAUGE, + AERO, + AERO_USD_FEED, + AERO_ORACLE_TIMEOUT, + AERO_ORACLE_ERROR, + ETH_USD_FEED, + ETH_ORACLE_TIMEOUT, + ETH_ORACLE_ERROR, +} from '../../../../test/plugins/individual-collateral/aerodrome/constants' + +// Aerodrome volatile plugin: WETH-AERO vAMM + +async function main() { + // ==== Read Configuration ==== + const [deployer] = await hre.ethers.getSigners() + + const chainId = await getChainId(hre) + + console.log(`Deploying Collateral to network ${hre.network.name} (${chainId}) + with burner account: ${deployer.address}`) + + if (!networkConfig[chainId]) { + throw new Error(`Missing network configuration for ${hre.network.name}`) + } + + // Get phase1 deployment + const phase1File = getDeploymentFilename(chainId) + if (!fileExists(phase1File)) { + throw new Error(`${phase1File} doesn't exist yet. Run phase 1`) + } + // Check previous step completed + const assetCollDeploymentFilename = getAssetCollDeploymentFilename(chainId) + const assetCollDeployments = getDeploymentFile(assetCollDeploymentFilename) + + const deployedCollateral: string[] = [] + + /******** Deploy Aerodrome Volatile Pool for WETH-AERO **************************/ + + let collateral: AerodromeVolatileCollateral + let wWethAero: AerodromeGaugeWrapper + + // Only for Base + if (baseL2Chains.includes(hre.network.name)) { + const AerodromeVolatileCollateralFactory = await hre.ethers.getContractFactory( + 'AerodromeVolatileCollateral' + ) + const AerodromeGaugeWrapperFactory = await ethers.getContractFactory('AerodromeGaugeWrapper') + + // Deploy gauge wrapper + const pool = await ethers.getContractAt('IAeroPool', AERO_WETH_AERO_POOL) + wWethAero = ( + await AerodromeGaugeWrapperFactory.deploy( + pool.address, + 'w' + (await pool.name()), + 'w' + (await pool.symbol()), + AERO, + AERO_WETH_AERO_GAUGE + ) + ) + await wWethAero.deployed() + + console.log( + `Deployed wrapper for Aerodrome Volatile WETH-AERO pool on ${hre.network.name} (${chainId}): ${wWethAero.address} ` + ) + + const oracleError = combinedError(AERO_ORACLE_ERROR, ETH_ORACLE_ERROR) // 0.5% & 0.15% + + collateral = await AerodromeVolatileCollateralFactory.connect( + deployer + ).deploy( + { + erc20: wWethAero.address, + targetName: ethers.utils.formatBytes32String('ETH_AERO_AERODROME_cpAMM'), + priceTimeout: PRICE_TIMEOUT, + chainlinkFeed: ONE_ADDRESS, // unused but cannot be zero + oracleError: oracleError.toString(), // unused but cannot be zero + oracleTimeout: AERO_ORACLE_TIMEOUT, // max of oracleTimeouts + maxTradeVolume: MAX_TRADE_VOL, + defaultThreshold: '0', + delayUntilDefault: '86400', // 24h + }, + { + pool: AERO_WETH_AERO_POOL, + poolType: AerodromePoolType.Volatile, + feeds: [[ETH_USD_FEED], [AERO_USD_FEED]], + oracleTimeouts: [[ETH_ORACLE_TIMEOUT], [AERO_ORACLE_TIMEOUT]], + oracleErrors: [[ETH_ORACLE_ERROR], [AERO_ORACLE_ERROR]], + } + ) + } else { + throw new Error(`Unsupported chainId: ${chainId}`) + } + + await collateral.deployed() + await (await collateral.refresh()).wait() + expect(await collateral.status()).to.equal(CollateralStatus.SOUND) + + console.log( + `Deployed Aerodrome Volatile Collateral for WETH-AERO to ${hre.network.name} (${chainId}): ${collateral.address}` + ) + + assetCollDeployments.collateral.aeroWETHAERO = collateral.address + assetCollDeployments.erc20s.aeroWETHAERO = wWethAero.address + deployedCollateral.push(collateral.address.toString()) + + fs.writeFileSync(assetCollDeploymentFilename, JSON.stringify(assetCollDeployments, null, 2)) + + console.log(`Deployed collateral to ${hre.network.name} (${chainId}) + New deployments: ${deployedCollateral} + Deployment file: ${assetCollDeploymentFilename}`) +} + +main().catch((error) => { + console.error(error) + process.exitCode = 1 +}) diff --git a/scripts/deployment/phase2-assets/collaterals/deploy_aerodrome_weth_cbbtc.ts b/scripts/deployment/phase2-assets/collaterals/deploy_aerodrome_weth_cbbtc.ts new file mode 100644 index 000000000..e55b10562 --- /dev/null +++ b/scripts/deployment/phase2-assets/collaterals/deploy_aerodrome_weth_cbbtc.ts @@ -0,0 +1,140 @@ +import fs from 'fs' +import hre, { ethers } from 'hardhat' +import { getChainId } from '../../../../common/blockchain-utils' +import { baseL2Chains, networkConfig } from '../../../../common/configuration' +import { expect } from 'chai' +import { CollateralStatus, ONE_ADDRESS } from '../../../../common/constants' +import { + getDeploymentFile, + getAssetCollDeploymentFilename, + IAssetCollDeployments, + getDeploymentFilename, + fileExists, +} from '../../common' +import { + AerodromeVolatileCollateral, + AerodromeGaugeWrapper, + IAeroPool, +} from '../../../../typechain' +import { combinedError } from '../../utils' +import { + AerodromePoolType, + MAX_TRADE_VOL, + PRICE_TIMEOUT, + AERO_WETH_cbBTC_POOL, + AERO_WETH_cbBTC_GAUGE, + AERO, + cbBTC_USD_FEED, + cbBTC_ORACLE_TIMEOUT, + cbBTC_ORACLE_ERROR, + ETH_USD_FEED, + ETH_ORACLE_TIMEOUT, + ETH_ORACLE_ERROR, +} from '../../../../test/plugins/individual-collateral/aerodrome/constants' + +// Aerodrome volatile plugin: WETH-cbBTCvAMM + +async function main() { + // ==== Read Configuration ==== + const [deployer] = await hre.ethers.getSigners() + + const chainId = await getChainId(hre) + + console.log(`Deploying Collateral to network ${hre.network.name} (${chainId}) + with burner account: ${deployer.address}`) + + if (!networkConfig[chainId]) { + throw new Error(`Missing network configuration for ${hre.network.name}`) + } + + // Get phase1 deployment + const phase1File = getDeploymentFilename(chainId) + if (!fileExists(phase1File)) { + throw new Error(`${phase1File} doesn't exist yet. Run phase 1`) + } + // Check previous step completed + const assetCollDeploymentFilename = getAssetCollDeploymentFilename(chainId) + const assetCollDeployments = getDeploymentFile(assetCollDeploymentFilename) + + const deployedCollateral: string[] = [] + + /******** Deploy Aerodrome Volatile Pool for WETH-cbBTC **************************/ + + let collateral: AerodromeVolatileCollateral + let wWethcbBTC: AerodromeGaugeWrapper + + // Only for Base + if (baseL2Chains.includes(hre.network.name)) { + const AerodromeVolatileCollateralFactory = await hre.ethers.getContractFactory( + 'AerodromeVolatileCollateral' + ) + const AerodromeGaugeWrapperFactory = await ethers.getContractFactory('AerodromeGaugeWrapper') + + // Deploy gauge wrapper + const pool = await ethers.getContractAt('IAeroPool', AERO_WETH_cbBTC_POOL) + wWethcbBTC = ( + await AerodromeGaugeWrapperFactory.deploy( + pool.address, + 'w' + (await pool.name()), + 'w' + (await pool.symbol()), + AERO, + AERO_WETH_cbBTC_GAUGE + ) + ) + await wWethcbBTC.deployed() + + console.log( + `Deployed wrapper for Aerodrome Volatile WETH-cbBTC pool on ${hre.network.name} (${chainId}): ${wWethcbBTC.address} ` + ) + + const oracleError = combinedError(cbBTC_ORACLE_ERROR, ETH_ORACLE_ERROR) // 0.3% & 0.15% + + collateral = await AerodromeVolatileCollateralFactory.connect( + deployer + ).deploy( + { + erc20: wWethcbBTC.address, + targetName: ethers.utils.formatBytes32String('ETH_BTC_AERODROME_cpAMM'), + priceTimeout: PRICE_TIMEOUT, + chainlinkFeed: ONE_ADDRESS, // unused but cannot be zero + oracleError: oracleError.toString(), // unused but cannot be zero + oracleTimeout: ETH_ORACLE_TIMEOUT, // max of oracleTimeouts + maxTradeVolume: MAX_TRADE_VOL, + defaultThreshold: '0', + delayUntilDefault: '86400', // 24h + }, + { + pool: AERO_WETH_cbBTC_POOL, + poolType: AerodromePoolType.Volatile, + feeds: [[ETH_USD_FEED], [cbBTC_USD_FEED]], + oracleTimeouts: [[ETH_ORACLE_TIMEOUT], [cbBTC_ORACLE_TIMEOUT]], + oracleErrors: [[ETH_ORACLE_ERROR], [cbBTC_ORACLE_ERROR]], + } + ) + } else { + throw new Error(`Unsupported chainId: ${chainId}`) + } + + await collateral.deployed() + await (await collateral.refresh()).wait() + expect(await collateral.status()).to.equal(CollateralStatus.SOUND) + + console.log( + `Deployed Aerodrome Volatile Collateral for WETH-cbBTC to ${hre.network.name} (${chainId}): ${collateral.address}` + ) + + assetCollDeployments.collateral.aeroWETHcbBTC = collateral.address + assetCollDeployments.erc20s.aeroWETHcbBTC = wWethcbBTC.address + deployedCollateral.push(collateral.address.toString()) + + fs.writeFileSync(assetCollDeploymentFilename, JSON.stringify(assetCollDeployments, null, 2)) + + console.log(`Deployed collateral to ${hre.network.name} (${chainId}) + New deployments: ${deployedCollateral} + Deployment file: ${assetCollDeploymentFilename}`) +} + +main().catch((error) => { + console.error(error) + process.exitCode = 1 +}) diff --git a/scripts/deployment/phase2-assets/collaterals/deploy_aerodrome_weth_degen.ts b/scripts/deployment/phase2-assets/collaterals/deploy_aerodrome_weth_degen.ts new file mode 100644 index 000000000..cb48b58ae --- /dev/null +++ b/scripts/deployment/phase2-assets/collaterals/deploy_aerodrome_weth_degen.ts @@ -0,0 +1,140 @@ +import fs from 'fs' +import hre, { ethers } from 'hardhat' +import { getChainId } from '../../../../common/blockchain-utils' +import { baseL2Chains, networkConfig } from '../../../../common/configuration' +import { expect } from 'chai' +import { CollateralStatus, ONE_ADDRESS } from '../../../../common/constants' +import { + getDeploymentFile, + getAssetCollDeploymentFilename, + IAssetCollDeployments, + getDeploymentFilename, + fileExists, +} from '../../common' +import { + AerodromeVolatileCollateral, + AerodromeGaugeWrapper, + IAeroPool, +} from '../../../../typechain' +import { combinedError } from '../../utils' +import { + AerodromePoolType, + MAX_TRADE_VOL, + PRICE_TIMEOUT, + AERO_WETH_DEGEN_POOL, + AERO_WETH_DEGEN_GAUGE, + AERO, + DEGEN_USD_FEED, + DEGEN_ORACLE_TIMEOUT, + DEGEN_ORACLE_ERROR, + ETH_USD_FEED, + ETH_ORACLE_TIMEOUT, + ETH_ORACLE_ERROR, +} from '../../../../test/plugins/individual-collateral/aerodrome/constants' + +// Aerodrome volatile plugin: WETH-DEGEN vAMM + +async function main() { + // ==== Read Configuration ==== + const [deployer] = await hre.ethers.getSigners() + + const chainId = await getChainId(hre) + + console.log(`Deploying Collateral to network ${hre.network.name} (${chainId}) + with burner account: ${deployer.address}`) + + if (!networkConfig[chainId]) { + throw new Error(`Missing network configuration for ${hre.network.name}`) + } + + // Get phase1 deployment + const phase1File = getDeploymentFilename(chainId) + if (!fileExists(phase1File)) { + throw new Error(`${phase1File} doesn't exist yet. Run phase 1`) + } + // Check previous step completed + const assetCollDeploymentFilename = getAssetCollDeploymentFilename(chainId) + const assetCollDeployments = getDeploymentFile(assetCollDeploymentFilename) + + const deployedCollateral: string[] = [] + + /******** Deploy Aerodrome Volatile Pool for WETH-DEGEN **************************/ + + let collateral: AerodromeVolatileCollateral + let wWethDEGEN: AerodromeGaugeWrapper + + // Only for Base + if (baseL2Chains.includes(hre.network.name)) { + const AerodromeVolatileCollateralFactory = await hre.ethers.getContractFactory( + 'AerodromeVolatileCollateral' + ) + const AerodromeGaugeWrapperFactory = await ethers.getContractFactory('AerodromeGaugeWrapper') + + // Deploy gauge wrapper + const pool = await ethers.getContractAt('IAeroPool', AERO_WETH_DEGEN_POOL) + wWethDEGEN = ( + await AerodromeGaugeWrapperFactory.deploy( + pool.address, + 'w' + (await pool.name()), + 'w' + (await pool.symbol()), + AERO, + AERO_WETH_DEGEN_GAUGE + ) + ) + await wWethDEGEN.deployed() + + console.log( + `Deployed wrapper for Aerodrome Volatile WETH-DEGEN pool on ${hre.network.name} (${chainId}): ${wWethDEGEN.address} ` + ) + + const oracleError = combinedError(DEGEN_ORACLE_ERROR, ETH_ORACLE_ERROR) // 0.5% & 0.15% + + collateral = await AerodromeVolatileCollateralFactory.connect( + deployer + ).deploy( + { + erc20: wWethDEGEN.address, + targetName: ethers.utils.formatBytes32String('ETH_DEGEN_AERODROME_cpAMM'), + priceTimeout: PRICE_TIMEOUT, + chainlinkFeed: ONE_ADDRESS, // unused but cannot be zero + oracleError: oracleError.toString(), // unused but cannot be zero + oracleTimeout: DEGEN_ORACLE_TIMEOUT, // max of oracleTimeouts + maxTradeVolume: MAX_TRADE_VOL, + defaultThreshold: '0', + delayUntilDefault: '86400', // 24h + }, + { + pool: AERO_WETH_DEGEN_POOL, + poolType: AerodromePoolType.Volatile, + feeds: [[ETH_USD_FEED], [DEGEN_USD_FEED]], + oracleTimeouts: [[ETH_ORACLE_TIMEOUT], [DEGEN_ORACLE_TIMEOUT]], + oracleErrors: [[ETH_ORACLE_ERROR], [DEGEN_ORACLE_ERROR]], + } + ) + } else { + throw new Error(`Unsupported chainId: ${chainId}`) + } + + await collateral.deployed() + await (await collateral.refresh()).wait() + expect(await collateral.status()).to.equal(CollateralStatus.SOUND) + + console.log( + `Deployed Aerodrome Volatile Collateral for WETH-DEGEN to ${hre.network.name} (${chainId}): ${collateral.address}` + ) + + assetCollDeployments.collateral.aeroWETHDEGEN = collateral.address + assetCollDeployments.erc20s.aeroWETHDEGEN = wWethDEGEN.address + deployedCollateral.push(collateral.address.toString()) + + fs.writeFileSync(assetCollDeploymentFilename, JSON.stringify(assetCollDeployments, null, 2)) + + console.log(`Deployed collateral to ${hre.network.name} (${chainId}) + New deployments: ${deployedCollateral} + Deployment file: ${assetCollDeploymentFilename}`) +} + +main().catch((error) => { + console.error(error) + process.exitCode = 1 +}) diff --git a/scripts/deployment/phase2-assets/collaterals/deploy_aerodrome_weth_well.ts b/scripts/deployment/phase2-assets/collaterals/deploy_aerodrome_weth_well.ts new file mode 100644 index 000000000..624b0961f --- /dev/null +++ b/scripts/deployment/phase2-assets/collaterals/deploy_aerodrome_weth_well.ts @@ -0,0 +1,140 @@ +import fs from 'fs' +import hre, { ethers } from 'hardhat' +import { getChainId } from '../../../../common/blockchain-utils' +import { baseL2Chains, networkConfig } from '../../../../common/configuration' +import { expect } from 'chai' +import { CollateralStatus, ONE_ADDRESS } from '../../../../common/constants' +import { + getDeploymentFile, + getAssetCollDeploymentFilename, + IAssetCollDeployments, + getDeploymentFilename, + fileExists, +} from '../../common' +import { + AerodromeVolatileCollateral, + AerodromeGaugeWrapper, + IAeroPool, +} from '../../../../typechain' +import { combinedError } from '../../utils' +import { + AerodromePoolType, + MAX_TRADE_VOL, + PRICE_TIMEOUT, + AERO_WETH_WELL_POOL, + AERO_WETH_WELL_GAUGE, + AERO, + WELL_USD_FEED, + WELL_ORACLE_TIMEOUT, + WELL_ORACLE_ERROR, + ETH_USD_FEED, + ETH_ORACLE_TIMEOUT, + ETH_ORACLE_ERROR, +} from '../../../../test/plugins/individual-collateral/aerodrome/constants' + +// Aerodrome volatile plugin: WETH-WELL vAMM + +async function main() { + // ==== Read Configuration ==== + const [deployer] = await hre.ethers.getSigners() + + const chainId = await getChainId(hre) + + console.log(`Deploying Collateral to network ${hre.network.name} (${chainId}) + with burner account: ${deployer.address}`) + + if (!networkConfig[chainId]) { + throw new Error(`Missing network configuration for ${hre.network.name}`) + } + + // Get phase1 deployment + const phase1File = getDeploymentFilename(chainId) + if (!fileExists(phase1File)) { + throw new Error(`${phase1File} doesn't exist yet. Run phase 1`) + } + // Check previous step completed + const assetCollDeploymentFilename = getAssetCollDeploymentFilename(chainId) + const assetCollDeployments = getDeploymentFile(assetCollDeploymentFilename) + + const deployedCollateral: string[] = [] + + /******** Deploy Aerodrome Volatile Pool for WETH-WELL **************************/ + + let collateral: AerodromeVolatileCollateral + let wWethWELL: AerodromeGaugeWrapper + + // Only for Base + if (baseL2Chains.includes(hre.network.name)) { + const AerodromeVolatileCollateralFactory = await hre.ethers.getContractFactory( + 'AerodromeVolatileCollateral' + ) + const AerodromeGaugeWrapperFactory = await ethers.getContractFactory('AerodromeGaugeWrapper') + + // Deploy gauge wrapper + const pool = await ethers.getContractAt('IAeroPool', AERO_WETH_WELL_POOL) + wWethWELL = ( + await AerodromeGaugeWrapperFactory.deploy( + pool.address, + 'w' + (await pool.name()), + 'w' + (await pool.symbol()), + AERO, + AERO_WETH_WELL_GAUGE + ) + ) + await wWethWELL.deployed() + + console.log( + `Deployed wrapper for Aerodrome Volatile WETH-WELL pool on ${hre.network.name} (${chainId}): ${wWethWELL.address} ` + ) + + const oracleError = combinedError(WELL_ORACLE_ERROR, ETH_ORACLE_ERROR) // 0.5% & 0.15% + + collateral = await AerodromeVolatileCollateralFactory.connect( + deployer + ).deploy( + { + erc20: wWethWELL.address, + targetName: ethers.utils.formatBytes32String('ETH_WELL_AERODROME_cpAMM'), + priceTimeout: PRICE_TIMEOUT, + chainlinkFeed: ONE_ADDRESS, // unused but cannot be zero + oracleError: oracleError.toString(), // unused but cannot be zero + oracleTimeout: WELL_ORACLE_TIMEOUT, // max of oracleTimeouts + maxTradeVolume: MAX_TRADE_VOL, + defaultThreshold: '0', + delayUntilDefault: '86400', // 24h + }, + { + pool: AERO_WETH_WELL_POOL, + poolType: AerodromePoolType.Volatile, + feeds: [[ETH_USD_FEED], [WELL_USD_FEED]], + oracleTimeouts: [[ETH_ORACLE_TIMEOUT], [WELL_ORACLE_TIMEOUT]], + oracleErrors: [[ETH_ORACLE_ERROR], [WELL_ORACLE_ERROR]], + } + ) + } else { + throw new Error(`Unsupported chainId: ${chainId}`) + } + + await collateral.deployed() + await (await collateral.refresh()).wait() + expect(await collateral.status()).to.equal(CollateralStatus.SOUND) + + console.log( + `Deployed Aerodrome Volatile Collateral for WETH-WELL to ${hre.network.name} (${chainId}): ${collateral.address}` + ) + + assetCollDeployments.collateral.aeroWETHWELL = collateral.address + assetCollDeployments.erc20s.aeroWETHWELL = wWethWELL.address + deployedCollateral.push(collateral.address.toString()) + + fs.writeFileSync(assetCollDeploymentFilename, JSON.stringify(assetCollDeployments, null, 2)) + + console.log(`Deployed collateral to ${hre.network.name} (${chainId}) + New deployments: ${deployedCollateral} + Deployment file: ${assetCollDeploymentFilename}`) +} + +main().catch((error) => { + console.error(error) + process.exitCode = 1 +}) diff --git a/scripts/verification/collateral-plugins/verify_aerodrome_mog_weth.ts b/scripts/verification/collateral-plugins/verify_aerodrome_mog_weth.ts new file mode 100644 index 000000000..c3841c355 --- /dev/null +++ b/scripts/verification/collateral-plugins/verify_aerodrome_mog_weth.ts @@ -0,0 +1,101 @@ +import hre, { ethers } from 'hardhat' +import { getChainId } from '../../../common/blockchain-utils' +import { baseL2Chains, developmentChains, networkConfig } from '../../../common/configuration' +import { ONE_ADDRESS } from '../../../common/constants' +import { + getDeploymentFile, + getAssetCollDeploymentFilename, + IAssetCollDeployments, +} from '../../deployment/common' +import { verifyContract } from '../../deployment/utils' +import { combinedError } from '../../deployment/utils' +import { IAeroPool } from '@typechain/IAeroPool' +import { + AerodromePoolType, + MAX_TRADE_VOL, + PRICE_TIMEOUT, + AERO_MOG_WETH_POOL, + AERO_MOG_WETH_GAUGE, + AERO, + MOG_ORACLE_TIMEOUT, + MOG_ORACLE_ERROR, + MOG_USD_FEED, + AERO_ORACLE_ERROR, + ETH_USD_FEED, + ETH_ORACLE_TIMEOUT, + ETH_ORACLE_ERROR, +} from '../../../test/plugins/individual-collateral/aerodrome/constants' + +let deployments: IAssetCollDeployments + +async function main() { + // ********** Read config ********** + const chainId = await getChainId(hre) + if (!networkConfig[chainId]) { + throw new Error(`Missing network configuration for ${hre.network.name}`) + } + + if (developmentChains.includes(hre.network.name)) { + throw new Error(`Cannot verify contracts for development chain ${hre.network.name}`) + } + + const assetCollDeploymentFilename = getAssetCollDeploymentFilename(chainId) + deployments = getDeploymentFile(assetCollDeploymentFilename) + + // Only on base, aways use wrapper + if (baseL2Chains.includes(hre.network.name)) { + const aeroMOGWETHPoolCollateral = await ethers.getContractAt( + 'AerodromeVolatileCollateral', + deployments.collateral.aeroMOGWETH as string + ) + + /******** Verify Gauge Wrapper **************************/ + + const pool = await ethers.getContractAt('IAeroPool', AERO_MOG_WETH_POOL) + await verifyContract( + chainId, + await aeroMOGWETHPoolCollateral.erc20(), + [ + pool.address, + 'w' + (await pool.name()), + 'w' + (await pool.symbol()), + AERO, + AERO_MOG_WETH_GAUGE, + ], + 'contracts/plugins/assets/aerodrome/AerodromeGaugeWrapper.sol:AerodromeGaugeWrapper' + ) + + /******** Verify WETH-AERO plugin **************************/ + const oracleError = combinedError(AERO_ORACLE_ERROR, ETH_ORACLE_ERROR) // 0.5% & 0.15% + await verifyContract( + chainId, + deployments.collateral.aeroMOGWETH, + [ + { + erc20: await aeroMOGWETHPoolCollateral.erc20(), + targetName: await aeroMOGWETHPoolCollateral.targetName(), + priceTimeout: PRICE_TIMEOUT, + chainlinkFeed: ONE_ADDRESS, // unused but cannot be zero + oracleError: oracleError.toString(), + oracleTimeout: MOG_ORACLE_TIMEOUT, // max of oracleTimeouts + maxTradeVolume: MAX_TRADE_VOL, + defaultThreshold: '0', + delayUntilDefault: '86400', + }, + { + pool: AERO_MOG_WETH_POOL, + poolType: AerodromePoolType.Volatile, + feeds: [[MOG_USD_FEED], [ETH_USD_FEED]], + oracleTimeouts: [[MOG_ORACLE_TIMEOUT], [ETH_ORACLE_TIMEOUT]], + oracleErrors: [[MOG_ORACLE_ERROR], [ETH_ORACLE_ERROR]], + }, + ], + 'contracts/plugins/assets/aerodrome/AerodromeVolatileCollateral.sol:AerodromeVolatileCollateral' + ) + } +} + +main().catch((error) => { + console.error(error) + process.exitCode = 1 +}) diff --git a/scripts/verification/collateral-plugins/verify_aerodrome_weth_aero.ts b/scripts/verification/collateral-plugins/verify_aerodrome_weth_aero.ts new file mode 100644 index 000000000..5d6f45cb4 --- /dev/null +++ b/scripts/verification/collateral-plugins/verify_aerodrome_weth_aero.ts @@ -0,0 +1,100 @@ +import hre, { ethers } from 'hardhat' +import { getChainId } from '../../../common/blockchain-utils' +import { baseL2Chains, developmentChains, networkConfig } from '../../../common/configuration' +import { ONE_ADDRESS } from '../../../common/constants' +import { + getDeploymentFile, + getAssetCollDeploymentFilename, + IAssetCollDeployments, +} from '../../deployment/common' +import { verifyContract } from '../../deployment/utils' +import { combinedError } from '../../deployment/utils' +import { IAeroPool } from '@typechain/IAeroPool' +import { + AerodromePoolType, + MAX_TRADE_VOL, + PRICE_TIMEOUT, + AERO_WETH_AERO_POOL, + AERO_WETH_AERO_GAUGE, + AERO, + AERO_ORACLE_TIMEOUT, + AERO_USD_FEED, + AERO_ORACLE_ERROR, + ETH_USD_FEED, + ETH_ORACLE_TIMEOUT, + ETH_ORACLE_ERROR, +} from '../../../test/plugins/individual-collateral/aerodrome/constants' + +let deployments: IAssetCollDeployments + +async function main() { + // ********** Read config ********** + const chainId = await getChainId(hre) + if (!networkConfig[chainId]) { + throw new Error(`Missing network configuration for ${hre.network.name}`) + } + + if (developmentChains.includes(hre.network.name)) { + throw new Error(`Cannot verify contracts for development chain ${hre.network.name}`) + } + + const assetCollDeploymentFilename = getAssetCollDeploymentFilename(chainId) + deployments = getDeploymentFile(assetCollDeploymentFilename) + + // Only on base, aways use wrapper + if (baseL2Chains.includes(hre.network.name)) { + const aeroWETHAEROPoolCollateral = await ethers.getContractAt( + 'AerodromeVolatileCollateral', + deployments.collateral.aeroWETHAERO as string + ) + + /******** Verify Gauge Wrapper **************************/ + + const pool = await ethers.getContractAt('IAeroPool', AERO_WETH_AERO_POOL) + await verifyContract( + chainId, + await aeroWETHAEROPoolCollateral.erc20(), + [ + pool.address, + 'w' + (await pool.name()), + 'w' + (await pool.symbol()), + AERO, + AERO_WETH_AERO_GAUGE, + ], + 'contracts/plugins/assets/aerodrome/AerodromeGaugeWrapper.sol:AerodromeGaugeWrapper' + ) + + /******** Verify WETH-AERO plugin **************************/ + const oracleError = combinedError(AERO_ORACLE_ERROR, ETH_ORACLE_ERROR) // 0.5% & 0.15% + await verifyContract( + chainId, + deployments.collateral.aeroWETHAERO, + [ + { + erc20: await aeroWETHAEROPoolCollateral.erc20(), + targetName: await aeroWETHAEROPoolCollateral.targetName(), + priceTimeout: PRICE_TIMEOUT, + chainlinkFeed: ONE_ADDRESS, // unused but cannot be zero + oracleError: oracleError.toString(), + oracleTimeout: AERO_ORACLE_TIMEOUT, // max of oracleTimeouts + maxTradeVolume: MAX_TRADE_VOL, + defaultThreshold: '0', + delayUntilDefault: '86400', + }, + { + pool: AERO_WETH_AERO_POOL, + poolType: AerodromePoolType.Volatile, + feeds: [[ETH_USD_FEED], [AERO_USD_FEED]], + oracleTimeouts: [[ETH_ORACLE_TIMEOUT], [AERO_ORACLE_TIMEOUT]], + oracleErrors: [[ETH_ORACLE_ERROR], [AERO_ORACLE_ERROR]], + }, + ], + 'contracts/plugins/assets/aerodrome/AerodromeVolatileCollateral.sol:AerodromeVolatileCollateral' + ) + } +} + +main().catch((error) => { + console.error(error) + process.exitCode = 1 +}) diff --git a/scripts/verification/collateral-plugins/verify_aerodrome_weth_cbbtc.ts b/scripts/verification/collateral-plugins/verify_aerodrome_weth_cbbtc.ts new file mode 100644 index 000000000..061714f4f --- /dev/null +++ b/scripts/verification/collateral-plugins/verify_aerodrome_weth_cbbtc.ts @@ -0,0 +1,100 @@ +import hre, { ethers } from 'hardhat' +import { getChainId } from '../../../common/blockchain-utils' +import { baseL2Chains, developmentChains, networkConfig } from '../../../common/configuration' +import { ONE_ADDRESS } from '../../../common/constants' +import { + getDeploymentFile, + getAssetCollDeploymentFilename, + IAssetCollDeployments, +} from '../../deployment/common' +import { verifyContract } from '../../deployment/utils' +import { combinedError } from '../../deployment/utils' +import { IAeroPool } from '@typechain/IAeroPool' +import { + AerodromePoolType, + MAX_TRADE_VOL, + PRICE_TIMEOUT, + AERO_WETH_cbBTC_POOL, + AERO_WETH_cbBTC_GAUGE, + AERO, + cbBTC_ORACLE_TIMEOUT, + cbBTC_USD_FEED, + cbBTC_ORACLE_ERROR, + ETH_USD_FEED, + ETH_ORACLE_TIMEOUT, + ETH_ORACLE_ERROR, +} from '../../../test/plugins/individual-collateral/aerodrome/constants' + +let deployments: IAssetCollDeployments + +async function main() { + // ********** Read config ********** + const chainId = await getChainId(hre) + if (!networkConfig[chainId]) { + throw new Error(`Missing network configuration for ${hre.network.name}`) + } + + if (developmentChains.includes(hre.network.name)) { + throw new Error(`Cannot verify contracts for development chain ${hre.network.name}`) + } + + const assetCollDeploymentFilename = getAssetCollDeploymentFilename(chainId) + deployments = getDeploymentFile(assetCollDeploymentFilename) + + // Only on base, aways use wrapper + if (baseL2Chains.includes(hre.network.name)) { + const aeroWETHcbBTCPoolCollateral = await ethers.getContractAt( + 'AerodromeVolatileCollateral', + deployments.collateral.aeroWETHcbBTC as string + ) + + /******** Verify Gauge Wrapper **************************/ + + const pool = await ethers.getContractAt('IAeroPool', AERO_WETH_cbBTC_POOL) + await verifyContract( + chainId, + await aeroWETHcbBTCPoolCollateral.erc20(), + [ + pool.address, + 'w' + (await pool.name()), + 'w' + (await pool.symbol()), + AERO, + AERO_WETH_cbBTC_GAUGE, + ], + 'contracts/plugins/assets/aerodrome/AerodromeGaugeWrapper.sol:AerodromeGaugeWrapper' + ) + + /******** Verify WETH-cbBTC plugin **************************/ + const oracleError = combinedError(cbBTC_ORACLE_ERROR, ETH_ORACLE_ERROR) // 0.3% & 0.15% + await verifyContract( + chainId, + deployments.collateral.aeroWETHcbBTC, + [ + { + erc20: await aeroWETHcbBTCPoolCollateral.erc20(), + targetName: await aeroWETHcbBTCPoolCollateral.targetName(), + priceTimeout: PRICE_TIMEOUT, + chainlinkFeed: ONE_ADDRESS, // unused but cannot be zero + oracleError: oracleError.toString(), + oracleTimeout: ETH_ORACLE_TIMEOUT, // max of oracleTimeouts + maxTradeVolume: MAX_TRADE_VOL, + defaultThreshold: '0', + delayUntilDefault: '86400', + }, + { + pool: AERO_WETH_cbBTC_POOL, + poolType: AerodromePoolType.Volatile, + feeds: [[ETH_USD_FEED], [cbBTC_USD_FEED]], + oracleTimeouts: [[ETH_ORACLE_TIMEOUT], [cbBTC_ORACLE_TIMEOUT]], + oracleErrors: [[ETH_ORACLE_ERROR], [cbBTC_ORACLE_ERROR]], + }, + ], + 'contracts/plugins/assets/aerodrome/AerodromeVolatileCollateral.sol:AerodromeVolatileCollateral' + ) + } +} + +main().catch((error) => { + console.error(error) + process.exitCode = 1 +}) diff --git a/scripts/verification/collateral-plugins/verify_aerodrome_weth_degen.ts b/scripts/verification/collateral-plugins/verify_aerodrome_weth_degen.ts new file mode 100644 index 000000000..533ef4d1e --- /dev/null +++ b/scripts/verification/collateral-plugins/verify_aerodrome_weth_degen.ts @@ -0,0 +1,100 @@ +import hre, { ethers } from 'hardhat' +import { getChainId } from '../../../common/blockchain-utils' +import { baseL2Chains, developmentChains, networkConfig } from '../../../common/configuration' +import { ONE_ADDRESS } from '../../../common/constants' +import { + getDeploymentFile, + getAssetCollDeploymentFilename, + IAssetCollDeployments, +} from '../../deployment/common' +import { verifyContract } from '../../deployment/utils' +import { combinedError } from '../../deployment/utils' +import { IAeroPool } from '@typechain/IAeroPool' +import { + AerodromePoolType, + MAX_TRADE_VOL, + PRICE_TIMEOUT, + AERO_WETH_DEGEN_POOL, + AERO_WETH_DEGEN_GAUGE, + AERO, + DEGEN_ORACLE_TIMEOUT, + DEGEN_USD_FEED, + DEGEN_ORACLE_ERROR, + ETH_USD_FEED, + ETH_ORACLE_TIMEOUT, + ETH_ORACLE_ERROR, +} from '../../../test/plugins/individual-collateral/aerodrome/constants' + +let deployments: IAssetCollDeployments + +async function main() { + // ********** Read config ********** + const chainId = await getChainId(hre) + if (!networkConfig[chainId]) { + throw new Error(`Missing network configuration for ${hre.network.name}`) + } + + if (developmentChains.includes(hre.network.name)) { + throw new Error(`Cannot verify contracts for development chain ${hre.network.name}`) + } + + const assetCollDeploymentFilename = getAssetCollDeploymentFilename(chainId) + deployments = getDeploymentFile(assetCollDeploymentFilename) + + // Only on base, aways use wrapper + if (baseL2Chains.includes(hre.network.name)) { + const aeroWETHDEGENPoolCollateral = await ethers.getContractAt( + 'AerodromeVolatileCollateral', + deployments.collateral.aeroWETHDEGEN as string + ) + + /******** Verify Gauge Wrapper **************************/ + + const pool = await ethers.getContractAt('IAeroPool', AERO_WETH_DEGEN_POOL) + await verifyContract( + chainId, + await aeroWETHDEGENPoolCollateral.erc20(), + [ + pool.address, + 'w' + (await pool.name()), + 'w' + (await pool.symbol()), + AERO, + AERO_WETH_DEGEN_GAUGE, + ], + 'contracts/plugins/assets/aerodrome/AerodromeGaugeWrapper.sol:AerodromeGaugeWrapper' + ) + + /******** Verify WETH-DEGEN plugin **************************/ + const oracleError = combinedError(DEGEN_ORACLE_ERROR, ETH_ORACLE_ERROR) // 0.5% & 0.15% + await verifyContract( + chainId, + deployments.collateral.aeroWETHDEGEN, + [ + { + erc20: await aeroWETHDEGENPoolCollateral.erc20(), + targetName: await aeroWETHDEGENPoolCollateral.targetName(), + priceTimeout: PRICE_TIMEOUT, + chainlinkFeed: ONE_ADDRESS, // unused but cannot be zero + oracleError: oracleError.toString(), + oracleTimeout: DEGEN_ORACLE_TIMEOUT, // max of oracleTimeouts + maxTradeVolume: MAX_TRADE_VOL, + defaultThreshold: '0', + delayUntilDefault: '86400', + }, + { + pool: AERO_WETH_DEGEN_POOL, + poolType: AerodromePoolType.Volatile, + feeds: [[ETH_USD_FEED], [DEGEN_USD_FEED]], + oracleTimeouts: [[ETH_ORACLE_TIMEOUT], [DEGEN_ORACLE_TIMEOUT]], + oracleErrors: [[ETH_ORACLE_ERROR], [DEGEN_ORACLE_ERROR]], + }, + ], + 'contracts/plugins/assets/aerodrome/AerodromeVolatileCollateral.sol:AerodromeVolatileCollateral' + ) + } +} + +main().catch((error) => { + console.error(error) + process.exitCode = 1 +}) diff --git a/scripts/verification/collateral-plugins/verify_aerodrome_weth_well.ts b/scripts/verification/collateral-plugins/verify_aerodrome_weth_well.ts new file mode 100644 index 000000000..bf61166ec --- /dev/null +++ b/scripts/verification/collateral-plugins/verify_aerodrome_weth_well.ts @@ -0,0 +1,100 @@ +import hre, { ethers } from 'hardhat' +import { getChainId } from '../../../common/blockchain-utils' +import { baseL2Chains, developmentChains, networkConfig } from '../../../common/configuration' +import { ONE_ADDRESS } from '../../../common/constants' +import { + getDeploymentFile, + getAssetCollDeploymentFilename, + IAssetCollDeployments, +} from '../../deployment/common' +import { verifyContract } from '../../deployment/utils' +import { combinedError } from '../../deployment/utils' +import { IAeroPool } from '@typechain/IAeroPool' +import { + AerodromePoolType, + MAX_TRADE_VOL, + PRICE_TIMEOUT, + AERO_WETH_WELL_POOL, + AERO_WETH_WELL_GAUGE, + AERO, + WELL_ORACLE_TIMEOUT, + WELL_USD_FEED, + WELL_ORACLE_ERROR, + ETH_USD_FEED, + ETH_ORACLE_TIMEOUT, + ETH_ORACLE_ERROR, +} from '../../../test/plugins/individual-collateral/aerodrome/constants' + +let deployments: IAssetCollDeployments + +async function main() { + // ********** Read config ********** + const chainId = await getChainId(hre) + if (!networkConfig[chainId]) { + throw new Error(`Missing network configuration for ${hre.network.name}`) + } + + if (developmentChains.includes(hre.network.name)) { + throw new Error(`Cannot verify contracts for development chain ${hre.network.name}`) + } + + const assetCollDeploymentFilename = getAssetCollDeploymentFilename(chainId) + deployments = getDeploymentFile(assetCollDeploymentFilename) + + // Only on base, aways use wrapper + if (baseL2Chains.includes(hre.network.name)) { + const aeroWETHWELLPoolCollateral = await ethers.getContractAt( + 'AerodromeVolatileCollateral', + deployments.collateral.aeroWETHWELL as string + ) + + /******** Verify Gauge Wrapper **************************/ + + const pool = await ethers.getContractAt('IAeroPool', AERO_WETH_WELL_POOL) + await verifyContract( + chainId, + await aeroWETHWELLPoolCollateral.erc20(), + [ + pool.address, + 'w' + (await pool.name()), + 'w' + (await pool.symbol()), + AERO, + AERO_WETH_WELL_GAUGE, + ], + 'contracts/plugins/assets/aerodrome/AerodromeGaugeWrapper.sol:AerodromeGaugeWrapper' + ) + + /******** Verify WETH-WELL plugin **************************/ + const oracleError = combinedError(WELL_ORACLE_ERROR, ETH_ORACLE_ERROR) // 0.5% & 0.15% + await verifyContract( + chainId, + deployments.collateral.aeroWETHWELL, + [ + { + erc20: await aeroWETHWELLPoolCollateral.erc20(), + targetName: await aeroWETHWELLPoolCollateral.targetName(), + priceTimeout: PRICE_TIMEOUT, + chainlinkFeed: ONE_ADDRESS, // unused but cannot be zero + oracleError: oracleError.toString(), + oracleTimeout: WELL_ORACLE_TIMEOUT, // max of oracleTimeouts + maxTradeVolume: MAX_TRADE_VOL, + defaultThreshold: '0', + delayUntilDefault: '86400', + }, + { + pool: AERO_WETH_WELL_POOL, + poolType: AerodromePoolType.Volatile, + feeds: [[ETH_USD_FEED], [WELL_USD_FEED]], + oracleTimeouts: [[ETH_ORACLE_TIMEOUT], [WELL_ORACLE_TIMEOUT]], + oracleErrors: [[ETH_ORACLE_ERROR], [WELL_ORACLE_ERROR]], + }, + ], + 'contracts/plugins/assets/aerodrome/AerodromeVolatileCollateral.sol:AerodromeVolatileCollateral' + ) + } +} + +main().catch((error) => { + console.error(error) + process.exitCode = 1 +}) diff --git a/scripts/verify_etherscan.ts b/scripts/verify_etherscan.ts index 409717d0d..a8692249c 100644 --- a/scripts/verify_etherscan.ts +++ b/scripts/verify_etherscan.ts @@ -92,9 +92,14 @@ async function main() { 'collateral-plugins/verify_wsteth.ts', 'collateral-plugins/verify_cbeth.ts', 'assets/verify_stg.ts', - 'collateral-plugins/verify_morphoeUSD.ts', 'collateral-plugins/verify_aerodrome_usdc_eusd.ts', - 'collateral-plugins/verify_aerodrome_usdz_usdc.ts' + 'collateral-plugins/verify_aerodrome_weth_aero.ts', + 'collateral-plugins/verify_aerodrome_mog_weth.ts', + 'collateral-plugins/verify_aerodrome_usdz_usdc.ts', + 'collateral-plugins/verify_aerodrome_weth_cbbtc.ts', + 'collateral-plugins/verify_aerodrome_weth_well.ts', + 'collateral-plugins/verify_aerodrome_weth_degen.ts', + 'collateral-plugins/verify_morphoeUSD.ts' ) } else if (chainId == '42161' || chainId == '421614') { // Arbitrum One diff --git a/test/plugins/individual-collateral/aerodrome/AerodromeStableCollateral.test.ts b/test/plugins/individual-collateral/aerodrome/AerodromeStableCollateral.test.ts index 462cf5a23..7ff8ddf3a 100644 --- a/test/plugins/individual-collateral/aerodrome/AerodromeStableCollateral.test.ts +++ b/test/plugins/individual-collateral/aerodrome/AerodromeStableCollateral.test.ts @@ -258,6 +258,7 @@ allStableTests.forEach((curr: AeroStablePoolEnumeration) => { const coll = await deployCollateral({ pool: curr.pool, gauge: curr.gauge, + chainlinkFeed: feed0.address, feeds: [[feed0.address], [feed1.address]], }) @@ -290,6 +291,7 @@ allStableTests.forEach((curr: AeroStablePoolEnumeration) => { const coll = await deployCollateral({ pool: curr.pool, gauge: curr.gauge, + chainlinkFeed: feed0.address, feeds: [[feed0.address], [feed1.address]], }) @@ -330,6 +332,7 @@ allStableTests.forEach((curr: AeroStablePoolEnumeration) => { const invalidCollateral = await deployCollateral({ pool: curr.pool, gauge: curr.gauge, + chainlinkFeed: invalidChainlinkFeed.address, feeds: [[invalidChainlinkFeed.address], [invalidChainlinkFeed.address]], }) diff --git a/test/plugins/individual-collateral/aerodrome/AerodromeVolatileCollateral.test.ts b/test/plugins/individual-collateral/aerodrome/AerodromeVolatileCollateral.test.ts new file mode 100644 index 000000000..2269132ea --- /dev/null +++ b/test/plugins/individual-collateral/aerodrome/AerodromeVolatileCollateral.test.ts @@ -0,0 +1,516 @@ +import collateralTests from '../collateralTests' +import { CollateralFixtureContext, CollateralOpts, MintCollateralFunc } from '../pluginTestTypes' +import { ethers } from 'hardhat' +import { ContractFactory, BigNumberish, BigNumber } from 'ethers' +import { + IAeroPool, + MockV3Aggregator, + MockV3Aggregator__factory, + AerodromeGaugeWrapper__factory, + TestICollateral, + AerodromeGaugeWrapper, + ERC20Mock, +} from '../../../../typechain' +import { ZERO_ADDRESS } from '#/common/constants' +import { bn, fp } from '../../../../common/numbers' +import { expect } from 'chai' +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' +import { + AerodromePoolType, + MOG_USD_FEED, + MOG_HOLDER, + MOG_ORACLE_ERROR, + MOG_ORACLE_TIMEOUT, + PRICE_TIMEOUT, + MAX_TRADE_VOL, + DEFAULT_THRESHOLD, + DELAY_UNTIL_DEFAULT, + AERO_MOG_WETH_POOL, + AERO_MOG_WETH_GAUGE, + AERO_MOG_WETH_HOLDER, + AERO, + AERO_USD_FEED, + AERO_ORACLE_ERROR, + AERO_ORACLE_TIMEOUT, + AERO_HOLDER, + MOG, + WETH, + WETH_HOLDER, + AERO_WETH_AERO_POOL, + AERO_WETH_AERO_GAUGE, + AERO_WETH_AERO_HOLDER, + AERO_WETH_cbBTC_POOL, + AERO_WETH_cbBTC_GAUGE, + AERO_WETH_cbBTC_HOLDER, + AERO_WETH_WELL_POOL, + AERO_WETH_WELL_GAUGE, + AERO_WETH_WELL_HOLDER, + AERO_WETH_DEGEN_POOL, + AERO_WETH_DEGEN_GAUGE, + AERO_WETH_DEGEN_HOLDER, + cbBTC, + cbBTC_USD_FEED, + cbBTC_ORACLE_ERROR, + cbBTC_ORACLE_TIMEOUT, + cbBTC_HOLDER, + WELL, + WELL_USD_FEED, + WELL_ORACLE_ERROR, + WELL_ORACLE_TIMEOUT, + WELL_HOLDER, + DEGEN, + DEGEN_USD_FEED, + DEGEN_ORACLE_ERROR, + DEGEN_ORACLE_TIMEOUT, + DEGEN_HOLDER, + ETH_USD_FEED, + ETH_ORACLE_ERROR, + ETH_ORACLE_TIMEOUT, +} from './constants' +import { mintWrappedLpToken, resetFork, getFeeds, pushAllFeedsForward } from './helpers' + +/* + Define interfaces +*/ + +interface AeroPoolTokenConfig { + token: string + feeds: string[] + oracleTimeouts: BigNumberish[] + oracleErrors: BigNumberish[] + holder: string +} + +interface AeroVolatilePoolEnumeration { + testName: string + pool: string + gauge: string + holder: string + toleranceDivisor: BigNumber + amountScaleDivisor: BigNumber + tokens: AeroPoolTokenConfig[] + oracleTimeout: BigNumberish + oracleError: BigNumberish +} + +interface AeroVolatileCollateralOpts extends CollateralOpts { + pool?: string + poolType?: AerodromePoolType + gauge?: string + feeds?: string[][] + oracleTimeouts?: BigNumberish[][] + oracleErrors?: BigNumberish[][] +} + +interface AerodromeCollateralFixtureContext extends CollateralFixtureContext { + feeds?: string[][] +} + +// ==== + +// Test all Aerodrome Volatile pools +const all: AeroVolatilePoolEnumeration[] = [ + { + testName: 'Aerodrome - MOG/WETH Volatile', + pool: AERO_MOG_WETH_POOL, + gauge: AERO_MOG_WETH_GAUGE, + holder: AERO_MOG_WETH_HOLDER, + tokens: [ + { + token: MOG, + feeds: [MOG_USD_FEED], + oracleTimeouts: [MOG_ORACLE_TIMEOUT], + oracleErrors: [MOG_ORACLE_ERROR], + holder: MOG_HOLDER, + }, + { + token: WETH, + feeds: [ETH_USD_FEED], + oracleTimeouts: [ETH_ORACLE_TIMEOUT], + oracleErrors: [ETH_ORACLE_ERROR], + holder: WETH_HOLDER, + }, + ], + oracleTimeout: MOG_ORACLE_TIMEOUT, // max + oracleError: MOG_ORACLE_ERROR.add(ETH_ORACLE_ERROR), // combined + amountScaleDivisor: bn('1'), + toleranceDivisor: bn('1e4'), + }, + { + testName: 'Aerodrome - WETH/AERO Volatile', + pool: AERO_WETH_AERO_POOL, + gauge: AERO_WETH_AERO_GAUGE, + holder: AERO_WETH_AERO_HOLDER, + tokens: [ + { + token: WETH, + feeds: [ETH_USD_FEED], + oracleTimeouts: [ETH_ORACLE_TIMEOUT], + oracleErrors: [ETH_ORACLE_ERROR], + holder: WETH_HOLDER, + }, + { + token: AERO, + feeds: [AERO_USD_FEED], + oracleTimeouts: [AERO_ORACLE_TIMEOUT], + oracleErrors: [AERO_ORACLE_ERROR], + holder: AERO_HOLDER, + }, + ], + oracleTimeout: AERO_ORACLE_TIMEOUT, // max + oracleError: AERO_ORACLE_ERROR.add(ETH_ORACLE_ERROR), // combined + amountScaleDivisor: bn('1e2'), + toleranceDivisor: bn('1e4'), + }, + { + testName: 'Aerodrome - WETH/cbBTC Volatile', + pool: AERO_WETH_cbBTC_POOL, + gauge: AERO_WETH_cbBTC_GAUGE, + holder: AERO_WETH_cbBTC_HOLDER, + tokens: [ + { + token: WETH, + feeds: [ETH_USD_FEED], + oracleTimeouts: [ETH_ORACLE_TIMEOUT], + oracleErrors: [ETH_ORACLE_ERROR], + holder: WETH_HOLDER, + }, + { + token: cbBTC, + feeds: [cbBTC_USD_FEED], + oracleTimeouts: [cbBTC_ORACLE_TIMEOUT], + oracleErrors: [cbBTC_ORACLE_ERROR], + holder: cbBTC_HOLDER, + }, + ], + oracleTimeout: ETH_ORACLE_TIMEOUT, // max + oracleError: cbBTC_ORACLE_ERROR.add(ETH_ORACLE_ERROR), // combined + amountScaleDivisor: bn('1e6'), + toleranceDivisor: bn('1e4'), + }, + { + testName: 'Aerodrome - WETH/WELL Volatile', + pool: AERO_WETH_WELL_POOL, + gauge: AERO_WETH_WELL_GAUGE, + holder: AERO_WETH_WELL_HOLDER, + tokens: [ + { + token: WETH, + feeds: [ETH_USD_FEED], + oracleTimeouts: [ETH_ORACLE_TIMEOUT], + oracleErrors: [ETH_ORACLE_ERROR], + holder: WETH_HOLDER, + }, + { + token: WELL, + feeds: [WELL_USD_FEED], + oracleTimeouts: [WELL_ORACLE_TIMEOUT], + oracleErrors: [WELL_ORACLE_ERROR], + holder: WELL_HOLDER, + }, + ], + oracleTimeout: WELL_ORACLE_TIMEOUT, // max + oracleError: WELL_ORACLE_ERROR.add(ETH_ORACLE_ERROR), // combined + amountScaleDivisor: bn('1e2'), + toleranceDivisor: bn('1e4'), + }, + { + testName: 'Aerodrome - WETH/DEGN Volatile', + pool: AERO_WETH_DEGEN_POOL, + gauge: AERO_WETH_DEGEN_GAUGE, + holder: AERO_WETH_DEGEN_HOLDER, + tokens: [ + { + token: WETH, + feeds: [ETH_USD_FEED], + oracleTimeouts: [ETH_ORACLE_TIMEOUT], + oracleErrors: [ETH_ORACLE_ERROR], + holder: WETH_HOLDER, + }, + { + token: DEGEN, + feeds: [DEGEN_USD_FEED], + oracleTimeouts: [DEGEN_ORACLE_TIMEOUT], + oracleErrors: [DEGEN_ORACLE_ERROR], + holder: DEGEN_HOLDER, + }, + ], + oracleTimeout: DEGEN_ORACLE_TIMEOUT, // max + oracleError: DEGEN_ORACLE_ERROR.add(ETH_ORACLE_ERROR), // combined + amountScaleDivisor: bn('1e2'), + toleranceDivisor: bn('1e4'), + }, +] + +all.forEach((curr: AeroVolatilePoolEnumeration) => { + const defaultCollateralOpts: AeroVolatileCollateralOpts = { + erc20: ZERO_ADDRESS, + targetName: ethers.utils.formatBytes32String('ETH'), // good enough to test swapping out + priceTimeout: PRICE_TIMEOUT, + chainlinkFeed: curr.tokens[0].feeds[0], // unused but cannot be zero + oracleTimeout: curr.oracleTimeout, // max of oracleTimeouts + oracleError: curr.oracleError, // combined oracle error + maxTradeVolume: MAX_TRADE_VOL, + defaultThreshold: DEFAULT_THRESHOLD, + delayUntilDefault: DELAY_UNTIL_DEFAULT, + pool: curr.pool, + poolType: AerodromePoolType.Volatile, + gauge: curr.gauge, + feeds: [curr.tokens[0].feeds, curr.tokens[1].feeds], + oracleTimeouts: [curr.tokens[0].oracleTimeouts, curr.tokens[1].oracleTimeouts], + oracleErrors: [curr.tokens[0].oracleErrors, curr.tokens[1].oracleErrors], + } + + const deployCollateral = async ( + opts: AeroVolatileCollateralOpts = {} + ): Promise => { + let pool: IAeroPool + let wrapper: AerodromeGaugeWrapper + + if (!opts.erc20) { + const AerodromGaugeWrapperFactory = ( + await ethers.getContractFactory('AerodromeGaugeWrapper') + ) + + // Create wrapper + pool = await ethers.getContractAt('IAeroPool', curr.pool) + + wrapper = await AerodromGaugeWrapperFactory.deploy( + pool.address, + 'w' + (await pool.name()), + 'w' + (await pool.symbol()), + AERO, + curr.gauge + ) + + opts.erc20 = wrapper.address + } + + opts = { ...defaultCollateralOpts, ...opts } + opts.feeds![0][0] = opts.chainlinkFeed! + + const AeroVolatileCollateralFactory: ContractFactory = await ethers.getContractFactory( + 'AerodromeVolatileCollateral' + ) + + const collateral = await AeroVolatileCollateralFactory.deploy( + { + erc20: opts.erc20, + targetName: opts.targetName, + priceTimeout: opts.priceTimeout, + chainlinkFeed: opts.chainlinkFeed, + oracleError: opts.oracleError, + oracleTimeout: opts.oracleTimeout, + maxTradeVolume: opts.maxTradeVolume, + defaultThreshold: opts.defaultThreshold, + delayUntilDefault: opts.delayUntilDefault, + }, + { + pool: opts.pool, + poolType: opts.poolType, + feeds: opts.feeds, + oracleTimeouts: opts.oracleTimeouts, + oracleErrors: opts.oracleErrors, + }, + { gasLimit: 2000000000 } + ) + await collateral.deployed() + + // Push forward chainlink feeds + await pushAllFeedsForward(collateral) + + // sometimes we are trying to test a negative test case and we want this to fail silently + // fortunately this syntax fails silently because our tools are terrible + await expect(collateral.refresh()) + + return collateral + } + + type Fixture = () => Promise + + const makeCollateralFixtureContext = ( + alice: SignerWithAddress, + opts: AeroVolatileCollateralOpts = {} + ): Fixture => { + const collateralOpts = { ...defaultCollateralOpts, ...opts } + + const makeCollateralFixtureContext = async () => { + const MockV3AggregatorFactory = ( + await ethers.getContractFactory('MockV3Aggregator') + ) + + // Substitute both feeds + const token0Feed = await MockV3AggregatorFactory.deploy(8, bn('1e8')) + collateralOpts.chainlinkFeed = token0Feed.address + + const token1Feed = await MockV3AggregatorFactory.deploy(8, bn('1e8')) + collateralOpts.feeds = [[token0Feed.address], [token1Feed.address]] + + const pool = await ethers.getContractAt('IAeroPool', curr.pool) + + const AerodromeGaugeWrapperFactory = ( + await ethers.getContractFactory('AerodromeGaugeWrapper') + ) + + const wrapper = await AerodromeGaugeWrapperFactory.deploy( + pool.address, + 'w' + (await pool.name()), + 'w' + (await pool.symbol()), + AERO, + curr.gauge + ) + + collateralOpts.erc20 = wrapper.address + + const collateral = await deployCollateral(collateralOpts) + const erc20 = await ethers.getContractAt( + 'AerodromeGaugeWrapper', + (await collateral.erc20()) as string + ) + + const rewardToken = await ethers.getContractAt('ERC20Mock', AERO) + + return { + alice, + collateral, + chainlinkFeed: token0Feed, + tok: erc20, + rewardToken, + } + } + + return makeCollateralFixtureContext + } + + /* + Define helper functions +*/ + + const mintCollateralTo: MintCollateralFunc = async ( + ctx: CollateralFixtureContext, + amount: BigNumberish, + user: SignerWithAddress, + recipient: string + ) => { + const gauge = await ethers.getContractAt('IAeroGauge', curr.gauge) + const pool = await ethers.getContractAt('IAeroPool', curr.pool) + + await mintWrappedLpToken( + ctx.tok as AerodromeGaugeWrapper, + gauge, + pool, + amount, + curr.holder, + user, + recipient + ) + } + + // eslint-disable-next-line @typescript-eslint/no-empty-function + const reduceTargetPerRef = async () => {} + + // eslint-disable-next-line @typescript-eslint/no-empty-function + const increaseTargetPerRef = async () => {} + + // eslint-disable-next-line @typescript-eslint/no-empty-function + const increaseRefPerTok = async () => {} + + // eslint-disable-next-line @typescript-eslint/no-empty-function + const reduceRefPerTok = async () => {} + + // eslint-disable-next-line @typescript-eslint/no-empty-function + const collateralSpecificConstructorTests = () => {} + + const collateralSpecificStatusTests = () => { + it('prices change as feed price changes', async () => { + const MockV3AggregatorFactory = await ethers.getContractFactory('MockV3Aggregator') + const feed0 = await MockV3AggregatorFactory.deploy(8, bn('1e8')) + const feed1 = await MockV3AggregatorFactory.deploy(8, bn('1e8')) + + const coll = await deployCollateral({ + pool: curr.pool, + gauge: curr.gauge, + chainlinkFeed: feed0.address, + feeds: [[feed0.address], [feed1.address]], + }) + + const initialRefPerTok = await coll.refPerTok() + const [low, high] = await coll.price() + + // Update values in Oracles increase by 10% + const allFeeds = await getFeeds(coll) + const initialPrices = await Promise.all(allFeeds.map((f) => f.latestRoundData())) + for (const [i, feed] of allFeeds.entries()) { + await feed.updateAnswer(initialPrices[i].answer.mul(110).div(100)).then((e) => e.wait()) + } + + const [newLow, newHigh] = await coll.price() + + // with 18 decimals of price precision a 1e-9 tolerance seems fine for a 10% change + expect(newLow).to.be.closeTo(low.mul(110).div(100), fp('1e-9')) + expect(newHigh).to.be.closeTo(high.mul(110).div(100), fp('1e-9')) + + // Check refPerTok remains _exactly_ the same + const finalRefPerTok = await coll.refPerTok() + expect(finalRefPerTok).to.equal(initialRefPerTok) + }) + } + + const getExpectedPrice = async (ctx: CollateralFixtureContext) => { + const initRefPerTok = await ctx.collateral.refPerTok() + const coll = await ethers.getContractAt('AerodromeVolatileCollateral', ctx.collateral.address) + + const feed0 = await ethers.getContractAt('MockV3Aggregator', (await coll.tokenFeeds(0))[0]) + const decimals0 = await feed0.decimals() + const initData0 = await feed0.latestRoundData() + + const feed1 = await ethers.getContractAt('MockV3Aggregator', (await coll.tokenFeeds(1))[0]) + const decimals1 = await feed1.decimals() + const initData1 = await feed1.latestRoundData() + + const avgPrice = initData0.answer + .mul(bn(10).pow(18 - decimals0)) + .add(initData1.answer.mul(bn(10).pow(18 - decimals1))) + .div(2) + + return avgPrice.mul(initRefPerTok).div(fp('1')) + } + + /* + Run the test suite + */ + + const emptyFn = () => { + return + } + + const opts = { + deployCollateral, + collateralSpecificConstructorTests, + collateralSpecificStatusTests, + beforeEachRewardsTest: emptyFn, + makeCollateralFixtureContext, + mintCollateralTo, + reduceTargetPerRef, + increaseTargetPerRef, + reduceRefPerTok, + increaseRefPerTok, + getExpectedPrice, + itClaimsRewards: it, + itChecksTargetPerRefDefault: it.skip, + itChecksTargetPerRefDefaultUp: it.skip, + itChecksRefPerTokDefault: it.skip, + itChecksPriceChanges: it.skip, + itChecksNonZeroDefaultThreshold: it.skip, + itHasRevenueHiding: it.skip, + resetFork, + collateralName: curr.testName, + chainlinkDefaultAnswer: bn('1e8'), + itIsPricedByPeg: false, + toleranceDivisor: curr.toleranceDivisor, + amountScaleDivisor: curr.amountScaleDivisor, + targetNetwork: 'base', + } + + collateralTests(opts) +}) diff --git a/test/plugins/individual-collateral/aerodrome/constants.ts b/test/plugins/individual-collateral/aerodrome/constants.ts index 089ca0b3e..984cde0eb 100644 --- a/test/plugins/individual-collateral/aerodrome/constants.ts +++ b/test/plugins/individual-collateral/aerodrome/constants.ts @@ -7,19 +7,44 @@ export const forkNetwork = useEnv('FORK_NETWORK') ?? 'base' // Base Addresses export const AERO_USDC_eUSD_GAUGE = '0x793F22aB88dC91793E5Ce6ADbd7E733B0BD4733e' export const AERO_USDC_eUSD_POOL = '0x7A034374C89C463DD65D8C9BCfe63BcBCED41f4F' -export const AERO_USDC_eUSD_HOLDER = '0xB6C8ea53ABA64a4BdE857D3b25d9DEbD0B149a0a' +export const AERO_USDC_eUSD_HOLDER = '0x5d823546463fD0Ac3b31DBA6AD38B56a39BFbf70' // for gauge + +export const AERO_WETH_AERO_GAUGE = '0x96a24aB830D4ec8b1F6f04Ceac104F1A3b211a01' +export const AERO_WETH_AERO_POOL = '0x7f670f78B17dEC44d5Ef68a48740b6f8849cc2e6' +export const AERO_WETH_AERO_HOLDER = '0x9f2cB6b3A5BfE6A7D42c3702F628201616649C00' // for pool + +export const AERO_MOG_WETH_GAUGE = '0x8FCc385d8d7f3A2e087853a79531630Bf96575e8' +export const AERO_MOG_WETH_POOL = '0x4a311ac4563abc30e71d0631c88a6232c1309ac5' +export const AERO_MOG_WETH_HOLDER = '0x76AbE28E4108eC1B56f429582087CFDdC757eAcc' // for pool export const AERO_USDz_USDC_GAUGE = '0xb7E4bBee04285F4B55d0A93b34E5dA95C3a7faf9' export const AERO_USDz_USDC_POOL = '0x6d0b9C9E92a3De30081563c3657B5258b3fFa38B' export const AERO_USDz_USDC_HOLDER = '0x4C3cB0D6273A27C68AB4B2F96DB211d8d75e98Da' +export const AERO_WETH_cbBTC_GAUGE = '0xAFdEBa12B6a870d6639d043030b4b49F9C7c62BB' +export const AERO_WETH_cbBTC_POOL = '0x2578365B3dfA7FfE60108e181EFb79FeDdec2319' +export const AERO_WETH_cbBTC_HOLDER = '0x106b9121d259fb46F1033BDBd29ad0C67650953C' + +export const AERO_WETH_WELL_GAUGE = '0x7b6964440b615aC1d31bc95681B133E112fB2684' +export const AERO_WETH_WELL_POOL = '0x89D0F320ac73dd7d9513FFC5bc58D1161452a657' +export const AERO_WETH_WELL_HOLDER = '0x5a6859C2f992B998837342d29911dD14E8DC2E1a' + +export const AERO_WETH_DEGEN_GAUGE = '0x86A1260AB9f758026Ce1a5830BdfF66DBcF736d5' +export const AERO_WETH_DEGEN_POOL = '0x2C4909355b0C036840819484c3A882A95659aBf3' +export const AERO_WETH_DEGEN_HOLDER = '0xC314990f1379fEe384Be9a74328C75Df8587aE77' + export const AERODROME_ROUTER = '0xcF77a3Ba9A5CA399B7c97c74d54e5b1Beb874E43' // Tokens export const USDC = networkConfig['8453'].tokens.USDC! +export const WETH = networkConfig['8453'].tokens.WETH! export const eUSD = networkConfig['8453'].tokens.eUSD! export const USDz = networkConfig['8453'].tokens.USDz! export const AERO = networkConfig['8453'].tokens.AERO! +export const MOG = networkConfig['8453'].tokens.MOG! +export const cbBTC = networkConfig['8453'].tokens.cbBTC! +export const WELL = networkConfig['8453'].tokens.WELL! +export const DEGEN = networkConfig['8453'].tokens.DEGEN! // USDC export const USDC_USD_FEED = networkConfig['8453'].chainlinkFeeds.USDC! @@ -39,14 +64,50 @@ export const USDz_ORACLE_TIMEOUT = bn('86400') export const USDz_ORACLE_ERROR = fp('0.005') export const USDz_HOLDER = '0xA87c9808C0eBE20a1427B5C769623c77201f6f4D' -export const FORK_BLOCK = 21668000 //19980400 +export const FORK_BLOCK = 22854038 //19980400 + +// AERO +export const AERO_ORACLE_ERROR = fp('0.005') // 0.5% +export const AERO_ORACLE_TIMEOUT = bn('86400') // 24hr +export const AERO_USD_FEED = networkConfig['8453'].chainlinkFeeds.AERO! +export const AERO_HOLDER = '0x807877258B55BfEfaBDD469dA1C72731C5070839' + +// ETH +export const ETH_ORACLE_ERROR = fp('0.0015') // 0.15% +export const ETH_ORACLE_TIMEOUT = bn('1200') // 20min +export const ETH_USD_FEED = networkConfig['8453'].chainlinkFeeds.ETHUSD! +export const WETH_HOLDER = '0x6446021F4E396dA3df4235C62537431372195D38' + +// MOG +export const MOG_ORACLE_ERROR = fp('0.005') // 0.5% +export const MOG_ORACLE_TIMEOUT = bn('86400') // 24hr +export const MOG_USD_FEED = networkConfig['8453'].chainlinkFeeds.MOG! +export const MOG_HOLDER = '0xBaeD383EDE0e5d9d72430661f3285DAa77E9439F' + +// cbBTC +export const cbBTC_ORACLE_ERROR = fp('0.003') // 0.3% +export const cbBTC_ORACLE_TIMEOUT = bn('1200') // 20 min +export const cbBTC_USD_FEED = networkConfig['8453'].chainlinkFeeds.cbBTC! +export const cbBTC_HOLDER = '0xF877ACaFA28c19b96727966690b2f44d35aD5976' + +// WELL +export const WELL_ORACLE_ERROR = fp('0.005') // 0.5% +export const WELL_ORACLE_TIMEOUT = bn('86400') // 24hr +export const WELL_USD_FEED = networkConfig['8453'].chainlinkFeeds.WELL! +export const WELL_HOLDER = '0xe66E3A37C3274Ac24FE8590f7D84A2427194DC17' + +// DEGEN +export const DEGEN_ORACLE_ERROR = fp('0.005') // 0.5% +export const DEGEN_ORACLE_TIMEOUT = bn('86400') // 24hr +export const DEGEN_USD_FEED = networkConfig['8453'].chainlinkFeeds.DEGEN! +export const DEGEN_HOLDER = '0x06A19654e0872Ba71c2261EA691Ecf8a0c677156' // Common export const FIX_ONE = 1n * 10n ** 18n export const ORACLE_ERROR = fp('0.005') export const PRICE_TIMEOUT = bn('604800') // 1 week export const DEFAULT_THRESHOLD = fp('0.02') // 2% -export const DELAY_UNTIL_DEFAULT = bn('259200') // 72h +export const DELAY_UNTIL_DEFAULT = bn('259200') // 72h CAREFUL THIS IS ONLY FOR STABLE POOLS export const MAX_TRADE_VOL = fp('1e6') export enum AerodromePoolType { diff --git a/test/plugins/individual-collateral/aerodrome/helpers.ts b/test/plugins/individual-collateral/aerodrome/helpers.ts index 69a8961d9..1edc8dfde 100644 --- a/test/plugins/individual-collateral/aerodrome/helpers.ts +++ b/test/plugins/individual-collateral/aerodrome/helpers.ts @@ -7,7 +7,9 @@ import { TestICollateral, MockV3Aggregator, } from '@typechain/index' +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' import { ethers } from 'hardhat' +import { BigNumberish } from 'ethers' import { USDC, eUSD, @@ -102,7 +104,10 @@ export const mintLpToken = async ( recipient: string ) => { await whileImpersonating(holder, async (signer) => { - await gauge.connect(signer).withdraw(amount) + // holder can have lpToken OR gauge + if ((await lpToken.balanceOf(signer.address)).lt(amount)) { + await gauge.connect(signer).withdraw(amount) + } await lpToken.connect(signer).transfer(recipient, amount) }) } @@ -122,9 +127,10 @@ export const mintWrappedLpToken = async ( } export const getFeeds = async (coll: TestICollateral): Promise => { - const aeroStableColl = await ethers.getContractAt('AerodromeStableCollateral', coll.address) + const aeroColl = await ethers.getContractAt('AerodromeVolatileCollateral', coll.address) + // works for AerodromeStableCollateral too - const feedAddrs = (await aeroStableColl.tokenFeeds(0)).concat(await aeroStableColl.tokenFeeds(1)) + const feedAddrs = (await aeroColl.tokenFeeds(0)).concat(await aeroColl.tokenFeeds(1)) const feeds: MockV3Aggregator[] = [] for (const feedAddr of feedAddrs) { @@ -190,7 +196,7 @@ export const allStableTests: AeroStablePoolEnumeration[] = [ ], oracleTimeout: PRICE_TIMEOUT, // max oracleError: ORACLE_ERROR, // combined - amountScaleDivisor: bn('1e2'), + amountScaleDivisor: bn('1e3'), toleranceDivisor: bn('1e2'), fix: makeWUSDCeUSD, },