CortexRails Protocol

The policy layer between intent and financial execution.

CortexRails gives financial protocols and autonomous agents a deterministic onchain policy layer for evaluating asset state, position risk, lifecycle conditions, and action-specific rules before value moves.

1policy surface
3consumer adapters
2Stylus engines
  1. Agent / Protocolproposes an action
  2. Intent{ asset, positionId, action, amount }
  3. CortexRailscanExecute() · onchain policy
  4. DecisionALLOW / LIMIT / REVIEW / BLOCK
  5. Executionconsuming adapter enforces, then moves value

Autonomous agents

Agents propose. CortexRails decides.

Autonomous agents can determine what they want to do. Financial protocols still need a deterministic boundary that decides what they are actually allowed to do.

CortexRails evaluates the requested action against the current asset, position, lifecycle, and policy state before execution.

01 · OffchainAgent intentStructured request: asset, position, action, amount
02 · Onchain readCortexRails policy evaluationPolicy.canExecute()
03 · ResultALLOW / LIMIT / BLOCKdecision · permittedAmount · reason
04 · Onchain writeAdapter executionThe adapter re-checks policy in the same transaction

No model sits in the decision path. The agent layer only translates units and enum names, and the authoritative answer is the onchain canExecute() result. Every adapter calls canExecute() again at execution time, so a stale or skipped evaluation can't bypass policy.

The policy primitive

One call before value moves.

Every consumer asks the same question through the same function. The same policy surface is called before different financial actions.

canExecute() is a view function. It never moves funds or enforces anything itself; the calling contract does both.

ILedgerLinePolicy

function canExecute(
    uint256 assetId,
    uint256 positionId,
    Action  action,     // BORROW | WITHDRAW | TRANSFER | ...
    uint256 amount      // 18-decimal internal units
) external view returns (PolicyResponse memory);

struct PolicyResponse {
    Decision decision;        // ALLOW | LIMIT | REVIEW | BLOCK
    uint256  permittedAmount; // the maximum permitted, never the request
    bytes32  reason;          // "OK", "EXCEEDS_CAPACITY", "RESTRICTED", ...
}

A consumer enforcing it · LendingAdapter.borrow()

PolicyResponse memory response =
    policy.canExecute(assetId, positionId, Action.BORROW, amount);

if (response.decision == Decision.BLOCK) revert PolicyBlocked(response.reason);

uint256 wouldOweTotal = debt[msg.sender] + amount;
if (wouldOweTotal > response.permittedAmount)
    revert ExceedsPermittedAmount(wouldOweTotal, response.permittedAmount);

Reuse

One policy surface. Multiple financial consumers.

Three independent adapter contracts call the same, unmodified Registry, Stylus engines, and Policy. Adding WITHDRAW and then TRANSFER needed no change to the Registry or either engine, just one new Policy branch each.

BORROW

Action.BORROW

CortexRails evaluates borrowing capacity and risk before a lending action executes: position value × collateral factor × risk adjustment, computed by the Stylus engines.

Policy
Lifecycle must be ACTIVE · request compared to capacity
Adapter
Adapter enforces existing debt + request ≤ permitted amount

LedgerLineLendingAdapter

WITHDRAW

Action.WITHDRAW

CortexRails evaluates lifecycle conditions before collateral leaves the vault. The adapter then applies debt safety: remaining capacity must still cover outstanding debt.

Policy
Lifecycle must be ACTIVE · permitted up to the full position
Adapter
Adapter blocks a withdrawal that would under-collateralize debt

LedgerLineVaultAdapter

TRANSFER

Action.TRANSFER

CortexRails evaluates lifecycle conditions before collateral changes owner. Ownership is reassigned in custody; no tokens move.

Policy
Lifecycle must be ACTIVE · permitted up to the full position
Adapter
Adapter blocks the transfer while any debt is outstanding

LedgerLineTransferAdapter

Decision model

Deterministic decisions.

The same state and the same request always produce the same response. LIMIT returns the maximum permitted amount, and CortexRails never quietly reduces the request. The caller decides whether to resubmit.

ALLOW

The action satisfies policy.

LIMIT

The requested amount exceeds the permitted amount. The response carries the maximum permitted.

REVIEW

Reserved in the Decision enum for a manual-review path. No policy branch returns it today.

BLOCK

The action violates policy, for example a non-ACTIVE lifecycle or zero capacity.

Policy simulation · Action.BORROWRuns in your browser · no chain call
Position value$200,000
× Collateral factor70%
× Risk adjustment80%
Borrowing capacity$112,000
$
canExecute(1, positionId, BORROW, 120,000)
lifecycle: ACTIVE
LIMIT
PolicyResponse {
decision: LIMIT
permittedAmount: 112,000
reason: "EXCEEDS_CAPACITY"
}

Same rules and reason codes as LedgerLinePolicy.canExecute() for BORROW, applied to an illustrative $200,000 position. The policy engine at /app reads the deployed contract live.

Policy inputs

Policy is state-aware.

A balance and a price are not the whole story. Tokenized assets carry lifecycle state that can change whether an action should execute at all.

