@pafi-dev/issuer 0.21.0 → 0.22.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/README.md +36 -0
- package/dist/index.cjs +77 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +137 -3
- package/dist/index.d.ts +137 -3
- package/dist/index.js +76 -6
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -14,6 +14,42 @@ interfaces. Don't bundle into a browser app — use
|
|
|
14
14
|
|
|
15
15
|
---
|
|
16
16
|
|
|
17
|
+
## v0.20.x — Bundler-driven gas estimate
|
|
18
|
+
|
|
19
|
+
**v0.20.0** — operator fee gasUnits now come from a Pimlico bundler
|
|
20
|
+
estimate cached server-side at PAFI's sponsor-relayer
|
|
21
|
+
(`POST /v1/estimate-gas-fee`). Eliminates the hardcoded
|
|
22
|
+
`SCENARIO_GAS_UNITS` table at the consumer layer.
|
|
23
|
+
|
|
24
|
+
- **New**: `FeeManagerConfig.bundlerClient?: BundlerEstimatorClient`. When
|
|
25
|
+
set, `feeManager.estimateGasFee({ partialUserOp, scenario,
|
|
26
|
+
contractAddress })` fetches gas units from the wired estimator
|
|
27
|
+
instead of running the legacy `gasUnits × premium` math.
|
|
28
|
+
Backwards-compatible — omit `bundlerClient` and the legacy hardcoded
|
|
29
|
+
path runs.
|
|
30
|
+
- **New**: `createPafiEstimatorClient({ baseUrl, apiKey, issuerId })`
|
|
31
|
+
HTTP adapter — POSTs to PAFI sponsor-relayer's
|
|
32
|
+
`/v1/estimate-gas-fee`. Pimlico key never touches issuer infra; the
|
|
33
|
+
bundler call happens server-side at PAFI.
|
|
34
|
+
- **New**: `RelayService.previewMintUserOp` / `previewBurnUserOp` —
|
|
35
|
+
build a dummy partial UserOp (placeholder 65-byte sig, no HSM call)
|
|
36
|
+
for the bundler estimate. `PTClaimHandler` / `PTRedeemHandler` wire
|
|
37
|
+
these automatically.
|
|
38
|
+
- **Changed**: `DEFAULT_PREMIUM_BPS` 12_000 → 10_000 (100%, no
|
|
39
|
+
double-pad). PAFI sponsor-relayer applies its own 110% premium
|
|
40
|
+
upstream; adding another SDK-side pad would over-charge users by
|
|
41
|
+
~21% vs. actual gas.
|
|
42
|
+
- **Changed**: `PTClaimHandler` now passes the FeeManager-computed
|
|
43
|
+
`feeAmount` explicitly into `RelayService.prepareMint`, so the
|
|
44
|
+
in-batch PT transfer matches the value returned in the response.
|
|
45
|
+
Pre-v0.20 the handler relied on `RelayService.resolveFee` falling
|
|
46
|
+
back to `quoteOperatorFeePt` (which uses the legacy 120% premium) —
|
|
47
|
+
causing a ~20% mismatch between displayed fee and actual transfer.
|
|
48
|
+
|
|
49
|
+
**v0.21.0** — bug fix follow-up to v0.20: closes the resolveFee
|
|
50
|
+
mismatch in `PTClaimHandler` (above). Strongly recommended over
|
|
51
|
+
v0.20.0 for any new consumer wiring.
|
|
52
|
+
|
|
17
53
|
## v0.15.x — Uniswap V3 migration (breaking)
|
|
18
54
|
|
|
19
55
|
**v0.15.0** — initial V3 migration:
|
package/dist/index.cjs
CHANGED
|
@@ -84,6 +84,7 @@ __export(index_exports, {
|
|
|
84
84
|
handleMobilePrepare: () => handleMobilePrepare,
|
|
85
85
|
handleMobileSubmit: () => handleMobileSubmit,
|
|
86
86
|
handleRedeemStatus: () => handleRedeemStatus,
|
|
87
|
+
makePostgresSingletonLock: () => makePostgresSingletonLock,
|
|
87
88
|
mergePaymasterFields: () => mergePaymasterFields,
|
|
88
89
|
payloadFromGenericError: () => payloadFromGenericError,
|
|
89
90
|
payloadFromHttpException: () => payloadFromHttpException,
|
|
@@ -1625,6 +1626,10 @@ var DEFAULT_BATCH_SIZE2 = 2000n;
|
|
|
1625
1626
|
var DEFAULT_POLL_INTERVAL_MS2 = 5e3;
|
|
1626
1627
|
var BurnIndexer = class {
|
|
1627
1628
|
provider;
|
|
1629
|
+
/**
|
|
1630
|
+
* The PointToken this indexer watches. Exposed so callers can key
|
|
1631
|
+
* leader-election locks / cursor stores by token (audit H-04 fix).
|
|
1632
|
+
*/
|
|
1628
1633
|
pointTokenAddress;
|
|
1629
1634
|
ledger;
|
|
1630
1635
|
cursorStore;
|
|
@@ -1784,6 +1789,45 @@ var BurnIndexer = class {
|
|
|
1784
1789
|
}
|
|
1785
1790
|
};
|
|
1786
1791
|
|
|
1792
|
+
// src/indexer/postgresSingletonLock.ts
|
|
1793
|
+
function makePostgresSingletonLock(runner) {
|
|
1794
|
+
return {
|
|
1795
|
+
async acquire(key) {
|
|
1796
|
+
const lockId = hashKeyToInt64(key);
|
|
1797
|
+
const rows = await runner.query(
|
|
1798
|
+
"SELECT pg_try_advisory_lock($1::bigint) AS got",
|
|
1799
|
+
[lockId]
|
|
1800
|
+
);
|
|
1801
|
+
const got = rows[0]?.got === true;
|
|
1802
|
+
if (!got) return null;
|
|
1803
|
+
return {
|
|
1804
|
+
async release() {
|
|
1805
|
+
try {
|
|
1806
|
+
await runner.query("SELECT pg_advisory_unlock($1::bigint)", [
|
|
1807
|
+
lockId
|
|
1808
|
+
]);
|
|
1809
|
+
} catch {
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1812
|
+
};
|
|
1813
|
+
}
|
|
1814
|
+
};
|
|
1815
|
+
}
|
|
1816
|
+
function hashKeyToInt64(key) {
|
|
1817
|
+
const FNV_OFFSET = 0xcbf29ce484222325n;
|
|
1818
|
+
const FNV_PRIME = 0x100000001b3n;
|
|
1819
|
+
const MASK_64 = (1n << 64n) - 1n;
|
|
1820
|
+
let hash = FNV_OFFSET;
|
|
1821
|
+
for (let i = 0; i < key.length; i++) {
|
|
1822
|
+
hash ^= BigInt(key.charCodeAt(i));
|
|
1823
|
+
hash = hash * FNV_PRIME & MASK_64;
|
|
1824
|
+
}
|
|
1825
|
+
const SIGNED_MAX = (1n << 63n) - 1n;
|
|
1826
|
+
const TWO64 = 1n << 64n;
|
|
1827
|
+
const signed = hash > SIGNED_MAX ? hash - TWO64 : hash;
|
|
1828
|
+
return signed.toString();
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1787
1831
|
// src/api/handlers.ts
|
|
1788
1832
|
var import_viem6 = require("viem");
|
|
1789
1833
|
var import_core6 = require("@pafi-dev/core");
|
|
@@ -3076,8 +3120,16 @@ var PTClaimHandler = class {
|
|
|
3076
3120
|
domain,
|
|
3077
3121
|
mintRequestNonce: request.mintRequestNonce,
|
|
3078
3122
|
deadline: signatureDeadline,
|
|
3079
|
-
mintFeeWrapperAddress: resolvedWrapper
|
|
3080
|
-
//
|
|
3123
|
+
mintFeeWrapperAddress: resolvedWrapper,
|
|
3124
|
+
// Pass the bundler-estimated `feeAmount` explicitly so the
|
|
3125
|
+
// RelayService skips its legacy `quoteOperatorFeePt` path
|
|
3126
|
+
// (which uses the SDK's old 12_000 bps premium default).
|
|
3127
|
+
// Without this, the response's `feeAmount` (from FeeManager,
|
|
3128
|
+
// 100% premium on top of PAFI's 110% server-side estimate)
|
|
3129
|
+
// would diverge from the actual PT.transfer amount in the
|
|
3130
|
+
// UserOp batch (`quoteOperatorFeePt`'s 120%), and the user
|
|
3131
|
+
// would see one value while the wallet transferred another.
|
|
3132
|
+
feeAmount
|
|
3081
3133
|
});
|
|
3082
3134
|
} catch (err) {
|
|
3083
3135
|
throw new PTClaimError(
|
|
@@ -4658,7 +4710,7 @@ var RedemptionService = class {
|
|
|
4658
4710
|
};
|
|
4659
4711
|
|
|
4660
4712
|
// src/config.ts
|
|
4661
|
-
function createIssuerService(config) {
|
|
4713
|
+
async function createIssuerService(config) {
|
|
4662
4714
|
if (!config.provider) {
|
|
4663
4715
|
throw new Error("createIssuerService: provider is required");
|
|
4664
4716
|
}
|
|
@@ -4777,9 +4829,26 @@ function createIssuerService(config) {
|
|
|
4777
4829
|
handlersConfig.mintFeeWrapperAddress = resolvedWrapperAddress;
|
|
4778
4830
|
}
|
|
4779
4831
|
const handlers = new IssuerApiHandlers(handlersConfig);
|
|
4832
|
+
const indexerLeaderLocks = [];
|
|
4780
4833
|
if (config.indexer?.autoStart) {
|
|
4781
|
-
|
|
4782
|
-
|
|
4834
|
+
const lock = config.indexer.singletonLock;
|
|
4835
|
+
if (!lock) {
|
|
4836
|
+
console.warn(
|
|
4837
|
+
"[@pafi-dev/issuer] indexer.autoStart=true without singletonLock \u2014 this is UNSAFE in multi-replica deployments (audit finding H-04). Either set replicas=1 + INDEXER_AUTOSTART=false on non-leader pods, or pass `singletonLock: makePostgresSingletonLock(dataSource)`. This permissive path will be removed in a future major release."
|
|
4838
|
+
);
|
|
4839
|
+
for (const idx of indexers.values()) {
|
|
4840
|
+
idx.start();
|
|
4841
|
+
}
|
|
4842
|
+
} else {
|
|
4843
|
+
for (const [tokenAddr, idx] of indexers.entries()) {
|
|
4844
|
+
const key = `pafi-issuer:point-indexer:${tokenAddr.toLowerCase()}`;
|
|
4845
|
+
const handle = await lock.acquire(key);
|
|
4846
|
+
if (!handle) {
|
|
4847
|
+
continue;
|
|
4848
|
+
}
|
|
4849
|
+
idx.start();
|
|
4850
|
+
indexerLeaderLocks.push(handle);
|
|
4851
|
+
}
|
|
4783
4852
|
}
|
|
4784
4853
|
}
|
|
4785
4854
|
return {
|
|
@@ -4790,6 +4859,7 @@ function createIssuerService(config) {
|
|
|
4790
4859
|
relay: relayService,
|
|
4791
4860
|
fee: feeManager,
|
|
4792
4861
|
indexers,
|
|
4862
|
+
indexerLeaderLocks,
|
|
4793
4863
|
api: handlers,
|
|
4794
4864
|
redemption
|
|
4795
4865
|
};
|
|
@@ -4997,7 +5067,7 @@ var MemoryRedemptionHistoryStore = class {
|
|
|
4997
5067
|
};
|
|
4998
5068
|
|
|
4999
5069
|
// src/index.ts
|
|
5000
|
-
var PAFI_ISSUER_SDK_VERSION = true ? "0.
|
|
5070
|
+
var PAFI_ISSUER_SDK_VERSION = true ? "0.22.1" : "dev";
|
|
5001
5071
|
// Annotate the CommonJS export names for ESM import in node:
|
|
5002
5072
|
0 && (module.exports = {
|
|
5003
5073
|
AdapterMisconfiguredError,
|
|
@@ -5064,6 +5134,7 @@ var PAFI_ISSUER_SDK_VERSION = true ? "0.21.0" : "dev";
|
|
|
5064
5134
|
handleMobilePrepare,
|
|
5065
5135
|
handleMobileSubmit,
|
|
5066
5136
|
handleRedeemStatus,
|
|
5137
|
+
makePostgresSingletonLock,
|
|
5067
5138
|
mergePaymasterFields,
|
|
5068
5139
|
payloadFromGenericError,
|
|
5069
5140
|
payloadFromHttpException,
|