Skip to content

Commit 5628e7f

Browse files
committed
Handling ssc cost via stakes.
1 parent 3f88c50 commit 5628e7f

File tree

4 files changed

+96
-20
lines changed

4 files changed

+96
-20
lines changed

contracts/core/EntryPoint.sol

Lines changed: 69 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import "../interfaces/IAccount.sol";
77
import "../interfaces/IAccountExecute.sol";
88
import "../interfaces/IPaymaster.sol";
99
import "../interfaces/IEntryPoint.sol";
10+
import "../interfaces/ISscOpcodes.sol";
1011

1112
import "../utils/Exec.sol";
1213
import "./StakeManager.sol";
@@ -28,8 +29,20 @@ contract EntryPoint is IEntryPoint, StakeManager, NonceManager, ReentrancyGuard,
2829

2930
using UserOperationLib for PackedUserOperation;
3031

32+
int256 constant public SSC_STORAGE_SLOT_COST = 12800000000000000;
33+
int256 constant public SSC_STORAGE_SLOT_REFUND = 12672000000000000;
34+
35+
int256 constant public SSC_ACCOUNT_COST = 12800000000000000;
36+
int256 constant public SSC_ACCOUNT_REFUND = 12672000000000000;
37+
38+
int256 constant public SSC_CODE_CREATED_COST = 12800000000000000;
39+
40+
uint256 constant public SSC_MAX_SPEND_FACTOR = 10;
41+
3142
SenderCreator private immutable _senderCreator = new SenderCreator();
3243

44+
ISscOpcodes private immutable _ssc = ISscOpcodes(address(0x665e930982A9a03c844641d453a2C3462ED7Ff41));
45+
3346
function senderCreator() internal view virtual returns (SenderCreator) {
3447
return _senderCreator;
3548
}
@@ -174,10 +187,12 @@ contract EntryPoint is IEntryPoint, StakeManager, NonceManager, ReentrancyGuard,
174187
function handleOps(
175188
PackedUserOperation[] calldata ops,
176189
address payable beneficiary
177-
) public nonReentrant {
190+
) public nonReentrant payable {
178191
uint256 opslen = ops.length;
179192
UserOpInfo[] memory opInfos = new UserOpInfo[](opslen);
180193

194+
int256 preSSC = _netSSC();
195+
181196
unchecked {
182197
for (uint256 i = 0; i < opslen; i++) {
183198
UserOpInfo memory opInfo = opInfos[i];
@@ -200,7 +215,12 @@ contract EntryPoint is IEntryPoint, StakeManager, NonceManager, ReentrancyGuard,
200215
collected += _executeUserOp(i, ops[i], opInfos[i]);
201216
}
202217

203-
_compensate(beneficiary, collected);
218+
// this can be negative in cases of refunds
219+
int256 netSSC = _netSSC() - preSSC;
220+
// In case of negative netSSC msg.value must be higher than abs(netSSC)
221+
uint256 sscCompensation = uint256(int256(msg.value) + netSSC);
222+
223+
_compensate(beneficiary, collected + sscCompensation);
204224
}
205225
}
206226

@@ -303,23 +323,10 @@ contract EntryPoint is IEntryPoint, StakeManager, NonceManager, ReentrancyGuard,
303323
uint256 preOpGas;
304324
}
305325

