dWallet Execution
How AURA uses Ika dWallet records to co-sign transactions across 6 chains without exposing a private key.
Why dWallet
Giving an AI agent a raw private key means the key can be extracted from memory, a compromised agent can drain the wallet instantly, and there is no on-chain record of what was authorized. Ika dWallet records solve all three: the agent never sees the key, every co-sign request is gated by the AURA program, and the audit trail lives on Solana.
Supported Chains
Chain IDs are u8 values used in ProposeTransactionArgs.target_chain and RegisterDwalletArgs.chain:
| ID | Chain | Notes |
|---|---|---|
| 0 | Solana | Native; no bridge |
| 1 | Bitcoin | bitcoin_manual_review_threshold_usd applies |
| 2 | Ethereum | EVM-compatible |
| 3 | Polygon | EVM-compatible |
| 4 | Arbitrum | EVM-compatible |
| 5 | Optimism | EVM-compatible |
These map to the Chain enum in aura-policy: Bitcoin, Ethereum, Solana, Polygon, Arbitrum, Optimism.
Registering a dWallet
Before any proposal can be approved for a chain, a DwalletRecord must be registered against the treasury. One record per chain.
// Register an Ethereum dWallet (owner signs)
await aura.dwallet.register({
treasury,
chain: 2, // Ethereum
dwalletId: "dwallet-abc123", // from Ika dWallet provisioning
address: "0xYourEthAddress",
balanceUsd: 5_000, // USD cents — balance hint
});RegisterDwalletArgs also accepts optional fields used in the full CPI path:
| Field | Type | Description |
|---|---|---|
chain | number | Chain ID 0–5 |
dwalletId | string | Unique dWallet identifier from Ika |
address | string | Native address on the target chain |
balanceUsd | BN | Current balance hint in USD cents |
dwalletAccount | PublicKey | null | Ika dWallet account (for CPI path) |
authorizedUserPubkey | Uint8Array | null | Authorized user public key bytes |
messageMetadataDigest | Uint8Array | null | 32-byte metadata digest |
publicKeyHex | string | null | Raw dWallet public key hex |
timestamp | BN | Registration timestamp |
Execution Flow
execute_pending Accounts
| Account | Seed / Source | Purpose |
|---|---|---|
operator | signer | Drives execution lifecycle |
treasury | ["treasury", owner, agentId] | Treasury PDA |
message_approval | derived on dWallet program | Stores co-signed bytes |
dwallet | dWallet account from Ika | The co-signer |
caller_program | aura-core program ID | CPI caller identity |
cpi_authority | ["__ika_cpi_authority"] on aura-core | Signs the CPI |
dwallet_program | 87W54kGYFQ1rgWqMeu4XTPHWXWmXSQCcjm8vCTfiq1oY | Ika dWallet program |
dwallet_coordinator | dWallet coordinator account | Network coordinator |
external_liveness | ["external_liveness", treasury] | Optional freshness check |
system_program | 11111111111111111111111111111111 | Account creation |
MessageApproval PDA Derivation
The MessageApproval PDA is derived on the Ika dWallet program, not on aura-core. The SDK exports deriveMessageApprovalAddress which mirrors aura-core::find_message_approval_pda:
import { deriveMessageApprovalAddress } from "@aura-protocol/sdk-ts";
const [messageApproval] = deriveMessageApprovalAddress(
DWALLET_DEVNET_PROGRAM_ID,
curveCode, // 0=secp256k1, 1=secp256r1, 2=ed25519, 3=ristretto
publicKeyBytes, // raw dWallet public key bytes
signatureSchemeCode, // Ika signature scheme code
messageDigest, // 32-byte Keccak-256 digest
messageMetadataDigest, // optional 32-byte metadata digest
);Seeds (in order):
b"dwallet"u16_le(curveCode) ++ publicKey— chunked into 32-byte segmentsb"message_approval"u16_le(signatureSchemeCode)messageDigest(32 bytes)messageMetadataDigest(32 bytes, only if non-zero)
Bitcoin Manual Review
Bitcoin transactions above bitcoin_manual_review_threshold_usd (default: 5_000 USD cents = $50) are blocked by ViolationCode::BitcoinManualReview in evaluate_public_precheck. A guardian must approve via approve_pending_execution before execute_pending can proceed.
finalize_execution Accounts
| Account | Purpose |
|---|---|
operator | Signer driving finalization |
treasury | Treasury PDA |
message_approval | MessageApproval PDA with co-signed bytes |
swarm_pool | Optional — updated after finalization if swarm is configured |
budget_envelope | Optional — updated after finalization if envelope is configured |
exposure_group | Optional — updated after finalization if group is configured |
external_liveness | Optional — freshness check if liveness_config requires it |
Security Properties
| Property | Guarantee |
|---|---|
| Key never exposed | Agent receives signed bytes, not the private key |
| Authorization gated | Only aura-core's cpi_authority PDA can request co-signs |
| Per-chain isolation | Each chain has its own DwalletRecord |
| Audit trail | Every execute_pending and finalize_execution emits on-chain events |
| Revocable | Owner can deregister a dWallet record at any time |
| Liveness checks | LivenessConfig.require_dwallet_freshness can require a fresh ExternalLiveness record |