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.
- package/dist/cjs/exports/wallets/smart.js +4 -2
- package/dist/cjs/exports/wallets/smart.js.map +1 -1
- package/dist/cjs/extensions/erc1155/drops/write/claimTo.js.map +1 -1
- package/dist/cjs/extensions/erc4337/__generated__/IEntryPoint_v07/events/PostOpRevertReason.js +32 -0
- package/dist/cjs/extensions/erc4337/__generated__/IEntryPoint_v07/events/PostOpRevertReason.js.map +1 -0
- package/dist/cjs/extensions/erc4337/__generated__/IEntryPoint_v07/read/getUserOpHash.js +153 -0
- package/dist/cjs/extensions/erc4337/__generated__/IEntryPoint_v07/read/getUserOpHash.js.map +1 -0
- package/dist/cjs/extensions/prebuilts/__generated__/VoteERC20/write/initialize.js +184 -0
- package/dist/cjs/extensions/prebuilts/__generated__/VoteERC20/write/initialize.js.map +1 -0
- package/dist/cjs/extensions/prebuilts/deploy-vote.js +122 -0
- package/dist/cjs/extensions/prebuilts/deploy-vote.js.map +1 -0
- package/dist/cjs/version.js +1 -1
- package/dist/cjs/version.js.map +1 -1
- package/dist/cjs/wallets/smart/index.js +13 -2
- package/dist/cjs/wallets/smart/index.js.map +1 -1
- package/dist/cjs/wallets/smart/lib/bundler.js +11 -2
- package/dist/cjs/wallets/smart/lib/bundler.js.map +1 -1
- package/dist/cjs/wallets/smart/lib/constants.js +27 -13
- package/dist/cjs/wallets/smart/lib/constants.js.map +1 -1
- package/dist/cjs/wallets/smart/lib/packUserOp.js +75 -0
- package/dist/cjs/wallets/smart/lib/packUserOp.js.map +1 -0
- package/dist/cjs/wallets/smart/lib/paymaster.js +11 -1
- package/dist/cjs/wallets/smart/lib/paymaster.js.map +1 -1
- package/dist/cjs/wallets/smart/lib/userop.js +201 -66
- package/dist/cjs/wallets/smart/lib/userop.js.map +1 -1
- package/dist/cjs/wallets/smart/lib/utils.js +1 -1
- package/dist/cjs/wallets/smart/lib/utils.js.map +1 -1
- package/dist/cjs/wallets/smart/smart-wallet.js +2 -1
- package/dist/cjs/wallets/smart/smart-wallet.js.map +1 -1
- package/dist/cjs/wallets/smart/types.js.map +1 -1
- package/dist/esm/exports/wallets/smart.js +1 -1
- package/dist/esm/exports/wallets/smart.js.map +1 -1
- package/dist/esm/extensions/erc1155/drops/write/claimTo.js.map +1 -1
- package/dist/esm/extensions/erc4337/__generated__/IEntryPoint_v07/events/PostOpRevertReason.js +29 -0
- package/dist/esm/extensions/erc4337/__generated__/IEntryPoint_v07/events/PostOpRevertReason.js.map +1 -0
- package/dist/esm/extensions/erc4337/__generated__/IEntryPoint_v07/read/getUserOpHash.js +145 -0
- package/dist/esm/extensions/erc4337/__generated__/IEntryPoint_v07/read/getUserOpHash.js.map +1 -0
- package/dist/esm/extensions/prebuilts/__generated__/VoteERC20/write/initialize.js +177 -0
- package/dist/esm/extensions/prebuilts/__generated__/VoteERC20/write/initialize.js.map +1 -0
- package/dist/esm/extensions/prebuilts/deploy-vote.js +119 -0
- package/dist/esm/extensions/prebuilts/deploy-vote.js.map +1 -0
- package/dist/esm/version.js +1 -1
- package/dist/esm/version.js.map +1 -1
- package/dist/esm/wallets/smart/index.js +14 -3
- package/dist/esm/wallets/smart/index.js.map +1 -1
- package/dist/esm/wallets/smart/lib/bundler.js +12 -3
- package/dist/esm/wallets/smart/lib/bundler.js.map +1 -1
- package/dist/esm/wallets/smart/lib/constants.js +23 -10
- package/dist/esm/wallets/smart/lib/constants.js.map +1 -1
- package/dist/esm/wallets/smart/lib/packUserOp.js +65 -0
- package/dist/esm/wallets/smart/lib/packUserOp.js.map +1 -0
- package/dist/esm/wallets/smart/lib/paymaster.js +12 -2
- package/dist/esm/wallets/smart/lib/paymaster.js.map +1 -1
- package/dist/esm/wallets/smart/lib/userop.js +202 -67
- package/dist/esm/wallets/smart/lib/userop.js.map +1 -1
- package/dist/esm/wallets/smart/lib/utils.js +1 -1
- package/dist/esm/wallets/smart/lib/utils.js.map +1 -1
- package/dist/esm/wallets/smart/smart-wallet.js +3 -2
- package/dist/esm/wallets/smart/smart-wallet.js.map +1 -1
- package/dist/esm/wallets/smart/types.js.map +1 -1
- package/dist/types/exports/wallets/smart.d.ts +2 -2
- package/dist/types/exports/wallets/smart.d.ts.map +1 -1
- package/dist/types/extensions/erc1155/drops/write/claimTo.d.ts +2 -3
- package/dist/types/extensions/erc1155/drops/write/claimTo.d.ts.map +1 -1
- package/dist/types/extensions/erc4337/__generated__/IEntryPoint_v07/events/PostOpRevertReason.d.ts +57 -0
- package/dist/types/extensions/erc4337/__generated__/IEntryPoint_v07/events/PostOpRevertReason.d.ts.map +1 -0
- package/dist/types/extensions/erc4337/__generated__/IEntryPoint_v07/read/getUserOpHash.d.ts +123 -0
- package/dist/types/extensions/erc4337/__generated__/IEntryPoint_v07/read/getUserOpHash.d.ts.map +1 -0
- package/dist/types/extensions/prebuilts/__generated__/VoteERC20/write/initialize.d.ts +129 -0
- package/dist/types/extensions/prebuilts/__generated__/VoteERC20/write/initialize.d.ts.map +1 -0
- package/dist/types/extensions/prebuilts/deploy-vote.d.ts +82 -0
- package/dist/types/extensions/prebuilts/deploy-vote.d.ts.map +1 -0
- package/dist/types/version.d.ts +1 -1
- package/dist/types/version.d.ts.map +1 -1
- package/dist/types/wallets/smart/index.d.ts.map +1 -1
- package/dist/types/wallets/smart/lib/bundler.d.ts +3 -3
- package/dist/types/wallets/smart/lib/bundler.d.ts.map +1 -1
- package/dist/types/wallets/smart/lib/constants.d.ts +6 -6
- package/dist/types/wallets/smart/lib/constants.d.ts.map +1 -1
- package/dist/types/wallets/smart/lib/packUserOp.d.ts +16 -0
- package/dist/types/wallets/smart/lib/packUserOp.d.ts.map +1 -0
- package/dist/types/wallets/smart/lib/paymaster.d.ts +3 -3
- package/dist/types/wallets/smart/lib/paymaster.d.ts.map +1 -1
- package/dist/types/wallets/smart/lib/userop.d.ts +6 -4
- package/dist/types/wallets/smart/lib/userop.d.ts.map +1 -1
- package/dist/types/wallets/smart/lib/utils.d.ts +2 -2
- package/dist/types/wallets/smart/lib/utils.d.ts.map +1 -1
- package/dist/types/wallets/smart/smart-wallet.d.ts.map +1 -1
- package/dist/types/wallets/smart/types.d.ts +62 -9
- package/dist/types/wallets/smart/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/exports/wallets/smart.ts +4 -2
- package/src/extensions/erc1155/drops/write/claimTo.ts +2 -3
- package/src/extensions/erc4337/__generated__/IEntryPoint_v07/events/PostOpRevertReason.ts +49 -0
- package/src/extensions/erc4337/__generated__/IEntryPoint_v07/read/getUserOpHash.ts +181 -0
- package/src/extensions/prebuilts/__generated__/VoteERC20/write/initialize.ts +230 -0
- package/src/extensions/prebuilts/deploy-vote.test.ts +73 -0
- package/src/extensions/prebuilts/deploy-vote.ts +213 -0
- package/src/extensions/vote/read/proposalExists.test.ts +123 -0
- package/src/version.ts +1 -1
- package/src/wallets/smart/index.ts +21 -5
- package/src/wallets/smart/lib/bundler.ts +21 -5
- package/src/wallets/smart/lib/constants.ts +29 -10
- package/src/wallets/smart/lib/packUserOp.ts +79 -0
- package/src/wallets/smart/lib/paymaster.ts +22 -5
- package/src/wallets/smart/lib/userop.ts +292 -85
- package/src/wallets/smart/lib/utils.ts +11 -4
- package/src/wallets/smart/smart-wallet-integration-v07.test.ts +206 -0
- package/src/wallets/smart/smart-wallet-integration.test.ts +1 -1
- package/src/wallets/smart/smart-wallet-zksync.test.ts +1 -2
- package/src/wallets/smart/smart-wallet.ts +4 -2
- 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.
|
1
|
+
export const version = "5.48.3-nightly-b661ce7b2fd58e7ff01faf47cf8fb3bb232841ec-20240827000337";
|