diff --git a/.gitmodules b/.gitmodules index a2db065..72d8123 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,7 @@ [submodule "lib/nexus"] path = lib/nexus - url = https://github.com/bcnmy/nexus + url = https://github.com/lib/nexus + branch = dev [submodule "lib/forge-std"] path = lib/forge-std url = https://github.com/foundry-rs/forge-std @@ -10,3 +11,11 @@ [submodule "lib/v3-core"] path = lib/v3-core url = https://github.com/Uniswap/v3-core +[submodule "lib/account-abstraction"] + path = lib/account-abstraction + url = https://github.com/eth-infinitism/account-abstraction + branch = releases/v0.7 +[submodule "lib/openzeppelin-contracts"] + path = lib/openzeppelin-contracts + url = https://github.com/OpenZeppelin/openzeppelin-contracts + branch = master diff --git a/contracts/references/SampleVerifyingPaymaster.sol b/contracts/references/SampleVerifyingPaymaster.sol deleted file mode 100644 index 1522c6e..0000000 --- a/contracts/references/SampleVerifyingPaymaster.sol +++ /dev/null @@ -1,119 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.26; - -/* solhint-disable reason-string */ -/* solhint-disable no-inline-assembly */ - -import "account-abstraction/contracts/core/BasePaymaster.sol"; -import "account-abstraction/contracts/core/UserOperationLib.sol"; -import "account-abstraction/contracts/core/Helpers.sol"; -import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.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 the account-specific signature: - * - the paymaster checks a signature to agree to PAY for GAS. - * - the account checks a signature to prove identity and account ownership. - */ -contract VerifyingPaymaster is BasePaymaster { - using UserOperationLib for PackedUserOperation; - - address public immutable verifyingSigner; - - uint256 private constant VALID_TIMESTAMP_OFFSET = PAYMASTER_DATA_OFFSET; - - uint256 private constant SIGNATURE_OFFSET = VALID_TIMESTAMP_OFFSET + 64; - - constructor(IEntryPoint _entryPoint, address _verifyingSigner) BasePaymaster(_entryPoint) { - verifyingSigner = _verifyingSigner; - } - - /** - * 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( - 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( - abi.encode( - sender, - userOp.nonce, - keccak256(userOp.initCode), - keccak256(userOp.callData), - userOp.accountGasLimits, - uint256(bytes32(userOp.paymasterAndData[PAYMASTER_VALIDATION_GAS_OFFSET:PAYMASTER_DATA_OFFSET])), - userOp.preVerificationGas, - userOp.gasFees, - block.chainid, - address(this), - 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:]; - } - - /** - * 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) - { - (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 "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 - if (verifyingSigner != ECDSA.recover(hash, signature)) { - return ("", _packValidationData(true, validUntil, validAfter)); - } - - //no need for other on-chain validation: entire UserOp should have been checked - // by the external service prior to signing it. - return ("", _packValidationData(false, validUntil, validAfter)); - } -} diff --git a/contracts/sponsorship/BiconomySponsorshipPaymaster.sol b/contracts/sponsorship/BiconomySponsorshipPaymaster.sol index f447cda..76460de 100644 --- a/contracts/sponsorship/BiconomySponsorshipPaymaster.sol +++ b/contracts/sponsorship/BiconomySponsorshipPaymaster.sol @@ -9,7 +9,7 @@ import "@account-abstraction/contracts/core/Helpers.sol"; import { SignatureCheckerLib } from "@solady/src/utils/SignatureCheckerLib.sol"; import { ECDSA as ECDSA_solady } from "@solady/src/utils/ECDSA.sol"; import { BiconomySponsorshipPaymasterErrors } from "../common/BiconomySponsorshipPaymasterErrors.sol"; -import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; +import { ReentrancyGuardTransient } from "@openzeppelin/contracts/utils/ReentrancyGuardTransient.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { SafeTransferLib } from "@solady/src/utils/SafeTransferLib.sol"; import { IBiconomySponsorshipPaymaster } from "../interfaces/IBiconomySponsorshipPaymaster.sol"; @@ -31,7 +31,7 @@ import { IBiconomySponsorshipPaymaster } from "../interfaces/IBiconomySponsorshi contract BiconomySponsorshipPaymaster is BasePaymaster, - ReentrancyGuard, + ReentrancyGuardTransient, BiconomySponsorshipPaymasterErrors, IBiconomySponsorshipPaymaster { diff --git a/contracts/token/BiconomyTokenPaymaster.sol b/contracts/token/BiconomyTokenPaymaster.sol index 837cc7d..a28a398 100644 --- a/contracts/token/BiconomyTokenPaymaster.sol +++ b/contracts/token/BiconomyTokenPaymaster.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.26; import { ECDSA as ECDSA_solady } from "@solady/src/utils/ECDSA.sol"; -import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; +import { ReentrancyGuardTransient } from "@openzeppelin/contracts/utils/ReentrancyGuardTransient.sol"; import { IEntryPoint } from "@account-abstraction/contracts/interfaces/IEntryPoint.sol"; import { SignatureCheckerLib } from "@solady/src/utils/SignatureCheckerLib.sol"; import { PackedUserOperation, UserOperationLib } from "@account-abstraction/contracts/core/UserOperationLib.sol"; @@ -19,14 +19,14 @@ import "@account-abstraction/contracts/core/Helpers.sol"; * @author ShivaanshK * @author livingrockrises * @notice Token Paymaster for Entry Point v0.7 - * @dev A paymaster that allows user to pay gas fee in ERC20 tokens. The paymaster owner chooses which tokens to + * @dev A paymaster that allows user to pay gas fees 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, + ReentrancyGuardTransient, BiconomyTokenPaymasterErrors, IBiconomyTokenPaymaster { @@ -208,7 +208,7 @@ contract BiconomyTokenPaymaster is address feeToken, address oracleAggregator, uint256 exchangeRate, - uint32 priceMarkup + uint32 dynamicAdjustment ) public view @@ -234,7 +234,7 @@ contract BiconomyTokenPaymaster is feeToken, oracleAggregator, exchangeRate, - priceMarkup + dynamicAdjustment ) ); } @@ -249,7 +249,7 @@ contract BiconomyTokenPaymaster is address feeToken, address oracleAggregator, uint256 exchangeRate, - uint32 priceMarkup, + uint32 dynamicAdjustment, bytes calldata signature ) { @@ -260,7 +260,7 @@ contract BiconomyTokenPaymaster is feeToken = address(bytes20(paymasterAndData[PAYMASTER_DATA_OFFSET + 13:PAYMASTER_DATA_OFFSET + 33])); oracleAggregator = address(bytes20(paymasterAndData[PAYMASTER_DATA_OFFSET + 33:PAYMASTER_DATA_OFFSET + 53])); exchangeRate = uint256(bytes32(paymasterAndData[PAYMASTER_DATA_OFFSET + 53:PAYMASTER_DATA_OFFSET + 85])); - priceMarkup = uint32(bytes4(paymasterAndData[PAYMASTER_DATA_OFFSET + 85:PAYMASTER_DATA_OFFSET + 89])); + dynamicAdjustment = uint32(bytes4(paymasterAndData[PAYMASTER_DATA_OFFSET + 85:PAYMASTER_DATA_OFFSET + 89])); signature = paymasterAndData[PAYMASTER_DATA_OFFSET + 89:]; } } @@ -289,7 +289,7 @@ contract BiconomyTokenPaymaster is address feeToken, address oracleAggregator, uint256 exchangeRate, - uint32 priceMarkup, + uint32 dynamicAdjustment, bytes calldata signature ) = parsePaymasterAndData(userOp.paymasterAndData); @@ -300,27 +300,29 @@ contract BiconomyTokenPaymaster is bool validSig = verifyingSigner.isValidSignatureNow( ECDSA_solady.toEthSignedMessageHash( getHash( - userOp, priceSource, validUntil, validAfter, feeToken, oracleAggregator, exchangeRate, priceMarkup + userOp, priceSource, validUntil, validAfter, feeToken, oracleAggregator, exchangeRate, dynamicAdjustment ) ), signature ); + // Return with SIG_VALIDATION_FAILED instead of reverting if (!validSig) { return ("", _packValidationData(true, validUntil, validAfter)); } + + } /** * @dev Post-operation handler. * This method is abstract in BasePaymaster and must be implemented in derived contracts. - * @param mode The mode of the post operation (opSucceeded, opReverted, or postOpReverted). * @param context The context value returned by validatePaymasterUserOp. * @param actualGasCost Actual gas used so far (excluding this postOp call). * @param actualUserOpFeePerGas The gas price this UserOp pays. */ function _postOp( - PostOpMode mode, + PostOpMode, bytes calldata context, uint256 actualGasCost, uint256 actualUserOpFeePerGas diff --git a/foundry.toml b/foundry.toml index 80fe03b..eae956e 100644 --- a/foundry.toml +++ b/foundry.toml @@ -4,7 +4,6 @@ auto_detect_solc = false block_timestamp = 1_680_220_800 # March 31, 2023 at 00:00 GMT bytecode_hash = "none" - evm_version = "paris" # See https://www.evmdiff.com/features?name=PUSH0&kind=opcode gas_reports = ["*"] optimizer = true optimizer_runs = 1_000_000 diff --git a/lib/account-abstraction b/lib/account-abstraction new file mode 160000 index 0000000..7af70c8 --- /dev/null +++ b/lib/account-abstraction @@ -0,0 +1 @@ +Subproject commit 7af70c8993a6f42973f520ae0752386a5032abe7 diff --git a/lib/forge-std b/lib/forge-std index 8948d45..1ce7535 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit 8948d45d3d9022c508b83eb5d26fd3a7a93f2f32 +Subproject commit 1ce7535a517406b9aec7ea1ea27c1b41376f712c diff --git a/lib/nexus b/lib/nexus index ab9616b..b8085a3 160000 --- a/lib/nexus +++ b/lib/nexus @@ -1 +1 @@ -Subproject commit ab9616bd71fcd51048e834f87a7b60dccbfc0adb +Subproject commit b8085a3d688afb9149c129a34b4bb9cefb93ae38 diff --git a/lib/nexus.git b/lib/nexus.git deleted file mode 160000 index 5d81e53..0000000 --- a/lib/nexus.git +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5d81e533941b49194fbc469b09b182c6c5d0e9d9 diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts new file mode 160000 index 0000000..b73bcb2 --- /dev/null +++ b/lib/openzeppelin-contracts @@ -0,0 +1 @@ +Subproject commit b73bcb231fb6f5e7bc973edc75ab7f6c812a2255 diff --git a/lib/v3-core b/lib/v3-core index e3589b1..d8b1c63 160000 --- a/lib/v3-core +++ b/lib/v3-core @@ -1 +1 @@ -Subproject commit e3589b192d0be27e100cd0daaf6c97204fdb1899 +Subproject commit d8b1c635c275d2a9450bd6a78f3fa2484fef73eb diff --git a/lib/v3-periphery b/lib/v3-periphery index 80f26c8..0682387 160000 --- a/lib/v3-periphery +++ b/lib/v3-periphery @@ -1 +1 @@ -Subproject commit 80f26c86c57b8a5e4b913f42844d4c8bd274d058 +Subproject commit 0682387198a24c7cd63566a2c58398533860a5d1 diff --git a/remappings.txt b/remappings.txt index 9622f6b..d8e3f38 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,10 +1,11 @@ -@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/ +@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/ @prb/test/=node_modules/@prb/test/ @nexus/=lib/nexus/ @forge-std/=lib/forge-std/ -@uniswap/v3-periphery/=lib/v3-periphery -@uniswap/v3-core/=lib/v3-core -@account-abstraction=node_modules/account-abstraction/ +@uniswap/v3-periphery/=lib/v3-periphery/ +@uniswap/v3-core/=lib/v3-core/ +@account-abstraction=lib/account-abstraction/ +account-abstraction=lib/account-abstraction/ @modulekit/=node_modules/modulekit/src/ @sentinellist/=node_modules/sentinellist/ -@solady/=node_modules/solady +@solady/=node_modules/solady/ diff --git a/test/base/TestBase.sol b/test/base/TestBase.sol index cd8c222..b21cfb8 100644 --- a/test/base/TestBase.sol +++ b/test/base/TestBase.sol @@ -5,6 +5,7 @@ import { Test } from "forge-std/src/Test.sol"; import { Vm } from "forge-std/src/Vm.sol"; import "@solady/src/utils/ECDSA.sol"; +import { TestHelper } from "@nexus/test/foundry/utils/TestHelper.t.sol"; import { EntryPoint } from "@account-abstraction/contracts/core/EntryPoint.sol"; import { IEntryPoint } from "@account-abstraction/contracts/interfaces/IEntryPoint.sol"; @@ -14,54 +15,18 @@ import { IPaymaster } from "@account-abstraction/contracts/interfaces/IPaymaster 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"; -abstract contract TestBase is CheatCodes, BaseEventsAndErrors { - // ----------------------------------------- - // State Variables - // ----------------------------------------- +abstract contract TestBase is CheatCodes, TestHelper, BaseEventsAndErrors { + address constant ENTRYPOINT_ADDRESS = address(0x0000000071727De22E5E9d8BAf0edAc6f37da032); - Vm.Wallet internal DEPLOYER; - Vm.Wallet internal ALICE; - Vm.Wallet internal BOB; - Vm.Wallet internal CHARLIE; - Vm.Wallet internal DAN; - Vm.Wallet internal EMMA; - Vm.Wallet internal BUNDLER; Vm.Wallet internal PAYMASTER_OWNER; Vm.Wallet internal PAYMASTER_SIGNER; Vm.Wallet internal PAYMASTER_FEE_COLLECTOR; Vm.Wallet internal DAPP_ACCOUNT; - Vm.Wallet internal FACTORY_OWNER; - - address internal ALICE_ADDRESS; - address internal BOB_ADDRESS; - address internal CHARLIE_ADDRESS; - address internal DAN_ADDRESS; - address internal EMMA_ADDRESS; - - Nexus internal ALICE_ACCOUNT; - Nexus internal BOB_ACCOUNT; - Nexus internal CHARLIE_ACCOUNT; - Nexus internal DAN_ACCOUNT; - Nexus internal EMMA_ACCOUNT; - - address constant ENTRYPOINT_ADDRESS = address(0x0000000071727De22E5E9d8BAf0edAc6f37da032); - IEntryPoint internal ENTRYPOINT; - - NexusAccountFactory internal FACTORY; - BiconomyMetaFactory internal META_FACTORY; - MockValidator internal VALIDATOR_MODULE; - Nexus internal ACCOUNT_IMPLEMENTATION; - Bootstrap internal BOOTSTRAPPER; // Used to buffer user op gas limits // GAS_LIMIT = (ESTIMATED_GAS * GAS_BUFFER_RATIO) / 100 @@ -83,6 +48,7 @@ abstract contract TestBase is CheatCodes, BaseEventsAndErrors { function setupTestEnvironment() internal virtual { /// Initializes the testing environment setupPredefinedWallets(); + setupPaymasterPredefinedWallets(); deployTestContracts(); deployNexusForPredefinedWallets(); } @@ -93,22 +59,7 @@ abstract contract TestBase is CheatCodes, BaseEventsAndErrors { return wallet; } - function setupPredefinedWallets() internal { - DEPLOYER = createAndFundWallet("DEPLOYER", 1000 ether); - BUNDLER = createAndFundWallet("BUNDLER", 1000 ether); - - ALICE = createAndFundWallet("ALICE", 1000 ether); - BOB = createAndFundWallet("BOB", 1000 ether); - CHARLIE = createAndFundWallet("CHARLIE", 1000 ether); - DAN = createAndFundWallet("DAN", 1000 ether); - EMMA = createAndFundWallet("EMMA", 1000 ether); - - ALICE_ADDRESS = ALICE.addr; - BOB_ADDRESS = BOB.addr; - CHARLIE_ADDRESS = CHARLIE.addr; - DAN_ADDRESS = DAN.addr; - EMMA_ADDRESS = EMMA.addr; - + function setupPaymasterPredefinedWallets() internal { PAYMASTER_OWNER = createAndFundWallet("PAYMASTER_OWNER", 1000 ether); PAYMASTER_SIGNER = createAndFundWallet("PAYMASTER_SIGNER", 1000 ether); PAYMASTER_FEE_COLLECTOR = createAndFundWallet("PAYMASTER_FEE_COLLECTOR", 1000 ether); @@ -116,227 +67,6 @@ abstract contract TestBase is CheatCodes, BaseEventsAndErrors { FACTORY_OWNER = createAndFundWallet("FACTORY_OWNER", 1000 ether); } - function deployTestContracts() internal { - ENTRYPOINT = new EntryPoint(); - vm.etch(ENTRYPOINT_ADDRESS, address(ENTRYPOINT).code); - ENTRYPOINT = IEntryPoint(ENTRYPOINT_ADDRESS); - ACCOUNT_IMPLEMENTATION = new Nexus(address(ENTRYPOINT)); - FACTORY = new NexusAccountFactory(address(ACCOUNT_IMPLEMENTATION), address(FACTORY_OWNER.addr)); - META_FACTORY = new BiconomyMetaFactory(address(FACTORY_OWNER.addr)); - vm.prank(FACTORY_OWNER.addr); - META_FACTORY.addFactoryToWhitelist(address(FACTORY)); - VALIDATOR_MODULE = new MockValidator(); - BOOTSTRAPPER = new Bootstrap(); - } - - // ----------------------------------------- - // Account Deployment Functions - // ----------------------------------------- - /// @notice Deploys an account with a specified wallet, deposit amount, and optional custom validator - /// @param wallet The wallet to deploy the account for - /// @param deposit The deposit amount - /// @param validator The custom validator address, if not provided uses default - /// @return The deployed Nexus account - function deployNexus(Vm.Wallet memory wallet, uint256 deposit, address validator) internal returns (Nexus) { - address payable accountAddress = calculateAccountAddress(wallet.addr, validator); - bytes memory initCode = buildInitCode(wallet.addr, validator); - - PackedUserOperation[] memory userOps = new PackedUserOperation[](1); - userOps[0] = buildUserOpWithInitAndCalldata(wallet, initCode, "", validator); - - ENTRYPOINT.depositTo{ value: deposit }(address(accountAddress)); - ENTRYPOINT.handleOps(userOps, payable(wallet.addr)); - assertTrue(MockValidator(validator).isOwner(accountAddress, wallet.addr)); - return Nexus(accountAddress); - } - - /// @notice Deploys Nexus accounts for predefined wallets - function deployNexusForPredefinedWallets() internal { - BOB_ACCOUNT = deployNexus(BOB, 100 ether, address(VALIDATOR_MODULE)); - vm.label(address(BOB_ACCOUNT), "BOB_ACCOUNT"); - ALICE_ACCOUNT = deployNexus(ALICE, 100 ether, address(VALIDATOR_MODULE)); - vm.label(address(ALICE_ACCOUNT), "ALICE_ACCOUNT"); - CHARLIE_ACCOUNT = deployNexus(CHARLIE, 100 ether, address(VALIDATOR_MODULE)); - vm.label(address(CHARLIE_ACCOUNT), "CHARLIE_ACCOUNT"); - DAN_ACCOUNT = deployNexus(DAN, 100 ether, address(VALIDATOR_MODULE)); - vm.label(address(DAN_ACCOUNT), "DAN_ACCOUNT"); - EMMA_ACCOUNT = deployNexus(EMMA, 100 ether, address(VALIDATOR_MODULE)); - vm.label(address(EMMA_ACCOUNT), "EMMA_ACCOUNT"); - } - // ----------------------------------------- - // Utility Functions - // ----------------------------------------- - - /// @notice Calculates the address of a new account - /// @param owner The address of the owner - /// @param validator The address of the validator - /// @return account The calculated account address - function calculateAccountAddress( - address owner, - address validator - ) - internal - view - returns (address payable account) - { - bytes memory moduleInstallData = abi.encodePacked(owner); - - BootstrapConfig[] memory validators = BootstrapLib.createArrayConfig(validator, moduleInstallData); - BootstrapConfig memory hook = BootstrapLib.createSingleConfig(address(0), ""); - bytes memory saDeploymentIndex = "0"; - - // Create initcode and salt to be sent to Factory - bytes memory _initData = BOOTSTRAPPER.getInitNexusScopedCalldata(validators, hook); - bytes32 salt = keccak256(saDeploymentIndex); - - account = FACTORY.computeAccountAddress(_initData, salt); - return account; - } - - /// @notice Prepares the init code for account creation with a validator - /// @param ownerAddress The address of the owner - /// @param validator The address of the validator - /// @return initCode The prepared init code - function buildInitCode(address ownerAddress, address validator) internal view returns (bytes memory initCode) { - bytes memory moduleInitData = abi.encodePacked(ownerAddress); - - BootstrapConfig[] memory validators = BootstrapLib.createArrayConfig(validator, moduleInitData); - BootstrapConfig memory hook = BootstrapLib.createSingleConfig(address(0), ""); - - bytes memory saDeploymentIndex = "0"; - - // Create initcode and salt to be sent to Factory - bytes memory _initData = BOOTSTRAPPER.getInitNexusScopedCalldata(validators, hook); - - bytes32 salt = keccak256(saDeploymentIndex); - - bytes memory factoryData = abi.encodeWithSelector(FACTORY.createAccount.selector, _initData, salt); - - // Prepend the factory address to the encoded function call to form the initCode - initCode = abi.encodePacked( - address(META_FACTORY), - abi.encodeWithSelector(META_FACTORY.deployWithFactory.selector, address(FACTORY), factoryData) - ); - } - - /// @notice Prepares a user operation with init code and call data - /// @param wallet The wallet for which the user operation is prepared - /// @param initCode The init code - /// @param callData The call data - /// @param validator The validator address - /// @return userOp The prepared user operation - function buildUserOpWithInitAndCalldata( - Vm.Wallet memory wallet, - bytes memory initCode, - bytes memory callData, - address validator - ) - internal - view - returns (PackedUserOperation memory userOp) - { - userOp = buildUserOpWithCalldata(wallet, callData, validator); - userOp.initCode = initCode; - - bytes memory signature = signUserOp(wallet, userOp); - userOp.signature = signature; - } - - /// @notice Prepares a user operation with call data and a validator - /// @param wallet The wallet for which the user operation is prepared - /// @param callData The call data - /// @param validator The validator address - /// @return userOp The prepared user operation - function buildUserOpWithCalldata( - Vm.Wallet memory wallet, - bytes memory callData, - address validator - ) - internal - view - returns (PackedUserOperation memory userOp) - { - address payable account = calculateAccountAddress(wallet.addr, validator); - uint256 nonce = getNonce(account, validator); - userOp = buildPackedUserOp(account, nonce); - userOp.callData = callData; - - bytes memory signature = signUserOp(wallet, userOp); - userOp.signature = signature; - } - - /// @notice Retrieves the nonce for a given account and validator - /// @param account The account address - /// @param validator The validator address - /// @return nonce The retrieved nonce - function getNonce(address account, address validator) internal view returns (uint256 nonce) { - uint192 key = uint192(bytes24(bytes20(address(validator)))); - nonce = ENTRYPOINT.getNonce(address(account), key); - } - - /// @notice Signs a user operation - /// @param wallet The wallet to sign the operation - /// @param userOp The user operation to sign - /// @return The signed user operation - function signUserOp( - Vm.Wallet memory wallet, - PackedUserOperation memory userOp - ) - internal - view - returns (bytes memory) - { - bytes32 opHash = ENTRYPOINT.getUserOpHash(userOp); - return signMessage(wallet, opHash); - } - - // ----------------------------------------- - // Utility Functions - // ----------------------------------------- - - /// @notice Modifies the address of a deployed contract in a test environment - /// @param originalAddress The original address of the contract - /// @param newAddress The new address to replace the original - function changeContractAddress(address originalAddress, address newAddress) internal { - vm.etch(newAddress, originalAddress.code); - } - - /// @notice Builds a user operation struct for account abstraction tests - /// @param sender The sender address - /// @param nonce The nonce - /// @return userOp The built user operation - function buildPackedUserOp(address sender, uint256 nonce) internal pure returns (PackedUserOperation memory) { - return PackedUserOperation({ - sender: sender, - nonce: nonce, - initCode: "", - callData: "", - accountGasLimits: bytes32(abi.encodePacked(uint128(3e6), uint128(3e6))), // verification and call gas limit - preVerificationGas: 3e5, // Adjusted preVerificationGas - gasFees: bytes32(abi.encodePacked(uint128(3e6), uint128(3e6))), // maxFeePerGas and maxPriorityFeePerGas - paymasterAndData: "", - signature: "" - }); - } - - /// @notice Signs a message and packs r, s, v into bytes - /// @param wallet The wallet to sign the message - /// @param messageHash The hash of the message to sign - /// @return signature The packed signature - function signMessage(Vm.Wallet memory wallet, bytes32 messageHash) internal pure returns (bytes memory signature) { - bytes32 userOpHash = ECDSA.toEthSignedMessageHash(messageHash); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(wallet.privateKey, userOpHash); - signature = abi.encodePacked(r, s, v); - } - - /// @notice Pre-funds a smart account and asserts success - /// @param sa The smart account address - /// @param prefundAmount The amount to pre-fund - function prefundSmartAccountAndAssertSuccess(address sa, uint256 prefundAmount) internal { - (bool res,) = sa.call{ value: prefundAmount }(""); // Pre-funding the account contract - assertTrue(res, "Pre-funding account should succeed"); - } - function estimateUserOpGasCosts(PackedUserOperation memory userOp) internal prankModifier(ENTRYPOINT_ADDRESS) diff --git a/test/unit/concrete/TestSponsorshipPaymaster.t.sol b/test/unit/concrete/TestSponsorshipPaymaster.t.sol index 3457c50..5193952 100644 --- a/test/unit/concrete/TestSponsorshipPaymaster.t.sol +++ b/test/unit/concrete/TestSponsorshipPaymaster.t.sol @@ -71,9 +71,9 @@ contract TestSponsorshipPaymasterWithDynamicAdjustment is TestBase { function test_OwnershipTransfer() external prankModifier(PAYMASTER_OWNER.addr) { vm.expectEmit(true, true, false, true, address(bicoPaymaster)); - emit OwnershipTransferred(PAYMASTER_OWNER.addr, DAN_ADDRESS); - bicoPaymaster.transferOwnership(DAN_ADDRESS); - assertEq(bicoPaymaster.owner(), DAN_ADDRESS); + emit OwnershipTransferred(PAYMASTER_OWNER.addr, BOB_ADDRESS); + bicoPaymaster.transferOwnership(BOB_ADDRESS); + assertEq(bicoPaymaster.owner(), BOB_ADDRESS); } function test_RevertIf_OwnershipTransferToZeroAddress() external prankModifier(PAYMASTER_OWNER.addr) { @@ -83,16 +83,16 @@ contract TestSponsorshipPaymasterWithDynamicAdjustment is TestBase { function test_RevertIf_UnauthorizedOwnershipTransfer() external { vm.expectRevert(abi.encodeWithSelector(Unauthorized.selector)); - bicoPaymaster.transferOwnership(DAN_ADDRESS); + bicoPaymaster.transferOwnership(BOB_ADDRESS); } function test_SetVerifyingSigner() external prankModifier(PAYMASTER_OWNER.addr) { vm.expectEmit(true, true, true, true, address(bicoPaymaster)); emit IBiconomySponsorshipPaymaster.VerifyingSignerChanged( - PAYMASTER_SIGNER.addr, DAN_ADDRESS, PAYMASTER_OWNER.addr + PAYMASTER_SIGNER.addr, BOB_ADDRESS, PAYMASTER_OWNER.addr ); - bicoPaymaster.setSigner(DAN_ADDRESS); - assertEq(bicoPaymaster.verifyingSigner(), DAN_ADDRESS); + bicoPaymaster.setSigner(BOB_ADDRESS); + assertEq(bicoPaymaster.verifyingSigner(), BOB_ADDRESS); } function test_RevertIf_SetVerifyingSignerToContract() external prankModifier(PAYMASTER_OWNER.addr) { @@ -107,16 +107,16 @@ contract TestSponsorshipPaymasterWithDynamicAdjustment is TestBase { function test_RevertIf_UnauthorizedSetVerifyingSigner() external { vm.expectRevert(abi.encodeWithSelector(Unauthorized.selector)); - bicoPaymaster.setSigner(DAN_ADDRESS); + bicoPaymaster.setSigner(BOB_ADDRESS); } function test_SetFeeCollector() external prankModifier(PAYMASTER_OWNER.addr) { vm.expectEmit(true, true, true, true, address(bicoPaymaster)); emit IBiconomySponsorshipPaymaster.FeeCollectorChanged( - PAYMASTER_FEE_COLLECTOR.addr, DAN_ADDRESS, PAYMASTER_OWNER.addr + PAYMASTER_FEE_COLLECTOR.addr, BOB_ADDRESS, PAYMASTER_OWNER.addr ); - bicoPaymaster.setFeeCollector(DAN_ADDRESS); - assertEq(bicoPaymaster.feeCollector(), DAN_ADDRESS); + bicoPaymaster.setFeeCollector(BOB_ADDRESS); + assertEq(bicoPaymaster.feeCollector(), BOB_ADDRESS); } function test_RevertIf_SetFeeCollectorToZeroAddress() external prankModifier(PAYMASTER_OWNER.addr) { @@ -126,7 +126,7 @@ contract TestSponsorshipPaymasterWithDynamicAdjustment is TestBase { function test_RevertIf_UnauthorizedSetFeeCollector() external { vm.expectRevert(abi.encodeWithSelector(Unauthorized.selector)); - bicoPaymaster.setFeeCollector(DAN_ADDRESS); + bicoPaymaster.setFeeCollector(BOB_ADDRESS); } function test_SetUnaccountedGas() external prankModifier(PAYMASTER_OWNER.addr) { @@ -178,16 +178,16 @@ contract TestSponsorshipPaymasterWithDynamicAdjustment is TestBase { function test_WithdrawTo() external prankModifier(DAPP_ACCOUNT.addr) { uint256 depositAmount = 10 ether; bicoPaymaster.depositFor{ value: depositAmount }(DAPP_ACCOUNT.addr); - uint256 danInitialBalance = DAN_ADDRESS.balance; + uint256 danInitialBalance = BOB_ADDRESS.balance; vm.expectEmit(true, true, true, true, address(bicoPaymaster)); - emit IBiconomySponsorshipPaymaster.GasWithdrawn(DAPP_ACCOUNT.addr, DAN_ADDRESS, depositAmount); - bicoPaymaster.withdrawTo(payable(DAN_ADDRESS), depositAmount); + emit IBiconomySponsorshipPaymaster.GasWithdrawn(DAPP_ACCOUNT.addr, BOB_ADDRESS, depositAmount); + bicoPaymaster.withdrawTo(payable(BOB_ADDRESS), depositAmount); uint256 dappPaymasterBalance = bicoPaymaster.getBalance(DAPP_ACCOUNT.addr); assertEq(dappPaymasterBalance, 0 ether); uint256 expectedDanBalance = danInitialBalance + depositAmount; - assertEq(DAN_ADDRESS.balance, expectedDanBalance); + assertEq(BOB_ADDRESS.balance, expectedDanBalance); } function test_RevertIf_WithdrawToZeroAddress() external prankModifier(DAPP_ACCOUNT.addr) { @@ -197,7 +197,7 @@ contract TestSponsorshipPaymasterWithDynamicAdjustment is TestBase { function test_RevertIf_WithdrawToExceedsBalance() external prankModifier(DAPP_ACCOUNT.addr) { vm.expectRevert(abi.encodeWithSelector(InsufficientFundsInGasTank.selector)); - bicoPaymaster.withdrawTo(payable(DAN_ADDRESS), 1 ether); + bicoPaymaster.withdrawTo(payable(BOB_ADDRESS), 1 ether); } function test_ValidatePaymasterAndPostOpWithoutDynamicAdjustment() external prankModifier(DAPP_ACCOUNT.addr) { diff --git a/test/unit/fuzz/TestFuzz_TestSponsorshipPaymaster.t.sol b/test/unit/fuzz/TestFuzz_TestSponsorshipPaymaster.t.sol index 1cf605a..983f610 100644 --- a/test/unit/fuzz/TestFuzz_TestSponsorshipPaymaster.t.sol +++ b/test/unit/fuzz/TestFuzz_TestSponsorshipPaymaster.t.sol @@ -38,16 +38,16 @@ contract TestFuzz_SponsorshipPaymasterWithDynamicAdjustment is TestBase { vm.deal(DAPP_ACCOUNT.addr, withdrawAmount); bicoPaymaster.depositFor{ value: withdrawAmount }(DAPP_ACCOUNT.addr); - uint256 danInitialBalance = DAN_ADDRESS.balance; + uint256 danInitialBalance = BOB_ADDRESS.balance; vm.expectEmit(true, true, true, true, address(bicoPaymaster)); - emit IBiconomySponsorshipPaymaster.GasWithdrawn(DAPP_ACCOUNT.addr, DAN_ADDRESS, withdrawAmount); - bicoPaymaster.withdrawTo(payable(DAN_ADDRESS), withdrawAmount); + emit IBiconomySponsorshipPaymaster.GasWithdrawn(DAPP_ACCOUNT.addr, BOB_ADDRESS, withdrawAmount); + bicoPaymaster.withdrawTo(payable(BOB_ADDRESS), withdrawAmount); uint256 dappPaymasterBalance = bicoPaymaster.getBalance(DAPP_ACCOUNT.addr); assertEq(dappPaymasterBalance, 0 ether); uint256 expectedDanBalance = danInitialBalance + withdrawAmount; - assertEq(DAN_ADDRESS.balance, expectedDanBalance); + assertEq(BOB_ADDRESS.balance, expectedDanBalance); } function testFuzz_Receive(uint256 ethAmount) external prankModifier(ALICE_ADDRESS) {