# Developer Contributions

Polystream is designed with a modular architecture that allows for easy integration of new yield-generating protocols. This is accomplished through the `IProtocolAdapter` interface, enabling developers to add support for any DeFi protocol that provides yield opportunities.

***

### Protocol Adapter Interface

Protocol adapters serve as standardized bridges between Polystream's core functionality and external DeFi protocols. All adapters implement the [`IProtocolAdapter.sol`](https://github.com/polystream-core/polystream-contracts/blob/main/src/adapters/interfaces/IProtocolAdapter.sol) interface.

```solidity
interface IProtocolAdapter {
    function supply(address asset, uint256 amount) external returns (uint256);
    function withdraw(address asset, uint256 amount) external returns (uint256);
    function withdrawToUser(address asset, uint256 amount, address user) external returns (uint256);
    function harvest(address asset) external returns (uint256);
    function convertFeeToReward(address asset, uint256 fee) external;
    function getAPY(address asset) external view returns (uint256);
    function getBalance(address asset) external view returns (uint256);
    function getTotalPrincipal(address asset) external view returns (uint256);
    function isAssetSupported(address asset) external view returns (bool);
    function getProtocolName() external view returns (string memory);
}
```

***

### Implementing a Protocol Adapter

#### Step 1: Create a new adapter contract

Create a new contract file in the `src/adapters` directory that implements the `IProtocolAdapter` interface:

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "./interfaces/IProtocolAdapter.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

/**
 * @title YourProtocolAdapter
 * @notice Adapter for interacting with YourProtocol
 */
contract YourProtocolAdapter is IProtocolAdapter, Ownable {
    // Protocol-specific variables and mappings
    
    // Implementation of interface methods
    // ...
}
```

#### Step 2: Implement key functions

**2.1 Supply Function**

The `supply` function allows users to deposit assets into the external protocol:

```solidity
/**
 * @dev Supply assets to the protocol
 * @param asset The address of the asset to supply
 * @param amount The amount of the asset to supply
 * @return The actual amount supplied
 */
function supply(address asset, uint256 amount) external override returns (uint256) {
    require(isAssetSupported(asset), "Asset not supported");
    require(amount > 0, "Amount must be greater than 0");
    
    // Get initial balance to verify transfer
    uint256 initialBalance = IERC20(asset).balanceOf(address(this));
    
    // Transfer asset from sender to this contract
    IERC20(asset).transferFrom(msg.sender, address(this), amount);
    
    // Verify the transfer
    uint256 receivedAmount = IERC20(asset).balanceOf(address(this)) - initialBalance;
    
    // Update your tracking variables
    totalPrincipal[asset] += receivedAmount;
    
    // Protocol-specific deposit logic
    // Example: depositToProtocol(asset, receivedAmount);
    
    return receivedAmount;
}
```

**2.2 Withdraw Function**

The `withdraw` function allows users to withdraw assets from the protocol:

```solidity
/**
 * @dev Withdraw assets from the protocol
 * @param asset The address of the asset to withdraw
 * @param amount The amount of the asset to withdraw
 * @return The actual amount withdrawn
 */
function withdraw(address asset, uint256 amount) external override returns (uint256) {
    require(isAssetSupported(asset), "Asset not supported");
    require(amount > 0, "Amount must be greater than 0");
    
    // Calculate maximum withdrawal amount
    uint256 maxWithdrawal = totalPrincipal[asset];
    uint256 withdrawAmount = amount > maxWithdrawal ? maxWithdrawal : amount;
    
    // Protocol-specific withdrawal logic
    // Example: uint256 withdrawn = withdrawFromProtocol(asset, withdrawAmount);
    uint256 withdrawn = 0; // Replace with actual implementation
    
    // Update state
    if (withdrawn <= totalPrincipal[asset]) {
        totalPrincipal[asset] -= withdrawn;
    } else {
        totalPrincipal[asset] = 0;
    }
    
    // Transfer withdrawn assets to the sender
    IERC20(asset).transfer(msg.sender, withdrawn);
    
    return withdrawn;
}
```

**2.3 Harvest Function**

The `harvest` function collects and compounds accumulated yield:

```solidity
/**
 * @dev Harvest yield from the protocol
 * @param asset The address of the asset
 * @return The total amount harvested
 */
function harvest(address asset) external override returns (uint256) {
    require(isAssetSupported(asset), "Asset not supported");
    
    // Protocol-specific yield harvesting logic
    // Example:
    // 1. Withdraw all assets
    // 2. Calculate yield as (withdrawn - principal)
    // 3. Claim any reward tokens
    // 4. Potentially swap reward tokens to the asset
    // 5. Redeposit everything
    
    uint256 yieldAmount = 0; // Replace with actual implementation
    
    // Update last harvest timestamp
    lastHarvestTimestamp[asset] = block.timestamp;
    
    return yieldAmount;
}
```

**2.4 APY Calculation Function**

The `getAPY` function provides the current annual percentage yield:

```solidity
/**
 * @dev Get the current APY for an asset
 * @param asset The address of the asset
 * @return The current APY in basis points (1% = 100)
 */
