thirdweb 5.72.0 → 5.73.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.
Files changed (66) hide show
  1. package/dist/cjs/exports/pay.js +5 -1
  2. package/dist/cjs/exports/pay.js.map +1 -1
  3. package/dist/cjs/pay/convert/cryptoToFiat.js +73 -0
  4. package/dist/cjs/pay/convert/cryptoToFiat.js.map +1 -0
  5. package/dist/cjs/pay/convert/fiatToCrypto.js +73 -0
  6. package/dist/cjs/pay/convert/fiatToCrypto.js.map +1 -0
  7. package/dist/cjs/pay/utils/definitions.js +5 -1
  8. package/dist/cjs/pay/utils/definitions.js.map +1 -1
  9. package/dist/cjs/react/core/hooks/wallets/useConnect.js +8 -1
  10. package/dist/cjs/react/core/hooks/wallets/useConnect.js.map +1 -1
  11. package/dist/cjs/transaction/actions/zksync/send-eip712-transaction.js +3 -1
  12. package/dist/cjs/transaction/actions/zksync/send-eip712-transaction.js.map +1 -1
  13. package/dist/cjs/version.js +1 -1
  14. package/dist/cjs/wallets/in-app/core/actions/get-enclave-user-status.js +1 -1
  15. package/dist/cjs/wallets/in-app/core/actions/get-enclave-user-status.js.map +1 -1
  16. package/dist/cjs/wallets/manager/index.js +27 -20
  17. package/dist/cjs/wallets/manager/index.js.map +1 -1
  18. package/dist/cjs/wallets/smart/lib/paymaster.js +4 -0
  19. package/dist/cjs/wallets/smart/lib/paymaster.js.map +1 -1
  20. package/dist/esm/exports/pay.js +2 -0
  21. package/dist/esm/exports/pay.js.map +1 -1
  22. package/dist/esm/pay/convert/cryptoToFiat.js +70 -0
  23. package/dist/esm/pay/convert/cryptoToFiat.js.map +1 -0
  24. package/dist/esm/pay/convert/fiatToCrypto.js +70 -0
  25. package/dist/esm/pay/convert/fiatToCrypto.js.map +1 -0
  26. package/dist/esm/pay/utils/definitions.js +2 -0
  27. package/dist/esm/pay/utils/definitions.js.map +1 -1
  28. package/dist/esm/react/core/hooks/wallets/useConnect.js +8 -1
  29. package/dist/esm/react/core/hooks/wallets/useConnect.js.map +1 -1
  30. package/dist/esm/transaction/actions/zksync/send-eip712-transaction.js +3 -1
  31. package/dist/esm/transaction/actions/zksync/send-eip712-transaction.js.map +1 -1
  32. package/dist/esm/version.js +1 -1
  33. package/dist/esm/wallets/in-app/core/actions/get-enclave-user-status.js +1 -1
  34. package/dist/esm/wallets/in-app/core/actions/get-enclave-user-status.js.map +1 -1
  35. package/dist/esm/wallets/manager/index.js +27 -20
  36. package/dist/esm/wallets/manager/index.js.map +1 -1
  37. package/dist/esm/wallets/smart/lib/paymaster.js +4 -0
  38. package/dist/esm/wallets/smart/lib/paymaster.js.map +1 -1
  39. package/dist/types/exports/pay.d.ts +2 -0
  40. package/dist/types/exports/pay.d.ts.map +1 -1
  41. package/dist/types/pay/convert/cryptoToFiat.d.ts +54 -0
  42. package/dist/types/pay/convert/cryptoToFiat.d.ts.map +1 -0
  43. package/dist/types/pay/convert/fiatToCrypto.d.ts +55 -0
  44. package/dist/types/pay/convert/fiatToCrypto.d.ts.map +1 -0
  45. package/dist/types/pay/utils/definitions.d.ts +2 -0
  46. package/dist/types/pay/utils/definitions.d.ts.map +1 -1
  47. package/dist/types/react/core/hooks/wallets/useConnect.d.ts +8 -1
  48. package/dist/types/react/core/hooks/wallets/useConnect.d.ts.map +1 -1
  49. package/dist/types/transaction/actions/zksync/send-eip712-transaction.d.ts.map +1 -1
  50. package/dist/types/version.d.ts +1 -1
  51. package/dist/types/wallets/manager/index.d.ts.map +1 -1
  52. package/dist/types/wallets/smart/lib/paymaster.d.ts.map +1 -1
  53. package/package.json +22 -21
  54. package/src/exports/pay.ts +10 -0
  55. package/src/pay/convert/cryptoToFiat.test.ts +95 -0
  56. package/src/pay/convert/cryptoToFiat.ts +111 -0
  57. package/src/pay/convert/fiatToCrypto.test.ts +96 -0
  58. package/src/pay/convert/fiatToCrypto.ts +110 -0
  59. package/src/pay/utils/definitions.ts +6 -0
  60. package/src/react/core/hooks/wallets/useConnect.ts +8 -1
  61. package/src/transaction/actions/zksync/send-eip712-transaction.ts +5 -1
  62. package/src/version.ts +1 -1
  63. package/src/wallets/in-app/core/actions/get-enclave-user-status.ts +1 -1
  64. package/src/wallets/manager/connection-manager.test.ts +68 -0
  65. package/src/wallets/manager/index.ts +39 -20
  66. package/src/wallets/smart/lib/paymaster.ts +7 -0
