随着区块链技术的飞速发展,以太坊作为最受欢迎的智能合约平台之一,其应用场景日益广泛,对于许多基于 Web 的区块链项目而言,后端服务器(如 PHP 应用)需要与以太坊网络进行交互,例如查询账户余额、发送交易、调用智能合约等,本文将详细介绍如何使用 PHP 接入以太坊钱包,实现与以太坊网络的通信。

为什么选择 PHP 接入以太坊?

PHP 作为一种广泛使用的服务器端脚本语言,拥有庞大的开发者社区和成熟的生态系统,将 PHP 与以太坊结合,可以利用 PHP 快速开发 Web 应用,并通过后端安全地管理以太坊相关的操作,

  • 用户资产管理:查询用户钱包余额、代币余额。
  • 交易处理:代用户发送 ETH 或 ERC20 代币。
  • 智能合约交互:调用智能合约的读/写函数。
  • 支付集成:实现基于以太坊的支付网关。

核心工具选择:Web3.php

要在 PHP 中与以太坊交互,最常用的库是 web3.php,这是一个用 PHP 实现的以太坊 JSON-RPC 客户端,它封装了与以太坊节点(如 Geth, Parity)或第三方服务(如 Infura, Alchemy)通信的细节,使得开发者可以方便地调用以太坊的各种功能。

安装 web3.php

你需要通过 Composer 来安装 web3.php,在你的项目根目录下运行以下命令:

composer require sc0vu/web3.php

配置以太坊节点或 RPC 服务

你需要连接到一个以太坊节点,这有几种方式:

  • 运行本地节点:在自己的服务器上运行 Geth 或 Parity 节点,优点是数据完全私有可控,缺点是对服务器配置要求较高,同步区块数据需要大量时间和存储空间。
  • 使用第三方 RPC 服务:如 Infura 或 Alchemy,它们提供稳定的云端节点服务,无需自己维护节点,适合开发和大多数生产场景,你只需要注册账号,获取一个项目 ID(Project ID)即可。

从 Infura 获取的 RPC URL 格式通常为:https://mainnet.infura.io/v3/YOUR_PROJECT_ID

PHP 接入以太坊钱包实战

下面我们通过几个常见的场景来展示如何使用 web3.php

连接以太坊网络并获取版本信息

这是最基础的验证连接是否成功的步骤。

<?php
require 'vendor/autoload.php';
use Web3\Web3;
use Web3\Providers\HttpProvider;
use Web3\RequestManagers\HttpManager;
// 替换为你的以太坊节点 RPC URL
$rpcUrl = 'https://mainnet.infura.io/v3/YOUR_PROJECT_ID';
$provider = new HttpProvider(new HttpManager($rpcUrl));
$web3 = new Web3($provider);
try {
    $web3->getVersion()->then(
        function ($version) {
            echo "以太坊客户端版本: " . $version . PHP_EOL;
        },
        function ($error) {
            echo "获取版本失败: " . $error->getMessage() . PHP_EOL;
        }
    );
} catch (Exception $e) {
    echo "连接错误: " . $e->getMessage() . PHP_EOL;
}
?>

查询账户余额

假设你想查询某个以太坊地址的 ETH 余额。

<?php
require 'vendor/autoload.php';
use Web3\Web3;
use Web3\Providers\HttpProvider;
use Web3\RequestManagers\HttpManager;
use Web3\Utils;
$rpcUrl = 'https://mainnet.infura.io/v3/YOUR_PROJECT_ID';
$provider = new HttpProvider(new HttpManager($rpcUrl));
$web3 = new Web3($provider);
// 要查询的地址
$address = '0x742d35Cc6634C0532925a3b844Bc454e4438f44e';
try {
    $web3->eth->getBalance($address, function ($err, $balance) {
        if ($err !== null) {
            echo "查询余额失败: " . $err->getMessage() . PHP_EOL;
            return;
        }
        // 余额是 Wei 单位,需要转换为 Ether
        $etherBalance = Utils::fromWei($balance, 'ether');
        echo "地址 {$address} 的余额: " . $etherBalance . " ETH" . PHP_EOL;
    });
} catch (Exception $e) {
    echo "连接错误: " . $e->getMessage() . PHP_EOL;
}
?>

