amashukov / blockchain-context-bundle
Symfony bundle providing typed Bitcoin Core, TON and EVM integration services.
Package info
github.com/AndreyMashukov/blockchain-context-bundle
Type:symfony-bundle
pkg:composer/amashukov/blockchain-context-bundle
Requires
- php: >=8.3
- ext-gmp: *
- ext-openssl: *
- ext-sodium: *
- amashukov/eth-php: ^0.1.0
- amashukov/http-client-php: ^0.1.0
- amashukov/ton-php: ^0.1.0
- nyholm/psr7: ^1.8
- psr/clock: ^1.0
- psr/log: ^3.0
- symfony/cache: ^7.0
- symfony/config: ^7.0
- symfony/dependency-injection: ^7.0
- symfony/framework-bundle: ^7.0
- symfony/http-kernel: ^7.0
- symfony/yaml: ^7.0
Requires (Dev)
- amashukov/rector-php-rules: ^0.4.0
- friendsofphp/php-cs-fixer: ^3.50
- phpstan/phpstan: ^2.0
- phpunit/phpunit: ^11.0
- rector/rector: ^2.0
- symfony/clock: ^7.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Symfony 7 bundle for crypto payments — provides typed Bitcoin Core, TON and EVM clients alongside signature, finality and transaction-building services.
amashukov/blockchain-context-bundle is a Symfony 7 bundle for crypto payments that exposes Bitcoin Core JSON-RPC together with the pure-PHP TON + EVM SDKs (amashukov/ton-php + amashukov/eth-php) through one autowired blockchain surface.
Features
- Autowired RPC clients — a typed
BitcoinRpcClient,Amashukov\EthRpc\JsonRpcProvider/EthRpcClient, andAmashukov\Toncenter\ToncenterClient, wired over PSR-18 transports. - Exact Bitcoin amounts — Bitcoin Core JSON numbers are preserved as decimal strings and converted to satoshis without floating-point arithmetic.
- Signing & wallet —
Amashukov\Eip1559TxSigner\Eip1559Signer(EIP-1559 offline signer) andAmashukov\TonWallet\WalletV4R2(built from a mnemonic via the bundle factory);ToncenterWalletRpcimplements the wallet's RPC port. - Per-chain domain services (host-app-agnostic, owning ports the host implements):
Detection\ChainDepositCheckChain,Finality\{ConfirmationCheckChain,ConfirmationCounterRegistry,DepthPollingFinalityVerifier},Gas\{EthGasFetcher,TonGasFetcher,ZeroGasFetcher},TxBuilder\DepositTxBuilderChain(+ per-chain TON / TON-Jetton / ETH / USDT-ERC20 builders),Explorer\DefaultExplorerUrl,Numeric\{BcDecimal,UuidIntCodec,UsdtJettonDecimals},Time\RealSleeper. SignatureVerifier— EIP-191 (personal_sign, secp256k1 ecrecover + Keccak-256) + Ed25519 (TON Connect) verification.PrivKeyEncrypter— AES-256-GCM authenticated encryption for keys at rest.DepositWalletDeriverInterfaceport +DerivedWallet/DepositEvidencevalue objects.- HD deposit wallets —
Service\Hd\EvmHdDeriver(BIP39 seed → BIP32 secp256k1,m/44'/60'/{account}'/0/{index}, lowercase0xaddress) andService\Hd\TonHdDeriver(BIP39 seed → SLIP-0010 ed25519,m/44'/607'/{account}'/0'/{index}', WalletV4R2 non-bounceableUQ…address) implementDepositWalletDeriverInterface. One mnemonic serves any number of separate address spaces through theaccountargument;TonHdDeriver::wallet($index)returns the signingWalletV4R2. The TON keys come from a BIP39 seed, not from a TON-native mnemonic, so wallet apps cannot import them from the phrase — recovery goes through the deriver. Pinned against the official BIP32 / SLIP-0010 / BIP39 vectors and against wallets derived by ethers and @ton/ton. - Tagged-iterator chains —
ChainDepositCheckChain,ConfirmationCheckChain,ConfirmationCounterRegistry, andDepositTxBuilderChainauto-collect their members via bundle-namespaced tags (blockchain_context.*); drop a new impl and it joins the chain with no DI edits.
Why amashukov/blockchain-context-bundle
There is no maintained Symfony bundle for crypto-payment infrastructure — most projects glue raw RPC clients into services by hand. This bundle wires Bitcoin Core, TON and EVM services into Symfony's container with autowiring, env-agnostic config, and a tagged-iterator extension model.
Installation
composer require amashukov/blockchain-context-bundle
Register the bundle (Symfony Flex does this automatically):
// config/bundles.php return [ // ... Amashukov\BlockchainContextBundle\BlockchainContextBundle::class => ['all' => true], ];
Usage
Inject any autowired service directly:
use Amashukov\EthRpc\JsonRpcProviderInterface; use Amashukov\Toncenter\ToncenterClientInterface; use Amashukov\BlockchainContextBundle\Service\Bitcoin\BitcoinRpcClientInterface; use Amashukov\BlockchainContextBundle\Service\SignatureVerifier; final class SomeService { public function __construct( private JsonRpcProviderInterface $eth, private ToncenterClientInterface $ton, private BitcoinRpcClientInterface $bitcoin, private SignatureVerifier $verifier, ) {} }
Bitcoin transaction timestamps
Bitcoin wallet transaction DTOs expose optional blockTime (mined block timestamp) and timeReceived (wallet first receipt). Missing fields remain null; malformed, negative, fractional and overflowing timestamps fail decoding. These are separate evidence clocks, not an inferred payment timestamp. Existing constructor calls remain compatible.
Configuration
The bundle is env-agnostic: it exposes a config tree and reads only %blockchain_context.*% parameters internally. The host maps them to its own environment. Config is split per chain (bitcoin: / eth: / ton:), each toggleable via enabled:
# config/packages/blockchain_context.yaml blockchain_context: bitcoin: enabled: true rpc_url: '%env(BITCOIN_RPC_URL)%' rpc_user: '%env(BITCOIN_RPC_USER)%' rpc_password: '%env(BITCOIN_RPC_PASSWORD)%' timeout_seconds: 30 explorer: '%env(BITCOIN_EXPLORER)%' # default https://mempool.space; '' disables links (regtest) eth: enabled: true rpc_url: '%env(ETH_RPC_URL)%' # EVM JSON-RPC endpoint chain_id: '%env(int:BRIDGE_ETH_CHAIN_ID)%' wallet_private_key:'%env(BRIDGE_ETH_WALLET_PRIVATE_KEY)%' usdt_token_address:'%env(USDT_TOKEN_ADDRESS)%' # USDT-ERC20 deposits explorer: '%env(BRIDGE_ETH_EXPLORER)%' # default https://etherscan.io ton: enabled: true toncenter_api_key: '%env(TONCENTER_API_KEY)%' # optional — lifts toncenter rate limit wallet_mnemonic: '%env(BRIDGE_TON_WALLET_MNEMONIC)%' bridge_contract: '%env(BRIDGE_TON_CONTRACT)%' # USDT-Jetton deposits finality_polls: '%env(int:TON_FINALITY_POLLS)%' # 0 = skip depth re-poll explorer: '%env(TON_EXPLORER)%' # default https://tonscan.org deposit_wallet_encryption_key: '%env(DEPOSIT_WALLET_ENCRYPTION_KEY)%' # base64 of 32 bytes
Every key is optional (sensible empty / 0 / explorer defaults), so a host that uses only one chain can leave the other section's values unset (or enabled: false).
Requirements
- PHP 8.3+
- Symfony 7.x (
symfony/framework-bundle) ext-gmp,ext-sodium,ext-openssl,ext-bcmath
Related packages
| Package | Layer |
|---|---|
| amashukov/ton-php | Umbrella TON SDK (Cell/BOC, wallet, toncenter) |
| amashukov/eth-php | Umbrella EVM SDK (Keccak, secp256k1, RLP, EIP-1559, ABI, RPC) |
| amashukov/http-client-php | PSR-18 cURL HTTP client |
| amashukov/eth-rpc-client-php | ethers.js v6-style JSON-RPC client |
| amashukov/toncenter-client-php | Typed toncenter v2 client |
Quality
- PHPStan level 9 across
src/. - php-cs-fixer with the
@PER-CSruleset. - GitHub Actions CI on every push.
composer install composer test # PHPUnit composer stan # PHPStan (level 9) composer cs # php-cs-fixer (dry-run) composer rector # Rector (dry-run)
License
MIT — see LICENSE.
Bitcoin transaction fee evidence
BitcoinRpcClientInterface::getRawTransaction($txid, $blockHash) uses Core verbosity 2 and returns typed inputs, outputs and block identity. BitcoinRawTransaction::fee is the exact complete input-minus-output sum; missing previous outputs or coinbase inputs leave it null. A proven zero stays zero. A conflicting reported fee or a returned transaction/block identity mismatch throws. Callers must check active-chain evidence and classify payer ownership separately. The whole transaction fee is not additive across multiple deposit outputs. Existing wallet transaction fee defaults are unchanged and must not be treated as proof of an external payer’s fee.