ASSET
Price and multiplier from the Registry, the single state store.
POSITION
Raw collateral balance per (assetId, positionId).
RISK
Collateral factor and risk adjustment, each bounded to ≤ 100%.
LIFECYCLE
ACTIVE · RESTRICTED · CORPORATE_ACTION · SUSPENDED · MATURING · REDEEMABLE · REDEEMED. Any state other than ACTIVE blocks every action with a state-specific reason.
ACTION
BORROW, WITHDRAW and TRANSFER have consumers. INCREASE_LEVERAGE and LIQUIDATE are reserved in the enum with no consumer yet.
STATE + POSITION + RISK + LIFECYCLE + ACTION POLICY DECISION

Agent intent / policy evaluation

An agent asks. CortexRails answers.

An illustrative $200,000 TSLA position under the testnet configuration (70% collateral factor, 80% risk adjustment) has $112,000 of borrowing capacity.

This walkthrough is illustrative. The policy engine at /app runs the same flow against the deployed contract with your connected wallet's real position.

Agent

“Borrow 120,000 USDG against TSLA.”

CortexRails
LIMIT
Requested
$120,000
Permitted
$112,000
Reason
EXCEEDS_CAPACITY
Agent adjusts

“Borrow 112,000 USDG.”

CortexRails
ALLOW
Requested
$112,000
Permitted
$112,000
Reason
OK
Execution

LendingAdapter.borrow() re-evaluates policy, checks existing debt, then transfers USDG.

Live deployment

Deployed on Robinhood Chain testnet.

Chain ID 46630. The current testnet deployment demonstrates CortexRails against the real TSLA Stock Token and USDG contracts, while policy state and reference pricing remain operator-configured in the test environment.

Real / deployed
  • Robinhood Chain testnet
  • Real TSLA Stock Token collateral
  • Real USDG borrow asset (6-decimal scaling)
  • Stylus PositionEngine and RiskEngine
  • Registry, Policy, three consumer adapters
  • canExecute() computed live on every call
Operator-configured
  • Policy reference price ($364.27)
  • Lifecycle state
  • Collateral factor and risk adjustment
  • No oracle pushes into the Registry automatically
Not claimed
  • Mainnet or production readiness
  • Decentralized live equity pricing
  • Automatic oracle-to-policy sync
  • Support for every tokenized asset
  • Third-party audit
ContractRoleAddress
LedgerLineRegistryState store0x8850…907c
LedgerLinePolicycanExecute()0x22fA…6F52
PositionEngineStylus (Rust/WASM)0xde83…Ff0F
RiskEngineStylus (Rust/WASM)0xf661…4eC4
LedgerLineLendingAdapterBORROW consumer0x39E0…3C97
LedgerLineVaultAdapterWITHDRAW consumer0xfF7E…5A6b
LedgerLineTransferAdapterTRANSFER consumer0xc5Af…6943
RobinhoodStockTokenAdapterAsset adapter · not wired into Registry0x3A1B…dfb9
TSLA Stock TokenCollateral · 18 decimals0xC9f9…Bd4E
USDGBorrow asset · 6 decimals0x7E95…802F

Every address above has live bytecode on Robinhood Chain testnet. Block numbers, deploy transactions, and the VaultAdapter redeploy history are in DEPLOYMENTS.md.

Architecture

Deciding is separate from executing.

The Registry holds state. Two stateless Stylus contracts compute position value and capacity. Policy turns that into a decision. Adapters custody funds and enforce the decision. The agent layer sits outside this deterministic core.

  1. 01Asset adapterRobinhoodStockTokenAdapter · deployed, not yet wired to Registry
  2. 02Registry / stateLedgerLineRegistry
  3. 03Position engineStylus · position value
  4. 04Risk engineStylus · borrowing capacity
  5. 05PolicycanExecute()
  6. 06Financial adapterLending · Vault · Transfer

Developers

Integrate the decision surface.

Applications and autonomous agents can submit structured action intents to CortexRails and receive the authoritative policy result before execution.

Intent → SDK → CortexRails Policy → Decision

Solidity consumers call canExecute() directly. Offchain code uses @ledgerline/core, a typed viem client in this repo's sdk/ directory. It is not yet published to npm.

TypeScript · sdk/src/agent.ts

import { LedgerLineClient, evaluateAgentIntent, suggestRetryIntent } from "@ledgerline/core";

const client = new LedgerLineClient({ rpcUrl }); // defaults to the testnet deployment

const intent = {
  asset: "TSLA",
  positionId: agentWallet,   // address or raw positionId
  action: "BORROW",          // "BORROW" | "WITHDRAW" | "TRANSFER"
  amount: "120000",          // human units; the SDK scales to 18 decimals
};

const result = await evaluateAgentIntent(client, intent);
// e.g. { decision: "LIMIT", permittedAmount: "112000", reason: "EXCEEDS_CAPACITY", raw, ... }

const retry = suggestRetryIntent(intent, result); // amount = permittedAmount, only on LIMIT

Agents propose. CortexRails decides. Adapters execute.

Evaluate a real position against the deployed policy.