Enforce transaction policies, spending limits, and approval requirements before autonomous agents execute on Robinhood Chain.
The engine01
Every proposed action runs the same fifteen checks in the same order. The same policy, request and state always produce the same decision and the same reason code.
Anything failed, ambiguous, timed-out or unverifiable ends in no signature. Unavailable fees are never zero; a missing policy is never allow-all.
Enforcement02
Fig. 1 · Authorization boundary
Allowed operations continue to the smart account. Operations that cross a threshold wait for a human, unsigned. Denied operations stop at the boundary: no signature, no broadcast, no transaction hash.
Per-transaction, hourly and gas limits are checked against a serialized reservation ledger, not a dashboard counter. Five concurrent requests for the last slot produce one reservation and four denials.
Targets are denied by default. Each allowlisted contract names its permitted 4-byte selectors and whether verified code is required. An explicit deny always overrides an allow.
Signatures come only from a policy-bound signer the smart account recognises. Approvals bind to the exact operation hash, policy version and expiry.
Policy workbench03
The canonical schema and evaluation order, running in your browser on illustrative data. Checks that need a live protected account are listed and explicitly not run.
POLICY WORKBENCH
◆ Simulation · example data · nothing is signed or broadcast
NO EVALUATION YET
Pick a transaction and evaluate it. Submit the same one twice to trigger loop detection, advance the clock to clear cooldowns, or keep allowing swaps until the hourly budget runs out.
Press Evaluate locally to run the queued checks, in this order. Checks marked not run need a live protected account and never run here.
Smart-account enforcement
The protected path is an ERC-4337 account whose validation rejects any operation without a valid Ravelin authorization. Plain EOA forwarding is a convenience, never a boundary.
Integration04
Approval, signing, submission, inclusion and success are separate states. The SDK returns a discriminated union and never treats a pending approval as permission.
agent.ts · proposed @ravelin-wall/sdk◆ pseudocode · not yet runnable
import { ravelin } from "@ravelin-wall/sdk";
const wall = ravelin({
apiKey: process.env.RAVELIN_API_KEY!,
chainId: 4663,
});
const result = await wall.operations.create({
account: "0x1111…1111", // protected ERC-4337 account
to: "0x3333…3333", // allowlisted router
value: 40_000_000_000_000_000n,
data: swapCalldata,
idempotencyKey: crypto.randomUUID(),
});
switch (result.decision) {
case "ALLOW": // authorized; submission tracked separately
await wall.operations.waitForReceipt(result.operationId);
break;
case "REQUIRES_HUMAN_SIGNATURE": // pending; nothing signed yet
await wall.approvals.waitFor(result.approvalId);
break;
case "DENY": // terminal; never broadcast
throw new RavelinDenied(result.reasonCode);
}Evaluation result
ALLOWReal output of the local reference evaluator for the swap above, computed at build time. transaction_hash is null: nothing was broadcast.
response.json · local evaluator
{
"request_id": "rw_req_example",
"decision": "ALLOW",
"reason_code": null,
"policy_id": "policy_trading_001",
"policy_version": 7,
"authorization_id": "rw_auth_1f538f0b952dd299",
"transaction_hash": null,
"retryable": false
}Request lifecycle05
Trust boundaries, the enclave's role, smart-account enforcement and the chain submission path are specified in the architecture document.
Network06
◆ Metrics unavailable
Block height, latency and transaction counts appear here only when backed by real telemetry and a published measurement definition.
Status page →