Skip to content

Commit fd491d7

Browse files
zimphaicemelon
andauthored
Modify access control (#14)
* modify access controll * add ci * remove slither for now --------- Co-authored-by: Haichen Shen <haichen@scroll.io>
1 parent 528ee01 commit fd491d7

17 files changed

+368
-158
lines changed

.github/workflows/contracts.yml

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
name: Contracts
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
paths:
8+
- '**'
9+
- '.github/workflows/contracts.yaml'
10+
pull_request:
11+
types:
12+
- opened
13+
- reopened
14+
- synchronize
15+
- ready_for_review
16+
paths:
17+
- '**'
18+
- '.github/workflows/contracts.yaml'
19+
20+
defaults:
21+
run:
22+
working-directory: '.'
23+
24+
jobs:
25+
foundry:
26+
if: github.event.pull_request.draft == false
27+
runs-on: ubuntu-latest
28+
permissions: {}
29+
30+
steps:
31+
- name: Checkout sources
32+
uses: actions/checkout@v4
33+
with:
34+
submodules: recursive
35+
persist-credentials: false
36+
37+
- name: Install Foundry
38+
uses: foundry-rs/foundry-toolchain@82dee4ba654bd2146511f85f0d013af94670c4de # v1.4.0
39+
with:
40+
version: nightly
41+
42+
- name: Setup LCOV
43+
uses: hrishikesh-kadam/setup-lcov@6c1aa0cc9e1c02f9f58f01ac599f1064ccc83470 # v1
44+
45+
- name: Install Node.js 18
46+
uses: actions/setup-node@v4
47+
with:
48+
node-version: '18'
49+
50+
- name: Get yarn cache directory path
51+
id: yarn-cache-dir-path
52+
run: echo "::set-output name=dir::$(yarn cache dir)"
53+
54+
- name: Cache yarn dependencies
55+
uses: actions/cache@v4
56+
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
57+
with:
58+
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
59+
key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }}
60+
restore-keys: |
61+
${{ runner.os }}-yarn-
62+
63+
- name: Cache node_modules
64+
id: npm_cache
65+
uses: actions/cache@v4
66+
with:
67+
path: node_modules
68+
key: node_modules-${{ hashFiles('yarn.lock') }}
69+
70+
- name: yarn install
71+
# if: steps.npm_cache.outputs.cache-hit != 'true'
72+
run: yarn install
73+
74+
- name: Compile with foundry
75+
run: forge build --evm-version cancun
76+
77+
- name: Run foundry tests
78+
run: forge test --evm-version cancun -vvv
79+
80+
- name: Run foundry coverage
81+
run : forge coverage --evm-version cancun --report lcov
82+
83+
- name : Prune coverage
84+
run : lcov --rc branch_coverage=1 --remove ./lcov.info -o ./lcov.info.pruned 'src/mocks/*' 'test/*' 'script/*' 'node_modules/*' 'lib/*' --ignore-errors unused,unused
85+
86+
- name: Upload coverage reports to Codecov
87+
uses: codecov/codecov-action@ad3126e916f78f00edff4ed0317cf185271ccc2d # v5.4.2
88+
env:
89+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
90+
with:
91+
files: lcov.info.pruned
92+
flags: contracts

.github/workflows/test.yml

Lines changed: 0 additions & 43 deletions
This file was deleted.

