@zoralabs/comments-contracts 0.0.1

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 (140) hide show
  1. package/.env.example +11 -0
  2. package/.turbo/turbo-build.log +60 -0
  3. package/LICENSE +21 -0
  4. package/README.md +70 -0
  5. package/_imagine/Enjoy.sol +41 -0
  6. package/abis/AccessControlUpgradeable.json +250 -0
  7. package/abis/Address.json +29 -0
  8. package/abis/Comments.json +62 -0
  9. package/abis/CommentsDeployerBase.json +15 -0
  10. package/abis/CommentsImpl.json +1750 -0
  11. package/abis/CommentsPermitTest.json +847 -0
  12. package/abis/CommentsTest.json +986 -0
  13. package/abis/CommentsTestBase.json +577 -0
  14. package/abis/Comments_mintAndCommentTest.json +690 -0
  15. package/abis/ContextUpgradeable.json +25 -0
  16. package/abis/ContractVersionBase.json +15 -0
  17. package/abis/Create2.json +28 -0
  18. package/abis/DeployImpl.json +22 -0
  19. package/abis/DeployNonDeterministic.json +22 -0
  20. package/abis/DeployScript.json +22 -0
  21. package/abis/DeterministicDeployerAndCaller.json +315 -0
  22. package/abis/DeterministicUUPSProxyDeployer.json +167 -0
  23. package/abis/ECDSA.json +29 -0
  24. package/abis/EIP712.json +67 -0
  25. package/abis/EIP712UpgradeableWithChainId.json +25 -0
  26. package/abis/ERC1155.json +416 -0
  27. package/abis/ERC1155Holder.json +99 -0
  28. package/abis/ERC165.json +21 -0
  29. package/abis/ERC165Upgradeable.json +44 -0
  30. package/abis/ERC1967Proxy.json +67 -0
  31. package/abis/ERC1967Utils.json +85 -0
  32. package/abis/GenerateDeterministicParams.json +22 -0
  33. package/abis/IAccessControl.json +195 -0
  34. package/abis/IBeacon.json +15 -0
  35. package/abis/IComments.json +654 -0
  36. package/abis/IContractMetadata.json +28 -0
  37. package/abis/IERC1155.json +295 -0
  38. package/abis/IERC1155Errors.json +104 -0
  39. package/abis/IERC1155MetadataURI.json +314 -0
  40. package/abis/IERC1155Receiver.json +99 -0
  41. package/abis/IERC1271.json +26 -0
  42. package/abis/IERC165.json +21 -0
  43. package/abis/IERC1822Proxiable.json +15 -0
  44. package/abis/IERC20.json +224 -0
  45. package/abis/IERC20Errors.json +88 -0
  46. package/abis/IERC5267.json +51 -0
  47. package/abis/IERC721.json +287 -0
  48. package/abis/IERC721Enumerable.json +343 -0
  49. package/abis/IERC721Errors.json +105 -0
  50. package/abis/IERC721Metadata.json +332 -0
  51. package/abis/IERC721TokenReceiver.json +36 -0
  52. package/abis/IHasContractName.json +15 -0
  53. package/abis/IImmutableCreate2Factory.json +93 -0
  54. package/abis/IMulticall3.json +440 -0
  55. package/abis/IProtocolRewards.json +342 -0
  56. package/abis/ISafe.json +15 -0
  57. package/abis/ISymbol.json +15 -0
  58. package/abis/IVersionedContract.json +15 -0
  59. package/abis/IZoraCreator1155.json +343 -0
  60. package/abis/ImmutableCreate2FactoryUtils.json +15 -0
  61. package/abis/Initializable.json +25 -0
  62. package/abis/LibString.json +7 -0
  63. package/abis/Math.json +7 -0
  64. package/abis/Mock1155.json +547 -0
  65. package/abis/MockERC20.json +322 -0
  66. package/abis/MockERC721.json +350 -0
  67. package/abis/MockMinter.json +64 -0
  68. package/abis/OwnableUpgradeable.json +99 -0
  69. package/abis/ProtocolRewards.json +494 -0
  70. package/abis/Proxy.json +6 -0
  71. package/abis/ProxyDeployerScript.json +15 -0
  72. package/abis/ProxyShim.json +112 -0
  73. package/abis/Script.json +15 -0
  74. package/abis/ShortStrings.json +18 -0
  75. package/abis/StdAssertions.json +379 -0
  76. package/abis/StdInvariant.json +180 -0
  77. package/abis/Strings.json +18 -0
  78. package/abis/Test.json +570 -0
  79. package/abis/UUPSUpgradeable.json +130 -0
  80. package/abis/UnorderedNoncesUpgradeable.json +42 -0
  81. package/abis/Vm.json +8627 -0
  82. package/abis/VmSafe.json +7297 -0
  83. package/abis/stdError.json +119 -0
  84. package/abis/stdStorageSafe.json +52 -0
  85. package/addresses/999999999.json +4 -0
  86. package/deterministicConfig/comments.json +8 -0
  87. package/dist/index.cjs +935 -0
  88. package/dist/index.cjs.map +1 -0
  89. package/dist/index.d.ts +2 -0
  90. package/dist/index.d.ts.map +1 -0
  91. package/dist/index.js +908 -0
  92. package/dist/index.js.map +1 -0
  93. package/dist/types.d.ts +4 -0
  94. package/dist/types.d.ts.map +1 -0
  95. package/dist/wagmiGenerated.d.ts +1354 -0
  96. package/dist/wagmiGenerated.d.ts.map +1 -0
  97. package/foundry.toml +24 -0
  98. package/package/index.ts +4 -0
  99. package/package/types.ts +5 -0
  100. package/package/wagmiGenerated.ts +907 -0
  101. package/package.json +62 -0
  102. package/remappings.txt +8 -0
  103. package/script/CommentsDeployerBase.sol +60 -0
  104. package/script/Deploy.s.sol +66 -0
  105. package/script/DeployImpl.s.sol +26 -0
  106. package/script/DeployNonDeterministic.s.sol +43 -0
  107. package/script/GenerateDeterministicParams.s.sol +55 -0
  108. package/script/bundle-abis.ts +109 -0
  109. package/script/storage-check.sh +57 -0
  110. package/script/update-contract-version.ts +63 -0
  111. package/scripts/abis.ts +3 -0
  112. package/scripts/backfillComments.ts +176 -0
  113. package/scripts/generateCommentsTestData.ts +247 -0
  114. package/scripts/getCommentsAddresses.ts +10 -0
  115. package/scripts/queries.ts +73 -0
  116. package/scripts/queryAndSaveComments.ts +48 -0
  117. package/scripts/queryQuantityOfComments.ts +53 -0
  118. package/scripts/signDeployAndCall.ts +51 -0
  119. package/scripts/turnkey.ts +36 -0
  120. package/scripts/utils.ts +127 -0
  121. package/scripts/writeComments.ts +198 -0
  122. package/slither.config.json +7 -0
  123. package/src/CommentsImpl.sol +552 -0
  124. package/src/deployments/CommentsDeployment.sol +14 -0
  125. package/src/interfaces/IComments.sol +156 -0
  126. package/src/interfaces/IZoraCreator1155.sol +12 -0
  127. package/src/proxy/Comments.sol +43 -0
  128. package/src/utils/EIP712UpgradeableWithChainId.sol +36 -0
  129. package/src/version/ContractVersionBase.sol +14 -0
  130. package/test/Comments.t.sol +482 -0
  131. package/test/CommentsTestBase.sol +86 -0
  132. package/test/Comments_mintAndComment.t.sol +101 -0
  133. package/test/Comments_permit.t.sol +397 -0
  134. package/test/mocks/Mock1155.sol +50 -0
  135. package/test/mocks/MockMinter.sol +29 -0
  136. package/test/mocks/ProtocolRewards.sol +1497 -0
  137. package/tsconfig.build.json +10 -0
  138. package/tsconfig.json +9 -0
  139. package/tsup.config.ts +11 -0
  140. package/wagmi.config.ts +14 -0
