thirdweb 5.102.4-nightly-283dc289fca1ad16a9296e610d293b73a7123709-20250602000433 → 5.102.4-nightly-3d1c321bd6f5783bdee43185deef88f91b325a23-20250604000438

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.
Files changed (64) hide show
  1. package/dist/cjs/analytics/track/helpers.js +41 -0
  2. package/dist/cjs/analytics/track/helpers.js.map +1 -0
  3. package/dist/cjs/analytics/track/transaction.js +25 -0
  4. package/dist/cjs/analytics/track/transaction.js.map +1 -1
  5. package/dist/cjs/gas/fee-data.js +1 -0
  6. package/dist/cjs/gas/fee-data.js.map +1 -1
  7. package/dist/cjs/react/core/hooks/transaction/useSendTransaction.js +13 -0
  8. package/dist/cjs/react/core/hooks/transaction/useSendTransaction.js.map +1 -1
  9. package/dist/cjs/transaction/actions/estimate-gas.js +2 -0
  10. package/dist/cjs/transaction/actions/estimate-gas.js.map +1 -1
  11. package/dist/cjs/transaction/actions/simulate.js +1 -0
  12. package/dist/cjs/transaction/actions/simulate.js.map +1 -1
  13. package/dist/cjs/transaction/extract-error.js +13 -1
  14. package/dist/cjs/transaction/extract-error.js.map +1 -1
  15. package/dist/cjs/version.js +1 -1
  16. package/dist/cjs/wallets/injected/index.js +35 -17
  17. package/dist/cjs/wallets/injected/index.js.map +1 -1
  18. package/dist/cjs/wallets/smart/index.js +16 -0
  19. package/dist/cjs/wallets/smart/index.js.map +1 -1
  20. package/dist/esm/analytics/track/helpers.js +37 -0
  21. package/dist/esm/analytics/track/helpers.js.map +1 -0
  22. package/dist/esm/analytics/track/transaction.js +24 -0
  23. package/dist/esm/analytics/track/transaction.js.map +1 -1
  24. package/dist/esm/gas/fee-data.js +1 -0
  25. package/dist/esm/gas/fee-data.js.map +1 -1
  26. package/dist/esm/react/core/hooks/transaction/useSendTransaction.js +13 -0
  27. package/dist/esm/react/core/hooks/transaction/useSendTransaction.js.map +1 -1
  28. package/dist/esm/transaction/actions/estimate-gas.js +2 -0
  29. package/dist/esm/transaction/actions/estimate-gas.js.map +1 -1
  30. package/dist/esm/transaction/actions/simulate.js +1 -0
  31. package/dist/esm/transaction/actions/simulate.js.map +1 -1
  32. package/dist/esm/transaction/extract-error.js +13 -1
  33. package/dist/esm/transaction/extract-error.js.map +1 -1
  34. package/dist/esm/version.js +1 -1
  35. package/dist/esm/wallets/injected/index.js +35 -17
  36. package/dist/esm/wallets/injected/index.js.map +1 -1
  37. package/dist/esm/wallets/smart/index.js +16 -0
  38. package/dist/esm/wallets/smart/index.js.map +1 -1
  39. package/dist/types/analytics/track/helpers.d.ts +12 -0
  40. package/dist/types/analytics/track/helpers.d.ts.map +1 -0
  41. package/dist/types/analytics/track/transaction.d.ts +15 -0
  42. package/dist/types/analytics/track/transaction.d.ts.map +1 -1
  43. package/dist/types/gas/fee-data.d.ts.map +1 -1
  44. package/dist/types/react/core/hooks/transaction/useSendTransaction.d.ts.map +1 -1
  45. package/dist/types/transaction/actions/estimate-gas.d.ts.map +1 -1
  46. package/dist/types/transaction/actions/simulate.d.ts.map +1 -1
  47. package/dist/types/transaction/extract-error.d.ts +1 -0
  48. package/dist/types/transaction/extract-error.d.ts.map +1 -1
  49. package/dist/types/version.d.ts +1 -1
  50. package/dist/types/wallets/injected/index.d.ts.map +1 -1
  51. package/dist/types/wallets/smart/index.d.ts.map +1 -1
  52. package/package.json +1 -1
  53. package/src/analytics/track/helpers.test.ts +109 -0
  54. package/src/analytics/track/helpers.ts +49 -0
  55. package/src/analytics/track/transaction.test.ts +110 -1
  56. package/src/analytics/track/transaction.ts +37 -0
  57. package/src/gas/fee-data.ts +1 -0
  58. package/src/react/core/hooks/transaction/useSendTransaction.ts +14 -0
  59. package/src/transaction/actions/estimate-gas.ts +2 -0
  60. package/src/transaction/actions/simulate.ts +1 -0
  61. package/src/transaction/extract-error.ts +16 -1
  62. package/src/version.ts +1 -1
  63. package/src/wallets/injected/index.ts +36 -18
  64. package/src/wallets/smart/index.ts +16 -0
