@meshconnect/uwc-react 0.7.0 → 0.7.1-snapshot.2c887a2

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.
@@ -6,6 +6,7 @@ export * from './useWallets';
6
6
  export * from './useWallet';
7
7
  export * from './useNetworks';
8
8
  export * from './useSignMessage';
9
+ export * from './useSignTypedData';
9
10
  export * from './useTransaction';
10
11
  export * from './useWalletCapabilities';
11
12
  export * from './useDetectedWallets';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAA;AAC/B,cAAc,oBAAoB,CAAA;AAClC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,cAAc,CAAA;AAC5B,cAAc,cAAc,CAAA;AAC5B,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA;AAC7B,cAAc,kBAAkB,CAAA;AAChC,cAAc,kBAAkB,CAAA;AAChC,cAAc,yBAAyB,CAAA;AACvC,cAAc,sBAAsB,CAAA;AACpC,cAAc,4BAA4B,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAA;AAC/B,cAAc,oBAAoB,CAAA;AAClC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,cAAc,CAAA;AAC5B,cAAc,cAAc,CAAA;AAC5B,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA;AAC7B,cAAc,kBAAkB,CAAA;AAChC,cAAc,oBAAoB,CAAA;AAClC,cAAc,kBAAkB,CAAA;AAChC,cAAc,yBAAyB,CAAA;AACvC,cAAc,sBAAsB,CAAA;AACpC,cAAc,4BAA4B,CAAA"}
@@ -6,6 +6,7 @@ export * from './useWallets';
6
6
  export * from './useWallet';
7
7
  export * from './useNetworks';
8
8
  export * from './useSignMessage';
9
+ export * from './useSignTypedData';
9
10
  export * from './useTransaction';
10
11
  export * from './useWalletCapabilities';
11
12
  export * from './useDetectedWallets';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAA;AAC/B,cAAc,oBAAoB,CAAA;AAClC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,cAAc,CAAA;AAC5B,cAAc,cAAc,CAAA;AAC5B,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA;AAC7B,cAAc,kBAAkB,CAAA;AAChC,cAAc,kBAAkB,CAAA;AAChC,cAAc,yBAAyB,CAAA;AACvC,cAAc,sBAAsB,CAAA;AACpC,cAAc,4BAA4B,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAA;AAC/B,cAAc,oBAAoB,CAAA;AAClC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,cAAc,CAAA;AAC5B,cAAc,cAAc,CAAA;AAC5B,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA;AAC7B,cAAc,kBAAkB,CAAA;AAChC,cAAc,oBAAoB,CAAA;AAClC,cAAc,kBAAkB,CAAA;AAChC,cAAc,yBAAyB,CAAA;AACvC,cAAc,sBAAsB,CAAA;AACpC,cAAc,4BAA4B,CAAA"}
