@pafi-dev/core 0.3.0-beta.1 → 0.3.0-beta.2

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
@@ -1,4 +1,4 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
2
 
3
3
  var _chunkFGKLDKJKcjs = require('./chunk-FGKLDKJK.cjs');
4
4
 
@@ -320,6 +320,127 @@ async function verifyMockBurnConsent(domain, message, signature, expectedUser) {
320
320
  };
321
321
  }
322
322
 
323
+ // src/web-handoff/webPopup.ts
324
+ var DEFAULT_WIDTH = 900;
325
+ var DEFAULT_HEIGHT = 700;
326
+ var DEFAULT_NAME = "pafi-web";
327
+ function openWebPopup(url, options = {}) {
328
+ if (typeof window === "undefined" || typeof window.open !== "function") {
329
+ throw new Error(
330
+ "openWebPopup: `window.open` is not available in this runtime. Register a platform adapter via setPafiWebModalAdapter() instead."
331
+ );
332
+ }
333
+ const width = _nullishCoalesce(options.width, () => ( DEFAULT_WIDTH));
334
+ const height = _nullishCoalesce(options.height, () => ( DEFAULT_HEIGHT));
335
+ const name = _nullishCoalesce(options.windowName, () => ( DEFAULT_NAME));
336
+ const screenW = _nullishCoalesce(_optionalChain([window, 'access', _ => _.screen, 'optionalAccess', _2 => _2.availWidth]), () => ( window.innerWidth));
337
+ const screenH = _nullishCoalesce(_optionalChain([window, 'access', _3 => _3.screen, 'optionalAccess', _4 => _4.availHeight]), () => ( window.innerHeight));
338
+ const left = Math.max(0, Math.floor((screenW - width) / 2));
339
+ const top = Math.max(0, Math.floor((screenH - height) / 2));
340
+ const features = [
341
+ `width=${width}`,
342
+ `height=${height}`,
343
+ `left=${left}`,
344
+ `top=${top}`,
345
+ "resizable=yes",
346
+ "scrollbars=yes",
347
+ "noopener=no"
348
+ // we need opener.postMessage to work
349
+ ].join(",");
350
+ const popup = window.open(url, name, features);
351
+ if (!popup) {
352
+ throw new Error(
353
+ "openWebPopup: popup was blocked. Ensure this call runs inside a user gesture (click handler)."
354
+ );
355
+ }
356
+ let closed = false;
357
+ let pollId = null;
358
+ let messageListener = null;
359
+ const dispose = () => {
360
+ if (closed) return;
361
+ closed = true;
362
+ if (pollId !== null) {
363
+ clearInterval(pollId);
364
+ pollId = null;
365
+ }
366
+ if (messageListener) {
367
+ window.removeEventListener("message", messageListener);
368
+ messageListener = null;
369
+ }
370
+ _optionalChain([options, 'access', _5 => _5.onClose, 'optionalCall', _6 => _6()]);
371
+ };
372
+ pollId = setInterval(() => {
373
+ if (popup.closed) {
374
+ dispose();
375
+ }
376
+ }, 500);
377
+ if (options.onMessage) {
378
+ const allowed = _nullishCoalesce(options.allowedOrigins, () => ( []));
379
+ const onMessage = options.onMessage;
380
+ messageListener = (event) => {
381
+ if (event.source !== popup) return;
382
+ if (!allowed.includes(event.origin)) return;
383
+ onMessage(event.data, event.origin);
384
+ };
385
+ window.addEventListener("message", messageListener);
386
+ }
387
+ return {
388
+ get isOpen() {
389
+ return !closed && !popup.closed;
390
+ },
391
+ close() {
392
+ if (closed) return;
393
+ try {
394
+ popup.close();
395
+ } catch (e) {
396
+ }
397
+ dispose();
398
+ },
399
+ focus() {
400
+ if (closed || popup.closed) return;
401
+ try {
402
+ popup.focus();
403
+ } catch (e2) {
404
+ }
405
+ },
406
+ postMessage(data) {
407
+ if (closed || popup.closed) return;
408
+ try {
409
+ popup.postMessage(data, "*");
410
+ } catch (e3) {
411
+ }
412
+ }
413
+ };
414
+ }
415
+ var webPopupAdapter = {
416
+ open(url, options) {
417
+ return openWebPopup(url, options);
418
+ }
419
+ };
420
+
421
+ // src/web-handoff/index.ts
422
+ var registeredAdapter = null;
423
+ function setPafiWebModalAdapter(adapter) {
424
+ registeredAdapter = adapter;
425
+ }
426
+ function getPafiWebModalAdapter() {
427
+ return registeredAdapter;
428
+ }
429
+ async function openPafiWebModal(url, options = {}) {
430
+ if (!url || typeof url !== "string") {
431
+ throw new Error("openPafiWebModal: `url` is required");
432
+ }
433
+ if (registeredAdapter) {
434
+ return Promise.resolve(registeredAdapter.open(url, options));
435
+ }
436
+ if (typeof window !== "undefined" && typeof window.open === "function") {
437
+ return openWebPopup(url, options);
438
+ }
439
+ throw new Error(
440
+ "openPafiWebModal: no adapter registered and `window.open` is unavailable. Call `setPafiWebModalAdapter()` with a platform-specific adapter (e.g. SFSafariViewController on iOS) during app boot."
441
+ );
442
+ }
443
+
323
444
  // src/index.ts
