@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.
- package/.turbo/turbo-build.log +49 -31
- package/README.md +10 -39
- package/abis/AddDelegateCommenterRole.json +9 -0
- package/abis/CallerAndCommenter.json +62 -0
- package/abis/CallerAndCommenterImpl.json +1218 -0
- package/abis/CallerAndCommenterMintAndCommentTest.json +771 -0
- package/abis/CallerAndCommenterSwapAndCommentTest.json +844 -0
- package/abis/CallerAndCommenterTestBase.json +577 -0
- package/abis/CommentsImpl.json +189 -59
- package/abis/CommentsImplConstants.json +106 -0
- package/abis/CommentsPermitTest.json +26 -6
- package/abis/CommentsTest.json +58 -10
- package/abis/Comments_mintAndCommentTest.json +11 -4
- package/abis/Comments_smartWallet.json +711 -0
- package/abis/DeployCallerAndCommenterImpl.json +9 -0
- package/abis/DeployImpl.json +0 -13
- package/abis/DeployNonDeterministic.json +0 -13
- package/abis/DeployScript.json +0 -13
- package/abis/EIP712Upgradeable.json +74 -0
- package/abis/EIP712UpgradeableWithChainId.json +49 -0
- package/abis/ERC20.json +310 -0
- package/abis/GenerateDeterministicParams.json +0 -13
- package/abis/ICallerAndCommenter.json +797 -0
- package/abis/IComments.json +629 -9
- package/abis/IERC20.json +39 -42
- package/abis/IERC20Metadata.json +224 -0
- package/abis/{CommentsDeployerBase.json → IMultiOwnable.json} +8 -2
- package/abis/IProtocolRewards.json +19 -0
- package/abis/ISecondarySwap.json +45 -0
- package/abis/IZoraCreator1155.json +51 -0
- package/abis/IZoraTimedSaleStrategy.json +91 -0
- package/abis/Mock1155.json +75 -1
- package/abis/Mock1155NoCreatorRewardRecipient.json +605 -0
- package/abis/Mock1155NoOwner.json +566 -0
- package/abis/{MockMinter.json → MockDelegateCommenter.json} +12 -2
- package/abis/MockERC20z.json +315 -0
- package/abis/MockMultiOwnable.json +212 -0
- package/abis/MockSecondarySwap.json +95 -0
- package/abis/MockZoraTimedSale.json +139 -0
- package/abis/Ownable2StepUpgradeable.json +138 -0
- package/abis/UnorderedNoncesUpgradeable.json +4 -4
- package/addresses/1.json +9 -0
- package/addresses/10.json +9 -0
- package/addresses/11155111.json +9 -0
- package/addresses/11155420.json +9 -0
- package/addresses/42161.json +9 -0
- package/addresses/7777777.json +9 -0
- package/addresses/81457.json +9 -0
- package/addresses/8453.json +9 -0
- package/addresses/84532.json +9 -0
- package/addresses/999999999.json +7 -2
- package/deterministicConfig/callerAndCommenter.json +8 -0
- package/deterministicConfig/comments.json +2 -2
- package/dist/index.cjs +724 -35
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +723 -35
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/wagmiGenerated.d.ts +1102 -57
- package/dist/wagmiGenerated.d.ts.map +1 -1
- package/package/types.ts +4 -1
- package/package/wagmiGenerated.ts +728 -32
- package/package.json +12 -11
- package/script/AddDelegateCommenterRole.s.sol +24 -0
- package/script/CommentsDeployerBase.sol +102 -19
- package/script/Deploy.s.sol +2 -44
- package/script/DeployCallerAndCommenterImpl.s.sol +29 -0
- package/script/DeployImpl.s.sol +1 -0
- package/script/DeployNonDeterministic.s.sol +22 -13
- package/script/GenerateDeterministicParams.s.sol +32 -4
- package/scripts/generateCommentsTestData.ts +170 -79
- package/src/CommentsImpl.sol +267 -134
- package/src/CommentsImplConstants.sol +44 -0
- package/src/interfaces/ICallerAndCommenter.sol +215 -0
- package/src/interfaces/IComments.sol +189 -42
- package/src/interfaces/IMultiOwnable.sol +10 -0
- package/src/interfaces/ISecondarySwap.sol +40 -0
- package/src/interfaces/IZoraCreator1155.sol +6 -1
- package/src/interfaces/IZoraCreator1155TypesV1.sol +46 -0
- package/src/interfaces/IZoraTimedSaleStrategy.sol +25 -0
- package/src/proxy/CallerAndCommenter.sol +43 -0
- package/src/utils/CallerAndCommenterImpl.sol +376 -0
- package/src/utils/EIP712UpgradeableWithChainId.sol +12 -23
- package/src/version/ContractVersionBase.sol +1 -1
- package/test/CallerAndCommenterTestBase.sol +77 -0
- package/test/CallerAndCommenter_mintAndComment.t copy.sol +214 -0
- package/test/CallerAndCommenter_swapAndComment.t.sol +523 -0
- package/test/Comments.t.sol +166 -29
- package/test/CommentsTestBase.sol +12 -20
- package/test/Comments_delegateComment.t.sol +129 -0
- package/test/Comments_permit.t.sol +131 -44
- package/test/Comments_smartWallet.t.sol +152 -0
- package/test/mocks/Mock1155.sol +12 -1
- package/test/mocks/Mock1155NoCreatorRewardRecipient.sol +65 -0
- package/test/mocks/Mock1155NoOwner.sol +53 -0
- package/test/mocks/MockDelegateCommenter.sol +36 -0
- package/test/mocks/MockIZoraCreator1155.sol +16 -0
- package/test/mocks/MockSecondarySwap.sol +30 -0
- package/test/mocks/MockZoraTimedSale.sol +38 -0
- package/wagmi.config.ts +3 -1
- package/abis/ProxyDeployerScript.json +0 -15
- package/scripts/backfillComments.ts +0 -176
- package/scripts/queries.ts +0 -73
- package/scripts/queryAndSaveComments.ts +0 -48
- package/scripts/queryQuantityOfComments.ts +0 -53
- package/scripts/writeComments.ts +0 -198
- package/src/deployments/CommentsDeployment.sol +0 -14
- package/test/Comments_mintAndComment.t.sol +0 -101
- package/test/mocks/MockMinter.sol +0 -29
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.17;
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
░░░░░░░░░░░░░░
|
|
8
|
+
░░▒▒░░░░░░░░░░░░░░░░░░░░
|
|
9
|
+
░░▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░
|
|
10
|
+
░░▒▒▒▒░░░░░░░░░░░░░░ ░░░░░░░░
|
|
11
|
+
░▓▓▒▒▒▒░░░░░░░░░░░░ ░░░░░░░
|
|
12
|
+
░▓▓▓▒▒▒▒░░░░░░░░░░░░ ░░░░░░░░
|
|
13
|
+
░▓▓▓▒▒▒▒░░░░░░░░░░░░░░ ░░░░░░░░░░
|
|
14
|
+
░▓▓▓▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░
|
|
15
|
+
░▓▓▓▓▓▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░
|
|
16
|
+
░▓▓▓▓▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░
|
|
17
|
+
░░▓▓▓▓▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░
|
|
18
|
+
░░▓▓▓▓▓▓▒▒▒▒▒▒▒▒░░░░░░░░░▒▒▒▒▒░░
|
|
19
|
+
░░▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░
|
|
20
|
+
░░▓▓▓▓▓▓▓▓▓▓▓▓▒▒░░░
|
|
21
|
+
|
|
22
|
+
OURS TRULY,
|
|
23
|
+
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/// Imagine. Mint. Enjoy.
|
|
27
|
+
/// @notice Interface for types used across the ZoraCreator1155 contract
|
|
28
|
+
/// @author @iainnash / @tbtstl
|
|
29
|
+
interface IZoraCreator1155TypesV1 {
|
|
30
|
+
/// @notice Used to store individual token data
|
|
31
|
+
struct TokenData {
|
|
32
|
+
string uri;
|
|
33
|
+
uint256 maxSupply;
|
|
34
|
+
uint256 totalMinted;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/// @notice Used to store contract-level configuration
|
|
38
|
+
struct ContractConfig {
|
|
39
|
+
address owner;
|
|
40
|
+
uint96 __gap1;
|
|
41
|
+
address payable fundsRecipient;
|
|
42
|
+
uint96 __gap2;
|
|
43
|
+
address transferHook;
|
|
44
|
+
uint96 __gap3;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.23;
|
|
3
|
+
|
|
4
|
+
interface IZoraTimedSaleStrategy {
|
|
5
|
+
function mint(address mintTo, uint256 quantity, address collection, uint256 tokenId, address mintReferral, string calldata comment) external payable;
|
|
6
|
+
|
|
7
|
+
/// @dev This is the SaleV1 style sale with a set end and start time and is used in both cases for storing key sale information
|
|
8
|
+
struct SaleStorage {
|
|
9
|
+
/// @notice The ERC20z address
|
|
10
|
+
address payable erc20zAddress;
|
|
11
|
+
/// @notice The sale start time
|
|
12
|
+
uint64 saleStart;
|
|
13
|
+
/// @notice The Uniswap pool address
|
|
14
|
+
address poolAddress;
|
|
15
|
+
/// @notice The sale end time
|
|
16
|
+
uint64 saleEnd;
|
|
17
|
+
/// @notice Boolean if the secondary market has been launched
|
|
18
|
+
bool secondaryActivated;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/// @notice Returns the sale config for a given token
|
|
22
|
+
/// @param collection The collection address
|
|
23
|
+
/// @param tokenId The ID of the token to get the sale config for
|
|
24
|
+
function sale(address collection, uint256 tokenId) external view returns (SaleStorage memory);
|
|
25
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.23;
|
|
3
|
+
|
|
4
|
+
import {Enjoy} from "_imagine/Enjoy.sol";
|
|
5
|
+
import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
|
|
6
|
+
|
|
7
|
+
/*
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
░░░░░░░░░░░░░░
|
|
11
|
+
░░▒▒░░░░░░░░░░░░░░░░░░░░
|
|
12
|
+
░░▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░
|
|
13
|
+
░░▒▒▒▒░░░░░░░░░░░░░░ ░░░░░░░░
|
|
14
|
+
░▓▓▒▒▒▒░░░░░░░░░░░░ ░░░░░░░
|
|
15
|
+
░▓▓▓▒▒▒▒░░░░░░░░░░░░ ░░░░░░░░
|
|
16
|
+
░▓▓▓▒▒▒▒░░░░░░░░░░░░░░ ░░░░░░░░░░
|
|
17
|
+
░▓▓▓▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░
|
|
18
|
+
░▓▓▓▓▓▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░
|
|
19
|
+
░▓▓▓▓▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░
|
|
20
|
+
░░▓▓▓▓▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░
|
|
21
|
+
░░▓▓▓▓▓▓▒▒▒▒▒▒▒▒░░░░░░░░░▒▒▒▒▒░░
|
|
22
|
+
░░▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░
|
|
23
|
+
░░▓▓▓▓▓▓▓▓▓▓▓▓▒▒░░░
|
|
24
|
+
|
|
25
|
+
OURS TRULY,
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/// @title Zora Comments Proxy Contract
|
|
31
|
+
/// @notice This is the proxy contract for the CallerAndCommenter contract.
|
|
32
|
+
/// @dev Inherits from ERC1967Proxy to enable upgradeable functionality.
|
|
33
|
+
/// @notice Imagine. Mint. Enjoy.
|
|
34
|
+
/// @author @oveddan
|
|
35
|
+
contract CallerAndCommenter is Enjoy, ERC1967Proxy {
|
|
36
|
+
bytes32 internal immutable name;
|
|
37
|
+
|
|
38
|
+
constructor(address _logic) ERC1967Proxy(_logic, "") {
|
|
39
|
+
// added to create unique bytecode for this contract
|
|
40
|
+
// so that it can be properly verified as its own contract.
|
|
41
|
+
name = keccak256("CallerAndCommenter");
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.23;
|
|
3
|
+
|
|
4
|
+
import {IComments} from "../interfaces/IComments.sol";
|
|
5
|
+
import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
|
|
6
|
+
import {EIP712UpgradeableWithChainId} from "./EIP712UpgradeableWithChainId.sol";
|
|
7
|
+
import {UnorderedNoncesUpgradeable} from "@zoralabs/shared-contracts/utils/UnorderedNoncesUpgradeable.sol";
|
|
8
|
+
import {SignatureChecker} from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";
|
|
9
|
+
import {IZoraTimedSaleStrategy} from "../interfaces/IZoraTimedSaleStrategy.sol";
|
|
10
|
+
import {ICallerAndCommenter} from "../interfaces/ICallerAndCommenter.sol";
|
|
11
|
+
import {ContractVersionBase} from "../version/ContractVersionBase.sol";
|
|
12
|
+
import {IHasContractName} from "@zoralabs/shared-contracts/interfaces/IContractMetadata.sol";
|
|
13
|
+
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
|
|
14
|
+
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
|
|
15
|
+
import {ISecondarySwap} from "../interfaces/ISecondarySwap.sol";
|
|
16
|
+
import {IERC1155} from "@openzeppelin/contracts/interfaces/IERC1155.sol";
|
|
17
|
+
|
|
18
|
+
/// @title Calls contracts and allows a user to add a comment to be associated with the call.
|
|
19
|
+
/// @author oveddan
|
|
20
|
+
/// @dev Upgradeable contract. Is given permission to delegateComment on the Comments contract,
|
|
21
|
+
/// meaning it is trusted to indicate who a comment is from.
|
|
22
|
+
contract CallerAndCommenterImpl is
|
|
23
|
+
ICallerAndCommenter,
|
|
24
|
+
Ownable2StepUpgradeable,
|
|
25
|
+
EIP712UpgradeableWithChainId,
|
|
26
|
+
UnorderedNoncesUpgradeable,
|
|
27
|
+
ContractVersionBase,
|
|
28
|
+
UUPSUpgradeable,
|
|
29
|
+
IHasContractName
|
|
30
|
+
{
|
|
31
|
+
IComments public immutable comments;
|
|
32
|
+
IZoraTimedSaleStrategy public immutable zoraTimedSale;
|
|
33
|
+
ISecondarySwap public immutable secondarySwap;
|
|
34
|
+
uint256 public immutable sparkValue;
|
|
35
|
+
|
|
36
|
+
IComments.CommentIdentifier internal emptyCommentIdentifier;
|
|
37
|
+
|
|
38
|
+
string constant DOMAIN_NAME = "CallerAndCommenter";
|
|
39
|
+
string constant DOMAIN_VERSION = "1";
|
|
40
|
+
|
|
41
|
+
IComments.CommentIdentifier internal emptyReplyTo;
|
|
42
|
+
|
|
43
|
+
constructor(address _comments, address _zoraTimedSale, address _swapHelper, uint256 _sparksValue) {
|
|
44
|
+
comments = IComments(_comments);
|
|
45
|
+
zoraTimedSale = IZoraTimedSaleStrategy(_zoraTimedSale);
|
|
46
|
+
secondarySwap = ISecondarySwap(_swapHelper);
|
|
47
|
+
sparkValue = _sparksValue;
|
|
48
|
+
_disableInitializers();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/// Initializes the upgradeable contract
|
|
52
|
+
/// @param owner of the contract that can perform upgrades
|
|
53
|
+
function initialize(address owner) external initializer {
|
|
54
|
+
__Ownable_init(owner);
|
|
55
|
+
__UUPSUpgradeable_init();
|
|
56
|
+
__EIP712_init(DOMAIN_NAME, DOMAIN_VERSION);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/// @notice Mints tokens and adds a comment, without needing to pay a spark for the comment.
|
|
60
|
+
/// @dev The payable amount should be the total mint fee. No spark value should be sent.
|
|
61
|
+
/// @param quantity The number of tokens to mint
|
|
62
|
+
/// @param collection The address of the 1155 collection to mint from
|
|
63
|
+
/// @param tokenId The 1155 token Id to mint
|
|
64
|
+
/// @param mintReferral The address to receive mint referral rewards, if any
|
|
65
|
+
/// @param comment The comment to be added. If empty, no comment will be added.
|
|
66
|
+
/// @return The identifier of the newly created comment
|
|
67
|
+
function timedSaleMintAndComment(
|
|
68
|
+
address commenter,
|
|
69
|
+
uint256 quantity,
|
|
70
|
+
address collection,
|
|
71
|
+
uint256 tokenId,
|
|
72
|
+
address mintReferral,
|
|
73
|
+
string calldata comment
|
|
74
|
+
) external payable returns (IComments.CommentIdentifier memory) {
|
|
75
|
+
if (commenter != msg.sender) {
|
|
76
|
+
revert CommenterMismatch(msg.sender, commenter);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return _timedSaleMintAndComment(commenter, quantity, collection, tokenId, mintReferral, comment);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function _timedSaleMintAndComment(
|
|
83
|
+
address commenter,
|
|
84
|
+
uint256 quantity,
|
|
85
|
+
address collection,
|
|
86
|
+
uint256 tokenId,
|
|
87
|
+
address mintReferral,
|
|
88
|
+
string calldata comment
|
|
89
|
+
) internal returns (IComments.CommentIdentifier memory commentIdentifier) {
|
|
90
|
+
zoraTimedSale.mint{value: msg.value}(commenter, quantity, collection, tokenId, mintReferral, "");
|
|
91
|
+
|
|
92
|
+
if (bytes(comment).length > 0) {
|
|
93
|
+
bytes32 commentId;
|
|
94
|
+
(commentIdentifier, commentId) = comments.delegateComment(commenter, collection, tokenId, comment, emptyReplyTo, address(0), address(0));
|
|
95
|
+
|
|
96
|
+
emit MintedAndCommented(commentId, commentIdentifier, quantity, comment);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
bytes32 constant PERMIT_TIMED_SALE_MINT_AND_COMMENT_DOMAIN =
|
|
101
|
+
keccak256(
|
|
102
|
+
"PermitTimedSaleMintAndComment(address commenter,uint256 quantity,address collection,uint256 tokenId,address mintReferral,string comment,uint256 deadline,bytes32 nonce,uint32 sourceChainId,uint32 destinationChainId)"
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
/// @notice Hashes the permit data for a timed sale mint and comment operation
|
|
106
|
+
/// @param permit The PermitTimedSaleMintAndComment struct containing the permit data
|
|
107
|
+
/// @return bytes32 The hash of the permit data for signing
|
|
108
|
+
function hashPermitTimedSaleMintAndComment(PermitTimedSaleMintAndComment memory permit) public view returns (bytes32) {
|
|
109
|
+
bytes32 structHash = keccak256(
|
|
110
|
+
abi.encode(
|
|
111
|
+
PERMIT_TIMED_SALE_MINT_AND_COMMENT_DOMAIN,
|
|
112
|
+
permit.commenter,
|
|
113
|
+
permit.quantity,
|
|
114
|
+
permit.collection,
|
|
115
|
+
permit.tokenId,
|
|
116
|
+
permit.mintReferral,
|
|
117
|
+
keccak256(bytes(permit.comment)),
|
|
118
|
+
permit.deadline,
|
|
119
|
+
permit.nonce,
|
|
120
|
+
permit.sourceChainId,
|
|
121
|
+
permit.destinationChainId
|
|
122
|
+
)
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
return _hashTypedDataV4(structHash, permit.sourceChainId);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function _validateSignature(bytes32 digest, bytes calldata signature, address signer) internal view {
|
|
129
|
+
if (!SignatureChecker.isValidSignatureNow(signer, digest, signature)) {
|
|
130
|
+
revert InvalidSignature();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function _validateAndUsePermit(
|
|
135
|
+
bytes32 digest,
|
|
136
|
+
bytes32 nonce,
|
|
137
|
+
bytes calldata signature,
|
|
138
|
+
address signer,
|
|
139
|
+
uint256 deadline,
|
|
140
|
+
uint32 destinationChainId
|
|
141
|
+
) internal {
|
|
142
|
+
if (block.timestamp > deadline) {
|
|
143
|
+
revert ERC2612ExpiredSignature(deadline);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (destinationChainId != uint32(block.chainid)) {
|
|
147
|
+
revert IncorrectDestinationChain(destinationChainId);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
_useCheckedNonce(signer, nonce);
|
|
151
|
+
_validateSignature(digest, signature, signer);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/// @notice Mints tokens and adds a comment, without needing to pay a spark for the comment. Attributes the
|
|
155
|
+
/// comment to the signer of the message. Meant to be used for cross-chain commenting. where a permit
|
|
156
|
+
/// @dev The signer must match the commenter field in the permit.
|
|
157
|
+
/// @param permit The PermitTimedSaleMintAndComment struct containing the permit data
|
|
158
|
+
/// @param signature The signature of the permit
|
|
159
|
+
/// @return The identifier of the newly created comment
|
|
160
|
+
function permitTimedSaleMintAndComment(
|
|
161
|
+
PermitTimedSaleMintAndComment calldata permit,
|
|
162
|
+
bytes calldata signature
|
|
163
|
+
) public payable returns (IComments.CommentIdentifier memory) {
|
|
164
|
+
bytes32 digest = hashPermitTimedSaleMintAndComment(permit);
|
|
165
|
+
|
|
166
|
+
_validateAndUsePermit(digest, permit.nonce, signature, permit.commenter, permit.deadline, permit.destinationChainId);
|
|
167
|
+
|
|
168
|
+
return _timedSaleMintAndComment(permit.commenter, permit.quantity, permit.collection, permit.tokenId, permit.mintReferral, permit.comment);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/// @notice Buys Zora 1155 tokens on secondary market and adds a comment, without needing to pay a spark for the comment.
|
|
172
|
+
/// @param commenter The address of the commenter. Must match the msg.sender. Commenter will be the recipient of the bought tokens.
|
|
173
|
+
/// @param quantity The number of tokens to buy
|
|
174
|
+
/// @param collection The address of the 1155 collection
|
|
175
|
+
/// @param tokenId The 1155 token Id to buy
|
|
176
|
+
/// @param excessRefundRecipient The address to receive any excess ETH refund
|
|
177
|
+
/// @param maxEthToSpend The maximum amount of ETH to spend on the purchase
|
|
178
|
+
/// @param sqrtPriceLimitX96 The sqrt price limit for the swap
|
|
179
|
+
/// @param comment The comment to be added
|
|
180
|
+
/// @return The identifier of the newly created comment
|
|
181
|
+
/// @dev This function can only be called by the commenter themselves
|
|
182
|
+
function buyOnSecondaryAndComment(
|
|
183
|
+
address commenter,
|
|
184
|
+
uint256 quantity,
|
|
185
|
+
address collection,
|
|
186
|
+
uint256 tokenId,
|
|
187
|
+
address payable excessRefundRecipient,
|
|
188
|
+
uint256 maxEthToSpend,
|
|
189
|
+
uint160 sqrtPriceLimitX96,
|
|
190
|
+
string calldata comment
|
|
191
|
+
) external payable returns (IComments.CommentIdentifier memory) {
|
|
192
|
+
if (commenter != msg.sender) {
|
|
193
|
+
revert CommenterMismatch(msg.sender, commenter);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return _buyOnSecondaryAndComment(commenter, quantity, collection, tokenId, excessRefundRecipient, maxEthToSpend, sqrtPriceLimitX96, comment);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function _buyOnSecondaryAndComment(
|
|
200
|
+
address commenter,
|
|
201
|
+
uint256 quantity,
|
|
202
|
+
address collection,
|
|
203
|
+
uint256 tokenId,
|
|
204
|
+
address payable excessRefundRecipient,
|
|
205
|
+
uint256 maxEthToSpend,
|
|
206
|
+
uint160 sqrtPriceLimitX96,
|
|
207
|
+
string calldata comment
|
|
208
|
+
) internal returns (IComments.CommentIdentifier memory commentIdentifier) {
|
|
209
|
+
address erc20zAddress = zoraTimedSale.sale(collection, tokenId).erc20zAddress;
|
|
210
|
+
if (erc20zAddress == address(0)) {
|
|
211
|
+
revert SaleNotSet(collection, tokenId);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
secondarySwap.buy1155{value: msg.value}({
|
|
215
|
+
erc20zAddress: erc20zAddress,
|
|
216
|
+
num1155ToBuy: quantity,
|
|
217
|
+
recipient: payable(commenter),
|
|
218
|
+
excessRefundRecipient: excessRefundRecipient,
|
|
219
|
+
maxEthToSpend: maxEthToSpend,
|
|
220
|
+
sqrtPriceLimitX96: sqrtPriceLimitX96
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
if (bytes(comment).length > 0) {
|
|
224
|
+
bytes32 commentId;
|
|
225
|
+
(commentIdentifier, commentId) = comments.delegateComment(commenter, collection, tokenId, comment, emptyReplyTo, address(0), address(0));
|
|
226
|
+
|
|
227
|
+
emit SwappedOnSecondaryAndCommented(commentId, commentIdentifier, quantity, comment, SwapDirection.BUY);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return commentIdentifier;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
bytes32 constant PERMIT_BUY_ON_SECONDARY_AND_COMMENT_DOMAIN =
|
|
234
|
+
keccak256(
|
|
235
|
+
"PermitBuyOnSecondaryAndComment(address commenter,uint256 quantity,address collection,uint256 tokenId,uint256 maxEthToSpend,uint160 sqrtPriceLimitX96,string comment,uint256 deadline,bytes32 nonce,uint32 sourceChainId,uint32 destinationChainId)"
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
function _hashPermitBuyOnSecondaryAndComment(PermitBuyOnSecondaryAndComment memory permit) internal pure returns (bytes memory) {
|
|
239
|
+
return
|
|
240
|
+
abi.encode(
|
|
241
|
+
PERMIT_BUY_ON_SECONDARY_AND_COMMENT_DOMAIN,
|
|
242
|
+
permit.commenter,
|
|
243
|
+
permit.quantity,
|
|
244
|
+
permit.collection,
|
|
245
|
+
permit.tokenId,
|
|
246
|
+
permit.maxEthToSpend,
|
|
247
|
+
permit.sqrtPriceLimitX96,
|
|
248
|
+
keccak256(bytes(permit.comment)),
|
|
249
|
+
permit.deadline,
|
|
250
|
+
permit.nonce,
|
|
251
|
+
permit.sourceChainId,
|
|
252
|
+
permit.destinationChainId
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/// @notice Hashes the permit data for a buy on secondary and comment operation
|
|
257
|
+
/// @param permit The PermitBuyOnSecondaryAndComment struct containing the permit data
|
|
258
|
+
/// @return bytes32 The hash of the permit data for signing
|
|
259
|
+
function hashPermitBuyOnSecondaryAndComment(PermitBuyOnSecondaryAndComment memory permit) public view returns (bytes32) {
|
|
260
|
+
return _hashTypedDataV4(keccak256(_hashPermitBuyOnSecondaryAndComment(permit)), permit.sourceChainId);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/// @notice Buys tokens on secondary market and adds a comment, without needing to pay a spark for the comment. Attributes the
|
|
264
|
+
/// comment to the signer of the message. Meant to be used for cross-chain commenting where a permit is used.
|
|
265
|
+
/// @dev The signer must match the commenter field in the permit.
|
|
266
|
+
/// @param permit The PermitBuyOnSecondaryAndComment struct containing the permit data
|
|
267
|
+
/// @param signature The signature of the permit
|
|
268
|
+
/// @return The identifier of the newly created comment
|
|
269
|
+
function permitBuyOnSecondaryAndComment(
|
|
270
|
+
PermitBuyOnSecondaryAndComment calldata permit,
|
|
271
|
+
bytes calldata signature
|
|
272
|
+
) public payable returns (IComments.CommentIdentifier memory) {
|
|
273
|
+
bytes32 digest = hashPermitBuyOnSecondaryAndComment(permit);
|
|
274
|
+
|
|
275
|
+
_validateAndUsePermit(digest, permit.nonce, signature, permit.commenter, permit.deadline, permit.destinationChainId);
|
|
276
|
+
|
|
277
|
+
return
|
|
278
|
+
_buyOnSecondaryAndComment(
|
|
279
|
+
permit.commenter,
|
|
280
|
+
permit.quantity,
|
|
281
|
+
permit.collection,
|
|
282
|
+
permit.tokenId,
|
|
283
|
+
payable(permit.commenter),
|
|
284
|
+
permit.maxEthToSpend,
|
|
285
|
+
permit.sqrtPriceLimitX96,
|
|
286
|
+
permit.comment
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/// @notice Sells Zora 1155 tokens on secondary market and adds a comment.
|
|
291
|
+
/// @dev Must sent ETH value of one spark for the comment. Commenter must have approved this contract to transfer the tokens
|
|
292
|
+
/// on the 1155 contract.
|
|
293
|
+
/// @param commenter The address of the commenter. Must match the msg.sender. Commenter will be the seller of the tokens.
|
|
294
|
+
/// @param quantity The number of tokens to sell
|
|
295
|
+
/// @param collection The address of the 1155 collection
|
|
296
|
+
/// @param tokenId The 1155 token Id to sell
|
|
297
|
+
/// @param recipient The address to receive the ETH proceeds
|
|
298
|
+
/// @param minEthToAcquire The minimum amount of ETH to receive from the sale
|
|
299
|
+
/// @param sqrtPriceLimitX96 The sqrt price limit for the swap
|
|
300
|
+
/// @param comment The comment to be added
|
|
301
|
+
/// @return commentIdentifier The identifier of the newly created comment
|
|
302
|
+
/// @dev This function can only be called by the commenter themselves
|
|
303
|
+
function sellOnSecondaryAndComment(
|
|
304
|
+
address commenter,
|
|
305
|
+
uint256 quantity,
|
|
306
|
+
address collection,
|
|
307
|
+
uint256 tokenId,
|
|
308
|
+
address payable recipient,
|
|
309
|
+
uint256 minEthToAcquire,
|
|
310
|
+
uint160 sqrtPriceLimitX96,
|
|
311
|
+
string calldata comment
|
|
312
|
+
) external payable returns (IComments.CommentIdentifier memory commentIdentifier) {
|
|
313
|
+
if (commenter != msg.sender) {
|
|
314
|
+
revert CommenterMismatch(msg.sender, commenter);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (bytes(comment).length == 0) {
|
|
318
|
+
// if we are not sending a comment, we should not send any ETH
|
|
319
|
+
if (msg.value != 0) {
|
|
320
|
+
revert WrongValueSent(0, msg.value);
|
|
321
|
+
}
|
|
322
|
+
} else {
|
|
323
|
+
// if we are sending a comment, we should be required to send one spark
|
|
324
|
+
if (msg.value != sparkValue) {
|
|
325
|
+
revert WrongValueSent(sparkValue, msg.value);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
bytes32 commentId;
|
|
329
|
+
// submit the comment, attaching the spark value if it is sent
|
|
330
|
+
(commentIdentifier, commentId) = comments.delegateComment{value: msg.value}(
|
|
331
|
+
commenter,
|
|
332
|
+
collection,
|
|
333
|
+
tokenId,
|
|
334
|
+
comment,
|
|
335
|
+
emptyReplyTo,
|
|
336
|
+
address(0),
|
|
337
|
+
address(0)
|
|
338
|
+
);
|
|
339
|
+
|
|
340
|
+
emit SwappedOnSecondaryAndCommented(commentId, commentIdentifier, quantity, comment, SwapDirection.SELL);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// wrapped around brackets to prevent stack too deep error
|
|
344
|
+
{
|
|
345
|
+
// transfer the tokens to the secondary swap
|
|
346
|
+
IERC1155(collection).safeTransferFrom(
|
|
347
|
+
// transferring from the commenter to the secondary swap contract.
|
|
348
|
+
// commenter must have approved this contract to transfer tokens.
|
|
349
|
+
address(commenter),
|
|
350
|
+
address(secondarySwap),
|
|
351
|
+
tokenId,
|
|
352
|
+
quantity,
|
|
353
|
+
abi.encode(recipient, minEthToAcquire, sqrtPriceLimitX96)
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
return commentIdentifier;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/// @notice Returns the name of the contract
|
|
361
|
+
/// @return The name of the contract
|
|
362
|
+
function contractName() public pure returns (string memory) {
|
|
363
|
+
return "Caller and Commenter";
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function _authorizeUpgrade(address newImplementation) internal override onlyOwner {
|
|
367
|
+
// check that new implementation's contract name matches the current contract name
|
|
368
|
+
if (!Strings.equal(IHasContractName(newImplementation).contractName(), this.contractName())) {
|
|
369
|
+
revert UpgradeToMismatchedContractName(this.contractName(), IHasContractName(newImplementation).contractName());
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function _equals(string memory a, string memory b) internal pure returns (bool) {
|
|
374
|
+
return (keccak256(bytes(a)) == keccak256(bytes(b)));
|
|
375
|
+
}
|
|
376
|
+
}
|
|
@@ -1,36 +1,25 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
pragma solidity ^0.8.20;
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {EIP712Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol";
|
|
5
|
+
import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
/// Extension of EIP712Upgradeable that allows for messages to be signed on other chains.
|
|
8
|
+
abstract contract EIP712UpgradeableWithChainId is EIP712Upgradeable {
|
|
7
9
|
bytes32 private constant TYPE_HASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
bytes32 private constant EIP712StorageLocation = 0xa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d100;
|
|
15
|
-
|
|
16
|
-
function _getEIP712Storage() private pure returns (EIP712Storage storage $) {
|
|
17
|
-
assembly {
|
|
18
|
-
$.slot := EIP712StorageLocation
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function __EIP712_init(string memory name, string memory version) internal onlyInitializing {
|
|
23
|
-
EIP712Storage storage $ = _getEIP712Storage();
|
|
24
|
-
$._name = name;
|
|
25
|
-
$._version = version;
|
|
11
|
+
/**
|
|
12
|
+
* @dev Returns the domain separator for the current chain.
|
|
13
|
+
*/
|
|
14
|
+
function _domainSeparatorV4(uint256 chainId) internal view returns (bytes32) {
|
|
15
|
+
return _buildDomainSeparator(chainId);
|
|
26
16
|
}
|
|
27
17
|
|
|
28
|
-
function
|
|
29
|
-
|
|
30
|
-
return keccak256(abi.encode(TYPE_HASH, keccak256(bytes($._name)), keccak256(bytes($._version)), chainId, address(this)));
|
|
18
|
+
function _buildDomainSeparator(uint256 chainId) private view returns (bytes32) {
|
|
19
|
+
return keccak256(abi.encode(TYPE_HASH, _EIP712NameHash(), _EIP712VersionHash(), chainId, address(this)));
|
|
31
20
|
}
|
|
32
21
|
|
|
33
22
|
function _hashTypedDataV4(bytes32 structHash, uint256 chainId) internal view returns (bytes32) {
|
|
34
|
-
return
|
|
23
|
+
return MessageHashUtils.toTypedDataHash(_domainSeparatorV4(chainId), structHash);
|
|
35
24
|
}
|
|
36
25
|
}
|
|
@@ -9,6 +9,6 @@ import {IVersionedContract} from "@zoralabs/shared-contracts/interfaces/IVersion
|
|
|
9
9
|
contract ContractVersionBase is IVersionedContract {
|
|
10
10
|
/// @notice The version of the contract
|
|
11
11
|
function contractVersion() external pure override returns (string memory) {
|
|
12
|
-
return "0.0.
|
|
12
|
+
return "0.0.2";
|
|
13
13
|
}
|
|
14
14
|
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.20;
|
|
3
|
+
|
|
4
|
+
import {Test} from "forge-std/Test.sol";
|
|
5
|
+
|
|
6
|
+
import {CallerAndCommenterImpl} from "../src/utils/CallerAndCommenterImpl.sol";
|
|
7
|
+
import {CommentsImpl} from "../src/CommentsImpl.sol";
|
|
8
|
+
import {Comments} from "../src/proxy/Comments.sol";
|
|
9
|
+
import {IComments} from "../src/interfaces/IComments.sol";
|
|
10
|
+
import {Mock1155} from "./mocks/Mock1155.sol";
|
|
11
|
+
import {MockZoraTimedSale} from "./mocks/MockZoraTimedSale.sol";
|
|
12
|
+
import {MockSecondarySwap} from "./mocks/MockSecondarySwap.sol";
|
|
13
|
+
import {ICallerAndCommenter} from "../src/interfaces/ICallerAndCommenter.sol";
|
|
14
|
+
import {CallerAndCommenter} from "../src/proxy/CallerAndCommenter.sol";
|
|
15
|
+
|
|
16
|
+
contract CallerAndCommenterTestBase is Test {
|
|
17
|
+
uint256 constant SPARKS_VALUE = 0.000001 ether;
|
|
18
|
+
|
|
19
|
+
address zoraRecipient = makeAddr("zoraRecipient");
|
|
20
|
+
address commentsAdmin = makeAddr("commentsAdmin");
|
|
21
|
+
address commenter;
|
|
22
|
+
uint256 commenterPrivateKey;
|
|
23
|
+
address backfiller = makeAddr("backfiller");
|
|
24
|
+
address tokenAdmin = makeAddr("tokenAdmin");
|
|
25
|
+
address protocolRewards = 0x7777777F279eba3d3Ad8F4E708545291A6fDBA8B;
|
|
26
|
+
|
|
27
|
+
IComments.CommentIdentifier emptyCommentIdentifier;
|
|
28
|
+
|
|
29
|
+
MockSecondarySwap mockSecondarySwap;
|
|
30
|
+
|
|
31
|
+
IComments comments;
|
|
32
|
+
|
|
33
|
+
Mock1155 mock1155;
|
|
34
|
+
|
|
35
|
+
uint256 tokenId1 = 1;
|
|
36
|
+
|
|
37
|
+
MockZoraTimedSale mockMinter;
|
|
38
|
+
ICallerAndCommenter callerAndCommenter;
|
|
39
|
+
|
|
40
|
+
function setUp() public {
|
|
41
|
+
vm.createSelectFork("zora_sepolia", 16028863);
|
|
42
|
+
|
|
43
|
+
(commenter, commenterPrivateKey) = makeAddrAndKey("commenter");
|
|
44
|
+
|
|
45
|
+
mock1155 = new Mock1155();
|
|
46
|
+
mock1155.createToken(tokenId1, tokenAdmin);
|
|
47
|
+
|
|
48
|
+
CommentsImpl commentsImpl = new CommentsImpl(SPARKS_VALUE, protocolRewards, zoraRecipient);
|
|
49
|
+
|
|
50
|
+
mockMinter = new MockZoraTimedSale();
|
|
51
|
+
mockSecondarySwap = new MockSecondarySwap(mockMinter);
|
|
52
|
+
|
|
53
|
+
comments = IComments(payable(address(new Comments(address(commentsImpl)))));
|
|
54
|
+
|
|
55
|
+
CallerAndCommenterImpl callerAndCommenterImpl = new CallerAndCommenterImpl(
|
|
56
|
+
address(comments),
|
|
57
|
+
address(mockMinter),
|
|
58
|
+
address(mockSecondarySwap),
|
|
59
|
+
SPARKS_VALUE
|
|
60
|
+
);
|
|
61
|
+
callerAndCommenter = ICallerAndCommenter(payable(address(new CallerAndCommenter(address(callerAndCommenterImpl)))));
|
|
62
|
+
|
|
63
|
+
address[] memory delegateCommenters = new address[](1);
|
|
64
|
+
delegateCommenters[0] = address(callerAndCommenter);
|
|
65
|
+
|
|
66
|
+
comments.initialize(commentsAdmin, backfiller, delegateCommenters);
|
|
67
|
+
callerAndCommenter.initialize(commentsAdmin);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function _expectedCommentIdentifier(
|
|
71
|
+
address _commenter,
|
|
72
|
+
address contractAddress,
|
|
73
|
+
uint256 tokenId
|
|
74
|
+
) internal view returns (IComments.CommentIdentifier memory) {
|
|
75
|
+
return IComments.CommentIdentifier({commenter: _commenter, contractAddress: contractAddress, tokenId: tokenId, nonce: comments.nextNonce()});
|
|
76
|
+
}
|
|
77
|
+
}
|