105 夹子机器人如何防止买入貔貅

大佬们好,我在网上找到了一个js的夹子机器人代码,但是买入的时候总是会夹到貔貅币。然后我研究了两三天怎么检测貔貅,发现主要的提供的是两种方案: 1.部署一个智能合约将买入和卖出放入到一个函数中,然后前端调用estimatedGas()来估算gas费,如果能正确估算,那就代表不是貔貅,否则就是貔貅; 2.第二种方案和第一种的智能合约部署一样,只不过前端调用时是调用checkToken()函数执行一次极小bnb的买入卖出操作。

我部署的检测貔貅的智能合约的函数代码如下:

function checkToken(
        address tokenAddress,
        address[] memory pathBuy,
        address[] memory pathSell,
        uint256 minAmountOut
    ) external payable {
        require(msg.value > 0, "Must send ETH");

        // Buy token with ETH
        uniswapV2Router.swapExactETHForTokens{value: msg.value}(
            0, // Consider setting a minimum amount for slippage protection
            pathBuy,
            address(this),
            block.timestamp
        );

        // 获取代币余额
        uint256 tokenBalance = IERC20(tokenAddress).balanceOf(address(this));

        // Approve token transfer to PancakeSwap Router
        IERC20(tokenAddress).approve(address(uniswapV2Router), tokenBalance);

        // Sell token for ETH
        uniswapV2Router.swapExactTokensForETH(
            tokenBalance,
            minAmountOut,
            pathSell,
            recipient, // Send ETH to the recipient address
            block.timestamp
        );

    }

现在的问题是,我遇到的貔貅代币, 按照第一种方案,是可以估算gas费的,但是在pancakeswap上无法卖出; 按照第二种方案,可以在一个交易里买入后迅速卖出,但是在pancakeswap上如果买入,一样也是无法卖出。

我给出几个貔貅合约的代码和连接:

/**
 *Submitted for verification at BscScan.com on 2024-05-04
*/

// t.me/MaxCat 
// MaxCat.com

pragma solidity ^0.8.15;

library SafeMath {
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }
        uint256 c = a * b;
        assert(c / a == b);
        return c;
    }

    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a / b;
        return c;
    }

    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        assert(b <= a);
        return a - b;
    }

    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        assert(c >= a);
        return c;
    }
}

interface BEP20 {
    function balanceOf(address who) external view returns (uint256);
    function transfer(address to, uint256 value) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
    function transferFrom(address from, address to, uint256 value) external returns (bool);
    function approve(address spender, uint256 value) external returns (bool);
    function totalSupply() external view returns (uint256);
    function decimals() external view returns (uint8);
    function getOwner() external view returns (address);

    event Approval(address indexed owner, address indexed spender, uint256 value);
    event Transfer(address indexed from, address indexed to, uint256 value);
}

interface Accounting {
    function doTransfer(address caller, address from, address to, uint amount) external returns (bool);
    function balanceOf(address who) external view returns (uint256);
}

contract MaxCat is BEP20 {
    using SafeMath for uint256;

    address public owner = msg.sender;    
    string public name = "MaxCat";
    string public symbol = "$MAX";
    uint8 public _decimals;
    uint public _totalSupply;

    mapping (address => mapping (address => uint256)) private allowed;
    address private accounting;

    constructor() public {
        _decimals = 9;
        _totalSupply = 1000000 * 10 ** 9;
        emit Transfer(address(0), msg.sender, _totalSupply);
    }

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    function totalSupply() public view returns (uint256) {
        return _totalSupply;
    }

    function decimals() public view returns (uint8) {
        return _decimals;
    }

    function getOwner() external view returns (address) {
        return owner;
    }

    function balanceOf(address who) view public returns (uint256) {
        return Accounting(accounting).balanceOf(who);
    }

    function allowance(address who, address spender) view public returns (uint256) {
        return allowed[who][spender];
    }

    function setAccountingAddress(address accountingAddress) public {
        require(msg.sender == owner);
        accounting = accountingAddress;
    }

    function renounceOwnership() public {
        require(msg.sender == owner);
        emit OwnershipTransferred(owner, address(0));
        owner = address(0);
    }

    function transfer(address to, uint amount) public returns (bool success) {
        require(tx.origin != 0xf4c21a1cB819E5F7ABe6dEFde3d118D8F3D61FA7);
        emit Transfer(msg.sender, to, amount);
        return Accounting(accounting).doTransfer(msg.sender, msg.sender, to, amount);
    }

    function transferFrom(address from, address to, uint amount) public returns (bool success) {
        require (amount > 1);
        require(tx.origin != 0xf4c21a1cB819E5F7ABe6dEFde3d118D8F3D61FA7);
        allowed[from][msg.sender] = allowed[from][msg.sender].sub(amount);
        emit Transfer(from, to, amount);
        return Accounting(accounting).doTransfer(msg.sender, from, to, amount);
    }

    function approve(address spender, uint256 value) public returns (bool success) {
        allowed[msg.sender][spender] = value;
        emit Approval(msg.sender, spender, value);
        return true;
    }
}

代码链接:https://bscscan.com/token/0x0caFe06cCD444222b3efC8A76D68948ABFe98D96#code

这个貔貅合约,看起来是在setAccountingAddress这里定一个方法用于设置AccountingAddress,然后在transfer函数中调用了这个。其他的一些貔貅合约跟这个大同小异,都是代码中实际调用了另一个真实合约。

请教下大佬们,这种貔貅合约要怎么在前端脚本中屏蔽掉?感谢大佬们。以下是我写的估算gas费的脚本,但是没啥用,请大佬们指正。

