diff --git a/src/wallet/KintoWalletFactory.sol b/src/wallet/KintoWalletFactory.sol index 53775a690..e95b358f4 100644 --- a/src/wallet/KintoWalletFactory.sol +++ b/src/wallet/KintoWalletFactory.sol @@ -198,15 +198,16 @@ contract KintoWalletFactory is Initializable, UUPSUpgradeable, OwnableUpgradeabl } /** - * @dev Send money to an account from privileged accounts + * @dev Send money to an account from privileged accounts or from kyc accounts to kyc accounts or contracts. * @param target The target address */ function sendMoneyToAccount(address target) external payable override { - require( - owner() == msg.sender || kintoID.isKYC(msg.sender) - || IAccessControl(address(kintoID)).hasRole(kintoID.KYC_PROVIDER_ROLE(), msg.sender), - "KYC or Provider role required" - ); + require(target != address(0), "Invalid target: zero address"); + bool isPrivileged = + owner() == msg.sender || IAccessControl(address(kintoID)).hasRole(kintoID.KYC_PROVIDER_ROLE(), msg.sender); + require(isPrivileged || kintoID.isKYC(msg.sender), "KYC or Provider role required"); + bool isValidTarget = kintoID.isKYC(target) || target.code.length > 0; + require(isValidTarget || isPrivileged, "Target is not valid"); (bool sent,) = target.call{value: msg.value}(""); require(sent, "Failed to send Ether"); } diff --git a/test/KintoWalletFactory.t.sol b/test/KintoWalletFactory.t.sol index 471346054..1d1825d58 100644 --- a/test/KintoWalletFactory.t.sol +++ b/test/KintoWalletFactory.t.sol @@ -321,10 +321,20 @@ contract KintoWalletFactoryTest is SharedSetup { function testSendMoneyToAccount_WhenCallerIsKYCd() public { approveKYC(_kycProvider, _user, _userPk); + approveKYC(_kycProvider, _user2, _user2Pk); + vm.deal(_user, 1 ether); vm.prank(_user); - _walletFactory.sendMoneyToAccount{value: 1e18}(address(123)); - assertEq(address(123).balance, 1e18); + _walletFactory.sendMoneyToAccount{value: 1e18}(address(_user2)); + assertEq(address(_user2).balance, 1e18); + } + + function testSendMoneyToAccount_WhenCallerIsKYCdAndTargetIsContract() public { + approveKYC(_kycProvider, _user, _userPk); + vm.deal(_user, 1 ether); + vm.prank(_user); + _walletFactory.sendMoneyToAccount{value: 1e18}(address(_kintoWallet)); + assertEq(address(_kintoWallet).balance, 1e18); } function testSendMoneyToAccount_WhenCallerIsOwner() public { @@ -334,6 +344,14 @@ contract KintoWalletFactoryTest is SharedSetup { assertEq(address(123).balance, 1e18); } + function testSendMoneyToAccount_WhenCallerIsOwner_WhenTargetIsKYC() public { + approveKYC(_kycProvider, _user, _userPk); + revokeKYC(_kycProvider, _owner, _ownerPk); + vm.prank(_owner); + _walletFactory.sendMoneyToAccount{value: 1e18}(address(_user)); + assertEq(address(_user).balance, 1e18); + } + function testSendMoneyToAccount_WhenCallerIsKYCProvider() public { vm.deal(_kycProvider, 1 ether); vm.prank(_kycProvider); @@ -353,6 +371,14 @@ contract KintoWalletFactoryTest is SharedSetup { assertEq(address(123).balance, 1e18); } + function testSendMoneyToAccount_WhenCallerIsKYCd_WhenTargetIsContract() public { + vm.deal(_kycProvider, 1 ether); + vm.prank(_kycProvider); + uint256 beforeBalance = address(_kintoWallet).balance; + _walletFactory.sendMoneyToAccount{value: 1e18}(address(_kintoWallet)); + assertEq(address(_kintoWallet).balance, beforeBalance + 1e18); + } + function testSendMoneyToAccount_RevertWhen_CallerIsNotAllowed() public { vm.deal(address(123), 1 ether); vm.prank(address(123)); @@ -360,6 +386,14 @@ contract KintoWalletFactoryTest is SharedSetup { _walletFactory.sendMoneyToAccount{value: 1e18}(address(123)); } + function testSendMoneyToAccount_RevertWhen_CallerIsKYCd_WhenTargetisNotKYCd() public { + approveKYC(_kycProvider, _user, _userPk); + vm.deal(_user, 1 ether); + vm.prank(_user); + vm.expectRevert("Target is not valid"); + _walletFactory.sendMoneyToAccount{value: 1e18}(address(123)); + } + /* ============ Claim From Faucet tests ============ */ function testClaimFromFaucet_WhenCallerIsKYCd() public {