@zoralabs/comments-contracts 0.0.1 → 0.0.2

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 (110) hide show
  1. package/.turbo/turbo-build.log +49 -31
  2. package/README.md +10 -39
  3. package/abis/AddDelegateCommenterRole.json +9 -0
  4. package/abis/CallerAndCommenter.json +62 -0
  5. package/abis/CallerAndCommenterImpl.json +1218 -0
  6. package/abis/CallerAndCommenterMintAndCommentTest.json +771 -0
  7. package/abis/CallerAndCommenterSwapAndCommentTest.json +844 -0
  8. package/abis/CallerAndCommenterTestBase.json +577 -0
  9. package/abis/CommentsImpl.json +189 -59
  10. package/abis/CommentsImplConstants.json +106 -0
  11. package/abis/CommentsPermitTest.json +26 -6
  12. package/abis/CommentsTest.json +58 -10
  13. package/abis/Comments_mintAndCommentTest.json +11 -4
  14. package/abis/Comments_smartWallet.json +711 -0
  15. package/abis/DeployCallerAndCommenterImpl.json +9 -0
  16. package/abis/DeployImpl.json +0 -13
  17. package/abis/DeployNonDeterministic.json +0 -13
  18. package/abis/DeployScript.json +0 -13
  19. package/abis/EIP712Upgradeable.json +74 -0
  20. package/abis/EIP712UpgradeableWithChainId.json +49 -0
  21. package/abis/ERC20.json +310 -0
  22. package/abis/GenerateDeterministicParams.json +0 -13
  23. package/abis/ICallerAndCommenter.json +797 -0
  24. package/abis/IComments.json +629 -9
  25. package/abis/IERC20.json +39 -42
  26. package/abis/IERC20Metadata.json +224 -0
  27. package/abis/{CommentsDeployerBase.json → IMultiOwnable.json} +8 -2
  28. package/abis/IProtocolRewards.json +19 -0
  29. package/abis/ISecondarySwap.json +45 -0
  30. package/abis/IZoraCreator1155.json +51 -0
  31. package/abis/IZoraTimedSaleStrategy.json +91 -0
  32. package/abis/Mock1155.json +75 -1
  33. package/abis/Mock1155NoCreatorRewardRecipient.json +605 -0
  34. package/abis/Mock1155NoOwner.json +566 -0
  35. package/abis/{MockMinter.json → MockDelegateCommenter.json} +12 -2
  36. package/abis/MockERC20z.json +315 -0
  37. package/abis/MockMultiOwnable.json +212 -0
  38. package/abis/MockSecondarySwap.json +95 -0
  39. package/abis/MockZoraTimedSale.json +139 -0
  40. package/abis/Ownable2StepUpgradeable.json +138 -0
  41. package/abis/UnorderedNoncesUpgradeable.json +4 -4
  42. package/addresses/1.json +9 -0
  43. package/addresses/10.json +9 -0
  44. package/addresses/11155111.json +9 -0
  45. package/addresses/11155420.json +9 -0
  46. package/addresses/42161.json +9 -0
  47. package/addresses/7777777.json +9 -0
  48. package/addresses/81457.json +9 -0
  49. package/addresses/8453.json +9 -0
  50. package/addresses/84532.json +9 -0
  51. package/addresses/999999999.json +7 -2
  52. package/deterministicConfig/callerAndCommenter.json +8 -0
  53. package/deterministicConfig/comments.json +2 -2
  54. package/dist/index.cjs +724 -35
  55. package/dist/index.cjs.map +1 -1
  56. package/dist/index.js +723 -35
  57. package/dist/index.js.map +1 -1
  58. package/dist/types.d.ts +1 -1
  59. package/dist/types.d.ts.map +1 -1
  60. package/dist/wagmiGenerated.d.ts +1102 -57
  61. package/dist/wagmiGenerated.d.ts.map +1 -1
  62. package/package/types.ts +4 -1
  63. package/package/wagmiGenerated.ts +728 -32
  64. package/package.json +12 -11
  65. package/script/AddDelegateCommenterRole.s.sol +24 -0
  66. package/script/CommentsDeployerBase.sol +102 -19
  67. package/script/Deploy.s.sol +2 -44
  68. package/script/DeployCallerAndCommenterImpl.s.sol +29 -0
  69. package/script/DeployImpl.s.sol +1 -0
  70. package/script/DeployNonDeterministic.s.sol +22 -13
  71. package/script/GenerateDeterministicParams.s.sol +32 -4
  72. package/scripts/generateCommentsTestData.ts +170 -79
  73. package/src/CommentsImpl.sol +267 -134
  74. package/src/CommentsImplConstants.sol +44 -0
  75. package/src/interfaces/ICallerAndCommenter.sol +215 -0
  76. package/src/interfaces/IComments.sol +189 -42
  77. package/src/interfaces/IMultiOwnable.sol +10 -0
  78. package/src/interfaces/ISecondarySwap.sol +40 -0
  79. package/src/interfaces/IZoraCreator1155.sol +6 -1
  80. package/src/interfaces/IZoraCreator1155TypesV1.sol +46 -0
  81. package/src/interfaces/IZoraTimedSaleStrategy.sol +25 -0
  82. package/src/proxy/CallerAndCommenter.sol +43 -0
  83. package/src/utils/CallerAndCommenterImpl.sol +376 -0
  84. package/src/utils/EIP712UpgradeableWithChainId.sol +12 -23
  85. package/src/version/ContractVersionBase.sol +1 -1
  86. package/test/CallerAndCommenterTestBase.sol +77 -0
  87. package/test/CallerAndCommenter_mintAndComment.t copy.sol +214 -0
  88. package/test/CallerAndCommenter_swapAndComment.t.sol +523 -0
  89. package/test/Comments.t.sol +166 -29
  90. package/test/CommentsTestBase.sol +12 -20
  91. package/test/Comments_delegateComment.t.sol +129 -0
  92. package/test/Comments_permit.t.sol +131 -44
  93. package/test/Comments_smartWallet.t.sol +152 -0
  94. package/test/mocks/Mock1155.sol +12 -1
  95. package/test/mocks/Mock1155NoCreatorRewardRecipient.sol +65 -0
  96. package/test/mocks/Mock1155NoOwner.sol +53 -0
  97. package/test/mocks/MockDelegateCommenter.sol +36 -0
  98. package/test/mocks/MockIZoraCreator1155.sol +16 -0
  99. package/test/mocks/MockSecondarySwap.sol +30 -0
  100. package/test/mocks/MockZoraTimedSale.sol +38 -0
  101. package/wagmi.config.ts +3 -1
  102. package/abis/ProxyDeployerScript.json +0 -15
  103. package/scripts/backfillComments.ts +0 -176
  104. package/scripts/queries.ts +0 -73
  105. package/scripts/queryAndSaveComments.ts +0 -48
  106. package/scripts/queryQuantityOfComments.ts +0 -53
  107. package/scripts/writeComments.ts +0 -198
  108. package/src/deployments/CommentsDeployment.sol +0 -14
  109. package/test/Comments_mintAndComment.t.sol +0 -101
  110. package/test/mocks/MockMinter.sol +0 -29
