get rid of annoying foundry warnings

Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
This commit is contained in:
Uncle Fatso 2026-03-09 00:37:41 +03:00
parent 0d4c945aa1
commit 8ac8b67767
Signed by: f4ts0
GPG Key ID: 565F4F2860226EBB
44 changed files with 1458 additions and 1354 deletions

View File

@ -1,12 +1,12 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin-contracts/token/ERC20/IERC20.sol";
import "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {IERC20} from "@openzeppelin-contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20Metadata} from "@openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "./types/NoteKeeper.sol";
import "./interfaces/IBondDepository.sol";
import {NoteKeeper} from "./types/NoteKeeper.sol";
import {IBondDepository} from "./interfaces/IBondDepository.sol";
contract GhostBondDepository is IBondDepository, NoteKeeper {
using SafeERC20 for IERC20;
@ -59,12 +59,12 @@ contract GhostBondDepository is IBondDepository, NoteKeeper {
market.capacity -= market.capacityInQuote ? amount : payout;
market.purchased += amount;
market.sold += uint64(payout);
market.totalDebt += uint64(payout);
market.sold += uint64(payout); // forge-lint: disable-line(unsafe-typecast)
market.totalDebt += uint64(payout); // forge-lint: disable-line(unsafe-typecast)
emit Bond(id, amount, price);
index = addNote(user, payout, uint48(expiry), uint48(id), referral);
index = addNote(user, payout, uint48(expiry), uint48(id), referral); // forge-lint: disable-line(unsafe-typecast)
IERC20(market.quoteToken).safeTransferFrom(msg.sender, address(_treasury), amount);
if (term.maxDebt < market.totalDebt) {
@ -108,8 +108,10 @@ contract GhostBondDepository is IBondDepository, NoteKeeper {
? ((market.capacity * 1e18) / price) / (10**meta.quoteDecimals)
: market.capacity;
// forge-lint: disable-next-line(unsafe-typecast)
markets[id].maxPayout = uint64((capacity * meta.depositInterval) / timeRemaining);
uint256 targetDebt = (capacity * meta.length) / timeRemaining;
// forge-lint: disable-next-line(unsafe-typecast)
uint64 newControlVariable = uint64((price * _treasury.baseSupply()) / targetDebt);
emit Tuned(id, terms[id].controlVariable, newControlVariable);
@ -118,7 +120,12 @@ contract GhostBondDepository is IBondDepository, NoteKeeper {
terms[id].controlVariable = newControlVariable;
} else {
uint64 change = terms[id].controlVariable - newControlVariable;
adjustments[id] = Adjustment(change, time, meta.tuneInterval, true);
adjustments[id] = Adjustment({
change: change,
lastAdjustment: time,
timeToAdjusted: meta.tuneInterval,
active: true
});
}
metadatas[id].lastTune = time;
}
@ -141,6 +148,8 @@ contract GhostBondDepository is IBondDepository, NoteKeeper {
uint64 targetDebt = uint64(_booleans[0]
? ((_market[0] * 1e18) / _market[1]) / 10**decimals
: _market[0]);
// forge-lint: disable-next-line(unsafe-typecast)
uint64 maxPayout = uint64((targetDebt * _intervals[0]) / secondsToConclusion);
uint256 maxDebt = targetDebt + ((targetDebt * _market[2]) / 1e5);
uint256 controlVariable = (_market[1] * _treasury.baseSupply()) / targetDebt;
@ -162,10 +171,10 @@ contract GhostBondDepository is IBondDepository, NoteKeeper {
terms.push(
Term({
fixedTerm: _booleans[1],
controlVariable: uint64(controlVariable),
vesting: uint48(_terms[0]),
conclusion: uint48(_terms[1]),
maxDebt: uint64(maxDebt)
controlVariable: uint64(controlVariable), // forge-lint: disable-line(unsafe-typecast)
vesting: uint48(_terms[0]), // forge-lint: disable-line(unsafe-typecast)
conclusion: uint48(_terms[1]), // forge-lint: disable-line(unsafe-typecast)
maxDebt: uint64(maxDebt) // forge-lint: disable-line(unsafe-typecast)
})
);
@ -173,9 +182,11 @@ contract GhostBondDepository is IBondDepository, NoteKeeper {
Metadata({
lastTune: uint48(block.timestamp),
lastDecay: uint48(block.timestamp),
// forge-lint: disable-next-line(unsafe-typecast)
length: uint48(secondsToConclusion),
depositInterval: _intervals[0],
tuneInterval: _intervals[1],
// forge-lint: disable-next-line(unsafe-typecast)
quoteDecimals: uint8(decimals)
})
);

View File

@ -1,10 +1,12 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin-contracts/token/ERC20/extensions/ERC20Permit.sol";
import {ERC20Permit} from "@openzeppelin-contracts/token/ERC20/extensions/ERC20Permit.sol";
import {ERC20} from "@openzeppelin-contracts/token/ERC20/ERC20.sol";
import "./interfaces/IFTSO.sol";
import "./types/GhostAccessControlled.sol";
import {IFTSO} from "./interfaces/IFTSO.sol";
import {IGhostAuthority} from "./interfaces/IGhostAuthority.sol";
import {GhostAccessControlled} from "./types/GhostAccessControlled.sol";
contract Fatso is ERC20Permit, IFTSO, GhostAccessControlled {
constructor(address _authority, string memory name, string memory symbol)

View File

@ -1,11 +1,11 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "./interfaces/IGatekeeper.sol";
import {IGatekeeper} from "./interfaces/IGatekeeper.sol";
contract Gatekeeper is IGatekeeper {
uint256 public override ghostedSupply;
address public immutable staking;
address public immutable staking; // forge-lint: disable-line(screaming-snake-case-immutable)
uint256 private _aggregatedPublicKey;
mapping(uint256 => mapping(uint256 => bool)) private _executedTransaction;
@ -32,7 +32,16 @@ contract Gatekeeper is IGatekeeper {
) external override {
if (msg.sender != staking) revert NotStaking();
_checkTransactionExistence(rx, s);
bytes32 message = keccak256(abi.encodePacked("materialize", receiver, amount));
bytes4 selector = bytes4(keccak256("materialize(address,uint256)"));
bytes32 message;
assembly {
let ptr := mload(0x40)
mstore(ptr, selector)
mstore(add(ptr, 4), receiver)
mstore(add(ptr, 36), amount)
message := keccak256(ptr, 68)
}
if (_incorrectSignature(rx, s, message)) revert WrongSignature();
ghostedSupply -= amount;
@ -45,7 +54,15 @@ contract Gatekeeper is IGatekeeper {
uint256 s
) external override {
_checkTransactionExistence(rx, s);
bytes32 message = keccak256(abi.encodePacked("rotate", aggregatedPublicKey));
bytes4 selector = bytes4(keccak256("rotate(uint256)"));
bytes32 message;
assembly {
let ptr := mload(0x40)
mstore(ptr, selector)
mstore(add(ptr, 4), aggregatedPublicKey)
message := keccak256(ptr, 36)
}
if (_incorrectSignature(rx, s, message)) revert WrongSignature();
_aggregatedPublicKey = aggregatedPublicKey;

View File

@ -1,8 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "./interfaces/IGhostAuthority.sol";
import "./types/GhostAccessControlled.sol";
import {IGhostAuthority} from "./interfaces/IGhostAuthority.sol";
import {GhostAccessControlled} from "./types/GhostAccessControlled.sol";
contract GhostAuthority is IGhostAuthority, GhostAccessControlled {
address public override governor;

View File

@ -1,12 +1,13 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin-contracts/token/ERC20/ERC20.sol";
import "@openzeppelin-contracts/token/ERC20/extensions/ERC20Permit.sol";
import "@openzeppelin-contracts/token/ERC20/extensions/ERC20Votes.sol";
import {ERC20} from "@openzeppelin-contracts/token/ERC20/ERC20.sol";
import {ERC20Permit} from "@openzeppelin-contracts/token/ERC20/extensions/ERC20Permit.sol";
import {ERC20Votes} from "@openzeppelin-contracts/token/ERC20/extensions/ERC20Votes.sol";
import {Nonces} from "@openzeppelin-contracts/utils/Nonces.sol";
import "./interfaces/ISTNK.sol";
import "./interfaces/IGHST.sol";
import {ISTNK} from "./interfaces/ISTNK.sol";
import {IGHST} from "./interfaces/IGHST.sol";
contract Ghost is IGHST, ERC20, ERC20Permit, ERC20Votes {
address public override staking;

View File

@ -1,25 +1,26 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin-contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20} from "@openzeppelin-contracts/token/ERC20/IERC20.sol";
import "./types/GhostAccessControlled.sol";
import {GhostAccessControlled} from "./types/GhostAccessControlled.sol";
import "./interfaces/ISTNK.sol";
import "./interfaces/IGHST.sol";
import "./interfaces/IDistributor.sol";
import "./interfaces/IStaking.sol";
import "./interfaces/IGatekeeper.sol";
import {ISTNK} from "./interfaces/ISTNK.sol";
import {IGHST} from "./interfaces/IGHST.sol";
import {IDistributor} from "./interfaces/IDistributor.sol";
import {IStaking} from "./interfaces/IStaking.sol";
import {IGatekeeper} from "./interfaces/IGatekeeper.sol";
import {IGhostAuthority} from "./interfaces/IGhostAuthority.sol";
contract GhostStaking is IStaking, GhostAccessControlled {
using SafeERC20 for IERC20;
using SafeERC20 for ISTNK;
using SafeERC20 for IGHST;
address public immutable ftso;
address public immutable stnk;
address public immutable ghst;
address public immutable ftso; // forge-lint: disable-line(screaming-snake-case-immutable)
address public immutable stnk; // forge-lint: disable-line(screaming-snake-case-immutable)
address public immutable ghst; // forge-lint: disable-line(screaming-snake-case-immutable)
uint48 public warmupPeriod;
@ -180,6 +181,7 @@ contract GhostStaking is IStaking, GhostAccessControlled {
}
function setWarmupPeriod(uint256 _warmupPeriod) external onlyGovernor {
// forge-lint: disable-next-line(unsafe-typecast)
warmupPeriod = uint48(_warmupPeriod);
emit WarmupSet(_warmupPeriod);
}

View File

@ -1,21 +1,21 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin-contracts/token/ERC20/IERC20.sol";
import "@uniswap-v2-core/interfaces/IUniswapV2Pair.sol";
import {IERC20} from "@openzeppelin-contracts/token/ERC20/IERC20.sol";
import {IUniswapV2Pair} from "@uniswap-v2-core/interfaces/IUniswapV2Pair.sol";
import "./types/GhostAccessControlled.sol";
import {GhostAccessControlled} from "./types/GhostAccessControlled.sol";
import "./interfaces/ITreasury.sol";
import "./interfaces/IStaking.sol";
import "./interfaces/IDistributor.sol";
import {ITreasury} from "./interfaces/ITreasury.sol";
import {IDistributor} from "./interfaces/IDistributor.sol";
import {IGhostAuthority} from "./interfaces/IGhostAuthority.sol";
contract GhostDistributor is IDistributor, GhostAccessControlled {
uint256 private constant DENOMINATOR = 1e6;
address private immutable ftso;
address private immutable treasury;
address private immutable staking;
address private immutable ftso; // forge-lint: disable-line(screaming-snake-case-immutable)
address private immutable treasury; // forge-lint: disable-line(screaming-snake-case-immutable)
address private immutable staking; // forge-lint: disable-line(screaming-snake-case-immutable)
uint256 public rewardRate;
uint256 public bounty;
@ -115,8 +115,8 @@ contract GhostDistributor is IDistributor, GhostAccessControlled {
adjustment = Adjust({
add: add,
rate: uint120(rate),
target: uint120(target)
rate: uint120(rate), // forge-lint: disable-line(unsafe-typecast)
target: uint120(target) // forge-lint: disable-line(unsafe-typecast)
});
}
}

View File

@ -1,23 +1,18 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "./libraries/FixedPoint.sol";
import {FixedPoint} from "./libraries/FixedPoint.sol";
import "@openzeppelin-contracts/utils/Address.sol";
import "@openzeppelin-contracts/token/ERC20/IERC20.sol";
import "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {IERC20Metadata} from "@openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {IUniswapV2Pair} from "@uniswap-v2-core/interfaces/IUniswapV2Pair.sol";
import "@uniswap-v2-core/interfaces/IUniswapV2ERC20.sol";
import "@uniswap-v2-core/interfaces/IUniswapV2Pair.sol";
import "./interfaces/IBondingCalculator.sol";
import {IBondingCalculator} from "./interfaces/IBondingCalculator.sol";
contract GhostBondingCalculator is IBondingCalculator {
using FixedPoint for *;
uint256 public override immutable fraction;
address internal immutable ftso;
uint256 public override immutable fraction; // forge-lint: disable-line(screaming-snake-case-immutable)
address internal immutable ftso; // forge-lint: disable-line(screaming-snake-case-immutable)
constructor(address _ftso, uint256 _numerator, uint256 _denominator) {
ftso = _ftso;

View File

@ -1,19 +1,20 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin-contracts/utils/Address.sol";
import "@openzeppelin-contracts/token/ERC20/extensions/ERC20Permit.sol";
import {ERC20Permit} from "@openzeppelin-contracts/token/ERC20/extensions/ERC20Permit.sol";
import {ERC20} from "@openzeppelin-contracts/token/ERC20/ERC20.sol";
import {IERC20} from "@openzeppelin-contracts/token/ERC20/IERC20.sol";
import "./interfaces/IGHST.sol";
import "./interfaces/ISTNK.sol";
import "./interfaces/IStaking.sol";
import {IGHST} from "./interfaces/IGHST.sol";
import {ISTNK} from "./interfaces/ISTNK.sol";
import {IStaking} from "./interfaces/IStaking.sol";
contract Stinky is ISTNK, ERC20Permit {
uint256 private constant INITIAL_SHARES_SUPPLY = 5 * 10**15;
uint256 private constant TOTAL_SHARES = type(uint256).max - (type(uint256).max % INITIAL_SHARES_SUPPLY);
uint256 private constant MAX_SUPPLY = type(uint128).max;
uint256 internal immutable _internalIndex;
uint256 internal immutable _INTERNAL_INDEX;
address internal _initializer;
uint256 private _sharesPerUnit;
@ -32,7 +33,7 @@ contract Stinky is ISTNK, ERC20Permit {
ERC20Permit(name)
{
_initializer = msg.sender;
_internalIndex = usedIndex;
_INTERNAL_INDEX = usedIndex;
_totalSupply = INITIAL_SHARES_SUPPLY;
_sharesPerUnit = TOTAL_SHARES / INITIAL_SHARES_SUPPLY;
}
@ -174,7 +175,7 @@ contract Stinky is ISTNK, ERC20Permit {
}
function index() public view override returns (uint256) {
return balanceForShares(_internalIndex);
return balanceForShares(_INTERNAL_INDEX);
}
function changeDebt(

View File

@ -1,28 +1,29 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin-contracts/token/ERC20/IERC20.sol";
import "@openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20} from "@openzeppelin-contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "@openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
import "@uniswap-v2-core-1.0.1/interfaces/IUniswapV2Factory.sol";
import "@uniswap-v2-core-1.0.1/interfaces/IUniswapV2Pair.sol";
import "@uniswap-v2-periphery-1.1.0-beta.0/interfaces/IUniswapV2Router02.sol";
import "@uniswap-v2-periphery-1.1.0-beta.0/interfaces/IUniswapV2Router01.sol";
import {IUniswapV2Factory} from "@uniswap-v2-core-1.0.1/interfaces/IUniswapV2Factory.sol";
import {IUniswapV2Pair} from "@uniswap-v2-core-1.0.1/interfaces/IUniswapV2Pair.sol";
import {IUniswapV2Router02} from "@uniswap-v2-periphery-1.1.0-beta.0/interfaces/IUniswapV2Router02.sol";
import {IUniswapV2Router01} from "@uniswap-v2-periphery-1.1.0-beta.0/interfaces/IUniswapV2Router01.sol";
import "./types/GhostAccessControlled.sol";
import "./libraries/FixedPoint.sol";
import {GhostAccessControlled} from "./types/GhostAccessControlled.sol";
import {Babylonian} from "./libraries/FixedPoint.sol";
import "./interfaces/IFTSO.sol";
import "./interfaces/ISTNK.sol";
import "./interfaces/IBondingCalculator.sol";
import "./interfaces/ITreasury.sol";
import {IFTSO} from "./interfaces/IFTSO.sol";
import {ISTNK} from "./interfaces/ISTNK.sol";
import {IBondingCalculator} from "./interfaces/IBondingCalculator.sol";
import {ITreasury} from "./interfaces/ITreasury.sol";
import {IGhostAuthority} from "./interfaces/IGhostAuthority.sol";
contract GhostTreasury is GhostAccessControlled, ITreasury {
using SafeERC20 for IERC20;
address public immutable ftso;
uint256 public immutable blocksNeededForQueue;
address public immutable ftso; // forge-lint: disable-line(screaming-snake-case-immutable)
uint256 public immutable blocksNeededForQueue; // forge-lint: disable-line(screaming-snake-case-immutable)
uint256 public totalReserves;
uint256 public totalDebt;
@ -234,7 +235,7 @@ contract GhostTreasury is GhostAccessControlled, ITreasury {
address weth = IUniswapV2Router01(router).WETH();
address pair = IUniswapV2Factory(IUniswapV2Router01(router).factory()).getPair(ftso, weth);
IUniswapV2Pair(pair).transfer(pair, liquidity);
IERC20(pair).safeTransfer(pair, liquidity);
(uint256 amount0, uint256 amount1) = IUniswapV2Pair(pair).burn(address(this));
if (destroyerMode) {
@ -286,8 +287,8 @@ contract GhostTreasury is GhostAccessControlled, ITreasury {
amountIn = amount - amountIn;
IERC20(weth).transfer(pair, amountIn);
IERC20(ftso).transfer(pair, amounts[1]);
IERC20(weth).safeTransfer(pair, amountIn);
IERC20(ftso).safeTransfer(pair, amounts[1]);
IUniswapV2Pair(pair).mint(address(this));
}

View File

@ -1,19 +1,20 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin-contracts/token/ERC20/IERC20.sol";
import "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20} from "@openzeppelin-contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
import "./types/GhostAccessControlled.sol";
import {GhostAccessControlled} from "./types/GhostAccessControlled.sol";
import "./interfaces/ITreasury.sol";
import "./interfaces/IAllocator.sol";
import "./interfaces/ITreasuryExtender.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;
ITreasury public immutable treasury; // forge-lint: disable-line(screaming-snake-case-immutable)
IAllocator[] public allocators;
mapping(IAllocator => mapping(uint256 => AllocatorData)) public allocatorData;
@ -134,6 +135,7 @@ contract TreasuryExtender is GhostAccessControlled, ITreasuryExtender {
amount = allocated + gain;
gain = 0;
} else {
// forge-lint: disable-next-line(unsafe-typecast)
gain -= uint128(amount);
amount += allocated;
}

View File

@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {IGovernor, Governor} from "@openzeppelin-contracts/governance/Governor.sol";
import {Governor} from "@openzeppelin-contracts/governance/Governor.sol";
import {GovernorPreventLateQuorum} from "@openzeppelin-contracts/governance/extensions/GovernorPreventLateQuorum.sol";
import {GovernorSettings} from "@openzeppelin-contracts/governance/extensions/GovernorSettings.sol";
import {GovernorStorage} from "@openzeppelin-contracts/governance/extensions/GovernorStorage.sol";
@ -10,8 +10,8 @@ import {GovernorVotesQuorumFraction} from "@openzeppelin-contracts/governance/ex
import {IVotes} from "@openzeppelin-contracts/governance/utils/IVotes.sol";
import {ReentrancyGuard} from "@openzeppelin-contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin-contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20} from "@openzeppelin-contracts/token/ERC20/IERC20.sol";
import {GovernorGhostCounting} from "./GovernorGhostCounting.sol";
import {Babylonian} from "../libraries/FixedPoint.sol";
@ -80,7 +80,7 @@ contract GhostGovernor is
if (releaseAmount > 0 && (currentProposalState & activePendingMask == 0)) {
lockedAmounts[proposalId] = 0;
IERC20(address(token())).transfer(proposer, releaseAmount);
IERC20(address(token())).safeTransfer(proposer, releaseAmount);
return releaseAmount;
}

View File

@ -20,7 +20,8 @@ abstract contract GovernorGhostCounting is Governor {
mapping(uint256 proposalId => ProposalVote) private _proposalVotes;
// solhint-disable-next-line func-name-mixedcase
// forge-lint: disable-next-item(mixed-case-function)
// solhint-disable-next-line mixed-case-function
function COUNTING_MODE() public pure virtual override returns (string memory) {
return "support=bravo&quorum=for";
}

View File

@ -1,9 +1,9 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin-contracts/token/ERC20/IERC20.sol";
import "./ITreasuryExtender.sol";
import "./IGhostAuthority.sol";
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);

View File

@ -1,8 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin-contracts/token/ERC20/IERC20.sol";
interface IBondDepository {
error DepositoryConcluded(uint48 concludedTime);
error DepositoryMaxPrice(uint256 price, uint256 maxPrice);

View File

@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin-contracts/token/ERC20/IERC20.sol";
import {IERC20} from "@openzeppelin-contracts/token/ERC20/IERC20.sol";
interface IFTSO is IERC20 {
function mint(address account, uint256 amount) external;

View File

@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin-contracts/token/ERC20/IERC20.sol";
import {IERC20} from "@openzeppelin-contracts/token/ERC20/IERC20.sol";
interface IGHST is IERC20 {
error NotStakingContract();

View File

@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin-contracts/token/ERC20/IERC20.sol";
import {IERC20} from "@openzeppelin-contracts/token/ERC20/IERC20.sol";
interface ISTNK is IERC20 {
error NotStakingContract();

View File

@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "./FullMath.sol";
import {FullMath} from "./FullMath.sol";
library Babylonian {
function sqrt(uint256 x) internal pure returns (uint256) {
@ -85,11 +85,11 @@ library BitMath {
}
library FixedPoint {
struct uq112x112 {
struct Uq112x112 {
uint224 _x;
}
struct uq144x112 {
struct Uq144x112 {
uint256 _x;
}
@ -98,38 +98,42 @@ library FixedPoint {
uint256 private constant Q224 = 0x100000000000000000000000000000000000000000000000000000000;
uint256 private constant LOWER_MASK = 0xffffffffffffffffffffffffffff; // decimal of UQ*x112 (lower 112 bits)
function decode(uq112x112 memory self) internal pure returns (uint112) {
function decode(Uq112x112 memory self) internal pure returns (uint112) {
return uint112(self._x >> RESOLUTION);
}
function decode112with18(uq112x112 memory self) internal pure returns (uint256) {
function decode112with18(Uq112x112 memory self) internal pure returns (uint256) {
return uint256(self._x) / 5192296858534827;
}
function fraction(uint256 numerator, uint256 denominator) internal pure returns (uq112x112 memory) {
function fraction(uint256 numerator, uint256 denominator) internal pure returns (Uq112x112 memory) {
require(denominator > 0, "FixedPoint::fraction: division by zero");
if (numerator == 0) return FixedPoint.uq112x112(0);
if (numerator == 0) return FixedPoint.Uq112x112(0);
if (numerator <= type(uint144).max) {
uint256 result = (numerator << RESOLUTION) / denominator;
require(result <= type(uint224).max, "FixedPoint::fraction: overflow");
return uq112x112(uint224(result));
// forge-lint: disable-next-line(unsafe-typecast)
return Uq112x112({ _x: uint224(result) });
} else {
uint256 result = FullMath.mulDiv(numerator, Q112, denominator);
require(result <= type(uint224).max, "FixedPoint::fraction: overflow");
return uq112x112(uint224(result));
// forge-lint: disable-next-line(unsafe-typecast)
return Uq112x112({ _x: uint224(result) });
}
}
// square root of a UQ112x112
// lossy between 0/1 and 40 bits
function sqrt(uq112x112 memory self) internal pure returns (uq112x112 memory) {
function sqrt(Uq112x112 memory self) internal pure returns (Uq112x112 memory) {
if (self._x <= type(uint144).max) {
return uq112x112(uint224(Babylonian.sqrt(uint256(self._x) << 112)));
// forge-lint: disable-next-line(unsafe-typecast)
return Uq112x112({ _x: uint224(Babylonian.sqrt(uint256(self._x) << 112)) });
}
uint8 safeShiftBits = 255 - BitMath.mostSignificantBit(self._x);
safeShiftBits -= safeShiftBits % 2;
return uq112x112(uint224(Babylonian.sqrt(uint256(self._x) << safeShiftBits) << ((112 - safeShiftBits) / 2)));
// forge-lint: disable-next-line(unsafe-typecast)
return Uq112x112({ _x: uint224(Babylonian.sqrt(uint256(self._x) << safeShiftBits) << ((112 - safeShiftBits) / 2)) });
}
}

View File

@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin-contracts/token/ERC20/ERC20.sol";
import {ERC20} from "@openzeppelin-contracts/token/ERC20/ERC20.sol";
contract ERC20Mock is ERC20 {
constructor(string memory name, string memory symbol) ERC20(name, symbol) {}

View File

@ -1,10 +1,11 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin-contracts/token/ERC20/extensions/ERC20Permit.sol";
import {ERC20} from "@openzeppelin-contracts/token/ERC20/ERC20.sol";
import {ERC20Permit} from "@openzeppelin-contracts/token/ERC20/extensions/ERC20Permit.sol";
contract Reserve is ERC20Permit {
address private immutable _owner;
address private immutable _owner; // forge-lint: disable-line(screaming-snake-case-immutable)
uint256 public accumulatedDonation;
uint256 public conversionRate;
uint256 public donationRate;

View File

@ -1,10 +1,10 @@
pragma solidity 0.8.20;
contract SigUtils {
bytes32 internal DOMAIN_SEPARATOR;
bytes32 internal immutable DOMAIN_SEPARATOR;
constructor(bytes32 _DOMAIN_SEPARATOR) {
DOMAIN_SEPARATOR = _DOMAIN_SEPARATOR;
constructor(bytes32 domainSeparator) {
DOMAIN_SEPARATOR = domainSeparator;
}
// keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
@ -23,34 +23,38 @@ contract SigUtils {
function getStructHash(Permit memory _permit)
internal
pure
returns (bytes32)
returns (bytes32 structHash)
{
return
keccak256(
abi.encode(
PERMIT_TYPEHASH,
_permit.owner,
_permit.spender,
_permit.value,
_permit.nonce,
_permit.deadline
)
);
assembly {
let ptr := mload(0x40)
mstore(ptr, PERMIT_TYPEHASH) // PERMIT_TYPEHASH
mstore(add(ptr, 32), mload(_permit)) // owner
mstore(add(ptr, 64), mload(add(_permit, 32))) // spender
mstore(add(ptr, 96), mload(add(_permit, 64))) // value
mstore(add(ptr, 128), mload(add(_permit, 96))) // nonce
mstore(add(ptr, 160), mload(add(_permit, 128))) // deadline
structHash := keccak256(ptr, 192)
}
}
// computes the hash of the fully encoded EIP-712 message for the domain, which can be used to recover the signer
function getTypedDataHash(Permit memory _permit)
public
view
returns (bytes32)
returns (bytes32 digest)
{
return
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR,
getStructHash(_permit)
)
);
bytes32 structHash = getStructHash(_permit);
bytes32 domainSeparator = DOMAIN_SEPARATOR;
assembly {
let ptr := mload(0x40)
mstore(ptr, 0x1901000000000000000000000000000000000000000000000000000000000000) // \x19\x01
mstore(add(ptr, 2), domainSeparator)
mstore(add(ptr, 34), structHash)
digest := keccak256(ptr, 66)
}
}
}

View File

@ -1,16 +1,17 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20} from "@openzeppelin-contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
import "./GhostAccessControlled.sol";
import "../interfaces/IAllocator.sol";
import "../interfaces/ITreasury.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;
ITreasuryExtender public immutable extender; // forge-lint: disable-line(screaming-snake-case-immutable)
AllocatorStatus public status;
uint256[] internal _ids;

View File

@ -1,17 +1,21 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "./GhostAccessControlled.sol";
import "@openzeppelin-contracts/token/ERC20/IERC20.sol";
import {GhostAccessControlled} from "./GhostAccessControlled.sol";
import {IGhostAuthority} from "../interfaces/IGhostAuthority.sol";
import {IERC20} from "@openzeppelin-contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
abstract contract FrontEndRewarder is GhostAccessControlled {
using SafeERC20 for IERC20;
uint256 public daoReward;
uint256 public refReward;
mapping(address => uint256) public rewards;
mapping(address => bool) public whitelisted;
IERC20 internal immutable _ftso;
IERC20 internal immutable _ftso; // forge-lint: disable-line(screaming-snake-case-immutable)
constructor(address _authorityAddress, address _ftsoAddress)
GhostAccessControlled(IGhostAuthority(_authorityAddress))
@ -22,7 +26,7 @@ abstract contract FrontEndRewarder is GhostAccessControlled {
function getReward() external {
uint256 reward = rewards[msg.sender];
rewards[msg.sender] = 0;
_ftso.transfer(msg.sender, reward);
_ftso.safeTransfer(msg.sender, reward);
}
function _giveRewards(

View File

@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "../interfaces/IGhostAuthority.sol";
import {IGhostAuthority} from "../interfaces/IGhostAuthority.sol";
abstract contract GhostAccessControlled {
error Unauthorized();

View File

@ -1,24 +1,26 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "./FrontEndRewarder.sol";
import {FrontEndRewarder} from "./FrontEndRewarder.sol";
import "../interfaces/IGHST.sol";
import "../interfaces/IStaking.sol";
import "../interfaces/ITreasury.sol";
import "../interfaces/INoteKeeper.sol";
import {IGHST} from "../interfaces/IGHST.sol";
import {IStaking} from "../interfaces/IStaking.sol";
import {ITreasury} from "../interfaces/ITreasury.sol";
import {INoteKeeper} from "../interfaces/INoteKeeper.sol";
import "@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";
abstract contract NoteKeeper is INoteKeeper, FrontEndRewarder {
using EnumerableSet for EnumerableSet.UintSet;
using SafeERC20 for IGHST;
mapping(address => Note[]) public notes;
mapping(address => mapping(uint256 => address)) private _noteTransfers;
mapping(address => EnumerableSet.UintSet) private _pendingIndexes;
IGHST internal immutable _ghst;
IStaking internal immutable _staking;
IGHST internal immutable _GHST;
IStaking internal immutable _STAKING;
ITreasury internal _treasury;
constructor(
@ -28,10 +30,10 @@ abstract contract NoteKeeper is INoteKeeper, FrontEndRewarder {
address _stakingAddress,
address _treasuryAddress
) FrontEndRewarder(_authority, _ftsoAddress) {
_ghst = IGHST(_ghstAddress);
_staking = IStaking(_stakingAddress);
_GHST = IGHST(_ghstAddress);
_STAKING = IStaking(_stakingAddress);
_treasury = ITreasury(_treasuryAddress);
_staking.toggleLock();
_STAKING.toggleLock();
}
function updateTreasury() external {
@ -54,7 +56,7 @@ abstract contract NoteKeeper is INoteKeeper, FrontEndRewarder {
_pendingIndexes[user].add(index);
notes[user].push(
Note({
payout: _ghst.balanceTo(payout),
payout: _GHST.balanceTo(payout),
created: uint48(block.timestamp),
matured: expiry,
redeemed: 0,
@ -64,7 +66,7 @@ abstract contract NoteKeeper is INoteKeeper, FrontEndRewarder {
uint256 rewards = _giveRewards(payout, referral);
_treasury.mint(address(this), payout + rewards);
_staking.stake(payout, address(this), false, true);
_STAKING.stake(payout, address(this), false, true);
}
function redeem(
@ -72,7 +74,7 @@ abstract contract NoteKeeper is INoteKeeper, FrontEndRewarder {
bool sendGhst,
uint256[] memory indexes
) public override returns (uint256 payout) {
_staking.claim(address(this), false);
_STAKING.claim(address(this), false);
uint48 time = uint48(block.timestamp);
uint256 i;
@ -87,8 +89,8 @@ abstract contract NoteKeeper is INoteKeeper, FrontEndRewarder {
unchecked { ++i; }
}
if (sendGhst) _ghst.transfer(user, payout);
else _staking.unwrap(user, payout);
if (sendGhst) _GHST.safeTransfer(user, payout);
else _STAKING.unwrap(user, payout);
}
function redeemAll(

View File

@ -1,14 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin-contracts/access/Ownable2Step.sol";
import {IERC20} from "@openzeppelin-contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
import {Ownable2Step} from "@openzeppelin-contracts/access/Ownable2Step.sol";
import {Ownable} from "@openzeppelin-contracts/access/Ownable.sol";
import "../interfaces/ITreasury.sol";
import "../interfaces/ISTNK.sol";
import "../interfaces/IGHST.sol";
import "../interfaces/IStaking.sol";
import "../interfaces/IClaim.sol";
import {ITreasury} from "../interfaces/ITreasury.sol";
import {IGHST} from "../interfaces/IGHST.sol";
import {IStaking} from "../interfaces/IStaking.sol";
import {IClaim} from "../interfaces/IClaim.sol";
contract GenesisClaim is IClaim, Ownable2Step {
using SafeERC20 for IERC20;
@ -18,12 +19,12 @@ contract GenesisClaim is IClaim, Ownable2Step {
uint256 public totalAllocated;
uint256 public maximumAllocated;
IERC20 internal immutable _ftso;
IERC20 internal immutable _reserve;
IGHST internal immutable _ghst;
ITreasury internal immutable _treasury;
IStaking internal immutable _staking;
address internal immutable _dao;
IERC20 internal immutable _FTSO;
IERC20 internal immutable _RESERVE;
IGHST internal immutable _GHST;
ITreasury internal immutable _TREASURY;
IStaking internal immutable _STAKING;
address internal immutable _DAO;
mapping(address => Term) private _terms;
mapping(address => address) public walletChange;
@ -40,16 +41,16 @@ contract GenesisClaim is IClaim, Ownable2Step {
maximumAllocated = _maximumAllocated;
useStatic = true;
_ftso = IERC20(_ftsoAddress);
_reserve = IERC20(_reserveAddress);
_treasury = ITreasury(_treasuryAddress);
_ghst = IGHST(_ghstAddress);
_staking = IStaking(_stakingAddress);
_dao = _daoAddress;
_FTSO = IERC20(_ftsoAddress);
_RESERVE = IERC20(_reserveAddress);
_TREASURY = ITreasury(_treasuryAddress);
_GHST = IGHST(_ghstAddress);
_STAKING = IStaking(_stakingAddress);
_DAO = _daoAddress;
}
function claim(uint256 amount) external {
_ftso.safeTransfer(msg.sender, _claim(amount));
_FTSO.safeTransfer(msg.sender, _claim(amount));
}
function stake(
@ -60,28 +61,28 @@ contract GenesisClaim is IClaim, Ownable2Step {
) external {
uint256 toStake = _claim(amount);
//
// _ftso.approve(address(_staking), toStake);
// _staking.stake(toStake, msg.sender);
// _FTSO.approve(address(_STAKING), toStake);
// _STAKING.stake(toStake, msg.sender);
//
// if (isFtso) _staking.claim(msg.sender);
_staking.stake(toStake, to, isRebasing, isClaim);
// if (isFtso) _STAKING.claim(msg.sender);
_STAKING.stake(toStake, to, isRebasing, isClaim);
}
function _claim(uint256 amount) internal returns (uint256 toSend) {
Term memory info = _terms[msg.sender];
_reserve.safeTransferFrom(msg.sender, address(this), amount);
_reserve.approve(address(_treasury), amount);
toSend = _treasury.deposit(address(_reserve), amount, 0);
_RESERVE.safeTransferFrom(msg.sender, address(this), amount);
_RESERVE.approve(address(_TREASURY), amount);
toSend = _TREASURY.deposit(address(_RESERVE), amount, 0);
if (redeemableFor(msg.sender) / 1e9 < toSend) revert NotEnoughVested();
if (info.max - claimed(msg.sender) < toSend) revert ClaimedOverMax();
if (useStatic) {
_terms[msg.sender].gClaimed = info.gClaimed + _ghst.balanceTo(toSend * 9 / 10);
_terms[msg.sender].gClaimed = info.gClaimed + _GHST.balanceTo(toSend * 9 / 10);
_terms[msg.sender].claimed = info.claimed + (toSend / 10);
} else {
_terms[msg.sender].gClaimed = info.gClaimed + _ghst.balanceTo(toSend);
_terms[msg.sender].gClaimed = info.gClaimed + _GHST.balanceTo(toSend);
}
}
@ -107,12 +108,12 @@ contract GenesisClaim is IClaim, Ownable2Step {
}
function claimed(address someAddress) public view returns (uint256) {
return _ghst.balanceFrom(_terms[someAddress].gClaimed)
return _GHST.balanceFrom(_terms[someAddress].gClaimed)
+ _terms[someAddress].claimed;
}
function circulatingSupply() public view returns ( uint ) {
return _treasury.baseSupply() - _ftso.balanceOf(_dao);
return _TREASURY.baseSupply() - _FTSO.balanceOf(_DAO);
}
function setTerms(
@ -134,7 +135,7 @@ contract GenesisClaim is IClaim, Ownable2Step {
}
function treatAllAsStaked() external {
if (msg.sender != _dao) revert NotADao();
if (msg.sender != _DAO) revert NotADao();
useStatic = false;
}
}

View File

@ -2,16 +2,17 @@ pragma solidity 0.8.20;
import {Test} from "forge-std/Test.sol";
import "../../src/FatsoERC20.sol";
import "../../src/StinkyERC20.sol";
import "../../src/GhstERC20.sol";
import "../../src/GhostAuthority.sol";
import "../../src/StakingDistributor.sol";
import "../../src/Treasury.sol";
import "../../src/Staking.sol";
import "../../src/BondDepository.sol";
import "../../src/mocks/ERC20Mock.sol";
import "../../src/StandardBondingCalculator.sol";
import {Fatso} from "../../src/FatsoERC20.sol";
import {Stinky} from "../../src/StinkyERC20.sol";
import {Ghost} from "../../src/GhstERC20.sol";
import {GhostAuthority} from "../../src/GhostAuthority.sol";
import {GhostTreasury} from "../../src/Treasury.sol";
import {GhostStaking} from "../../src/Staking.sol";
import {GhostBondDepository} from "../../src/BondDepository.sol";
import {ERC20Mock} from "../../src/mocks/ERC20Mock.sol";
import {GhostBondingCalculator} from "../../src/StandardBondingCalculator.sol";
import {ITreasury} from "../../src/interfaces/ITreasury.sol";
contract GhostBondDepositoryTest is Test {
uint256 public constant TOTAL_INITIAL_SUPPLY = 5000000000000000;
@ -21,24 +22,22 @@ contract GhostBondDepositoryTest is Test {
uint48 public constant EPOCH_NUMBER = 1;
uint48 public constant EPOCH_END_TIME = 1337;
uint256 public constant initialMint = 1000000000000000000000000;
uint256 public constant initialDeposit = 100000000000000000000000;
uint256 public constant capacity = 10000e9;
uint256 public constant initialPrice = 400e9;
uint256 public constant buffer = 2e5;
uint256 public constant INITIAL_MINT = 1000000000000000000000000;
uint256 public constant CAPACITY = 10000e9;
uint256 public constant INITIAL_PRICE = 400e9;
uint256 public constant BUFFER = 2e5;
address constant initializer = 0x0000000000000000000000000000000000000001;
address constant governor = 0x0000000000000000000000000000000000000003;
address constant guardian = 0x0000000000000000000000000000000000000004;
address constant policy = 0x0000000000000000000000000000000000000005;
address constant vault = 0x0000000000000000000000000000000000000006;
address constant alice = 0x0000000000000000000000000000000000000007;
address constant bob = 0x0000000000000000000000000000000000000008;
address constant INITIALIZER = 0x0000000000000000000000000000000000000001;
address constant GOVERNOR = 0x0000000000000000000000000000000000000003;
address constant GUARDIAN = 0x0000000000000000000000000000000000000004;
address constant POLICY = 0x0000000000000000000000000000000000000005;
address constant VAULT = 0x0000000000000000000000000000000000000006;
address constant ALICE = 0x0000000000000000000000000000000000000007;
uint256 public constant vesting = 100;
uint256 public constant timeToConclusion = 60 * 60 * 24;
uint256 public constant depositInterval = 60 * 60 * 4;
uint256 public constant tuneInterval = 60 * 60;
uint256 public constant VESTING = 100;
uint256 public constant TIME_TO_CONCLUSION = 60 * 60 * 24;
uint256 public constant DEPOSIT_INTERVAL = 60 * 60 * 4;
uint256 public constant TUNE_INTERVAL = 60 * 60;
uint256 public conclusion;
@ -53,12 +52,12 @@ contract GhostBondDepositoryTest is Test {
GhostBondingCalculator calculator;
function setUp() public {
vm.startPrank(initializer);
vm.startPrank(INITIALIZER);
authority = new GhostAuthority(
governor,
guardian,
policy,
vault
GOVERNOR,
GUARDIAN,
POLICY,
VAULT
);
reserve = new ERC20Mock("Reserve Token", "RET");
ftso = new Fatso(address(authority), "Fatso", "FTSO");
@ -89,31 +88,31 @@ contract GhostBondDepositoryTest is Test {
}
function _createFirstBond() internal {
vm.startPrank(governor);
vm.startPrank(GOVERNOR);
authority.pushVault(address(treasury));
treasury.enable(ITreasury.STATUS.REWARDMANAGER, address(depository), address(0));
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, alice, address(0));
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, ALICE, address(0));
treasury.enable(ITreasury.STATUS.RESERVETOKEN, address(reserve), address(calculator));
vm.stopPrank();
vm.startPrank(alice);
reserve.mint(alice, initialMint);
vm.startPrank(ALICE);
reserve.mint(ALICE, INITIAL_MINT);
reserve.approve(address(treasury), type(uint256).max);
treasury.deposit(address(reserve), initialMint, treasury.tokenValue(address(reserve), initialMint) / 2);
treasury.deposit(address(reserve), INITIAL_MINT, treasury.tokenValue(address(reserve), INITIAL_MINT) / 2);
assertEq(ftso.totalSupply(), treasury.baseSupply());
reserve.mint(alice, initialMint);
reserve.mint(ALICE, INITIAL_MINT);
reserve.approve(address(depository), type(uint256).max);
vm.stopPrank();
conclusion = block.timestamp + timeToConclusion;
conclusion = block.timestamp + TIME_TO_CONCLUSION;
vm.prank(policy);
vm.prank(POLICY);
depository.create(
[capacity, initialPrice, buffer],
[vesting, conclusion],
[CAPACITY, INITIAL_PRICE, BUFFER],
[VESTING, conclusion],
address(reserve),
[uint32(depositInterval), uint32(tuneInterval)],
[uint32(DEPOSIT_INTERVAL), uint32(TUNE_INTERVAL)], // forge-lint: disable-line(unsafe-typecast)
[false, true]
);
}
@ -124,23 +123,23 @@ contract GhostBondDepositoryTest is Test {
function test_shouldConcludeInCorrectAmountOfTime() public view {
(, , , uint48 concludes,) = depository.terms(0);
assertEq(concludes, uint48(conclusion));
assertEq(concludes, uint48(conclusion)); // forge-lint: disable-line(unsafe-typecast)
(, , uint48 length, , ,) = depository.metadatas(0);
assertEq(length, timeToConclusion);
assertEq(length, TIME_TO_CONCLUSION);
}
function test_shouldSetMaxPayoutToCorrectPercentageOfCapacity() public view {
(, , , , uint256 maxPayout, ,) = depository.markets(0);
assertEq(maxPayout, capacity / 6);
assertEq(maxPayout, CAPACITY / 6);
}
function test_shouldReturnIdsOfAllMarkets() public {
vm.prank(policy);
vm.prank(POLICY);
depository.create(
[capacity, initialPrice, buffer],
[vesting, conclusion],
[CAPACITY, INITIAL_PRICE, BUFFER],
[VESTING, conclusion],
address(reserve),
[uint32(depositInterval), uint32(tuneInterval)],
[uint32(DEPOSIT_INTERVAL), uint32(TUNE_INTERVAL)], // forge-lint: disable-line(unsafe-typecast)
[false, true]
);
@ -151,12 +150,12 @@ contract GhostBondDepositoryTest is Test {
}
function test_shouldUpdateIdsOfMarkets() public {
vm.startPrank(policy);
vm.startPrank(POLICY);
depository.create(
[capacity, initialPrice, buffer],
[vesting, conclusion],
[CAPACITY, INITIAL_PRICE, BUFFER],
[VESTING, conclusion],
address(reserve),
[uint32(depositInterval), uint32(tuneInterval)],
[uint32(DEPOSIT_INTERVAL), uint32(TUNE_INTERVAL)], // forge-lint: disable-line(unsafe-typecast)
[false, true]
);
depository.close(0);
@ -174,7 +173,7 @@ contract GhostBondDepositoryTest is Test {
}
function test_shouldStartWithPriceAtInitialPrice() public view {
assertEq(depository.marketPrice(0), initialPrice);
assertEq(depository.marketPrice(0), INITIAL_PRICE);
}
function test_shouldGiveAccuratePayoutForPrice() public view {
@ -187,20 +186,20 @@ contract GhostBondDepositoryTest is Test {
function test_shouldDecayDebt() public {
(, , , uint256 totalDebt, , ,) = depository.markets(0);
skip(depositInterval);
vm.prank(alice);
depository.deposit(0, 0, initialPrice, alice, alice);
skip(DEPOSIT_INTERVAL);
vm.prank(ALICE);
depository.deposit(0, 0, INITIAL_PRICE, ALICE, ALICE);
(, , , uint256 newTotalDebt, , ,) = depository.markets(0);
assertEq(totalDebt > newTotalDebt, true);
}
function test_shouldStartAdjustmentIfBehindSchedule() public {
skip(depositInterval);
skip(DEPOSIT_INTERVAL);
uint256 amount = 10_000 * 1e18;
vm.prank(alice);
depository.deposit(0, amount, initialPrice, alice, alice);
vm.prank(ALICE);
depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE);
(, , , bool active) = depository.adjustments(0);
assertEq(active, true);
@ -210,11 +209,11 @@ contract GhostBondDepositoryTest is Test {
(, uint64 ctrlVariable, , ,) = depository.terms(0);
uint256 amount = 10_000 * 1e18;
vm.startPrank(alice);
depository.deposit(0, amount, initialPrice, alice, alice);
skip(depositInterval);
vm.startPrank(ALICE);
depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE);
skip(DEPOSIT_INTERVAL);
(uint64 change, , ,) = depository.adjustments(0);
depository.deposit(0, amount, initialPrice, alice, alice);
depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE);
vm.stopPrank();
(, uint64 newCtrlVariable, , ,) = depository.terms(0);
@ -222,16 +221,16 @@ contract GhostBondDepositoryTest is Test {
}
function test_adjustmentShouldLowerControlVariableByHalfOfTuneInterval() public {
skip(depositInterval);
skip(DEPOSIT_INTERVAL);
(, uint64 ctrlVariable, , ,) = depository.terms(0);
uint256 amount = 10_000 * 1e18;
vm.startPrank(alice);
depository.deposit(0, amount, initialPrice, alice, alice);
vm.startPrank(ALICE);
depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE);
(uint64 change, , ,) = depository.adjustments(0);
skip(tuneInterval / 2);
skip(TUNE_INTERVAL / 2);
depository.deposit(0, amount, initialPrice, alice, alice);
depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE);
(, uint64 newCtrlVariable, , ,) = depository.terms(0);
vm.stopPrank();
@ -244,13 +243,13 @@ contract GhostBondDepositoryTest is Test {
(, uint64 ctrlVariable, , ,) = depository.terms(0);
uint256 amount = 10_000 * 1e18;
vm.startPrank(alice);
depository.deposit(0, amount, initialPrice, alice, alice);
vm.startPrank(ALICE);
depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE);
(uint64 change, , ,) = depository.adjustments(0);
skip(tuneInterval / 2);
depository.deposit(0, amount, initialPrice, alice, alice);
skip(tuneInterval / 2);
depository.deposit(0, amount, initialPrice, alice, alice);
skip(TUNE_INTERVAL / 2);
depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE);
skip(TUNE_INTERVAL / 2);
depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE);
vm.stopPrank();
(, uint64 newCtrlVariable, , ,) = depository.terms(0);
@ -259,53 +258,53 @@ contract GhostBondDepositoryTest is Test {
function test_shouldAllowDeposit() public {
uint256 amount = 10_000 * 1e18;
vm.prank(alice);
depository.deposit(0, amount, initialPrice, alice, alice);
vm.prank(ALICE);
depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE);
uint256[] memory arr = depository.indexesFor(alice);
uint256[] memory arr = depository.indexesFor(ALICE);
assertEq(arr.length, 1);
}
function test_shouldNotAllowDepositGreaterThanMaxPayout() public {
uint256 amount = 6_700_000 * 1e18;
vm.expectRevert();
vm.prank(alice);
depository.deposit(0, amount, initialPrice, alice, alice);
vm.prank(ALICE);
depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE);
}
function test_shouldNotRedeemAfterVested() public {
uint256 balance = ftso.balanceOf(alice);
uint256 balance = ftso.balanceOf(ALICE);
uint256 amount = 10_000 * 1e18; // 10,000
vm.startPrank(alice);
depository.deposit(0, amount, initialPrice, alice, alice);
depository.redeemAll(alice, true);
vm.startPrank(ALICE);
depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE);
depository.redeemAll(ALICE, true);
vm.stopPrank();
assertEq(ftso.balanceOf(alice), balance);
assertEq(ftso.balanceOf(ALICE), balance);
}
function test_shouldRedeemAfterVested() public {
uint256 amount = 10_000 * 1e18; // 10,000
vm.startPrank(alice);
(uint256 expectedPayout, ,) = depository.deposit(0, amount, initialPrice, alice, alice);
vm.startPrank(ALICE);
(uint256 expectedPayout, ,) = depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE);
skip(depositInterval);
depository.redeemAll(alice, true);
skip(DEPOSIT_INTERVAL);
depository.redeemAll(ALICE, true);
vm.stopPrank();
uint256 aliceBalance = ghst.balanceOf(alice);
uint256 aliceBalance = ghst.balanceOf(ALICE);
assertEq(aliceBalance >= ghst.balanceTo(expectedPayout), true);
assertEq(aliceBalance < ghst.balanceTo(expectedPayout * 10001 / 10000), true);
}
function test_shouldCorrectlyRedeemPartially() public {
uint256 amount = 1 * 1e18;
vm.startPrank(alice);
depository.deposit(0, amount, initialPrice * 2, alice, alice);
depository.deposit(0, amount, initialPrice * 2, alice, alice);
depository.deposit(0, amount, initialPrice * 2, alice, alice);
depository.deposit(0, amount, initialPrice * 2, alice, alice);
vm.startPrank(ALICE);
depository.deposit(0, amount, INITIAL_PRICE * 2, ALICE, ALICE);
depository.deposit(0, amount, INITIAL_PRICE * 2, ALICE, ALICE);
depository.deposit(0, amount, INITIAL_PRICE * 2, ALICE, ALICE);
depository.deposit(0, amount, INITIAL_PRICE * 2, ALICE, ALICE);
skip(depositInterval);
skip(DEPOSIT_INTERVAL);
uint256[] memory indexesToRemove = new uint256[](1);
indexesToRemove[0] = 1;
@ -313,22 +312,22 @@ contract GhostBondDepositoryTest is Test {
uint256[] memory nextIndexToRemove = new uint256[](1);
nextIndexToRemove[0] = 3;
uint256[] memory allIndexes = depository.indexesFor(alice);
uint256[] memory allIndexes = depository.indexesFor(ALICE);
assertEq(allIndexes.length, 4);
assertEq(allIndexes[0], 0);
assertEq(allIndexes[1], 1);
assertEq(allIndexes[2], 2);
assertEq(allIndexes[3], 3);
depository.redeem(alice, true, indexesToRemove);
uint256[] memory allIndexesOneRemoved = depository.indexesFor(alice);
depository.redeem(ALICE, true, indexesToRemove);
uint256[] memory allIndexesOneRemoved = depository.indexesFor(ALICE);
assertEq(allIndexesOneRemoved.length, 3);
assertEq(allIndexesOneRemoved[0], 0);
assertEq(allIndexesOneRemoved[1], 3);
assertEq(allIndexesOneRemoved[2], 2);
depository.redeem(alice, true, nextIndexToRemove);
uint256[] memory allIndexesTwoRemoved = depository.indexesFor(alice);
depository.redeem(ALICE, true, nextIndexToRemove);
uint256[] memory allIndexesTwoRemoved = depository.indexesFor(ALICE);
assertEq(allIndexesTwoRemoved.length, 2);
assertEq(allIndexesTwoRemoved[0], 0);
assertEq(allIndexesTwoRemoved[1], 2);
@ -339,20 +338,20 @@ contract GhostBondDepositoryTest is Test {
function test_afterSuccesfullWarmupAutoClaimExecuted() public {
uint256 amount = 10_000 * 1e18; // 10,000
vm.prank(governor);
vm.prank(GOVERNOR);
staking.setWarmupPeriod(1);
vm.startPrank(alice);
vm.startPrank(ALICE);
assertEq(ghst.balanceOf(alice), 0);
(uint256 expectedPayout, ,) = depository.deposit(0, amount, initialPrice, alice, alice);
assertEq(ghst.balanceOf(ALICE), 0);
(uint256 expectedPayout, ,) = depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE);
assertEq(ghst.balanceOf(address(depository)), 0);
skip(depositInterval);
skip(DEPOSIT_INTERVAL);
staking.rebase();
depository.redeemAll(alice, true);
uint256 aliceBalance = ghst.balanceOf(alice);
depository.redeemAll(ALICE, true);
uint256 aliceBalance = ghst.balanceOf(ALICE);
assertEq(aliceBalance >= ghst.balanceTo(expectedPayout), true);
assertEq(aliceBalance < ghst.balanceTo(expectedPayout * 10001 / 10000), true);
@ -360,7 +359,7 @@ contract GhostBondDepositoryTest is Test {
}
function test_externalAccountCouldNotClaimFromWarmup() public {
vm.startPrank(alice);
vm.startPrank(ALICE);
vm.expectRevert();
staking.claim(address(depository), false);
@ -375,17 +374,17 @@ contract GhostBondDepositoryTest is Test {
(, , , , uint64 maxPayout, ,) = depository.markets(0);
uint256 price = depository.marketPrice(0);
uint256 amount = maxPayout * price;
vm.prank(alice);
depository.deposit(0, amount, initialPrice, alice, alice);
skip(depositInterval);
vm.prank(ALICE);
depository.deposit(0, amount, INITIAL_PRICE, ALICE, ALICE);
skip(DEPOSIT_INTERVAL);
uint256 newPrice = depository.marketPrice(0);
assertEq(newPrice < initialPrice, true);
assertEq(newPrice < INITIAL_PRICE, true);
}
function test_shouldCloseMarket() public {
(uint256 cap, , , , , ,) = depository.markets(0);
assertEq(cap > 0, true);
vm.prank(policy);
vm.prank(POLICY);
depository.close(0);
(uint256 newCap, , , , , ,) = depository.markets(0);
assertEq(newCap, 0);

View File

@ -2,42 +2,42 @@ pragma solidity 0.8.20;
import {Test} from "forge-std/Test.sol";
import "../../src/Gatekeeper.sol";
import {Gatekeeper} from "../../src/Gatekeeper.sol";
contract GatekeeperTest is Test {
address constant alice = 0x0000000000000000000000000000000000000001;
address constant bob = 0x0000000000000000000000000000000000000002;
uint256 constant initAmount = 69 * 1e18;
address constant ALICE = 0x0000000000000000000000000000000000000001;
address constant BOB = 0x0000000000000000000000000000000000000002;
uint256 constant INIT_AMOUNT = 69 * 1e18;
Gatekeeper gatekeeper;
event Ghosted(bytes32 indexed receiver, uint256 indexed amount);
function setUp() public {
gatekeeper = new Gatekeeper(alice, 0);
gatekeeper = new Gatekeeper(ALICE, 0);
}
function test_correctInitialization() public {
assertEq(gatekeeper.staking(), alice);
assertEq(gatekeeper.staking(), ALICE);
assertEq(gatekeeper.ghostedSupply(), 0);
Gatekeeper anotherGatekeeper = new Gatekeeper(bob, initAmount);
assertEq(anotherGatekeeper.staking(), bob);
assertEq(anotherGatekeeper.ghostedSupply(), initAmount);
Gatekeeper anotherGatekeeper = new Gatekeeper(BOB, INIT_AMOUNT);
assertEq(anotherGatekeeper.staking(), BOB);
assertEq(anotherGatekeeper.ghostedSupply(), INIT_AMOUNT);
}
function test_ghostTokensWork(uint256 ghostAmount) public {
vm.assume(ghostAmount > 0);
bytes32 receiver = bytes32(abi.encodePacked(alice));
bytes32 receiver = bytes32(abi.encodePacked(ALICE));
uint256 ghostedSupply = gatekeeper.ghostedSupply();
vm.prank(alice);
vm.prank(ALICE);
gatekeeper.ghost(receiver, ghostAmount);
assertEq(gatekeeper.ghostedSupply(), ghostedSupply + ghostAmount);
}
function test_couldNotGhostTokensFromArbitraryAddress(address someone) public {
vm.assume(someone != alice);
bytes32 receiver = bytes32(abi.encodePacked(alice));
vm.assume(someone != ALICE);
bytes32 receiver = bytes32(abi.encodePacked(ALICE));
vm.expectRevert();
vm.prank(someone);
@ -47,17 +47,17 @@ contract GatekeeperTest is Test {
function test_ghostTokensEmitsEvent(uint256 ghostAmount) public {
vm.assume(ghostAmount > 0);
bytes32 receiver = bytes32(abi.encodePacked(alice));
bytes32 receiver = bytes32(abi.encodePacked(ALICE));
vm.expectEmit(true, true, true, false, address(gatekeeper));
emit Ghosted(receiver, ghostAmount);
vm.prank(alice);
vm.prank(ALICE);
gatekeeper.ghost(receiver, ghostAmount);
}
function test_materializeWork(uint256 ghostAmount) public {
vm.expectRevert();
gatekeeper.materialize(alice, ghostAmount, 0, 0);
gatekeeper.materialize(ALICE, ghostAmount, 0, 0);
}
function test_rotateWork(uint256 aggregatedPublicKey) public {

View File

@ -3,9 +3,13 @@ pragma solidity 0.8.20;
import {Test} from "forge-std/Test.sol";
import {Strings} from "@openzeppelin-contracts/utils/Strings.sol";
import "../../src/governance/GhostGovernor.sol";
import "../../src/GhstERC20.sol";
import "../../src/StinkyERC20.sol";
import {Ghost} from "../../src/GhstERC20.sol";
import {Stinky} from "../../src/StinkyERC20.sol";
import {GhostGovernor} from "../../src/governance/GhostGovernor.sol";
import {IGovernor} from "@openzeppelin-contracts/governance/IGovernor.sol";
import {IVotes} from "@openzeppelin-contracts/governance/utils/IVotes.sol";
import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
contract GhostGovernorExposed is GhostGovernor {
constructor(
@ -42,6 +46,7 @@ contract GhostGovernorExposed is GhostGovernor {
contract GhostGovernorTest is Test {
using Strings for address;
using SafeERC20 for Ghost;
uint48 public constant VOTE_EXTENSION = 69;
uint48 public constant VOTING_DELAY = 420;
@ -49,22 +54,22 @@ contract GhostGovernorTest is Test {
uint256 public constant PROPOSAL_THRESHOLD = 69 * 1e18;
uint256 public constant QUORUM_FRACTION = 20; // percent
address constant init = 0x0000000000000000000000000000000000000001;
address constant alice = 0x0000000000000000000000000000000000000002;
address constant bob = 0x0000000000000000000000000000000000000003;
address constant carol = 0x0000000000000000000000000000000000000004;
address constant dave = 0x0000000000000000000000000000000000000005;
address constant eve = 0x0000000000000000000000000000000000000006;
address constant INIT = 0x0000000000000000000000000000000000000001;
address constant ALICE = 0x0000000000000000000000000000000000000002;
address constant BOB = 0x0000000000000000000000000000000000000003;
address constant CAROL = 0x0000000000000000000000000000000000000004;
address constant DAVE = 0x0000000000000000000000000000000000000005;
address constant EVE = 0x0000000000000000000000000000000000000006;
GhostGovernorExposed public governor;
Stinky public stnk;
Ghost public ghst;
function setUp() public {
vm.startPrank(init);
vm.startPrank(INIT);
stnk = new Stinky(10819917194513808e56, "Stinky Test Name", "STNKTST");
ghst = new Ghost(address(stnk), "Ghost Test Name", "GHSTTST");
ghst.initialize(init);
ghst.initialize(INIT);
governor = new GhostGovernorExposed(
ghst,
VOTE_EXTENSION,
@ -75,13 +80,13 @@ contract GhostGovernorTest is Test {
);
vm.stopPrank();
vm.prank(alice);
vm.prank(ALICE);
ghst.approve(address(governor), type(uint256).max);
vm.prank(bob);
vm.prank(BOB);
ghst.approve(address(governor), type(uint256).max);
vm.prank(carol);
vm.prank(CAROL);
ghst.approve(address(governor), type(uint256).max);
}
@ -154,129 +159,129 @@ contract GhostGovernorTest is Test {
}
function test_succeededOnAbsoluteMajority() public {
vm.startPrank(init);
ghst.mint(alice, PROPOSAL_THRESHOLD);
ghst.mint(bob, 555 * 1e17);
ghst.mint(carol, 345 * 1e17 + 1);
ghst.mint(dave, 21 * 1e18);
vm.startPrank(INIT);
ghst.mint(ALICE, PROPOSAL_THRESHOLD);
ghst.mint(BOB, 555 * 1e17);
ghst.mint(CAROL, 345 * 1e17 + 1);
ghst.mint(DAVE, 21 * 1e18);
vm.stopPrank();
vm.roll(block.number + 1);
(uint256 proposalId,,,,) = _proposeDummy(alice, 69);
(uint256 proposalId,,,,) = _proposeDummy(ALICE, 69);
_waitForActive(proposalId);
assertEq(uint8(governor.state(proposalId)), uint8(IGovernor.ProposalState.Active));
_castVoteWrapper(proposalId, bob, 1, true, true);
_castVoteWrapper(proposalId, BOB, 1, true, true);
assertEq(uint8(governor.state(proposalId)), uint8(IGovernor.ProposalState.Succeeded));
vm.expectRevert();
vm.prank(carol);
vm.prank(CAROL);
governor.castVote(proposalId, 0);
vm.expectRevert();
vm.prank(dave);
vm.prank(DAVE);
governor.castVote(proposalId, 0);
}
function test_defeatedOnAbsoluteMajority() public {
vm.startPrank(init);
ghst.mint(alice, PROPOSAL_THRESHOLD);
ghst.mint(bob, 555 * 1e17);
ghst.mint(carol, 345 * 1e17 + 1);
ghst.mint(dave, 21 * 1e18);
vm.startPrank(INIT);
ghst.mint(ALICE, PROPOSAL_THRESHOLD);
ghst.mint(BOB, 555 * 1e17);
ghst.mint(CAROL, 345 * 1e17 + 1);
ghst.mint(DAVE, 21 * 1e18);
vm.stopPrank();
vm.roll(block.number + 1);
(uint256 proposalId,,,,) = _proposeDummy(alice, 69);
(uint256 proposalId,,,,) = _proposeDummy(ALICE, 69);
_waitForActive(proposalId);
assertEq(uint8(governor.state(proposalId)), uint8(IGovernor.ProposalState.Active));
_castVoteWrapper(proposalId, bob, 0, true, false);
_castVoteWrapper(proposalId, BOB, 0, true, false);
assertEq(uint8(governor.state(proposalId)), uint8(IGovernor.ProposalState.Active));
_castVoteWrapper(proposalId, carol, 0, true, false);
_castVoteWrapper(proposalId, CAROL, 0, true, false);
assertEq(uint8(governor.state(proposalId)), uint8(IGovernor.ProposalState.Defeated));
vm.expectRevert();
vm.prank(dave);
vm.prank(DAVE);
governor.castVote(proposalId, 1);
}
function test_onlyOneActiveProposal() public {
uint256 amount = PROPOSAL_THRESHOLD;
vm.startPrank(init);
ghst.mint(alice, amount);
ghst.mint(bob, 2 * amount);
ghst.mint(carol, 3 * amount);
vm.startPrank(INIT);
ghst.mint(ALICE, amount);
ghst.mint(BOB, 2 * amount);
ghst.mint(CAROL, 3 * amount);
vm.stopPrank();
vm.roll(block.number + 1);
(uint256 proposalId,,,,) = _proposeDummy(alice, 69);
(uint256 proposalId,,,,) = _proposeDummy(ALICE, 69);
_waitForActive(proposalId);
vm.prank(alice);
vm.prank(ALICE);
assertEq(governor.releaseLocked(proposalId), 0);
assertEq(governor.lockedAmounts(proposalId), amount);
assertEq(ghst.balanceOf(alice), 0);
assertEq(ghst.balanceOf(ALICE), 0);
(uint256 againstVotes, uint256 forVotes, uint256 abstainVotes) = governor.proposalVotes(proposalId);
assertEq(againstVotes, 0);
assertEq(forVotes, amount);
assertEq(abstainVotes, 0);
_castVoteWrapper(proposalId, bob, 1, true, true);
_castVoteWrapper(proposalId, BOB, 1, true, true);
(againstVotes, forVotes, abstainVotes) = governor.proposalVotes(proposalId);
assertEq(againstVotes, 0);
assertEq(forVotes, 3 * amount);
assertEq(abstainVotes, 0);
assertEq(ghst.balanceOf(alice), 0);
assertEq(ghst.balanceOf(bob), 2 * amount);
assertEq(ghst.balanceOf(carol), 3 * amount);
assertEq(ghst.balanceOf(ALICE), 0);
assertEq(ghst.balanceOf(BOB), 2 * amount);
assertEq(ghst.balanceOf(CAROL), 3 * amount);
_waitForSucceed(proposalId);
vm.prank(alice);
vm.prank(ALICE);
assertEq(governor.releaseLocked(proposalId), amount);
assertEq(governor.lockedAmounts(proposalId), 0);
assertEq(ghst.balanceOf(alice), amount);
assertEq(ghst.balanceOf(ALICE), amount);
vm.prank(alice);
vm.prank(ALICE);
assertEq(governor.releaseLocked(proposalId), 0);
assertEq(governor.lockedAmounts(proposalId), 0);
assertEq(ghst.balanceOf(alice), amount);
assertEq(ghst.balanceOf(ALICE), amount);
assertEq(ghst.balanceOf(alice), amount);
assertEq(ghst.balanceOf(bob), 2 * amount);
assertEq(ghst.balanceOf(carol), 3 * amount);
assertEq(ghst.balanceOf(ALICE), amount);
assertEq(ghst.balanceOf(BOB), 2 * amount);
assertEq(ghst.balanceOf(CAROL), 3 * amount);
(proposalId,,,,) = _proposeDummy(bob, 420);
(proposalId,,,,) = _proposeDummy(BOB, 420);
_waitForActive(proposalId);
_castVoteWrapper(proposalId, alice, 1, true, true);
_castVoteWrapper(proposalId, carol, 1, true, true);
_castVoteWrapper(proposalId, ALICE, 1, true, true);
_castVoteWrapper(proposalId, CAROL, 1, true, true);
vm.roll(block.number + 3);
(uint256 newProposalId,,,,) = _proposeDummy(carol, 1337);
(uint256 newProposalId,,,,) = _proposeDummy(CAROL, 1337);
assertEq(uint8(governor.state(proposalId)), uint8(IGovernor.ProposalState.Succeeded));
assertEq(uint8(governor.state(newProposalId)), uint8(IGovernor.ProposalState.Pending));
assertEq(ghst.balanceOf(alice), amount);
assertEq(ghst.balanceOf(bob), 0);
assertEq(ghst.balanceOf(carol), 0);
assertEq(ghst.balanceOf(ALICE), amount);
assertEq(ghst.balanceOf(BOB), 0);
assertEq(ghst.balanceOf(CAROL), 0);
vm.prank(bob);
vm.prank(BOB);
assertEq(governor.releaseLocked(proposalId), 2 * amount);
assertEq(governor.lockedAmounts(proposalId), 0);
assertEq(ghst.balanceOf(bob), 2 * amount);
assertEq(ghst.balanceOf(BOB), 2 * amount);
vm.prank(carol);
vm.prank(CAROL);
assertEq(governor.releaseLocked(newProposalId), 0);
assertEq(governor.lockedAmounts(newProposalId), 3 * amount);
assertEq(ghst.balanceOf(carol), 0);
assertEq(ghst.balanceOf(CAROL), 0);
}
function test_proposalCountWorks() public {
@ -291,8 +296,8 @@ contract GhostGovernorTest is Test {
break;
}
vm.prank(init);
ghst.mint(alice, amount);
vm.prank(INIT);
ghst.mint(ALICE, amount);
vm.roll(block.number + 1);
}
@ -302,7 +307,7 @@ contract GhostGovernorTest is Test {
uint256[] memory values,
bytes[] memory calldatas,
string memory description
) = _proposeDummy(alice, i);
) = _proposeDummy(ALICE, i);
{
(
@ -348,43 +353,43 @@ contract GhostGovernorTest is Test {
}
function test_preventLateQuorumWorks() public {
vm.startPrank(init);
ghst.mint(alice, PROPOSAL_THRESHOLD);
ghst.mint(bob, 50 * 1e18);
ghst.mint(carol, 100 * 1e18);
ghst.mint(dave, 100 * 1e18);
ghst.mint(eve, 500 * 1e18);
vm.startPrank(INIT);
ghst.mint(ALICE, PROPOSAL_THRESHOLD);
ghst.mint(BOB, 50 * 1e18);
ghst.mint(CAROL, 100 * 1e18);
ghst.mint(DAVE, 100 * 1e18);
ghst.mint(EVE, 500 * 1e18);
vm.stopPrank();
vm.roll(block.number + 1);
(uint256 proposalId,,,,) = _proposeDummy(alice, 69);
(uint256 proposalId,,,,) = _proposeDummy(ALICE, 69);
assertEq(uint8(governor.state(proposalId)), uint8(IGovernor.ProposalState.Pending));
vm.roll(block.number + VOTING_DELAY + 1);
assertEq(uint8(governor.state(proposalId)), uint8(IGovernor.ProposalState.Active));
assertEq(governor.voteOf(proposalId, bob), 0);
assertEq(governor.voteOf(proposalId, carol), 0);
assertEq(governor.voteOf(proposalId, dave), 0);
assertEq(governor.voteOf(proposalId, eve), 0);
assertEq(governor.voteOf(proposalId, BOB), 0);
assertEq(governor.voteOf(proposalId, CAROL), 0);
assertEq(governor.voteOf(proposalId, DAVE), 0);
assertEq(governor.voteOf(proposalId, EVE), 0);
vm.roll(governor.proposalDeadline(proposalId));
_castVoteWrapper(proposalId, bob, 1, false, true);
_castVoteWrapper(proposalId, BOB, 1, false, true);
vm.roll(governor.proposalDeadline(proposalId));
_castVoteWrapper(proposalId, carol, 0, true, false);
_castVoteWrapper(proposalId, CAROL, 0, true, false);
vm.roll(governor.proposalDeadline(proposalId));
_castVoteWrapper(proposalId, dave, 1, true, false);
_castVoteWrapper(proposalId, DAVE, 1, true, false);
vm.roll(governor.proposalDeadline(proposalId));
_castVoteWrapper(proposalId, eve, 1, true, true);
_castVoteWrapper(proposalId, EVE, 1, true, true);
vm.prank(alice);
vm.prank(ALICE);
governor.execute(proposalId);
assertEq(uint8(governor.state(proposalId)), uint8(IGovernor.ProposalState.Executed));
assertEq(governor.voteOf(proposalId, bob), 2);
assertEq(governor.voteOf(proposalId, carol), 1);
assertEq(governor.voteOf(proposalId, dave), 2);
assertEq(governor.voteOf(proposalId, eve), 2);
assertEq(governor.voteOf(proposalId, BOB), 2);
assertEq(governor.voteOf(proposalId, CAROL), 1);
assertEq(governor.voteOf(proposalId, DAVE), 2);
assertEq(governor.voteOf(proposalId, EVE), 2);
}
function test_quorumNumeratorWorks() public {
@ -392,12 +397,12 @@ contract GhostGovernorTest is Test {
uint256 denominator = governor.quorumDenominator();
uint256 currentBlock = block.number;
vm.startPrank(init);
ghst.mint(alice, 1 * 1e18);
ghst.mint(bob, 69 * 1e18);
ghst.mint(carol, 420 * 1e18);
ghst.mint(dave, 1337 * 1e18);
ghst.mint(eve, 69420 * 1e18);
vm.startPrank(INIT);
ghst.mint(ALICE, 1 * 1e18);
ghst.mint(BOB, 69 * 1e18);
ghst.mint(CAROL, 420 * 1e18);
ghst.mint(DAVE, 1337 * 1e18);
ghst.mint(EVE, 69420 * 1e18);
vm.stopPrank();
vm.roll(currentBlock + 2);
@ -405,79 +410,79 @@ contract GhostGovernorTest is Test {
}
function test_votingPowerTransfer() public {
vm.startPrank(init);
ghst.mint(alice, 35 * 1e18);
ghst.mint(bob, 34 * 1e18);
vm.startPrank(INIT);
ghst.mint(ALICE, 35 * 1e18);
ghst.mint(BOB, 34 * 1e18);
vm.stopPrank();
vm.roll(block.number + 1);
vm.expectRevert();
vm.prank(alice);
ghst.delegate(alice);
vm.prank(ALICE);
ghst.delegate(ALICE);
vm.expectRevert();
vm.prank(bob);
ghst.delegate(bob);
vm.prank(BOB);
ghst.delegate(BOB);
_assertVotesEqualToBalance();
vm.prank(alice);
ghst.transfer(bob, 420);
vm.prank(ALICE);
ghst.safeTransfer(BOB, 420);
_assertVotesEqualToBalance();
uint256 aliceBalance = ghst.balanceOf(alice);
vm.prank(alice);
ghst.transfer(bob, aliceBalance);
uint256 aliceBalance = ghst.balanceOf(ALICE);
vm.prank(ALICE);
ghst.safeTransfer(BOB, aliceBalance);
_assertVotesEqualToBalance();
vm.prank(bob);
ghst.transfer(carol, 1337);
vm.prank(BOB);
ghst.safeTransfer(CAROL, 1337);
_assertVotesEqualToBalance();
uint256 bobBalance = ghst.balanceOf(bob);
vm.prank(bob);
ghst.transfer(carol, bobBalance);
uint256 bobBalance = ghst.balanceOf(BOB);
vm.prank(BOB);
ghst.safeTransfer(CAROL, bobBalance);
_assertVotesEqualToBalance();
vm.expectRevert();
vm.prank(carol);
ghst.delegate(carol);
vm.prank(CAROL);
ghst.delegate(CAROL);
assertEq(ghst.getVotes(alice), 0);
assertEq(ghst.getVotes(bob), 0);
assertEq(ghst.getVotes(carol), 69 * 1e18);
assertEq(ghst.getVotes(ALICE), 0);
assertEq(ghst.getVotes(BOB), 0);
assertEq(ghst.getVotes(CAROL), 69 * 1e18);
assertEq(ghst.balanceOf(alice), 0);
assertEq(ghst.balanceOf(bob), 0);
assertEq(ghst.balanceOf(carol), 69 * 1e18);
assertEq(ghst.balanceOf(ALICE), 0);
assertEq(ghst.balanceOf(BOB), 0);
assertEq(ghst.balanceOf(CAROL), 69 * 1e18);
vm.prank(init);
ghst.burn(carol, 69 * 1e18);
vm.prank(INIT);
ghst.burn(CAROL, 69 * 1e18);
assertEq(ghst.getVotes(carol), 0);
assertEq(ghst.balanceOf(carol), 0);
assertEq(ghst.getVotes(CAROL), 0);
assertEq(ghst.balanceOf(CAROL), 0);
assertEq(ghst.totalSupply(), 0);
}
function test_changesOfTotalSupplyHasNoAffectOnPastTotalSupply() public {
vm.prank(init);
ghst.mint(alice, 100);
vm.prank(INIT);
ghst.mint(ALICE, 100);
vm.roll(block.number + 1);
assertEq(ghst.getPastTotalSupply(block.number - 1), 100);
assertEq(ghst.totalSupply(), 100);
vm.prank(init);
ghst.burn(alice, 50);
vm.prank(INIT);
ghst.burn(ALICE, 50);
vm.roll(block.number + 1);
assertEq(ghst.getPastTotalSupply(block.number - 2), 100);
assertEq(ghst.getPastTotalSupply(block.number - 1), 50);
assertEq(ghst.totalSupply(), 50);
vm.prank(init);
ghst.mint(bob, 150);
vm.prank(INIT);
ghst.mint(BOB, 150);
vm.roll(block.number + 1);
assertEq(ghst.getPastTotalSupply(block.number - 3), 100);
@ -487,12 +492,12 @@ contract GhostGovernorTest is Test {
}
function test_proposeAutoForVote() public {
vm.startPrank(init);
ghst.mint(alice, PROPOSAL_THRESHOLD);
vm.startPrank(INIT);
ghst.mint(ALICE, PROPOSAL_THRESHOLD);
vm.stopPrank();
vm.roll(block.number + 1);
(uint256 proposalId,,,,) = _proposeDummy(alice, 69);
(uint256 proposalId,,,,) = _proposeDummy(ALICE, 69);
(uint256 againstVotes, uint256 forVotes, uint256 abstainVotes) = governor.proposalVotes(proposalId);
assertEq(forVotes, PROPOSAL_THRESHOLD);
@ -502,68 +507,68 @@ contract GhostGovernorTest is Test {
vm.roll(block.number + 1);
vm.expectRevert();
vm.prank(alice);
vm.prank(ALICE);
governor.castVote(proposalId, 1);
}
function test_releaseAfterDefeated() public {
vm.startPrank(init);
ghst.mint(alice, PROPOSAL_THRESHOLD);
ghst.mint(bob, PROPOSAL_THRESHOLD);
ghst.mint(carol, PROPOSAL_THRESHOLD);
vm.startPrank(INIT);
ghst.mint(ALICE, PROPOSAL_THRESHOLD);
ghst.mint(BOB, PROPOSAL_THRESHOLD);
ghst.mint(CAROL, PROPOSAL_THRESHOLD);
vm.stopPrank();
vm.roll(block.number + 1);
(uint256 proposalId,,,,) = _proposeDummy(alice, 69);
(uint256 proposalId,,,,) = _proposeDummy(ALICE, 69);
assertEq(governor.releaseLocked(proposalId), 0);
assertEq(ghst.balanceOf(alice), 0);
assertEq(ghst.balanceOf(ALICE), 0);
assertEq(ghst.balanceOf(address(governor)), PROPOSAL_THRESHOLD);
_waitForActive(proposalId);
_castVoteWrapper(proposalId, bob, 0, true, false);
_castVoteWrapper(proposalId, carol, 0, true, false);
_castVoteWrapper(proposalId, BOB, 0, true, false);
_castVoteWrapper(proposalId, CAROL, 0, true, false);
assertEq(uint8(governor.state(proposalId)), uint8(IGovernor.ProposalState.Defeated));
assertEq(governor.releaseLocked(proposalId), PROPOSAL_THRESHOLD);
assertEq(ghst.balanceOf(alice), PROPOSAL_THRESHOLD);
assertEq(ghst.balanceOf(ALICE), PROPOSAL_THRESHOLD);
assertEq(ghst.balanceOf(address(governor)), 0);
}
function test_releaseAfterSucceeded() public {
vm.startPrank(init);
ghst.mint(alice, PROPOSAL_THRESHOLD);
ghst.mint(bob, PROPOSAL_THRESHOLD);
ghst.mint(carol, PROPOSAL_THRESHOLD);
vm.startPrank(INIT);
ghst.mint(ALICE, PROPOSAL_THRESHOLD);
ghst.mint(BOB, PROPOSAL_THRESHOLD);
ghst.mint(CAROL, PROPOSAL_THRESHOLD);
vm.stopPrank();
vm.roll(block.number + 1);
(uint256 proposalId,,,,) = _proposeDummy(alice, 69);
(uint256 proposalId,,,,) = _proposeDummy(ALICE, 69);
assertEq(governor.releaseLocked(proposalId), 0);
assertEq(ghst.balanceOf(alice), 0);
assertEq(ghst.balanceOf(ALICE), 0);
assertEq(ghst.balanceOf(address(governor)), PROPOSAL_THRESHOLD);
_waitForActive(proposalId);
_castVoteWrapper(proposalId, bob, 1, true, true);
_castVoteWrapper(proposalId, BOB, 1, true, true);
assertEq(uint8(governor.state(proposalId)), uint8(IGovernor.ProposalState.Succeeded));
assertEq(governor.releaseLocked(proposalId), PROPOSAL_THRESHOLD);
assertEq(ghst.balanceOf(alice), PROPOSAL_THRESHOLD);
assertEq(ghst.balanceOf(ALICE), PROPOSAL_THRESHOLD);
assertEq(ghst.balanceOf(address(governor)), 0);
}
function test_releaseAfterDeadline() public {
vm.startPrank(init);
ghst.mint(alice, PROPOSAL_THRESHOLD);
ghst.mint(bob, 420 * PROPOSAL_THRESHOLD);
vm.startPrank(INIT);
ghst.mint(ALICE, PROPOSAL_THRESHOLD);
ghst.mint(BOB, 420 * PROPOSAL_THRESHOLD);
vm.stopPrank();
vm.roll(block.number + 1);
(uint256 proposalId,,,,) = _proposeDummy(alice, 69);
(uint256 proposalId,,,,) = _proposeDummy(ALICE, 69);
assertEq(governor.releaseLocked(proposalId), 0);
assertEq(ghst.balanceOf(alice), 0);
assertEq(ghst.balanceOf(ALICE), 0);
assertEq(ghst.balanceOf(address(governor)), PROPOSAL_THRESHOLD);
_waitForActive(proposalId);
@ -571,7 +576,7 @@ contract GhostGovernorTest is Test {
assertEq(uint8(governor.state(proposalId)), uint8(IGovernor.ProposalState.Defeated));
assertEq(governor.releaseLocked(proposalId), PROPOSAL_THRESHOLD);
assertEq(ghst.balanceOf(alice), PROPOSAL_THRESHOLD);
assertEq(ghst.balanceOf(ALICE), PROPOSAL_THRESHOLD);
assertEq(ghst.balanceOf(address(governor)), 0);
}
@ -586,7 +591,7 @@ contract GhostGovernorTest is Test {
)
{
targets = new address[](1);
targets[0] = address(uint160(index + 1));
targets[0] = address(uint160(index + 1)); // forge-lint: disable-line(unsafe-typecast)
values = new uint256[](1);
values[0] = 0;
@ -623,9 +628,9 @@ contract GhostGovernorTest is Test {
}
function _assertVotesEqualToBalance() private view {
assertEq(ghst.getVotes(alice), ghst.balanceOf(alice));
assertEq(ghst.getVotes(bob), ghst.balanceOf(bob));
assertEq(ghst.getVotes(carol), ghst.balanceOf(carol));
assertEq(ghst.getVotes(ALICE), ghst.balanceOf(ALICE));
assertEq(ghst.getVotes(BOB), ghst.balanceOf(BOB));
assertEq(ghst.getVotes(CAROL), ghst.balanceOf(CAROL));
}
function _waitForActive(uint256 proposalId) private {

View File

@ -2,25 +2,30 @@ pragma solidity 0.8.20;
import {Test} from "forge-std/Test.sol";
import "../../src/FatsoERC20.sol";
import "../../src/StinkyERC20.sol";
import "../../src/GhstERC20.sol";
import "../../src/GhostAuthority.sol";
import "../../src/StakingDistributor.sol";
import "../../src/Treasury.sol";
import "../../src/Staking.sol";
import "../../src/mocks/ERC20Mock.sol";
import "../../src/Gatekeeper.sol";
import "../../src/StandardBondingCalculator.sol";
import {Fatso} from "../../src/FatsoERC20.sol";
import {Stinky} from "../../src/StinkyERC20.sol";
import {Ghost} from "../../src/GhstERC20.sol";
import {GhostAuthority} from "../../src/GhostAuthority.sol";
import {GhostDistributor} from "../../src/StakingDistributor.sol";
import {GhostTreasury} from "../../src/Treasury.sol";
import {GhostStaking} from "../../src/Staking.sol";
import {ERC20Mock} from "../../src/mocks/ERC20Mock.sol";
import {Gatekeeper} from "../../src/Gatekeeper.sol";
import {GhostBondingCalculator} from "../../src/StandardBondingCalculator.sol";
import {ITreasury} from "../../src/interfaces/ITreasury.sol";
import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
contract StakingTest is Test {
address constant initializer = 0x0000000000000000000000000000000000000001;
address constant governor = 0x0000000000000000000000000000000000000003;
address constant guardian = 0x0000000000000000000000000000000000000004;
address constant policy = 0x0000000000000000000000000000000000000005;
address constant vault = 0x0000000000000000000000000000000000000006;
address constant alice = 0x0000000000000000000000000000000000000007;
address constant bob = 0x0000000000000000000000000000000000000008;
using SafeERC20 for Stinky;
address constant INITIALIZER = 0x0000000000000000000000000000000000000001;
address constant GOVERNOR = 0x0000000000000000000000000000000000000003;
address constant GUARDIAN = 0x0000000000000000000000000000000000000004;
address constant POLICY = 0x0000000000000000000000000000000000000005;
address constant VAULT = 0x0000000000000000000000000000000000000006;
address constant ALICE = 0x0000000000000000000000000000000000000007;
address constant BOB = 0x0000000000000000000000000000000000000008;
uint48 public constant EPOCH_LENGTH = 2200;
uint48 public constant EPOCH_NUMBER = 1;
@ -38,20 +43,20 @@ contract StakingTest is Test {
Gatekeeper gatekeeper;
GhostBondingCalculator calculator;
uint256 public constant amount = 69;
uint256 public constant bigAmount = amount * 1e9;
uint256 public constant AMOUNT = 69;
uint256 public constant BIG_AMOUNT = AMOUNT * 1e9;
event DistributorSet(address distributor);
event WarmupSet(uint256 warmup);
event Ghosted(bytes32 indexed receiver, uint256 indexed amount);
function setUp() public {
vm.startPrank(initializer);
vm.startPrank(INITIALIZER);
authority = new GhostAuthority(
governor,
guardian,
policy,
vault
GOVERNOR,
GUARDIAN,
POLICY,
VAULT
);
ftso = new Fatso(address(authority), "Fatso", "FTSO");
stnk = new Stinky(INITIAL_INDEX, "Stinky", "STNK");
@ -84,10 +89,10 @@ contract StakingTest is Test {
assertEq(distribute, 0);
}
function test_governorCouldSetDistributor(address maybeDistributor) public {
function test_GOVERNORCouldSetDistributor(address maybeDistributor) public {
vm.assume(maybeDistributor != address(0));
assertEq(staking.distributor(), address(0));
vm.prank(governor);
vm.prank(GOVERNOR);
staking.setDistributor(maybeDistributor);
assertEq(staking.distributor(), maybeDistributor);
}
@ -96,21 +101,21 @@ contract StakingTest is Test {
vm.assume(maybeDistributor != address(0));
vm.expectEmit(true, true, true, false, address(staking));
emit DistributorSet(maybeDistributor);
vm.prank(governor);
vm.prank(GOVERNOR);
staking.setDistributor(maybeDistributor);
}
function test_arbitraryAddressCouldNotAddDistributor(address someone, address maybeDistributor) public {
vm.assume(maybeDistributor != address(0) && someone != governor);
vm.assume(maybeDistributor != address(0) && someone != GOVERNOR);
vm.expectRevert();
vm.prank(someone);
staking.setDistributor(maybeDistributor);
}
function test_governorCouldSetWarmupPeriod(uint48 maybeWarmupPeriod) public {
function test_GOVERNORCouldSetWarmupPeriod(uint48 maybeWarmupPeriod) public {
vm.assume(maybeWarmupPeriod > 0);
assertEq(staking.warmupPeriod(), 0);
vm.prank(governor);
vm.prank(GOVERNOR);
staking.setWarmupPeriod(maybeWarmupPeriod);
assertEq(staking.warmupPeriod(), maybeWarmupPeriod);
}
@ -118,233 +123,233 @@ contract StakingTest is Test {
function test_emitsWarmupEvent(uint48 maybeWarmupPeriod) public {
vm.expectEmit(true, true, true, false, address(staking));
emit WarmupSet(maybeWarmupPeriod);
vm.prank(governor);
vm.prank(GOVERNOR);
staking.setWarmupPeriod(maybeWarmupPeriod);
}
function test_arbitraryAddressCouldNotSetWarmupPeriod(address someone, uint48 maybeWarmupPeriod) public {
vm.assume(maybeWarmupPeriod > 0 && someone != governor);
vm.assume(maybeWarmupPeriod > 0 && someone != GOVERNOR);
vm.expectRevert();
vm.prank(someone);
staking.setWarmupPeriod(maybeWarmupPeriod);
}
function test_stake_addAmountToTheWarmupWhenClaimIsFalse() public {
_mintAndApprove(alice, amount);
_mintAndApprove(ALICE, AMOUNT);
vm.prank(alice);
uint256 rebased = staking.stake(amount, alice, false, false);
vm.prank(ALICE);
uint256 rebased = staking.stake(AMOUNT, ALICE, false, false);
assertEq(ftso.balanceOf(alice), 0);
assertEq(ftso.balanceOf(ALICE), 0);
assertEq(staking.sharesInWarmup(), stnk.sharesForBalance(rebased));
(uint256 deposit, uint256 shares, uint48 expiry, bool lock) = staking.warmupInfo(alice);
assertEq(deposit, amount);
assertEq(shares, stnk.sharesForBalance(amount));
(uint256 deposit, uint256 shares, uint48 expiry, bool lock) = staking.warmupInfo(ALICE);
assertEq(deposit, AMOUNT);
assertEq(shares, stnk.sharesForBalance(AMOUNT));
assertEq(expiry, 1);
assertEq(lock, false);
}
function test_stake_exchangesFatsoToStinkyWhenClaimIsTrueAndRebasingIsTrue() public {
_mintAndApprove(alice, amount);
_mintAndApprove(ALICE, AMOUNT);
vm.prank(alice);
uint256 rebasedAmount = staking.stake(amount, alice, true, true);
vm.prank(ALICE);
uint256 rebasedAmount = staking.stake(AMOUNT, ALICE, true, true);
assertEq(ftso.balanceOf(alice), 0);
assertEq(stnk.balanceOf(alice), rebasedAmount);
assertEq(ftso.balanceOf(ALICE), 0);
assertEq(stnk.balanceOf(ALICE), rebasedAmount);
}
function test_stake_exchangesFatsoForNewlyMintedGhostWhenClaimIsTrueAndRebasingIsFalse() public {
_mintAndApprove(alice, amount);
_mintAndApprove(ALICE, AMOUNT);
vm.prank(alice);
uint256 rebasedAmount = staking.stake(amount, alice, false, true);
vm.prank(ALICE);
uint256 rebasedAmount = staking.stake(AMOUNT, ALICE, false, true);
assertEq(ftso.balanceOf(alice), 0);
assertEq(ghst.balanceOf(alice), rebasedAmount);
assertEq(ftso.balanceOf(ALICE), 0);
assertEq(ghst.balanceOf(ALICE), rebasedAmount);
}
function test_stake_addAmountToWarmupWhenClaimIsTrueAndWarmupGtZero() public {
_mintAndApprove(alice, amount);
vm.prank(governor);
_mintAndApprove(ALICE, AMOUNT);
vm.prank(GOVERNOR);
staking.setWarmupPeriod(1);
vm.prank(alice);
uint256 rebased = staking.stake(amount, alice, false, true);
vm.prank(ALICE);
uint256 rebased = staking.stake(AMOUNT, ALICE, false, true);
assertEq(ftso.balanceOf(alice), 0);
assertEq(ftso.balanceOf(ALICE), 0);
assertEq(staking.sharesInWarmup(), stnk.sharesForBalance(rebased));
(uint256 deposit, uint256 shares, uint48 expiry, bool lock) = staking.warmupInfo(alice);
assertEq(deposit, amount);
assertEq(shares, stnk.sharesForBalance(amount));
(uint256 deposit, uint256 shares, uint48 expiry, bool lock) = staking.warmupInfo(ALICE);
assertEq(deposit, AMOUNT);
assertEq(shares, stnk.sharesForBalance(AMOUNT));
assertEq(expiry, 2);
assertEq(lock, false);
}
function test_stake_allowsSelfDepositWhenNotLocked() public {
_mintAndApprove(alice, amount);
_mintAndApprove(ALICE, AMOUNT);
vm.prank(alice);
staking.stake(amount, bob, false, false);
vm.prank(ALICE);
staking.stake(AMOUNT, BOB, false, false);
assertEq(ftso.balanceOf(alice), 0);
assertEq(ftso.balanceOf(ALICE), 0);
}
function test_stake_disablesExternalDepositsWhenLocked() public {
_mintAndApprove(alice, amount);
vm.prank(bob);
_mintAndApprove(ALICE, AMOUNT);
vm.prank(BOB);
staking.toggleLock();
vm.expectRevert();
vm.prank(alice);
staking.stake(amount, bob, false, false);
vm.prank(ALICE);
staking.stake(AMOUNT, BOB, false, false);
assertEq(ftso.balanceOf(alice), amount);
assertEq(ftso.balanceOf(ALICE), AMOUNT);
}
function test_claim_transferStinkyWhenRebasingIsTrue() public {
_prepareAndRoll(alice, amount, false, false);
_prepareAndRoll(ALICE, AMOUNT, false, false);
assertEq(stnk.balanceOf(alice), 0);
vm.prank(alice);
uint256 rebased = staking.claim(alice, true);
assertEq(stnk.balanceOf(alice), rebased);
assertEq(stnk.balanceOf(ALICE), 0);
vm.prank(ALICE);
uint256 rebased = staking.claim(ALICE, true);
assertEq(stnk.balanceOf(ALICE), rebased);
assertEq(rebased > 0, true);
}
function test_claim_mintsGhostWhenRebasingIsFalse() public {
_prepareAndRoll(alice, amount, false, false);
_prepareAndRoll(ALICE, AMOUNT, false, false);
assertEq(ghst.balanceOf(alice), 0);
vm.prank(alice);
uint256 rebased = staking.claim(alice, false);
assertEq(ghst.balanceOf(alice), rebased);
assertEq(ghst.balanceOf(ALICE), 0);
vm.prank(ALICE);
uint256 rebased = staking.claim(ALICE, false);
assertEq(ghst.balanceOf(ALICE), rebased);
assertEq(rebased > 0, true);
}
function test_claim_preventsExternalClaimsWhenLocked() public {
_prepareAndRoll(alice, amount, false, false);
_prepareAndRoll(ALICE, AMOUNT, false, false);
assertEq(ghst.balanceOf(alice), 0);
vm.prank(alice);
assertEq(ghst.balanceOf(ALICE), 0);
vm.prank(ALICE);
staking.toggleLock();
vm.expectRevert();
vm.prank(bob);
staking.claim(alice, false);
assertEq(ghst.balanceOf(alice), 0);
vm.prank(BOB);
staking.claim(ALICE, false);
assertEq(ghst.balanceOf(ALICE), 0);
}
function test_claim_allowExternalClaimsWhenNotLocked() public {
_prepareAndRoll(alice, amount, false, false);
_prepareAndRoll(ALICE, AMOUNT, false, false);
assertEq(ghst.balanceOf(alice), 0);
vm.prank(bob);
uint256 rebased = staking.claim(alice, false);
assertEq(ghst.balanceOf(alice), rebased);
assertEq(ghst.balanceOf(ALICE), 0);
vm.prank(BOB);
uint256 rebased = staking.claim(ALICE, false);
assertEq(ghst.balanceOf(ALICE), rebased);
assertEq(rebased > 0, true);
}
function test_claim_doesNothingWhenThereIsNothingToClaim() public {
assertEq(ghst.balanceOf(alice), 0);
vm.prank(alice);
uint256 rebased = staking.claim(alice, false);
assertEq(ghst.balanceOf(alice), 0);
assertEq(ghst.balanceOf(ALICE), 0);
vm.prank(ALICE);
uint256 rebased = staking.claim(ALICE, false);
assertEq(ghst.balanceOf(ALICE), 0);
assertEq(rebased, 0);
}
function test_claim_doesNothingWhenWarmupIsntOver() public {
_mintAndApprove(alice, amount);
vm.prank(governor);
_mintAndApprove(ALICE, AMOUNT);
vm.prank(GOVERNOR);
staking.setWarmupPeriod(1337);
vm.prank(alice);
staking.stake(amount, alice, false, false);
vm.prank(ALICE);
staking.stake(AMOUNT, ALICE, false, false);
assertEq(stnk.balanceOf(alice), 0);
vm.prank(alice);
uint256 rebased = staking.claim(alice, true);
assertEq(stnk.balanceOf(alice), 0);
assertEq(stnk.balanceOf(ALICE), 0);
vm.prank(ALICE);
uint256 rebased = staking.claim(ALICE, true);
assertEq(stnk.balanceOf(ALICE), 0);
assertEq(rebased, 0);
}
function test_forefeit_removesStakeFromWarmupAndReturnsFatso() public {
_prepareAndRoll(alice, amount, false, false);
_prepareAndRoll(ALICE, AMOUNT, false, false);
assertEq(ftso.balanceOf(alice), 0);
assertEq(staking.sharesInWarmup(), stnk.sharesForBalance(amount));
assertEq(ftso.balanceOf(ALICE), 0);
assertEq(staking.sharesInWarmup(), stnk.sharesForBalance(AMOUNT));
vm.prank(alice);
vm.prank(ALICE);
uint256 deposit = staking.forfeit();
assertEq(ftso.balanceOf(alice), deposit);
assertEq(ftso.balanceOf(ALICE), deposit);
assertEq(staking.sharesInWarmup(), 0);
(uint256 depositInWarmup, uint256 shares, uint48 expiry,) = staking.warmupInfo(alice);
(uint256 depositInWarmup, uint256 shares, uint48 expiry,) = staking.warmupInfo(ALICE);
assertEq(depositInWarmup, 0);
assertEq(shares, 0);
assertEq(expiry, 0);
}
function test_forefeit_transfersZeroIfThereIsNoBalanceInWarmup() public {
vm.prank(alice);
vm.prank(ALICE);
uint256 deposit = staking.forfeit();
assertEq(deposit, 0);
assertEq(ftso.balanceOf(alice), 0);
assertEq(ftso.balanceOf(ALICE), 0);
assertEq(staking.sharesInWarmup(), 0);
}
function test_unstake_canRedeemStinkyToFatso() public {
_prepareAndRoll(alice, amount, true, true);
uint256 aliceBalance = stnk.balanceOf(alice);
vm.startPrank(alice);
_prepareAndRoll(ALICE, AMOUNT, true, true);
uint256 aliceBalance = stnk.balanceOf(ALICE);
vm.startPrank(ALICE);
stnk.approve(address(staking), aliceBalance);
staking.unstake(aliceBalance, alice, false, true);
staking.unstake(aliceBalance, ALICE, false, true);
vm.stopPrank();
assertEq(stnk.balanceOf(alice), 0);
assertEq(ftso.balanceOf(alice), amount);
assertEq(stnk.balanceOf(ALICE), 0);
assertEq(ftso.balanceOf(ALICE), AMOUNT);
}
function test_unstake_canRedeemGhostToFatso() public {
_prepareAndRoll(alice, amount, false, true);
uint256 aliceBalance = ghst.balanceOf(alice);
vm.startPrank(alice);
_prepareAndRoll(ALICE, AMOUNT, false, true);
uint256 aliceBalance = ghst.balanceOf(ALICE);
vm.startPrank(ALICE);
ghst.approve(address(staking), aliceBalance);
vm.roll(block.number + 69);
staking.unstake(aliceBalance, alice, false, false);
staking.unstake(aliceBalance, ALICE, false, false);
vm.stopPrank();
assertEq(ghst.balanceOf(alice), 0);
assertEq(ftso.balanceOf(alice), ghst.balanceFrom(aliceBalance));
assertEq(ghst.balanceOf(ALICE), 0);
assertEq(ftso.balanceOf(ALICE), ghst.balanceFrom(aliceBalance));
}
function test_wrap_convertsStinkyIntoGhost() public {
_prepareAndRoll(alice, amount, true, true);
uint256 aliceBalance = stnk.balanceOf(alice);
assertEq(ghst.balanceOf(alice), 0);
_prepareAndRoll(ALICE, AMOUNT, true, true);
uint256 aliceBalance = stnk.balanceOf(ALICE);
assertEq(ghst.balanceOf(ALICE), 0);
vm.startPrank(alice);
vm.startPrank(ALICE);
stnk.approve(address(staking), aliceBalance);
uint256 newBalance = staking.wrap(alice, aliceBalance);
uint256 newBalance = staking.wrap(ALICE, aliceBalance);
vm.stopPrank();
assertEq(stnk.balanceOf(alice), 0);
assertEq(ghst.balanceOf(alice), newBalance);
assertEq(stnk.balanceOf(ALICE), 0);
assertEq(ghst.balanceOf(ALICE), newBalance);
}
function test_wrap_convertsGhostToStinky() public {
_prepareAndRoll(alice, amount, false, true);
uint256 aliceBalance = ghst.balanceOf(alice);
assertEq(stnk.balanceOf(alice), 0);
_prepareAndRoll(ALICE, AMOUNT, false, true);
uint256 aliceBalance = ghst.balanceOf(ALICE);
assertEq(stnk.balanceOf(ALICE), 0);
vm.roll(block.number + 69);
vm.startPrank(alice);
vm.startPrank(ALICE);
ghst.approve(address(staking), aliceBalance);
uint256 newBalance = staking.unwrap(alice, aliceBalance);
uint256 newBalance = staking.unwrap(ALICE, aliceBalance);
vm.stopPrank();
assertEq(ghst.balanceOf(alice), 0);
assertEq(stnk.balanceOf(alice), newBalance);
assertEq(ghst.balanceOf(ALICE), 0);
assertEq(stnk.balanceOf(ALICE), newBalance);
}
function test_rebase_doesNothingIfTheBlockIsBeforeTheEpochEndBlock() public {
@ -373,11 +378,11 @@ contract StakingTest is Test {
assertEq(endBefore > block.timestamp, true);
vm.prank(address(staking));
stnk.transfer(alice, amount);
vm.prank(vault);
ftso.mint(address(staking), amount);
stnk.safeTransfer(ALICE, AMOUNT);
vm.prank(VAULT);
ftso.mint(address(staking), AMOUNT);
assertEq(stnk.balanceOf(alice), ftso.balanceOf(address(staking)));
assertEq(stnk.balanceOf(ALICE), ftso.balanceOf(address(staking)));
vm.warp(endBefore);
staking.rebase();
@ -393,9 +398,9 @@ contract StakingTest is Test {
assertEq(endBefore > block.timestamp, true);
vm.prank(address(staking));
stnk.transfer(alice, amount);
vm.prank(vault);
ftso.mint(address(staking), amount * 2);
stnk.safeTransfer(ALICE, AMOUNT);
vm.prank(VAULT);
ftso.mint(address(staking), AMOUNT * 2);
vm.warp(endBefore);
staking.rebase();
@ -403,13 +408,13 @@ contract StakingTest is Test {
(, uint48 numberAfter, uint48 endAfter, uint256 distributeAfter) = staking.epoch();
assertEq(endAfter, endBefore + length);
assertEq(numberAfter, numberBefore + 1);
assertEq(distributeAfter, amount);
assertEq(distributeAfter, AMOUNT);
}
function test_rebase_callDistributorIfSet() public {
(uint48 length, uint48 numberBefore, uint48 endBefore,) = staking.epoch();
assertEq(endBefore > block.timestamp, true);
uint256 extendedAmount = amount * 1e18;
uint256 extendedAmount = AMOUNT * 1e18;
GhostDistributor distributor = new GhostDistributor(
address(treasury),
@ -420,33 +425,33 @@ contract StakingTest is Test {
);
ERC20Mock reserve = new ERC20Mock("Reserve Token", "RET");
vm.startPrank(governor);
vm.startPrank(GOVERNOR);
authority.pushVault(address(treasury));
staking.setDistributor(address(distributor));
treasury.enable(ITreasury.STATUS.REWARDMANAGER, address(distributor), address(0));
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, alice, address(0));
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, bob, address(0));
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, ALICE, address(0));
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, BOB, address(0));
treasury.enable(ITreasury.STATUS.RESERVETOKEN, address(reserve), address(calculator));
vm.stopPrank();
vm.startPrank(bob);
reserve.mint(bob, extendedAmount);
vm.startPrank(BOB);
reserve.mint(BOB, extendedAmount);
reserve.approve(address(treasury), extendedAmount);
uint256 zeroProfit = treasury.tokenValue(address(reserve), extendedAmount);
treasury.deposit(address(reserve), extendedAmount, zeroProfit);
vm.stopPrank();
vm.startPrank(alice);
vm.startPrank(ALICE);
assertEq(ftso.balanceOf(address(staking)), 0);
reserve.mint(alice, extendedAmount);
reserve.mint(ALICE, extendedAmount);
reserve.approve(address(treasury), extendedAmount);
uint256 send = treasury.deposit(address(reserve), extendedAmount, 0);
assertEq(ftso.balanceOf(alice), send);
assertEq(ftso.balanceOf(ALICE), send);
assertEq(reserve.balanceOf(address(treasury)), extendedAmount * 2);
ftso.approve(address(staking), send);
staking.stake(send, alice, true, true);
staking.stake(send, ALICE, true, true);
assertEq(ftso.balanceOf(address(staking)), send);
assertEq(stnk.balanceOf(alice), send);
assertEq(stnk.balanceOf(ALICE), send);
vm.stopPrank();
assertEq(treasury.excessReserves(), send);
@ -465,7 +470,7 @@ contract StakingTest is Test {
}
function test_correctStakeAmountDuringRebase() public {
uint256 extendedAmount = amount * 1e18;
uint256 extendedAmount = AMOUNT * 1e18;
uint256 bounty = 1;
GhostDistributor distributor = new GhostDistributor(
address(treasury),
@ -476,34 +481,34 @@ contract StakingTest is Test {
);
ERC20Mock reserve = new ERC20Mock("Reserve Token", "RET");
vm.startPrank(governor);
vm.startPrank(GOVERNOR);
authority.pushVault(address(treasury));
staking.setDistributor(address(distributor));
distributor.setBounty(bounty);
treasury.enable(ITreasury.STATUS.REWARDMANAGER, address(distributor), address(0));
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, alice, address(0));
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, ALICE, address(0));
treasury.enable(ITreasury.STATUS.RESERVETOKEN, address(reserve), address(calculator));
vm.stopPrank();
vm.startPrank(alice);
reserve.mint(alice, extendedAmount);
vm.startPrank(ALICE);
reserve.mint(ALICE, extendedAmount);
reserve.approve(address(treasury), type(uint256).max);
uint256 profit = treasury.tokenValue(address(reserve), extendedAmount) - amount;
uint256 profit = treasury.tokenValue(address(reserve), extendedAmount) - AMOUNT;
treasury.deposit(address(reserve), extendedAmount, profit);
vm.stopPrank();
(,, uint48 end,) = staking.epoch();
skip(end);
vm.startPrank(alice);
vm.startPrank(ALICE);
ftso.approve(address(staking), type(uint256).max);
staking.stake(amount, alice, true, true);
staking.stake(AMOUNT, ALICE, true, true);
vm.stopPrank();
uint256 postBounty = amount + bounty;
uint256 postBounty = AMOUNT + bounty;
assertEq(stnk.balanceOf(alice), postBounty);
assertEq(stnk.balanceOf(ALICE), postBounty);
assertEq(ftso.balanceOf(address(staking)), postBounty);
skip(end + EPOCH_LENGTH);
@ -511,21 +516,21 @@ contract StakingTest is Test {
(,,, uint256 distribute) = staking.epoch();
assertEq(distribute, postBounty);
assertEq(stnk.balanceOf(alice), postBounty);
assertEq(stnk.balanceOf(ALICE), postBounty);
assertEq(ftso.balanceOf(address(staking)), 2 * postBounty + bounty);
}
function test_arbitraryAddressCouldNotAddGatekeeper(address someone, address maybeGatekeeper) public {
vm.assume(maybeGatekeeper != address(0) && someone != governor);
vm.assume(maybeGatekeeper != address(0) && someone != GOVERNOR);
vm.expectRevert();
vm.prank(someone);
staking.setDistributor(maybeGatekeeper);
}
function test_governorCouldSetGatekeeper(address maybeGatekeeper) public {
function test_GOVERNORCouldSetGatekeeper(address maybeGatekeeper) public {
vm.assume(maybeGatekeeper != address(0));
assertEq(staking.gatekeeper(), address(0));
vm.prank(governor);
vm.prank(GOVERNOR);
staking.setGatekeeperAddress(maybeGatekeeper);
assertEq(staking.gatekeeper(), maybeGatekeeper);
}
@ -533,80 +538,80 @@ contract StakingTest is Test {
function test_couldNotGhostIfNoGatekeeper() public {
assertEq(staking.ghostedSupply(), 0);
vm.expectRevert();
staking.ghost(bytes32(abi.encodePacked(alice)), amount);
staking.ghost(bytes32(abi.encodePacked(ALICE)), AMOUNT);
assertEq(staking.ghostedSupply(), 0);
}
function test_couldNotMaterializeIfNoGatekeeper() public {
assertEq(staking.ghostedSupply(), 0);
vm.expectRevert();
staking.materialize(alice, amount, 0, 0); // dummy rx and s
staking.materialize(ALICE, AMOUNT, 0, 0); // dummy rx and s
assertEq(staking.ghostedSupply(), 0);
}
function test_couldNotGhostTokensIfNoGhst() public {
assertEq(staking.gatekeeper(), address(0));
vm.prank(governor);
vm.prank(GOVERNOR);
staking.setGatekeeperAddress(address(gatekeeper));
assertEq(staking.gatekeeper(), address(gatekeeper));
assertEq(staking.ghostedSupply(), 0);
vm.expectRevert();
vm.prank(alice);
staking.ghost(bytes32(abi.encodePacked(alice)), amount);
vm.prank(ALICE);
staking.ghost(bytes32(abi.encodePacked(ALICE)), AMOUNT);
assertEq(staking.ghostedSupply(), 0);
}
function test_correctlyGhostTokens() public {
assertEq(staking.gatekeeper(), address(0));
vm.prank(governor);
vm.prank(GOVERNOR);
staking.setGatekeeperAddress(address(gatekeeper));
assertEq(staking.gatekeeper(), address(gatekeeper));
_prepareAndRoll(alice, bigAmount, true, true);
uint256 aliceBalance = stnk.balanceOf(alice);
_prepareAndRoll(ALICE, BIG_AMOUNT, true, true);
uint256 aliceBalance = stnk.balanceOf(ALICE);
vm.startPrank(alice);
vm.startPrank(ALICE);
stnk.approve(address(staking), aliceBalance);
uint256 ghstBalance = staking.wrap(alice, aliceBalance);
uint256 ghstBalance = staking.wrap(ALICE, aliceBalance);
vm.stopPrank();
assertEq(staking.ghostedSupply(), 0);
assertEq(stnk.circulatingSupply(), bigAmount - 1); // precision fix
assertEq(stnk.circulatingSupply(), BIG_AMOUNT - 1); // precision fix
assertEq(ghst.totalSupply(), ghstBalance);
vm.prank(alice);
staking.ghost(bytes32(abi.encodePacked(alice)), ghstBalance);
vm.prank(ALICE);
staking.ghost(bytes32(abi.encodePacked(ALICE)), ghstBalance);
assertEq(staking.ghostedSupply(), ghstBalance);
assertEq(stnk.circulatingSupply(), bigAmount - 1); // precision fix
assertEq(stnk.circulatingSupply(), BIG_AMOUNT - 1); // precision fix
assertEq(ghst.totalSupply(), 0);
}
function test_ghostTokensEmitsEvent() public {
assertEq(staking.gatekeeper(), address(0));
vm.prank(governor);
vm.prank(GOVERNOR);
staking.setGatekeeperAddress(address(gatekeeper));
assertEq(staking.gatekeeper(), address(gatekeeper));
_prepareAndRoll(alice, bigAmount, true, true);
uint256 aliceBalance = stnk.balanceOf(alice);
_prepareAndRoll(ALICE, BIG_AMOUNT, true, true);
uint256 aliceBalance = stnk.balanceOf(ALICE);
vm.startPrank(alice);
vm.startPrank(ALICE);
stnk.approve(address(staking), aliceBalance);
uint256 ghstBalance = staking.wrap(alice, aliceBalance);
uint256 ghstBalance = staking.wrap(ALICE, aliceBalance);
vm.stopPrank();
bytes32 receiver = bytes32(abi.encodePacked(alice));
bytes32 receiver = bytes32(abi.encodePacked(ALICE));
vm.expectEmit(true, true, true, false, address(gatekeeper));
emit Ghosted(receiver, ghstBalance);
vm.prank(alice);
vm.prank(ALICE);
staking.ghost(receiver, ghstBalance);
}
function _mintAndApprove(address who, uint256 value) internal {
vm.prank(vault);
vm.prank(VAULT);
ftso.mint(who, value);
vm.prank(who);
ftso.approve(address(staking), value);
@ -621,7 +626,7 @@ contract StakingTest is Test {
vm.prank(who);
uint256 rebased = staking.stake(value, who, rebase, claim);
(,, uint48 expiry,) = staking.warmupInfo(alice);
(,, uint48 expiry,) = staking.warmupInfo(ALICE);
vm.roll(expiry);
return rebased;
}

View File

@ -2,30 +2,32 @@ pragma solidity 0.8.20;
import {Test} from "forge-std/Test.sol";
import "../../src/FatsoERC20.sol";
import "../../src/StinkyERC20.sol";
import "../../src/GhstERC20.sol";
import "../../src/GhostAuthority.sol";
import "../../src/StakingDistributor.sol";
import "../../src/Treasury.sol";
import "../../src/Staking.sol";
import "../../src/mocks/ERC20Mock.sol";
import "../../src/StandardBondingCalculator.sol";
import {Fatso} from "../../src/FatsoERC20.sol";
import {Stinky} from "../../src/StinkyERC20.sol";
import {Ghost} from "../../src/GhstERC20.sol";
import {GhostAuthority} from "../../src/GhostAuthority.sol";
import {GhostDistributor} from "../../src/StakingDistributor.sol";
import {GhostTreasury} from "../../src/Treasury.sol";
import {GhostStaking} from "../../src/Staking.sol";
import {ERC20Mock} from "../../src/mocks/ERC20Mock.sol";
import {GhostBondingCalculator} from "../../src/StandardBondingCalculator.sol";
import {ITreasury} from "../../src/interfaces/ITreasury.sol";
contract StakingDistributorTest is Test {
address public constant owner = 0x0000000000000000000000000000000000000001;
address public constant governor = 0x0000000000000000000000000000000000000002;
address public constant guardian = 0x0000000000000000000000000000000000000003;
address public constant other = 0x0000000000000000000000000000000000000004;
address public constant alice = 0x0000000000000000000000000000000000000005;
address public constant bob = 0x0000000000000000000000000000000000000006;
address public constant OWNER = 0x0000000000000000000000000000000000000001;
address public constant GOVERNOR = 0x0000000000000000000000000000000000000002;
address public constant GUARDIAN = 0x0000000000000000000000000000000000000003;
address public constant OTHER = 0x0000000000000000000000000000000000000004;
address public constant ALICE = 0x0000000000000000000000000000000000000005;
address public constant BOB = 0x0000000000000000000000000000000000000006;
uint48 public constant EPOCH_LENGTH = 2200;
uint48 public constant EPOCH_NUMBER = 1;
uint48 public constant EPOCH_END_TIME = 1337;
uint256 public constant INITIAL_INDEX = 10819917194513808e56;
uint256 public constant amount = 69 * 1e18;
uint256 public constant AMOUNT = 69 * 1e18;
Fatso ftso;
Stinky stnk;
@ -41,12 +43,12 @@ contract StakingDistributorTest is Test {
uint256 public constant TEN_PERCENT = 1e5;
function setUp() public {
vm.startPrank(owner);
vm.startPrank(OWNER);
authority = new GhostAuthority(
governor,
guardian,
owner,
owner
GOVERNOR,
GUARDIAN,
OWNER,
OWNER
);
reserve = new ERC20Mock("Reserve Token", "RET");
ftso = new Fatso(address(authority), "Fatso", "FTSO");
@ -74,7 +76,7 @@ contract StakingDistributorTest is Test {
ghst.initialize(address(staking));
vm.stopPrank();
vm.startPrank(governor);
vm.startPrank(GOVERNOR);
staking.setDistributor(address(distributor));
authority.pushVault(address(treasury));
vm.stopPrank();
@ -83,7 +85,7 @@ contract StakingDistributorTest is Test {
function test_distribute_onlyByStaking() public {
_prepareTreasury();
vm.expectRevert();
vm.prank(other);
vm.prank(OTHER);
distributor.distribute();
vm.prank(address(staking));
@ -91,11 +93,11 @@ contract StakingDistributorTest is Test {
}
function test_bounty_onlyByGovernor(address who) public {
vm.assume(who != governor);
vm.assume(who != GOVERNOR);
_prepareTreasury();
assertEq(ftso.balanceOf(address(staking)), 0);
vm.prank(governor);
vm.prank(GOVERNOR);
distributor.setBounty(1337);
vm.prank(address(staking));
@ -104,23 +106,23 @@ contract StakingDistributorTest is Test {
}
function test_nextRewardFor_zeroIfAddressIsEmpty() public view {
assertEq(ftso.balanceOf(bob), 0);
assertEq(distributor.nextRewardFor(bob), 0);
assertEq(ftso.balanceOf(BOB), 0);
assertEq(distributor.nextRewardFor(BOB), 0);
}
function test_nextRewardFor_nextRewardForAddress() public {
_prepareTreasury();
vm.startPrank(bob);
reserve.mint(bob, amount);
reserve.approve(address(treasury), amount);
uint256 send = treasury.deposit(address(reserve), amount, treasury.tokenValue(address(reserve), amount));
vm.startPrank(BOB);
reserve.mint(BOB, AMOUNT);
reserve.approve(address(treasury), AMOUNT);
uint256 send = treasury.deposit(address(reserve), AMOUNT, treasury.tokenValue(address(reserve), AMOUNT));
vm.stopPrank();
assertEq(ftso.balanceOf(bob), send);
assertEq(distributor.nextRewardFor(bob), send * TEN_PERCENT / 1e6);
assertEq(ftso.balanceOf(BOB), send);
assertEq(distributor.nextRewardFor(BOB), send * TEN_PERCENT / 1e6);
}
function test_setBounty_shouldRevertIfNotByGovernor(address who) public {
vm.assume(who != governor);
vm.assume(who != GOVERNOR);
assertEq(distributor.bounty(), 0);
vm.expectRevert();
vm.prank(who);
@ -128,16 +130,16 @@ contract StakingDistributorTest is Test {
assertEq(distributor.bounty(), 0);
}
function test_setBounty_governorShouldSet() public {
function test_setBounty_GOVERNORShouldSet() public {
_prepareTreasury();
assertEq(distributor.bounty(), 0);
vm.prank(governor);
vm.prank(GOVERNOR);
distributor.setBounty(1337);
assertEq(distributor.bounty(), 1337);
}
function test_setPools_shouldRevertIfNotGovernor(address who) public {
vm.assume(who != governor);
vm.assume(who != GOVERNOR);
address[] memory newPools = new address[](1);
newPools[0] = who;
vm.expectRevert();
@ -145,19 +147,19 @@ contract StakingDistributorTest is Test {
distributor.setPools(newPools);
}
function test_setPools_governorShouldSet() public {
function test_setPools_GOVERNORShouldSet() public {
address[] memory newPools = new address[](1);
newPools[0] = other;
vm.prank(governor);
newPools[0] = OTHER;
vm.prank(GOVERNOR);
distributor.setPools(newPools);
assertEq(distributor.pools(0), other);
assertEq(distributor.pools(0), OTHER);
}
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);
newPools[0] = OTHER;
vm.prank(GOVERNOR);
distributor.setPools(newPools);
vm.expectRevert();
@ -165,43 +167,43 @@ contract StakingDistributorTest is Test {
distributor.removePool(0);
}
function test_removePools_governorShouldRemove() public {
function test_removePools_GOVERNORShouldRemove() public {
address[] memory newPools = new address[](1);
newPools[0] = other;
vm.startPrank(governor);
newPools[0] = OTHER;
vm.startPrank(GOVERNOR);
distributor.setPools(newPools);
assertEq(distributor.pools(0), other);
assertEq(distributor.pools(0), OTHER);
distributor.removePool(0);
vm.stopPrank();
}
function test_addPool_shouldRevertIfNotGovernor(address who) public {
vm.assume(who != governor);
vm.assume(who != GOVERNOR);
vm.expectRevert();
vm.prank(who);
distributor.addPool(other);
distributor.addPool(OTHER);
}
function test_addPool_shouldPushPoolWhenIndexTaken() public {
vm.prank(governor);
distributor.addPool(other);
assertEq(distributor.pools(0), other);
vm.prank(GOVERNOR);
distributor.addPool(OTHER);
assertEq(distributor.pools(0), OTHER);
}
function test_setAdjustment_shouldRevertIfNotGovernor(address who) public {
vm.assume(who != governor && who != guardian);
vm.assume(who != GOVERNOR && who != GUARDIAN);
vm.expectRevert();
vm.prank(who);
distributor.setAdjustment(69, 1337, true);
}
function test_setAdjustment_governorShouldIncreaseAdjustment() public {
vm.prank(governor);
function test_setAdjustment_GOVERNORShouldIncreaseAdjustment() public {
vm.prank(GOVERNOR);
distributor.setAdjustment(69, 1337, false);
}
function test_setAdjustment_shouldPopulateAdjustmentToRewardRateAfterRebase() public {
vm.prank(governor);
vm.prank(GOVERNOR);
distributor.setAdjustment(69, TEN_PERCENT * 2, true);
_prepareTreasury();
@ -213,7 +215,7 @@ contract StakingDistributorTest is Test {
}
function test_setAdjustment_willNotAdjustIfRateIsZero() public {
vm.prank(governor);
vm.prank(GOVERNOR);
distributor.setAdjustment(0, 1337, true);
_prepareTreasury();
@ -225,7 +227,7 @@ contract StakingDistributorTest is Test {
}
function test_setAdjustment_willStopIncreaseWhenTargetMet() public {
vm.prank(governor);
vm.prank(GOVERNOR);
distributor.setAdjustment(69, 1337, true);
_prepareTreasury();
@ -237,7 +239,7 @@ contract StakingDistributorTest is Test {
}
function test_setAdjustment_willStopDecreaseWhenTargetMet() public {
vm.prank(governor);
vm.prank(GOVERNOR);
distributor.setAdjustment(TEN_PERCENT, 1337, false);
_prepareTreasury();
@ -249,17 +251,17 @@ contract StakingDistributorTest is Test {
}
function _prepareTreasury() internal {
vm.startPrank(governor);
vm.startPrank(GOVERNOR);
treasury.enable(ITreasury.STATUS.REWARDMANAGER, address(distributor), address(0));
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, alice, address(0));
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, bob, address(0));
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, ALICE, address(0));
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, BOB, address(0));
treasury.enable(ITreasury.STATUS.RESERVETOKEN, address(reserve), address(calculator));
vm.stopPrank();
vm.startPrank(alice);
reserve.mint(alice, amount);
reserve.approve(address(treasury), amount);
treasury.deposit(address(reserve), amount, treasury.tokenValue(address(reserve), amount));
vm.startPrank(ALICE);
reserve.mint(ALICE, AMOUNT);
reserve.approve(address(treasury), AMOUNT);
treasury.deposit(address(reserve), AMOUNT, treasury.tokenValue(address(reserve), AMOUNT));
vm.stopPrank();
}
}

View File

@ -1,9 +1,12 @@
pragma solidity 0.8.20;
import {Test} from "forge-std/Test.sol";
import "@openzeppelin-contracts/token/ERC20/ERC20.sol";
import {ERC20} from "@openzeppelin-contracts/token/ERC20/ERC20.sol";
import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
abstract contract ERC20AllowanceTest is Test {
using SafeERC20 for ERC20;
ERC20 tokenAllowance;
uint256 amountAllowance;
uint256 maxAmountAllowance;
@ -43,9 +46,9 @@ abstract contract ERC20AllowanceTest is Test {
assertEq(tokenAllowance.balanceOf(aliceAllowance), amountAllowance);
assertEq(tokenAllowance.balanceOf(bobAllowance), 0);
assertEq(tokenAllowance.allowance(aliceAllowance, bobAllowance), amountAllowance);
vm.prank(bobAllowance);
bool success = tokenAllowance.transferFrom(aliceAllowance, bobAllowance, amountAllowance);
assertEq(success, true);
tokenAllowance.safeTransferFrom(aliceAllowance, bobAllowance, amountAllowance);
assertEq(tokenAllowance.balanceOf(aliceAllowance), 0);
assertEq(tokenAllowance.balanceOf(bobAllowance), amountAllowance);
assertEq(tokenAllowance.allowance(aliceAllowance, bobAllowance), 0);
@ -58,9 +61,13 @@ abstract contract ERC20AllowanceTest is Test {
assertEq(tokenAllowance.balanceOf(aliceAllowance), amountAllowance);
assertEq(tokenAllowance.balanceOf(bobAllowance), 0);
assertEq(tokenAllowance.allowance(aliceAllowance, bobAllowance), maxAmountAllowance);
vm.expectRevert();
vm.prank(bobAllowance);
tokenAllowance.transferFrom(aliceAllowance, bobAllowance, maxAmountAllowance);
assertEq(tokenAllowance.transferFrom(aliceAllowance, bobAllowance, maxAmountAllowance), false);
assertEq(tokenAllowance.balanceOf(aliceAllowance), amountAllowance);
assertEq(tokenAllowance.balanceOf(bobAllowance), 0);
assertEq(tokenAllowance.allowance(aliceAllowance, bobAllowance), maxAmountAllowance);
}
function test_allowance_couldNotTransferFromIfNotEnoughAllowance() public {
@ -70,9 +77,13 @@ abstract contract ERC20AllowanceTest is Test {
assertEq(tokenAllowance.balanceOf(aliceAllowance), maxRealAmountAllowance);
assertEq(tokenAllowance.balanceOf(bobAllowance), 0);
assertEq(tokenAllowance.allowance(aliceAllowance, bobAllowance), amountAllowance);
vm.expectRevert();
vm.prank(bobAllowance);
tokenAllowance.transferFrom(aliceAllowance, bobAllowance, maxAmountAllowance);
assertEq(tokenAllowance.transferFrom(aliceAllowance, bobAllowance, maxAmountAllowance), false);
assertEq(tokenAllowance.balanceOf(aliceAllowance), maxRealAmountAllowance);
assertEq(tokenAllowance.balanceOf(bobAllowance), 0);
assertEq(tokenAllowance.allowance(aliceAllowance, bobAllowance), amountAllowance);
}
function _mintAllowanceTokens(address who, uint256 value) internal virtual;

View File

@ -2,99 +2,100 @@ pragma solidity 0.8.20;
import {Test} from "forge-std/Test.sol";
import "../../src/FatsoERC20.sol";
import "../../src/GhostAuthority.sol";
import {Fatso} from "../../src/FatsoERC20.sol";
import {GhostAuthority} from "../../src/GhostAuthority.sol";
import {GhostAccessControlled} from "../../src/types/GhostAccessControlled.sol";
import "./Permit.t.sol";
import "./Allowance.t.sol";
import "./Transfer.t.sol";
import {ERC20PermitTest} from "./Permit.t.sol";
import {ERC20AllowanceTest} from "./Allowance.t.sol";
import {ERC20TransferTest} from "./Transfer.t.sol";
contract FatsoTest is Test, ERC20PermitTest, ERC20AllowanceTest, ERC20TransferTest {
GhostAuthority authority;
Fatso token;
address constant deployer = 0x0000000000000000000000000000000000000001;
address constant vault = 0x0000000000000000000000000000000000000002;
address constant alice = 0x0000000000000000000000000000000000000003;
address constant bob = 0x0000000000000000000000000000000000000004;
uint256 constant amount = 69;
uint256 constant maxAmount = type(uint256).max;
address constant DEPLOYER = 0x0000000000000000000000000000000000000001;
address constant VAULT = 0x0000000000000000000000000000000000000002;
address constant ALICE = 0x0000000000000000000000000000000000000003;
address constant BOB = 0x0000000000000000000000000000000000000004;
uint256 constant AMOUNT = 69;
uint256 constant MAX_AMOUNT = type(uint256).max;
string constant name = "Fatso Test Name";
string constant symbol = "FTSOTST";
string constant NAME = "Fatso Test Name";
string constant SYMBOL = "FTSOTST";
function setUp() public {
authority = new GhostAuthority(
deployer,
deployer,
deployer,
vault
DEPLOYER,
DEPLOYER,
DEPLOYER,
VAULT
);
token = new Fatso(address(authority), name, symbol);
initializePermit(address(token), amount, maxAmount);
initializeAllowance(alice, bob, address(token), amount, maxAmount, amount);
initializeTransfer(alice, bob, address(token), amount, 0);
token = new Fatso(address(authority), NAME, SYMBOL);
initializePermit(address(token), AMOUNT, MAX_AMOUNT);
initializeAllowance(ALICE, BOB, address(token), AMOUNT, MAX_AMOUNT, AMOUNT);
initializeTransfer(ALICE, BOB, address(token), AMOUNT, MAX_AMOUNT);
}
function test_mint_couldHappenFromVault() public {
uint256 totalSupply = token.totalSupply();
vm.prank(vault);
token.mint(alice, amount);
assertEq(token.totalSupply(), totalSupply + amount);
vm.prank(VAULT);
token.mint(ALICE, AMOUNT);
assertEq(token.totalSupply(), totalSupply + AMOUNT);
}
function test_mint_couldNotMintFromArbitraryAccount(address who) public {
vm.assume(who != vault);
vm.assume(who != VAULT);
vm.expectRevert();
vm.prank(who);
token.mint(alice, amount);
token.mint(ALICE, AMOUNT);
}
function test_correctlyConstructsAnERC20() public view {
assertEq(token.name(), name);
assertEq(token.symbol(), symbol);
assertEq(token.name(), NAME);
assertEq(token.symbol(), SYMBOL);
assertEq(token.decimals(), 9);
}
function test_mint_couldBeDoneByVault() public {
assertEq(token.balanceOf(bob), 0);
vm.prank(vault);
token.mint(bob, 69);
assertEq(token.balanceOf(bob), 69);
assertEq(token.balanceOf(BOB), 0);
vm.prank(VAULT);
token.mint(BOB, AMOUNT);
assertEq(token.balanceOf(BOB), AMOUNT);
}
function test_mint_couldNotBeDoneByArbitraryAccount() public {
vm.expectRevert(GhostAccessControlled.Unauthorized.selector);
vm.prank(deployer);
token.mint(bob, 69);
assertEq(token.balanceOf(bob), 0);
vm.prank(DEPLOYER);
token.mint(BOB, AMOUNT);
assertEq(token.balanceOf(BOB), 0);
}
function test_mint_totalSupplyIncreases() public {
assertEq(token.totalSupply(), 0);
vm.prank(vault);
token.mint(bob, 69);
assertEq(token.totalSupply(), 69);
vm.prank(VAULT);
token.mint(BOB, AMOUNT);
assertEq(token.totalSupply(), AMOUNT);
}
function test_burn_totalSupplyReduces() public {
_mintTokens(bob, 69);
vm.prank(bob);
token.burn(69);
_mintTokens(BOB, AMOUNT);
vm.prank(BOB);
token.burn(AMOUNT);
assertEq(token.totalSupply(), 0);
}
function test_burn_cannotExceedTotalSupply() public {
_mintTokens(bob, amount);
_mintTokens(BOB, AMOUNT);
vm.expectRevert();
vm.prank(bob);
token.burn(amount + 1);
assertEq(token.totalSupply(), amount);
vm.prank(BOB);
token.burn(AMOUNT + 1);
assertEq(token.totalSupply(), AMOUNT);
}
function _mintTokens(address who, uint256 value) internal {
uint256 totalSupply = token.totalSupply();
vm.prank(vault);
vm.prank(VAULT);
token.mint(who, value);
assertEq(token.totalSupply(), totalSupply + value);
}
@ -110,4 +111,8 @@ contract FatsoTest is Test, ERC20PermitTest, ERC20AllowanceTest, ERC20TransferTe
function _mintPermitTokens(address who, uint256 value) internal override {
_mintTokens(who, value);
}
function _getCurrentTotalSupply() internal override view returns (uint256) {
return token.totalSupply();
}
}

View File

@ -2,15 +2,15 @@ pragma solidity 0.8.20;
import {Test} from "forge-std/Test.sol";
import "../../src/GhstERC20.sol";
import "../../src/StinkyERC20.sol";
import "../../src/GhostAuthority.sol";
import "../../src/Staking.sol";
import {Ghost} from "../../src/GhstERC20.sol";
import {Stinky} from "../../src/StinkyERC20.sol";
import {GhostAuthority} from "../../src/GhostAuthority.sol";
import {GhostStaking} from "../../src/Staking.sol";
import "./Permit.t.sol";
import "./Allowance.t.sol";
import "./Transfer.t.sol";
import "./Votes.t.sol";
import {ERC20PermitTest} from "./Permit.t.sol";
import {ERC20AllowanceTest} from "./Allowance.t.sol";
import {ERC20TransferTest} from "./Transfer.t.sol";
import {ERC20VotesTest} from "./Votes.t.sol";
contract GhostTest is
Test,
@ -21,15 +21,15 @@ contract GhostTest is
{
uint256 constant public INITIAL_INDEX = 10819917194513808e56;
address constant initializer = 0x0000000000000000000000000000000000000001;
address constant alice = 0x0000000000000000000000000000000000000003;
address constant bob = 0x0000000000000000000000000000000000000004;
address constant treasury = 0x0000000000000000000000000000000000000005;
uint256 constant amount = 69;
uint256 constant maxAmount = type(uint256).max;
address constant INITIALIZER = 0x0000000000000000000000000000000000000001;
address constant ALICE = 0x0000000000000000000000000000000000000003;
address constant BOB = 0x0000000000000000000000000000000000000004;
address constant TREASURY = 0x0000000000000000000000000000000000000005;
uint256 constant AMOUNT = 69;
uint256 constant MAX_AMOUNT = type(uint256).max;
string constant name = "Ghost Test Name";
string constant symbol = "GHSTTST";
string constant NAME = "Ghost Test Name";
string constant SYMBOL = "GHSTTST";
Stinky stnk;
Ghost ghst;
@ -37,15 +37,15 @@ contract GhostTest is
GhostStaking public staking;
function setUp() public {
vm.startPrank(initializer);
vm.startPrank(INITIALIZER);
authority = new GhostAuthority(
initializer,
initializer,
initializer,
initializer
INITIALIZER,
INITIALIZER,
INITIALIZER,
INITIALIZER
);
stnk = new Stinky(INITIAL_INDEX, "Stinky", "STNK");
ghst = new Ghost(address(stnk), name, symbol);
ghst = new Ghost(address(stnk), NAME, SYMBOL);
staking = new GhostStaking(
address(0),
address(stnk),
@ -55,19 +55,19 @@ contract GhostTest is
1337,
address(authority)
);
stnk.initialize(address(staking), treasury, address(ghst));
stnk.initialize(address(staking), TREASURY, address(ghst));
ghst.initialize(address(staking));
vm.stopPrank();
initializePermit(address(ghst), amount, maxAmount);
initializeAllowance(alice, bob, address(ghst), amount, maxAmount, amount);
initializeTransfer(alice, bob, address(ghst), amount, 0);
initializeVotes(alice, bob, address(ghst), amount);
initializePermit(address(ghst), AMOUNT, MAX_AMOUNT);
initializeAllowance(ALICE, BOB, address(ghst), AMOUNT, MAX_AMOUNT, AMOUNT);
initializeTransfer(ALICE, BOB, address(ghst), AMOUNT, MAX_AMOUNT);
initializeVotes(ALICE, BOB, address(ghst), AMOUNT);
}
function test_isConstructedCorrectly() public view {
assertEq(ghst.name(), name);
assertEq(ghst.symbol(), symbol);
assertEq(ghst.name(), NAME);
assertEq(ghst.symbol(), SYMBOL);
assertEq(ghst.decimals(), 18);
assertEq(ghst.staking(), address(staking));
assertEq(ghst.stnk(), address(stnk));
@ -77,57 +77,57 @@ contract GhostTest is
function test_mint_couldBeDoneByStaking() public {
assertEq(ghst.totalSupply(), 0);
vm.prank(address(staking));
ghst.mint(alice, amount);
assertEq(ghst.totalSupply(), amount);
assertEq(ghst.balanceOf(alice), amount);
ghst.mint(ALICE, AMOUNT);
assertEq(ghst.totalSupply(), AMOUNT);
assertEq(ghst.balanceOf(ALICE), AMOUNT);
}
function test_mint_couldNotFromArbitraryAddress(address who) public {
vm.assume(who != address(staking));
vm.expectRevert();
vm.prank(who);
ghst.mint(alice, amount);
ghst.mint(ALICE, AMOUNT);
assertEq(ghst.totalSupply(), 0);
assertEq(ghst.balanceOf(who), 0);
}
function test_burn_couldBeDoneByOwner() public {
_mintTokens(alice, amount);
assertEq(ghst.totalSupply(), amount);
_mintTokens(ALICE, AMOUNT);
assertEq(ghst.totalSupply(), AMOUNT);
vm.prank(address(staking));
ghst.burn(alice, amount);
ghst.burn(ALICE, AMOUNT);
assertEq(ghst.totalSupply(), 0);
assertEq(ghst.balanceOf(alice), 0);
assertEq(ghst.balanceOf(ALICE), 0);
}
function test_burn_couldBeDoneByOwnerPartially(uint256 part) public {
vm.assume(part <= amount);
_mintTokens(alice, amount);
assertEq(ghst.totalSupply(), amount);
vm.assume(part <= AMOUNT);
_mintTokens(ALICE, AMOUNT);
assertEq(ghst.totalSupply(), AMOUNT);
vm.prank(address(staking));
ghst.burn(alice, part);
assertEq(ghst.totalSupply(), amount - part);
assertEq(ghst.balanceOf(alice), amount - part);
ghst.burn(ALICE, part);
assertEq(ghst.totalSupply(), AMOUNT - part);
assertEq(ghst.balanceOf(ALICE), AMOUNT - part);
}
function test_burn_couldNotBurnMoreThanBalance() public {
_mintTokens(alice, amount);
assertEq(ghst.totalSupply(), amount);
_mintTokens(ALICE, AMOUNT);
assertEq(ghst.totalSupply(), AMOUNT);
vm.expectRevert();
vm.prank(alice);
ghst.burn(alice, amount + 1);
assertEq(ghst.totalSupply(), amount);
assertEq(ghst.balanceOf(alice), amount);
vm.prank(ALICE);
ghst.burn(ALICE, AMOUNT + 1);
assertEq(ghst.totalSupply(), AMOUNT);
assertEq(ghst.balanceOf(ALICE), AMOUNT);
}
function test_burn_couldNotFromArbitraryAddress(address who) public {
vm.assume(who != address(staking));
_mintTokens(alice, amount);
_mintTokens(ALICE, AMOUNT);
vm.expectRevert();
vm.prank(who);
ghst.burn(alice, amount);
assertEq(ghst.totalSupply(), amount);
assertEq(ghst.balanceOf(alice), amount);
ghst.burn(ALICE, AMOUNT);
assertEq(ghst.totalSupply(), AMOUNT);
assertEq(ghst.balanceOf(ALICE), AMOUNT);
}
function test_balanceToCalculatesCorrectly(uint256 balanceToAmount) public view {
@ -169,4 +169,8 @@ contract GhostTest is
vm.prank(address(staking));
ghst.burn(who, value);
}
function _getCurrentTotalSupply() internal override view returns (uint256) {
return ghst.totalSupply();
}
}

View File

@ -1,10 +1,14 @@
pragma solidity 0.8.20;
import {Test} from "forge-std/Test.sol";
import "../../src/mocks/SigUtils.sol";
import "@openzeppelin-contracts/token/ERC20/extensions/ERC20Permit.sol";
import {SigUtils} from "../../src/mocks/SigUtils.sol";
import {ERC20Permit} from "@openzeppelin-contracts/token/ERC20/extensions/ERC20Permit.sol";
import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
abstract contract ERC20PermitTest is Test {
using SafeERC20 for ERC20Permit;
SigUtils sigUtils;
ERC20Permit permitToken;
@ -13,8 +17,8 @@ abstract contract ERC20PermitTest is Test {
uint256 permitAmount;
uint256 maxPermitAmount;
uint256 constant ownerPrivateKey = 0xA11CE;
uint256 constant spenderPrivateKey = 0xB0B;
uint256 constant OWNER_PRIVATE_KEY = 0xA11CE;
uint256 constant SPENDER_PRIVATE_KEY = 0xB0B;
function initializePermit(
address token,
@ -26,8 +30,8 @@ abstract contract ERC20PermitTest is Test {
permitToken = ERC20Permit(token);
sigUtils = new SigUtils(permitToken.DOMAIN_SEPARATOR());
owner = vm.addr(ownerPrivateKey);
spender = vm.addr(spenderPrivateKey);
owner = vm.addr(OWNER_PRIVATE_KEY);
spender = vm.addr(SPENDER_PRIVATE_KEY);
}
function test_permit_initialNonceIsZero() public view {
@ -47,7 +51,7 @@ abstract contract ERC20PermitTest is Test {
bytes32 digest = sigUtils.getTypedDataHash(permit);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(OWNER_PRIVATE_KEY, digest);
permitToken.permit(
permit.owner,
@ -75,7 +79,7 @@ abstract contract ERC20PermitTest is Test {
bytes32 digest = sigUtils.getTypedDataHash(permit);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(OWNER_PRIVATE_KEY, digest);
vm.warp(1 days + 1 seconds); // fast forward one second past the deadline
@ -103,7 +107,7 @@ abstract contract ERC20PermitTest is Test {
bytes32 digest = sigUtils.getTypedDataHash(permit);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(spenderPrivateKey, digest); // spender signs owner's approval
(uint8 v, bytes32 r, bytes32 s) = vm.sign(SPENDER_PRIVATE_KEY, digest); // spender signs owner's approval
vm.expectRevert();
permitToken.permit(
@ -129,7 +133,7 @@ abstract contract ERC20PermitTest is Test {
bytes32 digest = sigUtils.getTypedDataHash(permit);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(OWNER_PRIVATE_KEY, digest);
vm.expectRevert();
permitToken.permit(
@ -155,7 +159,7 @@ abstract contract ERC20PermitTest is Test {
bytes32 digest = sigUtils.getTypedDataHash(permit);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(OWNER_PRIVATE_KEY, digest);
permitToken.permit(
permit.owner,
@ -191,7 +195,7 @@ abstract contract ERC20PermitTest is Test {
bytes32 digest = sigUtils.getTypedDataHash(permit);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(OWNER_PRIVATE_KEY, digest);
permitToken.permit(
permit.owner,
@ -204,7 +208,7 @@ abstract contract ERC20PermitTest is Test {
);
vm.prank(spender);
permitToken.transferFrom(owner, spender, permitAmount);
permitToken.safeTransferFrom(owner, spender, permitAmount);
assertEq(permitToken.balanceOf(owner), 0);
assertEq(permitToken.balanceOf(spender), permitAmount);
@ -223,7 +227,7 @@ abstract contract ERC20PermitTest is Test {
bytes32 digest = sigUtils.getTypedDataHash(permit);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(OWNER_PRIVATE_KEY, digest);
permitToken.permit(
permit.owner,
@ -236,11 +240,10 @@ abstract contract ERC20PermitTest is Test {
);
vm.prank(spender);
permitToken.transferFrom(owner, spender, permitAmount);
permitToken.safeTransferFrom(owner, spender, permitAmount);
assertEq(permitToken.balanceOf(owner), 0);
assertEq(permitToken.balanceOf(spender), permitAmount);
assertEq(permitToken.allowance(owner, spender), maxPermitAmount);
}
function test_permit_invalidAllowance() public {
@ -255,7 +258,7 @@ abstract contract ERC20PermitTest is Test {
bytes32 digest = sigUtils.getTypedDataHash(permit);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(OWNER_PRIVATE_KEY, digest);
permitToken.permit(
permit.owner,
@ -269,22 +272,25 @@ abstract contract ERC20PermitTest is Test {
vm.expectRevert();
vm.prank(spender);
permitToken.transferFrom(owner, spender, permitAmount);
assertEq(permitToken.transferFrom(owner, spender, permitAmount), false);
assertEq(permitToken.balanceOf(owner), permitAmount);
assertEq(permitToken.balanceOf(spender), 0);
}
function test_permit_ivnalidBalance() public {
_mintPermitTokens(owner, permitAmount);
function test_permit_invalidBalance() public {
uint256 smallAmount = 2;
_mintPermitTokens(owner, smallAmount);
SigUtils.Permit memory permit = SigUtils.Permit({
owner: owner,
spender: spender,
value: 2,
value: permitAmount,
nonce: 0,
deadline: 1 days
});
bytes32 digest = sigUtils.getTypedDataHash(permit);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(OWNER_PRIVATE_KEY, digest);
permitToken.permit(
permit.owner,
@ -298,7 +304,9 @@ abstract contract ERC20PermitTest is Test {
vm.expectRevert();
vm.prank(spender);
permitToken.transferFrom(owner, spender, permitAmount);
assertEq(permitToken.transferFrom(owner, spender, permitAmount), false);
assertEq(permitToken.balanceOf(owner), smallAmount);
assertEq(permitToken.balanceOf(spender), 0);
}
function _mintPermitTokens(address who, uint256 permitAmount) internal virtual;

View File

@ -1,20 +1,18 @@
pragma solidity 0.8.20;
import {Test} from "forge-std/Test.sol";
import "../../src/mocks/Reserve.sol";
import "@openzeppelin-contracts/token/ERC20/IERC20.sol";
import {Reserve} from "../../src/mocks/Reserve.sol";
contract ReserveTest is Test {
address constant initializer = 0x0000000000000000000000000000000000000001;
address constant faucetAddress = 0x0000000000000000000000000000000000000002;
address constant aliceAddress = 0x0000000000000000000000000000000000000003;
address constant bobAddress = 0x0000000000000000000000000000000000000004;
uint256 constant conversionRate = 69 * 10e5;
uint256 constant sendAmount = 1e16;
address constant INITIALIZER = 0x0000000000000000000000000000000000000001;
address constant FAUCET_ADDRESS = 0x0000000000000000000000000000000000000002;
address constant ALICE_ADDRESS = 0x0000000000000000000000000000000000000003;
address constant BOB_ADDRESS = 0x0000000000000000000000000000000000000004;
uint256 constant CONVERSION_RATE = 69 * 10e5;
uint256 constant SEND_AMOUNT = 1e16;
string constant name = "Test DAI";
string constant symbol = "tDAI";
string constant NAME = "Test DAI";
string constant SYMBOL = "tDAI";
Reserve reserve;
@ -22,68 +20,68 @@ contract ReserveTest is Test {
event LogStakingContractUpdated(address stakingContract);
function setUp() public {
vm.prank(initializer);
vm.prank(INITIALIZER);
reserve = new Reserve(
"Test DAI",
"tDAI",
conversionRate
CONVERSION_RATE
);
}
function test_isConstructedCorrectly() public view {
assertEq(reserve.name(), name);
assertEq(reserve.symbol(), symbol);
assertEq(reserve.name(), NAME);
assertEq(reserve.symbol(), SYMBOL);
assertEq(reserve.decimals(), 18);
assertEq(reserve.totalSupply(), 0);
assertEq(reserve.donationRate(), 0);
assertEq(reserve.conversionRate(), conversionRate);
assertEq(reserve.conversionRate(), CONVERSION_RATE);
}
function test_mint_couldBeDoneWithValue() public {
assertEq(reserve.balanceOf(aliceAddress), 0);
deal(aliceAddress, sendAmount);
assertEq(reserve.balanceOf(ALICE_ADDRESS), 0);
deal(ALICE_ADDRESS, SEND_AMOUNT);
vm.prank(aliceAddress);
reserve.mint{ value: sendAmount }(aliceAddress);
vm.prank(ALICE_ADDRESS);
reserve.mint{ value: SEND_AMOUNT }(ALICE_ADDRESS);
assertEq(reserve.balanceOf(aliceAddress), sendAmount * conversionRate);
assertEq(reserve.balanceOf(ALICE_ADDRESS), SEND_AMOUNT * CONVERSION_RATE);
}
function test_mint_couldNotBeDoneWithoutEmptyValue() public {
assertEq(reserve.balanceOf(aliceAddress), 0);
vm.prank(aliceAddress);
reserve.mint(aliceAddress);
assertEq(reserve.balanceOf(aliceAddress), 0);
assertEq(reserve.balanceOf(ALICE_ADDRESS), 0);
vm.prank(ALICE_ADDRESS);
reserve.mint(ALICE_ADDRESS);
assertEq(reserve.balanceOf(ALICE_ADDRESS), 0);
}
function test_mint_couldNotMintIfNotEnoughValue() public {
assertEq(reserve.balanceOf(aliceAddress), 0);
assertEq(reserve.balanceOf(ALICE_ADDRESS), 0);
bool didRevert = false;
vm.prank(aliceAddress);
try reserve.mint{ value: type(uint256).max }(aliceAddress) {
vm.prank(ALICE_ADDRESS);
try reserve.mint{ value: type(uint256).max }(ALICE_ADDRESS) {
} catch {
didRevert = true;
}
assertEq(didRevert, true);
assertEq(reserve.balanceOf(aliceAddress), 0);
assertEq(reserve.balanceOf(ALICE_ADDRESS), 0);
}
function test_mint_superMintCouldBeDoneFromDeployer() public {
assertEq(reserve.balanceOf(initializer), 0);
vm.prank(initializer);
reserve.superMint(initializer, 420 * 1e18);
assertEq(reserve.balanceOf(initializer), 420 * 1e18);
assertEq(reserve.balanceOf(INITIALIZER), 0);
vm.prank(INITIALIZER);
reserve.superMint(INITIALIZER, 420 * 1e18);
assertEq(reserve.balanceOf(INITIALIZER), 420 * 1e18);
}
function test_mint_superMintCouldNotBeDoneFromArbitraryAddress() public {
assertEq(reserve.balanceOf(aliceAddress), 0);
assertEq(reserve.balanceOf(ALICE_ADDRESS), 0);
vm.expectRevert();
vm.prank(aliceAddress);
reserve.superMint(aliceAddress, 420 * 1e18);
assertEq(reserve.balanceOf(aliceAddress), 0);
vm.prank(ALICE_ADDRESS);
reserve.superMint(ALICE_ADDRESS, 420 * 1e18);
assertEq(reserve.balanceOf(ALICE_ADDRESS), 0);
}
function test_mint_donationNotTakenIfRateNotSet() public {
@ -91,11 +89,11 @@ contract ReserveTest is Test {
assertEq(reserve.donationRate(), 0);
assertEq(reserve.accumulatedDonation(), 0);
deal(aliceAddress, sendAmount);
vm.prank(aliceAddress);
reserve.mint{ value: sendAmount }(aliceAddress);
deal(ALICE_ADDRESS, SEND_AMOUNT);
vm.prank(ALICE_ADDRESS);
reserve.mint{ value: SEND_AMOUNT }(ALICE_ADDRESS);
assertEq(reserve.totalSupply(), sendAmount * conversionRate);
assertEq(reserve.totalSupply(), SEND_AMOUNT * CONVERSION_RATE);
assertEq(reserve.donationRate(), 0);
assertEq(reserve.accumulatedDonation(), 0);
}
@ -105,195 +103,195 @@ contract ReserveTest is Test {
assertEq(reserve.donationRate(), 0);
assertEq(reserve.accumulatedDonation(), 0);
vm.prank(initializer);
vm.prank(INITIALIZER);
reserve.changeDonationRate(1e4); // 10%
deal(aliceAddress, sendAmount);
vm.prank(aliceAddress);
reserve.mint{ value: sendAmount }(aliceAddress);
deal(ALICE_ADDRESS, SEND_AMOUNT);
vm.prank(ALICE_ADDRESS);
reserve.mint{ value: SEND_AMOUNT }(ALICE_ADDRESS);
assertEq(reserve.totalSupply(), sendAmount * conversionRate);
assertEq(reserve.totalSupply(), SEND_AMOUNT * CONVERSION_RATE);
assertEq(reserve.donationRate(), 1e4);
assertEq(reserve.accumulatedDonation(), sendAmount * 1e4 / 1e5);
assertEq(reserve.accumulatedDonation(), SEND_AMOUNT * 1e4 / 1e5);
}
function test_rate_couldBeChangedByDeployer() public {
assertEq(reserve.conversionRate(), conversionRate);
vm.prank(initializer);
assertEq(reserve.conversionRate(), CONVERSION_RATE);
vm.prank(INITIALIZER);
reserve.changeRate(1337);
assertEq(reserve.conversionRate(), 1337);
}
function test_rate_couldNotBeChangedByArbitraryAddress() public {
assertEq(reserve.conversionRate(), conversionRate);
assertEq(reserve.conversionRate(), CONVERSION_RATE);
vm.expectRevert();
vm.prank(aliceAddress);
vm.prank(ALICE_ADDRESS);
reserve.changeRate(1337);
assertEq(reserve.conversionRate(), conversionRate);
assertEq(reserve.conversionRate(), CONVERSION_RATE);
}
function test_withdraw_couldBeDoneByDeployer() public {
assertEq(address(reserve).balance, 0);
vm.prank(initializer);
vm.prank(INITIALIZER);
reserve.changeDonationRate(1e5);
deal(aliceAddress, sendAmount);
vm.prank(aliceAddress);
reserve.mint{ value: sendAmount }(aliceAddress);
deal(ALICE_ADDRESS, SEND_AMOUNT);
vm.prank(ALICE_ADDRESS);
reserve.mint{ value: SEND_AMOUNT }(ALICE_ADDRESS);
assertEq(address(reserve).balance, sendAmount);
assertEq(address(reserve).balance, SEND_AMOUNT);
vm.prank(initializer);
reserve.withdraw(payable(faucetAddress));
vm.prank(INITIALIZER);
reserve.withdraw(payable(FAUCET_ADDRESS));
assertEq(address(reserve).balance, 0);
assertEq(faucetAddress.balance, sendAmount);
assertEq(FAUCET_ADDRESS.balance, SEND_AMOUNT);
}
function test_withdraw_onlyAccumulatedDonationsCouldBeWithdrawn() public {
assertEq(address(reserve).balance, 0);
vm.prank(initializer);
vm.prank(INITIALIZER);
reserve.changeDonationRate(5 * 1e4); // 50%
deal(aliceAddress, sendAmount);
vm.prank(aliceAddress);
reserve.mint{ value: sendAmount }(aliceAddress);
deal(ALICE_ADDRESS, SEND_AMOUNT);
vm.prank(ALICE_ADDRESS);
reserve.mint{ value: SEND_AMOUNT }(ALICE_ADDRESS);
assertEq(address(reserve).balance, sendAmount);
assertEq(address(reserve).balance, SEND_AMOUNT);
vm.prank(initializer);
reserve.withdraw(payable(faucetAddress));
assertEq(address(reserve).balance, sendAmount / 2);
assertEq(faucetAddress.balance, sendAmount / 2);
vm.prank(INITIALIZER);
reserve.withdraw(payable(FAUCET_ADDRESS));
assertEq(address(reserve).balance, SEND_AMOUNT / 2);
assertEq(FAUCET_ADDRESS.balance, SEND_AMOUNT / 2);
vm.prank(initializer);
reserve.withdraw(payable(faucetAddress));
assertEq(address(reserve).balance, sendAmount / 2);
assertEq(faucetAddress.balance, sendAmount / 2);
vm.prank(INITIALIZER);
reserve.withdraw(payable(FAUCET_ADDRESS));
assertEq(address(reserve).balance, SEND_AMOUNT / 2);
assertEq(FAUCET_ADDRESS.balance, SEND_AMOUNT / 2);
}
function test_withdraw_couldNotBeDoneByArbitraryAddress() public {
assertEq(address(reserve).balance, 0);
deal(aliceAddress, sendAmount);
vm.prank(aliceAddress);
reserve.mint{ value: sendAmount }(aliceAddress);
deal(ALICE_ADDRESS, SEND_AMOUNT);
vm.prank(ALICE_ADDRESS);
reserve.mint{ value: SEND_AMOUNT }(ALICE_ADDRESS);
assertEq(address(reserve).balance, sendAmount);
assertEq(address(reserve).balance, SEND_AMOUNT);
vm.expectRevert();
vm.prank(aliceAddress);
reserve.withdraw(payable(aliceAddress));
vm.prank(ALICE_ADDRESS);
reserve.withdraw(payable(ALICE_ADDRESS));
assertEq(address(reserve).balance, sendAmount);
assertEq(aliceAddress.balance, 0);
assertEq(address(reserve).balance, SEND_AMOUNT);
assertEq(ALICE_ADDRESS.balance, 0);
}
function test_burn_shouldReturnFullAmountIfRateNotSet() public {
deal(aliceAddress, sendAmount);
vm.prank(aliceAddress);
reserve.mint{ value: sendAmount }(aliceAddress);
deal(ALICE_ADDRESS, SEND_AMOUNT);
vm.prank(ALICE_ADDRESS);
reserve.mint{ value: SEND_AMOUNT }(ALICE_ADDRESS);
uint256 balance = reserve.balanceOf(aliceAddress);
assertEq(reserve.totalSupply(), sendAmount * conversionRate);
uint256 balance = reserve.balanceOf(ALICE_ADDRESS);
assertEq(reserve.totalSupply(), SEND_AMOUNT * CONVERSION_RATE);
assertEq(reserve.totalSupply(), balance);
assertEq(reserve.accumulatedDonation(), 0);
assertEq(aliceAddress.balance, 0);
assertEq(ALICE_ADDRESS.balance, 0);
vm.prank(aliceAddress);
vm.prank(ALICE_ADDRESS);
reserve.burn(balance);
assertEq(reserve.totalSupply(), 0);
assertEq(reserve.balanceOf(aliceAddress), 0);
assertEq(reserve.balanceOf(ALICE_ADDRESS), 0);
assertEq(reserve.accumulatedDonation(), 0);
assertEq(aliceAddress.balance, sendAmount);
assertEq(ALICE_ADDRESS.balance, SEND_AMOUNT);
}
function test_burn_shouldTakeDonationInAccordanceToRate() public {
vm.prank(initializer);
vm.prank(INITIALIZER);
reserve.changeDonationRate(5 * 1e4); // 50%
deal(aliceAddress, sendAmount);
vm.prank(aliceAddress);
reserve.mint{ value: sendAmount }(aliceAddress);
deal(ALICE_ADDRESS, SEND_AMOUNT);
vm.prank(ALICE_ADDRESS);
reserve.mint{ value: SEND_AMOUNT }(ALICE_ADDRESS);
uint256 balance = reserve.balanceOf(aliceAddress);
assertEq(reserve.totalSupply(), sendAmount * conversionRate);
uint256 balance = reserve.balanceOf(ALICE_ADDRESS);
assertEq(reserve.totalSupply(), SEND_AMOUNT * CONVERSION_RATE);
assertEq(reserve.totalSupply(), balance);
assertEq(reserve.accumulatedDonation(), sendAmount / 2);
assertEq(aliceAddress.balance, 0);
assertEq(reserve.accumulatedDonation(), SEND_AMOUNT / 2);
assertEq(ALICE_ADDRESS.balance, 0);
vm.prank(aliceAddress);
vm.prank(ALICE_ADDRESS);
reserve.burn(balance);
assertEq(reserve.totalSupply(), 0);
assertEq(reserve.balanceOf(aliceAddress), 0);
assertEq(reserve.accumulatedDonation(), sendAmount / 2);
assertEq(aliceAddress.balance, sendAmount / 2);
assertEq(reserve.balanceOf(ALICE_ADDRESS), 0);
assertEq(reserve.accumulatedDonation(), SEND_AMOUNT / 2);
assertEq(ALICE_ADDRESS.balance, SEND_AMOUNT / 2);
}
function test_burn_multipleUsersAccumulateInTotal() public {
vm.prank(initializer);
vm.prank(INITIALIZER);
reserve.changeDonationRate(5 * 1e4); // 50%
deal(aliceAddress, sendAmount);
vm.prank(aliceAddress);
reserve.mint{ value: sendAmount }(aliceAddress);
deal(ALICE_ADDRESS, SEND_AMOUNT);
vm.prank(ALICE_ADDRESS);
reserve.mint{ value: SEND_AMOUNT }(ALICE_ADDRESS);
vm.prank(initializer);
reserve.changeRate(conversionRate * 9);
vm.prank(INITIALIZER);
reserve.changeRate(CONVERSION_RATE * 9);
deal(bobAddress, sendAmount);
vm.prank(bobAddress);
reserve.mint{ value: sendAmount }(bobAddress);
deal(BOB_ADDRESS, SEND_AMOUNT);
vm.prank(BOB_ADDRESS);
reserve.mint{ value: SEND_AMOUNT }(BOB_ADDRESS);
assertEq(aliceAddress.balance, 0);
assertEq(bobAddress.balance, 0);
uint256 aliceBalance = reserve.balanceOf(aliceAddress);
uint256 bobBalance = reserve.balanceOf(bobAddress);
assertEq(ALICE_ADDRESS.balance, 0);
assertEq(BOB_ADDRESS.balance, 0);
uint256 aliceBalance = reserve.balanceOf(ALICE_ADDRESS);
uint256 bobBalance = reserve.balanceOf(BOB_ADDRESS);
uint256 bobEstimation = reserve.estimateAmount(bobBalance);
uint256 aliceEstimation = reserve.estimateAmount(aliceBalance);
vm.prank(bobAddress);
vm.prank(BOB_ADDRESS);
reserve.burn(bobBalance);
vm.prank(aliceAddress);
vm.prank(ALICE_ADDRESS);
reserve.burn(aliceBalance);
assertEq(bobAddress.balance, sendAmount * 9 / 10);
assertEq(bobAddress.balance, bobEstimation);
assertEq(aliceAddress.balance, sendAmount / 10);
assertEq(aliceAddress.balance, aliceEstimation);
assertEq(BOB_ADDRESS.balance, SEND_AMOUNT * 9 / 10);
assertEq(BOB_ADDRESS.balance, bobEstimation);
assertEq(ALICE_ADDRESS.balance, SEND_AMOUNT / 10);
assertEq(ALICE_ADDRESS.balance, aliceEstimation);
assertEq(reserve.totalSupply(), 0);
assertEq(reserve.accumulatedDonation(), sendAmount);
assertEq(address(reserve).balance, sendAmount);
assertEq(reserve.accumulatedDonation(), SEND_AMOUNT);
assertEq(address(reserve).balance, SEND_AMOUNT);
}
function test_reserveFallback() public {
assertEq(address(reserve).balance, 0);
assertEq(reserve.balanceOf(aliceAddress), 0);
assertEq(reserve.balanceOf(ALICE_ADDRESS), 0);
vm.prank(aliceAddress);
vm.prank(ALICE_ADDRESS);
(bool success, ) = address(reserve).call(abi.encodeWithSignature("nonExistentFunction()"));
require(success, "Fallback call failed");
assertEq(address(reserve).balance, 0);
assertEq(reserve.balanceOf(aliceAddress), 0);
assertEq(reserve.balanceOf(ALICE_ADDRESS), 0);
}
function test_reserveReceive() public {
assertEq(address(reserve).balance, 0);
assertEq(reserve.balanceOf(aliceAddress), 0);
assertEq(reserve.balanceOf(ALICE_ADDRESS), 0);
deal(aliceAddress, sendAmount);
vm.prank(aliceAddress);
(bool success, ) = address(reserve).call{value: sendAmount}("");
deal(ALICE_ADDRESS, SEND_AMOUNT);
vm.prank(ALICE_ADDRESS);
(bool success, ) = address(reserve).call{value: SEND_AMOUNT}("");
require(success, "Transfer of native failed");
uint256 estimatedReceiveAmount = sendAmount * reserve.conversionRate();
assertEq(address(reserve).balance, sendAmount);
assertEq(reserve.balanceOf(aliceAddress), estimatedReceiveAmount);
uint256 estimatedReceiveAmount = SEND_AMOUNT * reserve.conversionRate();
assertEq(address(reserve).balance, SEND_AMOUNT);
assertEq(reserve.balanceOf(ALICE_ADDRESS), estimatedReceiveAmount);
}
}

View File

@ -1,21 +1,22 @@
pragma solidity 0.8.20;
import {Test} from "forge-std/Test.sol";
import {console} from "forge-std/console.sol";
import "../../src/FatsoERC20.sol";
import "../../src/StinkyERC20.sol";
import "../../src/GhstERC20.sol";
import "../../src/GhostAuthority.sol";
import "../../src/Staking.sol";
import {Fatso} from "../../src/FatsoERC20.sol";
import {Stinky} from "../../src/StinkyERC20.sol";
import {Ghost} from "../../src/GhstERC20.sol";
import {GhostAuthority} from "../../src/GhostAuthority.sol";
import {GhostStaking} from "../../src/Staking.sol";
import "./Permit.t.sol";
import "./Allowance.t.sol";
import "./Transfer.t.sol";
import {ERC20PermitTest} from "./Permit.t.sol";
import {ERC20AllowanceTest} from "./Allowance.t.sol";
import {ERC20TransferTest} from "./Transfer.t.sol";
import "@openzeppelin-contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
contract StinkyTest is Test, ERC20PermitTest, ERC20AllowanceTest, ERC20TransferTest {
using SafeERC20 for Stinky;
uint256 private constant INITIAL_SHARES_SUPPLY = 5 * 10**15; // 15 or 16
uint256 private constant TOTAL_SHARES = type(uint256).max - (type(uint256).max % INITIAL_SHARES_SUPPLY);
@ -28,29 +29,28 @@ contract StinkyTest is Test, ERC20PermitTest, ERC20AllowanceTest, ERC20TransferT
GhostAuthority public authority;
GhostStaking public staking;
address constant initializer = 0x0000000000000000000000000000000000000001;
address constant alice = 0x0000000000000000000000000000000000000003;
address constant bob = 0x0000000000000000000000000000000000000004;
address constant treasury = 0x0000000000000000000000000000000000000005;
uint256 constant amount = 69;
uint256 constant maxAmount = type(uint256).max;
address constant INITIALIZER = 0x0000000000000000000000000000000000000001;
address constant ALICE = 0x0000000000000000000000000000000000000003;
address constant BOB = 0x0000000000000000000000000000000000000004;
address constant TREASURY = 0x0000000000000000000000000000000000000005;
uint256 constant AMOUNT = 69;
string constant name = "Stinky Test Name";
string constant symbol = "STNKTST";
string constant NAME = "Stinky Test Name";
string constant SYMBOL = "STNKTST";
event Transfer(address indexed from, address indexed to, uint256 value);
event LogStakingContractUpdated(address stakingContract);
function setUp() public {
vm.startPrank(initializer);
vm.startPrank(INITIALIZER);
authority = new GhostAuthority(
initializer,
initializer,
initializer,
initializer
INITIALIZER,
INITIALIZER,
INITIALIZER,
INITIALIZER
);
ftso = new Fatso(address(authority), "Fatso", "FTSO");
stnk = new Stinky(INITIAL_INDEX, name, symbol);
stnk = new Stinky(INITIAL_INDEX, NAME, SYMBOL);
ghst = new Ghost(address(stnk), "Ghost", "GHST");
staking = new GhostStaking(
address(ftso),
@ -64,41 +64,41 @@ contract StinkyTest is Test, ERC20PermitTest, ERC20AllowanceTest, ERC20TransferT
ghst.initialize(address(staking));
vm.stopPrank();
initializePermit(address(stnk), amount, maxAmount);
initializeAllowance(alice, bob, address(stnk), amount, maxAmount, TOTAL_INITIAL_SUPPLY);
initializeTransfer(alice, bob, address(stnk), amount, TOTAL_INITIAL_SUPPLY);
initializePermit(address(stnk), AMOUNT, TOTAL_INITIAL_SUPPLY);
initializeAllowance(ALICE, BOB, address(stnk), AMOUNT, TOTAL_INITIAL_SUPPLY, TOTAL_INITIAL_SUPPLY);
initializeTransfer(ALICE, BOB, address(stnk), AMOUNT, TOTAL_INITIAL_SUPPLY);
}
function test_isConstructedCorrectly() public view {
assertEq(stnk.name(), name);
assertEq(stnk.symbol(), symbol);
assertEq(stnk.name(), NAME);
assertEq(stnk.symbol(), SYMBOL);
assertEq(stnk.decimals(), 9);
assertEq(stnk.totalSupply(), TOTAL_INITIAL_SUPPLY);
assertEq(stnk.index(), INITIAL_INDEX / (TOTAL_SHARES / INITIAL_SHARES_SUPPLY));
}
function test_initialization_couldBeDoneByInitializer() public {
vm.prank(initializer);
stnk.initialize(address(staking), treasury, address(ghst));
vm.prank(INITIALIZER);
stnk.initialize(address(staking), TREASURY, address(ghst));
assertEq(stnk.staking(), address(staking));
assertEq(stnk.treasury(), treasury);
assertEq(stnk.treasury(), TREASURY);
assertEq(stnk.ghst(), address(ghst));
}
function test_initialization_couldNotBeDoneByArbitraryAddress(address who) public {
vm.assume(who != initializer);
vm.assume(who != INITIALIZER);
vm.expectRevert();
vm.prank(who);
stnk.initialize(who, who, who);
}
function test_initialization_couldBeDoneOnlyOnce() public {
vm.prank(initializer);
stnk.initialize(address(staking), treasury, address(ghst));
vm.prank(INITIALIZER);
stnk.initialize(address(staking), TREASURY, address(ghst));
vm.expectRevert();
vm.prank(initializer);
stnk.initialize(initializer, initializer, initializer);
vm.prank(INITIALIZER);
stnk.initialize(INITIALIZER, INITIALIZER, INITIALIZER);
}
function test_initialization_correctTotalSharesOfStaking() public {
@ -119,56 +119,56 @@ contract StinkyTest is Test, ERC20PermitTest, ERC20AllowanceTest, ERC20TransferT
}
function test_circulatingSupplyIsCorrecBasedOnTotalSupply() public {
_mintTokens(alice, amount);
assertEq(stnk.circulatingSupply(), amount);
assertEq(stnk.balanceOf(address(staking)), TOTAL_INITIAL_SUPPLY - amount);
_mintTokens(ALICE, AMOUNT);
assertEq(stnk.circulatingSupply(), AMOUNT);
assertEq(stnk.balanceOf(address(staking)), TOTAL_INITIAL_SUPPLY - AMOUNT);
}
function test_circulatingSupplyIsCorrecBasedOnStakingBalance() public {
_mintTokens(alice, amount);
assertEq(stnk.circulatingSupply(), amount);
vm.prank(alice);
stnk.transfer(address(staking), amount);
_mintTokens(ALICE, AMOUNT);
assertEq(stnk.circulatingSupply(), AMOUNT);
vm.prank(ALICE);
stnk.safeTransfer(address(staking), AMOUNT);
assertEq(stnk.circulatingSupply(), 0);
assertEq(stnk.balanceOf(address(staking)), TOTAL_INITIAL_SUPPLY);
}
function test_circulatingSupplyIsCorrectBasedOnSupplyInWarmup() public {
vm.startPrank(initializer);
stnk.initialize(address(staking), treasury, address(ghst));
vm.startPrank(INITIALIZER);
stnk.initialize(address(staking), TREASURY, address(ghst));
staking.setWarmupPeriod(1337);
ftso.mint(alice, amount);
ftso.mint(ALICE, AMOUNT);
vm.stopPrank();
assertEq(stnk.circulatingSupply(), 0);
assertEq(ftso.totalSupply(), amount);
assertEq(ftso.totalSupply(), AMOUNT);
assertEq(stnk.totalSupply(), TOTAL_INITIAL_SUPPLY);
assertEq(stnk.balanceOf(address(staking)), TOTAL_INITIAL_SUPPLY);
vm.startPrank(alice);
ftso.approve(address(staking), amount);
staking.stake(amount, alice, true, true);
vm.startPrank(ALICE);
ftso.approve(address(staking), AMOUNT);
staking.stake(AMOUNT, ALICE, true, true);
vm.stopPrank();
assertEq(stnk.circulatingSupply(), amount);
assertEq(ftso.totalSupply(), amount);
assertEq(stnk.circulatingSupply(), AMOUNT);
assertEq(ftso.totalSupply(), AMOUNT);
assertEq(stnk.totalSupply(), TOTAL_INITIAL_SUPPLY);
assertEq(stnk.balanceOf(address(staking)), TOTAL_INITIAL_SUPPLY);
}
function test_circulatingSupplyIsCorrectBasedOnGhostSupply() public {
vm.startPrank(initializer);
stnk.initialize(address(staking), treasury, address(ghst));
ftso.mint(alice, amount);
vm.startPrank(INITIALIZER);
stnk.initialize(address(staking), TREASURY, address(ghst));
ftso.mint(ALICE, AMOUNT);
vm.stopPrank();
vm.startPrank(alice);
ftso.approve(address(staking), amount);
staking.stake(amount, alice, false, true);
vm.startPrank(ALICE);
ftso.approve(address(staking), AMOUNT);
staking.stake(AMOUNT, ALICE, false, true);
vm.stopPrank();
uint256 balanceTo = amount * 1e18 / ghst.index();
assertEq(ghst.balanceOf(alice), balanceTo);
uint256 balanceTo = AMOUNT * 1e18 / ghst.index();
assertEq(ghst.balanceOf(ALICE), balanceTo);
uint256 balanceFrom = ghst.totalSupply() * ghst.index() / 1e18;
assertEq(stnk.circulatingSupply(), balanceFrom);
}
@ -196,75 +196,78 @@ contract StinkyTest is Test, ERC20PermitTest, ERC20AllowanceTest, ERC20TransferT
assertEq(stnk.sharesForBalance(amountToTest), amountToTest * sharesPerUnit);
}
function test_balanceForSharesCorrect(uint256 amountToTest) public view {
function test_balance_forSharesCorrect(uint256 amountToTest) public view {
vm.assume(amountToTest < type(uint128).max);
uint256 sharesPerUnit = stnk.sharesForBalance(1);
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_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);
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);
stnk.changeDebt(AMOUNT, ALICE, true);
}
function test_bala_nceCouldNotDropBelowDebt() public {
_mintTokens(alice, amount * 3);
vm.prank(treasury);
stnk.changeDebt(amount, alice, true);
assertEq(stnk.balanceOf(bob), 0);
vm.prank(alice);
stnk.transfer(bob, amount);
assertEq(stnk.balanceOf(bob), amount);
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);
stnk.transfer(bob, amount * 2);
assertEq(stnk.balanceOf(bob), amount);
vm.prank(ALICE);
assertEq(stnk.transfer(BOB, AMOUNT * 2), false);
assertEq(stnk.balanceOf(BOB), AMOUNT);
assertEq(stnk.balanceOf(ALICE), AMOUNT * 2);
}
function test_rebase_couldNotBeDoneFromArbitraryAddress(address someone) public {
vm.assume(someone != address(staking));
vm.expectRevert();
vm.prank(someone);
stnk.rebase(amount, 1337);
stnk.rebase(AMOUNT, 1337);
}
function test_rebase_nothingChangesIfDeltaIsZero() public {
uint256 epoch = 1337;
uint256 prevSupply = stnk.totalSupply();
uint256 prevIndex = stnk.index();
_mintTokens(alice, amount);
assertEq(stnk.balanceOf(alice), amount);
_mintTokens(ALICE, AMOUNT);
assertEq(stnk.balanceOf(ALICE), AMOUNT);
vm.prank(address(staking));
stnk.rebase(0, epoch);
assertEq(stnk.totalSupply(), prevSupply);
assertEq(stnk.index(), prevIndex);
assertEq(stnk.balanceOf(alice), amount);
assertEq(stnk.balanceOf(ALICE), AMOUNT);
}
function test_rebase_changesIfProfitAndCirculatingSupply() public {
uint256 epoch = 1337;
_mintTokens(alice, amount);
assertEq(stnk.balanceOf(alice), amount);
_mintTokens(ALICE, AMOUNT);
assertEq(stnk.balanceOf(ALICE), AMOUNT);
vm.prank(address(staking));
stnk.rebase(amount, epoch);
assertEq(stnk.balanceOf(alice), amount * 2);
stnk.rebase(AMOUNT, epoch);
assertEq(stnk.balanceOf(ALICE), AMOUNT * 2);
assertEq(stnk.totalSupply(), TOTAL_INITIAL_SUPPLY * 2);
}
@ -273,9 +276,9 @@ contract StinkyTest is Test, ERC20PermitTest, ERC20AllowanceTest, ERC20TransferT
_initialize();
assertEq(stnk.balanceOf(address(staking)), TOTAL_INITIAL_SUPPLY);
vm.prank(address(staking));
stnk.rebase(amount, epoch);
assertEq(stnk.balanceOf(address(staking)), TOTAL_INITIAL_SUPPLY + amount);
assertEq(stnk.totalSupply(), TOTAL_INITIAL_SUPPLY + amount);
stnk.rebase(AMOUNT, epoch);
assertEq(stnk.balanceOf(address(staking)), TOTAL_INITIAL_SUPPLY + AMOUNT);
assertEq(stnk.totalSupply(), TOTAL_INITIAL_SUPPLY + AMOUNT);
}
function test_rebase_couldNotExceedMaxSupply() public {
@ -289,10 +292,10 @@ contract StinkyTest is Test, ERC20PermitTest, ERC20AllowanceTest, ERC20TransferT
}
function _initialize() internal {
vm.prank(initializer);
stnk.initialize(address(staking), treasury, address(ghst));
vm.prank(INITIALIZER);
stnk.initialize(address(staking), TREASURY, address(ghst));
assertEq(stnk.staking(), address(staking));
assertEq(stnk.treasury(), treasury);
assertEq(stnk.treasury(), TREASURY);
assertEq(stnk.ghst(), address(ghst));
}
@ -300,7 +303,7 @@ contract StinkyTest is Test, ERC20PermitTest, ERC20AllowanceTest, ERC20TransferT
_initialize();
uint256 totalSupply = stnk.totalSupply();
vm.prank(address(staking));
stnk.transfer(who, value);
stnk.safeTransfer(who, value);
assertEq(stnk.totalSupply(), totalSupply);
}
@ -315,4 +318,8 @@ contract StinkyTest is Test, ERC20PermitTest, ERC20AllowanceTest, ERC20TransferT
function _mintPermitTokens(address who, uint256 value) internal override {
_mintTokens(who, value);
}
function _getCurrentTotalSupply() internal override pure returns (uint256) {
return TOTAL_INITIAL_SUPPLY;
}
}

View File

@ -1,9 +1,12 @@
pragma solidity 0.8.20;
import {Test} from "forge-std/Test.sol";
import "@openzeppelin-contracts/token/ERC20/ERC20.sol";
import {ERC20} from "@openzeppelin-contracts/token/ERC20/ERC20.sol";
import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
abstract contract ERC20TransferTest is Test {
using SafeERC20 for ERC20;
ERC20 tokenTransfer;
uint256 amountTransfer;
uint256 totalSupplyTransfer;
@ -30,8 +33,7 @@ abstract contract ERC20TransferTest is Test {
assertEq(tokenTransfer.balanceOf(aliceTransfer), amountTransfer);
assertEq(tokenTransfer.balanceOf(bobTransfer), 0);
vm.prank(aliceTransfer);
bool success = tokenTransfer.transfer(bobTransfer, amountTransfer);
assertEq(success, true);
tokenTransfer.safeTransfer(bobTransfer, amountTransfer);
assertEq(tokenTransfer.balanceOf(bobTransfer), amountTransfer);
assertEq(tokenTransfer.balanceOf(aliceTransfer), 0);
}
@ -43,8 +45,7 @@ abstract contract ERC20TransferTest is Test {
assertEq(tokenTransfer.balanceOf(aliceTransfer), fuzzingTransferAmount);
assertEq(tokenTransfer.balanceOf(bobTransfer), 0);
vm.prank(aliceTransfer);
bool success = tokenTransfer.transfer(bobTransfer, fuzzingTransferAmount);
assertEq(success, true);
tokenTransfer.safeTransfer(bobTransfer, fuzzingTransferAmount);
assertEq(tokenTransfer.balanceOf(bobTransfer), fuzzingTransferAmount);
assertEq(tokenTransfer.balanceOf(aliceTransfer), 0);
}
@ -53,25 +54,20 @@ abstract contract ERC20TransferTest is Test {
_mintTransferTokens(aliceTransfer, amountTransfer);
vm.expectRevert();
vm.prank(aliceTransfer);
tokenTransfer.transfer(bobTransfer, type(uint256).max);
assertEq(tokenTransfer.transfer(bobTransfer, amountTransfer * 2), false);
assertEq(tokenTransfer.balanceOf(aliceTransfer), amountTransfer);
assertEq(tokenTransfer.balanceOf(bobTransfer), 0);
}
function test_transfer_doesNotChangeTotalSupply() public {
assertEq(tokenTransfer.totalSupply(), totalSupplyTransfer);
_mintTransferTokens(aliceTransfer, amountTransfer);
if (totalSupplyTransfer == 0) {
assertEq(tokenTransfer.totalSupply(), amountTransfer);
vm.prank(aliceTransfer);
tokenTransfer.transfer(bobTransfer, amountTransfer);
assertEq(tokenTransfer.totalSupply(), amountTransfer);
} else {
assertEq(tokenTransfer.totalSupply(), totalSupplyTransfer);
vm.prank(aliceTransfer);
tokenTransfer.transfer(bobTransfer, amountTransfer);
assertEq(tokenTransfer.totalSupply(), totalSupplyTransfer);
}
assertEq(tokenTransfer.totalSupply(), _getCurrentTotalSupply());
vm.prank(aliceTransfer);
tokenTransfer.safeTransfer(bobTransfer, amountTransfer);
assertEq(tokenTransfer.totalSupply(), _getCurrentTotalSupply());
}
function _mintTransferTokens(address who, uint256 value) internal virtual;
function _getCurrentTotalSupply() internal virtual returns (uint256);
}

View File

@ -1,18 +1,21 @@
pragma solidity 0.8.20;
import {Test} from "forge-std/Test.sol";
import "@openzeppelin-contracts/token/ERC20/extensions/ERC20Votes.sol";
import "@openzeppelin-contracts/utils/structs/Checkpoints.sol";
import {ERC20Votes} from "@openzeppelin-contracts/token/ERC20/extensions/ERC20Votes.sol";
import {Checkpoints} from "@openzeppelin-contracts/utils/structs/Checkpoints.sol";
import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
abstract contract ERC20VotesTest is Test {
using SafeERC20 for ERC20Votes;
ERC20Votes tokenVotes;
uint256 amountVotes;
address public aliceVotes;
address public bobVotes;
address constant public charlieVotes = 0x0000000000000000000000000000000000000069;
address constant public eveVotes = 0x0000000000000000000000000000000000001337;
address constant public CHARLIE_VOTES = 0x0000000000000000000000000000000000000069;
address constant public EVE_VOTES = 0x0000000000000000000000000000000000001337;
event DelegateChanged(
address indexed delegator,
@ -72,15 +75,15 @@ abstract contract ERC20VotesTest is Test {
vm.expectRevert();
vm.prank(aliceVotes);
tokenVotes.delegate(charlieVotes);
tokenVotes.delegate(CHARLIE_VOTES);
assertEq(tokenVotes.delegates(aliceVotes), aliceVotes);
assertEq(tokenVotes.delegates(bobVotes), bobVotes);
assertEq(tokenVotes.delegates(charlieVotes), charlieVotes);
assertEq(tokenVotes.delegates(CHARLIE_VOTES), CHARLIE_VOTES);
assertEq(tokenVotes.getVotes(aliceVotes), amountVotes);
assertEq(tokenVotes.getVotes(bobVotes), 0);
assertEq(tokenVotes.getVotes(charlieVotes), 0);
assertEq(tokenVotes.getVotes(CHARLIE_VOTES), 0);
// 1 Checkpoint generated
assertEq(tokenVotes.numCheckpoints(aliceVotes), 1);
@ -145,39 +148,39 @@ abstract contract ERC20VotesTest is Test {
emit DelegateVotesChanged(address(this), 100, 100 - 1);
vm.prank(address(this));
tokenVotes.transfer(bobVotes, 1);
tokenVotes.safeTransfer(bobVotes, 1);
assertEq(tokenVotes.getVotes(address(this)), 100 - 1);
// case 2: 'to' has a delegatee
_mintVotesTokens(charlieVotes, 100);
_mintVotesTokens(CHARLIE_VOTES, 100);
vm.expectRevert();
vm.prank(charlieVotes);
tokenVotes.delegate(eveVotes);
vm.prank(CHARLIE_VOTES);
tokenVotes.delegate(EVE_VOTES);
vm.roll(3);
vm.expectEmit(true, false, false, true, address(tokenVotes));
emit DelegateVotesChanged(charlieVotes, 100, 100 - 1);
emit DelegateVotesChanged(CHARLIE_VOTES, 100, 100 - 1);
vm.expectEmit(true, false, false, true, address(tokenVotes));
emit DelegateVotesChanged(eveVotes, 0, 1);
emit DelegateVotesChanged(EVE_VOTES, 0, 1);
vm.prank(charlieVotes);
tokenVotes.transfer(eveVotes, 1);
assertEq(tokenVotes.getVotes(charlieVotes), 100 - 1);
assertEq(tokenVotes.getVotes(eveVotes), 1);
vm.prank(CHARLIE_VOTES);
tokenVotes.safeTransfer(EVE_VOTES, 1);
assertEq(tokenVotes.getVotes(CHARLIE_VOTES), 100 - 1);
assertEq(tokenVotes.getVotes(EVE_VOTES), 1);
// test for {transferFrom}
// case 3: 'to' has no delegatee
vm.roll(4);
assertEq(tokenVotes.delegates(aliceVotes), aliceVotes);
assertEq(tokenVotes.delegates(bobVotes), bobVotes);
assertEq(tokenVotes.delegates(charlieVotes), charlieVotes);
assertEq(tokenVotes.delegates(eveVotes), eveVotes);
assertEq(tokenVotes.delegates(CHARLIE_VOTES), CHARLIE_VOTES);
assertEq(tokenVotes.delegates(EVE_VOTES), EVE_VOTES);
tokenVotes.approve(aliceVotes, 100);
assertEq(tokenVotes.delegates(aliceVotes), aliceVotes);
assertEq(tokenVotes.delegates(bobVotes), bobVotes);
assertEq(tokenVotes.delegates(charlieVotes), charlieVotes);
assertEq(tokenVotes.delegates(eveVotes), eveVotes);
assertEq(tokenVotes.delegates(CHARLIE_VOTES), CHARLIE_VOTES);
assertEq(tokenVotes.delegates(EVE_VOTES), EVE_VOTES);
vm.startPrank(aliceVotes);
vm.expectEmit(true, false, false, true, address(tokenVotes));
@ -185,21 +188,21 @@ abstract contract ERC20VotesTest is Test {
vm.expectEmit(true, false, false, true, address(tokenVotes));
emit DelegateVotesChanged(bobVotes, 1, 2);
tokenVotes.transferFrom(address(this), bobVotes, 1);
tokenVotes.safeTransferFrom(address(this), bobVotes, 1);
// case 4: 'to' has a delegatee
vm.roll(5);
assertEq(tokenVotes.delegates(aliceVotes), aliceVotes);
assertEq(tokenVotes.delegates(bobVotes), bobVotes);
assertEq(tokenVotes.delegates(charlieVotes), charlieVotes);
assertEq(tokenVotes.delegates(eveVotes), eveVotes);
assertEq(tokenVotes.delegates(CHARLIE_VOTES), CHARLIE_VOTES);
assertEq(tokenVotes.delegates(EVE_VOTES), EVE_VOTES);
vm.expectEmit(true, false, false, true, address(tokenVotes));
emit DelegateVotesChanged(address(this), 98, 98 - 1);
vm.expectEmit(true, false, false, true, address(tokenVotes));
emit DelegateVotesChanged(charlieVotes, 99, 99 + 1);
emit DelegateVotesChanged(CHARLIE_VOTES, 99, 99 + 1);
tokenVotes.transferFrom(address(this), charlieVotes, 1);
tokenVotes.safeTransferFrom(address(this), CHARLIE_VOTES, 1);
}
function test_votes_getPastVotesAndGetPastTotalSupply() external {

View File

@ -1,23 +1,23 @@
pragma solidity 0.8.20;
import {Test} from "forge-std/Test.sol";
import {StdChains} from "forge-std/StdChains.sol";
import "../../src/FatsoERC20.sol";
import "../../src/GhostAuthority.sol";
import "../../src/Treasury.sol";
import "../../src/StandardBondingCalculator.sol";
import "../../src/mocks/ERC20Mock.sol";
import {Fatso} from "../../src/FatsoERC20.sol";
import {GhostAuthority} from "../../src/GhostAuthority.sol";
import {GhostTreasury} from "../../src/Treasury.sol";
import {GhostBondingCalculator} from "../../src/StandardBondingCalculator.sol";
import {ERC20Mock} from "../../src/mocks/ERC20Mock.sol";
import {ITreasury} from "../../src/interfaces/ITreasury.sol";
import {IUniswapV2Pair} from "@uniswap-v2-core/interfaces/IUniswapV2Pair.sol";
contract GhostTreasuryTest is Test {
address public constant owner = 0x0000000000000000000000000000000000000001;
address public constant governor = 0x0000000000000000000000000000000000000002;
address public constant guardian = 0x0000000000000000000000000000000000000003;
address public constant other = 0x0000000000000000000000000000000000000004;
address public constant alice = 0x0000000000000000000000000000000000000005;
address public constant bob = 0x0000000000000000000000000000000000000006;
address public constant OWNER = 0x0000000000000000000000000000000000000001;
address public constant GOVERNOR = 0x0000000000000000000000000000000000000002;
address public constant GUARDIAN = 0x0000000000000000000000000000000000000003;
address public constant ALICE = 0x0000000000000000000000000000000000000004;
uint256 public constant amount = 69 * 1e18;
uint256 public constant AMOUNT = 69 * 1e18;
Fatso ftso;
ERC20Mock reserve;
@ -27,12 +27,12 @@ contract GhostTreasuryTest is Test {
GhostBondingCalculator calculator;
function setUp() public {
vm.startPrank(owner);
vm.startPrank(OWNER);
authority = new GhostAuthority(
governor,
guardian,
owner,
owner
GOVERNOR,
GUARDIAN,
OWNER,
OWNER
);
reserve = new ERC20Mock("Reserve Token", "RET");
liquidity = new ERC20Mock("Liquidity Token", "LDT");
@ -41,11 +41,11 @@ contract GhostTreasuryTest is Test {
calculator = new GhostBondingCalculator(address(ftso), 1, 1);
vm.stopPrank();
vm.prank(governor);
vm.prank(GOVERNOR);
authority.pushVault(address(treasury));
vm.startPrank(alice);
reserve.mint(alice, amount);
vm.startPrank(ALICE);
reserve.mint(ALICE, AMOUNT);
reserve.approve(address(treasury), type(uint256).max);
ftso.approve(address(treasury), type(uint256).max);
vm.stopPrank();
@ -53,109 +53,109 @@ contract GhostTreasuryTest is Test {
function test_deposit_onlyIfApprovedTokenAndApprovedAddress() public {
vm.expectRevert();
vm.prank(alice);
treasury.deposit(address(reserve), amount, 0);
vm.prank(ALICE);
treasury.deposit(address(reserve), AMOUNT, 0);
vm.prank(governor);
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, alice, address(0));
vm.prank(GOVERNOR);
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, ALICE, address(0));
vm.expectRevert();
vm.prank(alice);
treasury.deposit(address(reserve), amount, 0);
vm.prank(ALICE);
treasury.deposit(address(reserve), AMOUNT, 0);
vm.prank(governor);
vm.prank(GOVERNOR);
treasury.enable(ITreasury.STATUS.RESERVETOKEN, address(reserve), address(calculator));
vm.prank(alice);
uint256 send = treasury.deposit(address(reserve), amount, 0);
vm.prank(ALICE);
uint256 send = treasury.deposit(address(reserve), AMOUNT, 0);
assertEq(ftso.balanceOf(alice), send);
assertEq(reserve.balanceOf(address(treasury)), amount);
assertEq(ftso.balanceOf(ALICE), send);
assertEq(reserve.balanceOf(address(treasury)), AMOUNT);
}
function test_withdraw_onlyIfApprovedTokenAndApprovedAddress() public {
vm.startPrank(governor);
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, alice, address(0));
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.prank(ALICE);
treasury.deposit(address(reserve), AMOUNT, 0);
vm.expectRevert();
vm.prank(alice);
treasury.withdraw(address(reserve), amount);
vm.prank(ALICE);
treasury.withdraw(address(reserve), AMOUNT);
vm.prank(governor);
treasury.enable(ITreasury.STATUS.RESERVESPENDER, alice, address(0));
vm.prank(GOVERNOR);
treasury.enable(ITreasury.STATUS.RESERVESPENDER, ALICE, address(0));
vm.prank(alice);
treasury.withdraw(address(reserve), amount);
vm.prank(ALICE);
treasury.withdraw(address(reserve), AMOUNT);
assertEq(ftso.balanceOf(alice), 0);
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));
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);
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(ALICE);
treasury.manage(address(reserve), AMOUNT);
vm.prank(governor);
treasury.enable(ITreasury.STATUS.RESERVEMANAGER, alice, address(0));
vm.prank(GOVERNOR);
treasury.enable(ITreasury.STATUS.RESERVEMANAGER, ALICE, address(0));
vm.prank(alice);
treasury.manage(address(reserve), amount);
vm.prank(ALICE);
treasury.manage(address(reserve), AMOUNT);
assertEq(ftso.balanceOf(alice), send);
assertEq(reserve.balanceOf(alice), amount);
assertEq(ftso.balanceOf(ALICE), send);
assertEq(reserve.balanceOf(ALICE), AMOUNT);
assertEq(reserve.balanceOf(address(treasury)), 0);
}
function test_mint_onlyIfApprovedTokenAndApprovedAddress() public {
vm.startPrank(governor);
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, alice, address(0));
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);
uint256 tokenValue = treasury.tokenValue(address(reserve), AMOUNT);
vm.prank(ALICE);
uint256 send = treasury.deposit(address(reserve), AMOUNT, tokenValue);
vm.expectRevert();
vm.prank(alice);
treasury.mint(alice, 69);
vm.prank(ALICE);
treasury.mint(ALICE, 69);
vm.prank(governor);
treasury.enable(ITreasury.STATUS.REWARDMANAGER, alice, address(0));
vm.prank(GOVERNOR);
treasury.enable(ITreasury.STATUS.REWARDMANAGER, ALICE, address(0));
vm.prank(alice);
treasury.mint(alice, 69);
vm.prank(ALICE);
treasury.mint(ALICE, 69);
assertEq(ftso.balanceOf(alice), send + 69);
assertEq(reserve.balanceOf(address(treasury)), amount);
assertEq(ftso.balanceOf(ALICE), send + 69);
assertEq(reserve.balanceOf(address(treasury)), AMOUNT);
}
function test_auditTreasuryReserves() public {
vm.startPrank(governor);
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, alice, address(0));
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);
treasury.deposit(address(reserve), amount, tokenValue);
uint256 tokenValue = treasury.tokenValue(address(reserve), AMOUNT);
vm.prank(ALICE);
treasury.deposit(address(reserve), AMOUNT, tokenValue);
vm.prank(governor);
vm.prank(GOVERNOR);
treasury.auditReserves();
assertEq(treasury.permissions(ITreasury.STATUS.RESERVETOKEN, address(reserve)), true);
@ -166,7 +166,7 @@ contract GhostTreasuryTest is Test {
}
function test_randomAddressCouldNotEnableStatusAndCalculator(address who) public {
vm.assume(who != governor);
vm.assume(who != GOVERNOR);
vm.expectRevert();
vm.prank(who);
@ -182,7 +182,7 @@ contract GhostTreasuryTest is Test {
}
function test_enableStatusAndCalculator() public {
vm.startPrank(governor);
vm.startPrank(GOVERNOR);
treasury.enable(ITreasury.STATUS.RESERVETOKEN, address(reserve), address(calculator));
treasury.enable(ITreasury.STATUS.LIQUIDITYTOKEN, address(liquidity), address(calculator));
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, msg.sender, address(0));
@ -200,9 +200,9 @@ contract GhostTreasuryTest is Test {
}
function test_randomAddressCouldNotDisableStatusByAddress(address who) public {
vm.assume(who != governor && who != guardian);
vm.assume(who != GOVERNOR && who != GUARDIAN);
vm.startPrank(governor);
vm.startPrank(GOVERNOR);
treasury.enable(ITreasury.STATUS.RESERVETOKEN, address(reserve), address(calculator));
treasury.enable(ITreasury.STATUS.LIQUIDITYTOKEN, address(liquidity), address(calculator));
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, msg.sender, address(0));
@ -222,13 +222,13 @@ contract GhostTreasuryTest is Test {
}
function test_disableStatusByAddress() public {
vm.startPrank(governor);
vm.startPrank(GOVERNOR);
treasury.enable(ITreasury.STATUS.RESERVETOKEN, address(reserve), address(calculator));
treasury.enable(ITreasury.STATUS.LIQUIDITYTOKEN, address(liquidity), address(calculator));
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, msg.sender, address(0));
vm.stopPrank();
vm.startPrank(governor);
vm.startPrank(GOVERNOR);
treasury.disable(ITreasury.STATUS.RESERVETOKEN, address(reserve));
treasury.disable(ITreasury.STATUS.LIQUIDITYTOKEN, address(liquidity));
treasury.disable(ITreasury.STATUS.RESERVEDEPOSITOR, msg.sender);
@ -242,7 +242,7 @@ contract GhostTreasuryTest is Test {
function test_mainnet_disableReserveAndLiquidity() public {
address realDexPair = 0xB20bd5D04BE54f870D5C0d3cA85d82b34B836405;
vm.startPrank(governor);
vm.startPrank(GOVERNOR);
treasury.enable(ITreasury.STATUS.RESERVETOKEN, address(reserve), address(calculator));
treasury.enable(ITreasury.STATUS.LIQUIDITYTOKEN, realDexPair, address(calculator));
vm.stopPrank();
@ -258,7 +258,7 @@ contract GhostTreasuryTest is Test {
assertEq(liquidityValue + reserveEps >= reserves, true);
assertEq(liquidityValue - reserveEps <= reserves, true);
vm.startPrank(governor);
vm.startPrank(GOVERNOR);
treasury.disable(ITreasury.STATUS.RESERVETOKEN, address(reserve));
treasury.disable(ITreasury.STATUS.LIQUIDITYTOKEN, realDexPair);
vm.stopPrank();
@ -270,7 +270,7 @@ contract GhostTreasuryTest is Test {
function test_mainnet_tokenValueIsCorret() public {
address realDexPair = 0xB20bd5D04BE54f870D5C0d3cA85d82b34B836405;
vm.startPrank(governor);
vm.startPrank(GOVERNOR);
treasury.enable(ITreasury.STATUS.RESERVETOKEN, address(reserve), address(calculator));
treasury.enable(ITreasury.STATUS.LIQUIDITYTOKEN, realDexPair, address(calculator));
vm.stopPrank();
@ -288,7 +288,7 @@ contract GhostTreasuryTest is Test {
}
function test_randomAddressCouldNotTriggerTimelock(address who) public {
vm.assume(who != governor);
vm.assume(who != GOVERNOR);
vm.expectRevert();
vm.prank(who);
treasury.toggleTimelock();
@ -298,26 +298,26 @@ contract GhostTreasuryTest is Test {
assertEq(treasury.timelockEnabled(), false);
assertEq(treasury.onChainGovernanceTimelock(), 0);
vm.prank(governor);
vm.prank(GOVERNOR);
treasury.toggleTimelock();
uint256 queuedTime = block.number + 69 * 7;
assertEq(treasury.timelockEnabled(), false);
assertEq(treasury.onChainGovernanceTimelock(), queuedTime);
vm.roll(queuedTime + 1);
vm.prank(governor);
vm.prank(GOVERNOR);
treasury.toggleTimelock();
assertEq(treasury.timelockEnabled(), true);
assertEq(treasury.onChainGovernanceTimelock(), 0);
vm.prank(governor);
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);
vm.prank(GOVERNOR);
treasury.toggleTimelock();
assertEq(treasury.timelockEnabled(), false);
assertEq(treasury.onChainGovernanceTimelock(), 0);

View File

@ -1,23 +1,27 @@
pragma solidity 0.8.20;
import {Test} from "forge-std/Test.sol";
import {StdChains} from "forge-std/StdChains.sol";
import "../../src/FatsoERC20.sol";
import "../../src/GhostAuthority.sol";
import "../../src/Treasury.sol";
import "../../src/StandardBondingCalculator.sol";
import "../../src/mocks/ERC20Mock.sol";
import {Fatso} from "../../src/FatsoERC20.sol";
import {GhostAuthority} from "../../src/GhostAuthority.sol";
import {GhostTreasury} from "../../src/Treasury.sol";
import {GhostBondingCalculator} from "../../src/StandardBondingCalculator.sol";
import {ERC20Mock} from "../../src/mocks/ERC20Mock.sol";
import {ITreasury} from "../../src/interfaces/ITreasury.sol";
import {IERC20} from "@openzeppelin-contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "@openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
contract GhostTreasuryCoefficienBiggerTest is Test {
address public constant owner = 0x0000000000000000000000000000000000000001;
address public constant governor = 0x0000000000000000000000000000000000000002;
address public constant guardian = 0x0000000000000000000000000000000000000003;
address public constant other = 0x0000000000000000000000000000000000000004;
address public constant alice = 0x0000000000000000000000000000000000000005;
address public constant bob = 0x0000000000000000000000000000000000000006;
using SafeERC20 for IERC20;
uint256 public constant amount = 69 * 1e18;
address public constant OWNER = 0x0000000000000000000000000000000000000001;
address public constant GOVERNOR = 0x0000000000000000000000000000000000000002;
address public constant GUARDIAN = 0x0000000000000000000000000000000000000003;
address public constant ALICE = 0x0000000000000000000000000000000000000004;
uint256 public constant AMOUNT = 69 * 1e18;
Fatso ftso;
ERC20Mock reserve;
@ -27,12 +31,12 @@ contract GhostTreasuryCoefficienBiggerTest is Test {
GhostBondingCalculator calculator;
function setUp() public {
vm.startPrank(owner);
vm.startPrank(OWNER);
authority = new GhostAuthority(
governor,
guardian,
owner,
owner
GOVERNOR,
GUARDIAN,
OWNER,
OWNER
);
reserve = new ERC20Mock("Reserve Token", "RET");
liquidity = new ERC20Mock("Liquidity Token", "LDT");
@ -41,31 +45,31 @@ contract GhostTreasuryCoefficienBiggerTest is Test {
calculator = new GhostBondingCalculator(address(ftso), 20, 1);
vm.stopPrank();
vm.prank(governor);
vm.prank(GOVERNOR);
authority.pushVault(address(treasury));
vm.startPrank(alice);
reserve.mint(alice, amount);
vm.startPrank(ALICE);
reserve.mint(ALICE, AMOUNT);
reserve.approve(address(treasury), type(uint256).max);
ftso.approve(address(treasury), type(uint256).max);
vm.stopPrank();
}
function test_auditTreasuryReservesWithCoefficient() public {
vm.startPrank(governor);
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, alice, address(0));
vm.startPrank(GOVERNOR);
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, ALICE, address(0));
treasury.enable(ITreasury.STATUS.RESERVETOKEN, address(reserve), address(calculator));
vm.stopPrank();
assertEq(treasury.totalReserves(), 0);
uint256 tokenValue = treasury.tokenValue(address(reserve), amount);
vm.prank(alice);
IERC20(reserve).transfer(address(treasury), amount);
uint256 tokenValue = treasury.tokenValue(address(reserve), AMOUNT);
vm.prank(ALICE);
IERC20(reserve).safeTransfer(address(treasury), AMOUNT);
assertEq(treasury.totalReserves(), 0);
vm.prank(governor);
vm.prank(GOVERNOR);
treasury.auditReserves();
assertEq(treasury.totalReserves(), tokenValue);
@ -81,8 +85,8 @@ contract GhostTreasuryCoefficienBiggerTest is Test {
}
function test_originalCoefficientCorrect() public {
vm.startPrank(governor);
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, alice, address(0));
vm.startPrank(GOVERNOR);
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, ALICE, address(0));
treasury.enable(ITreasury.STATUS.RESERVETOKEN, address(reserve), address(calculator));
vm.stopPrank();

View File

@ -1,23 +1,27 @@
pragma solidity 0.8.20;
import {Test} from "forge-std/Test.sol";
import {StdChains} from "forge-std/StdChains.sol";
import "../../src/FatsoERC20.sol";
import "../../src/GhostAuthority.sol";
import "../../src/Treasury.sol";
import "../../src/StandardBondingCalculator.sol";
import "../../src/mocks/ERC20Mock.sol";
import {Fatso} from "../../src/FatsoERC20.sol";
import {GhostAuthority} from "../../src/GhostAuthority.sol";
import {GhostTreasury} from "../../src/Treasury.sol";
import {GhostBondingCalculator} from "../../src/StandardBondingCalculator.sol";
import {ERC20Mock} from "../../src/mocks/ERC20Mock.sol";
import {ITreasury} from "../../src/interfaces/ITreasury.sol";
import {IERC20} from "@openzeppelin-contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "@openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
contract GhostTreasuryCoefficientLesserTest is Test {
address public constant owner = 0x0000000000000000000000000000000000000001;
address public constant governor = 0x0000000000000000000000000000000000000002;
address public constant guardian = 0x0000000000000000000000000000000000000003;
address public constant other = 0x0000000000000000000000000000000000000004;
address public constant alice = 0x0000000000000000000000000000000000000005;
address public constant bob = 0x0000000000000000000000000000000000000006;
using SafeERC20 for IERC20;
uint256 public constant amount = 69 * 1e18;
address public constant OWNER = 0x0000000000000000000000000000000000000001;
address public constant GOVERNOR = 0x0000000000000000000000000000000000000002;
address public constant GUARDIAN = 0x0000000000000000000000000000000000000003;
address public constant ALICE = 0x0000000000000000000000000000000000000004;
uint256 public constant AMOUNT = 69 * 1e18;
Fatso ftso;
ERC20Mock reserve;
@ -27,12 +31,12 @@ contract GhostTreasuryCoefficientLesserTest is Test {
GhostBondingCalculator calculator;
function setUp() public {
vm.startPrank(owner);
vm.startPrank(OWNER);
authority = new GhostAuthority(
governor,
guardian,
owner,
owner
GOVERNOR,
GUARDIAN,
OWNER,
OWNER
);
reserve = new ERC20Mock("Reserve Token", "RET");
liquidity = new ERC20Mock("Liquidity Token", "LDT");
@ -41,11 +45,11 @@ contract GhostTreasuryCoefficientLesserTest is Test {
calculator = new GhostBondingCalculator(address(ftso), 1, 20);
vm.stopPrank();
vm.prank(governor);
vm.prank(GOVERNOR);
authority.pushVault(address(treasury));
vm.startPrank(alice);
reserve.mint(alice, amount);
vm.startPrank(ALICE);
reserve.mint(ALICE, AMOUNT);
reserve.approve(address(treasury), type(uint256).max);
ftso.approve(address(treasury), type(uint256).max);
vm.stopPrank();
@ -53,45 +57,45 @@ contract GhostTreasuryCoefficientLesserTest is Test {
function test_deposit_onlyIfApprovedTokenAndApprovedAddress() public {
vm.expectRevert();
vm.prank(alice);
treasury.deposit(address(reserve), amount, 0);
vm.prank(ALICE);
treasury.deposit(address(reserve), AMOUNT, 0);
vm.prank(governor);
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, alice, address(0));
vm.prank(GOVERNOR);
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, ALICE, address(0));
vm.expectRevert();
vm.prank(alice);
treasury.deposit(address(reserve), amount, 0);
vm.prank(ALICE);
treasury.deposit(address(reserve), AMOUNT, 0);
vm.prank(governor);
vm.prank(GOVERNOR);
treasury.enable(ITreasury.STATUS.RESERVETOKEN, address(reserve), address(calculator));
vm.prank(alice);
uint256 send = treasury.deposit(address(reserve), amount, 0);
vm.prank(ALICE);
uint256 send = treasury.deposit(address(reserve), AMOUNT, 0);
assertEq(ftso.balanceOf(alice), send);
assertEq(reserve.balanceOf(address(treasury)), amount);
assertEq(ftso.balanceOf(ALICE), send);
assertEq(reserve.balanceOf(address(treasury)), AMOUNT);
vm.startPrank(governor);
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, alice, address(0));
vm.startPrank(GOVERNOR);
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, ALICE, address(0));
treasury.enable(ITreasury.STATUS.RESERVETOKEN, address(reserve), address(calculator));
vm.stopPrank();
}
function test_auditTreasuryReservesWithCoefficient() public {
vm.startPrank(governor);
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, alice, address(0));
vm.startPrank(GOVERNOR);
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, ALICE, address(0));
treasury.enable(ITreasury.STATUS.RESERVETOKEN, address(reserve), address(calculator));
vm.stopPrank();
assertEq(treasury.totalReserves(), 0);
uint256 tokenValue = treasury.tokenValue(address(reserve), amount);
vm.prank(alice);
IERC20(reserve).transfer(address(treasury), amount);
uint256 tokenValue = treasury.tokenValue(address(reserve), AMOUNT);
vm.prank(ALICE);
IERC20(reserve).safeTransfer(address(treasury), AMOUNT);
assertEq(treasury.totalReserves(), 0);
vm.prank(governor);
vm.prank(GOVERNOR);
treasury.auditReserves();
assertEq(treasury.totalReserves(), tokenValue);
@ -107,8 +111,8 @@ contract GhostTreasuryCoefficientLesserTest is Test {
}
function test_originalCoefficientCorrect() public {
vm.startPrank(governor);
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, alice, address(0));
vm.startPrank(GOVERNOR);
treasury.enable(ITreasury.STATUS.RESERVEDEPOSITOR, ALICE, address(0));
treasury.enable(ITreasury.STATUS.RESERVETOKEN, address(reserve), address(calculator));
vm.stopPrank();

View File

@ -1,78 +1,83 @@
pragma solidity 0.8.20;
import {Test} from "forge-std/Test.sol";
import {StdChains} from "forge-std/StdChains.sol";
import "../../src/GhostAuthority.sol";
import "../../src/Treasury.sol";
import "../../src/StandardBondingCalculator.sol";
import {GhostAuthority} from "../../src/GhostAuthority.sol";
import {GhostTreasury} from "../../src/Treasury.sol";
import {GhostBondingCalculator} from "../../src/StandardBondingCalculator.sol";
import "@uniswap-v2-periphery-1.1.0-beta.0/interfaces/IWETH.sol";
import {ITreasury} from "../../src/interfaces/ITreasury.sol";
import {IERC20} from "@openzeppelin-contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
import {IWETH} from "@uniswap-v2-periphery-1.1.0-beta.0/interfaces/IWETH.sol";
contract GhostTreasuryRedemptionTest is Test {
address public constant owner = 0x0000000000000000000000000000000000000001;
address public constant governor = 0x0000000000000000000000000000000000000002;
address public constant guardian = 0x0000000000000000000000000000000000000003;
using SafeERC20 for IERC20;
uint256 public constant amount = 69 * 1e18;
address public constant dai = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
address public constant pair = 0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11;
address public constant router = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;
address public constant weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address public constant OWNER = 0x0000000000000000000000000000000000000001;
address public constant GOVERNOR = 0x0000000000000000000000000000000000000002;
address public constant GUARDIAN = 0x0000000000000000000000000000000000000003;
uint256 public constant AMOUNT = 69 * 1e18;
address public constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
address public constant PAIR = 0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11;
address public constant ROUTER = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;
address public constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
GhostTreasury treasury;
GhostAuthority authority;
GhostBondingCalculator calculator;
function setUp() public {
vm.startPrank(owner);
vm.startPrank(OWNER);
authority = new GhostAuthority(
governor,
guardian,
owner,
owner
GOVERNOR,
GUARDIAN,
OWNER,
OWNER
);
treasury = new GhostTreasury(dai, 69, address(authority));
calculator = new GhostBondingCalculator(dai, 4000, 1);
treasury = new GhostTreasury(DAI, 69, address(authority));
calculator = new GhostBondingCalculator(DAI, 4000, 1);
vm.stopPrank();
vm.startPrank(governor);
treasury.enable(ITreasury.STATUS.RESERVETOKEN, weth, address(calculator));
treasury.enable(ITreasury.STATUS.LIQUIDITYTOKEN, pair, address(calculator));
vm.startPrank(GOVERNOR);
treasury.enable(ITreasury.STATUS.RESERVETOKEN, WETH, address(calculator));
treasury.enable(ITreasury.STATUS.LIQUIDITYTOKEN, PAIR, address(calculator));
vm.stopPrank();
}
function test_mainnet_redemptionWorksCorrectly() public {
vm.deal(owner, amount);
vm.startPrank(owner);
IWETH(weth).deposit{value: amount}();
IERC20(weth).transfer(address(treasury), amount);
assertEq(IERC20(weth).balanceOf(address(treasury)), amount);
vm.deal(OWNER, AMOUNT);
vm.startPrank(OWNER);
IWETH(WETH).deposit{value: AMOUNT}();
IERC20(WETH).safeTransfer(address(treasury), AMOUNT);
assertEq(IERC20(WETH).balanceOf(address(treasury)), AMOUNT);
vm.stopPrank();
assertEq(treasury.totalReserves(), 0);
vm.prank(governor);
vm.prank(GOVERNOR);
treasury.auditReserves();
uint256 prevTotalReserves = treasury.totalReserves();
assertEq(prevTotalReserves > 0, true);
assertEq(IERC20(pair).balanceOf(address(treasury)), 0);
assertEq(IERC20(pair).balanceOf(address(treasury)), 0);
assertEq(IERC20(PAIR).balanceOf(address(treasury)), 0);
assertEq(IERC20(PAIR).balanceOf(address(treasury)), 0);
vm.prank(governor);
treasury.redeemReserve(router, amount);
vm.prank(GOVERNOR);
treasury.redeemReserve(ROUTER, AMOUNT);
assertEq(IERC20(dai).balanceOf(address(treasury)), 0);
assertEq(IERC20(weth).balanceOf(address(treasury)), 0);
assertEq(IERC20(pair).balanceOf(address(treasury)) > 0, true);
assertEq(IERC20(DAI).balanceOf(address(treasury)), 0);
assertEq(IERC20(WETH).balanceOf(address(treasury)), 0);
assertEq(IERC20(PAIR).balanceOf(address(treasury)) > 0, true);
uint256 liquidity = IERC20(pair).balanceOf(address(treasury));
vm.prank(governor);
treasury.forfeitReserves(router, liquidity, false);
uint256 liquidity = IERC20(PAIR).balanceOf(address(treasury));
vm.prank(GOVERNOR);
treasury.forfeitReserves(ROUTER, liquidity, false);
assertEq(IERC20(dai).balanceOf(address(treasury)) > 0, true);
assertEq(IERC20(weth).balanceOf(address(treasury)) > 0, true);
assertEq(IERC20(pair).balanceOf(address(treasury)), 0);
assertEq(IERC20(DAI).balanceOf(address(treasury)) > 0, true);
assertEq(IERC20(WETH).balanceOf(address(treasury)) > 0, true);
assertEq(IERC20(PAIR).balanceOf(address(treasury)), 0);
}
}