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);
}
}
Submitted on: 2025-10-19 13:31:21
Comments
Log in to comment.
No comments yet.