Skip to content

Commit f10d543

Browse files
Merge pull request #162 from thirdweb-dev/joaquim/twcontract
Leaner thirdwebContract
2 parents 91228c7 + 292119a commit f10d543

24 files changed

+1296
-1513
lines changed

contracts/ByocFactory.sol contracts/ContractDeployer.sol

+58-26
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,31 @@
11
// SPDX-License-Identifier: Apache-2.0
2-
pragma solidity ^0.8.11;
2+
pragma solidity ^0.8.0;
33

44
// ========== External imports ==========
55
import "@openzeppelin/contracts/utils/Create2.sol";
66
import "@openzeppelin/contracts/proxy/Clones.sol";
77
import "@openzeppelin/contracts/utils/Address.sol";
8+
import "@openzeppelin/contracts/utils/Multicall.sol";
89
import "@openzeppelin/contracts/access/AccessControlEnumerable.sol";
910
import "@openzeppelin/contracts/metatx/ERC2771Context.sol";
1011

1112
// ========== Internal imports ==========
12-
import { IByocFactory } from "./interfaces/IByocFactory.sol";
13+
import { IContractDeployer } from "./interfaces/IContractDeployer.sol";
1314
import { TWRegistry } from "./TWRegistry.sol";
14-
import "./ThirdwebContract.sol";
15+
import { IContractMetadataRegistry } from "./interfaces/IContractMetadataRegistry.sol";
16+
import { ThirdwebContract } from "./ThirdwebContract.sol";
1517

16-
contract ByocFactory is IByocFactory, ERC2771Context, AccessControlEnumerable, ThirdwebContract {
18+
contract ContractDeployer is IContractDeployer, ERC2771Context, Multicall, AccessControlEnumerable {
1719
/*///////////////////////////////////////////////////////////////
1820
State variables
1921
//////////////////////////////////////////////////////////////*/
2022

2123
/// @dev The main thirdweb registry.
2224
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;
2329

2430
/// @dev Whether the registry is paused.
2531
bool public isPaused;
@@ -35,8 +41,13 @@ contract ByocFactory is IByocFactory, ERC2771Context, AccessControlEnumerable, T
3541
_;
3642
}
3743

38-
constructor(address _twRegistry, address _trustedForwarder) ERC2771Context(_trustedForwarder) {
44+
constructor(
45+
address _twRegistry,
46+
address _metadataRegistry,
47+
address _trustedForwarder
48+
) ERC2771Context(_trustedForwarder) {
3949
registry = TWRegistry(_twRegistry);
50+
metadataRegistry = IContractMetadataRegistry(_metadataRegistry);
4051
_setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
4152
}
4253

@@ -51,25 +62,34 @@ contract ByocFactory is IByocFactory, ERC2771Context, AccessControlEnumerable, T
5162
bytes memory _constructorArgs,
5263
bytes32 _salt,
5364
uint256 _value,
54-
ThirdwebContract.ThirdwebInfo memory _thirdwebInfo
65+
string memory publishMetadataUri
5566
) external onlyUnpausedOrAdmin returns (address deployedAddress) {
56-
require(bytes(_thirdwebInfo.publishMetadataUri).length > 0, "No publish metadata");
67+
require(bytes(publishMetadataUri).length > 0, "No publish metadata");
68+
69+
address caller = _msgSender();
5770

5871
bytes memory contractBytecode = abi.encodePacked(_contractBytecode, _constructorArgs);
59-
bytes32 salt = _salt == "" ? keccak256(abi.encodePacked(_msgSender(), block.number)) : _salt;
72+
bytes32 salt = _salt == ""
73+
? keccak256(abi.encodePacked(caller, block.number, keccak256(contractBytecode)))
74+
: keccak256(abi.encodePacked(caller, _salt));
75+
76+
// compute the address of the clone and save it
77+
address computedContractAddress = Create2.computeAddress(salt, keccak256(contractBytecode), address(this));
78+
getContractDeployer[computedContractAddress] = caller;
6079

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

63-
ThirdwebContract(deployedAddress).setThirdwebInfo(_thirdwebInfo);
64-
require(
65-
keccak256(bytes(ThirdwebContract(deployedAddress).getPublishMetadataUri())) ==
66-
keccak256(bytes(_thirdwebInfo.publishMetadataUri)),
67-
"Not a thirdweb contract"
68-
);
83+
// set the owner
84+
ThirdwebContract(deployedAddress).tw_initializeOwner(caller);
6985

70-
registry.add(_publisher, deployedAddress);
86+
// register to metadata registry
87+
metadataRegistry.registerMetadata(deployedAddress, publishMetadataUri);
7188

72-
emit ContractDeployed(_msgSender(), _publisher, deployedAddress);
89+
// register to TWRegistry
90+
registry.add(caller, deployedAddress);
91+
92+
emit ContractDeployed(caller, _publisher, deployedAddress);
7393
}
7494

7595
/// @notice Deploys a clone pointing to an implementation of a published contract.
@@ -79,26 +99,38 @@ contract ByocFactory is IByocFactory, ERC2771Context, AccessControlEnumerable, T
7999
bytes memory _initializeData,
80100
bytes32 _salt,
81101
uint256 _value,
82-
ThirdwebContract.ThirdwebInfo memory _thirdwebInfo
102+
string memory publishMetadataUri
83103
) external onlyUnpausedOrAdmin returns (address deployedAddress) {
84-
bytes32 salt = _salt == "" ? keccak256(abi.encodePacked(_msgSender(), block.number)) : _salt;
104+
require(bytes(publishMetadataUri).length > 0, "No publish metadata");
105+
106+
address caller = _msgSender();
107+
108+
bytes32 salt = _salt == ""
109+
? keccak256(abi.encodePacked(caller, block.number, _implementation, _initializeData))
110+
: keccak256(abi.encodePacked(caller, _salt));
111+
112+
// compute the address of the clone and save it
113+
address computedContractAddress = Clones.predictDeterministicAddress(_implementation, salt, address(this));
114+
getContractDeployer[computedContractAddress] = caller;
115+
116+
// deploy the clone
85117
deployedAddress = Clones.cloneDeterministic(_implementation, salt);
86118

87-
ThirdwebContract(deployedAddress).setThirdwebInfo(_thirdwebInfo);
88-
require(
89-
keccak256(bytes(ThirdwebContract(deployedAddress).getPublishMetadataUri())) ==
90-
keccak256(bytes(_thirdwebInfo.publishMetadataUri)),
91-
"Not a thirdweb contract"
92-
);
119+
// set the owner
120+
ThirdwebContract(deployedAddress).tw_initializeOwner(caller);
121+
122+
// register to metadata registry
123+
metadataRegistry.registerMetadata(deployedAddress, publishMetadataUri);
93124

94-
registry.add(_publisher, deployedAddress);
125+
// register to TWRegistry
126+
registry.add(caller, deployedAddress);
95127

96128
if (_initializeData.length > 0) {
97129
// slither-disable-next-line unused-return
98130
Address.functionCallWithValue(deployedAddress, _initializeData, _value);
99131
}
100132

101-
emit ContractDeployed(_msgSender(), _publisher, deployedAddress);
133+
emit ContractDeployed(caller, _publisher, deployedAddress);
102134
}
103135

