thirdweb 5.88.2-nightly-0a292138ca21b2d7375db6ff9ef07dc91cffc7fc-20250211122816 → 5.88.2

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 (111) hide show
  1. package/dist/cjs/exports/extensions/erc1155.js +4 -2
  2. package/dist/cjs/exports/extensions/erc1155.js.map +1 -1
  3. package/dist/cjs/exports/extensions/erc20.js +3 -1
  4. package/dist/cjs/exports/extensions/erc20.js.map +1 -1
  5. package/dist/cjs/exports/extensions/erc721.js +4 -2
  6. package/dist/cjs/exports/extensions/erc721.js.map +1 -1
  7. package/dist/cjs/extensions/erc1155/__generated__/DropERC1155/read/verifyClaim.js +191 -0
  8. package/dist/cjs/extensions/erc1155/__generated__/DropERC1155/read/verifyClaim.js.map +1 -0
  9. package/dist/cjs/extensions/erc1155/drops/read/canClaim.js +45 -0
  10. package/dist/cjs/extensions/erc1155/drops/read/canClaim.js.map +1 -0
  11. package/dist/cjs/extensions/erc20/__generated__/DropERC20/read/verifyClaim.js +182 -0
  12. package/dist/cjs/extensions/erc20/__generated__/DropERC20/read/verifyClaim.js.map +1 -0
  13. package/dist/cjs/extensions/erc20/drops/read/canClaim.js +51 -0
  14. package/dist/cjs/extensions/erc20/drops/read/canClaim.js.map +1 -0
  15. package/dist/cjs/extensions/erc721/__generated__/DropERC721/read/verifyClaim.js +182 -0
  16. package/dist/cjs/extensions/erc721/__generated__/DropERC721/read/verifyClaim.js.map +1 -0
  17. package/dist/cjs/extensions/erc721/drops/read/canClaim.js +42 -0
  18. package/dist/cjs/extensions/erc721/drops/read/canClaim.js.map +1 -0
  19. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.js +12 -5
  20. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.js.map +1 -1
  21. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/WalletSelectorButton.js +6 -5
  22. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/WalletSelectorButton.js.map +1 -1
  23. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/fiat/FiatSteps.js +1 -1
  24. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/fiat/FiatSteps.js.map +1 -1
  25. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/swap/PaymentSelectionScreen.js +7 -3
  26. package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/swap/PaymentSelectionScreen.js.map +1 -1
  27. package/dist/cjs/react/web/utils/errors.js +6 -1
  28. package/dist/cjs/react/web/utils/errors.js.map +1 -1
  29. package/dist/cjs/transaction/extract-error.js +11 -4
  30. package/dist/cjs/transaction/extract-error.js.map +1 -1
  31. package/dist/cjs/version.js +1 -1
  32. package/dist/cjs/version.js.map +1 -1
  33. package/dist/esm/exports/extensions/erc1155.js +1 -0
  34. package/dist/esm/exports/extensions/erc1155.js.map +1 -1
  35. package/dist/esm/exports/extensions/erc20.js +1 -0
  36. package/dist/esm/exports/extensions/erc20.js.map +1 -1
  37. package/dist/esm/exports/extensions/erc721.js +1 -0
  38. package/dist/esm/exports/extensions/erc721.js.map +1 -1
  39. package/dist/esm/extensions/erc1155/__generated__/DropERC1155/read/verifyClaim.js +183 -0
  40. package/dist/esm/extensions/erc1155/__generated__/DropERC1155/read/verifyClaim.js.map +1 -0
  41. package/dist/esm/extensions/erc1155/drops/read/canClaim.js +42 -0
  42. package/dist/esm/extensions/erc1155/drops/read/canClaim.js.map +1 -0
  43. package/dist/esm/extensions/erc20/__generated__/DropERC20/read/verifyClaim.js +174 -0
  44. package/dist/esm/extensions/erc20/__generated__/DropERC20/read/verifyClaim.js.map +1 -0
  45. package/dist/esm/extensions/erc20/drops/read/canClaim.js +48 -0
  46. package/dist/esm/extensions/erc20/drops/read/canClaim.js.map +1 -0
  47. package/dist/esm/extensions/erc721/__generated__/DropERC721/read/verifyClaim.js +174 -0
  48. package/dist/esm/extensions/erc721/__generated__/DropERC721/read/verifyClaim.js.map +1 -0
  49. package/dist/esm/extensions/erc721/drops/read/canClaim.js +39 -0
  50. package/dist/esm/extensions/erc721/drops/read/canClaim.js.map +1 -0
  51. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.js +12 -5
  52. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.js.map +1 -1
  53. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/WalletSelectorButton.js +6 -5
  54. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/WalletSelectorButton.js.map +1 -1
  55. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/fiat/FiatSteps.js +1 -1
  56. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/fiat/FiatSteps.js.map +1 -1
  57. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/swap/PaymentSelectionScreen.js +7 -3
  58. package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/swap/PaymentSelectionScreen.js.map +1 -1
  59. package/dist/esm/react/web/utils/errors.js +6 -1
  60. package/dist/esm/react/web/utils/errors.js.map +1 -1
  61. package/dist/esm/transaction/extract-error.js +11 -5
  62. package/dist/esm/transaction/extract-error.js.map +1 -1
  63. package/dist/esm/version.js +1 -1
  64. package/dist/esm/version.js.map +1 -1
  65. package/dist/types/exports/extensions/erc1155.d.ts +1 -0
  66. package/dist/types/exports/extensions/erc1155.d.ts.map +1 -1
  67. package/dist/types/exports/extensions/erc20.d.ts +1 -0
  68. package/dist/types/exports/extensions/erc20.d.ts.map +1 -1
  69. package/dist/types/exports/extensions/erc721.d.ts +1 -0
  70. package/dist/types/exports/extensions/erc721.d.ts.map +1 -1
  71. package/dist/types/extensions/erc1155/__generated__/DropERC1155/read/verifyClaim.d.ts +143 -0
  72. package/dist/types/extensions/erc1155/__generated__/DropERC1155/read/verifyClaim.d.ts.map +1 -0
  73. package/dist/types/extensions/erc1155/drops/read/canClaim.d.ts +13 -0
  74. package/dist/types/extensions/erc1155/drops/read/canClaim.d.ts.map +1 -0
  75. package/dist/types/extensions/erc20/__generated__/DropERC20/read/verifyClaim.d.ts +136 -0
  76. package/dist/types/extensions/erc20/__generated__/DropERC20/read/verifyClaim.d.ts.map +1 -0
  77. package/dist/types/extensions/erc20/drops/read/canClaim.d.ts +15 -0
  78. package/dist/types/extensions/erc20/drops/read/canClaim.d.ts.map +1 -0
  79. package/dist/types/extensions/erc721/__generated__/DropERC721/read/verifyClaim.d.ts +136 -0
  80. package/dist/types/extensions/erc721/__generated__/DropERC721/read/verifyClaim.d.ts.map +1 -0
  81. package/dist/types/extensions/erc721/drops/read/canClaim.d.ts +12 -0
  82. package/dist/types/extensions/erc721/drops/read/canClaim.d.ts.map +1 -0
  83. package/dist/types/react/web/ui/ConnectWallet/screens/Buy/WalletSelectorButton.d.ts.map +1 -1
  84. package/dist/types/react/web/ui/ConnectWallet/screens/Buy/swap/PaymentSelectionScreen.d.ts.map +1 -1
  85. package/dist/types/react/web/utils/errors.d.ts.map +1 -1
  86. package/dist/types/transaction/extract-error.d.ts +4 -0
  87. package/dist/types/transaction/extract-error.d.ts.map +1 -1
  88. package/dist/types/version.d.ts +1 -1
  89. package/dist/types/version.d.ts.map +1 -1
  90. package/package.json +1 -1
  91. package/src/exports/extensions/erc1155.ts +5 -0
  92. package/src/exports/extensions/erc20.ts +5 -0
  93. package/src/exports/extensions/erc721.ts +5 -0
  94. package/src/extensions/erc1155/__generated__/DropERC1155/read/verifyClaim.ts +223 -0
  95. package/src/extensions/erc1155/drop1155.test.ts +42 -0
  96. package/src/extensions/erc1155/drops/read/canClaim.ts +57 -0
  97. package/src/extensions/erc20/__generated__/DropERC20/read/verifyClaim.ts +213 -0
  98. package/src/extensions/erc20/drop20.test.ts +38 -0
  99. package/src/extensions/erc20/drops/read/canClaim.ts +65 -0
  100. package/src/extensions/erc721/__generated__/DropERC721/read/verifyClaim.ts +213 -0
  101. package/src/extensions/erc721/drop721.test.ts +39 -0
  102. package/src/extensions/erc721/drops/read/canClaim.ts +53 -0
  103. package/src/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.tsx +16 -3
  104. package/src/react/web/ui/ConnectWallet/screens/Buy/WalletSelectorButton.tsx +27 -20
  105. package/src/react/web/ui/ConnectWallet/screens/Buy/fiat/FiatSteps.tsx +1 -2
  106. package/src/react/web/ui/ConnectWallet/screens/Buy/swap/PaymentSelectionScreen.tsx +28 -16
  107. package/src/react/web/utils/errors.ts +6 -1
  108. package/src/transaction/actions/gasless/providers/engine.test.ts +1 -1
  109. package/src/transaction/actions/gasless/providers/openzeppelin.test.ts +1 -1
  110. package/src/transaction/extract-error.ts +15 -13
  111. package/src/version.ts +1 -1
