@pafi-dev/issuer 0.5.6 → 0.5.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +69 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +34 -3
- package/dist/index.d.ts +34 -3
- package/dist/index.js +69 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1300,6 +1300,20 @@ var PTRedeemHandler = class {
|
|
|
1300
1300
|
redeemLockDurationMs;
|
|
1301
1301
|
signatureDeadlineSeconds;
|
|
1302
1302
|
now;
|
|
1303
|
+
/**
|
|
1304
|
+
* Per-user in-flight nonce guard (single-process only).
|
|
1305
|
+
*
|
|
1306
|
+
* Prevents two concurrent requests from reading the same on-chain
|
|
1307
|
+
* burnRequestNonce before either has completed, which would produce two
|
|
1308
|
+
* signed UserOps with the same nonce — only one succeeds on-chain; the
|
|
1309
|
+
* other leaves an orphaned pending credit and a wasted signer call.
|
|
1310
|
+
*
|
|
1311
|
+
* NOTE: This guard is effective only within a single Node.js process. For
|
|
1312
|
+
* multi-instance deployments (k8s, PM2 cluster), enforce mutual exclusion
|
|
1313
|
+
* via a distributed lock (Redis SETNX / Postgres advisory lock) keyed on
|
|
1314
|
+
* `(userAddress, pointTokenAddress)` BEFORE calling `handle()`.
|
|
1315
|
+
*/
|
|
1316
|
+
inFlightNonces = /* @__PURE__ */ new Map();
|
|
1303
1317
|
constructor(config) {
|
|
1304
1318
|
if (!config.ledger.reservePendingCredit) {
|
|
1305
1319
|
throw new PTRedeemError(
|
|
@@ -1352,6 +1366,27 @@ var PTRedeemHandler = class {
|
|
|
1352
1366
|
`failed to read burnRequestNonces(${request.userAddress}): ${err instanceof Error ? err.message : String(err)}`
|
|
1353
1367
|
);
|
|
1354
1368
|
}
|
|
1369
|
+
const userKey = (0, import_viem7.getAddress)(request.userAddress).toLowerCase();
|
|
1370
|
+
let userNonces = this.inFlightNonces.get(userKey);
|
|
1371
|
+
if (!userNonces) {
|
|
1372
|
+
userNonces = /* @__PURE__ */ new Set();
|
|
1373
|
+
this.inFlightNonces.set(userKey, userNonces);
|
|
1374
|
+
}
|
|
1375
|
+
if (userNonces.has(burnNonce)) {
|
|
1376
|
+
throw new PTRedeemError(
|
|
1377
|
+
"NONCE_IN_FLIGHT",
|
|
1378
|
+
`A burn request for nonce ${burnNonce} is already in progress for ${request.userAddress}. Retry after the current request completes.`
|
|
1379
|
+
);
|
|
1380
|
+
}
|
|
1381
|
+
userNonces.add(burnNonce);
|
|
1382
|
+
try {
|
|
1383
|
+
return await this._handleAfterNonceLock(request, burnNonce);
|
|
1384
|
+
} finally {
|
|
1385
|
+
userNonces.delete(burnNonce);
|
|
1386
|
+
if (userNonces.size === 0) this.inFlightNonces.delete(userKey);
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
async _handleAfterNonceLock(request, burnNonce) {
|
|
1355
1390
|
const onChainBalance = await (0, import_core4.getPointTokenBalance)(
|
|
1356
1391
|
this.provider,
|
|
1357
1392
|
this.pointTokenAddress,
|
|
@@ -1858,6 +1893,40 @@ var PafiBackendClient = class {
|
|
|
1858
1893
|
}
|
|
1859
1894
|
throw lastError;
|
|
1860
1895
|
}
|
|
1896
|
+
async relayUserOperation(request) {
|
|
1897
|
+
const fetchFn = this.config.fetchImpl ?? fetch;
|
|
1898
|
+
const url = `${this.config.url}/bundler/relay`;
|
|
1899
|
+
let response;
|
|
1900
|
+
try {
|
|
1901
|
+
response = await fetchFn(url, {
|
|
1902
|
+
method: "POST",
|
|
1903
|
+
headers: {
|
|
1904
|
+
"Content-Type": "application/json",
|
|
1905
|
+
Authorization: `Bearer ${this.config.apiKey}`,
|
|
1906
|
+
"X-Issuer-Id": this.config.issuerId
|
|
1907
|
+
},
|
|
1908
|
+
body: JSON.stringify(request)
|
|
1909
|
+
});
|
|
1910
|
+
} catch (err) {
|
|
1911
|
+
throw new PafiBackendError(
|
|
1912
|
+
"NETWORK_ERROR",
|
|
1913
|
+
`Network error: ${err instanceof Error ? err.message : String(err)}`,
|
|
1914
|
+
0
|
|
1915
|
+
);
|
|
1916
|
+
}
|
|
1917
|
+
const text = await response.text();
|
|
1918
|
+
let json = {};
|
|
1919
|
+
try {
|
|
1920
|
+
json = JSON.parse(text);
|
|
1921
|
+
} catch {
|
|
1922
|
+
}
|
|
1923
|
+
if (!response.ok) {
|
|
1924
|
+
const code = json.code ?? "INTERNAL_ERROR";
|
|
1925
|
+
const message = json.message ?? `HTTP ${response.status}`;
|
|
1926
|
+
throw new PafiBackendError(code, message, response.status, json);
|
|
1927
|
+
}
|
|
1928
|
+
return { userOpHash: json.userOpHash };
|
|
1929
|
+
}
|
|
1861
1930
|
async _doRequest(request) {
|
|
1862
1931
|
const fetchFn = this.config.fetchImpl ?? fetch;
|
|
1863
1932
|
const url = `${this.config.url}/paymaster/sponsor`;
|