generated from ZeframLou/foundry-template
-
Notifications
You must be signed in to change notification settings - Fork 2
/
CurveV2xPYTFactory.sol
258 lines (224 loc) · 8.43 KB
/
CurveV2xPYTFactory.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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.4;
import {ERC20} from "solmate/tokens/ERC20.sol";
import {PerpetualYieldToken} from "timeless/PerpetualYieldToken.sol";
import {xPYT} from "../xPYT.sol";
import {CurveV2xPYT} from "./CurveV2xPYT.sol";
import {ICurveFactory} from "./external/ICurveFactory.sol";
import {ICurveCryptoSwap2ETH} from "./external/ICurveCryptoSwap2ETH.sol";
/// @title CurveV2xPYTFactory
/// @author zefram.eth
/// @notice Factory for deploying CurveV2xPYT contracts
contract CurveV2xPYTFactory {
/// -----------------------------------------------------------------------
/// Errors
/// -----------------------------------------------------------------------
error CurveV2xPYTFactory__StringTooLong();
error CurveV2xPYTFactory__PoolCreationFailed();
/// -----------------------------------------------------------------------
/// Events
/// -----------------------------------------------------------------------
event DeployXPYT(
PerpetualYieldToken indexed pyt,
xPYT deployed,
ICurveCryptoSwap2ETH pool
);
/// -----------------------------------------------------------------------
/// Structs
/// -----------------------------------------------------------------------
struct CurvePoolParams {
string name;
string symbol;
uint256 A;
uint256 gamma;
uint256 mid_fee;
uint256 out_fee;
uint256 allowed_extra_profit;
uint256 fee_gamma;
uint256 adjustment_step;
uint256 admin_fee;
uint256 ma_half_time;
uint256 initial_price;
}
/// -----------------------------------------------------------------------
/// Immutable params
/// -----------------------------------------------------------------------
/// @notice The official Curve factory contract for v2 crypto pools
ICurveFactory public immutable curveFactory;
/// -----------------------------------------------------------------------
/// State variables
/// -----------------------------------------------------------------------
/// @notice Records xPYT contracts deployed by the factory
mapping(address => bool) public isXPYT;
/// -----------------------------------------------------------------------
/// Constructor
/// -----------------------------------------------------------------------
constructor(ICurveFactory curveFactory_) {
curveFactory = curveFactory_;
}
/// -----------------------------------------------------------------------
/// Public functions
/// -----------------------------------------------------------------------
/// @notice Deploys a CurveV2xPYT contract and its corresponding Curve V2 pool.
/// @param pyt The PYT to deploy the xPYT for
/// @param name_ The name of the xPYT token
/// @param symbol_ The symbol of the xPYT token
/// @param pounderRewardMultiplier_ The proportion of the yield claimed in pound() to give to the caller as reward
/// @param minOutputMultiplier_ The minimum acceptable ratio between the NYT output in pound() and the expected NYT output
/// based on the TWAP
/// @param curvePoolParams The parameters of the Curve pool to deploy
/// @return deployed The deployed xPYT
/// @return curvePool The deployed Curve pool
function deployCurveV2xPYT(
PerpetualYieldToken pyt,
string calldata name_,
string calldata symbol_,
uint256 pounderRewardMultiplier_,
uint256 minOutputMultiplier_,
CurvePoolParams calldata curvePoolParams
) external returns (CurveV2xPYT deployed, ICurveCryptoSwap2ETH curvePool) {
// deploy xPYT
deployed = new CurveV2xPYT(
pyt,
name_,
symbol_,
pounderRewardMultiplier_,
minOutputMultiplier_
);
// deploy curve pool
address[2] memory coins;
{
coins[0] = address(
pyt.gate().getNegativeYieldTokenForVault(pyt.vault())
); // NYT
coins[1] = address(deployed); // xPYT
}
curvePool = _deployCurvePool(coins, curvePoolParams);
// initialize xPYT
deployed.initialize(curvePool);
// record xPYT in factory
isXPYT[address(deployed)] = true;
// emit deployment event
emit DeployXPYT(pyt, deployed, curvePool);
}
/// @dev Calls the Curve factory and deploys a new Curve v2 crypto pool
function _deployCurvePool(
address[2] memory coins,
CurvePoolParams calldata p
) internal returns (ICurveCryptoSwap2ETH) {
// ensure the lengths of the name and symbol are within limits
if (_getStringLength(p.name) > 32 || _getStringLength(p.symbol) > 10) {
revert CurveV2xPYTFactory__StringTooLong();
}
// incrementally construct calldata to curveFactory.deploy_pool()
// in order to get around the stack-too-deep error
/**
Equivalent to:
return curveFactory.deploy_pool(
p.name,
p.symbol,
coins,
p.A,
p.gamma,
p.mid_fee,
p.out_fee,
p.allowed_extra_profit,
p.fee_gamma,
p.adjustment_step,
p.admin_fee,
p.ma_half_time,
p.initial_price
);
*/
bytes memory cd = new bytes(576); // calldata to the curve factory
address coin0 = coins[0];
address coin1 = coins[1];
uint256 num; // temporary variable for passing contents of p to Yul
// append the pointers to p.name and p.symbol
// append the coins array
assembly {
mstore(
add(cd, 0x20),
0x00000000000000000000000000000000000000000000000000000000000001c0
)
mstore(
add(cd, 0x40),
0x0000000000000000000000000000000000000000000000000000000000000200
)
mstore(add(cd, 0x60), coin0)
mstore(add(cd, 0x80), coin1)
}
// append the numerical parameters
num = p.A;
assembly {
mstore(add(cd, 0xa0), num)
}
num = p.gamma;
assembly {
mstore(add(cd, 0xc0), num)
}
num = p.mid_fee;
assembly {
mstore(add(cd, 0xe0), num)
}
num = p.out_fee;
assembly {
mstore(add(cd, 0x100), num)
}
num = p.allowed_extra_profit;
assembly {
mstore(add(cd, 0x120), num)
}
num = p.fee_gamma;
assembly {
mstore(add(cd, 0x140), num)
}
num = p.adjustment_step;
assembly {
mstore(add(cd, 0x160), num)
}
num = p.admin_fee;
assembly {
mstore(add(cd, 0x180), num)
}
num = p.ma_half_time;
assembly {
mstore(add(cd, 0x1a0), num)
}
num = p.initial_price;
assembly {
mstore(add(cd, 0x1c0), num)
// append the contents of p.name and p.symbol
let pos := add(calldataload(p), p) // the position of p.name in calldata
let tmp := calldataload(pos) // load p.name.length
mstore(add(cd, 0x1e0), tmp)
tmp := calldataload(add(pos, 0x20)) // load p.name
mstore(add(cd, 0x200), tmp)
tmp := calldataload(add(pos, 0x40)) // load p.symbol
mstore(add(cd, 0x220), tmp)
tmp := calldataload(add(pos, 0x60)) // load p.symbol.length
mstore(add(cd, 0x240), tmp)
}
// prepend the function selector
cd = bytes.concat(ICurveFactory.deploy_pool.selector, cd);
// make the call to the curve factory
(bool success, bytes memory result) = address(curveFactory).call(cd);
if (!success) {
revert CurveV2xPYTFactory__PoolCreationFailed();
}
// return the deployed pool
return abi.decode(result, (ICurveCryptoSwap2ETH));
}
/// -----------------------------------------------------------------------
/// Internal utilities
/// -----------------------------------------------------------------------
function _getStringLength(string calldata str)
internal
pure
returns (uint256 len)
{
assembly {
len := str.length
}
}
}