-
Notifications
You must be signed in to change notification settings - Fork 15
/
Controller_Incentivized.sol
324 lines (294 loc) · 11.9 KB
/
Controller_Incentivized.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
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.13;
import { AggregatorV3Interface } from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
import { IEmptySetReserve } from "@equilibria/emptyset-batcher/interfaces/IEmptySetReserve.sol";
import { Kept } from "@equilibria/root/attribute/Kept/Kept.sol";
import { Fixed6, Fixed6Lib } from "@equilibria/root/number/types/Fixed6.sol";
import { UFixed6, UFixed6Lib } from "@equilibria/root/number/types/UFixed6.sol";
import { UFixed18, UFixed18Lib } from "@equilibria/root/number/types/UFixed18.sol";
import { Token6 } from "@equilibria/root/token/types/Token6.sol";
import { Token18 } from "@equilibria/root/token/types/Token18.sol";
import { IVerifierBase } from "@equilibria/root/verifier/interfaces/IVerifierBase.sol";
import { IMarket } from "@perennial/core/contracts/interfaces/IMarket.sol";
import { IMarketFactory } from "@perennial/core/contracts/interfaces/IMarketFactory.sol";
import { IAccount } from "./interfaces/IAccount.sol";
import { IController } from "./interfaces/IController.sol";
import { IRelayer } from "./interfaces/IRelayer.sol";
import { Controller, IAccountVerifier } from "./Controller.sol";
import { Action } from "./types/Action.sol";
import { DeployAccount } from "./types/DeployAccount.sol";
import { MarketTransfer } from "./types/MarketTransfer.sol";
import { RebalanceConfigChange } from "./types/RebalanceConfigChange.sol";
import { RelayedNonceCancellation } from "./types/RelayedNonceCancellation.sol";
import { RelayedGroupCancellation } from "./types/RelayedGroupCancellation.sol";
import { RelayedOperatorUpdate } from "./types/RelayedOperatorUpdate.sol";
import { RelayedSignerUpdate } from "./types/RelayedSignerUpdate.sol";
import { RelayedAccessUpdateBatch } from "./types/RelayedAccessUpdateBatch.sol";
import { Withdrawal } from "./types/Withdrawal.sol";
/// @title Controller_Incentivized
/// @notice Controller which compensates keepers for handling or relaying messages. Subclass to handle differences in
/// gas calculations on different chains.
abstract contract Controller_Incentivized is Controller, IRelayer, Kept {
/// @dev Handles relayed messages for nonce cancellation
IVerifierBase public immutable nonceManager;
/// @dev Configuration used to calculate keeper compensation
KeepConfig public keepConfig;
/// @dev Configuration used to calculate keeper compensation with buffered gas
KeepConfig public keepConfigBuffered;
/// @dev Configuration used to calculate keeper compensation to withdraw from a collateral account
KeepConfig public keepConfigWithdrawal;
/// @dev Creates instance of Controller which compensates keepers
/// @param implementation_ Pristine collateral account contract
/// @param marketFactory_ Market factory contract
/// @param nonceManager_ Verifier contract to which nonce and group cancellations are relayed
constructor(
address implementation_,
IMarketFactory marketFactory_,
IVerifierBase nonceManager_
) Controller(implementation_, marketFactory_) {
nonceManager = nonceManager_;
}
/// @notice Configures message verification and keeper compensation
/// @param verifier_ Contract used to validate collateral account message signatures
/// @param chainlinkFeed_ ETH-USD price feed used for calculating keeper compensation
/// @param keepConfig_ Configuration used for unbuffered keeper compensation
/// @param keepConfigBuffered_ Configuration used for buffered keeper compensation
/// @param keepConfigWithdrawal_ Configuration used to compensate keepers for withdrawals
function initialize(
IAccountVerifier verifier_,
AggregatorV3Interface chainlinkFeed_,
KeepConfig memory keepConfig_,
KeepConfig memory keepConfigBuffered_,
KeepConfig memory keepConfigWithdrawal_
) external initializer(1) {
__Factory__initialize();
__Kept__initialize(chainlinkFeed_, DSU);
verifier = verifier_;
keepConfig = keepConfig_;
keepConfigBuffered = keepConfigBuffered_;
keepConfigWithdrawal = keepConfigWithdrawal_;
}
/// @inheritdoc IController
function changeRebalanceConfigWithSignature(
RebalanceConfigChange calldata configChange,
bytes calldata signature
)
external
override
keepCollateralAccount(
configChange.action.common.account,
abi.encode(configChange, signature),
configChange.action.maxFee,
0
)
{
_changeRebalanceConfigWithSignature(configChange, signature);
}
/// @inheritdoc IController
function deployAccountWithSignature(
DeployAccount calldata deployAccount_,
bytes calldata signature
)
external
override
keepCollateralAccount(
deployAccount_.action.common.account,
abi.encode(deployAccount_, signature),
deployAccount_.action.maxFee,
0
)
{
_deployAccountWithSignature(deployAccount_, signature);
}
/// @inheritdoc IController
function marketTransferWithSignature(
MarketTransfer calldata marketTransfer,
bytes calldata signature
)
external
override
keepCollateralAccount(
marketTransfer.action.common.account,
abi.encode(marketTransfer, signature),
marketTransfer.action.maxFee,
1
)
{
IAccount account = IAccount(getAccountAddress(marketTransfer.action.common.account));
_marketTransferWithSignature(account, marketTransfer, signature);
}
/// @inheritdoc IController
function rebalanceGroup(
address owner,
uint256 group
)
external
override
keepCollateralAccount(
owner,
abi.encode(owner, group),
groupToMaxRebalanceFee[owner][group],
groupToMarkets[owner][group].length
)
{
_rebalanceGroup(owner, group);
}
/// @inheritdoc IController
function withdrawWithSignature(
Withdrawal calldata withdrawal,
bytes calldata signature
) override external {
address account = getAccountAddress(withdrawal.action.common.account);
// levy fee prior to withdrawal
bytes memory data = abi.encode(account, withdrawal.action.maxFee);
_handleKeeperFee(
keepConfigWithdrawal,
0, // no way to calculate applicable gas prior to invocation
abi.encode(withdrawal, signature),
0,
data
);
_withdrawWithSignature(IAccount(account), withdrawal, signature);
}
/// @inheritdoc IRelayer
function relayNonceCancellation(
RelayedNonceCancellation calldata message,
bytes calldata outerSignature,
bytes calldata innerSignature
)
external
override
keepCollateralAccount(
message.action.common.account,
abi.encode(message, outerSignature, innerSignature),
message.action.maxFee,
0
)
{
// ensure the message was signed by the owner or a delegated signer
verifier.verifyRelayedNonceCancellation(message, outerSignature);
// relay the message to Verifier
nonceManager.cancelNonceWithSignature(message.nonceCancellation, innerSignature);
}
/// @inheritdoc IRelayer
function relayGroupCancellation(
RelayedGroupCancellation calldata message,
bytes calldata outerSignature,
bytes calldata innerSignature
)
external
override
keepCollateralAccount(
message.action.common.account,
abi.encode(message, outerSignature, innerSignature),
message.action.maxFee,
0
)
{
// ensure the message was signed by the owner or a delegated signer
verifier.verifyRelayedGroupCancellation(message, outerSignature);
// relay the message to Verifier
nonceManager.cancelGroupWithSignature(message.groupCancellation, innerSignature);
}
/// @inheritdoc IRelayer
function relayOperatorUpdate(
RelayedOperatorUpdate calldata message,
bytes calldata outerSignature,
bytes calldata innerSignature
)
external
override
keepCollateralAccount(
message.action.common.account,
abi.encode(message, outerSignature, innerSignature),
message.action.maxFee,
0
)
{
// ensure the message was signed by the owner or a delegated signer
verifier.verifyRelayedOperatorUpdate(message, outerSignature);
// relay the message to MarketFactory
marketFactory.updateOperatorWithSignature(message.operatorUpdate, innerSignature);
}
/// @inheritdoc IRelayer
function relaySignerUpdate(
RelayedSignerUpdate calldata message,
bytes calldata outerSignature,
bytes calldata innerSignature
)
external
override
keepCollateralAccount(
message.action.common.account,
abi.encode(message, outerSignature, innerSignature),
message.action.maxFee,
0
)
{
// ensure the message was signed by the owner or a delegated signer
verifier.verifyRelayedSignerUpdate(message, outerSignature);
// relay the message to MarketFactory
marketFactory.updateSignerWithSignature(message.signerUpdate, innerSignature);
}
/// @inheritdoc IRelayer
function relayAccessUpdateBatch(
RelayedAccessUpdateBatch calldata message,
bytes calldata outerSignature,
bytes calldata innerSignature
)
external
override
keepCollateralAccount(
message.action.common.account,
abi.encode(message, outerSignature, innerSignature),
message.action.maxFee,
0
)
{
// ensure the message was signed by the owner or a delegated signer
verifier.verifyRelayedAccessUpdateBatch(message, outerSignature);
// relay the message to MarketFactory
marketFactory.updateAccessBatchWithSignature(message.accessUpdateBatch, innerSignature);
}
/// @dev Transfers funds from collateral account to controller, and limits compensation
/// to the user-defined maxFee in the Action message
/// @param amount Calculated keeper fee
/// @param data Encoded address of collateral account and UFixed6 user-specified maximum fee
/// @return raisedKeeperFee Amount pulled from controller to keeper
function _raiseKeeperFee(
UFixed18 amount,
bytes memory data
) internal virtual override returns (UFixed18 raisedKeeperFee) {
(address account, UFixed6 maxFee) = abi.decode(data, (address, UFixed6));
raisedKeeperFee = amount.min(UFixed18Lib.from(maxFee));
// if the account has insufficient DSU to pay the fee, wrap
IAccount(account).wrapIfNecessary(raisedKeeperFee, false);
// transfer DSU to the Controller, such that Kept can transfer to keeper
DSU.pull(account, raisedKeeperFee);
}
modifier keepCollateralAccount(
address account,
bytes memory applicableCalldata,
UFixed6 maxFee,
uint256 bufferMultiplier
) {
bytes memory data = abi.encode(getAccountAddress(account), maxFee);
uint256 startGas = gasleft();
_;
uint256 applicableGas = startGas - gasleft();
_handleKeeperFee(
bufferMultiplier > 0
? KeepConfig(
keepConfigBuffered.multiplierBase,
keepConfigBuffered.bufferBase * (bufferMultiplier),
keepConfigBuffered.multiplierCalldata,
keepConfigBuffered.bufferCalldata * (bufferMultiplier)
)
: keepConfig,
applicableGas,
applicableCalldata,
0,
data
);
}
}