Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/paymaster foundry tests #1

Merged
merged 45 commits into from
Jul 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
55d967d
Setup nexus submodule and fix remappings for foundry test
ShivaanshK Jun 26, 2024
659b7e3
setup imports, accounts, and align versions
ShivaanshK Jun 27, 2024
967379c
Deployment test for BiconomySponsorshipPaymaster
ShivaanshK Jun 27, 2024
2e32ee2
basic state tests
ShivaanshK Jun 28, 2024
46d8c89
quick fix
ShivaanshK Jun 28, 2024
87c8c69
deposit tests
ShivaanshK Jun 28, 2024
6a85282
rename tests according to naming conventions
ShivaanshK Jun 28, 2024
0eb2f81
Check events being emitted
ShivaanshK Jun 28, 2024
502ee33
tests for withdrawing from paymaster
ShivaanshK Jun 28, 2024
d7efb30
tests for validating paymaster and postop
ShivaanshK Jul 1, 2024
03ff890
tests for receiving and withdrawing ether to/from paymaster
ShivaanshK Jul 1, 2024
404b493
commit unit tests
ShivaanshK Jul 1, 2024
4ba40d4
fuzz tests
ShivaanshK Jul 1, 2024
5868a5c
all fuzz tests
ShivaanshK Jul 1, 2024
2fd121c
get rid of nonfuzz test in fuzz file
ShivaanshK Jul 1, 2024
45c20eb
forge install: nexus
livingrockrises Jul 1, 2024
d01ace0
build dep fixes
livingrockrises Jul 2, 2024
d97036f
Merge pull request #2 from bcnmy/fix/project-build
ShivaanshK Jul 3, 2024
5da6fbe
add adam's requested changes
ShivaanshK Jul 3, 2024
b4276e4
fixed linting and changed visibility where applicable
ShivaanshK Jul 3, 2024
9e72f5c
yarn lint runs successfully
ShivaanshK Jul 3, 2024
c62faf2
Merge pull request #5 from bcnmy/feat/fix-linting
livingrockrises Jul 3, 2024
199d312
setPostOpCost tests
ShivaanshK Jul 4, 2024
58ebf22
merge
ShivaanshK Jul 4, 2024
de84d99
tests for withdrawErc20
ShivaanshK Jul 4, 2024
38f9891
fix forge build
livingrockrises Jul 4, 2024
3ac0568
add adam's requested changes
ShivaanshK Jul 3, 2024
de22654
setPostOpCost tests
ShivaanshK Jul 4, 2024
3972d14
fixed linting and changed visibility where applicable
ShivaanshK Jul 3, 2024
f4f341c
yarn lint runs successfully
ShivaanshK Jul 3, 2024
8326d90
tests for withdrawErc20
ShivaanshK Jul 4, 2024
1ee053e
fix forge build
livingrockrises Jul 4, 2024
e1ba22e
switched to custom errors and fixed postop gas calc
ShivaanshK Jul 4, 2024
10b3892
fixed a few things with gas calc and wrote tests
ShivaanshK Jul 6, 2024
2817e44
more tests for accounting for premiums
ShivaanshK Jul 7, 2024
1d96053
some cleanup
ShivaanshK Jul 7, 2024
40aed7e
make tests more modular
ShivaanshK Jul 8, 2024
81abba5
postop cost added back and more tests added
ShivaanshK Jul 9, 2024
4ba9a4b
Merge branch 'feat/paymaster-foundry-tests' of https://github.com/bcn…
livingrockrises Jul 9, 2024
abff5d2
fix postOpCost
livingrockrises Jul 9, 2024
f5acd1a
parse paymaster data test
ShivaanshK Jul 10, 2024
ebf05c1
comprehensive accounting tests
ShivaanshK Jul 10, 2024
36d854b
fix-lint
ShivaanshK Jul 10, 2024
d59cfd5
make UNACCOUNTED_GAS_LIMIT a constant
ShivaanshK Jul 10, 2024
358aeb1
added check for bundler balance change equal to paymaster EP balance …
ShivaanshK Jul 11, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[submodule "lib/nexus"]
path = lib/nexus
url = https://github.com/bcnmy/nexus
[submodule "lib/forge-std"]
path = lib/forge-std
url = https://github.com/foundry-rs/forge-std
2 changes: 1 addition & 1 deletion .solhint.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"extends": "solhint:recommended",
"rules": {
"compiler-version": ["error", "^0.8.24"],
"compiler-version": ["error", "^0.8.26"],
"func-visibility": ["warn", { "ignoreConstructors": true }],
"reentrancy": "error",
"state-visibility": "error",
Expand Down
166 changes: 94 additions & 72 deletions contracts/base/BasePaymaster.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.24;
pragma solidity ^0.8.26;

/* solhint-disable reason-string */

Expand All @@ -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,27 @@ 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");
}
}

function isContract(address _addr) internal view returns (bool) {
uint256 size;
assembly ("memory-safe") {
size := extcodesize(_addr)
}
return size > 0;
}
}
49 changes: 41 additions & 8 deletions contracts/common/Errors.sol
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity ^0.8.24;
pragma solidity ^0.8.26;

contract BiconomySponsorshipPaymasterErrors {

/**
* @notice Throws when the paymaster address provided is address(0)
*/
error PaymasterIdCannotBeZero();
error PaymasterIdCanNotBeZero();

/**
* @notice Throws when the 0 has been provided as deposit
Expand All @@ -16,26 +15,60 @@ contract BiconomySponsorshipPaymasterErrors {
/**
* @notice Throws when the verifiying signer address provided is address(0)
*/
error VerifyingSignerCannotBeZero();
error VerifyingSignerCanNotBeZero();

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

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

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

/**
* @notice Throws when ETH withdrawal fails
*/
error WithdrawalFailed();

/**
* @notice Throws when insufficient funds to withdraw
*/
error InsufficientFundsInGasTank();

/**
* @notice Throws when invalid signature length in paymasterAndData
*/
error InvalidSignatureLength();

/**
* @notice Throws when invalid signature length in paymasterAndData
*/
error InvalidDynamicAdjustment();

/**
* @notice Throws when insufficient funds for paymasterid
*/
error InsufficientFundsForPaymasterId();

/**
* @notice Throws when calling deposit()
*/
error UseDepositForInstead();

/**
* @notice Throws when trying to withdraw to address(0)
*/
error CanNotWithdrawToZeroAddress();

}
/**
* @notice Throws when trying unaccountedGas is too high
*/
error UnaccountedGasTooHigh();
}
24 changes: 11 additions & 13 deletions contracts/interfaces/IBiconomySponsorshipPaymaster.sol
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
pragma solidity ^0.8.26;

interface IBiconomySponsorshipPaymaster {
event PostopCostChanged(uint256 indexed _oldValue, uint256 indexed _newValue);
event FixedPriceMarkupChanged(uint32 indexed _oldValue, uint32 indexed _newValue);
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 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 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);
}
event TokensWithdrawn(address indexed token, address indexed to, uint256 indexed amount, address actor);
}
3 changes: 1 addition & 2 deletions contracts/mocks/Imports.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.24;
pragma solidity ^0.8.26;

/* solhint-disable reason-string */

Expand All @@ -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";

4 changes: 2 additions & 2 deletions contracts/mocks/MockValidator.sol
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
pragma solidity ^0.8.24;
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";
Loading
Loading