@provex/react 1.11.0 → 2.0.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/dist/index.cjs CHANGED
@@ -9,15 +9,15 @@ var currencies = require('@provex/utils/currencies');
9
9
  var tokens = require('@provex/utils/tokens');
10
10
  var units = require('@provex/utils/units');
11
11
  var conversionRates = require('@provex/utils/conversionRates');
12
- var reputation = require('@provex/utils/reputation');
13
12
  var contracts = require('@provex/utils/contracts');
14
13
  var proving = require('@provex/proving');
14
+ var chain = require('@provex/utils/chain');
15
15
  var utils = require('@provex/utils');
16
16
  var deposit = require('@provex/utils/deposit');
17
17
  var fees = require('@provex/utils/fees');
18
18
  var abis = require('@provex/abis');
19
- var chain = require('@provex/utils/chain');
20
19
  var chains = require('viem/chains');
20
+ var reputation = require('@provex/utils/reputation');
21
21
  var extensionReact = require('@provex/extension-react');
22
22
  var ids = require('@provex/utils/ids');
23
23
 
@@ -48,6 +48,15 @@ function createApiClient(apiUrl) {
48
48
  })
49
49
  };
50
50
  }
51
+
52
+ // src/lib/clock.ts
53
+ var systemClock = Object.freeze({
54
+ now: () => Date.now(),
55
+ setTimeout: (callback, delayMs) => globalThis.setTimeout(callback, delayMs),
56
+ clearTimeout: (handle) => globalThis.clearTimeout(handle),
57
+ setInterval: (callback, intervalMs) => globalThis.setInterval(callback, intervalMs),
58
+ clearInterval: (handle) => globalThis.clearInterval(handle)
59
+ });
51
60
  var ProvexContext = react.createContext(null);
52
61
  var defaultQueryClient = new reactQuery.QueryClient({
53
62
  defaultOptions: {
@@ -64,22 +73,23 @@ function ProvexProvider({
64
73
  publicClient: publicClient2,
65
74
  publicClients,
66
75
  wallet,
76
+ clock = systemClock,
67
77
  children
68
78
  }) {
69
79
  const resolvedQueryClient = queryClient ?? defaultQueryClient;
70
80
  const contextValue = react.useMemo(() => {
71
81
  const apiClient = createApiClient(config.apiUrl);
72
82
  const chainId = config.chain.id;
73
- const base2 = publicClients ?? {};
74
- const resolved = { ...base2 };
83
+ const base = publicClients ?? {};
84
+ const resolved = { ...base };
75
85
  if (publicClient2 && !resolved[chainId]) {
76
86
  resolved[chainId] = publicClient2;
77
87
  }
78
88
  if (!resolved[chainId]) {
79
89
  resolved[chainId] = viem.createPublicClient({ chain: config.chain, transport: viem.http() });
80
90
  }
81
- return { config, indexer, apiClient, publicClients: resolved, wallet };
82
- }, [config, indexer, publicClient2, publicClients, wallet]);
91
+ return { config, indexer, apiClient, publicClients: resolved, wallet, clock };
92
+ }, [config, indexer, publicClient2, publicClients, wallet, clock]);
83
93
  return /* @__PURE__ */ jsxRuntime.jsx(ProvexContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsx(reactQuery.QueryClientProvider, { client: resolvedQueryClient, children }) });
84
94
  }
