-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathStreamWeightActor.sol
More file actions
164 lines (140 loc) · 7.05 KB
/
Copy pathStreamWeightActor.sol
File metadata and controls
164 lines (140 loc) · 7.05 KB
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
// SPDX-License-Identifier: Apache-2.0 OR MIT
pragma solidity ^0.8.36;
import {IServiceRewardsActor} from "./interfaces/IServiceRewardsActor.sol";
import {Epoch} from "./lib/Epoch.sol";
import {FixedU18} from "./lib/FixedU18.sol";
import {GateParams, GateParamsLibrary} from "./lib/GateParams.sol";
import {FVMRewards} from "./lib/FVMRewards.sol";
import {PendingOp, Share, WeightRecord, WeightRecordUpdate} from "./lib/FVMRewardTypes.sol";
import {OwnersLibrary} from "./lib/Owners.sol";
import {UnanimousGovernance} from "./lib/UnanimousGovernance.sol";
import {IsAMultisig} from "./lib/IsAMultisig.sol";
uint64 constant SERVICE_ID = 2;
int256 constant STEP = 5e16; // 5%
/// @notice Owner-governed actor with sudo control over every f02 stream's weight schedule
/// (FIP-0118, solstice#3): registers, removes, reweights, and reassigns writers by stream id.
/// @dev Writes require unanimous owner approval, except `cancelPending`/`cancelPendingWeight`
/// (any single owner, immediate) and `quarterlyGateCheck` (fully permissionless).
contract StreamWeightActor is UnanimousGovernance {
using IsAMultisig for address;
using OwnersLibrary for address;
IServiceRewardsActor immutable SRA;
Epoch immutable QUARTER;
Epoch immutable HOLD;
/// @notice Deploys the actor with its two initial owners, bound to a Service Rewards Actor.
/// @param owner1 First owner; must be a multisig with more than one signer.
/// @param owner2 Second owner; must be a multisig with more than one signer.
/// @param sra Service Rewards Actor supplying QUARTER/HOLD and gating `quarterlyGateCheck`.
constructor(address owner1, address owner2, IServiceRewardsActor sra) {
owner1.requireMultisig();
owner2.requireMultisig();
owner1.addOwner();
owner2.addOwner();
SRA = sra;
QUARTER = sra.EPOCHS_PER_QUARTER();
HOLD = sra.SRA_CANCEL_HOLD();
GateParamsLibrary.init();
}
/// @notice Queues a new implicit stream.
/// @dev f02 resolves the recipient from protocol state; no writer or share map is stored.
/// @param id Stream id to register.
/// @param record Initial weight schedule.
/// @param activationEpoch Epoch the schedule begins applying.
function registerStream(uint64 id, WeightRecord calldata record, uint64 activationEpoch)
external
unanimousNoHold(keccak256(msg.data))
{
// hold enforced in f02
FVMRewards.registerStream(id, record, activationEpoch);
}
/// @notice Queues a new explicit stream with its designated writer and initial share map.
/// @param id Stream id to register.
/// @param record Initial weight schedule.
/// @param writer Address permitted to update the share map.
/// @param shares Initial wallet-to-share map; must be valid at registration.
/// @param activationEpoch Epoch the schedule begins applying.
function registerStream(
uint64 id,
WeightRecord calldata record,
address writer,
Share[] calldata shares,
uint64 activationEpoch
) external unanimousNoHold(keccak256(msg.data)) {
// hold enforced in f02
FVMRewards.registerStream(id, record, writer, shares, activationEpoch);
}
/// @notice Queues removal of a stream.
/// @param id Stream id to remove.
function removeStream(uint64 id) external unanimousNoHold(keccak256(msg.data)) {
// hold enforced in f02
FVMRewards.removeStream(id);
}
/// @notice Queues a discretionary weight-schedule write for one or more streams.
/// @param updates Id/record pairs to write.
function setWeightRecords(WeightRecordUpdate[] calldata updates) external unanimousNoHold(keccak256(msg.data)) {
// hold enforced in f02
FVMRewards.setWeightRecords(updates);
}
/// @notice Queues a writer change for an explicit stream.
/// @param id Stream id whose writer changes.
/// @param writer New designated writer.
function setDistribution(uint64 id, address writer) external unanimousNoHold(keccak256(msg.data)) {
// hold enforced in f02
FVMRewards.setDistribution(id, writer);
}
/// @notice Cancels a queued per-stream operation (register, remove, or setDistribution).
/// @dev Any current owner, immediate, bypassing unanimity.
/// @param id Stream id the pending operation targets.
/// @param op Kind of pending operation to cancel.
function cancelPending(uint64 id, PendingOp op) external {
// any owner can immediately cancel any pending operation
require(msg.sender.isOwner());
FVMRewards.cancelPending(id, op);
}
/// @notice Cancels a queued discretionary weight-schedule write (SetWeightRecords).
/// @dev Any current owner, immediate. Cannot cancel a gate-originated StepWeightRecords write.
/// @param op Weight operation to cancel.
function cancelPendingWeight(PendingOp op) external {
// any owner can immediately cancel any pending operation
require(msg.sender.isOwner());
FVMRewards.cancelPendingWeight(op);
}
/// @notice Replaces one of the two owners.
/// @param prevOwner Owner being removed.
/// @param newOwner Owner being added; must be a multisig with more than one signer.
function replaceOwner(address prevOwner, address newOwner) external unanimousNoHold(keccak256(msg.data)) {
newOwner.requireMultisig();
prevOwner.removeOwner();
newOwner.addOwner();
}
/// @notice All 8 gate steps have already been taken.
error StepsComplete();
/// @notice Advances the quarterly gate by one quarter, stepping SERVICE_ID's weight schedule
/// if the elapsed quarter's aggregated FPV cleared the next volume threshold.
/// @dev Permissionless; reverts via the SRA if the quarter's FPV is not yet bound.
function quarterlyGateCheck() external {
GateParamsLibrary.GateParamsInfo storage gateParamsInfo = GateParamsLibrary.getGateParamsSlot();
GateParams memory loaded = gateParamsInfo.params;
require(loaded.steps < 8, StepsComplete());
uint64 quarter = ++gateParamsInfo.lastCheckedQuarter;
// NOTE this will enforce afterBinding()
FixedU18 fpv = SRA.aggregatedFPV(quarter);
if (fpv >= loaded.nextThreshold()) {
int256 next = (int256(uint256(loaded.steps)) + 3) * STEP;
WeightRecordUpdate[] memory updates = new WeightRecordUpdate[](1);
updates[0].id = SERVICE_ID;
updates[0].record.floor = next;
updates[0].record.tStart = SRA.qEnd(quarter);
updates[0].record.vStart = next;
updates[0].record.cap = next;
updates[0].record.slope = 0;
gateParamsInfo.params.steps++;
FVMRewards.stepWeightRecords(updates);
}
}
/// @notice Overwrites the quarterly gate's parameters; has a HOLD-epoch timelock after unanimity.
/// @param params New volume target and step state.
function setGateParams(GateParams calldata params) external unanimous(keccak256(msg.data), HOLD) {
GateParamsLibrary.getGateParamsSlot().params = params;
}
}