@pafi-dev/issuer 0.5.11 → 0.5.13

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 CHANGED
@@ -45,6 +45,7 @@ __export(index_exports, {
45
45
  TopUpRedemptionHandler: () => TopUpRedemptionHandler,
46
46
  authenticateRequest: () => authenticateRequest,
47
47
  createIssuerService: () => createIssuerService,
48
+ createNativePtQuoter: () => createNativePtQuoter,
48
49
  createSubgraphNativeUsdtQuoter: () => createSubgraphNativeUsdtQuoter,
49
50
  createSubgraphPoolsProvider: () => createSubgraphPoolsProvider,
50
51
  serializeEntryToJsonRpc: () => serializeEntryToJsonRpc
@@ -1776,6 +1777,110 @@ function toUsdtPerNative(priceFloat, usdtDecimals) {
1776
1777
  return BigInt(whole + padded);
1777
1778
  }
1778
1779
 
1780
+ // src/pools/nativePtQuoter.ts
1781
+ var import_viem10 = require("viem");
1782
+ var CHAINLINK_ABI = (0, import_viem10.parseAbi)([
1783
+ "function latestRoundData() external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)"
1784
+ ]);
1785
+ var CHAINLINK_MAX_AGE_S = 3600n;
1786
+ var POOL_PRICE_QUERY = `
1787
+ query GetPoolPrice($id: ID!) {
1788
+ pafiToken(id: $id) {
1789
+ pool {
1790
+ token0 { id }
1791
+ token1 { id }
1792
+ token0Price
1793
+ token1Price
1794
+ }
1795
+ }
1796
+ }
1797
+ `;
1798
+ function createNativePtQuoter(config) {
1799
+ const {
1800
+ provider,
1801
+ pointTokenAddress,
1802
+ chainlinkFeedAddress = "0x71041dddad3595F9CEd3DcCFBe3D1F4b0a16Bb70",
1803
+ subgraphUrl = import_core6.PAFI_SUBGRAPH_URL,
1804
+ cacheTtlMs = 3e4,
1805
+ fallbackEthPriceUsd = 3e3,
1806
+ fallbackPtPriceUsdt = 0.1,
1807
+ fetchImpl = globalThis.fetch,
1808
+ now = () => Date.now()
1809
+ } = config;
1810
+ let ethPriceCache;
1811
+ let ptPriceCache;
1812
+ async function getEthPrice8dec() {
1813
+ const ts = now();
1814
+ if (ethPriceCache && ethPriceCache.expiresAt > ts) return ethPriceCache.value;
1815
+ try {
1816
+ const result = await provider.readContract({
1817
+ address: chainlinkFeedAddress,
1818
+ abi: CHAINLINK_ABI,
1819
+ functionName: "latestRoundData"
1820
+ });
1821
+ const answer = result[1];
1822
+ const updatedAt = result[3];
1823
+ if (answer <= 0n) throw new Error("Chainlink: non-positive price");
1824
+ const ageS = BigInt(Math.floor(ts / 1e3)) - updatedAt;
1825
+ if (ageS > CHAINLINK_MAX_AGE_S) {
1826
+ throw new Error(`Chainlink: price stale by ${ageS}s`);
1827
+ }
1828
+ ethPriceCache = { value: answer, expiresAt: ts + cacheTtlMs };
1829
+ return answer;
1830
+ } catch (err) {
1831
+ console.warn("[nativePtQuoter] Chainlink unavailable, using fallback:", err.message);
1832
+ return BigInt(Math.round(fallbackEthPriceUsd * 1e8));
1833
+ }
1834
+ }
1835
+ async function getPtPerUsdt18dec() {
1836
+ const ts = now();
1837
+ if (ptPriceCache && ptPriceCache.expiresAt > ts) return ptPriceCache.value;
1838
+ try {
1839
+ const response = await fetchImpl(subgraphUrl, {
1840
+ method: "POST",
1841
+ headers: { "Content-Type": "application/json" },
1842
+ body: JSON.stringify({
1843
+ query: POOL_PRICE_QUERY,
1844
+ variables: { id: pointTokenAddress.toLowerCase() }
1845
+ })
1846
+ });
1847
+ if (!response.ok) throw new Error(`subgraph HTTP ${response.status}`);
1848
+ const json = await response.json();
1849
+ if (json.errors?.length) throw new Error(json.errors.map((e) => e.message).join("; "));
1850
+ const pool = json.data?.pafiToken?.pool;
1851
+ if (!pool) throw new Error("pafiToken or pool not found in subgraph");
1852
+ const isPtToken0 = pool.token0.id.toLowerCase() === pointTokenAddress.toLowerCase();
1853
+ const ptPerUsdtStr = isPtToken0 ? pool.token0Price : pool.token1Price;
1854
+ if (!ptPerUsdtStr || Number(ptPerUsdtStr) <= 0) {
1855
+ throw new Error(`invalid PT/USDT price from subgraph: ${ptPerUsdtStr}`);
1856
+ }
1857
+ const raw = parseBigDecimalTo18(ptPerUsdtStr);
1858
+ if (raw === 0n) throw new Error(`pool price parsed to zero: ${ptPerUsdtStr}`);
1859
+ const value = 10n ** 24n / raw;
1860
+ ptPriceCache = { value, expiresAt: ts + cacheTtlMs };
1861
+ return value;
1862
+ } catch (err) {
1863
+ console.warn("[nativePtQuoter] subgraph unavailable, using fallback:", err.message);
1864
+ const ptPerUsdtHuman = 1 / fallbackPtPriceUsdt;
1865
+ return parseBigDecimalTo18(ptPerUsdtHuman.toFixed(18));
1866
+ }
1867
+ }
1868
+ return async (amountNative) => {
1869
+ if (amountNative === 0n) return 0n;
1870
+ const [ethPrice8dec, ptPerUsdt18dec] = await Promise.all([
1871
+ getEthPrice8dec(),
1872
+ getPtPerUsdt18dec()
1873
+ ]);
1874
+ return amountNative * ethPrice8dec * ptPerUsdt18dec / 10n ** 26n;
1875
+ };
1876
+ }
1877
+ function parseBigDecimalTo18(s) {
1878
+ const SCALE = 18;
1879
+ const [whole = "0", frac = ""] = s.split(".");
1880
+ const padded = (frac + "0".repeat(SCALE)).slice(0, SCALE);
1881
+ return BigInt(whole + padded);
1882
+ }
1883
+
1779
1884
  // src/balance/balanceAggregator.ts
1780
1885
  var import_core7 = require("@pafi-dev/core");
1781
1886
  var BalanceAggregator = class {
@@ -1981,7 +2086,7 @@ var PafiBackendClient = class {
1981
2086
  };
1982
2087
 
1983
2088
  // src/config.ts
1984
- var import_viem10 = require("viem");
2089
+ var import_viem11 = require("viem");
1985
2090
  var import_core8 = require("@pafi-dev/core");
1986
2091
  function createIssuerService(config) {
1987
2092
  if (!config.provider) {
@@ -2002,7 +2107,7 @@ function createIssuerService(config) {
2002
2107
  "createIssuerService: at least one of pointTokenAddress / pointTokenAddresses is required"
2003
2108
  );
2004
2109
  }
2005
- const tokenAddresses = rawAddresses.map((a) => (0, import_viem10.getAddress)(a));
2110
+ const tokenAddresses = rawAddresses.map((a) => (0, import_viem11.getAddress)(a));
2006
2111
  const ledger = config.ledger;
2007
2112
  const sessionStore = config.sessionStore ?? new MemorySessionStore();
2008
2113
  const policy = config.policy ?? new DefaultPolicyEngine({ ledger });
@@ -2119,7 +2224,7 @@ function serializeEntryToJsonRpc(entry, signature) {
2119
2224
  }
2120
2225
 
2121
2226
  // src/issuer-state/validator.ts
2122
- var import_viem11 = require("viem");
2227
+ var import_viem12 = require("viem");
2123
2228
  var import_core10 = require("@pafi-dev/core");
2124
2229
 
2125
2230
  // src/issuer-state/types.ts
@@ -2160,7 +2265,7 @@ var IssuerStateValidator = class _IssuerStateValidator {
2160
2265
  */
2161
2266
  invalidate(pointToken) {
2162
2267
  if (pointToken) {
2163
- const key = (0, import_viem11.getAddress)(pointToken);
2268
+ const key = (0, import_viem12.getAddress)(pointToken);
2164
2269
  this.pointTokenIssuerCache.delete(key);
2165
2270
  this.stateCache.delete(key);
2166
2271
  this.inflight.delete(key);
@@ -2175,7 +2280,7 @@ var IssuerStateValidator = class _IssuerStateValidator {
2175
2280
  * The issuer field is set at `initialize()` and never changes.
2176
2281
  */
2177
2282
  async getIssuerAddressForPointToken(pointToken) {
2178
- const key = (0, import_viem11.getAddress)(pointToken);
2283
+ const key = (0, import_viem12.getAddress)(pointToken);
2179
2284
  const cached = this.pointTokenIssuerCache.get(key);
2180
2285
  if (cached) return cached;
2181
2286
  const issuer = await this.provider.readContract({
@@ -2183,15 +2288,15 @@ var IssuerStateValidator = class _IssuerStateValidator {
2183
2288
  abi: import_core10.POINT_TOKEN_V2_ABI,
2184
2289
  functionName: "issuer"
2185
2290
  });
2186
- this.pointTokenIssuerCache.set(key, (0, import_viem11.getAddress)(issuer));
2187
- return (0, import_viem11.getAddress)(issuer);
2291
+ this.pointTokenIssuerCache.set(key, (0, import_viem12.getAddress)(issuer));
2292
+ return (0, import_viem12.getAddress)(issuer);
2188
2293
  }
2189
2294
  /**
2190
2295
  * Read registry record + totalSupply, with 30s cache and in-flight
2191
2296
  * deduplication. Does NOT throw on inactive/missing — returns raw state.
2192
2297
  */
2193
2298
  async getIssuerState(pointToken) {
2194
- const tokenAddr = (0, import_viem11.getAddress)(pointToken);
2299
+ const tokenAddr = (0, import_viem12.getAddress)(pointToken);
2195
2300
  const now = Date.now();
2196
2301
  const cached = this.stateCache.get(tokenAddr);
2197
2302
  if (cached && cached.expiresAt > now) return cached.value;
@@ -2258,7 +2363,7 @@ var IssuerStateValidator = class _IssuerStateValidator {
2258
2363
  }
2259
2364
  async fetchIssuerState(tokenAddr) {
2260
2365
  const issuerAddr = await this.getIssuerAddressForPointToken(tokenAddr);
2261
- const [tuple, totalSupply] = await Promise.all([
2366
+ const [issuerStruct, totalSupply] = await Promise.all([
2262
2367
  this.provider.readContract({
2263
2368
  address: this.registryAddress,
2264
2369
  abi: import_core10.issuerRegistryAbi,
@@ -2271,17 +2376,7 @@ var IssuerStateValidator = class _IssuerStateValidator {
2271
2376
  functionName: "totalSupply"
2272
2377
  })
2273
2378
  ]);
2274
- const issuer = {
2275
- issuerAddress: tuple[0],
2276
- signerAddress: tuple[1],
2277
- name: tuple[2],
2278
- symbol: tuple[3],
2279
- declaredTotalSupply: tuple[4],
2280
- capBasisPoints: tuple[5],
2281
- active: tuple[6],
2282
- pointToken: tuple[7],
2283
- mintingOracle: tuple[8]
2284
- };
2379
+ const issuer = issuerStruct;
2285
2380
  const hardCap = issuer.declaredTotalSupply * BigInt(issuer.capBasisPoints) / 10000n;
2286
2381
  const remaining = hardCap > totalSupply ? hardCap - totalSupply : 0n;
2287
2382
  return { issuer, totalSupply, hardCap, remaining };
@@ -2317,6 +2412,7 @@ var PAFI_ISSUER_SDK_VERSION = "0.4.0";
2317
2412
  TopUpRedemptionHandler,
2318
2413
  authenticateRequest,
2319
2414
  createIssuerService,
2415
+ createNativePtQuoter,
2320
2416
  createSubgraphNativeUsdtQuoter,
2321
2417
  createSubgraphPoolsProvider,
2322
2418
  serializeEntryToJsonRpc