@meshconnect/uwc-react 0.11.9-snapshot.089e410 → 0.11.9-snapshot.1a75c56
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/dist/hooks/useSignMessage.d.ts +3 -3
- package/dist/hooks/useSignMessage.d.ts.map +1 -1
- package/dist/hooks/useSignMessage.js +8 -6
- package/dist/hooks/useSignMessage.js.map +1 -1
- package/package.json +3 -3
- package/src/hooks/useSignMessage.test.tsx +195 -0
- package/src/hooks/useSignMessage.ts +10 -7
|
@@ -9,9 +9,9 @@ import type { UseSignMessageReturn } from '@meshconnect/uwc-types';
|
|
|
9
9
|
*
|
|
10
10
|
* const handleSign = async () => {
|
|
11
11
|
* try {
|
|
12
|
-
* const
|
|
13
|
-
* console.log('Signature:',
|
|
14
|
-
* // signature state
|
|
12
|
+
* const { signature, address } = await signMessage('Hello World')
|
|
13
|
+
* console.log('Signature:', signature, 'signed by', address)
|
|
14
|
+
* // The `signature` state also holds result.signature after the call.
|
|
15
15
|
* } catch (error) {
|
|
16
16
|
* console.error('Failed to sign:', error)
|
|
17
17
|
* }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSignMessage.d.ts","sourceRoot":"","sources":["../../src/hooks/useSignMessage.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"useSignMessage.d.ts","sourceRoot":"","sources":["../../src/hooks/useSignMessage.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAGV,oBAAoB,EAErB,MAAM,wBAAwB,CAAA;AAE/B;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,cAAc,IAAI,oBAAoB,CA0CrD"}
|
|
@@ -10,9 +10,9 @@ import { useConnectionContext } from './useConnectionContext';
|
|
|
10
10
|
*
|
|
11
11
|
* const handleSign = async () => {
|
|
12
12
|
* try {
|
|
13
|
-
* const
|
|
14
|
-
* console.log('Signature:',
|
|
15
|
-
* // signature state
|
|
13
|
+
* const { signature, address } = await signMessage('Hello World')
|
|
14
|
+
* console.log('Signature:', signature, 'signed by', address)
|
|
15
|
+
* // The `signature` state also holds result.signature after the call.
|
|
16
16
|
* } catch (error) {
|
|
17
17
|
* console.error('Failed to sign:', error)
|
|
18
18
|
* }
|
|
@@ -35,9 +35,11 @@ export function useSignMessage() {
|
|
|
35
35
|
setIsLoading(true);
|
|
36
36
|
setError(undefined);
|
|
37
37
|
try {
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
const result = await connector.signMessage(message);
|
|
39
|
+
// Keep the `signature` state as the SignatureType for backward-compat
|
|
40
|
+
// display; callers needing the exact signer read `result.address`.
|
|
41
|
+
setSignature(result.signature);
|
|
42
|
+
return result;
|
|
41
43
|
}
|
|
42
44
|
catch (err) {
|
|
43
45
|
const walletError = err;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSignMessage.js","sourceRoot":"","sources":["../../src/hooks/useSignMessage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAC7C,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;
|
|
1
|
+
{"version":3,"file":"useSignMessage.js","sourceRoot":"","sources":["../../src/hooks/useSignMessage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAC7C,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAQ7D;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,cAAc;IAC5B,MAAM,OAAO,GAAG,oBAAoB,CAAC,gBAAgB,CAAC,CAAA;IACtD,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,OAAO,CAAA;IACtC,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IACjD,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,EAA6B,CAAA;IACvE,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,EAA2B,CAAA;IAE7D,MAAM,WAAW,GAAG,WAAW,CAC7B,KAAK,EAAE,OAAe,EAA4B,EAAE;QAClD,IAAI,CAAC,SAAS,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;YACzC,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAA;QACxC,CAAC;QAED,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAA;QACxC,CAAC;QAED,YAAY,CAAC,IAAI,CAAC,CAAA;QAClB,QAAQ,CAAC,SAAS,CAAC,CAAA;QACnB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,WAAW,CAAC,OAAO,CAAC,CAAA;YACnD,sEAAsE;YACtE,mEAAmE;YACnE,YAAY,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;YAC9B,OAAO,MAAM,CAAA;QACf,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,WAAW,GAAG,GAAkB,CAAA;YACtC,QAAQ,CAAC,WAAW,CAAC,CAAA;YACrB,MAAM,GAAG,CAAA;QACX,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,KAAK,CAAC,CAAA;QACrB,CAAC;IACH,CAAC,EACD,CAAC,SAAS,EAAE,OAAO,CAAC,aAAa,CAAC,CACnC,CAAA;IAED,OAAO;QACL,WAAW;QACX,SAAS;QACT,SAAS;QACT,KAAK;KACN,CAAA;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meshconnect/uwc-react",
|
|
3
|
-
"version": "0.11.9-snapshot.
|
|
3
|
+
"version": "0.11.9-snapshot.1a75c56",
|
|
4
4
|
"description": "React hooks and components for Universal Wallet Connector",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
"src"
|
|
17
17
|
],
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@meshconnect/uwc-core": "1.4.1-snapshot.
|
|
20
|
-
"@meshconnect/uwc-types": "0.20.0-snapshot.
|
|
19
|
+
"@meshconnect/uwc-core": "1.4.1-snapshot.1a75c56",
|
|
20
|
+
"@meshconnect/uwc-types": "0.20.0-snapshot.1a75c56"
|
|
21
21
|
},
|
|
22
22
|
"peerDependencies": {
|
|
23
23
|
"react": "^18.0.0",
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import React, { act } from 'react'
|
|
2
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
|
|
3
|
+
import { createRoot } from 'react-dom/client'
|
|
4
|
+
import { ConnectionContext } from '../providers/ConnectionProvider'
|
|
5
|
+
import { useSignMessage } from './useSignMessage'
|
|
6
|
+
import type {
|
|
7
|
+
SignatureResult,
|
|
8
|
+
UseSignMessageReturn
|
|
9
|
+
} from '@meshconnect/uwc-types'
|
|
10
|
+
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
// Fixtures + helpers
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
|
|
15
|
+
const SIGNER_ADDRESS = '0xSignerAddress'
|
|
16
|
+
|
|
17
|
+
const RESULT: SignatureResult = {
|
|
18
|
+
signature: { type: 'standard', signature: '0xdeadbeef' },
|
|
19
|
+
address: SIGNER_ADDRESS
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Minimal stub that satisfies ConnectionContextValue's connector shape. */
|
|
23
|
+
function makeConnector(
|
|
24
|
+
signMessage: (message: string) => Promise<SignatureResult>
|
|
25
|
+
) {
|
|
26
|
+
return {
|
|
27
|
+
signMessage,
|
|
28
|
+
getSession: () => ({ isConnected: false }),
|
|
29
|
+
isReady: () => true,
|
|
30
|
+
subscribe: () => () => {},
|
|
31
|
+
getWallets: () => [],
|
|
32
|
+
getNetworks: () => []
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Minimal context value that wraps a connector stub. Pass `{ activeAddress }`
|
|
38
|
+
* as an object (not a bare arg) so an explicit `undefined` survives.
|
|
39
|
+
*/
|
|
40
|
+
function makeContextValue(
|
|
41
|
+
connector: ReturnType<typeof makeConnector>,
|
|
42
|
+
{ activeAddress }: { activeAddress: string | undefined } = {
|
|
43
|
+
activeAddress: '0xUserAddress'
|
|
44
|
+
}
|
|
45
|
+
) {
|
|
46
|
+
return {
|
|
47
|
+
connector,
|
|
48
|
+
session: {
|
|
49
|
+
isConnected: activeAddress !== undefined,
|
|
50
|
+
walletId: undefined,
|
|
51
|
+
networkId: undefined,
|
|
52
|
+
activeAddress
|
|
53
|
+
},
|
|
54
|
+
wallets: [],
|
|
55
|
+
networks: [],
|
|
56
|
+
isReady: true
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function renderHookInContext(
|
|
61
|
+
contextValue: ReturnType<typeof makeContextValue>
|
|
62
|
+
) {
|
|
63
|
+
const captured: { current: UseSignMessageReturn | null } = { current: null }
|
|
64
|
+
|
|
65
|
+
function Capture() {
|
|
66
|
+
captured.current = useSignMessage()
|
|
67
|
+
return null
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const container = document.createElement('div')
|
|
71
|
+
document.body.appendChild(container)
|
|
72
|
+
const root = createRoot(container)
|
|
73
|
+
|
|
74
|
+
async function render() {
|
|
75
|
+
await act(async () => {
|
|
76
|
+
root.render(
|
|
77
|
+
<ConnectionContext.Provider value={contextValue as never}>
|
|
78
|
+
<Capture />
|
|
79
|
+
</ConnectionContext.Provider>
|
|
80
|
+
)
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function unmount() {
|
|
85
|
+
await act(async () => {
|
|
86
|
+
root.unmount()
|
|
87
|
+
})
|
|
88
|
+
document.body.removeChild(container)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return { captured, render, unmount }
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
// Tests
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
|
|
98
|
+
describe('useSignMessage', () => {
|
|
99
|
+
let container: HTMLDivElement
|
|
100
|
+
let root: ReturnType<typeof createRoot>
|
|
101
|
+
|
|
102
|
+
beforeEach(() => {
|
|
103
|
+
container = document.createElement('div')
|
|
104
|
+
document.body.appendChild(container)
|
|
105
|
+
root = createRoot(container)
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
afterEach(async () => {
|
|
109
|
+
await act(async () => {
|
|
110
|
+
root.unmount()
|
|
111
|
+
})
|
|
112
|
+
if (document.body.contains(container)) {
|
|
113
|
+
document.body.removeChild(container)
|
|
114
|
+
}
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
it('resolves to the full SignatureResult { signature, address }', async () => {
|
|
118
|
+
const mockSign = vi.fn().mockResolvedValue(RESULT)
|
|
119
|
+
const ctxValue = makeContextValue(makeConnector(mockSign))
|
|
120
|
+
const { captured, render, unmount } = renderHookInContext(ctxValue)
|
|
121
|
+
|
|
122
|
+
await render()
|
|
123
|
+
expect(captured.current).not.toBeNull()
|
|
124
|
+
|
|
125
|
+
let result: SignatureResult | undefined
|
|
126
|
+
await act(async () => {
|
|
127
|
+
result = await captured.current!.signMessage('Hello World')
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
expect(mockSign).toHaveBeenCalledOnce()
|
|
131
|
+
expect(mockSign).toHaveBeenCalledWith('Hello World')
|
|
132
|
+
expect(result).toEqual(RESULT)
|
|
133
|
+
await unmount()
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
it('stores result.signature (the SignatureType) in the signature state', async () => {
|
|
137
|
+
const mockSign = vi.fn().mockResolvedValue(RESULT)
|
|
138
|
+
const ctxValue = makeContextValue(makeConnector(mockSign))
|
|
139
|
+
const { captured, render, unmount } = renderHookInContext(ctxValue)
|
|
140
|
+
|
|
141
|
+
await render()
|
|
142
|
+
|
|
143
|
+
await act(async () => {
|
|
144
|
+
await captured.current!.signMessage('Hello World')
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
expect(captured.current!.signature).toEqual(RESULT.signature)
|
|
148
|
+
await unmount()
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
it('sets error state and re-throws when connector rejects', async () => {
|
|
152
|
+
const walletError = { code: 4001, message: 'User rejected' }
|
|
153
|
+
const mockSign = vi.fn().mockRejectedValue(walletError)
|
|
154
|
+
const ctxValue = makeContextValue(makeConnector(mockSign))
|
|
155
|
+
const { captured, render, unmount } = renderHookInContext(ctxValue)
|
|
156
|
+
|
|
157
|
+
await render()
|
|
158
|
+
|
|
159
|
+
let thrownError: unknown
|
|
160
|
+
await act(async () => {
|
|
161
|
+
try {
|
|
162
|
+
await captured.current!.signMessage('Hello World')
|
|
163
|
+
} catch (err) {
|
|
164
|
+
thrownError = err
|
|
165
|
+
}
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
expect(thrownError).toBe(walletError)
|
|
169
|
+
expect(captured.current!.error).toBe(walletError)
|
|
170
|
+
await unmount()
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
it('throws "No wallet connected" when there is no active address', async () => {
|
|
174
|
+
const mockSign = vi.fn()
|
|
175
|
+
const ctxValue = makeContextValue(makeConnector(mockSign), {
|
|
176
|
+
activeAddress: undefined
|
|
177
|
+
})
|
|
178
|
+
const { captured, render, unmount } = renderHookInContext(ctxValue)
|
|
179
|
+
|
|
180
|
+
await render()
|
|
181
|
+
|
|
182
|
+
let thrownError: unknown
|
|
183
|
+
await act(async () => {
|
|
184
|
+
try {
|
|
185
|
+
await captured.current!.signMessage('Hello World')
|
|
186
|
+
} catch (err) {
|
|
187
|
+
thrownError = err
|
|
188
|
+
}
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
expect((thrownError as Error).message).toBe('No wallet connected')
|
|
192
|
+
expect(mockSign).not.toHaveBeenCalled()
|
|
193
|
+
await unmount()
|
|
194
|
+
})
|
|
195
|
+
})
|
|
@@ -2,6 +2,7 @@ import { useCallback, useState } from 'react'
|
|
|
2
2
|
import { useConnectionContext } from './useConnectionContext'
|
|
3
3
|
import type {
|
|
4
4
|
SignatureType,
|
|
5
|
+
SignatureResult,
|
|
5
6
|
UseSignMessageReturn,
|
|
6
7
|
WalletError
|
|
7
8
|
} from '@meshconnect/uwc-types'
|
|
@@ -16,9 +17,9 @@ import type {
|
|
|
16
17
|
*
|
|
17
18
|
* const handleSign = async () => {
|
|
18
19
|
* try {
|
|
19
|
-
* const
|
|
20
|
-
* console.log('Signature:',
|
|
21
|
-
* // signature state
|
|
20
|
+
* const { signature, address } = await signMessage('Hello World')
|
|
21
|
+
* console.log('Signature:', signature, 'signed by', address)
|
|
22
|
+
* // The `signature` state also holds result.signature after the call.
|
|
22
23
|
* } catch (error) {
|
|
23
24
|
* console.error('Failed to sign:', error)
|
|
24
25
|
* }
|
|
@@ -33,7 +34,7 @@ export function useSignMessage(): UseSignMessageReturn {
|
|
|
33
34
|
const [error, setError] = useState<WalletError | undefined>()
|
|
34
35
|
|
|
35
36
|
const signMessage = useCallback(
|
|
36
|
-
async (message: string): Promise<
|
|
37
|
+
async (message: string): Promise<SignatureResult> => {
|
|
37
38
|
if (!connector || !session.activeAddress) {
|
|
38
39
|
throw new Error('No wallet connected')
|
|
39
40
|
}
|
|
@@ -45,9 +46,11 @@ export function useSignMessage(): UseSignMessageReturn {
|
|
|
45
46
|
setIsLoading(true)
|
|
46
47
|
setError(undefined)
|
|
47
48
|
try {
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
const result = await connector.signMessage(message)
|
|
50
|
+
// Keep the `signature` state as the SignatureType for backward-compat
|
|
51
|
+
// display; callers needing the exact signer read `result.address`.
|
|
52
|
+
setSignature(result.signature)
|
|
53
|
+
return result
|
|
51
54
|
} catch (err) {
|
|
52
55
|
const walletError = err as WalletError
|
|
53
56
|
setError(walletError)
|