AURA

Examples

End-to-end devnet flows with the v3 functional SDK.

End-to-end recipes against devnet using the functional API (instructions.<domain>.*, accounts.*, pda.*).

Setup

import BN from "bn.js";
import {
  ComputeBudgetProgram,
  Connection,
  Keypair,
  Transaction,
  type TransactionInstruction,
} from "@solana/web3.js";
import { AuraClient } from "@aura-protocol/sdk-ts";

const connection = new Connection("https://api.devnet.solana.com", "confirmed");
const client = new AuraClient({ connection });
const owner = Keypair.generate(); // fund this on devnet

const now = () => new BN(Math.floor(Date.now() / 1000));

A send-and-confirm helper

client.sendInstructions returns before confirmation. To read state right after a write, confirm against the blockhash:

async function sendAndConfirm(
  instructions: TransactionInstruction[],
  signers: Keypair[] = [],
): Promise<string> {
  const { blockhash, lastValidBlockHeight } =
    await connection.getLatestBlockhash("confirmed");
  const tx = new Transaction({ blockhash, lastValidBlockHeight, feePayer: owner.publicKey });
  tx.add(ComputeBudgetProgram.setComputeUnitLimit({ units: 600_000 }), ...instructions);
  tx.sign(owner, ...signers);
  const sig = await connection.sendRawTransaction(tx.serialize());
  await connection.confirmTransaction({ signature: sig, blockhash, lastValidBlockHeight }, "confirmed");
  return sig;
}

Create and activate a treasury

import { accounts, instructions } from "@aura-protocol/sdk-ts";

const { treasury, input } = accounts.createTreasuryInput({
  owner: owner.publicKey,
  args: createTreasuryArgs, // see Instructions → Treasury for the full shape
});

await sendAndConfirm([await instructions.treasury.createTreasury(client, input)]);

// Move the agent state machine to Active.
await sendAndConfirm([
  await instructions.lifecycle.transitionAgentState(client, {
    accounts: { owner: owner.publicKey, treasury },
    args: { targetState: 1, now: now() },
  }),
]);

const state = await accounts.fetchTreasuryAccount(client, treasury);
console.log("agentState:", state.agentState, "paused:", state.executionPaused);

Propose, then cancel

const proposeIx = await instructions.execution.proposeTransaction(client, {
  accounts: {
    aiAuthority: owner.publicKey,
    treasury,
    sessionKeyAccount: null,
    swarmPool: null,
    addressList: null,
    complianceOracle: null,
    parentTreasury: null,
    budgetEnvelope: null,
    exposureGroup: null,
    dwalletState: null,
    chainProfile: null,
    trustIdentity: null,
    policyCanary: null,
  },
  args: {
    amountUsd: new BN(100),
    targetChain: 2,
    txType: 0,
    protocolId: null,
    currentTimestamp: now(),
    expectedOutputUsd: null,
    actualOutputUsd: null,
    quoteAgeSecs: null,
    counterpartyRiskScore: null,
    recipientOrContract: "0x000000000000000000000000000000000000dead",
    sanctionsProof: [],
    // Chain-binding fields — null for legacy proposals.
    // For chain-bound proposals supply nativeMessageHash + replay fields.
    // See the dWallet Execution guide for the full flow.
    assetId: null,
    nativeAmount: null,
    decimals: null,
    gasNativeAmount: null,
    gasAssetId: null,
    nativeMessageHash: null,
    calldataHash: null,
    utxoSetHash: null,
    sighashType: null,
    evmChainId: null,
    replayNonce: null,
    gasLimit: null,
    maxFeeNative: null,
    solanaRecentBlockhash: null,
    solanaMessageHash: null,
    confirmationsRequired: null,
  },
});
await sendAndConfirm([proposeIx]);

const afterPropose = await accounts.fetchTreasuryAccount(client, treasury);
console.log("pending:", afterPropose.pendingQueue.length);

// Cancel it.
await sendAndConfirm([
  await instructions.execution.cancelPending(client, {
    accounts: { owner: owner.publicKey, treasury, dwalletState: null },
    args: { now: now() },
  }),
]);

Configure an emergency multisig

const guardian1 = Keypair.generate().publicKey;
const guardian2 = Keypair.generate().publicKey;

await sendAndConfirm([
  await instructions.governance.configureMultisig(client, {
    accounts: { owner: owner.publicKey, treasury },
    args: {
      requiredSignatures: 1,
      guardians: [guardian1, guardian2],
      guardianWeights: [1, 1],
      requiredApprovalWeight: 1,
      timestamp: now(),
    },
  }),
]);

