Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions packages/core/src/manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,14 @@ export interface WalletManagerOptions {
}

export interface WalletManagerConfig {
wallets?: WalletAdapterConfig[]
wallets?: WalletAdapterConfig<any>[]
networks?: Record<string, NetworkConfig>
defaultNetwork?: string
options?: WalletManagerOptions
}

export class WalletManager {
public _clients: Map<WalletKey, BaseWallet> = new Map()
public _clients: Map<WalletKey, BaseWallet<any>> = new Map()
private _capabilities: Map<WalletKey, WalletCapabilities> = new Map()
private baseNetworkConfig: Record<string, NetworkConfig>
public store: Store<State>
Expand Down Expand Up @@ -249,9 +249,9 @@ export class WalletManager {

// ---------- Scoped Store Access ----------------------------------- //

private createStoreAccessor(walletKey: string): AdapterStoreAccessor {
private createStoreAccessor<T extends WalletAccount>(walletKey: string): AdapterStoreAccessor<T> {
return {
getWalletState: () => this.store.state.wallets[walletKey],
getWalletState: () => this.store.state.wallets[walletKey] as WalletState<T> | undefined,
getActiveWallet: () => this.store.state.activeWallet,
getActiveNetwork: () => this.store.state.activeNetwork,
getState: () => this.store.state,
Expand All @@ -275,7 +275,7 @@ export class WalletManager {

// ---------- Wallets ----------------------------------------------- //

private initializeWallets(walletConfigs: WalletAdapterConfig[]) {
private initializeWallets(walletConfigs: WalletAdapterConfig<any>[]) {
this.logger.info('Initializing wallets...')

for (const config of walletConfigs) {
Expand All @@ -288,7 +288,7 @@ export class WalletManager {

const storeAccessor = this.createStoreAccessor(walletKey)

const params: AdapterConstructorParams = {
const params: AdapterConstructorParams<any> = {
id: config.id,
metadata: config.metadata,
store: storeAccessor,
Expand Down Expand Up @@ -329,7 +329,7 @@ export class WalletManager {
}
}

public get wallets(): BaseWallet[] {
public get wallets(): BaseWallet<any>[] {
return [...this._clients.values()]
}

Expand Down Expand Up @@ -377,12 +377,12 @@ export class WalletManager {
}
}

public get availableWallets(): BaseWallet[] {
public get availableWallets(): BaseWallet<any>[] {
const activeNetwork = this.store.state.activeNetwork
return this.wallets.filter((w) => this.isWalletAvailable(w.walletKey, activeNetwork))
}

public getWallet(walletKey: WalletKey): BaseWallet | undefined {
public getWallet(walletKey: WalletKey): BaseWallet<any> | undefined {
return this._clients.get(walletKey)
}

Expand Down Expand Up @@ -558,7 +558,7 @@ export class WalletManager {

// ---------- Active Wallet ----------------------------------------- //

public get activeWallet(): BaseWallet | null {
public get activeWallet(): BaseWallet<any> | null {
const state = this.store.state
const activeWallet = this.wallets.find((wallet) => wallet.walletKey === state.activeWallet)
if (!activeWallet) {
Expand Down Expand Up @@ -598,7 +598,7 @@ export class WalletManager {

// ---------- Sign Transactions ------------------------------------- //

public get signTransactions(): BaseWallet['signTransactions'] {
public get signTransactions(): BaseWallet<any>['signTransactions'] {
if (!this.activeWallet) {
this.logger.error('No active wallet found!')
throw new Error('No active wallet found!')
Expand Down
15 changes: 9 additions & 6 deletions packages/core/src/wallets/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,16 @@ import type {
WalletMetadata
} from 'src/wallets/types'

export abstract class BaseWallet<TOptions = Record<string, unknown>> {
export abstract class BaseWallet<
TType extends WalletAccount = WalletAccount,
TOptions = Record<string, unknown>
> {
public readonly id: string
public readonly walletKey: string
public metadata: WalletMetadata

protected options: TOptions
protected store: AdapterStoreAccessor
protected store: AdapterStoreAccessor<TType>
protected getAlgodClient: () => algosdk.Algodv2

public subscribe: (callback: (state: State) => void) => () => void
Expand All @@ -31,7 +34,7 @@ export abstract class BaseWallet<TOptions = Record<string, unknown>> {
subscribe,
getAlgodClient,
options
}: AdapterConstructorParams<TOptions>) {
}: AdapterConstructorParams<TType, TOptions>) {
this.id = id
this.walletKey = id
this.metadata = { ...metadata }
Expand All @@ -48,7 +51,7 @@ export abstract class BaseWallet<TOptions = Record<string, unknown>> {

// ---------- Public Methods ---------------------------------------- //

public abstract connect(args?: Record<string, any>): Promise<WalletAccount[]>
public abstract connect(args?: Record<string, any>): Promise<TType[]>
public abstract disconnect(): Promise<void>
public abstract resumeSession(): Promise<void>

Expand Down Expand Up @@ -105,7 +108,7 @@ export abstract class BaseWallet<TOptions = Record<string, unknown>> {
return this.id.toUpperCase()
}

public get accounts(): WalletAccount[] {
public get accounts(): TType[] {
const walletState = this.store.getWalletState()
return walletState ? walletState.accounts : []
}
Expand All @@ -114,7 +117,7 @@ export abstract class BaseWallet<TOptions = Record<string, unknown>> {
return this.accounts.map((account) => account.address)
}

public get activeAccount(): WalletAccount | null {
public get activeAccount(): TType | null {
const walletState = this.store.getWalletState()
return walletState ? walletState.activeAccount : null
}
Expand Down
8 changes: 4 additions & 4 deletions packages/core/src/wallets/custom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ const ICON = `data:image/svg+xml;base64,${btoa(`

const CUSTOM_WALLET_ID = 'custom' as const

export class CustomWallet extends BaseWallet<CustomWalletOptions> {
export class CustomWallet extends BaseWallet<WalletAccount, CustomWalletOptions> {
private provider: CustomProvider

constructor(params: AdapterConstructorParams<CustomWalletOptions>) {
constructor(params: AdapterConstructorParams<WalletAccount, CustomWalletOptions>) {
super(params)

if (!params.options?.provider) {
Expand Down Expand Up @@ -182,11 +182,11 @@ export class CustomWallet extends BaseWallet<CustomWalletOptions> {

// ---------- Factory Function ----------------------------------------- //

export function custom(options: CustomWalletOptions): WalletAdapterConfig {
export function custom(options: CustomWalletOptions): WalletAdapterConfig<WalletAccount> {
return {
id: CUSTOM_WALLET_ID,
metadata: CustomWallet.defaultMetadata,
Adapter: CustomWallet as unknown as WalletAdapterConfig['Adapter'],
Adapter: CustomWallet as unknown as WalletAdapterConfig<WalletAccount>['Adapter'],
options: options as unknown as Record<string, unknown>
}
}
33 changes: 18 additions & 15 deletions packages/core/src/wallets/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,14 @@ export interface WalletCapabilities {
* All mutations are pre-bound to the adapter's wallet key —
* adapters cannot accidentally modify another wallet's state.
*/
export interface AdapterStoreAccessor {
getWalletState(): WalletState | undefined
export interface AdapterStoreAccessor<TType extends WalletAccount = WalletAccount> {
getWalletState(): WalletState<TType> | undefined
getActiveWallet(): WalletKey | null
getActiveNetwork(): string
getState(): State
addWallet(wallet: WalletState): void
addWallet(wallet: WalletState<TType>): void
removeWallet(): void
setAccounts(accounts: WalletAccount[]): void
setAccounts(accounts: TType[]): void
setActiveAccount(address: string): void
setActive(): void
}
Expand All @@ -73,10 +73,13 @@ export interface AdapterStoreAccessor {
* Generic over the options type so adapters receive typed options
* without unsafe casts.
*/
export interface AdapterConstructorParams<TOptions = Record<string, unknown>> {
export interface AdapterConstructorParams<
TType extends WalletAccount = WalletAccount,
TOptions = Record<string, unknown>
> {
id: string
metadata: WalletMetadata
store: AdapterStoreAccessor
store: AdapterStoreAccessor<TType>
subscribe: (callback: (state: State) => void) => () => void
getAlgodClient: () => algosdk.Algodv2
options?: TOptions
Expand All @@ -88,13 +91,13 @@ export interface AdapterConstructorParams<TOptions = Record<string, unknown>> {
* the manager handles heterogeneous adapter configs in a single array.
* Type safety lives in the factory function signature, not here.
*/
export interface WalletAdapterConfig {
export interface WalletAdapterConfig<TType extends WalletAccount = WalletAccount> {
/** Unique identifier for this wallet adapter */
id: string
/** Display metadata (name, icon) */
metadata: WalletMetadata
/** The adapter class constructor */
Adapter: new (params: AdapterConstructorParams) => BaseWallet
Adapter: new (params: AdapterConstructorParams<TType>) => BaseWallet
/** Wallet-specific options, passed through to the adapter constructor */
options?: Record<string, unknown>
/** Network capabilities — which networks this wallet supports */
Expand All @@ -108,27 +111,27 @@ export interface WalletAdapterConfig {
* (e.g. `useWallet()` in React). Defined in core so all framework
* adapters share a single type.
*/
export interface Wallet {
export interface Wallet<TType extends WalletAccount = WalletAccount> {
id: string
walletKey: string
metadata: WalletMetadata
accounts: WalletAccount[]
activeAccount: WalletAccount | null
accounts: TType[]
activeAccount: TType | null
isConnected: boolean
isActive: boolean
canSignData: boolean
canUsePrivateKey: boolean
connect: (args?: Record<string, any>) => Promise<WalletAccount[]>
connect: (args?: Record<string, any>) => Promise<TType[]>
disconnect: () => Promise<void>
setActive: () => void
setActiveAccount: (address: string) => void
}

// ---------- Wallet State ------------------------------------------- //

export type WalletState = {
accounts: WalletAccount[]
activeAccount: WalletAccount | null
export type WalletState<T = WalletAccount> = {
accounts: T[]
activeAccount: T | null
}

// ---------- Transaction Types -------------------------------------- //
Expand Down
15 changes: 8 additions & 7 deletions packages/frameworks/react/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
SignDataResponse,
SignMetadata,
type Wallet,
type WalletAccount,
WalletManager
} from '@txnlab/use-wallet'
import algosdk from 'algosdk'
Expand Down Expand Up @@ -120,7 +121,7 @@ export const useNetwork = () => {
}
}

export const useWallet = () => {
export const useWallet = <T extends WalletAccount = WalletAccount>() => {
const context = React.useContext(WalletContext)

if (!context) {
Expand All @@ -137,14 +138,14 @@ export const useWallet = () => {
const activeNetwork = useStore(manager.store, (state) => state.activeNetwork)

const transformToWallet = React.useCallback(
(wallet: BaseWallet): Wallet => {
<T extends WalletAccount>(wallet: BaseWallet<T>): Wallet<T> => {
const walletState = walletStateMap[wallet.walletKey]
return {
id: wallet.id,
walletKey: wallet.walletKey,
metadata: wallet.metadata,
accounts: walletState?.accounts ?? [],
activeAccount: walletState?.activeAccount ?? null,
accounts: (walletState?.accounts as T[]) ?? [],
activeAccount: (walletState?.activeAccount as T) ?? null,
isConnected: !!walletState,
isActive: wallet.walletKey === activeWalletId,
canSignData: wallet.canSignData ?? false,
Expand All @@ -159,16 +160,16 @@ export const useWallet = () => {
)

const wallets = React.useMemo(() => {
return [...manager.wallets.values()].map(transformToWallet)
return [...manager.wallets.values()].map((wallet) => transformToWallet(wallet))
}, [manager, transformToWallet])

const availableWallets = React.useMemo(() => {
return manager.availableWallets.map(transformToWallet)
return manager.availableWallets.map((wallet) => transformToWallet(wallet))
}, [manager, transformToWallet, activeNetwork])

const activeBaseWallet = activeWalletId ? manager.getWallet(activeWalletId) || null : null
const activeWallet = React.useMemo(() => {
return activeBaseWallet ? transformToWallet(activeBaseWallet) : null
return activeBaseWallet ? transformToWallet<T>(activeBaseWallet) : null
}, [activeBaseWallet, transformToWallet])

const activeWalletAccounts = activeWallet?.accounts ?? null
Expand Down
20 changes: 10 additions & 10 deletions packages/frameworks/solid/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,17 @@ import type {

export * from '@txnlab/use-wallet'

export interface Wallet {
export interface Wallet<T extends WalletAccount = WalletAccount> {
id: string
walletKey: WalletKey
metadata: WalletMetadata
readonly accounts: WalletAccount[]
readonly activeAccount: WalletAccount | null
readonly accounts: T[]
readonly activeAccount: T | null
readonly isConnected: boolean
readonly isActive: boolean
canSignData: boolean
canUsePrivateKey: boolean
connect: (args?: Record<string, any>) => Promise<WalletAccount[]>
connect: (args?: Record<string, any>) => Promise<T[]>
disconnect: () => Promise<void>
setActive: () => void
setActiveAccount: (address: string) => void
Expand Down Expand Up @@ -138,7 +138,7 @@ export const useNetwork = () => {
}
}

export const useWallet = () => {
export const useWallet = <T extends WalletAccount = WalletAccount>() => {
const manager = createMemo(() => useWalletManager())

const managerStatus = useStore(manager().store, (state) => state.managerStatus)
Expand All @@ -148,16 +148,16 @@ export const useWallet = () => {
const walletStateMap = useStore(manager().store, (state) => state.wallets)
const activeWalletId = useStore(manager().store, (state) => state.activeWallet)

const transformToWallet = (wallet: BaseWallet): Wallet => {
const transformToWallet = <T extends WalletAccount>(wallet: BaseWallet<T>): Wallet<T> => {
return {
id: wallet.id,
walletKey: wallet.walletKey,
metadata: wallet.metadata,
get accounts() {
return walletStateMap()[wallet.walletKey]?.accounts ?? []
return (walletStateMap()[wallet.walletKey]?.accounts as T[]) ?? []
},
get activeAccount() {
return walletStateMap()[wallet.walletKey]?.activeAccount ?? null
return (walletStateMap()[wallet.walletKey]?.activeAccount as T) ?? null
},
get isConnected() {
return !!walletStateMap()[wallet.walletKey]
Expand All @@ -174,7 +174,7 @@ export const useWallet = () => {
}
}

const wallets = [...manager().wallets].map(transformToWallet)
const wallets = [...manager().wallets].map((wallet) => transformToWallet(wallet))

const activeNetwork = useStore(manager().store, (state) => state.activeNetwork)

Expand All @@ -193,7 +193,7 @@ export const useWallet = () => {

const activeWallet = createMemo(() => {
const id = activeWalletId()
return id ? (wallets.find((w) => w.walletKey === id) ?? null) : null
return id ? ((wallets.find((w) => w.walletKey === id) as Wallet<T>) ?? null) : null
})

const activeWalletAccounts = createMemo(() => {
Expand Down
Loading
Loading