@pafi-dev/issuer 0.12.6 → 0.13.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 +211 -94
- package/dist/index.cjs +2 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +15 -8
- package/dist/index.d.ts +15 -8
- package/dist/index.js +2 -6
- package/dist/index.js.map +1 -1
- package/package.json +11 -2
package/README.md
CHANGED
|
@@ -4,26 +4,51 @@
|
|
|
4
4
|
[](https://opensource.org/licenses/Apache-2.0)
|
|
5
5
|
|
|
6
6
|
Backend SDK for PAFI issuer servers — claim, redeem, perp deposit,
|
|
7
|
-
mobile prepare/submit, EIP-7702 delegation, status polling,
|
|
8
|
-
`IssuerApiAdapter` that thins issuer
|
|
9
|
-
endpoint.
|
|
7
|
+
mobile prepare/submit, EIP-7702 delegation, status polling, redemption
|
|
8
|
+
restriction enforcement, and the `IssuerApiAdapter` that thins issuer
|
|
9
|
+
controllers to one line per endpoint.
|
|
10
10
|
|
|
11
11
|
**Server-only.** Pulls in signer wallets, HTTP clients, ledger
|
|
12
12
|
interfaces. Don't bundle into a browser app — use
|
|
13
|
-
`@pafi-dev/trading` (FE swap/quote) or `@pafi-dev/core` (primitives)
|
|
14
|
-
instead.
|
|
13
|
+
`@pafi-dev/trading` (FE swap/quote) or `@pafi-dev/core` (primitives).
|
|
15
14
|
|
|
16
15
|
---
|
|
17
16
|
|
|
18
|
-
##
|
|
17
|
+
## What's new in 0.13.0 (breaking)
|
|
19
18
|
|
|
20
|
-
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
-
|
|
19
|
+
Tracks `@pafi-dev/core@0.10.0`. The `ReceiverConsent` mint path was
|
|
20
|
+
removed from the entire SDK chain because the deployed `PointToken`
|
|
21
|
+
contract never had it — what was conceptually a "sponsored mint" is
|
|
22
|
+
actually the path-2 `MintForRequest` sig-gated mint with sponsor-relayer
|
|
23
|
+
paying gas.
|
|
24
|
+
|
|
25
|
+
| Change | Detail |
|
|
26
|
+
|---|---|
|
|
27
|
+
| `ApiUserResponse.receiverConsentNonce` removed | The `/user` endpoint no longer returns this field. Callers using it for signing should call the path-2 mint flow with `mintRequestNonce` instead. |
|
|
28
|
+
| `UserDto.receiverConsentNonce` removed | Wire DTO drops the field — `IssuerApiAdapter.user()` response is one field smaller. |
|
|
29
|
+
| Action required for issuer backends | Bump `@pafi-dev/core` to `0.10.0`, remove any `receiverConsentNonce` field from your own DTOs / mobile API surface, drop FE code that signed `ReceiverConsent` typed data. |
|
|
30
|
+
|
|
31
|
+
### What's new in v1.6 / 0.12.x
|
|
32
|
+
|
|
33
|
+
| Change | Detail |
|
|
34
|
+
|---|---|
|
|
35
|
+
| `RelayService.prepareMint` branches direct vs wrapper | Auto-detects `MintFeeWrapper` from chainId. Direct: `pointToken.mint(...)`. Wrapper: `mintFeeWrapper.mintWithFee(...)` |
|
|
36
|
+
| `PTClaimHandler` auto-resolves wrapper | No env config needed — `getContractAddresses(chainId).mintFeeWrapper`. Override via `mintFeeWrapperAddress` for fork tests |
|
|
37
|
+
| `PointIndexer` wrapper mode | Listens to `MintFeeWrapper.MintWithFee(pointToken, to, gross, net, fee)` instead of `Transfer(0x0→user)` when wrapper is configured |
|
|
38
|
+
| `IssuerStateValidator` v1.6 struct | Reads 7-field Issuer struct + queries `MintingOracle.tokenCaps()` for caps |
|
|
39
|
+
| `/config` exposes `mintFeeBpsByToken` | FE can preview "you'll receive net = gross × (1-bps/10000)" before user signs |
|
|
40
|
+
| Operator fee fallback enabled by default | Mint not blocked when subgraph has no pool yet |
|
|
41
|
+
| `PafiBackendClient` error envelope fix | Reads nested `error.message` — exposes Pimlico AA codes instead of generic "HTTP 500" |
|
|
24
42
|
|
|
25
43
|
---
|
|
26
44
|
|
|
45
|
+
## Requirements
|
|
46
|
+
|
|
47
|
+
- Node.js ≥ 18
|
|
48
|
+
- TypeScript ≥ 5.0
|
|
49
|
+
- `viem` ^2.0.0 (peer)
|
|
50
|
+
- `@pafi-dev/core` (transitive — re-exported)
|
|
51
|
+
|
|
27
52
|
## Installation
|
|
28
53
|
|
|
29
54
|
```bash
|
|
@@ -38,11 +63,9 @@ pnpm add @pafi-dev/issuer-postgres typeorm pg
|
|
|
38
63
|
|
|
39
64
|
```
|
|
40
65
|
@pafi-dev/issuer
|
|
41
|
-
├── handlers —
|
|
42
|
-
│ PerpDepositHandler)
|
|
66
|
+
├── handlers — PTClaimHandler, PTRedeemHandler, PerpDepositHandler
|
|
43
67
|
├── api/IssuerApiAdapter
|
|
44
|
-
│ — single class, every endpoint is one line in
|
|
45
|
-
│ your controller
|
|
68
|
+
│ — single class, every endpoint is one line in controller
|
|
46
69
|
├── api/handleMobilePrepare/Submit
|
|
47
70
|
│ — mobile claim/redeem orchestrators
|
|
48
71
|
├── api/handleClaimStatus/handleRedeemStatus
|
|
@@ -55,20 +78,16 @@ pnpm add @pafi-dev/issuer-postgres typeorm pg
|
|
|
55
78
|
├── userop-store — IPendingUserOpStore + MemoryPendingUserOpStore
|
|
56
79
|
├── pafi-backend — sponsor-relayer client + relay/paymaster helpers
|
|
57
80
|
├── auth — ISessionStore, AuthService (SIWE), MemorySessionStore
|
|
58
|
-
├── relay — RelayService, FeeManager
|
|
81
|
+
├── relay — RelayService (v1.6 wrapper-aware), FeeManager
|
|
59
82
|
├── pools — createSubgraphPoolsProvider, NativePtQuoter
|
|
60
83
|
├── policy — IPolicyEngine + DefaultPolicyEngine
|
|
61
84
|
├── balance — BalanceAggregator (off-chain + on-chain)
|
|
62
|
-
├── indexer — PointIndexer
|
|
63
|
-
├── issuer-state — IssuerStateValidator (
|
|
85
|
+
├── indexer — PointIndexer (wrapper + direct mode), BurnIndexer
|
|
86
|
+
├── issuer-state — IssuerStateValidator (v1.6 struct + oracle.tokenCaps)
|
|
87
|
+
├── redemption — RedemptionService (per-issuer policy enforcement)
|
|
64
88
|
└── errors — PafiSdkError base class for typed errors
|
|
65
89
|
```
|
|
66
90
|
|
|
67
|
-
> **Removed in 0.6.0** (2026-04-27): `SwapHandler`, `quotePointTokenToUsdt`,
|
|
68
|
-
> and `IssuerApiAdapter.swap()/quote()` — moved to `@pafi-dev/trading`.
|
|
69
|
-
> FE PAFI calls trading directly. `IssuerApiHandlers.handleClaim/handleRedeem`
|
|
70
|
-
> legacy methods + `TopUpRedemptionHandler` (Variant B) also dropped.
|
|
71
|
-
|
|
72
91
|
---
|
|
73
92
|
|
|
74
93
|
## Architecture
|
|
@@ -80,23 +99,63 @@ Auth guard ← issuer-specific (SIWE / Privy / NextAuth)
|
|
|
80
99
|
↓
|
|
81
100
|
Controller ← thin: routing + DTO + auth context
|
|
82
101
|
↓
|
|
83
|
-
IssuerApiAdapter ←
|
|
102
|
+
IssuerApiAdapter ← orchestrates flows
|
|
84
103
|
↓
|
|
85
|
-
PTClaimHandler ← signs
|
|
104
|
+
PTClaimHandler ← signs MintForRequest, locks balance, builds UserOp
|
|
105
|
+
(v1.6: routes to wrapper.mintWithFee)
|
|
86
106
|
PTRedeemHandler ← signs BurnRequest, reserves credit, builds UserOp
|
|
87
107
|
PerpDepositHandler ← Orderly Vault via PAFI Relay
|
|
88
108
|
↓
|
|
89
|
-
|
|
90
|
-
RelayService ← UserOp builders
|
|
109
|
+
IPointLedger ← your DB impl (Postgres recommended)
|
|
110
|
+
RelayService ← UserOp builders (auto-branches direct vs wrapper)
|
|
91
111
|
PafiBackendClient ← sponsor-relayer proxy
|
|
92
112
|
```
|
|
93
113
|
|
|
94
114
|
---
|
|
95
115
|
|
|
116
|
+
## v1.6 wrapper-mediated mint flow
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
[FE / mobile] → POST /claim/prepare
|
|
120
|
+
↓
|
|
121
|
+
[gg56] IssuerApiAdapter.claimPrepare()
|
|
122
|
+
↓
|
|
123
|
+
[gg56] PTClaimHandler:
|
|
124
|
+
1. Lock off-chain balance (gross amount)
|
|
125
|
+
2. Pre-validate via IssuerStateValidator (oracle cap check)
|
|
126
|
+
3. Sign MintForRequest EIP-712 with receiver = wrapper
|
|
127
|
+
4. Build UserOp callData:
|
|
128
|
+
BatchExecute([
|
|
129
|
+
{ mintFeeWrapper, mintWithFee(pt, user, gross, deadline, sig) },
|
|
130
|
+
{ pointToken, transfer(pafiFeeRecipient, operatorFeePT) }
|
|
131
|
+
])
|
|
132
|
+
↓
|
|
133
|
+
[gg56] PafiBackendClient.requestSponsorship() → sponsor-relayer
|
|
134
|
+
↓
|
|
135
|
+
[sponsor-relayer] decode calldata, recognize MINT_WITH_FEE (0x5284d08b),
|
|
136
|
+
validate intent, forward to Pimlico paymaster
|
|
137
|
+
↓
|
|
138
|
+
[gg56] returns { userOp, typedData, userOpHash, sponsored: true|false,
|
|
139
|
+
typedDataFallback, userOpHashFallback }
|
|
140
|
+
↓
|
|
141
|
+
[FE / mobile] sign typedData via signTypedData_v4
|
|
142
|
+
↓
|
|
143
|
+
[FE / mobile] POST /claim/submit { lockId, signature, variant }
|
|
144
|
+
↓
|
|
145
|
+
[gg56] forwards signed UserOp to Pimlico bundler → on-chain mint
|
|
146
|
+
↓
|
|
147
|
+
[chain] wrapper.mintWithFee → PointToken.mint(gross to wrapper) →
|
|
148
|
+
wrapper splits fee to recipients → transfers net to user
|
|
149
|
+
↓
|
|
150
|
+
[gg56] PointIndexer (wrapper mode) catches MintWithFee event →
|
|
151
|
+
deduct off-chain balance, lock → MINTED
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
96
156
|
## Quick start (NestJS)
|
|
97
157
|
|
|
98
|
-
Minimal reference at `examples/nestjs-issuer/` — full working backend
|
|
99
|
-
with all 11 endpoints, ~940 LoC total.
|
|
158
|
+
Minimal reference at `examples/nestjs-issuer/` — full working backend.
|
|
100
159
|
|
|
101
160
|
### 1. Wire `IssuerApiAdapter`
|
|
102
161
|
|
|
@@ -109,7 +168,6 @@ import {
|
|
|
109
168
|
PTRedeemHandler,
|
|
110
169
|
PerpDepositHandler,
|
|
111
170
|
MemoryPendingUserOpStore,
|
|
112
|
-
createSubgraphPoolsProvider,
|
|
113
171
|
type IssuerService,
|
|
114
172
|
} from "@pafi-dev/issuer";
|
|
115
173
|
import { PostgresPointLedger } from "@pafi-dev/issuer-postgres";
|
|
@@ -117,41 +175,48 @@ import { getContractAddresses } from "@pafi-dev/core";
|
|
|
117
175
|
|
|
118
176
|
export const issuerApiAdapterProvider: Provider = {
|
|
119
177
|
provide: ISSUER_API_ADAPTER,
|
|
120
|
-
useFactory: (issuerService, provider, walletClient, dataSource) => {
|
|
178
|
+
useFactory: (issuerService, provider, walletClient, dataSource, config) => {
|
|
121
179
|
const ledger = new PostgresPointLedger(dataSource);
|
|
122
180
|
const { issuerRegistry, batchExecutor } = getContractAddresses(8453);
|
|
181
|
+
const chainId = config.get<number>("CHAIN_ID");
|
|
182
|
+
const pointToken = config.get<`0x${string}`>("POINT_TOKEN_ADDRESS");
|
|
123
183
|
|
|
124
184
|
return new IssuerApiAdapter({
|
|
125
185
|
issuerService,
|
|
126
186
|
ledger,
|
|
127
187
|
provider,
|
|
128
188
|
issuerSignerWallet: walletClient,
|
|
129
|
-
pafiIssuerId:
|
|
189
|
+
pafiIssuerId: config.get("PAFI_ISSUER_ID"),
|
|
130
190
|
|
|
131
191
|
ptClaimHandler: new PTClaimHandler({
|
|
132
|
-
ledger,
|
|
192
|
+
ledger,
|
|
193
|
+
relayService: issuerService.relay,
|
|
194
|
+
provider,
|
|
133
195
|
issuerSignerWallet: walletClient,
|
|
134
|
-
pointTokenDomainName: "
|
|
196
|
+
pointTokenDomainName: config.get("POINT_TOKEN_DOMAIN_NAME"),
|
|
135
197
|
feeService: issuerService.fee,
|
|
136
198
|
issuerStateValidator: new IssuerStateValidator(provider, issuerRegistry),
|
|
199
|
+
// mintFeeWrapperAddress: optional override; SDK auto-resolves from chainId
|
|
137
200
|
}),
|
|
138
201
|
|
|
139
202
|
ptRedeemHandler: new PTRedeemHandler({
|
|
140
|
-
ledger,
|
|
141
|
-
|
|
203
|
+
ledger,
|
|
204
|
+
relayService: issuerService.relay,
|
|
205
|
+
provider,
|
|
206
|
+
pointTokenAddress: pointToken,
|
|
142
207
|
batchExecutorAddress: batchExecutor,
|
|
143
|
-
chainId
|
|
144
|
-
domain: { name: "
|
|
208
|
+
chainId,
|
|
209
|
+
domain: { name: config.get("POINT_TOKEN_DOMAIN_NAME"), verifyingContract: pointToken },
|
|
145
210
|
burnerSignerWallet: walletClient,
|
|
146
211
|
feeService: issuerService.fee,
|
|
147
212
|
}),
|
|
148
213
|
|
|
149
214
|
perpHandler: new PerpDepositHandler({
|
|
150
|
-
provider, feeService: issuerService.fee, pointTokenAddress:
|
|
215
|
+
provider, feeService: issuerService.fee, pointTokenAddress: pointToken,
|
|
151
216
|
}),
|
|
152
217
|
|
|
153
218
|
pendingUserOpStore: new MemoryPendingUserOpStore(),
|
|
154
|
-
pafiBackendClient, // optional —
|
|
219
|
+
pafiBackendClient, // optional — sponsor-relayer client for sponsored flows
|
|
155
220
|
});
|
|
156
221
|
},
|
|
157
222
|
inject: [...],
|
|
@@ -183,7 +248,6 @@ export class IssuerController {
|
|
|
183
248
|
aaNonce, mintRequestNonce,
|
|
184
249
|
}));
|
|
185
250
|
}
|
|
186
|
-
// ... other endpoints similarly thin
|
|
187
251
|
}
|
|
188
252
|
```
|
|
189
253
|
|
|
@@ -191,16 +255,12 @@ export class IssuerController {
|
|
|
191
255
|
|
|
192
256
|
```ts
|
|
193
257
|
import { createSdkErrorMapper, type SdkErrorBody } from "@pafi-dev/issuer";
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
notFound: (b: SdkErrorBody) => new NotFoundException(b),
|
|
201
|
-
forbidden: (b: SdkErrorBody) => new ForbiddenException(b),
|
|
202
|
-
unprocessable: (b: SdkErrorBody) => new UnprocessableEntityException(b),
|
|
203
|
-
serviceUnavailable: (b: SdkErrorBody) => new ServiceUnavailableException(b),
|
|
258
|
+
|
|
259
|
+
const sdkErrorMapper = createSdkErrorMapper({
|
|
260
|
+
notFound: (b) => new NotFoundException(b),
|
|
261
|
+
forbidden: (b) => new ForbiddenException(b),
|
|
262
|
+
unprocessable: (b) => new UnprocessableEntityException(b),
|
|
263
|
+
serviceUnavailable: (b) => new ServiceUnavailableException(b),
|
|
204
264
|
});
|
|
205
265
|
|
|
206
266
|
async function wrap<T>(fn: () => Promise<T>): Promise<T> {
|
|
@@ -209,91 +269,148 @@ async function wrap<T>(fn: () => Promise<T>): Promise<T> {
|
|
|
209
269
|
}
|
|
210
270
|
```
|
|
211
271
|
|
|
212
|
-
Every typed SDK error (`PafiSdkError` subclass) routes through this
|
|
213
|
-
funnel — `code`, `safeToRetry`, `details`, `httpStatus` come straight
|
|
214
|
-
off the error class. No magic strings, no per-error mapping.
|
|
215
|
-
|
|
216
272
|
---
|
|
217
273
|
|
|
218
274
|
## `IssuerApiAdapter` methods
|
|
219
275
|
|
|
220
|
-
| Method | HTTP route
|
|
221
|
-
| --- | --- |
|
|
222
|
-
| `pools(authedAddr, chainId, pointToken)` | `GET /pools` |
|
|
223
|
-
| `user(authedAddr, chainId, userAddr, pointToken)` | `GET /user` |
|
|
224
|
-
| `
|
|
225
|
-
| `
|
|
226
|
-
| `
|
|
227
|
-
| `
|
|
228
|
-
| `
|
|
229
|
-
| `
|
|
230
|
-
| `
|
|
231
|
-
| `
|
|
232
|
-
| `
|
|
233
|
-
|
|
234
|
-
> `swap()` and `quote()` removed in 0.6.0 — see `@pafi-dev/trading`.
|
|
235
|
-
> `config()` and `gasFee()` are optional read endpoints; FE can call
|
|
236
|
-
> `getContractAddresses()` and `quoteOperatorFeeUsdt()` from
|
|
237
|
-
> `@pafi-dev/core` directly with no backend round-trip.
|
|
276
|
+
| Method | HTTP route | Notes |
|
|
277
|
+
| --- | --- | --- |
|
|
278
|
+
| `pools(authedAddr, chainId, pointToken)` | `GET /pools` | |
|
|
279
|
+
| `user(authedAddr, chainId, userAddr, pointToken)` | `GET /user` | |
|
|
280
|
+
| `config(chainId)` | `GET /config` | v1.6: includes `mintFeeBpsByToken` + `contracts.mintFeeWrapper` |
|
|
281
|
+
| `claim({ ...nonces })` | `POST /claim` (web — sync `calls[]`) | |
|
|
282
|
+
| `redeem({ amount, aaNonce, ... })` | `POST /redeem` | |
|
|
283
|
+
| `perpDeposit({ amount, brokerId, aaNonce, ... })` | `POST /perp-deposit` | |
|
|
284
|
+
| `claimPrepare(...)` / `claimSubmit(...)` | Mobile claim flow | v1.6 wrapper-aware |
|
|
285
|
+
| `redeemPrepare(...)` / `redeemSubmit(...)` | Mobile redeem flow | |
|
|
286
|
+
| `claimStatus(authedAddr, lockId)` / `redeemStatus(...)` | Status polling | |
|
|
287
|
+
| `delegateStatus(authedAddr, chainId)` | EIP-7702 — check delegation | |
|
|
288
|
+
| `delegatePrepare(authedAddr, chainId)` | EIP-7702 — build auth hash | |
|
|
289
|
+
| `delegateSubmit({ authSig, ... })` | EIP-7702 — relay empty-batch | |
|
|
238
290
|
|
|
239
291
|
---
|
|
240
292
|
|
|
241
|
-
##
|
|
293
|
+
## Redemption restriction (v0.10+)
|
|
242
294
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
the orchestration:
|
|
295
|
+
Per-issuer policy enforced at `/redeem/prepare` time. Fetched from
|
|
296
|
+
PAFI issuer-api with 5-min cache + fail-open default.
|
|
246
297
|
|
|
247
298
|
```ts
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
299
|
+
import { RedemptionService, PolicyProvider } from "@pafi-dev/issuer";
|
|
300
|
+
import { PostgresRedemptionHistoryStore } from "@pafi-dev/issuer-postgres";
|
|
301
|
+
|
|
302
|
+
const redemption = new RedemptionService({
|
|
303
|
+
policyProvider: new PolicyProvider({
|
|
304
|
+
chainId: 8453,
|
|
305
|
+
issuerId: "gg56",
|
|
306
|
+
apiKey: process.env.PAFI_API_KEY,
|
|
307
|
+
}),
|
|
308
|
+
historyStore: new PostgresRedemptionHistoryStore(dataSource),
|
|
309
|
+
});
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
Wire to `createIssuerService({ redemption: {...} })`. Adapter exposes
|
|
313
|
+
`/redemption/preview` + `/redemption/evaluate` automatically.
|
|
251
314
|
|
|
252
|
-
|
|
315
|
+
---
|
|
316
|
+
|
|
317
|
+
## Mobile prepare/submit flow
|
|
253
318
|
|
|
254
|
-
|
|
255
|
-
|
|
319
|
+
```ts
|
|
320
|
+
// 1. mobile → POST /claim/prepare → backend runs:
|
|
321
|
+
const prepared = await api.claimPrepare({
|
|
322
|
+
authenticatedAddress, chainId, pointTokenAddress, amount, aaNonce, mintRequestNonce,
|
|
323
|
+
});
|
|
324
|
+
// returns: {
|
|
325
|
+
// lockId, userOpHash, typedData,
|
|
326
|
+
// userOpHashFallback, typedDataFallback,
|
|
327
|
+
// sponsored, // true if paymaster signed; false → use fallback
|
|
328
|
+
// needsDelegation, // true if user EOA not delegated yet
|
|
329
|
+
// feeAmount, signatureDeadline, expiresInSeconds,
|
|
330
|
+
// }
|
|
331
|
+
|
|
332
|
+
// 2. mobile signs typedData via Privy/MetaMask signTypedData_v4
|
|
333
|
+
// (NOT personal_sign — Pimlico Simple7702Account does raw ecrecover)
|
|
334
|
+
// If `sponsored: false`, sign `typedDataFallback` instead
|
|
335
|
+
|
|
336
|
+
// 3. mobile → POST /claim/submit:
|
|
337
|
+
const result = await api.claimSubmit({
|
|
338
|
+
authenticatedAddress, lockId, signature,
|
|
339
|
+
variant: prepared.sponsored ? "sponsored" : "fallback",
|
|
340
|
+
});
|
|
256
341
|
// returns: { userOpHash } — bundler hash for status polling
|
|
257
342
|
```
|
|
258
343
|
|
|
259
344
|
`handleMobileSubmit` enforces ownership: `entry.sender ===
|
|
260
|
-
authenticatedAddress`.
|
|
261
|
-
pending UserOp once they leak/guess the lockId.
|
|
345
|
+
authenticatedAddress`. Lock has 15-min TTL.
|
|
262
346
|
|
|
263
347
|
---
|
|
264
348
|
|
|
265
|
-
##
|
|
349
|
+
## PointIndexer modes
|
|
266
350
|
|
|
267
351
|
```ts
|
|
268
|
-
//
|
|
269
|
-
const
|
|
270
|
-
|
|
352
|
+
// Default — auto-resolves wrapper from chainId
|
|
353
|
+
const service = createIssuerService({
|
|
354
|
+
chainId: 8453,
|
|
355
|
+
// ...
|
|
356
|
+
indexer: { autoStart: true, pollIntervalMs: 5000 },
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
// Indexer listens to:
|
|
360
|
+
// - mode wrapper: MintFeeWrapper.MintWithFee filtered by pointToken
|
|
361
|
+
// (when wrapper is configured at chainId — v1.6 default)
|
|
362
|
+
// - mode direct: PointToken.Transfer(0x0 → user) (legacy / no wrapper)
|
|
271
363
|
```
|
|
272
364
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
365
|
+
Override for fork tests:
|
|
366
|
+
```ts
|
|
367
|
+
indexer: { mintFeeWrapperAddress: "0x000...dead" } // force direct mode
|
|
368
|
+
```
|
|
277
369
|
|
|
278
370
|
---
|
|
279
371
|
|
|
280
372
|
## Error classes
|
|
281
373
|
|
|
282
|
-
All SDK errors inherit `PafiSdkError`. Subclasses +
|
|
374
|
+
All SDK errors inherit `PafiSdkError`. Subclasses + HTTP mapping:
|
|
283
375
|
|
|
284
376
|
| Error | `httpStatus` | `safeToRetry` |
|
|
285
377
|
| --- | --- | --- |
|
|
286
378
|
| `PendingUserOpNotFoundError` | `not_found` | false |
|
|
287
379
|
| `PendingUserOpForbiddenError` | `forbidden` | false |
|
|
288
380
|
| `LockNotFoundError` | `not_found` | false |
|
|
289
|
-
| `IssuerStateError` | `unprocessable` | true if
|
|
381
|
+
| `IssuerStateError` (`ISSUER_NOT_REGISTERED` / `ISSUER_INACTIVE` / `MINT_CAP_EXCEEDED`) | `unprocessable` | true if cap exceeded |
|
|
290
382
|
| `PerpDepositError` | `unprocessable` | true if `RELAY_FEE_EXCEEDS_AMOUNT` |
|
|
291
383
|
| `PTClaimError`, `PTRedeemError` | `unprocessable` | false |
|
|
292
384
|
| `BundlerNotConfiguredError` | `service_unavailable` | false |
|
|
293
385
|
| `BundlerRejectedError` | `unprocessable` | false |
|
|
386
|
+
| `RedemptionPolicyError` (`REDEMPTION_DENIED` / `RATE_LIMIT_EXCEEDED` / ...) | `unprocessable` | varies |
|
|
387
|
+
|
|
388
|
+
---
|
|
389
|
+
|
|
390
|
+
## v1.5 → v1.6 migration checklist
|
|
391
|
+
|
|
392
|
+
1. `pnpm add @pafi-dev/issuer@latest @pafi-dev/core@latest` (≥ 0.12.7 + ≥ 0.9.6)
|
|
393
|
+
2. Update env: drop `MINT_FEE_WRAPPER_ADDRESS` if previously set
|
|
394
|
+
(SDK auto-resolves from chainId)
|
|
395
|
+
3. Re-deploy your issuer backend — `RelayService` now branches based on
|
|
396
|
+
wrapper presence; calldata changes from `mint(...)` to `mintWithFee(...)`
|
|
397
|
+
4. Verify on-chain prerequisites (PAFI ops responsibility):
|
|
398
|
+
- `IssuerRegistry.addIssuer(...)` cascade configures wrapper recipients
|
|
399
|
+
- `MintingOracle.registerToken(pointToken, issuer)` sets cap
|
|
400
|
+
- `PointToken.addMinter(signerAddress)` whitelists your signer
|
|
401
|
+
5. Indexer will pick up `MintWithFee` events automatically. If you wrote
|
|
402
|
+
custom Transfer-event consumers, switch to `MintWithFee`.
|
|
403
|
+
6. `/config` response now includes `mintFeeBpsByToken` — FE can preview
|
|
404
|
+
fee without round-tripping `getMintFeeBps()` separately.
|
|
294
405
|
|
|
295
406
|
---
|
|
296
407
|
|
|
408
|
+
## References
|
|
409
|
+
|
|
410
|
+
- Architecture: [`ARCHITECTURE.md`](../../ARCHITECTURE.md) at SDK root
|
|
411
|
+
- Fee flow & math: [`docs/FEE_FLOW.md`](../../../docs/FEE_FLOW.md)
|
|
412
|
+
- v1.6 SC commits: `cc26f62` (struct simplification), wrapper added
|
|
413
|
+
|
|
297
414
|
## License
|
|
298
415
|
|
|
299
416
|
Apache-2.0
|
package/dist/index.cjs
CHANGED
|
@@ -1846,16 +1846,14 @@ var IssuerApiHandlers = class _IssuerApiHandlers {
|
|
|
1846
1846
|
{ requested: pointToken }
|
|
1847
1847
|
);
|
|
1848
1848
|
}
|
|
1849
|
-
const [mintRequestNonce,
|
|
1849
|
+
const [mintRequestNonce, offChainBalance, onChainBalance, minter] = await Promise.all([
|
|
1850
1850
|
(0, import_core6.getMintRequestNonce)(this.provider, pointToken, normalizedAuthed),
|
|
1851
|
-
(0, import_core6.getReceiverConsentNonce)(this.provider, pointToken, normalizedAuthed),
|
|
1852
1851
|
this.ledger.getBalance(normalizedAuthed, pointToken),
|
|
1853
1852
|
(0, import_core6.getPointTokenBalance)(this.provider, pointToken, normalizedAuthed),
|
|
1854
1853
|
(0, import_core6.isMinter)(this.provider, pointToken, normalizedAuthed)
|
|
1855
1854
|
]);
|
|
1856
1855
|
return {
|
|
1857
1856
|
mintRequestNonce,
|
|
1858
|
-
receiverConsentNonce,
|
|
1859
1857
|
offChainBalance,
|
|
1860
1858
|
onChainBalance,
|
|
1861
1859
|
totalBalance: offChainBalance + onChainBalance,
|
|
@@ -3151,7 +3149,6 @@ var IssuerApiAdapter = class {
|
|
|
3151
3149
|
);
|
|
3152
3150
|
return {
|
|
3153
3151
|
mintRequestNonce: result.mintRequestNonce.toString(),
|
|
3154
|
-
receiverConsentNonce: result.receiverConsentNonce.toString(),
|
|
3155
3152
|
offChainBalance: result.offChainBalance.toString(),
|
|
3156
3153
|
onChainBalance: result.onChainBalance.toString(),
|
|
3157
3154
|
totalBalance: result.totalBalance.toString(),
|
|
@@ -4739,7 +4736,7 @@ var MemoryRedemptionHistoryStore = class {
|
|
|
4739
4736
|
};
|
|
4740
4737
|
|
|
4741
4738
|
// src/index.ts
|
|
4742
|
-
var PAFI_ISSUER_SDK_VERSION = true ? "0.
|
|
4739
|
+
var PAFI_ISSUER_SDK_VERSION = true ? "0.13.0" : "dev";
|
|
4743
4740
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4744
4741
|
0 && (module.exports = {
|
|
4745
4742
|
AdapterMisconfiguredError,
|