@piplabs/story-contracts 0.1.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of @piplabs/story-contracts might be problematic. Click here for more details.
- package/README.md +65 -0
- package/index.js +43 -0
- package/package.json +51 -0
- package/script/GenerateAlloc.s.sol +448 -0
- package/script/upgrades/DeployNewIPTokenStaking_V1_0_1.s.sol +42 -0
- package/script/utils/ChainIds.sol +12 -0
- package/script/utils/EIP1967Helper.sol +37 -0
- package/script/utils/InitializableHelper.sol +66 -0
- package/src/deploy/Create3.sol +62 -0
- package/src/interfaces/IIPTokenStaking.sol +303 -0
- package/src/interfaces/IUBIPool.sol +37 -0
- package/src/interfaces/IUpgradeEntrypoint.sol +33 -0
- package/src/libraries/Predeploys.sol +50 -0
- package/src/protocol/IPTokenStaking.sol +509 -0
- package/src/protocol/Secp256k1Verifier.sol +109 -0
- package/src/protocol/UBIPool.sol +98 -0
- package/src/protocol/UpgradeEntrypoint.sol +40 -0
- package/src/token/WIP.sol +90 -0
- package/test/data/ValidatorData.sol +122 -0
- package/test/deploy/Create3.t.sol +63 -0
- package/test/erc6551/Erc6551Registry.t.sol +33 -0
- package/test/secp256k/Secp256k1PubKeyVerifier.t.sol +50 -0
- package/test/stake/IPTokenStaking.t.sol +1022 -0
- package/test/timelock/Timelock.t.sol +136 -0
- package/test/token/WIP.t.sol +198 -0
- package/test/ubipool/UBIPool.t.sol +239 -0
- package/test/upgrade-entrypoint/UpgradeEntryPoint.t.sol +37 -0
- package/test/upgrades/PredeployUpgrades.t.sol +219 -0
- package/test/utils/Test.sol +170 -0
@@ -0,0 +1,219 @@
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-only
|
2
|
+
pragma solidity 0.8.23;
|
3
|
+
|
4
|
+
import { ProxyAdmin } from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
|
5
|
+
// solhint-disable-next-line max-line-length
|
6
|
+
import { ITransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
|
7
|
+
|
8
|
+
import { IPTokenStaking } from "../../src/protocol/IPTokenStaking.sol";
|
9
|
+
import { UpgradeEntrypoint } from "../../src/protocol/UpgradeEntrypoint.sol";
|
10
|
+
import { UBIPool } from "../../src/protocol/UBIPool.sol";
|
11
|
+
|
12
|
+
import { EIP1967Helper } from "../../script/utils/EIP1967Helper.sol";
|
13
|
+
import { Predeploys } from "../../src/libraries/Predeploys.sol";
|
14
|
+
import { Test } from "../utils/Test.sol";
|
15
|
+
|
16
|
+
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
|
17
|
+
|
18
|
+
abstract contract MockNewFeatures {
|
19
|
+
function foo() external pure returns (string memory) {
|
20
|
+
return "bar";
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
contract IPTokenStakingV2 is IPTokenStaking, MockNewFeatures {
|
25
|
+
constructor(uint256 defaultMinFee, uint256 maxDataLength) IPTokenStaking(defaultMinFee, maxDataLength) {}
|
26
|
+
}
|
27
|
+
|
28
|
+
contract UpgradeEntrypointV2 is UpgradeEntrypoint, MockNewFeatures {}
|
29
|
+
|
30
|
+
contract UBIPoolV2 is UBIPool, MockNewFeatures {
|
31
|
+
constructor(uint32 maxUBIPercentage) UBIPool(maxUBIPercentage) {}
|
32
|
+
}
|
33
|
+
|
34
|
+
contract InitialImplementation {
|
35
|
+
function foo() external pure returns (string memory) {
|
36
|
+
return "bar";
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
/**
|
41
|
+
* @title PredeployUpgrades
|
42
|
+
* @dev A script to test upgrading the precompile contracts
|
43
|
+
*/
|
44
|
+
contract PredeployUpgrades is Test {
|
45
|
+
function testUpgradeStaking() public {
|
46
|
+
// ---- Staking
|
47
|
+
address newImpl = address(
|
48
|
+
new IPTokenStakingV2(
|
49
|
+
1 ether, // defaultMinFee
|
50
|
+
256 // maxDataLength
|
51
|
+
)
|
52
|
+
);
|
53
|
+
ProxyAdmin proxyAdmin = ProxyAdmin(EIP1967Helper.getAdmin(Predeploys.Staking));
|
54
|
+
assertEq(proxyAdmin.owner(), address(timelock));
|
55
|
+
|
56
|
+
performTimelocked(
|
57
|
+
address(proxyAdmin),
|
58
|
+
abi.encodeWithSelector(
|
59
|
+
ProxyAdmin.upgradeAndCall.selector,
|
60
|
+
ITransparentUpgradeableProxy(Predeploys.Staking),
|
61
|
+
newImpl,
|
62
|
+
""
|
63
|
+
)
|
64
|
+
);
|
65
|
+
|
66
|
+
assertEq(EIP1967Helper.getImplementation(Predeploys.Staking), newImpl, "Staking not upgraded");
|
67
|
+
assertEq(
|
68
|
+
keccak256(abi.encode(IPTokenStakingV2(Predeploys.Staking).foo())),
|
69
|
+
keccak256(abi.encode("bar")),
|
70
|
+
"Upgraded to wrong iface"
|
71
|
+
);
|
72
|
+
}
|
73
|
+
|
74
|
+
function testUpgradeUpgradeEntrypoint() public {
|
75
|
+
// ---- Upgrades
|
76
|
+
address newImpl = address(new UpgradeEntrypointV2());
|
77
|
+
ProxyAdmin proxyAdmin = ProxyAdmin(EIP1967Helper.getAdmin(Predeploys.Upgrades));
|
78
|
+
assertEq(proxyAdmin.owner(), address(timelock));
|
79
|
+
|
80
|
+
performTimelocked(
|
81
|
+
address(proxyAdmin),
|
82
|
+
abi.encodeWithSelector(
|
83
|
+
ProxyAdmin.upgradeAndCall.selector,
|
84
|
+
ITransparentUpgradeableProxy(Predeploys.Upgrades),
|
85
|
+
newImpl,
|
86
|
+
""
|
87
|
+
)
|
88
|
+
);
|
89
|
+
assertEq(EIP1967Helper.getImplementation(Predeploys.Upgrades), newImpl, "Upgrades not upgraded");
|
90
|
+
assertEq(
|
91
|
+
keccak256(abi.encode(IPTokenStakingV2(Predeploys.Upgrades).foo())),
|
92
|
+
keccak256(abi.encode("bar")),
|
93
|
+
"Upgraded to wrong iface"
|
94
|
+
);
|
95
|
+
}
|
96
|
+
|
97
|
+
function testUpgradeUBIPool() public {
|
98
|
+
// ---- UBIPool
|
99
|
+
address newImpl = address(new UBIPoolV2(10_00));
|
100
|
+
ProxyAdmin proxyAdmin = ProxyAdmin(EIP1967Helper.getAdmin(Predeploys.UBIPool));
|
101
|
+
assertEq(proxyAdmin.owner(), address(timelock));
|
102
|
+
|
103
|
+
performTimelocked(
|
104
|
+
address(proxyAdmin),
|
105
|
+
abi.encodeWithSelector(
|
106
|
+
ProxyAdmin.upgradeAndCall.selector,
|
107
|
+
ITransparentUpgradeableProxy(Predeploys.UBIPool),
|
108
|
+
newImpl,
|
109
|
+
""
|
110
|
+
)
|
111
|
+
);
|
112
|
+
assertEq(EIP1967Helper.getImplementation(Predeploys.UBIPool), newImpl, "Upgrades not upgraded");
|
113
|
+
assertEq(
|
114
|
+
keccak256(abi.encode(IPTokenStakingV2(Predeploys.UBIPool).foo())),
|
115
|
+
keccak256(abi.encode("bar")),
|
116
|
+
"Upgraded to wrong iface"
|
117
|
+
);
|
118
|
+
}
|
119
|
+
|
120
|
+
function testUpgradeUnusedProxies() public {
|
121
|
+
address initialImpl = address(new InitialImplementation());
|
122
|
+
|
123
|
+
for (
|
124
|
+
uint160 i = uint160(Predeploys.Upgrades) + uint160(1);
|
125
|
+
i <= uint160(Predeploys.Namespace) + Predeploys.NamespaceSize;
|
126
|
+
i++
|
127
|
+
) {
|
128
|
+
// Verify predeploy is proxied and not upgraded
|
129
|
+
address predeploy = address(i);
|
130
|
+
assertTrue(Predeploys.proxied(predeploy), "Predeploy not proxied");
|
131
|
+
assertEq(
|
132
|
+
EIP1967Helper.getImplementation(predeploy),
|
133
|
+
Predeploys.getImplAddress(predeploy),
|
134
|
+
"Predeploy upgraded already"
|
135
|
+
);
|
136
|
+
ProxyAdmin proxyAdmin = ProxyAdmin(EIP1967Helper.getAdmin(predeploy));
|
137
|
+
assertEq(proxyAdmin.owner(), address(timelock));
|
138
|
+
|
139
|
+
// Revert if not owner
|
140
|
+
vm.expectRevert();
|
141
|
+
proxyAdmin.upgradeAndCall(ITransparentUpgradeableProxy(predeploy), initialImpl, "");
|
142
|
+
|
143
|
+
// Upgrade predeploy
|
144
|
+
performTimelocked(
|
145
|
+
address(proxyAdmin),
|
146
|
+
abi.encodeWithSelector(
|
147
|
+
ProxyAdmin.upgradeAndCall.selector,
|
148
|
+
ITransparentUpgradeableProxy(predeploy),
|
149
|
+
initialImpl,
|
150
|
+
""
|
151
|
+
)
|
152
|
+
);
|
153
|
+
|
154
|
+
// Verify predeploy is upgraded
|
155
|
+
assertEq(EIP1967Helper.getImplementation(predeploy), initialImpl, "Predeploy not upgraded");
|
156
|
+
assertEq(
|
157
|
+
keccak256(abi.encode(InitialImplementation(predeploy).foo())),
|
158
|
+
keccak256(abi.encode("bar")),
|
159
|
+
"Upgraded to wrong iface"
|
160
|
+
);
|
161
|
+
}
|
162
|
+
}
|
163
|
+
|
164
|
+
function testRenounceUpgradeability() public {
|
165
|
+
address newImpl = address(new InitialImplementation());
|
166
|
+
// For each predeploy, renounce upgradeability
|
167
|
+
for (
|
168
|
+
uint160 i = uint160(Predeploys.Namespace) + uint160(1);
|
169
|
+
i <= uint160(Predeploys.Namespace) + Predeploys.NamespaceSize;
|
170
|
+
i++
|
171
|
+
) {
|
172
|
+
address predeploy = address(i);
|
173
|
+
ProxyAdmin proxyAdmin = ProxyAdmin(EIP1967Helper.getAdmin(predeploy));
|
174
|
+
assertEq(proxyAdmin.owner(), address(timelock));
|
175
|
+
// Renounce ownership
|
176
|
+
performTimelocked(address(proxyAdmin), abi.encodeWithSelector(Ownable.renounceOwnership.selector));
|
177
|
+
|
178
|
+
// Verify ownership was renounced
|
179
|
+
assertEq(proxyAdmin.owner(), address(0), "Ownership not renounced");
|
180
|
+
|
181
|
+
// Try to upgrade
|
182
|
+
bytes memory upgradeAction = abi.encodeWithSelector(
|
183
|
+
ProxyAdmin.upgradeAndCall.selector,
|
184
|
+
ITransparentUpgradeableProxy(predeploy),
|
185
|
+
newImpl,
|
186
|
+
""
|
187
|
+
);
|
188
|
+
bytes memory expectedReason = abi.encodeWithSelector(
|
189
|
+
Ownable.OwnableUnauthorizedAccount.selector,
|
190
|
+
address(timelock)
|
191
|
+
);
|
192
|
+
expectRevertTimelocked(address(proxyAdmin), upgradeAction, expectedReason);
|
193
|
+
}
|
194
|
+
}
|
195
|
+
|
196
|
+
function testUpgradeLastPredeploy() public {
|
197
|
+
address newImpl = address(new InitialImplementation());
|
198
|
+
address lastPredeploy = address(uint160(Predeploys.Namespace) + uint160(Predeploys.NamespaceSize));
|
199
|
+
ProxyAdmin proxyAdmin = ProxyAdmin(EIP1967Helper.getAdmin(lastPredeploy));
|
200
|
+
assertEq(proxyAdmin.owner(), address(timelock));
|
201
|
+
|
202
|
+
// Upgrade last predeploy to new implementation
|
203
|
+
performTimelocked(
|
204
|
+
address(proxyAdmin),
|
205
|
+
abi.encodeWithSelector(
|
206
|
+
ProxyAdmin.upgradeAndCall.selector,
|
207
|
+
ITransparentUpgradeableProxy(lastPredeploy),
|
208
|
+
newImpl,
|
209
|
+
""
|
210
|
+
)
|
211
|
+
);
|
212
|
+
assertEq(EIP1967Helper.getImplementation(lastPredeploy), newImpl, "Last predeploy not upgraded");
|
213
|
+
assertEq(
|
214
|
+
keccak256(abi.encode(InitialImplementation(lastPredeploy).foo())),
|
215
|
+
keccak256(abi.encode("bar")),
|
216
|
+
"Upgraded to wrong iface"
|
217
|
+
);
|
218
|
+
}
|
219
|
+
}
|
@@ -0,0 +1,170 @@
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-only
|
2
|
+
pragma solidity 0.8.23;
|
3
|
+
/* solhint-disable no-console */
|
4
|
+
/* solhint-disable max-line-length */
|
5
|
+
|
6
|
+
import { Test as ForgeTest } from "forge-std/Test.sol";
|
7
|
+
|
8
|
+
import { IPTokenStaking } from "../../src/protocol/IPTokenStaking.sol";
|
9
|
+
import { UpgradeEntrypoint } from "../../src/protocol/UpgradeEntrypoint.sol";
|
10
|
+
import { UBIPool } from "../../src/protocol/UBIPool.sol";
|
11
|
+
import { Predeploys } from "../../src/libraries/Predeploys.sol";
|
12
|
+
import { Create3 } from "../../src/deploy/Create3.sol";
|
13
|
+
import { GenerateAlloc } from "../../script/GenerateAlloc.s.sol";
|
14
|
+
import { TimelockController } from "@openzeppelin/contracts/governance/TimelockController.sol";
|
15
|
+
import { ERC6551Registry } from "erc6551/ERC6551Registry.sol";
|
16
|
+
import { WIP } from "../../src/token/WIP.sol";
|
17
|
+
|
18
|
+
contract Test is ForgeTest {
|
19
|
+
address internal admin = address(0x123);
|
20
|
+
address internal executor = address(0x456);
|
21
|
+
address internal guardian = address(0x789);
|
22
|
+
|
23
|
+
address internal deployer = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd);
|
24
|
+
|
25
|
+
IPTokenStaking internal ipTokenStaking;
|
26
|
+
UpgradeEntrypoint internal upgradeEntrypoint;
|
27
|
+
UBIPool internal ubiPool;
|
28
|
+
Create3 internal create3;
|
29
|
+
ERC6551Registry internal erc6551Registry;
|
30
|
+
TimelockController internal timelock;
|
31
|
+
WIP internal wip;
|
32
|
+
|
33
|
+
function setUp() public virtual {
|
34
|
+
GenerateAlloc initializer = new GenerateAlloc();
|
35
|
+
initializer.disableStateDump(); // Faster tests. Don't call to verify JSON output
|
36
|
+
initializer.setAdminAddresses(admin, executor, guardian);
|
37
|
+
initializer.run();
|
38
|
+
ipTokenStaking = IPTokenStaking(Predeploys.Staking);
|
39
|
+
upgradeEntrypoint = UpgradeEntrypoint(Predeploys.Upgrades);
|
40
|
+
ubiPool = UBIPool(Predeploys.UBIPool);
|
41
|
+
create3 = Create3(Predeploys.Create3);
|
42
|
+
wip = WIP(payable(Predeploys.WIP));
|
43
|
+
erc6551Registry = ERC6551Registry(Predeploys.ERC6551Registry);
|
44
|
+
address timelockAddress = create3.getDeployed(deployer, keccak256("STORY_TIMELOCK_CONTROLLER"));
|
45
|
+
timelock = TimelockController(payable(timelockAddress));
|
46
|
+
require(timelockAddress.code.length > 0, "Timelock not deployed");
|
47
|
+
}
|
48
|
+
|
49
|
+
/// @notice schedules, waits for timelock and executes a timelocked call
|
50
|
+
/// @param target The address to call
|
51
|
+
/// @param data The data to call with
|
52
|
+
function performTimelocked(address target, bytes memory data) internal {
|
53
|
+
uint256 minDelay = timelock.getMinDelay();
|
54
|
+
vm.prank(admin);
|
55
|
+
timelock.schedule(
|
56
|
+
target,
|
57
|
+
0, // value
|
58
|
+
data,
|
59
|
+
bytes32(0), // predecessor: Non Zero if order must be respected
|
60
|
+
bytes32(keccak256("SALT")), // salt
|
61
|
+
minDelay
|
62
|
+
);
|
63
|
+
vm.warp(block.timestamp + minDelay + 1);
|
64
|
+
vm.prank(executor);
|
65
|
+
timelock.execute(
|
66
|
+
target,
|
67
|
+
0, // value
|
68
|
+
data,
|
69
|
+
bytes32(0), // predecessor: Non Zero if order must be respected
|
70
|
+
bytes32(keccak256("SALT")) // salt
|
71
|
+
);
|
72
|
+
}
|
73
|
+
|
74
|
+
/// @notice schedules, waits for timelock and executes a timelocked call, with a custom salt
|
75
|
+
/// @dev This is to be used if we want to call the same target with the same data multiple times
|
76
|
+
/// @param target The address to call
|
77
|
+
/// @param data The data to call with
|
78
|
+
/// @param salt The salt to use for the timelock
|
79
|
+
function performTimelocked(address target, bytes memory data, bytes32 salt) internal {
|
80
|
+
uint256 minDelay = timelock.getMinDelay();
|
81
|
+
vm.prank(admin);
|
82
|
+
timelock.schedule(
|
83
|
+
target,
|
84
|
+
0, // value
|
85
|
+
data,
|
86
|
+
bytes32(0), // predecessor: Non Zero if order must be respected
|
87
|
+
bytes32(salt), // salt
|
88
|
+
minDelay
|
89
|
+
);
|
90
|
+
vm.warp(block.timestamp + minDelay + 1);
|
91
|
+
vm.prank(executor);
|
92
|
+
timelock.execute(
|
93
|
+
target,
|
94
|
+
0, // value
|
95
|
+
data,
|
96
|
+
bytes32(0), // predecessor: Non Zero if order must be respected
|
97
|
+
bytes32(salt) // salt
|
98
|
+
);
|
99
|
+
}
|
100
|
+
|
101
|
+
/// @notice schedules a timelocked call
|
102
|
+
/// @param target The address to call
|
103
|
+
/// @param data The data to call with
|
104
|
+
function schedule(address target, bytes memory data) internal {
|
105
|
+
uint256 minDelay = timelock.getMinDelay();
|
106
|
+
vm.prank(admin);
|
107
|
+
timelock.schedule(
|
108
|
+
target,
|
109
|
+
0, // value
|
110
|
+
data,
|
111
|
+
bytes32(0), // predecessor: Non Zero if order must be respected
|
112
|
+
bytes32(keccak256("SALT")), // salt
|
113
|
+
minDelay
|
114
|
+
);
|
115
|
+
}
|
116
|
+
|
117
|
+
/// @notice waits for timelock (minDelay)
|
118
|
+
/// @dev This is to be called after schedule()
|
119
|
+
/// If the scheduled time > minDelay, this wait time won't be enough
|
120
|
+
/// and the test will revert
|
121
|
+
function waitForTimelock() internal {
|
122
|
+
uint256 minDelay = timelock.getMinDelay();
|
123
|
+
vm.warp(block.timestamp + minDelay + 1);
|
124
|
+
}
|
125
|
+
|
126
|
+
/// @notice executes a timelocked call
|
127
|
+
/// @param target The address to call
|
128
|
+
/// @param data The data to call with
|
129
|
+
function executeTimelocked(address target, bytes memory data) internal {
|
130
|
+
vm.prank(executor);
|
131
|
+
timelock.execute(
|
132
|
+
target,
|
133
|
+
0, // value
|
134
|
+
data,
|
135
|
+
bytes32(0), // predecessor: Non Zero if order must be respected
|
136
|
+
bytes32(keccak256("SALT")) // salt
|
137
|
+
);
|
138
|
+
}
|
139
|
+
|
140
|
+
/// @notice schedules, waits for timelock and executes a timelocked call that is expected to revert
|
141
|
+
/// @param target The address to call
|
142
|
+
/// @param data The data to call with
|
143
|
+
/// @param reason The expected revert reason
|
144
|
+
function expectRevertTimelocked(address target, bytes memory data, bytes memory reason) internal {
|
145
|
+
uint256 minDelay = timelock.getMinDelay();
|
146
|
+
vm.prank(admin);
|
147
|
+
timelock.schedule(
|
148
|
+
target,
|
149
|
+
0, // value
|
150
|
+
data,
|
151
|
+
bytes32(0), // predecessor: Non Zero if order must be respected
|
152
|
+
bytes32(keccak256("SALT")), // salt
|
153
|
+
minDelay
|
154
|
+
);
|
155
|
+
vm.warp(block.timestamp + minDelay + 1);
|
156
|
+
vm.prank(executor);
|
157
|
+
vm.expectRevert(bytes(reason));
|
158
|
+
timelock.execute(
|
159
|
+
target,
|
160
|
+
0, // value
|
161
|
+
data,
|
162
|
+
bytes32(0), // predecessor: Non Zero if order must be respected
|
163
|
+
bytes32(keccak256("SALT")) // salt
|
164
|
+
);
|
165
|
+
// Cancel the scheduled call to clean the hash in the timelock
|
166
|
+
bytes32 id = timelock.hashOperation(target, 0, data, bytes32(0), bytes32(keccak256("SALT")));
|
167
|
+
vm.prank(admin);
|
168
|
+
timelock.cancel(id);
|
169
|
+
}
|
170
|
+
}
|