Description:
ERC20 token contract. 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.30;
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
interface IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 value) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
interface IERC20Metadata is IERC20 {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
}
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
interface IERC20Errors {
error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
error ERC20InvalidSender(address sender);
error ERC20InvalidReceiver(address receiver);
error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
error ERC20InvalidApprover(address approver);
error ERC20InvalidSpender(address spender);
}
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
function name() public view virtual returns (string memory) {
return _name;
}
function symbol() public view virtual returns (string memory) {
return _symbol;
}
function decimals() public view virtual returns (uint8) {
return 18;
}
function totalSupply() public view virtual returns (uint256) {
return _totalSupply;
}
function balanceOf(address account) public view virtual returns (uint256) {
return _balances[account];
}
function transfer(address to, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_transfer(owner, to, value);
return true;
}
function allowance(address owner, address spender) public view virtual returns (uint256) {
return _allowances[owner][spender];
}
function approve(address spender, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, value);
return true;
}
function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, value);
_transfer(from, to, value);
return true;
}
function _transfer(address from, address to, uint256 value) internal {
if (from == address(0)) {
revert ERC20InvalidSender(address(0));
}
if (to == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(from, to, value);
}
function _update(address from, address to, uint256 value) internal virtual {
if (from == address(0)) {
_totalSupply += value;
} else {
uint256 fromBalance = _balances[from];
if (fromBalance < value) {
revert ERC20InsufficientBalance(from, fromBalance, value);
}
unchecked {
_balances[from] = fromBalance - value;
}
}
if (to == address(0)) {
unchecked {
_totalSupply -= value;
}
} else {
unchecked {
_balances[to] += value;
}
}
emit Transfer(from, to, value);
}
function _mint(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(address(0), account, value);
}
function _approve(address owner, address spender, uint256 value) internal {
_approve(owner, spender, value, true);
}
function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
if (owner == address(0)) {
revert ERC20InvalidApprover(address(0));
}
if (spender == address(0)) {
revert ERC20InvalidSpender(address(0));
}
_allowances[owner][spender] = value;
if (emitEvent) {
emit Approval(owner, spender, value);
}
}
function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
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);
}
}
}
}
abstract contract Ownable is Context {
address private _owner;
error OwnableUnauthorizedAccount(address account);
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
return _owner;
}
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
interface IUniswapV2Factory {
function createPair(address tokenA, address tokenB) external returns (address pair);
}
interface IUniswapV2Router {
function factory() external pure returns (address);
function WETH() external pure returns (address);
function swapExactTokensForETHSupportingFeeOnTransferTokens(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external;
}
contract Opal is Ownable, ERC20 {
IUniswapV2Router public immutable swapRouter;
address public constant ZERO_ADDRESS = address(0);
address public immutable swapPair;
address public taxWallet;
bool public feesEnabled;
bool private inSwapProcess;
bool public isActivated;
uint256 public activationBlock;
uint256 public activationTime;
uint256 private lastSwapBlock;
// Basis points constants (1 basis point = 0.01%)
uint256 public constant BASIS_POINTS = 10000; // 100% = 10000 basis points
uint256 public constant MAX_TOTAL_FEE_BPS = 2500; // 25%
uint256 public tokensForSwap;
uint256 public buyTaxBps; // Buy tax in basis points
uint256 public sellTaxBps; // Sell tax in basis points
mapping(address => bool) public excludedFromFees;
mapping(address => bool) public marketPairs;
event Activation();
event TaxWalletUpdated(address newWallet, address oldWallet);
event FeesStatusChanged(bool status);
event TokensForSwapUpdated(uint256 newValue, uint256 oldValue);
event BuyTaxUpdated(uint256 newValueBps, uint256 oldValueBps);
event SellTaxUpdated(uint256 newValueBps, uint256 oldValueBps);
event ExcludedFromFees(address account, bool isExcluded);
event MarketPairStatusUpdated(address pair, bool value);
error AlreadyActivated();
error InvalidAddress();
error AmountTooSmall();
error AmountTooLarge();
error FeeTooHigh();
error TaxCanOnlyDecrease();
error PairAlreadySet();
error NotActivated();
modifier lockSwapProcess() {
inSwapProcess = true;
_;
inSwapProcess = false;
}
constructor() Ownable(msg.sender) ERC20("Opal", "OPAL") {
address owner = msg.sender;
_mint(owner, 1_000_000_000 ether);
// Set wallet addresses
taxWallet = 0x1e041546A65537Cb6467A8E509172E3853E70b8A;
tokensForSwap = 50000 ether; // 50k tokens
feesEnabled = true;
buyTaxBps = 499; // 499 basis points = 4.99%
sellTaxBps = 1000; // 1000 basis points = 10%
swapRouter = IUniswapV2Router(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
swapPair = IUniswapV2Factory(swapRouter.factory()).createPair(
address(this),
swapRouter.WETH()
);
SetMarketPair(swapPair, true);
_approve(address(this), address(swapRouter), type(uint256).max);
_excludeFromFees(address(this), true);
_excludeFromFees(owner, true);
_excludeFromFees(taxWallet, true);
}
receive() external payable {}
function _transferOwnership(address newOwner) internal override {
address oldOwner = owner();
if (oldOwner != ZERO_ADDRESS) {
_excludeFromFees(oldOwner, false);
}
_excludeFromFees(newOwner, true);
super._transferOwnership(newOwner);
}
function enableTrading() external onlyOwner {
require(!isActivated, AlreadyActivated());
isActivated = true;
activationBlock = block.number;
activationTime = block.timestamp;
emit Activation();
}
function updateTaxWallet(address _taxWallet) external onlyOwner {
require(_taxWallet != ZERO_ADDRESS, InvalidAddress());
address oldWallet = taxWallet;
taxWallet = _taxWallet;
emit TaxWalletUpdated(taxWallet, oldWallet);
}
function setFeesEnabled(bool value) external onlyOwner {
feesEnabled = value;
emit FeesStatusChanged(value);
}
function setTokensForSwap(uint256 amount) external onlyOwner {
uint256 totalSupplyTokens = totalSupply();
require(amount >= (totalSupplyTokens * 1) / 1000000, AmountTooSmall());
require(amount <= (totalSupplyTokens * 5) / 1000, AmountTooLarge());
uint256 oldValue = tokensForSwap;
tokensForSwap = amount;
emit TokensForSwapUpdated(amount, oldValue);
}
function setTaxBps(uint256 _buyTaxBps, uint256 _sellTaxBps) external onlyOwner {
require(_buyTaxBps <= buyTaxBps, TaxCanOnlyDecrease());
require(_sellTaxBps <= sellTaxBps, TaxCanOnlyDecrease());
require(_buyTaxBps <= MAX_TOTAL_FEE_BPS, FeeTooHigh());
require(_sellTaxBps <= MAX_TOTAL_FEE_BPS, FeeTooHigh());
uint256 oldBuyTax = buyTaxBps;
uint256 oldSellTax = sellTaxBps;
buyTaxBps = _buyTaxBps;
sellTaxBps = _sellTaxBps;
emit BuyTaxUpdated(_buyTaxBps, oldBuyTax);
emit SellTaxUpdated(_sellTaxBps, oldSellTax);
}
function getSellTaxPercent() external view returns (uint256) {
return sellTaxBps;
}
function excludeFromFees(address[] calldata accounts, bool value) external onlyOwner {
for (uint256 i = 0; i < accounts.length; i++) {
_excludeFromFees(accounts[i], value);
}
}
function setMarketPair(address pair, bool value) external onlyOwner {
require(!marketPairs[pair], PairAlreadySet());
SetMarketPair(pair, value);
}
function _update(address from, address to, uint256 amount) internal virtual override {
require(
isActivated || excludedFromFees[from] || excludedFromFees[to],
NotActivated()
);
bool applyFee = feesEnabled &&
!inSwapProcess &&
!(excludedFromFees[from] || excludedFromFees[to]);
if (applyFee) {
uint256 feeAmount = 0;
if (marketPairs[to] && sellTaxBps > 0) {
// Sell tax calculation using basis points
feeAmount = (amount * sellTaxBps) / BASIS_POINTS;
} else if (marketPairs[from] && buyTaxBps > 0) {
// Buy tax calculation using basis points
feeAmount = (amount * buyTaxBps) / BASIS_POINTS;
}
if (feeAmount > 0) {
amount -= feeAmount;
super._update(from, address(this), feeAmount);
}
}
uint256 contractTokenBalance = balanceOf(address(this));
bool canSwap = contractTokenBalance >= tokensForSwap;
if (applyFee && !marketPairs[from] && canSwap) {
if (block.number > lastSwapBlock && block.number > activationBlock) {
_swapTokens(contractTokenBalance);
lastSwapBlock = block.number;
}
}
super._update(from, to, amount);
}
function _swapTokens(uint256 tokenAmount) internal virtual lockSwapProcess {
address[] memory path = new address[](2);
path[0] = address(this);
path[1] = swapRouter.WETH();
uint256 maxSwapAmount = tokensForSwap * 20;
if (tokenAmount > maxSwapAmount) {
tokenAmount = maxSwapAmount;
}
swapRouter.swapExactTokensForETHSupportingFeeOnTransferTokens(
tokenAmount,
0,
path,
address(this),
block.timestamp
);
uint256 ethBalance = address(this).balance;
if (ethBalance > 0) {
(bool success, ) = address(taxWallet).call{value: ethBalance}("");
require(success, "ETH transfer to tax wallet failed");
}
}
function _excludeFromFees(address account, bool value) internal virtual {
excludedFromFees[account] = value;
emit ExcludedFromFees(account, value);
}
function SetMarketPair(address pair, bool value) internal virtual {
marketPairs[pair] = value;
emit MarketPairStatusUpdated(pair, value);
}
function manualSwap() external onlyOwner {
uint256 contractBalance = balanceOf(address(this));
require(contractBalance > 0, "No tokens to swap");
_swapTokens(contractBalance);
}
function rescueETH() external onlyOwner {
uint256 balance = address(this).balance;
require(balance > 0, "No ETH to rescue");
(bool success, ) = payable(msg.sender).call{value: balance}("");
require(success, "ETH transfer failed");
}
function rescueTokens(address token) external onlyOwner {
require(token != address(this), "Cannot rescue own tokens");
uint256 balance = IERC20(token).balanceOf(address(this));
require(balance > 0, "No tokens to rescue");
IERC20(token).transfer(msg.sender, balance);
}
}
Submitted on: 2025-10-01 12:44:25
Comments
Log in to comment.
No comments yet.