Skip to content

Commit 83c5bb9

Browse files
feat: add _msgSenderOnlyEVCAccountOwner
1 parent 3058f08 commit 83c5bb9

File tree

2 files changed

+52
-6
lines changed

2 files changed

+52
-6
lines changed

src/utils/EVCUtil.sol

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ abstract contract EVCUtil {
5757
/// @dev This modifier can be used on access controlled functions to prevent non-standard authentication paths on
5858
/// the EVC.
5959
modifier onlyEVCAccount() virtual {
60-
_onlyEVCAccount(false);
60+
_authenticateCallerWithStandardContextState(false);
6161
_;
6262
}
6363

@@ -71,7 +71,7 @@ abstract contract EVCUtil {
7171
/// @dev This modifier can be used on access controlled functions to prevent non-standard authentication paths on
7272
/// the EVC.
7373
modifier onlyEVCAccountOwner() virtual {
74-
_onlyEVCAccount(true);
74+
_authenticateCallerWithStandardContextState(true);
7575
_;
7676
}
7777

@@ -133,6 +133,13 @@ abstract contract EVCUtil {
133133
return sender;
134134
}
135135

136+
/// @notice Retrieves the message sender, ensuring it's the EVC account owner and that the execution context is in a
137+
/// standard state (not operator authenticated, not control collateral in progress, not checks in progress).
138+
/// @return The address of the message sender.
139+
function _msgSenderOnlyEVCAccountOwner() internal view returns (address) {
140+
return _authenticateCallerWithStandardContextState(true);
141+
}
142+
136143
/// @notice Calls the current external function through the EVC.
137144
/// @dev This function is used to route the current call through the EVC if it's not already coming from the EVC. It
138145
/// makes the EVC set the execution context and call back this contract with unchanged calldata. msg.sender is used
@@ -177,22 +184,28 @@ abstract contract EVCUtil {
177184
/// onlyAccountOwner is true and the owner was already registered on the EVC, it verifies that the onBehalfOfAccount
178185
/// is the owner. If onlyAccountOwner is false, it allows any EVC account of the owner to call the function.
179186
/// @param onlyAccountOwner If true, only allows the account owner; if false, allows any EVC account of the owner
180-
function _onlyEVCAccount(bool onlyAccountOwner) internal view {
187+
/// @return The address of the message sender.
188+
function _authenticateCallerWithStandardContextState(bool onlyAccountOwner) internal view returns (address) {
181189
if (msg.sender == address(evc)) {
182190
EC ec = EC.wrap(evc.getRawExecutionContext());
183191

184192
if (ec.isOperatorAuthenticated() || ec.isControlCollateralInProgress() || ec.areChecksInProgress()) {
185193
revert NotAuthorized();
186194
}
187195

196+
address onBehalfOfAccount = ec.getOnBehalfOfAccount();
197+
188198
if (onlyAccountOwner) {
189-
address onBehalfOfAccount = ec.getOnBehalfOfAccount();
190199
address owner = evc.getAccountOwner(onBehalfOfAccount);
191200

192201
if (owner != address(0) && owner != onBehalfOfAccount) {
193202
revert NotAuthorized();
194203
}
195204
}
205+
206+
return onBehalfOfAccount;
196207
}
208+
209+
return msg.sender;
197210
}
198211
}

test/unit/EVCUtil/EVCUtil.t.sol

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ contract EVCClient is EVCUtil {
5757
return _msgSenderForBorrow();
5858
}
5959

60+
function msgSenderOnlyEVCAccountOwner() external view returns (address) {
61+
return _msgSenderOnlyEVCAccountOwner();
62+
}
63+
6064
fallback(bytes calldata) external returns (bytes memory) {
6165
return abi.encode(IVault.checkAccountStatus.selector);
6266
}
@@ -191,7 +195,10 @@ contract EVCUtilTest is Test {
191195
evcClient.calledByEVCWithChecksInProgress();
192196
}
193197

194-
function test_calledByEVCAccount_calledByEVCAccountOwner(address caller, uint8 id) external {
198+
function test_calledByEVCAccount_calledByEVCAccountOwner_msgSenderOnlyEVCAccountOwner(
199+
address caller,
200+
uint8 id
201+
) external {
195202
vm.assume(!evc.haveCommonOwner(caller, address(0)) && !evc.haveCommonOwner(caller, address(evc)));
196203
vm.assume(id != 0);
197204

@@ -203,6 +210,9 @@ contract EVCUtilTest is Test {
203210
vm.prank(caller);
204211
evcClient.calledByEVCAccountOwner();
205212

213+
vm.prank(caller);
214+
assertEq(evcClient.msgSenderOnlyEVCAccountOwner(), caller);
215+
206216
// msg.sender is EVC and operator is authenticated
207217
evc.setOperatorAuthenticated(true);
208218
vm.prank(address(evc));
@@ -213,6 +223,10 @@ contract EVCUtilTest is Test {
213223
vm.expectRevert(abi.encodeWithSelector(EVCUtil.NotAuthorized.selector));
214224
evcClient.calledByEVCAccountOwner();
215225

226+
vm.prank(address(evc));
227+
vm.expectRevert(abi.encodeWithSelector(EVCUtil.NotAuthorized.selector));
228+
evcClient.msgSenderOnlyEVCAccountOwner();
229+
216230
// msg.sender is EVC and control collateral is in progress
217231
evc.setOperatorAuthenticated(false);
218232
evc.setControlCollateralInProgress(true);
@@ -224,6 +238,10 @@ contract EVCUtilTest is Test {
224238
vm.expectRevert(abi.encodeWithSelector(EVCUtil.NotAuthorized.selector));
225239
evcClient.calledByEVCAccountOwner();
226240

241+
vm.prank(address(evc));
242+
vm.expectRevert(abi.encodeWithSelector(EVCUtil.NotAuthorized.selector));
243+
evcClient.msgSenderOnlyEVCAccountOwner();
244+
227245
// msg.sender is EVC and checks are in progress
228246
evc.setControlCollateralInProgress(false);
229247
evc.setChecksInProgress(true);
@@ -235,6 +253,10 @@ contract EVCUtilTest is Test {
235253
vm.expectRevert(abi.encodeWithSelector(EVCUtil.NotAuthorized.selector));
236254
evcClient.calledByEVCAccountOwner();
237255

256+
vm.prank(address(evc));
257+
vm.expectRevert(abi.encodeWithSelector(EVCUtil.NotAuthorized.selector));
258+
evcClient.msgSenderOnlyEVCAccountOwner();
259+
238260
// msg.sender is EVC, the owner is not registered yet
239261
evc.setChecksInProgress(false);
240262
evc.setOnBehalfOfAccount(caller);
@@ -246,6 +268,10 @@ contract EVCUtilTest is Test {
246268
vm.prank(address(evc));
247269
evcClient.calledByEVCAccountOwner();
248270

271+
assertEq(evc.getAccountOwner(caller), address(0));
272+
vm.prank(address(evc));
273+
assertEq(evcClient.msgSenderOnlyEVCAccountOwner(), caller);
274+
249275
// msg.sender is EVC, the owner is registered and the authenticated account is the owner
250276
evc.setOnBehalfOfAccount(address(0));
251277
vm.prank(caller);
@@ -256,10 +282,13 @@ contract EVCUtilTest is Test {
256282
evcClient.calledByEVCAccount();
257283

258284
assertEq(evc.getAccountOwner(caller), caller);
259-
evc.setOnBehalfOfAccount(caller);
260285
vm.prank(address(evc));
261286
evcClient.calledByEVCAccountOwner();
262287

288+
assertEq(evc.getAccountOwner(caller), caller);
289+
vm.prank(address(evc));
290+
assertEq(evcClient.msgSenderOnlyEVCAccountOwner(), caller);
291+
263292
// msg.sender is EVC, the owner is registered but the authenticated account is not the owner
264293
evc.setOnBehalfOfAccount(address(uint160(uint160(caller) ^ id)));
265294
vm.prank(address(evc));
@@ -268,6 +297,10 @@ contract EVCUtilTest is Test {
268297
vm.prank(address(evc));
269298
vm.expectRevert(abi.encodeWithSelector(EVCUtil.NotAuthorized.selector));
270299
evcClient.calledByEVCAccountOwner();
300+
301+
vm.prank(address(evc));
302+
vm.expectRevert(abi.encodeWithSelector(EVCUtil.NotAuthorized.selector));
303+
evcClient.msgSenderOnlyEVCAccountOwner();
271304
}
272305

273306
function test_msgSender(address caller) external {

0 commit comments

Comments
 (0)