Soloraa
Threat demo

A captured signature shouldn't move money twice.

A compromised relayer, a man-in-the-middle, an old log archive — any of them can resurface a previously-valid signed intent. Soloraa binds each signature to the wallet's nonce. The chain checks. The chain rejects.

Wallet 3Kh6…KR6N

program DfPL…1Ttf · devnet

wallet.nonce

0

signed intent nonce

status

ready

  1. 01

    Sign and broadcast a transfer

    Enclave signs the canonical 169-byte message. Relayer prepends the Ed25519 verify ix and broadcasts.

  2. 02

    Replay the same signed bytes

    Resubmit the captured intent with a fresh blockhash to dodge Solana's tx-dedup. Reaches the program.

Controls

Each run uses fresh state. The replay button only enables once the first transfer has confirmed.

What the chain actually does

The replay-rejection path doesn't depend on anything off-chain. The Anchor program's verifier reads the on-chain nonce and the signed nonce. They don't match. The program returns a typed error before funds move.

programs/solora/src/verify.rsrust
let on_chain_nonce = wallet.load()?.nonce;
let signed_nonce = msg.read_u64(80);   // bytes 80..88

require!(
    signed_nonce == on_chain_nonce,
    ErrorCode::IntentNonceMismatch,
);

// only after the gate: bump the nonce.
wallet.load_mut()?.nonce += 1;
solora_relayer/ops.ts (replay)ts
// Build a fresh wrapping tx but reuse the exact signed
// bytes from the previous run.
const replayTx = new web3.Transaction().add(...tx.instructions);
replayTx.feePayer = authority.publicKey;
replayTx.recentBlockhash = (
  await connection.getLatestBlockhash("confirmed")
).blockhash;
replayTx.sign(authority);

// Solana dedup misses it (different sig). The program catches it.
// AnchorError thrown in programs/solora/src/verify.rs:78
// Error Code: IntentNonceMismatch. Error Number: 6018.

Why this matters

Most "AI agent wallets" enforce policy at the SDK layer. A captured signature can be reused as long as you can convince the SDK to process it. Soloraa moves the gate onto Solana — the on-chain program is the verifier, and there's no SDK to convince. The same property rules out cross-fork replay, slot-skewed replay, and --no-preflight replay.