Skip to content

Commit

Permalink
Merge pull request #5 from bcnmy/feat/fix-linting
Browse files Browse the repository at this point in the history
Feat/fix linting
  • Loading branch information
livingrockrises authored Jul 3, 2024
2 parents 5da6fbe + 9e72f5c commit c62faf2
Show file tree
Hide file tree
Showing 12 changed files with 1,166 additions and 898 deletions.
156 changes: 85 additions & 71 deletions contracts/base/BasePaymaster.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import "account-abstraction/contracts/core/UserOperationLib.sol";
* provides helper methods for staking.
* Validates that the postOp is called only by the entryPoint.
*/

abstract contract BasePaymaster is IPaymaster, SoladyOwnable {
IEntryPoint public immutable entryPoint;

Expand All @@ -25,22 +26,92 @@ abstract contract BasePaymaster is IPaymaster, SoladyOwnable {
entryPoint = _entryPoint;
}

//sanity check: make sure this EntryPoint was compiled against the same
// IEntryPoint of this paymaster
function _validateEntryPointInterface(IEntryPoint _entryPoint) internal virtual {
require(IERC165(address(_entryPoint)).supportsInterface(type(IEntryPoint).interfaceId), "IEntryPoint interface mismatch");
/**
* Add stake for this paymaster.
* This method can also carry eth value to add to the current stake.
* @param unstakeDelaySec - The unstake delay for this paymaster. Can only be increased.
*/
function addStake(uint32 unstakeDelaySec) external payable onlyOwner {
entryPoint.addStake{ value: msg.value }(unstakeDelaySec);
}

/**
* Unlock the stake, in order to withdraw it.
* The paymaster can't serve requests once unlocked, until it calls addStake again
*/
function unlockStake() external onlyOwner {
entryPoint.unlockStake();
}

/**
* Withdraw the entire paymaster's stake.
* stake must be unlocked first (and then wait for the unstakeDelay to be over)
* @param withdrawAddress - The address to send withdrawn value.
*/
function withdrawStake(address payable withdrawAddress) external onlyOwner {
entryPoint.withdrawStake(withdrawAddress);
}

/// @inheritdoc IPaymaster
function postOp(
PostOpMode mode,
bytes calldata context,
uint256 actualGasCost,
uint256 actualUserOpFeePerGas
)
external
override
{
_requireFromEntryPoint();
_postOp(mode, context, actualGasCost, actualUserOpFeePerGas);
}

/// @inheritdoc IPaymaster
function validatePaymasterUserOp(
PackedUserOperation calldata userOp,
bytes32 userOpHash,
uint256 maxCost
) external override returns (bytes memory context, uint256 validationData) {
)
external
override
returns (bytes memory context, uint256 validationData)
{
_requireFromEntryPoint();
return _validatePaymasterUserOp(userOp, userOpHash, maxCost);
}

/**
* Add a deposit for this paymaster, used for paying for transaction fees.
*/
function deposit() external payable virtual {
entryPoint.depositTo{ value: msg.value }(address(this));
}

/**
* Withdraw value from the deposit.
* @param withdrawAddress - Target to send to.
* @param amount - Amount to withdraw.
*/
function withdrawTo(address payable withdrawAddress, uint256 amount) external virtual onlyOwner {
entryPoint.withdrawTo(withdrawAddress, amount);
}

/**
* Return current paymaster's deposit on the entryPoint.
*/
function getDeposit() public view returns (uint256) {
return entryPoint.balanceOf(address(this));
}

//sanity check: make sure this EntryPoint was compiled against the same
// IEntryPoint of this paymaster
function _validateEntryPointInterface(IEntryPoint _entryPoint) internal virtual {
require(
IERC165(address(_entryPoint)).supportsInterface(type(IEntryPoint).interfaceId),
"IEntryPoint interface mismatch"
);
}

/**
* Validate a user operation.
* @param userOp - The user operation.
Expand All @@ -51,18 +122,10 @@ abstract contract BasePaymaster is IPaymaster, SoladyOwnable {
PackedUserOperation calldata userOp,
bytes32 userOpHash,
uint256 maxCost
) internal virtual returns (bytes memory context, uint256 validationData);

/// @inheritdoc IPaymaster
function postOp(
PostOpMode mode,
bytes calldata context,
uint256 actualGasCost,
uint256 actualUserOpFeePerGas
) external override {
_requireFromEntryPoint();
_postOp(mode, context, actualGasCost, actualUserOpFeePerGas);
}
)
internal
virtual
returns (bytes memory context, uint256 validationData);

/**
* Post-operation handler.
Expand All @@ -84,68 +147,19 @@ abstract contract BasePaymaster is IPaymaster, SoladyOwnable {
bytes calldata context,
uint256 actualGasCost,
uint256 actualUserOpFeePerGas
) internal virtual {
)
internal
virtual
{
(mode, context, actualGasCost, actualUserOpFeePerGas); // unused params
// subclass must override this method if validatePaymasterUserOp returns a context
revert("must override");
}

/**
* Add a deposit for this paymaster, used for paying for transaction fees.
*/
function deposit() public virtual payable {
entryPoint.depositTo{value: msg.value}(address(this));
}

/**
* Withdraw value from the deposit.
* @param withdrawAddress - Target to send to.
* @param amount - Amount to withdraw.
*/
function withdrawTo(
address payable withdrawAddress,
uint256 amount
) public virtual onlyOwner {
entryPoint.withdrawTo(withdrawAddress, amount);
}

/**
* Add stake for this paymaster.
* This method can also carry eth value to add to the current stake.
* @param unstakeDelaySec - The unstake delay for this paymaster. Can only be increased.
*/
function addStake(uint32 unstakeDelaySec) external payable onlyOwner {
entryPoint.addStake{value: msg.value}(unstakeDelaySec);
}

/**
* Return current paymaster's deposit on the entryPoint.
*/
function getDeposit() public view returns (uint256) {
return entryPoint.balanceOf(address(this));
}

/**
* Unlock the stake, in order to withdraw it.
* The paymaster can't serve requests once unlocked, until it calls addStake again
*/
function unlockStake() external onlyOwner {
entryPoint.unlockStake();
}

/**
* Withdraw the entire paymaster's stake.
* stake must be unlocked first (and then wait for the unstakeDelay to be over)
* @param withdrawAddress - The address to send withdrawn value.
*/
function withdrawStake(address payable withdrawAddress) external onlyOwner {
entryPoint.withdrawStake(withdrawAddress);
}

/**
* Validate the call is made from a valid entrypoint
*/
function _requireFromEntryPoint() internal virtual {
require(msg.sender == address(entryPoint), "Sender not EntryPoint");
}
}
}
22 changes: 10 additions & 12 deletions contracts/interfaces/IBiconomySponsorshipPaymaster.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,16 @@
pragma solidity ^0.8.26;

