thirdweb 5.48.2 → 5.48.3-nightly-b661ce7b2fd58e7ff01faf47cf8fb3bb232841ec-20240827000337

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 (112) hide show
  1. package/dist/cjs/exports/wallets/smart.js +4 -2
  2. package/dist/cjs/exports/wallets/smart.js.map +1 -1
  3. package/dist/cjs/extensions/erc1155/drops/write/claimTo.js.map +1 -1
  4. package/dist/cjs/extensions/erc4337/__generated__/IEntryPoint_v07/events/PostOpRevertReason.js +32 -0
  5. package/dist/cjs/extensions/erc4337/__generated__/IEntryPoint_v07/events/PostOpRevertReason.js.map +1 -0
  6. package/dist/cjs/extensions/erc4337/__generated__/IEntryPoint_v07/read/getUserOpHash.js +153 -0
  7. package/dist/cjs/extensions/erc4337/__generated__/IEntryPoint_v07/read/getUserOpHash.js.map +1 -0
  8. package/dist/cjs/extensions/prebuilts/__generated__/VoteERC20/write/initialize.js +184 -0
  9. package/dist/cjs/extensions/prebuilts/__generated__/VoteERC20/write/initialize.js.map +1 -0
  10. package/dist/cjs/extensions/prebuilts/deploy-vote.js +122 -0
  11. package/dist/cjs/extensions/prebuilts/deploy-vote.js.map +1 -0
  12. package/dist/cjs/version.js +1 -1
  13. package/dist/cjs/version.js.map +1 -1
  14. package/dist/cjs/wallets/smart/index.js +13 -2
  15. package/dist/cjs/wallets/smart/index.js.map +1 -1
  16. package/dist/cjs/wallets/smart/lib/bundler.js +11 -2
  17. package/dist/cjs/wallets/smart/lib/bundler.js.map +1 -1
  18. package/dist/cjs/wallets/smart/lib/constants.js +27 -13
  19. package/dist/cjs/wallets/smart/lib/constants.js.map +1 -1
  20. package/dist/cjs/wallets/smart/lib/packUserOp.js +75 -0
  21. package/dist/cjs/wallets/smart/lib/packUserOp.js.map +1 -0
  22. package/dist/cjs/wallets/smart/lib/paymaster.js +11 -1
  23. package/dist/cjs/wallets/smart/lib/paymaster.js.map +1 -1
  24. package/dist/cjs/wallets/smart/lib/userop.js +201 -66
  25. package/dist/cjs/wallets/smart/lib/userop.js.map +1 -1
  26. package/dist/cjs/wallets/smart/lib/utils.js +1 -1
  27. package/dist/cjs/wallets/smart/lib/utils.js.map +1 -1
  28. package/dist/cjs/wallets/smart/smart-wallet.js +2 -1
  29. package/dist/cjs/wallets/smart/smart-wallet.js.map +1 -1
  30. package/dist/cjs/wallets/smart/types.js.map +1 -1
  31. package/dist/esm/exports/wallets/smart.js +1 -1
  32. package/dist/esm/exports/wallets/smart.js.map +1 -1
  33. package/dist/esm/extensions/erc1155/drops/write/claimTo.js.map +1 -1
  34. package/dist/esm/extensions/erc4337/__generated__/IEntryPoint_v07/events/PostOpRevertReason.js +29 -0
  35. package/dist/esm/extensions/erc4337/__generated__/IEntryPoint_v07/events/PostOpRevertReason.js.map +1 -0
  36. package/dist/esm/extensions/erc4337/__generated__/IEntryPoint_v07/read/getUserOpHash.js +145 -0
  37. package/dist/esm/extensions/erc4337/__generated__/IEntryPoint_v07/read/getUserOpHash.js.map +1 -0
  38. package/dist/esm/extensions/prebuilts/__generated__/VoteERC20/write/initialize.js +177 -0
  39. package/dist/esm/extensions/prebuilts/__generated__/VoteERC20/write/initialize.js.map +1 -0
  40. package/dist/esm/extensions/prebuilts/deploy-vote.js +119 -0
  41. package/dist/esm/extensions/prebuilts/deploy-vote.js.map +1 -0
  42. package/dist/esm/version.js +1 -1
  43. package/dist/esm/version.js.map +1 -1
  44. package/dist/esm/wallets/smart/index.js +14 -3
  45. package/dist/esm/wallets/smart/index.js.map +1 -1
  46. package/dist/esm/wallets/smart/lib/bundler.js +12 -3
  47. package/dist/esm/wallets/smart/lib/bundler.js.map +1 -1
  48. package/dist/esm/wallets/smart/lib/constants.js +23 -10
  49. package/dist/esm/wallets/smart/lib/constants.js.map +1 -1
  50. package/dist/esm/wallets/smart/lib/packUserOp.js +65 -0
  51. package/dist/esm/wallets/smart/lib/packUserOp.js.map +1 -0
  52. package/dist/esm/wallets/smart/lib/paymaster.js +12 -2
  53. package/dist/esm/wallets/smart/lib/paymaster.js.map +1 -1
  54. package/dist/esm/wallets/smart/lib/userop.js +202 -67
  55. package/dist/esm/wallets/smart/lib/userop.js.map +1 -1
  56. package/dist/esm/wallets/smart/lib/utils.js +1 -1
  57. package/dist/esm/wallets/smart/lib/utils.js.map +1 -1
  58. package/dist/esm/wallets/smart/smart-wallet.js +3 -2
  59. package/dist/esm/wallets/smart/smart-wallet.js.map +1 -1
  60. package/dist/esm/wallets/smart/types.js.map +1 -1
  61. package/dist/types/exports/wallets/smart.d.ts +2 -2
  62. package/dist/types/exports/wallets/smart.d.ts.map +1 -1
  63. package/dist/types/extensions/erc1155/drops/write/claimTo.d.ts +2 -3
  64. package/dist/types/extensions/erc1155/drops/write/claimTo.d.ts.map +1 -1
  65. package/dist/types/extensions/erc4337/__generated__/IEntryPoint_v07/events/PostOpRevertReason.d.ts +57 -0
  66. package/dist/types/extensions/erc4337/__generated__/IEntryPoint_v07/events/PostOpRevertReason.d.ts.map +1 -0
  67. package/dist/types/extensions/erc4337/__generated__/IEntryPoint_v07/read/getUserOpHash.d.ts +123 -0
  68. package/dist/types/extensions/erc4337/__generated__/IEntryPoint_v07/read/getUserOpHash.d.ts.map +1 -0
  69. package/dist/types/extensions/prebuilts/__generated__/VoteERC20/write/initialize.d.ts +129 -0
  70. package/dist/types/extensions/prebuilts/__generated__/VoteERC20/write/initialize.d.ts.map +1 -0
  71. package/dist/types/extensions/prebuilts/deploy-vote.d.ts +82 -0
  72. package/dist/types/extensions/prebuilts/deploy-vote.d.ts.map +1 -0
  73. package/dist/types/version.d.ts +1 -1
  74. package/dist/types/version.d.ts.map +1 -1
  75. package/dist/types/wallets/smart/index.d.ts.map +1 -1
  76. package/dist/types/wallets/smart/lib/bundler.d.ts +3 -3
  77. package/dist/types/wallets/smart/lib/bundler.d.ts.map +1 -1
  78. package/dist/types/wallets/smart/lib/constants.d.ts +6 -6
  79. package/dist/types/wallets/smart/lib/constants.d.ts.map +1 -1
  80. package/dist/types/wallets/smart/lib/packUserOp.d.ts +16 -0
  81. package/dist/types/wallets/smart/lib/packUserOp.d.ts.map +1 -0
  82. package/dist/types/wallets/smart/lib/paymaster.d.ts +3 -3
  83. package/dist/types/wallets/smart/lib/paymaster.d.ts.map +1 -1
  84. package/dist/types/wallets/smart/lib/userop.d.ts +6 -4
  85. package/dist/types/wallets/smart/lib/userop.d.ts.map +1 -1
  86. package/dist/types/wallets/smart/lib/utils.d.ts +2 -2
  87. package/dist/types/wallets/smart/lib/utils.d.ts.map +1 -1
  88. package/dist/types/wallets/smart/smart-wallet.d.ts.map +1 -1
  89. package/dist/types/wallets/smart/types.d.ts +62 -9
  90. package/dist/types/wallets/smart/types.d.ts.map +1 -1
  91. package/package.json +1 -1
  92. package/src/exports/wallets/smart.ts +4 -2
  93. package/src/extensions/erc1155/drops/write/claimTo.ts +2 -3
  94. package/src/extensions/erc4337/__generated__/IEntryPoint_v07/events/PostOpRevertReason.ts +49 -0
  95. package/src/extensions/erc4337/__generated__/IEntryPoint_v07/read/getUserOpHash.ts +181 -0
  96. package/src/extensions/prebuilts/__generated__/VoteERC20/write/initialize.ts +230 -0
  97. package/src/extensions/prebuilts/deploy-vote.test.ts +73 -0
  98. package/src/extensions/prebuilts/deploy-vote.ts +213 -0
  99. package/src/extensions/vote/read/proposalExists.test.ts +123 -0
  100. package/src/version.ts +1 -1
  101. package/src/wallets/smart/index.ts +21 -5
  102. package/src/wallets/smart/lib/bundler.ts +21 -5
  103. package/src/wallets/smart/lib/constants.ts +29 -10
  104. package/src/wallets/smart/lib/packUserOp.ts +79 -0
  105. package/src/wallets/smart/lib/paymaster.ts +22 -5
  106. package/src/wallets/smart/lib/userop.ts +292 -85
  107. package/src/wallets/smart/lib/utils.ts +11 -4
  108. package/src/wallets/smart/smart-wallet-integration-v07.test.ts +206 -0
  109. package/src/wallets/smart/smart-wallet-integration.test.ts +1 -1
  110. package/src/wallets/smart/smart-wallet-zksync.test.ts +1 -2
  111. package/src/wallets/smart/smart-wallet.ts +4 -2
  112. package/src/wallets/smart/types.ts +73 -9