script/DeployScroll.s.sol

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ contract DeployScroll is Script {
152152
(
153153
address(usxProxy),
154154
address(0), // Treasury address (will be set later)
155+
admin,
155156
governance
156157
)
157158
);
@@ -179,7 +180,7 @@ contract DeployScroll is Script {
179180
console.log("2.2. Deploying Treasury Proxy...");
180181
bytes memory treasuryInitData = abi.encodeCall(
181182
TreasuryDiamond.initialize,
182-
(usdcAddress, address(usxProxy), address(susxProxy), governance, governanceWarchest, assetManager, insuranceVault)
183+
(usdcAddress, address(usxProxy), address(susxProxy), admin, governance, governanceWarchest, assetManager, insuranceVault)
183184
);
184185

185186
treasuryProxy = address(new ERC1967Proxy(address(treasuryImpl), treasuryInitData));

src/StakedUSX.sol

Lines changed: 48 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ contract StakedUSX is ERC4626Upgradeable, UUPSUpgradeable, ReentrancyGuardUpgrad
2525
error ZeroAddress();
2626
error ZeroAmount();
2727
error NotGovernance();
28+
error NotAdmin();
2829
error NotTreasury();
2930
error WithdrawalAlreadyClaimed();
3031
error WithdrawalPeriodNotPassed();
@@ -38,6 +39,7 @@ contract StakedUSX is ERC4626Upgradeable, UUPSUpgradeable, ReentrancyGuardUpgrad
3839

3940
event TreasurySet(address indexed treasury);
4041
event GovernanceTransferred(address indexed oldGovernance, address indexed newGovernance);
42+
event AdminTransferred(address indexed oldAdmin, address indexed newAdmin);
4143
event EpochAdvanced(uint256 oldEpochBlock, uint256 newEpochBlock);
4244
event WithdrawalRequested(address indexed user, uint256 sharesAmount, uint256 withdrawalId);
4345
event WithdrawalClaimed(address indexed user, uint256 withdrawalId, uint256 usxAmount);
@@ -62,6 +64,11 @@ contract StakedUSX is ERC4626Upgradeable, UUPSUpgradeable, ReentrancyGuardUpgrad
6264
_;
6365
}
6466

