@glowlabs-org/utils 0.2.140 → 0.2.142

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/esm/index.js CHANGED
@@ -13,8 +13,8 @@ import { parseUnits, formatUnits } from 'viem';
13
13
  import { MerkleTree } from 'merkletreejs';
14
14
  import { solidityPackedKeccak256, keccak256 } from 'ethers';
15
15
  import Decimal from 'decimal.js';
16
- import { H as HUB_URL, U as USDG_WEIGHT_DECIMAL_PRECISION, G as GLOW_WEIGHT_DECIMAL_PRECISION, M as MAX_WEIGHT } from './farms-router-B5g58fsT.js';
17
- export { C as ControlRouter, F as FarmsRouter, c as KICKSTARTER_STATUS, K as KickstarterRouter, O as OFF_CHAIN_PAYMENT_CURRENCIES, P as PAYMENT_CURRENCIES, b as REGIONS, R as RegionRouter, S as STAKING_DIRECTIONS, T as TRANSFER_TYPES, W as WalletsRouter, u as useForwarder, a as useOffchainFractions } from './farms-router-B5g58fsT.js';
16
+ import { H as HUB_URL, U as USDG_WEIGHT_DECIMAL_PRECISION, G as GLOW_WEIGHT_DECIMAL_PRECISION, M as MAX_WEIGHT } from './farms-router-Cxbn5Hap.js';
17
+ export { C as ControlRouter, F as FarmsRouter, c as KICKSTARTER_STATUS, K as KickstarterRouter, O as OFF_CHAIN_PAYMENT_CURRENCIES, P as PAYMENT_CURRENCIES, b as REGIONS, R as RegionRouter, S as STAKING_DIRECTIONS, T as TRANSFER_TYPES, W as WalletsRouter, u as useForwarder, a as useOffchainFractions } from './farms-router-Cxbn5Hap.js';
18
18
 
19
19
  const GENESIS_TIMESTAMP = 1700352000;
20
20
 
