Skip to content

Commit

Permalink
Get as many pools working as possible before handling rate pools
Browse files Browse the repository at this point in the history
  • Loading branch information
crispymangoes committed Nov 6, 2023
1 parent a4a919f commit 62d1022
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 5 deletions.
16 changes: 16 additions & 0 deletions src/modules/price-router/Extensions/Curve/Curve2PoolExtension.sol
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,18 @@ contract Curve2PoolExtension is Extension {
address pool;
bool isCorrelated;
// TODO if we store the coins0 and coins1 here then we can store the underlying or constituent, and or rate provider contracts too.
// kinda like the above idea of saving the underlying or constituent in here....
}

struct NewExtensionStorage {
address pool;
address underlyingOrConstituent0;
address underlyingOrConstituent1;
bool divideRate0; // If we only have the market price of the underlying, and there is a rate with the underlying, then divide out the rate
bool divideRate1; // If we only new the safe price of sDAI, then we need to divide out the rate stored in the curve pool
bool isCorrelated; // but if we know the safe market price of DAI then we can just use that.
// TODO if we store the coins0 and coins1 here then we can store the underlying or constituent, and or rate provider contracts too.
// kinda like the above idea of saving the underlying or constituent in here....
}

/**
Expand Down Expand Up @@ -96,7 +108,11 @@ contract Curve2PoolExtension is Extension {
uint256 price1 = priceRouter.getPriceInUSD(getCoins(pool, 1));
uint256 minPrice = price0 < price1 ? price0 : price1;
price = minPrice.mulDivDown(pool.get_virtual_price(), 10 ** curveDecimals);
// TODO add in new underlying or constituent logic here with rates.
} else {
// TODO I wonder if rates would also need to come up here, like if this new pool ever gave the lp_price without adjusting for the rate, then we would need to?
// TODO ^^^ I dont think this makes sense cuz the point of it acconting for the rate is to keep the liquidity concentrated at that rate
// but you can only do that with correlated pairs.
price = pool.lp_price().mulDivDown(priceRouter.getPriceInUSD(getCoins(pool, 0)), 10 ** curveDecimals);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,12 @@ contract CurveEMAExtension is Extension {
return address(coins0) == CURVE_ETH ? WETH : coins0;
}

// TODO this code needs to change so that is can optionally handle tokens with rates, and basically take the price_oracle value and multiply by the rate.
// Examples are ETHx, sDAI, sFRAX.
/**
* @notice Helper function to get the price of an asset using a Curve EMA Oracle.
*/
function getPriceFromCurvePool(CurvePool pool, uint8 index, bool needIndex) public view returns (uint256) {
function getPriceFromCurvePool(CurvePool pool, uint8 index, bool needIndex) public view returns (uint256 price) {
return needIndex ? pool.price_oracle(index) : pool.price_oracle();
}
}
20 changes: 20 additions & 0 deletions test/resources/MainnetAddresses.sol
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ contract MainnetAddresses {
ERC20 public CVX = ERC20(0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B);
ERC20 public FRXETH = ERC20(0x5E8422345238F34275888049021821E8E08CAa1f);
ERC20 public CRVUSD = ERC20(0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E);
ERC20 public OETH = ERC20(0x856c4Efb76C1D1AE02e20CEB03A2A6a08b0b8dC3);
ERC20 public MKUSD = ERC20(0x4591DBfF62656E7859Afe5e45f6f47D3669fBB28);
ERC20 public YETH = ERC20(0x1BED97CBC3c24A4fb5C069C6E311a967386131f7);

// Chainlink Datafeeds
address public WETH_USD_FEED = 0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419;
Expand Down Expand Up @@ -206,6 +209,23 @@ contract MainnetAddresses {
address public WethCvxPool = 0xB576491F1E6e5E62f1d8F26062Ee822B40B0E0d4;
address public WethCvxToken = 0x3A283D9c08E8b55966afb64C515f5143cf907611;
address public WethCvxGauge = 0x7E1444BA99dcdFfE8fBdb42C02F0005D14f13BE1;
address public EthStethNgPool = 0x21E27a5E5513D6e65C4f830167390997aA84843a;
address public EthStethNgToken = 0x21E27a5E5513D6e65C4f830167390997aA84843a;
address public EthStethNgGauge = 0x79F21BC30632cd40d2aF8134B469a0EB4C9574AA;
address public EthOethPool = 0x94B17476A93b3262d87B9a326965D1E91f9c13E7;
address public EthOethToken = 0x94B17476A93b3262d87B9a326965D1E91f9c13E7;
address public EthOethGauge = 0xd03BE91b1932715709e18021734fcB91BB431715;
address public FraxCrvUsdPool = 0x0CD6f267b2086bea681E922E19D40512511BE538;
address public FraxCrvUsdToken = 0x0CD6f267b2086bea681E922E19D40512511BE538;
address public FraxCrvUsdGauge = 0x96424E6b5eaafe0c3B36CA82068d574D44BE4e3c;
address public mkUsdFraxUsdcPool = 0x0CFe5C777A7438C9Dd8Add53ed671cEc7A5FAeE5;
address public mkUsdFraxUsdcToken = 0x0CFe5C777A7438C9Dd8Add53ed671cEc7A5FAeE5;
address public mkUsdFraxUsdcGauge = 0xF184d80915Ba7d835D941BA70cDdf93DE36517ee;
address public WethYethPool = 0x69ACcb968B19a53790f43e57558F5E443A91aF22;
address public WethYethToken = 0x69ACcb968B19a53790f43e57558F5E443A91aF22;
address public WethYethGauge = 0x138cC21D15b7A06F929Fc6CFC88d2b830796F4f1;

address public WethMkUsdPool = 0xc89570207c5BA1B0E3cD372172cCaEFB173DB270;

// Uniswap V3
address public WSTETH_WETH_100 = 0x109830a1AAaD605BbF02a9dFA7B0B92EC2FB7dAa;
Expand Down
139 changes: 135 additions & 4 deletions test/testAdaptors/CurveAdaptor.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ contract CurveAdaptorTest is MainnetStarterTest, AdaptorHelperFunctions {
uint32 private fraxPosition = 7;
uint32 private frxethPosition = 8;
uint32 private cvxPosition = 9;
uint32 private oethPosition = 21;
uint32 private mkUsdPosition = 23;
uint32 private yethPosition = 25;
uint32 private UsdcCrvUsdPoolPosition = 10;
uint32 private WethRethPoolPosition = 11;
uint32 private UsdtCrvUsdPoolPosition = 12;
Expand All @@ -46,6 +49,11 @@ contract CurveAdaptorTest is MainnetStarterTest, AdaptorHelperFunctions {
uint32 private EthFrxethPoolPosition = 16;
uint32 private StethFrxethPoolPosition = 17;
uint32 private WethCvxPoolPosition = 18;
uint32 private EthStethNgPoolPosition = 19;
uint32 private EthOethPoolPosition = 20;
uint32 private fraxCrvUsdPoolPosition = 22;
uint32 private mkUsdFraxUsdcPoolPosition = 24;
uint32 private WethYethPoolPosition = 26;

uint32 private slippage = 0.9e4;
uint256 public initialAssets;
Expand Down Expand Up @@ -140,6 +148,33 @@ contract CurveAdaptorTest is MainnetStarterTest, AdaptorHelperFunctions {
settings = PriceRouter.AssetSettings(EXTENSION_DERIVATIVE, address(curveEMAExtension));
priceRouter.addAsset(CVX, settings, abi.encode(cStor), price);

// Add OETH
cStor.pool = EthOethPool;
cStor.index = 0;
cStor.needIndex = false;
price = curveEMAExtension.getPriceFromCurvePool(CurvePool(cStor.pool), cStor.index, cStor.needIndex);
price = price.mulDivDown(priceRouter.getPriceInUSD(WETH), 1e18);
settings = PriceRouter.AssetSettings(EXTENSION_DERIVATIVE, address(curveEMAExtension));
priceRouter.addAsset(OETH, settings, abi.encode(cStor), price);

// Add mkUsd
cStor.pool = WethMkUsdPool;
cStor.index = 0;
cStor.needIndex = false;
price = curveEMAExtension.getPriceFromCurvePool(CurvePool(cStor.pool), cStor.index, cStor.needIndex);
price = price.mulDivDown(priceRouter.getPriceInUSD(WETH), 1e18);
settings = PriceRouter.AssetSettings(EXTENSION_DERIVATIVE, address(curveEMAExtension));
priceRouter.addAsset(MKUSD, settings, abi.encode(cStor), price);

// Add yETH
cStor.pool = WethYethPool;
cStor.index = 0;
cStor.needIndex = false;
price = curveEMAExtension.getPriceFromCurvePool(CurvePool(cStor.pool), cStor.index, cStor.needIndex);
price = price.mulDivDown(priceRouter.getPriceInUSD(WETH), 1e18);
settings = PriceRouter.AssetSettings(EXTENSION_DERIVATIVE, address(curveEMAExtension));
priceRouter.addAsset(YETH, settings, abi.encode(cStor), price);

// Add 2pools.
// UsdcCrvUsdPool
// UsdcCrvUsdToken
Expand Down Expand Up @@ -177,6 +212,26 @@ contract CurveAdaptorTest is MainnetStarterTest, AdaptorHelperFunctions {
// WethCvxToken
// WethCvxGauge
_add2PoolAssetToPriceRouter(WethCvxPool, WethCvxToken, false, 154e8);
// EthStethNgPool
// EthStethNgToken
// EthStethNgGauge
_add2PoolAssetToPriceRouter(EthStethNgPool, EthStethNgToken, true, 1_800e8);
// EthOethPool
// EthOethToken
// EthOethGauge
_add2PoolAssetToPriceRouter(EthOethPool, EthOethToken, true, 1_800e8);
// FraxCrvUsdPool
// FraxCrvUsdToken
// FraxCrvUsdGauge
_add2PoolAssetToPriceRouter(FraxCrvUsdPool, FraxCrvUsdToken, true, 1e8);
// mkUsdFraxUsdcPool
// mkUsdFraxUsdcToken
// mkUsdFraxUsdcGauge
_add2PoolAssetToPriceRouter(mkUsdFraxUsdcPool, mkUsdFraxUsdcToken, true, 1e8);
// WethYethPool
// WethYethToken
// WethYethGauge
_add2PoolAssetToPriceRouter(WethYethPool, WethYethToken, true, 1_800e8);

// Add positions to registry.
registry.trustAdaptor(address(curveAdaptor));
Expand All @@ -190,6 +245,9 @@ contract CurveAdaptorTest is MainnetStarterTest, AdaptorHelperFunctions {
registry.trustPosition(fraxPosition, address(erc20Adaptor), abi.encode(FRAX));
registry.trustPosition(frxethPosition, address(erc20Adaptor), abi.encode(FRXETH));
registry.trustPosition(cvxPosition, address(erc20Adaptor), abi.encode(CVX));
registry.trustPosition(oethPosition, address(erc20Adaptor), abi.encode(OETH));
registry.trustPosition(mkUsdPosition, address(erc20Adaptor), abi.encode(MKUSD));
registry.trustPosition(yethPosition, address(erc20Adaptor), abi.encode(YETH));

registry.trustPosition(
UsdcCrvUsdPoolPosition,
Expand Down Expand Up @@ -237,6 +295,41 @@ contract CurveAdaptorTest is MainnetStarterTest, AdaptorHelperFunctions {
abi.encode(WethCvxPool, WethCvxToken, WethCvxGauge, CurvePool.claim_admin_fees.selector)
);

registry.trustPosition(
EthStethNgPoolPosition,
address(curveAdaptor),
abi.encode(EthStethNgPool, EthStethNgToken, EthStethNgGauge, CurvePool.withdraw_admin_fees.selector)
);

registry.trustPosition(
EthOethPoolPosition,
address(curveAdaptor),
abi.encode(EthOethPool, EthOethToken, EthOethGauge, CurvePool.withdraw_admin_fees.selector)
);

registry.trustPosition(
fraxCrvUsdPoolPosition,
address(curveAdaptor),
abi.encode(FraxCrvUsdPool, FraxCrvUsdToken, FraxCrvUsdGauge, CurvePool.withdraw_admin_fees.selector)
);

registry.trustPosition(
mkUsdFraxUsdcPoolPosition,
address(curveAdaptor),
abi.encode(
mkUsdFraxUsdcPool,
mkUsdFraxUsdcToken,
mkUsdFraxUsdcGauge,
CurvePool.withdraw_admin_fees.selector
)
);

registry.trustPosition(
WethYethPoolPosition,
address(curveAdaptor),
abi.encode(WethYethPool, WethYethToken, WethYethGauge, CurvePool.withdraw_admin_fees.selector)
);

string memory cellarName = "Curve Cellar V0.0";
uint256 initialDeposit = 1e6;
uint64 platformCut = 0.75e18;
Expand Down Expand Up @@ -265,8 +358,8 @@ contract CurveAdaptorTest is MainnetStarterTest, AdaptorHelperFunctions {

USDC.safeApprove(address(cellar), type(uint256).max);

for (uint32 i = 2; i < 19; ++i) cellar.addPositionToCatalogue(i);
for (uint32 i = 2; i < 19; ++i) cellar.addPosition(0, i, abi.encode(false), false);
for (uint32 i = 2; i < 27; ++i) cellar.addPositionToCatalogue(i);
for (uint32 i = 2; i < 27; ++i) cellar.addPosition(0, i, abi.encode(false), false);

cellar.setRebalanceDeviation(0.030e18);

Expand Down Expand Up @@ -312,16 +405,41 @@ contract CurveAdaptorTest is MainnetStarterTest, AdaptorHelperFunctions {
_manageLiquidityIn2PoolNoETH(assets, WethCvxPool, WethCvxToken, 0.0050e18);
}

function testManagingLiquidityIn2PoolCorrelatedWithETH0(uint256 assets) external {
function testManagingLiquidityIn2PoolNoETH7(uint256 assets) external {
assets = bound(assets, 1e6, 100_000e6);
_manageLiquidityIn2PoolNoETH(assets, FraxCrvUsdPool, FraxCrvUsdToken, 0.0005e18);
}

function testManagingLiquidityIn2PoolNoETH8(uint256 assets) external {
assets = bound(assets, 1e6, 100_000e6);
_manageLiquidityIn2PoolNoETH(assets, mkUsdFraxUsdcPool, mkUsdFraxUsdcToken, 0.0050e18);
}

function testManagingLiquidityIn2PoolNoETH9(uint256 assets) external {
assets = bound(assets, 1e6, 100_000e6);
_manageLiquidityIn2PoolNoETH(assets, WethYethPool, WethYethToken, 0.0050e18);
}

function testManagingLiquidityIn2PoolWithETH0(uint256 assets) external {
assets = bound(assets, 1e6, 1_000_000e6);
_manageLiquidityIn2PoolWithETH(assets, EthStethPool, EthStethToken, 0.0030e18);
}

function testManagingLiquidityIn2PoolCorrelatedWithETH1(uint256 assets) external {
function testManagingLiquidityIn2PoolWithETH1(uint256 assets) external {
assets = bound(assets, 1e6, 1_000_000e6);
_manageLiquidityIn2PoolWithETH(assets, EthFrxethPool, EthFrxethToken, 0.0010e18);
}

function testManagingLiquidityIn2PoolWithETH2(uint256 assets) external {
assets = bound(assets, 1e6, 1_000_000e6);
_manageLiquidityIn2PoolWithETH(assets, EthStethNgPool, EthStethNgToken, 0.0025e18);
}

function testManagingLiquidityIn2PoolWithETH3(uint256 assets) external {
assets = bound(assets, 1e6, 1_000_000e6);
_manageLiquidityIn2PoolWithETH(assets, EthOethPool, EthOethToken, 0.0010e18);
}

// TODO for sDAI and sFRAX pools, I think that they are a special pool type, where there is no LP price,
// so in pricing we need to either use the price of the underlying, or take the sDAI price, and divide out the rate.

Expand All @@ -343,6 +461,7 @@ contract CurveAdaptorTest is MainnetStarterTest, AdaptorHelperFunctions {
} else {
assets = priceRouter.getValue(USDC, assets, coins0);
if (coins0 == STETH) _takeSteth(assets, address(cellar));
else if (coins0 == OETH) _takeOeth(assets, address(cellar));
else deal(address(coins0), address(cellar), assets);
}
deal(address(USDC), address(cellar), 0);
Expand Down Expand Up @@ -383,8 +502,10 @@ contract CurveAdaptorTest is MainnetStarterTest, AdaptorHelperFunctions {
orderedTokenAmounts[0] = assets / 4;
orderedTokenAmounts[1] = coins1Amount;
if (coins0 == STETH) _takeSteth(assets / 4, address(cellar));
else if (coins0 == OETH) _takeOeth(assets / 4, address(cellar));
else deal(address(coins0), address(cellar), assets / 4);
if (coins1 == STETH) _takeSteth(coins1Amount, address(cellar));
else if (coins1 == OETH) _takeOeth(coins1Amount, address(cellar));
else deal(address(coins1), address(cellar), coins1Amount);
}
{
Expand Down Expand Up @@ -454,6 +575,7 @@ contract CurveAdaptorTest is MainnetStarterTest, AdaptorHelperFunctions {
} else {
assets = priceRouter.getValue(USDC, assets, coins0);
if (coins0 == STETH) _takeSteth(assets, address(cellar));
else if (coins0 == OETH) _takeOeth(assets, address(cellar));
else deal(address(coins0), address(cellar), assets);
}
deal(address(USDC), address(cellar), 0);
Expand Down Expand Up @@ -504,8 +626,10 @@ contract CurveAdaptorTest is MainnetStarterTest, AdaptorHelperFunctions {
orderedTokenAmounts[0] = assets / 4;
orderedTokenAmounts[1] = coins1Amount;
if (coins0 == STETH) _takeSteth(assets / 4, address(cellar));
else if (coins0 == OETH) _takeOeth(assets / 4, address(cellar));
else deal(address(coins0), address(cellar), assets / 4);
if (coins1 == STETH) _takeSteth(coins1Amount, address(cellar));
else if (coins1 == OETH) _takeOeth(coins1Amount, address(cellar));
else deal(address(coins1), address(cellar), coins1Amount);
}
{
Expand Down Expand Up @@ -605,4 +729,11 @@ contract CurveAdaptorTest is MainnetStarterTest, AdaptorHelperFunctions {
vm.prank(stethWhale);
STETH.safeTransfer(to, amount);
}

function _takeOeth(uint256 amount, address to) internal {
// STETH does not work with DEAL, so steal STETH from a whale.
address oethWhale = 0xEADB3840596cabF312F2bC88A4Bb0b93A4E1FF5F;
vm.prank(oethWhale);
OETH.safeTransfer(to, amount);
}
}

0 comments on commit 62d1022

Please sign in to comment.