104136
/*///////////////////////////////////////////////////////////////
+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
pragma solidity ^0.8.0;
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+
/// @dev Only accounts with OPERATOR_ROLE can register metadata for contracts.
14+
bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
15+
16+
/*///////////////////////////////////////////////////////////////
17+
Mappings
18+
//////////////////////////////////////////////////////////////*/
19+
20+
/// @dev contract address deployed => metadata uri
21+
mapping(address => string) public getMetadataUri;
22+
23+
/*///////////////////////////////////////////////////////////////
24+
Constructor
25+
//////////////////////////////////////////////////////////////*/
26+
27+
constructor(address _trustedForwarder) ERC2771Context(_trustedForwarder) {
28+
_setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
29+
}
30+
31+
/*///////////////////////////////////////////////////////////////
32+
External functions
33+
//////////////////////////////////////////////////////////////*/
34+
35+
/// @dev Records `metadataUri` as metadata for the contract at `contractAddress`.
36+
function registerMetadata(address contractAddress, string memory metadataUri) external {
37+
require(hasRole(OPERATOR_ROLE, _msgSender()), "Not operator.");
38+
require(bytes(metadataUri).length > 0, "No metadata");
39+
require(bytes(getMetadataUri[contractAddress]).length == 0, "Metadata already registered");
40+
41+
getMetadataUri[contractAddress] = metadataUri;
42+
43+
emit MetadataRegistered(contractAddress, metadataUri);
44+
}
45+
46+
/*///////////////////////////////////////////////////////////////
47+
Miscellaneous
48+
//////////////////////////////////////////////////////////////*/
49+
50+
function _msgSender() internal view virtual override(Context, ERC2771Context) returns (address sender) {
51+
return ERC2771Context._msgSender();
52+
}
53+
54+
function _msgData() internal view virtual override(Context, ERC2771Context) returns (bytes calldata) {
55+
return ERC2771Context._msgData();
56+
}
57+
}

contracts/ByocRegistry.sol contracts/ContractPublisher.sol

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// SPDX-License-Identifier: Apache-2.0
2-
pragma solidity ^0.8.11;
2+
pragma solidity ^0.8.0;
33

44
// ========== External imports ==========
55
import "@openzeppelin/contracts/metatx/ERC2771Context.sol";
@@ -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

