From 0860792f127f885f9ffa8afc1f9d64edcb90e733 Mon Sep 17 00:00:00 2001 From: Uncle Fatso Date: Fri, 4 Sep 2026 11:22:22 +0300 Subject: [PATCH] make governance great again Signed-off-by: Uncle Fatso --- foundry.toml | 3 +- src/BondDepository.sol | 40 +-- src/Gatekeeper.sol | 78 +++-- src/Staking.sol | 24 +- src/StakingDistributor.sol | 4 - src/StinkyERC20.sol | 14 - src/Treasury.sol | 183 ++---------- src/TreasuryExtender.sol | 249 ---------------- src/interfaces/IAllocator.sol | 50 ---- src/interfaces/IBondDepository.sol | 4 +- src/interfaces/IDistributor.sol | 2 +- src/interfaces/IGatekeeper.sol | 9 +- src/interfaces/ISTNK.sol | 9 - src/interfaces/IStaking.sol | 2 +- src/interfaces/IStorageHistory.sol | 2 +- src/interfaces/ITreasury.sol | 40 +-- src/interfaces/ITreasuryExtender.sol | 71 ----- src/libraries/Hashes.sol | 9 + src/libraries/Packing.sol | 34 ++- src/mocks/WeaverMock.sol | 3 +- src/types/BaseAllocator.sol | 176 ----------- src/types/NoteKeeper.sol | 31 +- src/types/StorageHistory.sol | 22 +- src/types/Weaver.sol | 14 +- test/bonding/BondDepositorty.t.sol | 52 ++-- test/gatekeeper/Gatekeeper.t.sol | 380 +++++++++++++++++++----- test/gatekeeper/GatekeeperHistory.t.sol | 37 ++- test/gatekeeper/GatekeeperWeaver.t.sol | 26 +- test/staking/Staking.t.sol | 21 +- test/staking/StakingDistributor.t.sol | 30 +- test/tokens/Stnk.t.sol | 37 --- test/treasury/Treasury.t.sol | 93 +----- 32 files changed, 608 insertions(+), 1141 deletions(-) delete mode 100644 src/TreasuryExtender.sol delete mode 100644 src/interfaces/IAllocator.sol delete mode 100644 src/interfaces/ITreasuryExtender.sol delete mode 100644 src/types/BaseAllocator.sol diff --git a/foundry.toml b/foundry.toml index c3c0e1d..dd923b8 100644 --- a/foundry.toml +++ b/foundry.toml @@ -19,7 +19,8 @@ gas_reports = [ "GhostBondingCalculator", "GhostTreasury", "GhostGovernorExposed", - "GatekeeperVerification", + "GatekeeperWeaver", + "Gatekeeper", ] remappings = [ "@openzeppelin-contracts/=dependencies/@openzeppelin-contracts-5.0.2/", diff --git a/src/BondDepository.sol b/src/BondDepository.sol index c6497c6..cea68db 100644 --- a/src/BondDepository.sol +++ b/src/BondDepository.sol @@ -5,15 +5,20 @@ import {IERC20} from "@openzeppelin-contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol"; import {IERC20Metadata} from "@openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol"; -import {NoteKeeper} from "./types/NoteKeeper.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 {FullMath} from "./libraries/FullMath.sol"; +import {NoteKeeper} from "./types/NoteKeeper.sol"; contract GhostBondDepository is IBondDepository, NoteKeeper { using SafeERC20 for IERC20; address public immutable WETH; + address public immutable TREASURY; + address public immutable AUTHORITY; Market[] public markets; Term[] public terms; @@ -29,8 +34,10 @@ contract GhostBondDepository is IBondDepository, NoteKeeper { address staking, address treasury, address wrapped - ) NoteKeeper(authority, ftso, ghst, staking, treasury) { - _ftso.approve(staking, type(uint256).max); + ) NoteKeeper(ghst, staking, treasury) { + IERC20(ftso).approve(staking, type(uint256).max); + AUTHORITY = authority; + TREASURY = treasury; WETH = wrapped; } @@ -38,8 +45,7 @@ contract GhostBondDepository is IBondDepository, NoteKeeper { uint256 id, uint256 amount, uint256 maxPrice, - address user, - address referral + address user ) external override @@ -71,12 +77,12 @@ contract GhostBondDepository is IBondDepository, NoteKeeper { 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}(); - IERC20(WETH).safeTransfer(address(_treasury), msg.value); + IERC20(WETH).safeTransfer(TREASURY, msg.value); 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) { @@ -124,7 +130,7 @@ contract GhostBondDepository is IBondDepository, NoteKeeper { markets[id].maxPayout = uint64(FullMath.mulDiv(capacity, meta.depositInterval, timeRemaining)); uint256 targetDebt = FullMath.mulDiv(capacity, meta.length, timeRemaining); // 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); @@ -151,8 +157,8 @@ contract GhostBondDepository is IBondDepository, NoteKeeper { bool[2] calldata _booleans ) external override returns (uint256 id) { if ( - msg.sender != authority.governor() && - msg.sender != authority.policy() + msg.sender != IGhostAuthority(AUTHORITY).governor() && + msg.sender != IGhostAuthority(AUTHORITY).policy() ) revert NotGuardianOrPolicy(); uint256 secondsToConclusion = _terms[1] - block.timestamp; @@ -164,7 +170,7 @@ contract GhostBondDepository is IBondDepository, NoteKeeper { // forge-lint: disable-next-line(unsafe-typecast) uint64 maxPayout = uint64(FullMath.mulDiv(targetDebt, _intervals[0], secondsToConclusion)); 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; @@ -204,13 +210,13 @@ contract GhostBondDepository is IBondDepository, NoteKeeper { ); marketsForQuote[_quoteToken].push(id); - emit MarketCreated(id, address(_ftso), _quoteToken, _market[1]); + emit MarketCreated(id, _quoteToken, _market[1]); } function close(uint256 id) external override { if ( - msg.sender != authority.governor() && - msg.sender != authority.policy() + msg.sender != IGhostAuthority(AUTHORITY).governor() && + msg.sender != IGhostAuthority(AUTHORITY).policy() ) revert NotGuardianOrPolicy(); terms[id].conclusion = uint48(block.timestamp); @@ -231,7 +237,7 @@ contract GhostBondDepository is IBondDepository, NoteKeeper { } 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) { @@ -309,7 +315,7 @@ contract GhostBondDepository is IBondDepository, NoteKeeper { } 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) diff --git a/src/Gatekeeper.sol b/src/Gatekeeper.sol index 9ee33d6..7598bc4 100644 --- a/src/Gatekeeper.sol +++ b/src/Gatekeeper.sol @@ -10,55 +10,69 @@ import {Weaver} from "./types/Weaver.sol"; import {FullMath} from "./libraries/FullMath.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"; contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard { using Checkpoints for Checkpoints.Trace256; using RotationPacking for RotationPacking.RotationState; 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_Q = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141; uint256 private constant SHIFT_FACTOR = (2**128) % SECP256K1_N; address public override staking; + address public override deployer; address public override storageHistory; uint256 public override existentialDeposit; - address public deployer; address private _previousAddress; + bool private _initialized; Checkpoints.Trace256 private _aggregatedPublicKeys; mapping(bytes32 => uint256) private _packedRotationStates; constructor( uint256 _existentialDeposit, - address _previousGatekeeperAddress - ) Weaver(_previousGatekeeperAddress) { + address _storageHistory + ) { existentialDeposit = _existentialDeposit; + storageHistory = _storageHistory; + staking = msg.sender; + deployer = tx.origin; + } + function initialize(address _previousGatekeeperAddress) external override { if (_previousGatekeeperAddress != address(0)) { - require(_previousGatekeeperAddress != address(0)); + require(_initialized == false); 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; - deployer = tx.origin; + 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 { require(msg.sender == deployer); // TODO: uncomment line below, needed only for testing purposes // and should push to hardcoded exodusSession 0 always - // deployer = address(0); + // _deployer = address(0); // forge-lint: disable-next-line(unsafe-typecast) uint256 packedRotationState = RotationPacking.pack(parity, uint64(exodusSession)); @@ -118,7 +132,7 @@ contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard { return _insertTreeNode(receiver, amount); } - function materialize( + function recall( uint256 exodusSession, uint256 amount, uint256 packed @@ -131,24 +145,24 @@ contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard { StorageHistory(storageHistory).trySetTransactionExecuted(exodusSession); IStorageHistory(storageHistory).tryIncreaseBridgeOut(amount); - uint256 commissionAmount = FullMath.mulDiv(amount, uint256(payload.commission), COMMISSION_DIVISOR); - uint256 receiverAmount = amount - commissionAmount; + uint256 bountyAmount = FullMath.mulDiv(amount, uint256(payload.bounty), BOUNTY_DIVISOR); + 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 baseSupply = IStaking(staking).baseSupply(); - uint256 minimumNativeRequired = FullMath.mulDiv(commissionAmount, totalReserves, baseSupply); + uint256 minimumNativeRequired = FullMath.mulDiv(bountyAmount, totalReserves, baseSupply); 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 }(""); if (!sentSuccess) revert SendFailed(); } - emit Materialized(payload.receiver, amount); + emit Recalled(payload.receiver, amount); } function rotate( @@ -168,6 +182,24 @@ contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard { 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 { (bytes32 px, uint8 p) = _extractPublicKey(call); diff --git a/src/Staking.sol b/src/Staking.sol index 7b08838..c37e8f6 100644 --- a/src/Staking.sol +++ b/src/Staking.sol @@ -6,6 +6,7 @@ import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol import {GhostWarmup} from "./Warmup.sol"; import {Gatekeeper} from "./Gatekeeper.sol"; +import {StorageHistory} from "./types/StorageHistory.sol"; import {GhostAccessControlled} from "./types/GhostAccessControlled.sol"; import {ISTNK} from "./interfaces/ISTNK.sol"; @@ -60,10 +61,14 @@ contract GhostStaking is IStaking, GhostAccessControlled { }); 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); + warmup = address(newWarmup); _lastRebaseBlock = 1; } @@ -162,7 +167,7 @@ contract GhostStaking is IStaking, GhostAccessControlled { 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(); IGHST(ghst).mint(receiver, amount); } @@ -200,13 +205,14 @@ contract GhostStaking is IStaking, GhostAccessControlled { emit WarmupSet(_warmupPeriod); } - function setGatekeeperAddress(uint256 existentialDeposit) external onlyGovernor { - Gatekeeper newGatekeeper = new Gatekeeper(existentialDeposit, gatekeeper); - address storageHistory = IGatekeeper(gatekeeper).storageHistory(); - IStorageHistory(storageHistory).setOwner(address(newGatekeeper)); + function updateGatekeeperAddress(address newGatekeeper) external onlyGovernor { + IGatekeeper(newGatekeeper).initialize(gatekeeper); - gatekeeper = address(newGatekeeper); - emit GatekeeperSet(address(newGatekeeper)); + address storageHistory = IGatekeeper(gatekeeper).storageHistory(); + IStorageHistory(storageHistory).setOwner(newGatekeeper); + + gatekeeper = newGatekeeper; + emit GatekeeperSet(newGatekeeper); } function index() public view override returns (uint256) { diff --git a/src/StakingDistributor.sol b/src/StakingDistributor.sol index c0cc6f0..01b9243 100644 --- a/src/StakingDistributor.sol +++ b/src/StakingDistributor.sol @@ -85,10 +85,6 @@ contract GhostDistributor is IDistributor, GhostAccessControlled { bounty = _bounty; } - function setPools(address[] calldata _pools) external override onlyGovernor { - pools = _pools; - } - function removePool(uint256 index) external override onlyGovernor { uint256 length = pools.length; pools[index] = pools[length - 1]; diff --git a/src/StinkyERC20.sol b/src/StinkyERC20.sol index fe12a96..5fb5225 100644 --- a/src/StinkyERC20.sol +++ b/src/StinkyERC20.sol @@ -26,7 +26,6 @@ contract Stinky is ISTNK, ERC20Permit { address public ghst; address public treasury; - mapping(address => uint256) public override debtBalances; mapping(address => uint256) private _shares; mapping(address => mapping(address => uint256)) private _allowedValue; @@ -180,18 +179,6 @@ contract Stinky is ISTNK, ERC20Permit { 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( address from, address to, @@ -200,7 +187,6 @@ contract Stinky is ISTNK, ERC20Permit { uint256 sharesValue = value * _sharesPerUnit; _shares[from] = _shares[from] - sharesValue; _shares[to] = _shares[to] + sharesValue; - if (balanceOf(from) < debtBalances[from]) revert DebtExists(); emit Transfer(from, to, value); } diff --git a/src/Treasury.sol b/src/Treasury.sol index 976be52..e774516 100644 --- a/src/Treasury.sol +++ b/src/Treasury.sol @@ -15,7 +15,6 @@ import {Babylonian} from "./libraries/FixedPoint.sol"; import {FullMath} from "./libraries/FullMath.sol"; import {IFTSO} from "./interfaces/IFTSO.sol"; -import {ISTNK} from "./interfaces/ISTNK.sol"; import {IBondingCalculator} from "./interfaces/IBondingCalculator.sol"; import {ITreasury} from "./interfaces/ITreasury.sol"; import {IGhostAuthority} from "./interfaces/IGhostAuthority.sol"; @@ -29,16 +28,10 @@ contract GhostTreasury is GhostAccessControlled, ITreasury { uint256 public totalReserves; uint256 public totalDebt; uint256 public ftsoDebt; - uint256 public onChainGovernanceTimelock; - address public stnk; - bool public timelockEnabled; - - Queue[] public permissionQueue; mapping(STATUS => address[]) public registry; mapping(STATUS => mapping(address => bool)) public permissions; mapping(address => address) public bondCalculator; - mapping(address => uint256) public debtLimit; constructor( address _ftso, @@ -69,35 +62,6 @@ contract GhostTreasury is GhostAccessControlled, ITreasury { 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 { if (!permissions[STATUS.REWARDMANAGER][msg.sender]) revert NotApproved(); if (amount > excessReserves()) revert InsufficientReserves(); @@ -105,50 +69,14 @@ contract GhostTreasury is GhostAccessControlled, ITreasury { emit Minted(msg.sender, recipient, amount); } - function incurDebt(address token, uint256 amount) external 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(); - 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(); + function withdraw(address token, uint256 amount) external onlyGovernor override { if (!permissions[STATUS.RESERVETOKEN][token]) revert NotAccepted(); - IERC20(token).safeTransferFrom(msg.sender, address(this), amount); - uint256 value = tokenValue(token, amount); - ISTNK(stnk).changeDebt(value, msg.sender, false); - totalDebt = totalDebt - value; - totalReserves = totalReserves + value; - emit RepayDebt(msg.sender, token, amount, value); - } - function repayDebtWithFtso(uint256 amount) external override { - if (!permissions[STATUS.RESERVEDEBTOR][msg.sender]) revert NotApproved(); - 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); + uint256 value = tokenValue(token, amount); + totalReserves = totalReserves - value; + + IERC20(token).safeTransfer(msg.sender, amount); + emit Withdrawal(token, amount, value); } function auditReserves() external { @@ -189,26 +117,17 @@ contract GhostTreasury is GhostAccessControlled, ITreasury { emit ReservesAudited(reserves); } - function setDebtLimit(address who, uint256 limit) external onlyGovernor { - debtLimit[who] = limit; - } - function enable( STATUS status, address someAddress, address calculatorAddress ) external onlyGovernor { - if (timelockEnabled) revert OnlyQueueTimelock(); - if (status == STATUS.STNK) { - stnk = someAddress; - } else { - permissions[status][someAddress] = true; - (bool registered, ) = indexInRegistry(someAddress, status); - if (!registered && (status == STATUS.LIQUIDITYTOKEN || status == STATUS.RESERVETOKEN)) { - assert(calculatorAddress != address(0)); - registry[status].push(someAddress); - bondCalculator[someAddress] = calculatorAddress; - } + permissions[status][someAddress] = true; + (bool registered, ) = indexInRegistry(someAddress, status); + if (!registered && (status == STATUS.LIQUIDITYTOKEN || status == STATUS.RESERVETOKEN)) { + assert(calculatorAddress != address(0)); + registry[status].push(someAddress); + bondCalculator[someAddress] = calculatorAddress; } emit Permissioned(someAddress, status, true); } @@ -222,12 +141,6 @@ contract GhostTreasury is GhostAccessControlled, ITreasury { 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( address router, uint256 liquidity, @@ -257,7 +170,7 @@ contract GhostTreasury is GhostAccessControlled, ITreasury { } function redeemReserve( - address router, // could be an issue + address router, uint256 amount ) external onlyGovernor { address weth = IUniswapV2Router01(router).WETH(); @@ -308,70 +221,6 @@ contract GhostTreasury is GhostAccessControlled, ITreasury { 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) { address[] memory reserveTokens = registry[STATUS.RESERVETOKEN]; return IBondingCalculator(bondCalculator[reserveTokens[0]]).fraction(); @@ -396,4 +245,10 @@ contract GhostTreasury is GhostAccessControlled, ITreasury { function baseSupply() external view override returns (uint256) { 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; + } } diff --git a/src/TreasuryExtender.sol b/src/TreasuryExtender.sol deleted file mode 100644 index cb1f2d9..0000000 --- a/src/TreasuryExtender.sol +++ /dev/null @@ -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); - } - } -} diff --git a/src/interfaces/IAllocator.sol b/src/interfaces/IAllocator.sol deleted file mode 100644 index 307c4bf..0000000 --- a/src/interfaces/IAllocator.sol +++ /dev/null @@ -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); -} diff --git a/src/interfaces/IBondDepository.sol b/src/interfaces/IBondDepository.sol index 28279b1..fa28a73 100644 --- a/src/interfaces/IBondDepository.sol +++ b/src/interfaces/IBondDepository.sol @@ -12,7 +12,6 @@ interface IBondDepository { event Tuned(uint256 indexed id, uint64 oldControlVariable, uint64 newControlVariable); event MarketCreated( uint256 indexed id, - address indexed baseToken, address indexed quoteToken, uint256 initialPrice ); @@ -55,8 +54,7 @@ interface IBondDepository { uint256 _bid, uint256 _amount, uint256 _maxPrice, - address _user, - address _referral + address _user ) external payable diff --git a/src/interfaces/IDistributor.sol b/src/interfaces/IDistributor.sol index 3d6bfbe..76e534f 100644 --- a/src/interfaces/IDistributor.sol +++ b/src/interfaces/IDistributor.sol @@ -17,8 +17,8 @@ interface IDistributor { function distribute() external; function retrieveBounty() external returns (uint256); function nextRewardFor(address who) external view returns (uint256); + function setBounty(uint256 _bounty) external; - function setPools(address[] calldata _pools) external; function removePool(uint256 index) external; function addPool(address pool) external; } diff --git a/src/interfaces/IGatekeeper.sol b/src/interfaces/IGatekeeper.sol index 787e81a..c48e030 100644 --- a/src/interfaces/IGatekeeper.sol +++ b/src/interfaces/IGatekeeper.sol @@ -14,17 +14,20 @@ interface IGatekeeper { error ExecutionReverted(); 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); 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 storageHistory() external view returns (address); function existentialDeposit() external view returns (uint256); function latestPublicKeyInfo() external view returns (bytes32, uint8, uint64); function getRotationInfoAt(uint256 session) external view returns (bytes32, uint8, uint64); - 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 ghost(bytes32 receiver, uint256 amount) external returns (uint256); + function initialize(address previousAddress) external; } diff --git a/src/interfaces/ISTNK.sol b/src/interfaces/ISTNK.sol index 8637627..f6eaa9d 100644 --- a/src/interfaces/ISTNK.sol +++ b/src/interfaces/ISTNK.sol @@ -5,9 +5,6 @@ import {IERC20} from "@openzeppelin-contracts/token/ERC20/IERC20.sol"; interface ISTNK is IERC20 { error NotStakingContract(); - error NotTreasury(); - error InsufficientBalance(); - error DebtExists(); error NotInitializer(); event LogSupply(uint256 indexed epoch, uint256 totalSupply); @@ -24,10 +21,4 @@ interface ISTNK is IERC20 { function index() external view returns (uint256); function toGhst(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; } diff --git a/src/interfaces/IStaking.sol b/src/interfaces/IStaking.sol index 18a958d..83d31e7 100644 --- a/src/interfaces/IStaking.sol +++ b/src/interfaces/IStaking.sol @@ -68,7 +68,7 @@ interface IStaking { function wrap(address _to, uint256 _amount) external returns (uint256 gBalance_); function unwrap(address _to, uint256 _amount) external returns (uint256 sBalance_); 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 index() external view returns (uint256); diff --git a/src/interfaces/IStorageHistory.sol b/src/interfaces/IStorageHistory.sol index 8c57e44..555c522 100644 --- a/src/interfaces/IStorageHistory.sol +++ b/src/interfaces/IStorageHistory.sol @@ -8,7 +8,7 @@ interface IStorageHistory { uint104 amountOut; } - error NotCreator(); + error NotOwner(); error NotDeployer(); error AlreadyExecuted(); error BridgeOutImpossible(); diff --git a/src/interfaces/ITreasury.sol b/src/interfaces/ITreasury.sol index 2688e43..fe795e0 100644 --- a/src/interfaces/ITreasury.sol +++ b/src/interfaces/ITreasury.sol @@ -3,50 +3,23 @@ pragma solidity ^0.8.20; interface ITreasury { error NotApproved(); - error InvalidToken(); error NotAccepted(); + error InvalidToken(); error InsufficientReserves(); - error TreasuryExceeds(); - error OnlyQueueTimelock(); - error EmptyAddress(); - error TimelockDisabled(); - error ActionNullified(); - error ActionExecuted(); - error TimelockNotComplete(); - error AlreadyInitialized(); enum STATUS { RESERVEDEPOSITOR, - RESERVESPENDER, RESERVETOKEN, - RESERVEMANAGER, LIQUIDITYDEPOSITOR, LIQUIDITYTOKEN, - LIQUIDITYMANAGER, - RESERVEDEBTOR, - REWARDMANAGER, - STNK, - FTSODEBTOR - } - - struct Queue { - STATUS managing; - address toPermit; - address calculator; - uint256 timelockEnd; - bool nullify; - bool executed; + REWARDMANAGER } 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 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 Withdrawal(address indexed token, uint256 indexed amount, uint256 value); function deposit( address _token, @@ -54,13 +27,8 @@ interface ITreasury { uint256 _profit ) external returns (uint256); - function withdraw(address _token, uint256 _amount) external; - function repayDebtWithReserves(address _token, uint256 _amount) external; - function repayDebtWithFtso(uint256 _amount) external; + function withdraw(address token, 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 indexInRegistry(address _address, STATUS _status) external view returns (bool, uint256); function excessReserves() external view returns (uint256); diff --git a/src/interfaces/ITreasuryExtender.sol b/src/interfaces/ITreasuryExtender.sol deleted file mode 100644 index 218f9c7..0000000 --- a/src/interfaces/ITreasuryExtender.sol +++ /dev/null @@ -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); -} diff --git a/src/libraries/Hashes.sol b/src/libraries/Hashes.sol index e4ebf09..807eaef 100644 --- a/src/libraries/Hashes.sol +++ b/src/libraries/Hashes.sol @@ -16,4 +16,13 @@ library Hashes { 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) + } + } } diff --git a/src/libraries/Packing.sol b/src/libraries/Packing.sol index ff96ca4..6fc1302 100644 --- a/src/libraries/Packing.sol +++ b/src/libraries/Packing.sol @@ -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 { struct RequestPayload { - uint32 commission; + uint32 bounty; uint64 chainId; 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(chainId) << 32; - packed |= uint256(commission); + packed |= uint256(bounty); return 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) { // forge-lint: disable-next-line(unsafe-typecast) - data.commission = uint32(packed & 0xFFFFFFFF); + data.bounty = uint32(packed & 0xFFFFFFFF); // forge-lint: disable-next-line(unsafe-typecast) data.chainId = uint64((packed >> 32) & 0xFFFFFFFFFFFFFFFF); // forge-lint: disable-next-line(unsafe-typecast) diff --git a/src/mocks/WeaverMock.sol b/src/mocks/WeaverMock.sol index 8f59256..0dc435a 100644 --- a/src/mocks/WeaverMock.sol +++ b/src/mocks/WeaverMock.sol @@ -6,7 +6,8 @@ import {Weaver} from "../types/Weaver.sol"; contract WeaverMock is Weaver { address private _previousAddress; - constructor(address previousWeaver) Weaver(previousWeaver) { + constructor(address previousWeaver) { + Weaver._initialize(previousWeaver); _previousAddress = previousWeaver; } diff --git a/src/types/BaseAllocator.sol b/src/types/BaseAllocator.sol deleted file mode 100644 index 2ffab54..0000000 --- a/src/types/BaseAllocator.sol +++ /dev/null @@ -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(); - } -} diff --git a/src/types/NoteKeeper.sol b/src/types/NoteKeeper.sol index 74422f2..8608312 100644 --- a/src/types/NoteKeeper.sol +++ b/src/types/NoteKeeper.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import {FrontEndRewarder} from "./FrontEndRewarder.sol"; - import {IGHST} from "../interfaces/IGHST.sol"; import {IStaking} from "../interfaces/IStaking.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 {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 SafeERC20 for IGHST; @@ -21,36 +19,20 @@ abstract contract NoteKeeper is INoteKeeper, FrontEndRewarder { IGHST internal immutable _GHST; IStaking internal immutable _STAKING; - ITreasury internal _treasury; + ITreasury internal immutable _TREASURY; - constructor( - address _authority, - address _ftsoAddress, - address _ghstAddress, - address _stakingAddress, - address _treasuryAddress - ) FrontEndRewarder(_authority, _ftsoAddress) { + constructor(address _ghstAddress, address _stakingAddress, address _treasuryAddress) { _GHST = IGHST(_ghstAddress); _STAKING = IStaking(_stakingAddress); - _treasury = ITreasury(_treasuryAddress); + _TREASURY = ITreasury(_treasuryAddress); _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( address user, uint256 payout, uint48 expiry, - uint48 marketId, - address referral + uint48 marketId ) internal returns (uint256 index) { index = notes[user].length; _pendingIndexes[user].add(index); @@ -64,8 +46,7 @@ abstract contract NoteKeeper is INoteKeeper, FrontEndRewarder { }) ); - uint256 rewards = _giveRewards(payout, referral); - _treasury.mint(address(this), payout + rewards); + _TREASURY.mint(address(this), payout); _STAKING.stake(payout, address(this), false, true); } diff --git a/src/types/StorageHistory.sol b/src/types/StorageHistory.sol index 54a8ee3..11d9c11 100644 --- a/src/types/StorageHistory.sol +++ b/src/types/StorageHistory.sol @@ -7,15 +7,15 @@ import {BitMaps} from "@openzeppelin-contracts/utils/structs/BitMaps.sol"; contract StorageHistory is IStorageHistory { using BitMaps for BitMaps.BitMap; - address private _deployer; + address immutable private DEPLOYER; address private _currentOwner; DeploymentSnapshot private _snapshot; BitMaps.BitMap private _executedTransaction; - constructor(address deployer) { + constructor() { _currentOwner = msg.sender; - _deployer = deployer; + DEPLOYER = msg.sender; } function deploymentSnapshot() external override view returns (DeploymentSnapshot memory) { @@ -31,25 +31,25 @@ contract StorageHistory is IStorageHistory { return _executedTransaction.get(session); } + function setOwner(address newOwner) external override { + if (msg.sender != DEPLOYER) revert NotDeployer(); + _currentOwner = newOwner; + } + function trySetTransactionExecuted(uint256 session) external override { - if (msg.sender != _currentOwner) revert NotCreator(); + if (msg.sender != _currentOwner) revert NotOwner(); if (_executedTransaction.get(session)) revert AlreadyExecuted(); _executedTransaction.set(session); } - function setOwner(address newOwner) external override { - if (msg.sender != _deployer) revert NotDeployer(); - _currentOwner = newOwner; - } - 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) _snapshot.amountIn += uint104(amount); } function tryIncreaseBridgeOut(uint256 amount) external override { - if (msg.sender != _currentOwner) revert NotCreator(); + if (msg.sender != _currentOwner) revert NotOwner(); if (bridgeImbalance() < amount) revert BridgeOutImpossible(); // forge-lint: disable-next-line(unsafe-typecast) _snapshot.amountOut += uint104(amount); diff --git a/src/types/Weaver.sol b/src/types/Weaver.sol index ddba959..6b6c377 100644 --- a/src/types/Weaver.sol +++ b/src/types/Weaver.sol @@ -22,14 +22,6 @@ abstract contract Weaver is IWeaver { mapping(uint256 => mapping(uint256 => bytes32[])) internal _slotValues; 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 getSlotValues( @@ -171,4 +163,10 @@ abstract contract Weaver is IWeaver { function _computeArgumentsHash(uint256 i, uint256 a, bytes32 r) internal pure returns (bytes32) { 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; + } } diff --git a/test/bonding/BondDepositorty.t.sol b/test/bonding/BondDepositorty.t.sol index 893d39f..5f28eed 100644 --- a/test/bonding/BondDepositorty.t.sol +++ b/test/bonding/BondDepositorty.t.sol @@ -226,7 +226,7 @@ contract GhostBondDepositoryTest is Test { skip(DEPOSIT_INTERVAL); vm.prank(ALICE); - depository.deposit(0, 0, INITIAL_PRICE, ALICE, ALICE); + depository.deposit(0, 0, INITIAL_PRICE, ALICE); (, , , uint256 newTotalDebt, , ,) = depository.markets(0); assertEq(totalDebt > newTotalDebt, true); @@ -237,7 +237,7 @@ contract GhostBondDepositoryTest is Test { uint256 amount = 10_000 * 1e18; vm.prank(ALICE); - depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE); + depository.deposit(0, amount, INITIAL_PRICE, ALICE); (, , , bool active) = depository.adjustments(0); assertEq(active, true); @@ -248,10 +248,10 @@ contract GhostBondDepositoryTest is Test { uint256 amount = 10_000 * 1e18; vm.startPrank(ALICE); - depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE); + depository.deposit(0, amount, INITIAL_PRICE, ALICE); skip(DEPOSIT_INTERVAL); (uint64 change, , ,) = depository.adjustments(0); - depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE); + depository.deposit(0, amount, INITIAL_PRICE, ALICE); vm.stopPrank(); (, uint64 newCtrlVariable, , ,) = depository.terms(0); @@ -264,11 +264,11 @@ contract GhostBondDepositoryTest is Test { uint256 amount = 10_000 * 1e18; vm.startPrank(ALICE); - depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE); + depository.deposit(0, amount, INITIAL_PRICE, ALICE); (uint64 change, , ,) = depository.adjustments(0); skip(TUNE_INTERVAL / 2); - depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE); + depository.deposit(0, amount, INITIAL_PRICE, ALICE); (, uint64 newCtrlVariable, , ,) = depository.terms(0); vm.stopPrank(); @@ -282,12 +282,12 @@ contract GhostBondDepositoryTest is Test { uint256 amount = 10_000 * 1e18; vm.startPrank(ALICE); - depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE); + depository.deposit(0, amount, INITIAL_PRICE, ALICE); (uint64 change, , ,) = depository.adjustments(0); skip(TUNE_INTERVAL / 2); - depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE); + depository.deposit(0, amount, INITIAL_PRICE, ALICE); skip(TUNE_INTERVAL / 2); - depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE); + depository.deposit(0, amount, INITIAL_PRICE, ALICE); vm.stopPrank(); (, uint64 newCtrlVariable, , ,) = depository.terms(0); @@ -297,7 +297,7 @@ contract GhostBondDepositoryTest is Test { function test_shouldAllowDeposit() public { uint256 amount = 10_000 * 1e18; vm.prank(ALICE); - depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE); + depository.deposit(0, amount, INITIAL_PRICE, ALICE); uint256[] memory arr = depository.indexesFor(ALICE); assertEq(arr.length, 1); @@ -307,14 +307,14 @@ contract GhostBondDepositoryTest is Test { uint256 amount = 6_700_000 * 1e18; vm.expectRevert(); vm.prank(ALICE); - depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE); + depository.deposit(0, amount, INITIAL_PRICE, ALICE); } function test_shouldNotRedeemImmediately() public { uint256 balance = ftso.balanceOf(ALICE); uint256 amount = 10_000 * 1e18; // 10,000 vm.startPrank(ALICE); - depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE); + depository.deposit(0, amount, INITIAL_PRICE, ALICE); depository.redeemAll(ALICE, true); vm.stopPrank(); assertEq(ftso.balanceOf(ALICE), balance); @@ -328,9 +328,9 @@ contract GhostBondDepositoryTest is Test { vm.stopPrank(); vm.prank(ALICE); - depository.deposit(0, amount, type(uint256).max, ALICE, ALICE); + depository.deposit(0, amount, type(uint256).max, ALICE); vm.prank(BOB); - depository.deposit(0, amount, type(uint256).max, BOB, BOB); + depository.deposit(0, amount, type(uint256).max, BOB); skip(DEPOSIT_INTERVAL); vm.roll(block.number + 1); @@ -346,7 +346,7 @@ contract GhostBondDepositoryTest is Test { function test_shouldRedeemAfterVested() public { uint256 amount = 10_000 * 1e18; // 10,000 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); depository.redeemAll(ALICE, true); @@ -360,10 +360,10 @@ contract GhostBondDepositoryTest is Test { function test_shouldCorrectlyRedeemPartially() public { uint256 amount = 1 * 1e18; vm.startPrank(ALICE); - depository.deposit(0, amount, INITIAL_PRICE * 2, ALICE, ALICE); - depository.deposit(0, amount, INITIAL_PRICE * 2, ALICE, ALICE); - depository.deposit(0, amount, INITIAL_PRICE * 2, ALICE, 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); + depository.deposit(0, amount, INITIAL_PRICE * 2, ALICE); + depository.deposit(0, amount, INITIAL_PRICE * 2, ALICE); skip(DEPOSIT_INTERVAL); @@ -405,7 +405,7 @@ contract GhostBondDepositoryTest is Test { vm.startPrank(ALICE); 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); skip(DEPOSIT_INTERVAL); @@ -437,7 +437,7 @@ contract GhostBondDepositoryTest is Test { uint256 price = depository.marketPrice(0); uint256 amount = maxPayout * price; vm.prank(ALICE); - depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE); + depository.deposit(0, amount, INITIAL_PRICE, ALICE); skip(DEPOSIT_INTERVAL); uint256 newPrice = depository.marketPrice(0); assertEq(newPrice < INITIAL_PRICE, true); @@ -460,7 +460,7 @@ contract GhostBondDepositoryTest is Test { assertEq(IERC20(address(weth)).balanceOf(address(treasury)), 0); 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(IERC20(address(weth)).balanceOf(address(treasury)), halfAmount); } @@ -473,7 +473,7 @@ contract GhostBondDepositoryTest is Test { assertEq(IERC20(address(weth)).balanceOf(address(treasury)), 0); 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(IERC20(address(weth)).balanceOf(address(treasury)), halfAmount); assertEq(payout, 0); @@ -488,7 +488,7 @@ contract GhostBondDepositoryTest is Test { vm.expectRevert(); 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(IERC20(address(weth)).balanceOf(address(treasury)), 0); @@ -502,7 +502,7 @@ contract GhostBondDepositoryTest is Test { uint256 i; for (; i < 8; ) { 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; } } @@ -519,7 +519,7 @@ contract GhostBondDepositoryTest is Test { i = 0; for (; i < 7; ) { 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; } } diff --git a/test/gatekeeper/Gatekeeper.t.sol b/test/gatekeeper/Gatekeeper.t.sol index cf1afa8..d14e8ad 100644 --- a/test/gatekeeper/Gatekeeper.t.sol +++ b/test/gatekeeper/Gatekeeper.t.sol @@ -6,21 +6,152 @@ import {Gatekeeper} from "../../src/Gatekeeper.sol"; import {FullMath} from "../../src/libraries/FullMath.sol"; import {RequestPacking} from "../../src/libraries/Packing.sol"; import {IStorageHistory} from "../../src/interfaces/IStorageHistory.sol"; +import {StorageHistory} from "../../src/types/StorageHistory.sol"; + +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 { Gatekeeper public gatekeeper; - mapping(address => uint256) private _materializedAmounts; + mapping(address => uint256) private _recalledAmounts; 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 { gatekeeper.ghost(receiver, amount); } - function runMaterialize( + function runRecall( uint256 exodusSession, uint256 amount, uint256 packed, @@ -31,15 +162,15 @@ contract MockStaking is Test { } 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 { - _materializedAmounts[receiver] += amount; + function recall(address receiver, uint256 amount) external { + _recalledAmounts[receiver] += amount; } - function materializedAmount(address who) external view returns (uint256) { - return _materializedAmounts[who]; + function recalledAmount(address who) external view returns (uint256) { + return _recalledAmounts[who]; } function totalReserves() external pure returns (uint256) { @@ -54,11 +185,13 @@ contract MockStaking is Test { contract GatekeeperTest is Test { using RequestPacking for RequestPacking.RequestPayload; - address constant ALICE = 0x0000000000000000000000000000000000000001; - address constant BOB = 0x0000000000000000000000000000000000000002; - uint256 constant EXISTENTIAL = 1337; - uint256 constant INIT_AMOUNT = 69 * 1e18; + address constant ALICE = 0x0000000000000000000000000000000000000001; + address constant BOB = 0x0000000000000000000000000000000000000002; + uint256 constant EXISTENTIAL = 1337; + uint256 constant INIT_AMOUNT = 69 * 1e18; + address constant DUMMY_ADDRESS = address(0x0101010101010101010101010101010101010101); + MockGovernance governance; Gatekeeper gatekeeper; MockStaking staking; @@ -68,6 +201,12 @@ contract GatekeeperTest is Test { vm.prank(ALICE, ALICE); staking = new MockStaking(EXISTENTIAL); 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 { @@ -105,7 +244,7 @@ contract GatekeeperTest is Test { 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(bobCommission > 0 && bobCommission <= type(uint32).max); @@ -122,14 +261,14 @@ contract GatekeeperTest is Test { assertEq(BOB.balance, 0 ether); uint256 aliceStartingNative = ALICE.balance; - uint256 previousAliceAmount = staking.materializedAmount(ALICE); - uint256 previousBobAmount = staking.materializedAmount(BOB); + uint256 previousAliceAmount = staking.recalledAmount(ALICE); + uint256 previousBobAmount = staking.recalledAmount(BOB); 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(previousBobAmount + (bobAmount - commissionAmount), staking.materializedAmount(BOB), 1000); + assertApproxEqAbs(previousAliceAmount + commissionAmount, staking.recalledAmount(ALICE), 1000); + assertApproxEqAbs(previousBobAmount + (bobAmount - commissionAmount), staking.recalledAmount(BOB), 1000); assertEq(BOB.balance, nativeNeeded); assertEq(ALICE.balance, aliceStartingNative - nativeNeeded); @@ -181,66 +320,43 @@ contract GatekeeperTest is Test { // could not apply signature from DKG #2 vm.expectRevert(); gatekeeper.verify( - hex"49f03a6700000000000000000000000000000000000000000000000000000000000000043492bc8bb10848f1f788496ccadde7d458eac4ac2c1eca2dd2c7a506d8fa2c5b0000000000000000000000000000000000000000000000000000000000000000", - hex"0496bacdcc4260a610c9ef465cc1d9f973dda7d4794a65c1d1ea8ea2bb8aa3ddb08d171f5aa267c1ddc64343b4f054424af0c61b57e1f359cd4e23876a0fa4a704", - 0x34383aacbe014a6fed8b5976ccfcdcbb93cef7e41a6634f934432d9ede912c14 + hex"49f03a6700000000000000000000000000000000000000000000000000000000000000133492bc8bb10848f1f788496ccadde7d458eac4ac2c1eca2dd2c7a506d8fa2c5b0000000000000000000000000000000000000000000000000000000000000000", + hex"0434c3b37293516c6204dc291821e19ab5b3d8afb0947a0a2dffc91600fbd3f6b9783a66887d515e774b98a63a35a08bb3d914407fd768e92020b0ebaa3c8602b3", + 0x71cd57e697af70f84a960cc94fcbae5889efbf919df410282a04c6d19f92609e ); // genesis validators 7 validators, DKG #1 - // exodus session #2 + // exodus session #11 gatekeeper.verify( - hex"49f03a6700000000000000000000000000000000000000000000000000000000000000024e8c1fe96d6737cdabbcc96501d6656b9d0b659b3a528ef507f2d52bef29e1570000000000000000000000000000000000000000000000000000000000000000", - hex"04460e7dce1ebd4cc7527ba80852b6fcdbf8810da3b2173c2f3b3da63206a44407f79d5b5b4c3adcdcce2404c7488240873babb856f065fa551caa6dddea68ca06", - 0x7f6ac70e1e3f3ddd7101a439e7da6f8622a0c8cc38ce42ce81d911d8ad23af90 + hex"49f03a67000000000000000000000000000000000000000000000000000000000000000b4e8c1fe96d6737cdabbcc96501d6656b9d0b659b3a528ef507f2d52bef29e1570000000000000000000000000000000000000000000000000000000000000000", + hex"04b51db09ec2f4bebdb65c5352efa75f8d07088408e3ff907c74b0d7928a40dbe06f864364a3e6d219849ca8e7d47e6c66f8a18ca9f4fa4752df13bb6c2eae1172", + 0x072e625bdd3ff7d09f2bf896b5e261efa007f8d81a9e2c828525ec74bd3ee08d ); (publicKey, parity, session) = gatekeeper.latestPublicKeyInfo(); assert(publicKey != prevPublicKey); assertEq(parity, prevParity); - assertEq(session, 2); + assertEq(session, 11); prevPublicKey = publicKey; prevParity = parity; // genesis validators 5 validators, DKG #2 - // exodus session #4 + // exodus session #19 gatekeeper.verify( - hex"49f03a6700000000000000000000000000000000000000000000000000000000000000043492bc8bb10848f1f788496ccadde7d458eac4ac2c1eca2dd2c7a506d8fa2c5b0000000000000000000000000000000000000000000000000000000000000000", - hex"0496bacdcc4260a610c9ef465cc1d9f973dda7d4794a65c1d1ea8ea2bb8aa3ddb08d171f5aa267c1ddc64343b4f054424af0c61b57e1f359cd4e23876a0fa4a704", - 0x34383aacbe014a6fed8b5976ccfcdcbb93cef7e41a6634f934432d9ede912c14 + hex"49f03a6700000000000000000000000000000000000000000000000000000000000000133492bc8bb10848f1f788496ccadde7d458eac4ac2c1eca2dd2c7a506d8fa2c5b0000000000000000000000000000000000000000000000000000000000000000", + hex"0434c3b37293516c6204dc291821e19ab5b3d8afb0947a0a2dffc91600fbd3f6b9783a66887d515e774b98a63a35a08bb3d914407fd768e92020b0ebaa3c8602b3", + 0x71cd57e697af70f84a960cc94fcbae5889efbf919df410282a04c6d19f92609e ); (publicKey, parity, session) = gatekeeper.latestPublicKeyInfo(); assert(publicKey != prevPublicKey); 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 - vm.expectRevert(); - 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 aliceAmountBefore = staking.recalledAmount(ALICE); + uint256 bobAmountBefore = staking.recalledAmount(BOB); uint256 aliceBalanceBefore = ALICE.balance; uint256 bobBalanceBefore = BOB.balance; @@ -248,20 +364,20 @@ contract GatekeeperTest is Test { // amount: 69; commission: 0%; ALICE // exodus session #1 gatekeeper.verify( - hex"39cd99050000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000004500000000000000000000000000000000000000010000000000007a6900000000", - hex"04bf0bcb3d42d8187c543a82ee201a221525047020ff0ac03e4d60e322d3eb6d8ab5bfdae43ea0fbf59128d201b8076727c3c63fe8a5c2b890fdb3c97a7da5f207", - 0x08f121a1615782c83cce41d7fc2064be57fdeeae8b45f716211b3c8c873f3e4c + hex"bf06188a0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000004500000000000000000000000000000000000000010000000000007a6900000000", + hex"04bccb4cd4633e2c61ca5c65006a6a09baa893be219b21509866123a8d6299f92aefe9c380bd9728dbe0b93a8042f431636d08ed191b5c04062bf14e1347059cb9", + 0xe579753d931a78b143d7e3d86e1117ccbc9d2d7357b0207ea682046a0b06d798 ); - assertEq(aliceAmountBefore + 69, staking.materializedAmount(ALICE)); - assertEq(bobAmountBefore, staking.materializedAmount(BOB)); + assertEq(aliceAmountBefore + 69, staking.recalledAmount(ALICE)); + assertEq(bobAmountBefore, staking.recalledAmount(BOB)); assertEq(aliceBalanceBefore, ALICE.balance); assertEq(bobBalanceBefore, BOB.balance); vm.deal(ALICE, 220); - aliceAmountBefore = staking.materializedAmount(ALICE); - bobAmountBefore = staking.materializedAmount(BOB); + aliceAmountBefore = staking.recalledAmount(ALICE); + bobAmountBefore = staking.recalledAmount(BOB); aliceBalanceBefore = ALICE.balance; bobBalanceBefore = BOB.balance; @@ -269,17 +385,143 @@ contract GatekeeperTest is Test { // execute bridge out, happened on DKG #1 // amount: 420; commission: 50%; BOB - // exodus session #3 + // exodus session #12 vm.prank(ALICE, ALICE); gatekeeper.verify{value: buyback}( - hex"39cd9905000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000001a400000000000000000000000000000000000000020000000000007a6980000000", - hex"04089940ed9bb8d7f3a77adfafb0d5ebc45af558ade46844856c049aa912b9d8401826e17f24c0aa625524ff4a65b430a19422b4f9754cec3b862641f17f99b1a9", - 0x9d2cf40ab0b1820858fe6f8950e2f511b4acb57b00b37bb93760978e2add4243 + hex"bf06188a000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000001a400000000000000000000000000000000000000020000000000007a6980000000", + hex"049f458500a78f56420380f0c6863905f1181e705f6b562eb469bd81a03676a2d9b436a53d2356ea0a5bb175ca837655fab8114c6e64cfdd47f3d36d1a945d7122", + 0x772731b359ee7ecac7830438333e4ffb61759b36610f69cef20f66f7a79a405f ); - assertEq(aliceAmountBefore + 210, staking.materializedAmount(ALICE)); - assertEq(bobAmountBefore + 210, staking.materializedAmount(BOB)); + assertEq(aliceAmountBefore + 210, staking.recalledAmount(ALICE)); + assertEq(bobAmountBefore + 210, staking.recalledAmount(BOB)); assertEq(aliceBalanceBefore - buyback, ALICE.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)); } } diff --git a/test/gatekeeper/GatekeeperHistory.t.sol b/test/gatekeeper/GatekeeperHistory.t.sol index df8e8aa..0d0e2af 100644 --- a/test/gatekeeper/GatekeeperHistory.t.sol +++ b/test/gatekeeper/GatekeeperHistory.t.sol @@ -4,36 +4,47 @@ import {Test} from "forge-std/Test.sol"; import {Gatekeeper} from "../../src/Gatekeeper.sol"; import {IStorageHistory} from "../../src/interfaces/IStorageHistory.sol"; +import {IGatekeeper} from "../../src/interfaces/IGatekeeper.sol"; import {RequestPacking} from "../../src/libraries/Packing.sol"; +import {StorageHistory} from "../../src/types/StorageHistory.sol"; contract MockStaking is Test { Gatekeeper public gatekeeper; - mapping(address => uint256) private _materializedAmounts; + mapping(address => uint256) private _recalledAmounts; 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 { - 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 { 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)); - gatekeeper.materialize(exodusSession, amount, packed); + gatekeeper.recall(exodusSession, amount, packed); } - function materialize(address receiver, uint256 amount) external { - _materializedAmounts[receiver] += amount; + function recall(address receiver, uint256 amount) external { + _recalledAmounts[receiver] += amount; } - function materializedAmount(address who) external view returns (uint256) { - return _materializedAmounts[who]; + function recalledAmount(address who) external view returns (uint256) { + return _recalledAmounts[who]; } } @@ -86,10 +97,10 @@ contract GatekeeperStorageHistoryTest is Test { } else { // forge-lint: disable-next-line(unsafe-typecast) uint256 packed = RequestPacking.pack(0, uint64(block.chainid), BOB); - uint256 previousAmount = staking.materializedAmount(BOB); + uint256 previousAmount = staking.recalledAmount(BOB); - staking.runMaterialize(exodusSession, amountToMaterialize, packed); - assertEq(previousAmount + amountToMaterialize, staking.materializedAmount(BOB)); + staking.runRecall(exodusSession, amountToMaterialize, packed); + assertEq(previousAmount + amountToMaterialize, staking.recalledAmount(BOB)); // forge-lint: disable-next-line(unsafe-typecast) amountOut += uint104(amountToMaterialize); @@ -104,7 +115,7 @@ contract GatekeeperStorageHistoryTest is Test { function test_inheritanceWorksForHistoricalStorge() public { uint256 exodusSession = 69; 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); address prevStorageHistory = gatekeeper.storageHistory(); diff --git a/test/gatekeeper/GatekeeperWeaver.t.sol b/test/gatekeeper/GatekeeperWeaver.t.sol index a224365..80ffb4d 100644 --- a/test/gatekeeper/GatekeeperWeaver.t.sol +++ b/test/gatekeeper/GatekeeperWeaver.t.sol @@ -7,13 +7,17 @@ import {Hashes} from "../../src/libraries/Hashes.sol"; import {Checkpoints} from "../../src/libraries/Checkpoints.sol"; import {IGatekeeper} from "../../src/interfaces/IGatekeeper.sol"; import {IStorageHistory} from "../../src/interfaces/IStorageHistory.sol"; +import {StorageHistory} from "../../src/types/StorageHistory.sol"; contract MockStaking { - GatekeeperVerification public gatekeeper; + GatekeeperWeaver public gatekeeper; address public governor; 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; } @@ -22,20 +26,24 @@ contract MockStaking { return gatekeeper.ghost(receiver, amount); } - function createNewGatekeeper(uint256 existential, address previousGatekeeper) external { + function createNewGatekeeper(uint256 existential) external { 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)); + gatekeeper = newGatekeeper; } } -contract GatekeeperVerification is Gatekeeper { +contract GatekeeperWeaver is Gatekeeper { using Checkpoints for Checkpoints.Trace256; 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) { return _filledEntries[session]; @@ -111,7 +119,7 @@ contract GatekeeperWeaverTest is Test { uint256 constant AMOUNT = 1 * 1e7; MockStaking staking; - GatekeeperVerification gatekeeper; + GatekeeperWeaver gatekeeper; function setUp() public { vm.prank(ALICE); @@ -156,7 +164,7 @@ contract GatekeeperWeaverTest is Test { vm.roll(block.number + 420); vm.prank(ALICE); - staking.createNewGatekeeper(EXISTENTIAL, address(gatekeeper)); + staking.createNewGatekeeper(EXISTENTIAL); gatekeeper = staking.gatekeeper(); uint256 finalSession = gatekeeper.currentWeavingSession(); diff --git a/test/staking/Staking.t.sol b/test/staking/Staking.t.sol index 6e77b81..703c783 100644 --- a/test/staking/Staking.t.sol +++ b/test/staking/Staking.t.sol @@ -9,10 +9,13 @@ import {GhostAuthority} from "../../src/GhostAuthority.sol"; import {GhostDistributor} from "../../src/StakingDistributor.sol"; import {GhostTreasury} from "../../src/Treasury.sol"; import {GhostStaking} from "../../src/Staking.sol"; +import {Gatekeeper} from "../../src/Gatekeeper.sol"; import {ERC20Mock} from "../../src/mocks/ERC20Mock.sol"; import {GhostBondingCalculator} from "../../src/StandardBondingCalculator.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"; contract RebaseBatcher { @@ -588,9 +591,25 @@ contract StakingTest is Test { function test_GOVERNORCouldSetGatekeeper() public { 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); - staking.setGatekeeperAddress(420); + staking.updateGatekeeperAddress(address(newGatekeeper)); + 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 { diff --git a/test/staking/StakingDistributor.t.sol b/test/staking/StakingDistributor.t.sol index 51b7965..4c7be08 100644 --- a/test/staking/StakingDistributor.t.sol +++ b/test/staking/StakingDistributor.t.sol @@ -139,29 +139,11 @@ contract StakingDistributorTest is Test { 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 { vm.assume(who != GOVERNOR); - address[] memory newPools = new address[](1); - newPools[0] = OTHER; vm.prank(GOVERNOR); - distributor.setPools(newPools); + distributor.addPool(OTHER); + assertEq(distributor.pools(0), OTHER); vm.expectRevert(); vm.prank(who); @@ -169,11 +151,11 @@ contract StakingDistributorTest is Test { } function test_removePools_GOVERNORShouldRemove() public { - address[] memory newPools = new address[](1); - newPools[0] = OTHER; - vm.startPrank(GOVERNOR); - distributor.setPools(newPools); + vm.prank(GOVERNOR); + distributor.addPool(OTHER); assertEq(distributor.pools(0), OTHER); + + vm.prank(GOVERNOR); distributor.removePool(0); vm.stopPrank(); } diff --git a/test/tokens/Stnk.t.sol b/test/tokens/Stnk.t.sol index 0e1257b..0e47c4a 100644 --- a/test/tokens/Stnk.t.sol +++ b/test/tokens/Stnk.t.sol @@ -203,43 +203,6 @@ contract StinkyTest is Test, ERC20PermitTest, ERC20AllowanceTest, ERC20TransferT 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 { vm.assume(someone != address(staking)); vm.expectRevert(); diff --git a/test/treasury/Treasury.t.sol b/test/treasury/Treasury.t.sol index ff6e912..0d38913 100644 --- a/test/treasury/Treasury.t.sol +++ b/test/treasury/Treasury.t.sol @@ -72,55 +72,6 @@ contract GhostTreasuryTest is Test { 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 { vm.startPrank(GOVERNOR); treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, ALICE, address(0)); @@ -287,39 +238,21 @@ contract GhostTreasuryTest is Test { assertEq(liquidityValue - reserveEps <= reserves, true); } - function test_randomAddressCouldNotTriggerTimelock(address who) public { - vm.assume(who != GOVERNOR); + 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(who); - treasury.toggleTimelock(); - } - - function test_triggerTimelock() public { - assertEq(treasury.timelockEnabled(), false); - assertEq(treasury.onChainGovernanceTimelock(), 0); + vm.prank(ALICE); + treasury.withdraw(address(reserve), AMOUNT); vm.prank(GOVERNOR); - treasury.toggleTimelock(); - uint256 queuedTime = block.number + 69 * 7; - 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); + treasury.withdraw(address(reserve), AMOUNT); + assertEq(reserve.balanceOf(address(treasury)), 0); } }