Skip to content

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.

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.

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

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.

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
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,
},
})

Use WalletFactorySDK to create new ARC-58 wallets:

import { WalletFactorySDK } from '@akta/sdk/wallet'
const factory = new WalletFactorySDK({ algorand })
// Create a new wallet
const 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.

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 }
],
})
OptionTypeDefaultDescription
namestring''Named alias for the plugin
clientSDKClientrequiredThe plugin’s SDK instance
globalbooleanfalseAllow any caller
callerstringRestrict to a specific caller address
escrowstring''Scope to an escrow (creates it if needed)
methodsMethodSpec[][]Restrict to specific methods
adminbooleanfalseGrant admin privileges
delegationTypebigint0nDelegation type
lastValidbigintMAX_UINT64Expiration round/timestamp
cooldownbigint0nGlobal cooldown between calls
useRoundsbooleanfalseUse round numbers instead of timestamps
useExecutionKeybooleanfalseRequire an execution key
coverFeesbooleanfalseEscrow covers inner transaction fees
canReclaimbooleantrueAllow admin to reclaim plugin fees
defaultToEscrowbooleanfalseDefault spending to escrow
allowancesAddAllowanceArgs[][]Spending allowances for the escrow
await wallet.removePlugin({
plugin: pluginAppId,
caller: callerAddress,
escrow: 'my-escrow',
})
// Get all plugins
const plugins = await wallet.getPlugins()
// Get by key
const plugin = await wallet.getPluginByKey({
plugin: appId,
caller: address,
escrow: 'name',
})
// Get by name
const plugin = await wallet.getPluginByName('pay')
// Get named plugin map
const named = await wallet.getNamedPlugins()

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,
})

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 signer
const result = await execution.send({
signer: mySigner,
})
// Create a new escrow
const { return: escrowId } = await wallet.newEscrow({
escrow: 'my-escrow'
})
// Lock/unlock an escrow
await wallet.toggleEscrowLock({ escrow: 'my-escrow' })
// Get all escrows
const escrows = await wallet.getEscrows()
// Get a specific escrow
const info = await wallet.getEscrow('my-escrow')
// Opt escrow into assets
await wallet.optInEscrow({
escrow: 'my-escrow',
assets: [assetId1, assetId2]
})
// Reclaim funds from escrow
await wallet.reclaimFunds({
escrow: 'my-escrow',
assets: [[0n, 1_000_000n, false]] // [assetId, amount, closeOut]
})
// Add allowances to an escrow
await 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 allowances
const allowances = await wallet.getAllowances()
const allowance = await wallet.getAllowance({
asset: 0n,
escrow: 'my-escrow'
})
// Remove allowances
await wallet.removeAllowances({
escrow: 'my-escrow',
assets: [0n, aktaAssetId]
})

Execution keys enable pre-signed transaction groups that can be executed within a validity window:

// Add an execution key
await wallet.addExecutionKey({
lease: leaseBytes,
groups: [groupHash1, groupHash2],
firstValid: 1000n,
lastValid: 2000n,
})
// Get all execution keys
const executions = await wallet.getExecutions()
// Remove an execution key
await wallet.removeExecutionKey({ lease: leaseBytes })
// Set wallet profile
await wallet.setNickname({ nickname: 'My Wallet' })
await wallet.setAvatar({ avatar: assetId })
await wallet.setBanner({ banner: assetId })
await wallet.setBio({ bio: 'Hello world' })
// Change admin
await wallet.changeAdmin({ newAdmin: newAdminAddr })
// Check balances
const [algo, akta] = await wallet.balance([0n, aktaAssetId])
// Get global state
const state = await wallet.getGlobalState()

For complex multi-step operations, use the group composer:

const group = wallet.group()
// Chain multiple operations
// group.addPlugin(...)
// group.usePlugin(...)
// Send the group
const result = await group.send()