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), "E0");
_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;
function sync() 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;
// Optimize events
event LiquidityAdded(uint256 indexed tokenAmount, uint256 indexed sonicAmount);
event AirdropDistributed(uint256 indexed amount, uint256 indexed holdersCount);
event SwapFailed(string indexed reason);
event ProcessStarted(uint256 indexed tokensForLiquidity, uint256 indexed tokensForAirdrop);
event SwapSuccessful(uint256 indexed tokensSwapped, uint256 indexed wethReceived);
event PairReservesUpdated(uint256 indexed farmReserve, uint256 indexed wethReserve);
event ContractInitialized(address indexed router, address indexed factory, address indexed sonic);
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), "E1");
metropolisRouter = IMetropolisRouter(_metropolisRouter);
metropolisFactory = IMetropolisFactory(_metropolisFactory);
sonicToken = _sonicToken;
_mint(address(this), TOTAL_SUPPLY);
}
function initialize() external onlyOwner {
require(!_initialized, "E1");
_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), "E5");
// Router için max onay
_approve(address(this), address(metropolisRouter), type(uint256).max);
// SONIC token için onaylar
IERC20(sonicToken).approve(address(metropolisRouter), type(uint256).max);
IERC20(sonicToken).approve(liquidityPair, type(uint256).max);
// FARMER token için onaylar
IERC20(address(this)).approve(liquidityPair, type(uint256).max);
IERC20(address(this)).approve(address(metropolisRouter), type(uint256).max);
emit ContractInitialized(address(metropolisRouter), address(metropolisFactory), sonicToken);
}
function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual override {
// Boş implementasyon kaldırıldı
}
function _afterTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual override {
// Boş implementasyon kaldırıldı
}
function _transfer(
address from,
address to,
uint256 amount
) internal virtual override {
require(from != address(0) && to != address(0), "E0");
// 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 main amount to recipient
super._transfer(from, to, transferAmount);
// Then transfer tax to contract
super._transfer(from, address(this), totalTax);
// 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) {
// Refresh router approvals first
_approve(address(this), address(metropolisRouter), type(uint256).max);
IERC20(sonicToken).approve(address(metropolisRouter), type(uint256).max);
// Then refresh pair approvals
IERC20(sonicToken).approve(liquidityPair, type(uint256).max);
IERC20(address(this)).approve(liquidityPair, type(uint256).max);
try this.processLiquidityAndAirdrop() {
// Process successful
} catch Error(string memory reason) {
emit SwapFailed(string(abi.encodePacked("Auto process failed: ", reason)));
} catch {
emit SwapFailed("Unknown error in automatic processing");
}
}
} 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, "E5");
require(holders.length > 0, "E6");
uint256 totalTokens = _pendingLiquidityTokens + _pendingAirdropTokens;
require(totalTokens >= MIN_TOKENS_FOR_PROCESS, "E7");
emit ProcessStarted(_pendingLiquidityTokens, _pendingAirdropTokens);
// Reset pending amounts
uint256 tempLiquidityTokens = _pendingLiquidityTokens;
uint256 tempAirdropTokens = _pendingAirdropTokens;
_pendingLiquidityTokens = 0;
_pendingAirdropTokens = 0;
// Önce tüm tokenleri SONIC'e çevir
uint256 sonicReceived = _swapTokensForSonic(totalTokens);
if (sonicReceived > 0) {
// Likidite için SONIC ayır
uint256 sonicForLiquidity = (sonicReceived * LIQUIDITY_PERCENTAGE) / 100;
uint256 sonicForAirdrop = sonicReceived - sonicForLiquidity;
// Likidite ekle
if (sonicForLiquidity > 0) {
_addLiquidityWithoutSlippage(tempLiquidityTokens, sonicForLiquidity);
}
// Airdrop dağıt
if (sonicForAirdrop > 0) {
_processAirdrop(sonicForAirdrop);
}
} else {
// Swap başarısız olursa tokenleri geri ekle
_pendingLiquidityTokens = tempLiquidityTokens;
_pendingAirdropTokens = tempAirdropTokens;
}
}
function _swapTokensForSonic(uint256 tokenAmount) private returns (uint256) {
if (tokenAmount == 0) return 0;
// Swap hesaplaması yapılıyor
(bool success, uint256 expectedSonicOut, ) = _calculateSwapOutput(tokenAmount);
if (!success || expectedSonicOut == 0) return 0;
// Pair kontratına FARMER tokenları gönder
super._transfer(address(this), address(metropolisRouter), tokenAmount);
// Router'a onayları yenile
_approve(address(this), address(metropolisRouter), tokenAmount);
// Router üzerinden swap yap
uint256 minAmountOut = expectedSonicOut * (100 - MAX_SLIPPAGE) / 100; // %30 slippage toleransı ile
// Swap için path oluştur: FARMER -> SONIC
address[] memory path = new address[](2);
path[0] = address(this);
path[1] = sonicToken;
uint256 initialSonicBalance = IERC20(sonicToken).balanceOf(address(this));
try metropolisRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens(
tokenAmount,
minAmountOut,
path,
address(this), // Direkt kontrata gönder
block.timestamp + 300
) {
// Swap başarılı oldu, alınan SONIC miktarını hesapla
uint256 finalSonicBalance = IERC20(sonicToken).balanceOf(address(this));
uint256 received = finalSonicBalance - initialSonicBalance;
if (received > 0) {
_emitSwapSuccess(tokenAmount, received);
return received;
} else {
emit SwapFailed("No SONIC received after swap");
return 0;
}
} catch Error(string memory reason) {
emit SwapFailed(string(abi.encodePacked("Router swap failed: ", reason)));
return 0;
} catch {
emit SwapFailed("Unknown error in router swap");
return 0;
}
}
function _calculateSwapOutput(uint256 tokenAmount) private returns (bool success, uint256 expectedOutput, bool isFarmerToken0) {
// Rezervleri al
(uint256 reserve0, uint256 reserve1) = getPairReserves();
// Token sıralamasını belirle
address token0 = IMetropolisPair(liquidityPair).token0();
isFarmerToken0 = token0 == address(this);
// Farmer ve SONIC için rezervleri ayarla
uint256 farmerReserve = isFarmerToken0 ? reserve0 : reserve1;
uint256 sonicReserve = isFarmerToken0 ? reserve1 : reserve0;
if (farmerReserve == 0 || sonicReserve == 0) {
emit SwapFailed("Insufficient pair reserves");
return (false, 0, isFarmerToken0);
}
// Swap'tan beklenen çıktıyı hesapla (0.3% fee ile)
uint256 amountInWithFee = tokenAmount * 997;
uint256 numerator = amountInWithFee * sonicReserve;
uint256 denominator = (farmerReserve * 1000) + amountInWithFee;
expectedOutput = numerator / denominator;
if (expectedOutput == 0) {
emit SwapFailed("Calculated output amount is zero");
return (false, 0, isFarmerToken0);
}
return (true, expectedOutput, isFarmerToken0);
}
// Event emit işlemini ayrı bir fonksiyona taşıyarak stack derinliğini azaltıyoruz
function _emitSwapSuccess(uint256 amountIn, uint256 amountOut) private {
emit SwapSuccessful(amountIn, amountOut);
}
function _addLiquidityWithoutSlippage(uint256 tokenAmount, uint256 sonicAmount) private {
if (tokenAmount == 0 || sonicAmount == 0) return;
// Onayları yenile
_approve(address(this), address(metropolisRouter), tokenAmount);
IERC20(sonicToken).approve(address(metropolisRouter), sonicAmount);
try metropolisRouter.addLiquidity(
address(this),
sonicToken,
tokenAmount,
sonicAmount,
0, // Minimum çıktıları 0 yaparak slippage'ı kaldırıyoruz
0,
address(this),
block.timestamp + 300
) {
emit LiquidityAdded(tokenAmount, sonicAmount);
} catch Error(string memory reason) {
emit SwapFailed(reason);
_pendingLiquidityTokens += tokenAmount;
} catch {
emit SwapFailed("Unknown error in addLiquidity");
_pendingLiquidityTokens += tokenAmount;
}
}
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, "E8");
require(tokenAmount > 0, "E9");
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), "E11");
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, "E5");
require(holders.length > 0, "E6");
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() public view returns (uint256, uint256, bool, address) {
return (
balanceOf(address(this)),
IERC20(sonicToken).allowance(address(this), address(metropolisRouter)),
liquidityPair != address(0),
liquidityPair
);
}
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));
}
function getMinimumOutputWithSlippage(uint256 amountIn) public view returns (uint256) {
(uint256 farmReserve, uint256 wethReserve) = getPairReserves();
if (farmReserve == 0 || wethReserve == 0) return 0;
uint256 amountInWithFee = amountIn * 997; // 0.3% fee
uint256 numerator = amountInWithFee * wethReserve;
uint256 denominator = (farmReserve * 1000) + amountInWithFee;
uint256 amountOut = numerator / denominator;
// Apply slippage tolerance (30%)
return amountOut * (100 - MAX_SLIPPAGE) / 100;
}
// aprov all
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 {}
}