Skip to content

Commit bcc1479

Browse files
committed
add abstract zksync
Signed-off-by: Adam Wolf <[email protected]>
1 parent 5272333 commit bcc1479

4 files changed

Lines changed: 827 additions & 31 deletions

File tree

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.22;
3+
4+
import {Ownable} from "solady/src/auth/Ownable.sol";
5+
import {TokenStandard} from "contracts/common/Structs.sol";
6+
import {MagicDropTokenImplRegistry} from "contracts/registry/MagicDropTokenImplRegistry.sol";
7+
import {ZKProxy} from "contracts/factory/zksync/ZKProxy.sol";
8+
9+
/// @title MagicDropCloneFactory
10+
/// @notice A zksync compatible factory contract for creating and managing clones of MagicDrop contracts
11+
contract MagicDropCloneFactory is Ownable {
12+
/*==============================================================
13+
= CONSTANTS =
14+
==============================================================*/
15+
16+
MagicDropTokenImplRegistry private _registry;
17+
bytes4 private constant INITIALIZE_SELECTOR = bytes4(keccak256("initialize(string,string,address)"));
18+
19+
/*==============================================================
20+
= EVENTS =
21+
==============================================================*/
22+
23+
event MagicDropFactoryInitialized();
24+
event NewContractInitialized(
25+
address contractAddress, address initialOwner, uint32 implId, TokenStandard standard, string name, string symbol
26+
);
27+
event Withdrawal(address to, uint256 amount);
28+
29+
/*==============================================================
30+
= ERRORS =
31+
==============================================================*/
32+
33+
error InitializationFailed();
34+
error RegistryAddressCannotBeZero();
35+
error InsufficientDeploymentFee();
36+
error WithdrawalFailed();
37+
error InitialOwnerCannotBeZero();
38+
39+
/*==============================================================
40+
= CONSTRUCTOR =
41+
==============================================================*/
42+
43+
/// @param initialOwner The address of the initial owner
44+
/// @param registry The address of the registry contract
45+
constructor(address initialOwner, address registry) public {
46+
if (registry == address(0)) {
47+
revert RegistryAddressCannotBeZero();
48+
}
49+
50+
_registry = MagicDropTokenImplRegistry(registry);
51+
_initializeOwner(initialOwner);
52+
53+
emit MagicDropFactoryInitialized();
54+
}
55+
56+
/*==============================================================
57+
= PUBLIC WRITE METHODS =
58+
==============================================================*/
59+
60+
/// @notice Creates a new deterministic clone of a MagicDrop contract
61+
/// @param name The name of the new contract
62+
/// @param symbol The symbol of the new contract
63+
/// @param standard The token standard of the new contract
64+
/// @param initialOwner The initial owner of the new contract
65+
/// @param implId The implementation ID
66+
/// @param salt A unique salt for deterministic address generation
67+
/// @return The address of the newly created contract
68+
function createContractDeterministic(
69+
string calldata name,
70+
string calldata symbol,
71+
TokenStandard standard,
72+
address payable initialOwner,
73+
uint32 implId,
74+
bytes32 salt
75+
) external payable returns (address) {
76+
address impl;
77+
// Retrieve the implementation address from the registry
78+
if (implId == 0) {
79+
impl = _registry.getDefaultImplementation(standard);
80+
} else {
81+
impl = _registry.getImplementation(standard, implId);
82+
}
83+
84+
if (initialOwner == address(0)) {
85+
revert InitialOwnerCannotBeZero();
86+
}
87+
88+
// Retrieve the deployment fee for the implementation and ensure the caller has sent the correct amount
89+
uint256 deploymentFee = _registry.getDeploymentFee(standard, implId);
90+
if (msg.value < deploymentFee) {
91+
revert InsufficientDeploymentFee();
92+
}
93+
94+
// Create a deterministic clone of the implementation contract
95+
address instance = address(new ZKProxy{salt: salt}(impl));
96+
97+
// Initialize the newly created contract
98+
(bool success,) = instance.call(abi.encodeWithSelector(INITIALIZE_SELECTOR, name, symbol, initialOwner));
99+
if (!success) {
100+
revert InitializationFailed();
101+
}
102+
103+
emit NewContractInitialized({
104+
contractAddress: instance,
105+
initialOwner: initialOwner,
106+
implId: implId,
107+
standard: standard,
108+
name: name,
109+
symbol: symbol
110+
});
111+
112+
return instance;
113+
}
114+
115+
/// @notice Creates a new clone of a MagicDrop contract
116+
/// @param name The name of the new contract
117+
/// @param symbol The symbol of the new contract
118+
/// @param standard The token standard of the new contract
119+
/// @param initialOwner The initial owner of the new contract
120+
/// @param implId The implementation ID
121+
/// @return The address of the newly created contract
122+
function createContract(
123+
string calldata name,
124+
string calldata symbol,
125+
TokenStandard standard,
126+
address payable initialOwner,
127+
uint32 implId
128+
) external payable returns (address) {
129+
address impl;
130+
// Retrieve the implementation address from the registry
131+
if (implId == 0) {
132+
impl = _registry.getDefaultImplementation(standard);
133+
} else {
134+
impl = _registry.getImplementation(standard, implId);
135+
}
136+
137+
if (initialOwner == address(0)) {
138+
revert InitialOwnerCannotBeZero();
139+
}
140+
141+
// Retrieve the deployment fee for the implementation and ensure the caller has sent the correct amount
142+
uint256 deploymentFee = _registry.getDeploymentFee(standard, implId);
143+
if (msg.value < deploymentFee) {
144+
revert InsufficientDeploymentFee();
145+
}
146+
147+
// Create a non-deterministic clone of the implementation contract
148+
address instance = address(new ZKProxy(impl));
149+
150+
// Initialize the newly created contract
151+
(bool success,) = instance.call(abi.encodeWithSelector(INITIALIZE_SELECTOR, name, symbol, initialOwner));
152+
if (!success) {
153+
revert InitializationFailed();
154+
}
155+
156+
emit NewContractInitialized({
157+
contractAddress: instance,
158+
initialOwner: initialOwner,
159+
implId: implId,
160+
standard: standard,
161+
name: name,
162+
symbol: symbol
163+
});
164+
165+
return instance;
166+
}
167+
168+
/*==============================================================
169+
= PUBLIC VIEW METHODS =
170+
==============================================================*/
171+
172+
/// @notice Retrieves the address of the registry contract
173+
/// @return The address of the registry contract
174+
function getRegistry() external view returns (address) {
175+
return address(_registry);
176+
}
177+
178+
/*==============================================================
179+
= ADMIN OPERATIONS =
180+
==============================================================*/
181+
182+
/// @notice Withdraws the contract's balance
183+
function withdraw(address to) external onlyOwner {
184+
(bool success,) = to.call{value: address(this).balance}("");
185+
if (!success) {
186+
revert WithdrawalFailed();
187+
}
188+
189+
emit Withdrawal(to, address(this).balance);
190+
}
191+
192+
/// @dev Overriden to prevent double-initialization of the owner.
193+
function _guardInitializeOwner() internal pure virtual override returns (bool) {
194+
return true;
195+
}
196+
197+
/// @notice Receives ETH
198+
receive() external payable {}
199+
200+
/// @notice Fallback function to receive ETH
201+
fallback() external payable {}
202+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
pragma solidity ^0.8.22;
2+
3+
// This is a ZKSync compatible proxy and a replacement for OZ Clones
4+
contract ZKProxy {
5+
address immutable implementation;
6+
7+
constructor(address _implementation) {
8+
implementation = _implementation;
9+
}
10+
11+
fallback() external payable {
12+
address impl = implementation;
13+
assembly {
14+
// The pointer to the free memory slot
15+
let ptr := mload(0x40)
16+
// Copy function signature and arguments from calldata at zero position into memory at pointer position
17+
calldatacopy(ptr, 0, calldatasize())
18+
// Delegatecall method of the implementation contract returns 0 on error
19+
let result := delegatecall(gas(), impl, ptr, calldatasize(), 0, 0)
20+
// Get the size of the last return data
21+
let size := returndatasize()
22+
// Copy the size length of bytes from return data at zero position to pointer position
23+
returndatacopy(ptr, 0, size)
24+
// Depending on the result value
25+
switch result
26+
case 0 {
27+
// End execution and revert state changes
28+
revert(ptr, size)
29+
}
30+
default {
31+
// Return data with length of size at pointers position
32+
return(ptr, size)
33+
}
34+
}
35+
}
36+
}

0 commit comments

Comments
 (0)