@@ -370,6 +370,7 @@ export interface GlwRegionRewardsResponse {
370
370
  export interface ControlWallet {
371
371
  address: string;
372
372
  controlBalance: string;
373
+ committedControl: string;
373
374
  stakedControl: string;
374
375
  createdAt: string;
375
376
  }
@@ -409,11 +410,11 @@ export interface WalletFarmInfo {
409
410
  export interface WalletDetails {
410
411
  wallet: string;
411
412
  controlBalance: string;
413
+ committedControl: string;
412
414
  stakedControl: string;
413
415
  createdAt: string;
414
416
  regions: WalletRegionStakeTotal[];
415
417
  ownedFarms: WalletFarmInfo[];
416
- purchasedFarms: WalletFarmInfo[];
417
418
  }
418
419
  export interface EstimateRewardScoreParams {
419
420
  userId: string;
@@ -0,0 +1,23 @@
1
+ import { type PublicClient } from "viem";
2
+ import { type Signer } from "ethers";
3
+ export declare function parseViemError(error: unknown): string;
4
+ export declare function parseEthersError(error: unknown): string;
5
+ export interface TransactionRetryOptions {
6
+ maxRetries?: number;
7
+ timeoutMs?: number;
8
+ enableLogging?: boolean;
9
+ }
10
+ /**
11
+ * Enhanced transaction receipt handler with retry logic for viem
12
+ * @param publicClient The viem public client
13
+ * @param hash The transaction hash
14
+ * @param options Retry configuration options
15
+ */
16
+ export declare function waitForViemTransactionWithRetry(publicClient: PublicClient, hash: `0x${string}`, options?: TransactionRetryOptions): Promise<void>;
17
+ /**
18
+ * Enhanced transaction receipt handler with retry logic for ethers.js
19
+ * @param signer The ethers signer
20
+ * @param txHash The transaction hash
21
+ * @param options Retry configuration options
22
+ */
23
+ export declare function waitForEthersTransactionWithRetry(signer: Signer, txHash: string, options?: TransactionRetryOptions): Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glowlabs-org/utils",
3
- "version": "0.2.140",
3
+ "version": "0.2.142",
4
4
  "description": "A library containing all typechain types and addresses relating to the glow guarded launch",
5
5
  "keywords": [],
6
6
  "author": "",
package/src/browser.ts CHANGED
@@ -13,3 +13,4 @@ export * from "./constants/addresses";
13
13
  export * from "./constants/weights";
14
14
  export * from "./constants/urls";
15
15
  export * from "./utils/stake-control";
16
+ export * from "./utils/transaction-utils";
@@ -4,6 +4,10 @@ import { ERC20_ABI } from "../abis/erc20.abi";
4
4
  import { getAddresses } from "../../constants/addresses";
5
5
  import { formatEther } from "viem";
6
6
  import { PendingTransferType, TRANSFER_TYPES } from "../types";
7
+ import {
8
+ parseEthersError,
9
+ waitForEthersTransactionWithRetry,
10
+ } from "../../utils/transaction-utils";
7
11
 
8
12
  export enum ForwarderError {
9
13
  CONTRACT_NOT_AVAILABLE = "Contract not available",
@@ -31,32 +35,6 @@ export interface ForwardParams {
31
35
  kickstarterId?: string;
32
36
  }
33
37
 
34
- // Utility to extract the most useful revert reason from an ethers error object
35
- function parseEthersError(error: unknown): string {
36
- if (!error) return "Unknown error";
37
- const possibleError: any = error;
38
-
39
- // If the error originates from a callStatic it will often be found at `error?.error?.body`
40
- if (possibleError?.error?.body) {
41
- try {
42
- const body = JSON.parse(possibleError.error.body);
43
- // Hardhat style errors
44
- if (body?.error?.message) return body.error.message as string;
45
- } catch {}
46
- }
47
-
48
- // Found on MetaMask/Alchemy shape errors
49
- if (possibleError?.data?.message) return possibleError.data.message as string;
50
- if (possibleError?.error?.message)
51
- return possibleError.error.message as string;
52
-
53
- // Standard ethers v5 message
54
- if (possibleError?.reason) return possibleError.reason as string;
55
- if (possibleError?.message) return possibleError.message as string;
56
-
57
- return ForwarderError.UNKNOWN_ERROR;
58
- }
59
-
60
38
  // Type-guard style helper to ensure a signer exists throughout the rest of the function.
61
39
  function assertSigner(
62
40
  maybeSigner: Signer | undefined
@@ -253,7 +231,7 @@ export function useForwarder(signer: Signer | undefined, CHAIN_ID: number) {
253
231
  ADDRESSES.FORWARDER,
254
232
  amount
255
233
  );
256
- await approveTx.wait();
234
+ await waitForEthersTransactionWithRetry(signer, approveTx.hash);
257
235
 
258
236
  return true;
259
237
  } catch (error) {
@@ -312,7 +290,7 @@ export function useForwarder(signer: Signer | undefined, CHAIN_ID: number) {
312
290
  ADDRESSES.FORWARDER,
313
291
  MaxUint256
314
292
  );
315
- await approveTx.wait();
293
+ await waitForEthersTransactionWithRetry(signer, approveTx.hash);
316
294
  } catch (approveError) {
317
295
  throw new Error(
318
296
  parseEthersError(approveError) || "Token approval failed"
@@ -391,7 +369,7 @@ export function useForwarder(signer: Signer | undefined, CHAIN_ID: number) {
391
369
  message
392
370
  );
393
371
  }
394
- await tx.wait();
372
+ await waitForEthersTransactionWithRetry(signer, tx.hash);
395
373
 
396
374
  return tx.hash;
397
375
  } catch (txError: any) {
@@ -715,7 +693,7 @@ export function useForwarder(signer: Signer | undefined, CHAIN_ID: number) {
715
693
 
716
694
  // Try to call mint function (common for test tokens)
717
695
  const tx = await usdcContract.mint(recipient, amount);
718
- await tx.wait();
696
+ await waitForEthersTransactionWithRetry(signer, tx.hash);
719
697
 
720
698
  return tx.hash;
721
699
  } catch (error: any) {
@@ -7,6 +7,10 @@ import {
7
7
  import { OFFCHAIN_FRACTIONS_ABI } from "../abis/offchainFractions";
8
8
  import { ERC20_ABI } from "../abis/erc20.abi";
9
9
  import { getAddresses } from "../../constants/addresses";
10
+ import {
11
+ parseViemError,
12
+ waitForViemTransactionWithRetry,
13
+ } from "../../utils/transaction-utils";
10
14
 
11
15
  export enum OffchainFractionsError {
12
16
  CONTRACT_NOT_AVAILABLE = "Contract not available",
@@ -63,31 +67,6 @@ export interface RefundDetails {
63
67
  useCounterfactualAddress: boolean;
64
68
  }
65
69
 
66
- // Utility to extract the most useful revert reason from a viem error object
67
- function parseViemError(error: unknown): string {
68
- if (!error) return "Unknown error";
69
-
70
- // Check if it's a viem BaseError
71
- if (error instanceof Error) {
72
- // For contract revert errors
73
- if ((error as any).cause?.reason) {
74
- return (error as any).cause.reason;
75
- }
76
-
77
- // For viem's shortMessage
78
- if ((error as any).shortMessage) {
79
- return (error as any).shortMessage;
80
- }
81
-
82
- // Fallback to regular message
83
- if (error.message) {
84
- return error.message;
85
- }
86
- }
87
-
88
- return OffchainFractionsError.UNKNOWN_ERROR;
89
- }
90
-
91
70
  // Type-guard style helper to ensure a wallet client exists throughout the rest of the function.
92
71
  function assertWalletClient(
93
72
  maybeWalletClient: WalletClient | undefined
@@ -195,9 +174,7 @@ export function useOffchainFractions(
195
174
  account: walletClient.account!,
196
175
  });
197
176
 
198
- await publicClient.waitForTransactionReceipt({
199
- hash,
200
- });
177
+ await waitForViemTransactionWithRetry(publicClient, hash);
201
178
 
202
179
  return true;
203
180
  } catch (error) {
@@ -286,9 +263,7 @@ export function useOffchainFractions(
286
263
  account: walletClient.account!,
287
264
  });
288
265
 
289
- await publicClient.waitForTransactionReceipt({
290
- hash,
291
- });
266
+ await waitForViemTransactionWithRetry(publicClient, hash);
292
267
 
293
268
  return hash;
294
269
  } catch (error) {
@@ -358,9 +333,7 @@ export function useOffchainFractions(
358
333
  chain: walletClient.chain,
359
334
  account: walletClient.account!,
360
335
  });
361
- await publicClient.waitForTransactionReceipt({
362
- hash: approveHash,
363
- });
336
+ await waitForViemTransactionWithRetry(publicClient, approveHash);
364
337
  }
365
338
 
366
339
  // Run a simulation first to surface any revert reason
@@ -402,9 +375,7 @@ export function useOffchainFractions(
402
375
  account: walletClient.account!,
403
376
  });
404
377
 
405
- await publicClient.waitForTransactionReceipt({
406
- hash,
407
- });
378
+ await waitForViemTransactionWithRetry(publicClient, hash);
408
379
 
409
380
  return hash;
410
381
  } catch (error) {
@@ -495,9 +466,7 @@ export function useOffchainFractions(
495
466
  account: walletClient.account!,
496
467
  });
497
468
 
498
- await publicClient.waitForTransactionReceipt({
499
- hash,
500
- });
469
+ await waitForViemTransactionWithRetry(publicClient, hash);
501
470
 
502
471
  return hash;
503
472
  } catch (error) {
@@ -552,9 +521,7 @@ export function useOffchainFractions(
552
521
  account: walletClient.account!,
553
522
  });
554
523
 
555
- await publicClient.waitForTransactionReceipt({
556
- hash,
557
- });
524
+ await waitForViemTransactionWithRetry(publicClient, hash);
558
525
 
559
526
  return hash;
560
527
  } catch (error) {
@@ -748,9 +715,7 @@ export function useOffchainFractions(
748
715
  account: walletClient.account!,
749
716
  });
750
717
 
751
- await publicClient.waitForTransactionReceipt({
752
- hash,
753
- });
718
+ await waitForViemTransactionWithRetry(publicClient, hash);
754
719
 
755
720
  return hash;
756
721
  } catch (error) {
@@ -808,9 +773,7 @@ export function useOffchainFractions(
808
773
  account: walletClient.account!,
809
774
  });
