diff --git a/contracts/libs/utils/TokenBalance.sol b/contracts/libs/utils/TokenBalance.sol index 4bdbe035..c8b9025c 100644 --- a/contracts/libs/utils/TokenBalance.sol +++ b/contracts/libs/utils/TokenBalance.sol @@ -38,19 +38,25 @@ library TokenBalance { if (token == ETHEREUM_ADDRESS) { amount = amount.min(balance); - (bool status, ) = payable(receiver).call{value: amount}(""); - require(status, "Failed to send eth"); + if (amount > 0) { + (bool status, ) = payable(receiver).call{value: amount}(""); + require(status, "Failed to send eth"); + } } else { if (balance < amount) { try IERC20Gov(token).mint(address(this), (amount - balance).from18(token)) - {} catch { - amount = balance; - } + {} catch {} + + amount = normThisBalance(token).min(amount); } - IERC20(token).safeTransfer(receiver, amount.from18(token)); + uint256 amountWithDecimals = amount.from18(token); + + if (amountWithDecimals > 0) { + IERC20(token).safeTransfer(receiver, amountWithDecimals); + } } return amount; diff --git a/contracts/mock/tokens/ERC20Mock.sol b/contracts/mock/tokens/ERC20Mock.sol index 3a010b36..bf341a62 100644 --- a/contracts/mock/tokens/ERC20Mock.sol +++ b/contracts/mock/tokens/ERC20Mock.sol @@ -20,7 +20,7 @@ contract ERC20Mock is ERC20 { return _decimals; } - function mint(address to, uint256 _amount) external { + function mint(address to, uint256 _amount) external virtual { require(_allowMint, "ERC20Mock: minting is off"); _mint(to, _amount); diff --git a/contracts/mock/tokens/ERC20MockWithBadMint.sol b/contracts/mock/tokens/ERC20MockWithBadMint.sol new file mode 100644 index 00000000..0932a652 --- /dev/null +++ b/contracts/mock/tokens/ERC20MockWithBadMint.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import "./ERC20Mock.sol"; + +contract ERC20MockWithBadMint is ERC20Mock { + constructor( + string memory name, + string memory symbol, + uint8 decimalPlaces + ) ERC20Mock(name, symbol, decimalPlaces) {} + + function mint(address to, uint256 _amount) external override { + return; + } +} diff --git a/test/gov/GovPool.test.js b/test/gov/GovPool.test.js index f4460a7c..71180aa9 100644 --- a/test/gov/GovPool.test.js +++ b/test/gov/GovPool.test.js @@ -66,6 +66,7 @@ const VotePowerMock = artifacts.require("VotePowerMock"); const ERC721RawPower = artifacts.require("ERC721RawPower"); const ERC721Expert = artifacts.require("ERC721Expert"); const ERC20Mock = artifacts.require("ERC20Mock"); +const ERC20MockWithBadMint = artifacts.require("ERC20MockWithBadMint"); const WethMock = artifacts.require("WETHMock"); const BscProperties = artifacts.require("NetworkPropertiesMock"); const ERC20 = artifacts.require("ERC20"); @@ -99,6 +100,7 @@ GovUserKeeper.numberFormat = "BigNumber"; ERC721EnumMock.numberFormat = "BigNumber"; ERC721Expert.numberFormat = "BigNumber"; ERC20Mock.numberFormat = "BigNumber"; +ERC20MockWithBadMint.numberFormat = "BigNumber"; ERC20.numberFormat = "BigNumber"; BABTMock.numberFormat = "BigNumber"; WethMock.numberFormat = "BigNumber"; @@ -4323,10 +4325,10 @@ describe("GovPool", () => { await govPool.moveProposalToValidators(1); await validators.voteExternalProposal(1, wei("1000000000000"), true, { from: SECOND }); - await network.provider.send("hardhat_setBalance", [govPool.address, "0x" + wei("100")]); - await govPool.execute(1); + await network.provider.send("hardhat_setBalance", [govPool.address, "0x" + toBN(wei("3.2")).toString(16)]); + await govPool.createProposal("example.com", [[settings.address, 0, getBytesAddSettings([NEW_SETTINGS])]], []); await govPool.vote(2, true, wei("1"), []); @@ -4349,6 +4351,16 @@ describe("GovPool", () => { let tx = await govPool.claimRewards([2], OWNER); + assert.equal( + await web3.eth.getBalance(OWNER), + balance.minus(toBN(tx.receipt.gasUsed).times(tx.receipt.effectiveGasPrice)).toFixed(), + ); + + await network.provider.send("hardhat_setBalance", [govPool.address, "0x" + wei("100")]); + balance = toBN(await web3.eth.getBalance(OWNER)); + + tx = await govPool.claimRewards([2], OWNER); + assert.equal( await web3.eth.getBalance(OWNER), balance.plus(wei("16")).minus(toBN(tx.receipt.gasUsed).times(tx.receipt.effectiveGasPrice)).toFixed(), @@ -4508,6 +4520,38 @@ describe("GovPool", () => { assert.equal((await newToken.balanceOf(OWNER)).toFixed(), wei("16")); }); + it("should send correct reward if mint neither revert nor mint", async () => { + newToken = await ERC20MockWithBadMint.new("NT", "NT", 18); + + NEW_SETTINGS.rewardsInfo.rewardToken = newToken.address; + + const bytes = getBytesEditSettings([1], [NEW_SETTINGS]); + + await govPool.createProposal("example.com", [[settings.address, 0, bytes]], []); + await govPool.vote(1, true, wei("100000000000000000000"), [], { from: SECOND }); + + await govPool.moveProposalToValidators(1); + await validators.voteExternalProposal(1, wei("1000000000000"), true, { from: SECOND }); + + await govPool.execute(1); + + await govPool.createProposal( + "example.com", + + [[settings.address, 0, getBytesAddSettings([NEW_SETTINGS])]], + [], + ); + await govPool.vote(2, true, wei("1"), []); + + assert.equal((await newToken.balanceOf(treasury)).toFixed(), "0"); + assert.equal((await newToken.balanceOf(OWNER)).toFixed(), wei("0")); + + await executeAndClaim(2, OWNER); + + assert.equal((await newToken.balanceOf(treasury)).toFixed(), wei("0")); + assert.equal((await newToken.balanceOf(OWNER)).toFixed(), wei("0")); + }); + describe("rewards with low pool balance", () => { async function comparePendingRewards( user,