make everyone happy about the phantomRefund with help of maturity
Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
This commit is contained in:
parent
ef598ae702
commit
2623451103
@ -41,6 +41,11 @@ contract GhostBondDepository is IBondDepository, NoteKeeper {
|
||||
WETH = wrapped;
|
||||
}
|
||||
|
||||
function addNote(address user, uint256 payout, uint48 expiry) external returns (uint256) {
|
||||
if (msg.sender != TREASURY) revert NotTreasury();
|
||||
return _addNote(user, payout, expiry, type(uint48).max);
|
||||
}
|
||||
|
||||
function deposit(
|
||||
uint256 id,
|
||||
uint256 amount,
|
||||
@ -77,7 +82,7 @@ contract GhostBondDepository is IBondDepository, NoteKeeper {
|
||||
|
||||
emit Bond(id, amount, price);
|
||||
|
||||
index = addNote(user, payout, uint48(expiry), uint48(id)); // forge-lint: disable-line(unsafe-typecast)
|
||||
index = _addNote(user, payout, uint48(expiry), uint48(id)); // forge-lint: disable-line(unsafe-typecast)
|
||||
|
||||
IWETH9(WETH).deposit{value: msg.value}();
|
||||
IERC20(WETH).safeTransfer(TREASURY, msg.value);
|
||||
|
||||
@ -25,6 +25,7 @@ contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard {
|
||||
uint256 public constant EXISTENTIAL_DEPOSIT = 500 * 1e12; // 0.005
|
||||
uint256 public constant FULL_GAS_RESTORATION_TIME = 3600; // 1 hour
|
||||
uint256 public constant MAX_ALLOWED_GAS_PRICE = 3e9; // 3 gwei
|
||||
uint256 public constant PHANTOM_EXPIRY = 432000; // seconds, 5 days
|
||||
uint256 public constant GAS_EXECUTION_BUFFER = 21000 + 5969 + 50955;
|
||||
uint256 public constant REGISTRY_INDEX = 0;
|
||||
|
||||
@ -160,7 +161,7 @@ contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard {
|
||||
_lastVerificationTime = block.timestamp;
|
||||
|
||||
uint256 gasSpent = (gasStart - gasleft() + GAS_EXECUTION_BUFFER) * currentGasPrice;
|
||||
try IStaking(staking).phantomRefund(msg.sender, gasSpent, REGISTRY_INDEX) {}
|
||||
try IStaking(staking).phantomRefund(msg.sender, gasSpent, REGISTRY_INDEX, PHANTOM_EXPIRY) {}
|
||||
catch {
|
||||
emit VoluntaryVerification(msg.sender, gasSpent);
|
||||
}
|
||||
|
||||
@ -171,14 +171,19 @@ contract GhostStaking is IStaking, GhostAccessControlled {
|
||||
IGatekeeper(gatekeeper).ghost(receiver, amount);
|
||||
}
|
||||
|
||||
function phantomRefund(address receiver, uint256 amount, uint256 registryIndex) external override {
|
||||
function phantomRefund(
|
||||
address receiver,
|
||||
uint256 amount,
|
||||
uint256 registryIndex,
|
||||
uint256 phantomExpiry
|
||||
) external override {
|
||||
if (gatekeeper != msg.sender) revert NotGatekeeper();
|
||||
|
||||
// forge-lint: disable-next-line(unsafe-typecast)
|
||||
uint48 phantomRelease = uint48(block.timestamp + phantomExpiry);
|
||||
address treasury = ISTNK(stnk).treasury();
|
||||
|
||||
uint256 gasFtsoAmount = ITreasury(treasury).phantomRefund(amount, registryIndex);
|
||||
uint256 gasGhstAmount = IGHST(ghst).balanceTo(gasFtsoAmount);
|
||||
|
||||
IGHST(ghst).mint(receiver, gasGhstAmount);
|
||||
ITreasury(treasury).phantomRefund(receiver, phantomRelease, amount, registryIndex);
|
||||
}
|
||||
|
||||
function recall(address receiver, uint256 amount) external override {
|
||||
|
||||
@ -18,6 +18,7 @@ import {FullMath} from "./libraries/FullMath.sol";
|
||||
import {IFTSO} from "./interfaces/IFTSO.sol";
|
||||
import {IBondingCalculator} from "./interfaces/IBondingCalculator.sol";
|
||||
import {ITreasury} from "./interfaces/ITreasury.sol";
|
||||
import {IBondDepository} from "./interfaces/IBondDepository.sol";
|
||||
import {IGhostAuthority} from "./interfaces/IGhostAuthority.sol";
|
||||
|
||||
contract GhostTreasury is GhostAccessControlled, ITreasury {
|
||||
@ -25,6 +26,7 @@ contract GhostTreasury is GhostAccessControlled, ITreasury {
|
||||
using EnumerableSet for EnumerableSet.AddressSet;
|
||||
|
||||
address public immutable FTSO;
|
||||
address public bondDepository;
|
||||
uint256 public totalReserves;
|
||||
|
||||
mapping(STATUS => EnumerableSet.AddressSet) private _registry;
|
||||
@ -77,20 +79,22 @@ contract GhostTreasury is GhostAccessControlled, ITreasury {
|
||||
}
|
||||
|
||||
function phantomRefund(
|
||||
address receiver,
|
||||
uint48 phantomRelease,
|
||||
uint256 gasSpent,
|
||||
uint256 index
|
||||
uint256 registryIndex
|
||||
) external override returns (uint256 gasValue) {
|
||||
if (!_permissions[STATUS.STAKING][msg.sender]) revert NotApproved();
|
||||
|
||||
address reserveToken = _registry[STATUS.RESERVETOKEN].at(index);
|
||||
address reserveToken = _registry[STATUS.RESERVETOKEN].at(registryIndex);
|
||||
if (!_permissions[STATUS.RESERVETOKEN][reserveToken]) revert NotApproved();
|
||||
|
||||
uint256 totalSupply = IERC20(FTSO).totalSupply();
|
||||
gasValue = tokenValue(reserveToken, gasSpent);
|
||||
gasValue = FullMath.mulDiv(gasValue, totalSupply, totalReserves);
|
||||
|
||||
if (gasValue <= excessReserves()) {
|
||||
IFTSO(FTSO).mint(msg.sender, gasValue);
|
||||
if (gasValue > 0 && gasValue <= excessReserves()) {
|
||||
IBondDepository(bondDepository).addNote(receiver, gasValue, phantomRelease);
|
||||
}
|
||||
}
|
||||
|
||||
@ -178,6 +182,15 @@ contract GhostTreasury is GhostAccessControlled, ITreasury {
|
||||
emit Permissioned(toDisable, status, false);
|
||||
}
|
||||
|
||||
function setBondDepository(address newBondDepository) external {
|
||||
if (
|
||||
msg.sender != authority.governor() &&
|
||||
msg.sender != authority.guardian()
|
||||
) revert NotApproved();
|
||||
bondDepository = newBondDepository;
|
||||
emit BondDepositoryUpdated(newBondDepository);
|
||||
}
|
||||
|
||||
function forfeitReserves(
|
||||
address router,
|
||||
uint256 liquidity,
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
interface IBondDepository {
|
||||
error NotTreasury();
|
||||
error DepositoryConcluded(uint48 concludedTime);
|
||||
error DepositoryMaxPrice(uint256 price, uint256 maxPrice);
|
||||
error DepositoryMaxSize(uint256 payout, uint256 maxPayout);
|
||||
@ -75,6 +76,7 @@ interface IBondDepository {
|
||||
) external returns (uint256 id_);
|
||||
|
||||
function close(uint256 _id) external;
|
||||
function addNote(address user, uint256 payout, uint48 expiry) external returns (uint256);
|
||||
function isLive(uint256 _bid) external view returns (bool);
|
||||
function liveMarkets() external view returns (uint256[] memory);
|
||||
function liveMarketsFor(address _quoteToken) external view returns (uint256[] memory);
|
||||
|
||||
@ -70,7 +70,7 @@ interface IStaking {
|
||||
function ghost(bytes32 receiver, uint256 amount) external;
|
||||
function recall(address receiver, uint256 amount) external;
|
||||
function recall(uint256 amount, uint256 registryIndex) external returns (address, uint256);
|
||||
function phantomRefund(address receiver, uint256 amount, uint256 registryIndex) external;
|
||||
function phantomRefund(address receiver, uint256 amount, uint256 registryIndex, uint256 phantomExpiry) external;
|
||||
function rebase() external returns (uint256);
|
||||
|
||||
function index() external view returns (uint256);
|
||||
|
||||
@ -21,6 +21,7 @@ interface ITreasury {
|
||||
event Minted(address indexed caller, address indexed recipient, uint256 amount);
|
||||
event Permissioned(address addr, STATUS indexed status, bool result);
|
||||
event Withdrawal(address indexed token, uint256 indexed amount, uint256 value);
|
||||
event BondDepositoryUpdated(address bondDepository);
|
||||
|
||||
function deposit(
|
||||
address _token,
|
||||
@ -29,7 +30,7 @@ interface ITreasury {
|
||||
) external returns (uint256);
|
||||
|
||||
function registered(STATUS status, address token) external view returns(bool);
|
||||
function phantomRefund(uint256 gasSpent, uint256 amount) external returns (uint256);
|
||||
function phantomRefund(address receiver, uint48 phantomExpiry, uint256 gasSpent, uint256 amount) external returns (uint256);
|
||||
function buyBack(address receiver, uint256 amount, uint256 index) external returns (address, uint256);
|
||||
function withdraw(address token, uint256 amount) external;
|
||||
function mint(address _recipient, uint256 _amount) external;
|
||||
|
||||
@ -27,7 +27,7 @@ abstract contract NoteKeeper is INoteKeeper {
|
||||
_TREASURY = ITreasury(_treasuryAddress);
|
||||
}
|
||||
|
||||
function addNote(
|
||||
function _addNote(
|
||||
address user,
|
||||
uint256 payout,
|
||||
uint48 expiry,
|
||||
|
||||
@ -5,6 +5,7 @@ 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 {GhostBondDepository} from "../../src/BondDepository.sol";
|
||||
import {GhostAuthority} from "../../src/GhostAuthority.sol";
|
||||
import {GhostTreasury} from "../../src/Treasury.sol";
|
||||
import {GhostStaking} from "../../src/Staking.sol";
|
||||
@ -46,6 +47,7 @@ contract GatekeeperRecallTest is Test {
|
||||
GhostTreasury treasury;
|
||||
GhostAuthority authority;
|
||||
GhostBondingCalculator calculator;
|
||||
GhostBondDepository bondDepository;
|
||||
WETH9 reserve;
|
||||
|
||||
function setUp() public {
|
||||
@ -71,15 +73,25 @@ contract GatekeeperRecallTest is Test {
|
||||
);
|
||||
treasury = new GhostTreasury(address(ftso), address(authority));
|
||||
calculator = new GhostBondingCalculator(address(ftso), 6000, 3);
|
||||
bondDepository = new GhostBondDepository(
|
||||
address(authority),
|
||||
address(ftso),
|
||||
address(ghst),
|
||||
address(staking),
|
||||
address(treasury),
|
||||
address(reserve)
|
||||
);
|
||||
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.REWARDMANAGER, address(bondDepository), address(0));
|
||||
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));
|
||||
treasury.setBondDepository(address(bondDepository));
|
||||
vm.stopPrank();
|
||||
|
||||
vm.deal(ALICE, INITIAL_MINT);
|
||||
@ -109,11 +121,11 @@ contract GatekeeperRecallTest is Test {
|
||||
|
||||
vm.warp(block.timestamp + gatekeeper.FULL_GAS_RESTORATION_TIME() * 69);
|
||||
|
||||
uint256 bobBalanceBeforeFirst = ghst.balanceOf(BOB);
|
||||
uint256 priceBeforeFirst = gatekeeper.getCurrentGasPrice();
|
||||
assertEq(priceBeforeFirst, gatekeeper.MAX_ALLOWED_GAS_PRICE());
|
||||
assertEq(ghst.balanceOf(BOB), 0);
|
||||
|
||||
vm.txGasPrice(2 gwei);
|
||||
vm.prank(BOB, BOB);
|
||||
gatekeeper.verify(
|
||||
hex"bf06188a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000017508f1956a800000000000000000000000000000000000000000020000000000007a6980000000",
|
||||
@ -121,10 +133,12 @@ contract GatekeeperRecallTest is Test {
|
||||
0x8c3fc03b5cb738777ceec396ee8647f782d6670992f8f973de2dfc83379978d5
|
||||
);
|
||||
|
||||
uint256 bobBalanceAfterFirst = ghst.balanceOf(BOB);
|
||||
assertGt(bobBalanceAfterFirst, bobBalanceBeforeFirst);
|
||||
(uint256 pendingPayout, bool matured) = bondDepository.pendingFor(BOB, 0);
|
||||
assertGt(pendingPayout, 0); // 19273751457699280
|
||||
assertFalse(matured);
|
||||
assertEq(gatekeeper.getCurrentGasPrice(), 0);
|
||||
|
||||
vm.txGasPrice(2 gwei);
|
||||
vm.prank(BOB, BOB);
|
||||
gatekeeper.verify(
|
||||
hex"3137142600000000000000000000000000000000000000000000000000000000000000120000000000007a69010101010101010101010101010101010101010100000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000042b7ce50000000000000000000000000000000000000000000000000000000000",
|
||||
@ -132,13 +146,12 @@ contract GatekeeperRecallTest is Test {
|
||||
0xdbeed3eb94a829710147595c1b10f2ac6ec761c9721d8cc4711842c212ac517e
|
||||
);
|
||||
|
||||
assertEq(ghst.balanceOf(BOB), bobBalanceAfterFirst);
|
||||
assertEq(gatekeeper.getCurrentGasPrice(), 0);
|
||||
|
||||
vm.warp(block.timestamp + gatekeeper.FULL_GAS_RESTORATION_TIME() / 2);
|
||||
uint256 gasPriceRestored = gatekeeper.getCurrentGasPrice();
|
||||
assertEq(gasPriceRestored, gatekeeper.MAX_ALLOWED_GAS_PRICE() / 2);
|
||||
|
||||
vm.txGasPrice(2 gwei);
|
||||
vm.prank(BOB, BOB);
|
||||
gatekeeper.verify(
|
||||
hex"31371426000000000000000000000000000000000000000000000000000000000000000d0000000000007a6901010101010101010101010101010101010101010000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064e4e33ef800000000000000000000000000000000000000000000000000000000000000010000000000000000000000000101010101010101010101010101010101010101000000000000000000000000010101010101010101010101010101010101010100000000000000000000000000000000000000000000000000000000",
|
||||
@ -146,8 +159,13 @@ contract GatekeeperRecallTest is Test {
|
||||
0xc2da93948f869c5102992bd1cbd70ef5f821ff522305c69d113faf2e89570ce7
|
||||
);
|
||||
|
||||
assertGt(ghst.balanceOf(BOB), bobBalanceAfterFirst);
|
||||
(pendingPayout, matured) = bondDepository.pendingFor(BOB, 1);
|
||||
assertGt(pendingPayout, 69);
|
||||
assertFalse(matured);
|
||||
assertEq(gatekeeper.getCurrentGasPrice(), 0);
|
||||
|
||||
uint256[] memory indexes = bondDepository.indexesFor(BOB);
|
||||
assertEq(indexes.length, 2);
|
||||
}
|
||||
|
||||
function test_recallChainWorks() public {
|
||||
@ -168,7 +186,6 @@ contract GatekeeperRecallTest is Test {
|
||||
uint256 actualAmount = 105000000000000000;
|
||||
address actualReceiver = address(0x0000000000000000000000000000000000000002);
|
||||
|
||||
uint256 bobGhstBefore = ghst.balanceOf(BOB);
|
||||
uint256 receiverEthBefore = actualReceiver.balance;
|
||||
uint256 receiverGhstBefore = ghst.balanceOf(actualReceiver);
|
||||
uint256 totalReservesBefore = treasury.totalReserves();
|
||||
@ -185,14 +202,15 @@ contract GatekeeperRecallTest is Test {
|
||||
vm.stopPrank();
|
||||
|
||||
{
|
||||
uint256 bobGhstAfter = ghst.balanceOf(BOB);
|
||||
(uint256 bobPhantomClaimAfter, bool maturedAfter) = bondDepository.pendingFor(BOB, 0);
|
||||
uint256 receiverGhstAfter = ghst.balanceOf(actualReceiver);
|
||||
|
||||
assertFalse(maturedAfter);
|
||||
assertTrue(
|
||||
ghst.balanceFrom(bobGhstAfter - bobGhstBefore) * totalReservesBefore / totalSupplyBefore
|
||||
ghst.balanceFrom(bobPhantomClaimAfter) * totalReservesBefore / totalSupplyBefore
|
||||
> gatekeeper.GAS_EXECUTION_BUFFER()
|
||||
);
|
||||
assertTrue(bobGhstAfter > bobGhstBefore);
|
||||
|
||||
assertApproxEqAbs(receiverGhstAfter - receiverGhstBefore, actualAmount / 2, 1e18);
|
||||
}
|
||||
|
||||
@ -211,7 +229,8 @@ contract GatekeeperRecallTest is Test {
|
||||
uint256 backingRatioBefore = totalReservesBefore * 1e18 / totalSupplyBefore;
|
||||
uint256 backingRatioAfter = treasury.totalReserves() * 1e18 / ftso.totalSupply();
|
||||
|
||||
uint256 gasFtsoMinted = ghst.balanceFrom(ghst.balanceOf(BOB) - bobGhstBefore);
|
||||
(uint256 bobPhantomClaim,) = bondDepository.pendingFor(BOB, 0);
|
||||
uint256 gasFtsoMinted = ghst.balanceFrom(bobPhantomClaim);
|
||||
uint256 expectedDelta = (backingRatioBefore * gasFtsoMinted) / totalSupplyBefore;
|
||||
|
||||
assertApproxEqAbs(backingRatioAfter, backingRatioBefore - expectedDelta, 1000);
|
||||
@ -220,5 +239,22 @@ contract GatekeeperRecallTest is Test {
|
||||
treasury.auditReserves();
|
||||
assertApproxEqAbs(treasury.totalReserves() * 1e18 / ftso.totalSupply(), backingRatioBefore - expectedDelta, 2000);
|
||||
}
|
||||
|
||||
{
|
||||
(uint256 phantomClaim, bool claimMatured) = bondDepository.pendingFor(BOB, 0);
|
||||
assertFalse(claimMatured);
|
||||
|
||||
vm.expectRevert();
|
||||
bondDepository.redeemAll(BOB, true);
|
||||
|
||||
vm.warp(block.timestamp + gatekeeper.PHANTOM_EXPIRY() + 69);
|
||||
(phantomClaim, claimMatured) = bondDepository.pendingFor(BOB, 0);
|
||||
assertTrue(claimMatured);
|
||||
|
||||
vm.prank(BOB);
|
||||
uint256 actualPayout = bondDepository.redeemAll(BOB, true);
|
||||
assertEq(phantomClaim, actualPayout);
|
||||
assertEq(actualPayout, ghst.balanceOf(BOB));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user