编译器
0.8.22+commit.4fc1097e
文件 1 的 4:Clones.sol
pragma solidity ^0.8.20;
library Clones {
error ERC1167FailedCreateClone();
function clone(address implementation) internal returns (address instance) {
assembly {
mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
instance := create(0, 0x09, 0x37)
}
if (instance == address(0)) {
revert ERC1167FailedCreateClone();
}
}
function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
assembly {
mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
instance := create2(0, 0x09, 0x37, salt)
}
if (instance == address(0)) {
revert ERC1167FailedCreateClone();
}
}
function predictDeterministicAddress(
address implementation,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
assembly {
let ptr := mload(0x40)
mstore(add(ptr, 0x38), deployer)
mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)
mstore(add(ptr, 0x14), implementation)
mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)
mstore(add(ptr, 0x58), salt)
mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))
predicted := keccak256(add(ptr, 0x43), 0x55)
}
}
function predictDeterministicAddress(
address implementation,
bytes32 salt
) internal view returns (address predicted) {
return predictDeterministicAddress(implementation, salt, address(this));
}
}
文件 2 的 4:Context.sol
pragma solidity ^0.8.20;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
文件 3 的 4:Ownable.sol
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
error OwnableUnauthorizedAccount(address account);
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
return _owner;
}
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 4 的 4:TLUniversalDeployer.sol
pragma solidity 0.8.22;
import {Ownable} from "openzeppelin/access/Ownable.sol";
import {Clones} from "openzeppelin/proxy/Clones.sol";
contract TLUniversalDeployer is Ownable {
struct ContractVersion {
string id;
address implementation;
}
struct DeployableContract {
bool created;
string cType;
ContractVersion[] versions;
}
string public constant VERSION = "1.0.0";
mapping(bytes32 => DeployableContract) private _deployableContracts;
bytes32[] private _deployableContractKeys;
event ContractDeployed(
address indexed sender,
address indexed deployedContract,
address indexed implementation,
string cType,
string version
);
error InvalidDeployableContract();
error InitializationFailed();
error ContractAlreadyCreated();
constructor(address initOwner) Ownable(initOwner) {}
function deploy(string calldata contractType, bytes calldata initializationCode) external {
bytes32 dcId = keccak256(bytes(contractType));
DeployableContract memory dc = _deployableContracts[dcId];
if (!dc.created) revert InvalidDeployableContract();
ContractVersion memory cv = dc.versions[dc.versions.length - 1];
_deploy(dc, cv, initializationCode);
}
function deploy(string calldata contractType, bytes calldata initializationCode, uint256 versionIndex) external {
bytes32 dcId = keccak256(bytes(contractType));
DeployableContract memory dc = _deployableContracts[dcId];
if (!dc.created) revert InvalidDeployableContract();
if (versionIndex >= dc.versions.length) revert InvalidDeployableContract();
ContractVersion memory cv = dc.versions[versionIndex];
_deploy(dc, cv, initializationCode);
}
function addDeployableContract(string calldata contractType, ContractVersion calldata version) external onlyOwner {
bytes32 dcId = keccak256(bytes(contractType));
DeployableContract storage dc = _deployableContracts[dcId];
if (!dc.created) {
dc.created = true;
dc.cType = contractType;
_deployableContractKeys.push(dcId);
}
dc.versions.push(version);
}
function getDeployableContracts() external view returns (string[] memory) {
string[] memory dcs = new string[](_deployableContractKeys.length);
for (uint256 i = 0; i < _deployableContractKeys.length; i++) {
dcs[i] = _deployableContracts[_deployableContractKeys[i]].cType;
}
return dcs;
}
function getDeployableContract(string calldata contractType) external view returns (DeployableContract memory) {
bytes32 dcId = keccak256(bytes(contractType));
DeployableContract memory dc = _deployableContracts[dcId];
return dc;
}
function predictDeployedContractAddress(
address sender,
string calldata contractType,
bytes calldata initializationCode
) external view returns (address) {
bytes32 dcId = keccak256(bytes(contractType));
DeployableContract memory dc = _deployableContracts[dcId];
if (!dc.created) revert InvalidDeployableContract();
ContractVersion memory cv = dc.versions[dc.versions.length - 1];
bytes32 salt = keccak256(abi.encodePacked(sender, initializationCode));
return Clones.predictDeterministicAddress(cv.implementation, salt);
}
function predictDeployedContractAddress(
address sender,
string calldata contractType,
bytes calldata initializationCode,
uint256 versionIndex
) external view returns (address) {
bytes32 dcId = keccak256(bytes(contractType));
DeployableContract memory dc = _deployableContracts[dcId];
if (!dc.created) revert InvalidDeployableContract();
if (versionIndex >= dc.versions.length) revert InvalidDeployableContract();
ContractVersion memory cv = dc.versions[versionIndex];
bytes32 salt = keccak256(abi.encodePacked(sender, initializationCode));
return Clones.predictDeterministicAddress(cv.implementation, salt);
}
function _deploy(DeployableContract memory dc, ContractVersion memory cv, bytes memory initializationCode)
private
{
bytes32 salt = keccak256(abi.encodePacked(msg.sender, initializationCode));
address deployedContract = Clones.cloneDeterministic(cv.implementation, salt);
(bool success,) = deployedContract.call(initializationCode);
if (!success) revert InitializationFailed();
emit ContractDeployed(msg.sender, deployedContract, cv.implementation, dc.cType, cv.id);
}
}
{
"compilationTarget": {
"src/TLUniversalDeployer.sol": "TLUniversalDeployer"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 20000
},
"remappings": [
":@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
":@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
":ds-test/=lib/forge-std/lib/ds-test/src/",
":erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
":forge-std/=lib/forge-std/src/",
":openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
":openzeppelin-contracts/=lib/openzeppelin-contracts/",
":openzeppelin-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
":openzeppelin/=lib/openzeppelin-contracts/contracts/"
]
}
[{"inputs":[{"internalType":"address","name":"initOwner","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ContractAlreadyCreated","type":"error"},{"inputs":[],"name":"ERC1167FailedCreateClone","type":"error"},{"inputs":[],"name":"InitializationFailed","type":"error"},{"inputs":[],"name":"InvalidDeployableContract","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"deployedContract","type":"address"},{"indexed":true,"internalType":"address","name":"implementation","type":"address"},{"indexed":false,"internalType":"string","name":"cType","type":"string"},{"indexed":false,"internalType":"string","name":"version","type":"string"}],"name":"ContractDeployed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[],"name":"VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"contractType","type":"string"},{"components":[{"internalType":"string","name":"id","type":"string"},{"internalType":"address","name":"implementation","type":"address"}],"internalType":"struct TLUniversalDeployer.ContractVersion","name":"version","type":"tuple"}],"name":"addDeployableContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"contractType","type":"string"},{"internalType":"bytes","name":"initializationCode","type":"bytes"}],"name":"deploy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"contractType","type":"string"},{"internalType":"bytes","name":"initializationCode","type":"bytes"},{"internalType":"uint256","name":"versionIndex","type":"uint256"}],"name":"deploy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"contractType","type":"string"}],"name":"getDeployableContract","outputs":[{"components":[{"internalType":"bool","name":"created","type":"bool"},{"internalType":"string","name":"cType","type":"string"},{"components":[{"internalType":"string","name":"id","type":"string"},{"internalType":"address","name":"implementation","type":"address"}],"internalType":"struct TLUniversalDeployer.ContractVersion[]","name":"versions","type":"tuple[]"}],"internalType":"struct TLUniversalDeployer.DeployableContract","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDeployableContracts","outputs":[{"internalType":"string[]","name":"","type":"string[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"string","name":"contractType","type":"string"},{"internalType":"bytes","name":"initializationCode","type":"bytes"},{"internalType":"uint256","name":"versionIndex","type":"uint256"}],"name":"predictDeployedContractAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"string","name":"contractType","type":"string"},{"internalType":"bytes","name":"initializationCode","type":"bytes"}],"name":"predictDeployedContractAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]