+18-26
Original file line numberDiff line numberDiff line change
@@ -2,40 +2,32 @@
22
pragma solidity ^0.8.0;
33

44
import "./feature/Ownable.sol";
5-
import "./feature/ContractMetadata.sol";
5+
import "./interfaces/IContractDeployer.sol";
66

7-
contract ThirdwebContract is Ownable, ContractMetadata {
8-
struct ThirdwebInfo {
9-
string publishMetadataUri;
10-
string contractURI;
11-
address owner;
12-
}
13-
14-
/// @dev The publish metadata of the contract of which this contract is an instance.
15-
string private publishMetadataUri;
16-
17-
/// @dev Returns the publish metadata for this contract.
18-
function getPublishMetadataUri() external view returns (string memory) {
19-
return publishMetadataUri;
20-
}
21-
22-
/// @dev Initializes the publish metadata and contract metadata at deploy time.
23-
function setThirdwebInfo(ThirdwebInfo memory _thirdwebInfo) external {
24-
require(bytes(publishMetadataUri).length == 0, "Published metadata already initialized");
25-
require(owner == address(0), "Owner already initialized");
7+
contract ThirdwebContract is Ownable {
8+
uint256 private hasSetOwner;
269

27-
publishMetadataUri = _thirdwebInfo.publishMetadataUri;
28-
contractURI = _thirdwebInfo.contractURI;
29-
owner = _thirdwebInfo.owner;
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;
3015
}
3116

3217
/// @dev Returns whether owner can be set
3318
function _canSetOwner() internal virtual override returns (bool) {
3419
return msg.sender == owner;
3520
}
3621

37-
/// @dev Returns whether contract metadata can be set
38-
function _canSetContractURI() internal virtual override returns (bool) {
39-
return msg.sender == owner;
22+
/// @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.
23+
function _contractDeployer() internal view returns (address) {
24+
if (address(this).code.length == 0) {
25+
try IContractDeployer(msg.sender).getContractDeployer(address(this)) returns (address deployer) {
26+
return deployer;
27+
} catch {
28+
return address(0);
29+
}
30+
}
31+
return address(0);
4032
}
4133
}

contracts/interfaces/IByocFactory.sol contracts/interfaces/IContractDeployer.sol

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
// SPDX-License-Identifier: Apache-2.0
2-
pragma solidity ^0.8.11;
2+
pragma solidity ^0.8.0;
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

@@ -18,7 +16,7 @@ interface IByocFactory {
1816
* @param constructorArgs The encoded constructor args to deploy the contract with.
1917
* @param salt The salt to use in the CREATE2 contract deployment.
2018
* @param value The native token value to pass to the contract on deployment.
21-
* @param thirdwebInfo The publish metadata URI and contract URI for the contract to deploy.
19+
* @param publishMetadataUri The publish metadata URI for the contract to deploy.
2220
*
2321
* @return deployedAddress The address of the contract deployed.
2422
*/
@@ -28,7 +26,7 @@ interface IByocFactory {
2826
bytes memory constructorArgs,
2927
bytes32 salt,
3028
uint256 value,
31-
ThirdwebContract.ThirdwebInfo memory thirdwebInfo
29+
string memory publishMetadataUri
3230
) external returns (address deployedAddress);
3331

3432
/**
@@ -39,7 +37,7 @@ interface IByocFactory {
3937
* @param initializeData The encoded function call to initialize the contract with.
4038
* @param salt The salt to use in the CREATE2 contract deployment.
4139
* @param value The native token value to pass to the contract on deployment.
42-
* @param thirdwebInfo The publish metadata URI and contract URI for the contract to deploy.
40+
* @param publishMetadataUri The publish metadata URI and for the contract to deploy.
4341
*
4442
* @return deployedAddress The address of the contract deployed.
4543
*/
@@ -49,6 +47,8 @@ interface IByocFactory {
4947
bytes memory initializeData,
5048
bytes32 salt,
5149
uint256 value,
52-
ThirdwebContract.ThirdwebInfo memory thirdwebInfo
50+
string memory publishMetadataUri
5351
) external returns (address deployedAddress);
52+
53+
function getContractDeployer(address _contract) external view returns (address);
5454
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
pragma solidity ^0.8.0;
3+
4+
interface IContractMetadataRegistry {
5+
/// @dev Emitted when a contract metadata is registered
6+
event MetadataRegistered(address indexed contractAddress, string metadataUri);
7+
8+
/// @dev Records `metadataUri` as metadata for the contract at `contractAddress`.
9+
function registerMetadata(address contractAddress, string memory metadataUri) external;
10+
}

contracts/interfaces/IByocRegistry.sol contracts/interfaces/IContractPublisher.sol

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
// SPDX-License-Identifier: Apache-2.0
2-
pragma solidity ^0.8.11;
2+
pragma solidity ^0.8.0;
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)