@pafi-dev/issuer 0.19.0 → 0.21.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 +11 -6
- package/dist/index.cjs +375 -73
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +260 -49
- package/dist/index.d.ts +260 -49
- package/dist/index.js +368 -69
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -862,58 +862,265 @@ var RelayService = class {
|
|
|
862
862
|
}
|
|
863
863
|
});
|
|
864
864
|
}
|
|
865
|
+
// =========================================================================
|
|
866
|
+
// Preview methods — produce bundler-ready partial UserOps WITHOUT signing.
|
|
867
|
+
//
|
|
868
|
+
// These exist so callers can fetch an accurate gas estimate from PAFI's
|
|
869
|
+
// `/v1/estimate-gas-fee` BEFORE committing to a signed MintRequest /
|
|
870
|
+
// BurnRequest (HSM/KMS signing is expensive in production). The
|
|
871
|
+
// returned `callData` matches the SHAPE of the real call; the EIP-712
|
|
872
|
+
// signature bytes are a placeholder. Bundler simulation doesn't
|
|
873
|
+
// validate the signature, so the gas estimate comes back accurate.
|
|
874
|
+
//
|
|
875
|
+
// Cache-wise: same SC version + same scenario produce identical
|
|
876
|
+
// calldata shape → identical bundler-returned gas units. The first
|
|
877
|
+
// call seeds the cache; subsequent ones hit it.
|
|
878
|
+
// =========================================================================
|
|
879
|
+
/**
|
|
880
|
+
* Build a dummy `PartialUserOperation` for the mint scenario, suitable
|
|
881
|
+
* for `feeManager.estimateGasFee({ partialUserOp, ... })`. NO signing —
|
|
882
|
+
* uses a 65-byte zero signature placeholder.
|
|
883
|
+
*/
|
|
884
|
+
previewMintUserOp(params) {
|
|
885
|
+
const useWrapper = params.mintFeeWrapperAddress !== void 0;
|
|
886
|
+
let mintCallData;
|
|
887
|
+
let mintTarget;
|
|
888
|
+
if (useWrapper) {
|
|
889
|
+
mintCallData = encodeFunctionData({
|
|
890
|
+
abi: mintFeeWrapperAbi,
|
|
891
|
+
functionName: "mintWithFee",
|
|
892
|
+
args: [
|
|
893
|
+
params.pointTokenAddress,
|
|
894
|
+
params.userAddress,
|
|
895
|
+
params.amount,
|
|
896
|
+
params.deadline,
|
|
897
|
+
PLACEHOLDER_SIG_65
|
|
898
|
+
]
|
|
899
|
+
});
|
|
900
|
+
mintTarget = params.mintFeeWrapperAddress;
|
|
901
|
+
} else {
|
|
902
|
+
mintCallData = encodeFunctionData({
|
|
903
|
+
abi: POINT_TOKEN_ABI,
|
|
904
|
+
functionName: "mint",
|
|
905
|
+
args: [
|
|
906
|
+
params.userAddress,
|
|
907
|
+
params.amount,
|
|
908
|
+
params.deadline,
|
|
909
|
+
PLACEHOLDER_SIG_65
|
|
910
|
+
]
|
|
911
|
+
});
|
|
912
|
+
mintTarget = params.pointTokenAddress;
|
|
913
|
+
}
|
|
914
|
+
return buildPartialUserOperation({
|
|
915
|
+
sender: params.userAddress,
|
|
916
|
+
nonce: params.aaNonce,
|
|
917
|
+
operations: [{ target: mintTarget, value: 0n, data: mintCallData }],
|
|
918
|
+
// Gas limits ignored by bundler estimate — it computes them.
|
|
919
|
+
gasLimits: {
|
|
920
|
+
callGasLimit: 1n,
|
|
921
|
+
verificationGasLimit: 1n,
|
|
922
|
+
preVerificationGas: 1n
|
|
923
|
+
}
|
|
924
|
+
});
|
|
925
|
+
}
|
|
926
|
+
/** Burn-side mirror of `previewMintUserOp`. */
|
|
927
|
+
previewBurnUserOp(params) {
|
|
928
|
+
const burnCallData = encodeFunctionData({
|
|
929
|
+
abi: POINT_TOKEN_ABI,
|
|
930
|
+
functionName: "burn",
|
|
931
|
+
args: [
|
|
932
|
+
params.userAddress,
|
|
933
|
+
params.amount,
|
|
934
|
+
params.deadline,
|
|
935
|
+
PLACEHOLDER_SIG_65
|
|
936
|
+
]
|
|
937
|
+
});
|
|
938
|
+
return buildPartialUserOperation({
|
|
939
|
+
sender: params.userAddress,
|
|
940
|
+
nonce: params.aaNonce,
|
|
941
|
+
operations: [
|
|
942
|
+
{ target: params.pointTokenAddress, value: 0n, data: burnCallData }
|
|
943
|
+
],
|
|
944
|
+
gasLimits: {
|
|
945
|
+
callGasLimit: 1n,
|
|
946
|
+
verificationGasLimit: 1n,
|
|
947
|
+
preVerificationGas: 1n
|
|
948
|
+
}
|
|
949
|
+
});
|
|
950
|
+
}
|
|
865
951
|
};
|
|
952
|
+
var PLACEHOLDER_SIG_65 = `0x${"00".repeat(65)}`;
|
|
866
953
|
function errorMessage(err) {
|
|
867
954
|
return err instanceof Error ? err.message : String(err);
|
|
868
955
|
}
|
|
869
956
|
|
|
870
957
|
// src/relay/feeManager.ts
|
|
871
958
|
var DEFAULT_GAS_UNITS = 500000n;
|
|
872
|
-
var DEFAULT_PREMIUM_BPS =
|
|
959
|
+
var DEFAULT_PREMIUM_BPS = 1e4;
|
|
873
960
|
var FeeManager = class _FeeManager {
|
|
874
961
|
provider;
|
|
875
|
-
|
|
962
|
+
fallbackGasUnits;
|
|
876
963
|
gasPremiumBps;
|
|
877
964
|
quoteNativeToFee;
|
|
965
|
+
bundlerClient;
|
|
966
|
+
metrics;
|
|
967
|
+
// Short-lived fee-value cache. Distinct from the estimator's cache:
|
|
968
|
+
// this absorbs burst calls (e.g. 5 user requests in 5s all hit the
|
|
969
|
+
// /gas-fee endpoint) by remembering the COMPUTED PT amount, not the
|
|
970
|
+
// gas units. Only used by the no-opts legacy path; estimator path
|
|
971
|
+
// gets its caching from the PAFI side instead.
|
|
878
972
|
cachedFee = null;
|
|
879
973
|
cacheExpiresAt = 0;
|
|
880
|
-
static
|
|
974
|
+
static FEE_CACHE_TTL_MS = 1e4;
|
|
881
975
|
constructor(config) {
|
|
882
976
|
if (!config.provider) throw new Error("FeeManager: provider required");
|
|
883
977
|
if (!config.quoteNativeToFee)
|
|
884
978
|
throw new Error("FeeManager: quoteNativeToFee required");
|
|
885
979
|
this.provider = config.provider;
|
|
886
|
-
this.
|
|
980
|
+
this.fallbackGasUnits = config.gasUnits ?? DEFAULT_GAS_UNITS;
|
|
887
981
|
this.gasPremiumBps = config.gasPremiumBps ?? DEFAULT_PREMIUM_BPS;
|
|
888
982
|
this.quoteNativeToFee = config.quoteNativeToFee;
|
|
983
|
+
this.bundlerClient = config.bundlerClient;
|
|
984
|
+
this.metrics = config.metrics;
|
|
889
985
|
}
|
|
890
986
|
/**
|
|
891
|
-
* Estimate the fee
|
|
892
|
-
* next sponsored UserOp:
|
|
987
|
+
* Estimate the operator fee for the next sponsored UserOp.
|
|
893
988
|
*
|
|
894
|
-
*
|
|
895
|
-
*
|
|
896
|
-
* fee = quoteNativeToFee(withPremium)
|
|
989
|
+
* Without `opts` → legacy path: `gasUnits × gasPrice × premium →
|
|
990
|
+
* quoteNativeToFee`. Cached for 10 s to absorb bursts.
|
|
897
991
|
*
|
|
898
|
-
*
|
|
899
|
-
*
|
|
900
|
-
*
|
|
992
|
+
* With `opts` AND `bundlerClient` → estimator path. Each call may
|
|
993
|
+
* hit a different bundler-cached result; the SDK does NOT add its
|
|
994
|
+
* own value cache because the estimator's cache TTL is the source
|
|
995
|
+
* of truth for "how long is this estimate good for".
|
|
901
996
|
*/
|
|
902
|
-
async estimateGasFee() {
|
|
997
|
+
async estimateGasFee(opts = {}) {
|
|
998
|
+
const isLegacyCall = !opts.partialUserOp && !opts.scenario && !opts.contractAddress;
|
|
903
999
|
const now = Date.now();
|
|
904
|
-
if (this.cachedFee !== null && now < this.cacheExpiresAt) {
|
|
1000
|
+
if (isLegacyCall && this.cachedFee !== null && now < this.cacheExpiresAt) {
|
|
905
1001
|
return this.cachedFee;
|
|
906
1002
|
}
|
|
1003
|
+
const t0 = Date.now();
|
|
1004
|
+
const { gasUnits, source } = await this.resolveGasUnits(opts);
|
|
1005
|
+
const latencyMs = Date.now() - t0;
|
|
1006
|
+
this.safeEmit(
|
|
1007
|
+
() => this.metrics?.onEstimate?.({
|
|
1008
|
+
source,
|
|
1009
|
+
scenario: opts.scenario,
|
|
1010
|
+
gasUnits,
|
|
1011
|
+
latencyMs
|
|
1012
|
+
})
|
|
1013
|
+
);
|
|
907
1014
|
const gasPrice = await this.provider.getGasPrice();
|
|
908
|
-
const nativeCost = gasPrice *
|
|
1015
|
+
const nativeCost = gasPrice * gasUnits;
|
|
909
1016
|
const withPremium = nativeCost * BigInt(this.gasPremiumBps) / 10000n;
|
|
910
1017
|
const fee = await this.quoteNativeToFee(withPremium);
|
|
911
|
-
|
|
912
|
-
|
|
1018
|
+
if (isLegacyCall) {
|
|
1019
|
+
this.cachedFee = fee;
|
|
1020
|
+
this.cacheExpiresAt = now + _FeeManager.FEE_CACHE_TTL_MS;
|
|
1021
|
+
}
|
|
913
1022
|
return fee;
|
|
914
1023
|
}
|
|
1024
|
+
/** Manually purge the legacy 10s fee cache. */
|
|
1025
|
+
invalidateCache() {
|
|
1026
|
+
this.cachedFee = null;
|
|
1027
|
+
this.cacheExpiresAt = 0;
|
|
1028
|
+
}
|
|
1029
|
+
async resolveGasUnits(opts) {
|
|
1030
|
+
if (!this.bundlerClient || !opts.partialUserOp || !opts.scenario || !opts.contractAddress) {
|
|
1031
|
+
return { gasUnits: this.fallbackGasUnits, source: "fallback" };
|
|
1032
|
+
}
|
|
1033
|
+
try {
|
|
1034
|
+
const result = await this.bundlerClient.getGasUnits({
|
|
1035
|
+
scenario: opts.scenario,
|
|
1036
|
+
contractAddress: opts.contractAddress,
|
|
1037
|
+
paymasterAddress: opts.paymasterAddress,
|
|
1038
|
+
partialUserOp: opts.partialUserOp
|
|
1039
|
+
});
|
|
1040
|
+
return { gasUnits: result.gasUnits, source: "estimator" };
|
|
1041
|
+
} catch (err) {
|
|
1042
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
1043
|
+
this.safeEmit(
|
|
1044
|
+
() => this.metrics?.onEstimatorError?.({
|
|
1045
|
+
scenario: opts.scenario,
|
|
1046
|
+
reason
|
|
1047
|
+
})
|
|
1048
|
+
);
|
|
1049
|
+
return { gasUnits: this.fallbackGasUnits, source: "fallback" };
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
safeEmit(fn) {
|
|
1053
|
+
try {
|
|
1054
|
+
fn();
|
|
1055
|
+
} catch {
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
915
1058
|
};
|
|
916
1059
|
|
|
1060
|
+
// src/relay/bundlerEstimator.ts
|
|
1061
|
+
var PafiEstimatorHttpError = class extends Error {
|
|
1062
|
+
status;
|
|
1063
|
+
body;
|
|
1064
|
+
constructor(status, body, message) {
|
|
1065
|
+
super(message ?? `PAFI estimator HTTP ${status}`);
|
|
1066
|
+
this.status = status;
|
|
1067
|
+
this.body = body;
|
|
1068
|
+
}
|
|
1069
|
+
};
|
|
1070
|
+
function createPafiEstimatorClient(config) {
|
|
1071
|
+
const { baseUrl, apiKey, issuerId } = config;
|
|
1072
|
+
if (!baseUrl) throw new Error("createPafiEstimatorClient: baseUrl required");
|
|
1073
|
+
if (!apiKey) throw new Error("createPafiEstimatorClient: apiKey required");
|
|
1074
|
+
if (!issuerId) throw new Error("createPafiEstimatorClient: issuerId required");
|
|
1075
|
+
const fetchImpl = config.fetchImpl ?? globalThis.fetch;
|
|
1076
|
+
if (!fetchImpl) {
|
|
1077
|
+
throw new Error(
|
|
1078
|
+
"createPafiEstimatorClient: no fetch implementation available \u2014 pass `fetchImpl`"
|
|
1079
|
+
);
|
|
1080
|
+
}
|
|
1081
|
+
const url = `${baseUrl.replace(/\/$/, "")}/v1/estimate-gas-fee`;
|
|
1082
|
+
return {
|
|
1083
|
+
async getGasUnits(input) {
|
|
1084
|
+
const body = JSON.stringify({
|
|
1085
|
+
partialUserOp: {
|
|
1086
|
+
sender: input.partialUserOp.sender,
|
|
1087
|
+
// Hex-encode bigint for JSON safety. Sponsor-relayer parses
|
|
1088
|
+
// back to bigint at the DTO layer.
|
|
1089
|
+
nonce: `0x${input.partialUserOp.nonce.toString(16)}`,
|
|
1090
|
+
callData: input.partialUserOp.callData,
|
|
1091
|
+
signature: input.partialUserOp.signature
|
|
1092
|
+
},
|
|
1093
|
+
scenario: input.scenario,
|
|
1094
|
+
contractAddress: input.contractAddress,
|
|
1095
|
+
paymasterAddress: input.paymasterAddress
|
|
1096
|
+
});
|
|
1097
|
+
const res = await fetchImpl(url, {
|
|
1098
|
+
method: "POST",
|
|
1099
|
+
headers: {
|
|
1100
|
+
"content-type": "application/json",
|
|
1101
|
+
authorization: `Bearer ${apiKey}`,
|
|
1102
|
+
"x-issuer-id": issuerId
|
|
1103
|
+
},
|
|
1104
|
+
body
|
|
1105
|
+
});
|
|
1106
|
+
if (!res.ok) {
|
|
1107
|
+
let errBody = null;
|
|
1108
|
+
try {
|
|
1109
|
+
errBody = await res.json();
|
|
1110
|
+
} catch {
|
|
1111
|
+
}
|
|
1112
|
+
throw new PafiEstimatorHttpError(res.status, errBody);
|
|
1113
|
+
}
|
|
1114
|
+
const json = await res.json();
|
|
1115
|
+
return {
|
|
1116
|
+
gasUnits: BigInt(json.gasUnits),
|
|
1117
|
+
source: json.source,
|
|
1118
|
+
expiresAt: json.expiresAt
|
|
1119
|
+
};
|
|
1120
|
+
}
|
|
1121
|
+
};
|
|
1122
|
+
}
|
|
1123
|
+
|
|
917
1124
|
// src/indexer/types.ts
|
|
918
1125
|
var InMemoryCursorStore = class {
|
|
919
1126
|
cursor;
|
|
@@ -1708,8 +1915,59 @@ var IssuerApiHandlers = class _IssuerApiHandlers {
|
|
|
1708
1915
|
}
|
|
1709
1916
|
};
|
|
1710
1917
|
|
|
1711
|
-
// src/api/
|
|
1918
|
+
// src/api/pointTokenDomainResolver.ts
|
|
1712
1919
|
import { getAddress as getAddress6 } from "viem";
|
|
1920
|
+
var NAME_ABI = [
|
|
1921
|
+
{
|
|
1922
|
+
type: "function",
|
|
1923
|
+
name: "name",
|
|
1924
|
+
stateMutability: "view",
|
|
1925
|
+
inputs: [],
|
|
1926
|
+
outputs: [{ type: "string" }]
|
|
1927
|
+
}
|
|
1928
|
+
];
|
|
1929
|
+
var PointTokenDomainResolver = class {
|
|
1930
|
+
provider;
|
|
1931
|
+
overrides;
|
|
1932
|
+
cache = /* @__PURE__ */ new Map();
|
|
1933
|
+
constructor(config) {
|
|
1934
|
+
this.provider = config.provider;
|
|
1935
|
+
this.overrides = /* @__PURE__ */ new Map();
|
|
1936
|
+
if (config.overrides) {
|
|
1937
|
+
for (const [addr, name] of Object.entries(config.overrides)) {
|
|
1938
|
+
this.overrides.set(getAddress6(addr).toLowerCase(), name);
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
async resolve(pointTokenAddress) {
|
|
1943
|
+
const key = getAddress6(pointTokenAddress).toLowerCase();
|
|
1944
|
+
const cached = this.cache.get(key);
|
|
1945
|
+
if (cached !== void 0) return cached;
|
|
1946
|
+
const override = this.overrides.get(key);
|
|
1947
|
+
if (override !== void 0) {
|
|
1948
|
+
this.cache.set(key, override);
|
|
1949
|
+
return override;
|
|
1950
|
+
}
|
|
1951
|
+
const name = await this.provider.readContract({
|
|
1952
|
+
address: pointTokenAddress,
|
|
1953
|
+
abi: NAME_ABI,
|
|
1954
|
+
functionName: "name"
|
|
1955
|
+
});
|
|
1956
|
+
this.cache.set(key, name);
|
|
1957
|
+
return name;
|
|
1958
|
+
}
|
|
1959
|
+
/** Invalidate one address (after deploy / proxy upgrade) or all. */
|
|
1960
|
+
invalidate(pointTokenAddress) {
|
|
1961
|
+
if (pointTokenAddress) {
|
|
1962
|
+
this.cache.delete(getAddress6(pointTokenAddress).toLowerCase());
|
|
1963
|
+
} else {
|
|
1964
|
+
this.cache.clear();
|
|
1965
|
+
}
|
|
1966
|
+
}
|
|
1967
|
+
};
|
|
1968
|
+
|
|
1969
|
+
// src/api/handlers/ptRedeemHandler.ts
|
|
1970
|
+
import { getAddress as getAddress7 } from "viem";
|
|
1713
1971
|
import {
|
|
1714
1972
|
signBurnRequest,
|
|
1715
1973
|
POINT_TOKEN_ABI as POINT_TOKEN_ABI2,
|
|
@@ -1735,10 +1993,9 @@ var PTRedeemHandler = class {
|
|
|
1735
1993
|
relayService;
|
|
1736
1994
|
provider;
|
|
1737
1995
|
feeService;
|
|
1738
|
-
pointTokenAddress;
|
|
1739
1996
|
batchExecutorAddress;
|
|
1740
1997
|
chainId;
|
|
1741
|
-
|
|
1998
|
+
domainResolver;
|
|
1742
1999
|
burnerSignerWallet;
|
|
1743
2000
|
redeemLockDurationMs;
|
|
1744
2001
|
signatureDeadlineSeconds;
|
|
@@ -1775,10 +2032,9 @@ var PTRedeemHandler = class {
|
|
|
1775
2032
|
this.relayService = config.relayService;
|
|
1776
2033
|
this.provider = config.provider;
|
|
1777
2034
|
this.feeService = config.feeService;
|
|
1778
|
-
this.
|
|
1779
|
-
this.batchExecutorAddress = getAddress6(config.batchExecutorAddress);
|
|
2035
|
+
this.batchExecutorAddress = getAddress7(config.batchExecutorAddress);
|
|
1780
2036
|
this.chainId = config.chainId;
|
|
1781
|
-
this.
|
|
2037
|
+
this.domainResolver = config.domainResolver;
|
|
1782
2038
|
this.burnerSignerWallet = config.burnerSignerWallet;
|
|
1783
2039
|
if (this.burnerSignerWallet?.account?.type === "local") {
|
|
1784
2040
|
console.warn("[PAFI] PTRedeemHandler: burnerSignerWallet uses a local (private key) account. Use a KMS-backed signer in production.");
|
|
@@ -1791,7 +2047,7 @@ var PTRedeemHandler = class {
|
|
|
1791
2047
|
}
|
|
1792
2048
|
}
|
|
1793
2049
|
async handle(request) {
|
|
1794
|
-
if (
|
|
2050
|
+
if (getAddress7(request.authenticatedAddress) !== getAddress7(request.userAddress)) {
|
|
1795
2051
|
throw new PTRedeemError(
|
|
1796
2052
|
"UNAUTHORIZED",
|
|
1797
2053
|
`userAddress (${request.userAddress}) does not match authenticated session (${request.authenticatedAddress})`
|
|
@@ -1800,11 +2056,12 @@ var PTRedeemHandler = class {
|
|
|
1800
2056
|
if (request.amount <= 0n) {
|
|
1801
2057
|
throw new PTRedeemError("INVALID_AMOUNT", "redeem amount must be positive");
|
|
1802
2058
|
}
|
|
2059
|
+
const pointTokenAddress = getAddress7(request.pointTokenAddress);
|
|
1803
2060
|
if (this.redemptionService) {
|
|
1804
2061
|
const decision = await this.redemptionService.evaluate(
|
|
1805
2062
|
request.userAddress,
|
|
1806
2063
|
request.amount,
|
|
1807
|
-
|
|
2064
|
+
pointTokenAddress
|
|
1808
2065
|
);
|
|
1809
2066
|
if (!decision.allowed) {
|
|
1810
2067
|
const denial = decision.denial;
|
|
@@ -1818,7 +2075,7 @@ var PTRedeemHandler = class {
|
|
|
1818
2075
|
let burnNonce;
|
|
1819
2076
|
try {
|
|
1820
2077
|
burnNonce = await this.provider.readContract({
|
|
1821
|
-
address:
|
|
2078
|
+
address: pointTokenAddress,
|
|
1822
2079
|
abi: POINT_TOKEN_ABI2,
|
|
1823
2080
|
functionName: "burnRequestNonces",
|
|
1824
2081
|
args: [request.userAddress]
|
|
@@ -1829,32 +2086,50 @@ var PTRedeemHandler = class {
|
|
|
1829
2086
|
`failed to read burnRequestNonces(${request.userAddress}): ${err instanceof Error ? err.message : String(err)}`
|
|
1830
2087
|
);
|
|
1831
2088
|
}
|
|
1832
|
-
const
|
|
1833
|
-
let userNonces = this.inFlightNonces.get(
|
|
2089
|
+
const nonceKey = `${getAddress7(request.userAddress).toLowerCase()}:${pointTokenAddress.toLowerCase()}`;
|
|
2090
|
+
let userNonces = this.inFlightNonces.get(nonceKey);
|
|
1834
2091
|
if (!userNonces) {
|
|
1835
2092
|
userNonces = /* @__PURE__ */ new Set();
|
|
1836
|
-
this.inFlightNonces.set(
|
|
2093
|
+
this.inFlightNonces.set(nonceKey, userNonces);
|
|
1837
2094
|
}
|
|
1838
2095
|
if (userNonces.has(burnNonce)) {
|
|
1839
2096
|
throw new PTRedeemError(
|
|
1840
2097
|
"NONCE_IN_FLIGHT",
|
|
1841
|
-
`A burn request for nonce ${burnNonce} is already in progress for ${request.userAddress}. Retry after the current request completes.`
|
|
2098
|
+
`A burn request for nonce ${burnNonce} is already in progress for ${request.userAddress} on ${pointTokenAddress}. Retry after the current request completes.`
|
|
1842
2099
|
);
|
|
1843
2100
|
}
|
|
1844
2101
|
userNonces.add(burnNonce);
|
|
1845
2102
|
try {
|
|
1846
|
-
return await this._handleAfterNonceLock(request, burnNonce);
|
|
2103
|
+
return await this._handleAfterNonceLock(request, burnNonce, pointTokenAddress);
|
|
1847
2104
|
} finally {
|
|
1848
2105
|
userNonces.delete(burnNonce);
|
|
1849
|
-
if (userNonces.size === 0) this.inFlightNonces.delete(
|
|
2106
|
+
if (userNonces.size === 0) this.inFlightNonces.delete(nonceKey);
|
|
1850
2107
|
}
|
|
1851
2108
|
}
|
|
1852
|
-
async _handleAfterNonceLock(request, burnNonce) {
|
|
2109
|
+
async _handleAfterNonceLock(request, burnNonce, pointTokenAddress) {
|
|
2110
|
+
const previewDeadline = BigInt(
|
|
2111
|
+
Math.floor(this.now() / 1e3) + this.signatureDeadlineSeconds
|
|
2112
|
+
);
|
|
1853
2113
|
let fee;
|
|
1854
2114
|
if (request.feeAmount !== void 0) {
|
|
1855
2115
|
fee = request.feeAmount > 0n ? request.feeAmount : 0n;
|
|
1856
2116
|
} else if (this.feeService) {
|
|
1857
|
-
|
|
2117
|
+
const previewUserOp = this.relayService.previewBurnUserOp({
|
|
2118
|
+
userAddress: request.userAddress,
|
|
2119
|
+
aaNonce: burnNonce,
|
|
2120
|
+
pointTokenAddress,
|
|
2121
|
+
amount: request.amount,
|
|
2122
|
+
deadline: previewDeadline
|
|
2123
|
+
});
|
|
2124
|
+
fee = await this.feeService.estimateGasFee({
|
|
2125
|
+
scenario: "burn",
|
|
2126
|
+
contractAddress: pointTokenAddress,
|
|
2127
|
+
partialUserOp: {
|
|
2128
|
+
sender: previewUserOp.sender,
|
|
2129
|
+
nonce: previewUserOp.nonce,
|
|
2130
|
+
callData: previewUserOp.callData
|
|
2131
|
+
}
|
|
2132
|
+
});
|
|
1858
2133
|
} else {
|
|
1859
2134
|
fee = 0n;
|
|
1860
2135
|
}
|
|
@@ -1867,7 +2142,7 @@ var PTRedeemHandler = class {
|
|
|
1867
2142
|
}
|
|
1868
2143
|
const onChainBalance = await getPointTokenBalance2(
|
|
1869
2144
|
this.provider,
|
|
1870
|
-
|
|
2145
|
+
pointTokenAddress,
|
|
1871
2146
|
request.userAddress
|
|
1872
2147
|
);
|
|
1873
2148
|
if (onChainBalance < request.amount) {
|
|
@@ -1876,13 +2151,12 @@ var PTRedeemHandler = class {
|
|
|
1876
2151
|
`insufficient on-chain PT balance: have ${onChainBalance}, need ${request.amount}`
|
|
1877
2152
|
);
|
|
1878
2153
|
}
|
|
1879
|
-
const deadline =
|
|
1880
|
-
|
|
1881
|
-
);
|
|
2154
|
+
const deadline = previewDeadline;
|
|
2155
|
+
const domainName = await this.domainResolver.resolve(pointTokenAddress);
|
|
1882
2156
|
const domain = {
|
|
1883
|
-
name:
|
|
2157
|
+
name: domainName,
|
|
1884
2158
|
chainId: this.chainId,
|
|
1885
|
-
verifyingContract:
|
|
2159
|
+
verifyingContract: pointTokenAddress
|
|
1886
2160
|
};
|
|
1887
2161
|
const sponsoredBurnAmount = request.amount - fee;
|
|
1888
2162
|
const sponsoredBurnRequest = {
|
|
@@ -1904,14 +2178,14 @@ var PTRedeemHandler = class {
|
|
|
1904
2178
|
request.userAddress,
|
|
1905
2179
|
sponsoredBurnAmount,
|
|
1906
2180
|
this.redeemLockDurationMs,
|
|
1907
|
-
|
|
2181
|
+
pointTokenAddress
|
|
1908
2182
|
);
|
|
1909
2183
|
try {
|
|
1910
2184
|
const sponsoredUserOp = await this.relayService.prepareBurn({
|
|
1911
2185
|
mode: "burnWithSig",
|
|
1912
2186
|
userAddress: request.userAddress,
|
|
1913
2187
|
aaNonce: request.aaNonce,
|
|
1914
|
-
pointTokenAddress
|
|
2188
|
+
pointTokenAddress,
|
|
1915
2189
|
batchExecutorAddress: this.batchExecutorAddress,
|
|
1916
2190
|
burnRequest: sponsoredBurnRequest,
|
|
1917
2191
|
burnerSignature: sponsoredSig,
|
|
@@ -1939,7 +2213,7 @@ var PTRedeemHandler = class {
|
|
|
1939
2213
|
request.userAddress,
|
|
1940
2214
|
request.amount,
|
|
1941
2215
|
this.redeemLockDurationMs,
|
|
1942
|
-
|
|
2216
|
+
pointTokenAddress
|
|
1943
2217
|
);
|
|
1944
2218
|
let fallbackUserOp;
|
|
1945
2219
|
try {
|
|
@@ -1947,7 +2221,7 @@ var PTRedeemHandler = class {
|
|
|
1947
2221
|
mode: "burnWithSig",
|
|
1948
2222
|
userAddress: request.userAddress,
|
|
1949
2223
|
aaNonce: request.aaNonce,
|
|
1950
|
-
pointTokenAddress
|
|
2224
|
+
pointTokenAddress,
|
|
1951
2225
|
batchExecutorAddress: this.batchExecutorAddress,
|
|
1952
2226
|
burnRequest: fallbackBurnRequest,
|
|
1953
2227
|
burnerSignature: fallbackSig,
|
|
@@ -1972,7 +2246,7 @@ var PTRedeemHandler = class {
|
|
|
1972
2246
|
await this.redemptionService.recordSuccessfulInitiate({
|
|
1973
2247
|
user: request.userAddress,
|
|
1974
2248
|
amountPt: request.amount,
|
|
1975
|
-
pointTokenAddress
|
|
2249
|
+
pointTokenAddress,
|
|
1976
2250
|
reservationId: sponsoredLockId
|
|
1977
2251
|
}).catch(() => {
|
|
1978
2252
|
});
|
|
@@ -2095,7 +2369,7 @@ async function handleRedeemStatus(params) {
|
|
|
2095
2369
|
}
|
|
2096
2370
|
|
|
2097
2371
|
// src/api/mobileHandlers.ts
|
|
2098
|
-
import { getAddress as
|
|
2372
|
+
import { getAddress as getAddress8 } from "viem";
|
|
2099
2373
|
import {
|
|
2100
2374
|
ENTRY_POINT_V08,
|
|
2101
2375
|
parseEip7702DelegatedAddress
|
|
@@ -2447,7 +2721,7 @@ async function handleMobileSubmit(params) {
|
|
|
2447
2721
|
if (!entry) {
|
|
2448
2722
|
throw new PendingUserOpNotFoundError(params.lockId);
|
|
2449
2723
|
}
|
|
2450
|
-
if (
|
|
2724
|
+
if (getAddress8(entry.sender) !== getAddress8(params.authenticatedAddress)) {
|
|
2451
2725
|
throw new PendingUserOpForbiddenError(params.lockId);
|
|
2452
2726
|
}
|
|
2453
2727
|
const variant = params.variant ?? "sponsored";
|
|
@@ -2463,7 +2737,7 @@ async function handleMobileSubmit(params) {
|
|
|
2463
2737
|
}
|
|
2464
2738
|
|
|
2465
2739
|
// src/api/handlers/ptClaimHandler.ts
|
|
2466
|
-
import { getAddress as
|
|
2740
|
+
import { getAddress as getAddress9 } from "viem";
|
|
2467
2741
|
import {
|
|
2468
2742
|
decodeBatchExecuteCalls,
|
|
2469
2743
|
getContractAddresses as getContractAddresses3
|
|
@@ -2512,7 +2786,7 @@ var PTClaimHandler = class {
|
|
|
2512
2786
|
};
|
|
2513
2787
|
}
|
|
2514
2788
|
async handle(request) {
|
|
2515
|
-
if (
|
|
2789
|
+
if (getAddress9(request.authenticatedAddress) !== getAddress9(request.userAddress)) {
|
|
2516
2790
|
throw new PTClaimError(
|
|
2517
2791
|
"VALIDATION_FAILED",
|
|
2518
2792
|
`userAddress (${request.userAddress}) does not match authenticated session (${request.authenticatedAddress})`
|
|
@@ -2550,9 +2824,28 @@ var PTClaimHandler = class {
|
|
|
2550
2824
|
const signatureDeadline = BigInt(
|
|
2551
2825
|
Math.floor(this.cfg.now() / 1e3) + this.cfg.signatureDeadlineSeconds
|
|
2552
2826
|
);
|
|
2553
|
-
const
|
|
2827
|
+
const previewUserOp = this.cfg.relayService.previewMintUserOp({
|
|
2828
|
+
userAddress: request.userAddress,
|
|
2829
|
+
aaNonce: request.aaNonce,
|
|
2830
|
+
pointTokenAddress: request.pointTokenAddress,
|
|
2831
|
+
amount: request.amount,
|
|
2832
|
+
deadline: signatureDeadline,
|
|
2833
|
+
mintFeeWrapperAddress: resolvedWrapper
|
|
2834
|
+
});
|
|
2835
|
+
const feeAmount = this.cfg.feeService ? await this.cfg.feeService.estimateGasFee({
|
|
2836
|
+
scenario: resolvedWrapper ? "mint-wrapped" : "mint",
|
|
2837
|
+
contractAddress: request.pointTokenAddress,
|
|
2838
|
+
partialUserOp: {
|
|
2839
|
+
sender: previewUserOp.sender,
|
|
2840
|
+
nonce: previewUserOp.nonce,
|
|
2841
|
+
callData: previewUserOp.callData
|
|
2842
|
+
}
|
|
2843
|
+
}) : 0n;
|
|
2844
|
+
const domainName = await this.cfg.domainResolver.resolve(
|
|
2845
|
+
request.pointTokenAddress
|
|
2846
|
+
);
|
|
2554
2847
|
const domain = {
|
|
2555
|
-
name:
|
|
2848
|
+
name: domainName,
|
|
2556
2849
|
chainId: request.chainId,
|
|
2557
2850
|
verifyingContract: request.pointTokenAddress
|
|
2558
2851
|
};
|
|
@@ -2767,7 +3060,7 @@ import {
|
|
|
2767
3060
|
getContractAddresses as getContractAddresses5,
|
|
2768
3061
|
serializeUserOpToJsonRpc as serializeUserOpToJsonRpc2
|
|
2769
3062
|
} from "@pafi-dev/core";
|
|
2770
|
-
import { getAddress as
|
|
3063
|
+
import { getAddress as getAddress10 } from "viem";
|
|
2771
3064
|
var DEFAULT_DELEGATE_GAS = {
|
|
2772
3065
|
callGasLimit: 100000n,
|
|
2773
3066
|
verificationGasLimit: 150000n,
|
|
@@ -2854,7 +3147,7 @@ async function handleDelegateSubmit(params) {
|
|
|
2854
3147
|
if (!entry) {
|
|
2855
3148
|
throw new PendingUserOpNotFoundError(params.lockId);
|
|
2856
3149
|
}
|
|
2857
|
-
if (
|
|
3150
|
+
if (getAddress10(entry.sender) !== getAddress10(params.authenticatedAddress)) {
|
|
2858
3151
|
throw new PendingUserOpForbiddenError(params.lockId);
|
|
2859
3152
|
}
|
|
2860
3153
|
if (!entry.eip7702Auth) {
|
|
@@ -2875,7 +3168,7 @@ async function handleDelegateSubmit(params) {
|
|
|
2875
3168
|
|
|
2876
3169
|
// src/api/issuerApiAdapter.ts
|
|
2877
3170
|
import { randomUUID } from "crypto";
|
|
2878
|
-
import { getAddress as
|
|
3171
|
+
import { getAddress as getAddress11 } from "viem";
|
|
2879
3172
|
import {
|
|
2880
3173
|
buildAndSignSponsorAuth,
|
|
2881
3174
|
decodeBatchExecuteCalls as decodeBatchExecuteCalls3,
|
|
@@ -2940,7 +3233,7 @@ var IssuerApiAdapter = class {
|
|
|
2940
3233
|
async pools(authenticatedAddress, chainId, pointTokenAddress) {
|
|
2941
3234
|
const result = await this.cfg.issuerService.api.handlePools(
|
|
2942
3235
|
authenticatedAddress,
|
|
2943
|
-
{ chainId, pointTokenAddress:
|
|
3236
|
+
{ chainId, pointTokenAddress: getAddress11(pointTokenAddress) }
|
|
2944
3237
|
);
|
|
2945
3238
|
return { pools: result.pools };
|
|
2946
3239
|
}
|
|
@@ -2949,8 +3242,8 @@ var IssuerApiAdapter = class {
|
|
|
2949
3242
|
authenticatedAddress,
|
|
2950
3243
|
{
|
|
2951
3244
|
chainId,
|
|
2952
|
-
userAddress:
|
|
2953
|
-
pointTokenAddress:
|
|
3245
|
+
userAddress: getAddress11(userAddress),
|
|
3246
|
+
pointTokenAddress: getAddress11(pointTokenAddress)
|
|
2954
3247
|
}
|
|
2955
3248
|
);
|
|
2956
3249
|
return {
|
|
@@ -2971,7 +3264,7 @@ var IssuerApiAdapter = class {
|
|
|
2971
3264
|
"ptClaimHandler",
|
|
2972
3265
|
"claim"
|
|
2973
3266
|
);
|
|
2974
|
-
const pointTokenAddress =
|
|
3267
|
+
const pointTokenAddress = getAddress11(input.pointTokenAddress);
|
|
2975
3268
|
const result = await ptClaimHandler.handle({
|
|
2976
3269
|
authenticatedAddress: input.authenticatedAddress,
|
|
2977
3270
|
userAddress: input.authenticatedAddress,
|
|
@@ -2998,9 +3291,11 @@ var IssuerApiAdapter = class {
|
|
|
2998
3291
|
}
|
|
2999
3292
|
async redeem(input) {
|
|
3000
3293
|
this.assertRedeemHandler();
|
|
3294
|
+
const pointTokenAddress = getAddress11(input.pointTokenAddress);
|
|
3001
3295
|
const response = await this.cfg.ptRedeemHandler.handle({
|
|
3002
3296
|
userAddress: input.authenticatedAddress,
|
|
3003
3297
|
authenticatedAddress: input.authenticatedAddress,
|
|
3298
|
+
pointTokenAddress,
|
|
3004
3299
|
amount: input.amount,
|
|
3005
3300
|
aaNonce: input.aaNonce,
|
|
3006
3301
|
chainId: input.chainId
|
|
@@ -3066,7 +3361,7 @@ var IssuerApiAdapter = class {
|
|
|
3066
3361
|
"ptClaimHandler",
|
|
3067
3362
|
"claimPrepare"
|
|
3068
3363
|
);
|
|
3069
|
-
const pointTokenAddress =
|
|
3364
|
+
const pointTokenAddress = getAddress11(input.pointTokenAddress);
|
|
3070
3365
|
const claimResult = await ptClaimHandler.handle({
|
|
3071
3366
|
authenticatedAddress: input.authenticatedAddress,
|
|
3072
3367
|
userAddress: input.authenticatedAddress,
|
|
@@ -3112,10 +3407,11 @@ var IssuerApiAdapter = class {
|
|
|
3112
3407
|
}
|
|
3113
3408
|
async redeemPrepare(input) {
|
|
3114
3409
|
this.assertRedeemHandler();
|
|
3115
|
-
const pointTokenAddress =
|
|
3410
|
+
const pointTokenAddress = getAddress11(input.pointTokenAddress);
|
|
3116
3411
|
const redeemResponse = await this.cfg.ptRedeemHandler.handle({
|
|
3117
3412
|
userAddress: input.authenticatedAddress,
|
|
3118
3413
|
authenticatedAddress: input.authenticatedAddress,
|
|
3414
|
+
pointTokenAddress,
|
|
3119
3415
|
amount: input.amount,
|
|
3120
3416
|
aaNonce: input.aaNonce,
|
|
3121
3417
|
chainId: input.chainId
|
|
@@ -3863,7 +4159,7 @@ var PafiBackendClient = class {
|
|
|
3863
4159
|
};
|
|
3864
4160
|
|
|
3865
4161
|
// src/config.ts
|
|
3866
|
-
import { getAddress as
|
|
4162
|
+
import { getAddress as getAddress12 } from "viem";
|
|
3867
4163
|
import { getContractAddresses as getContractAddresses7 } from "@pafi-dev/core";
|
|
3868
4164
|
|
|
3869
4165
|
// src/redemption/evaluator.ts
|
|
@@ -4192,7 +4488,7 @@ function createIssuerService(config) {
|
|
|
4192
4488
|
"createIssuerService: at least one of pointTokenAddress / pointTokenAddresses is required"
|
|
4193
4489
|
);
|
|
4194
4490
|
}
|
|
4195
|
-
const tokenAddresses = rawAddresses.map((a) =>
|
|
4491
|
+
const tokenAddresses = rawAddresses.map((a) => getAddress12(a));
|
|
4196
4492
|
const ledger = config.ledger;
|
|
4197
4493
|
const sessionStore = config.sessionStore ?? new MemorySessionStore();
|
|
4198
4494
|
const policy = config.policy ?? new DefaultPolicyEngine({ ledger });
|
|
@@ -4311,7 +4607,7 @@ function createIssuerService(config) {
|
|
|
4311
4607
|
}
|
|
4312
4608
|
|
|
4313
4609
|
// src/issuer-state/validator.ts
|
|
4314
|
-
import { getAddress as
|
|
4610
|
+
import { getAddress as getAddress13 } from "viem";
|
|
4315
4611
|
import {
|
|
4316
4612
|
POINT_TOKEN_ABI as POINT_TOKEN_ABI3,
|
|
4317
4613
|
issuerRegistryAbi,
|
|
@@ -4343,7 +4639,7 @@ var IssuerStateValidator = class _IssuerStateValidator {
|
|
|
4343
4639
|
*/
|
|
4344
4640
|
invalidate(pointToken) {
|
|
4345
4641
|
if (pointToken) {
|
|
4346
|
-
const key =
|
|
4642
|
+
const key = getAddress13(pointToken);
|
|
4347
4643
|
this.pointTokenIssuerCache.delete(key);
|
|
4348
4644
|
this.stateCache.delete(key);
|
|
4349
4645
|
this.inflight.delete(key);
|
|
@@ -4358,7 +4654,7 @@ var IssuerStateValidator = class _IssuerStateValidator {
|
|
|
4358
4654
|
* The issuer field is set at `initialize()` and never changes.
|
|
4359
4655
|
*/
|
|
4360
4656
|
async getIssuerAddressForPointToken(pointToken) {
|
|
4361
|
-
const key =
|
|
4657
|
+
const key = getAddress13(pointToken);
|
|
4362
4658
|
const cached = this.pointTokenIssuerCache.get(key);
|
|
4363
4659
|
if (cached) return cached;
|
|
4364
4660
|
const issuer = await this.provider.readContract({
|
|
@@ -4366,15 +4662,15 @@ var IssuerStateValidator = class _IssuerStateValidator {
|
|
|
4366
4662
|
abi: POINT_TOKEN_ABI3,
|
|
4367
4663
|
functionName: "issuer"
|
|
4368
4664
|
});
|
|
4369
|
-
this.pointTokenIssuerCache.set(key,
|
|
4370
|
-
return
|
|
4665
|
+
this.pointTokenIssuerCache.set(key, getAddress13(issuer));
|
|
4666
|
+
return getAddress13(issuer);
|
|
4371
4667
|
}
|
|
4372
4668
|
/**
|
|
4373
4669
|
* Read registry record + totalSupply, with 30s cache and in-flight
|
|
4374
4670
|
* deduplication. Does NOT throw on inactive/missing — returns raw state.
|
|
4375
4671
|
*/
|
|
4376
4672
|
async getIssuerState(pointToken) {
|
|
4377
|
-
const tokenAddr =
|
|
4673
|
+
const tokenAddr = getAddress13(pointToken);
|
|
4378
4674
|
const now = Date.now();
|
|
4379
4675
|
const cached = this.stateCache.get(tokenAddr);
|
|
4380
4676
|
if (cached && cached.expiresAt > now) return cached.value;
|
|
@@ -4517,7 +4813,7 @@ var MemoryRedemptionHistoryStore = class {
|
|
|
4517
4813
|
};
|
|
4518
4814
|
|
|
4519
4815
|
// src/index.ts
|
|
4520
|
-
var PAFI_ISSUER_SDK_VERSION = true ? "0.
|
|
4816
|
+
var PAFI_ISSUER_SDK_VERSION = true ? "0.21.0" : "dev";
|
|
4521
4817
|
export {
|
|
4522
4818
|
AdapterMisconfiguredError,
|
|
4523
4819
|
AuthError,
|
|
@@ -4549,12 +4845,14 @@ export {
|
|
|
4549
4845
|
PTRedeemHandler,
|
|
4550
4846
|
PafiBackendClient,
|
|
4551
4847
|
PafiBackendError,
|
|
4848
|
+
PafiEstimatorHttpError,
|
|
4552
4849
|
PafiSdkError,
|
|
4553
4850
|
PendingUserOpForbiddenError,
|
|
4554
4851
|
PendingUserOpNotFoundError,
|
|
4555
4852
|
PerpDepositError,
|
|
4556
4853
|
PerpDepositHandler,
|
|
4557
4854
|
PointIndexer,
|
|
4855
|
+
PointTokenDomainResolver,
|
|
4558
4856
|
PolicyProvider,
|
|
4559
4857
|
REDEMPTION_HISTORY_WINDOW_SEC,
|
|
4560
4858
|
RedemptionService,
|
|
@@ -4569,6 +4867,7 @@ export {
|
|
|
4569
4867
|
buildSdkErrorBody,
|
|
4570
4868
|
createIssuerService,
|
|
4571
4869
|
createNativePtQuoter,
|
|
4870
|
+
createPafiEstimatorClient,
|
|
4572
4871
|
createSdkErrorMapper,
|
|
4573
4872
|
createSubgraphNativeUsdtQuoter,
|
|
4574
4873
|
createSubgraphPoolsProvider,
|