const t = await accounts.fetchTreasuryAccount(client, treasury);
console.log("guardians:", t.multisig?.guardians.map((g) => g.key.toBase58()));

Compose multiple instructions

Pack several instructions into one transaction — pause then cancel atomically:

const pauseIx = await instructions.execution.pauseExecution(client, {
  accounts: { owner: owner.publicKey, treasury },
  args: { paused: true, now: now() },
});
const cancelIx = await instructions.execution.cancelPending(client, {
  accounts: { owner: owner.publicKey, treasury, dwalletState: null },
  args: { now: now() },
});
await sendAndConfirm([pauseIx, cancelIx]);

Use-case recipes

Each recipe builds on the client, owner, now(), treasury, and sendAndConfirm from Setup and uses pda/instructions/accounts from @aura-protocol/sdk-ts.

Set spending guardrails (budget + allowlist)

Cap a chain's daily/weekly spend and restrict recipients to an allow list:

import { pda, instructions } from "@aura-protocol/sdk-ts";
import { SystemProgram } from "@solana/web3.js";

const envelopeId = new BN(1);
const [budgetEnvelope] = pda.deriveBudgetEnvelopeAddress(treasury, envelopeId);
const [addressList] = pda.deriveAddressListAddress(treasury);

await sendAndConfirm([
  await instructions.budget.configureBudgetEnvelope(client, {
    accounts: { owner: owner.publicKey, treasury, budgetEnvelope, systemProgram: SystemProgram.programId },
    args: { envelopeId, scopeKind: 0, chain: 2, txType: null, protocolId: null, dailyLimitUsd: new BN(5_000), weeklyLimitUsd: new BN(20_000), now: now() },
  }),
  await instructions.addressLists.initAddressList(client, {
    accounts: { owner: owner.publicKey, treasury, addressList, systemProgram: SystemProgram.programId },
    args: { mode: 0, chain: 2, now: now() }, // 0 = allow list
  }),
]);

Delegate day-to-day ops with a session key

Issue a short-lived, chain-scoped key an agent can use without the owner key:

import { Keypair } from "@solana/web3.js";

const sessionKey = Keypair.generate();
const [sessionKeyAccount] = pda.deriveSessionKeyAddress(treasury, sessionKey.publicKey);

await sendAndConfirm([
  await instructions.lifecycle.issueSessionKey(client, {
    accounts: { authority: owner.publicKey, treasury, sessionKeyAccount, systemProgram: SystemProgram.programId },
    args: {
      sessionKey: sessionKey.publicKey,
      durationSecs: new BN(3_600),
      maxAmountUsdPerTx: new BN(500),
      maxDailySpendUsd: new BN(2_000),
      allowedChains: Buffer.from([2]),
      allowedTxTypes: Buffer.from([0]),
      maxProposalCount: 100,
      now: now(),
    },
  }),
]);

Schedule a recurring payment (DCA / payroll)

Create a standing order; a keeper executes each due run through the normal policy + sign + finalize path:

const intentId = new BN(1);
const [scheduledIntent] = pda.deriveScheduledIntentAddress(treasury, intentId);

await sendAndConfirm([
  await instructions.execution.createScheduledIntent(client, {
    accounts: { owner: owner.publicKey, treasury, scheduledIntent, systemProgram: SystemProgram.programId },
    args: {
      intentId,
      args: {
        kind: 0, chain: 2, txType: 0,
        intervalSecs: new BN(86_400), startAt: now(), endAt: null,
        maxRuns: null, perRunLimitUsd: new BN(100), totalBudgetUsd: null,
        recipients: [], amountUsd: new BN(100), skipOnDeny: true, catchUp: false,
        keeper: null, conditions: [], combinator: 0,
      },
    },
  }),
]);

// Later, when a run is due (any keeper can call):
await sendAndConfirm([
  await instructions.execution.executeScheduledIntent(client, {
    accounts: { caller: owner.publicKey, treasury, scheduledIntent, conditionFeed: null },
  }),
]);

Track a multi-chain dWallet and reserve spend

Register a dWallet, create its per-chain state, and reserve → settle a spend:

const chain = 2;
const [dwalletState] = pda.deriveDwalletStateAddress(treasury, chain);

