Based on the work of the Wallet Provider, this project adds support for various extensions that allow for cryptographic operations in specific contexts.
Extensions are modular components that enhance the capabilities of a wallet or provider. They allow for the addition of specialized features—such as secret management, logging, or custom signing—without bloating the core provider implementation.
An extension typically consists of:
- State: Data managed by the extension (e.g., a list of stored secrets).
- API: A set of methods to interact with the extension and its state.
Each domain is built around a generic store that holds the canonical state and exposes a small, pure API. Stores are intentionally source-agnostic: they don't require a keystore — records can just as easily come from an RPC service, an indexer, a remote wallet, or any other producer.
On top of each store, the project ships optional bridge extensions that wire the store to a specific source (e.g. the local keystore), and a unified/meta extension per domain that bundles the store plus any optional bridges, conditionally enabling them only when the underlying dependency is present.
💡 Recommended: For most developers, the unified extension for each domain is the best entry point. It hides the wiring between stores and bridges, conditionally enables capabilities based on what the provider exposes, and lets you opt down to the generic store or individual bridges only when you need finer control.
Cryptographic key material management — generation, derivation, and secure storage.
Unified Extension (recommended): TODO — a @algorandfoundation/keystore-extension meta-package that composes the store with the best available platform bridge (RN, web, node) via conditional loading.
Building blocks:
- Generic Store: Keystore Core — core types, interfaces, and the reactive store for secret management.
- Source Bridges:
- React Native Keystore — secure implementation for React Native using Keychain/MMKV.
- Integration Guide: how to adopt the keystore in React Native.
- React Native Keystore — secure implementation for React Native using Keychain/MMKV.
On-chain account lifecycle. Accounts may be backed by keystore-managed keys, or sourced from third parties such as RPC services, indexers, or watch-only feeds.
Unified Extension (recommended): TODO — a @algorandfoundation/accounts-extension meta-package that wraps the account store and conditionally enables the keystore bridge (and future RPC/indexer bridges) based on what the provider exposes.
Building blocks:
- Generic Store: Account Store — reactive store for managing accounts, independent of any specific source.
- Source Bridges:
- Accounts Keystore Bridge — optional bridge that populates the account store from keystore-derived keys when a keystore is available.
Decentralized identity (DID) management. Identities can be derived from keystore-managed seeds, but the store itself is generic and can equally hold imported, resolved, or remotely-issued DIDs.
Unified Extension (recommended): Identities Extension — meta-package that bundles the identity store with the keystore bridge, dynamically loading the bridge only when the provider exposes a keystore.
Building blocks:
- Generic Store: Identity Store — reactive store for managing identities and DIDs.
- Source Bridges:
- Identities Keystore Bridge — optional bridge that builds DID documents from keystore-managed seeds and their derived keys.
Cross-cutting extensions for tracking wallet activity.
Unified Extension (recommended): TODO — a @algorandfoundation/log-extension meta-package that composes the log store with conditional sinks.
Building blocks:
- Generic Store: Log Store — generalized logging store for wallet activity.
- Source Bridges: TODO — adapters that forward log entries to external sinks (remote telemetry, file, console transport, etc.).
To create a new extension, you define an interface that combines your custom state and your API.
Imagine you want an extension that logs all wallet activities.
export interface LoggerState {
logs: string[];
}
export interface LoggerApi {
log: (message: string) => void;
clear: () => void;
}
export interface LoggerExtension extends LoggerState {
logger: LoggerApi;
}import { Store } from "@tanstack/store";
import type { Provider, ExtensionOptions } from "@algorandfoundation/wallet-provider";
const store = new Store<LoggerState>({ logs: [] });
export const loggerExtension: (
provider: Provider,
options: ExtensionOptions,
) => LoggerExtension = () => ({
get logs() {
return store.state.logs;
},
logger: {
log: (message: string) => {
store.setState((state) => ({
logs: [...state.logs, `${new Date().toISOString()}: ${message}`],
}));
},
clear: () => {
store.setState(() => ({ logs: [] }));
},
},
});
export default loggerExtension;Extensions are typically used by extending the base Provider class. This "concrete provider" pattern provides full type safety for both the core provider and all its extensions.
import { Provider } from "@algorandfoundation/wallet-provider";
import { WithKeyStore } from "@algorandfoundation/react-native-keystore";
import { WithLogStore } from "@algorandfoundation/log-store";
import { keyStore } from "./stores/keystore";
import { logStore } from "./stores/logstore";
// 1. Define your application's provider with extensions
export class MyProvider extends Provider<typeof MyProvider.EXTENSIONS> {
static EXTENSIONS = [WithLogStore, WithKeyStore] as const;
// Add properties for type-safe access to extension state/APIs
logs!: string[];
keys!: any[];
status!: string;
}
// 2. Initialize the provider with required options
const provider = new MyProvider(
{
id: "my-app",
name: "My Application",
},
{
logs: { store: logStore },
keystore: {
extension: { store: keyStore },
},
},
);
// 3. Access extension APIs directly on the provider
await provider.keystore.generate({ type: "hd-seed", algorithm: "raw" });
provider.log("Generated a new seed");
console.log(provider.keys); // Reactive list of keysWe would like to acknowledge the following individuals and entities for their contributions and inspiration to this project and the broader Algorand ecosystem:
- Architectural Vision: Algorand Foundation and Bruno Martins (@bmartins) for his role as an Architect.
- use-wallet: TxnLab and Doug Richar (@drichar), along with Gabriel Kuettel (@gabrielkuettel) (currently at Algorand Foundation), for their role in building the
use-wallethook. - Ecosystem Support: The Engineering Teams at Algorand Foundation ranging from AlgoKit, Engineering, and Devrel for their role in providing ecosystem libraries and support.
- Wallets:
- Pera and Will Beaumount (@mjbeau) for their role in the ecosystem as a wallet and the large refactor to React Native.
- Akita for their role in ARC58 adoption. With special thanks to Algorand Foundation engineering to Kyle(@kylebee) and Joe Polny(@joe-p) for their contributions to the ARC58 plugin standards.
- Lute and Andrew Funk (@acfunk) for their contributions to web wallets, readily adopting the latest features.
- Kibis-is and Kieran Roneill (@kieranroneill) for their work as an extension-based wallet and contributions to ARC standards such as ARC27.
- Defly and Kevin Wellenzohn (@k13n) for pioneering wallet features and deep engagement with the Algorand ecosystem and ARC standards.