PHP项目Ethereum与ENS域名

wen PHP项目 2

本文目录导读:

PHP项目Ethereum与ENS域名

  1. 基础环境准备
  2. Ethereum 基础交互
  3. ENS域名服务集成
  4. 完整使用示例
  5. 错误处理和最佳实践
  6. 配置文件
  7. 注意事项

我将为您详细介绍PHP项目中如何集成Ethereum和ENS(以太坊域名服务)。

基础环境准备

安装依赖

composer require web3p/web3.php
composer require kornrunner/keccak
composer require phpseclib/phpseclib

Ethereum 基础交互

连接以太坊节点

<?php
require 'vendor/autoload.php';
use Web3\Web3;
use Web3\Providers\HttpProvider;
use Web3\RequestManagers\HttpRequestManager;
class EthereumClient
{
    private $web3;
    public function __construct($rpcUrl = 'https://mainnet.infura.io/v3/YOUR_PROJECT_ID')
    {
        $requestManager = new HttpRequestManager($rpcUrl);
        $provider = new HttpProvider($requestManager);
        $this->web3 = new Web3($provider);
    }
    // 获取账户余额
    public function getBalance($address)
    {
        $balance = '0';
        $this->web3->eth->getBalance($address, function($err, $result) use (&$balance) {
            if ($err !== null) {
                throw new Exception($err->getMessage());
            }
            $balance = $result->toString();
        });
        // 转换为ETH单位
        $ethBalance = bcdiv($balance, '1000000000000000000', 18);
        return $ethBalance;
    }
    // 获取最新区块号
    public function getBlockNumber()
    {
        $blockNumber = '0';
        $this->web3->eth->blockNumber(function($err, $result) use (&$blockNumber) {
            if ($err !== null) {
                throw new Exception($err->getMessage());
            }
            $blockNumber = $result->toString();
        });
        return $blockNumber;
    }
}

发送交易

<?php
class TransactionManager
{
    private $web3;
    private $account;
    public function __construct($privateKey, $rpcUrl)
    {
        // 初始化Web3连接
        $requestManager = new HttpRequestManager($rpcUrl);
        $provider = new HttpProvider($requestManager);
        $this->web3 = new Web3($provider);
        // 从私钥导入账户
        $this->account = new \Web3\Account();
        $this->account->privateKey = $privateKey;
    }
    public function sendETH($toAddress, $amountInETH)
    {
        $amountInWei = bcmul($amountInETH, '1000000000000000000', 0);
        $transaction = [
            'from' => $this->account->getAddress(),
            'to' => $toAddress,
            'value' => '0x' . dechex($amountInWei),
            'gas' => '0x5208', // 21000 gas
            'gasPrice' => '0x' . dechex($this->getGasPrice())
        ];
        $txHash = '';
        $this->web3->eth->sendTransaction($transaction, function($err, $result) use (&$txHash) {
            if ($err !== null) {
                throw new Exception($err->getMessage());
            }
            $txHash = $result;
        });
        return $txHash;
    }
    private function getGasPrice()
    {
        $gasPrice = '0';
        $this->web3->eth->gasPrice(function($err, $result) use (&$gasPrice) {
            if ($err !== null) {
                throw new Exception($err->getMessage());
            }
            $gasPrice = $result->toString();
        });
        return $gasPrice;
    }
}

ENS域名服务集成

ENS域名查询

<?php
use Web3\Web3;
use Web3\Contracts\Eth;
use Web3\Utils;
class ENSManager
{
    private $web3;
    private $ensContract;
    // ENS 合约地址
    const ENS_REGISTRY_ADDRESS = '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e';
    const ENS_RESOLVER_ADDRESS = '0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41';
    public function __construct($rpcUrl)
    {
        $requestManager = new HttpRequestManager($rpcUrl);
        $provider = new HttpProvider($requestManager);
        $this->web3 = new Web3($provider);
        // 初始化ENS合约
        $this->initENSContract();
    }
    private function initENSContract()
    {
        // ENS Registry ABI (简化版)
        $ensAbi = json_decode('[
            {
                "constant": true,
                "inputs": [{"name": "node", "type": "bytes32"}],
                "name": "resolver",
                "outputs": [{"name": "", "type": "address"}],
                "type": "function"
            }
        ]');
        $this->ensContract = new Eth(
            $this->web3->provider,
            $ensAbi,
            self::ENS_REGISTRY_ADDRESS
        );
    }
    // 解析ENS域名
    public function resolveName($domainName)
    {
        // 将域名转换为namehash
        $nameHash = $this->namehash($domainName);
        // 获取解析器地址
        $resolverAddress = $this->getResolver($nameHash);
        if ($resolverAddress === '0x0000000000000000000000000000000000000000') {
            throw new Exception("No resolver found for domain");
        }
        // 获取解析的地址
        return $this->getAddressFromResolver($resolverAddress, $nameHash);
    }
    // 反向解析:地址转ENS域名
    public function reverseResolve($address)
    {
        // 构建反向查询的namehash
        $reverseNode = $this->namehash(
            strtolower(substr($address, 2)) . '.addr.reverse'
        );
        // 获取解析器
        $resolverAddress = $this->getResolver($reverseNode);
        if ($resolverAddress === '0x0000000000000000000000000000000000000000') {
            return null;
        }
        // 获取ENS名称
        return $this->getNameFromResolver($resolverAddress, $reverseNode);
    }
    // 计算namehash
    private function namehash($name)
    {
        if (empty($name)) {
            return '0x0000000000000000000000000000000000000000000000000000000000000000';
        }
        $labels = explode('.', $name);
        $node = '0x0000000000000000000000000000000000000000000000000000000000000000';
        foreach ($labels as $label) {
            $labelHash = Utils::sha3($label);
            $node = Utils::sha3($node . $labelHash);
        }
        return $node;
    }
    // 获取解析器
    private function getResolver($nameHash)
    {
        $resolver = '0x0000000000000000000000000000000000000000';
        $this->ensContract->call('resolver', $nameHash, function($err, $result) use (&$resolver) {
            if ($err !== null) {
                throw new Exception($err->getMessage());
            }
            $resolver = $result[0];
        });
        return $resolver;
    }
    // 从解析器获取地址
    private function getAddressFromResolver($resolverAddress, $nameHash)
    {
        // 解析器ABI (简化版)
        $resolverAbi = json_decode('[
            {
                "constant": true,
                "inputs": [{"name": "node", "type": "bytes32"}],
                "name": "addr",
                "outputs": [{"name": "", "type": "address"}],
                "type": "function"
            }
        ]');
        $resolver = new Eth(
            $this->web3->provider,
            $resolverAbi,
            $resolverAddress
        );
        $address = '0x0000000000000000000000000000000000000000';
        $resolver->call('addr', $nameHash, function($err, $result) use (&$address) {
            if ($err !== null) {
                throw new Exception($err->getMessage());
            }
            $address = $result[0];
        });
        return $address;
    }
}

