thirdweb 5.34.0-nightly-f6ff5a78fc2d65f0f250b154f1405210ca57ce0a-20240708000345 → 5.34.0-nightly-325416ea19905901f30e795cbf93cb8a085be02f-20240708203622

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 (142) hide show
  1. package/dist/cjs/auth/core/verify-jwt.js +1 -1
  2. package/dist/cjs/auth/core/verify-jwt.js.map +1 -1
  3. package/dist/cjs/auth/verify-hash.js +98 -0
  4. package/dist/cjs/auth/verify-hash.js.map +1 -0
  5. package/dist/cjs/auth/verify-signature.js +5 -60
  6. package/dist/cjs/auth/verify-signature.js.map +1 -1
  7. package/dist/cjs/auth/verify-typed-data.js +80 -0
  8. package/dist/cjs/auth/verify-typed-data.js.map +1 -0
  9. package/dist/cjs/chains/chain-definitions/blast.js +17 -0
  10. package/dist/cjs/chains/chain-definitions/blast.js.map +1 -0
  11. package/dist/cjs/exports/chains.js +3 -1
  12. package/dist/cjs/exports/chains.js.map +1 -1
  13. package/dist/cjs/exports/extensions/erc20.js +3 -1
  14. package/dist/cjs/exports/extensions/erc20.js.map +1 -1
  15. package/dist/cjs/exports/utils.js +10 -1
  16. package/dist/cjs/exports/utils.js.map +1 -1
  17. package/dist/cjs/extensions/erc1271/checkContractWalletSignature.js +1 -0
  18. package/dist/cjs/extensions/erc1271/checkContractWalletSignature.js.map +1 -1
  19. package/dist/cjs/extensions/erc1271/checkContractWalletSignedTypedData.js +1 -0
  20. package/dist/cjs/extensions/erc1271/checkContractWalletSignedTypedData.js.map +1 -1
  21. package/dist/cjs/extensions/erc20/write/transferBatch.js +61 -0
  22. package/dist/cjs/extensions/erc20/write/transferBatch.js.map +1 -0
  23. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.js +21 -12
  24. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.js.map +1 -1
  25. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/PayWIthCreditCard.js +1 -1
  26. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/fiat/FiatSteps.js +4 -4
  27. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/fiat/FiatTxDetailsTable.js +1 -1
  28. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/main/useBuyTxStates.js +1 -3
  29. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/main/useBuyTxStates.js.map +1 -1
  30. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/main/useUISelectionStates.js +1 -4
  31. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/main/useUISelectionStates.js.map +1 -1
  32. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/swap/ConfirmationScreen.js +1 -1
  33. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/swap/Fees.js +4 -4
  34. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/swap/PayWithCrypto.js +1 -1
  35. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/tx-history/BuyTxHistoryButton.js +1 -1
  36. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/tx-history/TokenInfoRow.js +1 -1
  37. package/dist/cjs/utils/hashing/hashTypedData.js +122 -0
  38. package/dist/cjs/utils/hashing/hashTypedData.js.map +1 -0
  39. package/dist/cjs/utils/jwt/decode-jwt.js +2 -2
  40. package/dist/cjs/utils/jwt/decode-jwt.js.map +1 -1
  41. package/dist/cjs/utils/jwt/refresh-jwt.js +1 -1
  42. package/dist/cjs/utils/jwt/refresh-jwt.js.map +1 -1
  43. package/dist/cjs/version.js +1 -1
  44. package/dist/esm/auth/core/verify-jwt.js +2 -2
  45. package/dist/esm/auth/core/verify-jwt.js.map +1 -1
  46. package/dist/esm/auth/verify-hash.js +95 -0
  47. package/dist/esm/auth/verify-hash.js.map +1 -0
  48. package/dist/esm/auth/verify-signature.js +6 -61
  49. package/dist/esm/auth/verify-signature.js.map +1 -1
  50. package/dist/esm/auth/verify-typed-data.js +77 -0
  51. package/dist/esm/auth/verify-typed-data.js.map +1 -0
  52. package/dist/esm/chains/chain-definitions/blast.js +14 -0
  53. package/dist/esm/chains/chain-definitions/blast.js.map +1 -0
  54. package/dist/esm/exports/chains.js +1 -0
  55. package/dist/esm/exports/chains.js.map +1 -1
  56. package/dist/esm/exports/extensions/erc20.js +1 -0
  57. package/dist/esm/exports/extensions/erc20.js.map +1 -1
  58. package/dist/esm/exports/utils.js +6 -0
  59. package/dist/esm/exports/utils.js.map +1 -1
  60. package/dist/esm/extensions/erc1271/checkContractWalletSignature.js +1 -0
  61. package/dist/esm/extensions/erc1271/checkContractWalletSignature.js.map +1 -1
  62. package/dist/esm/extensions/erc1271/checkContractWalletSignedTypedData.js +1 -0
  63. package/dist/esm/extensions/erc1271/checkContractWalletSignedTypedData.js.map +1 -1
  64. package/dist/esm/extensions/erc20/write/transferBatch.js +58 -0
  65. package/dist/esm/extensions/erc20/write/transferBatch.js.map +1 -0
  66. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.js +21 -12
  67. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.js.map +1 -1
  68. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/PayWIthCreditCard.js +1 -1
  69. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/fiat/FiatSteps.js +4 -4
  70. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/fiat/FiatTxDetailsTable.js +1 -1
  71. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/main/useBuyTxStates.js +1 -3
  72. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/main/useBuyTxStates.js.map +1 -1
  73. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/main/useUISelectionStates.js +1 -4
  74. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/main/useUISelectionStates.js.map +1 -1
  75. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/swap/ConfirmationScreen.js +1 -1
  76. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/swap/Fees.js +4 -4
  77. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/swap/PayWithCrypto.js +1 -1
  78. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/tx-history/BuyTxHistoryButton.js +1 -1
  79. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/tx-history/TokenInfoRow.js +1 -1
  80. package/dist/esm/utils/hashing/hashTypedData.js +118 -0
  81. package/dist/esm/utils/hashing/hashTypedData.js.map +1 -0
  82. package/dist/esm/utils/jwt/decode-jwt.js +1 -1
  83. package/dist/esm/utils/jwt/decode-jwt.js.map +1 -1
  84. package/dist/esm/utils/jwt/refresh-jwt.js +2 -2
  85. package/dist/esm/utils/jwt/refresh-jwt.js.map +1 -1
  86. package/dist/esm/version.js +1 -1
  87. package/dist/types/auth/verify-hash.d.ts +45 -0
  88. package/dist/types/auth/verify-hash.d.ts.map +1 -0
  89. package/dist/types/auth/verify-signature.d.ts.map +1 -1
  90. package/dist/types/auth/verify-typed-data.d.ts +75 -0
  91. package/dist/types/auth/verify-typed-data.d.ts.map +1 -0
  92. package/dist/types/chains/chain-definitions/blast.d.ts +27 -0
  93. package/dist/types/chains/chain-definitions/blast.d.ts.map +1 -0
  94. package/dist/types/exports/chains.d.ts +1 -0
  95. package/dist/types/exports/chains.d.ts.map +1 -1
  96. package/dist/types/exports/extensions/erc20.d.ts +1 -0
  97. package/dist/types/exports/extensions/erc20.d.ts.map +1 -1
  98. package/dist/types/exports/utils.d.ts +4 -0
  99. package/dist/types/exports/utils.d.ts.map +1 -1
  100. package/dist/types/extensions/erc1271/checkContractWalletSignature.d.ts +1 -0
  101. package/dist/types/extensions/erc1271/checkContractWalletSignature.d.ts.map +1 -1
  102. package/dist/types/extensions/erc1271/checkContractWalletSignedTypedData.d.ts +1 -0
  103. package/dist/types/extensions/erc1271/checkContractWalletSignedTypedData.d.ts.map +1 -1
  104. package/dist/types/extensions/erc20/write/transferBatch.d.ts +42 -0
  105. package/dist/types/extensions/erc20/write/transferBatch.d.ts.map +1 -0
  106. package/dist/types/react/web/ui/ConnectWallet/screens/Buy/main/useBuyTxStates.d.ts.map +1 -1
  107. package/dist/types/react/web/ui/ConnectWallet/screens/Buy/main/useUISelectionStates.d.ts.map +1 -1
  108. package/dist/types/utils/hashing/hashTypedData.d.ts +17 -0
  109. package/dist/types/utils/hashing/hashTypedData.d.ts.map +1 -0
  110. package/dist/types/utils/jwt/decode-jwt.d.ts +1 -1
  111. package/dist/types/utils/jwt/decode-jwt.d.ts.map +1 -1
  112. package/dist/types/version.d.ts +1 -1
  113. package/package.json +1 -1
  114. package/src/auth/core/verify-jwt.ts +2 -2
  115. package/src/auth/verify-hash.test.ts +66 -0
  116. package/src/auth/verify-hash.ts +126 -0
  117. package/src/auth/verify-signature.ts +6 -77
  118. package/src/auth/verify-typed-data.test.ts +82 -0
  119. package/src/auth/verify-typed-data.ts +110 -0
  120. package/src/chains/chain-definitions/blast.ts +14 -0
  121. package/src/exports/chains.ts +1 -0
  122. package/src/exports/extensions/erc20.ts +4 -0
  123. package/src/exports/utils.ts +8 -0
  124. package/src/extensions/erc1271/checkContractWalletSignature.ts +1 -0
  125. package/src/extensions/erc1271/checkContractWalletSignedTypedData.ts +1 -0
  126. package/src/extensions/erc20/write/transferBatch.ts +78 -0
  127. package/src/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.tsx +42 -12
  128. package/src/react/web/ui/ConnectWallet/screens/Buy/PayWIthCreditCard.tsx +1 -1
  129. package/src/react/web/ui/ConnectWallet/screens/Buy/fiat/FiatSteps.tsx +5 -5
  130. package/src/react/web/ui/ConnectWallet/screens/Buy/fiat/FiatTxDetailsTable.tsx +1 -1
  131. package/src/react/web/ui/ConnectWallet/screens/Buy/main/useBuyTxStates.ts +1 -5
  132. package/src/react/web/ui/ConnectWallet/screens/Buy/main/useUISelectionStates.ts +1 -6
  133. package/src/react/web/ui/ConnectWallet/screens/Buy/swap/ConfirmationScreen.tsx +2 -2
  134. package/src/react/web/ui/ConnectWallet/screens/Buy/swap/Fees.tsx +4 -4
  135. package/src/react/web/ui/ConnectWallet/screens/Buy/swap/PayWithCrypto.tsx +1 -1
  136. package/src/react/web/ui/ConnectWallet/screens/Buy/tx-history/BuyTxHistoryButton.tsx +1 -1
  137. package/src/react/web/ui/ConnectWallet/screens/Buy/tx-history/TokenInfoRow.tsx +1 -1
  138. package/src/utils/hashing/hashTypedData.test.ts +207 -0
  139. package/src/utils/hashing/hashTypedData.ts +210 -0
  140. package/src/utils/jwt/decode-jwt.ts +1 -1
  141. package/src/utils/jwt/refresh-jwt.ts +2 -2
  142. package/src/version.ts +1 -1