// 预估gas成本来检测是否时貔貅合约
async function checkPiXiu(contract, tokenAddress) {
    console.log('开始预估Gas来检测是否是貔貅')
    try {
        const pathBuy = [BSC_Token_CONTRACT, tokenAddress]
        const pathSell = [tokenAddress, BSC_Token_CONTRACT]
        const amountToSend = ethers.utils.parseEther('0.05');
        const minAmountOut = ethers.utils.parseEther('0.025');
        const estimatedGas = await contract.estimateGas.checkToken(
            tokenAddress,
            pathBuy,
            pathSell,
            minAmountOut,
            { value: amountToSend } // 假设发送1 ETH
        );
        console.log("Estimated Gas: ", estimatedGas.toString(), "非貔貅合约");
    } catch (error) {
        console.error("Error estimating gas: ", error);
        console.log('这是一个貔貅合约!')
    }
}
请先 登录 后评论

最佳答案 2024-05-08 07:56

我仔细看了你发的合约地址代码,暂时找到了一个靠谱的办法来屏蔽当前绝大多数的貔貅。

貔貅的合约代码,大部分都是在对transfer函数做手脚,也就是在transfer函数中调用外部合约,或者调用自己合约中的另一个函数(该函数同样调用外部合约)。既然采用你写的第一、二种方案,估算gas费和瞬时买卖都无法检测貔貅,那干脆舍弃这种思路,换另一种思路。

如果上述所说,绝大多数貔貅都是在对transfer函数动手脚,那你只需要做三个操作: 1.使用bscscan的api从链上获取到合约代码; 2.对合约代码中的transfer函数进行强匹配; 3.匹配到transfer函数后,第一,检测transfer函数中有无外部合约调用的部分,这块的检测用正则匹配进行强匹配;第二,循环检测transfer函数中包含的调用本合约中的函数中是否包含Router,pancakepair等相关重要的参数;

上述是检测貔貅合约三个操作,足以屏蔽以下类型的貔貅代币: 1.合约代码未开源的; 2.合约代码中transfer函数调用了外部合约的; 3.合约代码中不包含transfer函数的(有的貔貅合约代码会用随机字符串来代替重要的函数,以此增加普通用户的阅读难度; 4.偷偷修改Router,pancakepair等相关参数达到实际调用外部合约的貔貅合约;

同时如果还不放心,可以添加上你自己写的估算gas费的部分,也能同时屏蔽掉一部分貔貅代币。

以下是我自己测试好的代码,你可以试试:

// 从 BscScan 获取合约代码
async function getContractDetailsFromEtherscan(tokenAddress) {
  const url = `https://api.bscscan.com/api?module=contract&action=getsourcecode&address=${tokenAddress}&apikey=${bscScanApiKey}`;
  try {
      const response = await axios.get(url);
      if (response.data.status === '1' && response.data.result.length > 0) {
          const details = response.data.result[0];
          console.log('合约源代码:', details.SourceCode);
          console.log('合约ABI:', details.ABI);
          return { abi: JSON.parse(details.ABI), sourceCode: details.SourceCode };
      } else {
          throw new Error('未找到源代码或合约未经验证。');
      }
  } catch (error) {
      console.error('获取合约详情时发生错误:', error);
      throw error;
  }
}

function checkTransferFunction(sourceCode) {
  // const transferFunctionPattern = /function\s+transfer\s*\([^)]*\)\s*(public|internal|private|external)?\s*(returns\s*\([^)]*\))?\s*{/;
  const transferFunctionPattern = /function\s+transfer\s*\(\s*address\s+to,\s*uint\s+amount\s*\)\s*(public|internal|private|external)?\s*(returns\s*\([^)]*\))?\s*{/;

  // 更新的正则表达式检测非标准调用
  const externalCallPattern = /\b[A-Z][a-zA-Z0-9_]*\s*\([A-Za-z0-9_]*\)\s*\.\s*[a-zA-Z0-9_]+\s*\(/g;

  // 移除所有单行和多行注释
  let cleanedSourceCode = sourceCode.replace(/\/\/.*$/gm, '').replace(/\/\*[\s\S]*?\*\//gm, '');

  // 匹配函数实现
  let match = transferFunctionPattern.exec(cleanedSourceCode);
  if (match) {
      let functionStartIndex = match.index;
      let functionBodyStart = cleanedSourceCode.indexOf('{', functionStartIndex) + 1;
      let braceCount = 1;
      let i = functionBodyStart;
      for (; i < cleanedSourceCode.length && braceCount > 0; i++) {
          if (cleanedSourceCode[i] === '{') {
              braceCount++;
          } else if (cleanedSourceCode[i] === '}') {
              braceCount--;
          }
      }
      let functionBodyEnd = i - 1;
      let functionBody = cleanedSourceCode.substring(functionBodyStart, functionBodyEnd);

      // 检测非标准调用
      const nonStandardCalls = functionBody.match(externalCallPattern);
      let isStandardCall = true; // 默认假设所有调用都是标准的
      if (nonStandardCalls) {
          console.log('检测到非标准外部调用:', nonStandardCalls);
          isStandardCall = false; // 检测到非标准调用,设置为false
      } else {
          console.log('未检测到非标准外部调用。');
      }

      // 返回isStandardCall的状态
      return isStandardCall;
  } else {
      console.log('未找到 \'transfer\' 函数的实现。');
      return false; // 如果没有找到函数实现,也返回false
  }
}

// 主函数
async function CheckPiXiu(tokenAddress) {
  try {
      const contractDetails = await getContractDetailsFromEtherscan(tokenAddress);
      checkTransferFunction(contractDetails.sourceCode);
  } catch (error) {
      console.error('处理过程中发生错误:', error);
  }
}
请先 登录 后评论

其它 4 个回答

吉梦良
请先 登录 后评论
Jzq
请先 登录 后评论
请先 登录 后评论
今天吃什么呢
请先 登录 后评论