@usdctofiat/offramp 1.0.0 → 1.1.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.
@@ -18,7 +18,10 @@ var addresses = contracts.addresses;
18
18
  var addrs = addresses;
19
19
  var USDC_ADDRESS = addrs.token || "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913";
20
20
  var ESCROW_ADDRESS = addrs.escrowV2 || addrs.escrow || "";
21
- var GATING_SERVICE_ADDRESS = getGatingServiceAddress(BASE_CHAIN_ID, RUNTIME_ENV);
21
+ var GATING_SERVICE_ADDRESS = getGatingServiceAddress(
22
+ BASE_CHAIN_ID,
23
+ RUNTIME_ENV
24
+ );
22
25
  var DELEGATE_RATE_MANAGER_ID = "0x8666d6fb0f6797c56e95339fd7ca82fdd348b9db200e10a4c4aa0a0b879fc41c";
23
26
  var RATE_MANAGER_REGISTRY_ADDRESS = "0xeed7db23e724ac4590d6db6f78fda6db203535f3";
24
27
  var REFERRER = "galleonlabs";
@@ -36,8 +39,64 @@ var FALLBACK_CURRENCIES = {
36
39
  venmo: ["USD"],
37
40
  cashapp: ["USD"],
38
41
  chime: ["USD"],
39
- revolut: ["USD", "EUR", "GBP", "SGD", "NZD", "AUD", "CAD", "HKD", "MXN", "SAR", "AED", "THB", "TRY", "PLN", "CHF", "ZAR", "CZK", "CNY", "DKK", "HUF", "NOK", "RON", "SEK"],
40
- wise: ["USD", "CNY", "EUR", "GBP", "AUD", "NZD", "CAD", "AED", "CHF", "ZAR", "SGD", "ILS", "HKD", "JPY", "PLN", "TRY", "IDR", "KES", "MYR", "MXN", "THB", "VND", "UGX", "CZK", "DKK", "HUF", "INR", "NOK", "PHP", "RON", "SEK"],
42
+ revolut: [
43
+ "USD",
44
+ "EUR",
45
+ "GBP",
46
+ "SGD",
47
+ "NZD",
48
+ "AUD",
49
+ "CAD",
50
+ "HKD",
51
+ "MXN",
52
+ "SAR",
53
+ "AED",
54
+ "THB",
55
+ "TRY",
56
+ "PLN",
57
+ "CHF",
58
+ "ZAR",
59
+ "CZK",
60
+ "CNY",
61
+ "DKK",
62
+ "HUF",
63
+ "NOK",
64
+ "RON",
65
+ "SEK"
66
+ ],
67
+ wise: [
68
+ "USD",
69
+ "CNY",
70
+ "EUR",
71
+ "GBP",
72
+ "AUD",
73
+ "NZD",
74
+ "CAD",
75
+ "AED",
76
+ "CHF",
77
+ "ZAR",
78
+ "SGD",
79
+ "ILS",
80
+ "HKD",
81
+ "JPY",
82
+ "PLN",
83
+ "TRY",
84
+ "IDR",
85
+ "KES",
86
+ "MYR",
87
+ "MXN",
88
+ "THB",
89
+ "VND",
90
+ "UGX",
91
+ "CZK",
92
+ "DKK",
93
+ "HUF",
94
+ "INR",
95
+ "NOK",
96
+ "PHP",
97
+ "RON",
98
+ "SEK"
99
+ ],
41
100
  mercadopago: ["ARS"],
42
101
  zelle: ["USD"],
43
102
  paypal: ["USD", "EUR", "GBP", "SGD", "NZD", "AUD", "CAD"],
@@ -64,19 +123,95 @@ function resolveSupportedCurrencies(platform) {
64
123
  return Array.from(codes).sort();
65
124
  }
66
125
  var BLUEPRINTS = {
67
- venmo: { id: "venmo", name: "Venmo", identifierLabel: "Username", placeholder: "venmo username (no @)", helperText: "Username without @ (publicly discoverable)", validation: z.string().min(1).regex(/^[a-zA-Z0-9_-]+$/), transform: (v) => v.replace(/^@+/, "") },
68
- cashapp: { id: "cashapp", name: "Cash App", identifierLabel: "Cashtag", placeholder: "cashtag (no $)", helperText: "Cashtag without $ (publicly discoverable)", validation: z.string().min(1).regex(/^[a-zA-Z0-9]+$/), transform: (v) => v.replace(/^\$+/, "") },
69
- chime: { id: "chime", name: "Chime", identifierLabel: "ChimeSign", placeholder: "$chimesign", helperText: "ChimeSign with $ (must be discoverable)", validation: z.string().min(2).regex(/^\$[a-zA-Z0-9]+$/), transform: (v) => {
70
- const t = v.trim().toLowerCase();
71
- return t.startsWith("$") ? t : `$${t}`;
72
- } },
73
- revolut: { id: "revolut", name: "Revolut", identifierLabel: "Revtag", placeholder: "revtag (no @)", helperText: "Revtag without @ (must be public)", validation: z.string().min(1).regex(/^[a-zA-Z0-9]+$/), transform: (v) => v.replace(/^@+/, "").trim() },
74
- wise: { id: "wise", name: "Wise", identifierLabel: "Wisetag", placeholder: "wisetag (no @)", helperText: "Your Wise @wisetag (no @)", validation: z.string().min(1).regex(/^[a-zA-Z0-9_-]+$/), transform: (v) => v.replace(/^@+/, "").trim() },
75
- mercadopago: { id: "mercadopago", name: "Mercado Pago", identifierLabel: "CVU", placeholder: "22-digit CVU", helperText: "CVU must be exactly 22 digits", validation: z.string().length(22).regex(/^\d{22}$/) },
76
- zelle: { id: "zelle", name: "Zelle", identifierLabel: "Email", placeholder: "email", helperText: "Registered Zelle email", validation: z.string().email() },
77
- paypal: { id: "paypal", name: "PayPal", identifierLabel: "Email", placeholder: "email", helperText: "Email linked to PayPal account", validation: z.string().email() },
78
- monzo: { id: "monzo", name: "Monzo", identifierLabel: "Username", placeholder: "monzo.me username", helperText: "Your Monzo.me username", validation: z.string().min(1).regex(/^[a-zA-Z0-9_-]+$/) },
79
- n26: { id: "n26", name: "N26", identifierLabel: "IBAN", placeholder: "IBAN (e.g. DE89...)", helperText: "Your IBAN (spaces will be removed)", validation: z.string().min(15).max(34).regex(/^[A-Z]{2}[0-9]{2}[A-Z0-9]+$/i), transform: (v) => v.replace(/\s/g, "").toUpperCase() }
126
+ venmo: {
127
+ id: "venmo",
128
+ name: "Venmo",
129
+ identifierLabel: "Username",
130
+ placeholder: "venmo username (no @)",
131
+ helperText: "Username without @ (publicly discoverable)",
132
+ validation: z.string().min(1).regex(/^[a-zA-Z0-9_-]+$/),
133
+ transform: (v) => v.replace(/^@+/, "")
134
+ },
135
+ cashapp: {
136
+ id: "cashapp",
137
+ name: "Cash App",
138
+ identifierLabel: "Cashtag",
139
+ placeholder: "cashtag (no $)",
140
+ helperText: "Cashtag without $ (publicly discoverable)",
141
+ validation: z.string().min(1).regex(/^[a-zA-Z0-9]+$/),
142
+ transform: (v) => v.replace(/^\$+/, "")
143
+ },
144
+ chime: {
145
+ id: "chime",
146
+ name: "Chime",
147
+ identifierLabel: "ChimeSign",
148
+ placeholder: "$chimesign",
149
+ helperText: "ChimeSign with $ (must be discoverable)",
150
+ validation: z.string().min(2).regex(/^\$[a-zA-Z0-9]+$/),
151
+ transform: (v) => {
152
+ const t = v.trim().toLowerCase();
153
+ return t.startsWith("$") ? t : `$${t}`;
154
+ }
155
+ },
156
+ revolut: {
157
+ id: "revolut",
158
+ name: "Revolut",
159
+ identifierLabel: "Revtag",
160
+ placeholder: "revtag (no @)",
161
+ helperText: "Revtag without @ (must be public)",
162
+ validation: z.string().min(1).regex(/^[a-zA-Z0-9]+$/),
163
+ transform: (v) => v.replace(/^@+/, "").trim()
164
+ },
165
+ wise: {
166
+ id: "wise",
167
+ name: "Wise",
168
+ identifierLabel: "Wisetag",
169
+ placeholder: "wisetag (no @)",
170
+ helperText: "Your Wise @wisetag (no @)",
171
+ validation: z.string().min(1).regex(/^[a-zA-Z0-9_-]+$/),
172
+ transform: (v) => v.replace(/^@+/, "").trim()
173
+ },
174
+ mercadopago: {
175
+ id: "mercadopago",
176
+ name: "Mercado Pago",
177
+ identifierLabel: "CVU",
178
+ placeholder: "22-digit CVU",
179
+ helperText: "CVU must be exactly 22 digits",
180
+ validation: z.string().length(22).regex(/^\d{22}$/)
181
+ },
182
+ zelle: {
183
+ id: "zelle",
184
+ name: "Zelle",
185
+ identifierLabel: "Email",
186
+ placeholder: "email",
187
+ helperText: "Registered Zelle email",
188
+ validation: z.string().email()
189
+ },
190
+ paypal: {
191
+ id: "paypal",
192
+ name: "PayPal",
193
+ identifierLabel: "Email",
194
+ placeholder: "email",
195
+ helperText: "Email linked to PayPal account",
196
+ validation: z.string().email()
197
+ },
198
+ monzo: {
199
+ id: "monzo",
200
+ name: "Monzo",
201
+ identifierLabel: "Username",
202
+ placeholder: "monzo.me username",
203
+ helperText: "Your Monzo.me username",
204
+ validation: z.string().min(1).regex(/^[a-zA-Z0-9_-]+$/)
205
+ },
206
+ n26: {
207
+ id: "n26",
208
+ name: "N26",
209
+ identifierLabel: "IBAN",
210
+ placeholder: "IBAN (e.g. DE89...)",
211
+ helperText: "Your IBAN (spaces will be removed)",
212
+ validation: z.string().min(15).max(34).regex(/^[A-Z]{2}[0-9]{2}[A-Z0-9]+$/i),
213
+ transform: (v) => v.replace(/\s/g, "").toUpperCase()
214
+ }
80
215
  };
81
216
  function buildPlatformEntry(bp) {
82
217
  const currencies = resolveSupportedCurrencies(bp.id);
@@ -93,7 +228,11 @@ function buildPlatformEntry(bp) {
93
228
  const transformed = bp.transform ? bp.transform(input) : input;
94
229
  const result = bp.validation.safeParse(transformed);
95
230
  if (!result.success) {
96
- return { valid: false, normalized: transformed, error: result.error.issues[0]?.message || "Invalid input" };
231
+ return {
232
+ valid: false,
233
+ normalized: transformed,
234
+ error: result.error.issues[0]?.message || "Invalid input"
235
+ };
97
236
  }
98
237
  return { valid: true, normalized: transformed };
99
238
  }
@@ -204,18 +343,459 @@ function isUserCancellation(error) {
204
343
  return msg.includes("user rejected") || msg.includes("user denied") || msg.includes("user cancelled") || msg.includes("rejected the request") || msg.includes("action_rejected");
205
344
  }
206
345
 
207
- // src/queries.ts
208
- import { formatUnits as formatUnits2 } from "viem";
209
- import { classifyDelegationState, defaultIndexerEndpoint, getCurrencyInfoFromHash as getCurrencyInfoFromHash2, IndexerClient, IndexerDepositService } from "@zkp2p/sdk";
346
+ // src/otc.ts
347
+ import { isAddress, zeroAddress } from "viem";
348
+
349
+ // ../../node_modules/@zkp2p/contracts-v2/abis/base/WhitelistPreIntentHook.json
350
+ var WhitelistPreIntentHook_default = [
351
+ {
352
+ inputs: [
353
+ {
354
+ internalType: "address",
355
+ name: "_orchestratorRegistry",
356
+ type: "address"
357
+ }
358
+ ],
359
+ stateMutability: "nonpayable",
360
+ type: "constructor"
361
+ },
362
+ {
363
+ inputs: [],
364
+ name: "EmptyArray",
365
+ type: "error"
366
+ },
367
+ {
368
+ inputs: [
369
+ {
370
+ internalType: "address",
371
+ name: "taker",
372
+ type: "address"
373
+ },
374
+ {
375
+ internalType: "address",
376
+ name: "escrow",
377
+ type: "address"
378
+ },
379
+ {
380
+ internalType: "uint256",
381
+ name: "depositId",
382
+ type: "uint256"
383
+ }
384
+ ],
385
+ name: "TakerNotInWhitelist",
386
+ type: "error"
387
+ },
388
+ {
389
+ inputs: [
390
+ {
391
+ internalType: "address",
392
+ name: "taker",
393
+ type: "address"
394
+ },
395
+ {
396
+ internalType: "address",
397
+ name: "escrow",
398
+ type: "address"
399
+ },
400
+ {
401
+ internalType: "uint256",
402
+ name: "depositId",
403
+ type: "uint256"
404
+ }
405
+ ],
406
+ name: "TakerNotWhitelisted",
407
+ type: "error"
408
+ },
409
+ {
410
+ inputs: [
411
+ {
412
+ internalType: "address",
413
+ name: "caller",
414
+ type: "address"
415
+ },
416
+ {
417
+ internalType: "address",
418
+ name: "owner",
419
+ type: "address"
420
+ },
421
+ {
422
+ internalType: "address",
423
+ name: "delegate",
424
+ type: "address"
425
+ }
426
+ ],
427
+ name: "UnauthorizedCallerOrDelegate",
428
+ type: "error"
429
+ },
430
+ {
431
+ inputs: [
432
+ {
433
+ internalType: "address",
434
+ name: "caller",
435
+ type: "address"
436
+ }
437
+ ],
438
+ name: "UnauthorizedOrchestratorCaller",
439
+ type: "error"
440
+ },
441
+ {
442
+ inputs: [],
443
+ name: "ZeroAddress",
444
+ type: "error"
445
+ },
446
+ {
447
+ anonymous: false,
448
+ inputs: [
449
+ {
450
+ indexed: true,
451
+ internalType: "address",
452
+ name: "escrow",
453
+ type: "address"
454
+ },
455
+ {
456
+ indexed: true,
457
+ internalType: "uint256",
458
+ name: "depositId",
459
+ type: "uint256"
460
+ },
461
+ {
462
+ indexed: true,
463
+ internalType: "address",
464
+ name: "taker",
465
+ type: "address"
466
+ }
467
+ ],
468
+ name: "TakerRemovedFromWhitelist",
469
+ type: "event"
470
+ },
471
+ {
472
+ anonymous: false,
473
+ inputs: [
474
+ {
475
+ indexed: true,
476
+ internalType: "address",
477
+ name: "escrow",
478
+ type: "address"
479
+ },
480
+ {
481
+ indexed: true,
482
+ internalType: "uint256",
483
+ name: "depositId",
484
+ type: "uint256"
485
+ },
486
+ {
487
+ indexed: true,
488
+ internalType: "address",
489
+ name: "taker",
490
+ type: "address"
491
+ }
492
+ ],
493
+ name: "TakerWhitelisted",
494
+ type: "event"
495
+ },
496
+ {
497
+ inputs: [
498
+ {
499
+ internalType: "address",
500
+ name: "_escrow",
501
+ type: "address"
502
+ },
503
+ {
504
+ internalType: "uint256",
505
+ name: "_depositId",
506
+ type: "uint256"
507
+ },
508
+ {
509
+ internalType: "address[]",
510
+ name: "_takers",
511
+ type: "address[]"
512
+ }
513
+ ],
514
+ name: "addToWhitelist",
515
+ outputs: [],
516
+ stateMutability: "nonpayable",
517
+ type: "function"
518
+ },
519
+ {
520
+ inputs: [
521
+ {
522
+ internalType: "address",
523
+ name: "_escrow",
524
+ type: "address"
525
+ },
526
+ {
527
+ internalType: "uint256",
528
+ name: "_depositId",
529
+ type: "uint256"
530
+ },
531
+ {
532
+ internalType: "address",
533
+ name: "_taker",
534
+ type: "address"
535
+ }
536
+ ],
537
+ name: "isWhitelisted",
538
+ outputs: [
539
+ {
540
+ internalType: "bool",
541
+ name: "",
542
+ type: "bool"
543
+ }
544
+ ],
545
+ stateMutability: "view",
546
+ type: "function"
547
+ },
548
+ {
549
+ inputs: [],
550
+ name: "orchestratorRegistry",
551
+ outputs: [
552
+ {
553
+ internalType: "contract IOrchestratorRegistry",
554
+ name: "",
555
+ type: "address"
556
+ }
557
+ ],
558
+ stateMutability: "view",
559
+ type: "function"
560
+ },
561
+ {
562
+ inputs: [
563
+ {
564
+ internalType: "address",
565
+ name: "_escrow",
566
+ type: "address"
567
+ },
568
+ {
569
+ internalType: "uint256",
570
+ name: "_depositId",
571
+ type: "uint256"
572
+ },
573
+ {
574
+ internalType: "address[]",
575
+ name: "_takers",
576
+ type: "address[]"
577
+ }
578
+ ],
579
+ name: "removeFromWhitelist",
580
+ outputs: [],
581
+ stateMutability: "nonpayable",
582
+ type: "function"
583
+ },
584
+ {
585
+ inputs: [
586
+ {
587
+ components: [
588
+ {
589
+ internalType: "address",
590
+ name: "taker",
591
+ type: "address"
592
+ },
593
+ {
594
+ internalType: "address",
595
+ name: "escrow",
596
+ type: "address"
597
+ },
598
+ {
599
+ internalType: "uint256",
600
+ name: "depositId",
601
+ type: "uint256"
602
+ },
603
+ {
604
+ internalType: "uint256",
605
+ name: "amount",
606
+ type: "uint256"
607
+ },
608
+ {
609
+ internalType: "address",
610
+ name: "to",
611
+ type: "address"
612
+ },
613
+ {
614
+ internalType: "bytes32",
615
+ name: "paymentMethod",
616
+ type: "bytes32"
617
+ },
618
+ {
619
+ internalType: "bytes32",
620
+ name: "fiatCurrency",
621
+ type: "bytes32"
622
+ },
623
+ {
624
+ internalType: "uint256",
625
+ name: "conversionRate",
626
+ type: "uint256"
627
+ },
628
+ {
629
+ internalType: "address",
630
+ name: "referrer",
631
+ type: "address"
632
+ },
633
+ {
634
+ internalType: "uint256",
635
+ name: "referrerFee",
636
+ type: "uint256"
637
+ },
638
+ {
639
+ internalType: "bytes",
640
+ name: "preIntentHookData",
641
+ type: "bytes"
642
+ }
643
+ ],
644
+ internalType: "struct IPreIntentHook.PreIntentContext",
645
+ name: "_ctx",
646
+ type: "tuple"
647
+ }
648
+ ],
649
+ name: "validateSignalIntent",
650
+ outputs: [],
651
+ stateMutability: "view",
652
+ type: "function"
653
+ },
654
+ {
655
+ inputs: [
656
+ {
657
+ internalType: "address",
658
+ name: "",
659
+ type: "address"
660
+ },
661
+ {
662
+ internalType: "uint256",
663
+ name: "",
664
+ type: "uint256"
665
+ },
666
+ {
667
+ internalType: "address",
668
+ name: "",
669
+ type: "address"
670
+ }
671
+ ],
672
+ name: "whitelist",
673
+ outputs: [
674
+ {
675
+ internalType: "bool",
676
+ name: "",
677
+ type: "bool"
678
+ }
679
+ ],
680
+ stateMutability: "view",
681
+ type: "function"
682
+ }
683
+ ];
210
684
 
211
685
  // src/deposit.ts
212
- import { createPublicClient, decodeEventLog, formatUnits, http, parseUnits } from "viem";
686
+ import { createPublicClient, decodeEventLog, formatUnits as formatUnits2, http, parseUnits } from "viem";
213
687
  import { base } from "viem/chains";
214
688
  import {
215
689
  OfframpClient,
216
690
  getSpreadOracleConfig,
217
691
  mapConversionRatesToOnchainMinRate
218
692
  } from "@zkp2p/sdk";
693
+
694
+ // src/queries.ts
695
+ import { formatUnits } from "viem";
696
+ import {
697
+ classifyDelegationState,
698
+ defaultIndexerEndpoint,
699
+ getCurrencyInfoFromHash as getCurrencyInfoFromHash2,
700
+ IndexerClient,
701
+ IndexerDepositService
702
+ } from "@zkp2p/sdk";
703
+ var indexerService = null;
704
+ function getIndexerService() {
705
+ if (!indexerService) {
706
+ const endpoint = defaultIndexerEndpoint("PRODUCTION");
707
+ const client = new IndexerClient(endpoint);
708
+ indexerService = new IndexerDepositService(client);
709
+ }
710
+ return indexerService;
711
+ }
712
+ function toBigInt(value) {
713
+ try {
714
+ return BigInt(value || "0");
715
+ } catch {
716
+ return 0n;
717
+ }
718
+ }
719
+ function toUsdc(value) {
720
+ return Number(formatUnits(toBigInt(value), 6));
721
+ }
722
+ function resolveStatus(deposit) {
723
+ if (deposit.status === "CLOSED") return "closed";
724
+ if (toBigInt(deposit.remainingDeposits) === 0n) return "empty";
725
+ return "active";
726
+ }
727
+ function resolveMethodNames(hashes) {
728
+ const names = /* @__PURE__ */ new Set();
729
+ for (const { paymentMethodHash } of hashes) {
730
+ const normalized = paymentMethodHash.toLowerCase();
731
+ for (const platform of Object.values(PLATFORMS)) {
732
+ const platformHashes = getPaymentMethodHashes(platform.id);
733
+ if (platformHashes.some((h) => h.toLowerCase() === normalized)) {
734
+ names.add(platform.name);
735
+ break;
736
+ }
737
+ }
738
+ }
739
+ return Array.from(names);
740
+ }
741
+ function mapDeposit(d) {
742
+ const delegationState = classifyDelegationState(
743
+ d.rateManagerId ?? void 0,
744
+ d.rateManagerAddress ?? void 0,
745
+ DELEGATE_RATE_MANAGER_ID,
746
+ RATE_MANAGER_REGISTRY_ADDRESS
747
+ );
748
+ return {
749
+ depositId: d.depositId,
750
+ compositeId: d.id,
751
+ txHash: d.txHash,
752
+ status: resolveStatus(d),
753
+ remainingUsdc: toUsdc(d.remainingDeposits),
754
+ outstandingUsdc: toUsdc(d.outstandingIntentAmount),
755
+ totalTakenUsdc: toUsdc(d.totalAmountTaken),
756
+ fulfilledIntents: d.fulfilledIntents ?? 0,
757
+ paymentMethods: resolveMethodNames(d.paymentMethods),
758
+ currencies: d.currencies.map((c) => {
759
+ const info = getCurrencyInfoFromHash2(c.currencyCode);
760
+ return info?.currencyCode ?? c.currencyCode;
761
+ }),
762
+ rateSource: d.currencies[0]?.rateSource || "unknown",
763
+ delegated: delegationState === "delegated_here",
764
+ escrowAddress: d.escrowAddress
765
+ };
766
+ }
767
+ function extractTxHash(result) {
768
+ if (typeof result === "string") return result;
769
+ if (result && typeof result === "object" && "hash" in result)
770
+ return result.hash;
771
+ if (result && typeof result === "object" && "transactionHash" in result)
772
+ return result.transactionHash;
773
+ throw new Error("Unexpected transaction result format");
774
+ }
775
+ async function deposits(walletAddress) {
776
+ const service = getIndexerService();
777
+ const raw = await service.fetchDepositsWithRelations(
778
+ { depositor: walletAddress },
779
+ { limit: 100 }
780
+ );
781
+ const statusOrder = { active: 0, empty: 1, closed: 2 };
782
+ return (raw || []).map(mapDeposit).sort((a, b) => {
783
+ const diff = statusOrder[a.status] - statusOrder[b.status];
784
+ if (diff !== 0) return diff;
785
+ return Number(BigInt(b.depositId) - BigInt(a.depositId));
786
+ });
787
+ }
788
+ async function close(walletClient, depositId, escrowAddress) {
789
+ const client = createSdkClient(walletClient);
790
+ const result = await client.withdrawDeposit({
791
+ depositId: BigInt(depositId),
792
+ escrowAddress: escrowAddress || ESCROW_ADDRESS,
793
+ txOverrides: { referrer: [REFERRER] }
794
+ });
795
+ return extractTxHash(result);
796
+ }
797
+
798
+ // src/deposit.ts
219
799
  function usdcToUnits(amount) {
220
800
  return parseUnits(amount, 6);
221
801
  }
@@ -228,10 +808,15 @@ var DEPOSIT_RECEIVED_ABI = [
228
808
  { indexed: true, name: "depositor", type: "address" },
229
809
  { indexed: true, name: "token", type: "address" },
230
810
  { indexed: false, name: "amount", type: "uint256" },
231
- { indexed: false, name: "intentAmountRange", type: "tuple", components: [
232
- { name: "min", type: "uint256" },
233
- { name: "max", type: "uint256" }
234
- ] },
811
+ {
812
+ indexed: false,
813
+ name: "intentAmountRange",
814
+ type: "tuple",
815
+ components: [
816
+ { name: "min", type: "uint256" },
817
+ { name: "max", type: "uint256" }
818
+ ]
819
+ },
235
820
  { indexed: false, name: "delegate", type: "address" },
236
821
  { indexed: false, name: "intentGuardian", type: "address" }
237
822
  ]
@@ -297,10 +882,12 @@ function attachOracleConfig(entries, conversionRates) {
297
882
  })
298
883
  );
