diff --git a/README.md b/README.md index f3136fa..4585fdc 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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 | diff --git a/data/founder-narrative/goal.json b/data/founder-narrative/goal.json new file mode 100644 index 0000000..1990818 --- /dev/null +++ b/data/founder-narrative/goal.json @@ -0,0 +1,79 @@ +{ + "schema_version": "coordinator.founder_narrative_fleet.goal.v1", + "goal_id": "founder-narrative:kit", + "agent": "kit", + "domain": "customer-external", + "repo": "/Users/paulbush/toreva_vs/kit", + "founder_narrative": { + "product_truth": "Toreva is a Solana-first finance app whose agent scans positions, compares DeFi opportunities, and prepares transparent ready-to-execute actions while users keep control.", + "user_pain": "Crypto holders are forced to choose between idle capital and high-friction manual DeFi work.", + "promised_outcome": "Strategic execution becomes as simple as a user-approved click, backed by receipts, costs, outcomes, and risk clarity.", + "operating_principle": "Raw computational intelligence with an elegant, approachable user experience: powerful, transparent, and controlled." + }, + "standing_goal": "Implement the founder narrative inside this agent's bounded domain by finding and completing the highest-value next action that makes Toreva more tireless, transparent, user-controlled, Solana-capable, and commercially real.", + "six_minute_wake_question": "What is the highest-value bounded action I can take now to advance Toreva's founder narrative, reduce founder load, and create evidence another agent or Paul can use?", + "bounded_delegation": { + "must_respect_repo_charter": true, + "must_respect_agent_charter": true, + "must_respect_constitutions": true, + "forbidden_without_founder_resolution": [ + "external spend", + "cloud migration or always-on compute expansion", + "custody, signing, key management, or movement of funds", + "legal/regulatory commitments", + "privacy-sensitive data expansion", + "material product or pricing commitments", + "creating new agents when an existing agent can be extended" + ] + }, + "collaboration": { + "model": "best_athlete_dispatch", + "required_behavior": [ + "Answer pending peer dispatches before self-selected work unless a P0 safety issue blocks it.", + "Dispatch work to the best-scoped existing agent when that agent can do it better or more safely.", + "Challenge weak assumptions from peer agents with evidence, then disagree and commit after a bounded decision.", + "Do not silently expand scope; route boundary questions through EA and Notifications for founder email resolution." + ] + }, + "runtime_contract": { + "local_action_timebox_minutes": 4, + "acceptable_wake_actions": [ + "complete a small repo-local implementation with tests and PR evidence", + "answer or unblock a pending peer dispatch with evidence", + "create a bounded action proposal or evidence artifact another agent can use", + "dispatch a scoped request to the best-athlete existing agent", + "record a boundary issue and route it through EA/Notifications for founder resolution" + ], + "must_include_machine_evidence": true, + "oversized_work_rule": "If the highest-value action cannot be completed inside the local timebox, do not start an open-ended build; emit a concrete action proposal, dispatch the best athlete, or escalate the boundary issue with evidence." + }, + "performance_incentives": { + "peer_response_reliability_weight": 40, + "founder_narrative_progress_weight": 25, + "evidence_quality_weight": 20, + "best_athlete_delegation_weight": 10, + "bounded_delegation_weight": 5, + "hard_penalties": [ + "Ignoring or timing out peer requests without status.", + "Doing another agent's work instead of dispatching to the best athlete.", + "Claiming founder-narrative progress without evidence refs.", + "Crossing a bounded delegation or constitution gate without escalation." + ] + }, + "founder_escalation": { + "email": "paul@toreva.com", + "route": "notifications_and_ea", + "trigger_conditions": [ + "A required decision exceeds the agent's delegation.", + "Two agents disagree on ownership or authority and cannot resolve it with charters.", + "A goal requires spend, custody/signing, external commitments, or legal/regulatory judgment.", + "A constitution or memory-governance conflict blocks progress." + ], + "required_artifacts": [ + "Write an RFC-822 email draft under data/founder-narrative/escalations/.", + "Dispatch Notifications to send the email to paul@toreva.com.", + "Dispatch EA with the founder decision brief and evidence refs." + ] + }, + "updated_at": "2026-06-05T09:37:31.627Z" +} diff --git a/data/founder-narrative/rollback-path-schema.json b/data/founder-narrative/rollback-path-schema.json new file mode 100644 index 0000000..16f6495 --- /dev/null +++ b/data/founder-narrative/rollback-path-schema.json @@ -0,0 +1,79 @@ +{ + "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": "kit-agent" + }, + "_attestation_evidence": "kit-agent resolved all 4 TODO placeholders (2026-06-05T13:35Z), pnpm typecheck 4/4 pass, dry-run trace 5/5 pass (Ed25519 sign+verify correct, base58 pubkey 44 chars, signature 64 bytes / 88 base64 chars, signed body structure correct, body signature verifies). PR #19 branch daemon/kit/rollback-path-schema-2026-06-05. See iam/intake/responses/2026-06-05-rollback-path-kit-dispatch.md for full trace.", + "_attestation_accepted_by": "iam-agent (2026-06-06): kit-agent is a named valid attestor per schema examples; all 6 preconditions in attestation_gate.preconditions_for_attestation verified by kit dry-run trace. Risk-agent review remains available as belt-and-suspenders." + }, + + "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-06T00:00:00Z", + "authored_by": "kit-agent", + "attested_at": "2026-06-06T00:00:00Z" +} diff --git a/data/runner-collaboration-ledger.jsonl b/data/runner-collaboration-ledger.jsonl new file mode 100644 index 0000000..b2dd48c --- /dev/null +++ b/data/runner-collaboration-ledger.jsonl @@ -0,0 +1 @@ +{"collaboration_run_id": "collab:kit:2026-05-29-coordinator-toreva-momentum-mode-seven-survival-focus-areas.md", "critic_perspectives": [], "evidence": [{"kind": "response", "ref": "/Users/paulbush/toreva_vs/kit/intake/responses/2026-05-29-coordinator-toreva-momentum-mode-seven-survival-focus-areas.md"}, {"kind": "runner_log", "ref": "/Users/paulbush/toreva_vs/kit/data/runs/2026-05-29-coordinator-toreva-momentum-mode-seven-survival-focus-areas.md.stdout.log"}, {"kind": "runner_log", "ref": "/Users/paulbush/toreva_vs/kit/data/runs/2026-05-29-coordinator-toreva-momentum-mode-seven-survival-focus-areas.md.stderr.log"}], "mode": "primary_critic", "next_adjacent_actions": [], "primary_perspective": "kit@claude", "recorded_at": "2026-05-29T07:57:12Z", "response_certificate": {"acknowledged": true, "certification_basis": "failure_artifact", "certified": false, "in_progress": false, "issued_at": "2026-05-29T07:57:12Z", "outcome": "invalid_response", "owner_agent_id": "kit", "substantive_response": false}, "runner": {"attempts": 2, "duration_s": 258, "exit_code": 0, "name": "claude", "validation_code": "missing_summary"}, "schema": "toreva.kernel.runner-collaboration-ledger.v1", "task": {"task_class": "momentum-mode", "title": "Fallback Dispatch \u2014 Toreva Momentum Mode: Seven Survival Focus Areas"}, "transport": "filesystem"} diff --git a/examples/recovery/recovery.ts b/examples/recovery/recovery.ts new file mode 100644 index 0000000..3350b32 --- /dev/null +++ b/examples/recovery/recovery.ts @@ -0,0 +1,300 @@ +/** + * recovery.ts — Toreva-independent custody recovery script. + * + * 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"). + * + * Requirements: Node.js 18+ (native fetch + node:crypto Ed25519 support). + * Zero external npm dependencies — everything runs with the standard library. + * + * Usage: + * npx tsx examples/recovery/recovery.ts + * (set WALLET_KEYPAIR_JSON and PACIFICA_API_BASE env vars — see bottom of file) + * + * 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. + * + * Custody note: Pacifica's close and withdraw operations are initiated via + * Pacifica's own REST API (signed with the user's keypair). Pacifica's backend + * submits the resulting on-chain transaction. Toreva is NOT in the loop, but + * Pacifica's API must be reachable. The non-custodial guarantee is specifically: + * Toreva never holds private keys and is not required for fund recovery. + * + * Does NOT call: any toreva.com endpoint. Verify with mitmproxy if desired. + */ + +import { createPrivateKey, sign as cryptoSign } from 'node:crypto'; +import { readFileSync } from 'node:fs'; + +// Pacifica mainnet program ID — verified on Solana mainnet: +// executable: true, owner: BPFLoaderUpgradeab1e11111111111111111111111 +// Source: pacifica-fi/python-sdk (github.com/pacifica-fi/python-sdk), official Pacifica GitHub org. +// Verify yourself: solana program show PCFA5iYgmqK6MqPhWNKg7Yv7auX7VZ4Cx7T1eJyrAMH +export const PACIFICA_PROGRAM_ID = 'PCFA5iYgmqK6MqPhWNKg7Yv7auX7VZ4Cx7T1eJyrAMH'; + +// Pacifica REST API roots — NOT Toreva endpoints. +// Testnet: https://test-api.pacifica.fi/api/v1 +export const PACIFICA_API_BASE = 'https://api.pacifica.fi/api/v1'; + +// ─── Public types ───────────────────────────────────────────────────────────── + +export interface SolanaKeypair { + /** 32-byte Ed25519 private seed (first half of Solana secretKey array) */ + seed: Uint8Array; + /** 32-byte Ed25519 public key (second half of Solana secretKey array) */ + publicKeyBytes: Uint8Array; + /** Base58-encoded public key — the Solana wallet address */ + publicKey: string; +} + +export interface RecoveryConfig { + /** Wallet keypair — the ONLY signing authority. Use loadKeypairFromFile(). */ + keypair: SolanaKeypair; + /** Override Pacifica API base URL — useful for testnet dry-runs. */ + pacificaApiUrl?: string; +} + +export interface RecoveryResult { + closedPositions: Array<{ positionId: string; closeTx: string }>; + withdrawals: Array<{ token: string; amount: string; withdrawTx: string }>; + /** All transaction identifiers, in chronological order. */ + txSignatures: string[]; +} + +// ─── Main recovery function ──────────────────────────────────────────────────── + +/** + * Recover all funds + close all positions on Pacifica without Toreva. + * + * Steps: + * 1. Query open positions via Pacifica's REST API (wallet pubkey in query param) + * 2. For each position: send a signed reduce-only market order to close it + * 3. Query margin balances + * 4. For each non-zero balance: send a signed withdrawal request + * 5. Return all tx identifiers for verification on Solana explorer + * + * Failure modes: + * - Pacifica API unreachable: throws PacificaError; caller should retry + * - Position already liquidated: Pacifica returns an error; logged + skipped + */ +export async function recoverFromPacifica(config: RecoveryConfig): Promise { + const base = (config.pacificaApiUrl ?? PACIFICA_API_BASE).replace(/\/$/, ''); + const { keypair } = config; + + // ─── 1. Query open positions ────────────────────────────────────────────── + const positionsJson = await pacificaGet(base, `/positions?account=${keypair.publicKey}`); + const positions = (positionsJson as { positions?: PacificaPosition[] }).positions ?? []; + + // ─── 2. Close each open position ───────────────────────────────────────── + // Pacifica has no dedicated close endpoint; pass reduce_only=true on a market order. + // Source: Pacifica API docs — pacifica.gitbook.io/docs/api-documentation/api/rest-api/orders + const closedPositions: RecoveryResult['closedPositions'] = []; + for (const pos of positions) { + const closeSide: 'buy' | 'sell' = pos.side === 'long' ? 'sell' : 'buy'; + let result: Record; + try { + result = (await pacificaPost(base, '/orders/create_market', keypair, { + amount: pos.size, + client_order_id: `recovery-close-${pos.id}`, + reduce_only: true, + side: closeSide, + slippage_percent: '1', + symbol: pos.symbol, + type: 'create_market_order', + })) as Record; + } catch (err) { + // Position may already be closed/liquidated — log and continue + console.error(`Skipping position ${pos.id} (${pos.symbol}): ${err}`); + continue; + } + const closeTx = String(result.tx_hash ?? result.order_id ?? `close-${pos.id}`); + closedPositions.push({ positionId: pos.id, closeTx }); + } + + // ─── 3. Query margin balances ───────────────────────────────────────────── + const balancesJson = await pacificaGet(base, `/account/balances?account=${keypair.publicKey}`); + const balances = (balancesJson as { balances?: PacificaBalance[] }).balances ?? []; + + // ─── 4. Withdraw each non-zero balance ─────────────────────────────────── + // Source: Pacifica API docs — pacifica.gitbook.io/docs/api-documentation/api/rest-api/account + const withdrawals: RecoveryResult['withdrawals'] = []; + for (const bal of balances) { + if (!bal.amount || BigInt(bal.amount) <= 0n) continue; + const result = (await pacificaPost(base, '/account/withdraw', keypair, { + amount: bal.amount, + token: bal.token, + type: 'withdraw_from_lake', + })) as Record; + const withdrawTx = String(result.tx_hash ?? result.withdrawal_id ?? `withdraw-${bal.token}`); + withdrawals.push({ token: bal.token, amount: bal.amount, withdrawTx }); + } + + return { + closedPositions, + withdrawals, + txSignatures: [ + ...closedPositions.map((c) => c.closeTx), + ...withdrawals.map((w) => w.withdrawTx), + ], + }; +} + +// ─── Pacifica authentication ───────────────────────────────────────────────── + +// Pacifica uses stateless Ed25519 per-request signing — no session token is issued. +// Each POST body includes: all request fields + account + timestamp + expiry_window. +// The combined object is sorted alphabetically, serialised as compact JSON, and +// signed with the wallet's Ed25519 private key. The base64 signature is appended. +// +// Source: pacifica-fi/python-sdk (github.com/pacifica-fi/python-sdk) + +// Pacifica API docs (pacifica.gitbook.io/docs/api-documentation). +function buildSignedBody( + keypair: SolanaKeypair, + fields: Record, +): Record { + const timestamp = Date.now(); + const body: Record = { + ...fields, + account: keypair.publicKey, + expiry_window: 5000, + timestamp, + }; + const sorted = Object.fromEntries( + Object.entries(body).sort(([a], [b]) => a.localeCompare(b)), + ); + const msgBytes = new TextEncoder().encode(JSON.stringify(sorted)); + const sigBytes = ed25519Sign(keypair.seed, msgBytes); + return { ...body, signature: Buffer.from(sigBytes).toString('base64') }; +} + +// ─── HTTP helpers ───────────────────────────────────────────────────────────── + +async function pacificaGet(base: string, path: string): Promise { + const res = await fetch(`${base}${path}`); + if (!res.ok) { + throw new PacificaError(`GET ${path} → ${res.status}: ${await res.text()}`); + } + return res.json(); +} + +async function pacificaPost( + base: string, + path: string, + keypair: SolanaKeypair, + fields: Record, +): Promise { + const body = buildSignedBody(keypair, fields); + const res = await fetch(`${base}${path}`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body), + }); + if (!res.ok) { + throw new PacificaError(`POST ${path} → ${res.status}: ${await res.text()}`); + } + return res.json(); +} + +// ─── Ed25519 signing via node:crypto (Node.js 15+) ─────────────────────────── + +// Signs arbitrary bytes using a 32-byte Ed25519 seed. +// node:crypto requires the seed wrapped in PKCS#8 DER format. +// PKCS#8 header bytes: 302e020100300506032b657004220420 (ASN.1 for Ed25519 OID 1.3.101.112) +function ed25519Sign(seed: Uint8Array, message: Uint8Array): Uint8Array { + const pkcs8Header = Buffer.from('302e020100300506032b657004220420', 'hex'); + const pkcs8Der = Buffer.concat([pkcs8Header, Buffer.from(seed.slice(0, 32))]); + const privKey = createPrivateKey({ key: pkcs8Der, format: 'der', type: 'pkcs8' }); + return new Uint8Array(cryptoSign(null, Buffer.from(message), privKey)); +} + +// ─── Keypair loader ────────────────────────────────────────────────────────── + +/** + * Load a Solana keypair from a JSON file (e.g. ~/.config/solana/id.json). + * The file must contain a JSON array of 64 numbers: [seed(32 bytes) | pubkey(32 bytes)]. + */ +export function loadKeypairFromFile(filePath: string): SolanaKeypair { + const raw = JSON.parse(readFileSync(filePath, 'utf8')) as number[]; + if (!Array.isArray(raw) || raw.length !== 64) { + throw new Error(`Expected a 64-element number array in ${filePath}`); + } + const seed = Uint8Array.from(raw.slice(0, 32)); + const publicKeyBytes = Uint8Array.from(raw.slice(32, 64)); + return { seed, publicKeyBytes, publicKey: base58Encode(publicKeyBytes) }; +} + +// ─── Minimal base58 encoder (for Solana public keys) ───────────────────────── + +const B58_ALPHA = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'; + +function base58Encode(bytes: Uint8Array): string { + const digits = [0]; + for (const byte of bytes) { + let carry = byte; + for (let i = 0; i < digits.length; i++) { + carry += digits[i]! << 8; + digits[i] = carry % 58; + carry = (carry / 58) | 0; + } + while (carry > 0) { + digits.push(carry % 58); + carry = (carry / 58) | 0; + } + } + let result = ''; + for (let i = 0; i < bytes.length && bytes[i] === 0; i++) result += '1'; + for (let i = digits.length - 1; i >= 0; i--) result += B58_ALPHA[digits[i]!]; + return result; +} + +// ─── Error types ───────────────────────────────────────────────────────────── + +export class PacificaError extends Error { + constructor(detail: string) { + super(`Pacifica API error: ${detail}`); + this.name = 'PacificaError'; + } +} + +// ─── Internal types ────────────────────────────────────────────────────────── + +interface PacificaPosition { + id: string; + symbol: string; + side: 'long' | 'short'; + size: string; +} + +interface PacificaBalance { + token: string; + amount: string; +} + +// ─── CLI entry point ───────────────────────────────────────────────────────── + +// When run directly (npx tsx recovery.ts), reads env vars and executes recovery. +// Set WALLET_KEYPAIR_JSON to your Solana keypair file path. +// Set PACIFICA_API_BASE to override the API root (e.g. for testnet dry-run). +if (process.argv[1] && new URL(process.argv[1], 'file://').pathname === new URL(import.meta.url).pathname) { + const keypairPath = process.env.WALLET_KEYPAIR_JSON ?? `${process.env.HOME}/.config/solana/id.json`; + const keypair = loadKeypairFromFile(keypairPath); + console.log(`Recovering funds for wallet: ${keypair.publicKey}`); + console.log(`Pacifica program ID (reference): ${PACIFICA_PROGRAM_ID}`); + recoverFromPacifica({ + keypair, + pacificaApiUrl: process.env.PACIFICA_API_BASE, + }) + .then((result) => { + console.log('Recovery complete:', JSON.stringify(result, null, 2)); + }) + .catch((err) => { + console.error('Recovery failed:', err); + process.exit(1); + }); +} diff --git a/skills/toreva-earn.md b/skills/toreva-earn.md index 2f11849..5503067 100644 --- a/skills/toreva-earn.md +++ b/skills/toreva-earn.md @@ -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. diff --git a/skills/toreva-establish-perps-agent.md b/skills/toreva-establish-perps-agent.md index 3536e3a..5e60734 100644 --- a/skills/toreva-establish-perps-agent.md +++ b/skills/toreva-establish-perps-agent.md @@ -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.