@soloraa/sdk
A thin TypeScript client over the Soloraa enclave HTTP API and the on-chain program. Submits structured intents, returns verified results, exposes a streaming surface for long sessions.
Quickstart
npm install @soloraa/sdk @solana/web3.js
import { SoloraaClient } from "@soloraa/sdk";
const client = new SoloraaClient({
rpcUrl: "https://api.devnet.solana.com",
enclaveUrl: "http://127.0.0.1:8080",
walletPda: "3Kh6Y1aeEE9Ss2wbR5DK24KJTDHJf3SZq7sWJkkzKR6N",
});
const result = await client.execute({
action: "transfer",
destination: "B4D6...8nqb",
amount: 1_000_000n,
});
console.log("confirmed", result.signature, "nonce", result.walletNonce);Client
The SoloraaClient holds the three endpoints it needs and the wallet it acts on behalf of. The client carries no signing keys — every signature comes from the enclave.
new SoloraaClient({
rpcUrl: string, // Solana RPC
enclaveUrl: string, // /sign-* endpoints
walletPda: string, // base58
relayerKeypair?: Keypair, // optional: pays tx fees + broadcasts
});execute()
Submit an intent, get a confirmed transaction back. Each action maps to a structured request the enclave can policy-check.
client.execute(intent: ExecutionIntent): Promise<ExecutionResult>
type ExecutionIntent =
| { action: "transfer"; destination: string; amount: bigint }
| {
action: "swap";
protocol: "jupiter";
inputMint: string;
outputMint: string;
amount: bigint;
constraints?: { maxSlippageBps?: number };
}
| {
action: "lend";
protocol: "kamino" | "marginfi" | "solend";
mint: string;
amount: bigint;
};
type ExecutionResult = {
signature: string; // confirmed tx signature
walletNonce: number; // post-execution
bytesSigned: Uint8Array; // 169-byte SOLORA_INTENT_V2
};verifyIntent()
Reverse-direction check: given a 169-byte signed intent and the enclave pubkey, confirm it would pass the on-chain verifier. Useful for tooling that observes intents without broadcasting.
client.verifyIntent(args: {
message: Uint8Array; // 169 bytes
signature: Uint8Array; // 64 bytes
enclavePubkey: string; // base58
}): Promise<VerifyResult>
type VerifyResult =
| { ok: true; fields: IntentFields }
| { ok: false; reason: IntentRejectReason };stream()
Subscribe to lifecycle events for a long-running agent. The stream yields typed events from each of the seven pipeline stages. Backed by Server-Sent Events.
for await (const event of client.stream({ runId })) {
if (event.stage === "verify" && event.error) {
// typed error code from the on-chain program
console.error(event.error.code, event.error.name);
}
}Errors
On-chain rejections surface as typed errors. Codes match the program's error.rs.
| Code | Name | Meaning |
|---|---|---|
| 6000 | WalletPaused | Wallet authority has paused execution. |
| 6017 | EnclaveSignerMismatch | Ed25519 ix signer ≠ wallet.enclave_signer. |
| 6018 | IntentNonceMismatch | Replay rejected — nonce stale. |
| 6019 | IntentExpired | Signed intent expiry passed. |
| 6021 | IntentPayloadMismatch | Destination, amount, or accounts altered. |
| 6027 | TargetProgramNotAllowed | CPI target not in allowlist. |
| 6033 | BlockhashMismatch | Slot/hash not in SlotHashes. |
| 6037 | MeasurementRevoked | Enclave measurement revoked by governance. |
| 6045 | AttestationMeasurementMismatch | Attestation cites unknown measurement. |
| 6046 | AttestationGovernorMismatch | Attestation signed by non-governor key. |
Deployment
The frontend and SDK both target the same on-chain program. See the repo's deployment runbook for the exact commands to stand up devnet infrastructure.
See the deployment runbook →