Reputation: 11
I'm encountering an issue while trying to send a transaction using the Web3PHP library. Here is the error am getting:
An error occurred: Error sending transaction: unmarshal transaction failed
This is just a test code but the error is all am getting, please if anyone could help m on this. Am expecting the code to sign the user wallet, then the money/transfer should complete successfully.
here is a .env file used:
ENCRYPTION_KEY=<private>
PRIVATE_KEY=<private>
BSC_RPC_URL=https://bsc-dataseed.binance.org/
CHAIN_ID=56
USDT_CONTRACT=0x55d398326f99059ff775485246999027b3197955
USDT_CONTRACT_ADDRESS=0x55d398326f99059fF775485246999027B3197955
ETH_CONTRACT_ADDRESS=0x2170Ed0880ac9A755fd29B2688956BD959F933F8
ADMIN_PRIVATE_KEY=<private>
ADMIN_WALLET=<private>```
and here is the code which keep giving me errror, please help check it:
<?php
// Enable error reporting
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
session_start();
require_once '../db.php';
require_once '../vendor/autoload.php';
use Web3\Utils;
use kornrunner\Keccak;
use kornrunner\RLP\RLP;
use Elliptic\EC;
// Function to decrypt private key
function decryptPrivateKey($encryptedKeyWithIv, $encryptionKey) {
$data = base64_decode($encryptedKeyWithIv);
$iv = substr($data, 0, 16);
$encryptedKey = substr($data, 16);
return openssl_decrypt($encryptedKey, 'AES-256-CBC', $encryptionKey, 0, $iv);
}
// Check admin session
if (!isset($_SESSION['admin_id'])) {
header("Location: admin-login.php");
exit;
}
// Load environment variables
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__ . '/../');
$dotenv->load();
$rpcUrl = $_ENV['BSC_RPC_URL'];
$usdtContractAddress = $_ENV['USDT_CONTRACT_ADDRESS'];
$chainId = intval($_ENV['CHAIN_ID'] ?? 56);
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
try {
$user_id = intval($_POST['user_id']);
$amount = floatval($_POST['amount']);
$receiver_address = $_POST['receiver_address'];
if (!$user_id || !$amount || !$receiver_address) {
throw new Exception("All fields are required.");
}
$database = new Database();
$db = $database->connect();
$query = $db->prepare("SELECT private_key, usdt_address FROM users WHERE id = :user_id");
$query->bindParam(':user_id', $user_id);
$query->execute();
$user = $query->fetch(PDO::FETCH_ASSOC);
if (!$user) {
throw new Exception("User not found.");
}
$encryptionKey = $_ENV['ENCRYPTION_KEY'];
$decryptedPrivateKey = decryptPrivateKey($user['private_key'], $encryptionKey);
// Log decrypted private key for debugging
error_log("Decrypted Private Key for User ID {$user_id}: " . $decryptedPrivateKey);
if (!$decryptedPrivateKey) {
throw new Exception("Failed to decrypt private key.");
}
$userWallet = $user['usdt_address'];
$amountInWei = Utils::toWei((string)$amount, 'mwei');
$gasPrice = Utils::toHex('5000000000', true); // Example gas price (5 Gwei)
$gasLimit = Utils::toHex('60000', true);
// Fetch nonce
$nonceResponse = file_get_contents($rpcUrl, false, stream_context_create([
'http' => [
'method' => 'POST',
'header' => 'Content-Type: application/json',
'content' => json_encode([
'jsonrpc' => '2.0',
'method' => 'eth_getTransactionCount',
'params' => [$userWallet, 'latest'],
'id' => 1,
]),
],
]));
$nonceData = json_decode($nonceResponse, true);
if (isset($nonceData['error'])) {
throw new Exception("Error fetching nonce: " . $nonceData['error']['message']);
}
$nonce = Utils::toHex(hexdec($nonceData['result']), true);
// Prepare transaction data
$methodId = substr(Keccak::hash("transfer(address,uint256)", 256), 0, 8);
$encodedReceiver = str_pad(substr($receiver_address, 2), 64, '0', STR_PAD_LEFT);
$encodedAmount = str_pad(Utils::toHex($amountInWei, false), 64, '0', STR_PAD_LEFT);
$data = '0x' . $methodId . $encodedReceiver . $encodedAmount;
$transaction = [
'nonce' => $nonce,
'gasPrice' => $gasPrice,
'gasLimit' => $gasLimit,
'to' => $usdtContractAddress,
'value' => '0x0',
'data' => $data,
'chainId' => $chainId,
];
// Encode and sign the transaction
$rlp = new RLP();
$ec = new EC('secp256k1');
$key = $ec->keyFromPrivate($decryptedPrivateKey);
$rawTx = [
$transaction['nonce'],
$transaction['gasPrice'],
$transaction['gasLimit'],
$transaction['to'],
$transaction['value'],
$transaction['data'],
Utils::toHex($transaction['chainId'], true),
'0x',
'0x',
];
$encodedTx = $rlp->encode($rawTx);
$msgHash = Keccak::hash(hex2bin($encodedTx), 256);
$signature = $key->sign($msgHash, ['canonical' => true]);
$rawTx[6] = Utils::toHex($signature->recoveryParam + $chainId * 2 + 35, true);
$rawTx[7] = '0x' . $signature->r->toString(16);
$rawTx[8] = '0x' . $signature->s->toString(16);
$signedTx = '0x' . $rlp->encode($rawTx);
// Log raw signed transaction for debugging
error_log("Signed Transaction: " . $signedTx);
// Send raw transaction
$txResponse = file_get_contents($rpcUrl, false, stream_context_create([
'http' => [
'method' => 'POST',
'header' => 'Content-Type: application/json',
'content' => json_encode([
'jsonrpc' => '2.0',
'method' => 'eth_sendRawTransaction',
'params' => [$signedTx],
'id' => 1,
]),
],
]));
$txData = json_decode($txResponse, true);
if (isset($txData['error'])) {
throw new Exception("Error sending transaction: " . $txData['error']['message']);
}
$txHash = $txData['result'];
// Log successful transaction
$query = $db->prepare("
INSERT INTO processed_transactions (tx_hash, user_id, amount, wallet_address, created_at)
VALUES (:tx_hash, :user_id, :amount, :receiver_address, NOW())
");
$query->bindParam(':tx_hash', $txHash);
$query->bindParam(':user_id', $user_id);
$query->bindParam(':amount', $amount);
$query->bindParam(':receiver_address', $receiver_address);
$query->execute();
echo "Transaction sent successfully. Hash: " . $txHash;
} catch (Exception $e) {
error_log($e->getMessage());
echo "An error occurred: " . $e->getMessage();
}
} else {
header("Location: admin-send-wallet.php");
exit;
}
I am expecting the transaction to be signed and then blockchain account authorized then the transfer should complete.
Upvotes: 1
Views: 53