810
775
 
811
- await publicClient.waitForTransactionReceipt({
812
- hash,
813
- });
776
+ await waitForViemTransactionWithRetry(publicClient, hash);
814
777
 
815
778
  return hash;
816
779
  } catch (error) {
@@ -455,6 +455,7 @@ export interface GlwRegionRewardsResponse {
455
455
  export interface ControlWallet {
456
456
  address: string;
457
457
  controlBalance: string;
458
+ committedControl: string;
458
459
  stakedControl: string;
459
460
  createdAt: string; // ISO 8601
460
461
  }
@@ -499,11 +500,11 @@ export interface WalletFarmInfo {
499
500
  export interface WalletDetails {
500
501
  wallet: string;
501
502
  controlBalance: string;
503
+ committedControl: string;
502
504
  stakedControl: string;
503
505
  createdAt: string; // ISO 8601
504
506
  regions: WalletRegionStakeTotal[];
505
507
  ownedFarms: WalletFarmInfo[];
506
- purchasedFarms: WalletFarmInfo[];
507
508
  }
508
509
 
509
510
  // ----------------------------- Farms Reward Score ---------------------------
@@ -0,0 +1,285 @@
1
+ import { type PublicClient } from "viem";
2
+ import { type Signer } from "ethers";
3
+
4
+ // Error parsing utilities
5
+ export function parseViemError(error: unknown): string {
6
+ if (!error) return "Unknown error";
7
+
8
+ // Check if it's a viem BaseError
9
+ if (error instanceof Error) {
10
+ // For contract revert errors
11
+ if ((error as any).cause?.reason) {
12
+ return (error as any).cause.reason;
13
+ }
14
+
15
+ // For viem's shortMessage
16
+ if ((error as any).shortMessage) {
17
+ return (error as any).shortMessage;
18
+ }
19
+
20
+ // Fallback to regular message
21
+ if (error.message) {
22
+ return error.message;
23
+ }
24
+ }
25
+
26
+ return "Unknown error";
27
+ }
28
+
29
+ export function parseEthersError(error: unknown): string {
30
+ if (!error) return "Unknown error";
31
+ const possibleError: any = error;
32
+
33
+ // If the error originates from a callStatic it will often be found at `error?.error?.body`
34
+ if (possibleError?.error?.body) {
35
+ try {
36
+ const body = JSON.parse(possibleError.error.body);
37
+ // Hardhat style errors
38
+ if (body?.error?.message) return body.error.message as string;
39
+ } catch {}
40
+ }
41
+
42
+ // Found on MetaMask/Alchemy shape errors
43
+ if (possibleError?.data?.message) return possibleError.data.message as string;
44
+ if (possibleError?.error?.message)
45
+ return possibleError.error.message as string;
46
+
47
+ // Standard ethers v5 message
48
+ if (possibleError?.reason) return possibleError.reason as string;
49
+ if (possibleError?.message) return possibleError.message as string;
50
+
51
+ return "Unknown error";
52
+ }
53
+
54
+ // Transaction receipt utilities
55
+ export interface TransactionRetryOptions {
56
+ maxRetries?: number;
57
+ timeoutMs?: number;
58
+ enableLogging?: boolean;
59
+ }
60
+
61
+ const DEFAULT_OPTIONS: Required<TransactionRetryOptions> = {
62
+ maxRetries: 3,
63
+ timeoutMs: 60000,
64
+ enableLogging: true,
65
+ };
66
+
67
+ /**
68
+ * Enhanced transaction receipt handler with retry logic for viem
69
+ * @param publicClient The viem public client
70
+ * @param hash The transaction hash
71
+ * @param options Retry configuration options
72
+ */
73
+ export async function waitForViemTransactionWithRetry(
74
+ publicClient: PublicClient,
75
+ hash: `0x${string}`,
76
+ options: TransactionRetryOptions = {}
77
+ ): Promise<void> {
78
+ const { maxRetries, timeoutMs, enableLogging } = {
79
+ ...DEFAULT_OPTIONS,
80
+ ...options,
81
+ };
82
+
83
+ let lastError: Error | null = null;
84
+
85
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
86
+ try {
87
+ // Wait for transaction receipt with timeout
88
+ const receipt = await publicClient.waitForTransactionReceipt({
89
+ hash,
90
+ timeout: timeoutMs,
91
+ });
92
+
93
+ // Check transaction status
94
+ if (receipt.status === "reverted") {
95
+ throw new Error(`Transaction ${hash} was reverted on-chain`);
96
+ }
97
+
98
+ if (enableLogging) {
99
+ console.log(
100
+ `Transaction ${hash} confirmed successfully in block ${receipt.blockNumber}`
101
+ );
102
+ }
103
+ return; // Success, exit the function
104
+ } catch (error) {
105
+ lastError = error as Error;
106
+
107
+ // Check if it's a timeout or not found error
108
+ const errorMessage = parseViemError(error);
109
+ const isRetryableError =
110
+ errorMessage.includes("could not be found") ||
111
+ errorMessage.includes("timeout") ||
112
+ errorMessage.includes("timed out") ||
113
+ errorMessage.includes("receipt") ||
114
+ errorMessage.includes("not be processed");
115
+
116
+ if (!isRetryableError || attempt === maxRetries) {
117
+ // If it's not a retryable error or we've exhausted retries, throw
118
+ throw new Error(
119
+ `Transaction failed after ${attempt} attempts: ${errorMessage}`
120
+ );
121
+ }
122
+
123
+ // Wait before retrying (exponential backoff)
124
+ const delay = Math.min(2000 * Math.pow(2, attempt - 1), 10000);
125
+ if (enableLogging) {
126
+ console.warn(
127
+ `Transaction receipt not found (attempt ${attempt}/${maxRetries}), retrying in ${delay}ms...`
128
+ );
129
+ }
130
+ await new Promise((resolve) => setTimeout(resolve, delay));
131
+
132
+ // Check transaction status before retrying
133
+ try {
134
+ const transaction = await publicClient.getTransaction({ hash });
135
+ if (!transaction) {
136
+ throw new Error("Transaction not found in mempool or blockchain");
137
+ }
138
+ if (enableLogging) {
139
+ console.log(
140
+ `Transaction ${hash} found in ${
141
+ transaction.blockNumber ? "block" : "mempool"
142
+ }, waiting for confirmation...`
143
+ );
144
+ }
145
+ } catch (txError) {
146
+ if (enableLogging) {
147
+ console.warn(
148
+ `Could not fetch transaction ${hash}: ${parseViemError(txError)}`
149
+ );
150
+ }
151
+ // Continue with retry anyway in case it's just an RPC issue
152
+ }
153
+ }
154
+ }
155
+
156
+ // This should never be reached, but just in case
157
+ throw new Error(
158
+ `Transaction failed after ${maxRetries} attempts: ${parseViemError(
159
+ lastError
160
+ )}`
161
+ );
162
+ }
163
+
164
+ /**
165
+ * Enhanced transaction receipt handler with retry logic for ethers.js
166
+ * @param signer The ethers signer
167
+ * @param txHash The transaction hash
168
+ * @param options Retry configuration options
169
+ */
170
+ export async function waitForEthersTransactionWithRetry(
171
+ signer: Signer,
172
+ txHash: string,
173
+ options: TransactionRetryOptions = {}
174
+ ): Promise<void> {
175
+ const { maxRetries, timeoutMs, enableLogging } = {
176
+ ...DEFAULT_OPTIONS,
177
+ ...options,
178
+ };
179
+
180
+ let lastError: Error | null = null;
181
+
182
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
183
+ try {
184
+ // Wait for transaction receipt with timeout
185
+ const provider = signer.provider;
186
+ if (!provider) {
187
+ throw new Error("Provider not available");
188
+ }
189
+
190
+ // Create a timeout promise
191
+ const timeoutPromise = new Promise((_, reject) => {
192
+ setTimeout(
193
+ () => reject(new Error("Transaction receipt timeout")),
194
+ timeoutMs
195
+ );
196
+ });
197
+
198
+ // Race between waiting for receipt and timeout
199
+ const receipt = await Promise.race([
200
+ provider.waitForTransaction(txHash, 1, timeoutMs),
201
+ timeoutPromise,
202
+ ]);
203
+
204
+ if (!receipt) {
205
+ throw new Error("Transaction receipt not found");
206
+ }
207
+
208
+ // Check transaction status (ethers.js uses status: 0 for failed, 1 for success)
209
+ if ((receipt as any).status === 0) {
210
+ throw new Error(`Transaction ${txHash} was reverted on-chain`);
211
+ }
212
+
213
+ if (enableLogging) {
214
+ console.log(
215
+ `Transaction ${txHash} confirmed successfully in block ${
216
+ (receipt as any).blockNumber
217
+ }`
218
+ );
219
+ }
220
+ return; // Success, exit the function
221
+ } catch (error) {
222
+ lastError = error as Error;
223
+
224
+ // Check if it's a timeout or not found error
225
+ const errorMessage = parseEthersError(error);
226
+ const isRetryableError =
227
+ errorMessage.includes("could not be found") ||
228
+ errorMessage.includes("timeout") ||
229
+ errorMessage.includes("timed out") ||
230
+ errorMessage.includes("receipt") ||
231
+ errorMessage.includes("not be processed") ||
232
+ errorMessage.includes("Transaction receipt timeout");
233
+
234
+ if (!isRetryableError || attempt === maxRetries) {
235
+ // If it's not a retryable error or we've exhausted retries, throw
236
+ throw new Error(
237
+ `Transaction failed after ${attempt} attempts: ${errorMessage}`
238
+ );
239
+ }
240
+
241
+ // Wait before retrying (exponential backoff)
242
+ const delay = Math.min(2000 * Math.pow(2, attempt - 1), 10000);
243
+ if (enableLogging) {
244
+ console.warn(
245
+ `Transaction receipt not found (attempt ${attempt}/${maxRetries}), retrying in ${delay}ms...`
246
+ );
247
+ }
248
+ await new Promise((resolve) => setTimeout(resolve, delay));
249
+
250
+ // Check transaction status before retrying
251
+ try {
252
+ const provider = signer.provider;
253
+ if (provider) {
254
+ const transaction = await provider.getTransaction(txHash);
255
+ if (!transaction) {
256
+ throw new Error("Transaction not found in mempool or blockchain");
257
+ }
258
+ if (enableLogging) {
259
+ console.log(
260
+ `Transaction ${txHash} found in ${
261
+ transaction.blockNumber ? "block" : "mempool"
262
+ }, waiting for confirmation...`
263
+ );
264
+ }
265
+ }
266
+ } catch (txError) {
267
+ if (enableLogging) {
268
+ console.warn(
269
+ `Could not fetch transaction ${txHash}: ${parseEthersError(
270
+ txError
271
+ )}`
272
+ );
273
+ }
274
+ // Continue with retry anyway in case it's just an RPC issue
275
+ }
276
+ }
277
+ }
278
+
279
+ // This should never be reached, but just in case
280
+ throw new Error(
281
+ `Transaction failed after ${maxRetries} attempts: ${parseEthersError(
282
+ lastError
283
+ )}`
284
+ );
285
+ }