@pafi-dev/core 0.9.6 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,21 +4,43 @@
4
4
  [![License: Apache-2.0](https://img.shields.io/badge/License-Apache--2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
5
5
 
6
6
  Pure primitives for the PAFI point-token system. EIP-712 signing,
7
- contract ABIs + addresses, ERC-4337 UserOp building, EIP-7702
8
- delegation, sponsor-auth signing, perp deposit calldata, fee quoting.
7
+ contract ABIs + addresses (v1.6), ERC-4337 UserOp building, EIP-7702
8
+ delegation, sponsor-auth signing, perp deposit calldata, fee quoting,
9
+ MintFeeWrapper helpers.
9
10
 
10
11
  **Browser + Node-safe.** Zero HTTP client. Peer-dep: `viem ^2`.
11
12
 
12
13
  ---
13
14
 
14
- ## Requirements
15
+ ## What's new in 0.10.0 (breaking)
15
16
 
16
- - Node.js >= 18 (or modern browser with native `fetch`)
17
- - TypeScript >= 5.0
18
- - `viem` ^2.0.0 (peer)
17
+ | Change | Detail |
18
+ |---|---|
19
+ | `ReceiverConsent` removed | The `ReceiverConsent` EIP-712 type, helpers (`buildReceiverConsentTypedData`, `signReceiverConsent`, `verifyReceiverConsent`), constants (`receiverConsentTypes`), and contract reader (`getReceiverConsentNonce`) are all **deleted**. The deployed `PointToken` contract never had a 3rd "sponsored" mint path — that pattern is implemented at the relayer layer (sponsor-relayer pays gas for path-2 `MintForRequest` sig-gated mint). The previous fallback that read ERC-2612 `nonces(owner)` was semantically wrong and is gone. |
20
+ | `PafiSDK.consent` namespace removed | The convenience class no longer exposes `consent.buildTypedData / sign / verify / getNonce`. |
21
+ | Action required for callers | Replace any `signReceiverConsent` / `getReceiverConsentNonce` usage with the path-2 sig-gated mint flow (`MintRequest` + `getMintRequestNonce`). |
22
+
23
+ ### What's new in v1.6 (since 0.9.0)
24
+
25
+ | Change | Detail |
26
+ |---|---|
27
+ | `MintFeeWrapper` (NEW) | Single global wrapper contract that skims a configurable fee on every sig-gated mint and distributes to per-PT recipients |
28
+ | EIP-712 typehash | `MintRequest(to, amount, nonce, deadline)` → **`MintForRequest(user, receiver, amount, nonce, deadline)`** (5 fields) |
29
+ | Issuer struct | Dropped `declaredTotalSupply` + `capBasisPoints` — caps moved to `MintingOracle.tokenCaps(pointToken)` (per-token, not per-issuer) |
30
+ | `verifyMintCap` signature | Now takes `pointToken` as first arg |
31
+ | New helpers | `getMintFeeBps`, `getMintFeeRecipients`, `getTokenCap` |
32
+ | Subgraph URL | Hardcoded to `pafi-subgraph-v3` (`getPafiServiceUrls(chainId)`) |
33
+ | `pafiHook` | DEPRECATED — V4 swap-time fee removed entirely in v1.6 |
34
+ | `getPtPerUsdt18dec` math fix (0.9.6) | Subgraph V4 returns HUMAN-readable price; old formula `10^24/raw` was off by ~10^11 |
19
35
 
20
36
  ---
21
37
 
38
+ ## Requirements
39
+
40
+ - Node.js ≥ 18 (or modern browser with native `fetch`)
41
+ - TypeScript ≥ 5.0
42
+ - `viem` ^2.0.0 (peer)
43
+
22
44
  ## Installation
23
45
 
24
46
  ```bash
@@ -27,10 +49,9 @@ pnpm add @pafi-dev/core viem
27
49
 
28
50
  ---
29
51
 
30
- ## What's in core (and what's not)
52
+ ## Package layout (v1.6)
31
53
 
32
- Core ships **data + primitives** only. Higher-level orchestration lives
33
- in sibling packages:
54
+ Core ships **data + primitives** only. Higher-level orchestration in siblings:
34
55
 
35
56
  | Concern | Package |
36
57
  | --- | --- |
@@ -39,75 +60,62 @@ in sibling packages:
39
60
  | Issuer DB ledger (TypeORM + Postgres) | `@pafi-dev/issuer-postgres` |
40
61
  | Trading (swap + quote, FE-callable) | `@pafi-dev/trading` |
41
62
 
42
- > **0.6.0 (2026-04-27):** swap UserOp builders + V4 quoting moved
43
- > from core to `@pafi-dev/trading`. Core retains the underlying
44
- > `universalRouterAbi`, `permit2Abi`, `v4QuoterAbi`, `PoolKey`,
45
- > `PathKey`, `QuoteResult` types — trading wraps them with the actual
46
- > swap logic. The `PafiSDK` class also drops `quote` / `swap`
47
- > namespaces; use trading or import primitives directly.
48
- >
49
- > **0.6.1 (2026-04-27):** added `quoteOperatorFeeUsdt` so FE can
50
- > compute USDT-denominated gas fee without round-tripping the issuer
51
- > backend (`/gas-fee` is now optional).
52
- >
53
- > **0.7.5 (2026-04-28):** added `delegateDirect()` — FE one-shot EIP-7702
54
- > delegation that user pays gas for. Bypasses the AA + sponsor-relayer
55
- > stack entirely. See [section below](#delegatedirect--fe-one-shot-delegation-no-aa--no-sponsor).
56
-
57
63
  ---
58
64
 
59
- ## Modules
65
+ ## Exports cheatsheet
60
66
 
61
- | Symbol / area | What it provides |
67
+ | Symbol / area | Provides |
62
68
  | --- | --- |
63
- | `getContractAddresses(chainId)` | All deployed PAFI contract addresses keyed by chain |
64
- | `signMintRequest`, `verifyMintRequest` | EIP-712 sign / recover for `MintRequest` |
65
- | `signBurnRequest`, `verifyBurnRequest` | EIP-712 sign / recover for `BurnRequest` |
66
- | `signReceiverConsent`, `verifyReceiverConsent` | EIP-712 for sponsored-mint consent |
69
+ | `getContractAddresses(chainId)` | All deployed PAFI v1.6 addresses keyed by chain |
70
+ | `signMintRequest`, `verifyMintRequest`, `buildMintRequestTypedData` | EIP-712 sign/recover for v1.6 `MintForRequest` (5 fields) |
71
+ | `signBurnRequest`, `verifyBurnRequest`, `buildBurnRequestTypedData` | EIP-712 for `BurnRequest` |
67
72
  | `signSponsorAuth`, `verifySponsorAuth`, `buildAndSignSponsorAuth` | EIP-712 for sponsor-relayer auth |
68
73
  | `buildPartialUserOperation`, `assembleUserOperation`, `computeUserOpHash` | ERC-4337 v0.7+ UserOp builder + hash |
69
- | `encodeBatchExecute`, `decodeBatchExecuteCalls` | BatchExecutor (Coinbase SW v2) calldata |
70
- | `buildDelegationUserOp`, `computeAuthorizationHash`, `parseEip7702DelegatedAddress` | EIP-7702 helpers |
71
- | `splitAuthorizationSig`, `buildEip7702Authorization` | Split user's authSig + assemble bundler tuple |
72
- | `delegateDirect` | One-shot FE-direct EIP-7702 delegation (user pays gas, no AA) |
74
+ | `encodeBatchExecute`, `decodeBatchExecuteCalls` | BatchExecutor (Pimlico Simple7702Account) calldata |
75
+ | `buildDelegationUserOp`, `computeAuthorizationHash`, `parseEip7702DelegatedAddress`, `splitAuthorizationSig`, `buildEip7702Authorization`, `delegateDirect` | EIP-7702 helpers (build + introspect) |
73
76
  | `buildPerpDepositViaRelay`, `buildPerpDepositWithGasDeduction`, `ORDERLY_RELAY_ABI`, `ORDERLY_VAULT_ABI`, `BROKER_HASHES`, `TOKEN_HASHES`, `computeAccountId` | Orderly perp deposit calldata + types |
74
- | `quoteOperatorFeePt`, `quoteOperatorFeeUsdt` | Off-issuer fee quoter (Chainlink + V4 subgraph) |
75
- | `fetchPafiPools`, `PAFI_SUBGRAPH_URL` | Pool discovery via PAFI subgraph |
76
- | `pointTokenAbi` (alias `POINT_TOKEN_V2_ABI`), `issuerRegistryAbi`, `mintingOracleAbi`, `pointTokenFactoryAbi`, `erc20Abi`, `permit2Abi`, `universalRouterAbi`, `v4QuoterAbi` | Contract ABIs |
77
- | `getMintRequestNonce`, `getBurnRequestNonce`, `getReceiverConsentNonce`, `getPointTokenBalance`, `isMinter`, `getTokenName` | On-chain read helpers |
78
- | `createLoginMessage` | EIP-4361 SIWE login message builder |
79
- | `parseEip7702DelegatedAddress`, `checkDelegation`, `isDelegatedTo`, `isDelegatedToTarget` | EIP-7702 delegation introspection |
77
+ | `quoteOperatorFeePt`, `quoteOperatorFeeUsdt` | Gas-reimbursement fee quoter (Chainlink + V4 subgraph) |
78
+ | `getMintFeeBps`, `getMintFeeRecipients` | v1.6 read wrapper fee config per pointToken |
79
+ | `getTokenCap` | v1.6 read `MintingOracle.tokenCaps(pointToken)` |
80
+ | `verifyMintCap`, `getPointTokenIssuer` | MintingOracle read helpers (v1.6 signature) |
81
+ | `getIssuer`, `isActiveIssuer`, `issuerRegistryGetIssuerFlatAbi` | IssuerRegistry helpers (v1.6 7 fields) |
82
+ | `getMintRequestNonce`, `getBurnRequestNonce`, `getPointTokenBalance`, `isMinter`, `getTokenName`, `getPointTokenIssuerAddress` | PointToken read helpers |
83
+ | `fetchPafiPools`, `PAFI_SUBGRAPH_URL` | Pool discovery via PAFI subgraph v3 |
84
+ | `getPafiServiceUrls(chainId)` | Hardcoded PAFI service endpoints (sponsorRelayer, issuerApi) |
85
+ | `pointTokenAbi` (alias `POINT_TOKEN_V2_ABI`), `issuerRegistryAbi`, `mintingOracleAbi`, `mintFeeWrapperAbi`, `pointTokenFactoryAbi`, `erc20Abi`, `permit2Abi`, `universalRouterAbi`, `v4QuoterAbi` | Contract ABIs (regenerated from Foundry artifacts) |
86
+ | `createLoginMessage` | EIP-4361 SIWE login builder |
80
87
 
81
88
  ---
82
89
 
83
- ## Contract addresses
90
+ ## Contract addresses (v1.6)
84
91
 
85
92
  ```ts
86
93
  import { getContractAddresses } from "@pafi-dev/core";
87
94
 
88
- const {
89
- pointToken,
90
- batchExecutor, // EIP-7702 delegation target (Coinbase SW v2)
91
- usdt,
92
- issuerRegistry,
93
- mintingOracle,
94
- pafiHook, // Uniswap V4 hook (10% fee on PT→USDT only)
95
- chainlinkEthUsd,
96
- orderlyRelay,
97
- pafiFeeRecipient,
98
- universalRouter,
99
- } = getContractAddresses(8453);
95
+ const addr = getContractAddresses(8453); // Base mainnet
96
+ // addr.issuerRegistry = 0xAB1d1e117c41636f30bb10194Fe6B774B6Da9E01
97
+ // addr.mintingOracle = 0x2f4cf8C5F8b41efC970c5b46a5d905CeA1f871a0
98
+ // addr.mintFeeWrapper = 0xD324EE2e3220B23d1b1BfbB85f5bC1EF2E917B93 (NEW)
99
+ // addr.usdt = 0x3F7e71B150e97316Bb9f363A32c19CcD36ac2382
100
+ // addr.batchExecutor = 0xe6Cae83BdE06E4c305530e199D7217f42808555B (Pimlico Simple7702)
101
+ // addr.pafiFeeRecipient = ... (PAFI ops wallet)
102
+ // addr.chainlinkEthUsd = 0x71041dddad3595F9CEd3DcCFBe3D1F4b0a16Bb70
103
+ // addr.pafiHook = 0x...dead (DEPRECATED in v1.6)
104
+ // addr.pointToken = dead-zero (PER-ISSUER — read from env or factory)
100
105
  ```
101
106
 
102
- Throws if `chainId` isn't in the map. Base mainnet (8453) is the only
103
- chain in active use.
107
+ > Per-issuer `pointToken` clones are NOT in the chain-level address bag.
108
+ > Read from your issuer env (e.g. `POINT_TOKEN_ADDRESS`) or
109
+ > `PointTokenFactory.createToken()` at runtime.
110
+
111
+ Deploy block: **45683465** (canonical for all v1.6 indexers).
104
112
 
105
113
  ---
106
114
 
107
- ## EIP-712 signing
115
+ ## EIP-712 — `MintForRequest` (v1.6)
108
116
 
109
117
  ```ts
110
- import { signMintRequest, getContractAddresses } from "@pafi-dev/core";
118
+ import { signMintRequest, verifyMintRequest } from "@pafi-dev/core";
111
119
  import { privateKeyToAccount } from "viem/accounts";
112
120
  import { createWalletClient, http } from "viem";
113
121
 
@@ -116,183 +124,131 @@ const wallet = createWalletClient({
116
124
  transport: http(),
117
125
  });
118
126
 
119
- const { pointToken } = getContractAddresses(8453);
120
127
  const sig = await signMintRequest(
121
128
  wallet,
122
- { name: "POINT", chainId: 8453, verifyingContract: pointToken },
129
+ { name: "POINT", chainId: 8453, verifyingContract: pointTokenAddress },
123
130
  {
124
- to: userAddress,
131
+ user, // off-chain spender (drives nonces)
132
+ receiver: wrapperAddress, // on-chain caller of PointToken.mint
133
+ // (= wrapper for wrapper-mediated; = user for direct)
125
134
  amount: 1000n * 10n ** 18n,
126
- nonce: currentMintRequestNonce,
135
+ nonce: currentMintRequestNonce, // from PointToken.mintRequestNonces(user)
127
136
  deadline: BigInt(Math.floor(Date.now() / 1000) + 900),
128
137
  },
129
138
  );
130
139
 
131
- // sig.serialized — bytes hex passed to PointToken.mint(...)
140
+ // sig.serialized — bytes hex passed to PointToken.mint(...) or wrapper.mintWithFee(...)
132
141
  ```
133
142
 
134
- Same pattern for `signBurnRequest`, `signReceiverConsent`,
135
- `signSponsorAuth`. KMS-backed wallets work pass any
136
- `viem WalletClient`.
143
+ Wrapper-mediated mint puts `receiver = wrapperAddress`; direct mint puts
144
+ `receiver = user`. The contract requires `msg.sender == receiver`.
137
145
 
138
146
  ---
139
147
 
140
- ## ERC-4337 UserOp building
148
+ ## MintFeeWrapper helpers (v1.6)
141
149
 
142
150
  ```ts
143
- import { encodeBatchExecute, buildPartialUserOperation } from "@pafi-dev/core";
151
+ import { getMintFeeBps, getMintFeeRecipients, getContractAddresses } from "@pafi-dev/core";
144
152
 
145
- const callData = encodeBatchExecute([
146
- { target: pointToken, value: 0n, data: mintCallData },
147
- { target: pointToken, value: 0n, data: feeTransferCallData }, // optional
148
- ]);
153
+ const { mintFeeWrapper } = getContractAddresses(8453);
154
+ const POINT_TOKEN = "0x855c2046...";
149
155
 
150
- const partial = buildPartialUserOperation({
151
- sender: userAddress,
152
- nonce: aaNonce,
153
- operations: [...],
154
- gasLimits: { callGasLimit: 300_000n, verificationGasLimit: 150_000n, preVerificationGas: 50_000n },
155
- });
156
+ const bps = await getMintFeeBps(provider, mintFeeWrapper, POINT_TOKEN);
157
+ // bps = 50 (= 0.50%)
158
+
159
+ const recipients = await getMintFeeRecipients(provider, mintFeeWrapper, POINT_TOKEN);
160
+ // [{ account: 0x..., basisPoints: 20 }, { account: 0x..., basisPoints: 30 }]
156
161
  ```
157
162
 
158
- `computeUserOpHash(userOp, chainId)` EntryPoint v0.8 EIP-712 digest.
159
- The mobile prepare/submit flow signs this hash via Privy
160
- `personal_sign`.
163
+ Wrapper deducts `gross × bps / 10000` PT on every mint and distributes
164
+ to recipients. User receives `gross × (1 - bps/10000)` net.
161
165
 
162
166
  ---
163
167
 
164
- ## EIP-7702 delegation helpers
168
+ ## Operator fee quoter
165
169
 
166
- ```ts
167
- import {
168
- computeAuthorizationHash,
169
- parseEip7702DelegatedAddress,
170
- splitAuthorizationSig,
171
- buildEip7702Authorization,
172
- } from "@pafi-dev/core";
170
+ Two helpers for FE direct usage (no backend round-trip):
173
171
 
174
- // Check whether user EOA already has delegation installed
175
- const code = await provider.getCode({ address: userAddress });
176
- const designator = parseEip7702DelegatedAddress(code); // null = not delegated
172
+ ```ts
173
+ import { quoteOperatorFeeUsdt, quoteOperatorFeePt } from "@pafi-dev/core";
177
174
 
178
- // Build the hash the user signs to authorize delegation
179
- const accountNonce = BigInt(await provider.getTransactionCount({ address: userAddress }));
180
- const authHash = computeAuthorizationHash(8453, batchExecutor, accountNonce);
175
+ // USDT-denominated (replaces issuer's GET /gas-fee). Chainlink-only.
176
+ const gasFeeUsdt = await quoteOperatorFeeUsdt({
177
+ provider, chainId: 8453, scenario: "mint",
178
+ });
181
179
 
182
- // On the bundler side: split the user's 65-byte signature into the tuple
183
- const { r, s, yParity } = splitAuthorizationSig(authSig);
184
- const authorization = buildEip7702Authorization({
185
- chainId: 8453,
186
- address: batchExecutor,
187
- nonce: accountNonce,
188
- authSig,
180
+ // PT-denominated (used by mint/burn flows). Adds V4 subgraph call.
181
+ const gasFeePt = await quoteOperatorFeePt({
182
+ provider, chainId: 8453, scenario: "mint",
183
+ pointTokenAddress: POINT_TOKEN,
184
+ allowStaleFallback: true, // recommended: don't block on subgraph outage
189
185
  });
190
186
  ```
191
187
 
192
- ---
193
-
194
- ## `delegateDirect` — FE one-shot delegation, no AA / no sponsor
195
-
196
- For the **FE-direct** path where the user pays gas in ETH and you don't
197
- want a dependency on `permissionless` / Pimlico bundlers / PAFI
198
- sponsor-relayer:
188
+ Both return `bigint` raw units. Fallback prices (`0.1 USDT/PT` default,
189
+ 3000 USD/ETH default) used when oracles unreachable + `allowStaleFallback`
190
+ is true. Sponsor-relayer's `FeeValidator` runs the same math server-side
191
+ with a 5% tolerance window.
199
192
 
200
- ```ts
201
- import { useSign7702Authorization, useWallets } from "@privy-io/react-auth";
202
- import { delegateDirect } from "@pafi-dev/core";
203
- import { createWalletClient, custom, createPublicClient, http } from "viem";
204
- import { base } from "viem/chains";
205
-
206
- const publicClient = createPublicClient({ chain: base, transport: http() });
207
-
208
- function DelegateButton() {
209
- const { wallets } = useWallets();
210
- const { signAuthorization } = useSign7702Authorization();
211
-
212
- // Privy ALWAYS signs EIP-7702 with the embedded wallet — pick that
213
- // one as the smart-account owner regardless of how user logged in.
214
- const wallet = wallets.find((w) => w.walletClientType === "privy");
215
-
216
- async function handleClick() {
217
- if (!wallet) return;
218
- const provider = await wallet.getEthereumProvider();
219
- const walletClient = createWalletClient({
220
- account: wallet.address as `0x${string}`,
221
- chain: base,
222
- transport: custom(provider),
223
- });
224
-
225
- const result = await delegateDirect({
226
- userAddress: wallet.address as `0x${string}`,
227
- chainId: 8453,
228
- publicClient,
229
- walletClient,
230
- signAuthorization,
231
- // optional: skipIfAlreadyDelegated (default true), waitForReceipt (default true)
232
- });
233
-
234
- if (result.status === "already-delegated") {
235
- console.log("Already delegated to", result.delegatedTo);
236
- } else {
237
- console.log("Delegated! tx:", result.txHash);
238
- // result.receipt — full TransactionReceipt when waitForReceipt=true
239
- }
240
- }
241
-
242
- return <button onClick={handleClick}>Delegate</button>;
243
- }
244
- ```
193
+ See [`docs/FEE_FLOW.md`](../../../docs/FEE_FLOW.md) in the main repo for
194
+ full math derivation.
245
195
 
246
- **What happens internally:**
196
+ ---
247
197
 
248
- 1. `getCode(userAddress)` if already delegated to expected impl, return early.
249
- 2. `getTransactionCount(userAddress, 'pending')` → tx nonce.
250
- 3. `signAuthorization({ contractAddress, chainId, nonce })` — Privy hook signs with embedded wallet (raw `secp256k1_sign`, no EIP-191 prefix).
251
- 4. `walletClient.sendTransaction({ to: userAddress, data: '0x', authorizationList: [auth] })` — viem encodes a type-4 EIP-7702 transaction.
252
- 5. `waitForTransactionReceipt(txHash)` (optional).
198
+ ## ERC-4337 UserOp building
253
199
 
254
- **Requirements:**
200
+ ```ts
201
+ import { encodeBatchExecute, buildPartialUserOperation } from "@pafi-dev/core";
255
202
 
256
- - Privy embedded wallet (external wallets like MetaMask **cannot**
257
- sign EIP-7702 only Privy's embedded wallet exposes raw
258
- `secp256k1_sign`).
259
- - A few cents of ETH on Base for gas (~$0.01–0.10).
203
+ const callData = encodeBatchExecute([
204
+ { target: mintFeeWrapper, value: 0n, data: mintWithFeeCallData },
205
+ { target: pointToken, value: 0n, data: feeTransferCallData },
206
+ ]);
260
207
 
261
- **vs. AA path:** `delegateDirect` produces 1 native L1 tx instead of an
262
- ERC-4337 UserOp. Cheaper, simpler, no bundler / paymaster involvement —
263
- but the user pays gas. Use the AA path (via `permissionless` +
264
- sponsor-relayer) when you want gas-free UX.
208
+ const partial = buildPartialUserOperation({
209
+ sender: userAddress,
210
+ nonce: aaNonce,
211
+ callData,
212
+ gasLimits: {
213
+ callGasLimit: 300_000n,
214
+ verificationGasLimit: 150_000n,
215
+ preVerificationGas: 50_000n,
216
+ },
217
+ });
218
+ ```
265
219
 
266
- **vs. mobile gg56 flow:** mobile delegate flow goes through gg56's
267
- `/delegate/{prepare,submit}` + sponsor-relayer for the gas sponsorship.
268
- `delegateDirect` is the FE-only counterpart that bypasses that
269
- infrastructure entirely.
220
+ `computeUserOpHash(userOp, chainId)` EntryPoint v0.8 EIP-712 digest.
221
+ Mobile signs this hash via `eth_signTypedData_v4` (NOT `personal_sign` —
222
+ Pimlico Simple7702Account does raw `ecrecover` without EIP-191 prefix).
270
223
 
271
224
  ---
272
225
 
273
- ## Operator fee quoter
226
+ ## EIP-7702 `delegateDirect`
274
227
 
275
- Two helpers for FE direct-mode usage (no backend round-trip):
228
+ FE one-shot delegation, no AA / no sponsor:
276
229
 
277
230
  ```ts
278
- import { quoteOperatorFeeUsdt, quoteOperatorFeePt } from "@pafi-dev/core";
231
+ import { delegateDirect } from "@pafi-dev/core";
232
+ import { useSign7702Authorization, useWallets } from "@privy-io/react-auth";
279
233
 
280
- // USDT-denominated (replaces issuer's GET /gas-fee).
281
- // Pure RPC + Chainlink read.
282
- const gasFeeUsdt = await quoteOperatorFeeUsdt({ provider, chainId: 8453 });
234
+ const { signAuthorization } = useSign7702Authorization();
235
+ const wallet = useWallets().find((w) => w.walletClientType === "privy");
283
236
 
284
- // PT-denominated (used by the swap flow as operator gas reimbursement
285
- // in PT). Needs a subgraph call to read PT spot price.
286
- const gasFeePt = await quoteOperatorFeePt({
287
- provider,
237
+ const result = await delegateDirect({
238
+ userAddress: wallet.address,
288
239
  chainId: 8453,
289
- pointTokenAddress: POINT_TOKEN,
240
+ publicClient,
241
+ walletClient,
242
+ signAuthorization,
243
+ // optional: skipIfAlreadyDelegated (default true), waitForReceipt (default true)
290
244
  });
245
+
246
+ // result.status: "already-delegated" | "broadcasted"
247
+ // result.txHash, result.delegatedTo, result.receipt?
291
248
  ```
292
249
 
293
- Both return `bigint` raw units. Sponsor-relayer's `FeeValidator` runs
294
- the same math server-side with a 5% tolerance window — minor drift
295
- between client and server is accepted.
250
+ **Privy embedded wallet only** external wallets (MetaMask) can't sign
251
+ EIP-7702 (no raw `secp256k1_sign` exposed). User pays ~$0.01–0.10 ETH gas.
296
252
 
297
253
  ---
298
254
 
@@ -309,15 +265,64 @@ const sponsorAuth = await buildAndSignSponsorAuth({
309
265
  issuerId: ISSUER_ID,
310
266
  issuerSignerWallet, // KMS-backed in prod
311
267
  });
268
+ // Pass to sponsor-relayer's POST /paymaster/sponsor as `sponsorAuth` field
269
+ ```
270
+
271
+ Issuer signer must be registered in `IssuerRegistry` (`signerAddress`
272
+ field) and whitelisted via `PointToken.addMinter(signer)`.
273
+
274
+ ---
275
+
276
+ ## Verify on-chain state (debug)
277
+
278
+ ```ts
279
+ import {
280
+ getIssuer, getTokenCap, getMintFeeBps,
281
+ getMintRequestNonce, isMinter,
282
+ } from "@pafi-dev/core";
283
+
284
+ // Issuer registry record (v1.6 — 7 fields)
285
+ const issuer = await getIssuer(provider, registry, issuerAddress);
286
+ // { issuerAddress, signerAddress, name, symbol, active, pointToken, mintingOracle }
287
+
288
+ // Per-token cap (v1.6 — moved off issuer struct)
289
+ const cap = await getTokenCap(provider, mintingOracle, pointToken);
290
+ // { declaredTotalSupply, capBasisPoints }
291
+
292
+ // Fee config
293
+ const bps = await getMintFeeBps(provider, wrapper, pointToken);
312
294
 
313
- // Pass to sponsor-relayer's POST /paymaster/sponsor as the `sponsorAuth` field.
295
+ // Mint state
296
+ const nonce = await getMintRequestNonce(provider, pointToken, user);
297
+ const authorized = await isMinter(provider, pointToken, signerAddress);
314
298
  ```
315
299
 
316
- The issuer signer must be registered in PAFI's `IssuerRegistry`
317
- (`signerAddress` field).
300
+ ---
301
+
302
+ ## v1.5 → v1.6 migration
303
+
304
+ 1. `pnpm add @pafi-dev/core@latest` (≥ 0.9.6)
305
+ 2. Replace `signMintRequest({ to, amount, nonce, deadline })` →
306
+ `signMintRequest({ user, receiver, amount, nonce, deadline })`
307
+ - Direct mint: `receiver = user`
308
+ - Wrapper-mediated mint: `receiver = mintFeeWrapper`
309
+ 3. Replace `verifyMintCap(client, oracle, issuer, amount)` →
310
+ `verifyMintCap(client, oracle, pointToken, issuer, amount)`
311
+ 4. Remove uses of `Issuer.declaredTotalSupply` / `Issuer.capBasisPoints`
312
+ — read from `getTokenCap(provider, oracle, pointToken)` instead
313
+ 5. Drop any reference to `pafiHook` for swap fees — V4 hook removed in v1.6
314
+ 6. Update calldata to call `mintFeeWrapper.mintWithFee(...)` when fee
315
+ is configured. `@pafi-dev/issuer`'s `RelayService` does this
316
+ automatically based on `getContractAddresses(chainId).mintFeeWrapper`
318
317
 
319
318
  ---
320
319
 
320
+ ## References
321
+
322
+ - v1.6 deploy block: **45683465** on Base mainnet (2026-05-07)
323
+ - Architecture: [`ARCHITECTURE.md`](../../ARCHITECTURE.md) at SDK root
324
+ - Fee math: [`docs/FEE_FLOW.md`](../../../docs/FEE_FLOW.md)
325
+
321
326
  ## License
322
327
 
323
328
  Apache-2.0
@@ -79,16 +79,6 @@ var burnRequestTypes = {
79
79
  { name: "deadline", type: "uint256" }
80
80
  ]
81
81
  };
82
- var receiverConsentTypes = {
83
- ReceiverConsent: [
84
- { name: "onBehalfOf", type: "address" },
85
- { name: "originalReceiver", type: "address" },
86
- { name: "amount", type: "uint256" },
87
- { name: "nonce", type: "uint256" },
88
- { name: "deadline", type: "uint256" },
89
- { name: "extData", type: "bytes" }
90
- ]
91
- };
92
82
  var SUPPORTED_CHAINS = {
93
83
  8453: { name: "Base" }
94
84
  };
@@ -210,48 +200,6 @@ async function verifyBurnRequest(domain, message, signature, expectedBurner) {
210
200
  return { isValid, recoveredAddress };
211
201
  }
212
202
 
213
- // src/eip712/receiverConsent.ts
214
-
215
- function buildReceiverConsentTypedData(domain, message) {
216
- return {
217
- domain: buildDomain(domain),
218
- types: receiverConsentTypes,
219
- primaryType: "ReceiverConsent",
220
- message
221
- };
222
- }
223
- async function signReceiverConsent(walletClient, domain, message) {
224
- const serialized = await walletClient.signTypedData({
225
- account: walletClient.account,
226
- domain: buildDomain(domain),
227
- types: receiverConsentTypes,
228
- primaryType: "ReceiverConsent",
229
- message
230
- });
231
- const { v, r, s } = _viem.parseSignature.call(void 0, serialized);
232
- return {
233
- v: Number(v),
234
- r,
235
- s,
236
- serialized
237
- };
238
- }
239
- async function verifyReceiverConsent(domain, message, signature, expectedReceiver) {
240
- const recoveredAddress = await _viem.recoverTypedDataAddress.call(void 0, {
241
- domain: buildDomain(domain),
242
- types: receiverConsentTypes,
243
- primaryType: "ReceiverConsent",
244
- message,
245
- signature
246
- });
247
- const isValid = _viem.getAddress.call(void 0, recoveredAddress) === _viem.getAddress.call(void 0, expectedReceiver);
248
- return { isValid, recoveredAddress };
249
- }
250
-
251
-
252
-
253
-
254
-
255
203
 
256
204
 
257
205
 
@@ -273,5 +221,5 @@ async function verifyReceiverConsent(domain, message, signature, expectedReceive
273
221
 
274
222
 
275
223
 
276
- exports.mintRequestTypes = mintRequestTypes; exports.burnRequestTypes = burnRequestTypes; exports.receiverConsentTypes = receiverConsentTypes; exports.SUPPORTED_CHAINS = SUPPORTED_CHAINS; exports.V4_QUOTER_ADDRESSES = V4_QUOTER_ADDRESSES; exports.UNIVERSAL_ROUTER_ADDRESSES = UNIVERSAL_ROUTER_ADDRESSES; exports.COMMON_TOKENS = COMMON_TOKENS; exports.COMMON_POOLS = COMMON_POOLS; exports.POINT_TOKEN_POOLS = POINT_TOKEN_POOLS; exports.ENTRY_POINT_V07 = ENTRY_POINT_V07; exports.ENTRY_POINT_V08 = ENTRY_POINT_V08; exports.PERMIT2_ADDRESS = PERMIT2_ADDRESS; exports.buildDomain = buildDomain; exports.Eip712DomainMismatchError = Eip712DomainMismatchError; exports.assertDomainMatchesContract = assertDomainMatchesContract; exports.buildMintRequestTypedData = buildMintRequestTypedData; exports.signMintRequest = signMintRequest; exports.verifyMintRequest = verifyMintRequest; exports.buildBurnRequestTypedData = buildBurnRequestTypedData; exports.signBurnRequest = signBurnRequest; exports.verifyBurnRequest = verifyBurnRequest; exports.buildReceiverConsentTypedData = buildReceiverConsentTypedData; exports.signReceiverConsent = signReceiverConsent; exports.verifyReceiverConsent = verifyReceiverConsent;
277
- //# sourceMappingURL=chunk-5NEAI2BH.cjs.map
224
+ exports.mintRequestTypes = mintRequestTypes; exports.burnRequestTypes = burnRequestTypes; exports.SUPPORTED_CHAINS = SUPPORTED_CHAINS; exports.V4_QUOTER_ADDRESSES = V4_QUOTER_ADDRESSES; exports.UNIVERSAL_ROUTER_ADDRESSES = UNIVERSAL_ROUTER_ADDRESSES; exports.COMMON_TOKENS = COMMON_TOKENS; exports.COMMON_POOLS = COMMON_POOLS; exports.POINT_TOKEN_POOLS = POINT_TOKEN_POOLS; exports.ENTRY_POINT_V07 = ENTRY_POINT_V07; exports.ENTRY_POINT_V08 = ENTRY_POINT_V08; exports.PERMIT2_ADDRESS = PERMIT2_ADDRESS; exports.buildDomain = buildDomain; exports.Eip712DomainMismatchError = Eip712DomainMismatchError; exports.assertDomainMatchesContract = assertDomainMatchesContract; exports.buildMintRequestTypedData = buildMintRequestTypedData; exports.signMintRequest = signMintRequest; exports.verifyMintRequest = verifyMintRequest; exports.buildBurnRequestTypedData = buildBurnRequestTypedData; exports.signBurnRequest = signBurnRequest; exports.verifyBurnRequest = verifyBurnRequest;
225
+ //# sourceMappingURL=chunk-4NTU7XGP.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/phitran/Pacific-Finance/pafi-backend/pafi-sdk/packages/core/dist/chunk-4NTU7XGP.cjs","../src/eip712/domain.ts","../src/eip712/mintRequest.ts","../src/constants.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-4NTU7XGP.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} from \"../types\";\nimport { buildDomain } from \"./domain\";\n\nconst PRIMARY_TYPE = \"MintForRequest\" as const;\n\n/**\n * Build the EIP-712 typed data object for a v1.6 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 v1.6 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\nexport async function verifyMintRequest(\n domain: PointTokenDomainConfig,\n message: MintRequest,\n signature: Hex,\n expectedMinter: Address,\n): Promise<SignatureVerification> {\n const recoveredAddress = await recoverTypedDataAddress({\n domain: buildDomain(domain),\n types: mintRequestTypes,\n primaryType: PRIMARY_TYPE,\n message,\n signature,\n });\n\n const isValid = getAddress(recoveredAddress) === getAddress(expectedMinter);\n\n return { isValid, recoveredAddress };\n}\n","import type { Address } 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 v1.6 sig-gated mint path.\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: \"nonce\", type: \"uint256\" },\n { name: \"deadline\", type: \"uint256\" },\n ],\n} as const;\n\nexport const burnRequestTypes = {\n BurnRequest: [\n { name: \"from\", type: \"address\" },\n { name: \"amount\", type: \"uint256\" },\n { name: \"nonce\", type: \"uint256\" },\n { name: \"deadline\", type: \"uint256\" },\n ],\n} as const;\n\n// `receiverConsentTypes` removed in v0.10 — the deployed PointToken\n// contract never had a `ReceiverConsent` path. What was conceptually\n// a \"sponsored\" mint variant is actually implemented at the relayer\n// layer (sponsor-relayer pays gas for the path-2 `MintForRequest`\n// sig-gated mint); no separate EIP-712 type or on-chain nonce mapping\n// is needed.\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\nexport const V4_QUOTER_ADDRESSES: Record<number, Address> = {\n 8453: \"0x0d5e0f971ed27fbff6c2837bf31316121532048d\",\n};\n\nexport const UNIVERSAL_ROUTER_ADDRESSES: Record<number, Address> = {\n 8453: \"0x6ff5693b99212da76ad316178a184ab56d299b43\",\n};\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 — existing Uniswap V4 pools\n 8453: [\n // WETH/USDC 0.3%\n {\n currency0: \"0x4200000000000000000000000000000000000006\",\n currency1: \"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913\",\n fee: 3000,\n tickSpacing: 60,\n hooks: \"0x0000000000000000000000000000000000000000\",\n },\n // WETH/USDC 0.05%\n {\n currency0: \"0x4200000000000000000000000000000000000006\",\n currency1: \"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913\",\n fee: 500,\n tickSpacing: 10,\n hooks: \"0x0000000000000000000000000000000000000000\",\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. */\nexport const PERMIT2_ADDRESS: Address = \"0x000000000022D473030F116dDEE9F6B43aC78BA3\";\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} from \"../types\";\nimport { buildDomain } from \"./domain\";\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\nexport async function verifyBurnRequest(\n domain: PointTokenDomainConfig,\n message: BurnRequest,\n signature: Hex,\n expectedBurner: Address,\n): Promise<SignatureVerification> {\n const recoveredAddress = await recoverTypedDataAddress({\n domain: buildDomain(domain),\n types: burnRequestTypes,\n primaryType: \"BurnRequest\",\n message,\n signature,\n });\n\n const isValid = getAddress(recoveredAddress) === getAddress(expectedBurner);\n\n return { isValid, recoveredAddress };\n}\n"]}
@@ -16,14 +16,6 @@ async function getMintRequestNonce(client, pointToken, receiver) {
16
16
  args: [receiver]
17
17
  });
18
18
  }
19
- async function getReceiverConsentNonce(client, pointToken, receiver) {
20
- return client.readContract({
21
- address: pointToken,
22
- abi: _chunkKRHGFUDIcjs.pointTokenAbi,
23
- functionName: "nonces",
24
- args: [receiver]
25
- });
26
- }
27
19
  async function isMinter(client, pointToken, account) {
28
20
  return client.readContract({
29
21
  address: pointToken,
@@ -176,6 +168,5 @@ async function getMintFeeRecipients(client, wrapperAddress, pointToken) {
176
168
 
177
169
 
178
170
 
179
-
180
- exports.getMintRequestNonce = getMintRequestNonce; exports.getReceiverConsentNonce = getReceiverConsentNonce; exports.isMinter = isMinter; exports.getTokenName = getTokenName; exports.getIssuer = getIssuer; exports.getPointTokenBalance = getPointTokenBalance; exports.getBurnRequestNonce = getBurnRequestNonce; exports.getIssuer2 = getIssuer2; exports.issuerRegistryGetIssuerFlatAbi = issuerRegistryGetIssuerFlatAbi; exports.isActiveIssuer = isActiveIssuer; exports.verifyMintCap = verifyMintCap; exports.getPointTokenIssuer = getPointTokenIssuer; exports.getTokenCap = getTokenCap; exports.getMintFeeBps = getMintFeeBps; exports.getMintFeeRecipients = getMintFeeRecipients;
181
- //# sourceMappingURL=chunk-BNO5SM25.cjs.map
171
+ exports.getMintRequestNonce = getMintRequestNonce; exports.isMinter = isMinter; exports.getTokenName = getTokenName; exports.getIssuer = getIssuer; exports.getPointTokenBalance = getPointTokenBalance; exports.getBurnRequestNonce = getBurnRequestNonce; exports.getIssuer2 = getIssuer2; exports.issuerRegistryGetIssuerFlatAbi = issuerRegistryGetIssuerFlatAbi; exports.isActiveIssuer = isActiveIssuer; exports.verifyMintCap = verifyMintCap; exports.getPointTokenIssuer = getPointTokenIssuer; exports.getTokenCap = getTokenCap; exports.getMintFeeBps = getMintFeeBps; exports.getMintFeeRecipients = getMintFeeRecipients;
172
+ //# sourceMappingURL=chunk-BBQLGBOD.cjs.map