@pafi-dev/issuer 0.3.0-beta.8 → 0.3.0-beta.9
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 +0 -212
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -193
- package/dist/index.d.ts +3 -193
- package/dist/index.js +0 -211
- package/dist/index.js.map +1 -1
- package/package.json +11 -11
package/dist/index.js
CHANGED
|
@@ -1807,37 +1807,16 @@ var PafiBackendError = class extends Error {
|
|
|
1807
1807
|
code;
|
|
1808
1808
|
httpStatus;
|
|
1809
1809
|
details;
|
|
1810
|
-
/**
|
|
1811
|
-
* Seconds to wait before retry. Populated from the server body
|
|
1812
|
-
* (e.g. rate limit returns the number of seconds until UTC midnight).
|
|
1813
|
-
*/
|
|
1814
1810
|
retryAfter;
|
|
1815
|
-
/**
|
|
1816
|
-
* `safeToRetry` as reported by the server body. Prefer this over the
|
|
1817
|
-
* code-based heuristic when available — the server knows more about
|
|
1818
|
-
* whether the same request will succeed on retry.
|
|
1819
|
-
*/
|
|
1820
1811
|
serverSafeToRetry;
|
|
1821
|
-
/**
|
|
1822
|
-
* Whether the caller can safely retry the same request.
|
|
1823
|
-
*
|
|
1824
|
-
* If the server provided `safeToRetry` in the body, trust that.
|
|
1825
|
-
* Otherwise fall back to a code-based heuristic.
|
|
1826
|
-
*/
|
|
1827
1812
|
get safeToRetry() {
|
|
1828
1813
|
if (this.serverSafeToRetry !== void 0) return this.serverSafeToRetry;
|
|
1829
1814
|
switch (this.code) {
|
|
1830
|
-
// Transient infra
|
|
1831
|
-
case "PAYMASTER_UNAVAILABLE":
|
|
1832
|
-
case "PAYMASTER_TIMEOUT":
|
|
1833
1815
|
case "RATE_LIMITER_UNAVAILABLE":
|
|
1834
|
-
case "KMS_UNAVAILABLE":
|
|
1835
|
-
case "SPONSOR_AUTH_SIGNING_FAILED":
|
|
1836
1816
|
case "INTERNAL_ERROR":
|
|
1837
1817
|
case "TIMEOUT":
|
|
1838
1818
|
case "NETWORK_ERROR":
|
|
1839
1819
|
return true;
|
|
1840
|
-
// Rate-limited — safe to retry after retryAfter window
|
|
1841
1820
|
case "RATE_LIMIT_EXCEEDED":
|
|
1842
1821
|
case "RATE_LIMIT_EXCEEDED_DAILY":
|
|
1843
1822
|
case "RATE_LIMIT_EXCEEDED_PER_USER":
|
|
@@ -1849,195 +1828,6 @@ var PafiBackendError = class extends Error {
|
|
|
1849
1828
|
}
|
|
1850
1829
|
};
|
|
1851
1830
|
|
|
1852
|
-
// src/pafi-backend/pafiBackendClient.ts
|
|
1853
|
-
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
1854
|
-
var RETRY_DEFAULTS = {
|
|
1855
|
-
maxAttempts: 1,
|
|
1856
|
-
initialDelayMs: 500,
|
|
1857
|
-
maxDelayMs: 1e4,
|
|
1858
|
-
maxRetryAfterMs: 3e4
|
|
1859
|
-
};
|
|
1860
|
-
var PafiBackendClient = class {
|
|
1861
|
-
url;
|
|
1862
|
-
issuerId;
|
|
1863
|
-
apiKey;
|
|
1864
|
-
fetchImpl;
|
|
1865
|
-
timeoutMs;
|
|
1866
|
-
retry;
|
|
1867
|
-
constructor(config) {
|
|
1868
|
-
if (!config.url) {
|
|
1869
|
-
throw new Error("PafiBackendClient: url is required");
|
|
1870
|
-
}
|
|
1871
|
-
if (!config.issuerId) {
|
|
1872
|
-
throw new Error("PafiBackendClient: issuerId is required");
|
|
1873
|
-
}
|
|
1874
|
-
if (!config.apiKey) {
|
|
1875
|
-
throw new Error("PafiBackendClient: apiKey is required");
|
|
1876
|
-
}
|
|
1877
|
-
this.url = config.url.replace(/\/+$/, "");
|
|
1878
|
-
this.issuerId = config.issuerId;
|
|
1879
|
-
this.apiKey = config.apiKey;
|
|
1880
|
-
this.fetchImpl = config.fetchImpl ?? globalThis.fetch;
|
|
1881
|
-
this.timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
1882
|
-
this.retry = { ...RETRY_DEFAULTS, ...config.retry ?? {} };
|
|
1883
|
-
if (!this.fetchImpl) {
|
|
1884
|
-
throw new Error(
|
|
1885
|
-
"PafiBackendClient: no fetch implementation available \u2014 pass `fetchImpl` or run on Node 18+"
|
|
1886
|
-
);
|
|
1887
|
-
}
|
|
1888
|
-
if (this.retry.maxAttempts < 1) {
|
|
1889
|
-
throw new Error("PafiBackendClient: retry.maxAttempts must be >= 1");
|
|
1890
|
-
}
|
|
1891
|
-
}
|
|
1892
|
-
/**
|
|
1893
|
-
* Request a SponsorAuth signature from PAFI sponsor-relayer (beta.8+).
|
|
1894
|
-
*
|
|
1895
|
-
* The relayer:
|
|
1896
|
-
* 1. Authenticates user (JWT) + issuer (API key)
|
|
1897
|
-
* 2. Per-(user, scenario) rate limit + per-issuer daily budget
|
|
1898
|
-
* 3. Scenario-specific intent validation (mint cap, KYC, etc.)
|
|
1899
|
-
* 4. Allocates nonce + signs SponsorAuth payload via KMS PAFI key
|
|
1900
|
-
* 5. Returns `{ sponsorAuth, payload }` for the FE to forward to
|
|
1901
|
-
* Privy's `signUserOperation({ sponsorAuth, payload })`.
|
|
1902
|
-
*
|
|
1903
|
-
* Retries on transient failures (5xx, timeouts, KMS unavailable,
|
|
1904
|
-
* rate-limit-with-retryAfter). 4xx that are not `safeToRetry` fail fast.
|
|
1905
|
-
*
|
|
1906
|
-
* See `pafi-backend/docs/SPONSOR_AUTH_DESIGN.md` for the full spec.
|
|
1907
|
-
*
|
|
1908
|
-
* @throws PafiBackendError on final failure after exhausting retries
|
|
1909
|
-
*/
|
|
1910
|
-
async requestSponsorAuth(req) {
|
|
1911
|
-
return this.postWithRetry(
|
|
1912
|
-
"/sponsor-auth",
|
|
1913
|
-
req
|
|
1914
|
-
);
|
|
1915
|
-
}
|
|
1916
|
-
/**
|
|
1917
|
-
* @deprecated Coinbase paymaster path — replaced by `requestSponsorAuth`
|
|
1918
|
-
* in beta.8. Will be removed in 1.0. Migrate by:
|
|
1919
|
-
* 1. Switch to `requestSponsorAuth` returning `{ sponsorAuth, payload }`
|
|
1920
|
-
* 2. Pass both to Privy `signUserOperation` instead of merging
|
|
1921
|
-
* paymasterData into the UserOp callData
|
|
1922
|
-
*/
|
|
1923
|
-
async requestSponsorship(req) {
|
|
1924
|
-
return this.postWithRetry(
|
|
1925
|
-
"/paymaster/sponsor",
|
|
1926
|
-
req
|
|
1927
|
-
);
|
|
1928
|
-
}
|
|
1929
|
-
// -------------------------------------------------------------------------
|
|
1930
|
-
// Internals
|
|
1931
|
-
// -------------------------------------------------------------------------
|
|
1932
|
-
async postWithRetry(path, body) {
|
|
1933
|
-
let lastError;
|
|
1934
|
-
for (let attempt = 1; attempt <= this.retry.maxAttempts; attempt++) {
|
|
1935
|
-
try {
|
|
1936
|
-
return await this.post(path, body);
|
|
1937
|
-
} catch (err) {
|
|
1938
|
-
if (!(err instanceof PafiBackendError)) throw err;
|
|
1939
|
-
lastError = err;
|
|
1940
|
-
const isLastAttempt = attempt >= this.retry.maxAttempts;
|
|
1941
|
-
if (isLastAttempt || !err.safeToRetry) throw err;
|
|
1942
|
-
const delay = this.computeBackoff(attempt, err.retryAfter);
|
|
1943
|
-
if (delay === null) throw err;
|
|
1944
|
-
await this.sleep(delay);
|
|
1945
|
-
}
|
|
1946
|
-
}
|
|
1947
|
-
throw lastError;
|
|
1948
|
-
}
|
|
1949
|
-
/**
|
|
1950
|
-
* Pick the delay before the next retry.
|
|
1951
|
-
* - If the server sent `retryAfter` (seconds), honor it (capped by
|
|
1952
|
-
* `maxRetryAfterMs`) — returns null if the server wait exceeds the
|
|
1953
|
-
* cap, signalling the caller should give up.
|
|
1954
|
-
* - Otherwise: exponential backoff with ±20% jitter, capped at
|
|
1955
|
-
* `maxDelayMs`.
|
|
1956
|
-
*/
|
|
1957
|
-
computeBackoff(attempt, retryAfter) {
|
|
1958
|
-
if (retryAfter !== void 0) {
|
|
1959
|
-
const serverMs = retryAfter * 1e3;
|
|
1960
|
-
if (serverMs > this.retry.maxRetryAfterMs) return null;
|
|
1961
|
-
return serverMs;
|
|
1962
|
-
}
|
|
1963
|
-
const exp = this.retry.initialDelayMs * 2 ** (attempt - 1);
|
|
1964
|
-
const capped = Math.min(exp, this.retry.maxDelayMs);
|
|
1965
|
-
const jitter = capped * (0.8 + Math.random() * 0.4);
|
|
1966
|
-
return Math.round(jitter);
|
|
1967
|
-
}
|
|
1968
|
-
sleep(ms) {
|
|
1969
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1970
|
-
}
|
|
1971
|
-
async post(path, body) {
|
|
1972
|
-
const controller = new AbortController();
|
|
1973
|
-
const timeoutId = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
1974
|
-
let response;
|
|
1975
|
-
try {
|
|
1976
|
-
response = await this.fetchImpl(`${this.url}${path}`, {
|
|
1977
|
-
method: "POST",
|
|
1978
|
-
headers: {
|
|
1979
|
-
"Content-Type": "application/json",
|
|
1980
|
-
"Authorization": `Bearer ${this.apiKey}`,
|
|
1981
|
-
"X-Issuer-Id": this.issuerId
|
|
1982
|
-
},
|
|
1983
|
-
body: JSON.stringify(body, this.bigintReplacer),
|
|
1984
|
-
signal: controller.signal
|
|
1985
|
-
});
|
|
1986
|
-
} catch (err) {
|
|
1987
|
-
if (err.name === "AbortError") {
|
|
1988
|
-
throw new PafiBackendError(
|
|
1989
|
-
"TIMEOUT",
|
|
1990
|
-
`PAFI Backend request timed out after ${this.timeoutMs}ms`,
|
|
1991
|
-
0
|
|
1992
|
-
);
|
|
1993
|
-
}
|
|
1994
|
-
throw new PafiBackendError(
|
|
1995
|
-
"NETWORK_ERROR",
|
|
1996
|
-
`PAFI Backend unreachable: ${err.message}`,
|
|
1997
|
-
0
|
|
1998
|
-
);
|
|
1999
|
-
} finally {
|
|
2000
|
-
clearTimeout(timeoutId);
|
|
2001
|
-
}
|
|
2002
|
-
const text = await response.text();
|
|
2003
|
-
if (!response.ok) {
|
|
2004
|
-
let code = "INTERNAL_ERROR";
|
|
2005
|
-
let message = text || response.statusText;
|
|
2006
|
-
let details;
|
|
2007
|
-
let retryAfter;
|
|
2008
|
-
let serverSafeToRetry;
|
|
2009
|
-
try {
|
|
2010
|
-
const parsed = JSON.parse(text);
|
|
2011
|
-
code = parsed.code ?? code;
|
|
2012
|
-
message = parsed.message ?? message;
|
|
2013
|
-
details = parsed.details;
|
|
2014
|
-
if (typeof parsed.retryAfter === "number") retryAfter = parsed.retryAfter;
|
|
2015
|
-
if (typeof parsed.safeToRetry === "boolean") serverSafeToRetry = parsed.safeToRetry;
|
|
2016
|
-
} catch {
|
|
2017
|
-
}
|
|
2018
|
-
throw new PafiBackendError(code, message, response.status, details, {
|
|
2019
|
-
...retryAfter !== void 0 ? { retryAfter } : {},
|
|
2020
|
-
...serverSafeToRetry !== void 0 ? { safeToRetry: serverSafeToRetry } : {}
|
|
2021
|
-
});
|
|
2022
|
-
}
|
|
2023
|
-
return JSON.parse(text, this.bigintReviver);
|
|
2024
|
-
}
|
|
2025
|
-
/** JSON replacer that stringifies bigints. Paired with bigintReviver. */
|
|
2026
|
-
bigintReplacer = (_key, value) => {
|
|
2027
|
-
return typeof value === "bigint" ? value.toString() : value;
|
|
2028
|
-
};
|
|
2029
|
-
/**
|
|
2030
|
-
* JSON reviver that coerces specific numeric-string fields back to
|
|
2031
|
-
* bigint. The server must send these fields as decimal strings.
|
|
2032
|
-
*/
|
|
2033
|
-
bigintReviver = (key, value) => {
|
|
2034
|
-
if (typeof value === "string" && (key.endsWith("GasLimit") || key === "nonce" || key === "callGasLimit" || key === "verificationGasLimit" || key === "preVerificationGas" || key === "maxFeePerGas" || key === "maxPriorityFeePerGas" || key === "paymasterVerificationGasLimit" || key === "paymasterPostOpGasLimit") && /^\d+$/.test(value)) {
|
|
2035
|
-
return BigInt(value);
|
|
2036
|
-
}
|
|
2037
|
-
return value;
|
|
2038
|
-
};
|
|
2039
|
-
};
|
|
2040
|
-
|
|
2041
1831
|
// src/config.ts
|
|
2042
1832
|
import { getAddress as getAddress9 } from "viem";
|
|
2043
1833
|
function createIssuerService(config) {
|
|
@@ -2149,7 +1939,6 @@ export {
|
|
|
2149
1939
|
PAFI_ISSUER_SDK_VERSION,
|
|
2150
1940
|
PTRedeemError,
|
|
2151
1941
|
PTRedeemHandler,
|
|
2152
|
-
PafiBackendClient,
|
|
2153
1942
|
PafiBackendError,
|
|
2154
1943
|
PointIndexer,
|
|
2155
1944
|
PrivateKeySigner,
|