Developers
Keythings Wallet Integration Guide
Everything you need to connect a dApp to the Keythings Wallet extension: capability-based authorization, RPC workflows, transaction publishing, and production hardening tips sourced directly from the keythings-extension-wallet repository.
Environment Checklist
The wallet ships as a Chrome Manifest V3 extension. For integration testing you only need the published build and a localhost dApp:
- Install Keythings Wallet from the Chrome Web Store and pin the extension.
- Enable developer options inside the extension and make sure
window.keetaappears on allowed origins. - Serve your dApp on
http://localhost:3000; restart the dev server after each wallet upgrade since the allowlist only trusts that origin.
Detect the Injected Provider
src/inpage/inpage-provider.ts injects window.keeta into approved origins. Always feature-detect and guide users when the wallet is missing or locked.
import type { KeetaProvider } from 'keythings-extension-wallet/types'
export function getKeetaProvider(): KeetaProvider {
const provider = window.keeta
if (!provider?.isKeeta) {
throw new Error('Keythings Wallet provider not detected. Ask the user to install or enable the extension.')
}
return provider
}The provider emits accountsChanged, chainChanged, and disconnect. Attach listeners during app bootstrap to keep UI state synchronized with wallet activity.
Simplified Capability System
Keythings Wallet uses a simplified two-tier capability system for dApp connections. The wallet automatically requests the appropriate permissions based on your dApp's needs.
Default Connection: Read + Transact
By default, when a dApp connects to Keythings Wallet, it receives both read and transact capabilities:
- Read Access: View wallet data, account balances, transaction history, and network information
- Transact Access: Send transactions, sign operations, and interact with smart contracts
The connection approval dialog will show: "Allow localhost:3000 to send transactions and view your wallet data"
Read-Only Connection
For dApps that only need to view wallet information (like portfolio trackers or analytics tools), you can request read-only access:
const provider = getKeetaProvider()
// Request read-only access (no transaction capabilities)
const granted = await provider.requestCapabilities(['read'])
console.table(granted)
// Default connection (read + transact) - no need to specify
const defaultGranted = await provider.requestCapabilities(['read', 'transact'])
The connection approval dialog will show: "Allow localhost:3000 to view your wallet data"
Capability Management
Capabilities expire and need to be refreshed. The wallet handles this automatically, but you can also refresh manually:
// Refresh capabilities before they expire
await provider.refreshCapabilities(['read', 'transact'])
// Check if capabilities are still valid
const isConnected = await provider.isConnected()
if (!isConnected) {
// Re-request capabilities
await provider.requestCapabilities(['read', 'transact'])
}
Account Connection Workflow
- Call
requestAccounts()to prompt the user. - Persist the approved account and active network in application state.
- Subscribe to provider events and react immediately to account, network, or lock changes.
const provider = getKeetaProvider()
const accounts = await provider.requestAccounts()
if (accounts.length === 0) {
throw new Error('User rejected the connection request or no accounts are available.')
}
provider.on?.('accountsChanged', (next) => {
console.info('Accounts changed', next)
})
provider.on?.('chainChanged', (chainId) => {
console.info('Network switched', chainId)
})
provider.on?.('disconnect', () => {
console.warn('Wallet disconnected — clear session state and prompt to reconnect.')
})
If you only need cached accounts, call getAccounts() once you have confirmed that the read capability token is still valid.
Wallet RPC Reference
All RPC requests flow through wallet-provider-handler.ts. The table below mirrors the supported methods. Useprovider.request({ method, params }) for lower-level access.
These JSON-RPC methods are the same wallet provider APIs documented in full detail in the API Reference. Use this table as a quick lookup while you are following the integration flow.
| Method | Summary |
|---|---|
| keeta_requestAccounts | Prompts the user to approve the dApp and returns authorized addresses. |
| keeta_getAccounts | Returns cached accounts without opening a prompt (requires prior approval). |
| keeta_requestCapabilities | Requests capabilities: `['read']` for read-only, `['read', 'transact']` for full access (default). |
| keeta_refreshCapabilities | Renews expiring capability tokens without prompting the user again. |
| keeta_getNetwork | Returns the active Keeta network (name + chainId). |
| keeta_switchNetwork | Requests a network switch. User confirmation is required. |
| keeta_getBalance | Reads the balance of a Keeta address (requires `read` capability). |
| keeta_getAllBalances | Returns all tracked token balances for the active account (requires `read` capability). |
| keeta_sendTransaction | Publishes a transaction built by the dApp (requires `transact` capability). |
| keeta_signMessage | Signs an arbitrary message (requires `transact` capability). |
| keeta_getKtaPrice | Returns the latest cached KTA → USD price snapshot. |
Additional helpers such as keeta_getTokenMetadata, keeta_getAccountState, and keeta_getCapabilityTokensare exposed through provider.request and obey the same capability checks.
History: For transaction history and ledger queries, use the Keeta JS SDK directly (for example UserClient.history or Client.history) or an indexer service. The wallet no longer exposes a history RPC.
Transactions & Builders
For multi-operation flows (storage accounts, token mints, permission changes), request a user client. The wallet instantiates the Keeta SDK inside the service worker and exposes hardened builder primitives.
const provider = getKeetaProvider()
await provider.requestCapabilities(['read', 'transact'])
const userClient = await provider.getUserClient()
const builder = userClient.initBuilder()
builder.send(
{ publicKeyString: 'kta_recipient_address' },
'1000000000',
{ publicKeyString: 'kta_token_address' }
)
if (typeof builder.computeBlocks === 'function') {
await builder.computeBlocks()
}
const receipt = await userClient.publishBuilder(builder)
console.log('Transaction hash', receipt?.blocks?.[0]?.hash ?? receipt)
When moving funds on behalf of a storage account, set the storage account context inside the builder and ensure the user has granted SEND_ON_BEHALF. Review the dApp’s src/app/lib/storage-account-manager.ts for the authorized pattern.
Auxiliary Data & Services
- KTA price:
keeta_getKtaPricereturns the cached CoinGecko snapshot generated by the background handler. - Token metadata:
keeta_getTokenMetadatareads decimals, ticker, and metadata fields from on-chain state. - Network details:
keeta_getNetworkmirrors the wallet’s active chain and should drive your dApp routing.
Localhost Allowlist
During development the wallet injects the provider into these origins:
Production dApps must be served over HTTPS. The wallet will prompt the user to approve new origins before injection.
Production Hardening Checklist
- Error surfaces: Wallet errors return
code,message, and optionaldata. Map knownKETA_ERROR_CODESto actionable UI. - Rate limiting: Throttle polling (balances, price, activity) to avoid frequent service-worker cold starts.
- Capability hygiene: Track
expiresAtand refresh tokens before expiry. Surface banners when permissions are lost. - User feedback: Mirror wallet prompts with a contextual modal (“Waiting for Keythings Wallet …”).
- Automated testing: Integrate
bun run test:e2e,bun tests/validation/validate-manifest.js, andbun run security:allin CI pipelines.
Next Steps & References
- Read
src/background/wallet-provider-handler.tsfor end-to-end authorization flow details. - Review the Security guide to align dApp messaging with wallet risk assessments.