@@ -14,6 +14,7 @@ import { resolvePromisedValue } from "../../utils/promise/resolve-promised-value
14
14
  import { toEther } from "../../utils/units.js";
15
15
  import { name } from "../common/read/name.js";
16
16
  import { deployERC20Contract } from "../prebuilts/deploy-erc20.js";
17
+ import { canClaim } from "./drops/read/canClaim.js";
17
18
  import { getClaimConditions } from "./drops/read/getClaimConditions.js";
18
19
  import { claimTo } from "./drops/write/claimTo.js";
19
20
  import { resetClaimEligibility } from "./drops/write/resetClaimEligibility.js";
@@ -76,6 +77,18 @@ describe.runIf(process.env.TW_SECRET_KEY)(
76
77
  }),
77
78
  account: TEST_ACCOUNT_A,
78
79
  });
80
+
81
+ expect(
82
+ await canClaim({
83
+ contract,
84
+ claimer: TEST_ACCOUNT_A.address,
85
+ quantity: "1",
86
+ }),
87
+ ).toMatchInlineSnapshot(`
88
+ {
89
+ "result": true,
90
+ }
91
+ `);
79
92
  const claimTx = claimTo({
80
93
  contract,
81
94
  to: TEST_ACCOUNT_A.address,
@@ -180,6 +193,31 @@ describe.runIf(process.env.TW_SECRET_KEY)(
180
193
  }
181
194
  `);
182
195
 
196
+ expect(
197
+ await canClaim({
198
+ contract,
199
+ claimer: TEST_ACCOUNT_A.address,
200
+ quantity: "1",
201
+ }),
202
+ ).toMatchInlineSnapshot(`
203
+ {
204
+ "result": true,
205
+ }
206
+ `);
207
+
208
+ expect(
209
+ await canClaim({
210
+ contract,
211
+ claimer: TEST_ACCOUNT_B.address,
212
+ quantity: "1",
213
+ }),
214
+ ).toMatchInlineSnapshot(`
215
+ {
216
+ "reason": "DropClaimExceedLimit - 0,1000000000000000000",
217
+ "result": false,
218
+ }
219
+ `);
220
+
183
221
  await sendAndConfirmTransaction({
184
222
  account: TEST_ACCOUNT_A,
185
223
  transaction: claimTo({
@@ -0,0 +1,65 @@
1
+ import { extractErrorResult } from "../../../../transaction/extract-error.js";
2
+ import type { BaseTransactionOptions } from "../../../../transaction/types.js";
3
+ import { getClaimParams } from "../../../../utils/extensions/drops/get-claim-params.js";
4
+ import { verifyClaim } from "../../__generated__/DropERC20/read/verifyClaim.js";
5
+ import { getActiveClaimConditionId } from "../../__generated__/IDropERC20/read/getActiveClaimConditionId.js";
6
+ import { decimals } from "../../read/decimals.js";
7
+
8
+ export type CanClaimParams = {
9
+ claimer: string;
10
+ from?: string;
11
+ } & ({ quantityInWei: bigint } | { quantity: string });
12
+
13
+ export type CanClaimResult = {
14
+ result: boolean;
15
+ reason?: string;
16
+ };
17
+
18
+ export async function canClaim(
19
+ options: BaseTransactionOptions<CanClaimParams>,
20
+ ): Promise<CanClaimResult> {
21
+ const quantityWei = await (async () => {
22
+ if ("quantityInWei" in options) {
23
+ return options.quantityInWei;
24
+ }
25
+
26
+ const { toUnits } = await import("../../../../utils/units.js");
27
+ return toUnits(
28
+ options.quantity,
29
+ await decimals({ contract: options.contract }),
30
+ );
31
+ })();
32
+ const [conditionId, { quantity, currency, pricePerToken, allowlistProof }] =
33
+ await Promise.all([
34
+ getActiveClaimConditionId({
35
+ contract: options.contract,
36
+ }),
37
+ getClaimParams({
38
+ contract: options.contract,
39
+ quantity: quantityWei,
40
+ to: options.claimer,
41
+ type: "erc20",
42
+ from: options.from,
43
+ tokenDecimals: await decimals({ contract: options.contract }),
44
+ }),
45
+ ]);
46
+ try {
47
+ await verifyClaim({
48
+ contract: options.contract,
49
+ claimer: options.claimer,
50
+ quantity,
51
+ currency,
52
+ pricePerToken,
53
+ allowlistProof,
54
+ conditionId,
55
+ });
56
+ return {
57
+ result: true,
58
+ };
59
+ } catch (error) {
60
+ return {
61
+ result: false,
62
+ reason: await extractErrorResult({ error, contract: options.contract }),
63
+ };
64
+ }
65
+ }
@@ -0,0 +1,213 @@
1
+ import type { AbiParameterToPrimitiveType } from "abitype";
2
+ import { readContract } from "../../../../../transaction/read-contract.js";
3
+ import type { BaseTransactionOptions } from "../../../../../transaction/types.js";
4
+ import { encodeAbiParameters } from "../../../../../utils/abi/encodeAbiParameters.js";
5
+ import { decodeAbiParameters } from "viem";
6
+ import type { Hex } from "../../../../../utils/encoding/hex.js";
7
+ import { detectMethod } from "../../../../../utils/bytecode/detectExtension.js";
8
+
9
+ /**
10
+ * Represents the parameters for the "verifyClaim" function.
11
+ */
12
+ export type VerifyClaimParams = {
13
+ conditionId: AbiParameterToPrimitiveType<{
14
+ type: "uint256";
15
+ name: "_conditionId";
16
+ }>;
17
+ claimer: AbiParameterToPrimitiveType<{ type: "address"; name: "_claimer" }>;
18
+ quantity: AbiParameterToPrimitiveType<{ type: "uint256"; name: "_quantity" }>;
19
+ currency: AbiParameterToPrimitiveType<{ type: "address"; name: "_currency" }>;
20
+ pricePerToken: AbiParameterToPrimitiveType<{
21
+ type: "uint256";
22
+ name: "_pricePerToken";
23
+ }>;
24
+ allowlistProof: AbiParameterToPrimitiveType<{
25
+ type: "tuple";
26
+ name: "_allowlistProof";
27
+ components: [
28
+ { type: "bytes32[]"; name: "proof" },
29
+ { type: "uint256"; name: "quantityLimitPerWallet" },
30
+ { type: "uint256"; name: "pricePerToken" },
31
+ { type: "address"; name: "currency" },
32
+ ];
33
+ }>;
34
+ };
35
+
36
+ export const FN_SELECTOR = "0x23a2902b" as const;
37
+ const FN_INPUTS = [
38
+ {
39
+ type: "uint256",
40
+ name: "_conditionId",
41
+ },
42
+ {
43
+ type: "address",
44
+ name: "_claimer",
45
+ },
46
+ {
47
+ type: "uint256",
48
+ name: "_quantity",
49
+ },
50
+ {
51
+ type: "address",
52
+ name: "_currency",
53
+ },
54
+ {
55
+ type: "uint256",
56
+ name: "_pricePerToken",
57
+ },
58
+ {
59
+ type: "tuple",
60
+ name: "_allowlistProof",
61
+ components: [
62
+ {
63
+ type: "bytes32[]",
64
+ name: "proof",
65
+ },
66
+ {
67
+ type: "uint256",
68
+ name: "quantityLimitPerWallet",
69
+ },
70
+ {
71
+ type: "uint256",
72
+ name: "pricePerToken",
73
+ },
74
+ {
75
+ type: "address",
76
+ name: "currency",
77
+ },
78
+ ],
79
+ },
80
+ ] as const;
81
+ const FN_OUTPUTS = [
82
+ {
83
+ type: "bool",
84
+ name: "isOverride",
85
+ },
86
+ ] as const;
87
+
88
+ /**
89
+ * Checks if the `verifyClaim` method is supported by the given contract.
90
+ * @param availableSelectors An array of 4byte function selectors of the contract. You can get this in various ways, such as using "whatsabi" or if you have the ABI of the contract available you can use it to generate the selectors.
91
+ * @returns A boolean indicating if the `verifyClaim` method is supported.
92
+ * @extension ERC721
93
+ * @example
94
+ * ```ts
95
+ * import { isVerifyClaimSupported } from "thirdweb/extensions/erc721";
96
+ * const supported = isVerifyClaimSupported(["0x..."]);
97
+ * ```
98
+ */
99
+ export function isVerifyClaimSupported(availableSelectors: string[]) {
100
+ return detectMethod({
101
+ availableSelectors,
102
+ method: [FN_SELECTOR, FN_INPUTS, FN_OUTPUTS] as const,
103
+ });
104
+ }
105
+
106
+ /**
107
+ * Encodes the parameters for the "verifyClaim" function.
108
+ * @param options - The options for the verifyClaim function.
109
+ * @returns The encoded ABI parameters.
110
+ * @extension ERC721
111
+ * @example
112
+ * ```ts
113
+ * import { encodeVerifyClaimParams } from "thirdweb/extensions/erc721";
114
+ * const result = encodeVerifyClaimParams({
115
+ * conditionId: ...,
116
+ * claimer: ...,
117
+ * quantity: ...,
118
+ * currency: ...,
119
+ * pricePerToken: ...,
120
+ * allowlistProof: ...,
121
+ * });
122
+ * ```
123
+ */
124
+ export function encodeVerifyClaimParams(options: VerifyClaimParams) {
125
+ return encodeAbiParameters(FN_INPUTS, [
126
+ options.conditionId,
127
+ options.claimer,
128
+ options.quantity,
129
+ options.currency,
130
+ options.pricePerToken,
131
+ options.allowlistProof,
132
+ ]);
133
+ }
134
+
135
+ /**
136
+ * Encodes the "verifyClaim" function into a Hex string with its parameters.
137
+ * @param options - The options for the verifyClaim function.
138
+ * @returns The encoded hexadecimal string.
139
+ * @extension ERC721
140
+ * @example
141
+ * ```ts
142
+ * import { encodeVerifyClaim } from "thirdweb/extensions/erc721";
143
+ * const result = encodeVerifyClaim({
144
+ * conditionId: ...,
145
+ * claimer: ...,
146
+ * quantity: ...,
147
+ * currency: ...,
148
+ * pricePerToken: ...,
149
+ * allowlistProof: ...,
150
+ * });
151
+ * ```
152
+ */
153
+ export function encodeVerifyClaim(options: VerifyClaimParams) {
154
+ // we do a "manual" concat here to avoid the overhead of the "concatHex" function
155
+ // we can do this because we know the specific formats of the values
156
+ return (FN_SELECTOR +
157
+ encodeVerifyClaimParams(options).slice(
158
+ 2,
159
+ )) as `${typeof FN_SELECTOR}${string}`;
160
+ }
161
+
162
+ /**
163
+ * Decodes the result of the verifyClaim function call.
164
+ * @param result - The hexadecimal result to decode.
165
+ * @returns The decoded result as per the FN_OUTPUTS definition.
166
+ * @extension ERC721
167
+ * @example
168
+ * ```ts
169
+ * import { decodeVerifyClaimResult } from "thirdweb/extensions/erc721";
170
+ * const result = decodeVerifyClaimResultResult("...");
171
+ * ```
172
+ */
173
+ export function decodeVerifyClaimResult(result: Hex) {
174
+ return decodeAbiParameters(FN_OUTPUTS, result)[0];
175
+ }
176
+
177
+ /**
178
+ * Calls the "verifyClaim" function on the contract.
179
+ * @param options - The options for the verifyClaim function.
180
+ * @returns The parsed result of the function call.
181
+ * @extension ERC721
182
+ * @example
183
+ * ```ts
184
+ * import { verifyClaim } from "thirdweb/extensions/erc721";
185
+ *
186
+ * const result = await verifyClaim({
187
+ * contract,
188
+ * conditionId: ...,
189
+ * claimer: ...,
190
+ * quantity: ...,
191
+ * currency: ...,
192
+ * pricePerToken: ...,
193
+ * allowlistProof: ...,
194
+ * });
195
+ *
196
+ * ```
197
+ */
198
+ export async function verifyClaim(
199
+ options: BaseTransactionOptions<VerifyClaimParams>,
200
+ ) {
201
+ return readContract({
202
+ contract: options.contract,
203
+ method: [FN_SELECTOR, FN_INPUTS, FN_OUTPUTS] as const,
204
+ params: [
205
+ options.conditionId,
206
+ options.claimer,
207
+ options.quantity,
208
+ options.currency,
209
+ options.pricePerToken,
210
+ options.allowlistProof,
211
+ ],
212
+ });
213
+ }
@@ -25,6 +25,7 @@ import { setClaimConditions } from "./drops/write/setClaimConditions.js";
25
25
  import { getNFT } from "./read/getNFT.js";
26
26
  import { lazyMint } from "./write/lazyMint.js";
27
27
 
28
+ import { canClaim } from "./drops/read/canClaim.js";
28
29
  describe.runIf(process.env.TW_SECRET_KEY)(
29
30
  "DropERC721",
30
31
  {
@@ -121,6 +122,19 @@ describe.runIf(process.env.TW_SECRET_KEY)(
121
122
  }),
122
123
  account: TEST_ACCOUNT_A,
123
124
  });
125
+
126
+ expect(
127
+ await canClaim({
128
+ contract,
129
+ claimer: TEST_ACCOUNT_A.address,
130
+ quantity: 1n,
131
+ }),
132
+ ).toMatchInlineSnapshot(`
133
+ {
134
+ "result": true,
135
+ }
136
+ `);
137
+
124
138
  const claimTx = claimTo({
125
139
  contract,
126
140
  to: TEST_ACCOUNT_A.address,
@@ -208,6 +222,31 @@ describe.runIf(process.env.TW_SECRET_KEY)(
208
222
  balanceOf({ contract, owner: TEST_ACCOUNT_B.address }),
209
223
  ).resolves.toBe(0n);
210
224
 
225
+ expect(
226
+ await canClaim({
227
+ contract,
228
+ claimer: TEST_ACCOUNT_A.address,
229
+ quantity: 1n,
230
+ }),
231
+ ).toMatchInlineSnapshot(`
232
+ {
233
+ "result": true,
234
+ }
235
+ `);
236
+
237
+ expect(
238
+ await canClaim({
239
+ contract,
240
+ claimer: TEST_ACCOUNT_B.address,
241
+ quantity: 1n,
242
+ }),
243
+ ).toMatchInlineSnapshot(`
244
+ {
245
+ "reason": "DropClaimExceedLimit - 0,1",
246
+ "result": false,
247
+ }
248
+ `);
249
+
211
250
  await sendAndConfirmTransaction({
212
251
  account: TEST_ACCOUNT_A,
213
252
  transaction: claimTo({
@@ -0,0 +1,53 @@
1
+ import { extractErrorResult } from "../../../../transaction/extract-error.js";
2
+ import type { BaseTransactionOptions } from "../../../../transaction/types.js";
3
+ import { getClaimParams } from "../../../../utils/extensions/drops/get-claim-params.js";
4
+ import { verifyClaim } from "../../__generated__/DropERC721/read/verifyClaim.js";
5
+ import { getActiveClaimConditionId } from "../../__generated__/IDrop/read/getActiveClaimConditionId.js";
6
+
7
+ export type CanClaimParams = {
8
+ claimer: string;
9
+ quantity: bigint;
10
+ from?: string;
11
+ };
12
+
13
+ export type CanClaimResult = {
14
+ result: boolean;
15
+ reason?: string;
16
+ };
17
+
18
+ export async function canClaim(
19
+ options: BaseTransactionOptions<CanClaimParams>,
20
+ ): Promise<CanClaimResult> {
21
+ const [conditionId, { quantity, currency, pricePerToken, allowlistProof }] =
22
+ await Promise.all([
23
+ getActiveClaimConditionId({
24
+ contract: options.contract,
25
+ }),
26
+ getClaimParams({
27
+ contract: options.contract,
28
+ quantity: options.quantity,
29
+ to: options.claimer,
30
+ type: "erc721",
31
+ from: options.from,
32
+ }),
33
+ ]);
34
+ try {
35
+ await verifyClaim({
36
+ contract: options.contract,
37
+ claimer: options.claimer,
38
+ quantity,
39
+ currency,
40
+ pricePerToken,
41
+ allowlistProof,
42
+ conditionId,
43
+ });
44
+ return {
45
+ result: true,
46
+ };
47
+ } catch (error) {
48
+ return {
49
+ result: false,
50
+ reason: await extractErrorResult({ error, contract: options.contract }),
51
+ };
52
+ }
53
+ }
@@ -517,6 +517,11 @@ function BuyScreenContent(props: BuyScreenContentProps) {
517
517
  screen.id === "buy-with-fiat") &&
518
518
  payer && (
519
519
  <TokenSelectedLayout
520
+ disabled={
521
+ ("prefillBuy" in payOptions &&
522
+ payOptions.prefillBuy?.allowEdits?.amount === false) ||
523
+ payOptions.mode !== "fund_wallet"
524
+ }
520
525
  title={props.title}
521
526
  selectedChain={toChain}
522
527
  selectedToken={toToken}
@@ -651,6 +656,7 @@ function SelectedTokenInfo(props: {
651
656
  tokenAmount: string;
652
657
  setTokenAmount: (amount: string) => void;
653
658
  client: ThirdwebClient;
659
+ disabled?: boolean;
654
660
  }) {
655
661
  const getWidth = () => {
656
662
  let chars = props.tokenAmount.replace(".", "").length;
@@ -680,7 +686,7 @@ function SelectedTokenInfo(props: {
680
686
  type="text"
681
687
  data-placeholder={props.tokenAmount === ""}
682
688
  value={props.tokenAmount || "0"}
683
- disabled={false} // TODO: add disabled freeze amount
689
+ disabled={props.disabled}
684
690
  onClick={(e) => {
685
691
  // put cursor at the end of the input
686
692
  if (props.tokenAmount === "") {
@@ -865,8 +871,13 @@ function MainScreen(props: {
865
871
  }}
866
872
  freezeAmount={payOptions.prefillBuy?.allowEdits?.amount === false}
867
873
  freezeChainAndToken={
868
- payOptions.prefillBuy?.allowEdits?.chain === false &&
869
- payOptions.prefillBuy?.allowEdits?.token === false
874
+ (payOptions.prefillBuy?.allowEdits?.chain === false &&
875
+ payOptions.prefillBuy?.allowEdits?.token === false) ||
876
+ (payOptions.buyWithCrypto !== false &&
877
+ payOptions.buyWithCrypto?.prefillSource?.allowEdits?.token ===
878
+ false &&
879
+ payOptions.buyWithCrypto?.prefillSource?.allowEdits?.chain ===
880
+ false)
870
881
  }
871
882
  token={toToken}
872
883
  chain={toChain}
@@ -924,6 +935,7 @@ function TokenSelectedLayout(props: {
924
935
  selectedChain: Chain;
925
936
  client: ThirdwebClient;
926
937
  onBack: () => void;
938
+ disabled?: boolean;
927
939
  }) {
928
940
  return (
929
941
  <Container>
@@ -944,6 +956,7 @@ function TokenSelectedLayout(props: {
944
956
  tokenAmount={props.tokenAmount}
945
957
  setTokenAmount={props.setTokenAmount}
946
958
  client={props.client}
959
+ disabled={props.disabled}
947
960
  />
948
961
 
949
962
  <Spacer y="md" />
@@ -1,4 +1,5 @@
1
1
  import styled from "@emotion/styled";
2
+ import { ChevronRightIcon } from "@radix-ui/react-icons";
2
3
  import { useState } from "react";
3
4
  import type { Chain } from "../../../../../../chains/types.js";
4
5
  import type { ThirdwebClient } from "../../../../../../client/client.js";
@@ -22,7 +23,7 @@ import { Container } from "../../../components/basic.js";
22
23
  import { Button } from "../../../components/buttons.js";
23
24
  import { Text } from "../../../components/text.js";
24
25
  import { Blobbie } from "../../Blobbie.js";
25
- import { formatTokenBalance } from "../formatTokenBalance.js";
26
+ import { FiatValue } from "./swap/FiatValue.js";
26
27
  import type { TokenBalance } from "./swap/PaymentSelectionScreen.js";
27
28
 
28
29
  export function WalletRowWithBalances(props: {
@@ -100,23 +101,29 @@ function TokenBalanceRow(props: {
100
101
  onClick={() => onClick(tokenBalance.token, wallet)}
101
102
  variant="secondary"
102
103
  >
103
- <TokenIcon
104
- token={tokenBalance.token}
105
- chain={tokenBalance.chain}
106
- size="md"
107
- client={client}
108
- />
109
- <Container flex="column" gap="3xs">
110
- <Text size="xs" color="primaryText">
111
- {tokenBalance.token.symbol}
112
- </Text>
113
- {chainInfo && <Text size="xs">{chainInfo.name}</Text>}
104
+ <Container flex="row" center="y" gap="md">
105
+ <TokenIcon
106
+ token={tokenBalance.token}
107
+ chain={tokenBalance.chain}
108
+ size="md"
109
+ client={client}
110
+ />
111
+ <Container flex="column" gap="3xs">
112
+ <Text size="xs" color="primaryText">
113
+ {tokenBalance.token.symbol}
114
+ </Text>
115
+ {chainInfo && <Text size="xs">{chainInfo.name}</Text>}
116
+ </Container>
114
117
  </Container>
115
- <div style={{ flex: 1 }} />
116
- <Container flex="row" center="y" gap="3xs">
117
- <Text size="xs" color="secondaryText">
118
- {formatTokenBalance(tokenBalance.balance, true)}
119
- </Text>
118
+ <Container flex="row" center="y" gap="3xs" color="secondaryText">
119
+ <FiatValue
120
+ tokenAmount={tokenBalance.balance.displayValue}
121
+ token={tokenBalance.token}
122
+ chain={tokenBalance.chain}
123
+ client={client}
124
+ size="xs"
125
+ />
126
+ <ChevronRightIcon width={iconSize.md} height={iconSize.md} />
120
127
  </Container>
121
128
  </StyledButton>
122
129
  );
@@ -149,7 +156,7 @@ export function WalletRow(props: {
149
156
  width={props.iconSize ? iconSize[props.iconSize] : iconSize.md}
150
157
  height={props.iconSize ? iconSize[props.iconSize] : iconSize.md}
151
158
  style={{
152
- borderRadius: "100%",
159
+ borderRadius: radius.sm,
153
160
  overflow: "hidden",
154
161
  border: `1px solid ${theme.colors.borderColor}`,
155
162
  }}
@@ -166,7 +173,7 @@ export function WalletRow(props: {
166
173
  style={{
167
174
  width: iconSize.md,
168
175
  height: iconSize.md,
169
- borderRadius: "100%",
176
+ borderRadius: radius.sm,
170
177
  overflow: "hidden",
171
178
  border: `1px solid ${theme.colors.borderColor}`,
172
179
  }}
@@ -186,7 +193,7 @@ const StyledButton = /* @__PURE__ */ styled(Button)((_) => {
186
193
  const theme = useCustomTheme();
187
194
  return {
188
195
  background: theme.colors.tertiaryBg,
189
- justifyContent: "flex-start",
196
+ justifyContent: "space-between",
190
197
  flexDirection: "row",
191
198
  padding: spacing.sm,
192
199
  gap: spacing.sm,
@@ -535,8 +535,7 @@ function PaymentSubStep(props: {
535
535
  {props.icon}
536
536
  </Container>
537
537
  <Container flex="column" gap="xxs">
538
- {props.primaryText}
539
- {props.secondaryText}
538
+ {props.primaryText} {props.secondaryText}
540
539
  </Container>
541
540
  </Container>
542
541
  );
@@ -166,25 +166,37 @@ export function PaymentSelectionScreen(props: {
166
166
  return <LoadingScreen />;
167
167
  }
168
168
 
169
+ const filteredWallets = Array.from(walletsAndBalances.data?.entries() || [])
170
+ .filter(([w]) => !props.hiddenWallets?.includes(w.id))
171
+ .filter(([, balances]) => {
172
+ const hasEnoughBalance = balances.some((b) => b.balance.value > 0);
173
+ return hasEnoughBalance;
174
+ });
175
+
169
176
  return (
170
177
  <Container>
171
178
  <Container flex="column" gap="xs">
172
- {Array.from(walletsAndBalances.data?.entries() || [])
173
- .filter(([w]) => !props.hiddenWallets?.includes(w.id))
174
- .map(([w, balances]) => {
175
- const address = w.getAccount()?.address;
176
- if (!address) return null;
177
- return (
178
- <WalletRowWithBalances
179
- key={w.id}
180
- wallet={w}
181
- balances={balances}
182
- client={props.client}
183
- address={address}
184
- onClick={props.onSelect}
185
- />
186
- );
187
- })}
179
+ {filteredWallets.length === 0 && (
180
+ <Container flex="column" gap="xs" py="md">
181
+ <Text size="xs" color="secondaryText" center>
182
+ <i>Insufficient funds in connected wallets</i>
183
+ </Text>
184
+ </Container>
185
+ )}
186
+ {filteredWallets.map(([w, balances]) => {
187
+ const address = w.getAccount()?.address;
188
+ if (!address) return null;
189
+ return (
190
+ <WalletRowWithBalances
191
+ key={w.id}
192
+ wallet={w}
193
+ balances={balances}
194
+ client={props.client}
195
+ address={address}
196
+ onClick={props.onSelect}
197
+ />
198
+ );
199
+ })}
188
200
  {!hideConnectButton && (
189
201
  <Button
190
202
  variant="secondary"
@@ -13,7 +13,12 @@ export const defaultMessage = "Unable to get price quote";
13
13
  // biome-ignore lint/suspicious/noExplicitAny: <explanation>
14
14
  export function getErrorMessage(err: any): ApiError {
15
15
  if (typeof err.error === "object" && err.error.code) {
16
- return err.error;
16
+ if (err.error.code === "MINIMUM_PURCHASE_AMOUNT") {
17
+ return {
18
+ code: "MINIMUM_PURCHASE_AMOUNT",
19
+ message: "Purchase amount is too low",
20
+ };
21
+ }
17
22
  }
18
23
  return {
19
24
  code: "UNABLE_TO_GET_PRICE_QUOTE",
@@ -78,7 +78,7 @@ describe.runIf(process.env.TW_SECRET_KEY)("prepareengineTransaction", () => {
78
78
  {
79
79
  "data": "0xa9059cbb00000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c80000000000000000000000000000000000000000000000056bc75e2d63100000",
80
80
  "from": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
81
- "gas": 64338n,
81
+ "gas": 64340n,
82
82
  "nonce": 0n,
83
83
  "to": "${erc20Contract.address}",
84
84
  "value": 0n,