> ## Documentation Index
> Fetch the complete documentation index at: https://etherspot.fyi/llms.txt
> Use this file to discover all available pages before exploring further.

# Paymaster

## Overview

EtherspotPaymaster is a smart contract that allows an external signer to sign a UserOperation and pay for the gas costs of executing that UserOperation. The paymaster signs to agree to pay for gas, and the wallet signs to prove identity and account ownership.

## Version

Solidity pragma version `^0.8.12`.

## Global Variables

* `VALID_TIMESTAMP_OFFSET`: A constant of type uint256 that represents a 20 second time offset used to validate timestamps.
* `SIGNATURE_OFFSET`: A constant of type uint256 that represents an 84-byte signature offset.
* `COST_OF_POST`: The pre-calculated cost of calling `_postOp` (required for gas calculation).

## Imports

* `ECDSA`: A contract from the OpenZeppelin library used for signature verification.
* `IERC20`: A contract from the OpenZeppelin library used for interacting with ERC20 tokens.
* `SafeERC20`: A contract from the OpenZeppelin library used for safe ERC20 token transfers.
* `Whitelist`: Whitelist.sol smart contract used for whitelisting addresses.

## Mappings

* `sponsorFunds`: A mapping of type `mapping(address => uint256)` used to store the amount of sponsor funds transferred to the paymaster contract.
* `senderNonce`: A mapping of type `mapping(address => uint256)` used to store the nonce of the sender.

## Events

* `SponsorSuccessful`: An event emitted when a sponsor successfully sponsors a user operation.
* `SponsorUnsuccessful`: An event emitted when a sponsor is unsuccessful in sponsoring a user operation.

## Constructor

* `constructor(IEntryPoint _entryPoint)`: A constructor that accepts an `IEntryPoint` parameter `_entryPoint`.

## Public/External Functions

* `depositFunds() external payable`: A function used to deposit funds to the paymaster.
  * Error `EtherspotPaymaster:: Not enough balance`: Checks that the sponsor has enough funds to deposit into paymaster contract.
* `withdrawFunds() address payable _sponsor, uint256 _amount) external`: A function used to withdraw sponsor funds from paymaster.
  * Error `EtherspotPaymaster:: can only withdraw own funds`: Checks `msg.sender` matches the sponsor address provided.
  * Error `EtherspotPaymaster:: not enough deposited funds`: Checks amount is >= deposited funds for the given sponsor.
* `checkSponsorFunds(address _sponsor) public view returns (uint256)`: A function used to check the amount of sponsor funds transferred to the paymaster contract for a given sponsor.
* `function getHash(UserOperation calldata userOp, uint48 validUntil, uint48 validAfter) public view returns (bytes32)`: A function to return the hash to be sign off-chain (and validate on-chain) by a sponsor.
* `function parsePaymasterAndData(bytes calldata paymasterAndData) public pure returns (uint48 validUntil, uint48 validAfter, bytes calldata signature)`: Extracts `validUntil`, `validAfter` and `signature` from `paymasterAndData` passed in as input.

## Internal Functions

* `_debitSponsor(address _sponsor, uint256 _amount) internal`: A function used to debit a sponsor's fund amount for gas costs once a transaction has been processed.
* `_creditSponsor`: A function used to credit a sponsor's deposited amount.
* `_pack(UserOperation calldata userOp)`: A function used to pack the user operation.
* `_validatePaymasterUserOp(UserOperation calldata userOp, bytes32 userOpHash, uint256 requiredPreFund)`: A function used to verify the external signer (sponsor) that signed the request. Debits the sponsor's deposited balance by full requiredPreFund amount (credits back in `_postOp`).
  * Error `EtherspotPaymaster:: invalid signature length in paymasterAndData`: Triggered on incorrect signature length.
  * Error `EtherspotPaymaster:: Sponsor paymaster funds too low`: Checks sponsor has enough funds to pay the gas costs for a sponsored UserOperation.
* `_postOp(PostOpMode mode, bytes calldata context, uint256 actualGasCost) internal override`: A function that overrides the `_postOp` function from `BasePaymaster.sol` that checks for a validated UserOperation and credits back any remaining funds after the gas cost for the UserOperation execution plus `_postOp` call.
  * Emits `SponsorSuccessful(paymaster, sender, userOpHash)` on successfully sponsored UserOperation.
  * Emits `SponsorUnsuccessful(paymaster, sender, userOpHash)` on unsuccessfully sponsored UserOperation.

## Contract Source Code

