NaijaPalaba Studio
NaijaPalaba Studio

Reputation: 11

Why do I get "Error sending transaction: unmarshal transaction failed" when using the Web3PHP library?

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

Answers (0)

Related Questions