Skip to content

Commit

Permalink
refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
ShivaanshK committed Aug 30, 2024
1 parent 5b48dfb commit a73ccb8
Show file tree
Hide file tree
Showing 10 changed files with 229 additions and 54 deletions.
5 changes: 4 additions & 1 deletion contracts/base/BasePaymaster.sol
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,10 @@ abstract contract BasePaymaster is IPaymaster, SoladyOwnable {
require(msg.sender == address(entryPoint), "Sender not EntryPoint");
}

function isContract(address _addr) internal view returns (bool) {
/**
* Check if address is a contract
*/
function _isContract(address _addr) internal view returns (bool) {
uint256 size;
assembly ("memory-safe") {
size := extcodesize(_addr)
Expand Down
22 changes: 22 additions & 0 deletions contracts/common/BiconomyTokenPaymasterErrors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,27 @@
pragma solidity ^0.8.26;

contract BiconomyTokenPaymasterErrors {
/**
* @notice Throws when the verifiying signer address provided is address(0)
*/
error VerifyingSignerCanNotBeZero();

/**
* @notice Throws when the fee collector address provided is address(0)
*/
error FeeCollectorCanNotBeZero();

/**
* @notice Throws when the fee collector address provided is a deployed contract
*/
error FeeCollectorCanNotBeContract();

/**
* @notice Throws when the fee collector address provided is a deployed contract
*/
error VerifyingSignerCanNotBeContract();
/**
* @notice Throws when trying unaccountedGas is too high
*/
error UnaccountedGasTooHigh();
}
2 changes: 1 addition & 1 deletion contracts/interfaces/IBiconomySponsorshipPaymaster.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ interface IBiconomySponsorshipPaymaster{

function setFeeCollector(address _newFeeCollector) external payable;

function setUnaccountedGas(uint48 value) external payable;
function setUnaccountedGas(uint16 value) external payable;

function withdrawERC20(IERC20 token, address target, uint256 amount) external;

Expand Down
26 changes: 24 additions & 2 deletions contracts/interfaces/IBiconomyTokenPaymaster.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,27 @@
pragma solidity ^0.8.26;

interface IBiconomyTokenPaymaster {

}
enum ExchangeRateSource {
EXTERNAL_EXCHANGE_RATE,
ORACLE_BASED,
TWAP_BASED
}

event UnaccountedGasChanged(uint256 indexed oldValue, uint256 indexed newValue);
event FixedDynamicAdjustmentChanged(uint32 indexed oldValue, uint32 indexed newValue);
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 DynamicAdjustmentCollected(address indexed paymasterId, uint256 indexed dynamicAdjustment);
event Received(address indexed sender, uint256 value);
event TokensWithdrawn(address indexed token, address indexed to, uint256 indexed amount, address actor);


function setSigner(address _newVerifyingSigner) external payable;

function setFeeCollector(address _newFeeCollector) external payable;

function setUnaccountedGas(uint16 value) external payable;
}
42 changes: 26 additions & 16 deletions contracts/sponsorship/BiconomySponsorshipPaymaster.sol
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,13 @@ contract BiconomySponsorshipPaymaster is

address public verifyingSigner;
address public feeCollector;
uint48 public unaccountedGas;
uint16 public unaccountedGas;
uint32 private constant PRICE_DENOMINATOR = 1e6;

// note: could rename to PAYMASTER_ID_OFFSET
// Offset in PaymasterAndData to get to PAYMASTER_ID_OFFSET
uint256 private constant PAYMASTER_ID_OFFSET = PAYMASTER_DATA_OFFSET;

// Limit for unaccounted gas cost
uint16 private constant UNACCOUNTED_GAS_LIMIT = 10_000;
uint16 private constant UNACCOUNTED_GAS_LIMIT = 50_000;

mapping(address => uint256) public paymasterIdBalances;

Expand All @@ -56,18 +55,14 @@ contract BiconomySponsorshipPaymaster is
IEntryPoint _entryPoint,
address _verifyingSigner,
address _feeCollector,
uint48 _unaccountedGas
uint16 _unaccountedGas
)
BasePaymaster(_owner, _entryPoint)
{
if (_verifyingSigner == address(0)) {
revert VerifyingSignerCanNotBeZero();
} else if (_feeCollector == address(0)) {
revert FeeCollectorCanNotBeZero();
} else if (_unaccountedGas > UNACCOUNTED_GAS_LIMIT) {
revert UnaccountedGasTooHigh();
_checkConstructorArgs(_verifyingSigner, _feeCollector, _unaccountedGas);
assembly ("memory-safe") {
sstore(verifyingSigner.slot, _verifyingSigner)
}
verifyingSigner = _verifyingSigner;
feeCollector = _feeCollector;
unaccountedGas = _unaccountedGas;
}
Expand Down Expand Up @@ -97,7 +92,7 @@ contract BiconomySponsorshipPaymaster is
* After setting the new signer address, it will emit an event VerifyingSignerChanged.
*/
function setSigner(address _newVerifyingSigner) external payable override onlyOwner {
if (isContract(_newVerifyingSigner)) revert VerifyingSignerCanNotBeContract();
if (_isContract(_newVerifyingSigner)) revert VerifyingSignerCanNotBeContract();
if (_newVerifyingSigner == address(0)) {
revert VerifyingSignerCanNotBeZero();
}
Expand All @@ -116,6 +111,7 @@ contract BiconomySponsorshipPaymaster is
* After setting the new fee collector address, it will emit an event FeeCollectorChanged.
*/
function setFeeCollector(address _newFeeCollector) external payable override onlyOwner {
if (_isContract(_newFeeCollector)) revert FeeCollectorCanNotBeContract();
if (_newFeeCollector == address(0)) revert FeeCollectorCanNotBeZero();
address oldFeeCollector = feeCollector;
feeCollector = _newFeeCollector;
Expand All @@ -127,19 +123,19 @@ contract BiconomySponsorshipPaymaster is
* @param value The new value to be set as the unaccountedEPGasOverhead.
* @notice only to be called by the owner of the contract.
*/
function setUnaccountedGas(uint48 value) external payable override onlyOwner {
function setUnaccountedGas(uint16 value) external payable override onlyOwner {
if (value > UNACCOUNTED_GAS_LIMIT) {
revert UnaccountedGasTooHigh();
}
uint256 oldValue = unaccountedGas;
uint16 oldValue = unaccountedGas;
unaccountedGas = value;
emit UnaccountedGasChanged(oldValue, value);
}

/**
* @dev Override the default implementation.
*/
function deposit() external payable override virtual {
function deposit() external payable virtual override {
revert UseDepositForInstead();
}

Expand Down Expand Up @@ -346,4 +342,18 @@ contract BiconomySponsorshipPaymaster is
SafeTransferLib.safeTransfer(address(token), target, amount);
emit TokensWithdrawn(address(token), target, amount, msg.sender);
}

function _checkConstructorArgs(address _verifyingSigner, address _feeCollector, uint16 _unaccountedGas) internal view {
if (_verifyingSigner == address(0)) {
revert VerifyingSignerCanNotBeZero();
} else if (_isContract(_verifyingSigner)) {
revert VerifyingSignerCanNotBeContract();
} else if (_feeCollector == address(0)) {
revert FeeCollectorCanNotBeZero();
} else if (_isContract(_feeCollector)) {
revert FeeCollectorCanNotBeContract();
} else if (_unaccountedGas > UNACCOUNTED_GAS_LIMIT) {
revert UnaccountedGasTooHigh();
}
}
}
109 changes: 107 additions & 2 deletions contracts/token/BiconomyTokenPaymaster.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@ import { BasePaymaster } from "../base/BasePaymaster.sol";
import { BiconomyTokenPaymasterErrors } from "../common/BiconomyTokenPaymasterErrors.sol";
import { IBiconomyTokenPaymaster } from "../interfaces/IBiconomyTokenPaymaster.sol";

/**
* @title BiconomyTokenPaymaster
* @author ShivaanshK<[email protected]>
* @author livingrockrises<[email protected]>
* @notice Token Paymaster for v0.7 Entry Point
* @dev A paymaster that allows user to pay gas fee in ERC20 tokens. The paymaster owner chooses which tokens to
* accept. The payment manager (usually the owner) first deposits native gas into the EntryPoint. Then, for each
* transaction, it takes the gas fee from the user's ERC20 token balance. The exchange rate between ETH and the token is
* calculated using 1 of three methods: external price source, off-chain oracle, or a TWAP oracle.
*/
contract BiconomyTokenPaymaster is
BasePaymaster,
ReentrancyGuard,
Expand All @@ -19,12 +29,86 @@ contract BiconomyTokenPaymaster is
using UserOperationLib for PackedUserOperation;
using SignatureCheckerLib for address;

address public verifyingSigner;
address public feeCollector;
uint16 public unaccountedGas;

// Limit for unaccounted gas cost
uint16 private constant UNACCOUNTED_GAS_LIMIT = 50_000;

constructor(
address _owner,
IEntryPoint _entryPoint
IEntryPoint _entryPoint,
address _verifyingSigner,
address _feeCollector,
uint16 _unaccountedGas
)
BasePaymaster(_owner, _entryPoint)
{ }
{
_checkConstructorArgs(_verifyingSigner, _feeCollector, _unaccountedGas);
assembly ("memory-safe") {
sstore(verifyingSigner.slot, _verifyingSigner)
}
verifyingSigner = _verifyingSigner;
feeCollector = _feeCollector;
unaccountedGas = _unaccountedGas;
}

/**
* @dev Set a new verifying signer address.
* Can only be called by the owner of the contract.
* @param _newVerifyingSigner The new address to be set as the verifying signer.
* @notice If _newVerifyingSigner is set to zero address, it will revert with an error.
* After setting the new signer address, it will emit an event VerifyingSignerChanged.
*/
function setSigner(address _newVerifyingSigner) external payable override onlyOwner {
if (_isContract(_newVerifyingSigner)) revert VerifyingSignerCanNotBeContract();
if (_newVerifyingSigner == address(0)) {
revert VerifyingSignerCanNotBeZero();
}
address oldSigner = verifyingSigner;
assembly ("memory-safe") {
sstore(verifyingSigner.slot, _newVerifyingSigner)
}
emit VerifyingSignerChanged(oldSigner, _newVerifyingSigner, msg.sender);
}

/**
* @dev Set a new fee collector address.
* Can only be called by the owner of the contract.
* @param _newFeeCollector The new address to be set as the fee collector.
* @notice If _newFeeCollector is set to zero address, it will revert with an error.
* After setting the new fee collector address, it will emit an event FeeCollectorChanged.
*/
function setFeeCollector(address _newFeeCollector) external payable override onlyOwner {
if (_isContract(_newFeeCollector)) revert FeeCollectorCanNotBeContract();
if (_newFeeCollector == address(0)) revert FeeCollectorCanNotBeZero();
address oldFeeCollector = feeCollector;
feeCollector = _newFeeCollector;
emit FeeCollectorChanged(oldFeeCollector, _newFeeCollector, msg.sender);
}

/**
* @dev Set a new unaccountedEPGasOverhead value.
* @param value The new value to be set as the unaccountedEPGasOverhead.
* @notice only to be called by the owner of the contract.
*/
function setUnaccountedGas(uint16 value) external payable override onlyOwner {
if (value > UNACCOUNTED_GAS_LIMIT) {
revert UnaccountedGasTooHigh();
}
uint16 oldValue = unaccountedGas;
unaccountedGas = value;
emit UnaccountedGasChanged(oldValue, value);
}

/**
* Add a deposit in native currency for this paymaster, used for paying for transaction fees.
* This is ideally done by the entity who is managing the received ERC20 gas tokens.
*/
function deposit() public payable virtual override nonReentrant {
entryPoint.depositTo{ value: msg.value }(address(this));
}

/**
* @dev Validate a user operation.
Expand Down Expand Up @@ -64,4 +148,25 @@ contract BiconomyTokenPaymaster is
{
// Implementation of post-operation logic
}

function _checkConstructorArgs(
address _verifyingSigner,
address _feeCollector,
uint16 _unaccountedGas
)
internal
view
{
if (_verifyingSigner == address(0)) {
revert VerifyingSignerCanNotBeZero();
} else if (_isContract(_verifyingSigner)) {
revert VerifyingSignerCanNotBeContract();
} else if (_feeCollector == address(0)) {
revert FeeCollectorCanNotBeZero();
} else if (_isContract(_feeCollector)) {
revert FeeCollectorCanNotBeContract();
} else if (_unaccountedGas > UNACCOUNTED_GAS_LIMIT) {
revert UnaccountedGasTooHigh();
}
}
}
4 changes: 2 additions & 2 deletions test/base/BaseEventsAndErrors.sol
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: Unlicensed
pragma solidity ^0.8.26;

import { EventsAndErrors } from "nexus/test/foundry/utils/EventsAndErrors.sol";
import { BiconomySponsorshipPaymasterErrors } from "./../../../contracts/common/BiconomySponsorshipPaymasterErrors.sol";
import { EventsAndErrors } from "@nexus/test/foundry/utils/EventsAndErrors.sol";
import { BiconomySponsorshipPaymasterErrors } from "../../contracts/common/BiconomySponsorshipPaymasterErrors.sol";

contract BaseEventsAndErrors is EventsAndErrors, BiconomySponsorshipPaymasterErrors {
// ==========================
Expand Down
36 changes: 18 additions & 18 deletions test/base/TestBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,25 @@ pragma solidity ^0.8.26;
import { Test } from "forge-std/src/Test.sol";
import { Vm } from "forge-std/src/Vm.sol";

import "solady/src/utils/ECDSA.sol";

import { EntryPoint } from "account-abstraction/contracts/core/EntryPoint.sol";
import { IEntryPoint } from "account-abstraction/contracts/interfaces/IEntryPoint.sol";
import { IAccount } from "account-abstraction/contracts/interfaces/IAccount.sol";
import { Exec } from "account-abstraction/contracts/utils/Exec.sol";
import { IPaymaster } from "account-abstraction/contracts/interfaces/IPaymaster.sol";
import { PackedUserOperation } from "account-abstraction/contracts/interfaces/PackedUserOperation.sol";

import { Nexus } from "nexus/contracts/Nexus.sol";
import { NexusAccountFactory } from "nexus/contracts/factory/NexusAccountFactory.sol";
import { BiconomyMetaFactory } from "nexus/contracts/factory/BiconomyMetaFactory.sol";
import { MockValidator } from "nexus/contracts/mocks/MockValidator.sol";
import { BootstrapLib } from "nexus/contracts/lib/BootstrapLib.sol";
import { Bootstrap, BootstrapConfig } from "nexus/contracts/utils/Bootstrap.sol";
import { CheatCodes } from "nexus/test/foundry/utils/CheatCodes.sol";
import "@solady/src/utils/ECDSA.sol";

import { EntryPoint } from "@account-abstraction/contracts/core/EntryPoint.sol";
import { IEntryPoint } from "@account-abstraction/contracts/interfaces/IEntryPoint.sol";
import { IAccount } from "@account-abstraction/contracts/interfaces/IAccount.sol";
import { Exec } from "@account-abstraction/contracts/utils/Exec.sol";
import { IPaymaster } from "@account-abstraction/contracts/interfaces/IPaymaster.sol";
import { PackedUserOperation } from "@account-abstraction/contracts/interfaces/PackedUserOperation.sol";

import { Nexus } from "@nexus/contracts/Nexus.sol";
import { NexusAccountFactory } from "@nexus/contracts/factory/NexusAccountFactory.sol";
import { BiconomyMetaFactory } from "@nexus/contracts/factory/BiconomyMetaFactory.sol";
import { MockValidator } from "@nexus/contracts/mocks/MockValidator.sol";
import { BootstrapLib } from "@nexus/contracts/lib/BootstrapLib.sol";
import { Bootstrap, BootstrapConfig } from "@nexus/contracts/utils/Bootstrap.sol";
import { CheatCodes } from "@nexus/test/foundry/utils/CheatCodes.sol";
import { BaseEventsAndErrors } from "./BaseEventsAndErrors.sol";

import { BiconomySponsorshipPaymaster } from "../../../contracts/sponsorship/BiconomySponsorshipPaymaster.sol";
import { BiconomySponsorshipPaymaster } from "../../contracts/sponsorship/BiconomySponsorshipPaymaster.sol";

abstract contract TestBase is CheatCodes, BaseEventsAndErrors {
// -----------------------------------------
Expand Down Expand Up @@ -409,7 +409,7 @@ abstract contract TestBase is CheatCodes, BaseEventsAndErrors {

vm.startPrank(paymaster.owner());
// Set unaccounted gas to be gas used in postop + 1000 for EP overhead and penalty
paymaster.setUnaccountedGas(uint48(postopGasUsed + 1000));
paymaster.setUnaccountedGas(uint16(postopGasUsed + 1000));
vm.stopPrank();

// Ammend the userop to have new gas limits and signature
Expand Down
Loading

0 comments on commit a73ccb8

Please sign in to comment.