interface IBiconomySponsorshipPaymaster {
event PostopCostChanged(uint256 indexed _oldValue, uint256 indexed _newValue);
event FixedPriceMarkupChanged(uint32 indexed _oldValue, uint32 indexed _newValue);
event PostopCostChanged(uint256 indexed oldValue, uint256 indexed newValue);
event FixedPriceMarkupChanged(uint32 indexed oldValue, uint32 indexed newValue);

event VerifyingSignerChanged(address indexed _oldSigner, address indexed _newSigner, address indexed _actor);
event VerifyingSignerChanged(address indexed oldSigner, address indexed newSigner, address indexed actor);

event FeeCollectorChanged(
address indexed _oldFeeCollector, address indexed _newFeeCollector, address indexed _actor
);
event GasDeposited(address indexed _paymasterId, uint256 indexed _value);
event GasWithdrawn(address indexed _paymasterId, address indexed _to, uint256 indexed _value);
event GasBalanceDeducted(address indexed _paymasterId, uint256 indexed _charge, bytes32 indexed userOpHash);
event PremiumCollected(address indexed _paymasterId, uint256 indexed _premium);
event FeeCollectorChanged(address indexed oldFeeCollector, address indexed newFeeCollector, address indexed actor);
event GasDeposited(address indexed paymasterId, uint256 indexed value);
event GasWithdrawn(address indexed paymasterId, address indexed to, uint256 indexed value);
event GasBalanceDeducted(address indexed paymasterId, uint256 indexed charge, bytes32 indexed userOpHash);
event PremiumCollected(address indexed paymasterId, uint256 indexed premium);
event Received(address indexed sender, uint256 value);
event TokensWithdrawn(address indexed _token, address indexed _to, uint256 indexed _amount, address actor);
}
event TokensWithdrawn(address indexed token, address indexed to, uint256 indexed amount, address actor);
}
1 change: 0 additions & 1 deletion contracts/mocks/Imports.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,3 @@ import "account-abstraction/contracts/core/EntryPointSimulations.sol";

import "@biconomy-devx/erc7579-msa/contracts/SmartAccount.sol";
import "@biconomy-devx/erc7579-msa/contracts/factory/AccountFactory.sol";

2 changes: 1 addition & 1 deletion contracts/mocks/MockValidator.sol
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
pragma solidity ^0.8.26;

