Tether USD (USDT)

Description:

ERC20 token contract with Burnable capabilities. Standard implementation for fungible tokens on Ethereum.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

/**
 * Tether USD (USDT) — ERC20 with EIP-2612 Permit (single-file, no imports)
 * - Fixed supply: 1,000,000,000 * 10^18 (minted to `receiver`)
 * - 18 decimals, burnable, no owner/admin
 * - Canonical EIP-2612 typehash and low-s / v checks
 */
contract TetherUSD {
    // --- ERC20 metadata ---
    string public constant name = "Tether USD";
    string public constant symbol = "USDT";
    uint8  public constant decimals = 18;

    // --- Supply ---
    uint256 public constant TOTAL_SUPPLY = 1_000_000_000 * (10 ** 18);

    // --- Storage ---
    mapping(address => uint256) private _balanceOf;
    mapping(address => mapping(address => uint256)) private _allowance;

    // --- Events ---
    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);

    // ======== EIP-2612 / EIP-712 ========
    // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")
    bytes32 private constant PERMIT_TYPEHASH =
        0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;

    // keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")
    bytes32 private constant EIP712DOMAIN_TYPEHASH =
        0x8b73e5c3af0a9d6c2c7a5a2e0a6f2d8f9b9b0a9d1a9e6c4a0b2c2c3a0f7f3e9f;

    // secp256k1n/2 (enforce low-s)
    uint256 private constant SECP256K1N_HALF =
        0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0;

    // EIP-2612 nonces
    mapping(address => uint256) public nonces;

    // Cached domain separator + chain id
    bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;
    uint256 private immutable _CACHED_CHAIN_ID;

    constructor(address receiver) {
        require(receiver != address(0), "receiver zero");

        _balanceOf[receiver] = TOTAL_SUPPLY;
        emit Transfer(address(0), receiver, TOTAL_SUPPLY);

        _CACHED_CHAIN_ID = block.chainid;
        _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator();
    }

    // ---- Domain separator (recomputes if chainId changed) ----
    function DOMAIN_SEPARATOR() public view returns (bytes32) {
        return (block.chainid == _CACHED_CHAIN_ID)
            ? _CACHED_DOMAIN_SEPARATOR
            : _buildDomainSeparator();
    }

    function _buildDomainSeparator() private view returns (bytes32) {
        return keccak256(
            abi.encode(
                EIP712DOMAIN_TYPEHASH,
                keccak256(bytes(name)),
                keccak256(bytes("1")),
                block.chainid,
                address(this)
            )
        );
    }

    // ======== ERC20 views ========
    function totalSupply() external pure returns (uint256) { return TOTAL_SUPPLY; }
    function balanceOf(address a) external view returns (uint256) { return _balanceOf[a]; }
    function allowance(address o, address s) external view returns (uint256) { return _allowance[o][s]; }

    // ======== ERC20 logic ========
    function transfer(address to, uint256 amount) external returns (bool) {
        _transfer(msg.sender, to, amount);
        return true;
    }

    function approve(address spender, uint256 amount) external returns (bool) {
        _approve(msg.sender, spender, amount);
        return true;
    }

    function transferFrom(address from, address to, uint256 amount) external returns (bool) {
        uint256 allowed = _allowance[from][msg.sender];
        require(allowed >= amount, "insufficient allowance");
        unchecked { _approve(from, msg.sender, allowed - amount); }
        _transfer(from, to, amount);
        return true;
    }

    // ======== Burn ========
    function burn(uint256 amount) external {
        _burn(msg.sender, amount);
    }

    function burnFrom(address from, uint256 amount) external {
        uint256 allowed = _allowance[from][msg.sender];
        require(allowed >= amount, "insufficient allowance");
        unchecked { _approve(from, msg.sender, allowed - amount); }
        _burn(from, amount);
    }

    // ======== EIP-2612 permit ========
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external {
        require(block.timestamp <= deadline, "permit expired");
        require(owner != address(0) && spender != address(0), "zero addr");

        uint256 nonce = nonces[owner];

        bytes32 structHash = keccak256(
            abi.encode(
                PERMIT_TYPEHASH,
                owner,
                spender,
                value,
                nonce,
                deadline
            )
        );

        bytes32 digest = keccak256(
            abi.encodePacked(
                "\x19\x01",
                DOMAIN_SEPARATOR(),
                structHash
            )
        );

        require(uint256(s) <= SECP256K1N_HALF, "bad s");
        require(v == 27 || v == 28, "bad v");

        address recovered = ecrecover(digest, v, r, s);
        require(recovered == owner && recovered != address(0), "bad sig");

        // increment nonce only after successful verification
        unchecked { nonces[owner] = nonce + 1; }

        _approve(owner, spender, value);
    }

    // ======== Internals ========
    function _transfer(address from, address to, uint256 amount) internal {
        require(from != address(0) && to != address(0), "zero addr");
        uint256 bal = _balanceOf[from];
        require(bal >= amount, "insufficient balance");
        unchecked {
            _balanceOf[from] = bal - amount;
            _balanceOf[to] += amount;
        }
        emit Transfer(from, to, amount);
    }

    function _approve(address owner, address spender, uint256 amount) internal {
        require(owner != address(0) && spender != address(0), "zero addr");
        _allowance[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    function _burn(address from, uint256 amount) internal {
        require(from != address(0), "zero addr");
        uint256 bal = _balanceOf[from];
        require(bal >= amount, "insufficient balance");
        unchecked { _balanceOf[from] = bal - amount; }
        emit Transfer(from, address(0), amount);
    }
}

Tags:
ERC20, Token, Burnable|addr:0x9991f906cdacf49ccb006894f08d160c9cf325f0|verified:true|block:23611045|tx:0x84c65abc4cbbd724bfa4e149ea548dc71a7fcc90598892fc604943fe12bfad14|first_check:1760873479

Submitted on: 2025-10-19 13:31:21

Comments

Log in to comment.

No comments yet.