```Solidity theme={null}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;

/* solhint-disable reason-string */

import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "../../account-abstraction/contracts/core/UserOperationLib.sol";
import "./BasePaymaster.sol";
import "./Whitelist.sol";

/**
 * A sample paymaster that uses external service to decide whether to pay for the UserOp.
 * The paymaster trusts an external signer to sign the transaction.
 * The calling user must pass the UserOp to that external signer first, which performs
 * whatever off-chain verification before signing the UserOp.
 * Note that this signature is NOT a replacement for wallet signature:
 * - the paymaster signs to agree to PAY for GAS.
 * - the wallet signs to prove identity and account ownership.
 */
contract EtherspotPaymaster is BasePaymaster, Whitelist, ReentrancyGuard {
    using ECDSA for bytes32;
    using UserOperationLib for UserOperation;

    uint256 private constant VALID_TIMESTAMP_OFFSET = 20;
    uint256 private constant SIGNATURE_OFFSET = 84;
    // calculated cost of the postOp
    uint256 private constant COST_OF_POST = 40000;

    mapping(address => uint256) private _sponsorBalances;

    event SponsorSuccessful(address paymaster, address sender);

    constructor(IEntryPoint _entryPoint) BasePaymaster(_entryPoint) {}

    function depositFunds() external payable nonReentrant {
        _creditSponsor(msg.sender, msg.value);
        entryPoint.depositTo{value: msg.value}(address(this));
    }

    function withdrawFunds(uint256 _amount) external nonReentrant {
        require(
            getSponsorBalance(msg.sender) >= _amount,
            "EtherspotPaymaster:: not enough deposited funds"
        );
        _debitSponsor(msg.sender, _amount);
        entryPoint.withdrawTo(payable(msg.sender), _amount);
    }

    function getSponsorBalance(address _sponsor) public view returns (uint256) {
        return _sponsorBalances[_sponsor];
    }

    function _debitSponsor(address _sponsor, uint256 _amount) internal {
        _sponsorBalances[_sponsor] -= _amount;
    }

    function _creditSponsor(address _sponsor, uint256 _amount) internal {
        _sponsorBalances[_sponsor] += _amount;
    }

    function _pack(
        UserOperation calldata userOp
    ) internal pure returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    userOp.getSender(),
                    userOp.nonce,
                    keccak256(userOp.initCode),
                    keccak256(userOp.callData),
                    userOp.callGasLimit,
                    userOp.verificationGasLimit,
                    userOp.preVerificationGas,
                    userOp.maxFeePerGas,
                    userOp.maxPriorityFeePerGas
                )
            );
    }

    /**
     * return the hash we're going to sign off-chain (and validate on-chain)
     * this method is called by the off-chain service, to sign the request.
     * it is called on-chain from the validatePaymasterUserOp, to validate the signature.
     * note that this signature covers all fields of the UserOperation, except the "paymasterAndData",
     * which will carry the signature itself.
     */
    function getHash(
        UserOperation calldata userOp,
        uint48 validUntil,
        uint48 validAfter
    ) public view returns (bytes32) {
        //can't use userOp.hash(), since it contains also the paymasterAndData itself.

        return
            keccak256(
                abi.encode(
                    _pack(userOp),
                    block.chainid,
                    address(this),
                    validUntil,
                    validAfter
                )
            );
    }

    /**
     * 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(
        UserOperation calldata userOp,
        bytes32 /*userOpHash*/,
        uint256 requiredPreFund
    ) internal override returns (bytes memory context, uint256 validationData) {
        (requiredPreFund);

        (
            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 "EtherspotPaymaster", and not "ECDSA"
        require(
            signature.length == 64 || signature.length == 65,
            "EtherspotPaymaster:: invalid signature length in paymasterAndData"
        );
        bytes32 hash = ECDSA.toEthSignedMessageHash(
            getHash(userOp, validUntil, validAfter)
        );
        address sig = userOp.getSender();

        // check for valid paymaster
        address sponsorSig = ECDSA.recover(hash, signature);

        // don't revert on signature failure: return SIG_VALIDATION_FAILED
        if (!_check(sponsorSig, sig)) {
            return ("", _packValidationData(true, validUntil, validAfter));
        }

        uint256 costOfPost = userOp.maxFeePerGas * COST_OF_POST;
        uint256 totalPreFund = requiredPreFund + costOfPost;

        // check sponsor has enough funds deposited to pay for gas
        require(
            getSponsorBalance(sponsorSig) >= totalPreFund,
            "EtherspotPaymaster:: Sponsor paymaster funds too low"
        );

        // debit requiredPreFund amount
        _debitSponsor(sponsorSig, totalPreFund);

        // no need for other on-chain validation: entire UserOp should have been checked
        // by the external service prior to signing it.
        return (
            abi.encode(sponsorSig, sig, totalPreFund, costOfPost),
            _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:SIGNATURE_OFFSET],
            (uint48, uint48)
        );
        signature = paymasterAndData[SIGNATURE_OFFSET:];
    }

    function _postOp(
        PostOpMode,
        bytes calldata context,
        uint256 actualGasCost
    ) internal override {
        (
            address paymaster,
            address sender,
            uint256 totalPrefund,
            uint256 costOfPost
        ) = abi.decode(context, (address, address, uint256, uint256));
        _creditSponsor(paymaster, totalPrefund - (actualGasCost + costOfPost));
        emit SponsorSuccessful(paymaster, sender);
    }
}
```

## License

This contract is licensed under the MIT license.