@@ -1,27 +1,10 @@
1
- import { equalBytes } from "@noble/curves/abstract/utils";
2
- import {
3
- type Signature,
4
- encodeDeployData,
5
- recoverAddress,
6
- serializeSignature,
7
- } from "viem";
1
+ import { type Signature, recoverAddress } from "viem";
8
2
  import type { Chain } from "../chains/types.js";
9
3
  import type { ThirdwebClient } from "../client/client.js";
10
- import { getContract } from "../contract/contract.js";
11
- import { eth_call } from "../rpc/actions/eth_call.js";
12
- import { getRpcClient } from "../rpc/rpc.js";
13
- import { fromBytes } from "../utils/encoding/from-bytes.js";
14
4
  import { type Hex, isHex } from "../utils/encoding/hex.js";
15
- import { toBytes } from "../utils/encoding/to-bytes.js";
16
5
  import { hashMessage } from "../utils/hashing/hashMessage.js";
17
6
  import type { Prettify } from "../utils/type-utils.js";
18
- import { DEFAULT_ACCOUNT_FACTORY } from "../wallets/smart/lib/constants.js";
19
- import {
20
- universalSignatureValidatorAbi,
21
- universalSignatureValidatorByteCode,
22
- } from "./constants.js";
23
- import { isErc6492Signature } from "./is-erc6492-signature.js";
24
- import { serializeErc6492Signature } from "./serialize-erc6492-signature.js";
7
+ import { verifyHash } from "./verify-hash.js";
25
8
 
