Skip to content

Commit 3a12cb5

Browse files
enterprise contract (#26)
* create base contract for contentsign * ClickContentSign based on BaseContentSign * add enterprise contentsign
1 parent 3878007 commit 3a12cb5

File tree

7 files changed

+167
-19
lines changed

7 files changed

+167
-19
lines changed

script/DeployContentSign.s.sol

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ pragma solidity ^0.8.20;
44
import {Script, console} from "forge-std/Script.sol";
55

66
import {WhitelistPaymaster} from "../src/paymasters/WhitelistPaymaster.sol";
7-
import {ContentSignNFT} from "../src/contentsign/ContentSignNFT.sol";
7+
import {ClickContentSign} from "../src/contentsign/ClickContentSign.sol";
88

99
contract DeployContentSign is Script {
1010
address internal whitelistAdmin;
@@ -22,7 +22,7 @@ contract DeployContentSign is Script {
2222
vm.startBroadcast();
2323

2424
WhitelistPaymaster paymaster = new WhitelistPaymaster(withdrawer);
25-
ContentSignNFT nft = new ContentSignNFT("ContentSign", "SIGNED", paymaster);
25+
ClickContentSign nft = new ClickContentSign("ContentSign", "SIGNED", paymaster);
2626

2727
address[] memory whitelist = new address[](1);
2828
whitelist[0] = address(nft);
@@ -35,7 +35,7 @@ contract DeployContentSign is Script {
3535

3636
vm.stopBroadcast();
3737

38-
console.log("Deployed ContentSignNFT at %s", address(nft));
38+
console.log("Deployed ClickContentSign at %s", address(nft));
3939
console.log("Deployed WhitelistPaymaster at %s", address(paymaster));
4040
console.log("Please ensure you fund the paymaster contract with enough ETH!");
4141
}
Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,13 @@ import {ERC721URIStorage} from "openzeppelin-contracts/contracts/token/ERC721/ex
88
import {WhitelistPaymaster} from "../paymasters/WhitelistPaymaster.sol";
99

1010
/// @notice a simple NFT contract for contentsign data where each nft is mapped to a one-time
11-
/// configurable URL
12-
contract ContentSignNFT is ERC721, ERC721URIStorage {
11+
/// configurable URL. This is used for every variant of ContentSign with associated hooks.
12+
abstract contract BaseContentSign is ERC721, ERC721URIStorage {
1313
uint256 public nextTokenId;
14-
WhitelistPaymaster public whitelistPaymaster;
1514

16-
error UserIsNotWhitelisted();
15+
error UserIsNotWhitelisted(address user);
1716

18-
constructor(string memory name, string memory symbol, WhitelistPaymaster whitelist) ERC721(name, symbol) {
19-
whitelistPaymaster = whitelist;
20-
}
17+
constructor(string memory name, string memory symbol) ERC721(name, symbol) {}
2118

2219
function safeMint(address to, string memory uri) public {
2320
_mustBeWhitelisted();
@@ -31,13 +28,21 @@ contract ContentSignNFT is ERC721, ERC721URIStorage {
3128
return super.tokenURI(tokenId);
3229
}
3330

34-
function supportsInterface(bytes4 interfaceId) public view override(ERC721, ERC721URIStorage) returns (bool) {
35-
return super.supportsInterface(interfaceId);
31+
function supportsInterface(bytes4 interfaceId)
32+
public
33+
view
34+
virtual
35+
override(ERC721, ERC721URIStorage)
36+
returns (bool)
37+
{
38+
return ERC721.supportsInterface(interfaceId) || ERC721URIStorage.supportsInterface(interfaceId);
3639
}
3740

3841
function _mustBeWhitelisted() internal view {
39-
if (!whitelistPaymaster.isWhitelistedUser(msg.sender)) {
40-
revert UserIsNotWhitelisted();
42+
if (!_userIsWhitelisted(msg.sender)) {
43+
revert UserIsNotWhitelisted(msg.sender);
4144
}
4245
}
46+
47+
function _userIsWhitelisted(address user) internal view virtual returns (bool);
4348
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// SPDX-License-Identifier: BSD-3-Clause-Clear
2+
3+
pragma solidity ^0.8.20;
4+
5+
import {BaseContentSign} from "./BaseContentSign.sol";
6+
import {WhitelistPaymaster} from "../paymasters/WhitelistPaymaster.sol";
7+
8+
/// @notice the content sign contract variant for Click. Only users whitelisted on the paymaster can mint tokens
9+
contract ClickContentSign is BaseContentSign {
10+
WhitelistPaymaster public whitelistPaymaster;
11+
12+
constructor(string memory name, string memory symbol, WhitelistPaymaster whitelist) BaseContentSign(name, symbol) {
13+
whitelistPaymaster = whitelist;
14+
}
15+
16+
function _userIsWhitelisted(address user) internal view override returns (bool) {
17+
return whitelistPaymaster.isWhitelistedUser(user);
18+
}
19+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// SPDX-License-Identifier: BSD-3-Clause-Clear
2+
3+
pragma solidity ^0.8.20;
4+
5+
import {BaseContentSign} from "./BaseContentSign.sol";
6+
7+
import {AccessControl} from "openzeppelin-contracts/contracts/access/AccessControl.sol";
8+
9+
/// @notice the content sign contract variant for enterprises. Only users whitelisted on this contract can mint
10+
contract EnterpriseContentSign is BaseContentSign, AccessControl {
11+
bytes32 public constant WHITELISTED_ROLE = keccak256("WHITELISTED_ROLE");
12+
13+
constructor(string memory name, string memory symbol) BaseContentSign(name, symbol) {
14+
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
15+
}
16+
17+
function supportsInterface(bytes4 interfaceId)
18+
public
19+
view
20+
override(BaseContentSign, AccessControl)
21+
returns (bool)
22+
{
23+
return BaseContentSign.supportsInterface(interfaceId) || AccessControl.supportsInterface(interfaceId);
24+
}
25+
26+
function _userIsWhitelisted(address user) internal view override returns (bool) {
27+
return hasRole(WHITELISTED_ROLE, user);
28+
}
29+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// SPDX-License-Identifier: BSD-3-Clause-Clear
2+
3+
pragma solidity ^0.8.20;
4+
5+
import {Test, console} from "forge-std/Test.sol";
6+
import {BaseContentSign} from "../../src/contentsign/BaseContentSign.sol";
7+
8+
contract MockContentSign is BaseContentSign {
9+
bool public whitelisted = false;
10+
11+
constructor() BaseContentSign("Mock", "MOCK") {}
12+
13+
function setWhitelisted(bool _whitelisted) public {
14+
whitelisted = _whitelisted;
15+
}
16+
17+
function _userIsWhitelisted(address) internal view override returns (bool) {
18+
return whitelisted;
19+
}
20+
}
21+
22+
contract BaseContentSignTest is Test {
23+
MockContentSign private contentSign;
24+
25+
address internal alice = vm.addr(1);
26+
27+
function setUp() public {
28+
contentSign = new MockContentSign();
29+
}
30+
31+
function test_setsMetadata() public {
32+
assertEq(contentSign.name(), "Mock");
33+
assertEq(contentSign.symbol(), "MOCK");
34+
}
35+
36+
function test_whitelistedCanMint() public {
37+
contentSign.setWhitelisted(true);
38+
contentSign.safeMint(alice, "uri");
39+
40+
assertEq(contentSign.ownerOf(0), alice);
41+
assertEq(contentSign.tokenURI(0), "uri");
42+
}
43+
44+
function test_nonWhitelistedCannotMint() public {
45+
contentSign.setWhitelisted(false);
46+
47+
vm.expectRevert(abi.encodeWithSelector(BaseContentSign.UserIsNotWhitelisted.selector, alice));
48+
vm.prank(alice);
49+
contentSign.safeMint(alice, "uri");
50+
}
51+
}
Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,21 @@
33
pragma solidity ^0.8.20;
44

55
import {Test, console} from "forge-std/Test.sol";
6-
import {ContentSignNFT} from "../../src/contentsign/ContentSignNFT.sol";
6+
import {BaseContentSign} from "../../src/contentsign/BaseContentSign.sol";
7+
import {ClickContentSign} from "../../src/contentsign/ClickContentSign.sol";
78
import {WhitelistPaymaster} from "../../src/paymasters/WhitelistPaymaster.sol";
89

9-
contract ContentSignNFTTest is Test {
10+
contract ClickContentSignTest is Test {
1011
WhitelistPaymaster private paymaster;
11-
ContentSignNFT private nft;
12+
ClickContentSign private nft;
1213

1314
address internal alice = vm.addr(1);
1415
address internal bob = vm.addr(2);
1516

1617
function setUp() public {
1718
vm.prank(alice);
1819
paymaster = new WhitelistPaymaster(alice);
19-
nft = new ContentSignNFT("Name", "Symbol", paymaster);
20+
nft = new ClickContentSign("Name", "Symbol", paymaster);
2021

2122
address[] memory contracts = new address[](1);
2223
contracts[0] = address(nft);
@@ -41,7 +42,7 @@ contract ContentSignNFTTest is Test {
4142
function test_nonWhitelistedCannotMint() public {
4243
address charlie = vm.addr(3);
4344

44-
vm.expectRevert(ContentSignNFT.UserIsNotWhitelisted.selector);
45+
vm.expectRevert(abi.encodeWithSelector(BaseContentSign.UserIsNotWhitelisted.selector, charlie));
4546
vm.prank(charlie);
4647
nft.safeMint(charlie, "uri");
4748
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// SPDX-License-Identifier: BSD-3-Clause-Clear
2+
3+
pragma solidity ^0.8.20;
4+
5+
import {Test, console} from "forge-std/Test.sol";
6+
import {BaseContentSign} from "../../src/contentsign/BaseContentSign.sol";
7+
import {EnterpriseContentSign} from "../../src/contentsign/EnterpriseContentSign.sol";
8+
9+
contract EnterpriseContentSignTest is Test {
10+
EnterpriseContentSign private nft;
11+
12+
address internal alice = vm.addr(1);
13+
address internal bob = vm.addr(2);
14+
15+
function setUp() public {
16+
vm.startPrank(alice);
17+
nft = new EnterpriseContentSign("Name", "Symbol");
18+
nft.grantRole(nft.WHITELISTED_ROLE(), bob);
19+
vm.stopPrank();
20+
}
21+
22+
function sets_adminRole() public {
23+
assertEq(nft.hasRole(nft.DEFAULT_ADMIN_ROLE(), alice), true);
24+
}
25+
26+
function test_whitelistedUserCanMint() public {
27+
assertEq(nft.hasRole(nft.WHITELISTED_ROLE(), bob), true);
28+
29+
vm.prank(bob);
30+
nft.safeMint(bob, "uri");
31+
32+
assertEq(nft.ownerOf(0), bob);
33+
assertEq(nft.tokenURI(0), "uri");
34+
}
35+
36+
function test_nonWhitelistedCannotMint() public {
37+
address charlie = vm.addr(3);
38+
39+
vm.expectRevert(abi.encodeWithSelector(BaseContentSign.UserIsNotWhitelisted.selector, charlie));
40+
vm.prank(charlie);
41+
nft.safeMint(charlie, "uri");
42+
}
43+
}

0 commit comments

Comments
 (0)