299
884
  }
300
- function extractTxHash(result) {
885
+ function extractTxHash2(result) {
301
886
  if (typeof result === "string") return result;
302
- if (result && typeof result === "object" && "hash" in result) return result.hash;
303
- if (result && typeof result === "object" && "transactionHash" in result) return result.transactionHash;
887
+ if (result && typeof result === "object" && "hash" in result)
888
+ return result.hash;
889
+ if (result && typeof result === "object" && "transactionHash" in result)
890
+ return result.transactionHash;
304
891
  throw new Error("Unexpected transaction result format");
305
892
  }
306
893
  function createSdkClient(walletClient) {
@@ -320,7 +907,7 @@ async function findUndelegatedDeposit(walletAddress) {
320
907
  { limit: 50 }
321
908
  );
322
909
  const escrowLower = ESCROW_ADDRESS.toLowerCase();
323
- return (raw || []).find((d) => {
910
+ const undelegated = (raw || []).filter((d) => {
324
911
  if (d.status === "CLOSED") return false;
325
912
  if (d.escrowAddress.toLowerCase() !== escrowLower) return false;
326
913
  const remaining = (() => {
@@ -331,9 +918,10 @@ async function findUndelegatedDeposit(walletAddress) {
331
918
  }
332
919
  })();
333
920
  if (remaining === 0n) return false;
334
- const hasOurVault = d.rateManagerId === DELEGATE_RATE_MANAGER_ID;
335
- return !hasOurVault;
336
- }) ?? null;
921
+ return d.rateManagerId !== DELEGATE_RATE_MANAGER_ID;
922
+ });
923
+ undelegated.sort((a, b) => Number(BigInt(b.depositId) - BigInt(a.depositId)));
924
+ return undelegated[0] ?? null;
337
925
  } catch {
338
926
  return null;
339
927
  }
@@ -375,11 +963,12 @@ async function offramp(walletClient, params, onProgress) {
375
963
  escrowAddress: existing.escrowAddress,
376
964
  txOverrides: txOverrides2
377
965
  });