await sendAndConfirm([
  await instructions.dwallet.registerDwallet(client, {
    accounts: { owner: owner.publicKey, treasury },
    args: { chain, dwalletId: "eth-1", address: "0x...dead", balanceUsd: new BN(5_000), dwalletAccount: null, authorizedUserPubkey: null, messageMetadataDigest: null, publicKeyHex: null, timestamp: now() },
  }),
  await instructions.dwallet.initDwalletState(client, {
    accounts: { owner: owner.publicKey, treasury, dwalletState, systemProgram: SystemProgram.programId },
    args: { chain, now: now() },
  }),
]);

await sendAndConfirm([
  await instructions.dwallet.reserveDwalletSpend(client, {
    accounts: { authority: owner.publicKey, treasury, dwalletState },
    args: { chain, amountUsd: new BN(100), now: now() },
  }),
]);

Coordinate an agent swarm

Create a shared pool and attach this treasury to it:

const swarmId = "trading-swarm-1";
const [swarmPool] = pda.deriveSwarmPoolAddress(swarmId);

await sendAndConfirm([
  await instructions.swarm.initSwarmPool(client, {
    accounts: { creator: owner.publicKey, swarmPool, systemProgram: SystemProgram.programId },
    args: { swarmId, sharedPoolLimitUsd: new BN(50_000), timestamp: now() },
  }),
  await instructions.swarm.configureSwarm(client, {
    accounts: { owner: owner.publicKey, treasury },
    args: { swarmId, memberAgents: ["agent-prod-1"], sharedPoolLimitUsd: new BN(50_000), timestamp: now() },
  }),
  await instructions.swarm.joinSwarm(client, {
    accounts: { owner: owner.publicKey, treasury, swarmPool },
    args: { now: now() },
  }),
]);

Emergency response

Register a recovery destination while healthy, then pause, shut down, and (after the activation window) break-glass recover to that address:

// 1. While healthy — register a per-chain cold wallet (48h lock on changes).
await sendAndConfirm([
  await instructions.governance.registerRecoveryDestination(client, {
    accounts: { owner: owner.publicKey, treasury },
    args: { chain: 2, address: "0x...coldwallet", now: now() },
  }),
]);

// 2. Incident — pause, then arm recovery via shutdown.
await sendAndConfirm([
  await instructions.execution.pauseExecution(client, {
    accounts: { owner: owner.publicKey, treasury },
    args: { paused: true, now: now() },
  }),
]);
await sendAndConfirm([
  await instructions.governance.emergencyShutdown(client, {
    accounts: { owner: owner.publicKey, treasury },
    args: { recoveryPubkey: owner.publicKey, now: now() },
  }),
]);

// 3. After the activation window — sweep to the registered destination.
await sendAndConfirm([
  await instructions.governance.breakGlassRecover(client, {
    accounts: { owner: owner.publicKey, treasury },
    args: { chain: 2, amountUsd: new BN(1_000), now: now() },
  }),
]);

Handle errors

import { isAuraError, parseAuraError, AuraErrorCode } from "@aura-protocol/sdk-ts";

try {
  await sendAndConfirm([proposeIx]);
} catch (err) {
  if (isAuraError(err, AuraErrorCode.ExecutionPaused)) {
    console.warn("treasury is paused — resume it first");
  } else {
    const parsed = parseAuraError(err);
    if (parsed) console.error(`${parsed.name}: ${parsed.message}`);
    else throw err;
  }
}

Parse events from a transaction

import { parseAuraEvents } from "@aura-protocol/sdk-ts";

const sig = await sendAndConfirm([proposeIx]);
const tx = await connection.getTransaction(sig, { maxSupportedTransactionVersion: 0 });
const events = parseAuraEvents(client, tx?.meta?.logMessages ?? []);
for (const event of events) console.log(event.name, event.data);

Discover the surface at runtime

import { AURA_FEATURE_DOMAINS, instructions } from "@aura-protocol/sdk-ts";

for (const domain of AURA_FEATURE_DOMAINS) {
  for (const feature of domain.instructions) {
    const accountsNeeded = instructions.listInstructionAccounts(feature.name).length;
    console.log(`${domain.label} / ${feature.name}: ${accountsNeeded} accounts`);
  }
}

For the full Ika-dependent signing flow (runAuraApproval, sendSolanaTransfer, signEvmPayload), see the dWallet Execution guide and the devnet test at packages/tests/sdk-ts/devnet/dwallet/transfer.devnet.test.ts.

On this page