@@ -11,10 +11,11 @@ import {IProtocolRewards} from "@zoralabs/protocol-rewards/src/interfaces/IProto
11
11
  import {ProtocolRewards} from "./mocks/ProtocolRewards.sol";
12
12
  import {CommentsTestBase} from "./CommentsTestBase.sol";
13
13
  import {UnorderedNoncesUpgradeable} from "@zoralabs/shared-contracts/utils/UnorderedNoncesUpgradeable.sol";
14
+ import {MockMultiOwnable} from "./Comments_smartWallet.t.sol";
14
15
 
15
16
  contract CommentsPermitTest is CommentsTestBase {
16
17
  function testPermitComment() public {
17
- IComments.PermitComment memory permitComment = _createPermitComment(collectorWithToken, "test comment", 3);
18
+ IComments.PermitComment memory permitComment = _createPermitComment(collectorWithToken, "test comment");
18
19
  bytes memory signature = _signPermitComment(permitComment, collectorWithTokenPrivateKey);
19
20
 
20
21
  IComments.CommentIdentifier memory expectedCommentIdentifier = _expectedCommentIdentifier(
@@ -27,7 +28,7 @@ contract CommentsPermitTest is CommentsTestBase {
27
28
 
28
29
  // any account can execute the permit comment on behalf of the collectorWithToken, but they must have enough eth to do so.
29
30
  address executor = makeAddr("executor");
30
- vm.deal(executor, permitComment.sparksQuantity * SPARKS_VALUE);
31
+ vm.deal(executor, SPARKS_VALUE);
31
32
 
32
33
  vm.expectEmit(true, true, true, true);
33
34
  emit IComments.Commented(
@@ -35,7 +36,7 @@ contract CommentsPermitTest is CommentsTestBase {
35
36
  expectedCommentIdentifier,
36
37
  bytes32(0),
37
38
  permitComment.replyTo,
38
- permitComment.sparksQuantity,
39
+ 1,
39
40
  permitComment.text,
40
41
  block.timestamp,
41
42
  permitComment.referrer
@@ -47,7 +48,7 @@ contract CommentsPermitTest is CommentsTestBase {
47
48
  }
48
49
 
49
50
  function testPermitComment_NonceUsedTwice() public {
50
- IComments.PermitComment memory permitComment = _createPermitComment(collectorWithToken, "test comment", 3);
51
+ IComments.PermitComment memory permitComment = _createPermitComment(collectorWithToken, "test comment");
51
52
  bytes memory signature = _signPermitComment(permitComment, collectorWithTokenPrivateKey);
52
53
 
53
54
  _setupTokenAndSparks(permitComment);
@@ -62,7 +63,7 @@ contract CommentsPermitTest is CommentsTestBase {
62
63
  }
63
64
 
64
65
  function testPermitComment_DeadlineExpired() public {
65
- IComments.PermitComment memory permitComment = _createPermitComment(collectorWithToken, "test comment", 3);
66
+ IComments.PermitComment memory permitComment = _createPermitComment(collectorWithToken, "test comment");
66
67
  bytes memory signature = _signPermitComment(permitComment, collectorWithTokenPrivateKey);
67
68
 
68
69
  _setupTokenAndSparks(permitComment);
@@ -71,7 +72,7 @@ contract CommentsPermitTest is CommentsTestBase {
71
72
  vm.warp(permitComment.deadline + 1);
72
73
 
73
74
  address executor = makeAddr("executor");
74
- vm.deal(executor, permitComment.sparksQuantity * SPARKS_VALUE);
75
+ vm.deal(executor, SPARKS_VALUE);
75
76
  vm.expectRevert(abi.encodeWithSelector(IComments.ERC2612ExpiredSignature.selector, permitComment.deadline));
76
77
  _executePermitComment(executor, permitComment, signature);
77
78
  }
@@ -81,7 +82,7 @@ contract CommentsPermitTest is CommentsTestBase {
81
82
  uint256 wrongSignerPrivateKey;
82
83
  (wrongSigner, wrongSignerPrivateKey) = makeAddrAndKey("wrongSigner");
83
84
 
84
- IComments.PermitComment memory permitComment = _createPermitComment(collectorWithToken, "test comment", 3);
85
+ IComments.PermitComment memory permitComment = _createPermitComment(collectorWithToken, "test comment");
85
86
  bytes memory wrongSignature = _signPermitComment(permitComment, wrongSignerPrivateKey);
86
87
 
87
88
  _setupTokenAndSparks(permitComment);
@@ -90,26 +91,101 @@ contract CommentsPermitTest is CommentsTestBase {
90
91
  _executePermitComment(collectorWithToken, permitComment, wrongSignature);
91
92
  }
92
93
 
93
- function testPermitComment_ZeroSparks() public {
94
- IComments.PermitComment memory permitComment = _createPermitComment(collectorWithToken, "test comment", 0);
94
+ function testPermitComment_ZeroSparks_Collector() public {
95
+ IComments.PermitComment memory permitComment = _createPermitComment(collectorWithToken, "test comment");
95
96
  bytes memory signature = _signPermitComment(permitComment, collectorWithTokenPrivateKey);
96
97
 
97
98
  _setupTokenAndSparks(permitComment);
98
99
 
99
100
  vm.expectRevert(IComments.MustSendAtLeastOneSpark.selector);
100
- _executePermitComment(collectorWithToken, permitComment, signature);
101
+ vm.prank(collectorWithToken);
102
+ comments.permitComment{value: 0}(permitComment, signature);
103
+ }
104
+
105
+ function testPermitComment_ZeroSparks_Creator() public {
106
+ IComments.PermitComment memory permitComment = _createPermitComment(tokenAdmin, "test comment");
107
+ bytes memory signature = _signPermitComment(permitComment, tokenAdminPrivateKey);
108
+
109
+ _setupTokenAndSparks(permitComment);
110
+
111
+ bytes32 expectedNonce = comments.nextNonce();
112
+
113
+ // any account can execute the permit comment on behalf of the tokenAdmin,
114
+ // it should be executed with 0 sparks
115
+ vm.prank(makeAddr("random account"));
116
+ comments.permitComment{value: 0}(permitComment, signature);
117
+
118
+ IComments.CommentIdentifier memory expectedCommentIdentifier = IComments.CommentIdentifier({
119
+ commenter: tokenAdmin,
120
+ contractAddress: address(mock1155),
121
+ tokenId: tokenId1,
122
+ nonce: expectedNonce
123
+ });
124
+ _assertCommentExists(expectedCommentIdentifier);
101
125
  }
102
126
 
103
127
  function testPermitComment_Not1155Holder() public {
104
- IComments.PermitComment memory permitComment = _createPermitComment(collectorWithToken, "test comment", 3);
128
+ IComments.PermitComment memory permitComment = _createPermitComment(collectorWithToken, "test comment");
105
129
  bytes memory signature = _signPermitComment(permitComment, collectorWithTokenPrivateKey);
106
130
 
107
131
  address executor = makeAddr("executor");
108
- vm.deal(executor, permitComment.sparksQuantity * SPARKS_VALUE);
132
+ vm.deal(executor, SPARKS_VALUE);
109
133
  vm.expectRevert(IComments.NotTokenHolderOrAdmin.selector);
110
134
  _executePermitComment(executor, permitComment, signature);
111
135
  }
112
136
 
137
+ function testPermitComment_SmartWalletOwner() public {
138
+ // Test scenario:
139
+ // We want to enable a smart wallet owner to comment when the smart wallet owns the token.
140
+ // - The smart wallet owns the 1155 token
141
+ // - privy account is an owner of the smart wallet
142
+ // - privy account is the one that signs the message
143
+ // - The comment is attributed to privy account (the smart wallet owner)
144
+ // We create a permit where:
145
+ // - commenter is privy account (smart wallet owner)
146
+ // - commenterSmartWallet is the smart wallet address
147
+ // - Privy account signs the message
148
+ // This should be a valid scenario, allowing a smart wallet owner to comment using a token owned by the smart wallet.
149
+ (address privyAccount, uint256 privyPrivateKey) = makeAddrAndKey("privy");
150
+
151
+ address smartWallet = address(new MockMultiOwnable(privyAccount));
152
+
153
+ mock1155.mint(smartWallet, tokenId1, 1, "");
154
+
155
+ IComments.PermitComment memory permitComment = IComments.PermitComment({
156
+ // comment will be attributed to the smart wallet - it should be the one that
157
+ // has the signature checked against it.
158
+ commenter: privyAccount,
159
+ contractAddress: address(mock1155),
160
+ tokenId: tokenId1,
161
+ replyTo: IComments.CommentIdentifier({commenter: address(0), contractAddress: address(0), tokenId: 0, nonce: bytes32(0)}),
162
+ text: "smart wallet comment",
163
+ deadline: uint48(block.timestamp + 1000),
164
+ nonce: bytes32(0),
165
+ referrer: address(0),
166
+ sourceChainId: uint32(100),
167
+ destinationChainId: uint32(block.chainid),
168
+ // collectorWithToken is the smart wallet owner - this is the account that actually owns the 1155 token.
169
+ commenterSmartWallet: smartWallet
170
+ });
171
+
172
+ // sign the permit - but we have the another account (privy) sign for the smart wallet, since it is also an owner
173
+ bytes memory signature = _signPermitComment(permitComment, privyPrivateKey);
174
+
175
+ IComments.CommentIdentifier memory expectedCommentIdentifier = _expectedCommentIdentifier(
176
+ permitComment.contractAddress,
177
+ permitComment.tokenId,
178
+ // privy account is the one that signs the message, but the smart wallet is the one that is commenting
179
+ privyAccount
180
+ );
181
+
182
+ address executor = makeAddr("executor");
183
+ vm.deal(executor, SPARKS_VALUE);
184
+ _executePermitComment(executor, permitComment, signature);
185
+
186
+ _assertCommentExists(expectedCommentIdentifier);
187
+ }
188
+
113
189
  function testPermitSparkCommentSparksComment() public {
114
190
  IComments.PermitSparkComment memory permitSparkComment = _postCommentAndCreatePermitSparkComment(sparker, 3);
115
191
  bytes memory signature = _signPermitSparkComment(permitSparkComment, sparkerPrivateKey);
@@ -119,16 +195,21 @@ contract CommentsPermitTest is CommentsTestBase {
119
195
  address executor = makeAddr("executor");
120
196
  vm.deal(executor, permitSparkComment.sparksQuantity * SPARKS_VALUE);
121
197
 
122
- bytes32 commentId = comments.hashCommentIdentifier(permitSparkComment.comment);
123
-
124
- uint256 beforeSparksCount = comments.commentSparksQuantity(commentId);
198
+ uint256 beforeSparksCount = comments.commentSparksQuantity(permitSparkComment.comment);
125
199
 
126
200
  vm.expectEmit(true, true, true, true);
127
- emit IComments.SparkedComment(commentId, permitSparkComment.comment, permitSparkComment.sparksQuantity, sparker, block.timestamp);
201
+ emit IComments.SparkedComment(
202
+ comments.hashCommentIdentifier(permitSparkComment.comment),
203
+ permitSparkComment.comment,
204
+ permitSparkComment.sparksQuantity,
205
+ sparker,
206
+ block.timestamp,
207
+ permitSparkComment.referrer
208
+ );
128
209
 
129
210
  _executePermitSparkComment(executor, permitSparkComment, signature);
130
211
 
131
- uint256 afterSparksCount = comments.commentSparksQuantity(commentId);
212
+ uint256 afterSparksCount = comments.commentSparksQuantity(permitSparkComment.comment);
132
213
  assertEq(afterSparksCount, beforeSparksCount + permitSparkComment.sparksQuantity);
133
214
  }
134
215
 
@@ -204,10 +285,10 @@ contract CommentsPermitTest is CommentsTestBase {
204
285
  }
205
286
 
206
287
  function testPermitCommentCrossChain() public {
207
- uint256 sourceChainId = 1; // Ethereum mainnet
208
- uint256 destinationChainId = block.chainid;
288
+ uint32 sourceChainId = 1; // Ethereum mainnet
289
+ uint32 destinationChainId = uint32(block.chainid);
209
290
 
210
- IComments.PermitComment memory permitComment = _createPermitComment(collectorWithToken, "cross-chain comment", 3);
291
+ IComments.PermitComment memory permitComment = _createPermitComment(collectorWithToken, "cross-chain comment");
211
292
  permitComment.sourceChainId = sourceChainId;
212
293
  permitComment.destinationChainId = destinationChainId;
213
294
 
@@ -222,7 +303,7 @@ contract CommentsPermitTest is CommentsTestBase {
222
303
  _setupTokenAndSparks(permitComment);
223
304
 
224
305
  address executor = makeAddr("executor");
225
- vm.deal(executor, permitComment.sparksQuantity * SPARKS_VALUE);
306
+ vm.deal(executor, SPARKS_VALUE);
226
307
 
227
308
  vm.expectEmit(true, true, true, true);
228
309
  emit IComments.Commented(
@@ -230,7 +311,7 @@ contract CommentsPermitTest is CommentsTestBase {
230
311
  expectedCommentIdentifier,
231
312
  bytes32(0),
232
313
  permitComment.replyTo,
233
- permitComment.sparksQuantity,
314
+ 1,
234
315
  permitComment.text,
235
316
  block.timestamp,
236
317
  permitComment.referrer
@@ -242,10 +323,10 @@ contract CommentsPermitTest is CommentsTestBase {
242
323
  }
243
324
 
244
325
  function testPermitCommentCrossChainInvalidDestination() public {
245
- uint256 sourceChainId = 1; // Ethereum mainnet
246
- uint256 invalidDestinationChainId = 42; // Some other chain ID
326
+ uint32 sourceChainId = 1; // Ethereum mainnet
327
+ uint32 invalidDestinationChainId = 42; // Some other chain ID
247
328
 
248
- IComments.PermitComment memory permitComment = _createPermitComment(collectorWithToken, "invalid cross-chain comment", 3);
329
+ IComments.PermitComment memory permitComment = _createPermitComment(collectorWithToken, "invalid cross-chain comment");
249
330
  permitComment.sourceChainId = sourceChainId;
250
331
  permitComment.destinationChainId = invalidDestinationChainId;
251
332
 
@@ -254,15 +335,15 @@ contract CommentsPermitTest is CommentsTestBase {
254
335
  _setupTokenAndSparks(permitComment);
255
336
 
256
337
  address executor = makeAddr("executor");
257
- vm.deal(executor, permitComment.sparksQuantity * SPARKS_VALUE);
338
+ vm.deal(executor, SPARKS_VALUE);
258
339
 
259
340
  vm.expectRevert(abi.encodeWithSelector(IComments.IncorrectDestinationChain.selector, invalidDestinationChainId));
260
341
  _executePermitComment(executor, permitComment, signature);
261
342
  }
262
343
 
263
344
  function testPermitSparkCommentCrossChain() public {
264
- uint256 sourceChainId = 1; // Ethereum mainnet
265
- uint256 destinationChainId = block.chainid;
345
+ uint32 sourceChainId = 1; // Ethereum mainnet
346
+ uint32 destinationChainId = uint32(block.chainid);
266
347
 
267
348
  IComments.PermitSparkComment memory permitSparkComment = _postCommentAndCreatePermitSparkComment(sparker, 3);
268
349
  permitSparkComment.sourceChainId = sourceChainId;
@@ -275,22 +356,28 @@ contract CommentsPermitTest is CommentsTestBase {
275
356
  address executor = makeAddr("executor");
276
357
  vm.deal(executor, permitSparkComment.sparksQuantity * SPARKS_VALUE);
277
358
 
278
- bytes32 commentId = comments.hashCommentIdentifier(permitSparkComment.comment);
359
+ uint256 beforeSparksCount = comments.commentSparksQuantity(permitSparkComment.comment);
279
360
 
280
- uint256 beforeSparksCount = comments.commentSparksQuantity(commentId);
361
+ bytes32 commentId = comments.hashCommentIdentifier(permitSparkComment.comment);
281
362
 
282
363
  vm.expectEmit(true, true, true, true);
283
- emit IComments.SparkedComment(commentId, permitSparkComment.comment, permitSparkComment.sparksQuantity, sparker, block.timestamp);
284
-
364
+ emit IComments.SparkedComment(
365
+ commentId,
366
+ permitSparkComment.comment,
367
+ permitSparkComment.sparksQuantity,
368
+ sparker,
369
+ block.timestamp,
370
+ permitSparkComment.referrer
371
+ );
285
372
  _executePermitSparkComment(executor, permitSparkComment, signature);
286
373
 
287
- uint256 afterSparksCount = comments.commentSparksQuantity(commentId);
374
+ uint256 afterSparksCount = comments.commentSparksQuantity(permitSparkComment.comment);
288
375
  assertEq(afterSparksCount, beforeSparksCount + permitSparkComment.sparksQuantity);
289
376
  }
290
377
 
291
378
  function testPermitSparkCommentCrossChainInvalidDestination() public {
292
- uint256 sourceChainId = 1; // Ethereum mainnet
293
- uint256 invalidDestinationChainId = 42; // Some other chain ID
379
+ uint32 sourceChainId = 1; // Ethereum mainnet
380
+ uint32 invalidDestinationChainId = 42; // Some other chain ID
294
381
 
295
382
  IComments.PermitSparkComment memory permitSparkComment = _postCommentAndCreatePermitSparkComment(sparker, 3);
296
383
  permitSparkComment.sourceChainId = sourceChainId;
@@ -312,7 +399,7 @@ contract CommentsPermitTest is CommentsTestBase {
312
399
  address commenter = makeAddr("commenter_b");
313
400
  IComments.CommentIdentifier memory commentIdentifier = _mockComment(commenter, emptyReplyTo);
314
401
 
315
- return _createPermitSparkComment(commentIdentifier, _sparker, sparksQuantity, block.chainid, block.chainid);
402
+ return _createPermitSparkComment(commentIdentifier, _sparker, sparksQuantity, uint32(block.chainid), uint32(block.chainid));
316
403
  }
317
404
 
318
405
  // Helper functions
@@ -320,8 +407,8 @@ contract CommentsPermitTest is CommentsTestBase {
320
407
  IComments.CommentIdentifier memory commentIdentifier,
321
408
  address _sparker,
322
409
  uint64 sparksQuantity,
323
- uint256 sourceChainId,
324
- uint256 destinationChainId
410
+ uint32 sourceChainId,
411
+ uint32 destinationChainId
325
412
  ) internal returns (IComments.PermitSparkComment memory) {
326
413
  return
327
414
  IComments.PermitSparkComment({
@@ -353,7 +440,7 @@ contract CommentsPermitTest is CommentsTestBase {
353
440
  }
354
441
 
355
442
  // Helper functions
356
- function _createPermitComment(address commenter, string memory text, uint64 sparksQuantity) internal returns (IComments.PermitComment memory) {
443
+ function _createPermitComment(address commenter, string memory text) internal returns (IComments.PermitComment memory) {
357
444
  return
358
445
  IComments.PermitComment({
359
446
  contractAddress: address(mock1155),
@@ -361,23 +448,23 @@ contract CommentsPermitTest is CommentsTestBase {
361
448
  commenter: commenter,
362
449
  replyTo: emptyCommentIdentifier,
363
450
  text: text,
364
- sparksQuantity: sparksQuantity,
365
451
  deadline: block.timestamp + 100,
366
452
  nonce: bytes32("1"),
367
453
  referrer: makeAddr("referrer"),
368
- sourceChainId: block.chainid,
369
- destinationChainId: block.chainid
454
+ sourceChainId: uint32(block.chainid),
455
+ destinationChainId: uint32(block.chainid),
456
+ commenterSmartWallet: address(0)
370
457
  });
371
458
  }
372
459
 
373
460
  function _setupTokenAndSparks(IComments.PermitComment memory permitComment) internal {
374
461
  mock1155.mint(permitComment.commenter, permitComment.tokenId, 1, "");
375
- vm.deal(permitComment.commenter, permitComment.sparksQuantity * SPARKS_VALUE);
462
+ vm.deal(permitComment.commenter, SPARKS_VALUE);
376
463
  }
377
464
 
378
465
  function _executePermitComment(address executor, IComments.PermitComment memory permitComment, bytes memory signature) internal {
379
466
  vm.prank(executor);
380
- comments.permitComment{value: permitComment.sparksQuantity * SPARKS_VALUE}(permitComment, signature);
467
+ comments.permitComment{value: SPARKS_VALUE}(permitComment, signature);
381
468
  }
382
469
 
383
470
  function _assertCommentExists(IComments.CommentIdentifier memory commentIdentifier) internal view {
@@ -0,0 +1,152 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.20;
3
+
4
+ import {CommentsTestBase} from "./CommentsTestBase.sol";
5
+ import {IMultiOwnable} from "../src/interfaces/IMultiOwnable.sol";
6
+ import {IComments} from "../src/interfaces/IComments.sol";
7
+ import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
8
+ import {ERC1155Holder} from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol";
9
+
10
+ contract MockMultiOwnable is IMultiOwnable, ERC1155Holder {
11
+ bytes4 internal constant MAGIC_VALUE = bytes4(keccak256("isValidSignature(bytes32,bytes)"));
12
+ mapping(address => bool) public isOwner;
13
+
14
+ constructor(address _owner) {
15
+ isOwner[_owner] = true;
16
+ }
17
+
18
+ function addOwner(address _owner) external {
19
+ isOwner[_owner] = true;
20
+ }
21
+
22
+ function isValidSignature(bytes32 _messageHash, bytes memory _signature) public view returns (bytes4 magicValue) {
23
+ address signatory = ECDSA.recover(_messageHash, _signature);
24
+
25
+ if (isOwner[signatory]) {
26
+ return MAGIC_VALUE;
27
+ } else {
28
+ return bytes4(0);
29
+ }
30
+ }
31
+
32
+ function isOwnerAddress(address account) external view returns (bool) {
33
+ return isOwner[account];
34
+ }
35
+ }
36
+
37
+ contract Comments_smartWallet is CommentsTestBase {
38
+ function test_commentWithSmartWalletOwner_whenHolder() public {
39
+ address smartWallet = address(new MockMultiOwnable(address(collectorWithoutToken)));
40
+
41
+ mock1155.mint(smartWallet, tokenId1, 1, "");
42
+
43
+ IComments.CommentIdentifier memory expectedCommentIdentifier = _expectedCommentIdentifier(address(mock1155), tokenId1, collectorWithoutToken);
44
+
45
+ vm.expectEmit(true, true, true, true);
46
+ emit IComments.Commented(
47
+ comments.hashCommentIdentifier(expectedCommentIdentifier),
48
+ expectedCommentIdentifier,
49
+ 0,
50
+ emptyCommentIdentifier,
51
+ 1,
52
+ "test comment",
53
+ block.timestamp,
54
+ address(0)
55
+ );
56
+ vm.prank(collectorWithoutToken);
57
+ vm.deal(collectorWithoutToken, SPARKS_VALUE);
58
+ comments.comment{value: SPARKS_VALUE}({
59
+ commenter: collectorWithoutToken,
60
+ contractAddress: address(mock1155),
61
+ tokenId: tokenId1,
62
+ text: "test comment",
63
+ replyTo: emptyCommentIdentifier,
64
+ commenterSmartWallet: smartWallet,
65
+ referrer: address(0)
66
+ });
67
+ }
68
+
69
+ function test_commentWithSmartWalletOwner_whenCreator() public {
70
+ address smartWallet = address(new MockMultiOwnable(address(collectorWithoutToken)));
71
+
72
+ uint256 tokenId = 10;
73
+ mock1155.createToken(tokenId, address(smartWallet));
74
+
75
+ IComments.CommentIdentifier memory expectedCommentIdentifier = _expectedCommentIdentifier(address(mock1155), tokenId, collectorWithoutToken);
76
+
77
+ vm.prank(collectorWithoutToken);
78
+ comments.comment({
79
+ commenter: collectorWithoutToken,
80
+ contractAddress: address(mock1155),
81
+ tokenId: tokenId,
82
+ text: "test comment",
83
+ replyTo: emptyCommentIdentifier,
84
+ commenterSmartWallet: smartWallet,
85
+ referrer: address(0)
86
+ });
87
+
88
+ // check that comment was created
89
+ (, bool exists) = comments.hashAndCheckCommentExists(expectedCommentIdentifier);
90
+ assertTrue(exists);
91
+ }
92
+
93
+ function test_commentWithSmartWalletOwner_revertsWhenNotHolder() public {
94
+ address smartWallet = address(new MockMultiOwnable(address(collectorWithoutToken)));
95
+
96
+ IComments.CommentIdentifier memory emptyReplyTo;
97
+
98
+ vm.expectRevert(abi.encodeWithSelector(IComments.NotTokenHolderOrAdmin.selector));
99
+ vm.prank(collectorWithoutToken);
100
+ vm.deal(collectorWithoutToken, SPARKS_VALUE);
101
+ comments.comment{value: SPARKS_VALUE}({
102
+ commenter: collectorWithoutToken,
103
+ contractAddress: address(mock1155),
104
+ tokenId: tokenId1,
105
+ text: "test comment",
106
+ replyTo: emptyReplyTo,
107
+ commenterSmartWallet: smartWallet,
108
+ referrer: address(0)
109
+ });
110
+ }
111
+
112
+ function test_commentWithSmartWalletOwner_revertsWhenNotOwner() public {
113
+ address smartWallet = address(new MockMultiOwnable(address(makeAddr("notOwner"))));
114
+
115
+ mock1155.mint(smartWallet, tokenId1, 1, "");
116
+
117
+ IComments.CommentIdentifier memory emptyReplyTo;
118
+
119
+ vm.expectRevert(IComments.NotSmartWalletOwner.selector);
120
+ vm.prank(collectorWithoutToken);
121
+ vm.deal(collectorWithoutToken, SPARKS_VALUE);
122
+ comments.comment{value: SPARKS_VALUE}({
123
+ commenter: collectorWithoutToken,
124
+ contractAddress: address(mock1155),
125
+ tokenId: tokenId1,
126
+ text: "test comment",
127
+ replyTo: emptyReplyTo,
128
+ commenterSmartWallet: smartWallet,
129
+ referrer: address(0)
130
+ });
131
+ }
132
+
133
+ function test_commentWithSmartWalletOwner_revertsWhenNotSmartWallet() public {
134
+ address smartWallet = makeAddr("smartWallet");
135
+
136
+ mock1155.mint(smartWallet, tokenId1, 1, "");
137
+
138
+ IComments.CommentIdentifier memory emptyReplyTo;
139
+
140
+ vm.expectRevert(IComments.NotSmartWallet.selector);
141
+ vm.prank(collectorWithoutToken);
142
+ comments.comment({
143
+ commenter: collectorWithoutToken,
144
+ contractAddress: address(mock1155),
145
+ tokenId: tokenId1,
146
+ text: "test comment",
147
+ replyTo: emptyReplyTo,
148
+ commenterSmartWallet: smartWallet,
149
+ referrer: address(0)
150
+ });
151
+ }
152
+ }
@@ -8,6 +8,9 @@ contract Mock1155 is ERC1155, IZoraCreator1155 {
8
8
  /// @notice This user role allows for any action to be performed
9
9
  uint256 public constant PERMISSION_BIT_ADMIN = 2 ** 1;
10
10
 
11
+ /// @notice Global contract configuration
12
+ ContractConfig public config;
13
+
11
14
  mapping(uint256 => address) public admins;
12
15
 
13
16
  constructor() ERC1155("") {}
@@ -40,11 +43,19 @@ contract Mock1155 is ERC1155, IZoraCreator1155 {
40
43
  return super.supportsInterface(interfaceId);
41
44
  }
42
45
 
43
- function mint(address to, uint256 id, uint256 amount, bytes memory data) external {
46
+ function mint(address to, uint256 id, uint256 amount, bytes memory data) external payable {
44
47
  if (!_tokenExists(id)) {
45
48
  revert("Token does not exist");
46
49
  }
47
50
 
48
51
  _mint(to, id, amount, data);
49
52
  }
53
+
54
+ function owner() external view returns (address) {
55
+ return config.owner;
56
+ }
57
+
58
+ function burn(address from, uint256 id, uint256 amount) external {
59
+ _safeTransferFrom(from, address(0), id, amount, "");
60
+ }
50
61
  }
@@ -0,0 +1,65 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.20;
3
+
4
+ import {ERC1155} from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
5
+ import {IZoraCreator1155} from "./MockIZoraCreator1155.sol";
6
+
7
+ contract Mock1155NoCreatorRewardRecipient is ERC1155, IZoraCreator1155 {
8
+ /// @notice This user role allows for any action to be performed
9
+ uint256 public constant PERMISSION_BIT_ADMIN = 2 ** 1;
10
+
11
+ /// @notice Global contract configuration
12
+ ContractConfig public config;
13
+
14
+ mapping(uint256 => address) public admins;
15
+
16
+ constructor() ERC1155("") {}
17
+
18
+ function isAdminOrRole(address user, uint256 tokenId, uint256 role) external view returns (bool) {
19
+ if (admins[tokenId] == user && role == PERMISSION_BIT_ADMIN) {
20
+ return true;
21
+ } else {
22
+ return false;
23
+ }
24
+ }
25
+
26
+ function _tokenExists(uint256 tokenId) internal view returns (bool) {
27
+ return admins[tokenId] != address(0);
28
+ }
29
+
30
+ function createToken(uint256 tokenId, address creator) external {
31
+ admins[tokenId] = creator;
32
+ }
33
+
34
+ function supportsInterface(bytes4 interfaceId) public view override(ERC1155, IZoraCreator1155) returns (bool) {
35
+ return super.supportsInterface(interfaceId);
36
+ }
37
+
38
+ function mint(address to, uint256 id, uint256 amount, bytes memory data) external {
39
+ if (!_tokenExists(id)) {
40
+ revert("Token does not exist");
41
+ }
42
+
43
+ _mint(to, id, amount, data);
44
+ }
45
+
46
+ function _setFundsRecipient(address payable fundsRecipient) internal {
47
+ config.fundsRecipient = fundsRecipient;
48
+ }
49
+
50
+ function setFundsRecipient(address payable fundsRecipient) external {
51
+ _setFundsRecipient(fundsRecipient);
52
+ }
53
+
54
+ function owner() external view returns (address) {
55
+ return config.owner;
56
+ }
57
+
58
+ function _setOwner(address newOwner) internal {
59
+ config.owner = newOwner;
60
+ }
61
+
62
+ function setOwner(address newOwner) external {
63
+ _setOwner(newOwner);
64
+ }
65
+ }
@@ -0,0 +1,53 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.20;
3
+
4
+ import {ERC1155} from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
5
+ import {IERC1155} from "@openzeppelin/contracts/interfaces/IERC1155.sol";
6
+ import {IZoraCreator1155TypesV1} from "../../src/interfaces/IZoraCreator1155TypesV1.sol";
7
+
8
+ // For testing without getCreatorRewardRecipient
9
+ interface IZoraCreator1155 is IERC1155, IZoraCreator1155TypesV1 {
10
+ function isAdminOrRole(address user, uint256 tokenId, uint256 role) external view returns (bool);
11
+
12
+ function supportsInterface(bytes4 interfaceId) external view returns (bool);
13
+ }
14
+
15
+ contract Mock1155NoOwner is ERC1155, IZoraCreator1155 {
16
+ /// @notice This user role allows for any action to be performed
17
+ uint256 public constant PERMISSION_BIT_ADMIN = 2 ** 1;
18
+
19
+ /// @notice Global contract configuration
20
+ ContractConfig public config;
21
+
22
+ mapping(uint256 => address) public admins;
23
+
24
+ constructor() ERC1155("") {}
25
+
26
+ function isAdminOrRole(address user, uint256 tokenId, uint256 role) external view returns (bool) {
27
+ if (admins[tokenId] == user && role == PERMISSION_BIT_ADMIN) {
28
+ return true;
29
+ } else {
30
+ return false;
31
+ }
32
+ }
33
+
34
+ function _tokenExists(uint256 tokenId) internal view returns (bool) {
35
+ return admins[tokenId] != address(0);
36
+ }
37
+
38
+ function createToken(uint256 tokenId, address creator) external {
39
+ admins[tokenId] = creator;
40
+ }
41
+
42
+ function supportsInterface(bytes4 interfaceId) public view override(ERC1155, IZoraCreator1155) returns (bool) {
43
+ return super.supportsInterface(interfaceId);
44
+ }
45
+
46
+ function mint(address to, uint256 id, uint256 amount, bytes memory data) external {
47
+ if (!_tokenExists(id)) {
48
+ revert("Token does not exist");
49
+ }
50
+
51
+ _mint(to, id, amount, data);
52
+ }
53
+ }