@fibo-crypto/react-sdk 0.3.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/LICENSE +21 -0
- package/README.md +312 -0
- package/dist/chunk-FRD4NVYJ.js +109 -0
- package/dist/chunk-FRD4NVYJ.js.map +1 -0
- package/dist/client.d.ts +284 -0
- package/dist/client.js +3 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +189 -0
- package/dist/index.js +407 -0
- package/dist/index.js.map +1 -0
- package/package.json +75 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
import { createFiboClient, DEFAULT_BASE_URL } from './chunk-FRD4NVYJ.js';
|
|
2
|
+
export { DEFAULT_BASE_URL, FiboApiError, createFiboClient } from './chunk-FRD4NVYJ.js';
|
|
3
|
+
import { PrivyProvider, usePrivy, useUser, useWallets, useSign7702Authorization } from '@privy-io/react-auth';
|
|
4
|
+
import { createContext, useMemo, useState, useRef, useCallback, useEffect, useContext } from 'react';
|
|
5
|
+
import { jsx } from 'react/jsx-runtime';
|
|
6
|
+
import { useCreateWallet } from '@privy-io/react-auth/extended-chains';
|
|
7
|
+
|
|
8
|
+
var DEFAULT_PRIVY_APP_ID = "cme8byq1e031tjg0b1dbmv3vx";
|
|
9
|
+
var FiboContext = createContext(null);
|
|
10
|
+
function FiboProvider({
|
|
11
|
+
publishableKey,
|
|
12
|
+
baseUrl = DEFAULT_BASE_URL,
|
|
13
|
+
externalUserId,
|
|
14
|
+
loginMethods = ["email", "google", "apple"],
|
|
15
|
+
theme,
|
|
16
|
+
privyAppIdOverride,
|
|
17
|
+
children
|
|
18
|
+
}) {
|
|
19
|
+
const client = useMemo(
|
|
20
|
+
() => createFiboClient({ baseUrl, apiKey: publishableKey }),
|
|
21
|
+
[baseUrl, publishableKey]
|
|
22
|
+
);
|
|
23
|
+
const ctxValue = useMemo(
|
|
24
|
+
() => ({ client, publishableKey, externalUserId }),
|
|
25
|
+
[client, publishableKey, externalUserId]
|
|
26
|
+
);
|
|
27
|
+
return /* @__PURE__ */ jsx(
|
|
28
|
+
PrivyProvider,
|
|
29
|
+
{
|
|
30
|
+
appId: privyAppIdOverride ?? DEFAULT_PRIVY_APP_ID,
|
|
31
|
+
config: {
|
|
32
|
+
appearance: {
|
|
33
|
+
theme: "light",
|
|
34
|
+
accentColor: theme?.accent ?? "#5B44F9"
|
|
35
|
+
},
|
|
36
|
+
embeddedWallets: {
|
|
37
|
+
ethereum: { createOnLogin: "users-without-wallets" },
|
|
38
|
+
solana: { createOnLogin: "users-without-wallets" }
|
|
39
|
+
},
|
|
40
|
+
loginMethods
|
|
41
|
+
},
|
|
42
|
+
children: /* @__PURE__ */ jsx(FiboContext.Provider, { value: ctxValue, children })
|
|
43
|
+
}
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
function useFiboContext() {
|
|
47
|
+
const ctx = useContext(FiboContext);
|
|
48
|
+
if (!ctx) {
|
|
49
|
+
throw new Error(
|
|
50
|
+
"useFibo* hooks must be called from inside <FiboProvider>. Wrap your app once at the top level."
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
return ctx;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// src/hooks/useFiboApi.ts
|
|
57
|
+
function useFiboApi() {
|
|
58
|
+
return useFiboContext().client;
|
|
59
|
+
}
|
|
60
|
+
function useFiboAuth() {
|
|
61
|
+
const { ready, authenticated, user, login, logout } = usePrivy();
|
|
62
|
+
return {
|
|
63
|
+
isReady: ready,
|
|
64
|
+
isAuthenticated: authenticated,
|
|
65
|
+
user: user ? {
|
|
66
|
+
id: user.id,
|
|
67
|
+
email: user.email?.address,
|
|
68
|
+
createdAt: user.createdAt ? new Date(user.createdAt).toISOString() : void 0
|
|
69
|
+
} : null,
|
|
70
|
+
login,
|
|
71
|
+
logout
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function useFiboBalance(address, opts = {}) {
|
|
75
|
+
const api = useFiboApi();
|
|
76
|
+
const [balance, setBalance] = useState(null);
|
|
77
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
78
|
+
const [error, setError] = useState(null);
|
|
79
|
+
const currentCtrlRef = useRef(null);
|
|
80
|
+
const fetchOnce = useCallback(
|
|
81
|
+
async (signal) => {
|
|
82
|
+
if (!address) {
|
|
83
|
+
setBalance(null);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
setIsLoading(true);
|
|
87
|
+
setError(null);
|
|
88
|
+
try {
|
|
89
|
+
const result = await api.getEvmBalance(address, { signal });
|
|
90
|
+
if (signal.aborted) return;
|
|
91
|
+
setBalance(result);
|
|
92
|
+
} catch (err) {
|
|
93
|
+
if (signal.aborted) return;
|
|
94
|
+
if (err instanceof Error && err.name === "AbortError") return;
|
|
95
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
96
|
+
} finally {
|
|
97
|
+
if (!signal.aborted) setIsLoading(false);
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
[api, address]
|
|
101
|
+
);
|
|
102
|
+
const refetch = useCallback(async () => {
|
|
103
|
+
currentCtrlRef.current?.abort();
|
|
104
|
+
const ctrl = new AbortController();
|
|
105
|
+
currentCtrlRef.current = ctrl;
|
|
106
|
+
await fetchOnce(ctrl.signal);
|
|
107
|
+
}, [fetchOnce]);
|
|
108
|
+
useEffect(() => {
|
|
109
|
+
const ctrl = new AbortController();
|
|
110
|
+
currentCtrlRef.current = ctrl;
|
|
111
|
+
void fetchOnce(ctrl.signal);
|
|
112
|
+
return () => ctrl.abort();
|
|
113
|
+
}, [fetchOnce]);
|
|
114
|
+
useEffect(() => {
|
|
115
|
+
if (!opts.refreshIntervalMs || !address) return;
|
|
116
|
+
const id = setInterval(() => void refetch(), opts.refreshIntervalMs);
|
|
117
|
+
return () => clearInterval(id);
|
|
118
|
+
}, [refetch, opts.refreshIntervalMs, address]);
|
|
119
|
+
return { balance, isLoading, error, refetch };
|
|
120
|
+
}
|
|
121
|
+
function useFiboTransactions(opts = {}) {
|
|
122
|
+
const api = useFiboApi();
|
|
123
|
+
const [transactions, setTransactions] = useState([]);
|
|
124
|
+
const [hasMore, setHasMore] = useState(false);
|
|
125
|
+
const [nextBefore, setNextBefore] = useState(void 0);
|
|
126
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
127
|
+
const [error, setError] = useState(null);
|
|
128
|
+
const { status, limit, before, refreshIntervalMs } = opts;
|
|
129
|
+
const currentCtrlRef = useRef(null);
|
|
130
|
+
const fetchOnce = useCallback(
|
|
131
|
+
async (signal) => {
|
|
132
|
+
setIsLoading(true);
|
|
133
|
+
setError(null);
|
|
134
|
+
try {
|
|
135
|
+
const params = {};
|
|
136
|
+
if (status !== void 0) params.status = status;
|
|
137
|
+
if (limit !== void 0) params.limit = limit;
|
|
138
|
+
if (before !== void 0) params.before = before;
|
|
139
|
+
const result = await api.listTransactions(params, { signal });
|
|
140
|
+
if (signal.aborted) return;
|
|
141
|
+
setTransactions(result.data);
|
|
142
|
+
setHasMore(result.has_more);
|
|
143
|
+
setNextBefore(result.next_before);
|
|
144
|
+
} catch (err) {
|
|
145
|
+
if (signal.aborted) return;
|
|
146
|
+
if (err instanceof Error && err.name === "AbortError") return;
|
|
147
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
148
|
+
} finally {
|
|
149
|
+
if (!signal.aborted) setIsLoading(false);
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
[api, status, limit, before]
|
|
153
|
+
);
|
|
154
|
+
const refetch = useCallback(async () => {
|
|
155
|
+
currentCtrlRef.current?.abort();
|
|
156
|
+
const ctrl = new AbortController();
|
|
157
|
+
currentCtrlRef.current = ctrl;
|
|
158
|
+
await fetchOnce(ctrl.signal);
|
|
159
|
+
}, [fetchOnce]);
|
|
160
|
+
useEffect(() => {
|
|
161
|
+
const ctrl = new AbortController();
|
|
162
|
+
currentCtrlRef.current = ctrl;
|
|
163
|
+
void fetchOnce(ctrl.signal);
|
|
164
|
+
return () => ctrl.abort();
|
|
165
|
+
}, [fetchOnce]);
|
|
166
|
+
useEffect(() => {
|
|
167
|
+
if (!refreshIntervalMs) return;
|
|
168
|
+
const id = setInterval(() => void refetch(), refreshIntervalMs);
|
|
169
|
+
return () => clearInterval(id);
|
|
170
|
+
}, [refetch, refreshIntervalMs]);
|
|
171
|
+
return { transactions, hasMore, nextBefore, isLoading, error, refetch };
|
|
172
|
+
}
|
|
173
|
+
var isBitcoinChain = (chainType) => chainType === "bitcoin-taproot" || chainType === "bitcoin-segwit";
|
|
174
|
+
function findEmbeddedWallet(user, chainType) {
|
|
175
|
+
if (!user) return null;
|
|
176
|
+
const w = user.linkedAccounts.filter((a) => a.type === "wallet").find((w2) => w2.walletClientType === "privy" && w2.chainType === chainType);
|
|
177
|
+
return w?.address ?? null;
|
|
178
|
+
}
|
|
179
|
+
function extractBitcoinWallets(user) {
|
|
180
|
+
if (!user) return [];
|
|
181
|
+
return user.linkedAccounts.filter((a) => a.type === "wallet").filter((w) => w.walletClientType === "privy" && isBitcoinChain(w.chainType)).map((w) => {
|
|
182
|
+
const wm = w;
|
|
183
|
+
return {
|
|
184
|
+
address: wm.address,
|
|
185
|
+
chainType: wm.chainType,
|
|
186
|
+
publicKey: wm.publicKey ?? void 0,
|
|
187
|
+
walletIndex: wm.walletIndex ?? void 0
|
|
188
|
+
};
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
function useFiboWallet() {
|
|
192
|
+
const { ready } = usePrivy();
|
|
193
|
+
const { user } = useUser();
|
|
194
|
+
const { createWallet } = useCreateWallet();
|
|
195
|
+
const evm = findEmbeddedWallet(user, "ethereum");
|
|
196
|
+
const solana = findEmbeddedWallet(user, "solana");
|
|
197
|
+
const bitcoinWallets = extractBitcoinWallets(user);
|
|
198
|
+
const firstTaproot = bitcoinWallets.find((w) => w.chainType === "bitcoin-taproot")?.address ?? null;
|
|
199
|
+
const createBitcoinWallet = useCallback(async () => {
|
|
200
|
+
const result = await createWallet({ chainType: "bitcoin-taproot" });
|
|
201
|
+
const wallet = result.wallet;
|
|
202
|
+
return {
|
|
203
|
+
address: wallet.address,
|
|
204
|
+
chainType: "bitcoin-taproot",
|
|
205
|
+
publicKey: wallet.public_key ?? void 0
|
|
206
|
+
};
|
|
207
|
+
}, [createWallet]);
|
|
208
|
+
return {
|
|
209
|
+
isReady: ready,
|
|
210
|
+
addresses: {
|
|
211
|
+
evm,
|
|
212
|
+
solana,
|
|
213
|
+
bitcoin: firstTaproot
|
|
214
|
+
},
|
|
215
|
+
bitcoinWallets,
|
|
216
|
+
createBitcoinWallet
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
var TICK_MS = 1e3;
|
|
220
|
+
function useFiboSwap() {
|
|
221
|
+
const api = useFiboApi();
|
|
222
|
+
const { wallets } = useWallets();
|
|
223
|
+
const { user } = useUser();
|
|
224
|
+
const { signAuthorization } = useSign7702Authorization();
|
|
225
|
+
const [currentQuote, setCurrentQuote] = useState(null);
|
|
226
|
+
const [result, setResult] = useState(null);
|
|
227
|
+
const [error, setError] = useState(null);
|
|
228
|
+
const [phase, setPhase] = useState("idle");
|
|
229
|
+
const [now, setNow] = useState(Date.now());
|
|
230
|
+
const currentQuoteRef = useRef(currentQuote);
|
|
231
|
+
useEffect(() => {
|
|
232
|
+
currentQuoteRef.current = currentQuote;
|
|
233
|
+
}, [currentQuote]);
|
|
234
|
+
useEffect(() => {
|
|
235
|
+
if (!currentQuote) return;
|
|
236
|
+
const id = setInterval(() => setNow(Date.now()), TICK_MS);
|
|
237
|
+
return () => clearInterval(id);
|
|
238
|
+
}, [currentQuote]);
|
|
239
|
+
const expiresInSeconds = currentQuote ? Math.max(
|
|
240
|
+
0,
|
|
241
|
+
Math.floor(
|
|
242
|
+
(new Date(currentQuote.expires_at).getTime() - now) / 1e3
|
|
243
|
+
)
|
|
244
|
+
) : 0;
|
|
245
|
+
const isExpired = currentQuote !== null && expiresInSeconds === 0;
|
|
246
|
+
const resolveEvmAddress = useCallback(
|
|
247
|
+
(override) => {
|
|
248
|
+
if (override) return override;
|
|
249
|
+
const w = wallets.find((w2) => w2.walletClientType === "privy");
|
|
250
|
+
if (!w?.address) {
|
|
251
|
+
throw new Error(
|
|
252
|
+
"No embedded EVM wallet found. Wait for `useFiboWallet().isReady` before calling quote()."
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
return w.address;
|
|
256
|
+
},
|
|
257
|
+
[wallets]
|
|
258
|
+
);
|
|
259
|
+
const quote = useCallback(
|
|
260
|
+
async (params) => {
|
|
261
|
+
setError(null);
|
|
262
|
+
setResult(null);
|
|
263
|
+
setPhase("quoting");
|
|
264
|
+
try {
|
|
265
|
+
const userAddress = resolveEvmAddress(params.userAddress);
|
|
266
|
+
const q = await api.quoteSwap({
|
|
267
|
+
source: {
|
|
268
|
+
token: params.source.token,
|
|
269
|
+
chain_id: params.source.chainId,
|
|
270
|
+
amount: params.source.amount
|
|
271
|
+
},
|
|
272
|
+
destination: {
|
|
273
|
+
token: params.destination.token,
|
|
274
|
+
chain_id: params.destination.chainId
|
|
275
|
+
},
|
|
276
|
+
user_address: userAddress
|
|
277
|
+
});
|
|
278
|
+
setCurrentQuote(q);
|
|
279
|
+
currentQuoteRef.current = q;
|
|
280
|
+
setNow(Date.now());
|
|
281
|
+
setPhase("idle");
|
|
282
|
+
return q;
|
|
283
|
+
} catch (err) {
|
|
284
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
285
|
+
setError(e);
|
|
286
|
+
setPhase("error");
|
|
287
|
+
throw e;
|
|
288
|
+
}
|
|
289
|
+
},
|
|
290
|
+
[api, resolveEvmAddress]
|
|
291
|
+
);
|
|
292
|
+
const executeSwap = useCallback(async () => {
|
|
293
|
+
setError(null);
|
|
294
|
+
setResult(null);
|
|
295
|
+
try {
|
|
296
|
+
const q = currentQuoteRef.current;
|
|
297
|
+
if (!q) {
|
|
298
|
+
throw new Error(
|
|
299
|
+
"No active quote. Call quote() first to get a swap quote before executing."
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
if (new Date(q.expires_at).getTime() <= Date.now()) {
|
|
303
|
+
throw new Error(
|
|
304
|
+
"Quote has expired. Call quote() again to fetch a fresh one."
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
const evmWallet = wallets.find((w) => w.walletClientType === "privy");
|
|
308
|
+
if (!evmWallet) {
|
|
309
|
+
throw new Error(
|
|
310
|
+
"No embedded EVM wallet found at execute time. Wait for `useFiboWallet().isReady`."
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
setPhase("preparing");
|
|
314
|
+
const prepared = await api.prepareTx({
|
|
315
|
+
quote_id: q.quote_id,
|
|
316
|
+
user_address: evmWallet.address
|
|
317
|
+
});
|
|
318
|
+
const signatures = [];
|
|
319
|
+
for (const step of prepared.steps) {
|
|
320
|
+
if (step.kind === "sign_eip7702") {
|
|
321
|
+
setPhase("signing_authorization");
|
|
322
|
+
const signed = await signAuthorization({
|
|
323
|
+
contractAddress: step.payload.contractAddress,
|
|
324
|
+
chainId: step.chain_id
|
|
325
|
+
});
|
|
326
|
+
const vRaw = "v" in signed ? signed.v : void 0;
|
|
327
|
+
const sig = {
|
|
328
|
+
r: signed.r,
|
|
329
|
+
s: signed.s,
|
|
330
|
+
yParity: signed.yParity,
|
|
331
|
+
v: vRaw !== void 0 ? vRaw.toString() : void 0,
|
|
332
|
+
address: signed.address,
|
|
333
|
+
chainId: signed.chainId,
|
|
334
|
+
nonce: signed.nonce
|
|
335
|
+
};
|
|
336
|
+
signatures.push({ step_index: step.step_index, signature: sig });
|
|
337
|
+
} else if (step.kind === "sign_evm_tx") {
|
|
338
|
+
throw new Error(
|
|
339
|
+
`sign_evm_tx step not yet supported by useFiboSwap (v0.2c). Submit an issue if you hit this.`
|
|
340
|
+
);
|
|
341
|
+
} else {
|
|
342
|
+
throw new Error(
|
|
343
|
+
`Unsupported step kind: ${step.kind}`
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
if (signatures.length === 0) {
|
|
348
|
+
throw new Error(
|
|
349
|
+
"prepare-tx returned no signing steps \u2014 backend invariant violation"
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
setPhase("submitting");
|
|
353
|
+
const submitResult = await api.submit({
|
|
354
|
+
quote_id: q.quote_id,
|
|
355
|
+
transaction_id: prepared.transaction_id,
|
|
356
|
+
signatures
|
|
357
|
+
});
|
|
358
|
+
if (!submitResult.next_step) {
|
|
359
|
+
throw new Error(
|
|
360
|
+
"Backend did not return a next_step from /v1/submit. Expected a sign_message step."
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
setPhase("signing_mee_quote");
|
|
364
|
+
const provider = await evmWallet.getEthereumProvider();
|
|
365
|
+
const rawSig = await provider.request({
|
|
366
|
+
method: "personal_sign",
|
|
367
|
+
params: [submitResult.next_step.payload.hash, evmWallet.address]
|
|
368
|
+
});
|
|
369
|
+
const quoteSignature = rawSig.startsWith("0x") ? rawSig : `0x${rawSig}`;
|
|
370
|
+
setPhase("executing");
|
|
371
|
+
const exec = await api.executeSignedQuote({
|
|
372
|
+
transaction_id: submitResult.transaction_id,
|
|
373
|
+
quote_signature: quoteSignature
|
|
374
|
+
});
|
|
375
|
+
setResult(exec);
|
|
376
|
+
setPhase("done");
|
|
377
|
+
return exec;
|
|
378
|
+
} catch (err) {
|
|
379
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
380
|
+
setError(e);
|
|
381
|
+
setPhase("error");
|
|
382
|
+
throw e;
|
|
383
|
+
}
|
|
384
|
+
}, [api, wallets, signAuthorization]);
|
|
385
|
+
const reset = useCallback(() => {
|
|
386
|
+
setCurrentQuote(null);
|
|
387
|
+
setResult(null);
|
|
388
|
+
setError(null);
|
|
389
|
+
setPhase("idle");
|
|
390
|
+
}, []);
|
|
391
|
+
return {
|
|
392
|
+
currentQuote,
|
|
393
|
+
result,
|
|
394
|
+
error,
|
|
395
|
+
phase,
|
|
396
|
+
isExpired,
|
|
397
|
+
expiresInSeconds,
|
|
398
|
+
isLoading: phase !== "idle" && phase !== "done" && phase !== "error",
|
|
399
|
+
quote,
|
|
400
|
+
executeSwap,
|
|
401
|
+
reset
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
export { FiboProvider, useFiboApi, useFiboAuth, useFiboBalance, useFiboSwap, useFiboTransactions, useFiboWallet };
|
|
406
|
+
//# sourceMappingURL=index.js.map
|
|
407
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/FiboProvider.tsx","../src/hooks/useFiboApi.ts","../src/hooks/useFiboAuth.ts","../src/hooks/useFiboBalance.ts","../src/hooks/useFiboTransactions.ts","../src/hooks/useFiboWallet.ts","../src/hooks/useFiboSwap.ts"],"names":["useState","useRef","useCallback","useEffect","w","usePrivy","useCreateExtendedWallet","useUser"],"mappings":"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","file":"index.js","sourcesContent":["// Top-level provider β wraps PrivyProvider with the shared Fibo Privy app\n// (per ADR 0002) and exposes an authenticated FiboClient via React context.\n//\n// Integrators wrap their app once with <FiboProvider publishableKey=\"pk_...\" />\n// and don't touch Privy directly β that's a transitive dep.\n\nimport { PrivyProvider } from '@privy-io/react-auth';\nimport { createContext, useContext, useMemo, type ReactNode } from 'react';\nimport { createFiboClient, DEFAULT_BASE_URL, type FiboClient } from './client.js';\n\n/** Fibo's shared Privy app id (per ADR 0002 β single app for all products). */\nconst DEFAULT_PRIVY_APP_ID = 'cme8byq1e031tjg0b1dbmv3vx';\n\ninterface FiboContextValue {\n client: FiboClient;\n publishableKey: string;\n externalUserId?: string;\n}\n\nconst FiboContext = createContext<FiboContextValue | null>(null);\n\nexport interface FiboTheme {\n /** Accent color β must be a hex string starting with `#` (e.g. `#5B44F9`). */\n accent?: `#${string}`;\n}\n\nexport type FiboLoginMethod =\n | 'email'\n | 'google'\n | 'apple'\n | 'github'\n | 'discord'\n | 'twitter'\n | 'sms'\n | 'passkey';\n\nexport interface FiboProviderProps {\n /** Publishable Fibo API key β `pk_test_*` or `pk_live_*`. */\n publishableKey: string;\n /** API base URL. Defaults to https://api.fibo-crypto.fr. */\n baseUrl?: string;\n /**\n * Your own user identifier for this user. Echoed back on `wallet.created` /\n * `tx.confirmed` webhooks so your backend can join Fibo's records with yours.\n */\n externalUserId?: string;\n /**\n * Privy login methods shown in the UI. Defaults to email + Google + Apple.\n */\n loginMethods?: FiboLoginMethod[];\n /** UI theming applied to Privy modals. */\n theme?: FiboTheme;\n /**\n * Override Fibo's shared Privy app id. Rarely needed β default covers all\n * integrators per ADR 0002. Use only if your tenant has been provisioned\n * with a dedicated Privy app (custom contractual setup).\n */\n privyAppIdOverride?: string;\n children: ReactNode;\n}\n\nexport function FiboProvider({\n publishableKey,\n baseUrl = DEFAULT_BASE_URL,\n externalUserId,\n loginMethods = ['email', 'google', 'apple'],\n theme,\n privyAppIdOverride,\n children,\n}: FiboProviderProps) {\n const client = useMemo(\n () => createFiboClient({ baseUrl, apiKey: publishableKey }),\n [baseUrl, publishableKey],\n );\n\n const ctxValue = useMemo<FiboContextValue>(\n () => ({ client, publishableKey, externalUserId }),\n [client, publishableKey, externalUserId],\n );\n\n return (\n <PrivyProvider\n appId={privyAppIdOverride ?? DEFAULT_PRIVY_APP_ID}\n config={{\n appearance: {\n theme: 'light',\n accentColor: theme?.accent ?? '#5B44F9',\n },\n embeddedWallets: {\n ethereum: { createOnLogin: 'users-without-wallets' },\n solana: { createOnLogin: 'users-without-wallets' },\n },\n loginMethods,\n }}\n >\n <FiboContext.Provider value={ctxValue}>{children}</FiboContext.Provider>\n </PrivyProvider>\n );\n}\n\n/**\n * Internal hook β surface the FiboContext to other hooks. NOT exported from\n * the package's public index (`src/index.ts`); consumed only by the\n * `useFiboApi` / `useFiboAuth` siblings via relative import. Keeping it\n * private lets us refactor `FiboContextValue` without a semver-major bump.\n */\nexport function useFiboContext(): FiboContextValue {\n const ctx = useContext(FiboContext);\n if (!ctx) {\n throw new Error(\n 'useFibo* hooks must be called from inside <FiboProvider>. Wrap your app once at the top level.',\n );\n }\n return ctx;\n}\n","// useFiboApi β returns the FiboClient configured by the surrounding\n// FiboProvider. Use for any direct REST call not covered by a higher-level\n// hook.\n//\n// const api = useFiboApi();\n// const balance = await api.getEvmBalance(address);\n\nimport type { FiboClient } from '../client.js';\nimport { useFiboContext } from '../FiboProvider.js';\n\nexport function useFiboApi(): FiboClient {\n return useFiboContext().client;\n}\n","// useFiboAuth β auth state + login/logout, surfaced from Privy.\n//\n// Keeps the Privy SDK invisible to integrator code: they import only from\n// @fibo-crypto/react-sdk and never touch @privy-io/react-auth directly (per ADR 0003).\n\nimport { usePrivy } from '@privy-io/react-auth';\n\nexport interface FiboUser {\n /** Privy user DID. Use as a stable identifier across sessions / devices. */\n id: string;\n email?: string;\n /** ISO string when the user was first created on Privy. */\n createdAt?: string;\n}\n\nexport interface FiboAuthState {\n /** True once Privy has finished bootstrapping (cookies / local storage read). */\n isReady: boolean;\n /** True once the user has completed login. */\n isAuthenticated: boolean;\n /** The current user, or null when not authenticated / not ready yet. */\n user: FiboUser | null;\n /** Open the Privy login modal. */\n login: () => void;\n /** Log the user out β clears cookies and disconnects embedded wallets. */\n logout: () => Promise<void>;\n}\n\nexport function useFiboAuth(): FiboAuthState {\n const { ready, authenticated, user, login, logout } = usePrivy();\n\n return {\n isReady: ready,\n isAuthenticated: authenticated,\n user: user\n ? {\n id: user.id,\n email: user.email?.address,\n createdAt: user.createdAt\n ? new Date(user.createdAt).toISOString()\n : undefined,\n }\n : null,\n login,\n logout,\n };\n}\n","// useFiboBalance β fetch + optionally poll a wallet's EVM balance.\n//\n// Light-weight implementation: useState + useEffect + AbortController.\n// No React Query peer-dep to keep the SDK install small. Consumers who\n// want full caching can call `useFiboApi().getEvmBalance(addr)` directly\n// and wrap it in their own data layer.\n\n'use client';\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { useFiboApi } from './useFiboApi.js';\nimport type { EvmBalanceResponse } from '../types.js';\n\nexport interface FiboBalanceState {\n /** Latest balance, or null while loading / on error / no address. */\n balance: EvmBalanceResponse | null;\n isLoading: boolean;\n error: Error | null;\n /** Manually re-fetch (e.g., after a swap completed). */\n refetch: () => Promise<void>;\n}\n\nexport interface UseFiboBalanceOptions {\n /**\n * Auto-refresh every N milliseconds. Defaults to no polling.\n * Set to ~10000 for a \"live\" balance display.\n */\n refreshIntervalMs?: number;\n}\n\n/**\n * Fetches the on-chain balance for an EVM address. Returns `{ balance, isLoading, error, refetch }`.\n * Pass `null` for `address` to skip the fetch (useful while waiting for a wallet to be ready).\n *\n * In-flight fetches are cancelled when `address` changes or the component unmounts β\n * stale results never overwrite the current state.\n */\nexport function useFiboBalance(\n address: string | null,\n opts: UseFiboBalanceOptions = {},\n): FiboBalanceState {\n const api = useFiboApi();\n const [balance, setBalance] = useState<EvmBalanceResponse | null>(null);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n // Live ref to the latest AbortController so manual refetch can also cancel\n // a previous in-flight call.\n const currentCtrlRef = useRef<AbortController | null>(null);\n\n const fetchOnce = useCallback(\n async (signal: AbortSignal) => {\n if (!address) {\n setBalance(null);\n return;\n }\n setIsLoading(true);\n setError(null);\n try {\n const result = await api.getEvmBalance(address, { signal });\n if (signal.aborted) return;\n setBalance(result);\n } catch (err) {\n if (signal.aborted) return;\n // node-fetch / undici abort errors land here as DOMException\n // 'AbortError'; suppress them since the caller asked for the cancel.\n if (err instanceof Error && err.name === 'AbortError') return;\n setError(err instanceof Error ? err : new Error(String(err)));\n } finally {\n if (!signal.aborted) setIsLoading(false);\n }\n },\n [api, address],\n );\n\n const refetch = useCallback(async () => {\n currentCtrlRef.current?.abort();\n const ctrl = new AbortController();\n currentCtrlRef.current = ctrl;\n await fetchOnce(ctrl.signal);\n }, [fetchOnce]);\n\n useEffect(() => {\n const ctrl = new AbortController();\n currentCtrlRef.current = ctrl;\n void fetchOnce(ctrl.signal);\n return () => ctrl.abort();\n }, [fetchOnce]);\n\n useEffect(() => {\n if (!opts.refreshIntervalMs || !address) return;\n const id = setInterval(() => void refetch(), opts.refreshIntervalMs);\n return () => clearInterval(id);\n }, [refetch, opts.refreshIntervalMs, address]);\n\n return { balance, isLoading, error, refetch };\n}\n","// useFiboTransactions β paginated list of the tenant's transactions.\n//\n// Wraps GET /v1/transactions. Supports filtering by status + cursor\n// pagination via `before` (created_at). Re-fetches on params change.\n//\n// Stability: opts fields (`status`, `limit`, `before`, `refreshIntervalMs`)\n// are individually tracked as useCallback deps β NOT the opts object\n// reference. Passing a new object literal each render is safe as long as\n// the primitive values are stable. If you compute opts dynamically (e.g.,\n// from a user filter), the primitive values must be stable, not the\n// container object.\n\n'use client';\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { useFiboApi } from './useFiboApi.js';\nimport type {\n Transaction,\n TransactionListParams,\n TransactionStatus,\n} from '../types.js';\n\nexport interface FiboTransactionsState {\n transactions: Transaction[];\n /** True if more pages exist beyond the current view (use `nextBefore` cursor). */\n hasMore: boolean;\n /** Cursor to pass as `before` for the next page. */\n nextBefore: string | undefined;\n isLoading: boolean;\n error: Error | null;\n refetch: () => Promise<void>;\n}\n\nexport interface UseFiboTransactionsOptions {\n /** Filter by status. */\n status?: TransactionStatus;\n /** Max rows per fetch (1..200, default 50). */\n limit?: number;\n /** ISO 8601 cursor for pagination. Pass `state.nextBefore` to get the next page. */\n before?: string;\n /** Auto-refresh interval in ms. Defaults to no polling. */\n refreshIntervalMs?: number;\n}\n\nexport function useFiboTransactions(\n opts: UseFiboTransactionsOptions = {},\n): FiboTransactionsState {\n const api = useFiboApi();\n const [transactions, setTransactions] = useState<Transaction[]>([]);\n const [hasMore, setHasMore] = useState(false);\n const [nextBefore, setNextBefore] = useState<string | undefined>(undefined);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const { status, limit, before, refreshIntervalMs } = opts;\n\n const currentCtrlRef = useRef<AbortController | null>(null);\n\n const fetchOnce = useCallback(\n async (signal: AbortSignal) => {\n setIsLoading(true);\n setError(null);\n try {\n const params: TransactionListParams = {};\n if (status !== undefined) params.status = status;\n if (limit !== undefined) params.limit = limit;\n if (before !== undefined) params.before = before;\n const result = await api.listTransactions(params, { signal });\n if (signal.aborted) return;\n setTransactions(result.data);\n setHasMore(result.has_more);\n setNextBefore(result.next_before);\n } catch (err) {\n if (signal.aborted) return;\n if (err instanceof Error && err.name === 'AbortError') return;\n setError(err instanceof Error ? err : new Error(String(err)));\n } finally {\n if (!signal.aborted) setIsLoading(false);\n }\n },\n [api, status, limit, before],\n );\n\n const refetch = useCallback(async () => {\n currentCtrlRef.current?.abort();\n const ctrl = new AbortController();\n currentCtrlRef.current = ctrl;\n await fetchOnce(ctrl.signal);\n }, [fetchOnce]);\n\n useEffect(() => {\n const ctrl = new AbortController();\n currentCtrlRef.current = ctrl;\n void fetchOnce(ctrl.signal);\n return () => ctrl.abort();\n }, [fetchOnce]);\n\n useEffect(() => {\n if (!refreshIntervalMs) return;\n const id = setInterval(() => void refetch(), refreshIntervalMs);\n return () => clearInterval(id);\n }, [refetch, refreshIntervalMs]);\n\n return { transactions, hasMore, nextBefore, isLoading, error, refetch };\n}\n","// useFiboWallet β multi-chain wallet address readout (EVM + Solana + Bitcoin).\n//\n// Reads the user's embedded wallet addresses from Privy:\n// - EVM (Ethereum / Base / Polygon / etc.) β auto-provisioned on login by FiboProvider\n// - Solana β auto-provisioned on login by FiboProvider\n// - Bitcoin (taproot / segwit) β NOT auto-provisioned; call `createBitcoinWallet()`\n//\n// Bitcoin uses Privy's `/extended-chains` sub-export. Privy doesn't auto-create\n// BTC wallets on login (unlike EVM and Solana), so the SDK exposes an explicit\n// creation primitive for the integrator to call when they want the user to\n// receive BTC.\n\n'use client';\n\nimport { useCallback } from 'react';\nimport { usePrivy, useUser } from '@privy-io/react-auth';\nimport type { User, WalletWithMetadata } from '@privy-io/react-auth';\nimport {\n useCreateWallet as useCreateExtendedWallet,\n} from '@privy-io/react-auth/extended-chains';\n\nexport type BitcoinChain = 'bitcoin-taproot' | 'bitcoin-segwit';\n\nexport interface FiboBitcoinWallet {\n address: string;\n chainType: BitcoinChain;\n publicKey?: string;\n walletIndex?: number;\n}\n\nexport interface FiboWalletState {\n /** Privy ready flag β true once the SDK has bootstrapped. Wait on this before signing. */\n isReady: boolean;\n\n /** Convenience flattened addresses for common single-wallet flows. */\n addresses: {\n evm: string | null;\n solana: string | null;\n /** First taproot BTC address, if any. */\n bitcoin: string | null;\n };\n\n /** All Bitcoin wallets (the user may own multiple BTC addresses). */\n bitcoinWallets: FiboBitcoinWallet[];\n\n /**\n * Provision a new BTC taproot wallet for the user. Resolves with the new wallet.\n * BTC isn't auto-created on login by Privy β call this when the user wants to receive BTC.\n */\n createBitcoinWallet: () => Promise<FiboBitcoinWallet>;\n}\n\nconst isBitcoinChain = (chainType: string): chainType is BitcoinChain =>\n chainType === 'bitcoin-taproot' || chainType === 'bitcoin-segwit';\n\nfunction findEmbeddedWallet(\n user: User | null | undefined,\n chainType: 'ethereum' | 'solana',\n): string | null {\n if (!user) return null;\n const w = user.linkedAccounts\n .filter((a): a is WalletWithMetadata => a.type === 'wallet')\n .find((w) => w.walletClientType === 'privy' && w.chainType === chainType);\n return w?.address ?? null;\n}\n\nfunction extractBitcoinWallets(\n user: User | null | undefined,\n): FiboBitcoinWallet[] {\n if (!user) return [];\n return user.linkedAccounts\n .filter((a): a is WalletWithMetadata => a.type === 'wallet')\n .filter((w) => w.walletClientType === 'privy' && isBitcoinChain(w.chainType))\n .map((w) => {\n const wm = w as WalletWithMetadata & {\n publicKey?: string | null;\n walletIndex?: number | null;\n };\n return {\n address: wm.address,\n chainType: wm.chainType as BitcoinChain,\n publicKey: wm.publicKey ?? undefined,\n walletIndex: wm.walletIndex ?? undefined,\n };\n });\n}\n\nexport function useFiboWallet(): FiboWalletState {\n const { ready } = usePrivy();\n const { user } = useUser();\n const { createWallet } = useCreateExtendedWallet();\n\n const evm = findEmbeddedWallet(user, 'ethereum');\n const solana = findEmbeddedWallet(user, 'solana');\n const bitcoinWallets = extractBitcoinWallets(user);\n const firstTaproot =\n bitcoinWallets.find((w) => w.chainType === 'bitcoin-taproot')?.address ?? null;\n\n const createBitcoinWallet = useCallback(async () => {\n const result = await createWallet({ chainType: 'bitcoin-taproot' });\n const wallet = result.wallet as { address: string; public_key?: string | null };\n return {\n address: wallet.address,\n chainType: 'bitcoin-taproot' as const,\n publicKey: wallet.public_key ?? undefined,\n };\n }, [createWallet]);\n\n return {\n isReady: ready,\n addresses: {\n evm,\n solana,\n bitcoin: firstTaproot,\n },\n bitcoinWallets,\n createBitcoinWallet,\n };\n}\n","// useFiboSwap β EVM same-chain & cross-chain swap ceremony.\n//\n// Wraps the full quote β prepare β sign EIP-7702 β submit β sign MEE quote\n// β execute flow into a single React hook. The integrator calls:\n//\n// const { quote, executeSwap, currentQuote, phase, ... } = useFiboSwap();\n// await quote({ source, destination }); // user picks an amount β quote\n// // ... (user reviews the breakdown UI) ...\n// await executeSwap(); // user clicks \"execute\"\n//\n// Privy handles the 7702 + personal_sign prompts via its UI; the integrator\n// doesn't need to render anything.\n//\n// Scope of v0.2c:\n// - EVM same-chain (USDCβETH on Base, etc.): TESTED via web/ PoC\n// - EVM cross-chain: TESTED (same prepare-tx ceremony, MEE handles routing)\n// - Solana / Bitcoin swaps: NOT in this hook yet β different signing flows.\n// Will land in v0.3 (Solana via @privy-io/react-auth/solana,\n// BTC via @privy-io/react-auth/extended-chains + @scure/btc-signer).\n\n'use client';\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { useSign7702Authorization, useUser, useWallets } from '@privy-io/react-auth';\nimport { useFiboApi } from './useFiboApi.js';\nimport type {\n Eip7702Signature,\n ExecuteResponse,\n PrepareStep,\n QuoteResponse,\n} from '../types.js';\n\nexport type FiboSwapPhase =\n | 'idle'\n | 'quoting'\n | 'preparing'\n | 'signing_authorization'\n | 'submitting'\n | 'signing_mee_quote'\n | 'executing'\n | 'done'\n | 'error';\n\nexport interface FiboSwapQuoteParams {\n source: { token: string; chainId: number; amount: string };\n destination: { token: string; chainId: number };\n /**\n * The user's EVM address. If omitted, the hook auto-resolves it from\n * Privy's first embedded EVM wallet. Pass explicitly if your app uses\n * a different wallet (multi-account scenarios).\n */\n userAddress?: string;\n}\n\nexport interface FiboSwapState {\n /** Latest quote returned by quote(). null until the first quote() call. */\n currentQuote: Extract<QuoteResponse, { kind: 'swap' }> | null;\n\n /** ExecuteResponse on success: { super_hash, tracker_url, ... }. */\n result: ExecuteResponse | null;\n\n /** Latest error from any phase. Cleared by the next quote() / executeSwap() call. */\n error: Error | null;\n\n /** High-level state machine β useful for \"Loading...\" vs \"Awaiting signature...\" UI. */\n phase: FiboSwapPhase;\n\n /** True if quote has expired (auto-tick). False if no quote yet. */\n isExpired: boolean;\n\n /** Seconds until current quote expires. 0 if no quote / expired. */\n expiresInSeconds: number;\n\n /** Convenience boolean: phase is not 'idle' / 'done' / 'error'. */\n isLoading: boolean;\n\n /**\n * Fetch a swap quote. Resolves with the QuoteResponse (also exposed via\n * state.currentQuote). Auto-resolves user_address from Privy if not passed.\n */\n quote: (\n params: FiboSwapQuoteParams,\n ) => Promise<Extract<QuoteResponse, { kind: 'swap' }>>;\n\n /**\n * Execute the current quote (the last one returned by quote()).\n * Runs the full prepare β sign β submit β sign MEE β execute ceremony.\n * Resolves with the ExecuteResponse on success.\n *\n * Throws if no quote is pending or if the quote has expired (caller\n * should call quote() again first).\n */\n executeSwap: () => Promise<ExecuteResponse>;\n\n /** Clear the current quote + result + error. Returns the hook to 'idle'. */\n reset: () => void;\n}\n\nconst TICK_MS = 1_000;\n\nexport function useFiboSwap(): FiboSwapState {\n const api = useFiboApi();\n const { wallets } = useWallets();\n const { user } = useUser();\n const { signAuthorization } = useSign7702Authorization();\n\n const [currentQuote, setCurrentQuote] = useState<Extract<\n QuoteResponse,\n { kind: 'swap' }\n > | null>(null);\n const [result, setResult] = useState<ExecuteResponse | null>(null);\n const [error, setError] = useState<Error | null>(null);\n const [phase, setPhase] = useState<FiboSwapPhase>('idle');\n const [now, setNow] = useState(Date.now());\n\n // Live ref so executeSwap can pull the latest currentQuote without\n // re-creating the callback identity on every quote() call.\n const currentQuoteRef = useRef(currentQuote);\n useEffect(() => {\n currentQuoteRef.current = currentQuote;\n }, [currentQuote]);\n\n // Tick the expiry countdown only while a quote is alive.\n useEffect(() => {\n if (!currentQuote) return;\n const id = setInterval(() => setNow(Date.now()), TICK_MS);\n return () => clearInterval(id);\n }, [currentQuote]);\n\n const expiresInSeconds = currentQuote\n ? Math.max(\n 0,\n Math.floor(\n (new Date(currentQuote.expires_at).getTime() - now) / 1000,\n ),\n )\n : 0;\n const isExpired = currentQuote !== null && expiresInSeconds === 0;\n\n const resolveEvmAddress = useCallback(\n (override?: string): string => {\n if (override) return override;\n const w = wallets.find((w) => w.walletClientType === 'privy');\n if (!w?.address) {\n throw new Error(\n 'No embedded EVM wallet found. Wait for `useFiboWallet().isReady` before calling quote().',\n );\n }\n return w.address;\n },\n [wallets],\n );\n\n const quote = useCallback(\n async (\n params: FiboSwapQuoteParams,\n ): Promise<Extract<QuoteResponse, { kind: 'swap' }>> => {\n setError(null);\n setResult(null);\n setPhase('quoting');\n try {\n const userAddress = resolveEvmAddress(params.userAddress);\n const q = await api.quoteSwap({\n source: {\n token: params.source.token,\n chain_id: params.source.chainId,\n amount: params.source.amount,\n },\n destination: {\n token: params.destination.token,\n chain_id: params.destination.chainId,\n },\n user_address: userAddress,\n });\n setCurrentQuote(q);\n // Sync the ref immediately so an executeSwap() call in the same\n // microtask sees the new quote (the useEffect-driven sync below\n // runs only after the next render commit β there's a brief window\n // where the ref is stale otherwise).\n currentQuoteRef.current = q;\n setNow(Date.now());\n setPhase('idle');\n return q;\n } catch (err) {\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n setPhase('error');\n throw e;\n }\n },\n [api, resolveEvmAddress],\n );\n\n const executeSwap = useCallback(async (): Promise<ExecuteResponse> => {\n setError(null);\n setResult(null);\n\n // Guard throws are inside the try so failures surface via state.error\n // (not just as a thrown promise rejection). Without this, the README\n // example wouldn't display guard errors at all β only ceremony errors.\n try {\n const q = currentQuoteRef.current;\n if (!q) {\n throw new Error(\n 'No active quote. Call quote() first to get a swap quote before executing.',\n );\n }\n if (new Date(q.expires_at).getTime() <= Date.now()) {\n throw new Error(\n 'Quote has expired. Call quote() again to fetch a fresh one.',\n );\n }\n const evmWallet = wallets.find((w) => w.walletClientType === 'privy');\n if (!evmWallet) {\n throw new Error(\n 'No embedded EVM wallet found at execute time. Wait for `useFiboWallet().isReady`.',\n );\n }\n\n // ---- Phase 1 β prepare-tx (backend emits signing steps) ----\n setPhase('preparing');\n const prepared = await api.prepareTx({\n quote_id: q.quote_id,\n user_address: evmWallet.address,\n });\n\n // ---- Phase 2 β sign each step ----\n const signatures: { step_index: number; signature: Eip7702Signature }[] = [];\n for (const step of prepared.steps) {\n if (step.kind === 'sign_eip7702') {\n setPhase('signing_authorization');\n // Privy fetches the on-chain nonce internally; we pass only the\n // smart-account contract address + chain. Returned shape matches\n // viem's signedAuthorization. CRITICAL: viem returns `v` as a\n // bigint, which JSON.stringify can't handle β convert to string\n // before transport. See docs/notes.md Β§ \"Privy 7702 v bigint\".\n const signed = await signAuthorization({\n contractAddress: step.payload.contractAddress,\n chainId: step.chain_id,\n });\n const vRaw = 'v' in signed ? (signed.v as bigint | undefined) : undefined;\n const sig: Eip7702Signature = {\n r: signed.r,\n s: signed.s,\n yParity: signed.yParity,\n v: vRaw !== undefined ? vRaw.toString() : undefined,\n address: signed.address as `0x${string}`,\n chainId: signed.chainId,\n nonce: signed.nonce,\n };\n signatures.push({ step_index: step.step_index, signature: sig });\n } else if (step.kind === 'sign_evm_tx') {\n // Per-tx signing via Privy's useSignTransaction. Not used for the\n // current Biconomy MEE flow (EIP-7702 authorization unlocks all\n // sub-tx execution). Future MEE features may emit this step kind.\n throw new Error(\n `sign_evm_tx step not yet supported by useFiboSwap (v0.2c). Submit an issue if you hit this.`,\n );\n } else {\n // redirect_ramping is reserved for buy_eure quotes (Monerium /\n // Transak flows). The SDK exposes those via separate hooks later.\n throw new Error(\n `Unsupported step kind: ${(step as { kind: string }).kind}`,\n );\n }\n }\n\n // Defensive guard β /v1/prepare-tx is contracted to return at least\n // one signing step. An empty steps[] indicates a backend invariant\n // violation; surface a clear error rather than letting /v1/submit\n // reject with a generic \"signatures: min 1\" validation message.\n if (signatures.length === 0) {\n throw new Error(\n 'prepare-tx returned no signing steps β backend invariant violation',\n );\n }\n\n // ---- Phase 3 β submit signatures, receive MEE quote hash to sign ----\n setPhase('submitting');\n const submitResult = await api.submit({\n quote_id: q.quote_id,\n transaction_id: prepared.transaction_id,\n signatures,\n });\n if (!submitResult.next_step) {\n throw new Error(\n 'Backend did not return a next_step from /v1/submit. Expected a sign_message step.',\n );\n }\n\n // ---- Phase 4 β sign the MEE quote hash via personal_sign ----\n setPhase('signing_mee_quote');\n // Privy's useSignMessage only accepts UTF-8 strings β it can't\n // personal_sign raw 32-byte hashes. We drop down to the embedded\n // wallet's EIP-1193 provider and call personal_sign directly with\n // the hex hash; personal_sign treats 0x-prefixed hex as raw bytes,\n // matching what MEE's signQuote expects.\n const provider = await evmWallet.getEthereumProvider();\n const rawSig = (await provider.request({\n method: 'personal_sign',\n params: [submitResult.next_step.payload.hash, evmWallet.address],\n })) as string;\n const quoteSignature = (\n rawSig.startsWith('0x') ? rawSig : `0x${rawSig}`\n ) as `0x${string}`;\n\n // ---- Phase 5 β execute the signed quote β MEE supertransaction ----\n setPhase('executing');\n const exec = await api.executeSignedQuote({\n transaction_id: submitResult.transaction_id,\n quote_signature: quoteSignature,\n });\n setResult(exec);\n setPhase('done');\n return exec;\n } catch (err) {\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n setPhase('error');\n throw e;\n }\n }, [api, wallets, signAuthorization]);\n\n const reset = useCallback(() => {\n setCurrentQuote(null);\n setResult(null);\n setError(null);\n setPhase('idle');\n }, []);\n\n // `user` is destructured for a future `needsPasskey` flag (v0.4 will\n // surface it on FiboSwapState). Unused for now; renamed via prefix to\n // satisfy linters without a fragile `void` suppression.\n const _user = user;\n void _user;\n\n return {\n currentQuote,\n result,\n error,\n phase,\n isExpired,\n expiresInSeconds,\n isLoading:\n phase !== 'idle' && phase !== 'done' && phase !== 'error',\n quote,\n executeSwap,\n reset,\n };\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fibo-crypto/react-sdk",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "React SDK for Fibo SaaS β non-custodial crypto infrastructure for B2B platforms",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Fibo <support@fibo-crypto.fr> (https://fibo-crypto.fr)",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"crypto",
|
|
9
|
+
"web3",
|
|
10
|
+
"wallet",
|
|
11
|
+
"swap",
|
|
12
|
+
"defi",
|
|
13
|
+
"privy",
|
|
14
|
+
"biconomy",
|
|
15
|
+
"lifi",
|
|
16
|
+
"eip-7702",
|
|
17
|
+
"gasless",
|
|
18
|
+
"non-custodial",
|
|
19
|
+
"react",
|
|
20
|
+
"sdk"
|
|
21
|
+
],
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://github.com/vigimani/fibo-b2b/issues"
|
|
24
|
+
},
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "git+https://github.com/vigimani/fibo-b2b.git",
|
|
28
|
+
"directory": "sdk"
|
|
29
|
+
},
|
|
30
|
+
"homepage": "https://docs.fibo-crypto.fr",
|
|
31
|
+
"type": "module",
|
|
32
|
+
"main": "./dist/index.js",
|
|
33
|
+
"module": "./dist/index.js",
|
|
34
|
+
"types": "./dist/index.d.ts",
|
|
35
|
+
"exports": {
|
|
36
|
+
".": {
|
|
37
|
+
"types": "./dist/index.d.ts",
|
|
38
|
+
"import": "./dist/index.js"
|
|
39
|
+
},
|
|
40
|
+
"./client": {
|
|
41
|
+
"types": "./dist/client.d.ts",
|
|
42
|
+
"import": "./dist/client.js"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"files": [
|
|
46
|
+
"dist",
|
|
47
|
+
"README.md",
|
|
48
|
+
"LICENSE"
|
|
49
|
+
],
|
|
50
|
+
"sideEffects": false,
|
|
51
|
+
"scripts": {
|
|
52
|
+
"build": "tsup",
|
|
53
|
+
"typecheck": "tsc --noEmit",
|
|
54
|
+
"pack:tgz": "npm run build && npm pack"
|
|
55
|
+
},
|
|
56
|
+
"dependencies": {
|
|
57
|
+
"@privy-io/react-auth": "^3.27.1"
|
|
58
|
+
},
|
|
59
|
+
"peerDependencies": {
|
|
60
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
61
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
62
|
+
},
|
|
63
|
+
"devDependencies": {
|
|
64
|
+
"@types/react": "^18.3.2",
|
|
65
|
+
"react": "^18.3.1",
|
|
66
|
+
"tsup": "^8.3.0",
|
|
67
|
+
"typescript": "^5.8.0"
|
|
68
|
+
},
|
|
69
|
+
"engines": {
|
|
70
|
+
"node": ">=18.0"
|
|
71
|
+
},
|
|
72
|
+
"publishConfig": {
|
|
73
|
+
"access": "public"
|
|
74
|
+
}
|
|
75
|
+
}
|