Contract Source Code:
File 1 of 1 : FARMER
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
// OpenZeppelin Contracts v5.0.1
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
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);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*/
abstract contract ReentrancyGuard {
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
modifier nonReentrant() {
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
_status = _ENTERED;
_;
_status = _NOT_ENTERED;
}
}
/**
* @dev Contract module which provides a basic access control mechanism
*/
abstract contract Ownable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert("Ownable: new owner is the zero address");
}
_transferOwnership(initialOwner);
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
return _owner;
}
function _checkOwner() internal view virtual {
require(owner() == msg.sender, "Ownable: caller is not the owner");
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
/**
* @dev Implementation of the {IERC20} interface.
*/
abstract contract ERC20 is IERC20 {
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 amount) public virtual returns (bool) {
address owner = msg.sender;
_transfer(owner, to, amount);
return true;
}
function allowance(address owner, address spender) public view virtual returns (uint256) {
return _allowances[owner][spender];
}
function approve(address spender, uint256 amount) public virtual returns (bool) {
address owner = msg.sender;
_approve(owner, spender, amount);
return true;
}
function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) {
address spender = msg.sender;
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
return true;
}
function _transfer(address from, address to, uint256 amount) internal virtual {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer 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 the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
unchecked {
_balances[account] += amount;
}
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
function _approve(address owner, address spender, uint256 amount) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_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 from, address to, uint256 amount) internal virtual {}
function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}
interface IMetropolisRouter {
function factory() external pure returns (address);
function WETH() external pure returns (address);
function addLiquidity(
address tokenA,
address tokenB,
uint amountADesired,
uint amountBDesired,
uint amountAMin,
uint amountBMin,
address to,
uint deadline
) external returns (uint amountA, uint amountB, uint liquidity);
function swapExactTokensForTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external returns (uint[] memory amounts);
function addLiquidityETH(
address token,
uint amountTokenDesired,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline
) external payable returns (uint amountToken, uint amountETH, uint liquidity);
}
interface IMetropolisFactory {
function getPair(address tokenA, address tokenB) external view returns (address pair);
function createPair(address tokenA, address tokenB) external returns (address pair);
}
// FARMER Token Contract
contract FARMER is ERC20, ReentrancyGuard, Ownable {
// Constants
uint256 public constant TOTAL_SUPPLY = 1_000_000 * 10**18;
uint256 public constant TAX_RATE = 5; // 5%
uint256 public constant LIQUIDITY_TAX = 25; // 2.5%
uint256 public constant AIRDROP_TAX = 25; // 2.5%
uint256 public constant MIN_HOLDER_AMOUNT = 10 * 10**18; // 10 tokens
uint256 public constant MIN_TOKENS_FOR_PROCESS = 100 * 10**18; // 100 token minimum for both operations
// Metropolis DEX addresses
IMetropolisRouter public metropolisRouter;
IMetropolisFactory public immutable metropolisFactory;
address public immutable sonicToken;
address public liquidityPair;
// Holder tracking
mapping(address => bool) public isHolder;
address[] public holders;
// Events
event LiquidityAdded(uint256 tokenAmount, uint256 sonicAmount);
event AirdropDistributed(uint256 amount, uint256 holdersCount);
event SwapFailed(string reason);
event LiquidityAddFailed(string reason);
event AirdropFailed(string reason);
event ProcessStarted(uint256 tokensForLiquidity, uint256 tokensForAirdrop);
event SwapSuccessful(uint256 tokensSwapped, uint256 wethReceived);
uint256 private _pendingLiquidityTokens;
uint256 private _pendingAirdropTokens;
constructor() ERC20("FARMER", "FARM") Ownable(msg.sender) {
metropolisRouter = IMetropolisRouter(0x95a7e403d7cF20F675fF9273D66e94d35ba49fA3);
metropolisFactory = IMetropolisFactory(0x1570300e9cFEC66c9Fb0C8bc14366C86EB170Ad0);
sonicToken = 0x039e2fB66102314Ce7b64Ce5Ce3E5183bc94aD38; // Doğru WETH adresi
// Mint all tokens to contract first
_mint(address(this), TOTAL_SUPPLY);
// Create pair
liquidityPair = metropolisFactory.createPair(address(this), sonicToken);
// Approve router for full supply
_approve(address(this), address(metropolisRouter), TOTAL_SUPPLY);
}
function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual override {
super._beforeTokenTransfer(from, to, amount);
}
function _afterTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual override {
super._afterTokenTransfer(from, to, amount);
}
function _transfer(
address from,
address to,
uint256 amount
) internal virtual override {
if (from == owner() || to == owner() || from == address(this) || to == address(this)) {
super._transfer(from, to, amount);
_updateHolder(from);
_updateHolder(to);
} else {
bool isSellOrBuy = to == liquidityPair || from == liquidityPair;
if (isSellOrBuy) {
uint256 totalTax = (amount * TAX_RATE) / 100; // %5
uint256 liquidityTax = (amount * LIQUIDITY_TAX) / 1000; // %2.5 (25/1000)
uint256 airdropTax = (amount * AIRDROP_TAX) / 1000; // %2.5 (25/1000)
uint256 transferAmount = amount - totalTax;
// Transfer the tax to contract
super._transfer(from, address(this), totalTax);
// Transfer the remaining amount
super._transfer(from, to, transferAmount);
// Accumulate tokens for liquidity and airdrop
_pendingLiquidityTokens += liquidityTax;
_pendingAirdropTokens += airdropTax;
// Process when either liquidity or airdrop tokens reach minimum
if ((_pendingLiquidityTokens >= MIN_TOKENS_FOR_PROCESS || _pendingAirdropTokens >= MIN_TOKENS_FOR_PROCESS) &&
holders.length > 0) {
_processLiquidityAndAirdrop();
}
} else {
super._transfer(from, to, amount);
}
_updateHolder(from);
_updateHolder(to);
}
}
function _updateHolder(address account) private {
if (account != liquidityPair && account != address(this)) {
uint256 balance = balanceOf(account);
bool isCurrentlyHolder = isHolder[account];
if (balance >= MIN_HOLDER_AMOUNT && !isCurrentlyHolder) {
isHolder[account] = true;
holders.push(account);
} else if (balance < MIN_HOLDER_AMOUNT && isCurrentlyHolder) {
isHolder[account] = false;
_removeHolder(account);
}
}
}
function _removeHolder(address account) private {
for (uint256 i = 0; i < holders.length; i++) {
if (holders[i] == account) {
holders[i] = holders[holders.length - 1];
holders.pop();
break;
}
}
}
function _processSwap(uint256 totalTokens) private returns (uint256) {
address[] memory path = new address[](2);
path[0] = address(this);
path[1] = sonicToken; // metropolisRouter.WETH() yerine sonicToken kullanıyoruz
// Router onayını kontrol et
uint256 currentAllowance = IERC20(address(this)).allowance(address(this), address(metropolisRouter));
if (currentAllowance < totalTokens) {
_approve(address(this), address(metropolisRouter), 0);
_approve(address(this), address(metropolisRouter), type(uint256).max);
}
try metropolisRouter.swapExactTokensForTokens(
totalTokens,
0, // minimum çıktı miktarı
path,
address(this), // WETH'leri kontrata gönder
block.timestamp + 300
) returns (uint[] memory) {
uint256 wethBalance = IERC20(sonicToken).balanceOf(address(this));
emit SwapSuccessful(totalTokens, wethBalance);
return wethBalance;
} catch Error(string memory reason) {
emit SwapFailed(reason);
return 0;
} catch {
emit SwapFailed("Unknown error in swap");
return 0;
}
}
function _processLiquidity(uint256 tokensForLiquidity, uint256 wethForLiquidity) private {
if (wethForLiquidity > 0) {
uint256 wethAllowance = IERC20(sonicToken).allowance(address(this), address(metropolisRouter));
if (wethAllowance < wethForLiquidity) {
IERC20(sonicToken).approve(address(metropolisRouter), type(uint256).max);
}
try metropolisRouter.addLiquidity(
address(this),
sonicToken,
tokensForLiquidity / 2,
wethForLiquidity,
0,
0,
address(this),
block.timestamp
) {
emit LiquidityAdded(tokensForLiquidity / 2, wethForLiquidity);
} catch Error(string memory reason) {
emit LiquidityAddFailed(reason);
_pendingLiquidityTokens = tokensForLiquidity;
} catch {
emit LiquidityAddFailed("Unknown error in addLiquidity");
_pendingLiquidityTokens = tokensForLiquidity;
}
}
}
function _processAirdrop(uint256 wethForAirdrop) private {
if (wethForAirdrop > 0 && holders.length > 0) {
uint256 amountPerHolder = wethForAirdrop / holders.length;
if (amountPerHolder > 0) {
for (uint256 i = 0; i < holders.length && i < 100; i++) {
try IERC20(sonicToken).transfer(holders[i], amountPerHolder) {
// Transfer successful
} catch Error(string memory reason) {
emit AirdropFailed(reason);
continue;
} catch {
emit AirdropFailed("Unknown error in WETH transfer");
continue;
}
}
emit AirdropDistributed(wethForAirdrop, holders.length);
}
}
}
function _processLiquidityAndAirdrop() private {
uint256 tokensForLiquidity = _pendingLiquidityTokens;
uint256 tokensForAirdrop = _pendingAirdropTokens;
uint256 totalTokens = tokensForLiquidity + tokensForAirdrop;
emit ProcessStarted(tokensForLiquidity, tokensForAirdrop);
// Önce kontrat bakiyesini kontrol et
uint256 contractBalance = IERC20(address(this)).balanceOf(address(this));
require(contractBalance >= totalTokens, "Insufficient token balance for swap");
_pendingLiquidityTokens = 0;
_pendingAirdropTokens = 0;
// Router approval kontrolü
uint256 currentAllowance = IERC20(address(this)).allowance(address(this), address(metropolisRouter));
if (currentAllowance < totalTokens) {
_approve(address(this), address(metropolisRouter), 0);
_approve(address(this), address(metropolisRouter), TOTAL_SUPPLY);
}
// Likidite havuzu kontrolü
address pair = IMetropolisFactory(metropolisRouter.factory()).getPair(address(this), metropolisRouter.WETH());
require(pair != address(0), "Liquidity pair does not exist");
require(pair == liquidityPair, "Invalid liquidity pair");
// Process swap
uint256 wethBalance = _processSwap(totalTokens);
if (wethBalance > 0) {
// Calculate proportions
uint256 wethForLiquidity = (wethBalance * tokensForLiquidity) / totalTokens;
uint256 wethForAirdrop = wethBalance - wethForLiquidity;
// Process liquidity
_processLiquidity(tokensForLiquidity, wethForLiquidity);
// Process airdrop
_processAirdrop(wethForAirdrop);
} else {
// If swap failed, restore pending amounts
_pendingLiquidityTokens = tokensForLiquidity;
_pendingAirdropTokens = tokensForAirdrop;
}
}
// View functions
function getHolderCount() external view returns (uint256) {
return holders.length;
}
function getHolders() external view returns (address[] memory) {
return holders;
}
// Initial liquidity function (must be called by owner with ETH)
function addInitialLiquidity(uint256 tokenAmount) external payable onlyOwner {
require(msg.value > 0, "Must provide ETH for initial liquidity");
require(tokenAmount > 0, "Must provide token amount for initial liquidity");
// Check contract balance
require(
IERC20(address(this)).balanceOf(address(this)) >= tokenAmount,
"Insufficient token balance in contract"
);
// Approve router to spend tokens directly from contract
_approve(address(this), address(metropolisRouter), tokenAmount);
// Add liquidity with minimal checks
try metropolisRouter.addLiquidityETH{value: msg.value}(
address(this),
tokenAmount,
0, // No minimum token amount
0, // No minimum ETH amount
msg.sender, // LP tokens will go to owner directly
block.timestamp // Immediate execution
) {
emit LiquidityAdded(tokenAmount, msg.value);
} catch {
// If failed, revert the approval
_approve(address(this), address(metropolisRouter), 0);
revert("Failed to add liquidity");
}
// Return any unused ETH
uint256 remainingETH = address(this).balance;
if (remainingETH > 0) {
payable(msg.sender).transfer(remainingETH);
}
}
function setRouter(address _router) external onlyOwner {
require(_router != address(0), "Invalid router address");
metropolisRouter = IMetropolisRouter(_router);
}
function rescueTokens(address _token, uint256 _amount) external onlyOwner {
require(_token != address(this), "Cannot rescue FARMER tokens");
IERC20(_token).transfer(owner(), _amount);
}
// Transfer tokens to owner
function withdrawTokensToOwner(uint256 amount) external onlyOwner {
require(
IERC20(address(this)).balanceOf(address(this)) >= amount,
"Insufficient balance"
);
_transfer(address(this), msg.sender, amount);
}
// View functions for pending amounts
function getPendingLiquidityTokens() external view returns (uint256) {
return _pendingLiquidityTokens;
}
function getPendingAirdropTokens() external view returns (uint256) {
return _pendingAirdropTokens;
}
// Process liquidity and airdrop manually if needed
function processLiquidityAndAirdrop() external onlyOwner {
require(_pendingLiquidityTokens > 0 || _pendingAirdropTokens > 0, "No pending tokens");
require(holders.length > 0, "No holders");
_processLiquidityAndAirdrop();
}
function checkAndRefreshAllowances() external onlyOwner {
// Check and refresh FARM approval for router
uint256 farmAllowance = IERC20(address(this)).allowance(address(this), address(metropolisRouter));
if (farmAllowance < TOTAL_SUPPLY) {
_approve(address(this), address(metropolisRouter), TOTAL_SUPPLY);
}
// Check and refresh WETH approval for router
uint256 wethBalance = IERC20(sonicToken).balanceOf(address(this));
if (wethBalance > 0) {
uint256 wethAllowance = IERC20(sonicToken).allowance(address(this), address(metropolisRouter));
if (wethAllowance < wethBalance) {
IERC20(sonicToken).approve(address(metropolisRouter), type(uint256).max);
}
}
}
// Debug function
function getSwapInfo() external view returns (
uint256 contractBalance,
uint256 routerAllowance,
bool pairExists,
address pair,
uint256 pendingLiq,
uint256 pendingAirdrop
) {
contractBalance = IERC20(address(this)).balanceOf(address(this));
routerAllowance = IERC20(address(this)).allowance(address(this), address(metropolisRouter));
pair = IMetropolisFactory(metropolisRouter.factory()).getPair(address(this), sonicToken);
pairExists = pair != address(0);
pendingLiq = _pendingLiquidityTokens;
pendingAirdrop = _pendingAirdropTokens;
}
receive() external payable {}
}