85
95
  function useProvex() {
@@ -109,6 +119,65 @@ function useProvexWallet() {
109
119
  function useOptionalProvexWallet() {
110
120
  return useOptionalProvex()?.wallet;
111
121
  }
122
+ function useClock() {
123
+ return useOptionalProvex()?.clock ?? systemClock;
124
+ }
125
+
126
+ // src/lib/updates.ts
127
+ var POLL_INTERVAL_MS = 1e3;
128
+ var SLOW_SYNC_THRESHOLD_MS = 15e3;
129
+ var sleep = (clock, delayMs) => new Promise((resolve) => {
130
+ clock.setTimeout(resolve, delayMs);
131
+ });
132
+ var checkMutation = async ({
133
+ queryDeposit,
134
+ depositIdHex,
135
+ status,
136
+ onSuccess,
137
+ onSlowSync,
138
+ slowSyncThresholdMs = SLOW_SYNC_THRESHOLD_MS,
139
+ clock = systemClock
140
+ }) => {
141
+ const startTime = clock.now();
142
+ let slowSyncFired = false;
143
+ while (true) {
144
+ const result = await queryDeposit(depositIdHex);
145
+ if (result?.status === status) {
146
+ break;
147
+ }
148
+ if (!slowSyncFired && onSlowSync && clock.now() - startTime > slowSyncThresholdMs) {
149
+ slowSyncFired = true;
150
+ onSlowSync();
151
+ }
152
+ await sleep(clock, POLL_INTERVAL_MS);
153
+ }
154
+ clock.setTimeout(() => {
155
+ onSuccess?.();
156
+ }, POLL_INTERVAL_MS);
157
+ };
158
+ var checkTransactionIndexed = async ({
159
+ queryTransaction,
160
+ hash,
161
+ onSuccess,
162
+ onSlowSync,
163
+ slowSyncThresholdMs = SLOW_SYNC_THRESHOLD_MS,
164
+ clock = systemClock
165
+ }) => {
166
+ const startTime = clock.now();
167
+ let slowSyncFired = false;
168
+ while (true) {
169
+ const result = await queryTransaction(hash);
170
+ if (result) {
171
+ break;
172
+ }
173
+ if (!slowSyncFired && onSlowSync && clock.now() - startTime > slowSyncThresholdMs) {
174
+ slowSyncFired = true;
175
+ onSlowSync();
176
+ }
177
+ await sleep(clock, POLL_INTERVAL_MS);
178
+ }
179
+ onSuccess?.();
180
+ };
112
181
  var DEFAULT_REFETCH_INTERVAL_MS = 5e3;
113
182
  function getRate({
114
183
  deposit,
@@ -238,57 +307,6 @@ var getTransactionGasInputs = async (chainId) => {
238
307
  };
239
308
  };
240
309
 
241
- // src/lib/updates.ts
242
- var POLL_INTERVAL_MS = 1e3;
243
- var SLOW_SYNC_THRESHOLD_MS = 15e3;
244
- var checkMutation = async ({
245
- queryDeposit,
246
- depositIdHex,
247
- status,
248
- onSuccess,
249
- onSlowSync,
250
- slowSyncThresholdMs = SLOW_SYNC_THRESHOLD_MS
251
- }) => {
252
- const startTime = Date.now();
253
- let slowSyncFired = false;
254
- while (true) {
255
- const result = await queryDeposit(depositIdHex);
256
- if (result?.status === status) {
257
- break;
258
- }
259
- if (!slowSyncFired && onSlowSync && Date.now() - startTime > slowSyncThresholdMs) {
260
- slowSyncFired = true;
261
- onSlowSync();
262
- }
263
- await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
264
- }
265
- setTimeout(() => {
266
- onSuccess?.();
267
- }, POLL_INTERVAL_MS);
268
- };
269
- var checkTransactionIndexed = async ({
270
- queryTransaction,
271
- hash,
272
- onSuccess,
273
- onSlowSync,
274
- slowSyncThresholdMs = SLOW_SYNC_THRESHOLD_MS
275
- }) => {
276
- const startTime = Date.now();
277
- let slowSyncFired = false;
278
- while (true) {
279
- const result = await queryTransaction(hash);
280
- if (result) {
281
- break;
282
- }
283
- if (!slowSyncFired && onSlowSync && Date.now() - startTime > slowSyncThresholdMs) {
284
- slowSyncFired = true;
285
- onSlowSync();
286
- }
287
- await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
288
- }
289
- onSuccess?.();
290
- };
291
-
292
310
  // src/lib/errors.ts
293
311
  var ERROR_PATTERNS = [
294
312
  // Wallet / signer rejections
@@ -663,7 +681,7 @@ var ProveXClient = class {
663
681
  * Handles all encoding internally:
664
682
  * 1. Registers payee details with the API (so buyers can discover them)
665
683
  * 2. Hashes provider keys and currency codes to bytes32
666
- * 3. Expands sub-providers (e.g., 'zelle' → zelle-chase, zelle-bofa, zelle-citi)
684
+ * 3. Collapses bank-level keys to their rail (e.g., 'zelle-chase' → 'zelle'); Zelle is one rail
667
685
  * 4. Encodes gating service witness signers
668
686
  * 5. Submits the on-chain transaction
669
687
  *
@@ -953,43 +971,6 @@ var ProveXClient = class {
953
971
  });
954
972
  return res.responseObject ?? null;
955
973
  }
956
- /**
957
- * Submit zkTLS proofs and get a signed attestation for fulfillIntent.
958
- * This is the proof verification step — after the buyer makes payment and
959
- * generates proofs via the browser extension.
960
- */
961
- async getAttestation(params) {
962
- const endpoint = `/api/v0/attestation/verify/${params.platform}/${params.actionType}`;
963
- const proof = params.proofs.length === 1 ? params.proofs[0] : params.proofs;
964
- const res = await fetch(`${this.apiUrl}${endpoint}`, {
965
- method: "POST",
966
- headers: { "Content-Type": "application/json" },
967
- body: JSON.stringify({
968
- proofType: "reclaim",
969
- proof: JSON.stringify(proof),
970
- chainId: params.chainId,
971
- verifyingContract: params.verifyingContract,
972
- intent: params.intent
973
- })
974
- });
975
- if (!res.ok) {
976
- let errorMessage = `Attestation error: ${res.status}`;
977
- try {
978
- const errorJson = await res.json();
979
- if (errorJson.details?.message) errorMessage = errorJson.details.message;
980
- else if (errorJson.message) errorMessage = errorJson.message;
981
- else if (errorJson.error) errorMessage = errorJson.error;
982
- } catch {
983
- }
984
- return {
985
- success: false,
986
- message: errorMessage,
987
- statusCode: res.status,
988
- responseObject: {}
989
- };
990
- }
991
- return await res.json();
992
- }
993
974
  /** Make a POST request to the API. */
994
975
  async apiPost(path, body) {
995
976
  const res = await fetch(`${this.apiUrl}${path}`, {
@@ -1089,6 +1070,11 @@ function createProveXClient(config) {
1089
1070
  }
1090
1071
 
1091
1072
  // src/hooks/useSignalIntent.ts
1073
+ var DEFAULT_SIGNAL_INTENT_MESSAGES = {
1074
+ clientOrDepositUnavailable: "Client or deposit not available",
1075
+ noEscrowAddress: "No escrow address found",
1076
+ success: "Intent signaled successfully"
1077
+ };
1092
1078
  var ERROR_PATTERNS2 = [
1093
1079
  { pattern: /user rejected|user denied|rejected the request|user cancelled|user canceled/i, message: "Transaction was cancelled." },
1094
1080
  { pattern: /InsufficientDepositLiquidity|0xc3df48f3/i, message: "This deposit no longer has enough available liquidity. Please try a smaller amount or a different deposit." },
@@ -1103,28 +1089,48 @@ function getUserFriendlyErrorMessage2(error) {
1103
1089
  }
1104
1090
  return "Something went wrong. Please try again.";
1105
1091
  }
1092
+ function isClientRejection(error) {
1093
+ return error instanceof ProveXError && error.isRejection;
1094
+ }
1095
+ var LOADING_STATUSES = /* @__PURE__ */ new Set([
1096
+ "loading_payee",
1097
+ "loading_intent",
1098
+ "prompt_wallet_confirm",
1099
+ "writing_intent"
1100
+ ]);
1106
1101
  function useSignalIntent({
1107
1102
  wallet,
1103
+ chainId,
1104
+ indexer,
1105
+ apiUrl,
1108
1106
  onSuccess,
1109
1107
  onFailure,
1110
1108
  deposit,
1111
- refetchDeposits
1109
+ refetchDeposits,
1110
+ beginTransaction,
1111
+ isRejection = isClientRejection,
1112
+ formatError = getUserFriendlyErrorMessage2,
1113
+ messages
1112
1114
  }) {
1113
1115
  const provex = useOptionalProvex();
1114
1116
  const [status, setStatus] = react.useState("input");
1115
1117
  const [message, setMessage] = react.useState(null);
1118
+ const copy = { ...DEFAULT_SIGNAL_INTENT_MESSAGES, ...messages };
1119
+ const chain$1 = (chainId === void 0 ? void 0 : chain.chainIdToChain.get(chainId)) ?? provex?.config.chain;
1120
+ const resolvedIndexer = indexer ?? provex?.indexer;
1121
+ const resolvedApiUrl = apiUrl ?? provex?.config.apiUrl;
1122
+ const escrow = deposit?.escrow ?? void 0;
1116
1123
  const client = react.useMemo(() => {
1117
- if (!provex) return null;
1124
+ if (!chain$1 || !wallet || !resolvedIndexer || resolvedApiUrl === void 0) return null;
1118
1125
  return createProveXClient({
1119
- chain: provex.config.chain,
1120
- apiUrl: provex.config.apiUrl,
1126
+ chain: chain$1,
1121
1127
  wallet,
1122
- indexer: provex.indexer
1128
+ indexer: resolvedIndexer,
1129
+ apiUrl: resolvedApiUrl,
1130
+ escrowAddress: escrow
1123
1131
  });
1124
- }, [provex, wallet]);
1125
- const isLoading = react.useMemo(() => {
1126
- return status === "loading_payee" || status === "loading_intent" || status === "prompt_wallet_confirm" || status === "writing_intent";
1127
- }, [status]);
1132
+ }, [chain$1, wallet, resolvedIndexer, resolvedApiUrl, escrow]);
1133
+ const isLoading = LOADING_STATUSES.has(status);
1128
1134
  const startOrder = react.useCallback(async ({
1129
1135
  paymentMethod,
1130
1136
  depositId,
@@ -1136,42 +1142,48 @@ function useSignalIntent({
1136
1142
  }) => {
1137
1143
  if (!client || !deposit) {
1138
1144
  setStatus("error");
1139
- setMessage("Client or deposit not available");
1145
+ setMessage(copy.clientOrDepositUnavailable);
1140
1146
  return;
1141
1147
  }
1142
- const escrow = deposit.escrow;
1143
- if (!escrow) {
1148
+ const depositEscrow = deposit.escrow;
1149
+ if (!depositEscrow) {
1144
1150
  setStatus("error");
1145
- setMessage("No escrow address found");
1151
+ setMessage(copy.noEscrowAddress);
1146
1152
  return;
1147
1153
  }
1154
+ const transaction = beginTransaction ? await beginTransaction() : void 0;
1155
+ if (transaction === null) return;
1148
1156
  setStatus("loading_intent");
1149
1157
  try {
1150
1158
  const result = await client.signalIntent({
1151
- deposit: { escrow, localId: deposit.localId ?? depositId },
1159
+ deposit: { escrow: depositEscrow, localId: deposit.localId ?? depositId },
1152
1160
  paymentMethod,
1153
1161
  tokenAmount,
1154
1162
  toAddress,
1155
1163
  fiatCurrencyCode,
1156
1164
  conversionRate,
1157
- subProvider
1165
+ subProvider,
1166
+ onAwaitingSignature: () => transaction?.awaitingSignature(),
1167
+ onTransactionHash: (hash) => transaction?.attach(hash)
1158
1168
  });
1159
1169
  setStatus("success");
1160
- setMessage("Intent signaled successfully");
1170
+ setMessage(copy.success);
1161
1171
  onSuccess(result.intentHash);
1162
1172
  } catch (error) {
1163
- if (error instanceof ProveXError && error.isRejection) {
1173
+ if (isRejection(error)) {
1174
+ transaction?.failed(error);
1164
1175
  setStatus("input");
1165
1176
  setMessage(null);
1166
1177
  return;
1167
1178
  }
1168
1179
  console.error("Failed to signal intent", error);
1180
+ transaction?.failed(error);
1169
1181
  setStatus("error");
1170
- setMessage(getUserFriendlyErrorMessage2(error));
1182
+ setMessage(formatError(error));
1171
1183
  refetchDeposits?.();
1172
1184
  onFailure?.();
1173
1185
  }
1174
- }, [client, deposit, onSuccess, onFailure, refetchDeposits]);
1186
+ }, [client, deposit, beginTransaction, isRejection, formatError, onSuccess, onFailure, refetchDeposits, copy.clientOrDepositUnavailable, copy.noEscrowAddress, copy.success]);
1175
1187
  return {
1176
1188
  startOrder,
1177
1189
  status,
@@ -1179,74 +1191,118 @@ function useSignalIntent({
1179
1191
  isLoading
1180
1192
  };
1181
1193
  }
1182
- function useReputation({
1183
- address,
1184
- chainId
1194
+ function filteringTierFor(options) {
1195
+ if (options.vipStatus === "unknown") return null;
1196
+ if (options.vipStatus === "vip") return payment.tiers.PRESIDENT;
1197
+ return options.tier;
1198
+ }
1199
+
1200
+ // src/hooks/useUserReputation.ts
1201
+ function computeUserReputation({
1202
+ stats,
1203
+ pulseTimestamp,
1204
+ now = () => /* @__PURE__ */ new Date()
1185
1205
  }) {
1186
- const { indexer } = useProvex();
1187
- const queryClient = reactQuery.useQueryClient();
1188
- const isBase = chainId === chains.base.id;
1189
- const reputationQuery = reactQuery.useQuery({
1190
- queryKey: ["provex", "reputation", address, isBase ? chains.base.id : "all"],
1206
+ if (!stats) return reputation.DEFAULT_REPUTATION;
1207
+ const makerVolume = stats.fulfilledMakerVolume ?? 0n;
1208
+ const makerCount = stats.fulfilledMakerCount ?? 0;
1209
+ const combinedVolume = stats.fulfilledVolume + makerVolume;
1210
+ const combinedFulfilledCount = stats.fulfilledCount + makerCount;
1211
+ let tier = reputation.getTierFromVolume(combinedVolume);
1212
+ const lockScore = stats.lateCancellations * 50;
1213
+ const dilutedLockScore = combinedFulfilledCount > 0 ? lockScore / (1 + combinedFulfilledCount * 0.1) : lockScore;
1214
+ let tierPenalty = 0;
1215
+ if (dilutedLockScore >= 1e3) tierPenalty = 4;
1216
+ else if (dilutedLockScore >= 500) tierPenalty = 3;
1217
+ else if (dilutedLockScore >= 200) tierPenalty = 2;
1218
+ else if (dilutedLockScore >= 50) tierPenalty = 1;
1219
+ if (tierPenalty > 0) {
1220
+ const currentTierIndex = reputation.TIER_ORDER.indexOf(tier);
1221
+ const newTierIndex = Math.max(0, currentTierIndex - tierPenalty);
1222
+ tier = reputation.TIER_ORDER[newTierIndex] ?? "peasant";
1223
+ }
1224
+ const cooldownHours = reputation.TAKER_TIERS[tier].cooldownHours;
1225
+ let cooldownEndsAt = null;
1226
+ let isOnCooldown = false;
1227
+ if (cooldownHours > 0 && pulseTimestamp !== null && pulseTimestamp !== void 0) {
1228
+ const lastSignaledTime = Number(pulseTimestamp) * 1e3;
1229
+ const cooldownMs = cooldownHours * 60 * 60 * 1e3;
1230
+ cooldownEndsAt = new Date(lastSignaledTime + cooldownMs);
1231
+ isOnCooldown = cooldownEndsAt > now();
1232
+ }
1233
+ return {
1234
+ tier,
1235
+ fulfilledVolume: combinedVolume,
1236
+ lockScore: dilutedLockScore,
1237
+ cooldownEndsAt,
1238
+ isOnCooldown
1239
+ };
1240
+ }
1241
+ var useIndexerReputationSource = ({ address }) => {
1242
+ const provex = useOptionalProvex();
1243
+ if (!provex) {
1244
+ throw new Error("useUserReputation needs a <ProvexProvider> or a `source` option");
1245
+ }
1246
+ const { indexer } = provex;
1247
+ const query = reactQuery.useQuery({
1248
+ queryKey: ["provex", "reputation", address, chains.pulsechain.id],
1191
1249
  queryFn: async () => {
1192
1250
  if (!address) return null;
1193
- return indexer.getUserReputation({
1194
- address,
1195
- chainId: isBase ? chains.base.id : void 0
1196
- });
1251
+ const data = await indexer.getUserReputation({ address, chainId: chains.pulsechain.id });
1252
+ return {
1253
+ fulfilledVolume: data.fulfilledVolume,
1254
+ fulfilledCount: data.fulfilledCount,
1255
+ lateCancellations: data.lateCancellations,
1256
+ lastSignaledAt: data.lastSignaledAt[chains.pulsechain.id] ?? null
1257
+ };
1197
1258
  },
1198
1259
  enabled: !!address,
1199
1260
  staleTime: 6e4,
1200
1261
  refetchInterval: 12e4
1201
1262
  });
1263
+ const refetch = react.useCallback(() => query.refetch(), [query]);
1264
+ const row = query.data ?? null;
1265
+ return {
1266
+ stats: row,
1267
+ lastSignaledTimestamp: row ? row.lastSignaledAt : null,
1268
+ isLoading: query.isLoading,
1269
+ error: query.error,
1270
+ refetch
1271
+ };
1272
+ };
1273
+ function useUserReputation({
1274
+ address,
1275
+ source = useIndexerReputationSource,
1276
+ vipStatus = "normal",
1277
+ disabled
1278
+ }) {
1279
+ const provex = useOptionalProvex();
1280
+ const isDisabled = disabled ?? provex?.config.reputationDisabled ?? false;
1281
+ const { stats, lastSignaledTimestamp, isLoading: sourceLoading, error, refetch: refetchSource } = source({ address });
1282
+ const canBypassCooldown = vipStatus === "vip";
1202
1283
  const reputation$1 = react.useMemo(() => {
1203
- const data = reputationQuery.data;
1204
- if (!data) return reputation.DEFAULT_REPUTATION;
1205
- let tier = reputation.getTierFromVolume(data.fulfilledVolume);
1206
- const lockScore = data.lateCancellations * 50;
1207
- const dilutedLockScore = data.fulfilledCount > 0 ? lockScore / (1 + data.fulfilledCount * 0.1) : lockScore;
1208
- let tierPenalty = 0;
1209
- if (dilutedLockScore >= 1e3) tierPenalty = 4;
1210
- else if (dilutedLockScore >= 500) tierPenalty = 3;
1211
- else if (dilutedLockScore >= 200) tierPenalty = 2;
1212
- else if (dilutedLockScore >= 50) tierPenalty = 1;
1213
- if (tierPenalty > 0) {
1214
- const currentTierIndex = reputation.TIER_ORDER.indexOf(tier);
1215
- const newTierIndex = Math.max(0, currentTierIndex - tierPenalty);
1216
- tier = reputation.TIER_ORDER[newTierIndex] ?? "peasant";
1217
- }
1218
- const cooldownHours = reputation.TAKER_TIERS[tier].cooldownHours;
1219
- let cooldownEndsAt = null;
1220
- let isOnCooldown = false;
1221
- const baseTimestamp = data.lastSignaledAt[chains.base.id] ?? null;
1222
- const pulseTimestamp = data.lastSignaledAt[369] ?? null;
1223
- const candidateTimestamps = isBase ? [baseTimestamp] : [baseTimestamp, pulseTimestamp];
1224
- const lastSignaledTimestamp = candidateTimestamps.filter((ts) => ts !== null).sort((a, b) => Number(b) - Number(a))[0] ?? null;
1225
- if (cooldownHours > 0 && lastSignaledTimestamp) {
1226
- const lastSignaledTime = Number(lastSignaledTimestamp) * 1e3;
1227
- const cooldownMs = cooldownHours * 60 * 60 * 1e3;
1228
- cooldownEndsAt = new Date(lastSignaledTime + cooldownMs);
1229
- isOnCooldown = cooldownEndsAt > /* @__PURE__ */ new Date();
1230
- }
1231
- return {
1232
- tier,
1233
- fulfilledVolume: data.fulfilledVolume,
1234
- lockScore: dilutedLockScore,
1235
- cooldownEndsAt,
1236
- isOnCooldown
1237
- };
1238
- }, [reputationQuery.data, isBase]);
1239
- const hasLoadedData = reputationQuery.data !== void 0;
1240
- const isLoading = reputationQuery.isLoading && !hasLoadedData;
1241
- const refetch = react.useCallback(() => {
1242
- queryClient.invalidateQueries({
1243
- queryKey: ["provex", "reputation", address]
1244
- });
1245
- }, [queryClient, address]);
1284
+ if (isDisabled) return reputation.DEFAULT_REPUTATION;
1285
+ const computed = computeUserReputation({ stats, pulseTimestamp: lastSignaledTimestamp });
1286
+ if (!canBypassCooldown) return computed;
1287
+ return { ...computed, tier: payment.tiers.PRESIDENT, isOnCooldown: false, cooldownEndsAt: null };
1288
+ }, [stats, lastSignaledTimestamp, canBypassCooldown, isDisabled]);
1289
+ const hasLoadedData = stats !== null;
1290
+ const isLoading = sourceLoading && !hasLoadedData;
1291
+ const refetch = react.useCallback(async () => {
1292
+ await refetchSource();
1293
+ }, [refetchSource]);
1294
+ const lastSignaledIntentMs = react.useMemo(
1295
+ () => lastSignaledTimestamp === null || lastSignaledTimestamp === void 0 ? null : Number(lastSignaledTimestamp) * 1e3,
1296
+ [lastSignaledTimestamp]
1297
+ );
1298
+ const filteringTier = isDisabled ? null : filteringTierFor({ vipStatus, tier: reputation$1.tier });
1246
1299
  return {
1247
1300
  reputation: reputation$1,
1301
+ lastSignaledIntentMs,
1302
+ vipStatus,
1303
+ filteringTier,
1248
1304
  isLoading,
1249
- error: reputationQuery.error,
1305
+ error,
1250
1306
  refetch
1251
1307
  };
1252
1308
  }
@@ -1268,22 +1324,52 @@ function getTierDisplayInfo(tier) {
1268
1324
  volumeRangeDisplay: reputation.getVolumeRangeDisplay(tier)
1269
1325
  };
1270
1326
  }
1327
+ var NO_LIMITS = {
1328
+ hasLimits: false,
1329
+ noCooldownProviders: [],
1330
+ selectedProviderCooldownHours: 0,
1331
+ cooldownRemaining: null,
1332
+ isOnCooldown: false,
1333
+ effectiveCap: null,
1334
+ amountExceedsCap: false,
1335
+ capExceededMessage: null,
1336
+ tierName: null,
1337
+ tierBaseCapUsd: null,
1338
+ capMultiplier: null,
1339
+ effectiveCapUsd: null,
1340
+ providerName: null
1341
+ };
1342
+ var COOLDOWN_TICK_MS = 6e4;
1271
1343
  function useReputationLimits({
1272
1344
  chainId,
1273
1345
  selectedPaymentMethod,
1274
1346
  tier,
1275
1347
  decimals,
1348
+ orderTokenAmount,
1276
1349
  amountInInt,
1277
- cooldownEndsAt
1350
+ cooldownEndsAt,
1351
+ disabled,
1352
+ clock
1278
1353
  }) {
1279
- const hasLimits = true;
1354
+ const provex = useOptionalProvex();
1355
+ const providerClock = useClock();
1356
+ const activeClock = clock ?? providerClock;
1357
+ const isDisabled = disabled ?? provex?.config.reputationDisabled ?? false;
1358
+ const hasLimits = !isDisabled;
1359
+ const orderAmount = orderTokenAmount ?? amountInInt ?? null;
1360
+ const [tick, setTick] = react.useState(0);
1361
+ react.useEffect(() => {
1362
+ if (!hasLimits || !cooldownEndsAt) return;
1363
+ const id = activeClock.setInterval(() => setTick((count) => count + 1), COOLDOWN_TICK_MS);
1364
+ return () => activeClock.clearInterval(id);
1365
+ }, [hasLimits, cooldownEndsAt, activeClock]);
1280
1366
  const { cooldownRemaining, isOnCooldown } = react.useMemo(() => {
1281
- const providerCooldownHours = reputation.getCooldownHours({ tier, providerKey: selectedPaymentMethod }) ;
1367
+ const providerCooldownHours = hasLimits ? reputation.getCooldownHours({ tier, providerKey: selectedPaymentMethod }) : 0;
1282
1368
  const providerHasCooldown = providerCooldownHours > 0;
1283
1369
  if (!cooldownEndsAt || !providerHasCooldown) {
1284
1370
  return { cooldownRemaining: null, isOnCooldown: false };
1285
1371
  }
1286
- const remainingMs = cooldownEndsAt.getTime() - Date.now();
1372
+ const remainingMs = cooldownEndsAt.getTime() - activeClock.now();
1287
1373
  if (remainingMs <= 0) {
1288
1374
  return { cooldownRemaining: null, isOnCooldown: false };
1289
1375
  }
@@ -1291,8 +1377,9 @@ function useReputationLimits({
1291
1377
  const minutes = Math.floor(remainingMs % (1e3 * 60 * 60) / (1e3 * 60));
1292
1378
  const formatted = hours > 0 ? `${hours}h ${minutes}m` : `${minutes}m`;
1293
1379
  return { cooldownRemaining: formatted, isOnCooldown: true };
1294
- }, [cooldownEndsAt, hasLimits, tier, selectedPaymentMethod]);
1380
+ }, [cooldownEndsAt, hasLimits, tier, selectedPaymentMethod, tick, activeClock]);
1295
1381
  const noCooldownProviders = react.useMemo(() => {
1382
+ if (!hasLimits) return [];
1296
1383
  const available = payment.verifiablePaymentMethods(chainId);
1297
1384
  return available.filter((key) => {
1298
1385
  if (key === selectedPaymentMethod) return false;
@@ -1302,22 +1389,43 @@ function useReputationLimits({
1302
1389
  });
1303
1390
  }, [hasLimits, chainId, selectedPaymentMethod, tier]);
1304
1391
  const selectedProviderCooldownHours = react.useMemo(() => {
1392
+ if (!hasLimits) return 0;
1305
1393
  return reputation.getCooldownHours({ tier, providerKey: selectedPaymentMethod });
1306
1394
  }, [hasLimits, tier, selectedPaymentMethod]);
1307
1395
  const effectiveCap = react.useMemo(() => {
1396
+ if (!hasLimits) return null;
1308
1397
  return reputation.getEffectiveCap({ tier, providerKey: selectedPaymentMethod, decimals });
1309
1398
  }, [hasLimits, tier, selectedPaymentMethod, decimals]);
1310
1399
  const amountExceedsCap = react.useMemo(
1311
- () => reputation.isOverOrderCap({ orderTokenAmount: amountInInt ?? null, cap: effectiveCap }),
1312
- [effectiveCap, amountInInt]
1400
+ () => reputation.isOverOrderCap({ orderTokenAmount: orderAmount, cap: effectiveCap }),
1401
+ [effectiveCap, orderAmount]
1313
1402
  );
1314
- const capExceededMessage = react.useMemo(() => {
1315
- if (!amountExceedsCap || effectiveCap === null) return null;
1403
+ const tierName = react.useMemo(
1404
+ () => hasLimits ? reputation.TAKER_TIERS[tier].name : null,
1405
+ [hasLimits, tier]
1406
+ );
1407
+ const providerName = react.useMemo(
1408
+ () => hasLimits ? payment.providerConfigs(chainId)[selectedPaymentMethod]?.name ?? selectedPaymentMethod : null,
1409
+ [hasLimits, chainId, selectedPaymentMethod]
1410
+ );
1411
+ const tierBaseCapUsd = react.useMemo(
1412
+ () => hasLimits && effectiveCap !== null && effectiveCap !== reputation.UNLIMITED_CAP_BASE_UNITS ? reputation.TAKER_TIERS[tier].baseCap : null,
1413
+ [hasLimits, tier, effectiveCap]
1414
+ );
1415
+ const capMultiplier = react.useMemo(
1416
+ () => hasLimits && effectiveCap !== null && effectiveCap !== reputation.UNLIMITED_CAP_BASE_UNITS ? payment.getPlatformRisk(chainId, selectedPaymentMethod).capMultiplier ?? 1 : null,
1417
+ [hasLimits, chainId, selectedPaymentMethod, effectiveCap]
1418
+ );
1419
+ const effectiveCapUsd = react.useMemo(() => {
1420
+ if (!hasLimits || effectiveCap === null) return null;
1316
1421
  if (effectiveCap === reputation.UNLIMITED_CAP_BASE_UNITS) return null;
1317
- const capUsd = reputation.getEffectiveCapDisplayUsd(effectiveCap, decimals) ?? 0;
1318
- const providerName = payment.providerConfigs(chainId)[selectedPaymentMethod]?.name ?? selectedPaymentMethod;
1319
- return `Amount exceeds your $${capUsd.toLocaleString()} limit for ${providerName}`;
1320
- }, [amountExceedsCap, effectiveCap, chainId, selectedPaymentMethod, decimals]);
1422
+ return reputation.getEffectiveCapDisplayUsd(effectiveCap, decimals) ?? null;
1423
+ }, [hasLimits, effectiveCap, decimals]);
1424
+ const capExceededMessage = react.useMemo(() => {
1425
+ if (!amountExceedsCap || effectiveCapUsd === null || providerName === null) return null;
1426
+ return `Amount exceeds your $${effectiveCapUsd.toLocaleString()} limit for ${providerName}`;
1427
+ }, [amountExceedsCap, effectiveCapUsd, providerName]);
1428
+ if (!hasLimits) return NO_LIMITS;
1321
1429
  return {
1322
1430
  hasLimits,
1323
1431
  noCooldownProviders,
@@ -1326,7 +1434,12 @@ function useReputationLimits({
1326
1434
  isOnCooldown,
1327
1435
  effectiveCap,
1328
1436
  amountExceedsCap,
1329
- capExceededMessage
1437
+ capExceededMessage,
1438
+ tierName,
1439
+ tierBaseCapUsd,
1440
+ capMultiplier,
1441
+ effectiveCapUsd,
1442
+ providerName
1330
1443
  };
1331
1444
  }
1332
1445
  var intentStatuses = {
@@ -1369,7 +1482,8 @@ function usePayeeDetails({
1369
1482
  }, [indexedIntent]);
1370
1483
  const expiryTime = indexedIntent?.expiryTime ?? null;
1371
1484
  const depositQuery = reactQuery.useQuery({
1372
- queryKey: ["provex", "deposit", indexedIntent?.escrow, indexedIntent?.depositLocalId, chainId],
1485
+ // Query keys are hashed with JSON.stringify, which throws on bigint.
1486
+ queryKey: ["provex", "deposit", indexedIntent?.escrow, indexedIntent?.depositLocalId.toString(), chainId],
1373
1487
  queryFn: () => indexer.getDepositWithVerifiers({
1374
1488
  escrow: indexedIntent.escrow,
1375
1489
  localId: indexedIntent.depositLocalId,
@@ -1721,36 +1835,100 @@ function useNullifierRegistry({
1721
1835
  checkNullifierUsed
1722
1836
  };
1723
1837
  }
1724
- function useProvexProving(deps) {
1838
+ function encodeOrchestratorFulfill(attestation, verificationData, intentHash) {
1839
+ const args = [
1840
+ {
1841
+ paymentProof: utils.encodeV3PaymentProof(attestation),
1842
+ intentHash,
1843
+ verificationData,
1844
+ postIntentHookData: "0x"
1845
+ }
1846
+ ];
1847
+ return {
1848
+ args,
1849
+ calldata: viem.encodeFunctionData({ abi: abis.OrchestratorAbi, functionName: "fulfillIntent", args }),
1850
+ abi: abis.OrchestratorAbi
1851
+ };
1852
+ }
1853
+ function createWalletProvingClient(options) {
1725
1854
  const {
1726
- proveXClient,
1855
+ orchestratorAddress,
1856
+ chainId,
1857
+ writeContract,
1858
+ publicClient: publicClient2,
1727
1859
  checkNullifierUsed,
1860
+ getGasInputs,
1861
+ encodeFulfill = encodeOrchestratorFulfill,
1862
+ onPrefill
1863
+ } = options;
1864
+ return {
1865
+ chainId,
1866
+ async fulfillIntent(params) {
1867
+ const context = { chainId, request: void 0 };
1868
+ const gasInputs = await getGasInputs();
1869
+ const { args, calldata, abi } = encodeFulfill(
1870
+ params.attestation,
1871
+ params.verificationData,
1872
+ params.intentHash
1873
+ );
1874
+ params.onAwaitingSignature?.(context);
1875
+ const hash = await writeContract({
1876
+ address: orchestratorAddress,
1877
+ abi,
1878
+ functionName: "fulfillIntent",
1879
+ args,
1880
+ chainId,
1881
+ ...gasInputs
1882
+ });
1883
+ onPrefill?.(
1884
+ {
1885
+ to: orchestratorAddress,
1886
+ data: calldata,
1887
+ value: 0n,
1888
+ originalTip: gasInputs.maxPriorityFeePerGas
1889
+ },
1890
+ gasInputs,
1891
+ hash
1892
+ );
1893
+ params.onTransactionHash?.({ ...context, hash });
1894
+ const receipt = publicClient2 ? await publicClient2.waitForTransactionReceipt({ hash }) : void 0;
1895
+ return { hash, receipt };
1896
+ },
1897
+ async checkNullifierUsed({ nullifier }) {
1898
+ return checkNullifierUsed(nullifier);
1899
+ }
1900
+ };
1901
+ }
1902
+
1903
+ // src/hooks/useProvingFlow.ts
1904
+ function useProvingFlow(options) {
1905
+ const {
1906
+ rail,
1728
1907
  request,
1729
1908
  paymentMethodId,
1730
1909
  captureAdapter,
1731
1910
  attestationBaseUrl,
1911
+ attestationServiceUrl,
1732
1912
  onAwaitingSignature,
1733
1913
  onTransactionHash,
1734
1914
  onComplete
1735
- } = deps;
1736
- const client = react.useMemo(
1737
- () => ({
1738
- // Pin the rail to the intent's chain. request.chainId is the single chain
1739
- // that drives both useNullifierRegistry (the read backing checkNullifierUsed)
1740
- // and ProveXClient.fulfillIntent here, so useProving's chain-pin assertion
1741
- // is a tautology in correct wiring and a tripwire if they ever diverge.
1742
- chainId: request.chainId,
1743
- fulfillIntent: proveXClient.fulfillIntent,
1744
- checkNullifierUsed: ({ nullifier }) => checkNullifierUsed(nullifier)
1745
- }),
1746
- [proveXClient, checkNullifierUsed, request.chainId]
1747
- );
1915
+ } = options;
1916
+ const chainId = options.chainId ?? request.chainId;
1917
+ const client = react.useMemo(() => {
1918
+ if (rail.kind === "wallet") return createWalletProvingClient({ ...rail, chainId });
1919
+ return {
1920
+ chainId,
1921
+ fulfillIntent: rail.fulfillIntent,
1922
+ checkNullifierUsed: ({ nullifier }) => rail.checkNullifierUsed(nullifier)
1923
+ };
1924
+ }, [rail, chainId]);
1748
1925
  const resolvedCaptureAdapter = react.useMemo(
1749
1926
  () => captureAdapter ?? extensionReact.createExtensionEnclaveAdapter({
1750
1927
  getPeer: () => window.peer,
1751
- attestationBaseUrl
1928
+ attestationBaseUrl,
1929
+ ...attestationServiceUrl ? { attestationServiceUrl } : {}
1752
1930
  }),
1753
- [captureAdapter, attestationBaseUrl]
1931
+ [captureAdapter, attestationBaseUrl, attestationServiceUrl]
1754
1932
  );
1755
1933
  const manualSelect = proving.useManualTransactionSelect();
1756
1934
  const { state, authenticate, fulfill, retryVerification, retrySubmission } = proving.useProving({
@@ -1766,6 +1944,17 @@ function useProvexProving(deps) {
1766
1944
  return { state, authenticate, fulfill, retryVerification, retrySubmission, manualSelect };
1767
1945
  }
1768
1946
 
1947
+ // src/hooks/useProvexProving.ts
1948
+ function useProvexProving(deps) {
1949
+ const { proveXClient, checkNullifierUsed, ...flowOptions } = deps;
1950
+ const { fulfillIntent } = proveXClient;
1951
+ const rail = react.useMemo(
1952
+ () => ({ kind: "client", fulfillIntent, checkNullifierUsed }),
1953
+ [fulfillIntent, checkNullifierUsed]
1954
+ );
1955
+ return useProvingFlow({ rail, ...flowOptions });
1956
+ }
1957
+
1769
1958
  // src/useProvexBuy.ts
1770
1959
  function parseAmountSafe(value, decimals) {
1771
1960
  if (!value || value === "." || value === "0.") return null;
@@ -1807,10 +1996,8 @@ function useProvexBuy({
1807
1996
  paymentMethod: activePaymentMethod,
1808
1997
  currency
1809
1998
  });
1810
- const { reputation: reputation$1 } = useReputation({
1811
- address: wallet.address,
1812
- chainId
1813
- });
1999
+ const { reputation } = useUserReputation({
2000
+ address: wallet.address});
1814
2001
  const amountBigInt = react.useMemo(
1815
2002
  () => parseAmountSafe(amount, currency.decimals),
1816
2003
  [amount, currency.decimals]
@@ -1846,12 +2033,11 @@ function useProvexBuy({
1846
2033
  refetchAllRef.current = payeeResult.refetchAll;
1847
2034
  react.useEffect(() => {
1848
2035
  if (phase !== "committed") return;
1849
- if (!intentHash) return;
1850
2036
  const interval = setInterval(() => {
1851
2037
  refetchAllRef.current();
1852
2038
  }, 5e3);
1853
2039
  return () => clearInterval(interval);
1854
- }, [phase, intentHash]);
2040
+ }, [phase]);
1855
2041
  react.useEffect(() => {
1856
2042
  if (!payeeResult.isPruned) return;
1857
2043
  if (phase === "browse") return;
@@ -1891,6 +2077,7 @@ function useProvexBuy({
1891
2077
  payeeDetailsHash,
1892
2078
  token,
1893
2079
  intent: {
2080
+ owner: intent.owner,
1894
2081
  amount: intent.amount,
1895
2082
  timestamp: intent.timestamp,
1896
2083
  paymentMethod: intent.paymentMethod,
@@ -1909,6 +2096,7 @@ function useProvexBuy({
1909
2096
  token: token ?? { decimals: 18 },
1910
2097
  intent: {
1911
2098
  intentHash: intentHash ?? "0x",
2099
+ owner: "0x",
1912
2100
  amount: "0",
1913
2101
  timestampMs: "0",
1914
2102
  paymentMethod: "0x",
@@ -1970,10 +2158,10 @@ function useProvexBuy({
1970
2158
  const limits = useReputationLimits({
1971
2159
  chainId,
1972
2160
  selectedPaymentMethod: activePaymentMethod,
1973
- tier: reputation$1.tier,
2161
+ tier: reputation.tier,
1974
2162
  decimals: tokenDecimals,
1975
- amountInInt: tokenAmount,
1976
- cooldownEndsAt: reputation$1.cooldownEndsAt
2163
+ orderTokenAmount: tokenAmount,
2164
+ cooldownEndsAt: reputation.cooldownEndsAt
1977
2165
  });
1978
2166
  const rateDisplay = react.useMemo(() => {
1979
2167
  if (!bestRate || !token) return null;
@@ -2059,7 +2247,7 @@ function useProvexBuy({
2059
2247
  tokenAmountDisplay,
2060
2248
  token,
2061
2249
  currency,
2062
- reputation: reputation$1 ?? reputation.DEFAULT_REPUTATION,
2250
+ reputation,
2063
2251
  limits,
2064
2252
  feePercentage: feeInfo?.percentage ?? null,
2065
2253
  payeeDetails: payeeResult.payeeDetails,
@@ -2541,6 +2729,15 @@ function ProvexBuyRoot({
2541
2729
  }
2542
2730
  );
2543
2731
  }
2732
+
2733
+ // src/hooks/useReputation.ts
2734
+ function useReputation({
2735
+ address,
2736
+ chainId
2737
+ }) {
2738
+ const { reputation, isLoading, error, refetch } = useUserReputation({ address});
2739
+ return { reputation, isLoading, error, refetch };
2740
+ }
2544
2741
  var useAllowance = ({
2545
2742
  token,
2546
2743
  spender,
@@ -3081,20 +3278,28 @@ var useTransactionWithIndexer = (options = {}) => {
3081
3278
  exports.BrowsePhase = BrowsePhase;
3082
3279
  exports.CommittedPhase = CommittedPhase;
3083
3280
  exports.CompletePhase = CompletePhase;
3281
+ exports.DEFAULT_SIGNAL_INTENT_MESSAGES = DEFAULT_SIGNAL_INTENT_MESSAGES;
3084
3282
  exports.ProveXClient = ProveXClient;
3085
3283
  exports.ProveXError = ProveXError;
3086
3284
  exports.ProvexBuy = ProvexBuy;
3087
3285
  exports.ProvexBuyProvider = ProvexBuyProvider;
3088
3286
  exports.ProvexProvider = ProvexProvider;
3089
3287
  exports.ProvingPhase = ProvingPhase;
3288
+ exports.checkMutation = checkMutation;
3289
+ exports.checkTransactionIndexed = checkTransactionIndexed;
3290
+ exports.computeUserReputation = computeUserReputation;
3090
3291
  exports.createApiClient = createApiClient;
3091
3292
  exports.createProveXClient = createProveXClient;
3293
+ exports.filteringTierFor = filteringTierFor;
3092
3294
  exports.getRate = getRate;
3093
3295
  exports.getTierDisplayInfo = getTierDisplayInfo;
3094
3296
  exports.intentStatuses = intentStatuses;
3297
+ exports.systemClock = systemClock;
3095
3298
  exports.useAllowance = useAllowance;
3096
3299
  exports.useCancelIntent = useCancelIntent;
3300
+ exports.useClock = useClock;
3097
3301
  exports.useDeposits = useDeposits;
3302
+ exports.useIndexerReputationSource = useIndexerReputationSource;
3098
3303
  exports.useNullifierRegistry = useNullifierRegistry;
3099
3304
  exports.useOptionalProvexPublicClient = useOptionalProvexPublicClient;
3100
3305
  exports.useOptionalProvexWallet = useOptionalProvexWallet;
@@ -3108,11 +3313,13 @@ exports.useProvexBuy = useProvexBuy;
3108
3313
  exports.useProvexBuyContext = useProvexBuyContext;
3109
3314
  exports.useProvexPublicClient = useProvexPublicClient;
3110
3315
  exports.useProvexWallet = useProvexWallet;
3316
+ exports.useProvingFlow = useProvingFlow;
3111
3317
  exports.useReleaseFundsToPayer = useReleaseFundsToPayer;
3112
3318
  exports.useReputation = useReputation;
3113
3319
  exports.useReputationLimits = useReputationLimits;
3114
3320
  exports.useSignalIntent = useSignalIntent;
3115
3321
  exports.useTransactionWithIndexer = useTransactionWithIndexer;
3322
+ exports.useUserReputation = useUserReputation;
3116
3323
  exports.useV3Escrow = useV3Escrow;
3117
3324
  //# sourceMappingURL=index.cjs.map
3118
3325
  //# sourceMappingURL=index.cjs.map