make governance great again

Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
This commit is contained in:
Uncle Fatso 2026-09-04 11:22:22 +03:00
parent 40b6d02e8d
commit 0860792f12
Signed by: f4ts0
GPG Key ID: 565F4F2860226EBB
32 changed files with 608 additions and 1141 deletions

View File

@ -19,7 +19,8 @@ gas_reports = [
"GhostBondingCalculator", "GhostBondingCalculator",
"GhostTreasury", "GhostTreasury",
"GhostGovernorExposed", "GhostGovernorExposed",
"GatekeeperVerification", "GatekeeperWeaver",
"Gatekeeper",
] ]
remappings = [ remappings = [
"@openzeppelin-contracts/=dependencies/@openzeppelin-contracts-5.0.2/", "@openzeppelin-contracts/=dependencies/@openzeppelin-contracts-5.0.2/",

View File

@ -5,15 +5,20 @@ import {IERC20} from "@openzeppelin-contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol"; import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20Metadata} from "@openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol"; import {IERC20Metadata} from "@openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {NoteKeeper} from "./types/NoteKeeper.sol";
import {IBondDepository} from "./interfaces/IBondDepository.sol"; import {IBondDepository} from "./interfaces/IBondDepository.sol";
import {IGhostAuthority} from "./interfaces/IGhostAuthority.sol";
import {ITreasury} from "./interfaces/ITreasury.sol";
import {IWETH9} from "./interfaces/IWETH9.sol"; import {IWETH9} from "./interfaces/IWETH9.sol";
import {FullMath} from "./libraries/FullMath.sol"; import {FullMath} from "./libraries/FullMath.sol";
import {NoteKeeper} from "./types/NoteKeeper.sol";
contract GhostBondDepository is IBondDepository, NoteKeeper { contract GhostBondDepository is IBondDepository, NoteKeeper {
using SafeERC20 for IERC20; using SafeERC20 for IERC20;
address public immutable WETH; address public immutable WETH;
address public immutable TREASURY;
address public immutable AUTHORITY;
Market[] public markets; Market[] public markets;
Term[] public terms; Term[] public terms;
@ -29,8 +34,10 @@ contract GhostBondDepository is IBondDepository, NoteKeeper {
address staking, address staking,
address treasury, address treasury,
address wrapped address wrapped
) NoteKeeper(authority, ftso, ghst, staking, treasury) { ) NoteKeeper(ghst, staking, treasury) {
_ftso.approve(staking, type(uint256).max); IERC20(ftso).approve(staking, type(uint256).max);
AUTHORITY = authority;
TREASURY = treasury;
WETH = wrapped; WETH = wrapped;
} }
@ -38,8 +45,7 @@ contract GhostBondDepository is IBondDepository, NoteKeeper {
uint256 id, uint256 id,
uint256 amount, uint256 amount,
uint256 maxPrice, uint256 maxPrice,
address user, address user
address referral
) )
external external
override override
@ -71,12 +77,12 @@ contract GhostBondDepository is IBondDepository, NoteKeeper {
emit Bond(id, amount, price); emit Bond(id, amount, price);
index = addNote(user, payout, uint48(expiry), uint48(id), referral); // forge-lint: disable-line(unsafe-typecast) index = addNote(user, payout, uint48(expiry), uint48(id)); // forge-lint: disable-line(unsafe-typecast)
IWETH9(WETH).deposit{value: msg.value}(); IWETH9(WETH).deposit{value: msg.value}();
IERC20(WETH).safeTransfer(address(_treasury), msg.value); IERC20(WETH).safeTransfer(TREASURY, msg.value);
if (msg.value < amount || market.quoteToken != WETH) { if (msg.value < amount || market.quoteToken != WETH) {
IERC20(market.quoteToken).safeTransferFrom(msg.sender, address(_treasury), amount); IERC20(market.quoteToken).safeTransferFrom(msg.sender, TREASURY, amount);
} }
if (term.maxDebt < market.totalDebt) { if (term.maxDebt < market.totalDebt) {
@ -124,7 +130,7 @@ contract GhostBondDepository is IBondDepository, NoteKeeper {
markets[id].maxPayout = uint64(FullMath.mulDiv(capacity, meta.depositInterval, timeRemaining)); markets[id].maxPayout = uint64(FullMath.mulDiv(capacity, meta.depositInterval, timeRemaining));
uint256 targetDebt = FullMath.mulDiv(capacity, meta.length, timeRemaining); uint256 targetDebt = FullMath.mulDiv(capacity, meta.length, timeRemaining);
// forge-lint: disable-next-line(unsafe-typecast) // forge-lint: disable-next-line(unsafe-typecast)
uint64 newControlVariable = uint64(FullMath.mulDiv(price, _treasury.baseSupply(), targetDebt)); uint64 newControlVariable = uint64(FullMath.mulDiv(price, ITreasury(TREASURY).baseSupply(), targetDebt));
emit Tuned(id, terms[id].controlVariable, newControlVariable); emit Tuned(id, terms[id].controlVariable, newControlVariable);
@ -151,8 +157,8 @@ contract GhostBondDepository is IBondDepository, NoteKeeper {
bool[2] calldata _booleans bool[2] calldata _booleans
) external override returns (uint256 id) { ) external override returns (uint256 id) {
if ( if (
msg.sender != authority.governor() && msg.sender != IGhostAuthority(AUTHORITY).governor() &&
msg.sender != authority.policy() msg.sender != IGhostAuthority(AUTHORITY).policy()
) revert NotGuardianOrPolicy(); ) revert NotGuardianOrPolicy();
uint256 secondsToConclusion = _terms[1] - block.timestamp; uint256 secondsToConclusion = _terms[1] - block.timestamp;
@ -164,7 +170,7 @@ contract GhostBondDepository is IBondDepository, NoteKeeper {
// forge-lint: disable-next-line(unsafe-typecast) // forge-lint: disable-next-line(unsafe-typecast)
uint64 maxPayout = uint64(FullMath.mulDiv(targetDebt, _intervals[0], secondsToConclusion)); uint64 maxPayout = uint64(FullMath.mulDiv(targetDebt, _intervals[0], secondsToConclusion));
uint256 maxDebt = targetDebt + FullMath.mulDiv(targetDebt, _market[2], 1e5); uint256 maxDebt = targetDebt + FullMath.mulDiv(targetDebt, _market[2], 1e5);
uint256 controlVariable = FullMath.mulDiv(_market[1], _treasury.baseSupply(), targetDebt); uint256 controlVariable = FullMath.mulDiv(_market[1], ITreasury(TREASURY).baseSupply(), targetDebt);
id = markets.length; id = markets.length;
@ -204,13 +210,13 @@ contract GhostBondDepository is IBondDepository, NoteKeeper {
); );
marketsForQuote[_quoteToken].push(id); marketsForQuote[_quoteToken].push(id);
emit MarketCreated(id, address(_ftso), _quoteToken, _market[1]); emit MarketCreated(id, _quoteToken, _market[1]);
} }
function close(uint256 id) external override { function close(uint256 id) external override {
if ( if (
msg.sender != authority.governor() && msg.sender != IGhostAuthority(AUTHORITY).governor() &&
msg.sender != authority.policy() msg.sender != IGhostAuthority(AUTHORITY).policy()
) revert NotGuardianOrPolicy(); ) revert NotGuardianOrPolicy();
terms[id].conclusion = uint48(block.timestamp); terms[id].conclusion = uint48(block.timestamp);
@ -231,7 +237,7 @@ contract GhostBondDepository is IBondDepository, NoteKeeper {
} }
function debtRatio(uint256 id) public view override returns (uint256) { function debtRatio(uint256 id) public view override returns (uint256) {
return FullMath.mulDiv(currentDebt(id), 10**metadatas[id].quoteDecimals, _treasury.baseSupply()); return FullMath.mulDiv(currentDebt(id), 10**metadatas[id].quoteDecimals, ITreasury(TREASURY).baseSupply());
} }
function currentDebt(uint256 id) public view override returns (uint256) { function currentDebt(uint256 id) public view override returns (uint256) {
@ -309,7 +315,7 @@ contract GhostBondDepository is IBondDepository, NoteKeeper {
} }
function _debtRatio(uint256 id) internal view returns (uint256) { function _debtRatio(uint256 id) internal view returns (uint256) {
return FullMath.mulDiv(markets[id].totalDebt, 10**metadatas[id].quoteDecimals, _treasury.baseSupply()); return FullMath.mulDiv(markets[id].totalDebt, 10**metadatas[id].quoteDecimals, ITreasury(TREASURY).baseSupply());
} }
function _controlDecay(uint256 id) function _controlDecay(uint256 id)

View File

@ -10,55 +10,69 @@ import {Weaver} from "./types/Weaver.sol";
import {FullMath} from "./libraries/FullMath.sol"; import {FullMath} from "./libraries/FullMath.sol";
import {Checkpoints} from "./libraries/Checkpoints.sol"; import {Checkpoints} from "./libraries/Checkpoints.sol";
import {RotationPacking, RequestPacking} from "./libraries/Packing.sol"; import {RotationPacking, RequestPacking, GovernancePacking} from "./libraries/Packing.sol";
import {ReentrancyGuard} from "@openzeppelin-contracts/utils/ReentrancyGuard.sol"; import {ReentrancyGuard} from "@openzeppelin-contracts/utils/ReentrancyGuard.sol";
contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard { contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard {
using Checkpoints for Checkpoints.Trace256; using Checkpoints for Checkpoints.Trace256;
using RotationPacking for RotationPacking.RotationState; using RotationPacking for RotationPacking.RotationState;
using RequestPacking for RequestPacking.RequestPayload; using RequestPacking for RequestPacking.RequestPayload;
using GovernancePacking for GovernancePacking.GovernancePayload;
uint256 private constant COMMISSION_DIVISOR = type(uint32).max; uint256 private constant BOUNTY_DIVISOR = type(uint32).max;
uint256 private constant SECP256K1_N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141; uint256 private constant SECP256K1_N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141;
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;
address public override staking; address public override staking;
address public override deployer;
address public override storageHistory; address public override storageHistory;
uint256 public override existentialDeposit; uint256 public override existentialDeposit;
address public deployer;
address private _previousAddress; address private _previousAddress;
bool private _initialized;
Checkpoints.Trace256 private _aggregatedPublicKeys; Checkpoints.Trace256 private _aggregatedPublicKeys;
mapping(bytes32 => uint256) private _packedRotationStates; mapping(bytes32 => uint256) private _packedRotationStates;
constructor( constructor(
uint256 _existentialDeposit, uint256 _existentialDeposit,
address _previousGatekeeperAddress address _storageHistory
) Weaver(_previousGatekeeperAddress) { ) {
existentialDeposit = _existentialDeposit; existentialDeposit = _existentialDeposit;
storageHistory = _storageHistory;
if (_previousGatekeeperAddress != address(0)) {
require(_previousGatekeeperAddress != address(0));
require(_previousGatekeeperAddress != address(this));
_previousAddress = _previousGatekeeperAddress;
storageHistory = IGatekeeper(_previousGatekeeperAddress).storageHistory();
staking = IGatekeeper(_previousGatekeeperAddress).staking();
} else {
StorageHistory newStorageHistory = new StorageHistory(msg.sender);
storageHistory = address(newStorageHistory);
staking = msg.sender; staking = msg.sender;
deployer = tx.origin; deployer = tx.origin;
} }
function initialize(address _previousGatekeeperAddress) external override {
if (_previousGatekeeperAddress != address(0)) {
require(_initialized == false);
require(_previousGatekeeperAddress != address(this));
address previousStorage = IGatekeeper(_previousGatekeeperAddress).storageHistory();
address previousStaking = IGatekeeper(_previousGatekeeperAddress).staking();
require(previousStorage != address(0));
require(previousStaking != address(0));
storageHistory = previousStorage;
staking = previousStaking;
deployer = IGatekeeper(_previousGatekeeperAddress).deployer();
Weaver._initialize(_previousGatekeeperAddress);
_previousAddress = _previousGatekeeperAddress;
}
require(msg.sender == staking);
_initialized = true;
} }
function updatePublicKeyMetadata(uint256 exodusSession, bytes32 publicKey, uint8 parity) external { function updatePublicKeyMetadata(uint256 exodusSession, bytes32 publicKey, uint8 parity) external {
require(msg.sender == deployer); require(msg.sender == deployer);
// TODO: uncomment line below, needed only for testing purposes // TODO: uncomment line below, needed only for testing purposes
// and should push to hardcoded exodusSession 0 always // and should push to hardcoded exodusSession 0 always
// deployer = address(0); // _deployer = address(0);
// forge-lint: disable-next-line(unsafe-typecast) // forge-lint: disable-next-line(unsafe-typecast)
uint256 packedRotationState = RotationPacking.pack(parity, uint64(exodusSession)); uint256 packedRotationState = RotationPacking.pack(parity, uint64(exodusSession));
@ -118,7 +132,7 @@ contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard {
return _insertTreeNode(receiver, amount); return _insertTreeNode(receiver, amount);
} }
function materialize( function recall(
uint256 exodusSession, uint256 exodusSession,
uint256 amount, uint256 amount,
uint256 packed uint256 packed
@ -131,24 +145,24 @@ contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard {
StorageHistory(storageHistory).trySetTransactionExecuted(exodusSession); StorageHistory(storageHistory).trySetTransactionExecuted(exodusSession);
IStorageHistory(storageHistory).tryIncreaseBridgeOut(amount); IStorageHistory(storageHistory).tryIncreaseBridgeOut(amount);
uint256 commissionAmount = FullMath.mulDiv(amount, uint256(payload.commission), COMMISSION_DIVISOR); uint256 bountyAmount = FullMath.mulDiv(amount, uint256(payload.bounty), BOUNTY_DIVISOR);
uint256 receiverAmount = amount - commissionAmount; uint256 receiverAmount = amount - bountyAmount;
IStaking(staking).materialize(payload.receiver, receiverAmount); IStaking(staking).recall(payload.receiver, receiverAmount);
if (commissionAmount > 0) { if (bountyAmount > 0) {
uint256 totalReserves = IStaking(staking).totalReserves(); uint256 totalReserves = IStaking(staking).totalReserves();
uint256 baseSupply = IStaking(staking).baseSupply(); uint256 baseSupply = IStaking(staking).baseSupply();
uint256 minimumNativeRequired = FullMath.mulDiv(commissionAmount, totalReserves, baseSupply); uint256 minimumNativeRequired = FullMath.mulDiv(bountyAmount, totalReserves, baseSupply);
if (minimumNativeRequired > msg.value) revert InsufficientValue(); if (minimumNativeRequired > msg.value) revert InsufficientValue();
IStaking(staking).materialize(tx.origin, commissionAmount); IStaking(staking).recall(tx.origin, bountyAmount);
(bool sentSuccess,) = payload.receiver.call{ value: msg.value }(""); (bool sentSuccess,) = payload.receiver.call{ value: msg.value }("");
if (!sentSuccess) revert SendFailed(); if (!sentSuccess) revert SendFailed();
} }
emit Materialized(payload.receiver, amount); emit Recalled(payload.receiver, amount);
} }
function rotate( function rotate(
@ -168,6 +182,24 @@ contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard {
emit Rotated(newPublicKey, newParity); emit Rotated(newPublicKey, newParity);
} }
function govern(
uint256 exodusSession,
uint256 packed,
bytes calldata
) external override returns (bytes memory) {
if (msg.sender != address(this)) revert NotGatekeeper();
GovernancePacking.GovernancePayload memory payload = GovernancePacking.unpack(packed);
if (payload.chainId != block.chainid) revert WrongChainId();
StorageHistory(storageHistory).trySetTransactionExecuted(exodusSession);
(bool success, bytes memory data) = payload.target.call(msg.data[132:]);
if (!success) revert ExecutionReverted();
return data;
}
function _verifySchnorrSignature(bytes calldata call, bytes calldata nonce, bytes32 s) internal view { function _verifySchnorrSignature(bytes calldata call, bytes calldata nonce, bytes32 s) internal view {
(bytes32 px, uint8 p) = _extractPublicKey(call); (bytes32 px, uint8 p) = _extractPublicKey(call);

View File

@ -6,6 +6,7 @@ import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol
import {GhostWarmup} from "./Warmup.sol"; import {GhostWarmup} from "./Warmup.sol";
import {Gatekeeper} from "./Gatekeeper.sol"; import {Gatekeeper} from "./Gatekeeper.sol";
import {StorageHistory} from "./types/StorageHistory.sol";
import {GhostAccessControlled} from "./types/GhostAccessControlled.sol"; import {GhostAccessControlled} from "./types/GhostAccessControlled.sol";
import {ISTNK} from "./interfaces/ISTNK.sol"; import {ISTNK} from "./interfaces/ISTNK.sol";
@ -60,10 +61,14 @@ contract GhostStaking is IStaking, GhostAccessControlled {
}); });
GhostWarmup newWarmup = new GhostWarmup(_ghst); GhostWarmup newWarmup = new GhostWarmup(_ghst);
warmup = address(newWarmup); StorageHistory newHistory = new StorageHistory();
Gatekeeper newGatekeeper = new Gatekeeper(_existentialDeposit, address(newHistory));
IStorageHistory(newHistory).setOwner(address(newGatekeeper));
IGatekeeper(newGatekeeper).initialize(address(0));
Gatekeeper newGatekeeper = new Gatekeeper(_existentialDeposit, address(0));
gatekeeper = address(newGatekeeper); gatekeeper = address(newGatekeeper);
warmup = address(newWarmup);
_lastRebaseBlock = 1; _lastRebaseBlock = 1;
} }
@ -162,7 +167,7 @@ contract GhostStaking is IStaking, GhostAccessControlled {
IGatekeeper(gatekeeper).ghost(receiver, amount); IGatekeeper(gatekeeper).ghost(receiver, amount);
} }
function materialize(address receiver, uint256 amount) external override { function recall(address receiver, uint256 amount) external override {
if (gatekeeper != msg.sender) revert NotGatekeeper(); if (gatekeeper != msg.sender) revert NotGatekeeper();
IGHST(ghst).mint(receiver, amount); IGHST(ghst).mint(receiver, amount);
} }
@ -200,13 +205,14 @@ contract GhostStaking is IStaking, GhostAccessControlled {
emit WarmupSet(_warmupPeriod); emit WarmupSet(_warmupPeriod);
} }
function setGatekeeperAddress(uint256 existentialDeposit) external onlyGovernor { function updateGatekeeperAddress(address newGatekeeper) external onlyGovernor {
Gatekeeper newGatekeeper = new Gatekeeper(existentialDeposit, gatekeeper); IGatekeeper(newGatekeeper).initialize(gatekeeper);
address storageHistory = IGatekeeper(gatekeeper).storageHistory();
IStorageHistory(storageHistory).setOwner(address(newGatekeeper));
gatekeeper = address(newGatekeeper); address storageHistory = IGatekeeper(gatekeeper).storageHistory();
emit GatekeeperSet(address(newGatekeeper)); IStorageHistory(storageHistory).setOwner(newGatekeeper);
gatekeeper = newGatekeeper;
emit GatekeeperSet(newGatekeeper);
} }
function index() public view override returns (uint256) { function index() public view override returns (uint256) {

View File

@ -85,10 +85,6 @@ contract GhostDistributor is IDistributor, GhostAccessControlled {
bounty = _bounty; bounty = _bounty;
} }
function setPools(address[] calldata _pools) external override onlyGovernor {
pools = _pools;
}
function removePool(uint256 index) external override onlyGovernor { function removePool(uint256 index) external override onlyGovernor {
uint256 length = pools.length; uint256 length = pools.length;
pools[index] = pools[length - 1]; pools[index] = pools[length - 1];

View File

@ -26,7 +26,6 @@ contract Stinky is ISTNK, ERC20Permit {
address public ghst; address public ghst;
address public treasury; address public treasury;
mapping(address => uint256) public override debtBalances;
mapping(address => uint256) private _shares; mapping(address => uint256) private _shares;
mapping(address => mapping(address => uint256)) private _allowedValue; mapping(address => mapping(address => uint256)) private _allowedValue;
@ -180,18 +179,6 @@ contract Stinky is ISTNK, ERC20Permit {
return balanceForShares(_INTERNAL_INDEX); return balanceForShares(_INTERNAL_INDEX);
} }
function changeDebt(
uint256 amount,
address debtor,
bool add
) external override {
if (msg.sender != treasury) revert NotTreasury();
uint256 debtBalance = debtBalances[debtor];
debtBalance = add ? debtBalance + amount : debtBalance - amount;
if (debtBalance > balanceOf(debtor)) revert InsufficientBalance();
debtBalances[debtor] = debtBalance;
}
function _transferInner( function _transferInner(
address from, address from,
address to, address to,
@ -200,7 +187,6 @@ contract Stinky is ISTNK, ERC20Permit {
uint256 sharesValue = value * _sharesPerUnit; uint256 sharesValue = value * _sharesPerUnit;
_shares[from] = _shares[from] - sharesValue; _shares[from] = _shares[from] - sharesValue;
_shares[to] = _shares[to] + sharesValue; _shares[to] = _shares[to] + sharesValue;
if (balanceOf(from) < debtBalances[from]) revert DebtExists();
emit Transfer(from, to, value); emit Transfer(from, to, value);
} }

View File

@ -15,7 +15,6 @@ import {Babylonian} from "./libraries/FixedPoint.sol";
import {FullMath} from "./libraries/FullMath.sol"; import {FullMath} from "./libraries/FullMath.sol";
import {IFTSO} from "./interfaces/IFTSO.sol"; import {IFTSO} from "./interfaces/IFTSO.sol";
import {ISTNK} from "./interfaces/ISTNK.sol";
import {IBondingCalculator} from "./interfaces/IBondingCalculator.sol"; import {IBondingCalculator} from "./interfaces/IBondingCalculator.sol";
import {ITreasury} from "./interfaces/ITreasury.sol"; import {ITreasury} from "./interfaces/ITreasury.sol";
import {IGhostAuthority} from "./interfaces/IGhostAuthority.sol"; import {IGhostAuthority} from "./interfaces/IGhostAuthority.sol";
@ -29,16 +28,10 @@ contract GhostTreasury is GhostAccessControlled, ITreasury {
uint256 public totalReserves; uint256 public totalReserves;
uint256 public totalDebt; uint256 public totalDebt;
uint256 public ftsoDebt; uint256 public ftsoDebt;
uint256 public onChainGovernanceTimelock;
address public stnk;
bool public timelockEnabled;
Queue[] public permissionQueue;
mapping(STATUS => address[]) public registry; mapping(STATUS => address[]) public registry;
mapping(STATUS => mapping(address => bool)) public permissions; mapping(STATUS => mapping(address => bool)) public permissions;
mapping(address => address) public bondCalculator; mapping(address => address) public bondCalculator;
mapping(address => uint256) public debtLimit;
constructor( constructor(
address _ftso, address _ftso,
@ -69,35 +62,6 @@ contract GhostTreasury is GhostAccessControlled, ITreasury {
emit Deposit(token, amount, value); emit Deposit(token, amount, value);
} }
function withdraw(address token, uint256 amount) external override {
if (!permissions[STATUS.RESERVETOKEN][token]) revert NotAccepted();
if (!permissions[STATUS.RESERVESPENDER][msg.sender]) revert NotApproved();
uint256 value = tokenValue(token, amount);
IFTSO(ftso).burnFrom(msg.sender, value);
totalReserves = totalReserves - value;
IERC20(token).safeTransfer(msg.sender, amount);
emit Withdrawal(token, amount, value);
}
function manage(address token, uint256 amount) external override {
if (permissions[STATUS.LIQUIDITYTOKEN][token]) {
if (!permissions[STATUS.LIQUIDITYMANAGER][msg.sender]) revert NotApproved();
} else {
if (!permissions[STATUS.RESERVEMANAGER][msg.sender]) revert NotApproved();
}
if (permissions[STATUS.RESERVETOKEN][token] || permissions[STATUS.LIQUIDITYTOKEN][token]) {
uint256 value = tokenValue(token, amount);
if (value > excessReserves()) revert InsufficientReserves();
totalReserves = totalReserves - value;
}
IERC20(token).safeTransfer(msg.sender, amount);
emit Managed(token, amount);
}
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();
@ -105,50 +69,14 @@ contract GhostTreasury is GhostAccessControlled, ITreasury {
emit Minted(msg.sender, recipient, amount); emit Minted(msg.sender, recipient, amount);
} }
function incurDebt(address token, uint256 amount) external override { function withdraw(address token, uint256 amount) external onlyGovernor override {
uint256 value;
if (token == ftso) {
if (!permissions[STATUS.FTSODEBTOR][msg.sender]) revert NotApproved();
value = amount;
} else {
if (!permissions[STATUS.RESERVEDEBTOR][msg.sender]) revert NotApproved();
if (!permissions[STATUS.RESERVETOKEN][token]) revert NotAccepted(); if (!permissions[STATUS.RESERVETOKEN][token]) revert NotAccepted();
value = tokenValue(token, amount);
}
if (value == 0) revert InvalidToken();
ISTNK(stnk).changeDebt(value, msg.sender, true);
if (ISTNK(stnk).debtBalances(msg.sender) > debtLimit[msg.sender]) revert TreasuryExceeds();
totalDebt = totalDebt + value;
if (token == ftso) {
IFTSO(ftso).mint(msg.sender, value);
ftsoDebt = ftsoDebt + value;
} else {
totalReserves = totalReserves - value;
IERC20(token).safeTransfer(msg.sender, amount);
}
emit CreateDebt(msg.sender, token, amount, value);
}
function repayDebtWithReserves(address token, uint256 amount) external override {
if (!permissions[STATUS.RESERVEDEBTOR][msg.sender]) revert NotApproved();
if (!permissions[STATUS.RESERVETOKEN][token]) revert NotAccepted();
IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
uint256 value = tokenValue(token, amount); uint256 value = tokenValue(token, amount);
ISTNK(stnk).changeDebt(value, msg.sender, false); totalReserves = totalReserves - value;
totalDebt = totalDebt - value;
totalReserves = totalReserves + value;
emit RepayDebt(msg.sender, token, amount, value);
}
function repayDebtWithFtso(uint256 amount) external override { IERC20(token).safeTransfer(msg.sender, amount);
if (!permissions[STATUS.RESERVEDEBTOR][msg.sender]) revert NotApproved(); emit Withdrawal(token, amount, value);
if (!permissions[STATUS.FTSODEBTOR][msg.sender]) revert NotApproved();
IFTSO(ftso).burnFrom(msg.sender, amount);
ISTNK(stnk).changeDebt(amount, msg.sender, false);
totalDebt = totalDebt - amount;
emit RepayDebt(msg.sender, ftso, amount, amount);
} }
function auditReserves() external { function auditReserves() external {
@ -189,19 +117,11 @@ contract GhostTreasury is GhostAccessControlled, ITreasury {
emit ReservesAudited(reserves); emit ReservesAudited(reserves);
} }
function setDebtLimit(address who, uint256 limit) external onlyGovernor {
debtLimit[who] = limit;
}
function enable( function enable(
STATUS status, STATUS status,
address someAddress, address someAddress,
address calculatorAddress address calculatorAddress
) external onlyGovernor { ) external onlyGovernor {
if (timelockEnabled) revert OnlyQueueTimelock();
if (status == STATUS.STNK) {
stnk = someAddress;
} else {
permissions[status][someAddress] = true; permissions[status][someAddress] = true;
(bool registered, ) = indexInRegistry(someAddress, status); (bool registered, ) = indexInRegistry(someAddress, status);
if (!registered && (status == STATUS.LIQUIDITYTOKEN || status == STATUS.RESERVETOKEN)) { if (!registered && (status == STATUS.LIQUIDITYTOKEN || status == STATUS.RESERVETOKEN)) {
@ -209,7 +129,6 @@ contract GhostTreasury is GhostAccessControlled, ITreasury {
registry[status].push(someAddress); registry[status].push(someAddress);
bondCalculator[someAddress] = calculatorAddress; bondCalculator[someAddress] = calculatorAddress;
} }
}
emit Permissioned(someAddress, status, true); emit Permissioned(someAddress, status, true);
} }
@ -222,12 +141,6 @@ contract GhostTreasury is GhostAccessControlled, ITreasury {
emit Permissioned(toDisable, status, false); emit Permissioned(toDisable, status, false);
} }
function _quantityToBeSwapped(uint256 xa, uint256 x1) internal pure returns (uint256) {
uint256 y1 = x1 * 1994 / 1000;
uint256 y2 = Babylonian.sqrt(y1**2 + 4 * xa * x1 * 997 / 1000);
return (y2 - y1) * 1000 / 1994;
}
function forfeitReserves( function forfeitReserves(
address router, address router,
uint256 liquidity, uint256 liquidity,
@ -257,7 +170,7 @@ contract GhostTreasury is GhostAccessControlled, ITreasury {
} }
function redeemReserve( function redeemReserve(
address router, // could be an issue address router,
uint256 amount uint256 amount
) external onlyGovernor { ) external onlyGovernor {
address weth = IUniswapV2Router01(router).WETH(); address weth = IUniswapV2Router01(router).WETH();
@ -308,70 +221,6 @@ contract GhostTreasury is GhostAccessControlled, ITreasury {
return (false, 0); return (false, 0);
} }
function queueTimelock(
STATUS status,
address someAddress,
address calculatorAddress
) external onlyGovernor {
if (someAddress == address(0)) revert EmptyAddress();
if (!timelockEnabled) revert TimelockDisabled();
uint256 timelock = block.number + blocksNeededForQueue;
if (status == STATUS.RESERVEMANAGER || status == STATUS.LIQUIDITYMANAGER) {
timelock = block.number + (blocksNeededForQueue * 2);
}
permissionQueue.push(
Queue({
managing: status,
toPermit: someAddress,
calculator: calculatorAddress,
timelockEnd: timelock,
nullify: false,
executed: false
})
);
emit PermissionQueued(status, someAddress);
}
function execute(uint256 idx) external {
if (timelockEnabled) revert TimelockDisabled();
Queue storage info = permissionQueue[idx];
if (info.nullify) revert ActionNullified();
if (info.executed) revert ActionExecuted();
if (info.timelockEnd > block.number) revert TimelockNotComplete();
if (info.managing == STATUS.STNK) {
stnk = info.toPermit;
} else {
permissions[info.managing][info.toPermit] = true;
(bool registered, ) = indexInRegistry(info.toPermit, info.managing);
if (!registered && (info.managing == STATUS.LIQUIDITYTOKEN || info.managing == STATUS.RESERVETOKEN)) {
assert(info.calculator != address(0));
registry[info.managing].push(info.toPermit);
bondCalculator[info.toPermit] = info.calculator;
}
}
permissionQueue[idx].executed = true;
emit Permissioned(info.toPermit, info.managing, true);
}
function nullify(uint256 idx) external onlyGovernor {
permissionQueue[idx].nullify = true;
}
function toggleTimelock() external onlyGovernor {
if (onChainGovernanceTimelock > 0 && onChainGovernanceTimelock <= block.number) {
timelockEnabled = !timelockEnabled;
onChainGovernanceTimelock = 0;
} else {
onChainGovernanceTimelock = block.number + (blocksNeededForQueue * 7);
}
}
function originalCoefficient() external view returns (uint256) { function originalCoefficient() external view returns (uint256) {
address[] memory reserveTokens = registry[STATUS.RESERVETOKEN]; address[] memory reserveTokens = registry[STATUS.RESERVETOKEN];
return IBondingCalculator(bondCalculator[reserveTokens[0]]).fraction(); return IBondingCalculator(bondCalculator[reserveTokens[0]]).fraction();
@ -396,4 +245,10 @@ contract GhostTreasury is GhostAccessControlled, ITreasury {
function baseSupply() external view override returns (uint256) { function baseSupply() external view override returns (uint256) {
return IFTSO(ftso).totalSupply() - ftsoDebt; return IFTSO(ftso).totalSupply() - ftsoDebt;
} }
function _quantityToBeSwapped(uint256 xa, uint256 x1) internal pure returns (uint256) {
uint256 y1 = x1 * 1994 / 1000;
uint256 y2 = Babylonian.sqrt(y1**2 + 4 * xa * x1 * 997 / 1000);
return (y2 - y1) * 1000 / 1994;
}
} }

View File

@ -1,249 +0,0 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {IERC20} from "@openzeppelin-contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
import {GhostAccessControlled} from "./types/GhostAccessControlled.sol";
import {ITreasury} from "./interfaces/ITreasury.sol";
import {IAllocator} from "./interfaces/IAllocator.sol";
import {ITreasuryExtender} from "./interfaces/ITreasuryExtender.sol";
import {IGhostAuthority} from "./interfaces/IGhostAuthority.sol";
contract TreasuryExtender is GhostAccessControlled, ITreasuryExtender {
using SafeERC20 for IERC20;
ITreasury public immutable treasury; // forge-lint: disable-line(screaming-snake-case-immutable)
IAllocator[] public allocators;
mapping(IAllocator => mapping(uint256 => AllocatorData)) public allocatorData;
constructor(address treasuryAddress, address authorityAddress)
GhostAccessControlled(IGhostAuthority(authorityAddress))
{
treasury = ITreasury(treasuryAddress);
allocators.push(IAllocator(address(0)));
}
function _allocatorActivated(IAllocator.AllocatorStatus status) internal pure {
if (IAllocator.AllocatorStatus.ACTIVATED != status) revert AllocatorNotActivated();
}
function _allocatorOffline(IAllocator.AllocatorStatus status) internal pure {
if (IAllocator.AllocatorStatus.OFFLINE != status) revert AllocatorNotOffline();
}
function _onlyAllocator(
IAllocator byStateId,
address sender,
uint256 id
) internal pure {
if (IAllocator(sender) != byStateId) revert OnlyAllocator(id, sender);
}
function registerDeposit(address newAllocator) external override onlyGuardian {
IAllocator allocator = IAllocator(newAllocator);
uint256 id = allocators.length;
allocators.push(allocator);
allocator.addId(id);
emit NewDepositRegistered(
newAllocator,
address(allocator.tokens()[allocator.tokenIds(id)]),
id
);
}
function setAllocatorLimits(
uint256 id,
AllocatorLimits calldata limits
) external override onlyGuardian {
IAllocator allocator = allocators[id];
_allocatorOffline(allocator.status());
allocatorData[allocator][id].limits = limits;
emit AllocatorLimitsChanged(id, limits.allocated, limits.loss);
}
function report(
uint256 id,
uint128 gain,
uint128 loss
) external override {
IAllocator allocator = allocators[id];
AllocatorData storage data = allocatorData[allocator][id];
AllocatorPerformance memory perf = data.performance;
IAllocator.AllocatorStatus status = allocator.status();
_onlyAllocator(allocator, msg.sender, id);
if (status == IAllocator.AllocatorStatus.OFFLINE) revert AllocatorOffline();
if (gain >= loss) {
if (loss == type(uint128).max) {
AllocatorData storage newAllocatorData =
allocatorData[allocators[allocators.length - 1]][id];
newAllocatorData.holdings.allocated = data.holdings.allocated;
newAllocatorData.performance.gain = data.performance.gain;
data.holdings.allocated = 0;
perf.gain = 0;
perf.loss = 0;
emit AllocatorReportedMigration(id);
} else {
perf.gain += gain;
emit AllocatorReportedGain(id, gain);
}
} else {
data.holdings.allocated -= loss;
perf.loss += loss;
emit AllocatorReportedLoss(id, loss);
}
data.performance = perf;
}
function requestFundsFromTreasury(
uint256 id,
uint256 amount
) external override onlyGuardian {
IAllocator allocator = allocators[id];
AllocatorData memory data = allocatorData[allocator][id];
address token = address(allocator.tokens()[allocator.tokenIds(id)]);
uint256 value = treasury.tokenValue(token, amount);
_allocatorActivated(allocator.status());
_allocatorBelowLimit(data, amount);
treasury.manage(token, amount);
allocatorData[allocator][id].holdings.allocated += amount;
IERC20(token).safeTransfer(address(allocator), amount);
emit AllocatorFunded(id, amount, value);
}
function returnFundsToTreasury(
uint256 id,
uint256 amount
) external override onlyGuardian {
IAllocator allocator = allocators[id];
uint256 allocated = allocatorData[allocator][id].holdings.allocated;
uint128 gain = allocatorData[allocator][id].performance.gain;
address token = address(allocator.tokens()[allocator.tokenIds(id)]);
if (amount > allocated) {
amount -= allocated;
if (amount > gain) {
amount = allocated + gain;
gain = 0;
} else {
// forge-lint: disable-next-line(unsafe-typecast)
gain -= uint128(amount);
amount += allocated;
}
allocated = 0;
} else {
allocated -= amount;
}
uint256 value = treasury.tokenValue(token, amount);
_allowTreasuryWithdrawal(IERC20(token));
IERC20(token).safeTransferFrom(address(allocator), address(this), amount);
allocatorData[allocator][id].holdings.allocated = allocated;
if (allocated == 0) allocatorData[allocator][id].performance.gain = gain;
assert(treasury.deposit(token, amount, value) == 0);
emit AllocatedWithdrawal(id, amount, value);
}
function returnRewardsToTreasury(
uint256 id,
address token,
uint256 amount
) external override {
_returnRewardsToTreasury(allocators[id], IERC20(token), amount);
}
function returnRewardsToTreasury(
address allocatorAddress,
address token,
uint256 amount
) external override {
_returnRewardsToTreasury(IAllocator(allocatorAddress), IERC20(token), amount);
}
function getAllocatorById(uint256 id)
external
view
override
returns (address)
{
return address(allocators[id]);
}
function getTotalAllocatorCount()
external
view
returns (uint256)
{
return allocators.length;
}
function getAllocatorLimits(uint256 id)
external
view
override
returns (AllocatorLimits memory)
{
return allocatorData[allocators[id]][id].limits;
}
function getAllocatorPerformance(uint256 id)
external
view
override
returns (AllocatorPerformance memory)
{
return allocatorData[allocators[id]][id].performance;
}
function getAllocatorAllocated(uint256 id)
external
view
override
returns (uint256)
{
return allocatorData[allocators[id]][id].holdings.allocated;
}
function _returnRewardsToTreasury(
IAllocator allocator,
IERC20 token,
uint256 amount
) internal onlyGuardian {
uint256 balance = token.balanceOf(address(allocator));
amount = balance < amount ? balance : amount;
uint256 value = treasury.tokenValue(address(token), amount);
_allowTreasuryWithdrawal(token);
token.safeTransferFrom(address(allocator), address(this), amount);
assert(treasury.deposit(address(token), amount, value) == 0);
emit AllocatorRewardsWithdrawal(address(allocator), amount, value);
}
function _allowTreasuryWithdrawal(IERC20 token) internal {
if (token.allowance(address(this), address(treasury)) == 0) {
token.approve(address(treasury), type(uint256).max);
}
}
function _allocatorBelowLimit(
AllocatorData memory data,
uint256 amount
) internal pure {
uint256 newAllocated = data.holdings.allocated + amount;
if (newAllocated > data.limits.allocated) {
revert AllocatorMaxAllocation(newAllocated, data.limits.allocated);
}
}
}

View File

@ -1,50 +0,0 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {IERC20} from "@openzeppelin-contracts/token/ERC20/IERC20.sol";
import {ITreasuryExtender} from "./ITreasuryExtender.sol";
import {IGhostAuthority} from "./IGhostAuthority.sol";
interface IAllocator {
error OnlyExtender(address sender);
error AllocatorNotActivated();
error AllocatorNotOffline();
error Migrating();
error NotMigrating();
enum AllocatorStatus {
OFFLINE,
ACTIVATED,
MIGRATING
}
struct AllocatorInitData {
IGhostAuthority authority;
ITreasuryExtender extender;
IERC20[] tokens;
}
event AllocatorDeployed(address authority, address extender);
event AllocatorActivated();
event AllocatorDeactivated(bool panic);
event LossLimitViolated(uint128 lastLoss, uint128 dloss, uint256 estimatedTotalAllocated);
event MigrationExecuted(address allocator);
event EtherReceived(uint256 amount);
function update(uint256 id) external;
function deallocate(uint256[] memory amounts) external;
function prepareMigration() external;
function migrate() external;
function activate() external;
function deactivate(bool panic) external;
function addId(uint256 id) external;
function name() external view returns (string memory);
function ids() external view returns (uint256[] memory);
function tokenIds(uint256 id) external view returns (uint256);
function version() external view returns (string memory);
function status() external view returns (AllocatorStatus);
function tokens() external view returns (IERC20[] memory);
function utilityTokens() external view returns (IERC20[] memory);
function rewardTokens() external view returns (IERC20[] memory);
function amountAllocated(uint256 id) external view returns (uint256);
}

View File

@ -12,7 +12,6 @@ interface IBondDepository {
event Tuned(uint256 indexed id, uint64 oldControlVariable, uint64 newControlVariable); event Tuned(uint256 indexed id, uint64 oldControlVariable, uint64 newControlVariable);
event MarketCreated( event MarketCreated(
uint256 indexed id, uint256 indexed id,
address indexed baseToken,
address indexed quoteToken, address indexed quoteToken,
uint256 initialPrice uint256 initialPrice
); );
@ -55,8 +54,7 @@ interface IBondDepository {
uint256 _bid, uint256 _bid,
uint256 _amount, uint256 _amount,
uint256 _maxPrice, uint256 _maxPrice,
address _user, address _user
address _referral
) )
external external
payable payable

View File

@ -17,8 +17,8 @@ interface IDistributor {
function distribute() external; function distribute() external;
function retrieveBounty() external returns (uint256); function retrieveBounty() external returns (uint256);
function nextRewardFor(address who) external view returns (uint256); function nextRewardFor(address who) external view returns (uint256);
function setBounty(uint256 _bounty) external; function setBounty(uint256 _bounty) external;
function setPools(address[] calldata _pools) external;
function removePool(uint256 index) external; function removePool(uint256 index) external;
function addPool(address pool) external; function addPool(address pool) external;
} }

View File

@ -14,17 +14,20 @@ interface IGatekeeper {
error ExecutionReverted(); error ExecutionReverted();
event Ghosted(bytes32 indexed receiver, uint256 indexed amount); event Ghosted(bytes32 indexed receiver, uint256 indexed amount);
event Materialized(address indexed receiver, uint256 indexed amount); event Recalled(address indexed receiver, uint256 indexed amount);
event Rotated(bytes32 indexed aggregatedPublicKey, uint8 indexed parity); event Rotated(bytes32 indexed aggregatedPublicKey, uint8 indexed parity);
function staking() external view returns (address); function staking() external view returns (address);
function storageHistory() 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 existentialDeposit() external view returns (uint256); 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);
function materialize(uint256 session, uint256 amount, uint256 payload) external payable; function govern(uint256 session, uint256 payload, bytes calldata call) external returns (bytes calldata);
function recall(uint256 session, uint256 amount, uint256 payload) external payable;
function rotate(uint256 session, bytes32 publicKey, uint8 parity) external; function rotate(uint256 session, bytes32 publicKey, uint8 parity) external;
function ghost(bytes32 receiver, uint256 amount) external returns (uint256); function ghost(bytes32 receiver, uint256 amount) external returns (uint256);
function initialize(address previousAddress) external;
} }

View File

@ -5,9 +5,6 @@ import {IERC20} from "@openzeppelin-contracts/token/ERC20/IERC20.sol";
interface ISTNK is IERC20 { interface ISTNK is IERC20 {
error NotStakingContract(); error NotStakingContract();
error NotTreasury();
error InsufficientBalance();
error DebtExists();
error NotInitializer(); error NotInitializer();
event LogSupply(uint256 indexed epoch, uint256 totalSupply); event LogSupply(uint256 indexed epoch, uint256 totalSupply);
@ -24,10 +21,4 @@ interface ISTNK is IERC20 {
function index() external view returns (uint256); function index() external view returns (uint256);
function toGhst(uint256 amount) external view returns (uint256); function toGhst(uint256 amount) external view returns (uint256);
function fromGhst(uint256 amount) external view returns (uint256); function fromGhst(uint256 amount) external view returns (uint256);
function debtBalances(address _address) external view returns (uint256);
function changeDebt(
uint256 amount,
address debtor,
bool add
) external;
} }

View File

@ -68,7 +68,7 @@ interface IStaking {
function wrap(address _to, uint256 _amount) external returns (uint256 gBalance_); function wrap(address _to, uint256 _amount) external returns (uint256 gBalance_);
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 materialize(address receiver, uint256 amount) external; function recall(address receiver, uint256 amount) external;
function rebase() external returns (uint256); function rebase() external returns (uint256);
function index() external view returns (uint256); function index() external view returns (uint256);

View File

@ -8,7 +8,7 @@ interface IStorageHistory {
uint104 amountOut; uint104 amountOut;
} }
error NotCreator(); error NotOwner();
error NotDeployer(); error NotDeployer();
error AlreadyExecuted(); error AlreadyExecuted();
error BridgeOutImpossible(); error BridgeOutImpossible();

View File

@ -3,50 +3,23 @@ pragma solidity ^0.8.20;
interface ITreasury { interface ITreasury {
error NotApproved(); error NotApproved();
error InvalidToken();
error NotAccepted(); error NotAccepted();
error InvalidToken();
error InsufficientReserves(); error InsufficientReserves();
error TreasuryExceeds();
error OnlyQueueTimelock();
error EmptyAddress();
error TimelockDisabled();
error ActionNullified();
error ActionExecuted();
error TimelockNotComplete();
error AlreadyInitialized();
enum STATUS { enum STATUS {
RESERVEDEPOSITOR, RESERVEDEPOSITOR,
RESERVESPENDER,
RESERVETOKEN, RESERVETOKEN,
RESERVEMANAGER,
LIQUIDITYDEPOSITOR, LIQUIDITYDEPOSITOR,
LIQUIDITYTOKEN, LIQUIDITYTOKEN,
LIQUIDITYMANAGER, REWARDMANAGER
RESERVEDEBTOR,
REWARDMANAGER,
STNK,
FTSODEBTOR
}
struct Queue {
STATUS managing;
address toPermit;
address calculator;
uint256 timelockEnd;
bool nullify;
bool executed;
} }
event Deposit(address indexed token, uint256 amount, uint256 value); event Deposit(address indexed token, uint256 amount, uint256 value);
event Withdrawal(address indexed token, uint256 amount, uint256 value);
event CreateDebt(address indexed debtor, address indexed token, uint256 amount, uint256 value);
event RepayDebt(address indexed debtor, address indexed token, uint256 amount, uint256 value);
event Managed(address indexed token, uint256 amount);
event ReservesAudited(uint256 indexed totalReserves); event ReservesAudited(uint256 indexed totalReserves);
event Minted(address indexed caller, address indexed recipient, uint256 amount); event Minted(address indexed caller, address indexed recipient, uint256 amount);
event PermissionQueued(STATUS indexed status, address queued);
event Permissioned(address addr, STATUS indexed status, bool result); event Permissioned(address addr, STATUS indexed status, bool result);
event Withdrawal(address indexed token, uint256 indexed amount, uint256 value);
function deposit( function deposit(
address _token, address _token,
@ -54,13 +27,8 @@ interface ITreasury {
uint256 _profit uint256 _profit
) external returns (uint256); ) external returns (uint256);
function withdraw(address _token, uint256 _amount) external; function withdraw(address token, uint256 amount) external;
function repayDebtWithReserves(address _token, uint256 _amount) external;
function repayDebtWithFtso(uint256 _amount) external;
function mint(address _recipient, uint256 _amount) external; function mint(address _recipient, uint256 _amount) external;
function manage(address _token, uint256 _amount) external;
function incurDebt(address _token, 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 indexInRegistry(address _address, STATUS _status) external view returns (bool, uint256);
function excessReserves() external view returns (uint256); function excessReserves() external view returns (uint256);

View File

@ -1,71 +0,0 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
interface ITreasuryExtender {
error AllocatorNotActivated();
error AllocatorNotOffline();
error AllocatorOffline();
error OnlyAllocator(uint256 id, address sender);
error AllocatorMaxAllocation(uint256 newAllocated, uint256 oldAllocated);
event AllocatedWithdrawal(uint256 id, uint256 amount, uint256 value);
struct AllocatorPerformance {
uint128 gain;
uint128 loss;
}
struct AllocatorLimits {
uint128 allocated;
uint128 loss;
}
struct AllocatorHoldings {
uint256 allocated;
}
struct AllocatorData {
AllocatorHoldings holdings;
AllocatorLimits limits;
AllocatorPerformance performance;
}
event NewDepositRegistered(address allocator, address token, uint256 id);
event AllocatorFunded(uint256 id, uint256 amount, uint256 value);
event AllocatorWithdrawal(uint256 id, uint256 amount, uint256 value);
event AllocatorRewardsWithdrawal(address allocator, uint256 amount, uint256 value);
event AllocatorReportedGain(uint256 id, uint128 gain);
event AllocatorReportedLoss(uint256 id, uint128 loss);
event AllocatorReportedMigration(uint256 id);
event AllocatorLimitsChanged(uint256 id, uint128 allocationLimit, uint128 lossLimit);
function registerDeposit(address newAllocator) external;
function setAllocatorLimits(uint256 id, AllocatorLimits memory limits) external;
function report(
uint256 id,
uint128 gain,
uint128 loss
) external;
function requestFundsFromTreasury(uint256 id, uint256 amount) external;
function returnFundsToTreasury(uint256 id, uint256 amount) external;
function returnRewardsToTreasury(
uint256 id,
address token,
uint256 amount
) external;
function returnRewardsToTreasury(
address allocator,
address token,
uint256 amount
) external;
function getTotalAllocatorCount() external view returns (uint256);
function getAllocatorById(uint256 id) external view returns (address);
function getAllocatorAllocated(uint256 id) external view returns (uint256);
function getAllocatorLimits(uint256 id) external view returns (AllocatorLimits memory);
function getAllocatorPerformance(uint256 id) external view returns (AllocatorPerformance memory);
}

View File

@ -16,4 +16,13 @@ library Hashes {
value := keccak256(0x00, 0x40) value := keccak256(0x00, 0x40)
} }
} }
function efficientKeccak256(bytes32 a, bytes32 b, bytes32 c) internal pure returns (bytes32 value) {
assembly ("memory-safe") {
mstore(0x00, a)
mstore(0x20, b)
mstore(0x40, c)
value := keccak256(0x00, 0x60)
}
}
} }

View File

@ -24,27 +24,51 @@ library RotationPacking {
} }
} }
library GovernancePacking {
struct GovernancePayload {
uint64 chainId;
address target;
}
function pack(uint64 chainId, address target) internal pure returns (uint256 packed) {
// forge-lint: disable-next-line(unsafe-typecast)
return (uint256(chainId) << 192) | (uint256(uint160(target)) << 32);
}
function pack(GovernancePayload calldata data) internal pure returns (uint256 packed) {
return pack(data.chainId, data.target);
}
function unpack(uint256 packed) internal pure returns (GovernancePayload memory state) {
// forge-lint: disable-next-line(unsafe-typecast)
state.target = address(uint160(packed >> 32));
// forge-lint: disable-next-line(unsafe-typecast)
state.chainId = uint64(packed >> 192);
return state;
}
}
library RequestPacking { library RequestPacking {
struct RequestPayload { struct RequestPayload {
uint32 commission; uint32 bounty;
uint64 chainId; uint64 chainId;
address receiver; address receiver;
} }
function pack(uint32 commission, uint64 chainId, address receiver) internal pure returns (uint256 packed) { function pack(uint32 bounty, uint64 chainId, address receiver) internal pure returns (uint256 packed) {
packed = uint256(uint160(receiver)) << 96; packed = uint256(uint160(receiver)) << 96;
packed |= uint256(chainId) << 32; packed |= uint256(chainId) << 32;
packed |= uint256(commission); packed |= uint256(bounty);
return packed; return packed;
} }
function pack(RequestPayload calldata data) internal pure returns (uint256 packed) { function pack(RequestPayload calldata data) internal pure returns (uint256 packed) {
return pack(data.commission, data.chainId, data.receiver); return pack(data.bounty, data.chainId, data.receiver);
} }
function unpack(uint256 packed) internal pure returns (RequestPayload memory data) { function unpack(uint256 packed) internal pure returns (RequestPayload memory data) {
// forge-lint: disable-next-line(unsafe-typecast) // forge-lint: disable-next-line(unsafe-typecast)
data.commission = uint32(packed & 0xFFFFFFFF); data.bounty = uint32(packed & 0xFFFFFFFF);
// forge-lint: disable-next-line(unsafe-typecast) // forge-lint: disable-next-line(unsafe-typecast)
data.chainId = uint64((packed >> 32) & 0xFFFFFFFFFFFFFFFF); data.chainId = uint64((packed >> 32) & 0xFFFFFFFFFFFFFFFF);
// forge-lint: disable-next-line(unsafe-typecast) // forge-lint: disable-next-line(unsafe-typecast)

View File

@ -6,7 +6,8 @@ import {Weaver} from "../types/Weaver.sol";
contract WeaverMock is Weaver { contract WeaverMock is Weaver {
address private _previousAddress; address private _previousAddress;
constructor(address previousWeaver) Weaver(previousWeaver) { constructor(address previousWeaver) {
Weaver._initialize(previousWeaver);
_previousAddress = previousWeaver; _previousAddress = previousWeaver;
} }

View File

@ -1,176 +0,0 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {IERC20} from "@openzeppelin-contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
import {GhostAccessControlled} from "./GhostAccessControlled.sol";
import {IAllocator} from "../interfaces/IAllocator.sol";
import {ITreasuryExtender} from "../interfaces/ITreasuryExtender.sol";
abstract contract BaseAllocator is GhostAccessControlled, IAllocator {
using SafeERC20 for IERC20;
ITreasuryExtender public immutable extender; // forge-lint: disable-line(screaming-snake-case-immutable)
AllocatorStatus public status;
uint256[] internal _ids;
IERC20[] internal _tokens;
mapping(uint256 => uint256) public tokenIds;
constructor(AllocatorInitData memory data)
GhostAccessControlled(data.authority)
{
_tokens = data.tokens;
extender = data.extender;
uint256 i;
for (; i < data.tokens.length; ) {
data.tokens[i].approve(
address(data.extender),
type(uint256).max
);
unchecked { ++i; }
}
emit AllocatorDeployed(
address(data.authority),
address(data.extender)
);
}
modifier onlyExtender {
_onlyExtender(msg.sender);
_;
}
modifier onlyActivated {
_onlyActivated(status);
_;
}
modifier onlyOffline {
_onlyOffline(status);
_;
}
modifier notMigrating {
_notMigrating(status);
_;
}
modifier isMigrating {
_isMigrating(status);
_;
}
function _activate() internal virtual {}
function _prepareMigration() internal virtual;
function _deactivate(bool panic) internal virtual;
function _update(uint256 id) internal virtual returns (uint128 gain, uint128 loss);
function deallocate(uint256[] memory amounts) public virtual;
function amountAllocated(uint256 id) public view virtual returns (uint256);
function rewardTokens() public view virtual returns (IERC20[] memory);
function utilityTokens() public view virtual returns (IERC20[] memory);
function name() external view virtual returns (string memory);
function update(uint256 id) external override onlyGuardian onlyActivated {
(uint128 gain, uint128 loss) = _update(id);
if (_lossLimitViolated(id, loss)) {
deactivate(true);
return;
}
if (gain + loss > 0) extender.report(id, gain, loss);
}
function prepareMigration() external override onlyGuardian notMigrating {
_prepareMigration();
status = AllocatorStatus.MIGRATING;
}
function migrate() external override onlyGuardian isMigrating {
IERC20[] memory utilityTokensArray = utilityTokens();
address newAllocator = extender.getAllocatorById(extender.getTotalAllocatorCount() - 1);
uint256 idLength = _ids.length;
uint256 utilLength = utilityTokensArray.length;
for (uint256 i; i < idLength; i++) {
IERC20 token = _tokens[i];
token.safeTransfer(newAllocator, token.balanceOf(address(this)));
extender.report(_ids[i], type(uint128).max, type(uint128).max);
}
for (uint256 i; i < utilLength; i++) {
IERC20 utilityToken = utilityTokensArray[i];
utilityToken.safeTransfer(newAllocator, utilityToken.balanceOf(address(this)));
}
deactivate(false);
emit MigrationExecuted(newAllocator);
}
function activate() external override onlyGuardian onlyOffline {
_activate();
status = AllocatorStatus.ACTIVATED;
emit AllocatorActivated();
}
function addId(uint256 id) external override onlyExtender {
_ids.push(id);
tokenIds[id] = _ids.length - 1;
}
function ids() external view override returns (uint256[] memory) {
return _ids;
}
function tokens() external view override returns (IERC20[] memory) {
return _tokens;
}
function deactivate(bool panic) public override onlyGuardian {
_deactivate(panic);
status = AllocatorStatus.OFFLINE;
emit AllocatorDeactivated(panic);
}
function version() public pure override returns (string memory) {
return "v2.0.0";
}
function _lossLimitViolated(uint256 id, uint128 loss) internal returns (bool) {
uint128 lastLoss = extender.getAllocatorPerformance(id).loss;
if ((loss + lastLoss) >= extender.getAllocatorLimits(id).loss) {
emit LossLimitViolated(lastLoss, loss, amountAllocated(tokenIds[id]));
return true;
}
return false;
}
function _onlyExtender(address sender) internal view {
if (sender != address(extender)) revert OnlyExtender(sender);
}
function _onlyActivated(AllocatorStatus inputStatus) internal pure {
if (inputStatus != AllocatorStatus.ACTIVATED) revert AllocatorNotActivated();
}
function _onlyOffline(AllocatorStatus inputStatus) internal pure {
if (inputStatus != AllocatorStatus.OFFLINE) revert AllocatorNotOffline();
}
function _notMigrating(AllocatorStatus inputStatus) internal pure {
if (inputStatus == AllocatorStatus.MIGRATING) revert Migrating();
}
function _isMigrating(AllocatorStatus inputStatus) internal pure {
if (inputStatus != AllocatorStatus.MIGRATING) revert NotMigrating();
}
}

View File

@ -1,8 +1,6 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.20; pragma solidity ^0.8.20;
import {FrontEndRewarder} from "./FrontEndRewarder.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";
import {ITreasury} from "../interfaces/ITreasury.sol"; import {ITreasury} from "../interfaces/ITreasury.sol";
@ -11,7 +9,7 @@ import {INoteKeeper} from "../interfaces/INoteKeeper.sol";
import {EnumerableSet} from "@openzeppelin-contracts/utils/structs/EnumerableSet.sol"; import {EnumerableSet} from "@openzeppelin-contracts/utils/structs/EnumerableSet.sol";
import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol"; import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
abstract contract NoteKeeper is INoteKeeper, FrontEndRewarder { abstract contract NoteKeeper is INoteKeeper {
using EnumerableSet for EnumerableSet.UintSet; using EnumerableSet for EnumerableSet.UintSet;
using SafeERC20 for IGHST; using SafeERC20 for IGHST;
@ -21,36 +19,20 @@ abstract contract NoteKeeper is INoteKeeper, FrontEndRewarder {
IGHST internal immutable _GHST; IGHST internal immutable _GHST;
IStaking internal immutable _STAKING; IStaking internal immutable _STAKING;
ITreasury internal _treasury; ITreasury internal immutable _TREASURY;
constructor( constructor(address _ghstAddress, address _stakingAddress, address _treasuryAddress) {
address _authority,
address _ftsoAddress,
address _ghstAddress,
address _stakingAddress,
address _treasuryAddress
) FrontEndRewarder(_authority, _ftsoAddress) {
_GHST = IGHST(_ghstAddress); _GHST = IGHST(_ghstAddress);
_STAKING = IStaking(_stakingAddress); _STAKING = IStaking(_stakingAddress);
_treasury = ITreasury(_treasuryAddress); _TREASURY = ITreasury(_treasuryAddress);
_STAKING.toggleLock(); _STAKING.toggleLock();
} }
function updateTreasury() external {
if (
msg.sender != authority.governor() &&
msg.sender != authority.guardian() &&
msg.sender != authority.policy()
) revert OnlyAuthorized();
_treasury = ITreasury(authority.vault());
}
function addNote( function addNote(
address user, address user,
uint256 payout, uint256 payout,
uint48 expiry, uint48 expiry,
uint48 marketId, uint48 marketId
address referral
) internal returns (uint256 index) { ) internal returns (uint256 index) {
index = notes[user].length; index = notes[user].length;
_pendingIndexes[user].add(index); _pendingIndexes[user].add(index);
@ -64,8 +46,7 @@ abstract contract NoteKeeper is INoteKeeper, FrontEndRewarder {
}) })
); );
uint256 rewards = _giveRewards(payout, referral); _TREASURY.mint(address(this), payout);
_treasury.mint(address(this), payout + rewards);
_STAKING.stake(payout, address(this), false, true); _STAKING.stake(payout, address(this), false, true);
} }

View File

@ -7,15 +7,15 @@ import {BitMaps} from "@openzeppelin-contracts/utils/structs/BitMaps.sol";
contract StorageHistory is IStorageHistory { contract StorageHistory is IStorageHistory {
using BitMaps for BitMaps.BitMap; using BitMaps for BitMaps.BitMap;
address private _deployer; address immutable private DEPLOYER;
address private _currentOwner; address private _currentOwner;
DeploymentSnapshot private _snapshot; DeploymentSnapshot private _snapshot;
BitMaps.BitMap private _executedTransaction; BitMaps.BitMap private _executedTransaction;
constructor(address deployer) { constructor() {
_currentOwner = msg.sender; _currentOwner = msg.sender;
_deployer = deployer; DEPLOYER = msg.sender;
} }
function deploymentSnapshot() external override view returns (DeploymentSnapshot memory) { function deploymentSnapshot() external override view returns (DeploymentSnapshot memory) {
@ -31,25 +31,25 @@ contract StorageHistory is IStorageHistory {
return _executedTransaction.get(session); return _executedTransaction.get(session);
} }
function setOwner(address newOwner) external override {
if (msg.sender != DEPLOYER) revert NotDeployer();
_currentOwner = newOwner;
}
function trySetTransactionExecuted(uint256 session) external override { function trySetTransactionExecuted(uint256 session) external override {
if (msg.sender != _currentOwner) revert NotCreator(); if (msg.sender != _currentOwner) revert NotOwner();
if (_executedTransaction.get(session)) revert AlreadyExecuted(); if (_executedTransaction.get(session)) revert AlreadyExecuted();
_executedTransaction.set(session); _executedTransaction.set(session);
} }
function setOwner(address newOwner) external override {
if (msg.sender != _deployer) revert NotDeployer();
_currentOwner = newOwner;
}
function increaseBridgeIn(uint256 amount) external override { function increaseBridgeIn(uint256 amount) external override {
if (msg.sender != _currentOwner) revert NotCreator(); if (msg.sender != _currentOwner) revert NotOwner();
// forge-lint: disable-next-line(unsafe-typecast) // forge-lint: disable-next-line(unsafe-typecast)
_snapshot.amountIn += uint104(amount); _snapshot.amountIn += uint104(amount);
} }
function tryIncreaseBridgeOut(uint256 amount) external override { function tryIncreaseBridgeOut(uint256 amount) external override {
if (msg.sender != _currentOwner) revert NotCreator(); if (msg.sender != _currentOwner) revert NotOwner();
if (bridgeImbalance() < amount) revert BridgeOutImpossible(); if (bridgeImbalance() < amount) revert BridgeOutImpossible();
// forge-lint: disable-next-line(unsafe-typecast) // forge-lint: disable-next-line(unsafe-typecast)
_snapshot.amountOut += uint104(amount); _snapshot.amountOut += uint104(amount);

View File

@ -22,14 +22,6 @@ abstract contract Weaver is IWeaver {
mapping(uint256 => mapping(uint256 => bytes32[])) internal _slotValues; mapping(uint256 => mapping(uint256 => bytes32[])) internal _slotValues;
mapping(uint256 => uint256) internal _filledEntries; mapping(uint256 => uint256) internal _filledEntries;
constructor(address _previousAddress) {
if (_previousAddress != address(0)) {
uint256 newSession = IWeaver(_previousAddress).currentWeavingSession() + 1;
currentWeavingSession = newSession;
startWeavingSession = newSession;
}
}
function previousAddress() external virtual view returns (address); function previousAddress() external virtual view returns (address);
function getSlotValues( function getSlotValues(
@ -171,4 +163,10 @@ abstract contract Weaver is IWeaver {
function _computeArgumentsHash(uint256 i, uint256 a, bytes32 r) internal pure returns (bytes32) { function _computeArgumentsHash(uint256 i, uint256 a, bytes32 r) internal pure returns (bytes32) {
return Hashes.efficientKeccak256(Hashes.efficientKeccak256(bytes32(a), r), bytes32(i)); return Hashes.efficientKeccak256(Hashes.efficientKeccak256(bytes32(a), r), bytes32(i));
} }
function _initialize(address _previousAddress) internal {
uint256 newSession = IWeaver(_previousAddress).currentWeavingSession() + 1;
currentWeavingSession = newSession;
startWeavingSession = newSession;
}
} }

View File

@ -226,7 +226,7 @@ contract GhostBondDepositoryTest is Test {
skip(DEPOSIT_INTERVAL); skip(DEPOSIT_INTERVAL);
vm.prank(ALICE); vm.prank(ALICE);
depository.deposit(0, 0, INITIAL_PRICE, ALICE, ALICE); depository.deposit(0, 0, INITIAL_PRICE, ALICE);
(, , , uint256 newTotalDebt, , ,) = depository.markets(0); (, , , uint256 newTotalDebt, , ,) = depository.markets(0);
assertEq(totalDebt > newTotalDebt, true); assertEq(totalDebt > newTotalDebt, true);
@ -237,7 +237,7 @@ contract GhostBondDepositoryTest is Test {
uint256 amount = 10_000 * 1e18; uint256 amount = 10_000 * 1e18;
vm.prank(ALICE); vm.prank(ALICE);
depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE); depository.deposit(0, amount, INITIAL_PRICE, ALICE);
(, , , bool active) = depository.adjustments(0); (, , , bool active) = depository.adjustments(0);
assertEq(active, true); assertEq(active, true);
@ -248,10 +248,10 @@ contract GhostBondDepositoryTest is Test {
uint256 amount = 10_000 * 1e18; uint256 amount = 10_000 * 1e18;
vm.startPrank(ALICE); vm.startPrank(ALICE);
depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE); depository.deposit(0, amount, INITIAL_PRICE, ALICE);
skip(DEPOSIT_INTERVAL); skip(DEPOSIT_INTERVAL);
(uint64 change, , ,) = depository.adjustments(0); (uint64 change, , ,) = depository.adjustments(0);
depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE); depository.deposit(0, amount, INITIAL_PRICE, ALICE);
vm.stopPrank(); vm.stopPrank();
(, uint64 newCtrlVariable, , ,) = depository.terms(0); (, uint64 newCtrlVariable, , ,) = depository.terms(0);
@ -264,11 +264,11 @@ contract GhostBondDepositoryTest is Test {
uint256 amount = 10_000 * 1e18; uint256 amount = 10_000 * 1e18;
vm.startPrank(ALICE); vm.startPrank(ALICE);
depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE); depository.deposit(0, amount, INITIAL_PRICE, ALICE);
(uint64 change, , ,) = depository.adjustments(0); (uint64 change, , ,) = depository.adjustments(0);
skip(TUNE_INTERVAL / 2); skip(TUNE_INTERVAL / 2);
depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE); depository.deposit(0, amount, INITIAL_PRICE, ALICE);
(, uint64 newCtrlVariable, , ,) = depository.terms(0); (, uint64 newCtrlVariable, , ,) = depository.terms(0);
vm.stopPrank(); vm.stopPrank();
@ -282,12 +282,12 @@ contract GhostBondDepositoryTest is Test {
uint256 amount = 10_000 * 1e18; uint256 amount = 10_000 * 1e18;
vm.startPrank(ALICE); vm.startPrank(ALICE);
depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE); depository.deposit(0, amount, INITIAL_PRICE, ALICE);
(uint64 change, , ,) = depository.adjustments(0); (uint64 change, , ,) = depository.adjustments(0);
skip(TUNE_INTERVAL / 2); skip(TUNE_INTERVAL / 2);
depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE); depository.deposit(0, amount, INITIAL_PRICE, ALICE);
skip(TUNE_INTERVAL / 2); skip(TUNE_INTERVAL / 2);
depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE); depository.deposit(0, amount, INITIAL_PRICE, ALICE);
vm.stopPrank(); vm.stopPrank();
(, uint64 newCtrlVariable, , ,) = depository.terms(0); (, uint64 newCtrlVariable, , ,) = depository.terms(0);
@ -297,7 +297,7 @@ contract GhostBondDepositoryTest is Test {
function test_shouldAllowDeposit() public { function test_shouldAllowDeposit() public {
uint256 amount = 10_000 * 1e18; uint256 amount = 10_000 * 1e18;
vm.prank(ALICE); vm.prank(ALICE);
depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE); depository.deposit(0, amount, INITIAL_PRICE, ALICE);
uint256[] memory arr = depository.indexesFor(ALICE); uint256[] memory arr = depository.indexesFor(ALICE);
assertEq(arr.length, 1); assertEq(arr.length, 1);
@ -307,14 +307,14 @@ contract GhostBondDepositoryTest is Test {
uint256 amount = 6_700_000 * 1e18; uint256 amount = 6_700_000 * 1e18;
vm.expectRevert(); vm.expectRevert();
vm.prank(ALICE); vm.prank(ALICE);
depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE); depository.deposit(0, amount, INITIAL_PRICE, ALICE);
} }
function test_shouldNotRedeemImmediately() public { function test_shouldNotRedeemImmediately() public {
uint256 balance = ftso.balanceOf(ALICE); uint256 balance = ftso.balanceOf(ALICE);
uint256 amount = 10_000 * 1e18; // 10,000 uint256 amount = 10_000 * 1e18; // 10,000
vm.startPrank(ALICE); vm.startPrank(ALICE);
depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE); depository.deposit(0, amount, INITIAL_PRICE, ALICE);
depository.redeemAll(ALICE, true); depository.redeemAll(ALICE, true);
vm.stopPrank(); vm.stopPrank();
assertEq(ftso.balanceOf(ALICE), balance); assertEq(ftso.balanceOf(ALICE), balance);
@ -328,9 +328,9 @@ contract GhostBondDepositoryTest is Test {
vm.stopPrank(); vm.stopPrank();
vm.prank(ALICE); vm.prank(ALICE);
depository.deposit(0, amount, type(uint256).max, ALICE, ALICE); depository.deposit(0, amount, type(uint256).max, ALICE);
vm.prank(BOB); vm.prank(BOB);
depository.deposit(0, amount, type(uint256).max, BOB, BOB); depository.deposit(0, amount, type(uint256).max, BOB);
skip(DEPOSIT_INTERVAL); skip(DEPOSIT_INTERVAL);
vm.roll(block.number + 1); vm.roll(block.number + 1);
@ -346,7 +346,7 @@ contract GhostBondDepositoryTest is Test {
function test_shouldRedeemAfterVested() public { function test_shouldRedeemAfterVested() public {
uint256 amount = 10_000 * 1e18; // 10,000 uint256 amount = 10_000 * 1e18; // 10,000
vm.startPrank(ALICE); vm.startPrank(ALICE);
(uint256 expectedPayout, ,) = depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE); (uint256 expectedPayout, ,) = depository.deposit(0, amount, INITIAL_PRICE, ALICE);
skip(DEPOSIT_INTERVAL); skip(DEPOSIT_INTERVAL);
depository.redeemAll(ALICE, true); depository.redeemAll(ALICE, true);
@ -360,10 +360,10 @@ contract GhostBondDepositoryTest is Test {
function test_shouldCorrectlyRedeemPartially() public { function test_shouldCorrectlyRedeemPartially() public {
uint256 amount = 1 * 1e18; uint256 amount = 1 * 1e18;
vm.startPrank(ALICE); vm.startPrank(ALICE);
depository.deposit(0, amount, INITIAL_PRICE * 2, ALICE, ALICE); depository.deposit(0, amount, INITIAL_PRICE * 2, ALICE);
depository.deposit(0, amount, INITIAL_PRICE * 2, ALICE, ALICE); depository.deposit(0, amount, INITIAL_PRICE * 2, ALICE);
depository.deposit(0, amount, INITIAL_PRICE * 2, ALICE, ALICE); depository.deposit(0, amount, INITIAL_PRICE * 2, ALICE);
depository.deposit(0, amount, INITIAL_PRICE * 2, ALICE, ALICE); depository.deposit(0, amount, INITIAL_PRICE * 2, ALICE);
skip(DEPOSIT_INTERVAL); skip(DEPOSIT_INTERVAL);
@ -405,7 +405,7 @@ contract GhostBondDepositoryTest is Test {
vm.startPrank(ALICE); vm.startPrank(ALICE);
assertEq(ghst.balanceOf(ALICE), 0); assertEq(ghst.balanceOf(ALICE), 0);
(uint256 expectedPayout, ,) = depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE); (uint256 expectedPayout, ,) = depository.deposit(0, amount, INITIAL_PRICE, ALICE);
assertEq(ghst.balanceOf(address(depository)), 0); assertEq(ghst.balanceOf(address(depository)), 0);
skip(DEPOSIT_INTERVAL); skip(DEPOSIT_INTERVAL);
@ -437,7 +437,7 @@ contract GhostBondDepositoryTest is Test {
uint256 price = depository.marketPrice(0); uint256 price = depository.marketPrice(0);
uint256 amount = maxPayout * price; uint256 amount = maxPayout * price;
vm.prank(ALICE); vm.prank(ALICE);
depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE); depository.deposit(0, amount, INITIAL_PRICE, ALICE);
skip(DEPOSIT_INTERVAL); skip(DEPOSIT_INTERVAL);
uint256 newPrice = depository.marketPrice(0); uint256 newPrice = depository.marketPrice(0);
assertEq(newPrice < INITIAL_PRICE, true); assertEq(newPrice < INITIAL_PRICE, true);
@ -460,7 +460,7 @@ contract GhostBondDepositoryTest is Test {
assertEq(IERC20(address(weth)).balanceOf(address(treasury)), 0); assertEq(IERC20(address(weth)).balanceOf(address(treasury)), 0);
vm.prank(ALICE); vm.prank(ALICE);
depository.deposit{value: halfAmount}(1, halfAmount, halfAmount, ALICE, ALICE); depository.deposit{value: halfAmount}(1, halfAmount, halfAmount, ALICE);
assertEq(ALICE.balance, halfAmount); assertEq(ALICE.balance, halfAmount);
assertEq(IERC20(address(weth)).balanceOf(address(treasury)), halfAmount); assertEq(IERC20(address(weth)).balanceOf(address(treasury)), halfAmount);
} }
@ -473,7 +473,7 @@ contract GhostBondDepositoryTest is Test {
assertEq(IERC20(address(weth)).balanceOf(address(treasury)), 0); assertEq(IERC20(address(weth)).balanceOf(address(treasury)), 0);
vm.prank(ALICE); vm.prank(ALICE);
(uint256 payout,,) = depository.deposit{value: halfAmount}(1, 0, halfAmount, ALICE, ALICE); (uint256 payout,,) = depository.deposit{value: halfAmount}(1, 0, halfAmount, ALICE);
assertEq(ALICE.balance, halfAmount); assertEq(ALICE.balance, halfAmount);
assertEq(IERC20(address(weth)).balanceOf(address(treasury)), halfAmount); assertEq(IERC20(address(weth)).balanceOf(address(treasury)), halfAmount);
assertEq(payout, 0); assertEq(payout, 0);
@ -488,7 +488,7 @@ contract GhostBondDepositoryTest is Test {
vm.expectRevert(); vm.expectRevert();
vm.prank(ALICE); vm.prank(ALICE);
depository.deposit{value: halfAmount}(1, amount, amount, ALICE, ALICE); depository.deposit{value: halfAmount}(1, amount, amount, ALICE);
assertEq(ALICE.balance, amount); assertEq(ALICE.balance, amount);
assertEq(IERC20(address(weth)).balanceOf(address(treasury)), 0); assertEq(IERC20(address(weth)).balanceOf(address(treasury)), 0);
@ -502,7 +502,7 @@ contract GhostBondDepositoryTest is Test {
uint256 i; uint256 i;
for (; i < 8; ) { for (; i < 8; ) {
vm.prank(ALICE); vm.prank(ALICE);
depository.deposit{value: aliceAmountUsed}(1, aliceAmountUsed, type(uint256).max, ALICE, ALICE); depository.deposit{value: aliceAmountUsed}(1, aliceAmountUsed, type(uint256).max, ALICE);
unchecked { ++i; } unchecked { ++i; }
} }
@ -519,7 +519,7 @@ contract GhostBondDepositoryTest is Test {
i = 0; i = 0;
for (; i < 7; ) { for (; i < 7; ) {
vm.prank(BOB); vm.prank(BOB);
depository.deposit{value: bobAmountUsed}(1, bobAmountUsed, type(uint256).max, BOB, BOB); depository.deposit{value: bobAmountUsed}(1, bobAmountUsed, type(uint256).max, BOB);
unchecked { ++i; } unchecked { ++i; }
} }

View File

@ -6,21 +6,152 @@ import {Gatekeeper} from "../../src/Gatekeeper.sol";
import {FullMath} from "../../src/libraries/FullMath.sol"; 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";
contract MockGovernance is Test {
address public immutable GATEKEEPER;
address public immutable DUMMY_ADDRESS;
bool public auditCalled;
enum STATUS {
RESERVEDEPOSITOR,
RESERVETOKEN,
LIQUIDITYDEPOSITOR,
LIQUIDITYTOKEN,
REWARDMANAGER
}
constructor(address gatekeeper, address dummyAddress) {
GATEKEEPER = gatekeeper;
DUMMY_ADDRESS = dummyAddress;
auditCalled = false;
}
function setDistributor(address distributor) external view {
require(msg.sender == GATEKEEPER);
assertEq(distributor, DUMMY_ADDRESS);
}
function setWarmupPeriod(uint256 warmupPeriod) external view {
require(msg.sender == GATEKEEPER);
assertEq(warmupPeriod, 69);
}
function setBounty(uint256 bounty) external view {
require(msg.sender == GATEKEEPER);
assertEq(bounty, 69);
}
function setAdjustment(uint256 rate, uint256 target, bool add) external view {
require(msg.sender == GATEKEEPER);
assertEq(rate, 69);
assertEq(target, 420);
assertEq(add, true);
}
function updateGatekeeperAddress(address gatekeeper) external view {
require(msg.sender == GATEKEEPER);
assertEq(gatekeeper, DUMMY_ADDRESS);
}
function addPool(address pool) external view {
require(msg.sender == GATEKEEPER);
assertEq(pool, DUMMY_ADDRESS);
}
function removePool(uint256 index) external view {
require(msg.sender == GATEKEEPER);
assertEq(index, 1337);
}
function close(uint256 id) external view {
require(msg.sender == GATEKEEPER);
assertEq(id, 420);
}
function create(
uint256[3] calldata market,
uint256[2] calldata terms,
address quoteToken,
uint32[2] calldata intervals,
bool[2] calldata booleans
) external view {
require(msg.sender == GATEKEEPER);
assertEq(market[0], 69);
assertEq(market[1], 420);
assertEq(market[2], 1337);
assertEq(terms[0], 420);
assertEq(terms[1], 1337);
assertEq(quoteToken, DUMMY_ADDRESS);
assertEq(intervals[0], 34);
assertEq(intervals[1], 35);
assertEq(booleans[0], true);
assertEq(booleans[1], true);
}
function enable(STATUS status, address token, address calculator) external view {
require(msg.sender == GATEKEEPER);
if (status != STATUS.RESERVETOKEN) revert();
assertEq(token, DUMMY_ADDRESS);
assertEq(calculator, DUMMY_ADDRESS);
}
function disable(STATUS status, address token) external view {
require(msg.sender == GATEKEEPER);
if (status != STATUS.LIQUIDITYTOKEN) revert();
assertEq(token, DUMMY_ADDRESS);
}
function forfeitReserves(address router, uint256 liquidity, bool destroyerMode) external view {
require(msg.sender == GATEKEEPER);
assertEq(router, DUMMY_ADDRESS);
assertEq(liquidity, 420);
assertEq(destroyerMode, true);
}
function redeemReserve(address router, uint256 amount) external view {
require(msg.sender == GATEKEEPER);
assertEq(router, DUMMY_ADDRESS);
assertEq(amount, 420);
}
function withdraw(address token, uint256 amount) external view {
require(msg.sender == GATEKEEPER);
assertEq(token, DUMMY_ADDRESS);
assertEq(amount, 420);
}
function auditReserves() external {
require(msg.sender == GATEKEEPER);
auditCalled = true;
}
}
contract MockStaking is Test { contract MockStaking is Test {
Gatekeeper public gatekeeper; Gatekeeper public gatekeeper;
mapping(address => uint256) private _materializedAmounts; mapping(address => uint256) private _recalledAmounts;
constructor(uint256 existential) { constructor(uint256 existential) {
gatekeeper = new Gatekeeper(existential, address(0)); StorageHistory history = new StorageHistory();
gatekeeper = new Gatekeeper(existential, address(history));
gatekeeper.initialize(address(0));
history.setOwner(address(gatekeeper));
} }
function runGhost(bytes32 receiver, uint256 amount) external { function runGhost(bytes32 receiver, uint256 amount) external {
gatekeeper.ghost(receiver, amount); gatekeeper.ghost(receiver, amount);
} }
function runMaterialize( function runRecall(
uint256 exodusSession, uint256 exodusSession,
uint256 amount, uint256 amount,
uint256 packed, uint256 packed,
@ -31,15 +162,15 @@ contract MockStaking is Test {
} }
vm.prank(address(gatekeeper), caller); vm.prank(address(gatekeeper), caller);
gatekeeper.materialize{value: msg.value}(exodusSession, amount, packed); gatekeeper.recall{value: msg.value}(exodusSession, amount, packed);
} }
function materialize(address receiver, uint256 amount) external { function recall(address receiver, uint256 amount) external {
_materializedAmounts[receiver] += amount; _recalledAmounts[receiver] += amount;
} }
function materializedAmount(address who) external view returns (uint256) { function recalledAmount(address who) external view returns (uint256) {
return _materializedAmounts[who]; return _recalledAmounts[who];
} }
function totalReserves() external pure returns (uint256) { function totalReserves() external pure returns (uint256) {
@ -58,7 +189,9 @@ contract GatekeeperTest is Test {
address constant BOB = 0x0000000000000000000000000000000000000002; address constant BOB = 0x0000000000000000000000000000000000000002;
uint256 constant EXISTENTIAL = 1337; uint256 constant EXISTENTIAL = 1337;
uint256 constant INIT_AMOUNT = 69 * 1e18; uint256 constant INIT_AMOUNT = 69 * 1e18;
address constant DUMMY_ADDRESS = address(0x0101010101010101010101010101010101010101);
MockGovernance governance;
Gatekeeper gatekeeper; Gatekeeper gatekeeper;
MockStaking staking; MockStaking staking;
@ -68,6 +201,12 @@ contract GatekeeperTest is Test {
vm.prank(ALICE, ALICE); vm.prank(ALICE, ALICE);
staking = new MockStaking(EXISTENTIAL); staking = new MockStaking(EXISTENTIAL);
gatekeeper = staking.gatekeeper(); gatekeeper = staking.gatekeeper();
MockGovernance tempGov = new MockGovernance(address(gatekeeper), DUMMY_ADDRESS);
bytes memory govBytecode = address(tempGov).code;
vm.etch(DUMMY_ADDRESS, govBytecode);
governance = MockGovernance(DUMMY_ADDRESS);
} }
function test_correctInitialization() public view { function test_correctInitialization() public view {
@ -105,7 +244,7 @@ contract GatekeeperTest is Test {
staking.runGhost(receiver, ghostAmount); staking.runGhost(receiver, ghostAmount);
} }
function test_materializeWork(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 > 1337 && bobAmount < 1_000 ether);
vm.assume(bobCommission > 0 && bobCommission <= type(uint32).max); vm.assume(bobCommission > 0 && bobCommission <= type(uint32).max);
@ -122,14 +261,14 @@ contract GatekeeperTest is Test {
assertEq(BOB.balance, 0 ether); assertEq(BOB.balance, 0 ether);
uint256 aliceStartingNative = ALICE.balance; uint256 aliceStartingNative = ALICE.balance;
uint256 previousAliceAmount = staking.materializedAmount(ALICE); uint256 previousAliceAmount = staking.recalledAmount(ALICE);
uint256 previousBobAmount = staking.materializedAmount(BOB); uint256 previousBobAmount = staking.recalledAmount(BOB);
vm.prank(ALICE); vm.prank(ALICE);
staking.runMaterialize{value: nativeNeeded}(exodusSession, bobAmount, bobPacked, ALICE); staking.runRecall{value: nativeNeeded}(exodusSession, bobAmount, bobPacked, ALICE);
assertApproxEqAbs(previousAliceAmount + commissionAmount, staking.materializedAmount(ALICE), 1000); assertApproxEqAbs(previousAliceAmount + commissionAmount, staking.recalledAmount(ALICE), 1000);
assertApproxEqAbs(previousBobAmount + (bobAmount - commissionAmount), staking.materializedAmount(BOB), 1000); assertApproxEqAbs(previousBobAmount + (bobAmount - commissionAmount), staking.recalledAmount(BOB), 1000);
assertEq(BOB.balance, nativeNeeded); assertEq(BOB.balance, nativeNeeded);
assertEq(ALICE.balance, aliceStartingNative - nativeNeeded); assertEq(ALICE.balance, aliceStartingNative - nativeNeeded);
@ -181,66 +320,43 @@ contract GatekeeperTest is Test {
// could not apply signature from DKG #2 // could not apply signature from DKG #2
vm.expectRevert(); vm.expectRevert();
gatekeeper.verify( gatekeeper.verify(
hex"49f03a6700000000000000000000000000000000000000000000000000000000000000043492bc8bb10848f1f788496ccadde7d458eac4ac2c1eca2dd2c7a506d8fa2c5b0000000000000000000000000000000000000000000000000000000000000000", hex"49f03a6700000000000000000000000000000000000000000000000000000000000000133492bc8bb10848f1f788496ccadde7d458eac4ac2c1eca2dd2c7a506d8fa2c5b0000000000000000000000000000000000000000000000000000000000000000",
hex"0496bacdcc4260a610c9ef465cc1d9f973dda7d4794a65c1d1ea8ea2bb8aa3ddb08d171f5aa267c1ddc64343b4f054424af0c61b57e1f359cd4e23876a0fa4a704", hex"0434c3b37293516c6204dc291821e19ab5b3d8afb0947a0a2dffc91600fbd3f6b9783a66887d515e774b98a63a35a08bb3d914407fd768e92020b0ebaa3c8602b3",
0x34383aacbe014a6fed8b5976ccfcdcbb93cef7e41a6634f934432d9ede912c14 0x71cd57e697af70f84a960cc94fcbae5889efbf919df410282a04c6d19f92609e
); );
// genesis validators 7 validators, DKG #1 // genesis validators 7 validators, DKG #1
// exodus session #2 // exodus session #11
gatekeeper.verify( gatekeeper.verify(
hex"49f03a6700000000000000000000000000000000000000000000000000000000000000024e8c1fe96d6737cdabbcc96501d6656b9d0b659b3a528ef507f2d52bef29e1570000000000000000000000000000000000000000000000000000000000000000", hex"49f03a67000000000000000000000000000000000000000000000000000000000000000b4e8c1fe96d6737cdabbcc96501d6656b9d0b659b3a528ef507f2d52bef29e1570000000000000000000000000000000000000000000000000000000000000000",
hex"04460e7dce1ebd4cc7527ba80852b6fcdbf8810da3b2173c2f3b3da63206a44407f79d5b5b4c3adcdcce2404c7488240873babb856f065fa551caa6dddea68ca06", hex"04b51db09ec2f4bebdb65c5352efa75f8d07088408e3ff907c74b0d7928a40dbe06f864364a3e6d219849ca8e7d47e6c66f8a18ca9f4fa4752df13bb6c2eae1172",
0x7f6ac70e1e3f3ddd7101a439e7da6f8622a0c8cc38ce42ce81d911d8ad23af90 0x072e625bdd3ff7d09f2bf896b5e261efa007f8d81a9e2c828525ec74bd3ee08d
); );
(publicKey, parity, session) = gatekeeper.latestPublicKeyInfo(); (publicKey, parity, session) = gatekeeper.latestPublicKeyInfo();
assert(publicKey != prevPublicKey); assert(publicKey != prevPublicKey);
assertEq(parity, prevParity); assertEq(parity, prevParity);
assertEq(session, 2); assertEq(session, 11);
prevPublicKey = publicKey; prevPublicKey = publicKey;
prevParity = parity; prevParity = parity;
// genesis validators 5 validators, DKG #2 // genesis validators 5 validators, DKG #2
// exodus session #4 // exodus session #19
gatekeeper.verify( gatekeeper.verify(
hex"49f03a6700000000000000000000000000000000000000000000000000000000000000043492bc8bb10848f1f788496ccadde7d458eac4ac2c1eca2dd2c7a506d8fa2c5b0000000000000000000000000000000000000000000000000000000000000000", hex"49f03a6700000000000000000000000000000000000000000000000000000000000000133492bc8bb10848f1f788496ccadde7d458eac4ac2c1eca2dd2c7a506d8fa2c5b0000000000000000000000000000000000000000000000000000000000000000",
hex"0496bacdcc4260a610c9ef465cc1d9f973dda7d4794a65c1d1ea8ea2bb8aa3ddb08d171f5aa267c1ddc64343b4f054424af0c61b57e1f359cd4e23876a0fa4a704", hex"0434c3b37293516c6204dc291821e19ab5b3d8afb0947a0a2dffc91600fbd3f6b9783a66887d515e774b98a63a35a08bb3d914407fd768e92020b0ebaa3c8602b3",
0x34383aacbe014a6fed8b5976ccfcdcbb93cef7e41a6634f934432d9ede912c14 0x71cd57e697af70f84a960cc94fcbae5889efbf919df410282a04c6d19f92609e
); );
(publicKey, parity, session) = gatekeeper.latestPublicKeyInfo(); (publicKey, parity, session) = gatekeeper.latestPublicKeyInfo();
assert(publicKey != prevPublicKey); assert(publicKey != prevPublicKey);
assertEq(parity, prevParity); assertEq(parity, prevParity);
assertEq(session, 4); assertEq(session, 19);
// could not apply DKG #0 no more
vm.expectRevert();
gatekeeper.verify(
hex"49f03a670000000000000000000000000000000000000000000000000000000000000000875cdcba4ae5494518fa2602791e667ca0998402b6a69e5b7cb4c72dba4e46690000000000000000000000000000000000000000000000000000000000000000",
hex"044d9fa18df9da04381ed256981570a6ef6a0893496ded966fc994662df931ed423b29e2737394b31d8498594caad7b77e36ab31ac79df3796a1b6f26baa5552ae",
0x24029a571fcaeadd14f4afe8be62afe3d07876ae270c3caf446ecb6cfc7c08f2
);
// could not apply DKG #1 no more uint256 aliceAmountBefore = staking.recalledAmount(ALICE);
vm.expectRevert(); uint256 bobAmountBefore = staking.recalledAmount(BOB);
gatekeeper.verify(
hex"49f03a6700000000000000000000000000000000000000000000000000000000000000024e8c1fe96d6737cdabbcc96501d6656b9d0b659b3a528ef507f2d52bef29e1570000000000000000000000000000000000000000000000000000000000000000",
hex"04460e7dce1ebd4cc7527ba80852b6fcdbf8810da3b2173c2f3b3da63206a44407f79d5b5b4c3adcdcce2404c7488240873babb856f065fa551caa6dddea68ca06",
0x7f6ac70e1e3f3ddd7101a439e7da6f8622a0c8cc38ce42ce81d911d8ad23af90
);
// could not apply DKG #2 no more
vm.expectRevert();
gatekeeper.verify(
hex"49f03a6700000000000000000000000000000000000000000000000000000000000000043492bc8bb10848f1f788496ccadde7d458eac4ac2c1eca2dd2c7a506d8fa2c5b0000000000000000000000000000000000000000000000000000000000000000",
hex"0496bacdcc4260a610c9ef465cc1d9f973dda7d4794a65c1d1ea8ea2bb8aa3ddb08d171f5aa267c1ddc64343b4f054424af0c61b57e1f359cd4e23876a0fa4a704",
0x34383aacbe014a6fed8b5976ccfcdcbb93cef7e41a6634f934432d9ede912c14
);
uint256 aliceAmountBefore = staking.materializedAmount(ALICE);
uint256 bobAmountBefore = staking.materializedAmount(BOB);
uint256 aliceBalanceBefore = ALICE.balance; uint256 aliceBalanceBefore = ALICE.balance;
uint256 bobBalanceBefore = BOB.balance; uint256 bobBalanceBefore = BOB.balance;
@ -248,20 +364,20 @@ contract GatekeeperTest is Test {
// amount: 69; commission: 0%; ALICE // amount: 69; commission: 0%; ALICE
// exodus session #1 // exodus session #1
gatekeeper.verify( gatekeeper.verify(
hex"39cd99050000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000004500000000000000000000000000000000000000010000000000007a6900000000", hex"bf06188a0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000004500000000000000000000000000000000000000010000000000007a6900000000",
hex"04bf0bcb3d42d8187c543a82ee201a221525047020ff0ac03e4d60e322d3eb6d8ab5bfdae43ea0fbf59128d201b8076727c3c63fe8a5c2b890fdb3c97a7da5f207", hex"04bccb4cd4633e2c61ca5c65006a6a09baa893be219b21509866123a8d6299f92aefe9c380bd9728dbe0b93a8042f431636d08ed191b5c04062bf14e1347059cb9",
0x08f121a1615782c83cce41d7fc2064be57fdeeae8b45f716211b3c8c873f3e4c 0xe579753d931a78b143d7e3d86e1117ccbc9d2d7357b0207ea682046a0b06d798
); );
assertEq(aliceAmountBefore + 69, staking.materializedAmount(ALICE)); assertEq(aliceAmountBefore + 69, staking.recalledAmount(ALICE));
assertEq(bobAmountBefore, staking.materializedAmount(BOB)); assertEq(bobAmountBefore, staking.recalledAmount(BOB));
assertEq(aliceBalanceBefore, ALICE.balance); assertEq(aliceBalanceBefore, ALICE.balance);
assertEq(bobBalanceBefore, BOB.balance); assertEq(bobBalanceBefore, BOB.balance);
vm.deal(ALICE, 220); vm.deal(ALICE, 220);
aliceAmountBefore = staking.materializedAmount(ALICE); aliceAmountBefore = staking.recalledAmount(ALICE);
bobAmountBefore = staking.materializedAmount(BOB); bobAmountBefore = staking.recalledAmount(BOB);
aliceBalanceBefore = ALICE.balance; aliceBalanceBefore = ALICE.balance;
bobBalanceBefore = BOB.balance; bobBalanceBefore = BOB.balance;
@ -269,17 +385,143 @@ contract GatekeeperTest is Test {
// execute bridge out, happened on DKG #1 // execute bridge out, happened on DKG #1
// amount: 420; commission: 50%; BOB // amount: 420; commission: 50%; BOB
// exodus session #3 // exodus session #12
vm.prank(ALICE, ALICE); vm.prank(ALICE, ALICE);
gatekeeper.verify{value: buyback}( gatekeeper.verify{value: buyback}(
hex"39cd9905000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000001a400000000000000000000000000000000000000020000000000007a6980000000", hex"bf06188a000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000001a400000000000000000000000000000000000000020000000000007a6980000000",
hex"04089940ed9bb8d7f3a77adfafb0d5ebc45af558ade46844856c049aa912b9d8401826e17f24c0aa625524ff4a65b430a19422b4f9754cec3b862641f17f99b1a9", hex"049f458500a78f56420380f0c6863905f1181e705f6b562eb469bd81a03676a2d9b436a53d2356ea0a5bb175ca837655fab8114c6e64cfdd47f3d36d1a945d7122",
0x9d2cf40ab0b1820858fe6f8950e2f511b4acb57b00b37bb93760978e2add4243 0x772731b359ee7ecac7830438333e4ffb61759b36610f69cef20f66f7a79a405f
); );
assertEq(aliceAmountBefore + 210, staking.materializedAmount(ALICE)); assertEq(aliceAmountBefore + 210, staking.recalledAmount(ALICE));
assertEq(bobAmountBefore + 210, staking.materializedAmount(BOB)); assertEq(bobAmountBefore + 210, staking.recalledAmount(BOB));
assertEq(aliceBalanceBefore - buyback, ALICE.balance); assertEq(aliceBalanceBefore - buyback, ALICE.balance);
assertEq(bobBalanceBefore + buyback, BOB.balance); assertEq(bobBalanceBefore + buyback, BOB.balance);
// execute setDistributor, exodusSession: 2
vm.prank(ALICE);
gatekeeper.verify(
hex"3137142600000000000000000000000000000000000000000000000000000000000000020000000000007a690101010101010101010101010101010101010101000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002475619ab5000000000000000000000000010101010101010101010101010101010101010100000000000000000000000000000000000000000000000000000000",
hex"049f222e7ec776c390c7fe96b9c2034c37f4fc71a9714588a5d98ccb50dc748c98fe375c628e7bcc4db4f204a2a168b22ecbc631616a69987ef42601ad239b2ab5",
0x515539c5b713900a4dd4b3b119b22a47f862c90fdff9da91ed3fca687ea8db06
);
// execute setWarmupPeriod, exodusSession: 3
vm.prank(ALICE);
gatekeeper.verify(
hex"3137142600000000000000000000000000000000000000000000000000000000000000030000000000007a6901010101010101010101010101010101010101010000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000024a85667c5000000000000000000000000000000000000000000000000000000000000004500000000000000000000000000000000000000000000000000000000",
hex"04c2d9b903c0bd640ff858de3518c64965e2ff07215a131c85a8febec629999cfd60970a3a99d0d04ba08a334f2aa865842ebde37407c6fee2b17f820c8104fc88",
0xe1727e132cbb7502a60bab56c5dedce1abca8130792ddd6d9ba59918766688e8
);
// execute setBounty, exodusSession: 4
vm.prank(ALICE);
gatekeeper.verify(
hex"3137142600000000000000000000000000000000000000000000000000000000000000040000000000007a69010101010101010101010101010101010101010100000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000245d87d363000000000000000000000000000000000000000000000000000000000000004500000000000000000000000000000000000000000000000000000000",
hex"04ec7d454696833a1c9c04d79be81e0dfe8a4791862f1dc5d4e908338df30621911fb182e6f204699c53ea5b11db1c855f55dfd149d4882a80afa437d80c58d9cd",
0x22d3138ed4e72f104c5d86814c1b4e6eeed39cdcf97e087effe33f860d06f133
);
// execute setAdjustment, exodusSession: 5
vm.prank(ALICE);
gatekeeper.verify(
hex"3137142600000000000000000000000000000000000000000000000000000000000000050000000000007a690101010101010101010101010101010101010101000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000006446d36ab5000000000000000000000000000000000000000000000000000000000000004500000000000000000000000000000000000000000000000000000000000001a4000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000",
hex"0484819be6cb8879179f91aaf94c1513234d9068052dd42bf067606033c2d173efe85e87acf5bc1020cab6ec67d5ec8f7b237c5f315b825b56e915e4736c2b7b62",
0x865b1e9006973e8b5b5492f2f6a0c85ee69ee7ae72f714ee076035865cd26a72
);
// execute updateGatekeeperAddress, exodusSession: 6
vm.prank(ALICE);
gatekeeper.verify(
hex"3137142600000000000000000000000000000000000000000000000000000000000000060000000000007a690101010101010101010101010101010101010101000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002484f157cd000000000000000000000000010101010101010101010101010101010101010100000000000000000000000000000000000000000000000000000000",
hex"040bfec065e85cb245d891a94b50b12ea8f91d27a90e113c0bf47f93e76c0c21380e6ff8dc05406d1e8ec34b2c90cc345fea1245c1ca3eb7ecccd8606bfb334f76",
0x15141679af9a42a9228adc686f802fe183515180bed3de3933e1622668e06796
);
// execute addPool, exodusSession: 7
vm.prank(ALICE);
gatekeeper.verify(
hex"3137142600000000000000000000000000000000000000000000000000000000000000070000000000007a6901010101010101010101010101010101010101010000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000024d914cd4b000000000000000000000000010101010101010101010101010101010101010100000000000000000000000000000000000000000000000000000000",
hex"04eb497652f4ad64ddd9223cd1af4785b37c6b2a41f8c1f54b906d595ab0d2b53305cd006238ca4eaf7eede77b3e2c0d1019e9f9d505fe80e86240e0d83051dcf2",
0xa93dcb9c9c399973a17abc3b3667e37da716bdd5e9ed9c0b99d9fc869a886700
);
// execute removePool, exodusSession: 8
vm.prank(ALICE);
gatekeeper.verify(
hex"3137142600000000000000000000000000000000000000000000000000000000000000080000000000007a6901010101010101010101010101010101010101010000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000024a38dcbd0000000000000000000000000000000000000000000000000000000000000053900000000000000000000000000000000000000000000000000000000",
hex"0446039e3eec08f356c0a6e9fdd243aed6d840c1d9d973d1496db70499061055d45faf8e945d2fb87016cdcfda040df8fcaf22fe9a466dbdd4687cbfde7c87d0d8",
0x110eedf82f6533d95a43a19de3da026c51557e552bd4c5b6f06ca94d6ab2c8db
);
// execute close, exodusSession: 9
vm.prank(ALICE);
gatekeeper.verify(
hex"3137142600000000000000000000000000000000000000000000000000000000000000090000000000007a69010101010101010101010101010101010101010100000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000240aebeb4e00000000000000000000000000000000000000000000000000000000000001a400000000000000000000000000000000000000000000000000000000",
hex"048da83909e645cf4313d00985c86bcbd24f584512f2279476767fb94829a2a9c7b95bcfeacc3bee2fe4710bfc2bcf2e72e58bcd37fc02293b660a7b70e2efd184",
0x8a28d3b7b5db6564e4abb846bc662a69d9b52ed08bd2ce2dbc6af4e86167f55e
);
// execute close, exodusSession: 10
vm.prank(ALICE);
gatekeeper.verify(
hex"31371426000000000000000000000000000000000000000000000000000000000000000a0000000000007a6901010101010101010101010101010101010101010000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000144a0139f41000000000000000000000000000000000000000000000000000000000000004500000000000000000000000000000000000000000000000000000000000001a4000000000000000000000000000000000000000000000000000000000000053900000000000000000000000000000000000000000000000000000000000001a400000000000000000000000000000000000000000000000000000000000005390000000000000000000000000101010101010101010101010101010101010101000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000230000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000",
hex"0417bd8b7509486284c1f613117c61abc39cee8a24422fce457099e430b805468be33547f272d8123e9aa6f8895babbaa3a3731eaeda7d8ad4286c9fb117034d21",
0x0b71c4d8ca7748ec35257a42f26e09aaab9fa4a03e40d50a96bfdfdce10ce7e5
);
// execute enable, exodusSession: 13
vm.prank(ALICE);
gatekeeper.verify(
hex"31371426000000000000000000000000000000000000000000000000000000000000000d0000000000007a6901010101010101010101010101010101010101010000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064e4e33ef800000000000000000000000000000000000000000000000000000000000000010000000000000000000000000101010101010101010101010101010101010101000000000000000000000000010101010101010101010101010101010101010100000000000000000000000000000000000000000000000000000000",
hex"04f34417b947a4da097ca9596838430bf40587339fb42a09210ceb0dbbcfd98a99b1e8fd8f4d2301ee5b5d304e1d8b8e8b81ac14688f1e7de41d65c09a65975f01",
0xa4084ea553df2f808c86437547550be910554d41d33a5d66a4643c9792c4f56f
);
// execute disable, exodusSession: 14
vm.prank(ALICE);
gatekeeper.verify(
hex"31371426000000000000000000000000000000000000000000000000000000000000000e0000000000007a6901010101010101010101010101010101010101010000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044529918310000000000000000000000000000000000000000000000000000000000000003000000000000000000000000010101010101010101010101010101010101010100000000000000000000000000000000000000000000000000000000",
hex"04f94dad10974c9f0b9307444dc4c660f1f169b25fd3ed9fd563df74714e73c2d4e92b3429505989327d449d9a68e6cce0f557574c92c0ca7bc8126dd710094e9e",
0xb6b75ed763f4d06b9d7a7557ca8d095df656ca25db74645fb47a0b45d27ec501
);
// execute forfeitReserves, exodusSession: 15
vm.prank(ALICE);
gatekeeper.verify(
hex"31371426000000000000000000000000000000000000000000000000000000000000000f0000000000007a6901010101010101010101010101010101010101010000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064f0de236c000000000000000000000000010101010101010101010101010101010101010100000000000000000000000000000000000000000000000000000000000001a4000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000",
hex"04af06146f4ca63ef11b82ad891d8e03525a11922316a7dd3f71ff4b6aa5ee6793d29e6af2355d26bc173bb52c1f5eb7d8b279ed40bef2d1cd521da33517b6d3cd",
0x6343b047bb8fe39200758de1e874ddfaf296e1f9259f0b720a070a69e7a3ee08
);
// execute redeemReserve, exodusSession: 16
vm.prank(ALICE);
gatekeeper.verify(
hex"3137142600000000000000000000000000000000000000000000000000000000000000100000000000007a690101010101010101010101010101010101010101000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000004482a21577000000000000000000000000010101010101010101010101010101010101010100000000000000000000000000000000000000000000000000000000000001a400000000000000000000000000000000000000000000000000000000",
hex"041ff894074000f2d934cf3b136165766044b177e69de4c36fac4d1265b855e1a5b4b7b2b25400fbf4d5eb4619039ffc95555db216991d3050bb6fd966f6e3eeab",
0x7d31ebf1e510616e541795b6448d387d2119b3fdd0124e5116c53726b6b1cc15
);
// execute withdraw, exodusSession: 17
vm.prank(ALICE);
gatekeeper.verify(
hex"3137142600000000000000000000000000000000000000000000000000000000000000110000000000007a6901010101010101010101010101010101010101010000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044f3fef3a3000000000000000000000000010101010101010101010101010101010101010100000000000000000000000000000000000000000000000000000000000001a400000000000000000000000000000000000000000000000000000000",
hex"04f31030cf8563dd976d1a823a84e43ebdcc13bb5e5e0d7a2ff372006c71e6ab3d4d0770771682dcd6eff9e13fb3d113b484d7384d08f247760b07149dca1aa234",
0x06aad271d98e69d1ff18bf39e6713f347aba0df04d99bc4a9304b780431d12ae
);
// execute auditReserves, exodusSession: 18
vm.prank(ALICE);
gatekeeper.verify(
hex"3137142600000000000000000000000000000000000000000000000000000000000000120000000000007a69010101010101010101010101010101010101010100000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000042b7ce50000000000000000000000000000000000000000000000000000000000",
hex"049e5d54fd32dcec64b20943a61763dc760b1e3e9d31948780e2193480d904984c0cdaa8074196c06104557b1a04de6aeeda135e0c3413192f9e4331f3b7447ff2",
0x43ed5301b16b4b2bc91c28dcfbaecc9949a7fc8fe5c57dfef6229f62493da6d2
);
address storageHistory = gatekeeper.storageHistory();
for (uint256 exodusSession; exodusSession < 19; exodusSession++) {
assert(IStorageHistory(storageHistory).isTransactionExecuted(exodusSession));
}
assert(IStorageHistory(storageHistory).isTransactionExecuted(19));
} }
} }

View File

@ -4,36 +4,47 @@ import {Test} from "forge-std/Test.sol";
import {Gatekeeper} from "../../src/Gatekeeper.sol"; import {Gatekeeper} from "../../src/Gatekeeper.sol";
import {IStorageHistory} from "../../src/interfaces/IStorageHistory.sol"; import {IStorageHistory} from "../../src/interfaces/IStorageHistory.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";
contract MockStaking is Test { contract MockStaking is Test {
Gatekeeper public gatekeeper; Gatekeeper public gatekeeper;
mapping(address => uint256) private _materializedAmounts; mapping(address => uint256) private _recalledAmounts;
constructor(uint256 existential) { constructor(uint256 existential) {
gatekeeper = new Gatekeeper(existential, address(0)); StorageHistory history = new StorageHistory();
gatekeeper = new Gatekeeper(existential, address(history));
gatekeeper.initialize(address(0));
history.setOwner(address(gatekeeper));
} }
function redoGatekeeper(uint256 existential) external { function redoGatekeeper(uint256 existential) external {
gatekeeper = new Gatekeeper(existential, address(gatekeeper)); Gatekeeper newGatekeeper = new Gatekeeper(existential, address(0));
newGatekeeper.initialize(address(gatekeeper));
address storageHistory = IGatekeeper(gatekeeper).storageHistory();
IStorageHistory(storageHistory).setOwner(address(newGatekeeper));
gatekeeper = newGatekeeper;
} }
function runGhost(bytes32 receiver, uint256 amount) external { function runGhost(bytes32 receiver, uint256 amount) external {
gatekeeper.ghost(receiver, amount); gatekeeper.ghost(receiver, amount);
} }
function runMaterialize(uint256 exodusSession, uint256 amount, uint256 packed) external { function runRecall(uint256 exodusSession, uint256 amount, uint256 packed) external {
vm.prank(address(gatekeeper)); vm.prank(address(gatekeeper));
gatekeeper.materialize(exodusSession, amount, packed); gatekeeper.recall(exodusSession, amount, packed);
} }
function materialize(address receiver, uint256 amount) external { function recall(address receiver, uint256 amount) external {
_materializedAmounts[receiver] += amount; _recalledAmounts[receiver] += amount;
} }
function materializedAmount(address who) external view returns (uint256) { function recalledAmount(address who) external view returns (uint256) {
return _materializedAmounts[who]; return _recalledAmounts[who];
} }
} }
@ -86,10 +97,10 @@ contract GatekeeperStorageHistoryTest is Test {
} else { } else {
// forge-lint: disable-next-line(unsafe-typecast) // forge-lint: disable-next-line(unsafe-typecast)
uint256 packed = RequestPacking.pack(0, uint64(block.chainid), BOB); uint256 packed = RequestPacking.pack(0, uint64(block.chainid), BOB);
uint256 previousAmount = staking.materializedAmount(BOB); uint256 previousAmount = staking.recalledAmount(BOB);
staking.runMaterialize(exodusSession, amountToMaterialize, packed); staking.runRecall(exodusSession, amountToMaterialize, packed);
assertEq(previousAmount + amountToMaterialize, staking.materializedAmount(BOB)); assertEq(previousAmount + amountToMaterialize, staking.recalledAmount(BOB));
// forge-lint: disable-next-line(unsafe-typecast) // forge-lint: disable-next-line(unsafe-typecast)
amountOut += uint104(amountToMaterialize); amountOut += uint104(amountToMaterialize);
@ -104,7 +115,7 @@ contract GatekeeperStorageHistoryTest is Test {
function test_inheritanceWorksForHistoricalStorge() public { function test_inheritanceWorksForHistoricalStorge() public {
uint256 exodusSession = 69; uint256 exodusSession = 69;
uint256 packed = RequestPacking.pack(0, uint64(block.chainid), BOB); uint256 packed = RequestPacking.pack(0, uint64(block.chainid), BOB);
staking.runMaterialize(exodusSession, INIT_AMOUNT, packed); staking.runRecall(exodusSession, INIT_AMOUNT, packed);
staking.runGhost(bytes32(abi.encodePacked(ALICE)), INIT_AMOUNT); staking.runGhost(bytes32(abi.encodePacked(ALICE)), INIT_AMOUNT);
address prevStorageHistory = gatekeeper.storageHistory(); address prevStorageHistory = gatekeeper.storageHistory();

View File

@ -7,13 +7,17 @@ import {Hashes} from "../../src/libraries/Hashes.sol";
import {Checkpoints} from "../../src/libraries/Checkpoints.sol"; import {Checkpoints} from "../../src/libraries/Checkpoints.sol";
import {IGatekeeper} from "../../src/interfaces/IGatekeeper.sol"; import {IGatekeeper} from "../../src/interfaces/IGatekeeper.sol";
import {IStorageHistory} from "../../src/interfaces/IStorageHistory.sol"; import {IStorageHistory} from "../../src/interfaces/IStorageHistory.sol";
import {StorageHistory} from "../../src/types/StorageHistory.sol";
contract MockStaking { contract MockStaking {
GatekeeperVerification public gatekeeper; GatekeeperWeaver public gatekeeper;
address public governor; address public governor;
constructor(uint256 existential) { constructor(uint256 existential) {
gatekeeper = new GatekeeperVerification(existential, address(0)); StorageHistory history = new StorageHistory();
gatekeeper = new GatekeeperWeaver(existential, address(history));
gatekeeper.initialize(address(0));
history.setOwner(address(gatekeeper));
governor = msg.sender; governor = msg.sender;
} }
@ -22,20 +26,24 @@ contract MockStaking {
return gatekeeper.ghost(receiver, amount); return gatekeeper.ghost(receiver, amount);
} }
function createNewGatekeeper(uint256 existential, address previousGatekeeper) external { function createNewGatekeeper(uint256 existential) external {
require(msg.sender == governor); require(msg.sender == governor);
GatekeeperVerification newGatekeeper = new GatekeeperVerification(existential, address(gatekeeper));
address storageHistory = IGatekeeper(previousGatekeeper).storageHistory(); GatekeeperWeaver newGatekeeper = new GatekeeperWeaver(existential, address(0));
newGatekeeper.initialize(address(gatekeeper));
address storageHistory = IGatekeeper(gatekeeper).storageHistory();
IStorageHistory(storageHistory).setOwner(address(newGatekeeper)); IStorageHistory(storageHistory).setOwner(address(newGatekeeper));
gatekeeper = newGatekeeper; gatekeeper = newGatekeeper;
} }
} }
contract GatekeeperVerification is Gatekeeper { 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 previousWeaver) Gatekeeper(existential, previousWeaver) {} constructor(uint256 existential, address storageHistory) Gatekeeper(existential, storageHistory) {}
function filledEntries(uint256 session) public view returns (uint256) { function filledEntries(uint256 session) public view returns (uint256) {
return _filledEntries[session]; return _filledEntries[session];
@ -111,7 +119,7 @@ contract GatekeeperWeaverTest is Test {
uint256 constant AMOUNT = 1 * 1e7; uint256 constant AMOUNT = 1 * 1e7;
MockStaking staking; MockStaking staking;
GatekeeperVerification gatekeeper; GatekeeperWeaver gatekeeper;
function setUp() public { function setUp() public {
vm.prank(ALICE); vm.prank(ALICE);
@ -156,7 +164,7 @@ contract GatekeeperWeaverTest is Test {
vm.roll(block.number + 420); vm.roll(block.number + 420);
vm.prank(ALICE); vm.prank(ALICE);
staking.createNewGatekeeper(EXISTENTIAL, address(gatekeeper)); staking.createNewGatekeeper(EXISTENTIAL);
gatekeeper = staking.gatekeeper(); gatekeeper = staking.gatekeeper();
uint256 finalSession = gatekeeper.currentWeavingSession(); uint256 finalSession = gatekeeper.currentWeavingSession();

View File

@ -9,10 +9,13 @@ import {GhostAuthority} from "../../src/GhostAuthority.sol";
import {GhostDistributor} from "../../src/StakingDistributor.sol"; import {GhostDistributor} from "../../src/StakingDistributor.sol";
import {GhostTreasury} from "../../src/Treasury.sol"; import {GhostTreasury} from "../../src/Treasury.sol";
import {GhostStaking} from "../../src/Staking.sol"; import {GhostStaking} from "../../src/Staking.sol";
import {Gatekeeper} from "../../src/Gatekeeper.sol";
import {ERC20Mock} from "../../src/mocks/ERC20Mock.sol"; import {ERC20Mock} from "../../src/mocks/ERC20Mock.sol";
import {GhostBondingCalculator} from "../../src/StandardBondingCalculator.sol"; import {GhostBondingCalculator} from "../../src/StandardBondingCalculator.sol";
import {ITreasury} from "../../src/interfaces/ITreasury.sol"; import {ITreasury} from "../../src/interfaces/ITreasury.sol";
import {IGatekeeper} from "../../src/interfaces/IGatekeeper.sol";
import {IStorageHistory} from "../../src/interfaces/IStorageHistory.sol";
import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol"; import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
contract RebaseBatcher { contract RebaseBatcher {
@ -588,9 +591,25 @@ contract StakingTest is Test {
function test_GOVERNORCouldSetGatekeeper() public { function test_GOVERNORCouldSetGatekeeper() public {
address previousGatekeeper = staking.gatekeeper(); address previousGatekeeper = staking.gatekeeper();
address storageHistory = IGatekeeper(previousGatekeeper).storageHistory();
vm.prank(address(previousGatekeeper));
IStorageHistory(storageHistory).trySetTransactionExecuted(34);
Gatekeeper newGatekeeper = new Gatekeeper(420, address(0));
vm.prank(GOVERNOR); vm.prank(GOVERNOR);
staking.setGatekeeperAddress(420); staking.updateGatekeeperAddress(address(newGatekeeper));
assert(staking.gatekeeper() != previousGatekeeper); assert(staking.gatekeeper() != previousGatekeeper);
assertEq(staking.gatekeeper(), address(newGatekeeper));
assertEq(newGatekeeper.previousAddress(), previousGatekeeper);
vm.prank(address(newGatekeeper));
IStorageHistory(storageHistory).trySetTransactionExecuted(35);
assert(IStorageHistory(storageHistory).isTransactionExecuted(34));
assert(IStorageHistory(storageHistory).isTransactionExecuted(35));
} }
function test_couldNotGhostTokensIfNoGhst() public { function test_couldNotGhostTokensIfNoGhst() public {

View File

@ -139,29 +139,11 @@ contract StakingDistributorTest is Test {
assertEq(distributor.bounty(), 1337); assertEq(distributor.bounty(), 1337);
} }
function test_setPools_shouldRevertIfNotGovernor(address who) public {
vm.assume(who != GOVERNOR);
address[] memory newPools = new address[](1);
newPools[0] = who;
vm.expectRevert();
vm.prank(who);
distributor.setPools(newPools);
}
function test_setPools_GOVERNORShouldSet() public {
address[] memory newPools = new address[](1);
newPools[0] = OTHER;
vm.prank(GOVERNOR);
distributor.setPools(newPools);
assertEq(distributor.pools(0), OTHER);
}
function test_removePools_shouldRevertIfNotGovernor(address who) public { function test_removePools_shouldRevertIfNotGovernor(address who) public {
vm.assume(who != GOVERNOR); vm.assume(who != GOVERNOR);
address[] memory newPools = new address[](1);
newPools[0] = OTHER;
vm.prank(GOVERNOR); vm.prank(GOVERNOR);
distributor.setPools(newPools); distributor.addPool(OTHER);
assertEq(distributor.pools(0), OTHER);
vm.expectRevert(); vm.expectRevert();
vm.prank(who); vm.prank(who);
@ -169,11 +151,11 @@ contract StakingDistributorTest is Test {
} }
function test_removePools_GOVERNORShouldRemove() public { function test_removePools_GOVERNORShouldRemove() public {
address[] memory newPools = new address[](1); vm.prank(GOVERNOR);
newPools[0] = OTHER; distributor.addPool(OTHER);
vm.startPrank(GOVERNOR);
distributor.setPools(newPools);
assertEq(distributor.pools(0), OTHER); assertEq(distributor.pools(0), OTHER);
vm.prank(GOVERNOR);
distributor.removePool(0); distributor.removePool(0);
vm.stopPrank(); vm.stopPrank();
} }

View File

@ -203,43 +203,6 @@ contract StinkyTest is Test, ERC20PermitTest, ERC20AllowanceTest, ERC20TransferT
assertEq(stnk.balanceForShares(amountToTest), amountToTest / sharesPerUnit); assertEq(stnk.balanceForShares(amountToTest), amountToTest / sharesPerUnit);
} }
function test_debt_couldBeChangedByTreasury() public {
_mintTokens(ALICE, AMOUNT);
assertEq(stnk.debtBalances(ALICE), 0);
vm.prank(TREASURY);
stnk.changeDebt(AMOUNT, ALICE, true);
assertEq(stnk.debtBalances(ALICE), AMOUNT);
vm.prank(TREASURY);
stnk.changeDebt(AMOUNT, ALICE, false);
assertEq(stnk.debtBalances(ALICE), 0);
}
function test_debt_couldNotBeChangeByArbitraryAddress(address someone) public {
vm.assume(someone != TREASURY);
_mintTokens(ALICE, AMOUNT);
assertEq(stnk.debtBalances(ALICE), 0);
vm.expectRevert();
vm.prank(someone);
stnk.changeDebt(AMOUNT, ALICE, true);
}
function test_balance_couldNotDropBelowDebt() public {
_mintTokens(ALICE, AMOUNT * 3);
vm.prank(TREASURY);
stnk.changeDebt(AMOUNT, ALICE, true);
assertEq(stnk.balanceOf(BOB), 0);
vm.prank(ALICE);
stnk.safeTransfer(BOB, AMOUNT);
assertEq(stnk.balanceOf(BOB), AMOUNT);
vm.expectRevert();
vm.prank(ALICE);
assertEq(stnk.transfer(BOB, AMOUNT * 2), false);
assertEq(stnk.balanceOf(BOB), AMOUNT);
assertEq(stnk.balanceOf(ALICE), AMOUNT * 2);
}
function test_rebase_couldNotBeDoneFromArbitraryAddress(address someone) public { function test_rebase_couldNotBeDoneFromArbitraryAddress(address someone) public {
vm.assume(someone != address(staking)); vm.assume(someone != address(staking));
vm.expectRevert(); vm.expectRevert();

View File

@ -72,55 +72,6 @@ contract GhostTreasuryTest is Test {
assertEq(reserve.balanceOf(address(treasury)), AMOUNT); assertEq(reserve.balanceOf(address(treasury)), AMOUNT);
} }
function test_withdraw_onlyIfApprovedTokenAndApprovedAddress() public {
vm.startPrank(GOVERNOR);
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, ALICE, address(0));
treasury.enable(ITreasury.STATUS.RESERVETOKEN, address(reserve), address(calculator));
vm.stopPrank();
vm.prank(ALICE);
treasury.deposit(address(reserve), AMOUNT, 0);
vm.expectRevert();
vm.prank(ALICE);
treasury.withdraw(address(reserve), AMOUNT);
vm.prank(GOVERNOR);
treasury.enable(ITreasury.STATUS.RESERVESPENDER, ALICE, address(0));
vm.prank(ALICE);
treasury.withdraw(address(reserve), AMOUNT);
assertEq(ftso.balanceOf(ALICE), 0);
assertEq(reserve.balanceOf(address(treasury)), 0);
}
function test_manage_onlyIfApprovedTokenAndApprovedAddress() public {
vm.startPrank(GOVERNOR);
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, ALICE, address(0));
treasury.enable(ITreasury.STATUS.RESERVETOKEN, address(reserve), address(calculator));
vm.stopPrank();
uint256 tokenValue = treasury.tokenValue(address(reserve), AMOUNT);
vm.prank(ALICE);
uint256 send = treasury.deposit(address(reserve), AMOUNT, tokenValue);
vm.expectRevert();
vm.prank(ALICE);
treasury.manage(address(reserve), AMOUNT);
vm.prank(GOVERNOR);
treasury.enable(ITreasury.STATUS.RESERVEMANAGER, ALICE, address(0));
vm.prank(ALICE);
treasury.manage(address(reserve), AMOUNT);
assertEq(ftso.balanceOf(ALICE), send);
assertEq(reserve.balanceOf(ALICE), AMOUNT);
assertEq(reserve.balanceOf(address(treasury)), 0);
}
function test_mint_onlyIfApprovedTokenAndApprovedAddress() public { function test_mint_onlyIfApprovedTokenAndApprovedAddress() public {
vm.startPrank(GOVERNOR); vm.startPrank(GOVERNOR);
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, ALICE, address(0)); treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, ALICE, address(0));
@ -287,39 +238,21 @@ contract GhostTreasuryTest is Test {
assertEq(liquidityValue - reserveEps <= reserves, true); assertEq(liquidityValue - reserveEps <= reserves, true);
} }
function test_randomAddressCouldNotTriggerTimelock(address who) public { function test_withdraw_onlyIfApprovedTokenAndApprovedAddress() public {
vm.assume(who != GOVERNOR); vm.startPrank(GOVERNOR);
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, ALICE, address(0));
treasury.enable(ITreasury.STATUS.RESERVETOKEN, address(reserve), address(calculator));
vm.stopPrank();
vm.prank(ALICE);
treasury.deposit(address(reserve), AMOUNT, 0);
vm.expectRevert(); vm.expectRevert();
vm.prank(who); vm.prank(ALICE);
treasury.toggleTimelock(); treasury.withdraw(address(reserve), AMOUNT);
}
function test_triggerTimelock() public {
assertEq(treasury.timelockEnabled(), false);
assertEq(treasury.onChainGovernanceTimelock(), 0);
vm.prank(GOVERNOR); vm.prank(GOVERNOR);
treasury.toggleTimelock(); treasury.withdraw(address(reserve), AMOUNT);
uint256 queuedTime = block.number + 69 * 7; assertEq(reserve.balanceOf(address(treasury)), 0);
assertEq(treasury.timelockEnabled(), false);
assertEq(treasury.onChainGovernanceTimelock(), queuedTime);
vm.roll(queuedTime + 1);
vm.prank(GOVERNOR);
treasury.toggleTimelock();
assertEq(treasury.timelockEnabled(), true);
assertEq(treasury.onChainGovernanceTimelock(), 0);
vm.prank(GOVERNOR);
treasury.toggleTimelock();
queuedTime = block.number + 69 * 7;
assertEq(treasury.timelockEnabled(), true);
assertEq(treasury.onChainGovernanceTimelock(), queuedTime);
vm.roll(queuedTime + 1);
vm.prank(GOVERNOR);
treasury.toggleTimelock();
assertEq(treasury.timelockEnabled(), false);
assertEq(treasury.onChainGovernanceTimelock(), 0);
} }
} }