@@ -0,0 +1,49 @@
1
+ /**
2
+ * @internal
3
+ */
4
+ export function isInsufficientFundsError(error: Error | unknown): boolean {
5
+ if (!error) return false;
6
+
7
+ const errorMessage =
8
+ typeof error === "string"
9
+ ? error
10
+ : (error as Error)?.message ||
11
+ (error as { data?: { message?: string } })?.data?.message ||
12
+ "";
13
+
14
+ const message = errorMessage.toLowerCase();
15
+
16
+ // Common patterns for insufficient funds errors
17
+ return (
18
+ message.includes("insufficient funds") ||
19
+ message.includes("insufficient balance") ||
20
+ (message.includes("insufficient") &&
21
+ (message.includes("native") || message.includes("gas"))) ||
22
+ // Common error codes from various wallets/providers
23
+ (error as { code?: string | number })?.code === "INSUFFICIENT_FUNDS" ||
24
+ (error as { reason?: string })?.reason === "insufficient funds"
25
+ );
26
+ }
27
+
28
+ /**
29
+ * @internal
30
+ */
31
+ export function getErrorDetails(error: Error | unknown): {
32
+ message: string;
33
+ code?: string | number;
34
+ } {
35
+ if (!error) return { message: "Unknown error" };
36
+
37
+ const message =
38
+ typeof error === "string"
39
+ ? error
40
+ : (error as Error)?.message ||
41
+ (error as { data?: { message?: string } })?.data?.message ||
42
+ String(error);
43
+
44
+ const code =
45
+ (error as { code?: string | number })?.code ||
46
+ (error as { reason?: string })?.reason;
47
+
48
+ return { message, code };
49
+ }
@@ -2,7 +2,10 @@ import { http, HttpResponse } from "msw";
2
2
  import { setupServer } from "msw/node";
3
3
  import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest";
4
4
  import type { ThirdwebClient } from "../../client/client.js";
5
- import { trackTransaction } from "./transaction.js";
5
+ import {
6
+ trackInsufficientFundsError,
7
+ trackTransaction,
8
+ } from "./transaction.js";
6
9
 
