@pafi-dev/issuer 0.3.0-beta.3 → 0.3.0-beta.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -30,8 +30,6 @@ __export(index_exports, {
30
30
  IssuerApiHandlers: () => IssuerApiHandlers,
31
31
  MemoryPointLedger: () => MemoryPointLedger,
32
32
  MemorySessionStore: () => MemorySessionStore,
33
- MintingGateway: () => MintingGateway,
34
- MintingGatewayError: () => MintingGatewayError,
35
33
  NonceManager: () => NonceManager,
36
34
  PAFI_ISSUER_SDK_VERSION: () => PAFI_ISSUER_SDK_VERSION,
37
35
  PTRedeemError: () => PTRedeemError,
@@ -47,8 +45,7 @@ __export(index_exports, {
47
45
  authenticateRequest: () => authenticateRequest,
48
46
  createIssuerService: () => createIssuerService,
49
47
  createSubgraphNativeUsdtQuoter: () => createSubgraphNativeUsdtQuoter,
50
- createSubgraphPoolsProvider: () => createSubgraphPoolsProvider,
51
- encodeExtData: () => import_core4.encodeExtData
48
+ createSubgraphPoolsProvider: () => createSubgraphPoolsProvider
52
49
  });
53
50
  module.exports = __toCommonJS(index_exports);
54
51
 
@@ -636,170 +633,12 @@ var RelayError = class extends Error {
636
633
  // src/relay/relayService.ts
637
634
  var import_viem5 = require("viem");
638
635
  var import_core3 = require("@pafi-dev/core");
639
- var DEFAULT_CONFIRMATION_TIMEOUT_MS = 6e4;
640
636
  var RelayService = class {
641
- relayAddress;
642
- operatorWallet;
643
- provider;
644
- confirmationTimeoutMs;
645
- simulateBeforeSubmit;
646
- constructor(config) {
647
- if (!config.relayAddress) {
648
- throw new Error("RelayService: relayAddress is required");
649
- }
650
- if (!config.operatorWallet) {
651
- throw new Error("RelayService: operatorWallet is required");
652
- }
653
- this.relayAddress = config.relayAddress;
654
- this.operatorWallet = config.operatorWallet;
655
- if (config.provider) this.provider = config.provider;
656
- this.confirmationTimeoutMs = config.confirmationTimeoutMs ?? DEFAULT_CONFIRMATION_TIMEOUT_MS;
657
- this.simulateBeforeSubmit = config.simulateBeforeSubmit ?? config.provider !== void 0;
658
- }
659
- /** Address the operator wallet is broadcasting from (for logging). */
660
- operatorAddress() {
661
- return this.operatorWallet.account?.address;
662
- }
663
- /**
664
- * Build calldata for the Relay `mintAndSwap` function. Kept public so
665
- * callers (e.g. the MintingGateway) can log or persist the encoded call
666
- * for audit before broadcasting.
667
- */
668
- encodeCall(params) {
669
- try {
670
- return (0, import_core3.encodeMintAndSwap)(params.mint, params.swap);
671
- } catch (err) {
672
- throw new RelayError(
673
- "ENCODE_FAILED",
674
- `Failed to encode mintAndSwap calldata: ${errorMessage(err)}`,
675
- err
676
- );
677
- }
678
- }
679
- /**
680
- * Submit a `mintAndSwap` transaction. Flow:
681
- *
682
- * 1. (optional) pre-flight simulate via provider
683
- * 2. writeContract through the operator wallet
684
- * 3. (optional) wait for the receipt and surface gasUsed / status
685
- *
686
- * Throws a typed `RelayError` on any failure so the MintingGateway can
687
- * decide whether to release the ledger lock (`SUBMIT_FAILED` and
688
- * `SIMULATION_FAILED` are safe to release; `TX_REVERTED` and `TIMEOUT`
689
- * need manual review because the tx may still land).
690
- *
691
- * @deprecated Since 0.3.0 — will be replaced by `prepareMint()` +
692
- * `prepareBurn()` in the v1.4 sponsored-UserOp flow. The SC team
693
- * still needs to finalize Relayer v2 ABI before the replacements
694
- * can ship (blocker B1). Kept for v0.2.x consumers. Removed in 2.0.
695
- */
696
- async submitMintAndSwap(params) {
697
- if (this.simulateBeforeSubmit && this.provider) {
698
- const operatorAddr = this.operatorWallet.account?.address;
699
- if (operatorAddr) {
700
- try {
701
- await (0, import_core3.simulateMintAndSwap)(
702
- this.provider,
703
- this.relayAddress,
704
- params.mint,
705
- params.swap,
706
- operatorAddr
707
- );
708
- } catch (err) {
709
- const reason = err instanceof import_core3.SimulationError ? err.reason : errorMessage(err);
710
- throw new RelayError(
711
- "SIMULATION_FAILED",
712
- `mintAndSwap would revert: ${reason}`,
713
- err
714
- );
715
- }
716
- }
717
- }
718
- let txHash;
719
- try {
720
- txHash = await this.operatorWallet.writeContract({
721
- address: this.relayAddress,
722
- abi: import_core3.relayAbi,
723
- functionName: "mintAndSwap",
724
- args: [params.mint, params.swap],
725
- ...this.operatorWallet.account ? { account: this.operatorWallet.account } : {}
726
- });
727
- } catch (err) {
728
- throw new RelayError(
729
- "SUBMIT_FAILED",
730
- `Failed to broadcast mintAndSwap: ${errorMessage(err)}`,
731
- err
732
- );
733
- }
734
- if (!this.provider) {
735
- return { txHash };
736
- }
737
- try {
738
- const receipt = await this.provider.waitForTransactionReceipt({
739
- hash: txHash,
740
- timeout: this.confirmationTimeoutMs
741
- });
742
- if (receipt.status !== "success") {
743
- throw new RelayError(
744
- "TX_REVERTED",
745
- `mintAndSwap reverted on-chain (tx=${txHash})`
746
- );
747
- }
748
- return {
749
- txHash,
750
- blockNumber: receipt.blockNumber,
751
- gasUsed: receipt.gasUsed,
752
- status: receipt.status
753
- };
754
- } catch (err) {
755
- if (err instanceof RelayError) throw err;
756
- throw new RelayError(
757
- "TIMEOUT",
758
- `Timed out waiting for mintAndSwap receipt (tx=${txHash}): ${errorMessage(err)}`,
759
- err
760
- );
761
- }
762
- }
763
- // ==========================================================================
764
- // v1.4 — Sponsored UserOp preparation (beta with mocked SC contracts)
765
- // ==========================================================================
766
- //
767
- // These two methods build unsigned `PartialUserOperation` payloads for
768
- // the Frontend to sign (via Privy) and submit to the Bundler. The
769
- // Issuer Backend no longer broadcasts — that's the Frontend's job.
770
- //
771
- // Uses mocked Relayer v2 + PointToken ABIs from `@pafi-dev/core/contracts`.
772
- // When SC delivers real ABIs, the imports swap but these method bodies
773
- // stay the same (calldata encoder is ABI-driven).
774
- // ==========================================================================
775
637
  /**
776
- * Build an unsigned UserOp for Scenario 1 (Mint).
777
- *
778
- * Flow:
779
- * 1. Encode `Relayer.mint(request, userSig, issuerSig)` as the inner call
780
- * 2. Optionally append a PT fee transfer from user → feeRecipient
781
- * (fee recovery happens on-chain via BatchExecutor, not via an
782
- * operator wallet)
783
- * 3. Wrap all inner calls into `BatchExecutor.execute(calls[])`
784
- * 4. Return a `PartialUserOperation` ready for:
785
- * - gas estimation (Bundler)
786
- * - paymaster sponsorship (PAFI Backend)
787
- * - user signature (Privy)
638
+ * Build an unsigned UserOp for Scenario 1 (Mint) — sig-gated
639
+ * `PointToken.mint(to, amount, deadline, minterSig)`.
788
640
  */
789
- /**
790
- * Build an unsigned UserOp for Scenario 1 (Mint) — direct
791
- * `PointToken.mint(amount)` flow (v1.4 post-Relayer-removal).
792
- *
793
- * `msg.sender` inside the batch = user EOA via EIP-7702 delegation.
794
- * `PointToken.mint` checks the caller is on its `minters` allowlist
795
- * (gg56 BE pre-validates this off-chain to avoid wasted gas).
796
- *
797
- * Optional PT fee transfer is appended after the mint when
798
- * `feeAmount > 0` — this is application-level fee recovery (no
799
- * Relayer doing it for us). The user must end up with `amount - fee`
800
- * net PT after the batch executes.
801
- */
802
- prepareMint(params) {
641
+ async prepareMint(params) {
803
642
  if (!params.batchExecutorAddress) {
804
643
  throw new RelayError(
805
644
  "ENCODE_FAILED",
@@ -818,12 +657,41 @@ var RelayService = class {
818
657
  if (params.amount <= 0n) {
819
658
  throw new RelayError("ENCODE_FAILED", "prepareMint: amount must be positive");
820
659
  }
660
+ if (!params.issuerSignerWallet) {
661
+ throw new RelayError(
662
+ "ENCODE_FAILED",
663
+ "prepareMint: issuerSignerWallet required (for MintRequest EIP-712 signature)"
664
+ );
665
+ }
666
+ if (params.deadline <= 0n) {
667
+ throw new RelayError("ENCODE_FAILED", "prepareMint: deadline must be positive");
668
+ }
669
+ let minterSig;
670
+ try {
671
+ const sig = await (0, import_core3.signMintRequest)(
672
+ params.issuerSignerWallet,
673
+ params.domain,
674
+ {
675
+ to: params.userAddress,
676
+ amount: params.amount,
677
+ nonce: params.mintRequestNonce,
678
+ deadline: params.deadline
679
+ }
680
+ );
681
+ minterSig = sig.serialized;
682
+ } catch (err) {
683
+ throw new RelayError(
684
+ "ENCODE_FAILED",
685
+ `prepareMint: failed to sign MintRequest: ${errorMessage(err)}`,
686
+ err
687
+ );
688
+ }
821
689
  let mintCallData;
822
690
  try {
823
691
  mintCallData = (0, import_viem5.encodeFunctionData)({
824
692
  abi: import_core3.POINT_TOKEN_V2_ABI,
825
693
  functionName: "mint",
826
- args: [params.amount]
694
+ args: [params.userAddress, params.amount, params.deadline, minterSig]
827
695
  });
828
696
  } catch (err) {
829
697
  throw new RelayError(
@@ -871,13 +739,12 @@ var RelayService = class {
871
739
  * Build an unsigned UserOp for Scenario 2 (Burn/Redeem).
872
740
  *
873
741
  * Two modes:
874
- * - `mode: 'burn'` — direct `PointToken.burn(amount)`; `msg.sender`
875
- * via EIP-7702 delegation is the user, so no signature needed
876
- * on-chain (the BurnConsent was already verified off-chain by
877
- * the issuer backend before we got here)
878
- * - `mode: 'burnWithSig'` `PointToken.burnWithSig(consent, sig)`;
879
- * used when the issuer hasn't verified the consent and the
880
- * contract has to do it on-chain
742
+ * - `mode: 'burn'` — direct `PointToken.burn(from, amount)`; only
743
+ * usable if the caller (via EIP-7702) is whitelisted as a burner.
744
+ * Rare in v1.4; kept for admin/operator tools.
745
+ * - `mode: 'burnWithSig'` `PointToken.burn(from, amount, deadline,
746
+ * burnerSig)`. Caller provides a pre-signed `BurnRequest` + sig
747
+ * bytes (typically from `PTRedeemHandler`).
881
748
  */
882
749
  prepareBurn(params) {
883
750
  if (!params.pointTokenAddress) {
@@ -892,19 +759,24 @@ var RelayService = class {
892
759
  let burnCallData;
893
760
  try {
894
761
  if (params.mode === "burnWithSig") {
895
- if (!params.burnConsent || !params.consentSignature) {
896
- throw new Error("burnWithSig requires burnConsent + consentSignature");
762
+ if (!params.burnRequest || !params.burnerSignature) {
763
+ throw new Error("burnWithSig requires burnRequest + burnerSignature");
897
764
  }
898
765
  burnCallData = (0, import_viem5.encodeFunctionData)({
899
766
  abi: import_core3.POINT_TOKEN_V2_ABI,
900
- functionName: "burnWithSig",
901
- args: [params.burnConsent, params.consentSignature]
767
+ functionName: "burn",
768
+ args: [
769
+ params.burnRequest.from,
770
+ params.burnRequest.amount,
771
+ params.burnRequest.deadline,
772
+ params.burnerSignature
773
+ ]
902
774
  });
903
775
  } else {
904
776
  burnCallData = (0, import_viem5.encodeFunctionData)({
905
777
  abi: import_core3.POINT_TOKEN_V2_ABI,
906
778
  functionName: "burn",
907
- args: [params.amount]
779
+ args: [params.userAddress, params.amount]
908
780
  });
909
781
  }
910
782
  } catch (err) {
@@ -974,252 +846,6 @@ var FeeManager = class {
974
846
  }
975
847
  };
976
848
 
977
- // src/gateway/types.ts
978
- var MintingGatewayError = class extends Error {
979
- code;
980
- /**
981
- * True if the ledger lock was released before this error was thrown,
982
- * meaning the user can safely retry. False means the funds are still
983
- * locked (e.g. tx may still land on-chain) and retry would double-spend.
984
- */
985
- safeToRetry;
986
- cause;
987
- constructor(code, message, opts) {
988
- super(message);
989
- this.name = "MintingGatewayError";
990
- this.code = code;
991
- this.safeToRetry = opts.safeToRetry;
992
- if (opts.cause !== void 0) this.cause = opts.cause;
993
- }
994
- };
995
-
996
- // src/gateway/mintingGateway.ts
997
- var import_core4 = require("@pafi-dev/core");
998
- var DEFAULT_LOCK_BUFFER_MS = 6e4;
999
- var MintingGateway = class {
1000
- ledger;
1001
- policy;
1002
- signer;
1003
- relayService;
1004
- now;
1005
- defaultLockBufferMs;
1006
- constructor(config) {
1007
- if (!config.ledger) throw new Error("MintingGateway: ledger required");
1008
- if (!config.policy) throw new Error("MintingGateway: policy required");
1009
- if (!config.signer) throw new Error("MintingGateway: signer required");
1010
- if (!config.relayService)
1011
- throw new Error("MintingGateway: relayService required");
1012
- this.ledger = config.ledger;
1013
- this.policy = config.policy;
1014
- this.signer = config.signer;
1015
- this.relayService = config.relayService;
1016
- this.now = config.now ?? (() => Date.now());
1017
- this.defaultLockBufferMs = config.defaultLockBufferMs ?? DEFAULT_LOCK_BUFFER_MS;
1018
- }
1019
- /**
1020
- * @deprecated Since 0.3.0 — will be renamed to `processMint()` once
1021
- * the SC team finalizes Relayer v2 ABI. The new flow drops the
1022
- * swap steps entirely (no more single-call mint+swap); users swap
1023
- * separately on PAFI Web. Kept here for v0.2.x consumers. Removed in 2.0.
1024
- */
1025
- async processMintAndCashOut(request) {
1026
- const { receiverConsent, receiverSignature } = request;
1027
- if (!receiverConsent || !receiverSignature) {
1028
- throw new MintingGatewayError(
1029
- "INVALID_REQUEST",
1030
- "receiverConsent and receiverSignature are required",
1031
- { safeToRetry: true }
1032
- );
1033
- }
1034
- if (receiverConsent.amount <= 0n) {
1035
- throw new MintingGatewayError(
1036
- "INVALID_REQUEST",
1037
- "consent amount must be positive",
1038
- { safeToRetry: true }
1039
- );
1040
- }
1041
- if (receiverConsent.originalReceiver !== request.userAddress) {
1042
- throw new MintingGatewayError(
1043
- "INVALID_REQUEST",
1044
- "consent.originalReceiver must equal request.userAddress",
1045
- { safeToRetry: true }
1046
- );
1047
- }
1048
- const nowSec = BigInt(Math.floor(this.now() / 1e3));
1049
- if (receiverConsent.deadline <= nowSec) {
1050
- throw new MintingGatewayError(
1051
- "CONSENT_EXPIRED",
1052
- "ReceiverConsent deadline has already passed",
1053
- { safeToRetry: true }
1054
- );
1055
- }
1056
- const consentResult = await (0, import_core4.verifyReceiverConsent)(
1057
- request.domain,
1058
- receiverConsent,
1059
- receiverSignature,
1060
- request.userAddress
1061
- );
1062
- if (!consentResult.isValid) {
1063
- throw new MintingGatewayError(
1064
- "INVALID_CONSENT_SIGNATURE",
1065
- `ReceiverConsent signature did not recover to ${request.userAddress}`,
1066
- { safeToRetry: true }
1067
- );
1068
- }
1069
- const policyDecision = await this.policy.evaluate({
1070
- userAddress: request.userAddress,
1071
- amount: receiverConsent.amount,
1072
- pointTokenAddress: request.pointTokenAddress,
1073
- chainId: request.chainId
1074
- });
1075
- if (!policyDecision.approved) {
1076
- const code = policyDecision.reason?.toLowerCase().includes("insufficient") ? "INSUFFICIENT_BALANCE" : "POLICY_REJECTED";
1077
- throw new MintingGatewayError(
1078
- code,
1079
- policyDecision.reason ?? "Minting request rejected by policy engine",
1080
- { safeToRetry: true }
1081
- );
1082
- }
1083
- const lockDurationMs = request.lockDurationMs ?? this.computeLockDurationMs(receiverConsent.deadline);
1084
- let lockId;
1085
- try {
1086
- lockId = await this.ledger.lockForMinting(
1087
- request.userAddress,
1088
- receiverConsent.amount,
1089
- lockDurationMs,
1090
- request.pointTokenAddress
1091
- );
1092
- } catch (err) {
1093
- throw new MintingGatewayError(
1094
- "INSUFFICIENT_BALANCE",
1095
- `Failed to lock ledger balance: ${errorMessage2(err)}`,
1096
- { safeToRetry: true, cause: err }
1097
- );
1098
- }
1099
- try {
1100
- let minterSignature;
1101
- try {
1102
- minterSignature = await this.signer.signMintRequest(request.domain, {
1103
- to: request.userAddress,
1104
- amount: receiverConsent.amount,
1105
- nonce: receiverConsent.nonce,
1106
- deadline: receiverConsent.deadline
1107
- });
1108
- } catch (err) {
1109
- await this.releaseLockSafely(lockId);
1110
- throw new MintingGatewayError(
1111
- "SIGNER_FAILED",
1112
- `Issuer signer failed: ${errorMessage2(err)}`,
1113
- { safeToRetry: true, cause: err }
1114
- );
1115
- }
1116
- const mintParams = {
1117
- pointToken: request.pointTokenAddress,
1118
- receiver: request.userAddress,
1119
- amount: receiverConsent.amount,
1120
- deadline: receiverConsent.deadline,
1121
- minterSig: minterSignature.serialized,
1122
- receiverSig: receiverSignature,
1123
- extData: receiverConsent.extData
1124
- };
1125
- const swapParams = {
1126
- path: request.swapPath,
1127
- deadline: request.swapDeadline
1128
- };
1129
- let relayResult;
1130
- try {
1131
- relayResult = await this.relayService.submitMintAndSwap({
1132
- mint: mintParams,
1133
- swap: swapParams
1134
- });
1135
- } catch (err) {
1136
- await this.handleRelayFailure(err, lockId);
1137
- }
1138
- const result = {
1139
- txHash: relayResult.txHash,
1140
- lockId
1141
- };
1142
- if (relayResult.blockNumber !== void 0) {
1143
- result.blockNumber = relayResult.blockNumber;
1144
- }
1145
- if (relayResult.gasUsed !== void 0) {
1146
- result.gasUsed = relayResult.gasUsed;
1147
- }
1148
- return result;
1149
- } catch (err) {
1150
- if (err instanceof MintingGatewayError) throw err;
1151
- await this.releaseLockSafely(lockId);
1152
- throw new MintingGatewayError(
1153
- "RELAY_SUBMIT_FAILED",
1154
- `Unexpected error: ${errorMessage2(err)}`,
1155
- { safeToRetry: true, cause: err }
1156
- );
1157
- }
1158
- }
1159
- // ---------------------------------------------------------------------------
1160
- // Internals
1161
- // ---------------------------------------------------------------------------
1162
- computeLockDurationMs(consentDeadlineSec) {
1163
- const nowMs = this.now();
1164
- const deadlineMs = Number(consentDeadlineSec) * 1e3;
1165
- const remaining = Math.max(0, deadlineMs - nowMs);
1166
- return remaining + this.defaultLockBufferMs;
1167
- }
1168
- /**
1169
- * Map a RelayError to a MintingGatewayError, releasing the lock only
1170
- * when the tx definitely did not land. `TX_REVERTED` and `TIMEOUT`
1171
- * leave the lock in place because the tx may still be in the mempool
1172
- * or already mined — releasing would enable a double-spend on retry.
1173
- * Always throws.
1174
- */
1175
- async handleRelayFailure(err, lockId) {
1176
- if (err instanceof RelayError) {
1177
- switch (err.code) {
1178
- case "ENCODE_FAILED":
1179
- case "SIMULATION_FAILED":
1180
- case "SUBMIT_FAILED":
1181
- case "NOT_CONFIGURED":
1182
- await this.releaseLockSafely(lockId);
1183
- throw new MintingGatewayError(
1184
- err.code === "SIMULATION_FAILED" ? "RELAY_SIMULATION_FAILED" : "RELAY_SUBMIT_FAILED",
1185
- err.message,
1186
- { safeToRetry: true, cause: err }
1187
- );
1188
- case "TX_REVERTED":
1189
- throw new MintingGatewayError("RELAY_REVERTED", err.message, {
1190
- safeToRetry: false,
1191
- cause: err
1192
- });
1193
- case "TIMEOUT":
1194
- throw new MintingGatewayError("RELAY_TIMEOUT", err.message, {
1195
- safeToRetry: false,
1196
- cause: err
1197
- });
1198
- }
1199
- }
1200
- await this.releaseLockSafely(lockId);
1201
- throw new MintingGatewayError(
1202
- "RELAY_SUBMIT_FAILED",
1203
- `Unexpected relay error: ${errorMessage2(err)}`,
1204
- { safeToRetry: true, cause: err }
1205
- );
1206
- }
1207
- /**
1208
- * Release a lock, swallowing any secondary error. We never want a lock
1209
- * release failure to mask the original error — the lock will auto-expire
1210
- * via TTL anyway.
1211
- */
1212
- async releaseLockSafely(lockId) {
1213
- try {
1214
- await this.ledger.releaseLock(lockId);
1215
- } catch {
1216
- }
1217
- }
1218
- };
1219
- function errorMessage2(err) {
1220
- return err instanceof Error ? err.message : String(err);
1221
- }
1222
-
1223
849
  // src/indexer/types.ts
1224
850
  var InMemoryCursorStore = class {
1225
851
  cursor;
@@ -1564,10 +1190,9 @@ var BurnIndexer = class {
1564
1190
 
1565
1191
  // src/api/handlers.ts
1566
1192
  var import_viem8 = require("viem");
1567
- var import_core5 = require("@pafi-dev/core");
1193
+ var import_core4 = require("@pafi-dev/core");
1568
1194
  var IssuerApiHandlers = class {
1569
1195
  authService;
1570
- gateway;
1571
1196
  ledger;
1572
1197
  provider;
1573
1198
  /**
@@ -1586,7 +1211,6 @@ var IssuerApiHandlers = class {
1586
1211
  poolsProvider;
1587
1212
  constructor(config) {
1588
1213
  this.authService = config.authService;
1589
- this.gateway = config.gateway;
1590
1214
  this.ledger = config.ledger;
1591
1215
  this.provider = config.provider;
1592
1216
  const raw = config.pointTokenAddresses && config.pointTokenAddresses.length > 0 ? config.pointTokenAddresses : config.pointTokenAddress ? [config.pointTokenAddress] : [];
@@ -1710,11 +1334,11 @@ var IssuerApiHandlers = class {
1710
1334
  );
1711
1335
  }
1712
1336
  const [mintRequestNonce, receiverConsentNonce, offChainBalance, onChainBalance, minter] = await Promise.all([
1713
- (0, import_core5.getMintRequestNonce)(this.provider, pointToken, normalizedAuthed),
1714
- (0, import_core5.getReceiverConsentNonce)(this.provider, pointToken, normalizedAuthed),
1337
+ (0, import_core4.getMintRequestNonce)(this.provider, pointToken, normalizedAuthed),
1338
+ (0, import_core4.getReceiverConsentNonce)(this.provider, pointToken, normalizedAuthed),
1715
1339
  this.ledger.getBalance(normalizedAuthed, pointToken),
1716
- (0, import_core5.getPointTokenBalance)(this.provider, pointToken, normalizedAuthed),
1717
- (0, import_core5.isMinter)(this.provider, pointToken, normalizedAuthed)
1340
+ (0, import_core4.getPointTokenBalance)(this.provider, pointToken, normalizedAuthed),
1341
+ (0, import_core4.isMinter)(this.provider, pointToken, normalizedAuthed)
1718
1342
  ]);
1719
1343
  return {
1720
1344
  mintRequestNonce,
@@ -1752,13 +1376,13 @@ var IssuerApiHandlers = class {
1752
1376
  `handleBuildConsentTypedData: unsupported pointToken ${pointToken}`
1753
1377
  );
1754
1378
  }
1755
- const name = await (0, import_core5.getTokenName)(this.provider, pointToken);
1379
+ const name = await (0, import_core4.getTokenName)(this.provider, pointToken);
1756
1380
  const domain = {
1757
1381
  name,
1758
1382
  verifyingContract: pointToken,
1759
1383
  chainId: this.chainId
1760
1384
  };
1761
- const typedData = (0, import_core5.buildReceiverConsentTypedData)(domain, request.receiverConsent);
1385
+ const typedData = (0, import_core4.buildReceiverConsentTypedData)(domain, request.receiverConsent);
1762
1386
  return {
1763
1387
  typedData: {
1764
1388
  domain: typedData.domain,
@@ -1768,55 +1392,13 @@ var IssuerApiHandlers = class {
1768
1392
  }
1769
1393
  };
1770
1394
  }
1771
- /**
1772
- * `POST /claim-and-swap`
1773
- *
1774
- * @deprecated Since 0.3.0 — the single-call mint-then-swap flow is
1775
- * retired in v1.4. Use the new `handleClaim()` (mint only) and let
1776
- * the user swap separately on PAFI Web. See
1777
- * [V1.4_V1.5_OVERVIEW.md §4] for the new scenario model. Will be
1778
- * removed in 2.0.
1779
- *
1780
- * Legacy behavior: the terminal handler forwards the verified
1781
- * consent to the MintingGateway, which runs the 11-step flow.
1782
- */
1783
- async handleClaimAndSwap(userAddress, request) {
1784
- if (request.chainId !== this.chainId) {
1785
- throw new Error(
1786
- `handleClaimAndSwap: unsupported chainId ${request.chainId}`
1787
- );
1788
- }
1789
- const pointToken = (0, import_viem8.getAddress)(request.pointTokenAddress);
1790
- if (!this.supportedTokens.has(pointToken)) {
1791
- throw new Error(
1792
- `handleClaimAndSwap: unsupported pointToken ${pointToken}`
1793
- );
1794
- }
1795
- const result = await this.gateway.processMintAndCashOut({
1796
- userAddress: (0, import_viem8.getAddress)(userAddress),
1797
- pointTokenAddress: pointToken,
1798
- chainId: request.chainId,
1799
- domain: request.domain,
1800
- receiverConsent: request.receiverConsent,
1801
- receiverSignature: request.receiverSignature,
1802
- swapPath: request.swapPath,
1803
- swapDeadline: request.swapDeadline
1804
- });
1805
- const response = {
1806
- txHash: result.txHash,
1807
- lockId: result.lockId
1808
- };
1809
- if (result.blockNumber !== void 0)
1810
- response.blockNumber = result.blockNumber;
1811
- if (result.gasUsed !== void 0) response.gasUsed = result.gasUsed;
1812
- return response;
1813
- }
1814
1395
  };
1815
1396
 
1816
1397
  // src/api/handlers/ptRedeemHandler.ts
1817
1398
  var import_viem9 = require("viem");
1818
- var import_core6 = require("@pafi-dev/core");
1399
+ var import_core5 = require("@pafi-dev/core");
1819
1400
  var DEFAULT_REDEEM_LOCK_MS = 15 * 60 * 1e3;
1401
+ var DEFAULT_SIG_DEADLINE_SEC = 15 * 60;
1820
1402
  var PTRedeemError = class extends Error {
1821
1403
  constructor(code, message) {
1822
1404
  super(message);
@@ -1828,11 +1410,14 @@ var PTRedeemError = class extends Error {
1828
1410
  var PTRedeemHandler = class {
1829
1411
  ledger;
1830
1412
  relayService;
1413
+ provider;
1831
1414
  pointTokenAddress;
1832
1415
  batchExecutorAddress;
1833
1416
  chainId;
1834
1417
  domain;
1418
+ burnerSignerWallet;
1835
1419
  redeemLockDurationMs;
1420
+ signatureDeadlineSeconds;
1836
1421
  now;
1837
1422
  constructor(config) {
1838
1423
  if (!config.ledger.reservePendingCredit) {
@@ -1841,46 +1426,68 @@ var PTRedeemHandler = class {
1841
1426
  "PTRedeemHandler requires a ledger that implements reservePendingCredit() (v0.3.0+)"
1842
1427
  );
1843
1428
  }
1429
+ if (!config.burnerSignerWallet) {
1430
+ throw new PTRedeemError(
1431
+ "SIGNING_FAILED",
1432
+ "PTRedeemHandler requires burnerSignerWallet (issuer burner signer)"
1433
+ );
1434
+ }
1844
1435
  this.ledger = config.ledger;
1845
1436
  this.relayService = config.relayService;
1437
+ this.provider = config.provider;
1846
1438
  this.pointTokenAddress = (0, import_viem9.getAddress)(config.pointTokenAddress);
1847
1439
  this.batchExecutorAddress = (0, import_viem9.getAddress)(config.batchExecutorAddress);
1848
1440
  this.chainId = config.chainId;
1849
1441
  this.domain = config.domain;
1442
+ this.burnerSignerWallet = config.burnerSignerWallet;
1850
1443
  this.redeemLockDurationMs = config.redeemLockDurationMs ?? DEFAULT_REDEEM_LOCK_MS;
1444
+ this.signatureDeadlineSeconds = config.signatureDeadlineSeconds ?? DEFAULT_SIG_DEADLINE_SEC;
1851
1445
  this.now = config.now ?? (() => Date.now());
1852
1446
  }
1853
1447
  async handle(request) {
1854
1448
  if (request.amount <= 0n) {
1855
- throw new PTRedeemError("INVALID_CONSENT", "redeem amount must be positive");
1449
+ throw new PTRedeemError("INVALID_AMOUNT", "redeem amount must be positive");
1856
1450
  }
1857
- if (request.consent.amount !== request.amount) {
1858
- throw new PTRedeemError(
1859
- "AMOUNT_MISMATCH",
1860
- `consent.amount (${request.consent.amount}) must match request.amount (${request.amount})`
1861
- );
1862
- }
1863
- const nowSeconds = BigInt(Math.floor(this.now() / 1e3));
1864
- if (request.consent.deadline <= nowSeconds) {
1451
+ let burnNonce;
1452
+ try {
1453
+ burnNonce = await this.provider.readContract({
1454
+ address: this.pointTokenAddress,
1455
+ abi: import_core5.POINT_TOKEN_V2_ABI,
1456
+ functionName: "burnRequestNonces",
1457
+ args: [request.userAddress]
1458
+ });
1459
+ } catch (err) {
1865
1460
  throw new PTRedeemError(
1866
- "EXPIRED_CONSENT",
1867
- `consent deadline (${request.consent.deadline}) already passed`
1461
+ "NONCE_READ_FAILED",
1462
+ `failed to read burnRequestNonces(${request.userAddress}): ${err instanceof Error ? err.message : String(err)}`
1868
1463
  );
1869
1464
  }
1870
- const verification = await (0, import_core6.verifyBurnConsent)(
1871
- {
1872
- name: this.domain.name,
1873
- chainId: this.chainId,
1874
- verifyingContract: this.domain.verifyingContract ?? this.pointTokenAddress
1875
- },
1876
- request.consent,
1877
- request.consentSignature,
1878
- request.userAddress
1465
+ const deadline = BigInt(
1466
+ Math.floor(this.now() / 1e3) + this.signatureDeadlineSeconds
1879
1467
  );
1880
- if (!verification.isValid) {
1468
+ const domain = {
1469
+ name: this.domain.name,
1470
+ chainId: this.chainId,
1471
+ verifyingContract: this.domain.verifyingContract ?? this.pointTokenAddress
1472
+ };
1473
+ const burnRequest = {
1474
+ from: request.userAddress,
1475
+ amount: request.amount,
1476
+ nonce: burnNonce,
1477
+ deadline
1478
+ };
1479
+ let burnerSignature;
1480
+ try {
1481
+ const sig = await (0, import_core5.signBurnRequest)(
1482
+ this.burnerSignerWallet,
1483
+ domain,
1484
+ burnRequest
1485
+ );
1486
+ burnerSignature = sig.serialized;
1487
+ } catch (err) {
1881
1488
  throw new PTRedeemError(
1882
- "SIGNATURE_MISMATCH",
1883
- `signer mismatch \u2014 expected ${request.userAddress}, got ${verification.recoveredAddress}`
1489
+ "SIGNING_FAILED",
1490
+ `failed to sign BurnRequest: ${err instanceof Error ? err.message : String(err)}`
1884
1491
  );
1885
1492
  }
1886
1493
  const lockId = await this.ledger.reservePendingCredit(
@@ -1895,33 +1502,21 @@ var PTRedeemHandler = class {
1895
1502
  aaNonce: request.aaNonce,
1896
1503
  pointTokenAddress: this.pointTokenAddress,
1897
1504
  batchExecutorAddress: this.batchExecutorAddress,
1898
- burnConsent: request.consent,
1899
- consentSignature: parseSigStruct(request.consentSignature)
1505
+ burnRequest,
1506
+ burnerSignature
1900
1507
  });
1901
1508
  return {
1902
1509
  lockId,
1903
1510
  userOp,
1904
- expiresInSeconds: Math.floor(this.redeemLockDurationMs / 1e3)
1511
+ expiresInSeconds: Math.floor(this.redeemLockDurationMs / 1e3),
1512
+ signatureDeadline: deadline
1905
1513
  };
1906
1514
  }
1907
1515
  };
1908
- function parseSigStruct(serialized) {
1909
- const raw = serialized.slice(2);
1910
- if (raw.length !== 130) {
1911
- throw new PTRedeemError(
1912
- "INVALID_CONSENT",
1913
- `signature must be 65 bytes, got ${raw.length / 2}`
1914
- );
1915
- }
1916
- const r = `0x${raw.slice(0, 64)}`;
1917
- const s = `0x${raw.slice(64, 128)}`;
1918
- const v = parseInt(raw.slice(128, 130), 16);
1919
- return { v, r, s };
1920
- }
1921
1516
 
1922
1517
  // src/api/handlers/topUpRedemptionHandler.ts
1923
1518
  var import_viem10 = require("viem");
1924
- var import_core7 = require("@pafi-dev/core");
1519
+ var import_core6 = require("@pafi-dev/core");
1925
1520
  var TopUpRedemptionError = class extends Error {
1926
1521
  constructor(code, message) {
1927
1522
  super(message);
@@ -1950,7 +1545,7 @@ var TopUpRedemptionHandler = class {
1950
1545
  return { action: "NO_TOP_UP_NEEDED", offChainBalance };
1951
1546
  }
1952
1547
  const shortfall = request.requiredAmount - offChainBalance;
1953
- const onChainBalance = await (0, import_core7.getPointTokenBalance)(
1548
+ const onChainBalance = await (0, import_core6.getPointTokenBalance)(
1954
1549
  this.provider,
1955
1550
  this.pointTokenAddress,
1956
1551
  request.userAddress
@@ -1963,24 +1558,10 @@ var TopUpRedemptionHandler = class {
1963
1558
  shortfall
1964
1559
  };
1965
1560
  }
1966
- if (request.redeemRequest.consent.amount < shortfall) {
1967
- throw new TopUpRedemptionError(
1968
- "CONSENT_AMOUNT_TOO_LOW",
1969
- `consent.amount (${request.redeemRequest.consent.amount}) must cover shortfall (${shortfall})`
1970
- );
1971
- }
1972
- if (request.redeemRequest.consent.amount !== shortfall) {
1973
- throw new TopUpRedemptionError(
1974
- "CONSENT_AMOUNT_TOO_LOW",
1975
- `consent.amount (${request.redeemRequest.consent.amount}) must equal shortfall (${shortfall}) exactly \u2014 re-sign with correct amount`
1976
- );
1977
- }
1978
1561
  const redeem = await this.ptRedeemHandler.handle({
1979
1562
  userAddress: request.userAddress,
1980
1563
  amount: shortfall,
1981
- consent: request.redeemRequest.consent,
1982
- consentSignature: request.redeemRequest.consentSignature,
1983
- aaNonce: request.redeemRequest.aaNonce
1564
+ aaNonce: request.aaNonce
1984
1565
  });
1985
1566
  return {
1986
1567
  action: "TOP_UP_STARTED",
@@ -2202,7 +1783,7 @@ function toUsdtPerNative(priceFloat, usdtDecimals) {
2202
1783
  }
2203
1784
 
2204
1785
  // src/balance/balanceAggregator.ts
2205
- var import_core8 = require("@pafi-dev/core");
1786
+ var import_core7 = require("@pafi-dev/core");
2206
1787
  var BalanceAggregator = class {
2207
1788
  provider;
2208
1789
  ledger;
@@ -2223,7 +1804,7 @@ var BalanceAggregator = class {
2223
1804
  async getCombinedBalance(user, pointToken) {
2224
1805
  const [offChain, onChain] = await Promise.all([
2225
1806
  this.ledger.getBalance(user, pointToken),
2226
- (0, import_core8.getPointTokenBalance)(this.provider, pointToken, user)
1807
+ (0, import_core7.getPointTokenBalance)(this.provider, pointToken, user)
2227
1808
  ]);
2228
1809
  return {
2229
1810
  offChain,
@@ -2473,15 +2054,6 @@ function createIssuerService(config) {
2473
2054
  if (!config.provider) {
2474
2055
  throw new Error("createIssuerService: provider is required");
2475
2056
  }
2476
- if (!config.operatorWallet) {
2477
- throw new Error("createIssuerService: operatorWallet is required");
2478
- }
2479
- if (!config.signer) {
2480
- throw new Error("createIssuerService: signer is required");
2481
- }
2482
- if (!config.relayAddress) {
2483
- throw new Error("createIssuerService: relayAddress is required");
2484
- }
2485
2057
  if (!config.auth?.jwtSecret) {
2486
2058
  throw new Error("createIssuerService: auth.jwtSecret is required");
2487
2059
  }
@@ -2508,18 +2080,7 @@ function createIssuerService(config) {
2508
2080
  authServiceConfig.jwtExpiresIn = config.auth.jwtExpiresIn;
2509
2081
  }
2510
2082
  const authService = new AuthService(authServiceConfig);
2511
- const relayServiceConfig = {
2512
- relayAddress: config.relayAddress,
2513
- operatorWallet: config.operatorWallet,
2514
- provider: config.provider
2515
- };
2516
- if (config.relay?.simulateBeforeSubmit !== void 0) {
2517
- relayServiceConfig.simulateBeforeSubmit = config.relay.simulateBeforeSubmit;
2518
- }
2519
- if (config.relay?.confirmationTimeoutMs !== void 0) {
2520
- relayServiceConfig.confirmationTimeoutMs = config.relay.confirmationTimeoutMs;
2521
- }
2522
- const relayService = new RelayService(relayServiceConfig);
2083
+ const relayService = new RelayService();
2523
2084
  let feeManager;
2524
2085
  if (config.fee) {
2525
2086
  feeManager = new FeeManager({
@@ -2527,16 +2088,6 @@ function createIssuerService(config) {
2527
2088
  provider: config.provider
2528
2089
  });
2529
2090
  }
2530
- const gatewayConfig = {
2531
- ledger,
2532
- policy,
2533
- signer: config.signer,
2534
- relayService
2535
- };
2536
- if (config.gateway?.defaultLockBufferMs !== void 0) {
2537
- gatewayConfig.defaultLockBufferMs = config.gateway.defaultLockBufferMs;
2538
- }
2539
- const gateway = new MintingGateway(gatewayConfig);
2540
2091
  const indexers = /* @__PURE__ */ new Map();
2541
2092
  for (const tokenAddress of tokenAddresses) {
2542
2093
  const indexerConfig = {
@@ -2564,7 +2115,6 @@ function createIssuerService(config) {
2564
2115
  const firstIndexer = indexers.get(tokenAddresses[0]);
2565
2116
  const handlersConfig = {
2566
2117
  authService,
2567
- gateway,
2568
2118
  ledger,
2569
2119
  provider: config.provider,
2570
2120
  pointTokenAddresses: tokenAddresses,
@@ -2584,10 +2134,8 @@ function createIssuerService(config) {
2584
2134
  sessionStore,
2585
2135
  ledger,
2586
2136
  policy,
2587
- signer: config.signer,
2588
2137
  relayService,
2589
2138
  feeManager,
2590
- gateway,
2591
2139
  indexers,
2592
2140
  indexer: firstIndexer,
2593
2141
  handlers
@@ -2608,8 +2156,6 @@ var PAFI_ISSUER_SDK_VERSION = "0.1.0";
2608
2156
  IssuerApiHandlers,
2609
2157
  MemoryPointLedger,
2610
2158
  MemorySessionStore,
2611
- MintingGateway,
2612
- MintingGatewayError,
2613
2159
  NonceManager,
2614
2160
  PAFI_ISSUER_SDK_VERSION,
2615
2161
  PTRedeemError,
@@ -2625,7 +2171,6 @@ var PAFI_ISSUER_SDK_VERSION = "0.1.0";
2625
2171
  authenticateRequest,
2626
2172
  createIssuerService,
2627
2173
  createSubgraphNativeUsdtQuoter,
2628
- createSubgraphPoolsProvider,
2629
- encodeExtData
2174
+ createSubgraphPoolsProvider
2630
2175
  });
2631
2176
  //# sourceMappingURL=index.cjs.map