Skip to content

Commit 36818f7

Browse files
Introduce ContractMetadataRegistry and refactor thirdweb deploy infrastructure
1 parent 04a4f9b commit 36818f7

16 files changed

+2187
-72
lines changed

contracts/ByocFactory.sol renamed to contracts/ContractDeployer.sol

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,26 @@ import "@openzeppelin/contracts/access/AccessControlEnumerable.sol";
1010
import "@openzeppelin/contracts/metatx/ERC2771Context.sol";
1111

1212
// ========== Internal imports ==========
13-
import { IByocFactory } from "./interfaces/IByocFactory.sol";
13+
import { IContractDeployer } from "./interfaces/IContractDeployer.sol";
1414
import { TWRegistry } from "./TWRegistry.sol";
15-
import "./ThirdwebContract.sol";
15+
import { IContractMetadataRegistry } from "./interfaces/IContractMetadataRegistry.sol";
16+
import { ThirdwebContract } from "./ThirdwebContract.sol";
1617

17-
contract ByocFactory is IByocFactory, ERC2771Context, Multicall, AccessControlEnumerable {
18+
contract ContractDeployer is IContractDeployer, ERC2771Context, Multicall, AccessControlEnumerable {
1819
/*///////////////////////////////////////////////////////////////
1920
State variables
2021
//////////////////////////////////////////////////////////////*/
2122

2223
/// @dev The main thirdweb registry.
2324
TWRegistry private immutable registry;
25+
/// @dev The contract metadta registry.
26+
IContractMetadataRegistry private immutable metadataRegistry;
27+
/// @dev contract address deployed through the factory => deployer
28+
mapping(address => address) public getContractDeployer;
2429

2530
/// @dev Whether the registry is paused.
2631
bool public isPaused;
2732

28-
/// @dev contract address deployed through the factory => deployer
29-
mapping(address => address) public getContractDeployer;
30-
3133
/*///////////////////////////////////////////////////////////////
3234
Constructor + modifiers
3335
//////////////////////////////////////////////////////////////*/
@@ -39,8 +41,13 @@ contract ByocFactory is IByocFactory, ERC2771Context, Multicall, AccessControlEn
3941
_;
4042
}
4143

42-
constructor(address _twRegistry, address _trustedForwarder) ERC2771Context(_trustedForwarder) {
44+
constructor(
45+
address _twRegistry,
46+
address _metadataRegistry,
47+
address _trustedForwarder
48+
) ERC2771Context(_trustedForwarder) {
4349
registry = TWRegistry(_twRegistry);
50+
metadataRegistry = IContractMetadataRegistry(_metadataRegistry);
4451
_setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
4552
}
4653

@@ -66,18 +73,20 @@ contract ByocFactory is IByocFactory, ERC2771Context, Multicall, AccessControlEn
6673
? keccak256(abi.encodePacked(caller, block.number, keccak256(contractBytecode)))
6774
: keccak256(abi.encodePacked(caller, _salt));
6875

76+
// compute the address of the clone and save it
6977
address computedContractAddress = Create2.computeAddress(salt, keccak256(contractBytecode), address(this));
7078
getContractDeployer[computedContractAddress] = caller;
7179

80+
// deploy the contract
7281
deployedAddress = Create2.deploy(_value, salt, contractBytecode);
7382

74-
ThirdwebContract(deployedAddress).setPublishMetadataUri(publishMetadataUri);
75-
require(
76-
keccak256(bytes(ThirdwebContract(deployedAddress).getPublishMetadataUri())) ==
77-
keccak256(bytes(publishMetadataUri)),
78-
"Not a thirdweb contract"
79-
);
83+
// set the owner
84+
ThirdwebContract(deployedAddress).tw_initializeOwner(caller);
8085

86+
// register to metadata registry
87+
metadataRegistry.registerMetadata(deployedAddress, publishMetadataUri);
88+
89+
// register to TWRegistry
8190
registry.add(caller, deployedAddress);
8291

8392
emit ContractDeployed(caller, _publisher, deployedAddress);
@@ -92,24 +101,28 @@ contract ByocFactory is IByocFactory, ERC2771Context, Multicall, AccessControlEn
92101
uint256 _value,
93102
string memory publishMetadataUri
94103
) external onlyUnpausedOrAdmin returns (address deployedAddress) {
104+
require(bytes(publishMetadataUri).length > 0, "No publish metadata");
105+
95106
address caller = _msgSender();
96107

97108
bytes32 salt = _salt == ""
98109
? keccak256(abi.encodePacked(caller, block.number, _implementation, _initializeData))
99110
: keccak256(abi.encodePacked(caller, _salt));
100111

112+
// compute the address of the clone and save it
101113
address computedContractAddress = Clones.predictDeterministicAddress(_implementation, salt, address(this));
102114
getContractDeployer[computedContractAddress] = caller;
103115

116+
// deploy the clone
104117
deployedAddress = Clones.cloneDeterministic(_implementation, salt);
105118

106-
ThirdwebContract(deployedAddress).setPublishMetadataUri(publishMetadataUri);
107-
require(
108-
keccak256(bytes(ThirdwebContract(deployedAddress).getPublishMetadataUri())) ==
109-
keccak256(bytes(publishMetadataUri)),
110-
"Not a thirdweb contract"
111-
);
119+
// set the owner
120+
ThirdwebContract(deployedAddress).tw_initializeOwner(caller);
121+
122+
// register to metadata registry
123+
metadataRegistry.registerMetadata(deployedAddress, publishMetadataUri);
112124

125+
// register to TWRegistry
113126
registry.add(caller, deployedAddress);
114127

115128
if (_initializeData.length > 0) {
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
pragma solidity ^0.8.11;
3+
4+
// ========== External imports ==========
5+
import "@openzeppelin/contracts/utils/Multicall.sol";
6+
import "@openzeppelin/contracts/access/AccessControlEnumerable.sol";
7+
import "@openzeppelin/contracts/metatx/ERC2771Context.sol";
8+
9+
// ========== Internal imports ==========
10+
import { IContractMetadataRegistry } from "./interfaces/IContractMetadataRegistry.sol";
11+
12+
contract ContractMetadataRegistry is IContractMetadataRegistry, ERC2771Context, Multicall, AccessControlEnumerable {
13+
bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
14+
15+
/*///////////////////////////////////////////////////////////////
16+
State variables
17+
//////////////////////////////////////////////////////////////*/
18+
19+
/// @dev contract address deployed => metadata uri
20+
mapping(address => string) public getMetadataUri;
21+
22+
/*///////////////////////////////////////////////////////////////
23+
Constructor + modifiers
24+
//////////////////////////////////////////////////////////////*/
25+
26+
/// @dev Checks whether the caller is a contract admin.
27+
modifier onlyAdmin() {
28+
require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "Must be admin");
29+
30+
_;
31+
}
32+
33+
constructor(address _trustedForwarder) ERC2771Context(_trustedForwarder) {
34+
_setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
35+
}
36+
37+
/*///////////////////////////////////////////////////////////////
38+
External methods
39+
//////////////////////////////////////////////////////////////*/
40+
41+
function registerMetadata(address contractAddress, string memory metadataUri) external onlyAdmin {
42+
require(hasRole(OPERATOR_ROLE, _msgSender()), "not operator.");
43+
require(bytes(metadataUri).length > 0, "No metadata");
44+
require(bytes(getMetadataUri[contractAddress]).length == 0, "Metadata already registered");
45+
getMetadataUri[contractAddress] = metadataUri;
46+
emit MetadataRegistered(contractAddress, metadataUri);
47+
}
48+
49+
/*///////////////////////////////////////////////////////////////
50+
Miscellaneous
51+
//////////////////////////////////////////////////////////////*/
52+
53+
function _msgSender() internal view virtual override(Context, ERC2771Context) returns (address sender) {
54+
return ERC2771Context._msgSender();
55+
}
56+
57+
function _msgData() internal view virtual override(Context, ERC2771Context) returns (bytes calldata) {
58+
return ERC2771Context._msgData();
59+
}
60+
}

contracts/ByocRegistry.sol renamed to contracts/ContractPublisher.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
88
import "@openzeppelin/contracts/utils/Multicall.sol";
99

1010
// ========== Internal imports ==========
11-
import { IByocRegistry } from "./interfaces/IByocRegistry.sol";
11+
import { IContractPublisher } from "./interfaces/IContractPublisher.sol";
1212

13-
contract ByocRegistry is IByocRegistry, ERC2771Context, AccessControlEnumerable, Multicall {
13+
contract ContractPublisher is IContractPublisher, ERC2771Context, AccessControlEnumerable, Multicall {
1414
using EnumerableSet for EnumerableSet.Bytes32Set;
1515

1616
/*///////////////////////////////////////////////////////////////

contracts/ThirdwebContract.sol

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,22 @@
11
// SPDX-License-Identifier: Apache-2.0
22
pragma solidity ^0.8.0;
33

4-
interface IContractDeployer {
5-
function getContractDeployer(address _contract) external view returns (address);
6-
}
7-
8-
error ThirdwebContract_MetadataAlreadyInitialized();
4+
import "./feature/Ownable.sol";
5+
import "./interfaces/IContractDeployer.sol";
96

10-
contract ThirdwebContract {
11-
/// @dev The publish metadata of the contract of which this contract is an instance.
12-
string private publishMetadataUri;
7+
contract ThirdwebContract is Ownable {
8+
uint256 private hasSetOwner;
139

14-
/// @dev Returns the publish metadata for this contract.
15-
function getPublishMetadataUri() external view returns (string memory) {
16-
return publishMetadataUri;
10+
/// @dev Initializes the owner of the contract.
11+
function tw_initializeOwner(address deployer) external {
12+
require(hasSetOwner == 0, "Owner already initialized");
13+
hasSetOwner = 1;
14+
owner = deployer;
1715
}
1816

19-
/// @dev Initializes the publish metadata and at deploy time.
20-
function setPublishMetadataUri(string memory uri) external {
21-
if (bytes(publishMetadataUri).length != 0) {
22-
revert ThirdwebContract_MetadataAlreadyInitialized();
23-
}
24-
publishMetadataUri = uri;
17+
/// @dev Returns whether owner can be set
18+
function _canSetOwner() internal virtual override returns (bool) {
19+
return msg.sender == owner;
2520
}
2621

2722
/// @dev Enable access to the original contract deployer in the constructor. If this function is called outside of a constructor, it will return address(0) instead.

contracts/interfaces/IByocFactory.sol renamed to contracts/interfaces/IContractDeployer.sol

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
// SPDX-License-Identifier: Apache-2.0
22
pragma solidity ^0.8.11;
33

4-
import "../ThirdwebContract.sol";
5-
6-
interface IByocFactory {
4+
interface IContractDeployer {
75
/// @dev Emitted when the registry is paused.
86
event Paused(bool isPaused);
97

@@ -51,4 +49,6 @@ interface IByocFactory {
5149
uint256 value,
5250
string memory publishMetadataUri
5351
) external returns (address deployedAddress);
52+
53+
function getContractDeployer(address _contract) external view returns (address);
5454
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
pragma solidity ^0.8.11;
3+
4+
interface IContractMetadataRegistry {
5+
/// @dev Emitted when a contract metadata is registered
6+
event MetadataRegistered(address indexed contractAddress, string metadataUri);
7+
8+
function registerMetadata(address contractAddress, string memory metadataUri) external;
9+
}

contracts/interfaces/IByocRegistry.sol renamed to contracts/interfaces/IContractPublisher.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ pragma solidity ^0.8.11;
33

44
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
55

6-
interface IByocRegistry {
6+
interface IContractPublisher {
77
struct CustomContractInstance {
88
string contractId;
99
uint256 publishTimestamp;

0 commit comments

Comments
 (0)