7
10
  const server = setupServer(
8
11
  http.post("https://c.thirdweb.com/event", () => {
@@ -127,4 +130,110 @@ describe("transaction tracking", () => {
127
130
  "test-partner-id",
128
131
  );
129
132
  });
133
+
134
+ it("should track insufficient funds error with correct data", async () => {
135
+ const mockClient: ThirdwebClient = {
136
+ clientId: "test-client-id",
137
+ secretKey: undefined,
138
+ };
139
+
140
+ let requestBody: unknown;
141
+ server.use(
142
+ http.post("https://c.thirdweb.com/event", async (handler) => {
143
+ requestBody = await handler.request.json();
144
+ return HttpResponse.json({});
145
+ }),
146
+ );
147
+
148
+ const mockError = new Error("Insufficient funds for gas * price + value");
149
+
150
+ await trackInsufficientFundsError({
151
+ client: mockClient,
152
+ error: mockError,
153
+ walletAddress: "0x1234567890123456789012345678901234567890",
154
+ chainId: 1,
155
+ contractAddress: "0xcontract",
156
+ transactionValue: 1000000000000000000n,
157
+ });
158
+
159
+ expect(requestBody).toEqual({
160
+ source: "sdk",
161
+ action: "transaction:insufficient_funds",
162
+ clientId: "test-client-id",
163
+ chainId: 1,
164
+ walletAddress: "0x1234567890123456789012345678901234567890",
165
+ contractAddress: "0xcontract",
166
+ transactionValue: "1000000000000000000",
167
+ requiredAmount: undefined,
168
+ userBalance: undefined,
169
+ errorMessage: "Insufficient funds for gas * price + value",
170
+ errorCode: undefined,
171
+ });
172
+ });
173
+
174
+ it("should not throw an error if insufficient funds tracking request fails", async () => {
175
+ const mockClient: ThirdwebClient = {
176
+ clientId: "test-client-id",
177
+ secretKey: undefined,
178
+ };
179
+
180
+ server.use(
181
+ http.post("https://c.thirdweb.com/event", () => {
182
+ return HttpResponse.error();
183
+ }),
184
+ );
185
+
186
+ const mockError = new Error("insufficient funds");
187
+
188
+ expect(() =>
189
+ trackInsufficientFundsError({
190
+ client: mockClient,
191
+ error: mockError,
192
+ walletAddress: "0x1234567890123456789012345678901234567890",
193
+ chainId: 137,
194
+ }),
195
+ ).not.toThrowError();
196
+
197
+ // Wait for the asynchronous POST request to complete
198
+ await new Promise((resolve) => setTimeout(resolve, 100));
199
+ });
200
+
201
+ it("should track insufficient funds error during transaction preparation", async () => {
202
+ const mockClient: ThirdwebClient = {
203
+ clientId: "test-client-id",
204
+ secretKey: undefined,
205
+ };
206
+
207
+ let requestBody: unknown;
208
+ server.use(
209
+ http.post("https://c.thirdweb.com/event", async (handler) => {
210
+ requestBody = await handler.request.json();
211
+ return HttpResponse.json({});
212
+ }),
213
+ );
214
+
215
+ const mockError = new Error("insufficient funds for gas");
216
+
217
+ await trackInsufficientFundsError({
218
+ client: mockClient,
219
+ error: mockError,
220
+ walletAddress: "0xabcdef1234567890abcdef1234567890abcdef12",
221
+ chainId: 42,
222
+ contractAddress: "0x0987654321098765432109876543210987654321",
223
+ });
224
+
225
+ expect(requestBody).toEqual({
226
+ source: "sdk",
227
+ action: "transaction:insufficient_funds",
228
+ clientId: "test-client-id",
229
+ chainId: 42,
230
+ walletAddress: "0xabcdef1234567890abcdef1234567890abcdef12",
231
+ contractAddress: "0x0987654321098765432109876543210987654321",
232
+ transactionValue: undefined,
233
+ requiredAmount: undefined,
234
+ userBalance: undefined,
235
+ errorMessage: "insufficient funds for gas",
236
+ errorCode: undefined,
237
+ });
238
+ });
130
239
  });
@@ -2,6 +2,7 @@ import type { ThirdwebClient } from "../../client/client.js";
2
2
  import { stringify } from "../../utils/json.js";
3
3
  import type { Ecosystem } from "../../wallets/in-app/core/wallet/types.js";
4
4
  import type { WalletId } from "../../wallets/wallet-types.js";
5
+ import { getErrorDetails } from "./helpers.js";
5
6
  import { track } from "./index.js";
6
7
 
7
8
  type TransactionEvent = {
@@ -55,3 +56,39 @@ function trackTransactionEvent(
55
56
  },
56
57
  });
57
58
  }
59
+
60
+ /**
61
+ * @internal
62
+ */
63
+ export async function trackInsufficientFundsError(args: {
64
+ client: ThirdwebClient;
65
+ ecosystem?: Ecosystem;
66
+ error: Error | unknown;
67
+ walletAddress?: string;
68
+ chainId?: number;
69
+ contractAddress?: string;
70
+ functionName?: string;
71
+ transactionValue?: bigint;
72
+ requiredAmount?: bigint;
73
+ userBalance?: bigint;
74
+ }) {
75
+ const errorDetails = getErrorDetails(args.error);
76
+
77
+ return track({
78
+ client: args.client,
79
+ ecosystem: args.ecosystem,
80
+ data: {
81
+ action: "transaction:insufficient_funds",
82
+ clientId: args.client.clientId,
83
+ chainId: args.chainId,
84
+ walletAddress: args.walletAddress,
85
+ contractAddress: args.contractAddress,
86
+ functionName: args.functionName,
87
+ transactionValue: args.transactionValue?.toString(),
88
+ requiredAmount: args.requiredAmount?.toString(),
89
+ userBalance: args.userBalance?.toString(),
90
+ errorMessage: errorDetails.message,
91
+ errorCode: errorDetails.code ? stringify(errorDetails.code) : undefined,
92
+ },
93
+ });
94
+ }
@@ -46,6 +46,7 @@ const FORCE_GAS_PRICE_CHAIN_IDS = [
46
46
  5464, // Saga Mainnet
47
47
  2020, // Ronin Mainnet
48
48
  2021, // Ronin Testnet (Saigon)
49
+ 98866, // Plume mainnet
49
50
  ];