@@ -0,0 +1,26 @@
1
+ import type { UseSignTypedDataReturn } from '@meshconnect/uwc-types';
2
+ /**
3
+ * Hook for signing EIP-712 typed structured data (eth_signTypedData_v4).
4
+ *
5
+ * Used by ERC-3009 (Transfer With Authorization) and EIP-2612 (Permit) relay
6
+ * flows — the user signs an off-chain authorization and Mesh's backend submits
7
+ * the transaction and pays gas. EVM-only (eip155); throws for TON connections.
8
+ *
9
+ * @returns Object containing signTypedData function, loading state, last signature, and error
10
+ * @throws Error if used outside of ConnectionProvider
11
+ * @example
12
+ * ```tsx
13
+ * const { signTypedData, isLoading, signature } = useSignTypedData()
14
+ *
15
+ * const handleSign = async () => {
16
+ * try {
17
+ * const sig = await signTypedData(typedData)
18
+ * // send sig to the relay endpoint
19
+ * } catch (error) {
20
+ * console.error('Failed to sign:', error)
21
+ * }
22
+ * }
23
+ * ```
24
+ */
25
+ export declare function useSignTypedData(): UseSignTypedDataReturn;
26
+ //# sourceMappingURL=useSignTypedData.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSignTypedData.d.ts","sourceRoot":"","sources":["../../src/hooks/useSignTypedData.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAEV,sBAAsB,EAEvB,MAAM,wBAAwB,CAAA;AAE/B;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,gBAAgB,IAAI,sBAAsB,CAyCzD"}
@@ -0,0 +1,62 @@
1
+ import { useContext, useCallback, useState } from 'react';
2
+ import { ConnectionContext } from '../providers/ConnectionProvider';
3
+ /**
4
+ * Hook for signing EIP-712 typed structured data (eth_signTypedData_v4).
5
+ *
6
+ * Used by ERC-3009 (Transfer With Authorization) and EIP-2612 (Permit) relay
7
+ * flows — the user signs an off-chain authorization and Mesh's backend submits
8
+ * the transaction and pays gas. EVM-only (eip155); throws for TON connections.
9
+ *
10
+ * @returns Object containing signTypedData function, loading state, last signature, and error
11
+ * @throws Error if used outside of ConnectionProvider
12
+ * @example
13
+ * ```tsx
14
+ * const { signTypedData, isLoading, signature } = useSignTypedData()
15
+ *
16
+ * const handleSign = async () => {
17
+ * try {
18
+ * const sig = await signTypedData(typedData)
19
+ * // send sig to the relay endpoint
20
+ * } catch (error) {
21
+ * console.error('Failed to sign:', error)
22
+ * }
23
+ * }
24
+ * ```
25
+ */
26
+ export function useSignTypedData() {
27
+ const context = useContext(ConnectionContext);
28
+ if (!context) {
29
+ throw new Error('useSignTypedData must be used within a ConnectionProvider');
30
+ }
31
+ const { connector, session } = context;
32
+ const [isLoading, setIsLoading] = useState(false);
33
+ const [signature, setSignature] = useState();
34
+ const [error, setError] = useState();
35
+ const signTypedData = useCallback(async (typedData) => {
36
+ if (!session.activeAddress) {
37
+ throw new Error('No wallet connected');
38
+ }
39
+ setIsLoading(true);
40
+ setError(undefined);
41
+ try {
42
+ const sig = await connector.signTypedData(typedData);
43
+ setSignature(sig);
44
+ return sig;
45
+ }
46
+ catch (err) {
47
+ const walletError = err;
48
+ setError(walletError);
49
+ throw err;
50
+ }
51
+ finally {
52
+ setIsLoading(false);
53
+ }
54
+ }, [connector, session.activeAddress]);
55
+ return {
56
+ signTypedData,
57
+ isLoading,
58
+ signature,
59
+ error
60
+ };
61
+ }
62
+ //# sourceMappingURL=useSignTypedData.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSignTypedData.js","sourceRoot":"","sources":["../../src/hooks/useSignTypedData.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAA;AAOnE;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,gBAAgB;IAC9B,MAAM,OAAO,GAAG,UAAU,CAAC,iBAAiB,CAAC,CAAA;IAE7C,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAA;IAC9E,CAAC;IAED,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,EAAsB,CAAA;IAChE,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,EAA2B,CAAA;IAE7D,MAAM,aAAa,GAAG,WAAW,CAC/B,KAAK,EAAE,SAA0B,EAAmB,EAAE;QACpD,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;YAC3B,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,GAAG,GAAG,MAAM,SAAS,CAAC,aAAa,CAAC,SAAS,CAAC,CAAA;YACpD,YAAY,CAAC,GAAG,CAAC,CAAA;YACjB,OAAO,GAAG,CAAA;QACZ,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,aAAa;QACb,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.7.0",
3
+ "version": "0.7.1-snapshot.2c887a2",
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": "0.8.0",
20
- "@meshconnect/uwc-types": "0.14.0"
19
+ "@meshconnect/uwc-types": "0.14.0-snapshot.2c887a2",
20
+ "@meshconnect/uwc-core": "0.8.1-snapshot.2c887a2"
21
21
  },
