├── .env.example ├── .gitignore ├── .solcover.js ├── DeployInformation.json ├── README.md ├── TestingEnvironmentInformation.json ├── audits └── SlowMist Audit Report - Account Abstraction.pdf ├── contracts ├── @eth-infinitism-v0.6 │ ├── core │ │ ├── BaseAccount.sol │ │ ├── BasePaymaster.sol │ │ ├── EntryPoint.sol │ │ ├── Helpers.sol │ │ ├── NonceManager.sol │ │ ├── SenderCreator.sol │ │ └── StakeManager.sol │ ├── interfaces │ │ ├── IAccount.sol │ │ ├── IAggregator.sol │ │ ├── IEntryPoint.sol │ │ ├── INonceManager.sol │ │ ├── IPaymaster.sol │ │ ├── IStakeManager.sol │ │ └── UserOperation.sol │ └── utils │ │ └── Exec.sol ├── core │ └── Validations.sol ├── helper │ ├── BundlerDepositHelper.sol │ ├── DeployFactory.sol │ └── HandleOpsHelper.sol ├── interfaces │ ├── ERC1155TokenReceiver.sol │ ├── ERC721TokenReceiver.sol │ ├── ERC777TokensRecipient.sol │ ├── IERC165.sol │ ├── IExOraclePriceData.sol │ ├── IFreeGasPaymaster.sol │ ├── ILBRouter.sol │ ├── IPriceOracle.sol │ ├── ISmartAccountProxy.sol │ ├── ISmartAccountProxyFactory.sol │ ├── ISwapAdapter.sol │ ├── ISwapRouter.sol │ ├── ITokenPaymaster.sol │ ├── IValidations.sol │ └── IWETH9.sol ├── mock │ ├── MockChainlinkOracle.sol │ ├── MockEntryPointL1.sol │ ├── MockEntryPointSimulation.sol │ ├── MockEntryPointV06.sol │ ├── MockGuard.sol │ ├── MockModule.sol │ ├── MockOKCSwap.sol │ ├── MockSignatureManager.sol │ ├── MockTokenPaymaster.sol │ ├── MockTradeJoeV2.sol │ ├── MockUSDT.sol │ ├── MockUniswapV2.sol │ ├── MockUniswapV3.sol │ ├── MockWETH9.sol │ ├── MockWrongDeployFactory.sol │ ├── SimulateToken.sol │ ├── TestToken.sol │ ├── Uniswap │ │ ├── MockFactory.sol │ │ └── MockRouter.sol │ ├── UserOperationHelper.sol │ └── interfaces │ │ └── MockIEntryPoint.sol ├── paymaster │ ├── FreeGasPaymaster.sol │ ├── SupportedEntryPointLib.sol │ ├── TokenPaymaster.sol │ ├── oracle │ │ ├── ChainlinkOracleAdapter.sol │ │ └── ExOracleAdapter.sol │ └── swap │ │ ├── OKCSwapAdapter.sol │ │ ├── TradeJoeV2Adapter.sol │ │ ├── UniSwapV2Adapter.sol │ │ └── UniSwapV3Adapter.sol ├── simulations │ ├── core │ │ ├── EntryPoint.sol │ │ ├── EntryPointSimulations.sol │ │ ├── NonceManager.sol │ │ └── StakeManager.sol │ └── interfaces │ │ ├── IEntryPoint.sol │ │ └── IEntryPointSimulations.sol └── wallet │ ├── base │ ├── Executor.sol │ ├── FallbackManager.sol │ ├── GuardManager.sol │ ├── ModuleManager.sol │ ├── OwnerManager.sol │ └── SignatureManager.sol │ ├── common │ ├── Enum.sol │ ├── EtherPaymentFallback.sol │ ├── SecuredTokenTransfer.sol │ ├── SelfAuthorized.sol │ ├── SignatureDecoder.sol │ └── Singleton.sol │ ├── handler │ └── DefaultCallbackHandler.sol │ ├── helper │ ├── SimulateTxAccessor.sol │ ├── SmartAccountInitCode.sol │ ├── SmartAccountInitCodeV06.sol │ └── SmartAccountStorage.sol │ ├── v1 │ ├── SmartAccount.sol │ ├── SmartAccountProxy.sol │ └── SmartAccountProxyFactory.sol │ └── v2 │ ├── AccountFactoryProxy.sol │ ├── AccountFactoryStorage.sol │ ├── AccountFactoryV2.sol │ ├── SmartAccountProxyV2.sol │ └── SmartAccountV2.sol ├── hardhat.config.ts ├── package-lock.json ├── package.json ├── scripts ├── Utils.js ├── createTestingEnvironment.js ├── deploy.js ├── deploySmartAccountV2.0.1.js ├── smokeTest.js └── testInTestingEnvironment.js ├── test ├── Utils.js ├── core │ ├── BundlerDepositHelper.js │ ├── EntryPointSimulations.js │ ├── EntryPointV06.js │ ├── Validations.js │ └── entryPointV06AccountV2.js ├── paymaster │ ├── 1_FreeGasPaymasterTest.js │ ├── 2_TokenPaymasterTest.js │ ├── 3_TokenSwapTest.js │ ├── FreeGasPaymaster.js │ ├── TokenPaymaster.js │ └── swap │ │ ├── OKCSwapAdapter.js │ │ ├── TradeJoeV2Adapter.js │ │ ├── UniSwapV2Adapter.js │ │ └── UniswapV3Adapter.js ├── wallet-v2 │ ├── create-account.test.js │ ├── erc1271-signature.test.js │ └── nonce.test.js └── wallet │ ├── 1_SmartAccountTest.js │ ├── SmartAccount.js │ ├── SmartAccountProxyFactory.js │ └── base │ ├── FallbackManager.js │ └── SignatureManager.js └── tsconfig.json /.env.example: -------------------------------------------------------------------------------- 1 | PRIVATE_KEY= 2 | PUBLIC_KEY= -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | coverage 4 | .coverage.json 5 | ContractAddress.json 6 | types 7 | abi 8 | .DS_Store 9 | 10 | # Hardhat files 11 | cache 12 | artifacts 13 | scripts/* 14 | !scripts/deploy.js 15 | !scripts/deploySmartAccountV2.0.1.js 16 | !scripts/smokeTest.js 17 | !scripts/Utils.js 18 | !scripts/createTestingEnvironment.js 19 | !scripts/testInTestingEnvironment.js 20 | typechain -------------------------------------------------------------------------------- /.solcover.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | configureYulOptimizer: [true], 3 | istanbulFolder: "./coverage", 4 | istanbulReporter: ["html", "lcov", "text-summary"], 5 | }; 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Account Abstraction 2 | 3 | ## Deployed Addresses 4 | 5 | Deployed on ETH, OKTC, BNB, POLYGON, OP, AVAX, ARBITRUM, LINEA 6 | 7 | ### Core Contracts 8 | 9 | #### V2.0.0 10 | | Contracts | Address | 11 | | ------------------------ | -------------------------------------------- | 12 | | EntryPoint | `0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789` | 13 | | TokenPaymaster | `0xfb4f3F12258976395b34304e2BFD76D15E0Af44a` | 14 | | FreeGasPaymaster | `0xF44F59E49dDC8c6F7A7DA1e1ec2a5B9d50536E5B` | 15 | | CirclePaymaster | `0x9F47b0277B44d44AA9575c7F0E0e961651a27c9F` | 16 | | DefaultCallbackHandler | `0xA0be66C8d60A3ca53E83b5f376C6259b8de02586` | 17 | | SmartAccount | `0x5147CE3947a407c95687131Be01A2b8d55FD0A40` | 18 | | SmartAccountProxyFactory | `0x22fF1Dc5998258Faa1Ea45a776B57484f8Ab80A2` | 19 | | SimulateToken | `0x6bfBc1134f64714aA0F481d01db7C821C9f5F3Dd` | 20 | | BundlerDepositHelper | `0x0aB8356df7Fc47e890982b8401395184387A905A` | 21 | | Validations | `0x228E505D1F21948968fB52794ea823f65053A294` | 22 | 23 | #### V1.0.0 24 | | Contracts | Address | 25 | | ------------------------ | -------------------------------------------- | 26 | | EntryPoint | `0xdc5319815CdAaC2d113f7F275bc893ed7D9cA469` | 27 | | TokenPaymaster | `0xd348FB9D8a421f5B3CB077e819dE38c9Cd7fe6F2` | 28 | | FreeGasPaymaster | `0xd4cA5B29f8E222aAEEF944F445D1aC368a5d7694` | 29 | | DefaultCallbackHandler | `0xc9b02677ebFa3f4dA43EBEfC6fc38e11148b664D` | 30 | | SmartAccount | `0x3DbeB76d9d9444D7Db9DcF3799e17ACd247f8fac` | 31 | | SmartAccountProxyFactory | `0x81E11c4701C5189b0122ef42DaF1fF3d453D968E` | 32 | | SmartAccountInitCode | `0xEcf3f6844166D323409D36A531B50286133fB250` | 33 | | UserOperationHelper | `0x9A998225AB0A872665B35a8dC615aAbd5e73Cd12` | 34 | | SimulateToken | `0xEE41192f81C3048fFa02cB9b8613c5fb12227FF0` | 35 | | BundlerDepositHelper | `0x71C9F21517F85D36A0FCDB8E31Ba8a8e28622cFa` | 36 | 37 | ### Oracle Adapters 38 | 39 | | Networks | Contracts | Address | 40 | | -------- | ---------------------- | ------------------------------------------ | 41 | | ETH | ChainlinkOracleAdapter | 0x7bB8FF337C5172E004C0dEca560c1c1bB7f7FF0A | 42 | | OKTC | EXOracleAdapter | 0x9857f966529cb205689B7D698f495eA423E48d9c | 43 | | BNB | ChainlinkOracleAdapter | 0x7bB8FF337C5172E004C0dEca560c1c1bB7f7FF0A | 44 | | POLYGON | ChainlinkOracleAdapter | 0x7bB8FF337C5172E004C0dEca560c1c1bB7f7FF0A | 45 | | OP | ChainlinkOracleAdapter | 0x7bB8FF337C5172E004C0dEca560c1c1bB7f7FF0A | 46 | | AVAX | ChainlinkOracleAdapter | 0x7bB8FF337C5172E004C0dEca560c1c1bB7f7FF0A | 47 | | ARBITRUM | ChainlinkOracleAdapter | 0x7bB8FF337C5172E004C0dEca560c1c1bB7f7FF0A | 48 | | LINEA | ChainlinkOracleAdapter | 0x7bB8FF337C5172E004C0dEca560c1c1bB7f7FF0A | 49 | 50 | ### Swap Helpers 51 | 52 | | Networks | Contracts | Address | 53 | | -------- | ----------------- | ------------------------------------------ | 54 | | ETH | UniSwapV3Adapter | 0x1C821cD745924f2E008e2B6759c272a1736c6d8b | 55 | | OKTC | OKCSwapAdapter | 0x03e70e92dC6ED4b65B7ace9b44b85Bb2b55400f2 | 56 | | BNB | UniSwapV2Adapter | 0x7f4D0B7ee0a9a75D947419F8fDfB78d5aB91E57e | 57 | | POLYGON | UniSwapV3Adapter | 0x1C821cD745924f2E008e2B6759c272a1736c6d8b | 58 | | OP | UniSwapV3Adapter | 0x1C821cD745924f2E008e2B6759c272a1736c6d8b | 59 | | AVAX | TradeJoeV2Adapter | 0xE92E3568087D2999227c7a289eAf3c4a29c4CB90 | 60 | | ARBITRUM | UniSwapV3Adapter | 0x1C821cD745924f2E008e2B6759c272a1736c6d8b | 61 | | LINEA | UniSwapV3Adapter | 0xef7CfC63c34b6D09b32E88da0d6483d92F017Cdc | 62 | -------------------------------------------------------------------------------- /TestingEnvironmentInformation.json: -------------------------------------------------------------------------------- 1 | { 2 | "bundler": "0x26ced4fc889d52210bd14af3bda90f7e36d10725", 3 | "ContractOnChain": { 4 | "SmartAccountV2": { 5 | "ContractPath": "SmartAccountV2", 6 | "ContractAddress": "0x9F5bb180aD1f86B4446F536cF08A3A80c5378a3b" 7 | }, 8 | "AccountFactoryV2": { 9 | "ContractPath": "AccountFactoryV2", 10 | "ContractAddress": "0x943D544FcF002e13753F479796592561d6F29E18" 11 | } 12 | }, 13 | "ContractSetNewCode": { 14 | "EntryPoint": { 15 | "ContractPath": "contracts/@eth-infinitism-v0.6/core/EntryPoint.sol:EntryPoint", 16 | "ContractAddress": "0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789" 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /audits/SlowMist Audit Report - Account Abstraction.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/okx/AccountAbstraction/fdf920e0d12958bdf9281848396c9841d8466111/audits/SlowMist Audit Report - Account Abstraction.pdf -------------------------------------------------------------------------------- /contracts/@eth-infinitism-v0.6/core/BaseAccount.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity ^0.8.12; 3 | 4 | /* solhint-disable avoid-low-level-calls */ 5 | /* solhint-disable no-empty-blocks */ 6 | 7 | import "../interfaces/IAccount.sol"; 8 | import "../interfaces/IEntryPoint.sol"; 9 | import "./Helpers.sol"; 10 | 11 | /** 12 | * Basic account implementation. 13 | * this contract provides the basic logic for implementing the IAccount interface - validateUserOp 14 | * specific account implementation should inherit it and provide the account-specific logic 15 | */ 16 | abstract contract BaseAccount is IAccount { 17 | using UserOperationLib for UserOperation; 18 | 19 | //return value in case of signature failure, with no time-range. 20 | // equivalent to _packValidationData(true,0,0); 21 | uint256 constant internal SIG_VALIDATION_FAILED = 1; 22 | 23 | /** 24 | * Return the account nonce. 25 | * This method returns the next sequential nonce. 26 | * For a nonce of a specific key, use `entrypoint.getNonce(account, key)` 27 | */ 28 | function getNonce() public view virtual returns (uint256) { 29 | return entryPoint().getNonce(address(this), 0); 30 | } 31 | 32 | /** 33 | * return the entryPoint used by this account. 34 | * subclass should return the current entryPoint used by this account. 35 | */ 36 | function entryPoint() public view virtual returns (IEntryPoint); 37 | 38 | /** 39 | * Validate user's signature and nonce. 40 | * subclass doesn't need to override this method. Instead, it should override the specific internal validation methods. 41 | */ 42 | function validateUserOp(UserOperation calldata userOp, bytes32 userOpHash, uint256 missingAccountFunds) 43 | external override virtual returns (uint256 validationData) { 44 | _requireFromEntryPoint(); 45 | validationData = _validateSignature(userOp, userOpHash); 46 | _validateNonce(userOp.nonce); 47 | _payPrefund(missingAccountFunds); 48 | } 49 | 50 | /** 51 | * ensure the request comes from the known entrypoint. 52 | */ 53 | function _requireFromEntryPoint() internal virtual view { 54 | require(msg.sender == address(entryPoint()), "account: not from EntryPoint"); 55 | } 56 | 57 | /** 58 | * validate the signature is valid for this message. 59 | * @param userOp validate the userOp.signature field 60 | * @param userOpHash convenient field: the hash of the request, to check the signature against 61 | * (also hashes the entrypoint and chain id) 62 | * @return validationData signature and time-range of this operation 63 | * <20-byte> sigAuthorizer - 0 for valid signature, 1 to mark signature failure, 64 | * otherwise, an address of an "authorizer" contract. 65 | * <6-byte> validUntil - last timestamp this operation is valid. 0 for "indefinite" 66 | * <6-byte> validAfter - first timestamp this operation is valid 67 | * If the account doesn't use time-range, it is enough to return SIG_VALIDATION_FAILED value (1) for signature failure. 68 | * Note that the validation code cannot use block.timestamp (or block.number) directly. 69 | */ 70 | function _validateSignature(UserOperation calldata userOp, bytes32 userOpHash) 71 | internal virtual returns (uint256 validationData); 72 | 73 | /** 74 | * Validate the nonce of the UserOperation. 75 | * This method may validate the nonce requirement of this account. 76 | * e.g. 77 | * To limit the nonce to use sequenced UserOps only (no "out of order" UserOps): 78 | * `require(nonce < type(uint64).max)` 79 | * For a hypothetical account that *requires* the nonce to be out-of-order: 80 | * `require(nonce & type(uint64).max == 0)` 81 | * 82 | * The actual nonce uniqueness is managed by the EntryPoint, and thus no other 83 | * action is needed by the account itself. 84 | * 85 | * @param nonce to validate 86 | * 87 | * solhint-disable-next-line no-empty-blocks 88 | */ 89 | function _validateNonce(uint256 nonce) internal view virtual { 90 | } 91 | 92 | /** 93 | * sends to the entrypoint (msg.sender) the missing funds for this transaction. 94 | * subclass MAY override this method for better funds management 95 | * (e.g. send to the entryPoint more than the minimum required, so that in future transactions 96 | * it will not be required to send again) 97 | * @param missingAccountFunds the minimum value this method should send the entrypoint. 98 | * this value MAY be zero, in case there is enough deposit, or the userOp has a paymaster. 99 | */ 100 | function _payPrefund(uint256 missingAccountFunds) internal virtual { 101 | if (missingAccountFunds != 0) { 102 | (bool success,) = payable(msg.sender).call{value : missingAccountFunds, gas : type(uint256).max}(""); 103 | (success); 104 | //ignore failure (its EntryPoint's job to verify, not account.) 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /contracts/@eth-infinitism-v0.6/core/BasePaymaster.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity ^0.8.12; 3 | 4 | 5 | /* solhint-disable reason-string */ 6 | 7 | import "@openzeppelin/contracts/access/Ownable.sol"; 8 | import "../interfaces/IPaymaster.sol"; 9 | import "../interfaces/IEntryPoint.sol"; 10 | import "./Helpers.sol"; 11 | 12 | /** 13 | * Helper class for creating a paymaster. 14 | * provides helper methods for staking. 15 | * validates that the postOp is called only by the entryPoint 16 | */ 17 | abstract contract BasePaymaster is IPaymaster, Ownable { 18 | 19 | IEntryPoint immutable public entryPoint; 20 | 21 | constructor(IEntryPoint _entryPoint) { 22 | entryPoint = _entryPoint; 23 | } 24 | 25 | /// @inheritdoc IPaymaster 26 | function validatePaymasterUserOp(UserOperation calldata userOp, bytes32 userOpHash, uint256 maxCost) 27 | external override returns (bytes memory context, uint256 validationData) { 28 | _requireFromEntryPoint(); 29 | return _validatePaymasterUserOp(userOp, userOpHash, maxCost); 30 | } 31 | 32 | function _validatePaymasterUserOp(UserOperation calldata userOp, bytes32 userOpHash, uint256 maxCost) 33 | internal virtual returns (bytes memory context, uint256 validationData); 34 | 35 | /// @inheritdoc IPaymaster 36 | function postOp(PostOpMode mode, bytes calldata context, uint256 actualGasCost) external override { 37 | _requireFromEntryPoint(); 38 | _postOp(mode, context, actualGasCost); 39 | } 40 | 41 | /** 42 | * post-operation handler. 43 | * (verified to be called only through the entryPoint) 44 | * @dev if subclass returns a non-empty context from validatePaymasterUserOp, it must also implement this method. 45 | * @param mode enum with the following options: 46 | * opSucceeded - user operation succeeded. 47 | * opReverted - user op reverted. still has to pay for gas. 48 | * postOpReverted - user op succeeded, but caused postOp (in mode=opSucceeded) to revert. 49 | * Now this is the 2nd call, after user's op was deliberately reverted. 50 | * @param context - the context value returned by validatePaymasterUserOp 51 | * @param actualGasCost - actual gas used so far (without this postOp call). 52 | */ 53 | function _postOp(PostOpMode mode, bytes calldata context, uint256 actualGasCost) internal virtual { 54 | 55 | (mode,context,actualGasCost); // unused params 56 | // subclass must override this method if validatePaymasterUserOp returns a context 57 | revert("must override"); 58 | } 59 | 60 | /** 61 | * add a deposit for this paymaster, used for paying for transaction fees 62 | */ 63 | function deposit() public payable { 64 | entryPoint.depositTo{value : msg.value}(address(this)); 65 | } 66 | 67 | /** 68 | * withdraw value from the deposit 69 | * @param withdrawAddress target to send to 70 | * @param amount to withdraw 71 | */ 72 | function withdrawTo(address payable withdrawAddress, uint256 amount) public onlyOwner { 73 | entryPoint.withdrawTo(withdrawAddress, amount); 74 | } 75 | /** 76 | * add stake for this paymaster. 77 | * This method can also carry eth value to add to the current stake. 78 | * @param unstakeDelaySec - the unstake delay for this paymaster. Can only be increased. 79 | */ 80 | function addStake(uint32 unstakeDelaySec) external payable onlyOwner { 81 | entryPoint.addStake{value : msg.value}(unstakeDelaySec); 82 | } 83 | 84 | /** 85 | * return current paymaster's deposit on the entryPoint. 86 | */ 87 | function getDeposit() public view returns (uint256) { 88 | return entryPoint.balanceOf(address(this)); 89 | } 90 | 91 | /** 92 | * unlock the stake, in order to withdraw it. 93 | * The paymaster can't serve requests once unlocked, until it calls addStake again 94 | */ 95 | function unlockStake() external onlyOwner { 96 | entryPoint.unlockStake(); 97 | } 98 | 99 | /** 100 | * withdraw the entire paymaster's stake. 101 | * stake must be unlocked first (and then wait for the unstakeDelay to be over) 102 | * @param withdrawAddress the address to send withdrawn value. 103 | */ 104 | function withdrawStake(address payable withdrawAddress) external onlyOwner { 105 | entryPoint.withdrawStake(withdrawAddress); 106 | } 107 | 108 | /// validate the call is made from a valid entrypoint 109 | function _requireFromEntryPoint() internal virtual { 110 | require(msg.sender == address(entryPoint), "Sender not EntryPoint"); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /contracts/@eth-infinitism-v0.6/core/Helpers.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity ^0.8.12; 3 | 4 | /* solhint-disable no-inline-assembly */ 5 | 6 | /** 7 | * returned data from validateUserOp. 8 | * validateUserOp returns a uint256, with is created by `_packedValidationData` and parsed by `_parseValidationData` 9 | * @param aggregator - address(0) - the account validated the signature by itself. 10 | * address(1) - the account failed to validate the signature. 11 | * otherwise - this is an address of a signature aggregator that must be used to validate the signature. 12 | * @param validAfter - this UserOp is valid only after this timestamp. 13 | * @param validaUntil - this UserOp is valid only up to this timestamp. 14 | */ 15 | struct ValidationData { 16 | address aggregator; 17 | uint48 validAfter; 18 | uint48 validUntil; 19 | } 20 | 21 | //extract sigFailed, validAfter, validUntil. 22 | // also convert zero validUntil to type(uint48).max 23 | function _parseValidationData(uint validationData) pure returns (ValidationData memory data) { 24 | address aggregator = address(uint160(validationData)); 25 | uint48 validUntil = uint48(validationData >> 160); 26 | if (validUntil == 0) { 27 | validUntil = type(uint48).max; 28 | } 29 | uint48 validAfter = uint48(validationData >> (48 + 160)); 30 | return ValidationData(aggregator, validAfter, validUntil); 31 | } 32 | 33 | // intersect account and paymaster ranges. 34 | function _intersectTimeRange(uint256 validationData, uint256 paymasterValidationData) pure returns (ValidationData memory) { 35 | ValidationData memory accountValidationData = _parseValidationData(validationData); 36 | ValidationData memory pmValidationData = _parseValidationData(paymasterValidationData); 37 | address aggregator = accountValidationData.aggregator; 38 | if (aggregator == address(0)) { 39 | aggregator = pmValidationData.aggregator; 40 | } 41 | uint48 validAfter = accountValidationData.validAfter; 42 | uint48 validUntil = accountValidationData.validUntil; 43 | uint48 pmValidAfter = pmValidationData.validAfter; 44 | uint48 pmValidUntil = pmValidationData.validUntil; 45 | 46 | if (validAfter < pmValidAfter) validAfter = pmValidAfter; 47 | if (validUntil > pmValidUntil) validUntil = pmValidUntil; 48 | return ValidationData(aggregator, validAfter, validUntil); 49 | } 50 | 51 | /** 52 | * helper to pack the return value for validateUserOp 53 | * @param data - the ValidationData to pack 54 | */ 55 | function _packValidationData(ValidationData memory data) pure returns (uint256) { 56 | return uint160(data.aggregator) | (uint256(data.validUntil) << 160) | (uint256(data.validAfter) << (160 + 48)); 57 | } 58 | 59 | /** 60 | * helper to pack the return value for validateUserOp, when not using an aggregator 61 | * @param sigFailed - true for signature failure, false for success 62 | * @param validUntil last timestamp this UserOperation is valid (or zero for infinite) 63 | * @param validAfter first timestamp this UserOperation is valid 64 | */ 65 | function _packValidationData(bool sigFailed, uint48 validUntil, uint48 validAfter) pure returns (uint256) { 66 | return (sigFailed ? 1 : 0) | (uint256(validUntil) << 160) | (uint256(validAfter) << (160 + 48)); 67 | } 68 | 69 | /** 70 | * keccak function over calldata. 71 | * @dev copy calldata into memory, do keccak and drop allocated memory. Strangely, this is more efficient than letting solidity do it. 72 | */ 73 | function calldataKeccak(bytes calldata data) pure returns (bytes32 ret) { 74 | assembly { 75 | let mem := mload(0x40) 76 | let len := data.length 77 | calldatacopy(mem, data.offset, len) 78 | ret := keccak256(mem, len) 79 | } 80 | } 81 | 82 | -------------------------------------------------------------------------------- /contracts/@eth-infinitism-v0.6/core/NonceManager.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity ^0.8.12; 3 | 4 | import "../interfaces/IEntryPoint.sol"; 5 | 6 | /** 7 | * nonce management functionality 8 | */ 9 | contract NonceManager is INonceManager { 10 | 11 | /** 12 | * The next valid sequence number for a given nonce key. 13 | */ 14 | mapping(address => mapping(uint192 => uint256)) public nonceSequenceNumber; 15 | 16 | function getNonce(address sender, uint192 key) 17 | public view override returns (uint256 nonce) { 18 | return nonceSequenceNumber[sender][key] | (uint256(key) << 64); 19 | } 20 | 21 | // allow an account to manually increment its own nonce. 22 | // (mainly so that during construction nonce can be made non-zero, 23 | // to "absorb" the gas cost of first nonce increment to 1st transaction (construction), 24 | // not to 2nd transaction) 25 | function incrementNonce(uint192 key) public override { 26 | nonceSequenceNumber[msg.sender][key]++; 27 | } 28 | 29 | /** 30 | * validate nonce uniqueness for this account. 31 | * called just after validateUserOp() 32 | */ 33 | function _validateAndUpdateNonce(address sender, uint256 nonce) internal returns (bool) { 34 | 35 | uint192 key = uint192(nonce >> 64); 36 | uint64 seq = uint64(nonce); 37 | return nonceSequenceNumber[sender][key]++ == seq; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /contracts/@eth-infinitism-v0.6/core/SenderCreator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity ^0.8.12; 3 | 4 | /** 5 | * helper contract for EntryPoint, to call userOp.initCode from a "neutral" address, 6 | * which is explicitly not the entryPoint itself. 7 | */ 8 | contract SenderCreator { 9 | 10 | /** 11 | * call the "initCode" factory to create and return the sender account address 12 | * @param initCode the initCode value from a UserOp. contains 20 bytes of factory address, followed by calldata 13 | * @return sender the returned address of the created account, or zero address on failure. 14 | */ 15 | function createSender(bytes calldata initCode) external returns (address sender) { 16 | address factory = address(bytes20(initCode[0 : 20])); 17 | bytes memory initCallData = initCode[20 :]; 18 | bool success; 19 | /* solhint-disable no-inline-assembly */ 20 | assembly { 21 | success := call(gas(), factory, 0, add(initCallData, 0x20), mload(initCallData), 0, 32) 22 | sender := mload(0) 23 | } 24 | if (!success) { 25 | sender = address(0); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /contracts/@eth-infinitism-v0.6/core/StakeManager.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-only 2 | pragma solidity ^0.8.12; 3 | 4 | import "../interfaces/IStakeManager.sol"; 5 | 6 | /* solhint-disable avoid-low-level-calls */ 7 | /* solhint-disable not-rely-on-time */ 8 | /** 9 | * manage deposits and stakes. 10 | * deposit is just a balance used to pay for UserOperations (either by a paymaster or an account) 11 | * stake is value locked for at least "unstakeDelay" by a paymaster. 12 | */ 13 | abstract contract StakeManager is IStakeManager { 14 | 15 | /// maps paymaster to their deposits and stakes 16 | mapping(address => DepositInfo) public deposits; 17 | 18 | /// @inheritdoc IStakeManager 19 | function getDepositInfo(address account) public view returns (DepositInfo memory info) { 20 | return deposits[account]; 21 | } 22 | 23 | // internal method to return just the stake info 24 | function _getStakeInfo(address addr) internal view returns (StakeInfo memory info) { 25 | DepositInfo storage depositInfo = deposits[addr]; 26 | info.stake = depositInfo.stake; 27 | info.unstakeDelaySec = depositInfo.unstakeDelaySec; 28 | } 29 | 30 | /// return the deposit (for gas payment) of the account 31 | function balanceOf(address account) public view returns (uint256) { 32 | return deposits[account].deposit; 33 | } 34 | 35 | receive() external payable { 36 | depositTo(msg.sender); 37 | } 38 | 39 | function _incrementDeposit(address account, uint256 amount) internal { 40 | DepositInfo storage info = deposits[account]; 41 | uint256 newAmount = info.deposit + amount; 42 | require(newAmount <= type(uint112).max, "deposit overflow"); 43 | info.deposit = uint112(newAmount); 44 | } 45 | 46 | /** 47 | * add to the deposit of the given account 48 | */ 49 | function depositTo(address account) public payable { 50 | _incrementDeposit(account, msg.value); 51 | DepositInfo storage info = deposits[account]; 52 | emit Deposited(account, info.deposit); 53 | } 54 | 55 | /** 56 | * add to the account's stake - amount and delay 57 | * any pending unstake is first cancelled. 58 | * @param unstakeDelaySec the new lock duration before the deposit can be withdrawn. 59 | */ 60 | function addStake(uint32 unstakeDelaySec) public payable { 61 | DepositInfo storage info = deposits[msg.sender]; 62 | require(unstakeDelaySec > 0, "must specify unstake delay"); 63 | require(unstakeDelaySec >= info.unstakeDelaySec, "cannot decrease unstake time"); 64 | uint256 stake = info.stake + msg.value; 65 | require(stake > 0, "no stake specified"); 66 | require(stake <= type(uint112).max, "stake overflow"); 67 | deposits[msg.sender] = DepositInfo( 68 | info.deposit, 69 | true, 70 | uint112(stake), 71 | unstakeDelaySec, 72 | 0 73 | ); 74 | emit StakeLocked(msg.sender, stake, unstakeDelaySec); 75 | } 76 | 77 | /** 78 | * attempt to unlock the stake. 79 | * the value can be withdrawn (using withdrawStake) after the unstake delay. 80 | */ 81 | function unlockStake() external { 82 | DepositInfo storage info = deposits[msg.sender]; 83 | require(info.unstakeDelaySec != 0, "not staked"); 84 | require(info.staked, "already unstaking"); 85 | uint48 withdrawTime = uint48(block.timestamp) + info.unstakeDelaySec; 86 | info.withdrawTime = withdrawTime; 87 | info.staked = false; 88 | emit StakeUnlocked(msg.sender, withdrawTime); 89 | } 90 | 91 | 92 | /** 93 | * withdraw from the (unlocked) stake. 94 | * must first call unlockStake and wait for the unstakeDelay to pass 95 | * @param withdrawAddress the address to send withdrawn value. 96 | */ 97 | function withdrawStake(address payable withdrawAddress) external { 98 | DepositInfo storage info = deposits[msg.sender]; 99 | uint256 stake = info.stake; 100 | require(stake > 0, "No stake to withdraw"); 101 | require(info.withdrawTime > 0, "must call unlockStake() first"); 102 | require(info.withdrawTime <= block.timestamp, "Stake withdrawal is not due"); 103 | info.unstakeDelaySec = 0; 104 | info.withdrawTime = 0; 105 | info.stake = 0; 106 | emit StakeWithdrawn(msg.sender, withdrawAddress, stake); 107 | (bool success,) = withdrawAddress.call{value : stake}(""); 108 | require(success, "failed to withdraw stake"); 109 | } 110 | 111 | /** 112 | * withdraw from the deposit. 113 | * @param withdrawAddress the address to send withdrawn value. 114 | * @param withdrawAmount the amount to withdraw. 115 | */ 116 | function withdrawTo(address payable withdrawAddress, uint256 withdrawAmount) external { 117 | DepositInfo storage info = deposits[msg.sender]; 118 | require(withdrawAmount <= info.deposit, "Withdraw amount too large"); 119 | info.deposit = uint112(info.deposit - withdrawAmount); 120 | emit Withdrawn(msg.sender, withdrawAddress, withdrawAmount); 121 | (bool success,) = withdrawAddress.call{value : withdrawAmount}(""); 122 | require(success, "failed to withdraw"); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /contracts/@eth-infinitism-v0.6/interfaces/IAccount.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity ^0.8.12; 3 | 4 | import "./UserOperation.sol"; 5 | 6 | interface IAccount { 7 | 8 | /** 9 | * Validate user's signature and nonce 10 | * the entryPoint will make the call to the recipient only if this validation call returns successfully. 11 | * signature failure should be reported by returning SIG_VALIDATION_FAILED (1). 12 | * This allows making a "simulation call" without a valid signature 13 | * Other failures (e.g. nonce mismatch, or invalid signature format) should still revert to signal failure. 14 | * 15 | * @dev Must validate caller is the entryPoint. 16 | * Must validate the signature and nonce 17 | * @param userOp the operation that is about to be executed. 18 | * @param userOpHash hash of the user's request data. can be used as the basis for signature. 19 | * @param missingAccountFunds missing funds on the account's deposit in the entrypoint. 20 | * This is the minimum amount to transfer to the sender(entryPoint) to be able to make the call. 21 | * The excess is left as a deposit in the entrypoint, for future calls. 22 | * can be withdrawn anytime using "entryPoint.withdrawTo()" 23 | * In case there is a paymaster in the request (or the current deposit is high enough), this value will be zero. 24 | * @return validationData packaged ValidationData structure. use `_packValidationData` and `_unpackValidationData` to encode and decode 25 | * <20-byte> sigAuthorizer - 0 for valid signature, 1 to mark signature failure, 26 | * otherwise, an address of an "authorizer" contract. 27 | * <6-byte> validUntil - last timestamp this operation is valid. 0 for "indefinite" 28 | * <6-byte> validAfter - first timestamp this operation is valid 29 | * If an account doesn't use time-range, it is enough to return SIG_VALIDATION_FAILED value (1) for signature failure. 30 | * Note that the validation code cannot use block.timestamp (or block.number) directly. 31 | */ 32 | function validateUserOp(UserOperation calldata userOp, bytes32 userOpHash, uint256 missingAccountFunds) 33 | external returns (uint256 validationData); 34 | } 35 | -------------------------------------------------------------------------------- /contracts/@eth-infinitism-v0.6/interfaces/IAggregator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity ^0.8.12; 3 | 4 | import "./UserOperation.sol"; 5 | 6 | /** 7 | * Aggregated Signatures validator. 8 | */ 9 | interface IAggregator { 10 | 11 | /** 12 | * validate aggregated signature. 13 | * revert if the aggregated signature does not match the given list of operations. 14 | */ 15 | function validateSignatures(UserOperation[] calldata userOps, bytes calldata signature) external view; 16 | 17 | /** 18 | * validate signature of a single userOp 19 | * This method is should be called by bundler after EntryPoint.simulateValidation() returns (reverts) with ValidationResultWithAggregation 20 | * First it validates the signature over the userOp. Then it returns data to be used when creating the handleOps. 21 | * @param userOp the userOperation received from the user. 22 | * @return sigForUserOp the value to put into the signature field of the userOp when calling handleOps. 23 | * (usually empty, unless account and aggregator support some kind of "multisig" 24 | */ 25 | function validateUserOpSignature(UserOperation calldata userOp) 26 | external view returns (bytes memory sigForUserOp); 27 | 28 | /** 29 | * aggregate multiple signatures into a single value. 30 | * This method is called off-chain to calculate the signature to pass with handleOps() 31 | * bundler MAY use optimized custom code perform this aggregation 32 | * @param userOps array of UserOperations to collect the signatures from. 33 | * @return aggregatedSignature the aggregated signature 34 | */ 35 | function aggregateSignatures(UserOperation[] calldata userOps) external view returns (bytes memory aggregatedSignature); 36 | } 37 | -------------------------------------------------------------------------------- /contracts/@eth-infinitism-v0.6/interfaces/INonceManager.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity ^0.8.12; 3 | 4 | interface INonceManager { 5 | 6 | /** 7 | * Return the next nonce for this sender. 8 | * Within a given key, the nonce values are sequenced (starting with zero, and incremented by one on each userop) 9 | * But UserOp with different keys can come with arbitrary order. 10 | * 11 | * @param sender the account address 12 | * @param key the high 192 bit of the nonce 13 | * @return nonce a full nonce to pass for next UserOp with this sender. 14 | */ 15 | function getNonce(address sender, uint192 key) 16 | external view returns (uint256 nonce); 17 | 18 | /** 19 | * Manually increment the nonce of the sender. 20 | * This method is exposed just for completeness.. 21 | * Account does NOT need to call it, neither during validation, nor elsewhere, 22 | * as the EntryPoint will update the nonce regardless. 23 | * Possible use-case is call it with various keys to "initialize" their nonces to one, so that future 24 | * UserOperations will not pay extra for the first transaction with a given key. 25 | */ 26 | function incrementNonce(uint192 key) external; 27 | } 28 | -------------------------------------------------------------------------------- /contracts/@eth-infinitism-v0.6/interfaces/IPaymaster.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity ^0.8.12; 3 | 4 | import "./UserOperation.sol"; 5 | 6 | /** 7 | * the interface exposed by a paymaster contract, who agrees to pay the gas for user's operations. 8 | * a paymaster must hold a stake to cover the required entrypoint stake and also the gas for the transaction. 9 | */ 10 | interface IPaymaster { 11 | 12 | enum PostOpMode { 13 | opSucceeded, // user op succeeded 14 | opReverted, // user op reverted. still has to pay for gas. 15 | postOpReverted //user op succeeded, but caused postOp to revert. Now it's a 2nd call, after user's op was deliberately reverted. 16 | } 17 | 18 | /** 19 | * payment validation: check if paymaster agrees to pay. 20 | * Must verify sender is the entryPoint. 21 | * Revert to reject this request. 22 | * Note that bundlers will reject this method if it changes the state, unless the paymaster is trusted (whitelisted) 23 | * The paymaster pre-pays using its deposit, and receive back a refund after the postOp method returns. 24 | * @param userOp the user operation 25 | * @param userOpHash hash of the user's request data. 26 | * @param maxCost the maximum cost of this transaction (based on maximum gas and gas price from userOp) 27 | * @return context value to send to a postOp 28 | * zero length to signify postOp is not required. 29 | * @return validationData signature and time-range of this operation, encoded the same as the return value of validateUserOperation 30 | * <20-byte> sigAuthorizer - 0 for valid signature, 1 to mark signature failure, 31 | * otherwise, an address of an "authorizer" contract. 32 | * <6-byte> validUntil - last timestamp this operation is valid. 0 for "indefinite" 33 | * <6-byte> validAfter - first timestamp this operation is valid 34 | * Note that the validation code cannot use block.timestamp (or block.number) directly. 35 | */ 36 | function validatePaymasterUserOp(UserOperation calldata userOp, bytes32 userOpHash, uint256 maxCost) 37 | external returns (bytes memory context, uint256 validationData); 38 | 39 | /** 40 | * post-operation handler. 41 | * Must verify sender is the entryPoint 42 | * @param mode enum with the following options: 43 | * opSucceeded - user operation succeeded. 44 | * opReverted - user op reverted. still has to pay for gas. 45 | * postOpReverted - user op succeeded, but caused postOp (in mode=opSucceeded) to revert. 46 | * Now this is the 2nd call, after user's op was deliberately reverted. 47 | * @param context - the context value returned by validatePaymasterUserOp 48 | * @param actualGasCost - actual gas used so far (without this postOp call). 49 | */ 50 | function postOp(PostOpMode mode, bytes calldata context, uint256 actualGasCost) external; 51 | } 52 | -------------------------------------------------------------------------------- /contracts/@eth-infinitism-v0.6/interfaces/IStakeManager.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-only 2 | pragma solidity ^0.8.12; 3 | 4 | /** 5 | * manage deposits and stakes. 6 | * deposit is just a balance used to pay for UserOperations (either by a paymaster or an account) 7 | * stake is value locked for at least "unstakeDelay" by the staked entity. 8 | */ 9 | interface IStakeManager { 10 | 11 | event Deposited( 12 | address indexed account, 13 | uint256 totalDeposit 14 | ); 15 | 16 | event Withdrawn( 17 | address indexed account, 18 | address withdrawAddress, 19 | uint256 amount 20 | ); 21 | 22 | /// Emitted when stake or unstake delay are modified 23 | event StakeLocked( 24 | address indexed account, 25 | uint256 totalStaked, 26 | uint256 unstakeDelaySec 27 | ); 28 | 29 | /// Emitted once a stake is scheduled for withdrawal 30 | event StakeUnlocked( 31 | address indexed account, 32 | uint256 withdrawTime 33 | ); 34 | 35 | event StakeWithdrawn( 36 | address indexed account, 37 | address withdrawAddress, 38 | uint256 amount 39 | ); 40 | 41 | /** 42 | * @param deposit the entity's deposit 43 | * @param staked true if this entity is staked. 44 | * @param stake actual amount of ether staked for this entity. 45 | * @param unstakeDelaySec minimum delay to withdraw the stake. 46 | * @param withdrawTime - first block timestamp where 'withdrawStake' will be callable, or zero if already locked 47 | * @dev sizes were chosen so that (deposit,staked, stake) fit into one cell (used during handleOps) 48 | * and the rest fit into a 2nd cell. 49 | * 112 bit allows for 10^15 eth 50 | * 48 bit for full timestamp 51 | * 32 bit allows 150 years for unstake delay 52 | */ 53 | struct DepositInfo { 54 | uint112 deposit; 55 | bool staked; 56 | uint112 stake; 57 | uint32 unstakeDelaySec; 58 | uint48 withdrawTime; 59 | } 60 | 61 | //API struct used by getStakeInfo and simulateValidation 62 | struct StakeInfo { 63 | uint256 stake; 64 | uint256 unstakeDelaySec; 65 | } 66 | 67 | /// @return info - full deposit information of given account 68 | function getDepositInfo(address account) external view returns (DepositInfo memory info); 69 | 70 | /// @return the deposit (for gas payment) of the account 71 | function balanceOf(address account) external view returns (uint256); 72 | 73 | /** 74 | * add to the deposit of the given account 75 | */ 76 | function depositTo(address account) external payable; 77 | 78 | /** 79 | * add to the account's stake - amount and delay 80 | * any pending unstake is first cancelled. 81 | * @param _unstakeDelaySec the new lock duration before the deposit can be withdrawn. 82 | */ 83 | function addStake(uint32 _unstakeDelaySec) external payable; 84 | 85 | /** 86 | * attempt to unlock the stake. 87 | * the value can be withdrawn (using withdrawStake) after the unstake delay. 88 | */ 89 | function unlockStake() external; 90 | 91 | /** 92 | * withdraw from the (unlocked) stake. 93 | * must first call unlockStake and wait for the unstakeDelay to pass 94 | * @param withdrawAddress the address to send withdrawn value. 95 | */ 96 | function withdrawStake(address payable withdrawAddress) external; 97 | 98 | /** 99 | * withdraw from the deposit. 100 | * @param withdrawAddress the address to send withdrawn value. 101 | * @param withdrawAmount the amount to withdraw. 102 | */ 103 | function withdrawTo(address payable withdrawAddress, uint256 withdrawAmount) external; 104 | } 105 | -------------------------------------------------------------------------------- /contracts/@eth-infinitism-v0.6/interfaces/UserOperation.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity ^0.8.12; 3 | 4 | /* solhint-disable no-inline-assembly */ 5 | 6 | import {calldataKeccak} from "../core/Helpers.sol"; 7 | 8 | /** 9 | * User Operation struct 10 | * @param sender the sender account of this request. 11 | * @param nonce unique value the sender uses to verify it is not a replay. 12 | * @param initCode if set, the account contract will be created by this constructor/ 13 | * @param callData the method call to execute on this account. 14 | * @param callGasLimit the gas limit passed to the callData method call. 15 | * @param verificationGasLimit gas used for validateUserOp and validatePaymasterUserOp. 16 | * @param preVerificationGas gas not calculated by the handleOps method, but added to the gas paid. Covers batch overhead. 17 | * @param maxFeePerGas same as EIP-1559 gas parameter. 18 | * @param maxPriorityFeePerGas same as EIP-1559 gas parameter. 19 | * @param paymasterAndData if set, this field holds the paymaster address and paymaster-specific data. the paymaster will pay for the transaction instead of the sender. 20 | * @param signature sender-verified signature over the entire request, the EntryPoint address and the chain ID. 21 | */ 22 | struct UserOperation { 23 | 24 | address sender; 25 | uint256 nonce; 26 | bytes initCode; 27 | bytes callData; 28 | uint256 callGasLimit; 29 | uint256 verificationGasLimit; 30 | uint256 preVerificationGas; 31 | uint256 maxFeePerGas; 32 | uint256 maxPriorityFeePerGas; 33 | bytes paymasterAndData; 34 | bytes signature; 35 | } 36 | 37 | /** 38 | * Utility functions helpful when working with UserOperation structs. 39 | */ 40 | library UserOperationLib { 41 | 42 | function getSender(UserOperation calldata userOp) internal pure returns (address) { 43 | address data; 44 | //read sender from userOp, which is first userOp member (saves 800 gas...) 45 | assembly {data := calldataload(userOp)} 46 | return address(uint160(data)); 47 | } 48 | 49 | //relayer/block builder might submit the TX with higher priorityFee, but the user should not 50 | // pay above what he signed for. 51 | function gasPrice(UserOperation calldata userOp) internal view returns (uint256) { 52 | unchecked { 53 | uint256 maxFeePerGas = userOp.maxFeePerGas; 54 | uint256 maxPriorityFeePerGas = userOp.maxPriorityFeePerGas; 55 | if (maxFeePerGas == maxPriorityFeePerGas) { 56 | //legacy mode (for networks that don't support basefee opcode) 57 | return maxFeePerGas; 58 | } 59 | return min(maxFeePerGas, maxPriorityFeePerGas + block.basefee); 60 | } 61 | } 62 | 63 | function pack(UserOperation calldata userOp) internal pure returns (bytes memory ret) { 64 | address sender = getSender(userOp); 65 | uint256 nonce = userOp.nonce; 66 | bytes32 hashInitCode = calldataKeccak(userOp.initCode); 67 | bytes32 hashCallData = calldataKeccak(userOp.callData); 68 | uint256 callGasLimit = userOp.callGasLimit; 69 | uint256 verificationGasLimit = userOp.verificationGasLimit; 70 | uint256 preVerificationGas = userOp.preVerificationGas; 71 | uint256 maxFeePerGas = userOp.maxFeePerGas; 72 | uint256 maxPriorityFeePerGas = userOp.maxPriorityFeePerGas; 73 | bytes32 hashPaymasterAndData = calldataKeccak(userOp.paymasterAndData); 74 | 75 | return abi.encode( 76 | sender, nonce, 77 | hashInitCode, hashCallData, 78 | callGasLimit, verificationGasLimit, preVerificationGas, 79 | maxFeePerGas, maxPriorityFeePerGas, 80 | hashPaymasterAndData 81 | ); 82 | } 83 | 84 | function hash(UserOperation calldata userOp) internal pure returns (bytes32) { 85 | return keccak256(pack(userOp)); 86 | } 87 | 88 | function min(uint256 a, uint256 b) internal pure returns (uint256) { 89 | return a < b ? a : b; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /contracts/@eth-infinitism-v0.6/utils/Exec.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0-only 2 | pragma solidity >=0.7.5 <0.9.0; 3 | 4 | // solhint-disable no-inline-assembly 5 | 6 | /** 7 | * Utility functions helpful when making different kinds of contract calls in Solidity. 8 | */ 9 | library Exec { 10 | function call( 11 | address to, 12 | uint256 value, 13 | bytes memory data, 14 | uint256 txGas 15 | ) internal returns (bool success) { 16 | assembly { 17 | success := call( 18 | txGas, 19 | to, 20 | value, 21 | add(data, 0x20), 22 | mload(data), 23 | 0, 24 | 0 25 | ) 26 | } 27 | } 28 | 29 | function staticcall( 30 | address to, 31 | bytes memory data, 32 | uint256 txGas 33 | ) internal view returns (bool success) { 34 | assembly { 35 | success := staticcall(txGas, to, add(data, 0x20), mload(data), 0, 0) 36 | } 37 | } 38 | 39 | function delegateCall( 40 | address to, 41 | bytes memory data, 42 | uint256 txGas 43 | ) internal returns (bool success) { 44 | assembly { 45 | success := delegatecall( 46 | txGas, 47 | to, 48 | add(data, 0x20), 49 | mload(data), 50 | 0, 51 | 0 52 | ) 53 | } 54 | } 55 | 56 | // get returned data from last call or calldelegate 57 | function getReturnData( 58 | uint256 maxLen 59 | ) internal pure returns (bytes memory returnData) { 60 | assembly { 61 | let len := returndatasize() 62 | if gt(len, maxLen) { 63 | len := maxLen 64 | } 65 | let ptr := mload(0x40) 66 | mstore(0x40, add(ptr, add(len, 0x20))) 67 | mstore(ptr, len) 68 | returndatacopy(add(ptr, 0x20), 0, len) 69 | returnData := ptr 70 | } 71 | } 72 | 73 | // revert with explicit byte array (probably reverted info from call) 74 | function revertWithData(bytes memory returnData) internal pure { 75 | assembly { 76 | revert(add(returnData, 32), mload(returnData)) 77 | } 78 | } 79 | 80 | function callAndRevert( 81 | address to, 82 | bytes memory data, 83 | uint256 maxLen 84 | ) internal { 85 | bool success = call(to, 0, data, gasleft()); 86 | if (!success) { 87 | revertWithData(getReturnData(maxLen)); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /contracts/core/Validations.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity ^0.8.12; 3 | import "@openzeppelin/contracts/access/Ownable.sol"; 4 | import "../interfaces/IValidations.sol"; 5 | 6 | contract Validations is Ownable, IValidations { 7 | bool public unrestrictedBundler; 8 | bool public unrestrictedModule; 9 | 10 | address public walletProxyFactory; 11 | 12 | mapping(address => bool) public officialBundlerWhiteList; 13 | mapping(address => bool) public moduleWhiteList; 14 | 15 | constructor(address _owner) { 16 | _transferOwnership(_owner); 17 | } 18 | 19 | function setUnrestrictedBundler(bool allowed) public onlyOwner { 20 | unrestrictedBundler = allowed; 21 | emit UnrestrictedBundlerSet(allowed); 22 | } 23 | 24 | function setUnrestrictedModule(bool allowed) public onlyOwner { 25 | unrestrictedModule = allowed; 26 | emit UnrestrictedModuleSet(allowed); 27 | } 28 | 29 | function setBundlerOfficialWhitelistBatch( 30 | address[] memory bundler, 31 | bool[] memory allowed 32 | ) public onlyOwner { 33 | uint256 length = bundler.length; 34 | require(length == allowed.length, "incorrect arrary length"); 35 | 36 | for (uint i = 0; i < length; i++) { 37 | officialBundlerWhiteList[bundler[i]] = allowed[i]; 38 | emit BundlerWhitelistSet(bundler[i], allowed[i]); 39 | } 40 | } 41 | 42 | function setBundlerOfficialWhitelist( 43 | address bundler, 44 | bool allowed 45 | ) public onlyOwner { 46 | officialBundlerWhiteList[bundler] = allowed; 47 | emit BundlerWhitelistSet(bundler, allowed); 48 | } 49 | 50 | function setWalletProxyFactoryWhitelist( 51 | address walletFactory 52 | ) public onlyOwner { 53 | require(walletProxyFactory == address(0), "already set"); 54 | walletProxyFactory = walletFactory; 55 | emit WalletFactoryWhitelistSet(walletFactory); 56 | } 57 | 58 | function setModuleWhitelist(address module, bool allowed) public onlyOwner { 59 | moduleWhiteList[module] = allowed; 60 | emit ModuleWhitelistSet(module, allowed); 61 | } 62 | 63 | function validateModuleWhitelist(address module) public view { 64 | if (!moduleWhiteList[module]) { 65 | require(unrestrictedModule, "not allowed module"); 66 | } 67 | } 68 | 69 | function validateBundlerWhiteList(address bundler) public view { 70 | if (!officialBundlerWhiteList[bundler]) { 71 | require( 72 | unrestrictedBundler && bundler == tx.origin, 73 | "called by illegal bundler" 74 | ); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /contracts/helper/BundlerDepositHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity ^0.8.12; 3 | 4 | import "@openzeppelin/contracts/access/Ownable.sol"; 5 | import "../interfaces/IValidations.sol"; 6 | import "../@eth-infinitism-v0.6/interfaces/IStakeManager.sol"; 7 | 8 | contract BundlerDepositHelper is Ownable { 9 | mapping(address => bool) public vaildEntryPoint; 10 | address public immutable validations; 11 | 12 | constructor(address _owner, address _validations) { 13 | _transferOwnership(_owner); 14 | validations = _validations; 15 | } 16 | 17 | function setValidEntryPoint( 18 | address entryPoint, 19 | bool isValid 20 | ) public onlyOwner { 21 | vaildEntryPoint[entryPoint] = isValid; 22 | } 23 | 24 | function batchDepositForBundler( 25 | address entryPoint, 26 | address[] memory bundlers, 27 | uint256[] memory amounts 28 | ) public payable { 29 | uint256 loopLength = bundlers.length; 30 | 31 | require( 32 | vaildEntryPoint[entryPoint], 33 | "BundlerDepositHelper: Invalid EntryPoint" 34 | ); 35 | require( 36 | loopLength == amounts.length, 37 | "BundlerDepositHelper: Invalid input" 38 | ); 39 | 40 | for (uint256 i = 0; i < loopLength; i++) { 41 | address bundler = bundlers[i]; 42 | uint256 amount = amounts[i]; 43 | 44 | require( 45 | IValidations(validations).officialBundlerWhiteList(bundler), 46 | "BundlerDepositHelper: Invalid bundler" 47 | ); 48 | 49 | payable(bundler).transfer(amount); 50 | } 51 | 52 | require( 53 | address(this).balance == 0, 54 | "BundlerDepositHelper: Invalid value" 55 | ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /contracts/helper/DeployFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity ^0.8.12; 3 | import "@openzeppelin/contracts/utils/Create2.sol"; 4 | 5 | contract DeployFactory { 6 | /** 7 | * @notice Deploys `_initCode` using `_salt` for defining the deterministic address. 8 | * @param _initCode Initialization code. 9 | * @param _salt Arbitrary value to modify resulting address. 10 | * @return createdContract Created contract address. 11 | */ 12 | function deploy( 13 | bytes memory _initCode, 14 | bytes32 _salt 15 | ) public returns (address payable createdContract) { 16 | address addr = getAddress(_initCode, _salt); 17 | uint256 codeSize = addr.code.length; 18 | if (codeSize > 0) { 19 | return payable(addr); 20 | } 21 | 22 | assembly { 23 | createdContract := create2( 24 | 0, 25 | add(_initCode, 0x20), 26 | mload(_initCode), 27 | _salt 28 | ) 29 | if iszero(extcodesize(createdContract)) { 30 | revert(0, 0) 31 | } 32 | } 33 | } 34 | 35 | function getAddress( 36 | bytes memory _initCode, 37 | bytes32 _salt 38 | ) public view returns (address) { 39 | return 40 | Create2.computeAddress(_salt, keccak256(_initCode), address(this)); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /contracts/helper/HandleOpsHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity ^0.8.12; 3 | 4 | import "../@eth-infinitism-v0.6/core/EntryPoint.sol"; 5 | 6 | contract HandleOpsHelper { 7 | EntryPoint public immutable ENTRYPOINT; 8 | 9 | constructor(address payable _entryPoint) { 10 | ENTRYPOINT = EntryPoint(_entryPoint); 11 | } 12 | 13 | function handleOps(UserOperation[] calldata ops) public { 14 | ENTRYPOINT.handleOps(ops, payable(msg.sender)); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /contracts/interfaces/ERC1155TokenReceiver.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0-only 2 | pragma solidity ^0.8.12; 3 | 4 | /** 5 | Note: The ERC-165 identifier for this interface is 0x4e2312e0. 6 | */ 7 | interface ERC1155TokenReceiver { 8 | /** 9 | @notice Handle the receipt of a single ERC1155 token type. 10 | @dev An ERC1155-compliant smart contract MUST call this function on the token recipient contract, at the end of a `safeTransferFrom` after the balance has been updated. 11 | This function MUST return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` (i.e. 0xf23a6e61) if it accepts the transfer. 12 | This function MUST revert if it rejects the transfer. 13 | Return of any other value than the prescribed keccak256 generated value MUST result in the transaction being reverted by the caller. 14 | @param _operator The address which initiated the transfer (i.e. msg.sender) 15 | @param _from The address which previously owned the token 16 | @param _id The ID of the token being transferred 17 | @param _value The amount of tokens being transferred 18 | @param _data Additional data with no specified format 19 | @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` 20 | */ 21 | function onERC1155Received( 22 | address _operator, 23 | address _from, 24 | uint256 _id, 25 | uint256 _value, 26 | bytes calldata _data 27 | ) external returns (bytes4); 28 | 29 | /** 30 | @notice Handle the receipt of multiple ERC1155 token types. 31 | @dev An ERC1155-compliant smart contract MUST call this function on the token recipient contract, at the end of a `safeBatchTransferFrom` after the balances have been updated. 32 | This function MUST return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` (i.e. 0xbc197c81) if it accepts the transfer(s). 33 | This function MUST revert if it rejects the transfer(s). 34 | Return of any other value than the prescribed keccak256 generated value MUST result in the transaction being reverted by the caller. 35 | @param _operator The address which initiated the batch transfer (i.e. msg.sender) 36 | @param _from The address which previously owned the token 37 | @param _ids An array containing ids of each token being transferred (order and length must match _values array) 38 | @param _values An array containing amounts of each token being transferred (order and length must match _ids array) 39 | @param _data Additional data with no specified format 40 | @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` 41 | */ 42 | function onERC1155BatchReceived( 43 | address _operator, 44 | address _from, 45 | uint256[] calldata _ids, 46 | uint256[] calldata _values, 47 | bytes calldata _data 48 | ) external returns (bytes4); 49 | } 50 | -------------------------------------------------------------------------------- /contracts/interfaces/ERC721TokenReceiver.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0-only 2 | pragma solidity ^0.8.12; 3 | 4 | /// @dev Note: the ERC-165 identifier for this interface is 0x150b7a02. 5 | interface ERC721TokenReceiver { 6 | /// @notice Handle the receipt of an NFT 7 | /// @dev The ERC721 smart contract calls this function on the recipient 8 | /// after a `transfer`. This function MAY throw to revert and reject the 9 | /// transfer. Return of other than the magic value MUST result in the 10 | /// transaction being reverted. 11 | /// Note: the contract address is always the message sender. 12 | /// @param _operator The address which called `safeTransferFrom` function 13 | /// @param _from The address which previously owned the token 14 | /// @param _tokenId The NFT identifier which is being transferred 15 | /// @param _data Additional data with no specified format 16 | /// @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` 17 | /// unless throwing 18 | function onERC721Received( 19 | address _operator, 20 | address _from, 21 | uint256 _tokenId, 22 | bytes calldata _data 23 | ) external returns (bytes4); 24 | } 25 | -------------------------------------------------------------------------------- /contracts/interfaces/ERC777TokensRecipient.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0-only 2 | pragma solidity ^0.8.12; 3 | 4 | interface ERC777TokensRecipient { 5 | function tokensReceived( 6 | address operator, 7 | address from, 8 | address to, 9 | uint256 amount, 10 | bytes calldata data, 11 | bytes calldata operatorData 12 | ) external; 13 | } 14 | -------------------------------------------------------------------------------- /contracts/interfaces/IERC165.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0-only 2 | pragma solidity ^0.8.12; 3 | 4 | /// @notice More details at https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/introspection/IERC165.sol 5 | interface IERC165 { 6 | /** 7 | * @dev Returns true if this contract implements the interface defined by 8 | * `interfaceId`. See the corresponding 9 | * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] 10 | * to learn more about how these ids are created. 11 | * 12 | * This function call must use less than 30 000 gas. 13 | */ 14 | function supportsInterface(bytes4 interfaceId) external view returns (bool); 15 | } 16 | -------------------------------------------------------------------------------- /contracts/interfaces/IExOraclePriceData.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0-only 2 | pragma solidity ^0.8.12; 3 | 4 | interface IExOraclePriceData { 5 | function latestRoundData( 6 | string calldata priceType, 7 | address dataSource 8 | ) 9 | external 10 | view 11 | returns ( 12 | uint80 roundId, 13 | int256 answer, 14 | uint256 startedAt, 15 | uint256 updatedAt, 16 | uint80 answeredInRound 17 | ); 18 | 19 | function get( 20 | string calldata priceType, 21 | address source 22 | ) external view returns (uint256 price, uint256 timestamp); 23 | 24 | function getOffchain( 25 | string calldata priceType, 26 | address source 27 | ) external view returns (uint256 price, uint256 timestamp); 28 | 29 | function getCumulativePrice( 30 | string calldata priceType, 31 | address source 32 | ) external view returns (uint256 cumulativePrice, uint32 timestamp); 33 | 34 | function lastResponseTime(address source) external view returns (uint256); 35 | } 36 | -------------------------------------------------------------------------------- /contracts/interfaces/IFreeGasPaymaster.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity ^0.8.12; 3 | 4 | import "../@eth-infinitism-v0.6/interfaces/IPaymaster.sol"; 5 | 6 | interface IFreeGasPaymaster is IPaymaster { 7 | event AddedToWhitelist(address indexed account); 8 | event RemovedFromWhitelist(address indexed account); 9 | event Withdrawal(address indexed token, uint256 amount); 10 | } 11 | -------------------------------------------------------------------------------- /contracts/interfaces/ILBRouter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | 3 | pragma solidity ^0.8.10; 4 | 5 | interface ILBRouter { 6 | function wavax() external view returns (address); 7 | 8 | function swapExactTokensForAVAX( 9 | uint256 amountIn, 10 | uint256 amountOutMinAVAX, 11 | uint256[] memory pairBinSteps, 12 | address[] memory tokenPath, 13 | address payable to, 14 | uint256 deadline 15 | ) external returns (uint256 amountOut); 16 | } 17 | -------------------------------------------------------------------------------- /contracts/interfaces/IPriceOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.12; 3 | import "@openzeppelin/contracts/access/Ownable.sol"; 4 | import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; 5 | 6 | interface IPriceOracle { 7 | function exchangePrice( 8 | address _token 9 | ) external view returns (uint256 price, uint8 decimals); 10 | 11 | function exchangeRate( 12 | address token 13 | ) external view returns (uint256 exchangeRate); 14 | 15 | function getValueOf( 16 | address tokenIn, 17 | address quote, 18 | uint256 amountIn 19 | ) external view returns (uint256 value); 20 | } 21 | 22 | abstract contract PriceOracle is IPriceOracle, Ownable { 23 | mapping(address => address) internal priceFeed; 24 | mapping(address => uint256) internal decimals; 25 | address public constant NATIVE_TOKEN = 26 | 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; 27 | 28 | constructor(address _owner) { 29 | _transferOwnership(_owner); 30 | } 31 | 32 | function setPriceFeed( 33 | address token, 34 | address aggregator 35 | ) external onlyOwner { 36 | require(aggregator != address(0), "Invalid aggregator address"); 37 | priceFeed[token] = aggregator; 38 | } 39 | 40 | function exchangePrice( 41 | address token 42 | ) public view virtual override returns (uint256 price, uint8 decimals); 43 | 44 | function exchangeRate( 45 | address token 46 | ) external view virtual override returns (uint256 price) { 47 | price = getValueOf(NATIVE_TOKEN, token, 1 ether); 48 | require(price != 0, "Price Oracle: Price is 0"); 49 | } 50 | 51 | function getValueOf( 52 | address tokenIn, 53 | address quote, 54 | uint256 amountIn 55 | ) public view virtual override returns (uint256 value) { 56 | (uint256 priceIn, uint8 decimalsIn) = exchangePrice(tokenIn); 57 | (uint256 priceQuote, uint8 decimalsQuote) = exchangePrice(quote); 58 | 59 | if ( 60 | decimalsIn + tokenDecimals(tokenIn) > 61 | decimalsQuote + tokenDecimals(quote) 62 | ) { 63 | value = 64 | (amountIn * priceIn) / 65 | (priceQuote * 66 | 10 ** 67 | (decimalsIn + 68 | tokenDecimals(tokenIn) - 69 | (tokenDecimals(quote) + decimalsQuote))); 70 | } else { 71 | value = 72 | ((amountIn * priceIn) * 73 | 10 ** 74 | (decimalsQuote + 75 | tokenDecimals(quote) - 76 | (tokenDecimals(tokenIn) + decimalsIn))) / 77 | priceQuote; 78 | } 79 | } 80 | 81 | function tokenDecimals(address _token) public view returns (uint256) { 82 | return 83 | decimals[_token] == 0 84 | ? IERC20Metadata(_token).decimals() 85 | : decimals[_token]; 86 | } 87 | 88 | function setDecimals(address _token, uint256 _decimals) external onlyOwner { 89 | decimals[_token] = _decimals; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /contracts/interfaces/ISmartAccountProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity ^0.8.12; 3 | 4 | /** 5 | * A wrapper factory contract to deploy SmartAccount as an Account-Abstraction wallet contract. 6 | */ 7 | interface ISmartAccountProxy { 8 | function masterCopy() external view returns (address); 9 | } 10 | -------------------------------------------------------------------------------- /contracts/interfaces/ISmartAccountProxyFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity ^0.8.12; 3 | 4 | interface ISmartAccountProxyFactory { 5 | function safeSingleton(address singleton) external view returns (bool); 6 | 7 | // function walletWhiteList(address wallet) external view returns (bool); 8 | } 9 | -------------------------------------------------------------------------------- /contracts/interfaces/ISwapAdapter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | 3 | pragma solidity ^0.8.12; 4 | 5 | interface ISwapAdapter { 6 | function swapToNative( 7 | address tokenIn, 8 | uint256 minAmountOut 9 | ) external returns (uint256 amountOut); 10 | 11 | function nativeToken() external view returns (address); 12 | } 13 | -------------------------------------------------------------------------------- /contracts/interfaces/ISwapRouter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity ^0.8.12; 3 | 4 | interface ISwapRouter { 5 | function factory() external pure returns (address); 6 | 7 | function WOKT() external pure returns (address); 8 | 9 | function pairCodeHash() external pure returns (bytes32); 10 | 11 | function addLiquidity( 12 | address tokenA, 13 | address tokenB, 14 | uint amountADesired, 15 | uint amountBDesired, 16 | uint amountAMin, 17 | uint amountBMin, 18 | address to, 19 | uint deadline 20 | ) external returns (uint amountA, uint amountB, uint liquidity); 21 | 22 | function addLiquidityOKT( 23 | address token, 24 | uint amountTokenDesired, 25 | uint amountTokenMin, 26 | uint amountOKTMin, 27 | address to, 28 | uint deadline 29 | ) 30 | external 31 | payable 32 | returns (uint amountToken, uint amountOKT, uint liquidity); 33 | 34 | function removeLiquidity( 35 | address tokenA, 36 | address tokenB, 37 | uint liquidity, 38 | uint amountAMin, 39 | uint amountBMin, 40 | address to, 41 | uint deadline 42 | ) external returns (uint amountA, uint amountB); 43 | 44 | function removeLiquidityOKT( 45 | address token, 46 | uint liquidity, 47 | uint amountTokenMin, 48 | uint amountOKTMin, 49 | address to, 50 | uint deadline 51 | ) external returns (uint amountToken, uint amountOKT); 52 | 53 | function removeLiquidityWithPermit( 54 | address tokenA, 55 | address tokenB, 56 | uint liquidity, 57 | uint amountAMin, 58 | uint amountBMin, 59 | address to, 60 | uint deadline, 61 | bool approveMax, 62 | uint8 v, 63 | bytes32 r, 64 | bytes32 s 65 | ) external returns (uint amountA, uint amountB); 66 | 67 | function removeLiquidityOKTWithPermit( 68 | address token, 69 | uint liquidity, 70 | uint amountTokenMin, 71 | uint amountOKTMin, 72 | address to, 73 | uint deadline, 74 | bool approveMax, 75 | uint8 v, 76 | bytes32 r, 77 | bytes32 s 78 | ) external returns (uint amountToken, uint amountOKT); 79 | 80 | function swapExactTokensForTokens( 81 | uint amountIn, 82 | uint amountOutMin, 83 | address[] calldata path, 84 | address to, 85 | uint deadline 86 | ) external returns (uint[] memory amounts); 87 | 88 | function swapTokensForExactTokens( 89 | uint amountOut, 90 | uint amountInMax, 91 | address[] calldata path, 92 | address to, 93 | uint deadline 94 | ) external returns (uint[] memory amounts); 95 | 96 | function swapExactOKTForTokens( 97 | uint amountOutMin, 98 | address[] calldata path, 99 | address to, 100 | uint deadline 101 | ) external payable returns (uint[] memory amounts); 102 | 103 | function swapTokensForExactOKT( 104 | uint amountOut, 105 | uint amountInMax, 106 | address[] calldata path, 107 | address to, 108 | uint deadline 109 | ) external returns (uint[] memory amounts); 110 | 111 | function swapExactTokensForOKT( 112 | uint amountIn, 113 | uint amountOutMin, 114 | address[] calldata path, 115 | address to, 116 | uint deadline 117 | ) external returns (uint[] memory amounts); 118 | 119 | function swapOKTForExactTokens( 120 | uint amountOut, 121 | address[] calldata path, 122 | address to, 123 | uint deadline 124 | ) external payable returns (uint[] memory amounts); 125 | 126 | function quote( 127 | uint amountA, 128 | uint reserveA, 129 | uint reserveB 130 | ) external pure returns (uint amountB); 131 | 132 | function getAmountOut( 133 | uint amountIn, 134 | uint reserveIn, 135 | uint reserveOut 136 | ) external pure returns (uint amountOut); 137 | 138 | function getAmountIn( 139 | uint amountOut, 140 | uint reserveIn, 141 | uint reserveOut 142 | ) external pure returns (uint amountIn); 143 | 144 | function getAmountsOut( 145 | uint amountIn, 146 | address[] calldata path 147 | ) external view returns (uint[] memory amounts); 148 | 149 | function getAmountsIn( 150 | uint amountOut, 151 | address[] calldata path 152 | ) external view returns (uint[] memory amounts); 153 | } 154 | -------------------------------------------------------------------------------- /contracts/interfaces/ITokenPaymaster.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity ^0.8.12; 3 | 4 | import "../@eth-infinitism-v0.6/interfaces/IPaymaster.sol"; 5 | 6 | interface ITokenPaymaster is IPaymaster { 7 | event TokenPriceLimitMaxSet(address indexed token, uint256 price); 8 | event TokenPriceLimitMinSet(address indexed token, uint256 price); 9 | event SlippageSet(address indexed token, uint256 slippage); 10 | event SwapAdapterSet(address indexed swapAdapter); 11 | event AddedToWhitelist(address indexed account); 12 | event RemovedFromWhitelist(address indexed account); 13 | event Withdrawal(address indexed token, uint256 amount); 14 | event PriceOracleUpdated(address indexed priceOracle); 15 | event SwappedToNative( 16 | address token, 17 | uint256 amountSwapped, 18 | uint256 amountDeposited 19 | ); 20 | 21 | event TokenCost( 22 | bytes32 indexed userOpHash, 23 | address indexed sender, 24 | address indexed token, 25 | uint256 ERC20Cost, 26 | uint256 gasCost 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /contracts/interfaces/IValidations.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity ^0.8.12; 3 | 4 | interface IValidations { 5 | struct bundlerInformation { 6 | address bundler; 7 | uint256 registeTime; 8 | } 9 | event UnrestrictedBundlerSet(bool allowed); 10 | event UnrestrictedModuleSet(bool allowed); 11 | event WalletFactoryWhitelistSet(address walletProxyFactory); 12 | event BundlerWhitelistSet(address indexed bundler, bool allowed); 13 | event ModuleWhitelistSet(address indexed module, bool allowed); 14 | 15 | function officialBundlerWhiteList( 16 | address bundler 17 | ) external view returns (bool); 18 | 19 | function moduleWhiteList(address module) external view returns (bool); 20 | 21 | function setUnrestrictedBundler(bool allowed) external; 22 | 23 | function setUnrestrictedModule(bool allowed) external; 24 | 25 | function setBundlerOfficialWhitelist( 26 | address bundler, 27 | bool allowed 28 | ) external; 29 | 30 | function setWalletProxyFactoryWhitelist(address walletFactory) external; 31 | 32 | function setModuleWhitelist(address module, bool allowed) external; 33 | 34 | function validateBundlerWhiteList(address bundler) external view; 35 | 36 | function validateModuleWhitelist(address module) external; 37 | } 38 | -------------------------------------------------------------------------------- /contracts/interfaces/IWETH9.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | pragma solidity ^0.8.12; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | 6 | /// @title Interface for WETH9 7 | interface IWETH9 is IERC20 { 8 | /// @notice Deposit ether to get wrapped ether 9 | function deposit() external payable; 10 | 11 | /// @notice Withdraw wrapped ether to get ether 12 | function withdraw(uint256) external; 13 | } 14 | -------------------------------------------------------------------------------- /contracts/mock/MockChainlinkOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.12; 3 | import "@openzeppelin/contracts/access/Ownable.sol"; 4 | 5 | contract MockChainlinkOracle is Ownable { 6 | int256 public price; 7 | uint8 public decimals; 8 | 9 | constructor(address owner) { 10 | _transferOwnership(owner); 11 | } 12 | 13 | function setPrice(int256 _price) external onlyOwner { 14 | price = _price; 15 | } 16 | 17 | function setDecimals(uint8 _decimals) external onlyOwner { 18 | decimals = _decimals; 19 | } 20 | 21 | function latestRoundData() 22 | external 23 | view 24 | returns ( 25 | uint80 roundId, 26 | int256 answer, 27 | uint256 startedAt, 28 | uint256 updatedAt, 29 | uint80 answeredInRound 30 | ) 31 | { 32 | return (0, price, 0, block.timestamp, 0); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /contracts/mock/MockEntryPointL1.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity ^0.8.12; 3 | 4 | /* solhint-disable avoid-low-level-calls */ 5 | /* solhint-disable no-inline-assembly */ 6 | 7 | import "../@eth-infinitism-v0.6/core/EntryPoint.sol"; 8 | 9 | contract MockEntryPointL1 is EntryPoint { 10 | constructor() EntryPoint() {} 11 | 12 | event CreatedSenderAddress(address sender); 13 | 14 | function incrementNonceForTarget(address target, uint192 key) public { 15 | nonceSequenceNumber[target][key]++; 16 | } 17 | 18 | function mockhandleOps(UserOperation[] calldata ops) public { 19 | handleOps(ops, payable(msg.sender)); 20 | } 21 | 22 | function mockhandleOpsNoRevert(UserOperation[] calldata ops) public { 23 | handleOps(ops, payable(msg.sender)); 24 | } 25 | 26 | function mockhandleAggregatedOps( 27 | UserOpsPerAggregator[] calldata ops 28 | ) public { 29 | handleAggregatedOps(ops, payable(msg.sender)); 30 | } 31 | 32 | function getValidationData( 33 | uint256 validationData 34 | ) public view returns (address aggregator, bool outOfTimeRange) { 35 | if (validationData == 0) { 36 | return (address(0), false); 37 | } 38 | ValidationData memory data = _parseValidationData(validationData); 39 | // solhint-disable-next-line not-rely-on-time 40 | outOfTimeRange = 41 | block.timestamp > data.validUntil || 42 | block.timestamp < data.validAfter; 43 | aggregator = data.aggregator; 44 | } 45 | 46 | function getSenderAddressByEvent(bytes calldata initCode) external { 47 | try this.getSenderAddress(initCode) {} catch (bytes memory data) { 48 | uint revertdata; 49 | assembly { 50 | let pos := data 51 | revertdata := mload(add(add(pos, 0x20), 0x04)) 52 | } 53 | emit CreatedSenderAddress(address(uint160(revertdata))); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /contracts/mock/MockEntryPointV06.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity ^0.8.12; 3 | 4 | /* solhint-disable avoid-low-level-calls */ 5 | /* solhint-disable no-inline-assembly */ 6 | 7 | import "../@eth-infinitism-v0.6/core/EntryPoint.sol"; 8 | 9 | contract MockEntryPointV06 is EntryPoint { 10 | constructor() EntryPoint() {} 11 | 12 | event CreatedSenderAddress(address sender); 13 | 14 | function incrementNonceForTarget(address target, uint192 key) public { 15 | nonceSequenceNumber[target][key]++; 16 | } 17 | 18 | function mockhandleOps(UserOperation[] calldata ops) public { 19 | handleOps(ops, payable(msg.sender)); 20 | } 21 | 22 | function mockhandleOpsNoRevert(UserOperation[] calldata ops) public { 23 | handleOps(ops, payable(msg.sender)); 24 | } 25 | 26 | function mockhandleAggregatedOps( 27 | UserOpsPerAggregator[] calldata ops 28 | ) public { 29 | handleAggregatedOps(ops, payable(msg.sender)); 30 | } 31 | 32 | function getValidationData( 33 | uint256 validationData 34 | ) public view returns (address aggregator, bool outOfTimeRange) { 35 | if (validationData == 0) { 36 | return (address(0), false); 37 | } 38 | ValidationData memory data = _parseValidationData(validationData); 39 | // solhint-disable-next-line not-rely-on-time 40 | outOfTimeRange = 41 | block.timestamp > data.validUntil || 42 | block.timestamp < data.validAfter; 43 | aggregator = data.aggregator; 44 | } 45 | 46 | function getSenderAddressByEvent(bytes calldata initCode) external { 47 | try this.getSenderAddress(initCode) {} catch (bytes memory data) { 48 | uint revertdata; 49 | assembly { 50 | let pos := data 51 | revertdata := mload(add(add(pos, 0x20), 0x04)) 52 | } 53 | emit CreatedSenderAddress(address(uint160(revertdata))); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /contracts/mock/MockGuard.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.12; 3 | import "../wallet/common/Enum.sol"; 4 | 5 | contract MockGuard { 6 | function checkTransaction( 7 | address to, 8 | uint256 value, 9 | bytes memory data, 10 | Enum.Operation operation 11 | ) external {} 12 | 13 | function checkAfterExecution(bool success) external {} 14 | } 15 | -------------------------------------------------------------------------------- /contracts/mock/MockModule.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.12; 3 | import "../wallet/common/Enum.sol"; 4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | 6 | interface ISmartAccount { 7 | function execTransactionFromModule( 8 | address to, 9 | uint256 value, 10 | bytes calldata data, 11 | Enum.Operation operation 12 | ) external; 13 | } 14 | 15 | contract MockModule { 16 | ISmartAccount public aa; 17 | 18 | constructor(address aaAccount) { 19 | aa = ISmartAccount(aaAccount); 20 | } 21 | 22 | function execTransaction( 23 | address to, 24 | uint256 value, 25 | bytes calldata data, 26 | Enum.Operation operation 27 | ) external { 28 | aa.execTransactionFromModule(to, value, data, operation); 29 | } 30 | 31 | function transferToken(address token, address to, uint256 amount) public { 32 | IERC20(token).transfer(to, amount); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /contracts/mock/MockOKCSwap.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity ^0.8.12; 3 | 4 | import "@uniswap/lib/contracts/libraries/TransferHelper.sol"; 5 | 6 | contract MockOKCSwapRouter { 7 | address public immutable WOKT; 8 | 9 | modifier ensure(uint deadline) { 10 | require(deadline >= block.timestamp, "UniswapV2Router: EXPIRED"); 11 | _; 12 | } 13 | 14 | receive() external payable {} 15 | 16 | constructor(address _WOKT) { 17 | WOKT = _WOKT; 18 | } 19 | 20 | function swapExactTokensForOKT( 21 | uint amountIn, 22 | uint, 23 | address[] calldata path, 24 | address to, 25 | uint deadline 26 | ) external virtual ensure(deadline) returns (uint[] memory amounts) { 27 | require(path[path.length - 1] == WOKT, "OKVSwap: INVALID_PATH"); 28 | 29 | TransferHelper.safeTransferFrom( 30 | path[0], 31 | msg.sender, 32 | address(this), 33 | amountIn 34 | ); 35 | 36 | TransferHelper.safeTransferETH(to, 1e18); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /contracts/mock/MockSignatureManager.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0-only 2 | pragma solidity ^0.8.12; 3 | import "../wallet/base/SignatureManager.sol"; 4 | 5 | 6 | /// mock SignatureManager contract 7 | contract MockSignatureManager is SignatureManager { 8 | constructor( 9 | address _EntryPoint, 10 | string memory _name, 11 | string memory _version 12 | ) SignatureManager(_EntryPoint, _name, _version) {} 13 | 14 | event Validation(uint256 data); 15 | 16 | /// @dev validate uop signature 17 | /// @param userOp uop info and signature 18 | /// @param userOpHash userOp's hash 19 | /// @return validation 20 | function getValidateSignatureReturn( 21 | UserOperation calldata userOp, 22 | bytes32 userOpHash 23 | ) 24 | public 25 | returns (uint256 validation) 26 | { 27 | 28 | validation = _validateSignature( 29 | userOp, 30 | getUOPSignedHash( 31 | SignatureType(uint8(bytes1(userOp.signature[0:1]))), 32 | msg.sender, 33 | userOp 34 | ) 35 | ); 36 | emit Validation(validation); 37 | } 38 | 39 | function changeOwner(address _newOwner) external { 40 | owner = _newOwner; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /contracts/mock/MockTradeJoeV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity ^0.8.12; 3 | 4 | import "@uniswap/lib/contracts/libraries/TransferHelper.sol"; 5 | 6 | contract MockTradeJoeV2Router { 7 | address public immutable wavax; 8 | 9 | modifier ensure(uint deadline) { 10 | require(deadline >= block.timestamp, "UniswapV2Router: EXPIRED"); 11 | _; 12 | } 13 | 14 | receive() external payable {} 15 | 16 | constructor(address _avax) { 17 | wavax = _avax; 18 | } 19 | 20 | function swapExactTokensForAVAX( 21 | uint amountIn, 22 | uint, 23 | uint256[] memory pairBinSteps, 24 | address[] calldata path, 25 | address to, 26 | uint deadline 27 | ) external virtual ensure(deadline) returns (uint[] memory amounts) { 28 | require(path[path.length - 1] == wavax, "TradeJoeV2: INVALID_PATH"); 29 | 30 | TransferHelper.safeTransferFrom( 31 | path[0], 32 | msg.sender, 33 | address(this), 34 | amountIn 35 | ); 36 | 37 | TransferHelper.safeTransferETH(to, 1e18); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /contracts/mock/MockUSDT.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.12; 4 | 5 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 6 | 7 | contract MockUSDT is ERC20 { 8 | constructor() ERC20("USDT", "USDT") {} 9 | 10 | function decimals() public view virtual override returns (uint8) { 11 | return 6; 12 | } 13 | 14 | function mint(address account, uint256 amount) public { 15 | _mint(account, amount); 16 | } 17 | 18 | function transferFrom( 19 | address from, 20 | address to, 21 | uint256 amount 22 | ) public virtual override returns (bool) { 23 | address spender = _msgSender(); 24 | _transfer(from, to, amount); 25 | return true; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /contracts/mock/MockUniswapV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity ^0.8.12; 3 | 4 | import "@uniswap/lib/contracts/libraries/TransferHelper.sol"; 5 | 6 | contract MockUniSwapV2Router { 7 | address public immutable WETH; 8 | 9 | modifier ensure(uint deadline) { 10 | require(deadline >= block.timestamp, "UniswapV2Router: EXPIRED"); 11 | _; 12 | } 13 | 14 | receive() external payable {} 15 | 16 | constructor(address _WETH) { 17 | WETH = _WETH; 18 | } 19 | 20 | function swapExactTokensForETH( 21 | uint amountIn, 22 | uint, 23 | address[] calldata path, 24 | address to, 25 | uint deadline 26 | ) external virtual ensure(deadline) returns (uint[] memory amounts) { 27 | require(path[path.length - 1] == WETH, "UniswapV2Router: INVALID_PATH"); 28 | 29 | TransferHelper.safeTransferFrom( 30 | path[0], 31 | msg.sender, 32 | address(this), 33 | amountIn 34 | ); 35 | 36 | TransferHelper.safeTransferETH(to, 1e18); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /contracts/mock/MockUniswapV3.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity ^0.8.12; 3 | 4 | import "@uniswap/lib/contracts/libraries/TransferHelper.sol"; 5 | import "../interfaces/IWETH9.sol"; 6 | 7 | contract MockUniSwapV3Router { 8 | struct ExactInputSingleParams { 9 | address tokenIn; 10 | address tokenOut; 11 | uint24 fee; 12 | address recipient; 13 | uint256 deadline; 14 | uint256 amountIn; 15 | uint256 amountOutMinimum; 16 | uint160 sqrtPriceLimitX96; 17 | } 18 | 19 | address public immutable WETH9; 20 | 21 | modifier ensure(uint deadline) { 22 | require(deadline >= block.timestamp, "UniswapV2Router: EXPIRED"); 23 | _; 24 | } 25 | 26 | receive() external payable {} 27 | 28 | constructor(address _WETH9) { 29 | WETH9 = _WETH9; 30 | } 31 | 32 | function exactInputSingle( 33 | ExactInputSingleParams calldata params 34 | ) external payable returns (uint256 amountOut) { 35 | TransferHelper.safeTransferFrom( 36 | params.tokenIn, 37 | msg.sender, 38 | address(this), 39 | params.amountIn 40 | ); 41 | 42 | TransferHelper.safeTransfer(WETH9, params.recipient, 1e18); 43 | 44 | amountOut = 1e18; 45 | } 46 | 47 | function deposit() public payable { 48 | IWETH9(WETH9).deposit{value: msg.value}(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /contracts/mock/MockWETH9.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.12; 2 | import "../interfaces/IWETH9.sol"; 3 | 4 | contract WETH9 is IWETH9 { 5 | string public name = "Wrapped Ether"; 6 | string public symbol = "WETH"; 7 | uint8 public decimals = 18; 8 | 9 | event Deposit(address indexed dst, uint wad); 10 | event Withdrawal(address indexed src, uint wad); 11 | 12 | mapping(address => uint) public balanceOf; 13 | mapping(address => mapping(address => uint)) public allowance; 14 | 15 | receive() external payable { 16 | deposit(); 17 | } 18 | 19 | function deposit() public payable { 20 | balanceOf[msg.sender] += msg.value; 21 | emit Deposit(msg.sender, msg.value); 22 | } 23 | 24 | function withdraw(uint wad) public { 25 | require(balanceOf[msg.sender] >= wad, "withdraw too much"); 26 | balanceOf[msg.sender] -= wad; 27 | (bool success, ) = payable(msg.sender).call{value: wad}(""); 28 | require(success, "failed to withdraw"); 29 | emit Withdrawal(msg.sender, wad); 30 | } 31 | 32 | function totalSupply() public view returns (uint) { 33 | return address(this).balance; 34 | } 35 | 36 | function approve(address guy, uint wad) public returns (bool) { 37 | allowance[msg.sender][guy] = wad; 38 | emit Approval(msg.sender, guy, wad); 39 | return true; 40 | } 41 | 42 | function transfer(address dst, uint wad) public returns (bool) { 43 | return transferFrom(msg.sender, dst, wad); 44 | } 45 | 46 | function transferFrom( 47 | address src, 48 | address dst, 49 | uint wad 50 | ) public returns (bool) { 51 | require(balanceOf[src] >= wad); 52 | 53 | if ( 54 | src != msg.sender && allowance[src][msg.sender] != type(uint256).max 55 | ) { 56 | require(allowance[src][msg.sender] >= wad); 57 | allowance[src][msg.sender] -= wad; 58 | } 59 | 60 | balanceOf[src] -= wad; 61 | balanceOf[dst] += wad; 62 | 63 | emit Transfer(src, dst, wad); 64 | 65 | return true; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /contracts/mock/MockWrongDeployFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity ^0.8.12; 3 | 4 | import "@openzeppelin/contracts/utils/Create2.sol"; 5 | import "@openzeppelin/contracts/access/Ownable.sol"; 6 | import "../wallet/v1/SmartAccount.sol"; 7 | import "../wallet/v1/SmartAccountProxy.sol"; 8 | 9 | /** 10 | * A wrapper factory contract to deploy SmartAccount as an Account-Abstraction wallet contract. 11 | */ 12 | contract MockWrongDeployFactory is Ownable { 13 | event ProxyCreation(SmartAccountProxy proxy, address singleton); 14 | event SafeSingletonSet(address safeSingleton, bool value); 15 | 16 | mapping(address => bool) public safeSingleton; 17 | // mapping(address => bool) public walletWhiteList; 18 | 19 | constructor(address _safeSingleton, address _owner) { 20 | safeSingleton[_safeSingleton] = true; 21 | _transferOwnership(_owner); 22 | } 23 | 24 | function setSafeSingleton( 25 | address _safeSingleton, 26 | bool value 27 | ) public onlyOwner { 28 | safeSingleton[_safeSingleton] = value; 29 | emit SafeSingletonSet(_safeSingleton, value); 30 | } 31 | 32 | /// @dev Allows to retrieve the runtime code of a deployed Proxy. This can be used to check that the expected Proxy was deployed. 33 | function proxyRuntimeCode() public pure returns (bytes memory) { 34 | return type(SmartAccountProxy).runtimeCode; 35 | } 36 | 37 | /// @dev Allows to retrieve the creation code used for the Proxy deployment. With this it is easily possible to calculate predicted address. 38 | function proxyCreationCode() public pure returns (bytes memory) { 39 | return type(SmartAccountProxy).creationCode; 40 | } 41 | 42 | /// @dev Allows to create new proxy contact using CREATE2 but it doesn't run the initializer. 43 | /// This method is only meant as an utility to be called from other methods 44 | /// @param _singleton Address of singleton contract. 45 | /// @param initializer Payload for message call sent to new proxy contract. 46 | /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract. 47 | function deployProxyWithNonce( 48 | address _singleton, 49 | bytes memory initializer, 50 | uint256 saltNonce 51 | ) internal returns (SmartAccountProxy proxy) { 52 | // If the initializer changes the proxy address should change too. Hashing the initializer data is cheaper than just concatinating it 53 | bytes32 salt = keccak256( 54 | abi.encodePacked(keccak256(initializer), saltNonce) 55 | ); 56 | bytes memory deploymentData = abi.encodePacked( 57 | type(SmartAccountProxy).creationCode 58 | ); 59 | // solhint-disable-next-line no-inline-assembly 60 | assembly { 61 | proxy := create2( 62 | 0x0, 63 | add(0x20, deploymentData), 64 | mload(deploymentData), 65 | salt 66 | ) 67 | } 68 | require(address(proxy) != address(0), "Create2 call failed"); 69 | // walletWhiteList[address(proxy)] = true; 70 | } 71 | 72 | /// @dev Allows to create new proxy contact and execute a message call to the new proxy within one transaction. 73 | /// @param _singleton Address of singleton contract. 74 | /// @param initializer Payload for message call sent to new proxy contract. 75 | /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract. 76 | function createProxyWithNonce( 77 | address _singleton, 78 | bytes memory initializer, 79 | uint256 saltNonce 80 | ) internal returns (SmartAccountProxy proxy) { 81 | proxy = deployProxyWithNonce(_singleton, initializer, saltNonce); 82 | if (initializer.length > 0) { 83 | // solhint-disable-next-line no-inline-assembly 84 | bytes memory initdata = abi.encodeWithSelector( 85 | SmartAccountProxy.initialize.selector, 86 | _singleton, 87 | initializer 88 | ); 89 | 90 | assembly { 91 | if eq( 92 | call( 93 | gas(), 94 | proxy, 95 | 0, 96 | add(initdata, 0x20), 97 | mload(initdata), 98 | 0, 99 | 0 100 | ), 101 | 0 102 | ) { 103 | revert(0, 0) 104 | } 105 | } 106 | } 107 | 108 | emit ProxyCreation(proxy, _singleton); 109 | } 110 | 111 | function createAccount( 112 | address _safeSingleton, 113 | bytes memory initializer, 114 | uint256 salt 115 | ) public returns (address) { 116 | require(safeSingleton[_safeSingleton], "Invalid singleton"); 117 | 118 | address addr = getAddress(_safeSingleton, initializer, salt); 119 | return addr; 120 | } 121 | 122 | /** 123 | * calculate the counterfactual address of this account as it would be returned by createAccount() 124 | * (uses the same "create2 signature" used by SmartAccountProxyFactory.createProxyWithNonce) 125 | */ 126 | function getAddress( 127 | address _safeSingleton, 128 | bytes memory initializer, 129 | uint256 salt 130 | ) public view returns (address) { 131 | //copied from deployProxyWithNonce 132 | bytes32 salt2 = keccak256( 133 | abi.encodePacked(keccak256(initializer), salt) 134 | ); 135 | bytes memory deploymentData = abi.encodePacked( 136 | type(SmartAccountProxy).creationCode 137 | ); 138 | return 139 | Create2.computeAddress( 140 | bytes32(salt2), 141 | keccak256(deploymentData), 142 | address(this) 143 | ); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /contracts/mock/SimulateToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.12; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 5 | 6 | contract SimulateToken is ERC20 { 7 | address owner; 8 | 9 | constructor(address _owner, uint256 _amount) ERC20("SimulateToken", "ST") { 10 | owner = _owner; 11 | _mint(owner, _amount); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /contracts/mock/TestToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.12; 4 | 5 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 6 | 7 | contract TestToken is ERC20 { 8 | constructor() ERC20("TestToken", "TST") {} 9 | 10 | function mint(address account, uint256 amount) public { 11 | _mint(account, amount); 12 | } 13 | 14 | function transferFrom( 15 | address from, 16 | address to, 17 | uint256 amount 18 | ) public virtual override returns (bool) { 19 | address spender = _msgSender(); 20 | _transfer(from, to, amount); 21 | return true; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /contracts/mock/Uniswap/MockFactory.sol: -------------------------------------------------------------------------------- 1 | pragma solidity =0.5.16; 2 | import "@uniswap/v2-core/contracts/UniswapV2Factory.sol"; 3 | 4 | contract MockUniswapV2Factory is UniswapV2Factory { 5 | constructor(address _feeToSetter) public UniswapV2Factory(_feeToSetter) {} 6 | } 7 | -------------------------------------------------------------------------------- /contracts/mock/Uniswap/MockRouter.sol: -------------------------------------------------------------------------------- 1 | pragma solidity =0.6.6; 2 | import "@uniswap/v2-periphery/contracts/UniswapV2Router02.sol"; 3 | 4 | contract MockUniswapV2Router is UniswapV2Router02 { 5 | constructor( 6 | address _factory, 7 | address _WETH 8 | ) public UniswapV2Router02(_factory, _WETH) {} 9 | } 10 | -------------------------------------------------------------------------------- /contracts/mock/UserOperationHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity ^0.8.12; 3 | 4 | import "../@eth-infinitism-v0.6/interfaces/UserOperation.sol"; 5 | import "@openzeppelin/contracts/access/Ownable.sol"; 6 | 7 | contract UserOperationHelper is Ownable { 8 | using UserOperationLib for UserOperation; 9 | 10 | mapping(address => bool) public tokenPaymasters; 11 | mapping(address => bool) public entryPointSimulations; 12 | 13 | receive() external payable {} 14 | 15 | constructor( 16 | address _tokenPaymaster, 17 | address _entryPointSimulation, 18 | address _owner 19 | ) { 20 | tokenPaymasters[_tokenPaymaster] = true; 21 | entryPointSimulations[_entryPointSimulation] = true; 22 | _transferOwnership(_owner); 23 | } 24 | 25 | function getUserOpHash( 26 | UserOperation calldata userOp, 27 | address entrypoint 28 | ) external view returns (bytes32) { 29 | return keccak256(abi.encode(userOp.hash(), entrypoint, block.chainid)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /contracts/mock/interfaces/MockIEntryPoint.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-only 2 | pragma solidity ^0.8.12; 3 | 4 | 5 | /// build for MockTokenPaymaster.sol 6 | /// not specify a specific instance version of entryPoint 7 | interface MockIEntryPoint { 8 | 9 | /** 10 | * add to the deposit of the given account 11 | */ 12 | function depositTo(address account) external payable; 13 | 14 | /** 15 | * withdraw from the deposit. 16 | * @param withdrawAddress the address to send withdrawn value. 17 | * @param withdrawAmount the amount to withdraw. 18 | */ 19 | function withdrawTo(address payable withdrawAddress, uint256 withdrawAmount) external; 20 | } 21 | -------------------------------------------------------------------------------- /contracts/paymaster/FreeGasPaymaster.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity ^0.8.12; 3 | 4 | import "@openzeppelin/contracts/access/Ownable.sol"; 5 | import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; 6 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 7 | import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; 8 | import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 9 | import "../interfaces/IFreeGasPaymaster.sol"; 10 | import "../interfaces/IPriceOracle.sol"; 11 | import "../@eth-infinitism-v0.6/interfaces/IEntryPoint.sol"; 12 | import "./SupportedEntryPointLib.sol"; 13 | 14 | contract FreeGasPaymaster is IFreeGasPaymaster, Ownable { 15 | using UserOperationLib for UserOperation; 16 | using SupportedEntryPointLib for SupportedEntryPoint; 17 | using ECDSA for bytes32; 18 | using SafeERC20 for IERC20; 19 | 20 | uint256 internal constant SIG_VALIDATION_FAILED = 1; 21 | address public immutable verifyingSigner; 22 | address public immutable ADDRESS_THIS; 23 | SupportedEntryPoint supportedEntryPoint; 24 | mapping(address => bool) public whitelist; 25 | 26 | constructor(address _verifyingSigner, address _owner) { 27 | verifyingSigner = _verifyingSigner; 28 | _transferOwnership(_owner); 29 | ADDRESS_THIS = address(this); 30 | } 31 | 32 | modifier validEntryPoint(address entrypoint) { 33 | require( 34 | supportedEntryPoint.isSupportedEntryPoint(entrypoint), 35 | "Not from supported entrypoint" 36 | ); 37 | _; 38 | } 39 | 40 | modifier onlyWhitelisted(address _address) { 41 | require(whitelist[_address], "Address is not whitelisted"); 42 | _; 43 | } 44 | 45 | /// @dev add entryPoint to supported list; 46 | /// @param entrypoint entryPoint contract address 47 | function addSupportedEntryPoint(address entrypoint) external onlyOwner { 48 | supportedEntryPoint.addEntryPointToList(entrypoint); 49 | } 50 | 51 | /// @dev remove entryPoint from supported list; 52 | /// @param entrypoint entryPoint contract address 53 | function removeSupportedEntryPoint(address entrypoint) external onlyOwner { 54 | supportedEntryPoint.removeEntryPointToList(entrypoint); 55 | } 56 | 57 | /// @dev check entrypoint 58 | /// @param entrypoint entryPoint contract address 59 | function isSupportedEntryPoint( 60 | address entrypoint 61 | ) external view returns (bool) { 62 | return supportedEntryPoint.isSupportedEntryPoint(entrypoint); 63 | } 64 | 65 | function addToWhitelist(address[] calldata addresses) external onlyOwner { 66 | for (uint256 i = 0; i < addresses.length; i++) { 67 | whitelist[addresses[i]] = true; 68 | emit AddedToWhitelist(addresses[i]); 69 | } 70 | } 71 | 72 | function removeFromWhitelist( 73 | address[] calldata addresses 74 | ) external onlyOwner { 75 | for (uint256 i = 0; i < addresses.length; i++) { 76 | whitelist[addresses[i]] = false; 77 | emit RemovedFromWhitelist(addresses[i]); 78 | } 79 | } 80 | 81 | function withdrawERC20( 82 | address token, 83 | uint256 amount, 84 | address withdrawAddress 85 | ) external onlyOwner onlyWhitelisted(withdrawAddress) { 86 | IERC20(token).safeTransfer(withdrawAddress, amount); 87 | emit Withdrawal(token, amount); 88 | } 89 | 90 | function withdrawDepositNativeToken( 91 | address entryPoint, 92 | address payable withdrawAddress, 93 | uint256 amount 94 | ) 95 | public 96 | onlyOwner 97 | onlyWhitelisted(withdrawAddress) 98 | validEntryPoint(entryPoint) 99 | { 100 | IEntryPoint(entryPoint).withdrawTo(withdrawAddress, amount); 101 | emit Withdrawal(address(0), amount); 102 | } 103 | 104 | function getHash( 105 | UserOperation calldata userOp, 106 | uint256 sigTime 107 | ) public view returns (bytes32) { 108 | return 109 | keccak256( 110 | abi.encode( 111 | userOp.getSender(), 112 | userOp.nonce, 113 | keccak256(userOp.initCode), 114 | keccak256(userOp.callData), 115 | userOp.callGasLimit, 116 | userOp.verificationGasLimit, 117 | userOp.preVerificationGas, 118 | userOp.maxFeePerGas, 119 | userOp.maxPriorityFeePerGas, 120 | block.chainid, 121 | ADDRESS_THIS, 122 | sigTime 123 | ) 124 | ); 125 | } 126 | 127 | function validatePaymasterUserOp( 128 | UserOperation calldata userOp, 129 | bytes32, 130 | uint256 131 | ) external view override returns (bytes memory, uint256) { 132 | uint256 sigTime = uint256(bytes32(userOp.paymasterAndData[20:52])); 133 | 134 | if ( 135 | verifyingSigner != 136 | getHash(userOp, sigTime).toEthSignedMessageHash().recover( 137 | userOp.paymasterAndData[52:] 138 | ) 139 | ) { 140 | return ("", SIG_VALIDATION_FAILED); 141 | } else { 142 | return ("", sigTime); 143 | } 144 | } 145 | 146 | function validatePaymasterUserOpWithoutSig( 147 | UserOperation calldata userOp, 148 | bytes32, 149 | uint256 150 | ) external view returns (bytes memory, uint256) { 151 | uint256 sigTime = uint256(bytes32(userOp.paymasterAndData[20:52])); 152 | 153 | bool sigValidate = verifyingSigner != 154 | getHash(userOp, sigTime).toEthSignedMessageHash().recover( 155 | userOp.paymasterAndData[52:] 156 | ); 157 | 158 | return ("", sigTime); 159 | } 160 | 161 | function postOp( 162 | PostOpMode mode, 163 | bytes calldata context, 164 | uint256 gasCost 165 | ) external override {} 166 | } 167 | -------------------------------------------------------------------------------- /contracts/paymaster/SupportedEntryPointLib.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity ^0.8.12; 3 | 4 | struct SupportedEntryPoint { 5 | mapping(address => bool) data; 6 | } 7 | 8 | library SupportedEntryPointLib { 9 | event AddSupportedEntryPoint(address entrypoint, uint256 blockTime); 10 | event RemoveSupportedEntryPoint(address entrypoint, uint256 blockTime); 11 | 12 | /// @dev add entryPoint to list 13 | function addEntryPointToList( 14 | SupportedEntryPoint storage self, 15 | address entrypoint 16 | ) internal { 17 | require(entrypoint != address(0), "address can't be zero"); 18 | // require(entrypoint.code.length > 0, "must be contract"); 19 | require(!self.data[entrypoint], "duplicate entrypoint"); 20 | 21 | self.data[entrypoint] = true; 22 | emit AddSupportedEntryPoint(entrypoint, block.timestamp); 23 | } 24 | 25 | /// @dev remove entryPoint from list 26 | function removeEntryPointToList( 27 | SupportedEntryPoint storage self, 28 | address entrypoint 29 | ) internal { 30 | require(self.data[entrypoint], "entrypoint not exists"); 31 | 32 | delete self.data[entrypoint]; 33 | emit RemoveSupportedEntryPoint(entrypoint, block.timestamp); 34 | } 35 | 36 | /// @dev check entrypoint 37 | function isSupportedEntryPoint( 38 | SupportedEntryPoint storage self, 39 | address entrypoint 40 | ) internal view returns (bool) { 41 | return self.data[entrypoint]; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /contracts/paymaster/oracle/ChainlinkOracleAdapter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity ^0.8.12; 3 | 4 | import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; 5 | import "../../interfaces/IPriceOracle.sol"; 6 | 7 | contract ChainlinkOracleAdapter is PriceOracle { 8 | constructor(address _owner) PriceOracle(_owner) {} 9 | 10 | uint256 public immutable DEFAULT_TIMEOUT = 1 days; 11 | mapping(address => uint256) public timeouts; 12 | 13 | function exchangePrice( 14 | address token 15 | ) public view virtual override returns (uint256 price, uint8 decimals) { 16 | AggregatorV3Interface tokenPriceFeed = AggregatorV3Interface( 17 | priceFeed[token] 18 | ); 19 | require( 20 | tokenPriceFeed != AggregatorV3Interface(address(0)), 21 | "tokenPriceFeed is not setted" 22 | ); 23 | ( 24 | , 25 | /* uint80 roundID */ int256 _price /*uint startedAt*/ /*uint timeStamp*/ /*uint80 answeredInRound*/, 26 | , 27 | uint256 timeStamp, 28 | 29 | ) = tokenPriceFeed.latestRoundData(); 30 | 31 | require( 32 | timeStamp + getTimeout(token) > block.timestamp, 33 | "price is outdated" 34 | ); 35 | // price -> uint256 36 | require(_price >= 0, "price is negative"); 37 | price = uint256(_price); 38 | decimals = tokenPriceFeed.decimals(); 39 | } 40 | 41 | function setTimeout(address token, uint256 timeout) public onlyOwner { 42 | timeouts[token] = timeout; 43 | } 44 | 45 | function getTimeout(address token) public view returns (uint256) { 46 | uint256 timeout = timeouts[token]; 47 | return timeout == 0 ? DEFAULT_TIMEOUT : timeout; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /contracts/paymaster/oracle/ExOracleAdapter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity ^0.8.12; 3 | import "../../interfaces/IPriceOracle.sol"; 4 | import "../../interfaces/IExOraclePriceData.sol"; 5 | 6 | contract EXOracleAdapter is PriceOracle { 7 | IExOraclePriceData public exOracle; 8 | mapping(address => string) public priceType; 9 | mapping(address => uint8) public oracleDecimals; 10 | 11 | uint256 public immutable DEFAULT_TIMEOUT = 1 days; 12 | mapping(address => uint256) public timeouts; 13 | 14 | function setExOraclePriceData( 15 | IExOraclePriceData _exOracle 16 | ) public onlyOwner { 17 | require(address(_exOracle) != address(0), "invalid address"); 18 | exOracle = _exOracle; 19 | } 20 | 21 | function setPriceType( 22 | address _address, 23 | string memory _priceType 24 | ) public onlyOwner { 25 | priceType[_address] = _priceType; 26 | } 27 | 28 | function setOracleDecimals( 29 | address _address, 30 | uint8 _decimals 31 | ) public onlyOwner { 32 | oracleDecimals[_address] = _decimals; 33 | } 34 | 35 | constructor( 36 | address _owner, 37 | IExOraclePriceData _oracle 38 | ) PriceOracle(_owner) { 39 | exOracle = _oracle; 40 | } 41 | 42 | function exchangePrice( 43 | address token 44 | ) public view virtual override returns (uint256 price, uint8 decimals) { 45 | uint256 timestamp; 46 | address feed = priceFeed[token]; 47 | 48 | decimals = oracleDecimals[token]; 49 | require(feed != address(0), "Oracle Price feed not set yet"); 50 | require(decimals != 0, "Oracle Decimals not set yet"); 51 | (price, timestamp) = IExOraclePriceData(exOracle).get( 52 | priceType[token], 53 | priceFeed[token] 54 | ); 55 | 56 | require( 57 | timestamp + getTimeout(token) > block.timestamp, 58 | "price is outdated" 59 | ); 60 | require(price != 0, "price is negative"); 61 | } 62 | 63 | function setTimeout(address token, uint256 timeout) public onlyOwner { 64 | timeouts[token] = timeout; 65 | } 66 | 67 | function getTimeout(address token) public view returns (uint256) { 68 | uint256 timeout = timeouts[token]; 69 | return timeout == 0 ? DEFAULT_TIMEOUT : timeout; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /contracts/paymaster/swap/OKCSwapAdapter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity >=0.7.0 <0.9.0; 3 | 4 | import "@openzeppelin/contracts/access/Ownable.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 6 | import "../../interfaces/ISwapRouter.sol"; 7 | import "../../interfaces/ISwapAdapter.sol"; 8 | 9 | contract OKCSwapAdapter is ISwapAdapter, Ownable { 10 | using SafeERC20 for IERC20; 11 | 12 | ISwapRouter public immutable swapRouter; 13 | address public immutable nativeToken; 14 | mapping(address => address[]) public paths; 15 | 16 | event PathSet(address indexed token, address[] path); 17 | 18 | receive() external payable {} 19 | 20 | constructor(address _swapRouter, address _owner) { 21 | _transferOwnership(_owner); 22 | swapRouter = ISwapRouter(_swapRouter); 23 | nativeToken = ISwapRouter(_swapRouter).WOKT(); 24 | } 25 | 26 | function setPath(address token, address[] memory path) external onlyOwner { 27 | paths[token] = path; 28 | emit PathSet(token, path); 29 | } 30 | 31 | function swapToNative( 32 | address tokenIn, 33 | uint256 minAmountOut 34 | ) external override returns (uint256 amountOut) { 35 | return swapToNativeViaOKCSwap(tokenIn, minAmountOut); 36 | } 37 | 38 | function swapToNativeViaOKCSwap( 39 | address tokenIn, 40 | uint256 minAmountOut 41 | ) internal returns (uint256 amountOut) { 42 | address[] memory path = paths[tokenIn]; 43 | require(path.length > 0, "SwapHelper: path not found"); 44 | 45 | uint256 tokenInBalance = IERC20(tokenIn).balanceOf(address(this)); 46 | 47 | IERC20(tokenIn).safeApprove(address(swapRouter), 0); 48 | IERC20(tokenIn).safeApprove(address(swapRouter), tokenInBalance); 49 | swapRouter.swapExactTokensForOKT( 50 | tokenInBalance, 51 | minAmountOut, 52 | path, 53 | address(this), 54 | block.timestamp 55 | ); 56 | 57 | amountOut = address(this).balance; 58 | payable(msg.sender).transfer(amountOut); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /contracts/paymaster/swap/TradeJoeV2Adapter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity >=0.7.0 <0.9.0; 3 | 4 | import "@openzeppelin/contracts/access/Ownable.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 6 | import "../../interfaces/ILBRouter.sol"; 7 | import "../../interfaces/ISwapAdapter.sol"; 8 | 9 | contract TradeJoeV2Adapter is ISwapAdapter, Ownable { 10 | using SafeERC20 for IERC20; 11 | 12 | ILBRouter public immutable TradeJoeLBRouter; 13 | address public immutable nativeToken; 14 | mapping(address => address[]) public paths; 15 | mapping(address => uint256[]) public airBinSteps; 16 | 17 | event PathSet(address indexed token, address[] path); 18 | event AirBinStepSet(address indexed token, uint256[] airBinStep); 19 | 20 | receive() external payable {} 21 | 22 | constructor(address _TradeJoeLBRouter, address _owner) { 23 | _transferOwnership(_owner); 24 | TradeJoeLBRouter = ILBRouter(_TradeJoeLBRouter); 25 | nativeToken = ILBRouter(_TradeJoeLBRouter).wavax(); 26 | } 27 | 28 | function setPath(address token, address[] memory path) external onlyOwner { 29 | paths[token] = path; 30 | emit PathSet(token, path); 31 | } 32 | 33 | function setAirBinStep( 34 | address token, 35 | uint256[] memory airBinStep 36 | ) external onlyOwner { 37 | airBinSteps[token] = airBinStep; 38 | emit AirBinStepSet(token, airBinStep); 39 | } 40 | 41 | function swapToNative( 42 | address tokenIn, 43 | uint256 minAmountOut 44 | ) external override returns (uint256 amountOut) { 45 | return swapToNativeViaTradeJoeV2(tokenIn, minAmountOut); 46 | } 47 | 48 | function swapToNativeViaTradeJoeV2( 49 | address tokenIn, 50 | uint256 minAmountOut 51 | ) internal returns (uint256 amountOut) { 52 | address[] memory path = paths[tokenIn]; 53 | uint256[] memory airBinStep = airBinSteps[tokenIn]; 54 | require(path.length > 0, "SwapHelper: path not found"); 55 | require( 56 | airBinStep.length == path.length - 1, 57 | "SwapHelper: airBinStep not found" 58 | ); 59 | 60 | uint256 tokenInBalance = IERC20(tokenIn).balanceOf(address(this)); 61 | 62 | IERC20(tokenIn).safeApprove(address(TradeJoeLBRouter), 0); 63 | IERC20(tokenIn).safeApprove(address(TradeJoeLBRouter), tokenInBalance); 64 | TradeJoeLBRouter.swapExactTokensForAVAX( 65 | tokenInBalance, 66 | minAmountOut, 67 | airBinStep, 68 | path, 69 | payable(address(this)), 70 | block.timestamp 71 | ); 72 | 73 | amountOut = address(this).balance; 74 | payable(msg.sender).transfer(amountOut); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /contracts/paymaster/swap/UniSwapV2Adapter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity ^0.8.12; 3 | 4 | import "@openzeppelin/contracts/access/Ownable.sol"; 5 | import "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol"; 6 | import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 7 | import "../../interfaces/ISwapAdapter.sol"; 8 | 9 | // swapHelper is a helper contract that is used to swap tokens to native via uniV2Router 10 | contract UniSwapV2Adapter is ISwapAdapter, Ownable { 11 | using SafeERC20 for IERC20; 12 | 13 | IUniswapV2Router02 public immutable uniV2Router; 14 | address public immutable nativeToken; 15 | mapping(address => address[]) public paths; 16 | 17 | event PathSet(address indexed token, address[] path); 18 | 19 | receive() external payable {} 20 | 21 | constructor(address _uniV2Router, address _owner) { 22 | _transferOwnership(_owner); 23 | uniV2Router = IUniswapV2Router02(_uniV2Router); 24 | nativeToken = IUniswapV2Router02(_uniV2Router).WETH(); 25 | } 26 | 27 | function setPath(address token, address[] memory path) external onlyOwner { 28 | paths[token] = path; 29 | emit PathSet(token, path); 30 | } 31 | 32 | function swapToNative( 33 | address tokenIn, 34 | uint256 minAmountOut 35 | ) external override returns (uint256 amountOut) { 36 | return swapToNativeViaUniV2(tokenIn, minAmountOut); 37 | } 38 | 39 | function swapToNativeViaUniV2( 40 | address tokenIn, 41 | uint256 minAmountOut 42 | ) internal returns (uint256 amountOut) { 43 | address[] memory path = paths[tokenIn]; 44 | require(path.length > 0, "SwapHelper: path not found"); 45 | 46 | uint256 tokenInBalance = IERC20(tokenIn).balanceOf(address(this)); 47 | 48 | IERC20(tokenIn).safeApprove(address(uniV2Router), 0); 49 | IERC20(tokenIn).safeApprove(address(uniV2Router), tokenInBalance); 50 | uniV2Router.swapExactTokensForETH( 51 | tokenInBalance, 52 | minAmountOut, 53 | path, 54 | address(this), 55 | block.timestamp 56 | ); 57 | 58 | amountOut = address(this).balance; 59 | payable(msg.sender).transfer(amountOut); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /contracts/paymaster/swap/UniSwapV3Adapter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity >=0.7.0 <0.9.0; 3 | 4 | import "@openzeppelin/contracts/access/Ownable.sol"; 5 | import "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol"; 6 | import "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol"; 7 | import "@uniswap/v3-periphery/contracts/interfaces/IPeripheryImmutableState.sol"; 8 | import "../../interfaces/IWETH9.sol"; 9 | import "../../interfaces/ISwapAdapter.sol"; 10 | 11 | contract UniSwapV3Adapter is ISwapAdapter, Ownable { 12 | ISwapRouter public immutable uniV3Router; 13 | address public immutable nativeToken; 14 | mapping(address => uint24) public poolFee; 15 | 16 | event PoolFeeSet(address indexed token, uint24 fee); 17 | 18 | receive() external payable {} 19 | 20 | constructor(address _uniV3Router, address _owner) { 21 | _transferOwnership(_owner); 22 | uniV3Router = ISwapRouter(_uniV3Router); 23 | nativeToken = IPeripheryImmutableState(_uniV3Router).WETH9(); 24 | } 25 | 26 | function setPoolFee(address token, uint24 fee) external onlyOwner { 27 | poolFee[token] = fee; 28 | emit PoolFeeSet(token, fee); 29 | } 30 | 31 | function swapToNative( 32 | address tokenIn, 33 | uint256 minAmountOut 34 | ) external override returns (uint256 amountOut) { 35 | return swapToNativeViaUniV3(tokenIn, minAmountOut); 36 | } 37 | 38 | function swapToNativeViaUniV3( 39 | address tokenIn, 40 | uint256 minAmountOut 41 | ) internal returns (uint256 amountOut) { 42 | uint256 tokenInBalance = IERC20(tokenIn).balanceOf(address(this)); 43 | 44 | TransferHelper.safeApprove(tokenIn, address(uniV3Router), 0); 45 | TransferHelper.safeApprove( 46 | tokenIn, 47 | address(uniV3Router), 48 | tokenInBalance 49 | ); 50 | 51 | ISwapRouter.ExactInputSingleParams memory params = ISwapRouter 52 | .ExactInputSingleParams({ 53 | tokenIn: tokenIn, 54 | tokenOut: nativeToken, 55 | fee: poolFee[tokenIn], 56 | recipient: address(this), 57 | deadline: block.timestamp, 58 | amountIn: tokenInBalance, 59 | amountOutMinimum: minAmountOut, 60 | sqrtPriceLimitX96: 0 61 | }); 62 | 63 | amountOut = uniV3Router.exactInputSingle(params); 64 | IWETH9(nativeToken).withdraw(amountOut); 65 | 66 | payable(msg.sender).transfer(address(this).balance); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /contracts/simulations/core/NonceManager.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity ^0.8.12; 3 | 4 | import "../interfaces/IEntryPoint.sol"; 5 | import "@account-abstraction/contracts/interfaces/INonceManager.sol"; 6 | 7 | /** 8 | * nonce management functionality 9 | */ 10 | contract NonceManager is INonceManager { 11 | 12 | /** 13 | * The next valid sequence number for a given nonce key. 14 | */ 15 | mapping(address => mapping(uint192 => uint256)) public nonceSequenceNumber; 16 | 17 | function getNonce(address sender, uint192 key) 18 | public view override returns (uint256 nonce) { 19 | return nonceSequenceNumber[sender][key] | (uint256(key) << 64); 20 | } 21 | 22 | // allow an account to manually increment its own nonce. 23 | // (mainly so that during construction nonce can be made non-zero, 24 | // to "absorb" the gas cost of first nonce increment to 1st transaction (construction), 25 | // not to 2nd transaction) 26 | function incrementNonce(uint192 key) public override { 27 | nonceSequenceNumber[msg.sender][key]++; 28 | } 29 | 30 | /** 31 | * validate nonce uniqueness for this account. 32 | * called just after validateUserOp() 33 | */ 34 | function _validateAndUpdateNonce(address sender, uint256 nonce) internal returns (bool) { 35 | 36 | uint192 key = uint192(nonce >> 64); 37 | uint64 seq = uint64(nonce); 38 | return nonceSequenceNumber[sender][key]++ == seq; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /contracts/simulations/core/StakeManager.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-only 2 | pragma solidity ^0.8.12; 3 | 4 | import "@account-abstraction/contracts/interfaces/IStakeManager.sol"; 5 | 6 | /* solhint-disable avoid-low-level-calls */ 7 | /* solhint-disable not-rely-on-time */ 8 | /** 9 | * manage deposits and stakes. 10 | * deposit is just a balance used to pay for UserOperations (either by a paymaster or an account) 11 | * stake is value locked for at least "unstakeDelay" by a paymaster. 12 | */ 13 | abstract contract StakeManager is IStakeManager { 14 | 15 | /// maps paymaster to their deposits and stakes 16 | mapping(address => DepositInfo) public deposits; 17 | 18 | /// @inheritdoc IStakeManager 19 | function getDepositInfo(address account) public view returns (DepositInfo memory info) { 20 | return deposits[account]; 21 | } 22 | 23 | // internal method to return just the stake info 24 | function _getStakeInfo(address addr) internal view returns (StakeInfo memory info) { 25 | DepositInfo storage depositInfo = deposits[addr]; 26 | info.stake = depositInfo.stake; 27 | info.unstakeDelaySec = depositInfo.unstakeDelaySec; 28 | } 29 | 30 | /// return the deposit (for gas payment) of the account 31 | function balanceOf(address account) public view returns (uint256) { 32 | return deposits[account].deposit; 33 | } 34 | 35 | receive() external payable { 36 | depositTo(msg.sender); 37 | } 38 | 39 | function _incrementDeposit(address account, uint256 amount) internal { 40 | DepositInfo storage info = deposits[account]; 41 | uint256 newAmount = info.deposit + amount; 42 | require(newAmount <= type(uint112).max, "deposit overflow"); 43 | info.deposit = uint112(newAmount); 44 | } 45 | 46 | /** 47 | * add to the deposit of the given account 48 | */ 49 | function depositTo(address account) public virtual payable { 50 | _incrementDeposit(account, msg.value); 51 | DepositInfo storage info = deposits[account]; 52 | emit Deposited(account, info.deposit); 53 | } 54 | 55 | /** 56 | * add to the account's stake - amount and delay 57 | * any pending unstake is first cancelled. 58 | * @param unstakeDelaySec the new lock duration before the deposit can be withdrawn. 59 | */ 60 | function addStake(uint32 unstakeDelaySec) public payable { 61 | DepositInfo storage info = deposits[msg.sender]; 62 | require(unstakeDelaySec > 0, "must specify unstake delay"); 63 | require(unstakeDelaySec >= info.unstakeDelaySec, "cannot decrease unstake time"); 64 | uint256 stake = info.stake + msg.value; 65 | require(stake > 0, "no stake specified"); 66 | require(stake <= type(uint112).max, "stake overflow"); 67 | deposits[msg.sender] = DepositInfo( 68 | info.deposit, 69 | true, 70 | uint112(stake), 71 | unstakeDelaySec, 72 | 0 73 | ); 74 | emit StakeLocked(msg.sender, stake, unstakeDelaySec); 75 | } 76 | 77 | /** 78 | * attempt to unlock the stake. 79 | * the value can be withdrawn (using withdrawStake) after the unstake delay. 80 | */ 81 | function unlockStake() external { 82 | DepositInfo storage info = deposits[msg.sender]; 83 | require(info.unstakeDelaySec != 0, "not staked"); 84 | require(info.staked, "already unstaking"); 85 | uint48 withdrawTime = uint48(block.timestamp) + info.unstakeDelaySec; 86 | info.withdrawTime = withdrawTime; 87 | info.staked = false; 88 | emit StakeUnlocked(msg.sender, withdrawTime); 89 | } 90 | 91 | /** 92 | * withdraw from the (unlocked) stake. 93 | * must first call unlockStake and wait for the unstakeDelay to pass 94 | * @param withdrawAddress the address to send withdrawn value. 95 | */ 96 | function withdrawStake(address payable withdrawAddress) external { 97 | DepositInfo storage info = deposits[msg.sender]; 98 | uint256 stake = info.stake; 99 | require(stake > 0, "No stake to withdraw"); 100 | require(info.withdrawTime > 0, "must call unlockStake() first"); 101 | require(info.withdrawTime <= block.timestamp, "Stake withdrawal is not due"); 102 | info.unstakeDelaySec = 0; 103 | info.withdrawTime = 0; 104 | info.stake = 0; 105 | emit StakeWithdrawn(msg.sender, withdrawAddress, stake); 106 | (bool success,) = withdrawAddress.call{value: stake}(""); 107 | require(success, "failed to withdraw stake"); 108 | } 109 | 110 | /** 111 | * withdraw from the deposit. 112 | * @param withdrawAddress the address to send withdrawn value. 113 | * @param withdrawAmount the amount to withdraw. 114 | */ 115 | function withdrawTo(address payable withdrawAddress, uint256 withdrawAmount) external { 116 | DepositInfo storage info = deposits[msg.sender]; 117 | require(withdrawAmount <= info.deposit, "Withdraw amount too large"); 118 | info.deposit = uint112(info.deposit - withdrawAmount); 119 | emit Withdrawn(msg.sender, withdrawAddress, withdrawAmount); 120 | (bool success,) = withdrawAddress.call{value: withdrawAmount}(""); 121 | require(success, "failed to withdraw"); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /contracts/simulations/interfaces/IEntryPointSimulations.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity ^0.8.12; 3 | 4 | import "@account-abstraction/contracts/interfaces/UserOperation.sol"; 5 | import "./IEntryPoint.sol"; 6 | 7 | interface IEntryPointSimulations is IEntryPoint { 8 | 9 | /** 10 | * return value of simulateHandleOp 11 | */ 12 | error SimulateHandleOpResult( 13 | uint48 validAfter, 14 | uint48 validUntil, 15 | address validationAggregator, 16 | uint256 preOpGas, 17 | bool execSuccess, 18 | bytes execErrMsg, 19 | uint256 actualGasUsed, 20 | uint256 postOpGas, 21 | uint256 paid, 22 | bool targetSuccess, 23 | bytes targetResult); 24 | 25 | /** 26 | * Simulate full execution of a UserOperation (including both validation and target execution) 27 | * It performs full validation of the UserOperation, but ignores signature error. 28 | * An optional target address is called after the userop succeeds, 29 | * and its value is returned (before the entire call is reverted). 30 | * @param op The UserOperation to simulate. 31 | * @param target - If nonzero, a target address to call after userop simulation. If called, 32 | * the targetSuccess and targetResult are set to the return from that call. 33 | * @param targetCallData - CallData to pass to target address. 34 | */ 35 | function simulateHandleOp( 36 | UserOperation calldata op, 37 | address target, 38 | bytes calldata targetCallData 39 | ) 40 | external; 41 | 42 | function estimateGas( 43 | UserOperation calldata op, 44 | address target, 45 | bytes calldata targetCallData 46 | ) external; 47 | } 48 | -------------------------------------------------------------------------------- /contracts/wallet/base/Executor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0-only 2 | pragma solidity ^0.8.12; 3 | import "../common/Enum.sol"; 4 | 5 | /// @title Executor - A contract that can execute transactions 6 | contract Executor { 7 | struct ExecuteParams { 8 | bool allowFailed; 9 | address to; 10 | uint256 value; 11 | bytes data; 12 | bytes nestedCalls; // ExecuteParams encoded as bytes 13 | } 14 | 15 | event HandleSuccessExternalCalls(); 16 | event HandleFailedExternalCalls(bytes revertReason); 17 | 18 | function execute( 19 | ExecuteParams memory params, 20 | Enum.Operation operation, 21 | uint256 txGas 22 | ) internal returns (bool success) { 23 | bytes memory result; 24 | 25 | if (operation == Enum.Operation.DelegateCall) { 26 | // solhint-disable-next-line no-inline-assembly 27 | (success, result) = params.to.delegatecall{gas: txGas}(params.data); 28 | } else { 29 | // solhint-disable-next-line no-inline-assembly 30 | (success, result) = payable(params.to).call{ 31 | gas: txGas, 32 | value: params.value 33 | }(params.data); 34 | } 35 | 36 | if (!success) { 37 | if (!params.allowFailed) { 38 | assembly { 39 | revert(add(result, 32), mload(result)) 40 | } 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /contracts/wallet/base/FallbackManager.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0-only 2 | pragma solidity ^0.8.12; 3 | 4 | import "../common/SelfAuthorized.sol"; 5 | 6 | /// @title Fallback Manager - A contract that manages fallback calls made to this contract 7 | contract FallbackManager is SelfAuthorized { 8 | event ChangedFallbackHandler(address handler); 9 | 10 | // keccak256("fallback_manager.handler.address") 11 | bytes32 internal constant FALLBACK_HANDLER_STORAGE_SLOT = 12 | 0x6c9a6c4a39284e37ed1cf53d337577d14212a4870fb976a4366c693b939918d5; 13 | 14 | function getFallbackHandler() 15 | public 16 | view 17 | returns (address fallbackHandler) 18 | { 19 | bytes32 slot = FALLBACK_HANDLER_STORAGE_SLOT; 20 | // solhint-disable-next-line no-inline-assembly 21 | assembly { 22 | let encoded := sload(slot) 23 | fallbackHandler := shr(96, encoded) 24 | } 25 | } 26 | 27 | /// @dev Allows to add a contract to handle fallback calls. 28 | /// Only fallback calls without value and with data will be forwarded. 29 | /// This can only be done via a Safe transaction. 30 | /// @param handler contract to handle fallbacks calls. 31 | function setFallbackHandler(address handler) external authorized { 32 | setFallbackHandler(handler, false); 33 | emit ChangedFallbackHandler(handler); 34 | } 35 | 36 | function setFallbackHandler(address handler, bool delegate) internal { 37 | require(handler != address(this), "handler illegal"); 38 | bytes32 slot = FALLBACK_HANDLER_STORAGE_SLOT; 39 | // solhint-disable-next-line no-inline-assembly 40 | assembly { 41 | let encoded := or(shl(96, handler), delegate) 42 | sstore(slot, encoded) 43 | } 44 | } 45 | 46 | function initializeFallbackHandler(address handler) internal { 47 | bytes32 slot = FALLBACK_HANDLER_STORAGE_SLOT; 48 | // solhint-disable-next-line no-inline-assembly 49 | assembly { 50 | let encoded := shl(96, handler) 51 | sstore(slot, encoded) 52 | } 53 | } 54 | 55 | // solhint-disable-next-line payable-fallback,no-complex-fallback 56 | fallback() external { 57 | assembly { 58 | // Load handler and delegate flag from storage 59 | let encoded := sload(FALLBACK_HANDLER_STORAGE_SLOT) 60 | let handler := shr(96, encoded) 61 | let delegate := and(encoded, 1) 62 | 63 | // Copy calldata to memory 64 | calldatacopy(0, 0, calldatasize()) 65 | 66 | // If delegate flag is set, delegate the call to the handler 67 | switch delegate 68 | case 0 { 69 | mstore(calldatasize(), shl(96, caller())) 70 | let success := call( 71 | gas(), 72 | handler, 73 | 0, 74 | 0, 75 | add(calldatasize(), 20), 76 | 0, 77 | 0 78 | ) 79 | returndatacopy(0, 0, returndatasize()) 80 | if iszero(success) { 81 | revert(0, returndatasize()) 82 | } 83 | return(0, returndatasize()) 84 | } 85 | case 1 { 86 | let result := delegatecall( 87 | gas(), 88 | handler, 89 | 0, 90 | calldatasize(), 91 | 0, 92 | 0 93 | ) 94 | 95 | returndatacopy(0, 0, returndatasize()) 96 | 97 | switch result 98 | case 0 { 99 | revert(0, returndatasize()) 100 | } 101 | default { 102 | return(0, returndatasize()) 103 | } 104 | } 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /contracts/wallet/base/ModuleManager.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0-only 2 | pragma solidity ^0.8.12; 3 | import "../common/Enum.sol"; 4 | import "../common/SelfAuthorized.sol"; 5 | import "./Executor.sol"; 6 | 7 | /// @title Module Manager - A contract that manages modules that can execute transactions via this contract 8 | contract ModuleManager is SelfAuthorized, Executor { 9 | event EnabledModule(address module); 10 | event DisabledModule(address module); 11 | event ExecutionFromModuleSuccess(address module); 12 | event ExecutionFromModuleFailure(address module); 13 | 14 | address internal constant SENTINEL_MODULES = address(0x1); 15 | mapping(address => address) internal modules; 16 | 17 | function initializeModules() internal { 18 | modules[SENTINEL_MODULES] = SENTINEL_MODULES; 19 | } 20 | 21 | function enableModule(address module) public authorized { 22 | // Module address cannot be null or sentinel. 23 | require(module != address(0) && module != SENTINEL_MODULES, "GS101"); 24 | // Module cannot be added twice. 25 | require(modules[module] == address(0), "GS102"); 26 | 27 | modules[module] = modules[SENTINEL_MODULES]; 28 | modules[SENTINEL_MODULES] = module; 29 | emit EnabledModule(module); 30 | } 31 | 32 | /// @dev Allows to remove a module from the whitelist. 33 | /// This can only be done via a Safe transaction. 34 | /// @notice Disables the module `module` for the Safe. 35 | /// @param prevModule Module that pointed to the module to be removed in the linked list 36 | /// @param module Module to be removed. 37 | function disableModule( 38 | address prevModule, 39 | address module 40 | ) public authorized { 41 | // Validate module address and check that it corresponds to module index. 42 | require(module != address(0) && module != SENTINEL_MODULES, "GS101"); 43 | require(modules[prevModule] == module, "GS103"); 44 | modules[prevModule] = modules[module]; 45 | modules[module] = address(0); 46 | emit DisabledModule(module); 47 | } 48 | 49 | /// @dev Returns if an module is enabled 50 | /// @return True if the module is enabled 51 | function isModuleEnabled(address module) public view returns (bool) { 52 | return SENTINEL_MODULES != module && modules[module] != address(0); 53 | } 54 | 55 | /// @dev Allows a Module to execute a Safe transaction without any further confirmations. 56 | /// @param to Destination address of module transaction. 57 | /// @param value Ether value of module transaction. 58 | /// @param data Data payload of module transaction. 59 | /// @param operation Operation type of module transaction. 60 | function execTransactionFromModule( 61 | address to, 62 | uint256 value, 63 | bytes calldata data, 64 | Enum.Operation operation 65 | ) public virtual { 66 | // Only whitelisted modules are allowed. 67 | require(modules[msg.sender] != address(0), "GS104"); 68 | // Execute transaction without further confirmations. 69 | if ( 70 | execute( 71 | ExecuteParams(false, to, value, data, ""), 72 | operation, 73 | gasleft() 74 | ) 75 | ) emit ExecutionFromModuleSuccess(msg.sender); 76 | else emit ExecutionFromModuleFailure(msg.sender); 77 | } 78 | 79 | /// @dev Allows a Module to execute a Safe transaction without any further confirmations and return data 80 | /// @param to Destination address of module transaction. 81 | /// @param value Ether value of module transaction. 82 | /// @param data Data payload of module transaction. 83 | /// @param operation Operation type of module transaction. 84 | function execTransactionFromModuleReturnData( 85 | address to, 86 | uint256 value, 87 | bytes calldata data, 88 | Enum.Operation operation 89 | ) public returns (bytes memory returnData) { 90 | execTransactionFromModule(to, value, data, operation); 91 | // solhint-disable-next-line no-inline-assembly 92 | assembly { 93 | // Load free memory location 94 | let ptr := mload(0x40) 95 | // We allocate memory for the return data by setting the free memory location to 96 | // current free memory location + data size + 32 bytes for data size value 97 | mstore(0x40, add(ptr, add(returndatasize(), 0x20))) 98 | // Store the size 99 | mstore(ptr, returndatasize()) 100 | // Store the data 101 | returndatacopy(add(ptr, 0x20), 0, returndatasize()) 102 | // Point the return data to the correct memory location 103 | returnData := ptr 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /contracts/wallet/base/OwnerManager.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0-only 2 | pragma solidity ^0.8.12; 3 | 4 | contract OwnerManager { 5 | event AAOwnerSet(address owner); 6 | 7 | address internal owner; 8 | 9 | uint256 private nonce; 10 | 11 | modifier onlyOwner() { 12 | require(isOwner(msg.sender), "not call by owner"); 13 | _; 14 | } 15 | 16 | function initializeOwners(address _owner) internal { 17 | owner = _owner; 18 | 19 | emit AAOwnerSet(_owner); 20 | } 21 | 22 | function isOwner(address _owner) public view returns (bool) { 23 | return owner == _owner; 24 | } 25 | 26 | function getOwner() public view returns (address) { 27 | return owner; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /contracts/wallet/common/Enum.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0-only 2 | pragma solidity ^0.8.12; 3 | 4 | /// @title Enum - Collection of enums 5 | contract Enum { 6 | enum Operation { 7 | Call, 8 | DelegateCall 9 | } 10 | enum SignatureType { 11 | EIP712Type, 12 | EIP191Type 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /contracts/wallet/common/EtherPaymentFallback.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0-only 2 | pragma solidity ^0.8.12; 3 | 4 | /// @title EtherPaymentFallback - A contract that has a fallback to accept ether payments 5 | /// @author Richard Meissner - 6 | contract EtherPaymentFallback { 7 | event SafeReceived(address indexed sender, uint256 value); 8 | 9 | /// @dev Fallback function accepts Ether transactions. 10 | receive() external payable { 11 | emit SafeReceived(msg.sender, msg.value); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /contracts/wallet/common/SecuredTokenTransfer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0-only 2 | pragma solidity ^0.8.12; 3 | 4 | /// @title SecuredTokenTransfer - Secure token transfer 5 | /// @author Richard Meissner - 6 | contract SecuredTokenTransfer { 7 | /// @dev Transfers a token and returns if it was a success 8 | /// @param token Token that should be transferred 9 | /// @param receiver Receiver to whom the token should be transferred 10 | /// @param amount The amount of tokens that should be transferred 11 | function transferToken( 12 | address token, 13 | address receiver, 14 | uint256 amount 15 | ) internal returns (bool transferred) { 16 | // 0xa9059cbb - keccack("transfer(address,uint256)") 17 | bytes memory data = abi.encodeWithSelector( 18 | 0xa9059cbb, 19 | receiver, 20 | amount 21 | ); 22 | // solhint-disable-next-line no-inline-assembly 23 | assembly { 24 | // We write the return value to scratch space. 25 | // See https://docs.soliditylang.org/en/v0.7.6/internals/layout_in_memory.html#layout-in-memory 26 | let success := call( 27 | sub(gas(), 10000), 28 | token, 29 | 0, 30 | add(data, 0x20), 31 | mload(data), 32 | 0, 33 | 0x20 34 | ) 35 | switch returndatasize() 36 | case 0 { 37 | transferred := success 38 | } 39 | case 0x20 { 40 | transferred := iszero(or(iszero(success), iszero(mload(0)))) 41 | } 42 | default { 43 | transferred := 0 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /contracts/wallet/common/SelfAuthorized.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0-only 2 | pragma solidity ^0.8.12; 3 | 4 | /// @title SelfAuthorized - authorizes current contract to perform actions 5 | /// @author Richard Meissner - 6 | contract SelfAuthorized { 7 | function requireSelfCall() private view { 8 | require(msg.sender == address(this), "GS031"); 9 | } 10 | 11 | modifier authorized() { 12 | // This is a function call as it minimized the bytecode size 13 | requireSelfCall(); 14 | _; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /contracts/wallet/common/SignatureDecoder.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0-only 2 | pragma solidity ^0.8.12; 3 | 4 | /// @title SignatureDecoder - Decodes signatures that a encoded as bytes 5 | /// @author Richard Meissner - 6 | contract SignatureDecoder { 7 | /// @dev divides bytes signature into `uint8 v, bytes32 r, bytes32 s`. 8 | /// @notice Make sure to peform a bounds check for @param pos, to avoid out of bounds access on @param signatures 9 | /// @param pos which signature to read. A prior bounds check of this parameter should be performed, to avoid out of bounds access 10 | /// @param signatures concatenated rsv signatures 11 | function signatureSplit( 12 | bytes memory signatures, 13 | uint256 pos 14 | ) internal pure returns (uint8 v, bytes32 r, bytes32 s) { 15 | // The signature format is a compact form of: 16 | // {bytes32 r}{bytes32 s}{uint8 v} 17 | // Compact means, uint8 is not padded to 32 bytes. 18 | // solhint-disable-next-line no-inline-assembly 19 | assembly { 20 | let signaturePos := mul(0x41, pos) 21 | r := mload(add(signatures, add(signaturePos, 0x20))) 22 | s := mload(add(signatures, add(signaturePos, 0x40))) 23 | // Here we are loading the last 32 bytes, including 31 bytes 24 | // of 's'. There is no 'mload8' to do this. 25 | // 26 | // 'byte' is not working due to the Solidity parser, so lets 27 | // use the second best option, 'and' 28 | v := and(mload(add(signatures, add(signaturePos, 0x41))), 0xff) 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /contracts/wallet/common/Singleton.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0-only 2 | pragma solidity ^0.8.12; 3 | import "./SelfAuthorized.sol"; 4 | 5 | /// @title Singleton - Base for singleton contracts (should always be first super contract) 6 | /// This contract is tightly coupled to our proxy contract 7 | contract Singleton is SelfAuthorized { 8 | event ImplementUpdated(address indexed implement); 9 | address internal singleton; 10 | 11 | function updateImplement(address implement) external authorized { 12 | singleton = implement; 13 | emit ImplementUpdated(implement); 14 | } 15 | 16 | function updateImplementAndCall( 17 | address implement, 18 | bytes calldata data 19 | ) external authorized { 20 | singleton = implement; 21 | emit ImplementUpdated(implement); 22 | (bool success, ) = implement.delegatecall(data); 23 | require(success, "Update implementation failed"); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /contracts/wallet/handler/DefaultCallbackHandler.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0-only 2 | pragma solidity ^0.8.12; 3 | 4 | import "../../interfaces/ERC1155TokenReceiver.sol"; 5 | import "../../interfaces/ERC721TokenReceiver.sol"; 6 | import "../../interfaces/ERC777TokensRecipient.sol"; 7 | import "../../interfaces/IERC165.sol"; 8 | 9 | /// @title Default Callback Handler - returns true for known token callbacks 10 | /// @author Richard Meissner - 11 | contract DefaultCallbackHandler is 12 | ERC1155TokenReceiver, 13 | ERC777TokensRecipient, 14 | ERC721TokenReceiver, 15 | IERC165 16 | { 17 | string public constant NAME = "Default Callback Handler"; 18 | string public constant VERSION = "1.0.0"; 19 | 20 | function onERC1155Received( 21 | address, 22 | address, 23 | uint256, 24 | uint256, 25 | bytes calldata 26 | ) external pure override returns (bytes4) { 27 | return 0xf23a6e61; 28 | } 29 | 30 | function onERC1155BatchReceived( 31 | address, 32 | address, 33 | uint256[] calldata, 34 | uint256[] calldata, 35 | bytes calldata 36 | ) external pure override returns (bytes4) { 37 | return 0xbc197c81; 38 | } 39 | 40 | function onERC721Received( 41 | address, 42 | address, 43 | uint256, 44 | bytes calldata 45 | ) external pure override returns (bytes4) { 46 | return 0x150b7a02; 47 | } 48 | 49 | function tokensReceived( 50 | address, 51 | address, 52 | address, 53 | uint256, 54 | bytes calldata, 55 | bytes calldata 56 | ) external pure override { 57 | // We implement this for completeness, doesn't really have any value 58 | } 59 | 60 | function supportsInterface( 61 | bytes4 interfaceId 62 | ) external view virtual override returns (bool) { 63 | return 64 | interfaceId == type(ERC1155TokenReceiver).interfaceId || 65 | interfaceId == type(ERC721TokenReceiver).interfaceId || 66 | interfaceId == type(IERC165).interfaceId; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /contracts/wallet/helper/SimulateTxAccessor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0-only 2 | pragma solidity ^0.8.12; 3 | import "../base/Executor.sol"; 4 | 5 | /// @title Simulate Transaction Accessor - can be used with StorageAccessible to simulate Safe transactions 6 | /// @author Richard Meissner - 7 | contract SimulateTxAccessor is Executor { 8 | address private immutable accessorSingleton; 9 | 10 | constructor() { 11 | accessorSingleton = address(this); 12 | } 13 | 14 | modifier onlyDelegateCall() { 15 | require( 16 | address(this) != accessorSingleton, 17 | "SimulateTxAccessor should only be called via delegatecall" 18 | ); 19 | _; 20 | } 21 | 22 | function simulate( 23 | address to, 24 | uint256 value, 25 | bytes calldata data, 26 | Enum.Operation operation 27 | ) 28 | external 29 | onlyDelegateCall 30 | returns (uint256 estimate, bool success, bytes memory returnData) 31 | { 32 | uint256 startGas = gasleft(); 33 | success = execute( 34 | ExecuteParams(false, to, value, data, ""), 35 | operation, 36 | gasleft() 37 | ); 38 | estimate = startGas - gasleft(); 39 | // solhint-disable-next-line no-inline-assembly 40 | assembly { 41 | // Load free memory location 42 | let ptr := mload(0x40) 43 | // We allocate memory for the return data by setting the free memory location to 44 | // current free memory location + data size + 32 bytes for data size value 45 | mstore(0x40, add(ptr, add(returndatasize(), 0x20))) 46 | // Store the size 47 | mstore(ptr, returndatasize()) 48 | // Store the data 49 | returndatacopy(add(ptr, 0x20), 0, returndatasize()) 50 | // Point the return data to the correct memory location 51 | returnData := ptr 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /contracts/wallet/helper/SmartAccountInitCode.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0-only 2 | pragma solidity ^0.8.12; 3 | 4 | import "../v1/SmartAccount.sol"; 5 | 6 | /// @title Simulate Transaction Accessor - can be used with StorageAccessible to simulate Safe transactions 7 | /// @author Richard Meissner - 8 | contract SmartAccountInitCode { 9 | function getInitCode( 10 | address SmartAccountProxyFactory, 11 | address SmartAccountImplement, 12 | address owner, 13 | uint256 salt 14 | ) public pure returns (bytes memory initCode) { 15 | bytes memory initializeData = abi.encodeWithSignature( 16 | "Initialize(address)", 17 | owner 18 | ); 19 | 20 | bytes memory data = abi.encodeWithSignature( 21 | "createAccount(address,bytes,uint256)", 22 | SmartAccountImplement, 23 | initializeData, 24 | salt 25 | ); 26 | 27 | initCode = abi.encodePacked( 28 | abi.encodePacked(SmartAccountProxyFactory, data) 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /contracts/wallet/helper/SmartAccountInitCodeV06.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0-only 2 | pragma solidity ^0.8.12; 3 | 4 | /// @title Simulate Transaction Accessor - can be used with StorageAccessible to simulate Safe transactions 5 | /// @author Richard Meissner - 6 | contract SmartAccountInitCodeV06 { 7 | 8 | function getInitCode( 9 | address smartAccountProxyV2Factory, 10 | address smartAccountV2Implement, 11 | address owner, 12 | bytes memory extradata, 13 | uint256 salt 14 | ) public pure returns (bytes memory initCode) { 15 | bytes memory initializeData = abi.encode( 16 | owner, 17 | extradata 18 | ); 19 | 20 | bytes memory data = abi.encodeWithSignature( 21 | "createAccount(address,bytes,uint256)", 22 | smartAccountV2Implement, 23 | initializeData, 24 | salt 25 | ); 26 | 27 | initCode = abi.encodePacked( 28 | abi.encodePacked(smartAccountProxyV2Factory, data) 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /contracts/wallet/helper/SmartAccountStorage.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0-only 2 | pragma solidity ^0.8.12; 3 | 4 | /// @title SmartAccountStorage - Storage layout of the SmartAccount contracts to be used in libraries 5 | contract SmartAccountStorage { 6 | // From /common/Singleton.sol 7 | address internal singleton; 8 | // From /common/ModuleManager.sol 9 | mapping(address => address) internal modules; 10 | 11 | // From /common/OwnerManager.sol 12 | mapping(address => address) internal owners; 13 | 14 | uint256 internal ownerCount; 15 | uint256 internal threshold; 16 | 17 | // From /SmartContract.sol 18 | bytes32 internal nonce; 19 | } 20 | -------------------------------------------------------------------------------- /contracts/wallet/v1/SmartAccount.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0-only 2 | pragma solidity ^0.8.12; 3 | 4 | import "../../interfaces/IValidations.sol"; 5 | import "../base/SignatureManager.sol"; 6 | import "../base/ModuleManager.sol"; 7 | import "../base/OwnerManager.sol"; 8 | import "../base/FallbackManager.sol"; 9 | import "../base/GuardManager.sol"; 10 | import "../common/EtherPaymentFallback.sol"; 11 | import "../common/Singleton.sol"; 12 | import "../common/SignatureDecoder.sol"; 13 | import "../common/SecuredTokenTransfer.sol"; 14 | 15 | contract SmartAccount is 16 | EtherPaymentFallback, 17 | Singleton, 18 | ModuleManager, 19 | OwnerManager, 20 | SignatureDecoder, 21 | SecuredTokenTransfer, 22 | FallbackManager, 23 | GuardManager, 24 | SignatureManager 25 | { 26 | IValidations public immutable VALIDATIONS; 27 | 28 | address public immutable SIMULATION; 29 | address public immutable FALLBACKHANDLER; 30 | 31 | constructor( 32 | address _entryPoint, 33 | address _simulation, 34 | address _fallbackHandler, 35 | address _validations, 36 | string memory _name, 37 | string memory _version 38 | ) SignatureManager(_entryPoint, _name, _version) { 39 | SIMULATION = _simulation; 40 | FALLBACKHANDLER = _fallbackHandler; 41 | VALIDATIONS = IValidations(_validations); 42 | } 43 | 44 | modifier onlyEntryPointOrSimulation() { 45 | require( 46 | msg.sender == address(entryPoint()) || msg.sender == SIMULATION, 47 | "Not from entrypoint" 48 | ); 49 | _; 50 | } 51 | 52 | modifier onlyWhiteListedBundler() { 53 | VALIDATIONS.validateBundlerWhiteList(tx.origin); 54 | _; 55 | } 56 | 57 | modifier onlyWhiteListedModule() { 58 | VALIDATIONS.validateModuleWhitelist(msg.sender); 59 | _; 60 | } 61 | 62 | function Initialize(address _owner) external { 63 | require(getOwner() == address(0), "account: have set up"); 64 | initializeOwners(_owner); 65 | initializeFallbackHandler(FALLBACKHANDLER); 66 | initializeModules(); 67 | } 68 | 69 | function validateUserOp( 70 | UserOperation calldata userOp, 71 | bytes32 userOpHash, 72 | uint256 missingAccountFunds 73 | ) 74 | public 75 | override 76 | onlyEntryPointOrSimulation 77 | returns (uint256 validationData) 78 | { 79 | validationData = super.validateUserOp( 80 | userOp, 81 | userOpHash, 82 | missingAccountFunds 83 | ); 84 | } 85 | 86 | function execTransactionFromEntrypoint( 87 | address to, 88 | uint256 value, 89 | bytes calldata data 90 | ) public onlyEntryPointOrSimulation onlyWhiteListedBundler { 91 | executeWithGuard(to, value, data); 92 | } 93 | 94 | function execTransactionFromEntrypointBatch( 95 | ExecuteParams[] calldata _params 96 | ) external onlyEntryPointOrSimulation onlyWhiteListedBundler { 97 | executeWithGuardBatch(_params); 98 | } 99 | 100 | function execTransactionFromEntrypointBatchRevertOnFail( 101 | ExecuteParams[] calldata _params 102 | ) external onlyEntryPointOrSimulation onlyWhiteListedBundler { 103 | execTransactionBatchRevertOnFail(_params); 104 | } 105 | 106 | function execTransactionFromModule( 107 | address to, 108 | uint256 value, 109 | bytes calldata data, 110 | Enum.Operation operation 111 | ) public override onlyWhiteListedModule { 112 | if (operation == Enum.Operation.Call) { 113 | ModuleManager.execTransactionFromModule(to, value, data, operation); 114 | } else { 115 | address originalFallbackHandler = getFallbackHandler(); 116 | 117 | setFallbackHandler(msg.sender, true); 118 | ModuleManager.execTransactionFromModule(to, value, data, operation); 119 | setFallbackHandler(originalFallbackHandler, false); 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /contracts/wallet/v1/SmartAccountProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0-only 2 | pragma solidity ^0.8.12; 3 | 4 | import "../../interfaces/ISmartAccountProxy.sol"; 5 | 6 | /// @title SmartAccountProxy - Generic proxy contract allows to execute all transactions applying the code of a master contract. 7 | contract SmartAccountProxy is ISmartAccountProxy { 8 | // singleton always needs to be first declared variable, to ensure that it is at the same location in the contracts to which calls are delegated. 9 | // To reduce deployment costs this variable is internal and needs to be retrieved via `getStorageAt` 10 | address internal singleton; 11 | 12 | /// @dev Constructor function sets address of singleton contract. 13 | /// @param _singleton Singleton address. 14 | function initialize(address _singleton, bytes memory _initdata) external { 15 | require(singleton == address(0), "Initialized already"); 16 | require(_singleton != address(0), "Invalid singleton address provided"); 17 | singleton = _singleton; 18 | 19 | (bool success, ) = _singleton.delegatecall(_initdata); 20 | require(success, "init failed"); 21 | } 22 | 23 | function masterCopy() external view returns (address) { 24 | return singleton; 25 | } 26 | 27 | /// @dev Fallback function forwards all transactions and returns all received return data. 28 | fallback() external payable { 29 | // solhint-disable-next-line no-inline-assembly 30 | assembly { 31 | let _singleton := and( 32 | sload(0), 33 | 0xffffffffffffffffffffffffffffffffffffffff 34 | ) 35 | calldatacopy(0, 0, calldatasize()) 36 | let success := delegatecall( 37 | gas(), 38 | _singleton, 39 | 0, 40 | calldatasize(), 41 | 0, 42 | 0 43 | ) 44 | returndatacopy(0, 0, returndatasize()) 45 | if eq(success, 0) { 46 | revert(0, returndatasize()) 47 | } 48 | return(0, returndatasize()) 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /contracts/wallet/v2/AccountFactoryProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0-only 2 | pragma solidity ^0.8.12; 3 | 4 | import {AccountFactoryStorageBase} from "./AccountFactoryStorage.sol"; 5 | 6 | contract AccountFactoryProxy is AccountFactoryStorageBase { 7 | 8 | event ImplementationSet(address indexed oldImpl, address indexed newImpl); 9 | 10 | constructor(address impl, address _owner, address walletTemplate) { 11 | implementation = impl; 12 | owner = _owner; 13 | 14 | (bool success, bytes memory returnData) = implementation.delegatecall( 15 | abi.encodeWithSignature("initialize(address)", 16 | walletTemplate 17 | )); 18 | require(success, string(returnData)); 19 | } 20 | 21 | function setImplementation(address impl) external onlyOwner { 22 | require(impl != address(0), "implementation is address 0"); 23 | address oldImpl = implementation; 24 | implementation = impl; 25 | emit ImplementationSet(oldImpl, impl); 26 | } 27 | 28 | receive() external payable { 29 | revert("do not transfer to me"); 30 | } 31 | 32 | /// @dev Fallback function forwards all transactions and returns all received return data. 33 | fallback() external payable { 34 | // solhint-disable-next-line no-inline-assembly 35 | assembly { 36 | let _singleton := and( 37 | sload(0), 38 | 0xffffffffffffffffffffffffffffffffffffffff 39 | ) 40 | calldatacopy(0, 0, calldatasize()) 41 | let success := delegatecall( 42 | gas(), 43 | _singleton, 44 | 0, 45 | calldatasize(), 46 | 0, 47 | 0 48 | ) 49 | returndatacopy(0, 0, returndatasize()) 50 | if eq(success, 0) { 51 | revert(0, returndatasize()) 52 | } 53 | return(0, returndatasize()) 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /contracts/wallet/v2/AccountFactoryStorage.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0-only 2 | pragma solidity ^0.8.12; 3 | 4 | contract AccountFactoryStorageBase { 5 | address public implementation; // keep it the 1st slot 6 | address public owner; // keep it the 2nd slot 7 | uint8 public initialized; // for initialize method. 8 | 9 | event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); 10 | 11 | modifier onlyOwner() { 12 | require(msg.sender == owner, "OnlyOwner allowed"); 13 | _; 14 | } 15 | 16 | function transferOwnership(address newOwner) public virtual onlyOwner { 17 | require(newOwner != address(0), "Ownable: new owner is the zero address"); 18 | address oldOwner = owner; 19 | owner = newOwner; 20 | emit OwnershipTransferred(oldOwner, newOwner); 21 | } 22 | } 23 | 24 | contract AccountFactoryStorage is AccountFactoryStorageBase { 25 | // SmartAccount template => bool, save the 26 | mapping(address => bool) public safeSingleton; 27 | // wallet address => bool, save accounts created by this Factory. 28 | // mapping(address => bool) public walletWhiteList; 29 | 30 | 31 | // NOTICE: add new storage variables below 32 | } -------------------------------------------------------------------------------- /contracts/wallet/v2/SmartAccountProxyV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0-only 2 | pragma solidity ^0.8.12; 3 | 4 | import "../../interfaces/ISmartAccountProxy.sol"; 5 | 6 | /// @title SmartAccountProxy - Generic proxy contract allows to execute all transactions applying the code of a master contract. 7 | contract SmartAccountProxyV2 is ISmartAccountProxy { 8 | // singleton always needs to be first declared variable, to ensure that it is at the same location in the contracts to which calls are delegated. 9 | // To reduce deployment costs this variable is internal and needs to be retrieved via `getStorageAt` 10 | address internal singleton; 11 | 12 | /// @dev Constructor function sets address of singleton contract. 13 | /// @param _singleton Singleton address. 14 | function initialize(address _singleton, bytes memory _initdata) external { 15 | require(singleton == address(0), "Initialized already"); 16 | require(_singleton != address(0), "Invalid singleton address provided"); 17 | singleton = _singleton; 18 | 19 | (address creator, bytes memory params) = abi.decode(_initdata,(address, bytes)); 20 | 21 | (bool success, bytes memory returnData) = _singleton.delegatecall( 22 | abi.encodeWithSignature("initialize(address,bytes)", creator, params) 23 | ); 24 | require(success, string(returnData)); 25 | } 26 | 27 | function masterCopy() external view returns (address) { 28 | return singleton; 29 | } 30 | 31 | /// @dev Fallback function forwards all transactions and returns all received return data. 32 | fallback() external payable { 33 | // solhint-disable-next-line no-inline-assembly 34 | assembly { 35 | let _singleton := and( 36 | sload(0), 37 | 0xffffffffffffffffffffffffffffffffffffffff 38 | ) 39 | calldatacopy(0, 0, calldatasize()) 40 | let success := delegatecall( 41 | gas(), 42 | _singleton, 43 | 0, 44 | calldatasize(), 45 | 0, 46 | 0 47 | ) 48 | returndatacopy(0, 0, returndatasize()) 49 | if eq(success, 0) { 50 | revert(0, returndatasize()) 51 | } 52 | return(0, returndatasize()) 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /contracts/wallet/v2/SmartAccountV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-3.0-only 2 | pragma solidity ^0.8.12; 3 | 4 | import "../../interfaces/IValidations.sol"; 5 | import "../base/SignatureManager.sol"; 6 | import "../base/ModuleManager.sol"; 7 | import "../base/OwnerManager.sol"; 8 | import "../base/FallbackManager.sol"; 9 | import "../base/GuardManager.sol"; 10 | import "../common/EtherPaymentFallback.sol"; 11 | import "../common/Singleton.sol"; 12 | import "../common/SignatureDecoder.sol"; 13 | import "../common/SecuredTokenTransfer.sol"; 14 | 15 | contract SmartAccountV2 is 16 | EtherPaymentFallback, 17 | Singleton, 18 | ModuleManager, 19 | OwnerManager, 20 | SignatureDecoder, 21 | SecuredTokenTransfer, 22 | FallbackManager, 23 | GuardManager, 24 | SignatureManager 25 | { 26 | IValidations public immutable VALIDATIONS; 27 | address public immutable FALLBACKHANDLER; 28 | 29 | constructor( 30 | address _entryPoint, 31 | address _fallbackHandler, 32 | address _validations, 33 | string memory _name, 34 | string memory _version 35 | ) SignatureManager(_entryPoint, _name, _version) { 36 | FALLBACKHANDLER = _fallbackHandler; 37 | VALIDATIONS = IValidations(_validations); 38 | } 39 | 40 | modifier onlyEntryPoint() { 41 | require(msg.sender == address(entryPoint()), "Not from entrypoint"); 42 | _; 43 | } 44 | 45 | modifier onlyWhiteListedBundler() { 46 | VALIDATIONS.validateBundlerWhiteList(tx.origin); 47 | _; 48 | } 49 | 50 | modifier onlyWhiteListedModule() { 51 | VALIDATIONS.validateModuleWhitelist(msg.sender); 52 | _; 53 | } 54 | 55 | function initialize( 56 | address creator, 57 | bytes memory /* place holder for future */ 58 | ) external { 59 | require(getOwner() == address(0), "account: have set up"); 60 | // set creator as owner by default. 61 | initializeOwners(creator); 62 | initializeFallbackHandler(FALLBACKHANDLER); 63 | initializeModules(); 64 | } 65 | 66 | function nonce() public view virtual returns (uint256) { 67 | return ENTRYPOINT.getNonce(address(this), 0); 68 | } 69 | 70 | function validateUserOp( 71 | UserOperation calldata userOp, 72 | bytes32 userOpHash, 73 | uint256 missingAccountFunds 74 | ) 75 | public 76 | override 77 | onlyEntryPoint 78 | onlyWhiteListedBundler 79 | returns (uint256 validationData) 80 | { 81 | validationData = super.validateUserOp( 82 | userOp, 83 | userOpHash, 84 | missingAccountFunds 85 | ); 86 | } 87 | 88 | function execTransactionFromEntrypoint( 89 | address to, 90 | uint256 value, 91 | bytes calldata data 92 | ) public onlyEntryPoint { 93 | executeWithGuard(to, value, data); 94 | } 95 | 96 | function execTransactionFromEntrypointBatch( 97 | ExecuteParams[] calldata _params 98 | ) external onlyEntryPoint { 99 | executeWithGuardBatch(_params); 100 | } 101 | 102 | function execTransactionFromEntrypointBatchRevertOnFail( 103 | ExecuteParams[] calldata _params 104 | ) external onlyEntryPoint { 105 | execTransactionBatchRevertOnFail(_params); 106 | } 107 | 108 | function execTransactionFromModule( 109 | address to, 110 | uint256 value, 111 | bytes calldata data, 112 | Enum.Operation operation 113 | ) public override onlyWhiteListedModule { 114 | if (operation == Enum.Operation.Call) { 115 | ModuleManager.execTransactionFromModule(to, value, data, operation); 116 | } else { 117 | address originalFallbackHandler = getFallbackHandler(); 118 | 119 | setFallbackHandler(msg.sender, true); 120 | ModuleManager.execTransactionFromModule(to, value, data, operation); 121 | setFallbackHandler(originalFallbackHandler, false); 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import "@nomicfoundation/hardhat-toolbox"; 2 | import "hardhat-gas-reporter"; 3 | import "@typechain/hardhat"; 4 | import dotenv from "dotenv"; 5 | import { HardhatUserConfig } from "hardhat/config"; 6 | 7 | import "hardhat-contract-sizer"; 8 | 9 | /** @type import('hardhat/config').HardhatUserConfig */ 10 | 11 | dotenv.config(); 12 | 13 | const accounts = process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [""]; 14 | const config: HardhatUserConfig = { 15 | contractSizer: { 16 | alphaSort: false, 17 | runOnCompile: false, 18 | disambiguatePaths: false, 19 | }, 20 | 21 | solidity: { 22 | compilers: [ 23 | { 24 | version: "0.5.16", 25 | settings: { 26 | optimizer: { 27 | enabled: true, 28 | runs: 999999, 29 | }, 30 | }, 31 | }, 32 | { 33 | version: "0.6.6", 34 | settings: { 35 | optimizer: { 36 | enabled: true, 37 | runs: 200, 38 | }, 39 | }, 40 | }, 41 | { 42 | version: "0.7.6", 43 | settings: { 44 | optimizer: { 45 | enabled: true, 46 | runs: 200, 47 | }, 48 | }, 49 | }, 50 | { 51 | version: "0.8.17", 52 | settings: { 53 | optimizer: { 54 | enabled: true, 55 | runs: 200, 56 | }, 57 | }, 58 | }, 59 | { 60 | version: "0.8.9", 61 | settings: { 62 | optimizer: { 63 | enabled: true, 64 | runs: 200, 65 | }, 66 | }, 67 | }, 68 | { 69 | version: "0.8.7", 70 | settings: { 71 | optimizer: { 72 | enabled: true, 73 | runs: 200, 74 | }, 75 | }, 76 | }, 77 | { 78 | version: "0.8.10", 79 | settings: { 80 | optimizer: { 81 | enabled: true, 82 | runs: 200, 83 | }, 84 | }, 85 | }, 86 | { 87 | version: "0.8.14", 88 | settings: { 89 | optimizer: { 90 | enabled: true, 91 | runs: 200, 92 | }, 93 | }, 94 | }, 95 | ], 96 | }, 97 | 98 | networks: { 99 | OKCMainnet: { 100 | url: "https://exchainrpc.okex.org", 101 | accounts, 102 | gas: 5000000, 103 | }, 104 | BNBMainnet: { 105 | url: "https://bsc.publicnode.com", 106 | accounts, 107 | gas: 5000000, 108 | }, 109 | AVAXMainnet: { 110 | url: "https://avalanche.blockpi.network/v1/rpc/public", 111 | accounts, 112 | gas: 5000000, 113 | }, 114 | eth: { 115 | url: "https://rpc.ankr.com/eth", 116 | accounts, 117 | // gas: 3000000, 118 | }, 119 | ARBMainnet: { 120 | url: "https://arb1.arbitrum.io/rpc", 121 | accounts, 122 | gas: 50000000, 123 | }, 124 | OPMainnet: { 125 | url: "https://endpoints.omniatech.io/v1/op/mainnet/public", 126 | accounts, 127 | gas: 5000000, 128 | }, 129 | LineaMainnet: { 130 | url: "https://1rpc.io/linea", 131 | accounts, 132 | gas: 5000000, 133 | }, 134 | PolygonMainnet: { 135 | url: "https://rpc-mainnet.matic.quiknode.pro", 136 | accounts, 137 | gas: 5000000, 138 | gasPrice: 240000000000, 139 | }, 140 | PolygonMainnet: { 141 | url: "https://rpc-mainnet.matic.quiknode.pro", 142 | accounts, 143 | gas: 5000000, 144 | gasPrice: 240000000000, 145 | }, 146 | local: { 147 | url: "http://127.0.0.1:8545/", 148 | accounts, 149 | gas: 5000000, 150 | gasPrice: 240000000000, 151 | }, 152 | hardhat: { 153 | allowUnlimitedContractSize: true, 154 | forking: { 155 | // url: "https://rpc.ankr.com/eth", //ETH 156 | // url: "https://1rpc.io/avax/c" //AVAX 157 | // url: "https://1rpc.io/linea" //Linea 158 | // url: "https://mainnet.optimism.io",//OP 159 | // url: "https://arb1.arbitrum.io/rpc" //ARB 160 | // url: "https://rpc-mainnet.matic.quiknode.pro", //Polygon 161 | // url: "https://bsc-dataseed4.binance.org", //BNB 162 | url: "https://1rpc.io/linea" //Linea 163 | }, 164 | gas: 5000000, 165 | gasPrice: 1000000000000, 166 | blockGasLimit: 2000000000, 167 | }, 168 | }, 169 | typechain: { 170 | outDir: "types", 171 | }, 172 | etherscan: { 173 | apiKey: "", 174 | customChains: [ 175 | { 176 | network: "ETH", 177 | chainId: 1, 178 | urls: { 179 | apiURL: "https://www.oklink.com/api/explorer/v1/contract/verify/async/api/ETH", 180 | browserURL: "https://www.oklink.com/cn/ETH" 181 | } 182 | } 183 | ] 184 | } 185 | }; 186 | 187 | export default config; 188 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "test": "hardhat test" 4 | }, 5 | "dependencies": { 6 | "@account-abstraction/contracts": "^0.6.0", 7 | "@chainlink/contracts": "^0.6.0", 8 | "@gnosis.pm/safe-contracts": "^1.3.0", 9 | "@nomicfoundation/hardhat-network-helpers": "^1.0.7", 10 | "@openzeppelin/contracts": "^4.8.1", 11 | "@openzeppelin/contracts-upgradeable": "^4.8.0", 12 | "@uniswap/v2-periphery": "^1.1.0-beta.0", 13 | "dotenv": "^16.3.1", 14 | "hardhat": "^2.16.1" 15 | }, 16 | "devDependencies": { 17 | "@nomicfoundation/hardhat-toolbox": "^2.0.0", 18 | "@nomiclabs/hardhat-etherscan": "^3.1.8", 19 | "@typechain/hardhat": "^6.1.6", 20 | "@types/chai": "^4.3.6", 21 | "@types/mocha": "^9.1.1", 22 | "@types/node": "^20.7.0", 23 | "@uniswap/v3-core": "^1.0.1", 24 | "@uniswap/v3-periphery": "^1.4.3", 25 | "chai": "^4.3.8", 26 | "hardhat-contract-sizer": "^2.9.0", 27 | "hardhat-gas-reporter": "^1.0.9", 28 | "solidity-coverage": "^0.8.4", 29 | "ts-node": "^10.9.1", 30 | "typechain": "^8.3.1", 31 | "typescript": "^5.2.2" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /scripts/createTestingEnvironment.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const { ethers, network } = require("hardhat"); 3 | 4 | const TestingEnvironmentInformation = JSON.parse(fs.readFileSync("TestingEnvironmentInformation.json")); 5 | 6 | async function instantiateContracts() { 7 | const contractInstances = {}; // 用于存储合约实例的对象 8 | 9 | for (const contractName in TestingEnvironmentInformation.ContractOnChain) { 10 | const contractInfo = TestingEnvironmentInformation.ContractOnChain[contractName]; 11 | 12 | const contractFactory = await ethers 13 | .getContractFactory(contractInfo.ContractPath); 14 | 15 | const contractInstance = await contractFactory.attach(contractInfo.ContractAddress); 16 | 17 | contractInstances[contractName] = contractInstance; 18 | 19 | console.log(`${contractName} ${contractInstance.address}`); 20 | } 21 | 22 | 23 | for (const contractName in TestingEnvironmentInformation.ContractSetNewCode) { 24 | const contractInfo = TestingEnvironmentInformation.ContractSetNewCode[contractName]; 25 | 26 | const contractFactory = await ethers 27 | .getContractFactory(contractInfo.ContractPath); 28 | 29 | const contractReDeploy = await contractFactory.deploy(); 30 | 31 | const contractInstance = await contractFactory.attach(contractInfo.ContractAddress); 32 | 33 | await network.provider.send("hardhat_setCode", [ 34 | contractInstance.address, 35 | await ethers.provider.getCode(contractReDeploy.address) 36 | ]); 37 | 38 | contractInstances[contractName] = contractInstance; 39 | 40 | console.log(`${contractName} ${contractInstance.address}`); 41 | } 42 | 43 | 44 | 45 | return contractInstances; 46 | } 47 | 48 | 49 | async function createTestingEnvironment() { 50 | let bundler, contractInstances 51 | 52 | BUNDLERADDRESS = TestingEnvironmentInformation["bundler"]; 53 | 54 | await network.provider.send("hardhat_setBalance", [ 55 | TestingEnvironmentInformation["bundler"], 56 | "0x1000000000000000000000000000", 57 | ]); 58 | 59 | bundler = await network.provider.request({ 60 | method: "hardhat_impersonateAccount", 61 | params: [BUNDLERADDRESS], 62 | }); 63 | 64 | bundler = await ethers.getSigner(BUNDLERADDRESS); 65 | 66 | contractInstances = await instantiateContracts(); 67 | 68 | return { bundler: bundler, contractInstances: contractInstances } 69 | } 70 | 71 | 72 | 73 | module.exports = { 74 | createTestingEnvironment 75 | }; 76 | 77 | 78 | -------------------------------------------------------------------------------- /scripts/deploySmartAccountV2.0.1.js: -------------------------------------------------------------------------------- 1 | const { ethers, network } = require("hardhat"); 2 | require("dotenv").config(); 3 | 4 | let contractAddress = {}, 5 | salt, 6 | owner, 7 | DeployFactory, 8 | SmartAccountV2, 9 | errorHotFixAddress, 10 | AccountFactoryProxy; 11 | 12 | const DeployInformation = { 13 | DeployFactory: "0xFaC897544659Fb136C064d5428947f5BC9cC1Fa2", 14 | salt: "0x0000000000000000000000000000000000000000000000000000000000000003", 15 | SmartAccountV2: { 16 | entryPoint: "0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789", 17 | defaultCallbackHandler: "0xA0be66C8d60A3ca53E83b5f376C6259b8de02586", 18 | validations: "0x228E505D1F21948968fB52794ea823f65053A294", 19 | name: "SA", 20 | version: "2.0.1", 21 | }, 22 | AccountFactoryProxy: { 23 | address: "0x22fF1Dc5998258Faa1Ea45a776B57484f8Ab80A2" 24 | } 25 | } 26 | 27 | async function getDeployFactory() { 28 | DeployFactory = await ethers 29 | .getContractFactory("DeployFactory") 30 | .then((f) => 31 | f.attach( 32 | DeployInformation["DeployFactory"], 33 | ), 34 | ); 35 | 36 | return DeployFactory; 37 | } 38 | 39 | async function deployContractByDeployFactory( 40 | DeployFactory, 41 | contractFactory, 42 | DeployInformation, 43 | salt, 44 | ) { 45 | const initCode = ethers.utils.solidityPack( 46 | ["bytes", "bytes"], 47 | [ethers.utils.hexDataSlice(contractFactory.bytecode, 0), DeployInformation], 48 | ); 49 | 50 | const address = await DeployFactory.getAddress(initCode, salt); 51 | 52 | const code = await ethers.provider.getCode(address); 53 | 54 | if (code !== "0x") { 55 | return address; 56 | } 57 | 58 | await DeployFactory.deploy(initCode, salt).then((tx) => tx.wait()); 59 | 60 | return address; 61 | } 62 | 63 | async function deploy( 64 | contractAddress, 65 | DeployFactory, 66 | contractName, 67 | constructorType, 68 | constructorArgs, 69 | salt, 70 | ) { 71 | const contractFactory = await ethers.getContractFactory(contractName); 72 | 73 | const DeployInformation = ethers.utils.defaultAbiCoder.encode( 74 | constructorType, 75 | constructorArgs, 76 | ); 77 | 78 | const contract = await contractFactory.attach( 79 | await deployContractByDeployFactory( 80 | DeployFactory, 81 | contractFactory, 82 | DeployInformation, 83 | salt, 84 | ), 85 | ); 86 | 87 | console.log(contractName + " " + contract.address); 88 | contractAddress[contractName] = contract.address; 89 | 90 | return contract; 91 | } 92 | 93 | async function deployAllContract() { 94 | DeployFactory = await getDeployFactory(); 95 | 96 | SmartAccountV2 = await deploy( 97 | contractAddress, 98 | DeployFactory, 99 | "SmartAccountV2", 100 | ["address", "address", "address", "string", "string"], 101 | [ 102 | DeployInformation["SmartAccountV2"]["entryPoint"], 103 | DeployInformation["SmartAccountV2"]["defaultCallbackHandler"], 104 | DeployInformation["SmartAccountV2"]["validations"], 105 | DeployInformation["SmartAccountV2"]["name"], 106 | DeployInformation["SmartAccountV2"]["version"], 107 | ], 108 | salt 109 | ); 110 | } 111 | 112 | async function setAllConfig() { 113 | AccountFactoryProxy = await ethers 114 | .getContractFactory("AccountFactoryV2") 115 | .then((f) => f.attach(DeployInformation["AccountFactoryProxy"]["address"])); 116 | /// set new singleton 117 | tx = await AccountFactoryProxy.setSafeSingleton(SmartAccountV2.address, true) 118 | await tx.wait(); 119 | console.log("AccountFactoryProxy setSafeSingleton", tx.hash); 120 | 121 | let hasSet = await AccountFactoryProxy.safeSingleton(SmartAccountV2.address); 122 | console.log(hasSet); 123 | 124 | let isSingle = await AccountFactoryProxy.safeSingleton(errorHotFixAddress); 125 | console.log("last hotfix singleton is:",errorHotFixAddress); 126 | 127 | if(isSingle) { 128 | let cancleTx = await AccountFactoryProxy.setSafeSingleton(errorHotFixAddress, false); 129 | await cancleTx.wait(); 130 | console.log("Cancle last hotfix singleton:", cancleTx.hash); 131 | } 132 | } 133 | 134 | 135 | async function main() { 136 | if ((await hre.ethers.provider.getNetwork()).chainId.toString() == 31337) { 137 | // ME = "0x794b93902449c524c3158f9e101204ecb2057f2e"; 138 | // owner = await network.provider.request({ 139 | // method: "hardhat_impersonateAccount", 140 | // params: [ME], 141 | // }); 142 | 143 | // await network.provider.send("hardhat_setBalance", [ 144 | // ME, 145 | // "0x1000000000000000000000000", 146 | // ]); 147 | 148 | // await network.provider.request({ 149 | // method: "hardhat_impersonateAccount", 150 | // params: [ME], 151 | // }); 152 | 153 | 154 | owner = await ethers.getSigner(); 155 | await network.provider.send("hardhat_setBalance", [ 156 | owner.address, 157 | "0x1000000000000000000000000", 158 | ]); 159 | } else { 160 | owner = await ethers.getSigner(); 161 | } 162 | salt = DeployInformation["salt"]; 163 | console.log("ownerAddress", owner.address); 164 | console.log("salt", salt); 165 | errorHotFixAddress = "0x9f73ECda4e7336FD854b8eE737E7753e45B0a6A0"; 166 | 167 | await deployAllContract(); 168 | await setAllConfig(); 169 | } 170 | 171 | main(); 172 | -------------------------------------------------------------------------------- /scripts/smokeTest.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | let { ethers } = require("hardhat"); 3 | const { expect } = require("chai"); 4 | const Utils = require("./Utils.js"); 5 | 6 | // Define the global variables 7 | let owner, 8 | sender, 9 | EntryPoint, 10 | SmartAccountV2, 11 | AccountFactoryProxy, 12 | TestAccountV2; 13 | 14 | async function instantiateContracts() { 15 | // Read the contents of the JSON file 16 | let data = fs.readFileSync("ContractAddress.json"); 17 | 18 | // Parse the JSON content into a JavaScript object 19 | let addresses = JSON.parse(data); 20 | 21 | // Instantiate each contract with its corresponding factory 22 | EntryPoint = await ethers 23 | .getContractFactory("contracts/@eth-infinitism-v0.6/core/EntryPoint.sol:EntryPoint") 24 | .then((f) => f.attach(addresses["EntryPointV06"])); 25 | SmartAccountV2 = await ethers 26 | .getContractFactory("SmartAccountV2") 27 | .then((f) => f.attach(addresses["SmartAccountV2"])); 28 | AccountFactoryProxy = await ethers 29 | .getContractFactory("AccountFactoryV2") 30 | .then((f) => f.attach(addresses["AccountFactoryProxy"])); 31 | } 32 | 33 | async function createAA() { 34 | owner = await ethers.getSigner(); 35 | 36 | TestAccountV2 = new Utils.SmartAccountV2({ ownerAddress: owner.address, random: 10001 }) 37 | 38 | await TestAccountV2.initialize({ 39 | SmartAccount: SmartAccountV2, 40 | SmartAccountProxyFactory: AccountFactoryProxy 41 | }) 42 | 43 | await TestAccountV2.deploy( 44 | { 45 | owner: owner, 46 | bundler: owner, 47 | EntryPoint: EntryPoint, 48 | SmartAccount: SmartAccountV2, 49 | SmartAccountProxyFactory: AccountFactoryProxy, 50 | sigType: 1, 51 | callGasLimit: 100000, 52 | verificationGasLimit: 1000000, 53 | preVerificationGas: 0, 54 | } 55 | ) 56 | } 57 | 58 | async function transferNativeToken() { 59 | let callData = SmartAccountV2.interface.encodeFunctionData( 60 | "execTransactionFromEntrypoint", 61 | [owner.address, ethers.utils.parseEther("0.001"), "0x"] 62 | ); 63 | 64 | let userOp = await TestAccountV2.generateSignedUOP({ 65 | sender: TestAccountV2.address, 66 | nonce: 1, 67 | initCode: "0x", 68 | callData: callData, 69 | paymasterAndData: "0x", 70 | owner: owner, 71 | SmartAccount: SmartAccountV2, 72 | EntryPoint: EntryPoint.address, 73 | sigType: 1, 74 | callGasLimit: 300000, 75 | verificationGasLimit: 1000000, 76 | preVerificationGas: 0, 77 | }); 78 | 79 | 80 | let gas = 2000000; 81 | if ((await hre.ethers.provider.getNetwork().chainId) == 42161) { 82 | gas = 30000000; 83 | } 84 | let balanceOfsenderBefore = await ethers.provider.getBalance(TestAccountV2.address); 85 | let tx = await EntryPoint.connect(owner).handleOps([userOp], owner.address, { 86 | gasLimit: gas, 87 | }); 88 | await tx.wait(); 89 | console.log("handleOps tx hash", tx.hash); 90 | 91 | let balanceOfsenderAfter = await ethers.provider.getBalance(TestAccountV2.address); 92 | 93 | console.log("balanceOfsenderBefore " + balanceOfsenderBefore); 94 | console.log("balanceOfsenderAfter " + balanceOfsenderAfter); 95 | 96 | await expect(tx).to.emit(EntryPoint, "UserOperationEvent"); 97 | } 98 | 99 | async function smokeTest() { 100 | await instantiateContracts(); 101 | 102 | await createAA(); 103 | 104 | await transferNativeToken(); 105 | } 106 | 107 | smokeTest(); 108 | 109 | module.exports = { 110 | createAA, 111 | transferNativeToken, 112 | smokeTest 113 | }; 114 | -------------------------------------------------------------------------------- /scripts/testInTestingEnvironment.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const { ethers, network } = require("hardhat"); 3 | const { createTestingEnvironment } = require("./createTestingEnvironment.js"); 4 | const { expect } = require("chai"); 5 | const Utils = require("./Utils.js"); 6 | 7 | let owner, 8 | bundler, 9 | EntryPoint, 10 | SmartAccountV2, 11 | AccountFactoryProxy, 12 | TestAccountV2; 13 | 14 | 15 | async function instantiateContracts() { 16 | testingEnvironment = await createTestingEnvironment() 17 | bundler = testingEnvironment.bundler; 18 | EntryPoint = testingEnvironment.contractInstances.EntryPoint; 19 | SmartAccountV2 = testingEnvironment.contractInstances.SmartAccountV2; 20 | AccountFactoryProxy = testingEnvironment.contractInstances.AccountFactoryV2 21 | } 22 | 23 | async function createAA() { 24 | owner = await ethers.getSigner(); 25 | 26 | await network.provider.send("hardhat_setBalance", [ 27 | owner.address, 28 | "0x1000000000000000000000000000", 29 | ]); 30 | 31 | 32 | TestAccountV2 = new Utils.SmartAccountV2({ ownerAddress: owner.address, random: 12345 }) 33 | 34 | await TestAccountV2.initialize({ 35 | SmartAccount: SmartAccountV2, 36 | SmartAccountProxyFactory: AccountFactoryProxy 37 | }) 38 | 39 | await TestAccountV2.deploy( 40 | { 41 | owner: owner, 42 | bundler: bundler, 43 | EntryPoint: EntryPoint, 44 | SmartAccount: SmartAccountV2, 45 | SmartAccountProxyFactory: AccountFactoryProxy, 46 | sigType: 1, 47 | callGasLimit: 0, 48 | verificationGasLimit: 700000, 49 | preVerificationGas: 0, 50 | } 51 | ) 52 | } 53 | 54 | async function transferNativeToken() { 55 | let callData = SmartAccountV2.interface.encodeFunctionData( 56 | "execTransactionFromEntrypoint", 57 | [owner.address, ethers.utils.parseEther("0.001"), "0x"] 58 | ); 59 | 60 | let userOp = await TestAccountV2.generateSignedUOP({ 61 | sender: TestAccountV2.address, 62 | nonce: 1, 63 | initCode: "0x", 64 | callData: callData, 65 | paymasterAndData: "0x", 66 | owner: owner, 67 | SmartAccount: SmartAccountV2, 68 | EntryPoint: EntryPoint.address, 69 | sigType: 1, 70 | callGasLimit: 300000, 71 | verificationGasLimit: 1000000, 72 | preVerificationGas: 0, 73 | }); 74 | 75 | 76 | let gas = 2000000; 77 | if ((await hre.ethers.provider.getNetwork().chainId) == 42161) { 78 | gas = 30000000; 79 | } 80 | let balanceOfsenderBefore = await ethers.provider.getBalance(TestAccountV2.address); 81 | let tx = await EntryPoint.connect(owner).handleOps([userOp], owner.address, { 82 | gasLimit: gas, 83 | }); 84 | await tx.wait(); 85 | console.log("handleOps tx hash", tx.hash); 86 | 87 | let balanceOfsenderAfter = await ethers.provider.getBalance(TestAccountV2.address); 88 | 89 | console.log("balanceOfsenderBefore " + balanceOfsenderBefore); 90 | console.log("balanceOfsenderAfter " + balanceOfsenderAfter); 91 | 92 | await expect(tx).to.emit(EntryPoint, "UserOperationEvent"); 93 | } 94 | 95 | async function testInTestingEnvironment() { 96 | await instantiateContracts(); 97 | await createAA(); 98 | await transferNativeToken(); 99 | } 100 | 101 | testInTestingEnvironment(); 102 | 103 | -------------------------------------------------------------------------------- /test/core/Validations.js: -------------------------------------------------------------------------------- 1 | let { loadFixture } = require("@nomicfoundation/hardhat-network-helpers"); 2 | let { expect } = require("chai"); 3 | 4 | describe("Validations", function () { 5 | async function deploy() { 6 | let [owner, alice, bob] = await ethers.getSigners(); 7 | let Validations = await ethers.getContractFactory("Validations"); 8 | let validations = await Validations.deploy(owner.address); 9 | 10 | return { 11 | owner, alice, bob, validations 12 | }; 13 | } 14 | 15 | describe("setBundlerOfficialWhitelistBatch", function () { 16 | it("should revert with setBundlerOfficialWhitelistBatch", async function () { 17 | const { owner, alice, bob, validations } = 18 | await loadFixture(deploy); 19 | await expect(validations.setBundlerOfficialWhitelistBatch([alice.address, bob.address], [true, true, true])).to.be.revertedWith("incorrect arrary length") 20 | await expect(validations.setBundlerOfficialWhitelistBatch([alice.address, bob.address], [true])).to.be.revertedWith("incorrect arrary length") 21 | }); 22 | 23 | it("should success with emit event", async function () { 24 | const { owner, alice, bob, validations } = await loadFixture(deploy); 25 | const tx = await validations.setBundlerOfficialWhitelistBatch( 26 | [alice.address, bob.address], 27 | [true, true] 28 | ); 29 | const rc = await tx.wait(); 30 | 31 | expect(tx).to.emit(validations, "BundlerWhitelistSet"); 32 | 33 | expect(rc.events.find((event) => event.event === "BundlerWhitelistSet" && event.args.bundler === alice.address).args.allowed).to.equal(true); 34 | 35 | expect(rc.events.find((event) => event.event === "BundlerWhitelistSet" && event.args.bundler === bob.address).args.allowed).to.equal(true); 36 | 37 | }); 38 | it("should change storage correctly", async function () { 39 | const { owner, alice, bob, validations } = 40 | await loadFixture(deploy); 41 | await expect(await validations.officialBundlerWhiteList(alice.address)).to.equal(false); 42 | 43 | await expect(await validations.officialBundlerWhiteList(bob.address)).to.equal(false); 44 | 45 | await validations.setBundlerOfficialWhitelistBatch([alice.address, bob.address], [true, true]); 46 | 47 | await expect(await validations.officialBundlerWhiteList(alice.address)).to.equal(true); 48 | 49 | await expect(await validations.officialBundlerWhiteList(bob.address)).to.equal(true); 50 | 51 | await validations.setBundlerOfficialWhitelistBatch([alice.address, bob.address], [false, false]); 52 | 53 | await expect(await validations.officialBundlerWhiteList(alice.address)).to.equal(false); 54 | 55 | await expect(await validations.officialBundlerWhiteList(bob.address)).to.equal(false); 56 | }); 57 | 58 | 59 | }); 60 | 61 | }); 62 | -------------------------------------------------------------------------------- /test/paymaster/FreeGasPaymaster.js: -------------------------------------------------------------------------------- 1 | const { loadFixture } = require("@nomicfoundation/hardhat-network-helpers"); 2 | const { expect } = require("chai"); 3 | let Utils = require("../Utils.js"); 4 | 5 | describe("FreeGasPaymaster", function () { 6 | async function deploy() { 7 | let [EntryPoint, owner, signer, Alice] = await ethers.getSigners(); 8 | 9 | let FreeGasPaymasterFactory = await ethers.getContractFactory( 10 | "FreeGasPaymaster" 11 | ); 12 | 13 | let FreeGasPaymaster = await FreeGasPaymasterFactory.deploy( 14 | signer.address, 15 | owner.address 16 | ); 17 | 18 | await FreeGasPaymaster.connect(owner).addSupportedEntryPoint(EntryPoint.address); 19 | 20 | return { 21 | owner, 22 | signer, 23 | Alice, 24 | FreeGasPaymaster, 25 | EntryPoint, 26 | }; 27 | } 28 | 29 | it("should read default value correctly", async function () { 30 | const { owner, signer, FreeGasPaymaster, EntryPoint } = await loadFixture( 31 | deploy 32 | ); 33 | 34 | let defaultSigner = await FreeGasPaymaster.verifyingSigner(); 35 | await expect(defaultSigner).to.equal(signer.address); 36 | 37 | let defaultOwner = await FreeGasPaymaster.owner(); 38 | await expect(defaultOwner).to.equal(owner.address); 39 | 40 | let isSupportedEntryPoint = await FreeGasPaymaster.isSupportedEntryPoint(EntryPoint.address); 41 | await expect(isSupportedEntryPoint).to.equal(true); 42 | }); 43 | 44 | it("Should validatePaymasterUserOp", async function () { 45 | const { owner, signer, Alice, FreeGasPaymaster, EntryPoint } = 46 | await loadFixture(deploy); 47 | 48 | let userOp = await Utils.generateFreePaymasterUOP( 49 | { 50 | signer: signer, 51 | FreeGasPaymaster: FreeGasPaymaster, 52 | sigTime: 1234567, 53 | }, 54 | ethers.constants.AddressZero, 55 | 0, 56 | "0x" 57 | ); 58 | 59 | let result = await FreeGasPaymaster.validatePaymasterUserOp( 60 | userOp, 61 | ethers.constants.HashZero, 62 | 0 63 | ); 64 | 65 | await expect(result[0]).to.equal("0x"); 66 | await expect(result[1].toNumber()).to.equal(1234567); 67 | }); 68 | }); 69 | -------------------------------------------------------------------------------- /test/wallet-v2/create-account.test.js: -------------------------------------------------------------------------------- 1 | const { expect } = require("chai"); 2 | const { ethers } = require("hardhat"); 3 | 4 | describe("WalletFactoryV2", function () { 5 | 6 | let accountFactoryProxy; 7 | let accountFactoryV2; 8 | 9 | // let accountProxyV2; 10 | let accountTemplate; 11 | 12 | let owner; 13 | let creator; 14 | let uslessAddress; 15 | 16 | beforeEach(async () => { 17 | const signers = await ethers.getSigners(); 18 | 19 | owner = signers[0]; 20 | creator = signers[1]; 21 | uslessAddress = signers[2]; 22 | 23 | const AF = await ethers.getContractFactory("AccountFactoryV2"); 24 | accountFactoryV2 = await AF.deploy(); 25 | await accountFactoryV2.deployed(); 26 | 27 | // const AP = await ethers.getContractFactory("SmartAccountProxyV2"); 28 | // accountProxyV2 = await AP.deploy(); 29 | // await accountProxyV2.deployed(); 30 | 31 | const A = await ethers.getContractFactory("SmartAccountV2"); 32 | accountTemplate = await A.deploy( 33 | owner.address, // entry point 34 | owner.address, // fallback 35 | owner.address, // validation 36 | "SmartAccount",// name 37 | "1.0.0" // version 38 | ); 39 | await accountTemplate.deployed(); 40 | 41 | 42 | const AFP = await ethers.getContractFactory("AccountFactoryProxy"); 43 | accountFactoryProxy = await AFP.deploy(accountFactoryV2.address, owner.address, accountTemplate.address); 44 | await accountFactoryProxy.deployed(); 45 | }) 46 | 47 | it("deploy empty extra bytes", async () => { 48 | let factory = await ethers.getContractAt("AccountFactoryV2", accountFactoryProxy.address); 49 | let coder = new ethers.utils.AbiCoder(); 50 | let initializer = coder.encode(["address", "bytes"], [creator.address, "0x"]); 51 | let proxyAddress = await factory.getAddress(accountTemplate.address, initializer, 0); 52 | 53 | await expect(factory.createAccount(accountTemplate.address, initializer, 0)) 54 | .to.emit(factory, "ProxyCreation") 55 | .withArgs(proxyAddress, accountTemplate.address); 56 | }) 57 | 58 | it("deploy account with signature bytes", async () => { 59 | let factory = await ethers.getContractAt("AccountFactoryV2", accountFactoryProxy.address); 60 | let coder = new ethers.utils.AbiCoder(); 61 | 62 | let r = '0xd693b532a80fed6392b428604171fb32fdbf953728a3a7ecc7d4062b1652c042' 63 | let s = '0x24e9c602ac800b983b035700a14b23f78a253ab762deab5dc27e3555a750b354' 64 | let v = 27; 65 | let params = coder.encode(["bytes32", "bytes32", "uint8"], [r, s, v]) 66 | 67 | let initializer = coder.encode(["address", "bytes"], [creator.address, params]); 68 | let proxyAddress = await factory.getAddress(accountTemplate.address, initializer, 0); 69 | 70 | // let tx = await factory.createAccount(accountTemplate.address, initializer, 0); 71 | await expect(factory.createAccount(accountTemplate.address, initializer, 0)) 72 | .to.emit(factory, "ProxyCreation") 73 | .withArgs(proxyAddress, accountTemplate.address); 74 | }) 75 | }); 76 | -------------------------------------------------------------------------------- /test/wallet-v2/erc1271-signature.test.js: -------------------------------------------------------------------------------- 1 | let { loadFixture } = require("@nomicfoundation/hardhat-network-helpers"); 2 | let { expect } = require("chai"); 3 | const { ethers } = require("hardhat"); 4 | 5 | describe("SmartAccount", function () { 6 | async function deploy() { 7 | let [owner] = await ethers.getSigners(); 8 | 9 | let EntryPoint0_6 = await ethers.getContractFactory( 10 | "MockEntryPointV06" 11 | ); 12 | let entrypoint0_6 = await EntryPoint0_6.deploy(); 13 | 14 | let SmartAccountV2 = await ethers.getContractFactory("SmartAccountV2"); 15 | 16 | let smartAccountV2 = await SmartAccountV2.deploy( 17 | entrypoint0_6.address, // entry point 18 | owner.address, // fallback 19 | owner.address, // validation 20 | "SmartAccount",// name 21 | "1.0.0" // version 22 | ); 23 | 24 | await smartAccountV2.initialize(owner.address, "0x"); 25 | 26 | return { 27 | owner, 28 | entrypoint0_6, 29 | smartAccountV2 30 | }; 31 | } 32 | 33 | describe("signature", function () { 34 | it("verify the signature correctly", async function () { 35 | let { 36 | smartAccountV2, owner 37 | } = await loadFixture(deploy); 38 | 39 | const signature = await owner._signTypedData( 40 | { 41 | name: "SmartAccount", 42 | version: "1.0.0", 43 | verifyingContract: smartAccountV2.address, 44 | chainId: (await ethers.provider.getNetwork()).chainId.toString() 45 | }, 46 | { isValidSignature: [{ name: "_hash", type: "bytes32" }] }, 47 | { "_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" } 48 | ) 49 | 50 | let r = await smartAccountV2.isValidSignature( 51 | "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", 52 | signature 53 | ); 54 | expect(r).to.equal("0x1626ba7e"); 55 | 56 | // try with invalid hash 57 | r = await smartAccountV2.isValidSignature( 58 | "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a471", 59 | signature 60 | ); 61 | expect(r).to.equal("0xffffffff"); 62 | }) 63 | }); 64 | }); -------------------------------------------------------------------------------- /test/wallet-v2/nonce.test.js: -------------------------------------------------------------------------------- 1 | let { loadFixture } = require("@nomicfoundation/hardhat-network-helpers"); 2 | let { expect } = require("chai"); 3 | 4 | describe("SmartAccount", function () { 5 | async function deploy() { 6 | let [owner] = await ethers.getSigners(); 7 | 8 | let EntryPoint0_6 = await ethers.getContractFactory( 9 | "MockEntryPointV06" 10 | ); 11 | let entrypoint0_6 = await EntryPoint0_6.deploy(); 12 | 13 | let SmartAccountV2 = await ethers.getContractFactory("SmartAccountV2"); 14 | 15 | let smartAccountV2 = await SmartAccountV2.deploy( 16 | entrypoint0_6.address, // entry point 17 | owner.address, // fallback 18 | owner.address, // validation 19 | "SmartAccount",// name 20 | "1.0.0" // version 21 | ); 22 | 23 | return { 24 | owner, 25 | entrypoint0_6, 26 | smartAccountV2 27 | }; 28 | } 29 | 30 | describe("nonce", function () { 31 | it("should return nonce correctly", async function () { 32 | let { 33 | entrypoint0_6, 34 | smartAccountV2 35 | } = await loadFixture(deploy); 36 | 37 | await expect(await smartAccountV2.nonce()).to.be.equal(0) 38 | await entrypoint0_6.incrementNonceForTarget(smartAccountV2.address, 0) 39 | await expect(await smartAccountV2.nonce()).to.be.equal(1) 40 | }); 41 | }); 42 | }); -------------------------------------------------------------------------------- /test/wallet/SmartAccountProxyFactory.js: -------------------------------------------------------------------------------- 1 | let { time, loadFixture } = require("@nomicfoundation/hardhat-network-helpers"); 2 | let { expect } = require("chai"); 3 | let { generateAccount, generateUOP } = require("../Utils.js"); 4 | 5 | describe("SmartAccountProxyFactory", function () { 6 | async function deploy() { 7 | let [owner, bundler, Alice] = await ethers.getSigners(); 8 | 9 | // setEntryPoint to owner to simplify testing 10 | let EntryPoint = owner.address; 11 | let SimulationContract = owner.address; 12 | 13 | let DefaultCallbackHandlerFactory = await ethers.getContractFactory( 14 | "contracts/wallet/handler/DefaultCallbackHandler.sol:DefaultCallbackHandler" 15 | ); 16 | let DefaultCallbackHandler = await DefaultCallbackHandlerFactory.deploy(); 17 | 18 | let Validations = await ethers.getContractFactory("Validations"); 19 | let validations = await Validations.deploy(owner.address); 20 | 21 | await validations.setBundlerOfficialWhitelist(owner.address, true); 22 | 23 | let SmartAccountFactory = await ethers.getContractFactory( 24 | "contracts/wallet/v1/SmartAccount.sol:SmartAccount" 25 | ); 26 | let SmartAccount = await SmartAccountFactory.deploy( 27 | EntryPoint, 28 | SimulationContract, 29 | DefaultCallbackHandler.address, 30 | validations.address, 31 | "SA", 32 | "1.0" 33 | ); 34 | 35 | let SmartAccountProxysFactory = await ethers.getContractFactory( 36 | "contracts/wallet/v1/SmartAccountProxyFactory.sol:SmartAccountProxyFactory" 37 | ); 38 | let SmartAccountProxyFactory = await SmartAccountProxysFactory.deploy( 39 | SmartAccount.address, 40 | owner.address 41 | ); 42 | 43 | return { 44 | owner, 45 | Alice, 46 | bundler, 47 | EntryPoint, 48 | SmartAccount, 49 | DefaultCallbackHandler, 50 | SmartAccountProxyFactory, 51 | validations 52 | }; 53 | } 54 | 55 | it("should read default value correctly", async function () { 56 | const { owner, SmartAccountProxyFactory, SmartAccount } = await loadFixture( 57 | deploy 58 | ); 59 | 60 | await expect(await SmartAccountProxyFactory.owner()).to.equal( 61 | owner.address 62 | ); 63 | await expect( 64 | await SmartAccountProxyFactory.safeSingleton(SmartAccount.address) 65 | ).to.equal(true); 66 | }); 67 | 68 | it("should set safeSingleton value correctly", async function () { 69 | const { owner, SmartAccountProxyFactory } = await loadFixture(deploy); 70 | 71 | const safeSingletonAddress = "0x1234567890123456789012345678901234567890"; 72 | const value = true; 73 | 74 | let tx = await SmartAccountProxyFactory.setSafeSingleton( 75 | safeSingletonAddress, 76 | value 77 | ); 78 | await tx.wait(); 79 | 80 | await expect(tx) 81 | .to.emit(SmartAccountProxyFactory, "SafeSingletonSet") 82 | .withArgs(safeSingletonAddress, value); 83 | 84 | await expect( 85 | await SmartAccountProxyFactory.safeSingleton(safeSingletonAddress) 86 | ).to.equal(value); 87 | }); 88 | 89 | it("Should depoly Account with Args expected", async function () { 90 | const { Alice, bundler, SmartAccount, SmartAccountProxyFactory } = 91 | await loadFixture(deploy); 92 | const random = 0; 93 | const initializeData = SmartAccount.interface.encodeFunctionData( 94 | "Initialize", 95 | [Alice.address] 96 | ); 97 | 98 | let accountExpect = await SmartAccountProxyFactory.getAddress( 99 | SmartAccount.address, 100 | initializeData, 101 | random 102 | ); 103 | 104 | tx = await SmartAccountProxyFactory.createAccount( 105 | SmartAccount.address, 106 | initializeData, 107 | random 108 | ); 109 | await tx.wait(); 110 | 111 | let events = await SmartAccountProxyFactory.queryFilter("ProxyCreation"); 112 | await expect(events.length).to.equal(1); 113 | let accountDeployed = events[0].args.proxy; 114 | 115 | await expect(accountDeployed).to.be.equal(accountExpect); 116 | }); 117 | 118 | it("Should depoly Account with Args expected", async function () { 119 | const { 120 | Alice, 121 | bundler, 122 | EntryPoint, 123 | SmartAccount, 124 | DefaultCallbackHandler, 125 | SmartAccountProxyFactory, 126 | } = await loadFixture(deploy); 127 | 128 | let initializeData = SmartAccount.interface.encodeFunctionData( 129 | "Initialize", 130 | [Alice.address] 131 | ); 132 | 133 | let tx = await SmartAccountProxyFactory.createAccount( 134 | SmartAccount.address, 135 | initializeData, 136 | 0 137 | ); 138 | 139 | let events = await SmartAccountProxyFactory.queryFilter("ProxyCreation"); 140 | await expect(events.length).to.equal(1); 141 | let AA = await SmartAccount.attach(events[0].args.proxy); 142 | 143 | // await validations.validateWalletWhitelist(AA.address); 144 | await expect(await AA.entryPoint()).to.be.equal(EntryPoint); 145 | await expect(await AA.getOwner()).to.be.equal(Alice.address); 146 | await expect(await AA.getFallbackHandler()).to.be.equal( 147 | DefaultCallbackHandler.address 148 | ); 149 | }); 150 | 151 | it("should not revert if deployed", async function () { 152 | const { Alice, SmartAccount, SmartAccountProxyFactory } = await loadFixture( 153 | deploy 154 | ); 155 | 156 | let initializeData = SmartAccount.interface.encodeFunctionData( 157 | "Initialize", 158 | [Alice.address] 159 | ); 160 | 161 | let tx = await SmartAccountProxyFactory.createAccount( 162 | SmartAccount.address, 163 | initializeData, 164 | 0 165 | ); 166 | await tx.wait(); 167 | await expect(tx).to.emit(SmartAccountProxyFactory, "ProxyCreation"); 168 | 169 | tx = await SmartAccountProxyFactory.createAccount( 170 | SmartAccount.address, 171 | initializeData, 172 | 0 173 | ); 174 | await tx.wait(); 175 | await expect(tx).to.not.emit(SmartAccountProxyFactory, "ProxyCreation"); 176 | }); 177 | }); 178 | -------------------------------------------------------------------------------- /test/wallet/base/FallbackManager.js: -------------------------------------------------------------------------------- 1 | let { loadFixture } = require("@nomicfoundation/hardhat-network-helpers"); 2 | let { expect } = require("chai"); 3 | let Utils = require("../../Utils.js"); 4 | 5 | describe("FallbackManager", function () { 6 | async function deploy() { 7 | let [owner, bundler, Alice] = await ethers.getSigners(); 8 | 9 | // setEntryPoint to owner to simplify testing 10 | let EntryPoint = owner.address; 11 | let SimulationContract = owner.address; 12 | 13 | let DefaultCallbackHandlerFactory = await ethers.getContractFactory( 14 | "contracts/wallet/handler/DefaultCallbackHandler.sol:DefaultCallbackHandler" 15 | ); 16 | let DefaultCallbackHandler = await DefaultCallbackHandlerFactory.deploy(); 17 | 18 | let Validations = await ethers.getContractFactory("Validations"); 19 | let validations = await Validations.deploy(owner.address); 20 | 21 | await validations.setBundlerOfficialWhitelist(owner.address, true); 22 | 23 | let SmartAccountFactory = await ethers.getContractFactory( 24 | "contracts/wallet/v1/SmartAccount.sol:SmartAccount" 25 | ); 26 | let SmartAccount = await SmartAccountFactory.deploy( 27 | EntryPoint, 28 | SimulationContract, 29 | DefaultCallbackHandler.address, 30 | validations.address, 31 | "SA", 32 | "1.0" 33 | ); 34 | 35 | let SmartAccountProxysFactory = await ethers.getContractFactory( 36 | "contracts/wallet/v1/SmartAccountProxyFactory.sol:SmartAccountProxyFactory" 37 | ); 38 | let SmartAccountProxyFactory = await SmartAccountProxysFactory.deploy( 39 | SmartAccount.address, 40 | owner.address 41 | ); 42 | 43 | let initializeData = SmartAccount.interface.encodeFunctionData( 44 | "Initialize", 45 | [Alice.address] 46 | ); 47 | 48 | let tx = await SmartAccountProxyFactory.createAccount( 49 | SmartAccount.address, 50 | initializeData, 51 | 0 52 | ); 53 | 54 | let events = await SmartAccountProxyFactory.queryFilter("ProxyCreation"); 55 | await expect(events.length).to.equal(1); 56 | let AA = await SmartAccount.attach(events[0].args.proxy); 57 | 58 | let SmartAccountProxy = await ethers.getContractFactory( 59 | "contracts/wallet/v1/SmartAccountProxy.sol:SmartAccountProxy" 60 | ); 61 | let AAProxy = await SmartAccountProxy.attach(AA.address); 62 | 63 | return { 64 | owner, 65 | EntryPoint, 66 | SmartAccount, 67 | DefaultCallbackHandler, 68 | Alice, 69 | AA, 70 | AAProxy, 71 | }; 72 | } 73 | 74 | describe("setFallbackHandler", function () { 75 | it("should revert with handler illegal", async function () { 76 | let { Alice, AA } = await loadFixture(deploy); 77 | 78 | // enocde functioncall of updateImplement 79 | let setFallbackHandlerCalldata = AA.interface.encodeFunctionData( 80 | "setFallbackHandler", 81 | [AA.address] 82 | ); 83 | 84 | await expect(AA.execTransactionFromEntrypoint( 85 | AA.address, 86 | 0, 87 | setFallbackHandlerCalldata 88 | )).to.be.revertedWith("handler illegal"); 89 | 90 | setFallbackHandlerCalldata = AA.interface.encodeFunctionData( 91 | "setFallbackHandler", 92 | [Alice.address] 93 | ); 94 | let tx = await AA.execTransactionFromEntrypoint( 95 | AA.address, 96 | 0, 97 | setFallbackHandlerCalldata 98 | ); 99 | await expect(await AA.getFallbackHandler()).to.equal(Alice.address); 100 | }); 101 | 102 | it("should change slot correctly", async function () { 103 | let { Alice, AA } = await loadFixture(deploy); 104 | 105 | let setFallbackHandlerCalldata = AA.interface.encodeFunctionData( 106 | "setFallbackHandler", 107 | [Alice.address] 108 | ); 109 | let tx = await AA.execTransactionFromEntrypoint( 110 | AA.address, 111 | 0, 112 | setFallbackHandlerCalldata 113 | ); 114 | await expect(await AA.getFallbackHandler()).to.equal(Alice.address); 115 | }); 116 | }); 117 | }); 118 | -------------------------------------------------------------------------------- /test/wallet/base/SignatureManager.js: -------------------------------------------------------------------------------- 1 | let { loadFixture } = require("@nomicfoundation/hardhat-network-helpers"); 2 | let { expect } = require("chai"); 3 | let Utils = require("../../Utils.js"); 4 | 5 | describe("SignatureManager", function () { 6 | async function deploy() { 7 | let [owner, bundler, Alice] = await ethers.getSigners(); 8 | 9 | // setEntryPoint to owner to simplify testing 10 | let EntryPoint = owner.address; 11 | let SimulationContract = owner.address; 12 | 13 | let DefaultCallbackHandlerFactory = await ethers.getContractFactory( 14 | "contracts/wallet/handler/DefaultCallbackHandler.sol:DefaultCallbackHandler" 15 | ); 16 | let DefaultCallbackHandler = await DefaultCallbackHandlerFactory.deploy(); 17 | 18 | let Validations = await ethers.getContractFactory("Validations"); 19 | let validations = await Validations.deploy(owner.address); 20 | 21 | await validations.setBundlerOfficialWhitelist(owner.address, true); 22 | 23 | let SmartAccountFactory = await ethers.getContractFactory( 24 | "contracts/wallet/v1/SmartAccount.sol:SmartAccount" 25 | ); 26 | let SmartAccount = await SmartAccountFactory.deploy( 27 | EntryPoint, 28 | SimulationContract, 29 | DefaultCallbackHandler.address, 30 | validations.address, 31 | "SA", 32 | "1.0" 33 | ); 34 | 35 | let SmartAccountProxysFactory = await ethers.getContractFactory( 36 | "contracts/wallet/v1/SmartAccountProxyFactory.sol:SmartAccountProxyFactory" 37 | ); 38 | let SmartAccountProxyFactory = await SmartAccountProxysFactory.deploy( 39 | SmartAccount.address, 40 | owner.address 41 | ); 42 | 43 | let initializeData = SmartAccount.interface.encodeFunctionData( 44 | "Initialize", 45 | [Alice.address] 46 | ); 47 | 48 | let tx = await SmartAccountProxyFactory.createAccount( 49 | SmartAccount.address, 50 | initializeData, 51 | 0 52 | ); 53 | 54 | let events = await SmartAccountProxyFactory.queryFilter("ProxyCreation"); 55 | await expect(events.length).to.equal(1); 56 | let AA = await SmartAccount.attach(events[0].args.proxy); 57 | 58 | let SmartAccountProxy = await ethers.getContractFactory( 59 | "contracts/wallet/v1/SmartAccountProxy.sol:SmartAccountProxy" 60 | ); 61 | let AAProxy = await SmartAccountProxy.attach(AA.address); 62 | 63 | let UserOpHelperFactory = await ethers.getContractFactory( 64 | "UserOperationHelper" 65 | ); 66 | let UserOpHelper = await UserOpHelperFactory.deploy( 67 | ethers.constants.AddressZero, 68 | EntryPoint, 69 | owner.address 70 | ); 71 | 72 | let SignatureManagerFactory = await ethers.getContractFactory( 73 | "contracts/mock/MockSignatureManager.sol:MockSignatureManager", 74 | owner 75 | ); 76 | let SignatureManager = await SignatureManagerFactory.connect(owner).deploy( 77 | EntryPoint, 78 | "SA", 79 | "1.0" 80 | ); 81 | 82 | await SignatureManager.connect(owner).changeOwner(owner.address); 83 | 84 | return { 85 | owner, 86 | EntryPoint, 87 | SmartAccount, 88 | UserOpHelper, 89 | DefaultCallbackHandler, 90 | Alice, 91 | AA, 92 | AAProxy, 93 | SignatureManager 94 | }; 95 | } 96 | 97 | describe("check SignatureManager", function () { 98 | it("shuold revert not equal", async function () { 99 | let { SignatureManager, EntryPoint, owner } = await loadFixture(deploy); 100 | // enocde functioncall of updateImplement 101 | await expect(await SignatureManager.entryPoint()).to.equal(EntryPoint); 102 | await expect(await SignatureManager.getOwner()).to.equal(owner.address); 103 | 104 | }); 105 | it("should revert is validation", async function () { 106 | let { SignatureManager, SmartAccount, EntryPoint, owner, Alice, UserOpHelper } = await loadFixture(deploy); 107 | 108 | let userOp = await Utils.generateSignedUOP({ 109 | sender: owner.address, 110 | nonce: 0, 111 | initCode: "0x", 112 | callData: "0x12345678", 113 | paymasterAndData: "0x", 114 | owner: owner, 115 | SmartAccount: SmartAccount, 116 | EntryPoint: EntryPoint, 117 | sigType: 1, 118 | sigTime: 123456, 119 | }); 120 | 121 | let userOpHash = await UserOpHelper.getUserOpHash(userOp, EntryPoint); 122 | let validateDataReturn = await SignatureManager.getValidateSignatureReturn( 123 | userOp, 124 | userOpHash, 125 | ); 126 | 127 | let sigTime = ethers.BigNumber.from("123456"); 128 | sigTime = sigTime.mul(ethers.BigNumber.from("2").pow(160)); 129 | 130 | await expect(validateDataReturn).to.emit(SignatureManager, "Validation") 131 | .withArgs( 132 | sigTime 133 | ); 134 | }); 135 | }); 136 | 137 | 138 | 139 | }); 140 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { 5 | "types/*": ["types/*"] 6 | }, 7 | "target": "es2020", 8 | "module": "commonjs", 9 | "esModuleInterop": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "strict": true, 12 | "skipLibCheck": true, 13 | "resolveJsonModule": true 14 | }, 15 | "include": ["./scripts", "./test", "./types"], 16 | "files": ["./hardhat.config.ts"] 17 | } 18 | 19 | --------------------------------------------------------------------------------