大佬们好,我在网上找到了一个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('这是一个貔貅合约!')
}
}
我仔细看了你发的合约地址代码,暂时找到了一个靠谱的办法来屏蔽当前绝大多数的貔貅。
貔貅的合约代码,大部分都是在对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);
}
}