26
9
  export type VerifyEOASignatureParams = {
27
10
  message: string;
@@ -113,69 +96,15 @@ export async function verifyContractWalletSignature({
113
96
  client,
114
97
  accountFactory,
115
98
  }: VerifyContractWalletSignatureParams) {
116
- console.log("verifyContractWalletSignature");
117
99
  const messageHash = hashMessage(message);
118
-
119
- const signatureHex = (() => {
120
- if (isHex(signature)) return signature;
121
- if (typeof signature === "object" && "r" in signature && "s" in signature)
122
- return serializeSignature(signature);
123
- if (signature instanceof Uint8Array) return fromBytes(signature, "hex");
124
- // We should never hit this but TS doesn't know that
125
- throw new Error(
126
- `Invalid signature type for signature ${signature}: ${typeof signature}`,
127
- );
128
- })();
129
-
130
- const accountContract = getContract({
100
+ return verifyHash({
101
+ hash: messageHash,
102
+ signature,
131
103
  address,
132
- chain,
133
104
  client,
134
- });
135
-
136
- const wrappedSignature = await (async () => {
137
- // If this sigature was already wrapped for ERC-6492, carry on
138
- if (isErc6492Signature(signatureHex)) return signatureHex;
139
-
140
- // If the contract is already deployed, return the original signature
141
- const { isContractDeployed } = await import(
142
- "../utils/bytecode/is-contract-deployed.js"
143
- );
144
- const isDeployed = await isContractDeployed(accountContract);
145
- if (!isDeployed) return signatureHex;
146
-
147
- // Otherwise, serialize the signature for ERC-6492 validation
148
- return serializeErc6492Signature({
149
- address: accountFactory?.address ?? DEFAULT_ACCOUNT_FACTORY,
150
- data: accountFactory?.verificationCalldata ?? "0x",
151
- signature: signatureHex,
152
- });
153
- })();
154
-
155
- const verificationData = encodeDeployData({
156
- abi: universalSignatureValidatorAbi,
157
- args: [address, messageHash, wrappedSignature],
158
- bytecode: universalSignatureValidatorByteCode,
159
- });
160
-
161
- const rpcRequest = getRpcClient({
162
105
  chain,
163
- client,
106
+ accountFactory,
164
107
  });
165
-
166
- try {
167
- const result = await eth_call(rpcRequest, {
168
- data: verificationData,
169
- });
170
-
171
- const hexResult = isHex(result) ? toBytes(result) : result;
172
- return equalBytes(hexResult, toBytes("0x1"));
173
- } catch (error) {
174
- console.log("error", error);
175
- // TODO: Improve overall RPC error handling so we can tell if this was an actual verification failure or some other error
176
- // Verification failed somehow
177
- return false;
178
- }
179
108
  }
180
109
 
181
110
  export type VerifySignatureParams = Prettify<
@@ -0,0 +1,82 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import { TEST_CLIENT } from "../../test/src/test-clients.js";
3
+ import { ANVIL_PKEY_A, TEST_ACCOUNT_A } from "../../test/src/test-wallets.js";
4
+ import { typedData } from "../../test/src/typed-data.js";
5
+ import { mainnet } from "../chains/chain-definitions/ethereum.js";
6
+ import { signTypedData } from "../utils/signatures/sign-typed-data.js";
7
+ import { verifyTypedData } from "./verify-typed-data.js";
8
+
9
+ describe("verifyTypedData", async () => {
10
+ test("valid EOA signature", async () => {
11
+ const signature = signTypedData({
12
+ ...typedData.basic,
13
+ primaryType: "Mail",
14
+ privateKey: ANVIL_PKEY_A,
15
+ });
16
+
17
+ expect(
18
+ await verifyTypedData({
19
+ ...typedData.basic,
20
+ primaryType: "Mail",
21
+ address: TEST_ACCOUNT_A.address,
22
+ signature,
23
+ chain: mainnet,
24
+ client: TEST_CLIENT,
25
+ }),
26
+ ).toBe(true);
27
+ });
28
+
29
+ test("invalid EOA signature", async () => {
30
+ expect(
31
+ await verifyTypedData({
32
+ ...typedData.basic,
33
+ primaryType: "Mail",
34
+ address: TEST_ACCOUNT_A.address,
35
+ signature: "0xdead",
36
+ chain: mainnet,
37
+ client: TEST_CLIENT,
38
+ }),
39
+ ).toBe(false);
40
+ });
41
+
42
+ test("valid smart account signature", async () => {
43
+ expect(
44
+ await verifyTypedData({
45
+ ...typedData.basic,
46
+ primaryType: "Mail",
47
+ address: "0x3FCf42e10CC70Fe75A62EB3aDD6D305Aa840d145",
48
+ signature:
49
+ "0x79d756d805073dc97b7bc885b0d56ddf319a2599530fe1e178c2a7de5be88980068d24f20a79b318ea0a84d33ae06f93db77e4235e5d9eeb8b1d7a63922ada3e1c",
50
+ chain: mainnet,
51
+ client: TEST_CLIENT,
52
+ }),
53
+ ).toBe(true);
54
+ });
55
+
56
+ test("invalid smart account signature", async () => {
57
+ expect(
58
+ await verifyTypedData({
59
+ ...typedData.basic,
60
+ primaryType: "Mail",
61
+ address: "0x3FCf42e10CC70Fe75A62EB3aDD6D305Aa840d145",
62
+ signature: "0xdead",
63
+ chain: mainnet,
64
+ client: TEST_CLIENT,
65
+ }),
66
+ ).toBe(false);
67
+ });
68
+
69
+ test("non-existent account", async () => {
70
+ expect(
71
+ await verifyTypedData({
72
+ ...typedData.basic,
73
+ primaryType: "Mail",
74
+ address: "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef",
75
+ signature:
76
+ "0x79d756d805073dc97b7bc885b0d56ddf319a2599530fe1e178c2a7de5be88980068d24f20a79b318ea0a84d33ae06f93db77e4235e5d9eeb8b1d7a63922ada3e1c",
77
+ chain: mainnet,
78
+ client: TEST_CLIENT,
79
+ }),
80
+ ).toBe(false);
81
+ });
82
+ });
@@ -0,0 +1,110 @@
1
+ import type { Signature, TypedData, TypedDataDefinition } from "viem";
2
+ import { hashTypedData } from "viem";
3
+ import type { Chain } from "../chains/types.js";
4
+ import type { ThirdwebClient } from "../client/client.js";
5
+ import type { Hex } from "../utils/encoding/hex.js";
6
+ import type { HashTypedDataParams } from "../utils/hashing/hashTypedData.js";
7
+ import { type VerifyHashParams, verifyHash } from "./verify-hash.js";
8
+
9
+ export type VerifyTypedDataParams<
10
+ typedData extends TypedData | Record<string, unknown> = TypedData,
11
+ primaryType extends keyof typedData | "EIP712Domain" = keyof typedData,
12
+ > = Omit<VerifyHashParams, "hash"> &
13
+ TypedDataDefinition<typedData, primaryType> & {
14
+ address: string;
15
+ signature: string | Uint8Array | Signature;
16
+ client: ThirdwebClient;
17
+ chain: Chain;
18
+ accountFactory?: {
19
+ address: string;
20
+ verificationCalldata: Hex;
21
+ };
22
+ };
23
+
24
+ /**
25
+ * @description Verify am [EIP-712](https://eips.ethereum.org/EIPS/eip-712) typed data signature. This function is interoperable with all wallet types (smart accounts or EOAs).
26
+ *
27
+ * @param {string} options.address The address that signed the typed data
28
+ * @param {string | Uint8Array | Signature} options.signature The signature that was signed
29
+ * @param {ThirdwebClient} options.client The Thirdweb client
30
+ * @param {Chain} options.chain The chain that the address is on. For an EOA, this can be any chain.
31
+ * @param {string} [options.accountFactory.address] The address of the account factory that created the account if using a smart account with a custom account factory
32
+ * @param {Hex} [options.accountFactory.verificationCalldata] The calldata that was used to create the account if using a smart account with a custom account factory
33
+ * @param {typeof VerifyTypedDataParams.message} options.message The EIP-712 message that was signed.
34
+ * @param {typeof VerifyTypedDataParams.domain} options.domain The EIP-712 domain that was signed.
35
+ * @param {typeof VerifyTypedDataParams.primaryType} options.primaryType The EIP-712 primary type that was signed.
36
+ * @param {typeof VerifyTypedDataParams.types} options.types The EIP-712 types that were signed.
37
+ *
38
+ * @returns {Promise<boolean>} A promise that resolves to `true` if the signature is valid, or `false` otherwise.
39
+ *
40
+ * @example
41
+ * ```ts
42
+ * import { verifyTypedData } from "thirdweb/utils";
43
+ * const isValid = await verifyTypedData({
44
+ * address: "0x...",
45
+ * signature: "0x...",
46
+ * client,
47
+ * chain,
48
+ * domain: {
49
+ name: "Ether Mail",
50
+ version: "1",
51
+ chainId: 1,
52
+ verifyingContract: "0x0000000000000000000000000000000000000000",
53
+ },
54
+ * primaryType: "Mail",
55
+ * types: {
56
+ Person: [
57
+ { name: "name", type: "string" },
58
+ { name: "wallet", type: "address" },
59
+ ],
60
+ Mail: [
61
+ { name: "from", type: "Person" },
62
+ { name: "to", type: "Person" },
63
+ { name: "contents", type: "string" },
64
+ ],
65
+ },
66
+ message: {
67
+ from: {
68
+ name: "Cow",
69
+ wallet: "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826",
70
+ },
71
+ to: {
72
+ name: "Bob",
73
+ wallet: "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB",
74
+ },
75
+ contents: "Hello, Bob!",
76
+ },
77
+ * });
78
+ * ```
79
+ *
80
+ * @auth
81
+ */
82
+ export async function verifyTypedData<
83
+ typedData extends TypedData | Record<string, unknown>,
84
+ primaryType extends keyof typedData | "EIP712Domain",
85
+ >({
86
+ address,
87
+ signature,
88
+ client,
89
+ chain,
90
+ accountFactory,
91
+ message,
92
+ domain,
93
+ primaryType,
94
+ types,
95
+ }: VerifyTypedDataParams<typedData, primaryType>): Promise<boolean> {
96
+ const messageHash = hashTypedData({
97
+ message,
98
+ domain,
99
+ primaryType,
100
+ types,
101
+ } as HashTypedDataParams);
102
+ return verifyHash({
103
+ hash: messageHash,
104
+ signature,
105
+ address,
106
+ chain,
107
+ client,
108
+ accountFactory,
109
+ });
110
+ }
@@ -0,0 +1,14 @@
1
+ import { defineChain } from "../utils.js";
2
+
3
+ export const blast = /* @__PURE__ */ defineChain({
4
+ id: 81457,
5
+ name: "Blast",
6
+ nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
7
+ blockExplorers: [
8
+ {
9
+ name: "Blastscan",
10
+ url: "https://blastscan.io",
11
+ apiUrl: "https://api.blastscan.io/api",
12
+ },
13
+ ],
14
+ });
@@ -21,6 +21,7 @@ export { baseSepolia } from "../chains/chain-definitions/base-sepolia.js";
21
21
  export { base } from "../chains/chain-definitions/base.js";
22
22
  // mainnet = alias for ethereum
23
23
  export { ethereum, mainnet } from "../chains/chain-definitions/ethereum.js";
24
+ export { blast } from "../chains/chain-definitions/blast.js";
24
25
  export { optimismSepolia } from "../chains/chain-definitions/optimism-sepolia.js";
25
26
  export { optimism } from "../chains/chain-definitions/optimism.js";
26
27
  export { lineaSepolia } from "../chains/chain-definitions/linea-sepolia.js";
@@ -33,6 +33,10 @@ export {
33
33
  transferFrom,
34
34
  type TransferFromParams,
35
35
  } from "../../extensions/erc20/write/transferFrom.js";
36
+ export {
37
+ transferBatch,
38
+ type TransferBatchParams,
39
+ } from "../../extensions/erc20/write/transferBatch.js";
36
40
  export {
37
41
  approve,
38
42
  type ApproveParams,
@@ -149,3 +149,11 @@ export { stringify } from "../utils/json.js";
149
149
  // values
150
150
  // ------------------------------------------------
151
151
  export { maxUint256 } from "viem";
152
+
153
+ // ------------------------------------------------
154
+ // jwt
155
+ // ------------------------------------------------
156
+ export { decodeJWT } from "../utils/jwt/decode-jwt.js";
157
+ export { encodeJWT, type JWTPayloadInput } from "../utils/jwt/encode-jwt.js";
158
+ export { refreshJWT, type RefreshJWTParams } from "../utils/jwt/refresh-jwt.js";
159
+ export type { JWTPayload } from "../utils/jwt/types.js";
@@ -12,6 +12,7 @@ const MAGIC_VALUE = "0x1626ba7e";
12
12
 
13
13
  /**
14
14
  * Checks if a contract wallet signature is valid.
15
+ * @deprecated Use `verifySignature` instead
15
16
  * @param options - The options for the checkContractWalletSignature function.
16
17
  * @param options.contract - The contract to check the signature against.
17
18
  * @param options.message - The message to check the signature against.
@@ -15,6 +15,7 @@ const MAGIC_VALUE = "0x1626ba7e";
15
15
 
16
16
  /**
17
17
  * Checks if a contract wallet signature is valid.
18
+ * @deprecated Use `verifyTypedData` instead
18
19
  * @param options - The options for the checkContractWalletSignature function.
19
20
  * @param options.contract - The contract to check the signature against.
20
21
  * @param options.message - The message to check the signature against.
@@ -0,0 +1,78 @@
1
+ import type { BaseTransactionOptions } from "../../../transaction/types.js";
2
+ import type { Prettify } from "../../../utils/type-utils.js";
3
+ import { toUnits } from "../../../utils/units.js";
4
+ import { multicall } from "../../common/__generated__/IMulticall/write/multicall.js";
5
+ import { encodeTransfer } from "../__generated__/IERC20/write/transfer.js";
6
+ /**
7
+ * Represents the parameters for a batch transfer operation.
8
+ */
9
+ export type TransferBatchParams = Prettify<{
10
+ batch: Array<
11
+ { to: string } & (
12
+ | {
13
+ amount: number | string;
14
+ }
15
+ | {
16
+ amountWei: bigint;
17
+ }
18
+ )
19
+ >;
20
+ }>;
21
+
22
+ /**
23
+ * Transfers a batch of ERC20 tokens from the sender's address to the specified recipient address.
24
+ * @param options - The options for the batch transfer transaction.
25
+ * @returns A promise that resolves to the prepared transaction.
26
+ * @extension ERC20
27
+ * @example
28
+ * ```ts
29
+ * import { transferBatch } from "thirdweb/extensions/erc20";
30
+ * import { sendTransaction } from "thirdweb";
31
+ *
32
+ * const transaction = transferBatch({
33
+ * contract,
34
+ * batch: [
35
+ * {
36
+ * to: "0x...",
37
+ * amount: 100,
38
+ * },
39
+ * {
40
+ * to: "0x...",
41
+ * amount: "0.1",
42
+ * },
43
+ * ]);
44
+ *
45
+ * await sendTransaction({ transaction, account });
46
+ * ```
47
+ */
48
+ export function transferBatch(
49
+ options: BaseTransactionOptions<TransferBatchParams>,
50
+ ) {
51
+ return multicall({
52
+ contract: options.contract,
53
+ asyncParams: async () => {
54
+ return {
55
+ data: await Promise.all(
56
+ options.batch.map(async (transfer) => {
57
+ let amount: bigint;
58
+ if ("amount" in transfer) {
59
+ // if we need to parse the amount from ether to gwei then we pull in the decimals extension
60
+ const { decimals } = await import("../read/decimals.js");
61
+ // it's OK to call this multiple times because the call is cached
62
+ // if this fails we fall back to `18` decimals
63
+ const d = await decimals(options).catch(() => 18);
64
+ // turn ether into gwei
65
+ amount = toUnits(transfer.amount.toString(), d);
66
+ } else {
67
+ amount = transfer.amountWei;
68
+ }
69
+ return encodeTransfer({
70
+ to: transfer.to,
71
+ value: amount,
72
+ });
73
+ }),
74
+ ),
75
+ };
76
+ },
77
+ });
78
+ }
@@ -141,6 +141,8 @@ function BuyScreenContent(props: BuyScreenContentProps) {
141
141
  id: "main",
142
142
  });
143
143
 
144
+ const [hasEditedAmount, setHasEditedAmount] = useState(false);
145
+
144
146
  // UI selection
145
147
  const {
146
148
  tokenAmount,
@@ -408,6 +410,8 @@ function BuyScreenContent(props: BuyScreenContentProps) {
408
410
  supportedDestinations={supportedDestinations}
409
411
  onBack={props.onBack}
410
412
  theme={props.theme}
413
+ hasEditedAmount={hasEditedAmount}
414
+ setHasEditedAmount={setHasEditedAmount}
411
415
  />
412
416
  )}
413
417
 
@@ -486,6 +490,7 @@ function BuyScreenContent(props: BuyScreenContentProps) {
486
490
  }}
487
491
  payer={payer}
488
492
  setTokenAmount={setTokenAmount}
493
+ setHasEditedAmount={setHasEditedAmount}
489
494
  />
490
495
  )}
