Skip to content

Commit

Permalink
Finish vast majority of curve adaptor tests
Browse files Browse the repository at this point in the history
  • Loading branch information
crispymangoes committed Nov 8, 2023
1 parent 8d2c9d4 commit 8580f68
Show file tree
Hide file tree
Showing 4 changed files with 613 additions and 21 deletions.
2 changes: 2 additions & 0 deletions src/interfaces/external/Curve/CurvePool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ interface CurvePool {

function remove_liquidity_one_coin(uint256 token_amount, int128 i, uint256 min_amount) external;

function remove_liquidity(uint256 token_amount, uint256[2] memory min_amounts) external;

function lp_price() external view returns (uint256);

function get_virtual_price() external view returns (uint256);
Expand Down
43 changes: 32 additions & 11 deletions src/modules/adaptors/Curve/CurveAdaptor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ contract CurveAdaptor is BaseAdaptor, CurveHelper {
// Also maybe a bool indicating whether you want to deposit into the gauge or nah
//====================================================================

error CurveAdaptor___Slippage();

//============================================ Global Functions ===========================================
/**
* @dev Identifier unique to this adaptor for a shared registry.
Expand All @@ -60,7 +62,13 @@ contract CurveAdaptor is BaseAdaptor, CurveHelper {
* so there is nothing to do.
*/
function deposit(uint256 assets, bytes memory adaptorData, bytes memory) public override {
(, ERC20 token, CurveGauge gauge) = abi.decode(adaptorData, (CurvePool, ERC20, CurveGauge));
(CurvePool pool, ERC20 token, CurveGauge gauge, bytes4 selector) = abi.decode(
adaptorData,
(CurvePool, ERC20, CurveGauge, bytes4)
);

if (selector != bytes4(0)) _callReentrancyFunction(pool, selector);
else revert BaseAdaptor__UserDepositsNotAllowed();

if (address(gauge) != address(0)) {
// Deposit into gauge.
Expand All @@ -78,13 +86,21 @@ contract CurveAdaptor is BaseAdaptor, CurveHelper {
* @param adaptorData data needed to withdraw from this position
* @dev configurationData is NOT used
*/
function withdraw(uint256 assets, address receiver, bytes memory adaptorData, bytes memory) public override {
function withdraw(
uint256 assets,
address receiver,
bytes memory adaptorData,
bytes memory configurationData
) public override {
_externalReceiverCheck(receiver);
(CurvePool pool, ERC20 token, CurveGauge gauge, bytes4 selector) = abi.decode(
adaptorData,
(CurvePool, ERC20, CurveGauge, bytes4)
);
_callReentrancyFunction(pool, selector);
bool isLiquid = abi.decode(configurationData, (bool));

if (isLiquid && selector != bytes4(0)) _callReentrancyFunction(pool, selector);
else revert BaseAdaptor__UserWithdrawsNotAllowed();

uint256 tokenBalance = token.balanceOf(address(this));
if (tokenBalance < assets) {
Expand All @@ -103,10 +119,14 @@ contract CurveAdaptor is BaseAdaptor, CurveHelper {
bytes memory adaptorData,
bytes memory configurationData
) public view override returns (uint256) {
(, ERC20 token, CurveGauge gauge) = abi.decode(adaptorData, (CurvePool, ERC20, CurveGauge));
(, ERC20 token, CurveGauge gauge, bytes4 selector) = abi.decode(
adaptorData,
(CurvePool, ERC20, CurveGauge, bytes4)
);
bool isLiquid = abi.decode(configurationData, (bool));
if (isLiquid) {
return token.balanceOf(msg.sender) + gauge.balanceOf(msg.sender);
if (isLiquid && selector != bytes4(0)) {
uint256 gaugeBalance = address(gauge) != address(0) ? gauge.balanceOf(msg.sender) : 0;
return token.balanceOf(msg.sender) + gaugeBalance;
} else return 0;
}

Expand All @@ -115,7 +135,8 @@ contract CurveAdaptor is BaseAdaptor, CurveHelper {
*/
function balanceOf(bytes memory adaptorData) public view override returns (uint256) {
(, ERC20 token, CurveGauge gauge) = abi.decode(adaptorData, (CurvePool, ERC20, CurveGauge));
return token.balanceOf(msg.sender) + gauge.balanceOf(msg.sender);
uint256 gaugeBalance = address(gauge) != address(0) ? gauge.balanceOf(msg.sender) : 0;
return token.balanceOf(msg.sender) + gaugeBalance;
}

/**
Expand Down Expand Up @@ -158,7 +179,7 @@ contract CurveAdaptor is BaseAdaptor, CurveHelper {

uint256 lpValueIn = Cellar(address(this)).priceRouter().getValues(tokens, orderedTokenAmounts, token);
uint256 minValueOut = lpValueIn.mulDivDown(curveSlippage, 1e4);
if (balanceDelta < minValueOut) revert(":(0");
if (balanceDelta < minValueOut) revert CurveAdaptor___Slippage();

for (uint256 i; i < tokens.length; ++i)
if (orderedTokenAmounts[i] > 0) _revokeExternalApproval(tokens[i], pool);
Expand Down Expand Up @@ -197,7 +218,7 @@ contract CurveAdaptor is BaseAdaptor, CurveHelper {
for (uint256 i; i < tokens.length; ++i) if (address(tokens[i]) == CURVE_ETH) tokens[i] = ERC20(nativeWrapper);
uint256 lpValueIn = Cellar(address(this)).priceRouter().getValues(tokens, orderedTokenAmounts, token);
uint256 minValueOut = lpValueIn.mulDivDown(curveSlippage, 1e4);
if (lpOut < minValueOut) revert(":(1");
if (lpOut < minValueOut) revert CurveAdaptor___Slippage();

for (uint256 i; i < tokens.length; ++i) {
if (address(tokens[i]) == CURVE_ETH) _revokeExternalApproval(ERC20(nativeWrapper), addressThis);
Expand Down Expand Up @@ -226,7 +247,7 @@ contract CurveAdaptor is BaseAdaptor, CurveHelper {

uint256 lpValueOut = Cellar(address(this)).priceRouter().getValues(tokens, balanceDelta, token);
uint256 minValueOut = lpTokenAmount.mulDivDown(curveSlippage, 1e4);
if (lpValueOut < minValueOut) revert(":(2");
if (lpValueOut < minValueOut) revert CurveAdaptor___Slippage();

_revokeExternalApproval(token, pool);
}
Expand Down Expand Up @@ -256,7 +277,7 @@ contract CurveAdaptor is BaseAdaptor, CurveHelper {
for (uint256 i; i < tokens.length; ++i) if (address(tokens[i]) == CURVE_ETH) tokens[i] = ERC20(nativeWrapper);
uint256 lpValueOut = Cellar(address(this)).priceRouter().getValues(tokens, tokensOut, token);
uint256 minValueOut = lpTokenAmount.mulDivDown(curveSlippage, 1e4);
if (lpValueOut < minValueOut) revert(":(3");
if (lpValueOut < minValueOut) revert CurveAdaptor___Slippage();

_revokeExternalApproval(token, addressThis);
}
Expand Down
5 changes: 4 additions & 1 deletion src/modules/adaptors/Curve/CurveHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -218,10 +218,13 @@ contract CurveHelper {
CellarWithOracle(caller).sharePriceOracle();
}

// TODO move into a seperate contract since convex adaptor would use this as well.
// TODO should this really restrict what can be called?
function _callReentrancyFunction(CurvePool pool, bytes4 selector) internal {
if (selector == CurvePool.claim_admin_fees.selector) pool.claim_admin_fees();
else if (selector == CurvePool.withdraw_admin_fees.selector) pool.withdraw_admin_fees();
else if (selector == bytes4(keccak256(abi.encodePacked("price_oracle()")))) pool.price_oracle();
else if (selector == bytes4(CurvePool.get_virtual_price.selector)) pool.get_virtual_price();
else revert("unknown selector");
}

/**
Expand Down
Loading

0 comments on commit 8580f68

Please sign in to comment.