@zoralabs/comments-contracts 0.0.1 → 0.0.2-COMMENTS.0

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 (104) hide show
  1. package/.turbo/turbo-build.log +48 -30
  2. package/CHANGELOG.md +8 -0
  3. package/README.md +10 -39
  4. package/abis/AddDelegateCommenterRole.json +22 -0
  5. package/abis/CallerAndCommenter.json +62 -0
  6. package/abis/CallerAndCommenterImpl.json +1218 -0
  7. package/abis/CallerAndCommenterMintAndCommentTest.json +771 -0
  8. package/abis/CallerAndCommenterSwapAndCommentTest.json +844 -0
  9. package/abis/CallerAndCommenterTestBase.json +577 -0
  10. package/abis/CommentsImpl.json +189 -59
  11. package/abis/CommentsImplConstants.json +106 -0
  12. package/abis/CommentsPermitTest.json +26 -6
  13. package/abis/CommentsTest.json +58 -10
  14. package/abis/Comments_mintAndCommentTest.json +11 -4
  15. package/abis/Comments_smartWallet.json +711 -0
  16. package/abis/DeployCallerAndCommenterImpl.json +22 -0
  17. package/abis/EIP712Upgradeable.json +74 -0
  18. package/abis/EIP712UpgradeableWithChainId.json +49 -0
  19. package/abis/ERC20.json +310 -0
  20. package/abis/ICallerAndCommenter.json +797 -0
  21. package/abis/IComments.json +629 -9
  22. package/abis/IERC20.json +39 -42
  23. package/abis/IERC20Metadata.json +224 -0
  24. package/abis/IMultiOwnable.json +21 -0
  25. package/abis/IProtocolRewards.json +19 -0
  26. package/abis/ISecondarySwap.json +45 -0
  27. package/abis/IZoraCreator1155.json +51 -0
  28. package/abis/IZoraTimedSaleStrategy.json +91 -0
  29. package/abis/Mock1155.json +75 -1
  30. package/abis/Mock1155NoCreatorRewardRecipient.json +605 -0
  31. package/abis/Mock1155NoOwner.json +566 -0
  32. package/abis/{MockMinter.json → MockDelegateCommenter.json} +12 -2
  33. package/abis/MockERC20z.json +315 -0
  34. package/abis/MockMultiOwnable.json +212 -0
  35. package/abis/MockSecondarySwap.json +95 -0
  36. package/abis/MockZoraTimedSale.json +139 -0
  37. package/abis/Ownable2StepUpgradeable.json +138 -0
  38. package/abis/UnorderedNoncesUpgradeable.json +4 -4
  39. package/addresses/10.json +9 -0
  40. package/addresses/11155111.json +9 -0
  41. package/addresses/11155420.json +9 -0
  42. package/addresses/42161.json +9 -0
  43. package/addresses/7777777.json +9 -0
  44. package/addresses/8453.json +9 -0
  45. package/addresses/84532.json +9 -0
  46. package/addresses/999999999.json +7 -2
  47. package/deterministicConfig/callerAndCommenter.json +8 -0
  48. package/deterministicConfig/comments.json +2 -2
  49. package/dist/index.cjs +724 -35
  50. package/dist/index.cjs.map +1 -1
  51. package/dist/index.js +723 -35
  52. package/dist/index.js.map +1 -1
  53. package/dist/types.d.ts +1 -1
  54. package/dist/types.d.ts.map +1 -1
  55. package/dist/wagmiGenerated.d.ts +1102 -57
  56. package/dist/wagmiGenerated.d.ts.map +1 -1
  57. package/package/types.ts +4 -1
  58. package/package/wagmiGenerated.ts +728 -32
  59. package/package.json +11 -10
  60. package/script/AddDelegateCommenterRole.s.sol +24 -0
  61. package/script/CommentsDeployerBase.sol +101 -19
  62. package/script/Deploy.s.sol +2 -44
  63. package/script/DeployCallerAndCommenterImpl.s.sol +27 -0
  64. package/script/DeployImpl.s.sol +1 -0
  65. package/script/DeployNonDeterministic.s.sol +22 -13
  66. package/script/GenerateDeterministicParams.s.sol +32 -4
  67. package/scripts/generateCommentsTestData.ts +170 -79
  68. package/src/CommentsImpl.sol +267 -134
  69. package/src/CommentsImplConstants.sol +44 -0
  70. package/src/interfaces/ICallerAndCommenter.sol +215 -0
  71. package/src/interfaces/IComments.sol +189 -42
  72. package/src/interfaces/IMultiOwnable.sol +10 -0
  73. package/src/interfaces/ISecondarySwap.sol +40 -0
  74. package/src/interfaces/IZoraCreator1155.sol +6 -1
  75. package/src/interfaces/IZoraCreator1155TypesV1.sol +46 -0
  76. package/src/interfaces/IZoraTimedSaleStrategy.sol +25 -0
  77. package/src/proxy/CallerAndCommenter.sol +43 -0
  78. package/src/utils/CallerAndCommenterImpl.sol +376 -0
  79. package/src/utils/EIP712UpgradeableWithChainId.sol +12 -23
  80. package/src/version/ContractVersionBase.sol +1 -1
  81. package/test/CallerAndCommenterTestBase.sol +77 -0
  82. package/test/CallerAndCommenter_mintAndComment.t copy.sol +214 -0
  83. package/test/CallerAndCommenter_swapAndComment.t.sol +523 -0
  84. package/test/Comments.t.sol +166 -29
  85. package/test/CommentsTestBase.sol +12 -20
  86. package/test/Comments_delegateComment.t.sol +129 -0
  87. package/test/Comments_permit.t.sol +131 -44
  88. package/test/Comments_smartWallet.t.sol +152 -0
  89. package/test/mocks/Mock1155.sol +12 -1
  90. package/test/mocks/Mock1155NoCreatorRewardRecipient.sol +65 -0
  91. package/test/mocks/Mock1155NoOwner.sol +53 -0
  92. package/test/mocks/MockDelegateCommenter.sol +36 -0
  93. package/test/mocks/MockIZoraCreator1155.sol +16 -0
  94. package/test/mocks/MockSecondarySwap.sol +30 -0
  95. package/test/mocks/MockZoraTimedSale.sol +38 -0
  96. package/wagmi.config.ts +3 -1
  97. package/scripts/backfillComments.ts +0 -176
  98. package/scripts/queries.ts +0 -73
  99. package/scripts/queryAndSaveComments.ts +0 -48
  100. package/scripts/queryQuantityOfComments.ts +0 -53
  101. package/scripts/writeComments.ts +0 -198
  102. package/src/deployments/CommentsDeployment.sol +0 -14
  103. package/test/Comments_mintAndComment.t.sol +0 -101
  104. package/test/mocks/MockMinter.sol +0 -29