491
496
  </TokenSelectedLayout>
@@ -512,8 +517,8 @@ function SelectedTokenInfo(props: {
512
517
  }}
513
518
  >
514
519
  <Container flex="row" gap="xs" center="y">
515
- <Text color="primaryText" data-testid="tokenAmount" size="xxl">
516
- {formatNumber(Number(props.tokenAmount), 5)}
520
+ <Text color="primaryText" data-testid="tokenAmount" size="xl">
521
+ {formatNumber(Number(props.tokenAmount), 6)}
517
522
  </Text>
518
523
 
519
524
  <Container flex="row" gap="xxs" center="y">
@@ -559,6 +564,8 @@ function MainScreen(props: {
559
564
  supportedDestinations: SupportedChainAndTokens;
560
565
  onBack: (() => void) | undefined;
561
566
  theme: "light" | "dark" | Theme;
567
+ hasEditedAmount: boolean;
568
+ setHasEditedAmount: (hasEdited: boolean) => void;
562
569
  }) {
563
570
  const { showPaymentSelection, buyWithCryptoEnabled, buyWithFiatEnabled } =
564
571
  useEnabledPaymentMethods({
@@ -568,7 +575,6 @@ function MainScreen(props: {
568
575
  toToken: props.toToken,
569
576
  });
570
577
 
571
- const [hasEditedAmount, setHasEditedAmount] = useState(false);
572
578
  const {
573
579
  buyForTx,
574
580
  setTokenAmount,
@@ -584,7 +590,7 @@ function MainScreen(props: {
584
590
  const { amountNeeded } = useBuyTxStates({
585
591
  setTokenAmount,
586
592
  buyForTx: buyForTx || null,
587
- hasEditedAmount,
593
+ hasEditedAmount: props.hasEditedAmount,
588
594
  account: payerAccount || null,
589
595
  });
590
596
 
@@ -605,7 +611,7 @@ function MainScreen(props: {
605
611
  <Spacer y="lg" />
606
612
  <BuyForTxUI
607
613
  amountNeeded={String(
608
- formatNumber(Number(toEther(amountNeeded)), 4),
614
+ formatNumber(Number(toEther(amountNeeded)), 6),
609
615
  )}
610
616
  buyForTx={props.buyForTx}
611
617
  client={client}
@@ -619,7 +625,7 @@ function MainScreen(props: {
619
625
  <BuyTokenInput
620
626
  value={tokenAmount}
621
627
  onChange={async (value) => {
622
- setHasEditedAmount(true);
628
+ props.setHasEditedAmount(true);
623
629
  setTokenAmount(value);
624
630
  }}
625
631
  freezeAmount={payOptions.prefillBuy?.allowEdits?.amount === false}
@@ -1094,6 +1100,7 @@ function FiatScreenContent(props: {
1094
1100
  isEmbed: boolean;
1095
1101
  payer: PayerInfo;
1096
1102
  setTokenAmount: (amount: string) => void;
1103
+ setHasEditedAmount: (hasEdited: boolean) => void;
1097
1104
  }) {
1098
1105
  const [receiverAddress, setReceiverAddress] = useState(
1099
1106
  props.payer.account.address,
@@ -1182,18 +1189,27 @@ function FiatScreenContent(props: {
1182
1189
  try {
1183
1190
  if (err.error.code === "MINIMUM_PURCHASE_AMOUNT") {
1184
1191
  const obj = err.error as AmountTooLowError;
1185
-
1186
1192
  const minAmountUSD = obj.data.minimumAmountUSDCents;
1187
1193
  const currentAmountUSD = obj.data.requestedAmountUSDCents;
1194
+
1195
+ // avoid divide by zero
1196
+ // if we can't calculate the minimum amount in # of tokens, don't show the button to set the minimum amount
1197
+ if (obj.data.requestedAmountUSDCents === 0) {
1198
+ return {
1199
+ msg: [
1200
+ "Purchase amount is too low",
1201
+ "Increase the amount and try again",
1202
+ ],
1203
+ };
1204
+ }
1205
+
1188
1206
  const currentAmountToken = Number(props.tokenAmount);
1189
1207
  const minAmountToken =
1190
1208
  (minAmountUSD * currentAmountToken) / currentAmountUSD;
1191
1209
  const minAmountTokenWithBuffer = minAmountToken * 1.2; // 20% buffer
1192
- const formattedNum = formatNumber(minAmountTokenWithBuffer, 3);
1193
1210
 
1194
1211
  return {
1195
- msg: [`Minimum purchase amount is ${formattedNum}`],
1196
- minAmount: formattedNum,
1212
+ minAmount: minAmountTokenWithBuffer,
1197
1213
  };
1198
1214
  }
1199
1215
  } catch {}
@@ -1279,7 +1295,20 @@ function FiatScreenContent(props: {
1279
1295
  {/* Error message */}
1280
1296
  {errorMsg && (
1281
1297
  <div>
1282
- {errorMsg.msg.map((msg) => (
1298
+ {errorMsg.minAmount && (
1299
+ <Text color="danger" size="sm" center multiline>
1300
+ Minimum amount is {formatNumber(errorMsg.minAmount, 6)}{" "}
1301
+ <TokenSymbol
1302
+ token={toToken}
1303
+ chain={toChain}
1304
+ size="sm"
1305
+ inline
1306
+ color="danger"
1307
+ />
1308
+ </Text>
1309
+ )}
1310
+
1311
+ {errorMsg.msg?.map((msg) => (
1283
1312
  <Text color="danger" size="sm" center multiline key={msg}>
1284
1313
  {msg}
1285
1314
  </Text>
@@ -1293,6 +1322,7 @@ function FiatScreenContent(props: {
1293
1322
  fullWidth
1294
1323
  onClick={() => {
1295
1324
  props.setTokenAmount(String(errorMsg.minAmount));
1325
+ props.setHasEditedAmount(true);
1296
1326
  }}
1297
1327
  >
1298
1328
  Set Minimum
@@ -1376,7 +1406,7 @@ function BuyForTxUI(props: {
1376
1406
  <Text size="sm">Your Balance</Text>
1377
1407
  <Container flex="row" gap="xs">
1378
1408
  <Text color="primaryText" size="sm">
1379
- {formatNumber(Number(toEther(props.buyForTx.balance)), 4)}{" "}
1409
+ {formatNumber(Number(toEther(props.buyForTx.balance)), 6)}{" "}
1380
1410
  {props.buyForTx.tokenSymbol}
1381
1411
  </Text>
1382
1412
  <TokenIcon
@@ -82,7 +82,7 @@ export function PayWithCreditCard(props: {
82
82
  <Skeleton width="100px" height={fontSize.lg} />
83
83
  ) : (
84
84
  <Text size="lg" color={props.value ? "primaryText" : "secondaryText"}>
85
- {props.value ? `${formatNumber(Number(props.value), 4)}` : "--"}
85
+ {props.value ? `${formatNumber(Number(props.value), 6)}` : "--"}
86
86
  </Text>
87
87
  )}
88
88
  </div>
@@ -163,7 +163,7 @@ export function FiatSteps(props: {
163
163
  const onRampTokenInfo = (
164
164
  <div>
165
165
  <Text color="primaryText" size="sm">
166
- {formatNumber(Number(onRampTokenAmount), 4)}{" "}
166
+ {formatNumber(Number(onRampTokenAmount), 6)}{" "}
167
167
  <TokenSymbol token={onRampToken} chain={onRampChain} size="sm" inline />
168
168
  </Text>
169
169
  </div>
@@ -203,7 +203,7 @@ export function FiatSteps(props: {
203
203
  textDecoration: "line-through",
204
204
  }}
205
205
  >
206
- {formatNumber(Number(toTokenAmount), 4)}{" "}
206
+ {formatNumber(Number(toTokenAmount), 6)}{" "}
207
207
  <TokenSymbol
208
208
  token={toToken}
209
209
  chain={toChain}
@@ -213,7 +213,7 @@ export function FiatSteps(props: {
213
213
  />
214
214
  </Text>{" "}
215
215
  <Text color="danger" size="sm" inline>
216
- {formatNumber(Number(props.status.destination.amount), 4)}{" "}
216
+ {formatNumber(Number(props.status.destination.amount), 6)}{" "}
217
217
  <TokenSymbol
218
218
  token={{
219
219
  address: props.status.destination.token.tokenAddress,
@@ -231,7 +231,7 @@ export function FiatSteps(props: {
231
231
 
232
232
  const toTokenInfo = partialSuccessToTokenInfo || (
233
233
  <Text color="primaryText" size="sm">
234
- {formatNumber(Number(toTokenAmount), 4)}{" "}
234
+ {formatNumber(Number(toTokenAmount), 6)}{" "}
235
235
  <TokenSymbol token={toToken} chain={toChain} size="sm" inline />
236
236
  </Text>
237
237
  );
@@ -330,7 +330,7 @@ export function FiatSteps(props: {
330
330
  icon: fiatIcon,
331
331
  primaryText: (
332
332
  <Text color="primaryText" size="sm">
333
- {formatNumber(Number(fromCurrencyAmount), 4)} {fromCurrencySymbol}
333
+ {formatNumber(Number(fromCurrencyAmount), 6)} {fromCurrencySymbol}
334
334
  </Text>
335
335
  ),
336
336
  }}
@@ -75,7 +75,7 @@ export function OnRampTxDetailsTable(props: {
75
75
  <Container flex="row" gap="xs" center="y">
76
76
  <currencyMeta.icon size={iconSize.sm} />
77
77
  <Text color="primaryText">
78
- {formatNumber(Number(props.fiat.amount), 4)}{" "}
78
+ {formatNumber(Number(props.fiat.amount), 2)}{" "}
79
79
  {props.fiat.currencySymbol}
80
80
  </Text>
81
81
  </Container>
@@ -1,5 +1,4 @@
1
1
  import { useEffect, useState } from "react";
2
- import { formatNumber } from "../../../../../../../utils/formatNumber.js";
3
2
  import { toEther } from "../../../../../../../utils/units.js";
4
3
  import type { Account } from "../../../../../../../wallets/interfaces/wallet.js";
5
4
  import { getTotalTxCostForBuy } from "../../../../../../core/hooks/transaction/useSendTransaction.js";
@@ -48,10 +47,7 @@ export function useBuyTxStates(options: {
48
47
 
49
48
  if (shouldRefreshTokenAmount) {
50
49
  if (totalCost > buyForTx.balance) {
51
- const _tokenAmount = String(
52
- formatNumber(Number(toEther(totalCost - buyForTx.balance)), 4),
53
- );
54
- setTokenAmount(_tokenAmount);
50
+ setTokenAmount(toEther(totalCost - buyForTx.balance));
55
51
  }
56
52
  }
57
53
  } catch {