204 lines
9.1 KiB
Solidity
204 lines
9.1 KiB
Solidity
pragma solidity 0.8.20;
|
|
|
|
import {Test} from "forge-std/Test.sol";
|
|
|
|
import {Fatso} from "../../src/FatsoERC20.sol";
|
|
import {Stinky} from "../../src/StinkyERC20.sol";
|
|
import {Ghost} from "../../src/GhstERC20.sol";
|
|
import {GhostAuthority} from "../../src/GhostAuthority.sol";
|
|
import {GhostTreasury} from "../../src/Treasury.sol";
|
|
import {GhostStaking} from "../../src/Staking.sol";
|
|
import {GhostBondDepository} from "../../src/BondDepository.sol";
|
|
import {ERC20Mock} from "../../src/mocks/ERC20Mock.sol";
|
|
import {WETH9} from "../../src/mocks/WETH9.sol";
|
|
import {GhostBondingCalculator} from "../../src/StandardBondingCalculator.sol";
|
|
import {Gatekeeper} from "../../src/Gatekeeper.sol";
|
|
|
|
import {ITreasury} from "../../src/interfaces/ITreasury.sol";
|
|
import {IGatekeeper} from "../../src/interfaces/IGatekeeper.sol";
|
|
import {IERC20} from "@openzeppelin-contracts/token/ERC20/IERC20.sol";
|
|
|
|
contract GatekeeperRecallTest is Test {
|
|
uint256 public constant TOTAL_INITIAL_SUPPLY = 5000000000000000;
|
|
uint256 public constant LARGE_APPROVAL = 100000000000000000000000000000000;
|
|
uint256 public constant INITIAL_INDEX = 10819917194513808e56;
|
|
uint48 public constant EPOCH_LENGTH = 2200;
|
|
uint48 public constant EPOCH_NUMBER = 1;
|
|
uint48 public constant EPOCH_END_TIME = 1337;
|
|
|
|
uint256 public constant INITIAL_MINT = 1000000000000000000000000;
|
|
uint256 public constant CAPACITY = 10000e9;
|
|
uint256 public constant INITIAL_PRICE = 400e9;
|
|
uint256 public constant BUFFER = 2e5;
|
|
|
|
address constant INITIALIZER = 0x0000000000000000000000000000000000000001;
|
|
address constant GOVERNOR = 0x0000000000000000000000000000000000000003;
|
|
address constant GUARDIAN = 0x0000000000000000000000000000000000000004;
|
|
address constant POLICY = 0x0000000000000000000000000000000000000005;
|
|
address constant VAULT = 0x0000000000000000000000000000000000000006;
|
|
address constant ALICE = 0x0000000000000000000000000000000000000007;
|
|
address constant BOB = 0x0000000000000000000000000000000000000008;
|
|
|
|
uint256 public constant VESTING = 100;
|
|
uint256 public constant TIME_TO_CONCLUSION = 60 * 60 * 24;
|
|
uint256 public constant DEPOSIT_INTERVAL = 60 * 60 * 4;
|
|
uint256 public constant TUNE_INTERVAL = 60 * 60;
|
|
|
|
Fatso ftso;
|
|
Stinky stnk;
|
|
Ghost ghst;
|
|
GhostStaking staking;
|
|
GhostTreasury treasury;
|
|
GhostAuthority authority;
|
|
GhostBondingCalculator calculator;
|
|
WETH9 reserve;
|
|
|
|
function setUp() public {
|
|
vm.startPrank(INITIALIZER, INITIALIZER);
|
|
reserve = new WETH9();
|
|
authority = new GhostAuthority(
|
|
GOVERNOR,
|
|
GUARDIAN,
|
|
POLICY,
|
|
VAULT
|
|
);
|
|
ftso = new Fatso(address(authority), "Fatso", "FTSO");
|
|
stnk = new Stinky(INITIAL_INDEX, "Stinky", "STNK");
|
|
ghst = new Ghost(address(stnk), "Ghost", "GHST");
|
|
staking = new GhostStaking(
|
|
address(ftso),
|
|
address(stnk),
|
|
address(ghst),
|
|
EPOCH_LENGTH,
|
|
EPOCH_NUMBER,
|
|
EPOCH_END_TIME,
|
|
address(authority)
|
|
);
|
|
treasury = new GhostTreasury(address(ftso), address(authority));
|
|
calculator = new GhostBondingCalculator(address(ftso), 6000, 3);
|
|
stnk.initialize(address(staking), address(treasury), address(ghst));
|
|
ghst.initialize(address(staking));
|
|
vm.stopPrank();
|
|
|
|
vm.startPrank(GOVERNOR);
|
|
authority.pushVault(address(treasury));
|
|
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, ALICE, address(0));
|
|
treasury.enable(ITreasury.STATUS.RESERVETOKEN, address(reserve), address(calculator));
|
|
treasury.enable(ITreasury.STATUS.STAKING, address(staking), address(0));
|
|
vm.stopPrank();
|
|
|
|
vm.deal(ALICE, INITIAL_MINT);
|
|
|
|
vm.startPrank(ALICE);
|
|
reserve.deposit{value: INITIAL_MINT}();
|
|
reserve.approve(address(treasury), type(uint256).max);
|
|
treasury.deposit(address(reserve), INITIAL_MINT, treasury.tokenValue(address(reserve), INITIAL_MINT) / 2);
|
|
assertEq(ftso.totalSupply(), treasury.baseSupply());
|
|
vm.stopPrank();
|
|
}
|
|
|
|
function test_recallChainWorks() public {
|
|
vm.startPrank(INITIALIZER);
|
|
Gatekeeper gatekeeper = Gatekeeper(payable(staking.gatekeeper()));
|
|
gatekeeper.updatePublicKeyMetadata(0, 0x4e8c1fe96d6737cdabbcc96501d6656b9d0b659b3a528ef507f2d52bef29e157, 0);
|
|
vm.stopPrank();
|
|
|
|
vm.startPrank(ALICE);
|
|
uint256 aliceBalance = ftso.balanceOf(ALICE);
|
|
ftso.approve(address(staking), type(uint256).max);
|
|
staking.stake(aliceBalance, ALICE, false, true);
|
|
|
|
uint256 ghostBalance = ghst.balanceOf(ALICE);
|
|
staking.ghost(bytes32(abi.encodePacked(ALICE)), ghostBalance);
|
|
vm.stopPrank();
|
|
|
|
uint256 bountyPercent = type(uint32).max / 2;
|
|
uint256 amountToBridge = 42 * 1e15;
|
|
address evmReceiver = address(0x0000000000000000000000000000000000000002);
|
|
|
|
uint256 bobGhstBefore = ghst.balanceOf(BOB);
|
|
uint256 receiverEthBefore = evmReceiver.balance;
|
|
uint256 receiverGhstBefore = ghst.balanceOf(evmReceiver);
|
|
uint256 totalReservesBefore = treasury.totalReserves();
|
|
uint256 totalSupplyBefore = ftso.totalSupply();
|
|
uint256 ghostedSupplyBefore = gatekeeper.ghostedSupply();
|
|
|
|
vm.txGasPrice(2 gwei);
|
|
vm.startPrank(BOB, BOB);
|
|
gatekeeper.verify(
|
|
hex"bf06188a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000009536c70891000000000000000000000000000000000000000000020000000000007a6980000000",
|
|
hex"046566d4b0904bf35afd25dff583d53bd30e50f57cc790ad08377e0b85006b38b85046b527098f3dfdadc30d6623b6b1c4644306dd56d39f7c9d6b3b295d005c33",
|
|
0x942fb44ad0422c197f9c3016ff7c7e574a9f86182c5485e39fc9bebf61228ad6
|
|
);
|
|
vm.stopPrank();
|
|
|
|
{
|
|
uint256 bobEarnedGhst = ghst.balanceOf(BOB) - bobGhstBefore;
|
|
assertTrue(bobEarnedGhst > 0);
|
|
|
|
uint256 bobEarnedFtso = ghst.balanceFrom(bobEarnedGhst);
|
|
|
|
uint256 totalReservesAfter = treasury.totalReserves();
|
|
uint256 totalSupplyAfter = ftso.totalSupply();
|
|
|
|
uint256 gasUsed = 2 * 1e9 * 329188;
|
|
uint256 gasReserveValue = treasury.tokenValue(address(reserve), gasUsed);
|
|
uint256 expectedGasFtso = gasReserveValue * totalSupplyAfter / totalReservesAfter;
|
|
|
|
uint256 simulatedGhst = ghst.balanceTo(expectedGasFtso);
|
|
uint256 dynamicGasPriceEstimation = ghst.balanceFrom(simulatedGhst);
|
|
|
|
assertApproxEqAbs(bobEarnedFtso, dynamicGasPriceEstimation, 1);
|
|
}
|
|
|
|
{
|
|
uint256 expectedBounty = amountToBridge * bountyPercent / type(uint32).max;
|
|
uint256 expectedMintToReceiver = amountToBridge - expectedBounty;
|
|
uint256 actualMintToReceiver = ghst.balanceOf(evmReceiver) - receiverGhstBefore;
|
|
assertApproxEqAbs(actualMintToReceiver, expectedMintToReceiver, 1);
|
|
}
|
|
|
|
{
|
|
uint256 receiverEthAfter = evmReceiver.balance;
|
|
uint256 receivedEth = receiverEthAfter - receiverEthBefore;
|
|
assertTrue(receivedEth > 0);
|
|
|
|
uint256 totalReservesAfter = treasury.totalReserves();
|
|
uint256 totalSupplyAfter = ftso.totalSupply();
|
|
|
|
uint256 expectedBounty = amountToBridge * bountyPercent / type(uint32).max;
|
|
uint256 totalBountyFtso = ghst.balanceFrom(expectedBounty);
|
|
uint256 totalBountyReserveValue = totalBountyFtso * totalReservesAfter / totalSupplyAfter;
|
|
|
|
uint256 fraction = calculator.fraction();
|
|
uint256 totalBountyInEth = totalBountyReserveValue * 1e18 / fraction;
|
|
totalBountyInEth = totalBountyInEth * 1e18 / 1e9;
|
|
|
|
uint256 physicalGasSpentEth = 2 * 1e9 * 329188; // 2 gwei * gas Used
|
|
assertApproxEqAbs(receivedEth, totalBountyInEth - physicalGasSpentEth, 1);
|
|
}
|
|
|
|
{
|
|
uint256 totalReservesAfter = treasury.totalReserves();
|
|
uint256 totalSupplyAfter = ftso.totalSupply();
|
|
|
|
uint256 backingRatioBefore = totalReservesBefore * 1e18 / totalSupplyBefore;
|
|
uint256 backingRatioAfter = totalReservesAfter * 1e18 / totalSupplyAfter;
|
|
uint256 receivedValue = treasury.tokenValue(address(reserve), evmReceiver.balance);
|
|
|
|
assertEq(backingRatioAfter, backingRatioBefore);
|
|
assertApproxEqAbs(totalReservesAfter + receivedValue, totalReservesBefore, 2000); // 2e-16%
|
|
|
|
vm.prank(GOVERNOR);
|
|
treasury.auditReserves();
|
|
assertEq(treasury.totalReserves() + receivedValue, totalReservesBefore);
|
|
}
|
|
|
|
{
|
|
uint256 expectedMintToReceiver = amountToBridge * (type(uint32).max - bountyPercent) / type(uint32).max;
|
|
uint256 bobGhstAfter = ghst.balanceOf(BOB);
|
|
assertApproxEqAbs(ghostedSupplyBefore - bobGhstAfter - expectedMintToReceiver, gatekeeper.ghostedSupply(), 1);
|
|
}
|
|
}
|
|
}
|