Installation:
composer require farbcode/laravel-evm
php artisan evm:install
This publishes the config file (config/evm.php) and creates a migration for transaction records.
Configuration:
Update config/evm.php with your Ethereum node provider (e.g., Infura, Alchemy) and private key:
'providers' => [
'mainnet' => [
'url' => env('ETHEREUM_NODE_URL'),
'private_key' => env('ETHEREUM_PRIVATE_KEY'),
],
],
First Use Case: Send a test transaction via a controller:
use Farbcode\Evm\Facades\Evm;
public function sendTestTransaction()
{
$transaction = Evm::sendTransaction(
from: '0xFromAddress',
to: '0xToAddress',
value: 100000000000000000, // 0.1 ETH
gasLimit: 21000,
nonce: 5
);
return response()->json($transaction);
}
config/evm.php (node providers, defaults).database/migrations/[timestamp]_create_evm_transactions_table.php (track transactions).Farbcode\Evm\Facades\Evm (main API entry point).Farbcode\Evm\Events\TransactionSent, Farbcode\Evm\Events\TransactionConfirmed (listen for async updates).Use for read operations (e.g., balance checks, contract calls):
$balance = Evm::call([
'to' => '0xContractAddress',
'data' => '0x...', // ABI-encoded function call
]);
For production-grade reliability, leverage Laravel Queues:
// Dispatch a job
SendEvmTransaction::dispatch(
from: '0xFromAddress',
to: '0xToAddress',
value: wei(0.1),
gasLimit: 21000,
description: 'User payment'
);
// Listen for events (e.g., in a service)
Event::listen(TransactionConfirmed::class, function ($event) {
// Update DB, notify user, etc.
});
Use Evm::contract() to interact with smart contracts:
$contract = Evm::contract('0xContractAddress', $abi);
$result = $contract->call('balanceOf', ['0xUserAddress']);
$tx = $contract->send('transfer', ['0xRecipient', wei(0.1)]);
Dynamically estimate fees:
$gasPrice = Evm::estimateGasPrice(); // Legacy
$maxFeePerGas = Evm::estimateMaxFeePerGas(); // EIP-1559
$maxPriorityFeePerGas = Evm::estimateMaxPriorityFeePerGas();
$transaction = Evm::sendTransaction([
'to' => '0xToAddress',
'value' => wei(0.1),
'maxFeePerGas' => $maxFeePerGas,
'maxPriorityFeePerGas' => $maxPriorityFeePerGas,
]);
Ensure your queue worker processes SendEvmTransaction jobs:
php artisan queue:work --queue=evm
evm, but configurable in config/evm.php.Extend the package’s events for custom logic:
// app/Listeners/HandleTransactionConfirmed.php
public function handle(TransactionConfirmed $event)
{
// Log, notify, or update models
Log::info("Transaction confirmed: {$event->hash}");
}
Use the EvmServiceProvider in tests to mock the provider:
use Farbcode\Evm\Testing\MockEvm;
beforeEach(function () {
MockEvm::shouldReceive('sendTransaction')
->once()
->andReturn(['hash' => '0xMockHash']);
});
Configure multiple providers (e.g., mainnet, testnet) in config/evm.php and switch contexts:
Evm::setProvider('testnet');
$balance = Evm::getBalance('0xAddress');
nonce field in the evm_transactions table. Avoid manual nonce handling unless absolutely necessary.TransactionFailed events for nonce-related errors.gasLimit results in failed transactions.Evm::estimateGas() for dynamic limits:
$gasLimit = Evm::estimateGas([
'to' => '0xToAddress',
'data' => '0x...',
]);
.env and restrict file permissions:
chmod 600 .env
laravel/vault or AWS Secrets Manager.SendEvmTransaction job:
public function handle()
{
if (Transaction::where('hash', $this->hash)->exists()) {
return; // Skip if already processed
}
// Proceed with sending
}
TransactionFailed events for rate-limit errors and implement retries with exponential backoff in your job.Enable debug logging in config/evm.php:
'debug' => env('EVM_DEBUG', false),
Logs will appear in storage/logs/laravel.log.
Listen for TransactionFailed events to capture errors:
Event::listen(TransactionFailed::class, function ($event) {
Log::error("Failed TX: {$event->hash}. Error: {$event->error}");
});
Use a local EVM node (e.g., Ganache) for testing:
config([
'evm.providers.testnet.url' => 'http://localhost:8545',
]);
Extend the evm_transactions table by publishing and modifying the migration:
php artisan vendor:publish --tag="evm-migrations"
Add fields like user_id or metadata:
Schema::table('evm_transactions', function (Blueprint $table) {
$table->foreignId('user_id')->constrained()->nullable();
$table->json('metadata')->nullable();
});
Implement Farbcode\Evm\Contracts\Provider for non-Ethereum EVMs (e.g., Polygon, BSC):
class PolygonProvider implements Provider {
public function sendTransaction(array $payload): array {
// Custom logic
}
}
Register in config/evm.php:
'providers' => [
'polygon' => [
'class' => \App\Providers\PolygonProvider::class,
],
],
Extend the package to emit webhooks on transaction confirmation:
Event::listen(TransactionConfirmed::class, function ($event) {
Http::post(env('WEBHOOK_URL'), [
'hash' => $event->hash,
'status' => 'confirmed',
]);
});
Replace the default gas estimation with a custom oracle (e.g., Chainlink):
Evm::extend(function ($evm) {
$evm->estimateMaxFeePerGas = function () {
return request('chainlink_oracle')->maxFeePerGas;
};
});
How can I help you explore Laravel packages today?