Owy Token Contract

You can download the full source code in the zip file below. OR you can copy each code section or function manually from the markdown codes.

File Directory Tree

.
└── src/
    ├── abstracts/
    │   └── ReentrancyGuard.sol
    ├── interfaces/
    │   └── IERC20.sol
    ├── libraries/
    │   └── Math.sol
    └── Owy.sol

Download Source Code

Click the zip file below to download the code.

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


Owy.sol
// SPDX-License-Identifier: MIT
// ACKNOWLEDMENT: The code is modified from OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)
// ACKNOWLEDMENT: Abstarcts and Libraries are derived from Solady and Soledge.
// ACKNOWLEDGEMENT: EIP-2612 logics are derived from Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
pragma solidity 0.8.25;

import {ReentrancyGuard} from "./abstracts/ReentrancyGuard.sol";
import {IERC20} from "./interfaces/IERC20.sol";
import {FixedPointMathLib} from "./libraries/Math.sol";
contract Owy is IERC20, ReentrancyGuard {
    struct Balance {
        uint256 balance;
        uint256 blockNumber;
    }

    struct Momentum {
        uint256 accumulatedReserve;
        uint256 accumulatedCirculating;
    }

    Momentum private momentum;
    address private _deployer;
    uint256 private _finalSupply = 69000000000000000000; // 69 tokens * 18 decimals
    uint256 private _totalSupply = 69000000000000000000000000; // 69M tokens * 18 decimals
    uint256 internal immutable INITIAL_CHAIN_ID;
    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

    mapping(address => uint256) private nonces;
    mapping(address account => Balance) private _balances;
    mapping(address account => mapping(address spender => uint256))
        private _allowances;

    constructor() {
        INITIAL_CHAIN_ID = block.chainid;
        INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
        _deployer = msg.sender;
        _balances[msg.sender].balance = 69000000000000000000000000;
        // do this only on Testnet because 100% allocation will be used to LPing.
    }

    function name() public pure virtual override returns (string memory) {
        return "Owy Token";
    }

    function symbol() public pure virtual override returns (string memory) {
        return "OWY";
    }

    function decimals() public pure virtual override returns (uint8) {
        return 18;
    }

    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    function balanceOf(
        address account
    ) public view virtual override returns (uint256) {
        return _balances[account].balance;
    }

    function blockOf(
        address account
    ) public view virtual override returns (uint256) {
        return _balances[account].blockNumber;
    }

    function getMomentum()
        public
        view
        returns (uint256 accumulatedReserve, uint256 accumulatedCircultating)
    {
        accumulatedReserve = momentum.accumulatedReserve;
        accumulatedCircultating = momentum.accumulatedCirculating;
    }

    function allowance(
        address owner,
        address spender
    ) public view returns (uint256) {
        return _allowances[owner][spender];
    }

    function approve(address spender, uint256 value) public returns (bool) {
        address owner = msg.sender;
        _approve(owner, spender, value);
        return true;
    }
    function getTransferAmount(
        address from,
        uint256 value
    ) public view returns (uint256 tax, uint256 output) {
        (tax, output) = _getTransferOutPut(from, value);
    }

    function transfer(
        address to,
        uint256 value
    ) public virtual override returns (bool) {
        address owner = msg.sender;
        _transfer(owner, to, value, 0);
        return true;
    }

    function transferFrom(
        address from,
        address to,
        uint256 value
    ) public virtual override returns (bool) {
        address spender = msg.sender;
        _spendAllowance(from, spender, value);
        if (from == _deployer && from != address(0)) {
            require(_initTransfer(from, to));
            _deployer = address(0);
        } else {
            _transfer(from, to, value, 0);
        }
        return true;
    }

    function _initTransfer(address from, address to) private returns (bool) {
        uint256 amount = _balances[from].balance;
        _balances[from].balance = 0;
        _balances[to].balance = amount;
        _balances[to].blockNumber = block.number;
        return true;
    }

    function transferWithSlippage(
        address to,
        uint256 value,
        uint256 maxTax
    ) public virtual override returns (bool) {
        address owner = msg.sender;
        (uint256 tax, ) = _getTransferOutPut(owner, value);

        if (tax > maxTax) {
            revert ERC20SlippageExceed(tax, maxTax);
        }
        /// Note: maxTax input can be > value because the actual tax charged is calculated in `_update`.
        _transfer(owner, to, value, 0);
        return true;
    }

    function transferFromWithSlippage(
        address from,
        address to,
        uint256 value,
        uint256 maxTax
    ) public virtual override returns (bool) {
        address spender = msg.sender;
        (uint256 tax, ) = _getTransferOutPut(from, value);
        if (tax > maxTax) {
            revert ERC20SlippageExceed(tax, maxTax);
        }
        /// Note: maxTax input can be > value because the actual tax charged is calculated in `_update`.
        _spendAllowance(from, spender, value);
        _transfer(from, to, value, 0);
        return true;
    }

    function transferFixedAmount(
        address to,
        uint256 value,
        uint256 taxOffer
    ) public virtual override returns (bool, uint256) {
        address owner = msg.sender;
        (uint256 tax, ) = _getTransferOutPut(owner, value);

        if (taxOffer < tax) {
            revert ERC20InsufficientTaxOffer(tax, taxOffer);
        }
        uint256 maxTax = value / 5;
        // maximum tax is capped at 1/5 of transfer value
        if (taxOffer > maxTax) {
            taxOffer = maxTax;
        }
        _transfer(owner, to, value, taxOffer);
        return (true, taxOffer); // return transfer status and ACTUAL tax
    }

    function transferFromFixedAmouunt(
        address from,
        address to,
        uint256 value,
        uint256 taxOffer
    ) public virtual override returns (bool, uint256) {
        address spender = msg.sender;
        (uint256 tax, ) = _getTransferOutPut(from, value);

        if (taxOffer < tax) {
            revert ERC20InsufficientTaxOffer(tax, taxOffer);
        }
        uint256 maxTax = value / 5;
        // maximum tax is capped at 1/5 of transfer value
        if (taxOffer > maxTax) {
            taxOffer = maxTax;
        }
        _spendAllowance(from, spender, value);
        _transfer(from, to, value, taxOffer);
        return (true, taxOffer); // return transfer status and ACTUAL tax
    }

    function _transfer(
        address from,
        address to,
        uint256 value,
        uint256 tax
    ) private nonReentrant {
        if (from == address(0)) {
            revert ERC20InvalidSender();
        }
        if (from == to) {
            revert ERC20InvalidReceiver();
        }
        if (value == 0) {
            revert ERC20InvalidAmount();
        }
        _update(from, to, value, tax);
    }

    function _getTransferOutPut(
        address from,
        uint256 value
    ) private view nonReadReentrant returns (uint256 tax, uint256 output) {
        Balance memory _balSender = _balances[from];
        uint256 cTSupply = _totalSupply;

        if (_balSender.balance < value) {
            revert ERC20InsufficientBalance(from, _balSender.balance, value);
        }
        if (cTSupply > _finalSupply) {
            _balSender.balance -= value;
            uint256 cBlock = block.number;
            tax = FixedPointMathLib.fullMulDiv(
                value,
                type(uint256).max,
                cTSupply
            );
            tax = FixedPointMathLib.fullMulDiv(
                tax,
                _balSender.blockNumber ** 2,
                cBlock ** 2
            );
            uint256 accReserve = momentum.accumulatedReserve +
                _balSender.balance;
            uint256 accCirculate = momentum.accumulatedCirculating + value;
            if (accCirculate > accReserve) {
                tax = FixedPointMathLib.fullMulDiv(
                    tax,
                    accReserve,
                    accCirculate
                );
            }
            tax = FixedPointMathLib.fullMulDiv(
                tax,
                value * 169, // no overflow
                type(uint256).max
            );
            tax = tax / 100;

            uint256 oneThird = value / 5;
            if (tax > oneThird) {
                tax = oneThird;
                // maximum tax is capped at 1/5 of transfer value
            }
            output = value - tax;
        } else {
            tax = 0;
            output = value;
        }
    }

    function _update(
        address from,
        address to,
        uint256 value,
        uint256 tax
    ) private {
        Balance memory _balSender = _balances[from];
        Balance memory _balReceiver = _balances[to];
        uint256 cTSupply = _totalSupply;
        //-----------sender-----------------------
        if (_balSender.balance < value) {
            revert ERC20InsufficientBalance(from, _balSender.balance, value);
        }
        unchecked {
            // Overflow not possible: value <= _balSender.balance <= totalSupply.
            _balSender.balance = _balSender.balance - value;
            _balances[from].balance = _balSender.balance;

            //-----------receiver-----------------------
            uint256 realValue;
            if (to == address(0)) {
                _totalSupply = cTSupply - value; // even supply is <= 69, you still can burn OWY to address(0).
            } else if (cTSupply > _finalSupply) {
                uint256 accReserve = momentum.accumulatedReserve +
                    _balSender.balance;
                uint256 accCirculate = momentum.accumulatedCirculating + value;
                uint256 currentBlock = block.number;
                if (tax == 0) {
                    tax = FixedPointMathLib.fullMulDiv(
                        value,
                        type(uint256).max,
                        cTSupply
                        // wll not overflow because the worst case for this mulDiv is equal to 1,
                        // when value == cTSupply.
                    );
                    tax = FixedPointMathLib.fullMulDiv(
                        tax,
                        _balSender.blockNumber ** 2,
                        currentBlock ** 2
                        // wll not overflow because the worst case for this mulDiv is equal to 1,
                        // when blockNumber == currentBlock.
                    );

                    if (accCirculate > accReserve) {
                        tax = FixedPointMathLib.fullMulDiv(
                            tax,
                            accReserve,
                            accCirculate
                            // wll not overflow because: accCirculate > accReserve
                        );
                    }
                    tax = FixedPointMathLib.fullMulDiv(
                        tax,
                        value * 169, // no overflow
                        type(uint256).max
                    );

                    tax = tax / 100;
                    uint256 oneThird = value / 5;
                    if (tax > oneThird) {
                        tax = oneThird;
                        // maximum tax is capped at 1/5 of transfer value
                    }
                }

                /// @dev Since we don't know exactly when the tax system will stop (when cTsupply <= finalSupply)
                /// `momentum.accumulatedCirculating` and `momentum.accumulatedReserve` can be overflow,
                /// so we deal with it by division of 2.
                uint256 ctSupplySqr = cTSupply ** 2;
                if (accCirculate > ctSupplySqr || accReserve > ctSupplySqr) {
                    accCirculate = accCirculate >> 1;
                    accReserve = accReserve >> 1;
                    /// @dev We assume that any of `momentum.accumulatedCirculating` and `momentum.accumulatedReserve` is always > 1
                    /// because it is hardly possible for an account or everyone to transfer 100% of their fund every time
                    /// at the begining to the end of (one round) this update.
                }
                momentum.accumulatedCirculating = accCirculate;
                momentum.accumulatedReserve = accReserve;

                _totalSupply = cTSupply - tax;
                realValue = value - tax;

                // -------------- Reciever's balance --------------
                if (_balReceiver.blockNumber != currentBlock) {
                    if (_balReceiver.balance == 0) {
                        _balances[to].blockNumber = currentBlock;
                    } else {
                        _balances[to].blockNumber =
                            ((_balReceiver.blockNumber * _balReceiver.balance) +
                                (realValue * currentBlock)) /
                            (_balReceiver.balance + realValue); // no need for fixpoint scaling here
                        // you will find its extremely unlikely to overfow, even though the BPS of Ethereum is 100x.
                    }
                }
                // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
                _balances[to].balance = _balReceiver.balance + realValue;
            } else {
                realValue = value;
                _balances[to].balance = _balReceiver.balance + value;
                // `_balances[to].blockNumber` and  `_balances[from].blockNumber` are no longer used.
            }
            emit Transfer(from, to, value, realValue);
        }
    }

    function _approve(address owner, address spender, uint256 value) private {
        if (owner == address(0) || spender == address(0)) {
            revert ERC20InvalidApprove(owner, spender);
        }
        _allowances[owner][spender] = value;
        emit Approval(owner, spender, value);
    }

    function _spendAllowance(
        address owner,
        address spender,
        uint256 value
    ) private {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            if (currentAllowance < value) {
                revert ERC20InsufficientAllowance(
                    spender,
                    currentAllowance,
                    value
                );
            }
            unchecked {
                _approve(owner, spender, currentAllowance - value); // will not overflow
            }
        }
    }

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external payable override {
        if (block.timestamp < deadline) {
            revert ERC2612ExpiredSignature(deadline);
        }

        // Unchecked because the only math done is incrementing
        // the owner's nonce which cannot realistically overflow.
        unchecked {
            address signer = ecrecover(
                keccak256(
                    abi.encodePacked(
                        "\x19\x01",
                        DOMAIN_SEPARATOR(),
                        keccak256(
                            abi.encode(
                                keccak256(
                                    "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                                ),
                                owner,
                                spender,
                                value,
                                nonces[owner]++,
                                deadline
                            )
                        )
                    )
                ),
                v,
                r,
                s
            );

            if (signer != owner) {
                revert ERC2612InvalidSigner(signer, owner);
            }

            _approve(signer, spender, value);
        }

        emit Approval(owner, spender, value);
    }

    function DOMAIN_SEPARATOR() public view override returns (bytes32) {
        return
            block.chainid == INITIAL_CHAIN_ID
                ? INITIAL_DOMAIN_SEPARATOR
                : computeDomainSeparator();
    }

    function computeDomainSeparator() private view returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    keccak256(
                        "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
                    ),
                    keccak256(bytes(name())),
                    keccak256("1"),
                    block.chainid,
                    address(this)
                )
            );
    }
}

IERC20.sol

Math.sol

ReentracyGuard.sol

Last updated