@@ -0,0 +1,176 @@
1
+ import { WalletClient, PublicClient, Transport, Chain, Account } from "viem";
2
+ import { getChainNamePositionalArg, getChainConfig } from "./utils";
3
+ import { queryCommentsPage, MintComment } from "./queries";
4
+ import {
5
+ writeCommentsToContract,
6
+ getBackfillerAccount,
7
+ filterCommentsThatAreAlreadyOnChain,
8
+ } from "./writeComments";
9
+
10
+ const queryAndWriteCommentsOfPage = async ({
11
+ subgraph,
12
+ pageSize,
13
+ page,
14
+ walletClient,
15
+ publicClient,
16
+ chainId,
17
+ backfiller,
18
+ }: {
19
+ subgraph: string;
20
+ pageSize: number;
21
+ page: number;
22
+ walletClient: WalletClient<Transport, Chain>;
23
+
24
+ publicClient: PublicClient;
25
+ chainId: number;
26
+ backfiller: Account;
27
+ }): Promise<boolean> => {
28
+ // query and page through each batch of comments
29
+ // for each page, write to the comments contract the comment
30
+ // if there is an empty array, return
31
+
32
+ const pagedComments = await queryCommentsPage({
33
+ subgraph,
34
+ page,
35
+ pageSize,
36
+ skip: 0,
37
+ });
38
+
39
+ if (pagedComments.length === 0) {
40
+ console.log("no comments returned from query");
41
+ return false;
42
+ }
43
+
44
+ const filteredComments = await filterCommentsThatAreAlreadyOnChain({
45
+ publicClient,
46
+ chainId,
47
+ comments: pagedComments,
48
+ });
49
+
50
+ if (filteredComments.length === 0) {
51
+ console.log("no comments returned that are not already on chain");
52
+ return true;
53
+ }
54
+
55
+ console.log({
56
+ pagedComments: pagedComments.length,
57
+ filteredComments: filteredComments.length,
58
+ });
59
+
60
+ const processCommentsInChunks = async (
61
+ filteredComments: MintComment[],
62
+ initialChunkSize: number,
63
+ walletClient: WalletClient<Transport, Chain>,
64
+ publicClient: PublicClient,
65
+ chainId: number,
66
+ ) => {
67
+ let chunkSize = initialChunkSize;
68
+
69
+ while (chunkSize > 0) {
70
+ try {
71
+ const chunks: MintComment[][] = [];
72
+ for (let i = 0; i < filteredComments.length; i += chunkSize) {
73
+ chunks.push([...filteredComments].slice(i, i + chunkSize));
74
+ }
75
+
76
+ console.log(`processing ${chunks.length} chunks of size ${chunkSize}`);
77
+
78
+ for (let index = 0; index < chunks.length; index++) {
79
+ const chunk = chunks[index];
80
+ if (chunk.length > 0) {
81
+ console.log(
82
+ `writing chunk ${index + 1} of ${chunks.length}, size ${chunk.length}`,
83
+ );
84
+ await writeCommentsToContract({
85
+ comments: chunk,
86
+ walletClient,
87
+ publicClient,
88
+ chainId,
89
+ account: backfiller,
90
+ write: true,
91
+ });
92
+ }
93
+ }
94
+
95
+ // If we've made it here without errors, we're done
96
+ return;
97
+ } catch (error) {
98
+ console.log(error);
99
+ console.error(
100
+ `failed with chunk size ${chunkSize}. Retrying with smaller chunks.`,
101
+ );
102
+ chunkSize -= 100;
103
+ }
104
+ }
105
+
106
+ console.error("failed to process comments even with minimum chunk size.");
107
+ };
108
+
109
+ await processCommentsInChunks(
110
+ filteredComments,
111
+ 700,
112
+ walletClient,
113
+ publicClient,
114
+ chainId,
115
+ );
116
+
117
+ return true;
118
+ };
119
+
120
+ const queryAndWriteComments = async ({
121
+ subgraph,
122
+ pageSize,
123
+ walletClient,
124
+ publicClient,
125
+ chainId,
126
+ }: {
127
+ subgraph: string;
128
+ pageSize: number;
129
+ walletClient: WalletClient<Transport, Chain>;
130
+ publicClient: PublicClient;
131
+ chainId: number;
132
+ }) => {
133
+ let page = 0;
134
+
135
+ const backfiller = getBackfillerAccount();
136
+
137
+ while (true) {
138
+ const gotSomeComments = await queryAndWriteCommentsOfPage({
139
+ subgraph,
140
+ pageSize,
141
+ walletClient,
142
+ publicClient,
143
+ chainId,
144
+ backfiller,
145
+ page,
146
+ });
147
+
148
+ if (!gotSomeComments) {
149
+ console.log("done querying comments");
150
+ break;
151
+ }
152
+
153
+ page += 1;
154
+ }
155
+ };
156
+
157
+ const backfillComments = async (chainName: string) => {
158
+ const { publicClient, walletClient, chainId, subgraph } =
159
+ await getChainConfig(chainName);
160
+
161
+ const pageSize = 1000;
162
+
163
+ await queryAndWriteComments({
164
+ subgraph,
165
+ pageSize,
166
+ walletClient,
167
+ publicClient,
168
+ chainId,
169
+ });
170
+ };
171
+
172
+ export const main = async () => {
173
+ await backfillComments(getChainNamePositionalArg());
174
+ };
175
+
176
+ main();
@@ -0,0 +1,247 @@
1
+ import {
2
+ parseEther,
3
+ PublicClient,
4
+ Address,
5
+ WalletClient,
6
+ Hex,
7
+ Chain,
8
+ Transport,
9
+ numberToHex,
10
+ zeroAddress,
11
+ keccak256,
12
+ Account,
13
+ } from "viem";
14
+ import { zoraCreator1155ImplABI } from "@zoralabs/zora-1155-contracts";
15
+ import { privateKeyToAccount } from "viem/accounts";
16
+ import { zoraTimedSaleStrategyImplABI } from "./abis";
17
+ import { zoraSepolia } from "viem/chains";
18
+ import { CommentIdentifier } from "../package/types";
19
+ import { getCommentsAddress } from "./getCommentsAddresses";
20
+ import { commentsImplABI } from "../package/wagmiGenerated";
21
+ import { getChainConfig } from "./utils";
22
+
23
+ const MINT_FEE = parseEther("0.000111");
24
+ const SPARK_VALUE = parseEther("0.000001");
25
+ const TEST_1155_CONTRACT = "0xD42557F24034b53e7340A40bb5813eF9Ba88F2b4";
26
+ const TEST_TOKEN_ID = 3n;
27
+ const ZORA_TIMED_SALE_STRATEGY = "0x777777722D078c97c6ad07d9f36801e653E356Ae";
28
+ const GAS_FEE = parseEther("0.000001");
29
+
30
+ const getAccountFromEnv = (keyName: string) => {
31
+ const privateKey = process.env[keyName] as Address;
32
+ if (!privateKey) {
33
+ throw new Error(`${keyName} not found in environment`);
34
+ }
35
+ return privateKeyToAccount(privateKey);
36
+ };
37
+
38
+ const waitForReceiptAndEnsureSuccess = async (
39
+ hash: Hex,
40
+ publicClient: PublicClient<Transport, Chain>,
41
+ ) => {
42
+ const receipt = await publicClient.waitForTransactionReceipt({ hash });
43
+ if (receipt.status !== "success") {
44
+ throw new Error("Transaction failed");
45
+ }
46
+ };
47
+
48
+ const generateTestComments = async (
49
+ walletClient: WalletClient<Transport, Chain>,
50
+ publicClient: PublicClient<Transport, Chain>,
51
+ commentsAddress: Address,
52
+ account: Account,
53
+ commentor: Account,
54
+ commentIdentifier: CommentIdentifier,
55
+ replyCommentIdentifier: CommentIdentifier,
56
+ ) => {
57
+ // check if commentor has a mint on 1155
58
+ const balance = await publicClient.readContract({
59
+ abi: zoraCreator1155ImplABI,
60
+ address: TEST_1155_CONTRACT,
61
+ functionName: "balanceOf",
62
+ args: [commentor.address, TEST_TOKEN_ID],
63
+ });
64
+
65
+ let tx: Hex;
66
+
67
+ if (balance === 0n) {
68
+ console.log("minting token to commentor");
69
+
70
+ tx = await walletClient.writeContract({
71
+ abi: zoraTimedSaleStrategyImplABI,
72
+ address: ZORA_TIMED_SALE_STRATEGY,
73
+ functionName: "mint",
74
+ account,
75
+ args: [
76
+ commentor.address,
77
+ 1n,
78
+ TEST_1155_CONTRACT,
79
+ TEST_TOKEN_ID,
80
+ zeroAddress,
81
+ "",
82
+ ],
83
+ value: MINT_FEE,
84
+ });
85
+
86
+ await waitForReceiptAndEnsureSuccess(tx, publicClient);
87
+ }
88
+
89
+ const emptyCommentIdentifier: CommentIdentifier = {
90
+ commenter: zeroAddress,
91
+ contractAddress: zeroAddress,
92
+ tokenId: 0n,
93
+ nonce: keccak256(numberToHex(0)),
94
+ } as const;
95
+
96
+ console.log("sending 2 sparks worth of eth to commenter");
97
+
98
+ // send 2 sparks worth of eth to commenter
99
+ tx = await walletClient.sendTransaction({
100
+ account,
101
+ to: commentor.address,
102
+ value: (SPARK_VALUE + GAS_FEE) * 2n,
103
+ });
104
+ await waitForReceiptAndEnsureSuccess(tx, publicClient);
105
+
106
+ console.log("commenting");
107
+
108
+ tx = await walletClient.writeContract({
109
+ abi: commentsImplABI,
110
+ address: commentsAddress,
111
+ functionName: "comment",
112
+ account: commentor,
113
+ args: [
114
+ commentIdentifier,
115
+ "This is a test comment",
116
+ 1n,
117
+ emptyCommentIdentifier,
118
+ zeroAddress,
119
+ ],
120
+ value: SPARK_VALUE,
121
+ });
122
+
123
+ await waitForReceiptAndEnsureSuccess(tx, publicClient);
124
+
125
+ console.log("replying to comment");
126
+
127
+ tx = await walletClient.writeContract({
128
+ abi: commentsImplABI,
129
+ address: commentsAddress,
130
+ functionName: "comment",
131
+ account: commentor,
132
+ args: [
133
+ replyCommentIdentifier,
134
+ "This is a test reply",
135
+ 1n,
136
+ commentIdentifier,
137
+ zeroAddress,
138
+ ],
139
+ value: SPARK_VALUE,
140
+ });
141
+
142
+ await waitForReceiptAndEnsureSuccess(tx, publicClient);
143
+ };
144
+
145
+ const sparkComment = async ({
146
+ walletClient,
147
+ publicClient,
148
+ commentsAddress,
149
+ account,
150
+ sparker,
151
+ commentIdentifier,
152
+ }: {
153
+ walletClient: WalletClient<Transport, Chain>;
154
+ publicClient: PublicClient<Transport, Chain>;
155
+ commentsAddress: Address;
156
+ account: Account;
157
+ sparker: Account;
158
+ commentIdentifier: CommentIdentifier;
159
+ }) => {
160
+ console.log("sending sparks worth of eth to sparker + gas");
161
+ // send sparks worth of eth to sparker + gas
162
+ let tx = await walletClient.sendTransaction({
163
+ account,
164
+ to: sparker.address,
165
+ value: SPARK_VALUE + GAS_FEE,
166
+ });
167
+ await waitForReceiptAndEnsureSuccess(tx, publicClient);
168
+
169
+ console.log("sparking comment");
170
+
171
+ // spark the comment
172
+ tx = await walletClient.writeContract({
173
+ abi: commentsImplABI,
174
+ address: commentsAddress,
175
+ functionName: "sparkComment",
176
+ account: sparker,
177
+ args: [commentIdentifier, 1n, zeroAddress],
178
+ value: SPARK_VALUE,
179
+ });
180
+
181
+ await waitForReceiptAndEnsureSuccess(tx, publicClient);
182
+ };
183
+
184
+ export const generateCommentsTestData = async () => {
185
+ const { walletClient, publicClient } = await getChainConfig("zora-sepolia");
186
+
187
+ const commentsAddress = (await getCommentsAddress(zoraSepolia.id)).COMMENTS;
188
+
189
+ const account = getAccountFromEnv("PRIVATE_KEY");
190
+
191
+ const commentor = getAccountFromEnv("COMMENTOR_PRIVATE_KEY");
192
+ const sparker = getAccountFromEnv("SPARKER_PRIVATE_KEY");
193
+
194
+ // comment and reply to comment
195
+ const commentIdentifier: CommentIdentifier = {
196
+ commenter: commentor.address,
197
+ contractAddress: TEST_1155_CONTRACT,
198
+ tokenId: TEST_TOKEN_ID,
199
+ nonce: keccak256(numberToHex(1)),
200
+ };
201
+
202
+ const replyCommentIdentifier: CommentIdentifier = {
203
+ ...commentIdentifier,
204
+ nonce: keccak256(numberToHex(2)),
205
+ };
206
+
207
+ await generateTestComments(
208
+ walletClient,
209
+ publicClient,
210
+ commentsAddress,
211
+ account,
212
+ commentor,
213
+ commentIdentifier,
214
+ replyCommentIdentifier,
215
+ );
216
+
217
+ // spark the comment twice
218
+ await sparkComment({
219
+ walletClient,
220
+ publicClient,
221
+ commentsAddress,
222
+ account,
223
+ sparker,
224
+ commentIdentifier,
225
+ });
226
+
227
+ await sparkComment({
228
+ walletClient,
229
+ publicClient,
230
+ commentsAddress,
231
+ account,
232
+ sparker,
233
+ commentIdentifier,
234
+ });
235
+
236
+ // spark the reply comment
237
+ await sparkComment({
238
+ walletClient,
239
+ publicClient,
240
+ commentsAddress,
241
+ account,
242
+ sparker,
243
+ commentIdentifier: replyCommentIdentifier,
244
+ });
245
+ };
246
+
247
+ generateCommentsTestData();
@@ -0,0 +1,10 @@
1
+ import { Address } from "viem";
2
+ import { readFile } from "fs/promises";
3
+
4
+ export const getCommentsAddress = async (chainId: number) => {
5
+ const addresses = await readFile(`./addresses/${chainId}.json`, "utf8");
6
+
7
+ return JSON.parse(addresses) as {
8
+ COMMENTS: Address;
9
+ };
10
+ };
@@ -0,0 +1,73 @@
1
+ import { Address, PublicClient, Hex } from "viem";
2
+ import { commentsImplABI } from "../package/wagmiGenerated";
3
+
4
+ export type MintComment = {
5
+ tokenAndContract: {
6
+ address: Address;
7
+ tokenId: string;
8
+ };
9
+ sender: Address;
10
+ txn: {
11
+ id: Hex;
12
+ block: string;
13
+ logIndex: string;
14
+ };
15
+ timestamp: string;
16
+ comment: string;
17
+ };
18
+
19
+ export const queryCommentsPage = async ({
20
+ subgraph,
21
+ pageSize,
22
+ page,
23
+ skip = 0,
24
+ }: {
25
+ subgraph: string;
26
+ pageSize: number;
27
+ page: number;
28
+ skip?: number;
29
+ }) => {
30
+ const query = `
31
+ query MintComments {
32
+ mintComments(
33
+ first: ${pageSize},
34
+ skip: ${page * pageSize + skip},
35
+ orderBy: timestamp,
36
+ orderDirection: asc
37
+ ) {
38
+ tokenAndContract {
39
+ address
40
+ tokenId
41
+ }
42
+ sender
43
+ txn {
44
+ id
45
+ block
46
+ logIndex
47
+ }
48
+ timestamp
49
+ comment
50
+ }
51
+ }
52
+ `;
53
+
54
+ // post to subgraph url with query
55
+ const response = await fetch(subgraph, {
56
+ method: "POST",
57
+ headers: {
58
+ "Content-Type": "application/json",
59
+ },
60
+ body: JSON.stringify({ query }),
61
+ });
62
+
63
+ const responseJson = await response.json();
64
+
65
+ if (!responseJson.data) {
66
+ console.error("Errored querying graphql");
67
+ console.error(responseJson);
68
+ }
69
+
70
+ const mintComments = responseJson.data.mintComments;
71
+
72
+ return mintComments as MintComment[];
73
+ };
@@ -0,0 +1,48 @@
1
+ import { getChainNamePositionalArg } from "./utils";
2
+ import { getChainConfig } from "./utils";
3
+ import { queryCommentsPage } from "./queries";
4
+ import { saveComments } from "./savedCommentsDump";
5
+
6
+ export const queryAndSaveComments = async (chainName: string) => {
7
+ const { subgraph } = await getChainConfig(chainName);
8
+
9
+ const pageSize = 1000;
10
+ let page = 0;
11
+
12
+ // iterate over all pages until comments are empty
13
+ const batchSize = 20;
14
+ let hasMoreComments = true;
15
+
16
+ // Clear the file at the beginning
17
+ await saveComments(chainName, []);
18
+
19
+ while (hasMoreComments) {
20
+ const batchPromises = Array.from({ length: batchSize }, (_, i) =>
21
+ queryCommentsPage({
22
+ subgraph,
23
+ pageSize: pageSize,
24
+ page: page + i,
25
+ }),
26
+ );
27
+
28
+ const batchResults = await Promise.all(batchPromises);
29
+
30
+ for (let i = 0; i < batchResults.length; i++) {
31
+ const commentsPage = batchResults[i];
32
+ if (commentsPage.length > 0) {
33
+ console.log(`Queried page ${page}: ${commentsPage.length} comments`);
34
+ await saveComments(chainName, commentsPage, true); // Use append mode
35
+ page++;
36
+ } else {
37
+ hasMoreComments = false;
38
+ break;
39
+ }
40
+ }
41
+ }
42
+ };
43
+
44
+ export const main = async () => {
45
+ await queryAndSaveComments(getChainNamePositionalArg());
46
+ };
47
+
48
+ main();
@@ -0,0 +1,53 @@
1
+ import { getChainConfig } from "./utils";
2
+ import { queryCommentsPage } from "./queries";
3
+ import { getChainNamePositionalArg } from "./utils";
4
+
5
+ export const queryQuantityOfComments = async (chainName: string) => {
6
+ const { subgraph } = await getChainConfig(chainName);
7
+
8
+ // do a breadth first search to find the total number of comments;
9
+ // we want to find the lowest page that has comments, and from that page, we know
10
+ // the total number of comments
11
+
12
+ // midpoint is 500k, we go up to 1 million
13
+
14
+ const low = 100_000 / pageSize;
15
+ const high = 2_000_000 / pageSize;
16
+
17
+ const { page, quantity } = await search(low, high, subgraph);
18
+
19
+ console.log(`Found last page ${page} with ${quantity} comments`);
20
+
21
+ const totalComments = page * pageSize + quantity;
22
+
23
+ console.log(`Total comments: ${totalComments}`);
24
+ };
25
+
26
+ const pageSize = 1000;
27
+
28
+ // recursive binary search to find the last page of comments
29
+ const search = async (low: number, high: number, subgraph: string) => {
30
+ const midpoint = Math.floor(low + (high - low) / 2);
31
+ const commentsPage = await queryCommentsPage({
32
+ subgraph,
33
+ pageSize,
34
+ page: midpoint,
35
+ });
36
+ console.log(
37
+ `Found ${commentsPage.length} comments on page ${midpoint}, comments so far ${midpoint * pageSize}`,
38
+ );
39
+ if (commentsPage.length === 0) {
40
+ return await search(low, midpoint, subgraph);
41
+ ``;
42
+ }
43
+ if (commentsPage.length < pageSize) {
44
+ return { page: midpoint, quantity: commentsPage.length };
45
+ }
46
+ return await search(midpoint, high, subgraph);
47
+ };
48
+
49
+ const main = async () => {
50
+ await queryQuantityOfComments(getChainNamePositionalArg());
51
+ };
52
+
53
+ main();
@@ -0,0 +1,51 @@
1
+ import { Hex, Address } from "viem";
2
+ import { loadTurnkeyAccount } from "./turnkey";
3
+
4
+ const loadParameters = () => {
5
+ const [, , chainId, salt, creationCode, init, deployerAddress] = process.argv;
6
+
7
+ return {
8
+ chainId: +chainId,
9
+ salt: salt as Hex,
10
+ creationCode: creationCode as Hex,
11
+ init: init as Hex,
12
+ deployerAddress: deployerAddress as Address,
13
+ };
14
+ };
15
+
16
+ /// Deploy the mints manager and 1155 contract deteriministically using turnkey
17
+ async function main() {
18
+ const parameters = loadParameters();
19
+
20
+ const turnkeyAccount = await loadTurnkeyAccount();
21
+
22
+ // "create(bytes32 salt,bytes code,bytes postCreateCall,uint256 postCreateCallValue)");
23
+ const signature = await turnkeyAccount.signTypedData({
24
+ types: {
25
+ create: [
26
+ { name: "salt", type: "bytes32" },
27
+ { name: "code", type: "bytes" },
28
+ { name: "postCreateCall", type: "bytes" },
29
+ ],
30
+ },
31
+ primaryType: "create",
32
+ message: {
33
+ code: parameters.creationCode,
34
+ salt: parameters.salt,
35
+ postCreateCall: parameters.init,
36
+ },
37
+ domain: {
38
+ chainId: parameters.chainId,
39
+ name: "DeterministicDeployerAndCaller",
40
+ version: "1",
41
+ verifyingContract: parameters.deployerAddress,
42
+ },
43
+ });
44
+
45
+ console.log(signature);
46
+ }
47
+
48
+ main().catch((error) => {
49
+ console.error(error);
50
+ process.exit(1);
51
+ });
@@ -0,0 +1,36 @@
1
+ import { TurnkeyClient } from "@turnkey/http";
2
+ import { ApiKeyStamper } from "@turnkey/api-key-stamper";
3
+ import { createAccount } from "@turnkey/viem";
4
+
5
+ import { fileURLToPath } from "url";
6
+ import { dirname } from "path";
7
+ import * as path from "path";
8
+ import * as dotenv from "dotenv";
9
+
10
+ const __filename = fileURLToPath(import.meta.url);
11
+ const __dirname = dirname(__filename);
12
+
13
+ dotenv.config({ path: path.resolve(__dirname, "../.env") });
14
+
15
+ export const loadTurnkeyAccount = async () => {
16
+ const httpClient = new TurnkeyClient(
17
+ {
18
+ baseUrl: "https://api.turnkey.com",
19
+ },
20
+ // This uses API key credentials.
21
+ // If you're using passkeys, use `@turnkey/webauthn-stamper` to collect webauthn signatures:
22
+ new ApiKeyStamper({
23
+ apiPublicKey: process.env.TURNKEY_API_PUBLIC_KEY!,
24
+ apiPrivateKey: process.env.TURNKEY_API_PRIVATE_KEY!,
25
+ }),
26
+ );
27
+
28
+ // Create the Viem custom account
29
+ return await createAccount({
30
+ client: httpClient,
31
+ organizationId: process.env.TURNKEY_ORGANIZATION_ID!,
32
+ signWith: process.env.TURNKEY_PRIVATE_KEY_ID!,
33
+ // optional; will be fetched from Turnkey if not provided
34
+ ethereumAddress: process.env.TURNKEY_TARGET_ADDRESS!,
35
+ });
36
+ };