Skip to content

Commit

Permalink
feat:add setters and update lint
Browse files Browse the repository at this point in the history
  • Loading branch information
livingrockrises committed Oct 22, 2024
1 parent 146929a commit 1087b8e
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 108 deletions.
3 changes: 1 addition & 2 deletions contracts/interfaces/IBiconomySponsorshipPaymaster.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@ import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { PackedUserOperation } from "account-abstraction/core/UserOperationLib.sol";

interface IBiconomySponsorshipPaymaster {

struct WithdrawalRequest {
uint256 amount;
address to;
uint256 requestSubmittedTimestamp;
}
}

event UnaccountedGasChanged(uint256 indexed oldValue, uint256 indexed newValue);
event FixedPriceMarkupChanged(uint256 indexed oldValue, uint256 indexed newValue);
Expand Down
142 changes: 85 additions & 57 deletions contracts/sponsorship/BiconomySponsorshipPaymaster.sol
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,17 @@ contract BiconomySponsorshipPaymaster is
uint256 private constant _UNACCOUNTED_GAS_LIMIT = 100_000;

mapping(address => uint256) public paymasterIdBalances;
mapping(address => bool) internal trustedPaymasterIds;
mapping (address paymasterId => WithdrawalRequest request) requests;
mapping(address => bool) internal _trustedPaymasterIds;
mapping(address paymasterId => WithdrawalRequest request) internal _requests;

constructor(
address owner,
IEntryPoint entryPointArg,
address verifyingSignerArg,
address feeCollectorArg,
uint256 unaccountedGasArg,
uint256 _paymasterIdWithdrawalDelay,
uint256 _minDeposit
uint256 paymasterIdWithdrawalDelayArg,
uint256 minDepositArg
)
BasePaymaster(owner, entryPointArg)
{
Expand All @@ -74,8 +74,8 @@ contract BiconomySponsorshipPaymaster is
}
feeCollector = feeCollectorArg;
unaccountedGas = unaccountedGasArg;
paymasterIdWithdrawalDelay = _paymasterIdWithdrawalDelay;
minDeposit = _minDeposit;
paymasterIdWithdrawalDelay = paymasterIdWithdrawalDelayArg;
minDeposit = minDepositArg;
}

