@usdctofiat/offramp 1.3.0 → 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/CHANGELOG.md ADDED
@@ -0,0 +1,252 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@usdctofiat/offramp` will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
6
+ and this project adheres to [Semantic Versioning](https://semver.org/).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [2.0.0] - 2026-04-24
11
+
12
+ ### ⚠ BREAKING CHANGES
13
+
14
+ The upstream zkp2p-clients protocol cut curator's maker registration over to a
15
+ new schema and endpoint, and now requires Peer browser-extension
16
+ pre-registration for PayPal and Wise makers. Previous versions of this SDK
17
+ were silently broken against the live API. Upgrading is **mandatory** for any
18
+ integrator still on `1.x` — the old payload shape is rejected by curator.
19
+
20
+ - **Endpoint**: maker registration now hits `POST /v2/makers/create`
21
+ (upstream commit 3243ce5). `/v1/makers/create` is gone.
22
+ - **Payload shape**: `buildDepositData(platform, identifier, telegramUsername?)`
23
+ now returns `{ offchainId, telegramUsername? }` instead of legacy
24
+ platform-specific blobs like `{ paypalEmail, venmoUsername, wisetag, … }`
25
+ (upstream commit e4e5bdd / PR #580). Curator hashes `offchainId + processorName`
26
+ directly, so the old keys are no longer read.
27
+ - **PayPal identifier**: PayPal deposits now use the PayPal.me username as the
28
+ offchainId, not the account email (upstream #649). `PLATFORMS.PAYPAL.validate(...)`
29
+ rejects emails and accepts `paypal.me/user`, `@user`, or the bare username,
30
+ all normalized to a lowercase `paypal.me` username.
31
+ - **New error code**: `OFFRAMP_ERROR_CODES.EXTENSION_REGISTRATION_REQUIRED`.
32
+ `offramp()` throws this when curator rejects a PayPal or Wise maker because
33
+ the user has not yet registered their handle inside the Peer (PeerAuth)
34
+ browser extension. Recover via `usePeerExtensionRegistration(platform)` (React)
35
+ or drive `peerExtensionSdk` manually, then retry the call.
36
+
37
+ ### Added
38
+
39
+ - **Extension handshake helpers** (core entry point):
40
+ - `peerExtensionSdk`, `PEER_EXTENSION_CHROME_URL`, `getPeerExtensionState`,
41
+ `isPeerExtensionAvailable`, `openPeerExtensionInstallPage`,
42
+ `createPeerExtensionSdk` — re-exports from `@zkp2p/sdk` so integrators
43
+ don't need a direct transitive dependency on it.
44
+ - Types: `PeerExtensionApi`, `PeerExtensionSdk`, `PeerExtensionSdkOptions`,
45
+ `PeerExtensionState`, `PeerExtensionWindow`, `PeerConnectionStatus`.
46
+ - **React hook** `usePeerExtensionRegistration(platform)` in
47
+ `@usdctofiat/offramp/react`. Drives the full install → connect → verify
48
+ flow with a phase state machine (`checking | needs_install | needs_connection | ready | unsupported`),
49
+ auto-invoking `requestConnection()` and `openSidebar('/verify/<providerId>')`
50
+ in response to `peerExtensionSdk.getState()`.
51
+ - **Platform metadata**: `PLATFORMS.PAYPAL.extensionRegistration` and
52
+ `PLATFORMS.WISE.extensionRegistration` surface the `providerId`, user-facing
53
+ prompt, CTA copy, and minimum extension version. Access via
54
+ `getPeerExtensionRegistrationInfo(platformId)`.
55
+ - **Error detection helper**: `isPeerExtensionRegistrationError(platformId, message, statusCode)`
56
+ returns true when a failed `/v2/makers/create` response should be treated as
57
+ "maker needs extension registration". Mirrors the upstream heuristic
58
+ (400 status on a registration-required platform, or any response whose
59
+ message contains `creating the maker` / `create the maker`).
60
+ - **MakersCreateError**: thrown by the internal maker-registration fetch,
61
+ preserving the HTTP status and raw response body so callers can inspect the
62
+ failure. Re-raised by `offramp()` as the `cause` on the surfaced `OfframpError`.
63
+ - **PayPal.me normalizer**: `normalizePaypalMeUsername(input)` exported for
64
+ integrators that want to normalize user input before hashing or display.
65
+ - **`PayeeDepositData`** type (the flat shape returned by `buildDepositData`).
66
+
67
+ ### Changed
68
+
69
+ - `PLATFORMS.PAYPAL.identifier` updated to username semantics (`label: "PayPal.me Username"`,
70
+ `placeholder: "paypal.me/your-username"`, `help: "Your PayPal.me username, not your email"`).
71
+ - `PLATFORMS.PAYPAL.validate(...)` enforces `[a-z0-9._-]+` and lowercases the
72
+ normalized value.
73
+ - `PLATFORMS.WISE` now ships `extensionRegistration` metadata (no prompt change
74
+ for the happy path).
75
+
76
+ ### Migration guide
77
+
78
+ If you were on `@usdctofiat/offramp@1.x`:
79
+
80
+ 1. Upgrade to `2.0.0`.
81
+ 2. For PayPal integrations, re-prompt users for their PayPal.me username
82
+ (not email). You can round-trip legacy emails through
83
+ `PLATFORMS.PAYPAL.validate(email)` to detect them — invalid input signals
84
+ the user needs to re-enter.
85
+ 3. Handle the new `EXTENSION_REGISTRATION_REQUIRED` error. Minimum example
86
+ with the React hook:
87
+
88
+ ```tsx
89
+ import { PLATFORMS, OFFRAMP_ERROR_CODES } from "@usdctofiat/offramp";
90
+ import { useOfframp, usePeerExtensionRegistration } from "@usdctofiat/offramp/react";
91
+
92
+ const { offramp, lastError } = useOfframp();
93
+ const peer = usePeerExtensionRegistration(PLATFORMS.PAYPAL);
94
+
95
+ if (lastError?.code === OFFRAMP_ERROR_CODES.EXTENSION_REGISTRATION_REQUIRED) {
96
+ // Render peer.phase-aware CTAs using peer.installExtension /
97
+ // peer.connectExtension / peer.openVerifySidebar, then call offramp() again.
98
+ }
99
+ ```
100
+
101
+ 4. If you hand-rolled a `POST /v1/makers/create` call, switch to
102
+ `POST /v2/makers/create` with `{ processorName, offchainId, telegramUsername? }`.
103
+
104
+ ## [1.3.0] - 2026-04-21
105
+
106
+ ### Removed
107
+
108
+ - Widget entry point. The `@usdctofiat/offramp/widget` subpath and its exports (`OfframpButton`, `OfframpInline`, `OfframpDrawer`), plus the `sdk.widget.view` / `sdk.widget.click` telemetry events. Call `offramp(...)` directly or use `useOfframp` from `@usdctofiat/offramp/react`. The `create-offramp-app` templates were updated to the plain-function flow.
109
+
110
+ ## [1.2.0] - 2026-04-21
111
+
112
+ ### Added
113
+
114
+ - Widget entry point at `@usdctofiat/offramp/widget` with `OfframpButton`, `OfframpInline`, `OfframpDrawer` components and `sdk.widget.*` telemetry events. Reverted in 1.3.0.
115
+ - Anonymous SDK usage telemetry (`sdk.deposit.*`) written to Firestore. Opt out via `new Offramp({ telemetry: false })`.
116
+
117
+ ### Changed
118
+
119
+ - `viem` moved from a direct dependency to a peer dependency so consumers share a single `viem` copy with the SDK, preventing duplicate-module `WalletClient` type clashes.
120
+
121
+ ## [1.1.4] - 2026-04-16
122
+
123
+ ### Fixed
124
+
125
+ - Case-insensitive `rateManagerId` comparison when matching delegated deposits.
126
+
127
+ ## [1.1.3] - 2026-04-09
128
+
129
+ ### Changed
130
+
131
+ - Bump `@zkp2p/sdk` to `0.3.1`. Picks up the curator API migration: the SDK now targets `/v3/intent/sign` (the legacy `/v3/intent` path is still aliased but scheduled for removal on 2026-07-01) and the maker schema is now flat (`offchainId` + `telegramUsername` columns). Transparent to SDK consumers.
132
+
133
+ ## [1.1.2] - 2026-04-06
134
+
135
+ ### Fixed
136
+
137
+ - Widen `viem` dependency from exact `2.45.3` to `^2.45.0`. The exact pin introduced in 1.1.1 forced npm to install a nested copy of `viem` whenever the consumer had any other `viem` version, causing TypeScript `Client is not assignable to Client` errors at the SDK boundary (two structurally identical but distinct module copies).
138
+
139
+ ## [1.1.1] - 2026-04-05
140
+
141
+ ### Fixed
142
+
143
+ - Updated `WhitelistPreIntentHook` address to the redeployed contract. The old hook reverted on OTC intent fulfillment via `OrchestratorV2`.
144
+
145
+ ## [1.1.0] - 2026-04-03
146
+
147
+ ### Added
148
+
149
+ - OTC private orders: restrict a deposit to a single taker wallet.
150
+ - `otcTaker` parameter on `offramp()` for one-call OTC deposit creation.
151
+ - `enableOtc(walletClient, depositId, takerAddress)` to restrict an existing deposit.
152
+ - `disableOtc(walletClient, depositId)` to make a deposit public again.
153
+ - `getOtcLink(depositId)` returns the taker URL without a transaction.
154
+ - `OfframpResult.otcLink` returned when `otcTaker` is provided.
155
+ - `"restricting"` step added to `OfframpStep` for progress callbacks.
156
+
157
+ ## [1.0.1] - 2026-03-29
158
+
159
+ ### Fixed
160
+
161
+ - Resume the most recent undelegated deposit first when a wallet has multiple in flight.
162
+
163
+ ## [1.0.0] - 2026-03-29
164
+
165
+ ### Changed
166
+
167
+ - Minimal surface refactor. The public API is now the `Offramp` class plus `offramp()` function (core) and `useOfframp` (React). This is the first stable release.
168
+
169
+ ### Removed
170
+
171
+ - Pre-1.0 surface area not carried forward. Integrators on 0.2.x should migrate to the new API.
172
+
173
+ ## [0.2.1] - 2026-03-29
174
+
175
+ ### Fixed
176
+
177
+ - Use `defaultIndexerEndpoint` for read-only indexer queries so SDK reads work without additional configuration.
178
+
179
+ ## [0.2.0] - 2026-03-29
180
+
181
+ ### Added
182
+
183
+ - Structured errors (`OfframpError` with `code`, `step`, `txHash`, `depositId`).
184
+ - Deposit recovery: the SDK resumes the most recent undelegated deposit for a wallet instead of creating a new one.
185
+
186
+ ### Fixed
187
+
188
+ - Indexer endpoint resolution.
189
+
190
+ ## [0.1.5] - 2026-03-29
191
+
192
+ ### Added
193
+
194
+ - `delegateDeposit()` for retrying delegation or delegating an existing deposit.
195
+
196
+ ## [0.1.4] - 2026-03-29
197
+
198
+ ### Added
199
+
200
+ - Typed constant maps for platforms and currencies.
201
+ - Currency metadata (symbol, decimals, display name).
202
+
203
+ ## [0.1.3] - 2026-03-29
204
+
205
+ ### Added
206
+
207
+ - Pre-flight USDC balance check before submitting a deposit.
208
+ - Amount truncation to USDC decimals.
209
+ - Wallet validation on deposit inputs.
210
+
211
+ ## [0.1.2] - 2026-03-29
212
+
213
+ ### Changed
214
+
215
+ - Reduced minimum deposit and minimum order to 1 USDC.
216
+
217
+ ## [0.1.1] - 2026-03-29
218
+
219
+ ### Fixed
220
+
221
+ - Resolve currency hashes to ISO codes in error messages and responses.
222
+ - Improved error messages for invalid currency and platform inputs.
223
+
224
+ ## [0.1.0] - 2026-03-29
225
+
226
+ ### Added
227
+
228
+ - Initial release of `@usdctofiat/offramp`.
229
+ - `Offramp` class and `offramp()` function for creating delegated USDC-to-fiat deposits on Base.
230
+ - `useOfframp` React hook (`@usdctofiat/offramp/react`).
231
+ - `getDeposits`, `withdrawDeposit`, `getPlatforms`, `validateIdentifier` helpers.
232
+ - All deposits automatically delegate to the Delegate vault, use `track_market` pricing, and carry Galleon Labs attribution.
233
+ - Dual CJS/ESM build via tsup with type definitions for both entry points.
234
+
235
+ [Unreleased]: https://github.com/ADWilkinson/galleonlabs-zkp2p/compare/main...HEAD
236
+ [1.3.0]: https://github.com/ADWilkinson/galleonlabs-zkp2p/releases/tag/offramp-sdk-v1.3.0
237
+ [1.2.0]: https://github.com/ADWilkinson/galleonlabs-zkp2p/releases/tag/offramp-sdk-v1.2.0
238
+ [1.1.4]: https://github.com/ADWilkinson/galleonlabs-zkp2p/releases/tag/offramp-sdk-v1.1.4
239
+ [1.1.3]: https://github.com/ADWilkinson/galleonlabs-zkp2p/releases/tag/offramp-sdk-v1.1.3
240
+ [1.1.2]: https://github.com/ADWilkinson/galleonlabs-zkp2p/releases/tag/offramp-sdk-v1.1.2
241
+ [1.1.1]: https://github.com/ADWilkinson/galleonlabs-zkp2p/releases/tag/offramp-sdk-v1.1.1
242
+ [1.1.0]: https://github.com/ADWilkinson/galleonlabs-zkp2p/releases/tag/offramp-sdk-v1.1.0
243
+ [1.0.1]: https://github.com/ADWilkinson/galleonlabs-zkp2p/releases/tag/offramp-sdk-v1.0.1
244
+ [1.0.0]: https://github.com/ADWilkinson/galleonlabs-zkp2p/releases/tag/offramp-sdk-v1.0.0
245
+ [0.2.1]: https://github.com/ADWilkinson/galleonlabs-zkp2p/releases/tag/offramp-sdk-v0.2.1
246
+ [0.2.0]: https://github.com/ADWilkinson/galleonlabs-zkp2p/releases/tag/offramp-sdk-v0.2.0
247
+ [0.1.5]: https://github.com/ADWilkinson/galleonlabs-zkp2p/releases/tag/offramp-sdk-v0.1.5
248
+ [0.1.4]: https://github.com/ADWilkinson/galleonlabs-zkp2p/releases/tag/offramp-sdk-v0.1.4
249
+ [0.1.3]: https://github.com/ADWilkinson/galleonlabs-zkp2p/releases/tag/offramp-sdk-v0.1.3
250
+ [0.1.2]: https://github.com/ADWilkinson/galleonlabs-zkp2p/releases/tag/offramp-sdk-v0.1.2
251
+ [0.1.1]: https://github.com/ADWilkinson/galleonlabs-zkp2p/releases/tag/offramp-sdk-v0.1.1
252
+ [0.1.0]: https://github.com/ADWilkinson/galleonlabs-zkp2p/releases/tag/offramp-sdk-v0.1.0
package/README.md CHANGED
@@ -111,22 +111,102 @@ getOtcLink("362"); // just the URL, no tx
111
111
  ## Error Handling
112
112
 
113
113
  ```typescript
