Conduit Architecture
Conduits are the primary user-facing layer in Railnet. They wrap a single STEAM Vehicle with an ERC20 share token, providing familiar DeFi interactions (deposit, withdraw, transfer shares) while the underlying Vehicle handles protocol-specific complexity.
Think of it as:
- Vehicle: The engine (executes protocol-specific logic like Aave supplies or ERC-7540 redeem requests)
- Conduit: The dashboard (provides a familiar interface — ERC20 shares, fees, access control)
Users interact with Conduits; Conduits interact with Vehicles on their behalf.
The Share Token Model
A Conduit issues its own ERC20 shares (conduit shares) to represent ownership. Internally, the Conduit holds shares issued by the underlying Vehicle.
The conversion chain:
Conduit Shares ↔ Vehicle Shares ↔ Underlying Assets As the underlying Vehicle earns yield, Vehicle share value increases → Conduit’s totalAssets() increases → each conduit share is worth more. Users never touch Vehicle shares directly.
Key Components
When building with Conduits, there are four main integration areas:
- Vault Operations: Deposit and withdraw through
create()andprocess(). Conduit handles asset approval, Vehicle interaction, and share minting/burning. - Yield Tracking: Monitor share price appreciation via
totalAssets()andholdings(). Useconvert()for pure asset-to-share conversion,estimate()for conversion including fees. - Access Control: Optional AccountList module provides allowlist/blocklist for who can deposit and receive shares. A
transferEnabledflag controls share transferability. - Fee Management: Optional FeeManager integration supports management fees (time-based), performance fees (high water mark), deposit fees (entry), and redeem fees (exit).
Access control and fees are both optional modules chosen at deployment. They are covered in detail in Fees & Modules.
The Create → Process Lifecycle
Conduits simplify the STEAM lifecycle into two user-facing operations:
Create
When a user calls create(query, receiver):
- Validation: Conduit checks the receiver, ensures it’s enabled, verifies sender is allowed (via AccountList if configured).
- Asset Pull: Pulls assets from
msg.sender— underlying tokens for deposits, conduit shares for redeems. - Vehicle Interaction: Approves the Vehicle and calls
vehicle.create(). - Fee Capture: Captures current fee configuration to ensure consistent fee application at settlement.
- Auto-Processing: If
receiver == msg.sender, automatically attempts to process immediately.
Process
The process(query) function advances the query through STEAM:
- Fee Accrual: Accrues management and performance fees by minting shares to FeeManager.
- State Check: Checks current state in the Vehicle.
- PAUSED: Calls vehicle.resume() to continue.
- UNLOCKING: Calls vehicle.unlock(), mints conduit shares (deposits) or transfers assets (redeems), applies transactional fees.
- RECOVERING: Calls vehicle.recover() to refund assets or restore shares.
Deposit Flow
Step-by-step deposit:
User deposits 1,000 USDC into Conduit
│
├─ 1. Conduit pulls 1,000 USDC from user
│
├─ 2. Conduit approves Vehicle, calls vehicle.create(DEPOSIT)
│
├─ 3a. Synchronous Vehicle (e.g., Aave):
│ → Query reaches UNLOCKING immediately
│ → Conduit calls vehicle.unlock()
│ → Conduit mints shares to user (minus deposit fee if any)
│ → Done in one transaction
│
└─ 3b. Asynchronous Vehicle (e.g., ERC7540):
→ Query enters PROCESSING
→ User (or Keeper) must call process() later
→ When ready: UNLOCKING → unlock → mint shares Withdrawal Flow
Step-by-step redeem:
User redeems 1,000 conduit shares
│
├─ 1. Conduit pulls 1,000 shares from user
│
├─ 2. Conduit calls vehicle.create(REDEEM)
│
├─ 3. Vehicle redeems underlying assets
│
├─ 4. Conduit applies redeem fee (if any)
│
└─ 5. Conduit transfers base assets to user
Conduit shares are burned Conduit vs Direct Vehicle vs MultiVehicle
| Aspect | Direct Vehicle | Conduit | MultiVehicle |
|---|---|---|---|
| Share Token | Vehicle shares (internal) | Conduit ERC20 shares | MultiVehicle ERC20 shares |
| Fee Management | None | Full suite (mgmt, perf, deposit, redeem) | Full suite via FeeManager |
| Access Control | None | AccountList (allowlist/blocklist) | EAC role-based |
| Multi-Protocol | No | No (wraps single vehicle) | Yes (multiple sub-vehicles) |
| User Experience | Manual query management | Simple create/process | Automated queue-based |
| Best For | Protocol integrations | End-user products | Institutional strategies |
Key Interface Summary
| Method | Description |
|---|---|
create(query, receiver, sourceSalt) | Create deposit or redeem query, auto-processes if possible |
process(query) | Advance query through STEAM lifecycle; returns (queryId, newState) |
holdings() | Total Vehicle shares held by the Conduit |
totalAssets() | Total underlying assets managed |
estimate(asset, mode, estimationType) | Preview output including fees |
convert(asset, sharesToAssets) | Pure conversion without fees |
ready() | Whether the Conduit is open for deposits. Replaces isEnabled(); enable() opens it |
isTransferable(from, to) | Whether a share transfer between these accounts is allowed |
forceRedeem(user, amount, output) | Eject a blocked or sanctioned holder; needs CONDUIT_FORCE_REDEEM |
getVehicle() | The underlying Vehicle contract |
asset() | The underlying ERC20 asset address |
queriesCount() | Total queries created through this Conduit |
Deployment
Conduits are deployed via ConduitFactory.spawn():
- Deploy Conduit proxy via Beacon pattern.
- Initialize with Vehicle, FeeManager, AccountList and the
transferEnabledflag. - Make initial deposit (anti-inflation protection).
- If sync vehicle: initial deposit settles immediately → Conduit enabled.
- If async vehicle: initial deposit stored as
PendingDeposit→ callfinalizeConduitDeposit()after settlement.
Walk it end to end, with a live wallet, in Deploy a Conduit. For the modules
referenced above — FeeManager, AccountList, OwnerRegistry, interceptions and the transferEnabled latch — see Fees & Modules.