Skip to content

SteerProtocol/vault-template

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

3 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Steer Vault Template

Template repository for building ERC-4626 vaults that integrate with Steer Protocol infrastructure.

🎯 What This Template Provides

βœ… Interface Contracts (Required)

  • IVaultManaged.sol - Core vault interface with manager execution function
  • IVaultInitializable.sol - Required initialization signature
  • IVaultRegistry.sol - Registry interface

βœ… Mock Infrastructure (For Testing)

  • MockOrchestrator.sol - Simulates Steer orchestrator
  • MockRegistry.sol - Simulates vault registry
  • MockERC20.sol - Test token

βœ… Example Implementation

  • MinimalVault.sol - Complete working example showing integration points
  • Note: This is a REFERENCE IMPLEMENTATION - you should implement your own strategy

βœ… Tests

  • Complete test suite showing integration requirements
  • Tests for ERC-4626 compliance
  • Tests for manager functions
  • Tests for governance functions

πŸš€ Quick Start

1. Installation

# Clone this template
git clone <this-repo>
cd vault-template

# Install dependencies
forge install OpenZeppelin/openzeppelin-contracts@v5.0.0
forge install OpenZeppelin/openzeppelin-contracts-upgradeable@v5.0.0

# Build
forge build

# Run tests
forge test

2. Verify Setup

# Should see all tests passing
forge test -vv

# Expected output:
# [PASS] test_Deposit_Success()
# [PASS] test_Tend_OnlyManager()
# [PASS] test_Initialize_Success()
# etc...

πŸ“š Building Your Vault

Step 1: Understand the Requirements

Read the integration guide: ../docs/STEER-VAULT-INTEGRATION-GUIDE.md

Required:

  • βœ… Implement IVaultManaged (ERC-4626 + tend function)
  • βœ… Implement IVaultInitializable (standard initialization)
  • βœ… Manager-only access control on tend()

Optional but Recommended:

  • Pausable functionality
  • Governance functions
  • Monitoring view functions

Step 2: Create Your Vault

# Create your vault contract
cp src/examples/MinimalVault.sol src/YourVault.sol

Edit YourVault.sol:

contract YourVault is 
    Initializable,
    ERC4626Upgradeable,
    PausableUpgradeable,
    IVaultManaged,
    IVaultInitializable
{
    // 1. Customize initialization parameters
    function initialize(
        address _manager,
        address _orchestrator,
        address _governance,
        bytes memory _params
    ) public initializer {
        // Decode YOUR parameters
        (address _asset, /* your params */) = abi.decode(_params, (/*...*/));
        
        // Your init logic
    }
    
    // 2. Implement YOUR strategy in tend()
    function tend(bytes calldata data) external override onlyManager {
        // Decode YOUR strategy parameters
        (/* your params */) = abi.decode(data, (/*...*/));
        
        // Execute YOUR strategy logic
        // - Rebalance positions
        // - Harvest rewards
        // - Compound yields
        // - Whatever your strategy does
    }
    
    // 3. Override totalAssets() if deploying externally
    function totalAssets() public view override returns (uint256) {
        // Return: idle + deployed to external protocols
        return IERC20(asset()).balanceOf(address(this)) + /* deployed */;
    }
}

Step 3: Create Your Tests

# Create test file
cp test/MinimalVault.t.sol test/YourVault.t.sol

Edit YourVault.t.sol:

  • Import your vault
  • Test your specific strategy logic
  • Verify integration requirements still met

Step 4: Run Tests

# Run all tests
forge test

# Run specific test
forge test --match-contract YourVaultTest

# Run with gas reporting
forge test --gas-report

# Run with coverage
forge coverage

πŸ§ͺ Testing Your Vault

Required Tests (Integration)

These tests verify your vault meets Steer requirements:

function test_Initialize_CorrectSignature() public {
    // Verify initialize() has correct signature
    vault.initialize(manager, orchestrator, governance, params);
}

function test_Tend_OnlyManager() public {
    // Verify only manager can call tend
    vm.prank(notManager);
    vm.expectRevert();
    vault.tend(data);
}

function test_ERC4626_Compliant() public {
    // Verify ERC-4626 functions work
    vault.deposit(assets, receiver);
    vault.withdraw(assets, receiver, owner);
}

function test_Tend_DoesNotBrickWithdrawals() public {
    // Deposit -> Tend -> Withdraw should work
    vault.deposit(1000, user);
    vault.tend(data);
    vault.withdraw(500, user, user); // Should succeed
}

Your Strategy Tests

Add tests specific to your strategy:

function test_YourStrategy_Rebalancing() public {
    // Test your rebalancing logic
}

function test_YourStrategy_Harvesting() public {
    // Test your reward harvesting
}

function test_YourStrategy_EdgeCases() public {
    // Test edge cases specific to your strategy
}

πŸ“– Example Strategies

Example 1: Simple Lending Strategy

function _handleTend(bytes calldata data) internal {
    (address lendingPool, uint256 targetAllocation) = 
        abi.decode(data, (address, uint256));
    
    uint256 idle = IERC20(asset()).balanceOf(address(this));
    
    if (idle > targetAllocation) {
        // Deposit excess to lending pool
        uint256 toDeposit = idle - targetAllocation;
        IERC20(asset()).approve(lendingPool, toDeposit);
        ILendingPool(lendingPool).deposit(toDeposit);
    }
}

function totalAssets() public view override returns (uint256) {
    uint256 idle = IERC20(asset()).balanceOf(address(this));
    uint256 lent = ILendingPool(lendingPool).balanceOf(address(this));
    return idle + lent;
}

Example 2: Multi-Pool Allocation

