it-healer/laravel-evm
Composer 安装命令:
composer require it-healer/laravel-evm
包简介
A library for Laravel that allows you to work with any EVM network (Ethereum, BSC, Polygon, ...): wallets, addresses, tokens, deposits and transfers.
README 文档
README
English | Русский
A Laravel package for working with any EVM network (Ethereum, BSC, Polygon, Arbitrum, Base, ...): HD wallets, address generation, native coin and ERC-20 token balances, incoming deposit tracking with webhooks, and outgoing transfers (legacy and EIP-1559).
Unlike single-chain packages, wallets and addresses here are network-agnostic — the same EVM address works in every chain. Networks are first-class records: add Polygon and all your existing addresses immediately work in it, with balances, transactions and deposits tracked per network.
Requirements
- PHP 8.2+ with
ext-gmp - Laravel 10 / 11 / 12 / 13
Installation
composer require it-healer/laravel-evm
php artisan evm:install # publishes config + migrations
php artisan migrate
Schedule the sync (host app, e.g. routes/console.php):
Schedule::command('evm:sync')->everyMinute()->withoutOverlapping()->runInBackground();
Networks
Networks can be created manually or picked from the chainid.network catalog:
use ItHealer\LaravelEvm\Facades\Evm; // Manually $polygon = Evm::createNetwork( chainId: 137, name: 'polygon', currencySymbol: 'POL', title: 'Polygon Mainnet', ); // From the catalog (resolves title, currency, decimals, explorer URL) $bsc = Evm::createNetworkFromChainList(56); // Catalog browsing/searching (cached 24h) — for admin UI pickers $chains = app(\ItHealer\LaravelEvm\Services\ChainList\ChainListService::class); $chains->search('polygon'); // Collection<ChainDTO> $chains->find(42161); // ChainDTO|null
Per-network settings: tx_type (null = auto-detect EIP-1559 by baseFeePerGas, 0 = force legacy,
2 = force EIP-1559), lag_blocks (sync overlap, increase for fast chains like BSC/Polygon),
confirmations_target, active (deactivated networks are skipped by sync and refuse transfers).
Nodes (RPC)
Each network needs at least one RPC node. Alchemy URLs are built automatically:
Evm::createNode($polygon, 'public', 'https://polygon-rpc.com'); Evm::createAlchemyNode($polygon, apiKey: 'YOUR_ALCHEMY_KEY', name: 'alchemy');
The node is health-checked before saving. With several nodes per network the least-used
working one is picked automatically (Evm::getNode($network)).
Explorers (transaction history)
Incoming/outgoing transfers are detected via explorer drivers:
| Driver | Coverage | Notes |
|---|---|---|
etherscan_v2 |
60+ chains | One Etherscan API key for all chains (the rate limit is shared!) |
alchemy |
Alchemy networks | Uses alchemy_getAssetTransfers; base_url must be the Alchemy URL |
use ItHealer\LaravelEvm\Enums\ExplorerDriver; use ItHealer\LaravelEvm\Services\AlchemyUrlFactory; Evm::createExplorer($polygon, ExplorerDriver::EtherscanV2, 'etherscan', apiKey: 'ETHERSCAN_KEY'); Evm::createExplorer($polygon, ExplorerDriver::Alchemy, 'alchemy', baseURL: AlchemyUrlFactory::make(137, 'YOUR_ALCHEMY_KEY'));
Tokens
Tokens are tracked per network. Create from the contract (3 RPC calls) or from a token list without any RPC:
// On-chain metadata Evm::createToken($polygon, '0xc2132D05D31c914a87C6611C10748AEb04B58e8F'); // From token lists (config: evm.token_lists) — for admin UI pickers $tokens = app(\ItHealer\LaravelEvm\Services\TokenList\TokenListService::class); $usdt = $tokens->forChain(137)->firstWhere(fn ($t) => $t->symbol() === 'USDT'); Evm::createTokenFromList($polygon, $usdt);
Set active = false on a token to stop tracking it without deleting its history.
Wallets & addresses
// New wallet (BIP-39 mnemonic generated, primary address derived at index 0) $wallet = Evm::createWallet('main', password: 'secret'); // Import an existing mnemonic $wallet = Evm::createWallet('imported', mnemonic: 'test test ... junk'); // More addresses $address = Evm::createAddress($wallet, 'Deposit #2'); // Watch-only Evm::importAddress($wallet, '0x52908400098527886E0F7030069857D2E4169EE7'); // Validation / checksums Evm::validateAddress('0x...'); // EIP-55 aware Evm::toChecksumAddress('0x...');
Derivation paths
Different wallets use different BIP-44 paths. The path template is configured per wallet
({index} is replaced with the address index):
use ItHealer\LaravelEvm\Evm as EvmCore; Evm::createWallet('metamask', mnemonic: $m); // m/44'/60'/0'/0/{index} Evm::createWallet('ledger', mnemonic: $m, derivationPath: EvmCore::PATH_LEDGER_LIVE); // m/44'/60'/{index}'/0/0 Evm::createWallet('ledger-old', mnemonic: $m, derivationPath: EvmCore::PATH_LEDGER_LEGACY); // m/44'/60'/0'/{index} Evm::createWallet('custom', mnemonic: $m, derivationPath: "m/44'/60'/1'/0/{index}");
The default template is config('evm.wallet.default_derivation_path').
Secrets (mnemonic, seed, private keys) are stored AES-256 encrypted; with a wallet password
the encryption key is derived from it — unlock with $wallet->unlockWallet($password) before
reading private keys or transferring.
Balances
Balances are stored per (address, network) and refreshed by the sync:
$row = $address->balanceForNetwork($polygon); // EvmAddressBalance $row->balance; // BigDecimal, native coin $row->tokens; // [contract => '123.45'] $wallet->balanceForNetwork($polygon); // BigDecimal, sum over addresses $wallet->tokensForNetwork($polygon); // [contract => BigDecimal] // Live on-chain queries Evm::getBalance($polygon, $address); Evm::getBalanceOfToken($polygon, $address, $usdtToken);
Transfers
All transfer methods take the network first. Fees are estimated automatically:
legacy gasPrice, or EIP-1559 maxFeePerGas = 2 × baseFee + priorityFee (see config('evm.fee')).
Nonces are allocated under a per-(chain, address) cache lock, so concurrent transfers don't clash.
// Preview (no signing): amounts, gas, fee, resulting balances, error if any $preview = Evm::previewTransfer($polygon, $fromAddress, '0xRecipient', '0.5'); // Native coin $result = Evm::transfer($polygon, $fromAddress, '0xRecipient', '0.5'); $result->txid(); // ERC-20 Evm::transferToken($polygon, $usdtToken, $fromAddress, '0xRecipient', '100'); // transferFrom (collector pays gas, requires prior approve) Evm::transferFromToken($polygon, $usdtToken, $collectorAddress, '0xHolder', '0xRecipient', '100');
Sync & deposits
evm:sync walks every active network → every wallet → every address: refreshes balances,
imports transaction history via the explorer driver (cursor sync_block_number per
address×network with a lag_blocks overlap; re-runs are idempotent), records incoming
transfers as EvmDeposit and calls your webhook handler for each new deposit.
// config/evm.php 'webhook_handler' => \App\Services\WebhookHandlers\EvmWebhookHandler::class,
use ItHealer\LaravelEvm\Models\EvmDeposit; use ItHealer\LaravelEvm\Webhook\WebhookHandlerInterface; class EvmWebhookHandler implements WebhookHandlerInterface { public function handle(EvmDeposit $deposit): void { $deposit->network; // EvmNetwork — branch per chain $deposit->symbol; // 'POL' or token symbol $deposit->amount; // BigDecimal $deposit->confirmations; } }
Commands:
evm:sync # everything (under a cache lock)
evm:network-sync {network} # one network (id, chain id or name)
evm:wallet-sync {wallet_id} [--network=]
evm:address-sync {address_id} [--network=] [--force]
evm:node-sync {node_id} # health check
evm:explorer-sync {explorer_id} # health check
Sync services support progress/cancellation hooks (for UI-driven resyncs):
(new AddressNetworkSync($address, $network, force: true)) ->onProgress(fn (int $count, string $stage) => cache()->put($key, $count)) ->cancelWhen(fn (): bool => (bool) cache()->get($cancelKey)) ->run();
Adaptive synchronization (touch)
For large installations enable adaptive sync (evm.touch) so addresses are polled often
while in use and rarely while idle, instead of every run. An address is "active" for
waiting_seconds after its last touch_at (set on user/merchant activity); while active it
syncs no more often than fast_interval, while idle no more often than slow_interval.
// config/evm.php 'touch' => [ 'enabled' => true, 'waiting_seconds' => 1800, // stay "active" 30 min after last touch 'fast_interval' => 60, // while active: at most once per 60s 'slow_interval' => 3600, // while idle: at most once per hour (null = skip idle entirely) ],
Mark activity by updating touch_at whenever the wallet is used (GUI view, API call, unlock):
$address->update(['touch_at' => now()]); // or in bulk for a wallet: $wallet->addresses()->update(['touch_at' => now()]);
Defaults (fast_interval 0, slow_interval null) preserve the legacy behavior: active
addresses sync every run, idle ones are skipped. evm:address-sync --force bypasses the schedule.
Real-time deposits with Alchemy webhooks
Polling pays for an explorer scan of every address on every run, even when nothing happened.
Alchemy Address Activity webhooks
flip this around: Alchemy pushes a notification the moment a watched address has on-chain
activity — incoming or outgoing — the package verifies its signature and triggers a targeted
AddressNetworkSync for that address (matched whether it appears as sender or recipient, across every
wallet it belongs to). Deposit detection, outgoing transaction history, dedup and your webhook_handler
stay exactly the same — you just stop paying Compute Units for idle polling.
Free tier: 5 webhooks (= up to 5 networks), up to 100k addresses per webhook. The Notify Auth Token (dashboard → Webhooks → top-right) is not your RPC API key.
1. Configure
EVM_ALCHEMY_NOTIFY_AUTH_TOKEN=your-notify-auth-token EVM_ALCHEMY_WEBHOOK_ENABLED=true EVM_ALCHEMY_WEBHOOK_URL=https://your-app.com/evm/alchemy/webhook # public HTTPS receiver EVM_ALCHEMY_AUTO_SUBSCRIBE=true # subscribe new addresses automatically
The receiver route is registered by the package (path evm.alchemy.webhook.path, default
evm/alchemy/webhook). It is not under the web/auth middleware groups — the HMAC
signature is the authentication — so keep it out of any localized/CSRF-protected prefix.
2. Create the webhook and subscribe addresses
php artisan evm:alchemy-setup polygon --reconcile # creates the webhook + pushes existing addresses
--reconcile (or evm:alchemy-reconcile) diffs the addresses the package tracks in the network
(available addresses of wallets that have the network attached) against Alchemy's list and applies
the difference (batched to 500/request). Run it again after attaching a network to a wallet, or rely
on evm.alchemy.auto_subscribe for incremental add/remove on address create/delete.
php artisan evm:alchemy-reconcile # all configured networks php artisan evm:alchemy-reconcile polygon # one network
Programmatic API (facade):
Evm::ensureAlchemyWebhook($polygon); // create or reuse, returns EvmAlchemyWebhook Evm::subscribeAlchemyAddress($address, $polygon); Evm::unsubscribeAlchemyAddress($address, $polygon); Evm::reconcileAlchemyWebhook($polygon); // ['added' => [...], 'removed' => [...]]
3. Confirmations
Address Activity webhooks fire once (when the tx is mined) and are not re-sent as confirmations
grow. If your handler waits for N confirmations, schedule a cheap, targeted top-up that re-syncs only
addresses with deposits still below confirmations_target:
Schedule::command('evm:confirm-deposits')->everyFiveMinutes()->withoutOverlapping();
Keep evm:sync as a rare fallback (e.g. hourly) for any missed deliveries — re-runs are idempotent.
Compute Units, cost control & load balancing
Every RPC call on a node and every explorer request is metered in a credits counter
(Compute Units) that resets at the start of each calendar month. Node and explorer
selection picks the one with the fewest credits this month, so load (and Alchemy CU
spend) is distributed automatically across several nodes/explorers of a network.
$node = Evm::getNode($polygon); // the least-used node this month $node->credits; // CU spent this month $node->creditsThisMonth(); // same, but 0 if the counter is from a previous month
CU costs per method come from ItHealer\LaravelEvm\Services\Alchemy\ComputeUnits and mirror
Alchemy's published prices; override any of them in config('evm.compute_units'). For non-Alchemy
providers the same table is just a load weight.
Reducing Alchemy CU
Polling is CU-hungry because alchemy_getAssetTransfers costs 150 CU and runs for both
directions on every address, every run. Options, cheapest first:
- Use webhooks instead of polling (see above) — you pay CU only when activity happens.
evm.sync.track_outgoing = false— detect deposits only (query justtoAddress), halving thegetAssetTransfersrequests on the Alchemy explorer.evm.sync.block_cache_ttl = N(seconds) — fetcheth_blockNumberonce per network per run instead of once per address.- Stored token decimals are reused automatically, so token balance reads no longer spend an
extra
decimals()eth_call. - Add several nodes/explorers per network — requests spread across them by least-credits.
Model customization
Every model can be replaced in config('evm.models') with a subclass — the package
resolves models through this map everywhere.
Testing
vendor/bin/pest
Address derivation and transaction signing (legacy + EIP-1559) are verified against ethers.js-generated vectors, explorer drivers and sync against HTTP fakes.
Custom development & contacts / Заказная разработка и контакты
EN — Need a new project built from scratch, or these modules integrated into your existing application? Contact the developer directly — custom development, module integration and ongoing support are available.
RU — Нужен новый проект «под ключ» или интеграция этих модулей в существующее приложение? Свяжитесь с разработчиком напрямую — доступны заказная разработка, интеграция модулей и поддержка.
- 🌐 Website / Сайт: it-healer.com
- ✈️ Telegram: @biodynamist · +90 551 629 47 16
- 📱 WhatsApp: +90 551 629 47 16
- 📧 Email: info@it-healer.com
- 🐛 Issues / Баг-репорты: GitHub Issues
License
MIT
it-healer/laravel-evm 适用场景与选型建议
it-healer/laravel-evm 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 63 次下载、GitHub Stars 达 1, 最近一次更新时间为 2026 年 06 月 11 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「php」 「laravel」 「Polygon」 「ethereum」 「bsc」 「it-healer」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 it-healer/laravel-evm 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 it-healer/laravel-evm 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 it-healer/laravel-evm 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
PHP helper classes to enforce RFC7946 polygon winding order in GeoJSON
Open-source native PHP library for doing geometry operations. Can read and write a wide variety of formats: (E)WKT, (E)WKB, TWKB, GeoJSON, KML, GPX, GeoRSS. Works with all Simple-Feature geometries (Point, LineString, Polygon...) and can be used to get centroids, bounding-boxes, area, etc.
Pure (no dependencies) implementation of blocksdk
Alfabank REST API integration
Intended for use with lib16 SVG Builder.
A production-grade Laravel package for PostGIS spatial types and geospatial queries through Eloquent.
统计信息
- 总下载量: 63
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 1
- 点击次数: 30
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-06-11