receive() external payable {
Expand All @@ -90,8 +90,7 @@ contract BiconomySponsorshipPaymaster is
function depositFor(address paymasterId) external payable nonReentrant {
if (paymasterId == address(0)) revert PaymasterIdCanNotBeZero();
if (msg.value == 0) revert DepositCanNotBeZero();
if(paymasterIdBalances[paymasterId] + msg.value < minDeposit)
revert LowDeposit();
if (paymasterIdBalances[paymasterId] + msg.value < minDeposit) revert LowDeposit();
paymasterIdBalances[paymasterId] += msg.value;
entryPoint.depositTo{ value: msg.value }(address(this));
emit GasDeposited(paymasterId, msg.value);
Expand All @@ -116,6 +115,26 @@ contract BiconomySponsorshipPaymaster is
emit VerifyingSignerChanged(oldSigner, newVerifyingSigner, msg.sender);
}

/**
* @dev Set a new trusted paymasterId.
* Can only be called by the owner of the contract.
* @param paymasterId The paymasterId to be set as trusted.
* @param isTrusted Whether the paymasterId is trusted or not.
*/
function setTrustedPaymasterId(address paymasterId, bool isTrusted) external payable onlyOwner {
if (paymasterId == address(0)) revert PaymasterIdCanNotBeZero();
_trustedPaymasterIds[paymasterId] = isTrusted;
}

/**
* @dev Set a new minimum deposit value.
* Can only be called by the owner of the contract.
* @param newMinDeposit The new minimum deposit value to be set.
*/
function setMinDeposit(uint256 newMinDeposit) external payable onlyOwner {
minDeposit = newMinDeposit;
}

/**
* @dev Set a new fee collector address.
* Can only be called by the owner of the contract.
Expand Down Expand Up @@ -171,9 +190,9 @@ contract BiconomySponsorshipPaymaster is
if (withdrawAddress == address(0)) revert CanNotWithdrawToZeroAddress();
if (amount == 0) revert CanNotWithdrawZeroAmount();
uint256 currentBalance = paymasterIdBalances[msg.sender];
if (amount > currentBalance)
revert InsufficientFundsInGasTank();
requests[msg.sender] = WithdrawalRequest({amount: amount, to: withdrawAddress, requestSubmittedTimestamp: block.timestamp });
if (amount > currentBalance) revert InsufficientFundsInGasTank();
_requests[msg.sender] =
WithdrawalRequest({ amount: amount, to: withdrawAddress, requestSubmittedTimestamp: block.timestamp });
emit WithdrawalRequestSubmitted(withdrawAddress, amount);
}

Expand All @@ -183,15 +202,14 @@ contract BiconomySponsorshipPaymaster is
* @param paymasterId paymasterId (Dapp Depositor address)
*/
function executeWithdrawalRequest(address paymasterId) external nonReentrant {
WithdrawalRequest memory req = requests[paymasterId];
if(req.requestSubmittedTimestamp == 0) revert NoRequestSubmitted();
uint256 clearanceTimestamp = req.requestSubmittedTimestamp + getDelay(paymasterId);
if (block.timestamp < clearanceTimestamp)
revert RequestNotClearedYet(clearanceTimestamp);
WithdrawalRequest memory req = _requests[paymasterId];
if (req.requestSubmittedTimestamp == 0) revert NoRequestSubmitted();
uint256 clearanceTimestamp = req.requestSubmittedTimestamp + _getDelay(paymasterId);
if (block.timestamp < clearanceTimestamp) revert RequestNotClearedYet(clearanceTimestamp);
uint256 currentBalance = paymasterIdBalances[paymasterId];
req.amount = req.amount > currentBalance ? currentBalance : req.amount;
paymasterIdBalances[paymasterId] = currentBalance - req.amount;
delete requests[paymasterId];
delete _requests[paymasterId];
entryPoint.withdrawTo(payable(req.to), req.amount);
emit GasWithdrawn(paymasterId, req.to, req.amount);
}
Expand All @@ -200,7 +218,7 @@ contract BiconomySponsorshipPaymaster is
* @dev Cancel a withdrawal request for the paymasterId (Dapp Depositor address)
*/
function cancelWithdrawalRequest() external {
delete requests[msg.sender];
delete _requests[msg.sender];
emit WithdrawalRequestCancelledFor(msg.sender);
}

Expand All @@ -212,6 +230,7 @@ contract BiconomySponsorshipPaymaster is
}

function withdrawTo(address payable withdrawAddress, uint256 amount) external virtual override {
(withdrawAddress, amount);
revert SubmitRequestInstead();
}

