@pafi-dev/issuer 0.6.1 → 0.7.1
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 +316 -167
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +78 -13
- package/dist/index.d.ts +78 -13
- package/dist/index.js +315 -168
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -13,6 +13,26 @@ var PafiSdkError = class extends Error {
|
|
|
13
13
|
this.name = new.target.name;
|
|
14
14
|
}
|
|
15
15
|
};
|
|
16
|
+
var ValidationError = class extends PafiSdkError {
|
|
17
|
+
httpStatus = "unprocessable";
|
|
18
|
+
code;
|
|
19
|
+
details;
|
|
20
|
+
constructor(code, message, details) {
|
|
21
|
+
super(message);
|
|
22
|
+
this.code = code;
|
|
23
|
+
this.details = details;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
var ConfigurationError = class extends PafiSdkError {
|
|
27
|
+
httpStatus = "service_unavailable";
|
|
28
|
+
code;
|
|
29
|
+
details;
|
|
30
|
+
constructor(code, message, details) {
|
|
31
|
+
super(message);
|
|
32
|
+
this.code = code;
|
|
33
|
+
this.details = details;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
16
36
|
|
|
17
37
|
// src/policy/defaultPolicy.ts
|
|
18
38
|
var DefaultPolicyEngine = class {
|
|
@@ -183,12 +203,32 @@ import { getAddress as getAddress2 } from "viem";
|
|
|
183
203
|
import { parseLoginMessage, verifyLoginMessage } from "@pafi-dev/core";
|
|
184
204
|
|
|
185
205
|
// src/auth/errors.ts
|
|
186
|
-
|
|
206
|
+
function statusForCode(code) {
|
|
207
|
+
switch (code) {
|
|
208
|
+
case "INVALID_MESSAGE":
|
|
209
|
+
case "DOMAIN_MISMATCH":
|
|
210
|
+
case "CHAIN_MISMATCH":
|
|
211
|
+
case "MESSAGE_EXPIRED":
|
|
212
|
+
case "MESSAGE_NOT_YET_VALID":
|
|
213
|
+
case "MALFORMED_TOKEN":
|
|
214
|
+
return "unprocessable";
|
|
215
|
+
case "NONCE_INVALID":
|
|
216
|
+
case "SIGNATURE_INVALID":
|
|
217
|
+
case "MISSING_TOKEN":
|
|
218
|
+
case "TOKEN_INVALID":
|
|
219
|
+
case "TOKEN_EXPIRED":
|
|
220
|
+
return "forbidden";
|
|
221
|
+
case "SESSION_REVOKED":
|
|
222
|
+
return "not_found";
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
var AuthError = class extends PafiSdkError {
|
|
187
226
|
code;
|
|
227
|
+
httpStatus;
|
|
188
228
|
constructor(code, message) {
|
|
189
229
|
super(message);
|
|
190
|
-
this.name = "AuthError";
|
|
191
230
|
this.code = code;
|
|
231
|
+
this.httpStatus = statusForCode(code);
|
|
192
232
|
}
|
|
193
233
|
};
|
|
194
234
|
|
|
@@ -385,14 +425,15 @@ async function authenticateRequest(authHeader, authService) {
|
|
|
385
425
|
}
|
|
386
426
|
|
|
387
427
|
// src/relay/types.ts
|
|
388
|
-
var RelayError = class extends
|
|
428
|
+
var RelayError = class extends PafiSdkError {
|
|
429
|
+
httpStatus = "unprocessable";
|
|
389
430
|
code;
|
|
390
|
-
cause;
|
|
391
431
|
constructor(code, message, cause) {
|
|
392
432
|
super(message);
|
|
393
|
-
this.name = "RelayError";
|
|
394
433
|
this.code = code;
|
|
395
|
-
if (cause !== void 0)
|
|
434
|
+
if (cause !== void 0) {
|
|
435
|
+
this.cause = cause;
|
|
436
|
+
}
|
|
396
437
|
}
|
|
397
438
|
};
|
|
398
439
|
|
|
@@ -1107,13 +1148,16 @@ var IssuerApiHandlers = class {
|
|
|
1107
1148
|
/** `POST /auth/login` */
|
|
1108
1149
|
async handleLogin(body) {
|
|
1109
1150
|
if (!body || typeof body.message !== "string" || body.message.length === 0 || typeof body.signature !== "string" || body.signature.length <= 2) {
|
|
1110
|
-
throw new
|
|
1151
|
+
throw new ValidationError(
|
|
1152
|
+
"INVALID_LOGIN_BODY",
|
|
1153
|
+
"handleLogin: message and signature are required"
|
|
1154
|
+
);
|
|
1111
1155
|
}
|
|
1112
1156
|
if (body.message.length > 4096) {
|
|
1113
|
-
throw new
|
|
1157
|
+
throw new ValidationError("MESSAGE_TOO_LONG", "message too long");
|
|
1114
1158
|
}
|
|
1115
1159
|
if (body.signature.length > 260) {
|
|
1116
|
-
throw new
|
|
1160
|
+
throw new ValidationError("SIGNATURE_TOO_LONG", "signature too long");
|
|
1117
1161
|
}
|
|
1118
1162
|
const result = await this.authService.login(body.message, body.signature);
|
|
1119
1163
|
return {
|
|
@@ -1130,11 +1174,15 @@ var IssuerApiHandlers = class {
|
|
|
1130
1174
|
*/
|
|
1131
1175
|
async handleConfig(chainId) {
|
|
1132
1176
|
if (!Number.isInteger(chainId) || chainId <= 0) {
|
|
1133
|
-
throw new
|
|
1177
|
+
throw new ValidationError("INVALID_CHAIN_ID", "invalid chainId", {
|
|
1178
|
+
chainId
|
|
1179
|
+
});
|
|
1134
1180
|
}
|
|
1135
1181
|
if (chainId !== this.chainId) {
|
|
1136
|
-
throw new
|
|
1137
|
-
|
|
1182
|
+
throw new ValidationError(
|
|
1183
|
+
"UNSUPPORTED_CHAIN_ID",
|
|
1184
|
+
`handleConfig: unsupported chainId ${chainId}`,
|
|
1185
|
+
{ requested: chainId, supported: this.chainId }
|
|
1138
1186
|
);
|
|
1139
1187
|
}
|
|
1140
1188
|
const contracts = {
|
|
@@ -1151,7 +1199,8 @@ var IssuerApiHandlers = class {
|
|
|
1151
1199
|
/** `GET /gas-fee` — quoted in USDT (6-decimal base units). */
|
|
1152
1200
|
async handleGasFee() {
|
|
1153
1201
|
if (!this.feeManager) {
|
|
1154
|
-
throw new
|
|
1202
|
+
throw new ConfigurationError(
|
|
1203
|
+
"FEE_MANAGER_NOT_CONFIGURED",
|
|
1155
1204
|
"handleGasFee: feeManager is not configured on this issuer"
|
|
1156
1205
|
);
|
|
1157
1206
|
}
|
|
@@ -1173,13 +1222,16 @@ var IssuerApiHandlers = class {
|
|
|
1173
1222
|
*/
|
|
1174
1223
|
async handlePools(_userAddress, request) {
|
|
1175
1224
|
if (!this.poolsProvider) {
|
|
1176
|
-
throw new
|
|
1225
|
+
throw new ConfigurationError(
|
|
1226
|
+
"POOLS_PROVIDER_NOT_CONFIGURED",
|
|
1177
1227
|
"handlePools: poolsProvider is not configured on this issuer"
|
|
1178
1228
|
);
|
|
1179
1229
|
}
|
|
1180
1230
|
if (request.chainId !== this.chainId) {
|
|
1181
|
-
throw new
|
|
1182
|
-
|
|
1231
|
+
throw new ValidationError(
|
|
1232
|
+
"UNSUPPORTED_CHAIN_ID",
|
|
1233
|
+
`handlePools: unsupported chainId ${request.chainId}`,
|
|
1234
|
+
{ requested: request.chainId, supported: this.chainId }
|
|
1183
1235
|
);
|
|
1184
1236
|
}
|
|
1185
1237
|
return this.poolsProvider(request);
|
|
@@ -1193,21 +1245,27 @@ var IssuerApiHandlers = class {
|
|
|
1193
1245
|
*/
|
|
1194
1246
|
async handleUser(userAddress, request) {
|
|
1195
1247
|
if (request.chainId !== this.chainId) {
|
|
1196
|
-
throw new
|
|
1197
|
-
|
|
1248
|
+
throw new ValidationError(
|
|
1249
|
+
"UNSUPPORTED_CHAIN_ID",
|
|
1250
|
+
`handleUser: unsupported chainId ${request.chainId}`,
|
|
1251
|
+
{ requested: request.chainId, supported: this.chainId }
|
|
1198
1252
|
);
|
|
1199
1253
|
}
|
|
1200
1254
|
const normalizedAuthed = getAddress5(userAddress);
|
|
1201
1255
|
const normalizedRequest = getAddress5(request.userAddress);
|
|
1202
1256
|
if (normalizedAuthed !== normalizedRequest) {
|
|
1203
|
-
throw new
|
|
1204
|
-
"
|
|
1257
|
+
throw new ValidationError(
|
|
1258
|
+
"USER_ADDRESS_MISMATCH",
|
|
1259
|
+
"handleUser: request userAddress must match authenticated user",
|
|
1260
|
+
{ authenticated: normalizedAuthed, requested: normalizedRequest }
|
|
1205
1261
|
);
|
|
1206
1262
|
}
|
|
1207
1263
|
const pointToken = getAddress5(request.pointTokenAddress);
|
|
1208
1264
|
if (!this.supportedTokens.has(pointToken)) {
|
|
1209
|
-
throw new
|
|
1210
|
-
|
|
1265
|
+
throw new ValidationError(
|
|
1266
|
+
"UNSUPPORTED_POINT_TOKEN",
|
|
1267
|
+
`handleUser: unsupported pointToken ${pointToken}`,
|
|
1268
|
+
{ requested: pointToken }
|
|
1211
1269
|
);
|
|
1212
1270
|
}
|
|
1213
1271
|
const [mintRequestNonce, receiverConsentNonce, offChainBalance, onChainBalance, minter] = await Promise.all([
|
|
@@ -1408,69 +1466,82 @@ var PTRedeemHandler = class {
|
|
|
1408
1466
|
this.redeemLockDurationMs,
|
|
1409
1467
|
this.pointTokenAddress
|
|
1410
1468
|
);
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
userAddress: request.userAddress,
|
|
1414
|
-
aaNonce: request.aaNonce,
|
|
1415
|
-
pointTokenAddress: this.pointTokenAddress,
|
|
1416
|
-
batchExecutorAddress: this.batchExecutorAddress,
|
|
1417
|
-
burnRequest: sponsoredBurnRequest,
|
|
1418
|
-
burnerSignature: sponsoredSig,
|
|
1419
|
-
feeAmount: fee,
|
|
1420
|
-
feeRecipient
|
|
1421
|
-
});
|
|
1422
|
-
let fallback = void 0;
|
|
1423
|
-
if (fee > 0n) {
|
|
1424
|
-
const fallbackBurnRequest = {
|
|
1425
|
-
from: request.userAddress,
|
|
1426
|
-
amount: request.amount,
|
|
1427
|
-
nonce: burnNonce,
|
|
1428
|
-
deadline
|
|
1429
|
-
};
|
|
1430
|
-
let fallbackSig;
|
|
1431
|
-
try {
|
|
1432
|
-
fallbackSig = (await signBurnRequest(this.burnerSignerWallet, domain, fallbackBurnRequest)).serialized;
|
|
1433
|
-
} catch (err) {
|
|
1434
|
-
throw new PTRedeemError(
|
|
1435
|
-
"SIGNING_FAILED",
|
|
1436
|
-
`failed to sign fallback BurnRequest: ${err instanceof Error ? err.message : String(err)}`
|
|
1437
|
-
);
|
|
1438
|
-
}
|
|
1439
|
-
const fallbackLockId = await this.ledger.reservePendingCredit(
|
|
1440
|
-
request.userAddress,
|
|
1441
|
-
request.amount,
|
|
1442
|
-
this.redeemLockDurationMs,
|
|
1443
|
-
this.pointTokenAddress
|
|
1444
|
-
);
|
|
1445
|
-
const fallbackUserOp = await this.relayService.prepareBurn({
|
|
1469
|
+
try {
|
|
1470
|
+
const sponsoredUserOp = await this.relayService.prepareBurn({
|
|
1446
1471
|
mode: "burnWithSig",
|
|
1447
1472
|
userAddress: request.userAddress,
|
|
1448
1473
|
aaNonce: request.aaNonce,
|
|
1449
1474
|
pointTokenAddress: this.pointTokenAddress,
|
|
1450
1475
|
batchExecutorAddress: this.batchExecutorAddress,
|
|
1451
|
-
burnRequest:
|
|
1452
|
-
burnerSignature:
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
// auto-quoting RelayService would try to quote a fee here
|
|
1456
|
-
// and re-add the PT.transfer we're trying to strip.
|
|
1457
|
-
feeAmount: 0n
|
|
1476
|
+
burnRequest: sponsoredBurnRequest,
|
|
1477
|
+
burnerSignature: sponsoredSig,
|
|
1478
|
+
feeAmount: fee,
|
|
1479
|
+
feeRecipient
|
|
1458
1480
|
});
|
|
1459
|
-
fallback =
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1481
|
+
let fallback = void 0;
|
|
1482
|
+
if (fee > 0n) {
|
|
1483
|
+
const fallbackBurnRequest = {
|
|
1484
|
+
from: request.userAddress,
|
|
1485
|
+
amount: request.amount,
|
|
1486
|
+
nonce: burnNonce,
|
|
1487
|
+
deadline
|
|
1488
|
+
};
|
|
1489
|
+
let fallbackSig;
|
|
1490
|
+
try {
|
|
1491
|
+
fallbackSig = (await signBurnRequest(this.burnerSignerWallet, domain, fallbackBurnRequest)).serialized;
|
|
1492
|
+
} catch (err) {
|
|
1493
|
+
throw new PTRedeemError(
|
|
1494
|
+
"SIGNING_FAILED",
|
|
1495
|
+
`failed to sign fallback BurnRequest: ${err instanceof Error ? err.message : String(err)}`
|
|
1496
|
+
);
|
|
1497
|
+
}
|
|
1498
|
+
const fallbackLockId = await this.ledger.reservePendingCredit(
|
|
1499
|
+
request.userAddress,
|
|
1500
|
+
request.amount,
|
|
1501
|
+
this.redeemLockDurationMs,
|
|
1502
|
+
this.pointTokenAddress
|
|
1503
|
+
);
|
|
1504
|
+
let fallbackUserOp;
|
|
1505
|
+
try {
|
|
1506
|
+
fallbackUserOp = await this.relayService.prepareBurn({
|
|
1507
|
+
mode: "burnWithSig",
|
|
1508
|
+
userAddress: request.userAddress,
|
|
1509
|
+
aaNonce: request.aaNonce,
|
|
1510
|
+
pointTokenAddress: this.pointTokenAddress,
|
|
1511
|
+
batchExecutorAddress: this.batchExecutorAddress,
|
|
1512
|
+
burnRequest: fallbackBurnRequest,
|
|
1513
|
+
burnerSignature: fallbackSig,
|
|
1514
|
+
// Explicit 0n — fallback is fee-free regardless of how
|
|
1515
|
+
// RelayService is configured. Without this, an
|
|
1516
|
+
// auto-quoting RelayService would try to quote a fee here
|
|
1517
|
+
// and re-add the PT.transfer we're trying to strip.
|
|
1518
|
+
feeAmount: 0n
|
|
1519
|
+
});
|
|
1520
|
+
} catch (err) {
|
|
1521
|
+
await this.ledger.releaseLock(fallbackLockId).catch(() => {
|
|
1522
|
+
});
|
|
1523
|
+
throw err;
|
|
1524
|
+
}
|
|
1525
|
+
fallback = {
|
|
1526
|
+
lockId: fallbackLockId,
|
|
1527
|
+
userOp: fallbackUserOp,
|
|
1528
|
+
netCreditAmount: request.amount
|
|
1529
|
+
};
|
|
1530
|
+
}
|
|
1531
|
+
return {
|
|
1532
|
+
lockId: sponsoredLockId,
|
|
1533
|
+
userOp: sponsoredUserOp,
|
|
1534
|
+
netCreditAmount: sponsoredBurnAmount,
|
|
1535
|
+
feeAmount: fee,
|
|
1536
|
+
fallback,
|
|
1537
|
+
expiresInSeconds: Math.floor(this.redeemLockDurationMs / 1e3),
|
|
1538
|
+
signatureDeadline: deadline
|
|
1463
1539
|
};
|
|
1540
|
+
} catch (err) {
|
|
1541
|
+
await this.ledger.releaseLock(sponsoredLockId).catch(() => {
|
|
1542
|
+
});
|
|
1543
|
+
throw err;
|
|
1464
1544
|
}
|
|
1465
|
-
return {
|
|
1466
|
-
lockId: sponsoredLockId,
|
|
1467
|
-
userOp: sponsoredUserOp,
|
|
1468
|
-
netCreditAmount: sponsoredBurnAmount,
|
|
1469
|
-
feeAmount: fee,
|
|
1470
|
-
fallback,
|
|
1471
|
-
expiresInSeconds: Math.floor(this.redeemLockDurationMs / 1e3),
|
|
1472
|
-
signatureDeadline: deadline
|
|
1473
|
-
};
|
|
1474
1545
|
}
|
|
1475
1546
|
};
|
|
1476
1547
|
|
|
@@ -1744,6 +1815,41 @@ async function prepareMobileUserOp(params) {
|
|
|
1744
1815
|
};
|
|
1745
1816
|
}
|
|
1746
1817
|
|
|
1818
|
+
// src/pafi-backend/types.ts
|
|
1819
|
+
var PafiBackendError = class extends Error {
|
|
1820
|
+
constructor(code, message, httpStatus, details, opts) {
|
|
1821
|
+
super(message);
|
|
1822
|
+
this.code = code;
|
|
1823
|
+
this.httpStatus = httpStatus;
|
|
1824
|
+
this.details = details;
|
|
1825
|
+
this.name = "PafiBackendError";
|
|
1826
|
+
if (opts?.retryAfter !== void 0) this.retryAfter = opts.retryAfter;
|
|
1827
|
+
if (opts?.safeToRetry !== void 0) this.serverSafeToRetry = opts.safeToRetry;
|
|
1828
|
+
}
|
|
1829
|
+
code;
|
|
1830
|
+
httpStatus;
|
|
1831
|
+
details;
|
|
1832
|
+
retryAfter;
|
|
1833
|
+
serverSafeToRetry;
|
|
1834
|
+
get safeToRetry() {
|
|
1835
|
+
if (this.serverSafeToRetry !== void 0) return this.serverSafeToRetry;
|
|
1836
|
+
switch (this.code) {
|
|
1837
|
+
case "RATE_LIMITER_UNAVAILABLE":
|
|
1838
|
+
case "INTERNAL_ERROR":
|
|
1839
|
+
case "TIMEOUT":
|
|
1840
|
+
case "NETWORK_ERROR":
|
|
1841
|
+
return true;
|
|
1842
|
+
case "RATE_LIMIT_EXCEEDED":
|
|
1843
|
+
case "RATE_LIMIT_EXCEEDED_DAILY":
|
|
1844
|
+
case "RATE_LIMIT_EXCEEDED_PER_USER":
|
|
1845
|
+
case "ISSUER_BUDGET_EXCEEDED":
|
|
1846
|
+
return true;
|
|
1847
|
+
default:
|
|
1848
|
+
return false;
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
};
|
|
1852
|
+
|
|
1747
1853
|
// src/pafi-backend/helpers.ts
|
|
1748
1854
|
var BundlerNotConfiguredError = class extends PafiSdkError {
|
|
1749
1855
|
code = "BUNDLER_NOT_CONFIGURED";
|
|
@@ -1779,8 +1885,23 @@ async function requestPaymaster(params) {
|
|
|
1779
1885
|
});
|
|
1780
1886
|
} catch (err) {
|
|
1781
1887
|
const msg = err instanceof Error ? err.message : String(err);
|
|
1782
|
-
|
|
1783
|
-
|
|
1888
|
+
if (err instanceof PafiBackendError && isTransientPaymasterError(err.code)) {
|
|
1889
|
+
params.onWarning?.(`Paymaster sponsorship declined (transient): ${msg}`);
|
|
1890
|
+
return void 0;
|
|
1891
|
+
}
|
|
1892
|
+
throw err;
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
function isTransientPaymasterError(code) {
|
|
1896
|
+
switch (code) {
|
|
1897
|
+
case "NETWORK_ERROR":
|
|
1898
|
+
case "TIMEOUT":
|
|
1899
|
+
case "PAYMASTER_UNAVAILABLE":
|
|
1900
|
+
case "RATE_LIMITER_UNAVAILABLE":
|
|
1901
|
+
case "INTERNAL_ERROR":
|
|
1902
|
+
return true;
|
|
1903
|
+
default:
|
|
1904
|
+
return false;
|
|
1784
1905
|
}
|
|
1785
1906
|
}
|
|
1786
1907
|
function defaultFunctionForScenario(scenario) {
|
|
@@ -1964,39 +2085,19 @@ var PTClaimHandler = class {
|
|
|
1964
2085
|
this.cfg.lockDurationMs,
|
|
1965
2086
|
request.pointTokenAddress
|
|
1966
2087
|
);
|
|
1967
|
-
const signatureDeadline = BigInt(
|
|
1968
|
-
Math.floor(this.cfg.now() / 1e3) + this.cfg.signatureDeadlineSeconds
|
|
1969
|
-
);
|
|
1970
|
-
const feeAmount = this.cfg.feeService ? await this.cfg.feeService.estimateGasFee() : 0n;
|
|
1971
|
-
const domain = {
|
|
1972
|
-
name: this.cfg.pointTokenDomainName,
|
|
1973
|
-
chainId: request.chainId,
|
|
1974
|
-
verifyingContract: request.pointTokenAddress
|
|
1975
|
-
};
|
|
1976
|
-
let userOp;
|
|
1977
2088
|
try {
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
aaNonce: request.aaNonce,
|
|
1981
|
-
batchExecutorAddress,
|
|
1982
|
-
pointTokenAddress: request.pointTokenAddress,
|
|
1983
|
-
amount: request.amount,
|
|
1984
|
-
issuerSignerWallet: this.cfg.issuerSignerWallet,
|
|
1985
|
-
domain,
|
|
1986
|
-
mintRequestNonce: request.mintRequestNonce,
|
|
1987
|
-
deadline: signatureDeadline
|
|
1988
|
-
// No feeAmount/feeRecipient — RelayService auto-resolves.
|
|
1989
|
-
});
|
|
1990
|
-
} catch (err) {
|
|
1991
|
-
throw new PTClaimError(
|
|
1992
|
-
"BUILD_FAILED",
|
|
1993
|
-
`prepareMint failed: ${err instanceof Error ? err.message : String(err)}`
|
|
2089
|
+
const signatureDeadline = BigInt(
|
|
2090
|
+
Math.floor(this.cfg.now() / 1e3) + this.cfg.signatureDeadlineSeconds
|
|
1994
2091
|
);
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
2092
|
+
const feeAmount = this.cfg.feeService ? await this.cfg.feeService.estimateGasFee() : 0n;
|
|
2093
|
+
const domain = {
|
|
2094
|
+
name: this.cfg.pointTokenDomainName,
|
|
2095
|
+
chainId: request.chainId,
|
|
2096
|
+
verifyingContract: request.pointTokenAddress
|
|
2097
|
+
};
|
|
2098
|
+
let userOp;
|
|
1998
2099
|
try {
|
|
1999
|
-
|
|
2100
|
+
userOp = await this.cfg.relayService.prepareMint({
|
|
2000
2101
|
userAddress: request.userAddress,
|
|
2001
2102
|
aaNonce: request.aaNonce,
|
|
2002
2103
|
batchExecutorAddress,
|
|
@@ -2005,28 +2106,54 @@ var PTClaimHandler = class {
|
|
|
2005
2106
|
issuerSignerWallet: this.cfg.issuerSignerWallet,
|
|
2006
2107
|
domain,
|
|
2007
2108
|
mintRequestNonce: request.mintRequestNonce,
|
|
2008
|
-
deadline: signatureDeadline
|
|
2009
|
-
feeAmount
|
|
2109
|
+
deadline: signatureDeadline
|
|
2110
|
+
// No feeAmount/feeRecipient — RelayService auto-resolves.
|
|
2010
2111
|
});
|
|
2011
2112
|
} catch (err) {
|
|
2012
2113
|
throw new PTClaimError(
|
|
2013
2114
|
"BUILD_FAILED",
|
|
2014
|
-
`prepareMint
|
|
2115
|
+
`prepareMint failed: ${err instanceof Error ? err.message : String(err)}`
|
|
2015
2116
|
);
|
|
2016
2117
|
}
|
|
2118
|
+
let fallback;
|
|
2119
|
+
if (feeAmount > 0n) {
|
|
2120
|
+
try {
|
|
2121
|
+
fallback = await this.cfg.relayService.prepareMint({
|
|
2122
|
+
userAddress: request.userAddress,
|
|
2123
|
+
aaNonce: request.aaNonce,
|
|
2124
|
+
batchExecutorAddress,
|
|
2125
|
+
pointTokenAddress: request.pointTokenAddress,
|
|
2126
|
+
amount: request.amount,
|
|
2127
|
+
issuerSignerWallet: this.cfg.issuerSignerWallet,
|
|
2128
|
+
domain,
|
|
2129
|
+
mintRequestNonce: request.mintRequestNonce,
|
|
2130
|
+
deadline: signatureDeadline,
|
|
2131
|
+
feeAmount: 0n
|
|
2132
|
+
});
|
|
2133
|
+
} catch (err) {
|
|
2134
|
+
throw new PTClaimError(
|
|
2135
|
+
"BUILD_FAILED",
|
|
2136
|
+
`prepareMint (fallback) failed: ${err instanceof Error ? err.message : String(err)}`
|
|
2137
|
+
);
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
2140
|
+
const calls = decodeBatchExecuteCalls(userOp.callData);
|
|
2141
|
+
const callsFallback = fallback ? decodeBatchExecuteCalls(fallback.callData) : void 0;
|
|
2142
|
+
return {
|
|
2143
|
+
userOp,
|
|
2144
|
+
fallback,
|
|
2145
|
+
lockId,
|
|
2146
|
+
feeAmount,
|
|
2147
|
+
signatureDeadline,
|
|
2148
|
+
expiresInSeconds: Math.floor(this.cfg.lockDurationMs / 1e3),
|
|
2149
|
+
calls,
|
|
2150
|
+
callsFallback
|
|
2151
|
+
};
|
|
2152
|
+
} catch (err) {
|
|
2153
|
+
await this.cfg.ledger.releaseLock(lockId).catch(() => {
|
|
2154
|
+
});
|
|
2155
|
+
throw err;
|
|
2017
2156
|
}
|
|
2018
|
-
const calls = decodeBatchExecuteCalls(userOp.callData);
|
|
2019
|
-
const callsFallback = fallback ? decodeBatchExecuteCalls(fallback.callData) : void 0;
|
|
2020
|
-
return {
|
|
2021
|
-
userOp,
|
|
2022
|
-
fallback,
|
|
2023
|
-
lockId,
|
|
2024
|
-
feeAmount,
|
|
2025
|
-
signatureDeadline,
|
|
2026
|
-
expiresInSeconds: Math.floor(this.cfg.lockDurationMs / 1e3),
|
|
2027
|
-
calls,
|
|
2028
|
-
callsFallback
|
|
2029
|
-
};
|
|
2030
2157
|
}
|
|
2031
2158
|
};
|
|
2032
2159
|
|
|
@@ -2040,7 +2167,8 @@ import {
|
|
|
2040
2167
|
buildPerpDepositViaRelay,
|
|
2041
2168
|
computeAccountId,
|
|
2042
2169
|
decodeBatchExecuteCalls as decodeBatchExecuteCalls2,
|
|
2043
|
-
getContractAddresses as getContractAddresses4
|
|
2170
|
+
getContractAddresses as getContractAddresses4,
|
|
2171
|
+
quoteOperatorFeeUsdt
|
|
2044
2172
|
} from "@pafi-dev/core";
|
|
2045
2173
|
var PerpDepositError = class extends PafiSdkError {
|
|
2046
2174
|
httpStatus = "unprocessable";
|
|
@@ -2103,14 +2231,19 @@ var PerpDepositHandler = class {
|
|
|
2103
2231
|
totalAmount: request.amount,
|
|
2104
2232
|
maxFee: 0n
|
|
2105
2233
|
};
|
|
2106
|
-
const [relayTokenFee,
|
|
2234
|
+
const [relayTokenFee, usdcGasFee] = await Promise.all([
|
|
2107
2235
|
this.cfg.provider.readContract({
|
|
2108
2236
|
address: relayAddress,
|
|
2109
2237
|
abi: ORDERLY_RELAY_ABI,
|
|
2110
2238
|
functionName: "quoteTokenFee",
|
|
2111
2239
|
args: [requestForQuote]
|
|
2112
2240
|
}),
|
|
2113
|
-
|
|
2241
|
+
quoteOperatorFeeUsdt({
|
|
2242
|
+
provider: this.cfg.provider,
|
|
2243
|
+
chainId: request.chainId,
|
|
2244
|
+
gasUnits: this.cfg.gasUnits,
|
|
2245
|
+
premiumBps: this.cfg.gasPremiumBps
|
|
2246
|
+
})
|
|
2114
2247
|
]);
|
|
2115
2248
|
if (relayTokenFee >= request.amount) {
|
|
2116
2249
|
throw new PerpDepositError(
|
|
@@ -2118,6 +2251,12 @@ var PerpDepositHandler = class {
|
|
|
2118
2251
|
`Relay quoted fee ${relayTokenFee} >= deposit amount ${request.amount}`
|
|
2119
2252
|
);
|
|
2120
2253
|
}
|
|
2254
|
+
if (usdcGasFee > 0n && usdcGasFee >= request.amount) {
|
|
2255
|
+
throw new PerpDepositError(
|
|
2256
|
+
"FEE_EXCEEDS_AMOUNT",
|
|
2257
|
+
`USDC gas fee ${usdcGasFee} >= deposit amount ${request.amount}`
|
|
2258
|
+
);
|
|
2259
|
+
}
|
|
2121
2260
|
const maxFee = relayTokenFee * BigInt(1e4 + this.cfg.maxFeePremiumBps) / 10000n;
|
|
2122
2261
|
const depositReq = {
|
|
2123
2262
|
token: usdcAddress,
|
|
@@ -2131,11 +2270,10 @@ var PerpDepositHandler = class {
|
|
|
2131
2270
|
aaNonce: request.aaNonce,
|
|
2132
2271
|
relayAddress,
|
|
2133
2272
|
request: depositReq,
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
gasFeePtRecipient: pafiFeeRecipient
|
|
2273
|
+
gasFeeUsdc: usdcGasFee,
|
|
2274
|
+
gasFeeUsdcRecipient: pafiFeeRecipient
|
|
2137
2275
|
});
|
|
2138
|
-
const fallbackOp =
|
|
2276
|
+
const fallbackOp = usdcGasFee > 0n ? buildPerpDepositViaRelay({
|
|
2139
2277
|
userAddress: request.userAddress,
|
|
2140
2278
|
aaNonce: request.aaNonce,
|
|
2141
2279
|
relayAddress,
|
|
@@ -2144,7 +2282,7 @@ var PerpDepositHandler = class {
|
|
|
2144
2282
|
return {
|
|
2145
2283
|
userOp: sponsoredOp,
|
|
2146
2284
|
fallback: fallbackOp,
|
|
2147
|
-
feeAmount:
|
|
2285
|
+
feeAmount: usdcGasFee,
|
|
2148
2286
|
relayTokenFee,
|
|
2149
2287
|
maxFee,
|
|
2150
2288
|
netDeposit: request.amount - relayTokenFee,
|
|
@@ -2270,9 +2408,45 @@ import {
|
|
|
2270
2408
|
getContractAddresses as getContractAddresses6,
|
|
2271
2409
|
parseEip7702DelegatedAddress as parseEip7702DelegatedAddress2
|
|
2272
2410
|
} from "@pafi-dev/core";
|
|
2411
|
+
var AdapterMisconfiguredError = class extends Error {
|
|
2412
|
+
code = "ADAPTER_MISCONFIGURED";
|
|
2413
|
+
constructor(message) {
|
|
2414
|
+
super(message);
|
|
2415
|
+
this.name = "AdapterMisconfiguredError";
|
|
2416
|
+
}
|
|
2417
|
+
};
|
|
2273
2418
|
var IssuerApiAdapter = class {
|
|
2274
2419
|
cfg;
|
|
2275
2420
|
constructor(config) {
|
|
2421
|
+
if (config.ptClaimHandler) {
|
|
2422
|
+
if (typeof config.ledger.bindMintUserOpHash !== "function") {
|
|
2423
|
+
throw new AdapterMisconfiguredError(
|
|
2424
|
+
"ledger.bindMintUserOpHash is required when ptClaimHandler is wired (mobile claim flow). Implement it on your IPointLedger or omit ptClaimHandler from IssuerApiAdapter config."
|
|
2425
|
+
);
|
|
2426
|
+
}
|
|
2427
|
+
if (typeof config.ledger.getMintLock !== "function") {
|
|
2428
|
+
throw new AdapterMisconfiguredError(
|
|
2429
|
+
"ledger.getMintLock is required when ptClaimHandler is wired \u2014 claimStatus uses it to look up the lock."
|
|
2430
|
+
);
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
if (config.ptRedeemHandler) {
|
|
2434
|
+
if (typeof config.ledger.reservePendingCredit !== "function") {
|
|
2435
|
+
throw new AdapterMisconfiguredError(
|
|
2436
|
+
"ledger.reservePendingCredit is required when ptRedeemHandler is wired (burn/redeem reverse flow). PTRedeemHandler also enforces this at construction; see ledger/types.ts comments."
|
|
2437
|
+
);
|
|
2438
|
+
}
|
|
2439
|
+
if (typeof config.ledger.bindCreditUserOpHash !== "function") {
|
|
2440
|
+
throw new AdapterMisconfiguredError(
|
|
2441
|
+
"ledger.bindCreditUserOpHash is required when ptRedeemHandler is wired (mobile redeem flow)."
|
|
2442
|
+
);
|
|
2443
|
+
}
|
|
2444
|
+
if (typeof config.ledger.getPendingCredit !== "function") {
|
|
2445
|
+
throw new AdapterMisconfiguredError(
|
|
2446
|
+
"ledger.getPendingCredit is required when ptRedeemHandler is wired \u2014 redeemStatus uses it to look up the credit."
|
|
2447
|
+
);
|
|
2448
|
+
}
|
|
2449
|
+
}
|
|
2276
2450
|
this.cfg = config;
|
|
2277
2451
|
}
|
|
2278
2452
|
// ------------------------------ Read endpoints ---------------------------
|
|
@@ -2573,6 +2747,11 @@ var IssuerApiAdapter = class {
|
|
|
2573
2747
|
/**
|
|
2574
2748
|
* Build + sign a SponsorAuth payload. Returns `undefined` when no
|
|
2575
2749
|
* issuer id is configured, so the controller can skip the field.
|
|
2750
|
+
*
|
|
2751
|
+
* v0.7.1 — `scenario` typed as `SponsorshipScenario` (was `string`).
|
|
2752
|
+
* Previously a typo (`"perp_deposit"` vs `"perp-deposit"`) compiled
|
|
2753
|
+
* fine but rejected at L1 by sponsor-relayer's IntentValidator. See
|
|
2754
|
+
* SDK_ISSUER_AUDIT.md N5.
|
|
2576
2755
|
*/
|
|
2577
2756
|
async buildSponsorAuth(authenticatedAddress, callData, chainId, scenario) {
|
|
2578
2757
|
if (!this.cfg.pafiIssuerId) return void 0;
|
|
@@ -3033,41 +3212,6 @@ var BalanceAggregator = class {
|
|
|
3033
3212
|
}
|
|
3034
3213
|
};
|
|
3035
3214
|
|
|
3036
|
-
// src/pafi-backend/types.ts
|
|
3037
|
-
var PafiBackendError = class extends Error {
|
|
3038
|
-
constructor(code, message, httpStatus, details, opts) {
|
|
3039
|
-
super(message);
|
|
3040
|
-
this.code = code;
|
|
3041
|
-
this.httpStatus = httpStatus;
|
|
3042
|
-
this.details = details;
|
|
3043
|
-
this.name = "PafiBackendError";
|
|
3044
|
-
if (opts?.retryAfter !== void 0) this.retryAfter = opts.retryAfter;
|
|
3045
|
-
if (opts?.safeToRetry !== void 0) this.serverSafeToRetry = opts.safeToRetry;
|
|
3046
|
-
}
|
|
3047
|
-
code;
|
|
3048
|
-
httpStatus;
|
|
3049
|
-
details;
|
|
3050
|
-
retryAfter;
|
|
3051
|
-
serverSafeToRetry;
|
|
3052
|
-
get safeToRetry() {
|
|
3053
|
-
if (this.serverSafeToRetry !== void 0) return this.serverSafeToRetry;
|
|
3054
|
-
switch (this.code) {
|
|
3055
|
-
case "RATE_LIMITER_UNAVAILABLE":
|
|
3056
|
-
case "INTERNAL_ERROR":
|
|
3057
|
-
case "TIMEOUT":
|
|
3058
|
-
case "NETWORK_ERROR":
|
|
3059
|
-
return true;
|
|
3060
|
-
case "RATE_LIMIT_EXCEEDED":
|
|
3061
|
-
case "RATE_LIMIT_EXCEEDED_DAILY":
|
|
3062
|
-
case "RATE_LIMIT_EXCEEDED_PER_USER":
|
|
3063
|
-
case "ISSUER_BUDGET_EXCEEDED":
|
|
3064
|
-
return true;
|
|
3065
|
-
default:
|
|
3066
|
-
return false;
|
|
3067
|
-
}
|
|
3068
|
-
}
|
|
3069
|
-
};
|
|
3070
|
-
|
|
3071
3215
|
// src/pafi-backend/client.ts
|
|
3072
3216
|
function serializeBigInt(_key, value) {
|
|
3073
3217
|
return typeof value === "bigint" ? value.toString(10) : value;
|
|
@@ -3512,14 +3656,16 @@ var IssuerStateValidator = class _IssuerStateValidator {
|
|
|
3512
3656
|
};
|
|
3513
3657
|
|
|
3514
3658
|
// src/index.ts
|
|
3515
|
-
var PAFI_ISSUER_SDK_VERSION = "0.
|
|
3659
|
+
var PAFI_ISSUER_SDK_VERSION = true ? "0.7.1" : "dev";
|
|
3516
3660
|
export {
|
|
3661
|
+
AdapterMisconfiguredError,
|
|
3517
3662
|
AuthError,
|
|
3518
3663
|
AuthService,
|
|
3519
3664
|
BalanceAggregator,
|
|
3520
3665
|
BundlerNotConfiguredError,
|
|
3521
3666
|
BundlerRejectedError,
|
|
3522
3667
|
BurnIndexer,
|
|
3668
|
+
ConfigurationError,
|
|
3523
3669
|
DefaultPolicyEngine,
|
|
3524
3670
|
FeeManager,
|
|
3525
3671
|
InMemoryCursorStore,
|
|
@@ -3547,6 +3693,7 @@ export {
|
|
|
3547
3693
|
PointIndexer,
|
|
3548
3694
|
RelayError,
|
|
3549
3695
|
RelayService,
|
|
3696
|
+
ValidationError,
|
|
3550
3697
|
authenticateRequest,
|
|
3551
3698
|
createIssuerService,
|
|
3552
3699
|
createNativePtQuoter,
|