Adding New Protocols to Yieldinator
This guide outlines the process for integrating new DeFi protocols with the Yieldinator Facet through the adapter pattern.
Overview
The Yieldinator Facet is designed to be extensible, allowing for easy integration of new yield-generating protocols. This is achieved through a standardized adapter interface that abstracts away protocol-specific details while providing a consistent API for the Yieldinator Facet to interact with.
Adapter Implementation Process
1. Create a New Adapter Contract
Start by creating a new contract that inherits from YieldinatorAdapter:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./YieldinatorAdapter.sol";
// Import protocol-specific interfaces
interface IProtocolContract {
// Protocol-specific functions
}
contract NewProtocolAdapter is YieldinatorAdapter {
using SafeERC20 for IERC20;
// Protocol-specific state variables
constructor(address _admin) YieldinatorAdapter("Protocol Name", _admin) {
// Initialize protocol-specific variables
}
// Implement required interface functions
// ...
}2. Implement Required Interface Functions
At a minimum, you must implement the following functions:
function deposit(address _token, uint256 _amount)
external
override
onlyRole(YIELDINATOR_ROLE)
nonReentrant
returns (bool);
function withdraw(address _token, uint256 _amount)
external
override
onlyRole(YIELDINATOR_ROLE)
nonReentrant
returns (bool);
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 getTotalDeposited(address _token)
external
view
override
returns (uint256);3. Add Protocol-Specific Functionality
Implement protocol-specific functions for:
Asset registration
Protocol interaction
Reward handling
APY calculation
Error handling
4. Implement Security Measures
Ensure your adapter includes:
Non-reentrancy guards on all external calls
Role-based access control
Input validation
Error handling
Safe token transfers using SafeERC20
5. Optimize for Gas Efficiency
Consider the following optimizations:
Minimize external calls
Batch operations where possible
Use efficient storage patterns
Optimize approval management
Integration with Yieldinator Facet
1. Deploy the Adapter
Deploy the adapter contract with appropriate constructor parameters:
const newAdapter = await NewProtocolAdapter.deploy(adminAddress);
await newAdapter.deployed();2. Register the Adapter with Yieldinator
Call the registerAdapter function on the Yieldinator Facet:
await yieldinatorFacet.registerAdapter(
"Protocol Name",
newAdapter.address,
[supportedTokens]
);3. Configure Risk Parameters
Set appropriate risk parameters for the new protocol:
await yieldinatorFacet.setProtocolRiskLevel(
newAdapter.address,
riskLevel // 1-10 scale
);4. Register Assets
For each asset supported by the protocol, register it with the adapter:
await newAdapter.registerAsset(
assetAddress,
protocolSpecificParameters
);Testing Requirements
Before integrating a new protocol adapter, ensure comprehensive testing:
Unit Tests: Test each function in isolation
Integration Tests: Test interaction with the actual protocol
Security Tests: Test for common vulnerabilities
Gas Optimization: Measure gas usage for key operations
Stress Tests: Test with various token amounts and market conditions
Documentation Requirements
For each new protocol adapter, provide:
Protocol Overview: Brief description of the protocol
Implementation Details: Key functions and design decisions
Usage Examples: Code snippets showing how to use the adapter
Security Considerations: Protocol-specific security concerns
APY Calculation: How yield is calculated and harvested
Integration Requirements: Required contracts and parameters
Example: Implementing a Basic Lending Protocol Adapter
Here's a simplified example of implementing an adapter for a basic lending protocol:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./YieldinatorAdapter.sol";
interface ILendingPool {
function deposit(address asset, uint256 amount) external;
function withdraw(address asset, uint256 amount) external returns (uint256);
function getReserveData(address asset) external view returns (uint256 liquidityRate);
}
contract BasicLendingAdapter is YieldinatorAdapter {
using SafeERC20 for IERC20;
ILendingPool public lendingPool;
mapping(address => uint256) public depositedAmount;
constructor(address _lendingPool, address _admin) YieldinatorAdapter("Basic Lending", _admin) {
lendingPool = ILendingPool(_lendingPool);
}
function deposit(address _token, uint256 _amount)
external
override
onlyRole(YIELDINATOR_ROLE)
nonReentrant
returns (bool)
{
IERC20(_token).safeTransferFrom(msg.sender, address(this), _amount);
IERC20(_token).safeApprove(address(lendingPool), _amount);
lendingPool.deposit(_token, _amount);
depositedAmount[_token] += _amount;
emit Deposited(_token, _amount);
return true;
}
// Implement other required functions...
}Approval Process
New protocol adapters should go through the following approval process:
Code Review: At least two team members must review the code
Security Audit: External audit for complex protocols
Testnet Deployment: Test on a testnet before mainnet
Gradual Rollout: Start with low exposure limits
Monitoring Period: Monitor performance for at least 2 weeks
Maintenance and Updates
Protocol adapters require ongoing maintenance:
Regular Updates: Keep up with protocol changes
Performance Monitoring: Track APY and gas costs
Risk Assessment: Regularly review risk parameters
Bug Fixes: Promptly address any issues
Feature Enhancements: Add new features as protocols evolve