@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.
- package/.env.example +11 -0
- package/.turbo/turbo-build.log +60 -0
- package/LICENSE +21 -0
- package/README.md +70 -0
- package/_imagine/Enjoy.sol +41 -0
- package/abis/AccessControlUpgradeable.json +250 -0
- package/abis/Address.json +29 -0
- package/abis/Comments.json +62 -0
- package/abis/CommentsDeployerBase.json +15 -0
- package/abis/CommentsImpl.json +1750 -0
- package/abis/CommentsPermitTest.json +847 -0
- package/abis/CommentsTest.json +986 -0
- package/abis/CommentsTestBase.json +577 -0
- package/abis/Comments_mintAndCommentTest.json +690 -0
- package/abis/ContextUpgradeable.json +25 -0
- package/abis/ContractVersionBase.json +15 -0
- package/abis/Create2.json +28 -0
- package/abis/DeployImpl.json +22 -0
- package/abis/DeployNonDeterministic.json +22 -0
- package/abis/DeployScript.json +22 -0
- package/abis/DeterministicDeployerAndCaller.json +315 -0
- package/abis/DeterministicUUPSProxyDeployer.json +167 -0
- package/abis/ECDSA.json +29 -0
- package/abis/EIP712.json +67 -0
- package/abis/EIP712UpgradeableWithChainId.json +25 -0
- package/abis/ERC1155.json +416 -0
- package/abis/ERC1155Holder.json +99 -0
- package/abis/ERC165.json +21 -0
- package/abis/ERC165Upgradeable.json +44 -0
- package/abis/ERC1967Proxy.json +67 -0
- package/abis/ERC1967Utils.json +85 -0
- package/abis/GenerateDeterministicParams.json +22 -0
- package/abis/IAccessControl.json +195 -0
- package/abis/IBeacon.json +15 -0
- package/abis/IComments.json +654 -0
- package/abis/IContractMetadata.json +28 -0
- package/abis/IERC1155.json +295 -0
- package/abis/IERC1155Errors.json +104 -0
- package/abis/IERC1155MetadataURI.json +314 -0
- package/abis/IERC1155Receiver.json +99 -0
- package/abis/IERC1271.json +26 -0
- package/abis/IERC165.json +21 -0
- package/abis/IERC1822Proxiable.json +15 -0
- package/abis/IERC20.json +224 -0
- package/abis/IERC20Errors.json +88 -0
- package/abis/IERC5267.json +51 -0
- package/abis/IERC721.json +287 -0
- package/abis/IERC721Enumerable.json +343 -0
- package/abis/IERC721Errors.json +105 -0
- package/abis/IERC721Metadata.json +332 -0
- package/abis/IERC721TokenReceiver.json +36 -0
- package/abis/IHasContractName.json +15 -0
- package/abis/IImmutableCreate2Factory.json +93 -0
- package/abis/IMulticall3.json +440 -0
- package/abis/IProtocolRewards.json +342 -0
- package/abis/ISafe.json +15 -0
- package/abis/ISymbol.json +15 -0
- package/abis/IVersionedContract.json +15 -0
- package/abis/IZoraCreator1155.json +343 -0
- package/abis/ImmutableCreate2FactoryUtils.json +15 -0
- package/abis/Initializable.json +25 -0
- package/abis/LibString.json +7 -0
- package/abis/Math.json +7 -0
- package/abis/Mock1155.json +547 -0
- package/abis/MockERC20.json +322 -0
- package/abis/MockERC721.json +350 -0
- package/abis/MockMinter.json +64 -0
- package/abis/OwnableUpgradeable.json +99 -0
- package/abis/ProtocolRewards.json +494 -0
- package/abis/Proxy.json +6 -0
- package/abis/ProxyDeployerScript.json +15 -0
- package/abis/ProxyShim.json +112 -0
- package/abis/Script.json +15 -0
- package/abis/ShortStrings.json +18 -0
- package/abis/StdAssertions.json +379 -0
- package/abis/StdInvariant.json +180 -0
- package/abis/Strings.json +18 -0
- package/abis/Test.json +570 -0
- package/abis/UUPSUpgradeable.json +130 -0
- package/abis/UnorderedNoncesUpgradeable.json +42 -0
- package/abis/Vm.json +8627 -0
- package/abis/VmSafe.json +7297 -0
- package/abis/stdError.json +119 -0
- package/abis/stdStorageSafe.json +52 -0
- package/addresses/999999999.json +4 -0
- package/deterministicConfig/comments.json +8 -0
- package/dist/index.cjs +935 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +908 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +4 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/wagmiGenerated.d.ts +1354 -0
- package/dist/wagmiGenerated.d.ts.map +1 -0
- package/foundry.toml +24 -0
- package/package/index.ts +4 -0
- package/package/types.ts +5 -0
- package/package/wagmiGenerated.ts +907 -0
- package/package.json +62 -0
- package/remappings.txt +8 -0
- package/script/CommentsDeployerBase.sol +60 -0
- package/script/Deploy.s.sol +66 -0
- package/script/DeployImpl.s.sol +26 -0
- package/script/DeployNonDeterministic.s.sol +43 -0
- package/script/GenerateDeterministicParams.s.sol +55 -0
- package/script/bundle-abis.ts +109 -0
- package/script/storage-check.sh +57 -0
- package/script/update-contract-version.ts +63 -0
- package/scripts/abis.ts +3 -0
- package/scripts/backfillComments.ts +176 -0
- package/scripts/generateCommentsTestData.ts +247 -0
- package/scripts/getCommentsAddresses.ts +10 -0
- package/scripts/queries.ts +73 -0
- package/scripts/queryAndSaveComments.ts +48 -0
- package/scripts/queryQuantityOfComments.ts +53 -0
- package/scripts/signDeployAndCall.ts +51 -0
- package/scripts/turnkey.ts +36 -0
- package/scripts/utils.ts +127 -0
- package/scripts/writeComments.ts +198 -0
- package/slither.config.json +7 -0
- package/src/CommentsImpl.sol +552 -0
- package/src/deployments/CommentsDeployment.sol +14 -0
- package/src/interfaces/IComments.sol +156 -0
- package/src/interfaces/IZoraCreator1155.sol +12 -0
- package/src/proxy/Comments.sol +43 -0
- package/src/utils/EIP712UpgradeableWithChainId.sol +36 -0
- package/src/version/ContractVersionBase.sol +14 -0
- package/test/Comments.t.sol +482 -0
- package/test/CommentsTestBase.sol +86 -0
- package/test/Comments_mintAndComment.t.sol +101 -0
- package/test/Comments_permit.t.sol +397 -0
- package/test/mocks/Mock1155.sol +50 -0
- package/test/mocks/MockMinter.sol +29 -0
- package/test/mocks/ProtocolRewards.sol +1497 -0
- package/tsconfig.build.json +10 -0
- package/tsconfig.json +9 -0
- package/tsup.config.ts +11 -0
- 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
|
+
};
|