mirror of
https://github.com/vacp2p/staking-reward-streamer.git
synced 2026-01-09 13:08:03 -05:00
As described in #206 it's theoretically possible to cause overflows when either Karma is issued via `mint()` or `setReward()` because both functions don't take the supply of the other "domain" into account. This commit fixes this by performing an arithmetic operation on `super.totalSupply()`, `totalDistributorAllocation` and the newly issued `amount`. If there's an overflow, the code will cause an overflow and reverts. This ensure overflows can't happen *after* rewards have been issued. Closes #206
195 lines
6.1 KiB
Solidity
195 lines
6.1 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.26;
|
|
|
|
import { Test } from "forge-std/Test.sol";
|
|
import { DeployKarmaScript } from "../script/DeployKarma.s.sol";
|
|
import { DeploymentConfig } from "../script/DeploymentConfig.s.sol";
|
|
import { Karma } from "../src/Karma.sol";
|
|
import { KarmaDistributorMock } from "./mocks/KarmaDistributorMock.sol";
|
|
|
|
contract KarmaTest is Test {
|
|
Karma public karma;
|
|
|
|
address public owner;
|
|
address public alice = makeAddr("alice");
|
|
address public bob = makeAddr("bob");
|
|
|
|
KarmaDistributorMock public distributor1;
|
|
KarmaDistributorMock public distributor2;
|
|
|
|
function setUp() public virtual {
|
|
DeployKarmaScript karmaDeployment = new DeployKarmaScript();
|
|
(Karma _karma, DeploymentConfig deploymentConfig) = karmaDeployment.run();
|
|
karma = _karma;
|
|
(address deployer,) = deploymentConfig.activeNetworkConfig();
|
|
owner = deployer;
|
|
|
|
distributor1 = new KarmaDistributorMock();
|
|
distributor2 = new KarmaDistributorMock();
|
|
|
|
vm.startBroadcast(owner);
|
|
karma.addRewardDistributor(address(distributor1));
|
|
karma.addRewardDistributor(address(distributor2));
|
|
vm.stopBroadcast();
|
|
}
|
|
|
|
function testAddKarmaDistributorOnlyOwner() public {
|
|
KarmaDistributorMock distributor3 = new KarmaDistributorMock();
|
|
|
|
vm.prank(alice);
|
|
vm.expectRevert("Ownable: caller is not the owner");
|
|
karma.addRewardDistributor(address(distributor3));
|
|
|
|
vm.prank(owner);
|
|
karma.addRewardDistributor(address(distributor3));
|
|
|
|
address[] memory distributors = karma.getRewardDistributors();
|
|
assertEq(distributors.length, 3);
|
|
assertEq(distributors[0], address(distributor1));
|
|
assertEq(distributors[1], address(distributor2));
|
|
assertEq(distributors[2], address(distributor3));
|
|
}
|
|
|
|
function testRemoveKarmaDistributorOnlyOwner() public {
|
|
vm.prank(alice);
|
|
vm.expectRevert("Ownable: caller is not the owner");
|
|
karma.removeRewardDistributor(address(distributor1));
|
|
|
|
vm.prank(owner);
|
|
karma.removeRewardDistributor(address(distributor1));
|
|
|
|
address[] memory distributors = karma.getRewardDistributors();
|
|
assertEq(distributors.length, 1);
|
|
assertEq(distributors[0], address(distributor2));
|
|
}
|
|
|
|
function testRemoveUnknownKarmaDistributor() public {
|
|
vm.prank(owner);
|
|
vm.expectRevert(Karma.Karma__UnknownDistributor.selector);
|
|
karma.removeRewardDistributor(address(1));
|
|
}
|
|
|
|
function testTotalSupply() public {
|
|
vm.startBroadcast(owner);
|
|
karma.setReward(address(distributor1), 1000 ether, 1000);
|
|
karma.setReward(address(distributor2), 2000 ether, 2000);
|
|
vm.stopBroadcast();
|
|
|
|
distributor1.setTotalKarmaShares(1000 ether);
|
|
distributor2.setTotalKarmaShares(2000 ether);
|
|
|
|
vm.prank(owner);
|
|
karma.mint(owner, 500 ether);
|
|
|
|
uint256 totalSupply = karma.totalSupply();
|
|
assertEq(totalSupply, 3500 ether);
|
|
}
|
|
|
|
function testBalanceOfWithNoSystemTotalKarma() public view {
|
|
uint256 aliceBalance = karma.balanceOf(alice);
|
|
assertEq(aliceBalance, 0);
|
|
|
|
uint256 bobBalance = karma.balanceOf(bob);
|
|
assertEq(bobBalance, 0);
|
|
}
|
|
|
|
function testBalanceOf() public {
|
|
vm.startBroadcast(owner);
|
|
karma.setReward(address(distributor1), 1000 ether, 1000);
|
|
karma.setReward(address(distributor2), 2000 ether, 2000);
|
|
vm.stopBroadcast();
|
|
|
|
distributor1.setTotalKarmaShares(1000 ether);
|
|
distributor2.setTotalKarmaShares(2000 ether);
|
|
|
|
distributor1.setUserKarmaShare(alice, 1000e18);
|
|
distributor2.setUserKarmaShare(alice, 2000e18);
|
|
|
|
vm.prank(owner);
|
|
karma.mint(alice, 500e18);
|
|
|
|
uint256 expectedBalance = 3500e18;
|
|
|
|
uint256 balance = karma.balanceOf(alice);
|
|
assertEq(balance, expectedBalance);
|
|
}
|
|
|
|
function testMintOnlyOwner() public {
|
|
vm.startBroadcast(owner);
|
|
karma.setReward(address(distributor1), 1000 ether, 1000);
|
|
karma.setReward(address(distributor2), 2000 ether, 2000);
|
|
vm.stopBroadcast();
|
|
|
|
distributor1.setTotalKarmaShares(1000 ether);
|
|
distributor2.setTotalKarmaShares(2000 ether);
|
|
assertEq(karma.totalSupply(), 3000 ether);
|
|
|
|
vm.prank(alice);
|
|
vm.expectRevert("Ownable: caller is not the owner");
|
|
karma.mint(alice, 1000e18);
|
|
|
|
vm.prank(owner);
|
|
karma.mint(alice, 1000e18);
|
|
assertEq(karma.totalSupply(), 4000e18);
|
|
}
|
|
|
|
function testTransfersNotAllowed() public {
|
|
vm.expectRevert(Karma.Karma__TransfersNotAllowed.selector);
|
|
karma.transfer(alice, 100e18);
|
|
|
|
vm.expectRevert(Karma.Karma__TransfersNotAllowed.selector);
|
|
karma.approve(alice, 100e18);
|
|
|
|
vm.expectRevert(Karma.Karma__TransfersNotAllowed.selector);
|
|
karma.transferFrom(alice, bob, 100e18);
|
|
|
|
uint256 allowance = karma.allowance(alice, bob);
|
|
assertEq(allowance, 0);
|
|
}
|
|
}
|
|
|
|
contract KarmaOwnershipTest is KarmaTest {
|
|
function setUp() public override {
|
|
super.setUp();
|
|
}
|
|
|
|
function testInitialOwner() public view {
|
|
assertEq(karma.owner(), owner);
|
|
}
|
|
|
|
function testOwnershipTransfer() public {
|
|
vm.prank(owner);
|
|
karma.transferOwnership(alice);
|
|
assertEq(karma.owner(), owner);
|
|
|
|
vm.prank(alice);
|
|
karma.acceptOwnership();
|
|
assertEq(karma.owner(), alice);
|
|
}
|
|
}
|
|
|
|
contract OverflowTest is KarmaTest {
|
|
function setUp() public override {
|
|
super.setUp();
|
|
}
|
|
|
|
function test_RevertWhen_MintingCausesOverflow() public {
|
|
vm.startBroadcast(owner);
|
|
karma.setReward(address(distributor1), type(uint256).max, 1000);
|
|
vm.stopBroadcast();
|
|
|
|
vm.prank(owner);
|
|
vm.expectRevert();
|
|
karma.mint(owner, 1e18);
|
|
}
|
|
|
|
function test_RevertWhen_SettingRewardCausesOverflow() public {
|
|
vm.prank(owner);
|
|
karma.mint(owner, type(uint256).max);
|
|
|
|
vm.prank(owner);
|
|
vm.expectRevert();
|
|
karma.setReward(address(distributor1), 1e18, 1000);
|
|
}
|
|
}
|