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.