Strategy contracts
The contracts that register, execute, fund and track trading strategies.
Strategy lifecycle
The registry statuses are SHADOW, GUARDIAN_REVIEW, PAPER_TRADING, PENDING_ORACLE_APPROVAL, LIVE, PAUSED and DEPRECATED.
How the pipeline runs today:
- One protocol keeper submits strategies to the registry.
- Guardian nodes vote on the backtest results.
- Candidate selection uses
keccak(timestamp, prevrandao, nonce). It does not use Chainlink VRF. - LIVE status does not move capital. No vault capital is deployed to strategies yet.
- Paper trading is not live yet.
Addresses (Base Sepolia)
Verified addresses are published in the Base Sepolia deployment manifest (15 verified contracts as of the latest testnet release). The entries below are placeholders pending the published manifest reference.
| Contract | Address | Purpose |
|---|---|---|
| StrategyRegistry | 0x3B7CF2663B5155c904E1E325B03b6498C3780e00 | Strategy registration |
| StrategyExecutor | (forthcoming) | Strategy execution |
| AllocationManager | (forthcoming) | Capital allocation |
| PerformanceTracker | (forthcoming) | Performance metrics |
StrategyRegistry
The central registry for all trading strategies.
Strategy structure
struct Strategy {
bytes32 id;
string name;
address creator;
StrategyStatus status;
uint256 riskScore;
uint256 maxAllocation;
uint256 createdAt;
bytes32 dnaHash; // Strategy parameters hash
}
enum StrategyStatus {
NONE,
SHADOW,
GUARDIAN_REVIEW,
PAPER_TRADING,
PENDING_ORACLE_APPROVAL,
LIVE,
PAUSED,
DEPRECATED
}
Key functions
// Register new strategy
function registerStrategy(
string calldata name,
bytes32 dnaHash,
uint256 riskScore
) external onlyRole(ORACLE_ROLE) returns (bytes32 strategyId);
// Promote strategy to next stage
function promoteStrategy(
bytes32 strategyId,
bytes calldata validationProof
) external onlyRole(GUARDIAN_ROLE);
// Deprecate strategy
function deprecateStrategy(
bytes32 strategyId,
string calldata reason
) external onlyRole(ADMIN_ROLE);
// Get strategy info
function getStrategy(bytes32 strategyId) external view returns (Strategy memory);
Events
event StrategyRegistered(bytes32 indexed strategyId, string name, address creator);
event StrategyPromoted(bytes32 indexed strategyId, StrategyStatus newStatus);
event StrategyDeprecated(bytes32 indexed strategyId, string reason);
StrategyExecutor
Executes trades for live strategies.
Execution flow
// Execute strategy trade
function executeTrade(
bytes32 strategyId,
TradeParams calldata params
) external onlyRole(ORACLE_ROLE) returns (bytes32 tradeId);
struct TradeParams {
address tokenIn;
address tokenOut;
uint256 amountIn;
uint256 minAmountOut;
bytes swapData;
uint256 deadline;
}
Risk controls
// Check trade against risk limits
function validateTrade(
bytes32 strategyId,
TradeParams calldata params
) internal view {
Strategy memory strategy = registry.getStrategy(strategyId);
// Check position size
require(
params.amountIn <= strategy.maxAllocation * maxPositionPct / 100,
"Position too large"
);
// Check daily volume
require(
dailyVolume[strategyId] + params.amountIn <= maxDailyVolume,
"Daily volume exceeded"
);
// Check slippage
uint256 expectedOut = oracle.getExpectedOutput(params);
require(
params.minAmountOut >= expectedOut * (100 - maxSlippage) / 100,
"Slippage too high"
);
}
AllocationManager
Manages capital allocation across strategies.
Allocation structure
struct Allocation {
bytes32 strategyId;
uint256 amount;
uint256 percentage;
uint256 lastRebalance;
}
Key functions
// Set strategy allocation
function setAllocation(
bytes32 strategyId,
uint256 percentage
) external onlyRole(ADMIN_ROLE);
// Rebalance allocations
function rebalance() external onlyRole(ORACLE_ROLE);
// Get current allocations
function getAllocations() external view returns (Allocation[] memory);
Rebalancing logic
function rebalance() external onlyRole(ORACLE_ROLE) {
Allocation[] memory allocations = getAllocations();
uint256 totalValue = getTotalValue();
for (uint256 i = 0; i < allocations.length; i++) {
uint256 targetValue = totalValue * allocations[i].percentage / 100;
uint256 currentValue = getStrategyValue(allocations[i].strategyId);
if (currentValue > targetValue * (100 + rebalanceThreshold) / 100) {
// Reduce allocation
_reduceAllocation(allocations[i].strategyId, currentValue - targetValue);
} else if (currentValue < targetValue * (100 - rebalanceThreshold) / 100) {
// Increase allocation
_increaseAllocation(allocations[i].strategyId, targetValue - currentValue);
}
}
}
PerformanceTracker
Tracks and records strategy performance metrics.
Metrics structure
struct PerformanceMetrics {
uint256 totalReturn;
uint256 sharpeRatio;
uint256 maxDrawdown;
uint256 winRate;
uint256 avgTradeSize;
uint256 totalTrades;
uint256 lastUpdate;
}
Key functions
// Record trade result
function recordTrade(
bytes32 strategyId,
int256 pnl,
uint256 tradeSize
) external onlyRole(EXECUTOR_ROLE);
// Get performance metrics
function getMetrics(bytes32 strategyId) external view returns (PerformanceMetrics memory);
// Calculate Sharpe ratio
function calculateSharpe(bytes32 strategyId) external view returns (uint256);
Integration with vaults
Not live. Strategies are designed as the active trading layer (the ATE sleeve). They will connect to vaults through the onchain execution path: ExecutionRouter, StrategyRegistry and BaseRateGovernor. The code below shows the planned design:
// Vault allocates to strategy
function allocateToStrategy(
bytes32 strategyId,
uint256 amount
) external onlyVault {
require(registry.getStrategy(strategyId).status == StrategyStatus.LIVE, "Not live");
allocationManager.allocate(strategyId, amount);
}
// Strategy returns profits to vault
function returnProfits(
address vault,
uint256 amount
) external onlyRole(EXECUTOR_ROLE) {
IERC20(asset).transfer(vault, amount);
emit ProfitsReturned(vault, amount);
}
Security features
Circuit breaker
// Emergency stop for strategy
function emergencyStop(bytes32 strategyId) external onlyRole(GUARDIAN_ROLE) {
strategies[strategyId].status = StrategyStatus.DEPRECATED;
_closeAllPositions(strategyId);
emit EmergencyStop(strategyId, msg.sender);
}
Position limits
| Limit | Value |
|---|---|
| Max Position Size | 10% of strategy allocation |
| Max Daily Volume | 50% of strategy allocation |
| Max Slippage | 2% |
| Max Drawdown | 20% (triggers Guardian review and possible emergency stop) |
Last updated: June 2026