From 920e9f7c2d3fcf7934f3849916ae0d69b8cc4a65 Mon Sep 17 00:00:00 2001 From: Ramon Recuero Date: Thu, 4 Jan 2024 12:51:36 -0800 Subject: [PATCH 01/43] KintoApp --- src/apps/KintoApp.sol | 193 +++++++++++++++++++++++++++++++++++ src/interfaces/IKintoApp.sol | 39 +++++++ 2 files changed, 232 insertions(+) create mode 100644 src/apps/KintoApp.sol create mode 100644 src/interfaces/IKintoApp.sol diff --git a/src/apps/KintoApp.sol b/src/apps/KintoApp.sol new file mode 100644 index 000000000..6846a13b8 --- /dev/null +++ b/src/apps/KintoApp.sol @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.12; + +import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721BurnableUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; + +import "../interfaces/IKintoID.sol"; +import "../interfaces/IKintoApp.sol"; +import "../interfaces/IKintoWalletFactory.sol"; + +// import "forge-std/console2.sol"; + +/** + * @title KintoApp + * @dev A contract that holds all the information of a KintoApp + */ +contract KintoApp is + Initializable, + ERC721Upgradeable, + ERC721EnumerableUpgradeable, + ERC721BurnableUpgradeable, + AccessControlUpgradeable, + UUPSUpgradeable, + IKintoApp +{ + + /* ============ Constants ============ */ + bytes32 public constant override UPGRADER_ROLE = keccak256("UPGRADER_ROLE"); + bytes32 public constant override DEVELOPER_ADMIN = keccak256("DEVELOPER_ADMIN"); + + /* ============ State Variables ============ */ + + uint256 private _nextTokenId; + + mapping (address => IKintoApp.Metadata) public appMetadata; + mapping (address => address) public childToParentContract; + + /* ============ Events ============ */ + + /* ============ Constructor & Initializers ============ */ + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + function initialize() external initializer { + __ERC721_init("Kinto APP", "KINTOAPP"); + __ERC721Enumerable_init(); + __ERC721Burnable_init(); + __AccessControl_init(); + __UUPSUpgradeable_init(); + _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); + _grantRole(UPGRADER_ROLE, msg.sender); + _grantRole(DEVELOPER_ADMIN, msg.sender); + } + + /** + * @dev Authorize the upgrade. Only by the upgrader role. + * @param newImplementation address of the new implementation + */ + // This function is called by the proxy contract when the implementation is upgraded + function _authorizeUpgrade(address newImplementation) internal override onlyRole(UPGRADER_ROLE) {} + + /* ============ App Registration ============ */ + + /** + * @dev Register a new app and mints the NFT to the creator + * @param _name The name of the app + * @param parentContract The address of the parent contract + * @param childContracts The addresses of the child contracts + * @param appLimits The limits of the app + */ + function registerApp(string calldata _name, address parentContract, address[] calldata childContracts, uint256[4] calldata appLimits) external override { + require(appLimits.length == 4, "Invalid app limits"); + IKintoApp.Metadata memory metadata = IKintoApp.Metadata({ + name: _name, + developerWallet: msg.sender, + dsaEnabled: false, + rateLimitPeriod: appLimits[0], + rateLimitNumber: appLimits[1], + gasLimitPeriod: appLimits[2], + gasLimitCost: appLimits[3] + }); + appMetadata[parentContract] = metadata; + for (uint256 i = 0; i < childContracts.length; i++) { + childToParentContract[childContracts[i]] = parentContract; + } + _nextTokenId++; + _safeMint(msg.sender, _nextTokenId); + } + + /** + * @dev Allows the app to request PII data + * @param app The name of the app + */ + function enableDSA(address app) external override onlyRole(DEVELOPER_ADMIN) { + require(appMetadata[app].dsaEnabled == false, "DSA already enabled"); + appMetadata[app].dsaEnabled = true; + } + + /* ============ App Info Fetching ============ */ + + /** + * @dev Returns the metadata of the app + * @param _contract The address of the app + * @return The metadata of the app + */ + function getAppMetadata(address _contract) external view override returns (IKintoApp.Metadata memory) { + address finalContract = childToParentContract[_contract] != address(0) ? childToParentContract[_contract] : _contract; + return appMetadata[finalContract]; + } + + /** + * @dev Returns the limits of the app + * @param _contract The address of the app + * @return The limits of the app + */ + function getContractLimits(address _contract) external view override returns (uint256[4] memory) { + address finalContract = childToParentContract[_contract] != address(0) ? childToParentContract[_contract] : _contract; + IKintoApp.Metadata memory metadata = appMetadata[finalContract]; + return [metadata.rateLimitPeriod, metadata.rateLimitNumber, metadata.gasLimitPeriod, metadata.gasLimitCost]; + } + + /* ============ Token name, symbol & URI ============ */ + + /** + * @dev Gets the token name. + * @return string representing the token name + */ + function name() public pure override(ERC721Upgradeable, IKintoApp) returns (string memory) { + return "Kinto APP"; + } + + /** + * @dev Gets the token symbol. + * @return string representing the token symbol + */ + function symbol() public pure override(ERC721Upgradeable, IKintoApp) returns (string memory) { + return "KINTOAPP"; + } + + /** + * @dev Returns the base token URI. ID is appended + * @return token URI. + */ + function _baseURI() internal pure override returns (string memory) { + return "https://kinto.xyz/metadata/kintoapp/"; + } + + + /* ============ Disable token transfers ============ */ + + /** + * @dev Hook that is called before any token transfer. Allow only mints and burns, no transfers. + * @param from source address + * @param to target address + * @param batchSize The first id + */ + function _beforeTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) + internal + virtual + override(ERC721Upgradeable, ERC721EnumerableUpgradeable) + { + require( + (from == address(0) && to != address(0)) || (from != address(0) && to == address(0)), + "Only mint or burn transfers are allowed" + ); + super._beforeTokenTransfer(from, to, firstTokenId, batchSize); + } + + /* ============ Interface ============ */ + + /** + * @dev Returns whether the contract implements the interface defined by the id + * @param interfaceId id of the interface to be checked. + * @return true if the contract implements the interface defined by the id. + */ + function supportsInterface(bytes4 interfaceId) + public + view + override(ERC721Upgradeable, ERC721EnumerableUpgradeable, AccessControlUpgradeable) + returns (bool) + { + return super.supportsInterface(interfaceId); + } + +} diff --git a/src/interfaces/IKintoApp.sol b/src/interfaces/IKintoApp.sol new file mode 100644 index 000000000..c7700cbb1 --- /dev/null +++ b/src/interfaces/IKintoApp.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +interface IKintoApp { + /* ============ Structs ============ */ + + struct Metadata { + string name; + address developerWallet; // the address that deploys the wallet + bool dsaEnabled; // whether or not this application can request PII from users + uint rateLimitPeriod; + uint rateLimitNumber; // in txs + uint gasLimitPeriod; + uint gasLimitCost; // in eth + } + + /* ============ State Change ============ */ + + function registerApp(string calldata _name, address parentContract, address[] calldata childContracts, uint256[4] calldata appLimits) external; + + function enableDSA(address app) external; + + /* ============ Basic Viewers ============ */ + + function name() external pure returns (string memory); + + function symbol() external pure returns (string memory); + + function getContractLimits(address _contract) external view returns (uint256[4] memory); + + function getAppMetadata(address _contract) external view returns (Metadata memory); + + /* ============ Constants and attrs ============ */ + + function DEVELOPER_ADMIN() external view returns (bytes32); + + function UPGRADER_ROLE() external view returns (bytes32); + +} From ae08457b740394c18da02a43b1f962174762423c Mon Sep 17 00:00:00 2001 From: Ramon Recuero Date: Fri, 5 Jan 2024 09:56:51 -0800 Subject: [PATCH 02/43] Complete functionality --- src/apps/KintoApp.sol | 70 +++++++++++++++++++++++++++++------- src/interfaces/IKintoApp.sol | 6 ++++ 2 files changed, 63 insertions(+), 13 deletions(-) diff --git a/src/apps/KintoApp.sol b/src/apps/KintoApp.sol index 6846a13b8..901f41bbd 100644 --- a/src/apps/KintoApp.sol +++ b/src/apps/KintoApp.sol @@ -39,6 +39,8 @@ contract KintoApp is mapping (address => IKintoApp.Metadata) public appMetadata; mapping (address => address) public childToParentContract; + mapping (address => mapping (address => bool)) public appSponsoredContracts; // other contracts to be sponsored + /* ============ Events ============ */ @@ -78,23 +80,38 @@ contract KintoApp is */ function registerApp(string calldata _name, address parentContract, address[] calldata childContracts, uint256[4] calldata appLimits) external override { require(appLimits.length == 4, "Invalid app limits"); - IKintoApp.Metadata memory metadata = IKintoApp.Metadata({ - name: _name, - developerWallet: msg.sender, - dsaEnabled: false, - rateLimitPeriod: appLimits[0], - rateLimitNumber: appLimits[1], - gasLimitPeriod: appLimits[2], - gasLimitCost: appLimits[3] - }); - appMetadata[parentContract] = metadata; - for (uint256 i = 0; i < childContracts.length; i++) { - childToParentContract[childContracts[i]] = parentContract; - } + _updateMetadata(_name, parentContract, childContracts, appLimits); _nextTokenId++; _safeMint(msg.sender, _nextTokenId); } + /** + * @dev Allows the developer to set sponsored contracts + * @param _app The address of the app + * @param _contracts The addresses of the contracts + * @param _flags The flags of the contracts + */ + function setSponsoredContracts(address _app, address[] calldata _contracts, bool[] calldata _flags) external override { + require(_contracts.length == _flags.length, "Invalid input"); + require(msg.sender == appMetadata[_app].developerWallet, "Only developer can set sponsored contracts"); + for (uint256 i = 0; i < _contracts.length; i++) { + appSponsoredContracts[_app][_contracts[i]] = _flags[i]; + } + } + + /** + * @dev Allows the developer to update the metadata of the app + * @param _name The name of the app + * @param parentContract The address of the parent contract + * @param childContracts The addresses of the child contracts + * @param appLimits The limits of the app + */ + function updateMetadata(string calldata _name, address parentContract, address[] calldata childContracts, uint256[4] calldata appLimits) external override { + require(appLimits.length == 4, "Invalid app limits"); + require(msg.sender == appMetadata[parentContract].developerWallet, "Only developer can update metadata"); + _updateMetadata(_name, parentContract, childContracts, appLimits); + } + /** * @dev Allows the app to request PII data * @param app The name of the app @@ -127,6 +144,16 @@ contract KintoApp is return [metadata.rateLimitPeriod, metadata.rateLimitNumber, metadata.gasLimitPeriod, metadata.gasLimitCost]; } + /** + * @dev Returns whether a contract is sponsored by an app + * @param _app The address of the app + * @param _contract The address of the contract + * @return bool true or false + */ + function isContractSponsoredByApp(address _app, address _contract) external view override returns (bool) { + return childToParentContract[_contract] == _app || appSponsoredContracts[_app][_contract]; + } + /* ============ Token name, symbol & URI ============ */ /** @@ -153,6 +180,23 @@ contract KintoApp is return "https://kinto.xyz/metadata/kintoapp/"; } + /* =========== App metadata params =========== */ + function _updateMetadata(string calldata _name, address parentContract, address[] calldata childContracts, uint256[4] calldata appLimits) internal { + IKintoApp.Metadata memory metadata = IKintoApp.Metadata({ + name: _name, + developerWallet: msg.sender, + dsaEnabled: false, + rateLimitPeriod: appLimits[0], + rateLimitNumber: appLimits[1], + gasLimitPeriod: appLimits[2], + gasLimitCost: appLimits[3] + }); + appMetadata[parentContract] = metadata; + for (uint256 i = 0; i < childContracts.length; i++) { + childToParentContract[childContracts[i]] = parentContract; + } + } + /* ============ Disable token transfers ============ */ diff --git a/src/interfaces/IKintoApp.sol b/src/interfaces/IKintoApp.sol index c7700cbb1..51a66ae98 100644 --- a/src/interfaces/IKintoApp.sol +++ b/src/interfaces/IKintoApp.sol @@ -20,6 +20,10 @@ interface IKintoApp { function enableDSA(address app) external; + function setSponsoredContracts(address _app, address[] calldata _contracts, bool[] calldata _flags) external; + + function updateMetadata(string calldata _name, address parentContract, address[] calldata childContracts, uint256[4] calldata appLimits) external; + /* ============ Basic Viewers ============ */ function name() external pure returns (string memory); @@ -30,6 +34,8 @@ interface IKintoApp { function getAppMetadata(address _contract) external view returns (Metadata memory); + function isContractSponsoredByApp(address _app, address _contract) external view returns (bool); + /* ============ Constants and attrs ============ */ function DEVELOPER_ADMIN() external view returns (bytes32); From d68e8dbdbf93320e792c8bf2f3b3c2641ab1378f Mon Sep 17 00:00:00 2001 From: Ramon Recuero Date: Fri, 5 Jan 2024 10:21:24 -0800 Subject: [PATCH 03/43] Modifies paymaster to read from sponsored contracts --- src/apps/KintoApp.sol | 2 +- src/interfaces/ISponsorPaymaster.sol | 6 ++++++ src/paymasters/SponsorPaymaster.sol | 18 +++++++++++++++--- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/apps/KintoApp.sol b/src/apps/KintoApp.sol index 901f41bbd..37e2a8907 100644 --- a/src/apps/KintoApp.sol +++ b/src/apps/KintoApp.sol @@ -151,7 +151,7 @@ contract KintoApp is * @return bool true or false */ function isContractSponsoredByApp(address _app, address _contract) external view override returns (bool) { - return childToParentContract[_contract] == _app || appSponsoredContracts[_app][_contract]; + return _contract == _app || childToParentContract[_contract] == _app || appSponsoredContracts[_app][_contract]; } /* ============ Token name, symbol & URI ============ */ diff --git a/src/interfaces/ISponsorPaymaster.sol b/src/interfaces/ISponsorPaymaster.sol index 14e1b8f29..ca38b8a0d 100644 --- a/src/interfaces/ISponsorPaymaster.sol +++ b/src/interfaces/ISponsorPaymaster.sol @@ -1,6 +1,8 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13; +import {IKintoApp} from "./IKintoApp.sol"; + interface ISponsorPaymaster { /* ============ Structs ============ */ @@ -15,6 +17,8 @@ interface ISponsorPaymaster { function initialize(address owner) external; + function setAppRegistry(address _appRegistry) external; + function addDepositFor(address account) external payable; function withdrawTokensTo(address target, uint256 amount) external; @@ -31,6 +35,8 @@ interface ISponsorPaymaster { function balances(address account) external view returns (uint256 amount); + function appRegistry() external view returns (IKintoApp); + function contractSpent(address account) external view returns (uint256 amount); function unlockBlock(address account) external view returns (uint256 block); diff --git a/src/paymasters/SponsorPaymaster.sol b/src/paymasters/SponsorPaymaster.sol index 06c8e6ddc..fd32c2afc 100644 --- a/src/paymasters/SponsorPaymaster.sol +++ b/src/paymasters/SponsorPaymaster.sol @@ -11,6 +11,7 @@ import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import "@aa/core/BasePaymaster.sol"; import "@aa/core/UserOperationLib.sol"; import "../interfaces/ISponsorPaymaster.sol"; +import "../interfaces/IKintoApp.sol"; import "../interfaces/IKintoWallet.sol"; /** @@ -46,6 +47,8 @@ contract SponsorPaymaster is Initializable, BasePaymaster, UUPSUpgradeable, Reen mapping(address => mapping(address => ISponsorPaymaster.RateLimitData)) public costLimit; mapping(address => ISponsorPaymaster.RateLimitData) public totalRateLimit; + IKintoApp public override appRegistry; + // ========== Constructor & Upgrades ============ constructor(IEntryPoint __entryPoint) BasePaymaster(__entryPoint) { @@ -74,6 +77,14 @@ contract SponsorPaymaster is Initializable, BasePaymaster, UUPSUpgradeable, Reen (newImplementation); } + /** + * @dev Set the app registry + * @param _appRegistry address of the app registry + */ + function setAppRegistry(address _appRegistry) external override onlyOwner { + appRegistry = IKintoApp(_appRegistry); + } + // ========== Deposit Mgmt ============ /** @@ -256,7 +267,7 @@ contract SponsorPaymaster is Initializable, BasePaymaster, UUPSUpgradeable, Reen // Function to extract the first target contract function _getLastTargetContract(address sender, bytes calldata callData) private - pure + view returns (address lastTargetContract) { // Extract the function selector from the callData @@ -267,9 +278,10 @@ contract SponsorPaymaster is Initializable, BasePaymaster, UUPSUpgradeable, Reen // Decode callData for executeBatch (address[] memory targetContracts,,) = abi.decode(callData[4:], (address[], uint256[], bytes[])); lastTargetContract = targetContracts[targetContracts.length - 1]; + // App contract must be last for (uint256 i = 0; i < targetContracts.length - 1; i++) { - if (targetContracts[i] != lastTargetContract && targetContracts[i] != sender) { - revert("SP: executeBatch must come from same contract or sender wallet"); + if (!appRegistry.isContractSponsoredByApp(lastTargetContract, targetContracts[i]) && targetContracts[i] != sender) { + revert("SP: executeBatch targets must be sponsored by the contract or be the sender wallet"); } } } else if (selector == IKintoWallet.execute.selector) { From 670ed464e23f5793ec16eb03aa056d6cb5297325 Mon Sep 17 00:00:00 2001 From: Ramon Recuero Date: Fri, 5 Jan 2024 10:26:55 -0800 Subject: [PATCH 04/43] Makes paymaster a bit more resilient --- src/apps/KintoApp.sol | 15 +++++++++++++++ src/interfaces/IKintoApp.sol | 2 ++ src/paymasters/SponsorPaymaster.sol | 4 ++-- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/apps/KintoApp.sol b/src/apps/KintoApp.sol index 37e2a8907..132e51835 100644 --- a/src/apps/KintoApp.sol +++ b/src/apps/KintoApp.sol @@ -154,6 +154,21 @@ contract KintoApp is return _contract == _app || childToParentContract[_contract] == _app || appSponsoredContracts[_app][_contract]; } + /** + * @dev Returns the contract that sponsors a contract + * @param _contract The address of the contract + * @return The address of the contract that sponsors the contract + */ + function getContractSponsor(address _contract) external view override returns (address) { + if (appMetadata[_contract].developerWallet != address(0)) { + return _contract; + } + if (childToParentContract[_contract] != address(0)) { + return childToParentContract[_contract]; + } + return _contract; + } + /* ============ Token name, symbol & URI ============ */ /** diff --git a/src/interfaces/IKintoApp.sol b/src/interfaces/IKintoApp.sol index 51a66ae98..57a6e39fb 100644 --- a/src/interfaces/IKintoApp.sol +++ b/src/interfaces/IKintoApp.sol @@ -34,6 +34,8 @@ interface IKintoApp { function getAppMetadata(address _contract) external view returns (Metadata memory); + function getContractSponsor(address _contract) external view returns (address); + function isContractSponsoredByApp(address _app, address _contract) external view returns (bool); /* ============ Constants and attrs ============ */ diff --git a/src/paymasters/SponsorPaymaster.sol b/src/paymasters/SponsorPaymaster.sol index fd32c2afc..6de664254 100644 --- a/src/paymasters/SponsorPaymaster.sol +++ b/src/paymasters/SponsorPaymaster.sol @@ -277,8 +277,8 @@ contract SponsorPaymaster is Initializable, BasePaymaster, UUPSUpgradeable, Reen if (selector == IKintoWallet.executeBatch.selector) { // Decode callData for executeBatch (address[] memory targetContracts,,) = abi.decode(callData[4:], (address[], uint256[], bytes[])); - lastTargetContract = targetContracts[targetContracts.length - 1]; - // App contract must be last + lastTargetContract = appRegistry.getContractSponsor(targetContracts[targetContracts.length - 1]); + // Last contract must be a contract app for (uint256 i = 0; i < targetContracts.length - 1; i++) { if (!appRegistry.isContractSponsoredByApp(lastTargetContract, targetContracts[i]) && targetContracts[i] != sender) { revert("SP: executeBatch targets must be sponsored by the contract or be the sender wallet"); From 4ef149ec57cab94dceedc67b3ab0e7035df66ea0 Mon Sep 17 00:00:00 2001 From: Ramon Recuero Date: Fri, 5 Jan 2024 10:39:26 -0800 Subject: [PATCH 05/43] Get gas limits from app registry --- src/apps/KintoApp.sol | 12 ++++++- src/paymasters/SponsorPaymaster.sol | 55 +++++++++++++++-------------- 2 files changed, 40 insertions(+), 27 deletions(-) diff --git a/src/apps/KintoApp.sol b/src/apps/KintoApp.sol index 132e51835..7422991cd 100644 --- a/src/apps/KintoApp.sol +++ b/src/apps/KintoApp.sol @@ -32,6 +32,11 @@ contract KintoApp is /* ============ Constants ============ */ bytes32 public constant override UPGRADER_ROLE = keccak256("UPGRADER_ROLE"); bytes32 public constant override DEVELOPER_ADMIN = keccak256("DEVELOPER_ADMIN"); + + uint256 public constant RATE_LIMIT_PERIOD = 1 minutes; + uint256 public constant RATE_LIMIT_THRESHOLD = 10; + uint256 public constant GAS_LIMIT_PERIOD = 30 days; + uint256 public constant GAS_LIMIT_THRESHOLD = 1e16; // 0.01 ETH /* ============ State Variables ============ */ @@ -141,7 +146,12 @@ contract KintoApp is function getContractLimits(address _contract) external view override returns (uint256[4] memory) { address finalContract = childToParentContract[_contract] != address(0) ? childToParentContract[_contract] : _contract; IKintoApp.Metadata memory metadata = appMetadata[finalContract]; - return [metadata.rateLimitPeriod, metadata.rateLimitNumber, metadata.gasLimitPeriod, metadata.gasLimitCost]; + return [ + metadata.rateLimitPeriod != 0 ? metadata.rateLimitPeriod : RATE_LIMIT_PERIOD, + metadata.rateLimitNumber != 0 ? metadata.rateLimitNumber : RATE_LIMIT_THRESHOLD, + metadata.gasLimitPeriod != 0 ? metadata.gasLimitPeriod : GAS_LIMIT_PERIOD, + metadata.gasLimitCost != 0 ? metadata.gasLimitPeriod : GAS_LIMIT_THRESHOLD + ]; } /** diff --git a/src/paymasters/SponsorPaymaster.sol b/src/paymasters/SponsorPaymaster.sol index 6de664254..84f9661e4 100644 --- a/src/paymasters/SponsorPaymaster.sol +++ b/src/paymasters/SponsorPaymaster.sol @@ -33,12 +33,8 @@ contract SponsorPaymaster is Initializable, BasePaymaster, UUPSUpgradeable, Reen uint256 public constant MAX_COST_OF_USEROP = 3e15; // 0.03 ETH uint256 public constant RATE_LIMIT_PERIOD = 1 minutes; - uint256 public constant RATE_LIMIT_THRESHOLD_SINGLE = 10; uint256 public constant RATE_LIMIT_THRESHOLD_TOTAL = 50; - uint256 public constant GAS_LIMIT_PERIOD = 30 days; - uint256 public constant GAS_LIMIT_THRESHOLD_SINGLE = 1e16; // 0.01 ETH - mapping(address => uint256) public balances; mapping(address => uint256) public contractSpent; // keeps track of total gas consumption by contract mapping(address => uint256) public unlockBlock; @@ -200,27 +196,30 @@ contract SponsorPaymaster is Initializable, BasePaymaster, UUPSUpgradeable, Reen uint256 ethMaxCost = (maxCost + COST_OF_POST * gasPriceUserOp); require(ethMaxCost <= MAX_COST_OF_USEROP, "SP: gas too high for user op"); + // Check Kinto rate limiting + ISponsorPaymaster.RateLimitData memory globalData = totalRateLimit[userOp.sender]; + if (block.timestamp < globalData.lastOperationTime + RATE_LIMIT_PERIOD) { + require(globalData.operationCount < RATE_LIMIT_THRESHOLD_TOTAL, "SP: Kinto Rate limit exceeded"); + } + + // Get app limits + uint256[4] memory appLimits = appRegistry.getContractLimits(targetAccount); + // Check app rate limiting ISponsorPaymaster.RateLimitData memory data = rateLimit[userOp.sender][targetAccount]; - if (block.timestamp < data.lastOperationTime + RATE_LIMIT_PERIOD) { - require(data.operationCount < RATE_LIMIT_THRESHOLD_SINGLE, "SP: App Rate limit exceeded"); + if (block.timestamp < data.lastOperationTime + appLimits[0]) { + require(data.operationCount < appLimits[1], "SP: App Rate limit exceeded"); } // Check Kinto Gas limit app ISponsorPaymaster.RateLimitData memory gasLimit = costLimit[userOp.sender][targetAccount]; - if (block.timestamp < gasLimit.lastOperationTime + GAS_LIMIT_PERIOD) { + if (block.timestamp < gasLimit.lastOperationTime + appLimits[2]) { require( - (gasLimit.ethCostCount + ethMaxCost) <= GAS_LIMIT_THRESHOLD_SINGLE, "SP: Kinto Gas App limit exceeded" + (gasLimit.ethCostCount + ethMaxCost) <= appLimits[3], "SP: Kinto Gas App limit exceeded" ); } else { // First time need to be checked - require(ethMaxCost <= GAS_LIMIT_THRESHOLD_SINGLE, "SP: Kinto Gas App limit exceeded"); - } - - // Check Kinto rate limiting - ISponsorPaymaster.RateLimitData memory globalData = totalRateLimit[userOp.sender]; - if (block.timestamp < globalData.lastOperationTime + RATE_LIMIT_PERIOD) { - require(globalData.operationCount < RATE_LIMIT_THRESHOLD_TOTAL, "SP: Kinto Rate limit exceeded"); + require(ethMaxCost <= appLimits[3], "SP: Kinto Gas App limit exceeded"); } require(unlockBlock[targetAccount] == 0, "SP: deposit not locked"); @@ -238,9 +237,21 @@ contract SponsorPaymaster is Initializable, BasePaymaster, UUPSUpgradeable, Reen uint256 ethCost = (actualGasCost + COST_OF_POST * gasPricePostOp); balances[account] -= ethCost; contractSpent[account] += ethCost; + + // Global network rate limit + ISponsorPaymaster.RateLimitData storage globalTxLimit = totalRateLimit[userAccount]; + if (block.timestamp > globalTxLimit.lastOperationTime + RATE_LIMIT_PERIOD) { + globalTxLimit.lastOperationTime = block.timestamp; + globalTxLimit.operationCount = 1; + } else { + globalTxLimit.operationCount += 1; + } + + uint256[4] memory appLimits = appRegistry.getContractLimits(account); + // Updates app rate limiting ISponsorPaymaster.RateLimitData storage appTxLimit = rateLimit[userAccount][account]; - if (block.timestamp > appTxLimit.lastOperationTime + RATE_LIMIT_PERIOD) { + if (block.timestamp > appTxLimit.lastOperationTime + appLimits[0]) { appTxLimit.lastOperationTime = block.timestamp; appTxLimit.operationCount = 1; } else { @@ -248,20 +259,12 @@ contract SponsorPaymaster is Initializable, BasePaymaster, UUPSUpgradeable, Reen } // App gas limit ISponsorPaymaster.RateLimitData storage costApp = costLimit[userAccount][account]; - if (block.timestamp > costApp.lastOperationTime + GAS_LIMIT_PERIOD) { + if (block.timestamp > costApp.lastOperationTime + appLimits[2]) { costApp.lastOperationTime = block.timestamp; costApp.ethCostCount = ethCost; } else { costApp.ethCostCount += ethCost; } - // Global network rate limit - ISponsorPaymaster.RateLimitData storage globalTxLimit = totalRateLimit[userAccount]; - if (block.timestamp > globalTxLimit.lastOperationTime + RATE_LIMIT_PERIOD) { - globalTxLimit.lastOperationTime = block.timestamp; - globalTxLimit.operationCount = 1; - } else { - globalTxLimit.operationCount += 1; - } } // Function to extract the first target contract @@ -287,7 +290,7 @@ contract SponsorPaymaster is Initializable, BasePaymaster, UUPSUpgradeable, Reen } else if (selector == IKintoWallet.execute.selector) { // Decode callData for execute (address targetContract,,) = abi.decode(callData[4:], (address, uint256, bytes)); - lastTargetContract = targetContract; + lastTargetContract = appRegistry.getContractSponsor(targetContract); } else { // Handle unknown function or error revert("SP: Unknown function selector"); From d2f8616ac67502cdb6e7cdfbcdce3778946406c5 Mon Sep 17 00:00:00 2001 From: Ramon Recuero Date: Fri, 5 Jan 2024 11:03:30 -0800 Subject: [PATCH 06/43] Changes in the wallet --- src/interfaces/IKintoWallet.sol | 9 +- src/wallet/KintoWallet.sol | 80 ++++------------- test/KintoWallet.t.sol | 154 -------------------------------- 3 files changed, 19 insertions(+), 224 deletions(-) diff --git a/src/interfaces/IKintoWallet.sol b/src/interfaces/IKintoWallet.sol index b997bbe23..8d2418d5a 100644 --- a/src/interfaces/IKintoWallet.sol +++ b/src/interfaces/IKintoWallet.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.13; import {IEntryPoint} from "@aa/core/BaseAccount.sol"; import {IKintoWalletFactory} from "./IKintoWalletFactory.sol"; import {IKintoID} from "./IKintoID.sol"; +import {IKintoApp} from "./IKintoApp.sol"; interface IKintoWallet { /* ============ Structs ============ */ @@ -28,10 +29,6 @@ interface IKintoWallet { function cancelRecovery() external; - function approveTokens(address app, address[] calldata tokens, uint256[] calldata amount) external; - - function revokeTokens(address app, address[] calldata tokens) external; - function setAppKey(address app, address signer) external; function setAppWhitelist(address[] calldata apps, bool[] calldata flags) external; @@ -56,12 +53,12 @@ interface IKintoWallet { function isFunderWhitelisted(address funder) external view returns (bool); - function isTokenApproved(address app, address token) external view returns (uint256); - function appSigner(address app) external view returns (address); function appWhitelist(address app) external view returns (bool); + function appRegistry() external view returns (IKintoApp); + function signerPolicy() external view returns (uint8); /* solhint-disable func-name-mixedcase */ diff --git a/src/wallet/KintoWallet.sol b/src/wallet/KintoWallet.sol index c2c8991ee..6ab444fa1 100644 --- a/src/wallet/KintoWallet.sol +++ b/src/wallet/KintoWallet.sol @@ -14,6 +14,7 @@ import "../interfaces/IKintoEntryPoint.sol"; import "../libraries/ByteSignature.sol"; import "../interfaces/IKintoWallet.sol"; import "../interfaces/IKintoWalletFactory.sol"; +import "../interfaces/IKintoApp.sol"; /* solhint-disable avoid-low-level-calls */ /* solhint-disable no-inline-assembly */ @@ -45,9 +46,9 @@ contract KintoWallet is Initializable, BaseAccount, TokenCallbackHandler, IKinto address[] public override owners; address public override recoverer; mapping(address => bool) public override funderWhitelist; - mapping(address => mapping(address => uint256)) private _tokenApprovals; mapping(address => address) public override appSigner; mapping(address => bool) public override appWhitelist; + IKintoApp public override appRegistry = IKintoApp(address(0)); /* ============ Events ============ */ event KintoWalletInitialized(IEntryPoint indexed entryPoint, address indexed owner); @@ -168,40 +169,17 @@ contract KintoWallet is Initializable, BaseAccount, TokenCallbackHandler, IKinto return funderWhitelist[funder]; } - /* ============ Token Approvals ============ */ + /* ============ Token & App whitelists ============ */ /** - * @dev Approve tokens to a specific app - * @param app app address - * @param tokens tokens array - * @param amount amount array - */ - function approveTokens(address app, address[] calldata tokens, uint256[] calldata amount) - external - override - onlySelf - { - require(tokens.length == amount.length, "KW-at: invalid array"); - require(appWhitelist[app], "KW-at: app not whitelisted"); - for (uint256 i = 0; i < tokens.length; i++) { - if (_tokenApprovals[app][tokens[i]] > 0) { - IERC20(tokens[i]).approve(app, 0); - } - _tokenApprovals[app][tokens[i]] = amount[i]; - IERC20(tokens[i]).approve(app, amount[i]); - } - } - - /** - * @dev Revoke token approvals given to a specific app - * @param app app address - * @param tokens tokens array + * @dev Allos the wallet to transact with specific apps + * @param apps apps array + * @param flags whether to allow or disallow the app */ - function revokeTokens(address app, address[] calldata tokens) external override onlySelf { - require(appWhitelist[app], "KW-rt: app not whitelisted"); - for (uint256 i = 0; i < tokens.length; i++) { - _tokenApprovals[app][tokens[i]] = 0; - IERC20(tokens[i]).approve(app, 0); + function setAppWhitelist(address[] calldata apps, bool[] calldata flags) external override onlySelf { + require(apps.length == flags.length, "KW-apw: invalid array"); + for (uint256 i = 0; i < apps.length; i++) { + appWhitelist[apps[i]] = flags[i]; } } @@ -219,18 +197,6 @@ contract KintoWallet is Initializable, BaseAccount, TokenCallbackHandler, IKinto appSigner[app] = signer; } - /** - * @dev Allos the wallet to transact with a specific app - * @param apps apps array - * @param flags whether to allow or disallow the app - */ - function setAppWhitelist(address[] calldata apps, bool[] calldata flags) external override onlySelf { - require(apps.length == flags.length, "KW-apw: invalid array"); - for (uint256 i = 0; i < apps.length; i++) { - appWhitelist[apps[i]] = flags[i]; - } - } - /* ============ Recovery Process ============ */ /** @@ -288,10 +254,6 @@ contract KintoWallet is Initializable, BaseAccount, TokenCallbackHandler, IKinto return owners.length; } - function isTokenApproved(address app, address token) external view override returns (uint256) { - return _tokenApprovals[app][token]; - } - /* ============ IAccountOverrides ============ */ /// implement template method of BaseAccount @@ -371,14 +333,8 @@ contract KintoWallet is Initializable, BaseAccount, TokenCallbackHandler, IKinto } } - function _preventDirectApproval(bytes calldata _bytes) internal pure { - // Prevent direct deployment of KintoWallet contracts - bytes4 approvalBytes = bytes4(keccak256(bytes("approve(address,uint256)"))); - require(bytes4(_bytes[:4]) != approvalBytes, "KW: Direct ERC20 approval not allowed"); - } - - function _checkAppWhitelist(address app) internal view { - require(appWhitelist[app] || app == address(this), "KW: app not whitelisted"); + function _checkAppWhitelist(address _contract) internal view { + require(appWhitelist[appRegistry.getContractSponsor(_contract)] || _contract == address(this), "KW: contract not whitelisted"); } function _onlySelf() internal view { @@ -393,8 +349,6 @@ contract KintoWallet is Initializable, BaseAccount, TokenCallbackHandler, IKinto function _executeInner(address dest, uint256 value, bytes calldata func) internal { _checkAppWhitelist(dest); - // Prevents direct approval - _preventDirectApproval(func); dest.functionCallWithValue(func, value); } @@ -407,14 +361,11 @@ contract KintoWallet is Initializable, BaseAccount, TokenCallbackHandler, IKinto if (selector == IKintoWallet.executeBatch.selector) { // Decode callData for executeBatch (address[] memory targetContracts,,) = abi.decode(callData[4:], (address[], uint256[], bytes[])); - address lastTargetContract = targetContracts[targetContracts.length - 1]; + address lastTargetContract = appRegistry.getContractSponsor(targetContracts[targetContracts.length - 1]); for (uint256 i = 0; i < targetContracts.length; i++) { - // App signer should only be valid for the app itself and its tokens + // App signer should only be valid for the app itself and its children // It is important that wallet calls are not allowed through the app signer - if ( - targetContracts[i] != lastTargetContract // same contract - && _tokenApprovals[lastTargetContract][targetContracts[i]] == 0 - ) { + if (!appRegistry.isContractSponsoredByApp(lastTargetContract, targetContracts[i])) { return address(0); } } @@ -422,6 +373,7 @@ contract KintoWallet is Initializable, BaseAccount, TokenCallbackHandler, IKinto } else if (selector == IKintoWallet.execute.selector) { // Decode callData for execute (address targetContract,,) = abi.decode(callData[4:], (address, uint256, bytes)); + // Do not allow txs to the wallet via app key if (targetContract == address(this)) { return address(0); } diff --git a/test/KintoWallet.t.sol b/test/KintoWallet.t.sol index 6ec1bd098..588d833ce 100644 --- a/test/KintoWallet.t.sol +++ b/test/KintoWallet.t.sol @@ -1127,160 +1127,6 @@ contract KintoWalletTest is AATestScaffolding, UserOp { vm.stopPrank(); } - /* ============ Token Approvals ============ */ - - function testApproveTokens() public { - vm.startPrank(_owner); - _setPaymasterForContract(address(_kintoWalletv1)); - address app = address(100); - address[] memory tokens = new address[](1); - tokens[0] = address(_engenCredits); - uint256[] memory amounts = new uint256[](1); - amounts[0] = 1e12; - - uint256 startingNonce = _kintoWalletv1.getNonce(); - uint256[] memory privateKeys = new uint256[](1); - privateKeys[0] = 1; - - UserOperation memory userOp = this.createUserOperationWithPaymaster( - _chainID, - address(_kintoWalletv1), - startingNonce + 1, - privateKeys, - address(_kintoWalletv1), - 0, - abi.encodeWithSignature("approveTokens(address,address[],uint256[])", app, tokens, amounts), - address(_paymaster) - ); - UserOperation[] memory userOps = new UserOperation[](2); - userOps[0] = createApprovalUserOp( - _chainID, privateKeys, address(_kintoWalletv1), _kintoWalletv1.getNonce(), address(app), address(_paymaster) - ); - userOps[1] = userOp; - - _entryPoint.handleOps(userOps, payable(_owner)); - assertEq(_kintoWalletv1.isTokenApproved(app, tokens[0]), 1e12); - assertEq(_kintoWalletv1.isTokenApproved(app, address(24)), 0); - vm.stopPrank(); - } - - function testFailApproveTokensWithoutWhitelist() public { - vm.startPrank(_owner); - _setPaymasterForContract(address(_kintoWalletv1)); - address app = address(100); - address[] memory tokens = new address[](1); - tokens[0] = address(_engenCredits); - uint256[] memory amounts = new uint256[](1); - amounts[0] = 1e12; - - uint256 startingNonce = _kintoWalletv1.getNonce(); - uint256[] memory privateKeys = new uint256[](1); - privateKeys[0] = 1; - - UserOperation memory userOp = this.createUserOperationWithPaymaster( - _chainID, - address(_kintoWalletv1), - startingNonce + 1, - privateKeys, - address(_kintoWalletv1), - 0, - abi.encodeWithSignature("approveTokens(address,address[],uint256[])", app, tokens, amounts), - address(_paymaster) - ); - UserOperation[] memory userOps = new UserOperation[](2); - userOps[0] = userOp; - - _entryPoint.handleOps(userOps, payable(_owner)); - assertEq(_kintoWalletv1.isTokenApproved(app, tokens[0]), 1e12); - assertEq(_kintoWalletv1.isTokenApproved(app, address(24)), 0); - vm.stopPrank(); - } - - function testApproveAndRevokeTokens() public { - vm.startPrank(_owner); - _setPaymasterForContract(address(_kintoWalletv1)); - address app = address(100); - address[] memory tokens = new address[](1); - tokens[0] = address(_engenCredits); - uint256[] memory amounts = new uint256[](1); - amounts[0] = 1e12; - - uint256 startingNonce = _kintoWalletv1.getNonce(); - uint256[] memory privateKeys = new uint256[](1); - privateKeys[0] = 1; - - UserOperation memory userOp = this.createUserOperationWithPaymaster( - _chainID, - address(_kintoWalletv1), - startingNonce + 1, - privateKeys, - address(_kintoWalletv1), - 0, - abi.encodeWithSignature("approveTokens(address,address[],uint256[])", app, tokens, amounts), - address(_paymaster) - ); - UserOperation[] memory userOps = new UserOperation[](2); - userOps[0] = createApprovalUserOp( - _chainID, privateKeys, address(_kintoWalletv1), _kintoWalletv1.getNonce(), address(app), address(_paymaster) - ); - userOps[1] = userOp; - - _entryPoint.handleOps(userOps, payable(_owner)); - assertEq(_kintoWalletv1.isTokenApproved(app, tokens[0]), 1e12); - assertEq(_kintoWalletv1.isTokenApproved(app, address(24)), 0); - - userOps = new UserOperation[](1); - userOps[0] = this.createUserOperationWithPaymaster( - _chainID, - address(_kintoWalletv1), - _kintoWalletv1.getNonce(), - privateKeys, - address(_kintoWalletv1), - 0, - abi.encodeWithSignature("revokeTokens(address,address[])", app, tokens), - address(_paymaster) - ); - _entryPoint.handleOps(userOps, payable(_owner)); - assertEq(_kintoWalletv1.isTokenApproved(app, tokens[0]), 0); - - vm.stopPrank(); - } - - function testFailCallingApproveDirectly() public { - vm.startPrank(_owner); - _setPaymasterForContract(address(_engenCredits)); - address app = address(100); - - uint256 startingNonce = _kintoWalletv1.getNonce(); - uint256[] memory privateKeys = new uint256[](1); - privateKeys[0] = 1; - - UserOperation memory userOp = this.createUserOperationWithPaymaster( - _chainID, - address(_kintoWalletv1), - startingNonce + 1, - privateKeys, - address(_engenCredits), - 0, - abi.encodeWithSignature("approve(address,uint256)", address(app), 1e12), - address(_paymaster) - ); - UserOperation[] memory userOps = new UserOperation[](2); - userOps[0] = createApprovalUserOp( - _chainID, - privateKeys, - address(_kintoWalletv1), - _kintoWalletv1.getNonce(), - address(_engenCredits), - address(_paymaster) - ); - userOps[1] = userOp; - - _entryPoint.handleOps(userOps, payable(_owner)); - assertEq(_engenCredits.allowance(address(_kintoWalletv1), app), 1e12); - vm.stopPrank(); - } - /* ============ App Key ============ */ function testFailSettingAppKeyNoWhitelist() public { From 78764b67c15462ddb5664c1a3cfa27fb66a0a7fc Mon Sep 17 00:00:00 2001 From: Ramon Recuero Date: Fri, 5 Jan 2024 11:03:52 -0800 Subject: [PATCH 07/43] forge fmt --- src/apps/KintoApp.sol | 44 ++++++++++++++++++++--------- src/interfaces/IKintoApp.sol | 25 ++++++++++------ src/paymasters/SponsorPaymaster.sol | 9 +++--- src/wallet/KintoWallet.sol | 5 +++- 4 files changed, 56 insertions(+), 27 deletions(-) diff --git a/src/apps/KintoApp.sol b/src/apps/KintoApp.sol index 7422991cd..d93175ea7 100644 --- a/src/apps/KintoApp.sol +++ b/src/apps/KintoApp.sol @@ -28,7 +28,6 @@ contract KintoApp is UUPSUpgradeable, IKintoApp { - /* ============ Constants ============ */ bytes32 public constant override UPGRADER_ROLE = keccak256("UPGRADER_ROLE"); bytes32 public constant override DEVELOPER_ADMIN = keccak256("DEVELOPER_ADMIN"); @@ -37,15 +36,14 @@ contract KintoApp is uint256 public constant RATE_LIMIT_THRESHOLD = 10; uint256 public constant GAS_LIMIT_PERIOD = 30 days; uint256 public constant GAS_LIMIT_THRESHOLD = 1e16; // 0.01 ETH - + /* ============ State Variables ============ */ uint256 private _nextTokenId; - mapping (address => IKintoApp.Metadata) public appMetadata; - mapping (address => address) public childToParentContract; - mapping (address => mapping (address => bool)) public appSponsoredContracts; // other contracts to be sponsored - + mapping(address => IKintoApp.Metadata) public appMetadata; + mapping(address => address) public childToParentContract; + mapping(address => mapping(address => bool)) public appSponsoredContracts; // other contracts to be sponsored /* ============ Events ============ */ @@ -83,7 +81,12 @@ contract KintoApp is * @param childContracts The addresses of the child contracts * @param appLimits The limits of the app */ - function registerApp(string calldata _name, address parentContract, address[] calldata childContracts, uint256[4] calldata appLimits) external override { + function registerApp( + string calldata _name, + address parentContract, + address[] calldata childContracts, + uint256[4] calldata appLimits + ) external override { require(appLimits.length == 4, "Invalid app limits"); _updateMetadata(_name, parentContract, childContracts, appLimits); _nextTokenId++; @@ -96,7 +99,10 @@ contract KintoApp is * @param _contracts The addresses of the contracts * @param _flags The flags of the contracts */ - function setSponsoredContracts(address _app, address[] calldata _contracts, bool[] calldata _flags) external override { + function setSponsoredContracts(address _app, address[] calldata _contracts, bool[] calldata _flags) + external + override + { require(_contracts.length == _flags.length, "Invalid input"); require(msg.sender == appMetadata[_app].developerWallet, "Only developer can set sponsored contracts"); for (uint256 i = 0; i < _contracts.length; i++) { @@ -111,7 +117,12 @@ contract KintoApp is * @param childContracts The addresses of the child contracts * @param appLimits The limits of the app */ - function updateMetadata(string calldata _name, address parentContract, address[] calldata childContracts, uint256[4] calldata appLimits) external override { + function updateMetadata( + string calldata _name, + address parentContract, + address[] calldata childContracts, + uint256[4] calldata appLimits + ) external override { require(appLimits.length == 4, "Invalid app limits"); require(msg.sender == appMetadata[parentContract].developerWallet, "Only developer can update metadata"); _updateMetadata(_name, parentContract, childContracts, appLimits); @@ -134,7 +145,8 @@ contract KintoApp is * @return The metadata of the app */ function getAppMetadata(address _contract) external view override returns (IKintoApp.Metadata memory) { - address finalContract = childToParentContract[_contract] != address(0) ? childToParentContract[_contract] : _contract; + address finalContract = + childToParentContract[_contract] != address(0) ? childToParentContract[_contract] : _contract; return appMetadata[finalContract]; } @@ -144,7 +156,8 @@ contract KintoApp is * @return The limits of the app */ function getContractLimits(address _contract) external view override returns (uint256[4] memory) { - address finalContract = childToParentContract[_contract] != address(0) ? childToParentContract[_contract] : _contract; + address finalContract = + childToParentContract[_contract] != address(0) ? childToParentContract[_contract] : _contract; IKintoApp.Metadata memory metadata = appMetadata[finalContract]; return [ metadata.rateLimitPeriod != 0 ? metadata.rateLimitPeriod : RATE_LIMIT_PERIOD, @@ -206,7 +219,12 @@ contract KintoApp is } /* =========== App metadata params =========== */ - function _updateMetadata(string calldata _name, address parentContract, address[] calldata childContracts, uint256[4] calldata appLimits) internal { + function _updateMetadata( + string calldata _name, + address parentContract, + address[] calldata childContracts, + uint256[4] calldata appLimits + ) internal { IKintoApp.Metadata memory metadata = IKintoApp.Metadata({ name: _name, developerWallet: msg.sender, @@ -222,7 +240,6 @@ contract KintoApp is } } - /* ============ Disable token transfers ============ */ /** @@ -258,5 +275,4 @@ contract KintoApp is { return super.supportsInterface(interfaceId); } - } diff --git a/src/interfaces/IKintoApp.sol b/src/interfaces/IKintoApp.sol index 57a6e39fb..1ca448581 100644 --- a/src/interfaces/IKintoApp.sol +++ b/src/interfaces/IKintoApp.sol @@ -8,21 +8,31 @@ interface IKintoApp { string name; address developerWallet; // the address that deploys the wallet bool dsaEnabled; // whether or not this application can request PII from users - uint rateLimitPeriod; - uint rateLimitNumber; // in txs - uint gasLimitPeriod; - uint gasLimitCost; // in eth + uint256 rateLimitPeriod; + uint256 rateLimitNumber; // in txs + uint256 gasLimitPeriod; + uint256 gasLimitCost; // in eth } /* ============ State Change ============ */ - function registerApp(string calldata _name, address parentContract, address[] calldata childContracts, uint256[4] calldata appLimits) external; + function registerApp( + string calldata _name, + address parentContract, + address[] calldata childContracts, + uint256[4] calldata appLimits + ) external; function enableDSA(address app) external; function setSponsoredContracts(address _app, address[] calldata _contracts, bool[] calldata _flags) external; - function updateMetadata(string calldata _name, address parentContract, address[] calldata childContracts, uint256[4] calldata appLimits) external; + function updateMetadata( + string calldata _name, + address parentContract, + address[] calldata childContracts, + uint256[4] calldata appLimits + ) external; /* ============ Basic Viewers ============ */ @@ -31,7 +41,7 @@ interface IKintoApp { function symbol() external pure returns (string memory); function getContractLimits(address _contract) external view returns (uint256[4] memory); - + function getAppMetadata(address _contract) external view returns (Metadata memory); function getContractSponsor(address _contract) external view returns (address); @@ -43,5 +53,4 @@ interface IKintoApp { function DEVELOPER_ADMIN() external view returns (bytes32); function UPGRADER_ROLE() external view returns (bytes32); - } diff --git a/src/paymasters/SponsorPaymaster.sol b/src/paymasters/SponsorPaymaster.sol index 84f9661e4..4b0a2b4c6 100644 --- a/src/paymasters/SponsorPaymaster.sol +++ b/src/paymasters/SponsorPaymaster.sol @@ -214,9 +214,7 @@ contract SponsorPaymaster is Initializable, BasePaymaster, UUPSUpgradeable, Reen // Check Kinto Gas limit app ISponsorPaymaster.RateLimitData memory gasLimit = costLimit[userOp.sender][targetAccount]; if (block.timestamp < gasLimit.lastOperationTime + appLimits[2]) { - require( - (gasLimit.ethCostCount + ethMaxCost) <= appLimits[3], "SP: Kinto Gas App limit exceeded" - ); + require((gasLimit.ethCostCount + ethMaxCost) <= appLimits[3], "SP: Kinto Gas App limit exceeded"); } else { // First time need to be checked require(ethMaxCost <= appLimits[3], "SP: Kinto Gas App limit exceeded"); @@ -283,7 +281,10 @@ contract SponsorPaymaster is Initializable, BasePaymaster, UUPSUpgradeable, Reen lastTargetContract = appRegistry.getContractSponsor(targetContracts[targetContracts.length - 1]); // Last contract must be a contract app for (uint256 i = 0; i < targetContracts.length - 1; i++) { - if (!appRegistry.isContractSponsoredByApp(lastTargetContract, targetContracts[i]) && targetContracts[i] != sender) { + if ( + !appRegistry.isContractSponsoredByApp(lastTargetContract, targetContracts[i]) + && targetContracts[i] != sender + ) { revert("SP: executeBatch targets must be sponsored by the contract or be the sender wallet"); } } diff --git a/src/wallet/KintoWallet.sol b/src/wallet/KintoWallet.sol index 6ab444fa1..29a58ae9f 100644 --- a/src/wallet/KintoWallet.sol +++ b/src/wallet/KintoWallet.sol @@ -334,7 +334,10 @@ contract KintoWallet is Initializable, BaseAccount, TokenCallbackHandler, IKinto } function _checkAppWhitelist(address _contract) internal view { - require(appWhitelist[appRegistry.getContractSponsor(_contract)] || _contract == address(this), "KW: contract not whitelisted"); + require( + appWhitelist[appRegistry.getContractSponsor(_contract)] || _contract == address(this), + "KW: contract not whitelisted" + ); } function _onlySelf() internal view { From 7c5fea6ab8c40c7e0a9c635b9a322427d0d084c7 Mon Sep 17 00:00:00 2001 From: Ramon Recuero Date: Sat, 6 Jan 2024 12:36:36 -0800 Subject: [PATCH 08/43] cleanup --- src/apps/KintoApp.sol | 94 +++++++++++++-------------- src/interfaces/IKintoApp.sol | 12 ++++ test/KintoApp.t.sol | 108 +++++++++++++++++++++++++++++++ test/KintoWallet.t.sol | 2 +- test/SponsorPaymastExploit.t.sol | 2 +- 5 files changed, 169 insertions(+), 49 deletions(-) create mode 100644 test/KintoApp.t.sol diff --git a/src/apps/KintoApp.sol b/src/apps/KintoApp.sol index d93175ea7..122bac6cc 100644 --- a/src/apps/KintoApp.sol +++ b/src/apps/KintoApp.sol @@ -32,18 +32,18 @@ contract KintoApp is bytes32 public constant override UPGRADER_ROLE = keccak256("UPGRADER_ROLE"); bytes32 public constant override DEVELOPER_ADMIN = keccak256("DEVELOPER_ADMIN"); - uint256 public constant RATE_LIMIT_PERIOD = 1 minutes; - uint256 public constant RATE_LIMIT_THRESHOLD = 10; - uint256 public constant GAS_LIMIT_PERIOD = 30 days; - uint256 public constant GAS_LIMIT_THRESHOLD = 1e16; // 0.01 ETH + uint256 public constant override RATE_LIMIT_PERIOD = 1 minutes; + uint256 public constant override RATE_LIMIT_THRESHOLD = 10; + uint256 public constant override GAS_LIMIT_PERIOD = 30 days; + uint256 public constant override GAS_LIMIT_THRESHOLD = 1e16; // 0.01 ETH /* ============ State Variables ============ */ - uint256 private _nextTokenId; - - mapping(address => IKintoApp.Metadata) public appMetadata; - mapping(address => address) public childToParentContract; - mapping(address => mapping(address => bool)) public appSponsoredContracts; // other contracts to be sponsored + uint256 public override appCount; + mapping(address => IKintoApp.Metadata) private _appMetadata; + mapping(address => mapping(address => bool)) private _appSponsoredContracts; // other contracts to be sponsored + + mapping(address => address) public override childToParentContract; /* ============ Events ============ */ @@ -72,6 +72,32 @@ contract KintoApp is // This function is called by the proxy contract when the implementation is upgraded function _authorizeUpgrade(address newImplementation) internal override onlyRole(UPGRADER_ROLE) {} + /* ============ Token name, symbol & URI ============ */ + + /** + * @dev Gets the token name. + * @return string representing the token name + */ + function name() public pure override(ERC721Upgradeable, IKintoApp) returns (string memory) { + return "Kinto APP"; + } + + /** + * @dev Gets the token symbol. + * @return string representing the token symbol + */ + function symbol() public pure override(ERC721Upgradeable, IKintoApp) returns (string memory) { + return "KINTOAPP"; + } + + /** + * @dev Returns the base token URI. ID is appended + * @return token URI. + */ + function _baseURI() internal pure override returns (string memory) { + return "https://kinto.xyz/metadata/kintoapp/"; + } + /* ============ App Registration ============ */ /** @@ -89,8 +115,8 @@ contract KintoApp is ) external override { require(appLimits.length == 4, "Invalid app limits"); _updateMetadata(_name, parentContract, childContracts, appLimits); - _nextTokenId++; - _safeMint(msg.sender, _nextTokenId); + appCount++; + _safeMint(msg.sender, appCount); } /** @@ -104,9 +130,9 @@ contract KintoApp is override { require(_contracts.length == _flags.length, "Invalid input"); - require(msg.sender == appMetadata[_app].developerWallet, "Only developer can set sponsored contracts"); + require(msg.sender == _appMetadata[_app].developerWallet, "Only developer can set sponsored contracts"); for (uint256 i = 0; i < _contracts.length; i++) { - appSponsoredContracts[_app][_contracts[i]] = _flags[i]; + _appSponsoredContracts[_app][_contracts[i]] = _flags[i]; } } @@ -124,7 +150,7 @@ contract KintoApp is uint256[4] calldata appLimits ) external override { require(appLimits.length == 4, "Invalid app limits"); - require(msg.sender == appMetadata[parentContract].developerWallet, "Only developer can update metadata"); + require(msg.sender == _appMetadata[parentContract].developerWallet, "Only developer can update metadata"); _updateMetadata(_name, parentContract, childContracts, appLimits); } @@ -133,8 +159,8 @@ contract KintoApp is * @param app The name of the app */ function enableDSA(address app) external override onlyRole(DEVELOPER_ADMIN) { - require(appMetadata[app].dsaEnabled == false, "DSA already enabled"); - appMetadata[app].dsaEnabled = true; + require(_appMetadata[app].dsaEnabled == false, "DSA already enabled"); + _appMetadata[app].dsaEnabled = true; } /* ============ App Info Fetching ============ */ @@ -147,7 +173,7 @@ contract KintoApp is function getAppMetadata(address _contract) external view override returns (IKintoApp.Metadata memory) { address finalContract = childToParentContract[_contract] != address(0) ? childToParentContract[_contract] : _contract; - return appMetadata[finalContract]; + return _appMetadata[finalContract]; } /** @@ -158,7 +184,7 @@ contract KintoApp is function getContractLimits(address _contract) external view override returns (uint256[4] memory) { address finalContract = childToParentContract[_contract] != address(0) ? childToParentContract[_contract] : _contract; - IKintoApp.Metadata memory metadata = appMetadata[finalContract]; + IKintoApp.Metadata memory metadata = _appMetadata[finalContract]; return [ metadata.rateLimitPeriod != 0 ? metadata.rateLimitPeriod : RATE_LIMIT_PERIOD, metadata.rateLimitNumber != 0 ? metadata.rateLimitNumber : RATE_LIMIT_THRESHOLD, @@ -174,7 +200,7 @@ contract KintoApp is * @return bool true or false */ function isContractSponsoredByApp(address _app, address _contract) external view override returns (bool) { - return _contract == _app || childToParentContract[_contract] == _app || appSponsoredContracts[_app][_contract]; + return _contract == _app || childToParentContract[_contract] == _app || _appSponsoredContracts[_app][_contract]; } /** @@ -183,7 +209,7 @@ contract KintoApp is * @return The address of the contract that sponsors the contract */ function getContractSponsor(address _contract) external view override returns (address) { - if (appMetadata[_contract].developerWallet != address(0)) { + if (_appMetadata[_contract].developerWallet != address(0)) { return _contract; } if (childToParentContract[_contract] != address(0)) { @@ -192,32 +218,6 @@ contract KintoApp is return _contract; } - /* ============ Token name, symbol & URI ============ */ - - /** - * @dev Gets the token name. - * @return string representing the token name - */ - function name() public pure override(ERC721Upgradeable, IKintoApp) returns (string memory) { - return "Kinto APP"; - } - - /** - * @dev Gets the token symbol. - * @return string representing the token symbol - */ - function symbol() public pure override(ERC721Upgradeable, IKintoApp) returns (string memory) { - return "KINTOAPP"; - } - - /** - * @dev Returns the base token URI. ID is appended - * @return token URI. - */ - function _baseURI() internal pure override returns (string memory) { - return "https://kinto.xyz/metadata/kintoapp/"; - } - /* =========== App metadata params =========== */ function _updateMetadata( string calldata _name, @@ -234,7 +234,7 @@ contract KintoApp is gasLimitPeriod: appLimits[2], gasLimitCost: appLimits[3] }); - appMetadata[parentContract] = metadata; + _appMetadata[parentContract] = metadata; for (uint256 i = 0; i < childContracts.length; i++) { childToParentContract[childContracts[i]] = parentContract; } diff --git a/src/interfaces/IKintoApp.sol b/src/interfaces/IKintoApp.sol index 1ca448581..4ce3637e3 100644 --- a/src/interfaces/IKintoApp.sol +++ b/src/interfaces/IKintoApp.sol @@ -39,6 +39,10 @@ interface IKintoApp { function name() external pure returns (string memory); function symbol() external pure returns (string memory); + + function appCount() external view returns (uint256); + + function childToParentContract(address _contract) external view returns (address); function getContractLimits(address _contract) external view returns (uint256[4] memory); @@ -53,4 +57,12 @@ interface IKintoApp { function DEVELOPER_ADMIN() external view returns (bytes32); function UPGRADER_ROLE() external view returns (bytes32); + + function RATE_LIMIT_PERIOD() external view returns (uint256); + + function RATE_LIMIT_THRESHOLD() external view returns (uint256); + + function GAS_LIMIT_PERIOD() external view returns (uint256); + + function GAS_LIMIT_THRESHOLD() external view returns (uint256); } diff --git a/test/KintoApp.t.sol b/test/KintoApp.t.sol new file mode 100644 index 000000000..39a3029bf --- /dev/null +++ b/test/KintoApp.t.sol @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import "../src/wallet/KintoWallet.sol"; +import "../src/wallet/KintoWalletFactory.sol"; +import "../src/paymasters/SponsorPaymaster.sol"; +import "../src/KintoID.sol"; +import "../src/apps/KintoApp.sol"; +import {UserOp} from "./helpers/UserOp.sol"; +import {UUPSProxy} from "./helpers/UUPSProxy.sol"; +import {AATestScaffolding} from "./helpers/AATestScaffolding.sol"; +import {Create2Helper} from "./helpers/Create2Helper.sol"; +import "./helpers/KYCSignature.sol"; + +import "@aa/interfaces/IAccount.sol"; +import "@aa/interfaces/INonceManager.sol"; +import "@aa/interfaces/IEntryPoint.sol"; +import "@aa/core/EntryPoint.sol"; +import "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol"; +import {UpgradeableBeacon} from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; +import {SignatureChecker} from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; +import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; + +import "forge-std/Test.sol"; +import "forge-std/console.sol"; + +contract KintoAppV2 is KintoApp { + function newFunction() external pure returns (uint256) { + return 1; + } + + constructor() KintoApp() {} +} + +contract KintoAppTest is Create2Helper, UserOp, AATestScaffolding { + using ECDSAUpgradeable for bytes32; + using SignatureChecker for address; + + uint256 _chainID = 1; + + UUPSProxy _proxyViewer; + KintoApp _implkintoApp; + KintoAppV2 _implkintoAppV2; + KintoApp _kintoApp; + KintoAppV2 _kintoApp2; + + function setUp() public { + vm.chainId(_chainID); + vm.startPrank(address(1)); + _owner.transfer(1e18); + vm.stopPrank(); + deployAAScaffolding(_owner, 1, _kycProvider, _recoverer); + vm.startPrank(_owner); + _implkintoApp = new KintoApp{salt: 0}(); + // deploy _proxy contract and point it to _implementation + _proxyViewer = new UUPSProxy{salt: 0}(address(_implkintoApp), ""); + // wrap in ABI to support easier calls + _kintoApp = KintoApp(address(_proxyViewer)); + // Initialize kyc viewer _proxy + _kintoApp.initialize(); + vm.stopPrank(); + } + + function testUp() public { + console.log("address owner", address(_owner)); + assertEq(_kintoApp.hasRole(_kintoApp.UPGRADER_ROLE(), _owner), true); + assertEq(_kintoApp.name(), "Kinto APP"); + assertEq(_kintoApp.symbol(), "KINTOAPP"); + assertEq(_kintoApp.RATE_LIMIT_PERIOD(), 1 minutes); + assertEq(_kintoApp.RATE_LIMIT_THRESHOLD(), 10); + assertEq(_kintoApp.GAS_LIMIT_PERIOD(), 30 days); + assertEq(_kintoApp.GAS_LIMIT_THRESHOLD(), 1e16); + } + + /* ============ Upgrade Tests ============ */ + + function testOwnerCanUpgradeApp() public { + vm.startPrank(_owner); + KintoAppV2 _implementationV2 = new KintoAppV2(); + _kintoApp.upgradeTo(address(_implementationV2)); + // re-wrap the _proxy + _kintoApp2 = KintoAppV2(address(_kintoApp)); + assertEq(_kintoApp2.newFunction(), 1); + vm.stopPrank(); + } + + function test_RevertWhen_OthersCannotUpgradeFactory() public { + KintoAppV2 _implementationV2 = new KintoAppV2(); + vm.expectRevert("only owner"); + _kintoApp.upgradeTo(address(_implementationV2)); + } + + /* ============ App Tests ============ */ + + function testRegisterApp() public { + assertEq(_kintoApp.appCount(), 0); + address[] memory childContracts = new address[](1); + childContracts[0] = address(7); + uint256[] memory appLimits = new uint256[](4); + appLimits[0] = _kintoApp.RATE_LIMIT_PERIOD(); + appLimits[1] = _kintoApp.RATE_LIMIT_THRESHOLD(); + appLimits[2] = _kintoApp.GAS_LIMIT_PERIOD(); + appLimits[3] = _kintoApp.GAS_LIMIT_THRESHOLD(); + _kintoApp.registerApp("test", address(0), childContracts, [appLimits[0], appLimits[1], appLimits[2], appLimits[3]]); + assertEq(_kintoApp.appCount(), 1); + } +} diff --git a/test/KintoWallet.t.sol b/test/KintoWallet.t.sol index 78b631468..dc15483d1 100644 --- a/test/KintoWallet.t.sol +++ b/test/KintoWallet.t.sol @@ -187,7 +187,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // sender prefunds the contract vm.deal(address(_kintoWalletv1), 1 ether); vm.prank(address(_kintoWalletv1)); - payable(address(counter)).call{value: 1 ether}(""); + // address(counter).call{value: 1 ether}(""); UserOperation[] memory userOps = new UserOperation[](2); diff --git a/test/SponsorPaymastExploit.t.sol b/test/SponsorPaymastExploit.t.sol index cf0b7c9f6..fede593e2 100644 --- a/test/SponsorPaymastExploit.t.sol +++ b/test/SponsorPaymastExploit.t.sol @@ -32,7 +32,7 @@ contract MyOpCreator is UserOp, KYCSignature { uint256 value, bytes calldata _bytesOp, address _paymaster - ) public returns (UserOperation memory op) { + ) public view returns (UserOperation memory op) { op = UserOperation({ sender: _account, nonce: nonce, From 501e31b7b42cf9c1ba6bf2e01b6f1ce35bca7b46 Mon Sep 17 00:00:00 2001 From: Ramon Recuero Date: Sat, 6 Jan 2024 13:58:54 -0800 Subject: [PATCH 09/43] First tests of KintoApp --- src/apps/KintoApp.sol | 9 ++--- test/KintoApp.t.sol | 79 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 78 insertions(+), 10 deletions(-) diff --git a/src/apps/KintoApp.sol b/src/apps/KintoApp.sol index 122bac6cc..9b89e8951 100644 --- a/src/apps/KintoApp.sol +++ b/src/apps/KintoApp.sol @@ -3,7 +3,6 @@ pragma solidity ^0.8.12; import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721BurnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; @@ -23,7 +22,6 @@ contract KintoApp is Initializable, ERC721Upgradeable, ERC721EnumerableUpgradeable, - ERC721BurnableUpgradeable, AccessControlUpgradeable, UUPSUpgradeable, IKintoApp @@ -57,7 +55,6 @@ contract KintoApp is function initialize() external initializer { __ERC721_init("Kinto APP", "KINTOAPP"); __ERC721Enumerable_init(); - __ERC721Burnable_init(); __AccessControl_init(); __UUPSUpgradeable_init(); _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); @@ -189,7 +186,7 @@ contract KintoApp is metadata.rateLimitPeriod != 0 ? metadata.rateLimitPeriod : RATE_LIMIT_PERIOD, metadata.rateLimitNumber != 0 ? metadata.rateLimitNumber : RATE_LIMIT_THRESHOLD, metadata.gasLimitPeriod != 0 ? metadata.gasLimitPeriod : GAS_LIMIT_PERIOD, - metadata.gasLimitCost != 0 ? metadata.gasLimitPeriod : GAS_LIMIT_THRESHOLD + metadata.gasLimitCost != 0 ? metadata.gasLimitCost : GAS_LIMIT_THRESHOLD ]; } @@ -254,8 +251,8 @@ contract KintoApp is override(ERC721Upgradeable, ERC721EnumerableUpgradeable) { require( - (from == address(0) && to != address(0)) || (from != address(0) && to == address(0)), - "Only mint or burn transfers are allowed" + (from == address(0) && to != address(0)), + "Only mint transfers are allowed" ); super._beforeTokenTransfer(from, to, firstTokenId, batchSize); } diff --git a/test/KintoApp.t.sol b/test/KintoApp.t.sol index 39a3029bf..b9039f2de 100644 --- a/test/KintoApp.t.sol +++ b/test/KintoApp.t.sol @@ -87,13 +87,20 @@ contract KintoAppTest is Create2Helper, UserOp, AATestScaffolding { function test_RevertWhen_OthersCannotUpgradeFactory() public { KintoAppV2 _implementationV2 = new KintoAppV2(); - vm.expectRevert("only owner"); + bytes memory err = abi.encodePacked( + "AccessControl: account ", + Strings.toHexString(address(this)), + " is missing role ", + Strings.toHexString(uint256(_implementationV2.UPGRADER_ROLE()), 32) + ); + vm.expectRevert(err); _kintoApp.upgradeTo(address(_implementationV2)); } - /* ============ App Tests ============ */ + /* ============ App Tests & Viewers ============ */ - function testRegisterApp() public { + function testRegisterApp(string memory name ,address parentContract) public { + vm.startPrank(_user); assertEq(_kintoApp.appCount(), 0); address[] memory childContracts = new address[](1); childContracts[0] = address(7); @@ -102,7 +109,71 @@ contract KintoAppTest is Create2Helper, UserOp, AATestScaffolding { appLimits[1] = _kintoApp.RATE_LIMIT_THRESHOLD(); appLimits[2] = _kintoApp.GAS_LIMIT_PERIOD(); appLimits[3] = _kintoApp.GAS_LIMIT_THRESHOLD(); - _kintoApp.registerApp("test", address(0), childContracts, [appLimits[0], appLimits[1], appLimits[2], appLimits[3]]); + _kintoApp.registerApp(name, parentContract, childContracts, [appLimits[0], appLimits[1], appLimits[2], appLimits[3]]); assertEq(_kintoApp.appCount(), 1); + IKintoApp.Metadata memory metadata = _kintoApp.getAppMetadata(parentContract); + assertEq(metadata.name, name); + assertEq(metadata.developerWallet, address(_user)); + assertEq(metadata.dsaEnabled, false); + assertEq(metadata.rateLimitPeriod, appLimits[0]); + assertEq(metadata.rateLimitNumber, appLimits[1]); + assertEq(metadata.gasLimitPeriod, appLimits[2]); + assertEq(metadata.gasLimitCost, appLimits[3]); + assertEq(_kintoApp.isContractSponsoredByApp(parentContract, address(7)), true); + assertEq(_kintoApp.getContractSponsor(address(7)), parentContract); + uint256[4] memory limits = _kintoApp.getContractLimits(address(7)); + assertEq(limits[0], appLimits[0]); + assertEq(limits[1], appLimits[1]); + assertEq(limits[2], appLimits[2]); + assertEq(limits[3], appLimits[3]); + limits = _kintoApp.getContractLimits(parentContract); + assertEq(limits[0], appLimits[0]); + assertEq(limits[1], appLimits[1]); + assertEq(limits[2], appLimits[2]); + assertEq(limits[3], appLimits[3]); + metadata = _kintoApp.getAppMetadata(address(7)); + assertEq(metadata.name, name); + vm.stopPrank(); + } + + function testRegisterAppAndUpdate(string memory name ,address parentContract) public { + vm.startPrank(_user); + address[] memory childContracts = new address[](1); + childContracts[0] = address(8); + uint256[] memory appLimits = new uint256[](4); + appLimits[0] = _kintoApp.RATE_LIMIT_PERIOD(); + appLimits[1] = _kintoApp.RATE_LIMIT_THRESHOLD(); + appLimits[2] = _kintoApp.GAS_LIMIT_PERIOD(); + appLimits[3] = _kintoApp.GAS_LIMIT_THRESHOLD(); + _kintoApp.registerApp(name, parentContract, childContracts, [appLimits[0], appLimits[1], appLimits[2], appLimits[3]]); + _kintoApp.updateMetadata("test2", parentContract, childContracts, [uint(1), uint(1), uint(1), uint(1)]); + IKintoApp.Metadata memory metadata = _kintoApp.getAppMetadata(parentContract); + assertEq(metadata.name, "test2"); + assertEq(metadata.developerWallet, address(_user)); + assertEq(metadata.dsaEnabled, false); + assertEq(metadata.rateLimitPeriod, 1); + assertEq(metadata.rateLimitNumber, 1); + assertEq(metadata.gasLimitPeriod, 1); + assertEq(metadata.gasLimitCost, 1); + assertEq(_kintoApp.isContractSponsoredByApp(parentContract, address(7)), false); + assertEq(_kintoApp.isContractSponsoredByApp(parentContract, address(8)), true); + vm.stopPrank(); + } + + /* ============ Transfer Test ============ */ + + function test_RevertWhen_TransfersAreDisabled(address parentContract) public { + vm.startPrank(_user); + address[] memory childContracts = new address[](1); + childContracts[0] = address(8); + uint256[] memory appLimits = new uint256[](4); + appLimits[0] = _kintoApp.RATE_LIMIT_PERIOD(); + appLimits[1] = _kintoApp.RATE_LIMIT_THRESHOLD(); + appLimits[2] = _kintoApp.GAS_LIMIT_PERIOD(); + appLimits[3] = _kintoApp.GAS_LIMIT_THRESHOLD(); + _kintoApp.registerApp("", parentContract, childContracts, [appLimits[0], appLimits[1], appLimits[2], appLimits[3]]); + uint256 tokenIdx = _kintoApp.tokenOfOwnerByIndex(_user, 0); + vm.expectRevert("Only mint transfers are allowed"); + _kintoApp.safeTransferFrom(_user, _user2, tokenIdx); } } From a73523004834e5117122fbb32bec64d9a1fc732c Mon Sep 17 00:00:00 2001 From: Ramon Recuero Date: Sat, 6 Jan 2024 14:36:39 -0800 Subject: [PATCH 10/43] mechanism to set the new param in wallet impl --- src/apps/KintoApp.sol | 7 ++----- src/interfaces/IKintoApp.sol | 2 +- src/interfaces/IKintoWallet.sol | 4 ++++ src/wallet/KintoWallet.sol | 11 ++++++++++- src/wallet/KintoWalletFactory.sol | 12 ++++++++++++ test/KintoApp.t.sol | 20 ++++++++++++++------ 6 files changed, 43 insertions(+), 13 deletions(-) diff --git a/src/apps/KintoApp.sol b/src/apps/KintoApp.sol index 9b89e8951..3a68fdb07 100644 --- a/src/apps/KintoApp.sol +++ b/src/apps/KintoApp.sol @@ -40,7 +40,7 @@ contract KintoApp is uint256 public override appCount; mapping(address => IKintoApp.Metadata) private _appMetadata; mapping(address => mapping(address => bool)) private _appSponsoredContracts; // other contracts to be sponsored - + mapping(address => address) public override childToParentContract; /* ============ Events ============ */ @@ -250,10 +250,7 @@ contract KintoApp is virtual override(ERC721Upgradeable, ERC721EnumerableUpgradeable) { - require( - (from == address(0) && to != address(0)), - "Only mint transfers are allowed" - ); + require((from == address(0) && to != address(0)), "Only mint transfers are allowed"); super._beforeTokenTransfer(from, to, firstTokenId, batchSize); } diff --git a/src/interfaces/IKintoApp.sol b/src/interfaces/IKintoApp.sol index 4ce3637e3..e559e20ca 100644 --- a/src/interfaces/IKintoApp.sol +++ b/src/interfaces/IKintoApp.sol @@ -39,7 +39,7 @@ interface IKintoApp { function name() external pure returns (string memory); function symbol() external pure returns (string memory); - + function appCount() external view returns (uint256); function childToParentContract(address _contract) external view returns (address); diff --git a/src/interfaces/IKintoWallet.sol b/src/interfaces/IKintoWallet.sol index 8d2418d5a..bf9522287 100644 --- a/src/interfaces/IKintoWallet.sol +++ b/src/interfaces/IKintoWallet.sol @@ -33,6 +33,8 @@ interface IKintoWallet { function setAppWhitelist(address[] calldata apps, bool[] calldata flags) external; + function setAppRegistryAndWalletFactory(address _appRegistry, address _walletFactory) external; + /* ============ Basic Viewers ============ */ function getOwnersCount() external view returns (uint256); @@ -59,6 +61,8 @@ interface IKintoWallet { function appRegistry() external view returns (IKintoApp); + function walletFactory() external view returns (IKintoWalletFactory); + function signerPolicy() external view returns (uint8); /* solhint-disable func-name-mixedcase */ diff --git a/src/wallet/KintoWallet.sol b/src/wallet/KintoWallet.sol index 1379935ad..b9eeb749f 100644 --- a/src/wallet/KintoWallet.sol +++ b/src/wallet/KintoWallet.sol @@ -48,7 +48,8 @@ contract KintoWallet is Initializable, BaseAccount, TokenCallbackHandler, IKinto mapping(address => bool) public override funderWhitelist; mapping(address => address) public override appSigner; mapping(address => bool) public override appWhitelist; - IKintoApp public override appRegistry = IKintoApp(address(0)); + IKintoApp public override appRegistry; + IKintoWalletFactory public override walletFactory; /* ============ Events ============ */ event KintoWalletInitialized(IEntryPoint indexed entryPoint, address indexed owner); @@ -91,6 +92,14 @@ contract KintoWallet is Initializable, BaseAccount, TokenCallbackHandler, IKinto emit KintoWalletInitialized(_entryPoint, anOwner); } + // Function to be called upon wallet upgrade to save gas on subsequent calls to walletFactory + function setAppRegistryAndWalletFactory(address _appRegistry, address _walletFactory) external onlyFactory { + require(address(appRegistry) == address(0) && _appRegistry != address(0), "KW-i3: invalid address"); + require(address(walletFactory) == address(0) && _walletFactory != address(0), "KW-i3: invalid address"); + appRegistry = IKintoApp(_appRegistry); + walletFactory = IKintoWalletFactory(_walletFactory); + } + /* ============ Execution methods ============ */ /** diff --git a/src/wallet/KintoWalletFactory.sol b/src/wallet/KintoWalletFactory.sol index 49cdec463..d9ebb4138 100644 --- a/src/wallet/KintoWalletFactory.sol +++ b/src/wallet/KintoWalletFactory.sol @@ -266,3 +266,15 @@ contract KintoWalletFactory is Initializable, UUPSUpgradeable, OwnableUpgradeabl return created; } } + +contract KinwoWalletFactoryV2 is KintoWalletFactory { + constructor(KintoWallet _implAddressP) KintoWalletFactory(_implAddressP) {} + + function setAddressesOnImplementation( + IKintoWallet newImplementationWallet, + address _appRegistry, + address _walletFactory + ) external onlyOwner { + newImplementationWallet.setAppRegistryAndWalletFactory(_appRegistry, _walletFactory); + } +} diff --git a/test/KintoApp.t.sol b/test/KintoApp.t.sol index b9039f2de..6834c6506 100644 --- a/test/KintoApp.t.sol +++ b/test/KintoApp.t.sol @@ -99,7 +99,7 @@ contract KintoAppTest is Create2Helper, UserOp, AATestScaffolding { /* ============ App Tests & Viewers ============ */ - function testRegisterApp(string memory name ,address parentContract) public { + function testRegisterApp(string memory name, address parentContract) public { vm.startPrank(_user); assertEq(_kintoApp.appCount(), 0); address[] memory childContracts = new address[](1); @@ -109,7 +109,9 @@ contract KintoAppTest is Create2Helper, UserOp, AATestScaffolding { appLimits[1] = _kintoApp.RATE_LIMIT_THRESHOLD(); appLimits[2] = _kintoApp.GAS_LIMIT_PERIOD(); appLimits[3] = _kintoApp.GAS_LIMIT_THRESHOLD(); - _kintoApp.registerApp(name, parentContract, childContracts, [appLimits[0], appLimits[1], appLimits[2], appLimits[3]]); + _kintoApp.registerApp( + name, parentContract, childContracts, [appLimits[0], appLimits[1], appLimits[2], appLimits[3]] + ); assertEq(_kintoApp.appCount(), 1); IKintoApp.Metadata memory metadata = _kintoApp.getAppMetadata(parentContract); assertEq(metadata.name, name); @@ -136,7 +138,7 @@ contract KintoAppTest is Create2Helper, UserOp, AATestScaffolding { vm.stopPrank(); } - function testRegisterAppAndUpdate(string memory name ,address parentContract) public { + function testRegisterAppAndUpdate(string memory name, address parentContract) public { vm.startPrank(_user); address[] memory childContracts = new address[](1); childContracts[0] = address(8); @@ -145,8 +147,12 @@ contract KintoAppTest is Create2Helper, UserOp, AATestScaffolding { appLimits[1] = _kintoApp.RATE_LIMIT_THRESHOLD(); appLimits[2] = _kintoApp.GAS_LIMIT_PERIOD(); appLimits[3] = _kintoApp.GAS_LIMIT_THRESHOLD(); - _kintoApp.registerApp(name, parentContract, childContracts, [appLimits[0], appLimits[1], appLimits[2], appLimits[3]]); - _kintoApp.updateMetadata("test2", parentContract, childContracts, [uint(1), uint(1), uint(1), uint(1)]); + _kintoApp.registerApp( + name, parentContract, childContracts, [appLimits[0], appLimits[1], appLimits[2], appLimits[3]] + ); + _kintoApp.updateMetadata( + "test2", parentContract, childContracts, [uint256(1), uint256(1), uint256(1), uint256(1)] + ); IKintoApp.Metadata memory metadata = _kintoApp.getAppMetadata(parentContract); assertEq(metadata.name, "test2"); assertEq(metadata.developerWallet, address(_user)); @@ -171,7 +177,9 @@ contract KintoAppTest is Create2Helper, UserOp, AATestScaffolding { appLimits[1] = _kintoApp.RATE_LIMIT_THRESHOLD(); appLimits[2] = _kintoApp.GAS_LIMIT_PERIOD(); appLimits[3] = _kintoApp.GAS_LIMIT_THRESHOLD(); - _kintoApp.registerApp("", parentContract, childContracts, [appLimits[0], appLimits[1], appLimits[2], appLimits[3]]); + _kintoApp.registerApp( + "", parentContract, childContracts, [appLimits[0], appLimits[1], appLimits[2], appLimits[3]] + ); uint256 tokenIdx = _kintoApp.tokenOfOwnerByIndex(_user, 0); vm.expectRevert("Only mint transfers are allowed"); _kintoApp.safeTransferFrom(_user, _user2, tokenIdx); From 7983d4d4271112429182288d2f78f12afccf93e0 Mon Sep 17 00:00:00 2001 From: Ramon Recuero Date: Sat, 6 Jan 2024 14:48:28 -0800 Subject: [PATCH 11/43] More tests passing --- src/wallet/KintoWalletFactory.sol | 6 +----- test/KintoApp.t.sol | 12 ------------ test/helpers/AATestScaffolding.sol | 29 +++++++++++++++++++++++++++++ 3 files changed, 30 insertions(+), 17 deletions(-) diff --git a/src/wallet/KintoWalletFactory.sol b/src/wallet/KintoWalletFactory.sol index d9ebb4138..bb03cd71f 100644 --- a/src/wallet/KintoWalletFactory.sol +++ b/src/wallet/KintoWalletFactory.sol @@ -265,10 +265,6 @@ contract KintoWalletFactory is Initializable, UUPSUpgradeable, OwnableUpgradeabl } catch {} return created; } -} - -contract KinwoWalletFactoryV2 is KintoWalletFactory { - constructor(KintoWallet _implAddressP) KintoWalletFactory(_implAddressP) {} function setAddressesOnImplementation( IKintoWallet newImplementationWallet, @@ -277,4 +273,4 @@ contract KinwoWalletFactoryV2 is KintoWalletFactory { ) external onlyOwner { newImplementationWallet.setAppRegistryAndWalletFactory(_appRegistry, _walletFactory); } -} +} \ No newline at end of file diff --git a/test/KintoApp.t.sol b/test/KintoApp.t.sol index 6834c6506..2b9bd4bc9 100644 --- a/test/KintoApp.t.sol +++ b/test/KintoApp.t.sol @@ -39,10 +39,7 @@ contract KintoAppTest is Create2Helper, UserOp, AATestScaffolding { uint256 _chainID = 1; - UUPSProxy _proxyViewer; - KintoApp _implkintoApp; KintoAppV2 _implkintoAppV2; - KintoApp _kintoApp; KintoAppV2 _kintoApp2; function setUp() public { @@ -51,15 +48,6 @@ contract KintoAppTest is Create2Helper, UserOp, AATestScaffolding { _owner.transfer(1e18); vm.stopPrank(); deployAAScaffolding(_owner, 1, _kycProvider, _recoverer); - vm.startPrank(_owner); - _implkintoApp = new KintoApp{salt: 0}(); - // deploy _proxy contract and point it to _implementation - _proxyViewer = new UUPSProxy{salt: 0}(address(_implkintoApp), ""); - // wrap in ABI to support easier calls - _kintoApp = KintoApp(address(_proxyViewer)); - // Initialize kyc viewer _proxy - _kintoApp.initialize(); - vm.stopPrank(); } function testUp() public { diff --git a/test/helpers/AATestScaffolding.sol b/test/helpers/AATestScaffolding.sol index 7214d1c37..9f4f039b8 100644 --- a/test/helpers/AATestScaffolding.sol +++ b/test/helpers/AATestScaffolding.sol @@ -11,6 +11,7 @@ import {UUPSProxy} from "../helpers/UUPSProxy.sol"; import {KYCSignature} from "../helpers/KYCSignature.sol"; import "../../src/wallet/KintoWallet.sol"; +import "../../src/apps/KintoApp.sol"; import "../../src/tokens/EngenCredits.sol"; import "../../src/wallet/KintoWalletFactory.sol"; import "../../src/paymasters/SponsorPaymaster.sol"; @@ -30,6 +31,7 @@ abstract contract AATestScaffolding is KYCSignature { IKintoEntryPoint _entryPoint; KintoWalletFactory _walletFactoryI; KintoWalletFactory _walletFactory; + KintoApp _kintoApp; KintoID _implementation; KintoID _kintoIDv1; SponsorPaymaster _paymaster; @@ -40,6 +42,7 @@ abstract contract AATestScaffolding is KYCSignature { UUPSProxy _proxyf; UUPSProxy _proxys; UUPSProxy _proxycredit; + UUPSProxy _proxyapp; EngenCredits _engenCredits; function deployAAScaffolding(address _owner, uint256 _ownerPk, address _kycProvider, address _recoverer) public { @@ -67,6 +70,9 @@ abstract contract AATestScaffolding is KYCSignature { // Deploy Engen Credits deployEngenCredits(_owner); + // Deploy Kinto App + deployKintoApp(_owner); + // Give some eth vm.deal(_owner, 1e20); } @@ -149,6 +155,29 @@ abstract contract AATestScaffolding is KYCSignature { vm.stopPrank(); } + function deployKintoApp(address _owner) public { + vm.startPrank(_owner); + + // deploy the Kinto App registry + _kintoApp = new KintoApp{salt: 0}(); + + // deploy _proxy contract and point it to _implementation + _proxyapp = new UUPSProxy{salt: 0}(address(_kintoApp), ""); + + // wrap in ABI to support easier calls + _kintoApp = KintoApp(address(_proxyapp)); + + // initialize proxy + _kintoApp.initialize(); + + // Upgrade to a new impl with the registry and wallet factory + _kintoWalletImpl = new KintoWallet{salt: 0}(_entryPoint, _kintoIDv1); + _walletFactory.setAddressesOnImplementation(_kintoWalletImpl, address(_kintoApp), address(_walletFactory)); + _walletFactory.upgradeAllWalletImplementations(_kintoWalletImpl); + + vm.stopPrank(); + } + function approveKYC(address _kycProvider, address _account, uint256 _accountPk) public { vm.startPrank(_kycProvider); From 3df9a65e71dfd6bdf48e5db4ddd2ac62d5ba14f9 Mon Sep 17 00:00:00 2001 From: Ramon Recuero Date: Sat, 6 Jan 2024 20:22:54 -0800 Subject: [PATCH 12/43] More test fixes --- script/deploy.sol | 2 +- script/migrations/02-upgrade_wallet.sol | 7 +- script/migrations/04-deploy_admin_wallet.sol | 1 - script/migrations/05-deploy_faucet.sol | 1 - script/migrations/06-counter_advance.sol | 1 - script/upgrade.sol | 6 +- src/interfaces/IKintoWallet.sol | 4 -- src/wallet/KintoWallet.sol | 18 ++--- src/wallet/KintoWalletFactory.sol | 7 -- test/EngenCredits.t.sol | 8 +++ test/KintoWallet.t.sol | 6 +- test/KintoWalletFactory.t.sol | 6 +- test/SponsorPaymastExploit.t.sol | 70 ++------------------ test/helpers/AATestScaffolding.sol | 25 ++++--- 14 files changed, 47 insertions(+), 115 deletions(-) diff --git a/script/deploy.sol b/script/deploy.sol index 08fe129e7..f8112b063 100644 --- a/script/deploy.sol +++ b/script/deploy.sol @@ -105,7 +105,7 @@ contract KintoInitialDeployScript is Create2Helper, ArtifactsReader { console.log("Wallet Implementation already deployed at", address(walletImplementationAddr)); } else { // Deploy Wallet Implementation - _walletImpl = new KintoWallet{salt: 0}(_entryPoint, _kintoIDv1); + _walletImpl = new KintoWallet{salt: 0}(_entryPoint, _kintoIDv1, IKintoApp(address(0))); console.log("Wallet Implementation deployed at", address(_walletImpl)); } diff --git a/script/migrations/02-upgrade_wallet.sol b/script/migrations/02-upgrade_wallet.sol index 415fde063..c0ee1b0aa 100644 --- a/script/migrations/02-upgrade_wallet.sol +++ b/script/migrations/02-upgrade_wallet.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.13; import "forge-std/Script.sol"; import "../../src/wallet/KintoWalletFactory.sol"; -import {KintoWalletV2} from "../../src/wallet/KintoWallet.sol"; +import {KintoWallet} from "../../src/wallet/KintoWallet.sol"; import {Create2Helper} from "../../test/helpers/Create2Helper.sol"; import {ArtifactsReader} from "../../test/helpers/ArtifactsReader.sol"; import {UUPSProxy} from "../../test/helpers/UUPSProxy.sol"; @@ -12,6 +12,11 @@ import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import "forge-std/console.sol"; +// Upgradeable version of KintoWallet +contract KintoWalletV2 is KintoWallet { + constructor(IEntryPoint _entryPoint, IKintoID _kintoID) KintoWallet(_entryPoint, _kintoID, IKintoApp(address(0))) {} +} + contract KintoMigration2DeployScript is Create2Helper, ArtifactsReader { using ECDSAUpgradeable for bytes32; diff --git a/script/migrations/04-deploy_admin_wallet.sol b/script/migrations/04-deploy_admin_wallet.sol index 211fd0c24..6db8d93eb 100644 --- a/script/migrations/04-deploy_admin_wallet.sol +++ b/script/migrations/04-deploy_admin_wallet.sol @@ -4,7 +4,6 @@ pragma solidity ^0.8.13; import "forge-std/Script.sol"; import "../../src/wallet/KintoWalletFactory.sol"; import "../../src/KintoID.sol"; -import {KintoWalletV2} from "../../src/wallet/KintoWallet.sol"; import {Create2Helper} from "../../test/helpers/Create2Helper.sol"; import {ArtifactsReader} from "../../test/helpers/ArtifactsReader.sol"; import {UUPSProxy} from "../../test/helpers/UUPSProxy.sol"; diff --git a/script/migrations/05-deploy_faucet.sol b/script/migrations/05-deploy_faucet.sol index 89b996603..062134e04 100644 --- a/script/migrations/05-deploy_faucet.sol +++ b/script/migrations/05-deploy_faucet.sol @@ -3,7 +3,6 @@ pragma solidity ^0.8.13; import "forge-std/Script.sol"; import "../../src/Faucet.sol"; -import {KintoWalletV2} from "../../src/wallet/KintoWallet.sol"; import {Create2Helper} from "../../test/helpers/Create2Helper.sol"; import {ArtifactsReader} from "../../test/helpers/ArtifactsReader.sol"; import {UUPSProxy} from "../../test/helpers/UUPSProxy.sol"; diff --git a/script/migrations/06-counter_advance.sol b/script/migrations/06-counter_advance.sol index 8768666b0..6c9739781 100644 --- a/script/migrations/06-counter_advance.sol +++ b/script/migrations/06-counter_advance.sol @@ -3,7 +3,6 @@ pragma solidity ^0.8.13; import "forge-std/Script.sol"; import "../../src/sample/Counter.sol"; -import {KintoWalletV2} from "../../src/wallet/KintoWallet.sol"; import {Create2Helper} from "../../test/helpers/Create2Helper.sol"; import {ArtifactsReader} from "../../test/helpers/ArtifactsReader.sol"; import {UUPSProxy} from "../../test/helpers/UUPSProxy.sol"; diff --git a/script/upgrade.sol b/script/upgrade.sol index 613039793..bc18b13ec 100644 --- a/script/upgrade.sol +++ b/script/upgrade.sol @@ -47,8 +47,8 @@ contract KintoWalletFactoryUpgradeScript is ArtifactsReader { } } -contract KintoWalletV3 is KintoWallet { - constructor(IEntryPoint _entryPoint, IKintoID _kintoIDv1) KintoWallet(_entryPoint, _kintoIDv1) {} +contract KintoWalletVTest is KintoWallet { + constructor(IEntryPoint _entryPoint, IKintoID _kintoIDv1, IKintoApp _kintoApp) KintoWallet(_entryPoint, _kintoIDv1, _kintoApp) {} } contract KintoWalletsUpgradeScript is ArtifactsReader { @@ -67,7 +67,7 @@ contract KintoWalletsUpgradeScript is ArtifactsReader { _walletFactory = KintoWalletFactory(payable(_getChainDeployment("KintoWalletFactory"))); // Deploy new wallet implementation _kintoWalletImpl = - new KintoWalletV3(IEntryPoint(_getChainDeployment("EntryPoint")), IKintoID(_getChainDeployment("KintoID"))); + new KintoWalletVTest(IEntryPoint(_getChainDeployment("EntryPoint")), IKintoID(_getChainDeployment("KintoID")), IKintoApp(_getChainDeployment("KintoApp"))); // // Upgrade all implementations _walletFactory.upgradeAllWalletImplementations(_kintoWalletImpl); vm.stopBroadcast(); diff --git a/src/interfaces/IKintoWallet.sol b/src/interfaces/IKintoWallet.sol index bf9522287..8d2418d5a 100644 --- a/src/interfaces/IKintoWallet.sol +++ b/src/interfaces/IKintoWallet.sol @@ -33,8 +33,6 @@ interface IKintoWallet { function setAppWhitelist(address[] calldata apps, bool[] calldata flags) external; - function setAppRegistryAndWalletFactory(address _appRegistry, address _walletFactory) external; - /* ============ Basic Viewers ============ */ function getOwnersCount() external view returns (uint256); @@ -61,8 +59,6 @@ interface IKintoWallet { function appRegistry() external view returns (IKintoApp); - function walletFactory() external view returns (IKintoWalletFactory); - function signerPolicy() external view returns (uint8); /* solhint-disable func-name-mixedcase */ diff --git a/src/wallet/KintoWallet.sol b/src/wallet/KintoWallet.sol index b9eeb749f..871eb0fe3 100644 --- a/src/wallet/KintoWallet.sol +++ b/src/wallet/KintoWallet.sol @@ -48,8 +48,7 @@ contract KintoWallet is Initializable, BaseAccount, TokenCallbackHandler, IKinto mapping(address => bool) public override funderWhitelist; mapping(address => address) public override appSigner; mapping(address => bool) public override appWhitelist; - IKintoApp public override appRegistry; - IKintoWalletFactory public override walletFactory; + IKintoApp public immutable override appRegistry; /* ============ Events ============ */ event KintoWalletInitialized(IEntryPoint indexed entryPoint, address indexed owner); @@ -70,9 +69,10 @@ contract KintoWallet is Initializable, BaseAccount, TokenCallbackHandler, IKinto /* ============ Constructor & Initializers ============ */ - constructor(IEntryPoint __entryPoint, IKintoID _kintoID) { + constructor(IEntryPoint __entryPoint, IKintoID _kintoID, IKintoApp _kintoApp) { _entryPoint = __entryPoint; kintoID = _kintoID; + appRegistry = IKintoApp(_kintoApp); _disableInitializers(); } @@ -92,14 +92,6 @@ contract KintoWallet is Initializable, BaseAccount, TokenCallbackHandler, IKinto emit KintoWalletInitialized(_entryPoint, anOwner); } - // Function to be called upon wallet upgrade to save gas on subsequent calls to walletFactory - function setAppRegistryAndWalletFactory(address _appRegistry, address _walletFactory) external onlyFactory { - require(address(appRegistry) == address(0) && _appRegistry != address(0), "KW-i3: invalid address"); - require(address(walletFactory) == address(0) && _walletFactory != address(0), "KW-i3: invalid address"); - appRegistry = IKintoApp(_appRegistry); - walletFactory = IKintoWalletFactory(_walletFactory); - } - /* ============ Execution methods ============ */ /** @@ -396,6 +388,6 @@ contract KintoWallet is Initializable, BaseAccount, TokenCallbackHandler, IKinto } // Upgradeable version of KintoWallet -contract KintoWalletV2 is KintoWallet { - constructor(IEntryPoint _entryPoint, IKintoID _kintoID) KintoWallet(_entryPoint, _kintoID) {} +contract KintoWalletV3 is KintoWallet { + constructor(IEntryPoint _entryPoint, IKintoID _kintoID, IKintoApp _kintoApp) KintoWallet(_entryPoint, _kintoID, _kintoApp) {} } diff --git a/src/wallet/KintoWalletFactory.sol b/src/wallet/KintoWalletFactory.sol index bb03cd71f..86da64f78 100644 --- a/src/wallet/KintoWalletFactory.sol +++ b/src/wallet/KintoWalletFactory.sol @@ -266,11 +266,4 @@ contract KintoWalletFactory is Initializable, UUPSUpgradeable, OwnableUpgradeabl return created; } - function setAddressesOnImplementation( - IKintoWallet newImplementationWallet, - address _appRegistry, - address _walletFactory - ) external onlyOwner { - newImplementationWallet.setAppRegistryAndWalletFactory(_appRegistry, _walletFactory); - } } \ No newline at end of file diff --git a/test/EngenCredits.t.sol b/test/EngenCredits.t.sol index 5883c9abf..ef9c679ad 100644 --- a/test/EngenCredits.t.sol +++ b/test/EngenCredits.t.sol @@ -31,6 +31,14 @@ contract EngenCreditsTest is Create2Helper, UserOp, AATestScaffolding { deployAAScaffolding(_owner, 1, _kycProvider, _recoverer); _fundPaymasterForContract(address(_engenCredits)); _fundPaymasterForContract(address(_kintoWalletv1)); + vm.startPrank(_owner); + _kintoApp.registerApp( + "engen credits", + address(_engenCredits), + new address[](0), + [uint256(0), uint256(0), uint256(0), uint256(0)] + ); + vm.stopPrank(); } function testUp() public { diff --git a/test/KintoWallet.t.sol b/test/KintoWallet.t.sol index dc15483d1..1f60816b5 100644 --- a/test/KintoWallet.t.sol +++ b/test/KintoWallet.t.sol @@ -25,7 +25,7 @@ import {Test, stdError} from "forge-std/Test.sol"; import "forge-std/console.sol"; contract KintoWalletv2 is KintoWallet { - constructor(IEntryPoint _entryPoint, IKintoID _kintoID) KintoWallet(_entryPoint, _kintoID) {} + constructor(IEntryPoint _entryPoint, IKintoID _kintoID, IKintoApp _kintoApp) KintoWallet(_entryPoint, _kintoID, _kintoApp) {} function newFunction() public pure returns (uint256) { return 1; @@ -88,7 +88,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { function test_RevertWhen_OwnerCannotUpgrade() public { // deploy a KintoWalletv2 - KintoWalletv2 _implementationV2 = new KintoWalletv2(_entryPoint, _kintoIDv1); + KintoWalletv2 _implementationV2 = new KintoWalletv2(_entryPoint, _kintoIDv1, _kintoApp); uint256 nonce = _kintoWalletv1.getNonce(); @@ -121,7 +121,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { IKintoWallet userWallet = _walletFactory.createAccount(_user, _recoverer, 0); // deploy a KintoWalletv2 - KintoWalletv2 _implementationV2 = new KintoWalletv2(_entryPoint, _kintoIDv1); + KintoWalletv2 _implementationV2 = new KintoWalletv2(_entryPoint, _kintoIDv1, _kintoApp); // try calling upgradeTo from _user wallet to upgrade _owner wallet uint256 nonce = userWallet.getNonce(); diff --git a/test/KintoWalletFactory.t.sol b/test/KintoWalletFactory.t.sol index 46244828f..7ba952a71 100644 --- a/test/KintoWalletFactory.t.sol +++ b/test/KintoWalletFactory.t.sol @@ -24,7 +24,7 @@ import "forge-std/Test.sol"; import "forge-std/console.sol"; contract KintoWalletV999 is KintoWallet { - constructor(IEntryPoint _entryPoint, IKintoID _kintoID) KintoWallet(_entryPoint, _kintoID) {} + constructor(IEntryPoint _entryPoint, IKintoID _kintoID, IKintoApp _kintoApp) KintoWallet(_entryPoint, _kintoID, _kintoApp) {} function walletFunction() public pure returns (uint256) { return 1; @@ -95,7 +95,7 @@ contract KintoWalletFactoryTest is Create2Helper, UserOp, AATestScaffolding { vm.startPrank(_owner); // Deploy wallet implementation - _kintoWalletImpl = new KintoWalletV999(_entryPoint, _kintoIDv1); + _kintoWalletImpl = new KintoWalletV999(_entryPoint, _kintoIDv1, _kintoApp); // deploy walletv1 through wallet factory and initializes it _kintoWalletv1 = _walletFactory.createAccount(_owner, _owner, 0); @@ -110,7 +110,7 @@ contract KintoWalletFactoryTest is Create2Helper, UserOp, AATestScaffolding { function testUpgrade_RevertWhen_CallerIsNotOwner() public { // deploy wallet implementation - _kintoWalletImpl = new KintoWalletV999(_entryPoint, _kintoIDv1); + _kintoWalletImpl = new KintoWalletV999(_entryPoint, _kintoIDv1, _kintoApp); // deploy walletv1 through wallet factory and initializes it _kintoWalletv1 = _walletFactory.createAccount(_owner, _owner, 0); diff --git a/test/SponsorPaymastExploit.t.sol b/test/SponsorPaymastExploit.t.sol index fede593e2..26b67ad08 100644 --- a/test/SponsorPaymastExploit.t.sol +++ b/test/SponsorPaymastExploit.t.sol @@ -7,6 +7,8 @@ import "../src/paymasters/SponsorPaymaster.sol"; import "../src/KintoID.sol"; import {UserOp} from "./helpers/UserOp.sol"; import {UUPSProxy} from "./helpers/UUPSProxy.sol"; +import {AATestScaffolding} from "./helpers/AATestScaffolding.sol"; +import {Create2Helper} from "./helpers/Create2Helper.sol"; import {KYCSignature} from "./helpers/KYCSignature.sol"; import "@aa/interfaces/IAccount.sol"; @@ -63,24 +65,10 @@ contract Counter { } } -contract SponsorPaymasterExploitTest is MyOpCreator { +contract SponsorPaymasterExploitTest is Create2Helper, MyOpCreator, AATestScaffolding { using ECDSAUpgradeable for bytes32; using SignatureChecker for address; - EntryPoint _entryPoint; - KintoWalletFactory _walletFactoryI; - KintoWalletFactory _walletFactory; - KintoID _implementation; - KintoID _kintoIDv1; - SponsorPaymaster _paymaster; - - KintoWallet _kintoWalletImpl; - IKintoWallet _kintoWalletv1; - UUPSProxy _proxy; - UUPSProxy _proxyf; - UUPSProxy _proxys; - UpgradeableBeacon _beacon; - uint256 _chainID = 1; function setUp() public { @@ -88,47 +76,9 @@ contract SponsorPaymasterExploitTest is MyOpCreator { vm.startPrank(address(1)); _owner.transfer(1e18); vm.stopPrank(); - vm.startPrank(_owner); - // Deploy Kinto ID - _implementation = new KintoID(); - // deploy _proxy contract and point it to _implementation - _proxy = new UUPSProxy{salt: 0}(address(_implementation), ""); - // wrap in ABI to support easier calls - _kintoIDv1 = KintoID(address(_proxy)); - // Initialize _proxy - _kintoIDv1.initialize(); - _kintoIDv1.grantRole(_kintoIDv1.KYC_PROVIDER_ROLE(), _kycProvider); - _entryPoint = new EntryPoint{salt: 0}(); - // Deploy wallet implementation - _kintoWalletImpl = new KintoWallet{salt: 0}(_entryPoint, _kintoIDv1); - //Deploy wallet factory implementation - _walletFactoryI = new KintoWalletFactory{salt: 0}(_kintoWalletImpl); - _proxyf = new UUPSProxy{salt: 0}(address(_walletFactoryI), ""); - _walletFactory = KintoWalletFactory(address(_proxyf)); - // Initialize wallet factory - _walletFactory.initialize(_kintoIDv1); - // Set the wallet factory in the entry point - _entryPoint.setWalletFactory(address(_walletFactory)); - // Mint an nft to the owner - IKintoID.SignatureData memory sigdata = - _auxCreateSignature(_kintoIDv1, _owner, _owner, 1, block.timestamp + 1000); - uint8[] memory traits = new uint8[](0); - vm.startPrank(_kycProvider); - _kintoIDv1.mintIndividualKyc(sigdata, traits); - vm.stopPrank(); - vm.startPrank(_owner); - // deploy walletv1 through wallet factory and initializes it - _kintoWalletv1 = _walletFactory.createAccount(_owner, _recoverer, 0); - console.log("wallet address ", address(_kintoWalletv1)); - // deploy the paymaster - _paymaster = new SponsorPaymaster{salt: 0}(_entryPoint); - // deploy _proxy contract and point it to _implementation - _proxys = new UUPSProxy(address(_paymaster), ""); - // wrap in ABI to support easier calls - _paymaster = SponsorPaymaster(address(_proxys)); - // Initialize proxy - _paymaster.initialize(_owner); - vm.stopPrank(); + deployAAScaffolding(_owner, 1, _kycProvider, _recoverer); + _fundPaymasterForContract(address(_engenCredits)); + _fundPaymasterForContract(address(_kintoWalletv1)); } function testExploit() public { @@ -168,12 +118,4 @@ contract SponsorPaymasterExploitTest is MyOpCreator { vm.stopPrank(); } - // funds contract so paymaster can use to pay for gas - function _fundPaymasterForContract(address _contract) private { - vm.startPrank(_owner); - vm.deal(_owner, 1000e18); - // We add the deposit to the counter contract in the paymaster - _paymaster.addDepositFor{value: 100e18}(address(_contract)); - vm.stopPrank(); - } } diff --git a/test/helpers/AATestScaffolding.sol b/test/helpers/AATestScaffolding.sol index 9f4f039b8..2ba9e89bb 100644 --- a/test/helpers/AATestScaffolding.sol +++ b/test/helpers/AATestScaffolding.sol @@ -49,6 +49,9 @@ abstract contract AATestScaffolding is KYCSignature { // Deploy Kinto ID deployKintoID(_owner, _kycProvider); + // Deploy Kinto App + deployKintoApp(_owner); + // vm.startPrank(_owner); EntryPoint entry = new EntryPoint{salt: 0}(); _entryPoint = IKintoEntryPoint(address(entry)); @@ -60,19 +63,16 @@ abstract contract AATestScaffolding is KYCSignature { // Approve wallet's owner KYC approveKYC(_kycProvider, _owner, _ownerPk); - // deploy WalletV1 through wallet factory and initialize it - vm.prank(_owner); - _kintoWalletv1 = _walletFactory.createAccount(_owner, _recoverer, 0); - // Deploy paymaster deployPaymaster(_owner); // Deploy Engen Credits deployEngenCredits(_owner); - // Deploy Kinto App - deployKintoApp(_owner); - + vm.prank(_owner); + // deploy WalletV1 through wallet factory and initialize it + _kintoWalletv1 = _walletFactory.createAccount(_owner, _recoverer, 0); + console.log("kinto wallet app registry in wall", address(_kintoWalletv1.appRegistry())); // Give some eth vm.deal(_owner, 1e20); } @@ -103,7 +103,8 @@ abstract contract AATestScaffolding is KYCSignature { vm.startPrank(_owner); // Deploy wallet implementation - _kintoWalletImpl = new KintoWallet{salt: 0}(_entryPoint, _kintoIDv1); + console.log("APPPP", address(_kintoApp)); + _kintoWalletImpl = new KintoWallet{salt: 0}(_entryPoint, _kintoIDv1, _kintoApp); //Deploy wallet factory implementation _walletFactoryI = new KintoWalletFactory{salt: 0}(_kintoWalletImpl); @@ -134,6 +135,9 @@ abstract contract AATestScaffolding is KYCSignature { // initialize proxy _paymaster.initialize(_owner); + // Set the registry in the paymaster + _paymaster.setAppRegistry(address(_kintoApp)); + vm.stopPrank(); } @@ -170,11 +174,6 @@ abstract contract AATestScaffolding is KYCSignature { // initialize proxy _kintoApp.initialize(); - // Upgrade to a new impl with the registry and wallet factory - _kintoWalletImpl = new KintoWallet{salt: 0}(_entryPoint, _kintoIDv1); - _walletFactory.setAddressesOnImplementation(_kintoWalletImpl, address(_kintoApp), address(_walletFactory)); - _walletFactory.upgradeAllWalletImplementations(_kintoWalletImpl); - vm.stopPrank(); } From 8e638e2f625de202a7d07906f3ca688b6ed1fa4c Mon Sep 17 00:00:00 2001 From: Ramon Recuero Date: Sat, 6 Jan 2024 20:26:59 -0800 Subject: [PATCH 13/43] forge format --- script/upgrade.sol | 11 ++++++++--- src/wallet/KintoWallet.sol | 4 +++- src/wallet/KintoWalletFactory.sol | 3 +-- test/EngenCredits.t.sol | 5 +---- test/KintoWallet.t.sol | 4 +++- test/KintoWalletFactory.t.sol | 4 +++- test/SponsorPaymastExploit.t.sol | 1 - 7 files changed, 19 insertions(+), 13 deletions(-) diff --git a/script/upgrade.sol b/script/upgrade.sol index bc18b13ec..14610c961 100644 --- a/script/upgrade.sol +++ b/script/upgrade.sol @@ -48,7 +48,9 @@ contract KintoWalletFactoryUpgradeScript is ArtifactsReader { } contract KintoWalletVTest is KintoWallet { - constructor(IEntryPoint _entryPoint, IKintoID _kintoIDv1, IKintoApp _kintoApp) KintoWallet(_entryPoint, _kintoIDv1, _kintoApp) {} + constructor(IEntryPoint _entryPoint, IKintoID _kintoIDv1, IKintoApp _kintoApp) + KintoWallet(_entryPoint, _kintoIDv1, _kintoApp) + {} } contract KintoWalletsUpgradeScript is ArtifactsReader { @@ -66,8 +68,11 @@ contract KintoWalletsUpgradeScript is ArtifactsReader { _walletFactory = KintoWalletFactory(payable(_getChainDeployment("KintoWalletFactory"))); // Deploy new wallet implementation - _kintoWalletImpl = - new KintoWalletVTest(IEntryPoint(_getChainDeployment("EntryPoint")), IKintoID(_getChainDeployment("KintoID")), IKintoApp(_getChainDeployment("KintoApp"))); + _kintoWalletImpl = new KintoWalletVTest( + IEntryPoint(_getChainDeployment("EntryPoint")), + IKintoID(_getChainDeployment("KintoID")), + IKintoApp(_getChainDeployment("KintoApp")) + ); // // Upgrade all implementations _walletFactory.upgradeAllWalletImplementations(_kintoWalletImpl); vm.stopBroadcast(); diff --git a/src/wallet/KintoWallet.sol b/src/wallet/KintoWallet.sol index 871eb0fe3..0c6835564 100644 --- a/src/wallet/KintoWallet.sol +++ b/src/wallet/KintoWallet.sol @@ -389,5 +389,7 @@ contract KintoWallet is Initializable, BaseAccount, TokenCallbackHandler, IKinto // Upgradeable version of KintoWallet contract KintoWalletV3 is KintoWallet { - constructor(IEntryPoint _entryPoint, IKintoID _kintoID, IKintoApp _kintoApp) KintoWallet(_entryPoint, _kintoID, _kintoApp) {} + constructor(IEntryPoint _entryPoint, IKintoID _kintoID, IKintoApp _kintoApp) + KintoWallet(_entryPoint, _kintoID, _kintoApp) + {} } diff --git a/src/wallet/KintoWalletFactory.sol b/src/wallet/KintoWalletFactory.sol index 86da64f78..49cdec463 100644 --- a/src/wallet/KintoWalletFactory.sol +++ b/src/wallet/KintoWalletFactory.sol @@ -265,5 +265,4 @@ contract KintoWalletFactory is Initializable, UUPSUpgradeable, OwnableUpgradeabl } catch {} return created; } - -} \ No newline at end of file +} diff --git a/test/EngenCredits.t.sol b/test/EngenCredits.t.sol index ef9c679ad..260ae5205 100644 --- a/test/EngenCredits.t.sol +++ b/test/EngenCredits.t.sol @@ -33,10 +33,7 @@ contract EngenCreditsTest is Create2Helper, UserOp, AATestScaffolding { _fundPaymasterForContract(address(_kintoWalletv1)); vm.startPrank(_owner); _kintoApp.registerApp( - "engen credits", - address(_engenCredits), - new address[](0), - [uint256(0), uint256(0), uint256(0), uint256(0)] + "engen credits", address(_engenCredits), new address[](0), [uint256(0), uint256(0), uint256(0), uint256(0)] ); vm.stopPrank(); } diff --git a/test/KintoWallet.t.sol b/test/KintoWallet.t.sol index 1f60816b5..cb4badf2d 100644 --- a/test/KintoWallet.t.sol +++ b/test/KintoWallet.t.sol @@ -25,7 +25,9 @@ import {Test, stdError} from "forge-std/Test.sol"; import "forge-std/console.sol"; contract KintoWalletv2 is KintoWallet { - constructor(IEntryPoint _entryPoint, IKintoID _kintoID, IKintoApp _kintoApp) KintoWallet(_entryPoint, _kintoID, _kintoApp) {} + constructor(IEntryPoint _entryPoint, IKintoID _kintoID, IKintoApp _kintoApp) + KintoWallet(_entryPoint, _kintoID, _kintoApp) + {} function newFunction() public pure returns (uint256) { return 1; diff --git a/test/KintoWalletFactory.t.sol b/test/KintoWalletFactory.t.sol index 7ba952a71..4a31420f0 100644 --- a/test/KintoWalletFactory.t.sol +++ b/test/KintoWalletFactory.t.sol @@ -24,7 +24,9 @@ import "forge-std/Test.sol"; import "forge-std/console.sol"; contract KintoWalletV999 is KintoWallet { - constructor(IEntryPoint _entryPoint, IKintoID _kintoID, IKintoApp _kintoApp) KintoWallet(_entryPoint, _kintoID, _kintoApp) {} + constructor(IEntryPoint _entryPoint, IKintoID _kintoID, IKintoApp _kintoApp) + KintoWallet(_entryPoint, _kintoID, _kintoApp) + {} function walletFunction() public pure returns (uint256) { return 1; diff --git a/test/SponsorPaymastExploit.t.sol b/test/SponsorPaymastExploit.t.sol index 26b67ad08..2639b7987 100644 --- a/test/SponsorPaymastExploit.t.sol +++ b/test/SponsorPaymastExploit.t.sol @@ -117,5 +117,4 @@ contract SponsorPaymasterExploitTest is Create2Helper, MyOpCreator, AATestScaffo vm.stopPrank(); } - } From ea66e1d50b314122192baff2f4abab882e5e9262 Mon Sep 17 00:00:00 2001 From: Ramon Recuero Date: Sat, 6 Jan 2024 20:32:28 -0800 Subject: [PATCH 14/43] All tests passing --- test/KintoWallet.t.sol | 6 +++--- test/helpers/AATestScaffolding.sol | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/test/KintoWallet.t.sol b/test/KintoWallet.t.sol index cb4badf2d..bba2bc395 100644 --- a/test/KintoWallet.t.sol +++ b/test/KintoWallet.t.sol @@ -150,7 +150,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { vm.recordLogs(); _entryPoint.handleOps(userOps, payable(_owner)); - assertRevertReasonEq("KW: app not whitelisted"); + assertRevertReasonEq("KW: contract not whitelisted"); vm.stopPrank(); } @@ -248,7 +248,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { ); vm.recordLogs(); _entryPoint.handleOps(userOps, payable(_owner)); - assertRevertReasonEq("KW: app not whitelisted"); + assertRevertReasonEq("KW: contract not whitelisted"); assertEq(counter.count(), 0); } @@ -417,7 +417,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { uint256 expectedOpIndex = 0; // Adjust as needed string memory expectedMessage = "AA33 reverted"; bytes memory additionalMessage = - abi.encodePacked("SP: executeBatch must come from same contract or sender wallet"); + abi.encodePacked("SP: executeBatch targets must be sponsored by the contract or be the sender wallet"); bytes memory expectedAdditionalData = abi.encodeWithSelector( bytes4(keccak256("Error(string)")), // Standard error selector additionalMessage diff --git a/test/helpers/AATestScaffolding.sol b/test/helpers/AATestScaffolding.sol index 2ba9e89bb..f5abcd2f0 100644 --- a/test/helpers/AATestScaffolding.sol +++ b/test/helpers/AATestScaffolding.sol @@ -72,7 +72,6 @@ abstract contract AATestScaffolding is KYCSignature { vm.prank(_owner); // deploy WalletV1 through wallet factory and initialize it _kintoWalletv1 = _walletFactory.createAccount(_owner, _recoverer, 0); - console.log("kinto wallet app registry in wall", address(_kintoWalletv1.appRegistry())); // Give some eth vm.deal(_owner, 1e20); } @@ -103,7 +102,6 @@ abstract contract AATestScaffolding is KYCSignature { vm.startPrank(_owner); // Deploy wallet implementation - console.log("APPPP", address(_kintoApp)); _kintoWalletImpl = new KintoWallet{salt: 0}(_entryPoint, _kintoIDv1, _kintoApp); //Deploy wallet factory implementation From 7ff1442d075ca6cf094d8ccfdf4b43b9bf450d72 Mon Sep 17 00:00:00 2001 From: Ramon Recuero Date: Sun, 7 Jan 2024 10:53:00 -0800 Subject: [PATCH 15/43] Tests for KintoApp --- test/KintoApp.t.sol | 100 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 99 insertions(+), 1 deletion(-) diff --git a/test/KintoApp.t.sol b/test/KintoApp.t.sol index 2b9bd4bc9..355619c77 100644 --- a/test/KintoApp.t.sol +++ b/test/KintoApp.t.sol @@ -154,10 +154,108 @@ contract KintoAppTest is Create2Helper, UserOp, AATestScaffolding { vm.stopPrank(); } + /* ============ DSA Test ============ */ + + function testOwnerCanEnableDSA() public { + vm.startPrank(_owner); + address parentContract = address(_engenCredits); + address[] memory childContracts = new address[](1); + childContracts[0] = address(8); + uint256[] memory appLimits = new uint256[](4); + appLimits[0] = _kintoApp.RATE_LIMIT_PERIOD(); + appLimits[1] = _kintoApp.RATE_LIMIT_THRESHOLD(); + appLimits[2] = _kintoApp.GAS_LIMIT_PERIOD(); + appLimits[3] = _kintoApp.GAS_LIMIT_THRESHOLD(); + _kintoApp.registerApp( + "", parentContract, childContracts, [appLimits[0], appLimits[1], appLimits[2], appLimits[3]] + ); + _kintoApp.enableDSA(parentContract); + IKintoApp.Metadata memory metadata = _kintoApp.getAppMetadata(parentContract); + assertEq(metadata.dsaEnabled, true); + vm.stopPrank(); + } + + function test_Revert_When_User_TriesToEnableDSA() public { + vm.startPrank(_user); + address parentContract = address(_engenCredits); + address[] memory childContracts = new address[](1); + childContracts[0] = address(8); + uint256[] memory appLimits = new uint256[](4); + appLimits[0] = _kintoApp.RATE_LIMIT_PERIOD(); + appLimits[1] = _kintoApp.RATE_LIMIT_THRESHOLD(); + appLimits[2] = _kintoApp.GAS_LIMIT_PERIOD(); + appLimits[3] = _kintoApp.GAS_LIMIT_THRESHOLD(); + _kintoApp.registerApp( + "", parentContract, childContracts, [appLimits[0], appLimits[1], appLimits[2], appLimits[3]] + ); + bytes memory err = abi.encodePacked( + "AccessControl: account ", + Strings.toHexString(address(_user)), + " is missing role ", + Strings.toHexString(uint256(_kintoApp.DEVELOPER_ADMIN()), 32) + ); + vm.expectRevert(err); + _kintoApp.enableDSA(parentContract); + } + + /* ============ Sponsored Contracts Test ============ */ + + function testAppCreatorCanSetSponsoredContracts() public { + vm.startPrank(_user); + address parentContract = address(_engenCredits); + address[] memory childContracts = new address[](1); + childContracts[0] = address(8); + uint256[] memory appLimits = new uint256[](4); + appLimits[0] = _kintoApp.RATE_LIMIT_PERIOD(); + appLimits[1] = _kintoApp.RATE_LIMIT_THRESHOLD(); + appLimits[2] = _kintoApp.GAS_LIMIT_PERIOD(); + appLimits[3] = _kintoApp.GAS_LIMIT_THRESHOLD(); + _kintoApp.registerApp( + "", parentContract, childContracts, [appLimits[0], appLimits[1], appLimits[2], appLimits[3]] + ); + address[] memory contracts = new address[](2); + contracts[0] = address(8); + contracts[1] = address(9); + bool[] memory flags = new bool[](2); + flags[0] = false; + flags[1] = true; + _kintoApp.setSponsoredContracts(parentContract, contracts, flags); + assertEq(_kintoApp.isContractSponsoredByApp(parentContract, address(8)), true); // child contracts always sponsored + assertEq(_kintoApp.isContractSponsoredByApp(parentContract, address(9)), true); + assertEq(_kintoApp.isContractSponsoredByApp(parentContract, address(10)), false); + vm.stopPrank(); + } + + function test_Revert_When_NotCreator_TriesToSetSponsoredContracts() public { + vm.startPrank(_user); + address parentContract = address(_engenCredits); + address[] memory childContracts = new address[](1); + childContracts[0] = address(8); + uint256[] memory appLimits = new uint256[](4); + appLimits[0] = _kintoApp.RATE_LIMIT_PERIOD(); + appLimits[1] = _kintoApp.RATE_LIMIT_THRESHOLD(); + appLimits[2] = _kintoApp.GAS_LIMIT_PERIOD(); + appLimits[3] = _kintoApp.GAS_LIMIT_THRESHOLD(); + _kintoApp.registerApp( + "", parentContract, childContracts, [appLimits[0], appLimits[1], appLimits[2], appLimits[3]] + ); + address[] memory contracts = new address[](2); + contracts[0] = address(8); + contracts[1] = address(9); + bool[] memory flags = new bool[](2); + flags[0] = false; + flags[1] = true; + vm.startPrank(_user2); + vm.expectRevert("Only developer can set sponsored contracts"); + _kintoApp.setSponsoredContracts(parentContract, contracts, flags); + vm.stopPrank(); + } + /* ============ Transfer Test ============ */ - function test_RevertWhen_TransfersAreDisabled(address parentContract) public { + function test_RevertWhen_TransfersAreDisabled() public { vm.startPrank(_user); + address parentContract = address(_engenCredits); address[] memory childContracts = new address[](1); childContracts[0] = address(8); uint256[] memory appLimits = new uint256[](4); From fbcc9ccfe8e217128230c57d481ceb2dbb57c002 Mon Sep 17 00:00:00 2001 From: Ramon Recuero Date: Sun, 7 Jan 2024 11:10:35 -0800 Subject: [PATCH 16/43] Renames app registry --- script/deploy.sol | 2 +- script/migrations/02-upgrade_wallet.sol | 2 +- script/upgrade.sol | 4 ++-- .../{KintoApp.sol => KintoAppRegistry.sol} | 20 ++++++++-------- .../{IKintoApp.sol => IKintoAppRegistry.sol} | 2 +- src/interfaces/IKintoWallet.sol | 4 ++-- src/interfaces/ISponsorPaymaster.sol | 4 ++-- src/paymasters/SponsorPaymaster.sol | 6 ++--- src/wallet/KintoWallet.sol | 10 ++++---- ...{KintoApp.t.sol => KintoAppRegistry.t.sol} | 24 +++++++++---------- test/KintoWallet.t.sol | 2 +- test/KintoWalletFactory.t.sol | 2 +- test/helpers/AATestScaffolding.sol | 8 +++---- 13 files changed, 45 insertions(+), 45 deletions(-) rename src/apps/{KintoApp.sol => KintoAppRegistry.sol} (95%) rename src/interfaces/{IKintoApp.sol => IKintoAppRegistry.sol} (98%) rename test/{KintoApp.t.sol => KintoAppRegistry.t.sol} (93%) diff --git a/script/deploy.sol b/script/deploy.sol index f8112b063..bf2f7b3ac 100644 --- a/script/deploy.sol +++ b/script/deploy.sol @@ -105,7 +105,7 @@ contract KintoInitialDeployScript is Create2Helper, ArtifactsReader { console.log("Wallet Implementation already deployed at", address(walletImplementationAddr)); } else { // Deploy Wallet Implementation - _walletImpl = new KintoWallet{salt: 0}(_entryPoint, _kintoIDv1, IKintoApp(address(0))); + _walletImpl = new KintoWallet{salt: 0}(_entryPoint, _kintoIDv1, IKintoAppRegistry(address(0))); console.log("Wallet Implementation deployed at", address(_walletImpl)); } diff --git a/script/migrations/02-upgrade_wallet.sol b/script/migrations/02-upgrade_wallet.sol index c0ee1b0aa..bc7de46b6 100644 --- a/script/migrations/02-upgrade_wallet.sol +++ b/script/migrations/02-upgrade_wallet.sol @@ -14,7 +14,7 @@ import "forge-std/console.sol"; // Upgradeable version of KintoWallet contract KintoWalletV2 is KintoWallet { - constructor(IEntryPoint _entryPoint, IKintoID _kintoID) KintoWallet(_entryPoint, _kintoID, IKintoApp(address(0))) {} + constructor(IEntryPoint _entryPoint, IKintoID _kintoID) KintoWallet(_entryPoint, _kintoID, IKintoAppRegistry(address(0))) {} } contract KintoMigration2DeployScript is Create2Helper, ArtifactsReader { diff --git a/script/upgrade.sol b/script/upgrade.sol index 14610c961..0b979ecb3 100644 --- a/script/upgrade.sol +++ b/script/upgrade.sol @@ -48,7 +48,7 @@ contract KintoWalletFactoryUpgradeScript is ArtifactsReader { } contract KintoWalletVTest is KintoWallet { - constructor(IEntryPoint _entryPoint, IKintoID _kintoIDv1, IKintoApp _kintoApp) + constructor(IEntryPoint _entryPoint, IKintoID _kintoIDv1, IKintoAppRegistry _kintoApp) KintoWallet(_entryPoint, _kintoIDv1, _kintoApp) {} } @@ -71,7 +71,7 @@ contract KintoWalletsUpgradeScript is ArtifactsReader { _kintoWalletImpl = new KintoWalletVTest( IEntryPoint(_getChainDeployment("EntryPoint")), IKintoID(_getChainDeployment("KintoID")), - IKintoApp(_getChainDeployment("KintoApp")) + IKintoAppRegistry(_getChainDeployment("IKintoAppRegistry")) ); // // Upgrade all implementations _walletFactory.upgradeAllWalletImplementations(_kintoWalletImpl); diff --git a/src/apps/KintoApp.sol b/src/apps/KintoAppRegistry.sol similarity index 95% rename from src/apps/KintoApp.sol rename to src/apps/KintoAppRegistry.sol index 3a68fdb07..e0c3e676f 100644 --- a/src/apps/KintoApp.sol +++ b/src/apps/KintoAppRegistry.sol @@ -9,22 +9,22 @@ import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import "../interfaces/IKintoID.sol"; -import "../interfaces/IKintoApp.sol"; +import "../interfaces/IKintoAppRegistry.sol"; import "../interfaces/IKintoWalletFactory.sol"; // import "forge-std/console2.sol"; /** - * @title KintoApp + * @title KintoAppRegistry * @dev A contract that holds all the information of a KintoApp */ -contract KintoApp is +contract KintoAppRegistry is Initializable, ERC721Upgradeable, ERC721EnumerableUpgradeable, AccessControlUpgradeable, UUPSUpgradeable, - IKintoApp + IKintoAppRegistry { /* ============ Constants ============ */ bytes32 public constant override UPGRADER_ROLE = keccak256("UPGRADER_ROLE"); @@ -38,7 +38,7 @@ contract KintoApp is /* ============ State Variables ============ */ uint256 public override appCount; - mapping(address => IKintoApp.Metadata) private _appMetadata; + mapping(address => IKintoAppRegistry.Metadata) private _appMetadata; mapping(address => mapping(address => bool)) private _appSponsoredContracts; // other contracts to be sponsored mapping(address => address) public override childToParentContract; @@ -75,7 +75,7 @@ contract KintoApp is * @dev Gets the token name. * @return string representing the token name */ - function name() public pure override(ERC721Upgradeable, IKintoApp) returns (string memory) { + function name() public pure override(ERC721Upgradeable, IKintoAppRegistry) returns (string memory) { return "Kinto APP"; } @@ -83,7 +83,7 @@ contract KintoApp is * @dev Gets the token symbol. * @return string representing the token symbol */ - function symbol() public pure override(ERC721Upgradeable, IKintoApp) returns (string memory) { + function symbol() public pure override(ERC721Upgradeable, IKintoAppRegistry) returns (string memory) { return "KINTOAPP"; } @@ -167,7 +167,7 @@ contract KintoApp is * @param _contract The address of the app * @return The metadata of the app */ - function getAppMetadata(address _contract) external view override returns (IKintoApp.Metadata memory) { + function getAppMetadata(address _contract) external view override returns (IKintoAppRegistry.Metadata memory) { address finalContract = childToParentContract[_contract] != address(0) ? childToParentContract[_contract] : _contract; return _appMetadata[finalContract]; @@ -181,7 +181,7 @@ contract KintoApp is function getContractLimits(address _contract) external view override returns (uint256[4] memory) { address finalContract = childToParentContract[_contract] != address(0) ? childToParentContract[_contract] : _contract; - IKintoApp.Metadata memory metadata = _appMetadata[finalContract]; + IKintoAppRegistry.Metadata memory metadata = _appMetadata[finalContract]; return [ metadata.rateLimitPeriod != 0 ? metadata.rateLimitPeriod : RATE_LIMIT_PERIOD, metadata.rateLimitNumber != 0 ? metadata.rateLimitNumber : RATE_LIMIT_THRESHOLD, @@ -222,7 +222,7 @@ contract KintoApp is address[] calldata childContracts, uint256[4] calldata appLimits ) internal { - IKintoApp.Metadata memory metadata = IKintoApp.Metadata({ + IKintoAppRegistry.Metadata memory metadata = IKintoAppRegistry.Metadata({ name: _name, developerWallet: msg.sender, dsaEnabled: false, diff --git a/src/interfaces/IKintoApp.sol b/src/interfaces/IKintoAppRegistry.sol similarity index 98% rename from src/interfaces/IKintoApp.sol rename to src/interfaces/IKintoAppRegistry.sol index e559e20ca..f80300d83 100644 --- a/src/interfaces/IKintoApp.sol +++ b/src/interfaces/IKintoAppRegistry.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13; -interface IKintoApp { +interface IKintoAppRegistry { /* ============ Structs ============ */ struct Metadata { diff --git a/src/interfaces/IKintoWallet.sol b/src/interfaces/IKintoWallet.sol index 8d2418d5a..b8c4629a3 100644 --- a/src/interfaces/IKintoWallet.sol +++ b/src/interfaces/IKintoWallet.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.13; import {IEntryPoint} from "@aa/core/BaseAccount.sol"; import {IKintoWalletFactory} from "./IKintoWalletFactory.sol"; import {IKintoID} from "./IKintoID.sol"; -import {IKintoApp} from "./IKintoApp.sol"; +import {IKintoAppRegistry} from "./IKintoAppRegistry.sol"; interface IKintoWallet { /* ============ Structs ============ */ @@ -57,7 +57,7 @@ interface IKintoWallet { function appWhitelist(address app) external view returns (bool); - function appRegistry() external view returns (IKintoApp); + function appRegistry() external view returns (IKintoAppRegistry); function signerPolicy() external view returns (uint8); diff --git a/src/interfaces/ISponsorPaymaster.sol b/src/interfaces/ISponsorPaymaster.sol index ca38b8a0d..9ecb9e003 100644 --- a/src/interfaces/ISponsorPaymaster.sol +++ b/src/interfaces/ISponsorPaymaster.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13; -import {IKintoApp} from "./IKintoApp.sol"; +import {IKintoAppRegistry} from "./IKintoAppRegistry.sol"; interface ISponsorPaymaster { /* ============ Structs ============ */ @@ -35,7 +35,7 @@ interface ISponsorPaymaster { function balances(address account) external view returns (uint256 amount); - function appRegistry() external view returns (IKintoApp); + function appRegistry() external view returns (IKintoAppRegistry); function contractSpent(address account) external view returns (uint256 amount); diff --git a/src/paymasters/SponsorPaymaster.sol b/src/paymasters/SponsorPaymaster.sol index 4b0a2b4c6..796f57852 100644 --- a/src/paymasters/SponsorPaymaster.sol +++ b/src/paymasters/SponsorPaymaster.sol @@ -11,7 +11,7 @@ import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import "@aa/core/BasePaymaster.sol"; import "@aa/core/UserOperationLib.sol"; import "../interfaces/ISponsorPaymaster.sol"; -import "../interfaces/IKintoApp.sol"; +import "../interfaces/IKintoAppRegistry.sol"; import "../interfaces/IKintoWallet.sol"; /** @@ -43,7 +43,7 @@ contract SponsorPaymaster is Initializable, BasePaymaster, UUPSUpgradeable, Reen mapping(address => mapping(address => ISponsorPaymaster.RateLimitData)) public costLimit; mapping(address => ISponsorPaymaster.RateLimitData) public totalRateLimit; - IKintoApp public override appRegistry; + IKintoAppRegistry public override appRegistry; // ========== Constructor & Upgrades ============ @@ -78,7 +78,7 @@ contract SponsorPaymaster is Initializable, BasePaymaster, UUPSUpgradeable, Reen * @param _appRegistry address of the app registry */ function setAppRegistry(address _appRegistry) external override onlyOwner { - appRegistry = IKintoApp(_appRegistry); + appRegistry = IKintoAppRegistry(_appRegistry); } // ========== Deposit Mgmt ============ diff --git a/src/wallet/KintoWallet.sol b/src/wallet/KintoWallet.sol index 0c6835564..443ebc691 100644 --- a/src/wallet/KintoWallet.sol +++ b/src/wallet/KintoWallet.sol @@ -14,7 +14,7 @@ import "../interfaces/IKintoEntryPoint.sol"; import "../libraries/ByteSignature.sol"; import "../interfaces/IKintoWallet.sol"; import "../interfaces/IKintoWalletFactory.sol"; -import "../interfaces/IKintoApp.sol"; +import "../interfaces/IKintoAppRegistry.sol"; /* solhint-disable avoid-low-level-calls */ /* solhint-disable no-inline-assembly */ @@ -48,7 +48,7 @@ contract KintoWallet is Initializable, BaseAccount, TokenCallbackHandler, IKinto mapping(address => bool) public override funderWhitelist; mapping(address => address) public override appSigner; mapping(address => bool) public override appWhitelist; - IKintoApp public immutable override appRegistry; + IKintoAppRegistry public immutable override appRegistry; /* ============ Events ============ */ event KintoWalletInitialized(IEntryPoint indexed entryPoint, address indexed owner); @@ -69,10 +69,10 @@ contract KintoWallet is Initializable, BaseAccount, TokenCallbackHandler, IKinto /* ============ Constructor & Initializers ============ */ - constructor(IEntryPoint __entryPoint, IKintoID _kintoID, IKintoApp _kintoApp) { + constructor(IEntryPoint __entryPoint, IKintoID _kintoID, IKintoAppRegistry _kintoApp) { _entryPoint = __entryPoint; kintoID = _kintoID; - appRegistry = IKintoApp(_kintoApp); + appRegistry = _kintoApp; _disableInitializers(); } @@ -389,7 +389,7 @@ contract KintoWallet is Initializable, BaseAccount, TokenCallbackHandler, IKinto // Upgradeable version of KintoWallet contract KintoWalletV3 is KintoWallet { - constructor(IEntryPoint _entryPoint, IKintoID _kintoID, IKintoApp _kintoApp) + constructor(IEntryPoint _entryPoint, IKintoID _kintoID, IKintoAppRegistry _kintoApp) KintoWallet(_entryPoint, _kintoID, _kintoApp) {} } diff --git a/test/KintoApp.t.sol b/test/KintoAppRegistry.t.sol similarity index 93% rename from test/KintoApp.t.sol rename to test/KintoAppRegistry.t.sol index 355619c77..8ea2a6d87 100644 --- a/test/KintoApp.t.sol +++ b/test/KintoAppRegistry.t.sol @@ -5,7 +5,7 @@ import "../src/wallet/KintoWallet.sol"; import "../src/wallet/KintoWalletFactory.sol"; import "../src/paymasters/SponsorPaymaster.sol"; import "../src/KintoID.sol"; -import "../src/apps/KintoApp.sol"; +import "../src/apps/KintoAppRegistry.sol"; import {UserOp} from "./helpers/UserOp.sol"; import {UUPSProxy} from "./helpers/UUPSProxy.sol"; import {AATestScaffolding} from "./helpers/AATestScaffolding.sol"; @@ -25,22 +25,22 @@ import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import "forge-std/Test.sol"; import "forge-std/console.sol"; -contract KintoAppV2 is KintoApp { +contract KintoAppRegistryV2 is KintoAppRegistry { function newFunction() external pure returns (uint256) { return 1; } - constructor() KintoApp() {} + constructor() KintoAppRegistry() {} } -contract KintoAppTest is Create2Helper, UserOp, AATestScaffolding { +contract KintoAppRegistryTest is Create2Helper, UserOp, AATestScaffolding { using ECDSAUpgradeable for bytes32; using SignatureChecker for address; uint256 _chainID = 1; - KintoAppV2 _implkintoAppV2; - KintoAppV2 _kintoApp2; + KintoAppRegistryV2 _implkintoAppV2; + KintoAppRegistryV2 _kintoApp2; function setUp() public { vm.chainId(_chainID); @@ -65,16 +65,16 @@ contract KintoAppTest is Create2Helper, UserOp, AATestScaffolding { function testOwnerCanUpgradeApp() public { vm.startPrank(_owner); - KintoAppV2 _implementationV2 = new KintoAppV2(); + KintoAppRegistryV2 _implementationV2 = new KintoAppRegistryV2(); _kintoApp.upgradeTo(address(_implementationV2)); // re-wrap the _proxy - _kintoApp2 = KintoAppV2(address(_kintoApp)); + _kintoApp2 = KintoAppRegistryV2(address(_kintoApp)); assertEq(_kintoApp2.newFunction(), 1); vm.stopPrank(); } function test_RevertWhen_OthersCannotUpgradeFactory() public { - KintoAppV2 _implementationV2 = new KintoAppV2(); + KintoAppRegistryV2 _implementationV2 = new KintoAppRegistryV2(); bytes memory err = abi.encodePacked( "AccessControl: account ", Strings.toHexString(address(this)), @@ -101,7 +101,7 @@ contract KintoAppTest is Create2Helper, UserOp, AATestScaffolding { name, parentContract, childContracts, [appLimits[0], appLimits[1], appLimits[2], appLimits[3]] ); assertEq(_kintoApp.appCount(), 1); - IKintoApp.Metadata memory metadata = _kintoApp.getAppMetadata(parentContract); + IKintoAppRegistry.Metadata memory metadata = _kintoApp.getAppMetadata(parentContract); assertEq(metadata.name, name); assertEq(metadata.developerWallet, address(_user)); assertEq(metadata.dsaEnabled, false); @@ -141,7 +141,7 @@ contract KintoAppTest is Create2Helper, UserOp, AATestScaffolding { _kintoApp.updateMetadata( "test2", parentContract, childContracts, [uint256(1), uint256(1), uint256(1), uint256(1)] ); - IKintoApp.Metadata memory metadata = _kintoApp.getAppMetadata(parentContract); + IKintoAppRegistry.Metadata memory metadata = _kintoApp.getAppMetadata(parentContract); assertEq(metadata.name, "test2"); assertEq(metadata.developerWallet, address(_user)); assertEq(metadata.dsaEnabled, false); @@ -170,7 +170,7 @@ contract KintoAppTest is Create2Helper, UserOp, AATestScaffolding { "", parentContract, childContracts, [appLimits[0], appLimits[1], appLimits[2], appLimits[3]] ); _kintoApp.enableDSA(parentContract); - IKintoApp.Metadata memory metadata = _kintoApp.getAppMetadata(parentContract); + IKintoAppRegistry.Metadata memory metadata = _kintoApp.getAppMetadata(parentContract); assertEq(metadata.dsaEnabled, true); vm.stopPrank(); } diff --git a/test/KintoWallet.t.sol b/test/KintoWallet.t.sol index bba2bc395..9540cc18c 100644 --- a/test/KintoWallet.t.sol +++ b/test/KintoWallet.t.sol @@ -25,7 +25,7 @@ import {Test, stdError} from "forge-std/Test.sol"; import "forge-std/console.sol"; contract KintoWalletv2 is KintoWallet { - constructor(IEntryPoint _entryPoint, IKintoID _kintoID, IKintoApp _kintoApp) + constructor(IEntryPoint _entryPoint, IKintoID _kintoID, IKintoAppRegistry _kintoApp) KintoWallet(_entryPoint, _kintoID, _kintoApp) {} diff --git a/test/KintoWalletFactory.t.sol b/test/KintoWalletFactory.t.sol index 4a31420f0..f775fe711 100644 --- a/test/KintoWalletFactory.t.sol +++ b/test/KintoWalletFactory.t.sol @@ -24,7 +24,7 @@ import "forge-std/Test.sol"; import "forge-std/console.sol"; contract KintoWalletV999 is KintoWallet { - constructor(IEntryPoint _entryPoint, IKintoID _kintoID, IKintoApp _kintoApp) + constructor(IEntryPoint _entryPoint, IKintoID _kintoID, IKintoAppRegistry _kintoApp) KintoWallet(_entryPoint, _kintoID, _kintoApp) {} diff --git a/test/helpers/AATestScaffolding.sol b/test/helpers/AATestScaffolding.sol index f5abcd2f0..66043e5a5 100644 --- a/test/helpers/AATestScaffolding.sol +++ b/test/helpers/AATestScaffolding.sol @@ -11,7 +11,7 @@ import {UUPSProxy} from "../helpers/UUPSProxy.sol"; import {KYCSignature} from "../helpers/KYCSignature.sol"; import "../../src/wallet/KintoWallet.sol"; -import "../../src/apps/KintoApp.sol"; +import "../../src/apps/KintoAppRegistry.sol"; import "../../src/tokens/EngenCredits.sol"; import "../../src/wallet/KintoWalletFactory.sol"; import "../../src/paymasters/SponsorPaymaster.sol"; @@ -31,7 +31,7 @@ abstract contract AATestScaffolding is KYCSignature { IKintoEntryPoint _entryPoint; KintoWalletFactory _walletFactoryI; KintoWalletFactory _walletFactory; - KintoApp _kintoApp; + KintoAppRegistry _kintoApp; KintoID _implementation; KintoID _kintoIDv1; SponsorPaymaster _paymaster; @@ -161,13 +161,13 @@ abstract contract AATestScaffolding is KYCSignature { vm.startPrank(_owner); // deploy the Kinto App registry - _kintoApp = new KintoApp{salt: 0}(); + _kintoApp = new KintoAppRegistry{salt: 0}(); // deploy _proxy contract and point it to _implementation _proxyapp = new UUPSProxy{salt: 0}(address(_kintoApp), ""); // wrap in ABI to support easier calls - _kintoApp = KintoApp(address(_proxyapp)); + _kintoApp = KintoAppRegistry(address(_proxyapp)); // initialize proxy _kintoApp.initialize(); From 2c0df80793d12030a15e9deaa830065c7cbbd892 Mon Sep 17 00:00:00 2001 From: Ramon Recuero Date: Sun, 7 Jan 2024 11:14:33 -0800 Subject: [PATCH 17/43] Renames isContractSponsored --- script/migrations/02-upgrade_wallet.sol | 4 +++- src/apps/KintoAppRegistry.sol | 2 +- src/interfaces/IKintoAppRegistry.sol | 2 +- src/paymasters/SponsorPaymaster.sol | 2 +- src/wallet/KintoWallet.sol | 2 +- test/KintoAppRegistry.t.sol | 12 ++++++------ 6 files changed, 13 insertions(+), 11 deletions(-) diff --git a/script/migrations/02-upgrade_wallet.sol b/script/migrations/02-upgrade_wallet.sol index bc7de46b6..bcd2dd42b 100644 --- a/script/migrations/02-upgrade_wallet.sol +++ b/script/migrations/02-upgrade_wallet.sol @@ -14,7 +14,9 @@ import "forge-std/console.sol"; // Upgradeable version of KintoWallet contract KintoWalletV2 is KintoWallet { - constructor(IEntryPoint _entryPoint, IKintoID _kintoID) KintoWallet(_entryPoint, _kintoID, IKintoAppRegistry(address(0))) {} + constructor(IEntryPoint _entryPoint, IKintoID _kintoID) + KintoWallet(_entryPoint, _kintoID, IKintoAppRegistry(address(0))) + {} } contract KintoMigration2DeployScript is Create2Helper, ArtifactsReader { diff --git a/src/apps/KintoAppRegistry.sol b/src/apps/KintoAppRegistry.sol index e0c3e676f..ed6799b98 100644 --- a/src/apps/KintoAppRegistry.sol +++ b/src/apps/KintoAppRegistry.sol @@ -196,7 +196,7 @@ contract KintoAppRegistry is * @param _contract The address of the contract * @return bool true or false */ - function isContractSponsoredByApp(address _app, address _contract) external view override returns (bool) { + function isContractSponsored(address _app, address _contract) external view override returns (bool) { return _contract == _app || childToParentContract[_contract] == _app || _appSponsoredContracts[_app][_contract]; } diff --git a/src/interfaces/IKintoAppRegistry.sol b/src/interfaces/IKintoAppRegistry.sol index f80300d83..216d1584c 100644 --- a/src/interfaces/IKintoAppRegistry.sol +++ b/src/interfaces/IKintoAppRegistry.sol @@ -50,7 +50,7 @@ interface IKintoAppRegistry { function getContractSponsor(address _contract) external view returns (address); - function isContractSponsoredByApp(address _app, address _contract) external view returns (bool); + function isContractSponsored(address _app, address _contract) external view returns (bool); /* ============ Constants and attrs ============ */ diff --git a/src/paymasters/SponsorPaymaster.sol b/src/paymasters/SponsorPaymaster.sol index 796f57852..727353709 100644 --- a/src/paymasters/SponsorPaymaster.sol +++ b/src/paymasters/SponsorPaymaster.sol @@ -282,7 +282,7 @@ contract SponsorPaymaster is Initializable, BasePaymaster, UUPSUpgradeable, Reen // Last contract must be a contract app for (uint256 i = 0; i < targetContracts.length - 1; i++) { if ( - !appRegistry.isContractSponsoredByApp(lastTargetContract, targetContracts[i]) + !appRegistry.isContractSponsored(lastTargetContract, targetContracts[i]) && targetContracts[i] != sender ) { revert("SP: executeBatch targets must be sponsored by the contract or be the sender wallet"); diff --git a/src/wallet/KintoWallet.sol b/src/wallet/KintoWallet.sol index 443ebc691..0887c412d 100644 --- a/src/wallet/KintoWallet.sol +++ b/src/wallet/KintoWallet.sol @@ -369,7 +369,7 @@ contract KintoWallet is Initializable, BaseAccount, TokenCallbackHandler, IKinto for (uint256 i = 0; i < targetContracts.length; i++) { // App signer should only be valid for the app itself and its children // It is important that wallet calls are not allowed through the app signer - if (!appRegistry.isContractSponsoredByApp(lastTargetContract, targetContracts[i])) { + if (!appRegistry.isContractSponsored(lastTargetContract, targetContracts[i])) { return address(0); } } diff --git a/test/KintoAppRegistry.t.sol b/test/KintoAppRegistry.t.sol index 8ea2a6d87..50215933c 100644 --- a/test/KintoAppRegistry.t.sol +++ b/test/KintoAppRegistry.t.sol @@ -109,7 +109,7 @@ contract KintoAppRegistryTest is Create2Helper, UserOp, AATestScaffolding { assertEq(metadata.rateLimitNumber, appLimits[1]); assertEq(metadata.gasLimitPeriod, appLimits[2]); assertEq(metadata.gasLimitCost, appLimits[3]); - assertEq(_kintoApp.isContractSponsoredByApp(parentContract, address(7)), true); + assertEq(_kintoApp.isContractSponsored(parentContract, address(7)), true); assertEq(_kintoApp.getContractSponsor(address(7)), parentContract); uint256[4] memory limits = _kintoApp.getContractLimits(address(7)); assertEq(limits[0], appLimits[0]); @@ -149,8 +149,8 @@ contract KintoAppRegistryTest is Create2Helper, UserOp, AATestScaffolding { assertEq(metadata.rateLimitNumber, 1); assertEq(metadata.gasLimitPeriod, 1); assertEq(metadata.gasLimitCost, 1); - assertEq(_kintoApp.isContractSponsoredByApp(parentContract, address(7)), false); - assertEq(_kintoApp.isContractSponsoredByApp(parentContract, address(8)), true); + assertEq(_kintoApp.isContractSponsored(parentContract, address(7)), false); + assertEq(_kintoApp.isContractSponsored(parentContract, address(8)), true); vm.stopPrank(); } @@ -220,9 +220,9 @@ contract KintoAppRegistryTest is Create2Helper, UserOp, AATestScaffolding { flags[0] = false; flags[1] = true; _kintoApp.setSponsoredContracts(parentContract, contracts, flags); - assertEq(_kintoApp.isContractSponsoredByApp(parentContract, address(8)), true); // child contracts always sponsored - assertEq(_kintoApp.isContractSponsoredByApp(parentContract, address(9)), true); - assertEq(_kintoApp.isContractSponsoredByApp(parentContract, address(10)), false); + assertEq(_kintoApp.isContractSponsored(parentContract, address(8)), true); // child contracts always sponsored + assertEq(_kintoApp.isContractSponsored(parentContract, address(9)), true); + assertEq(_kintoApp.isContractSponsored(parentContract, address(10)), false); vm.stopPrank(); } From 077dc3f2bf45fbc7d6d030693d9e468506d04852 Mon Sep 17 00:00:00 2001 From: Ramon Recuero Date: Sun, 7 Jan 2024 11:21:10 -0800 Subject: [PATCH 18/43] Adds events --- src/apps/KintoAppRegistry.sol | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/apps/KintoAppRegistry.sol b/src/apps/KintoAppRegistry.sol index ed6799b98..24ee17274 100644 --- a/src/apps/KintoAppRegistry.sol +++ b/src/apps/KintoAppRegistry.sol @@ -12,8 +12,6 @@ import "../interfaces/IKintoID.sol"; import "../interfaces/IKintoAppRegistry.sol"; import "../interfaces/IKintoWalletFactory.sol"; -// import "forge-std/console2.sol"; - /** * @title KintoAppRegistry * @dev A contract that holds all the information of a KintoApp @@ -44,6 +42,9 @@ contract KintoAppRegistry is mapping(address => address) public override childToParentContract; /* ============ Events ============ */ + event AppCreated(address indexed _app, address _owner, uint256 _timestamp); + event AppUpdated(address indexed _app, address _owner, uint256 _timestamp); + event AppDSAEnabled(address indexed _app, uint256 _timestamp); /* ============ Constructor & Initializers ============ */ @@ -114,6 +115,7 @@ contract KintoAppRegistry is _updateMetadata(_name, parentContract, childContracts, appLimits); appCount++; _safeMint(msg.sender, appCount); + emit AppCreated(parentContract, msg.sender, block.timestamp); } /** @@ -149,6 +151,7 @@ contract KintoAppRegistry is require(appLimits.length == 4, "Invalid app limits"); require(msg.sender == _appMetadata[parentContract].developerWallet, "Only developer can update metadata"); _updateMetadata(_name, parentContract, childContracts, appLimits); + emit AppUpdated(parentContract, msg.sender, block.timestamp); } /** @@ -158,6 +161,7 @@ contract KintoAppRegistry is function enableDSA(address app) external override onlyRole(DEVELOPER_ADMIN) { require(_appMetadata[app].dsaEnabled == false, "DSA already enabled"); _appMetadata[app].dsaEnabled = true; + emit AppDSAEnabled(app, block.timestamp); } /* ============ App Info Fetching ============ */ From 17cc6a6bdaa9614b348446b64e952b076ae2912c Mon Sep 17 00:00:00 2001 From: Ramon Recuero Date: Sun, 7 Jan 2024 11:24:50 -0800 Subject: [PATCH 19/43] Struct packing --- src/interfaces/IKintoAppRegistry.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/interfaces/IKintoAppRegistry.sol b/src/interfaces/IKintoAppRegistry.sol index 216d1584c..bf8202177 100644 --- a/src/interfaces/IKintoAppRegistry.sol +++ b/src/interfaces/IKintoAppRegistry.sol @@ -5,13 +5,13 @@ interface IKintoAppRegistry { /* ============ Structs ============ */ struct Metadata { - string name; - address developerWallet; // the address that deploys the wallet - bool dsaEnabled; // whether or not this application can request PII from users + address developerWallet; // 160 bits + bool dsaEnabled; uint256 rateLimitPeriod; uint256 rateLimitNumber; // in txs uint256 gasLimitPeriod; uint256 gasLimitCost; // in eth + string name; } /* ============ State Change ============ */ From 5b64e2644f678b61260d5893c9947533a44fa081 Mon Sep 17 00:00:00 2001 From: Ramon Recuero Date: Sun, 7 Jan 2024 13:40:32 -0800 Subject: [PATCH 20/43] Changes AccessControl to ownable in KintoAppRegistry --- src/apps/KintoAppRegistry.sol | 19 +++++++------------ src/interfaces/IKintoAppRegistry.sol | 4 ---- test/KintoAppRegistry.t.sol | 18 +++--------------- 3 files changed, 10 insertions(+), 31 deletions(-) diff --git a/src/apps/KintoAppRegistry.sol b/src/apps/KintoAppRegistry.sol index 24ee17274..697628016 100644 --- a/src/apps/KintoAppRegistry.sol +++ b/src/apps/KintoAppRegistry.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.12; import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; @@ -20,14 +20,11 @@ contract KintoAppRegistry is Initializable, ERC721Upgradeable, ERC721EnumerableUpgradeable, - AccessControlUpgradeable, + OwnableUpgradeable, UUPSUpgradeable, IKintoAppRegistry { /* ============ Constants ============ */ - bytes32 public constant override UPGRADER_ROLE = keccak256("UPGRADER_ROLE"); - bytes32 public constant override DEVELOPER_ADMIN = keccak256("DEVELOPER_ADMIN"); - uint256 public constant override RATE_LIMIT_PERIOD = 1 minutes; uint256 public constant override RATE_LIMIT_THRESHOLD = 10; uint256 public constant override GAS_LIMIT_PERIOD = 30 days; @@ -56,11 +53,9 @@ contract KintoAppRegistry is function initialize() external initializer { __ERC721_init("Kinto APP", "KINTOAPP"); __ERC721Enumerable_init(); - __AccessControl_init(); + __Ownable_init(); __UUPSUpgradeable_init(); - _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); - _grantRole(UPGRADER_ROLE, msg.sender); - _grantRole(DEVELOPER_ADMIN, msg.sender); + _transferOwnership(msg.sender); } /** @@ -68,7 +63,7 @@ contract KintoAppRegistry is * @param newImplementation address of the new implementation */ // This function is called by the proxy contract when the implementation is upgraded - function _authorizeUpgrade(address newImplementation) internal override onlyRole(UPGRADER_ROLE) {} + function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} /* ============ Token name, symbol & URI ============ */ @@ -158,7 +153,7 @@ contract KintoAppRegistry is * @dev Allows the app to request PII data * @param app The name of the app */ - function enableDSA(address app) external override onlyRole(DEVELOPER_ADMIN) { + function enableDSA(address app) external override onlyOwner { require(_appMetadata[app].dsaEnabled == false, "DSA already enabled"); _appMetadata[app].dsaEnabled = true; emit AppDSAEnabled(app, block.timestamp); @@ -268,7 +263,7 @@ contract KintoAppRegistry is function supportsInterface(bytes4 interfaceId) public view - override(ERC721Upgradeable, ERC721EnumerableUpgradeable, AccessControlUpgradeable) + override(ERC721Upgradeable, ERC721EnumerableUpgradeable) returns (bool) { return super.supportsInterface(interfaceId); diff --git a/src/interfaces/IKintoAppRegistry.sol b/src/interfaces/IKintoAppRegistry.sol index bf8202177..c04e96af6 100644 --- a/src/interfaces/IKintoAppRegistry.sol +++ b/src/interfaces/IKintoAppRegistry.sol @@ -54,10 +54,6 @@ interface IKintoAppRegistry { /* ============ Constants and attrs ============ */ - function DEVELOPER_ADMIN() external view returns (bytes32); - - function UPGRADER_ROLE() external view returns (bytes32); - function RATE_LIMIT_PERIOD() external view returns (uint256); function RATE_LIMIT_THRESHOLD() external view returns (uint256); diff --git a/test/KintoAppRegistry.t.sol b/test/KintoAppRegistry.t.sol index 50215933c..f2ef83f91 100644 --- a/test/KintoAppRegistry.t.sol +++ b/test/KintoAppRegistry.t.sol @@ -52,7 +52,7 @@ contract KintoAppRegistryTest is Create2Helper, UserOp, AATestScaffolding { function testUp() public { console.log("address owner", address(_owner)); - assertEq(_kintoApp.hasRole(_kintoApp.UPGRADER_ROLE(), _owner), true); + assertEq(_kintoApp.owner(), _owner); assertEq(_kintoApp.name(), "Kinto APP"); assertEq(_kintoApp.symbol(), "KINTOAPP"); assertEq(_kintoApp.RATE_LIMIT_PERIOD(), 1 minutes); @@ -75,13 +75,7 @@ contract KintoAppRegistryTest is Create2Helper, UserOp, AATestScaffolding { function test_RevertWhen_OthersCannotUpgradeFactory() public { KintoAppRegistryV2 _implementationV2 = new KintoAppRegistryV2(); - bytes memory err = abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(address(this)), - " is missing role ", - Strings.toHexString(uint256(_implementationV2.UPGRADER_ROLE()), 32) - ); - vm.expectRevert(err); + vm.expectRevert("Ownable: caller is not the owner"); _kintoApp.upgradeTo(address(_implementationV2)); } @@ -188,13 +182,7 @@ contract KintoAppRegistryTest is Create2Helper, UserOp, AATestScaffolding { _kintoApp.registerApp( "", parentContract, childContracts, [appLimits[0], appLimits[1], appLimits[2], appLimits[3]] ); - bytes memory err = abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(address(_user)), - " is missing role ", - Strings.toHexString(uint256(_kintoApp.DEVELOPER_ADMIN()), 32) - ); - vm.expectRevert(err); + vm.expectRevert("Ownable: caller is not the owner"); _kintoApp.enableDSA(parentContract); } From d972bee99dbca24a96e8412af1db5ffcc40ee902 Mon Sep 17 00:00:00 2001 From: Ramon Recuero Date: Sun, 7 Jan 2024 13:40:44 -0800 Subject: [PATCH 21/43] Fixes KYC viewer issue --- src/viewers/KYCViewer.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/src/viewers/KYCViewer.sol b/src/viewers/KYCViewer.sol index 99ee7a03d..7e3de5de2 100644 --- a/src/viewers/KYCViewer.sol +++ b/src/viewers/KYCViewer.sol @@ -37,6 +37,7 @@ contract KYCViewer is Initializable, UUPSUpgradeable, OwnableUpgradeable, IKYCVi */ function initialize() external initializer { __Ownable_init(); + __UUPSUpgradeable_init(); _transferOwnership(msg.sender); } From c686a757f6b8198f504c498f9943068365a16915 Mon Sep 17 00:00:00 2001 From: Ramon Recuero Date: Sun, 7 Jan 2024 14:27:18 -0800 Subject: [PATCH 22/43] Adds needed migrations --- .env.sample | 1 + script/migrations/07-deploy_kinto_app.sol | 61 +++++++++++++++++++ script/migrations/08-upgrade_wallet_v3.sol | 60 ++++++++++++++++++ script/migrations/09-upgrade_paymasterv2.sol | 54 ++++++++++++++++ .../10-upgrade_wallet_factoryv2.sol | 56 +++++++++++++++++ script/upgrade.sol | 10 +-- src/paymasters/SponsorPaymaster.sol | 4 ++ src/wallet/KintoWalletFactory.sol | 4 ++ test/KintoWalletFactory.t.sol | 14 ++--- test/SponsorPaymaster.t.sol | 16 ++--- 10 files changed, 260 insertions(+), 20 deletions(-) create mode 100644 script/migrations/07-deploy_kinto_app.sol create mode 100644 script/migrations/08-upgrade_wallet_v3.sol create mode 100644 script/migrations/09-upgrade_paymasterv2.sol create mode 100644 script/migrations/10-upgrade_wallet_factoryv2.sol diff --git a/.env.sample b/.env.sample index bea8b21f5..d58599a75 100644 --- a/.env.sample +++ b/.env.sample @@ -4,3 +4,4 @@ KINTO_RPC_URL="https://kinto-mainnet.calderachain.xyz/http" MAINNET_NETWORK_ID="7887" TESTNET_NETWORK_ID="7887000" KINTO_EXPLORER_URL="https://kinto-mainnet.calderaexplorer.xyz/" +LEDGER_ADMIN="" diff --git a/script/migrations/07-deploy_kinto_app.sol b/script/migrations/07-deploy_kinto_app.sol new file mode 100644 index 000000000..d3844e59c --- /dev/null +++ b/script/migrations/07-deploy_kinto_app.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import "forge-std/Script.sol"; +import "../../src/apps/KintoAppRegistry.sol"; +import "../../src/paymasters/SponsorPaymaster.sol"; +import "../../src/interfaces/IKintoWalletFactory.sol"; +import {Create2Helper} from "../../test/helpers/Create2Helper.sol"; +import {ArtifactsReader} from "../../test/helpers/ArtifactsReader.sol"; +import {UUPSProxy} from "../../test/helpers/UUPSProxy.sol"; +import "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; +import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import "forge-std/console.sol"; + +contract KintoMigration7DeployScript is Create2Helper, ArtifactsReader { + using ECDSAUpgradeable for bytes32; + + KintoAppRegistry _kintoApp; + + function setUp() public {} + + // solhint-disable code-complexity + function run() public { + console.log("RUNNING ON CHAIN WITH ID", vm.toString(block.chainid)); + // Execute this script with the hot wallet, not with ledger + uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); + address admin = vm.envAddress("LEGER_ADMIN"); + if (admin == address(0)) { + console.log("Admin key not set", admin); + return; + } + vm.startBroadcast(deployerPrivateKey); + console.log("Executing with address", msg.sender); + vm.startBroadcast(); + address appAddr = _getChainDeployment("KintoAppRegistry"); + if (appAddr != address(0)) { + console.log("KintoAppRegistry already deployed", appAddr); + return; + } + address walletFactoryAddr = _getChainDeployment("KintoWalletFactory"); + IKintoWalletFactory _walletFactory = IKintoWalletFactory(walletFactoryAddr); + _kintoApp = KintoAppRegistry( + _walletFactory.deployContract( + msg.sender, 0, abi.encodePacked(type(KintoAppRegistry).creationCode), bytes32(0) + ) + ); + // Give ownership to admin + _kintoApp.transferOwnership(admin); + address credits = _getChainDeployment("EngenCredits"); + // Create Engen App + _kintoApp.registerApp("Engen", credits, new address[](0), [uint256(0), uint256(0), uint256(0), uint256(0)]); + // Fund in the paymaster + SponsorPaymaster _paymaster = SponsorPaymaster(payable(_getChainDeployment("SponsorPaymaster"))); + _paymaster.addDepositFor{value: 1e17}(credits); + vm.stopBroadcast(); + // Writes the addresses to a file + console.log("Add these new addresses to the artifacts file"); + console.log(string.concat('"KintoAppRegistry": "', vm.toString(address(_kintoApp)), '"')); + } +} diff --git a/script/migrations/08-upgrade_wallet_v3.sol b/script/migrations/08-upgrade_wallet_v3.sol new file mode 100644 index 000000000..d42c2cff0 --- /dev/null +++ b/script/migrations/08-upgrade_wallet_v3.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import "forge-std/Script.sol"; +import "../../src/wallet/KintoWalletFactory.sol"; +import {KintoWallet} from "../../src/wallet/KintoWallet.sol"; +import {Create2Helper} from "../../test/helpers/Create2Helper.sol"; +import {ArtifactsReader} from "../../test/helpers/ArtifactsReader.sol"; +import {UUPSProxy} from "../../test/helpers/UUPSProxy.sol"; +import "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; +import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import "forge-std/console.sol"; + +contract KintoMigration8DeployScript is Create2Helper, ArtifactsReader { + using ECDSAUpgradeable for bytes32; + + KintoWalletFactory _walletFactory; + KintoWalletV3 _kintoWalletImpl; + UUPSProxy _proxy; + + function setUp() public {} + + // solhint-disable code-complexity + function run() public { + console.log("RUNNING ON CHAIN WITH ID", vm.toString(block.chainid)); + // Execute this script with the ledger admin + console.log("Executing with address", msg.sender); + vm.startBroadcast(); + address walletFactoryAddr = _getChainDeployment("KintoWalletFactory"); + if (walletFactoryAddr == address(0)) { + console.log("Need to execute main deploy script first", walletFactoryAddr); + return; + } + address kintoAppAddr = _getChainDeployment("KintoAppRegistry"); + if (kintoAppAddr == address(0)) { + console.log("Need to deploy kinto app registry first", kintoAppAddr); + return; + } + _walletFactory = KintoWalletFactory(payable(walletFactoryAddr)); + + bytes memory bytecode = abi.encodePacked( + abi.encodePacked(type(KintoWalletV3).creationCode), + abi.encode( + _getChainDeployment("EntryPoint"), + IKintoID(_getChainDeployment("KintoID")), + IKintoAppRegistry(_getChainDeployment("KintoAppRegistry")) + ) // Encoded constructor arguments + ); + + // Deploy new wallet implementation + _kintoWalletImpl = KintoWalletV3(payable(_walletFactory.deployContract(msg.sender, 0, bytecode, bytes32(0)))); + // Upgrade all implementations + _walletFactory.upgradeAllWalletImplementations(_kintoWalletImpl); + vm.stopBroadcast(); + // Writes the addresses to a file + console.log("Add these new addresses to the artifacts file"); + console.log(string.concat('"KintoWalletV3-impl": "', vm.toString(address(_kintoWalletImpl)), '"')); + } +} diff --git a/script/migrations/09-upgrade_paymasterv2.sol b/script/migrations/09-upgrade_paymasterv2.sol new file mode 100644 index 000000000..cce96654e --- /dev/null +++ b/script/migrations/09-upgrade_paymasterv2.sol @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import "forge-std/Script.sol"; +import "../../src/wallet/KintoWalletFactory.sol"; +import "../../src/paymasters/SponsorPaymaster.sol"; +import {KintoWallet} from "../../src/wallet/KintoWallet.sol"; +import {Create2Helper} from "../../test/helpers/Create2Helper.sol"; +import {ArtifactsReader} from "../../test/helpers/ArtifactsReader.sol"; +import {UUPSProxy} from "../../test/helpers/UUPSProxy.sol"; +import "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; +import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import "forge-std/console.sol"; + +contract KintoMigration9DeployScript is Create2Helper, ArtifactsReader { + using ECDSAUpgradeable for bytes32; + + SponsorPaymaster _paymaster; + KintoWalletFactory _walletFactory; + SponsorPaymasterV2 _paymasterImpl; + UUPSProxy _proxy; + + function setUp() public {} + + // solhint-disable code-complexity + function run() public { + console.log("RUNNING ON CHAIN WITH ID", vm.toString(block.chainid)); + // Execute this script with the ledger admin + console.log("Executing with address", msg.sender); + vm.startBroadcast(); + address sponsorAddr = _getChainDeployment("SponsorPaymaster"); + if (sponsorAddr == address(0)) { + console.log("Need to execute main deploy script first", sponsorAddr); + return; + } + _paymaster = SponsorPaymaster(payable(sponsorAddr)); + + _walletFactory = KintoWalletFactory(payable(_getChainDeployment("KintoWalletFactory"))); + bytes memory bytecode = abi.encodePacked( + abi.encodePacked(type(SponsorPaymasterV2).creationCode), + abi.encode(_getChainDeployment("EntryPoint")) // Encoded constructor arguments + ); + + // Deploy new paymaster implementation + _paymasterImpl = SponsorPaymasterV2(payable(_walletFactory.deployContract(msg.sender, 0, bytecode, bytes32(0)))); + // Upgrade + _paymaster.upgradeTo(address(_paymasterImpl)); + vm.stopBroadcast(); + // Writes the addresses to a file + console.log("Add these new addresses to the artifacts file"); + console.log(string.concat('"SponsorPaymasterV2-impl": "', vm.toString(address(_paymasterImpl)), '"')); + } +} diff --git a/script/migrations/10-upgrade_wallet_factoryv2.sol b/script/migrations/10-upgrade_wallet_factoryv2.sol new file mode 100644 index 000000000..e4b074709 --- /dev/null +++ b/script/migrations/10-upgrade_wallet_factoryv2.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import "forge-std/Script.sol"; +import "../../src/wallet/KintoWalletFactory.sol"; +import {KintoWallet} from "../../src/wallet/KintoWallet.sol"; +import {Create2Helper} from "../../test/helpers/Create2Helper.sol"; +import {ArtifactsReader} from "../../test/helpers/ArtifactsReader.sol"; +import {UUPSProxy} from "../../test/helpers/UUPSProxy.sol"; +import "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; +import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import "forge-std/console.sol"; + +contract KintoMigration9DeployScript is Create2Helper, ArtifactsReader { + using ECDSAUpgradeable for bytes32; + + KintoWalletFactory _walletFactory; + KintoWalletFactoryV2 _factoryImpl; + UUPSProxy _proxy; + + function setUp() public {} + + // solhint-disable code-complexity + function run() public { + console.log("RUNNING ON CHAIN WITH ID", vm.toString(block.chainid)); + // Execute this script with the ledger admin + console.log("Executing with address", msg.sender); + vm.startBroadcast(); + address factoryAddr = _getChainDeployment("KintoWalletFactory"); + if (factoryAddr == address(0)) { + console.log("Need to execute main deploy script first", factoryAddr); + return; + } + _walletFactory = KintoWalletFactory(payable(_getChainDeployment("KintoWalletFactory"))); + + address newImpl = _getChainDeployment("KintoWalletV3-impl"); + if (newImpl == address(0)) { + console.log("Need to deploy the new wallet first", newImpl); + return; + } + bytes memory bytecode = abi.encodePacked( + abi.encodePacked(type(KintoWalletFactoryV2).creationCode), + abi.encode(_getChainDeployment("KintoWalletV3-impl")) // Encoded constructor arguments + ); + + // Deploy new paymaster implementation + _factoryImpl = KintoWalletFactoryV2(payable(_walletFactory.deployContract(msg.sender, 0, bytecode, bytes32(0)))); + // Upgrade + _walletFactory.upgradeTo(address(_factoryImpl)); + vm.stopBroadcast(); + // Writes the addresses to a file + console.log("Add these new addresses to the artifacts file"); + console.log(string.concat('"KintoWalletFactoryV2-impl": "', vm.toString(address(_factoryImpl)), '"')); + } +} diff --git a/script/upgrade.sol b/script/upgrade.sol index 0b979ecb3..f411a3815 100644 --- a/script/upgrade.sol +++ b/script/upgrade.sol @@ -22,7 +22,7 @@ import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import "forge-std/console.sol"; -contract KintoWalletFactoryV2 is KintoWalletFactory { +contract KintoWalletFactoryV999 is KintoWalletFactory { constructor(KintoWallet _impl) KintoWalletFactory(_impl) {} } @@ -39,10 +39,10 @@ contract KintoWalletFactoryUpgradeScript is ArtifactsReader { uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); vm.startBroadcast(deployerPrivateKey); _oldKintoWalletFactory = KintoWalletFactory(payable(_getChainDeployment("KintoWalletFactory"))); - KintoWalletFactoryV2 _implementationV2 = - new KintoWalletFactoryV2(KintoWallet(payable(_getChainDeployment("KintoWallet-impl")))); - _oldKintoWalletFactory.upgradeTo(address(_implementationV2)); - console.log("KintoWalletFactory Upgraded to implementation", address(_implementationV2)); + KintoWalletFactoryV999 _implementationV999 = + new KintoWalletFactoryV999(KintoWallet(payable(_getChainDeployment("KintoWallet-impl")))); + _oldKintoWalletFactory.upgradeTo(address(_implementationV999)); + console.log("KintoWalletFactory Upgraded to implementation", address(_implementationV999)); vm.stopBroadcast(); } } diff --git a/src/paymasters/SponsorPaymaster.sol b/src/paymasters/SponsorPaymaster.sol index 727353709..2f160db61 100644 --- a/src/paymasters/SponsorPaymaster.sol +++ b/src/paymasters/SponsorPaymaster.sol @@ -298,3 +298,7 @@ contract SponsorPaymaster is Initializable, BasePaymaster, UUPSUpgradeable, Reen } } } + +contract SponsorPaymasterV2 is SponsorPaymaster { + constructor(IEntryPoint __entryPoint) SponsorPaymaster(__entryPoint) {} +} diff --git a/src/wallet/KintoWalletFactory.sol b/src/wallet/KintoWalletFactory.sol index 49cdec463..2f65f09d9 100644 --- a/src/wallet/KintoWalletFactory.sol +++ b/src/wallet/KintoWalletFactory.sol @@ -266,3 +266,7 @@ contract KintoWalletFactory is Initializable, UUPSUpgradeable, OwnableUpgradeabl return created; } } + +contract KintoWalletFactoryV2 is KintoWalletFactory { + constructor(KintoWallet _implAddressP) KintoWalletFactory(_implAddressP) {} +} diff --git a/test/KintoWalletFactory.t.sol b/test/KintoWalletFactory.t.sol index f775fe711..8237214a9 100644 --- a/test/KintoWalletFactory.t.sol +++ b/test/KintoWalletFactory.t.sol @@ -45,7 +45,7 @@ contract Counter { } } -contract KintoWalletFactoryV2 is KintoWalletFactory { +contract KintoWalletFactoryV999 is KintoWalletFactory { constructor(KintoWallet _impl) KintoWalletFactory(_impl) {} function newFunction() public pure returns (uint256) { @@ -57,7 +57,7 @@ contract KintoWalletFactoryTest is Create2Helper, UserOp, AATestScaffolding { using ECDSAUpgradeable for bytes32; using SignatureChecker for address; - KintoWalletFactoryV2 _walletFactoryv2; + KintoWalletFactoryV999 _walletFactoryv2; KintoWalletV999 _kintoWalletv2; uint256 _chainID = 1; @@ -79,18 +79,18 @@ contract KintoWalletFactoryTest is Create2Helper, UserOp, AATestScaffolding { function testOwnerCanUpgradeFactory() public { vm.startPrank(_owner); - KintoWalletFactoryV2 _implementationV2 = new KintoWalletFactoryV2(_kintoWalletImpl); - _walletFactory.upgradeTo(address(_implementationV2)); + KintoWalletFactoryV999 _implementationV999 = new KintoWalletFactoryV999(_kintoWalletImpl); + _walletFactory.upgradeTo(address(_implementationV999)); // re-wrap the _proxy - _walletFactoryv2 = KintoWalletFactoryV2(address(_walletFactory)); + _walletFactoryv2 = KintoWalletFactoryV999(address(_walletFactory)); assertEq(_walletFactoryv2.newFunction(), 1); vm.stopPrank(); } function test_RevertWhen_OthersCannotUpgradeFactory() public { - KintoWalletFactoryV2 _implementationV2 = new KintoWalletFactoryV2(_kintoWalletImpl); + KintoWalletFactoryV999 _implementationV999 = new KintoWalletFactoryV999(_kintoWalletImpl); vm.expectRevert("Ownable: caller is not the owner"); - _walletFactory.upgradeTo(address(_implementationV2)); + _walletFactory.upgradeTo(address(_implementationV999)); } function testAllWalletsUpgrade() public { diff --git a/test/SponsorPaymaster.t.sol b/test/SponsorPaymaster.t.sol index 8b52a6cdc..124f7e36e 100644 --- a/test/SponsorPaymaster.t.sol +++ b/test/SponsorPaymaster.t.sol @@ -15,7 +15,7 @@ import "@aa/core/EntryPoint.sol"; import "forge-std/Test.sol"; import "forge-std/console.sol"; -contract SponsorPaymasterV2 is SponsorPaymaster { +contract SponsorPaymasterV999 is SponsorPaymaster { constructor(IEntryPoint __entryPoint, address _owner) SponsorPaymaster(__entryPoint) { _disableInitializers(); _transferOwnership(_owner); @@ -32,7 +32,7 @@ contract SponsorPaymasterTest is KYCSignature { EntryPoint _entryPoint; SponsorPaymaster _paymaster; - SponsorPaymasterV2 _paymasterv2; + SponsorPaymasterV999 _paymasterv999; UUPSProxy _proxy; address _owner = address(1); @@ -66,18 +66,18 @@ contract SponsorPaymasterTest is KYCSignature { function testOwnerCanUpgrade() public { vm.startPrank(_owner); - SponsorPaymasterV2 _implementationV2 = new SponsorPaymasterV2(_entryPoint, _owner); - _paymaster.upgradeTo(address(_implementationV2)); + SponsorPaymasterV999 _implementationV999 = new SponsorPaymasterV999(_entryPoint, _owner); + _paymaster.upgradeTo(address(_implementationV999)); // re-wrap the _proxy - _paymasterv2 = SponsorPaymasterV2(address(_proxy)); - assertEq(_paymasterv2.newFunction(), 1); + _paymasterv999 = SponsorPaymasterV999(address(_proxy)); + assertEq(_paymasterv999.newFunction(), 1); vm.stopPrank(); } function testUpgrade_RevertWhen_CallerIsNotOwner() public { - SponsorPaymasterV2 _implementationV2 = new SponsorPaymasterV2(_entryPoint, _owner); + SponsorPaymasterV999 _implementationV999 = new SponsorPaymasterV999(_entryPoint, _owner); vm.expectRevert("SP: not owner"); - _paymaster.upgradeTo(address(_implementationV2)); + _paymaster.upgradeTo(address(_implementationV999)); } // Deposit & Stake From 09cba1492f11af6d64d64347cf08cd90e33e1fad Mon Sep 17 00:00:00 2001 From: Ramon Recuero Date: Mon, 8 Jan 2024 09:11:38 -0800 Subject: [PATCH 23/43] Update src/paymasters/SponsorPaymaster.sol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Federico Martín Alconada Verzini --- src/paymasters/SponsorPaymaster.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/paymasters/SponsorPaymaster.sol b/src/paymasters/SponsorPaymaster.sol index 2f160db61..036f9022b 100644 --- a/src/paymasters/SponsorPaymaster.sol +++ b/src/paymasters/SponsorPaymaster.sol @@ -202,7 +202,7 @@ contract SponsorPaymaster is Initializable, BasePaymaster, UUPSUpgradeable, Reen require(globalData.operationCount < RATE_LIMIT_THRESHOLD_TOTAL, "SP: Kinto Rate limit exceeded"); } - // Get app limits + // Get per-app limits uint256[4] memory appLimits = appRegistry.getContractLimits(targetAccount); // Check app rate limiting From d703a1f29ef83fb2ee055697ffb8797faa2ebb9b Mon Sep 17 00:00:00 2001 From: Ramon Recuero Date: Mon, 8 Jan 2024 09:11:50 -0800 Subject: [PATCH 24/43] Update src/paymasters/SponsorPaymaster.sol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Federico Martín Alconada Verzini --- src/paymasters/SponsorPaymaster.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/paymasters/SponsorPaymaster.sol b/src/paymasters/SponsorPaymaster.sol index 036f9022b..b5795aafd 100644 --- a/src/paymasters/SponsorPaymaster.sol +++ b/src/paymasters/SponsorPaymaster.sol @@ -196,7 +196,7 @@ contract SponsorPaymaster is Initializable, BasePaymaster, UUPSUpgradeable, Reen uint256 ethMaxCost = (maxCost + COST_OF_POST * gasPriceUserOp); require(ethMaxCost <= MAX_COST_OF_USEROP, "SP: gas too high for user op"); - // Check Kinto rate limiting + // Check global rate limit (across all apps) ISponsorPaymaster.RateLimitData memory globalData = totalRateLimit[userOp.sender]; if (block.timestamp < globalData.lastOperationTime + RATE_LIMIT_PERIOD) { require(globalData.operationCount < RATE_LIMIT_THRESHOLD_TOTAL, "SP: Kinto Rate limit exceeded"); From ec08496b6e9ac8771c3397d77233406766c54a82 Mon Sep 17 00:00:00 2001 From: Ramon Recuero Date: Mon, 8 Jan 2024 09:18:58 -0800 Subject: [PATCH 25/43] Changes compiler version --- src/apps/KintoAppRegistry.sol | 2 +- src/interfaces/IKintoAppRegistry.sol | 2 +- test/KintoAppRegistry.t.sol | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/apps/KintoAppRegistry.sol b/src/apps/KintoAppRegistry.sol index 697628016..ae749eac8 100644 --- a/src/apps/KintoAppRegistry.sol +++ b/src/apps/KintoAppRegistry.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.23; import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol"; diff --git a/src/interfaces/IKintoAppRegistry.sol b/src/interfaces/IKintoAppRegistry.sol index c04e96af6..3406f4f57 100644 --- a/src/interfaces/IKintoAppRegistry.sol +++ b/src/interfaces/IKintoAppRegistry.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; +pragma solidity ^0.8.23; interface IKintoAppRegistry { /* ============ Structs ============ */ diff --git a/test/KintoAppRegistry.t.sol b/test/KintoAppRegistry.t.sol index f2ef83f91..904457b1c 100644 --- a/test/KintoAppRegistry.t.sol +++ b/test/KintoAppRegistry.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; +pragma solidity ^0.8.23; import "../src/wallet/KintoWallet.sol"; import "../src/wallet/KintoWalletFactory.sol"; From f5fc112a15db10bb41114f8f7e6e36361118c693 Mon Sep 17 00:00:00 2001 From: Ramon Recuero Date: Mon, 8 Jan 2024 09:24:24 -0800 Subject: [PATCH 26/43] Update src/apps/KintoAppRegistry.sol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Federico Martín Alconada Verzini --- src/apps/KintoAppRegistry.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apps/KintoAppRegistry.sol b/src/apps/KintoAppRegistry.sol index ae749eac8..363c51765 100644 --- a/src/apps/KintoAppRegistry.sol +++ b/src/apps/KintoAppRegistry.sol @@ -200,7 +200,7 @@ contract KintoAppRegistry is } /** - * @dev Returns the contract that sponsors a contract + * @dev Returns the sponsoring contract * @param _contract The address of the contract * @return The address of the contract that sponsors the contract */ From f7072a284af47886d51e8850a1fcd0afba447731 Mon Sep 17 00:00:00 2001 From: Ramon Recuero Date: Mon, 8 Jan 2024 09:37:37 -0800 Subject: [PATCH 27/43] Update test/KintoAppRegistry.t.sol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Federico Martín Alconada Verzini --- test/KintoAppRegistry.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/KintoAppRegistry.t.sol b/test/KintoAppRegistry.t.sol index 904457b1c..7b03c9cf9 100644 --- a/test/KintoAppRegistry.t.sol +++ b/test/KintoAppRegistry.t.sol @@ -73,7 +73,7 @@ contract KintoAppRegistryTest is Create2Helper, UserOp, AATestScaffolding { vm.stopPrank(); } - function test_RevertWhen_OthersCannotUpgradeFactory() public { + function test_RevertWhen_OthersCannotUpgradeAppRegistry() public { KintoAppRegistryV2 _implementationV2 = new KintoAppRegistryV2(); vm.expectRevert("Ownable: caller is not the owner"); _kintoApp.upgradeTo(address(_implementationV2)); From a2e1334f53f94bf9229ef51be3cd3505fee6ff75 Mon Sep 17 00:00:00 2001 From: Ramon Recuero Date: Mon, 8 Jan 2024 09:38:41 -0800 Subject: [PATCH 28/43] Addresses comments --- script/migrations/07-deploy_kinto_app.sol | 6 +-- src/apps/KintoAppRegistry.sol | 43 ++++++++++--------- src/interfaces/IKintoAppRegistry.sol | 8 ++-- src/paymasters/SponsorPaymaster.sol | 4 +- src/wallet/KintoWallet.sol | 8 ++-- test/KintoAppRegistry.t.sol | 50 +++++++++++------------ 6 files changed, 58 insertions(+), 61 deletions(-) diff --git a/script/migrations/07-deploy_kinto_app.sol b/script/migrations/07-deploy_kinto_app.sol index d3844e59c..2a1618cf3 100644 --- a/script/migrations/07-deploy_kinto_app.sol +++ b/script/migrations/07-deploy_kinto_app.sol @@ -25,14 +25,13 @@ contract KintoMigration7DeployScript is Create2Helper, ArtifactsReader { console.log("RUNNING ON CHAIN WITH ID", vm.toString(block.chainid)); // Execute this script with the hot wallet, not with ledger uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - address admin = vm.envAddress("LEGER_ADMIN"); + address admin = vm.envAddress("LEDGER_ADMIN"); if (admin == address(0)) { console.log("Admin key not set", admin); return; } - vm.startBroadcast(deployerPrivateKey); + vm.broadcast(deployerPrivateKey); console.log("Executing with address", msg.sender); - vm.startBroadcast(); address appAddr = _getChainDeployment("KintoAppRegistry"); if (appAddr != address(0)) { console.log("KintoAppRegistry already deployed", appAddr); @@ -53,7 +52,6 @@ contract KintoMigration7DeployScript is Create2Helper, ArtifactsReader { // Fund in the paymaster SponsorPaymaster _paymaster = SponsorPaymaster(payable(_getChainDeployment("SponsorPaymaster"))); _paymaster.addDepositFor{value: 1e17}(credits); - vm.stopBroadcast(); // Writes the addresses to a file console.log("Add these new addresses to the artifacts file"); console.log(string.concat('"KintoAppRegistry": "', vm.toString(address(_kintoApp)), '"')); diff --git a/src/apps/KintoAppRegistry.sol b/src/apps/KintoAppRegistry.sol index 363c51765..4322b71de 100644 --- a/src/apps/KintoAppRegistry.sol +++ b/src/apps/KintoAppRegistry.sol @@ -34,7 +34,7 @@ contract KintoAppRegistry is uint256 public override appCount; mapping(address => IKintoAppRegistry.Metadata) private _appMetadata; - mapping(address => mapping(address => bool)) private _appSponsoredContracts; // other contracts to be sponsored + mapping(address => mapping(address => bool)) private _sponsoredContracts; // other contracts to be sponsored mapping(address => address) public override childToParentContract; @@ -97,17 +97,17 @@ contract KintoAppRegistry is * @dev Register a new app and mints the NFT to the creator * @param _name The name of the app * @param parentContract The address of the parent contract - * @param childContracts The addresses of the child contracts + * @param appContracts The addresses of the child contracts * @param appLimits The limits of the app */ function registerApp( string calldata _name, address parentContract, - address[] calldata childContracts, + address[] calldata appContracts, uint256[4] calldata appLimits ) external override { require(appLimits.length == 4, "Invalid app limits"); - _updateMetadata(_name, parentContract, childContracts, appLimits); + _updateMetadata(_name, parentContract, appContracts, appLimits); appCount++; _safeMint(msg.sender, appCount); emit AppCreated(parentContract, msg.sender, block.timestamp); @@ -124,9 +124,9 @@ contract KintoAppRegistry is override { require(_contracts.length == _flags.length, "Invalid input"); - require(msg.sender == _appMetadata[_app].developerWallet, "Only developer can set sponsored contracts"); + require(msg.sender == _appMetadata[_app].admin, "Only developer can set sponsored contracts"); for (uint256 i = 0; i < _contracts.length; i++) { - _appSponsoredContracts[_app][_contracts[i]] = _flags[i]; + _sponsoredContracts[_app][_contracts[i]] = _flags[i]; } } @@ -134,18 +134,17 @@ contract KintoAppRegistry is * @dev Allows the developer to update the metadata of the app * @param _name The name of the app * @param parentContract The address of the parent contract - * @param childContracts The addresses of the child contracts + * @param appContracts The addresses of the child contracts * @param appLimits The limits of the app */ function updateMetadata( string calldata _name, address parentContract, - address[] calldata childContracts, + address[] calldata appContracts, uint256[4] calldata appLimits ) external override { - require(appLimits.length == 4, "Invalid app limits"); - require(msg.sender == _appMetadata[parentContract].developerWallet, "Only developer can update metadata"); - _updateMetadata(_name, parentContract, childContracts, appLimits); + require(msg.sender == _appMetadata[parentContract].admin, "Only developer can update metadata"); + _updateMetadata(_name, parentContract, appContracts, appLimits); emit AppUpdated(parentContract, msg.sender, block.timestamp); } @@ -167,9 +166,9 @@ contract KintoAppRegistry is * @return The metadata of the app */ function getAppMetadata(address _contract) external view override returns (IKintoAppRegistry.Metadata memory) { - address finalContract = + address mainContract = childToParentContract[_contract] != address(0) ? childToParentContract[_contract] : _contract; - return _appMetadata[finalContract]; + return _appMetadata[mainContract]; } /** @@ -178,9 +177,9 @@ contract KintoAppRegistry is * @return The limits of the app */ function getContractLimits(address _contract) external view override returns (uint256[4] memory) { - address finalContract = + address mainContract = childToParentContract[_contract] != address(0) ? childToParentContract[_contract] : _contract; - IKintoAppRegistry.Metadata memory metadata = _appMetadata[finalContract]; + IKintoAppRegistry.Metadata memory metadata = _appMetadata[mainContract]; return [ metadata.rateLimitPeriod != 0 ? metadata.rateLimitPeriod : RATE_LIMIT_PERIOD, metadata.rateLimitNumber != 0 ? metadata.rateLimitNumber : RATE_LIMIT_THRESHOLD, @@ -196,7 +195,7 @@ contract KintoAppRegistry is * @return bool true or false */ function isContractSponsored(address _app, address _contract) external view override returns (bool) { - return _contract == _app || childToParentContract[_contract] == _app || _appSponsoredContracts[_app][_contract]; + return _contract == _app || childToParentContract[_contract] == _app || _sponsoredContracts[_app][_contract]; } /** @@ -204,8 +203,8 @@ contract KintoAppRegistry is * @param _contract The address of the contract * @return The address of the contract that sponsors the contract */ - function getContractSponsor(address _contract) external view override returns (address) { - if (_appMetadata[_contract].developerWallet != address(0)) { + function getSponsor(address _contract) external view override returns (address) { + if (_appMetadata[_contract].admin != address(0)) { return _contract; } if (childToParentContract[_contract] != address(0)) { @@ -218,12 +217,12 @@ contract KintoAppRegistry is function _updateMetadata( string calldata _name, address parentContract, - address[] calldata childContracts, + address[] calldata appContracts, uint256[4] calldata appLimits ) internal { IKintoAppRegistry.Metadata memory metadata = IKintoAppRegistry.Metadata({ name: _name, - developerWallet: msg.sender, + admin: msg.sender, dsaEnabled: false, rateLimitPeriod: appLimits[0], rateLimitNumber: appLimits[1], @@ -231,8 +230,8 @@ contract KintoAppRegistry is gasLimitCost: appLimits[3] }); _appMetadata[parentContract] = metadata; - for (uint256 i = 0; i < childContracts.length; i++) { - childToParentContract[childContracts[i]] = parentContract; + for (uint256 i = 0; i < appContracts.length; i++) { + childToParentContract[appContracts[i]] = parentContract; } } diff --git a/src/interfaces/IKintoAppRegistry.sol b/src/interfaces/IKintoAppRegistry.sol index 3406f4f57..86bec51b2 100644 --- a/src/interfaces/IKintoAppRegistry.sol +++ b/src/interfaces/IKintoAppRegistry.sol @@ -5,7 +5,7 @@ interface IKintoAppRegistry { /* ============ Structs ============ */ struct Metadata { - address developerWallet; // 160 bits + address admin; // 160 bits bool dsaEnabled; uint256 rateLimitPeriod; uint256 rateLimitNumber; // in txs @@ -19,7 +19,7 @@ interface IKintoAppRegistry { function registerApp( string calldata _name, address parentContract, - address[] calldata childContracts, + address[] calldata appContracts, uint256[4] calldata appLimits ) external; @@ -30,7 +30,7 @@ interface IKintoAppRegistry { function updateMetadata( string calldata _name, address parentContract, - address[] calldata childContracts, + address[] calldata appContracts, uint256[4] calldata appLimits ) external; @@ -48,7 +48,7 @@ interface IKintoAppRegistry { function getAppMetadata(address _contract) external view returns (Metadata memory); - function getContractSponsor(address _contract) external view returns (address); + function getSponsor(address _contract) external view returns (address); function isContractSponsored(address _app, address _contract) external view returns (bool); diff --git a/src/paymasters/SponsorPaymaster.sol b/src/paymasters/SponsorPaymaster.sol index 62ed75b03..1ac84654b 100644 --- a/src/paymasters/SponsorPaymaster.sol +++ b/src/paymasters/SponsorPaymaster.sol @@ -278,7 +278,7 @@ contract SponsorPaymaster is Initializable, BasePaymaster, UUPSUpgradeable, Reen if (selector == IKintoWallet.executeBatch.selector) { // Decode callData for executeBatch (address[] memory targetContracts,,) = abi.decode(callData[4:], (address[], uint256[], bytes[])); - lastTargetContract = appRegistry.getContractSponsor(targetContracts[targetContracts.length - 1]); + lastTargetContract = appRegistry.getSponsor(targetContracts[targetContracts.length - 1]); // Last contract must be a contract app for (uint256 i = 0; i < targetContracts.length - 1; i++) { if ( @@ -291,7 +291,7 @@ contract SponsorPaymaster is Initializable, BasePaymaster, UUPSUpgradeable, Reen } else if (selector == IKintoWallet.execute.selector) { // Decode callData for execute (address targetContract,,) = abi.decode(callData[4:], (address, uint256, bytes)); - lastTargetContract = appRegistry.getContractSponsor(targetContract); + lastTargetContract = appRegistry.getSponsor(targetContract); } else { // Handle unknown function or error revert("SP: Unknown function selector"); diff --git a/src/wallet/KintoWallet.sol b/src/wallet/KintoWallet.sol index f7d890a07..0dc5e745e 100644 --- a/src/wallet/KintoWallet.sol +++ b/src/wallet/KintoWallet.sol @@ -336,7 +336,7 @@ contract KintoWallet is Initializable, BaseAccount, TokenCallbackHandler, IKinto function _checkAppWhitelist(address _contract) internal view { require( - appWhitelist[appRegistry.getContractSponsor(_contract)] || _contract == address(this), + appWhitelist[appRegistry.getSponsor(_contract)] || _contract == address(this), "KW: contract not whitelisted" ); } @@ -365,7 +365,7 @@ contract KintoWallet is Initializable, BaseAccount, TokenCallbackHandler, IKinto if (selector == IKintoWallet.executeBatch.selector) { // Decode callData for executeBatch (address[] memory targetContracts,,) = abi.decode(callData[4:], (address[], uint256[], bytes[])); - address lastTargetContract = appRegistry.getContractSponsor(targetContracts[targetContracts.length - 1]); + address lastTargetContract = appRegistry.getSponsor(targetContracts[targetContracts.length - 1]); for (uint256 i = 0; i < targetContracts.length; i++) { // App signer should only be valid for the app itself and its children // It is important that wallet calls are not allowed through the app signer @@ -389,7 +389,7 @@ contract KintoWallet is Initializable, BaseAccount, TokenCallbackHandler, IKinto // Upgradeable version of KintoWallet contract KintoWalletV3 is KintoWallet { - constructor(IEntryPoint _entryPoint, IKintoID _kintoID, IKintoAppRegistry _kintoApp) - KintoWallet(_entryPoint, _kintoID, _kintoApp) + constructor(IEntryPoint _entryPoint, IKintoID _kintoID, IKintoAppRegistry _appRegistry) + KintoWallet(_entryPoint, _kintoID, _appRegistry) {} } diff --git a/test/KintoAppRegistry.t.sol b/test/KintoAppRegistry.t.sol index 7b03c9cf9..8a563949b 100644 --- a/test/KintoAppRegistry.t.sol +++ b/test/KintoAppRegistry.t.sol @@ -84,27 +84,27 @@ contract KintoAppRegistryTest is Create2Helper, UserOp, AATestScaffolding { function testRegisterApp(string memory name, address parentContract) public { vm.startPrank(_user); assertEq(_kintoApp.appCount(), 0); - address[] memory childContracts = new address[](1); - childContracts[0] = address(7); + address[] memory appContracts = new address[](1); + appContracts[0] = address(7); uint256[] memory appLimits = new uint256[](4); appLimits[0] = _kintoApp.RATE_LIMIT_PERIOD(); appLimits[1] = _kintoApp.RATE_LIMIT_THRESHOLD(); appLimits[2] = _kintoApp.GAS_LIMIT_PERIOD(); appLimits[3] = _kintoApp.GAS_LIMIT_THRESHOLD(); _kintoApp.registerApp( - name, parentContract, childContracts, [appLimits[0], appLimits[1], appLimits[2], appLimits[3]] + name, parentContract, appContracts, [appLimits[0], appLimits[1], appLimits[2], appLimits[3]] ); assertEq(_kintoApp.appCount(), 1); IKintoAppRegistry.Metadata memory metadata = _kintoApp.getAppMetadata(parentContract); assertEq(metadata.name, name); - assertEq(metadata.developerWallet, address(_user)); + assertEq(metadata.admin, address(_user)); assertEq(metadata.dsaEnabled, false); assertEq(metadata.rateLimitPeriod, appLimits[0]); assertEq(metadata.rateLimitNumber, appLimits[1]); assertEq(metadata.gasLimitPeriod, appLimits[2]); assertEq(metadata.gasLimitCost, appLimits[3]); assertEq(_kintoApp.isContractSponsored(parentContract, address(7)), true); - assertEq(_kintoApp.getContractSponsor(address(7)), parentContract); + assertEq(_kintoApp.getSponsor(address(7)), parentContract); uint256[4] memory limits = _kintoApp.getContractLimits(address(7)); assertEq(limits[0], appLimits[0]); assertEq(limits[1], appLimits[1]); @@ -122,22 +122,22 @@ contract KintoAppRegistryTest is Create2Helper, UserOp, AATestScaffolding { function testRegisterAppAndUpdate(string memory name, address parentContract) public { vm.startPrank(_user); - address[] memory childContracts = new address[](1); - childContracts[0] = address(8); + address[] memory appContracts = new address[](1); + appContracts[0] = address(8); uint256[] memory appLimits = new uint256[](4); appLimits[0] = _kintoApp.RATE_LIMIT_PERIOD(); appLimits[1] = _kintoApp.RATE_LIMIT_THRESHOLD(); appLimits[2] = _kintoApp.GAS_LIMIT_PERIOD(); appLimits[3] = _kintoApp.GAS_LIMIT_THRESHOLD(); _kintoApp.registerApp( - name, parentContract, childContracts, [appLimits[0], appLimits[1], appLimits[2], appLimits[3]] + name, parentContract, appContracts, [appLimits[0], appLimits[1], appLimits[2], appLimits[3]] ); _kintoApp.updateMetadata( - "test2", parentContract, childContracts, [uint256(1), uint256(1), uint256(1), uint256(1)] + "test2", parentContract, appContracts, [uint256(1), uint256(1), uint256(1), uint256(1)] ); IKintoAppRegistry.Metadata memory metadata = _kintoApp.getAppMetadata(parentContract); assertEq(metadata.name, "test2"); - assertEq(metadata.developerWallet, address(_user)); + assertEq(metadata.admin, address(_user)); assertEq(metadata.dsaEnabled, false); assertEq(metadata.rateLimitPeriod, 1); assertEq(metadata.rateLimitNumber, 1); @@ -153,15 +153,15 @@ contract KintoAppRegistryTest is Create2Helper, UserOp, AATestScaffolding { function testOwnerCanEnableDSA() public { vm.startPrank(_owner); address parentContract = address(_engenCredits); - address[] memory childContracts = new address[](1); - childContracts[0] = address(8); + address[] memory appContracts = new address[](1); + appContracts[0] = address(8); uint256[] memory appLimits = new uint256[](4); appLimits[0] = _kintoApp.RATE_LIMIT_PERIOD(); appLimits[1] = _kintoApp.RATE_LIMIT_THRESHOLD(); appLimits[2] = _kintoApp.GAS_LIMIT_PERIOD(); appLimits[3] = _kintoApp.GAS_LIMIT_THRESHOLD(); _kintoApp.registerApp( - "", parentContract, childContracts, [appLimits[0], appLimits[1], appLimits[2], appLimits[3]] + "", parentContract, appContracts, [appLimits[0], appLimits[1], appLimits[2], appLimits[3]] ); _kintoApp.enableDSA(parentContract); IKintoAppRegistry.Metadata memory metadata = _kintoApp.getAppMetadata(parentContract); @@ -172,15 +172,15 @@ contract KintoAppRegistryTest is Create2Helper, UserOp, AATestScaffolding { function test_Revert_When_User_TriesToEnableDSA() public { vm.startPrank(_user); address parentContract = address(_engenCredits); - address[] memory childContracts = new address[](1); - childContracts[0] = address(8); + address[] memory appContracts = new address[](1); + appContracts[0] = address(8); uint256[] memory appLimits = new uint256[](4); appLimits[0] = _kintoApp.RATE_LIMIT_PERIOD(); appLimits[1] = _kintoApp.RATE_LIMIT_THRESHOLD(); appLimits[2] = _kintoApp.GAS_LIMIT_PERIOD(); appLimits[3] = _kintoApp.GAS_LIMIT_THRESHOLD(); _kintoApp.registerApp( - "", parentContract, childContracts, [appLimits[0], appLimits[1], appLimits[2], appLimits[3]] + "", parentContract, appContracts, [appLimits[0], appLimits[1], appLimits[2], appLimits[3]] ); vm.expectRevert("Ownable: caller is not the owner"); _kintoApp.enableDSA(parentContract); @@ -191,15 +191,15 @@ contract KintoAppRegistryTest is Create2Helper, UserOp, AATestScaffolding { function testAppCreatorCanSetSponsoredContracts() public { vm.startPrank(_user); address parentContract = address(_engenCredits); - address[] memory childContracts = new address[](1); - childContracts[0] = address(8); + address[] memory appContracts = new address[](1); + appContracts[0] = address(8); uint256[] memory appLimits = new uint256[](4); appLimits[0] = _kintoApp.RATE_LIMIT_PERIOD(); appLimits[1] = _kintoApp.RATE_LIMIT_THRESHOLD(); appLimits[2] = _kintoApp.GAS_LIMIT_PERIOD(); appLimits[3] = _kintoApp.GAS_LIMIT_THRESHOLD(); _kintoApp.registerApp( - "", parentContract, childContracts, [appLimits[0], appLimits[1], appLimits[2], appLimits[3]] + "", parentContract, appContracts, [appLimits[0], appLimits[1], appLimits[2], appLimits[3]] ); address[] memory contracts = new address[](2); contracts[0] = address(8); @@ -217,15 +217,15 @@ contract KintoAppRegistryTest is Create2Helper, UserOp, AATestScaffolding { function test_Revert_When_NotCreator_TriesToSetSponsoredContracts() public { vm.startPrank(_user); address parentContract = address(_engenCredits); - address[] memory childContracts = new address[](1); - childContracts[0] = address(8); + address[] memory appContracts = new address[](1); + appContracts[0] = address(8); uint256[] memory appLimits = new uint256[](4); appLimits[0] = _kintoApp.RATE_LIMIT_PERIOD(); appLimits[1] = _kintoApp.RATE_LIMIT_THRESHOLD(); appLimits[2] = _kintoApp.GAS_LIMIT_PERIOD(); appLimits[3] = _kintoApp.GAS_LIMIT_THRESHOLD(); _kintoApp.registerApp( - "", parentContract, childContracts, [appLimits[0], appLimits[1], appLimits[2], appLimits[3]] + "", parentContract, appContracts, [appLimits[0], appLimits[1], appLimits[2], appLimits[3]] ); address[] memory contracts = new address[](2); contracts[0] = address(8); @@ -244,15 +244,15 @@ contract KintoAppRegistryTest is Create2Helper, UserOp, AATestScaffolding { function test_RevertWhen_TransfersAreDisabled() public { vm.startPrank(_user); address parentContract = address(_engenCredits); - address[] memory childContracts = new address[](1); - childContracts[0] = address(8); + address[] memory appContracts = new address[](1); + appContracts[0] = address(8); uint256[] memory appLimits = new uint256[](4); appLimits[0] = _kintoApp.RATE_LIMIT_PERIOD(); appLimits[1] = _kintoApp.RATE_LIMIT_THRESHOLD(); appLimits[2] = _kintoApp.GAS_LIMIT_PERIOD(); appLimits[3] = _kintoApp.GAS_LIMIT_THRESHOLD(); _kintoApp.registerApp( - "", parentContract, childContracts, [appLimits[0], appLimits[1], appLimits[2], appLimits[3]] + "", parentContract, appContracts, [appLimits[0], appLimits[1], appLimits[2], appLimits[3]] ); uint256 tokenIdx = _kintoApp.tokenOfOwnerByIndex(_user, 0); vm.expectRevert("Only mint transfers are allowed"); From e3c2b8e92849648ec4a9e2a1f6c141515938e7b6 Mon Sep 17 00:00:00 2001 From: Ramon Recuero Date: Mon, 8 Jan 2024 09:40:54 -0800 Subject: [PATCH 29/43] more comments --- src/paymasters/SponsorPaymaster.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/paymasters/SponsorPaymaster.sol b/src/paymasters/SponsorPaymaster.sol index 1ac84654b..3b53acb0a 100644 --- a/src/paymasters/SponsorPaymaster.sol +++ b/src/paymasters/SponsorPaymaster.sol @@ -41,7 +41,7 @@ contract SponsorPaymaster is Initializable, BasePaymaster, UUPSUpgradeable, Reen // A mapping for rate limiting data: account => contract => RateLimitData mapping(address => mapping(address => ISponsorPaymaster.RateLimitData)) public rateLimit; mapping(address => mapping(address => ISponsorPaymaster.RateLimitData)) public costLimit; - mapping(address => ISponsorPaymaster.RateLimitData) public totalRateLimit; + mapping(address => ISponsorPaymaster.RateLimitData) public globalRateLimit; IKintoAppRegistry public override appRegistry; @@ -197,7 +197,7 @@ contract SponsorPaymaster is Initializable, BasePaymaster, UUPSUpgradeable, Reen require(ethMaxCost <= MAX_COST_OF_USEROP, "SP: gas too high for user op"); // Check global rate limit (across all apps) - ISponsorPaymaster.RateLimitData memory globalData = totalRateLimit[userOp.sender]; + ISponsorPaymaster.RateLimitData memory globalData = globalRateLimit[userOp.sender]; if (block.timestamp < globalData.lastOperationTime + RATE_LIMIT_PERIOD) { require(globalData.operationCount < RATE_LIMIT_THRESHOLD_TOTAL, "SP: Kinto Rate limit exceeded"); } @@ -237,7 +237,7 @@ contract SponsorPaymaster is Initializable, BasePaymaster, UUPSUpgradeable, Reen contractSpent[account] += ethCost; // Global network rate limit - ISponsorPaymaster.RateLimitData storage globalTxLimit = totalRateLimit[userAccount]; + ISponsorPaymaster.RateLimitData storage globalTxLimit = globalRateLimit[userAccount]; if (block.timestamp > globalTxLimit.lastOperationTime + RATE_LIMIT_PERIOD) { globalTxLimit.lastOperationTime = block.timestamp; globalTxLimit.operationCount = 1; From ab6a8f27ac5ea0f42186b92f86412bacb4c1fbd7 Mon Sep 17 00:00:00 2001 From: Ramon Recuero Date: Mon, 8 Jan 2024 09:51:29 -0800 Subject: [PATCH 30/43] Adds missing events --- src/apps/KintoAppRegistry.sol | 3 --- src/paymasters/SponsorPaymaster.sol | 9 ++++++--- src/wallet/KintoWallet.sol | 2 ++ 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/apps/KintoAppRegistry.sol b/src/apps/KintoAppRegistry.sol index 4322b71de..d77d36133 100644 --- a/src/apps/KintoAppRegistry.sol +++ b/src/apps/KintoAppRegistry.sol @@ -204,9 +204,6 @@ contract KintoAppRegistry is * @return The address of the contract that sponsors the contract */ function getSponsor(address _contract) external view override returns (address) { - if (_appMetadata[_contract].admin != address(0)) { - return _contract; - } if (childToParentContract[_contract] != address(0)) { return childToParentContract[_contract]; } diff --git a/src/paymasters/SponsorPaymaster.sol b/src/paymasters/SponsorPaymaster.sol index 3b53acb0a..f0f189f13 100644 --- a/src/paymasters/SponsorPaymaster.sol +++ b/src/paymasters/SponsorPaymaster.sol @@ -26,6 +26,9 @@ contract SponsorPaymaster is Initializable, BasePaymaster, UUPSUpgradeable, Reen using UserOperationLib for UserOperation; using SafeERC20 for IERC20; + // ========== Events ============ + event AppRegistrySet(address appRegistry, address _oldRegistry); + //calculated cost of the postOp uint256 public constant COST_OF_POST = 60_000; uint256 public constant MAX_COST_OF_VERIFICATION = 180_000; @@ -78,6 +81,8 @@ contract SponsorPaymaster is Initializable, BasePaymaster, UUPSUpgradeable, Reen * @param _appRegistry address of the app registry */ function setAppRegistry(address _appRegistry) external override onlyOwner { + require(_appRegistry != address(0) && _appRegistry != address(appRegistry), "SP: appRegistry cannot be 0"); + emit AppRegistrySet(_appRegistry, address(appRegistry)); appRegistry = IKintoAppRegistry(_appRegistry); } @@ -132,9 +137,7 @@ contract SponsorPaymaster is Initializable, BasePaymaster, UUPSUpgradeable, Reen entryPoint.withdrawTo(payable(target), amount); } - /** - * Viewers & validation ******** - */ + /* =============== Viewers & validation ============= */ /** * Return the deposit info for the account diff --git a/src/wallet/KintoWallet.sol b/src/wallet/KintoWallet.sol index 0dc5e745e..1f72827cd 100644 --- a/src/wallet/KintoWallet.sol +++ b/src/wallet/KintoWallet.sol @@ -54,6 +54,7 @@ contract KintoWallet is Initializable, BaseAccount, TokenCallbackHandler, IKinto event KintoWalletInitialized(IEntryPoint indexed entryPoint, address indexed owner); event WalletPolicyChanged(uint256 newPolicy, uint256 oldPolicy); event RecovererChanged(address indexed newRecoverer, address indexed recoverer); + event SignersChanged(address[] newSigners, address[] oldSigners); /* ============ Modifiers ============ */ @@ -326,6 +327,7 @@ contract KintoWallet is Initializable, BaseAccount, TokenCallbackHandler, IKinto require(newSigners[i] != address(0), "KW-rs: invalid signer address"); } owners = newSigners; + emit SignersChanged(newSigners, owners); // Change policy if needed. if (_policy != signerPolicy) { setSignerPolicy(_policy); From 59b92f7b507730ea68e4301d5205867d6bbd2f5b Mon Sep 17 00:00:00 2001 From: Ramon Recuero Date: Mon, 8 Jan 2024 10:02:44 -0800 Subject: [PATCH 31/43] broadcast --- script/migrations/07-deploy_kinto_app.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/script/migrations/07-deploy_kinto_app.sol b/script/migrations/07-deploy_kinto_app.sol index 2a1618cf3..c7b2ea0e6 100644 --- a/script/migrations/07-deploy_kinto_app.sol +++ b/script/migrations/07-deploy_kinto_app.sol @@ -30,7 +30,7 @@ contract KintoMigration7DeployScript is Create2Helper, ArtifactsReader { console.log("Admin key not set", admin); return; } - vm.broadcast(deployerPrivateKey); + vm.startBroadcast(deployerPrivateKey); console.log("Executing with address", msg.sender); address appAddr = _getChainDeployment("KintoAppRegistry"); if (appAddr != address(0)) { @@ -52,6 +52,7 @@ contract KintoMigration7DeployScript is Create2Helper, ArtifactsReader { // Fund in the paymaster SponsorPaymaster _paymaster = SponsorPaymaster(payable(_getChainDeployment("SponsorPaymaster"))); _paymaster.addDepositFor{value: 1e17}(credits); + vm.stopBroadcast(); // Writes the addresses to a file console.log("Add these new addresses to the artifacts file"); console.log(string.concat('"KintoAppRegistry": "', vm.toString(address(_kintoApp)), '"')); From 5be6ae1730063fa233b3f1d34ee1dd88b29079e3 Mon Sep 17 00:00:00 2001 From: Ramon Recuero Date: Mon, 8 Jan 2024 10:04:20 -0800 Subject: [PATCH 32/43] comments --- src/apps/KintoAppRegistry.sol | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/apps/KintoAppRegistry.sol b/src/apps/KintoAppRegistry.sol index d77d36133..02299b0c3 100644 --- a/src/apps/KintoAppRegistry.sol +++ b/src/apps/KintoAppRegistry.sol @@ -34,8 +34,9 @@ contract KintoAppRegistry is uint256 public override appCount; mapping(address => IKintoAppRegistry.Metadata) private _appMetadata; - mapping(address => mapping(address => bool)) private _sponsoredContracts; // other contracts to be sponsored - + // other contracts to be sponsored that dont belong in the app + mapping(address => mapping(address => bool)) private _sponsoredContracts; + // Mapping between the app and all the contracts that belong to it mapping(address => address) public override childToParentContract; /* ============ Events ============ */ From cc537c52275d06ed3e37ae208593d1fa41fdadcd Mon Sep 17 00:00:00 2001 From: Ramon Recuero Date: Mon, 8 Jan 2024 10:29:59 -0800 Subject: [PATCH 33/43] Fixes app whitelist and child parent req --- src/apps/KintoAppRegistry.sol | 2 +- src/wallet/KintoWallet.sol | 1 + test/KintoWallet.t.sol | 31 ++++++++++++++++++++++++++++-- test/helpers/AATestScaffolding.sol | 15 +++++++++++++++ 4 files changed, 46 insertions(+), 3 deletions(-) diff --git a/src/apps/KintoAppRegistry.sol b/src/apps/KintoAppRegistry.sol index 02299b0c3..b597fdb7f 100644 --- a/src/apps/KintoAppRegistry.sol +++ b/src/apps/KintoAppRegistry.sol @@ -107,7 +107,7 @@ contract KintoAppRegistry is address[] calldata appContracts, uint256[4] calldata appLimits ) external override { - require(appLimits.length == 4, "Invalid app limits"); + require(childToParentContract[parentContract] == address(0), "Parent contract already registered as a child"); _updateMetadata(_name, parentContract, appContracts, appLimits); appCount++; _safeMint(msg.sender, appCount); diff --git a/src/wallet/KintoWallet.sol b/src/wallet/KintoWallet.sol index 1f72827cd..ef08e2bf8 100644 --- a/src/wallet/KintoWallet.sol +++ b/src/wallet/KintoWallet.sol @@ -181,6 +181,7 @@ contract KintoWallet is Initializable, BaseAccount, TokenCallbackHandler, IKinto function setAppWhitelist(address[] calldata apps, bool[] calldata flags) external override onlySelf { require(apps.length == flags.length, "KW-apw: invalid array"); for (uint256 i = 0; i < apps.length; i++) { + require(appRegistry.getAppMetadata(apps[i]).admin != address(0), "KW-apw: app must be registered"); appWhitelist[apps[i]] = flags[i]; } } diff --git a/test/KintoWallet.t.sol b/test/KintoWallet.t.sol index 67db8d206..ccf69d564 100644 --- a/test/KintoWallet.t.sol +++ b/test/KintoWallet.t.sol @@ -161,6 +161,8 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // deploy the counter contract Counter counter = new Counter(); assertEq(counter.count(), 0); + address[] memory childContracts = new address[](0); + createApp(_owner, "test", address(counter), childContracts); // send a transaction to the counter contract through our wallet // without a paymaster and without prefunding the wallet @@ -185,7 +187,8 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // deploy the counter contract Counter counter = new Counter(); assertEq(counter.count(), 0); - + address[] memory childContracts = new address[](0); + createApp(_owner, "test", address(counter), childContracts); // prefund wallet vm.deal(address(_kintoWalletv1), 1 ether); @@ -257,6 +260,8 @@ contract KintoWalletTest is AATestScaffolding, UserOp { assertEq(counter.count(), 0); vm.stopPrank(); _fundPaymasterForContract(address(counter)); + address[] memory childContracts = new address[](0); + createApp(_owner, "test", address(counter), childContracts); vm.startPrank(_owner); // Let's send a transaction to the counter contract through our wallet uint256 nonce = _kintoWalletv1.getNonce(); @@ -295,6 +300,8 @@ contract KintoWalletTest is AATestScaffolding, UserOp { assertEq(counter.count(), 0); uint256 nonce = _kintoWalletv1.getNonce(); vm.stopPrank(); + address[] memory childContracts = new address[](0); + createApp(_owner, "test", address(counter), childContracts); vm.startPrank(_owner); _fundPaymasterForContract(address(counter)); // Let's send a transaction to the counter contract through our wallet @@ -337,6 +344,8 @@ contract KintoWalletTest is AATestScaffolding, UserOp { assertEq(counter.count(), 0); uint256 nonce = _kintoWalletv1.getNonce(); vm.stopPrank(); + address[] memory childContracts = new address[](0); + createApp(_owner, "test", address(counter), childContracts); vm.startPrank(_owner); _fundPaymasterForContract(address(counter)); address[] memory targets = new address[](3); @@ -378,6 +387,9 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // only fund counter _fundPaymasterForContract(address(counter)); + address[] memory childContracts = new address[](0); + createApp(_owner, "test", address(counter), childContracts); + // prep batch address[] memory targets = new address[](3); targets[0] = address(_kintoWalletv1); @@ -709,6 +721,8 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // (4). fund paymaster for Counter contract _fundPaymasterForContract(address(counter)); + address[] memory childContracts = new address[](0); + createApp(_owner, "test", address(counter), childContracts); // (5). Set private keys privateKeys = new uint256[](2); @@ -776,6 +790,8 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // (4). fund paymaster for Counter contract _fundPaymasterForContract(address(counter)); + address[] memory childContracts = new address[](0); + createApp(_owner, "test", address(counter), childContracts); // (5). Create 2 user ops: userOps = new UserOperation[](2); @@ -841,6 +857,8 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // (4). fund paymaster for Counter contract _fundPaymasterForContract(address(counter)); + address[] memory childContracts = new address[](0); + createApp(_owner, "test", address(counter), childContracts); // (5). Set private keys privateKeys = new uint256[](3); @@ -883,6 +901,8 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // (2). fund paymaster for Counter contract _fundPaymasterForContract(address(counter)); + address[] memory childContracts = new address[](0); + createApp(_owner, "test", address(counter), childContracts); // (3). Create 2 user ops: UserOperation[] memory userOps = new UserOperation[](1); @@ -949,6 +969,8 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // (4). fund paymaster for Counter contract _fundPaymasterForContract(address(counter)); + address[] memory childContracts = new address[](0); + createApp(_owner, "test", address(counter), childContracts); // (5). Set private keys privateKeys = new uint256[](2); @@ -1168,6 +1190,8 @@ contract KintoWalletTest is AATestScaffolding, UserOp { function test_RevertWhen_SettingAppKeyNoWhitelist() public { address app = address(_engenCredits); + address[] memory childContracts = new address[](0); + createApp(_owner, "test", address(_engenCredits), childContracts); UserOperation memory userOp = this.createUserOperationWithPaymaster( _chainID, address(_kintoWalletv1), @@ -1197,6 +1221,8 @@ contract KintoWalletTest is AATestScaffolding, UserOp { function testSettingAppKey() public { address app = address(_engenCredits); uint256 nonce = _kintoWalletv1.getNonce(); + address[] memory childContracts = new address[](0); + createApp(_owner, "test", address(_engenCredits), childContracts); UserOperation[] memory userOps = new UserOperation[](2); userOps[0] = createWhitelistAppOp( @@ -1221,7 +1247,6 @@ contract KintoWalletTest is AATestScaffolding, UserOp { function testMultisigTransactionWith2SignersWithAppkey() public { vm.startPrank(_owner); - // set 2 owners address[] memory owners = new address[](2); owners[0] = _owner; @@ -1250,6 +1275,8 @@ contract KintoWalletTest is AATestScaffolding, UserOp { Counter counter = new Counter(); assertEq(counter.count(), 0); vm.stopPrank(); + address[] memory childContracts = new address[](0); + createApp(_owner, "test", address(counter), childContracts); // Fund counter contract vm.startPrank(_owner); diff --git a/test/helpers/AATestScaffolding.sol b/test/helpers/AATestScaffolding.sol index 66043e5a5..54a1f32c5 100644 --- a/test/helpers/AATestScaffolding.sol +++ b/test/helpers/AATestScaffolding.sol @@ -206,6 +206,21 @@ abstract contract AATestScaffolding is KYCSignature { vm.stopPrank(); } + function createApp(address _owner, string memory name, address parentContract, address[] memory appContracts) + public + { + vm.startPrank(_owner); + uint256[] memory appLimits = new uint256[](4); + appLimits[0] = _kintoApp.RATE_LIMIT_PERIOD(); + appLimits[1] = _kintoApp.RATE_LIMIT_THRESHOLD(); + appLimits[2] = _kintoApp.GAS_LIMIT_PERIOD(); + appLimits[3] = _kintoApp.GAS_LIMIT_THRESHOLD(); + _kintoApp.registerApp( + name, parentContract, appContracts, [appLimits[0], appLimits[1], appLimits[2], appLimits[3]] + ); + vm.stopPrank(); + } + ////// helper methods to assert the revert reason on UserOperationRevertReason events //// // string reasons From 5b7d8464676162d01792bda711ddd31d2becaa46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Federico=20Mart=C3=ADn=20Alconada=20Verzini?= Date: Mon, 8 Jan 2024 16:17:37 -0300 Subject: [PATCH 34/43] refactor: rmv unneeded files --- test/KintoWallet.t.sol | 47 +++++++++--------------------------------- 1 file changed, 10 insertions(+), 37 deletions(-) diff --git a/test/KintoWallet.t.sol b/test/KintoWallet.t.sol index ccf69d564..972089bcc 100644 --- a/test/KintoWallet.t.sol +++ b/test/KintoWallet.t.sol @@ -1,39 +1,16 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13; -import "../src/wallet/KintoWallet.sol"; -import "../src/wallet/KintoWalletFactory.sol"; -import "../src/tokens/EngenCredits.sol"; -import "../src/paymasters/SponsorPaymaster.sol"; -import "../src/KintoID.sol"; +import "../src/interfaces/IKintoWallet.sol"; +import "@aa/interfaces/IEntryPoint.sol"; + +import {KintoWalletV3 as KintoWallet} from "../src/wallet/KintoWallet.sol"; import {UserOp} from "./helpers/UserOp.sol"; -import {UUPSProxy} from "./helpers/UUPSProxy.sol"; -import {KYCSignature} from "./helpers/KYCSignature.sol"; import {AATestScaffolding} from "./helpers/AATestScaffolding.sol"; -import "@aa/interfaces/IAccount.sol"; -import "@aa/interfaces/INonceManager.sol"; -import "@aa/interfaces/IEntryPoint.sol"; -import "@aa/core/EntryPoint.sol"; -import "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol"; -import {UpgradeableBeacon} from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; -import {SignatureChecker} from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol"; -import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; -import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; - import {Test, stdError} from "forge-std/Test.sol"; import "forge-std/console.sol"; -contract KintoWalletv2 is KintoWallet { - constructor(IEntryPoint _entryPoint, IKintoID _kintoID, IKintoAppRegistry _kintoApp) - KintoWallet(_entryPoint, _kintoID, _kintoApp) - {} - - function newFunction() public pure returns (uint256) { - return 1; - } -} - contract Counter { uint256 public count; @@ -47,10 +24,6 @@ contract Counter { } contract KintoWalletTest is AATestScaffolding, UserOp { - using ECDSAUpgradeable for bytes32; - using SignatureChecker for address; - - KintoWalletv2 _kintoWalletv2; uint256[] privateKeys; uint256 _chainID = 1; @@ -89,8 +62,8 @@ contract KintoWalletTest is AATestScaffolding, UserOp { /* ============ Upgrade Tests ============ */ function test_RevertWhen_OwnerCannotUpgrade() public { - // deploy a KintoWalletv2 - KintoWalletv2 _implementationV2 = new KintoWalletv2(_entryPoint, _kintoIDv1, _kintoApp); + // deploy a new implementation + KintoWallet _newImplementation = new KintoWallet(_entryPoint, _kintoIDv1, _kintoApp); uint256 nonce = _kintoWalletv1.getNonce(); @@ -102,7 +75,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { privateKeys, address(_kintoWalletv1), 0, - abi.encodeWithSignature("upgradeTo(address)", address(_implementationV2)), + abi.encodeWithSignature("upgradeTo(address)", address(_newImplementation)), address(_paymaster) ); UserOperation[] memory userOps = new UserOperation[](1); @@ -122,8 +95,8 @@ contract KintoWalletTest is AATestScaffolding, UserOp { approveKYC(_kycProvider, _user, _userPk); IKintoWallet userWallet = _walletFactory.createAccount(_user, _recoverer, 0); - // deploy a KintoWalletv2 - KintoWalletv2 _implementationV2 = new KintoWalletv2(_entryPoint, _kintoIDv1, _kintoApp); + // deploy a new implementation + KintoWallet _newImplementation = new KintoWallet(_entryPoint, _kintoIDv1, _kintoApp); // try calling upgradeTo from _user wallet to upgrade _owner wallet uint256 nonce = userWallet.getNonce(); @@ -136,7 +109,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { privateKeys, address(_kintoWalletv1), 0, - abi.encodeWithSignature("upgradeTo(address)", address(_implementationV2)), + abi.encodeWithSignature("upgradeTo(address)", address(_newImplementation)), address(_paymaster) ); From bb63af79ca8a76b5315ca2a89609ce39d662e60b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Federico=20Mart=C3=ADn=20Alconada=20Verzini?= Date: Mon, 8 Jan 2024 16:26:01 -0300 Subject: [PATCH 35/43] refactor: unify Counter contracts --- src/sample/Counter.sol | 2 +- src/sample/CounterInitializable.sol | 21 +++++++++++++++++++++ src/sample/OwnableCounter.sol | 13 +++++++++++++ test/DeployTests.t.sol | 27 ++------------------------- test/KintoWallet.t.sol | 13 +------------ test/KintoWalletFactory.t.sol | 13 +------------ test/SponsorPaymastExploit.t.sol | 13 +------------ 7 files changed, 40 insertions(+), 62 deletions(-) create mode 100644 src/sample/CounterInitializable.sol create mode 100644 src/sample/OwnableCounter.sol diff --git a/src/sample/Counter.sol b/src/sample/Counter.sol index e60fc639c..2f6725ee0 100644 --- a/src/sample/Counter.sol +++ b/src/sample/Counter.sol @@ -8,7 +8,7 @@ contract Counter { count = 0; } - function increment() public { + function increment() public virtual { count += 1; } } diff --git a/src/sample/CounterInitializable.sol b/src/sample/CounterInitializable.sol new file mode 100644 index 000000000..13459a9d5 --- /dev/null +++ b/src/sample/CounterInitializable.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; + +contract CounterInitializable is Initializable, OwnableUpgradeable, UUPSUpgradeable { + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + function initialize(address initialOwner) public initializer { + __Ownable_init(); + _transferOwnership(initialOwner); + __UUPSUpgradeable_init(); + } + + function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} +} diff --git a/src/sample/OwnableCounter.sol b/src/sample/OwnableCounter.sol new file mode 100644 index 000000000..84ed1640b --- /dev/null +++ b/src/sample/OwnableCounter.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +import "./Counter.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; + +contract OwnableCounter is Counter, Ownable { + constructor() Ownable() {} + + function increment() public override onlyOwner { + count += 1; + } +} diff --git a/test/DeployTests.t.sol b/test/DeployTests.t.sol index 2b4bcda19..ea8148951 100644 --- a/test/DeployTests.t.sol +++ b/test/DeployTests.t.sol @@ -11,6 +11,8 @@ import {UUPSProxy} from "./helpers/UUPSProxy.sol"; import {AATestScaffolding} from "./helpers/AATestScaffolding.sol"; import {Create2Helper} from "./helpers/Create2Helper.sol"; import "./helpers/KYCSignature.sol"; +import "../src/sample/CounterInitializable.sol"; +import {OwnableCounter as Counter} from "../src/sample/OwnableCounter.sol"; import "@aa/interfaces/IAccount.sol"; import "@aa/interfaces/INonceManager.sol"; @@ -28,31 +30,6 @@ import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import "forge-std/Test.sol"; import "forge-std/console.sol"; -contract Counter is Ownable { - uint256 public count = 0; - - constructor() Ownable() {} - - function increment() public onlyOwner { - count += 1; - } -} - -contract CounterInitializable is Initializable, OwnableUpgradeable, UUPSUpgradeable { - /// @custom:oz-upgrades-unsafe-allow constructor - constructor() { - _disableInitializers(); - } - - function initialize(address initialOwner) public initializer { - __Ownable_init(); - _transferOwnership(initialOwner); - __UUPSUpgradeable_init(); - } - - function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} -} - contract DeveloperDeployTest is Create2Helper, UserOp, AATestScaffolding { using ECDSAUpgradeable for bytes32; using SignatureChecker for address; diff --git a/test/KintoWallet.t.sol b/test/KintoWallet.t.sol index 972089bcc..4beaf14b4 100644 --- a/test/KintoWallet.t.sol +++ b/test/KintoWallet.t.sol @@ -7,22 +7,11 @@ import "@aa/interfaces/IEntryPoint.sol"; import {KintoWalletV3 as KintoWallet} from "../src/wallet/KintoWallet.sol"; import {UserOp} from "./helpers/UserOp.sol"; import {AATestScaffolding} from "./helpers/AATestScaffolding.sol"; +import "../src/sample/Counter.sol"; import {Test, stdError} from "forge-std/Test.sol"; import "forge-std/console.sol"; -contract Counter { - uint256 public count; - - constructor() { - count = 0; - } - - function increment() public { - count += 1; - } -} - contract KintoWalletTest is AATestScaffolding, UserOp { uint256[] privateKeys; diff --git a/test/KintoWalletFactory.t.sol b/test/KintoWalletFactory.t.sol index 8237214a9..e5048b029 100644 --- a/test/KintoWalletFactory.t.sol +++ b/test/KintoWalletFactory.t.sol @@ -9,6 +9,7 @@ import {UserOp} from "./helpers/UserOp.sol"; import {UUPSProxy} from "./helpers/UUPSProxy.sol"; import {AATestScaffolding} from "./helpers/AATestScaffolding.sol"; import {Create2Helper} from "./helpers/Create2Helper.sol"; +import "../src/sample/Counter.sol"; import "@aa/interfaces/IAccount.sol"; import "@aa/interfaces/INonceManager.sol"; @@ -33,18 +34,6 @@ contract KintoWalletV999 is KintoWallet { } } -contract Counter { - uint256 public count; - - constructor() { - count = 0; - } - - function increment() public { - count += 1; - } -} - contract KintoWalletFactoryV999 is KintoWalletFactory { constructor(KintoWallet _impl) KintoWalletFactory(_impl) {} diff --git a/test/SponsorPaymastExploit.t.sol b/test/SponsorPaymastExploit.t.sol index 2639b7987..28db55c44 100644 --- a/test/SponsorPaymastExploit.t.sol +++ b/test/SponsorPaymastExploit.t.sol @@ -10,6 +10,7 @@ import {UUPSProxy} from "./helpers/UUPSProxy.sol"; import {AATestScaffolding} from "./helpers/AATestScaffolding.sol"; import {Create2Helper} from "./helpers/Create2Helper.sol"; import {KYCSignature} from "./helpers/KYCSignature.sol"; +import "../src/sample/Counter.sol"; import "@aa/interfaces/IAccount.sol"; import "@aa/interfaces/INonceManager.sol"; @@ -53,18 +54,6 @@ contract MyOpCreator is UserOp, KYCSignature { } } -contract Counter { - uint256 public count; - - constructor() { - count = 0; - } - - function increment() public { - count += 1; - } -} - contract SponsorPaymasterExploitTest is Create2Helper, MyOpCreator, AATestScaffolding { using ECDSAUpgradeable for bytes32; using SignatureChecker for address; From de4b17f16ef8e42668c5fb778123a2e34ce93992 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Federico=20Mart=C3=ADn=20Alconada=20Verzini?= Date: Mon, 8 Jan 2024 16:38:47 -0300 Subject: [PATCH 36/43] refactor: rename _kintoApp as _kintoAppRegistry --- test/EngenCredits.t.sol | 2 +- test/KintoAppRegistry.t.sol | 134 ++++++++++++++--------------- test/KintoWallet.t.sol | 6 +- test/KintoWalletFactory.t.sol | 8 +- test/helpers/AATestScaffolding.sol | 54 ++++++------ 5 files changed, 105 insertions(+), 99 deletions(-) diff --git a/test/EngenCredits.t.sol b/test/EngenCredits.t.sol index 8bc4c51ff..ee14254ca 100644 --- a/test/EngenCredits.t.sol +++ b/test/EngenCredits.t.sol @@ -32,7 +32,7 @@ contract EngenCreditsTest is Create2Helper, UserOp, AATestScaffolding { _fundPaymasterForContract(address(_engenCredits)); _fundPaymasterForContract(address(_kintoWalletv1)); vm.startPrank(_owner); - _kintoApp.registerApp( + _kintoAppRegistry.registerApp( "engen credits", address(_engenCredits), new address[](0), [uint256(0), uint256(0), uint256(0), uint256(0)] ); vm.stopPrank(); diff --git a/test/KintoAppRegistry.t.sol b/test/KintoAppRegistry.t.sol index 8a563949b..c9aea6764 100644 --- a/test/KintoAppRegistry.t.sol +++ b/test/KintoAppRegistry.t.sol @@ -52,13 +52,13 @@ contract KintoAppRegistryTest is Create2Helper, UserOp, AATestScaffolding { function testUp() public { console.log("address owner", address(_owner)); - assertEq(_kintoApp.owner(), _owner); - assertEq(_kintoApp.name(), "Kinto APP"); - assertEq(_kintoApp.symbol(), "KINTOAPP"); - assertEq(_kintoApp.RATE_LIMIT_PERIOD(), 1 minutes); - assertEq(_kintoApp.RATE_LIMIT_THRESHOLD(), 10); - assertEq(_kintoApp.GAS_LIMIT_PERIOD(), 30 days); - assertEq(_kintoApp.GAS_LIMIT_THRESHOLD(), 1e16); + assertEq(_kintoAppRegistry.owner(), _owner); + assertEq(_kintoAppRegistry.name(), "Kinto APP"); + assertEq(_kintoAppRegistry.symbol(), "KINTOAPP"); + assertEq(_kintoAppRegistry.RATE_LIMIT_PERIOD(), 1 minutes); + assertEq(_kintoAppRegistry.RATE_LIMIT_THRESHOLD(), 10); + assertEq(_kintoAppRegistry.GAS_LIMIT_PERIOD(), 30 days); + assertEq(_kintoAppRegistry.GAS_LIMIT_THRESHOLD(), 1e16); } /* ============ Upgrade Tests ============ */ @@ -66,9 +66,9 @@ contract KintoAppRegistryTest is Create2Helper, UserOp, AATestScaffolding { function testOwnerCanUpgradeApp() public { vm.startPrank(_owner); KintoAppRegistryV2 _implementationV2 = new KintoAppRegistryV2(); - _kintoApp.upgradeTo(address(_implementationV2)); + _kintoAppRegistry.upgradeTo(address(_implementationV2)); // re-wrap the _proxy - _kintoApp2 = KintoAppRegistryV2(address(_kintoApp)); + _kintoApp2 = KintoAppRegistryV2(address(_kintoAppRegistry)); assertEq(_kintoApp2.newFunction(), 1); vm.stopPrank(); } @@ -76,26 +76,26 @@ contract KintoAppRegistryTest is Create2Helper, UserOp, AATestScaffolding { function test_RevertWhen_OthersCannotUpgradeAppRegistry() public { KintoAppRegistryV2 _implementationV2 = new KintoAppRegistryV2(); vm.expectRevert("Ownable: caller is not the owner"); - _kintoApp.upgradeTo(address(_implementationV2)); + _kintoAppRegistry.upgradeTo(address(_implementationV2)); } /* ============ App Tests & Viewers ============ */ function testRegisterApp(string memory name, address parentContract) public { vm.startPrank(_user); - assertEq(_kintoApp.appCount(), 0); + assertEq(_kintoAppRegistry.appCount(), 0); address[] memory appContracts = new address[](1); appContracts[0] = address(7); uint256[] memory appLimits = new uint256[](4); - appLimits[0] = _kintoApp.RATE_LIMIT_PERIOD(); - appLimits[1] = _kintoApp.RATE_LIMIT_THRESHOLD(); - appLimits[2] = _kintoApp.GAS_LIMIT_PERIOD(); - appLimits[3] = _kintoApp.GAS_LIMIT_THRESHOLD(); - _kintoApp.registerApp( + appLimits[0] = _kintoAppRegistry.RATE_LIMIT_PERIOD(); + appLimits[1] = _kintoAppRegistry.RATE_LIMIT_THRESHOLD(); + appLimits[2] = _kintoAppRegistry.GAS_LIMIT_PERIOD(); + appLimits[3] = _kintoAppRegistry.GAS_LIMIT_THRESHOLD(); + _kintoAppRegistry.registerApp( name, parentContract, appContracts, [appLimits[0], appLimits[1], appLimits[2], appLimits[3]] ); - assertEq(_kintoApp.appCount(), 1); - IKintoAppRegistry.Metadata memory metadata = _kintoApp.getAppMetadata(parentContract); + assertEq(_kintoAppRegistry.appCount(), 1); + IKintoAppRegistry.Metadata memory metadata = _kintoAppRegistry.getAppMetadata(parentContract); assertEq(metadata.name, name); assertEq(metadata.admin, address(_user)); assertEq(metadata.dsaEnabled, false); @@ -103,19 +103,19 @@ contract KintoAppRegistryTest is Create2Helper, UserOp, AATestScaffolding { assertEq(metadata.rateLimitNumber, appLimits[1]); assertEq(metadata.gasLimitPeriod, appLimits[2]); assertEq(metadata.gasLimitCost, appLimits[3]); - assertEq(_kintoApp.isContractSponsored(parentContract, address(7)), true); - assertEq(_kintoApp.getSponsor(address(7)), parentContract); - uint256[4] memory limits = _kintoApp.getContractLimits(address(7)); + assertEq(_kintoAppRegistry.isContractSponsored(parentContract, address(7)), true); + assertEq(_kintoAppRegistry.getSponsor(address(7)), parentContract); + uint256[4] memory limits = _kintoAppRegistry.getContractLimits(address(7)); assertEq(limits[0], appLimits[0]); assertEq(limits[1], appLimits[1]); assertEq(limits[2], appLimits[2]); assertEq(limits[3], appLimits[3]); - limits = _kintoApp.getContractLimits(parentContract); + limits = _kintoAppRegistry.getContractLimits(parentContract); assertEq(limits[0], appLimits[0]); assertEq(limits[1], appLimits[1]); assertEq(limits[2], appLimits[2]); assertEq(limits[3], appLimits[3]); - metadata = _kintoApp.getAppMetadata(address(7)); + metadata = _kintoAppRegistry.getAppMetadata(address(7)); assertEq(metadata.name, name); vm.stopPrank(); } @@ -125,17 +125,17 @@ contract KintoAppRegistryTest is Create2Helper, UserOp, AATestScaffolding { address[] memory appContracts = new address[](1); appContracts[0] = address(8); uint256[] memory appLimits = new uint256[](4); - appLimits[0] = _kintoApp.RATE_LIMIT_PERIOD(); - appLimits[1] = _kintoApp.RATE_LIMIT_THRESHOLD(); - appLimits[2] = _kintoApp.GAS_LIMIT_PERIOD(); - appLimits[3] = _kintoApp.GAS_LIMIT_THRESHOLD(); - _kintoApp.registerApp( + appLimits[0] = _kintoAppRegistry.RATE_LIMIT_PERIOD(); + appLimits[1] = _kintoAppRegistry.RATE_LIMIT_THRESHOLD(); + appLimits[2] = _kintoAppRegistry.GAS_LIMIT_PERIOD(); + appLimits[3] = _kintoAppRegistry.GAS_LIMIT_THRESHOLD(); + _kintoAppRegistry.registerApp( name, parentContract, appContracts, [appLimits[0], appLimits[1], appLimits[2], appLimits[3]] ); - _kintoApp.updateMetadata( + _kintoAppRegistry.updateMetadata( "test2", parentContract, appContracts, [uint256(1), uint256(1), uint256(1), uint256(1)] ); - IKintoAppRegistry.Metadata memory metadata = _kintoApp.getAppMetadata(parentContract); + IKintoAppRegistry.Metadata memory metadata = _kintoAppRegistry.getAppMetadata(parentContract); assertEq(metadata.name, "test2"); assertEq(metadata.admin, address(_user)); assertEq(metadata.dsaEnabled, false); @@ -143,8 +143,8 @@ contract KintoAppRegistryTest is Create2Helper, UserOp, AATestScaffolding { assertEq(metadata.rateLimitNumber, 1); assertEq(metadata.gasLimitPeriod, 1); assertEq(metadata.gasLimitCost, 1); - assertEq(_kintoApp.isContractSponsored(parentContract, address(7)), false); - assertEq(_kintoApp.isContractSponsored(parentContract, address(8)), true); + assertEq(_kintoAppRegistry.isContractSponsored(parentContract, address(7)), false); + assertEq(_kintoAppRegistry.isContractSponsored(parentContract, address(8)), true); vm.stopPrank(); } @@ -156,15 +156,15 @@ contract KintoAppRegistryTest is Create2Helper, UserOp, AATestScaffolding { address[] memory appContracts = new address[](1); appContracts[0] = address(8); uint256[] memory appLimits = new uint256[](4); - appLimits[0] = _kintoApp.RATE_LIMIT_PERIOD(); - appLimits[1] = _kintoApp.RATE_LIMIT_THRESHOLD(); - appLimits[2] = _kintoApp.GAS_LIMIT_PERIOD(); - appLimits[3] = _kintoApp.GAS_LIMIT_THRESHOLD(); - _kintoApp.registerApp( + appLimits[0] = _kintoAppRegistry.RATE_LIMIT_PERIOD(); + appLimits[1] = _kintoAppRegistry.RATE_LIMIT_THRESHOLD(); + appLimits[2] = _kintoAppRegistry.GAS_LIMIT_PERIOD(); + appLimits[3] = _kintoAppRegistry.GAS_LIMIT_THRESHOLD(); + _kintoAppRegistry.registerApp( "", parentContract, appContracts, [appLimits[0], appLimits[1], appLimits[2], appLimits[3]] ); - _kintoApp.enableDSA(parentContract); - IKintoAppRegistry.Metadata memory metadata = _kintoApp.getAppMetadata(parentContract); + _kintoAppRegistry.enableDSA(parentContract); + IKintoAppRegistry.Metadata memory metadata = _kintoAppRegistry.getAppMetadata(parentContract); assertEq(metadata.dsaEnabled, true); vm.stopPrank(); } @@ -175,15 +175,15 @@ contract KintoAppRegistryTest is Create2Helper, UserOp, AATestScaffolding { address[] memory appContracts = new address[](1); appContracts[0] = address(8); uint256[] memory appLimits = new uint256[](4); - appLimits[0] = _kintoApp.RATE_LIMIT_PERIOD(); - appLimits[1] = _kintoApp.RATE_LIMIT_THRESHOLD(); - appLimits[2] = _kintoApp.GAS_LIMIT_PERIOD(); - appLimits[3] = _kintoApp.GAS_LIMIT_THRESHOLD(); - _kintoApp.registerApp( + appLimits[0] = _kintoAppRegistry.RATE_LIMIT_PERIOD(); + appLimits[1] = _kintoAppRegistry.RATE_LIMIT_THRESHOLD(); + appLimits[2] = _kintoAppRegistry.GAS_LIMIT_PERIOD(); + appLimits[3] = _kintoAppRegistry.GAS_LIMIT_THRESHOLD(); + _kintoAppRegistry.registerApp( "", parentContract, appContracts, [appLimits[0], appLimits[1], appLimits[2], appLimits[3]] ); vm.expectRevert("Ownable: caller is not the owner"); - _kintoApp.enableDSA(parentContract); + _kintoAppRegistry.enableDSA(parentContract); } /* ============ Sponsored Contracts Test ============ */ @@ -194,11 +194,11 @@ contract KintoAppRegistryTest is Create2Helper, UserOp, AATestScaffolding { address[] memory appContracts = new address[](1); appContracts[0] = address(8); uint256[] memory appLimits = new uint256[](4); - appLimits[0] = _kintoApp.RATE_LIMIT_PERIOD(); - appLimits[1] = _kintoApp.RATE_LIMIT_THRESHOLD(); - appLimits[2] = _kintoApp.GAS_LIMIT_PERIOD(); - appLimits[3] = _kintoApp.GAS_LIMIT_THRESHOLD(); - _kintoApp.registerApp( + appLimits[0] = _kintoAppRegistry.RATE_LIMIT_PERIOD(); + appLimits[1] = _kintoAppRegistry.RATE_LIMIT_THRESHOLD(); + appLimits[2] = _kintoAppRegistry.GAS_LIMIT_PERIOD(); + appLimits[3] = _kintoAppRegistry.GAS_LIMIT_THRESHOLD(); + _kintoAppRegistry.registerApp( "", parentContract, appContracts, [appLimits[0], appLimits[1], appLimits[2], appLimits[3]] ); address[] memory contracts = new address[](2); @@ -207,10 +207,10 @@ contract KintoAppRegistryTest is Create2Helper, UserOp, AATestScaffolding { bool[] memory flags = new bool[](2); flags[0] = false; flags[1] = true; - _kintoApp.setSponsoredContracts(parentContract, contracts, flags); - assertEq(_kintoApp.isContractSponsored(parentContract, address(8)), true); // child contracts always sponsored - assertEq(_kintoApp.isContractSponsored(parentContract, address(9)), true); - assertEq(_kintoApp.isContractSponsored(parentContract, address(10)), false); + _kintoAppRegistry.setSponsoredContracts(parentContract, contracts, flags); + assertEq(_kintoAppRegistry.isContractSponsored(parentContract, address(8)), true); // child contracts always sponsored + assertEq(_kintoAppRegistry.isContractSponsored(parentContract, address(9)), true); + assertEq(_kintoAppRegistry.isContractSponsored(parentContract, address(10)), false); vm.stopPrank(); } @@ -220,11 +220,11 @@ contract KintoAppRegistryTest is Create2Helper, UserOp, AATestScaffolding { address[] memory appContracts = new address[](1); appContracts[0] = address(8); uint256[] memory appLimits = new uint256[](4); - appLimits[0] = _kintoApp.RATE_LIMIT_PERIOD(); - appLimits[1] = _kintoApp.RATE_LIMIT_THRESHOLD(); - appLimits[2] = _kintoApp.GAS_LIMIT_PERIOD(); - appLimits[3] = _kintoApp.GAS_LIMIT_THRESHOLD(); - _kintoApp.registerApp( + appLimits[0] = _kintoAppRegistry.RATE_LIMIT_PERIOD(); + appLimits[1] = _kintoAppRegistry.RATE_LIMIT_THRESHOLD(); + appLimits[2] = _kintoAppRegistry.GAS_LIMIT_PERIOD(); + appLimits[3] = _kintoAppRegistry.GAS_LIMIT_THRESHOLD(); + _kintoAppRegistry.registerApp( "", parentContract, appContracts, [appLimits[0], appLimits[1], appLimits[2], appLimits[3]] ); address[] memory contracts = new address[](2); @@ -235,7 +235,7 @@ contract KintoAppRegistryTest is Create2Helper, UserOp, AATestScaffolding { flags[1] = true; vm.startPrank(_user2); vm.expectRevert("Only developer can set sponsored contracts"); - _kintoApp.setSponsoredContracts(parentContract, contracts, flags); + _kintoAppRegistry.setSponsoredContracts(parentContract, contracts, flags); vm.stopPrank(); } @@ -247,15 +247,15 @@ contract KintoAppRegistryTest is Create2Helper, UserOp, AATestScaffolding { address[] memory appContracts = new address[](1); appContracts[0] = address(8); uint256[] memory appLimits = new uint256[](4); - appLimits[0] = _kintoApp.RATE_LIMIT_PERIOD(); - appLimits[1] = _kintoApp.RATE_LIMIT_THRESHOLD(); - appLimits[2] = _kintoApp.GAS_LIMIT_PERIOD(); - appLimits[3] = _kintoApp.GAS_LIMIT_THRESHOLD(); - _kintoApp.registerApp( + appLimits[0] = _kintoAppRegistry.RATE_LIMIT_PERIOD(); + appLimits[1] = _kintoAppRegistry.RATE_LIMIT_THRESHOLD(); + appLimits[2] = _kintoAppRegistry.GAS_LIMIT_PERIOD(); + appLimits[3] = _kintoAppRegistry.GAS_LIMIT_THRESHOLD(); + _kintoAppRegistry.registerApp( "", parentContract, appContracts, [appLimits[0], appLimits[1], appLimits[2], appLimits[3]] ); - uint256 tokenIdx = _kintoApp.tokenOfOwnerByIndex(_user, 0); + uint256 tokenIdx = _kintoAppRegistry.tokenOfOwnerByIndex(_user, 0); vm.expectRevert("Only mint transfers are allowed"); - _kintoApp.safeTransferFrom(_user, _user2, tokenIdx); + _kintoAppRegistry.safeTransferFrom(_user, _user2, tokenIdx); } } diff --git a/test/KintoWallet.t.sol b/test/KintoWallet.t.sol index 4beaf14b4..9c64c6300 100644 --- a/test/KintoWallet.t.sol +++ b/test/KintoWallet.t.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.13; import "../src/interfaces/IKintoWallet.sol"; import "@aa/interfaces/IEntryPoint.sol"; -import {KintoWalletV3 as KintoWallet} from "../src/wallet/KintoWallet.sol"; +import "../src/wallet/KintoWallet.sol"; import {UserOp} from "./helpers/UserOp.sol"; import {AATestScaffolding} from "./helpers/AATestScaffolding.sol"; import "../src/sample/Counter.sol"; @@ -52,7 +52,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { function test_RevertWhen_OwnerCannotUpgrade() public { // deploy a new implementation - KintoWallet _newImplementation = new KintoWallet(_entryPoint, _kintoIDv1, _kintoApp); + KintoWallet _newImplementation = new KintoWallet(_entryPoint, _kintoIDv1, _kintoAppRegistry); uint256 nonce = _kintoWalletv1.getNonce(); @@ -85,7 +85,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { IKintoWallet userWallet = _walletFactory.createAccount(_user, _recoverer, 0); // deploy a new implementation - KintoWallet _newImplementation = new KintoWallet(_entryPoint, _kintoIDv1, _kintoApp); + KintoWallet _newImplementation = new KintoWallet(_entryPoint, _kintoIDv1, _kintoAppRegistry); // try calling upgradeTo from _user wallet to upgrade _owner wallet uint256 nonce = userWallet.getNonce(); diff --git a/test/KintoWalletFactory.t.sol b/test/KintoWalletFactory.t.sol index e5048b029..c264e5da5 100644 --- a/test/KintoWalletFactory.t.sol +++ b/test/KintoWalletFactory.t.sol @@ -25,8 +25,8 @@ import "forge-std/Test.sol"; import "forge-std/console.sol"; contract KintoWalletV999 is KintoWallet { - constructor(IEntryPoint _entryPoint, IKintoID _kintoID, IKintoAppRegistry _kintoApp) - KintoWallet(_entryPoint, _kintoID, _kintoApp) + constructor(IEntryPoint _entryPoint, IKintoID _kintoID, IKintoAppRegistry _kintoAppRegistry) + KintoWallet(_entryPoint, _kintoID, _kintoAppRegistry) {} function walletFunction() public pure returns (uint256) { @@ -86,7 +86,7 @@ contract KintoWalletFactoryTest is Create2Helper, UserOp, AATestScaffolding { vm.startPrank(_owner); // Deploy wallet implementation - _kintoWalletImpl = new KintoWalletV999(_entryPoint, _kintoIDv1, _kintoApp); + _kintoWalletImpl = new KintoWalletV999(_entryPoint, _kintoIDv1, _kintoAppRegistry); // deploy walletv1 through wallet factory and initializes it _kintoWalletv1 = _walletFactory.createAccount(_owner, _owner, 0); @@ -101,7 +101,7 @@ contract KintoWalletFactoryTest is Create2Helper, UserOp, AATestScaffolding { function testUpgrade_RevertWhen_CallerIsNotOwner() public { // deploy wallet implementation - _kintoWalletImpl = new KintoWalletV999(_entryPoint, _kintoIDv1, _kintoApp); + _kintoWalletImpl = new KintoWalletV999(_entryPoint, _kintoIDv1, _kintoAppRegistry); // deploy walletv1 through wallet factory and initializes it _kintoWalletv1 = _walletFactory.createAccount(_owner, _owner, 0); diff --git a/test/helpers/AATestScaffolding.sol b/test/helpers/AATestScaffolding.sol index 54a1f32c5..e86f59d26 100644 --- a/test/helpers/AATestScaffolding.sol +++ b/test/helpers/AATestScaffolding.sol @@ -29,21 +29,26 @@ abstract contract AATestScaffolding is KYCSignature { using SignatureChecker for address; IKintoEntryPoint _entryPoint; - KintoWalletFactory _walletFactoryI; - KintoWalletFactory _walletFactory; - KintoAppRegistry _kintoApp; + + KintoAppRegistry _kintoAppRegistry; + KintoID _implementation; KintoID _kintoIDv1; - SponsorPaymaster _paymaster; + // Wallet & Factory + KintoWalletFactory _walletFactory; KintoWallet _kintoWalletImpl; IKintoWallet _kintoWalletv1; - UUPSProxy _proxy; - UUPSProxy _proxyf; - UUPSProxy _proxys; - UUPSProxy _proxycredit; - UUPSProxy _proxyapp; + EngenCredits _engenCredits; + SponsorPaymaster _paymaster; + + // proxies + UUPSProxy _proxy; + UUPSProxy _proxyFactory; + UUPSProxy _proxyPaymaster; + UUPSProxy _proxyCredit; + UUPSProxy _proxyRegistry; function deployAAScaffolding(address _owner, uint256 _ownerPk, address _kycProvider, address _recoverer) public { // Deploy Kinto ID @@ -70,7 +75,8 @@ abstract contract AATestScaffolding is KYCSignature { deployEngenCredits(_owner); vm.prank(_owner); - // deploy WalletV1 through wallet factory and initialize it + + // deploy latest KintoWallet version through wallet factory and initialize it _kintoWalletv1 = _walletFactory.createAccount(_owner, _recoverer, 0); // Give some eth vm.deal(_owner, 1e20); @@ -102,12 +108,12 @@ abstract contract AATestScaffolding is KYCSignature { vm.startPrank(_owner); // Deploy wallet implementation - _kintoWalletImpl = new KintoWallet{salt: 0}(_entryPoint, _kintoIDv1, _kintoApp); + _kintoWalletImpl = new KintoWallet{salt: 0}(_entryPoint, _kintoIDv1, _kintoAppRegistry); - //Deploy wallet factory implementation - _walletFactoryI = new KintoWalletFactory{salt: 0}(_kintoWalletImpl); - _proxyf = new UUPSProxy{salt: 0}(address(_walletFactoryI), ""); - _walletFactory = KintoWalletFactory(address(_proxyf)); + // Deploy wallet factory implementation + KintoWalletFactory _walletFactoryI = new KintoWalletFactory{salt: 0}(_kintoWalletImpl); + _proxyFactory = new UUPSProxy{salt: 0}(address(_walletFactoryI), ""); + _walletFactory = KintoWalletFactory(address(_proxyFactory)); // Initialize wallet factory _walletFactory.initialize(_kintoIDv1); @@ -125,16 +131,16 @@ abstract contract AATestScaffolding is KYCSignature { _paymaster = new SponsorPaymaster{salt: 0}(_entryPoint); // deploy _proxy contract and point it to _implementation - _proxys = new UUPSProxy{salt: 0}(address(_paymaster), ""); + _proxyPaymaster = new UUPSProxy{salt: 0}(address(_paymaster), ""); // wrap in ABI to support easier calls - _paymaster = SponsorPaymaster(address(_proxys)); + _paymaster = SponsorPaymaster(address(_proxyPaymaster)); // initialize proxy _paymaster.initialize(_owner); // Set the registry in the paymaster - _paymaster.setAppRegistry(address(_kintoApp)); + _paymaster.setAppRegistry(address(_kintoAppRegistry)); vm.stopPrank(); } @@ -146,10 +152,10 @@ abstract contract AATestScaffolding is KYCSignature { _engenCredits = new EngenCredits{salt: 0}(); // deploy _proxy contract and point it to _implementation - _proxycredit = new UUPSProxy{salt: 0}(address(_engenCredits), ""); + _proxyCredit = new UUPSProxy{salt: 0}(address(_engenCredits), ""); // wrap in ABI to support easier calls - _engenCredits = EngenCredits(address(_proxycredit)); + _engenCredits = EngenCredits(address(_proxyCredit)); // initialize proxy _engenCredits.initialize(); @@ -161,16 +167,16 @@ abstract contract AATestScaffolding is KYCSignature { vm.startPrank(_owner); // deploy the Kinto App registry - _kintoApp = new KintoAppRegistry{salt: 0}(); + _kintoAppRegistry = new KintoAppRegistry{salt: 0}(); // deploy _proxy contract and point it to _implementation - _proxyapp = new UUPSProxy{salt: 0}(address(_kintoApp), ""); + _proxyRegistry = new UUPSProxy{salt: 0}(address(_kintoAppRegistry), ""); // wrap in ABI to support easier calls - _kintoApp = KintoAppRegistry(address(_proxyapp)); + _kintoAppRegistry = KintoAppRegistry(address(_proxyRegistry)); // initialize proxy - _kintoApp.initialize(); + _kintoAppRegistry.initialize(); vm.stopPrank(); } From 6e069408e6c313fb8e36b423b6d6978cbc637cdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Federico=20Mart=C3=ADn=20Alconada=20Verzini?= Date: Mon, 8 Jan 2024 16:45:08 -0300 Subject: [PATCH 37/43] chore: rebase --- test/helpers/AATestScaffolding.sol | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/helpers/AATestScaffolding.sol b/test/helpers/AATestScaffolding.sol index e86f59d26..2282f1ffc 100644 --- a/test/helpers/AATestScaffolding.sol +++ b/test/helpers/AATestScaffolding.sol @@ -217,11 +217,11 @@ abstract contract AATestScaffolding is KYCSignature { { vm.startPrank(_owner); uint256[] memory appLimits = new uint256[](4); - appLimits[0] = _kintoApp.RATE_LIMIT_PERIOD(); - appLimits[1] = _kintoApp.RATE_LIMIT_THRESHOLD(); - appLimits[2] = _kintoApp.GAS_LIMIT_PERIOD(); - appLimits[3] = _kintoApp.GAS_LIMIT_THRESHOLD(); - _kintoApp.registerApp( + appLimits[0] = _kintoAppRegistry.RATE_LIMIT_PERIOD(); + appLimits[1] = _kintoAppRegistry.RATE_LIMIT_THRESHOLD(); + appLimits[2] = _kintoAppRegistry.GAS_LIMIT_PERIOD(); + appLimits[3] = _kintoAppRegistry.GAS_LIMIT_THRESHOLD(); + _kintoAppRegistry.registerApp( name, parentContract, appContracts, [appLimits[0], appLimits[1], appLimits[2], appLimits[3]] ); vm.stopPrank(); From 6a2f60196dffb7975144f0e3be75157cd4220c8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Federico=20Mart=C3=ADn=20Alconada=20Verzini?= Date: Mon, 8 Jan 2024 17:58:34 -0300 Subject: [PATCH 38/43] refactor: some refactors on the tests files and also renamed setAppWhitelist to whitelistApp --- script/deploy.sol | 1 + script/migrations/04-deploy_admin_wallet.sol | 1 + script/migrations/08-upgrade_wallet_v3.sol | 2 +- script/upgrade.sol | 11 +- src/interfaces/IKintoWallet.sol | 4 +- src/wallet/KintoWallet.sol | 2 +- src/wallet/KintoWalletFactory.sol | 32 +- test/EngenCredits.t.sol | 50 +-- test/KYCViewer.t.sol | 8 +- test/KintoWallet.t.sol | 403 +++++++++---------- test/KintoWalletFactory.t.sol | 59 +-- test/SponsorPaymastExploit.t.sol | 6 +- test/SponsorPaymaster.t.sol | 8 +- test/helpers/AATestScaffolding.sol | 16 +- test/helpers/UserOp.sol | 2 +- 15 files changed, 298 insertions(+), 307 deletions(-) diff --git a/script/deploy.sol b/script/deploy.sol index bf2f7b3ac..aec1e61c2 100644 --- a/script/deploy.sol +++ b/script/deploy.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.13; import "forge-std/Script.sol"; import "../src/KintoID.sol"; +import {KintoWalletV3 as KintoWallet} from "../src/wallet/KintoWallet.sol"; import "../src/viewers/KYCViewer.sol"; import "../src/interfaces/IKintoID.sol"; import "../src/sample/Counter.sol"; diff --git a/script/migrations/04-deploy_admin_wallet.sol b/script/migrations/04-deploy_admin_wallet.sol index 6db8d93eb..b8e700bcc 100644 --- a/script/migrations/04-deploy_admin_wallet.sol +++ b/script/migrations/04-deploy_admin_wallet.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.13; import "forge-std/Script.sol"; import "../../src/wallet/KintoWalletFactory.sol"; import "../../src/KintoID.sol"; +import "../../src/wallet/KintoWallet.sol"; import {Create2Helper} from "../../test/helpers/Create2Helper.sol"; import {ArtifactsReader} from "../../test/helpers/ArtifactsReader.sol"; import {UUPSProxy} from "../../test/helpers/UUPSProxy.sol"; diff --git a/script/migrations/08-upgrade_wallet_v3.sol b/script/migrations/08-upgrade_wallet_v3.sol index d42c2cff0..65bc7fc40 100644 --- a/script/migrations/08-upgrade_wallet_v3.sol +++ b/script/migrations/08-upgrade_wallet_v3.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.13; import "forge-std/Script.sol"; import "../../src/wallet/KintoWalletFactory.sol"; -import {KintoWallet} from "../../src/wallet/KintoWallet.sol"; +import "../../src/wallet/KintoWallet.sol"; import {Create2Helper} from "../../test/helpers/Create2Helper.sol"; import {ArtifactsReader} from "../../test/helpers/ArtifactsReader.sol"; import {UUPSProxy} from "../../test/helpers/UUPSProxy.sol"; diff --git a/script/upgrade.sol b/script/upgrade.sol index f411a3815..70552e65c 100644 --- a/script/upgrade.sol +++ b/script/upgrade.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.13; import "forge-std/Script.sol"; import "../src/KintoID.sol"; +import "../src/wallet/KintoWallet.sol"; import "../src/interfaces/IKintoID.sol"; import "../src/sample/Counter.sol"; import "../src/ETHPriceIsRight.sol"; @@ -22,7 +23,7 @@ import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import "forge-std/console.sol"; -contract KintoWalletFactoryV999 is KintoWalletFactory { +contract KintoWalletFactoryUpgrade is KintoWalletFactory { constructor(KintoWallet _impl) KintoWalletFactory(_impl) {} } @@ -39,10 +40,10 @@ contract KintoWalletFactoryUpgradeScript is ArtifactsReader { uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); vm.startBroadcast(deployerPrivateKey); _oldKintoWalletFactory = KintoWalletFactory(payable(_getChainDeployment("KintoWalletFactory"))); - KintoWalletFactoryV999 _implementationV999 = - new KintoWalletFactoryV999(KintoWallet(payable(_getChainDeployment("KintoWallet-impl")))); - _oldKintoWalletFactory.upgradeTo(address(_implementationV999)); - console.log("KintoWalletFactory Upgraded to implementation", address(_implementationV999)); + KintoWalletFactoryUpgrade _newImplementation = + new KintoWalletFactoryUpgrade(KintoWallet(payable(_getChainDeployment("KintoWallet-impl")))); + _oldKintoWalletFactory.upgradeTo(address(_newImplementation)); + console.log("KintoWalletFactory Upgraded to implementation", address(_newImplementation)); vm.stopBroadcast(); } } diff --git a/src/interfaces/IKintoWallet.sol b/src/interfaces/IKintoWallet.sol index b8c4629a3..7f6b9d486 100644 --- a/src/interfaces/IKintoWallet.sol +++ b/src/interfaces/IKintoWallet.sol @@ -11,6 +11,8 @@ interface IKintoWallet { /* ============ State Change ============ */ + function initialize(address anOwner, address _recoverer) external; + function execute(address dest, uint256 value, bytes calldata func) external; function executeBatch(address[] calldata dest, uint256[] calldata values, bytes[] calldata func) external; @@ -31,7 +33,7 @@ interface IKintoWallet { function setAppKey(address app, address signer) external; - function setAppWhitelist(address[] calldata apps, bool[] calldata flags) external; + function whitelistApp(address[] calldata apps, bool[] calldata flags) external; /* ============ Basic Viewers ============ */ diff --git a/src/wallet/KintoWallet.sol b/src/wallet/KintoWallet.sol index ef08e2bf8..514b231b9 100644 --- a/src/wallet/KintoWallet.sol +++ b/src/wallet/KintoWallet.sol @@ -178,7 +178,7 @@ contract KintoWallet is Initializable, BaseAccount, TokenCallbackHandler, IKinto * @param apps apps array * @param flags whether to allow or disallow the app */ - function setAppWhitelist(address[] calldata apps, bool[] calldata flags) external override onlySelf { + function whitelistApp(address[] calldata apps, bool[] calldata flags) external override onlySelf { require(apps.length == flags.length, "KW-apw: invalid array"); for (uint256 i = 0; i < apps.length; i++) { require(appRegistry.getAppMetadata(apps[i]).admin != address(0), "KW-apw: app must be registered"); diff --git a/src/wallet/KintoWalletFactory.sol b/src/wallet/KintoWalletFactory.sol index 568f50c5d..f7650dc3c 100644 --- a/src/wallet/KintoWalletFactory.sol +++ b/src/wallet/KintoWalletFactory.sol @@ -10,7 +10,7 @@ import {SafeBeaconProxy} from "../proxy/SafeBeaconProxy.sol"; import "../interfaces/IKintoID.sol"; import "../interfaces/IKintoWalletFactory.sol"; -import "./KintoWallet.sol"; +import "../interfaces/IKintoWallet.sol"; /** * @title KintoWalletFactory @@ -25,7 +25,7 @@ import "./KintoWallet.sol"; contract KintoWalletFactory is Initializable, UUPSUpgradeable, OwnableUpgradeable, IKintoWalletFactory { /* ============ State Variables ============ */ UpgradeableBeacon public beacon; - KintoWallet private immutable _implAddress; + IKintoWallet private immutable _implAddress; IKintoID public override kintoID; mapping(address => uint256) public override walletTs; uint256 public override factoryWalletVersion; @@ -36,7 +36,7 @@ contract KintoWalletFactory is Initializable, UUPSUpgradeable, OwnableUpgradeabl event KintoWalletFactoryUpgraded(address indexed oldImplementation, address indexed newImplementation); /* ============ Constructor & Upgrades ============ */ - constructor(KintoWallet _implAddressP) { + constructor(IKintoWallet _implAddressP) { _disableInitializers(); _implAddress = _implAddressP; } @@ -101,13 +101,13 @@ contract KintoWalletFactory is Initializable, UUPSUpgradeable, OwnableUpgradeabl uint256 codeSize = addr.code.length; if (codeSize > 0) { - return KintoWallet(payable(addr)); + return IKintoWallet(payable(addr)); } - ret = KintoWallet( + ret = IKintoWallet( payable( new SafeBeaconProxy{salt: bytes32(salt)}( - address(beacon), abi.encodeCall(KintoWallet.initialize, (owner, recoverer)) + address(beacon), abi.encodeCall(IKintoWallet.initialize, (owner, recoverer)) ) ) ); @@ -126,8 +126,8 @@ contract KintoWalletFactory is Initializable, UUPSUpgradeable, OwnableUpgradeabl */ function startWalletRecovery(address payable wallet) external override { require(walletTs[wallet] > 0, "invalid wallet"); - require(msg.sender == KintoWallet(wallet).recoverer(), "only recoverer"); - KintoWallet(wallet).startRecovery(); + require(msg.sender == IKintoWallet(wallet).recoverer(), "only recoverer"); + IKintoWallet(wallet).startRecovery(); } /** @@ -137,14 +137,14 @@ contract KintoWalletFactory is Initializable, UUPSUpgradeable, OwnableUpgradeabl */ function completeWalletRecovery(address payable wallet, address[] calldata newSigners) external override { require(walletTs[wallet] > 0, "invalid wallet"); - require(msg.sender == KintoWallet(wallet).recoverer(), "only recoverer"); - KintoWallet(wallet).finishRecovery(newSigners); + require(msg.sender == IKintoWallet(wallet).recoverer(), "only recoverer"); + IKintoWallet(wallet).finishRecovery(newSigners); } function changeWalletRecoverer(address payable wallet, address _newRecoverer) external override { require(walletTs[wallet] > 0, "invalid wallet"); - require(msg.sender == KintoWallet(wallet).recoverer(), "only recoverer"); - KintoWallet(wallet).changeRecoverer(_newRecoverer); + require(msg.sender == IKintoWallet(wallet).recoverer(), "only recoverer"); + IKintoWallet(wallet).changeRecoverer(_newRecoverer); } /* ============ Deploy Custom Contract ============ */ @@ -183,8 +183,8 @@ contract KintoWalletFactory is Initializable, UUPSUpgradeable, OwnableUpgradeabl */ function fundWallet(address payable wallet) external payable override { require( - msg.value > 0 && walletTs[wallet] > 0 && kintoID.isKYC(KintoWallet(wallet).owners(0)) - && KintoWallet(payable(wallet)).isFunderWhitelisted(msg.sender), + msg.value > 0 && walletTs[wallet] > 0 && kintoID.isKYC(IKintoWallet(wallet).owners(0)) + && IKintoWallet(payable(wallet)).isFunderWhitelisted(msg.sender), "Invalid wallet or funder" ); wallet.transfer(msg.value); @@ -215,7 +215,7 @@ contract KintoWalletFactory is Initializable, UUPSUpgradeable, OwnableUpgradeabl keccak256( abi.encodePacked( type(SafeBeaconProxy).creationCode, - abi.encode(address(beacon), abi.encodeCall(KintoWallet.initialize, (owner, recoverer))) + abi.encode(address(beacon), abi.encodeCall(IKintoWallet.initialize, (owner, recoverer))) ) ) ); @@ -268,5 +268,5 @@ contract KintoWalletFactory is Initializable, UUPSUpgradeable, OwnableUpgradeabl } contract KintoWalletFactoryV2 is KintoWalletFactory { - constructor(KintoWallet _implAddressP) KintoWalletFactory(_implAddressP) {} + constructor(IKintoWallet _implAddressP) KintoWalletFactory(_implAddressP) {} } diff --git a/test/EngenCredits.t.sol b/test/EngenCredits.t.sol index ee14254ca..8ae0cc737 100644 --- a/test/EngenCredits.t.sol +++ b/test/EngenCredits.t.sol @@ -30,7 +30,7 @@ contract EngenCreditsTest is Create2Helper, UserOp, AATestScaffolding { vm.stopPrank(); deployAAScaffolding(_owner, 1, _kycProvider, _recoverer); _fundPaymasterForContract(address(_engenCredits)); - _fundPaymasterForContract(address(_kintoWalletv1)); + _fundPaymasterForContract(address(_kintoWallet)); vm.startPrank(_owner); _kintoAppRegistry.registerApp( "engen credits", address(_engenCredits), new address[](0), [uint256(0), uint256(0), uint256(0), uint256(0)] @@ -118,16 +118,16 @@ contract EngenCreditsTest is Create2Helper, UserOp, AATestScaffolding { /* ============ Engen Tests ============ */ function testWalletCanGetPoints() public { - assertEq(_engenCredits.balanceOf(address(_kintoWalletv1)), 0); - assertEq(_engenCredits.calculatePoints(address(_kintoWalletv1)), 15); + assertEq(_engenCredits.balanceOf(address(_kintoWallet)), 0); + assertEq(_engenCredits.calculatePoints(address(_kintoWallet)), 15); vm.startPrank(_owner); // Let's send a transaction to the counter contract through our wallet - uint256 startingNonce = _kintoWalletv1.getNonce(); + uint256 startingNonce = _kintoWallet.getNonce(); uint256[] memory privateKeys = new uint256[](1); privateKeys[0] = 1; UserOperation memory userOp = this.createUserOperationWithPaymaster( _chainID, - address(_kintoWalletv1), + address(_kintoWallet), startingNonce + 1, privateKeys, address(_engenCredits), @@ -139,15 +139,15 @@ contract EngenCreditsTest is Create2Helper, UserOp, AATestScaffolding { userOps[0] = createWhitelistAppOp( _chainID, privateKeys, - address(_kintoWalletv1), - _kintoWalletv1.getNonce(), + address(_kintoWallet), + _kintoWallet.getNonce(), address(_engenCredits), address(_paymaster) ); userOps[1] = userOp; // Execute the transaction via the entry point _entryPoint.handleOps(userOps, payable(_owner)); - assertEq(_engenCredits.balanceOf(address(_kintoWalletv1)), 15); + assertEq(_engenCredits.balanceOf(address(_kintoWallet)), 15); vm.stopPrank(); } @@ -156,17 +156,17 @@ contract EngenCreditsTest is Create2Helper, UserOp, AATestScaffolding { uint256[] memory points = new uint256[](1); points[0] = 10; address[] memory addresses = new address[](1); - addresses[0] = address(_kintoWalletv1); + addresses[0] = address(_kintoWallet); _engenCredits.setPhase1Override(addresses, points); - assertEq(_engenCredits.balanceOf(address(_kintoWalletv1)), 0); - assertEq(_engenCredits.calculatePoints(address(_kintoWalletv1)), 20); + assertEq(_engenCredits.balanceOf(address(_kintoWallet)), 0); + assertEq(_engenCredits.calculatePoints(address(_kintoWallet)), 20); // Let's send a transaction to the counter contract through our wallet - uint256 startingNonce = _kintoWalletv1.getNonce(); + uint256 startingNonce = _kintoWallet.getNonce(); uint256[] memory privateKeys = new uint256[](1); privateKeys[0] = 1; UserOperation memory userOp = this.createUserOperationWithPaymaster( _chainID, - address(_kintoWalletv1), + address(_kintoWallet), startingNonce + 1, privateKeys, address(_engenCredits), @@ -178,29 +178,29 @@ contract EngenCreditsTest is Create2Helper, UserOp, AATestScaffolding { userOps[0] = createWhitelistAppOp( _chainID, privateKeys, - address(_kintoWalletv1), - _kintoWalletv1.getNonce(), + address(_kintoWallet), + _kintoWallet.getNonce(), address(_engenCredits), address(_paymaster) ); userOps[1] = userOp; // Execute the transaction via the entry point _entryPoint.handleOps(userOps, payable(_owner)); - assertEq(_engenCredits.balanceOf(address(_kintoWalletv1)), 20); + assertEq(_engenCredits.balanceOf(address(_kintoWallet)), 20); vm.stopPrank(); } function testWalletCannotGetPointsTwice() public { - assertEq(_engenCredits.balanceOf(address(_kintoWalletv1)), 0); - assertEq(_engenCredits.calculatePoints(address(_kintoWalletv1)), 15); + assertEq(_engenCredits.balanceOf(address(_kintoWallet)), 0); + assertEq(_engenCredits.calculatePoints(address(_kintoWallet)), 15); vm.startPrank(_owner); // Let's send a transaction to the counter contract through our wallet - uint256 startingNonce = _kintoWalletv1.getNonce(); + uint256 startingNonce = _kintoWallet.getNonce(); uint256[] memory privateKeys = new uint256[](1); privateKeys[0] = 1; UserOperation memory userOp = this.createUserOperationWithPaymaster( _chainID, - address(_kintoWalletv1), + address(_kintoWallet), startingNonce + 1, privateKeys, address(_engenCredits), @@ -212,19 +212,19 @@ contract EngenCreditsTest is Create2Helper, UserOp, AATestScaffolding { userOps[0] = createWhitelistAppOp( _chainID, privateKeys, - address(_kintoWalletv1), - _kintoWalletv1.getNonce(), + address(_kintoWallet), + _kintoWallet.getNonce(), address(_engenCredits), address(_paymaster) ); userOps[1] = userOp; // Execute the transaction via the entry point _entryPoint.handleOps(userOps, payable(_owner)); - assertEq(_engenCredits.balanceOf(address(_kintoWalletv1)), 15); + assertEq(_engenCredits.balanceOf(address(_kintoWallet)), 15); // call again userOp = this.createUserOperationWithPaymaster( _chainID, - address(_kintoWalletv1), + address(_kintoWallet), startingNonce + 2, privateKeys, address(_engenCredits), @@ -235,7 +235,7 @@ contract EngenCreditsTest is Create2Helper, UserOp, AATestScaffolding { userOps = new UserOperation[](1); userOps[0] = userOp; _entryPoint.handleOps(userOps, payable(_owner)); - assertEq(_engenCredits.balanceOf(address(_kintoWalletv1)), 15); + assertEq(_engenCredits.balanceOf(address(_kintoWallet)), 15); vm.stopPrank(); } } diff --git a/test/KYCViewer.t.sol b/test/KYCViewer.t.sol index 622675eee..cc36e5269 100644 --- a/test/KYCViewer.t.sol +++ b/test/KYCViewer.t.sol @@ -91,9 +91,9 @@ contract KYCViewerTest is Create2Helper, UserOp, AATestScaffolding { /* ============ Viewer Tests ============ */ function testIsKYCBothOwnerAndWallet() public { - assertEq(_kycViewer.isKYC(address(_kintoWalletv1)), _kycViewer.isKYC(_owner)); - assertEq(_kycViewer.isIndividual(address(_kintoWalletv1)), _kycViewer.isIndividual(_owner)); - assertEq(_kycViewer.isCompany(address(_kintoWalletv1)), false); - assertEq(_kycViewer.hasTrait(address(_kintoWalletv1), 6), false); + assertEq(_kycViewer.isKYC(address(_kintoWallet)), _kycViewer.isKYC(_owner)); + assertEq(_kycViewer.isIndividual(address(_kintoWallet)), _kycViewer.isIndividual(_owner)); + assertEq(_kycViewer.isCompany(address(_kintoWallet)), false); + assertEq(_kycViewer.hasTrait(address(_kintoWallet), 6), false); } } diff --git a/test/KintoWallet.t.sol b/test/KintoWallet.t.sol index 9c64c6300..364122c93 100644 --- a/test/KintoWallet.t.sol +++ b/test/KintoWallet.t.sol @@ -33,8 +33,8 @@ contract KintoWalletTest is AATestScaffolding, UserOp { deployAAScaffolding(_owner, 1, _kycProvider, _recoverer); - // Add paymaster to _kintoWalletv1 - _fundPaymasterForContract(address(_kintoWalletv1)); + // Add paymaster to _kintoWallet + _fundPaymasterForContract(address(_kintoWallet)); // Default tests to use 1 private key for simplicity privateKeys = new uint256[](1); @@ -44,7 +44,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { } function testUp() public { - assertEq(_kintoWalletv1.owners(0), _owner); + assertEq(_kintoWallet.owners(0), _owner); assertEq(_entryPoint.walletFactory(), address(_walletFactory)); } @@ -54,15 +54,15 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // deploy a new implementation KintoWallet _newImplementation = new KintoWallet(_entryPoint, _kintoIDv1, _kintoAppRegistry); - uint256 nonce = _kintoWalletv1.getNonce(); + uint256 nonce = _kintoWallet.getNonce(); // try calling upgradeTo from _owner wallet to upgrade _owner wallet UserOperation memory userOp = this.createUserOperationWithPaymaster( _chainID, - address(_kintoWalletv1), + address(_kintoWallet), nonce, privateKeys, - address(_kintoWalletv1), + address(_kintoWallet), 0, abi.encodeWithSignature("upgradeTo(address)", address(_newImplementation)), address(_paymaster) @@ -96,7 +96,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { address(userWallet), nonce, privateKeys, - address(_kintoWalletv1), + address(_kintoWallet), 0, abi.encodeWithSignature("upgradeTo(address)", address(_newImplementation)), address(_paymaster) @@ -130,8 +130,8 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // without a paymaster and without prefunding the wallet UserOperation memory userOp = this.createUserOperation( _chainID, - address(_kintoWalletv1), - _kintoWalletv1.getNonce(), + address(_kintoWallet), + _kintoWallet.getNonce(), privateKeys, address(counter), 0, @@ -152,26 +152,21 @@ contract KintoWalletTest is AATestScaffolding, UserOp { address[] memory childContracts = new address[](0); createApp(_owner, "test", address(counter), childContracts); // prefund wallet - vm.deal(address(_kintoWalletv1), 1 ether); + vm.deal(address(_kintoWallet), 1 ether); UserOperation[] memory userOps = new UserOperation[](2); // whitelist app userOps[0] = createWhitelistAppOp( - _chainID, - privateKeys, - address(_kintoWalletv1), - _kintoWalletv1.getNonce(), - address(counter), - address(_paymaster) + _chainID, privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(counter), address(_paymaster) ); // send a transaction to the counter contract through our wallet // without a paymaster but prefunding the wallet userOps[1] = this.createUserOperation( _chainID, - address(_kintoWalletv1), - _kintoWalletv1.getNonce() + 1, + address(_kintoWallet), + _kintoWallet.getNonce() + 1, privateKeys, address(counter), 0, @@ -193,8 +188,8 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // send a transaction to the counter contract through our wallet UserOperation memory userOp = this.createUserOperationWithPaymaster( _chainID, - address(_kintoWalletv1), - _kintoWalletv1.getNonce(), + address(_kintoWallet), + _kintoWallet.getNonce(), privateKeys, address(counter), 0, @@ -226,12 +221,12 @@ contract KintoWalletTest is AATestScaffolding, UserOp { createApp(_owner, "test", address(counter), childContracts); vm.startPrank(_owner); // Let's send a transaction to the counter contract through our wallet - uint256 nonce = _kintoWalletv1.getNonce(); + uint256 nonce = _kintoWallet.getNonce(); bool[] memory flags = new bool[](1); flags[0] = true; UserOperation memory userOp2 = this.createUserOperationWithPaymaster( _chainID, - address(_kintoWalletv1), + address(_kintoWallet), nonce + 1, privateKeys, address(counter), @@ -241,12 +236,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { ); UserOperation[] memory userOps = new UserOperation[](2); userOps[0] = createWhitelistAppOp( - _chainID, - privateKeys, - address(_kintoWalletv1), - _kintoWalletv1.getNonce(), - address(counter), - address(_paymaster) + _chainID, privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(counter), address(_paymaster) ); userOps[1] = userOp2; // Execute the transactions via the entry point @@ -260,7 +250,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // Let's deploy the counter contract Counter counter = new Counter(); assertEq(counter.count(), 0); - uint256 nonce = _kintoWalletv1.getNonce(); + uint256 nonce = _kintoWallet.getNonce(); vm.stopPrank(); address[] memory childContracts = new address[](0); createApp(_owner, "test", address(counter), childContracts); @@ -269,7 +259,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // Let's send a transaction to the counter contract through our wallet UserOperation memory userOp = this.createUserOperationWithPaymaster( _chainID, - address(_kintoWalletv1), + address(_kintoWallet), nonce + 1, privateKeys, address(counter), @@ -279,7 +269,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { ); UserOperation memory userOp2 = this.createUserOperationWithPaymaster( _chainID, - address(_kintoWalletv1), + address(_kintoWallet), nonce + 2, privateKeys, address(counter), @@ -289,7 +279,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { ); UserOperation[] memory userOps = new UserOperation[](3); userOps[0] = createWhitelistAppOp( - _chainID, privateKeys, address(_kintoWalletv1), nonce, address(counter), address(_paymaster) + _chainID, privateKeys, address(_kintoWallet), nonce, address(counter), address(_paymaster) ); userOps[1] = userOp; userOps[2] = userOp2; @@ -304,14 +294,14 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // Let's deploy the counter contract Counter counter = new Counter(); assertEq(counter.count(), 0); - uint256 nonce = _kintoWalletv1.getNonce(); + uint256 nonce = _kintoWallet.getNonce(); vm.stopPrank(); address[] memory childContracts = new address[](0); createApp(_owner, "test", address(counter), childContracts); vm.startPrank(_owner); _fundPaymasterForContract(address(counter)); address[] memory targets = new address[](3); - targets[0] = address(_kintoWalletv1); + targets[0] = address(_kintoWallet); targets[1] = address(counter); targets[2] = address(counter); uint256[] memory values = new uint256[](3); @@ -323,13 +313,13 @@ contract KintoWalletTest is AATestScaffolding, UserOp { apps[0] = address(counter); bool[] memory flags = new bool[](1); flags[0] = true; - calls[0] = abi.encodeWithSignature("setAppWhitelist(address[],bool[])", apps, flags); + calls[0] = abi.encodeWithSignature("whitelistApp(address[],bool[])", apps, flags); calls[1] = abi.encodeWithSignature("increment()"); calls[2] = abi.encodeWithSignature("increment()"); OperationParams memory opParams = OperationParams({targetContracts: targets, values: values, bytesOps: calls}); UserOperation memory userOp = this.createUserOperationBatchWithPaymaster( - _chainID, address(_kintoWalletv1), nonce, privateKeys, opParams, address(_paymaster) + _chainID, address(_kintoWallet), nonce, privateKeys, opParams, address(_paymaster) ); UserOperation[] memory userOps = new UserOperation[](1); userOps[0] = userOp; @@ -354,7 +344,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // prep batch address[] memory targets = new address[](3); - targets[0] = address(_kintoWalletv1); + targets[0] = address(_kintoWallet); targets[1] = address(counter); targets[2] = address(counter2); @@ -369,16 +359,16 @@ contract KintoWalletTest is AATestScaffolding, UserOp { bool[] memory flags = new bool[](1); flags[0] = true; - // we want to do 3 calls: setAppWhitelist, increment counter and increment counter2 + // we want to do 3 calls: whitelistApp, increment counter and increment counter2 bytes[] memory calls = new bytes[](3); - calls[0] = abi.encodeWithSignature("setAppWhitelist(address[],bool[])", apps, flags); + calls[0] = abi.encodeWithSignature("whitelistApp(address[],bool[])", apps, flags); calls[1] = abi.encodeWithSignature("increment()"); calls[2] = abi.encodeWithSignature("increment()"); // send all transactions via batch OperationParams memory opParams = OperationParams({targetContracts: targets, values: values, bytesOps: calls}); UserOperation memory userOp = this.createUserOperationBatchWithPaymaster( - _chainID, address(_kintoWalletv1), _kintoWalletv1.getNonce(), privateKeys, opParams, address(_paymaster) + _chainID, address(_kintoWallet), _kintoWallet.getNonce(), privateKeys, opParams, address(_paymaster) ); UserOperation[] memory userOps = new UserOperation[](1); userOps[0] = userOp; @@ -411,22 +401,22 @@ contract KintoWalletTest is AATestScaffolding, UserOp { address[] memory owners = new address[](2); owners[0] = _owner; owners[1] = _user; - uint256 nonce = _kintoWalletv1.getNonce(); + uint256 nonce = _kintoWallet.getNonce(); UserOperation memory userOp = this.createUserOperationWithPaymaster( _chainID, - address(_kintoWalletv1), + address(_kintoWallet), nonce, privateKeys, - address(_kintoWalletv1), + address(_kintoWallet), 0, - abi.encodeWithSignature("resetSigners(address[],uint8)", owners, _kintoWalletv1.signerPolicy()), + abi.encodeWithSignature("resetSigners(address[],uint8)", owners, _kintoWallet.signerPolicy()), address(_paymaster) ); UserOperation[] memory userOps = new UserOperation[](1); userOps[0] = userOp; // Execute the transaction via the entry point _entryPoint.handleOps(userOps, payable(_owner)); - assertEq(_kintoWalletv1.owners(1), _user); + assertEq(_kintoWallet.owners(1), _user); vm.stopPrank(); } @@ -437,12 +427,12 @@ contract KintoWalletTest is AATestScaffolding, UserOp { UserOperation memory userOp = this.createUserOperationWithPaymaster( _chainID, - address(_kintoWalletv1), - _kintoWalletv1.getNonce(), + address(_kintoWallet), + _kintoWallet.getNonce(), privateKeys, - address(_kintoWalletv1), + address(_kintoWallet), 0, - abi.encodeWithSignature("resetSigners(address[],uint8)", owners, _kintoWalletv1.signerPolicy()), + abi.encodeWithSignature("resetSigners(address[],uint8)", owners, _kintoWallet.signerPolicy()), address(_paymaster) ); UserOperation[] memory userOps = new UserOperation[](1); @@ -464,12 +454,12 @@ contract KintoWalletTest is AATestScaffolding, UserOp { UserOperation memory userOp = this.createUserOperationWithPaymaster( _chainID, - address(_kintoWalletv1), - _kintoWalletv1.getNonce(), + address(_kintoWallet), + _kintoWallet.getNonce(), privateKeys, - address(_kintoWalletv1), + address(_kintoWallet), 0, - abi.encodeWithSignature("resetSigners(address[],uint8)", owners, _kintoWalletv1.signerPolicy()), + abi.encodeWithSignature("resetSigners(address[],uint8)", owners, _kintoWallet.signerPolicy()), address(_paymaster) ); UserOperation[] memory userOps = new UserOperation[](1); @@ -495,12 +485,12 @@ contract KintoWalletTest is AATestScaffolding, UserOp { UserOperation memory userOp = this.createUserOperationWithPaymaster( _chainID, - address(_kintoWalletv1), - _kintoWalletv1.getNonce(), + address(_kintoWallet), + _kintoWallet.getNonce(), privateKeys, - address(_kintoWalletv1), + address(_kintoWallet), 0, - abi.encodeWithSignature("resetSigners(address[],uint8)", owners, _kintoWalletv1.signerPolicy()), + abi.encodeWithSignature("resetSigners(address[],uint8)", owners, _kintoWallet.signerPolicy()), address(_paymaster) ); UserOperation[] memory userOps = new UserOperation[](1); @@ -523,12 +513,12 @@ contract KintoWalletTest is AATestScaffolding, UserOp { UserOperation memory userOp = this.createUserOperationWithPaymaster( _chainID, - address(_kintoWalletv1), - _kintoWalletv1.getNonce(), + address(_kintoWallet), + _kintoWallet.getNonce(), privateKeys, - address(_kintoWalletv1), + address(_kintoWallet), 0, - abi.encodeWithSignature("resetSigners(address[],uint8)", owners, _kintoWalletv1.signerPolicy()), + abi.encodeWithSignature("resetSigners(address[],uint8)", owners, _kintoWallet.signerPolicy()), address(_paymaster) ); UserOperation[] memory userOps = new UserOperation[](1); @@ -543,23 +533,23 @@ contract KintoWalletTest is AATestScaffolding, UserOp { address[] memory owners = new address[](2); owners[0] = _owner; owners[1] = _user; - uint256 nonce = _kintoWalletv1.getNonce(); + uint256 nonce = _kintoWallet.getNonce(); UserOperation memory userOp = this.createUserOperationWithPaymaster( _chainID, - address(_kintoWalletv1), + address(_kintoWallet), nonce, privateKeys, - address(_kintoWalletv1), + address(_kintoWallet), 0, - abi.encodeWithSignature("resetSigners(address[],uint8)", owners, _kintoWalletv1.ALL_SIGNERS()), + abi.encodeWithSignature("resetSigners(address[],uint8)", owners, _kintoWallet.ALL_SIGNERS()), address(_paymaster) ); UserOperation[] memory userOps = new UserOperation[](1); userOps[0] = userOp; // Execute the transaction via the entry point _entryPoint.handleOps(userOps, payable(_owner)); - assertEq(_kintoWalletv1.owners(1), _user); - assertEq(_kintoWalletv1.signerPolicy(), _kintoWalletv1.ALL_SIGNERS()); + assertEq(_kintoWallet.owners(1), _user); + assertEq(_kintoWallet.signerPolicy(), _kintoWallet.ALL_SIGNERS()); vm.stopPrank(); } @@ -569,24 +559,24 @@ contract KintoWalletTest is AATestScaffolding, UserOp { owners[0] = _owner; owners[1] = _user; owners[2] = _user2; - uint256 nonce = _kintoWalletv1.getNonce(); + uint256 nonce = _kintoWallet.getNonce(); UserOperation memory userOp = this.createUserOperationWithPaymaster( _chainID, - address(_kintoWalletv1), + address(_kintoWallet), nonce, privateKeys, - address(_kintoWalletv1), + address(_kintoWallet), 0, - abi.encodeWithSignature("resetSigners(address[],uint8)", owners, _kintoWalletv1.MINUS_ONE_SIGNER()), + abi.encodeWithSignature("resetSigners(address[],uint8)", owners, _kintoWallet.MINUS_ONE_SIGNER()), address(_paymaster) ); UserOperation[] memory userOps = new UserOperation[](1); userOps[0] = userOp; // Execute the transaction via the entry point _entryPoint.handleOps(userOps, payable(_owner)); - assertEq(_kintoWalletv1.owners(1), _user); - assertEq(_kintoWalletv1.owners(2), _user2); - assertEq(_kintoWalletv1.signerPolicy(), _kintoWalletv1.MINUS_ONE_SIGNER()); + assertEq(_kintoWallet.owners(1), _user); + assertEq(_kintoWallet.owners(2), _user2); + assertEq(_kintoWallet.signerPolicy(), _kintoWallet.MINUS_ONE_SIGNER()); vm.stopPrank(); } @@ -595,31 +585,31 @@ contract KintoWalletTest is AATestScaffolding, UserOp { owners[0] = _owner; owners[1] = _user; - uint256 nonce = _kintoWalletv1.getNonce(); + uint256 nonce = _kintoWallet.getNonce(); // call setSignerPolicy with ALL_SIGNERS policy should revert because the wallet has 1 owners // and the policy requires 3 owners. UserOperation[] memory userOps = new UserOperation[](2); userOps[0] = this.createUserOperationWithPaymaster( _chainID, - address(_kintoWalletv1), + address(_kintoWallet), nonce, privateKeys, - address(_kintoWalletv1), + address(_kintoWallet), 0, - abi.encodeWithSignature("setSignerPolicy(uint8)", _kintoWalletv1.ALL_SIGNERS()), + abi.encodeWithSignature("setSignerPolicy(uint8)", _kintoWallet.ALL_SIGNERS()), address(_paymaster) ); // call resetSigners with existing policy (SINGLE_SIGNER) should revert because I'm passing 2 owners userOps[1] = this.createUserOperationWithPaymaster( _chainID, - address(_kintoWalletv1), + address(_kintoWallet), nonce + 1, privateKeys, - address(_kintoWalletv1), + address(_kintoWallet), 0, - abi.encodeWithSignature("resetSigners(address[], uint8)", owners, _kintoWalletv1.signerPolicy()), + abi.encodeWithSignature("resetSigners(address[], uint8)", owners, _kintoWallet.signerPolicy()), address(_paymaster) ); @@ -644,8 +634,8 @@ contract KintoWalletTest is AATestScaffolding, UserOp { reasons[1] = "Address: low-level call with value failed"; assertRevertReasonEq(reasons); - assertEq(_kintoWalletv1.owners(0), _owner); - assertEq(_kintoWalletv1.signerPolicy(), _kintoWalletv1.SINGLE_SIGNER()); + assertEq(_kintoWallet.owners(0), _owner); + assertEq(_kintoWallet.signerPolicy(), _kintoWallet.SINGLE_SIGNER()); } /* ============ Multisig Transactions ============ */ @@ -658,12 +648,12 @@ contract KintoWalletTest is AATestScaffolding, UserOp { UserOperation memory userOp = this.createUserOperationWithPaymaster( _chainID, - address(_kintoWalletv1), - _kintoWalletv1.getNonce(), + address(_kintoWallet), + _kintoWallet.getNonce(), privateKeys, - address(_kintoWalletv1), + address(_kintoWallet), 0, - abi.encodeWithSignature("resetSigners(address[],uint8)", owners, _kintoWalletv1.ALL_SIGNERS()), + abi.encodeWithSignature("resetSigners(address[],uint8)", owners, _kintoWallet.ALL_SIGNERS()), address(_paymaster) ); UserOperation[] memory userOps = new UserOperation[](1); @@ -671,11 +661,11 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // (2). execute the transaction via the entry point vm.expectEmit(); - emit WalletPolicyChanged(_kintoWalletv1.ALL_SIGNERS(), _kintoWalletv1.SINGLE_SIGNER()); + emit WalletPolicyChanged(_kintoWallet.ALL_SIGNERS(), _kintoWallet.SINGLE_SIGNER()); _entryPoint.handleOps(userOps, payable(_owner)); - assertEq(_kintoWalletv1.owners(1), _user); - assertEq(_kintoWalletv1.signerPolicy(), _kintoWalletv1.ALL_SIGNERS()); + assertEq(_kintoWallet.owners(1), _user); + assertEq(_kintoWallet.signerPolicy(), _kintoWallet.ALL_SIGNERS()); // (3). deploy Counter contract Counter counter = new Counter(); @@ -695,19 +685,14 @@ contract KintoWalletTest is AATestScaffolding, UserOp { userOps = new UserOperation[](2); // a. Approval UserOp userOps[0] = createWhitelistAppOp( - _chainID, - privateKeys, - address(_kintoWalletv1), - _kintoWalletv1.getNonce(), - address(counter), - address(_paymaster) + _chainID, privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(counter), address(_paymaster) ); // b. Counter increment userOps[1] = this.createUserOperationWithPaymaster( _chainID, - address(_kintoWalletv1), - _kintoWalletv1.getNonce() + 1, + address(_kintoWallet), + _kintoWallet.getNonce() + 1, privateKeys, address(counter), 0, @@ -728,12 +713,12 @@ contract KintoWalletTest is AATestScaffolding, UserOp { UserOperation memory userOp = this.createUserOperationWithPaymaster( _chainID, - address(_kintoWalletv1), - _kintoWalletv1.getNonce(), + address(_kintoWallet), + _kintoWallet.getNonce(), privateKeys, - address(_kintoWalletv1), + address(_kintoWallet), 0, - abi.encodeWithSignature("resetSigners(address[],uint8)", owners, _kintoWalletv1.ALL_SIGNERS()), + abi.encodeWithSignature("resetSigners(address[],uint8)", owners, _kintoWallet.ALL_SIGNERS()), address(_paymaster) ); UserOperation[] memory userOps = new UserOperation[](1); @@ -741,10 +726,10 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // (2). execute the transaction via the entry point vm.expectEmit(); - emit WalletPolicyChanged(_kintoWalletv1.ALL_SIGNERS(), _kintoWalletv1.SINGLE_SIGNER()); + emit WalletPolicyChanged(_kintoWallet.ALL_SIGNERS(), _kintoWallet.SINGLE_SIGNER()); _entryPoint.handleOps(userOps, payable(_owner)); - assertEq(_kintoWalletv1.owners(1), _user); - assertEq(_kintoWalletv1.signerPolicy(), _kintoWalletv1.ALL_SIGNERS()); + assertEq(_kintoWallet.owners(1), _user); + assertEq(_kintoWallet.signerPolicy(), _kintoWallet.ALL_SIGNERS()); // (3). deploy Counter contract Counter counter = new Counter(); @@ -760,19 +745,14 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // a. Approval UserOp userOps[0] = createWhitelistAppOp( - _chainID, - privateKeys, - address(_kintoWalletv1), - _kintoWalletv1.getNonce(), - address(counter), - address(_paymaster) + _chainID, privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(counter), address(_paymaster) ); // b. Counter increment userOps[1] = this.createUserOperationWithPaymaster( _chainID, - address(_kintoWalletv1), - _kintoWalletv1.getNonce() + 1, + address(_kintoWallet), + _kintoWallet.getNonce() + 1, privateKeys, address(counter), 0, @@ -794,12 +774,12 @@ contract KintoWalletTest is AATestScaffolding, UserOp { UserOperation memory userOp = this.createUserOperationWithPaymaster( _chainID, - address(_kintoWalletv1), - _kintoWalletv1.getNonce(), + address(_kintoWallet), + _kintoWallet.getNonce(), privateKeys, - address(_kintoWalletv1), + address(_kintoWallet), 0, - abi.encodeWithSignature("resetSigners(address[],uint8)", owners, _kintoWalletv1.ALL_SIGNERS()), + abi.encodeWithSignature("resetSigners(address[],uint8)", owners, _kintoWallet.ALL_SIGNERS()), address(_paymaster) ); UserOperation[] memory userOps = new UserOperation[](1); @@ -807,10 +787,10 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // (2). execute the transaction via the entry point vm.expectEmit(); - emit WalletPolicyChanged(_kintoWalletv1.ALL_SIGNERS(), _kintoWalletv1.SINGLE_SIGNER()); + emit WalletPolicyChanged(_kintoWallet.ALL_SIGNERS(), _kintoWallet.SINGLE_SIGNER()); _entryPoint.handleOps(userOps, payable(_owner)); - assertEq(_kintoWalletv1.owners(1), _user); - assertEq(_kintoWalletv1.signerPolicy(), _kintoWalletv1.ALL_SIGNERS()); + assertEq(_kintoWallet.owners(1), _user); + assertEq(_kintoWallet.signerPolicy(), _kintoWallet.ALL_SIGNERS()); // (3). deploy Counter contract Counter counter = new Counter(); @@ -832,18 +812,13 @@ contract KintoWalletTest is AATestScaffolding, UserOp { userOps = new UserOperation[](2); // a. Approval UserOp userOps[0] = createWhitelistAppOp( - _chainID, - privateKeys, - address(_kintoWalletv1), - _kintoWalletv1.getNonce(), - address(counter), - address(_paymaster) + _chainID, privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(counter), address(_paymaster) ); // b. Counter increment userOps[1] = this.createUserOperationWithPaymaster( _chainID, - address(_kintoWalletv1), - _kintoWalletv1.getNonce() + 1, + address(_kintoWallet), + _kintoWallet.getNonce() + 1, privateKeys, address(counter), 0, @@ -871,19 +846,14 @@ contract KintoWalletTest is AATestScaffolding, UserOp { userOps = new UserOperation[](2); // a. Approval UserOp userOps[0] = createWhitelistAppOp( - _chainID, - privateKeys, - address(_kintoWalletv1), - _kintoWalletv1.getNonce(), - address(counter), - address(_paymaster) + _chainID, privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(counter), address(_paymaster) ); // b. Counter increment userOps[1] = this.createUserOperationWithPaymaster( _chainID, - address(_kintoWalletv1), - _kintoWalletv1.getNonce() + 1, + address(_kintoWallet), + _kintoWallet.getNonce() + 1, privateKeys, address(counter), 0, @@ -906,12 +876,12 @@ contract KintoWalletTest is AATestScaffolding, UserOp { UserOperation memory userOp = this.createUserOperationWithPaymaster( _chainID, - address(_kintoWalletv1), - _kintoWalletv1.getNonce(), + address(_kintoWallet), + _kintoWallet.getNonce(), privateKeys, - address(_kintoWalletv1), + address(_kintoWallet), 0, - abi.encodeWithSignature("resetSigners(address[],uint8)", owners, _kintoWalletv1.ALL_SIGNERS()), + abi.encodeWithSignature("resetSigners(address[],uint8)", owners, _kintoWallet.ALL_SIGNERS()), address(_paymaster) ); UserOperation[] memory userOps = new UserOperation[](1); @@ -919,11 +889,11 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // (2). execute the transaction via the entry point vm.expectEmit(); - emit WalletPolicyChanged(_kintoWalletv1.ALL_SIGNERS(), _kintoWalletv1.SINGLE_SIGNER()); + emit WalletPolicyChanged(_kintoWallet.ALL_SIGNERS(), _kintoWallet.SINGLE_SIGNER()); _entryPoint.handleOps(userOps, payable(_owner)); - assertEq(_kintoWalletv1.owners(1), _user); - assertEq(_kintoWalletv1.signerPolicy(), _kintoWalletv1.ALL_SIGNERS()); + assertEq(_kintoWallet.owners(1), _user); + assertEq(_kintoWallet.signerPolicy(), _kintoWallet.ALL_SIGNERS()); // (3). deploy Counter contract Counter counter = new Counter(); @@ -944,19 +914,14 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // a. Approval UserOp userOps[0] = createWhitelistAppOp( - _chainID, - privateKeys, - address(_kintoWalletv1), - _kintoWalletv1.getNonce(), - address(counter), - address(_paymaster) + _chainID, privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(counter), address(_paymaster) ); // b. Counter increment userOps[1] = this.createUserOperationWithPaymaster( _chainID, - address(_kintoWalletv1), - _kintoWalletv1.getNonce() + 1, + address(_kintoWallet), + _kintoWallet.getNonce() + 1, privateKeys, address(counter), 0, @@ -973,11 +938,11 @@ contract KintoWalletTest is AATestScaffolding, UserOp { function testRecoverAccountSuccessfully() public { vm.startPrank(_recoverer); - assertEq(_kintoWalletv1.owners(0), _owner); + assertEq(_kintoWallet.owners(0), _owner); // start Recovery - _walletFactory.startWalletRecovery(payable(address(_kintoWalletv1))); - assertEq(_kintoWalletv1.inRecovery(), block.timestamp); + _walletFactory.startWalletRecovery(payable(address(_kintoWallet))); + assertEq(_kintoWallet.inRecovery(), block.timestamp); vm.stopPrank(); // Mint NFT to new owner and burn old @@ -992,7 +957,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { assertEq(_kintoIDv1.isKYC(_user), true); // Pass recovery time - vm.warp(block.timestamp + _kintoWalletv1.RECOVERY_TIME() + 1); + vm.warp(block.timestamp + _kintoWallet.RECOVERY_TIME() + 1); address[] memory users = new address[](1); users[0] = _user; IKintoID.MonitorUpdateData[][] memory updates = new IKintoID.MonitorUpdateData[][](1); @@ -1002,38 +967,38 @@ contract KintoWalletTest is AATestScaffolding, UserOp { vm.prank(_kycProvider); _kintoIDv1.monitor(users, updates); vm.prank(_recoverer); - _walletFactory.completeWalletRecovery(payable(address(_kintoWalletv1)), users); - assertEq(_kintoWalletv1.inRecovery(), 0); - assertEq(_kintoWalletv1.owners(0), _user); + _walletFactory.completeWalletRecovery(payable(address(_kintoWallet)), users); + assertEq(_kintoWallet.inRecovery(), 0); + assertEq(_kintoWallet.owners(0), _user); } function test_RevertWhen_RecoverNotRecoverer(address someone) public { - vm.assume(someone != _kintoWalletv1.recoverer()); + vm.assume(someone != _kintoWallet.recoverer()); // start recovery vm.expectRevert("only recoverer"); - _walletFactory.startWalletRecovery(payable(address(_kintoWalletv1))); + _walletFactory.startWalletRecovery(payable(address(_kintoWallet))); } function test_RevertWhen_DirectCall() public { vm.prank(_recoverer); vm.expectRevert("KW: only factory"); - _kintoWalletv1.startRecovery(); + _kintoWallet.startRecovery(); } function test_RevertWhen_RecoverWithoutBurningOldOwner() public { - assertEq(_kintoWalletv1.owners(0), _owner); + assertEq(_kintoWallet.owners(0), _owner); // start recovery vm.prank(_recoverer); - _walletFactory.startWalletRecovery(payable(address(_kintoWalletv1))); - assertEq(_kintoWalletv1.inRecovery(), block.timestamp); + _walletFactory.startWalletRecovery(payable(address(_kintoWallet))); + assertEq(_kintoWallet.inRecovery(), block.timestamp); // approve KYC for _user (mint NFT) approveKYC(_kycProvider, _user, _userPk); assertEq(_kintoIDv1.isKYC(_user), true); // pass recovery time - vm.warp(block.timestamp + _kintoWalletv1.RECOVERY_TIME() + 1); + vm.warp(block.timestamp + _kintoWallet.RECOVERY_TIME() + 1); // monitor AML IKintoID.MonitorUpdateData[][] memory updates = new IKintoID.MonitorUpdateData[][](1); @@ -1049,23 +1014,23 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // complete recovery vm.prank(_recoverer); vm.expectRevert("KW-fr: Old KYC must be burned"); - _walletFactory.completeWalletRecovery(payable(address(_kintoWalletv1)), users); + _walletFactory.completeWalletRecovery(payable(address(_kintoWallet)), users); } function test_RevertWhen_RecoverWithoutNewOwnerKYCd() public { - assertEq(_kintoWalletv1.owners(0), _owner); + assertEq(_kintoWallet.owners(0), _owner); // start Recovery vm.prank(_recoverer); - _walletFactory.startWalletRecovery(payable(address(_kintoWalletv1))); - assertEq(_kintoWalletv1.inRecovery(), block.timestamp); + _walletFactory.startWalletRecovery(payable(address(_kintoWallet))); + assertEq(_kintoWallet.inRecovery(), block.timestamp); // burn old owner NFT revokeKYC(_kycProvider, _owner, _ownerPk); assertEq(_kintoIDv1.isKYC(_owner), false); // pass recovery time - vm.warp(block.timestamp + _kintoWalletv1.RECOVERY_TIME() + 1); + vm.warp(block.timestamp + _kintoWallet.RECOVERY_TIME() + 1); // complete recovery assertEq(_kintoIDv1.isKYC(_user), false); // new owner is not KYC'd @@ -1073,16 +1038,16 @@ contract KintoWalletTest is AATestScaffolding, UserOp { users[0] = _user; vm.prank(_recoverer); vm.expectRevert("KW-rs: KYC Required"); - _walletFactory.completeWalletRecovery(payable(address(_kintoWalletv1)), users); + _walletFactory.completeWalletRecovery(payable(address(_kintoWallet)), users); } function test_RevertWhen_RecoverNotEnoughTime() public { - assertEq(_kintoWalletv1.owners(0), _owner); + assertEq(_kintoWallet.owners(0), _owner); // start Recovery vm.prank(_recoverer); - _walletFactory.startWalletRecovery(payable(address(_kintoWalletv1))); - assertEq(_kintoWalletv1.inRecovery(), block.timestamp); + _walletFactory.startWalletRecovery(payable(address(_kintoWallet))); + assertEq(_kintoWallet.inRecovery(), block.timestamp); // burn old owner NFT revokeKYC(_kycProvider, _owner, _ownerPk); @@ -1093,7 +1058,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { assertEq(_kintoIDv1.isKYC(_user), true); // pass recovery time (not enough) - vm.warp(block.timestamp + _kintoWalletv1.RECOVERY_TIME() - 1); + vm.warp(block.timestamp + _kintoWallet.RECOVERY_TIME() - 1); // monitor AML IKintoID.MonitorUpdateData[][] memory updates = new IKintoID.MonitorUpdateData[][](1); @@ -1110,16 +1075,16 @@ contract KintoWalletTest is AATestScaffolding, UserOp { vm.prank(_recoverer); vm.expectRevert("KW-fr: too early"); - _walletFactory.completeWalletRecovery(payable(address(_kintoWalletv1)), users); + _walletFactory.completeWalletRecovery(payable(address(_kintoWallet)), users); } /* ============ Funder Whitelist ============ */ function testWalletOwnersAreWhitelisted() public { vm.startPrank(_owner); - assertEq(_kintoWalletv1.isFunderWhitelisted(_owner), true); - assertEq(_kintoWalletv1.isFunderWhitelisted(_user), false); - assertEq(_kintoWalletv1.isFunderWhitelisted(_user2), false); + assertEq(_kintoWallet.isFunderWhitelisted(_owner), true); + assertEq(_kintoWallet.isFunderWhitelisted(_user), false); + assertEq(_kintoWallet.isFunderWhitelisted(_user2), false); vm.stopPrank(); } @@ -1127,15 +1092,15 @@ contract KintoWalletTest is AATestScaffolding, UserOp { vm.startPrank(_owner); address[] memory funders = new address[](1); funders[0] = address(23); - uint256 nonce = _kintoWalletv1.getNonce(); + uint256 nonce = _kintoWallet.getNonce(); bool[] memory flags = new bool[](1); flags[0] = true; UserOperation memory userOp = this.createUserOperationWithPaymaster( _chainID, - address(_kintoWalletv1), + address(_kintoWallet), nonce, privateKeys, - address(_kintoWalletv1), + address(_kintoWallet), 0, abi.encodeWithSignature("setFunderWhitelist(address[],bool[])", funders, flags), address(_paymaster) @@ -1144,7 +1109,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { userOps[0] = userOp; // Execute the transaction via the entry point _entryPoint.handleOps(userOps, payable(_owner)); - assertEq(_kintoWalletv1.isFunderWhitelisted(address(23)), true); + assertEq(_kintoWallet.isFunderWhitelisted(address(23)), true); vm.stopPrank(); } @@ -1156,10 +1121,10 @@ contract KintoWalletTest is AATestScaffolding, UserOp { createApp(_owner, "test", address(_engenCredits), childContracts); UserOperation memory userOp = this.createUserOperationWithPaymaster( _chainID, - address(_kintoWalletv1), - _kintoWalletv1.getNonce(), + address(_kintoWallet), + _kintoWallet.getNonce(), privateKeys, - address(_kintoWalletv1), + address(_kintoWallet), 0, abi.encodeWithSignature("setAppKey(address,address)", app, _user), address(_paymaster) @@ -1168,7 +1133,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { userOps[0] = userOp; // Execute the transaction via the entry point - address appSignerBefore = _kintoWalletv1.appSigner(app); + address appSignerBefore = _kintoWallet.appSigner(app); // @dev handleOps fails silently (does not revert) vm.expectEmit(true, true, true, false); emit UserOperationRevertReason( @@ -1177,26 +1142,26 @@ contract KintoWalletTest is AATestScaffolding, UserOp { vm.recordLogs(); _entryPoint.handleOps(userOps, payable(_owner)); assertRevertReasonEq("KW-apk: invalid address"); - assertEq(_kintoWalletv1.appSigner(app), appSignerBefore); + assertEq(_kintoWallet.appSigner(app), appSignerBefore); } function testSettingAppKey() public { address app = address(_engenCredits); - uint256 nonce = _kintoWalletv1.getNonce(); + uint256 nonce = _kintoWallet.getNonce(); address[] memory childContracts = new address[](0); createApp(_owner, "test", address(_engenCredits), childContracts); UserOperation[] memory userOps = new UserOperation[](2); userOps[0] = createWhitelistAppOp( - _chainID, privateKeys, address(_kintoWalletv1), nonce, address(_engenCredits), address(_paymaster) + _chainID, privateKeys, address(_kintoWallet), nonce, address(_engenCredits), address(_paymaster) ); userOps[1] = this.createUserOperationWithPaymaster( _chainID, - address(_kintoWalletv1), + address(_kintoWallet), nonce + 1, privateKeys, - address(_kintoWalletv1), + address(_kintoWallet), 0, abi.encodeWithSignature("setAppKey(address,address)", app, _user), address(_paymaster) @@ -1204,7 +1169,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // Execute the transaction via the entry point _entryPoint.handleOps(userOps, payable(_owner)); - assertEq(_kintoWalletv1.appSigner(app), _user); + assertEq(_kintoWallet.appSigner(app), _user); } function testMultisigTransactionWith2SignersWithAppkey() public { @@ -1217,12 +1182,12 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // generate the user operation wihch changes the policy to ALL_SIGNERS UserOperation memory userOp = this.createUserOperationWithPaymaster( _chainID, - address(_kintoWalletv1), - _kintoWalletv1.getNonce(), + address(_kintoWallet), + _kintoWallet.getNonce(), privateKeys, - address(_kintoWalletv1), + address(_kintoWallet), 0, - abi.encodeWithSignature("resetSigners(address[],uint8)", owners, _kintoWalletv1.ALL_SIGNERS()), + abi.encodeWithSignature("resetSigners(address[],uint8)", owners, _kintoWallet.ALL_SIGNERS()), address(_paymaster) ); UserOperation[] memory userOps = new UserOperation[](1); @@ -1230,8 +1195,8 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // Execute the transaction via the entry point _entryPoint.handleOps(userOps, payable(_owner)); - assertEq(_kintoWalletv1.owners(1), _user2); - assertEq(_kintoWalletv1.signerPolicy(), _kintoWalletv1.ALL_SIGNERS()); + assertEq(_kintoWallet.owners(1), _user2); + assertEq(_kintoWallet.signerPolicy(), _kintoWallet.ALL_SIGNERS()); // Deploy Counter contract Counter counter = new Counter(); @@ -1250,19 +1215,14 @@ contract KintoWalletTest is AATestScaffolding, UserOp { privateKeys[0] = _ownerPk; privateKeys[1] = _user2Pk; userOps[0] = createWhitelistAppOp( - _chainID, - privateKeys, - address(_kintoWalletv1), - _kintoWalletv1.getNonce(), - address(counter), - address(_paymaster) + _chainID, privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(counter), address(_paymaster) ); userOps[1] = this.createUserOperationWithPaymaster( _chainID, - address(_kintoWalletv1), - _kintoWalletv1.getNonce() + 1, + address(_kintoWallet), + _kintoWallet.getNonce() + 1, privateKeys, - address(_kintoWalletv1), + address(_kintoWallet), 0, abi.encodeWithSignature("setAppKey(address,address)", address(counter), _user), address(_paymaster) @@ -1276,8 +1236,8 @@ contract KintoWalletTest is AATestScaffolding, UserOp { privateKeysApp[0] = 3; userOps[0] = this.createUserOperationWithPaymaster( _chainID, - address(_kintoWalletv1), - _kintoWalletv1.getNonce(), + address(_kintoWallet), + _kintoWallet.getNonce(), privateKeysApp, address(counter), 0, @@ -1290,4 +1250,27 @@ contract KintoWalletTest is AATestScaffolding, UserOp { assertEq(counter.count(), 1); vm.stopPrank(); } + + /* ============ Whitelist ============ */ + + function testWhitelistRegisteredApp() public { + // (3). deploy Counter contract + Counter counter = new Counter(); + assertEq(counter.count(), 0); + + // (4). fund paymaster for Counter contract + _fundPaymasterForContract(address(counter)); + address[] memory childContracts = new address[](0); + createApp(_owner, "test", address(counter), childContracts); + + // (6). Create whitelist app user op + UserOperation[] memory userOps = new UserOperation[](1); + userOps[0] = createWhitelistAppOp( + _chainID, privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(counter), address(_paymaster) + ); + + // (7). execute the transaction via the entry point + _entryPoint.handleOps(userOps, payable(_owner)); + assertEq(counter.count(), 1); + } } diff --git a/test/KintoWalletFactory.t.sol b/test/KintoWalletFactory.t.sol index c264e5da5..8eced7327 100644 --- a/test/KintoWalletFactory.t.sol +++ b/test/KintoWalletFactory.t.sol @@ -1,15 +1,16 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13; -import "../src/wallet/KintoWallet.sol"; import "../src/wallet/KintoWalletFactory.sol"; import "../src/paymasters/SponsorPaymaster.sol"; import "../src/KintoID.sol"; +import {KintoWalletV3 as KintoWallet} from "../src/wallet/KintoWallet.sol"; import {UserOp} from "./helpers/UserOp.sol"; import {UUPSProxy} from "./helpers/UUPSProxy.sol"; import {AATestScaffolding} from "./helpers/AATestScaffolding.sol"; import {Create2Helper} from "./helpers/Create2Helper.sol"; import "../src/sample/Counter.sol"; +import "../src/interfaces/IKintoWallet.sol"; import "@aa/interfaces/IAccount.sol"; import "@aa/interfaces/INonceManager.sol"; @@ -20,11 +21,12 @@ import {UpgradeableBeacon} from "@openzeppelin/contracts/proxy/beacon/Upgradeabl import {SignatureChecker} from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "forge-std/Test.sol"; import "forge-std/console.sol"; -contract KintoWalletV999 is KintoWallet { +contract KintoWalletUpgrade is KintoWallet { constructor(IEntryPoint _entryPoint, IKintoID _kintoID, IKintoAppRegistry _kintoAppRegistry) KintoWallet(_entryPoint, _kintoID, _kintoAppRegistry) {} @@ -34,7 +36,7 @@ contract KintoWalletV999 is KintoWallet { } } -contract KintoWalletFactoryV999 is KintoWalletFactory { +contract KintoWalletFactoryUpgrade is KintoWalletFactory { constructor(KintoWallet _impl) KintoWalletFactory(_impl) {} function newFunction() public pure returns (uint256) { @@ -46,8 +48,8 @@ contract KintoWalletFactoryTest is Create2Helper, UserOp, AATestScaffolding { using ECDSAUpgradeable for bytes32; using SignatureChecker for address; - KintoWalletFactoryV999 _walletFactoryv2; - KintoWalletV999 _kintoWalletv2; + KintoWalletFactoryUpgrade _walletFactoryv2; + KintoWalletUpgrade _kintoWalletv2; uint256 _chainID = 1; @@ -68,43 +70,44 @@ contract KintoWalletFactoryTest is Create2Helper, UserOp, AATestScaffolding { function testOwnerCanUpgradeFactory() public { vm.startPrank(_owner); - KintoWalletFactoryV999 _implementationV999 = new KintoWalletFactoryV999(_kintoWalletImpl); - _walletFactory.upgradeTo(address(_implementationV999)); + KintoWalletFactoryUpgrade _newImplementation = new KintoWalletFactoryUpgrade(_kintoWalletImpl); + _walletFactory.upgradeTo(address(_newImplementation)); // re-wrap the _proxy - _walletFactoryv2 = KintoWalletFactoryV999(address(_walletFactory)); + _walletFactoryv2 = KintoWalletFactoryUpgrade(address(_walletFactory)); assertEq(_walletFactoryv2.newFunction(), 1); vm.stopPrank(); } function test_RevertWhen_OthersCannotUpgradeFactory() public { - KintoWalletFactoryV999 _implementationV999 = new KintoWalletFactoryV999(_kintoWalletImpl); + KintoWalletFactoryUpgrade _newImplementation = new KintoWalletFactoryUpgrade(_kintoWalletImpl); vm.expectRevert("Ownable: caller is not the owner"); - _walletFactory.upgradeTo(address(_implementationV999)); + _walletFactory.upgradeTo(address(_newImplementation)); } function testAllWalletsUpgrade() public { vm.startPrank(_owner); - // Deploy wallet implementation - _kintoWalletImpl = new KintoWalletV999(_entryPoint, _kintoIDv1, _kintoAppRegistry); + // Deploy a new wallet implementation + _kintoWalletImpl = + KintoWallet(payable(address(new KintoWalletUpgrade(_entryPoint, _kintoIDv1, _kintoAppRegistry)))); // deploy walletv1 through wallet factory and initializes it - _kintoWalletv1 = _walletFactory.createAccount(_owner, _owner, 0); + _kintoWallet = _walletFactory.createAccount(_owner, _owner, 0); // Upgrade all implementations _walletFactory.upgradeAllWalletImplementations(_kintoWalletImpl); - KintoWalletV999 walletV2 = KintoWalletV999(payable(address(_kintoWalletv1))); + KintoWalletUpgrade walletV2 = KintoWalletUpgrade(payable(address(_kintoWallet))); assertEq(walletV2.walletFunction(), 1); vm.stopPrank(); } function testUpgrade_RevertWhen_CallerIsNotOwner() public { - // deploy wallet implementation - _kintoWalletImpl = new KintoWalletV999(_entryPoint, _kintoIDv1, _kintoAppRegistry); + // deploy a new wallet implementation + _kintoWalletImpl = new KintoWalletUpgrade(_entryPoint, _kintoIDv1, _kintoAppRegistry); // deploy walletv1 through wallet factory and initializes it - _kintoWalletv1 = _walletFactory.createAccount(_owner, _owner, 0); + _kintoWallet = _walletFactory.createAccount(_owner, _owner, 0); // upgrade all implementations vm.expectRevert("Ownable: caller is not the owner"); @@ -127,7 +130,7 @@ contract KintoWalletFactoryTest is Create2Helper, UserOp, AATestScaffolding { function testDeploy_RevertWhen_CreateWalletThroughDeploy() public { vm.startPrank(_owner); - bytes memory initialize = abi.encodeWithSelector(KintoWallet.initialize.selector, _owner, _owner); + bytes memory initialize = abi.encodeWithSelector(IKintoWallet.initialize.selector, _owner, _owner); bytes memory bytecode = abi.encodePacked( type(SafeBeaconProxy).creationCode, abi.encode(address(_walletFactory.beacon()), initialize) ); @@ -138,14 +141,14 @@ contract KintoWalletFactoryTest is Create2Helper, UserOp, AATestScaffolding { function testSignerCanFundWallet() public { vm.startPrank(_owner); - _walletFactory.fundWallet{value: 1e18}(payable(address(_kintoWalletv1))); - assertEq(address(_kintoWalletv1).balance, 1e18); + _walletFactory.fundWallet{value: 1e18}(payable(address(_kintoWallet))); + assertEq(address(_kintoWallet).balance, 1e18); } function testWhitelistedSignerCanFundWallet() public { vm.startPrank(_owner); - _fundPaymasterForContract(address(_kintoWalletv1)); - uint256 startingNonce = _kintoWalletv1.getNonce(); + _fundPaymasterForContract(address(_kintoWallet)); + uint256 startingNonce = _kintoWallet.getNonce(); address[] memory funders = new address[](1); funders[0] = _funder; bool[] memory flags = new bool[](1); @@ -154,10 +157,10 @@ contract KintoWalletFactoryTest is Create2Helper, UserOp, AATestScaffolding { privateKeys[0] = 1; UserOperation memory userOp = this.createUserOperationWithPaymaster( _chainID, - address(_kintoWalletv1), + address(_kintoWallet), startingNonce, privateKeys, - address(_kintoWalletv1), + address(_kintoWallet), 0, abi.encodeWithSignature("setFunderWhitelist(address[],bool[])", funders, flags), address(_paymaster) @@ -170,8 +173,8 @@ contract KintoWalletFactoryTest is Create2Helper, UserOp, AATestScaffolding { _funder.transfer(1e17); vm.stopPrank(); vm.startPrank(_funder); - _walletFactory.fundWallet{value: 1e17}(payable(address(_kintoWalletv1))); - assertEq(address(_kintoWalletv1).balance, 1e17); + _walletFactory.fundWallet{value: 1e17}(payable(address(_kintoWallet))); + assertEq(address(_kintoWallet).balance, 1e17); } function testSignerCannotFundInvalidWallet() public { @@ -186,12 +189,12 @@ contract KintoWalletFactoryTest is Create2Helper, UserOp, AATestScaffolding { vm.stopPrank(); vm.startPrank(_user); vm.expectRevert("Invalid wallet or funder"); - _walletFactory.fundWallet{value: 1e18}(payable(address(_kintoWalletv1))); + _walletFactory.fundWallet{value: 1e18}(payable(address(_kintoWallet))); } function testSignerCannotFundWalletWithoutEth() public { vm.startPrank(_owner); vm.expectRevert("Invalid wallet or funder"); - _walletFactory.fundWallet{value: 0}(payable(address(_kintoWalletv1))); + _walletFactory.fundWallet{value: 0}(payable(address(_kintoWallet))); } } diff --git a/test/SponsorPaymastExploit.t.sol b/test/SponsorPaymastExploit.t.sol index 28db55c44..f60bda715 100644 --- a/test/SponsorPaymastExploit.t.sol +++ b/test/SponsorPaymastExploit.t.sol @@ -67,7 +67,7 @@ contract SponsorPaymasterExploitTest is Create2Helper, MyOpCreator, AATestScaffo vm.stopPrank(); deployAAScaffolding(_owner, 1, _kycProvider, _recoverer); _fundPaymasterForContract(address(_engenCredits)); - _fundPaymasterForContract(address(_kintoWalletv1)); + _fundPaymasterForContract(address(_kintoWallet)); } function testExploit() public { @@ -79,13 +79,13 @@ contract SponsorPaymasterExploitTest is Create2Helper, MyOpCreator, AATestScaffo _fundPaymasterForContract(address(counter)); vm.startPrank(_owner); // Let's send a transaction to the counter contract through our wallet - uint256 startingNonce = _kintoWalletv1.getNonce(); + uint256 startingNonce = _kintoWallet.getNonce(); uint256[] memory privateKeys = new uint256[](1); privateKeys[0] = 1; UserOperation memory userOp = this._createOp( _chainID, - address(_kintoWalletv1), + address(_kintoWallet), startingNonce, privateKeys, address(counter), diff --git a/test/SponsorPaymaster.t.sol b/test/SponsorPaymaster.t.sol index 124f7e36e..964baf054 100644 --- a/test/SponsorPaymaster.t.sol +++ b/test/SponsorPaymaster.t.sol @@ -66,8 +66,8 @@ contract SponsorPaymasterTest is KYCSignature { function testOwnerCanUpgrade() public { vm.startPrank(_owner); - SponsorPaymasterV999 _implementationV999 = new SponsorPaymasterV999(_entryPoint, _owner); - _paymaster.upgradeTo(address(_implementationV999)); + SponsorPaymasterV999 _newImplementation = new SponsorPaymasterV999(_entryPoint, _owner); + _paymaster.upgradeTo(address(_newImplementation)); // re-wrap the _proxy _paymasterv999 = SponsorPaymasterV999(address(_proxy)); assertEq(_paymasterv999.newFunction(), 1); @@ -75,9 +75,9 @@ contract SponsorPaymasterTest is KYCSignature { } function testUpgrade_RevertWhen_CallerIsNotOwner() public { - SponsorPaymasterV999 _implementationV999 = new SponsorPaymasterV999(_entryPoint, _owner); + SponsorPaymasterV999 _newImplementation = new SponsorPaymasterV999(_entryPoint, _owner); vm.expectRevert("SP: not owner"); - _paymaster.upgradeTo(address(_implementationV999)); + _paymaster.upgradeTo(address(_newImplementation)); } // Deposit & Stake diff --git a/test/helpers/AATestScaffolding.sol b/test/helpers/AATestScaffolding.sol index 2282f1ffc..36d56030f 100644 --- a/test/helpers/AATestScaffolding.sol +++ b/test/helpers/AATestScaffolding.sol @@ -10,10 +10,10 @@ import {IKintoEntryPoint} from "../../src/interfaces/IKintoEntryPoint.sol"; import {UUPSProxy} from "../helpers/UUPSProxy.sol"; import {KYCSignature} from "../helpers/KYCSignature.sol"; -import "../../src/wallet/KintoWallet.sol"; +import {KintoWalletV3 as KintoWallet} from "../../src/wallet/KintoWallet.sol"; import "../../src/apps/KintoAppRegistry.sol"; import "../../src/tokens/EngenCredits.sol"; -import "../../src/wallet/KintoWalletFactory.sol"; +import {KintoWalletFactoryV2 as KintoWalletFactory} from "../../src/wallet/KintoWalletFactory.sol"; import "../../src/paymasters/SponsorPaymaster.sol"; import "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol"; @@ -38,7 +38,7 @@ abstract contract AATestScaffolding is KYCSignature { // Wallet & Factory KintoWalletFactory _walletFactory; KintoWallet _kintoWalletImpl; - IKintoWallet _kintoWalletv1; + IKintoWallet _kintoWallet; EngenCredits _engenCredits; SponsorPaymaster _paymaster; @@ -55,7 +55,7 @@ abstract contract AATestScaffolding is KYCSignature { deployKintoID(_owner, _kycProvider); // Deploy Kinto App - deployKintoApp(_owner); + deployAppRegistry(_owner); // vm.startPrank(_owner); EntryPoint entry = new EntryPoint{salt: 0}(); @@ -77,7 +77,7 @@ abstract contract AATestScaffolding is KYCSignature { vm.prank(_owner); // deploy latest KintoWallet version through wallet factory and initialize it - _kintoWalletv1 = _walletFactory.createAccount(_owner, _recoverer, 0); + _kintoWallet = _walletFactory.createAccount(_owner, _recoverer, 0); // Give some eth vm.deal(_owner, 1e20); } @@ -111,8 +111,8 @@ abstract contract AATestScaffolding is KYCSignature { _kintoWalletImpl = new KintoWallet{salt: 0}(_entryPoint, _kintoIDv1, _kintoAppRegistry); // Deploy wallet factory implementation - KintoWalletFactory _walletFactoryI = new KintoWalletFactory{salt: 0}(_kintoWalletImpl); - _proxyFactory = new UUPSProxy{salt: 0}(address(_walletFactoryI), ""); + KintoWalletFactory _implementation = new KintoWalletFactory{salt: 0}(_kintoWalletImpl); + _proxyFactory = new UUPSProxy{salt: 0}(address(_implementation), ""); _walletFactory = KintoWalletFactory(address(_proxyFactory)); // Initialize wallet factory @@ -163,7 +163,7 @@ abstract contract AATestScaffolding is KYCSignature { vm.stopPrank(); } - function deployKintoApp(address _owner) public { + function deployAppRegistry(address _owner) public { vm.startPrank(_owner); // deploy the Kinto App registry diff --git a/test/helpers/UserOp.sol b/test/helpers/UserOp.sol index 8b5987ad8..3e0253856 100644 --- a/test/helpers/UserOp.sol +++ b/test/helpers/UserOp.sol @@ -230,7 +230,7 @@ abstract contract UserOp is Test { pk, address(wallet), 0, - abi.encodeWithSignature("setAppWhitelist(address[],bool[])", targets, flags), + abi.encodeWithSignature("whitelistApp(address[],bool[])", targets, flags), address(_paymaster) ); } From 352f5926842bcb82a4b33c47cee36e31fb99f4bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Federico=20Mart=C3=ADn=20Alconada=20Verzini?= Date: Mon, 8 Jan 2024 18:08:05 -0300 Subject: [PATCH 39/43] test: add whitelist tests --- test/KintoWallet.t.sol | 67 +++++++++++++++++++++--------- test/helpers/AATestScaffolding.sol | 2 +- 2 files changed, 48 insertions(+), 21 deletions(-) diff --git a/test/KintoWallet.t.sol b/test/KintoWallet.t.sol index 364122c93..9ab39a9ad 100644 --- a/test/KintoWallet.t.sol +++ b/test/KintoWallet.t.sol @@ -124,7 +124,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { Counter counter = new Counter(); assertEq(counter.count(), 0); address[] memory childContracts = new address[](0); - createApp(_owner, "test", address(counter), childContracts); + registerApp(_owner, "test", address(counter), childContracts); // send a transaction to the counter contract through our wallet // without a paymaster and without prefunding the wallet @@ -150,7 +150,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { Counter counter = new Counter(); assertEq(counter.count(), 0); address[] memory childContracts = new address[](0); - createApp(_owner, "test", address(counter), childContracts); + registerApp(_owner, "test", address(counter), childContracts); // prefund wallet vm.deal(address(_kintoWallet), 1 ether); @@ -218,7 +218,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { vm.stopPrank(); _fundPaymasterForContract(address(counter)); address[] memory childContracts = new address[](0); - createApp(_owner, "test", address(counter), childContracts); + registerApp(_owner, "test", address(counter), childContracts); vm.startPrank(_owner); // Let's send a transaction to the counter contract through our wallet uint256 nonce = _kintoWallet.getNonce(); @@ -253,7 +253,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { uint256 nonce = _kintoWallet.getNonce(); vm.stopPrank(); address[] memory childContracts = new address[](0); - createApp(_owner, "test", address(counter), childContracts); + registerApp(_owner, "test", address(counter), childContracts); vm.startPrank(_owner); _fundPaymasterForContract(address(counter)); // Let's send a transaction to the counter contract through our wallet @@ -297,7 +297,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { uint256 nonce = _kintoWallet.getNonce(); vm.stopPrank(); address[] memory childContracts = new address[](0); - createApp(_owner, "test", address(counter), childContracts); + registerApp(_owner, "test", address(counter), childContracts); vm.startPrank(_owner); _fundPaymasterForContract(address(counter)); address[] memory targets = new address[](3); @@ -340,7 +340,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { _fundPaymasterForContract(address(counter)); address[] memory childContracts = new address[](0); - createApp(_owner, "test", address(counter), childContracts); + registerApp(_owner, "test", address(counter), childContracts); // prep batch address[] memory targets = new address[](3); @@ -674,7 +674,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // (4). fund paymaster for Counter contract _fundPaymasterForContract(address(counter)); address[] memory childContracts = new address[](0); - createApp(_owner, "test", address(counter), childContracts); + registerApp(_owner, "test", address(counter), childContracts); // (5). Set private keys privateKeys = new uint256[](2); @@ -738,7 +738,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // (4). fund paymaster for Counter contract _fundPaymasterForContract(address(counter)); address[] memory childContracts = new address[](0); - createApp(_owner, "test", address(counter), childContracts); + registerApp(_owner, "test", address(counter), childContracts); // (5). Create 2 user ops: userOps = new UserOperation[](2); @@ -800,7 +800,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // (4). fund paymaster for Counter contract _fundPaymasterForContract(address(counter)); address[] memory childContracts = new address[](0); - createApp(_owner, "test", address(counter), childContracts); + registerApp(_owner, "test", address(counter), childContracts); // (5). Set private keys privateKeys = new uint256[](3); @@ -839,7 +839,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // (2). fund paymaster for Counter contract _fundPaymasterForContract(address(counter)); address[] memory childContracts = new address[](0); - createApp(_owner, "test", address(counter), childContracts); + registerApp(_owner, "test", address(counter), childContracts); // (3). Create 2 user ops: UserOperation[] memory userOps = new UserOperation[](1); @@ -902,7 +902,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // (4). fund paymaster for Counter contract _fundPaymasterForContract(address(counter)); address[] memory childContracts = new address[](0); - createApp(_owner, "test", address(counter), childContracts); + registerApp(_owner, "test", address(counter), childContracts); // (5). Set private keys privateKeys = new uint256[](2); @@ -1118,7 +1118,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { function test_RevertWhen_SettingAppKeyNoWhitelist() public { address app = address(_engenCredits); address[] memory childContracts = new address[](0); - createApp(_owner, "test", address(_engenCredits), childContracts); + registerApp(_owner, "test", address(_engenCredits), childContracts); UserOperation memory userOp = this.createUserOperationWithPaymaster( _chainID, address(_kintoWallet), @@ -1149,7 +1149,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { address app = address(_engenCredits); uint256 nonce = _kintoWallet.getNonce(); address[] memory childContracts = new address[](0); - createApp(_owner, "test", address(_engenCredits), childContracts); + registerApp(_owner, "test", address(_engenCredits), childContracts); UserOperation[] memory userOps = new UserOperation[](2); userOps[0] = createWhitelistAppOp( @@ -1203,7 +1203,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { assertEq(counter.count(), 0); vm.stopPrank(); address[] memory childContracts = new address[](0); - createApp(_owner, "test", address(counter), childContracts); + registerApp(_owner, "test", address(counter), childContracts); // Fund counter contract vm.startPrank(_owner); @@ -1254,23 +1254,50 @@ contract KintoWalletTest is AATestScaffolding, UserOp { /* ============ Whitelist ============ */ function testWhitelistRegisteredApp() public { - // (3). deploy Counter contract + // (1). deploy Counter contract Counter counter = new Counter(); assertEq(counter.count(), 0); - // (4). fund paymaster for Counter contract + // (2). fund paymaster for Counter contract _fundPaymasterForContract(address(counter)); + + // (3). register app address[] memory childContracts = new address[](0); - createApp(_owner, "test", address(counter), childContracts); + registerApp(_owner, "test", address(counter), childContracts); - // (6). Create whitelist app user op + // (4). Create whitelist app user op UserOperation[] memory userOps = new UserOperation[](1); userOps[0] = createWhitelistAppOp( _chainID, privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(counter), address(_paymaster) ); - // (7). execute the transaction via the entry point + // (5). execute the transaction via the entry point _entryPoint.handleOps(userOps, payable(_owner)); - assertEq(counter.count(), 1); + } + + function testWhitelist_revertWhen_AppNotRegistered() public { + // (1). deploy Counter contract + Counter counter = new Counter(); + assertEq(counter.count(), 0); + + // (2). fund paymaster for Counter contract + _fundPaymasterForContract(address(counter)); + address[] memory childContracts = new address[](0); + // registerApp(_owner, "test", address(counter), childContracts); + + // (3). Create whitelist app user op + UserOperation[] memory userOps = new UserOperation[](1); + userOps[0] = createWhitelistAppOp( + _chainID, privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(counter), address(_paymaster) + ); + + // (4). execute the transaction via the entry point and expect a revert event + vm.expectEmit(true, true, true, false); + emit UserOperationRevertReason( + _entryPoint.getUserOpHash(userOps[0]), userOps[0].sender, userOps[0].nonce, bytes("") + ); + vm.recordLogs(); + _entryPoint.handleOps(userOps, payable(_owner)); + assertRevertReasonEq("KW-apw: app must be registered"); } } diff --git a/test/helpers/AATestScaffolding.sol b/test/helpers/AATestScaffolding.sol index 36d56030f..51dc1b315 100644 --- a/test/helpers/AATestScaffolding.sol +++ b/test/helpers/AATestScaffolding.sol @@ -212,7 +212,7 @@ abstract contract AATestScaffolding is KYCSignature { vm.stopPrank(); } - function createApp(address _owner, string memory name, address parentContract, address[] memory appContracts) + function registerApp(address _owner, string memory name, address parentContract, address[] memory appContracts) public { vm.startPrank(_owner); From 2b391a7ca50245ab4dbf8ee038b0cafcf0e86243 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Federico=20Mart=C3=ADn=20Alconada=20Verzini?= Date: Mon, 8 Jan 2024 23:11:12 -0300 Subject: [PATCH 40/43] test: add more tests --- test/EngenCredits.t.sol | 6 +-- test/KintoWallet.t.sol | 92 ++++++++++++++++++++++++++++------------- test/helpers/UserOp.sol | 4 +- 3 files changed, 68 insertions(+), 34 deletions(-) diff --git a/test/EngenCredits.t.sol b/test/EngenCredits.t.sol index 8ae0cc737..880aca5f3 100644 --- a/test/EngenCredits.t.sol +++ b/test/EngenCredits.t.sol @@ -136,7 +136,7 @@ contract EngenCreditsTest is Create2Helper, UserOp, AATestScaffolding { address(_paymaster) ); UserOperation[] memory userOps = new UserOperation[](2); - userOps[0] = createWhitelistAppOp( + userOps[0] = _whitelistAppOp( _chainID, privateKeys, address(_kintoWallet), @@ -175,7 +175,7 @@ contract EngenCreditsTest is Create2Helper, UserOp, AATestScaffolding { address(_paymaster) ); UserOperation[] memory userOps = new UserOperation[](2); - userOps[0] = createWhitelistAppOp( + userOps[0] = _whitelistAppOp( _chainID, privateKeys, address(_kintoWallet), @@ -209,7 +209,7 @@ contract EngenCreditsTest is Create2Helper, UserOp, AATestScaffolding { address(_paymaster) ); UserOperation[] memory userOps = new UserOperation[](2); - userOps[0] = createWhitelistAppOp( + userOps[0] = _whitelistAppOp( _chainID, privateKeys, address(_kintoWallet), diff --git a/test/KintoWallet.t.sol b/test/KintoWallet.t.sol index 9ab39a9ad..0690a7bea 100644 --- a/test/KintoWallet.t.sol +++ b/test/KintoWallet.t.sol @@ -49,18 +49,17 @@ contract KintoWalletTest is AATestScaffolding, UserOp { } /* ============ Upgrade Tests ============ */ - + // FIXME: these I think these upgrade tests are wrong because, basically, the KintoWallet.sol does not have + // an upgrade function. The upgrade function is in the UUPSUpgradeable.sol contract. function test_RevertWhen_OwnerCannotUpgrade() public { // deploy a new implementation KintoWallet _newImplementation = new KintoWallet(_entryPoint, _kintoIDv1, _kintoAppRegistry); - uint256 nonce = _kintoWallet.getNonce(); - // try calling upgradeTo from _owner wallet to upgrade _owner wallet UserOperation memory userOp = this.createUserOperationWithPaymaster( _chainID, address(_kintoWallet), - nonce, + _kintoWallet.getNonce(), privateKeys, address(_kintoWallet), 0, @@ -157,7 +156,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { UserOperation[] memory userOps = new UserOperation[](2); // whitelist app - userOps[0] = createWhitelistAppOp( + userOps[0] = _whitelistAppOp( _chainID, privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(counter), address(_paymaster) ); @@ -178,15 +177,17 @@ contract KintoWalletTest is AATestScaffolding, UserOp { assertEq(counter.count(), 1); } - function test_RevertWhen_TransactionViaPaymasterAndNoApproval() public { - // deploy the counter contract + function testTransaction_RevertWhen_AppNotRegisteredAndNotWhitelisted() public { + // (1). deploy Counter contract Counter counter = new Counter(); assertEq(counter.count(), 0); + // (2). fund paymaster for Counter contract _fundPaymasterForContract(address(counter)); - // send a transaction to the counter contract through our wallet - UserOperation memory userOp = this.createUserOperationWithPaymaster( + // (3). Create Counter increment user op + UserOperation[] memory userOps = new UserOperation[](1); + userOps[0] = this.createUserOperationWithPaymaster( _chainID, address(_kintoWallet), _kintoWallet.getNonce(), @@ -196,10 +197,44 @@ contract KintoWalletTest is AATestScaffolding, UserOp { abi.encodeWithSignature("increment()"), address(_paymaster) ); + + // (4). execute the transaction via the entry point + vm.expectEmit(true, true, true, false); + emit UserOperationRevertReason( + _entryPoint.getUserOpHash(userOps[0]), userOps[0].sender, userOps[0].nonce, bytes("") + ); + vm.recordLogs(); + _entryPoint.handleOps(userOps, payable(_owner)); + assertRevertReasonEq("KW: contract not whitelisted"); + assertEq(counter.count(), 0); + } + + function testTransaction_RevertWhen_AppRegisteredButNotWhitelisted() public { + // (1). deploy Counter contract + Counter counter = new Counter(); + assertEq(counter.count(), 0); + + // (2). fund paymaster for Counter contract + _fundPaymasterForContract(address(counter)); + + // (3). register app + address[] memory childContracts = new address[](0); + registerApp(_owner, "test", address(counter), childContracts); + + // (4). Create Counter increment user op UserOperation[] memory userOps = new UserOperation[](1); - userOps[0] = userOp; + userOps[0] = this.createUserOperationWithPaymaster( + _chainID, + address(_kintoWallet), + _kintoWallet.getNonce(), + privateKeys, + address(counter), + 0, + abi.encodeWithSignature("increment()"), + address(_paymaster) + ); - // execute the transaction via the entry point + // (5). execute the transaction via the entry point vm.expectEmit(true, true, true, false); emit UserOperationRevertReason( _entryPoint.getUserOpHash(userOps[0]), userOps[0].sender, userOps[0].nonce, bytes("") @@ -235,7 +270,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { address(_paymaster) ); UserOperation[] memory userOps = new UserOperation[](2); - userOps[0] = createWhitelistAppOp( + userOps[0] = _whitelistAppOp( _chainID, privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(counter), address(_paymaster) ); userOps[1] = userOp2; @@ -278,9 +313,8 @@ contract KintoWalletTest is AATestScaffolding, UserOp { address(_paymaster) ); UserOperation[] memory userOps = new UserOperation[](3); - userOps[0] = createWhitelistAppOp( - _chainID, privateKeys, address(_kintoWallet), nonce, address(counter), address(_paymaster) - ); + userOps[0] = + _whitelistAppOp(_chainID, privateKeys, address(_kintoWallet), nonce, address(counter), address(_paymaster)); userOps[1] = userOp; userOps[2] = userOp2; // Execute the transaction via the entry point @@ -683,8 +717,8 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // (6). Create 2 user ops: userOps = new UserOperation[](2); - // a. Approval UserOp - userOps[0] = createWhitelistAppOp( + // a. whitelist app + userOps[0] = _whitelistAppOp( _chainID, privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(counter), address(_paymaster) ); @@ -743,8 +777,8 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // (5). Create 2 user ops: userOps = new UserOperation[](2); - // a. Approval UserOp - userOps[0] = createWhitelistAppOp( + // a. whitelist app + userOps[0] = _whitelistAppOp( _chainID, privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(counter), address(_paymaster) ); @@ -810,8 +844,8 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // (6). Create 2 user ops: userOps = new UserOperation[](2); - // a. Approval UserOp - userOps[0] = createWhitelistAppOp( + // a. whitelist app + userOps[0] = _whitelistAppOp( _chainID, privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(counter), address(_paymaster) ); // b. Counter increment @@ -844,8 +878,8 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // (3). Create 2 user ops: UserOperation[] memory userOps = new UserOperation[](1); userOps = new UserOperation[](2); - // a. Approval UserOp - userOps[0] = createWhitelistAppOp( + // a. whitelist app + userOps[0] = _whitelistAppOp( _chainID, privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(counter), address(_paymaster) ); @@ -912,8 +946,8 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // (6). Create 2 user ops: userOps = new UserOperation[](2); - // a. Approval UserOp - userOps[0] = createWhitelistAppOp( + // a. whitelist app + userOps[0] = _whitelistAppOp( _chainID, privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(counter), address(_paymaster) ); @@ -1152,7 +1186,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { registerApp(_owner, "test", address(_engenCredits), childContracts); UserOperation[] memory userOps = new UserOperation[](2); - userOps[0] = createWhitelistAppOp( + userOps[0] = _whitelistAppOp( _chainID, privateKeys, address(_kintoWallet), nonce, address(_engenCredits), address(_paymaster) ); @@ -1214,7 +1248,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { privateKeys = new uint256[](2); privateKeys[0] = _ownerPk; privateKeys[1] = _user2Pk; - userOps[0] = createWhitelistAppOp( + userOps[0] = _whitelistAppOp( _chainID, privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(counter), address(_paymaster) ); userOps[1] = this.createUserOperationWithPaymaster( @@ -1267,7 +1301,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // (4). Create whitelist app user op UserOperation[] memory userOps = new UserOperation[](1); - userOps[0] = createWhitelistAppOp( + userOps[0] = _whitelistAppOp( _chainID, privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(counter), address(_paymaster) ); @@ -1287,7 +1321,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // (3). Create whitelist app user op UserOperation[] memory userOps = new UserOperation[](1); - userOps[0] = createWhitelistAppOp( + userOps[0] = _whitelistAppOp( _chainID, privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(counter), address(_paymaster) ); diff --git a/test/helpers/UserOp.sol b/test/helpers/UserOp.sol index 3e0253856..fa0f3c02c 100644 --- a/test/helpers/UserOp.sol +++ b/test/helpers/UserOp.sol @@ -211,14 +211,14 @@ abstract contract UserOp is Test { return op; } - function createWhitelistAppOp( + function _whitelistAppOp( uint256 _chainId, uint256[] memory pk, address wallet, uint256 startingNonce, address app, address _paymaster - ) public view returns (UserOperation memory userOp) { + ) internal view returns (UserOperation memory userOp) { address[] memory targets = new address[](1); targets[0] = address(app); bool[] memory flags = new bool[](1); From 52e7be4a05f6b110a2854ecb2a4a274254189a43 Mon Sep 17 00:00:00 2001 From: Ramon Recuero Date: Tue, 9 Jan 2024 09:42:58 -0800 Subject: [PATCH 41/43] Register app gated by deployer --- script/migrations/07-deploy_kinto_app.sol | 1 + src/apps/KintoAppRegistry.sol | 14 ++++++++++++-- src/interfaces/IKintoAppRegistry.sol | 6 ++++++ src/interfaces/IKintoWalletFactory.sol | 2 ++ src/wallet/KintoWalletFactory.sol | 2 ++ test/KintoWallet.t.sol | 2 -- test/helpers/AATestScaffolding.sol | 5 +++-- 7 files changed, 26 insertions(+), 6 deletions(-) diff --git a/script/migrations/07-deploy_kinto_app.sol b/script/migrations/07-deploy_kinto_app.sol index c7b2ea0e6..1fb230b9f 100644 --- a/script/migrations/07-deploy_kinto_app.sol +++ b/script/migrations/07-deploy_kinto_app.sol @@ -44,6 +44,7 @@ contract KintoMigration7DeployScript is Create2Helper, ArtifactsReader { msg.sender, 0, abi.encodePacked(type(KintoAppRegistry).creationCode), bytes32(0) ) ); + _kintoApp.setWalletFactory(_walletFactory); // Give ownership to admin _kintoApp.transferOwnership(admin); address credits = _getChainDeployment("EngenCredits"); diff --git a/src/apps/KintoAppRegistry.sol b/src/apps/KintoAppRegistry.sol index b597fdb7f..3630e62e8 100644 --- a/src/apps/KintoAppRegistry.sol +++ b/src/apps/KintoAppRegistry.sol @@ -31,13 +31,13 @@ contract KintoAppRegistry is uint256 public constant override GAS_LIMIT_THRESHOLD = 1e16; // 0.01 ETH /* ============ State Variables ============ */ - - uint256 public override appCount; + IKintoWalletFactory public override walletFactory; mapping(address => IKintoAppRegistry.Metadata) private _appMetadata; // other contracts to be sponsored that dont belong in the app mapping(address => mapping(address => bool)) private _sponsoredContracts; // Mapping between the app and all the contracts that belong to it mapping(address => address) public override childToParentContract; + uint256 public override appCount; /* ============ Events ============ */ event AppCreated(address indexed _app, address _owner, uint256 _timestamp); @@ -66,6 +66,11 @@ contract KintoAppRegistry is // This function is called by the proxy contract when the implementation is upgraded function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} + function setWalletFactory(IKintoWalletFactory _walletFactory) external onlyOwner { + require(address(_walletFactory) != address(0), "Invalid wallet factory address"); + walletFactory = _walletFactory; + } + /* ============ Token name, symbol & URI ============ */ /** @@ -108,6 +113,11 @@ contract KintoAppRegistry is uint256[4] calldata appLimits ) external override { require(childToParentContract[parentContract] == address(0), "Parent contract already registered as a child"); + require( + walletFactory.deployedBy(parentContract) == address(0) + || walletFactory.deployedBy(parentContract) == msg.sender, + "Parent contract not deployed by wallet factory or admin" + ); _updateMetadata(_name, parentContract, appContracts, appLimits); appCount++; _safeMint(msg.sender, appCount); diff --git a/src/interfaces/IKintoAppRegistry.sol b/src/interfaces/IKintoAppRegistry.sol index 86bec51b2..1780ab8ee 100644 --- a/src/interfaces/IKintoAppRegistry.sol +++ b/src/interfaces/IKintoAppRegistry.sol @@ -1,6 +1,8 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.23; +import {IKintoWalletFactory} from "./IKintoWalletFactory.sol"; + interface IKintoAppRegistry { /* ============ Structs ============ */ @@ -34,6 +36,8 @@ interface IKintoAppRegistry { uint256[4] calldata appLimits ) external; + function setWalletFactory(IKintoWalletFactory _walletFactory) external; + /* ============ Basic Viewers ============ */ function name() external pure returns (string memory); @@ -52,6 +56,8 @@ interface IKintoAppRegistry { function isContractSponsored(address _app, address _contract) external view returns (bool); + function walletFactory() external view returns (IKintoWalletFactory); + /* ============ Constants and attrs ============ */ function RATE_LIMIT_PERIOD() external view returns (uint256); diff --git a/src/interfaces/IKintoWalletFactory.sol b/src/interfaces/IKintoWalletFactory.sol index dceb975ba..8231c0ab5 100644 --- a/src/interfaces/IKintoWalletFactory.sol +++ b/src/interfaces/IKintoWalletFactory.sol @@ -37,6 +37,8 @@ interface IKintoWalletFactory { function getWalletTimestamp(address wallet) external view returns (uint256); + function deployedBy(address wallet) external view returns (address); + /* ============ Constants and attrs ============ */ function kintoID() external view returns (IKintoID); diff --git a/src/wallet/KintoWalletFactory.sol b/src/wallet/KintoWalletFactory.sol index f7650dc3c..6e9606eab 100644 --- a/src/wallet/KintoWalletFactory.sol +++ b/src/wallet/KintoWalletFactory.sol @@ -30,6 +30,7 @@ contract KintoWalletFactory is Initializable, UUPSUpgradeable, OwnableUpgradeabl mapping(address => uint256) public override walletTs; uint256 public override factoryWalletVersion; uint256 public override totalWallets; + mapping(address => address) public override deployedBy; /* ============ Events ============ */ event KintoWalletFactoryCreation(address indexed account, address indexed owner, uint256 version); @@ -263,6 +264,7 @@ contract KintoWalletFactory is Initializable, UUPSUpgradeable, OwnableUpgradeabl OwnableUpgradeable(created).transferOwnership(newOwner); } } catch {} + deployedBy[created] = newOwner; return created; } } diff --git a/test/KintoWallet.t.sol b/test/KintoWallet.t.sol index 0690a7bea..7df20cbcc 100644 --- a/test/KintoWallet.t.sol +++ b/test/KintoWallet.t.sol @@ -1316,8 +1316,6 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // (2). fund paymaster for Counter contract _fundPaymasterForContract(address(counter)); - address[] memory childContracts = new address[](0); - // registerApp(_owner, "test", address(counter), childContracts); // (3). Create whitelist app user op UserOperation[] memory userOps = new UserOperation[](1); diff --git a/test/helpers/AATestScaffolding.sol b/test/helpers/AATestScaffolding.sol index 51dc1b315..ff9bcf21c 100644 --- a/test/helpers/AATestScaffolding.sol +++ b/test/helpers/AATestScaffolding.sol @@ -111,8 +111,8 @@ abstract contract AATestScaffolding is KYCSignature { _kintoWalletImpl = new KintoWallet{salt: 0}(_entryPoint, _kintoIDv1, _kintoAppRegistry); // Deploy wallet factory implementation - KintoWalletFactory _implementation = new KintoWalletFactory{salt: 0}(_kintoWalletImpl); - _proxyFactory = new UUPSProxy{salt: 0}(address(_implementation), ""); + KintoWalletFactory _implementation2 = new KintoWalletFactory{salt: 0}(_kintoWalletImpl); + _proxyFactory = new UUPSProxy{salt: 0}(address(_implementation2), ""); _walletFactory = KintoWalletFactory(address(_proxyFactory)); // Initialize wallet factory @@ -120,6 +120,7 @@ abstract contract AATestScaffolding is KYCSignature { // Set the wallet factory in the entry point _entryPoint.setWalletFactory(address(_walletFactory)); + _kintoAppRegistry.setWalletFactory(_walletFactory); vm.stopPrank(); } From db286ec305fd0e419d417a00b584095b6b637e12 Mon Sep 17 00:00:00 2001 From: Ramon Recuero Date: Tue, 9 Jan 2024 09:59:51 -0800 Subject: [PATCH 42/43] Modifies migration --- script/migrations/07-deploy_kinto_app.sol | 18 +++----- script/migrations/11-create_engen_app.sol | 52 +++++++++++++++++++++++ 2 files changed, 57 insertions(+), 13 deletions(-) create mode 100644 script/migrations/11-create_engen_app.sol diff --git a/script/migrations/07-deploy_kinto_app.sol b/script/migrations/07-deploy_kinto_app.sol index 1fb230b9f..63a12f343 100644 --- a/script/migrations/07-deploy_kinto_app.sol +++ b/script/migrations/07-deploy_kinto_app.sol @@ -23,14 +23,11 @@ contract KintoMigration7DeployScript is Create2Helper, ArtifactsReader { // solhint-disable code-complexity function run() public { console.log("RUNNING ON CHAIN WITH ID", vm.toString(block.chainid)); - // Execute this script with the hot wallet, not with ledger - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - address admin = vm.envAddress("LEDGER_ADMIN"); - if (admin == address(0)) { - console.log("Admin key not set", admin); - return; - } - vm.startBroadcast(deployerPrivateKey); + // If not using ledger, replace + // Execute this script with the admin + // uint256 deployerPrivateKey = vm.envUint('PRIVATE_KEY'); + // vm.startBroadcast(deployerPrivateKey); + vm.startBroadcast(); console.log("Executing with address", msg.sender); address appAddr = _getChainDeployment("KintoAppRegistry"); if (appAddr != address(0)) { @@ -44,12 +41,7 @@ contract KintoMigration7DeployScript is Create2Helper, ArtifactsReader { msg.sender, 0, abi.encodePacked(type(KintoAppRegistry).creationCode), bytes32(0) ) ); - _kintoApp.setWalletFactory(_walletFactory); - // Give ownership to admin - _kintoApp.transferOwnership(admin); address credits = _getChainDeployment("EngenCredits"); - // Create Engen App - _kintoApp.registerApp("Engen", credits, new address[](0), [uint256(0), uint256(0), uint256(0), uint256(0)]); // Fund in the paymaster SponsorPaymaster _paymaster = SponsorPaymaster(payable(_getChainDeployment("SponsorPaymaster"))); _paymaster.addDepositFor{value: 1e17}(credits); diff --git a/script/migrations/11-create_engen_app.sol b/script/migrations/11-create_engen_app.sol new file mode 100644 index 000000000..50098c3d5 --- /dev/null +++ b/script/migrations/11-create_engen_app.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import "forge-std/Script.sol"; +import "../../src/wallet/KintoWalletFactory.sol"; +import "../../src/KintoID.sol"; +import "../../src/wallet/KintoWallet.sol"; +import {Create2Helper} from "../../test/helpers/Create2Helper.sol"; +import {ArtifactsReader} from "../../test/helpers/ArtifactsReader.sol"; +import {UUPSProxy} from "../../test/helpers/UUPSProxy.sol"; +import "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; +import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import "forge-std/console.sol"; + +contract KintoMigration11DeployScript is Create2Helper, ArtifactsReader { + using ECDSAUpgradeable for bytes32; + + KintoWalletFactory _walletFactory; + KintoWallet _kintoWalletv1; + KintoID _kintoIDv1; + UUPSProxy _proxy; + + function setUp() public {} + + // solhint-disable code-complexity + function run() public { + console.log("RUNNING ON CHAIN WITH ID", vm.toString(block.chainid)); + // If not using ledger, replace + uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); + address deployer = vm.rememberKey(deployerPrivateKey); + vm.startBroadcast(deployerPrivateKey); + console.log("Executing with address", deployer); + address walletFactoryAddr = _getChainDeployment("KintoWalletFactory"); + if (walletFactoryAddr == address(0)) { + console.log("Need to execute main deploy script first", walletFactoryAddr); + return; + } + address credits = _getChainDeployment("EngenCredits"); + IKintoAppRegistry _kintoApp = IKintoAppRegistry(_getChainDeployment("KintoAppRegistry")); + + // TODO: This needs to go through the entry point + _kintoApp.setWalletFactory(_walletFactory); + // // Create Engen App + _kintoApp.registerApp("Engen", credits, new address[](0), [uint256(0), uint256(0), uint256(0), uint256(0)]); + + vm.stopBroadcast(); + + // Writes the addresses to a file + console.log("Engen APP created and minted"); + } +} \ No newline at end of file From 1f8cd4b91bf69f6928874bae48606a90c8dbcf9b Mon Sep 17 00:00:00 2001 From: Ramon Recuero Date: Tue, 9 Jan 2024 11:23:06 -0800 Subject: [PATCH 43/43] Makes app registry immutable --- script/migrations/11-create_engen_app.sol | 11 +++++------ src/apps/KintoAppRegistry.sol | 10 +++------- src/interfaces/IKintoAppRegistry.sol | 2 -- test/KintoAppRegistry.t.sol | 6 +++--- test/KintoWalletFactory.t.sol | 2 +- test/helpers/AATestScaffolding.sol | 22 ++++++++++++---------- 6 files changed, 24 insertions(+), 29 deletions(-) diff --git a/script/migrations/11-create_engen_app.sol b/script/migrations/11-create_engen_app.sol index 50098c3d5..0a1cbfc3a 100644 --- a/script/migrations/11-create_engen_app.sol +++ b/script/migrations/11-create_engen_app.sol @@ -38,15 +38,14 @@ contract KintoMigration11DeployScript is Create2Helper, ArtifactsReader { } address credits = _getChainDeployment("EngenCredits"); IKintoAppRegistry _kintoApp = IKintoAppRegistry(_getChainDeployment("KintoAppRegistry")); - - // TODO: This needs to go through the entry point - _kintoApp.setWalletFactory(_walletFactory); - // // Create Engen App + + // TODO: This needs to go through the entry point and the wallet we created in 4 + // Create Engen App _kintoApp.registerApp("Engen", credits, new address[](0), [uint256(0), uint256(0), uint256(0), uint256(0)]); - + vm.stopBroadcast(); // Writes the addresses to a file console.log("Engen APP created and minted"); } -} \ No newline at end of file +} diff --git a/src/apps/KintoAppRegistry.sol b/src/apps/KintoAppRegistry.sol index 3630e62e8..5299497a7 100644 --- a/src/apps/KintoAppRegistry.sol +++ b/src/apps/KintoAppRegistry.sol @@ -31,7 +31,7 @@ contract KintoAppRegistry is uint256 public constant override GAS_LIMIT_THRESHOLD = 1e16; // 0.01 ETH /* ============ State Variables ============ */ - IKintoWalletFactory public override walletFactory; + IKintoWalletFactory public immutable override walletFactory; mapping(address => IKintoAppRegistry.Metadata) private _appMetadata; // other contracts to be sponsored that dont belong in the app mapping(address => mapping(address => bool)) private _sponsoredContracts; @@ -47,8 +47,9 @@ contract KintoAppRegistry is /* ============ Constructor & Initializers ============ */ /// @custom:oz-upgrades-unsafe-allow constructor - constructor() { + constructor(IKintoWalletFactory _walletFactory) { _disableInitializers(); + walletFactory = _walletFactory; } function initialize() external initializer { @@ -66,11 +67,6 @@ contract KintoAppRegistry is // This function is called by the proxy contract when the implementation is upgraded function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} - function setWalletFactory(IKintoWalletFactory _walletFactory) external onlyOwner { - require(address(_walletFactory) != address(0), "Invalid wallet factory address"); - walletFactory = _walletFactory; - } - /* ============ Token name, symbol & URI ============ */ /** diff --git a/src/interfaces/IKintoAppRegistry.sol b/src/interfaces/IKintoAppRegistry.sol index 1780ab8ee..b31caff02 100644 --- a/src/interfaces/IKintoAppRegistry.sol +++ b/src/interfaces/IKintoAppRegistry.sol @@ -36,8 +36,6 @@ interface IKintoAppRegistry { uint256[4] calldata appLimits ) external; - function setWalletFactory(IKintoWalletFactory _walletFactory) external; - /* ============ Basic Viewers ============ */ function name() external pure returns (string memory); diff --git a/test/KintoAppRegistry.t.sol b/test/KintoAppRegistry.t.sol index c9aea6764..9fc382425 100644 --- a/test/KintoAppRegistry.t.sol +++ b/test/KintoAppRegistry.t.sol @@ -30,7 +30,7 @@ contract KintoAppRegistryV2 is KintoAppRegistry { return 1; } - constructor() KintoAppRegistry() {} + constructor(IKintoWalletFactory _walletFactory) KintoAppRegistry(_walletFactory) {} } contract KintoAppRegistryTest is Create2Helper, UserOp, AATestScaffolding { @@ -65,7 +65,7 @@ contract KintoAppRegistryTest is Create2Helper, UserOp, AATestScaffolding { function testOwnerCanUpgradeApp() public { vm.startPrank(_owner); - KintoAppRegistryV2 _implementationV2 = new KintoAppRegistryV2(); + KintoAppRegistryV2 _implementationV2 = new KintoAppRegistryV2(_walletFactory); _kintoAppRegistry.upgradeTo(address(_implementationV2)); // re-wrap the _proxy _kintoApp2 = KintoAppRegistryV2(address(_kintoAppRegistry)); @@ -74,7 +74,7 @@ contract KintoAppRegistryTest is Create2Helper, UserOp, AATestScaffolding { } function test_RevertWhen_OthersCannotUpgradeAppRegistry() public { - KintoAppRegistryV2 _implementationV2 = new KintoAppRegistryV2(); + KintoAppRegistryV2 _implementationV2 = new KintoAppRegistryV2(_walletFactory); vm.expectRevert("Ownable: caller is not the owner"); _kintoAppRegistry.upgradeTo(address(_implementationV2)); } diff --git a/test/KintoWalletFactory.t.sol b/test/KintoWalletFactory.t.sol index 8eced7327..97750c89a 100644 --- a/test/KintoWalletFactory.t.sol +++ b/test/KintoWalletFactory.t.sol @@ -62,7 +62,7 @@ contract KintoWalletFactoryTest is Create2Helper, UserOp, AATestScaffolding { } function testUp() public { - assertEq(_walletFactory.factoryWalletVersion(), 1); + assertEq(_walletFactory.factoryWalletVersion(), 2); assertEq(_entryPoint.walletFactory(), address(_walletFactory)); } diff --git a/test/helpers/AATestScaffolding.sol b/test/helpers/AATestScaffolding.sol index ff9bcf21c..ffd1ea310 100644 --- a/test/helpers/AATestScaffolding.sol +++ b/test/helpers/AATestScaffolding.sol @@ -9,7 +9,7 @@ import "../../src/KintoID.sol"; import {IKintoEntryPoint} from "../../src/interfaces/IKintoEntryPoint.sol"; import {UUPSProxy} from "../helpers/UUPSProxy.sol"; import {KYCSignature} from "../helpers/KYCSignature.sol"; - +import {Create2Helper} from "../helpers/Create2Helper.sol"; import {KintoWalletV3 as KintoWallet} from "../../src/wallet/KintoWallet.sol"; import "../../src/apps/KintoAppRegistry.sol"; import "../../src/tokens/EngenCredits.sol"; @@ -24,7 +24,7 @@ import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import "forge-std/Test.sol"; import "forge-std/console.sol"; -abstract contract AATestScaffolding is KYCSignature { +abstract contract AATestScaffolding is Create2Helper, KYCSignature { using ECDSAUpgradeable for bytes32; using SignatureChecker for address; @@ -54,9 +54,6 @@ abstract contract AATestScaffolding is KYCSignature { // Deploy Kinto ID deployKintoID(_owner, _kycProvider); - // Deploy Kinto App - deployAppRegistry(_owner); - // vm.startPrank(_owner); EntryPoint entry = new EntryPoint{salt: 0}(); _entryPoint = IKintoEntryPoint(address(entry)); @@ -65,6 +62,9 @@ abstract contract AATestScaffolding is KYCSignature { // Deploy wallet & wallet factory deployWalletFactory(_owner); + // Deploy Kinto App + deployAppRegistry(_owner); + // Approve wallet's owner KYC approveKYC(_kycProvider, _owner, _ownerPk); @@ -107,20 +107,18 @@ abstract contract AATestScaffolding is KYCSignature { function deployWalletFactory(address _owner) public { vm.startPrank(_owner); - // Deploy wallet implementation - _kintoWalletImpl = new KintoWallet{salt: 0}(_entryPoint, _kintoIDv1, _kintoAppRegistry); + // Deploy wallet implementation (temporary because of loop dependency on app) + _kintoWalletImpl = new KintoWallet{salt: 0}(_entryPoint, _kintoIDv1, KintoAppRegistry(address(0))); // Deploy wallet factory implementation KintoWalletFactory _implementation2 = new KintoWalletFactory{salt: 0}(_kintoWalletImpl); _proxyFactory = new UUPSProxy{salt: 0}(address(_implementation2), ""); _walletFactory = KintoWalletFactory(address(_proxyFactory)); - // Initialize wallet factory _walletFactory.initialize(_kintoIDv1); // Set the wallet factory in the entry point _entryPoint.setWalletFactory(address(_walletFactory)); - _kintoAppRegistry.setWalletFactory(_walletFactory); vm.stopPrank(); } @@ -168,7 +166,7 @@ abstract contract AATestScaffolding is KYCSignature { vm.startPrank(_owner); // deploy the Kinto App registry - _kintoAppRegistry = new KintoAppRegistry{salt: 0}(); + _kintoAppRegistry = new KintoAppRegistry{salt: 0}(IKintoWalletFactory(_walletFactory)); // deploy _proxy contract and point it to _implementation _proxyRegistry = new UUPSProxy{salt: 0}(address(_kintoAppRegistry), ""); @@ -179,6 +177,10 @@ abstract contract AATestScaffolding is KYCSignature { // initialize proxy _kintoAppRegistry.initialize(); + // Deploy a new wallet implementation an upgrade + // Deploy wallet implementation + _kintoWalletImpl = new KintoWallet{salt: 0}(_entryPoint, _kintoIDv1, _kintoAppRegistry); + _walletFactory.upgradeAllWalletImplementations(_kintoWalletImpl); vm.stopPrank(); }