@pafi-dev/issuer 0.5.9 → 0.5.10

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
@@ -28,6 +28,8 @@ __export(index_exports, {
28
28
  FeeManager: () => FeeManager,
29
29
  InMemoryCursorStore: () => InMemoryCursorStore,
30
30
  IssuerApiHandlers: () => IssuerApiHandlers,
31
+ IssuerStateError: () => IssuerStateError,
32
+ IssuerStateValidator: () => IssuerStateValidator,
31
33
  MemorySessionStore: () => MemorySessionStore,
32
34
  NonceManager: () => NonceManager,
33
35
  PAFI_ISSUER_SDK_VERSION: () => PAFI_ISSUER_SDK_VERSION,
@@ -2093,6 +2095,176 @@ function createIssuerService(config) {
2093
2095
  };
2094
2096
  }
2095
2097
 
2098
+ // src/issuer-state/validator.ts
2099
+ var import_viem11 = require("viem");
2100
+ var import_core9 = require("@pafi-dev/core");
2101
+
2102
+ // src/issuer-state/types.ts
2103
+ var IssuerStateError = class extends Error {
2104
+ constructor(code, message, details) {
2105
+ super(message);
2106
+ this.code = code;
2107
+ this.details = details;
2108
+ this.name = "IssuerStateError";
2109
+ }
2110
+ code;
2111
+ details;
2112
+ };
2113
+
2114
+ // src/issuer-state/validator.ts
2115
+ var ISSUER_RECORD_TTL_MS = 3e4;
2116
+ var IssuerStateValidator = class _IssuerStateValidator {
2117
+ constructor(provider, registryAddress) {
2118
+ this.provider = provider;
2119
+ this.registryAddress = registryAddress;
2120
+ }
2121
+ provider;
2122
+ registryAddress;
2123
+ pointTokenIssuerCache = /* @__PURE__ */ new Map();
2124
+ stateCache = /* @__PURE__ */ new Map();
2125
+ inflight = /* @__PURE__ */ new Map();
2126
+ /**
2127
+ * Convenience factory — reads `registryAddress` from the SDK
2128
+ * `CONTRACT_ADDRESSES` map for the given chain.
2129
+ */
2130
+ static forChain(provider, chainId) {
2131
+ const { issuerRegistry } = (0, import_core9.getContractAddresses)(chainId);
2132
+ return new _IssuerStateValidator(provider, issuerRegistry);
2133
+ }
2134
+ /**
2135
+ * Invalidate cached state for one PointToken, or everything if omitted.
2136
+ * Call after admin txs that change registry or cap settings.
2137
+ */
2138
+ invalidate(pointToken) {
2139
+ if (pointToken) {
2140
+ const key = (0, import_viem11.getAddress)(pointToken);
2141
+ this.pointTokenIssuerCache.delete(key);
2142
+ this.stateCache.delete(key);
2143
+ this.inflight.delete(key);
2144
+ } else {
2145
+ this.pointTokenIssuerCache.clear();
2146
+ this.stateCache.clear();
2147
+ this.inflight.clear();
2148
+ }
2149
+ }
2150
+ /**
2151
+ * Resolve `PointToken.issuer()` once per token and memoize.
2152
+ * The issuer field is set at `initialize()` and never changes.
2153
+ */
2154
+ async getIssuerAddressForPointToken(pointToken) {
2155
+ const key = (0, import_viem11.getAddress)(pointToken);
2156
+ const cached = this.pointTokenIssuerCache.get(key);
2157
+ if (cached) return cached;
2158
+ const issuer = await this.provider.readContract({
2159
+ address: key,
2160
+ abi: import_core9.POINT_TOKEN_V2_ABI,
2161
+ functionName: "issuer"
2162
+ });
2163
+ this.pointTokenIssuerCache.set(key, (0, import_viem11.getAddress)(issuer));
2164
+ return (0, import_viem11.getAddress)(issuer);
2165
+ }
2166
+ /**
2167
+ * Read registry record + totalSupply, with 30s cache and in-flight
2168
+ * deduplication. Does NOT throw on inactive/missing — returns raw state.
2169
+ */
2170
+ async getIssuerState(pointToken) {
2171
+ const tokenAddr = (0, import_viem11.getAddress)(pointToken);
2172
+ const now = Date.now();
2173
+ const cached = this.stateCache.get(tokenAddr);
2174
+ if (cached && cached.expiresAt > now) return cached.value;
2175
+ const existing = this.inflight.get(tokenAddr);
2176
+ if (existing) return existing;
2177
+ const promise = this.fetchIssuerState(tokenAddr).then((state) => {
2178
+ this.stateCache.set(tokenAddr, {
2179
+ value: state,
2180
+ expiresAt: Date.now() + ISSUER_RECORD_TTL_MS
2181
+ });
2182
+ return state;
2183
+ }).finally(() => {
2184
+ this.inflight.delete(tokenAddr);
2185
+ });
2186
+ this.inflight.set(tokenAddr, promise);
2187
+ return promise;
2188
+ }
2189
+ /**
2190
+ * Validate that `amount` PT can be minted on `pointToken` right now.
2191
+ *
2192
+ * Throws `IssuerStateError` with:
2193
+ * - `ISSUER_NOT_REGISTERED` — registry has no record for this issuer
2194
+ * - `ISSUER_INACTIVE` — issuer.active is false
2195
+ * - `MINT_CAP_EXCEEDED` — totalSupply + amount would exceed hardCap
2196
+ *
2197
+ * Returns the fetched state on success so callers can log without a
2198
+ * second RPC round-trip.
2199
+ */
2200
+ async preValidateMint(pointToken, amount) {
2201
+ let state;
2202
+ try {
2203
+ state = await this.getIssuerState(pointToken);
2204
+ } catch (err) {
2205
+ if (err.message.includes("IssuerNotFound")) {
2206
+ throw new IssuerStateError(
2207
+ "ISSUER_NOT_REGISTERED",
2208
+ `IssuerRegistry has no record for PointToken ${pointToken}`,
2209
+ { pointToken }
2210
+ );
2211
+ }
2212
+ throw err;
2213
+ }
2214
+ const { issuer, totalSupply, hardCap, remaining } = state;
2215
+ if (!issuer.active) {
2216
+ throw new IssuerStateError(
2217
+ "ISSUER_INACTIVE",
2218
+ `Issuer ${issuer.issuerAddress} is deactivated on IssuerRegistry`,
2219
+ { issuer: issuer.issuerAddress, pointToken: issuer.pointToken }
2220
+ );
2221
+ }
2222
+ if (totalSupply + amount > hardCap) {
2223
+ throw new IssuerStateError(
2224
+ "MINT_CAP_EXCEEDED",
2225
+ `Requested ${amount} PT would exceed mint cap. Cap=${hardCap}, minted=${totalSupply}, remaining=${remaining}`,
2226
+ {
2227
+ requested: amount.toString(),
2228
+ cap: hardCap.toString(),
2229
+ minted: totalSupply.toString(),
2230
+ remaining: remaining.toString()
2231
+ }
2232
+ );
2233
+ }
2234
+ return state;
2235
+ }
2236
+ async fetchIssuerState(tokenAddr) {
2237
+ const issuerAddr = await this.getIssuerAddressForPointToken(tokenAddr);
2238
+ const [tuple, totalSupply] = await Promise.all([
2239
+ this.provider.readContract({
2240
+ address: this.registryAddress,
2241
+ abi: import_core9.issuerRegistryAbi,
2242
+ functionName: "getIssuer",
2243
+ args: [issuerAddr]
2244
+ }),
2245
+ this.provider.readContract({
2246
+ address: tokenAddr,
2247
+ abi: import_core9.POINT_TOKEN_V2_ABI,
2248
+ functionName: "totalSupply"
2249
+ })
2250
+ ]);
2251
+ const issuer = {
2252
+ issuerAddress: tuple[0],
2253
+ signerAddress: tuple[1],
2254
+ name: tuple[2],
2255
+ symbol: tuple[3],
2256
+ declaredTotalSupply: tuple[4],
2257
+ capBasisPoints: tuple[5],
2258
+ active: tuple[6],
2259
+ pointToken: tuple[7],
2260
+ mintingOracle: tuple[8]
2261
+ };
2262
+ const hardCap = issuer.declaredTotalSupply * BigInt(issuer.capBasisPoints) / 10000n;
2263
+ const remaining = hardCap > totalSupply ? hardCap - totalSupply : 0n;
2264
+ return { issuer, totalSupply, hardCap, remaining };
2265
+ }
2266
+ };
2267
+
2096
2268
  // src/index.ts
2097
2269
  var PAFI_ISSUER_SDK_VERSION = "0.4.0";
2098
2270
  // Annotate the CommonJS export names for ESM import in node:
@@ -2105,6 +2277,8 @@ var PAFI_ISSUER_SDK_VERSION = "0.4.0";
2105
2277
  FeeManager,
2106
2278
  InMemoryCursorStore,
2107
2279
  IssuerApiHandlers,
2280
+ IssuerStateError,
2281
+ IssuerStateValidator,
2108
2282
  MemorySessionStore,
2109
2283
  NonceManager,
2110
2284
  PAFI_ISSUER_SDK_VERSION,