ghost-dao-contracts/src/StinkyERC20.sol
Uncle Fatso 46b33b4c75
initial push for smart-contracts
Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
2025-04-28 14:17:04 +03:00

225 lines
7.0 KiB
Solidity

// 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);
}
}