This page covers the smart contract implementation details. See Glossary.
Why STEAM exists
Traditional vault standards like ERC-4626 assume synchronous operations: deposit assets, receive shares instantly. But many DeFi operations are inherently asynchronous:- Cooldown periods — Ethena requires waiting before unstaking sUSDe
- Withdrawal queues — Syrup processes withdrawals in a FIFO queue
- Multi-step strategies — Multi-Vehicles coordinate across multiple protocols
STEAM is more than an interface — it is a framework for building reliable DeFi infrastructure. By standardizing how time, failure, and complex state are handled, STEAM enables Railnet to orchestrate assets across diverse yield sources under a single operational model.
The Query model
The core of STEAM is the Query — a structured request representing a deposit or redemption operation. Each Query carries its own identity, ownership, and state.keccak256(abi.encode(chainId, vehicleAddress, query)). The salt field ensures that repeated operations with identical parameters produce unique IDs.
The state machine
Every Query moves through a defined set of states. This makes the status of any operation transparent and auditable at all times.States
| State | Description | Terminal |
|---|---|---|
| EMPTY | No query exists yet. Default state before creation. | No |
| PROCESSING | Vehicle received assets, performing protocol operations. | No |
| WAITING | Paused for an external condition (cooldown, oracle, KYC). | No |
| UNLOCKING | Operation succeeded, output assets ready for claim. | No |
| RECOVERING | Error occurred, assets being recovered. | No |
| REJECTED | Query failed, assets returned to owner. | Yes |
| SETTLED | Query complete, assets distributed to receiver. | Yes |
State diagram
Transition types
State transitions are categorized by their trigger mechanism:- Method-driven (synchronous) — Occur within the same transaction as a direct call to a Vehicle method (
create(),resume(),unlock(),recover()) - Protocol-driven (asynchronous) — Triggered by external protocol events, off-chain verification, or settlement delays
The Query lifecycle
Every operation follows three phases:Creation
You call
create() with a Query struct. The Vehicle pulls all input assets from the owner and starts the operation.- For sync protocols (Aave, Compound): the Query transitions directly to UNLOCKING
- For async protocols (Ethena, Syrup): the Query enters PROCESSING — see Sync vs async operations
create() fails for any reason, the entire transaction reverts. No assets are transferred, no query is created.Execution
The Vehicle interacts with the underlying protocol. For async operations, the Query may pass through WAITING if an external condition must be met (cooldown period, oracle update).Once the condition is satisfied,
resume() moves the Query back to PROCESSING, and eventually the protocol signals success (UNLOCKING) or failure (RECOVERING).Lifecycle methods
| Method | Valid from | Transitions to | Description |
|---|---|---|---|
create(query) | EMPTY | PROCESSING or UNLOCKING | Initiates a new query. Pulls input assets. |
resume(query) | WAITING | PROCESSING | Resumes after an external condition is met. |
unlock(query) | UNLOCKING | SETTLED or PROCESSING (partial) | Distributes output assets to receiver. |
recover(query) | RECOVERING | REJECTED or PROCESSING (partial) | Returns input assets after failure. |
Events
Vehicles emit two events to enable off-chain tracking and automation:| Event | Parameters | Emitted when |
|---|---|---|
Created(id, query) | Indexed query ID + full Query struct | A new query is created via create() |
Updated(id, state, possibleNext) | Indexed query ID + new state + array of possible next states | Any state transition occurs |
possibleNext array in the Updated event signals whether the state may change asynchronously:
- Empty array — The state is stable. No further transitions will occur until the owner calls a method (
unlock(),recover(),resume()). - Non-empty array — The state may change asynchronously via protocol-driven transitions. Off-chain systems should monitor for further
Updatedevents.
Transition flows
Synchronous flow (Aave, Compound, ERC-4626)
create(), immediately reaching UNLOCKING. Call unlock() to finalize.
Asynchronous flow (Ethena, Syrup)
Error recovery
recover() to reclaim assets.
Partial settlement
Whenunlock() or recover() cannot fully distribute assets in a single call, the Query returns to PROCESSING instead of reaching a terminal state.
Asset[] showing what was distributed in that step. The cycle repeats until all assets are fully claimed or recovered.
Critical constraints
View methods
Vehicles provide methods to inspect state and simulate operations:| Method | Returns | Description |
|---|---|---|
state(query) | State | Current state of a specific query |
estimate(assets, mode, type) | Asset[] | Expected output including fees (non-binding) |
convert(assets, sharesToAssets) | Asset[] | Pure conversion excluding fees |
error(query) | bytes | Error data for queries in RECOVERING or REJECTED |
asset() | address | The base asset of the vehicle (e.g., USDC) |
routes() | (Route[], Route[]) | Deposit and redeem route combinations |
totalAssets() | uint256 | Total value of all managed assets |
maxDeposit(account) | Asset[] | Maximum deposit amounts per asset for an account |
maxRedeem(account) | Asset[] | Maximum redeemable shares per asset for an account |
ready() | bool | Whether the vehicle accepts new queries |
estimate() provides a best-effort preview, not a commitment. Always implement slippage protection when using estimates for transaction previews.Fee semantics
Theestimate() and convert() methods serve different purposes:
estimate()includes all applicable entry/exit fees, slippage, and operational costsconvert()excludes all fees, providing a pure share-to-asset conversion
create() when assets are pulled. They are not applied during recovery operations, allowing users to reclaim assets without additional charges.
Next steps
Sync vs async operations
How synchronous and asynchronous Vehicles differ in practice
Accounting and flow of funds
How assets move through Multi-Vehicles and Sectors
Create a sync Vehicle
Build a Vehicle for protocols with instant settlement
Create an async Vehicle
Build a Vehicle for protocols with delayed settlement