324
445
  var PafiSDK = class {
325
446
 
@@ -669,5 +790,10 @@ var PafiSDK = class {
669
790
 
670
791
 
671
792
 
672
- exports.ApiError = _chunk5QJZT7VBcjs.ApiError; exports.BATCH_EXECUTOR_ABI = _chunkCXFUP2YKcjs.BATCH_EXECUTOR_ABI; exports.BATCH_EXECUTOR_ADDRESS_BASE_MAINNET = MOCK_BATCH_EXECUTOR_ADDRESS_BASE_MAINNET; exports.BATCH_EXECUTOR_ADDRESS_BASE_SEPOLIA = MOCK_BATCH_EXECUTOR_ADDRESS_BASE_SEPOLIA; exports.BURN_CONSENT_TYPES = MOCK_BURN_CONSENT_TYPES; exports.COMMON_POOLS = _chunkCWP4PFOPcjs.COMMON_POOLS; exports.COMMON_TOKENS = _chunkCWP4PFOPcjs.COMMON_TOKENS; exports.CONTRACT_ADDRESSES = MOCK_ADDRESSES; exports.ConfigurationError = _chunk5QJZT7VBcjs.ConfigurationError; exports.ENTRY_POINT_V07 = ENTRY_POINT_V07; exports.MINT_REQUEST_V2_TYPES = MOCK_MINT_REQUEST_V2_TYPES; exports.POINT_TOKEN_POOLS = _chunkCWP4PFOPcjs.POINT_TOKEN_POOLS; exports.POINT_TOKEN_V2_ABI = MOCK_POINT_TOKEN_V2_ABI; exports.PafiSDK = PafiSDK; exports.PafiSDKError = _chunk5QJZT7VBcjs.PafiSDKError; exports.RELAYER_V2_ABI = MOCK_RELAYER_V2_ABI; exports.RELAYER_V2_MINT_SELECTOR = MOCK_RELAYER_V2_MINT_SELECTOR; exports.SETTLE_ALL = _chunkCXFUP2YKcjs.SETTLE_ALL; exports.SUPPORTED_CHAINS = _chunkCWP4PFOPcjs.SUPPORTED_CHAINS; exports.SWAP_EXACT_IN = _chunkCXFUP2YKcjs.SWAP_EXACT_IN; exports.SigningError = _chunk5QJZT7VBcjs.SigningError; exports.SimulationError = _chunk5QJZT7VBcjs.SimulationError; exports.TAKE_ALL = _chunkCXFUP2YKcjs.TAKE_ALL; exports.UNIVERSAL_ROUTER_ADDRESSES = _chunkCWP4PFOPcjs.UNIVERSAL_ROUTER_ADDRESSES; exports.V4_QUOTER_ADDRESSES = _chunkCWP4PFOPcjs.V4_QUOTER_ADDRESSES; exports.V4_SWAP = _chunkCXFUP2YKcjs.V4_SWAP; exports.ZERO_VALUE = ZERO_VALUE; exports._resetPaymasterConfigForTests = _resetPaymasterConfigForTests; exports.assembleUserOperation = _chunkCXFUP2YKcjs.assembleUserOperation; exports.buildAllPaths = _chunkCAE2BVKIcjs.buildAllPaths; exports.buildBurnConsentTypedData = buildMockBurnConsentTypedData; exports.buildDomain = _chunkBNTVOKYTcjs.buildDomain; exports.buildErc20ApprovalCalldata = _chunkCXFUP2YKcjs.buildErc20ApprovalCalldata; exports.buildMintRequestTypedData = _chunkBNTVOKYTcjs.buildMintRequestTypedData; exports.buildMintRequestV2TypedData = buildMockMintRequestV2TypedData; exports.buildPartialUserOperation = _chunkCXFUP2YKcjs.buildPartialUserOperation; exports.buildPermit2ApprovalCalldata = _chunkCXFUP2YKcjs.buildPermit2ApprovalCalldata; exports.buildReceiverConsentTypedData = _chunkBNTVOKYTcjs.buildReceiverConsentTypedData; exports.buildRelaySwapParams = _chunkL74CZAVQcjs.buildRelaySwapParams; exports.buildSwapFromQuote = _chunkCXFUP2YKcjs.buildSwapFromQuote; exports.buildSwapWithGasDeduction = _chunkCXFUP2YKcjs.buildSwapWithGasDeduction; exports.buildUniversalRouterExecuteArgs = _chunkCXFUP2YKcjs.buildUniversalRouterExecuteArgs; exports.buildV4SwapInput = _chunkCXFUP2YKcjs.buildV4SwapInput; exports.checkAllowance = _chunkCXFUP2YKcjs.checkAllowance; exports.checkEthAndBranch = checkEthAndBranch; exports.combineRoutes = _chunkCAE2BVKIcjs.combineRoutes; exports.createLoginMessage = _chunkGWLEEXM4cjs.createLoginMessage; exports.decodeExtData = _chunkL74CZAVQcjs.decodeExtData; exports.decodeMintAndSwap = _chunkL74CZAVQcjs.decodeMintAndSwap; exports.encodeBatchExecute = _chunkCXFUP2YKcjs.encodeBatchExecute; exports.encodeExtData = _chunkL74CZAVQcjs.encodeExtData; exports.encodeMintAndSwap = _chunkL74CZAVQcjs.encodeMintAndSwap; exports.erc20Abi = _chunkIPXARZ6Fcjs.erc20Abi; exports.erc20ApproveOp = _chunkCXFUP2YKcjs.erc20ApproveOp; exports.erc20BurnOp = _chunkCXFUP2YKcjs.erc20BurnOp; exports.erc20TransferOp = _chunkCXFUP2YKcjs.erc20TransferOp; exports.findBestQuote = _chunkCAE2BVKIcjs.findBestQuote; exports.getContractAddresses = getMockAddresses; exports.getIssuer = _chunkX3I5XWARcjs.getIssuer2; exports.getMintRequestNonce = _chunkX3I5XWARcjs.getMintRequestNonce; exports.getPaymasterConfig = getPaymasterConfig; exports.getPointTokenBalance = _chunkX3I5XWARcjs.getPointTokenBalance; exports.getPointTokenIssuer = _chunkX3I5XWARcjs.getPointTokenIssuer; exports.getPointTokenIssuerAddress = _chunkX3I5XWARcjs.getIssuer; exports.getReceiverConsentNonce = _chunkX3I5XWARcjs.getReceiverConsentNonce; exports.getTokenName = _chunkX3I5XWARcjs.getTokenName; exports.getUsdt = _chunkX3I5XWARcjs.getUsdt; exports.isActiveIssuer = _chunkX3I5XWARcjs.isActiveIssuer; exports.isMinter = _chunkX3I5XWARcjs.isMinter; exports.isPaymasterConfigured = isPaymasterConfigured; exports.issuerRegistryAbi = _chunkS2XRFFP3cjs.issuerRegistryAbi; exports.mintRequestTypes = _chunkCWP4PFOPcjs.mintRequestTypes; exports.mintingOracleAbi = _chunkS2XRFFP3cjs.mintingOracleAbi; exports.parseLoginMessage = _chunkGWLEEXM4cjs.parseLoginMessage; exports.permit2Abi = _chunkIPXARZ6Fcjs.permit2Abi; exports.pointTokenAbi = _chunkS2XRFFP3cjs.pointTokenAbi; exports.pointTokenFactoryAbi = _chunkFGKLDKJKcjs.pointTokenFactoryAbi; exports.quoteBestRoute = _chunkCAE2BVKIcjs.quoteBestRoute; exports.quoteExactInput = _chunkCAE2BVKIcjs.quoteExactInput; exports.quoteExactInputSingle = _chunkCAE2BVKIcjs.quoteExactInputSingle; exports.rawCallOp = _chunkCXFUP2YKcjs.rawCallOp; exports.receiverConsentTypes = _chunkCWP4PFOPcjs.receiverConsentTypes; exports.relayAbi = _chunk27V32VNMcjs.relayAbi; exports.setPaymasterConfig = setPaymasterConfig; exports.signBurnConsent = signMockBurnConsent; exports.signMintRequest = _chunkBNTVOKYTcjs.signMintRequest; exports.signMintRequestV2 = signMockMintRequestV2; exports.signReceiverConsent = _chunkBNTVOKYTcjs.signReceiverConsent; exports.simulateMintAndSwap = _chunkL74CZAVQcjs.simulateMintAndSwap; exports.simulateSwap = _chunkCXFUP2YKcjs.simulateSwap; exports.universalRouterAbi = _chunkIPXARZ6Fcjs.universalRouterAbi; exports.v4QuoterAbi = _chunkCL3QSI4Ocjs.v4QuoterAbi; exports.verifyBurnConsent = verifyMockBurnConsent; exports.verifyLoginMessage = _chunkGWLEEXM4cjs.verifyLoginMessage; exports.verifyMintCap = _chunkX3I5XWARcjs.verifyMintCap; exports.verifyMintRequest = _chunkBNTVOKYTcjs.verifyMintRequest; exports.verifyMintRequestV2 = verifyMockMintRequestV2; exports.verifyReceiverConsent = _chunkBNTVOKYTcjs.verifyReceiverConsent;
793
+
794
+
795
+
796
+
797
+
798
+ exports.ApiError = _chunk5QJZT7VBcjs.ApiError; exports.BATCH_EXECUTOR_ABI = _chunkCXFUP2YKcjs.BATCH_EXECUTOR_ABI; exports.BATCH_EXECUTOR_ADDRESS_BASE_MAINNET = MOCK_BATCH_EXECUTOR_ADDRESS_BASE_MAINNET; exports.BATCH_EXECUTOR_ADDRESS_BASE_SEPOLIA = MOCK_BATCH_EXECUTOR_ADDRESS_BASE_SEPOLIA; exports.BURN_CONSENT_TYPES = MOCK_BURN_CONSENT_TYPES; exports.COMMON_POOLS = _chunkCWP4PFOPcjs.COMMON_POOLS; exports.COMMON_TOKENS = _chunkCWP4PFOPcjs.COMMON_TOKENS; exports.CONTRACT_ADDRESSES = MOCK_ADDRESSES; exports.ConfigurationError = _chunk5QJZT7VBcjs.ConfigurationError; exports.ENTRY_POINT_V07 = ENTRY_POINT_V07; exports.MINT_REQUEST_V2_TYPES = MOCK_MINT_REQUEST_V2_TYPES; exports.POINT_TOKEN_POOLS = _chunkCWP4PFOPcjs.POINT_TOKEN_POOLS; exports.POINT_TOKEN_V2_ABI = MOCK_POINT_TOKEN_V2_ABI; exports.PafiSDK = PafiSDK; exports.PafiSDKError = _chunk5QJZT7VBcjs.PafiSDKError; exports.RELAYER_V2_ABI = MOCK_RELAYER_V2_ABI; exports.RELAYER_V2_MINT_SELECTOR = MOCK_RELAYER_V2_MINT_SELECTOR; exports.SETTLE_ALL = _chunkCXFUP2YKcjs.SETTLE_ALL; exports.SUPPORTED_CHAINS = _chunkCWP4PFOPcjs.SUPPORTED_CHAINS; exports.SWAP_EXACT_IN = _chunkCXFUP2YKcjs.SWAP_EXACT_IN; exports.SigningError = _chunk5QJZT7VBcjs.SigningError; exports.SimulationError = _chunk5QJZT7VBcjs.SimulationError; exports.TAKE_ALL = _chunkCXFUP2YKcjs.TAKE_ALL; exports.UNIVERSAL_ROUTER_ADDRESSES = _chunkCWP4PFOPcjs.UNIVERSAL_ROUTER_ADDRESSES; exports.V4_QUOTER_ADDRESSES = _chunkCWP4PFOPcjs.V4_QUOTER_ADDRESSES; exports.V4_SWAP = _chunkCXFUP2YKcjs.V4_SWAP; exports.ZERO_VALUE = ZERO_VALUE; exports._resetPaymasterConfigForTests = _resetPaymasterConfigForTests; exports.assembleUserOperation = _chunkCXFUP2YKcjs.assembleUserOperation; exports.buildAllPaths = _chunkCAE2BVKIcjs.buildAllPaths; exports.buildBurnConsentTypedData = buildMockBurnConsentTypedData; exports.buildDomain = _chunkBNTVOKYTcjs.buildDomain; exports.buildErc20ApprovalCalldata = _chunkCXFUP2YKcjs.buildErc20ApprovalCalldata; exports.buildMintRequestTypedData = _chunkBNTVOKYTcjs.buildMintRequestTypedData; exports.buildMintRequestV2TypedData = buildMockMintRequestV2TypedData; exports.buildPartialUserOperation = _chunkCXFUP2YKcjs.buildPartialUserOperation; exports.buildPermit2ApprovalCalldata = _chunkCXFUP2YKcjs.buildPermit2ApprovalCalldata; exports.buildReceiverConsentTypedData = _chunkBNTVOKYTcjs.buildReceiverConsentTypedData; exports.buildRelaySwapParams = _chunkL74CZAVQcjs.buildRelaySwapParams; exports.buildSwapFromQuote = _chunkCXFUP2YKcjs.buildSwapFromQuote; exports.buildSwapWithGasDeduction = _chunkCXFUP2YKcjs.buildSwapWithGasDeduction; exports.buildUniversalRouterExecuteArgs = _chunkCXFUP2YKcjs.buildUniversalRouterExecuteArgs; exports.buildV4SwapInput = _chunkCXFUP2YKcjs.buildV4SwapInput; exports.checkAllowance = _chunkCXFUP2YKcjs.checkAllowance; exports.checkEthAndBranch = checkEthAndBranch; exports.combineRoutes = _chunkCAE2BVKIcjs.combineRoutes; exports.createLoginMessage = _chunkGWLEEXM4cjs.createLoginMessage; exports.decodeExtData = _chunkL74CZAVQcjs.decodeExtData; exports.decodeMintAndSwap = _chunkL74CZAVQcjs.decodeMintAndSwap; exports.encodeBatchExecute = _chunkCXFUP2YKcjs.encodeBatchExecute; exports.encodeExtData = _chunkL74CZAVQcjs.encodeExtData; exports.encodeMintAndSwap = _chunkL74CZAVQcjs.encodeMintAndSwap; exports.erc20Abi = _chunkIPXARZ6Fcjs.erc20Abi; exports.erc20ApproveOp = _chunkCXFUP2YKcjs.erc20ApproveOp; exports.erc20BurnOp = _chunkCXFUP2YKcjs.erc20BurnOp; exports.erc20TransferOp = _chunkCXFUP2YKcjs.erc20TransferOp; exports.findBestQuote = _chunkCAE2BVKIcjs.findBestQuote; exports.getContractAddresses = getMockAddresses; exports.getIssuer = _chunkX3I5XWARcjs.getIssuer2; exports.getMintRequestNonce = _chunkX3I5XWARcjs.getMintRequestNonce; exports.getPafiWebModalAdapter = getPafiWebModalAdapter; exports.getPaymasterConfig = getPaymasterConfig; exports.getPointTokenBalance = _chunkX3I5XWARcjs.getPointTokenBalance; exports.getPointTokenIssuer = _chunkX3I5XWARcjs.getPointTokenIssuer; exports.getPointTokenIssuerAddress = _chunkX3I5XWARcjs.getIssuer; exports.getReceiverConsentNonce = _chunkX3I5XWARcjs.getReceiverConsentNonce; exports.getTokenName = _chunkX3I5XWARcjs.getTokenName; exports.getUsdt = _chunkX3I5XWARcjs.getUsdt; exports.isActiveIssuer = _chunkX3I5XWARcjs.isActiveIssuer; exports.isMinter = _chunkX3I5XWARcjs.isMinter; exports.isPaymasterConfigured = isPaymasterConfigured; exports.issuerRegistryAbi = _chunkS2XRFFP3cjs.issuerRegistryAbi; exports.mintRequestTypes = _chunkCWP4PFOPcjs.mintRequestTypes; exports.mintingOracleAbi = _chunkS2XRFFP3cjs.mintingOracleAbi; exports.openPafiWebModal = openPafiWebModal; exports.openWebPopup = openWebPopup; exports.parseLoginMessage = _chunkGWLEEXM4cjs.parseLoginMessage; exports.permit2Abi = _chunkIPXARZ6Fcjs.permit2Abi; exports.pointTokenAbi = _chunkS2XRFFP3cjs.pointTokenAbi; exports.pointTokenFactoryAbi = _chunkFGKLDKJKcjs.pointTokenFactoryAbi; exports.quoteBestRoute = _chunkCAE2BVKIcjs.quoteBestRoute; exports.quoteExactInput = _chunkCAE2BVKIcjs.quoteExactInput; exports.quoteExactInputSingle = _chunkCAE2BVKIcjs.quoteExactInputSingle; exports.rawCallOp = _chunkCXFUP2YKcjs.rawCallOp; exports.receiverConsentTypes = _chunkCWP4PFOPcjs.receiverConsentTypes; exports.relayAbi = _chunk27V32VNMcjs.relayAbi; exports.setPafiWebModalAdapter = setPafiWebModalAdapter; exports.setPaymasterConfig = setPaymasterConfig; exports.signBurnConsent = signMockBurnConsent; exports.signMintRequest = _chunkBNTVOKYTcjs.signMintRequest; exports.signMintRequestV2 = signMockMintRequestV2; exports.signReceiverConsent = _chunkBNTVOKYTcjs.signReceiverConsent; exports.simulateMintAndSwap = _chunkL74CZAVQcjs.simulateMintAndSwap; exports.simulateSwap = _chunkCXFUP2YKcjs.simulateSwap; exports.universalRouterAbi = _chunkIPXARZ6Fcjs.universalRouterAbi; exports.v4QuoterAbi = _chunkCL3QSI4Ocjs.v4QuoterAbi; exports.verifyBurnConsent = verifyMockBurnConsent; exports.verifyLoginMessage = _chunkGWLEEXM4cjs.verifyLoginMessage; exports.verifyMintCap = _chunkX3I5XWARcjs.verifyMintCap; exports.verifyMintRequest = _chunkBNTVOKYTcjs.verifyMintRequest; exports.verifyMintRequestV2 = verifyMockMintRequestV2; exports.verifyReceiverConsent = _chunkBNTVOKYTcjs.verifyReceiverConsent; exports.webPopupAdapter = webPopupAdapter;
673
799
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/phitran/Pacific-Finance/pafi-backend/pafi-sdk/packages/core/dist/index.cjs","../src/index.ts","../src/userop/types.ts","../src/paymaster/config.ts","../src/utils/checkEthAndBranch.ts","../src/contracts/mocks/relayerV2.mock.ts","../src/contracts/mocks/pointTokenV2.mock.ts","../src/contracts/mocks/batchExecutor.mock.ts","../src/contracts/mocks/addresses.mock.ts","../src/contracts/mocks/eip712/mintRequestV2.mock.ts","../src/contracts/mocks/eip712/burnConsent.mock.ts"],"names":["parseAbi","parseSignature","recoverTypedDataAddress"],"mappings":"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F,IAAA;AACF,EAAA;AAAA;AAAA;AAAA;AAM2D,EAAA;AACtB,IAAA;AACrC,EAAA;AAEyE,EAAA;AACtC,IAAA;AACnC,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAkBsB,EAAA;AACb,IAAA;AACgB,MAAA;AACD,MAAA;AACpB,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgBqC,EAAA;AACH,IAAA;AAClC,EAAA;AAAA;AAAA;AAAA;AAAA;AAW6C,EAAA;AACT,IAAA;AACpC,EAAA;AAAA;AAAA;AAAA;AAAA;AAM4D,EAAA;AACd,IAAA;AAC9C,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAoB6B,EAAA;AACpB,IAAA;AACgB,MAAA;AACrB,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAkBiC,EAAA;AACxB,IAAA;AACgB,MAAA;AACrB,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAkBmB,EAAA;AACiB,IAAA;AACE,IAAA;AACb,IAAA;AACT,IAAA;AACiD,MAAA;AAC/D,IAAA;AAC0E,IAAA;AAC5E,EAAA;AAAA;AAGsD,EAAA;AAClB,IAAA;AACX,IAAA;AACT,IAAA;AACiD,MAAA;AAC/D,IAAA;AAC8C,IAAA;AAChD,EAAA;AACF;AD+JkH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/Users/phitran/Pacific-Finance/pafi-backend/pafi-sdk/packages/core/dist/index.cjs","sourcesContent":[null,"import { createPublicClient, http } from \"viem\";\nimport type { Address, Hex, PublicClient, WalletClient } from \"viem\";\n\n// -------------------------------------------------------------------------\n// Re-export all sub-modules\n// -------------------------------------------------------------------------\nexport * from \"./types\";\nexport * from \"./constants\";\nexport * from \"./errors\";\nexport * from \"./abi/index\";\nexport * from \"./eip712/index\";\nexport * from \"./relay/index\";\nexport * from \"./contract/index\";\nexport * from \"./quoting/index\";\nexport * from \"./swap/index\";\nexport * from \"./auth/index\";\n\n// v1.4 — Account Abstraction primitives (EIP-7702 + ERC-4337 v0.7)\nexport * from \"./userop/index\";\nexport * from \"./paymaster/index\";\nexport * from \"./utils/index\";\n\n// v1.4 — Contract ABIs + addresses + EIP-712 v2 types.\n// MOCKED until SC team ships real ABIs. See `src/contracts/mocks/README.md`.\n// Consumers: `import { RELAYER_V2_ABI, buildBurnConsentTypedData, ... } from '@pafi-dev/core'`\n// The re-export swaps mock → real in one file when SC delivers.\nexport * from \"./contracts/index\";\n\n// -------------------------------------------------------------------------\n// Internal imports for PafiSDK class\n// -------------------------------------------------------------------------\nimport { buildMintRequestTypedData, signMintRequest, verifyMintRequest } from \"./eip712/mintRequest\";\nimport {\n buildReceiverConsentTypedData,\n signReceiverConsent,\n verifyReceiverConsent,\n} from \"./eip712/receiverConsent\";\nimport {\n getMintRequestNonce,\n getReceiverConsentNonce,\n getTokenName,\n} from \"./contract/pointToken\";\nimport {\n encodeMintAndSwap,\n decodeMintAndSwap,\n encodeExtData,\n buildRelaySwapParams,\n} from \"./relay/calldata\";\nimport { findBestQuote } from \"./quoting/quote\";\nimport {\n buildSwapFromQuote,\n buildUniversalRouterExecuteArgs,\n} from \"./swap/universalRouter\";\nimport { simulateMintAndSwap } from \"./relay/simulate\";\nimport { simulateSwap } from \"./swap/simulate\";\nimport type { SimulationResult } from \"./relay/simulate\";\nimport type { SwapSimulationResult } from \"./swap/simulate\";\nimport { createLoginMessage } from \"./auth/loginMessage\";\nimport type { LoginMessageParams } from \"./auth/types\";\nimport { ConfigurationError } from \"./errors\";\nimport type {\n BestQuote,\n EIP712Signature,\n MintParams,\n MintRequest,\n PafiSDKConfig,\n PathKey,\n PointTokenDomainConfig,\n PoolKey,\n QuoteResult,\n ReceiverConsent,\n SignatureVerification,\n SwapParams,\n} from \"./types\";\n\n// -------------------------------------------------------------------------\n// PafiSDK — convenience class wrapping all contract + crypto primitives.\n//\n// This class is HTTP-client-free on purpose. It covers signing, verifying,\n// contract reads, and calldata encoding — the things that need a signer\n// or a provider but no HTTP layer. The issuer backend defines its own HTTP\n// contract via `@pafi/issuer`; frontends build `fetch()` calls against\n// those types directly.\n// -------------------------------------------------------------------------\n\nexport class PafiSDK {\n private _pointTokenAddress?: Address;\n private _relayContractAddress?: Address;\n private _signer?: WalletClient;\n private _provider?: PublicClient;\n private _chainId?: number;\n\n constructor(config: PafiSDKConfig) {\n this._pointTokenAddress = config.pointTokenAddress;\n this._relayContractAddress = config.relayContractAddress;\n this._signer = config.signer;\n this._chainId = config.chainId;\n\n if (config.provider) {\n this._provider = config.provider;\n } else if (config.rpcUrl) {\n this._provider = createPublicClient({\n transport: http(config.rpcUrl),\n });\n }\n }\n\n // -------------------------------------------------------------------------\n // Setters\n // -------------------------------------------------------------------------\n\n setPointTokenAddress(address: Address): void {\n this._pointTokenAddress = address;\n }\n\n setRelayContractAddress(address: Address): void {\n this._relayContractAddress = address;\n }\n\n setSigner(signer: WalletClient): void {\n this._signer = signer;\n }\n\n setProvider(provider: PublicClient): void {\n this._provider = provider;\n }\n\n // -------------------------------------------------------------------------\n // Private guards\n // -------------------------------------------------------------------------\n\n private requirePointToken(): Address {\n if (!this._pointTokenAddress) {\n throw new ConfigurationError(\"pointTokenAddress not set\");\n }\n return this._pointTokenAddress;\n }\n\n private requireProvider(): PublicClient {\n if (!this._provider) {\n throw new ConfigurationError(\"provider not set\");\n }\n return this._provider;\n }\n\n private requireSigner(): WalletClient {\n if (!this._signer) {\n throw new ConfigurationError(\"signer not set\");\n }\n return this._signer;\n }\n\n private requireChainId(): number {\n if (this._chainId === undefined) {\n throw new ConfigurationError(\"chainId not set\");\n }\n return this._chainId;\n }\n\n // -------------------------------------------------------------------------\n // Domain\n // -------------------------------------------------------------------------\n\n async getDomain(): Promise<PointTokenDomainConfig> {\n const provider = this.requireProvider();\n const pointToken = this.requirePointToken();\n const chainId = this.requireChainId();\n const name = await getTokenName(provider, pointToken);\n return { name, verifyingContract: pointToken, chainId };\n }\n\n // -------------------------------------------------------------------------\n // EIP-712 — delegates to pure functions\n // -------------------------------------------------------------------------\n\n /**\n * Build the EIP-712 typed data for a MintRequest.\n * Pass the result to any external signer (Privy, WalletConnect, etc.).\n */\n async buildMintRequestTypedData(message: MintRequest) {\n const domain = await this.getDomain();\n return buildMintRequestTypedData(domain, message);\n }\n\n /**\n * Build the EIP-712 typed data for a ReceiverConsent.\n * Pass the result to any external signer (Privy, WalletConnect, etc.).\n */\n async buildReceiverConsentTypedData(message: ReceiverConsent) {\n const domain = await this.getDomain();\n return buildReceiverConsentTypedData(domain, message);\n }\n\n async signMintRequest(message: MintRequest): Promise<EIP712Signature> {\n const domain = await this.getDomain();\n return signMintRequest(this.requireSigner(), domain, message);\n }\n\n async verifyMintRequest(\n message: MintRequest,\n signature: Hex,\n expectedMinter: Address,\n ): Promise<SignatureVerification> {\n const domain = await this.getDomain();\n return verifyMintRequest(domain, message, signature, expectedMinter);\n }\n\n async signReceiverConsent(\n message: ReceiverConsent,\n ): Promise<EIP712Signature> {\n const domain = await this.getDomain();\n return signReceiverConsent(this.requireSigner(), domain, message);\n }\n\n async verifyReceiverConsent(\n message: ReceiverConsent,\n signature: Hex,\n expectedReceiver: Address,\n ): Promise<SignatureVerification> {\n const domain = await this.getDomain();\n return verifyReceiverConsent(domain, message, signature, expectedReceiver);\n }\n\n // -------------------------------------------------------------------------\n // Contract reads\n // -------------------------------------------------------------------------\n\n async getMintRequestNonce(receiver: Address): Promise<bigint> {\n return getMintRequestNonce(\n this.requireProvider(),\n this.requirePointToken(),\n receiver,\n );\n }\n\n async getReceiverConsentNonce(receiver: Address): Promise<bigint> {\n return getReceiverConsentNonce(\n this.requireProvider(),\n this.requirePointToken(),\n receiver,\n );\n }\n\n // -------------------------------------------------------------------------\n // Relay calldata — delegates to pure functions\n // -------------------------------------------------------------------------\n\n encodeMintAndSwap(mint: MintParams, swap: SwapParams): Hex {\n return encodeMintAndSwap(mint, swap);\n }\n\n decodeMintAndSwap(calldata: Hex): { mint: MintParams; swap: SwapParams } {\n return decodeMintAndSwap(calldata);\n }\n\n // -------------------------------------------------------------------------\n // Quoting — delegates to pure functions\n // -------------------------------------------------------------------------\n\n /**\n * Find the best swap route from `tokenIn` to `tokenOut`.\n * Merges `pools` with COMMON_POOLS for the configured chain, builds all\n * paths, and quotes via a single multicall RPC call.\n */\n async findBestQuote(\n tokenIn: Address,\n tokenOut: Address,\n exactAmount: bigint,\n pools: PoolKey[] = [],\n quoterAddress?: Address,\n maxHops?: number,\n ): Promise<BestQuote> {\n return findBestQuote(\n this.requireProvider(),\n this.requireChainId(),\n tokenIn,\n tokenOut,\n exactAmount,\n pools,\n quoterAddress,\n maxHops,\n );\n }\n\n // -------------------------------------------------------------------------\n // Swap building — delegates to pure functions\n // -------------------------------------------------------------------------\n\n /**\n * Build UniversalRouter execute args from a quote result.\n * The caller provides `minAmountOut` after applying their own slippage.\n */\n buildSwapFromQuote(params: {\n quote: QuoteResult;\n currencyIn: Address;\n currencyOut: Address;\n amountIn: bigint;\n minAmountOut: bigint;\n }): { commands: Hex; inputs: Hex[] } {\n return buildSwapFromQuote(params);\n }\n\n /**\n * Build Relay SwapParams + extData from a quote result.\n * Returns ready-to-use args for `Relay.mintAndSwap`.\n */\n buildRelaySwapParams(params: {\n quote: { path: PathKey[] };\n minAmountOut: bigint;\n feeInUsdt: bigint;\n deadline: bigint;\n }): { swapParams: SwapParams; extData: Hex } {\n return buildRelaySwapParams(params);\n }\n\n /**\n * Encode extData for ReceiverConsent and MintParams.\n * extData = abi.encode(minAmountOut, feeInUsdt)\n */\n encodeExtData(minAmountOut: bigint, feeInUsdt: bigint): Hex {\n return encodeExtData(minAmountOut, feeInUsdt);\n }\n\n // -------------------------------------------------------------------------\n // Simulation — dry-run via eth_call (no gas spent)\n // -------------------------------------------------------------------------\n\n /**\n * Simulate a Relay.mintAndSwap call. Catches reverts (bad signatures,\n * expired deadlines, insufficient mint cap, pool errors) before submitting.\n *\n * @param relayAddress - Relay contract address\n * @param mint - MintParams with real signatures\n * @param swap - SwapParams (path + deadline)\n * @param from - Relayer address that will submit the tx\n */\n async simulateMintAndSwap(\n relayAddress: Address,\n mint: MintParams,\n swap: SwapParams,\n from: Address,\n ): Promise<SimulationResult> {\n return simulateMintAndSwap(\n this.requireProvider(),\n relayAddress,\n mint,\n swap,\n from,\n );\n }\n\n /**\n * Simulate a UniversalRouter.execute swap call. Catches reverts (bad\n * approvals, insufficient balance, pool errors) before submitting.\n *\n * @param routerAddress - UniversalRouter contract address\n * @param commands - Packed command bytes (from buildSwapFromQuote)\n * @param inputs - Encoded inputs (from buildSwapFromQuote)\n * @param deadline - Unix timestamp\n * @param from - Address that will execute the swap\n */\n async simulateSwap(\n routerAddress: Address,\n commands: Hex,\n inputs: Hex[],\n deadline: bigint,\n from: Address,\n ): Promise<SwapSimulationResult> {\n return simulateSwap(\n this.requireProvider(),\n routerAddress,\n commands,\n inputs,\n deadline,\n from,\n );\n }\n\n // -------------------------------------------------------------------------\n // Auth — EIP-4361 login helpers (offline, stateless)\n //\n // These are convenience wrappers around the pure functions in\n // `./auth/loginMessage.ts`. They do NOT hit the network — the caller is\n // responsible for fetching the nonce, POSTing the signed message, and\n // storing the JWT returned by the issuer backend.\n // -------------------------------------------------------------------------\n\n /**\n * Build an EIP-4361 login message for the current signer + chain. The\n * caller supplies the issuer-specific fields (domain, nonce, uri); the SDK\n * fills in the wallet address and chain id from its own config.\n */\n async createLoginMessage(\n params: Omit<LoginMessageParams, \"address\" | \"chainId\">,\n ): Promise<string> {\n const signer = this.requireSigner();\n const chainId = this.requireChainId();\n const account = signer.account;\n if (!account) {\n throw new ConfigurationError(\"signer has no account attached\");\n }\n return createLoginMessage({ ...params, address: account.address, chainId });\n }\n\n /** Sign a login message string with the current signer (personal_sign) */\n async signLoginMessage(message: string): Promise<Hex> {\n const signer = this.requireSigner();\n const account = signer.account;\n if (!account) {\n throw new ConfigurationError(\"signer has no account attached\");\n }\n return signer.signMessage({ account, message });\n }\n}\n","import type { Address, Hex } from \"viem\";\n\n/**\n * ERC-4337 v0.7 EntryPoint standard address (deployed deterministically\n * at the same address across chains).\n * https://eips.ethereum.org/EIPS/eip-4337\n */\nexport const ENTRY_POINT_V07: Address =\n \"0x0000000071727De22E5E9d8BAf0edAc6f37da032\";\n\n/**\n * A single call inside a batch. `BatchExecutor.execute(Call[])` iterates\n * and invokes each one. When the batch runs via EIP-7702 delegation,\n * `msg.sender` for each call is the user's EOA.\n */\nexport interface Operation {\n target: Address;\n value: bigint;\n data: Hex;\n}\n\n/**\n * Paymaster fields attached to a UserOperation. Populated by the\n * Coinbase Paymaster (via PAFI Backend proxy) before the user signs.\n */\nexport interface PaymasterFields {\n paymaster: Address;\n paymasterData: Hex;\n paymasterVerificationGasLimit: bigint;\n paymasterPostOpGasLimit: bigint;\n}\n\n/**\n * Partial UserOp used during preparation — before paymaster fields are\n * attached or the user signs.\n */\nexport interface PartialUserOperation {\n sender: Address;\n nonce: bigint;\n callData: Hex;\n callGasLimit: bigint;\n verificationGasLimit: bigint;\n preVerificationGas: bigint;\n maxFeePerGas: bigint;\n maxPriorityFeePerGas: bigint;\n}\n\n/**\n * Full ERC-4337 v0.7 UserOperation, ready for bundler submission.\n */\nexport interface UserOperation extends PartialUserOperation {\n paymaster: Address;\n paymasterData: Hex;\n paymasterVerificationGasLimit: bigint;\n paymasterPostOpGasLimit: bigint;\n signature: Hex;\n}\n\n/**\n * Receipt returned by a bundler after a UserOp lands on-chain.\n */\nexport interface UserOpReceipt {\n userOpHash: Hex;\n success: boolean;\n txHash: Hex;\n blockNumber: bigint;\n gasUsed: bigint;\n /** Effective gas cost paid (wei). */\n actualGasCost: bigint;\n}\n\n/**\n * Sentinel operation value used in tests + docs when `Operation.value`\n * is irrelevant (ERC-20 transfers, for example).\n */\nexport const ZERO_VALUE = 0n;\n","import type { PaymasterConfig } from \"./types\";\n\n/**\n * Module-level paymaster config. Read by batch builders (via\n * `getPaymasterConfig()`) and by `@pafi/issuer` `RelayService` when\n * building UserOps.\n *\n * Consumers call `setPaymasterConfig()` once at application boot. All\n * subsequent helpers that need the feeRecipient / issuer identity /\n * backend URL pull from here.\n *\n * This is global state by design — making every batch builder take a\n * config param would clutter every call site. The alternative (a\n * context/service) has more ceremony than this flow justifies.\n */\nlet _config: PaymasterConfig | null = null;\n\n/**\n * Set the application-wide paymaster config. Safe to call multiple\n * times (later calls override earlier). Throws if required fields\n * are missing.\n */\nexport function setPaymasterConfig(config: PaymasterConfig): void {\n if (!config.pafiBackendUrl) {\n throw new Error(\"setPaymasterConfig: pafiBackendUrl is required\");\n }\n if (!config.issuerId) {\n throw new Error(\"setPaymasterConfig: issuerId is required\");\n }\n if (!config.apiKey) {\n throw new Error(\"setPaymasterConfig: apiKey is required\");\n }\n if (!config.feeRecipient) {\n throw new Error(\"setPaymasterConfig: feeRecipient is required\");\n }\n _config = { ...config };\n}\n\n/**\n * Get the current paymaster config. Throws if `setPaymasterConfig()`\n * has not been called yet — this surfaces boot-order bugs early\n * instead of failing with \"paymaster.feeRecipient is undefined\" at\n * the point of use.\n */\nexport function getPaymasterConfig(): PaymasterConfig {\n if (!_config) {\n throw new Error(\n \"PaymasterConfig not initialized — call setPaymasterConfig() at application boot before invoking any batch builder\",\n );\n }\n return _config;\n}\n\n/** Test helper — clear the singleton. */\nexport function _resetPaymasterConfigForTests(): void {\n _config = null;\n}\n\n/** Check whether paymaster config has been initialized. */\nexport function isPaymasterConfigured(): boolean {\n return _config !== null;\n}\n","import type { Address, PublicClient } from \"viem\";\n\n/**\n * Submission path chosen by `checkEthAndBranch`.\n * - `normal`: initiator has enough ETH; submit via `walletClient.writeContract`\n * or a plain `eth_sendRawTransaction`. No paymaster round-trip.\n * - `paymaster`: initiator doesn't have enough ETH; wrap the batch as a\n * UserOperation and route through PAFI Backend → Coinbase Paymaster\n * → Bundler.\n */\nexport type SubmissionPath = \"normal\" | \"paymaster\";\n\nexport interface CheckEthAndBranchParams {\n /** viem PublicClient bound to the target chain. */\n client: PublicClient;\n /** The address whose ETH balance we check. */\n initiator: Address;\n /** Estimated gas cost in wei for the upcoming tx. */\n estimatedGasWei: bigint;\n /**\n * Optional safety margin multiplier (basis points). Defaults to\n * 11_000 (110%) — the initiator needs 10% above the estimate to\n * qualify for the normal path. Prevents edge cases where gas price\n * spikes between estimation and submission cause a \"has enough\"\n * decision to fail at broadcast time.\n */\n marginBps?: number;\n}\n\nconst DEFAULT_MARGIN_BPS = 11_000;\n\n/**\n * Step 3 of the Generalized Flow ([SPONSORED_PATH_FLOW.md §2]):\n * choose between the normal path (initiator pays ETH directly) and the\n * paymaster path (bundler + Coinbase Paymaster).\n *\n * Intentionally synchronous in spirit — the only network call is\n * `getBalance`. Callers can parallelize it with other reads.\n */\nexport async function checkEthAndBranch(\n params: CheckEthAndBranchParams,\n): Promise<SubmissionPath> {\n const marginBps = params.marginBps ?? DEFAULT_MARGIN_BPS;\n const required =\n (params.estimatedGasWei * BigInt(marginBps)) / 10_000n;\n\n const balance = await params.client.getBalance({\n address: params.initiator,\n });\n\n return balance >= required ? \"normal\" : \"paymaster\";\n}\n","import { parseAbi } from \"viem\";\nimport type { Address } from \"viem\";\n\n/**\n * ⚠️ MOCK — Relayer v2 `mint()` ABI for the v1.4 sponsored flow.\n *\n * Signature guess based on stakeholder memo (`REQUIREMENTS_V2.md §3`):\n *\n * mint(\n * MintRequest request, // EIP-712 signed by user + issuer\n * Signature userSig, // v, r, s\n * Signature issuerSig, // v, r, s\n * uint256 feeAmount, // PT units to split off to feeRecipient\n * address feeRecipient // typically operator or fee collector\n * )\n *\n * Behaviour expected:\n * - Verify user + issuer EIP-712 signatures against `request`\n * - Mint `request.amount` PT to `request.to`\n * - Atomic transfer `feeAmount` from `request.to` → `feeRecipient`\n * (net mint = `amount - feeAmount`)\n * - Increment the on-chain nonce to prevent replay\n *\n * When SC team publishes the real ABI, drop it in under\n * `src/contracts/real/relayerV2.abi.ts` and flip the export in\n * `src/contracts/index.ts` — call sites unchanged.\n *\n * See [SC_MOCK_PLAN.md §2.1] for the rationale + swap checklist.\n */\n\nexport const MOCK_RELAYER_V2_ABI = parseAbi([\n \"function mint((address to, uint256 amount, uint256 feeAmount, address feeRecipient, uint256 nonce, uint256 deadline, bytes extData) request, (uint8 v, bytes32 r, bytes32 s) userSig, (uint8 v, bytes32 r, bytes32 s) issuerSig) external\",\n // View functions we'll likely want (nonce getter is standard)\n \"function mintRequestNonce(address user) external view returns (uint256)\",\n \"event Minted(address indexed user, uint256 amount, uint256 feeAmount, address indexed feeRecipient, bytes32 requestHash)\",\n] as const);\n\n/**\n * Calldata function selector for `mint((...),(...),(...))` as encoded\n * by viem against {@link MOCK_RELAYER_V2_ABI}. Callers compare against\n * this to sanity-check decoded UserOp inner calls.\n *\n * Recompute when the real ABI lands — almost certainly differs.\n */\nexport const MOCK_RELAYER_V2_MINT_SELECTOR = \"0xMOCKED__\" as const;\n\n/**\n * Struct shape that matches {@link MOCK_RELAYER_V2_ABI}. Exported so\n * builders can accept a typed input without `as const` gymnastics.\n */\nexport interface MockMintRequestV2 {\n to: Address;\n amount: bigint;\n feeAmount: bigint;\n feeRecipient: Address;\n nonce: bigint;\n deadline: bigint;\n extData: `0x${string}`;\n}\n\nexport interface MockSignatureStruct {\n v: number;\n r: `0x${string}`;\n s: `0x${string}`;\n}\n","import { parseAbi } from \"viem\";\n\n/**\n * ⚠️ MOCK — `PointToken` v1.4 burn surface.\n *\n * Two variants mocked — SC team will pick ONE before stable; the other\n * gets deleted during Phase B swap.\n *\n * Variant A (simpler, most likely):\n * burn(uint256 amount)\n * - burns from `msg.sender`\n * - caller handles authorization (the user is `msg.sender` via\n * EIP-7702 delegation, so `msg.sender == user`)\n *\n * Variant B (signature-based, if SC prefers explicit consent on-chain):\n * burnWithSig(BurnConsent consent, Signature sig)\n * - verifies EIP-712 signature before burning\n * - used when the caller isn't the user (e.g. a relayer burns on\n * behalf of the user)\n *\n * Either way the ERC-20 emits `Transfer(from, address(0), amount)` so\n * `BurnIndexer` semantics don't change.\n */\nexport const MOCK_POINT_TOKEN_V2_ABI = parseAbi([\n // Variant A\n \"function burn(uint256 amount) external\",\n // Variant B\n \"function burnWithSig((address user, address pointToken, uint256 amount, uint256 nonce, uint256 deadline) consent, (uint8 v, bytes32 r, bytes32 s) sig) external\",\n // Standard reads we always need\n \"function balanceOf(address user) external view returns (uint256)\",\n \"function burnNonce(address user) external view returns (uint256)\",\n // ERC-20 Transfer event (inherited) — BurnIndexer filters `to = 0x0`\n \"event Transfer(address indexed from, address indexed to, uint256 value)\",\n] as const);\n","import { parseAbi } from \"viem\";\nimport type { Address } from \"viem\";\n\n/**\n * ⚠️ MOCK — placeholder BatchExecutor address.\n *\n * Real deployment:\n * - Coinbase may pre-deploy a canonical BatchExecutor that we target\n * via EIP-7702 delegation (see blocker B2 in V1.4_V1.5_READINESS.md)\n * - Otherwise PAFI deploys a minimal one and publishes the address\n *\n * The ABI itself (`execute(Call[])`) is stable and lives under\n * `../../userop/batchExecute.ts` — that's the real one, unchanged\n * by the mock swap. Only the **address** is mocked here.\n *\n * Pattern: checksum address with hex \"DEAD0001\" suffix per chain so\n * it's obvious in logs which chain is using a mock.\n */\n\n// Base Sepolia (testnet)\nexport const MOCK_BATCH_EXECUTOR_ADDRESS_BASE_SEPOLIA =\n \"0x000000000000000000000000000000000000DE01\" as Address;\n\n// Base mainnet\nexport const MOCK_BATCH_EXECUTOR_ADDRESS_BASE_MAINNET =\n \"0x000000000000000000000000000000000000DE02\" as Address;\n\n// Re-export the real ABI from userop module (not mocked — ABI is stable)\nexport { BATCH_EXECUTOR_ABI } from \"../../userop/batchExecute\";\n","import type { Address } from \"viem\";\n\n/**\n * ⚠️ MOCK — per-chain contract addresses for the v1.4 sponsored flow.\n *\n * Real addresses come from SC team once they deploy Relayer v2 and\n * pick a BatchExecutor. Until then we use placeholders so the SDK\n * code path compiles + tests execute end-to-end.\n *\n * **Intentionally not merge-safe to mainnet** — callers that resolve\n * a mainnet address here get a `0x...DEAD` placeholder which will\n * revert at contract-level. That's the point: prod refuses to run\n * on mocks.\n *\n * Chain id map:\n * - 8453 Base mainnet\n * - 84532 Base Sepolia\n *\n * Swap workflow when SC delivers:\n * 1. Rename this file → `addresses.ts` under `src/contracts/real/`\n * 2. Fill real addresses per chain\n * 3. Update `src/contracts/index.ts` re-export\n * 4. Delete MOCK_* named exports\n */\n\nexport interface ContractAddresses {\n relayerV2: Address;\n pointToken: Address;\n batchExecutor: Address;\n usdt: Address;\n issuerRegistry: Address;\n mintingOracle: Address;\n}\n\nconst MOCK_PLACEHOLDER = (suffix: string): Address =>\n `0x000000000000000000000000000000000000${suffix.toLowerCase().padStart(4, \"0\")}` as Address;\n\nexport const MOCK_ADDRESSES: Record<number, ContractAddresses> = {\n // Base Sepolia — safe targets for integration tests (MockRelayer\n // fixtures deployed by Hardhat will override these at test time)\n 84532: {\n relayerV2: MOCK_PLACEHOLDER(\"de10\"),\n pointToken: MOCK_PLACEHOLDER(\"de11\"),\n batchExecutor: MOCK_PLACEHOLDER(\"de01\"),\n usdt: MOCK_PLACEHOLDER(\"de12\"),\n issuerRegistry: MOCK_PLACEHOLDER(\"de13\"),\n mintingOracle: MOCK_PLACEHOLDER(\"de14\"),\n },\n // Base mainnet — intentionally left as placeholder. Do NOT ship to\n // prod without swapping.\n 8453: {\n relayerV2: MOCK_PLACEHOLDER(\"dead\"),\n pointToken: MOCK_PLACEHOLDER(\"dead\"),\n batchExecutor: MOCK_PLACEHOLDER(\"dead\"),\n usdt: MOCK_PLACEHOLDER(\"dead\"),\n issuerRegistry: MOCK_PLACEHOLDER(\"dead\"),\n mintingOracle: MOCK_PLACEHOLDER(\"dead\"),\n },\n};\n\n/**\n * Lookup helper — throws if the chain isn't in the map so callers fail\n * loudly on misconfiguration instead of silently using `undefined`.\n */\nexport function getMockAddresses(chainId: number): ContractAddresses {\n const addrs = MOCK_ADDRESSES[chainId];\n if (!addrs) {\n throw new Error(\n `getMockAddresses: no mock addresses for chainId ${chainId}. ` +\n `Supported: ${Object.keys(MOCK_ADDRESSES).join(\", \")}`,\n );\n }\n return addrs;\n}\n","import {\n parseSignature,\n recoverTypedDataAddress,\n type Address,\n type Hex,\n type WalletClient,\n} from \"viem\";\nimport { buildDomain } from \"../../../eip712/domain\";\nimport type {\n EIP712Signature,\n PointTokenDomainConfig,\n SignatureVerification,\n} from \"../../../types\";\nimport type { MockMintRequestV2 } from \"../relayerV2.mock\";\n\n/**\n * ⚠️ MOCK — `MintRequest` v2 EIP-712 types.\n *\n * Extends v0.2.x `MintRequest` (4 fields) with three new fields needed\n * for the v1.4 sponsored flow:\n *\n * + feeAmount PT units taken from the mint for the operator\n * + feeRecipient where the fee goes (operator or treasury)\n * + extData arbitrary bytes for future extensions (swap path, etc.)\n *\n * When SC team confirms the real struct, drop this file's content into\n * the real builder and remove the MOCK_ prefix. The function signatures\n * below stay stable — only `MOCK_MINT_REQUEST_V2_TYPES` changes.\n */\nexport const MOCK_MINT_REQUEST_V2_TYPES = {\n MintRequest: [\n { name: \"to\", type: \"address\" },\n { name: \"amount\", type: \"uint256\" },\n { name: \"feeAmount\", type: \"uint256\" },\n { name: \"feeRecipient\", type: \"address\" },\n { name: \"nonce\", type: \"uint256\" },\n { name: \"deadline\", type: \"uint256\" },\n { name: \"extData\", type: \"bytes\" },\n ],\n} as const;\n\n/**\n * Build the typed-data envelope that any EIP-712 signer (viem, ethers,\n * Privy) can consume. Callers typically pass the result to\n * `walletClient.signTypedData()` or `privy.signTypedData()`.\n */\nexport function buildMockMintRequestV2TypedData(\n domain: PointTokenDomainConfig,\n message: MockMintRequestV2,\n) {\n return {\n domain: buildDomain(domain),\n types: MOCK_MINT_REQUEST_V2_TYPES,\n primaryType: \"MintRequest\" as const,\n message,\n };\n}\n\n/**\n * Sign a `MintRequest` v2 with a viem WalletClient — server-side flow\n * where the issuer holds the private key directly (KMS-backed signer\n * is the production path; see `@pafi-dev/issuer` KMSSignerAdapter).\n */\nexport async function signMockMintRequestV2(\n walletClient: WalletClient,\n domain: PointTokenDomainConfig,\n message: MockMintRequestV2,\n): Promise<EIP712Signature> {\n const serialized = await walletClient.signTypedData({\n account: walletClient.account!,\n domain: buildDomain(domain),\n types: MOCK_MINT_REQUEST_V2_TYPES,\n primaryType: \"MintRequest\",\n message,\n });\n\n const { v, r, s } = parseSignature(serialized);\n return {\n v: Number(v),\n r,\n s,\n serialized,\n };\n}\n\n/**\n * Verify a `MintRequest` v2 signature and check it came from the\n * expected signer (either user or issuer depending on which side is\n * being verified).\n */\nexport async function verifyMockMintRequestV2(\n domain: PointTokenDomainConfig,\n message: MockMintRequestV2,\n signature: Hex,\n expectedSigner: Address,\n): Promise<SignatureVerification> {\n const recoveredAddress = await recoverTypedDataAddress({\n domain: buildDomain(domain),\n types: MOCK_MINT_REQUEST_V2_TYPES,\n primaryType: \"MintRequest\",\n message,\n signature,\n });\n\n const isValid =\n recoveredAddress.toLowerCase() === expectedSigner.toLowerCase();\n\n return {\n isValid,\n recoveredAddress,\n };\n}\n","import {\n parseSignature,\n recoverTypedDataAddress,\n type Address,\n type Hex,\n type WalletClient,\n} from \"viem\";\nimport { buildDomain } from \"../../../eip712/domain\";\nimport type {\n EIP712Signature,\n PointTokenDomainConfig,\n SignatureVerification,\n} from \"../../../types\";\n\n/**\n * ⚠️ MOCK — `BurnConsent` EIP-712 type for the v1.4 reverse flow.\n *\n * New type entirely — v0.2.x had no burn-for-credit flow.\n *\n * User signs `BurnConsent` to authorize burning `amount` PT from their\n * wallet in exchange for an off-chain credit of (amount - gasFee).\n * Signature is consumed either by:\n * - A relayer calling `PointToken.burnWithSig(...)` (Variant B)\n * - The paymaster-proxy-sponsored UserOp calling `PointToken.burn(...)`\n * with msg.sender = user via EIP-7702 (Variant A)\n *\n * Either way the tx emits `Transfer(user → 0x0)` which the issuer's\n * `BurnIndexer` watches for and uses to credit the off-chain ledger.\n *\n * Field list is a best-guess — SC team may add fields (e.g. `feeAmount`\n * explicit, `extData`). Update this mock when they freeze the spec.\n */\nexport interface MockBurnConsent {\n user: Address;\n pointToken: Address;\n amount: bigint;\n nonce: bigint;\n deadline: bigint;\n}\n\nexport const MOCK_BURN_CONSENT_TYPES = {\n BurnConsent: [\n { name: \"user\", type: \"address\" },\n { name: \"pointToken\", type: \"address\" },\n { name: \"amount\", type: \"uint256\" },\n { name: \"nonce\", type: \"uint256\" },\n { name: \"deadline\", type: \"uint256\" },\n ],\n} as const;\n\nexport function buildMockBurnConsentTypedData(\n domain: PointTokenDomainConfig,\n message: MockBurnConsent,\n) {\n return {\n domain: buildDomain(domain),\n types: MOCK_BURN_CONSENT_TYPES,\n primaryType: \"BurnConsent\" as const,\n message,\n };\n}\n\nexport async function signMockBurnConsent(\n walletClient: WalletClient,\n domain: PointTokenDomainConfig,\n message: MockBurnConsent,\n): Promise<EIP712Signature> {\n const serialized = await walletClient.signTypedData({\n account: walletClient.account!,\n domain: buildDomain(domain),\n types: MOCK_BURN_CONSENT_TYPES,\n primaryType: \"BurnConsent\",\n message,\n });\n\n const { v, r, s } = parseSignature(serialized);\n return {\n v: Number(v),\n r,\n s,\n serialized,\n };\n}\n\nexport async function verifyMockBurnConsent(\n domain: PointTokenDomainConfig,\n message: MockBurnConsent,\n signature: Hex,\n expectedUser: Address,\n): Promise<SignatureVerification> {\n const recoveredAddress = await recoverTypedDataAddress({\n domain: buildDomain(domain),\n types: MOCK_BURN_CONSENT_TYPES,\n primaryType: \"BurnConsent\",\n message,\n signature,\n });\n\n const isValid =\n recoveredAddress.toLowerCase() === expectedUser.toLowerCase();\n\n return {\n isValid,\n recoveredAddress,\n };\n}\n"]}
1
+ {"version":3,"sources":["/Users/phitran/Pacific-Finance/pafi-backend/pafi-sdk/packages/core/dist/index.cjs","../src/index.ts","../src/userop/types.ts","../src/paymaster/config.ts","../src/utils/checkEthAndBranch.ts","../src/contracts/mocks/relayerV2.mock.ts","../src/contracts/mocks/pointTokenV2.mock.ts","../src/contracts/mocks/batchExecutor.mock.ts","../src/contracts/mocks/addresses.mock.ts","../src/contracts/mocks/eip712/mintRequestV2.mock.ts","../src/contracts/mocks/eip712/burnConsent.mock.ts","../src/web-handoff/webPopup.ts","../src/web-handoff/index.ts"],"names":["parseAbi","parseSignature","recoverTypedDataAddress"],"mappings":"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;AAAA;AAM2D,EAAA;AACtB,IAAA;AACrC,EAAA;AAEyE,EAAA;AACtC,IAAA;AACnC,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAkBsB,EAAA;AACb,IAAA;AACgB,MAAA;AACD,MAAA;AACpB,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgBqC,EAAA;AACH,IAAA;AAClC,EAAA;AAAA;AAAA;AAAA;AAAA;AAW6C,EAAA;AACT,IAAA;AACpC,EAAA;AAAA;AAAA;AAAA;AAAA;AAM4D,EAAA;AACd,IAAA;AAC9C,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAoB6B,EAAA;AACpB,IAAA;AACgB,MAAA;AACrB,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAkBiC,EAAA;AACxB,IAAA;AACgB,MAAA;AACrB,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAkBmB,EAAA;AACiB,IAAA;AACE,IAAA;AACb,IAAA;AACT,IAAA;AACiD,MAAA;AAC/D,IAAA;AAC0E,IAAA;AAC5E,EAAA;AAAA;AAGsD,EAAA;AAClB,IAAA;AACX,IAAA;AACT,IAAA;AACiD,MAAA;AAC/D,IAAA;AAC8C,IAAA;AAChD,EAAA;AACF;ADoRkH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/Users/phitran/Pacific-Finance/pafi-backend/pafi-sdk/packages/core/dist/index.cjs","sourcesContent":[null,"import { createPublicClient, http } from \"viem\";\nimport type { Address, Hex, PublicClient, WalletClient } from \"viem\";\n\n// -------------------------------------------------------------------------\n// Re-export all sub-modules\n// -------------------------------------------------------------------------\nexport * from \"./types\";\nexport * from \"./constants\";\nexport * from \"./errors\";\nexport * from \"./abi/index\";\nexport * from \"./eip712/index\";\nexport * from \"./relay/index\";\nexport * from \"./contract/index\";\nexport * from \"./quoting/index\";\nexport * from \"./swap/index\";\nexport * from \"./auth/index\";\n\n// v1.4 — Account Abstraction primitives (EIP-7702 + ERC-4337 v0.7)\nexport * from \"./userop/index\";\nexport * from \"./paymaster/index\";\nexport * from \"./utils/index\";\n\n// v1.4 — Contract ABIs + addresses + EIP-712 v2 types.\n// MOCKED until SC team ships real ABIs. See `src/contracts/mocks/README.md`.\n// Consumers: `import { RELAYER_V2_ABI, buildBurnConsentTypedData, ... } from '@pafi-dev/core'`\n// The re-export swaps mock → real in one file when SC delivers.\nexport * from \"./contracts/index\";\n\n// v1.5 — PAFI Web handoff (mobile + desktop modal helper).\n// `openPafiWebModal()` + adapter registry for platform-specific UX.\nexport * from \"./web-handoff/index\";\n\n// -------------------------------------------------------------------------\n// Internal imports for PafiSDK class\n// -------------------------------------------------------------------------\nimport { buildMintRequestTypedData, signMintRequest, verifyMintRequest } from \"./eip712/mintRequest\";\nimport {\n buildReceiverConsentTypedData,\n signReceiverConsent,\n verifyReceiverConsent,\n} from \"./eip712/receiverConsent\";\nimport {\n getMintRequestNonce,\n getReceiverConsentNonce,\n getTokenName,\n} from \"./contract/pointToken\";\nimport {\n encodeMintAndSwap,\n decodeMintAndSwap,\n encodeExtData,\n buildRelaySwapParams,\n} from \"./relay/calldata\";\nimport { findBestQuote } from \"./quoting/quote\";\nimport {\n buildSwapFromQuote,\n buildUniversalRouterExecuteArgs,\n} from \"./swap/universalRouter\";\nimport { simulateMintAndSwap } from \"./relay/simulate\";\nimport { simulateSwap } from \"./swap/simulate\";\nimport type { SimulationResult } from \"./relay/simulate\";\nimport type { SwapSimulationResult } from \"./swap/simulate\";\nimport { createLoginMessage } from \"./auth/loginMessage\";\nimport type { LoginMessageParams } from \"./auth/types\";\nimport { ConfigurationError } from \"./errors\";\nimport type {\n BestQuote,\n EIP712Signature,\n MintParams,\n MintRequest,\n PafiSDKConfig,\n PathKey,\n PointTokenDomainConfig,\n PoolKey,\n QuoteResult,\n ReceiverConsent,\n SignatureVerification,\n SwapParams,\n} from \"./types\";\n\n// -------------------------------------------------------------------------\n// PafiSDK — convenience class wrapping all contract + crypto primitives.\n//\n// This class is HTTP-client-free on purpose. It covers signing, verifying,\n// contract reads, and calldata encoding — the things that need a signer\n// or a provider but no HTTP layer. The issuer backend defines its own HTTP\n// contract via `@pafi/issuer`; frontends build `fetch()` calls against\n// those types directly.\n// -------------------------------------------------------------------------\n\nexport class PafiSDK {\n private _pointTokenAddress?: Address;\n private _relayContractAddress?: Address;\n private _signer?: WalletClient;\n private _provider?: PublicClient;\n private _chainId?: number;\n\n constructor(config: PafiSDKConfig) {\n this._pointTokenAddress = config.pointTokenAddress;\n this._relayContractAddress = config.relayContractAddress;\n this._signer = config.signer;\n this._chainId = config.chainId;\n\n if (config.provider) {\n this._provider = config.provider;\n } else if (config.rpcUrl) {\n this._provider = createPublicClient({\n transport: http(config.rpcUrl),\n });\n }\n }\n\n // -------------------------------------------------------------------------\n // Setters\n // -------------------------------------------------------------------------\n\n setPointTokenAddress(address: Address): void {\n this._pointTokenAddress = address;\n }\n\n setRelayContractAddress(address: Address): void {\n this._relayContractAddress = address;\n }\n\n setSigner(signer: WalletClient): void {\n this._signer = signer;\n }\n\n setProvider(provider: PublicClient): void {\n this._provider = provider;\n }\n\n // -------------------------------------------------------------------------\n // Private guards\n // -------------------------------------------------------------------------\n\n private requirePointToken(): Address {\n if (!this._pointTokenAddress) {\n throw new ConfigurationError(\"pointTokenAddress not set\");\n }\n return this._pointTokenAddress;\n }\n\n private requireProvider(): PublicClient {\n if (!this._provider) {\n throw new ConfigurationError(\"provider not set\");\n }\n return this._provider;\n }\n\n private requireSigner(): WalletClient {\n if (!this._signer) {\n throw new ConfigurationError(\"signer not set\");\n }\n return this._signer;\n }\n\n private requireChainId(): number {\n if (this._chainId === undefined) {\n throw new ConfigurationError(\"chainId not set\");\n }\n return this._chainId;\n }\n\n // -------------------------------------------------------------------------\n // Domain\n // -------------------------------------------------------------------------\n\n async getDomain(): Promise<PointTokenDomainConfig> {\n const provider = this.requireProvider();\n const pointToken = this.requirePointToken();\n const chainId = this.requireChainId();\n const name = await getTokenName(provider, pointToken);\n return { name, verifyingContract: pointToken, chainId };\n }\n\n // -------------------------------------------------------------------------\n // EIP-712 — delegates to pure functions\n // -------------------------------------------------------------------------\n\n /**\n * Build the EIP-712 typed data for a MintRequest.\n * Pass the result to any external signer (Privy, WalletConnect, etc.).\n */\n async buildMintRequestTypedData(message: MintRequest) {\n const domain = await this.getDomain();\n return buildMintRequestTypedData(domain, message);\n }\n\n /**\n * Build the EIP-712 typed data for a ReceiverConsent.\n * Pass the result to any external signer (Privy, WalletConnect, etc.).\n */\n async buildReceiverConsentTypedData(message: ReceiverConsent) {\n const domain = await this.getDomain();\n return buildReceiverConsentTypedData(domain, message);\n }\n\n async signMintRequest(message: MintRequest): Promise<EIP712Signature> {\n const domain = await this.getDomain();\n return signMintRequest(this.requireSigner(), domain, message);\n }\n\n async verifyMintRequest(\n message: MintRequest,\n signature: Hex,\n expectedMinter: Address,\n ): Promise<SignatureVerification> {\n const domain = await this.getDomain();\n return verifyMintRequest(domain, message, signature, expectedMinter);\n }\n\n async signReceiverConsent(\n message: ReceiverConsent,\n ): Promise<EIP712Signature> {\n const domain = await this.getDomain();\n return signReceiverConsent(this.requireSigner(), domain, message);\n }\n\n async verifyReceiverConsent(\n message: ReceiverConsent,\n signature: Hex,\n expectedReceiver: Address,\n ): Promise<SignatureVerification> {\n const domain = await this.getDomain();\n return verifyReceiverConsent(domain, message, signature, expectedReceiver);\n }\n\n // -------------------------------------------------------------------------\n // Contract reads\n // -------------------------------------------------------------------------\n\n async getMintRequestNonce(receiver: Address): Promise<bigint> {\n return getMintRequestNonce(\n this.requireProvider(),\n this.requirePointToken(),\n receiver,\n );\n }\n\n async getReceiverConsentNonce(receiver: Address): Promise<bigint> {\n return getReceiverConsentNonce(\n this.requireProvider(),\n this.requirePointToken(),\n receiver,\n );\n }\n\n // -------------------------------------------------------------------------\n // Relay calldata — delegates to pure functions\n // -------------------------------------------------------------------------\n\n encodeMintAndSwap(mint: MintParams, swap: SwapParams): Hex {\n return encodeMintAndSwap(mint, swap);\n }\n\n decodeMintAndSwap(calldata: Hex): { mint: MintParams; swap: SwapParams } {\n return decodeMintAndSwap(calldata);\n }\n\n // -------------------------------------------------------------------------\n // Quoting — delegates to pure functions\n // -------------------------------------------------------------------------\n\n /**\n * Find the best swap route from `tokenIn` to `tokenOut`.\n * Merges `pools` with COMMON_POOLS for the configured chain, builds all\n * paths, and quotes via a single multicall RPC call.\n */\n async findBestQuote(\n tokenIn: Address,\n tokenOut: Address,\n exactAmount: bigint,\n pools: PoolKey[] = [],\n quoterAddress?: Address,\n maxHops?: number,\n ): Promise<BestQuote> {\n return findBestQuote(\n this.requireProvider(),\n this.requireChainId(),\n tokenIn,\n tokenOut,\n exactAmount,\n pools,\n quoterAddress,\n maxHops,\n );\n }\n\n // -------------------------------------------------------------------------\n // Swap building — delegates to pure functions\n // -------------------------------------------------------------------------\n\n /**\n * Build UniversalRouter execute args from a quote result.\n * The caller provides `minAmountOut` after applying their own slippage.\n */\n buildSwapFromQuote(params: {\n quote: QuoteResult;\n currencyIn: Address;\n currencyOut: Address;\n amountIn: bigint;\n minAmountOut: bigint;\n }): { commands: Hex; inputs: Hex[] } {\n return buildSwapFromQuote(params);\n }\n\n /**\n * Build Relay SwapParams + extData from a quote result.\n * Returns ready-to-use args for `Relay.mintAndSwap`.\n */\n buildRelaySwapParams(params: {\n quote: { path: PathKey[] };\n minAmountOut: bigint;\n feeInUsdt: bigint;\n deadline: bigint;\n }): { swapParams: SwapParams; extData: Hex } {\n return buildRelaySwapParams(params);\n }\n\n /**\n * Encode extData for ReceiverConsent and MintParams.\n * extData = abi.encode(minAmountOut, feeInUsdt)\n */\n encodeExtData(minAmountOut: bigint, feeInUsdt: bigint): Hex {\n return encodeExtData(minAmountOut, feeInUsdt);\n }\n\n // -------------------------------------------------------------------------\n // Simulation — dry-run via eth_call (no gas spent)\n // -------------------------------------------------------------------------\n\n /**\n * Simulate a Relay.mintAndSwap call. Catches reverts (bad signatures,\n * expired deadlines, insufficient mint cap, pool errors) before submitting.\n *\n * @param relayAddress - Relay contract address\n * @param mint - MintParams with real signatures\n * @param swap - SwapParams (path + deadline)\n * @param from - Relayer address that will submit the tx\n */\n async simulateMintAndSwap(\n relayAddress: Address,\n mint: MintParams,\n swap: SwapParams,\n from: Address,\n ): Promise<SimulationResult> {\n return simulateMintAndSwap(\n this.requireProvider(),\n relayAddress,\n mint,\n swap,\n from,\n );\n }\n\n /**\n * Simulate a UniversalRouter.execute swap call. Catches reverts (bad\n * approvals, insufficient balance, pool errors) before submitting.\n *\n * @param routerAddress - UniversalRouter contract address\n * @param commands - Packed command bytes (from buildSwapFromQuote)\n * @param inputs - Encoded inputs (from buildSwapFromQuote)\n * @param deadline - Unix timestamp\n * @param from - Address that will execute the swap\n */\n async simulateSwap(\n routerAddress: Address,\n commands: Hex,\n inputs: Hex[],\n deadline: bigint,\n from: Address,\n ): Promise<SwapSimulationResult> {\n return simulateSwap(\n this.requireProvider(),\n routerAddress,\n commands,\n inputs,\n deadline,\n from,\n );\n }\n\n // -------------------------------------------------------------------------\n // Auth — EIP-4361 login helpers (offline, stateless)\n //\n // These are convenience wrappers around the pure functions in\n // `./auth/loginMessage.ts`. They do NOT hit the network — the caller is\n // responsible for fetching the nonce, POSTing the signed message, and\n // storing the JWT returned by the issuer backend.\n // -------------------------------------------------------------------------\n\n /**\n * Build an EIP-4361 login message for the current signer + chain. The\n * caller supplies the issuer-specific fields (domain, nonce, uri); the SDK\n * fills in the wallet address and chain id from its own config.\n */\n async createLoginMessage(\n params: Omit<LoginMessageParams, \"address\" | \"chainId\">,\n ): Promise<string> {\n const signer = this.requireSigner();\n const chainId = this.requireChainId();\n const account = signer.account;\n if (!account) {\n throw new ConfigurationError(\"signer has no account attached\");\n }\n return createLoginMessage({ ...params, address: account.address, chainId });\n }\n\n /** Sign a login message string with the current signer (personal_sign) */\n async signLoginMessage(message: string): Promise<Hex> {\n const signer = this.requireSigner();\n const account = signer.account;\n if (!account) {\n throw new ConfigurationError(\"signer has no account attached\");\n }\n return signer.signMessage({ account, message });\n }\n}\n","import type { Address, Hex } from \"viem\";\n\n/**\n * ERC-4337 v0.7 EntryPoint standard address (deployed deterministically\n * at the same address across chains).\n * https://eips.ethereum.org/EIPS/eip-4337\n */\nexport const ENTRY_POINT_V07: Address =\n \"0x0000000071727De22E5E9d8BAf0edAc6f37da032\";\n\n/**\n * A single call inside a batch. `BatchExecutor.execute(Call[])` iterates\n * and invokes each one. When the batch runs via EIP-7702 delegation,\n * `msg.sender` for each call is the user's EOA.\n */\nexport interface Operation {\n target: Address;\n value: bigint;\n data: Hex;\n}\n\n/**\n * Paymaster fields attached to a UserOperation. Populated by the\n * Coinbase Paymaster (via PAFI Backend proxy) before the user signs.\n */\nexport interface PaymasterFields {\n paymaster: Address;\n paymasterData: Hex;\n paymasterVerificationGasLimit: bigint;\n paymasterPostOpGasLimit: bigint;\n}\n\n/**\n * Partial UserOp used during preparation — before paymaster fields are\n * attached or the user signs.\n */\nexport interface PartialUserOperation {\n sender: Address;\n nonce: bigint;\n callData: Hex;\n callGasLimit: bigint;\n verificationGasLimit: bigint;\n preVerificationGas: bigint;\n maxFeePerGas: bigint;\n maxPriorityFeePerGas: bigint;\n}\n\n/**\n * Full ERC-4337 v0.7 UserOperation, ready for bundler submission.\n */\nexport interface UserOperation extends PartialUserOperation {\n paymaster: Address;\n paymasterData: Hex;\n paymasterVerificationGasLimit: bigint;\n paymasterPostOpGasLimit: bigint;\n signature: Hex;\n}\n\n/**\n * Receipt returned by a bundler after a UserOp lands on-chain.\n */\nexport interface UserOpReceipt {\n userOpHash: Hex;\n success: boolean;\n txHash: Hex;\n blockNumber: bigint;\n gasUsed: bigint;\n /** Effective gas cost paid (wei). */\n actualGasCost: bigint;\n}\n\n/**\n * Sentinel operation value used in tests + docs when `Operation.value`\n * is irrelevant (ERC-20 transfers, for example).\n */\nexport const ZERO_VALUE = 0n;\n","import type { PaymasterConfig } from \"./types\";\n\n/**\n * Module-level paymaster config. Read by batch builders (via\n * `getPaymasterConfig()`) and by `@pafi/issuer` `RelayService` when\n * building UserOps.\n *\n * Consumers call `setPaymasterConfig()` once at application boot. All\n * subsequent helpers that need the feeRecipient / issuer identity /\n * backend URL pull from here.\n *\n * This is global state by design — making every batch builder take a\n * config param would clutter every call site. The alternative (a\n * context/service) has more ceremony than this flow justifies.\n */\nlet _config: PaymasterConfig | null = null;\n\n/**\n * Set the application-wide paymaster config. Safe to call multiple\n * times (later calls override earlier). Throws if required fields\n * are missing.\n */\nexport function setPaymasterConfig(config: PaymasterConfig): void {\n if (!config.pafiBackendUrl) {\n throw new Error(\"setPaymasterConfig: pafiBackendUrl is required\");\n }\n if (!config.issuerId) {\n throw new Error(\"setPaymasterConfig: issuerId is required\");\n }\n if (!config.apiKey) {\n throw new Error(\"setPaymasterConfig: apiKey is required\");\n }\n if (!config.feeRecipient) {\n throw new Error(\"setPaymasterConfig: feeRecipient is required\");\n }\n _config = { ...config };\n}\n\n/**\n * Get the current paymaster config. Throws if `setPaymasterConfig()`\n * has not been called yet — this surfaces boot-order bugs early\n * instead of failing with \"paymaster.feeRecipient is undefined\" at\n * the point of use.\n */\nexport function getPaymasterConfig(): PaymasterConfig {\n if (!_config) {\n throw new Error(\n \"PaymasterConfig not initialized — call setPaymasterConfig() at application boot before invoking any batch builder\",\n );\n }\n return _config;\n}\n\n/** Test helper — clear the singleton. */\nexport function _resetPaymasterConfigForTests(): void {\n _config = null;\n}\n\n/** Check whether paymaster config has been initialized. */\nexport function isPaymasterConfigured(): boolean {\n return _config !== null;\n}\n","import type { Address, PublicClient } from \"viem\";\n\n/**\n * Submission path chosen by `checkEthAndBranch`.\n * - `normal`: initiator has enough ETH; submit via `walletClient.writeContract`\n * or a plain `eth_sendRawTransaction`. No paymaster round-trip.\n * - `paymaster`: initiator doesn't have enough ETH; wrap the batch as a\n * UserOperation and route through PAFI Backend → Coinbase Paymaster\n * → Bundler.\n */\nexport type SubmissionPath = \"normal\" | \"paymaster\";\n\nexport interface CheckEthAndBranchParams {\n /** viem PublicClient bound to the target chain. */\n client: PublicClient;\n /** The address whose ETH balance we check. */\n initiator: Address;\n /** Estimated gas cost in wei for the upcoming tx. */\n estimatedGasWei: bigint;\n /**\n * Optional safety margin multiplier (basis points). Defaults to\n * 11_000 (110%) — the initiator needs 10% above the estimate to\n * qualify for the normal path. Prevents edge cases where gas price\n * spikes between estimation and submission cause a \"has enough\"\n * decision to fail at broadcast time.\n */\n marginBps?: number;\n}\n\nconst DEFAULT_MARGIN_BPS = 11_000;\n\n/**\n * Step 3 of the Generalized Flow ([SPONSORED_PATH_FLOW.md §2]):\n * choose between the normal path (initiator pays ETH directly) and the\n * paymaster path (bundler + Coinbase Paymaster).\n *\n * Intentionally synchronous in spirit — the only network call is\n * `getBalance`. Callers can parallelize it with other reads.\n */\nexport async function checkEthAndBranch(\n params: CheckEthAndBranchParams,\n): Promise<SubmissionPath> {\n const marginBps = params.marginBps ?? DEFAULT_MARGIN_BPS;\n const required =\n (params.estimatedGasWei * BigInt(marginBps)) / 10_000n;\n\n const balance = await params.client.getBalance({\n address: params.initiator,\n });\n\n return balance >= required ? \"normal\" : \"paymaster\";\n}\n","import { parseAbi } from \"viem\";\nimport type { Address } from \"viem\";\n\n/**\n * ⚠️ MOCK — Relayer v2 `mint()` ABI for the v1.4 sponsored flow.\n *\n * Signature guess based on stakeholder memo (`REQUIREMENTS_V2.md §3`):\n *\n * mint(\n * MintRequest request, // EIP-712 signed by user + issuer\n * Signature userSig, // v, r, s\n * Signature issuerSig, // v, r, s\n * uint256 feeAmount, // PT units to split off to feeRecipient\n * address feeRecipient // typically operator or fee collector\n * )\n *\n * Behaviour expected:\n * - Verify user + issuer EIP-712 signatures against `request`\n * - Mint `request.amount` PT to `request.to`\n * - Atomic transfer `feeAmount` from `request.to` → `feeRecipient`\n * (net mint = `amount - feeAmount`)\n * - Increment the on-chain nonce to prevent replay\n *\n * When SC team publishes the real ABI, drop it in under\n * `src/contracts/real/relayerV2.abi.ts` and flip the export in\n * `src/contracts/index.ts` — call sites unchanged.\n *\n * See [SC_MOCK_PLAN.md §2.1] for the rationale + swap checklist.\n */\n\nexport const MOCK_RELAYER_V2_ABI = parseAbi([\n \"function mint((address to, uint256 amount, uint256 feeAmount, address feeRecipient, uint256 nonce, uint256 deadline, bytes extData) request, (uint8 v, bytes32 r, bytes32 s) userSig, (uint8 v, bytes32 r, bytes32 s) issuerSig) external\",\n // View functions we'll likely want (nonce getter is standard)\n \"function mintRequestNonce(address user) external view returns (uint256)\",\n \"event Minted(address indexed user, uint256 amount, uint256 feeAmount, address indexed feeRecipient, bytes32 requestHash)\",\n] as const);\n\n/**\n * Calldata function selector for `mint((...),(...),(...))` as encoded\n * by viem against {@link MOCK_RELAYER_V2_ABI}. Callers compare against\n * this to sanity-check decoded UserOp inner calls.\n *\n * Recompute when the real ABI lands — almost certainly differs.\n */\nexport const MOCK_RELAYER_V2_MINT_SELECTOR = \"0xMOCKED__\" as const;\n\n/**\n * Struct shape that matches {@link MOCK_RELAYER_V2_ABI}. Exported so\n * builders can accept a typed input without `as const` gymnastics.\n */\nexport interface MockMintRequestV2 {\n to: Address;\n amount: bigint;\n feeAmount: bigint;\n feeRecipient: Address;\n nonce: bigint;\n deadline: bigint;\n extData: `0x${string}`;\n}\n\nexport interface MockSignatureStruct {\n v: number;\n r: `0x${string}`;\n s: `0x${string}`;\n}\n","import { parseAbi } from \"viem\";\n\n/**\n * ⚠️ MOCK — `PointToken` v1.4 burn surface.\n *\n * Two variants mocked — SC team will pick ONE before stable; the other\n * gets deleted during Phase B swap.\n *\n * Variant A (simpler, most likely):\n * burn(uint256 amount)\n * - burns from `msg.sender`\n * - caller handles authorization (the user is `msg.sender` via\n * EIP-7702 delegation, so `msg.sender == user`)\n *\n * Variant B (signature-based, if SC prefers explicit consent on-chain):\n * burnWithSig(BurnConsent consent, Signature sig)\n * - verifies EIP-712 signature before burning\n * - used when the caller isn't the user (e.g. a relayer burns on\n * behalf of the user)\n *\n * Either way the ERC-20 emits `Transfer(from, address(0), amount)` so\n * `BurnIndexer` semantics don't change.\n */\nexport const MOCK_POINT_TOKEN_V2_ABI = parseAbi([\n // Variant A\n \"function burn(uint256 amount) external\",\n // Variant B\n \"function burnWithSig((address user, address pointToken, uint256 amount, uint256 nonce, uint256 deadline) consent, (uint8 v, bytes32 r, bytes32 s) sig) external\",\n // Standard reads we always need\n \"function balanceOf(address user) external view returns (uint256)\",\n \"function burnNonce(address user) external view returns (uint256)\",\n // ERC-20 Transfer event (inherited) — BurnIndexer filters `to = 0x0`\n \"event Transfer(address indexed from, address indexed to, uint256 value)\",\n] as const);\n","import { parseAbi } from \"viem\";\nimport type { Address } from \"viem\";\n\n/**\n * ⚠️ MOCK — placeholder BatchExecutor address.\n *\n * Real deployment:\n * - Coinbase may pre-deploy a canonical BatchExecutor that we target\n * via EIP-7702 delegation (see blocker B2 in V1.4_V1.5_READINESS.md)\n * - Otherwise PAFI deploys a minimal one and publishes the address\n *\n * The ABI itself (`execute(Call[])`) is stable and lives under\n * `../../userop/batchExecute.ts` — that's the real one, unchanged\n * by the mock swap. Only the **address** is mocked here.\n *\n * Pattern: checksum address with hex \"DEAD0001\" suffix per chain so\n * it's obvious in logs which chain is using a mock.\n */\n\n// Base Sepolia (testnet)\nexport const MOCK_BATCH_EXECUTOR_ADDRESS_BASE_SEPOLIA =\n \"0x000000000000000000000000000000000000DE01\" as Address;\n\n// Base mainnet\nexport const MOCK_BATCH_EXECUTOR_ADDRESS_BASE_MAINNET =\n \"0x000000000000000000000000000000000000DE02\" as Address;\n\n// Re-export the real ABI from userop module (not mocked — ABI is stable)\nexport { BATCH_EXECUTOR_ABI } from \"../../userop/batchExecute\";\n","import type { Address } from \"viem\";\n\n/**\n * ⚠️ MOCK — per-chain contract addresses for the v1.4 sponsored flow.\n *\n * Real addresses come from SC team once they deploy Relayer v2 and\n * pick a BatchExecutor. Until then we use placeholders so the SDK\n * code path compiles + tests execute end-to-end.\n *\n * **Intentionally not merge-safe to mainnet** — callers that resolve\n * a mainnet address here get a `0x...DEAD` placeholder which will\n * revert at contract-level. That's the point: prod refuses to run\n * on mocks.\n *\n * Chain id map:\n * - 8453 Base mainnet\n * - 84532 Base Sepolia\n *\n * Swap workflow when SC delivers:\n * 1. Rename this file → `addresses.ts` under `src/contracts/real/`\n * 2. Fill real addresses per chain\n * 3. Update `src/contracts/index.ts` re-export\n * 4. Delete MOCK_* named exports\n */\n\nexport interface ContractAddresses {\n relayerV2: Address;\n pointToken: Address;\n batchExecutor: Address;\n usdt: Address;\n issuerRegistry: Address;\n mintingOracle: Address;\n}\n\nconst MOCK_PLACEHOLDER = (suffix: string): Address =>\n `0x000000000000000000000000000000000000${suffix.toLowerCase().padStart(4, \"0\")}` as Address;\n\nexport const MOCK_ADDRESSES: Record<number, ContractAddresses> = {\n // Base Sepolia — safe targets for integration tests (MockRelayer\n // fixtures deployed by Hardhat will override these at test time)\n 84532: {\n relayerV2: MOCK_PLACEHOLDER(\"de10\"),\n pointToken: MOCK_PLACEHOLDER(\"de11\"),\n batchExecutor: MOCK_PLACEHOLDER(\"de01\"),\n usdt: MOCK_PLACEHOLDER(\"de12\"),\n issuerRegistry: MOCK_PLACEHOLDER(\"de13\"),\n mintingOracle: MOCK_PLACEHOLDER(\"de14\"),\n },\n // Base mainnet — intentionally left as placeholder. Do NOT ship to\n // prod without swapping.\n 8453: {\n relayerV2: MOCK_PLACEHOLDER(\"dead\"),\n pointToken: MOCK_PLACEHOLDER(\"dead\"),\n batchExecutor: MOCK_PLACEHOLDER(\"dead\"),\n usdt: MOCK_PLACEHOLDER(\"dead\"),\n issuerRegistry: MOCK_PLACEHOLDER(\"dead\"),\n mintingOracle: MOCK_PLACEHOLDER(\"dead\"),\n },\n};\n\n/**\n * Lookup helper — throws if the chain isn't in the map so callers fail\n * loudly on misconfiguration instead of silently using `undefined`.\n */\nexport function getMockAddresses(chainId: number): ContractAddresses {\n const addrs = MOCK_ADDRESSES[chainId];\n if (!addrs) {\n throw new Error(\n `getMockAddresses: no mock addresses for chainId ${chainId}. ` +\n `Supported: ${Object.keys(MOCK_ADDRESSES).join(\", \")}`,\n );\n }\n return addrs;\n}\n","import {\n parseSignature,\n recoverTypedDataAddress,\n type Address,\n type Hex,\n type WalletClient,\n} from \"viem\";\nimport { buildDomain } from \"../../../eip712/domain\";\nimport type {\n EIP712Signature,\n PointTokenDomainConfig,\n SignatureVerification,\n} from \"../../../types\";\nimport type { MockMintRequestV2 } from \"../relayerV2.mock\";\n\n/**\n * ⚠️ MOCK — `MintRequest` v2 EIP-712 types.\n *\n * Extends v0.2.x `MintRequest` (4 fields) with three new fields needed\n * for the v1.4 sponsored flow:\n *\n * + feeAmount PT units taken from the mint for the operator\n * + feeRecipient where the fee goes (operator or treasury)\n * + extData arbitrary bytes for future extensions (swap path, etc.)\n *\n * When SC team confirms the real struct, drop this file's content into\n * the real builder and remove the MOCK_ prefix. The function signatures\n * below stay stable — only `MOCK_MINT_REQUEST_V2_TYPES` changes.\n */\nexport const MOCK_MINT_REQUEST_V2_TYPES = {\n MintRequest: [\n { name: \"to\", type: \"address\" },\n { name: \"amount\", type: \"uint256\" },\n { name: \"feeAmount\", type: \"uint256\" },\n { name: \"feeRecipient\", type: \"address\" },\n { name: \"nonce\", type: \"uint256\" },\n { name: \"deadline\", type: \"uint256\" },\n { name: \"extData\", type: \"bytes\" },\n ],\n} as const;\n\n/**\n * Build the typed-data envelope that any EIP-712 signer (viem, ethers,\n * Privy) can consume. Callers typically pass the result to\n * `walletClient.signTypedData()` or `privy.signTypedData()`.\n */\nexport function buildMockMintRequestV2TypedData(\n domain: PointTokenDomainConfig,\n message: MockMintRequestV2,\n) {\n return {\n domain: buildDomain(domain),\n types: MOCK_MINT_REQUEST_V2_TYPES,\n primaryType: \"MintRequest\" as const,\n message,\n };\n}\n\n/**\n * Sign a `MintRequest` v2 with a viem WalletClient — server-side flow\n * where the issuer holds the private key directly (KMS-backed signer\n * is the production path; see `@pafi-dev/issuer` KMSSignerAdapter).\n */\nexport async function signMockMintRequestV2(\n walletClient: WalletClient,\n domain: PointTokenDomainConfig,\n message: MockMintRequestV2,\n): Promise<EIP712Signature> {\n const serialized = await walletClient.signTypedData({\n account: walletClient.account!,\n domain: buildDomain(domain),\n types: MOCK_MINT_REQUEST_V2_TYPES,\n primaryType: \"MintRequest\",\n message,\n });\n\n const { v, r, s } = parseSignature(serialized);\n return {\n v: Number(v),\n r,\n s,\n serialized,\n };\n}\n\n/**\n * Verify a `MintRequest` v2 signature and check it came from the\n * expected signer (either user or issuer depending on which side is\n * being verified).\n */\nexport async function verifyMockMintRequestV2(\n domain: PointTokenDomainConfig,\n message: MockMintRequestV2,\n signature: Hex,\n expectedSigner: Address,\n): Promise<SignatureVerification> {\n const recoveredAddress = await recoverTypedDataAddress({\n domain: buildDomain(domain),\n types: MOCK_MINT_REQUEST_V2_TYPES,\n primaryType: \"MintRequest\",\n message,\n signature,\n });\n\n const isValid =\n recoveredAddress.toLowerCase() === expectedSigner.toLowerCase();\n\n return {\n isValid,\n recoveredAddress,\n };\n}\n","import {\n parseSignature,\n recoverTypedDataAddress,\n type Address,\n type Hex,\n type WalletClient,\n} from \"viem\";\nimport { buildDomain } from \"../../../eip712/domain\";\nimport type {\n EIP712Signature,\n PointTokenDomainConfig,\n SignatureVerification,\n} from \"../../../types\";\n\n/**\n * ⚠️ MOCK — `BurnConsent` EIP-712 type for the v1.4 reverse flow.\n *\n * New type entirely — v0.2.x had no burn-for-credit flow.\n *\n * User signs `BurnConsent` to authorize burning `amount` PT from their\n * wallet in exchange for an off-chain credit of (amount - gasFee).\n * Signature is consumed either by:\n * - A relayer calling `PointToken.burnWithSig(...)` (Variant B)\n * - The paymaster-proxy-sponsored UserOp calling `PointToken.burn(...)`\n * with msg.sender = user via EIP-7702 (Variant A)\n *\n * Either way the tx emits `Transfer(user → 0x0)` which the issuer's\n * `BurnIndexer` watches for and uses to credit the off-chain ledger.\n *\n * Field list is a best-guess — SC team may add fields (e.g. `feeAmount`\n * explicit, `extData`). Update this mock when they freeze the spec.\n */\nexport interface MockBurnConsent {\n user: Address;\n pointToken: Address;\n amount: bigint;\n nonce: bigint;\n deadline: bigint;\n}\n\nexport const MOCK_BURN_CONSENT_TYPES = {\n BurnConsent: [\n { name: \"user\", type: \"address\" },\n { name: \"pointToken\", type: \"address\" },\n { name: \"amount\", type: \"uint256\" },\n { name: \"nonce\", type: \"uint256\" },\n { name: \"deadline\", type: \"uint256\" },\n ],\n} as const;\n\nexport function buildMockBurnConsentTypedData(\n domain: PointTokenDomainConfig,\n message: MockBurnConsent,\n) {\n return {\n domain: buildDomain(domain),\n types: MOCK_BURN_CONSENT_TYPES,\n primaryType: \"BurnConsent\" as const,\n message,\n };\n}\n\nexport async function signMockBurnConsent(\n walletClient: WalletClient,\n domain: PointTokenDomainConfig,\n message: MockBurnConsent,\n): Promise<EIP712Signature> {\n const serialized = await walletClient.signTypedData({\n account: walletClient.account!,\n domain: buildDomain(domain),\n types: MOCK_BURN_CONSENT_TYPES,\n primaryType: \"BurnConsent\",\n message,\n });\n\n const { v, r, s } = parseSignature(serialized);\n return {\n v: Number(v),\n r,\n s,\n serialized,\n };\n}\n\nexport async function verifyMockBurnConsent(\n domain: PointTokenDomainConfig,\n message: MockBurnConsent,\n signature: Hex,\n expectedUser: Address,\n): Promise<SignatureVerification> {\n const recoveredAddress = await recoverTypedDataAddress({\n domain: buildDomain(domain),\n types: MOCK_BURN_CONSENT_TYPES,\n primaryType: \"BurnConsent\",\n message,\n signature,\n });\n\n const isValid =\n recoveredAddress.toLowerCase() === expectedUser.toLowerCase();\n\n return {\n isValid,\n recoveredAddress,\n };\n}\n","import type {\n ModalOpenOptions,\n PafiWebModalAdapter,\n PafiWebModalHandle,\n} from \"./types\";\n\nconst DEFAULT_WIDTH = 900;\nconst DEFAULT_HEIGHT = 700;\nconst DEFAULT_NAME = \"pafi-web\";\n\n/**\n * Web browser popup adapter. Opens the given URL in a centered\n * `window.open()` popup, polls for close, and optionally forwards\n * `postMessage` events back to the caller.\n *\n * Runtime requirement: `window.open` must exist. Throws if called\n * under Node / SSR / React Native — use `setPafiWebModalAdapter()` to\n * provide a platform-specific adapter in those environments.\n *\n * ## Popup blocking\n *\n * Browsers block `window.open()` unless it happens inside a user\n * gesture (click handler). Callers MUST wire this into a direct click\n * handler — wrapping it in a `setTimeout` or async await before the\n * open call will trigger the blocker.\n */\nexport function openWebPopup(\n url: string,\n options: ModalOpenOptions = {},\n): PafiWebModalHandle {\n if (typeof window === \"undefined\" || typeof window.open !== \"function\") {\n throw new Error(\n \"openWebPopup: `window.open` is not available in this runtime. \" +\n \"Register a platform adapter via setPafiWebModalAdapter() instead.\",\n );\n }\n\n const width = options.width ?? DEFAULT_WIDTH;\n const height = options.height ?? DEFAULT_HEIGHT;\n const name = options.windowName ?? DEFAULT_NAME;\n\n // Center on the screen — works in all major browsers. Falls back\n // gracefully if `screen.availWidth`/`screenX` are unavailable.\n const screenW = window.screen?.availWidth ?? window.innerWidth;\n const screenH = window.screen?.availHeight ?? window.innerHeight;\n const left = Math.max(0, Math.floor((screenW - width) / 2));\n const top = Math.max(0, Math.floor((screenH - height) / 2));\n\n const features = [\n `width=${width}`,\n `height=${height}`,\n `left=${left}`,\n `top=${top}`,\n \"resizable=yes\",\n \"scrollbars=yes\",\n \"noopener=no\", // we need opener.postMessage to work\n ].join(\",\");\n\n const popup = window.open(url, name, features);\n if (!popup) {\n throw new Error(\n \"openWebPopup: popup was blocked. Ensure this call runs inside a user gesture (click handler).\",\n );\n }\n\n // Set up state + listeners --------------------------------------------\n\n let closed = false;\n let pollId: ReturnType<typeof setInterval> | null = null;\n let messageListener: ((e: MessageEvent) => void) | null = null;\n\n const dispose = (): void => {\n if (closed) return;\n closed = true;\n if (pollId !== null) {\n clearInterval(pollId);\n pollId = null;\n }\n if (messageListener) {\n window.removeEventListener(\"message\", messageListener);\n messageListener = null;\n }\n options.onClose?.();\n };\n\n // Poll every 500ms for popup close — there's no `close` event.\n // Stops when `popup.closed` flips to true OR our handle is closed.\n pollId = setInterval(() => {\n if (popup.closed) {\n dispose();\n }\n }, 500);\n\n // Wire postMessage filtering by origin — secure-by-default. An\n // empty or missing `allowedOrigins` rejects ALL messages; the\n // caller must explicitly whitelist PAFI Web's host(s) before any\n // payload is delivered. This prevents a compromised popup (via\n // opener leak or redirect) from impersonating PAFI Web.\n if (options.onMessage) {\n const allowed = options.allowedOrigins ?? [];\n const onMessage = options.onMessage;\n messageListener = (event: MessageEvent): void => {\n if (event.source !== popup) return;\n if (!allowed.includes(event.origin)) return;\n onMessage(event.data, event.origin);\n };\n window.addEventListener(\"message\", messageListener);\n }\n\n // Handle object -------------------------------------------------------\n\n return {\n get isOpen(): boolean {\n return !closed && !popup.closed;\n },\n close(): void {\n if (closed) return;\n try {\n popup.close();\n } catch {\n // Some browsers block close() unless the popup was opened by\n // the same document. Dispose our listeners anyway.\n }\n dispose();\n },\n focus(): void {\n if (closed || popup.closed) return;\n try {\n popup.focus();\n } catch {\n // No-op on failure — focus is best-effort.\n }\n },\n postMessage(data: unknown): void {\n if (closed || popup.closed) return;\n try {\n // targetOrigin '*' is fine here because the caller owns the\n // data being sent. For security on the receiving side, the\n // PAFI Web page should check event.origin itself.\n popup.postMessage(data, \"*\");\n } catch {\n // No-op.\n }\n },\n };\n}\n\n/**\n * The web popup packaged as a {@link PafiWebModalAdapter} so callers\n * can register it explicitly (e.g. in a test harness).\n */\nexport const webPopupAdapter: PafiWebModalAdapter = {\n open(url, options) {\n return openWebPopup(url, options);\n },\n};\n","import { openWebPopup } from \"./webPopup\";\nimport type {\n ModalOpenOptions,\n PafiWebModalAdapter,\n PafiWebModalHandle,\n} from \"./types\";\n\nexport type {\n ModalOpenOptions,\n PafiWebModalAdapter,\n PafiWebModalHandle,\n} from \"./types\";\nexport { openWebPopup, webPopupAdapter } from \"./webPopup\";\n\n/**\n * Module-level adapter registry — allows platform consumers (React\n * Native, Electron, Tauri, etc.) to plug in their own handoff\n * implementation without forking the SDK.\n *\n * Callers set this once at app boot; `openPafiWebModal()` below uses\n * whatever is registered, or falls back to the web popup adapter\n * when `window.open` is available.\n */\nlet registeredAdapter: PafiWebModalAdapter | null = null;\n\n/**\n * Register the adapter used by `openPafiWebModal()`. Typically called\n * once during app initialization — mobile apps register a\n * SFSafariViewController / Chrome Custom Tabs adapter, desktop apps\n * can leave it unset (web popup is the default).\n *\n * Pass `null` to unregister and fall back to the default.\n */\nexport function setPafiWebModalAdapter(\n adapter: PafiWebModalAdapter | null,\n): void {\n registeredAdapter = adapter;\n}\n\n/**\n * Return the currently registered adapter, or `null` when none is set.\n * Useful for tests that want to snapshot-and-restore the adapter.\n */\nexport function getPafiWebModalAdapter(): PafiWebModalAdapter | null {\n return registeredAdapter;\n}\n\n/**\n * Open PAFI Web in the host platform's appropriate UX:\n *\n * - Browser (window.open): centered popup, 900×700 by default\n * - React Native (with adapter registered): SFSafariViewController\n * / Chrome Custom Tabs via `react-native-inappbrowser-reborn` or\n * `expo-web-browser`\n * - Desktop (with adapter registered): custom BrowserWindow / new tab\n *\n * Resolution order:\n * 1. If an adapter was registered via `setPafiWebModalAdapter()`, use it.\n * 2. Else if `window.open` is available, use the built-in web popup.\n * 3. Else throw with a clear error pointing at the adapter registry.\n *\n * @example\n * ```ts\n * // User clicks \"Trade on PAFI\" button\n * button.addEventListener('click', async () => {\n * const modal = await openPafiWebModal('https://app.pacificfinance.org', {\n * allowedOrigins: ['https://app.pacificfinance.org'],\n * onMessage: (data, origin) => {\n * if (typeof data === 'object' && data && 'txHash' in data) {\n * console.log('Swap confirmed:', data.txHash);\n * modal.close();\n * }\n * },\n * onClose: () => {\n * console.log('User closed modal');\n * },\n * });\n * });\n * ```\n */\nexport async function openPafiWebModal(\n url: string,\n options: ModalOpenOptions = {},\n): Promise<PafiWebModalHandle> {\n if (!url || typeof url !== \"string\") {\n throw new Error(\"openPafiWebModal: `url` is required\");\n }\n\n if (registeredAdapter) {\n return Promise.resolve(registeredAdapter.open(url, options));\n }\n\n if (typeof window !== \"undefined\" && typeof window.open === \"function\") {\n return openWebPopup(url, options);\n }\n\n throw new Error(\n \"openPafiWebModal: no adapter registered and `window.open` is unavailable. \" +\n \"Call `setPafiWebModalAdapter()` with a platform-specific adapter (e.g. SFSafariViewController on iOS) during app boot.\",\n );\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -744,6 +744,188 @@ declare function buildMockBurnConsentTypedData(domain: PointTokenDomainConfig, m
744
744
  declare function signMockBurnConsent(walletClient: WalletClient, domain: PointTokenDomainConfig, message: MockBurnConsent): Promise<EIP712Signature>;
745
745
  declare function verifyMockBurnConsent(domain: PointTokenDomainConfig, message: MockBurnConsent, signature: Hex, expectedUser: Address): Promise<SignatureVerification>;
746
746
 
747
+ /**
748
+ * Options for opening a PAFI Web modal — passed through by all adapters.
749
+ * Web-only fields (width/height/windowName) are ignored by mobile
750
+ * adapters that can't resize a system in-app browser.
751
+ */
752
+ interface ModalOpenOptions {
753
+ /** Web popup pixel width. Default 900. Ignored on mobile. */
754
+ width?: number;
755
+ /** Web popup pixel height. Default 700. Ignored on mobile. */
756
+ height?: number;
757
+ /**
758
+ * `window.open()` target name. Same name across calls re-uses an
759
+ * already-open popup. Default: `"pafi-web"`. Web-only.
760
+ */
761
+ windowName?: string;
762
+ /**
763
+ * Called when the modal closes — either user-initiated (clicked
764
+ * close, swiped down on mobile) or programmatically via `handle.close()`.
765
+ */
766
+ onClose?: () => void;
767
+ /**
768
+ * Called when the opened window posts a message back to the parent
769
+ * via `window.opener.postMessage(data, origin)`. PAFI Web uses this
770
+ * to signal "swap complete" / "perp deposit complete" back to the
771
+ * issuer app without needing a redirect.
772
+ *
773
+ * `origin` is the posting window's origin — caller MUST verify it
774
+ * matches the expected PAFI Web host before trusting `data`.
775
+ */
776
+ onMessage?: (data: unknown, origin: string) => void;
777
+ /**
778
+ * Allowed origins for `onMessage` — messages from other origins are
779
+ * silently dropped. Pass the PAFI Web host(s), e.g.
780
+ * `["https://app.pacificfinance.org", "https://app-dev.pacificfinance.org"]`.
781
+ *
782
+ * Default: empty (all messages rejected). Set explicitly for security.
783
+ */
784
+ allowedOrigins?: string[];
785
+ }
786
+ /**
787
+ * Handle to an open PAFI Web modal. Methods are no-ops when the modal
788
+ * has already closed — safe to call without tracking state yourself.
789
+ */
790
+ interface PafiWebModalHandle {
791
+ /** True when the modal is still open. */
792
+ readonly isOpen: boolean;
793
+ /** Close the modal. Fires `onClose` if the callback was supplied. */
794
+ close(): void;
795
+ /** Bring the modal to focus (web popup only; mobile ignores). */
796
+ focus(): void;
797
+ /**
798
+ * Post a message INTO the modal — useful for ping/pong handshakes
799
+ * with PAFI Web. The modal page must be on the same origin or
800
+ * explicitly listen for the message.
801
+ */
802
+ postMessage(data: unknown): void;
803
+ }
804
+ /**
805
+ * Adapter that knows how to open a URL in the right UX for the host
806
+ * platform. Callers register one via `setPafiWebModalAdapter()`; if
807
+ * none is registered, the core module falls back to `openWebPopup()`
808
+ * (if `window` is present) or throws (in Node / React Native without
809
+ * an adapter set).
810
+ *
811
+ * ## Mobile adapter example (React Native)
812
+ *
813
+ * ```ts
814
+ * import { setPafiWebModalAdapter } from '@pafi-dev/core';
815
+ * import InAppBrowser from 'react-native-inappbrowser-reborn';
816
+ *
817
+ * setPafiWebModalAdapter({
818
+ * async open(url, options) {
819
+ * const result = await InAppBrowser.open(url, {
820
+ * // iOS: SFSafariViewController
821
+ * dismissButtonStyle: 'close',
822
+ * preferredBarTintColor: '#1a1a1a',
823
+ * preferredControlTintColor: 'white',
824
+ * // Android: Chrome Custom Tab
825
+ * toolbarColor: '#1a1a1a',
826
+ * secondaryToolbarColor: 'black',
827
+ * });
828
+ * options?.onClose?.();
829
+ * return {
830
+ * isOpen: false,
831
+ * close: () => InAppBrowser.close(),
832
+ * focus: () => {},
833
+ * postMessage: () => {},
834
+ * };
835
+ * },
836
+ * });
837
+ * ```
838
+ *
839
+ * ## Expo adapter example
840
+ *
841
+ * ```ts
842
+ * import * as WebBrowser from 'expo-web-browser';
843
+ *
844
+ * setPafiWebModalAdapter({
845
+ * async open(url, options) {
846
+ * await WebBrowser.openBrowserAsync(url);
847
+ * options?.onClose?.();
848
+ * return { isOpen: false, close: WebBrowser.dismissBrowser, focus: () => {}, postMessage: () => {} };
849
+ * },
850
+ * });
851
+ * ```
852
+ */
853
+ interface PafiWebModalAdapter {
854
+ open(url: string, options?: ModalOpenOptions): PafiWebModalHandle | Promise<PafiWebModalHandle>;
855
+ }
856
+
857
+ /**
858
+ * Web browser popup adapter. Opens the given URL in a centered
859
+ * `window.open()` popup, polls for close, and optionally forwards
860
+ * `postMessage` events back to the caller.
861
+ *
862
+ * Runtime requirement: `window.open` must exist. Throws if called
863
+ * under Node / SSR / React Native — use `setPafiWebModalAdapter()` to
864
+ * provide a platform-specific adapter in those environments.
865
+ *
866
+ * ## Popup blocking
867
+ *
868
+ * Browsers block `window.open()` unless it happens inside a user
869
+ * gesture (click handler). Callers MUST wire this into a direct click
870
+ * handler — wrapping it in a `setTimeout` or async await before the
871
+ * open call will trigger the blocker.
872
+ */
873
+ declare function openWebPopup(url: string, options?: ModalOpenOptions): PafiWebModalHandle;
874
+ /**
875
+ * The web popup packaged as a {@link PafiWebModalAdapter} so callers
876
+ * can register it explicitly (e.g. in a test harness).
877
+ */
878
+ declare const webPopupAdapter: PafiWebModalAdapter;
879
+
880
+ /**
881
+ * Register the adapter used by `openPafiWebModal()`. Typically called
882
+ * once during app initialization — mobile apps register a
883
+ * SFSafariViewController / Chrome Custom Tabs adapter, desktop apps
884
+ * can leave it unset (web popup is the default).
885
+ *
886
+ * Pass `null` to unregister and fall back to the default.
887
+ */
888
+ declare function setPafiWebModalAdapter(adapter: PafiWebModalAdapter | null): void;
889
+ /**
890
+ * Return the currently registered adapter, or `null` when none is set.
891
+ * Useful for tests that want to snapshot-and-restore the adapter.
892
+ */
893
+ declare function getPafiWebModalAdapter(): PafiWebModalAdapter | null;
894
+ /**
895
+ * Open PAFI Web in the host platform's appropriate UX:
896
+ *
897
+ * - Browser (window.open): centered popup, 900×700 by default
898
+ * - React Native (with adapter registered): SFSafariViewController
899
+ * / Chrome Custom Tabs via `react-native-inappbrowser-reborn` or
900
+ * `expo-web-browser`
901
+ * - Desktop (with adapter registered): custom BrowserWindow / new tab
902
+ *
903
+ * Resolution order:
904
+ * 1. If an adapter was registered via `setPafiWebModalAdapter()`, use it.
905
+ * 2. Else if `window.open` is available, use the built-in web popup.
906
+ * 3. Else throw with a clear error pointing at the adapter registry.
907
+ *
908
+ * @example
909
+ * ```ts
910
+ * // User clicks "Trade on PAFI" button
911
+ * button.addEventListener('click', async () => {
912
+ * const modal = await openPafiWebModal('https://app.pacificfinance.org', {
913
+ * allowedOrigins: ['https://app.pacificfinance.org'],
914
+ * onMessage: (data, origin) => {
915
+ * if (typeof data === 'object' && data && 'txHash' in data) {
916
+ * console.log('Swap confirmed:', data.txHash);
917
+ * modal.close();
918
+ * }
919
+ * },
920
+ * onClose: () => {
921
+ * console.log('User closed modal');
922
+ * },
923
+ * });
924
+ * });
925
+ * ```
926
+ */
927
+ declare function openPafiWebModal(url: string, options?: ModalOpenOptions): Promise<PafiWebModalHandle>;
928
+
747
929
  declare class PafiSDK {
748
930
  private _pointTokenAddress?;
749
931
  private _relayContractAddress?;
@@ -906,4 +1088,4 @@ declare class PafiSDK {
906
1088
  signLoginMessage(message: string): Promise<Hex>;
907
1089
  }
908
1090
 
909
- export { ApiError, BATCH_EXECUTOR_ABI, MOCK_BATCH_EXECUTOR_ADDRESS_BASE_MAINNET as BATCH_EXECUTOR_ADDRESS_BASE_MAINNET, MOCK_BATCH_EXECUTOR_ADDRESS_BASE_SEPOLIA as BATCH_EXECUTOR_ADDRESS_BASE_SEPOLIA, MOCK_BURN_CONSENT_TYPES as BURN_CONSENT_TYPES, BestQuote, type BuildPartialUserOpParams, type MockBurnConsent as BurnConsent, COMMON_POOLS, COMMON_TOKENS, MOCK_ADDRESSES as CONTRACT_ADDRESSES, ChainConfig, type CheckEthAndBranchParams, ConfigurationError, type ContractAddresses, EIP712Signature, LoginMessageParams, MOCK_MINT_REQUEST_V2_TYPES as MINT_REQUEST_V2_TYPES, MintParams, MintRequest, type MockMintRequestV2 as MintRequestV2, Operation, POINT_TOKEN_POOLS, MOCK_POINT_TOKEN_V2_ABI as POINT_TOKEN_V2_ABI, PafiSDK, PafiSDKConfig, PafiSDKError, PartialUserOperation, PathKey, type PaymasterConfig, PointTokenDomainConfig, PoolKey, QuoteResult, MOCK_RELAYER_V2_ABI as RELAYER_V2_ABI, MOCK_RELAYER_V2_MINT_SELECTOR as RELAYER_V2_MINT_SELECTOR, ReceiverConsent, SUPPORTED_CHAINS, type MockSignatureStruct as SignatureStruct, SignatureVerification, SigningError, SimulationError, SimulationResult, type SponsorshipScenario, type SubmissionPath, SwapParams, SwapSimulationResult, UNIVERSAL_ROUTER_ADDRESSES, UserOperation, V4_QUOTER_ADDRESSES, _resetPaymasterConfigForTests, assembleUserOperation, buildMockBurnConsentTypedData as buildBurnConsentTypedData, buildMockMintRequestV2TypedData as buildMintRequestV2TypedData, buildPartialUserOperation, checkEthAndBranch, encodeBatchExecute, erc20ApproveOp, erc20BurnOp, erc20TransferOp, getMockAddresses as getContractAddresses, getPaymasterConfig, isPaymasterConfigured, mintRequestTypes, rawCallOp, receiverConsentTypes, setPaymasterConfig, signMockBurnConsent as signBurnConsent, signMockMintRequestV2 as signMintRequestV2, verifyMockBurnConsent as verifyBurnConsent, verifyMockMintRequestV2 as verifyMintRequestV2 };
1091
+ export { ApiError, BATCH_EXECUTOR_ABI, MOCK_BATCH_EXECUTOR_ADDRESS_BASE_MAINNET as BATCH_EXECUTOR_ADDRESS_BASE_MAINNET, MOCK_BATCH_EXECUTOR_ADDRESS_BASE_SEPOLIA as BATCH_EXECUTOR_ADDRESS_BASE_SEPOLIA, MOCK_BURN_CONSENT_TYPES as BURN_CONSENT_TYPES, BestQuote, type BuildPartialUserOpParams, type MockBurnConsent as BurnConsent, COMMON_POOLS, COMMON_TOKENS, MOCK_ADDRESSES as CONTRACT_ADDRESSES, ChainConfig, type CheckEthAndBranchParams, ConfigurationError, type ContractAddresses, EIP712Signature, LoginMessageParams, MOCK_MINT_REQUEST_V2_TYPES as MINT_REQUEST_V2_TYPES, MintParams, MintRequest, type MockMintRequestV2 as MintRequestV2, type ModalOpenOptions, Operation, POINT_TOKEN_POOLS, MOCK_POINT_TOKEN_V2_ABI as POINT_TOKEN_V2_ABI, PafiSDK, PafiSDKConfig, PafiSDKError, type PafiWebModalAdapter, type PafiWebModalHandle, PartialUserOperation, PathKey, type PaymasterConfig, PointTokenDomainConfig, PoolKey, QuoteResult, MOCK_RELAYER_V2_ABI as RELAYER_V2_ABI, MOCK_RELAYER_V2_MINT_SELECTOR as RELAYER_V2_MINT_SELECTOR, ReceiverConsent, SUPPORTED_CHAINS, type MockSignatureStruct as SignatureStruct, SignatureVerification, SigningError, SimulationError, SimulationResult, type SponsorshipScenario, type SubmissionPath, SwapParams, SwapSimulationResult, UNIVERSAL_ROUTER_ADDRESSES, UserOperation, V4_QUOTER_ADDRESSES, _resetPaymasterConfigForTests, assembleUserOperation, buildMockBurnConsentTypedData as buildBurnConsentTypedData, buildMockMintRequestV2TypedData as buildMintRequestV2TypedData, buildPartialUserOperation, checkEthAndBranch, encodeBatchExecute, erc20ApproveOp, erc20BurnOp, erc20TransferOp, getMockAddresses as getContractAddresses, getPafiWebModalAdapter, getPaymasterConfig, isPaymasterConfigured, mintRequestTypes, openPafiWebModal, openWebPopup, rawCallOp, receiverConsentTypes, setPafiWebModalAdapter, setPaymasterConfig, signMockBurnConsent as signBurnConsent, signMockMintRequestV2 as signMintRequestV2, verifyMockBurnConsent as verifyBurnConsent, verifyMockMintRequestV2 as verifyMintRequestV2, webPopupAdapter };
package/dist/index.d.ts CHANGED
@@ -744,6 +744,188 @@ declare function buildMockBurnConsentTypedData(domain: PointTokenDomainConfig, m
744
744
  declare function signMockBurnConsent(walletClient: WalletClient, domain: PointTokenDomainConfig, message: MockBurnConsent): Promise<EIP712Signature>;
745
745
  declare function verifyMockBurnConsent(domain: PointTokenDomainConfig, message: MockBurnConsent, signature: Hex, expectedUser: Address): Promise<SignatureVerification>;
746
746
 
747
+ /**
748
+ * Options for opening a PAFI Web modal — passed through by all adapters.
749
+ * Web-only fields (width/height/windowName) are ignored by mobile
750
+ * adapters that can't resize a system in-app browser.
751
+ */
752
+ interface ModalOpenOptions {
753
+ /** Web popup pixel width. Default 900. Ignored on mobile. */
754
+ width?: number;
755
+ /** Web popup pixel height. Default 700. Ignored on mobile. */
756
+ height?: number;
757
+ /**
758
+ * `window.open()` target name. Same name across calls re-uses an
759
+ * already-open popup. Default: `"pafi-web"`. Web-only.
760
+ */
761
+ windowName?: string;
762
+ /**
763
+ * Called when the modal closes — either user-initiated (clicked
764
+ * close, swiped down on mobile) or programmatically via `handle.close()`.
765
+ */
766
+ onClose?: () => void;
767
+ /**
768
+ * Called when the opened window posts a message back to the parent
769
+ * via `window.opener.postMessage(data, origin)`. PAFI Web uses this
770
+ * to signal "swap complete" / "perp deposit complete" back to the
771
+ * issuer app without needing a redirect.
772
+ *
773
+ * `origin` is the posting window's origin — caller MUST verify it
774
+ * matches the expected PAFI Web host before trusting `data`.
775
+ */
776
+ onMessage?: (data: unknown, origin: string) => void;
777
+ /**
778
+ * Allowed origins for `onMessage` — messages from other origins are
779
+ * silently dropped. Pass the PAFI Web host(s), e.g.
780
+ * `["https://app.pacificfinance.org", "https://app-dev.pacificfinance.org"]`.
781
+ *
782
+ * Default: empty (all messages rejected). Set explicitly for security.
783
+ */
784
+ allowedOrigins?: string[];
785
+ }
786
+ /**
787
+ * Handle to an open PAFI Web modal. Methods are no-ops when the modal
788
+ * has already closed — safe to call without tracking state yourself.
789
+ */
790
+ interface PafiWebModalHandle {
791
+ /** True when the modal is still open. */
792
+ readonly isOpen: boolean;
793
+ /** Close the modal. Fires `onClose` if the callback was supplied. */
794
+ close(): void;
795
+ /** Bring the modal to focus (web popup only; mobile ignores). */
796
+ focus(): void;
797
+ /**
798
+ * Post a message INTO the modal — useful for ping/pong handshakes
799
+ * with PAFI Web. The modal page must be on the same origin or
800
+ * explicitly listen for the message.
801
+ */
802
+ postMessage(data: unknown): void;
803
+ }
804
+ /**
805
+ * Adapter that knows how to open a URL in the right UX for the host
806
+ * platform. Callers register one via `setPafiWebModalAdapter()`; if
807
+ * none is registered, the core module falls back to `openWebPopup()`
808
+ * (if `window` is present) or throws (in Node / React Native without
809
+ * an adapter set).
810
+ *
811
+ * ## Mobile adapter example (React Native)
812
+ *
813
+ * ```ts
814
+ * import { setPafiWebModalAdapter } from '@pafi-dev/core';
815
+ * import InAppBrowser from 'react-native-inappbrowser-reborn';
816
+ *
817
+ * setPafiWebModalAdapter({
818
+ * async open(url, options) {
819
+ * const result = await InAppBrowser.open(url, {
820
+ * // iOS: SFSafariViewController
821
+ * dismissButtonStyle: 'close',
822
+ * preferredBarTintColor: '#1a1a1a',
823
+ * preferredControlTintColor: 'white',
824
+ * // Android: Chrome Custom Tab
825
+ * toolbarColor: '#1a1a1a',
826
+ * secondaryToolbarColor: 'black',
827
+ * });
828
+ * options?.onClose?.();
829
+ * return {
830
+ * isOpen: false,
831
+ * close: () => InAppBrowser.close(),
832
+ * focus: () => {},
833
+ * postMessage: () => {},
834
+ * };
835
+ * },
836
+ * });
837
+ * ```
838
+ *
839
+ * ## Expo adapter example
840
+ *
841
+ * ```ts
842
+ * import * as WebBrowser from 'expo-web-browser';
843
+ *
844
+ * setPafiWebModalAdapter({
845
+ * async open(url, options) {
846
+ * await WebBrowser.openBrowserAsync(url);
847
+ * options?.onClose?.();
848
+ * return { isOpen: false, close: WebBrowser.dismissBrowser, focus: () => {}, postMessage: () => {} };
849
+ * },
850
+ * });
851
+ * ```
852
+ */
853
+ interface PafiWebModalAdapter {
854
+ open(url: string, options?: ModalOpenOptions): PafiWebModalHandle | Promise<PafiWebModalHandle>;
855
+ }
856
+
857
+ /**
858
+ * Web browser popup adapter. Opens the given URL in a centered
859
+ * `window.open()` popup, polls for close, and optionally forwards
860
+ * `postMessage` events back to the caller.
861
+ *
862
+ * Runtime requirement: `window.open` must exist. Throws if called
863
+ * under Node / SSR / React Native — use `setPafiWebModalAdapter()` to
864
+ * provide a platform-specific adapter in those environments.
865
+ *
866
+ * ## Popup blocking
867
+ *
868
+ * Browsers block `window.open()` unless it happens inside a user
869
+ * gesture (click handler). Callers MUST wire this into a direct click
870
+ * handler — wrapping it in a `setTimeout` or async await before the
871
+ * open call will trigger the blocker.
872
+ */
873
+ declare function openWebPopup(url: string, options?: ModalOpenOptions): PafiWebModalHandle;
874
+ /**
875
+ * The web popup packaged as a {@link PafiWebModalAdapter} so callers
876
+ * can register it explicitly (e.g. in a test harness).
877
+ */
878
+ declare const webPopupAdapter: PafiWebModalAdapter;
879
+
880
+ /**
881
+ * Register the adapter used by `openPafiWebModal()`. Typically called
882
+ * once during app initialization — mobile apps register a
883
+ * SFSafariViewController / Chrome Custom Tabs adapter, desktop apps
884
+ * can leave it unset (web popup is the default).
885
+ *
886
+ * Pass `null` to unregister and fall back to the default.
887
+ */
888
+ declare function setPafiWebModalAdapter(adapter: PafiWebModalAdapter | null): void;
889
+ /**
890
+ * Return the currently registered adapter, or `null` when none is set.
891
+ * Useful for tests that want to snapshot-and-restore the adapter.
892
+ */
893
+ declare function getPafiWebModalAdapter(): PafiWebModalAdapter | null;
894
+ /**
895
+ * Open PAFI Web in the host platform's appropriate UX:
896
+ *
897
+ * - Browser (window.open): centered popup, 900×700 by default
898
+ * - React Native (with adapter registered): SFSafariViewController
899
+ * / Chrome Custom Tabs via `react-native-inappbrowser-reborn` or
900
+ * `expo-web-browser`
901
+ * - Desktop (with adapter registered): custom BrowserWindow / new tab
902
+ *
903
+ * Resolution order:
904
+ * 1. If an adapter was registered via `setPafiWebModalAdapter()`, use it.
905
+ * 2. Else if `window.open` is available, use the built-in web popup.
906
+ * 3. Else throw with a clear error pointing at the adapter registry.
907
+ *
908
+ * @example
909
+ * ```ts
910
+ * // User clicks "Trade on PAFI" button
911
+ * button.addEventListener('click', async () => {
912
+ * const modal = await openPafiWebModal('https://app.pacificfinance.org', {
913
+ * allowedOrigins: ['https://app.pacificfinance.org'],
914
+ * onMessage: (data, origin) => {
915
+ * if (typeof data === 'object' && data && 'txHash' in data) {
916
+ * console.log('Swap confirmed:', data.txHash);
917
+ * modal.close();
918
+ * }
919
+ * },
920
+ * onClose: () => {
921
+ * console.log('User closed modal');
922
+ * },
923
+ * });
924
+ * });
925
+ * ```
926
+ */
927
+ declare function openPafiWebModal(url: string, options?: ModalOpenOptions): Promise<PafiWebModalHandle>;
928
+
747
929
  declare class PafiSDK {
748
930
  private _pointTokenAddress?;
749
931
  private _relayContractAddress?;
@@ -906,4 +1088,4 @@ declare class PafiSDK {
906
1088
  signLoginMessage(message: string): Promise<Hex>;
907
1089
  }
908
1090
 
909
- export { ApiError, BATCH_EXECUTOR_ABI, MOCK_BATCH_EXECUTOR_ADDRESS_BASE_MAINNET as BATCH_EXECUTOR_ADDRESS_BASE_MAINNET, MOCK_BATCH_EXECUTOR_ADDRESS_BASE_SEPOLIA as BATCH_EXECUTOR_ADDRESS_BASE_SEPOLIA, MOCK_BURN_CONSENT_TYPES as BURN_CONSENT_TYPES, BestQuote, type BuildPartialUserOpParams, type MockBurnConsent as BurnConsent, COMMON_POOLS, COMMON_TOKENS, MOCK_ADDRESSES as CONTRACT_ADDRESSES, ChainConfig, type CheckEthAndBranchParams, ConfigurationError, type ContractAddresses, EIP712Signature, LoginMessageParams, MOCK_MINT_REQUEST_V2_TYPES as MINT_REQUEST_V2_TYPES, MintParams, MintRequest, type MockMintRequestV2 as MintRequestV2, Operation, POINT_TOKEN_POOLS, MOCK_POINT_TOKEN_V2_ABI as POINT_TOKEN_V2_ABI, PafiSDK, PafiSDKConfig, PafiSDKError, PartialUserOperation, PathKey, type PaymasterConfig, PointTokenDomainConfig, PoolKey, QuoteResult, MOCK_RELAYER_V2_ABI as RELAYER_V2_ABI, MOCK_RELAYER_V2_MINT_SELECTOR as RELAYER_V2_MINT_SELECTOR, ReceiverConsent, SUPPORTED_CHAINS, type MockSignatureStruct as SignatureStruct, SignatureVerification, SigningError, SimulationError, SimulationResult, type SponsorshipScenario, type SubmissionPath, SwapParams, SwapSimulationResult, UNIVERSAL_ROUTER_ADDRESSES, UserOperation, V4_QUOTER_ADDRESSES, _resetPaymasterConfigForTests, assembleUserOperation, buildMockBurnConsentTypedData as buildBurnConsentTypedData, buildMockMintRequestV2TypedData as buildMintRequestV2TypedData, buildPartialUserOperation, checkEthAndBranch, encodeBatchExecute, erc20ApproveOp, erc20BurnOp, erc20TransferOp, getMockAddresses as getContractAddresses, getPaymasterConfig, isPaymasterConfigured, mintRequestTypes, rawCallOp, receiverConsentTypes, setPaymasterConfig, signMockBurnConsent as signBurnConsent, signMockMintRequestV2 as signMintRequestV2, verifyMockBurnConsent as verifyBurnConsent, verifyMockMintRequestV2 as verifyMintRequestV2 };
1091
+ export { ApiError, BATCH_EXECUTOR_ABI, MOCK_BATCH_EXECUTOR_ADDRESS_BASE_MAINNET as BATCH_EXECUTOR_ADDRESS_BASE_MAINNET, MOCK_BATCH_EXECUTOR_ADDRESS_BASE_SEPOLIA as BATCH_EXECUTOR_ADDRESS_BASE_SEPOLIA, MOCK_BURN_CONSENT_TYPES as BURN_CONSENT_TYPES, BestQuote, type BuildPartialUserOpParams, type MockBurnConsent as BurnConsent, COMMON_POOLS, COMMON_TOKENS, MOCK_ADDRESSES as CONTRACT_ADDRESSES, ChainConfig, type CheckEthAndBranchParams, ConfigurationError, type ContractAddresses, EIP712Signature, LoginMessageParams, MOCK_MINT_REQUEST_V2_TYPES as MINT_REQUEST_V2_TYPES, MintParams, MintRequest, type MockMintRequestV2 as MintRequestV2, type ModalOpenOptions, Operation, POINT_TOKEN_POOLS, MOCK_POINT_TOKEN_V2_ABI as POINT_TOKEN_V2_ABI, PafiSDK, PafiSDKConfig, PafiSDKError, type PafiWebModalAdapter, type PafiWebModalHandle, PartialUserOperation, PathKey, type PaymasterConfig, PointTokenDomainConfig, PoolKey, QuoteResult, MOCK_RELAYER_V2_ABI as RELAYER_V2_ABI, MOCK_RELAYER_V2_MINT_SELECTOR as RELAYER_V2_MINT_SELECTOR, ReceiverConsent, SUPPORTED_CHAINS, type MockSignatureStruct as SignatureStruct, SignatureVerification, SigningError, SimulationError, SimulationResult, type SponsorshipScenario, type SubmissionPath, SwapParams, SwapSimulationResult, UNIVERSAL_ROUTER_ADDRESSES, UserOperation, V4_QUOTER_ADDRESSES, _resetPaymasterConfigForTests, assembleUserOperation, buildMockBurnConsentTypedData as buildBurnConsentTypedData, buildMockMintRequestV2TypedData as buildMintRequestV2TypedData, buildPartialUserOperation, checkEthAndBranch, encodeBatchExecute, erc20ApproveOp, erc20BurnOp, erc20TransferOp, getMockAddresses as getContractAddresses, getPafiWebModalAdapter, getPaymasterConfig, isPaymasterConfigured, mintRequestTypes, openPafiWebModal, openWebPopup, rawCallOp, receiverConsentTypes, setPafiWebModalAdapter, setPaymasterConfig, signMockBurnConsent as signBurnConsent, signMockMintRequestV2 as signMintRequestV2, verifyMockBurnConsent as verifyBurnConsent, verifyMockMintRequestV2 as verifyMintRequestV2, webPopupAdapter };
package/dist/index.js CHANGED
@@ -320,6 +320,127 @@ async function verifyMockBurnConsent(domain, message, signature, expectedUser) {
320
320
  };
321
321
  }
322
322
 
323
+ // src/web-handoff/webPopup.ts
324
+ var DEFAULT_WIDTH = 900;
325
+ var DEFAULT_HEIGHT = 700;
326
+ var DEFAULT_NAME = "pafi-web";
327
+ function openWebPopup(url, options = {}) {
328
+ if (typeof window === "undefined" || typeof window.open !== "function") {
329
+ throw new Error(
330
+ "openWebPopup: `window.open` is not available in this runtime. Register a platform adapter via setPafiWebModalAdapter() instead."
331
+ );
332
+ }
333
+ const width = options.width ?? DEFAULT_WIDTH;
334
+ const height = options.height ?? DEFAULT_HEIGHT;
335
+ const name = options.windowName ?? DEFAULT_NAME;
336
+ const screenW = window.screen?.availWidth ?? window.innerWidth;
337
+ const screenH = window.screen?.availHeight ?? window.innerHeight;
338
+ const left = Math.max(0, Math.floor((screenW - width) / 2));
339
+ const top = Math.max(0, Math.floor((screenH - height) / 2));
340
+ const features = [
341
+ `width=${width}`,
342
+ `height=${height}`,
343
+ `left=${left}`,
344
+ `top=${top}`,
345
+ "resizable=yes",
346
+ "scrollbars=yes",
347
+ "noopener=no"
348
+ // we need opener.postMessage to work
349
+ ].join(",");
350
+ const popup = window.open(url, name, features);
351
+ if (!popup) {
352
+ throw new Error(
353
+ "openWebPopup: popup was blocked. Ensure this call runs inside a user gesture (click handler)."
354
+ );
355
+ }
356
+ let closed = false;
357
+ let pollId = null;
358
+ let messageListener = null;
359
+ const dispose = () => {
360
+ if (closed) return;
361
+ closed = true;
362
+ if (pollId !== null) {
363
+ clearInterval(pollId);
364
+ pollId = null;
365
+ }
366
+ if (messageListener) {
367
+ window.removeEventListener("message", messageListener);
368
+ messageListener = null;
369
+ }
370
+ options.onClose?.();
371
+ };
372
+ pollId = setInterval(() => {
373
+ if (popup.closed) {
374
+ dispose();
375
+ }
376
+ }, 500);
377
+ if (options.onMessage) {
378
+ const allowed = options.allowedOrigins ?? [];
379
+ const onMessage = options.onMessage;
380
+ messageListener = (event) => {
381
+ if (event.source !== popup) return;
382
+ if (!allowed.includes(event.origin)) return;
383
+ onMessage(event.data, event.origin);
384
+ };
385
+ window.addEventListener("message", messageListener);
386
+ }
387
+ return {
388
+ get isOpen() {
389
+ return !closed && !popup.closed;
390
+ },
391
+ close() {
392
+ if (closed) return;
393
+ try {
394
+ popup.close();
395
+ } catch {
396
+ }
397
+ dispose();
398
+ },
399
+ focus() {
400
+ if (closed || popup.closed) return;
401
+ try {
402
+ popup.focus();
403
+ } catch {
404
+ }
405
+ },
406
+ postMessage(data) {
407
+ if (closed || popup.closed) return;
408
+ try {
409
+ popup.postMessage(data, "*");
410
+ } catch {
411
+ }
412
+ }
413
+ };
414
+ }
415
+ var webPopupAdapter = {
416
+ open(url, options) {
417
+ return openWebPopup(url, options);
418
+ }
419
+ };
420
+
421
+ // src/web-handoff/index.ts
422
+ var registeredAdapter = null;
423
+ function setPafiWebModalAdapter(adapter) {
424
+ registeredAdapter = adapter;
425
+ }
426
+ function getPafiWebModalAdapter() {
427
+ return registeredAdapter;
428
+ }
429
+ async function openPafiWebModal(url, options = {}) {
430
+ if (!url || typeof url !== "string") {
431
+ throw new Error("openPafiWebModal: `url` is required");
432
+ }
433
+ if (registeredAdapter) {
434
+ return Promise.resolve(registeredAdapter.open(url, options));
435
+ }
436
+ if (typeof window !== "undefined" && typeof window.open === "function") {
437
+ return openWebPopup(url, options);
438
+ }
439
+ throw new Error(
440
+ "openPafiWebModal: no adapter registered and `window.open` is unavailable. Call `setPafiWebModalAdapter()` with a platform-specific adapter (e.g. SFSafariViewController on iOS) during app boot."
441
+ );
442
+ }
443
+
323
444
  // src/index.ts
324
445
  var PafiSDK = class {
325
446
  _pointTokenAddress;
@@ -631,6 +752,7 @@ export {
631
752
  getMockAddresses as getContractAddresses,
632
753
  getIssuer2 as getIssuer,
633
754
  getMintRequestNonce,
755
+ getPafiWebModalAdapter,
634
756
  getPaymasterConfig,
635
757
  getPointTokenBalance,
636
758
  getPointTokenIssuer,
@@ -644,6 +766,8 @@ export {
644
766
  issuerRegistryAbi,
645
767
  mintRequestTypes,
646
768
  mintingOracleAbi,
769
+ openPafiWebModal,
770
+ openWebPopup,
647
771
  parseLoginMessage,
648
772
  permit2Abi,
649
773
  pointTokenAbi,
@@ -654,6 +778,7 @@ export {
654
778
  rawCallOp,
655
779
  receiverConsentTypes,
656
780
  relayAbi,
781
+ setPafiWebModalAdapter,
657
782
  setPaymasterConfig,
658
783
  signMockBurnConsent as signBurnConsent,
659
784
  signMintRequest,
@@ -668,6 +793,7 @@ export {
668
793
  verifyMintCap,
669
794
  verifyMintRequest,
670
795
  verifyMockMintRequestV2 as verifyMintRequestV2,
671
- verifyReceiverConsent
796
+ verifyReceiverConsent,
797
+ webPopupAdapter
672
798
  };
673
799
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/userop/types.ts","../src/paymaster/config.ts","../src/utils/checkEthAndBranch.ts","../src/contracts/mocks/relayerV2.mock.ts","../src/contracts/mocks/pointTokenV2.mock.ts","../src/contracts/mocks/batchExecutor.mock.ts","../src/contracts/mocks/addresses.mock.ts","../src/contracts/mocks/eip712/mintRequestV2.mock.ts","../src/contracts/mocks/eip712/burnConsent.mock.ts"],"sourcesContent":["import { createPublicClient, http } from \"viem\";\nimport type { Address, Hex, PublicClient, WalletClient } from \"viem\";\n\n// -------------------------------------------------------------------------\n// Re-export all sub-modules\n// -------------------------------------------------------------------------\nexport * from \"./types\";\nexport * from \"./constants\";\nexport * from \"./errors\";\nexport * from \"./abi/index\";\nexport * from \"./eip712/index\";\nexport * from \"./relay/index\";\nexport * from \"./contract/index\";\nexport * from \"./quoting/index\";\nexport * from \"./swap/index\";\nexport * from \"./auth/index\";\n\n// v1.4 — Account Abstraction primitives (EIP-7702 + ERC-4337 v0.7)\nexport * from \"./userop/index\";\nexport * from \"./paymaster/index\";\nexport * from \"./utils/index\";\n\n// v1.4 — Contract ABIs + addresses + EIP-712 v2 types.\n// MOCKED until SC team ships real ABIs. See `src/contracts/mocks/README.md`.\n// Consumers: `import { RELAYER_V2_ABI, buildBurnConsentTypedData, ... } from '@pafi-dev/core'`\n// The re-export swaps mock → real in one file when SC delivers.\nexport * from \"./contracts/index\";\n\n// -------------------------------------------------------------------------\n// Internal imports for PafiSDK class\n// -------------------------------------------------------------------------\nimport { buildMintRequestTypedData, signMintRequest, verifyMintRequest } from \"./eip712/mintRequest\";\nimport {\n buildReceiverConsentTypedData,\n signReceiverConsent,\n verifyReceiverConsent,\n} from \"./eip712/receiverConsent\";\nimport {\n getMintRequestNonce,\n getReceiverConsentNonce,\n getTokenName,\n} from \"./contract/pointToken\";\nimport {\n encodeMintAndSwap,\n decodeMintAndSwap,\n encodeExtData,\n buildRelaySwapParams,\n} from \"./relay/calldata\";\nimport { findBestQuote } from \"./quoting/quote\";\nimport {\n buildSwapFromQuote,\n buildUniversalRouterExecuteArgs,\n} from \"./swap/universalRouter\";\nimport { simulateMintAndSwap } from \"./relay/simulate\";\nimport { simulateSwap } from \"./swap/simulate\";\nimport type { SimulationResult } from \"./relay/simulate\";\nimport type { SwapSimulationResult } from \"./swap/simulate\";\nimport { createLoginMessage } from \"./auth/loginMessage\";\nimport type { LoginMessageParams } from \"./auth/types\";\nimport { ConfigurationError } from \"./errors\";\nimport type {\n BestQuote,\n EIP712Signature,\n MintParams,\n MintRequest,\n PafiSDKConfig,\n PathKey,\n PointTokenDomainConfig,\n PoolKey,\n QuoteResult,\n ReceiverConsent,\n SignatureVerification,\n SwapParams,\n} from \"./types\";\n\n// -------------------------------------------------------------------------\n// PafiSDK — convenience class wrapping all contract + crypto primitives.\n//\n// This class is HTTP-client-free on purpose. It covers signing, verifying,\n// contract reads, and calldata encoding — the things that need a signer\n// or a provider but no HTTP layer. The issuer backend defines its own HTTP\n// contract via `@pafi/issuer`; frontends build `fetch()` calls against\n// those types directly.\n// -------------------------------------------------------------------------\n\nexport class PafiSDK {\n private _pointTokenAddress?: Address;\n private _relayContractAddress?: Address;\n private _signer?: WalletClient;\n private _provider?: PublicClient;\n private _chainId?: number;\n\n constructor(config: PafiSDKConfig) {\n this._pointTokenAddress = config.pointTokenAddress;\n this._relayContractAddress = config.relayContractAddress;\n this._signer = config.signer;\n this._chainId = config.chainId;\n\n if (config.provider) {\n this._provider = config.provider;\n } else if (config.rpcUrl) {\n this._provider = createPublicClient({\n transport: http(config.rpcUrl),\n });\n }\n }\n\n // -------------------------------------------------------------------------\n // Setters\n // -------------------------------------------------------------------------\n\n setPointTokenAddress(address: Address): void {\n this._pointTokenAddress = address;\n }\n\n setRelayContractAddress(address: Address): void {\n this._relayContractAddress = address;\n }\n\n setSigner(signer: WalletClient): void {\n this._signer = signer;\n }\n\n setProvider(provider: PublicClient): void {\n this._provider = provider;\n }\n\n // -------------------------------------------------------------------------\n // Private guards\n // -------------------------------------------------------------------------\n\n private requirePointToken(): Address {\n if (!this._pointTokenAddress) {\n throw new ConfigurationError(\"pointTokenAddress not set\");\n }\n return this._pointTokenAddress;\n }\n\n private requireProvider(): PublicClient {\n if (!this._provider) {\n throw new ConfigurationError(\"provider not set\");\n }\n return this._provider;\n }\n\n private requireSigner(): WalletClient {\n if (!this._signer) {\n throw new ConfigurationError(\"signer not set\");\n }\n return this._signer;\n }\n\n private requireChainId(): number {\n if (this._chainId === undefined) {\n throw new ConfigurationError(\"chainId not set\");\n }\n return this._chainId;\n }\n\n // -------------------------------------------------------------------------\n // Domain\n // -------------------------------------------------------------------------\n\n async getDomain(): Promise<PointTokenDomainConfig> {\n const provider = this.requireProvider();\n const pointToken = this.requirePointToken();\n const chainId = this.requireChainId();\n const name = await getTokenName(provider, pointToken);\n return { name, verifyingContract: pointToken, chainId };\n }\n\n // -------------------------------------------------------------------------\n // EIP-712 — delegates to pure functions\n // -------------------------------------------------------------------------\n\n /**\n * Build the EIP-712 typed data for a MintRequest.\n * Pass the result to any external signer (Privy, WalletConnect, etc.).\n */\n async buildMintRequestTypedData(message: MintRequest) {\n const domain = await this.getDomain();\n return buildMintRequestTypedData(domain, message);\n }\n\n /**\n * Build the EIP-712 typed data for a ReceiverConsent.\n * Pass the result to any external signer (Privy, WalletConnect, etc.).\n */\n async buildReceiverConsentTypedData(message: ReceiverConsent) {\n const domain = await this.getDomain();\n return buildReceiverConsentTypedData(domain, message);\n }\n\n async signMintRequest(message: MintRequest): Promise<EIP712Signature> {\n const domain = await this.getDomain();\n return signMintRequest(this.requireSigner(), domain, message);\n }\n\n async verifyMintRequest(\n message: MintRequest,\n signature: Hex,\n expectedMinter: Address,\n ): Promise<SignatureVerification> {\n const domain = await this.getDomain();\n return verifyMintRequest(domain, message, signature, expectedMinter);\n }\n\n async signReceiverConsent(\n message: ReceiverConsent,\n ): Promise<EIP712Signature> {\n const domain = await this.getDomain();\n return signReceiverConsent(this.requireSigner(), domain, message);\n }\n\n async verifyReceiverConsent(\n message: ReceiverConsent,\n signature: Hex,\n expectedReceiver: Address,\n ): Promise<SignatureVerification> {\n const domain = await this.getDomain();\n return verifyReceiverConsent(domain, message, signature, expectedReceiver);\n }\n\n // -------------------------------------------------------------------------\n // Contract reads\n // -------------------------------------------------------------------------\n\n async getMintRequestNonce(receiver: Address): Promise<bigint> {\n return getMintRequestNonce(\n this.requireProvider(),\n this.requirePointToken(),\n receiver,\n );\n }\n\n async getReceiverConsentNonce(receiver: Address): Promise<bigint> {\n return getReceiverConsentNonce(\n this.requireProvider(),\n this.requirePointToken(),\n receiver,\n );\n }\n\n // -------------------------------------------------------------------------\n // Relay calldata — delegates to pure functions\n // -------------------------------------------------------------------------\n\n encodeMintAndSwap(mint: MintParams, swap: SwapParams): Hex {\n return encodeMintAndSwap(mint, swap);\n }\n\n decodeMintAndSwap(calldata: Hex): { mint: MintParams; swap: SwapParams } {\n return decodeMintAndSwap(calldata);\n }\n\n // -------------------------------------------------------------------------\n // Quoting — delegates to pure functions\n // -------------------------------------------------------------------------\n\n /**\n * Find the best swap route from `tokenIn` to `tokenOut`.\n * Merges `pools` with COMMON_POOLS for the configured chain, builds all\n * paths, and quotes via a single multicall RPC call.\n */\n async findBestQuote(\n tokenIn: Address,\n tokenOut: Address,\n exactAmount: bigint,\n pools: PoolKey[] = [],\n quoterAddress?: Address,\n maxHops?: number,\n ): Promise<BestQuote> {\n return findBestQuote(\n this.requireProvider(),\n this.requireChainId(),\n tokenIn,\n tokenOut,\n exactAmount,\n pools,\n quoterAddress,\n maxHops,\n );\n }\n\n // -------------------------------------------------------------------------\n // Swap building — delegates to pure functions\n // -------------------------------------------------------------------------\n\n /**\n * Build UniversalRouter execute args from a quote result.\n * The caller provides `minAmountOut` after applying their own slippage.\n */\n buildSwapFromQuote(params: {\n quote: QuoteResult;\n currencyIn: Address;\n currencyOut: Address;\n amountIn: bigint;\n minAmountOut: bigint;\n }): { commands: Hex; inputs: Hex[] } {\n return buildSwapFromQuote(params);\n }\n\n /**\n * Build Relay SwapParams + extData from a quote result.\n * Returns ready-to-use args for `Relay.mintAndSwap`.\n */\n buildRelaySwapParams(params: {\n quote: { path: PathKey[] };\n minAmountOut: bigint;\n feeInUsdt: bigint;\n deadline: bigint;\n }): { swapParams: SwapParams; extData: Hex } {\n return buildRelaySwapParams(params);\n }\n\n /**\n * Encode extData for ReceiverConsent and MintParams.\n * extData = abi.encode(minAmountOut, feeInUsdt)\n */\n encodeExtData(minAmountOut: bigint, feeInUsdt: bigint): Hex {\n return encodeExtData(minAmountOut, feeInUsdt);\n }\n\n // -------------------------------------------------------------------------\n // Simulation — dry-run via eth_call (no gas spent)\n // -------------------------------------------------------------------------\n\n /**\n * Simulate a Relay.mintAndSwap call. Catches reverts (bad signatures,\n * expired deadlines, insufficient mint cap, pool errors) before submitting.\n *\n * @param relayAddress - Relay contract address\n * @param mint - MintParams with real signatures\n * @param swap - SwapParams (path + deadline)\n * @param from - Relayer address that will submit the tx\n */\n async simulateMintAndSwap(\n relayAddress: Address,\n mint: MintParams,\n swap: SwapParams,\n from: Address,\n ): Promise<SimulationResult> {\n return simulateMintAndSwap(\n this.requireProvider(),\n relayAddress,\n mint,\n swap,\n from,\n );\n }\n\n /**\n * Simulate a UniversalRouter.execute swap call. Catches reverts (bad\n * approvals, insufficient balance, pool errors) before submitting.\n *\n * @param routerAddress - UniversalRouter contract address\n * @param commands - Packed command bytes (from buildSwapFromQuote)\n * @param inputs - Encoded inputs (from buildSwapFromQuote)\n * @param deadline - Unix timestamp\n * @param from - Address that will execute the swap\n */\n async simulateSwap(\n routerAddress: Address,\n commands: Hex,\n inputs: Hex[],\n deadline: bigint,\n from: Address,\n ): Promise<SwapSimulationResult> {\n return simulateSwap(\n this.requireProvider(),\n routerAddress,\n commands,\n inputs,\n deadline,\n from,\n );\n }\n\n // -------------------------------------------------------------------------\n // Auth — EIP-4361 login helpers (offline, stateless)\n //\n // These are convenience wrappers around the pure functions in\n // `./auth/loginMessage.ts`. They do NOT hit the network — the caller is\n // responsible for fetching the nonce, POSTing the signed message, and\n // storing the JWT returned by the issuer backend.\n // -------------------------------------------------------------------------\n\n /**\n * Build an EIP-4361 login message for the current signer + chain. The\n * caller supplies the issuer-specific fields (domain, nonce, uri); the SDK\n * fills in the wallet address and chain id from its own config.\n */\n async createLoginMessage(\n params: Omit<LoginMessageParams, \"address\" | \"chainId\">,\n ): Promise<string> {\n const signer = this.requireSigner();\n const chainId = this.requireChainId();\n const account = signer.account;\n if (!account) {\n throw new ConfigurationError(\"signer has no account attached\");\n }\n return createLoginMessage({ ...params, address: account.address, chainId });\n }\n\n /** Sign a login message string with the current signer (personal_sign) */\n async signLoginMessage(message: string): Promise<Hex> {\n const signer = this.requireSigner();\n const account = signer.account;\n if (!account) {\n throw new ConfigurationError(\"signer has no account attached\");\n }\n return signer.signMessage({ account, message });\n }\n}\n","import type { Address, Hex } from \"viem\";\n\n/**\n * ERC-4337 v0.7 EntryPoint standard address (deployed deterministically\n * at the same address across chains).\n * https://eips.ethereum.org/EIPS/eip-4337\n */\nexport const ENTRY_POINT_V07: Address =\n \"0x0000000071727De22E5E9d8BAf0edAc6f37da032\";\n\n/**\n * A single call inside a batch. `BatchExecutor.execute(Call[])` iterates\n * and invokes each one. When the batch runs via EIP-7702 delegation,\n * `msg.sender` for each call is the user's EOA.\n */\nexport interface Operation {\n target: Address;\n value: bigint;\n data: Hex;\n}\n\n/**\n * Paymaster fields attached to a UserOperation. Populated by the\n * Coinbase Paymaster (via PAFI Backend proxy) before the user signs.\n */\nexport interface PaymasterFields {\n paymaster: Address;\n paymasterData: Hex;\n paymasterVerificationGasLimit: bigint;\n paymasterPostOpGasLimit: bigint;\n}\n\n/**\n * Partial UserOp used during preparation — before paymaster fields are\n * attached or the user signs.\n */\nexport interface PartialUserOperation {\n sender: Address;\n nonce: bigint;\n callData: Hex;\n callGasLimit: bigint;\n verificationGasLimit: bigint;\n preVerificationGas: bigint;\n maxFeePerGas: bigint;\n maxPriorityFeePerGas: bigint;\n}\n\n/**\n * Full ERC-4337 v0.7 UserOperation, ready for bundler submission.\n */\nexport interface UserOperation extends PartialUserOperation {\n paymaster: Address;\n paymasterData: Hex;\n paymasterVerificationGasLimit: bigint;\n paymasterPostOpGasLimit: bigint;\n signature: Hex;\n}\n\n/**\n * Receipt returned by a bundler after a UserOp lands on-chain.\n */\nexport interface UserOpReceipt {\n userOpHash: Hex;\n success: boolean;\n txHash: Hex;\n blockNumber: bigint;\n gasUsed: bigint;\n /** Effective gas cost paid (wei). */\n actualGasCost: bigint;\n}\n\n/**\n * Sentinel operation value used in tests + docs when `Operation.value`\n * is irrelevant (ERC-20 transfers, for example).\n */\nexport const ZERO_VALUE = 0n;\n","import type { PaymasterConfig } from \"./types\";\n\n/**\n * Module-level paymaster config. Read by batch builders (via\n * `getPaymasterConfig()`) and by `@pafi/issuer` `RelayService` when\n * building UserOps.\n *\n * Consumers call `setPaymasterConfig()` once at application boot. All\n * subsequent helpers that need the feeRecipient / issuer identity /\n * backend URL pull from here.\n *\n * This is global state by design — making every batch builder take a\n * config param would clutter every call site. The alternative (a\n * context/service) has more ceremony than this flow justifies.\n */\nlet _config: PaymasterConfig | null = null;\n\n/**\n * Set the application-wide paymaster config. Safe to call multiple\n * times (later calls override earlier). Throws if required fields\n * are missing.\n */\nexport function setPaymasterConfig(config: PaymasterConfig): void {\n if (!config.pafiBackendUrl) {\n throw new Error(\"setPaymasterConfig: pafiBackendUrl is required\");\n }\n if (!config.issuerId) {\n throw new Error(\"setPaymasterConfig: issuerId is required\");\n }\n if (!config.apiKey) {\n throw new Error(\"setPaymasterConfig: apiKey is required\");\n }\n if (!config.feeRecipient) {\n throw new Error(\"setPaymasterConfig: feeRecipient is required\");\n }\n _config = { ...config };\n}\n\n/**\n * Get the current paymaster config. Throws if `setPaymasterConfig()`\n * has not been called yet — this surfaces boot-order bugs early\n * instead of failing with \"paymaster.feeRecipient is undefined\" at\n * the point of use.\n */\nexport function getPaymasterConfig(): PaymasterConfig {\n if (!_config) {\n throw new Error(\n \"PaymasterConfig not initialized — call setPaymasterConfig() at application boot before invoking any batch builder\",\n );\n }\n return _config;\n}\n\n/** Test helper — clear the singleton. */\nexport function _resetPaymasterConfigForTests(): void {\n _config = null;\n}\n\n/** Check whether paymaster config has been initialized. */\nexport function isPaymasterConfigured(): boolean {\n return _config !== null;\n}\n","import type { Address, PublicClient } from \"viem\";\n\n/**\n * Submission path chosen by `checkEthAndBranch`.\n * - `normal`: initiator has enough ETH; submit via `walletClient.writeContract`\n * or a plain `eth_sendRawTransaction`. No paymaster round-trip.\n * - `paymaster`: initiator doesn't have enough ETH; wrap the batch as a\n * UserOperation and route through PAFI Backend → Coinbase Paymaster\n * → Bundler.\n */\nexport type SubmissionPath = \"normal\" | \"paymaster\";\n\nexport interface CheckEthAndBranchParams {\n /** viem PublicClient bound to the target chain. */\n client: PublicClient;\n /** The address whose ETH balance we check. */\n initiator: Address;\n /** Estimated gas cost in wei for the upcoming tx. */\n estimatedGasWei: bigint;\n /**\n * Optional safety margin multiplier (basis points). Defaults to\n * 11_000 (110%) — the initiator needs 10% above the estimate to\n * qualify for the normal path. Prevents edge cases where gas price\n * spikes between estimation and submission cause a \"has enough\"\n * decision to fail at broadcast time.\n */\n marginBps?: number;\n}\n\nconst DEFAULT_MARGIN_BPS = 11_000;\n\n/**\n * Step 3 of the Generalized Flow ([SPONSORED_PATH_FLOW.md §2]):\n * choose between the normal path (initiator pays ETH directly) and the\n * paymaster path (bundler + Coinbase Paymaster).\n *\n * Intentionally synchronous in spirit — the only network call is\n * `getBalance`. Callers can parallelize it with other reads.\n */\nexport async function checkEthAndBranch(\n params: CheckEthAndBranchParams,\n): Promise<SubmissionPath> {\n const marginBps = params.marginBps ?? DEFAULT_MARGIN_BPS;\n const required =\n (params.estimatedGasWei * BigInt(marginBps)) / 10_000n;\n\n const balance = await params.client.getBalance({\n address: params.initiator,\n });\n\n return balance >= required ? \"normal\" : \"paymaster\";\n}\n","import { parseAbi } from \"viem\";\nimport type { Address } from \"viem\";\n\n/**\n * ⚠️ MOCK — Relayer v2 `mint()` ABI for the v1.4 sponsored flow.\n *\n * Signature guess based on stakeholder memo (`REQUIREMENTS_V2.md §3`):\n *\n * mint(\n * MintRequest request, // EIP-712 signed by user + issuer\n * Signature userSig, // v, r, s\n * Signature issuerSig, // v, r, s\n * uint256 feeAmount, // PT units to split off to feeRecipient\n * address feeRecipient // typically operator or fee collector\n * )\n *\n * Behaviour expected:\n * - Verify user + issuer EIP-712 signatures against `request`\n * - Mint `request.amount` PT to `request.to`\n * - Atomic transfer `feeAmount` from `request.to` → `feeRecipient`\n * (net mint = `amount - feeAmount`)\n * - Increment the on-chain nonce to prevent replay\n *\n * When SC team publishes the real ABI, drop it in under\n * `src/contracts/real/relayerV2.abi.ts` and flip the export in\n * `src/contracts/index.ts` — call sites unchanged.\n *\n * See [SC_MOCK_PLAN.md §2.1] for the rationale + swap checklist.\n */\n\nexport const MOCK_RELAYER_V2_ABI = parseAbi([\n \"function mint((address to, uint256 amount, uint256 feeAmount, address feeRecipient, uint256 nonce, uint256 deadline, bytes extData) request, (uint8 v, bytes32 r, bytes32 s) userSig, (uint8 v, bytes32 r, bytes32 s) issuerSig) external\",\n // View functions we'll likely want (nonce getter is standard)\n \"function mintRequestNonce(address user) external view returns (uint256)\",\n \"event Minted(address indexed user, uint256 amount, uint256 feeAmount, address indexed feeRecipient, bytes32 requestHash)\",\n] as const);\n\n/**\n * Calldata function selector for `mint((...),(...),(...))` as encoded\n * by viem against {@link MOCK_RELAYER_V2_ABI}. Callers compare against\n * this to sanity-check decoded UserOp inner calls.\n *\n * Recompute when the real ABI lands — almost certainly differs.\n */\nexport const MOCK_RELAYER_V2_MINT_SELECTOR = \"0xMOCKED__\" as const;\n\n/**\n * Struct shape that matches {@link MOCK_RELAYER_V2_ABI}. Exported so\n * builders can accept a typed input without `as const` gymnastics.\n */\nexport interface MockMintRequestV2 {\n to: Address;\n amount: bigint;\n feeAmount: bigint;\n feeRecipient: Address;\n nonce: bigint;\n deadline: bigint;\n extData: `0x${string}`;\n}\n\nexport interface MockSignatureStruct {\n v: number;\n r: `0x${string}`;\n s: `0x${string}`;\n}\n","import { parseAbi } from \"viem\";\n\n/**\n * ⚠️ MOCK — `PointToken` v1.4 burn surface.\n *\n * Two variants mocked — SC team will pick ONE before stable; the other\n * gets deleted during Phase B swap.\n *\n * Variant A (simpler, most likely):\n * burn(uint256 amount)\n * - burns from `msg.sender`\n * - caller handles authorization (the user is `msg.sender` via\n * EIP-7702 delegation, so `msg.sender == user`)\n *\n * Variant B (signature-based, if SC prefers explicit consent on-chain):\n * burnWithSig(BurnConsent consent, Signature sig)\n * - verifies EIP-712 signature before burning\n * - used when the caller isn't the user (e.g. a relayer burns on\n * behalf of the user)\n *\n * Either way the ERC-20 emits `Transfer(from, address(0), amount)` so\n * `BurnIndexer` semantics don't change.\n */\nexport const MOCK_POINT_TOKEN_V2_ABI = parseAbi([\n // Variant A\n \"function burn(uint256 amount) external\",\n // Variant B\n \"function burnWithSig((address user, address pointToken, uint256 amount, uint256 nonce, uint256 deadline) consent, (uint8 v, bytes32 r, bytes32 s) sig) external\",\n // Standard reads we always need\n \"function balanceOf(address user) external view returns (uint256)\",\n \"function burnNonce(address user) external view returns (uint256)\",\n // ERC-20 Transfer event (inherited) — BurnIndexer filters `to = 0x0`\n \"event Transfer(address indexed from, address indexed to, uint256 value)\",\n] as const);\n","import { parseAbi } from \"viem\";\nimport type { Address } from \"viem\";\n\n/**\n * ⚠️ MOCK — placeholder BatchExecutor address.\n *\n * Real deployment:\n * - Coinbase may pre-deploy a canonical BatchExecutor that we target\n * via EIP-7702 delegation (see blocker B2 in V1.4_V1.5_READINESS.md)\n * - Otherwise PAFI deploys a minimal one and publishes the address\n *\n * The ABI itself (`execute(Call[])`) is stable and lives under\n * `../../userop/batchExecute.ts` — that's the real one, unchanged\n * by the mock swap. Only the **address** is mocked here.\n *\n * Pattern: checksum address with hex \"DEAD0001\" suffix per chain so\n * it's obvious in logs which chain is using a mock.\n */\n\n// Base Sepolia (testnet)\nexport const MOCK_BATCH_EXECUTOR_ADDRESS_BASE_SEPOLIA =\n \"0x000000000000000000000000000000000000DE01\" as Address;\n\n// Base mainnet\nexport const MOCK_BATCH_EXECUTOR_ADDRESS_BASE_MAINNET =\n \"0x000000000000000000000000000000000000DE02\" as Address;\n\n// Re-export the real ABI from userop module (not mocked — ABI is stable)\nexport { BATCH_EXECUTOR_ABI } from \"../../userop/batchExecute\";\n","import type { Address } from \"viem\";\n\n/**\n * ⚠️ MOCK — per-chain contract addresses for the v1.4 sponsored flow.\n *\n * Real addresses come from SC team once they deploy Relayer v2 and\n * pick a BatchExecutor. Until then we use placeholders so the SDK\n * code path compiles + tests execute end-to-end.\n *\n * **Intentionally not merge-safe to mainnet** — callers that resolve\n * a mainnet address here get a `0x...DEAD` placeholder which will\n * revert at contract-level. That's the point: prod refuses to run\n * on mocks.\n *\n * Chain id map:\n * - 8453 Base mainnet\n * - 84532 Base Sepolia\n *\n * Swap workflow when SC delivers:\n * 1. Rename this file → `addresses.ts` under `src/contracts/real/`\n * 2. Fill real addresses per chain\n * 3. Update `src/contracts/index.ts` re-export\n * 4. Delete MOCK_* named exports\n */\n\nexport interface ContractAddresses {\n relayerV2: Address;\n pointToken: Address;\n batchExecutor: Address;\n usdt: Address;\n issuerRegistry: Address;\n mintingOracle: Address;\n}\n\nconst MOCK_PLACEHOLDER = (suffix: string): Address =>\n `0x000000000000000000000000000000000000${suffix.toLowerCase().padStart(4, \"0\")}` as Address;\n\nexport const MOCK_ADDRESSES: Record<number, ContractAddresses> = {\n // Base Sepolia — safe targets for integration tests (MockRelayer\n // fixtures deployed by Hardhat will override these at test time)\n 84532: {\n relayerV2: MOCK_PLACEHOLDER(\"de10\"),\n pointToken: MOCK_PLACEHOLDER(\"de11\"),\n batchExecutor: MOCK_PLACEHOLDER(\"de01\"),\n usdt: MOCK_PLACEHOLDER(\"de12\"),\n issuerRegistry: MOCK_PLACEHOLDER(\"de13\"),\n mintingOracle: MOCK_PLACEHOLDER(\"de14\"),\n },\n // Base mainnet — intentionally left as placeholder. Do NOT ship to\n // prod without swapping.\n 8453: {\n relayerV2: MOCK_PLACEHOLDER(\"dead\"),\n pointToken: MOCK_PLACEHOLDER(\"dead\"),\n batchExecutor: MOCK_PLACEHOLDER(\"dead\"),\n usdt: MOCK_PLACEHOLDER(\"dead\"),\n issuerRegistry: MOCK_PLACEHOLDER(\"dead\"),\n mintingOracle: MOCK_PLACEHOLDER(\"dead\"),\n },\n};\n\n/**\n * Lookup helper — throws if the chain isn't in the map so callers fail\n * loudly on misconfiguration instead of silently using `undefined`.\n */\nexport function getMockAddresses(chainId: number): ContractAddresses {\n const addrs = MOCK_ADDRESSES[chainId];\n if (!addrs) {\n throw new Error(\n `getMockAddresses: no mock addresses for chainId ${chainId}. ` +\n `Supported: ${Object.keys(MOCK_ADDRESSES).join(\", \")}`,\n );\n }\n return addrs;\n}\n","import {\n parseSignature,\n recoverTypedDataAddress,\n type Address,\n type Hex,\n type WalletClient,\n} from \"viem\";\nimport { buildDomain } from \"../../../eip712/domain\";\nimport type {\n EIP712Signature,\n PointTokenDomainConfig,\n SignatureVerification,\n} from \"../../../types\";\nimport type { MockMintRequestV2 } from \"../relayerV2.mock\";\n\n/**\n * ⚠️ MOCK — `MintRequest` v2 EIP-712 types.\n *\n * Extends v0.2.x `MintRequest` (4 fields) with three new fields needed\n * for the v1.4 sponsored flow:\n *\n * + feeAmount PT units taken from the mint for the operator\n * + feeRecipient where the fee goes (operator or treasury)\n * + extData arbitrary bytes for future extensions (swap path, etc.)\n *\n * When SC team confirms the real struct, drop this file's content into\n * the real builder and remove the MOCK_ prefix. The function signatures\n * below stay stable — only `MOCK_MINT_REQUEST_V2_TYPES` changes.\n */\nexport const MOCK_MINT_REQUEST_V2_TYPES = {\n MintRequest: [\n { name: \"to\", type: \"address\" },\n { name: \"amount\", type: \"uint256\" },\n { name: \"feeAmount\", type: \"uint256\" },\n { name: \"feeRecipient\", type: \"address\" },\n { name: \"nonce\", type: \"uint256\" },\n { name: \"deadline\", type: \"uint256\" },\n { name: \"extData\", type: \"bytes\" },\n ],\n} as const;\n\n/**\n * Build the typed-data envelope that any EIP-712 signer (viem, ethers,\n * Privy) can consume. Callers typically pass the result to\n * `walletClient.signTypedData()` or `privy.signTypedData()`.\n */\nexport function buildMockMintRequestV2TypedData(\n domain: PointTokenDomainConfig,\n message: MockMintRequestV2,\n) {\n return {\n domain: buildDomain(domain),\n types: MOCK_MINT_REQUEST_V2_TYPES,\n primaryType: \"MintRequest\" as const,\n message,\n };\n}\n\n/**\n * Sign a `MintRequest` v2 with a viem WalletClient — server-side flow\n * where the issuer holds the private key directly (KMS-backed signer\n * is the production path; see `@pafi-dev/issuer` KMSSignerAdapter).\n */\nexport async function signMockMintRequestV2(\n walletClient: WalletClient,\n domain: PointTokenDomainConfig,\n message: MockMintRequestV2,\n): Promise<EIP712Signature> {\n const serialized = await walletClient.signTypedData({\n account: walletClient.account!,\n domain: buildDomain(domain),\n types: MOCK_MINT_REQUEST_V2_TYPES,\n primaryType: \"MintRequest\",\n message,\n });\n\n const { v, r, s } = parseSignature(serialized);\n return {\n v: Number(v),\n r,\n s,\n serialized,\n };\n}\n\n/**\n * Verify a `MintRequest` v2 signature and check it came from the\n * expected signer (either user or issuer depending on which side is\n * being verified).\n */\nexport async function verifyMockMintRequestV2(\n domain: PointTokenDomainConfig,\n message: MockMintRequestV2,\n signature: Hex,\n expectedSigner: Address,\n): Promise<SignatureVerification> {\n const recoveredAddress = await recoverTypedDataAddress({\n domain: buildDomain(domain),\n types: MOCK_MINT_REQUEST_V2_TYPES,\n primaryType: \"MintRequest\",\n message,\n signature,\n });\n\n const isValid =\n recoveredAddress.toLowerCase() === expectedSigner.toLowerCase();\n\n return {\n isValid,\n recoveredAddress,\n };\n}\n","import {\n parseSignature,\n recoverTypedDataAddress,\n type Address,\n type Hex,\n type WalletClient,\n} from \"viem\";\nimport { buildDomain } from \"../../../eip712/domain\";\nimport type {\n EIP712Signature,\n PointTokenDomainConfig,\n SignatureVerification,\n} from \"../../../types\";\n\n/**\n * ⚠️ MOCK — `BurnConsent` EIP-712 type for the v1.4 reverse flow.\n *\n * New type entirely — v0.2.x had no burn-for-credit flow.\n *\n * User signs `BurnConsent` to authorize burning `amount` PT from their\n * wallet in exchange for an off-chain credit of (amount - gasFee).\n * Signature is consumed either by:\n * - A relayer calling `PointToken.burnWithSig(...)` (Variant B)\n * - The paymaster-proxy-sponsored UserOp calling `PointToken.burn(...)`\n * with msg.sender = user via EIP-7702 (Variant A)\n *\n * Either way the tx emits `Transfer(user → 0x0)` which the issuer's\n * `BurnIndexer` watches for and uses to credit the off-chain ledger.\n *\n * Field list is a best-guess — SC team may add fields (e.g. `feeAmount`\n * explicit, `extData`). Update this mock when they freeze the spec.\n */\nexport interface MockBurnConsent {\n user: Address;\n pointToken: Address;\n amount: bigint;\n nonce: bigint;\n deadline: bigint;\n}\n\nexport const MOCK_BURN_CONSENT_TYPES = {\n BurnConsent: [\n { name: \"user\", type: \"address\" },\n { name: \"pointToken\", type: \"address\" },\n { name: \"amount\", type: \"uint256\" },\n { name: \"nonce\", type: \"uint256\" },\n { name: \"deadline\", type: \"uint256\" },\n ],\n} as const;\n\nexport function buildMockBurnConsentTypedData(\n domain: PointTokenDomainConfig,\n message: MockBurnConsent,\n) {\n return {\n domain: buildDomain(domain),\n types: MOCK_BURN_CONSENT_TYPES,\n primaryType: \"BurnConsent\" as const,\n message,\n };\n}\n\nexport async function signMockBurnConsent(\n walletClient: WalletClient,\n domain: PointTokenDomainConfig,\n message: MockBurnConsent,\n): Promise<EIP712Signature> {\n const serialized = await walletClient.signTypedData({\n account: walletClient.account!,\n domain: buildDomain(domain),\n types: MOCK_BURN_CONSENT_TYPES,\n primaryType: \"BurnConsent\",\n message,\n });\n\n const { v, r, s } = parseSignature(serialized);\n return {\n v: Number(v),\n r,\n s,\n serialized,\n };\n}\n\nexport async function verifyMockBurnConsent(\n domain: PointTokenDomainConfig,\n message: MockBurnConsent,\n signature: Hex,\n expectedUser: Address,\n): Promise<SignatureVerification> {\n const recoveredAddress = await recoverTypedDataAddress({\n domain: buildDomain(domain),\n types: MOCK_BURN_CONSENT_TYPES,\n primaryType: \"BurnConsent\",\n message,\n signature,\n });\n\n const isValid =\n recoveredAddress.toLowerCase() === expectedUser.toLowerCase();\n\n return {\n isValid,\n recoveredAddress,\n };\n}\n"],"mappings":"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","names":["parseAbi","parseSignature","recoverTypedDataAddress","parseSignature","recoverTypedDataAddress"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/userop/types.ts","../src/paymaster/config.ts","../src/utils/checkEthAndBranch.ts","../src/contracts/mocks/relayerV2.mock.ts","../src/contracts/mocks/pointTokenV2.mock.ts","../src/contracts/mocks/batchExecutor.mock.ts","../src/contracts/mocks/addresses.mock.ts","../src/contracts/mocks/eip712/mintRequestV2.mock.ts","../src/contracts/mocks/eip712/burnConsent.mock.ts","../src/web-handoff/webPopup.ts","../src/web-handoff/index.ts"],"sourcesContent":["import { createPublicClient, http } from \"viem\";\nimport type { Address, Hex, PublicClient, WalletClient } from \"viem\";\n\n// -------------------------------------------------------------------------\n// Re-export all sub-modules\n// -------------------------------------------------------------------------\nexport * from \"./types\";\nexport * from \"./constants\";\nexport * from \"./errors\";\nexport * from \"./abi/index\";\nexport * from \"./eip712/index\";\nexport * from \"./relay/index\";\nexport * from \"./contract/index\";\nexport * from \"./quoting/index\";\nexport * from \"./swap/index\";\nexport * from \"./auth/index\";\n\n// v1.4 — Account Abstraction primitives (EIP-7702 + ERC-4337 v0.7)\nexport * from \"./userop/index\";\nexport * from \"./paymaster/index\";\nexport * from \"./utils/index\";\n\n// v1.4 — Contract ABIs + addresses + EIP-712 v2 types.\n// MOCKED until SC team ships real ABIs. See `src/contracts/mocks/README.md`.\n// Consumers: `import { RELAYER_V2_ABI, buildBurnConsentTypedData, ... } from '@pafi-dev/core'`\n// The re-export swaps mock → real in one file when SC delivers.\nexport * from \"./contracts/index\";\n\n// v1.5 — PAFI Web handoff (mobile + desktop modal helper).\n// `openPafiWebModal()` + adapter registry for platform-specific UX.\nexport * from \"./web-handoff/index\";\n\n// -------------------------------------------------------------------------\n// Internal imports for PafiSDK class\n// -------------------------------------------------------------------------\nimport { buildMintRequestTypedData, signMintRequest, verifyMintRequest } from \"./eip712/mintRequest\";\nimport {\n buildReceiverConsentTypedData,\n signReceiverConsent,\n verifyReceiverConsent,\n} from \"./eip712/receiverConsent\";\nimport {\n getMintRequestNonce,\n getReceiverConsentNonce,\n getTokenName,\n} from \"./contract/pointToken\";\nimport {\n encodeMintAndSwap,\n decodeMintAndSwap,\n encodeExtData,\n buildRelaySwapParams,\n} from \"./relay/calldata\";\nimport { findBestQuote } from \"./quoting/quote\";\nimport {\n buildSwapFromQuote,\n buildUniversalRouterExecuteArgs,\n} from \"./swap/universalRouter\";\nimport { simulateMintAndSwap } from \"./relay/simulate\";\nimport { simulateSwap } from \"./swap/simulate\";\nimport type { SimulationResult } from \"./relay/simulate\";\nimport type { SwapSimulationResult } from \"./swap/simulate\";\nimport { createLoginMessage } from \"./auth/loginMessage\";\nimport type { LoginMessageParams } from \"./auth/types\";\nimport { ConfigurationError } from \"./errors\";\nimport type {\n BestQuote,\n EIP712Signature,\n MintParams,\n MintRequest,\n PafiSDKConfig,\n PathKey,\n PointTokenDomainConfig,\n PoolKey,\n QuoteResult,\n ReceiverConsent,\n SignatureVerification,\n SwapParams,\n} from \"./types\";\n\n// -------------------------------------------------------------------------\n// PafiSDK — convenience class wrapping all contract + crypto primitives.\n//\n// This class is HTTP-client-free on purpose. It covers signing, verifying,\n// contract reads, and calldata encoding — the things that need a signer\n// or a provider but no HTTP layer. The issuer backend defines its own HTTP\n// contract via `@pafi/issuer`; frontends build `fetch()` calls against\n// those types directly.\n// -------------------------------------------------------------------------\n\nexport class PafiSDK {\n private _pointTokenAddress?: Address;\n private _relayContractAddress?: Address;\n private _signer?: WalletClient;\n private _provider?: PublicClient;\n private _chainId?: number;\n\n constructor(config: PafiSDKConfig) {\n this._pointTokenAddress = config.pointTokenAddress;\n this._relayContractAddress = config.relayContractAddress;\n this._signer = config.signer;\n this._chainId = config.chainId;\n\n if (config.provider) {\n this._provider = config.provider;\n } else if (config.rpcUrl) {\n this._provider = createPublicClient({\n transport: http(config.rpcUrl),\n });\n }\n }\n\n // -------------------------------------------------------------------------\n // Setters\n // -------------------------------------------------------------------------\n\n setPointTokenAddress(address: Address): void {\n this._pointTokenAddress = address;\n }\n\n setRelayContractAddress(address: Address): void {\n this._relayContractAddress = address;\n }\n\n setSigner(signer: WalletClient): void {\n this._signer = signer;\n }\n\n setProvider(provider: PublicClient): void {\n this._provider = provider;\n }\n\n // -------------------------------------------------------------------------\n // Private guards\n // -------------------------------------------------------------------------\n\n private requirePointToken(): Address {\n if (!this._pointTokenAddress) {\n throw new ConfigurationError(\"pointTokenAddress not set\");\n }\n return this._pointTokenAddress;\n }\n\n private requireProvider(): PublicClient {\n if (!this._provider) {\n throw new ConfigurationError(\"provider not set\");\n }\n return this._provider;\n }\n\n private requireSigner(): WalletClient {\n if (!this._signer) {\n throw new ConfigurationError(\"signer not set\");\n }\n return this._signer;\n }\n\n private requireChainId(): number {\n if (this._chainId === undefined) {\n throw new ConfigurationError(\"chainId not set\");\n }\n return this._chainId;\n }\n\n // -------------------------------------------------------------------------\n // Domain\n // -------------------------------------------------------------------------\n\n async getDomain(): Promise<PointTokenDomainConfig> {\n const provider = this.requireProvider();\n const pointToken = this.requirePointToken();\n const chainId = this.requireChainId();\n const name = await getTokenName(provider, pointToken);\n return { name, verifyingContract: pointToken, chainId };\n }\n\n // -------------------------------------------------------------------------\n // EIP-712 — delegates to pure functions\n // -------------------------------------------------------------------------\n\n /**\n * Build the EIP-712 typed data for a MintRequest.\n * Pass the result to any external signer (Privy, WalletConnect, etc.).\n */\n async buildMintRequestTypedData(message: MintRequest) {\n const domain = await this.getDomain();\n return buildMintRequestTypedData(domain, message);\n }\n\n /**\n * Build the EIP-712 typed data for a ReceiverConsent.\n * Pass the result to any external signer (Privy, WalletConnect, etc.).\n */\n async buildReceiverConsentTypedData(message: ReceiverConsent) {\n const domain = await this.getDomain();\n return buildReceiverConsentTypedData(domain, message);\n }\n\n async signMintRequest(message: MintRequest): Promise<EIP712Signature> {\n const domain = await this.getDomain();\n return signMintRequest(this.requireSigner(), domain, message);\n }\n\n async verifyMintRequest(\n message: MintRequest,\n signature: Hex,\n expectedMinter: Address,\n ): Promise<SignatureVerification> {\n const domain = await this.getDomain();\n return verifyMintRequest(domain, message, signature, expectedMinter);\n }\n\n async signReceiverConsent(\n message: ReceiverConsent,\n ): Promise<EIP712Signature> {\n const domain = await this.getDomain();\n return signReceiverConsent(this.requireSigner(), domain, message);\n }\n\n async verifyReceiverConsent(\n message: ReceiverConsent,\n signature: Hex,\n expectedReceiver: Address,\n ): Promise<SignatureVerification> {\n const domain = await this.getDomain();\n return verifyReceiverConsent(domain, message, signature, expectedReceiver);\n }\n\n // -------------------------------------------------------------------------\n // Contract reads\n // -------------------------------------------------------------------------\n\n async getMintRequestNonce(receiver: Address): Promise<bigint> {\n return getMintRequestNonce(\n this.requireProvider(),\n this.requirePointToken(),\n receiver,\n );\n }\n\n async getReceiverConsentNonce(receiver: Address): Promise<bigint> {\n return getReceiverConsentNonce(\n this.requireProvider(),\n this.requirePointToken(),\n receiver,\n );\n }\n\n // -------------------------------------------------------------------------\n // Relay calldata — delegates to pure functions\n // -------------------------------------------------------------------------\n\n encodeMintAndSwap(mint: MintParams, swap: SwapParams): Hex {\n return encodeMintAndSwap(mint, swap);\n }\n\n decodeMintAndSwap(calldata: Hex): { mint: MintParams; swap: SwapParams } {\n return decodeMintAndSwap(calldata);\n }\n\n // -------------------------------------------------------------------------\n // Quoting — delegates to pure functions\n // -------------------------------------------------------------------------\n\n /**\n * Find the best swap route from `tokenIn` to `tokenOut`.\n * Merges `pools` with COMMON_POOLS for the configured chain, builds all\n * paths, and quotes via a single multicall RPC call.\n */\n async findBestQuote(\n tokenIn: Address,\n tokenOut: Address,\n exactAmount: bigint,\n pools: PoolKey[] = [],\n quoterAddress?: Address,\n maxHops?: number,\n ): Promise<BestQuote> {\n return findBestQuote(\n this.requireProvider(),\n this.requireChainId(),\n tokenIn,\n tokenOut,\n exactAmount,\n pools,\n quoterAddress,\n maxHops,\n );\n }\n\n // -------------------------------------------------------------------------\n // Swap building — delegates to pure functions\n // -------------------------------------------------------------------------\n\n /**\n * Build UniversalRouter execute args from a quote result.\n * The caller provides `minAmountOut` after applying their own slippage.\n */\n buildSwapFromQuote(params: {\n quote: QuoteResult;\n currencyIn: Address;\n currencyOut: Address;\n amountIn: bigint;\n minAmountOut: bigint;\n }): { commands: Hex; inputs: Hex[] } {\n return buildSwapFromQuote(params);\n }\n\n /**\n * Build Relay SwapParams + extData from a quote result.\n * Returns ready-to-use args for `Relay.mintAndSwap`.\n */\n buildRelaySwapParams(params: {\n quote: { path: PathKey[] };\n minAmountOut: bigint;\n feeInUsdt: bigint;\n deadline: bigint;\n }): { swapParams: SwapParams; extData: Hex } {\n return buildRelaySwapParams(params);\n }\n\n /**\n * Encode extData for ReceiverConsent and MintParams.\n * extData = abi.encode(minAmountOut, feeInUsdt)\n */\n encodeExtData(minAmountOut: bigint, feeInUsdt: bigint): Hex {\n return encodeExtData(minAmountOut, feeInUsdt);\n }\n\n // -------------------------------------------------------------------------\n // Simulation — dry-run via eth_call (no gas spent)\n // -------------------------------------------------------------------------\n\n /**\n * Simulate a Relay.mintAndSwap call. Catches reverts (bad signatures,\n * expired deadlines, insufficient mint cap, pool errors) before submitting.\n *\n * @param relayAddress - Relay contract address\n * @param mint - MintParams with real signatures\n * @param swap - SwapParams (path + deadline)\n * @param from - Relayer address that will submit the tx\n */\n async simulateMintAndSwap(\n relayAddress: Address,\n mint: MintParams,\n swap: SwapParams,\n from: Address,\n ): Promise<SimulationResult> {\n return simulateMintAndSwap(\n this.requireProvider(),\n relayAddress,\n mint,\n swap,\n from,\n );\n }\n\n /**\n * Simulate a UniversalRouter.execute swap call. Catches reverts (bad\n * approvals, insufficient balance, pool errors) before submitting.\n *\n * @param routerAddress - UniversalRouter contract address\n * @param commands - Packed command bytes (from buildSwapFromQuote)\n * @param inputs - Encoded inputs (from buildSwapFromQuote)\n * @param deadline - Unix timestamp\n * @param from - Address that will execute the swap\n */\n async simulateSwap(\n routerAddress: Address,\n commands: Hex,\n inputs: Hex[],\n deadline: bigint,\n from: Address,\n ): Promise<SwapSimulationResult> {\n return simulateSwap(\n this.requireProvider(),\n routerAddress,\n commands,\n inputs,\n deadline,\n from,\n );\n }\n\n // -------------------------------------------------------------------------\n // Auth — EIP-4361 login helpers (offline, stateless)\n //\n // These are convenience wrappers around the pure functions in\n // `./auth/loginMessage.ts`. They do NOT hit the network — the caller is\n // responsible for fetching the nonce, POSTing the signed message, and\n // storing the JWT returned by the issuer backend.\n // -------------------------------------------------------------------------\n\n /**\n * Build an EIP-4361 login message for the current signer + chain. The\n * caller supplies the issuer-specific fields (domain, nonce, uri); the SDK\n * fills in the wallet address and chain id from its own config.\n */\n async createLoginMessage(\n params: Omit<LoginMessageParams, \"address\" | \"chainId\">,\n ): Promise<string> {\n const signer = this.requireSigner();\n const chainId = this.requireChainId();\n const account = signer.account;\n if (!account) {\n throw new ConfigurationError(\"signer has no account attached\");\n }\n return createLoginMessage({ ...params, address: account.address, chainId });\n }\n\n /** Sign a login message string with the current signer (personal_sign) */\n async signLoginMessage(message: string): Promise<Hex> {\n const signer = this.requireSigner();\n const account = signer.account;\n if (!account) {\n throw new ConfigurationError(\"signer has no account attached\");\n }\n return signer.signMessage({ account, message });\n }\n}\n","import type { Address, Hex } from \"viem\";\n\n/**\n * ERC-4337 v0.7 EntryPoint standard address (deployed deterministically\n * at the same address across chains).\n * https://eips.ethereum.org/EIPS/eip-4337\n */\nexport const ENTRY_POINT_V07: Address =\n \"0x0000000071727De22E5E9d8BAf0edAc6f37da032\";\n\n/**\n * A single call inside a batch. `BatchExecutor.execute(Call[])` iterates\n * and invokes each one. When the batch runs via EIP-7702 delegation,\n * `msg.sender` for each call is the user's EOA.\n */\nexport interface Operation {\n target: Address;\n value: bigint;\n data: Hex;\n}\n\n/**\n * Paymaster fields attached to a UserOperation. Populated by the\n * Coinbase Paymaster (via PAFI Backend proxy) before the user signs.\n */\nexport interface PaymasterFields {\n paymaster: Address;\n paymasterData: Hex;\n paymasterVerificationGasLimit: bigint;\n paymasterPostOpGasLimit: bigint;\n}\n\n/**\n * Partial UserOp used during preparation — before paymaster fields are\n * attached or the user signs.\n */\nexport interface PartialUserOperation {\n sender: Address;\n nonce: bigint;\n callData: Hex;\n callGasLimit: bigint;\n verificationGasLimit: bigint;\n preVerificationGas: bigint;\n maxFeePerGas: bigint;\n maxPriorityFeePerGas: bigint;\n}\n\n/**\n * Full ERC-4337 v0.7 UserOperation, ready for bundler submission.\n */\nexport interface UserOperation extends PartialUserOperation {\n paymaster: Address;\n paymasterData: Hex;\n paymasterVerificationGasLimit: bigint;\n paymasterPostOpGasLimit: bigint;\n signature: Hex;\n}\n\n/**\n * Receipt returned by a bundler after a UserOp lands on-chain.\n */\nexport interface UserOpReceipt {\n userOpHash: Hex;\n success: boolean;\n txHash: Hex;\n blockNumber: bigint;\n gasUsed: bigint;\n /** Effective gas cost paid (wei). */\n actualGasCost: bigint;\n}\n\n/**\n * Sentinel operation value used in tests + docs when `Operation.value`\n * is irrelevant (ERC-20 transfers, for example).\n */\nexport const ZERO_VALUE = 0n;\n","import type { PaymasterConfig } from \"./types\";\n\n/**\n * Module-level paymaster config. Read by batch builders (via\n * `getPaymasterConfig()`) and by `@pafi/issuer` `RelayService` when\n * building UserOps.\n *\n * Consumers call `setPaymasterConfig()` once at application boot. All\n * subsequent helpers that need the feeRecipient / issuer identity /\n * backend URL pull from here.\n *\n * This is global state by design — making every batch builder take a\n * config param would clutter every call site. The alternative (a\n * context/service) has more ceremony than this flow justifies.\n */\nlet _config: PaymasterConfig | null = null;\n\n/**\n * Set the application-wide paymaster config. Safe to call multiple\n * times (later calls override earlier). Throws if required fields\n * are missing.\n */\nexport function setPaymasterConfig(config: PaymasterConfig): void {\n if (!config.pafiBackendUrl) {\n throw new Error(\"setPaymasterConfig: pafiBackendUrl is required\");\n }\n if (!config.issuerId) {\n throw new Error(\"setPaymasterConfig: issuerId is required\");\n }\n if (!config.apiKey) {\n throw new Error(\"setPaymasterConfig: apiKey is required\");\n }\n if (!config.feeRecipient) {\n throw new Error(\"setPaymasterConfig: feeRecipient is required\");\n }\n _config = { ...config };\n}\n\n/**\n * Get the current paymaster config. Throws if `setPaymasterConfig()`\n * has not been called yet — this surfaces boot-order bugs early\n * instead of failing with \"paymaster.feeRecipient is undefined\" at\n * the point of use.\n */\nexport function getPaymasterConfig(): PaymasterConfig {\n if (!_config) {\n throw new Error(\n \"PaymasterConfig not initialized — call setPaymasterConfig() at application boot before invoking any batch builder\",\n );\n }\n return _config;\n}\n\n/** Test helper — clear the singleton. */\nexport function _resetPaymasterConfigForTests(): void {\n _config = null;\n}\n\n/** Check whether paymaster config has been initialized. */\nexport function isPaymasterConfigured(): boolean {\n return _config !== null;\n}\n","import type { Address, PublicClient } from \"viem\";\n\n/**\n * Submission path chosen by `checkEthAndBranch`.\n * - `normal`: initiator has enough ETH; submit via `walletClient.writeContract`\n * or a plain `eth_sendRawTransaction`. No paymaster round-trip.\n * - `paymaster`: initiator doesn't have enough ETH; wrap the batch as a\n * UserOperation and route through PAFI Backend → Coinbase Paymaster\n * → Bundler.\n */\nexport type SubmissionPath = \"normal\" | \"paymaster\";\n\nexport interface CheckEthAndBranchParams {\n /** viem PublicClient bound to the target chain. */\n client: PublicClient;\n /** The address whose ETH balance we check. */\n initiator: Address;\n /** Estimated gas cost in wei for the upcoming tx. */\n estimatedGasWei: bigint;\n /**\n * Optional safety margin multiplier (basis points). Defaults to\n * 11_000 (110%) — the initiator needs 10% above the estimate to\n * qualify for the normal path. Prevents edge cases where gas price\n * spikes between estimation and submission cause a \"has enough\"\n * decision to fail at broadcast time.\n */\n marginBps?: number;\n}\n\nconst DEFAULT_MARGIN_BPS = 11_000;\n\n/**\n * Step 3 of the Generalized Flow ([SPONSORED_PATH_FLOW.md §2]):\n * choose between the normal path (initiator pays ETH directly) and the\n * paymaster path (bundler + Coinbase Paymaster).\n *\n * Intentionally synchronous in spirit — the only network call is\n * `getBalance`. Callers can parallelize it with other reads.\n */\nexport async function checkEthAndBranch(\n params: CheckEthAndBranchParams,\n): Promise<SubmissionPath> {\n const marginBps = params.marginBps ?? DEFAULT_MARGIN_BPS;\n const required =\n (params.estimatedGasWei * BigInt(marginBps)) / 10_000n;\n\n const balance = await params.client.getBalance({\n address: params.initiator,\n });\n\n return balance >= required ? \"normal\" : \"paymaster\";\n}\n","import { parseAbi } from \"viem\";\nimport type { Address } from \"viem\";\n\n/**\n * ⚠️ MOCK — Relayer v2 `mint()` ABI for the v1.4 sponsored flow.\n *\n * Signature guess based on stakeholder memo (`REQUIREMENTS_V2.md §3`):\n *\n * mint(\n * MintRequest request, // EIP-712 signed by user + issuer\n * Signature userSig, // v, r, s\n * Signature issuerSig, // v, r, s\n * uint256 feeAmount, // PT units to split off to feeRecipient\n * address feeRecipient // typically operator or fee collector\n * )\n *\n * Behaviour expected:\n * - Verify user + issuer EIP-712 signatures against `request`\n * - Mint `request.amount` PT to `request.to`\n * - Atomic transfer `feeAmount` from `request.to` → `feeRecipient`\n * (net mint = `amount - feeAmount`)\n * - Increment the on-chain nonce to prevent replay\n *\n * When SC team publishes the real ABI, drop it in under\n * `src/contracts/real/relayerV2.abi.ts` and flip the export in\n * `src/contracts/index.ts` — call sites unchanged.\n *\n * See [SC_MOCK_PLAN.md §2.1] for the rationale + swap checklist.\n */\n\nexport const MOCK_RELAYER_V2_ABI = parseAbi([\n \"function mint((address to, uint256 amount, uint256 feeAmount, address feeRecipient, uint256 nonce, uint256 deadline, bytes extData) request, (uint8 v, bytes32 r, bytes32 s) userSig, (uint8 v, bytes32 r, bytes32 s) issuerSig) external\",\n // View functions we'll likely want (nonce getter is standard)\n \"function mintRequestNonce(address user) external view returns (uint256)\",\n \"event Minted(address indexed user, uint256 amount, uint256 feeAmount, address indexed feeRecipient, bytes32 requestHash)\",\n] as const);\n\n/**\n * Calldata function selector for `mint((...),(...),(...))` as encoded\n * by viem against {@link MOCK_RELAYER_V2_ABI}. Callers compare against\n * this to sanity-check decoded UserOp inner calls.\n *\n * Recompute when the real ABI lands — almost certainly differs.\n */\nexport const MOCK_RELAYER_V2_MINT_SELECTOR = \"0xMOCKED__\" as const;\n\n/**\n * Struct shape that matches {@link MOCK_RELAYER_V2_ABI}. Exported so\n * builders can accept a typed input without `as const` gymnastics.\n */\nexport interface MockMintRequestV2 {\n to: Address;\n amount: bigint;\n feeAmount: bigint;\n feeRecipient: Address;\n nonce: bigint;\n deadline: bigint;\n extData: `0x${string}`;\n}\n\nexport interface MockSignatureStruct {\n v: number;\n r: `0x${string}`;\n s: `0x${string}`;\n}\n","import { parseAbi } from \"viem\";\n\n/**\n * ⚠️ MOCK — `PointToken` v1.4 burn surface.\n *\n * Two variants mocked — SC team will pick ONE before stable; the other\n * gets deleted during Phase B swap.\n *\n * Variant A (simpler, most likely):\n * burn(uint256 amount)\n * - burns from `msg.sender`\n * - caller handles authorization (the user is `msg.sender` via\n * EIP-7702 delegation, so `msg.sender == user`)\n *\n * Variant B (signature-based, if SC prefers explicit consent on-chain):\n * burnWithSig(BurnConsent consent, Signature sig)\n * - verifies EIP-712 signature before burning\n * - used when the caller isn't the user (e.g. a relayer burns on\n * behalf of the user)\n *\n * Either way the ERC-20 emits `Transfer(from, address(0), amount)` so\n * `BurnIndexer` semantics don't change.\n */\nexport const MOCK_POINT_TOKEN_V2_ABI = parseAbi([\n // Variant A\n \"function burn(uint256 amount) external\",\n // Variant B\n \"function burnWithSig((address user, address pointToken, uint256 amount, uint256 nonce, uint256 deadline) consent, (uint8 v, bytes32 r, bytes32 s) sig) external\",\n // Standard reads we always need\n \"function balanceOf(address user) external view returns (uint256)\",\n \"function burnNonce(address user) external view returns (uint256)\",\n // ERC-20 Transfer event (inherited) — BurnIndexer filters `to = 0x0`\n \"event Transfer(address indexed from, address indexed to, uint256 value)\",\n] as const);\n","import { parseAbi } from \"viem\";\nimport type { Address } from \"viem\";\n\n/**\n * ⚠️ MOCK — placeholder BatchExecutor address.\n *\n * Real deployment:\n * - Coinbase may pre-deploy a canonical BatchExecutor that we target\n * via EIP-7702 delegation (see blocker B2 in V1.4_V1.5_READINESS.md)\n * - Otherwise PAFI deploys a minimal one and publishes the address\n *\n * The ABI itself (`execute(Call[])`) is stable and lives under\n * `../../userop/batchExecute.ts` — that's the real one, unchanged\n * by the mock swap. Only the **address** is mocked here.\n *\n * Pattern: checksum address with hex \"DEAD0001\" suffix per chain so\n * it's obvious in logs which chain is using a mock.\n */\n\n// Base Sepolia (testnet)\nexport const MOCK_BATCH_EXECUTOR_ADDRESS_BASE_SEPOLIA =\n \"0x000000000000000000000000000000000000DE01\" as Address;\n\n// Base mainnet\nexport const MOCK_BATCH_EXECUTOR_ADDRESS_BASE_MAINNET =\n \"0x000000000000000000000000000000000000DE02\" as Address;\n\n// Re-export the real ABI from userop module (not mocked — ABI is stable)\nexport { BATCH_EXECUTOR_ABI } from \"../../userop/batchExecute\";\n","import type { Address } from \"viem\";\n\n/**\n * ⚠️ MOCK — per-chain contract addresses for the v1.4 sponsored flow.\n *\n * Real addresses come from SC team once they deploy Relayer v2 and\n * pick a BatchExecutor. Until then we use placeholders so the SDK\n * code path compiles + tests execute end-to-end.\n *\n * **Intentionally not merge-safe to mainnet** — callers that resolve\n * a mainnet address here get a `0x...DEAD` placeholder which will\n * revert at contract-level. That's the point: prod refuses to run\n * on mocks.\n *\n * Chain id map:\n * - 8453 Base mainnet\n * - 84532 Base Sepolia\n *\n * Swap workflow when SC delivers:\n * 1. Rename this file → `addresses.ts` under `src/contracts/real/`\n * 2. Fill real addresses per chain\n * 3. Update `src/contracts/index.ts` re-export\n * 4. Delete MOCK_* named exports\n */\n\nexport interface ContractAddresses {\n relayerV2: Address;\n pointToken: Address;\n batchExecutor: Address;\n usdt: Address;\n issuerRegistry: Address;\n mintingOracle: Address;\n}\n\nconst MOCK_PLACEHOLDER = (suffix: string): Address =>\n `0x000000000000000000000000000000000000${suffix.toLowerCase().padStart(4, \"0\")}` as Address;\n\nexport const MOCK_ADDRESSES: Record<number, ContractAddresses> = {\n // Base Sepolia — safe targets for integration tests (MockRelayer\n // fixtures deployed by Hardhat will override these at test time)\n 84532: {\n relayerV2: MOCK_PLACEHOLDER(\"de10\"),\n pointToken: MOCK_PLACEHOLDER(\"de11\"),\n batchExecutor: MOCK_PLACEHOLDER(\"de01\"),\n usdt: MOCK_PLACEHOLDER(\"de12\"),\n issuerRegistry: MOCK_PLACEHOLDER(\"de13\"),\n mintingOracle: MOCK_PLACEHOLDER(\"de14\"),\n },\n // Base mainnet — intentionally left as placeholder. Do NOT ship to\n // prod without swapping.\n 8453: {\n relayerV2: MOCK_PLACEHOLDER(\"dead\"),\n pointToken: MOCK_PLACEHOLDER(\"dead\"),\n batchExecutor: MOCK_PLACEHOLDER(\"dead\"),\n usdt: MOCK_PLACEHOLDER(\"dead\"),\n issuerRegistry: MOCK_PLACEHOLDER(\"dead\"),\n mintingOracle: MOCK_PLACEHOLDER(\"dead\"),\n },\n};\n\n/**\n * Lookup helper — throws if the chain isn't in the map so callers fail\n * loudly on misconfiguration instead of silently using `undefined`.\n */\nexport function getMockAddresses(chainId: number): ContractAddresses {\n const addrs = MOCK_ADDRESSES[chainId];\n if (!addrs) {\n throw new Error(\n `getMockAddresses: no mock addresses for chainId ${chainId}. ` +\n `Supported: ${Object.keys(MOCK_ADDRESSES).join(\", \")}`,\n );\n }\n return addrs;\n}\n","import {\n parseSignature,\n recoverTypedDataAddress,\n type Address,\n type Hex,\n type WalletClient,\n} from \"viem\";\nimport { buildDomain } from \"../../../eip712/domain\";\nimport type {\n EIP712Signature,\n PointTokenDomainConfig,\n SignatureVerification,\n} from \"../../../types\";\nimport type { MockMintRequestV2 } from \"../relayerV2.mock\";\n\n/**\n * ⚠️ MOCK — `MintRequest` v2 EIP-712 types.\n *\n * Extends v0.2.x `MintRequest` (4 fields) with three new fields needed\n * for the v1.4 sponsored flow:\n *\n * + feeAmount PT units taken from the mint for the operator\n * + feeRecipient where the fee goes (operator or treasury)\n * + extData arbitrary bytes for future extensions (swap path, etc.)\n *\n * When SC team confirms the real struct, drop this file's content into\n * the real builder and remove the MOCK_ prefix. The function signatures\n * below stay stable — only `MOCK_MINT_REQUEST_V2_TYPES` changes.\n */\nexport const MOCK_MINT_REQUEST_V2_TYPES = {\n MintRequest: [\n { name: \"to\", type: \"address\" },\n { name: \"amount\", type: \"uint256\" },\n { name: \"feeAmount\", type: \"uint256\" },\n { name: \"feeRecipient\", type: \"address\" },\n { name: \"nonce\", type: \"uint256\" },\n { name: \"deadline\", type: \"uint256\" },\n { name: \"extData\", type: \"bytes\" },\n ],\n} as const;\n\n/**\n * Build the typed-data envelope that any EIP-712 signer (viem, ethers,\n * Privy) can consume. Callers typically pass the result to\n * `walletClient.signTypedData()` or `privy.signTypedData()`.\n */\nexport function buildMockMintRequestV2TypedData(\n domain: PointTokenDomainConfig,\n message: MockMintRequestV2,\n) {\n return {\n domain: buildDomain(domain),\n types: MOCK_MINT_REQUEST_V2_TYPES,\n primaryType: \"MintRequest\" as const,\n message,\n };\n}\n\n/**\n * Sign a `MintRequest` v2 with a viem WalletClient — server-side flow\n * where the issuer holds the private key directly (KMS-backed signer\n * is the production path; see `@pafi-dev/issuer` KMSSignerAdapter).\n */\nexport async function signMockMintRequestV2(\n walletClient: WalletClient,\n domain: PointTokenDomainConfig,\n message: MockMintRequestV2,\n): Promise<EIP712Signature> {\n const serialized = await walletClient.signTypedData({\n account: walletClient.account!,\n domain: buildDomain(domain),\n types: MOCK_MINT_REQUEST_V2_TYPES,\n primaryType: \"MintRequest\",\n message,\n });\n\n const { v, r, s } = parseSignature(serialized);\n return {\n v: Number(v),\n r,\n s,\n serialized,\n };\n}\n\n/**\n * Verify a `MintRequest` v2 signature and check it came from the\n * expected signer (either user or issuer depending on which side is\n * being verified).\n */\nexport async function verifyMockMintRequestV2(\n domain: PointTokenDomainConfig,\n message: MockMintRequestV2,\n signature: Hex,\n expectedSigner: Address,\n): Promise<SignatureVerification> {\n const recoveredAddress = await recoverTypedDataAddress({\n domain: buildDomain(domain),\n types: MOCK_MINT_REQUEST_V2_TYPES,\n primaryType: \"MintRequest\",\n message,\n signature,\n });\n\n const isValid =\n recoveredAddress.toLowerCase() === expectedSigner.toLowerCase();\n\n return {\n isValid,\n recoveredAddress,\n };\n}\n","import {\n parseSignature,\n recoverTypedDataAddress,\n type Address,\n type Hex,\n type WalletClient,\n} from \"viem\";\nimport { buildDomain } from \"../../../eip712/domain\";\nimport type {\n EIP712Signature,\n PointTokenDomainConfig,\n SignatureVerification,\n} from \"../../../types\";\n\n/**\n * ⚠️ MOCK — `BurnConsent` EIP-712 type for the v1.4 reverse flow.\n *\n * New type entirely — v0.2.x had no burn-for-credit flow.\n *\n * User signs `BurnConsent` to authorize burning `amount` PT from their\n * wallet in exchange for an off-chain credit of (amount - gasFee).\n * Signature is consumed either by:\n * - A relayer calling `PointToken.burnWithSig(...)` (Variant B)\n * - The paymaster-proxy-sponsored UserOp calling `PointToken.burn(...)`\n * with msg.sender = user via EIP-7702 (Variant A)\n *\n * Either way the tx emits `Transfer(user → 0x0)` which the issuer's\n * `BurnIndexer` watches for and uses to credit the off-chain ledger.\n *\n * Field list is a best-guess — SC team may add fields (e.g. `feeAmount`\n * explicit, `extData`). Update this mock when they freeze the spec.\n */\nexport interface MockBurnConsent {\n user: Address;\n pointToken: Address;\n amount: bigint;\n nonce: bigint;\n deadline: bigint;\n}\n\nexport const MOCK_BURN_CONSENT_TYPES = {\n BurnConsent: [\n { name: \"user\", type: \"address\" },\n { name: \"pointToken\", type: \"address\" },\n { name: \"amount\", type: \"uint256\" },\n { name: \"nonce\", type: \"uint256\" },\n { name: \"deadline\", type: \"uint256\" },\n ],\n} as const;\n\nexport function buildMockBurnConsentTypedData(\n domain: PointTokenDomainConfig,\n message: MockBurnConsent,\n) {\n return {\n domain: buildDomain(domain),\n types: MOCK_BURN_CONSENT_TYPES,\n primaryType: \"BurnConsent\" as const,\n message,\n };\n}\n\nexport async function signMockBurnConsent(\n walletClient: WalletClient,\n domain: PointTokenDomainConfig,\n message: MockBurnConsent,\n): Promise<EIP712Signature> {\n const serialized = await walletClient.signTypedData({\n account: walletClient.account!,\n domain: buildDomain(domain),\n types: MOCK_BURN_CONSENT_TYPES,\n primaryType: \"BurnConsent\",\n message,\n });\n\n const { v, r, s } = parseSignature(serialized);\n return {\n v: Number(v),\n r,\n s,\n serialized,\n };\n}\n\nexport async function verifyMockBurnConsent(\n domain: PointTokenDomainConfig,\n message: MockBurnConsent,\n signature: Hex,\n expectedUser: Address,\n): Promise<SignatureVerification> {\n const recoveredAddress = await recoverTypedDataAddress({\n domain: buildDomain(domain),\n types: MOCK_BURN_CONSENT_TYPES,\n primaryType: \"BurnConsent\",\n message,\n signature,\n });\n\n const isValid =\n recoveredAddress.toLowerCase() === expectedUser.toLowerCase();\n\n return {\n isValid,\n recoveredAddress,\n };\n}\n","import type {\n ModalOpenOptions,\n PafiWebModalAdapter,\n PafiWebModalHandle,\n} from \"./types\";\n\nconst DEFAULT_WIDTH = 900;\nconst DEFAULT_HEIGHT = 700;\nconst DEFAULT_NAME = \"pafi-web\";\n\n/**\n * Web browser popup adapter. Opens the given URL in a centered\n * `window.open()` popup, polls for close, and optionally forwards\n * `postMessage` events back to the caller.\n *\n * Runtime requirement: `window.open` must exist. Throws if called\n * under Node / SSR / React Native — use `setPafiWebModalAdapter()` to\n * provide a platform-specific adapter in those environments.\n *\n * ## Popup blocking\n *\n * Browsers block `window.open()` unless it happens inside a user\n * gesture (click handler). Callers MUST wire this into a direct click\n * handler — wrapping it in a `setTimeout` or async await before the\n * open call will trigger the blocker.\n */\nexport function openWebPopup(\n url: string,\n options: ModalOpenOptions = {},\n): PafiWebModalHandle {\n if (typeof window === \"undefined\" || typeof window.open !== \"function\") {\n throw new Error(\n \"openWebPopup: `window.open` is not available in this runtime. \" +\n \"Register a platform adapter via setPafiWebModalAdapter() instead.\",\n );\n }\n\n const width = options.width ?? DEFAULT_WIDTH;\n const height = options.height ?? DEFAULT_HEIGHT;\n const name = options.windowName ?? DEFAULT_NAME;\n\n // Center on the screen — works in all major browsers. Falls back\n // gracefully if `screen.availWidth`/`screenX` are unavailable.\n const screenW = window.screen?.availWidth ?? window.innerWidth;\n const screenH = window.screen?.availHeight ?? window.innerHeight;\n const left = Math.max(0, Math.floor((screenW - width) / 2));\n const top = Math.max(0, Math.floor((screenH - height) / 2));\n\n const features = [\n `width=${width}`,\n `height=${height}`,\n `left=${left}`,\n `top=${top}`,\n \"resizable=yes\",\n \"scrollbars=yes\",\n \"noopener=no\", // we need opener.postMessage to work\n ].join(\",\");\n\n const popup = window.open(url, name, features);\n if (!popup) {\n throw new Error(\n \"openWebPopup: popup was blocked. Ensure this call runs inside a user gesture (click handler).\",\n );\n }\n\n // Set up state + listeners --------------------------------------------\n\n let closed = false;\n let pollId: ReturnType<typeof setInterval> | null = null;\n let messageListener: ((e: MessageEvent) => void) | null = null;\n\n const dispose = (): void => {\n if (closed) return;\n closed = true;\n if (pollId !== null) {\n clearInterval(pollId);\n pollId = null;\n }\n if (messageListener) {\n window.removeEventListener(\"message\", messageListener);\n messageListener = null;\n }\n options.onClose?.();\n };\n\n // Poll every 500ms for popup close — there's no `close` event.\n // Stops when `popup.closed` flips to true OR our handle is closed.\n pollId = setInterval(() => {\n if (popup.closed) {\n dispose();\n }\n }, 500);\n\n // Wire postMessage filtering by origin — secure-by-default. An\n // empty or missing `allowedOrigins` rejects ALL messages; the\n // caller must explicitly whitelist PAFI Web's host(s) before any\n // payload is delivered. This prevents a compromised popup (via\n // opener leak or redirect) from impersonating PAFI Web.\n if (options.onMessage) {\n const allowed = options.allowedOrigins ?? [];\n const onMessage = options.onMessage;\n messageListener = (event: MessageEvent): void => {\n if (event.source !== popup) return;\n if (!allowed.includes(event.origin)) return;\n onMessage(event.data, event.origin);\n };\n window.addEventListener(\"message\", messageListener);\n }\n\n // Handle object -------------------------------------------------------\n\n return {\n get isOpen(): boolean {\n return !closed && !popup.closed;\n },\n close(): void {\n if (closed) return;\n try {\n popup.close();\n } catch {\n // Some browsers block close() unless the popup was opened by\n // the same document. Dispose our listeners anyway.\n }\n dispose();\n },\n focus(): void {\n if (closed || popup.closed) return;\n try {\n popup.focus();\n } catch {\n // No-op on failure — focus is best-effort.\n }\n },\n postMessage(data: unknown): void {\n if (closed || popup.closed) return;\n try {\n // targetOrigin '*' is fine here because the caller owns the\n // data being sent. For security on the receiving side, the\n // PAFI Web page should check event.origin itself.\n popup.postMessage(data, \"*\");\n } catch {\n // No-op.\n }\n },\n };\n}\n\n/**\n * The web popup packaged as a {@link PafiWebModalAdapter} so callers\n * can register it explicitly (e.g. in a test harness).\n */\nexport const webPopupAdapter: PafiWebModalAdapter = {\n open(url, options) {\n return openWebPopup(url, options);\n },\n};\n","import { openWebPopup } from \"./webPopup\";\nimport type {\n ModalOpenOptions,\n PafiWebModalAdapter,\n PafiWebModalHandle,\n} from \"./types\";\n\nexport type {\n ModalOpenOptions,\n PafiWebModalAdapter,\n PafiWebModalHandle,\n} from \"./types\";\nexport { openWebPopup, webPopupAdapter } from \"./webPopup\";\n\n/**\n * Module-level adapter registry — allows platform consumers (React\n * Native, Electron, Tauri, etc.) to plug in their own handoff\n * implementation without forking the SDK.\n *\n * Callers set this once at app boot; `openPafiWebModal()` below uses\n * whatever is registered, or falls back to the web popup adapter\n * when `window.open` is available.\n */\nlet registeredAdapter: PafiWebModalAdapter | null = null;\n\n/**\n * Register the adapter used by `openPafiWebModal()`. Typically called\n * once during app initialization — mobile apps register a\n * SFSafariViewController / Chrome Custom Tabs adapter, desktop apps\n * can leave it unset (web popup is the default).\n *\n * Pass `null` to unregister and fall back to the default.\n */\nexport function setPafiWebModalAdapter(\n adapter: PafiWebModalAdapter | null,\n): void {\n registeredAdapter = adapter;\n}\n\n/**\n * Return the currently registered adapter, or `null` when none is set.\n * Useful for tests that want to snapshot-and-restore the adapter.\n */\nexport function getPafiWebModalAdapter(): PafiWebModalAdapter | null {\n return registeredAdapter;\n}\n\n/**\n * Open PAFI Web in the host platform's appropriate UX:\n *\n * - Browser (window.open): centered popup, 900×700 by default\n * - React Native (with adapter registered): SFSafariViewController\n * / Chrome Custom Tabs via `react-native-inappbrowser-reborn` or\n * `expo-web-browser`\n * - Desktop (with adapter registered): custom BrowserWindow / new tab\n *\n * Resolution order:\n * 1. If an adapter was registered via `setPafiWebModalAdapter()`, use it.\n * 2. Else if `window.open` is available, use the built-in web popup.\n * 3. Else throw with a clear error pointing at the adapter registry.\n *\n * @example\n * ```ts\n * // User clicks \"Trade on PAFI\" button\n * button.addEventListener('click', async () => {\n * const modal = await openPafiWebModal('https://app.pacificfinance.org', {\n * allowedOrigins: ['https://app.pacificfinance.org'],\n * onMessage: (data, origin) => {\n * if (typeof data === 'object' && data && 'txHash' in data) {\n * console.log('Swap confirmed:', data.txHash);\n * modal.close();\n * }\n * },\n * onClose: () => {\n * console.log('User closed modal');\n * },\n * });\n * });\n * ```\n */\nexport async function openPafiWebModal(\n url: string,\n options: ModalOpenOptions = {},\n): Promise<PafiWebModalHandle> {\n if (!url || typeof url !== \"string\") {\n throw new Error(\"openPafiWebModal: `url` is required\");\n }\n\n if (registeredAdapter) {\n return Promise.resolve(registeredAdapter.open(url, options));\n }\n\n if (typeof window !== \"undefined\" && typeof window.open === \"function\") {\n return openWebPopup(url, options);\n }\n\n throw new Error(\n \"openPafiWebModal: no adapter registered and `window.open` is unavailable. \" +\n \"Call `setPafiWebModalAdapter()` with a platform-specific adapter (e.g. SFSafariViewController on iOS) during app boot.\",\n );\n}\n"],"mappings":"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","names":["parseAbi","parseSignature","recoverTypedDataAddress","parseSignature","recoverTypedDataAddress"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pafi-dev/core",
3
- "version": "0.3.0-beta.1",
3
+ "version": "0.3.0-beta.2",
4
4
  "description": "EIP-712 signing, contract interaction, and Relay calldata for the PAFI point token system",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",