Skip to content

Commit

Permalink
Merge pull request #162 from thirdweb-dev/joaquim/twcontract
Browse files Browse the repository at this point in the history
Leaner thirdwebContract
  • Loading branch information
joaquim-verges authored May 20, 2022
2 parents 91228c7 + 292119a commit f10d543
Show file tree
Hide file tree
Showing 24 changed files with 1,296 additions and 1,513 deletions.
84 changes: 58 additions & 26 deletions contracts/ByocFactory.sol → contracts/ContractDeployer.sol
Original file line number Diff line number Diff line change
@@ -1,25 +1,31 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.11;
pragma solidity ^0.8.0;

// ========== External imports ==========
import "@openzeppelin/contracts/utils/Create2.sol";
import "@openzeppelin/contracts/proxy/Clones.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/utils/Multicall.sol";
import "@openzeppelin/contracts/access/AccessControlEnumerable.sol";
import "@openzeppelin/contracts/metatx/ERC2771Context.sol";

// ========== Internal imports ==========
import { IByocFactory } from "./interfaces/IByocFactory.sol";
import { IContractDeployer } from "./interfaces/IContractDeployer.sol";
import { TWRegistry } from "./TWRegistry.sol";
import "./ThirdwebContract.sol";
import { IContractMetadataRegistry } from "./interfaces/IContractMetadataRegistry.sol";
import { ThirdwebContract } from "./ThirdwebContract.sol";

