Wallet (ARC-58)
The WalletSDK is the foundational piece of the Akita ecosystem. It manages ARC-58 abstracted accounts — programmable wallets secured by passkeys (device-native biometrics like Face ID and fingerprint) rather than seed phrases, with permissions controlled through a modular plugin architecture.
Concepts
Section titled “Concepts”ARC-58 Abstracted Accounts
Section titled “ARC-58 Abstracted Accounts”An ARC-58 wallet is a smart contract that holds assets and delegates transaction authority through plugins. Wallets are secured by passkey-based account abstraction, eliminating seed phrases entirely. The wallet admin installs plugins that define what operations different callers can perform.
Plugins
Section titled “Plugins”A plugin is another smart contract that has been granted permission to execute transactions on behalf of the wallet. Plugins can be:
- Global — callable by anyone
- Caller-restricted — only a specific address can invoke
- Method-restricted — only specific methods on the plugin are allowed
- Time-limited — expires after a round or timestamp
- Cooldown-gated — rate-limited per method
Sub-Accounts (Escrows)
Section titled “Sub-Accounts (Escrows)”Each wallet can spawn isolated sub-accounts (escrows) with their own fund separation and independent plugin installations. These function as security-bounded environments — a compromised plugin or misbehaving agent in one sub-account cannot access funds or permissions in another. Plugins can be scoped to specific sub-accounts, and spending allowances are set per-sub-account.
Spending Allowances
Section titled “Spending Allowances”Allowances limit how much an escrow can spend of a given asset. They support multiple modes:
- Flat — a fixed total amount
- Window — replenishes over a time window
- Drip — continuous linear replenishment
Initialization
Section titled “Initialization”import { WalletSDK } from '@akta/sdk/wallet'import { AlgorandClient } from '@algorandfoundation/algokit-utils'
const algorand = AlgorandClient.testNet()const wallet = new WalletSDK({ algorand })For write operations, set a default sender and signer:
const wallet = new WalletSDK({ algorand, factoryParams: { defaultSender: myAddress, defaultSigner: mySigner, },})Creating Wallets
Section titled “Creating Wallets”Use WalletFactorySDK to create new ARC-58 wallets:
import { WalletFactorySDK } from '@akta/sdk/wallet'
const factory = new WalletFactorySDK({ algorand })
// Create a new walletconst newWallet = await factory.new({ nickname: 'My Wallet' })With additional options:
const newWallet = await factory.new({ sender: myAddress, signer: mySigner, nickname: 'My Wallet', admin: adminAddress, // Defaults to sender controlledAddress: existingAddr, // Existing account to abstract referrer: referrerAddress, // Referrer who funded the MBR})The referrer parameter enables organic growth incentives — referrers who fund a new wallet’s minimum balance requirement receive a percentage of fees from the referred user’s wallet activity.
Plugin Management
Section titled “Plugin Management”Installing a Plugin
Section titled “Installing a Plugin”Install a plugin restricted to a specific caller:
await wallet.addPlugin({ client: payPluginSDK, caller: agentAddress,})Or restrict to a specific caller and add options:
await wallet.addPlugin({ name: 'pay', client: payPluginSDK, caller: agentAddress, escrow: 'agent-escrow', methods: [ { name: payPluginSDK.pay, cooldown: 0n } ],})Plugin Options
Section titled “Plugin Options”| Option | Type | Default | Description |
|---|---|---|---|
name | string | '' | Named alias for the plugin |
client | SDKClient | required | The plugin’s SDK instance |
global | boolean | false | Allow any caller |
caller | string | — | Restrict to a specific caller address |
escrow | string | '' | Scope to an escrow (creates it if needed) |
methods | MethodSpec[] | [] | Restrict to specific methods |
admin | boolean | false | Grant admin privileges |
delegationType | bigint | 0n | Delegation type |
lastValid | bigint | MAX_UINT64 | Expiration round/timestamp |
cooldown | bigint | 0n | Global cooldown between calls |
useRounds | boolean | false | Use round numbers instead of timestamps |
useExecutionKey | boolean | false | Require an execution key |
coverFees | boolean | false | Escrow covers inner transaction fees |
canReclaim | boolean | true | Allow admin to reclaim plugin fees |
defaultToEscrow | boolean | false | Default spending to escrow |
allowances | AddAllowanceArgs[] | [] | Spending allowances for the escrow |
Removing a Plugin
Section titled “Removing a Plugin”await wallet.removePlugin({ plugin: pluginAppId, caller: callerAddress, escrow: 'my-escrow',})Reading Plugins
Section titled “Reading Plugins”// Get all pluginsconst plugins = await wallet.getPlugins()
// Get by keyconst plugin = await wallet.getPluginByKey({ plugin: appId, caller: address, escrow: 'name',})
// Get by nameconst plugin = await wallet.getPluginByName('pay')
// Get named plugin mapconst named = await wallet.getNamedPlugins()Using Plugins
Section titled “Using Plugins”The usePlugin method is the primary way to execute plugin operations through the wallet:
import { PayPluginSDK } from '@akta/sdk/wallet'
const pay = new PayPluginSDK({ algorand })
await wallet.usePlugin({ sender: myAddress, signer: mySigner, calls: [ pay.pay({ sender: myAddress, signer: mySigner, payments: [{ receiver: recipientAddr, amount: 1_000_000n, asset: 0n }], }), ],})Add optional parameters for more control:
await wallet.usePlugin({ sender: myAddress, signer: mySigner, calls: [pay.pay(...)], name: 'pay', // Use named plugin lookup escrow: 'my-escrow', // Scope to an escrow fundsRequest: [ // Request funds from escrow { asset: 0n, amount: 1_000_000n } ], consolidateFees: true,})Pre-Building Transactions
Section titled “Pre-Building Transactions”For execution keys and deferred signing, use wallet.build.usePlugin():
const execution = await wallet.build.usePlugin({ calls: [PayPlugin.pay(...)], escrow: 'my-escrow', lease: 'unique-lease-id', windowSize: 1000n, // Valid for 1000 rounds skipSignatures: true, // Sign later})
// Later, send with a signerconst result = await execution.send({ signer: mySigner,})Escrow Management
Section titled “Escrow Management”// Create a new escrowconst { return: escrowId } = await wallet.newEscrow({ escrow: 'my-escrow'})
// Lock/unlock an escrowawait wallet.toggleEscrowLock({ escrow: 'my-escrow' })
// Get all escrowsconst escrows = await wallet.getEscrows()
// Get a specific escrowconst info = await wallet.getEscrow('my-escrow')
// Opt escrow into assetsawait wallet.optInEscrow({ escrow: 'my-escrow', assets: [assetId1, assetId2]})
// Reclaim funds from escrowawait wallet.reclaimFunds({ escrow: 'my-escrow', assets: [[0n, 1_000_000n, false]] // [assetId, amount, closeOut]})Spending Allowances
Section titled “Spending Allowances”// Add allowances to an escrowawait wallet.addAllowances({ escrow: 'my-escrow', allowances: [ { asset: 0n, type: 'flat', amount: 10_000_000n }, { asset: aktaAssetId, type: 'window', amount: 5_000_000n, window: 86400n }, ]})
// Read allowancesconst allowances = await wallet.getAllowances()const allowance = await wallet.getAllowance({ asset: 0n, escrow: 'my-escrow'})
// Remove allowancesawait wallet.removeAllowances({ escrow: 'my-escrow', assets: [0n, aktaAssetId]})Execution Keys
Section titled “Execution Keys”Execution keys enable pre-signed transaction groups that can be executed within a validity window:
// Add an execution keyawait wallet.addExecutionKey({ lease: leaseBytes, groups: [groupHash1, groupHash2], firstValid: 1000n, lastValid: 2000n,})
// Get all execution keysconst executions = await wallet.getExecutions()
// Remove an execution keyawait wallet.removeExecutionKey({ lease: leaseBytes })Other Operations
Section titled “Other Operations”// Set wallet profileawait wallet.setNickname({ nickname: 'My Wallet' })await wallet.setAvatar({ avatar: assetId })await wallet.setBanner({ banner: assetId })await wallet.setBio({ bio: 'Hello world' })
// Change adminawait wallet.changeAdmin({ newAdmin: newAdminAddr })
// Check balancesconst [algo, akta] = await wallet.balance([0n, aktaAssetId])
// Get global stateconst state = await wallet.getGlobalState()Group Composer
Section titled “Group Composer”For complex multi-step operations, use the group composer:
const group = wallet.group()
// Chain multiple operations// group.addPlugin(...)// group.usePlugin(...)
// Send the groupconst result = await group.send()