make governance great again
Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
This commit is contained in:
parent
40b6d02e8d
commit
0860792f12
@ -19,7 +19,8 @@ gas_reports = [
|
|||||||
"GhostBondingCalculator",
|
"GhostBondingCalculator",
|
||||||
"GhostTreasury",
|
"GhostTreasury",
|
||||||
"GhostGovernorExposed",
|
"GhostGovernorExposed",
|
||||||
"GatekeeperVerification",
|
"GatekeeperWeaver",
|
||||||
|
"Gatekeeper",
|
||||||
]
|
]
|
||||||
remappings = [
|
remappings = [
|
||||||
"@openzeppelin-contracts/=dependencies/@openzeppelin-contracts-5.0.2/",
|
"@openzeppelin-contracts/=dependencies/@openzeppelin-contracts-5.0.2/",
|
||||||
|
|||||||
@ -5,15 +5,20 @@ import {IERC20} from "@openzeppelin-contracts/token/ERC20/IERC20.sol";
|
|||||||
import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
|
import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
|
||||||
import {IERC20Metadata} from "@openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol";
|
import {IERC20Metadata} from "@openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol";
|
||||||
|
|
||||||
import {NoteKeeper} from "./types/NoteKeeper.sol";
|
|
||||||
import {IBondDepository} from "./interfaces/IBondDepository.sol";
|
import {IBondDepository} from "./interfaces/IBondDepository.sol";
|
||||||
|
import {IGhostAuthority} from "./interfaces/IGhostAuthority.sol";
|
||||||
|
import {ITreasury} from "./interfaces/ITreasury.sol";
|
||||||
|
|
||||||
import {IWETH9} from "./interfaces/IWETH9.sol";
|
import {IWETH9} from "./interfaces/IWETH9.sol";
|
||||||
import {FullMath} from "./libraries/FullMath.sol";
|
import {FullMath} from "./libraries/FullMath.sol";
|
||||||
|
import {NoteKeeper} from "./types/NoteKeeper.sol";
|
||||||
|
|
||||||
contract GhostBondDepository is IBondDepository, NoteKeeper {
|
contract GhostBondDepository is IBondDepository, NoteKeeper {
|
||||||
using SafeERC20 for IERC20;
|
using SafeERC20 for IERC20;
|
||||||
|
|
||||||
address public immutable WETH;
|
address public immutable WETH;
|
||||||
|
address public immutable TREASURY;
|
||||||
|
address public immutable AUTHORITY;
|
||||||
|
|
||||||
Market[] public markets;
|
Market[] public markets;
|
||||||
Term[] public terms;
|
Term[] public terms;
|
||||||
@ -29,8 +34,10 @@ contract GhostBondDepository is IBondDepository, NoteKeeper {
|
|||||||
address staking,
|
address staking,
|
||||||
address treasury,
|
address treasury,
|
||||||
address wrapped
|
address wrapped
|
||||||
) NoteKeeper(authority, ftso, ghst, staking, treasury) {
|
) NoteKeeper(ghst, staking, treasury) {
|
||||||
_ftso.approve(staking, type(uint256).max);
|
IERC20(ftso).approve(staking, type(uint256).max);
|
||||||
|
AUTHORITY = authority;
|
||||||
|
TREASURY = treasury;
|
||||||
WETH = wrapped;
|
WETH = wrapped;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,8 +45,7 @@ contract GhostBondDepository is IBondDepository, NoteKeeper {
|
|||||||
uint256 id,
|
uint256 id,
|
||||||
uint256 amount,
|
uint256 amount,
|
||||||
uint256 maxPrice,
|
uint256 maxPrice,
|
||||||
address user,
|
address user
|
||||||
address referral
|
|
||||||
)
|
)
|
||||||
external
|
external
|
||||||
override
|
override
|
||||||
@ -71,12 +77,12 @@ contract GhostBondDepository is IBondDepository, NoteKeeper {
|
|||||||
|
|
||||||
emit Bond(id, amount, price);
|
emit Bond(id, amount, price);
|
||||||
|
|
||||||
index = addNote(user, payout, uint48(expiry), uint48(id), referral); // forge-lint: disable-line(unsafe-typecast)
|
index = addNote(user, payout, uint48(expiry), uint48(id)); // forge-lint: disable-line(unsafe-typecast)
|
||||||
|
|
||||||
IWETH9(WETH).deposit{value: msg.value}();
|
IWETH9(WETH).deposit{value: msg.value}();
|
||||||
IERC20(WETH).safeTransfer(address(_treasury), msg.value);
|
IERC20(WETH).safeTransfer(TREASURY, msg.value);
|
||||||
if (msg.value < amount || market.quoteToken != WETH) {
|
if (msg.value < amount || market.quoteToken != WETH) {
|
||||||
IERC20(market.quoteToken).safeTransferFrom(msg.sender, address(_treasury), amount);
|
IERC20(market.quoteToken).safeTransferFrom(msg.sender, TREASURY, amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (term.maxDebt < market.totalDebt) {
|
if (term.maxDebt < market.totalDebt) {
|
||||||
@ -124,7 +130,7 @@ contract GhostBondDepository is IBondDepository, NoteKeeper {
|
|||||||
markets[id].maxPayout = uint64(FullMath.mulDiv(capacity, meta.depositInterval, timeRemaining));
|
markets[id].maxPayout = uint64(FullMath.mulDiv(capacity, meta.depositInterval, timeRemaining));
|
||||||
uint256 targetDebt = FullMath.mulDiv(capacity, meta.length, timeRemaining);
|
uint256 targetDebt = FullMath.mulDiv(capacity, meta.length, timeRemaining);
|
||||||
// forge-lint: disable-next-line(unsafe-typecast)
|
// forge-lint: disable-next-line(unsafe-typecast)
|
||||||
uint64 newControlVariable = uint64(FullMath.mulDiv(price, _treasury.baseSupply(), targetDebt));
|
uint64 newControlVariable = uint64(FullMath.mulDiv(price, ITreasury(TREASURY).baseSupply(), targetDebt));
|
||||||
|
|
||||||
emit Tuned(id, terms[id].controlVariable, newControlVariable);
|
emit Tuned(id, terms[id].controlVariable, newControlVariable);
|
||||||
|
|
||||||
@ -151,8 +157,8 @@ contract GhostBondDepository is IBondDepository, NoteKeeper {
|
|||||||
bool[2] calldata _booleans
|
bool[2] calldata _booleans
|
||||||
) external override returns (uint256 id) {
|
) external override returns (uint256 id) {
|
||||||
if (
|
if (
|
||||||
msg.sender != authority.governor() &&
|
msg.sender != IGhostAuthority(AUTHORITY).governor() &&
|
||||||
msg.sender != authority.policy()
|
msg.sender != IGhostAuthority(AUTHORITY).policy()
|
||||||
) revert NotGuardianOrPolicy();
|
) revert NotGuardianOrPolicy();
|
||||||
|
|
||||||
uint256 secondsToConclusion = _terms[1] - block.timestamp;
|
uint256 secondsToConclusion = _terms[1] - block.timestamp;
|
||||||
@ -164,7 +170,7 @@ contract GhostBondDepository is IBondDepository, NoteKeeper {
|
|||||||
// forge-lint: disable-next-line(unsafe-typecast)
|
// forge-lint: disable-next-line(unsafe-typecast)
|
||||||
uint64 maxPayout = uint64(FullMath.mulDiv(targetDebt, _intervals[0], secondsToConclusion));
|
uint64 maxPayout = uint64(FullMath.mulDiv(targetDebt, _intervals[0], secondsToConclusion));
|
||||||
uint256 maxDebt = targetDebt + FullMath.mulDiv(targetDebt, _market[2], 1e5);
|
uint256 maxDebt = targetDebt + FullMath.mulDiv(targetDebt, _market[2], 1e5);
|
||||||
uint256 controlVariable = FullMath.mulDiv(_market[1], _treasury.baseSupply(), targetDebt);
|
uint256 controlVariable = FullMath.mulDiv(_market[1], ITreasury(TREASURY).baseSupply(), targetDebt);
|
||||||
|
|
||||||
id = markets.length;
|
id = markets.length;
|
||||||
|
|
||||||
@ -204,13 +210,13 @@ contract GhostBondDepository is IBondDepository, NoteKeeper {
|
|||||||
);
|
);
|
||||||
|
|
||||||
marketsForQuote[_quoteToken].push(id);
|
marketsForQuote[_quoteToken].push(id);
|
||||||
emit MarketCreated(id, address(_ftso), _quoteToken, _market[1]);
|
emit MarketCreated(id, _quoteToken, _market[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function close(uint256 id) external override {
|
function close(uint256 id) external override {
|
||||||
if (
|
if (
|
||||||
msg.sender != authority.governor() &&
|
msg.sender != IGhostAuthority(AUTHORITY).governor() &&
|
||||||
msg.sender != authority.policy()
|
msg.sender != IGhostAuthority(AUTHORITY).policy()
|
||||||
) revert NotGuardianOrPolicy();
|
) revert NotGuardianOrPolicy();
|
||||||
|
|
||||||
terms[id].conclusion = uint48(block.timestamp);
|
terms[id].conclusion = uint48(block.timestamp);
|
||||||
@ -231,7 +237,7 @@ contract GhostBondDepository is IBondDepository, NoteKeeper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function debtRatio(uint256 id) public view override returns (uint256) {
|
function debtRatio(uint256 id) public view override returns (uint256) {
|
||||||
return FullMath.mulDiv(currentDebt(id), 10**metadatas[id].quoteDecimals, _treasury.baseSupply());
|
return FullMath.mulDiv(currentDebt(id), 10**metadatas[id].quoteDecimals, ITreasury(TREASURY).baseSupply());
|
||||||
}
|
}
|
||||||
|
|
||||||
function currentDebt(uint256 id) public view override returns (uint256) {
|
function currentDebt(uint256 id) public view override returns (uint256) {
|
||||||
@ -309,7 +315,7 @@ contract GhostBondDepository is IBondDepository, NoteKeeper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _debtRatio(uint256 id) internal view returns (uint256) {
|
function _debtRatio(uint256 id) internal view returns (uint256) {
|
||||||
return FullMath.mulDiv(markets[id].totalDebt, 10**metadatas[id].quoteDecimals, _treasury.baseSupply());
|
return FullMath.mulDiv(markets[id].totalDebt, 10**metadatas[id].quoteDecimals, ITreasury(TREASURY).baseSupply());
|
||||||
}
|
}
|
||||||
|
|
||||||
function _controlDecay(uint256 id)
|
function _controlDecay(uint256 id)
|
||||||
|
|||||||
@ -10,55 +10,69 @@ import {Weaver} from "./types/Weaver.sol";
|
|||||||
|
|
||||||
import {FullMath} from "./libraries/FullMath.sol";
|
import {FullMath} from "./libraries/FullMath.sol";
|
||||||
import {Checkpoints} from "./libraries/Checkpoints.sol";
|
import {Checkpoints} from "./libraries/Checkpoints.sol";
|
||||||
import {RotationPacking, RequestPacking} from "./libraries/Packing.sol";
|
import {RotationPacking, RequestPacking, GovernancePacking} from "./libraries/Packing.sol";
|
||||||
import {ReentrancyGuard} from "@openzeppelin-contracts/utils/ReentrancyGuard.sol";
|
import {ReentrancyGuard} from "@openzeppelin-contracts/utils/ReentrancyGuard.sol";
|
||||||
|
|
||||||
contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard {
|
contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard {
|
||||||
using Checkpoints for Checkpoints.Trace256;
|
using Checkpoints for Checkpoints.Trace256;
|
||||||
using RotationPacking for RotationPacking.RotationState;
|
using RotationPacking for RotationPacking.RotationState;
|
||||||
using RequestPacking for RequestPacking.RequestPayload;
|
using RequestPacking for RequestPacking.RequestPayload;
|
||||||
|
using GovernancePacking for GovernancePacking.GovernancePayload;
|
||||||
|
|
||||||
uint256 private constant COMMISSION_DIVISOR = type(uint32).max;
|
uint256 private constant BOUNTY_DIVISOR = type(uint32).max;
|
||||||
uint256 private constant SECP256K1_N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141;
|
uint256 private constant SECP256K1_N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141;
|
||||||
uint256 private constant SECP256K1_Q = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141;
|
uint256 private constant SECP256K1_Q = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141;
|
||||||
uint256 private constant SHIFT_FACTOR = (2**128) % SECP256K1_N;
|
uint256 private constant SHIFT_FACTOR = (2**128) % SECP256K1_N;
|
||||||
|
|
||||||
address public override staking;
|
address public override staking;
|
||||||
|
address public override deployer;
|
||||||
address public override storageHistory;
|
address public override storageHistory;
|
||||||
uint256 public override existentialDeposit;
|
uint256 public override existentialDeposit;
|
||||||
|
|
||||||
address public deployer;
|
|
||||||
address private _previousAddress;
|
address private _previousAddress;
|
||||||
|
bool private _initialized;
|
||||||
|
|
||||||
Checkpoints.Trace256 private _aggregatedPublicKeys;
|
Checkpoints.Trace256 private _aggregatedPublicKeys;
|
||||||
mapping(bytes32 => uint256) private _packedRotationStates;
|
mapping(bytes32 => uint256) private _packedRotationStates;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
uint256 _existentialDeposit,
|
uint256 _existentialDeposit,
|
||||||
address _previousGatekeeperAddress
|
address _storageHistory
|
||||||
) Weaver(_previousGatekeeperAddress) {
|
) {
|
||||||
existentialDeposit = _existentialDeposit;
|
existentialDeposit = _existentialDeposit;
|
||||||
|
storageHistory = _storageHistory;
|
||||||
if (_previousGatekeeperAddress != address(0)) {
|
|
||||||
require(_previousGatekeeperAddress != address(0));
|
|
||||||
require(_previousGatekeeperAddress != address(this));
|
|
||||||
_previousAddress = _previousGatekeeperAddress;
|
|
||||||
|
|
||||||
storageHistory = IGatekeeper(_previousGatekeeperAddress).storageHistory();
|
|
||||||
staking = IGatekeeper(_previousGatekeeperAddress).staking();
|
|
||||||
} else {
|
|
||||||
StorageHistory newStorageHistory = new StorageHistory(msg.sender);
|
|
||||||
storageHistory = address(newStorageHistory);
|
|
||||||
staking = msg.sender;
|
staking = msg.sender;
|
||||||
deployer = tx.origin;
|
deployer = tx.origin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function initialize(address _previousGatekeeperAddress) external override {
|
||||||
|
if (_previousGatekeeperAddress != address(0)) {
|
||||||
|
require(_initialized == false);
|
||||||
|
require(_previousGatekeeperAddress != address(this));
|
||||||
|
|
||||||
|
address previousStorage = IGatekeeper(_previousGatekeeperAddress).storageHistory();
|
||||||
|
address previousStaking = IGatekeeper(_previousGatekeeperAddress).staking();
|
||||||
|
|
||||||
|
require(previousStorage != address(0));
|
||||||
|
require(previousStaking != address(0));
|
||||||
|
|
||||||
|
storageHistory = previousStorage;
|
||||||
|
staking = previousStaking;
|
||||||
|
deployer = IGatekeeper(_previousGatekeeperAddress).deployer();
|
||||||
|
Weaver._initialize(_previousGatekeeperAddress);
|
||||||
|
|
||||||
|
_previousAddress = _previousGatekeeperAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
require(msg.sender == staking);
|
||||||
|
_initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updatePublicKeyMetadata(uint256 exodusSession, bytes32 publicKey, uint8 parity) external {
|
function updatePublicKeyMetadata(uint256 exodusSession, bytes32 publicKey, uint8 parity) external {
|
||||||
require(msg.sender == deployer);
|
require(msg.sender == deployer);
|
||||||
// TODO: uncomment line below, needed only for testing purposes
|
// TODO: uncomment line below, needed only for testing purposes
|
||||||
// and should push to hardcoded exodusSession 0 always
|
// and should push to hardcoded exodusSession 0 always
|
||||||
// deployer = address(0);
|
// _deployer = address(0);
|
||||||
|
|
||||||
// forge-lint: disable-next-line(unsafe-typecast)
|
// forge-lint: disable-next-line(unsafe-typecast)
|
||||||
uint256 packedRotationState = RotationPacking.pack(parity, uint64(exodusSession));
|
uint256 packedRotationState = RotationPacking.pack(parity, uint64(exodusSession));
|
||||||
@ -118,7 +132,7 @@ contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard {
|
|||||||
return _insertTreeNode(receiver, amount);
|
return _insertTreeNode(receiver, amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
function materialize(
|
function recall(
|
||||||
uint256 exodusSession,
|
uint256 exodusSession,
|
||||||
uint256 amount,
|
uint256 amount,
|
||||||
uint256 packed
|
uint256 packed
|
||||||
@ -131,24 +145,24 @@ contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard {
|
|||||||
StorageHistory(storageHistory).trySetTransactionExecuted(exodusSession);
|
StorageHistory(storageHistory).trySetTransactionExecuted(exodusSession);
|
||||||
IStorageHistory(storageHistory).tryIncreaseBridgeOut(amount);
|
IStorageHistory(storageHistory).tryIncreaseBridgeOut(amount);
|
||||||
|
|
||||||
uint256 commissionAmount = FullMath.mulDiv(amount, uint256(payload.commission), COMMISSION_DIVISOR);
|
uint256 bountyAmount = FullMath.mulDiv(amount, uint256(payload.bounty), BOUNTY_DIVISOR);
|
||||||
uint256 receiverAmount = amount - commissionAmount;
|
uint256 receiverAmount = amount - bountyAmount;
|
||||||
|
|
||||||
IStaking(staking).materialize(payload.receiver, receiverAmount);
|
IStaking(staking).recall(payload.receiver, receiverAmount);
|
||||||
|
|
||||||
if (commissionAmount > 0) {
|
if (bountyAmount > 0) {
|
||||||
uint256 totalReserves = IStaking(staking).totalReserves();
|
uint256 totalReserves = IStaking(staking).totalReserves();
|
||||||
uint256 baseSupply = IStaking(staking).baseSupply();
|
uint256 baseSupply = IStaking(staking).baseSupply();
|
||||||
uint256 minimumNativeRequired = FullMath.mulDiv(commissionAmount, totalReserves, baseSupply);
|
uint256 minimumNativeRequired = FullMath.mulDiv(bountyAmount, totalReserves, baseSupply);
|
||||||
|
|
||||||
if (minimumNativeRequired > msg.value) revert InsufficientValue();
|
if (minimumNativeRequired > msg.value) revert InsufficientValue();
|
||||||
IStaking(staking).materialize(tx.origin, commissionAmount);
|
IStaking(staking).recall(tx.origin, bountyAmount);
|
||||||
|
|
||||||
(bool sentSuccess,) = payload.receiver.call{ value: msg.value }("");
|
(bool sentSuccess,) = payload.receiver.call{ value: msg.value }("");
|
||||||
if (!sentSuccess) revert SendFailed();
|
if (!sentSuccess) revert SendFailed();
|
||||||
}
|
}
|
||||||
|
|
||||||
emit Materialized(payload.receiver, amount);
|
emit Recalled(payload.receiver, amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
function rotate(
|
function rotate(
|
||||||
@ -168,6 +182,24 @@ contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard {
|
|||||||
emit Rotated(newPublicKey, newParity);
|
emit Rotated(newPublicKey, newParity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function govern(
|
||||||
|
uint256 exodusSession,
|
||||||
|
uint256 packed,
|
||||||
|
bytes calldata
|
||||||
|
) external override returns (bytes memory) {
|
||||||
|
if (msg.sender != address(this)) revert NotGatekeeper();
|
||||||
|
|
||||||
|
GovernancePacking.GovernancePayload memory payload = GovernancePacking.unpack(packed);
|
||||||
|
if (payload.chainId != block.chainid) revert WrongChainId();
|
||||||
|
|
||||||
|
StorageHistory(storageHistory).trySetTransactionExecuted(exodusSession);
|
||||||
|
|
||||||
|
(bool success, bytes memory data) = payload.target.call(msg.data[132:]);
|
||||||
|
if (!success) revert ExecutionReverted();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
function _verifySchnorrSignature(bytes calldata call, bytes calldata nonce, bytes32 s) internal view {
|
function _verifySchnorrSignature(bytes calldata call, bytes calldata nonce, bytes32 s) internal view {
|
||||||
(bytes32 px, uint8 p) = _extractPublicKey(call);
|
(bytes32 px, uint8 p) = _extractPublicKey(call);
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol
|
|||||||
|
|
||||||
import {GhostWarmup} from "./Warmup.sol";
|
import {GhostWarmup} from "./Warmup.sol";
|
||||||
import {Gatekeeper} from "./Gatekeeper.sol";
|
import {Gatekeeper} from "./Gatekeeper.sol";
|
||||||
|
import {StorageHistory} from "./types/StorageHistory.sol";
|
||||||
import {GhostAccessControlled} from "./types/GhostAccessControlled.sol";
|
import {GhostAccessControlled} from "./types/GhostAccessControlled.sol";
|
||||||
|
|
||||||
import {ISTNK} from "./interfaces/ISTNK.sol";
|
import {ISTNK} from "./interfaces/ISTNK.sol";
|
||||||
@ -60,10 +61,14 @@ contract GhostStaking is IStaking, GhostAccessControlled {
|
|||||||
});
|
});
|
||||||
|
|
||||||
GhostWarmup newWarmup = new GhostWarmup(_ghst);
|
GhostWarmup newWarmup = new GhostWarmup(_ghst);
|
||||||
warmup = address(newWarmup);
|
StorageHistory newHistory = new StorageHistory();
|
||||||
|
Gatekeeper newGatekeeper = new Gatekeeper(_existentialDeposit, address(newHistory));
|
||||||
|
|
||||||
|
IStorageHistory(newHistory).setOwner(address(newGatekeeper));
|
||||||
|
IGatekeeper(newGatekeeper).initialize(address(0));
|
||||||
|
|
||||||
Gatekeeper newGatekeeper = new Gatekeeper(_existentialDeposit, address(0));
|
|
||||||
gatekeeper = address(newGatekeeper);
|
gatekeeper = address(newGatekeeper);
|
||||||
|
warmup = address(newWarmup);
|
||||||
|
|
||||||
_lastRebaseBlock = 1;
|
_lastRebaseBlock = 1;
|
||||||
}
|
}
|
||||||
@ -162,7 +167,7 @@ contract GhostStaking is IStaking, GhostAccessControlled {
|
|||||||
IGatekeeper(gatekeeper).ghost(receiver, amount);
|
IGatekeeper(gatekeeper).ghost(receiver, amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
function materialize(address receiver, uint256 amount) external override {
|
function recall(address receiver, uint256 amount) external override {
|
||||||
if (gatekeeper != msg.sender) revert NotGatekeeper();
|
if (gatekeeper != msg.sender) revert NotGatekeeper();
|
||||||
IGHST(ghst).mint(receiver, amount);
|
IGHST(ghst).mint(receiver, amount);
|
||||||
}
|
}
|
||||||
@ -200,13 +205,14 @@ contract GhostStaking is IStaking, GhostAccessControlled {
|
|||||||
emit WarmupSet(_warmupPeriod);
|
emit WarmupSet(_warmupPeriod);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setGatekeeperAddress(uint256 existentialDeposit) external onlyGovernor {
|
function updateGatekeeperAddress(address newGatekeeper) external onlyGovernor {
|
||||||
Gatekeeper newGatekeeper = new Gatekeeper(existentialDeposit, gatekeeper);
|
IGatekeeper(newGatekeeper).initialize(gatekeeper);
|
||||||
address storageHistory = IGatekeeper(gatekeeper).storageHistory();
|
|
||||||
IStorageHistory(storageHistory).setOwner(address(newGatekeeper));
|
|
||||||
|
|
||||||
gatekeeper = address(newGatekeeper);
|
address storageHistory = IGatekeeper(gatekeeper).storageHistory();
|
||||||
emit GatekeeperSet(address(newGatekeeper));
|
IStorageHistory(storageHistory).setOwner(newGatekeeper);
|
||||||
|
|
||||||
|
gatekeeper = newGatekeeper;
|
||||||
|
emit GatekeeperSet(newGatekeeper);
|
||||||
}
|
}
|
||||||
|
|
||||||
function index() public view override returns (uint256) {
|
function index() public view override returns (uint256) {
|
||||||
|
|||||||
@ -85,10 +85,6 @@ contract GhostDistributor is IDistributor, GhostAccessControlled {
|
|||||||
bounty = _bounty;
|
bounty = _bounty;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setPools(address[] calldata _pools) external override onlyGovernor {
|
|
||||||
pools = _pools;
|
|
||||||
}
|
|
||||||
|
|
||||||
function removePool(uint256 index) external override onlyGovernor {
|
function removePool(uint256 index) external override onlyGovernor {
|
||||||
uint256 length = pools.length;
|
uint256 length = pools.length;
|
||||||
pools[index] = pools[length - 1];
|
pools[index] = pools[length - 1];
|
||||||
|
|||||||
@ -26,7 +26,6 @@ contract Stinky is ISTNK, ERC20Permit {
|
|||||||
address public ghst;
|
address public ghst;
|
||||||
address public treasury;
|
address public treasury;
|
||||||
|
|
||||||
mapping(address => uint256) public override debtBalances;
|
|
||||||
mapping(address => uint256) private _shares;
|
mapping(address => uint256) private _shares;
|
||||||
mapping(address => mapping(address => uint256)) private _allowedValue;
|
mapping(address => mapping(address => uint256)) private _allowedValue;
|
||||||
|
|
||||||
@ -180,18 +179,6 @@ contract Stinky is ISTNK, ERC20Permit {
|
|||||||
return balanceForShares(_INTERNAL_INDEX);
|
return balanceForShares(_INTERNAL_INDEX);
|
||||||
}
|
}
|
||||||
|
|
||||||
function changeDebt(
|
|
||||||
uint256 amount,
|
|
||||||
address debtor,
|
|
||||||
bool add
|
|
||||||
) external override {
|
|
||||||
if (msg.sender != treasury) revert NotTreasury();
|
|
||||||
uint256 debtBalance = debtBalances[debtor];
|
|
||||||
debtBalance = add ? debtBalance + amount : debtBalance - amount;
|
|
||||||
if (debtBalance > balanceOf(debtor)) revert InsufficientBalance();
|
|
||||||
debtBalances[debtor] = debtBalance;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _transferInner(
|
function _transferInner(
|
||||||
address from,
|
address from,
|
||||||
address to,
|
address to,
|
||||||
@ -200,7 +187,6 @@ contract Stinky is ISTNK, ERC20Permit {
|
|||||||
uint256 sharesValue = value * _sharesPerUnit;
|
uint256 sharesValue = value * _sharesPerUnit;
|
||||||
_shares[from] = _shares[from] - sharesValue;
|
_shares[from] = _shares[from] - sharesValue;
|
||||||
_shares[to] = _shares[to] + sharesValue;
|
_shares[to] = _shares[to] + sharesValue;
|
||||||
if (balanceOf(from) < debtBalances[from]) revert DebtExists();
|
|
||||||
emit Transfer(from, to, value);
|
emit Transfer(from, to, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
167
src/Treasury.sol
167
src/Treasury.sol
@ -15,7 +15,6 @@ import {Babylonian} from "./libraries/FixedPoint.sol";
|
|||||||
import {FullMath} from "./libraries/FullMath.sol";
|
import {FullMath} from "./libraries/FullMath.sol";
|
||||||
|
|
||||||
import {IFTSO} from "./interfaces/IFTSO.sol";
|
import {IFTSO} from "./interfaces/IFTSO.sol";
|
||||||
import {ISTNK} from "./interfaces/ISTNK.sol";
|
|
||||||
import {IBondingCalculator} from "./interfaces/IBondingCalculator.sol";
|
import {IBondingCalculator} from "./interfaces/IBondingCalculator.sol";
|
||||||
import {ITreasury} from "./interfaces/ITreasury.sol";
|
import {ITreasury} from "./interfaces/ITreasury.sol";
|
||||||
import {IGhostAuthority} from "./interfaces/IGhostAuthority.sol";
|
import {IGhostAuthority} from "./interfaces/IGhostAuthority.sol";
|
||||||
@ -29,16 +28,10 @@ contract GhostTreasury is GhostAccessControlled, ITreasury {
|
|||||||
uint256 public totalReserves;
|
uint256 public totalReserves;
|
||||||
uint256 public totalDebt;
|
uint256 public totalDebt;
|
||||||
uint256 public ftsoDebt;
|
uint256 public ftsoDebt;
|
||||||
uint256 public onChainGovernanceTimelock;
|
|
||||||
|
|
||||||
address public stnk;
|
|
||||||
bool public timelockEnabled;
|
|
||||||
|
|
||||||
Queue[] public permissionQueue;
|
|
||||||
mapping(STATUS => address[]) public registry;
|
mapping(STATUS => address[]) public registry;
|
||||||
mapping(STATUS => mapping(address => bool)) public permissions;
|
mapping(STATUS => mapping(address => bool)) public permissions;
|
||||||
mapping(address => address) public bondCalculator;
|
mapping(address => address) public bondCalculator;
|
||||||
mapping(address => uint256) public debtLimit;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
address _ftso,
|
address _ftso,
|
||||||
@ -69,35 +62,6 @@ contract GhostTreasury is GhostAccessControlled, ITreasury {
|
|||||||
emit Deposit(token, amount, value);
|
emit Deposit(token, amount, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
function withdraw(address token, uint256 amount) external override {
|
|
||||||
if (!permissions[STATUS.RESERVETOKEN][token]) revert NotAccepted();
|
|
||||||
if (!permissions[STATUS.RESERVESPENDER][msg.sender]) revert NotApproved();
|
|
||||||
|
|
||||||
uint256 value = tokenValue(token, amount);
|
|
||||||
IFTSO(ftso).burnFrom(msg.sender, value);
|
|
||||||
totalReserves = totalReserves - value;
|
|
||||||
|
|
||||||
IERC20(token).safeTransfer(msg.sender, amount);
|
|
||||||
emit Withdrawal(token, amount, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function manage(address token, uint256 amount) external override {
|
|
||||||
if (permissions[STATUS.LIQUIDITYTOKEN][token]) {
|
|
||||||
if (!permissions[STATUS.LIQUIDITYMANAGER][msg.sender]) revert NotApproved();
|
|
||||||
} else {
|
|
||||||
if (!permissions[STATUS.RESERVEMANAGER][msg.sender]) revert NotApproved();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (permissions[STATUS.RESERVETOKEN][token] || permissions[STATUS.LIQUIDITYTOKEN][token]) {
|
|
||||||
uint256 value = tokenValue(token, amount);
|
|
||||||
if (value > excessReserves()) revert InsufficientReserves();
|
|
||||||
totalReserves = totalReserves - value;
|
|
||||||
}
|
|
||||||
|
|
||||||
IERC20(token).safeTransfer(msg.sender, amount);
|
|
||||||
emit Managed(token, amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
function mint(address recipient, uint256 amount) external override {
|
function mint(address recipient, uint256 amount) external override {
|
||||||
if (!permissions[STATUS.REWARDMANAGER][msg.sender]) revert NotApproved();
|
if (!permissions[STATUS.REWARDMANAGER][msg.sender]) revert NotApproved();
|
||||||
if (amount > excessReserves()) revert InsufficientReserves();
|
if (amount > excessReserves()) revert InsufficientReserves();
|
||||||
@ -105,50 +69,14 @@ contract GhostTreasury is GhostAccessControlled, ITreasury {
|
|||||||
emit Minted(msg.sender, recipient, amount);
|
emit Minted(msg.sender, recipient, amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
function incurDebt(address token, uint256 amount) external override {
|
function withdraw(address token, uint256 amount) external onlyGovernor override {
|
||||||
uint256 value;
|
|
||||||
if (token == ftso) {
|
|
||||||
if (!permissions[STATUS.FTSODEBTOR][msg.sender]) revert NotApproved();
|
|
||||||
value = amount;
|
|
||||||
} else {
|
|
||||||
if (!permissions[STATUS.RESERVEDEBTOR][msg.sender]) revert NotApproved();
|
|
||||||
if (!permissions[STATUS.RESERVETOKEN][token]) revert NotAccepted();
|
if (!permissions[STATUS.RESERVETOKEN][token]) revert NotAccepted();
|
||||||
value = tokenValue(token, amount);
|
|
||||||
}
|
|
||||||
if (value == 0) revert InvalidToken();
|
|
||||||
|
|
||||||
ISTNK(stnk).changeDebt(value, msg.sender, true);
|
|
||||||
if (ISTNK(stnk).debtBalances(msg.sender) > debtLimit[msg.sender]) revert TreasuryExceeds();
|
|
||||||
totalDebt = totalDebt + value;
|
|
||||||
|
|
||||||
if (token == ftso) {
|
|
||||||
IFTSO(ftso).mint(msg.sender, value);
|
|
||||||
ftsoDebt = ftsoDebt + value;
|
|
||||||
} else {
|
|
||||||
totalReserves = totalReserves - value;
|
|
||||||
IERC20(token).safeTransfer(msg.sender, amount);
|
|
||||||
}
|
|
||||||
emit CreateDebt(msg.sender, token, amount, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function repayDebtWithReserves(address token, uint256 amount) external override {
|
|
||||||
if (!permissions[STATUS.RESERVEDEBTOR][msg.sender]) revert NotApproved();
|
|
||||||
if (!permissions[STATUS.RESERVETOKEN][token]) revert NotAccepted();
|
|
||||||
IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
|
|
||||||
uint256 value = tokenValue(token, amount);
|
uint256 value = tokenValue(token, amount);
|
||||||
ISTNK(stnk).changeDebt(value, msg.sender, false);
|
totalReserves = totalReserves - value;
|
||||||
totalDebt = totalDebt - value;
|
|
||||||
totalReserves = totalReserves + value;
|
|
||||||
emit RepayDebt(msg.sender, token, amount, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function repayDebtWithFtso(uint256 amount) external override {
|
IERC20(token).safeTransfer(msg.sender, amount);
|
||||||
if (!permissions[STATUS.RESERVEDEBTOR][msg.sender]) revert NotApproved();
|
emit Withdrawal(token, amount, value);
|
||||||
if (!permissions[STATUS.FTSODEBTOR][msg.sender]) revert NotApproved();
|
|
||||||
IFTSO(ftso).burnFrom(msg.sender, amount);
|
|
||||||
ISTNK(stnk).changeDebt(amount, msg.sender, false);
|
|
||||||
totalDebt = totalDebt - amount;
|
|
||||||
emit RepayDebt(msg.sender, ftso, amount, amount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function auditReserves() external {
|
function auditReserves() external {
|
||||||
@ -189,19 +117,11 @@ contract GhostTreasury is GhostAccessControlled, ITreasury {
|
|||||||
emit ReservesAudited(reserves);
|
emit ReservesAudited(reserves);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setDebtLimit(address who, uint256 limit) external onlyGovernor {
|
|
||||||
debtLimit[who] = limit;
|
|
||||||
}
|
|
||||||
|
|
||||||
function enable(
|
function enable(
|
||||||
STATUS status,
|
STATUS status,
|
||||||
address someAddress,
|
address someAddress,
|
||||||
address calculatorAddress
|
address calculatorAddress
|
||||||
) external onlyGovernor {
|
) external onlyGovernor {
|
||||||
if (timelockEnabled) revert OnlyQueueTimelock();
|
|
||||||
if (status == STATUS.STNK) {
|
|
||||||
stnk = someAddress;
|
|
||||||
} else {
|
|
||||||
permissions[status][someAddress] = true;
|
permissions[status][someAddress] = true;
|
||||||
(bool registered, ) = indexInRegistry(someAddress, status);
|
(bool registered, ) = indexInRegistry(someAddress, status);
|
||||||
if (!registered && (status == STATUS.LIQUIDITYTOKEN || status == STATUS.RESERVETOKEN)) {
|
if (!registered && (status == STATUS.LIQUIDITYTOKEN || status == STATUS.RESERVETOKEN)) {
|
||||||
@ -209,7 +129,6 @@ contract GhostTreasury is GhostAccessControlled, ITreasury {
|
|||||||
registry[status].push(someAddress);
|
registry[status].push(someAddress);
|
||||||
bondCalculator[someAddress] = calculatorAddress;
|
bondCalculator[someAddress] = calculatorAddress;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
emit Permissioned(someAddress, status, true);
|
emit Permissioned(someAddress, status, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,12 +141,6 @@ contract GhostTreasury is GhostAccessControlled, ITreasury {
|
|||||||
emit Permissioned(toDisable, status, false);
|
emit Permissioned(toDisable, status, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _quantityToBeSwapped(uint256 xa, uint256 x1) internal pure returns (uint256) {
|
|
||||||
uint256 y1 = x1 * 1994 / 1000;
|
|
||||||
uint256 y2 = Babylonian.sqrt(y1**2 + 4 * xa * x1 * 997 / 1000);
|
|
||||||
return (y2 - y1) * 1000 / 1994;
|
|
||||||
}
|
|
||||||
|
|
||||||
function forfeitReserves(
|
function forfeitReserves(
|
||||||
address router,
|
address router,
|
||||||
uint256 liquidity,
|
uint256 liquidity,
|
||||||
@ -257,7 +170,7 @@ contract GhostTreasury is GhostAccessControlled, ITreasury {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function redeemReserve(
|
function redeemReserve(
|
||||||
address router, // could be an issue
|
address router,
|
||||||
uint256 amount
|
uint256 amount
|
||||||
) external onlyGovernor {
|
) external onlyGovernor {
|
||||||
address weth = IUniswapV2Router01(router).WETH();
|
address weth = IUniswapV2Router01(router).WETH();
|
||||||
@ -308,70 +221,6 @@ contract GhostTreasury is GhostAccessControlled, ITreasury {
|
|||||||
return (false, 0);
|
return (false, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function queueTimelock(
|
|
||||||
STATUS status,
|
|
||||||
address someAddress,
|
|
||||||
address calculatorAddress
|
|
||||||
) external onlyGovernor {
|
|
||||||
if (someAddress == address(0)) revert EmptyAddress();
|
|
||||||
if (!timelockEnabled) revert TimelockDisabled();
|
|
||||||
|
|
||||||
uint256 timelock = block.number + blocksNeededForQueue;
|
|
||||||
if (status == STATUS.RESERVEMANAGER || status == STATUS.LIQUIDITYMANAGER) {
|
|
||||||
timelock = block.number + (blocksNeededForQueue * 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
permissionQueue.push(
|
|
||||||
Queue({
|
|
||||||
managing: status,
|
|
||||||
toPermit: someAddress,
|
|
||||||
calculator: calculatorAddress,
|
|
||||||
timelockEnd: timelock,
|
|
||||||
nullify: false,
|
|
||||||
executed: false
|
|
||||||
})
|
|
||||||
);
|
|
||||||
emit PermissionQueued(status, someAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
function execute(uint256 idx) external {
|
|
||||||
if (timelockEnabled) revert TimelockDisabled();
|
|
||||||
|
|
||||||
Queue storage info = permissionQueue[idx];
|
|
||||||
|
|
||||||
if (info.nullify) revert ActionNullified();
|
|
||||||
if (info.executed) revert ActionExecuted();
|
|
||||||
if (info.timelockEnd > block.number) revert TimelockNotComplete();
|
|
||||||
|
|
||||||
if (info.managing == STATUS.STNK) {
|
|
||||||
stnk = info.toPermit;
|
|
||||||
} else {
|
|
||||||
permissions[info.managing][info.toPermit] = true;
|
|
||||||
(bool registered, ) = indexInRegistry(info.toPermit, info.managing);
|
|
||||||
if (!registered && (info.managing == STATUS.LIQUIDITYTOKEN || info.managing == STATUS.RESERVETOKEN)) {
|
|
||||||
assert(info.calculator != address(0));
|
|
||||||
registry[info.managing].push(info.toPermit);
|
|
||||||
bondCalculator[info.toPermit] = info.calculator;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
permissionQueue[idx].executed = true;
|
|
||||||
emit Permissioned(info.toPermit, info.managing, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
function nullify(uint256 idx) external onlyGovernor {
|
|
||||||
permissionQueue[idx].nullify = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleTimelock() external onlyGovernor {
|
|
||||||
if (onChainGovernanceTimelock > 0 && onChainGovernanceTimelock <= block.number) {
|
|
||||||
timelockEnabled = !timelockEnabled;
|
|
||||||
onChainGovernanceTimelock = 0;
|
|
||||||
} else {
|
|
||||||
onChainGovernanceTimelock = block.number + (blocksNeededForQueue * 7);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function originalCoefficient() external view returns (uint256) {
|
function originalCoefficient() external view returns (uint256) {
|
||||||
address[] memory reserveTokens = registry[STATUS.RESERVETOKEN];
|
address[] memory reserveTokens = registry[STATUS.RESERVETOKEN];
|
||||||
return IBondingCalculator(bondCalculator[reserveTokens[0]]).fraction();
|
return IBondingCalculator(bondCalculator[reserveTokens[0]]).fraction();
|
||||||
@ -396,4 +245,10 @@ contract GhostTreasury is GhostAccessControlled, ITreasury {
|
|||||||
function baseSupply() external view override returns (uint256) {
|
function baseSupply() external view override returns (uint256) {
|
||||||
return IFTSO(ftso).totalSupply() - ftsoDebt;
|
return IFTSO(ftso).totalSupply() - ftsoDebt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _quantityToBeSwapped(uint256 xa, uint256 x1) internal pure returns (uint256) {
|
||||||
|
uint256 y1 = x1 * 1994 / 1000;
|
||||||
|
uint256 y2 = Babylonian.sqrt(y1**2 + 4 * xa * x1 * 997 / 1000);
|
||||||
|
return (y2 - y1) * 1000 / 1994;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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);
|
|
||||||
}
|
|
||||||
@ -12,7 +12,6 @@ interface IBondDepository {
|
|||||||
event Tuned(uint256 indexed id, uint64 oldControlVariable, uint64 newControlVariable);
|
event Tuned(uint256 indexed id, uint64 oldControlVariable, uint64 newControlVariable);
|
||||||
event MarketCreated(
|
event MarketCreated(
|
||||||
uint256 indexed id,
|
uint256 indexed id,
|
||||||
address indexed baseToken,
|
|
||||||
address indexed quoteToken,
|
address indexed quoteToken,
|
||||||
uint256 initialPrice
|
uint256 initialPrice
|
||||||
);
|
);
|
||||||
@ -55,8 +54,7 @@ interface IBondDepository {
|
|||||||
uint256 _bid,
|
uint256 _bid,
|
||||||
uint256 _amount,
|
uint256 _amount,
|
||||||
uint256 _maxPrice,
|
uint256 _maxPrice,
|
||||||
address _user,
|
address _user
|
||||||
address _referral
|
|
||||||
)
|
)
|
||||||
external
|
external
|
||||||
payable
|
payable
|
||||||
|
|||||||
@ -17,8 +17,8 @@ interface IDistributor {
|
|||||||
function distribute() external;
|
function distribute() external;
|
||||||
function retrieveBounty() external returns (uint256);
|
function retrieveBounty() external returns (uint256);
|
||||||
function nextRewardFor(address who) external view returns (uint256);
|
function nextRewardFor(address who) external view returns (uint256);
|
||||||
|
|
||||||
function setBounty(uint256 _bounty) external;
|
function setBounty(uint256 _bounty) external;
|
||||||
function setPools(address[] calldata _pools) external;
|
|
||||||
function removePool(uint256 index) external;
|
function removePool(uint256 index) external;
|
||||||
function addPool(address pool) external;
|
function addPool(address pool) external;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,17 +14,20 @@ interface IGatekeeper {
|
|||||||
error ExecutionReverted();
|
error ExecutionReverted();
|
||||||
|
|
||||||
event Ghosted(bytes32 indexed receiver, uint256 indexed amount);
|
event Ghosted(bytes32 indexed receiver, uint256 indexed amount);
|
||||||
event Materialized(address indexed receiver, uint256 indexed amount);
|
event Recalled(address indexed receiver, uint256 indexed amount);
|
||||||
event Rotated(bytes32 indexed aggregatedPublicKey, uint8 indexed parity);
|
event Rotated(bytes32 indexed aggregatedPublicKey, uint8 indexed parity);
|
||||||
|
|
||||||
function staking() external view returns (address);
|
function staking() external view returns (address);
|
||||||
function storageHistory() external view returns (address);
|
function deployer() external view returns (address);
|
||||||
function ghostedSupply() external view returns (uint256);
|
function ghostedSupply() external view returns (uint256);
|
||||||
|
function storageHistory() external view returns (address);
|
||||||
function existentialDeposit() external view returns (uint256);
|
function existentialDeposit() external view returns (uint256);
|
||||||
function latestPublicKeyInfo() external view returns (bytes32, uint8, uint64);
|
function latestPublicKeyInfo() external view returns (bytes32, uint8, uint64);
|
||||||
function getRotationInfoAt(uint256 session) external view returns (bytes32, uint8, uint64);
|
function getRotationInfoAt(uint256 session) external view returns (bytes32, uint8, uint64);
|
||||||
|
|
||||||
function materialize(uint256 session, uint256 amount, uint256 payload) external payable;
|
function govern(uint256 session, uint256 payload, bytes calldata call) external returns (bytes calldata);
|
||||||
|
function recall(uint256 session, uint256 amount, uint256 payload) external payable;
|
||||||
function rotate(uint256 session, bytes32 publicKey, uint8 parity) external;
|
function rotate(uint256 session, bytes32 publicKey, uint8 parity) external;
|
||||||
function ghost(bytes32 receiver, uint256 amount) external returns (uint256);
|
function ghost(bytes32 receiver, uint256 amount) external returns (uint256);
|
||||||
|
function initialize(address previousAddress) external;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,9 +5,6 @@ import {IERC20} from "@openzeppelin-contracts/token/ERC20/IERC20.sol";
|
|||||||
|
|
||||||
interface ISTNK is IERC20 {
|
interface ISTNK is IERC20 {
|
||||||
error NotStakingContract();
|
error NotStakingContract();
|
||||||
error NotTreasury();
|
|
||||||
error InsufficientBalance();
|
|
||||||
error DebtExists();
|
|
||||||
error NotInitializer();
|
error NotInitializer();
|
||||||
|
|
||||||
event LogSupply(uint256 indexed epoch, uint256 totalSupply);
|
event LogSupply(uint256 indexed epoch, uint256 totalSupply);
|
||||||
@ -24,10 +21,4 @@ interface ISTNK is IERC20 {
|
|||||||
function index() external view returns (uint256);
|
function index() external view returns (uint256);
|
||||||
function toGhst(uint256 amount) external view returns (uint256);
|
function toGhst(uint256 amount) external view returns (uint256);
|
||||||
function fromGhst(uint256 amount) external view returns (uint256);
|
function fromGhst(uint256 amount) external view returns (uint256);
|
||||||
function debtBalances(address _address) external view returns (uint256);
|
|
||||||
function changeDebt(
|
|
||||||
uint256 amount,
|
|
||||||
address debtor,
|
|
||||||
bool add
|
|
||||||
) external;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -68,7 +68,7 @@ interface IStaking {
|
|||||||
function wrap(address _to, uint256 _amount) external returns (uint256 gBalance_);
|
function wrap(address _to, uint256 _amount) external returns (uint256 gBalance_);
|
||||||
function unwrap(address _to, uint256 _amount) external returns (uint256 sBalance_);
|
function unwrap(address _to, uint256 _amount) external returns (uint256 sBalance_);
|
||||||
function ghost(bytes32 receiver, uint256 amount) external;
|
function ghost(bytes32 receiver, uint256 amount) external;
|
||||||
function materialize(address receiver, uint256 amount) external;
|
function recall(address receiver, uint256 amount) external;
|
||||||
function rebase() external returns (uint256);
|
function rebase() external returns (uint256);
|
||||||
|
|
||||||
function index() external view returns (uint256);
|
function index() external view returns (uint256);
|
||||||
|
|||||||
@ -8,7 +8,7 @@ interface IStorageHistory {
|
|||||||
uint104 amountOut;
|
uint104 amountOut;
|
||||||
}
|
}
|
||||||
|
|
||||||
error NotCreator();
|
error NotOwner();
|
||||||
error NotDeployer();
|
error NotDeployer();
|
||||||
error AlreadyExecuted();
|
error AlreadyExecuted();
|
||||||
error BridgeOutImpossible();
|
error BridgeOutImpossible();
|
||||||
|
|||||||
@ -3,50 +3,23 @@ pragma solidity ^0.8.20;
|
|||||||
|
|
||||||
interface ITreasury {
|
interface ITreasury {
|
||||||
error NotApproved();
|
error NotApproved();
|
||||||
error InvalidToken();
|
|
||||||
error NotAccepted();
|
error NotAccepted();
|
||||||
|
error InvalidToken();
|
||||||
error InsufficientReserves();
|
error InsufficientReserves();
|
||||||
error TreasuryExceeds();
|
|
||||||
error OnlyQueueTimelock();
|
|
||||||
error EmptyAddress();
|
|
||||||
error TimelockDisabled();
|
|
||||||
error ActionNullified();
|
|
||||||
error ActionExecuted();
|
|
||||||
error TimelockNotComplete();
|
|
||||||
error AlreadyInitialized();
|
|
||||||
|
|
||||||
enum STATUS {
|
enum STATUS {
|
||||||
RESERVEDEPOSITOR,
|
RESERVEDEPOSITOR,
|
||||||
RESERVESPENDER,
|
|
||||||
RESERVETOKEN,
|
RESERVETOKEN,
|
||||||
RESERVEMANAGER,
|
|
||||||
LIQUIDITYDEPOSITOR,
|
LIQUIDITYDEPOSITOR,
|
||||||
LIQUIDITYTOKEN,
|
LIQUIDITYTOKEN,
|
||||||
LIQUIDITYMANAGER,
|
REWARDMANAGER
|
||||||
RESERVEDEBTOR,
|
|
||||||
REWARDMANAGER,
|
|
||||||
STNK,
|
|
||||||
FTSODEBTOR
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Queue {
|
|
||||||
STATUS managing;
|
|
||||||
address toPermit;
|
|
||||||
address calculator;
|
|
||||||
uint256 timelockEnd;
|
|
||||||
bool nullify;
|
|
||||||
bool executed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
event Deposit(address indexed token, uint256 amount, uint256 value);
|
event Deposit(address indexed token, uint256 amount, uint256 value);
|
||||||
event Withdrawal(address indexed token, uint256 amount, uint256 value);
|
|
||||||
event CreateDebt(address indexed debtor, address indexed token, uint256 amount, uint256 value);
|
|
||||||
event RepayDebt(address indexed debtor, address indexed token, uint256 amount, uint256 value);
|
|
||||||
event Managed(address indexed token, uint256 amount);
|
|
||||||
event ReservesAudited(uint256 indexed totalReserves);
|
event ReservesAudited(uint256 indexed totalReserves);
|
||||||
event Minted(address indexed caller, address indexed recipient, uint256 amount);
|
event Minted(address indexed caller, address indexed recipient, uint256 amount);
|
||||||
event PermissionQueued(STATUS indexed status, address queued);
|
|
||||||
event Permissioned(address addr, STATUS indexed status, bool result);
|
event Permissioned(address addr, STATUS indexed status, bool result);
|
||||||
|
event Withdrawal(address indexed token, uint256 indexed amount, uint256 value);
|
||||||
|
|
||||||
function deposit(
|
function deposit(
|
||||||
address _token,
|
address _token,
|
||||||
@ -54,13 +27,8 @@ interface ITreasury {
|
|||||||
uint256 _profit
|
uint256 _profit
|
||||||
) external returns (uint256);
|
) external returns (uint256);
|
||||||
|
|
||||||
function withdraw(address _token, uint256 _amount) external;
|
function withdraw(address token, uint256 amount) external;
|
||||||
function repayDebtWithReserves(address _token, uint256 _amount) external;
|
|
||||||
function repayDebtWithFtso(uint256 _amount) external;
|
|
||||||
function mint(address _recipient, uint256 _amount) external;
|
function mint(address _recipient, uint256 _amount) external;
|
||||||
function manage(address _token, uint256 _amount) external;
|
|
||||||
function incurDebt(address _token, uint256 _amount) external;
|
|
||||||
|
|
||||||
function tokenValue(address _token, uint256 _amount) external view returns (uint256 value_);
|
function tokenValue(address _token, uint256 _amount) external view returns (uint256 value_);
|
||||||
function indexInRegistry(address _address, STATUS _status) external view returns (bool, uint256);
|
function indexInRegistry(address _address, STATUS _status) external view returns (bool, uint256);
|
||||||
function excessReserves() external view returns (uint256);
|
function excessReserves() external view returns (uint256);
|
||||||
|
|||||||
@ -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);
|
|
||||||
}
|
|
||||||
@ -16,4 +16,13 @@ library Hashes {
|
|||||||
value := keccak256(0x00, 0x40)
|
value := keccak256(0x00, 0x40)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function efficientKeccak256(bytes32 a, bytes32 b, bytes32 c) internal pure returns (bytes32 value) {
|
||||||
|
assembly ("memory-safe") {
|
||||||
|
mstore(0x00, a)
|
||||||
|
mstore(0x20, b)
|
||||||
|
mstore(0x40, c)
|
||||||
|
value := keccak256(0x00, 0x60)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,27 +24,51 @@ library RotationPacking {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
library GovernancePacking {
|
||||||
|
struct GovernancePayload {
|
||||||
|
uint64 chainId;
|
||||||
|
address target;
|
||||||
|
}
|
||||||
|
|
||||||
|
function pack(uint64 chainId, address target) internal pure returns (uint256 packed) {
|
||||||
|
// forge-lint: disable-next-line(unsafe-typecast)
|
||||||
|
return (uint256(chainId) << 192) | (uint256(uint160(target)) << 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
function pack(GovernancePayload calldata data) internal pure returns (uint256 packed) {
|
||||||
|
return pack(data.chainId, data.target);
|
||||||
|
}
|
||||||
|
|
||||||
|
function unpack(uint256 packed) internal pure returns (GovernancePayload memory state) {
|
||||||
|
// forge-lint: disable-next-line(unsafe-typecast)
|
||||||
|
state.target = address(uint160(packed >> 32));
|
||||||
|
// forge-lint: disable-next-line(unsafe-typecast)
|
||||||
|
state.chainId = uint64(packed >> 192);
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
library RequestPacking {
|
library RequestPacking {
|
||||||
struct RequestPayload {
|
struct RequestPayload {
|
||||||
uint32 commission;
|
uint32 bounty;
|
||||||
uint64 chainId;
|
uint64 chainId;
|
||||||
address receiver;
|
address receiver;
|
||||||
}
|
}
|
||||||
|
|
||||||
function pack(uint32 commission, uint64 chainId, address receiver) internal pure returns (uint256 packed) {
|
function pack(uint32 bounty, uint64 chainId, address receiver) internal pure returns (uint256 packed) {
|
||||||
packed = uint256(uint160(receiver)) << 96;
|
packed = uint256(uint160(receiver)) << 96;
|
||||||
packed |= uint256(chainId) << 32;
|
packed |= uint256(chainId) << 32;
|
||||||
packed |= uint256(commission);
|
packed |= uint256(bounty);
|
||||||
return packed;
|
return packed;
|
||||||
}
|
}
|
||||||
|
|
||||||
function pack(RequestPayload calldata data) internal pure returns (uint256 packed) {
|
function pack(RequestPayload calldata data) internal pure returns (uint256 packed) {
|
||||||
return pack(data.commission, data.chainId, data.receiver);
|
return pack(data.bounty, data.chainId, data.receiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
function unpack(uint256 packed) internal pure returns (RequestPayload memory data) {
|
function unpack(uint256 packed) internal pure returns (RequestPayload memory data) {
|
||||||
// forge-lint: disable-next-line(unsafe-typecast)
|
// forge-lint: disable-next-line(unsafe-typecast)
|
||||||
data.commission = uint32(packed & 0xFFFFFFFF);
|
data.bounty = uint32(packed & 0xFFFFFFFF);
|
||||||
// forge-lint: disable-next-line(unsafe-typecast)
|
// forge-lint: disable-next-line(unsafe-typecast)
|
||||||
data.chainId = uint64((packed >> 32) & 0xFFFFFFFFFFFFFFFF);
|
data.chainId = uint64((packed >> 32) & 0xFFFFFFFFFFFFFFFF);
|
||||||
// forge-lint: disable-next-line(unsafe-typecast)
|
// forge-lint: disable-next-line(unsafe-typecast)
|
||||||
|
|||||||
@ -6,7 +6,8 @@ import {Weaver} from "../types/Weaver.sol";
|
|||||||
contract WeaverMock is Weaver {
|
contract WeaverMock is Weaver {
|
||||||
address private _previousAddress;
|
address private _previousAddress;
|
||||||
|
|
||||||
constructor(address previousWeaver) Weaver(previousWeaver) {
|
constructor(address previousWeaver) {
|
||||||
|
Weaver._initialize(previousWeaver);
|
||||||
_previousAddress = previousWeaver;
|
_previousAddress = previousWeaver;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,8 +1,6 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
pragma solidity ^0.8.20;
|
pragma solidity ^0.8.20;
|
||||||
|
|
||||||
import {FrontEndRewarder} from "./FrontEndRewarder.sol";
|
|
||||||
|
|
||||||
import {IGHST} from "../interfaces/IGHST.sol";
|
import {IGHST} from "../interfaces/IGHST.sol";
|
||||||
import {IStaking} from "../interfaces/IStaking.sol";
|
import {IStaking} from "../interfaces/IStaking.sol";
|
||||||
import {ITreasury} from "../interfaces/ITreasury.sol";
|
import {ITreasury} from "../interfaces/ITreasury.sol";
|
||||||
@ -11,7 +9,7 @@ import {INoteKeeper} from "../interfaces/INoteKeeper.sol";
|
|||||||
import {EnumerableSet} from "@openzeppelin-contracts/utils/structs/EnumerableSet.sol";
|
import {EnumerableSet} from "@openzeppelin-contracts/utils/structs/EnumerableSet.sol";
|
||||||
import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
|
import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
|
||||||
|
|
||||||
abstract contract NoteKeeper is INoteKeeper, FrontEndRewarder {
|
abstract contract NoteKeeper is INoteKeeper {
|
||||||
using EnumerableSet for EnumerableSet.UintSet;
|
using EnumerableSet for EnumerableSet.UintSet;
|
||||||
using SafeERC20 for IGHST;
|
using SafeERC20 for IGHST;
|
||||||
|
|
||||||
@ -21,36 +19,20 @@ abstract contract NoteKeeper is INoteKeeper, FrontEndRewarder {
|
|||||||
|
|
||||||
IGHST internal immutable _GHST;
|
IGHST internal immutable _GHST;
|
||||||
IStaking internal immutable _STAKING;
|
IStaking internal immutable _STAKING;
|
||||||
ITreasury internal _treasury;
|
ITreasury internal immutable _TREASURY;
|
||||||
|
|
||||||
constructor(
|
constructor(address _ghstAddress, address _stakingAddress, address _treasuryAddress) {
|
||||||
address _authority,
|
|
||||||
address _ftsoAddress,
|
|
||||||
address _ghstAddress,
|
|
||||||
address _stakingAddress,
|
|
||||||
address _treasuryAddress
|
|
||||||
) FrontEndRewarder(_authority, _ftsoAddress) {
|
|
||||||
_GHST = IGHST(_ghstAddress);
|
_GHST = IGHST(_ghstAddress);
|
||||||
_STAKING = IStaking(_stakingAddress);
|
_STAKING = IStaking(_stakingAddress);
|
||||||
_treasury = ITreasury(_treasuryAddress);
|
_TREASURY = ITreasury(_treasuryAddress);
|
||||||
_STAKING.toggleLock();
|
_STAKING.toggleLock();
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateTreasury() external {
|
|
||||||
if (
|
|
||||||
msg.sender != authority.governor() &&
|
|
||||||
msg.sender != authority.guardian() &&
|
|
||||||
msg.sender != authority.policy()
|
|
||||||
) revert OnlyAuthorized();
|
|
||||||
_treasury = ITreasury(authority.vault());
|
|
||||||
}
|
|
||||||
|
|
||||||
function addNote(
|
function addNote(
|
||||||
address user,
|
address user,
|
||||||
uint256 payout,
|
uint256 payout,
|
||||||
uint48 expiry,
|
uint48 expiry,
|
||||||
uint48 marketId,
|
uint48 marketId
|
||||||
address referral
|
|
||||||
) internal returns (uint256 index) {
|
) internal returns (uint256 index) {
|
||||||
index = notes[user].length;
|
index = notes[user].length;
|
||||||
_pendingIndexes[user].add(index);
|
_pendingIndexes[user].add(index);
|
||||||
@ -64,8 +46,7 @@ abstract contract NoteKeeper is INoteKeeper, FrontEndRewarder {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
uint256 rewards = _giveRewards(payout, referral);
|
_TREASURY.mint(address(this), payout);
|
||||||
_treasury.mint(address(this), payout + rewards);
|
|
||||||
_STAKING.stake(payout, address(this), false, true);
|
_STAKING.stake(payout, address(this), false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,15 +7,15 @@ import {BitMaps} from "@openzeppelin-contracts/utils/structs/BitMaps.sol";
|
|||||||
contract StorageHistory is IStorageHistory {
|
contract StorageHistory is IStorageHistory {
|
||||||
using BitMaps for BitMaps.BitMap;
|
using BitMaps for BitMaps.BitMap;
|
||||||
|
|
||||||
address private _deployer;
|
address immutable private DEPLOYER;
|
||||||
address private _currentOwner;
|
address private _currentOwner;
|
||||||
|
|
||||||
DeploymentSnapshot private _snapshot;
|
DeploymentSnapshot private _snapshot;
|
||||||
BitMaps.BitMap private _executedTransaction;
|
BitMaps.BitMap private _executedTransaction;
|
||||||
|
|
||||||
constructor(address deployer) {
|
constructor() {
|
||||||
_currentOwner = msg.sender;
|
_currentOwner = msg.sender;
|
||||||
_deployer = deployer;
|
DEPLOYER = msg.sender;
|
||||||
}
|
}
|
||||||
|
|
||||||
function deploymentSnapshot() external override view returns (DeploymentSnapshot memory) {
|
function deploymentSnapshot() external override view returns (DeploymentSnapshot memory) {
|
||||||
@ -31,25 +31,25 @@ contract StorageHistory is IStorageHistory {
|
|||||||
return _executedTransaction.get(session);
|
return _executedTransaction.get(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setOwner(address newOwner) external override {
|
||||||
|
if (msg.sender != DEPLOYER) revert NotDeployer();
|
||||||
|
_currentOwner = newOwner;
|
||||||
|
}
|
||||||
|
|
||||||
function trySetTransactionExecuted(uint256 session) external override {
|
function trySetTransactionExecuted(uint256 session) external override {
|
||||||
if (msg.sender != _currentOwner) revert NotCreator();
|
if (msg.sender != _currentOwner) revert NotOwner();
|
||||||
if (_executedTransaction.get(session)) revert AlreadyExecuted();
|
if (_executedTransaction.get(session)) revert AlreadyExecuted();
|
||||||
_executedTransaction.set(session);
|
_executedTransaction.set(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setOwner(address newOwner) external override {
|
|
||||||
if (msg.sender != _deployer) revert NotDeployer();
|
|
||||||
_currentOwner = newOwner;
|
|
||||||
}
|
|
||||||
|
|
||||||
function increaseBridgeIn(uint256 amount) external override {
|
function increaseBridgeIn(uint256 amount) external override {
|
||||||
if (msg.sender != _currentOwner) revert NotCreator();
|
if (msg.sender != _currentOwner) revert NotOwner();
|
||||||
// forge-lint: disable-next-line(unsafe-typecast)
|
// forge-lint: disable-next-line(unsafe-typecast)
|
||||||
_snapshot.amountIn += uint104(amount);
|
_snapshot.amountIn += uint104(amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
function tryIncreaseBridgeOut(uint256 amount) external override {
|
function tryIncreaseBridgeOut(uint256 amount) external override {
|
||||||
if (msg.sender != _currentOwner) revert NotCreator();
|
if (msg.sender != _currentOwner) revert NotOwner();
|
||||||
if (bridgeImbalance() < amount) revert BridgeOutImpossible();
|
if (bridgeImbalance() < amount) revert BridgeOutImpossible();
|
||||||
// forge-lint: disable-next-line(unsafe-typecast)
|
// forge-lint: disable-next-line(unsafe-typecast)
|
||||||
_snapshot.amountOut += uint104(amount);
|
_snapshot.amountOut += uint104(amount);
|
||||||
|
|||||||
@ -22,14 +22,6 @@ abstract contract Weaver is IWeaver {
|
|||||||
mapping(uint256 => mapping(uint256 => bytes32[])) internal _slotValues;
|
mapping(uint256 => mapping(uint256 => bytes32[])) internal _slotValues;
|
||||||
mapping(uint256 => uint256) internal _filledEntries;
|
mapping(uint256 => uint256) internal _filledEntries;
|
||||||
|
|
||||||
constructor(address _previousAddress) {
|
|
||||||
if (_previousAddress != address(0)) {
|
|
||||||
uint256 newSession = IWeaver(_previousAddress).currentWeavingSession() + 1;
|
|
||||||
currentWeavingSession = newSession;
|
|
||||||
startWeavingSession = newSession;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function previousAddress() external virtual view returns (address);
|
function previousAddress() external virtual view returns (address);
|
||||||
|
|
||||||
function getSlotValues(
|
function getSlotValues(
|
||||||
@ -171,4 +163,10 @@ abstract contract Weaver is IWeaver {
|
|||||||
function _computeArgumentsHash(uint256 i, uint256 a, bytes32 r) internal pure returns (bytes32) {
|
function _computeArgumentsHash(uint256 i, uint256 a, bytes32 r) internal pure returns (bytes32) {
|
||||||
return Hashes.efficientKeccak256(Hashes.efficientKeccak256(bytes32(a), r), bytes32(i));
|
return Hashes.efficientKeccak256(Hashes.efficientKeccak256(bytes32(a), r), bytes32(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _initialize(address _previousAddress) internal {
|
||||||
|
uint256 newSession = IWeaver(_previousAddress).currentWeavingSession() + 1;
|
||||||
|
currentWeavingSession = newSession;
|
||||||
|
startWeavingSession = newSession;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -226,7 +226,7 @@ contract GhostBondDepositoryTest is Test {
|
|||||||
|
|
||||||
skip(DEPOSIT_INTERVAL);
|
skip(DEPOSIT_INTERVAL);
|
||||||
vm.prank(ALICE);
|
vm.prank(ALICE);
|
||||||
depository.deposit(0, 0, INITIAL_PRICE, ALICE, ALICE);
|
depository.deposit(0, 0, INITIAL_PRICE, ALICE);
|
||||||
|
|
||||||
(, , , uint256 newTotalDebt, , ,) = depository.markets(0);
|
(, , , uint256 newTotalDebt, , ,) = depository.markets(0);
|
||||||
assertEq(totalDebt > newTotalDebt, true);
|
assertEq(totalDebt > newTotalDebt, true);
|
||||||
@ -237,7 +237,7 @@ contract GhostBondDepositoryTest is Test {
|
|||||||
|
|
||||||
uint256 amount = 10_000 * 1e18;
|
uint256 amount = 10_000 * 1e18;
|
||||||
vm.prank(ALICE);
|
vm.prank(ALICE);
|
||||||
depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE);
|
depository.deposit(0, amount, INITIAL_PRICE, ALICE);
|
||||||
|
|
||||||
(, , , bool active) = depository.adjustments(0);
|
(, , , bool active) = depository.adjustments(0);
|
||||||
assertEq(active, true);
|
assertEq(active, true);
|
||||||
@ -248,10 +248,10 @@ contract GhostBondDepositoryTest is Test {
|
|||||||
uint256 amount = 10_000 * 1e18;
|
uint256 amount = 10_000 * 1e18;
|
||||||
|
|
||||||
vm.startPrank(ALICE);
|
vm.startPrank(ALICE);
|
||||||
depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE);
|
depository.deposit(0, amount, INITIAL_PRICE, ALICE);
|
||||||
skip(DEPOSIT_INTERVAL);
|
skip(DEPOSIT_INTERVAL);
|
||||||
(uint64 change, , ,) = depository.adjustments(0);
|
(uint64 change, , ,) = depository.adjustments(0);
|
||||||
depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE);
|
depository.deposit(0, amount, INITIAL_PRICE, ALICE);
|
||||||
vm.stopPrank();
|
vm.stopPrank();
|
||||||
|
|
||||||
(, uint64 newCtrlVariable, , ,) = depository.terms(0);
|
(, uint64 newCtrlVariable, , ,) = depository.terms(0);
|
||||||
@ -264,11 +264,11 @@ contract GhostBondDepositoryTest is Test {
|
|||||||
uint256 amount = 10_000 * 1e18;
|
uint256 amount = 10_000 * 1e18;
|
||||||
|
|
||||||
vm.startPrank(ALICE);
|
vm.startPrank(ALICE);
|
||||||
depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE);
|
depository.deposit(0, amount, INITIAL_PRICE, ALICE);
|
||||||
(uint64 change, , ,) = depository.adjustments(0);
|
(uint64 change, , ,) = depository.adjustments(0);
|
||||||
skip(TUNE_INTERVAL / 2);
|
skip(TUNE_INTERVAL / 2);
|
||||||
|
|
||||||
depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE);
|
depository.deposit(0, amount, INITIAL_PRICE, ALICE);
|
||||||
(, uint64 newCtrlVariable, , ,) = depository.terms(0);
|
(, uint64 newCtrlVariable, , ,) = depository.terms(0);
|
||||||
vm.stopPrank();
|
vm.stopPrank();
|
||||||
|
|
||||||
@ -282,12 +282,12 @@ contract GhostBondDepositoryTest is Test {
|
|||||||
uint256 amount = 10_000 * 1e18;
|
uint256 amount = 10_000 * 1e18;
|
||||||
|
|
||||||
vm.startPrank(ALICE);
|
vm.startPrank(ALICE);
|
||||||
depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE);
|
depository.deposit(0, amount, INITIAL_PRICE, ALICE);
|
||||||
(uint64 change, , ,) = depository.adjustments(0);
|
(uint64 change, , ,) = depository.adjustments(0);
|
||||||
skip(TUNE_INTERVAL / 2);
|
skip(TUNE_INTERVAL / 2);
|
||||||
depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE);
|
depository.deposit(0, amount, INITIAL_PRICE, ALICE);
|
||||||
skip(TUNE_INTERVAL / 2);
|
skip(TUNE_INTERVAL / 2);
|
||||||
depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE);
|
depository.deposit(0, amount, INITIAL_PRICE, ALICE);
|
||||||
vm.stopPrank();
|
vm.stopPrank();
|
||||||
|
|
||||||
(, uint64 newCtrlVariable, , ,) = depository.terms(0);
|
(, uint64 newCtrlVariable, , ,) = depository.terms(0);
|
||||||
@ -297,7 +297,7 @@ contract GhostBondDepositoryTest is Test {
|
|||||||
function test_shouldAllowDeposit() public {
|
function test_shouldAllowDeposit() public {
|
||||||
uint256 amount = 10_000 * 1e18;
|
uint256 amount = 10_000 * 1e18;
|
||||||
vm.prank(ALICE);
|
vm.prank(ALICE);
|
||||||
depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE);
|
depository.deposit(0, amount, INITIAL_PRICE, ALICE);
|
||||||
|
|
||||||
uint256[] memory arr = depository.indexesFor(ALICE);
|
uint256[] memory arr = depository.indexesFor(ALICE);
|
||||||
assertEq(arr.length, 1);
|
assertEq(arr.length, 1);
|
||||||
@ -307,14 +307,14 @@ contract GhostBondDepositoryTest is Test {
|
|||||||
uint256 amount = 6_700_000 * 1e18;
|
uint256 amount = 6_700_000 * 1e18;
|
||||||
vm.expectRevert();
|
vm.expectRevert();
|
||||||
vm.prank(ALICE);
|
vm.prank(ALICE);
|
||||||
depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE);
|
depository.deposit(0, amount, INITIAL_PRICE, ALICE);
|
||||||
}
|
}
|
||||||
|
|
||||||
function test_shouldNotRedeemImmediately() public {
|
function test_shouldNotRedeemImmediately() public {
|
||||||
uint256 balance = ftso.balanceOf(ALICE);
|
uint256 balance = ftso.balanceOf(ALICE);
|
||||||
uint256 amount = 10_000 * 1e18; // 10,000
|
uint256 amount = 10_000 * 1e18; // 10,000
|
||||||
vm.startPrank(ALICE);
|
vm.startPrank(ALICE);
|
||||||
depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE);
|
depository.deposit(0, amount, INITIAL_PRICE, ALICE);
|
||||||
depository.redeemAll(ALICE, true);
|
depository.redeemAll(ALICE, true);
|
||||||
vm.stopPrank();
|
vm.stopPrank();
|
||||||
assertEq(ftso.balanceOf(ALICE), balance);
|
assertEq(ftso.balanceOf(ALICE), balance);
|
||||||
@ -328,9 +328,9 @@ contract GhostBondDepositoryTest is Test {
|
|||||||
vm.stopPrank();
|
vm.stopPrank();
|
||||||
|
|
||||||
vm.prank(ALICE);
|
vm.prank(ALICE);
|
||||||
depository.deposit(0, amount, type(uint256).max, ALICE, ALICE);
|
depository.deposit(0, amount, type(uint256).max, ALICE);
|
||||||
vm.prank(BOB);
|
vm.prank(BOB);
|
||||||
depository.deposit(0, amount, type(uint256).max, BOB, BOB);
|
depository.deposit(0, amount, type(uint256).max, BOB);
|
||||||
|
|
||||||
skip(DEPOSIT_INTERVAL);
|
skip(DEPOSIT_INTERVAL);
|
||||||
vm.roll(block.number + 1);
|
vm.roll(block.number + 1);
|
||||||
@ -346,7 +346,7 @@ contract GhostBondDepositoryTest is Test {
|
|||||||
function test_shouldRedeemAfterVested() public {
|
function test_shouldRedeemAfterVested() public {
|
||||||
uint256 amount = 10_000 * 1e18; // 10,000
|
uint256 amount = 10_000 * 1e18; // 10,000
|
||||||
vm.startPrank(ALICE);
|
vm.startPrank(ALICE);
|
||||||
(uint256 expectedPayout, ,) = depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE);
|
(uint256 expectedPayout, ,) = depository.deposit(0, amount, INITIAL_PRICE, ALICE);
|
||||||
|
|
||||||
skip(DEPOSIT_INTERVAL);
|
skip(DEPOSIT_INTERVAL);
|
||||||
depository.redeemAll(ALICE, true);
|
depository.redeemAll(ALICE, true);
|
||||||
@ -360,10 +360,10 @@ contract GhostBondDepositoryTest is Test {
|
|||||||
function test_shouldCorrectlyRedeemPartially() public {
|
function test_shouldCorrectlyRedeemPartially() public {
|
||||||
uint256 amount = 1 * 1e18;
|
uint256 amount = 1 * 1e18;
|
||||||
vm.startPrank(ALICE);
|
vm.startPrank(ALICE);
|
||||||
depository.deposit(0, amount, INITIAL_PRICE * 2, ALICE, ALICE);
|
depository.deposit(0, amount, INITIAL_PRICE * 2, ALICE);
|
||||||
depository.deposit(0, amount, INITIAL_PRICE * 2, ALICE, ALICE);
|
depository.deposit(0, amount, INITIAL_PRICE * 2, ALICE);
|
||||||
depository.deposit(0, amount, INITIAL_PRICE * 2, ALICE, ALICE);
|
depository.deposit(0, amount, INITIAL_PRICE * 2, ALICE);
|
||||||
depository.deposit(0, amount, INITIAL_PRICE * 2, ALICE, ALICE);
|
depository.deposit(0, amount, INITIAL_PRICE * 2, ALICE);
|
||||||
|
|
||||||
skip(DEPOSIT_INTERVAL);
|
skip(DEPOSIT_INTERVAL);
|
||||||
|
|
||||||
@ -405,7 +405,7 @@ contract GhostBondDepositoryTest is Test {
|
|||||||
vm.startPrank(ALICE);
|
vm.startPrank(ALICE);
|
||||||
|
|
||||||
assertEq(ghst.balanceOf(ALICE), 0);
|
assertEq(ghst.balanceOf(ALICE), 0);
|
||||||
(uint256 expectedPayout, ,) = depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE);
|
(uint256 expectedPayout, ,) = depository.deposit(0, amount, INITIAL_PRICE, ALICE);
|
||||||
assertEq(ghst.balanceOf(address(depository)), 0);
|
assertEq(ghst.balanceOf(address(depository)), 0);
|
||||||
|
|
||||||
skip(DEPOSIT_INTERVAL);
|
skip(DEPOSIT_INTERVAL);
|
||||||
@ -437,7 +437,7 @@ contract GhostBondDepositoryTest is Test {
|
|||||||
uint256 price = depository.marketPrice(0);
|
uint256 price = depository.marketPrice(0);
|
||||||
uint256 amount = maxPayout * price;
|
uint256 amount = maxPayout * price;
|
||||||
vm.prank(ALICE);
|
vm.prank(ALICE);
|
||||||
depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE);
|
depository.deposit(0, amount, INITIAL_PRICE, ALICE);
|
||||||
skip(DEPOSIT_INTERVAL);
|
skip(DEPOSIT_INTERVAL);
|
||||||
uint256 newPrice = depository.marketPrice(0);
|
uint256 newPrice = depository.marketPrice(0);
|
||||||
assertEq(newPrice < INITIAL_PRICE, true);
|
assertEq(newPrice < INITIAL_PRICE, true);
|
||||||
@ -460,7 +460,7 @@ contract GhostBondDepositoryTest is Test {
|
|||||||
assertEq(IERC20(address(weth)).balanceOf(address(treasury)), 0);
|
assertEq(IERC20(address(weth)).balanceOf(address(treasury)), 0);
|
||||||
|
|
||||||
vm.prank(ALICE);
|
vm.prank(ALICE);
|
||||||
depository.deposit{value: halfAmount}(1, halfAmount, halfAmount, ALICE, ALICE);
|
depository.deposit{value: halfAmount}(1, halfAmount, halfAmount, ALICE);
|
||||||
assertEq(ALICE.balance, halfAmount);
|
assertEq(ALICE.balance, halfAmount);
|
||||||
assertEq(IERC20(address(weth)).balanceOf(address(treasury)), halfAmount);
|
assertEq(IERC20(address(weth)).balanceOf(address(treasury)), halfAmount);
|
||||||
}
|
}
|
||||||
@ -473,7 +473,7 @@ contract GhostBondDepositoryTest is Test {
|
|||||||
assertEq(IERC20(address(weth)).balanceOf(address(treasury)), 0);
|
assertEq(IERC20(address(weth)).balanceOf(address(treasury)), 0);
|
||||||
|
|
||||||
vm.prank(ALICE);
|
vm.prank(ALICE);
|
||||||
(uint256 payout,,) = depository.deposit{value: halfAmount}(1, 0, halfAmount, ALICE, ALICE);
|
(uint256 payout,,) = depository.deposit{value: halfAmount}(1, 0, halfAmount, ALICE);
|
||||||
assertEq(ALICE.balance, halfAmount);
|
assertEq(ALICE.balance, halfAmount);
|
||||||
assertEq(IERC20(address(weth)).balanceOf(address(treasury)), halfAmount);
|
assertEq(IERC20(address(weth)).balanceOf(address(treasury)), halfAmount);
|
||||||
assertEq(payout, 0);
|
assertEq(payout, 0);
|
||||||
@ -488,7 +488,7 @@ contract GhostBondDepositoryTest is Test {
|
|||||||
|
|
||||||
vm.expectRevert();
|
vm.expectRevert();
|
||||||
vm.prank(ALICE);
|
vm.prank(ALICE);
|
||||||
depository.deposit{value: halfAmount}(1, amount, amount, ALICE, ALICE);
|
depository.deposit{value: halfAmount}(1, amount, amount, ALICE);
|
||||||
|
|
||||||
assertEq(ALICE.balance, amount);
|
assertEq(ALICE.balance, amount);
|
||||||
assertEq(IERC20(address(weth)).balanceOf(address(treasury)), 0);
|
assertEq(IERC20(address(weth)).balanceOf(address(treasury)), 0);
|
||||||
@ -502,7 +502,7 @@ contract GhostBondDepositoryTest is Test {
|
|||||||
uint256 i;
|
uint256 i;
|
||||||
for (; i < 8; ) {
|
for (; i < 8; ) {
|
||||||
vm.prank(ALICE);
|
vm.prank(ALICE);
|
||||||
depository.deposit{value: aliceAmountUsed}(1, aliceAmountUsed, type(uint256).max, ALICE, ALICE);
|
depository.deposit{value: aliceAmountUsed}(1, aliceAmountUsed, type(uint256).max, ALICE);
|
||||||
unchecked { ++i; }
|
unchecked { ++i; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -519,7 +519,7 @@ contract GhostBondDepositoryTest is Test {
|
|||||||
i = 0;
|
i = 0;
|
||||||
for (; i < 7; ) {
|
for (; i < 7; ) {
|
||||||
vm.prank(BOB);
|
vm.prank(BOB);
|
||||||
depository.deposit{value: bobAmountUsed}(1, bobAmountUsed, type(uint256).max, BOB, BOB);
|
depository.deposit{value: bobAmountUsed}(1, bobAmountUsed, type(uint256).max, BOB);
|
||||||
unchecked { ++i; }
|
unchecked { ++i; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,21 +6,152 @@ import {Gatekeeper} from "../../src/Gatekeeper.sol";
|
|||||||
import {FullMath} from "../../src/libraries/FullMath.sol";
|
import {FullMath} from "../../src/libraries/FullMath.sol";
|
||||||
import {RequestPacking} from "../../src/libraries/Packing.sol";
|
import {RequestPacking} from "../../src/libraries/Packing.sol";
|
||||||
import {IStorageHistory} from "../../src/interfaces/IStorageHistory.sol";
|
import {IStorageHistory} from "../../src/interfaces/IStorageHistory.sol";
|
||||||
|
import {StorageHistory} from "../../src/types/StorageHistory.sol";
|
||||||
|
|
||||||
|
contract MockGovernance is Test {
|
||||||
|
address public immutable GATEKEEPER;
|
||||||
|
address public immutable DUMMY_ADDRESS;
|
||||||
|
|
||||||
|
bool public auditCalled;
|
||||||
|
|
||||||
|
enum STATUS {
|
||||||
|
RESERVEDEPOSITOR,
|
||||||
|
RESERVETOKEN,
|
||||||
|
LIQUIDITYDEPOSITOR,
|
||||||
|
LIQUIDITYTOKEN,
|
||||||
|
REWARDMANAGER
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(address gatekeeper, address dummyAddress) {
|
||||||
|
GATEKEEPER = gatekeeper;
|
||||||
|
DUMMY_ADDRESS = dummyAddress;
|
||||||
|
auditCalled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setDistributor(address distributor) external view {
|
||||||
|
require(msg.sender == GATEKEEPER);
|
||||||
|
assertEq(distributor, DUMMY_ADDRESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setWarmupPeriod(uint256 warmupPeriod) external view {
|
||||||
|
require(msg.sender == GATEKEEPER);
|
||||||
|
assertEq(warmupPeriod, 69);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setBounty(uint256 bounty) external view {
|
||||||
|
require(msg.sender == GATEKEEPER);
|
||||||
|
assertEq(bounty, 69);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setAdjustment(uint256 rate, uint256 target, bool add) external view {
|
||||||
|
require(msg.sender == GATEKEEPER);
|
||||||
|
assertEq(rate, 69);
|
||||||
|
assertEq(target, 420);
|
||||||
|
assertEq(add, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateGatekeeperAddress(address gatekeeper) external view {
|
||||||
|
require(msg.sender == GATEKEEPER);
|
||||||
|
assertEq(gatekeeper, DUMMY_ADDRESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addPool(address pool) external view {
|
||||||
|
require(msg.sender == GATEKEEPER);
|
||||||
|
assertEq(pool, DUMMY_ADDRESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
function removePool(uint256 index) external view {
|
||||||
|
require(msg.sender == GATEKEEPER);
|
||||||
|
assertEq(index, 1337);
|
||||||
|
}
|
||||||
|
|
||||||
|
function close(uint256 id) external view {
|
||||||
|
require(msg.sender == GATEKEEPER);
|
||||||
|
assertEq(id, 420);
|
||||||
|
}
|
||||||
|
|
||||||
|
function create(
|
||||||
|
uint256[3] calldata market,
|
||||||
|
uint256[2] calldata terms,
|
||||||
|
address quoteToken,
|
||||||
|
uint32[2] calldata intervals,
|
||||||
|
bool[2] calldata booleans
|
||||||
|
) external view {
|
||||||
|
require(msg.sender == GATEKEEPER);
|
||||||
|
|
||||||
|
assertEq(market[0], 69);
|
||||||
|
assertEq(market[1], 420);
|
||||||
|
assertEq(market[2], 1337);
|
||||||
|
|
||||||
|
assertEq(terms[0], 420);
|
||||||
|
assertEq(terms[1], 1337);
|
||||||
|
|
||||||
|
assertEq(quoteToken, DUMMY_ADDRESS);
|
||||||
|
|
||||||
|
assertEq(intervals[0], 34);
|
||||||
|
assertEq(intervals[1], 35);
|
||||||
|
|
||||||
|
assertEq(booleans[0], true);
|
||||||
|
assertEq(booleans[1], true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function enable(STATUS status, address token, address calculator) external view {
|
||||||
|
require(msg.sender == GATEKEEPER);
|
||||||
|
|
||||||
|
if (status != STATUS.RESERVETOKEN) revert();
|
||||||
|
assertEq(token, DUMMY_ADDRESS);
|
||||||
|
assertEq(calculator, DUMMY_ADDRESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
function disable(STATUS status, address token) external view {
|
||||||
|
require(msg.sender == GATEKEEPER);
|
||||||
|
|
||||||
|
if (status != STATUS.LIQUIDITYTOKEN) revert();
|
||||||
|
assertEq(token, DUMMY_ADDRESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
function forfeitReserves(address router, uint256 liquidity, bool destroyerMode) external view {
|
||||||
|
require(msg.sender == GATEKEEPER);
|
||||||
|
assertEq(router, DUMMY_ADDRESS);
|
||||||
|
assertEq(liquidity, 420);
|
||||||
|
assertEq(destroyerMode, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function redeemReserve(address router, uint256 amount) external view {
|
||||||
|
require(msg.sender == GATEKEEPER);
|
||||||
|
assertEq(router, DUMMY_ADDRESS);
|
||||||
|
assertEq(amount, 420);
|
||||||
|
}
|
||||||
|
|
||||||
|
function withdraw(address token, uint256 amount) external view {
|
||||||
|
require(msg.sender == GATEKEEPER);
|
||||||
|
assertEq(token, DUMMY_ADDRESS);
|
||||||
|
assertEq(amount, 420);
|
||||||
|
}
|
||||||
|
|
||||||
|
function auditReserves() external {
|
||||||
|
require(msg.sender == GATEKEEPER);
|
||||||
|
auditCalled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
contract MockStaking is Test {
|
contract MockStaking is Test {
|
||||||
Gatekeeper public gatekeeper;
|
Gatekeeper public gatekeeper;
|
||||||
|
|
||||||
mapping(address => uint256) private _materializedAmounts;
|
mapping(address => uint256) private _recalledAmounts;
|
||||||
|
|
||||||
constructor(uint256 existential) {
|
constructor(uint256 existential) {
|
||||||
gatekeeper = new Gatekeeper(existential, address(0));
|
StorageHistory history = new StorageHistory();
|
||||||
|
gatekeeper = new Gatekeeper(existential, address(history));
|
||||||
|
gatekeeper.initialize(address(0));
|
||||||
|
history.setOwner(address(gatekeeper));
|
||||||
}
|
}
|
||||||
|
|
||||||
function runGhost(bytes32 receiver, uint256 amount) external {
|
function runGhost(bytes32 receiver, uint256 amount) external {
|
||||||
gatekeeper.ghost(receiver, amount);
|
gatekeeper.ghost(receiver, amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
function runMaterialize(
|
function runRecall(
|
||||||
uint256 exodusSession,
|
uint256 exodusSession,
|
||||||
uint256 amount,
|
uint256 amount,
|
||||||
uint256 packed,
|
uint256 packed,
|
||||||
@ -31,15 +162,15 @@ contract MockStaking is Test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
vm.prank(address(gatekeeper), caller);
|
vm.prank(address(gatekeeper), caller);
|
||||||
gatekeeper.materialize{value: msg.value}(exodusSession, amount, packed);
|
gatekeeper.recall{value: msg.value}(exodusSession, amount, packed);
|
||||||
}
|
}
|
||||||
|
|
||||||
function materialize(address receiver, uint256 amount) external {
|
function recall(address receiver, uint256 amount) external {
|
||||||
_materializedAmounts[receiver] += amount;
|
_recalledAmounts[receiver] += amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
function materializedAmount(address who) external view returns (uint256) {
|
function recalledAmount(address who) external view returns (uint256) {
|
||||||
return _materializedAmounts[who];
|
return _recalledAmounts[who];
|
||||||
}
|
}
|
||||||
|
|
||||||
function totalReserves() external pure returns (uint256) {
|
function totalReserves() external pure returns (uint256) {
|
||||||
@ -58,7 +189,9 @@ contract GatekeeperTest is Test {
|
|||||||
address constant BOB = 0x0000000000000000000000000000000000000002;
|
address constant BOB = 0x0000000000000000000000000000000000000002;
|
||||||
uint256 constant EXISTENTIAL = 1337;
|
uint256 constant EXISTENTIAL = 1337;
|
||||||
uint256 constant INIT_AMOUNT = 69 * 1e18;
|
uint256 constant INIT_AMOUNT = 69 * 1e18;
|
||||||
|
address constant DUMMY_ADDRESS = address(0x0101010101010101010101010101010101010101);
|
||||||
|
|
||||||
|
MockGovernance governance;
|
||||||
Gatekeeper gatekeeper;
|
Gatekeeper gatekeeper;
|
||||||
MockStaking staking;
|
MockStaking staking;
|
||||||
|
|
||||||
@ -68,6 +201,12 @@ contract GatekeeperTest is Test {
|
|||||||
vm.prank(ALICE, ALICE);
|
vm.prank(ALICE, ALICE);
|
||||||
staking = new MockStaking(EXISTENTIAL);
|
staking = new MockStaking(EXISTENTIAL);
|
||||||
gatekeeper = staking.gatekeeper();
|
gatekeeper = staking.gatekeeper();
|
||||||
|
|
||||||
|
MockGovernance tempGov = new MockGovernance(address(gatekeeper), DUMMY_ADDRESS);
|
||||||
|
bytes memory govBytecode = address(tempGov).code;
|
||||||
|
|
||||||
|
vm.etch(DUMMY_ADDRESS, govBytecode);
|
||||||
|
governance = MockGovernance(DUMMY_ADDRESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
function test_correctInitialization() public view {
|
function test_correctInitialization() public view {
|
||||||
@ -105,7 +244,7 @@ contract GatekeeperTest is Test {
|
|||||||
staking.runGhost(receiver, ghostAmount);
|
staking.runGhost(receiver, ghostAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
function test_materializeWork(uint256 exodusSession, uint256 bobAmount, uint32 bobCommission) public {
|
function test_recallWork(uint256 exodusSession, uint256 bobAmount, uint32 bobCommission) public {
|
||||||
vm.assume(bobAmount > 1337 && bobAmount < 1_000 ether);
|
vm.assume(bobAmount > 1337 && bobAmount < 1_000 ether);
|
||||||
vm.assume(bobCommission > 0 && bobCommission <= type(uint32).max);
|
vm.assume(bobCommission > 0 && bobCommission <= type(uint32).max);
|
||||||
|
|
||||||
@ -122,14 +261,14 @@ contract GatekeeperTest is Test {
|
|||||||
assertEq(BOB.balance, 0 ether);
|
assertEq(BOB.balance, 0 ether);
|
||||||
uint256 aliceStartingNative = ALICE.balance;
|
uint256 aliceStartingNative = ALICE.balance;
|
||||||
|
|
||||||
uint256 previousAliceAmount = staking.materializedAmount(ALICE);
|
uint256 previousAliceAmount = staking.recalledAmount(ALICE);
|
||||||
uint256 previousBobAmount = staking.materializedAmount(BOB);
|
uint256 previousBobAmount = staking.recalledAmount(BOB);
|
||||||
|
|
||||||
vm.prank(ALICE);
|
vm.prank(ALICE);
|
||||||
staking.runMaterialize{value: nativeNeeded}(exodusSession, bobAmount, bobPacked, ALICE);
|
staking.runRecall{value: nativeNeeded}(exodusSession, bobAmount, bobPacked, ALICE);
|
||||||
|
|
||||||
assertApproxEqAbs(previousAliceAmount + commissionAmount, staking.materializedAmount(ALICE), 1000);
|
assertApproxEqAbs(previousAliceAmount + commissionAmount, staking.recalledAmount(ALICE), 1000);
|
||||||
assertApproxEqAbs(previousBobAmount + (bobAmount - commissionAmount), staking.materializedAmount(BOB), 1000);
|
assertApproxEqAbs(previousBobAmount + (bobAmount - commissionAmount), staking.recalledAmount(BOB), 1000);
|
||||||
|
|
||||||
assertEq(BOB.balance, nativeNeeded);
|
assertEq(BOB.balance, nativeNeeded);
|
||||||
assertEq(ALICE.balance, aliceStartingNative - nativeNeeded);
|
assertEq(ALICE.balance, aliceStartingNative - nativeNeeded);
|
||||||
@ -181,66 +320,43 @@ contract GatekeeperTest is Test {
|
|||||||
// could not apply signature from DKG #2
|
// could not apply signature from DKG #2
|
||||||
vm.expectRevert();
|
vm.expectRevert();
|
||||||
gatekeeper.verify(
|
gatekeeper.verify(
|
||||||
hex"49f03a6700000000000000000000000000000000000000000000000000000000000000043492bc8bb10848f1f788496ccadde7d458eac4ac2c1eca2dd2c7a506d8fa2c5b0000000000000000000000000000000000000000000000000000000000000000",
|
hex"49f03a6700000000000000000000000000000000000000000000000000000000000000133492bc8bb10848f1f788496ccadde7d458eac4ac2c1eca2dd2c7a506d8fa2c5b0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
hex"0496bacdcc4260a610c9ef465cc1d9f973dda7d4794a65c1d1ea8ea2bb8aa3ddb08d171f5aa267c1ddc64343b4f054424af0c61b57e1f359cd4e23876a0fa4a704",
|
hex"0434c3b37293516c6204dc291821e19ab5b3d8afb0947a0a2dffc91600fbd3f6b9783a66887d515e774b98a63a35a08bb3d914407fd768e92020b0ebaa3c8602b3",
|
||||||
0x34383aacbe014a6fed8b5976ccfcdcbb93cef7e41a6634f934432d9ede912c14
|
0x71cd57e697af70f84a960cc94fcbae5889efbf919df410282a04c6d19f92609e
|
||||||
);
|
);
|
||||||
|
|
||||||
// genesis validators 7 validators, DKG #1
|
// genesis validators 7 validators, DKG #1
|
||||||
// exodus session #2
|
// exodus session #11
|
||||||
gatekeeper.verify(
|
gatekeeper.verify(
|
||||||
hex"49f03a6700000000000000000000000000000000000000000000000000000000000000024e8c1fe96d6737cdabbcc96501d6656b9d0b659b3a528ef507f2d52bef29e1570000000000000000000000000000000000000000000000000000000000000000",
|
hex"49f03a67000000000000000000000000000000000000000000000000000000000000000b4e8c1fe96d6737cdabbcc96501d6656b9d0b659b3a528ef507f2d52bef29e1570000000000000000000000000000000000000000000000000000000000000000",
|
||||||
hex"04460e7dce1ebd4cc7527ba80852b6fcdbf8810da3b2173c2f3b3da63206a44407f79d5b5b4c3adcdcce2404c7488240873babb856f065fa551caa6dddea68ca06",
|
hex"04b51db09ec2f4bebdb65c5352efa75f8d07088408e3ff907c74b0d7928a40dbe06f864364a3e6d219849ca8e7d47e6c66f8a18ca9f4fa4752df13bb6c2eae1172",
|
||||||
0x7f6ac70e1e3f3ddd7101a439e7da6f8622a0c8cc38ce42ce81d911d8ad23af90
|
0x072e625bdd3ff7d09f2bf896b5e261efa007f8d81a9e2c828525ec74bd3ee08d
|
||||||
);
|
);
|
||||||
|
|
||||||
(publicKey, parity, session) = gatekeeper.latestPublicKeyInfo();
|
(publicKey, parity, session) = gatekeeper.latestPublicKeyInfo();
|
||||||
assert(publicKey != prevPublicKey);
|
assert(publicKey != prevPublicKey);
|
||||||
assertEq(parity, prevParity);
|
assertEq(parity, prevParity);
|
||||||
assertEq(session, 2);
|
assertEq(session, 11);
|
||||||
|
|
||||||
prevPublicKey = publicKey;
|
prevPublicKey = publicKey;
|
||||||
prevParity = parity;
|
prevParity = parity;
|
||||||
|
|
||||||
// genesis validators 5 validators, DKG #2
|
// genesis validators 5 validators, DKG #2
|
||||||
// exodus session #4
|
// exodus session #19
|
||||||
gatekeeper.verify(
|
gatekeeper.verify(
|
||||||
hex"49f03a6700000000000000000000000000000000000000000000000000000000000000043492bc8bb10848f1f788496ccadde7d458eac4ac2c1eca2dd2c7a506d8fa2c5b0000000000000000000000000000000000000000000000000000000000000000",
|
hex"49f03a6700000000000000000000000000000000000000000000000000000000000000133492bc8bb10848f1f788496ccadde7d458eac4ac2c1eca2dd2c7a506d8fa2c5b0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
hex"0496bacdcc4260a610c9ef465cc1d9f973dda7d4794a65c1d1ea8ea2bb8aa3ddb08d171f5aa267c1ddc64343b4f054424af0c61b57e1f359cd4e23876a0fa4a704",
|
hex"0434c3b37293516c6204dc291821e19ab5b3d8afb0947a0a2dffc91600fbd3f6b9783a66887d515e774b98a63a35a08bb3d914407fd768e92020b0ebaa3c8602b3",
|
||||||
0x34383aacbe014a6fed8b5976ccfcdcbb93cef7e41a6634f934432d9ede912c14
|
0x71cd57e697af70f84a960cc94fcbae5889efbf919df410282a04c6d19f92609e
|
||||||
);
|
);
|
||||||
|
|
||||||
(publicKey, parity, session) = gatekeeper.latestPublicKeyInfo();
|
(publicKey, parity, session) = gatekeeper.latestPublicKeyInfo();
|
||||||
assert(publicKey != prevPublicKey);
|
assert(publicKey != prevPublicKey);
|
||||||
assertEq(parity, prevParity);
|
assertEq(parity, prevParity);
|
||||||
assertEq(session, 4);
|
assertEq(session, 19);
|
||||||
|
|
||||||
// could not apply DKG #0 no more
|
|
||||||
vm.expectRevert();
|
|
||||||
gatekeeper.verify(
|
|
||||||
hex"49f03a670000000000000000000000000000000000000000000000000000000000000000875cdcba4ae5494518fa2602791e667ca0998402b6a69e5b7cb4c72dba4e46690000000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
hex"044d9fa18df9da04381ed256981570a6ef6a0893496ded966fc994662df931ed423b29e2737394b31d8498594caad7b77e36ab31ac79df3796a1b6f26baa5552ae",
|
|
||||||
0x24029a571fcaeadd14f4afe8be62afe3d07876ae270c3caf446ecb6cfc7c08f2
|
|
||||||
);
|
|
||||||
|
|
||||||
// could not apply DKG #1 no more
|
uint256 aliceAmountBefore = staking.recalledAmount(ALICE);
|
||||||
vm.expectRevert();
|
uint256 bobAmountBefore = staking.recalledAmount(BOB);
|
||||||
gatekeeper.verify(
|
|
||||||
hex"49f03a6700000000000000000000000000000000000000000000000000000000000000024e8c1fe96d6737cdabbcc96501d6656b9d0b659b3a528ef507f2d52bef29e1570000000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
hex"04460e7dce1ebd4cc7527ba80852b6fcdbf8810da3b2173c2f3b3da63206a44407f79d5b5b4c3adcdcce2404c7488240873babb856f065fa551caa6dddea68ca06",
|
|
||||||
0x7f6ac70e1e3f3ddd7101a439e7da6f8622a0c8cc38ce42ce81d911d8ad23af90
|
|
||||||
);
|
|
||||||
|
|
||||||
// could not apply DKG #2 no more
|
|
||||||
vm.expectRevert();
|
|
||||||
gatekeeper.verify(
|
|
||||||
hex"49f03a6700000000000000000000000000000000000000000000000000000000000000043492bc8bb10848f1f788496ccadde7d458eac4ac2c1eca2dd2c7a506d8fa2c5b0000000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
hex"0496bacdcc4260a610c9ef465cc1d9f973dda7d4794a65c1d1ea8ea2bb8aa3ddb08d171f5aa267c1ddc64343b4f054424af0c61b57e1f359cd4e23876a0fa4a704",
|
|
||||||
0x34383aacbe014a6fed8b5976ccfcdcbb93cef7e41a6634f934432d9ede912c14
|
|
||||||
);
|
|
||||||
|
|
||||||
uint256 aliceAmountBefore = staking.materializedAmount(ALICE);
|
|
||||||
uint256 bobAmountBefore = staking.materializedAmount(BOB);
|
|
||||||
uint256 aliceBalanceBefore = ALICE.balance;
|
uint256 aliceBalanceBefore = ALICE.balance;
|
||||||
uint256 bobBalanceBefore = BOB.balance;
|
uint256 bobBalanceBefore = BOB.balance;
|
||||||
|
|
||||||
@ -248,20 +364,20 @@ contract GatekeeperTest is Test {
|
|||||||
// amount: 69; commission: 0%; ALICE
|
// amount: 69; commission: 0%; ALICE
|
||||||
// exodus session #1
|
// exodus session #1
|
||||||
gatekeeper.verify(
|
gatekeeper.verify(
|
||||||
hex"39cd99050000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000004500000000000000000000000000000000000000010000000000007a6900000000",
|
hex"bf06188a0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000004500000000000000000000000000000000000000010000000000007a6900000000",
|
||||||
hex"04bf0bcb3d42d8187c543a82ee201a221525047020ff0ac03e4d60e322d3eb6d8ab5bfdae43ea0fbf59128d201b8076727c3c63fe8a5c2b890fdb3c97a7da5f207",
|
hex"04bccb4cd4633e2c61ca5c65006a6a09baa893be219b21509866123a8d6299f92aefe9c380bd9728dbe0b93a8042f431636d08ed191b5c04062bf14e1347059cb9",
|
||||||
0x08f121a1615782c83cce41d7fc2064be57fdeeae8b45f716211b3c8c873f3e4c
|
0xe579753d931a78b143d7e3d86e1117ccbc9d2d7357b0207ea682046a0b06d798
|
||||||
);
|
);
|
||||||
|
|
||||||
assertEq(aliceAmountBefore + 69, staking.materializedAmount(ALICE));
|
assertEq(aliceAmountBefore + 69, staking.recalledAmount(ALICE));
|
||||||
assertEq(bobAmountBefore, staking.materializedAmount(BOB));
|
assertEq(bobAmountBefore, staking.recalledAmount(BOB));
|
||||||
assertEq(aliceBalanceBefore, ALICE.balance);
|
assertEq(aliceBalanceBefore, ALICE.balance);
|
||||||
assertEq(bobBalanceBefore, BOB.balance);
|
assertEq(bobBalanceBefore, BOB.balance);
|
||||||
|
|
||||||
vm.deal(ALICE, 220);
|
vm.deal(ALICE, 220);
|
||||||
|
|
||||||
aliceAmountBefore = staking.materializedAmount(ALICE);
|
aliceAmountBefore = staking.recalledAmount(ALICE);
|
||||||
bobAmountBefore = staking.materializedAmount(BOB);
|
bobAmountBefore = staking.recalledAmount(BOB);
|
||||||
aliceBalanceBefore = ALICE.balance;
|
aliceBalanceBefore = ALICE.balance;
|
||||||
bobBalanceBefore = BOB.balance;
|
bobBalanceBefore = BOB.balance;
|
||||||
|
|
||||||
@ -269,17 +385,143 @@ contract GatekeeperTest is Test {
|
|||||||
|
|
||||||
// execute bridge out, happened on DKG #1
|
// execute bridge out, happened on DKG #1
|
||||||
// amount: 420; commission: 50%; BOB
|
// amount: 420; commission: 50%; BOB
|
||||||
// exodus session #3
|
// exodus session #12
|
||||||
vm.prank(ALICE, ALICE);
|
vm.prank(ALICE, ALICE);
|
||||||
gatekeeper.verify{value: buyback}(
|
gatekeeper.verify{value: buyback}(
|
||||||
hex"39cd9905000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000001a400000000000000000000000000000000000000020000000000007a6980000000",
|
hex"bf06188a000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000001a400000000000000000000000000000000000000020000000000007a6980000000",
|
||||||
hex"04089940ed9bb8d7f3a77adfafb0d5ebc45af558ade46844856c049aa912b9d8401826e17f24c0aa625524ff4a65b430a19422b4f9754cec3b862641f17f99b1a9",
|
hex"049f458500a78f56420380f0c6863905f1181e705f6b562eb469bd81a03676a2d9b436a53d2356ea0a5bb175ca837655fab8114c6e64cfdd47f3d36d1a945d7122",
|
||||||
0x9d2cf40ab0b1820858fe6f8950e2f511b4acb57b00b37bb93760978e2add4243
|
0x772731b359ee7ecac7830438333e4ffb61759b36610f69cef20f66f7a79a405f
|
||||||
);
|
);
|
||||||
|
|
||||||
assertEq(aliceAmountBefore + 210, staking.materializedAmount(ALICE));
|
assertEq(aliceAmountBefore + 210, staking.recalledAmount(ALICE));
|
||||||
assertEq(bobAmountBefore + 210, staking.materializedAmount(BOB));
|
assertEq(bobAmountBefore + 210, staking.recalledAmount(BOB));
|
||||||
assertEq(aliceBalanceBefore - buyback, ALICE.balance);
|
assertEq(aliceBalanceBefore - buyback, ALICE.balance);
|
||||||
assertEq(bobBalanceBefore + buyback, BOB.balance);
|
assertEq(bobBalanceBefore + buyback, BOB.balance);
|
||||||
|
|
||||||
|
// execute setDistributor, exodusSession: 2
|
||||||
|
vm.prank(ALICE);
|
||||||
|
gatekeeper.verify(
|
||||||
|
hex"3137142600000000000000000000000000000000000000000000000000000000000000020000000000007a690101010101010101010101010101010101010101000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002475619ab5000000000000000000000000010101010101010101010101010101010101010100000000000000000000000000000000000000000000000000000000",
|
||||||
|
hex"049f222e7ec776c390c7fe96b9c2034c37f4fc71a9714588a5d98ccb50dc748c98fe375c628e7bcc4db4f204a2a168b22ecbc631616a69987ef42601ad239b2ab5",
|
||||||
|
0x515539c5b713900a4dd4b3b119b22a47f862c90fdff9da91ed3fca687ea8db06
|
||||||
|
);
|
||||||
|
|
||||||
|
// execute setWarmupPeriod, exodusSession: 3
|
||||||
|
vm.prank(ALICE);
|
||||||
|
gatekeeper.verify(
|
||||||
|
hex"3137142600000000000000000000000000000000000000000000000000000000000000030000000000007a6901010101010101010101010101010101010101010000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000024a85667c5000000000000000000000000000000000000000000000000000000000000004500000000000000000000000000000000000000000000000000000000",
|
||||||
|
hex"04c2d9b903c0bd640ff858de3518c64965e2ff07215a131c85a8febec629999cfd60970a3a99d0d04ba08a334f2aa865842ebde37407c6fee2b17f820c8104fc88",
|
||||||
|
0xe1727e132cbb7502a60bab56c5dedce1abca8130792ddd6d9ba59918766688e8
|
||||||
|
);
|
||||||
|
|
||||||
|
// execute setBounty, exodusSession: 4
|
||||||
|
vm.prank(ALICE);
|
||||||
|
gatekeeper.verify(
|
||||||
|
hex"3137142600000000000000000000000000000000000000000000000000000000000000040000000000007a69010101010101010101010101010101010101010100000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000245d87d363000000000000000000000000000000000000000000000000000000000000004500000000000000000000000000000000000000000000000000000000",
|
||||||
|
hex"04ec7d454696833a1c9c04d79be81e0dfe8a4791862f1dc5d4e908338df30621911fb182e6f204699c53ea5b11db1c855f55dfd149d4882a80afa437d80c58d9cd",
|
||||||
|
0x22d3138ed4e72f104c5d86814c1b4e6eeed39cdcf97e087effe33f860d06f133
|
||||||
|
);
|
||||||
|
|
||||||
|
// execute setAdjustment, exodusSession: 5
|
||||||
|
vm.prank(ALICE);
|
||||||
|
gatekeeper.verify(
|
||||||
|
hex"3137142600000000000000000000000000000000000000000000000000000000000000050000000000007a690101010101010101010101010101010101010101000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000006446d36ab5000000000000000000000000000000000000000000000000000000000000004500000000000000000000000000000000000000000000000000000000000001a4000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000",
|
||||||
|
hex"0484819be6cb8879179f91aaf94c1513234d9068052dd42bf067606033c2d173efe85e87acf5bc1020cab6ec67d5ec8f7b237c5f315b825b56e915e4736c2b7b62",
|
||||||
|
0x865b1e9006973e8b5b5492f2f6a0c85ee69ee7ae72f714ee076035865cd26a72
|
||||||
|
);
|
||||||
|
|
||||||
|
// execute updateGatekeeperAddress, exodusSession: 6
|
||||||
|
vm.prank(ALICE);
|
||||||
|
gatekeeper.verify(
|
||||||
|
hex"3137142600000000000000000000000000000000000000000000000000000000000000060000000000007a690101010101010101010101010101010101010101000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002484f157cd000000000000000000000000010101010101010101010101010101010101010100000000000000000000000000000000000000000000000000000000",
|
||||||
|
hex"040bfec065e85cb245d891a94b50b12ea8f91d27a90e113c0bf47f93e76c0c21380e6ff8dc05406d1e8ec34b2c90cc345fea1245c1ca3eb7ecccd8606bfb334f76",
|
||||||
|
0x15141679af9a42a9228adc686f802fe183515180bed3de3933e1622668e06796
|
||||||
|
);
|
||||||
|
|
||||||
|
// execute addPool, exodusSession: 7
|
||||||
|
vm.prank(ALICE);
|
||||||
|
gatekeeper.verify(
|
||||||
|
hex"3137142600000000000000000000000000000000000000000000000000000000000000070000000000007a6901010101010101010101010101010101010101010000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000024d914cd4b000000000000000000000000010101010101010101010101010101010101010100000000000000000000000000000000000000000000000000000000",
|
||||||
|
hex"04eb497652f4ad64ddd9223cd1af4785b37c6b2a41f8c1f54b906d595ab0d2b53305cd006238ca4eaf7eede77b3e2c0d1019e9f9d505fe80e86240e0d83051dcf2",
|
||||||
|
0xa93dcb9c9c399973a17abc3b3667e37da716bdd5e9ed9c0b99d9fc869a886700
|
||||||
|
);
|
||||||
|
|
||||||
|
// execute removePool, exodusSession: 8
|
||||||
|
vm.prank(ALICE);
|
||||||
|
gatekeeper.verify(
|
||||||
|
hex"3137142600000000000000000000000000000000000000000000000000000000000000080000000000007a6901010101010101010101010101010101010101010000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000024a38dcbd0000000000000000000000000000000000000000000000000000000000000053900000000000000000000000000000000000000000000000000000000",
|
||||||
|
hex"0446039e3eec08f356c0a6e9fdd243aed6d840c1d9d973d1496db70499061055d45faf8e945d2fb87016cdcfda040df8fcaf22fe9a466dbdd4687cbfde7c87d0d8",
|
||||||
|
0x110eedf82f6533d95a43a19de3da026c51557e552bd4c5b6f06ca94d6ab2c8db
|
||||||
|
);
|
||||||
|
|
||||||
|
// execute close, exodusSession: 9
|
||||||
|
vm.prank(ALICE);
|
||||||
|
gatekeeper.verify(
|
||||||
|
hex"3137142600000000000000000000000000000000000000000000000000000000000000090000000000007a69010101010101010101010101010101010101010100000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000240aebeb4e00000000000000000000000000000000000000000000000000000000000001a400000000000000000000000000000000000000000000000000000000",
|
||||||
|
hex"048da83909e645cf4313d00985c86bcbd24f584512f2279476767fb94829a2a9c7b95bcfeacc3bee2fe4710bfc2bcf2e72e58bcd37fc02293b660a7b70e2efd184",
|
||||||
|
0x8a28d3b7b5db6564e4abb846bc662a69d9b52ed08bd2ce2dbc6af4e86167f55e
|
||||||
|
);
|
||||||
|
|
||||||
|
// execute close, exodusSession: 10
|
||||||
|
vm.prank(ALICE);
|
||||||
|
gatekeeper.verify(
|
||||||
|
hex"31371426000000000000000000000000000000000000000000000000000000000000000a0000000000007a6901010101010101010101010101010101010101010000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000144a0139f41000000000000000000000000000000000000000000000000000000000000004500000000000000000000000000000000000000000000000000000000000001a4000000000000000000000000000000000000000000000000000000000000053900000000000000000000000000000000000000000000000000000000000001a400000000000000000000000000000000000000000000000000000000000005390000000000000000000000000101010101010101010101010101010101010101000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000230000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000",
|
||||||
|
hex"0417bd8b7509486284c1f613117c61abc39cee8a24422fce457099e430b805468be33547f272d8123e9aa6f8895babbaa3a3731eaeda7d8ad4286c9fb117034d21",
|
||||||
|
0x0b71c4d8ca7748ec35257a42f26e09aaab9fa4a03e40d50a96bfdfdce10ce7e5
|
||||||
|
);
|
||||||
|
|
||||||
|
// execute enable, exodusSession: 13
|
||||||
|
vm.prank(ALICE);
|
||||||
|
gatekeeper.verify(
|
||||||
|
hex"31371426000000000000000000000000000000000000000000000000000000000000000d0000000000007a6901010101010101010101010101010101010101010000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064e4e33ef800000000000000000000000000000000000000000000000000000000000000010000000000000000000000000101010101010101010101010101010101010101000000000000000000000000010101010101010101010101010101010101010100000000000000000000000000000000000000000000000000000000",
|
||||||
|
hex"04f34417b947a4da097ca9596838430bf40587339fb42a09210ceb0dbbcfd98a99b1e8fd8f4d2301ee5b5d304e1d8b8e8b81ac14688f1e7de41d65c09a65975f01",
|
||||||
|
0xa4084ea553df2f808c86437547550be910554d41d33a5d66a4643c9792c4f56f
|
||||||
|
);
|
||||||
|
|
||||||
|
// execute disable, exodusSession: 14
|
||||||
|
vm.prank(ALICE);
|
||||||
|
gatekeeper.verify(
|
||||||
|
hex"31371426000000000000000000000000000000000000000000000000000000000000000e0000000000007a6901010101010101010101010101010101010101010000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044529918310000000000000000000000000000000000000000000000000000000000000003000000000000000000000000010101010101010101010101010101010101010100000000000000000000000000000000000000000000000000000000",
|
||||||
|
hex"04f94dad10974c9f0b9307444dc4c660f1f169b25fd3ed9fd563df74714e73c2d4e92b3429505989327d449d9a68e6cce0f557574c92c0ca7bc8126dd710094e9e",
|
||||||
|
0xb6b75ed763f4d06b9d7a7557ca8d095df656ca25db74645fb47a0b45d27ec501
|
||||||
|
);
|
||||||
|
|
||||||
|
// execute forfeitReserves, exodusSession: 15
|
||||||
|
vm.prank(ALICE);
|
||||||
|
gatekeeper.verify(
|
||||||
|
hex"31371426000000000000000000000000000000000000000000000000000000000000000f0000000000007a6901010101010101010101010101010101010101010000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064f0de236c000000000000000000000000010101010101010101010101010101010101010100000000000000000000000000000000000000000000000000000000000001a4000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000",
|
||||||
|
hex"04af06146f4ca63ef11b82ad891d8e03525a11922316a7dd3f71ff4b6aa5ee6793d29e6af2355d26bc173bb52c1f5eb7d8b279ed40bef2d1cd521da33517b6d3cd",
|
||||||
|
0x6343b047bb8fe39200758de1e874ddfaf296e1f9259f0b720a070a69e7a3ee08
|
||||||
|
);
|
||||||
|
|
||||||
|
// execute redeemReserve, exodusSession: 16
|
||||||
|
vm.prank(ALICE);
|
||||||
|
gatekeeper.verify(
|
||||||
|
hex"3137142600000000000000000000000000000000000000000000000000000000000000100000000000007a690101010101010101010101010101010101010101000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000004482a21577000000000000000000000000010101010101010101010101010101010101010100000000000000000000000000000000000000000000000000000000000001a400000000000000000000000000000000000000000000000000000000",
|
||||||
|
hex"041ff894074000f2d934cf3b136165766044b177e69de4c36fac4d1265b855e1a5b4b7b2b25400fbf4d5eb4619039ffc95555db216991d3050bb6fd966f6e3eeab",
|
||||||
|
0x7d31ebf1e510616e541795b6448d387d2119b3fdd0124e5116c53726b6b1cc15
|
||||||
|
);
|
||||||
|
|
||||||
|
// execute withdraw, exodusSession: 17
|
||||||
|
vm.prank(ALICE);
|
||||||
|
gatekeeper.verify(
|
||||||
|
hex"3137142600000000000000000000000000000000000000000000000000000000000000110000000000007a6901010101010101010101010101010101010101010000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044f3fef3a3000000000000000000000000010101010101010101010101010101010101010100000000000000000000000000000000000000000000000000000000000001a400000000000000000000000000000000000000000000000000000000",
|
||||||
|
hex"04f31030cf8563dd976d1a823a84e43ebdcc13bb5e5e0d7a2ff372006c71e6ab3d4d0770771682dcd6eff9e13fb3d113b484d7384d08f247760b07149dca1aa234",
|
||||||
|
0x06aad271d98e69d1ff18bf39e6713f347aba0df04d99bc4a9304b780431d12ae
|
||||||
|
);
|
||||||
|
|
||||||
|
// execute auditReserves, exodusSession: 18
|
||||||
|
vm.prank(ALICE);
|
||||||
|
gatekeeper.verify(
|
||||||
|
hex"3137142600000000000000000000000000000000000000000000000000000000000000120000000000007a69010101010101010101010101010101010101010100000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000042b7ce50000000000000000000000000000000000000000000000000000000000",
|
||||||
|
hex"049e5d54fd32dcec64b20943a61763dc760b1e3e9d31948780e2193480d904984c0cdaa8074196c06104557b1a04de6aeeda135e0c3413192f9e4331f3b7447ff2",
|
||||||
|
0x43ed5301b16b4b2bc91c28dcfbaecc9949a7fc8fe5c57dfef6229f62493da6d2
|
||||||
|
);
|
||||||
|
|
||||||
|
address storageHistory = gatekeeper.storageHistory();
|
||||||
|
for (uint256 exodusSession; exodusSession < 19; exodusSession++) {
|
||||||
|
assert(IStorageHistory(storageHistory).isTransactionExecuted(exodusSession));
|
||||||
|
}
|
||||||
|
assert(IStorageHistory(storageHistory).isTransactionExecuted(19));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,36 +4,47 @@ import {Test} from "forge-std/Test.sol";
|
|||||||
|
|
||||||
import {Gatekeeper} from "../../src/Gatekeeper.sol";
|
import {Gatekeeper} from "../../src/Gatekeeper.sol";
|
||||||
import {IStorageHistory} from "../../src/interfaces/IStorageHistory.sol";
|
import {IStorageHistory} from "../../src/interfaces/IStorageHistory.sol";
|
||||||
|
import {IGatekeeper} from "../../src/interfaces/IGatekeeper.sol";
|
||||||
import {RequestPacking} from "../../src/libraries/Packing.sol";
|
import {RequestPacking} from "../../src/libraries/Packing.sol";
|
||||||
|
import {StorageHistory} from "../../src/types/StorageHistory.sol";
|
||||||
|
|
||||||
contract MockStaking is Test {
|
contract MockStaking is Test {
|
||||||
Gatekeeper public gatekeeper;
|
Gatekeeper public gatekeeper;
|
||||||
|
|
||||||
mapping(address => uint256) private _materializedAmounts;
|
mapping(address => uint256) private _recalledAmounts;
|
||||||
|
|
||||||
constructor(uint256 existential) {
|
constructor(uint256 existential) {
|
||||||
gatekeeper = new Gatekeeper(existential, address(0));
|
StorageHistory history = new StorageHistory();
|
||||||
|
gatekeeper = new Gatekeeper(existential, address(history));
|
||||||
|
gatekeeper.initialize(address(0));
|
||||||
|
history.setOwner(address(gatekeeper));
|
||||||
}
|
}
|
||||||
|
|
||||||
function redoGatekeeper(uint256 existential) external {
|
function redoGatekeeper(uint256 existential) external {
|
||||||
gatekeeper = new Gatekeeper(existential, address(gatekeeper));
|
Gatekeeper newGatekeeper = new Gatekeeper(existential, address(0));
|
||||||
|
newGatekeeper.initialize(address(gatekeeper));
|
||||||
|
|
||||||
|
address storageHistory = IGatekeeper(gatekeeper).storageHistory();
|
||||||
|
IStorageHistory(storageHistory).setOwner(address(newGatekeeper));
|
||||||
|
|
||||||
|
gatekeeper = newGatekeeper;
|
||||||
}
|
}
|
||||||
|
|
||||||
function runGhost(bytes32 receiver, uint256 amount) external {
|
function runGhost(bytes32 receiver, uint256 amount) external {
|
||||||
gatekeeper.ghost(receiver, amount);
|
gatekeeper.ghost(receiver, amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
function runMaterialize(uint256 exodusSession, uint256 amount, uint256 packed) external {
|
function runRecall(uint256 exodusSession, uint256 amount, uint256 packed) external {
|
||||||
vm.prank(address(gatekeeper));
|
vm.prank(address(gatekeeper));
|
||||||
gatekeeper.materialize(exodusSession, amount, packed);
|
gatekeeper.recall(exodusSession, amount, packed);
|
||||||
}
|
}
|
||||||
|
|
||||||
function materialize(address receiver, uint256 amount) external {
|
function recall(address receiver, uint256 amount) external {
|
||||||
_materializedAmounts[receiver] += amount;
|
_recalledAmounts[receiver] += amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
function materializedAmount(address who) external view returns (uint256) {
|
function recalledAmount(address who) external view returns (uint256) {
|
||||||
return _materializedAmounts[who];
|
return _recalledAmounts[who];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,10 +97,10 @@ contract GatekeeperStorageHistoryTest is Test {
|
|||||||
} else {
|
} else {
|
||||||
// forge-lint: disable-next-line(unsafe-typecast)
|
// forge-lint: disable-next-line(unsafe-typecast)
|
||||||
uint256 packed = RequestPacking.pack(0, uint64(block.chainid), BOB);
|
uint256 packed = RequestPacking.pack(0, uint64(block.chainid), BOB);
|
||||||
uint256 previousAmount = staking.materializedAmount(BOB);
|
uint256 previousAmount = staking.recalledAmount(BOB);
|
||||||
|
|
||||||
staking.runMaterialize(exodusSession, amountToMaterialize, packed);
|
staking.runRecall(exodusSession, amountToMaterialize, packed);
|
||||||
assertEq(previousAmount + amountToMaterialize, staking.materializedAmount(BOB));
|
assertEq(previousAmount + amountToMaterialize, staking.recalledAmount(BOB));
|
||||||
|
|
||||||
// forge-lint: disable-next-line(unsafe-typecast)
|
// forge-lint: disable-next-line(unsafe-typecast)
|
||||||
amountOut += uint104(amountToMaterialize);
|
amountOut += uint104(amountToMaterialize);
|
||||||
@ -104,7 +115,7 @@ contract GatekeeperStorageHistoryTest is Test {
|
|||||||
function test_inheritanceWorksForHistoricalStorge() public {
|
function test_inheritanceWorksForHistoricalStorge() public {
|
||||||
uint256 exodusSession = 69;
|
uint256 exodusSession = 69;
|
||||||
uint256 packed = RequestPacking.pack(0, uint64(block.chainid), BOB);
|
uint256 packed = RequestPacking.pack(0, uint64(block.chainid), BOB);
|
||||||
staking.runMaterialize(exodusSession, INIT_AMOUNT, packed);
|
staking.runRecall(exodusSession, INIT_AMOUNT, packed);
|
||||||
staking.runGhost(bytes32(abi.encodePacked(ALICE)), INIT_AMOUNT);
|
staking.runGhost(bytes32(abi.encodePacked(ALICE)), INIT_AMOUNT);
|
||||||
|
|
||||||
address prevStorageHistory = gatekeeper.storageHistory();
|
address prevStorageHistory = gatekeeper.storageHistory();
|
||||||
|
|||||||
@ -7,13 +7,17 @@ import {Hashes} from "../../src/libraries/Hashes.sol";
|
|||||||
import {Checkpoints} from "../../src/libraries/Checkpoints.sol";
|
import {Checkpoints} from "../../src/libraries/Checkpoints.sol";
|
||||||
import {IGatekeeper} from "../../src/interfaces/IGatekeeper.sol";
|
import {IGatekeeper} from "../../src/interfaces/IGatekeeper.sol";
|
||||||
import {IStorageHistory} from "../../src/interfaces/IStorageHistory.sol";
|
import {IStorageHistory} from "../../src/interfaces/IStorageHistory.sol";
|
||||||
|
import {StorageHistory} from "../../src/types/StorageHistory.sol";
|
||||||
|
|
||||||
contract MockStaking {
|
contract MockStaking {
|
||||||
GatekeeperVerification public gatekeeper;
|
GatekeeperWeaver public gatekeeper;
|
||||||
address public governor;
|
address public governor;
|
||||||
|
|
||||||
constructor(uint256 existential) {
|
constructor(uint256 existential) {
|
||||||
gatekeeper = new GatekeeperVerification(existential, address(0));
|
StorageHistory history = new StorageHistory();
|
||||||
|
gatekeeper = new GatekeeperWeaver(existential, address(history));
|
||||||
|
gatekeeper.initialize(address(0));
|
||||||
|
history.setOwner(address(gatekeeper));
|
||||||
governor = msg.sender;
|
governor = msg.sender;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22,20 +26,24 @@ contract MockStaking {
|
|||||||
return gatekeeper.ghost(receiver, amount);
|
return gatekeeper.ghost(receiver, amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createNewGatekeeper(uint256 existential, address previousGatekeeper) external {
|
function createNewGatekeeper(uint256 existential) external {
|
||||||
require(msg.sender == governor);
|
require(msg.sender == governor);
|
||||||
GatekeeperVerification newGatekeeper = new GatekeeperVerification(existential, address(gatekeeper));
|
|
||||||
address storageHistory = IGatekeeper(previousGatekeeper).storageHistory();
|
GatekeeperWeaver newGatekeeper = new GatekeeperWeaver(existential, address(0));
|
||||||
|
newGatekeeper.initialize(address(gatekeeper));
|
||||||
|
|
||||||
|
address storageHistory = IGatekeeper(gatekeeper).storageHistory();
|
||||||
IStorageHistory(storageHistory).setOwner(address(newGatekeeper));
|
IStorageHistory(storageHistory).setOwner(address(newGatekeeper));
|
||||||
|
|
||||||
gatekeeper = newGatekeeper;
|
gatekeeper = newGatekeeper;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
contract GatekeeperVerification is Gatekeeper {
|
contract GatekeeperWeaver is Gatekeeper {
|
||||||
using Checkpoints for Checkpoints.Trace256;
|
using Checkpoints for Checkpoints.Trace256;
|
||||||
using Checkpoints for Checkpoints.Trace160;
|
using Checkpoints for Checkpoints.Trace160;
|
||||||
|
|
||||||
constructor(uint256 existential, address previousWeaver) Gatekeeper(existential, previousWeaver) {}
|
constructor(uint256 existential, address storageHistory) Gatekeeper(existential, storageHistory) {}
|
||||||
|
|
||||||
function filledEntries(uint256 session) public view returns (uint256) {
|
function filledEntries(uint256 session) public view returns (uint256) {
|
||||||
return _filledEntries[session];
|
return _filledEntries[session];
|
||||||
@ -111,7 +119,7 @@ contract GatekeeperWeaverTest is Test {
|
|||||||
uint256 constant AMOUNT = 1 * 1e7;
|
uint256 constant AMOUNT = 1 * 1e7;
|
||||||
|
|
||||||
MockStaking staking;
|
MockStaking staking;
|
||||||
GatekeeperVerification gatekeeper;
|
GatekeeperWeaver gatekeeper;
|
||||||
|
|
||||||
function setUp() public {
|
function setUp() public {
|
||||||
vm.prank(ALICE);
|
vm.prank(ALICE);
|
||||||
@ -156,7 +164,7 @@ contract GatekeeperWeaverTest is Test {
|
|||||||
vm.roll(block.number + 420);
|
vm.roll(block.number + 420);
|
||||||
|
|
||||||
vm.prank(ALICE);
|
vm.prank(ALICE);
|
||||||
staking.createNewGatekeeper(EXISTENTIAL, address(gatekeeper));
|
staking.createNewGatekeeper(EXISTENTIAL);
|
||||||
gatekeeper = staking.gatekeeper();
|
gatekeeper = staking.gatekeeper();
|
||||||
|
|
||||||
uint256 finalSession = gatekeeper.currentWeavingSession();
|
uint256 finalSession = gatekeeper.currentWeavingSession();
|
||||||
|
|||||||
@ -9,10 +9,13 @@ import {GhostAuthority} from "../../src/GhostAuthority.sol";
|
|||||||
import {GhostDistributor} from "../../src/StakingDistributor.sol";
|
import {GhostDistributor} from "../../src/StakingDistributor.sol";
|
||||||
import {GhostTreasury} from "../../src/Treasury.sol";
|
import {GhostTreasury} from "../../src/Treasury.sol";
|
||||||
import {GhostStaking} from "../../src/Staking.sol";
|
import {GhostStaking} from "../../src/Staking.sol";
|
||||||
|
import {Gatekeeper} from "../../src/Gatekeeper.sol";
|
||||||
import {ERC20Mock} from "../../src/mocks/ERC20Mock.sol";
|
import {ERC20Mock} from "../../src/mocks/ERC20Mock.sol";
|
||||||
import {GhostBondingCalculator} from "../../src/StandardBondingCalculator.sol";
|
import {GhostBondingCalculator} from "../../src/StandardBondingCalculator.sol";
|
||||||
|
|
||||||
import {ITreasury} from "../../src/interfaces/ITreasury.sol";
|
import {ITreasury} from "../../src/interfaces/ITreasury.sol";
|
||||||
|
import {IGatekeeper} from "../../src/interfaces/IGatekeeper.sol";
|
||||||
|
import {IStorageHistory} from "../../src/interfaces/IStorageHistory.sol";
|
||||||
import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
|
import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
|
||||||
|
|
||||||
contract RebaseBatcher {
|
contract RebaseBatcher {
|
||||||
@ -588,9 +591,25 @@ contract StakingTest is Test {
|
|||||||
|
|
||||||
function test_GOVERNORCouldSetGatekeeper() public {
|
function test_GOVERNORCouldSetGatekeeper() public {
|
||||||
address previousGatekeeper = staking.gatekeeper();
|
address previousGatekeeper = staking.gatekeeper();
|
||||||
|
address storageHistory = IGatekeeper(previousGatekeeper).storageHistory();
|
||||||
|
|
||||||
|
vm.prank(address(previousGatekeeper));
|
||||||
|
IStorageHistory(storageHistory).trySetTransactionExecuted(34);
|
||||||
|
|
||||||
|
Gatekeeper newGatekeeper = new Gatekeeper(420, address(0));
|
||||||
|
|
||||||
vm.prank(GOVERNOR);
|
vm.prank(GOVERNOR);
|
||||||
staking.setGatekeeperAddress(420);
|
staking.updateGatekeeperAddress(address(newGatekeeper));
|
||||||
|
|
||||||
assert(staking.gatekeeper() != previousGatekeeper);
|
assert(staking.gatekeeper() != previousGatekeeper);
|
||||||
|
assertEq(staking.gatekeeper(), address(newGatekeeper));
|
||||||
|
assertEq(newGatekeeper.previousAddress(), previousGatekeeper);
|
||||||
|
|
||||||
|
vm.prank(address(newGatekeeper));
|
||||||
|
IStorageHistory(storageHistory).trySetTransactionExecuted(35);
|
||||||
|
|
||||||
|
assert(IStorageHistory(storageHistory).isTransactionExecuted(34));
|
||||||
|
assert(IStorageHistory(storageHistory).isTransactionExecuted(35));
|
||||||
}
|
}
|
||||||
|
|
||||||
function test_couldNotGhostTokensIfNoGhst() public {
|
function test_couldNotGhostTokensIfNoGhst() public {
|
||||||
|
|||||||
@ -139,29 +139,11 @@ contract StakingDistributorTest is Test {
|
|||||||
assertEq(distributor.bounty(), 1337);
|
assertEq(distributor.bounty(), 1337);
|
||||||
}
|
}
|
||||||
|
|
||||||
function test_setPools_shouldRevertIfNotGovernor(address who) public {
|
|
||||||
vm.assume(who != GOVERNOR);
|
|
||||||
address[] memory newPools = new address[](1);
|
|
||||||
newPools[0] = who;
|
|
||||||
vm.expectRevert();
|
|
||||||
vm.prank(who);
|
|
||||||
distributor.setPools(newPools);
|
|
||||||
}
|
|
||||||
|
|
||||||
function test_setPools_GOVERNORShouldSet() public {
|
|
||||||
address[] memory newPools = new address[](1);
|
|
||||||
newPools[0] = OTHER;
|
|
||||||
vm.prank(GOVERNOR);
|
|
||||||
distributor.setPools(newPools);
|
|
||||||
assertEq(distributor.pools(0), OTHER);
|
|
||||||
}
|
|
||||||
|
|
||||||
function test_removePools_shouldRevertIfNotGovernor(address who) public {
|
function test_removePools_shouldRevertIfNotGovernor(address who) public {
|
||||||
vm.assume(who != GOVERNOR);
|
vm.assume(who != GOVERNOR);
|
||||||
address[] memory newPools = new address[](1);
|
|
||||||
newPools[0] = OTHER;
|
|
||||||
vm.prank(GOVERNOR);
|
vm.prank(GOVERNOR);
|
||||||
distributor.setPools(newPools);
|
distributor.addPool(OTHER);
|
||||||
|
assertEq(distributor.pools(0), OTHER);
|
||||||
|
|
||||||
vm.expectRevert();
|
vm.expectRevert();
|
||||||
vm.prank(who);
|
vm.prank(who);
|
||||||
@ -169,11 +151,11 @@ contract StakingDistributorTest is Test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function test_removePools_GOVERNORShouldRemove() public {
|
function test_removePools_GOVERNORShouldRemove() public {
|
||||||
address[] memory newPools = new address[](1);
|
vm.prank(GOVERNOR);
|
||||||
newPools[0] = OTHER;
|
distributor.addPool(OTHER);
|
||||||
vm.startPrank(GOVERNOR);
|
|
||||||
distributor.setPools(newPools);
|
|
||||||
assertEq(distributor.pools(0), OTHER);
|
assertEq(distributor.pools(0), OTHER);
|
||||||
|
|
||||||
|
vm.prank(GOVERNOR);
|
||||||
distributor.removePool(0);
|
distributor.removePool(0);
|
||||||
vm.stopPrank();
|
vm.stopPrank();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -203,43 +203,6 @@ contract StinkyTest is Test, ERC20PermitTest, ERC20AllowanceTest, ERC20TransferT
|
|||||||
assertEq(stnk.balanceForShares(amountToTest), amountToTest / sharesPerUnit);
|
assertEq(stnk.balanceForShares(amountToTest), amountToTest / sharesPerUnit);
|
||||||
}
|
}
|
||||||
|
|
||||||
function test_debt_couldBeChangedByTreasury() public {
|
|
||||||
_mintTokens(ALICE, AMOUNT);
|
|
||||||
assertEq(stnk.debtBalances(ALICE), 0);
|
|
||||||
vm.prank(TREASURY);
|
|
||||||
stnk.changeDebt(AMOUNT, ALICE, true);
|
|
||||||
assertEq(stnk.debtBalances(ALICE), AMOUNT);
|
|
||||||
vm.prank(TREASURY);
|
|
||||||
stnk.changeDebt(AMOUNT, ALICE, false);
|
|
||||||
assertEq(stnk.debtBalances(ALICE), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
function test_debt_couldNotBeChangeByArbitraryAddress(address someone) public {
|
|
||||||
vm.assume(someone != TREASURY);
|
|
||||||
_mintTokens(ALICE, AMOUNT);
|
|
||||||
assertEq(stnk.debtBalances(ALICE), 0);
|
|
||||||
vm.expectRevert();
|
|
||||||
vm.prank(someone);
|
|
||||||
stnk.changeDebt(AMOUNT, ALICE, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
function test_balance_couldNotDropBelowDebt() public {
|
|
||||||
_mintTokens(ALICE, AMOUNT * 3);
|
|
||||||
vm.prank(TREASURY);
|
|
||||||
stnk.changeDebt(AMOUNT, ALICE, true);
|
|
||||||
assertEq(stnk.balanceOf(BOB), 0);
|
|
||||||
|
|
||||||
vm.prank(ALICE);
|
|
||||||
stnk.safeTransfer(BOB, AMOUNT);
|
|
||||||
assertEq(stnk.balanceOf(BOB), AMOUNT);
|
|
||||||
|
|
||||||
vm.expectRevert();
|
|
||||||
vm.prank(ALICE);
|
|
||||||
assertEq(stnk.transfer(BOB, AMOUNT * 2), false);
|
|
||||||
assertEq(stnk.balanceOf(BOB), AMOUNT);
|
|
||||||
assertEq(stnk.balanceOf(ALICE), AMOUNT * 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
function test_rebase_couldNotBeDoneFromArbitraryAddress(address someone) public {
|
function test_rebase_couldNotBeDoneFromArbitraryAddress(address someone) public {
|
||||||
vm.assume(someone != address(staking));
|
vm.assume(someone != address(staking));
|
||||||
vm.expectRevert();
|
vm.expectRevert();
|
||||||
|
|||||||
@ -72,55 +72,6 @@ contract GhostTreasuryTest is Test {
|
|||||||
assertEq(reserve.balanceOf(address(treasury)), AMOUNT);
|
assertEq(reserve.balanceOf(address(treasury)), AMOUNT);
|
||||||
}
|
}
|
||||||
|
|
||||||
function test_withdraw_onlyIfApprovedTokenAndApprovedAddress() public {
|
|
||||||
vm.startPrank(GOVERNOR);
|
|
||||||
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, ALICE, address(0));
|
|
||||||
treasury.enable(ITreasury.STATUS.RESERVETOKEN, address(reserve), address(calculator));
|
|
||||||
vm.stopPrank();
|
|
||||||
|
|
||||||
vm.prank(ALICE);
|
|
||||||
treasury.deposit(address(reserve), AMOUNT, 0);
|
|
||||||
|
|
||||||
vm.expectRevert();
|
|
||||||
vm.prank(ALICE);
|
|
||||||
treasury.withdraw(address(reserve), AMOUNT);
|
|
||||||
|
|
||||||
vm.prank(GOVERNOR);
|
|
||||||
treasury.enable(ITreasury.STATUS.RESERVESPENDER, ALICE, address(0));
|
|
||||||
|
|
||||||
vm.prank(ALICE);
|
|
||||||
treasury.withdraw(address(reserve), AMOUNT);
|
|
||||||
|
|
||||||
assertEq(ftso.balanceOf(ALICE), 0);
|
|
||||||
assertEq(reserve.balanceOf(address(treasury)), 0);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function test_manage_onlyIfApprovedTokenAndApprovedAddress() public {
|
|
||||||
vm.startPrank(GOVERNOR);
|
|
||||||
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, ALICE, address(0));
|
|
||||||
treasury.enable(ITreasury.STATUS.RESERVETOKEN, address(reserve), address(calculator));
|
|
||||||
vm.stopPrank();
|
|
||||||
|
|
||||||
uint256 tokenValue = treasury.tokenValue(address(reserve), AMOUNT);
|
|
||||||
vm.prank(ALICE);
|
|
||||||
uint256 send = treasury.deposit(address(reserve), AMOUNT, tokenValue);
|
|
||||||
|
|
||||||
vm.expectRevert();
|
|
||||||
vm.prank(ALICE);
|
|
||||||
treasury.manage(address(reserve), AMOUNT);
|
|
||||||
|
|
||||||
vm.prank(GOVERNOR);
|
|
||||||
treasury.enable(ITreasury.STATUS.RESERVEMANAGER, ALICE, address(0));
|
|
||||||
|
|
||||||
vm.prank(ALICE);
|
|
||||||
treasury.manage(address(reserve), AMOUNT);
|
|
||||||
|
|
||||||
assertEq(ftso.balanceOf(ALICE), send);
|
|
||||||
assertEq(reserve.balanceOf(ALICE), AMOUNT);
|
|
||||||
assertEq(reserve.balanceOf(address(treasury)), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
function test_mint_onlyIfApprovedTokenAndApprovedAddress() public {
|
function test_mint_onlyIfApprovedTokenAndApprovedAddress() public {
|
||||||
vm.startPrank(GOVERNOR);
|
vm.startPrank(GOVERNOR);
|
||||||
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, ALICE, address(0));
|
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, ALICE, address(0));
|
||||||
@ -287,39 +238,21 @@ contract GhostTreasuryTest is Test {
|
|||||||
assertEq(liquidityValue - reserveEps <= reserves, true);
|
assertEq(liquidityValue - reserveEps <= reserves, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
function test_randomAddressCouldNotTriggerTimelock(address who) public {
|
function test_withdraw_onlyIfApprovedTokenAndApprovedAddress() public {
|
||||||
vm.assume(who != GOVERNOR);
|
vm.startPrank(GOVERNOR);
|
||||||
|
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, ALICE, address(0));
|
||||||
|
treasury.enable(ITreasury.STATUS.RESERVETOKEN, address(reserve), address(calculator));
|
||||||
|
vm.stopPrank();
|
||||||
|
|
||||||
|
vm.prank(ALICE);
|
||||||
|
treasury.deposit(address(reserve), AMOUNT, 0);
|
||||||
|
|
||||||
vm.expectRevert();
|
vm.expectRevert();
|
||||||
vm.prank(who);
|
vm.prank(ALICE);
|
||||||
treasury.toggleTimelock();
|
treasury.withdraw(address(reserve), AMOUNT);
|
||||||
}
|
|
||||||
|
|
||||||
function test_triggerTimelock() public {
|
|
||||||
assertEq(treasury.timelockEnabled(), false);
|
|
||||||
assertEq(treasury.onChainGovernanceTimelock(), 0);
|
|
||||||
|
|
||||||
vm.prank(GOVERNOR);
|
vm.prank(GOVERNOR);
|
||||||
treasury.toggleTimelock();
|
treasury.withdraw(address(reserve), AMOUNT);
|
||||||
uint256 queuedTime = block.number + 69 * 7;
|
assertEq(reserve.balanceOf(address(treasury)), 0);
|
||||||
assertEq(treasury.timelockEnabled(), false);
|
|
||||||
assertEq(treasury.onChainGovernanceTimelock(), queuedTime);
|
|
||||||
|
|
||||||
vm.roll(queuedTime + 1);
|
|
||||||
vm.prank(GOVERNOR);
|
|
||||||
treasury.toggleTimelock();
|
|
||||||
assertEq(treasury.timelockEnabled(), true);
|
|
||||||
assertEq(treasury.onChainGovernanceTimelock(), 0);
|
|
||||||
|
|
||||||
vm.prank(GOVERNOR);
|
|
||||||
treasury.toggleTimelock();
|
|
||||||
queuedTime = block.number + 69 * 7;
|
|
||||||
assertEq(treasury.timelockEnabled(), true);
|
|
||||||
assertEq(treasury.onChainGovernanceTimelock(), queuedTime);
|
|
||||||
|
|
||||||
vm.roll(queuedTime + 1);
|
|
||||||
vm.prank(GOVERNOR);
|
|
||||||
treasury.toggleTimelock();
|
|
||||||
assertEq(treasury.timelockEnabled(), false);
|
|
||||||
assertEq(treasury.onChainGovernanceTimelock(), 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user