contract ByocFactory is IByocFactory, ERC2771Context, AccessControlEnumerable, ThirdwebContract {
contract ContractDeployer is IContractDeployer, ERC2771Context, Multicall, AccessControlEnumerable {
/*///////////////////////////////////////////////////////////////
State variables
//////////////////////////////////////////////////////////////*/

/// @dev The main thirdweb registry.
TWRegistry private immutable registry;
/// @dev The contract metadta registry.
IContractMetadataRegistry private immutable metadataRegistry;
/// @dev contract address deployed through the factory => deployer
mapping(address => address) public getContractDeployer;

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

constructor(address _twRegistry, address _trustedForwarder) ERC2771Context(_trustedForwarder) {
constructor(
address _twRegistry,
address _metadataRegistry,
address _trustedForwarder
) ERC2771Context(_trustedForwarder) {
registry = TWRegistry(_twRegistry);
metadataRegistry = IContractMetadataRegistry(_metadataRegistry);
_setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
}

Expand All @@ -51,25 +62,34 @@ contract ByocFactory is IByocFactory, ERC2771Context, AccessControlEnumerable, T
bytes memory _constructorArgs,
bytes32 _salt,
uint256 _value,
ThirdwebContract.ThirdwebInfo memory _thirdwebInfo
string memory publishMetadataUri
) external onlyUnpausedOrAdmin returns (address deployedAddress) {
require(bytes(_thirdwebInfo.publishMetadataUri).length > 0, "No publish metadata");
require(bytes(publishMetadataUri).length > 0, "No publish metadata");

address caller = _msgSender();

bytes memory contractBytecode = abi.encodePacked(_contractBytecode, _constructorArgs);
bytes32 salt = _salt == "" ? keccak256(abi.encodePacked(_msgSender(), block.number)) : _salt;
bytes32 salt = _salt == ""
? keccak256(abi.encodePacked(caller, block.number, keccak256(contractBytecode)))
: keccak256(abi.encodePacked(caller, _salt));

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

// deploy the contract
deployedAddress = Create2.deploy(_value, salt, contractBytecode);

ThirdwebContract(deployedAddress).setThirdwebInfo(_thirdwebInfo);
require(
keccak256(bytes(ThirdwebContract(deployedAddress).getPublishMetadataUri())) ==
keccak256(bytes(_thirdwebInfo.publishMetadataUri)),
"Not a thirdweb contract"
);
// set the owner
ThirdwebContract(deployedAddress).tw_initializeOwner(caller);

registry.add(_publisher, deployedAddress);
// register to metadata registry
metadataRegistry.registerMetadata(deployedAddress, publishMetadataUri);

emit ContractDeployed(_msgSender(), _publisher, deployedAddress);
// register to TWRegistry
registry.add(caller, deployedAddress);

emit ContractDeployed(caller, _publisher, deployedAddress);
}

/// @notice Deploys a clone pointing to an implementation of a published contract.
Expand All @@ -79,26 +99,38 @@ contract ByocFactory is IByocFactory, ERC2771Context, AccessControlEnumerable, T
bytes memory _initializeData,
bytes32 _salt,
uint256 _value,
ThirdwebContract.ThirdwebInfo memory _thirdwebInfo
string memory publishMetadataUri
) external onlyUnpausedOrAdmin returns (address deployedAddress) {
bytes32 salt = _salt == "" ? keccak256(abi.encodePacked(_msgSender(), block.number)) : _salt;
require(bytes(publishMetadataUri).length > 0, "No publish metadata");

address caller = _msgSender();

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

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

// deploy the clone
deployedAddress = Clones.cloneDeterministic(_implementation, salt);

ThirdwebContract(deployedAddress).setThirdwebInfo(_thirdwebInfo);
require(
keccak256(bytes(ThirdwebContract(deployedAddress).getPublishMetadataUri())) ==
keccak256(bytes(_thirdwebInfo.publishMetadataUri)),
"Not a thirdweb contract"
);
// set the owner
ThirdwebContract(deployedAddress).tw_initializeOwner(caller);

// register to metadata registry
metadataRegistry.registerMetadata(deployedAddress, publishMetadataUri);

registry.add(_publisher, deployedAddress);
// register to TWRegistry
registry.add(caller, deployedAddress);

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

emit ContractDeployed(_msgSender(), _publisher, deployedAddress);
emit ContractDeployed(caller, _publisher, deployedAddress);
}

/*///////////////////////////////////////////////////////////////
Expand Down
57 changes: 57 additions & 0 deletions contracts/ContractMetadataRegistry.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

// ========== External imports ==========
import "@openzeppelin/contracts/utils/Multicall.sol";
import "@openzeppelin/contracts/access/AccessControlEnumerable.sol";
import "@openzeppelin/contracts/metatx/ERC2771Context.sol";

// ========== Internal imports ==========
import { IContractMetadataRegistry } from "./interfaces/IContractMetadataRegistry.sol";

contract ContractMetadataRegistry is IContractMetadataRegistry, ERC2771Context, Multicall, AccessControlEnumerable {
/// @dev Only accounts with OPERATOR_ROLE can register metadata for contracts.
bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");

/*///////////////////////////////////////////////////////////////
Mappings
//////////////////////////////////////////////////////////////*/

/// @dev contract address deployed => metadata uri
mapping(address => string) public getMetadataUri;

/*///////////////////////////////////////////////////////////////
Constructor
//////////////////////////////////////////////////////////////*/

constructor(address _trustedForwarder) ERC2771Context(_trustedForwarder) {
_setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
}

/*///////////////////////////////////////////////////////////////
External functions
//////////////////////////////////////////////////////////////*/

/// @dev Records `metadataUri` as metadata for the contract at `contractAddress`.
function registerMetadata(address contractAddress, string memory metadataUri) external {
require(hasRole(OPERATOR_ROLE, _msgSender()), "Not operator.");
require(bytes(metadataUri).length > 0, "No metadata");
require(bytes(getMetadataUri[contractAddress]).length == 0, "Metadata already registered");

getMetadataUri[contractAddress] = metadataUri;

emit MetadataRegistered(contractAddress, metadataUri);
}

/*///////////////////////////////////////////////////////////////
Miscellaneous
//////////////////////////////////////////////////////////////*/

function _msgSender() internal view virtual override(Context, ERC2771Context) returns (address sender) {
return ERC2771Context._msgSender();
}

function _msgData() internal view virtual override(Context, ERC2771Context) returns (bytes calldata) {
return ERC2771Context._msgData();
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.11;
pragma solidity ^0.8.0;

// ========== External imports ==========
import "@openzeppelin/contracts/metatx/ERC2771Context.sol";
Expand All @@ -8,9 +8,9 @@ import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import "@openzeppelin/contracts/utils/Multicall.sol";

// ========== Internal imports ==========
import { IByocRegistry } from "./interfaces/IByocRegistry.sol";
import { IContractPublisher } from "./interfaces/IContractPublisher.sol";

contract ByocRegistry is IByocRegistry, ERC2771Context, AccessControlEnumerable, Multicall {
contract ContractPublisher is IContractPublisher, ERC2771Context, AccessControlEnumerable, Multicall {
using EnumerableSet for EnumerableSet.Bytes32Set;

/*///////////////////////////////////////////////////////////////
Expand Down
44 changes: 18 additions & 26 deletions contracts/ThirdwebContract.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,32 @@
pragma solidity ^0.8.0;

import "./feature/Ownable.sol";
import "./feature/ContractMetadata.sol";
import "./interfaces/IContractDeployer.sol";

contract ThirdwebContract is Ownable, ContractMetadata {
struct ThirdwebInfo {
string publishMetadataUri;
string contractURI;
address owner;
}

/// @dev The publish metadata of the contract of which this contract is an instance.
string private publishMetadataUri;

/// @dev Returns the publish metadata for this contract.
function getPublishMetadataUri() external view returns (string memory) {
return publishMetadataUri;
}

/// @dev Initializes the publish metadata and contract metadata at deploy time.
function setThirdwebInfo(ThirdwebInfo memory _thirdwebInfo) external {
require(bytes(publishMetadataUri).length == 0, "Published metadata already initialized");
require(owner == address(0), "Owner already initialized");
contract ThirdwebContract is Ownable {
uint256 private hasSetOwner;

publishMetadataUri = _thirdwebInfo.publishMetadataUri;
contractURI = _thirdwebInfo.contractURI;
owner = _thirdwebInfo.owner;
/// @dev Initializes the owner of the contract.
function tw_initializeOwner(address deployer) external {
require(hasSetOwner == 0, "Owner already initialized");
hasSetOwner = 1;
owner = deployer;
}

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

/// @dev Returns whether contract metadata can be set
function _canSetContractURI() internal virtual override returns (bool) {
return msg.sender == owner;
/// @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.
function _contractDeployer() internal view returns (address) {
if (address(this).code.length == 0) {
try IContractDeployer(msg.sender).getContractDeployer(address(this)) returns (address deployer) {
return deployer;
} catch {
return address(0);
}
}
return address(0);
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.11;
pragma solidity ^0.8.0;

import "../ThirdwebContract.sol";

interface IByocFactory {
interface IContractDeployer {
/// @dev Emitted when the registry is paused.
event Paused(bool isPaused);

Expand All @@ -18,7 +16,7 @@ interface IByocFactory {
* @param constructorArgs The encoded constructor args to deploy the contract with.
* @param salt The salt to use in the CREATE2 contract deployment.
* @param value The native token value to pass to the contract on deployment.
* @param thirdwebInfo The publish metadata URI and contract URI for the contract to deploy.
* @param publishMetadataUri The publish metadata URI for the contract to deploy.
*
* @return deployedAddress The address of the contract deployed.
*/
Expand All @@ -28,7 +26,7 @@ interface IByocFactory {
bytes memory constructorArgs,
bytes32 salt,
uint256 value,
ThirdwebContract.ThirdwebInfo memory thirdwebInfo
string memory publishMetadataUri
) external returns (address deployedAddress);

/**
Expand All @@ -39,7 +37,7 @@ interface IByocFactory {
* @param initializeData The encoded function call to initialize the contract with.
* @param salt The salt to use in the CREATE2 contract deployment.
* @param value The native token value to pass to the contract on deployment.
* @param thirdwebInfo The publish metadata URI and contract URI for the contract to deploy.
* @param publishMetadataUri The publish metadata URI and for the contract to deploy.
*
* @return deployedAddress The address of the contract deployed.
*/
Expand All @@ -49,6 +47,8 @@ interface IByocFactory {
bytes memory initializeData,
bytes32 salt,
uint256 value,
ThirdwebContract.ThirdwebInfo memory thirdwebInfo
string memory publishMetadataUri
) external returns (address deployedAddress);

function getContractDeployer(address _contract) external view returns (address);
}
10 changes: 10 additions & 0 deletions contracts/interfaces/IContractMetadataRegistry.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

interface IContractMetadataRegistry {
/// @dev Emitted when a contract metadata is registered
event MetadataRegistered(address indexed contractAddress, string metadataUri);

/// @dev Records `metadataUri` as metadata for the contract at `contractAddress`.
function registerMetadata(address contractAddress, string memory metadataUri) external;
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.11;
pragma solidity ^0.8.0;

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

interface IByocRegistry {
interface IContractPublisher {
struct CustomContractInstance {
string contractId;
uint256 publishTimestamp;
Expand Down
Loading

0 comments on commit f10d543

Please sign in to comment.