批量转账

Web3博主,职业撸空投。不闻窗外事,退役程序员。

Mirror笔记链接 : Mirror笔记

打赏地址EVM:xuruojing.eth

电报群聊链接:https://t.me/+MLLv_VeuhuhlNTJl…

推特地址:https://x.com/xuruojing

欧意注册链接:https://ouxyi.club/join/99890710

币安注册链接:https://accounts.binance.com/register?ref=56353246…

独立IP购买链接:https://bp.byteplus.pro

撸空投,如果是大量的撸空投,会遇到一个问题,除了大量从cex提币到链上,还有一个问题是从链上提币到交易所。今天教程说下怎么批量提币到交易所,同时也是批量转账的教程。

1,前提准备

需要在本地安装对应的环境

npm install ethers dotenv   //在控制台执行这行脚本,这是安装ethers的代码包

安装node请看上一篇教程。

2,私钥和目标地址

我使用的是本地的txt文件,一个文件名字叫 source.txt,另一个文件名字叫target.txt,前者存的是私钥,后者存的是地址。

代码的功能是:

source.txt文件里第一行的私钥对应的地址里的ETH转到target.txt的第一行的地址

source.txt文件里第二行的私钥对应的地址里的ETH转到target.txt的第二行的地址

source.txt文件里第三行的私钥对应的地址里的ETH转到target.txt的第三行的地址

source.txt示例:

target.txt示例:

注意:每一行后面不能有空格,最后一行之后不能转行。source.txt和target.txt的行数要一样。否则会爆异常

3,转账代码

require("dotenv").config();
const { ethers } = require("ethers");
const fs = require("fs").promises;

async function readFileLines(filePath) {
  try {
    const content = await fs.readFile(filePath, "utf8");
    return content
      .split("\n")
      .map((line) => line.trim())
      .filter((line) => line);
  } catch (error) {
    console.error(`读取文件 ${filePath} 失败:`, error.message);
    throw error;
  }
}

async function transferAllETH(fromPrivateKey, toAddress, index) {
  const rpcUrl = "https://mainnet.base.org";
  const provider = new ethers.JsonRpcProvider(rpcUrl);
  const wallet = new ethers.Wallet(fromPrivateKey, provider);

  try {
    const balance = await provider.getBalance(wallet.address);
    console.log(`\n[转账对 ${index + 1}]`);
    console.log("从钱包地址:", wallet.address);
    console.log("转到地址:", toAddress);
    console.log("原始余额:", ethers.formatEther(balance), "ETH");

    if (balance === BigInt(0)) {
      console.log("钱包余额为0,跳过转账");
      return;
    }

    const gasPrice = await provider.getFeeData();
    const gasLimit = 21000;

    const estimatedGasCost = gasPrice.maxFeePerGas * BigInt(gasLimit);
    const gasCostWithBuffer = (estimatedGasCost * BigInt(110)) / BigInt(100);

    const transferAmount = balance - gasCostWithBuffer;

    console.log("预估gas成本:", ethers.formatEther(estimatedGasCost), "ETH");
    console.log(
      "带buffer的gas成本:",
      ethers.formatEther(gasCostWithBuffer),
      "ETH"
    );
    console.log("计划转账金额:", ethers.formatEther(transferAmount), "ETH");

    if (transferAmount <= 0) {
      throw new Error("余额不足支付gas费");
    }

    const tx = {
      to: toAddress,
      value: transferAmount,
      gasLimit: gasLimit,
      maxFeePerGas: gasPrice.maxFeePerGas,
      maxPriorityFeePerGas: gasPrice.maxPriorityFeePerGas,
    };

    const transaction = await wallet.sendTransaction(tx);
    console.log("交易已发送,交易哈希:", transaction.hash);

    const receipt = await transaction.wait();
    console.log("交易已确认,区块号:", receipt.blockNumber);
    return true;
  } catch (error) {
    console.error(`转账对 ${index + 1} 失败:`, error.message);
    return false;
  }
}

// 添加一个生成随机延迟的函数
function getRandomDelay() {
  const min = 100; // 最小延迟100秒
  const max = 1000; // 最大延迟1000秒
  return Math.floor(Math.random() * (max - min + 1) + min) * 1000; // 转换为毫秒
}

async function batchTransfer() {
  try {
    const sourcePrivateKeys = await readFileLines("./source.txt");
    const targetAddresses = await readFileLines("./target.txt");

    if (sourcePrivateKeys.length !== targetAddresses.length) {
      throw new Error("源私钥文件和目标地址文件的行数不匹配!");
    }

    console.log(`总共发现 ${sourcePrivateKeys.length} 个转账任务`);

    const results = {
      success: 0,
      failed: 0,
      total: sourcePrivateKeys.length,
    };

    // 创建转账任务数组
    const transferTasks = sourcePrivateKeys.map((privateKey, index) => {
      return async () => {
        try {
          // 执行转账
          const success = await transferAllETH(
            privateKey,
            targetAddresses[index],
            index
          );

          if (success) {
            results.success++;
          } else {
            results.failed++;
          }

          // 如果不是最后一个转账,添加随机延迟
          if (index < sourcePrivateKeys.length - 1) {
            const delay = getRandomDelay();
            const delaySeconds = delay / 1000;
            console.log(`\n等待 ${delaySeconds} 秒后执行下一个转账...`);
            await new Promise((resolve) => setTimeout(resolve, delay));
          }
        } catch (error) {
          console.error(`任务 ${index + 1} 执行失败:`, error.message);
          results.failed++;
        }
      };
    });

    // 串行执行所有任务
    for (const task of transferTasks) {
      await task();
    }

    // 打印最终结果
    console.log("\n批量转账完成!");
    console.log(`总任务数: ${results.total}`);
    console.log(`成功: ${results.success}`);
    console.log(`失败: ${results.failed}`);
  } catch (error) {
    console.error("批量转账过程中发生错误:", error.message);
  }
}

// 执行批量转账
batchTransfer()
  .then(() => console.log("批量转账程序执行完毕"))
  .catch((error) => console.error("程序执行失败:", error));

代码解释:

  • 每次转账都会间隔随机时间,代码示例里是100秒-1000秒,你可以修改,修改

      const min = 100; // 最小延迟100秒
      const max = 1000; // 最大延迟1000秒
    

    把这里的数字修改成自己需要的就行。但是上面的数字必须比下面的数字大。

  • 每次转账都是把base链的所有ETH转走。我目前的逻辑是计算gas(21000),转账金额是钱包余额-gas*1.1倍。避免出现gas不够的情况。

执行脚本之后的的输出日志是这样的:

输出日志
输出日志