@@ -0,0 +1,95 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { TEST_CLIENT } from "~test/test-clients.js";
3
+ import { TEST_ACCOUNT_A } from "~test/test-wallets.js";
4
+ import { base } from "../../chains/chain-definitions/base.js";
5
+ import { ethereum } from "../../chains/chain-definitions/ethereum.js";
6
+ import { sepolia } from "../../chains/chain-definitions/sepolia.js";
7
+ import { NATIVE_TOKEN_ADDRESS } from "../../constants/addresses.js";
8
+ import { convertCryptoToFiat } from "./cryptoToFiat.js";
9
+
10
+ describe.runIf(process.env.TW_SECRET_KEY)("Pay: crypto-to-fiat", () => {
11
+ it("should convert ETH price to USD on Ethereum mainnet", async () => {
12
+ const data = await convertCryptoToFiat({
13
+ chain: ethereum,
14
+ fromTokenAddress: NATIVE_TOKEN_ADDRESS,
15
+ fromAmount: 1,
16
+ to: "USD",
17
+ client: TEST_CLIENT,
18
+ });
19
+ expect(data.result).toBeDefined();
20
+ // Should be a number
21
+ expect(!Number.isNaN(data.result)).toBe(true);
22
+ // Since eth is around US$3000, we can add a test to check if the price is greater than $1500 (as a safe margin)
23
+ // let's hope that scenario does not happen :(
24
+ expect(Number(data.result) > 1500).toBe(true);
25
+ });
26
+
27
+ it("should convert ETH price to USD on Base mainnet", async () => {
28
+ const data = await convertCryptoToFiat({
29
+ chain: base,
30
+ fromTokenAddress: NATIVE_TOKEN_ADDRESS,
31
+ fromAmount: 1,
32
+ to: "USD",
33
+ client: TEST_CLIENT,
34
+ });
35
+ expect(data.result).toBeDefined();
36
+ // Should be a number
37
+ expect(!Number.isNaN(data.result)).toBe(true);
38
+ // Since eth is around US$3000, we can add a test to check if the price is greater than $1500 (as a safe margin)
39
+ // let's hope that scenario does not happen :(
40
+ expect(data.result > 1500).toBe(true);
41
+ });
42
+
43
+ it("should return zero if fromAmount is zero", async () => {
44
+ const data = await convertCryptoToFiat({
45
+ chain: base,
46
+ fromTokenAddress: NATIVE_TOKEN_ADDRESS,
47
+ fromAmount: 0,
48
+ to: "USD",
49
+ client: TEST_CLIENT,
50
+ });
51
+ expect(data.result).toBe(0);
52
+ });
53
+
54
+ it("should throw error for testnet chain (because testnets are not supported", async () => {
55
+ await expect(() =>
56
+ convertCryptoToFiat({
57
+ chain: sepolia,
58
+ fromTokenAddress: NATIVE_TOKEN_ADDRESS,
59
+ fromAmount: 1,
60
+ to: "USD",
61
+ client: TEST_CLIENT,
62
+ }),
63
+ ).rejects.toThrowError(
64
+ `Cannot fetch price for a testnet (chainId: ${sepolia.id})`,
65
+ );
66
+ });
67
+
68
+ it("should throw error if fromTokenAddress is set to an invalid EVM address", async () => {
69
+ await expect(() =>
70
+ convertCryptoToFiat({
71
+ chain: ethereum,
72
+ fromTokenAddress: "haha",
73
+ fromAmount: 1,
74
+ to: "USD",
75
+ client: TEST_CLIENT,
76
+ }),
77
+ ).rejects.toThrowError(
78
+ "Invalid fromTokenAddress. Expected a valid EVM contract address",
79
+ );
80
+ });
81
+
82
+ it("should throw error if fromTokenAddress is set to a wallet address", async () => {
83
+ await expect(() =>
84
+ convertCryptoToFiat({
85
+ chain: base,
86
+ fromTokenAddress: TEST_ACCOUNT_A.address,
87
+ fromAmount: 1,
88
+ to: "USD",
89
+ client: TEST_CLIENT,
90
+ }),
91
+ ).rejects.toThrowError(
92
+ `Error: ${TEST_ACCOUNT_A.address} on chainId: ${base.id} is not a valid contract address.`,
93
+ );
94
+ });
95
+ });
@@ -0,0 +1,111 @@
1
+ import type { Address } from "abitype";
2
+ import type { Chain } from "../../chains/types.js";
3
+ import type { ThirdwebClient } from "../../client/client.js";
4
+ import { NATIVE_TOKEN_ADDRESS } from "../../constants/addresses.js";
5
+ import { getBytecode } from "../../contract/actions/get-bytecode.js";
6
+ import { getContract } from "../../contract/contract.js";
7
+ import { isAddress } from "../../utils/address.js";
8
+ import { getClientFetch } from "../../utils/fetch.js";
9
+ import { getPayConvertCryptoToFiatEndpoint } from "../utils/definitions.js";
10
+
11
+ /**
12
+ * Props for the `convertCryptoToFiat` function
13
+ * @buyCrypto
14
+ */
15
+ export type ConvertCryptoToFiatParams = {
16
+ client: ThirdwebClient;
17
+ /**
18
+ * The contract address of the token
19
+ * For native token, use NATIVE_TOKEN_ADDRESS
20
+ */
21
+ fromTokenAddress: Address;
22
+ /**
23
+ * The amount of token to convert to fiat value
24
+ */
25
+ fromAmount: number;
26
+ /**
27
+ * The chain that the token is deployed to
28
+ */
29
+ chain: Chain;
30
+ /**
31
+ * The fiat symbol. e.g "USD"
32
+ * Only USD is supported at the moment.
33
+ */
34
+ to: "USD";
35
+ };
36
+
37
+ /**
38
+ * Get a price of a token (using tokenAddress + chainId) in fiat.
39
+ * Only USD is supported at the moment.
40
+ * @example
41
+ * ### Basic usage
42
+ * For native token (non-ERC20), you should use NATIVE_TOKEN_ADDRESS as the value for `tokenAddress`
43
+ * ```ts
44
+ * import { convertCryptoToFiat } from "thirdweb/pay";
45
+ *
46
+ * // Get Ethereum price
47
+ * const result = convertCryptoToFiat({
48
+ * fromTokenAddress: NATIVE_TOKEN_ADDRESS,
49
+ * to: "USD",
50
+ * chain: ethereum,
51
+ * fromAmount: 1,
52
+ * });
53
+ *
54
+ * // Result: `{ result: 3404.11 }`
55
+ * ```
56
+ * @buyCrypto
57
+ * @returns a number representing the price (in selected fiat) of "x" token, with "x" being the `fromAmount`.
58
+ */
59
+ export async function convertCryptoToFiat(
60
+ options: ConvertCryptoToFiatParams,
61
+ ): Promise<{ result: number }> {
62
+ const { client, fromTokenAddress, to, chain, fromAmount } = options;
63
+ if (Number(fromAmount) === 0) {
64
+ return { result: 0 };
65
+ }
66
+ // Testnets just don't work with our current provider(s)
67
+ if (chain.testnet === true) {
68
+ throw new Error(`Cannot fetch price for a testnet (chainId: ${chain.id})`);
69
+ }
70
+ // Some provider that we are using will return `0` for unsupported token
71
+ // so we should do some basic input validations before sending the request
72
+
73
+ // Make sure it's a valid EVM address
74
+ if (!isAddress(fromTokenAddress)) {
75
+ throw new Error(
76
+ "Invalid fromTokenAddress. Expected a valid EVM contract address",
77
+ );
78
+ }
79
+ // Make sure it's either a valid contract or a native token address
80
+ if (fromTokenAddress.toLowerCase() !== NATIVE_TOKEN_ADDRESS.toLowerCase()) {
81
+ const bytecode = await getBytecode(
82
+ getContract({
83
+ address: fromTokenAddress,
84
+ chain,
85
+ client,
86
+ }),
87
+ ).catch(() => undefined);
88
+ if (!bytecode || bytecode === "0x") {
89
+ throw new Error(
90
+ `Error: ${fromTokenAddress} on chainId: ${chain.id} is not a valid contract address.`,
91
+ );
92
+ }
93
+ }
94
+ const params = {
95
+ fromTokenAddress,
96
+ to,
97
+ chainId: String(chain.id),
98
+ fromAmount: String(fromAmount),
99
+ };
100
+ const queryString = new URLSearchParams(params).toString();
101
+ const url = `${getPayConvertCryptoToFiatEndpoint()}?${queryString}`;
102
+ const response = await getClientFetch(client)(url);
103
+ if (!response.ok) {
104
+ throw new Error(
105
+ `Failed to fetch ${to} value for token (${fromTokenAddress}) on chainId: ${chain.id}`,
106
+ );
107
+ }
108
+
109
+ const data: { result: number } = await response.json();
110
+ return data;
111
+ }
@@ -0,0 +1,96 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { TEST_CLIENT } from "~test/test-clients.js";
3
+ import { TEST_ACCOUNT_A } from "~test/test-wallets.js";
4
+ import { base } from "../../chains/chain-definitions/base.js";
5
+ import { ethereum } from "../../chains/chain-definitions/ethereum.js";
6
+ import { sepolia } from "../../chains/chain-definitions/sepolia.js";
7
+ import { NATIVE_TOKEN_ADDRESS } from "../../constants/addresses.js";
8
+ import { convertFiatToCrypto } from "./fiatToCrypto.js";
9
+
10
+ describe.runIf(process.env.TW_SECRET_KEY)("Pay: fiatToCrypto", () => {
11
+ it("should convert fiat price to token on Ethereum mainnet", async () => {
12
+ const data = await convertFiatToCrypto({
13
+ chain: ethereum,
14
+ from: "USD",
15
+ fromAmount: 1,
16
+ to: NATIVE_TOKEN_ADDRESS,
17
+ client: TEST_CLIENT,
18
+ });
19
+ expect(data.result).toBeDefined();
20
+ // Should be a number
21
+ expect(!Number.isNaN(data.result)).toBe(true);
22
+ // Since eth is around US$3000, 1 USD should be around 0.0003
23
+ // we give it some safe margin so the test won't be flaky
24
+ expect(data.result < 0.001).toBe(true);
25
+ });
26
+
27
+ it("should convert fiat price to token on Base mainnet", async () => {
28
+ const data = await convertFiatToCrypto({
29
+ chain: base,
30
+ from: "USD",
31
+ fromAmount: 1,
32
+ to: NATIVE_TOKEN_ADDRESS,
33
+ client: TEST_CLIENT,
34
+ });
35
+
36
+ expect(data.result).toBeDefined();
37
+ // Should be a number
38
+ expect(!Number.isNaN(data.result)).toBe(true);
39
+ // Since eth is around US$3000, 1 USD should be around 0.0003
40
+ // we give it some safe margin so the test won't be flaky
41
+ expect(data.result < 0.001).toBe(true);
42
+ });
43
+
44
+ it("should return zero if the fromAmount is zero", async () => {
45
+ const data = await convertFiatToCrypto({
46
+ chain: base,
47
+ from: "USD",
48
+ fromAmount: 0,
49
+ to: NATIVE_TOKEN_ADDRESS,
50
+ client: TEST_CLIENT,
51
+ });
52
+ expect(data.result).toBe(0);
53
+ });
54
+
55
+ it("should throw error for testnet chain (because testnets are not supported", async () => {
56
+ await expect(() =>
57
+ convertFiatToCrypto({
58
+ chain: sepolia,
59
+ to: NATIVE_TOKEN_ADDRESS,
60
+ fromAmount: 1,
61
+ from: "USD",
62
+ client: TEST_CLIENT,
63
+ }),
64
+ ).rejects.toThrowError(
65
+ `Cannot fetch price for a testnet (chainId: ${sepolia.id})`,
66
+ );
67
+ });
68
+
69
+ it("should throw error if `to` is set to an invalid EVM address", async () => {
70
+ await expect(() =>
71
+ convertFiatToCrypto({
72
+ chain: ethereum,
73
+ to: "haha",
74
+ fromAmount: 1,
75
+ from: "USD",
76
+ client: TEST_CLIENT,
77
+ }),
78
+ ).rejects.toThrowError(
79
+ "Invalid `to`. Expected a valid EVM contract address",
80
+ );
81
+ });
82
+
83
+ it("should throw error if `to` is set to a wallet address", async () => {
84
+ await expect(() =>
85
+ convertFiatToCrypto({
86
+ chain: base,
87
+ to: TEST_ACCOUNT_A.address,
88
+ fromAmount: 1,
89
+ from: "USD",
90
+ client: TEST_CLIENT,
91
+ }),
92
+ ).rejects.toThrowError(
93
+ `Error: ${TEST_ACCOUNT_A.address} on chainId: ${base.id} is not a valid contract address.`,
94
+ );
95
+ });
96
+ });
@@ -0,0 +1,110 @@
1
+ import type { Address } from "abitype";
2
+ import type { Chain } from "../../chains/types.js";
3
+ import type { ThirdwebClient } from "../../client/client.js";
4
+ import { NATIVE_TOKEN_ADDRESS } from "../../constants/addresses.js";
5
+ import { getBytecode } from "../../contract/actions/get-bytecode.js";
6
+ import { getContract } from "../../contract/contract.js";
7
+ import { isAddress } from "../../utils/address.js";
8
+ import { getClientFetch } from "../../utils/fetch.js";
9
+ import { getPayConvertFiatToCryptoEndpoint } from "../utils/definitions.js";
10
+
11
+ /**
12
+ * Props for the `convertFiatToCrypto` function
13
+ * @buyCrypto
14
+ */
15
+ export type ConvertFiatToCryptoParams = {
16
+ client: ThirdwebClient;
17
+ /**
18
+ * The fiat symbol. e.g: "USD"
19
+ * Currently only USD is supported.
20
+ */
21
+ from: "USD";
22
+ /**
23
+ * The total amount of fiat to convert
24
+ * e.g: If you want to convert 2 cents to USD, enter `0.02`
25
+ */
26
+ fromAmount: number;
27
+ /**
28
+ * The token address
29
+ * For native token, use NATIVE_TOKEN_ADDRESS
30
+ */
31
+ to: Address;
32
+ /**
33
+ * The chain that the token is deployed to
34
+ */
35
+ chain: Chain;
36
+ };
37
+
38
+ /**
39
+ * Convert a fiat value to a token.
40
+ * Currently only USD is supported.
41
+ * @example
42
+ * ### Basic usage
43
+ * ```ts
44
+ * import { convertFiatToCrypto } from "thirdweb/pay";
45
+ *
46
+ * // Convert 2 cents to ETH
47
+ * const result = await convertFiatToCrypto({
48
+ * from: "USD",
49
+ * // the token address. For native token, use NATIVE_TOKEN_ADDRESS
50
+ * to: "0x...",
51
+ * // the chain (of the chain where the token belong to)
52
+ * chain: ethereum,
53
+ * // 2 cents
54
+ * fromAmount: 0.02,
55
+ * });
56
+ * ```
57
+ * Result: `{ result: 0.0000057 }`
58
+ * @buyCrypto
59
+ */
60
+ export async function convertFiatToCrypto(
61
+ options: ConvertFiatToCryptoParams,
62
+ ): Promise<{ result: number }> {
63
+ const { client, from, to, chain, fromAmount } = options;
64
+ if (Number(fromAmount) === 0) {
65
+ return { result: 0 };
66
+ }
67
+ // Testnets just don't work with our current provider(s)
68
+ if (chain.testnet === true) {
69
+ throw new Error(`Cannot fetch price for a testnet (chainId: ${chain.id})`);
70
+ }
71
+ // Some provider that we are using will return `0` for unsupported token
72
+ // so we should do some basic input validations before sending the request
73
+
74
+ // Make sure it's a valid EVM address
75
+ if (!isAddress(to)) {
76
+ throw new Error("Invalid `to`. Expected a valid EVM contract address");
77
+ }
78
+ // Make sure it's either a valid contract or a native token
79
+ if (to.toLowerCase() !== NATIVE_TOKEN_ADDRESS.toLowerCase()) {
80
+ const bytecode = await getBytecode(
81
+ getContract({
82
+ address: to,
83
+ chain,
84
+ client,
85
+ }),
86
+ ).catch(() => undefined);
87
+ if (!bytecode || bytecode === "0x") {
88
+ throw new Error(
89
+ `Error: ${to} on chainId: ${chain.id} is not a valid contract address.`,
90
+ );
91
+ }
92
+ }
93
+ const params = {
94
+ from,
95
+ to,
96
+ chainId: String(chain.id),
97
+ fromAmount: String(fromAmount),
98
+ };
99
+ const queryString = new URLSearchParams(params).toString();
100
+ const url = `${getPayConvertFiatToCryptoEndpoint()}?${queryString}`;
101
+ const response = await getClientFetch(client)(url);
102
+ if (!response.ok) {
103
+ throw new Error(
104
+ `Failed to convert ${to} value to token (${to}) on chainId: ${chain.id}`,
105
+ );
106
+ }
107
+
108
+ const data: { result: number } = await response.json();
109
+ return data;
110
+ }
@@ -76,3 +76,9 @@ export const getPaySupportedSources = () =>
76
76
  */
