@1sat/react 0.0.6 → 0.0.8

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.
@@ -1,56 +1,21 @@
1
1
  import type { ReactNode } from 'react';
2
2
  export interface ConnectButtonProps {
3
- /** Custom class name for the button */
4
3
  className?: string;
5
- /** Custom styles for the button */
6
4
  style?: React.CSSProperties;
7
- /** Text to show when disconnected */
8
5
  connectLabel?: ReactNode;
9
- /** Text to show when connecting */
10
6
  connectingLabel?: ReactNode;
11
- /** Text to show when connected (receives address) */
12
- connectedLabel?: ReactNode | ((address: string) => ReactNode);
13
- /** Called after successful connection */
7
+ connectedLabel?: ReactNode | ((identityKey: string) => ReactNode);
14
8
  onConnect?: () => void;
15
- /** Called after disconnection */
16
9
  onDisconnect?: () => void;
17
- /** Called on error */
18
10
  onError?: (error: Error) => void;
19
- /** Whether to disconnect on click when connected */
20
11
  disconnectOnClick?: boolean;
21
- /** Custom render function for full control */
22
12
  children?: (props: {
23
13
  isConnected: boolean;
24
14
  isConnecting: boolean;
25
- address: string | null;
15
+ identityKey: string | null;
26
16
  connect: () => Promise<void>;
27
- disconnect: () => Promise<void>;
17
+ disconnect: () => void;
28
18
  }) => ReactNode;
29
19
  }
30
- /**
31
- * Ready-to-use connect button component
32
- *
33
- * @example
34
- * ```tsx
35
- * // Basic usage
36
- * <ConnectButton />
37
- *
38
- * // With custom labels
39
- * <ConnectButton
40
- * connectLabel="Connect 1Sat"
41
- * connectingLabel="Connecting..."
42
- * connectedLabel={(addr) => `Connected: ${addr}`}
43
- * />
44
- *
45
- * // With custom render
46
- * <ConnectButton>
47
- * {({ isConnected, connect, disconnect }) => (
48
- * <button onClick={isConnected ? disconnect : connect}>
49
- * {isConnected ? 'Disconnect' : 'Connect'}
50
- * </button>
51
- * )}
52
- * </ConnectButton>
53
- * ```
54
- */
55
20
  export declare function ConnectButton({ className, style, connectLabel, connectingLabel, connectedLabel, onConnect, onDisconnect, onError, disconnectOnClick, children, }: ConnectButtonProps): import("react/jsx-runtime").JSX.Element;
56
21
  //# sourceMappingURL=ConnectButton.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ConnectButton.d.ts","sourceRoot":"","sources":["../src/ConnectButton.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGtC,MAAM,WAAW,kBAAkB;IAClC,uCAAuC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,mCAAmC;IACnC,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAA;IAC3B,qCAAqC;IACrC,YAAY,CAAC,EAAE,SAAS,CAAA;IACxB,mCAAmC;IACnC,eAAe,CAAC,EAAE,SAAS,CAAA;IAC3B,qDAAqD;IACrD,cAAc,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK,SAAS,CAAC,CAAA;IAC7D,yCAAyC;IACzC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB,iCAAiC;IACjC,YAAY,CAAC,EAAE,MAAM,IAAI,CAAA;IACzB,sBAAsB;IACtB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;IAChC,oDAAoD;IACpD,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE;QAClB,WAAW,EAAE,OAAO,CAAA;QACpB,YAAY,EAAE,OAAO,CAAA;QACrB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;QACtB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;QAC5B,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;KAC/B,KAAK,SAAS,CAAA;CACf;AAUD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,aAAa,CAAC,EAC7B,SAAS,EACT,KAAK,EACL,YAA+B,EAC/B,eAAiC,EACjC,cAAc,EACd,SAAS,EACT,YAAY,EACZ,OAAO,EACP,iBAAwB,EACxB,QAAQ,GACR,EAAE,kBAAkB,2CA0EpB"}
