diff --git a/contracts/gov/GovPool.sol b/contracts/gov/GovPool.sol index abff89da..7d9a61bf 100644 --- a/contracts/gov/GovPool.sol +++ b/contracts/gov/GovPool.sol @@ -163,11 +163,10 @@ contract GovPool is uint256[] calldata nftIds ) external payable override onlyBABTHolder { require(amount > 0 || nftIds.length > 0, "Gov: empty deposit"); - _checkValue(amount); _lockBlock(DEPOSIT_WITHDRAW, msg.sender); - if (amount != 0) { + if (amount != 0 || msg.value != 0) { _govUserKeeper.depositTokens{value: msg.value}(msg.sender, msg.sender, amount); } @@ -274,20 +273,20 @@ contract GovPool is ) external payable override onlyThis { require(amount > 0 || nftIds.length > 0, "Gov: empty delegation"); require(getExpertStatus(delegatee), "Gov: delegatee is not an expert"); - _checkValue(amount); _lockBlock(DELEGATE_UNDELEGATE_TREASURY, msg.sender); _unlock(delegatee); - if (amount != 0) { + if (amount != 0 || msg.value != 0) { address token = _govUserKeeper.tokenAddress(); + uint256 amountWithNativeDecimals = _govUserKeeper.getAmountWithNativeDecimals( + msg.value, + amount + ); - if (amount != msg.value) { - IERC20(token).safeTransfer( - address(_govUserKeeper), - (amount - msg.value).from18Safe(token) - ); + if (amountWithNativeDecimals != 0) { + IERC20(token).safeTransfer(address(_govUserKeeper), amountWithNativeDecimals); } _govUserKeeper.delegateTokensTreasury{value: msg.value}(delegatee, amount); @@ -630,8 +629,4 @@ contract GovPool is "Gov: not BABT holder" ); } - - function _checkValue(uint256 amount) internal view { - require(msg.value <= amount, "Gov: value is greater than amount to transfer"); - } } diff --git a/contracts/gov/user-keeper/GovUserKeeper.sol b/contracts/gov/user-keeper/GovUserKeeper.sol index f1df200c..b7e50460 100644 --- a/contracts/gov/user-keeper/GovUserKeeper.sol +++ b/contracts/gov/user-keeper/GovUserKeeper.sol @@ -93,18 +93,15 @@ contract GovUserKeeper is IGovUserKeeper, OwnableUpgradeable, ERC721HolderUpgrad uint256 amount ) external payable override onlyOwner withSupportedToken { address token = tokenAddress; - uint256 fullAmount = amount; + uint256 amountWithNativeDecimals = getAmountWithNativeDecimals(msg.value, amount); - if (msg.value != 0) { - _wrapNative(msg.value); - amount -= msg.value; - } + _handleNative(msg.value, true); - if (amount > 0) { - IERC20(token).safeTransferFrom(payer, address(this), amount.from18Safe(token)); + if (amountWithNativeDecimals > 0) { + IERC20(token).safeTransferFrom(payer, address(this), amountWithNativeDecimals); } - _usersInfo[receiver].balances[IGovPool.VoteType.PersonalVote].tokens += fullAmount; + _usersInfo[receiver].balances[IGovPool.VoteType.PersonalVote].tokens += amount; } function withdrawTokens( @@ -156,9 +153,7 @@ contract GovUserKeeper is IGovUserKeeper, OwnableUpgradeable, ERC721HolderUpgrad address delegatee, uint256 amount ) external payable override onlyOwner withSupportedToken { - if (msg.value != 0) { - _wrapNative(msg.value); - } + _handleNative(msg.value, true); _usersInfo[delegatee].balances[IGovPool.VoteType.TreasuryVote].tokens += amount; } @@ -749,16 +744,32 @@ contract GovUserKeeper is IGovUserKeeper, OwnableUpgradeable, ERC721HolderUpgrad ); } + function getAmountWithNativeDecimals( + uint256 value, + uint256 amount + ) public view returns (uint256 nativeAmount) { + require( + value == 0 || _isWrapped(), + "GovUK: should not send ether if Gov token is not native" + ); + + nativeAmount = amount.from18Safe(tokenAddress); + require(nativeAmount >= value, "GovUK: ether value is greater than amount"); + + nativeAmount -= value; + } + function _sendNativeOrToken(address receiver, uint256 amount) internal { address token = tokenAddress; + amount = amount.from18Safe(token); if (_isWrapped()) { - _unwrapNative(amount); + _handleNative(amount, false); (bool ok, ) = payable(receiver).call{value: amount}(""); require(ok, "GovUK: can't send ether"); } else { - IERC20(token).safeTransfer(receiver, amount.from18Safe(token)); + IERC20(token).safeTransfer(receiver, amount); } } @@ -828,14 +839,16 @@ contract GovUserKeeper is IGovUserKeeper, OwnableUpgradeable, ERC721HolderUpgrad require(_nftInfo.nftAddress != address(0), "GovUK: nft is not supported"); } - function _wrapNative(uint256 value) internal { - require(_isWrapped(), "GovUK: not native token pool"); - - IWETH(wethAddress).deposit{value: value}(); - } + function _handleNative(uint256 value, bool wrapping) internal { + if (value == 0) { + return; + } - function _unwrapNative(uint256 value) internal { - IWETH(wethAddress).withdraw(value); + if (wrapping) { + IWETH(wethAddress).deposit{value: value}(); + } else { + IWETH(wethAddress).withdraw(value); + } } function _isWrapped() internal view returns (bool) { diff --git a/contracts/interfaces/gov/user-keeper/IGovUserKeeper.sol b/contracts/interfaces/gov/user-keeper/IGovUserKeeper.sol index 08917aa7..aa9c474e 100644 --- a/contracts/interfaces/gov/user-keeper/IGovUserKeeper.sol +++ b/contracts/interfaces/gov/user-keeper/IGovUserKeeper.sol @@ -351,4 +351,13 @@ interface IGovUserKeeper { address delegator, address delegatee ) external view returns (uint256 delegatedPower); + + /// @notice The function for getting the wrapped token amount, not covered by ether + /// @param value the ether value sent alongside the call + /// @param amount the total amount of wrapped ether with 18 decimals + /// @return nativeAmount the amount of wrapped ether with native decimals minus ether value + function getAmountWithNativeDecimals( + uint256 value, + uint256 amount + ) external view returns (uint256 nativeAmount); } diff --git a/contracts/libs/utils/TokenBalance.sol b/contracts/libs/utils/TokenBalance.sol index 0fbd7212..4bdbe035 100644 --- a/contracts/libs/utils/TokenBalance.sol +++ b/contracts/libs/utils/TokenBalance.sol @@ -28,6 +28,10 @@ library TokenBalance { uint256 amount, TransferType transferType ) internal returns (uint256) { + if (amount == 0) { + return 0; + } + uint256 balance = normThisBalance(token); require(balance >= amount || transferType == TransferType.TryMint, "Insufficient funds"); @@ -57,7 +61,7 @@ library TokenBalance { } function sendFunds(IERC20 token, address receiver, uint256 amount) internal { - token.safeTransfer(receiver, amount.from18(address(token))); + token.safeTransfer(receiver, amount.from18Safe(address(token))); } function thisBalance(address token) internal view returns (uint256) { diff --git a/test/gov/GovPool.test.js b/test/gov/GovPool.test.js index 135ca6fe..8ac4ae44 100644 --- a/test/gov/GovPool.test.js +++ b/test/gov/GovPool.test.js @@ -693,14 +693,26 @@ describe("GovPool", () => { }); it("cant deposit ether if gov token is not native", async () => { - await truffleAssert.reverts(govPool.deposit(wei("100"), [], { value: 100 }), "GovUK: not native token pool"); + await truffleAssert.reverts( + govPool.deposit(wei("100"), [], { value: 100 }), + "GovUK: should not send ether if Gov token is not native", + ); }); it("cant overdeposit ether", async () => { + await switchWeth(0, token.address); + await truffleAssert.reverts( govPool.deposit(1, [1, 2, 3], { value: 2 }), - "Gov: value is greater than amount to transfer", + "GovUK: ether value is greater than amount", ); + + await truffleAssert.reverts( + govPool.deposit(0, [1, 2, 3], { value: 1 }), + "DecimalsConverter: conversion failed", + ); + + await switchWeth(0, weth.address); }); it("could deposit ether", async () => { @@ -3660,6 +3672,13 @@ describe("GovPool", () => { await truffleAssert.reverts(delegateTreasury(THIRD, "0", []), "Gov: empty delegation"); }); + it("should not delegate treasury if incorrect amount and value", async () => { + await truffleAssert.reverts( + delegateTreasury(THIRD, "0", [11], wei("1")), + "GovUK: should not send ether if Gov token is not native", + ); + }); + it("should create proposal for delegateTreasury and undelegateTreasury", async () => { assert.equal((await token.balanceOf(THIRD)).toFixed(), "0"); assert.equal((await nft.balanceOf(THIRD)).toFixed(), "0"); @@ -3757,7 +3776,7 @@ describe("GovPool", () => { await truffleAssert.reverts( delegateTreasury(THIRD, wei("100"), [], wei("200")), - "Gov: value is greater than amount to transfer", + "GovUK: ether value is greater than amount", ); await delegateTreasury(THIRD, wei("100"), [], wei("100")); @@ -5283,6 +5302,10 @@ describe("GovPool", () => { await govPool.transferCreditAmount([CREDIT_TOKEN_1.address], ["1000"], SECOND, { from: VALIDATORS }); assert.equal((await CREDIT_TOKEN_1.balanceOf(SECOND)).toFixed(), "1000"); + + await govPool.transferCreditAmount([CREDIT_TOKEN_1.address], ["0"], SECOND, { from: VALIDATORS }); + + assert.equal((await CREDIT_TOKEN_1.balanceOf(SECOND)).toFixed(), "1000"); }); it("cant get more than month limit", async () => {