make the dream a reality; treasury buyBack added
Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
This commit is contained in:
parent
2e35fb24c7
commit
fe9f92281b
@ -69,9 +69,6 @@ INITIAL_INDEX=
|
||||
COEFFICIENT_NUMERATOR=
|
||||
COEFFICIENT_DENOMINATOR=
|
||||
|
||||
## Blocks needed for permissions to take place, only needed if timelock enabled
|
||||
BLOCKS_NEEDED_FOR_TREASURY_QUEUE=
|
||||
|
||||
## Multiplier for each native coin where result of multiplication represents amount
|
||||
## of mint tokens.
|
||||
RESERVE_MINT_RATE=
|
||||
@ -105,10 +102,8 @@ GOVERNOR_PROPOSAL_THRESHOLD=
|
||||
GOVERNOR_QUORUM_FRACTION=
|
||||
|
||||
###################### Initial ghosted supply on gatekeeper ###########################
|
||||
## existential - minimum amount that could be reflected inside other chain ##
|
||||
## previousWeaver - previous weaver address if any to make linked list of weavers ##
|
||||
#######################################################################################
|
||||
INITIAL_EXISTENTIAL_DEPOSIT=
|
||||
PREVIOUS_WEAVER_ADDRESS=
|
||||
|
||||
SEPOLIA_TEST_RPC_URL=
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import {IWETH9} from "./interfaces/IWETH9.sol";
|
||||
import {IStaking} from "./interfaces/IStaking.sol";
|
||||
import {IGatekeeper} from "./interfaces/IGatekeeper.sol";
|
||||
import {IStorageHistory} from "./interfaces/IStorageHistory.sol";
|
||||
@ -24,10 +25,13 @@ contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard {
|
||||
uint256 private constant SECP256K1_Q = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141;
|
||||
uint256 private constant SHIFT_FACTOR = (2**128) % SECP256K1_N;
|
||||
|
||||
uint256 public constant GAS_RECALL_SPENDING = 329188;
|
||||
uint256 public constant EXISTENTIAL_DEPOSIT = 500 * 1e12;
|
||||
uint256 public constant REGISTRY_INDEX = 0;
|
||||
|
||||
address public override staking;
|
||||
address public override deployer;
|
||||
address public override storageHistory;
|
||||
uint256 public override existentialDeposit;
|
||||
|
||||
address private _previousAddress;
|
||||
bool private _initialized;
|
||||
@ -35,16 +39,14 @@ contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard {
|
||||
Checkpoints.Trace256 private _aggregatedPublicKeys;
|
||||
mapping(bytes32 => uint256) private _packedRotationStates;
|
||||
|
||||
constructor(
|
||||
uint256 _existentialDeposit,
|
||||
address _storageHistory
|
||||
) {
|
||||
existentialDeposit = _existentialDeposit;
|
||||
constructor(address _storageHistory) {
|
||||
storageHistory = _storageHistory;
|
||||
staking = msg.sender;
|
||||
deployer = tx.origin;
|
||||
}
|
||||
|
||||
receive() external payable {}
|
||||
|
||||
function initialize(address _previousGatekeeperAddress) external override {
|
||||
if (_previousGatekeeperAddress != address(0)) {
|
||||
require(_initialized == false);
|
||||
@ -124,7 +126,7 @@ contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard {
|
||||
|
||||
function ghost(bytes32 receiver, uint256 amount) external override returns (uint256) {
|
||||
if (msg.sender != staking) revert NotStaking();
|
||||
if (amount < existentialDeposit) revert NonExistentAmount();
|
||||
if (amount < EXISTENTIAL_DEPOSIT) revert NonExistentAmount();
|
||||
|
||||
IStorageHistory(storageHistory).increaseBridgeIn(amount);
|
||||
|
||||
@ -143,25 +145,25 @@ contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard {
|
||||
if (payload.chainId != block.chainid) revert WrongChainId();
|
||||
|
||||
StorageHistory(storageHistory).trySetTransactionExecuted(exodusSession);
|
||||
IStorageHistory(storageHistory).tryIncreaseBridgeOut(amount);
|
||||
|
||||
uint256 bountyAmount = FullMath.mulDiv(amount, uint256(payload.bounty), BOUNTY_DIVISOR);
|
||||
uint256 bountyAmount = FullMath.mulDiv(amount, BOUNTY_DIVISOR - uint256(payload.bounty), BOUNTY_DIVISOR);
|
||||
uint256 receiverAmount = amount - bountyAmount;
|
||||
uint256 bridgeOutImbalance = receiverAmount;
|
||||
|
||||
IStaking(staking).recall(payload.receiver, receiverAmount);
|
||||
|
||||
if (bountyAmount > 0) {
|
||||
uint256 totalReserves = IStaking(staking).totalReserves();
|
||||
uint256 baseSupply = IStaking(staking).baseSupply();
|
||||
uint256 minimumNativeRequired = FullMath.mulDiv(bountyAmount, totalReserves, baseSupply);
|
||||
uint256 gasSpent = GAS_RECALL_SPENDING * tx.gasprice;
|
||||
(address token, uint256 forGas, uint256 sent) = IStaking(staking).recall(bountyAmount, gasSpent, REGISTRY_INDEX);
|
||||
bridgeOutImbalance += forGas;
|
||||
|
||||
if (minimumNativeRequired > msg.value) revert InsufficientValue();
|
||||
IStaking(staking).recall(tx.origin, bountyAmount);
|
||||
|
||||
(bool sentSuccess,) = payload.receiver.call{ value: msg.value }("");
|
||||
IWETH9(token).withdraw(sent);
|
||||
(bool sentSuccess,) = payload.receiver.call{ value: sent }("");
|
||||
if (!sentSuccess) revert SendFailed();
|
||||
}
|
||||
|
||||
IStorageHistory(storageHistory).tryIncreaseBridgeOut(bridgeOutImbalance);
|
||||
|
||||
emit Recalled(payload.receiver, amount);
|
||||
}
|
||||
|
||||
|
||||
@ -9,6 +9,7 @@ import {Gatekeeper} from "./Gatekeeper.sol";
|
||||
import {StorageHistory} from "./types/StorageHistory.sol";
|
||||
import {GhostAccessControlled} from "./types/GhostAccessControlled.sol";
|
||||
|
||||
import {IFTSO} from "./interfaces/IFTSO.sol";
|
||||
import {ISTNK} from "./interfaces/ISTNK.sol";
|
||||
import {IGHST} from "./interfaces/IGHST.sol";
|
||||
import {IStaking} from "./interfaces/IStaking.sol";
|
||||
@ -46,8 +47,7 @@ contract GhostStaking is IStaking, GhostAccessControlled {
|
||||
uint48 _epochLength,
|
||||
uint48 _firstEpochNumber,
|
||||
uint48 _firstEpochTime,
|
||||
address _authority,
|
||||
uint256 _existentialDeposit
|
||||
address _authority
|
||||
) GhostAccessControlled(IGhostAuthority(_authority)) {
|
||||
ftso = _ftso;
|
||||
stnk = _stnk;
|
||||
@ -62,7 +62,7 @@ contract GhostStaking is IStaking, GhostAccessControlled {
|
||||
|
||||
GhostWarmup newWarmup = new GhostWarmup(_ghst);
|
||||
StorageHistory newHistory = new StorageHistory();
|
||||
Gatekeeper newGatekeeper = new Gatekeeper(_existentialDeposit, address(newHistory));
|
||||
Gatekeeper newGatekeeper = new Gatekeeper(address(newHistory));
|
||||
|
||||
IStorageHistory(newHistory).setOwner(address(newGatekeeper));
|
||||
IGatekeeper(newGatekeeper).initialize(address(0));
|
||||
@ -172,6 +172,29 @@ contract GhostStaking is IStaking, GhostAccessControlled {
|
||||
IGHST(ghst).mint(receiver, amount);
|
||||
}
|
||||
|
||||
function recall(
|
||||
uint256 amount,
|
||||
uint256 gasSpent,
|
||||
uint256 registryIndex
|
||||
) external override returns (address reserveToken, uint256 gasGhstAmount, uint256 value) {
|
||||
if (gatekeeper != msg.sender) revert NotGatekeeper();
|
||||
|
||||
address treasury = ISTNK(stnk).treasury();
|
||||
uint256 ftsoAmount = IGHST(ghst).balanceFrom(amount);
|
||||
|
||||
uint256 gasFtsoAmount;
|
||||
(reserveToken, value, gasFtsoAmount) = ITreasury(treasury).buyBack(
|
||||
msg.sender,
|
||||
ftsoAmount,
|
||||
gasSpent,
|
||||
registryIndex
|
||||
);
|
||||
|
||||
gasGhstAmount = IGHST(ghst).balanceTo(gasFtsoAmount);
|
||||
IFTSO(ftso).burn(ftsoAmount - gasFtsoAmount);
|
||||
IGHST(ghst).mint(tx.origin, gasGhstAmount);
|
||||
}
|
||||
|
||||
function rebase() public override returns (uint256 bounty) {
|
||||
if (epoch.end <= block.timestamp && block.number > _lastRebaseBlock) {
|
||||
ISTNK(stnk).rebase(epoch.distribute, epoch.number);
|
||||
|
||||
132
src/Treasury.sol
132
src/Treasury.sol
@ -4,6 +4,7 @@ pragma solidity ^0.8.20;
|
||||
import {IERC20} from "@openzeppelin-contracts/token/ERC20/IERC20.sol";
|
||||
import {IERC20Metadata} from "@openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol";
|
||||
import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
|
||||
import {EnumerableSet} from "@openzeppelin-contracts/utils/structs/EnumerableSet.sol";
|
||||
|
||||
import {IUniswapV2Factory} from "@uniswap-v2-core-1.0.1/interfaces/IUniswapV2Factory.sol";
|
||||
import {IUniswapV2Pair} from "@uniswap-v2-core-1.0.1/interfaces/IUniswapV2Pair.sol";
|
||||
@ -21,25 +22,22 @@ import {IGhostAuthority} from "./interfaces/IGhostAuthority.sol";
|
||||
|
||||
contract GhostTreasury is GhostAccessControlled, ITreasury {
|
||||
using SafeERC20 for IERC20;
|
||||
using EnumerableSet for EnumerableSet.AddressSet;
|
||||
|
||||
address public immutable ftso; // forge-lint: disable-line(screaming-snake-case-immutable)
|
||||
uint256 public immutable blocksNeededForQueue; // forge-lint: disable-line(screaming-snake-case-immutable)
|
||||
address public immutable FTSO;
|
||||
|
||||
uint256 public totalReserves;
|
||||
uint256 public totalDebt;
|
||||
uint256 public ftsoDebt;
|
||||
|
||||
mapping(STATUS => address[]) public registry;
|
||||
mapping(STATUS => mapping(address => bool)) public permissions;
|
||||
mapping(STATUS => EnumerableSet.AddressSet) private _registry;
|
||||
mapping(STATUS => mapping(address => bool)) private _permissions;
|
||||
mapping(address => address) public bondCalculator;
|
||||
|
||||
constructor(
|
||||
address _ftso,
|
||||
uint256 _timelock,
|
||||
address _authority
|
||||
) GhostAccessControlled(IGhostAuthority(_authority)) {
|
||||
ftso = _ftso;
|
||||
blocksNeededForQueue = _timelock;
|
||||
FTSO = _ftso;
|
||||
}
|
||||
|
||||
function deposit(
|
||||
@ -47,30 +45,30 @@ contract GhostTreasury is GhostAccessControlled, ITreasury {
|
||||
uint256 amount,
|
||||
uint256 profit
|
||||
) external override returns (uint256 send) {
|
||||
if (permissions[STATUS.RESERVETOKEN][token]) {
|
||||
if (!permissions[STATUS.RESERVEDEPOSITOR][msg.sender]) revert NotApproved();
|
||||
} else if (permissions[STATUS.LIQUIDITYTOKEN][token]) {
|
||||
if (!permissions[STATUS.LIQUIDITYDEPOSITOR][msg.sender]) revert NotApproved();
|
||||
if (_permissions[STATUS.RESERVETOKEN][token]) {
|
||||
if (!_permissions[STATUS.RESERVEDEPOSITOR][msg.sender]) revert NotApproved();
|
||||
} else if (_permissions[STATUS.LIQUIDITYTOKEN][token]) {
|
||||
if (!_permissions[STATUS.LIQUIDITYDEPOSITOR][msg.sender]) revert NotApproved();
|
||||
} else revert InvalidToken();
|
||||
|
||||
IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
|
||||
uint256 value = tokenValue(token, amount);
|
||||
send = value - profit;
|
||||
IFTSO(ftso).mint(msg.sender, send);
|
||||
IFTSO(FTSO).mint(msg.sender, send);
|
||||
|
||||
totalReserves = totalReserves + value;
|
||||
emit Deposit(token, amount, value);
|
||||
}
|
||||
|
||||
function mint(address recipient, uint256 amount) external override {
|
||||
if (!permissions[STATUS.REWARDMANAGER][msg.sender]) revert NotApproved();
|
||||
if (!_permissions[STATUS.REWARDMANAGER][msg.sender]) revert NotApproved();
|
||||
if (amount > excessReserves()) revert InsufficientReserves();
|
||||
IFTSO(ftso).mint(recipient, amount);
|
||||
IFTSO(FTSO).mint(recipient, amount);
|
||||
emit Minted(msg.sender, recipient, amount);
|
||||
}
|
||||
|
||||
function withdraw(address token, uint256 amount) external onlyGovernor override {
|
||||
if (!permissions[STATUS.RESERVETOKEN][token]) revert NotAccepted();
|
||||
if (!_permissions[STATUS.RESERVETOKEN][token]) revert NotAccepted();
|
||||
|
||||
uint256 value = tokenValue(token, amount);
|
||||
totalReserves = totalReserves - value;
|
||||
@ -79,6 +77,30 @@ contract GhostTreasury is GhostAccessControlled, ITreasury {
|
||||
emit Withdrawal(token, amount, value);
|
||||
}
|
||||
|
||||
function buyBack(
|
||||
address receiver,
|
||||
uint256 amount,
|
||||
uint256 gasSpent,
|
||||
uint256 index
|
||||
) external override returns (address reserveToken, uint256 value, uint256 gasValue) {
|
||||
if (!_permissions[STATUS.STAKING][msg.sender]) revert NotApproved();
|
||||
|
||||
reserveToken = _registry[STATUS.RESERVETOKEN].at(index);
|
||||
if (!_permissions[STATUS.RESERVETOKEN][reserveToken]) revert NotApproved();
|
||||
|
||||
uint256 totalSupply = IERC20(FTSO).totalSupply();
|
||||
gasValue = tokenValue(reserveToken, gasSpent);
|
||||
gasValue = FullMath.mulDiv(gasValue, totalSupply, totalReserves);
|
||||
if (amount <= gasValue) revert GasExceedsBounty();
|
||||
|
||||
uint256 reservesToSend = FullMath.mulDiv(amount - gasValue, totalReserves, totalSupply);
|
||||
totalReserves = totalReserves - reservesToSend;
|
||||
|
||||
value = FullMath.mulDiv(reservesToSend, 1e18, IBondingCalculator(bondCalculator[reserveToken]).fraction());
|
||||
value = FullMath.mulDiv(value, 10**IERC20Metadata(reserveToken).decimals(), 1e9);
|
||||
IERC20(reserveToken).safeTransfer(receiver, value);
|
||||
}
|
||||
|
||||
function auditReserves() external {
|
||||
if (
|
||||
msg.sender != authority.governor() &&
|
||||
@ -86,12 +108,12 @@ contract GhostTreasury is GhostAccessControlled, ITreasury {
|
||||
) revert NotApproved();
|
||||
|
||||
uint256 reserves;
|
||||
address[] memory reserveTokens = registry[STATUS.RESERVETOKEN];
|
||||
uint256 reserveTokensLength = _registry[STATUS.RESERVETOKEN].length();
|
||||
|
||||
uint256 i;
|
||||
for (; i < reserveTokens.length;) {
|
||||
address reserveToken = reserveTokens[i];
|
||||
if (permissions[STATUS.RESERVETOKEN][reserveToken]) {
|
||||
for (; i < reserveTokensLength;) {
|
||||
address reserveToken = _registry[STATUS.RESERVETOKEN].at(i);
|
||||
if (_permissions[STATUS.RESERVETOKEN][reserveToken]) {
|
||||
reserves = reserves + tokenValue(
|
||||
reserveToken,
|
||||
IERC20(reserveToken).balanceOf(address(this))
|
||||
@ -100,11 +122,12 @@ contract GhostTreasury is GhostAccessControlled, ITreasury {
|
||||
unchecked { ++i; }
|
||||
}
|
||||
|
||||
address[] memory liquidityTokens = registry[STATUS.LIQUIDITYTOKEN];
|
||||
uint256 liquidityTokensLength = _registry[STATUS.LIQUIDITYTOKEN].length();
|
||||
|
||||
i = 0;
|
||||
for (; i < liquidityTokens.length;) {
|
||||
address liquidityToken = liquidityTokens[i];
|
||||
if (permissions[STATUS.LIQUIDITYTOKEN][liquidityToken]) {
|
||||
for (; i < liquidityTokensLength;) {
|
||||
address liquidityToken = _registry[STATUS.LIQUIDITYTOKEN].at(i);
|
||||
if (_permissions[STATUS.LIQUIDITYTOKEN][liquidityToken]) {
|
||||
reserves = reserves + tokenValue(
|
||||
liquidityToken,
|
||||
IERC20(liquidityToken).balanceOf(address(this))
|
||||
@ -122,11 +145,13 @@ contract GhostTreasury is GhostAccessControlled, ITreasury {
|
||||
address someAddress,
|
||||
address calculatorAddress
|
||||
) external onlyGovernor {
|
||||
permissions[status][someAddress] = true;
|
||||
(bool registered, ) = indexInRegistry(someAddress, status);
|
||||
if (!registered && (status == STATUS.LIQUIDITYTOKEN || status == STATUS.RESERVETOKEN)) {
|
||||
assert(calculatorAddress != address(0));
|
||||
registry[status].push(someAddress);
|
||||
_permissions[status][someAddress] = true;
|
||||
|
||||
bool alreadyRegistered = _registry[status].contains(someAddress);
|
||||
if (!alreadyRegistered && (status == STATUS.LIQUIDITYTOKEN || status == STATUS.RESERVETOKEN)) {
|
||||
if (calculatorAddress == address(0)) revert();
|
||||
|
||||
_registry[status].add(someAddress);
|
||||
bondCalculator[someAddress] = calculatorAddress;
|
||||
}
|
||||
emit Permissioned(someAddress, status, true);
|
||||
@ -137,7 +162,7 @@ contract GhostTreasury is GhostAccessControlled, ITreasury {
|
||||
msg.sender != authority.governor() &&
|
||||
msg.sender != authority.guardian()
|
||||
) revert NotApproved();
|
||||
permissions[status][toDisable] = false;
|
||||
_permissions[status][toDisable] = false;
|
||||
emit Permissioned(toDisable, status, false);
|
||||
}
|
||||
|
||||
@ -147,7 +172,7 @@ contract GhostTreasury is GhostAccessControlled, ITreasury {
|
||||
bool destroyerMode
|
||||
) external onlyGovernor {
|
||||
address weth = IUniswapV2Router01(router).WETH();
|
||||
address pair = IUniswapV2Factory(IUniswapV2Router01(router).factory()).getPair(ftso, weth);
|
||||
address pair = IUniswapV2Factory(IUniswapV2Router01(router).factory()).getPair(FTSO, weth);
|
||||
|
||||
IERC20(pair).safeTransfer(pair, liquidity);
|
||||
(uint256 amount0, uint256 amount1) = IUniswapV2Pair(pair).burn(address(this));
|
||||
@ -157,15 +182,15 @@ contract GhostTreasury is GhostAccessControlled, ITreasury {
|
||||
address token0 = IUniswapV2Pair(pair).token0();
|
||||
address token1 = IUniswapV2Pair(pair).token1();
|
||||
|
||||
if (token0 == ftso) {
|
||||
if (token0 == FTSO) {
|
||||
amountToDestroy = amount0;
|
||||
}
|
||||
|
||||
if (token1 == ftso) {
|
||||
if (token1 == FTSO) {
|
||||
amountToDestroy = amount1;
|
||||
}
|
||||
|
||||
IFTSO(ftso).burn(amountToDestroy);
|
||||
IFTSO(FTSO).burn(amountToDestroy);
|
||||
}
|
||||
}
|
||||
|
||||
@ -174,16 +199,16 @@ contract GhostTreasury is GhostAccessControlled, ITreasury {
|
||||
uint256 amount
|
||||
) external onlyGovernor {
|
||||
address weth = IUniswapV2Router01(router).WETH();
|
||||
address pair = IUniswapV2Factory(IUniswapV2Router01(router).factory()).getPair(ftso, weth);
|
||||
address pair = IUniswapV2Factory(IUniswapV2Router01(router).factory()).getPair(FTSO, weth);
|
||||
|
||||
IERC20(weth).approve(router, amount);
|
||||
|
||||
(uint256 reserve0, uint256 reserve1,) = IUniswapV2Pair(pair).getReserves();
|
||||
address[] memory path = new address[](2);
|
||||
path[0] = weth;
|
||||
path[1] = ftso;
|
||||
path[1] = FTSO;
|
||||
|
||||
if (ftso < weth) {
|
||||
if (FTSO < weth) {
|
||||
reserve0 = reserve1 ^ reserve0;
|
||||
reserve1 = reserve1 ^ reserve0;
|
||||
reserve0 = reserve1 ^ reserve0;
|
||||
@ -202,48 +227,41 @@ contract GhostTreasury is GhostAccessControlled, ITreasury {
|
||||
amountIn = amount - amountIn;
|
||||
|
||||
IERC20(weth).safeTransfer(pair, amountIn);
|
||||
IERC20(ftso).safeTransfer(pair, amounts[1]);
|
||||
IERC20(FTSO).safeTransfer(pair, amounts[1]);
|
||||
IUniswapV2Pair(pair).mint(address(this));
|
||||
}
|
||||
|
||||
function indexInRegistry(
|
||||
address someAddress,
|
||||
STATUS status
|
||||
) public view override returns (bool, uint256) {
|
||||
address[] memory entries = registry[status];
|
||||
uint256 i;
|
||||
for (; i < entries.length; ) {
|
||||
if (someAddress == entries[i]) {
|
||||
return (true, i);
|
||||
}
|
||||
unchecked { ++i; }
|
||||
}
|
||||
return (false, 0);
|
||||
function permissioned(STATUS status, address someAddress) external view returns (bool) {
|
||||
return _permissions[status][someAddress];
|
||||
}
|
||||
|
||||
function registered(STATUS status, address someAddress) external view returns (bool) {
|
||||
return _registry[status].contains(someAddress);
|
||||
}
|
||||
|
||||
function originalCoefficient() external view returns (uint256) {
|
||||
address[] memory reserveTokens = registry[STATUS.RESERVETOKEN];
|
||||
return IBondingCalculator(bondCalculator[reserveTokens[0]]).fraction();
|
||||
address reserveToken = _registry[STATUS.RESERVETOKEN].at(0);
|
||||
return IBondingCalculator(bondCalculator[reserveToken]).fraction();
|
||||
}
|
||||
|
||||
function excessReserves() public view override returns (uint256) {
|
||||
return totalReserves - (IFTSO(ftso).totalSupply() - totalDebt);
|
||||
return totalReserves - (IFTSO(FTSO).totalSupply() - totalDebt);
|
||||
}
|
||||
|
||||
function tokenValue(
|
||||
address token,
|
||||
uint256 amount
|
||||
) public view override returns (uint256 value) {
|
||||
if (permissions[STATUS.LIQUIDITYTOKEN][token]) {
|
||||
if (_permissions[STATUS.LIQUIDITYTOKEN][token]) {
|
||||
value = IBondingCalculator(bondCalculator[token]).valuation(token, amount);
|
||||
} else if (permissions[STATUS.RESERVETOKEN][token]) {
|
||||
} else if (_permissions[STATUS.RESERVETOKEN][token]) {
|
||||
value = FullMath.mulDiv(amount, 1e9, 10**IERC20Metadata(token).decimals());
|
||||
value = FullMath.mulDiv(value, IBondingCalculator(bondCalculator[token]).fraction(), 1e18);
|
||||
}
|
||||
}
|
||||
|
||||
function baseSupply() external view override returns (uint256) {
|
||||
return IFTSO(ftso).totalSupply() - ftsoDebt;
|
||||
return IFTSO(FTSO).totalSupply();
|
||||
}
|
||||
|
||||
function _quantityToBeSwapped(uint256 xa, uint256 x1) internal pure returns (uint256) {
|
||||
|
||||
@ -21,7 +21,6 @@ interface IGatekeeper {
|
||||
function deployer() external view returns (address);
|
||||
function ghostedSupply() external view returns (uint256);
|
||||
function storageHistory() external view returns (address);
|
||||
function existentialDeposit() external view returns (uint256);
|
||||
function latestPublicKeyInfo() external view returns (bytes32, uint8, uint64);
|
||||
function getRotationInfoAt(uint256 session) external view returns (bytes32, uint8, uint64);
|
||||
|
||||
|
||||
@ -69,6 +69,7 @@ interface IStaking {
|
||||
function unwrap(address _to, uint256 _amount) external returns (uint256 sBalance_);
|
||||
function ghost(bytes32 receiver, uint256 amount) external;
|
||||
function recall(address receiver, uint256 amount) external;
|
||||
function recall(uint256 amount, uint256 gasSpent, uint256 registryIndex) external returns (address, uint256, uint256);
|
||||
function rebase() external returns (uint256);
|
||||
|
||||
function index() external view returns (uint256);
|
||||
|
||||
@ -5,6 +5,7 @@ interface ITreasury {
|
||||
error NotApproved();
|
||||
error NotAccepted();
|
||||
error InvalidToken();
|
||||
error GasExceedsBounty();
|
||||
error InsufficientReserves();
|
||||
|
||||
enum STATUS {
|
||||
@ -12,7 +13,8 @@ interface ITreasury {
|
||||
RESERVETOKEN,
|
||||
LIQUIDITYDEPOSITOR,
|
||||
LIQUIDITYTOKEN,
|
||||
REWARDMANAGER
|
||||
REWARDMANAGER,
|
||||
STAKING
|
||||
}
|
||||
|
||||
event Deposit(address indexed token, uint256 amount, uint256 value);
|
||||
@ -27,10 +29,10 @@ interface ITreasury {
|
||||
uint256 _profit
|
||||
) external returns (uint256);
|
||||
|
||||
function buyBack(address receiver, uint256 amount, uint256 gasSpent, uint256 index) external returns (address, uint256, uint256);
|
||||
function withdraw(address token, uint256 amount) external;
|
||||
function mint(address _recipient, uint256 _amount) external;
|
||||
function tokenValue(address _token, uint256 _amount) external view returns (uint256 value_);
|
||||
function indexInRegistry(address _address, STATUS _status) external view returns (bool, uint256);
|
||||
function excessReserves() external view returns (uint256);
|
||||
function baseSupply() external view returns (uint256);
|
||||
function totalReserves() external view returns (uint256);
|
||||
|
||||
@ -75,10 +75,9 @@ contract GhostBondDepositoryTest is Test {
|
||||
EPOCH_LENGTH,
|
||||
EPOCH_NUMBER,
|
||||
EPOCH_END_TIME,
|
||||
address(authority),
|
||||
0
|
||||
address(authority)
|
||||
);
|
||||
treasury = new GhostTreasury(address(ftso), 69, address(authority));
|
||||
treasury = new GhostTreasury(address(ftso), address(authority));
|
||||
calculator = new GhostBondingCalculator(address(ftso), 1, 1);
|
||||
stnk.initialize(address(staking), address(treasury), address(ghst));
|
||||
ghst.initialize(address(staking));
|
||||
|
||||
@ -7,6 +7,7 @@ import {FullMath} from "../../src/libraries/FullMath.sol";
|
||||
import {RequestPacking} from "../../src/libraries/Packing.sol";
|
||||
import {IStorageHistory} from "../../src/interfaces/IStorageHistory.sol";
|
||||
import {StorageHistory} from "../../src/types/StorageHistory.sol";
|
||||
import {WETH9} from "../../src/mocks/WETH9.sol";
|
||||
|
||||
contract MockGovernance is Test {
|
||||
address public immutable GATEKEEPER;
|
||||
@ -137,12 +138,14 @@ contract MockGovernance is Test {
|
||||
|
||||
contract MockStaking is Test {
|
||||
Gatekeeper public gatekeeper;
|
||||
WETH9 public mockReserve;
|
||||
|
||||
mapping(address => uint256) private _recalledAmounts;
|
||||
|
||||
constructor(uint256 existential) {
|
||||
constructor() {
|
||||
mockReserve = new WETH9();
|
||||
StorageHistory history = new StorageHistory();
|
||||
gatekeeper = new Gatekeeper(existential, address(history));
|
||||
gatekeeper = new Gatekeeper(address(history));
|
||||
gatekeeper.initialize(address(0));
|
||||
history.setOwner(address(gatekeeper));
|
||||
}
|
||||
@ -169,6 +172,11 @@ contract MockStaking is Test {
|
||||
_recalledAmounts[receiver] += amount;
|
||||
}
|
||||
|
||||
function recall(uint256 amount, uint256, uint256) external returns (address, uint256, uint256){
|
||||
_recalledAmounts[tx.origin] += amount;
|
||||
return (address(mockReserve), 0, 0);
|
||||
}
|
||||
|
||||
function recalledAmount(address who) external view returns (uint256) {
|
||||
return _recalledAmounts[who];
|
||||
}
|
||||
@ -187,7 +195,6 @@ contract GatekeeperTest is Test {
|
||||
|
||||
address constant ALICE = 0x0000000000000000000000000000000000000001;
|
||||
address constant BOB = 0x0000000000000000000000000000000000000002;
|
||||
uint256 constant EXISTENTIAL = 1337;
|
||||
uint256 constant INIT_AMOUNT = 69 * 1e18;
|
||||
address constant DUMMY_ADDRESS = address(0x0101010101010101010101010101010101010101);
|
||||
|
||||
@ -199,7 +206,7 @@ contract GatekeeperTest is Test {
|
||||
|
||||
function setUp() public {
|
||||
vm.prank(ALICE, ALICE);
|
||||
staking = new MockStaking(EXISTENTIAL);
|
||||
staking = new MockStaking();
|
||||
gatekeeper = staking.gatekeeper();
|
||||
|
||||
MockGovernance tempGov = new MockGovernance(address(gatekeeper), DUMMY_ADDRESS);
|
||||
@ -215,7 +222,7 @@ contract GatekeeperTest is Test {
|
||||
}
|
||||
|
||||
function test_ghostTokensWork(uint256 ghostAmount) public {
|
||||
vm.assume(ghostAmount >= EXISTENTIAL && ghostAmount < type(uint96).max);
|
||||
vm.assume(ghostAmount >= gatekeeper.EXISTENTIAL_DEPOSIT() && ghostAmount < type(uint96).max);
|
||||
bytes32 receiver = bytes32(abi.encodePacked(ALICE));
|
||||
uint256 ghostedSupply = gatekeeper.ghostedSupply();
|
||||
|
||||
@ -235,7 +242,7 @@ contract GatekeeperTest is Test {
|
||||
}
|
||||
|
||||
function test_ghostTokensEmitsEvent(uint256 ghostAmount) public {
|
||||
vm.assume(ghostAmount >= EXISTENTIAL);
|
||||
vm.assume(ghostAmount >= gatekeeper.EXISTENTIAL_DEPOSIT());
|
||||
bytes32 receiver = bytes32(abi.encodePacked(ALICE));
|
||||
|
||||
vm.expectEmit(true, true, true, false, address(gatekeeper));
|
||||
@ -245,7 +252,7 @@ contract GatekeeperTest is Test {
|
||||
}
|
||||
|
||||
function test_recallWork(uint256 exodusSession, uint256 bobAmount, uint32 bobCommission) public {
|
||||
vm.assume(bobAmount > 1337 && bobAmount < 1_000 ether);
|
||||
vm.assume(bobAmount > gatekeeper.EXISTENTIAL_DEPOSIT() && bobAmount < 1_000 ether);
|
||||
vm.assume(bobCommission > 0 && bobCommission <= type(uint32).max);
|
||||
|
||||
address storageHistory = gatekeeper.storageHistory();
|
||||
@ -259,19 +266,9 @@ contract GatekeeperTest is Test {
|
||||
|
||||
vm.deal(ALICE, nativeNeeded + 1 ether);
|
||||
assertEq(BOB.balance, 0 ether);
|
||||
uint256 aliceStartingNative = ALICE.balance;
|
||||
|
||||
uint256 previousAliceAmount = staking.recalledAmount(ALICE);
|
||||
uint256 previousBobAmount = staking.recalledAmount(BOB);
|
||||
|
||||
vm.prank(ALICE);
|
||||
staking.runRecall{value: nativeNeeded}(exodusSession, bobAmount, bobPacked, ALICE);
|
||||
|
||||
assertApproxEqAbs(previousAliceAmount + commissionAmount, staking.recalledAmount(ALICE), 1000);
|
||||
assertApproxEqAbs(previousBobAmount + (bobAmount - commissionAmount), staking.recalledAmount(BOB), 1000);
|
||||
|
||||
assertEq(BOB.balance, nativeNeeded);
|
||||
assertEq(ALICE.balance, aliceStartingNative - nativeNeeded);
|
||||
staking.runRecall(exodusSession, bobAmount, bobPacked, ALICE);
|
||||
|
||||
assert(IStorageHistory(storageHistory).isTransactionExecuted(exodusSession));
|
||||
}
|
||||
@ -283,7 +280,7 @@ contract GatekeeperTest is Test {
|
||||
}
|
||||
|
||||
function test_couldNotBridgeBelowExistential(uint256 amount) public {
|
||||
vm.assume(amount < EXISTENTIAL);
|
||||
vm.assume(amount < gatekeeper.EXISTENTIAL_DEPOSIT());
|
||||
bytes32 receiver = bytes32(abi.encodePacked(ALICE));
|
||||
|
||||
vm.expectRevert();
|
||||
@ -363,6 +360,7 @@ contract GatekeeperTest is Test {
|
||||
// execute bridge out, happened on DKG #0
|
||||
// amount: 69; commission: 0%; ALICE
|
||||
// exodus session #1
|
||||
vm.prank(ALICE, ALICE);
|
||||
gatekeeper.verify(
|
||||
hex"bf06188a0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000004500000000000000000000000000000000000000010000000000007a6900000000",
|
||||
hex"04bccb4cd4633e2c61ca5c65006a6a09baa893be219b21509866123a8d6299f92aefe9c380bd9728dbe0b93a8042f431636d08ed191b5c04062bf14e1347059cb9",
|
||||
@ -393,10 +391,8 @@ contract GatekeeperTest is Test {
|
||||
0x772731b359ee7ecac7830438333e4ffb61759b36610f69cef20f66f7a79a405f
|
||||
);
|
||||
|
||||
assertEq(aliceAmountBefore + 210, staking.recalledAmount(ALICE));
|
||||
assertEq(bobAmountBefore + 210, staking.recalledAmount(BOB));
|
||||
assertEq(aliceBalanceBefore - buyback, ALICE.balance);
|
||||
assertEq(bobBalanceBefore + buyback, BOB.balance);
|
||||
assertApproxEqAbs(aliceAmountBefore + 210, staking.recalledAmount(ALICE), 1);
|
||||
assertApproxEqAbs(bobAmountBefore + 210, staking.recalledAmount(BOB), 1);
|
||||
|
||||
// execute setDistributor, exodusSession: 2
|
||||
vm.prank(ALICE);
|
||||
|
||||
@ -7,21 +7,24 @@ import {IStorageHistory} from "../../src/interfaces/IStorageHistory.sol";
|
||||
import {IGatekeeper} from "../../src/interfaces/IGatekeeper.sol";
|
||||
import {RequestPacking} from "../../src/libraries/Packing.sol";
|
||||
import {StorageHistory} from "../../src/types/StorageHistory.sol";
|
||||
import {WETH9} from "../../src/mocks/WETH9.sol";
|
||||
|
||||
contract MockStaking is Test {
|
||||
Gatekeeper public gatekeeper;
|
||||
WETH9 public mockReserve;
|
||||
|
||||
mapping(address => uint256) private _recalledAmounts;
|
||||
|
||||
constructor(uint256 existential) {
|
||||
constructor() {
|
||||
mockReserve = new WETH9();
|
||||
StorageHistory history = new StorageHistory();
|
||||
gatekeeper = new Gatekeeper(existential, address(history));
|
||||
gatekeeper = new Gatekeeper(address(history));
|
||||
gatekeeper.initialize(address(0));
|
||||
history.setOwner(address(gatekeeper));
|
||||
}
|
||||
|
||||
function redoGatekeeper(uint256 existential) external {
|
||||
Gatekeeper newGatekeeper = new Gatekeeper(existential, address(0));
|
||||
function redoGatekeeper() external {
|
||||
Gatekeeper newGatekeeper = new Gatekeeper(address(0));
|
||||
newGatekeeper.initialize(address(gatekeeper));
|
||||
|
||||
address storageHistory = IGatekeeper(gatekeeper).storageHistory();
|
||||
@ -43,6 +46,11 @@ contract MockStaking is Test {
|
||||
_recalledAmounts[receiver] += amount;
|
||||
}
|
||||
|
||||
function recall(uint256 amount, uint256, uint256) external returns (address, uint256, uint256){
|
||||
_recalledAmounts[tx.origin] += amount;
|
||||
return (address(mockReserve), amount, 0);
|
||||
}
|
||||
|
||||
function recalledAmount(address who) external view returns (uint256) {
|
||||
return _recalledAmounts[who];
|
||||
}
|
||||
@ -54,14 +62,13 @@ contract GatekeeperStorageHistoryTest is Test {
|
||||
address constant ALICE = 0x0000000000000000000000000000000000000001;
|
||||
address constant BOB = 0x0000000000000000000000000000000000000002;
|
||||
uint256 constant INIT_AMOUNT = 1337 * 1e18;
|
||||
uint256 constant EXISTENTIAL = 0;
|
||||
|
||||
Gatekeeper gatekeeper;
|
||||
MockStaking staking;
|
||||
|
||||
function setUp() public {
|
||||
vm.prank(ALICE);
|
||||
staking = new MockStaking(EXISTENTIAL);
|
||||
staking = new MockStaking();
|
||||
|
||||
staking.runGhost(bytes32(abi.encodePacked(ALICE)), INIT_AMOUNT);
|
||||
gatekeeper = staking.gatekeeper();
|
||||
@ -78,7 +85,7 @@ contract GatekeeperStorageHistoryTest is Test {
|
||||
}
|
||||
|
||||
function test_historicalAmountsOnlyIncrease(uint256 ghostAmount, uint64 exodusSession) public {
|
||||
vm.assume(ghostAmount > 0 && ghostAmount < INIT_AMOUNT);
|
||||
vm.assume(ghostAmount > gatekeeper.EXISTENTIAL_DEPOSIT() && ghostAmount < INIT_AMOUNT);
|
||||
|
||||
uint256 amountToGhost = ghostAmount;
|
||||
uint256 amountToMaterialize = ghostAmount / 2;
|
||||
@ -99,6 +106,7 @@ contract GatekeeperStorageHistoryTest is Test {
|
||||
uint256 packed = RequestPacking.pack(0, uint64(block.chainid), BOB);
|
||||
uint256 previousAmount = staking.recalledAmount(BOB);
|
||||
|
||||
vm.prank(BOB, BOB);
|
||||
staking.runRecall(exodusSession, amountToMaterialize, packed);
|
||||
assertEq(previousAmount + amountToMaterialize, staking.recalledAmount(BOB));
|
||||
|
||||
@ -121,7 +129,7 @@ contract GatekeeperStorageHistoryTest is Test {
|
||||
address prevStorageHistory = gatekeeper.storageHistory();
|
||||
IStorageHistory.DeploymentSnapshot memory prevSnapshot = IStorageHistory(prevStorageHistory).deploymentSnapshot();
|
||||
|
||||
staking.redoGatekeeper(EXISTENTIAL + 1);
|
||||
staking.redoGatekeeper();
|
||||
assert(gatekeeper != staking.gatekeeper());
|
||||
|
||||
address currStorageHistory = gatekeeper.storageHistory();
|
||||
@ -133,8 +141,5 @@ contract GatekeeperStorageHistoryTest is Test {
|
||||
|
||||
assert(IStorageHistory(prevStorageHistory).isTransactionExecuted(exodusSession));
|
||||
assert(IStorageHistory(currStorageHistory).isTransactionExecuted(exodusSession));
|
||||
|
||||
assertEq(gatekeeper.existentialDeposit(), EXISTENTIAL);
|
||||
assertEq(staking.gatekeeper().existentialDeposit(), EXISTENTIAL + 1);
|
||||
}
|
||||
}
|
||||
|
||||
203
test/gatekeeper/GatekeeperRecall.t.sol
Normal file
203
test/gatekeeper/GatekeeperRecall.t.sol
Normal file
@ -0,0 +1,203 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -13,9 +13,9 @@ contract MockStaking {
|
||||
GatekeeperWeaver public gatekeeper;
|
||||
address public governor;
|
||||
|
||||
constructor(uint256 existential) {
|
||||
constructor() {
|
||||
StorageHistory history = new StorageHistory();
|
||||
gatekeeper = new GatekeeperWeaver(existential, address(history));
|
||||
gatekeeper = new GatekeeperWeaver(address(history));
|
||||
gatekeeper.initialize(address(0));
|
||||
history.setOwner(address(gatekeeper));
|
||||
governor = msg.sender;
|
||||
@ -26,10 +26,10 @@ contract MockStaking {
|
||||
return gatekeeper.ghost(receiver, amount);
|
||||
}
|
||||
|
||||
function createNewGatekeeper(uint256 existential) external {
|
||||
function createNewGatekeeper() external {
|
||||
require(msg.sender == governor);
|
||||
|
||||
GatekeeperWeaver newGatekeeper = new GatekeeperWeaver(existential, address(0));
|
||||
GatekeeperWeaver newGatekeeper = new GatekeeperWeaver(address(0));
|
||||
newGatekeeper.initialize(address(gatekeeper));
|
||||
|
||||
address storageHistory = IGatekeeper(gatekeeper).storageHistory();
|
||||
@ -43,7 +43,7 @@ contract GatekeeperWeaver is Gatekeeper {
|
||||
using Checkpoints for Checkpoints.Trace256;
|
||||
using Checkpoints for Checkpoints.Trace160;
|
||||
|
||||
constructor(uint256 existential, address storageHistory) Gatekeeper(existential, storageHistory) {}
|
||||
constructor(address storageHistory) Gatekeeper(storageHistory) {}
|
||||
|
||||
function filledEntries(uint256 session) public view returns (uint256) {
|
||||
return _filledEntries[session];
|
||||
@ -115,7 +115,6 @@ contract GatekeeperWeaver is Gatekeeper {
|
||||
contract GatekeeperWeaverTest is Test {
|
||||
address constant ALICE = 0x0000000000000000000000000000000000000001;
|
||||
address constant BOB = 0x0000000000000000000000000000000000000002;
|
||||
uint256 constant EXISTENTIAL = 1337;
|
||||
uint256 constant AMOUNT = 1 * 1e7;
|
||||
|
||||
MockStaking staking;
|
||||
@ -123,7 +122,7 @@ contract GatekeeperWeaverTest is Test {
|
||||
|
||||
function setUp() public {
|
||||
vm.prank(ALICE);
|
||||
staking = new MockStaking(EXISTENTIAL);
|
||||
staking = new MockStaking();
|
||||
gatekeeper = staking.gatekeeper();
|
||||
}
|
||||
|
||||
@ -164,7 +163,7 @@ contract GatekeeperWeaverTest is Test {
|
||||
vm.roll(block.number + 420);
|
||||
|
||||
vm.prank(ALICE);
|
||||
staking.createNewGatekeeper(EXISTENTIAL);
|
||||
staking.createNewGatekeeper();
|
||||
gatekeeper = staking.gatekeeper();
|
||||
|
||||
uint256 finalSession = gatekeeper.currentWeavingSession();
|
||||
@ -256,13 +255,15 @@ contract GatekeeperWeaverTest is Test {
|
||||
}
|
||||
}
|
||||
|
||||
function _prepareArrays(uint256 count) private pure returns (bytes32[] memory, uint256[] memory) {
|
||||
function _prepareArrays(uint256 count) private view returns (bytes32[] memory, uint256[] memory) {
|
||||
bytes32[] memory whos = new bytes32[](count);
|
||||
uint256[] memory amounts = new uint256[](count);
|
||||
|
||||
uint256 existential = gatekeeper.EXISTENTIAL_DEPOSIT();
|
||||
|
||||
for (uint256 i = 0; i < count; i++) {
|
||||
whos[i] = keccak256(abi.encodePacked("user", i));
|
||||
amounts[i] = EXISTENTIAL + 1 + i;
|
||||
amounts[i] = existential + 1 + i;
|
||||
}
|
||||
|
||||
return (whos, amounts);
|
||||
|
||||
@ -106,10 +106,9 @@ contract StakingTest is Test {
|
||||
EPOCH_LENGTH,
|
||||
EPOCH_NUMBER,
|
||||
EPOCH_END_TIME,
|
||||
address(authority),
|
||||
0
|
||||
address(authority)
|
||||
);
|
||||
treasury = new GhostTreasury(address(ftso), 69, address(authority));
|
||||
treasury = new GhostTreasury(address(ftso), address(authority));
|
||||
stnk.initialize(address(staking), address(treasury), address(ghst));
|
||||
ghst.initialize(address(staking));
|
||||
calculator = new GhostBondingCalculator(address(ftso), 1, 1);
|
||||
@ -596,7 +595,7 @@ contract StakingTest is Test {
|
||||
vm.prank(address(previousGatekeeper));
|
||||
IStorageHistory(storageHistory).trySetTransactionExecuted(34);
|
||||
|
||||
Gatekeeper newGatekeeper = new Gatekeeper(420, address(0));
|
||||
Gatekeeper newGatekeeper = new Gatekeeper(address(0));
|
||||
|
||||
vm.prank(GOVERNOR);
|
||||
staking.updateGatekeeperAddress(address(newGatekeeper));
|
||||
|
||||
@ -61,10 +61,9 @@ contract StakingDistributorTest is Test {
|
||||
EPOCH_LENGTH,
|
||||
EPOCH_NUMBER,
|
||||
EPOCH_END_TIME,
|
||||
address(authority),
|
||||
0
|
||||
address(authority)
|
||||
);
|
||||
treasury = new GhostTreasury(address(ftso), 69, address(authority));
|
||||
treasury = new GhostTreasury(address(ftso), address(authority));
|
||||
calculator = new GhostBondingCalculator(address(ftso), 1, 1);
|
||||
distributor = new GhostDistributor(
|
||||
address(treasury),
|
||||
|
||||
@ -53,8 +53,7 @@ contract GhostTest is
|
||||
69,
|
||||
1337,
|
||||
1337,
|
||||
address(authority),
|
||||
0
|
||||
address(authority)
|
||||
);
|
||||
stnk.initialize(address(staking), TREASURY, address(ghst));
|
||||
ghst.initialize(address(staking));
|
||||
|
||||
@ -59,8 +59,7 @@ contract StinkyTest is Test, ERC20PermitTest, ERC20AllowanceTest, ERC20TransferT
|
||||
69,
|
||||
1337,
|
||||
1337,
|
||||
address(authority),
|
||||
0
|
||||
address(authority)
|
||||
);
|
||||
ghst.initialize(address(staking));
|
||||
vm.stopPrank();
|
||||
|
||||
@ -37,7 +37,7 @@ contract GhostTreasuryTest is Test {
|
||||
reserve = new ERC20Mock("Reserve Token", "RET");
|
||||
liquidity = new ERC20Mock("Liquidity Token", "LDT");
|
||||
ftso = new Fatso(address(authority), "Fatso", "FTSO");
|
||||
treasury = new GhostTreasury(address(ftso), 69, address(authority));
|
||||
treasury = new GhostTreasury(address(ftso), address(authority));
|
||||
calculator = new GhostBondingCalculator(address(ftso), 1, 1);
|
||||
vm.stopPrank();
|
||||
|
||||
@ -109,8 +109,8 @@ contract GhostTreasuryTest is Test {
|
||||
vm.prank(GOVERNOR);
|
||||
treasury.auditReserves();
|
||||
|
||||
assertEq(treasury.permissions(ITreasury.STATUS.RESERVETOKEN, address(reserve)), true);
|
||||
assertEq(treasury.registry(ITreasury.STATUS.RESERVETOKEN, 0), address(reserve));
|
||||
assertEq(treasury.permissioned(ITreasury.STATUS.RESERVETOKEN, address(reserve)), true);
|
||||
assertEq(treasury.registered(ITreasury.STATUS.RESERVETOKEN, address(reserve)), true);
|
||||
assertEq(
|
||||
treasury.tokenValue(address(reserve), reserve.balanceOf(address(treasury))),
|
||||
treasury.totalReserves());
|
||||
@ -139,15 +139,13 @@ contract GhostTreasuryTest is Test {
|
||||
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, msg.sender, address(0));
|
||||
vm.stopPrank();
|
||||
|
||||
assertEq(treasury.registry(ITreasury.STATUS.RESERVETOKEN, 0), address(reserve));
|
||||
assertEq(treasury.registry(ITreasury.STATUS.LIQUIDITYTOKEN, 0), address(liquidity));
|
||||
assertEq(treasury.registered(ITreasury.STATUS.RESERVETOKEN, address(reserve)), true);
|
||||
assertEq(treasury.registered(ITreasury.STATUS.LIQUIDITYTOKEN, address(liquidity)), true);
|
||||
assertEq(treasury.bondCalculator(address(liquidity)), address(calculator));
|
||||
vm.expectRevert();
|
||||
treasury.registry(ITreasury.STATUS.RESERVEDEPOSITOR, 0);
|
||||
|
||||
assertEq(treasury.permissions(ITreasury.STATUS.RESERVETOKEN, address(reserve)), true);
|
||||
assertEq(treasury.permissions(ITreasury.STATUS.LIQUIDITYTOKEN, address(liquidity)), true);
|
||||
assertEq(treasury.permissions(ITreasury.STATUS.RESERVEDEPOSITOR, msg.sender), true);
|
||||
assertEq(treasury.permissioned(ITreasury.STATUS.RESERVETOKEN, address(reserve)), true);
|
||||
assertEq(treasury.permissioned(ITreasury.STATUS.LIQUIDITYTOKEN, address(liquidity)), true);
|
||||
assertEq(treasury.permissioned(ITreasury.STATUS.RESERVEDEPOSITOR, msg.sender), true);
|
||||
}
|
||||
|
||||
function test_randomAddressCouldNotDisableStatusByAddress(address who) public {
|
||||
@ -185,9 +183,9 @@ contract GhostTreasuryTest is Test {
|
||||
treasury.disable(ITreasury.STATUS.RESERVEDEPOSITOR, msg.sender);
|
||||
vm.stopPrank();
|
||||
|
||||
assertEq(treasury.permissions(ITreasury.STATUS.RESERVETOKEN, address(reserve)), false);
|
||||
assertEq(treasury.permissions(ITreasury.STATUS.LIQUIDITYTOKEN, address(liquidity)), false);
|
||||
assertEq(treasury.permissions(ITreasury.STATUS.RESERVEDEPOSITOR, msg.sender), false);
|
||||
assertEq(treasury.permissioned(ITreasury.STATUS.RESERVETOKEN, address(reserve)), false);
|
||||
assertEq(treasury.permissioned(ITreasury.STATUS.LIQUIDITYTOKEN, address(liquidity)), false);
|
||||
assertEq(treasury.permissioned(ITreasury.STATUS.RESERVEDEPOSITOR, msg.sender), false);
|
||||
}
|
||||
|
||||
function test_mainnet_disableReserveAndLiquidity() public {
|
||||
|
||||
@ -41,7 +41,7 @@ contract GhostTreasuryCoefficienBiggerTest is Test {
|
||||
reserve = new ERC20Mock("Reserve Token", "RET");
|
||||
liquidity = new ERC20Mock("Liquidity Token", "LDT");
|
||||
ftso = new Fatso(address(authority), "Fatso", "FTSO");
|
||||
treasury = new GhostTreasury(address(ftso), 69, address(authority));
|
||||
treasury = new GhostTreasury(address(ftso), address(authority));
|
||||
calculator = new GhostBondingCalculator(address(ftso), 20, 1);
|
||||
vm.stopPrank();
|
||||
|
||||
|
||||
@ -41,7 +41,7 @@ contract GhostTreasuryCoefficientLesserTest is Test {
|
||||
reserve = new ERC20Mock("Reserve Token", "RET");
|
||||
liquidity = new ERC20Mock("Liquidity Token", "LDT");
|
||||
ftso = new Fatso(address(authority), "Fatso", "FTSO");
|
||||
treasury = new GhostTreasury(address(ftso), 69, address(authority));
|
||||
treasury = new GhostTreasury(address(ftso), address(authority));
|
||||
calculator = new GhostBondingCalculator(address(ftso), 1, 20);
|
||||
vm.stopPrank();
|
||||
|
||||
|
||||
@ -37,7 +37,7 @@ contract GhostTreasuryRedemptionTest is Test {
|
||||
OWNER,
|
||||
OWNER
|
||||
);
|
||||
treasury = new GhostTreasury(DAI, 69, address(authority));
|
||||
treasury = new GhostTreasury(DAI, address(authority));
|
||||
calculator = new GhostBondingCalculator(DAI, 4000, 1);
|
||||
vm.stopPrank();
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user