A modular, extensible wallet provider abstraction for wallet providers.
Inspired by the architecture of OctoKit and TxnLab's use-wallet, this package provides a base for building wallet integrations that can be dynamically extended with additional functionality.
The Provider is the base class that represents a wallet's identity and core configuration. It manages:
- Configuration: Shared options for the provider and its extensions.
- Composition: Orchestrating the application of extensions.
Extensions are modular functions that augment the Provider with specific capabilities.
- Responsibility: Extensions handle domain-specific logic such as account management, transaction signing, or specific API integrations (e.g., Liquid, OIDC).
- Flexibility: They can be independent packages, provided by third parties, or baseline defaults (like KeyStore + BIP39).
- Prefer Composition: Use
withExtensionsto create specialized Provider classes with a fixed set of extensions.
The following example demonstrates how to create a specialized provider with extensions and use it in your application. See the DISCOVERY document for a high level concept of what a Provider's shape may look like. Read our Architectural Decision Records (ADRs) for more context on the project's design.
import {
Provider,
type Extension,
type ProviderOptions,
} from "@algorandfoundation/wallet-provider";
// 1. Define an extension that adds logging capabilities
type LoggerExtension = { log: (msg: string) => void };
const withLogger: Extension<LoggerExtension> = (provider) => {
return {
log: (msg: string) => console.log(`[${provider.name}] ${msg}`),
};
};
// 2. Define an extension that handles accounts (mock example)
type AccountExtension = { accounts: string[]; getAccounts: () => string[] };
const withAccounts: Extension<AccountExtension> = (provider, options) => {
const accounts = options.accounts ? ["address1", "address2"] : [];
return {
accounts: accounts,
getAccounts: () => accounts,
};
};
// 3. Create a specialized Provider class with these extensions
const MyWalletProvider = Provider.withExtensions([withLogger, withAccounts]);
// 4. Instantiate the provider with metadata and options
const config: ProviderOptions = {
id: "my-wallet",
name: "My Wallet",
icon: "https://example.com/icon.png",
};
const wallet = new MyWalletProvider(config, {
accounts: true, // This option is passed to the extensions
});
// 5. Use the augmented functionality
wallet.log("Wallet initialized!");
const accounts = wallet.getAccounts();
console.log("Available accounts:", accounts);Consult the CONTRIBUTING guide for information on development, testing, and pull requests.
We 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.