77
77
  export const getPayBuyHistoryEndpoint = () =>
78
78
  `${getPayBaseUrl()}/wallet/history/v1`;
79
+
80
+ export const getPayConvertFiatToCryptoEndpoint = () =>
81
+ `${getPayBaseUrl()}/convert/fiat-to-crypto/v1`;
82
+
83
+ export const getPayConvertCryptoToFiatEndpoint = () =>
84
+ `${getPayBaseUrl()}/convert/crypto-to-fiat/v1`;
@@ -11,9 +11,14 @@ import { useSetActiveWalletConnectionStatus } from "./useSetActiveWalletConnecti
11
11
  * @returns A function that lets you connect a wallet.
12
12
  * @example
13
13
  * ```jsx
14
+ * import { createThirdwebClient } from "thirdweb";
14
15
  * import { useConnect } from "thirdweb/react";
15
16
  * import { createWallet } from "thirdweb/wallets";
16
17
  *
18
+ * const client = createThirdwebClient({
19
+ * clientId: "YOUR_CLIENT_ID",
20
+ * });
21
+ *
17
22
  * function Example() {
18
23
  * const { connect, isConnecting, error } = useConnect();
19
24
  * return (
@@ -23,7 +28,9 @@ import { useSetActiveWalletConnectionStatus } from "./useSetActiveWalletConnecti
23
28
  * // instantiate wallet
24
29
  * const wallet = createWallet("io.metamask");
25
30
  * // connect wallet
26
- * await wallet.connect();
31
+ * await wallet.connect({
32
+ * client,
33
+ * });
27
34
  * // return the wallet
28
35
  * return wallet;
29
36
  * })
@@ -173,7 +173,11 @@ export async function getZkGasFees(args: {
173
173
  resolvePromisedValue(transaction.eip712),
174
174
  ]);
175
175
  let gasPerPubdata = eip712?.gasPerPubdata;
176
- if (!gas || !maxFeePerGas || !maxPriorityFeePerGas) {
176
+ if (
177
+ gas === undefined ||
178
+ maxFeePerGas === undefined ||
179
+ maxPriorityFeePerGas === undefined
180
+ ) {
177
181
  const rpc = getRpcClient(transaction);
178
182
  const params = await formatTransaction({ transaction, from });
179
183
  const result = (await rpc({
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const version = "5.72.0";
1
+ export const version = "5.73.0";
@@ -37,7 +37,7 @@ export async function getUserStatus({
37
37
  return undefined;
38
38
  }
39
39
  const result = await response.json();
40
- throw new Error(`Failed to get user status: ${result.error}`);
40
+ throw new Error(`Failed to get user status: ${result.message}`);
41
41
  }
42
42
 
43
43
  return (await response.json()) as UserStatus;
@@ -0,0 +1,68 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+ import { TEST_CLIENT } from "../../../test/src/test-clients.js";
3
+ import { TEST_ACCOUNT_A } from "../../../test/src/test-wallets.js";
4
+ import { sepolia } from "../../chains/chain-definitions/sepolia.js";
5
+ import type { ThirdwebClient } from "../../client/client.js";
6
+ import type { AsyncStorage } from "../../utils/storage/AsyncStorage.js";
7
+ import type { Account, Wallet } from "../interfaces/wallet.js";
8
+ import type { SmartWalletOptions } from "../smart/types.js";
9
+ import { createConnectionManager } from "./index.js";
10
+
11
+ describe.runIf(process.env.TW_SECRET_KEY)("Connection Manager", () => {
12
+ let storage: AsyncStorage;
13
+ let client: ThirdwebClient;
14
+ let wallet: Wallet;
15
+ let account: Account;
16
+ let smartWalletOptions: SmartWalletOptions;
17
+
18
+ beforeEach(() => {
19
+ storage = {
20
+ getItem: vi.fn(),
21
+ setItem: vi.fn(),
22
+ removeItem: vi.fn(),
23
+ };
24
+ client = TEST_CLIENT;
25
+ account = TEST_ACCOUNT_A;
26
+ wallet = {
27
+ id: "wallet-id",
28
+ getAccount: vi.fn().mockReturnValue(account),
29
+ subscribe: vi.fn(),
30
+ disconnect: vi.fn(),
31
+ switchChain: vi.fn(),
32
+ getChain: vi.fn().mockReturnValue(sepolia),
33
+ getConfig: vi.fn(),
34
+ } as unknown as Wallet;
35
+ smartWalletOptions = {
36
+ chain: sepolia,
37
+ } as SmartWalletOptions;
38
+ });
39
+
40
+ it("connect should handle connection and call onConnect", async () => {
41
+ const manager = createConnectionManager(storage);
42
+ const onConnect = vi.fn();
43
+
44
+ await manager.connect(wallet, { client, onConnect });
45
+
46
+ expect(onConnect).toHaveBeenCalledWith(wallet);
47
+ expect(storage.setItem).toHaveBeenCalledWith(expect.any(String), wallet.id);
48
+ });
49
+
50
+ it("handleConnection should connect smart wallet", async () => {
51
+ const manager = createConnectionManager(storage);
52
+
53
+ const smartWallet = await manager.handleConnection(wallet, {
54
+ client,
55
+ accountAbstraction: smartWalletOptions,
56
+ });
57
+
58
+ expect(manager.activeWalletStore.getValue()).toBe(smartWallet);
59
+ });
60
+
61
+ it("handleConnection should add wallet to connected wallets", async () => {
62
+ const manager = createConnectionManager(storage);
63
+
64
+ await manager.handleConnection(wallet, { client });
65
+
66
+ expect(manager.connectedWallets.getValue()).toContain(wallet);
67
+ });
68
+ });
@@ -128,37 +128,56 @@ export function createConnectionManager(storage: AsyncStorage) {
128
128
  throw new Error("Can not set a wallet without an account as active");
129
129
  }
130
130
 
131
- const personalWallet = wallet;
132
- let activeWallet = personalWallet;
133
- const isInAppSmartAccount = hasSmartAccount(wallet);
134
- if (options?.accountAbstraction && !isInAppSmartAccount) {
135
- activeWallet = smartWallet(options.accountAbstraction);
136
- await activeWallet.connect({
137
- personalAccount: wallet.getAccount(),
138
- client: options.client,
139
- });
140
- }
131
+ const activeWallet = await (async () => {
132
+ if (options?.accountAbstraction && !hasSmartAccount(wallet)) {
133
+ return await handleSmartWalletConnection(
134
+ account,
135
+ options.client,
136
+ options.accountAbstraction,
137
+ );
138
+ } else {
139
+ return wallet;
140
+ }
141
+ })();
141
142
 
142
143
  // add personal wallet to connected wallets list
143
- addConnectedWallet(personalWallet);
144
+ addConnectedWallet(wallet);
144
145
 
145
- if (personalWallet.id !== "smart") {
146
- await storage.setItem(LAST_ACTIVE_EOA_ID, personalWallet.id);
146
+ if (wallet.id !== "smart") {
147
+ await storage.setItem(LAST_ACTIVE_EOA_ID, wallet.id);
147
148
  }
148
149
 
150
+ handleSetActiveWallet(activeWallet);
151
+
152
+ wallet.subscribe("accountChanged", async () => {
153
+ // We reimplement connect here to prevent memory leaks
154
+ const newWallet = await handleConnection(wallet, options);
155
+ options?.onConnect?.(newWallet);
156
+ });
157
+
149
158
  return activeWallet;
150
159
  };
151
160
 
161
+ const handleSmartWalletConnection = async (
162
+ signer: Account,
163
+ client: ThirdwebClient,
164
+ options: SmartWalletOptions,
165
+ ) => {
166
+ const wallet = smartWallet(options);
167
+
168
+ await wallet.connect({
169
+ personalAccount: signer,
170
+ client: client,
171
+ chain: options.chain,
172
+ });
173
+
174
+ return wallet;
175
+ };
176
+
152
177
  const connect = async (wallet: Wallet, options?: ConnectManagerOptions) => {
153
- // connectedWallet can be either wallet or smartWallet based on
178
+ // connectedWallet can be either wallet or smartWallet
154
179
  const connectedWallet = await handleConnection(wallet, options);
155
180
  options?.onConnect?.(connectedWallet);
156
- handleSetActiveWallet(connectedWallet);
157
- wallet.subscribe("accountChanged", async () => {
158
- const newConnectedWallet = await handleConnection(wallet, options);
159
- options?.onConnect?.(newConnectedWallet);
160
- handleSetActiveWallet(newConnectedWallet);
161
- });
162
181
  return connectedWallet;
163
182
  };
164
183
 
@@ -83,6 +83,13 @@ Code: ${code}`,
83
83
  paymasterAndData: res.result,
84
84
  };
85
85
  }
86
+ // check for policy errors
87
+ if (res.result.policyId && res.result.reason) {
88
+ console.warn(
89
+ `Paymaster policy rejected this transaction with reason: ${res.result.reason} (policyId: ${res.result.policyId})`,
90
+ );
91
+ }
92
+
86
93
  return {
87
94
  paymasterAndData: res.result.paymasterAndData,
88
95
  verificationGasLimit: res.result.verificationGasLimit