function getAPY(address asset) external view override returns (uint256) {
    require(isAssetSupported(asset), "Asset not supported");
    
    // Protocol-specific APY calculation logic
    // Example: Query protocol's rate, convert to basis points
    
    uint256 apyBps = 0; // Replace with actual implementation
    return apyBps;
}
```

#### Step 3: Implement additional helper functions

Additional functions include:

```solidity
/**
 * @dev Get the current balance in the protocol
 * @param asset The address of the asset
 * @return The current balance
 */
function getBalance(address asset) external view override returns (uint256) {
    require(isAssetSupported(asset), "Asset not supported");
    
    // Protocol-specific balance retrieval
    uint256 balance = 0; // Replace with actual implementation
    return balance;
}

/**
 * @dev Check if an asset is supported
 * @param asset The address of the asset
 * @return True if the asset is supported
 */
function isAssetSupported(address asset) external view override returns (bool) {
    return supportedAssets[asset];
}

/**
 * @dev Get the name of the protocol
 * @return The protocol name
 */
function getProtocolName() external pure override returns (string memory) {
    return "Your Protocol Name";
}

/**
 * @dev Get total principal amount for this asset
 * @param asset The address of the asset
 * @return The total principal amount
 */
function getTotalPrincipal(address asset) external view override returns (uint256) {
    return totalPrincipal[asset];
}

/**
 * @dev Convert fees to rewards in the protocol
 * @param asset The address of the asset
 * @param fee The amount of fee to convert
 */
function convertFeeToReward(address asset, uint256 fee) external override {
    require(isAssetSupported(asset), "Asset not supported");
    require(fee > 0, "Fee must be greater than 0");
    require(fee <= totalPrincipal[asset], "Fee exceeds total principal");
    
    // Reduce the total principal to convert fee to yield
    totalPrincipal[asset] -= fee;
}

/**
 * @dev Withdraw assets from the protocol directly to user
 * @param asset The address of the asset
 * @param amount The amount to withdraw
 * @param user The recipient address
 * @return The actual amount withdrawn
 */
function withdrawToUser(address asset, uint256 amount, address user) external override returns (uint256) {
    // Implementation similar to withdraw but sends assets to user instead of msg.sender
}
```

***

### Example Adapter Implementation: AaveAdapter

The [`AaveAdapter.sol`](https://github.com/polystream-core/polystream-contracts/blob/main/src/adapters/AaveAdapter.sol) provides a comprehensive implementation example for integrating with the Aave protocol:

```solidity
contract AaveAdapter is IProtocolAdapter, Ownable {
    // Aave Pool contract
    IAavePoolMinimal public immutable pool;

    // Optional contracts for reward token harvesting
    IRewardsController public rewardsController;
    IPriceOracleGetter public priceOracle;
    ISyncSwapRouter public syncSwapRouter;

    // Mapping of asset address to aToken address
    mapping(address => address) public aTokens;

    // Supported assets
    mapping(address => bool) public supportedAssets;

    // Protocol name
    string private constant PROTOCOL_NAME = "Aave V3";

    // Tracking initial deposits for profit calculation
    mapping(address => uint256) private initialDeposits;

    // Last harvest timestamp per asset
    mapping(address => uint256) public lastHarvestTimestamp;

    // Add tracking for total principal per asset
    mapping(address => uint256) public totalPrincipal;
    
    // Additional implementation...
}
```

Key implementation patterns from the AaveAdapter:

1. **Asset Management**: Track both principal deposits and yield separately
2. **Yield Harvesting**: Withdraw all assets, calculate yield, claim rewards, and redeposit
3. **Principal Protection**: Ensure withdrawals don't exceed the tracked principal
4. **Reward Handling**: Claim and potentially swap additional reward tokens

### Testing Your Protocol Adapter

Create a test file in the `test` directory to verify your adapter's functionality:

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "forge-std/Test.sol";
import "../src/adapters/YourProtocolAdapter.sol";

contract YourProtocolAdapterTest is Test {
    YourProtocolAdapter public adapter;
    address public testUser;
    address public testAsset;
    
    function setUp() public {
        // Setup adapter and test environment
        adapter = new YourProtocolAdapter(...constructor params...);
        testUser = address(0x1);
        testAsset = address(0x2);
        
        // Mock necessary functions
        // ...
    }
    
    function testSupply() public {
        // Test logic
        // ...
    }
    
    function testWithdraw() public {
        // Test logic
        // ...
    }
    
    function testHarvest() public {
        // Test logic
        // ...
    }
    
    // Additional tests
    // ...
}
```

### Examples

For more comprehensive examples, refer to the existing adapter implementations:

* [`AaveAdapter.sol`](https://github.com/polystream-core/polystream-contracts/blob/main/src/adapters/AaveAdapter.sol): Integrates with Aave for lending-based yield
* [`SyncSwapAdapter.sol`](https://github.com/polystream-core/polystream-contracts/blob/main/src/adapters/SyncSwapAdapter.sol): Integrates with SyncSwap for LP-based yield
