@usdctofiat/offramp 1.3.0 → 2.0.1
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 +270 -0
- package/README.md +82 -2
- package/dist/{chunk-JQBKGGI2.js → chunk-747S3WWB.js} +117 -40
- package/dist/chunk-747S3WWB.js.map +1 -0
- package/dist/{errors-CfZrDly0.d.cts → errors-DKQ2N_ih.d.cts} +73 -1
- package/dist/{errors-CfZrDly0.d.ts → errors-DKQ2N_ih.d.ts} +73 -1
- package/dist/index.cjs +127 -38
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +30 -1
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +219 -40
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +86 -2
- package/dist/react.d.ts +86 -2
- package/dist/react.js +122 -3
- package/dist/react.js.map +1 -1
- package/package.json +3 -2
- package/dist/chunk-JQBKGGI2.js.map +0 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
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.1] - 2026-04-24
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- `isPeerExtensionRegistrationError` dropped an unreachable exact-match branch
|
|
15
|
+
(`message === info.requiredPrompt`). The check was dead code — `offramp()`
|
|
16
|
+
always passes the prefixed `MakersCreateError.message`, so the direct-equality
|
|
17
|
+
path never fired. Detection still works via the 400-status heuristic and the
|
|
18
|
+
curator-phrase match; no observable behavior change.
|
|
19
|
+
|
|
20
|
+
### Docs
|
|
21
|
+
|
|
22
|
+
- Trimmed over-long JSDoc blocks on `PeerExtensionRegistrationInfo`,
|
|
23
|
+
`PlatformEntry.extensionRegistration`, `PayeeDepositData`, `buildDepositData`,
|
|
24
|
+
`isPeerExtensionRegistrationError`, and the `extension.ts` module header.
|
|
25
|
+
- Fixed the `usePeerExtensionRegistration` JSDoc example — `useOfframp` imports
|
|
26
|
+
from `@usdctofiat/offramp/react`, not the core entry.
|
|
27
|
+
|
|
28
|
+
## [2.0.0] - 2026-04-24
|
|
29
|
+
|
|
30
|
+
### ⚠ BREAKING CHANGES
|
|
31
|
+
|
|
32
|
+
The upstream zkp2p-clients protocol cut curator's maker registration over to a
|
|
33
|
+
new schema and endpoint, and now requires Peer browser-extension
|
|
34
|
+
pre-registration for PayPal and Wise makers. Previous versions of this SDK
|
|
35
|
+
were silently broken against the live API. Upgrading is **mandatory** for any
|
|
36
|
+
integrator still on `1.x`: the old payload shape is rejected by curator.
|
|
37
|
+
|
|
38
|
+
- **Endpoint**: maker registration now hits `POST /v2/makers/create`
|
|
39
|
+
(upstream commit 3243ce5). `/v1/makers/create` is gone.
|
|
40
|
+
- **Payload shape**: `buildDepositData(platform, identifier, telegramUsername?)`
|
|
41
|
+
now returns `{ offchainId, telegramUsername? }` instead of legacy
|
|
42
|
+
platform-specific blobs like `{ paypalEmail, venmoUsername, wisetag, … }`
|
|
43
|
+
(upstream commit e4e5bdd / PR #580). Curator hashes `offchainId + processorName`
|
|
44
|
+
directly, so the old keys are no longer read.
|
|
45
|
+
- **PayPal identifier**: PayPal deposits now use the PayPal.me username as the
|
|
46
|
+
offchainId, not the account email (upstream #649). `PLATFORMS.PAYPAL.validate(...)`
|
|
47
|
+
rejects emails and accepts `paypal.me/user`, `@user`, or the bare username,
|
|
48
|
+
all normalized to a lowercase `paypal.me` username.
|
|
49
|
+
- **New error code**: `OFFRAMP_ERROR_CODES.EXTENSION_REGISTRATION_REQUIRED`.
|
|
50
|
+
`offramp()` throws this when curator rejects a PayPal or Wise maker because
|
|
51
|
+
the user has not yet registered their handle inside the Peer (PeerAuth)
|
|
52
|
+
browser extension. Recover via `usePeerExtensionRegistration(platform)` (React)
|
|
53
|
+
or drive `peerExtensionSdk` manually, then retry the call.
|
|
54
|
+
|
|
55
|
+
### Added
|
|
56
|
+
|
|
57
|
+
- **Extension handshake helpers** (core entry point):
|
|
58
|
+
- `peerExtensionSdk`, `PEER_EXTENSION_CHROME_URL`, `getPeerExtensionState`,
|
|
59
|
+
`isPeerExtensionAvailable`, `openPeerExtensionInstallPage`,
|
|
60
|
+
`createPeerExtensionSdk`. Re-exported from `@zkp2p/sdk` so integrators
|
|
61
|
+
don't need a direct dependency on it.
|
|
62
|
+
- Types: `PeerExtensionApi`, `PeerExtensionSdk`, `PeerExtensionSdkOptions`,
|
|
63
|
+
`PeerExtensionState`, `PeerExtensionWindow`, `PeerConnectionStatus`.
|
|
64
|
+
- **React hook** `usePeerExtensionRegistration(platform)` in
|
|
65
|
+
`@usdctofiat/offramp/react`. Drives the full install → connect → verify
|
|
66
|
+
flow with a phase state machine (`checking | needs_install | needs_connection | ready | unsupported`),
|
|
67
|
+
auto-invoking `requestConnection()` and `openSidebar('/verify/<providerId>')`
|
|
68
|
+
in response to `peerExtensionSdk.getState()`.
|
|
69
|
+
- **Platform metadata**: `PLATFORMS.PAYPAL.extensionRegistration` and
|
|
70
|
+
`PLATFORMS.WISE.extensionRegistration` surface the `providerId`, user-facing
|
|
71
|
+
prompt, CTA copy, and minimum extension version. Access via
|
|
72
|
+
`getPeerExtensionRegistrationInfo(platformId)`.
|
|
73
|
+
- **Error detection helper**: `isPeerExtensionRegistrationError(platformId, message, statusCode)`
|
|
74
|
+
returns true when a failed `/v2/makers/create` response should be treated as
|
|
75
|
+
"maker needs extension registration". Mirrors the upstream heuristic
|
|
76
|
+
(400 status on a registration-required platform, or any response whose
|
|
77
|
+
message contains `creating the maker` / `create the maker`).
|
|
78
|
+
- **MakersCreateError**: thrown by the internal maker-registration fetch,
|
|
79
|
+
preserving the HTTP status and raw response body so callers can inspect the
|
|
80
|
+
failure. Re-raised by `offramp()` as the `cause` on the surfaced `OfframpError`.
|
|
81
|
+
- **PayPal.me normalizer**: `normalizePaypalMeUsername(input)` exported for
|
|
82
|
+
integrators that want to normalize user input before hashing or display.
|
|
83
|
+
- **`PayeeDepositData`** type (the flat shape returned by `buildDepositData`).
|
|
84
|
+
|
|
85
|
+
### Changed
|
|
86
|
+
|
|
87
|
+
- `PLATFORMS.PAYPAL.identifier` updated to username semantics (`label: "PayPal.me Username"`,
|
|
88
|
+
`placeholder: "paypal.me/your-username"`, `help: "Your PayPal.me username, not your email"`).
|
|
89
|
+
- `PLATFORMS.PAYPAL.validate(...)` enforces `[a-z0-9._-]+` and lowercases the
|
|
90
|
+
normalized value.
|
|
91
|
+
- `PLATFORMS.WISE` now ships `extensionRegistration` metadata (no prompt change
|
|
92
|
+
for the happy path).
|
|
93
|
+
|
|
94
|
+
### Migration guide
|
|
95
|
+
|
|
96
|
+
If you were on `@usdctofiat/offramp@1.x`:
|
|
97
|
+
|
|
98
|
+
1. Upgrade to `2.0.0`.
|
|
99
|
+
2. For PayPal integrations, re-prompt users for their PayPal.me username
|
|
100
|
+
(not email). You can round-trip legacy emails through
|
|
101
|
+
`PLATFORMS.PAYPAL.validate(email)` to detect them — invalid input signals
|
|
102
|
+
the user needs to re-enter.
|
|
103
|
+
3. Handle the new `EXTENSION_REGISTRATION_REQUIRED` error. Minimum example
|
|
104
|
+
with the React hook:
|
|
105
|
+
|
|
106
|
+
```tsx
|
|
107
|
+
import { PLATFORMS, OFFRAMP_ERROR_CODES } from "@usdctofiat/offramp";
|
|
108
|
+
import { useOfframp, usePeerExtensionRegistration } from "@usdctofiat/offramp/react";
|
|
109
|
+
|
|
110
|
+
const { offramp, lastError } = useOfframp();
|
|
111
|
+
const peer = usePeerExtensionRegistration(PLATFORMS.PAYPAL);
|
|
112
|
+
|
|
113
|
+
if (lastError?.code === OFFRAMP_ERROR_CODES.EXTENSION_REGISTRATION_REQUIRED) {
|
|
114
|
+
// Render peer.phase-aware CTAs using peer.installExtension /
|
|
115
|
+
// peer.connectExtension / peer.openVerifySidebar, then call offramp() again.
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
4. If you hand-rolled a `POST /v1/makers/create` call, switch to
|
|
120
|
+
`POST /v2/makers/create` with `{ processorName, offchainId, telegramUsername? }`.
|
|
121
|
+
|
|
122
|
+
## [1.3.0] - 2026-04-21
|
|
123
|
+
|
|
124
|
+
### Removed
|
|
125
|
+
|
|
126
|
+
- 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.
|
|
127
|
+
|
|
128
|
+
## [1.2.0] - 2026-04-21
|
|
129
|
+
|
|
130
|
+
### Added
|
|
131
|
+
|
|
132
|
+
- Widget entry point at `@usdctofiat/offramp/widget` with `OfframpButton`, `OfframpInline`, `OfframpDrawer` components and `sdk.widget.*` telemetry events. Reverted in 1.3.0.
|
|
133
|
+
- Anonymous SDK usage telemetry (`sdk.deposit.*`) written to Firestore. Opt out via `new Offramp({ telemetry: false })`.
|
|
134
|
+
|
|
135
|
+
### Changed
|
|
136
|
+
|
|
137
|
+
- `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.
|
|
138
|
+
|
|
139
|
+
## [1.1.4] - 2026-04-16
|
|
140
|
+
|
|
141
|
+
### Fixed
|
|
142
|
+
|
|
143
|
+
- Case-insensitive `rateManagerId` comparison when matching delegated deposits.
|
|
144
|
+
|
|
145
|
+
## [1.1.3] - 2026-04-09
|
|
146
|
+
|
|
147
|
+
### Changed
|
|
148
|
+
|
|
149
|
+
- 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.
|
|
150
|
+
|
|
151
|
+
## [1.1.2] - 2026-04-06
|
|
152
|
+
|
|
153
|
+
### Fixed
|
|
154
|
+
|
|
155
|
+
- 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).
|
|
156
|
+
|
|
157
|
+
## [1.1.1] - 2026-04-05
|
|
158
|
+
|
|
159
|
+
### Fixed
|
|
160
|
+
|
|
161
|
+
- Updated `WhitelistPreIntentHook` address to the redeployed contract. The old hook reverted on OTC intent fulfillment via `OrchestratorV2`.
|
|
162
|
+
|
|
163
|
+
## [1.1.0] - 2026-04-03
|
|
164
|
+
|
|
165
|
+
### Added
|
|
166
|
+
|
|
167
|
+
- OTC private orders: restrict a deposit to a single taker wallet.
|
|
168
|
+
- `otcTaker` parameter on `offramp()` for one-call OTC deposit creation.
|
|
169
|
+
- `enableOtc(walletClient, depositId, takerAddress)` to restrict an existing deposit.
|
|
170
|
+
- `disableOtc(walletClient, depositId)` to make a deposit public again.
|
|
171
|
+
- `getOtcLink(depositId)` returns the taker URL without a transaction.
|
|
172
|
+
- `OfframpResult.otcLink` returned when `otcTaker` is provided.
|
|
173
|
+
- `"restricting"` step added to `OfframpStep` for progress callbacks.
|
|
174
|
+
|
|
175
|
+
## [1.0.1] - 2026-03-29
|
|
176
|
+
|
|
177
|
+
### Fixed
|
|
178
|
+
|
|
179
|
+
- Resume the most recent undelegated deposit first when a wallet has multiple in flight.
|
|
180
|
+
|
|
181
|
+
## [1.0.0] - 2026-03-29
|
|
182
|
+
|
|
183
|
+
### Changed
|
|
184
|
+
|
|
185
|
+
- Minimal surface refactor. The public API is now the `Offramp` class plus `offramp()` function (core) and `useOfframp` (React). This is the first stable release.
|
|
186
|
+
|
|
187
|
+
### Removed
|
|
188
|
+
|
|
189
|
+
- Pre-1.0 surface area not carried forward. Integrators on 0.2.x should migrate to the new API.
|
|
190
|
+
|
|
191
|
+
## [0.2.1] - 2026-03-29
|
|
192
|
+
|
|
193
|
+
### Fixed
|
|
194
|
+
|
|
195
|
+
- Use `defaultIndexerEndpoint` for read-only indexer queries so SDK reads work without additional configuration.
|
|
196
|
+
|
|
197
|
+
## [0.2.0] - 2026-03-29
|
|
198
|
+
|
|
199
|
+
### Added
|
|
200
|
+
|
|
201
|
+
- Structured errors (`OfframpError` with `code`, `step`, `txHash`, `depositId`).
|
|
202
|
+
- Deposit recovery: the SDK resumes the most recent undelegated deposit for a wallet instead of creating a new one.
|
|
203
|
+
|
|
204
|
+
### Fixed
|
|
205
|
+
|
|
206
|
+
- Indexer endpoint resolution.
|
|
207
|
+
|
|
208
|
+
## [0.1.5] - 2026-03-29
|
|
209
|
+
|
|
210
|
+
### Added
|
|
211
|
+
|
|
212
|
+
- `delegateDeposit()` for retrying delegation or delegating an existing deposit.
|
|
213
|
+
|
|
214
|
+
## [0.1.4] - 2026-03-29
|
|
215
|
+
|
|
216
|
+
### Added
|
|
217
|
+
|
|
218
|
+
- Typed constant maps for platforms and currencies.
|
|
219
|
+
- Currency metadata (symbol, decimals, display name).
|
|
220
|
+
|
|
221
|
+
## [0.1.3] - 2026-03-29
|
|
222
|
+
|
|
223
|
+
### Added
|
|
224
|
+
|
|
225
|
+
- Pre-flight USDC balance check before submitting a deposit.
|
|
226
|
+
- Amount truncation to USDC decimals.
|
|
227
|
+
- Wallet validation on deposit inputs.
|
|
228
|
+
|
|
229
|
+
## [0.1.2] - 2026-03-29
|
|
230
|
+
|
|
231
|
+
### Changed
|
|
232
|
+
|
|
233
|
+
- Reduced minimum deposit and minimum order to 1 USDC.
|
|
234
|
+
|
|
235
|
+
## [0.1.1] - 2026-03-29
|
|
236
|
+
|
|
237
|
+
### Fixed
|
|
238
|
+
|
|
239
|
+
- Resolve currency hashes to ISO codes in error messages and responses.
|
|
240
|
+
- Improved error messages for invalid currency and platform inputs.
|
|
241
|
+
|
|
242
|
+
## [0.1.0] - 2026-03-29
|
|
243
|
+
|
|
244
|
+
### Added
|
|
245
|
+
|
|
246
|
+
- Initial release of `@usdctofiat/offramp`.
|
|
247
|
+
- `Offramp` class and `offramp()` function for creating delegated USDC-to-fiat deposits on Base.
|
|
248
|
+
- `useOfframp` React hook (`@usdctofiat/offramp/react`).
|
|
249
|
+
- `getDeposits`, `withdrawDeposit`, `getPlatforms`, `validateIdentifier` helpers.
|
|
250
|
+
- All deposits automatically delegate to the Delegate vault, use `track_market` pricing, and carry Galleon Labs attribution.
|
|
251
|
+
- Dual CJS/ESM build via tsup with type definitions for both entry points.
|
|
252
|
+
|
|
253
|
+
[Unreleased]: https://github.com/ADWilkinson/galleonlabs-zkp2p/compare/main...HEAD
|
|
254
|
+
[1.3.0]: https://github.com/ADWilkinson/galleonlabs-zkp2p/releases/tag/offramp-sdk-v1.3.0
|
|
255
|
+
[1.2.0]: https://github.com/ADWilkinson/galleonlabs-zkp2p/releases/tag/offramp-sdk-v1.2.0
|
|
256
|
+
[1.1.4]: https://github.com/ADWilkinson/galleonlabs-zkp2p/releases/tag/offramp-sdk-v1.1.4
|
|
257
|
+
[1.1.3]: https://github.com/ADWilkinson/galleonlabs-zkp2p/releases/tag/offramp-sdk-v1.1.3
|
|
258
|
+
[1.1.2]: https://github.com/ADWilkinson/galleonlabs-zkp2p/releases/tag/offramp-sdk-v1.1.2
|
|
259
|
+
[1.1.1]: https://github.com/ADWilkinson/galleonlabs-zkp2p/releases/tag/offramp-sdk-v1.1.1
|
|
260
|
+
[1.1.0]: https://github.com/ADWilkinson/galleonlabs-zkp2p/releases/tag/offramp-sdk-v1.1.0
|
|
261
|
+
[1.0.1]: https://github.com/ADWilkinson/galleonlabs-zkp2p/releases/tag/offramp-sdk-v1.0.1
|
|
262
|
+
[1.0.0]: https://github.com/ADWilkinson/galleonlabs-zkp2p/releases/tag/offramp-sdk-v1.0.0
|
|
263
|
+
[0.2.1]: https://github.com/ADWilkinson/galleonlabs-zkp2p/releases/tag/offramp-sdk-v0.2.1
|
|
264
|
+
[0.2.0]: https://github.com/ADWilkinson/galleonlabs-zkp2p/releases/tag/offramp-sdk-v0.2.0
|
|
265
|
+
[0.1.5]: https://github.com/ADWilkinson/galleonlabs-zkp2p/releases/tag/offramp-sdk-v0.1.5
|
|
266
|
+
[0.1.4]: https://github.com/ADWilkinson/galleonlabs-zkp2p/releases/tag/offramp-sdk-v0.1.4
|
|
267
|
+
[0.1.3]: https://github.com/ADWilkinson/galleonlabs-zkp2p/releases/tag/offramp-sdk-v0.1.3
|
|
268
|
+
[0.1.2]: https://github.com/ADWilkinson/galleonlabs-zkp2p/releases/tag/offramp-sdk-v0.1.2
|
|
269
|
+
[0.1.1]: https://github.com/ADWilkinson/galleonlabs-zkp2p/releases/tag/offramp-sdk-v0.1.1
|
|
270
|
+
[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
|
-
|
|
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: "
|
|
194
|
-
placeholder: "
|
|
195
|
-
helperText: "
|
|
196
|
-
validation: z.string().
|
|
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,36 @@ function getPaymentMethodHashes(platform) {
|
|
|
293
322
|
}
|
|
294
323
|
return Array.from(hashes);
|
|
295
324
|
}
|
|
296
|
-
function buildDepositData(platform, identifier) {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
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
|
+
if (statusCode === 400) return true;
|
|
338
|
+
const normalized = (message ?? "").trim();
|
|
339
|
+
if (!normalized) return false;
|
|
340
|
+
const lower = normalized.toLowerCase();
|
|
341
|
+
return EXTENSION_REGISTRATION_ERROR_PATTERNS.some((p) => lower.includes(p));
|
|
321
342
|
}
|
|
322
343
|
|
|
323
344
|
// src/errors.ts
|
|
345
|
+
var MakersCreateError = class extends Error {
|
|
346
|
+
status;
|
|
347
|
+
body;
|
|
348
|
+
constructor(message, status, body) {
|
|
349
|
+
super(message);
|
|
350
|
+
this.name = "MakersCreateError";
|
|
351
|
+
this.status = status;
|
|
352
|
+
this.body = body;
|
|
353
|
+
}
|
|
354
|
+
};
|
|
324
355
|
var OfframpError = class extends Error {
|
|
325
356
|
code;
|
|
326
357
|
step;
|
|
@@ -1122,23 +1153,39 @@ function extractDepositIdFromLogs(logs) {
|
|
|
1122
1153
|
}
|
|
1123
1154
|
return null;
|
|
1124
1155
|
}
|
|
1125
|
-
async function registerPayeeDetails(processorName,
|
|
1156
|
+
async function registerPayeeDetails(processorName, payload) {
|
|
1126
1157
|
const controller = new AbortController();
|
|
1127
1158
|
const timeout = setTimeout(() => controller.abort(), PAYEE_REGISTRATION_TIMEOUT_MS);
|
|
1128
1159
|
try {
|
|
1129
|
-
const res = await fetch(`${API_BASE_URL}/
|
|
1160
|
+
const res = await fetch(`${API_BASE_URL}/v2/makers/create`, {
|
|
1130
1161
|
method: "POST",
|
|
1131
1162
|
headers: { "Content-Type": "application/json" },
|
|
1132
|
-
body: JSON.stringify({ processorName,
|
|
1163
|
+
body: JSON.stringify({ processorName, ...payload }),
|
|
1133
1164
|
signal: controller.signal
|
|
1134
1165
|
});
|
|
1135
1166
|
if (!res.ok) {
|
|
1136
1167
|
const txt = await res.text().catch(() => "");
|
|
1137
|
-
|
|
1168
|
+
let detail = txt || res.statusText;
|
|
1169
|
+
try {
|
|
1170
|
+
const parsed = JSON.parse(txt);
|
|
1171
|
+
if (typeof parsed.message === "string" && parsed.message.trim()) {
|
|
1172
|
+
detail = parsed.message;
|
|
1173
|
+
}
|
|
1174
|
+
} catch {
|
|
1175
|
+
}
|
|
1176
|
+
throw new MakersCreateError(
|
|
1177
|
+
`makers/create failed (${res.status}): ${detail}`,
|
|
1178
|
+
res.status,
|
|
1179
|
+
txt
|
|
1180
|
+
);
|
|
1138
1181
|
}
|
|
1139
1182
|
const json = await res.json();
|
|
1140
1183
|
if (!json.success || !json.responseObject?.hashedOnchainId) {
|
|
1141
|
-
throw new
|
|
1184
|
+
throw new MakersCreateError(
|
|
1185
|
+
json.message || "makers/create returned no hashedOnchainId",
|
|
1186
|
+
json.statusCode ?? null,
|
|
1187
|
+
""
|
|
1188
|
+
);
|
|
1142
1189
|
}
|
|
1143
1190
|
return json.responseObject.hashedOnchainId;
|
|
1144
1191
|
} finally {
|
|
@@ -1340,11 +1387,21 @@ async function offramp(walletClient, params, onProgress, telemetryContext) {
|
|
|
1340
1387
|
}
|
|
1341
1388
|
emitProgress({ step: "registering" });
|
|
1342
1389
|
let hashedOnchainId;
|
|
1390
|
+
const canonicalName = platformId.startsWith("zelle") ? "zelle" : platformId;
|
|
1343
1391
|
try {
|
|
1344
|
-
const
|
|
1345
|
-
|
|
1346
|
-
hashedOnchainId = await registerPayeeDetails(canonicalName, depositData);
|
|
1392
|
+
const payeePayload = buildDepositData(canonicalName, normalizedIdentifier);
|
|
1393
|
+
hashedOnchainId = await registerPayeeDetails(canonicalName, payeePayload);
|
|
1347
1394
|
} catch (err) {
|
|
1395
|
+
const status = err instanceof MakersCreateError ? err.status : null;
|
|
1396
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1397
|
+
if (isPeerExtensionRegistrationError(canonicalName, message, status)) {
|
|
1398
|
+
throw new OfframpError(
|
|
1399
|
+
`${platform.name} maker is not yet registered in the Peer extension. Verify this handle in the extension, then retry.`,
|
|
1400
|
+
"EXTENSION_REGISTRATION_REQUIRED",
|
|
1401
|
+
"registering",
|
|
1402
|
+
err
|
|
1403
|
+
);
|
|
1404
|
+
}
|
|
1348
1405
|
throw new OfframpError(
|
|
1349
1406
|
"Payee registration failed",
|
|
1350
1407
|
"REGISTRATION_FAILED",
|
|
@@ -1677,9 +1734,23 @@ function createOfframp(options) {
|
|
|
1677
1734
|
return new Offramp(options);
|
|
1678
1735
|
}
|
|
1679
1736
|
|
|
1737
|
+
// src/extension.ts
|
|
1738
|
+
import {
|
|
1739
|
+
PEER_EXTENSION_CHROME_URL,
|
|
1740
|
+
peerExtensionSdk,
|
|
1741
|
+
createPeerExtensionSdk,
|
|
1742
|
+
isPeerExtensionAvailable,
|
|
1743
|
+
openPeerExtensionInstallPage,
|
|
1744
|
+
getPeerExtensionState
|
|
1745
|
+
} from "@zkp2p/sdk";
|
|
1746
|
+
|
|
1680
1747
|
export {
|
|
1681
1748
|
ESCROW_ADDRESS,
|
|
1749
|
+
normalizePaypalMeUsername,
|
|
1682
1750
|
PLATFORMS,
|
|
1751
|
+
getPeerExtensionRegistrationInfo,
|
|
1752
|
+
isPeerExtensionRegistrationError,
|
|
1753
|
+
MakersCreateError,
|
|
1683
1754
|
OfframpError,
|
|
1684
1755
|
enableOtc,
|
|
1685
1756
|
disableOtc,
|
|
@@ -1695,6 +1766,12 @@ export {
|
|
|
1695
1766
|
offramp,
|
|
1696
1767
|
CURRENCIES,
|
|
1697
1768
|
Offramp,
|
|
1698
|
-
createOfframp
|
|
1769
|
+
createOfframp,
|
|
1770
|
+
PEER_EXTENSION_CHROME_URL,
|
|
1771
|
+
peerExtensionSdk,
|
|
1772
|
+
createPeerExtensionSdk,
|
|
1773
|
+
isPeerExtensionAvailable,
|
|
1774
|
+
openPeerExtensionInstallPage,
|
|
1775
|
+
getPeerExtensionState
|
|
1699
1776
|
};
|
|
1700
|
-
//# sourceMappingURL=chunk-
|
|
1777
|
+
//# sourceMappingURL=chunk-747S3WWB.js.map
|