@pafi-dev/core 0.25.0 → 0.25.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/auth/index.cjs +2 -2
- package/dist/auth/index.js +1 -1
- package/dist/{chunk-3ZT7KTN4.cjs → chunk-F7IKZKZX.cjs} +23 -6
- package/dist/chunk-F7IKZKZX.cjs.map +1 -0
- package/dist/{chunk-K4GBOB5V.js → chunk-MOTNU74I.js} +23 -6
- package/dist/chunk-MOTNU74I.js.map +1 -0
- package/dist/{chunk-IKLFFJJK.js → chunk-NSTUVR2D.js} +1 -1
- package/dist/chunk-NSTUVR2D.js.map +1 -0
- package/dist/{chunk-6DSK5VR2.cjs → chunk-QFBHBFEY.cjs} +1 -1
- package/dist/chunk-QFBHBFEY.cjs.map +1 -0
- package/dist/eip712/index.cjs +2 -2
- package/dist/eip712/index.js +1 -1
- package/dist/index.cjs +160 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +91 -5
- package/dist/index.d.ts +91 -5
- package/dist/index.js +148 -20
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-3ZT7KTN4.cjs.map +0 -1
- package/dist/chunk-6DSK5VR2.cjs.map +0 -1
- package/dist/chunk-IKLFFJJK.js.map +0 -1
- package/dist/chunk-K4GBOB5V.js.map +0 -1
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/phitran/Pacific-Finance/pafi-backend/pafi-sdk/packages/core/dist/chunk-3ZT7KTN4.cjs","../src/eip712/domain.ts","../src/eip712/mintRequest.ts","../src/constants.ts","../src/eip712/verifyDeadline.ts","../src/eip712/burnRequest.ts"],"names":["parseSignature","recoverTypedDataAddress","getAddress"],"mappings":"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","file":"/Users/phitran/Pacific-Finance/pafi-backend/pafi-sdk/packages/core/dist/chunk-3ZT7KTN4.cjs","sourcesContent":[null,"import type { Address, PublicClient } from \"viem\";\nimport type { PointTokenDomainConfig } from \"../types\";\nimport { pointTokenAbi } from \"../abi/pointToken\";\n\n/**\n * Build the EIP-712 domain struct from a PointToken config. Uses\n * `config.version` when supplied; defaults to `\"1\"` for back-compat.\n */\nexport function buildDomain(config: PointTokenDomainConfig) {\n return {\n name: config.name,\n version: config.version ?? \"1\",\n chainId: config.chainId,\n verifyingContract: config.verifyingContract,\n };\n}\n\n/**\n * Domain mismatch error thrown by `assertDomainMatchesContract`.\n */\nexport class Eip712DomainMismatchError extends Error {\n constructor(\n public readonly field: \"name\" | \"version\" | \"chainId\" | \"verifyingContract\",\n public readonly expected: string,\n public readonly actual: string,\n ) {\n super(\n `EIP-712 domain mismatch on field \"${field}\": expected ${expected}, got ${actual}. ` +\n `Local SDK config is out of sync with deployed PointToken — signatures will be rejected on-chain. ` +\n `Update SDK config or contract before producing more signatures.`,\n );\n this.name = \"Eip712DomainMismatchError\";\n }\n}\n\n/**\n * One-RPC health check that the local EIP-712 domain config matches\n * what the deployed `PointToken` reports via `eip712Domain()`. If the\n * contract has bumped `version` from `\"1\"` to `\"2\"` (or any field\n * differs), every signature produced with the stale config will be\n * silently rejected on-chain (`ECDSA: invalid signature` — opaque to\n * the user).\n *\n * Recommended: call once at issuer-startup health check, not on every\n * mint. Throws `Eip712DomainMismatchError` describing the diverged\n * field.\n *\n * @example\n * ```ts\n * import { assertDomainMatchesContract, buildDomain } from \"@pafi-dev/core\";\n *\n * const expected = buildDomain({\n * name: \"PointToken\",\n * chainId: 8453,\n * verifyingContract: \"0x855c2046AD49AcF9B3B32557176FfCB1a1A38A22\",\n * });\n *\n * await assertDomainMatchesContract(publicClient, expected);\n * // → throws Eip712DomainMismatchError if version on-chain has bumped\n * ```\n */\nexport async function assertDomainMatchesContract(\n client: PublicClient,\n expected: ReturnType<typeof buildDomain>,\n): Promise<void> {\n const onChain = (await client.readContract({\n address: expected.verifyingContract as Address,\n abi: pointTokenAbi,\n functionName: \"eip712Domain\",\n })) as readonly [\n `0x${string}`, // fields (bytes1)\n string, // name\n string, // version\n bigint, // chainId\n Address, // verifyingContract\n `0x${string}`, // salt\n readonly bigint[], // extensions\n ];\n\n const [, name, version, chainId, verifyingContract] = onChain;\n\n if (name !== expected.name) {\n throw new Eip712DomainMismatchError(\"name\", expected.name, name);\n }\n if (version !== expected.version) {\n throw new Eip712DomainMismatchError(\n \"version\",\n expected.version,\n version,\n );\n }\n if (chainId !== BigInt(expected.chainId)) {\n throw new Eip712DomainMismatchError(\n \"chainId\",\n String(expected.chainId),\n chainId.toString(),\n );\n }\n if (\n verifyingContract.toLowerCase() !==\n expected.verifyingContract.toLowerCase()\n ) {\n throw new Eip712DomainMismatchError(\n \"verifyingContract\",\n expected.verifyingContract,\n verifyingContract,\n );\n }\n}\n","import { getAddress, parseSignature, recoverTypedDataAddress } from \"viem\";\nimport type { Address, Hex, WalletClient } from \"viem\";\nimport { mintRequestTypes } from \"../constants\";\nimport type {\n EIP712Signature,\n MintRequest,\n PointTokenDomainConfig,\n SignatureVerification,\n SignatureVerifyOptions,\n} from \"../types\";\nimport { buildDomain } from \"./domain\";\nimport { assertValidCurrentTimeSec } from \"./verifyDeadline\";\n\nconst PRIMARY_TYPE = \"MintForRequest\" as const;\n\n/**\n * Build the EIP-712 typed data object for a MintForRequest.\n * Returns the standard `{ domain, types, primaryType, message }` structure\n * that any EIP-712 signer (viem, ethers, Privy, WalletConnect) can consume.\n */\nexport function buildMintRequestTypedData(\n domain: PointTokenDomainConfig,\n message: MintRequest,\n) {\n return {\n domain: buildDomain(domain),\n types: mintRequestTypes,\n primaryType: PRIMARY_TYPE,\n message,\n };\n}\n\n/**\n * Sign a MintForRequest. Caller passes the full 5-field message:\n * - user = off-chain spender (drives nonce stream)\n * - receiver = on-chain caller (= msg.sender of `mint()`; wrapper or user)\n * - amount = PT amount\n * - nonce = pointToken.mintRequestNonces(user)\n * - deadline = unix seconds\n */\nexport async function signMintRequest(\n walletClient: WalletClient,\n domain: PointTokenDomainConfig,\n message: MintRequest,\n): Promise<EIP712Signature> {\n const serialized = await walletClient.signTypedData({\n account: walletClient.account!,\n domain: buildDomain(domain),\n types: mintRequestTypes,\n primaryType: PRIMARY_TYPE,\n message,\n });\n\n const { v, r, s } = parseSignature(serialized);\n\n return {\n v: Number(v),\n r,\n s,\n serialized,\n };\n}\n\n/**\n * Verify a MintForRequest signature. Always recovers the signer and\n * compares against `expectedMinter`. Pass `options.currentTimeSec`\n * (unix seconds) to additionally enforce the request's `deadline` —\n * the on-chain contract enforces `block.timestamp <= deadline`, so\n * a stale-but-cryptographically-valid sig will only revert\n * on-submission unless the caller pre-flights here.\n *\n * Result shape:\n * - `{ isValid: true, recoveredAddress }` — sig matches and (if\n * deadline check requested) deadline has not elapsed.\n * - `{ isValid: false, recoveredAddress, reason: 'INVALID_SIGNER' }`\n * — sig does not recover to `expectedMinter`.\n * - `{ isValid: false, recoveredAddress, reason: 'DEADLINE_PASSED' }`\n * — sig is valid but `deadline < currentTimeSec`.\n *\n * Throws (synchronous Promise rejection):\n * - When `options.currentTimeSec` is provided but not a finite,\n * non-negative number. Misusing the API should fail loudly.\n */\nexport async function verifyMintRequest(\n domain: PointTokenDomainConfig,\n message: MintRequest,\n signature: Hex,\n expectedMinter: Address,\n options?: SignatureVerifyOptions,\n): Promise<SignatureVerification> {\n assertValidCurrentTimeSec(options?.currentTimeSec);\n\n const recoveredAddress = await recoverTypedDataAddress({\n domain: buildDomain(domain),\n types: mintRequestTypes,\n primaryType: PRIMARY_TYPE,\n message,\n signature,\n });\n\n if (getAddress(recoveredAddress) !== getAddress(expectedMinter)) {\n return { isValid: false, recoveredAddress, reason: \"INVALID_SIGNER\" };\n }\n\n if (\n options?.currentTimeSec !== undefined &&\n message.deadline < BigInt(options.currentTimeSec)\n ) {\n return { isValid: false, recoveredAddress, reason: \"DEADLINE_PASSED\" };\n }\n\n return { isValid: true, recoveredAddress };\n}\n","import type { Address, Hex } from \"viem\";\nimport type { ChainConfig, PoolKey } from \"./types\";\n\n// -------------------------------------------------------------------------\n// EIP-712 type definitions for viem\n// -------------------------------------------------------------------------\n\n/**\n * EIP-712 typed data for the sig-gated mint path.\n *\n * V2 dual-bucket — adds `source` (uint8) so the on-chain digest matches\n * `MintForRequest(address user,address receiver,uint256 amount,uint8 source,uint256 nonce,uint256 deadline)`.\n * Source values per `IPointToken.Source`:\n * 0 = EQUITY (mint backed by issuer's declared capital)\n * 1 = VAULT (mint backed by LP collateral in SettlementVault)\n *\n * SDK helpers `signMintRequest` / `prepareMint` hardcode `source = 0`\n * (EQUITY). VAULT minting is contract-ready but no SDK writer ships it\n * until the LP/admin dashboard lands; sponsor-relayer fails closed on\n * `source != EQUITY` for the same reason.\n *\n * Contract enforces:\n * - msg.sender == receiver (the on-chain caller of `mint`)\n * - nonce == mintRequestNonces[user] (per-user nonce stream)\n *\n * `user` is the off-chain spender (whose nonce advances), `receiver` is\n * the on-chain mint recipient. For direct mints `user == receiver` (user\n * calls PointToken.mint themselves). For wrapper-mediated mints `user ==\n * end-user`, `receiver == wrapper address`.\n */\nexport const mintRequestTypes = {\n MintForRequest: [\n { name: \"user\", type: \"address\" },\n { name: \"receiver\", type: \"address\" },\n { name: \"amount\", type: \"uint256\" },\n { name: \"source\", type: \"uint8\" },\n { name: \"nonce\", type: \"uint256\" },\n { name: \"deadline\", type: \"uint256\" },\n ],\n} as const;\n\n/**\n * EIP-712 typed data for the sig-gated burn path. V2 adds `source`\n * (uint8) to match the contract's 6-field digest:\n * `BurnRequest(address from,uint256 amount,uint8 source,uint256 nonce,uint256 deadline)`\n *\n * Same EQUITY-only policy as `mintRequestTypes`.\n */\nexport const burnRequestTypes = {\n BurnRequest: [\n { name: \"from\", type: \"address\" },\n { name: \"amount\", type: \"uint256\" },\n { name: \"source\", type: \"uint8\" },\n { name: \"nonce\", type: \"uint256\" },\n { name: \"deadline\", type: \"uint256\" },\n ],\n} as const;\n\n// Sponsored mints are implemented at the relayer layer (sponsor-relayer\n// pays gas for the sig-gated `MintForRequest`); the deployed PointToken\n// contract has no separate `ReceiverConsent` path, no extra EIP-712 type,\n// and no separate on-chain nonce mapping.\n\n// -------------------------------------------------------------------------\n// Chain-indexed constants — add entries here as new chains are supported\n// -------------------------------------------------------------------------\n\nexport const SUPPORTED_CHAINS: Record<number, ChainConfig> = {\n 8453: { name: \"Base\" },\n};\n\n/**\n * Uniswap V3 QuoterV2 — used by `findBestQuote` / `quoteExactInput` etc.\n * QuoterV2 (vs V1) reverts cleanly inside multicall and returns a 4-tuple\n * `(amountOut, sqrtPriceX96AfterList, initializedTicksCrossedList, gasEstimate)`.\n */\nexport const QUOTER_V2_ADDRESSES: Record<number, Address> = {\n 8453: \"0xa0765363D9EA1347Afcff6Ae21D6D7B9d36490D0\",\n};\n\n/**\n * Uniswap UniversalRouter — used for AA/batched swaps via PT delegated\n * accounts. Speaks V3 commands (`V3_SWAP_EXACT_IN = 0x00`,\n * `V3_SWAP_EXACT_OUT = 0x01`).\n */\nexport const UNIVERSAL_ROUTER_ADDRESSES: Record<number, Address> = {\n 8453: \"0x008887C992A5bDC24097E717Bfb71CE89483c5A2\",\n};\n\n/**\n * Uniswap V3 SwapRouter — used by `swapDirect`-style flows that bypass\n * the UniversalRouter. Takes `exactInput` / `exactOutput` with packed-bytes\n * paths directly.\n */\nexport const V3_SWAP_ROUTER_ADDRESSES: Record<number, Address> = {\n 8453: \"0xca937aC69708b00B72cc3247440211d8DbDAaFF8\",\n};\n\n/**\n * PAFI's Uniswap V3 factory — used together with `V3_POOL_INIT_CODE_HASH`\n * to derive pool addresses deterministically (`computeV3PoolAddress`).\n */\nexport const V3_FACTORY_ADDRESSES: Record<number, Address> = {\n 8453: \"0x154bAFC6C311f3909080f28438294Cd5184c2924\",\n};\n\n/**\n * Pool-init code hash for PAFI's V3 deployment. Combined with the factory\n * address + sorted tokens + fee, deterministically yields a pool's\n * address via the standard Uniswap V3 CREATE2 derivation.\n */\nexport const V3_POOL_INIT_CODE_HASH: Hex =\n \"0x15380e4800aaa7f219c07d4e07d86cc0e3df225c6a518a1f14f8565f6d9c57c3\";\n\nexport const COMMON_TOKENS: Record<number, Record<string, Address>> = {\n // Base\n 8453: {\n WETH: \"0x4200000000000000000000000000000000000006\",\n USDC: \"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913\",\n USDT: \"0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2\",\n },\n};\n\nexport const COMMON_POOLS: Record<number, PoolKey[]> = {\n // Base — Uniswap V3 pools (PAFI deployment)\n 8453: [\n // WETH/USDC 0.3%\n {\n token0: \"0x4200000000000000000000000000000000000006\",\n token1: \"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913\",\n fee: 3000,\n },\n // WETH/USDC 0.05%\n {\n token0: \"0x4200000000000000000000000000000000000006\",\n token1: \"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913\",\n fee: 500,\n },\n ],\n};\n\nexport const POINT_TOKEN_POOLS: Record<number, Record<Address, PoolKey[]>> = {\n // chainId → pointTokenAddress → PoolKey[]\n};\n\n// -------------------------------------------------------------------------\n// Protocol constants — chain-agnostic (same address on every EVM chain)\n// -------------------------------------------------------------------------\n\n/** ERC-4337 v0.7 EntryPoint — deployed deterministically across all EVM chains. */\nexport const ENTRY_POINT_V07: Address = \"0x0000000071727De22E5E9d8BAf0edAc6f37da032\";\n\n/**\n * ERC-4337 v0.8 EntryPoint — used by Pimlico's `Simple7702Account` impl\n * (`0xe6Cae83BdE06E4c305530e199D7217f42808555B`) and by permissionless's\n * `to7702SimpleSmartAccount`. EIP-7702 delegated EOAs in PAFI's flow\n * point at this EntryPoint, NOT v0.7.\n *\n * Why this matters: account.validateUserOp does\n * `require(msg.sender == entryPoint(), \"account: not from EntryPoint\")`.\n * If the bundler/paymaster sim runs against a different EntryPoint than\n * what the account's `entryPoint()` returns, the require fails and you\n * see `AA23 reverted account: not from EntryPoint`.\n */\nexport const ENTRY_POINT_V08: Address = \"0x4337084d9e255ff0702461cf8895ce9e3b5ff108\";\n\n/** Permit2 — Uniswap's universal approval contract, same address on all EVM chains. */\n// export const PERMIT2_ADDRESS: Address = \"0x000000000022D473030F116dDEE9F6B43aC78BA3\";\n/**\n * Permit2 — PAFI's deployed instance. PAFI ships its own forked deployment.\n */\nexport const PERMIT2_ADDRESS: Address = \"0xEB450d21ae68D3303Cf5775A54Cc84EE7c3fC8eC\";","/**\n * Shared input validator for `verifyMintRequest` /\n * `verifyBurnRequest`'s `options.currentTimeSec`. Centralised so both\n * helpers fail in the same way on bad input — a quiet skip on\n * `NaN` / negative values would defeat the purpose of opting into\n * deadline checking.\n *\n * No-op when `currentTimeSec` is undefined: the caller did not opt in\n * to deadline checking.\n */\nexport function assertValidCurrentTimeSec(\n currentTimeSec: number | undefined,\n): void {\n if (currentTimeSec === undefined) return;\n if (!Number.isFinite(currentTimeSec) || currentTimeSec < 0) {\n throw new Error(\n `verifyDeadline: currentTimeSec must be a finite non-negative number (got ${String(\n currentTimeSec,\n )})`,\n );\n }\n}\n","import { getAddress, parseSignature, recoverTypedDataAddress } from \"viem\";\nimport type { Address, Hex, WalletClient } from \"viem\";\nimport { burnRequestTypes } from \"../constants\";\nimport type {\n BurnRequest,\n EIP712Signature,\n PointTokenDomainConfig,\n SignatureVerification,\n SignatureVerifyOptions,\n} from \"../types\";\nimport { buildDomain } from \"./domain\";\nimport { assertValidCurrentTimeSec } from \"./verifyDeadline\";\n\n/**\n * EIP-712 helpers for `BurnRequest` — consumed by the sig-gated burn\n * path on `PointToken`:\n *\n * burn(address from, uint256 amount, uint256 deadline, bytes burnerSig)\n *\n * Solidity type hash:\n * BurnRequest(address from,uint256 amount,uint256 nonce,uint256 deadline)\n *\n * Issuer backend signs with its burner signer (HSM/KMS). On-chain\n * `msg.sender` must equal `from`, and the recovered signer must be in\n * `burners[]`. Nonce comes from `burnRequestNonces[from]` and is\n * auto-incremented on success.\n */\nexport function buildBurnRequestTypedData(\n domain: PointTokenDomainConfig,\n message: BurnRequest,\n) {\n return {\n domain: buildDomain(domain),\n types: burnRequestTypes,\n primaryType: \"BurnRequest\" as const,\n message,\n };\n}\n\nexport async function signBurnRequest(\n walletClient: WalletClient,\n domain: PointTokenDomainConfig,\n message: BurnRequest,\n): Promise<EIP712Signature> {\n const serialized = await walletClient.signTypedData({\n account: walletClient.account!,\n domain: buildDomain(domain),\n types: burnRequestTypes,\n primaryType: \"BurnRequest\",\n message,\n });\n\n const { v, r, s } = parseSignature(serialized);\n\n return {\n v: Number(v),\n r,\n s,\n serialized,\n };\n}\n\n/**\n * Verify a BurnRequest signature. Symmetric with `verifyMintRequest`:\n * always recovers the signer; opt-in deadline check via\n * `options.currentTimeSec`. See that helper for the full contract.\n */\nexport async function verifyBurnRequest(\n domain: PointTokenDomainConfig,\n message: BurnRequest,\n signature: Hex,\n expectedBurner: Address,\n options?: SignatureVerifyOptions,\n): Promise<SignatureVerification> {\n assertValidCurrentTimeSec(options?.currentTimeSec);\n\n const recoveredAddress = await recoverTypedDataAddress({\n domain: buildDomain(domain),\n types: burnRequestTypes,\n primaryType: \"BurnRequest\",\n message,\n signature,\n });\n\n if (getAddress(recoveredAddress) !== getAddress(expectedBurner)) {\n return { isValid: false, recoveredAddress, reason: \"INVALID_SIGNER\" };\n }\n\n if (\n options?.currentTimeSec !== undefined &&\n message.deadline < BigInt(options.currentTimeSec)\n ) {\n return { isValid: false, recoveredAddress, reason: \"DEADLINE_PASSED\" };\n }\n\n return { isValid: true, recoveredAddress };\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/phitran/Pacific-Finance/pafi-backend/pafi-sdk/packages/core/dist/chunk-6DSK5VR2.cjs","../src/auth/loginMessage.ts","../src/auth/sponsorAuth.ts"],"names":[],"mappings":"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,IAAA;AACR,MAAA;AAEF,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAEgB;AACP,EAAA;AACC,IAAA;AACG,IAAA;AACT,IAAA;AACA,IAAA;AACF,EAAA;AACF;AAES;AACA,EAAA;AACI,IAAA;AACD,IAAA;AACR,IAAA;AACO,IAAA;AACP,IAAA;AACU,IAAA;AACA,IAAA;AACZ,EAAA;AACF;AAEgB;AACP,EAAA;AACG,IAAA;AACD,IAAA;AACP,IAAA;AACS,IAAA;AACX,EAAA;AACF;AAEgB;AACP,EAAA;AACT;AAEA;AAIU,EAAA;AAED,EAAA;AACI,IAAA;AACT,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACD,EAAA;AACH;AA4DA;AAGQ,EAAA;AAGA,EAAA;AAEA,EAAA;AACK,IAAA;AACD,IAAA;AACR,IAAA;AACA,IAAA;AACA,IAAA;AACU,IAAA;AACA,IAAA;AACZ,EAAA;AAEY,EAAA;AAEL,EAAA;AACL,IAAA;AACS,IAAA;AACD,IAAA;AACR,IAAA;AACO,IAAA;AACP,IAAA;AACU,IAAA;AACA,IAAA;AACZ,EAAA;AACF;AAEA;AAKQ,EAAA;AACM,EAAA;AACD,IAAA;AACX,EAAA;AAQK,EAAA;AACM,IAAA;AACX,EAAA;AAEI,EAAA;AACA,EAAA;AACM,IAAA;AAER,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACD,IAAA;AACK,EAAA;AACG,IAAA;AACX,EAAA;AAEI,EAAA;AACO,IAAA;AACX,EAAA;AAES,EAAA;AACX;AFEc;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/chunk-6DSK5VR2.cjs","sourcesContent":[null,"import { getAddress, verifyMessage, recoverMessageAddress } from \"viem\";\nimport type { Address, Hex } from \"viem\";\nimport type { LoginMessageParams, VerifyLoginResult } from \"./types\";\n\nconst DEFAULT_VERSION = \"1\";\nconst DEFAULT_STATEMENT = \"Sign in with Ethereum to PAFI.\";\n\n/**\n * Build an EIP-4361 login message string.\n *\n * The output is a deterministic plain-text message that the wallet signs via\n * `personal_sign`. The same message can be parsed back with\n * {@link parseLoginMessage} and verified with {@link verifyLoginMessage}.\n */\nexport function createLoginMessage(params: LoginMessageParams): string {\n const {\n domain,\n address,\n chainId,\n nonce,\n uri,\n statement = DEFAULT_STATEMENT,\n version = DEFAULT_VERSION,\n issuedAt = new Date(),\n expirationTime,\n notBefore,\n requestId,\n } = params;\n\n if (!domain) throw new Error(\"createLoginMessage: domain required\");\n if (!nonce) throw new Error(\"createLoginMessage: nonce required\");\n if (!uri) throw new Error(\"createLoginMessage: uri required\");\n\n const checksummed = getAddress(address);\n\n const lines: string[] = [];\n lines.push(`${domain} wants you to sign in with your Ethereum account:`);\n lines.push(checksummed);\n lines.push(\"\");\n if (statement) {\n lines.push(statement);\n lines.push(\"\");\n }\n lines.push(`URI: ${uri}`);\n lines.push(`Version: ${version}`);\n lines.push(`Chain ID: ${chainId}`);\n lines.push(`Nonce: ${nonce}`);\n lines.push(`Issued At: ${issuedAt.toISOString()}`);\n if (expirationTime) {\n lines.push(`Expiration Time: ${expirationTime.toISOString()}`);\n }\n if (notBefore) {\n lines.push(`Not Before: ${notBefore.toISOString()}`);\n }\n if (requestId) {\n lines.push(`Request ID: ${requestId}`);\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Parse a login message string built by {@link createLoginMessage} back into\n * its structured fields. Throws if the message does not match the expected\n * EIP-4361 layout.\n */\nexport function parseLoginMessage(message: string): LoginMessageParams {\n const lines = message.split(\"\\n\");\n if (lines.length < 7) {\n throw new Error(\"parseLoginMessage: message too short\");\n }\n\n const headerLine = lines[0] ?? \"\";\n const headerMatch = headerLine.match(\n /^(?<domain>.+) wants you to sign in with your Ethereum account:$/,\n );\n if (!headerMatch || !headerMatch.groups) {\n throw new Error(\"parseLoginMessage: invalid header line\");\n }\n const domain = headerMatch.groups[\"domain\"]!;\n\n const addressLine = lines[1] ?? \"\";\n if (!/^0x[0-9a-fA-F]{40}$/.test(addressLine)) {\n throw new Error(\"parseLoginMessage: invalid address line\");\n }\n const address = getAddress(addressLine);\n\n // After address: blank line, optional statement + blank line, then key:value lines.\n let cursor = 2;\n if (lines[cursor] !== \"\") {\n throw new Error(\"parseLoginMessage: missing blank line after address\");\n }\n cursor++;\n\n let statement: string | undefined;\n // Statement is present if the next line is not a known key.\n if (cursor < lines.length && !looksLikeKeyValue(lines[cursor]!)) {\n statement = lines[cursor];\n cursor++;\n if (lines[cursor] !== \"\") {\n throw new Error(\"parseLoginMessage: missing blank line after statement\");\n }\n cursor++;\n }\n\n const fields = new Map<string, string>();\n for (; cursor < lines.length; cursor++) {\n const line = lines[cursor]!;\n if (line === \"\") continue;\n const idx = line.indexOf(\": \");\n if (idx === -1) {\n throw new Error(`parseLoginMessage: malformed field line: \"${line}\"`);\n }\n const key = line.slice(0, idx);\n const value = line.slice(idx + 2);\n fields.set(key, value);\n }\n\n const uri = requireField(fields, \"URI\");\n const version = requireField(fields, \"Version\");\n const chainId = Number(requireField(fields, \"Chain ID\"));\n if (!Number.isInteger(chainId)) {\n throw new Error(\"parseLoginMessage: Chain ID is not an integer\");\n }\n const nonce = requireField(fields, \"Nonce\");\n const issuedAt = new Date(requireField(fields, \"Issued At\"));\n const expirationTime = optionalDate(fields, \"Expiration Time\");\n const notBefore = optionalDate(fields, \"Not Before\");\n const requestId = fields.get(\"Request ID\");\n\n const result: LoginMessageParams = {\n domain,\n address,\n chainId,\n nonce,\n uri,\n version,\n issuedAt,\n };\n if (statement !== undefined) result.statement = statement;\n if (expirationTime !== undefined) result.expirationTime = expirationTime;\n if (notBefore !== undefined) result.notBefore = notBefore;\n if (requestId !== undefined) result.requestId = requestId;\n return result;\n}\n\n/**\n * Verify that a login message was signed by the address embedded in the\n * message. Returns `{ valid, address }` where `address` is the recovered\n * signer (checksummed). Does NOT check expiration / not-before / nonce\n * consumption — those are the AuthService's responsibility.\n */\nexport async function verifyLoginMessage(\n message: string,\n signature: Hex,\n): Promise<VerifyLoginResult> {\n const parsed = parseLoginMessage(message);\n const valid = await verifyMessage({\n address: parsed.address,\n message,\n signature,\n });\n if (valid) {\n return { valid: true, address: parsed.address };\n }\n // Recover anyway so callers can log the mismatch.\n const recovered = await recoverMessageAddress({ message, signature });\n return { valid: false, address: getAddress(recovered) as Address };\n}\n\n// -------------------------------------------------------------------------\n// helpers\n// -------------------------------------------------------------------------\n\nconst KNOWN_KEYS = new Set([\n \"URI\",\n \"Version\",\n \"Chain ID\",\n \"Nonce\",\n \"Issued At\",\n \"Expiration Time\",\n \"Not Before\",\n \"Request ID\",\n]);\n\nfunction looksLikeKeyValue(line: string): boolean {\n const idx = line.indexOf(\": \");\n if (idx === -1) return false;\n return KNOWN_KEYS.has(line.slice(0, idx));\n}\n\nfunction requireField(fields: Map<string, string>, key: string): string {\n const value = fields.get(key);\n if (value === undefined) {\n throw new Error(`parseLoginMessage: missing required field \"${key}\"`);\n }\n return value;\n}\n\nfunction optionalDate(\n fields: Map<string, string>,\n key: string,\n): Date | undefined {\n const raw = fields.get(key);\n if (raw === undefined) return undefined;\n return new Date(raw);\n}\n","import { keccak256, recoverTypedDataAddress } from \"viem\";\nimport type { Address, Hex, WalletClient } from \"viem\";\n\n/**\n * Generate a high-entropy nonce for SponsorAuth. Uses 64 bits of CSPRNG\n * (`crypto.randomBytes` Node, `crypto.getRandomValues` browser) in the low\n * half plus a millisecond timestamp prefix in the high half — keeps\n * sponsor-relayer's single-use nonce gate clean at scale.\n */\nexport function generateSponsorAuthNonce(): bigint {\n // Per-spec timestamp prefix (high 64 bits, ms precision) so server-side\n // freshness checks can derive an approx age from the nonce alone.\n const tsMs = BigInt(Date.now());\n\n // 64 bits of CSPRNG entropy in the low half. Try Web Crypto first (browser\n // + modern Node), fall back to node:crypto.\n let randHigh: number;\n let randLow: number;\n const g = globalThis as unknown as {\n crypto?: { getRandomValues?: (a: Uint32Array) => Uint32Array };\n };\n if (g.crypto?.getRandomValues) {\n const buf = new Uint32Array(2);\n g.crypto.getRandomValues(buf);\n randHigh = buf[0]!;\n randLow = buf[1]!;\n } else {\n // Node fallback — require lazily so browser bundlers can tree-shake.\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const nodeCrypto = require(\"node:crypto\") as {\n randomBytes: (n: number) => Buffer;\n };\n const b = nodeCrypto.randomBytes(8);\n randHigh = b.readUInt32BE(0);\n randLow = b.readUInt32BE(4);\n }\n\n const rand =\n (BigInt(randHigh) << 32n) | BigInt(randLow);\n return (tsMs << 64n) | rand;\n}\n\n/**\n * Validate hex-encoded signature shape: starts with `0x`, even length\n * AFTER the prefix, and 65 bytes (`0x` + 130 hex) minimum. Allows\n * larger sizes for EIP-1271 contract signatures.\n */\nfunction isValidHexSignature(sig: string): boolean {\n if (!sig.startsWith(\"0x\")) return false;\n const hex = sig.slice(2);\n // Must be even length (each byte = 2 hex chars).\n if (hex.length % 2 !== 0) return false;\n // ECDSA = 65 bytes = 130 hex chars. EIP-1271 may be longer; reject\n // shorter lengths.\n if (hex.length < 130) return false;\n // Hex regex (0–9, a–f, A–F).\n return /^[0-9a-fA-F]+$/.test(hex);\n}\n\nexport const SPONSOR_AUTH_DOMAIN_NAME = \"PafiSponsorAuth\";\n\nexport const SPONSOR_AUTH_TYPES = {\n SponsorAuth: [\n { name: \"chainId\", type: \"uint256\" },\n { name: \"sender\", type: \"address\" },\n { name: \"callDataHash\", type: \"bytes32\" },\n { name: \"nonce\", type: \"uint256\" },\n { name: \"expiresAt\", type: \"uint256\" },\n { name: \"scenario\", type: \"string\" },\n { name: \"issuerId\", type: \"string\" },\n ],\n} as const;\n\nexport interface SponsorAuthPayload {\n chainId: number;\n sender: Address;\n callDataHash: Hex;\n nonce: bigint;\n expiresAt: number;\n scenario: string;\n issuerId: string;\n}\n\nexport interface SponsorAuthVerifyResult {\n ok: boolean;\n recoveredAddress?: Address;\n reason?: \"EXPIRED\" | \"INVALID_SIGNER\" | \"INVALID_SIGNATURE_FORMAT\";\n}\n\n/**\n * Domain anchor for SponsorAuth EIP-712 signatures. The relayer service\n * is off-chain (no on-chain SponsorRelayer contract), so we bind to a\n * stable PAFI-controlled marker registered as `SPONSOR_AUTH_DOMAIN_ANCHOR`\n * in the relayer config. This rules out cross-system replay (a sibling\n * EIP-712 payload with the same field names cannot validate against\n * this domain) and gives wallets a meaningful \"verifying contract\"\n * preview instead of `0x0`.\n *\n * If you ever deploy an on-chain `SponsorRelayer` contract, switch this\n * to its address.\n */\n// Bytes 1-4 = \"PAFI\" (0x50414649); bytes 5-12 = \"SPONSOR_\" (0x53504F4E534F525F);\n// bytes 13-19 = padding; byte 20 = version tag (0x01). Total = 20 bytes.\n// Stable, system-specific marker. To migrate to a real EOA / on-chain\n// SponsorRelayer contract, replace this constant and bump `version` to \"2\".\nexport const SPONSOR_AUTH_DOMAIN_ANCHOR_BASE_MAINNET: Address =\n \"0x5041464953504F4E534F525F0000000000000001\" as Address;\n\nconst SPONSOR_AUTH_DOMAIN_ANCHORS: Record<number, Address> = {\n // Base mainnet — uses the literal-tag anchor above. To migrate to a\n // real EOA / contract, replace this entry and bump `version` to \"2\"\n // so existing sigs become invalid.\n 8453: SPONSOR_AUTH_DOMAIN_ANCHOR_BASE_MAINNET,\n};\n\nexport function getSponsorAuthDomainAnchor(chainId: number): Address {\n const anchor = SPONSOR_AUTH_DOMAIN_ANCHORS[chainId];\n if (!anchor) {\n throw new Error(\n `buildSponsorAuthDomain: no SponsorAuth domain anchor configured for chainId ${chainId}. ` +\n `Add an entry to SPONSOR_AUTH_DOMAIN_ANCHORS in @pafi-dev/core/auth/sponsorAuth.ts.`,\n );\n }\n return anchor;\n}\n\nexport function buildSponsorAuthDomain(chainId: number) {\n return {\n name: SPONSOR_AUTH_DOMAIN_NAME,\n version: \"1\",\n chainId,\n verifyingContract: getSponsorAuthDomainAnchor(chainId),\n };\n}\n\nfunction buildMessage(payload: SponsorAuthPayload) {\n return {\n chainId: BigInt(payload.chainId),\n sender: payload.sender,\n callDataHash: payload.callDataHash,\n nonce: payload.nonce,\n expiresAt: BigInt(payload.expiresAt),\n scenario: payload.scenario,\n issuerId: payload.issuerId,\n };\n}\n\nexport function buildSponsorAuthTypedData(payload: SponsorAuthPayload) {\n return {\n domain: buildSponsorAuthDomain(payload.chainId),\n types: SPONSOR_AUTH_TYPES,\n primaryType: \"SponsorAuth\" as const,\n message: buildMessage(payload),\n };\n}\n\nexport function computeCallDataHash(callData: Hex): Hex {\n return keccak256(callData);\n}\n\nexport async function signSponsorAuth(\n wallet: WalletClient,\n payload: SponsorAuthPayload,\n): Promise<Hex> {\n const { domain, types, primaryType, message } =\n buildSponsorAuthTypedData(payload);\n return wallet.signTypedData({\n account: wallet.account!,\n domain,\n types,\n primaryType,\n message,\n });\n}\n\nexport interface BuiltSponsorAuth {\n sig: Hex;\n chainId: number;\n sender: Address;\n callDataHash: Hex;\n /** Decimal-string for JSON transport (bigint not safely serializable). */\n nonce: string;\n expiresAt: number;\n scenario: string;\n issuerId: string;\n}\n\nexport interface BuildSponsorAuthParams {\n /** User EOA the sponsorship is for. */\n userAddress: Address;\n /** UserOp `callData` to bind the auth to (hashed via keccak256). */\n callData: Hex;\n /** Chain id the UserOp will execute on. */\n chainId: number;\n /** Scenario tag for logs / rate-limiter (`mint` / `burn` / `swap` / etc.). */\n scenario: string;\n /** Issuer id (matches `pafi_issuers` row in PAFI's sponsor-relayer). */\n issuerId: string;\n /** Issuer signer wallet (HSM/KMS-backed in production). */\n issuerSignerWallet: WalletClient;\n /** Validity window in seconds. Default 600 (10 min). */\n expiresInSeconds?: number;\n /**\n * Optional explicit nonce. When omitted, defaults to a unique\n * `Date.now() * 1e6 + random` — matches the gg56 reference impl.\n * Override for test fixtures or when chaining replay-protection.\n */\n nonce?: bigint;\n}\n\n/**\n * Build, sign, and serialize a `SponsorAuth` payload in one call.\n * Replaces the ~30 LoC private `buildSponsorAuth` helper that every\n * issuer would otherwise reimplement on each sponsored endpoint.\n *\n * Output is JSON-safe (`nonce` as decimal string) and matches the\n * shape sponsor-relayer's `/paymaster/sponsor` accepts as the\n * `sponsorAuth` field.\n *\n * Issuer typically calls this from each sponsored controller route:\n *\n * ```ts\n * const sponsorAuth = await buildAndSignSponsorAuth({\n * userAddress: user.userAddress,\n * callData: userOp.callData,\n * chainId,\n * scenario: 'mint',\n * issuerId: this.config.get('PAFI_ISSUER_ID'),\n * issuerSignerWallet: this.issuerSignerWallet,\n * });\n * return { ...response, sponsorAuth };\n * ```\n */\nexport async function buildAndSignSponsorAuth(\n params: BuildSponsorAuthParams,\n): Promise<BuiltSponsorAuth> {\n const expiresAt =\n Math.floor(Date.now() / 1000) + (params.expiresInSeconds ?? 600);\n\n const nonce = params.nonce ?? generateSponsorAuthNonce();\n\n const payload: SponsorAuthPayload = {\n chainId: params.chainId,\n sender: params.userAddress,\n callDataHash: computeCallDataHash(params.callData),\n nonce,\n expiresAt,\n scenario: params.scenario,\n issuerId: params.issuerId,\n };\n\n const sig = await signSponsorAuth(params.issuerSignerWallet, payload);\n\n return {\n sig,\n chainId: payload.chainId,\n sender: payload.sender,\n callDataHash: payload.callDataHash,\n nonce: payload.nonce.toString(),\n expiresAt: payload.expiresAt,\n scenario: payload.scenario,\n issuerId: payload.issuerId,\n };\n}\n\nexport async function verifySponsorAuth(\n payload: SponsorAuthPayload,\n signature: Hex,\n expectedSigner: Address,\n): Promise<SponsorAuthVerifyResult> {\n const nowSec = Math.floor(Date.now() / 1000);\n if (payload.expiresAt < nowSec) {\n return { ok: false, reason: \"EXPIRED\" };\n }\n\n // v0.7.1 — strict hex format check before deferring to viem. Previous\n // length-only `< 132` accepted arbitrary 132+ char strings even when\n // not valid hex; recoverTypedDataAddress would throw and the catch\n // collapsed both cases to INVALID_SIGNATURE_FORMAT. Verify hex shape\n // up front + accept ECDSA (65 byte = 132 hex char) AND EIP-1271\n // (variable-length, must still be valid hex).\n if (!isValidHexSignature(signature)) {\n return { ok: false, reason: \"INVALID_SIGNATURE_FORMAT\" };\n }\n\n let recovered: Address;\n try {\n const { domain, types, primaryType, message } =\n buildSponsorAuthTypedData(payload);\n recovered = await recoverTypedDataAddress({\n domain,\n types,\n primaryType,\n message,\n signature,\n });\n } catch {\n return { ok: false, reason: \"INVALID_SIGNATURE_FORMAT\" };\n }\n\n if (recovered.toLowerCase() !== expectedSigner.toLowerCase()) {\n return { ok: false, reason: \"INVALID_SIGNER\", recoveredAddress: recovered };\n }\n\n return { ok: true, recoveredAddress: recovered };\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/auth/loginMessage.ts","../src/auth/sponsorAuth.ts"],"sourcesContent":["import { getAddress, verifyMessage, recoverMessageAddress } from \"viem\";\nimport type { Address, Hex } from \"viem\";\nimport type { LoginMessageParams, VerifyLoginResult } from \"./types\";\n\nconst DEFAULT_VERSION = \"1\";\nconst DEFAULT_STATEMENT = \"Sign in with Ethereum to PAFI.\";\n\n/**\n * Build an EIP-4361 login message string.\n *\n * The output is a deterministic plain-text message that the wallet signs via\n * `personal_sign`. The same message can be parsed back with\n * {@link parseLoginMessage} and verified with {@link verifyLoginMessage}.\n */\nexport function createLoginMessage(params: LoginMessageParams): string {\n const {\n domain,\n address,\n chainId,\n nonce,\n uri,\n statement = DEFAULT_STATEMENT,\n version = DEFAULT_VERSION,\n issuedAt = new Date(),\n expirationTime,\n notBefore,\n requestId,\n } = params;\n\n if (!domain) throw new Error(\"createLoginMessage: domain required\");\n if (!nonce) throw new Error(\"createLoginMessage: nonce required\");\n if (!uri) throw new Error(\"createLoginMessage: uri required\");\n\n const checksummed = getAddress(address);\n\n const lines: string[] = [];\n lines.push(`${domain} wants you to sign in with your Ethereum account:`);\n lines.push(checksummed);\n lines.push(\"\");\n if (statement) {\n lines.push(statement);\n lines.push(\"\");\n }\n lines.push(`URI: ${uri}`);\n lines.push(`Version: ${version}`);\n lines.push(`Chain ID: ${chainId}`);\n lines.push(`Nonce: ${nonce}`);\n lines.push(`Issued At: ${issuedAt.toISOString()}`);\n if (expirationTime) {\n lines.push(`Expiration Time: ${expirationTime.toISOString()}`);\n }\n if (notBefore) {\n lines.push(`Not Before: ${notBefore.toISOString()}`);\n }\n if (requestId) {\n lines.push(`Request ID: ${requestId}`);\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Parse a login message string built by {@link createLoginMessage} back into\n * its structured fields. Throws if the message does not match the expected\n * EIP-4361 layout.\n */\nexport function parseLoginMessage(message: string): LoginMessageParams {\n const lines = message.split(\"\\n\");\n if (lines.length < 7) {\n throw new Error(\"parseLoginMessage: message too short\");\n }\n\n const headerLine = lines[0] ?? \"\";\n const headerMatch = headerLine.match(\n /^(?<domain>.+) wants you to sign in with your Ethereum account:$/,\n );\n if (!headerMatch || !headerMatch.groups) {\n throw new Error(\"parseLoginMessage: invalid header line\");\n }\n const domain = headerMatch.groups[\"domain\"]!;\n\n const addressLine = lines[1] ?? \"\";\n if (!/^0x[0-9a-fA-F]{40}$/.test(addressLine)) {\n throw new Error(\"parseLoginMessage: invalid address line\");\n }\n const address = getAddress(addressLine);\n\n // After address: blank line, optional statement + blank line, then key:value lines.\n let cursor = 2;\n if (lines[cursor] !== \"\") {\n throw new Error(\"parseLoginMessage: missing blank line after address\");\n }\n cursor++;\n\n let statement: string | undefined;\n // Statement is present if the next line is not a known key.\n if (cursor < lines.length && !looksLikeKeyValue(lines[cursor]!)) {\n statement = lines[cursor];\n cursor++;\n if (lines[cursor] !== \"\") {\n throw new Error(\"parseLoginMessage: missing blank line after statement\");\n }\n cursor++;\n }\n\n const fields = new Map<string, string>();\n for (; cursor < lines.length; cursor++) {\n const line = lines[cursor]!;\n if (line === \"\") continue;\n const idx = line.indexOf(\": \");\n if (idx === -1) {\n throw new Error(`parseLoginMessage: malformed field line: \"${line}\"`);\n }\n const key = line.slice(0, idx);\n const value = line.slice(idx + 2);\n fields.set(key, value);\n }\n\n const uri = requireField(fields, \"URI\");\n const version = requireField(fields, \"Version\");\n const chainId = Number(requireField(fields, \"Chain ID\"));\n if (!Number.isInteger(chainId)) {\n throw new Error(\"parseLoginMessage: Chain ID is not an integer\");\n }\n const nonce = requireField(fields, \"Nonce\");\n const issuedAt = new Date(requireField(fields, \"Issued At\"));\n const expirationTime = optionalDate(fields, \"Expiration Time\");\n const notBefore = optionalDate(fields, \"Not Before\");\n const requestId = fields.get(\"Request ID\");\n\n const result: LoginMessageParams = {\n domain,\n address,\n chainId,\n nonce,\n uri,\n version,\n issuedAt,\n };\n if (statement !== undefined) result.statement = statement;\n if (expirationTime !== undefined) result.expirationTime = expirationTime;\n if (notBefore !== undefined) result.notBefore = notBefore;\n if (requestId !== undefined) result.requestId = requestId;\n return result;\n}\n\n/**\n * Verify that a login message was signed by the address embedded in the\n * message. Returns `{ valid, address }` where `address` is the recovered\n * signer (checksummed). Does NOT check expiration / not-before / nonce\n * consumption — those are the AuthService's responsibility.\n */\nexport async function verifyLoginMessage(\n message: string,\n signature: Hex,\n): Promise<VerifyLoginResult> {\n const parsed = parseLoginMessage(message);\n const valid = await verifyMessage({\n address: parsed.address,\n message,\n signature,\n });\n if (valid) {\n return { valid: true, address: parsed.address };\n }\n // Recover anyway so callers can log the mismatch.\n const recovered = await recoverMessageAddress({ message, signature });\n return { valid: false, address: getAddress(recovered) as Address };\n}\n\n// -------------------------------------------------------------------------\n// helpers\n// -------------------------------------------------------------------------\n\nconst KNOWN_KEYS = new Set([\n \"URI\",\n \"Version\",\n \"Chain ID\",\n \"Nonce\",\n \"Issued At\",\n \"Expiration Time\",\n \"Not Before\",\n \"Request ID\",\n]);\n\nfunction looksLikeKeyValue(line: string): boolean {\n const idx = line.indexOf(\": \");\n if (idx === -1) return false;\n return KNOWN_KEYS.has(line.slice(0, idx));\n}\n\nfunction requireField(fields: Map<string, string>, key: string): string {\n const value = fields.get(key);\n if (value === undefined) {\n throw new Error(`parseLoginMessage: missing required field \"${key}\"`);\n }\n return value;\n}\n\nfunction optionalDate(\n fields: Map<string, string>,\n key: string,\n): Date | undefined {\n const raw = fields.get(key);\n if (raw === undefined) return undefined;\n return new Date(raw);\n}\n","import { keccak256, recoverTypedDataAddress } from \"viem\";\nimport type { Address, Hex, WalletClient } from \"viem\";\n\n/**\n * Generate a high-entropy nonce for SponsorAuth. Uses 64 bits of CSPRNG\n * (`crypto.randomBytes` Node, `crypto.getRandomValues` browser) in the low\n * half plus a millisecond timestamp prefix in the high half — keeps\n * sponsor-relayer's single-use nonce gate clean at scale.\n */\nexport function generateSponsorAuthNonce(): bigint {\n // Per-spec timestamp prefix (high 64 bits, ms precision) so server-side\n // freshness checks can derive an approx age from the nonce alone.\n const tsMs = BigInt(Date.now());\n\n // 64 bits of CSPRNG entropy in the low half. Try Web Crypto first (browser\n // + modern Node), fall back to node:crypto.\n let randHigh: number;\n let randLow: number;\n const g = globalThis as unknown as {\n crypto?: { getRandomValues?: (a: Uint32Array) => Uint32Array };\n };\n if (g.crypto?.getRandomValues) {\n const buf = new Uint32Array(2);\n g.crypto.getRandomValues(buf);\n randHigh = buf[0]!;\n randLow = buf[1]!;\n } else {\n // Node fallback — require lazily so browser bundlers can tree-shake.\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const nodeCrypto = require(\"node:crypto\") as {\n randomBytes: (n: number) => Buffer;\n };\n const b = nodeCrypto.randomBytes(8);\n randHigh = b.readUInt32BE(0);\n randLow = b.readUInt32BE(4);\n }\n\n const rand =\n (BigInt(randHigh) << 32n) | BigInt(randLow);\n return (tsMs << 64n) | rand;\n}\n\n/**\n * Validate hex-encoded signature shape: starts with `0x`, even length\n * AFTER the prefix, and 65 bytes (`0x` + 130 hex) minimum. Allows\n * larger sizes for EIP-1271 contract signatures.\n */\nfunction isValidHexSignature(sig: string): boolean {\n if (!sig.startsWith(\"0x\")) return false;\n const hex = sig.slice(2);\n // Must be even length (each byte = 2 hex chars).\n if (hex.length % 2 !== 0) return false;\n // ECDSA = 65 bytes = 130 hex chars. EIP-1271 may be longer; reject\n // shorter lengths.\n if (hex.length < 130) return false;\n // Hex regex (0–9, a–f, A–F).\n return /^[0-9a-fA-F]+$/.test(hex);\n}\n\nexport const SPONSOR_AUTH_DOMAIN_NAME = \"PafiSponsorAuth\";\n\nexport const SPONSOR_AUTH_TYPES = {\n SponsorAuth: [\n { name: \"chainId\", type: \"uint256\" },\n { name: \"sender\", type: \"address\" },\n { name: \"callDataHash\", type: \"bytes32\" },\n { name: \"nonce\", type: \"uint256\" },\n { name: \"expiresAt\", type: \"uint256\" },\n { name: \"scenario\", type: \"string\" },\n { name: \"issuerId\", type: \"string\" },\n ],\n} as const;\n\nexport interface SponsorAuthPayload {\n chainId: number;\n sender: Address;\n callDataHash: Hex;\n nonce: bigint;\n expiresAt: number;\n scenario: string;\n issuerId: string;\n}\n\nexport interface SponsorAuthVerifyResult {\n ok: boolean;\n recoveredAddress?: Address;\n reason?: \"EXPIRED\" | \"INVALID_SIGNER\" | \"INVALID_SIGNATURE_FORMAT\";\n}\n\n/**\n * Domain anchor for SponsorAuth EIP-712 signatures. The relayer service\n * is off-chain (no on-chain SponsorRelayer contract), so we bind to a\n * stable PAFI-controlled marker registered as `SPONSOR_AUTH_DOMAIN_ANCHOR`\n * in the relayer config. This rules out cross-system replay (a sibling\n * EIP-712 payload with the same field names cannot validate against\n * this domain) and gives wallets a meaningful \"verifying contract\"\n * preview instead of `0x0`.\n *\n * If you ever deploy an on-chain `SponsorRelayer` contract, switch this\n * to its address.\n */\n// Bytes 1-4 = \"PAFI\" (0x50414649); bytes 5-12 = \"SPONSOR_\" (0x53504F4E534F525F);\n// bytes 13-19 = padding; byte 20 = version tag (0x01). Total = 20 bytes.\n// Stable, system-specific marker. To migrate to a real EOA / on-chain\n// SponsorRelayer contract, replace this constant and bump `version` to \"2\".\nexport const SPONSOR_AUTH_DOMAIN_ANCHOR_BASE_MAINNET: Address =\n \"0x5041464953504F4E534F525F0000000000000001\" as Address;\n\nconst SPONSOR_AUTH_DOMAIN_ANCHORS: Record<number, Address> = {\n // Base mainnet — uses the literal-tag anchor above. To migrate to a\n // real EOA / contract, replace this entry and bump `version` to \"2\"\n // so existing sigs become invalid.\n 8453: SPONSOR_AUTH_DOMAIN_ANCHOR_BASE_MAINNET,\n};\n\nexport function getSponsorAuthDomainAnchor(chainId: number): Address {\n const anchor = SPONSOR_AUTH_DOMAIN_ANCHORS[chainId];\n if (!anchor) {\n throw new Error(\n `buildSponsorAuthDomain: no SponsorAuth domain anchor configured for chainId ${chainId}. ` +\n `Add an entry to SPONSOR_AUTH_DOMAIN_ANCHORS in @pafi-dev/core/auth/sponsorAuth.ts.`,\n );\n }\n return anchor;\n}\n\nexport function buildSponsorAuthDomain(chainId: number) {\n return {\n name: SPONSOR_AUTH_DOMAIN_NAME,\n version: \"1\",\n chainId,\n verifyingContract: getSponsorAuthDomainAnchor(chainId),\n };\n}\n\nfunction buildMessage(payload: SponsorAuthPayload) {\n return {\n chainId: BigInt(payload.chainId),\n sender: payload.sender,\n callDataHash: payload.callDataHash,\n nonce: payload.nonce,\n expiresAt: BigInt(payload.expiresAt),\n scenario: payload.scenario,\n issuerId: payload.issuerId,\n };\n}\n\nexport function buildSponsorAuthTypedData(payload: SponsorAuthPayload) {\n return {\n domain: buildSponsorAuthDomain(payload.chainId),\n types: SPONSOR_AUTH_TYPES,\n primaryType: \"SponsorAuth\" as const,\n message: buildMessage(payload),\n };\n}\n\nexport function computeCallDataHash(callData: Hex): Hex {\n return keccak256(callData);\n}\n\nexport async function signSponsorAuth(\n wallet: WalletClient,\n payload: SponsorAuthPayload,\n): Promise<Hex> {\n const { domain, types, primaryType, message } =\n buildSponsorAuthTypedData(payload);\n return wallet.signTypedData({\n account: wallet.account!,\n domain,\n types,\n primaryType,\n message,\n });\n}\n\nexport interface BuiltSponsorAuth {\n sig: Hex;\n chainId: number;\n sender: Address;\n callDataHash: Hex;\n /** Decimal-string for JSON transport (bigint not safely serializable). */\n nonce: string;\n expiresAt: number;\n scenario: string;\n issuerId: string;\n}\n\nexport interface BuildSponsorAuthParams {\n /** User EOA the sponsorship is for. */\n userAddress: Address;\n /** UserOp `callData` to bind the auth to (hashed via keccak256). */\n callData: Hex;\n /** Chain id the UserOp will execute on. */\n chainId: number;\n /** Scenario tag for logs / rate-limiter (`mint` / `burn` / `swap` / etc.). */\n scenario: string;\n /** Issuer id (matches `pafi_issuers` row in PAFI's sponsor-relayer). */\n issuerId: string;\n /** Issuer signer wallet (HSM/KMS-backed in production). */\n issuerSignerWallet: WalletClient;\n /** Validity window in seconds. Default 600 (10 min). */\n expiresInSeconds?: number;\n /**\n * Optional explicit nonce. When omitted, defaults to a unique\n * `Date.now() * 1e6 + random` — matches the gg56 reference impl.\n * Override for test fixtures or when chaining replay-protection.\n */\n nonce?: bigint;\n}\n\n/**\n * Build, sign, and serialize a `SponsorAuth` payload in one call.\n * Replaces the ~30 LoC private `buildSponsorAuth` helper that every\n * issuer would otherwise reimplement on each sponsored endpoint.\n *\n * Output is JSON-safe (`nonce` as decimal string) and matches the\n * shape sponsor-relayer's `/paymaster/sponsor` accepts as the\n * `sponsorAuth` field.\n *\n * Issuer typically calls this from each sponsored controller route:\n *\n * ```ts\n * const sponsorAuth = await buildAndSignSponsorAuth({\n * userAddress: user.userAddress,\n * callData: userOp.callData,\n * chainId,\n * scenario: 'mint',\n * issuerId: this.config.get('PAFI_ISSUER_ID'),\n * issuerSignerWallet: this.issuerSignerWallet,\n * });\n * return { ...response, sponsorAuth };\n * ```\n */\nexport async function buildAndSignSponsorAuth(\n params: BuildSponsorAuthParams,\n): Promise<BuiltSponsorAuth> {\n const expiresAt =\n Math.floor(Date.now() / 1000) + (params.expiresInSeconds ?? 600);\n\n const nonce = params.nonce ?? generateSponsorAuthNonce();\n\n const payload: SponsorAuthPayload = {\n chainId: params.chainId,\n sender: params.userAddress,\n callDataHash: computeCallDataHash(params.callData),\n nonce,\n expiresAt,\n scenario: params.scenario,\n issuerId: params.issuerId,\n };\n\n const sig = await signSponsorAuth(params.issuerSignerWallet, payload);\n\n return {\n sig,\n chainId: payload.chainId,\n sender: payload.sender,\n callDataHash: payload.callDataHash,\n nonce: payload.nonce.toString(),\n expiresAt: payload.expiresAt,\n scenario: payload.scenario,\n issuerId: payload.issuerId,\n };\n}\n\nexport async function verifySponsorAuth(\n payload: SponsorAuthPayload,\n signature: Hex,\n expectedSigner: Address,\n): Promise<SponsorAuthVerifyResult> {\n const nowSec = Math.floor(Date.now() / 1000);\n if (payload.expiresAt < nowSec) {\n return { ok: false, reason: \"EXPIRED\" };\n }\n\n // v0.7.1 — strict hex format check before deferring to viem. Previous\n // length-only `< 132` accepted arbitrary 132+ char strings even when\n // not valid hex; recoverTypedDataAddress would throw and the catch\n // collapsed both cases to INVALID_SIGNATURE_FORMAT. Verify hex shape\n // up front + accept ECDSA (65 byte = 132 hex char) AND EIP-1271\n // (variable-length, must still be valid hex).\n if (!isValidHexSignature(signature)) {\n return { ok: false, reason: \"INVALID_SIGNATURE_FORMAT\" };\n }\n\n let recovered: Address;\n try {\n const { domain, types, primaryType, message } =\n buildSponsorAuthTypedData(payload);\n recovered = await recoverTypedDataAddress({\n domain,\n types,\n primaryType,\n message,\n signature,\n });\n } catch {\n return { ok: false, reason: \"INVALID_SIGNATURE_FORMAT\" };\n }\n\n if (recovered.toLowerCase() !== expectedSigner.toLowerCase()) {\n return { ok: false, reason: \"INVALID_SIGNER\", recoveredAddress: recovered };\n }\n\n return { ok: true, recoveredAddress: recovered };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/eip712/domain.ts","../src/eip712/mintRequest.ts","../src/constants.ts","../src/eip712/verifyDeadline.ts","../src/eip712/burnRequest.ts"],"sourcesContent":["import type { Address, PublicClient } from \"viem\";\nimport type { PointTokenDomainConfig } from \"../types\";\nimport { pointTokenAbi } from \"../abi/pointToken\";\n\n/**\n * Build the EIP-712 domain struct from a PointToken config. Uses\n * `config.version` when supplied; defaults to `\"1\"` for back-compat.\n */\nexport function buildDomain(config: PointTokenDomainConfig) {\n return {\n name: config.name,\n version: config.version ?? \"1\",\n chainId: config.chainId,\n verifyingContract: config.verifyingContract,\n };\n}\n\n/**\n * Domain mismatch error thrown by `assertDomainMatchesContract`.\n */\nexport class Eip712DomainMismatchError extends Error {\n constructor(\n public readonly field: \"name\" | \"version\" | \"chainId\" | \"verifyingContract\",\n public readonly expected: string,\n public readonly actual: string,\n ) {\n super(\n `EIP-712 domain mismatch on field \"${field}\": expected ${expected}, got ${actual}. ` +\n `Local SDK config is out of sync with deployed PointToken — signatures will be rejected on-chain. ` +\n `Update SDK config or contract before producing more signatures.`,\n );\n this.name = \"Eip712DomainMismatchError\";\n }\n}\n\n/**\n * One-RPC health check that the local EIP-712 domain config matches\n * what the deployed `PointToken` reports via `eip712Domain()`. If the\n * contract has bumped `version` from `\"1\"` to `\"2\"` (or any field\n * differs), every signature produced with the stale config will be\n * silently rejected on-chain (`ECDSA: invalid signature` — opaque to\n * the user).\n *\n * Recommended: call once at issuer-startup health check, not on every\n * mint. Throws `Eip712DomainMismatchError` describing the diverged\n * field.\n *\n * @example\n * ```ts\n * import { assertDomainMatchesContract, buildDomain } from \"@pafi-dev/core\";\n *\n * const expected = buildDomain({\n * name: \"PointToken\",\n * chainId: 8453,\n * verifyingContract: \"0x855c2046AD49AcF9B3B32557176FfCB1a1A38A22\",\n * });\n *\n * await assertDomainMatchesContract(publicClient, expected);\n * // → throws Eip712DomainMismatchError if version on-chain has bumped\n * ```\n */\nexport async function assertDomainMatchesContract(\n client: PublicClient,\n expected: ReturnType<typeof buildDomain>,\n): Promise<void> {\n const onChain = (await client.readContract({\n address: expected.verifyingContract as Address,\n abi: pointTokenAbi,\n functionName: \"eip712Domain\",\n })) as readonly [\n `0x${string}`, // fields (bytes1)\n string, // name\n string, // version\n bigint, // chainId\n Address, // verifyingContract\n `0x${string}`, // salt\n readonly bigint[], // extensions\n ];\n\n const [, name, version, chainId, verifyingContract] = onChain;\n\n if (name !== expected.name) {\n throw new Eip712DomainMismatchError(\"name\", expected.name, name);\n }\n if (version !== expected.version) {\n throw new Eip712DomainMismatchError(\n \"version\",\n expected.version,\n version,\n );\n }\n if (chainId !== BigInt(expected.chainId)) {\n throw new Eip712DomainMismatchError(\n \"chainId\",\n String(expected.chainId),\n chainId.toString(),\n );\n }\n if (\n verifyingContract.toLowerCase() !==\n expected.verifyingContract.toLowerCase()\n ) {\n throw new Eip712DomainMismatchError(\n \"verifyingContract\",\n expected.verifyingContract,\n verifyingContract,\n );\n }\n}\n","import { getAddress, parseSignature, recoverTypedDataAddress } from \"viem\";\nimport type { Address, Hex, WalletClient } from \"viem\";\nimport { mintRequestTypes } from \"../constants\";\nimport type {\n EIP712Signature,\n MintRequest,\n PointTokenDomainConfig,\n SignatureVerification,\n SignatureVerifyOptions,\n} from \"../types\";\nimport { buildDomain } from \"./domain\";\nimport { assertValidCurrentTimeSec } from \"./verifyDeadline\";\n\nconst PRIMARY_TYPE = \"MintForRequest\" as const;\n\n/**\n * Build the EIP-712 typed data object for a MintForRequest.\n * Returns the standard `{ domain, types, primaryType, message }` structure\n * that any EIP-712 signer (viem, ethers, Privy, WalletConnect) can consume.\n */\nexport function buildMintRequestTypedData(\n domain: PointTokenDomainConfig,\n message: MintRequest,\n) {\n return {\n domain: buildDomain(domain),\n types: mintRequestTypes,\n primaryType: PRIMARY_TYPE,\n message,\n };\n}\n\n/**\n * Sign a MintForRequest. Caller passes the full 5-field message:\n * - user = off-chain spender (drives nonce stream)\n * - receiver = on-chain caller (= msg.sender of `mint()`; wrapper or user)\n * - amount = PT amount\n * - nonce = pointToken.mintRequestNonces(user)\n * - deadline = unix seconds\n */\nexport async function signMintRequest(\n walletClient: WalletClient,\n domain: PointTokenDomainConfig,\n message: MintRequest,\n): Promise<EIP712Signature> {\n const serialized = await walletClient.signTypedData({\n account: walletClient.account!,\n domain: buildDomain(domain),\n types: mintRequestTypes,\n primaryType: PRIMARY_TYPE,\n message,\n });\n\n const { v, r, s } = parseSignature(serialized);\n\n return {\n v: Number(v),\n r,\n s,\n serialized,\n };\n}\n\n/**\n * Verify a MintForRequest signature. Always recovers the signer and\n * compares against `expectedMinter`. Pass `options.currentTimeSec`\n * (unix seconds) to additionally enforce the request's `deadline` —\n * the on-chain contract enforces `block.timestamp <= deadline`, so\n * a stale-but-cryptographically-valid sig will only revert\n * on-submission unless the caller pre-flights here.\n *\n * Result shape:\n * - `{ isValid: true, recoveredAddress }` — sig matches and (if\n * deadline check requested) deadline has not elapsed.\n * - `{ isValid: false, recoveredAddress, reason: 'INVALID_SIGNER' }`\n * — sig does not recover to `expectedMinter`.\n * - `{ isValid: false, recoveredAddress, reason: 'DEADLINE_PASSED' }`\n * — sig is valid but `deadline < currentTimeSec`.\n *\n * Throws (synchronous Promise rejection):\n * - When `options.currentTimeSec` is provided but not a finite,\n * non-negative number. Misusing the API should fail loudly.\n */\nexport async function verifyMintRequest(\n domain: PointTokenDomainConfig,\n message: MintRequest,\n signature: Hex,\n expectedMinter: Address,\n options?: SignatureVerifyOptions,\n): Promise<SignatureVerification> {\n assertValidCurrentTimeSec(options?.currentTimeSec);\n\n const recoveredAddress = await recoverTypedDataAddress({\n domain: buildDomain(domain),\n types: mintRequestTypes,\n primaryType: PRIMARY_TYPE,\n message,\n signature,\n });\n\n if (getAddress(recoveredAddress) !== getAddress(expectedMinter)) {\n return { isValid: false, recoveredAddress, reason: \"INVALID_SIGNER\" };\n }\n\n if (\n options?.currentTimeSec !== undefined &&\n message.deadline < BigInt(options.currentTimeSec)\n ) {\n return { isValid: false, recoveredAddress, reason: \"DEADLINE_PASSED\" };\n }\n\n return { isValid: true, recoveredAddress };\n}\n","import type { Address, Hex } from \"viem\";\nimport type { ChainConfig, PoolKey } from \"./types\";\n\n// -------------------------------------------------------------------------\n// EIP-712 type definitions for viem\n// -------------------------------------------------------------------------\n\n/**\n * EIP-712 typed data for the sig-gated mint path.\n *\n * V2 dual-bucket — adds `source` (uint8) so the on-chain digest matches\n * `MintForRequest(address user,address receiver,uint256 amount,uint8 source,uint256 nonce,uint256 deadline)`.\n * Source values per `IPointToken.Source`:\n * 0 = EQUITY (mint backed by issuer's declared capital)\n * 1 = VAULT (mint backed by LP collateral in SettlementVault)\n *\n * SDK helpers `signMintRequest` / `prepareMint` hardcode `source = 0`\n * (EQUITY). VAULT minting is contract-ready but no SDK writer ships it\n * until the LP/admin dashboard lands; sponsor-relayer fails closed on\n * `source != EQUITY` for the same reason.\n *\n * Contract enforces:\n * - msg.sender == receiver (the on-chain caller of `mint`)\n * - nonce == mintRequestNonces[user] (per-user nonce stream)\n *\n * `user` is the off-chain spender (whose nonce advances), `receiver` is\n * the on-chain mint recipient. For direct mints `user == receiver` (user\n * calls PointToken.mint themselves). For wrapper-mediated mints `user ==\n * end-user`, `receiver == wrapper address`.\n */\nexport const mintRequestTypes = {\n MintForRequest: [\n { name: \"user\", type: \"address\" },\n { name: \"receiver\", type: \"address\" },\n { name: \"amount\", type: \"uint256\" },\n { name: \"source\", type: \"uint8\" },\n { name: \"nonce\", type: \"uint256\" },\n { name: \"deadline\", type: \"uint256\" },\n ],\n} as const;\n\n/**\n * EIP-712 typed data for the sig-gated burn path. V2 adds `source`\n * (uint8) to match the contract's 6-field digest:\n * `BurnRequest(address from,uint256 amount,uint8 source,uint256 nonce,uint256 deadline)`\n *\n * Same EQUITY-only policy as `mintRequestTypes`.\n */\nexport const burnRequestTypes = {\n BurnRequest: [\n { name: \"from\", type: \"address\" },\n { name: \"amount\", type: \"uint256\" },\n { name: \"source\", type: \"uint8\" },\n { name: \"nonce\", type: \"uint256\" },\n { name: \"deadline\", type: \"uint256\" },\n ],\n} as const;\n\n// Sponsored mints are implemented at the relayer layer (sponsor-relayer\n// pays gas for the sig-gated `MintForRequest`); the deployed PointToken\n// contract has no separate `ReceiverConsent` path, no extra EIP-712 type,\n// and no separate on-chain nonce mapping.\n\n// -------------------------------------------------------------------------\n// Chain-indexed constants — add entries here as new chains are supported\n// -------------------------------------------------------------------------\n\nexport const SUPPORTED_CHAINS: Record<number, ChainConfig> = {\n 8453: { name: \"Base\" },\n};\n\n/**\n * Uniswap V3 QuoterV2 — used by `findBestQuote` / `quoteExactInput` etc.\n * QuoterV2 (vs V1) reverts cleanly inside multicall and returns a 4-tuple\n * `(amountOut, sqrtPriceX96AfterList, initializedTicksCrossedList, gasEstimate)`.\n */\nexport const QUOTER_V2_ADDRESSES: Record<number, Address> = {\n 8453: \"0xa0765363D9EA1347Afcff6Ae21D6D7B9d36490D0\",\n};\n\n/**\n * Uniswap UniversalRouter — used for AA/batched swaps via PT delegated\n * accounts. Speaks V3 commands (`V3_SWAP_EXACT_IN = 0x00`,\n * `V3_SWAP_EXACT_OUT = 0x01`).\n */\nexport const UNIVERSAL_ROUTER_ADDRESSES: Record<number, Address> = {\n 8453: \"0x008887C992A5bDC24097E717Bfb71CE89483c5A2\",\n};\n\n/**\n * Uniswap V3 SwapRouter — used by `swapDirect`-style flows that bypass\n * the UniversalRouter. Takes `exactInput` / `exactOutput` with packed-bytes\n * paths directly.\n */\nexport const V3_SWAP_ROUTER_ADDRESSES: Record<number, Address> = {\n 8453: \"0xca937aC69708b00B72cc3247440211d8DbDAaFF8\",\n};\n\n/**\n * PAFI's Uniswap V3 factory — used together with `V3_POOL_INIT_CODE_HASH`\n * to derive pool addresses deterministically (`computeV3PoolAddress`).\n */\nexport const V3_FACTORY_ADDRESSES: Record<number, Address> = {\n 8453: \"0x154bAFC6C311f3909080f28438294Cd5184c2924\",\n};\n\n/**\n * Pool-init code hash for PAFI's V3 deployment. Combined with the factory\n * address + sorted tokens + fee, deterministically yields a pool's\n * address via the standard Uniswap V3 CREATE2 derivation.\n */\nexport const V3_POOL_INIT_CODE_HASH: Hex =\n \"0x15380e4800aaa7f219c07d4e07d86cc0e3df225c6a518a1f14f8565f6d9c57c3\";\n\nexport const COMMON_TOKENS: Record<number, Record<string, Address>> = {\n // Base\n 8453: {\n WETH: \"0x4200000000000000000000000000000000000006\",\n USDC: \"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913\",\n USDT: \"0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2\",\n },\n};\n\nexport const COMMON_POOLS: Record<number, PoolKey[]> = {\n // Base — Uniswap V3 pools (PAFI deployment)\n 8453: [\n // WETH/USDC 0.3%\n {\n token0: \"0x4200000000000000000000000000000000000006\",\n token1: \"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913\",\n fee: 3000,\n },\n // WETH/USDC 0.05%\n {\n token0: \"0x4200000000000000000000000000000000000006\",\n token1: \"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913\",\n fee: 500,\n },\n ],\n};\n\nexport const POINT_TOKEN_POOLS: Record<number, Record<Address, PoolKey[]>> = {\n // chainId → pointTokenAddress → PoolKey[]\n};\n\n// -------------------------------------------------------------------------\n// Protocol constants — chain-agnostic (same address on every EVM chain)\n// -------------------------------------------------------------------------\n\n/** ERC-4337 v0.7 EntryPoint — deployed deterministically across all EVM chains. */\nexport const ENTRY_POINT_V07: Address = \"0x0000000071727De22E5E9d8BAf0edAc6f37da032\";\n\n/**\n * ERC-4337 v0.8 EntryPoint — used by Pimlico's `Simple7702Account` impl\n * (`0xe6Cae83BdE06E4c305530e199D7217f42808555B`) and by permissionless's\n * `to7702SimpleSmartAccount`. EIP-7702 delegated EOAs in PAFI's flow\n * point at this EntryPoint, NOT v0.7.\n *\n * Why this matters: account.validateUserOp does\n * `require(msg.sender == entryPoint(), \"account: not from EntryPoint\")`.\n * If the bundler/paymaster sim runs against a different EntryPoint than\n * what the account's `entryPoint()` returns, the require fails and you\n * see `AA23 reverted account: not from EntryPoint`.\n */\nexport const ENTRY_POINT_V08: Address = \"0x4337084d9e255ff0702461cf8895ce9e3b5ff108\";\n\n/** Permit2 — Uniswap's universal approval contract, same address on all EVM chains. */\n// export const PERMIT2_ADDRESS: Address = \"0x000000000022D473030F116dDEE9F6B43aC78BA3\";\n/**\n * Permit2 — PAFI's deployed instance. PAFI ships its own forked deployment.\n */\nexport const PERMIT2_ADDRESS: Address = \"0xEB450d21ae68D3303Cf5775A54Cc84EE7c3fC8eC\";","/**\n * Shared input validator for `verifyMintRequest` /\n * `verifyBurnRequest`'s `options.currentTimeSec`. Centralised so both\n * helpers fail in the same way on bad input — a quiet skip on\n * `NaN` / negative values would defeat the purpose of opting into\n * deadline checking.\n *\n * No-op when `currentTimeSec` is undefined: the caller did not opt in\n * to deadline checking.\n */\nexport function assertValidCurrentTimeSec(\n currentTimeSec: number | undefined,\n): void {\n if (currentTimeSec === undefined) return;\n if (!Number.isFinite(currentTimeSec) || currentTimeSec < 0) {\n throw new Error(\n `verifyDeadline: currentTimeSec must be a finite non-negative number (got ${String(\n currentTimeSec,\n )})`,\n );\n }\n}\n","import { getAddress, parseSignature, recoverTypedDataAddress } from \"viem\";\nimport type { Address, Hex, WalletClient } from \"viem\";\nimport { burnRequestTypes } from \"../constants\";\nimport type {\n BurnRequest,\n EIP712Signature,\n PointTokenDomainConfig,\n SignatureVerification,\n SignatureVerifyOptions,\n} from \"../types\";\nimport { buildDomain } from \"./domain\";\nimport { assertValidCurrentTimeSec } from \"./verifyDeadline\";\n\n/**\n * EIP-712 helpers for `BurnRequest` — consumed by the sig-gated burn\n * path on `PointToken`:\n *\n * burn(address from, uint256 amount, uint256 deadline, bytes burnerSig)\n *\n * Solidity type hash:\n * BurnRequest(address from,uint256 amount,uint256 nonce,uint256 deadline)\n *\n * Issuer backend signs with its burner signer (HSM/KMS). On-chain\n * `msg.sender` must equal `from`, and the recovered signer must be in\n * `burners[]`. Nonce comes from `burnRequestNonces[from]` and is\n * auto-incremented on success.\n */\nexport function buildBurnRequestTypedData(\n domain: PointTokenDomainConfig,\n message: BurnRequest,\n) {\n return {\n domain: buildDomain(domain),\n types: burnRequestTypes,\n primaryType: \"BurnRequest\" as const,\n message,\n };\n}\n\nexport async function signBurnRequest(\n walletClient: WalletClient,\n domain: PointTokenDomainConfig,\n message: BurnRequest,\n): Promise<EIP712Signature> {\n const serialized = await walletClient.signTypedData({\n account: walletClient.account!,\n domain: buildDomain(domain),\n types: burnRequestTypes,\n primaryType: \"BurnRequest\",\n message,\n });\n\n const { v, r, s } = parseSignature(serialized);\n\n return {\n v: Number(v),\n r,\n s,\n serialized,\n };\n}\n\n/**\n * Verify a BurnRequest signature. Symmetric with `verifyMintRequest`:\n * always recovers the signer; opt-in deadline check via\n * `options.currentTimeSec`. See that helper for the full contract.\n */\nexport async function verifyBurnRequest(\n domain: PointTokenDomainConfig,\n message: BurnRequest,\n signature: Hex,\n expectedBurner: Address,\n options?: SignatureVerifyOptions,\n): Promise<SignatureVerification> {\n assertValidCurrentTimeSec(options?.currentTimeSec);\n\n const recoveredAddress = await recoverTypedDataAddress({\n domain: buildDomain(domain),\n types: burnRequestTypes,\n primaryType: \"BurnRequest\",\n message,\n signature,\n });\n\n if (getAddress(recoveredAddress) !== getAddress(expectedBurner)) {\n return { isValid: false, recoveredAddress, reason: \"INVALID_SIGNER\" };\n }\n\n if (\n options?.currentTimeSec !== undefined &&\n message.deadline < BigInt(options.currentTimeSec)\n ) {\n return { isValid: false, recoveredAddress, reason: \"DEADLINE_PASSED\" };\n }\n\n return { isValid: true, recoveredAddress };\n}\n"],"mappings":"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","names":["getAddress","parseSignature","recoverTypedDataAddress","parseSignature","recoverTypedDataAddress","getAddress"]}
|