完整使用示例

<?php
require 'vendor/autoload.php';
class EthereumENSExample
{
    private $ethClient;
    private $ensManager;
    private $txManager;
    public function __construct()
    {
        $rpcUrl = 'https://mainnet.infura.io/v3/YOUR_PROJECT_ID';
        $this->ethClient = new EthereumClient($rpcUrl);
        $this->ensManager = new ENSManager($rpcUrl);
    }
    public function demoENSResolver()
    {
        try {
            // 解析ENS域名
            $domain = 'vitalik.eth';
            $resolvedAddress = $this->ensManager->resolveName($domain);
            echo "Domain: {$domain}\n";
            echo "Resolved Address: {$resolvedAddress}\n";
            // 获取余额
            $balance = $this->ethClient->getBalance($resolvedAddress);
            echo "Balance: {$balance} ETH\n";
            // 反向解析
            $reverseName = $this->ensManager->reverseResolve($resolvedAddress);
            if ($reverseName) {
                echo "Reverse Resolution: {$reverseName}\n";
            }
        } catch (Exception $e) {
            echo "Error: " . $e->getMessage() . "\n";
        }
    }
}
// 运行示例
$example = new EthereumENSExample();
$example->demoENSResolver();

错误处理和最佳实践

<?php
class EthereumService
{
    private $cache;
    public function __construct()
    {
        // 使用缓存减少RPC调用
        $this->cache = new \Redis();
    }
    public function safeResolveName($domainName)
    {
        // 缓存检查
        $cacheKey = "ens_resolve_{$domainName}";
        $cachedResult = $this->cache->get($cacheKey);
        if ($cachedResult !== false) {
            return $cachedResult;
        }
        try {
            $address = $this->ensManager->resolveName($domainName);
            // 缓存结果(缓存1小时)
            $this->cache->setex($cacheKey, 3600, $address);
            return $address;
        } catch (Exception $e) {
            // 记录错误日志
            error_log("ENS resolution failed: " . $e->getMessage());
            throw $e;
        }
    }
    public function batchResolveNames(array $domainNames)
    {
        $results = [];
        foreach ($domainNames as $domain) {
            try {
                $results[$domain] = $this->safeResolveName($domain);
            } catch (Exception $e) {
                $results[$domain] = null;
            }
        }
        return $results;
    }
}

配置文件

<?php
// config/ethereum.php
return [
    'rpc_url' => env('ETHEREUM_RPC_URL', 'https://mainnet.infura.io/v3/YOUR_PROJECT_ID'),
    'chain_id' => env('ETHEREUM_CHAIN_ID', 1), // 1: Mainnet, 4: Rinkeby, 5: Goerli
    'gas_limit' => env('ETHEREUM_GAS_LIMIT', 21000),
    'gas_price_multiplier' => env('ETHEREUM_GAS_PRICE_MULTIPLIER', 1.1),
    'ens' => [
        'enabled' => true,
        'cache_ttl' => 3600, // 缓存时间(秒)
        'default_resolver' => '0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41'
    ]
];

注意事项

  1. 安全考虑

    • 永远不要在代码中硬编码私钥
    • 使用环境变量或安全的密钥管理服务
    • 限制RPC调用的频率和数量
  2. 性能优化

    • 实现缓存机制减少RPC调用
    • 使用批量查询减少网络开销
    • 考虑使用WebSocket连接实时数据
  3. 错误处理

    • 妥善处理网络超时
    • 验证输入数据的有效性
    • 记录详细的错误日志
  4. ENS相关

    • ENS解析可能需要多个RPC调用
    • 考虑使用ENS子图(The Graph)提高查询效率
    • 注意ENS域名的过期和更新

这个实现提供了PHP项目中集成Ethereum和ENS的基础框架,您可以根据实际需求进行扩展和优化。

抱歉,评论功能暂时关闭!