Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 38 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,35 +14,39 @@ Best-execution routing across Jupiter Perps, Pacifica, Drift, and Flash Trade.

Your agent decides. Toreva executes. Every action receipted.

## Agentic perps setup
## Establish your wallet

Use `toreva_establish` before perps execution when an agent needs delegated
authority for a human wallet. The standard perps pattern is:
Use `toreva_establish` to attach a policy-controlled delegated authority to
your Solana wallet. Your wallet stays the root owner — Toreva creates a bounded
session key that enforces spend caps, allowed-token constraints, and revocation
policy. Non-custodial: Toreva never holds private key material. Every
establishment is receipted.

```text
human wallet
-> Toreva/Swig master authority
-> venue-specific child capability
-> Pacifica API agent wallet when Pacifica is selected
```bash
# Minimum — attach delegated authority to your wallet
toreva_establish({ walletAddress: "your-wallet-address" })
```

The human wallet remains the root owner. The Swig authority is the policy and
capital-management control layer. Pacifica uses a separate API agent wallet
because Pacifica REST orders require an on-curve Ed25519 signer. Toreva can
create, bind, fund, route, monitor, and revoke that child capability through
Toreva surfaces; the user does not need to open Pacifica.
Once established, your agent can execute across all supported primitives —
earn, perps, and more — without re-authenticating for each operation.

For best execution, omit `venue` on `toreva_perps_long` or
`toreva_perps_short`. Toreva will compare enabled venues and route by estimated
all-in cost. Set `venue` only when you intentionally want a specific venue.
Perps tools use the Gateway MCP field contract: `walletAddress`, `token`,
`sizeUsd`, `leverage`, `collateralToken`, and `collateralAmount`.
## Earn

The public integration packet lives in this repo:
Deploy idle USDC to yield across supported venues with `toreva_earn`. Scan,
compare, and execute from a single tool.

- [Agentic perps integration patterns](./docs/agentic-perps-integration-patterns.md)
- [OpenAPI-style relay examples](./docs/toreva-perps.openapi.json)
- [Claude Code agent prompt](./docs/claude-code-agent-prompt.md)
```bash
npx toreva earn-compare --asset USDC --venue kamino
npx toreva earn-compare --asset USDC --venue marginfi
```

| Venue | Asset |
| --- | --- |
| Kamino Finance | USDC |
| Marginfi | USDC |

Every earn execution returns a read-evidence receipt, a venue-intelligence
receipt, and a sentinel review receipt.

## Install

Expand Down Expand Up @@ -114,6 +118,18 @@ truth for agent, SDK, CLI, Skills, and MCP integration details.

## Perps tools

Run `toreva_establish` first to attach a delegated authority before execution.
For best fill, omit `venue` — Toreva compares enabled venues and routes by
estimated all-in cost. Set `venue` only when you intentionally want a specific
venue. Perps tools use fields: `walletAddress`, `token`, `sizeUsd`, `leverage`,
`collateralToken`, and `collateralAmount`.

The public integration packet lives in this repo:

- [Agentic perps integration patterns](./docs/agentic-perps-integration-patterns.md)
- [OpenAPI-style relay examples](./docs/toreva-perps.openapi.json)
- [Claude Code agent prompt](./docs/claude-code-agent-prompt.md)

| Tool | Fee | What it does |
| --- | --- | --- |
| `toreva_perps_long` | 1 bps | Open long — routes to better fill |
Expand Down
77 changes: 77 additions & 0 deletions data/founder-narrative/rollback-path-schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
{
"schema_version": "kit.rollback_path.v1",
"description": "Canonical rollback_path field definition for action packets. Describes how a user cancels or recovers without Toreva involvement.",

"schema": {
"rollback_path": {
"type": "object",
"required": ["method", "description", "non_custodial_note", "recovery_evidence_ref", "attested_by"],
"properties": {
"method": {
"type": "string",
"enum": ["user_initiated_withdrawal", "app_cancel", "timeout_expiry"],
"description": "Mechanism by which the action can be undone or funds recovered."
},
"description": {
"type": "string",
"description": "Human-readable summary of how the user cancels or recovers. Must be true without Toreva online."
},
"non_custodial_note": {
"type": "string",
"const": "Toreva does not hold private keys. User retains signing authority.",
"description": "Non-custodial invariant confirmation. Value is fixed."
},
"recovery_evidence_ref": {
"type": "string",
"description": "File path (relative to repo root) or URL to the kit recovery script or equivalent attestable artefact."
},
"attested_by": {
"type": "string",
"description": "Agent that verified non-custodial recoverability by reviewing the recovery_evidence_ref. Leave 'pending' until review complete.",
"examples": ["kit-agent", "risk-agent", "pending"]
}
}
}
},

"invariants": [
"rollback_path.method must be exercisable without Toreva being online or in the loop.",
"rollback_path.non_custodial_note value is fixed and must not be altered.",
"attested_by must not be a live agent name until recovery_evidence_ref contains no TODO placeholders.",
"For any action_packet labelled ready-to-execute, attested_by must be a named agent (not 'pending')."
],

"method_guidance": {
"user_initiated_withdrawal": "User calls venue protocol directly (e.g. Pacifica close-position + withdraw). Suitable for perps positions. Requires working recovery script with no TODOs.",
"app_cancel": "User taps 'Cancel' in Toreva app before execution is dispatched on-chain. Toreva must not have yet submitted the transaction. Suitable for pre-signed but unbroadcast actions.",
"timeout_expiry": "Action packet has an on-chain expiry; if user takes no action the packet lapses. Suitable for limit/conditional orders."
},

"populated_example": {
"_context": "DeFi rebalance recommendation — research/backtested variant (no live fund movement yet)",
"rollback_path": {
"method": "user_initiated_withdrawal",
"description": "User can close all Pacifica perp positions and withdraw margin directly via Pacifica's native API using only their wallet keypair. No Toreva endpoint is required. Steps: (1) run kit/examples/recovery/recovery.ts with walletKeypair + any public Solana RPC; (2) script closes open positions on-chain; (3) script withdraws margin balances to wallet. Toreva need not be online at any step.",
"non_custodial_note": "Toreva does not hold private keys. User retains signing authority.",
"recovery_evidence_ref": "kit/examples/recovery/recovery.ts",
"attested_by": "pending"
},
"_attestation_blocker": "recovery.ts contains TODO placeholders for PACIFICA_PROGRAM_ID, authenticateWithPacifica, buildPacificaCloseInstruction, and buildPacificaWithdrawInstruction. kit-agent must replace these with authoritative Pacifica values before attested_by can be set to a live agent name. See kit/examples/recovery/recovery.ts inline TODOs."
},

"attestation_gate": {
"preconditions_for_attestation": [
"PACIFICA_PROGRAM_ID replaced with verifiable mainnet program ID (checkable on Solana explorer).",
"authenticateWithPacifica implements Pacifica's documented auth-challenge flow (GET /auth/challenge → sign nonce → POST /auth/verify or equivalent).",
"buildPacificaCloseInstruction builds from Pacifica's published IDL or SDK — no placeholder throws.",
"buildPacificaWithdrawInstruction same as above.",
"Script compiles with pnpm typecheck.",
"At least one integration test or a manual dry-run trace showing the auth + position-query path succeeds without Toreva endpoints."
],
"attestation_class": "not-class-a",
"attestation_class_rationale": "Pacifica program IDs are public on-chain data. Implementing against a public DEX protocol's documented API does not cross custody, revenue, legal, or product-model gates. Research step only — requires consulting Pacifica's public docs or on-chain registry."
},

"updated_at": "2026-06-05T00:00:00Z",
"authored_by": "kit-agent"
}
153 changes: 153 additions & 0 deletions examples/recovery/recovery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/**
* recovery.ts — Toreva-independent custody recovery script (skeleton).
*
* Path: kit/examples/recovery/recovery.ts
*
* Purpose: prove that an integrator can withdraw margin and close perp
* positions on Pacifica using ONLY their wallet keypair + Pacifica's
* native API, with Toreva entirely offline. This is the artefact behind
* prospect packet §2.3 ("pull-the-plug test").
*
* Bus-first note: this is engineering code, not a kit-agent claim.
* Authoring + reviewing + merging follow standard PR flow. Risk-agent
* audits the source post-merge to verify "no Toreva-side state required
* to recover funds" before publishing risk.attestation.custody_recoverable.
*
* Status: SKELETON — not yet attested.
* Blockers before attestation:
* - Replace PACIFICA_PROGRAM_ID with authoritative mainnet program ID
* - Implement authenticateWithPacifica per Pacifica's auth-challenge docs
* - Implement buildPacificaCloseInstruction using Pacifica's IDL/SDK
* - Implement buildPacificaWithdrawInstruction using Pacifica's IDL/SDK
* - Run pnpm typecheck + at least one dry-run trace against devnet
* Once all TODOs are resolved, risk-agent reviews and sets attested_by
* in kit/data/founder-narrative/rollback-path-schema.json.
*/

import { Connection, Keypair, PublicKey, Transaction } from '@solana/web3.js';

// ─── TODO(kit): replace with Pacifica's authoritative constants ──────────
// Pacifica program ID (Solana mainnet) — verify against Pacifica docs or on-chain
const PACIFICA_PROGRAM_ID = new PublicKey('REPLACE_WITH_PACIFICA_MAINNET_PROGRAM_ID');
// Pacifica's native HTTP API root (for off-chain queries — NOT Toreva)
const PACIFICA_API_URL = 'https://api.pacifica.fi';
// ─────────────────────────────────────────────────────────────────────────

export interface RecoveryConfig {
/** User's wallet keypair — the ONLY signing authority */
walletKeypair: Keypair;
/** Solana RPC endpoint — public RPC works; user can supply their own */
rpcUrl: string;
/** Optional override for Pacifica API root (default: production) */
pacificaApiUrl?: string;
}

export interface RecoveryResult {
closedPositions: Array<{ positionId: string; closeTx: string }>;
withdrawals: Array<{ token: string; amount: string; withdrawTx: string }>;
/** All tx signatures, in chronological order */
txSignatures: string[];
}

/**
* Recover all funds + close all positions on Pacifica without Toreva.
*
* Steps:
* 1. Connect directly to Pacifica's API (no Toreva intermediary)
* 2. Authenticate by signing Pacifica's challenge with walletKeypair
* 3. Query open positions for walletKeypair.publicKey
* 4. For each position: build close instruction, sign locally, submit
* 5. Query margin balances, build withdraw instructions, sign, submit
* 6. Return all tx signatures for caller to verify on Solana explorer
*
* Failure modes:
* - Pacifica API down: throws PacificaUnavailableError; caller can retry
* - Insufficient SOL for tx fees: throws InsufficientFeeBalance (advise top-up)
* - Position cannot be closed (e.g. liquidated already): logged + skipped
*
* Does NOT call: any toreva.com endpoint. Verify with mitmproxy if desired.
*/
export async function recoverFromPacifica(config: RecoveryConfig): Promise<RecoveryResult> {
const apiUrl = config.pacificaApiUrl ?? PACIFICA_API_URL;
const connection = new Connection(config.rpcUrl, 'confirmed');
const wallet = config.walletKeypair.publicKey;

// ─── 1. Authenticate to Pacifica ────────────────────────────────────────
// TODO(kit): implement Pacifica's actual auth challenge flow per their docs.
// Typical pattern: GET /auth/challenge?wallet=<pubkey> → sign nonce → POST /auth/verify.
const sessionToken = await authenticateWithPacifica(apiUrl, config.walletKeypair);

// ─── 2. Query open positions ────────────────────────────────────────────
const positions = await fetch(`${apiUrl}/v1/positions?wallet=${wallet.toBase58()}`, {
headers: { Authorization: `Bearer ${sessionToken}` },
}).then((r) => r.json() as Promise<Array<{ id: string; size: string; market: string }>>);

// ─── 3. Close each position ─────────────────────────────────────────────
const closedPositions: RecoveryResult['closedPositions'] = [];
for (const pos of positions) {
// TODO(kit): build the close-position instruction using Pacifica's IDL/program
const closeInstruction = await buildPacificaCloseInstruction(connection, wallet, pos.id);
const tx = new Transaction().add(closeInstruction);
const blockhash = await connection.getLatestBlockhash('confirmed');
tx.recentBlockhash = blockhash.blockhash;
tx.feePayer = wallet;
tx.sign(config.walletKeypair); // sign LOCALLY — keypair never leaves caller's machine
const sig = await connection.sendRawTransaction(tx.serialize());
await connection.confirmTransaction({ signature: sig, ...blockhash }, 'confirmed');
closedPositions.push({ positionId: pos.id, closeTx: sig });
}

// ─── 4. Query margin balances ───────────────────────────────────────────
const balances = await fetch(`${apiUrl}/v1/balances?wallet=${wallet.toBase58()}`, {
headers: { Authorization: `Bearer ${sessionToken}` },
}).then((r) => r.json() as Promise<Array<{ token: string; amount: string }>>);

// ─── 5. Withdraw each balance ───────────────────────────────────────────
const withdrawals: RecoveryResult['withdrawals'] = [];
for (const bal of balances) {
if (BigInt(bal.amount) <= 0n) continue;
const withdrawInstruction = await buildPacificaWithdrawInstruction(
connection,
wallet,
bal.token,
bal.amount,
);
const tx = new Transaction().add(withdrawInstruction);
const blockhash = await connection.getLatestBlockhash('confirmed');
tx.recentBlockhash = blockhash.blockhash;
tx.feePayer = wallet;
tx.sign(config.walletKeypair);
const sig = await connection.sendRawTransaction(tx.serialize());
await connection.confirmTransaction({ signature: sig, ...blockhash }, 'confirmed');
withdrawals.push({ token: bal.token, amount: bal.amount, withdrawTx: sig });
}

return {
closedPositions,
withdrawals,
txSignatures: [...closedPositions.map((c) => c.closeTx), ...withdrawals.map((w) => w.withdrawTx)],
};
}

// ─── Helpers (TODO(kit): fill in with Pacifica's authoritative spec) ─────

async function authenticateWithPacifica(_apiUrl: string, _kp: Keypair): Promise<string> {
throw new Error('TODO(kit): implement Pacifica auth-challenge per their docs');
}

async function buildPacificaCloseInstruction(
_conn: Connection,
_wallet: PublicKey,
_positionId: string,
) {
throw new Error('TODO(kit): build close-position instruction using Pacifica IDL');
}

async function buildPacificaWithdrawInstruction(
_conn: Connection,
_wallet: PublicKey,
_token: string,
_amount: string,
) {
throw new Error('TODO(kit): build withdraw instruction using Pacifica IDL');
}
21 changes: 21 additions & 0 deletions skills/toreva-earn.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,25 @@

Non-custodial execution primitives for Solana. Best-execution routing across Jupiter Perps, Pacifica, Drift, and Flash Trade. 1 bps to open. Everything else is free.

Use this skill to scan, compare, and deploy idle USDC into yield positions across supported venues.

**Operations**

- `scan` — survey current USDC yield positions
- `simulate` — preview expected yield before execution
- `execute` — deploy USDC to the selected venue

**Supported venues**

| Venue | Asset |
| --- | --- |
| Kamino Finance | USDC |
| Marginfi | USDC |

**Read-only compare** — use `toreva earn-compare` in the CLI or call `toreva_earn`
with `operation: scan` to compare live APYs before committing capital.

Every execution returns a receipt triple: a read-evidence ID, a
venue-intelligence receipt, and a sentinel review receipt.

Execution only — not financial advice.
36 changes: 26 additions & 10 deletions skills/toreva-establish-perps-agent.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,42 @@
# toreva-establish-perps-agent
# toreva-establish-wallet

Non-custodial execution primitives for Solana. Best-execution routing across Jupiter Perps, Pacifica, Drift, and Flash Trade. 1 bps to open. Everything else is free.

Use this before perps execution when an agent needs a delegated authority graph.
Use `toreva_establish` to attach a policy-controlled delegated authority to a
Solana wallet before execution. The wallet holder remains the root owner; Toreva
creates a bounded session key constrained by spend caps, allowed-token lists, and
expiry policy. Non-custodial: Toreva never holds private key material. Every
establishment is receipted and revocable.

Recommended pattern:
**Minimum call**

Provide `walletAddress`. Capabilities and authority options are optional and
default to a safe base policy.

**With earn**

After establishment, use `toreva_earn` to deploy USDC yield across Kamino and
Marginfi without repeated authority setup.

**With perps**

For agents that need a separate on-curve signer (Pacifica REST orders require
an Ed25519 signer), pass a capability for the venue. The recommended pattern:

```text
human wallet
-> Toreva/Swig master authority
-> Toreva delegated authority
-> perps child capability
-> Pacifica API agent wallet if Pacifica is selected
```

The human wallet remains root owner. The Pacifica API agent wallet is a
venue-specific child signer for Pacifica REST orders. It is governed by Toreva
policy, approvals, receipts, monitoring, and revocation.
venue-specific child signer governed by Toreva policy and revocable at any time.

For open-long/open-short, omit `venue` unless the user explicitly asks for one.
Toreva will compare enabled venues and route by estimated all-in cost.
For open-long/open-short, omit `venue` unless the user explicitly requests one.
Toreva compares enabled venues and routes by estimated all-in cost.

Use Gateway MCP fields: `walletAddress` for the human wallet, and for opens
use `token`, `sizeUsd`, `leverage`, `collateralToken`, and `collateralAmount`.
Use Gateway MCP fields: `walletAddress` for the human wallet; for opens use
`token`, `sizeUsd`, `leverage`, `collateralToken`, and `collateralAmount`.

Execution only — not financial advice.
Loading