You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何使用PHP实现ERC20代币在不同账户间的转账?

Alright, let's tackle how to work with ERC20 tokens using PHP without any Node.js or JavaScript dependencies. All you need is a connection to an Ethereum node (like a local Geth/Parity instance or a service like Infura) and some PHP code to handle JSON-RPC calls and transaction signing. Here's a step-by-step breakdown of the most common operations:

Prerequisites
  • A running Ethereum node (or access to a hosted node service)
  • Basic understanding of ERC20 standard method signatures
  • For transaction signing: Install these PHP packages via Composer:
    composer require kornrunner/keccak simplito/elliptic-php
    

1. Fetch ERC20 Contract Basic Info (name, symbol, decimals, totalSupply)

All compliant ERC20 contracts expose these read-only view functions. We'll use eth_call to query them without writing to the blockchain.

Example: Get Token Name

<?php
$nodeUrl = "https://mainnet.infura.io/v3/YOUR_PROJECT_ID"; // Replace with your node URL
$contractAddress = "0x6B175474E89094C44Da98b954EedeAC495271d0F"; // DAI contract example

// ERC20 `name` method signature hash: 0x06fdde03
$payload = [
    "jsonrpc" => "2.0",
    "method" => "eth_call",
    "params" => [
        ["to" => $contractAddress, "data" => "0x06fdde03"],
        "latest"
    ],
    "id" => 1
];

// Send JSON-RPC request
$ch = curl_init($nodeUrl);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Content-Type: application/json"]);
$response = curl_exec($ch);
curl_close($ch);

// Parse and format result
$result = json_decode($response, true);
$tokenName = hex2bin(substr($result['result'], 2));
echo "Token Name: " . $tokenName . "\n";
?>

Other Common Info Methods

  • Symbol: Use method signature 0x95d89b41
  • Decimals: Use method signature 0x313ce567 (returns a uint8, convert hex to decimal directly)
  • Total Supply: Use method signature 0x18160ddd (returns uint256, convert hex to decimal then divide by decimals for human-readable value)

2. Check an Address's ERC20 Balance

Use the balanceOf method to query how many tokens an address holds.

<?php
$nodeUrl = "https://mainnet.infura.io/v3/YOUR_PROJECT_ID";
$contractAddress = "0x6B175474E89094C44Da98b954EedeAC495271d0F";
$holderAddress = "0xYourTargetAddressHere";

// Encode address to 64-character hex (left-pad with zeros)
$encodedAddress = str_pad(substr($holderAddress, 2), 64, '0', STR_PAD_LEFT);
// `balanceOf` method signature + encoded address
$data = "0x70a08231" . $encodedAddress;

$payload = [
    "jsonrpc" => "2.0",
    "method" => "eth_call",
    "params" => [["to" => $contractAddress, "data" => $data], "latest"],
    "id" => 1
];

// Send request and parse
$ch = curl_init($nodeUrl);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Content-Type: application/json"]);
$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response, true);
$rawBalance = hexdec($result['result']);
$decimals = 18; // Replace with actual token decimals
$humanReadableBalance = $rawBalance / (10 ** $decimals);

echo "Balance: " . $humanReadableBalance . " DAI\n";
?>

3. Send ERC20 Tokens to Another Address

This requires signing a transaction with your private key and broadcasting it to the network. It's a write operation, so you'll need to pay gas fees in ETH.

<?php
require_once 'vendor/autoload.php';

use Elliptic\EC;
use kornrunner\Keccak;

$nodeUrl = "https://mainnet.infura.io/v3/YOUR_PROJECT_ID";
$contractAddress = "0x6B175474E89094C44Da98b954EedeAC495271d0F";
$fromAddress = "0xYourWalletAddress";
$privateKey = "YOUR_PRIVATE_KEY_WITHOUT_0X_PREFIX";
$toAddress = "0xRecipientAddress";
$amount = 1 * (10 ** 18); // 1 DAI (adjust for token decimals)