@@ -0,0 +1,523 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.20;
3
+
4
+ import {IComments} from "../src/interfaces/IComments.sol";
5
+ import {ICallerAndCommenter} from "../src/interfaces/ICallerAndCommenter.sol";
6
+ import {CallerAndCommenterTestBase} from "./CallerAndCommenterTestBase.sol";
7
+ import {ISecondarySwap} from "../src/interfaces/ISecondarySwap.sol";
8
+ import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
9
+ import {CallerAndCommenterImpl} from "../src/utils/CallerAndCommenterImpl.sol";
10
+ import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
11
+ import {IERC1155} from "@openzeppelin/contracts/interfaces/IERC1155.sol";
12
+ import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol";
13
+
14
+ contract CallerAndCommenterSwapAndCommentTest is CallerAndCommenterTestBase {
15
+ function testBuyOnSecondaryAndComment() public {
16
+ // setup the sale so that we have a link between the erc20z and the 1155
17
+ address erc20z = mockMinter.setSale(address(mock1155), tokenId1);
18
+
19
+ uint256 quantity = 5;
20
+
21
+ address excessRefundRecipient = makeAddr("excessRefundRecipient");
22
+ uint256 maxEthToSpend = 2 ether;
23
+ uint160 sqrtPriceLimitX96 = 1000;
24
+ string memory comment = "test comment";
25
+
26
+ IComments.CommentIdentifier memory expectedCommentIdentifier = _expectedCommentIdentifier(commenter, address(mock1155), tokenId1);
27
+ bytes32 expectedCommentId = comments.hashCommentIdentifier(expectedCommentIdentifier);
28
+
29
+ uint256 valueToSpend = 1 ether;
30
+ vm.deal(commenter, valueToSpend);
31
+
32
+ vm.expectEmit(true, true, true, true);
33
+ emit IComments.Commented(expectedCommentId, expectedCommentIdentifier, bytes32(0), emptyCommentIdentifier, 0, comment, block.timestamp, address(0));
34
+ vm.expectEmit(true, true, true, true);
35
+ emit ICallerAndCommenter.SwappedOnSecondaryAndCommented(
36
+ expectedCommentId,
37
+ expectedCommentIdentifier,
38
+ quantity,
39
+ comment,
40
+ ICallerAndCommenter.SwapDirection.BUY
41
+ );
42
+
43
+ vm.expectCall(
44
+ address(mockSecondarySwap),
45
+ valueToSpend,
46
+ abi.encodeWithSelector(ISecondarySwap.buy1155.selector, erc20z, quantity, commenter, excessRefundRecipient, maxEthToSpend, sqrtPriceLimitX96)
47
+ );
48
+
49
+ vm.prank(commenter);
50
+ callerAndCommenter.buyOnSecondaryAndComment{value: valueToSpend}({
51
+ commenter: commenter,
52
+ quantity: quantity,
53
+ collection: address(mock1155),
54
+ tokenId: tokenId1,
55
+ excessRefundRecipient: payable(excessRefundRecipient),
56
+ maxEthToSpend: maxEthToSpend,
57
+ sqrtPriceLimitX96: sqrtPriceLimitX96,
58
+ comment: comment
59
+ });
60
+ }
61
+
62
+ function testBuyOnSecondaryAndComment_revertsWhenSaleNotSet() public {
63
+ vm.expectRevert(abi.encodeWithSelector(ICallerAndCommenter.SaleNotSet.selector, address(mock1155), tokenId1));
64
+ vm.prank(commenter);
65
+ callerAndCommenter.buyOnSecondaryAndComment({
66
+ commenter: commenter,
67
+ quantity: 1,
68
+ collection: address(mock1155),
69
+ tokenId: tokenId1,
70
+ excessRefundRecipient: payable(address(0)),
71
+ maxEthToSpend: 0,
72
+ sqrtPriceLimitX96: 0,
73
+ comment: "test comment"
74
+ });
75
+ }
76
+
77
+ function testBuyOnSecondaryAndComment_revertsWhenCommenterMismatch() public {
78
+ address commenter2 = makeAddr("commenter2");
79
+ vm.expectRevert(abi.encodeWithSelector(ICallerAndCommenter.CommenterMismatch.selector, commenter2, commenter));
80
+ vm.prank(commenter2);
81
+ callerAndCommenter.buyOnSecondaryAndComment({
82
+ commenter: commenter,
83
+ quantity: 1,
84
+ collection: address(mock1155),
85
+ tokenId: tokenId1,
86
+ excessRefundRecipient: payable(address(0)),
87
+ maxEthToSpend: 0,
88
+ sqrtPriceLimitX96: 0,
89
+ comment: "test comment"
90
+ });
91
+ }
92
+
93
+ function testBuyOnSecondaryAndCommentWhenNoCommentDoesNotComment() public {
94
+ uint256 quantity = 1;
95
+ uint256 maxEthToSpend = 0.2 ether;
96
+ uint160 sqrtPriceLimitX96 = 0;
97
+ address excessRefundRecipient = address(0);
98
+
99
+ mockMinter.setSale(address(mock1155), tokenId1);
100
+
101
+ vm.prank(commenter);
102
+ IComments.CommentIdentifier memory result = callerAndCommenter.buyOnSecondaryAndComment({
103
+ commenter: commenter,
104
+ quantity: quantity,
105
+ collection: address(mock1155),
106
+ tokenId: tokenId1,
107
+ excessRefundRecipient: payable(excessRefundRecipient),
108
+ maxEthToSpend: maxEthToSpend,
109
+ sqrtPriceLimitX96: sqrtPriceLimitX96,
110
+ comment: ""
111
+ });
112
+
113
+ assertEq(result.commenter, address(0));
114
+ assertEq(result.contractAddress, address(0));
115
+ assertEq(result.tokenId, 0);
116
+ assertEq(result.nonce, bytes32(0));
117
+ }
118
+
119
+ function upgradeForkCallerAndCommenterToNewImplementation() internal {
120
+ // upgrade the current caller and commenter on the fork to the new implementation
121
+ address COMMENTS = 0x7777777C2B3132e03a65721a41745C07170a5877;
122
+ address ZORA_TIMED_SALE_STRATEGY = 0x777777722D078c97c6ad07d9f36801e653E356Ae;
123
+ address SECONDARY_SWAP = 0x777777EDF27Ac61671e3D5718b10bf6a8802f9f1;
124
+ // deploy the new implementation
125
+ CallerAndCommenterImpl callerAndCallerImp = new CallerAndCommenterImpl(COMMENTS, ZORA_TIMED_SALE_STRATEGY, SECONDARY_SWAP, SPARKS_VALUE);
126
+ // here we override the caller and commenter with the current one on the fork
127
+ callerAndCommenter = ICallerAndCommenter(0x77777775C5074b74540d9cC63Dd840A8c692B4B5);
128
+ // upgrade to the new implementation
129
+ address owner = OwnableUpgradeable(address(callerAndCommenter)).owner();
130
+ vm.prank(owner);
131
+ UUPSUpgradeable(address(callerAndCommenter)).upgradeToAndCall(address(callerAndCallerImp), "");
132
+ }
133
+
134
+ function testFork_buyOnSecondaryAndComment() public {
135
+ // upgrade the forked caller and commenter to the new implementation
136
+ upgradeForkCallerAndCommenterToNewImplementation();
137
+ // this is a known zora test collection where we can secondary swap
138
+ address test1155Address = 0xE79585bF83BbBfAE0fB80222b0a72F2c1D040612;
139
+ uint256 testTokenId = 1;
140
+
141
+ address excessRefundRecipient = makeAddr("excessRefundRecipient");
142
+ uint256 maxEthToSpend = 237222215770897;
143
+ uint160 sqrtPriceLimitX96 = 0;
144
+ string memory comment = "test comment";
145
+
146
+ IComments.CommentIdentifier memory expectedCommentIdentifier = IComments.CommentIdentifier({
147
+ commenter: commenter,
148
+ contractAddress: test1155Address,
149
+ tokenId: testTokenId,
150
+ // we need to get the nonce from the fork comments contract
151
+ nonce: callerAndCommenter.comments().nextNonce()
152
+ });
153
+ bytes32 expectedCommentId = callerAndCommenter.comments().hashCommentIdentifier(expectedCommentIdentifier);
154
+
155
+ uint256 quantity = 1;
156
+ uint256 valueToSpend = maxEthToSpend;
157
+ vm.deal(commenter, valueToSpend);
158
+
159
+ vm.expectEmit(true, true, true, true);
160
+ emit IComments.Commented(expectedCommentId, expectedCommentIdentifier, bytes32(0), emptyCommentIdentifier, 0, comment, block.timestamp, address(0));
161
+ vm.expectEmit(true, true, true, true);
162
+ emit ICallerAndCommenter.SwappedOnSecondaryAndCommented(
163
+ expectedCommentId,
164
+ expectedCommentIdentifier,
165
+ quantity,
166
+ comment,
167
+ ICallerAndCommenter.SwapDirection.BUY
168
+ );
169
+
170
+ // call the caller and commenter contract
171
+ vm.prank(commenter);
172
+ callerAndCommenter.buyOnSecondaryAndComment{value: valueToSpend}({
173
+ commenter: commenter,
174
+ quantity: quantity,
175
+ collection: test1155Address,
176
+ tokenId: testTokenId,
177
+ excessRefundRecipient: payable(excessRefundRecipient),
178
+ maxEthToSpend: maxEthToSpend,
179
+ sqrtPriceLimitX96: sqrtPriceLimitX96,
180
+ comment: comment
181
+ });
182
+ }
183
+
184
+ function testPermitBuyOnSecondaryAndComment() public {
185
+ uint256 quantity = 5;
186
+ uint256 valueToSpend = 1 ether;
187
+
188
+ address contractAddress = address(mock1155);
189
+ uint256 tokenId = tokenId1;
190
+
191
+ string memory comment = "test comment";
192
+
193
+ address erc20z = mockMinter.setSale(contractAddress, tokenId);
194
+
195
+ ICallerAndCommenter.PermitBuyOnSecondaryAndComment memory permit = _createPermitBuy(
196
+ commenter,
197
+ quantity,
198
+ contractAddress,
199
+ tokenId,
200
+ 2 ether,
201
+ 1000,
202
+ comment,
203
+ block.timestamp + 1 hours
204
+ );
205
+
206
+ bytes memory signature = _signPermit(permit, commenterPrivateKey);
207
+
208
+ IComments.CommentIdentifier memory expectedCommentIdentifier = _expectedCommentIdentifier(commenter, contractAddress, tokenId);
209
+
210
+ bytes32 expectedCommentId = comments.hashCommentIdentifier(expectedCommentIdentifier);
211
+
212
+ vm.deal(commenter, valueToSpend);
213
+ vm.expectEmit(true, true, true, true);
214
+ emit IComments.Commented(expectedCommentId, expectedCommentIdentifier, bytes32(0), emptyCommentIdentifier, 0, comment, block.timestamp, address(0));
215
+ vm.expectEmit(true, true, true, true);
216
+ emit ICallerAndCommenter.SwappedOnSecondaryAndCommented(
217
+ expectedCommentId,
218
+ expectedCommentIdentifier,
219
+ quantity,
220
+ comment,
221
+ ICallerAndCommenter.SwapDirection.BUY
222
+ );
223
+
224
+ vm.expectCall(
225
+ address(mockSecondarySwap),
226
+ valueToSpend,
227
+ abi.encodeWithSelector(
228
+ ISecondarySwap.buy1155.selector,
229
+ erc20z,
230
+ quantity,
231
+ commenter,
232
+ permit.commenter,
233
+ permit.maxEthToSpend,
234
+ permit.sqrtPriceLimitX96
235
+ )
236
+ );
237
+
238
+ IComments.CommentIdentifier memory result = callerAndCommenter.permitBuyOnSecondaryAndComment{value: valueToSpend}(permit, signature);
239
+
240
+ assertEq(result.commenter, commenter);
241
+ assertEq(result.contractAddress, contractAddress);
242
+ assertEq(result.tokenId, tokenId);
243
+ assertEq(result.nonce, bytes32(0));
244
+
245
+ // validate that the comment was created
246
+ (, bool exists) = comments.hashAndCheckCommentExists(expectedCommentIdentifier);
247
+ assertEq(exists, true);
248
+ }
249
+
250
+ function testPermitBuyOnSecondaryAndComment_ExpiredDeadline() public {
251
+ ICallerAndCommenter.PermitBuyOnSecondaryAndComment memory permit = _createPermitBuy(
252
+ commenter,
253
+ 1,
254
+ address(mock1155),
255
+ tokenId1,
256
+ 1 ether,
257
+ 1000,
258
+ "test comment",
259
+ block.timestamp - 1 // Expired deadline
260
+ );
261
+
262
+ bytes memory signature = _signPermit(permit, commenterPrivateKey);
263
+
264
+ vm.expectRevert(abi.encodeWithSelector(IComments.ERC2612ExpiredSignature.selector, permit.deadline));
265
+ callerAndCommenter.permitBuyOnSecondaryAndComment{value: 1 ether}(permit, signature);
266
+ }
267
+
268
+ function testPermitBuyOnSecondaryAndComment_InvalidSignature() public {
269
+ ICallerAndCommenter.PermitBuyOnSecondaryAndComment memory permit = _createPermitBuy(
270
+ commenter,
271
+ 1,
272
+ address(mock1155),
273
+ tokenId1,
274
+ 1 ether,
275
+ 1000,
276
+ "test comment",
277
+ block.timestamp + 1 hours
278
+ );
279
+
280
+ bytes memory signature = _signPermit(permit, 5); // Wrong signer
281
+
282
+ vm.expectRevert(IComments.InvalidSignature.selector);
283
+ callerAndCommenter.permitBuyOnSecondaryAndComment{value: 1 ether}(permit, signature);
284
+ }
285
+
286
+ function testPermitBuyOnSecondaryAndComment_IncorrectDestinationChain() public {
287
+ ICallerAndCommenter.PermitBuyOnSecondaryAndComment memory permit = _createPermitBuy(
288
+ commenter,
289
+ 1,
290
+ address(mock1155),
291
+ tokenId1,
292
+ 1 ether,
293
+ 1000,
294
+ "test comment",
295
+ block.timestamp + 1 hours
296
+ );
297
+ permit.destinationChainId = uint32(block.chainid) + 1; // Incorrect destination chain
298
+
299
+ bytes memory signature = _signPermit(permit, commenterPrivateKey);
300
+
301
+ vm.expectRevert(abi.encodeWithSelector(ICallerAndCommenter.IncorrectDestinationChain.selector, permit.destinationChainId));
302
+ callerAndCommenter.permitBuyOnSecondaryAndComment{value: 1 ether}(permit, signature);
303
+ }
304
+
305
+ function testSellOnSecondaryAndComment() public {
306
+ // setup the sale so that we have a link between the erc20z and the 1155
307
+ mockMinter.setSale(address(mock1155), tokenId1);
308
+
309
+ uint256 quantityToSwap = 5;
310
+ mock1155.mint(commenter, tokenId1, quantityToSwap, "");
311
+
312
+ address payable recipient = payable(makeAddr("recipient"));
313
+ uint256 minEthToAcquire = 1 ether;
314
+ uint160 sqrtPriceLimitX96 = 1000;
315
+ string memory comment = "test comment";
316
+
317
+ IComments.CommentIdentifier memory expectedCommentIdentifier = _expectedCommentIdentifier(commenter, address(mock1155), tokenId1);
318
+ bytes32 expectedCommentId = comments.hashCommentIdentifier(expectedCommentIdentifier);
319
+
320
+ bytes memory expectedData = abi.encode(recipient, minEthToAcquire, sqrtPriceLimitX96);
321
+
322
+ // commenter needs to approve the caller to transfer the 1155s
323
+ // since it is to transfer to the secondary swap
324
+ vm.prank(commenter);
325
+ IERC1155(address(mock1155)).setApprovalForAll(address(callerAndCommenter), true);
326
+
327
+ vm.expectEmit(true, true, true, true);
328
+ emit IComments.Commented({
329
+ commentId: expectedCommentId,
330
+ commentIdentifier: expectedCommentIdentifier,
331
+ replyToId: bytes32(0),
332
+ replyTo: emptyCommentIdentifier,
333
+ sparksQuantity: 1,
334
+ text: comment,
335
+ timestamp: block.timestamp,
336
+ referrer: address(0)
337
+ });
338
+ vm.expectEmit(true, true, true, true);
339
+ emit ICallerAndCommenter.SwappedOnSecondaryAndCommented(
340
+ expectedCommentId,
341
+ expectedCommentIdentifier,
342
+ quantityToSwap,
343
+ comment,
344
+ ICallerAndCommenter.SwapDirection.SELL
345
+ );
346
+
347
+ // make sure the mock secondary swap received the 1155s
348
+ // using the expected call
349
+ vm.expectCall(
350
+ address(mockSecondarySwap),
351
+ 0,
352
+ abi.encodeWithSelector(IERC1155Receiver.onERC1155Received.selector, address(callerAndCommenter), commenter, tokenId1, quantityToSwap, expectedData)
353
+ );
354
+
355
+ vm.deal(commenter, SPARKS_VALUE);
356
+ vm.prank(commenter);
357
+ callerAndCommenter.sellOnSecondaryAndComment{value: SPARKS_VALUE}(
358
+ commenter,
359
+ quantityToSwap,
360
+ address(mock1155),
361
+ tokenId1,
362
+ recipient,
363
+ minEthToAcquire,
364
+ sqrtPriceLimitX96,
365
+ comment
366
+ );
367
+
368
+ // make sure the mock secondary swap received the 1155s
369
+ assertEq(IERC1155(address(mock1155)).balanceOf(address(mockSecondarySwap), tokenId1), quantityToSwap);
370
+ }
371
+
372
+ function testFork_sellOnSecondaryAndComment() public {
373
+ // upgrade the forked caller and commenter to the new implementation
374
+ upgradeForkCallerAndCommenterToNewImplementation();
375
+ // this is a known zora test collection where we can secondary swap
376
+ address test1155Address = 0xE79585bF83BbBfAE0fB80222b0a72F2c1D040612;
377
+ uint256 testTokenId = 1;
378
+
379
+ uint256 quantityToBuy = 5;
380
+
381
+ uint256 maxEthToSpend = 237222215770897 * quantityToBuy;
382
+ uint256 valueToSpend = maxEthToSpend;
383
+
384
+ // first we need to buy the 1155s on secondary
385
+ vm.deal(commenter, valueToSpend);
386
+ vm.prank(commenter);
387
+ callerAndCommenter.buyOnSecondaryAndComment{value: valueToSpend}({
388
+ commenter: commenter,
389
+ quantity: quantityToBuy,
390
+ collection: test1155Address,
391
+ tokenId: testTokenId,
392
+ excessRefundRecipient: payable(commenter),
393
+ maxEthToSpend: maxEthToSpend,
394
+ sqrtPriceLimitX96: 0,
395
+ comment: "test comment when buying"
396
+ });
397
+
398
+ // now we can sell the 1155s on secondary
399
+
400
+ uint256 quantityToSell = 3;
401
+ string memory sellingComment = "test comment when selling";
402
+
403
+ vm.prank(commenter);
404
+ IERC1155(address(test1155Address)).setApprovalForAll(address(callerAndCommenter), true);
405
+
406
+ // perform the sell
407
+ vm.deal(commenter, SPARKS_VALUE);
408
+ vm.prank(commenter);
409
+ callerAndCommenter.sellOnSecondaryAndComment{value: SPARKS_VALUE}({
410
+ commenter: commenter,
411
+ quantity: quantityToSell,
412
+ collection: test1155Address,
413
+ tokenId: testTokenId,
414
+ recipient: payable(commenter),
415
+ minEthToAcquire: 0,
416
+ sqrtPriceLimitX96: 0,
417
+ comment: sellingComment
418
+ });
419
+ }
420
+
421
+ function testSellOnSecondaryAndComment_CommenterMismatch() public {
422
+ vm.expectRevert(abi.encodeWithSelector(ICallerAndCommenter.CommenterMismatch.selector, commenter, makeAddr("other")));
423
+ vm.prank(commenter);
424
+ callerAndCommenter.sellOnSecondaryAndComment(makeAddr("other"), 1, address(mock1155), tokenId1, payable(address(0)), 1 ether, 1000, "test comment");
425
+ }
426
+
427
+ function testSellOnSecondaryAndComment_SaleNotSet() public {
428
+ uint256 quantityToSwap = 5;
429
+ mock1155.mint(commenter, tokenId1, quantityToSwap, "");
430
+ vm.prank(commenter);
431
+ IERC1155(address(mock1155)).setApprovalForAll(address(callerAndCommenter), true);
432
+
433
+ vm.expectRevert(ISecondarySwap.SaleNotSet.selector);
434
+ vm.prank(commenter);
435
+ vm.deal(commenter, SPARKS_VALUE);
436
+ callerAndCommenter.sellOnSecondaryAndComment{value: SPARKS_VALUE}(
437
+ commenter,
438
+ 1,
439
+ address(mock1155),
440
+ tokenId1,
441
+ payable(address(0)),
442
+ 1 ether,
443
+ 1000,
444
+ "test comment"
445
+ );
446
+ }
447
+
448
+ function testSellOnSecondaryAndComment_RevertsWhenNotOneSparkAndACommentIsSent(uint16 sparksQuantity, bool commentIsSent) public {
449
+ uint256 valueToSend = sparksQuantity * SPARKS_VALUE;
450
+ // setup the sale so that we have a link between the erc20z and the 1155
451
+ mockMinter.setSale(address(mock1155), tokenId1);
452
+
453
+ uint256 quantityToSwap = 5;
454
+ mock1155.mint(commenter, tokenId1, quantityToSwap, "");
455
+
456
+ address recipient = makeAddr("recipient");
457
+ uint256 minEthToAcquire = 1 ether;
458
+ uint160 sqrtPriceLimitX96 = 1000;
459
+ string memory comment = commentIsSent ? "test comment" : "";
460
+
461
+ // commenter needs to approve the caller to transfer the 1155s
462
+ // since it is to transfer to the secondary swap
463
+ vm.prank(commenter);
464
+ IERC1155(address(mock1155)).setApprovalForAll(address(callerAndCommenter), true);
465
+
466
+ // if we are sending a comment, we should be required to send one spark
467
+ if (commentIsSent) {
468
+ if (sparksQuantity != 1) {
469
+ vm.expectRevert(abi.encodeWithSelector(ICallerAndCommenter.WrongValueSent.selector, SPARKS_VALUE, valueToSend));
470
+ }
471
+ } else {
472
+ // if we are not sending a comment, we should not send any ETH
473
+ if (valueToSend != 0) {
474
+ vm.expectRevert(abi.encodeWithSelector(ICallerAndCommenter.WrongValueSent.selector, 0, valueToSend));
475
+ }
476
+ }
477
+
478
+ vm.deal(commenter, valueToSend);
479
+ vm.prank(commenter);
480
+ callerAndCommenter.sellOnSecondaryAndComment{value: valueToSend}(
481
+ commenter,
482
+ quantityToSwap,
483
+ address(mock1155),
484
+ tokenId1,
485
+ payable(recipient),
486
+ minEthToAcquire,
487
+ sqrtPriceLimitX96,
488
+ comment
489
+ );
490
+ }
491
+
492
+ function _createPermitBuy(
493
+ address _commenter,
494
+ uint256 _quantity,
495
+ address _collection,
496
+ uint256 _tokenId,
497
+ uint256 _maxEthToSpend,
498
+ uint160 _sqrtPriceLimitX96,
499
+ string memory _comment,
500
+ uint256 _deadline
501
+ ) internal view returns (ICallerAndCommenter.PermitBuyOnSecondaryAndComment memory) {
502
+ return
503
+ ICallerAndCommenter.PermitBuyOnSecondaryAndComment({
504
+ commenter: _commenter,
505
+ quantity: _quantity,
506
+ collection: _collection,
507
+ tokenId: _tokenId,
508
+ maxEthToSpend: _maxEthToSpend,
509
+ sqrtPriceLimitX96: _sqrtPriceLimitX96,
510
+ comment: _comment,
511
+ deadline: _deadline,
512
+ nonce: bytes32(0),
513
+ sourceChainId: uint32(block.chainid),
514
+ destinationChainId: uint32(block.chainid)
515
+ });
516
+ }
517
+
518
+ function _signPermit(ICallerAndCommenter.PermitBuyOnSecondaryAndComment memory _permit, uint256 _privateKey) internal view returns (bytes memory) {
519
+ bytes32 digest = callerAndCommenter.hashPermitBuyOnSecondaryAndComment(_permit);
520
+ (uint8 v, bytes32 r, bytes32 s) = vm.sign(_privateKey, digest);
521
+ return abi.encodePacked(r, s, v);
522
+ }
523
+ }