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:

  1. Unit Tests: Test each function in isolation

  2. Integration Tests: Test interaction with the actual protocol

  3. Security Tests: Test for common vulnerabilities

  4. Gas Optimization: Measure gas usage for key operations

  5. Stress Tests: Test with various token amounts and market conditions

Documentation Requirements

For each new protocol adapter, provide:

  1. Protocol Overview: Brief description of the protocol

  2. Implementation Details: Key functions and design decisions

  3. Usage Examples: Code snippets showing how to use the adapter

  4. Security Considerations: Protocol-specific security concerns

  5. APY Calculation: How yield is calculated and harvested

  6. 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:

  1. Code Review: At least two team members must review the code

  2. Security Audit: External audit for complex protocols

  3. Testnet Deployment: Test on a testnet before mainnet

  4. Gradual Rollout: Start with low exposure limits

  5. Monitoring Period: Monitor performance for at least 2 weeks

Maintenance and Updates

Protocol adapters require ongoing maintenance:

  1. Regular Updates: Keep up with protocol changes

  2. Performance Monitoring: Track APY and gas costs

  3. Risk Assessment: Regularly review risk parameters

  4. Bug Fixes: Promptly address any issues

  5. Feature Enhancements: Add new features as protocols evolve