pragma solidity 0.8.20; import {Test} from "forge-std/Test.sol"; import {Ghost} from "../../src/GhstERC20.sol"; import {Stinky} from "../../src/StinkyERC20.sol"; import {GhostAuthority} from "../../src/GhostAuthority.sol"; import {GhostStaking} from "../../src/Staking.sol"; import {ERC20PermitTest} from "./Permit.t.sol"; import {ERC20AllowanceTest} from "./Allowance.t.sol"; import {ERC20TransferTest} from "./Transfer.t.sol"; import {ERC20VotesTest} from "./Votes.t.sol"; contract GhostTest is Test, ERC20PermitTest, ERC20AllowanceTest, ERC20TransferTest, ERC20VotesTest { uint256 constant public INITIAL_INDEX = 10819917194513808e56; address constant INITIALIZER = 0x0000000000000000000000000000000000000001; address constant ALICE = 0x0000000000000000000000000000000000000003; address constant BOB = 0x0000000000000000000000000000000000000004; address constant TREASURY = 0x0000000000000000000000000000000000000005; uint256 constant AMOUNT = 69; uint256 constant MAX_AMOUNT = type(uint256).max; string constant NAME = "Ghost Test Name"; string constant SYMBOL = "GHSTTST"; Stinky stnk; Ghost ghst; GhostAuthority public authority; GhostStaking public staking; function setUp() public { vm.startPrank(INITIALIZER); authority = new GhostAuthority( INITIALIZER, INITIALIZER, INITIALIZER, INITIALIZER ); stnk = new Stinky(INITIAL_INDEX, "Stinky", "STNK"); ghst = new Ghost(address(stnk), NAME, SYMBOL); staking = new GhostStaking( address(0), address(stnk), address(ghst), 69, 1337, 1337, address(authority) ); stnk.initialize(address(staking), TREASURY, address(ghst)); ghst.initialize(address(staking)); vm.stopPrank(); initializePermit(address(ghst), AMOUNT, MAX_AMOUNT); initializeAllowance(ALICE, BOB, address(ghst), AMOUNT, MAX_AMOUNT, AMOUNT); initializeTransfer(ALICE, BOB, address(ghst), AMOUNT, MAX_AMOUNT); initializeVotes(ALICE, BOB, address(ghst), AMOUNT); } function test_isConstructedCorrectly() public view { assertEq(ghst.name(), NAME); assertEq(ghst.symbol(), SYMBOL); assertEq(ghst.decimals(), 18); assertEq(ghst.staking(), address(staking)); assertEq(ghst.stnk(), address(stnk)); assertEq(ghst.index(), stnk.index()); } function test_mint_couldBeDoneByStaking() public { assertEq(ghst.totalSupply(), 0); vm.prank(address(staking)); ghst.mint(ALICE, AMOUNT); assertEq(ghst.totalSupply(), AMOUNT); assertEq(ghst.balanceOf(ALICE), AMOUNT); } function test_mint_couldNotFromArbitraryAddress(address who) public { vm.assume(who != address(staking)); vm.expectRevert(); vm.prank(who); ghst.mint(ALICE, AMOUNT); assertEq(ghst.totalSupply(), 0); assertEq(ghst.balanceOf(who), 0); } function test_burn_couldBeDoneByOwner() public { _mintTokens(ALICE, AMOUNT); assertEq(ghst.totalSupply(), AMOUNT); vm.prank(address(staking)); ghst.burn(ALICE, AMOUNT); assertEq(ghst.totalSupply(), 0); assertEq(ghst.balanceOf(ALICE), 0); } function test_burn_couldBeDoneByOwnerPartially(uint256 part) public { vm.assume(part <= AMOUNT); _mintTokens(ALICE, AMOUNT); assertEq(ghst.totalSupply(), AMOUNT); vm.prank(address(staking)); ghst.burn(ALICE, part); assertEq(ghst.totalSupply(), AMOUNT - part); assertEq(ghst.balanceOf(ALICE), AMOUNT - part); } function test_burn_couldNotBurnMoreThanBalance() public { _mintTokens(ALICE, AMOUNT); assertEq(ghst.totalSupply(), AMOUNT); vm.expectRevert(); vm.prank(ALICE); ghst.burn(ALICE, AMOUNT + 1); assertEq(ghst.totalSupply(), AMOUNT); assertEq(ghst.balanceOf(ALICE), AMOUNT); } function test_burn_couldNotFromArbitraryAddress(address who) public { vm.assume(who != address(staking)); _mintTokens(ALICE, AMOUNT); vm.expectRevert(); vm.prank(who); ghst.burn(ALICE, AMOUNT); assertEq(ghst.totalSupply(), AMOUNT); assertEq(ghst.balanceOf(ALICE), AMOUNT); } function test_balanceToCalculatesCorrectly(uint256 balanceToAmount) public view { vm.assume(balanceToAmount < type(uint128).max - 1); uint256 index = ghst.index(); assertEq(ghst.balanceTo(balanceToAmount), balanceToAmount * 1e18 / index); } function test_balanceFromCalculatesCorrectly(uint256 balanceFromAmount) public view { vm.assume(balanceFromAmount < type(uint128).max - 1); uint256 index = ghst.index(); assertEq(ghst.balanceFrom(balanceFromAmount), balanceFromAmount * index / 1e18); } function _mintTokens(address who, uint256 value) internal { uint256 totalSupply = ghst.totalSupply(); vm.prank(address(staking)); ghst.mint(who, value); assertEq(ghst.totalSupply(), totalSupply + value); } function _mintTransferTokens(address who, uint256 value) internal override { _mintTokens(who, value); } function _mintAllowanceTokens(address who, uint256 value) internal override { _mintTokens(who, value); } function _mintPermitTokens(address who, uint256 value) internal override { _mintTokens(who, value); } function _mintVotesTokens(address who, uint256 value) internal override { _mintTokens(who, value); } function _burnVotesTokens(address who, uint256 value) internal override { vm.prank(address(staking)); ghst.burn(who, value); } function _getCurrentTotalSupply() internal override view returns (uint256) { return ghst.totalSupply(); } }