// Step 1: Get transaction nonce
$payloadNonce = [
    "jsonrpc" => "2.0",
    "method" => "eth_getTransactionCount",
    "params" => [$fromAddress, "pending"],
    "id" => 1
];
$ch = curl_init($nodeUrl);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payloadNonce));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Content-Type: application/json"]);
$nonce = hexdec(json_decode(curl_exec($ch), true)['result']);

// Step 2: Get current gas price
$payloadGasPrice = ["jsonrpc" => "2.0", "method" => "eth_gasPrice", "params" => [], "id" => 1];
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payloadGasPrice));
$gasPrice = hexdec(json_decode(curl_exec($ch), true)['result']);

// Step 3: Encode transfer data
$encodedTo = str_pad(substr($toAddress, 2), 64, '0', STR_PAD_LEFT);
$encodedAmount = str_pad(dechex($amount), 64, '0', STR_PAD_LEFT);
$data = "0xa9059cbb" . $encodedTo . $encodedAmount; // `transfer` method signature

// Step 4: Build transaction structure
$tx = [
    'nonce' => '0x' . dechex($nonce),
    'gasPrice' => '0x' . dechex($gasPrice),
    'gasLimit' => '0x' . dechex(210000), // Safe gas limit for ERC20 transfers
    'to' => $contractAddress,
    'value' => '0x0', // No ETH needed for ERC20 transfers
    'data' => $data,
    'chainId' => 1 // Mainnet=1, Goerli=5, Sepolia=11155111
];

// Step 5: Sign transaction
$ec = new EC('secp256k1');
$key = $ec->keyFromPrivate($privateKey, 'hex');
$txRLP = serializeTransaction($tx);
$hash = Keccak::hash(hex2bin($txRLP), 256);
$signature = $key->sign($hash, ['canonical' => true]);

$r = str_pad($signature->r->toString('hex'), 64, '0', STR_PAD_LEFT);
$s = str_pad($signature->s->toString('hex'), 64, '0', STR_PAD_LEFT);
$v = dechex($signature->recoveryParam + 35 + $tx['chainId'] * 2);
$signedTx = '0x' . $txRLP . $r . $s . $v;

// Step 6: Broadcast transaction
$payloadSendTx = [
    "jsonrpc" => "2.0",
    "method" => "eth_sendRawTransaction",
    "params" => [$signedTx],
    "id" => 1
];
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payloadSendTx));
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

if (isset($response['result'])) {
    echo "Transaction sent! Hash: " . $response['result'] . "\n";
} else {
    echo "Error: " . $response['error']['message'] . "\n";
}

// Helper: RLP serialize transaction
function serializeTransaction($tx) {
    $elements = [
        hex2bin(substr($tx['nonce'], 2)),
        hex2bin(substr($tx['gasPrice'], 2)),
        hex2bin(substr($tx['gasLimit'], 2)),
        hex2bin(substr($tx['to'], 2)),
        hex2bin(substr($tx['value'], 2)),
        hex2bin(substr($tx['data'], 2)),
        hex2bin(dechex($tx['chainId'])),
        hex2bin('00'),
        hex2bin('00')
    ];
    return rlpEncode($elements);
}

function rlpEncode($input) {
    if (is_string($input)) {
        if (strlen($input) == 1 && ord($input) < 0x80) return $input;
        return encodeLength(strlen($input), 0x80) . $input;
    } elseif (is_array($input)) {
        $output = '';
        foreach ($input as $item) $output .= rlpEncode($item);
        return encodeLength(strlen($output), 0xc0) . $output;
    }
    throw new Exception('Invalid input type');
}

function encodeLength($length, $offset) {
    if ($length < 0x3f) return chr($offset + $length);
    elseif ($length <= 0xff) return chr($offset + 0x80 + 1) . chr($length);
    elseif ($length <= 0xffff) return chr($offset + 0x80 + 2) . pack('n', $length);
    elseif ($length <= 0xffffffff) return chr($offset + 0x80 + 4) . pack('N', $length);
    throw new Exception('Length too large');
}
?>

内容的提问来源于stack exchange,提问作者Furqan Siddiqui

火山引擎 最新活动