The Curve adapter enables the Yieldinator Facet to integrate with Curve Finance's liquidity pools and gauges, allowing vaults to earn trading fees and CRV rewards by providing liquidity to Curve's stablecoin and crypto pools.
Overview
Curve Finance is a decentralized exchange optimized for low-slippage swaps between assets that should be priced similarly (e.g., stablecoins or wrapped versions of the same asset). The Curve adapter facilitates deposits into Curve pools, manages LP token balances, stakes in Curve gauges, and harvests yield from trading fees and CRV rewards.
Implementation Details
Contract: CurveAdapter.sol
The adapter implements the standard YieldinatorAdapter interface with Curve-specific functionality:
contractCurveAdapterisYieldinatorAdapter{// Curve contractsaddresspublic crvToken; ICurveMinter public curveMinter;// Pool configurationstructPoolConfig{ ICurvePool pool; ICurveGauge gauge;uint8 poolSize;// Number of tokens in the pool (2, 3, or 4)int128 tokenIndex;// Index of the token in the pooladdress lpToken;// LP token addressbool useGauge;// Whether to stake in gaugebool active;// Whether the pool is active}// Mapping from token to pool configurationmapping(address=> PoolConfig)public tokenToPool;// Deposited amounts per tokenmapping(address=>uint256)public depositedAmount;// Constructor and core functions...}
Key Functions
Pool Registration
Before using the adapter for a specific token, the pool must be registered:
Deposit
Deposits tokens into a Curve pool and optionally stakes LP tokens in the gauge:
The function:
Transfers tokens from the caller to the adapter
Prepares an amounts array based on the pool size (2, 3, or 4 tokens)
Adds liquidity to the Curve pool
If using a gauge, stakes the LP tokens in the gauge
Updates the deposited amount tracking
Withdraw
Withdraws tokens from a Curve pool:
The function:
Calculates how many LP tokens to withdraw based on the virtual price
If using a gauge, withdraws LP tokens from the gauge
Removes liquidity from the pool, receiving the underlying token
Transfers tokens to the caller
Updates the deposited amount tracking
Harvest Yield
Harvests CRV rewards and trading fees:
The function:
If using a gauge, mints CRV rewards via the CurveMinter
Claims any additional rewards from the gauge
Transfers all reward tokens to the caller
Emergency Withdraw
Provides emergency withdrawal functionality:
APY Calculation
Returns the current APY for a token in Curve:
Note: In a production environment, this would require external data sources to calculate accurately.
Additional Functions
The adapter includes additional Curve-specific functions:
Usage Example
Security Considerations
The adapter uses OpenZeppelin's SafeERC20 for token transfers to prevent common ERC20 vulnerabilities.
Non-reentrancy guards protect against reentrancy attacks during external calls.
Role-based access control ensures only authorized addresses can call sensitive functions.
The adapter validates pool configurations to prevent incorrect setups.
Gas Optimization
The adapter supports different pool sizes (2, 3, or 4 tokens) to optimize gas usage for each pool type.
LP tokens are staked in gauges only if the useGauge flag is set, allowing for gas savings when gauges are not needed.
The adapter includes a try/catch pattern for iterating through reward tokens to handle potential failures gracefully.
Integration Requirements
To use the Curve adapter, the following components are required:
The CRV token address
The Curve Minter contract address
Curve pool addresses for the target assets
Curve gauge addresses (if using gauges)
LP token addresses for each pool
Proper role assignments for the Yieldinator Facet
Supported Pool Types
The adapter supports various Curve pool types:
2-token pools (e.g., USDC/USDT)
3-token pools (e.g., DAI/USDC/USDT)
4-token pools (e.g., sUSD/DAI/USDC/USDT)
Each pool requires specific configuration parameters during registration.
function harvestYield(address _token) external override onlyRole(YIELDINATOR_ROLE) nonReentrant returns (uint256)
function emergencyWithdraw(address _token) external override onlyRole(YIELDINATOR_ROLE) nonReentrant returns (uint256)
function getCurrentAPY(address _token) external view override returns (uint256)
function getClaimableCRV(address _token) external view returns (uint256)
// Initialize the adapter
CurveAdapter curveAdapter = new CurveAdapter(crvTokenAddress, curveMinterAddress, adminAddress);
// Register a pool
curveAdapter.registerPool(
daiAddress,
triPoolAddress,
triPoolGaugeAddress,
3, // 3-token pool
0, // DAI is at index 0
triPoolLpTokenAddress,
true // Use gauge for staking
);
// Deposit tokens
curveAdapter.deposit(daiAddress, 1000 * 1e18);
// After some time, harvest yield
uint256 harvestedYield = curveAdapter.harvestYield(daiAddress);
// Withdraw tokens
curveAdapter.withdraw(daiAddress, 500 * 1e18);
// In case of emergency
curveAdapter.emergencyWithdraw(daiAddress);