Overview
The ank-engine module coordinates protocol execution, per-user portfolios, optional gas/fee policies, and per-tick lifecycle. It provides the main orchestration layer for running backtests and simulations.
Engine
pub struct Engine {
pub protocols: IndexMap<String, Box<dyn Protocol>>,
pub portfolios: IndexMap<UserId, Balances>,
pub ts: Timestamp,
pub step_idx: u64,
pub metrics: EngineMetrics,
}
Orchestrates protocols, user portfolios, and optional fee policy.
protocols
IndexMap<String, Box<dyn Protocol>>
Registered protocol instances (by id)
portfolios
IndexMap<UserId, Balances>
User wallets (token → amount e18)
Methods
new
pub fn new(protocols: IndexMap<String, Box<dyn Protocol>>, start_ts: Timestamp) -> Self
Create a new engine starting at start_ts.
protocols
IndexMap<String, Box<dyn Protocol>>
required
Map of protocol id to protocol instance
Initial simulation timestamp
set_fees
pub fn set_fees(&mut self, f: Option<FeesConfig>)
Set or clear the fee configuration. Only available when the fees feature is enabled.
f
Option<FeesConfig>
required
Fee configuration, or None to disable fees
balances_mut
pub fn balances_mut(&mut self, user: UserId) -> &mut Balances
Mutable access to a user’s balances (creates an empty wallet if missing).
Mutable reference to user’s balances
balances
pub fn balances(&self, user: UserId) -> Balances
Cloned snapshot of a user’s balances.
Cloned balances (empty if user not found)
balances_ref
pub fn balances_ref(&self, user: UserId) -> Option<&Balances>
Immutable reference to a user’s balances.
Reference to user’s balances, or None if not found
tick
pub fn tick<F>(&mut self, user: UserId, plan: F) -> Result<Vec<Vec<ExecOutcome>>>
where
F: FnMut(EngineCtx, &IndexMap<String, Box<dyn Protocol>>, &IndexMap<UserId, Balances>) -> Vec<TxBundle>
Execute one planning pass: call plan, then execute the produced bundles for user.
The engine first calls on_tick for all protocols, then invokes the planner callback to get bundles, and finally executes them.
User identifier executing actions
Planner callback that produces transaction bundles based on current context
return
Result<Vec<Vec<ExecOutcome>>>
Execution outcomes for each bundle
tick_with_bundles
pub fn tick_with_bundles(
&mut self,
user: UserId,
bundles: Vec<TxBundle>,
) -> Result<Vec<Vec<ExecOutcome>>>
Convenience: run exactly the provided bundles for one user this tick.
User identifier executing actions
Pre-built transaction bundles to execute
return
Result<Vec<Vec<ExecOutcome>>>
Execution outcomes for each bundle
tick_multi
pub fn tick_multi(
&mut self,
plans: Vec<(UserId, Vec<TxBundle>)>,
) -> Result<Vec<Vec<ExecOutcome>>>
Execute bundles for multiple users in one global tick (shared timestamp).
plans
Vec<(UserId, Vec<TxBundle>)>
required
List of (user, bundles) pairs to execute in this tick
return
Result<Vec<Vec<ExecOutcome>>>
All execution outcomes
Supporting Types
EngineCtx
pub struct EngineCtx {
pub ts: Timestamp,
pub step_idx: u64,
}
Execution context passed to planners.
Simulation timestamp (e.g., block index)
EngineMetrics
pub struct EngineMetrics {
pub steps: u64,
pub bundles_submitted: u64,
pub txs_executed: u64,
pub txs_rejected_gas: u64,
}
High-level counters for observability.
Number of ticks processed
Total txs executed successfully
Txs rejected at precharge (fees shortfall)
Fee Configuration (requires fees feature)
FeesConfig
pub struct FeesConfig {
pub gas_token: TokenId,
pub price: GasPricePolicy,
pub on_shortfall: OnShortfall,
}
Full fee configuration.
The token used to pay gas (e.g., native coin)
Behavior when funds are insufficient
GasPricePolicy
pub enum GasPricePolicy {
FixedGwei(u64),
Callback(Arc<dyn Fn(Timestamp) -> u64 + Send + Sync>),
}
How the engine obtains gas price (in gwei).
Fixed gas price in gwei (1e9 wei)
Callback
Arc<dyn Fn(Timestamp) -> u64 + Send + Sync>
Callback: compute gas price (in gwei) for the given timestamp
OnShortfall
pub enum OnShortfall {
RejectTx,
AllowDebt,
}
What to do if the user cannot pay fees.
Reject the tx before execution (only in precharge path)
Allow negative balance on the gas token
Usage Example
use ank_engine::{Engine, EngineCtx};
use ank_exec::TxBundle;
use indexmap::IndexMap;
let protocols = IndexMap::new();
let mut engine = Engine::new(protocols, 0);
// Set initial balances
let user = 1u64;
engine.balances_mut(user).set(TokenId(0), 1_000_000_000_000_000_000u128);
// Execute a tick with a planner
let outcomes = engine.tick(user, |ctx, protocols, portfolios| {
// Your planning logic here
vec![TxBundle::default()]
})?;