发送 ETH 交易(需要钱包私钥)

发送交易是更复杂的操作,需要发送方的私钥对交易进行签名。请务必注意安全,不要在生产环境中硬编码私钥! 建议使用环境变量或安全的密钥管理系统。

<?php
require 'vendor/autoload.php';
use Web3\Web3;
use Web3\Providers\HttpProvider;
use Web3\RequestManagers\HttpManager;
use Web3\Utils;
use Web3\Contract;
use Web3 personal;
$rpcUrl = 'https://mainnet.infura.io/v3/YOUR_PROJECT_ID';
$provider = new HttpProvider(new HttpManager($rpcUrl));
$web3 = new Web3($provider);
// 发送方私钥(仅用于示例,实际使用请妥善保管)
$privateKey = 'YOUR_PRIVATE_KEY_HERE';
// 发送方地址
$fromAddress = '0xYourFromAddress';
// 接收方地址
$toAddress = '0x742d35Cc6634C0532925a3b844Bc454e4438f44e';
// 发送金额(ETH)
$amount = '0.01';
// Gas 价格 (Gwei)
$gasPrice = '20';
// Gas 限制
$gasLimit = '21000';
try {
    // 1. 获取当前 nonce
    $web3->eth->getTransactionCount($fromAddress, 'latest', function ($err, $nonce) use ($web3, $privateKey, $toAddress, $amount, $gasPrice, $gasLimit) {
        if ($err !== null) {
            echo "获取 Nonce 失败: " . $err->getMessage() . PHP_EOL;
            return;
        }
        // 2. 创建交易对象
        $transaction = [
            'from' => $fromAddress,
            'to' => $toAddress,
            'value' => Utils::toWei($amount, 'ether'),
            'gas' => $gasLimit,
            'gasPrice' => Utils::toWei($gasPrice, 'gwei'),
            'nonce' => $nonce,
            'chainId' => 1 // 主网 chainId, 测试网如 Ropsten 是 3
        ];
        // 3. 使用私钥签名交易
        $web3->eth->sendRawTransaction($web3->eth->signTransaction($transaction, $privateKey), function ($err, $txHash) {
            if ($err !== null) {
                echo "发送交易失败: " . $err->getMessage() . PHP_EOL;
                return;
            }
            echo "交易发送成功! 交易哈希: " . $txHash . PHP_EOL;
        });
    });
} catch (Exception $e) {
    echo "发送交易错误: " . $e->getMessage() . PHP_EOL;
}
?>

与智能合约交互(以调用 ERC20 代币 transfer 为例)

与智能合约交互需要合约的 ABI (Application Binary Interface) 和地址。

<?php
require 'vendor/autoload.php';
use Web3\Web3;
use Web3\Providers\HttpProvider;
use Web3\RequestManagers\HttpManager;
use Web3\Utils;
use Web3\Contract;
$rpcUrl = 'https://mainnet.infura.io/v3/YOUR_PROJECT_ID';
$provider = new HttpProvider(new HttpManager($rpcUrl));
$web3 = new Web3($provider);
// ERC20 代币合约地址 (DAI)
$contractAddress = '0x6B175474E89094C44Da98b954EedeAC495271d0F';
// 代币 ABI (简化版,实际需要完整的 ABI)
$abi = '[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"}]';
// 调用 transfer 函数的参数
$toAddress = '0xRecipientAddress';
$tokenAmount = '1000000000000000000'; // 1 代币,根据代币精度调整
try {
    $contract = new Contract($provider, $abi);
    $contract->at($contractAddress