378
- const txHash = extractTxHash(result);
966
+ const txHash = extractTxHash2(result);
379
967
  onProgress?.({ step: "done", txHash, depositId: existing.depositId });
380
968
  return { depositId: existing.depositId, txHash, resumed: true };
381
969
  } catch (err) {
382
- if (isUserCancellation(err)) throw new OfframpError("User cancelled", "USER_CANCELLED", "resuming", err);
970
+ if (isUserCancellation(err))
971
+ throw new OfframpError("User cancelled", "USER_CANCELLED", "resuming", err);
383
972
  throw new OfframpError(
384
973
  "Found undelegated deposit but delegation failed. Try again.",
385
974
  "DELEGATION_FAILED",
@@ -397,13 +986,24 @@ async function offramp(walletClient, params, onProgress) {
397
986
  const publicClient = createPublicClient({ chain: base, transport: http(BASE_RPC_URL) });
398
987
  const balance = await publicClient.readContract({
399
988
  address: USDC_ADDRESS,
400
- abi: [{ name: "balanceOf", type: "function", stateMutability: "view", inputs: [{ name: "account", type: "address" }], outputs: [{ name: "", type: "uint256" }] }],
989
+ abi: [
990
+ {
991
+ name: "balanceOf",
992
+ type: "function",
993
+ stateMutability: "view",
994
+ inputs: [{ name: "account", type: "address" }],
995
+ outputs: [{ name: "", type: "uint256" }]
996
+ }
997
+ ],
401
998
  functionName: "balanceOf",
402
999
  args: [walletAddress]
403
1000
  });
404
1001
  if (balance < amountUnits) {
405
- const available = Number(formatUnits(balance, 6));
406
- throw new OfframpError(`Insufficient USDC balance. Have ${available.toFixed(2)}, need ${truncatedAmount}.`, "VALIDATION");
1002
+ const available = Number(formatUnits2(balance, 6));
1003
+ throw new OfframpError(
1004
+ `Insufficient USDC balance. Have ${available.toFixed(2)}, need ${truncatedAmount}.`,
1005
+ "VALIDATION"
1006
+ );
407
1007
  }
408
1008
  } catch (err) {
409
1009
  if (err instanceof OfframpError) throw err;
@@ -420,7 +1020,8 @@ async function offramp(walletClient, params, onProgress) {
420
1020
  txOverrides
421
1021
  });
422
1022
  } catch (err) {
423
- if (isUserCancellation(err)) throw new OfframpError("User cancelled", "USER_CANCELLED", "approving", err);
1023
+ if (isUserCancellation(err))
1024
+ throw new OfframpError("User cancelled", "USER_CANCELLED", "approving", err);
424
1025
  const detail = err instanceof Error ? err.message : String(err);
425
1026
  throw new OfframpError(`USDC approval failed: ${detail}`, "APPROVAL_FAILED", "approving", err);
426
1027
  }
@@ -450,11 +1051,13 @@ async function offramp(walletClient, params, onProgress) {
450
1051
  depositData: [buildDepositData(platformId, normalizedIdentifier)],
451
1052
  conversionRates,
452
1053
  paymentMethodsOverride: [methodHash],
453
- paymentMethodDataOverride: [{
454
- intentGatingService: GATING_SERVICE_ADDRESS,
455
- payeeDetails: hashedOnchainId,
456
- data: "0x"
457
- }],
1054
+ paymentMethodDataOverride: [
1055
+ {
1056
+ intentGatingService: GATING_SERVICE_ADDRESS,
1057
+ payeeDetails: hashedOnchainId,
1058
+ data: "0x"
1059
+ }
1060
+ ],
458
1061
  currenciesOverride,
459
1062
  escrowAddress: ESCROW_ADDRESS,
460
1063
  txOverrides
@@ -462,9 +1065,15 @@ async function offramp(walletClient, params, onProgress) {
462
1065
  if (!result?.hash) throw new Error("No transaction hash returned");
463
1066
  hash = result.hash;
464
1067
  } catch (err) {
465
- if (isUserCancellation(err)) throw new OfframpError("User cancelled", "USER_CANCELLED", "depositing", err);
1068
+ if (isUserCancellation(err))
1069
+ throw new OfframpError("User cancelled", "USER_CANCELLED", "depositing", err);
466
1070
  const detail = err instanceof Error ? err.message : String(err);
467
- throw new OfframpError(`Deposit transaction failed: ${detail}`, "DEPOSIT_FAILED", "depositing", err);
1071
+ throw new OfframpError(
1072
+ `Deposit transaction failed: ${detail}`,
1073
+ "DEPOSIT_FAILED",
1074
+ "depositing",
1075
+ err
1076
+ );
468
1077
  }
469
1078
  onProgress?.({ step: "confirming", txHash: hash });
470
1079
  let depositId = "";
@@ -515,7 +1124,13 @@ async function offramp(walletClient, params, onProgress) {
515
1124
  });
516
1125
  } catch (delegationError) {
517
1126
  if (isUserCancellation(delegationError)) {
518
- throw new OfframpError("User cancelled delegation", "USER_CANCELLED", "delegating", delegationError, { txHash: hash, depositId });
1127
+ throw new OfframpError(
1128
+ "User cancelled delegation",
1129
+ "USER_CANCELLED",
1130
+ "delegating",
1131
+ delegationError,
1132
+ { txHash: hash, depositId }
1133
+ );
519
1134
  }
520
1135
  throw new OfframpError(
521
1136
  "Deposit created but delegation failed. Call offramp() again to resume delegation.",
@@ -525,109 +1140,154 @@ async function offramp(walletClient, params, onProgress) {
525
1140
  { txHash: hash, depositId }
526
1141
  );
527
1142
  }
1143
+ let otcLink;
1144
+ if (params.otcTaker) {
1145
+ onProgress?.({ step: "restricting", txHash: hash, depositId });
1146
+ try {
1147
+ const otcResult = await enableOtc(walletClient, depositId, params.otcTaker);
1148
+ otcLink = otcResult.otcLink;
1149
+ } catch (otcError) {
1150
+ if (isUserCancellation(otcError)) {
1151
+ throw new OfframpError(
1152
+ "User cancelled OTC restriction",
1153
+ "USER_CANCELLED",
1154
+ "restricting",
1155
+ otcError,
1156
+ {
1157
+ txHash: hash,
1158
+ depositId
1159
+ }
1160
+ );
1161
+ }
1162
+ throw new OfframpError(
1163
+ "Deposit created and delegated but OTC restriction failed. Use enableOtc() to retry.",
1164
+ "DEPOSIT_FAILED",
1165
+ "restricting",
1166
+ otcError,
1167
+ { txHash: hash, depositId }
1168
+ );
1169
+ }
1170
+ }
528
1171
  onProgress?.({ step: "done", txHash: hash, depositId });
529
- return { depositId, txHash: hash, resumed: false };
1172
+ return { depositId, txHash: hash, resumed: false, otcLink };
530
1173
  }