Expand Down Expand Up @@ -283,8 +302,10 @@ contract BiconomySponsorshipPaymaster is
validUntil = uint48(bytes6(paymasterAndData[_PAYMASTER_ID_OFFSET + 20:_PAYMASTER_ID_OFFSET + 26]));
validAfter = uint48(bytes6(paymasterAndData[_PAYMASTER_ID_OFFSET + 26:_PAYMASTER_ID_OFFSET + 32]));
priceMarkup = uint32(bytes4(paymasterAndData[_PAYMASTER_ID_OFFSET + 32:_PAYMASTER_ID_OFFSET + 36]));
paymasterValidationGasLimit = uint128(bytes16(paymasterAndData[_PAYMASTER_VALIDATION_GAS_OFFSET:_PAYMASTER_POSTOP_GAS_OFFSET]));
paymasterPostOpGasLimit = uint128(bytes16(paymasterAndData[_PAYMASTER_POSTOP_GAS_OFFSET : _PAYMASTER_DATA_OFFSET]));
paymasterValidationGasLimit =
uint128(bytes16(paymasterAndData[_PAYMASTER_VALIDATION_GAS_OFFSET:_PAYMASTER_POSTOP_GAS_OFFSET]));
paymasterPostOpGasLimit =
uint128(bytes16(paymasterAndData[_PAYMASTER_POSTOP_GAS_OFFSET:_PAYMASTER_DATA_OFFSET]));
signature = paymasterAndData[_PAYMASTER_ID_OFFSET + 36:];
}
}
Expand All @@ -302,31 +323,30 @@ contract BiconomySponsorshipPaymaster is
internal
override
{
unchecked {
(address paymasterId, uint32 priceMarkup, uint256 prechargedAmount) =
abi.decode(context, (address, uint32, uint256));

// Include unaccountedGas since EP doesn't include this in actualGasCost
// unaccountedGas = postOpGas + EP overhead gas + estimated penalty
actualGasCost = actualGasCost + (unaccountedGas * actualUserOpFeePerGas);
// Apply the price markup
uint256 adjustedGasCost = (actualGasCost * priceMarkup) / _PRICE_DENOMINATOR;

uint256 premium = adjustedGasCost - actualGasCost;

// Add priceMarkup to fee collector balance
paymasterIdBalances[feeCollector] += premium;

if (prechargedAmount > adjustedGasCost) {
// If overcharged refund the excess
paymasterIdBalances[paymasterId] += (prechargedAmount - adjustedGasCost);
} else {
// deduct what needs to be deducted from paymasterId
paymasterIdBalances[paymasterId] -= (adjustedGasCost - prechargedAmount);
}
// here adjustedGasCost does not account for gasPenalty. prechargedAmount accounts for penalty with maxGasPenalty
emit GasBalanceDeducted(paymasterId, adjustedGasCost, premium);
(address paymasterId, uint32 priceMarkup, uint256 prechargedAmount) =
abi.decode(context, (address, uint32, uint256));

// Include unaccountedGas since EP doesn't include this in actualGasCost
// unaccountedGas = postOpGas + EP overhead gas + estimated penalty
actualGasCost = actualGasCost + (unaccountedGas * actualUserOpFeePerGas);
// Apply the price markup
uint256 adjustedGasCost = (actualGasCost * priceMarkup) / _PRICE_DENOMINATOR;

uint256 premium = adjustedGasCost - actualGasCost;

// Add priceMarkup to fee collector balance
paymasterIdBalances[feeCollector] += premium;

if (prechargedAmount > adjustedGasCost) {
// If overcharged refund the excess
paymasterIdBalances[paymasterId] += (prechargedAmount - adjustedGasCost);
} else {
// deduct what needs to be deducted from paymasterId
paymasterIdBalances[paymasterId] -= (adjustedGasCost - prechargedAmount);
}
// here adjustedGasCost does not account for gasPenalty. prechargedAmount accounts for penalty with
// maxGasPenalty
emit GasBalanceDeducted(paymasterId, adjustedGasCost, premium);
}

/**
Expand All @@ -349,10 +369,17 @@ contract BiconomySponsorshipPaymaster is
returns (bytes memory context, uint256 validationData)
{
(userOpHash);
(address paymasterId, uint48 validUntil, uint48 validAfter, uint32 priceMarkup, uint128 paymasterValidationGasLimit, uint128 paymasterPostOpGasLimit, bytes calldata signature) =
parsePaymasterAndData(userOp.paymasterAndData);
(
address paymasterId,
uint48 validUntil,
uint48 validAfter,
uint32 priceMarkup,
uint128 paymasterValidationGasLimit,
uint128 paymasterPostOpGasLimit,
bytes calldata signature
) = parsePaymasterAndData(userOp.paymasterAndData);
(paymasterValidationGasLimit, paymasterPostOpGasLimit);

//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"
Expand Down Expand Up @@ -382,13 +409,15 @@ contract BiconomySponsorshipPaymaster is

// callGasLimit + paymasterPostOpGas
uint256 maxPenalty = (
uint128(uint256(userOp.accountGasLimits)) +
uint128(bytes16(userOp.paymasterAndData[_PAYMASTER_POSTOP_GAS_OFFSET : _PAYMASTER_DATA_OFFSET]))
) * 10 * userOp.unpackMaxFeePerGas() / 100;
(
uint128(uint256(userOp.accountGasLimits))
+ uint128(bytes16(userOp.paymasterAndData[_PAYMASTER_POSTOP_GAS_OFFSET:_PAYMASTER_DATA_OFFSET]))
) * 10 * userOp.unpackMaxFeePerGas()
) / 100;

// Deduct the max gas cost.
uint256 effectiveCost =
((requiredPreFund + unaccountedGas * userOp.unpackMaxFeePerGas()) * priceMarkup / _PRICE_DENOMINATOR);
(((requiredPreFund + unaccountedGas * userOp.unpackMaxFeePerGas()) * priceMarkup) / _PRICE_DENOMINATOR);

if (effectiveCost + maxPenalty > paymasterIdBalances[paymasterId]) {
revert InsufficientFundsForPaymasterId();
Expand Down Expand Up @@ -424,15 +453,14 @@ contract BiconomySponsorshipPaymaster is
}
}

function _getDelay(address paymasterId) internal view returns (uint256) {
if (_trustedPaymasterIds[paymasterId]) return 0;
return paymasterIdWithdrawalDelay;
}

function _withdrawERC20(IERC20 token, address target, uint256 amount) private {
if (target == address(0)) revert CanNotWithdrawToZeroAddress();
SafeTransferLib.safeTransfer(address(token), target, amount);
emit TokensWithdrawn(address(token), target, amount, msg.sender);
}

function getDelay(address paymasterId) internal view returns (uint256) {
if (trustedPaymasterIds[paymasterId])
return 0;
return paymasterIdWithdrawalDelay;
}
}
23 changes: 12 additions & 11 deletions test/base/TestBase.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;

import { Test } from "forge-std/Test.sol";
import { Vm } from "forge-std/Vm.sol";
import { console2 } from "forge-std/console2.sol";
Expand All @@ -26,7 +27,6 @@ import {
} from "../../../contracts/token/BiconomyTokenPaymaster.sol";

abstract contract TestBase is CheatCodes, TestHelper, BaseEventsAndErrors {

using UserOperationLib for PackedUserOperation;

address constant ENTRYPOINT_ADDRESS = address(0x0000000071727De22E5E9d8BAf0edAc6f37da032);
Expand Down Expand Up @@ -212,14 +212,14 @@ abstract contract TestBase is CheatCodes, TestHelper, BaseEventsAndErrors {
pmData.priceMarkup,
new bytes(65) // Zero signature
);

{
// Generate hash to be signed
bytes32 paymasterHash =
paymaster.getHash(userOp, pmData.paymasterId, pmData.validUntil, pmData.validAfter, pmData.priceMarkup);
// Generate hash to be signed
bytes32 paymasterHash =
paymaster.getHash(userOp, pmData.paymasterId, pmData.validUntil, pmData.validAfter, pmData.priceMarkup);

// Sign the hash
signature = signMessage(signer, paymasterHash);
// Sign the hash
signature = signMessage(signer, paymasterHash);
}

// Final paymaster data with the actual signature
Expand Down Expand Up @@ -333,8 +333,8 @@ abstract contract TestBase is CheatCodes, TestHelper, BaseEventsAndErrors {

function getMaxPenalty(PackedUserOperation calldata userOp) public view returns (uint256) {
return (
uint128(uint256(userOp.accountGasLimits)) +
uint128(bytes16(userOp.paymasterAndData[_PAYMASTER_POSTOP_GAS_OFFSET : _PAYMASTER_DATA_OFFSET]))
uint128(uint256(userOp.accountGasLimits))
+ uint128(bytes16(userOp.paymasterAndData[_PAYMASTER_POSTOP_GAS_OFFSET:_PAYMASTER_DATA_OFFSET]))
) * 10 * userOp.unpackMaxFeePerGas() / 100;
}

Expand All @@ -351,8 +351,9 @@ abstract contract TestBase is CheatCodes, TestHelper, BaseEventsAndErrors {
internal
view
{
(uint256 expectedPriceMarkup, uint256 actualPriceMarkup) =
getPriceMarkups(bicoPaymaster, initialDappPaymasterBalance, initialFeeCollectorBalance, priceMarkup, maxPenalty);
(uint256 expectedPriceMarkup, uint256 actualPriceMarkup) = getPriceMarkups(
bicoPaymaster, initialDappPaymasterBalance, initialFeeCollectorBalance, priceMarkup, maxPenalty
);
uint256 totalGasFeePaid = BUNDLER.addr.balance - initialBundlerBalance;
uint256 gasPaidByDapp = initialDappPaymasterBalance - bicoPaymaster.getBalance(DAPP_ACCOUNT.addr);

Expand Down
33 changes: 15 additions & 18 deletions test/unit/concrete/TestSponsorshipPaymaster.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,21 @@ import { MockToken } from "@nexus/contracts/mocks/MockToken.sol";
contract TestSponsorshipPaymasterWithPriceMarkup is TestBase {
BiconomySponsorshipPaymaster public bicoPaymaster;

uint256 public constant WITHDRAWAL_DELAY = 3600;
uint256 public constant WITHDRAWAL_DELAY = 3600;
uint256 public constant MIN_DEPOSIT = 1e15;

function setUp() public {
setupPaymasterTestEnvironment();
// Deploy Sponsorship Paymaster
bicoPaymaster = new BiconomySponsorshipPaymaster(
{
owner: PAYMASTER_OWNER.addr,
entryPointArg: ENTRYPOINT,
verifyingSignerArg: PAYMASTER_SIGNER.addr,
feeCollectorArg: PAYMASTER_FEE_COLLECTOR.addr,
unaccountedGasArg: 7e3,
_paymasterIdWithdrawalDelay: WITHDRAWAL_DELAY,
_minDeposit: MIN_DEPOSIT
}
);
bicoPaymaster = new BiconomySponsorshipPaymaster({
owner: PAYMASTER_OWNER.addr,
entryPointArg: ENTRYPOINT,
verifyingSignerArg: PAYMASTER_SIGNER.addr,
feeCollectorArg: PAYMASTER_FEE_COLLECTOR.addr,
unaccountedGasArg: 7e3,
paymasterIdWithdrawalDelayArg: WITHDRAWAL_DELAY,
minDepositArg: MIN_DEPOSIT
});
}

function test_Deploy() external {
Expand Down Expand Up @@ -55,7 +53,9 @@ contract TestSponsorshipPaymasterWithPriceMarkup is TestBase {

function test_RevertIf_DeployWithFeeCollectorSetToZero() external {
vm.expectRevert(abi.encodeWithSelector(FeeCollectorCanNotBeZero.selector));
new BiconomySponsorshipPaymaster(PAYMASTER_OWNER.addr, ENTRYPOINT, PAYMASTER_SIGNER.addr, address(0), 7e3, 3600, 1e15);
new BiconomySponsorshipPaymaster(
PAYMASTER_OWNER.addr, ENTRYPOINT, PAYMASTER_SIGNER.addr, address(0), 7e3, 3600, 1e15
);
}

function test_RevertIf_DeployWithFeeCollectorAsContract() external {
Expand Down Expand Up @@ -186,7 +186,6 @@ contract TestSponsorshipPaymasterWithPriceMarkup is TestBase {
bicoPaymaster.deposit{ value: 1 ether }();
}


function test_RevertIf_TriesWithdrawToWithoutRequest() external prankModifier(DAPP_ACCOUNT.addr) {
vm.expectRevert(abi.encodeWithSelector(SubmitRequestInstead.selector));
bicoPaymaster.withdrawTo(payable(BOB_ADDRESS), 1 ether);
Expand Down Expand Up @@ -242,7 +241,7 @@ contract TestSponsorshipPaymasterWithPriceMarkup is TestBase {
vm.warp(block.timestamp + WITHDRAWAL_DELAY + 1);
uint256 dappPaymasterBalanceBefore = bicoPaymaster.getBalance(DAPP_ACCOUNT.addr);
uint256 bobBalanceBefore = BOB_ADDRESS.balance;
bicoPaymaster.executeWithdrawalRequest(DAPP_ACCOUNT.addr);
bicoPaymaster.executeWithdrawalRequest(DAPP_ACCOUNT.addr);
uint256 dappPaymasterBalanceAfter = bicoPaymaster.getBalance(DAPP_ACCOUNT.addr);
uint256 bobBalanceAfter = BOB_ADDRESS.balance;
assertEq(dappPaymasterBalanceAfter, dappPaymasterBalanceBefore - depositAmount);
Expand All @@ -252,9 +251,7 @@ contract TestSponsorshipPaymasterWithPriceMarkup is TestBase {
bicoPaymaster.executeWithdrawalRequest(DAPP_ACCOUNT.addr);
}

// try to use balance while request is cleared


// try to use balance while request is cleared

// test minimal deposit
function test_depositFor_RevertsIf_DepositIsLessThanMinDeposit() external {
Expand Down
Loading

0 comments on commit 1087b8e

Please sign in to comment.