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