1
+ {"version":3,"file":"ConnectButton.d.ts","sourceRoot":"","sources":["../src/ConnectButton.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGtC,MAAM,WAAW,kBAAkB;IAClC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAA;IAC3B,YAAY,CAAC,EAAE,SAAS,CAAA;IACxB,eAAe,CAAC,EAAE,SAAS,CAAA;IAC3B,cAAc,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,WAAW,EAAE,MAAM,KAAK,SAAS,CAAC,CAAA;IACjE,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB,YAAY,CAAC,EAAE,MAAM,IAAI,CAAA;IACzB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;IAChC,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE;QAClB,WAAW,EAAE,OAAO,CAAA;QACpB,YAAY,EAAE,OAAO,CAAA;QACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;QAC1B,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;QAC5B,UAAU,EAAE,MAAM,IAAI,CAAA;KACtB,KAAK,SAAS,CAAA;CACf;AAOD,wBAAgB,aAAa,CAAC,EAC7B,SAAS,EACT,KAAK,EACL,YAA+B,EAC/B,eAAiC,EACjC,cAAc,EACd,SAAS,EACT,YAAY,EACZ,OAAO,EACP,iBAAwB,EACxB,QAAQ,GACR,EAAE,kBAAkB,2CA0EpB"}
@@ -0,0 +1,19 @@
1
+ import { type ReactNode } from 'react';
2
+ export interface WalletSelectorProviderInfo {
3
+ type: string;
4
+ name: string;
5
+ icon?: string;
6
+ detected: boolean;
7
+ isConnecting: boolean;
8
+ connect: () => Promise<void>;
9
+ }
10
+ export interface WalletSelectorRenderProps {
11
+ providers: WalletSelectorProviderInfo[];
12
+ error: Error | null;
13
+ }
14
+ export interface WalletSelectorProps {
15
+ onClose?: () => void;
16
+ children: (props: WalletSelectorRenderProps) => ReactNode;
17
+ }
18
+ export declare function WalletSelector({ onClose, children }: WalletSelectorProps): import("react/jsx-runtime").JSX.Element;
19
+ //# sourceMappingURL=WalletSelector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WalletSelector.d.ts","sourceRoot":"","sources":["../src/WalletSelector.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAyB,MAAM,OAAO,CAAA;AAG7D,MAAM,WAAW,0BAA0B;IAC1C,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,OAAO,CAAA;IACjB,YAAY,EAAE,OAAO,CAAA;IACrB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC5B;AAED,MAAM,WAAW,yBAAyB;IACzC,SAAS,EAAE,0BAA0B,EAAE,CAAA;IACvC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;CACnB;AAED,MAAM,WAAW,mBAAmB;IACnC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,QAAQ,EAAE,CAAC,KAAK,EAAE,yBAAyB,KAAK,SAAS,CAAA;CACzD;AAED,wBAAgB,cAAc,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,mBAAmB,2CA6BxE"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export * from '@1sat/connect';
2
- export { OneSatProvider, useOneSatContext, type OneSatContextValue, type OneSatProviderProps, } from './context';
3
- export { useBalance, useOrdinals, useTokens, useUtxos, useSignTransaction, useSignMessage, useInscribe, useSendOrdinals, useTransferToken, useCreateListing, usePurchaseListing, useCancelListing, } from './hooks';
2
+ export { WalletProvider, useWallet, type WalletContextValue, type WalletProviderProps, type WalletStatus, } from './wallet-context';
4
3
  export { ConnectButton, type ConnectButtonProps } from './ConnectButton';
4
+ export { WalletSelector, type WalletSelectorProps, type WalletSelectorProviderInfo, type WalletSelectorRenderProps, } from './WalletSelector';
5
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,eAAe,CAAA;AAG7B,OAAO,EACN,cAAc,EACd,gBAAgB,EAChB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,GACxB,MAAM,WAAW,CAAA;AAGlB,OAAO,EACN,UAAU,EACV,WAAW,EACX,SAAS,EACT,QAAQ,EACR,kBAAkB,EAClB,cAAc,EACd,WAAW,EACX,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,GAChB,MAAM,SAAS,CAAA;AAGhB,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,iBAAiB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,eAAe,CAAA;AAG7B,OAAO,EACN,cAAc,EACd,SAAS,EACT,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,YAAY,GACjB,MAAM,kBAAkB,CAAA;AAGzB,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACxE,OAAO,EACN,cAAc,EACd,KAAK,mBAAmB,EACxB,KAAK,0BAA0B,EAC/B,KAAK,yBAAyB,GAC9B,MAAM,kBAAkB,CAAA"}
package/dist/index.js CHANGED
@@ -1,10 +1,12 @@
1
1
  "use client";
2
+
2
3
  // src/index.ts
3
4
  export * from "@1sat/connect";
4
5
 
5
- // src/context.tsx
6
+ // src/wallet-context.tsx
6
7
  import {
7
- createOneSat
8
+ connectWallet,
9
+ getAvailableProviders
8
10
  } from "@1sat/connect";
9
11
  import {
10
12
  createContext,
@@ -12,326 +14,167 @@ import {
12
14
  useContext,
13
15
  useEffect,
14
16
  useMemo,
17
+ useRef,
15
18
  useState
16
19
  } from "react";
17
20
  import { jsx } from "react/jsx-runtime";
18
- "use client";
19
- var OneSatContext = createContext(null);
20
- function OneSatProvider({
21
- appName,
22
- popupUrl,
23
- timeout,
24
- network,
21
+ var STORAGE_KEY = "onesat_wallet_provider";
22
+ function loadStored() {
23
+ if (typeof window === "undefined")
24
+ return null;
25
+ try {
26
+ const raw = localStorage.getItem(STORAGE_KEY);
27
+ if (!raw)
28
+ return null;
29
+ return JSON.parse(raw);
30
+ } catch {
31
+ return null;
32
+ }
33
+ }
34
+ function saveStored(providerType, identityKey) {
35
+ if (typeof window === "undefined")
36
+ return;
37
+ localStorage.setItem(STORAGE_KEY, JSON.stringify({ providerType, identityKey }));
38
+ }
39
+ function clearStored() {
40
+ if (typeof window === "undefined")
41
+ return;
42
+ localStorage.removeItem(STORAGE_KEY);
43
+ }
44
+ var WalletContext = createContext(null);
45
+ function WalletProvider({
46
+ autoDetect = true,
47
+ providers,
25
48
  children
26
49
  }) {
27
- const [provider, setProvider] = useState(null);
28
- const [isConnected, setIsConnected] = useState(false);
29
- const [isConnecting, setIsConnecting] = useState(false);
30
- const [paymentAddress, setPaymentAddress] = useState(null);
31
- const [ordinalAddress, setOrdinalAddress] = useState(null);
32
- const [identityPubKey, setIdentityPubKey] = useState(null);
50
+ const [wallet, setWallet] = useState(null);
51
+ const [status, setStatus] = useState("disconnected");
52
+ const [identityKey, setIdentityKey] = useState(null);
53
+ const [providerType, setProviderType] = useState(null);
33
54
  const [error, setError] = useState(null);
34
- useEffect(() => {
35
- if (typeof window === "undefined")
36
- return;
37
- const p = createOneSat({ appName, popupUrl, timeout, network });
38
- setProvider(p);
39
- if (p.isConnected()) {
40
- const addresses = p.getAddresses();
41
- if (addresses) {
42
- setIsConnected(true);
43
- setPaymentAddress(addresses.paymentAddress);
44
- setOrdinalAddress(addresses.ordinalAddress);
45
- setIdentityPubKey(p.getIdentityPubKey());
55
+ const disconnectRef = useRef(null);
56
+ const availableProviders = useMemo(() => getAvailableProviders({ providers }), [providers]);
57
+ const applyResult = useCallback((result) => {
58
+ setWallet(result.wallet);
59
+ setIdentityKey(result.identityKey);
60
+ setProviderType(result.provider);
61
+ setStatus("connected");
62
+ setError(null);
63
+ disconnectRef.current = result.disconnect;
64
+ saveStored(result.provider, result.identityKey);
65
+ }, []);
66
+ const disconnect = useCallback(() => {
67
+ disconnectRef.current?.();
68
+ disconnectRef.current = null;
69
+ setWallet(null);
70
+ setIdentityKey(null);
71
+ setProviderType(null);
72
+ setStatus("disconnected");
73
+ setError(null);
74
+ clearStored();
75
+ }, []);
76
+ const connect = useCallback(async (selectedType) => {
77
+ setError(null);
78
+ if (selectedType) {
79
+ const provider = availableProviders.find((p) => p.type === selectedType);
80
+ if (!provider) {
81
+ setError(new Error(`Unknown provider: ${selectedType}`));
82
+ return;
83
+ }
84
+ setStatus("connecting");
85
+ try {
86
+ applyResult(await provider.connect());
87
+ } catch (e) {
88
+ setError(e instanceof Error ? e : new Error(String(e)));
89
+ setStatus("selecting");
46
90
  }
91
+ return;
47
92
  }
48
- const handleConnect = (result) => {
49
- setIsConnected(true);
50
- setPaymentAddress(result.paymentAddress);
51
- setOrdinalAddress(result.ordinalAddress);
52
- setIdentityPubKey(result.identityPubKey);
53
- };
54
- const handleDisconnect = () => {
55
- setIsConnected(false);
56
- setPaymentAddress(null);
57
- setOrdinalAddress(null);
58
- setIdentityPubKey(null);
59
- };
60
- p.on("connect", handleConnect);
61
- p.on("disconnect", handleDisconnect);
62
- return () => {
63
- p.off("connect", handleConnect);
64
- p.off("disconnect", handleDisconnect);
65
- };
66
- }, [appName, popupUrl, timeout, network]);
67
- const connect = useCallback(async () => {
68
- if (!provider) {
69
- throw new Error("Provider not initialized");
93
+ setStatus("detecting");
94
+ const result = await connectWallet({
95
+ autoDetect: true,
96
+ providers
97
+ });
98
+ if (result) {
99
+ applyResult(result);
100
+ } else {
101
+ setStatus("selecting");
70
102
  }
71
- setIsConnecting(true);
72
- setError(null);
73
- try {
74
- const result = await provider.connect();
75
- return result;
76
- } catch (e) {
77
- const err = e instanceof Error ? e : new Error(String(e));
78
- setError(err);
79
- throw err;
80
- } finally {
81
- setIsConnecting(false);
103
+ }, [availableProviders, providers, applyResult]);
104
+ const mountRef = useRef({
105
+ autoDetect,
106
+ availableProviders,
107
+ connect,
108
+ applyResult
109
+ });
110
+ mountRef.current = { autoDetect, availableProviders, connect, applyResult };
111
+ useEffect(() => {
112
+ const {
113
+ autoDetect: auto,
114
+ availableProviders: providers2,
115
+ connect: doConnect,
116
+ applyResult: apply
117
+ } = mountRef.current;
118
+ const stored = loadStored();
119
+ if (stored) {
120
+ setStatus("connecting");
121
+ const provider = providers2.find((p) => p.type === stored.providerType);
122
+ if (provider) {
123
+ provider.connect().then(apply).catch(() => {
124
+ clearStored();
125
+ if (auto) {
126
+ doConnect();
127
+ } else {
128
+ setStatus("selecting");
129
+ }
130
+ });
131
+ return;
132
+ }
82
133
  }
83
- }, [provider]);
84
- const disconnect = useCallback(async () => {
85
- if (!provider)
86
- return;
87
- await provider.disconnect();
88
- }, [provider]);
134
+ if (auto) {
135
+ doConnect();
136
+ } else {
137
+ setStatus("selecting");
138
+ }
139
+ }, []);
89
140
  const value = useMemo(() => ({
90
- provider,
91
- isConnected,
92
- isConnecting,
93
- paymentAddress,
94
- ordinalAddress,
95
- identityPubKey,
141
+ wallet,
142
+ status,
143
+ identityKey,
144
+ providerType,
145
+ availableProviders,
96
146
  connect,
97
147
  disconnect,
98
148
  error
99
149
  }), [
100
- provider,
101
- isConnected,
102
- isConnecting,
103
- paymentAddress,
104
- ordinalAddress,
105
- identityPubKey,
150
+ wallet,
151
+ status,
152
+ identityKey,
153
+ providerType,
154
+ availableProviders,
106
155
  connect,
107
156
  disconnect,
108
157
  error
109
158
  ]);
110
- return /* @__PURE__ */ jsx(OneSatContext.Provider, {
159
+ return /* @__PURE__ */ jsx(WalletContext.Provider, {
111
160
  value,
112
161
  children
113
162
  });
114
163
  }
115
- function useOneSatContext() {
116
- const context = useContext(OneSatContext);
117
- if (!context) {
118
- throw new Error("useOneSatContext must be used within an OneSatProvider");
164
+ function useWallet() {
165
+ const ctx = useContext(WalletContext);
166
+ if (!ctx) {
167
+ throw new Error("useWallet must be used within a WalletProvider");
119
168
  }
120
- return context;
121
- }
122
- // src/hooks.ts
123
- import { useCallback as useCallback2, useEffect as useEffect2, useRef, useState as useState2 } from "react";
124
- "use client";
125
- function useProviderAction(actionFn) {
126
- const { provider, isConnected } = useOneSatContext();
127
- const [isLoading, setIsLoading] = useState2(false);
128
- const [error, setError] = useState2(null);
129
- const actionRef = useRef(actionFn);
130
- actionRef.current = actionFn;
131
- const mutate = useCallback2(async (...args) => {
132
- if (!provider || !isConnected)
133
- throw new Error("Wallet not connected");
134
- setIsLoading(true);
135
- setError(null);
136
- try {
137
- return await actionRef.current(provider, ...args);
138
- } catch (e) {
139
- const err = e instanceof Error ? e : new Error(String(e));
140
- setError(err);
141
- throw err;
142
- } finally {
143
- setIsLoading(false);
144
- }
145
- }, [provider, isConnected]);
146
- return { mutate, isLoading, error };
147
- }
148
- function useBalance() {
149
- const { provider, isConnected } = useOneSatContext();
150
- const [balance, setBalance] = useState2(null);
151
- const [isLoading, setIsLoading] = useState2(false);
152
- const [error, setError] = useState2(null);
153
- const refetch = useCallback2(async () => {
154
- if (!provider || !isConnected)
155
- return;
156
- setIsLoading(true);
157
- setError(null);
158
- try {
159
- setBalance(await provider.getBalance());
160
- } catch (e) {
161
- setError(e instanceof Error ? e : new Error(String(e)));
162
- } finally {
163
- setIsLoading(false);
164
- }
165
- }, [provider, isConnected]);
166
- useEffect2(() => {
167
- if (isConnected) {
168
- refetch();
169
- } else {
170
- setBalance(null);
171
- }
172
- }, [isConnected, refetch]);
173
- return {
174
- satoshis: balance?.satoshis ?? 0,
175
- usd: balance?.usd ?? 0,
176
- isLoading,
177
- error,
178
- refetch
179
- };
180
- }
181
- function useOrdinals(limit, offset) {
182
- const { provider, isConnected } = useOneSatContext();
183
- const [ordinals, setOrdinals] = useState2([]);
184
- const [isLoading, setIsLoading] = useState2(false);
185
- const [error, setError] = useState2(null);
186
- const refetch = useCallback2(async () => {
187
- if (!provider || !isConnected)
188
- return;
189
- setIsLoading(true);
190
- setError(null);
191
- try {
192
- const opts = limit !== undefined || offset !== undefined ? { limit, offset } : undefined;
193
- setOrdinals(await provider.getOrdinals(opts));
194
- } catch (e) {
195
- setError(e instanceof Error ? e : new Error(String(e)));
196
- } finally {
197
- setIsLoading(false);
198
- }
199
- }, [provider, isConnected, limit, offset]);
200
- useEffect2(() => {
201
- if (isConnected) {
202
- refetch();
203
- } else {
204
- setOrdinals([]);
205
- }
206
- }, [isConnected, refetch]);
207
- return { ordinals, isLoading, error, refetch };
208
- }
209
- function useTokens(limit, offset) {
210
- const { provider, isConnected } = useOneSatContext();
211
- const [tokens, setTokens] = useState2([]);
212
- const [isLoading, setIsLoading] = useState2(false);
213
- const [error, setError] = useState2(null);
214
- const refetch = useCallback2(async () => {
215
- if (!provider || !isConnected)
216
- return;
217
- setIsLoading(true);
218
- setError(null);
219
- try {
220
- const opts = limit !== undefined || offset !== undefined ? { limit, offset } : undefined;
221
- setTokens(await provider.getTokens(opts));
222
- } catch (e) {
223
- setError(e instanceof Error ? e : new Error(String(e)));
224
- } finally {
225
- setIsLoading(false);
226
- }
227
- }, [provider, isConnected, limit, offset]);
228
- useEffect2(() => {
229
- if (isConnected) {
230
- refetch();
231
- } else {
232
- setTokens([]);
233
- }
234
- }, [isConnected, refetch]);
235
- return { tokens, isLoading, error, refetch };
236
- }
237
- function useUtxos() {
238
- const { provider, isConnected } = useOneSatContext();
239
- const [utxos, setUtxos] = useState2([]);
240
- const [isLoading, setIsLoading] = useState2(false);
241
- const [error, setError] = useState2(null);
242
- const refetch = useCallback2(async () => {
243
- if (!provider || !isConnected)
244
- return;
245
- setIsLoading(true);
246
- setError(null);
247
- try {
248
- setUtxos(await provider.getUtxos());
249
- } catch (e) {
250
- setError(e instanceof Error ? e : new Error(String(e)));
251
- } finally {
252
- setIsLoading(false);
253
- }
254
- }, [provider, isConnected]);
255
- useEffect2(() => {
256
- if (isConnected) {
257
- refetch();
258
- } else {
259
- setUtxos([]);
260
- }
261
- }, [isConnected, refetch]);
262
- return { utxos, isLoading, error, refetch };
263
- }
264
- function useSignTransaction() {
265
- const {
266
- mutate: signTransaction,
267
- isLoading,
268
- error
269
- } = useProviderAction((p, request) => p.signTransaction(request));
270
- return { signTransaction, isLoading, error };
271
- }
272
- function useSignMessage() {
273
- const {
274
- mutate: signMessage,
275
- isLoading,
276
- error
277
- } = useProviderAction((p, message) => p.signMessage(message));
278
- return { signMessage, isLoading, error };
279
- }
280
- function useInscribe() {
281
- const {
282
- mutate: inscribe,
283
- isLoading,
284
- error
285
- } = useProviderAction((p, request) => p.inscribe(request));
286
- return { inscribe, isLoading, error };
287
- }
288
- function useSendOrdinals() {
289
- const {
290
- mutate: sendOrdinals,
291
- isLoading,
292
- error
293
- } = useProviderAction((p, request) => p.sendOrdinals(request));
294
- return { sendOrdinals, isLoading, error };
295
- }
296
- function useTransferToken() {
297
- const {
298
- mutate: transferToken,
299
- isLoading,
300
- error
301
- } = useProviderAction((p, request) => p.transferToken(request));
302
- return { transferToken, isLoading, error };
303
- }
304
- function useCreateListing() {
305
- const {
306
- mutate: createListing,
307
- isLoading,
308
- error
309
- } = useProviderAction((p, request) => p.createListing(request));
310
- return { createListing, isLoading, error };
311
- }
312
- function usePurchaseListing() {
313
- const {
314
- mutate: purchaseListing,
315
- isLoading,
316
- error
317
- } = useProviderAction((p, request) => p.purchaseListing(request));
318
- return { purchaseListing, isLoading, error };
319
- }
320
- function useCancelListing() {
321
- const {
322
- mutate: cancelListing,
323
- isLoading,
324
- error
325
- } = useProviderAction((p, request) => p.cancelListing(request));
326
- return { cancelListing, isLoading, error };
169
+ return ctx;
327
170
  }
328
171
  // src/ConnectButton.tsx
329
172
  import { jsx as jsx2, Fragment } from "react/jsx-runtime";
330
173
  "use client";
331
- function truncateAddress(address) {
332
- if (address.length <= 12)
333
- return address;
334
- return `${address.slice(0, 6)}...${address.slice(-4)}`;
174
+ function truncateIdentityKey(key) {
175
+ if (key.length <= 12)
176
+ return key;
177
+ return `${key.slice(0, 6)}...${key.slice(-4)}`;
335
178
  }
336
179
  function ConnectButton({
337
180
  className,
@@ -346,23 +189,24 @@ function ConnectButton({
346
189
  children
347
190
  }) {
348
191
  const {
349
- isConnected,
350
- isConnecting,
351
- paymentAddress,
352
- connect: providerConnect,
353
- disconnect: providerDisconnect
354
- } = useOneSatContext();
192
+ status,
193
+ identityKey,
194
+ connect: walletConnect,
195
+ disconnect: walletDisconnect
196
+ } = useWallet();
197
+ const isConnected = status === "connected";
198
+ const isConnecting = status === "detecting" || status === "connecting";
355
199
  const connect = async () => {
356
200
  try {
357
- await providerConnect();
201
+ await walletConnect();
358
202
  onConnect?.();
359
203
  } catch (e) {
360
204
  onError?.(e instanceof Error ? e : new Error(String(e)));
361
205
  }
362
206
  };
363
- const disconnect = async () => {
207
+ const disconnect = () => {
364
208
  try {
365
- await providerDisconnect();
209
+ walletDisconnect();
366
210
  onDisconnect?.();
367
211
  } catch (e) {
368
212
  onError?.(e instanceof Error ? e : new Error(String(e)));
@@ -373,7 +217,7 @@ function ConnectButton({
373
217
  children: children({
374
218
  isConnected,
375
219
  isConnecting,
376
- address: paymentAddress,
220
+ identityKey,
377
221
  connect,
378
222
  disconnect
379
223
  })
@@ -389,7 +233,7 @@ function ConnectButton({
389
233
  });
390
234
  }
391
235
  if (isConnected) {
392
- const label = typeof connectedLabel === "function" ? connectedLabel(paymentAddress ?? "") : connectedLabel ?? truncateAddress(paymentAddress ?? "");
236
+ const label = typeof connectedLabel === "function" ? connectedLabel(identityKey ?? "") : connectedLabel ?? truncateIdentityKey(identityKey ?? "");
393
237
  return /* @__PURE__ */ jsx2("button", {
394
238
  className,
395
239
  style,
@@ -406,20 +250,37 @@ function ConnectButton({
406
250
  children: connectLabel
407
251
  });
408
252
  }
253
+ // src/WalletSelector.tsx
254
+ import { useCallback as useCallback2, useState as useState2 } from "react";
255
+ import { jsx as jsx3, Fragment as Fragment2 } from "react/jsx-runtime";
256
+ "use client";
257
+ function WalletSelector({ onClose, children }) {
258
+ const { availableProviders, connect, error } = useWallet();
259
+ const [connectingType, setConnectingType] = useState2(null);
260
+ const handleConnect = useCallback2(async (providerType) => {
261
+ setConnectingType(providerType);
262
+ try {
263
+ await connect(providerType);
264
+ onClose?.();
265
+ } finally {
266
+ setConnectingType(null);
267
+ }
268
+ }, [connect, onClose]);
269
+ const providers = availableProviders.map((p) => ({
270
+ type: p.type,
271
+ name: p.name,
272
+ icon: p.icon,
273
+ detected: p.detected,
274
+ isConnecting: connectingType === p.type,
275
+ connect: () => handleConnect(p.type)
276
+ }));
277
+ return /* @__PURE__ */ jsx3(Fragment2, {
278
+ children: children({ providers, error })
279
+ });
280
+ }
409
281
  export {
410
- useUtxos,
411
- useTransferToken,
412
- useTokens,
413
- useSignTransaction,
414
- useSignMessage,
415
- useSendOrdinals,
416
- usePurchaseListing,
417
- useOrdinals,
418
- useOneSatContext,
419
- useInscribe,
420
- useCreateListing,
421
- useCancelListing,
422
- useBalance,
423
- OneSatProvider,
282
+ useWallet,
283
+ WalletSelector,
284
+ WalletProvider,
424
285
  ConnectButton
425
286
  };
@@ -0,0 +1,22 @@
1
+ import { type AvailableProvider, type WalletProviderConfig } from '@1sat/connect';
2
+ import type { WalletInterface } from '@bsv/sdk';
3
+ import { type ReactNode } from 'react';
4
+ export type WalletStatus = 'disconnected' | 'detecting' | 'selecting' | 'connecting' | 'connected';
5
+ export interface WalletContextValue {
6
+ wallet: WalletInterface | null;
7
+ status: WalletStatus;
8
+ identityKey: string | null;
9
+ providerType: string | null;
10
+ availableProviders: AvailableProvider[];
11
+ connect: (providerType?: string) => Promise<void>;
12
+ disconnect: () => void;
13
+ error: Error | null;
14
+ }
15
+ export interface WalletProviderProps {
16
+ autoDetect?: boolean;
17
+ providers?: WalletProviderConfig[];
18
+ children: ReactNode;
19
+ }
20
+ export declare function WalletProvider({ autoDetect, providers, children, }: WalletProviderProps): import("react/jsx-runtime").JSX.Element;
21
+ export declare function useWallet(): WalletContextValue;
22
+ //# sourceMappingURL=wallet-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wallet-context.d.ts","sourceRoot":"","sources":["../src/wallet-context.tsx"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,iBAAiB,EAEtB,KAAK,oBAAoB,EAGzB,MAAM,eAAe,CAAA;AACtB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAC/C,OAAO,EACN,KAAK,SAAS,EAQd,MAAM,OAAO,CAAA;AAEd,MAAM,MAAM,YAAY,GACrB,cAAc,GACd,WAAW,GACX,WAAW,GACX,YAAY,GACZ,WAAW,CAAA;AAEd,MAAM,WAAW,kBAAkB;IAClC,MAAM,EAAE,eAAe,GAAG,IAAI,CAAA;IAC9B,MAAM,EAAE,YAAY,CAAA;IACpB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,kBAAkB,EAAE,iBAAiB,EAAE,CAAA;IACvC,OAAO,EAAE,CAAC,YAAY,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACjD,UAAU,EAAE,MAAM,IAAI,CAAA;IACtB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;CACnB;AAED,MAAM,WAAW,mBAAmB;IACnC,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,SAAS,CAAC,EAAE,oBAAoB,EAAE,CAAA;IAClC,QAAQ,EAAE,SAAS,CAAA;CACnB;AAmCD,wBAAgB,cAAc,CAAC,EAC9B,UAAiB,EACjB,SAAS,EACT,QAAQ,GACR,EAAE,mBAAmB,2CA6IrB;AAED,wBAAgB,SAAS,IAAI,kBAAkB,CAM9C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@1sat/react",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "description": "React hooks and components for 1Sat wallet integration",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -19,12 +19,14 @@
19
19
  "keywords": ["1sat", "bsv", "ordinals", "wallet", "sdk", "react", "hooks"],
20
20
  "license": "MIT",
21
21
  "dependencies": {
22
- "@1sat/connect": "^0.0.9"
22
+ "@1sat/connect": "0.0.12"
23
23
  },
24
24
  "peerDependencies": {
25
+ "@bsv/sdk": "^2.0.0",
25
26
  "react": "^18.0.0 || ^19.0.0"
26
27
  },
27
28
  "devDependencies": {
29
+ "@bsv/sdk": "^2.0.12",
28
30
  "@types/react": "^19.0.0",
29
31
  "react": "^19.0.0",
30
32
  "typescript": "^5.9.3"
package/dist/context.d.ts DELETED
@@ -1,57 +0,0 @@
1
- import { type ConnectResult, type OneSatProvider as OneSatProviderInterface } from '@1sat/connect';
2
- import { type ReactNode } from 'react';
3
- export interface OneSatContextValue {
4
- /** The underlying provider instance */
5
- provider: OneSatProviderInterface | null;
6
- /** Whether the wallet is connected */
7
- isConnected: boolean;
8
- /** Whether a connection is in progress */
9
- isConnecting: boolean;
10
- /** The payment address (if connected) */
11
- paymentAddress: string | null;
12
- /** The ordinal address (if connected) */
13
- ordinalAddress: string | null;
14
- /** The identity public key (if connected) */
15
- identityPubKey: string | null;
16
- /** Connect to the wallet */
17
- connect: () => Promise<ConnectResult>;
18
- /** Disconnect from the wallet */
19
- disconnect: () => Promise<void>;
20
- /** Any connection error */
21
- error: Error | null;
22
- }
23
- export interface OneSatProviderProps {
24
- /** Name of the dApp (shown in approval popup) */
25
- appName?: string;
26
- /** Base URL for the wallet popup (default: https://1sat.market) */
27
- popupUrl?: string;
28
- /** Request timeout in milliseconds (default: 300000 = 5 minutes) */
29
- timeout?: number;
30
- /** Network to use (default: main) */
31
- network?: 'main' | 'test';
32
- /** Children to render */
33
- children: ReactNode;
34
- }
35
- /**
36
- * React provider for 1Sat wallet functionality
37
- *
38
- * @example
39
- * ```tsx
40
- * import { OneSatProvider } from '@1sat/react'
41
- *
42
- * function App() {
43
- * return (
44
- * <OneSatProvider appName="My dApp">
45
- * <MyApp />
46
- * </OneSatProvider>
47
- * )
48
- * }
49
- * ```
50
- */
51
- export declare function OneSatProvider({ appName, popupUrl, timeout, network, children, }: OneSatProviderProps): import("react/jsx-runtime").JSX.Element;
52
- /**
53
- * Hook to access the 1Sat context
54
- * @throws Error if used outside of OneSatProvider
55
- */
56
- export declare function useOneSatContext(): OneSatContextValue;
57
- //# sourceMappingURL=context.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.tsx"],"names":[],"mappings":"AAEA,OAAO,EACN,KAAK,aAAa,EAClB,KAAK,cAAc,IAAI,uBAAuB,EAE9C,MAAM,eAAe,CAAA;AACtB,OAAO,EACN,KAAK,SAAS,EAOd,MAAM,OAAO,CAAA;AAEd,MAAM,WAAW,kBAAkB;IAClC,uCAAuC;IACvC,QAAQ,EAAE,uBAAuB,GAAG,IAAI,CAAA;IACxC,sCAAsC;IACtC,WAAW,EAAE,OAAO,CAAA;IACpB,0CAA0C;IAC1C,YAAY,EAAE,OAAO,CAAA;IACrB,yCAAyC;IACzC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,yCAAyC;IACzC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,6CAA6C;IAC7C,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,4BAA4B;IAC5B,OAAO,EAAE,MAAM,OAAO,CAAC,aAAa,CAAC,CAAA;IACrC,iCAAiC;IACjC,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/B,2BAA2B;IAC3B,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;CACnB;AAID,MAAM,WAAW,mBAAmB;IACnC,iDAAiD;IACjD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,mEAAmE;IACnE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,oEAAoE;IACpE,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,qCAAqC;IACrC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACzB,yBAAyB;IACzB,QAAQ,EAAE,SAAS,CAAA;CACnB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,cAAc,CAAC,EAC9B,OAAO,EACP,QAAQ,EACR,OAAO,EACP,OAAO,EACP,QAAQ,GACR,EAAE,mBAAmB,2CAqGrB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,kBAAkB,CAMrD"}
package/dist/hooks.d.ts DELETED
@@ -1,186 +0,0 @@
1
- import type { CancelListingRequest, CreateListingRequest, InscribeRequest, OrdinalOutput, PurchaseListingRequest, SendOrdinalsRequest, SignTransactionRequest, TokenOutput, TransferTokenRequest, Utxo } from '@1sat/connect';
2
- /**
3
- * Hook to get the wallet balance
4
- *
5
- * @example
6
- * ```tsx
7
- * function Balance() {
8
- * const { satoshis, isLoading } = useBalance()
9
- * if (isLoading) return <span>Loading...</span>
10
- * return <span>{satoshis} sats</span>
11
- * }
12
- * ```
13
- */
14
- export declare function useBalance(): {
15
- satoshis: number;
16
- usd: number;
17
- isLoading: boolean;
18
- error: Error | null;
19
- refetch: () => Promise<void>;
20
- };
21
- /**
22
- * Hook to get ordinals from the wallet
23
- *
24
- * @example
25
- * ```tsx
26
- * function Gallery() {
27
- * const { ordinals, isLoading } = useOrdinals()
28
- * if (isLoading) return <span>Loading...</span>
29
- * return ordinals.map(ord => <div key={ord.outpoint}>{ord.origin}</div>)
30
- * }
31
- * ```
32
- */
33
- export declare function useOrdinals(limit?: number, offset?: number): {
34
- ordinals: OrdinalOutput[];
35
- isLoading: boolean;
36
- error: Error | null;
37
- refetch: () => Promise<void>;
38
- };
39
- /**
40
- * Hook to get tokens from the wallet
41
- *
42
- * @example
43
- * ```tsx
44
- * function Tokens() {
45
- * const { tokens, isLoading } = useTokens()
46
- * if (isLoading) return <span>Loading...</span>
47
- * return tokens.map(t => <div key={t.outpoint}>{t.symbol}: {t.amount}</div>)
48
- * }
49
- * ```
50
- */
51
- export declare function useTokens(limit?: number, offset?: number): {
52
- tokens: TokenOutput[];
53
- isLoading: boolean;
54
- error: Error | null;
55
- refetch: () => Promise<void>;
56
- };
57
- /**
58
- * Hook to get payment UTXOs from the wallet
59
- *
60
- * @example
61
- * ```tsx
62
- * function Utxos() {
63
- * const { utxos, refetch } = useUtxos()
64
- * return <span>{utxos.length} UTXOs available</span>
65
- * }
66
- * ```
67
- */
68
- export declare function useUtxos(): {
69
- utxos: Utxo[];
70
- isLoading: boolean;
71
- error: Error | null;
72
- refetch: () => Promise<void>;
73
- };
74
- /**
75
- * Hook for signing transactions
76
- *
77
- * @example
78
- * ```tsx
79
- * const { signTransaction, isLoading } = useSignTransaction()
80
- * const result = await signTransaction({ rawtx, description: 'Send payment' })
81
- * ```
82
- */
83
- export declare function useSignTransaction(): {
84
- signTransaction: (request: SignTransactionRequest) => Promise<import("@1sat/connect").SignTransactionResult>;
85
- isLoading: boolean;
86
- error: Error | null;
87
- };
88
- /**
89
- * Hook for signing messages (BSM)
90
- *
91
- * @example
92
- * ```tsx
93
- * const { signMessage, isLoading } = useSignMessage()
94
- * const result = await signMessage('Hello, World!')
95
- * ```
96
- */
97
- export declare function useSignMessage(): {
98
- signMessage: (message: string) => Promise<import("@1sat/connect").SignMessageResult>;
99
- isLoading: boolean;
100
- error: Error | null;
101
- };
102
- /**
103
- * Hook for inscribing ordinals
104
- *
105
- * @example
106
- * ```tsx
107
- * const { inscribe, isLoading } = useInscribe()
108
- * const result = await inscribe({ dataB64, contentType: 'text/plain' })
109
- * ```
110
- */
111
- export declare function useInscribe(): {
112
- inscribe: (request: InscribeRequest) => Promise<import("@1sat/connect").InscribeResult>;
113
- isLoading: boolean;
114
- error: Error | null;
115
- };
116
- /**
117
- * Hook for sending ordinals
118
- *
119
- * @example
120
- * ```tsx
121
- * const { sendOrdinals, isLoading } = useSendOrdinals()
122
- * await sendOrdinals({ outpoints: ['txid_0'], destinationAddress: 'addr' })
123
- * ```
124
- */
125
- export declare function useSendOrdinals(): {
126
- sendOrdinals: (request: SendOrdinalsRequest) => Promise<import("@1sat/connect").SendResult>;
127
- isLoading: boolean;
128
- error: Error | null;
129
- };
130
- /**
131
- * Hook for transferring tokens (BSV20/21)
132
- *
133
- * @example
134
- * ```tsx
135
- * const { transferToken, isLoading } = useTransferToken()
136
- * await transferToken({ tokenId: 'origin_0', amount: '100', destinationAddress: 'addr' })
137
- * ```
138
- */
139
- export declare function useTransferToken(): {
140
- transferToken: (request: TransferTokenRequest) => Promise<import("@1sat/connect").SendResult>;
141
- isLoading: boolean;
142
- error: Error | null;
143
- };
144
- /**
145
- * Hook for creating marketplace listings
146
- *
147
- * @example
148
- * ```tsx
149
- * const { createListing, isLoading } = useCreateListing()
150
- * await createListing({ outpoints: ['txid_0'], priceSatoshis: 100000 })
151
- * ```
152
- */
153
- export declare function useCreateListing(): {
154
- createListing: (request: CreateListingRequest) => Promise<import("@1sat/connect").ListingResult>;
155
- isLoading: boolean;
156
- error: Error | null;
157
- };
158
- /**
159
- * Hook for purchasing a listed ordinal
160
- *
161
- * @example
162
- * ```tsx
163
- * const { purchaseListing, isLoading } = usePurchaseListing()
164
- * await purchaseListing({ listingOutpoint: 'txid_0' })
165
- * ```
166
- */
167
- export declare function usePurchaseListing(): {
168
- purchaseListing: (request: PurchaseListingRequest) => Promise<import("@1sat/connect").SendResult>;
169
- isLoading: boolean;
170
- error: Error | null;
171
- };
172
- /**
173
- * Hook for cancelling marketplace listings
174
- *
175
- * @example
176
- * ```tsx
177
- * const { cancelListing, isLoading } = useCancelListing()
178
- * await cancelListing({ listingOutpoints: ['txid_0'] })
179
- * ```
180
- */
181
- export declare function useCancelListing(): {
182
- cancelListing: (request: CancelListingRequest) => Promise<import("@1sat/connect").SendResult>;
183
- isLoading: boolean;
184
- error: Error | null;
185
- };
186
- //# sourceMappingURL=hooks.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAEX,oBAAoB,EACpB,oBAAoB,EACpB,eAAe,EAEf,aAAa,EACb,sBAAsB,EACtB,mBAAmB,EACnB,sBAAsB,EACtB,WAAW,EACX,oBAAoB,EACpB,IAAI,EACJ,MAAM,eAAe,CAAA;AA6CtB;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU;;;;;;EAkCzB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;;;;;EAgC1D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;;;;;EAgCxD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,QAAQ;;;;;EA4BvB;AAID;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB;;;;EASjC;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc;;;;EAO7B;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW;;;;EAO1B;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe;;;;EAS9B;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB;;;;EAS/B;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB;;;;EAS/B;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB;;;;EASjC;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB;;;;EAS/B"}