@usdctofiat/offramp 1.1.4 → 2.0.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/dist/react.d.cts CHANGED
@@ -1,11 +1,14 @@
1
1
  import { WalletClient } from 'viem';
2
- import { h as OfframpStep, e as OfframpError, O as OfframpParams, b as OfframpResult, D as DepositInfo } from './errors-Dtzrl98J.cjs';
2
+ import { m as OfframpState, n as OfframpStep, j as OfframpError, O as OfframpParams, b as OfframpResult, D as DepositInfo, c as OfframpCreateOptions, q as PeerExtensionRegistrationInfo, P as PlatformEntry } from './errors-Deoi_xYZ.cjs';
3
3
  import '@zkp2p/sdk';
4
4
 
5
5
  interface UseOfframpReturn {
6
+ state: OfframpState;
6
7
  step: OfframpStep | null;
7
8
  txHash: string | null;
8
9
  depositId: string | null;
10
+ lastError: OfframpError | null;
11
+ /** @deprecated Use `lastError` instead. */
9
12
  error: OfframpError | null;
10
13
  isLoading: boolean;
11
14
  offramp: (walletClient: WalletClient, params: OfframpParams) => Promise<OfframpResult>;
@@ -13,6 +16,90 @@ interface UseOfframpReturn {
13
16
  close: (walletClient: WalletClient, depositId: string, escrowAddress?: string) => Promise<string>;
14
17
  reset: () => void;
15
18
  }
16
- declare function useOfframp(): UseOfframpReturn;
19
+ declare function useOfframp(defaultOptions?: OfframpCreateOptions): UseOfframpReturn;
17
20
 
18
- export { type UseOfframpReturn, useOfframp };
21
+ /**
22
+ * Phase surfaced to callers:
23
+ * - `checking` — polling the extension state on mount
24
+ * - `needs_install` — the Peer extension is not installed; call
25
+ * `installExtension()` or surface the install URL
26
+ * - `needs_connection` — the extension is installed but this origin is not
27
+ * yet approved; call `connectExtension()` to trigger the Peer approval
28
+ * prompt
29
+ * - `ready` — extension installed and origin approved; call
30
+ * `openVerifySidebar()` to send the user into the `/verify/<providerId>`
31
+ * handler, or just retry the `offramp(...)` call
32
+ * - `unsupported` — the provided platform does not require extension
33
+ * registration; the hook is a no-op for it
34
+ */
35
+ type PeerExtensionRegistrationPhase = "checking" | "needs_install" | "needs_connection" | "ready" | "unsupported";
36
+ interface UsePeerExtensionRegistrationReturn {
37
+ /** Current handshake phase. */
38
+ phase: PeerExtensionRegistrationPhase;
39
+ /**
40
+ * Registration metadata for this platform (prompt copy, CTA label, min
41
+ * extension version). `null` for platforms that don't require extension
42
+ * registration, in which case `phase === "unsupported"`.
43
+ */
44
+ info: PeerExtensionRegistrationInfo | null;
45
+ /** True while a `requestConnection()` or initial state check is in flight. */
46
+ busy: boolean;
47
+ /** Last error from `requestConnection()` / `openSidebar()`, if any. */
48
+ error: string | null;
49
+ /** Re-poll `peerExtensionSdk.getState()`. Call after the user confirms they've finished the verify flow. */
50
+ refresh: () => Promise<void>;
51
+ /**
52
+ * Open the Chrome Web Store listing for the Peer extension. Falls back to
53
+ * `window.open(PEER_EXTENSION_CHROME_URL)` outside of browsers where
54
+ * `openInstallPage()` would throw.
55
+ */
56
+ installExtension: () => void;
57
+ /**
58
+ * Request an origin connection. Triggers the Peer extension's approval
59
+ * prompt. Returns `true` if the user approved, `false` otherwise. On
60
+ * approval the hook auto-advances to `ready` and opens the verify sidebar.
61
+ */
62
+ connectExtension: () => Promise<boolean>;
63
+ /**
64
+ * Open `/verify/<providerId>` in the Peer extension sidebar so the user
65
+ * can finish registering their handle. Safe to call from any phase — the
66
+ * hook will fall back to install/connect if those steps aren't done yet.
67
+ */
68
+ openVerifySidebar: () => Promise<void>;
69
+ }
70
+ /**
71
+ * React hook that drives the Peer extension handshake required for PayPal
72
+ * and Wise makers. Mirrors the CTA state machine used in usdctofiat.xyz.
73
+ *
74
+ * ```tsx
75
+ * import { PLATFORMS, useOfframp } from "@usdctofiat/offramp";
76
+ * import { usePeerExtensionRegistration } from "@usdctofiat/offramp/react";
77
+ *
78
+ * function PayPalSellButton() {
79
+ * const { offramp, lastError } = useOfframp();
80
+ * const peer = usePeerExtensionRegistration(PLATFORMS.PAYPAL);
81
+ *
82
+ * const needsExtension = lastError?.code === "EXTENSION_REGISTRATION_REQUIRED";
83
+ *
84
+ * return (
85
+ * <>
86
+ * <button onClick={() => offramp(walletClient, params)}>Sell USDC</button>
87
+ * {needsExtension && peer.phase === "needs_install" && (
88
+ * <button onClick={peer.installExtension}>Install Peer Extension</button>
89
+ * )}
90
+ * {needsExtension && peer.phase === "needs_connection" && (
91
+ * <button onClick={peer.connectExtension} disabled={peer.busy}>
92
+ * Connect Peer Extension
93
+ * </button>
94
+ * )}
95
+ * {needsExtension && peer.phase === "ready" && (
96
+ * <button onClick={peer.openVerifySidebar}>Verify in Peer</button>
97
+ * )}
98
+ * </>
99
+ * );
100
+ * }
101
+ * ```
102
+ */
103
+ declare function usePeerExtensionRegistration(platform: PlatformEntry | null | undefined): UsePeerExtensionRegistrationReturn;
104
+
105
+ export { type PeerExtensionRegistrationPhase, type UseOfframpReturn, type UsePeerExtensionRegistrationReturn, useOfframp, usePeerExtensionRegistration };
package/dist/react.d.ts CHANGED
@@ -1,11 +1,14 @@
1
1
  import { WalletClient } from 'viem';
2
- import { h as OfframpStep, e as OfframpError, O as OfframpParams, b as OfframpResult, D as DepositInfo } from './errors-Dtzrl98J.js';
2
+ import { m as OfframpState, n as OfframpStep, j as OfframpError, O as OfframpParams, b as OfframpResult, D as DepositInfo, c as OfframpCreateOptions, q as PeerExtensionRegistrationInfo, P as PlatformEntry } from './errors-Deoi_xYZ.js';
3
3
  import '@zkp2p/sdk';
4
4
 
5
5
  interface UseOfframpReturn {
6
+ state: OfframpState;
6
7
  step: OfframpStep | null;
7
8
  txHash: string | null;
8
9
  depositId: string | null;
10
+ lastError: OfframpError | null;
11
+ /** @deprecated Use `lastError` instead. */
9
12
  error: OfframpError | null;
10
13
  isLoading: boolean;
11
14
  offramp: (walletClient: WalletClient, params: OfframpParams) => Promise<OfframpResult>;
@@ -13,6 +16,90 @@ interface UseOfframpReturn {
13
16
  close: (walletClient: WalletClient, depositId: string, escrowAddress?: string) => Promise<string>;
14
17
  reset: () => void;
15
18
  }
16
- declare function useOfframp(): UseOfframpReturn;
19
+ declare function useOfframp(defaultOptions?: OfframpCreateOptions): UseOfframpReturn;
17
20
 
18
- export { type UseOfframpReturn, useOfframp };
21
+ /**
22
+ * Phase surfaced to callers:
23
+ * - `checking` — polling the extension state on mount
24
+ * - `needs_install` — the Peer extension is not installed; call
25
+ * `installExtension()` or surface the install URL
26
+ * - `needs_connection` — the extension is installed but this origin is not
27
+ * yet approved; call `connectExtension()` to trigger the Peer approval
28
+ * prompt
29
+ * - `ready` — extension installed and origin approved; call
30
+ * `openVerifySidebar()` to send the user into the `/verify/<providerId>`
31
+ * handler, or just retry the `offramp(...)` call
32
+ * - `unsupported` — the provided platform does not require extension
33
+ * registration; the hook is a no-op for it
34
+ */
35
+ type PeerExtensionRegistrationPhase = "checking" | "needs_install" | "needs_connection" | "ready" | "unsupported";
36
+ interface UsePeerExtensionRegistrationReturn {
37
+ /** Current handshake phase. */
38
+ phase: PeerExtensionRegistrationPhase;
39
+ /**
40
+ * Registration metadata for this platform (prompt copy, CTA label, min
41
+ * extension version). `null` for platforms that don't require extension
42
+ * registration, in which case `phase === "unsupported"`.
43
+ */
44
+ info: PeerExtensionRegistrationInfo | null;
45
+ /** True while a `requestConnection()` or initial state check is in flight. */
46
+ busy: boolean;
47
+ /** Last error from `requestConnection()` / `openSidebar()`, if any. */
48
+ error: string | null;
49
+ /** Re-poll `peerExtensionSdk.getState()`. Call after the user confirms they've finished the verify flow. */
50
+ refresh: () => Promise<void>;
51
+ /**
52
+ * Open the Chrome Web Store listing for the Peer extension. Falls back to
53
+ * `window.open(PEER_EXTENSION_CHROME_URL)` outside of browsers where
54
+ * `openInstallPage()` would throw.
55
+ */
56
+ installExtension: () => void;
57
+ /**
58
+ * Request an origin connection. Triggers the Peer extension's approval
59
+ * prompt. Returns `true` if the user approved, `false` otherwise. On
60
+ * approval the hook auto-advances to `ready` and opens the verify sidebar.
61
+ */
62
+ connectExtension: () => Promise<boolean>;
63
+ /**
64
+ * Open `/verify/<providerId>` in the Peer extension sidebar so the user
65
+ * can finish registering their handle. Safe to call from any phase — the
66
+ * hook will fall back to install/connect if those steps aren't done yet.
67
+ */
68
+ openVerifySidebar: () => Promise<void>;
69
+ }
70
+ /**
71
+ * React hook that drives the Peer extension handshake required for PayPal
72
+ * and Wise makers. Mirrors the CTA state machine used in usdctofiat.xyz.
73
+ *
74
+ * ```tsx
75
+ * import { PLATFORMS, useOfframp } from "@usdctofiat/offramp";
76
+ * import { usePeerExtensionRegistration } from "@usdctofiat/offramp/react";
77
+ *
78
+ * function PayPalSellButton() {
79
+ * const { offramp, lastError } = useOfframp();
80
+ * const peer = usePeerExtensionRegistration(PLATFORMS.PAYPAL);
81
+ *
82
+ * const needsExtension = lastError?.code === "EXTENSION_REGISTRATION_REQUIRED";
83
+ *
84
+ * return (
85
+ * <>
86
+ * <button onClick={() => offramp(walletClient, params)}>Sell USDC</button>
87
+ * {needsExtension && peer.phase === "needs_install" && (
88
+ * <button onClick={peer.installExtension}>Install Peer Extension</button>
89
+ * )}
90
+ * {needsExtension && peer.phase === "needs_connection" && (
91
+ * <button onClick={peer.connectExtension} disabled={peer.busy}>
92
+ * Connect Peer Extension
93
+ * </button>
94
+ * )}
95
+ * {needsExtension && peer.phase === "ready" && (
96
+ * <button onClick={peer.openVerifySidebar}>Verify in Peer</button>
97
+ * )}
98
+ * </>
99
+ * );
100
+ * }
101
+ * ```
102
+ */
103
+ declare function usePeerExtensionRegistration(platform: PlatformEntry | null | undefined): UsePeerExtensionRegistrationReturn;
104
+
105
+ export { type PeerExtensionRegistrationPhase, type UseOfframpReturn, type UsePeerExtensionRegistrationReturn, useOfframp, usePeerExtensionRegistration };
package/dist/react.js CHANGED
@@ -1,42 +1,61 @@
1
1
  import {
2
2
  OfframpError,
3
+ PEER_EXTENSION_CHROME_URL,
3
4
  close,
5
+ createOfframp,
4
6
  deposits,
5
- offramp
6
- } from "./chunk-OJY6DE3I.js";
7
+ getPeerExtensionRegistrationInfo,
8
+ peerExtensionSdk
9
+ } from "./chunk-HBR3Z6HX.js";
7
10
 
8
11
  // src/hooks/useOfframp.ts
9
12
  import { useCallback, useRef, useState } from "react";
10
- function useOfframp() {
13
+ function mapStepToState(step) {
14
+ if (step === "resuming" || step === "restricting") return "delegating";
15
+ if (step === "done") return "done";
16
+ return step;
17
+ }
18
+ function useOfframp(defaultOptions = {}) {
19
+ const [state, setState] = useState("idle");
11
20
  const [step, setStep] = useState(null);
12
21
  const [txHash, setTxHash] = useState(null);
13
22
  const [depositId, setDepositId] = useState(null);
14
- const [error, setError] = useState(null);
23
+ const [lastError, setLastError] = useState(null);
15
24
  const [isLoading, setIsLoading] = useState(false);
16
25
  const lockRef = useRef(false);
17
26
  const reset = useCallback(() => {
27
+ setState("idle");
18
28
  setStep(null);
19
29
  setTxHash(null);
20
30
  setDepositId(null);
21
- setError(null);
31
+ setLastError(null);
22
32
  setIsLoading(false);
23
33
  lockRef.current = false;
24
34
  }, []);
25
- const offramp2 = useCallback(
35
+ const offramp = useCallback(
26
36
  async (walletClient, params) => {
27
37
  if (lockRef.current) throw new OfframpError("Deposit already in progress", "VALIDATION");
28
38
  lockRef.current = true;
29
39
  setIsLoading(true);
30
- setError(null);
40
+ setState("idle");
41
+ setLastError(null);
31
42
  setStep(null);
32
43
  setTxHash(null);
33
44
  setDepositId(null);
34
45
  try {
35
- const result = await offramp(walletClient, params, (progress) => {
46
+ const sdk = createOfframp({
47
+ walletClient,
48
+ integratorId: params.integratorId ?? defaultOptions.integratorId,
49
+ referralId: params.referralId ?? defaultOptions.referralId,
50
+ telemetry: defaultOptions.telemetry
51
+ });
52
+ const result = await sdk.createDeposit(params, (progress) => {
36
53
  setStep(progress.step);
54
+ setState(mapStepToState(progress.step));
37
55
  if (progress.txHash) setTxHash(progress.txHash);
38
56
  if (progress.depositId) setDepositId(progress.depositId);
39
57
  });
58
+ setState("done");
40
59
  setDepositId(result.depositId);
41
60
  setTxHash(result.txHash);
42
61
  return result;
@@ -45,28 +64,147 @@ function useOfframp() {
45
64
  err instanceof Error ? err.message : "Offramp failed",
46
65
  "DEPOSIT_FAILED"
47
66
  );
48
- setError(offrampError);
49
- throw err;
67
+ setLastError(offrampError);
68
+ setState("error");
69
+ throw offrampError;
50
70
  } finally {
51
71
  setIsLoading(false);
52
72
  lockRef.current = false;
53
73
  }
54
74
  },
55
- []
75
+ [defaultOptions.integratorId, defaultOptions.referralId, defaultOptions.telemetry]
56
76
  );
57
77
  return {
78
+ state,
58
79
  step,
59
80
  txHash,
60
81
  depositId,
61
- error,
82
+ lastError,
83
+ error: lastError,
62
84
  isLoading,
63
- offramp: offramp2,
85
+ offramp,
64
86
  deposits,
65
87
  close,
66
88
  reset
67
89
  };
68
90
  }
91
+
92
+ // src/hooks/usePeerExtensionRegistration.ts
93
+ import { useCallback as useCallback2, useEffect, useRef as useRef2, useState as useState2 } from "react";
94
+ function usePeerExtensionRegistration(platform) {
95
+ const info = platform ? getPeerExtensionRegistrationInfo(platform.id) : null;
96
+ const [phase, setPhase] = useState2(
97
+ info ? "checking" : "unsupported"
98
+ );
99
+ const [busy, setBusy] = useState2(false);
100
+ const [error, setError] = useState2(null);
101
+ const mountedRef = useRef2(true);
102
+ const providerId = info?.providerId ?? null;
103
+ const refresh = useCallback2(async () => {
104
+ if (!providerId) {
105
+ setPhase("unsupported");
106
+ return;
107
+ }
108
+ try {
109
+ const state = await peerExtensionSdk.getState();
110
+ if (!mountedRef.current) return;
111
+ setPhase(mapSdkState(state));
112
+ } catch {
113
+ if (!mountedRef.current) return;
114
+ setPhase("needs_install");
115
+ }
116
+ }, [providerId]);
117
+ useEffect(() => {
118
+ mountedRef.current = true;
119
+ setError(null);
120
+ setBusy(false);
121
+ if (!providerId) {
122
+ setPhase("unsupported");
123
+ } else {
124
+ setPhase("checking");
125
+ refresh();
126
+ }
127
+ return () => {
128
+ mountedRef.current = false;
129
+ };
130
+ }, [providerId, refresh]);
131
+ const installExtension = useCallback2(() => {
132
+ setError(null);
133
+ try {
134
+ peerExtensionSdk.openInstallPage();
135
+ } catch {
136
+ if (typeof window !== "undefined") {
137
+ window.open(PEER_EXTENSION_CHROME_URL, "_blank", "noopener,noreferrer");
138
+ }
139
+ }
140
+ }, []);
141
+ const connectExtension = useCallback2(async () => {
142
+ if (!providerId) return false;
143
+ setError(null);
144
+ setBusy(true);
145
+ try {
146
+ const approved = await peerExtensionSdk.requestConnection();
147
+ if (!mountedRef.current) return approved;
148
+ if (approved) {
149
+ setPhase("ready");
150
+ peerExtensionSdk.openSidebar(`/verify/${providerId}`);
151
+ } else {
152
+ setError("Connection was not approved. Try again once you've approved Peer.");
153
+ }
154
+ return approved;
155
+ } catch (err) {
156
+ if (!mountedRef.current) return false;
157
+ setError(
158
+ err instanceof Error ? err.message : "Couldn't request a Peer extension connection. Make sure Peer is installed and enabled."
159
+ );
160
+ refresh();
161
+ return false;
162
+ } finally {
163
+ if (mountedRef.current) setBusy(false);
164
+ }
165
+ }, [providerId, refresh]);
166
+ const openVerifySidebar = useCallback2(async () => {
167
+ if (!providerId) return;
168
+ setError(null);
169
+ const state = await peerExtensionSdk.getState().catch(() => "needs_install");
170
+ if (!mountedRef.current) return;
171
+ const nextPhase = mapSdkState(state);
172
+ setPhase(nextPhase);
173
+ if (nextPhase === "needs_install") {
174
+ installExtension();
175
+ return;
176
+ }
177
+ if (nextPhase === "needs_connection") {
178
+ await connectExtension();
179
+ return;
180
+ }
181
+ try {
182
+ peerExtensionSdk.openSidebar(`/verify/${providerId}`);
183
+ } catch (err) {
184
+ setError(
185
+ err instanceof Error ? err.message : "Couldn't open the Peer extension. Make sure it's enabled."
186
+ );
187
+ refresh();
188
+ }
189
+ }, [connectExtension, installExtension, providerId, refresh]);
190
+ return {
191
+ phase,
192
+ info,
193
+ busy,
194
+ error,
195
+ refresh,
196
+ installExtension,
197
+ connectExtension,
198
+ openVerifySidebar
199
+ };
200
+ }
201
+ function mapSdkState(state) {
202
+ if (state === "needs_install") return "needs_install";
203
+ if (state === "needs_connection") return "needs_connection";
204
+ return "ready";
205
+ }
69
206
  export {
70
- useOfframp
207
+ useOfframp,
208
+ usePeerExtensionRegistration
71
209
  };
72
210
  //# sourceMappingURL=react.js.map
package/dist/react.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/hooks/useOfframp.ts"],"sourcesContent":["import { useCallback, useRef, useState } from \"react\";\nimport type { WalletClient } from \"viem\";\nimport type { OfframpParams, OfframpResult, OfframpStep, DepositInfo } from \"../types\";\nimport { offramp as offrampFn } from \"../deposit\";\nimport { deposits as depositsFn, close as closeFn } from \"../queries\";\nimport { OfframpError } from \"../errors\";\n\nexport interface UseOfframpReturn {\n step: OfframpStep | null;\n txHash: string | null;\n depositId: string | null;\n error: OfframpError | null;\n isLoading: boolean;\n offramp: (walletClient: WalletClient, params: OfframpParams) => Promise<OfframpResult>;\n deposits: (walletAddress: string) => Promise<DepositInfo[]>;\n close: (walletClient: WalletClient, depositId: string, escrowAddress?: string) => Promise<string>;\n reset: () => void;\n}\n\nexport function useOfframp(): UseOfframpReturn {\n const [step, setStep] = useState<OfframpStep | null>(null);\n const [txHash, setTxHash] = useState<string | null>(null);\n const [depositId, setDepositId] = useState<string | null>(null);\n const [error, setError] = useState<OfframpError | null>(null);\n const [isLoading, setIsLoading] = useState(false);\n const lockRef = useRef(false);\n\n const reset = useCallback(() => {\n setStep(null);\n setTxHash(null);\n setDepositId(null);\n setError(null);\n setIsLoading(false);\n lockRef.current = false;\n }, []);\n\n const offramp = useCallback(\n async (walletClient: WalletClient, params: OfframpParams): Promise<OfframpResult> => {\n if (lockRef.current) throw new OfframpError(\"Deposit already in progress\", \"VALIDATION\");\n lockRef.current = true;\n setIsLoading(true);\n setError(null);\n setStep(null);\n setTxHash(null);\n setDepositId(null);\n\n try {\n const result = await offrampFn(walletClient, params, (progress) => {\n setStep(progress.step);\n if (progress.txHash) setTxHash(progress.txHash);\n if (progress.depositId) setDepositId(progress.depositId);\n });\n setDepositId(result.depositId);\n setTxHash(result.txHash);\n return result;\n } catch (err) {\n const offrampError =\n err instanceof OfframpError\n ? err\n : new OfframpError(\n err instanceof Error ? err.message : \"Offramp failed\",\n \"DEPOSIT_FAILED\",\n );\n setError(offrampError);\n throw err;\n } finally {\n setIsLoading(false);\n lockRef.current = false;\n }\n },\n [],\n );\n\n return {\n step,\n txHash,\n depositId,\n error,\n isLoading,\n offramp,\n deposits: depositsFn,\n close: closeFn,\n reset,\n };\n}\n"],"mappings":";;;;;;;;AAAA,SAAS,aAAa,QAAQ,gBAAgB;AAmBvC,SAAS,aAA+B;AAC7C,QAAM,CAAC,MAAM,OAAO,IAAI,SAA6B,IAAI;AACzD,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAwB,IAAI;AACxD,QAAM,CAAC,WAAW,YAAY,IAAI,SAAwB,IAAI;AAC9D,QAAM,CAAC,OAAO,QAAQ,IAAI,SAA8B,IAAI;AAC5D,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,KAAK;AAChD,QAAM,UAAU,OAAO,KAAK;AAE5B,QAAM,QAAQ,YAAY,MAAM;AAC9B,YAAQ,IAAI;AACZ,cAAU,IAAI;AACd,iBAAa,IAAI;AACjB,aAAS,IAAI;AACb,iBAAa,KAAK;AAClB,YAAQ,UAAU;AAAA,EACpB,GAAG,CAAC,CAAC;AAEL,QAAMA,WAAU;AAAA,IACd,OAAO,cAA4B,WAAkD;AACnF,UAAI,QAAQ,QAAS,OAAM,IAAI,aAAa,+BAA+B,YAAY;AACvF,cAAQ,UAAU;AAClB,mBAAa,IAAI;AACjB,eAAS,IAAI;AACb,cAAQ,IAAI;AACZ,gBAAU,IAAI;AACd,mBAAa,IAAI;AAEjB,UAAI;AACF,cAAM,SAAS,MAAM,QAAU,cAAc,QAAQ,CAAC,aAAa;AACjE,kBAAQ,SAAS,IAAI;AACrB,cAAI,SAAS,OAAQ,WAAU,SAAS,MAAM;AAC9C,cAAI,SAAS,UAAW,cAAa,SAAS,SAAS;AAAA,QACzD,CAAC;AACD,qBAAa,OAAO,SAAS;AAC7B,kBAAU,OAAO,MAAM;AACvB,eAAO;AAAA,MACT,SAAS,KAAK;AACZ,cAAM,eACJ,eAAe,eACX,MACA,IAAI;AAAA,UACF,eAAe,QAAQ,IAAI,UAAU;AAAA,UACrC;AAAA,QACF;AACN,iBAAS,YAAY;AACrB,cAAM;AAAA,MACR,UAAE;AACA,qBAAa,KAAK;AAClB,gBAAQ,UAAU;AAAA,MACpB;AAAA,IACF;AAAA,IACA,CAAC;AAAA,EACH;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAAA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;","names":["offramp"]}
1
+ {"version":3,"sources":["../src/hooks/useOfframp.ts","../src/hooks/usePeerExtensionRegistration.ts"],"sourcesContent":["import { useCallback, useRef, useState } from \"react\";\nimport type { WalletClient } from \"viem\";\nimport type {\n OfframpCreateOptions,\n OfframpParams,\n OfframpResult,\n OfframpState,\n OfframpStep,\n DepositInfo,\n} from \"../types\";\nimport { createOfframp } from \"../offramp\";\nimport { deposits as depositsFn, close as closeFn } from \"../queries\";\nimport { OfframpError } from \"../errors\";\n\nexport interface UseOfframpReturn {\n state: OfframpState;\n step: OfframpStep | null;\n txHash: string | null;\n depositId: string | null;\n lastError: OfframpError | null;\n /** @deprecated Use `lastError` instead. */\n error: OfframpError | null;\n isLoading: boolean;\n offramp: (walletClient: WalletClient, params: OfframpParams) => Promise<OfframpResult>;\n deposits: (walletAddress: string) => Promise<DepositInfo[]>;\n close: (walletClient: WalletClient, depositId: string, escrowAddress?: string) => Promise<string>;\n reset: () => void;\n}\n\nfunction mapStepToState(step: OfframpStep): OfframpState {\n if (step === \"resuming\" || step === \"restricting\") return \"delegating\";\n if (step === \"done\") return \"done\";\n return step;\n}\n\nexport function useOfframp(defaultOptions: OfframpCreateOptions = {}): UseOfframpReturn {\n const [state, setState] = useState<OfframpState>(\"idle\");\n const [step, setStep] = useState<OfframpStep | null>(null);\n const [txHash, setTxHash] = useState<string | null>(null);\n const [depositId, setDepositId] = useState<string | null>(null);\n const [lastError, setLastError] = useState<OfframpError | null>(null);\n const [isLoading, setIsLoading] = useState(false);\n const lockRef = useRef(false);\n\n const reset = useCallback(() => {\n setState(\"idle\");\n setStep(null);\n setTxHash(null);\n setDepositId(null);\n setLastError(null);\n setIsLoading(false);\n lockRef.current = false;\n }, []);\n\n const offramp = useCallback(\n async (walletClient: WalletClient, params: OfframpParams): Promise<OfframpResult> => {\n if (lockRef.current) throw new OfframpError(\"Deposit already in progress\", \"VALIDATION\");\n lockRef.current = true;\n setIsLoading(true);\n setState(\"idle\");\n setLastError(null);\n setStep(null);\n setTxHash(null);\n setDepositId(null);\n\n try {\n const sdk = createOfframp({\n walletClient,\n integratorId: params.integratorId ?? defaultOptions.integratorId,\n referralId: params.referralId ?? defaultOptions.referralId,\n telemetry: defaultOptions.telemetry,\n });\n const result = await sdk.createDeposit(params, (progress) => {\n setStep(progress.step);\n setState(mapStepToState(progress.step));\n if (progress.txHash) setTxHash(progress.txHash);\n if (progress.depositId) setDepositId(progress.depositId);\n });\n setState(\"done\");\n setDepositId(result.depositId);\n setTxHash(result.txHash);\n return result;\n } catch (err) {\n const offrampError =\n err instanceof OfframpError\n ? err\n : new OfframpError(\n err instanceof Error ? err.message : \"Offramp failed\",\n \"DEPOSIT_FAILED\",\n );\n setLastError(offrampError);\n setState(\"error\");\n throw offrampError;\n } finally {\n setIsLoading(false);\n lockRef.current = false;\n }\n },\n [defaultOptions.integratorId, defaultOptions.referralId, defaultOptions.telemetry],\n );\n\n return {\n state,\n step,\n txHash,\n depositId,\n lastError,\n error: lastError,\n isLoading,\n offramp,\n deposits: depositsFn,\n close: closeFn,\n reset,\n };\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { peerExtensionSdk, type PeerExtensionState, PEER_EXTENSION_CHROME_URL } from \"../extension\";\nimport {\n getPeerExtensionRegistrationInfo,\n type PeerExtensionRegistrationInfo,\n type PlatformEntry,\n} from \"../platforms\";\n\n/**\n * Phase surfaced to callers:\n * - `checking` — polling the extension state on mount\n * - `needs_install` — the Peer extension is not installed; call\n * `installExtension()` or surface the install URL\n * - `needs_connection` — the extension is installed but this origin is not\n * yet approved; call `connectExtension()` to trigger the Peer approval\n * prompt\n * - `ready` — extension installed and origin approved; call\n * `openVerifySidebar()` to send the user into the `/verify/<providerId>`\n * handler, or just retry the `offramp(...)` call\n * - `unsupported` — the provided platform does not require extension\n * registration; the hook is a no-op for it\n */\nexport type PeerExtensionRegistrationPhase =\n | \"checking\"\n | \"needs_install\"\n | \"needs_connection\"\n | \"ready\"\n | \"unsupported\";\n\nexport interface UsePeerExtensionRegistrationReturn {\n /** Current handshake phase. */\n phase: PeerExtensionRegistrationPhase;\n /**\n * Registration metadata for this platform (prompt copy, CTA label, min\n * extension version). `null` for platforms that don't require extension\n * registration, in which case `phase === \"unsupported\"`.\n */\n info: PeerExtensionRegistrationInfo | null;\n /** True while a `requestConnection()` or initial state check is in flight. */\n busy: boolean;\n /** Last error from `requestConnection()` / `openSidebar()`, if any. */\n error: string | null;\n /** Re-poll `peerExtensionSdk.getState()`. Call after the user confirms they've finished the verify flow. */\n refresh: () => Promise<void>;\n /**\n * Open the Chrome Web Store listing for the Peer extension. Falls back to\n * `window.open(PEER_EXTENSION_CHROME_URL)` outside of browsers where\n * `openInstallPage()` would throw.\n */\n installExtension: () => void;\n /**\n * Request an origin connection. Triggers the Peer extension's approval\n * prompt. Returns `true` if the user approved, `false` otherwise. On\n * approval the hook auto-advances to `ready` and opens the verify sidebar.\n */\n connectExtension: () => Promise<boolean>;\n /**\n * Open `/verify/<providerId>` in the Peer extension sidebar so the user\n * can finish registering their handle. Safe to call from any phase — the\n * hook will fall back to install/connect if those steps aren't done yet.\n */\n openVerifySidebar: () => Promise<void>;\n}\n\n/**\n * React hook that drives the Peer extension handshake required for PayPal\n * and Wise makers. Mirrors the CTA state machine used in usdctofiat.xyz.\n *\n * ```tsx\n * import { PLATFORMS, useOfframp } from \"@usdctofiat/offramp\";\n * import { usePeerExtensionRegistration } from \"@usdctofiat/offramp/react\";\n *\n * function PayPalSellButton() {\n * const { offramp, lastError } = useOfframp();\n * const peer = usePeerExtensionRegistration(PLATFORMS.PAYPAL);\n *\n * const needsExtension = lastError?.code === \"EXTENSION_REGISTRATION_REQUIRED\";\n *\n * return (\n * <>\n * <button onClick={() => offramp(walletClient, params)}>Sell USDC</button>\n * {needsExtension && peer.phase === \"needs_install\" && (\n * <button onClick={peer.installExtension}>Install Peer Extension</button>\n * )}\n * {needsExtension && peer.phase === \"needs_connection\" && (\n * <button onClick={peer.connectExtension} disabled={peer.busy}>\n * Connect Peer Extension\n * </button>\n * )}\n * {needsExtension && peer.phase === \"ready\" && (\n * <button onClick={peer.openVerifySidebar}>Verify in Peer</button>\n * )}\n * </>\n * );\n * }\n * ```\n */\nexport function usePeerExtensionRegistration(\n platform: PlatformEntry | null | undefined,\n): UsePeerExtensionRegistrationReturn {\n const info = platform ? getPeerExtensionRegistrationInfo(platform.id) : null;\n const [phase, setPhase] = useState<PeerExtensionRegistrationPhase>(\n info ? \"checking\" : \"unsupported\",\n );\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const mountedRef = useRef(true);\n // Close over the current provider id so state refreshes triggered after\n // `platform` swaps don't race with the previous one.\n const providerId = info?.providerId ?? null;\n\n const refresh = useCallback(async () => {\n if (!providerId) {\n setPhase(\"unsupported\");\n return;\n }\n try {\n const state = await peerExtensionSdk.getState();\n if (!mountedRef.current) return;\n setPhase(mapSdkState(state));\n } catch {\n if (!mountedRef.current) return;\n setPhase(\"needs_install\");\n }\n }, [providerId]);\n\n useEffect(() => {\n mountedRef.current = true;\n setError(null);\n setBusy(false);\n if (!providerId) {\n setPhase(\"unsupported\");\n } else {\n setPhase(\"checking\");\n refresh();\n }\n return () => {\n mountedRef.current = false;\n };\n }, [providerId, refresh]);\n\n const installExtension = useCallback(() => {\n setError(null);\n try {\n peerExtensionSdk.openInstallPage();\n } catch {\n if (typeof window !== \"undefined\") {\n window.open(PEER_EXTENSION_CHROME_URL, \"_blank\", \"noopener,noreferrer\");\n }\n }\n }, []);\n\n const connectExtension = useCallback(async (): Promise<boolean> => {\n if (!providerId) return false;\n setError(null);\n setBusy(true);\n try {\n const approved = await peerExtensionSdk.requestConnection();\n if (!mountedRef.current) return approved;\n if (approved) {\n setPhase(\"ready\");\n peerExtensionSdk.openSidebar(`/verify/${providerId}`);\n } else {\n setError(\"Connection was not approved. Try again once you've approved Peer.\");\n }\n return approved;\n } catch (err) {\n if (!mountedRef.current) return false;\n setError(\n err instanceof Error\n ? err.message\n : \"Couldn't request a Peer extension connection. Make sure Peer is installed and enabled.\",\n );\n // Re-check state in case the user installed or disabled something.\n refresh();\n return false;\n } finally {\n if (mountedRef.current) setBusy(false);\n }\n }, [providerId, refresh]);\n\n const openVerifySidebar = useCallback(async () => {\n if (!providerId) return;\n setError(null);\n // Resync state before opening — the user may have installed or\n // disconnected while the hook was idle.\n const state = await peerExtensionSdk.getState().catch(() => \"needs_install\" as const);\n if (!mountedRef.current) return;\n const nextPhase = mapSdkState(state);\n setPhase(nextPhase);\n if (nextPhase === \"needs_install\") {\n installExtension();\n return;\n }\n if (nextPhase === \"needs_connection\") {\n await connectExtension();\n return;\n }\n try {\n peerExtensionSdk.openSidebar(`/verify/${providerId}`);\n } catch (err) {\n setError(\n err instanceof Error\n ? err.message\n : \"Couldn't open the Peer extension. Make sure it's enabled.\",\n );\n refresh();\n }\n }, [connectExtension, installExtension, providerId, refresh]);\n\n return {\n phase,\n info,\n busy,\n error,\n refresh,\n installExtension,\n connectExtension,\n openVerifySidebar,\n };\n}\n\nfunction mapSdkState(state: PeerExtensionState): PeerExtensionRegistrationPhase {\n if (state === \"needs_install\") return \"needs_install\";\n if (state === \"needs_connection\") return \"needs_connection\";\n return \"ready\";\n}\n"],"mappings":"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","names":["useCallback","useRef","useState","useState","useRef","useCallback"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usdctofiat/offramp",
3
- "version": "1.1.4",
3
+ "version": "2.0.0",
4
4
  "description": "USDC-to-fiat offramp SDK — create delegated deposits in one function call",
5
5
  "license": "MIT",
6
6
  "author": "Galleon Labs",
@@ -11,7 +11,8 @@
11
11
  },
12
12
  "files": [
13
13
  "dist",
14
- "README.md"
14
+ "README.md",
15
+ "CHANGELOG.md"
15
16
  ],
16
17
  "type": "module",
17
18
  "main": "./dist/index.cjs",
@@ -39,27 +40,37 @@
39
40
  }
40
41
  }
41
42
  },
43
+ "publishConfig": {
44
+ "access": "public"
45
+ },
42
46
  "scripts": {
43
47
  "build": "tsup",
44
48
  "typecheck": "tsgo --noEmit",
45
49
  "prepublishOnly": "tsup",
46
- "lint": "oxlint -c ../../.oxlintrc.json ."
50
+ "lint": "oxlint -c ../../.oxlintrc.json .",
51
+ "test": "vitest run"
47
52
  },
48
53
  "dependencies": {
49
- "@zkp2p/sdk": "0.3.1",
50
- "viem": "^2.45.0",
54
+ "@zkp2p/sdk": "0.3.2",
51
55
  "zod": "^4.3.6"
52
56
  },
53
57
  "devDependencies": {
54
58
  "tsup": "^8.5.0",
55
- "typescript": "^6.0.2"
59
+ "typescript": "^6.0.2",
60
+ "viem": "^2.45.0",
61
+ "vitest": "^4.1.2"
56
62
  },
57
63
  "peerDependencies": {
58
- "react": ">=18.0.0"
64
+ "react": ">=18.0.0",
65
+ "react-dom": ">=18.0.0",
66
+ "viem": "^2"
59
67
  },
60
68
  "peerDependenciesMeta": {
61
69
  "react": {
62
70
  "optional": true
71
+ },
72
+ "react-dom": {
73
+ "optional": true
63
74
  }
64
75
  },
65
76
  "engines": {