Curve Protocol Adapter
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
CurveAdapter.solThe adapter implements the standard YieldinatorAdapter interface with Curve-specific functionality:
contract CurveAdapter is YieldinatorAdapter {
// Curve contracts
address public crvToken;
ICurveMinter public curveMinter;
// Pool configuration
struct PoolConfig {
ICurvePool pool;
ICurveGauge gauge;
uint8 poolSize; // Number of tokens in the pool (2, 3, or 4)
int128 tokenIndex; // Index of the token in the pool
address lpToken; // LP token address
bool useGauge; // Whether to stake in gauge
bool active; // Whether the pool is active
}
// Mapping from token to pool configuration
mapping(address => PoolConfig) public tokenToPool;
// Deposited amounts per token
mapping(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:
function registerPool(
address _token,
address _pool,
address _gauge,
uint8 _poolSize,
int128 _tokenIndex,
address _lpToken,
bool _useGauge
) external onlyRole(ADAPTER_ADMIN_ROLE)Deposit
Deposits tokens into a Curve pool and optionally stakes LP tokens in the gauge:
function deposit(address _token, uint256 _amount) external override onlyRole(YIELDINATOR_ROLE) nonReentrant returns (bool)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:
function withdraw(address _token, uint256 _amount) external override onlyRole(YIELDINATOR_ROLE) nonReentrant returns (bool)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:
function harvestYield(address _token) external override onlyRole(YIELDINATOR_ROLE) nonReentrant returns (uint256)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:
function emergencyWithdraw(address _token) external override onlyRole(YIELDINATOR_ROLE) nonReentrant returns (uint256)APY Calculation
Returns the current APY for a token in Curve:
function getCurrentAPY(address _token) external view override returns (uint256)Note: In a production environment, this would require external data sources to calculate accurately.
Additional Functions
The adapter includes additional Curve-specific functions:
function getClaimableCRV(address _token) external view returns (uint256)Usage Example
// 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);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
useGaugeflag 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.