114
- import { OfframpError } from "@usdctofiat/offramp";
114
+ import { OfframpError, OFFRAMP_ERROR_CODES } from "@usdctofiat/offramp";
115
115
 
116
116
  try {
117
117
  await offramp(walletClient, params);
118
118
  } catch (err) {
119
119
  if (err instanceof OfframpError) {
120
120
  if (err.code === "USER_CANCELLED") return;
121
- // For any failure: call offramp() again to resume
121
+ if (err.code === OFFRAMP_ERROR_CODES.EXTENSION_REGISTRATION_REQUIRED) {
122
+ // User needs to register in the Peer extension first — see below.
123
+ return;
124
+ }
125
+ // For any other failure: call offramp() again to resume
122
126
  }
123
127
  }
124
128
  ```
125
129
 
130
+ ## Peer Extension Registration (PayPal, Wise)
131
+
132
+ PayPal and Wise makers must register their handle inside the Peer (PeerAuth)
133
+ browser extension before the first deposit. The SDK throws `OfframpError`
134
+ with code `EXTENSION_REGISTRATION_REQUIRED` when curator rejects a maker for
135
+ this reason. Drive the three-step handshake via the React hook:
136
+
137
+ ```tsx
138
+ import { PLATFORMS, CURRENCIES, OFFRAMP_ERROR_CODES } from "@usdctofiat/offramp";
139
+ import { useOfframp, usePeerExtensionRegistration } from "@usdctofiat/offramp/react";
140
+
141
+ function PayPalSellButton({ walletClient }) {
142
+ const { offramp, lastError } = useOfframp();
143
+ const peer = usePeerExtensionRegistration(PLATFORMS.PAYPAL);
144
+
145
+ const needsExtension = lastError?.code === OFFRAMP_ERROR_CODES.EXTENSION_REGISTRATION_REQUIRED;
146
+
147
+ return (
148
+ <>
149
+ <button
150
+ onClick={() =>
151
+ offramp(walletClient, {
152
+ amount: "100",
153
+ platform: PLATFORMS.PAYPAL,
154
+ currency: CURRENCIES.USD,
155
+ identifier: "alicepay", // PayPal.me username, NOT email
156
+ })
157
+ }
158
+ >
159
+ Sell USDC
160
+ </button>
161
+
162
+ {needsExtension && (
163
+ <div>
164
+ <p>{peer.info?.requiredPrompt}</p>
165
+ {peer.phase === "needs_install" && (
166
+ <button onClick={peer.installExtension}>Install Peer Extension</button>
167
+ )}
168
+ {peer.phase === "needs_connection" && (
169
+ <button onClick={peer.connectExtension} disabled={peer.busy}>
170
+ Connect Peer Extension
171
+ </button>
172
+ )}
173
+ {peer.phase === "ready" && (
174
+ <button onClick={peer.openVerifySidebar}>Verify in Peer</button>
175
+ )}
176
+ {peer.info?.ctaSubtext && <small>{peer.info.ctaSubtext}</small>}
177
+ </div>
178
+ )}
179
+ </>
180
+ );
181
+ }
182
+ ```
183
+
184
+ Or drive the handshake manually via `peerExtensionSdk`:
185
+
186
+ ```typescript
187
+ import { peerExtensionSdk } from "@usdctofiat/offramp";
188
+
189
+ const state = await peerExtensionSdk.getState();
190
+ if (state === "needs_install") {
191
+ peerExtensionSdk.openInstallPage();
192
+ } else if (state === "needs_connection") {
193
+ const approved = await peerExtensionSdk.requestConnection();
194
+ if (approved) peerExtensionSdk.openSidebar("/verify/paypal");
195
+ } else {
196
+ peerExtensionSdk.openSidebar("/verify/paypal");
197
+ }
198
+ ```
199
+
200
+ After the user completes the flow in the extension, call `offramp()` again.
201
+
126
202
  ## How It Works
127
203
 
128
204
  Every deposit is automatically delegated to the Delegate vault for oracle-based rate management, auto-closes when filled, and is attributed to Galleon Labs via ERC-8021.
129
205
 
206
+ ## Webhooks
207
+
208
+ Subscribe to `deposit.created`, `deposit.filled`, `deposit.partially_filled`, `deposit.closed`, `otc.enabled`, `otc.disabled`, and `otc.taken` events for your attributed deposits. Register endpoints at [usdctofiat.xyz/developers](https://usdctofiat.xyz/developers) with your Peerlytics API key. HMAC-SHA256 signed deliveries use the `X-Usdctofiat-Signature: t=<unix>,v1=<hex>` header. Reference HMAC receiver in the [starters repo](https://github.com/ADWilkinson/usdctofiat-peerlytics-starters).
209
+
130
210
  ## Links
131
211
 
132
212
  [usdctofiat.xyz](https://usdctofiat.xyz) · [delegate.usdctofiat.xyz](https://delegate.usdctofiat.xyz) · [peerlytics.xyz](https://peerlytics.xyz) · [orderbook.peerlytics.xyz](https://orderbook.peerlytics.xyz)
@@ -122,6 +122,17 @@ function resolveSupportedCurrencies(platform) {
122
122
  }
123
123
  return Array.from(codes).sort();
124
124
  }
125
+ function normalizePaypalMeUsername(value) {
126
+ const trimmed = value.trim();
127
+ if (!trimmed) return "";
128
+ const withoutProtocol = trimmed.replace(/^https?:\/\//i, "").replace(/^www\./i, "");
129
+ if (/^paypal\.me(?:[?#].*)?$/i.test(withoutProtocol)) return "";
130
+ const withoutDomain = withoutProtocol.replace(/^paypal\.me\//i, "");
131
+ const [pathWithoutQuery] = withoutDomain.split(/[?#]/, 1);
132
+ const sanitizedPath = pathWithoutQuery.replace(/^\/+/, "");
133
+ const [username] = sanitizedPath.split("/", 1);
134
+ return username.replace(/^@+/, "").trim().toLowerCase();
135
+ }
125
136
  var BLUEPRINTS = {
126
137
  venmo: {
127
138
  id: "venmo",
@@ -169,7 +180,13 @@ var BLUEPRINTS = {
169
180
  placeholder: "wisetag (no @)",
170
181
  helperText: "Your Wise @wisetag (no @)",
171
182
  validation: z.string().min(1).regex(/^[a-zA-Z0-9_-]+$/),
172
- transform: (v) => v.replace(/^@+/, "").trim()
183
+ transform: (v) => v.replace(/^@+/, "").trim(),
184
+ extensionRegistration: {
185
+ providerId: "wise",
186
+ requiredPrompt: "This Wisetag is not registered yet. Verify it in the Peer extension, then refresh and retry with the same Wisetag.",
187
+ ctaLabel: "Install Peer Extension",
188
+ minExtensionVersion: "0.4.12"
189
+ }
173
190
  },
174
191
  mercadopago: {
175
192
  id: "mercadopago",
@@ -190,10 +207,18 @@ var BLUEPRINTS = {
190
207
  paypal: {
191
208
  id: "paypal",
192
209
  name: "PayPal",
193
- identifierLabel: "Email",
194
- placeholder: "email",
195
- helperText: "Email linked to PayPal account",
196
- validation: z.string().email()
210
+ identifierLabel: "PayPal.me Username",
211
+ placeholder: "paypal.me/your-username",
212
+ helperText: "Your PayPal.me username, not your email",
213
+ validation: z.string().min(1, "PayPal.me username is required").regex(/^[a-z0-9._-]+$/i, "Use your PayPal.me username (letters, numbers, . _ -)"),
214
+ transform: normalizePaypalMeUsername,
215
+ extensionRegistration: {
216
+ providerId: "paypal",
217
+ requiredPrompt: "This PayPal username is not registered yet. Verify it in the Peer extension, then refresh and retry with the same PayPal username.",
218
+ ctaLabel: "Install Peer Extension",
219
+ ctaSubtext: "PayPal Business accounts are not supported at this time.",
220
+ minExtensionVersion: "0.4.14"
221
+ }
197
222
  },
198
223
  monzo: {
199
224
  id: "monzo",
@@ -224,6 +249,7 @@ function buildPlatformEntry(bp) {
224
249
  placeholder: bp.placeholder,
225
250
  help: bp.helperText
226
251
  },
252
+ ...bp.extensionRegistration ? { extensionRegistration: bp.extensionRegistration } : {},
227
253
  validate(input) {
228
254
  const transformed = bp.transform ? bp.transform(input) : input;
229
255
  const result = bp.validation.safeParse(transformed);
@@ -250,6 +276,9 @@ var PLATFORMS = {
250
276
  MONZO: buildPlatformEntry(BLUEPRINTS.monzo),
251
277
  N26: buildPlatformEntry(BLUEPRINTS.n26)
252
278
  };
279
+ function getPlatformById(id) {
280
+ return Object.values(PLATFORMS).find((p) => p.id === id) ?? null;
281
+ }
253
282
  function normalizePaymentMethodLookupName(platform) {
254
283
  const normalized = platform.trim().toLowerCase();
255
284
  if (!normalized) return null;
@@ -293,34 +322,37 @@ function getPaymentMethodHashes(platform) {
293
322
  }
294
323
  return Array.from(hashes);
295
324
  }
296
- function buildDepositData(platform, identifier) {
297
- switch (platform) {
298
- case "venmo":
299
- return { venmoUsername: identifier, telegramUsername: "" };
300
- case "cashapp":
301
- return { cashtag: identifier, telegramUsername: "" };
302
- case "chime":
303
- return { chimesign: identifier.toLowerCase(), telegramUsername: "" };
304
- case "revolut":
305
- return { revolutUsername: identifier, telegramUsername: "" };
306
- case "wise":
307
- return { wisetag: identifier, telegramUsername: "" };
308
- case "mercadopago":
309
- return { cvu: identifier, telegramUsername: "" };
310
- case "zelle":
311
- return { zelleEmail: identifier, telegramUsername: "" };
312
- case "paypal":
313
- return { paypalEmail: identifier, telegramUsername: "" };
314
- case "monzo":
315
- return { monzoMeUsername: identifier, telegramUsername: "" };
316
- case "n26":
317
- return { iban: identifier, telegramUsername: "" };
318
- default:
319
- return { identifier, telegramUsername: "" };
320
- }
325
+ function buildDepositData(platform, identifier, telegramUsername = "") {
326
+ const offchainId = platform === "chime" ? identifier.toLowerCase() : identifier;
327
+ return telegramUsername ? { offchainId, telegramUsername } : { offchainId };
328
+ }
329
+ function getPeerExtensionRegistrationInfo(platform) {
330
+ const entry = getPlatformById(platform);
331
+ return entry?.extensionRegistration ?? null;
332
+ }
333
+ var EXTENSION_REGISTRATION_ERROR_PATTERNS = ["creating the maker", "create the maker"];
334
+ function isPeerExtensionRegistrationError(platform, message, statusCode) {
335
+ const info = getPeerExtensionRegistrationInfo(platform);
336
+ if (!info) return false;
337
+ const normalized = (message ?? "").trim();
338
+ if (normalized === info.requiredPrompt) return true;
339
+ if (statusCode === 400) return true;
340
+ if (!normalized) return false;
341
+ const lower = normalized.toLowerCase();
342
+ return EXTENSION_REGISTRATION_ERROR_PATTERNS.some((p) => lower.includes(p));
321
343
  }
322
344
 
323
345
  // src/errors.ts
346
+ var MakersCreateError = class extends Error {
347
+ status;
348
+ body;
349
+ constructor(message, status, body) {
350
+ super(message);
351
+ this.name = "MakersCreateError";
352
+ this.status = status;
353
+ this.body = body;
354
+ }
355
+ };
324
356
  var OfframpError = class extends Error {
325
357
  code;
326
358
  step;
@@ -1122,23 +1154,39 @@ function extractDepositIdFromLogs(logs) {
1122
1154
  }
1123
1155
  return null;
1124
1156
  }
1125
- async function registerPayeeDetails(processorName, depositData) {
1157
+ async function registerPayeeDetails(processorName, payload) {
1126
1158
  const controller = new AbortController();
1127
1159
  const timeout = setTimeout(() => controller.abort(), PAYEE_REGISTRATION_TIMEOUT_MS);
1128
1160
  try {
1129
- const res = await fetch(`${API_BASE_URL}/v1/makers/create`, {
1161
+ const res = await fetch(`${API_BASE_URL}/v2/makers/create`, {
1130
1162
  method: "POST",
1131
1163
  headers: { "Content-Type": "application/json" },
1132
- body: JSON.stringify({ processorName, depositData }),
1164
+ body: JSON.stringify({ processorName, ...payload }),
1133
1165
  signal: controller.signal
1134
1166
  });
1135
1167
  if (!res.ok) {
1136
1168
  const txt = await res.text().catch(() => "");
1137
- throw new Error(`makers/create failed (${res.status}): ${txt || res.statusText}`);
1169
+ let detail = txt || res.statusText;
1170
+ try {
1171
+ const parsed = JSON.parse(txt);
1172
+ if (typeof parsed.message === "string" && parsed.message.trim()) {
1173
+ detail = parsed.message;
1174
+ }
1175
+ } catch {
1176
+ }
1177
+ throw new MakersCreateError(
1178
+ `makers/create failed (${res.status}): ${detail}`,
1179
+ res.status,
1180
+ txt
1181
+ );
1138
1182
  }
1139
1183
  const json = await res.json();
1140
1184
  if (!json.success || !json.responseObject?.hashedOnchainId) {
1141
- throw new Error(json.message || "makers/create returned no hashedOnchainId");
1185
+ throw new MakersCreateError(
1186
+ json.message || "makers/create returned no hashedOnchainId",
1187
+ json.statusCode ?? null,
1188
+ ""
1189
+ );
1142
1190
  }
1143
1191
  return json.responseObject.hashedOnchainId;
1144
1192
  } finally {
@@ -1340,11 +1388,21 @@ async function offramp(walletClient, params, onProgress, telemetryContext) {
1340
1388
  }
1341
1389
  emitProgress({ step: "registering" });
1342
1390
  let hashedOnchainId;
1391
+ const canonicalName = platformId.startsWith("zelle") ? "zelle" : platformId;
1343
1392
  try {
1344
- const canonicalName = platformId.startsWith("zelle") ? "zelle" : platformId;
1345
- const depositData = buildDepositData(platformId, normalizedIdentifier);
1346
- hashedOnchainId = await registerPayeeDetails(canonicalName, depositData);
1393
+ const payeePayload = buildDepositData(canonicalName, normalizedIdentifier);
1394
+ hashedOnchainId = await registerPayeeDetails(canonicalName, payeePayload);
1347
1395
  } catch (err) {
1396
+ const status = err instanceof MakersCreateError ? err.status : null;
1397
+ const message = err instanceof Error ? err.message : String(err);
1398
+ if (isPeerExtensionRegistrationError(canonicalName, message, status)) {
1399
+ throw new OfframpError(
1400
+ `${platform.name} maker is not yet registered in the Peer extension. Verify this handle in the extension, then retry.`,
1401
+ "EXTENSION_REGISTRATION_REQUIRED",
1402
+ "registering",
1403
+ err
1404
+ );
1405
+ }
1348
1406
  throw new OfframpError(
1349
1407
  "Payee registration failed",
1350
1408
  "REGISTRATION_FAILED",
@@ -1677,9 +1735,23 @@ function createOfframp(options) {
1677
1735
  return new Offramp(options);
1678
1736
  }
1679
1737
 
1738
+ // src/extension.ts
1739
+ import {
1740
+ PEER_EXTENSION_CHROME_URL,
1741
+ peerExtensionSdk,
1742
+ createPeerExtensionSdk,
1743
+ isPeerExtensionAvailable,
1744
+ openPeerExtensionInstallPage,
1745
+ getPeerExtensionState
1746
+ } from "@zkp2p/sdk";
1747
+
1680
1748
  export {
1681
1749
  ESCROW_ADDRESS,
1750
+ normalizePaypalMeUsername,
1682
1751
  PLATFORMS,
1752
+ getPeerExtensionRegistrationInfo,
1753
+ isPeerExtensionRegistrationError,
1754
+ MakersCreateError,
1683
1755
  OfframpError,
1684
1756
  enableOtc,
1685
1757
  disableOtc,
@@ -1695,6 +1767,12 @@ export {
1695
1767
  offramp,
1696
1768
  CURRENCIES,
1697
1769
  Offramp,
1698
- createOfframp
1770
+ createOfframp,
1771
+ PEER_EXTENSION_CHROME_URL,
1772
+ peerExtensionSdk,
1773
+ createPeerExtensionSdk,
1774
+ isPeerExtensionAvailable,
1775
+ openPeerExtensionInstallPage,
1776
+ getPeerExtensionState
1699
1777
  };
1700
- //# sourceMappingURL=chunk-JQBKGGI2.js.map
1778
+ //# sourceMappingURL=chunk-HBR3Z6HX.js.map