get rid of annoying foundry warnings
Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
This commit is contained in:
parent
0d4c945aa1
commit
8ac8b67767
@ -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)
|
||||
})
|
||||
);
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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));
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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";
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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)) });
|
||||
}
|
||||
}
|
||||
|
||||
@ -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) {}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user