22
22
  "peerDependencies": {
23
23
  "react": "^18.0.0",
@@ -6,6 +6,7 @@ export * from './useWallets'
6
6
  export * from './useWallet'
7
7
  export * from './useNetworks'
8
8
  export * from './useSignMessage'
9
+ export * from './useSignTypedData'
9
10
  export * from './useTransaction'
10
11
  export * from './useWalletCapabilities'
11
12
  export * from './useDetectedWallets'
@@ -0,0 +1,252 @@
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 { useSignTypedData } from './useSignTypedData'
6
+ import type {
7
+ EIP712TypedData,
8
+ UseSignTypedDataReturn
9
+ } from '@meshconnect/uwc-types'
10
+
11
+ // ---------------------------------------------------------------------------
12
+ // Fixtures + helpers
13
+ // ---------------------------------------------------------------------------
14
+
15
+ const USDC_TYPED_DATA: EIP712TypedData = {
16
+ domain: {
17
+ name: 'USD Coin',
18
+ version: '2',
19
+ chainId: 1,
20
+ verifyingContract: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'
21
+ },
22
+ types: {
23
+ TransferWithAuthorization: [
24
+ { name: 'from', type: 'address' },
25
+ { name: 'to', type: 'address' },
26
+ { name: 'value', type: 'uint256' },
27
+ { name: 'validAfter', type: 'uint256' },
28
+ { name: 'validBefore', type: 'uint256' },
29
+ { name: 'nonce', type: 'bytes32' }
30
+ ]
31
+ },
32
+ primaryType: 'TransferWithAuthorization',
33
+ message: {
34
+ from: '0xabc',
35
+ to: '0xdef',
36
+ value: '1000000',
37
+ validAfter: 0,
38
+ validBefore: 9999999999,
39
+ nonce: '0xdeadbeef'
40
+ }
41
+ }
42
+
43
+ /** Minimal stub that satisfies ConnectionContextValue's connector shape. */
44
+ function makeConnector(
45
+ signTypedData: (typedData: EIP712TypedData) => Promise<string>
46
+ ) {
47
+ return {
48
+ signTypedData,
49
+ getSession: () => ({ isConnected: false }),
50
+ isReady: () => true,
51
+ subscribe: () => () => {},
52
+ getWallets: () => [],
53
+ getNetworks: () => []
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Minimal context value that wraps a connector stub. Pass `{ activeAddress }`
59
+ * as an object (not a bare arg) so an explicit `undefined` survives.
60
+ */
61
+ function makeContextValue(
62
+ connector: ReturnType<typeof makeConnector>,
63
+ { activeAddress }: { activeAddress: string | undefined } = {
64
+ activeAddress: '0xUserAddress'
65
+ }
66
+ ) {
67
+ return {
68
+ connector,
69
+ session: {
70
+ isConnected: activeAddress !== undefined,
71
+ walletId: undefined,
72
+ networkId: undefined,
73
+ activeAddress
74
+ },
75
+ wallets: [],
76
+ networks: [],
77
+ isReady: true
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Renders `useSignTypedData` inside a component that exposes its return value
83
+ * via a captured ref. Mirrors useSignSolanaTransaction.test.tsx: createRoot +
84
+ * act, no @testing-library/react (not installed in this package).
85
+ */
86
+ function renderHookInContext(
87
+ contextValue: ReturnType<typeof makeContextValue>
88
+ ) {
89
+ const captured: { current: UseSignTypedDataReturn | null } = { current: null }
90
+
91
+ function Capture() {
92
+ captured.current = useSignTypedData()
93
+ return null
94
+ }
95
+
96
+ const container = document.createElement('div')
97
+ document.body.appendChild(container)
98
+ const root = createRoot(container)
99
+
100
+ async function render() {
101
+ await act(async () => {
102
+ root.render(
103
+ <ConnectionContext.Provider value={contextValue as never}>
104
+ <Capture />
105
+ </ConnectionContext.Provider>
106
+ )
107
+ })
108
+ }
109
+
110
+ async function unmount() {
111
+ await act(async () => {
112
+ root.unmount()
113
+ })
114
+ document.body.removeChild(container)
115
+ }
116
+
117
+ return { captured, render, unmount }
118
+ }
119
+
120
+ // ---------------------------------------------------------------------------
121
+ // Tests
122
+ // ---------------------------------------------------------------------------
123
+
124
+ describe('useSignTypedData', () => {
125
+ let container: HTMLDivElement
126
+ let root: ReturnType<typeof createRoot>
127
+
128
+ beforeEach(() => {
129
+ container = document.createElement('div')
130
+ document.body.appendChild(container)
131
+ root = createRoot(container)
132
+ })
133
+
134
+ afterEach(async () => {
135
+ await act(async () => {
136
+ root.unmount()
137
+ })
138
+ if (document.body.contains(container)) {
139
+ document.body.removeChild(container)
140
+ }
141
+ })
142
+
143
+ it('throws when rendered outside ConnectionProvider', () => {
144
+ function BareHook() {
145
+ useSignTypedData()
146
+ return null
147
+ }
148
+
149
+ expect(() => {
150
+ const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
151
+ try {
152
+ act(() => {
153
+ root.render(<BareHook />)
154
+ })
155
+ } finally {
156
+ errorSpy.mockRestore()
157
+ }
158
+ }).toThrowError('useSignTypedData must be used within a ConnectionProvider')
159
+ })
160
+
161
+ it('returns the signature from connector.signTypedData on success', async () => {
162
+ const mockSign = vi.fn().mockResolvedValue('0xtypedsig')
163
+ const ctxValue = makeContextValue(makeConnector(mockSign))
164
+ const { captured, render, unmount } = renderHookInContext(ctxValue)
165
+
166
+ await render()
167
+ expect(captured.current).not.toBeNull()
168
+
169
+ let result: string | undefined
170
+ await act(async () => {
171
+ result = await captured.current!.signTypedData(USDC_TYPED_DATA)
172
+ })
173
+
174
+ expect(mockSign).toHaveBeenCalledOnce()
175
+ expect(mockSign).toHaveBeenCalledWith(USDC_TYPED_DATA)
176
+ expect(result).toBe('0xtypedsig')
177
+ expect(captured.current!.signature).toBe('0xtypedsig')
178
+ await unmount()
179
+ })
180
+
181
+ it('sets error state and re-throws when connector rejects', async () => {
182
+ const walletError = { code: 4001, message: 'User rejected' }
183
+ const mockSign = vi.fn().mockRejectedValue(walletError)
184
+ const ctxValue = makeContextValue(makeConnector(mockSign))
185
+ const { captured, render, unmount } = renderHookInContext(ctxValue)
186
+
187
+ await render()
188
+
189
+ let thrownError: unknown
190
+ await act(async () => {
191
+ try {
192
+ await captured.current!.signTypedData(USDC_TYPED_DATA)
193
+ } catch (err) {
194
+ thrownError = err
195
+ }
196
+ })
197
+
198
+ expect(thrownError).toBe(walletError)
199
+ expect(captured.current!.error).toBe(walletError)
200
+ await unmount()
201
+ })
202
+
203
+ it('sets isLoading=true during call and false after success', async () => {
204
+ let resolveSign!: (v: string) => void
205
+ const pendingSign = new Promise<string>(res => {
206
+ resolveSign = res
207
+ })
208
+ const mockSign = vi.fn().mockReturnValue(pendingSign)
209
+ const ctxValue = makeContextValue(makeConnector(mockSign))
210
+ const { captured, render, unmount } = renderHookInContext(ctxValue)
211
+
212
+ await render()
213
+
214
+ let callPromise: Promise<string>
215
+ await act(async () => {
216
+ callPromise = captured.current!.signTypedData(USDC_TYPED_DATA)
217
+ })
218
+
219
+ expect(captured.current!.isLoading).toBe(true)
220
+
221
+ await act(async () => {
222
+ resolveSign('0xsig')
223
+ await callPromise!
224
+ })
225
+
226
+ expect(captured.current!.isLoading).toBe(false)
227
+ await unmount()
228
+ })
229
+
230
+ it('throws "No wallet connected" when there is no active address', async () => {
231
+ const mockSign = vi.fn()
232
+ const ctxValue = makeContextValue(makeConnector(mockSign), {
233
+ activeAddress: undefined
234
+ })
235
+ const { captured, render, unmount } = renderHookInContext(ctxValue)
236
+
237
+ await render()
238
+
239
+ let thrownError: unknown
240
+ await act(async () => {
241
+ try {
242
+ await captured.current!.signTypedData(USDC_TYPED_DATA)
243
+ } catch (err) {
244
+ thrownError = err
245
+ }
246
+ })
247
+
248
+ expect((thrownError as Error).message).toBe('No wallet connected')
249
+ expect(mockSign).not.toHaveBeenCalled()
250
+ await unmount()
251
+ })
252
+ })
@@ -0,0 +1,73 @@
1
+ import { useContext, useCallback, useState } from 'react'
2
+ import { ConnectionContext } from '../providers/ConnectionProvider'
3
+ import type {
4
+ EIP712TypedData,
5
+ UseSignTypedDataReturn,
6
+ WalletError
7
+ } from '@meshconnect/uwc-types'
8
+
9
+ /**
10
+ * Hook for signing EIP-712 typed structured data (eth_signTypedData_v4).
11
+ *
12
+ * Used by ERC-3009 (Transfer With Authorization) and EIP-2612 (Permit) relay
13
+ * flows — the user signs an off-chain authorization and Mesh's backend submits
14
+ * the transaction and pays gas. EVM-only (eip155); throws for TON connections.
15
+ *
16
+ * @returns Object containing signTypedData function, loading state, last signature, and error
17
+ * @throws Error if used outside of ConnectionProvider
18
+ * @example
19
+ * ```tsx
20
+ * const { signTypedData, isLoading, signature } = useSignTypedData()
21
+ *
22
+ * const handleSign = async () => {
23
+ * try {
24
+ * const sig = await signTypedData(typedData)
25
+ * // send sig to the relay endpoint
26
+ * } catch (error) {
27
+ * console.error('Failed to sign:', error)
28
+ * }
29
+ * }
30
+ * ```
31
+ */
32
+ export function useSignTypedData(): UseSignTypedDataReturn {
33
+ const context = useContext(ConnectionContext)
34
+
35
+ if (!context) {
36
+ throw new Error('useSignTypedData must be used within a ConnectionProvider')
37
+ }
38
+
39
+ const { connector, session } = context
40
+ const [isLoading, setIsLoading] = useState(false)
41
+ const [signature, setSignature] = useState<string | undefined>()
42
+ const [error, setError] = useState<WalletError | undefined>()
43
+
44
+ const signTypedData = useCallback(
45
+ async (typedData: EIP712TypedData): Promise<string> => {
46
+ if (!session.activeAddress) {
47
+ throw new Error('No wallet connected')
48
+ }
49
+
50
+ setIsLoading(true)
51
+ setError(undefined)
52
+ try {
53
+ const sig = await connector.signTypedData(typedData)
54
+ setSignature(sig)
55
+ return sig
56
+ } catch (err) {
57
+ const walletError = err as WalletError
58
+ setError(walletError)
59
+ throw err
60
+ } finally {
61
+ setIsLoading(false)
62
+ }
63
+ },
64
+ [connector, session.activeAddress]
65
+ )
66
+
67
+ return {
68
+ signTypedData,
69
+ isLoading,
70
+ signature,
71
+ error
72
+ }
73
+ }