Description:
Decentralized Finance (DeFi) protocol contract providing Swap, Liquidity functionality.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
// █████ ███████ ███████████ █████████
// ░░███ ███░░░░░███ ░█░░░███░░░█ ███░░░░░███
// ███████ ██████ ███ ░░███░ ░███ ░ ███ ░░░
// ███░░███ ███░░███░███ ░███ ░███ ░███
//░███ ░███ ░███████ ░███ ░███ ░███ ░███
//░███ ░███ ░███░░░ ░░███ ███ ░███ ░░███ ███
//░░████████░░██████ ░░░███████░ █████ ░░█████████
// ░░░░░░░░ ░░░░░░ ░░░░░░░ ░░░░░ ░░░░░░░░░
// ███ ██████████ ███████ ███████████ █████████
// ██████ ░░███░░░░███ ███░░░░░███ ░█░░░███░░░█ ███░░░░░███
// ███░░░ ░███ ░░███ ███ ░░███░ ░███ ░ ███ ░░░
// ░░█████ ░███ ░███░███ ░███ ░███ ░███
// ░░░░███ ░███ ░███░███ ░███ ░███ ░███
// ██████ ░███ ███ ░░███ ███ ░███ ░░███ ███
//░░░███ ██████████ ░░░███████░ █████ ░░█████████
// ░░░ ░░░░░░░░░░ ░░░░░░░ ░░░░░ ░░░░░░░░░
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
/* ---------- Minimal V2 Interfaces ---------- */
interface IUniswapV2Pair {
function token0() external view returns (address);
function token1() external view returns (address);
function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32);
}
interface IRouter {
function WETH() external pure returns (address);
function factory() external pure returns (address);
function swapExactTokensForETHSupportingFeeOnTransferTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external payable;
function swapExactETHForTokensSupportingFeeOnTransferTokens(
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external payable;
function addLiquidityETH(
address token,
uint amountTokenDesired,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline
) external payable returns (uint amountToken, uint amountETH, uint liquidity);
}
interface IFactory {
function createPair(address tokenA, address tokenB) external returns (address pair);
function getPair(address tokenA, address tokenB) external view returns (address pair);
function treasury() external view returns (address treasury);
}
/* ---------- OZ IERC20 / ERC20 / Ownable ---------- */
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 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 amount) 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; }
}
contract ERC20 is Context, IERC20, IERC20Metadata {
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 override returns (string memory) { return _name; }
function symbol() public view virtual override returns (string memory) { return _symbol; }
function decimals() public view 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]; }
function transfer(address to, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender(); _transfer(owner, to, amount); return true;
}
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
function approve(address spender, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender(); _approve(owner, spender, amount); return true;
}
function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
address spender = _msgSender(); _spendAllowance(from, spender, amount); _transfer(from, to, amount); return true;
}
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
address owner = _msgSender(); _approve(owner, spender, allowance(owner, spender) + addedValue); return true;
}
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
address owner = _msgSender();
uint256 currentAllowance = allowance(owner, spender);
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked { _approve(owner, spender, currentAllowance - subtractedValue); }
return true;
}
function _transfer(address from, address to, uint256 amount) internal virtual {
require(from != address(0), "ERC20: transfer from zero"); require(to != address(0), "ERC20: transfer to zero");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from]; require(fromBalance >= amount, "ERC20: amount exceeds balance");
unchecked { _balances[from] = fromBalance - amount; }
_balances[to] += amount; emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, amount);
}
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to zero");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount; _balances[account] += amount; emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from zero");
_beforeTokenTransfer(account, address(0), amount);
uint256 bal = _balances[account]; require(bal >= amount, "ERC20: burn exceeds balance");
unchecked { _balances[account] = bal - amount; }
_totalSupply -= amount; emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
function _approve(address owner, address spender, uint256 amount) internal virtual {
require(owner != address(0), "ERC20: approve from zero"); require(spender != address(0), "ERC20: approve to zero");
_allowances[owner][spender] = amount; emit Approval(owner, spender, amount);
}
function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked { _approve(owner, spender, currentAllowance - amount); }
}
}
function _beforeTokenTransfer(address, address, uint256) internal virtual {}
function _afterTokenTransfer(address, address, uint256) internal virtual {}
}
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor() { _transferOwnership(_msgSender()); }
modifier onlyOwner() { _checkOwner(); _; }
function owner() public view virtual returns (address) { return _owner; }
function _checkOwner() internal view virtual { require(owner() == _msgSender(), "Ownable: caller is not the owner"); }
function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); }
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is zero"); _transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner);
}
}
/* =================== TOKEN =================== */
contract deOTC is Ownable, ERC20("deOTC", "DOTC") {
/* ---- routers / pairs ---- */
IRouter public Router; // marketing router (DOTC->ETH)
IRouter public BurnRouter; // burn router (ETH->burnToken)
address public swapPair; // DOTC-WETH pair (for detecting buys/sells)
/* ---- fees (in basis points; 10000 = 100%) ---- */
uint256 public marketingFeeB; // buy marketing bps
uint256 public marketingFeeS; // sell marketing bps
uint256 public burnFee; // shared burn bps (applies on both buy/sell)
uint256 public swapAtAmount; // when contract balance >= this, perform fee processing
/* ---- fee accounting ---- */
uint256 public tokensForMarketing;
uint256 public tokensForBurn;
/* ---- burn destination ---- */
address public burnToken; // token to buy & send to dead (if not self-burn)
bool public isSelfBurn; // if true, burn DOTC itself
address public constant dead = 0x000000000000000000000000000000000000dEaD;
bool public burnPaused; // tracks if burning is paused
event BurnPaused(bool paused);
/* ---- wallets & flags ---- */
address payable public marketingWallet;
mapping (address => bool) public automatedMarketMakerPairs;
mapping (address => bool) private _isExcludedFromFees;
/* ---- reentrancy guard for swaps ---- */
bool private inSwapAndLiquify;
modifier lockTheSwap { inSwapAndLiquify = true; _; inSwapAndLiquify = false; }
/* ---- Admin controls --- */
address public tokenAdmin;
function setTokenAdmin(address _admin) external onlyOwnerOrAdmin {
require(_admin != address(0), "Admin cannot be zero address");
tokenAdmin = _admin;
}
modifier onlyOwnerOrAdmin() {
require(msg.sender == owner() || msg.sender == tokenAdmin, "Not owner or admin");
_;
}
/* ---- events ---- */
event ExcludeFromFees(address indexed account, bool isExcluded);
event SetAutomatedMarketMakerPair(address indexed pair, bool indexed value);
/* ---- burn tracking ---- */
struct BurnStats {
uint256 totalEthSpent;
uint256 totalTokensBurned;
}
mapping(address => BurnStats) public burnHistory;
address[] public burnTokensUsed;
mapping(address => bool) private _burnTokenSeen;
/* ------------ constructor ------------ */
constructor(
uint256 _marketingFeeBuy,
uint256 _marketingFeeSell,
address _router,
address _marketingWallet,
uint256 initialSupply
) {
marketingWallet = payable(_marketingWallet);
setMarketingFeeBuy(_marketingFeeBuy);
setMarketingFeeSell(_marketingFeeSell);
_isExcludedFromFees[msg.sender] = true;
_isExcludedFromFees[address(this)] = true;
_isExcludedFromFees[_marketingWallet] = true;
_mint(msg.sender, initialSupply * (10**18));
swapAtAmount = (initialSupply * (10**18)) * 10 / 1_000_000; // 0.01% of initial supply
updateSwapRouter(_router);
tokenAdmin = msg.sender;
}
/* ------------ admin: fees & wallets ------------ */
function setMarketingFeeBuy(uint256 _newMarketingFeeBuy) public onlyOwner {
marketingFeeB = _newMarketingFeeBuy;
uint256 totalBuy = marketingFeeB + (burnPaused ? 0 : burnFee);
if (_newMarketingFeeBuy > 0) require(marketingWallet != address(0), "Set marketing wallet first");
require(totalBuy <= 2000, "Buy total fee > 20%");
}
function setMarketingFeeSell(uint256 _newMarketingFeeSell) public onlyOwner {
marketingFeeS = _newMarketingFeeSell;
uint256 totalSell = marketingFeeS + (burnPaused ? 0 : burnFee);
if (_newMarketingFeeSell > 0) require(marketingWallet != address(0), "Set marketing wallet first");
require(totalSell <= 2000, "Sell total fee > 20%");
}
function setMarketingWallet(address payable newMarketingWallet) public onlyOwner {
if (_isExcludedFromFees[marketingWallet] == true) {
excludeFromFees(marketingWallet, false);
}
marketingWallet = newMarketingWallet;
if (_isExcludedFromFees[marketingWallet] == false) {
excludeFromFees(marketingWallet, true);
}
}
function setupBurn(address _newBurnToken, uint256 _newBurnFee, address _newBurnRouter) external onlyOwnerOrAdmin {
setBurnToken(_newBurnToken);
setBurnRouter(_newBurnRouter);
setBurnFee(_newBurnFee);
}
function setBurnToken(address _newBurnToken) public onlyOwnerOrAdmin {
burnToken = _newBurnToken;
isSelfBurn = (_newBurnToken == address(this));
}
function setBurnFee(uint256 _newBurnFee) public onlyOwner {
burnFee = _newBurnFee;
uint256 totalBuy = burnFee + marketingFeeB;
uint256 totalSell = burnFee + marketingFeeS;
require(totalBuy <= 2000, "Buy total fee > 20%");
require(totalSell <= 2000, "Sell total fee > 20%");
if (burnFee > 0 && !burnPaused) {
require(burnToken != address(0), "Burn token not set");
if (!isSelfBurn) require(address(BurnRouter) != address(0), "Burn router not set");
}
}
function pauseBurning() external onlyOwnerOrAdmin {
require(!burnPaused, "Burning already paused");
burnPaused = true;
emit BurnPaused(true);
}
function unpauseBurning() external onlyOwnerOrAdmin {
require(burnPaused, "Burning not paused");
if (burnFee > 0) {
require(burnToken != address(0), "Burn token not set");
if (!isSelfBurn) require(address(BurnRouter) != address(0), "Burn router not set");
}
burnPaused = false;
emit BurnPaused(false);
}
function setSwapAtAmount(uint256 _newSwapAtAmount) external onlyOwner {
swapAtAmount = _newSwapAtAmount;
}
/* ------------ manual swap & burn ------------ */
function manualSwapNBurn() external onlyOwnerOrAdmin {
require(!inSwapAndLiquify, "Swap already in progress");
_swapFees();
}
/* ------------ router mgmt ------------ */
function updateSwapRouter(address newAddress) public onlyOwner {
require(newAddress != address(Router), "Router unchanged");
Router = IRouter(newAddress);
address weth = Router.WETH();
address pair = IFactory(Router.factory()).getPair(address(this), weth);
if (pair == address(0)) {
pair = IFactory(Router.factory()).createPair(address(this), weth);
}
if (!automatedMarketMakerPairs[pair]) {
_setAutomatedMarketMakerPair(pair, true);
}
_approve(address(this), address(Router), type(uint256).max);
swapPair = pair;
}
function setBurnRouter(address _newBurnRouter) public onlyOwnerOrAdmin {
require(_newBurnRouter != address(0), "BurnRouter=0");
address weth;
address factory;
try IRouter(_newBurnRouter).WETH() returns (address w) { weth = w; } catch { revert("Not a V2 router"); }
try IRouter(_newBurnRouter).factory() returns (address f) { factory = f; } catch { revert("Not a V2 router"); }
require(weth != address(0) && factory != address(0), "Bad router");
require(burnToken != address(0), "Set burnToken first");
bool ok = _v2PairHasLiquidity(factory, weth, burnToken);
require(ok, "No WETH-burnToken liquidity");
BurnRouter = IRouter(_newBurnRouter);
}
function excludeFromFees(address account, bool excluded) public onlyOwner {
require(_isExcludedFromFees[account] != excluded, "Already set");
_isExcludedFromFees[account] = excluded;
emit ExcludeFromFees(account, excluded);
}
function _setAutomatedMarketMakerPair(address pair, bool value) public onlyOwner {
require(automatedMarketMakerPairs[pair] != value, "Pair already set");
automatedMarketMakerPairs[pair] = value;
emit SetAutomatedMarketMakerPair(pair, value);
}
/* ------------ fee logic ------------ */
function _isBuy(address from, address to) internal view returns (bool) {
return automatedMarketMakerPairs[from] && !automatedMarketMakerPairs[to];
}
function _isSell(address from, address to) internal view returns (bool) {
return automatedMarketMakerPairs[to] && !automatedMarketMakerPairs[from];
}
function _totalFeeBpsFor(address from, address to)
internal
view
returns (uint256 totalBps, uint256 mktBps, uint256 brnBps)
{
bool buy = _isBuy(from, to);
bool sell = _isSell(from, to);
if (buy) {
mktBps = marketingFeeB; brnBps = burnPaused ? 0 : burnFee; totalBps = mktBps + brnBps;
} else if (sell) {
mktBps = marketingFeeS; brnBps = burnPaused ? 0 : burnFee; totalBps = mktBps + brnBps;
} else {
mktBps = 0; brnBps = 0; totalBps = 0;
}
}
function _transfer(address from, address to, uint256 amount) internal override {
require(from != address(0), "ERC20: transfer from zero");
require(to != address(0), "ERC20: transfer to zero");
if (_isExcludedFromFees[from] || _isExcludedFromFees[to]) {
super._transfer(from, to, amount);
return;
}
(uint256 totalBps, uint256 mktBps, uint256 brnBps) = _totalFeeBpsFor(from, to);
uint256 fees;
if (totalBps > 0) {
fees = (amount * totalBps) / 10_000;
if (fees > 0) {
super._transfer(from, address(this), fees);
if (brnBps == 0) {
tokensForMarketing += fees;
} else if (mktBps == 0) {
tokensForBurn += fees;
} else {
uint256 forMarketing = (fees * mktBps) / totalBps;
uint256 forBurn = fees - forMarketing;
tokensForMarketing += forMarketing;
tokensForBurn += forBurn;
}
amount -= fees;
}
}
bool shouldSwap = _isSell(from, to) && !inSwapAndLiquify && balanceOf(address(this)) >= swapAtAmount;
if (shouldSwap) {
_swapFees();
}
super._transfer(from, to, amount);
}
function _swapFees() private lockTheSwap {
uint256 contractTokenBalance = balanceOf(address(this));
uint256 totalTokensToProcess = tokensForMarketing + tokensForBurn;
if (contractTokenBalance == 0 || totalTokensToProcess == 0) return;
if (isSelfBurn && tokensForBurn > 0 && !burnPaused) {
uint256 burnTokens = (contractTokenBalance * tokensForBurn) / totalTokensToProcess;
if (burnTokens > 0) {
super._transfer(address(this), dead, burnTokens);
burnHistory[burnToken].totalTokensBurned += burnTokens;
if (!_burnTokenSeen[burnToken]) {
_burnTokenSeen[burnToken] = true;
burnTokensUsed.push(burnToken);
}
contractTokenBalance -= burnTokens;
}
}
if (contractTokenBalance > 0) {
address[] memory path = new address[](2);
path[0] = address(this);
path[1] = Router.WETH();
_approve(address(this), address(Router), type(uint256).max);
try Router.swapExactTokensForETHSupportingFeeOnTransferTokens(
contractTokenBalance, 0, path, address(this), block.timestamp
) { } catch {
revert("swap DOTC->ETH failed");
}
}
uint256 balanceETH = address(this).balance;
uint256 totalAfter = tokensForMarketing + (burnPaused ? 0 : tokensForBurn);
if (balanceETH == 0 || totalAfter == 0) {
tokensForMarketing = 0;
tokensForBurn = 0;
return;
}
uint256 ethForBurn = burnPaused ? 0 : (balanceETH * tokensForBurn) / totalAfter;
uint256 ethForMarketing = balanceETH - ethForBurn;
if (!isSelfBurn && tokensForBurn > 0 && ethForBurn > 0 && !burnPaused) {
address[] memory path = new address[](2);
path[0] = BurnRouter.WETH();
path[1] = burnToken;
uint256 burnTokenBefore = IERC20(burnToken).balanceOf(address(this));
try BurnRouter.swapExactETHForTokensSupportingFeeOnTransferTokens{value: ethForBurn}(
0, path, address(this), block.timestamp
) { } catch {
revert("swap ETH->burnToken failed");
}
uint256 burnTokenReceived = IERC20(burnToken).balanceOf(address(this)) - burnTokenBefore;
if (burnTokenReceived > 0) {
IERC20(burnToken).transfer(dead, burnTokenReceived);
burnHistory[burnToken].totalEthSpent += ethForBurn;
burnHistory[burnToken].totalTokensBurned += burnTokenReceived;
if (!_burnTokenSeen[burnToken]) {
_burnTokenSeen[burnToken] = true;
burnTokensUsed.push(burnToken);
}
}
}
if (ethForMarketing > 0) {
payable(marketingWallet).transfer(ethForMarketing);
}
tokensForMarketing = 0;
tokensForBurn = 0;
}
/* ------------ utilities ------------ */
function _v2PairHasLiquidity(address factory, address tokenA, address tokenB)
internal
view
returns (bool ok)
{
address pair = IFactory(factory).getPair(tokenA, tokenB);
if (pair == address(0)) return false;
(uint112 r0, uint112 r1, ) = IUniswapV2Pair(pair).getReserves();
if (r0 == 0 || r1 == 0) return false;
return true;
}
function isExcludedFromFees(address account) external view returns(bool) {
return _isExcludedFromFees[account];
}
function currentBuyFeeBps() external view returns (uint256) {
return marketingFeeB + (burnPaused ? 0 : burnFee);
}
function currentSellFeeBps() external view returns (uint256) {
return marketingFeeS + (burnPaused ? 0 : burnFee);
}
struct BurnInfo {
address token;
uint256 ethSpent;
uint256 tokensBurned;
}
function getBurnStats(uint256 page, uint256 pageSize)
external
view
returns (
BurnInfo memory current, // the currently configured burnToken stats
BurnInfo[] memory history, // paginated other burn tokens
uint256 totalPages // based on pageSize, excluding current
)
{
require(page >= 1 && pageSize > 0, "bad paging");
// 1) Current
address curr = burnToken; // can be address(0) if not set
current = BurnInfo({
token: curr,
ethSpent: burnHistory[curr].totalEthSpent,
tokensBurned: burnHistory[curr].totalTokensBurned
});
// 2) History = burnTokensUsed excluding current
uint256 n = burnTokensUsed.length;
uint256 histCount = n;
// If current is set and present in the list, exclude it from history
if (curr != address(0) && _burnTokenSeen[curr]) {
unchecked { histCount = n - 1; }
}
// total pages (at least 1 so UIs are simpler)
totalPages = (histCount == 0) ? 1 : ((histCount + pageSize - 1) / pageSize);
if (histCount == 0) {
// nothing else in history
history = new BurnInfo[](0);
return (current, history, totalPages);
}
uint256 start = (page - 1) * pageSize;
if (start >= histCount) {
// out-of-range page -> empty page
history = new BurnInfo[](0);
return (current, history, totalPages);
}
uint256 remaining = histCount - start;
uint256 len = remaining < pageSize ? remaining : pageSize;
history = new BurnInfo[](len);
// 3) Walk the source list, skipping `curr`, then take the window [start, start+len)
uint256 filled = 0;
uint256 seen = 0;
for (uint256 i = 0; i < n && filled < len; i++) {
address t = burnTokensUsed[i];
if (t == curr) continue; // exclude current from history
if (seen < start) { seen++; continue; }
history[filled] = BurnInfo({
token: t,
ethSpent: burnHistory[t].totalEthSpent,
tokensBurned: burnHistory[t].totalTokensBurned
});
filled++;
}
}
/* ------------ owner rescue ------------ */
function removeNative() external onlyOwner {
payable(msg.sender).transfer(address(this).balance);
}
function removeTokens(address _tokenAddress) external onlyOwner {
ERC20(_tokenAddress).transfer(msg.sender, ERC20(_tokenAddress).balanceOf(address(this)));
}
/* ------------ receive ETH ------------ */
receive() external payable {}
}
Submitted on: 2025-10-14 09:26:12
Comments
Log in to comment.
No comments yet.