ZBLOG

以太坊钱包余额转账代码详解,从原理到实践

在区块链的世界里,以太坊作为最智能的合约平台之一,其代币(如ETH及各种ERC20代币)的转移是核心操作之一,对于开发者而言,掌握如何通过代码安全、有效地将钱包中的余额转出,是一项至关重要的技能,本文将详细介绍以太坊钱包余额转账的核心代码实现,涵盖基本原理、代码示例(以JavaScript和Solidity为例)以及关键注意事项。

核心原理:以太坊转账的本质

以太坊上的转账本质上是一次交易(Transaction),发起转账时,发送方(From账户)需要向以太坊网络广播一笔包含以下关键信息的交易:

  1. 接收方地址 (To Address):接收资产的地址。
  2. 转账金额 (Value):对于ETH,是以太为单位(1 ETH = 10^18 wei);对于ERC20代币,是代币的最小单位。
  3. 发送方签名 (Signature):使用发送方私钥对交易进行签名,证明该交易确实由发送方发起且未经篡改。
  4. Gas Limit:发送方愿意为这笔交易支付的最大计算量单位,用于限制交易执行的成本。
  5. Gas Price:发送方愿意为每个Gas单位支付的价格,决定了交易的优先级。
  6. nonce:发送方账户发起的交易序列号,用于防止重放攻击。

当矿工(或验证者)打包这笔交易时,会从中扣除相应的Gas费用(Gas Limit * Gas Price),然后将资产从发送方地址转移到接收方地址。

准备工作:环境与依赖

在编写代码之前,你需要准备以下工具和环境:

  1. 以太坊节点或Infura/Alchemy等服务:用于连接以太坊网络,Infura和Alchemy提供了便捷的RPC接口,适合开发测试。
  2. 钱包软件(如MetaMask):用于管理私钥、查看地址和测试。切记:不要在代码中硬编码私钥!
  3. 开发环境
    • Node.js:用于运行JavaScript代码。
    • 以太坊相关库
      • ethers.jsweb3.js:用于与以太坊交互的JavaScript库,本文主要使用ethers.js,因其更现代且API友好。
      • solc:Solidity编译器,如果需要编译Solidity合约。
  4. 测试ETH:从测试网络(如Goerli、Sepolia)获取测试ETH,用于支付Gas费用和测试转账。

代码实现:以JavaScript (ethers.js) 为例

这是最常见的情况,即从一个外部拥有账户(EOA)通过代码发送ETH或ERC20代币。

1 发送ETH转账

const ethers = require("ethers");
// 1. 配置Provider(连接到以太坊网络)
const provider = new ethers.providers.JsonRpcProvider("https://goerli.infura.io/v3/YOUR_INFURA_PROJECT_ID"); // 替换为你的Infura ID或节点URL
// 2. 创建Wallet(从私钥创建,实际应用中应从安全的地方获取,如硬件钱包、环境变量等)
// 警告:不要将私钥直接写在代码中!这里仅作示例
const privateKey = "YOUR_PRIVATE_KEY"; // 替换为发送方的私钥
const wallet = new ethers.Wallet(privateKey, provider);
// 3. 接收方地址
const recipientAddress = "0xRecipientAddressHere..."; // 替换为接收方地址
// 4. 转账金额(以ETH为单位,转换为wei)
const amountToSend = ethers.utils.parseEther("0.01"); // 转账0.01 ETH
// 5. 构建并发送交易
async function sendEthTransaction() {
    try {
        // 获取当前nonce(防止交易重复)
        const nonce = await wallet.getTransactionCount();
        // 构建交易对象
        const tx = {
            to: recipientAddress,
            value: amountToSend,
            gasLimit: ethers.utils.hexlify(21000), // ETH转账通常21000 gas
            nonce: nonce,
            chainId: 5, // Goerli测试网的chainId,主网为1
        };
        // 发送交易(会返回交易哈希)
        const txResponse = await wallet.sendTransaction(tx);
        console.log("交易已发送,哈希:", txResponse.hash);
        // 等待交易被打包
        const txReceipt = await txResponse.wait();
        console.log("交易已确认,区块号:", txReceipt.blockNumber);
        console.log("余额已转出!");
    } catch (error) {
        console.error("转账失败:", error);
    }
}
sendEthTransaction();

2 发送ERC20代币转账

ERC20代币的转账不是直接调用transfer方法到接收方地址,而是调用代币合约的transfer方法,参数为接收方地址和转账金额。

const ethers = require("ethers");
// 1. 配置Provider和Wallet(同上)
const provider = new ethers.providers.JsonRpcProvider("https://goerli.infura.io/v3/YOUR_INFURA_PROJECT_ID");
const privateKey = "YOUR_PRIVATE_KEY";
const wallet = new ethers.Wallet(privateKey, provider);
// 2. ERC20代币合约地址(以Goerli测试网的DAI为例)
const tokenAddress = "0x11fE4B6AE13d2a6055C8D9cF65c22B0E383A8687"; // 替换为你要转账的代币合约地址
// 3. 接收方地址
const recipientAddress = "0xRecipientAddressHere...";
// 4. 代币合约ABI(只需包含transfer方法)
const tokenAbi = [
    "function transfer(address to, uint amount) returns (bool)",
    "function balanceOf(address account) view returns (uint)",
    "function decimals() view returns (uint8)"
];
// 5. 创建代币合约实例
const tokenContract = new ethers.Contract(tokenAddress, tokenAbi, wallet);
// 6. 转账金额(注意代币的decimals)
const amountToSend = ethers.utils.parseUnits("100", 18); // 假设代币有18位小数,转账100个代币
async function sendErc20Transaction() {
    try {
        // 获取当前nonce
        const nonce = await wallet.getTransactionCount();
        // 调用代币合约的transfer方法
        const txResponse = await tokenContract.transfer(recipientAddress, amountToSend);
        console.log("代币转账交易已发送,哈希:", txResponse.hash);
        // 等待交易被打包
        const txReceipt = await txResponse.wait();
        console.log("代币转账交易已确认,区块号:", txReceipt.blockNumber);
        console.log("代币余额已转出!");
    } catch (error) {
        console.error("代币转账失败:", error);
    }
}
// 可选:先查询发送方代币余额
async function checkTokenBalance() {
    const balance = await tokenContract.balanceOf(wallet.address);
    console.log(`发送方 ${wallet.address} 的代币余额:`, ethers.utils.formatUnits(balance, 18));
}
checkTokenBalance().then(() => sendErc20Transaction());

Solidity 合约中的转账代码

如果你想在智能合约内部实现余额转出(合约管理员提取资金或用户提取收益),代码会略有不同。

1 合约接收ETH并允许提取

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract EtherWallet {
    address public owner;
    constructor() {
        owner = msg.sender; // 部署者成为所有者
    }
    // 接收ETH
    receive() external payable {}
    // 提取ETH到指定地址
    function withdrawEth(address payable _to, uint256 _amount) external {
        require(msg.sender == owner, "Only owner can withdraw");
        require(address(this).balance >= _amount, "Insufficient balance");
        // 转账ETH
        (bool success, ) = _to.call{value: _amount}("");
        require(success, "ETH transfer failed");
    }
    // 查询合约ETH余额
    function getBalance() external view returns (uint256) {
        return address(this).balance;
    }
}

2 合约内部转账ERC20代币

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract TokenWallet {
    address public owner;
    constructor() {
        owner = msg.sender;
    }
    // 提取ERC20代币到指定地址
    function withdrawErc20(address _tokenAddress, address _to, uint256 _amount) external {
        require(msg.sender ==
分享:
扫描分享到社交APP