531
1174
 
532
- // src/queries.ts
533
- var indexerService = null;
534
- function getIndexerService() {
535
- if (!indexerService) {
536
- const endpoint = defaultIndexerEndpoint("PRODUCTION");
537
- const client = new IndexerClient(endpoint);
538
- indexerService = new IndexerDepositService(client);
1175
+ // src/otc.ts
1176
+ var WHITELIST_HOOK_ADDRESS = "0xd793369b11357cdd076A9c631F6c44ff8e6353eA";
1177
+ var OTC_BASE_URL = "https://usdctofiat.xyz";
1178
+ async function enableOtc(walletClient, depositId, takerAddress, escrowAddress) {
1179
+ if (!isAddress(takerAddress)) {
1180
+ throw new OfframpError("Invalid taker address", "VALIDATION");
539
1181
  }
540
- return indexerService;
541
- }
542
- function toBigInt(value) {
1182
+ const escrow = escrowAddress || ESCROW_ADDRESS;
1183
+ const taker = takerAddress;
1184
+ const depositIdBig = BigInt(depositId);
1185
+ const client = createSdkClient(walletClient);
1186
+ const txOverrides = { referrer: [REFERRER] };
543
1187
  try {
544
- return BigInt(value || "0");
545
- } catch {
546
- return 0n;
547
- }
548
- }
549
- function toUsdc(value) {
550
- return Number(formatUnits2(toBigInt(value), 6));
551
- }
552
- function resolveStatus(deposit) {
553
- if (deposit.status === "CLOSED") return "closed";
554
- if (toBigInt(deposit.remainingDeposits) === 0n) return "empty";
555
- return "active";
556
- }
557
- function resolveMethodNames(hashes) {
558
- const names = /* @__PURE__ */ new Set();
559
- for (const { paymentMethodHash } of hashes) {
560
- const normalized = paymentMethodHash.toLowerCase();
561
- for (const platform of Object.values(PLATFORMS)) {
562
- const platformHashes = getPaymentMethodHashes(platform.id);
563
- if (platformHashes.some((h) => h.toLowerCase() === normalized)) {
564
- names.add(platform.name);
565
- break;
1188
+ const currentHook = await client.getDepositWhitelistHook(depositIdBig, {
1189
+ escrowAddress: escrow
1190
+ });
1191
+ if (currentHook !== zeroAddress) {
1192
+ const isAlready = await readIsWhitelisted(
1193
+ walletClient,
1194
+ escrow,
1195
+ depositIdBig,
1196
+ taker
1197
+ );
1198
+ if (isAlready) {
1199
+ return {
1200
+ depositId,
1201
+ takerAddress: taker,
1202
+ otcLink: getOtcLink2(depositId, escrow)
1203
+ };
566
1204
  }
567
1205
  }
1206
+ await writeContract(walletClient, "addToWhitelist", [escrow, depositIdBig, [taker]]);
1207
+ if (currentHook === zeroAddress) {
1208
+ await client.setDepositWhitelistHook({
1209
+ depositId: depositIdBig,
1210
+ whitelistHook: WHITELIST_HOOK_ADDRESS,
1211
+ escrowAddress: escrow,
1212
+ txOverrides
1213
+ });
1214
+ }
1215
+ return {
1216
+ depositId,
1217
+ takerAddress: taker,
1218
+ otcLink: getOtcLink2(depositId, escrow)
1219
+ };
1220
+ } catch (err) {
1221
+ if (err instanceof OfframpError) throw err;
1222
+ if (isUserCancellation(err)) {
1223
+ throw new OfframpError("User cancelled", "USER_CANCELLED", void 0, err, { depositId });
1224
+ }
1225
+ const detail = err instanceof Error ? err.message : String(err);
1226
+ throw new OfframpError(`Failed to enable OTC: ${detail}`, "DEPOSIT_FAILED", void 0, err, {
1227
+ depositId
1228
+ });
568
1229
  }
569
- return Array.from(names);
570
1230
  }
571
- function mapDeposit(d) {
572
- const delegationState = classifyDelegationState(
573
- d.rateManagerId ?? void 0,
574
- d.rateManagerAddress ?? void 0,
575
- DELEGATE_RATE_MANAGER_ID,
576
- RATE_MANAGER_REGISTRY_ADDRESS
577
- );
578
- return {
579
- depositId: d.depositId,
580
- compositeId: d.id,
581
- txHash: d.txHash,
582
- status: resolveStatus(d),
583
- remainingUsdc: toUsdc(d.remainingDeposits),
584
- outstandingUsdc: toUsdc(d.outstandingIntentAmount),
585
- totalTakenUsdc: toUsdc(d.totalAmountTaken),
586
- fulfilledIntents: d.fulfilledIntents ?? 0,
587
- paymentMethods: resolveMethodNames(d.paymentMethods),
588
- currencies: d.currencies.map((c) => {
589
- const info = getCurrencyInfoFromHash2(c.currencyCode);
590
- return info?.currencyCode ?? c.currencyCode;
591
- }),
592
- rateSource: d.currencies[0]?.rateSource || "unknown",
593
- delegated: delegationState === "delegated_here",
594
- escrowAddress: d.escrowAddress
595
- };
1231
+ async function disableOtc(walletClient, depositId, escrowAddress) {
1232
+ const escrow = escrowAddress || ESCROW_ADDRESS;
1233
+ const client = createSdkClient(walletClient);
1234
+ const txOverrides = { referrer: [REFERRER] };
1235
+ try {
1236
+ await client.setDepositWhitelistHook({
1237
+ depositId: BigInt(depositId),
1238
+ whitelistHook: zeroAddress,
1239
+ escrowAddress: escrow,
1240
+ txOverrides
1241
+ });
1242
+ } catch (err) {
1243
+ if (isUserCancellation(err)) {
1244
+ throw new OfframpError("User cancelled", "USER_CANCELLED", void 0, err, { depositId });
1245
+ }
1246
+ const detail = err instanceof Error ? err.message : String(err);
1247
+ throw new OfframpError(`Failed to disable OTC: ${detail}`, "DEPOSIT_FAILED", void 0, err, {
1248
+ depositId
1249
+ });
1250
+ }
596
1251
  }
597
- function extractTxHash2(result) {
598
- if (typeof result === "string") return result;
599
- if (result && typeof result === "object" && "hash" in result) return result.hash;
600
- if (result && typeof result === "object" && "transactionHash" in result) return result.transactionHash;
601
- throw new Error("Unexpected transaction result format");
1252
+ function getOtcLink2(depositId, escrowAddress) {
1253
+ const escrow = escrowAddress || ESCROW_ADDRESS;
1254
+ return `${OTC_BASE_URL}/deposit/${escrow}/${depositId}`;
602
1255
  }
603
- async function deposits(walletAddress) {
604
- const service = getIndexerService();
605
- const raw = await service.fetchDepositsWithRelations(
606
- { depositor: walletAddress },
607
- { limit: 100 }
608
- );
609
- const statusOrder = { active: 0, empty: 1, closed: 2 };
610
- return (raw || []).map(mapDeposit).sort((a, b) => {
611
- const diff = statusOrder[a.status] - statusOrder[b.status];
612
- if (diff !== 0) return diff;
613
- return Number(BigInt(b.depositId) - BigInt(a.depositId));
1256
+ async function writeContract(walletClient, functionName, args) {
1257
+ const wc = walletClient;
1258
+ if (typeof wc.writeContract !== "function") {
1259
+ throw new OfframpError("Wallet client does not support writeContract", "VALIDATION");
1260
+ }
1261
+ await wc.writeContract({
1262
+ address: WHITELIST_HOOK_ADDRESS,
1263
+ abi: WhitelistPreIntentHook_default,
1264
+ functionName,
1265
+ args
614
1266
  });
615
1267
  }
616
- async function close(walletClient, depositId, escrowAddress) {
617
- const client = createSdkClient(walletClient);
618
- const result = await client.withdrawDeposit({
619
- depositId: BigInt(depositId),
620
- escrowAddress: escrowAddress || ESCROW_ADDRESS,
621
- txOverrides: { referrer: [REFERRER] }
1268
+ async function readIsWhitelisted(walletClient, escrow, depositId, taker) {
1269
+ const { createPublicClient: createPublicClient2, http: http2 } = await import("viem");
1270
+ const { base: base2 } = await import("viem/chains");
1271
+ const publicClient = createPublicClient2({
1272
+ chain: base2,
1273
+ transport: http2("https://mainnet.base.org")
1274
+ });
1275
+ return publicClient.readContract({
1276
+ address: WHITELIST_HOOK_ADDRESS,
1277
+ abi: WhitelistPreIntentHook_default,
1278
+ functionName: "isWhitelisted",
1279
+ args: [escrow, depositId, taker]
622
1280
  });
623
- return extractTxHash2(result);
624
1281
  }
625
1282
 
626
1283
  export {
627
1284
  PLATFORMS,
628
1285
  OfframpError,
1286
+ enableOtc,
1287
+ disableOtc,
1288
+ getOtcLink2 as getOtcLink,
629
1289
  deposits,
630
1290
  close,
631
1291
  offramp
632
1292
  };
633
- //# sourceMappingURL=chunk-WQCLEJWY.js.map
1293
+ //# sourceMappingURL=chunk-ZKIWUJGL.js.map