-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathPrivateOfferFactory.sol
180 lines (163 loc) · 8.07 KB
/
PrivateOfferFactory.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.23;
import "@openzeppelin/contracts/utils/Create2.sol";
import "../PrivateOffer.sol";
import "./VestingCloneFactory.sol";
/**
* @title PrivateOfferFactory
* @author malteish, cjentzsch
* @notice This contract deploys PrivateOffers using create2. It is used to deploy PrivateOffers with a deterministic address.
* I also deploys the vesting contracts used for token lockup.
* @dev One deployment of this contract can be used for deployment of any number of PrivateOffers using create2.
*/
contract PrivateOfferFactory {
event Deploy(address indexed privateOffer);
event NewPrivateOfferWithLockup(address privateOffer, address vesting);
VestingCloneFactory public immutable vestingCloneFactory;
constructor(VestingCloneFactory _vestingCloneFactory) {
require(address(_vestingCloneFactory) != address(0), "VestingCloneFactory must not be 0");
vestingCloneFactory = _vestingCloneFactory;
}
/**
* @notice Deploys a contract using create2. During the deployment, `_currencyPayer` pays `_currencyReceiver` for the purchase of `_tokenAmount` tokens at `_tokenPrice` per token.
* The tokens are minted to `_tokenReceiver`. The token is deployed at `_token` and the currency is `_currency`.
*/
function deployPrivateOffer(
bytes32 _rawSalt,
PrivateOfferArguments calldata _arguments
) external returns (address) {
return _deployPrivateOffer(_rawSalt, _arguments);
}
/**
* @notice Deploys a contract using create2. During the deployment, `_currencyPayer` pays `_currencyReceiver` for the purchase of `_tokenAmount` tokens at `_tokenPrice` per token.
* The tokens are minted to `_tokenReceiver`. The token is deployed at `_token` and the currency is `_currency`.
* @param _rawSalt Value influencing the addresses of the deployed contract, but nothing else.
* @param _arguments Arguments for the PrivateOffer contract.
* @param _vestingStart The start of the vesting period.
* @param _vestingCliff The cliff of the vesting period.
* @param _vestingDuration The duration of the vesting period.
* @param _vestingContractOwner The owner of the vesting contract.
*/
function deployPrivateOfferWithTimeLock(
bytes32 _rawSalt,
PrivateOfferArguments calldata _arguments,
uint64 _vestingStart,
uint64 _vestingCliff,
uint64 _vestingDuration,
address _vestingContractOwner,
address trustedForwarder
) external returns (address) {
// deploy the vesting contract
Vesting vesting = Vesting(
vestingCloneFactory.createVestingCloneWithLockupPlan(
_rawSalt,
trustedForwarder,
_vestingContractOwner,
address(_arguments.token),
_arguments.tokenAmount,
_arguments.tokenReceiver,
_vestingStart,
_vestingCliff,
_vestingDuration
)
);
// update currency receiver to be the vesting contract
PrivateOfferArguments memory calldataArguments = _arguments;
calldataArguments.tokenReceiver = address(vesting);
// deploy the private offer
address privateOffer = _deployPrivateOffer(_rawSalt, calldataArguments);
require(_arguments.token.balanceOf(address(vesting)) == _arguments.tokenAmount, "Execution failed");
emit NewPrivateOfferWithLockup(address(privateOffer), address(vesting));
return address(vesting);
}
/**
* @notice Predicts the addresses of the PrivateOffer and Vesting contracts that would be deployed with the given parameters.
* @param _rawSalt Value influencing the addresses of the deployed contracts, but nothing else.
* @param _arguments Arguments for the PrivateOffer contract.
* @param _vestingStart Begin of the vesting period.
* @param _vestingCliff Cliff duration.
* @param _vestingDuration Total vesting duration.
* @param _vestingContractOwner Address that will own the vesting contract (note: this is not the token receiver or the beneficiary, but rather the company admin)
* @param trustedForwarder ERC2771 trusted forwarder address
* @return privateOfferAddress The address of the PrivateOffer contract that would be deployed.
* @return vestingAddress The address of the Vesting contract that would be deployed.
*/
function predictPrivateOfferAndTimeLockAddress(
bytes32 _rawSalt,
PrivateOfferArguments calldata _arguments,
uint64 _vestingStart,
uint64 _vestingCliff,
uint64 _vestingDuration,
address _vestingContractOwner,
address trustedForwarder
) public view returns (address, address) {
address vestingAddress = vestingCloneFactory.predictCloneAddressWithLockupPlan(
_rawSalt,
trustedForwarder,
_vestingContractOwner,
address(_arguments.token),
_arguments.tokenAmount,
_arguments.tokenReceiver,
_vestingStart,
_vestingCliff,
_vestingDuration
);
// since the vesting contracts address will be used as the token receiver, we need to use it for the prediction
PrivateOfferArguments memory arguments = _arguments;
arguments.tokenReceiver = vestingAddress;
address privateOfferAddress = predictPrivateOfferAddress(_rawSalt, arguments);
return (privateOfferAddress, vestingAddress);
}
/**
* @notice Predicts the address of the PrivateOffer contract that would be deployed with the given parameters.
* @param _salt Value influencing the addresses of the deployed contract, but nothing else.
* @param _arguments Parameters for the PrivateOffer contract (which also influence the address of the deployed contract)
*/
function predictPrivateOfferAddress(
bytes32 _salt,
PrivateOfferArguments memory _arguments
) public view returns (address) {
bytes memory bytecode = _getBytecode(_arguments);
return Create2.computeAddress(_salt, keccak256(bytecode));
}
/**
* Calculates a salt from all input parameters.
* @param _rawSalt Value influencing the addresses of the deployed contract, but nothing else.
* @param _arguments Arguments for the PrivateOffer contract.
* @param _vestingStart Begin of the vesting period.
* @param _vestingCliff Cliff duration.
* @param _vestingDuration Total vesting duration.
* @param _vestingContractOwner Address that will own the vesting contract (note: this is not the token receiver or the beneficiary, but rather the company admin)
*/
function _getSalt(
bytes32 _rawSalt,
PrivateOfferArguments calldata _arguments,
uint64 _vestingStart,
uint64 _vestingCliff,
uint64 _vestingDuration,
address _vestingContractOwner
) private pure returns (bytes32) {
return
keccak256(
abi.encode(_rawSalt, _arguments, _vestingStart, _vestingCliff, _vestingDuration, _vestingContractOwner)
);
}
/**
* @dev Generates the bytecode of the contract to be deployed, using the parameters.
* @param _arguments Arguments for the PrivateOffer contract.
* @return bytecode of the contract to be deployed.
*/
function _getBytecode(PrivateOfferArguments memory _arguments) private pure returns (bytes memory) {
return abi.encodePacked(type(PrivateOffer).creationCode, abi.encode(_arguments));
}
/**
* Creates a PrivateOffer contract using create2.
* @param _rawSalt Value influencing the addresses of the deployed contract, but nothing else.
* @param _arguments Parameters for the PrivateOffer contract (which also influence the address of the deployed contract)
*/
function _deployPrivateOffer(bytes32 _rawSalt, PrivateOfferArguments memory _arguments) private returns (address) {
address privateOffer = Create2.deploy(0, _rawSalt, _getBytecode(_arguments));
emit Deploy(privateOffer);
return privateOffer;
}
}