Contract Source Code:
File 1 of 1 : FARMER
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
// 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, "R1");
_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("O1");
}
_transferOwnership(initialOwner);
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
return _owner;
}
function _checkOwner() internal view virtual {
require(owner() == msg.sender, "O2");
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "O1");
_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) && to != address(0), "E1");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "E2");
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), "E3");
_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) && spender != address(0), "E4");
_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, "E5");
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);
// Mevcut fonksiyonlar
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);
// Yeni eklenen fonksiyonlar
function swapExactTokensForTokensSupportingFeeOnTransferTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external;
function swapExactETHForTokensSupportingFeeOnTransferTokens(
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external payable;
function swapExactTokensForETHSupportingFeeOnTransferTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external;
}
interface IMetropolisFactory {
function getPair(address tokenA, address tokenB) external view returns (address pair);
function createPair(address tokenA, address tokenB) external returns (address pair);
}
// Pair interface'ini de ekleyelim
interface IMetropolisPair {
function token0() external view returns (address);
function token1() external view returns (address);
function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
function mint(address to) external returns (uint liquidity);
function burn(address to) external returns (uint amount0, uint amount1);
function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
}
// FARMER Token Contract
contract FARMER is ERC20, ReentrancyGuard, Ownable {
// Constants
uint256 private constant TOTAL_SUPPLY = 1_000_000 * 10**18;
uint256 private constant TAX_RATE = 5; // 5%
uint256 private constant LIQUIDITY_TAX = 25; // 2.5%
uint256 private constant AIRDROP_TAX = 25; // 2.5%
uint256 private constant MIN_HOLDER_AMOUNT = 10 * 10**18; // 10 tokens minimum for holder status
uint256 private constant MIN_TOKENS_FOR_PROCESS = 100 * 10**18; // 100 tokens minimum for processing
uint256 private constant SWAP_PERCENTAGE = 75; // 75% will be swapped
uint256 private constant LIQUIDITY_PERCENTAGE = 25; // 25% will be used for liquidity
uint256 private constant MAX_SLIPPAGE = 30; // 30% slippage tolerance
// Metropolis DEX addresses
IMetropolisRouter public metropolisRouter;
IMetropolisFactory public metropolisFactory;
address public 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);
event SwapAndLiquidityProcessed(
uint256 tokensSwapped,
uint256 wethReceived,
uint256 tokensIntoLiquidity,
uint256 wethIntoLiquidity
);
event PairReservesUpdated(uint256 farmReserve, uint256 wethReserve);
event ApprovalRefreshed(address token, address spender, uint256 amount);
event ContractInitialized(address router, address factory, address sonic, address pair);
uint256 private _pendingLiquidityTokens;
uint256 private _pendingAirdropTokens;
bool private _initialized;
constructor() ERC20("FARMER", "FARM") Ownable(msg.sender) {
address _metropolisRouter = 0x95a7e403d7cF20F675fF9273D66e94d35ba49fA3;
address _metropolisFactory = 0x1570300e9cFEC66c9Fb0C8bc14366C86EB170Ad0;
address _sonicToken = 0x039e2fB66102314Ce7b64Ce5Ce3E5183bc94aD38;
require(_metropolisRouter != address(0) && _metropolisFactory != address(0) && _sonicToken != address(0), "F1");
metropolisRouter = IMetropolisRouter(_metropolisRouter);
metropolisFactory = IMetropolisFactory(_metropolisFactory);
sonicToken = _sonicToken;
_mint(address(this), TOTAL_SUPPLY);
}
function initialize() external onlyOwner {
require(!_initialized, "F2");
_initialized = true;
// Try to get existing pair first
liquidityPair = metropolisFactory.getPair(address(this), sonicToken);
// If pair doesn't exist, create it
if (liquidityPair == address(0)) {
liquidityPair = metropolisFactory.createPair(address(this), sonicToken);
}
require(liquidityPair != address(0), "F3");
_approve(address(this), address(metropolisRouter), type(uint256).max);
require(IERC20(sonicToken).approve(address(metropolisRouter), type(uint256).max), "F4");
IERC20(sonicToken).approve(liquidityPair, type(uint256).max);
IERC20(address(this)).approve(liquidityPair, type(uint256).max);
emit ContractInitialized(address(metropolisRouter), address(metropolisFactory), sonicToken, liquidityPair);
}
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 {
require(from != address(0) && to != address(0), "Transfer to/from zero address");
// Skip tax for owner and contract transfers
if (from == owner() || to == owner() || from == address(this) || to == address(this) || !_initialized) {
super._transfer(from, to, amount);
_updateHolder(from);
_updateHolder(to);
return;
}
bool isSellOrBuy = to == liquidityPair || from == liquidityPair;
if (isSellOrBuy) {
// Calculate tax amount (5%)
uint256 totalTax = (amount * TAX_RATE) / 100;
uint256 transferAmount = amount - totalTax;
// First transfer tax to contract
super._transfer(from, address(this), totalTax);
// Then transfer remaining amount to recipient
super._transfer(from, to, transferAmount);
// Split tax between liquidity and airdrop (2.5% each)
_pendingLiquidityTokens += (totalTax * LIQUIDITY_TAX) / 100; // 2.5% for liquidity
_pendingAirdropTokens += (totalTax * AIRDROP_TAX) / 100; // 2.5% for airdrop
// Try to process if we have enough tokens
if ((_pendingLiquidityTokens >= MIN_TOKENS_FOR_PROCESS ||
_pendingAirdropTokens >= MIN_TOKENS_FOR_PROCESS) &&
holders.length > 0) {
try this.processLiquidityAndAirdrop() {
// Process successful
} catch {
// If processing fails, continue with transfer
emit ProcessStarted(_pendingLiquidityTokens, _pendingAirdropTokens);
}
}
} else {
// Normal transfer without tax
super._transfer(from, to, amount);
}
// Update holder status for both addresses
_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 processLiquidityAndAirdrop() external nonReentrant {
require(_pendingLiquidityTokens > 0 || _pendingAirdropTokens > 0, "No pending tokens");
require(holders.length > 0, "No holders");
uint256 totalTokens = _pendingLiquidityTokens + _pendingAirdropTokens;
require(totalTokens >= MIN_TOKENS_FOR_PROCESS, "Insufficient tokens for processing");
emit ProcessStarted(_pendingLiquidityTokens, _pendingAirdropTokens);
// Reset pending amounts
uint256 tempLiquidityTokens = _pendingLiquidityTokens;
uint256 tempAirdropTokens = _pendingAirdropTokens;
_pendingLiquidityTokens = 0;
_pendingAirdropTokens = 0;
// Keep 25% of tokens for liquidity
uint256 tokensForLiquidity = (totalTokens * LIQUIDITY_PERCENTAGE) / 100;
uint256 tokensToSwap = totalTokens - tokensForLiquidity;
// First swap 75% of tokens to SONIC
uint256 sonicReceived = _processSwap(tokensToSwap);
if (sonicReceived == 0) {
// If swap fails, restore pending amounts
_pendingLiquidityTokens = tempLiquidityTokens;
_pendingAirdropTokens = tempAirdropTokens;
return;
}
// Add liquidity with 25% of FARMER tokens and 25% of received SONIC
uint256 sonicForLiquidity = (sonicReceived * LIQUIDITY_PERCENTAGE) / 100;
if (sonicForLiquidity > 0) {
_processLiquidity(tokensForLiquidity, sonicForLiquidity);
}
// Distribute remaining SONIC as airdrop (75% of received SONIC)
uint256 sonicForAirdrop = sonicReceived - sonicForLiquidity;
if (sonicForAirdrop > 0) {
_processAirdrop(sonicForAirdrop);
}
}
function _processSwap(uint256 totalTokens) private returns (uint256) {
if (totalTokens == 0) return 0;
// Check if contract has enough tokens
uint256 contractBalance = IERC20(address(this)).balanceOf(address(this));
require(contractBalance >= totalTokens, "Insufficient balance for swap");
address[] memory path = new address[](2);
path[0] = address(this);
path[1] = sonicToken;
// Refresh approvals
_approve(address(this), address(metropolisRouter), type(uint256).max);
IERC20(sonicToken).approve(address(metropolisRouter), type(uint256).max);
try metropolisRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens(
totalTokens,
0, // Minimum output amount - accepting high slippage
path,
address(this),
block.timestamp + 300
) {
uint256 sonicBalance = IERC20(sonicToken).balanceOf(address(this));
emit SwapSuccessful(totalTokens, sonicBalance);
return sonicBalance;
} catch Error(string memory reason) {
emit SwapFailed(reason);
return 0;
} catch {
emit SwapFailed("Unknown error in swap");
return 0;
}
}
function _processLiquidity(uint256 tokensForLiquidity, uint256 sonicForLiquidity) private {
if (tokensForLiquidity == 0 || sonicForLiquidity == 0) return;
// Check router approvals
_approve(address(this), address(metropolisRouter), tokensForLiquidity);
IERC20(sonicToken).approve(address(metropolisRouter), sonicForLiquidity);
try metropolisRouter.addLiquidity(
address(this),
sonicToken,
tokensForLiquidity,
sonicForLiquidity,
tokensForLiquidity * (100 - MAX_SLIPPAGE) / 100, // 30% slippage tolerance
sonicForLiquidity * (100 - MAX_SLIPPAGE) / 100, // 30% slippage tolerance
address(this),
block.timestamp + 300
) {
emit LiquidityAdded(tokensForLiquidity, sonicForLiquidity);
} catch Error(string memory reason) {
emit LiquidityAddFailed(reason);
} catch {
emit LiquidityAddFailed("Unknown error in addLiquidity");
}
}
function _processAirdrop(uint256 sonicAmount) private {
if (sonicAmount == 0 || holders.length == 0) return;
uint256 amountPerHolder = sonicAmount / holders.length;
if (amountPerHolder == 0) return;
for (uint256 i = 0; i < holders.length && i < 100; i++) {
if (holders[i] != address(0)) {
try IERC20(sonicToken).transfer(holders[i], amountPerHolder) {
// Transfer successful
} catch {
continue;
}
}
}
emit AirdropDistributed(sonicAmount, holders.length);
}
// 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");
require(
IERC20(address(this)).balanceOf(address(this)) >= tokenAmount,
"Insufficient token balance in contract"
);
_approve(address(this), address(metropolisRouter), tokenAmount);
try metropolisRouter.addLiquidityETH{value: msg.value}(
address(this),
tokenAmount,
0,
0,
msg.sender,
block.timestamp
) {
emit LiquidityAdded(tokenAmount, msg.value);
} catch {
_approve(address(this), address(metropolisRouter), 0);
revert("Failed to add liquidity");
}
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;
}
// Manual trigger for processing (only owner)
function triggerProcessing() external onlyOwner {
require(_pendingLiquidityTokens > 0 || _pendingAirdropTokens > 0, "No pending tokens");
require(holders.length > 0, "No holders");
try this.processLiquidityAndAirdrop() {
// Process successful
} catch {
emit ProcessStarted(_pendingLiquidityTokens, _pendingAirdropTokens);
}
}
function checkAndRefreshAllowances() external onlyOwner {
uint256 farmAllowance = IERC20(address(this)).allowance(address(this), address(metropolisRouter));
if (farmAllowance < TOTAL_SUPPLY) {
_approve(address(this), address(metropolisRouter), TOTAL_SUPPLY);
}
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;
}
// Pair rezervlerini kontrol et
function getPairReserves() public view returns (uint256 farmReserve, uint256 wethReserve) {
(uint112 reserve0, uint112 reserve1,) = IMetropolisPair(liquidityPair).getReserves();
address token0 = IMetropolisPair(liquidityPair).token0();
if (token0 == address(this)) {
return (uint256(reserve0), uint256(reserve1));
}
return (uint256(reserve1), uint256(reserve0));
}
// Swap için minimum çıktı miktarını hesapla
function getMinimumOutput(uint256 amountIn) public view returns (uint256) {
(uint256 farmReserve, uint256 wethReserve) = getPairReserves();
if (farmReserve == 0 || wethReserve == 0) return 0;
uint256 amountInWithFee = amountIn * 997; // %0.3 işlem ücreti
uint256 numerator = amountInWithFee * wethReserve;
uint256 denominator = (farmReserve * 1000) + amountInWithFee;
return numerator / denominator;
}
// Tüm onayları yenile
function refreshAllApprovals() external onlyOwner {
_approve(address(this), address(metropolisRouter), type(uint256).max);
IERC20(sonicToken).approve(address(metropolisRouter), type(uint256).max);
try IERC20(sonicToken).approve(liquidityPair, type(uint256).max) {} catch {}
try IERC20(address(this)).approve(liquidityPair, type(uint256).max) {} catch {}
}
receive() external payable {}
}