diff --git a/codecov.yml b/codecov.yml index 23ca894d8..3964ad93b 100644 --- a/codecov.yml +++ b/codecov.yml @@ -3,6 +3,7 @@ ignore: - "script" # ignore scripts/ folder - "lib" # ignore libs/ folder - "src/libraries" # ignore src/libraries/ folder since coverage is not yet supported + - "src/sample" # ignore src/sample/ folder comment: layout: " diff, flags, files" diff --git a/script/migrations/12-create_engen_app.sol b/script/migrations/12-create_engen_app.sol index 8d693140e..3ec5137a4 100644 --- a/script/migrations/12-create_engen_app.sol +++ b/script/migrations/12-create_engen_app.sol @@ -75,37 +75,37 @@ contract KintoMigration12DeployScript is ArtifactsReader, UserOp { flags[0] = true; flags[1] = true; - userOps[0] = this.createUserOperation( + userOps[0] = _createUserOperation( block.chainid, address(_kintoWallet), - nonce, - privateKeys, address(_kintoWallet), 0, + nonce, + privateKeys, abi.encodeWithSelector(IKintoWallet.whitelistApp.selector, apps, flags), address(_paymaster) ); // call initialise on KintoRegistryApp - userOps[1] = this.createUserOperation( + userOps[1] = _createUserOperation( block.chainid, address(_kintoWallet), - nonce + 1, - privateKeys, address(_kintoAppRegistry), 0, + nonce + 1, + privateKeys, abi.encodeWithSelector(KintoAppRegistry.initialize.selector), address(_paymaster) ); // register Engen Credits app into the registry - userOps[2] = this.createUserOperation( + userOps[2] = _createUserOperation( block.chainid, address(_kintoWallet), - nonce + 2, - privateKeys, address(_kintoAppRegistry), 0, + nonce + 2, + privateKeys, abi.encodeWithSignature( "registerApp(string,address,address[],uint256[4])", "Engen", diff --git a/script/migrations/14-factory_v3.sol b/script/migrations/14-factory_v3.sol index 21fa90d8a..36d0e8d9d 100644 --- a/script/migrations/14-factory_v3.sol +++ b/script/migrations/14-factory_v3.sol @@ -74,6 +74,7 @@ contract KintoMigration14DeployScript is Create2Helper, ArtifactsReader { bytecode = abi.encodePacked(type(KintoIDV4).creationCode); _kintoIDImpl = KintoIDV4(payable(_walletFactory.deployContract(vm.envAddress("LEDGER_ADMIN"), 0, bytecode, bytes32(0)))); + vm.stopBroadcast(); // Start admin vm.startBroadcast(); diff --git a/script/migrations/15-faucet_v2.sol b/script/migrations/15-faucet_v2.sol index fc7a4d6bb..3d35260b0 100644 --- a/script/migrations/15-faucet_v2.sol +++ b/script/migrations/15-faucet_v2.sol @@ -88,38 +88,38 @@ contract KintoMigration15DeployScript is Create2Helper, ArtifactsReader, UserOp bool[] memory flags = new bool[](1); flags[0] = true; - userOps[0] = this.createUserOperation( + userOps[0] = _createUserOperation( block.chainid, _from, - nonce, - privateKeys, _from, 0, + nonce, + privateKeys, abi.encodeWithSelector(IKintoWallet.whitelistApp.selector, apps, flags), _getChainDeployment("SponsorPaymaster") ); } // (2). initialise faucet - userOps[1] = this.createUserOperation( + userOps[1] = _createUserOperation( block.chainid, _from, - nonce + 1, - privateKeys, _faucet, 0, + nonce + 1, + privateKeys, abi.encodeWithSelector(Faucet.initialize.selector), _getChainDeployment("SponsorPaymaster") ); // (3). call startFaucet - userOps[2] = this.createUserOperation( + userOps[2] = _createUserOperation( block.chainid, _from, - nonce + 2, - privateKeys, _faucet, 1 ether, + nonce + 2, + privateKeys, abi.encodeWithSelector(Faucet.startFaucet.selector), _getChainDeployment("SponsorPaymaster") ); diff --git a/script/migrations/17-faucet_v3.sol b/script/migrations/17-faucet_v3.sol index 16a6d5ce3..834bf31b3 100644 --- a/script/migrations/17-faucet_v3.sol +++ b/script/migrations/17-faucet_v3.sol @@ -63,13 +63,13 @@ contract KintoMigration15DeployScript is Create2Helper, ArtifactsReader, UserOp uint256[] memory privateKeys = new uint256[](1); privateKeys[0] = _signerPk; UserOperation[] memory userOps = new UserOperation[](1); - userOps[0] = this.createUserOperation( + userOps[0] = _createUserOperation( block.chainid, adminWallet, - nonce, - privateKeys, faucetProxy, 0, + nonce, + privateKeys, abi.encodeWithSelector(UUPSUpgradeable.upgradeTo.selector, address(_newFaucetImpl)), _getChainDeployment("SponsorPaymaster") ); diff --git a/script/test.sol b/script/test.sol index 4406a5e7d..9624d79c3 100644 --- a/script/test.sol +++ b/script/test.sol @@ -140,16 +140,16 @@ contract KintoDeployTestCounter is AASetup, KYCSignature, UserOp { console.log("Counter already has balance to pay for tx", computed); } // Let's send a transaction to the counter contract through our wallet - uint256 startingNonce = _newWallet.getNonce(); + uint256 nonce = _newWallet.getNonce(); uint256[] memory privateKeys = new uint256[](1); privateKeys[0] = deployerPrivateKey; - UserOperation memory userOp = this.createUserOperation( + UserOperation memory userOp = _createUserOperation( block.chainid, address(_newWallet), - startingNonce, - privateKeys, address(counter), 0, + nonce, + privateKeys, abi.encodeWithSignature("increment()"), address(_sponsorPaymaster), [uint256(5000000), 3, 3] @@ -218,16 +218,16 @@ contract KintoDeployETHPriceIsRight is AASetup, KYCSignature, UserOp { console.log("ETHPriceIsRight already has balance to pay for tx", computed); } // Let's send a transaction to the counter contract through our wallet - uint256 startingNonce = _newWallet.getNonce(); + uint256 nonce = _newWallet.getNonce(); uint256[] memory privateKeys = new uint256[](1); privateKeys[0] = deployerPrivateKey; - UserOperation memory userOp = this.createUserOperation( + UserOperation memory userOp = _createUserOperation( block.chainid, address(_newWallet), - startingNonce, - privateKeys, address(ethpriceisright), 0, + nonce, + privateKeys, abi.encodeWithSignature("enterGuess(uint256)", 7000), address(_sponsorPaymaster), [uint256(5000000), 3, 3] diff --git a/src/KintoID.sol b/src/KintoID.sol index fd4f36340..22c663c5b 100644 --- a/src/KintoID.sol +++ b/src/KintoID.sol @@ -168,32 +168,21 @@ contract KintoID is /* ============ Burn ============ */ - /** - * @dev Burns a KYC token. - * @param _signatureData Signature data - */ - function burnKYC(SignatureData calldata _signatureData) external override { - require(balanceOf(_signatureData.signer) > 0, "Nothing to burn"); - - _burnp(tokenOfOwnerByIndex(_signatureData.signer, 0), _signatureData); - } - function burn(uint256 /* tokenId */ ) public pure override { require(false, "Use burnKYC instead"); } /** - * @dev Burns a token. - * @param _tokenId Token ID to be burned + * @dev Burns a KYC token. * @param _signatureData Signature data */ - function _burnp(uint256 _tokenId, SignatureData calldata _signatureData) - private - onlySignerVerified(_signatureData) - { + function burnKYC(SignatureData calldata _signatureData) external override onlySignerVerified(_signatureData) { + require(balanceOf(_signatureData.signer) > 0, "Nothing to burn"); + nonces[_signatureData.signer] += 1; - _burn(_tokenId); + _burn(tokenOfOwnerByIndex(_signatureData.signer, 0)); require(balanceOf(_signatureData.signer) == 0, "Balance after burn must be 0"); + // Update metadata after burning the token Metadata storage meta = _kycmetas[_signatureData.signer]; meta.mintedAt = 0; @@ -218,8 +207,12 @@ contract KintoID is { require(_accounts.length == _traitsAndSanctions.length, "Length mismatch"); require(_accounts.length <= 200, "Too many accounts to monitor at once"); + + uint256 time = block.timestamp; + for (uint256 i = 0; i < _accounts.length; i += 1) { Metadata storage meta = _kycmetas[_accounts[i]]; + if (balanceOf(_accounts[i]) == 0) { continue; } @@ -237,8 +230,9 @@ contract KintoID is } } } - lastMonitoredAt = block.timestamp; - emit AccountsMonitoredAt(msg.sender, _accounts.length, block.timestamp); + + lastMonitoredAt = time; + emit AccountsMonitoredAt(msg.sender, _accounts.length, time); } /** diff --git a/src/paymasters/SponsorPaymaster.sol b/src/paymasters/SponsorPaymaster.sol index f7b1d456b..86810eb57 100644 --- a/src/paymasters/SponsorPaymaster.sol +++ b/src/paymasters/SponsorPaymaster.sol @@ -286,19 +286,19 @@ contract SponsorPaymaster is Initializable, BasePaymaster, UUPSUpgradeable, Reen bytes4 selector = bytes4(callData[:4]); // function selector if (selector == IKintoWallet.executeBatch.selector) { // decode callData for executeBatch - (address[] memory targetContracts,,) = abi.decode(callData[4:], (address[], uint256[], bytes[])); - sponsor = appRegistry.getSponsor(targetContracts[targetContracts.length - 1]); + (address[] memory targets,,) = abi.decode(callData[4:], (address[], uint256[], bytes[])); + sponsor = appRegistry.getSponsor(targets[targets.length - 1]); // last contract must be a contract app - for (uint256 i = 0; i < targetContracts.length - 1; i++) { - if (!appRegistry.isContractSponsored(sponsor, targetContracts[i]) && targetContracts[i] != sender) { + for (uint256 i = 0; i < targets.length - 1; i++) { + if (!appRegistry.isContractSponsored(sponsor, targets[i]) && targets[i] != sender) { revert("SP: executeBatch targets must be sponsored by the contract or be the sender wallet"); } } } else if (selector == IKintoWallet.execute.selector) { // decode callData for execute - (address targetContract,,) = abi.decode(callData[4:], (address, uint256, bytes)); - sponsor = appRegistry.getSponsor(targetContract); + (address target,,) = abi.decode(callData[4:], (address, uint256, bytes)); + sponsor = appRegistry.getSponsor(target); } else { // handle unknown function or error revert("SP: Unknown function selector"); diff --git a/src/wallet/KintoWallet.sol b/src/wallet/KintoWallet.sol index 1e7cec3fc..ecd625c60 100644 --- a/src/wallet/KintoWallet.sol +++ b/src/wallet/KintoWallet.sol @@ -360,24 +360,24 @@ contract KintoWallet is Initializable, BaseAccount, TokenCallbackHandler, IKinto // Compare the selector with the known function selectors if (selector == IKintoWallet.executeBatch.selector) { // Decode callData for executeBatch - (address[] memory targetContracts,,) = abi.decode(callData[4:], (address[], uint256[], bytes[])); - address lastTargetContract = appRegistry.getSponsor(targetContracts[targetContracts.length - 1]); - for (uint256 i = 0; i < targetContracts.length; i++) { + (address[] memory targets,,) = abi.decode(callData[4:], (address[], uint256[], bytes[])); + address lastTargetContract = appRegistry.getSponsor(targets[targets.length - 1]); + for (uint256 i = 0; i < targets.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.isContractSponsored(lastTargetContract, targetContracts[i])) { + if (!appRegistry.isContractSponsored(lastTargetContract, targets[i])) { return address(0); } } return lastTargetContract; } else if (selector == IKintoWallet.execute.selector) { // Decode callData for execute - (address targetContract,,) = abi.decode(callData[4:], (address, uint256, bytes)); + (address target,,) = abi.decode(callData[4:], (address, uint256, bytes)); // Do not allow txs to the wallet via app key - if (targetContract == address(this)) { + if (target == address(this)) { return address(0); } - return targetContract; + return target; } return address(0); } diff --git a/test/EngenCredits.t.sol b/test/EngenCredits.t.sol index cf862a928..f8a8b1f8d 100644 --- a/test/EngenCredits.t.sol +++ b/test/EngenCredits.t.sol @@ -123,27 +123,20 @@ contract EngenCreditsTest is UserOp, AATestScaffolding { assertEq(_engenCredits.calculatePoints(address(_kintoWallet)), 15); vm.startPrank(_owner); // Let's send a transaction to the counter contract through our wallet - uint256 startingNonce = _kintoWallet.getNonce(); + uint256 nonce = _kintoWallet.getNonce(); uint256[] memory privateKeys = new uint256[](1); privateKeys[0] = 1; - UserOperation memory userOp = this.createUserOperation( - _chainID, + UserOperation memory userOp = _createUserOperation( address(_kintoWallet), - startingNonce + 1, - privateKeys, address(_engenCredits), - 0, + nonce + 1, + privateKeys, abi.encodeWithSignature("mintCredits()"), address(_paymaster) ); UserOperation[] memory userOps = new UserOperation[](2); userOps[0] = _whitelistAppOp( - _chainID, - privateKeys, - address(_kintoWallet), - _kintoWallet.getNonce(), - address(_engenCredits), - address(_paymaster) + privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(_engenCredits), address(_paymaster) ); userOps[1] = userOp; // Execute the transaction via the entry point @@ -162,27 +155,20 @@ contract EngenCreditsTest is UserOp, AATestScaffolding { 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 = _kintoWallet.getNonce(); + uint256 nonce = _kintoWallet.getNonce(); uint256[] memory privateKeys = new uint256[](1); privateKeys[0] = 1; - UserOperation memory userOp = this.createUserOperation( - _chainID, + UserOperation memory userOp = _createUserOperation( address(_kintoWallet), - startingNonce + 1, - privateKeys, address(_engenCredits), - 0, + nonce + 1, + privateKeys, abi.encodeWithSignature("mintCredits()"), address(_paymaster) ); UserOperation[] memory userOps = new UserOperation[](2); userOps[0] = _whitelistAppOp( - _chainID, - privateKeys, - address(_kintoWallet), - _kintoWallet.getNonce(), - address(_engenCredits), - address(_paymaster) + privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(_engenCredits), address(_paymaster) ); userOps[1] = userOp; // Execute the transaction via the entry point @@ -196,40 +182,31 @@ contract EngenCreditsTest is UserOp, AATestScaffolding { assertEq(_engenCredits.calculatePoints(address(_kintoWallet)), 15); vm.startPrank(_owner); // Let's send a transaction to the counter contract through our wallet - uint256 startingNonce = _kintoWallet.getNonce(); + uint256 nonce = _kintoWallet.getNonce(); uint256[] memory privateKeys = new uint256[](1); privateKeys[0] = 1; - UserOperation memory userOp = this.createUserOperation( - _chainID, + UserOperation memory userOp = _createUserOperation( address(_kintoWallet), - startingNonce + 1, - privateKeys, address(_engenCredits), - 0, + nonce + 1, + privateKeys, abi.encodeWithSignature("mintCredits()"), address(_paymaster) ); UserOperation[] memory userOps = new UserOperation[](2); userOps[0] = _whitelistAppOp( - _chainID, - privateKeys, - address(_kintoWallet), - _kintoWallet.getNonce(), - address(_engenCredits), - address(_paymaster) + privateKeys, 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(_kintoWallet)), 15); // call again - userOp = this.createUserOperation( - _chainID, + userOp = _createUserOperation( address(_kintoWallet), - startingNonce + 2, - privateKeys, address(_engenCredits), - 0, + nonce + 2, + privateKeys, abi.encodeWithSignature("mintCredits()"), address(_paymaster) ); diff --git a/test/Faucet.t.sol b/test/Faucet.t.sol index 3c37002a0..e5d13ea26 100644 --- a/test/Faucet.t.sol +++ b/test/Faucet.t.sol @@ -137,13 +137,67 @@ contract FaucetTest is UserOp, AATestScaffolding { function testClaimOnBehalf() public { IFaucet.SignatureData memory sigdata = _auxCreateSignature(_user, 3, block.timestamp + 1000); vm.startPrank(_owner); + _faucet.startFaucet{value: 1 ether}(); assertEq(_faucet.claimed(_user), false); assertEq(_faucet.nonces(_user), 0); + _walletFactory.claimFromFaucet(address(_faucet), sigdata); assertEq(_faucet.claimed(_user), true); assertEq(_faucet.nonces(_user), 1); + vm.stopPrank(); } + function testClaimOnBehalf_RevertWhen_CallerIsNotFactory() public { + vm.prank(_owner); + _faucet.startFaucet{value: 1 ether}(); + + IFaucet.SignatureData memory sigdata = _auxCreateSignature(_user, 3, block.timestamp + 1000); + vm.expectRevert("Only wallet factory can call this"); + _faucet.claimKintoETH(sigdata); + } + + function testClaim_RevertWhen_FaucerIsNotActive() public { + vm.prank(_owner); + vm.expectRevert("Faucet is not active"); + _faucet.claimKintoETH(); + } + + // fixme: makes foundry to hang + // function testClaim_DeactivatesWhenNotEnoughBalanceForNextClaim() public { + // vm.prank(_owner); + // _faucet.startFaucet{value: 1 ether}(); + + // for (uint256 i = 1; i <= 2500; i++) { + // vm.prank(vm.addr(i)); + // _faucet.claimKintoETH(); + // } + // // assert faucet is deactivated + // assertEq(address(_faucet).balance, 0); + // assertEq(_faucet.active(), false); + // } + + /* ============ Withdraw Tests ============ */ + + function testWithdrawAll() public { + vm.startPrank(_owner); + + _faucet.startFaucet{value: 1 ether}(); + assertEq(address(_faucet).balance, 1 ether); + + _faucet.withdrawAll(); + assertEq(address(_faucet).balance, 0); + + vm.stopPrank(); + } + + function testWithdrawAll_RevertWhen_CallerIsNotOwner() public { + vm.prank(_owner); + _faucet.startFaucet{value: 1 ether}(); + assertEq(address(_faucet).balance, 1 ether); + + vm.expectRevert("Ownable: caller is not the owner"); + _faucet.withdrawAll(); + } } diff --git a/test/KintoID.t.sol b/test/KintoID.t.sol index 477ac233e..ed26c0421 100644 --- a/test/KintoID.t.sol +++ b/test/KintoID.t.sol @@ -47,8 +47,7 @@ contract KintoIDTest is KYCSignature, AATestScaffolding, UserOp { assertEq(_kintoIDv1.name(), "Kinto ID"); assertEq(_kintoIDv1.symbol(), "KINTOID"); } - - // Upgrade Tests + /* ============ Upgrade tests ============ */ function testOwnerCanUpgrade() public { vm.startPrank(_owner); @@ -93,7 +92,7 @@ contract KintoIDTest is KYCSignature, AATestScaffolding, UserOp { assertEq(_kintoIDv2.newFunction(), 1); } - // Mint Tests + /* ============ Mint tests ============ */ function testMintIndividualKYC() public { IKintoID.SignatureData memory sigdata = _auxCreateSignature(_kintoIDv1, _user, _user, 3, block.timestamp + 1000); @@ -162,7 +161,34 @@ contract KintoIDTest is KYCSignature, AATestScaffolding, UserOp { _kintoIDv1.mintIndividualKyc(sigdata, traits); } - // Burn Tests + function testMintIndividualKYC_RevertWhen_AlreadyMinted() public { + IKintoID.SignatureData memory sigdata = _auxCreateSignature(_kintoIDv1, _user, _user, 3, block.timestamp + 1000); + uint16[] memory traits = new uint16[](0); + + vm.prank(_kycProvider); + _kintoIDv1.mintIndividualKyc(sigdata, traits); + + // try minting again should revert + sigdata = _auxCreateSignature(_kintoIDv1, _user, _user, 3, block.timestamp + 1000); + vm.expectRevert("Balance before mint must be 0"); + vm.prank(_kycProvider); + _kintoIDv1.mintIndividualKyc(sigdata, traits); + } + + /* ============ Burn tests ============ */ + + function testBurnKYC_RevertWhen_UsingBurn() public { + vm.expectRevert("Use burnKYC instead"); + _kintoIDv1.burn(1); + } + + function test_RevertWhen_BurnIsCalled() public { + approveKYC(_kycProvider, _user, _userPk); + uint256 tokenIdx = _kintoIDv1.tokenOfOwnerByIndex(_user, 0); + vm.prank(_user); + vm.expectRevert("Use burnKYC instead"); + _kintoIDv1.burn(tokenIdx); + } function testBurnKYC() public { IKintoID.SignatureData memory sigdata = _auxCreateSignature(_kintoIDv1, _user, _user, 3, block.timestamp + 1000); @@ -211,14 +237,27 @@ contract KintoIDTest is KYCSignature, AATestScaffolding, UserOp { _kintoIDv1.burnKYC(sigdata); } - // Monitor Tests + /* ============ Monitor tests ============ */ + function testMonitorNoChanges() public { vm.startPrank(_kycProvider); _kintoIDv1.monitor(new address[](0), new IKintoID.MonitorUpdateData[][](0)); assertEq(_kintoIDv1.lastMonitoredAt(), block.timestamp); } - function test_RevertWhen_OnlyProviderCanMonitor(address someone) public { + function test_RevertWhen_LenghtMismatch() public { + vm.expectRevert("Length mismatch"); + vm.prank(_kycProvider); + _kintoIDv1.monitor(new address[](2), new IKintoID.MonitorUpdateData[][](1)); + } + + function test_RevertWhen_TooManyAccounts() public { + vm.expectRevert("Too many accounts to monitor at once"); + vm.prank(_kycProvider); + _kintoIDv1.monitor(new address[](201), new IKintoID.MonitorUpdateData[][](201)); + } + + function test_RevertWhen_CallerIsNotProvider(address someone) public { vm.assume(someone != _kycProvider); bytes memory err = abi.encodePacked( "AccessControl: account ", @@ -241,16 +280,19 @@ contract KintoIDTest is KYCSignature, AATestScaffolding, UserOp { assertEq(_kintoIDv1.isSanctionsMonitored(6), false); } - function testSettingTraitsAndSanctions() public { + function testMonitor_WhenPassingTraitsAndSactions() public { approveKYC(_kycProvider, _user, _userPk); // monitor address[] memory accounts = new address[](1); accounts[0] = _user; + IKintoID.MonitorUpdateData[][] memory updates = new IKintoID.MonitorUpdateData[][](1); - updates[0] = new IKintoID.MonitorUpdateData[](2); - updates[0][0] = IKintoID.MonitorUpdateData(true, true, 5); - updates[0][1] = IKintoID.MonitorUpdateData(true, false, 1); // remove 1 + updates[0] = new IKintoID.MonitorUpdateData[](4); + updates[0][0] = IKintoID.MonitorUpdateData(true, true, 5); // add trait 5 + updates[0][1] = IKintoID.MonitorUpdateData(true, false, 1); // remove trait 1 + updates[0][2] = IKintoID.MonitorUpdateData(false, true, 6); // add sanction 6 + updates[0][3] = IKintoID.MonitorUpdateData(false, false, 2); // remove sanction 2 vm.prank(_kycProvider); _kintoIDv1.monitor(accounts, updates); @@ -259,11 +301,13 @@ contract KintoIDTest is KYCSignature, AATestScaffolding, UserOp { assertEq(_kintoIDv1.hasTrait(_user, 1), false); assertEq(_kintoIDv1.isSanctionsSafeIn(_user, 5), true); assertEq(_kintoIDv1.isSanctionsSafeIn(_user, 1), true); + assertEq(_kintoIDv1.isSanctionsSafeIn(_user, 6), false); + assertEq(_kintoIDv1.isSanctionsSafeIn(_user, 2), true); } /* ============ Trait Tests ============ */ - function testAddTrait_WhenCallerIsProvider() public { + function testAddTrait() public { approveKYC(_kycProvider, _user, _userPk); vm.prank(_kycProvider); _kintoIDv1.addTrait(_user, 1); @@ -290,7 +334,7 @@ contract KintoIDTest is KYCSignature, AATestScaffolding, UserOp { _kintoIDv1.addTrait(_user, 1); } - function testRemoveTrait_WhenCallerIsProvider() public { + function testRemoveTrait() public { vm.startPrank(_kycProvider); IKintoID.SignatureData memory sigdata = _auxCreateSignature(_kintoIDv1, _user, _user, 3, block.timestamp + 1000); uint16[] memory traits = new uint16[](1); @@ -304,10 +348,7 @@ contract KintoIDTest is KYCSignature, AATestScaffolding, UserOp { } function testRemoveTrait_RevertWhen_CallerIsNotProvider() public { - uint16[] memory traits = new uint16[](1); - traits[0] = 1; - approveKYC(_kycProvider, _user, _userPk, traits); - assertEq(_kintoIDv1.hasTrait(_user, 1), true); + approveKYC(_kycProvider, _user, _userPk); bytes memory err = abi.encodePacked( "AccessControl: account ", @@ -320,9 +361,33 @@ contract KintoIDTest is KYCSignature, AATestScaffolding, UserOp { _kintoIDv1.removeTrait(_user, 1); } + function testRemoveTrait_RevertWhen_AccountIsNotKYCd() public { + vm.expectRevert("Account must have a KYC token"); + vm.prank(_kycProvider); + _kintoIDv1.removeTrait(_user, 1); + } + + function testTrais() public { + approveKYC(_kycProvider, _user, _userPk); + + vm.startPrank(_kycProvider); + + _kintoIDv1.addTrait(_user, 0); + _kintoIDv1.addTrait(_user, 1); + _kintoIDv1.addTrait(_user, 2); + + vm.stopPrank(); + + bool[] memory traits = _kintoIDv1.traits(_user); + assertEq(traits[0], true); + assertEq(traits[1], true); + assertEq(traits[2], true); + assertEq(traits[3], false); + } + /* ============ Sanction Tests ============ */ - function testProviderCanAddSanction() public { + function testAddSanction() public { vm.startPrank(_kycProvider); IKintoID.SignatureData memory sigdata = _auxCreateSignature(_kintoIDv1, _user, _user, 3, block.timestamp + 1000); uint16[] memory traits = new uint16[](1); @@ -334,7 +399,7 @@ contract KintoIDTest is KYCSignature, AATestScaffolding, UserOp { assertEq(_kintoIDv1.lastMonitoredAt(), block.timestamp); } - function testProviderCanRemoveSancion() public { + function testRemoveSancion() public { vm.startPrank(_kycProvider); IKintoID.SignatureData memory sigdata = _auxCreateSignature(_kintoIDv1, _user, _user, 3, block.timestamp + 1000); uint16[] memory traits = new uint16[](1); @@ -349,9 +414,7 @@ contract KintoIDTest is KYCSignature, AATestScaffolding, UserOp { } function testAddSanction_RevertWhen_CallerIsNotKYCProvider() public { - uint16[] memory traits = new uint16[](1); - traits[0] = 1; - approveKYC(_kycProvider, _user, _userPk, traits); + approveKYC(_kycProvider, _user, _userPk, new uint16[](1)); bytes memory err = abi.encodePacked( "AccessControl: account ", @@ -382,6 +445,18 @@ contract KintoIDTest is KYCSignature, AATestScaffolding, UserOp { _kintoIDv1.removeSanction(_user2, 1); } + function testAddSanction_RevertWhen_AccountIsNotKYCd() public { + vm.expectRevert("Account must have a KYC token"); + vm.prank(_kycProvider); + _kintoIDv1.addSanction(_user, 1); + } + + function testRemoveSanction_RevertWhen_AccountIsNotKYCd() public { + vm.expectRevert("Account must have a KYC token"); + vm.prank(_kycProvider); + _kintoIDv1.removeSanction(_user, 1); + } + /* ============ Transfer Tests ============ */ function test_RevertWhen_TransfersAreDisabled() public { @@ -392,14 +467,6 @@ contract KintoIDTest is KYCSignature, AATestScaffolding, UserOp { _kintoIDv1.safeTransferFrom(_user, _user2, tokenIdx); } - function test_RevertWhen_BurnIsCalled() public { - approveKYC(_kycProvider, _user, _userPk); - uint256 tokenIdx = _kintoIDv1.tokenOfOwnerByIndex(_user, 0); - vm.prank(_user); - vm.expectRevert("Use burnKYC instead"); - _kintoIDv1.burn(tokenIdx); - } - function testDappSignature() public { // vm.startPrank(_kycProvider); // bytes memory sig = hex"0fcafa82e64fcfd3c38209e23270274132e88061f1718c7ff45e8c0ddbbe7cdd59b5af57e10a5d8221baa6ae37b57d02acace7e25fc29cb4025f15269e0939aa1b"; @@ -414,4 +481,17 @@ contract KintoIDTest is KYCSignature, AATestScaffolding, UserOp { // ); // assertEq(valid, true); } + + /* ============ Supports Interface tests ============ */ + + function testSupportsInterface() public { + bytes4 InterfaceERC721Upgradeable = bytes4(keccak256("balanceOf(address)")) + ^ bytes4(keccak256("ownerOf(uint256)")) ^ bytes4(keccak256("safeTransferFrom(address,address,uint256,bytes)")) + ^ bytes4(keccak256("safeTransferFrom(address,address,uint256)")) + ^ bytes4(keccak256("transferFrom(address,address,uint256)")) ^ bytes4(keccak256("approve(address,uint256)")) + ^ bytes4(keccak256("setApprovalForAll(address,bool)")) ^ bytes4(keccak256("getApproved(uint256)")) + ^ bytes4(keccak256("isApprovedForAll(address,address)")); + + assertTrue(_kintoIDv1.supportsInterface(InterfaceERC721Upgradeable)); + } } diff --git a/test/KintoWallet.t.sol b/test/KintoWallet.t.sol index b6507e349..da773f7d6 100644 --- a/test/KintoWallet.t.sol +++ b/test/KintoWallet.t.sol @@ -15,7 +15,6 @@ import {AATestScaffolding} from "./helpers/AATestScaffolding.sol"; contract KintoWalletTest is AATestScaffolding, UserOp { uint256[] privateKeys; - uint256 _chainID = 1; // events event UserOperationRevertReason( @@ -26,8 +25,6 @@ contract KintoWalletTest is AATestScaffolding, UserOp { event RecovererChanged(address indexed newRecoverer, address indexed recoverer); function setUp() public { - vm.chainId(_chainID); - deployAAScaffolding(_owner, 1, _kycProvider, _recoverer); // Add paymaster to _kintoWallet @@ -47,20 +44,18 @@ contract KintoWalletTest is AATestScaffolding, UserOp { /* ============ Upgrade Tests ============ */ - // FIXME: these I think these upgrade tests are wrong because, basically, the KintoWallet.sol does not have + // FIXME: 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); // try calling upgradeTo from _owner wallet to upgrade _owner wallet - UserOperation memory userOp = this.createUserOperation( - _chainID, + UserOperation memory userOp = _createUserOperation( + address(_kintoWallet), address(_kintoWallet), _kintoWallet.getNonce(), privateKeys, - address(_kintoWallet), - 0, abi.encodeWithSignature("upgradeTo(address)", address(_newImplementation)), address(_paymaster) ); @@ -89,13 +84,11 @@ contract KintoWalletTest is AATestScaffolding, UserOp { uint256 nonce = userWallet.getNonce(); privateKeys[0] = _userPk; - UserOperation memory userOp = this.createUserOperation( - _chainID, + UserOperation memory userOp = _createUserOperation( address(userWallet), + address(_kintoWallet), nonce, privateKeys, - address(_kintoWallet), - 0, abi.encodeWithSignature("upgradeTo(address)", address(_newImplementation)), address(_paymaster) ); @@ -115,7 +108,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { vm.stopPrank(); } - /* ============ One Signer Account Transaction Tests ============ */ + /* ============ One Signer Account Transaction Tests (execute) ============ */ function test_RevertWhen_SendingTransactionDirectlyAndPrefundNotPaid() public { // deploy the counter contract @@ -125,13 +118,11 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // send a transaction to the counter contract through our wallet // without a paymaster and without prefunding the wallet - UserOperation memory userOp = this.createUserOperation( - _chainID, + UserOperation memory userOp = _createUserOperation( address(_kintoWallet), + address(counter), _kintoWallet.getNonce(), privateKeys, - address(counter), - 0, abi.encodeWithSignature("increment()") ); UserOperation[] memory userOps = new UserOperation[](1); @@ -154,18 +145,16 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // whitelist app userOps[0] = _whitelistAppOp( - _chainID, privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(counter), address(_paymaster) + 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, + userOps[1] = _createUserOperation( address(_kintoWallet), + address(counter), _kintoWallet.getNonce() + 1, privateKeys, - address(counter), - 0, abi.encodeWithSignature("increment()") ); @@ -184,13 +173,11 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // (3). Create Counter increment user op UserOperation[] memory userOps = new UserOperation[](1); - userOps[0] = this.createUserOperation( - _chainID, + userOps[0] = _createUserOperation( address(_kintoWallet), + address(counter), _kintoWallet.getNonce(), privateKeys, - address(counter), - 0, abi.encodeWithSignature("increment()"), address(_paymaster) ); @@ -219,13 +206,11 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // (4). Create Counter increment user op UserOperation[] memory userOps = new UserOperation[](1); - userOps[0] = this.createUserOperation( - _chainID, + userOps[0] = _createUserOperation( address(_kintoWallet), + address(counter), _kintoWallet.getNonce(), privateKeys, - address(counter), - 0, abi.encodeWithSignature("increment()"), address(_paymaster) ); @@ -254,19 +239,17 @@ contract KintoWalletTest is AATestScaffolding, UserOp { uint256 nonce = _kintoWallet.getNonce(); bool[] memory flags = new bool[](1); flags[0] = true; - UserOperation memory userOp2 = this.createUserOperation( - _chainID, + UserOperation memory userOp2 = _createUserOperation( address(_kintoWallet), + address(counter), nonce + 1, privateKeys, - address(counter), - 0, abi.encodeWithSignature("increment()"), address(_paymaster) ); UserOperation[] memory userOps = new UserOperation[](2); userOps[0] = _whitelistAppOp( - _chainID, privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(counter), address(_paymaster) + privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(counter), address(_paymaster) ); userOps[1] = userOp2; // Execute the transactions via the entry point @@ -286,29 +269,24 @@ contract KintoWalletTest is AATestScaffolding, UserOp { vm.startPrank(_owner); _fundPaymasterForContract(address(counter)); // Let's send a transaction to the counter contract through our wallet - UserOperation memory userOp = this.createUserOperation( - _chainID, + UserOperation memory userOp = _createUserOperation( address(_kintoWallet), + address(counter), nonce + 1, privateKeys, - address(counter), - 0, abi.encodeWithSignature("increment()"), address(_paymaster) ); - UserOperation memory userOp2 = this.createUserOperation( - _chainID, + UserOperation memory userOp2 = _createUserOperation( address(_kintoWallet), + address(counter), nonce + 2, privateKeys, - address(counter), - 0, abi.encodeWithSignature("increment()"), address(_paymaster) ); UserOperation[] memory userOps = new UserOperation[](3); - userOps[0] = - _whitelistAppOp(_chainID, privateKeys, address(_kintoWallet), nonce, address(counter), address(_paymaster)); + userOps[0] = _whitelistAppOp(privateKeys, address(_kintoWallet), nonce, address(counter), address(_paymaster)); userOps[1] = userOp; userOps[2] = userOp2; // Execute the transaction via the entry point @@ -317,6 +295,8 @@ contract KintoWalletTest is AATestScaffolding, UserOp { vm.stopPrank(); } + /* ============ One Signer Account Transaction Tests (executeBatch) ============ */ + function testMultipleTransactionsExecuteBatchPaymaster() public { vm.startPrank(_owner); // Let's deploy the counter contract @@ -344,10 +324,9 @@ contract KintoWalletTest is AATestScaffolding, UserOp { 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(_kintoWallet), nonce, privateKeys, opParams, address(_paymaster) - ); + OperationParamsBatch memory opParams = OperationParamsBatch({targets: targets, values: values, bytesOps: calls}); + UserOperation memory userOp = + _createUserOperation(address(_kintoWallet), nonce, privateKeys, opParams, address(_paymaster)); UserOperation[] memory userOps = new UserOperation[](1); userOps[0] = userOp; // Execute the transaction via the entry point @@ -392,9 +371,9 @@ contract KintoWalletTest is AATestScaffolding, UserOp { 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(_kintoWallet), _kintoWallet.getNonce(), privateKeys, opParams, address(_paymaster) + OperationParamsBatch memory opParams = OperationParamsBatch({targets: targets, values: values, bytesOps: calls}); + UserOperation memory userOp = _createUserOperation( + address(_kintoWallet), _kintoWallet.getNonce(), privateKeys, opParams, address(_paymaster) ); UserOperation[] memory userOps = new UserOperation[](1); userOps[0] = userOp; @@ -428,13 +407,11 @@ contract KintoWalletTest is AATestScaffolding, UserOp { owners[0] = _owner; owners[1] = _user; uint256 nonce = _kintoWallet.getNonce(); - UserOperation memory userOp = this.createUserOperation( - _chainID, + UserOperation memory userOp = _createUserOperation( + address(_kintoWallet), address(_kintoWallet), nonce, privateKeys, - address(_kintoWallet), - 0, abi.encodeWithSignature("resetSigners(address[],uint8)", owners, _kintoWallet.signerPolicy()), address(_paymaster) ); @@ -451,13 +428,11 @@ contract KintoWalletTest is AATestScaffolding, UserOp { owners[0] = _owner; owners[1] = _owner; - UserOperation memory userOp = this.createUserOperation( - _chainID, + UserOperation memory userOp = _createUserOperation( + address(_kintoWallet), address(_kintoWallet), _kintoWallet.getNonce(), privateKeys, - address(_kintoWallet), - 0, abi.encodeWithSignature("resetSigners(address[],uint8)", owners, _kintoWallet.signerPolicy()), address(_paymaster) ); @@ -478,13 +453,11 @@ contract KintoWalletTest is AATestScaffolding, UserOp { function test_RevertWhen_WithEmptyArray() public { address[] memory owners = new address[](0); - UserOperation memory userOp = this.createUserOperation( - _chainID, + UserOperation memory userOp = _createUserOperation( + address(_kintoWallet), address(_kintoWallet), _kintoWallet.getNonce(), privateKeys, - address(_kintoWallet), - 0, abi.encodeWithSignature("resetSigners(address[],uint8)", owners, _kintoWallet.signerPolicy()), address(_paymaster) ); @@ -509,13 +482,11 @@ contract KintoWalletTest is AATestScaffolding, UserOp { owners[2] = _user; owners[3] = _user; - UserOperation memory userOp = this.createUserOperation( - _chainID, + UserOperation memory userOp = _createUserOperation( + address(_kintoWallet), address(_kintoWallet), _kintoWallet.getNonce(), privateKeys, - address(_kintoWallet), - 0, abi.encodeWithSignature("resetSigners(address[],uint8)", owners, _kintoWallet.signerPolicy()), address(_paymaster) ); @@ -537,13 +508,11 @@ contract KintoWalletTest is AATestScaffolding, UserOp { address[] memory owners = new address[](1); owners[0] = _user; - UserOperation memory userOp = this.createUserOperation( - _chainID, + UserOperation memory userOp = _createUserOperation( + address(_kintoWallet), address(_kintoWallet), _kintoWallet.getNonce(), privateKeys, - address(_kintoWallet), - 0, abi.encodeWithSignature("resetSigners(address[],uint8)", owners, _kintoWallet.signerPolicy()), address(_paymaster) ); @@ -560,13 +529,11 @@ contract KintoWalletTest is AATestScaffolding, UserOp { owners[0] = _owner; owners[1] = _user; uint256 nonce = _kintoWallet.getNonce(); - UserOperation memory userOp = this.createUserOperation( - _chainID, + UserOperation memory userOp = _createUserOperation( + address(_kintoWallet), address(_kintoWallet), nonce, privateKeys, - address(_kintoWallet), - 0, abi.encodeWithSignature("resetSigners(address[],uint8)", owners, _kintoWallet.ALL_SIGNERS()), address(_paymaster) ); @@ -586,13 +553,11 @@ contract KintoWalletTest is AATestScaffolding, UserOp { owners[1] = _user; owners[2] = _user2; uint256 nonce = _kintoWallet.getNonce(); - UserOperation memory userOp = this.createUserOperation( - _chainID, + UserOperation memory userOp = _createUserOperation( + address(_kintoWallet), address(_kintoWallet), nonce, privateKeys, - address(_kintoWallet), - 0, abi.encodeWithSignature("resetSigners(address[],uint8)", owners, _kintoWallet.MINUS_ONE_SIGNER()), address(_paymaster) ); @@ -616,25 +581,21 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // 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.createUserOperation( - _chainID, + userOps[0] = _createUserOperation( + address(_kintoWallet), address(_kintoWallet), nonce, privateKeys, - address(_kintoWallet), - 0, 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.createUserOperation( - _chainID, + userOps[1] = _createUserOperation( + address(_kintoWallet), address(_kintoWallet), nonce + 1, privateKeys, - address(_kintoWallet), - 0, abi.encodeWithSignature("resetSigners(address[], uint8)", owners, _kintoWallet.signerPolicy()), address(_paymaster) ); @@ -672,13 +633,11 @@ contract KintoWalletTest is AATestScaffolding, UserOp { owners[0] = _owner; owners[1] = _user; - UserOperation memory userOp = this.createUserOperation( - _chainID, + UserOperation memory userOp = _createUserOperation( + address(_kintoWallet), address(_kintoWallet), _kintoWallet.getNonce(), privateKeys, - address(_kintoWallet), - 0, abi.encodeWithSignature("resetSigners(address[],uint8)", owners, _kintoWallet.ALL_SIGNERS()), address(_paymaster) ); @@ -710,17 +669,15 @@ contract KintoWalletTest is AATestScaffolding, UserOp { userOps = new UserOperation[](2); // a. whitelist app userOps[0] = _whitelistAppOp( - _chainID, privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(counter), address(_paymaster) + privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(counter), address(_paymaster) ); // b. Counter increment - userOps[1] = this.createUserOperation( - _chainID, + userOps[1] = _createUserOperation( address(_kintoWallet), + address(counter), _kintoWallet.getNonce() + 1, privateKeys, - address(counter), - 0, abi.encodeWithSignature("increment()"), address(_paymaster) ); @@ -736,13 +693,11 @@ contract KintoWalletTest is AATestScaffolding, UserOp { owners[0] = _owner; owners[1] = _user; - UserOperation memory userOp = this.createUserOperation( - _chainID, + UserOperation memory userOp = _createUserOperation( + address(_kintoWallet), address(_kintoWallet), _kintoWallet.getNonce(), privateKeys, - address(_kintoWallet), - 0, abi.encodeWithSignature("resetSigners(address[],uint8)", owners, _kintoWallet.ALL_SIGNERS()), address(_paymaster) ); @@ -769,17 +724,15 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // a. whitelist app userOps[0] = _whitelistAppOp( - _chainID, privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(counter), address(_paymaster) + privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(counter), address(_paymaster) ); // b. Counter increment - userOps[1] = this.createUserOperation( - _chainID, + userOps[1] = _createUserOperation( address(_kintoWallet), + address(counter), _kintoWallet.getNonce() + 1, privateKeys, - address(counter), - 0, abi.encodeWithSignature("increment()"), address(_paymaster) ); @@ -796,13 +749,11 @@ contract KintoWalletTest is AATestScaffolding, UserOp { owners[1] = _user; owners[2] = _user2; - UserOperation memory userOp = this.createUserOperation( - _chainID, + UserOperation memory userOp = _createUserOperation( + address(_kintoWallet), address(_kintoWallet), _kintoWallet.getNonce(), privateKeys, - address(_kintoWallet), - 0, abi.encodeWithSignature("resetSigners(address[],uint8)", owners, _kintoWallet.ALL_SIGNERS()), address(_paymaster) ); @@ -835,16 +786,14 @@ contract KintoWalletTest is AATestScaffolding, UserOp { userOps = new UserOperation[](2); // a. whitelist app userOps[0] = _whitelistAppOp( - _chainID, privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(counter), address(_paymaster) + privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(counter), address(_paymaster) ); // b. Counter increment - userOps[1] = this.createUserOperation( - _chainID, + userOps[1] = _createUserOperation( address(_kintoWallet), + address(counter), _kintoWallet.getNonce() + 1, privateKeys, - address(counter), - 0, abi.encodeWithSignature("increment()"), address(_paymaster) ); @@ -868,17 +817,15 @@ contract KintoWalletTest is AATestScaffolding, UserOp { userOps = new UserOperation[](2); // a. whitelist app userOps[0] = _whitelistAppOp( - _chainID, privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(counter), address(_paymaster) + privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(counter), address(_paymaster) ); // b. Counter increment - userOps[1] = this.createUserOperation( - _chainID, + userOps[1] = _createUserOperation( address(_kintoWallet), + address(counter), _kintoWallet.getNonce() + 1, privateKeys, - address(counter), - 0, abi.encodeWithSignature("increment()"), address(_paymaster) ); @@ -896,13 +843,11 @@ contract KintoWalletTest is AATestScaffolding, UserOp { owners[1] = _user; owners[2] = _user2; - UserOperation memory userOp = this.createUserOperation( - _chainID, + UserOperation memory userOp = _createUserOperation( + address(_kintoWallet), address(_kintoWallet), _kintoWallet.getNonce(), privateKeys, - address(_kintoWallet), - 0, abi.encodeWithSignature("resetSigners(address[],uint8)", owners, _kintoWallet.ALL_SIGNERS()), address(_paymaster) ); @@ -935,17 +880,15 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // a. whitelist app userOps[0] = _whitelistAppOp( - _chainID, privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(counter), address(_paymaster) + privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(counter), address(_paymaster) ); // b. Counter increment - userOps[1] = this.createUserOperation( - _chainID, + userOps[1] = _createUserOperation( address(_kintoWallet), + address(counter), _kintoWallet.getNonce() + 1, privateKeys, - address(counter), - 0, abi.encodeWithSignature("increment()"), address(_paymaster) ); @@ -1116,13 +1059,11 @@ contract KintoWalletTest is AATestScaffolding, UserOp { uint256 nonce = _kintoWallet.getNonce(); bool[] memory flags = new bool[](1); flags[0] = true; - UserOperation memory userOp = this.createUserOperation( - _chainID, + UserOperation memory userOp = _createUserOperation( + address(_kintoWallet), address(_kintoWallet), nonce, privateKeys, - address(_kintoWallet), - 0, abi.encodeWithSignature("setFunderWhitelist(address[],bool[])", funders, flags), address(_paymaster) ); @@ -1139,13 +1080,11 @@ contract KintoWalletTest is AATestScaffolding, UserOp { function test_RevertWhen_SettingAppKeyNoWhitelist() public { address app = address(_engenCredits); registerApp(_owner, "test", address(_engenCredits)); - UserOperation memory userOp = this.createUserOperation( - _chainID, + UserOperation memory userOp = _createUserOperation( + address(_kintoWallet), address(_kintoWallet), _kintoWallet.getNonce(), privateKeys, - address(_kintoWallet), - 0, abi.encodeWithSignature("setAppKey(address,address)", app, _user), address(_paymaster) ); @@ -1171,17 +1110,14 @@ contract KintoWalletTest is AATestScaffolding, UserOp { registerApp(_owner, "test", address(_engenCredits)); UserOperation[] memory userOps = new UserOperation[](2); - userOps[0] = _whitelistAppOp( - _chainID, privateKeys, address(_kintoWallet), nonce, address(_engenCredits), address(_paymaster) - ); + userOps[0] = + _whitelistAppOp(privateKeys, address(_kintoWallet), nonce, address(_engenCredits), address(_paymaster)); - userOps[1] = this.createUserOperation( - _chainID, + userOps[1] = _createUserOperation( + address(_kintoWallet), address(_kintoWallet), nonce + 1, privateKeys, - address(_kintoWallet), - 0, abi.encodeWithSignature("setAppKey(address,address)", app, _user), address(_paymaster) ); @@ -1199,13 +1135,11 @@ contract KintoWalletTest is AATestScaffolding, UserOp { owners[1] = _user2; // generate the user operation wihch changes the policy to ALL_SIGNERS - UserOperation memory userOp = this.createUserOperation( - _chainID, + UserOperation memory userOp = _createUserOperation( + address(_kintoWallet), address(_kintoWallet), _kintoWallet.getNonce(), privateKeys, - address(_kintoWallet), - 0, abi.encodeWithSignature("resetSigners(address[],uint8)", owners, _kintoWallet.ALL_SIGNERS()), address(_paymaster) ); @@ -1233,32 +1167,27 @@ contract KintoWalletTest is AATestScaffolding, UserOp { privateKeys[0] = _ownerPk; privateKeys[1] = _user2Pk; userOps[0] = _whitelistAppOp( - _chainID, privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(counter), address(_paymaster) + privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(counter), address(_paymaster) ); - userOps[1] = this.createUserOperation( - _chainID, + userOps[1] = _createUserOperation( + address(_kintoWallet), address(_kintoWallet), _kintoWallet.getNonce() + 1, privateKeys, - address(_kintoWallet), - 0, abi.encodeWithSignature("setAppKey(address,address)", address(counter), _user), address(_paymaster) ); _entryPoint.handleOps(userOps, payable(_owner)); userOps = new UserOperation[](1); - console.log("counter address", address(counter)); - console.log("user address", _user); + // Set only app key signature uint256[] memory privateKeysApp = new uint256[](1); privateKeysApp[0] = 3; - userOps[0] = this.createUserOperation( - _chainID, + userOps[0] = _createUserOperation( address(_kintoWallet), + address(counter), _kintoWallet.getNonce(), privateKeysApp, - address(counter), - 0, abi.encodeWithSignature("increment()"), address(_paymaster) ); @@ -1285,7 +1214,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // (4). Create whitelist app user op UserOperation[] memory userOps = new UserOperation[](1); userOps[0] = _whitelistAppOp( - _chainID, privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(counter), address(_paymaster) + privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(counter), address(_paymaster) ); // (5). execute the transaction via the entry point @@ -1303,7 +1232,7 @@ contract KintoWalletTest is AATestScaffolding, UserOp { // // (3). Create whitelist app user op // UserOperation[] memory userOps = new UserOperation[](1); // userOps[0] = _whitelistAppOp( - // _chainID, privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(counter), address(_paymaster) + // privateKeys, address(_kintoWallet), _kintoWallet.getNonce(), address(counter), address(_paymaster) // ); // // (4). execute the transaction via the entry point and expect a revert event diff --git a/test/KintoWalletFactory.t.sol b/test/KintoWalletFactory.t.sol index ae11049a1..c48e06568 100644 --- a/test/KintoWalletFactory.t.sol +++ b/test/KintoWalletFactory.t.sol @@ -41,10 +41,7 @@ contract KintoWalletFactoryTest is UserOp, AATestScaffolding { KintoWalletFactoryUpgrade _walletFactoryv2; KintoWalletUpgrade _kintoWalletv2; - uint256 _chainID = 1; - function setUp() public { - vm.chainId(_chainID); vm.startPrank(address(1)); _owner.transfer(1e18); vm.stopPrank(); @@ -139,20 +136,18 @@ contract KintoWalletFactoryTest is UserOp, AATestScaffolding { function testWhitelistedSignerCanFundWallet() public { vm.startPrank(_owner); _fundPaymasterForContract(address(_kintoWallet)); - uint256 startingNonce = _kintoWallet.getNonce(); + uint256 nonce = _kintoWallet.getNonce(); address[] memory funders = new address[](1); funders[0] = _funder; bool[] memory flags = new bool[](1); flags[0] = true; uint256[] memory privateKeys = new uint256[](1); privateKeys[0] = 1; - UserOperation memory userOp = this.createUserOperation( - _chainID, + UserOperation memory userOp = _createUserOperation( address(_kintoWallet), - startingNonce, - privateKeys, address(_kintoWallet), - 0, + nonce, + privateKeys, abi.encodeWithSignature("setFunderWhitelist(address[],bool[])", funders, flags), address(_paymaster) ); diff --git a/test/SponsorPaymastExploit.t.sol b/test/SponsorPaymastExploit.t.sol index ed542153d..0aef917c1 100644 --- a/test/SponsorPaymastExploit.t.sol +++ b/test/SponsorPaymastExploit.t.sol @@ -23,7 +23,7 @@ contract MyOpCreator is UserOp, KYCSignature { address _account, uint256 nonce, uint256[] calldata _privateKeyOwners, - address _targetContract, + address _target, uint256 value, bytes calldata _bytesOp, address _paymaster @@ -32,7 +32,7 @@ contract MyOpCreator is UserOp, KYCSignature { sender: _account, nonce: nonce, initCode: bytes(""), - callData: abi.encodeCall(KintoWallet.execute, (_targetContract, value, _bytesOp)), + callData: abi.encodeCall(KintoWallet.execute, (_target, value, _bytesOp)), callGasLimit: 40000, // generate from call simulation verificationGasLimit: 150000, // verification gas. will add create2 cost (3200+200*length) if initCode exists preVerificationGas: 99e18, // should also cover calldata cost. @@ -71,14 +71,14 @@ contract SponsorPaymasterExploitTest is MyOpCreator, AATestScaffolding { _fundPaymasterForContract(address(counter)); vm.startPrank(_owner); // Let's send a transaction to the counter contract through our wallet - uint256 startingNonce = _kintoWallet.getNonce(); + uint256 nonce = _kintoWallet.getNonce(); uint256[] memory privateKeys = new uint256[](1); privateKeys[0] = 1; UserOperation memory userOp = this._createOp( _chainID, address(_kintoWallet), - startingNonce, + nonce, privateKeys, address(counter), 0, diff --git a/test/SponsorPaymaster.t.sol b/test/SponsorPaymaster.t.sol index cc0f7785b..be253fb8d 100644 --- a/test/SponsorPaymaster.t.sol +++ b/test/SponsorPaymaster.t.sol @@ -32,7 +32,6 @@ contract SponsorPaymasterTest is KYCSignature, UserOp, AATestScaffolding { using ECDSAUpgradeable for bytes32; using SignatureChecker for address; - uint256 _chainID = 1; uint256[] privateKeys; function setUp() public { @@ -153,13 +152,11 @@ contract SponsorPaymasterTest is KYCSignature, UserOp, AATestScaffolding { /* ============ PER-OP: Global Rate limits ============ */ function testValidatePaymasterUserOp() public { - UserOperation memory userOp = this.createUserOperation( - _chainID, + UserOperation memory userOp = _createUserOperation( + address(_kintoWallet), address(_kintoWallet), _kintoWallet.getNonce(), privateKeys, - address(_kintoWallet), - 0, abi.encodeWithSignature("increment()"), address(_paymaster) ); @@ -169,13 +166,11 @@ contract SponsorPaymasterTest is KYCSignature, UserOp, AATestScaffolding { } function testValidatePaymasterUserOp_RevertWhen_GasLimitIsLessThanCostOfPost() public { - UserOperation memory userOp = this.createUserOperation( - _chainID, + UserOperation memory userOp = _createUserOperation( + address(_kintoWallet), address(_kintoWallet), _kintoWallet.getNonce(), privateKeys, - address(_kintoWallet), - 0, abi.encodeWithSignature("increment()"), address(_paymaster) ); @@ -189,13 +184,11 @@ contract SponsorPaymasterTest is KYCSignature, UserOp, AATestScaffolding { } function testValidatePaymasterUserOp_RevertWhen_GasLimitIsMoreThanCostOfVerification() public { - UserOperation memory userOp = this.createUserOperation( - _chainID, + UserOperation memory userOp = _createUserOperation( + address(_kintoWallet), address(_kintoWallet), _kintoWallet.getNonce(), privateKeys, - address(_kintoWallet), - 0, abi.encodeWithSignature("increment()"), address(_paymaster) ); @@ -209,13 +202,11 @@ contract SponsorPaymasterTest is KYCSignature, UserOp, AATestScaffolding { } function testValidatePaymasterUserOp_RevertWhen_PreGasLimitIsMoreThanMaxPreVerification() public { - UserOperation memory userOp = this.createUserOperation( - _chainID, + UserOperation memory userOp = _createUserOperation( + address(_kintoWallet), address(_kintoWallet), _kintoWallet.getNonce(), privateKeys, - address(_kintoWallet), - 0, abi.encodeWithSignature("increment()"), address(_paymaster) ); @@ -229,13 +220,11 @@ contract SponsorPaymasterTest is KYCSignature, UserOp, AATestScaffolding { } function testValidatePaymasterUserOp_RevertWhen_PaymasterAndDataIsNotLength20() public { - UserOperation memory userOp = this.createUserOperation( - _chainID, + UserOperation memory userOp = _createUserOperation( + address(_kintoWallet), address(_kintoWallet), _kintoWallet.getNonce(), privateKeys, - address(_kintoWallet), - 0, abi.encodeWithSignature("increment()"), address(_paymaster) ); @@ -249,13 +238,11 @@ contract SponsorPaymasterTest is KYCSignature, UserOp, AATestScaffolding { } function testValidatePaymasterUserOp_RevertWhen_GasIsTooHigh() public { - UserOperation memory userOp = this.createUserOperation( - _chainID, + UserOperation memory userOp = _createUserOperation( + address(_kintoWallet), address(_kintoWallet), _kintoWallet.getNonce(), privateKeys, - address(_kintoWallet), - 0, abi.encodeWithSignature("increment()"), address(_paymaster) ); @@ -541,13 +528,11 @@ contract SponsorPaymasterTest is KYCSignature, UserOp, AATestScaffolding { userOps = new UserOperation[](amt); // we iterate from 1 because the first op is whitelisting the app for (uint256 i = 0; i < amt; i++) { - userOps[i] = this.createUserOperation( - _chainID, + userOps[i] = _createUserOperation( address(_kintoWallet), + address(app), nonce, privateKeys, - address(app), - 0, abi.encodeWithSignature("increment()"), address(_paymaster) ); diff --git a/test/helpers/UserOp.sol b/test/helpers/UserOp.sol index f2ae6e84d..55c5d3ec4 100644 --- a/test/helpers/UserOp.sol +++ b/test/helpers/UserOp.sol @@ -34,12 +34,163 @@ abstract contract UserOp is Test { address payable _recoverer = payable(vm.addr(_recovererPk)); address payable _funder = payable(vm.addr(_funderPk)); - struct OperationParams { - address[] targetContracts; + // constants + uint256 constant CHAIN_ID = 1; + + // gas constants + uint256 constant CALL_GAS_LIMIT = 4_000_000; + uint256 constant VERIFICATION_GAS_LIMIT = 210_000; + uint256 constant PRE_VERIFICATION_GAS = 21_000; + uint256 constant MAX_FEE_PER_GAS = 1; + uint256 constant MAX_PRIORITY_FEE_PER_GAS = 1e9; + + struct OperationParamsBatch { + address[] targets; uint256[] values; bytes[] bytesOps; } + function _createUserOperation( + address _from, + address _target, + uint256 _nonce, + uint256[] memory _privateKeyOwners, + bytes memory _bytesOp + ) internal view returns (UserOperation memory op) { + return _createUserOperation( + CHAIN_ID, + _from, + _target, + 0, + _nonce, + _privateKeyOwners, + _bytesOp, + address(0), + [CALL_GAS_LIMIT, MAX_FEE_PER_GAS, MAX_PRIORITY_FEE_PER_GAS] + ); + } + + // with paymaster + function _createUserOperation( + address _from, + address _target, + uint256 _nonce, + uint256[] memory _privateKeyOwners, + bytes memory _bytesOp, + address _paymaster + ) internal view returns (UserOperation memory op) { + return _createUserOperation( + CHAIN_ID, + _from, + _target, + 0, + _nonce, + _privateKeyOwners, + _bytesOp, + _paymaster, + [CALL_GAS_LIMIT, MAX_FEE_PER_GAS, MAX_PRIORITY_FEE_PER_GAS] + ); + } + + // with chain ID and paymaster + function _createUserOperation( + uint256 _chainID, + address _from, + address _target, + uint256 _value, + uint256 _nonce, + uint256[] memory _privateKeyOwners, + bytes memory _bytesOp, + address _paymaster + ) internal view returns (UserOperation memory op) { + return _createUserOperation( + _chainID, + _from, + _target, + _value, + _nonce, + _privateKeyOwners, + _bytesOp, + _paymaster, + [CALL_GAS_LIMIT, MAX_FEE_PER_GAS, MAX_PRIORITY_FEE_PER_GAS] + ); + } + + // with all params (chain ID, paymaster and gas limits) + function _createUserOperation( + uint256 _chainID, + address _from, + address _target, + uint256 _value, + uint256 _nonce, + uint256[] memory _privateKeyOwners, + bytes memory _bytesOp, + address _paymaster, + uint256[3] memory _gasLimits + ) internal view returns (UserOperation memory op) { + op = UserOperation({ + sender: _from, + nonce: _nonce, + initCode: bytes(""), + callData: abi.encodeCall(KintoWallet.execute, (_target, _value, _bytesOp)), + callGasLimit: _gasLimits[0], // generate from call simulation + verificationGasLimit: 210_000, // verification gas. will add create2 cost (3200+200*length) if initCode exists + preVerificationGas: 21_000, // should also cover calldata cost. + maxFeePerGas: _gasLimits[1], // grab from current gas + maxPriorityFeePerGas: _gasLimits[2], // grab from current gas + paymasterAndData: abi.encodePacked(_paymaster), + signature: bytes("") + }); + op.signature = _signUserOp(op, KintoWallet(payable(_from)).entryPoint(), _chainID, _privateKeyOwners); + return op; + } + + // with execute batch + function _createUserOperation( + address _from, + uint256 _nonce, + uint256[] memory _privateKeyOwners, + OperationParamsBatch memory opParams, + address _paymaster + ) internal view returns (UserOperation memory op) { + op = _createUserOperation( + CHAIN_ID, + _from, + address(0), + 0, + _nonce, + _privateKeyOwners, + bytes(""), + _paymaster, + [CALL_GAS_LIMIT, MAX_FEE_PER_GAS, MAX_PRIORITY_FEE_PER_GAS] + ); + op.callData = abi.encodeCall(KintoWallet.executeBatch, (opParams.targets, opParams.values, opParams.bytesOps)); + op.signature = _signUserOp(op, KintoWallet(payable(_from)).entryPoint(), CHAIN_ID, _privateKeyOwners); + } + + // user ops generators + + function _whitelistAppOp(uint256[] memory pk, address from, uint256 nonce, address app, address _paymaster) + internal + view + returns (UserOperation memory userOp) + { + address[] memory targets = new address[](1); + targets[0] = address(app); + bool[] memory flags = new bool[](1); + flags[0] = true; + return _createUserOperation( + from, + from, // target is the wallet itself + nonce, + pk, + abi.encodeWithSignature("whitelistApp(address[],bool[])", targets, flags), + address(_paymaster) + ); + } + + // signature helpers + function _packUserOp(UserOperation memory op, bool forSig) internal pure returns (bytes memory) { if (forSig) { return abi.encode( @@ -83,7 +234,7 @@ abstract contract UserOp is Test { UserOperation memory op, IEntryPoint _entryPoint, uint256 chainID, - uint256[] calldata privateKeys + uint256[] memory privateKeys ) internal pure returns (bytes memory) { bytes32 hash = _getUserOpHash(op, _entryPoint, chainID); hash = hash.toEthSignedMessageHash(); @@ -100,158 +251,4 @@ abstract contract UserOp is Test { return signature; } - - function createUserOperation( - uint256 _chainID, - address _account, - uint256 nonce, - uint256[] calldata _privateKeyOwners, - address _targetContract, - uint256 value, - bytes calldata _bytesOp - ) public view returns (UserOperation memory op) { - return this.createUserOperation( - _chainID, _account, nonce, _privateKeyOwners, _targetContract, value, _bytesOp, address(0) - ); - } - - function createUserOperation( - uint256 _chainID, - address _account, - uint256 nonce, - uint256[] calldata _privateKeyOwners, - address _targetContract, - uint256 value, - bytes calldata _bytesOp, - address _paymaster - ) public view returns (UserOperation memory op) { - op = UserOperation({ - sender: _account, - nonce: nonce, - initCode: bytes(""), - callData: abi.encodeCall(KintoWallet.execute, (_targetContract, value, _bytesOp)), - callGasLimit: 4_000_000, // generate from call simulation - verificationGasLimit: 210_000, // verification gas. will add create2 cost (3200+200*length) if initCode exists - preVerificationGas: 21_000, // should also cover calldata cost. - maxFeePerGas: 1, // grab from current gas - maxPriorityFeePerGas: 1e9, // grab from current gas - paymasterAndData: abi.encodePacked(_paymaster), - signature: bytes("") - }); - op.signature = _signUserOp(op, KintoWallet(payable(_account)).entryPoint(), _chainID, _privateKeyOwners); - return op; - } - - function createUserOperation( - uint256 _chainID, - address _account, - uint256 nonce, - uint256[] calldata _privateKeyOwners, - address _targetContract, - uint256 value, - bytes calldata _bytesOp, - address _paymaster, - uint256[3] calldata _gasLimits - ) public view returns (UserOperation memory op) { - op = UserOperation({ - sender: _account, - nonce: nonce, - initCode: bytes(""), - callData: abi.encodeCall(KintoWallet.execute, (_targetContract, value, _bytesOp)), - callGasLimit: _gasLimits[0], // generate from call simulation - verificationGasLimit: 210_000, // verification gas. will add create2 cost (3200+200*length) if initCode exists - preVerificationGas: 21_000, // should also cover calldata cost. - maxFeePerGas: _gasLimits[1], // grab from current gas - maxPriorityFeePerGas: _gasLimits[2], // grab from current gas - paymasterAndData: abi.encodePacked(_paymaster), - signature: bytes("") - }); - op.signature = _signUserOp(op, KintoWallet(payable(_account)).entryPoint(), _chainID, _privateKeyOwners); - return op; - } - - function createUserOperationBatchWithPaymaster( - uint256 _chainID, - address _account, - uint256 nonce, - uint256[] calldata _privateKeyOwners, - OperationParams calldata opParams, - address _paymaster - ) public view returns (UserOperation memory op) { - op = _prepareUserOperation(_account, nonce, opParams, _paymaster); - op.signature = _signUserOp(op, KintoWallet(payable(_account)).entryPoint(), _chainID, _privateKeyOwners); - } - - function _prepareUserOperation(address _account, uint256 nonce, OperationParams memory opParams, address _paymaster) - internal - pure - returns (UserOperation memory op) - { - op = UserOperation({ - sender: _account, - nonce: nonce, - initCode: bytes(""), - callData: abi.encodeCall( - KintoWallet.executeBatch, (opParams.targetContracts, opParams.values, opParams.bytesOps) - ), - callGasLimit: 4_000_000, // generate from call simulation - verificationGasLimit: 210_000, // verification gas - preVerificationGas: 21_000, // should also cover calldata cost. - maxFeePerGas: 1, // grab from current gas - maxPriorityFeePerGas: 1e9, // grab from current gas - paymasterAndData: abi.encodePacked(_paymaster), - signature: bytes("") - }); - - return op; - } - - function _registerAppOp( - uint256 _chainId, - uint256[] memory pk, - address wallet, - uint256 startingNonce, - address _paymaster, - string memory name, - address parentContract, - address[] memory appContracts, - uint256[4] memory appLimits - ) internal view returns (UserOperation memory userOp) { - return this.createUserOperation( - _chainId, - address(wallet), - startingNonce, - pk, - address(wallet), - 0, - abi.encodeWithSignature( - "registerApp(string,address,address[],uint256[4])", name, parentContract, appContracts, appLimits - ), - address(_paymaster) - ); - } - - function _whitelistAppOp( - uint256 _chainId, - uint256[] memory pk, - address wallet, - uint256 startingNonce, - address app, - address _paymaster - ) internal view returns (UserOperation memory userOp) { - address[] memory targets = new address[](1); - targets[0] = address(app); - bool[] memory flags = new bool[](1); - flags[0] = true; - return this.createUserOperation( - _chainId, - address(wallet), - startingNonce, - pk, - address(wallet), - 0, - abi.encodeWithSignature("whitelistApp(address[],bool[])", targets, flags), - address(_paymaster) - ); - } } diff --git a/utils/export.js b/utils/export.js index 3238c38d2..1360e0957 100644 --- a/utils/export.js +++ b/utils/export.js @@ -3,63 +3,65 @@ const path = require('path'); const execSync = require('child_process').execSync; const dirPath = './src'; // Replace with your actual directory path -const files = fs.readdirSync(dirPath); +const network = process.argv[2]; +console.log(`Exporting contracts for network: ${network}\n`); +const addresses = JSON.parse(fs.readFileSync(`./test/artifacts/${network}/addresses.json`, 'utf-8')); let contracts = {}; -const network = process.argv[2]; -console.log('Exporting contracts for network:', network); -const addresses = JSON.parse(fs.readFileSync(`./test/artifacts/${network}/addresses.json`, 'utf-8')); +/** + * Processes a single .sol to extract its ABI and address. + * @param {string} filePath - path of the Solidity file. + * @param {string} contractName - name of the contract. + */ +function processSolidityFile(filePath, contractName) { + const cmd = `forge inspect ${contractName} abi`; + const result = execSync(cmd).toString(); + console.log(`Exported: ${contractName} ABI`); -// Loop through all directories in src -for (let i = 0; i < files.length; i++) { - const filePath = path.join(dirPath, files[i]); - const fileExt = path.extname(filePath); - if (fileExt === '' && !filePath.includes('interfaces') && !filePath.includes('libraries')) { // Ensure we only process directories - const dirFiles = fs.readdirSync(filePath); - for (let j = 0; j < dirFiles.length; j++) { - const dirFilePath = path.join(filePath, dirFiles[j]); - const dirFileExt = path.extname(dirFilePath); + const jsonObject = JSON.parse(result); + let address = addresses[contractName]; + if (!address || address.length < 8) console.error(`* Missing address for ${contractName}`); + contracts[contractName] = { abi: jsonObject, address: address }; +} + +/** + * Processes a directory containing .sol files. + * @param {string} dir - directory to process. + */ +function processDirectory(dir) { + const dirFiles = fs.readdirSync(dir); + dirFiles.forEach(file => { + const filePath = path.join(dir, file); + const fileExt = path.extname(filePath); - if (dirFileExt === '.sol') { // Ensure we only process .sol files - const contractName = path.basename(dirFilePath, '.sol'); - const cmd = `forge inspect ${contractName} abi`; - const result = execSync(cmd).toString(); - console.log('Exported:', contractName, 'ABI'); - const jsonObject = JSON.parse(result); - let address = addresses[contractName]; - if (!address || address.length < 8) { - address = "0x0000000000000000000000000000000000000000"; - console.error('MISSING ADDRESS FOR', contractName); - } - contracts[contractName] = {"abi": jsonObject, "address": address}; - } + if (fileExt === '.sol') { + const contractName = path.basename(filePath, '.sol'); + processSolidityFile(filePath, contractName); } - } + }); } +/** + * Processes all .sol files in the specified directory and its subdirectories. + */ +function processFiles() { + const files = fs.readdirSync(dirPath); + files.forEach(file => { + const filePath = path.join(dirPath, file); + const fileExt = path.extname(filePath); -for (let i = 0; i < files.length; i++) { - const filePath = path.join(dirPath, files[i]); - const fileExt = path.extname(filePath); - - if (fileExt === '.sol') { // Ensure we only process .sol files - const contractName = path.basename(filePath, '.sol'); - const cmd = `forge inspect ${contractName} abi`; - const result = execSync(cmd).toString(); - - console.log('Exported:', contractName, 'ABI'); - - const jsonObject = JSON.parse(result); - let address = addresses[contractName]; - if (!address || address.length < 8) { - address = '0x0000000000000000000000000000000000000000'; - console.error('MISSING ADDRESS FOR', contractName); + if (fileExt === '' && !filePath.includes('interfaces') && !filePath.includes('libraries')) { + processDirectory(filePath); + } else if (fileExt === '.sol') { + const contractName = path.basename(filePath, '.sol'); + processSolidityFile(filePath, contractName); } - contracts[contractName] = {"abi": jsonObject, "address": address}; - } + }); } -const jsonString = JSON.stringify({"contracts": contracts}); -fs.writeFileSync(`./artifacts/${network}.json`, jsonString); \ No newline at end of file +processFiles(); + +const jsonString = JSON.stringify({ contracts: contracts }, null, 2); +fs.writeFileSync(`./artifacts/${network}.json`, jsonString);