67+
modifier onlyAdmin() {
68+
if (msg.sender != _getStorage().admin) revert NotAdmin();
69+
_;
70+
}
71+
6572
modifier onlyTreasury() {
6673
if (msg.sender != address(_getStorage().treasury)) revert NotTreasury();
6774
_;
@@ -88,6 +95,7 @@ contract StakedUSX is ERC4626Upgradeable, UUPSUpgradeable, ReentrancyGuardUpgrad
8895
struct SUSXStorage {
8996
IERC20 USX; // USX token reference (the underlying asset)
9097
ITreasury treasury; // treasury contract
98+
address admin; // address that controls admin of the contract
9199
address governance; // address that controls governance of the contract
92100
uint256 withdrawalPeriod; // withdrawal period in seconds, (default == 15 * 24 * 60 * 60 (15 days))
93101
uint256 withdrawalFeeFraction; // fraction of withdrawals determining the withdrawal fee, (default 0.05% == 500) with precision 6 decimals
@@ -126,7 +134,7 @@ contract StakedUSX is ERC4626Upgradeable, UUPSUpgradeable, ReentrancyGuardUpgrad
126134
/// @param _usx Address of the USX token
127135
/// @param _treasury Address of the Treasury contract
128136
/// @param _governance Address of the governance
129-
function initialize(address _usx, address _treasury, address _governance) public initializer {
137+
function initialize(address _usx, address _treasury, address _admin, address _governance) public initializer {
130138
if (_usx == address(0) || _governance == address(0)) revert ZeroAddress();
131139

132140
// Initialize ERC4626, ERC20, and ReentrancyGuard
@@ -137,6 +145,7 @@ contract StakedUSX is ERC4626Upgradeable, UUPSUpgradeable, ReentrancyGuardUpgrad
137145
SUSXStorage storage $ = _getStorage();
138146
$.USX = IERC20(_usx);
139147
$.treasury = ITreasury(_treasury);
148+
$.admin = _admin;
140149
$.governance = _governance;
141150

142151
// Set default values
@@ -147,7 +156,7 @@ contract StakedUSX is ERC4626Upgradeable, UUPSUpgradeable, ReentrancyGuardUpgrad
147156

148157
/// @notice Set the initial Treasury address - can only be called once when treasury is address(0)
149158
/// @param _treasury Address of the Treasury contract
150-
function initializeTreasury(address _treasury) external onlyGovernance {
159+
function initializeTreasury(address _treasury) external onlyAdmin {
151160
if (_treasury == address(0)) revert ZeroAddress();
152161
SUSXStorage storage $ = _getStorage();
153162
if ($.treasury != ITreasury(address(0))) revert TreasuryAlreadySet();
@@ -216,15 +225,6 @@ contract StakedUSX is ERC4626Upgradeable, UUPSUpgradeable, ReentrancyGuardUpgrad
216225

217226
/*=========================== Governance Functions =========================*/
218227

219-
/// @notice Sets withdrawal period in seconds
220-
/// @param _withdrawalPeriod The new withdrawal period in seconds
221-
function setWithdrawalPeriod(uint256 _withdrawalPeriod) public onlyGovernance {
222-
SUSXStorage storage $ = _getStorage();
223-
uint256 oldPeriod = $.withdrawalPeriod;
224-
$.withdrawalPeriod = _withdrawalPeriod;
225-
emit WithdrawalPeriodSet(oldPeriod, _withdrawalPeriod);
226-
}
227-
228228
/// @notice Sets withdrawal fee with precision to 0.001 percent
229229
/// @param _withdrawalFeeFraction The new withdrawal fee fraction
230230
function setWithdrawalFeeFraction(uint256 _withdrawalFeeFraction) public onlyGovernance {
@@ -235,16 +235,6 @@ contract StakedUSX is ERC4626Upgradeable, UUPSUpgradeable, ReentrancyGuardUpgrad
235235
emit WithdrawalFeeFractionSet(oldFraction, _withdrawalFeeFraction);
236236
}
237237

238-
/// @notice Sets duration of epoch in seconds
239-
/// @param _epochDurationSeconds The new epoch duration in seconds
240-
function setEpochDuration(uint256 _epochDurationSeconds) public onlyGovernance {
241-
if (_epochDurationSeconds < MIN_EPOCH_DURATION) revert InvalidEpochDuration();
242-
SUSXStorage storage $ = _getStorage();
243-
uint256 oldDuration = $.epochDuration;
244-
$.epochDuration = _epochDurationSeconds;
245-
emit EpochDurationSet(oldDuration, _epochDurationSeconds);
246-
}
247-
248238
/// @notice Set new governance address
249239
/// @param newGovernance Address of new governance
250240
function setGovernance(address newGovernance) external onlyGovernance {
@@ -257,15 +247,46 @@ contract StakedUSX is ERC4626Upgradeable, UUPSUpgradeable, ReentrancyGuardUpgrad
257247
emit GovernanceTransferred(oldGovernance, newGovernance);
258248
}
259249

250+
/*=========================== Admin Functions =========================*/
251+
252+
/// @notice Set new admin address
253+
/// @param newAdmin Address of new admin
254+
function setAdmin(address newAdmin) external onlyAdmin {
255+
if (newAdmin == address(0)) revert ZeroAddress();
256+
SUSXStorage storage $ = _getStorage();
257+
address oldAdmin = $.admin;
258+
$.admin = newAdmin;
259+
emit AdminTransferred(oldAdmin, newAdmin);
260+
}
261+
262+
/// @notice Sets withdrawal period in seconds
263+
/// @param _withdrawalPeriod The new withdrawal period in seconds
264+
function setWithdrawalPeriod(uint256 _withdrawalPeriod) public onlyAdmin {
265+
SUSXStorage storage $ = _getStorage();
266+
uint256 oldPeriod = $.withdrawalPeriod;
267+
$.withdrawalPeriod = _withdrawalPeriod;
268+
emit WithdrawalPeriodSet(oldPeriod, _withdrawalPeriod);
269+
}
270+
271+
/// @notice Sets duration of epoch in seconds
272+
/// @param _epochDurationSeconds The new epoch duration in seconds
273+
function setEpochDuration(uint256 _epochDurationSeconds) public onlyAdmin {
274+
if (_epochDurationSeconds < MIN_EPOCH_DURATION) revert InvalidEpochDuration();
275+
SUSXStorage storage $ = _getStorage();
276+
uint256 oldDuration = $.epochDuration;
277+
$.epochDuration = _epochDurationSeconds;
278+
emit EpochDurationSet(oldDuration, _epochDurationSeconds);
279+
}
280+
260281
/// @notice Unpause deposit, allowing users to deposit again
261-
function unpauseDeposit() external onlyGovernance {
282+
function unpauseDeposit() external onlyAdmin {
262283
SUSXStorage storage $ = _getStorage();
263284
$.depositPaused = false;
264285
emit DepositPausedChanged(false);
265286
}
266287

267288
/// @notice Pause deposit, preventing users from depositing USX
268-
function pauseDeposit() external onlyGovernance {
289+
function pauseDeposit() external onlyAdmin {
269290
SUSXStorage storage $ = _getStorage();
270291
$.depositPaused = true;
271292
emit DepositPausedChanged(true);
@@ -415,6 +436,10 @@ contract StakedUSX is ERC4626Upgradeable, UUPSUpgradeable, ReentrancyGuardUpgrad
415436
return _getStorage().governance;
416437
}
417438

439+
function admin() public view returns (address) {
440+
return _getStorage().admin;
441+
}
442+
418443
function withdrawalPeriod() public view returns (uint256) {
419444
return _getStorage().withdrawalPeriod;
420445
}

src/TreasuryDiamond.sol

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,14 @@ contract TreasuryDiamond is Initializable, UUPSUpgradeable, ReentrancyGuardUpgra
5252
address _USDC,
5353
address _USX,
5454
address _sUSX,
55+
address _admin,
5556
address _governance,
5657
address _governanceWarchest,
5758
address _assetManager,
5859
address _insuranceVault
5960
) public initializer {
6061
if (
61-
_USDC == address(0) || _USX == address(0) || _sUSX == address(0) || _governance == address(0)
62+
_USDC == address(0) || _USX == address(0) || _sUSX == address(0) || _admin == address(0) || _governance == address(0)
6263
|| _governanceWarchest == address(0)
6364
) {
6465
revert ZeroAddress();
@@ -71,6 +72,7 @@ contract TreasuryDiamond is Initializable, UUPSUpgradeable, ReentrancyGuardUpgra
7172
$.USDC = IERC20(_USDC);
7273
$.USX = IUSX(_USX);
7374
$.sUSX = IStakedUSX(_sUSX);
75+
$.admin = _admin;
7476
$.governance = _governance;
7577
$.assetManager = _assetManager;
7678
$.governanceWarchest = _governanceWarchest;
@@ -199,6 +201,16 @@ contract TreasuryDiamond is Initializable, UUPSUpgradeable, ReentrancyGuardUpgra
199201
emit InsuranceVaultTransferred(oldInsuranceVault, newInsuranceVault);
200202
}
201203

204+
/// @notice Set new admin address
205+
/// @param newAdmin Address of new admin
206+
function setAdmin(address newAdmin) external onlyAdmin {
207+
if (newAdmin == address(0)) revert ZeroAddress();
208+
TreasuryStorage.TreasuryStorageStruct storage $ = _getStorage();
209+
address oldAdmin = $.admin;
210+
$.admin = newAdmin;
211+
212+
emit AdminTransferred(oldAdmin, newAdmin);
213+
}
202214
/*=========================== Fallback =========================*/
203215

204216
/// @dev Fallback function that delegates calls to facets

src/TreasuryStorage.sol

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,15 @@ contract TreasuryStorage {
2727
error InvalidInsuranceFundFraction();
2828

2929
// Access control errors
30+
error NotAdmin();
3031
error NotGovernance();
3132
error NotAllocator();
3233
error NotTreasury();
3334
error NotReporter();
3435

3536
/*=========================== Events =========================*/
3637

38+
event AdminTransferred(address indexed oldAdmin, address indexed newAdmin);
3739
event GovernanceTransferred(address indexed oldGovernance, address indexed newGovernance);
3840
event GovernanceWarchestTransferred(address indexed oldGovernanceWarchest, address indexed newGovernanceWarchest);
3941
event InsuranceVaultTransferred(address indexed oldInsuranceVault, address indexed newInsuranceVault);
@@ -57,6 +59,12 @@ contract TreasuryStorage {
5759

5860
/*=========================== Modifiers =========================*/
5961

62+
// Modifier to restrict access to admin functions
63+
modifier onlyAdmin() {
64+
if (msg.sender != _getStorage().admin) revert NotAdmin();
65+
_;
66+
}
67+
6068
// Modifier to restrict access to governance functions
6169
modifier onlyGovernance() {
6270
if (msg.sender != _getStorage().governance) revert NotGovernance();
@@ -88,6 +96,7 @@ contract TreasuryStorage {
8896
IUSX USX; // USX token contract
8997
IStakedUSX sUSX; // sUSX vault contract
9098
IERC20 USDC; // USDC token contract
99+
address admin; // Admin address
91100
address governance; // Governance address
92101
address reporter; // Reporter address
93102
address allocator; // Allocator address
@@ -126,6 +135,10 @@ contract TreasuryStorage {
126135
return _getStorage().USDC;
127136
}
128137

138+
function admin() public view returns (address) {
139+
return _getStorage().admin;
140+
}
141+
129142
function governance() public view returns (address) {
130143
return _getStorage().governance;
131144
}

0 commit comments

Comments
 (0)