Skip to content

Commit c07f6a2

Browse files
authored
Merge pull request #107 from ethereum-optimism/harry/class_based_approach
feat: type-safe hosted wallet generics
2 parents cfbe340 + 80fd6fc commit c07f6a2

File tree

9 files changed

+74
-34
lines changed

9 files changed

+74
-34
lines changed

packages/demo/backend/src/config/verbs.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import { baseSepolia, unichain } from 'viem/chains'
44

55
import { env } from './env.js'
66

7-
let verbsInstance: Verbs
7+
let verbsInstance: Verbs<'privy'>
88

9-
export function createVerbsConfig(): VerbsConfig {
9+
export function createVerbsConfig(): VerbsConfig<'privy'> {
1010
return {
1111
wallet: {
1212
hostedWalletConfig: {
@@ -53,7 +53,7 @@ export function createVerbsConfig(): VerbsConfig {
5353
}
5454
}
5555

56-
export function initializeVerbs(config?: VerbsConfig): void {
56+
export function initializeVerbs(config?: VerbsConfig<'privy'>): void {
5757
const verbsConfig = config || createVerbsConfig()
5858
verbsInstance = new Verbs(verbsConfig)
5959
}

packages/sdk/src/types/verbs.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
import type { ChainConfig } from '@/types/chain.js'
2-
import type { HostedProviderUnion } from '@/wallet/providers/hostedProvider.types.js'
2+
import type {
3+
HostedProviderType,
4+
ProviderSpec,
5+
} from '@/wallet/providers/hostedProvider.types.js'
36

47
import type { LendConfig } from './lend.js'
58

69
/**
710
* Verbs SDK configuration
811
* @description Configuration object for initializing the Verbs SDK
912
*/
10-
export interface VerbsConfig {
13+
export interface VerbsConfig<
14+
THostedWalletProviderType extends HostedProviderType,
15+
> {
1116
/** Wallet configuration */
12-
wallet: WalletConfig
17+
wallet: WalletConfig<THostedWalletProviderType>
1318
/** Lending provider configuration (optional) */
1419
lend?: LendConfig
1520
/** Chains to use for the SDK */
@@ -20,9 +25,9 @@ export interface VerbsConfig {
2025
* Wallet configuration
2126
* @description Configuration for wallet providers
2227
*/
23-
export type WalletConfig = {
28+
export type WalletConfig<THostedProviderType extends HostedProviderType> = {
2429
/** Hosted wallet configuration */
25-
hostedWalletConfig: HostedWalletConfig
30+
hostedWalletConfig: HostedWalletConfig<THostedProviderType>
2631
/** Smart wallet configuration for ERC-4337 infrastructure */
2732
smartWalletConfig: SmartWalletConfig
2833
}
@@ -31,9 +36,11 @@ export type WalletConfig = {
3136
* Hosted wallet configuration
3237
* @description Configuration for hosted wallets / signers
3338
*/
34-
export interface HostedWalletConfig {
39+
export interface HostedWalletConfig<
40+
THostedProviderType extends HostedProviderType,
41+
> {
3542
/** Wallet provider for account creation, management, and signing */
36-
provider: HostedProviderUnion
43+
provider: ProviderSpec<THostedProviderType>
3744
}
3845

3946
/**

packages/sdk/src/verbs.ts

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@ import { LendProviderMorpho } from '@/lend/index.js'
22
import { ChainManager } from '@/services/ChainManager.js'
33
import type { LendProvider } from '@/types/lend.js'
44
import type { VerbsConfig } from '@/types/verbs.js'
5-
import type { HostedWalletProvider } from '@/wallet/providers/base/HostedWalletProvider.js'
65
import type { SmartWalletProvider } from '@/wallet/providers/base/SmartWalletProvider.js'
76
import { DefaultSmartWalletProvider } from '@/wallet/providers/DefaultSmartWalletProvider.js'
7+
import type {
8+
HostedProviderInstanceMap,
9+
HostedProviderType,
10+
} from '@/wallet/providers/hostedProvider.types.js'
811
import { HostedWalletProviderRegistry } from '@/wallet/providers/HostedWalletProviderRegistry.js'
912
import { WalletNamespace } from '@/wallet/WalletNamespace.js'
1013
import { WalletProvider } from '@/wallet/WalletProvider.js'
@@ -13,15 +16,18 @@ import { WalletProvider } from '@/wallet/WalletProvider.js'
1316
* Main Verbs SDK class
1417
* @description Core implementation of the Verbs SDK
1518
*/
16-
export class Verbs {
17-
public readonly wallet: WalletNamespace
19+
export class Verbs<THostedWalletProviderType extends HostedProviderType> {
20+
public readonly wallet: WalletNamespace<
21+
HostedProviderInstanceMap[THostedWalletProviderType],
22+
SmartWalletProvider
23+
>
1824
private chainManager: ChainManager
1925
private lendProvider?: LendProvider
20-
private hostedWalletProvider!: HostedWalletProvider
26+
private hostedWalletProvider!: HostedProviderInstanceMap[THostedWalletProviderType]
2127
private smartWalletProvider!: SmartWalletProvider
2228
private hostedWalletProviderRegistry: HostedWalletProviderRegistry
2329

24-
constructor(config: VerbsConfig) {
30+
constructor(config: VerbsConfig<THostedWalletProviderType>) {
2531
this.chainManager = new ChainManager(config.chains)
2632
this.hostedWalletProviderRegistry = new HostedWalletProviderRegistry()
2733

@@ -58,7 +64,9 @@ export class Verbs {
5864
* @param config - Wallet configuration
5965
* @returns WalletProvider instance
6066
*/
61-
private createWalletProvider(config: VerbsConfig['wallet']) {
67+
private createWalletProvider(
68+
config: VerbsConfig<THostedWalletProviderType>['wallet'],
69+
) {
6270
const hostedWalletProviderConfig = config.hostedWalletConfig.provider
6371
const factory = this.hostedWalletProviderRegistry.getFactory(
6472
hostedWalletProviderConfig.type,
@@ -100,7 +108,9 @@ export class Verbs {
100108
* @param config - Wallet configuration
101109
* @returns WalletNamespace instance
102110
*/
103-
private createWalletNamespace(config: VerbsConfig['wallet']) {
111+
private createWalletNamespace(
112+
config: VerbsConfig<THostedWalletProviderType>['wallet'],
113+
) {
104114
const walletProvider = this.createWalletProvider(config)
105115
return new WalletNamespace(walletProvider)
106116
}

packages/sdk/src/wallet/WalletNamespace.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,20 @@ import type { SmartWallet } from '@/wallet/base/SmartWallet.js'
77
import type { Wallet } from '@/wallet/base/Wallet.js'
88
import type { WalletProvider } from '@/wallet/WalletProvider.js'
99

10+
import type { HostedWalletProvider } from './providers/base/HostedWalletProvider.js'
11+
import type { SmartWalletProvider } from './providers/base/SmartWalletProvider.js'
12+
1013
/**
1114
* Wallet namespace that provides unified wallet operations
1215
* @description Provides access to wallet functionality through a single provider interface
1316
*/
14-
export class WalletNamespace {
15-
private provider: WalletProvider
17+
export class WalletNamespace<
18+
H extends HostedWalletProvider = HostedWalletProvider,
19+
S extends SmartWalletProvider = SmartWalletProvider,
20+
> {
21+
private provider: WalletProvider<H, S>
1622

17-
constructor(provider: WalletProvider) {
23+
constructor(provider: WalletProvider<H, S>) {
1824
this.provider = provider
1925
}
2026

@@ -24,7 +30,7 @@ export class WalletNamespace {
2430
* advanced functionality beyond the unified interface is needed
2531
* @returns The configured hosted wallet provider instance
2632
*/
27-
get hostedWalletProvider() {
33+
get hostedWalletProvider(): H {
2834
return this.provider.hostedWalletProvider
2935
}
3036

@@ -34,7 +40,7 @@ export class WalletNamespace {
3440
* advanced functionality beyond the unified interface is needed
3541
* @returns The configured smart wallet provider instance
3642
*/
37-
get smartWalletProvider() {
43+
get smartWalletProvider(): S {
3844
return this.provider.smartWalletProvider
3945
}
4046

packages/sdk/src/wallet/WalletProvider.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,13 @@ import type { SmartWalletProvider } from '@/wallet/providers/base/SmartWalletPro
1515
* @description Main wallet provider that combines hosted wallet and smart wallet functionality.
1616
* Provides a unified interface for all wallet operations while supporting pluggable providers.
1717
*/
18-
export class WalletProvider {
18+
export class WalletProvider<
19+
H extends HostedWalletProvider,
20+
S extends SmartWalletProvider = SmartWalletProvider,
21+
> {
1922
constructor(
20-
public readonly hostedWalletProvider: HostedWalletProvider,
21-
public readonly smartWalletProvider: SmartWalletProvider,
23+
public readonly hostedWalletProvider: H,
24+
public readonly smartWalletProvider: S,
2225
) {}
2326

2427
/**

packages/sdk/src/wallet/providers/HostedWalletProviderRegistry.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ export class HostedWalletProviderRegistry {
2323
})
2424
}
2525

26-
getFactory<TType extends HostedProviderType>(type: TType) {
26+
getFactory<TType extends HostedProviderType>(
27+
type: TType,
28+
): HostedProviderFactory<TType> {
2729
const factory = this.registry.get(type) as
2830
| HostedProviderFactory<TType>
2931
| undefined

packages/sdk/src/wallet/providers/PrivyHostedWalletProvider.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ export class PrivyHostedWalletProvider extends HostedWalletProvider {
1818
*/
1919
constructor(
2020
private readonly privyClient: PrivyClient,
21-
private readonly chainManager: ChainManager,
21+
chainManager: ChainManager,
2222
) {
23-
super()
23+
super(chainManager)
2424
}
2525

2626
async toVerbsWallet(

packages/sdk/src/wallet/providers/base/HostedWalletProvider.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { ChainManager } from '@/services/ChainManager.js'
12
import type { HostedWalletToVerbsWalletOptions } from '@/types/wallet.js'
23
import type { Wallet } from '@/wallet/base/Wallet.js'
34

@@ -8,6 +9,11 @@ import type { Wallet } from '@/wallet/base/Wallet.js'
89
* as signers for smart wallets or standalone wallet functionality.
910
*/
1011
export abstract class HostedWalletProvider {
12+
protected chainManager: ChainManager
13+
14+
protected constructor(chainManager: ChainManager) {
15+
this.chainManager = chainManager
16+
}
1117
/**
1218
* Convert a hosted wallet to a Verbs wallet
1319
* @description Converts a hosted wallet to a Verbs wallet instance.

packages/sdk/src/wallet/providers/hostedProvider.types.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,36 @@ import type { PrivyClient } from '@privy-io/server-auth'
22

33
import type { ChainManager } from '@/services/ChainManager.js'
44
import type { HostedWalletProvider } from '@/wallet/providers/base/HostedWalletProvider.js'
5+
import type { PrivyHostedWalletProvider } from '@/wallet/providers/PrivyHostedWalletProvider.js'
56

67
export interface PrivyOptions {
78
privyClient: PrivyClient
89
}
9-
1010
export interface HostedProviderConfigMap {
1111
privy: PrivyOptions
1212
}
1313

14+
export interface HostedProviderInstanceMap {
15+
privy: PrivyHostedWalletProvider
16+
}
17+
1418
export type HostedProviderType = keyof HostedProviderConfigMap
1519

1620
export interface HostedProviderDeps {
1721
chainManager: ChainManager
1822
}
1923

24+
export type ProviderSpec<TType extends HostedProviderType> = {
25+
type: TType
26+
config: HostedProviderConfigMap[TType]
27+
}
28+
2029
export interface HostedProviderFactory<
2130
TType extends HostedProviderType = HostedProviderType,
2231
TOptions = HostedProviderConfigMap[TType],
32+
TInstance extends HostedWalletProvider = HostedProviderInstanceMap[TType],
2333
> {
2434
type: TType
2535
validateOptions(options: unknown): options is TOptions
26-
create(deps: HostedProviderDeps, options: TOptions): HostedWalletProvider
36+
create(deps: HostedProviderDeps, options: TOptions): TInstance
2737
}
28-
29-
export type HostedProviderUnion = {
30-
[K in HostedProviderType]: { type: K; config: HostedProviderConfigMap[K] }
31-
}[HostedProviderType]

0 commit comments

Comments
 (0)