-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathKlasterPaymaster.sol
More file actions
94 lines (83 loc) · 3.93 KB
/
KlasterPaymaster.sol
File metadata and controls
94 lines (83 loc) · 3.93 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
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@account-abstraction/contracts/core/BasePaymaster.sol";
import "@account-abstraction/contracts/interfaces/IEntryPoint.sol";
import "@account-abstraction/contracts/core/Helpers.sol";
contract KlasterPaymaster is BasePaymaster, ReentrancyGuard {
error EmptyMessageValue();
error InsufficientBalance();
constructor(IEntryPoint _entryPoint) payable BasePaymaster(_entryPoint) {}
function handleOps(UserOperation[] calldata ops) public payable {
if (msg.value == 0) {
revert EmptyMessageValue();
}
entryPoint.depositTo{value: msg.value}(address(this));
entryPoint.handleOps(ops, payable(msg.sender));
entryPoint.withdrawTo(payable(msg.sender), entryPoint.getDepositInfo(address(this)).deposit);
}
function simulateHandleOp(UserOperation calldata op, address target, bytes calldata callData) external payable {
if (msg.value == 0) {
revert EmptyMessageValue();
}
entryPoint.depositTo{value: msg.value}(address(this));
entryPoint.simulateHandleOp(op, target, callData);
}
function simulateValidation(UserOperation calldata op) external payable {
if (msg.value == 0) {
revert EmptyMessageValue();
}
entryPoint.depositTo{value: msg.value}(address(this));
entryPoint.simulateValidation(op);
}
// accept all userOps
function _validatePaymasterUserOp(UserOperation calldata userOp, bytes32 userOpHash, uint256 maxCost)
internal
virtual
override
returns (bytes memory context, uint256 validationData)
{
if (entryPoint.getDepositInfo(address(this)).deposit < maxCost) {
revert InsufficientBalance();
}
(uint256 maxGasLimit, uint256 nodeOperatorPremium) =
abi.decode(userOp.paymasterAndData[20:], (uint256, uint256));
return (abi.encode(userOp.sender, userOp.maxFeePerGas, maxGasLimit, nodeOperatorPremium), 0);
}
/**
* Post-operation handler.
* (verified to be called only through the entryPoint)
* executes userOp and gives back refund to the userOp.sender if userOp.sender has overpaid for execution.
* @dev if subclass returns a non-empty context from validatePaymasterUserOp, it must also implement this method.
* @param mode enum with the following options:
* opSucceeded - user operation succeeded.
* opReverted - user op reverted. still has to pay for gas.
* postOpReverted - user op succeeded, but caused postOp (in mode=opSucceeded) to revert.
* Now this is the 2nd call, after user's op was deliberately reverted.
* @param context - the context value returned by validatePaymasterUserOp
* @param actualGasCost - actual gas used so far (without this postOp call).
*/
function _postOp(PostOpMode mode, bytes calldata context, uint256 actualGasCost) internal virtual override {
if (mode == PostOpMode.postOpReverted) {
return;
}
(address sender, uint256 maxFeePerGas, uint256 maxGasLimit, uint256 nodeOperatorPremium) =
abi.decode(context, (address, uint256, uint256, uint256));
uint256 refund = calculateRefund(maxGasLimit, maxFeePerGas, actualGasCost, nodeOperatorPremium);
if (refund > 0) {
entryPoint.withdrawTo(payable(sender), refund);
}
}
function calculateRefund(
uint256 maxGasLimit,
uint256 maxFeePerGas,
uint256 actualGasCost,
uint256 nodeOperatorPremium
) public pure returns (uint256 refund) {
uint256 costWithPremium = (actualGasCost * (100 + nodeOperatorPremium)) / 100;
uint256 maxCost = maxGasLimit * maxFeePerGas;
if (costWithPremium < maxCost) {
refund = maxCost - costWithPremium;
}
}
}