@meshconnect/uwc-core 1.0.3 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +247 -76
- package/dist/events.d.ts +241 -5
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +77 -1
- package/dist/events.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/logger/create-logger.d.ts +32 -0
- package/dist/logger/create-logger.d.ts.map +1 -0
- package/dist/logger/create-logger.js +79 -0
- package/dist/logger/create-logger.js.map +1 -0
- package/dist/managers/event-manager.d.ts.map +1 -1
- package/dist/managers/event-manager.js +5 -1
- package/dist/managers/event-manager.js.map +1 -1
- package/dist/observability/connect-observer.d.ts +33 -0
- package/dist/observability/connect-observer.d.ts.map +1 -0
- package/dist/observability/connect-observer.js +115 -0
- package/dist/observability/connect-observer.js.map +1 -0
- package/dist/observability/focus-tracker.d.ts +42 -0
- package/dist/observability/focus-tracker.d.ts.map +1 -0
- package/dist/observability/focus-tracker.js +57 -0
- package/dist/observability/focus-tracker.js.map +1 -0
- package/dist/observability/instrument-handoff.d.ts +54 -0
- package/dist/observability/instrument-handoff.d.ts.map +1 -0
- package/dist/observability/instrument-handoff.js +173 -0
- package/dist/observability/instrument-handoff.js.map +1 -0
- package/dist/observability/lifecycle-bridge.d.ts +69 -0
- package/dist/observability/lifecycle-bridge.d.ts.map +1 -0
- package/dist/observability/lifecycle-bridge.js +155 -0
- package/dist/observability/lifecycle-bridge.js.map +1 -0
- package/dist/observability/scrub.d.ts +31 -0
- package/dist/observability/scrub.d.ts.map +1 -0
- package/dist/observability/scrub.js +167 -0
- package/dist/observability/scrub.js.map +1 -0
- package/dist/observability/telemetry.d.ts +157 -0
- package/dist/observability/telemetry.d.ts.map +1 -0
- package/dist/observability/telemetry.js +373 -0
- package/dist/observability/telemetry.js.map +1 -0
- package/dist/services/connection-service.d.ts +23 -1
- package/dist/services/connection-service.d.ts.map +1 -1
- package/dist/services/connection-service.js +128 -19
- package/dist/services/connection-service.js.map +1 -1
- package/dist/services/network-switch-service.d.ts +2 -1
- package/dist/services/network-switch-service.d.ts.map +1 -1
- package/dist/services/network-switch-service.js +45 -16
- package/dist/services/network-switch-service.js.map +1 -1
- package/dist/services/signature-service.d.ts +14 -7
- package/dist/services/signature-service.d.ts.map +1 -1
- package/dist/services/signature-service.js +56 -58
- package/dist/services/signature-service.js.map +1 -1
- package/dist/services/transaction-service.d.ts +19 -5
- package/dist/services/transaction-service.d.ts.map +1 -1
- package/dist/services/transaction-service.js +55 -38
- package/dist/services/transaction-service.js.map +1 -1
- package/dist/universal-wallet-connector.d.ts +56 -1
- package/dist/universal-wallet-connector.d.ts.map +1 -1
- package/dist/universal-wallet-connector.js +207 -110
- package/dist/universal-wallet-connector.js.map +1 -1
- package/dist/utils/id.d.ts +2 -0
- package/dist/utils/id.d.ts.map +1 -0
- package/dist/utils/id.js +41 -0
- package/dist/utils/id.js.map +1 -0
- package/dist/utils/resolve-session-provider.d.ts +10 -0
- package/dist/utils/resolve-session-provider.d.ts.map +1 -0
- package/dist/utils/resolve-session-provider.js +21 -0
- package/dist/utils/resolve-session-provider.js.map +1 -0
- package/dist/utils/to-wallet-error.d.ts +35 -0
- package/dist/utils/to-wallet-error.d.ts.map +1 -0
- package/dist/utils/to-wallet-error.js +130 -0
- package/dist/utils/to-wallet-error.js.map +1 -0
- package/dist/utils/validate-wallet-session.d.ts +15 -0
- package/dist/utils/validate-wallet-session.d.ts.map +1 -0
- package/dist/utils/validate-wallet-session.js +29 -0
- package/dist/utils/validate-wallet-session.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +3 -0
- package/dist/version.js.map +1 -0
- package/package.json +6 -6
- package/src/events.ts +326 -14
- package/src/index.ts +10 -0
- package/src/logger/create-logger.test.ts +126 -0
- package/src/logger/create-logger.ts +112 -0
- package/src/managers/event-manager.test.ts +55 -0
- package/src/managers/event-manager.ts +5 -1
- package/src/observability/connect-observer.test.ts +214 -0
- package/src/observability/connect-observer.ts +128 -0
- package/src/observability/focus-tracker.test.ts +191 -0
- package/src/observability/focus-tracker.ts +96 -0
- package/src/observability/instrument-handoff.test.ts +397 -0
- package/src/observability/instrument-handoff.ts +231 -0
- package/src/observability/lifecycle-bridge.test.ts +398 -0
- package/src/observability/lifecycle-bridge.ts +216 -0
- package/src/observability/scrub.test.ts +183 -0
- package/src/observability/scrub.ts +174 -0
- package/src/observability/telemetry.test.ts +549 -0
- package/src/observability/telemetry.ts +546 -0
- package/src/services/connection-service.test.ts +261 -1
- package/src/services/connection-service.ts +167 -38
- package/src/services/network-switch-service.test.ts +51 -1
- package/src/services/network-switch-service.ts +56 -24
- package/src/services/signature-service.test.ts +181 -76
- package/src/services/signature-service.ts +75 -85
- package/src/services/transaction-service.test.ts +270 -69
- package/src/services/transaction-service.ts +74 -51
- package/src/universal-wallet-connector.instrumentation-guard.test.ts +199 -0
- package/src/universal-wallet-connector.observability.test.ts +610 -0
- package/src/universal-wallet-connector.test.ts +27 -146
- package/src/universal-wallet-connector.ts +279 -142
- package/src/utils/id.test.ts +15 -0
- package/src/utils/id.ts +48 -0
- package/src/utils/resolve-session-provider.test.ts +54 -0
- package/src/utils/resolve-session-provider.ts +25 -0
- package/src/utils/to-wallet-error.test.ts +181 -0
- package/src/utils/to-wallet-error.ts +152 -0
- package/src/utils/validate-wallet-session.test.ts +77 -0
- package/src/utils/validate-wallet-session.ts +40 -0
- package/src/version.test.ts +11 -0
- package/src/version.ts +2 -0
|
@@ -12,6 +12,7 @@ import type {
|
|
|
12
12
|
import type { SessionManager } from '../managers/session-manager'
|
|
13
13
|
import type { EventManager } from '../managers/event-manager'
|
|
14
14
|
import type { ServiceOptions } from './connection-service'
|
|
15
|
+
import { instrumentHandoff } from '../observability/instrument-handoff'
|
|
15
16
|
|
|
16
17
|
export class NetworkSwitchService {
|
|
17
18
|
private isLoading = false
|
|
@@ -22,7 +23,9 @@ export class NetworkSwitchService {
|
|
|
22
23
|
private sessionManager: SessionManager,
|
|
23
24
|
private connectors: Map<ConnectionMode, Connector>,
|
|
24
25
|
private usingIntegratedBrowser: boolean,
|
|
25
|
-
private eventManager: EventManager
|
|
26
|
+
private eventManager: EventManager,
|
|
27
|
+
// Observational wallet-response deadline for the handoff timeout marker.
|
|
28
|
+
private walletResponseTimeoutMs: number
|
|
26
29
|
) {}
|
|
27
30
|
|
|
28
31
|
getLoadingState() {
|
|
@@ -61,7 +64,13 @@ export class NetworkSwitchService {
|
|
|
61
64
|
throw new Error('No connection mode found in session')
|
|
62
65
|
}
|
|
63
66
|
|
|
64
|
-
// Get the provider for the current connection mode
|
|
67
|
+
// Get the provider for the current connection mode.
|
|
68
|
+
// NOTE: deliberately NOT `resolveSessionProvider` (utils) — this
|
|
69
|
+
// pre-existing dispatch groups `tonConnect` with `injected` (a TON switch
|
|
70
|
+
// only proceeds when the wallet also carries injected-provider metadata,
|
|
71
|
+
// whose `supportedNetworkIds` gate below relies on), while the shared
|
|
72
|
+
// helper yields undefined for TON. Unifying them changes TON switch
|
|
73
|
+
// behaviour and needs its own change.
|
|
65
74
|
let provider: SupportedProvider | undefined
|
|
66
75
|
|
|
67
76
|
if (
|
|
@@ -106,6 +115,10 @@ export class NetworkSwitchService {
|
|
|
106
115
|
session.activeNetwork
|
|
107
116
|
)
|
|
108
117
|
|
|
118
|
+
// A no-op switch (target chain already active) still hands off to the wallet
|
|
119
|
+
// on some connectors; flag it so the funnel can separate it from real switches.
|
|
120
|
+
const targetAlreadyActive = networkId === session.activeNetwork?.id
|
|
121
|
+
|
|
109
122
|
try {
|
|
110
123
|
// Set loading states
|
|
111
124
|
this.isLoading = true
|
|
@@ -115,28 +128,47 @@ export class NetworkSwitchService {
|
|
|
115
128
|
isWaitingForUserApproval: requiresApproval
|
|
116
129
|
})
|
|
117
130
|
|
|
118
|
-
// Perform the network switch and get the new address
|
|
119
|
-
//
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
network,
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
131
|
+
// Perform the network switch and get the new address. Instrument the
|
|
132
|
+
// wallet hand-off HERE — after validation + the `networkSwitching` emit —
|
|
133
|
+
// so `awaitingWallet` marks the real wallet boundary; pre-wallet validation
|
|
134
|
+
// failures throw above and never enter the funnel.
|
|
135
|
+
// Pass the appropriate provider based on connection mode.
|
|
136
|
+
const result = await instrumentHandoff(
|
|
137
|
+
{
|
|
138
|
+
eventManager: this.eventManager,
|
|
139
|
+
timeoutMs: this.walletResponseTimeoutMs,
|
|
140
|
+
signal: options?.signal
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
operation: 'switchNetwork',
|
|
144
|
+
connectionMode: session.connectionMode,
|
|
145
|
+
walletId: currentWallet.id,
|
|
146
|
+
namespace: network.namespace,
|
|
147
|
+
chainId: network.id,
|
|
148
|
+
targetAlreadyActive
|
|
149
|
+
},
|
|
150
|
+
async () => {
|
|
151
|
+
if (
|
|
152
|
+
(session.connectionMode === 'injected' ||
|
|
153
|
+
session.connectionMode === 'tonConnect') &&
|
|
154
|
+
provider
|
|
155
|
+
) {
|
|
156
|
+
return await activeConnector.switchNetwork(
|
|
157
|
+
network,
|
|
158
|
+
provider as
|
|
159
|
+
| ExtensionInjectedProvider
|
|
160
|
+
| IntegratedBrowserInjectedProvider
|
|
161
|
+
)
|
|
162
|
+
} else if (session.connectionMode === 'walletConnect' && provider) {
|
|
163
|
+
return await activeConnector.switchNetwork(
|
|
164
|
+
network,
|
|
165
|
+
provider as WalletConnectProvider
|
|
166
|
+
)
|
|
167
|
+
} else {
|
|
168
|
+
return await activeConnector.switchNetwork(network)
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
)
|
|
140
172
|
|
|
141
173
|
options?.signal?.throwIfAborted()
|
|
142
174
|
|
|
@@ -1,16 +1,59 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
|
2
2
|
import { SignatureService } from './signature-service'
|
|
3
3
|
import { SessionManager } from '../managers/session-manager'
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
import { EventManager } from '../managers/event-manager'
|
|
5
|
+
import type {
|
|
6
|
+
ConnectionMode,
|
|
7
|
+
Connector,
|
|
8
|
+
WalletMetadata
|
|
9
|
+
} from '../../../types/src'
|
|
10
|
+
|
|
11
|
+
// The service owns the whole wallet-boundary op: validation (session guards,
|
|
12
|
+
// connector capability, provider resolution) BEFORE the funnel, then the
|
|
13
|
+
// instrumented connector call — same layering as ConnectionService /
|
|
14
|
+
// NetworkSwitchService. The façade only delegates + emits `error`.
|
|
15
|
+
|
|
16
|
+
const injectedWallet = {
|
|
17
|
+
id: 'metamask',
|
|
18
|
+
name: 'MetaMask',
|
|
19
|
+
extensionInjectedProvider: { supportedNetworkIds: ['eip155:1'] }
|
|
20
|
+
} as unknown as WalletMetadata
|
|
21
|
+
|
|
22
|
+
const walletWithoutProviders = {
|
|
23
|
+
id: 'metamask',
|
|
24
|
+
name: 'MetaMask'
|
|
25
|
+
} as unknown as WalletMetadata
|
|
26
|
+
|
|
27
|
+
describe('SignatureService.signMessage', () => {
|
|
7
28
|
let signatureService: SignatureService
|
|
8
29
|
let sessionManager: SessionManager
|
|
30
|
+
let eventManager: EventManager
|
|
9
31
|
let mockConnector: Connector
|
|
10
32
|
let connectors: Map<ConnectionMode, Connector>
|
|
33
|
+
let events: string[]
|
|
34
|
+
|
|
35
|
+
const connectSession = (
|
|
36
|
+
mode: ConnectionMode = 'injected',
|
|
37
|
+
wallet: WalletMetadata = injectedWallet
|
|
38
|
+
) =>
|
|
39
|
+
sessionManager.updateSession({
|
|
40
|
+
connectionMode: mode,
|
|
41
|
+
activeWallet: wallet,
|
|
42
|
+
activeAddress: '0x123'
|
|
43
|
+
})
|
|
11
44
|
|
|
12
45
|
beforeEach(() => {
|
|
13
46
|
sessionManager = new SessionManager()
|
|
47
|
+
eventManager = new EventManager()
|
|
48
|
+
events = []
|
|
49
|
+
for (const name of [
|
|
50
|
+
'awaitingWallet',
|
|
51
|
+
'walletSucceeded',
|
|
52
|
+
'walletRejected',
|
|
53
|
+
'walletFailed'
|
|
54
|
+
] as const) {
|
|
55
|
+
eventManager.on(name, () => events.push(name))
|
|
56
|
+
}
|
|
14
57
|
|
|
15
58
|
mockConnector = {
|
|
16
59
|
connect: vi.fn(),
|
|
@@ -23,41 +66,78 @@ describe('SignatureService', () => {
|
|
|
23
66
|
connectors = new Map()
|
|
24
67
|
connectors.set('injected', mockConnector)
|
|
25
68
|
|
|
26
|
-
signatureService = new SignatureService(
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
69
|
+
signatureService = new SignatureService(
|
|
70
|
+
sessionManager,
|
|
71
|
+
connectors,
|
|
72
|
+
false,
|
|
73
|
+
eventManager,
|
|
74
|
+
60_000
|
|
75
|
+
)
|
|
31
76
|
})
|
|
32
77
|
|
|
33
|
-
it('
|
|
78
|
+
it('throws "No active wallet" first on an empty session (public façade message)', async () => {
|
|
34
79
|
await expect(signatureService.signMessage('test message')).rejects.toThrow(
|
|
35
|
-
'No active
|
|
80
|
+
'No active wallet'
|
|
36
81
|
)
|
|
82
|
+
expect(events).toEqual([])
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
it('records walletSucceeded when an abort races completion (the wallet settled first)', async () => {
|
|
86
|
+
// The signature EXISTS — the funnel must record the wallet's truthful
|
|
87
|
+
// outcome; the abort still rejects the caller, after the terminal.
|
|
88
|
+
connectSession()
|
|
89
|
+
const controller = new AbortController()
|
|
90
|
+
vi.mocked(mockConnector.signMessage!).mockImplementation(async () => {
|
|
91
|
+
controller.abort()
|
|
92
|
+
return '0xsig' as never
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
await expect(
|
|
96
|
+
signatureService.signMessage('m', { signal: controller.signal })
|
|
97
|
+
).rejects.toMatchObject({ name: 'AbortError' })
|
|
98
|
+
expect(events).toEqual(['awaitingWallet', 'walletSucceeded'])
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
it('rejects with a CUSTOM abort reason after the wallet settled — success terminal, no spurious failure', async () => {
|
|
102
|
+
connectSession()
|
|
103
|
+
const controller = new AbortController()
|
|
104
|
+
vi.mocked(mockConnector.signMessage!).mockImplementation(async () => {
|
|
105
|
+
controller.abort(new Error('nav away'))
|
|
106
|
+
return '0xsig' as never
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
await expect(
|
|
110
|
+
signatureService.signMessage('m', { signal: controller.signal })
|
|
111
|
+
).rejects.toThrow('nav away')
|
|
112
|
+
expect(events).toEqual(['awaitingWallet', 'walletSucceeded'])
|
|
37
113
|
})
|
|
38
114
|
|
|
39
|
-
it('
|
|
115
|
+
it('throws when no active address', async () => {
|
|
40
116
|
sessionManager.updateSession({
|
|
41
|
-
connectionMode: 'injected'
|
|
117
|
+
connectionMode: 'injected',
|
|
118
|
+
activeWallet: injectedWallet
|
|
42
119
|
})
|
|
43
120
|
|
|
44
121
|
await expect(signatureService.signMessage('test message')).rejects.toThrow(
|
|
45
122
|
'No active wallet address'
|
|
46
123
|
)
|
|
124
|
+
expect(events).toEqual([])
|
|
47
125
|
})
|
|
48
126
|
|
|
49
|
-
it('
|
|
127
|
+
it('throws when connector not found', async () => {
|
|
50
128
|
sessionManager.updateSession({
|
|
51
129
|
connectionMode: 'unknown' as ConnectionMode,
|
|
130
|
+
activeWallet: injectedWallet,
|
|
52
131
|
activeAddress: '0x123'
|
|
53
132
|
})
|
|
54
133
|
|
|
55
134
|
await expect(signatureService.signMessage('test message')).rejects.toThrow(
|
|
56
135
|
'No connector found for connection mode: unknown'
|
|
57
136
|
)
|
|
137
|
+
expect(events).toEqual([])
|
|
58
138
|
})
|
|
59
139
|
|
|
60
|
-
it('
|
|
140
|
+
it('throws when connector does not support signing', async () => {
|
|
61
141
|
const connectorWithoutSign = {
|
|
62
142
|
connect: vi.fn(),
|
|
63
143
|
disconnect: vi.fn(),
|
|
@@ -65,53 +145,66 @@ describe('SignatureService', () => {
|
|
|
65
145
|
switchNetwork: vi.fn()
|
|
66
146
|
}
|
|
67
147
|
connectors.set('injected', connectorWithoutSign)
|
|
68
|
-
|
|
69
|
-
sessionManager.updateSession({
|
|
70
|
-
connectionMode: 'injected',
|
|
71
|
-
activeAddress: '0x123'
|
|
72
|
-
})
|
|
148
|
+
connectSession()
|
|
73
149
|
|
|
74
150
|
await expect(signatureService.signMessage('test message')).rejects.toThrow(
|
|
75
151
|
'Connector does not support message signing'
|
|
76
152
|
)
|
|
153
|
+
expect(events).toEqual([])
|
|
77
154
|
})
|
|
78
155
|
|
|
79
|
-
it('
|
|
80
|
-
|
|
81
|
-
connectionMode: 'injected',
|
|
82
|
-
activeAddress: '0x123'
|
|
83
|
-
})
|
|
156
|
+
it('a missing provider fails BEFORE the funnel (validation, not a wallet outcome)', async () => {
|
|
157
|
+
connectSession('injected', walletWithoutProviders)
|
|
84
158
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
expect(result).toBe('0xsignature')
|
|
88
|
-
expect(mockConnector.signMessage).toHaveBeenCalledWith(
|
|
89
|
-
'test message',
|
|
90
|
-
provider
|
|
159
|
+
await expect(signatureService.signMessage('test message')).rejects.toThrow(
|
|
160
|
+
'No provider available for the active wallet'
|
|
91
161
|
)
|
|
162
|
+
expect(events).toEqual([])
|
|
163
|
+
expect(mockConnector.signMessage).not.toHaveBeenCalled()
|
|
92
164
|
})
|
|
93
165
|
|
|
94
|
-
it('
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
})
|
|
166
|
+
it('signs and emits awaitingWallet → walletSucceeded around the wallet call', async () => {
|
|
167
|
+
connectSession()
|
|
168
|
+
|
|
169
|
+
const result = await signatureService.signMessage('test message')
|
|
99
170
|
|
|
100
|
-
const provider = {} as any
|
|
101
|
-
const result = await signatureService.signMessage('test message', provider)
|
|
102
171
|
expect(result).toBe('0xsignature')
|
|
172
|
+
// The resolved session provider is threaded to the connector.
|
|
103
173
|
expect(mockConnector.signMessage).toHaveBeenCalledWith(
|
|
104
174
|
'test message',
|
|
105
|
-
|
|
175
|
+
injectedWallet.extensionInjectedProvider
|
|
106
176
|
)
|
|
177
|
+
expect(events).toEqual(['awaitingWallet', 'walletSucceeded'])
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
it('a user rejection closes the handoff as walletRejected', async () => {
|
|
181
|
+
connectSession()
|
|
182
|
+
vi.mocked(mockConnector.signMessage!).mockRejectedValueOnce({ code: 4001 })
|
|
183
|
+
|
|
184
|
+
await expect(signatureService.signMessage('test message')).rejects.toEqual({
|
|
185
|
+
code: 4001
|
|
186
|
+
})
|
|
187
|
+
expect(events).toEqual(['awaitingWallet', 'walletRejected'])
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
it('tonConnect enters the funnel with the connector resolving its own provider', async () => {
|
|
191
|
+
connectors.set('tonConnect', mockConnector)
|
|
192
|
+
connectSession('tonConnect', walletWithoutProviders)
|
|
193
|
+
|
|
194
|
+
await signatureService.signMessage('hello')
|
|
195
|
+
|
|
196
|
+
expect(mockConnector.signMessage).toHaveBeenCalledWith('hello')
|
|
197
|
+
expect(events).toEqual(['awaitingWallet', 'walletSucceeded'])
|
|
107
198
|
})
|
|
108
199
|
})
|
|
109
200
|
|
|
110
201
|
describe('SignatureService.signTypedData', () => {
|
|
111
202
|
let signatureService: SignatureService
|
|
112
203
|
let sessionManager: SessionManager
|
|
204
|
+
let eventManager: EventManager
|
|
113
205
|
let mockConnector: Connector
|
|
114
206
|
let connectors: Map<ConnectionMode, Connector>
|
|
207
|
+
let events: string[]
|
|
115
208
|
|
|
116
209
|
const typedData = {
|
|
117
210
|
domain: {
|
|
@@ -143,6 +236,15 @@ describe('SignatureService.signTypedData', () => {
|
|
|
143
236
|
|
|
144
237
|
beforeEach(() => {
|
|
145
238
|
sessionManager = new SessionManager()
|
|
239
|
+
eventManager = new EventManager()
|
|
240
|
+
events = []
|
|
241
|
+
for (const name of [
|
|
242
|
+
'awaitingWallet',
|
|
243
|
+
'walletSucceeded',
|
|
244
|
+
'walletFailed'
|
|
245
|
+
] as const) {
|
|
246
|
+
eventManager.on(name, () => events.push(name))
|
|
247
|
+
}
|
|
146
248
|
|
|
147
249
|
mockConnector = {
|
|
148
250
|
connect: vi.fn(),
|
|
@@ -156,33 +258,23 @@ describe('SignatureService.signTypedData', () => {
|
|
|
156
258
|
connectors = new Map()
|
|
157
259
|
connectors.set('injected', mockConnector)
|
|
158
260
|
|
|
159
|
-
signatureService = new SignatureService(
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
261
|
+
signatureService = new SignatureService(
|
|
262
|
+
sessionManager,
|
|
263
|
+
connectors,
|
|
264
|
+
false,
|
|
265
|
+
eventManager,
|
|
266
|
+
60_000
|
|
165
267
|
)
|
|
166
268
|
})
|
|
167
269
|
|
|
168
|
-
it('
|
|
169
|
-
sessionManager.updateSession({ connectionMode: 'injected' })
|
|
170
|
-
await expect(signatureService.signTypedData(typedData)).rejects.toThrow(
|
|
171
|
-
'No active wallet address'
|
|
172
|
-
)
|
|
173
|
-
})
|
|
174
|
-
|
|
175
|
-
it('should throw when connector not found', async () => {
|
|
176
|
-
sessionManager.updateSession({
|
|
177
|
-
connectionMode: 'unknown' as ConnectionMode,
|
|
178
|
-
activeAddress: '0x123'
|
|
179
|
-
})
|
|
270
|
+
it('throws "No active wallet" first on an empty session', async () => {
|
|
180
271
|
await expect(signatureService.signTypedData(typedData)).rejects.toThrow(
|
|
181
|
-
'No
|
|
272
|
+
'No active wallet'
|
|
182
273
|
)
|
|
274
|
+
expect(events).toEqual([])
|
|
183
275
|
})
|
|
184
276
|
|
|
185
|
-
it('
|
|
277
|
+
it('throws when connector does not support signTypedData', async () => {
|
|
186
278
|
const connectorWithoutTypedData = {
|
|
187
279
|
connect: vi.fn(),
|
|
188
280
|
disconnect: vi.fn(),
|
|
@@ -192,56 +284,69 @@ describe('SignatureService.signTypedData', () => {
|
|
|
192
284
|
connectors.set('injected', connectorWithoutTypedData)
|
|
193
285
|
sessionManager.updateSession({
|
|
194
286
|
connectionMode: 'injected',
|
|
287
|
+
activeWallet: injectedWallet,
|
|
195
288
|
activeAddress: '0x123'
|
|
196
289
|
})
|
|
197
290
|
await expect(signatureService.signTypedData(typedData)).rejects.toThrow(
|
|
198
291
|
'Connector does not support typed data signing'
|
|
199
292
|
)
|
|
293
|
+
expect(events).toEqual([])
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
it('throws the façade message for TON, outside the funnel', async () => {
|
|
297
|
+
// Shape of the real TonConnectConnector: NO signTypedData. The TON branch
|
|
298
|
+
// must win over the generic capability guard, not be shadowed by it.
|
|
299
|
+
const tonConnector = { ...mockConnector }
|
|
300
|
+
delete (tonConnector as { signTypedData?: unknown }).signTypedData
|
|
301
|
+
connectors.set('tonConnect', tonConnector)
|
|
302
|
+
sessionManager.updateSession({
|
|
303
|
+
connectionMode: 'tonConnect',
|
|
304
|
+
activeWallet: injectedWallet,
|
|
305
|
+
activeAddress: '0x123'
|
|
306
|
+
})
|
|
307
|
+
await expect(signatureService.signTypedData(typedData)).rejects.toThrow(
|
|
308
|
+
'signTypedData is not supported for TON wallets'
|
|
309
|
+
)
|
|
310
|
+
expect(events).toEqual([])
|
|
200
311
|
})
|
|
201
312
|
|
|
202
|
-
it('
|
|
313
|
+
it('a missing injected provider fails BEFORE the funnel', async () => {
|
|
203
314
|
sessionManager.updateSession({
|
|
204
315
|
connectionMode: 'injected',
|
|
316
|
+
activeWallet: walletWithoutProviders,
|
|
205
317
|
activeAddress: '0x123'
|
|
206
318
|
})
|
|
207
319
|
await expect(signatureService.signTypedData(typedData)).rejects.toThrow(
|
|
208
|
-
'
|
|
320
|
+
'No provider available for the active wallet'
|
|
209
321
|
)
|
|
322
|
+
expect(events).toEqual([])
|
|
210
323
|
})
|
|
211
324
|
|
|
212
|
-
it('
|
|
325
|
+
it('signs via the injected connector inside the funnel', async () => {
|
|
213
326
|
sessionManager.updateSession({
|
|
214
327
|
connectionMode: 'injected',
|
|
328
|
+
activeWallet: injectedWallet,
|
|
215
329
|
activeAddress: '0x123'
|
|
216
330
|
})
|
|
217
|
-
const
|
|
218
|
-
const result = await signatureService.signTypedData(typedData, provider)
|
|
331
|
+
const result = await signatureService.signTypedData(typedData)
|
|
219
332
|
expect(result).toBe('0xtypedsig')
|
|
220
333
|
expect(mockConnector.signTypedData).toHaveBeenCalledWith(
|
|
221
334
|
typedData,
|
|
222
|
-
|
|
335
|
+
injectedWallet.extensionInjectedProvider
|
|
223
336
|
)
|
|
337
|
+
expect(events).toEqual(['awaitingWallet', 'walletSucceeded'])
|
|
224
338
|
})
|
|
225
339
|
|
|
226
|
-
it('
|
|
340
|
+
it('signs via walletConnect with no provider (connector resolves its own)', async () => {
|
|
227
341
|
connectors.set('walletConnect', mockConnector)
|
|
228
342
|
sessionManager.updateSession({
|
|
229
343
|
connectionMode: 'walletConnect',
|
|
344
|
+
activeWallet: walletWithoutProviders,
|
|
230
345
|
activeAddress: '0x123'
|
|
231
346
|
})
|
|
232
347
|
const result = await signatureService.signTypedData(typedData)
|
|
233
348
|
expect(result).toBe('0xtypedsig')
|
|
234
349
|
expect(mockConnector.signTypedData).toHaveBeenCalledWith(typedData)
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
it('should throw for tonConnect mode', async () => {
|
|
238
|
-
connectors.set('tonConnect', mockConnector)
|
|
239
|
-
sessionManager.updateSession({
|
|
240
|
-
connectionMode: 'tonConnect',
|
|
241
|
-
activeAddress: '0x123'
|
|
242
|
-
})
|
|
243
|
-
await expect(signatureService.signTypedData(typedData)).rejects.toThrow(
|
|
244
|
-
'signTypedData is not supported for connection mode: tonConnect'
|
|
245
|
-
)
|
|
350
|
+
expect(events).toEqual(['awaitingWallet', 'walletSucceeded'])
|
|
246
351
|
})
|
|
247
352
|
})
|