@glowlabs-org/utils 0.2.173 → 0.2.175
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/cjs/browser.js +1 -1
- package/dist/cjs/{calculate-farm-efficiency-CKxIgfpG.js → calculate-farm-efficiency-psH9YXnF.js} +290 -100
- package/dist/cjs/calculate-farm-efficiency-psH9YXnF.js.map +1 -0
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/lib/hooks/use-forwarder.d.ts +2 -1
- package/dist/esm/browser.js +2 -2
- package/dist/esm/{calculate-farm-efficiency-DbFNiaxO.js → calculate-farm-efficiency-DvqnF04d.js} +290 -100
- package/dist/esm/calculate-farm-efficiency-DvqnF04d.js.map +1 -0
- package/dist/esm/index.js +2 -2
- package/dist/esm/lib/hooks/use-forwarder.d.ts +2 -1
- package/package.json +1 -1
- package/src/lib/hooks/use-forwarder.ts +335 -125
- package/src/utils/transaction-utils.ts +21 -10
- package/dist/cjs/calculate-farm-efficiency-CKxIgfpG.js.map +0 -1
- package/dist/esm/calculate-farm-efficiency-DbFNiaxO.js.map +0 -1
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 './calculate-farm-efficiency-
|
|
17
|
-
export { C as ControlRouter, F as FarmsRouter, e as KICKSTARTER_STATUS, K as KickstarterRouter, O as OFF_CHAIN_PAYMENT_CURRENCIES, P as PAYMENT_CURRENCIES, d as REGIONS, R as RegionRouter, S as STAKING_DIRECTIONS, T as TRANSFER_TYPES, W as WalletsRouter, f as calculateFarmEfficiency, c as configureSentry, u as useForwarder, a as useOffchainFractions, b as useRewardsKernel } from './calculate-farm-efficiency-
|
|
16
|
+
import { H as HUB_URL, U as USDG_WEIGHT_DECIMAL_PRECISION, G as GLOW_WEIGHT_DECIMAL_PRECISION, M as MAX_WEIGHT } from './calculate-farm-efficiency-DvqnF04d.js';
|
|
17
|
+
export { C as ControlRouter, F as FarmsRouter, e as KICKSTARTER_STATUS, K as KickstarterRouter, O as OFF_CHAIN_PAYMENT_CURRENCIES, P as PAYMENT_CURRENCIES, d as REGIONS, R as RegionRouter, S as STAKING_DIRECTIONS, T as TRANSFER_TYPES, W as WalletsRouter, f as calculateFarmEfficiency, c as configureSentry, u as useForwarder, a as useOffchainFractions, b as useRewardsKernel } from './calculate-farm-efficiency-DvqnF04d.js';
|
|
18
18
|
|
|
19
19
|
const GENESIS_TIMESTAMP = 1700352000;
|
|
20
20
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type Signer } from "ethers";
|
|
2
|
+
import { type PublicClient, type WalletClient } from "viem";
|
|
2
3
|
import { PendingTransferType } from "../types";
|
|
3
4
|
export declare enum ForwarderError {
|
|
4
5
|
CONTRACT_NOT_AVAILABLE = "Contract not available",
|
|
@@ -19,7 +20,7 @@ export interface ForwardParams {
|
|
|
19
20
|
regionId?: number;
|
|
20
21
|
kickstarterId?: string;
|
|
21
22
|
}
|
|
22
|
-
export declare function useForwarder(signer: Signer | undefined, CHAIN_ID: number): {
|
|
23
|
+
export declare function useForwarder(signer: Signer | undefined, CHAIN_ID: number, publicClient?: PublicClient, walletClient?: WalletClient): {
|
|
23
24
|
forwardTokens: (params: ForwardParams) => Promise<string>;
|
|
24
25
|
payProtocolFeeAndMintGCTLAndStake: (amount: bigint, userAddress: string, applicationId: string, regionId?: number, currency?: Currency) => Promise<string>;
|
|
25
26
|
sponsorProtocolFeeAndMintGCTLAndStake: (amount: bigint, userAddress: string, applicationId: string, currency?: Currency) => Promise<string>;
|
package/package.json
CHANGED
|
@@ -2,11 +2,18 @@ import { Contract, MaxUint256, type Signer } from "ethers";
|
|
|
2
2
|
import { FORWARDER_ABI } from "../abis/forwarderABI";
|
|
3
3
|
import { ERC20_ABI } from "../abis/erc20.abi";
|
|
4
4
|
import { getAddresses } from "../../constants/addresses";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
type PublicClient,
|
|
7
|
+
type WalletClient,
|
|
8
|
+
type Address,
|
|
9
|
+
formatEther,
|
|
10
|
+
} from "viem";
|
|
6
11
|
import { PendingTransferType, TRANSFER_TYPES } from "../types";
|
|
7
12
|
import {
|
|
8
13
|
parseEthersError,
|
|
14
|
+
parseViemError,
|
|
9
15
|
waitForEthersTransactionWithRetry,
|
|
16
|
+
waitForViemTransactionWithRetry,
|
|
10
17
|
} from "../../utils/transaction-utils";
|
|
11
18
|
import {
|
|
12
19
|
sentryAddBreadcrumb,
|
|
@@ -48,7 +55,24 @@ function assertSigner(
|
|
|
48
55
|
}
|
|
49
56
|
}
|
|
50
57
|
|
|
51
|
-
|
|
58
|
+
// Type-guard style helper to ensure a wallet client exists throughout the rest of the function.
|
|
59
|
+
function assertWalletClient(
|
|
60
|
+
maybeWalletClient: WalletClient | undefined
|
|
61
|
+
): asserts maybeWalletClient is WalletClient {
|
|
62
|
+
if (!maybeWalletClient) {
|
|
63
|
+
throw new Error(ForwarderError.SIGNER_NOT_AVAILABLE);
|
|
64
|
+
}
|
|
65
|
+
if (!maybeWalletClient.account) {
|
|
66
|
+
throw new Error("Wallet client must have an account");
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function useForwarder(
|
|
71
|
+
signer: Signer | undefined,
|
|
72
|
+
CHAIN_ID: number,
|
|
73
|
+
publicClient?: PublicClient,
|
|
74
|
+
walletClient?: WalletClient
|
|
75
|
+
) {
|
|
52
76
|
// Use dynamic addresses based on chain configuration
|
|
53
77
|
const ADDRESSES = getAddresses(CHAIN_ID);
|
|
54
78
|
|
|
@@ -58,7 +82,16 @@ export function useForwarder(signer: Signer | undefined, CHAIN_ID: number) {
|
|
|
58
82
|
isProcessing = value;
|
|
59
83
|
};
|
|
60
84
|
|
|
61
|
-
//
|
|
85
|
+
// Helper to assert public client is available
|
|
86
|
+
function assertPublicClient(
|
|
87
|
+
maybePublicClient: PublicClient | undefined
|
|
88
|
+
): asserts maybePublicClient is PublicClient {
|
|
89
|
+
if (!maybePublicClient) {
|
|
90
|
+
throw new Error("Public client not available");
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Returns a contract instance for Forwarder (Legacy Ethers)
|
|
62
95
|
function getForwarderContract() {
|
|
63
96
|
assertSigner(signer);
|
|
64
97
|
return new Contract(ADDRESSES.FORWARDER, FORWARDER_ABI, signer);
|
|
@@ -221,27 +254,64 @@ export function useForwarder(signer: Signer | undefined, CHAIN_ID: number) {
|
|
|
221
254
|
amount: bigint,
|
|
222
255
|
currency: Currency = "USDC"
|
|
223
256
|
): Promise<boolean> {
|
|
224
|
-
assertSigner(signer);
|
|
225
|
-
|
|
226
257
|
try {
|
|
227
|
-
const tokenContract = getTokenContract(currency);
|
|
228
|
-
if (!tokenContract)
|
|
229
|
-
throw new Error(ForwarderError.CONTRACT_NOT_AVAILABLE);
|
|
230
|
-
|
|
231
258
|
setIsProcessing(true);
|
|
232
259
|
|
|
233
|
-
//
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
260
|
+
// Get token address
|
|
261
|
+
let tokenAddress: string;
|
|
262
|
+
switch (currency) {
|
|
263
|
+
case "USDC":
|
|
264
|
+
tokenAddress = ADDRESSES.USDC;
|
|
265
|
+
break;
|
|
266
|
+
case "GLW":
|
|
267
|
+
tokenAddress = ADDRESSES.GLW;
|
|
268
|
+
break;
|
|
269
|
+
case "USDG":
|
|
270
|
+
tokenAddress = ADDRESSES.USDG;
|
|
271
|
+
break;
|
|
272
|
+
default:
|
|
273
|
+
throw new Error(
|
|
274
|
+
`Currency ${currency} not yet supported. Only USDC, GLW, and USDG are currently supported.`
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// If walletClient and publicClient are available, use Viem (better for mobile)
|
|
279
|
+
if (walletClient && publicClient) {
|
|
280
|
+
assertWalletClient(walletClient);
|
|
281
|
+
assertPublicClient(publicClient);
|
|
282
|
+
|
|
283
|
+
const hash = await walletClient.writeContract({
|
|
284
|
+
address: tokenAddress as Address,
|
|
285
|
+
abi: ERC20_ABI,
|
|
286
|
+
functionName: "approve",
|
|
287
|
+
args: [ADDRESSES.FORWARDER as Address, amount],
|
|
288
|
+
chain: walletClient.chain,
|
|
289
|
+
account: walletClient.account!,
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
await waitForViemTransactionWithRetry(publicClient, hash);
|
|
293
|
+
} else {
|
|
294
|
+
// Fallback to Ethers
|
|
295
|
+
assertSigner(signer);
|
|
296
|
+
const tokenContract = getTokenContract(currency);
|
|
297
|
+
if (!tokenContract)
|
|
298
|
+
throw new Error(ForwarderError.CONTRACT_NOT_AVAILABLE);
|
|
299
|
+
|
|
300
|
+
const approveTx = await tokenContract.approve(
|
|
301
|
+
ADDRESSES.FORWARDER,
|
|
302
|
+
amount
|
|
303
|
+
);
|
|
304
|
+
await waitForEthersTransactionWithRetry(signer, approveTx.hash, {
|
|
305
|
+
timeoutMs: 60000,
|
|
306
|
+
pollIntervalMs: 2000,
|
|
307
|
+
});
|
|
308
|
+
}
|
|
242
309
|
|
|
243
310
|
return true;
|
|
244
311
|
} catch (error) {
|
|
312
|
+
if (walletClient && publicClient) {
|
|
313
|
+
throw new Error(parseViemError(error));
|
|
314
|
+
}
|
|
245
315
|
throw new Error(parseEthersError(error));
|
|
246
316
|
} finally {
|
|
247
317
|
setIsProcessing(false);
|
|
@@ -253,19 +323,10 @@ export function useForwarder(signer: Signer | undefined, CHAIN_ID: number) {
|
|
|
253
323
|
* @param params Forward parameters including type, amount, and required fields
|
|
254
324
|
*/
|
|
255
325
|
async function forwardTokens(params: ForwardParams): Promise<string> {
|
|
256
|
-
assertSigner(signer);
|
|
257
|
-
|
|
258
326
|
try {
|
|
259
|
-
const forwarderContract = getForwarderContract();
|
|
260
|
-
if (!forwarderContract)
|
|
261
|
-
throw new Error(ForwarderError.CONTRACT_NOT_AVAILABLE);
|
|
262
|
-
|
|
263
327
|
setIsProcessing(true);
|
|
264
328
|
|
|
265
329
|
const { amount, currency = "USDC" } = params;
|
|
266
|
-
const tokenContract = getTokenContract(currency);
|
|
267
|
-
if (!tokenContract)
|
|
268
|
-
throw new Error(ForwarderError.CONTRACT_NOT_AVAILABLE);
|
|
269
330
|
|
|
270
331
|
sentryAddBreadcrumb({
|
|
271
332
|
category: "forwarder",
|
|
@@ -284,55 +345,19 @@ export function useForwarder(signer: Signer | undefined, CHAIN_ID: number) {
|
|
|
284
345
|
},
|
|
285
346
|
});
|
|
286
347
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
// Construct the appropriate message for this forward type
|
|
290
|
-
const message = constructForwardMessage(params);
|
|
291
|
-
|
|
292
|
-
// Special handling: PayAuditFees can ONLY be USDC, and must call forward()
|
|
348
|
+
// Special handling checks
|
|
293
349
|
const isAuditFees = params.type === TRANSFER_TYPES.PayAuditFees;
|
|
294
350
|
if (isAuditFees && currency !== "USDC") {
|
|
295
351
|
throw new Error("PayAuditFees only supports USDC");
|
|
296
352
|
}
|
|
297
353
|
|
|
298
|
-
// CommitKickstarter supports only USDC or USDG (GLW not allowed)
|
|
299
354
|
const isCommitKickstarter =
|
|
300
355
|
params.type === TRANSFER_TYPES.CommitKickstarter;
|
|
301
356
|
if (isCommitKickstarter && currency === "GLW") {
|
|
302
357
|
throw new Error("CommitKickstarter supports only USDC or USDG");
|
|
303
358
|
}
|
|
304
359
|
|
|
305
|
-
//
|
|
306
|
-
const allowance: bigint = await tokenContract.allowance(
|
|
307
|
-
owner,
|
|
308
|
-
ADDRESSES.FORWARDER
|
|
309
|
-
);
|
|
310
|
-
console.log("allowance", allowance.toString());
|
|
311
|
-
if (allowance < amount) {
|
|
312
|
-
try {
|
|
313
|
-
const approveTx = await tokenContract.approve(
|
|
314
|
-
ADDRESSES.FORWARDER,
|
|
315
|
-
MaxUint256
|
|
316
|
-
);
|
|
317
|
-
await waitForEthersTransactionWithRetry(signer, approveTx.hash, {
|
|
318
|
-
timeoutMs: 60000,
|
|
319
|
-
pollIntervalMs: 2000,
|
|
320
|
-
});
|
|
321
|
-
} catch (approveError) {
|
|
322
|
-
sentryCaptureException(approveError, {
|
|
323
|
-
action: "forwardTokens.approve",
|
|
324
|
-
chainId: CHAIN_ID,
|
|
325
|
-
spender: ADDRESSES.FORWARDER,
|
|
326
|
-
amount: MaxUint256.toString(),
|
|
327
|
-
currency,
|
|
328
|
-
});
|
|
329
|
-
throw new Error(
|
|
330
|
-
parseEthersError(approveError) || "Token approval failed"
|
|
331
|
-
);
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
// Get the token address based on currency
|
|
360
|
+
// Get token address
|
|
336
361
|
let tokenAddress: string;
|
|
337
362
|
switch (currency) {
|
|
338
363
|
case "USDC":
|
|
@@ -348,27 +373,192 @@ export function useForwarder(signer: Signer | undefined, CHAIN_ID: number) {
|
|
|
348
373
|
throw new Error(`Unsupported currency for forwarding: ${currency}`);
|
|
349
374
|
}
|
|
350
375
|
|
|
351
|
-
|
|
376
|
+
const message = constructForwardMessage(params);
|
|
352
377
|
const sendToCounterfactualWallet = !isAuditFees;
|
|
353
378
|
|
|
354
|
-
//
|
|
355
|
-
|
|
379
|
+
// Handle transaction based on available client
|
|
380
|
+
if (walletClient && publicClient) {
|
|
381
|
+
// --- VIEM IMPLEMENTATION ---
|
|
382
|
+
assertWalletClient(walletClient);
|
|
383
|
+
assertPublicClient(publicClient);
|
|
384
|
+
|
|
385
|
+
const owner = walletClient.account!.address;
|
|
386
|
+
|
|
387
|
+
// Check allowance
|
|
388
|
+
const allowance = (await publicClient.readContract({
|
|
389
|
+
address: tokenAddress as Address,
|
|
390
|
+
abi: ERC20_ABI,
|
|
391
|
+
functionName: "allowance",
|
|
392
|
+
args: [owner as Address, ADDRESSES.FORWARDER as Address],
|
|
393
|
+
})) as bigint;
|
|
394
|
+
|
|
395
|
+
if (allowance < amount) {
|
|
396
|
+
try {
|
|
397
|
+
const approveHash = await walletClient.writeContract({
|
|
398
|
+
address: tokenAddress as Address,
|
|
399
|
+
abi: ERC20_ABI,
|
|
400
|
+
functionName: "approve",
|
|
401
|
+
args: [ADDRESSES.FORWARDER as Address, MaxUint256],
|
|
402
|
+
chain: walletClient.chain,
|
|
403
|
+
account: walletClient.account!,
|
|
404
|
+
});
|
|
405
|
+
await waitForViemTransactionWithRetry(publicClient, approveHash);
|
|
406
|
+
} catch (approveError) {
|
|
407
|
+
sentryCaptureException(approveError, {
|
|
408
|
+
action: "forwardTokens.approve",
|
|
409
|
+
chainId: CHAIN_ID,
|
|
410
|
+
spender: ADDRESSES.FORWARDER,
|
|
411
|
+
amount: MaxUint256.toString(),
|
|
412
|
+
currency,
|
|
413
|
+
});
|
|
414
|
+
throw new Error(
|
|
415
|
+
parseViemError(approveError) || "Token approval failed"
|
|
416
|
+
);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Simulate
|
|
421
|
+
try {
|
|
422
|
+
if (!isAuditFees && currency === "USDC") {
|
|
423
|
+
await publicClient.simulateContract({
|
|
424
|
+
address: ADDRESSES.FORWARDER as Address,
|
|
425
|
+
abi: FORWARDER_ABI,
|
|
426
|
+
functionName: "swapUSDCAndForwardUSDG",
|
|
427
|
+
args: [
|
|
428
|
+
amount,
|
|
429
|
+
ADDRESSES.FOUNDATION_WALLET as Address,
|
|
430
|
+
sendToCounterfactualWallet,
|
|
431
|
+
message,
|
|
432
|
+
],
|
|
433
|
+
account: walletClient.account!,
|
|
434
|
+
});
|
|
435
|
+
} else {
|
|
436
|
+
await publicClient.simulateContract({
|
|
437
|
+
address: ADDRESSES.FORWARDER as Address,
|
|
438
|
+
abi: FORWARDER_ABI,
|
|
439
|
+
functionName: "forward",
|
|
440
|
+
args: [
|
|
441
|
+
tokenAddress as Address,
|
|
442
|
+
(isAuditFees
|
|
443
|
+
? ADDRESSES.AUDIT_FEE_WALLET
|
|
444
|
+
: ADDRESSES.FOUNDATION_WALLET) as Address,
|
|
445
|
+
amount,
|
|
446
|
+
sendToCounterfactualWallet,
|
|
447
|
+
message,
|
|
448
|
+
],
|
|
449
|
+
account: walletClient.account!,
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
} catch (staticError) {
|
|
453
|
+
sentryCaptureException(staticError, {
|
|
454
|
+
action: "forwardTokens.simulate",
|
|
455
|
+
chainId: CHAIN_ID,
|
|
456
|
+
function:
|
|
457
|
+
!isAuditFees && currency === "USDC"
|
|
458
|
+
? "swapUSDCAndForwardUSDG"
|
|
459
|
+
: "forward",
|
|
460
|
+
tokenAddress,
|
|
461
|
+
amount: amount.toString(),
|
|
462
|
+
currency,
|
|
463
|
+
isAuditFees,
|
|
464
|
+
});
|
|
465
|
+
throw new Error(parseViemError(staticError));
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// Write
|
|
469
|
+
let hash;
|
|
356
470
|
if (!isAuditFees && currency === "USDC") {
|
|
357
|
-
await
|
|
358
|
-
.
|
|
359
|
-
|
|
471
|
+
hash = await walletClient.writeContract({
|
|
472
|
+
address: ADDRESSES.FORWARDER as Address,
|
|
473
|
+
abi: FORWARDER_ABI,
|
|
474
|
+
functionName: "swapUSDCAndForwardUSDG",
|
|
475
|
+
args: [
|
|
360
476
|
amount,
|
|
361
|
-
ADDRESSES.FOUNDATION_WALLET,
|
|
477
|
+
ADDRESSES.FOUNDATION_WALLET as Address,
|
|
362
478
|
sendToCounterfactualWallet,
|
|
363
479
|
message,
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
480
|
+
],
|
|
481
|
+
chain: walletClient.chain,
|
|
482
|
+
account: walletClient.account!,
|
|
483
|
+
});
|
|
368
484
|
} else {
|
|
369
|
-
await
|
|
370
|
-
.
|
|
371
|
-
|
|
485
|
+
hash = await walletClient.writeContract({
|
|
486
|
+
address: ADDRESSES.FORWARDER as Address,
|
|
487
|
+
abi: FORWARDER_ABI,
|
|
488
|
+
functionName: "forward",
|
|
489
|
+
args: [
|
|
490
|
+
tokenAddress as Address,
|
|
491
|
+
(isAuditFees
|
|
492
|
+
? ADDRESSES.AUDIT_FEE_WALLET
|
|
493
|
+
: ADDRESSES.FOUNDATION_WALLET) as Address,
|
|
494
|
+
amount,
|
|
495
|
+
sendToCounterfactualWallet,
|
|
496
|
+
message,
|
|
497
|
+
],
|
|
498
|
+
chain: walletClient.chain,
|
|
499
|
+
account: walletClient.account!,
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
await waitForViemTransactionWithRetry(publicClient, hash);
|
|
504
|
+
return hash;
|
|
505
|
+
} else {
|
|
506
|
+
// --- ETHERS IMPLEMENTATION (Legacy) ---
|
|
507
|
+
assertSigner(signer);
|
|
508
|
+
const forwarderContract = getForwarderContract();
|
|
509
|
+
if (!forwarderContract)
|
|
510
|
+
throw new Error(ForwarderError.CONTRACT_NOT_AVAILABLE);
|
|
511
|
+
|
|
512
|
+
const tokenContract = getTokenContract(currency);
|
|
513
|
+
if (!tokenContract)
|
|
514
|
+
throw new Error(ForwarderError.CONTRACT_NOT_AVAILABLE);
|
|
515
|
+
|
|
516
|
+
const owner = await signer.getAddress();
|
|
517
|
+
|
|
518
|
+
// Check allowance
|
|
519
|
+
const allowance: bigint = await tokenContract.allowance(
|
|
520
|
+
owner,
|
|
521
|
+
ADDRESSES.FORWARDER
|
|
522
|
+
);
|
|
523
|
+
|
|
524
|
+
if (allowance < amount) {
|
|
525
|
+
try {
|
|
526
|
+
const approveTx = await tokenContract.approve(
|
|
527
|
+
ADDRESSES.FORWARDER,
|
|
528
|
+
MaxUint256
|
|
529
|
+
);
|
|
530
|
+
await waitForEthersTransactionWithRetry(signer, approveTx.hash, {
|
|
531
|
+
timeoutMs: 60000,
|
|
532
|
+
pollIntervalMs: 2000,
|
|
533
|
+
});
|
|
534
|
+
} catch (approveError) {
|
|
535
|
+
sentryCaptureException(approveError, {
|
|
536
|
+
action: "forwardTokens.approve",
|
|
537
|
+
chainId: CHAIN_ID,
|
|
538
|
+
spender: ADDRESSES.FORWARDER,
|
|
539
|
+
amount: MaxUint256.toString(),
|
|
540
|
+
currency,
|
|
541
|
+
});
|
|
542
|
+
throw new Error(
|
|
543
|
+
parseEthersError(approveError) || "Token approval failed"
|
|
544
|
+
);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// Determine function and call static
|
|
549
|
+
try {
|
|
550
|
+
if (!isAuditFees && currency === "USDC") {
|
|
551
|
+
await forwarderContract
|
|
552
|
+
.getFunction("swapUSDCAndForwardUSDG")
|
|
553
|
+
.staticCall(
|
|
554
|
+
amount,
|
|
555
|
+
ADDRESSES.FOUNDATION_WALLET,
|
|
556
|
+
sendToCounterfactualWallet,
|
|
557
|
+
message,
|
|
558
|
+
{ from: owner }
|
|
559
|
+
);
|
|
560
|
+
} else {
|
|
561
|
+
await forwarderContract.getFunction("forward").staticCall(
|
|
372
562
|
tokenAddress,
|
|
373
563
|
isAuditFees
|
|
374
564
|
? ADDRESSES.AUDIT_FEE_WALLET
|
|
@@ -378,49 +568,51 @@ export function useForwarder(signer: Signer | undefined, CHAIN_ID: number) {
|
|
|
378
568
|
message,
|
|
379
569
|
{ from: owner }
|
|
380
570
|
);
|
|
571
|
+
}
|
|
572
|
+
} catch (staticError) {
|
|
573
|
+
sentryCaptureException(staticError, {
|
|
574
|
+
action: "forwardTokens.staticCall",
|
|
575
|
+
chainId: CHAIN_ID,
|
|
576
|
+
function:
|
|
577
|
+
!isAuditFees && currency === "USDC"
|
|
578
|
+
? "swapUSDCAndForwardUSDG"
|
|
579
|
+
: "forward",
|
|
580
|
+
tokenAddress,
|
|
581
|
+
amount: amount.toString(),
|
|
582
|
+
currency,
|
|
583
|
+
isAuditFees,
|
|
584
|
+
});
|
|
585
|
+
throw new Error(parseEthersError(staticError));
|
|
381
586
|
}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
587
|
+
|
|
588
|
+
// Execute transaction
|
|
589
|
+
let tx;
|
|
590
|
+
if (!isAuditFees && currency === "USDC") {
|
|
591
|
+
tx = await forwarderContract.getFunction("swapUSDCAndForwardUSDG")(
|
|
592
|
+
amount,
|
|
593
|
+
ADDRESSES.FOUNDATION_WALLET,
|
|
594
|
+
sendToCounterfactualWallet,
|
|
595
|
+
message
|
|
596
|
+
);
|
|
597
|
+
} else {
|
|
598
|
+
tx = await forwarderContract.getFunction("forward")(
|
|
599
|
+
tokenAddress,
|
|
600
|
+
isAuditFees
|
|
601
|
+
? ADDRESSES.AUDIT_FEE_WALLET
|
|
602
|
+
: ADDRESSES.FOUNDATION_WALLET,
|
|
603
|
+
amount,
|
|
604
|
+
sendToCounterfactualWallet,
|
|
605
|
+
message
|
|
606
|
+
);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
await waitForEthersTransactionWithRetry(signer, tx.hash, {
|
|
610
|
+
timeoutMs: 60000,
|
|
611
|
+
pollIntervalMs: 2000,
|
|
394
612
|
});
|
|
395
|
-
throw new Error(parseEthersError(staticError));
|
|
396
|
-
}
|
|
397
613
|
|
|
398
|
-
|
|
399
|
-
let tx;
|
|
400
|
-
if (!isAuditFees && currency === "USDC") {
|
|
401
|
-
tx = await forwarderContract.getFunction("swapUSDCAndForwardUSDG")(
|
|
402
|
-
amount,
|
|
403
|
-
ADDRESSES.FOUNDATION_WALLET,
|
|
404
|
-
sendToCounterfactualWallet,
|
|
405
|
-
message
|
|
406
|
-
);
|
|
407
|
-
} else {
|
|
408
|
-
tx = await forwarderContract.getFunction("forward")(
|
|
409
|
-
tokenAddress,
|
|
410
|
-
isAuditFees
|
|
411
|
-
? ADDRESSES.AUDIT_FEE_WALLET
|
|
412
|
-
: ADDRESSES.FOUNDATION_WALLET,
|
|
413
|
-
amount,
|
|
414
|
-
sendToCounterfactualWallet,
|
|
415
|
-
message
|
|
416
|
-
);
|
|
614
|
+
return tx.hash;
|
|
417
615
|
}
|
|
418
|
-
await waitForEthersTransactionWithRetry(signer, tx.hash, {
|
|
419
|
-
timeoutMs: 60000,
|
|
420
|
-
pollIntervalMs: 2000,
|
|
421
|
-
});
|
|
422
|
-
|
|
423
|
-
return tx.hash;
|
|
424
616
|
} catch (txError: any) {
|
|
425
617
|
sentryCaptureException(txError, {
|
|
426
618
|
action: "forwardTokens",
|
|
@@ -434,6 +626,9 @@ export function useForwarder(signer: Signer | undefined, CHAIN_ID: number) {
|
|
|
434
626
|
kickstarterId: params.kickstarterId,
|
|
435
627
|
userAddress: params.userAddress,
|
|
436
628
|
});
|
|
629
|
+
if (walletClient && publicClient) {
|
|
630
|
+
throw new Error(parseViemError(txError));
|
|
631
|
+
}
|
|
437
632
|
throw new Error(parseEthersError(txError));
|
|
438
633
|
} finally {
|
|
439
634
|
setIsProcessing(false);
|
|
@@ -511,6 +706,7 @@ export function useForwarder(signer: Signer | undefined, CHAIN_ID: number) {
|
|
|
511
706
|
type: TRANSFER_TYPES.PayProtocolFee,
|
|
512
707
|
currency,
|
|
513
708
|
applicationId,
|
|
709
|
+
regionId: undefined, // Fix missing argument
|
|
514
710
|
});
|
|
515
711
|
}
|
|
516
712
|
|
|
@@ -543,7 +739,9 @@ export function useForwarder(signer: Signer | undefined, CHAIN_ID: number) {
|
|
|
543
739
|
regionId?: number,
|
|
544
740
|
currency: Currency = "USDC"
|
|
545
741
|
): Promise<string> {
|
|
546
|
-
|
|
742
|
+
// If using WalletClient, we don't strictly need a signer here anymore for forwardTokens
|
|
743
|
+
// But other methods might still rely on it.
|
|
744
|
+
if (!walletClient) assertSigner(signer);
|
|
547
745
|
|
|
548
746
|
// GCTL minting only supports USDC and USDG
|
|
549
747
|
if (currency === "GLW") {
|
|
@@ -569,7 +767,7 @@ export function useForwarder(signer: Signer | undefined, CHAIN_ID: number) {
|
|
|
569
767
|
userAddress: string,
|
|
570
768
|
currency: Currency = "USDC"
|
|
571
769
|
): Promise<string> {
|
|
572
|
-
assertSigner(signer);
|
|
770
|
+
if (!walletClient) assertSigner(signer);
|
|
573
771
|
|
|
574
772
|
// GCTL minting only supports USDC and USDG
|
|
575
773
|
if (currency === "GLW") {
|
|
@@ -754,10 +952,22 @@ export function useForwarder(signer: Signer | undefined, CHAIN_ID: number) {
|
|
|
754
952
|
|
|
755
953
|
// Try to call mint function (common for test tokens)
|
|
756
954
|
const tx = await usdcContract.mint(recipient, amount);
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
955
|
+
|
|
956
|
+
if (publicClient) {
|
|
957
|
+
await waitForViemTransactionWithRetry(
|
|
958
|
+
publicClient,
|
|
959
|
+
tx.hash as `0x${string}`,
|
|
960
|
+
{
|
|
961
|
+
timeoutMs: 60000,
|
|
962
|
+
pollIntervalMs: 2000,
|
|
963
|
+
}
|
|
964
|
+
);
|
|
965
|
+
} else {
|
|
966
|
+
await waitForEthersTransactionWithRetry(signer, tx.hash, {
|
|
967
|
+
timeoutMs: 60000,
|
|
968
|
+
pollIntervalMs: 2000,
|
|
969
|
+
});
|
|
970
|
+
}
|
|
761
971
|
|
|
762
972
|
return tx.hash;
|
|
763
973
|
} catch (error: any) {
|
|
@@ -802,6 +1012,6 @@ export function useForwarder(signer: Signer | undefined, CHAIN_ID: number) {
|
|
|
802
1012
|
addresses: ADDRESSES,
|
|
803
1013
|
|
|
804
1014
|
// Signer availability
|
|
805
|
-
isSignerAvailable: !!signer,
|
|
1015
|
+
isSignerAvailable: !!signer || !!walletClient,
|
|
806
1016
|
};
|
|
807
1017
|
}
|
|
@@ -172,16 +172,27 @@ export async function waitForEthersTransactionWithRetry(
|
|
|
172
172
|
}
|
|
173
173
|
} catch (error) {
|
|
174
174
|
const errorMessage = parseEthersError(error);
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
)
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
175
|
+
|
|
176
|
+
// Treat not found/receipt missing as retryable without counting towards errors
|
|
177
|
+
const isNotFound =
|
|
178
|
+
errorMessage.toLowerCase().includes("not found") ||
|
|
179
|
+
errorMessage.toLowerCase().includes("could not be found") ||
|
|
180
|
+
errorMessage.toLowerCase().includes("receipt") ||
|
|
181
|
+
errorMessage.toLowerCase().includes("not confirmed") ||
|
|
182
|
+
errorMessage.toLowerCase().includes("transactionreceiptnotfound");
|
|
183
|
+
|
|
184
|
+
if (!isNotFound) {
|
|
185
|
+
consecutiveErrors++;
|
|
186
|
+
if (consecutiveErrors >= maxRetries) {
|
|
187
|
+
throw new Error(
|
|
188
|
+
`Transaction failed after ${consecutiveErrors} attempts: ${errorMessage}`
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
if (enableLogging) {
|
|
192
|
+
console.warn(
|
|
193
|
+
`Error fetching receipt (attempt ${consecutiveErrors}/${maxRetries}), retrying in ${pollIntervalMs}ms...`
|
|
194
|
+
);
|
|
195
|
+
}
|
|
185
196
|
}
|
|
186
197
|
}
|
|
187
198
|
|