// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "@openzeppelin-contracts/utils/Address.sol"; import "@openzeppelin-contracts/token/ERC20/extensions/ERC20Permit.sol"; import "./interfaces/IGHST.sol"; import "./interfaces/ISTNK.sol"; import "./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; address internal _initializer; uint256 private _sharesPerUnit; uint256 private _totalSupply; address public staking; address public ghst; address public treasury; mapping(address => uint256) public override debtBalances; mapping(address => uint256) private _shares; mapping(address => mapping(address => uint256)) private _allowedValue; constructor(uint256 usedIndex) ERC20("Stinky", "STNK") ERC20Permit("Stinky") { _initializer = msg.sender; _internalIndex = usedIndex; _totalSupply = INITIAL_SHARES_SUPPLY; _sharesPerUnit = TOTAL_SHARES / INITIAL_SHARES_SUPPLY; } function initialize( address _staking, address _treasury, address _ghst ) external { if (msg.sender != _initializer) revert NotInitializer(); staking = _staking; treasury = _treasury; ghst = _ghst; _shares[_staking] = TOTAL_SHARES; _initializer = address(0); emit Transfer(address(0), _staking, _totalSupply); emit LogStakingContractUpdated(_staking); } function rebase( uint256 supplyDelta, uint256 epoch ) public override returns (uint256 newTotalSupply) { if (msg.sender != staking) revert NotStakingContract(); uint256 currentCirculatingSupply = circulatingSupply(); uint256 rebasePercent = 0; if (supplyDelta == 0) { newTotalSupply = _totalSupply; } else { if (currentCirculatingSupply > 0) { supplyDelta = supplyDelta * _totalSupply / currentCirculatingSupply; rebasePercent = supplyDelta * 1e18 / currentCirculatingSupply; } newTotalSupply = _totalSupply + supplyDelta; } if (newTotalSupply > MAX_SUPPLY) { newTotalSupply = MAX_SUPPLY; } _totalSupply = newTotalSupply; _sharesPerUnit = TOTAL_SHARES / newTotalSupply; emit LogSupply(epoch, newTotalSupply); emit LogRebase(epoch, rebasePercent, index()); } function transfer( address to, uint256 value ) public override(IERC20, ERC20) returns (bool) { _transferInner(msg.sender, to, value); return true; } function transferFrom( address from, address to, uint256 value ) public override(IERC20, ERC20) returns (bool) { _spendAllowance(from, msg.sender, value); _transferInner(from, to, value); return true; } function approve( address spender, uint256 value ) public override(IERC20, ERC20) returns (bool) { _approve(msg.sender, spender, value, true); return true; } function increaseAllowance( address spender, uint256 imbalance ) public override returns (bool) { imbalance = imbalance + _allowedValue[msg.sender][spender]; _approve(msg.sender, spender, imbalance, true); return true; } function decreaseAllowance( address spender, uint256 imbalance ) public override returns (bool) { uint256 prevAllowance = _allowedValue[msg.sender][spender]; imbalance = imbalance < prevAllowance ? prevAllowance - imbalance : 0; _approve(msg.sender, spender, imbalance, true); return true; } function decimals() public pure override returns (uint8) { return 9; } function totalSupply() public view override(IERC20, ERC20) returns (uint256) { return _totalSupply; } function balanceOf( address who ) public view override(IERC20, ERC20) returns (uint256) { return _shares[who] / _sharesPerUnit; } function allowance( address owner, address spender ) public view override(IERC20, ERC20) returns (uint256) { return _allowedValue[owner][spender]; } function sharesForBalance(uint256 amount) public view override returns (uint256) { return amount * _sharesPerUnit; } function balanceForShares(uint256 shares) public view override returns (uint256) { return shares / _sharesPerUnit; } function toGhst(uint256 amount) external view override returns (uint256) { return IGHST(ghst).balanceTo(amount); } function fromGhst(uint256 amount) external view override returns (uint256) { return IGHST(ghst).balanceFrom(amount); } function circulatingSupply() public view override returns (uint256) { return _totalSupply + IGHST(ghst).balanceFrom(IERC20(ghst).totalSupply()) + IStaking(staking).supplyInWarmup() - balanceOf(staking); } function index() public view override returns (uint256) { return balanceForShares(_internalIndex); } function changeDebt( uint256 amount, address debtor, bool add ) external override { if (msg.sender != treasury) revert NotTreasury(); uint256 debtBalance = debtBalances[debtor]; debtBalance = add ? debtBalance + amount : debtBalance - amount; if (debtBalance > balanceOf(debtor)) revert InsufficientBalance(); debtBalances[debtor] = debtBalance; } function _transferInner( address from, address to, uint256 value ) internal { uint256 sharesValue = value * _sharesPerUnit; _shares[from] = _shares[from] - sharesValue; _shares[to] = _shares[to] + sharesValue; if (balanceOf(from) < debtBalances[from]) revert DebtExists(); emit Transfer(from, to, value); } function _spendAllowance(address owner, address spender, uint256 value) internal override { uint256 currentAllowance = allowance(owner, spender); if (currentAllowance != type(uint256).max) { if (currentAllowance < value) { revert ERC20InsufficientAllowance(spender, currentAllowance, value); } unchecked { _approve(owner, spender, currentAllowance - value, false); } } } function _approve( address owner, address spender, uint256 value, bool emitEvent ) internal override { if (owner == address(0)) revert ERC20InvalidApprover(address(0)); if (spender == address(0)) revert ERC20InvalidSpender(address(0)); _allowedValue[owner][spender] = value; if (emitEvent) emit Approval(owner, spender, value); } }