@@ -0,0 +1,230 @@
1
+ import type { AbiParameterToPrimitiveType } from "abitype";
2
+ import type {
3
+ BaseTransactionOptions,
4
+ WithOverrides,
5
+ } from "../../../../../transaction/types.js";
6
+ import { prepareContractCall } from "../../../../../transaction/prepare-contract-call.js";
7
+ import { encodeAbiParameters } from "../../../../../utils/abi/encodeAbiParameters.js";
8
+ import { once } from "../../../../../utils/promise/once.js";
9
+ import type { ThirdwebContract } from "../../../../../contract/contract.js";
10
+ import { detectMethod } from "../../../../../utils/bytecode/detectExtension.js";
11
+
12
+ /**
13
+ * Represents the parameters for the "initialize" function.
14
+ */
15
+ export type InitializeParams = WithOverrides<{
16
+ name: AbiParameterToPrimitiveType<{ type: "string"; name: "_name" }>;
17
+ contractURI: AbiParameterToPrimitiveType<{
18
+ type: "string";
19
+ name: "_contractURI";
20
+ }>;
21
+ trustedForwarders: AbiParameterToPrimitiveType<{
22
+ type: "address[]";
23
+ name: "_trustedForwarders";
24
+ }>;
25
+ token: AbiParameterToPrimitiveType<{ type: "address"; name: "_token" }>;
26
+ initialVotingDelay: AbiParameterToPrimitiveType<{
27
+ type: "uint256";
28
+ name: "_initialVotingDelay";
29
+ }>;
30
+ initialVotingPeriod: AbiParameterToPrimitiveType<{
31
+ type: "uint256";
32
+ name: "_initialVotingPeriod";
33
+ }>;
34
+ initialProposalThreshold: AbiParameterToPrimitiveType<{
35
+ type: "uint256";
36
+ name: "_initialProposalThreshold";
37
+ }>;
38
+ initialVoteQuorumFraction: AbiParameterToPrimitiveType<{
39
+ type: "uint256";
40
+ name: "_initialVoteQuorumFraction";
41
+ }>;
42
+ }>;
43
+
44
+ export const FN_SELECTOR = "0x7cf43f8d" as const;
45
+ const FN_INPUTS = [
46
+ {
47
+ type: "string",
48
+ name: "_name",
49
+ },
50
+ {
51
+ type: "string",
52
+ name: "_contractURI",
53
+ },
54
+ {
55
+ type: "address[]",
56
+ name: "_trustedForwarders",
57
+ },
58
+ {
59
+ type: "address",
60
+ name: "_token",
61
+ },
62
+ {
63
+ type: "uint256",
64
+ name: "_initialVotingDelay",
65
+ },
66
+ {
67
+ type: "uint256",
68
+ name: "_initialVotingPeriod",
69
+ },
70
+ {
71
+ type: "uint256",
72
+ name: "_initialProposalThreshold",
73
+ },
74
+ {
75
+ type: "uint256",
76
+ name: "_initialVoteQuorumFraction",
77
+ },
78
+ ] as const;
79
+ const FN_OUTPUTS = [] as const;
80
+
81
+ /**
82
+ * Checks if the `initialize` method is supported by the given contract.
83
+ * @param contract The ThirdwebContract.
84
+ * @returns A promise that resolves to a boolean indicating if the `initialize` method is supported.
85
+ * @extension PREBUILTS
86
+ * @example
87
+ * ```ts
88
+ * import { isInitializeSupported } from "thirdweb/extensions/prebuilts";
89
+ *
90
+ * const supported = await isInitializeSupported(contract);
91
+ * ```
92
+ */
93
+ export async function isInitializeSupported(contract: ThirdwebContract<any>) {
94
+ return detectMethod({
95
+ contract,
96
+ method: [FN_SELECTOR, FN_INPUTS, FN_OUTPUTS] as const,
97
+ });
98
+ }
99
+
100
+ /**
101
+ * Encodes the parameters for the "initialize" function.
102
+ * @param options - The options for the initialize function.
103
+ * @returns The encoded ABI parameters.
104
+ * @extension PREBUILTS
105
+ * @example
106
+ * ```ts
107
+ * import { encodeInitializeParams } "thirdweb/extensions/prebuilts";
108
+ * const result = encodeInitializeParams({
109
+ * name: ...,
110
+ * contractURI: ...,
111
+ * trustedForwarders: ...,
112
+ * token: ...,
113
+ * initialVotingDelay: ...,
114
+ * initialVotingPeriod: ...,
115
+ * initialProposalThreshold: ...,
116
+ * initialVoteQuorumFraction: ...,
117
+ * });
118
+ * ```
119
+ */
120
+ export function encodeInitializeParams(options: InitializeParams) {
121
+ return encodeAbiParameters(FN_INPUTS, [
122
+ options.name,
123
+ options.contractURI,
124
+ options.trustedForwarders,
125
+ options.token,
126
+ options.initialVotingDelay,
127
+ options.initialVotingPeriod,
128
+ options.initialProposalThreshold,
129
+ options.initialVoteQuorumFraction,
130
+ ]);
131
+ }
132
+
133
+ /**
134
+ * Encodes the "initialize" function into a Hex string with its parameters.
135
+ * @param options - The options for the initialize function.
136
+ * @returns The encoded hexadecimal string.
137
+ * @extension PREBUILTS
138
+ * @example
139
+ * ```ts
140
+ * import { encodeInitialize } "thirdweb/extensions/prebuilts";
141
+ * const result = encodeInitialize({
142
+ * name: ...,
143
+ * contractURI: ...,
144
+ * trustedForwarders: ...,
145
+ * token: ...,
146
+ * initialVotingDelay: ...,
147
+ * initialVotingPeriod: ...,
148
+ * initialProposalThreshold: ...,
149
+ * initialVoteQuorumFraction: ...,
150
+ * });
151
+ * ```
152
+ */
153
+ export function encodeInitialize(options: InitializeParams) {
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
+ encodeInitializeParams(options).slice(
158
+ 2,
159
+ )) as `${typeof FN_SELECTOR}${string}`;
160
+ }
161
+
162
+ /**
163
+ * Prepares a transaction to call the "initialize" function on the contract.
164
+ * @param options - The options for the "initialize" function.
165
+ * @returns A prepared transaction object.
166
+ * @extension PREBUILTS
167
+ * @example
168
+ * ```ts
169
+ * import { initialize } from "thirdweb/extensions/prebuilts";
170
+ *
171
+ * const transaction = initialize({
172
+ * contract,
173
+ * name: ...,
174
+ * contractURI: ...,
175
+ * trustedForwarders: ...,
176
+ * token: ...,
177
+ * initialVotingDelay: ...,
178
+ * initialVotingPeriod: ...,
179
+ * initialProposalThreshold: ...,
180
+ * initialVoteQuorumFraction: ...,
181
+ * overrides: {
182
+ * ...
183
+ * }
184
+ * });
185
+ *
186
+ * // Send the transaction
187
+ * ...
188
+ *
189
+ * ```
190
+ */
191
+ export function initialize(
192
+ options: BaseTransactionOptions<
193
+ | InitializeParams
194
+ | {
195
+ asyncParams: () => Promise<InitializeParams>;
196
+ }
197
+ >,
198
+ ) {
199
+ const asyncOptions = once(async () => {
200
+ return "asyncParams" in options ? await options.asyncParams() : options;
201
+ });
202
+
203
+ return prepareContractCall({
204
+ contract: options.contract,
205
+ method: [FN_SELECTOR, FN_INPUTS, FN_OUTPUTS] as const,
206
+ params: async () => {
207
+ const resolvedOptions = await asyncOptions();
208
+ return [
209
+ resolvedOptions.name,
210
+ resolvedOptions.contractURI,
211
+ resolvedOptions.trustedForwarders,
212
+ resolvedOptions.token,
213
+ resolvedOptions.initialVotingDelay,
214
+ resolvedOptions.initialVotingPeriod,
215
+ resolvedOptions.initialProposalThreshold,
216
+ resolvedOptions.initialVoteQuorumFraction,
217
+ ] as const;
218
+ },
219
+ value: async () => (await asyncOptions()).overrides?.value,
220
+ accessList: async () => (await asyncOptions()).overrides?.accessList,
221
+ gas: async () => (await asyncOptions()).overrides?.gas,
222
+ gasPrice: async () => (await asyncOptions()).overrides?.gasPrice,
223
+ maxFeePerGas: async () => (await asyncOptions()).overrides?.maxFeePerGas,
224
+ maxPriorityFeePerGas: async () =>
225
+ (await asyncOptions()).overrides?.maxPriorityFeePerGas,
226
+ nonce: async () => (await asyncOptions()).overrides?.nonce,
227
+ extraGas: async () => (await asyncOptions()).overrides?.extraGas,
228
+ erc20Value: async () => (await asyncOptions()).overrides?.erc20Value,
229
+ });
230
+ }
@@ -0,0 +1,73 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { ANVIL_CHAIN } from "~test/chains.js";
3
+ import { TEST_CONTRACT_URI } from "~test/ipfs-uris.js";
4
+ import { TEST_CLIENT } from "~test/test-clients.js";
5
+ import { TEST_ACCOUNT_A } from "~test/test-wallets.js";
6
+ import { isAddress } from "../../utils/address.js";
7
+ import { deployERC20Contract } from "./deploy-erc20.js";
8
+ import { deployVoteContract } from "./deploy-vote.js";
9
+
10
+ describe.runIf(process.env.TW_SECRET_KEY)("deploy-voteERC20 contract", () => {
11
+ it("should deploy Vote contract", async () => {
12
+ const tokenAddress = await deployERC20Contract({
13
+ client: TEST_CLIENT,
14
+ chain: ANVIL_CHAIN,
15
+ account: TEST_ACCOUNT_A,
16
+ type: "TokenERC20",
17
+ params: {
18
+ name: "Token",
19
+ contractURI: TEST_CONTRACT_URI,
20
+ },
21
+ });
22
+ const address = await deployVoteContract({
23
+ account: TEST_ACCOUNT_A,
24
+ client: TEST_CLIENT,
25
+ chain: ANVIL_CHAIN,
26
+ params: {
27
+ name: "",
28
+ contractURI: TEST_CONTRACT_URI,
29
+ tokenAddress: tokenAddress,
30
+ // user needs 0.5 <token> to create proposal
31
+ initialProposalThreshold: "0.5",
32
+ // vote expires 10 blocks later
33
+ initialVotingPeriod: 10,
34
+ // Requires 51% of users who voted, voted "For", for this proposal to pass
35
+ minVoteQuorumRequiredPercent: 51,
36
+ },
37
+ });
38
+ expect(address).toBeDefined();
39
+ expect(isAddress(address)).toBe(true);
40
+ // Further tests to verify the functionality of this contract
41
+ // are done in other Vote tests
42
+ });
43
+
44
+ it("should throw if passed an non-integer-like value to minVoteQuorumRequiredPercent", async () => {
45
+ const tokenAddress = await deployERC20Contract({
46
+ client: TEST_CLIENT,
47
+ chain: ANVIL_CHAIN,
48
+ account: TEST_ACCOUNT_A,
49
+ type: "TokenERC20",
50
+ params: {
51
+ name: "Token",
52
+ contractURI: TEST_CONTRACT_URI,
53
+ },
54
+ });
55
+ await expect(() =>
56
+ deployVoteContract({
57
+ account: TEST_ACCOUNT_A,
58
+ client: TEST_CLIENT,
59
+ chain: ANVIL_CHAIN,
60
+ params: {
61
+ name: "",
62
+ contractURI: TEST_CONTRACT_URI,
63
+ tokenAddress: tokenAddress,
64
+ initialProposalThreshold: "0.5",
65
+ initialVotingPeriod: 10,
66
+ minVoteQuorumRequiredPercent: 51.12,
67
+ },
68
+ }),
69
+ ).rejects.toThrowError(
70
+ "51.12 is an invalid value. Only integer-like values accepted",
71
+ );
72
+ });
73
+ });
@@ -0,0 +1,213 @@
1
+ import type { Chain } from "../../chains/types.js";
2
+ import type { ThirdwebClient } from "../../client/client.js";
3
+ import { type ThirdwebContract, getContract } from "../../contract/contract.js";
4
+ import { deployViaAutoFactory } from "../../contract/deployment/deploy-via-autofactory.js";
5
+ import { getOrDeployInfraForPublishedContract } from "../../contract/deployment/utils/bootstrap.js";
6
+ import type { FileOrBufferOrString } from "../../storage/upload/types.js";
7
+ import type { Prettify } from "../../utils/type-utils.js";
8
+ import type { ClientAndChainAndAccount } from "../../utils/types.js";
9
+ import { decimals } from "../erc20/read/decimals.js";
10
+
11
+ /**
12
+ * @extension DEPLOY
13
+ */
14
+ export type VoteContractParams = {
15
+ name: string;
16
+ /**
17
+ * The contract address for the ERC20 that will be used as voting power
18
+ */
19
+ tokenAddress: string;
20
+ /**
21
+ * The number of blocks after a proposal is created that voting on the proposal starts.
22
+ * A block is a series of blockchain transactions and occurs every ~1 seconds.
23
+ * Block time is different across EVM networks
24
+ *
25
+ * Defaults to 0 (zero)
26
+ */
27
+ initialVotingDelay?: number;
28
+ /**
29
+ * The number of blocks that voters have to vote on any new proposal.
30
+ */
31
+ initialVotingPeriod: number;
32
+ /**
33
+ * The minimum number of voting tokens a wallet needs in order to create proposals.
34
+ * This amount that you have to enter is _not_ in wei. If you want users to have a least 0.5 ERC20 token to create proposals,
35
+ * enter `"0.5"` or `0.5`. The deploy script will fetch the ERC20 token's decimals and do the unit conversion for you.
36
+ */
37
+ initialProposalThreshold: string | number;
38
+ /**
39
+ * The fraction of the total voting power that is required for a proposal to pass.
40
+ * A value of 0 indicates that no voting power is sufficient,
41
+ * whereas a value of 100 indicates that the entirety of voting power must vote for a proposal to pass.
42
+ * `initialProposalThreshold` should be an integer or an integer-convertoble string. For example:
43
+ * - 51 or "51" is a valid input
44
+ * - 51.225 or "51.225" is an invalid input
45
+ */
46
+ minVoteQuorumRequiredPercent: number | string;
47
+ description?: string;
48
+ image?: FileOrBufferOrString;
49
+ external_link?: string;
50
+ social_urls?: Record<string, string>;
51
+ symbol?: string;
52
+ contractURI?: string;
53
+ defaultAdmin?: string;
54
+ trustedForwarders?: string[];
55
+ };
56
+
57
+ /**
58
+ * @extension DEPLOY
59
+ */
60
+ export type DeployVoteContractOptions = Prettify<
61
+ ClientAndChainAndAccount & {
62
+ params: VoteContractParams;
63
+ }
64
+ >;
65
+
66
+ /**
67
+ * Deploys a thirdweb [`VoteERC20 contract`](https://thirdweb.com/thirdweb.eth/VoteERC20)
68
+ * On chains where the thirdweb infrastructure contracts are not deployed, this function will deploy them as well.
69
+ * @param options - The deployment options.
70
+ * @returns The deployed contract address.
71
+ * @extension DEPLOY
72
+ *
73
+ * @example
74
+ * ```ts
75
+ * import { deployVoteContract } from "thirdweb/deploys";
76
+ * const contractAddress = await deployVoteContract({
77
+ * chain,
78
+ * client,
79
+ * account,
80
+ * params: {
81
+ * tokenAddress: "0x...",
82
+ * // user needs 0.5 <token> to create proposal
83
+ * initialProposalThreshold: "0.5",
84
+ * // vote expires 10 blocks later
85
+ * initialVotingPeriod: 10,
86
+ * // Requires 51% of users who voted, voted "For", for this proposal to pass
87
+ * minVoteQuorumRequiredPercent: 51,
88
+ * }
89
+ * });
90
+ * ```
91
+ */
92
+ export async function deployVoteContract(options: DeployVoteContractOptions) {
93
+ const { chain, client, account, params } = options;
94
+ const { cloneFactoryContract, implementationContract } =
95
+ await getOrDeployInfraForPublishedContract({
96
+ chain,
97
+ client,
98
+ account,
99
+ contractId: "VoteERC20",
100
+ constructorParams: [],
101
+ });
102
+ const initializeTransaction = await getInitializeTransaction({
103
+ client,
104
+ implementationContract,
105
+ params,
106
+ accountAddress: account.address,
107
+ chain,
108
+ });
109
+
110
+ return deployViaAutoFactory({
111
+ client,
112
+ chain,
113
+ account,
114
+ cloneFactoryContract,
115
+ initializeTransaction,
116
+ });
117
+ }
118
+
119
+ async function getInitializeTransaction(options: {
120
+ client: ThirdwebClient;
121
+ implementationContract: ThirdwebContract;
122
+ params: VoteContractParams;
123
+ accountAddress: string;
124
+ chain: Chain;
125
+ }) {
126
+ const { client, implementationContract, params, chain } = options;
127
+ const {
128
+ name,
129
+ tokenAddress,
130
+ initialProposalThreshold,
131
+ minVoteQuorumRequiredPercent,
132
+ initialVotingDelay,
133
+ initialVotingPeriod,
134
+ description,
135
+ symbol,
136
+ image,
137
+ external_link,
138
+ social_urls,
139
+ } = params;
140
+ const tokenErc20Contract = getContract({
141
+ address: tokenAddress,
142
+ client,
143
+ chain,
144
+ });
145
+
146
+ /**
147
+ * A good side effect for checking for token decimals (instead of just taking in value in wei)
148
+ * is that it validates the token address that user entered. In case they enter an invalid ERC20 contract address,
149
+ * the extension will throw.
150
+ */
151
+ const _decimals = await decimals({ contract: tokenErc20Contract });
152
+ if (!_decimals) {
153
+ throw new Error(`Could not fetch decimals for contract: ${tokenAddress}`);
154
+ }
155
+ const [{ toUnits }, { upload }, { initialize }] = await Promise.all([
156
+ import("../../utils/units.js"),
157
+ import("../../storage/upload.js"),
158
+ import("./__generated__/VoteERC20/write/initialize.js"),
159
+ ]);
160
+ const initialProposalThresholdInWei = toUnits(
161
+ String(initialProposalThreshold),
162
+ _decimals,
163
+ );
164
+ const contractURI =
165
+ params.contractURI ||
166
+ (await upload({
167
+ client,
168
+ files: [
169
+ {
170
+ name,
171
+ description,
172
+ symbol,
173
+ image,
174
+ external_link,
175
+ social_urls,
176
+ },
177
+ ],
178
+ })) ||
179
+ "";
180
+
181
+ // Validate initialVoteQuorumFraction
182
+ const _num = Number(minVoteQuorumRequiredPercent);
183
+ if (Number.isNaN(_num)) {
184
+ throw new Error(
185
+ `${minVoteQuorumRequiredPercent} is not a valid minVoteQuorumRequiredPercent`,
186
+ );
187
+ }
188
+ if (_num < 0 || _num > 100) {
189
+ throw new Error("minVoteQuorumRequiredPercent must be >= 0 and <= 100");
190
+ }
191
+
192
+ // Make sure if user is passing a float, it should only have 2 digit after the decimal point
193
+ if (!Number.isInteger(_num)) {
194
+ throw new Error(
195
+ `${_num} is an invalid value. Only integer-like values accepted`,
196
+ );
197
+ }
198
+
199
+ const initialVoteQuorumFraction = BigInt(_num);
200
+
201
+ return initialize({
202
+ contract: implementationContract,
203
+ name,
204
+ token: tokenAddress,
205
+ // Make sure the final value passed to `initialProposalThreshold` is in wei
206
+ initialProposalThreshold: initialProposalThresholdInWei,
207
+ initialVoteQuorumFraction,
208
+ initialVotingDelay: BigInt(initialVotingDelay || 0),
209
+ initialVotingPeriod: BigInt(initialVotingPeriod),
210
+ contractURI,
211
+ trustedForwarders: params.trustedForwarders || [],
212
+ });
213
+ }
@@ -0,0 +1,123 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { ANVIL_CHAIN } from "~test/chains.js";
3
+ import { TEST_CONTRACT_URI } from "~test/ipfs-uris.js";
4
+ import { TEST_CLIENT } from "~test/test-clients.js";
5
+ import { TEST_ACCOUNT_A } from "~test/test-wallets.js";
6
+ import { getContract } from "../../../contract/contract.js";
7
+ import { delegate } from "../../../extensions/erc20/__generated__/IVotes/write/delegate.js";
8
+ import { mintTo } from "../../../extensions/erc20/write/mintTo.js";
9
+ import { deployERC20Contract } from "../../../extensions/prebuilts/deploy-erc20.js";
10
+ import { deployVoteContract } from "../../../extensions/prebuilts/deploy-vote.js";
11
+ import { sendAndConfirmTransaction } from "../../../transaction/actions/send-and-confirm-transaction.js";
12
+ import { propose } from "../__generated__/Vote/write/propose.js";
13
+ import { getAll } from "./getAll.js";
14
+ import { proposalExists } from "./proposalExists.js";
15
+
16
+ const account = TEST_ACCOUNT_A;
17
+ const client = TEST_CLIENT;
18
+ const chain = ANVIL_CHAIN;
19
+
20
+ describe.runIf(process.env.TW_SECRET_KEY)("proposal exists", () => {
21
+ it("should return false if Vote doesn't have any proposal", async () => {
22
+ const tokenAddress = await deployERC20Contract({
23
+ client: TEST_CLIENT,
24
+ chain: ANVIL_CHAIN,
25
+ account: TEST_ACCOUNT_A,
26
+ type: "TokenERC20",
27
+ params: {
28
+ name: "Token",
29
+ contractURI: TEST_CONTRACT_URI,
30
+ },
31
+ });
32
+ const address = await deployVoteContract({
33
+ account: TEST_ACCOUNT_A,
34
+ client: TEST_CLIENT,
35
+ chain: ANVIL_CHAIN,
36
+ params: {
37
+ name: "",
38
+ contractURI: TEST_CONTRACT_URI,
39
+ tokenAddress: tokenAddress,
40
+ initialProposalThreshold: "0.5",
41
+ initialVotingPeriod: 10,
42
+ minVoteQuorumRequiredPercent: 51,
43
+ },
44
+ });
45
+
46
+ const contract = getContract({
47
+ address,
48
+ chain,
49
+ client,
50
+ });
51
+
52
+ const result = await proposalExists({ contract, proposalId: 0n });
53
+ expect(result).toBe(false);
54
+ });
55
+
56
+ it("should return true if Vote has the proposal (id)", async () => {
57
+ const tokenAddress = await deployERC20Contract({
58
+ client: TEST_CLIENT,
59
+ chain: ANVIL_CHAIN,
60
+ account: TEST_ACCOUNT_A,
61
+ type: "TokenERC20",
62
+ params: {
63
+ name: "Token",
64
+ contractURI: TEST_CONTRACT_URI,
65
+ },
66
+ });
67
+ const address = await deployVoteContract({
68
+ account: TEST_ACCOUNT_A,
69
+ client: TEST_CLIENT,
70
+ chain: ANVIL_CHAIN,
71
+ params: {
72
+ name: "",
73
+ contractURI: TEST_CONTRACT_URI,
74
+ tokenAddress: tokenAddress,
75
+ initialProposalThreshold: "0.5",
76
+ initialVotingPeriod: 10,
77
+ minVoteQuorumRequiredPercent: 51,
78
+ },
79
+ });
80
+
81
+ const contract = getContract({
82
+ address,
83
+ chain,
84
+ client,
85
+ });
86
+
87
+ const tokenContract = getContract({
88
+ address: tokenAddress,
89
+ chain,
90
+ client,
91
+ });
92
+ // first step: mint enough tokens so it passes the voting threshold
93
+ const mintTransaction = mintTo({
94
+ contract: tokenContract,
95
+ to: account.address,
96
+ amount: "1000",
97
+ });
98
+ await sendAndConfirmTransaction({ transaction: mintTransaction, account });
99
+ // 2nd step: to delegate the token
100
+ const delegation = delegate({
101
+ contract: tokenContract,
102
+ delegatee: account.address,
103
+ });
104
+ await sendAndConfirmTransaction({ transaction: delegation, account });
105
+
106
+ // step 3: create a proposal
107
+ const transaction = propose({
108
+ contract,
109
+ description: "first proposal",
110
+ targets: [contract.address],
111
+ values: [0n],
112
+ calldatas: ["0x"],
113
+ });
114
+ await sendAndConfirmTransaction({ transaction, account });
115
+ const allProposals = await getAll({ contract });
116
+ expect(allProposals.length).toBe(1);
117
+ const result = await proposalExists({
118
+ contract,
119
+ proposalId: allProposals[0]?.proposalId || -1n,
120
+ });
121
+ expect(result).toBe(true);
122
+ });
123
+ });
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const version = "5.48.2";
1
+ export const version = "5.48.3-nightly-b661ce7b2fd58e7ff01faf47cf8fb3bb232841ec-20240827000337";