306-
/**
307-
* Inner function to handle a UserOperation.
308-
* Must be declared "external" to open a call context, but it can only be called by handleOps.
309-
* @param callData - The callData to execute.
310-
* @param opInfo - The UserOpInfo struct.
311-
* @param context - The context bytes.
312-
* @return actualGasCost - the actual cost in eth this UserOperation paid for gas
313-
*/
314-
function innerHandleOp(
315-
bytes memory callData,
316-
UserOpInfo memory opInfo,
317-
bytes calldata context
318-
) external returns (uint256 actualGasCost) {
319-
uint256 preGas = gasleft();
326+
function innerExecuteCall(MemoryUserOp memory mUserOp, bytes memory callData) external {
320327
require(msg.sender == address(this), "AA92 internal call only");
321-
MemoryUserOp memory mUserOp = opInfo.mUserOp;
322328

329+
int256 preSSC = _netSSC();
323330
uint256 callGasLimit = mUserOp.callGasLimit;
324331
unchecked {
325332
// handleOps was called with gas limit too low. abort entire bundle.
@@ -335,12 +342,46 @@ contract EntryPoint is IEntryPoint, StakeManager, NonceManager, ReentrancyGuard,
335342
}
336343
}
337344
}
345+
if (callData.length > 0) {
346+
bool success = Exec.call(mUserOp.sender, 0, callData, callGasLimit);
347+
if (!success) {
348+
require(false, string(Exec.getReturnData(REVERT_REASON_MAX_LEN)));
349+
} else {
350+
int256 netSSC = _netSSC() - preSSC;
351+
if (netSSC < 0) {
352+
// increment deposit for the user's account
353+
_incrementDeposit(mUserOp.sender, uint256(-netSSC));
354+
} else {
355+
require(uint256(netSSC) <= SSC_MAX_SPEND_FACTOR * callGasLimit * getUserOpGasPrice(mUserOp), "AA97 ssc max cost violation");
356+
_decrementDeposit(mUserOp.sender, uint256(netSSC));
357+
}
358+
}
359+
}
360+
}
361+
362+
/**
363+
* Inner function to handle a UserOperation.
364+
* Must be declared "external" to open a call context, but it can only be called by handleOps.
365+
* @param callData - The callData to execute.
366+
* @param opInfo - The UserOpInfo struct.
367+
* @param context - The context bytes.
368+
* @return actualGasCost - the actual cost in eth this UserOperation paid for gas
369+
*/
370+
function innerHandleOp(
371+
bytes memory callData,
372+
UserOpInfo memory opInfo,
373+
bytes calldata context
374+
) external returns (uint256 actualGasCost) {
375+
uint256 preGas = gasleft();
376+
require(msg.sender == address(this), "AA92 internal call only");
377+
MemoryUserOp memory mUserOp = opInfo.mUserOp;
338378

339379
IPaymaster.PostOpMode mode = IPaymaster.PostOpMode.opSucceeded;
340380
if (callData.length > 0) {
341-
bool success = Exec.call(mUserOp.sender, 0, callData, callGasLimit);
381+
(bool success, bytes memory result) = address(this).call(
382+
abi.encodeWithSelector(this.innerExecuteCall.selector, mUserOp, callData)
383+
);
342384
if (!success) {
343-
bytes memory result = Exec.getReturnData(REVERT_REASON_MAX_LEN);
344385
if (result.length > 0) {
345386
emit UserOperationRevertReason(
346387
opInfo.userOpHash,
@@ -413,6 +454,15 @@ contract EntryPoint is IEntryPoint, StakeManager, NonceManager, ReentrancyGuard,
413454
}
414455
}
415456

457+
/**
458+
* Calculates total ssc cost / refund
459+
*/
460+
function _netSSC() internal view returns (int256) {
461+
return int256(_ssc.accountsCreated()) * SSC_ACCOUNT_COST - int256(_ssc.accountsCleared()) * SSC_ACCOUNT_REFUND
462+
+ int256(_ssc.slotsCreated()) * SSC_STORAGE_SLOT_COST - int256(_ssc.slotsCleared()) * SSC_STORAGE_SLOT_REFUND
463+
+ int256(_ssc.codeCreated()) * SSC_CODE_CREATED_COST;
464+
}
465+
416466
/**
417467
* Create sender smart contract account if init code is provided.
418468
* @param opIndex - The operation index.

contracts/core/StakeManager.sol

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,22 @@ abstract contract StakeManager is IStakeManager {
5656
return newAmount;
5757
}
5858

59+
/**
60+
* Increments an account's deposit.
61+
* @param account - The account to increment.
62+
* @param amount - The amount to increment by.
63+
* @return the updated deposit of this account
64+
*/
65+
function _decrementDeposit(address account, uint256 amount) internal returns (uint256) {
66+
unchecked {
67+
DepositInfo storage info = deposits[account];
68+
require(info.deposit >= amount, "cannot decrement stake");
69+
uint256 newAmount = info.deposit - amount;
70+
info.deposit = newAmount;
71+
return newAmount;
72+
}
73+
}
74+
5975
/**
6076
* Add to the deposit of the given account.
6177
* @param account - The account to add to.

contracts/interfaces/IEntryPoint.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ interface IEntryPoint is IStakeManager, INonceManager {
154154
function handleOps(
155155
PackedUserOperation[] calldata ops,
156156
address payable beneficiary
157-
) external;
157+
) external payable;
158158

159159
/**
160160
* Execute a batch of UserOperation with Aggregators
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// SPDX-License-Identifier: GPL-3.0
2+
pragma solidity ^0.8.23;
3+
4+
interface ISscOpcodes {
5+
function accountsCreated() external view returns (uint256); // D0
6+
function accountsCleared() external view returns (uint256); // D1
7+
function slotsCreated() external view returns (uint256); // D2
8+
function slotsCleared() external view returns (uint256); // D3
9+
function codeCreated() external view returns (uint256); // D4
10+
}

0 commit comments

Comments
 (0)