function _handleTend(bytes calldata data) internal {
    (address[] memory pools, uint256[] memory allocations) = 
        abi.decode(data, (address[], uint256[]));
    
    // Withdraw from all pools
    for (uint i = 0; i < currentPools.length; i++) {
        IPool(currentPools[i]).withdraw(type(uint256).max);
    }
    
    // Deposit to new allocations
    for (uint i = 0; i < pools.length; i++) {
        IERC20(asset()).approve(pools[i], allocations[i]);
        IPool(pools[i]).deposit(allocations[i]);
    }
}

Example 3: Harvest and Compound

function _handleTend(bytes calldata data) internal {
    (bytes memory swapRoute, uint256 minOutput) = 
        abi.decode(data, (bytes, uint256));
    
    // Claim rewards
    IProtocol(protocol).claimRewards();
    
    // Swap rewards to asset
    uint256 rewards = IERC20(rewardToken).balanceOf(address(this));
    IERC20(rewardToken).approve(swapper, rewards);
    uint256 assets = ISwapper(swapper).swap(swapRoute, rewards, minOutput);
    
    // Reinvest
    IERC20(asset()).approve(protocol, assets);
    IProtocol(protocol).deposit(assets);
}

πŸ”§ Customization Points

1. Initialization Parameters

Customize what you need in initialize():

// Simple
abi.encode(address asset, uint256 minDeposit)

// With protocol integration
abi.encode(address asset, address protocol, uint256 minDeposit)

// With multi-pool
abi.encode(address asset, address[] pools, uint256[] weights)

// With helper pattern
abi.encode(address asset, address helper, bytes config)

2. Tend Parameters

Customize what orchestrator sends in tend():

// Simple rebalancing
abi.encode(address[] pools, uint256[] amounts)

// Action-based
abi.encode(uint8 action, bytes actionData)

// Complex routes
abi.encode(bytes[] swapRoutes, uint256[] minOutputs, address[] targets)

3. Additional View Functions

Add monitoring functions for your strategy:

function getPositionValue() external view returns (uint256);
function getAllocations() external view returns (address[] memory, uint256[] memory);
function pendingRewards() external view returns (uint256);

4. Additional Events

Add events for monitoring:

event PositionRebalanced(address[] oldPools, address[] newPools);
event RewardsHarvested(uint256 amount, uint256 compounded);
event AllocationUpdated(address pool, uint256 oldAmount, uint256 newAmount);

πŸŽ“ Best Practices

βœ… Do's

  • βœ… Keep tend() idempotent (safe to call multiple times)
  • βœ… Add comprehensive events for monitoring
  • βœ… Implement pausable for emergencies
  • βœ… Test with MockOrchestrator before mainnet
  • βœ… Document your parameter encoding clearly
  • βœ… Add view functions for position tracking
  • βœ… Handle edge cases (zero balances, failed swaps, etc.)
  • βœ… Use try-catch for external protocol calls
  • βœ… Add reentrancy guards where needed

❌ Don'ts

  • ❌ Don't change initialize() signature
  • ❌ Don't make tend() callable by anyone
  • ❌ Don't brick withdrawals in tend()
  • ❌ Don't forget to update totalAssets() if deploying externally
  • ❌ Don't hardcode addresses (use params)
  • ❌ Don't skip error handling
  • ❌ Don't assume external calls succeed

πŸ“Š Submission Checklist

Before submitting to Steer, verify:

  • Implements IVaultManaged interface
  • Implements IVaultInitializable interface
  • Initialize has correct signature
  • tend() is manager-only
  • ERC-4626 functions work correctly
  • totalAssets() returns idle + deployed
  • All tests pass: forge test
  • Documented parameter encoding
  • Documented strategy in 2-3 sentences
  • Tested with MockOrchestrator
  • Tested with MockRegistry
  • Code is clean and commented

πŸ“ž Support

Documentation

  • πŸ“– Integration Guide: ../docs/STEER-VAULT-INTEGRATION-GUIDE.md
  • πŸ“– Minimal Requirements: ../docs/steer-vault-builder-integration-requirements.md
  • πŸ“– Reference Architecture: ../docs/reference-stability-vault-porting-guide.md

Getting Help

πŸ—οΈ Project Structure

vault-template/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ interfaces/           # Required interfaces
β”‚   β”‚   β”œβ”€β”€ IVaultManaged.sol
β”‚   β”‚   β”œβ”€β”€ IVaultInitializable.sol
β”‚   β”‚   └── IVaultRegistry.sol
β”‚   β”œβ”€β”€ mocks/               # Testing infrastructure
β”‚   β”‚   β”œβ”€β”€ MockOrchestrator.sol
β”‚   β”‚   β”œβ”€β”€ MockRegistry.sol
β”‚   β”‚   └── MockERC20.sol
β”‚   └── examples/            # Reference implementations
β”‚       └── MinimalVault.sol
β”œβ”€β”€ test/
β”‚   └── MinimalVault.t.sol   # Example tests
β”œβ”€β”€ lib/                     # Dependencies (gitignored)
β”œβ”€β”€ foundry.toml            # Foundry configuration
β”œβ”€β”€ remappings.txt          # Import remappings
└── README.md               # This file

πŸ”„ Next Steps

  1. Understand Requirements: Read STEER-VAULT-INTEGRATION-GUIDE.md
  2. Install Dependencies: Run forge install
  3. Study Example: Review MinimalVault.sol
  4. Build Your Vault: Implement your strategy
  5. Test Thoroughly: Run forge test
  6. Submit: Contact integrations@steer.finance

πŸ“„ License

MIT License - See LICENSE file


Ready to build? Start with src/examples/MinimalVault.sol and customize for your strategy!

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published