50
51
 
51
52
  /**
@@ -1,5 +1,7 @@
1
1
  import { type UseMutationResult, useMutation } from "@tanstack/react-query";
2
+ import { isInsufficientFundsError } from "../../../../analytics/track/helpers.js";
2
3
  import { trackPayEvent } from "../../../../analytics/track/pay.js";
4
+ import { trackInsufficientFundsError } from "../../../../analytics/track/transaction.js";
3
5
  import * as Bridge from "../../../../bridge/index.js";
4
6
  import type { Chain } from "../../../../chains/types.js";
5
7
  import type { BuyWithCryptoStatus } from "../../../../pay/buyWithCrypto/getStatus.js";
@@ -174,6 +176,18 @@ export function useSendTransactionCore(args: {
174
176
 
175
177
  resolve(res);
176
178
  } catch (e) {
179
+ // Track insufficient funds errors specifically
180
+ if (isInsufficientFundsError(e)) {
181
+ trackInsufficientFundsError({
182
+ client: tx.client,
183
+ error: e,
184
+ walletAddress: account.address,
185
+ chainId: tx.chain.id,
186
+ contractAddress: await resolvePromisedValue(tx.to ?? undefined),
187
+ transactionValue: await resolvePromisedValue(tx.value),
188
+ });
189
+ }
190
+
177
191
  reject(e);
178
192
  }
179
193
  };
@@ -94,6 +94,7 @@ export async function estimateGas(
94
94
  throw await extractError({
95
95
  error,
96
96
  contract: options.transaction.__contract,
97
+ fromAddress,
97
98
  });
98
99
  }
99
100
  }
@@ -150,6 +151,7 @@ export async function estimateGas(
150
151
  throw await extractError({
151
152
  error,
152
153
  contract: options.transaction.__contract,
154
+ fromAddress,
153
155
  });
154
156
  }
155
157
  })();
@@ -89,6 +89,7 @@ export async function simulateTransaction<
89
89
  throw await extractError({
90
90
  error,
91
91
  contract: options.transaction.__contract,
92
+ fromAddress: from,
92
93
  });
93
94
  }
94
95
  }
@@ -1,5 +1,7 @@
1
1
  import type { Abi } from "abitype";
2
2
  import { type Hex, decodeErrorResult, stringify } from "viem";
3
+ import { isInsufficientFundsError } from "../analytics/track/helpers.js";
4
+ import { trackInsufficientFundsError } from "../analytics/track/transaction.js";
3
5
  import { resolveContractAbi } from "../contract/actions/resolve-abi.js";
4
6
  import type { ThirdwebContract } from "../contract/contract.js";
5
7
  import { isHex } from "../utils/encoding/hex.js";
@@ -11,8 +13,21 @@ import { IS_DEV } from "../utils/process.js";
11
13
  export async function extractError<abi extends Abi>(args: {
12
14
  error: unknown;
13
15
  contract?: ThirdwebContract<abi>;
16
+ fromAddress?: string;
14
17
  }) {
15
- const { error, contract } = args;
18
+ const { error, contract, fromAddress } = args;
19
+
20
+ // Track insufficient funds errors during transaction preparation
21
+ if (isInsufficientFundsError(error) && contract) {
22
+ trackInsufficientFundsError({
23
+ client: contract.client,
24
+ error,
25
+ walletAddress: fromAddress,
26
+ chainId: contract.chain?.id,
27
+ contractAddress: contract.address,
28
+ });
29
+ }
30
+
16
31
  const result = await extractErrorResult({ error, contract });
17
32
  if (result) {
18
33
  return new TransactionError(result, contract);
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const version = "5.102.4-nightly-283dc289fca1ad16a9296e610d293b73a7123709-20250602000433";
1
+ export const version = "5.102.4-nightly-3d1c321bd6f5783bdee43185deef88f91b325a23-20250604000438";
@@ -5,7 +5,9 @@ import {
5
5
  serializeTypedData,
6
6
  validateTypedData,
7
7
  } from "viem";
8
+ import { isInsufficientFundsError } from "../../analytics/track/helpers.js";
8
9
  import { trackTransaction } from "../../analytics/track/transaction.js";
10
+ import { trackInsufficientFundsError } from "../../analytics/track/transaction.js";
9
11
  import type { Chain } from "../../chains/types.js";
10
12
  import { getCachedChain, getChainMetadata } from "../../chains/utils.js";
11
13
  import type { ThirdwebClient } from "../../client/client.js";
@@ -200,25 +202,41 @@ function createAccount({
200
202
  },
201
203
  ];
202
204
 
203
- const transactionHash = (await provider.request({
204
- method: "eth_sendTransaction",
205
- // @ts-expect-error - overriding types here
206
- params,
207
- })) as Hex;
208
-
209
- trackTransaction({
210
- client,
211
- chainId: tx.chainId,
212
- walletAddress: getAddress(address),
213
- walletType: id,
214
- transactionHash,
215
- contractAddress: tx.to ?? undefined,
216
- gasPrice: tx.gasPrice,
217
- });
205
+ try {
206
+ const transactionHash = (await provider.request({
207
+ method: "eth_sendTransaction",
208
+ // @ts-expect-error - overriding types here
209
+ params,
210
+ })) as Hex;
211
+
212
+ trackTransaction({
213
+ client,
214
+ chainId: tx.chainId,
215
+ walletAddress: getAddress(address),
216
+ walletType: id,
217
+ transactionHash,
218
+ contractAddress: tx.to ?? undefined,
219
+ gasPrice: tx.gasPrice,
220
+ });
221
+
222
+ return {
223
+ transactionHash,
224
+ };
225
+ } catch (error) {
226
+ // Track insufficient funds errors
227
+ if (isInsufficientFundsError(error)) {
228
+ trackInsufficientFundsError({
229
+ client,
230
+ error,
231
+ walletAddress: getAddress(address),
232
+ chainId: tx.chainId,
233
+ contractAddress: tx.to || undefined,
234
+ transactionValue: tx.value,
235
+ });
236
+ }
218
237
 
219
- return {
220
- transactionHash,
221
- };
238
+ throw error;
239
+ }
222
240
  },
223
241
  async signMessage({ message }) {
224
242
  if (!account.address) {
@@ -1,5 +1,7 @@
1
1
  import type * as ox__TypedData from "ox/TypedData";
2
+ import { isInsufficientFundsError } from "../../analytics/track/helpers.js";
2
3
  import { trackTransaction } from "../../analytics/track/transaction.js";
4
+ import { trackInsufficientFundsError } from "../../analytics/track/transaction.js";
3
5
  import type { Chain } from "../../chains/types.js";
4
6
  import { getCachedChain } from "../../chains/utils.js";
5
7
  import type { ThirdwebClient } from "../../client/client.js";
@@ -559,6 +561,20 @@ async function _sendUserOp(args: {
559
561
  chain: options.chain,
560
562
  transactionHash: receipt.transactionHash,
561
563
  };
564
+ } catch (error) {
565
+ // Track insufficient funds errors
566
+ if (isInsufficientFundsError(error)) {
567
+ trackInsufficientFundsError({
568
+ client: options.client,
569
+ error,
570
+ walletAddress: options.accountContract.address,
571
+ chainId: options.chain.id,
572
+ contractAddress: await resolvePromisedValue(executeTx.to ?? undefined),
573
+ transactionValue: await resolvePromisedValue(executeTx.value),
574
+ });
575
+ }
576
+
577
+ throw error;
562
578
  } finally {
563
579
  // reset the isDeploying flag after every transaction or error
564
580
  clearAccountDeploying(options.accountContract);