import "@biconomy-devx/erc7579-msa/test/foundry/mocks/MockValidator.sol";
import "@biconomy-devx/erc7579-msa/test/foundry/mocks/MockValidator.sol";
56 changes: 39 additions & 17 deletions contracts/references/SampleVerifyingPaymaster.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
* - the account checks a signature to prove identity and account ownership.
*/
contract VerifyingPaymaster is BasePaymaster {

using UserOperationLib for PackedUserOperation;

address public immutable verifyingSigner;
Expand All @@ -40,19 +39,25 @@ contract VerifyingPaymaster is BasePaymaster {
* note that this signature covers all fields of the UserOperation, except the "paymasterAndData",
* which will carry the signature itself.
*/
function getHash(PackedUserOperation calldata userOp, uint48 validUntil, uint48 validAfter)
public view returns (bytes32) {
function getHash(
PackedUserOperation calldata userOp,
uint48 validUntil,
uint48 validAfter
)
public
view
returns (bytes32)
{
//can't use userOp.hash(), since it contains also the paymasterAndData itself.
address sender = userOp.getSender();
return
keccak256(
return keccak256(
abi.encode(
sender,
userOp.nonce,
keccak256(userOp.initCode),
keccak256(userOp.callData),
userOp.accountGasLimits,
uint256(bytes32(userOp.paymasterAndData[PAYMASTER_VALIDATION_GAS_OFFSET : PAYMASTER_DATA_OFFSET])),
uint256(bytes32(userOp.paymasterAndData[PAYMASTER_VALIDATION_GAS_OFFSET:PAYMASTER_DATA_OFFSET])),
userOp.preVerificationGas,
userOp.gasFees,
block.chainid,
Expand All @@ -63,21 +68,43 @@ contract VerifyingPaymaster is BasePaymaster {
);
}

function parsePaymasterAndData(bytes calldata paymasterAndData)
public
pure
returns (uint48 validUntil, uint48 validAfter, bytes calldata signature)
{
(validUntil, validAfter) = abi.decode(paymasterAndData[VALID_TIMESTAMP_OFFSET:], (uint48, uint48));
signature = paymasterAndData[SIGNATURE_OFFSET:];
}

/**
* verify our external signer signed this request.
* the "paymasterAndData" is expected to be the paymaster and a signature over the entire request params
* paymasterAndData[:20] : address(this)
* paymasterAndData[20:84] : abi.encode(validUntil, validAfter)
* paymasterAndData[84:] : signature
*/
function _validatePaymasterUserOp(PackedUserOperation calldata userOp, bytes32 /*userOpHash*/, uint256 requiredPreFund)
internal view override returns (bytes memory context, uint256 validationData) {
function _validatePaymasterUserOp(
PackedUserOperation calldata userOp,
bytes32, /*userOpHash*/
uint256 requiredPreFund
)
internal
view
override
returns (bytes memory context, uint256 validationData)
{
(requiredPreFund);

(uint48 validUntil, uint48 validAfter, bytes calldata signature) = parsePaymasterAndData(userOp.paymasterAndData);
(uint48 validUntil, uint48 validAfter, bytes calldata signature) =
parsePaymasterAndData(userOp.paymasterAndData);
//ECDSA library supports both 64 and 65-byte long signatures.
// we only "require" it here so that the revert reason on invalid signature will be of "VerifyingPaymaster", and not "ECDSA"
require(signature.length == 64 || signature.length == 65, "VerifyingPaymaster: invalid signature length in paymasterAndData");
// we only "require" it here so that the revert reason on invalid signature will be of "VerifyingPaymaster", and
// not "ECDSA"
require(
signature.length == 64 || signature.length == 65,
"VerifyingPaymaster: invalid signature length in paymasterAndData"
);
bytes32 hash = MessageHashUtils.toEthSignedMessageHash(getHash(userOp, validUntil, validAfter));

//don't revert on signature failure: return SIG_VALIDATION_FAILED
Expand All @@ -89,9 +116,4 @@ contract VerifyingPaymaster is BasePaymaster {
// by the external service prior to signing it.
return ("", _packValidationData(false, validUntil, validAfter));
}

function parsePaymasterAndData(bytes calldata paymasterAndData) public pure returns (uint48 validUntil, uint48 validAfter, bytes calldata signature) {
(validUntil, validAfter) = abi.decode(paymasterAndData[VALID_TIMESTAMP_OFFSET :], (uint48, uint48));
signature = paymasterAndData[SIGNATURE_OFFSET :];
}
}
}
Loading

0 comments on commit c62faf2

Please sign in to comment.