PHP isn’t obsolete—it’s a quiet workhorse of Web3 infrastructure. This guide shows how to build a production-ready Ethereum integration using Symfony 7, PHP 8, PHP isn’t obsolete—it’s a quiet workhorse of Web3 infrastructure. This guide shows how to build a production-ready Ethereum integration using Symfony 7, PHP 8,

How to Build Real-World Web3 Infrastructure Using Symfony 7.4

2025/12/16 18:52
8 min read

The narrative that “PHP is dead” has been wrong for a decade. The narrative that “PHP can’t do Web3” is just as incorrect.

While Node.js dominates the frontend dApp ecosystem, PHP and Symfony are quietly powering the heavy lifting of the decentralized web: indexing off-chain data, managing private key orchestration for enterprise wallets, and bridging the gap between Web2 business logic and Web3 protocols.

In this guide, we will build a production-ready Web3 integration using Symfony 7.4 and PHP 8.3+. We won’t use obscure, unmaintained wrappers. We will use the industry-standard libraries to read the blockchain, interact with smart contracts, and implement a Sign-In with Ethereum (SIWE) authentication system using Symfony’s security core.

The Stack & Prerequisites

We are simulating a real-world environment. We will assume you are running Symfony 7.4 (the LTS release as of late 2025).

Requirements

  • PHP 8.3 or higher (with gmp and bcmath extensions enabled).
  • Symfony 7.4 CLI.
  • Composer.
  • An Ethereum Node URL (Infura, Alchemy, or a local Hardhat/Anvil node).

Library Selection

We will use the following strictly typed, verified libraries:

  1. web3p/web3.php: The foundational library for JSON-RPC communication.
  2. kornrunner/keccak: For Keccak-256 hashing (standard in Ethereum).
  3. simplito/elliptic-php: For cryptographic signature verification (essential for SIWE).

Installation

Create your project and install dependencies. Note that we explicitly allow web3p/web3.php to interface with modern Guzzle versions if needed.

\

composer create-project symfony/website-skeleton my_web3_app cd my_web3_app # Install the Web3 standard library composer require web3p/web3.php:^0.3 # Install crypto utilities for signature verification composer require kornrunner/keccak:^1.1 simplito/elliptic-php:^1.0 # Install the Maker bundle for rapid prototyping composer require --dev symfony/maker-bundle

\

Infrastructure: The Ethereum Client Service

Directly instantiating libraries in controllers is an anti-pattern. We will wrap the Web3 connection in a robust Symfony Service using Dependency Injection.

First, configure your node URL in .env:

# .env ETHEREUM_NODE_URL="https://mainnet.infura.io/v3/YOUR_INFURA_ID"

Now, create the service. We use PHP 8.2 Readonly Classes and Constructor Promotion for clean architecture.

\

// src/Service/Web3Client.php namespace App\Service; use Web3\Web3; use Web3\Eth; use Web3\Contract; use Web3\Providers\HttpProvider; use Web3\RequestManagers\HttpRequestManager; use Symfony\Component\DependencyInjection\Attribute\Autowire; readonly class Web3Client { private Web3 $web3; public function __construct( #[Autowire(env: 'ETHEREUM_NODE_URL')] private string $nodeUrl ) { // We utilize a timeout of 10 seconds for RPC calls $provider = new HttpProvider(new HttpRequestManager($this->nodeUrl, 10)); $this->web3 = new Web3($provider); } public function getEth(): Eth { return $this->web3->eth; } public function getContract(string $abi, string $address): Contract { return new Contract($this->web3->provider, $abi); } }

Reading State: Balance Checker

Let’s verify our connection by reading the native ETH balance of an address.

Note on Asynchrony: web3p/web3.php uses callbacks by default. To make this compatible with Symfony’s synchronous request/response lifecycle, we wrap the callback in a simple latch or use the returned promise if available. For simplicity and reliability in this version, we will use a referenced variable capture method which is the standard pattern for this library in PHP 8.

\

// src/Controller/WalletController.php namespace App\Controller; use App\Service\Web3Client; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\Routing\Attribute\Route; use Web3\Utils; #[Route('/api/wallet')] class WalletController extends AbstractController { public function __construct(private Web3Client $web3Client) {} #[Route('/balance/{address}', name: 'app_wallet_balance', methods: ['GET'])] public function balance(string $address): JsonResponse { $balance = null; $error = null; // Fetch balance via JSON-RPC $this->web3Client->getEth()->getBalance($address, function ($err, $data) use (&$balance, &$error) { if ($err !== null) { $error = $err; return; } $balance = $data; }); if ($error) { return $this->json(['error' => $error->getMessage()], 500); } // Convert BigInteger to Ether string // web3p returns PHP GMP/BigInteger objects $ethBalance = Utils::fromWei($balance, 'ether'); [$whole, $decimals] = $ethBalance; return $this->json([ 'address' => $address, 'balance_wei' => (string) $balance, 'balance_eth' => $whole . '.' . $decimals, ]); } }

Start your server (symfony server:start) and visit https://localhost:8000/api/wallet/balance/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 (Vitalik’s address). You should see a JSON response with his current balance.

Smart Contract Interaction (ERC-20)

Reading ETH is easy. Reading a token balance (like USDC) requires the ABI (Application Binary Interface).

We will create a Service method to read any ERC-20 balance.

\

// src/Service/TokenService.php namespace App\Service; use Web3\Contract; use Web3\Utils; class TokenService { // Minimal ERC-20 ABI for 'balanceOf' private const ERC20_ABI = '[{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"type":"function"}]'; public function __construct(private Web3Client $web3Client) {} public function getBalance(string $tokenAddress, string $walletAddress): string { $contract = $this->web3Client->getContract(self::ERC20_ABI, $tokenAddress); $resultBalance = null; // The "at" method sets the contract address for the call $contract->at($tokenAddress)->call('balanceOf', $walletAddress, function ($err, $result) use (&$resultBalance) { if ($err !== null) { throw new \RuntimeException($err->getMessage()); } // Result is an array based on outputs in ABI $resultBalance = $result['balance']; }); // Assuming 18 decimals for standard ERC-20 // In production, you should fetch the 'decimals' function from the contract first $formatted = Utils::fromWei($resultBalance, 'ether'); return $formatted[0] . '.' . $formatted[1]; } }

Security: Sign-In with Ethereum (SIWE)

This is the most critical part of Web3 UX. We do not want users to create passwords. We want them to sign a message with their wallet (Metamask, Rabby, etc.) to prove ownership.

The Logic:

  1. Frontend requests a “nonce” (a random string) from Symfony.
  2. Frontend signs a message: “I am signing into MyApp with nonce: X”.
  3. Frontend sends the addresssignature and message to Symfony.
  4. Symfony cryptographically recovers the public key from the signature.
  5. If the recovered address matches the claimed address, the user is authenticated.

The Cryptographic Verifier

We need a helper to perform ecrecover. PHP does not have this built-in easily, so we use simplito/elliptic-php and kornrunner/keccak.

\

// src/Security/Web3/SignatureVerifier.php namespace App\Security\Web3; use Elliptic\EC; use kornrunner\Keccak; class SignatureVerifier { public function verifySignature(string $message, string $signature, string $address): bool { // 1. Hash the message according to Ethereum standard (EIP-191) $prefix = sprintf("\x19Ethereum Signed Message:\n%d", strlen($message)); $hash = Keccak::hash($prefix . $message, 256); // 2. Parse Signature (Remove 0x, split into r, s, v) $signature = substr($signature, 2); $r = substr($signature, 0, 64); $s = substr($signature, 64, 64); $v = hexdec(substr($signature, 128, 2)); // Adjust v for recovery (Ethereum uses 27/28, library expects 0/1) $recId = $v - 27; if ($recId < 0 || $recId > 1) { return false; } // 3. Recover Public Key $ec = new EC('secp256k1'); try { $pubKey = $ec->recoverPubKey($hash, ['r' => $r, 's' => $s], $recId); } catch (\Exception $e) { return false; } // 4. Derive Address from Public Key // Drop first byte (04 prefix), hash the rest, take last 20 bytes $pubKeyHex = $pubKey->encode('hex'); $pubKeyHex = substr($pubKeyHex, 2); $addressHash = Keccak::hash(hex2bin($pubKeyHex), 256); $recoveredAddress = '0x' . substr($addressHash, -40); // 5. Compare (Case insensitive) return strtolower($address) === strtolower($recoveredAddress); } }

The Symfony Authenticator

Now we implement the Symfony 7 AbstractAuthenticator.

\

// src/Security/Web3Authenticator.php namespace App\Security; use App\Repository\UserRepository; use App\Security\Web3\SignatureVerifier; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Http\Authenticator\AbstractAuthenticator; use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; use Symfony\Component\Security\Http\Authenticator\Passport\Passport; use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport; class Web3Authenticator extends AbstractAuthenticator { public function __construct( private SignatureVerifier $verifier, private UserRepository $userRepository ) {} public function supports(Request $request): ?bool { return $request->isMethod('POST') && $request->getPathInfo() === '/api/login_web3'; } public function authenticate(Request $request): Passport { $data = json_decode($request->getContent(), true); $address = $data['address'] ?? ''; $message = $data['message'] ?? ''; // Contains the nonce $signature = $data['signature'] ?? ''; if (!$address || !$message || !$signature) { throw new AuthenticationException('Missing Web3 credentials.'); } // Verify the signature matches the address if (!$this->verifier->verifySignature($message, $signature, $address)) { throw new AuthenticationException('Invalid signature.'); } // Check nonce (Optional but recommended: Verify nonce exists in session/cache) // $storedNonce = $request->getSession()->get('login_nonce'); // if (!str_contains($message, $storedNonce)) throw ... return new SelfValidatingPassport( new UserBadge($address, function ($userIdentifier) { // Find user by wallet address or create new one return $this->userRepository->findOrCreateByWallet($userIdentifier); }) ); } public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response { return new JsonResponse(['message' => 'Welcome to Web3', 'user' => $token->getUser()->getUserIdentifier()]); } public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response { return new JsonResponse(['error' => $exception->getMessage()], 401); } }

Advanced: Indexing Events with Symfony Messenger

Web3 is often about reacting to things happening off-chain. You shouldn’t make your user wait while you query the blockchain. Instead, use a worker.

We will create a command that polls for “Transfer” events and dispatches them to the Messenger bus.

\

// src/Command/BlockchainListenerCommand.php namespace App\Command; use App\Service\Web3Client; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; #[AsCommand(name: 'app:blockchain:listen', description: 'Polls for ERC20 Transfer events')] class BlockchainListenerCommand extends Command { public function __construct(private Web3Client $web3Client) { parent::__construct(); } protected function execute(InputInterface $input, OutputInterface $output): int { $contractAddress = '0x...'; // USDC or your token $transferTopic = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'; // Keccak('Transfer(address,address,uint256)') $output->writeln("Listening for events on $contractAddress..."); // In a real app, you would store the 'last_scanned_block' in a DB $currentBlock = 'latest'; // Uses eth_getLogs $this->web3Client->getEth()->getLogs([ 'address' => $contractAddress, 'topics' => [$transferTopic], 'fromBlock' => '0x' . dechex(20000000) // Hex block number ], function ($err, $logs) use ($output) { if ($err) { $output->writeln("Error: " . $err->getMessage()); return; } foreach ($logs as $log) { // Dispatch to Symfony Messenger here $output->writeln("Transfer detected in transaction: " . $log->transactionHash); } }); return Command::SUCCESS; } }

Note: In production, you would run this command inside a supervisord loop or cron, maintaining state of the last scanned block to ensure no events are missed.

Conclusion

We have successfully bridged the gap. You now have a Symfony 7.4 application that can:

  1. Read direct blockchain state via JSON-RPC.
  2. Decode smart contract data (ERC-20).
  3. Authenticate users securely using their Ethereum wallets (SIWE) without passwords.
  4. Listen for on-chain events via CLI commands.

Web3 is not about rewriting your entire stack in Solidity or Rust. It’s about orchestration. Symfony is the perfect orchestrator — stable, secure and typed.

Ready to Tokenize Your Enterprise?

If you are looking to integrate high-value assets onto the blockchain or need a secure audit of your current Web3-PHP architecture, I can help.

Contact me to discuss your Web3 Strategy https://www.linkedin.com/in/matthew-mochalkin/

\

Market Opportunity
RealLink Logo
RealLink Price(REAL)
$0.05343
$0.05343$0.05343
+2.92%
USD
RealLink (REAL) Live Price Chart
Disclaimer: The articles reposted on this site are sourced from public platforms and are provided for informational purposes only. They do not necessarily reflect the views of MEXC. All rights remain with the original authors. If you believe any content infringes on third-party rights, please contact [email protected] for removal. MEXC makes no guarantees regarding the accuracy, completeness, or timeliness of the content and is not responsible for any actions taken based on the information provided. The content does not constitute financial, legal, or other professional advice, nor should it be considered a recommendation or endorsement by MEXC.

You May Also Like

Bad News for European Crypto Holders? EU Calls For Harsher Crypto Regulation Despite MiCA

Bad News for European Crypto Holders? EU Calls For Harsher Crypto Regulation Despite MiCA

EU regulators push stricter crypto rules beyond MiCA, seeking ESMA oversight, cybersecurity audits, and AMLR bans on privacy tokens. European regulators are now calling louder for stricter crypto rules.  France’s AMF, Austria’s FMA and Italy’s CONSOB are now arguing that the Markets in Crypto-Assets Regulation (also known as MiCA framework) is not enough to manage […] The post Bad News for European Crypto Holders? EU Calls For Harsher Crypto Regulation Despite MiCA appeared first on Live Bitcoin News.
Share
LiveBitcoinNews2025/09/18 13:00
Here’s Why This Analyst Predicts Shiba Inu 568% Surge

Here’s Why This Analyst Predicts Shiba Inu 568% Surge

Popular community member Heber Mayen suggests that Shiba Inu is poised for an explosive breakout. In a tweet yesterday, Mayen shared Shiba Inu’s one-month price chart, showing the asset up 3.65% over the past 30 days to $0.00001345.Visit Website
Share
The Crypto Basic2025/09/19 14:59
AVAX One Unveils Ambitious $550M Avalanche Reserve Plan

AVAX One Unveils Ambitious $550M Avalanche Reserve Plan

BitcoinWorld AVAX One Unveils Ambitious $550M Avalanche Reserve Plan In a groundbreaking move that’s sending ripples across both traditional finance and the cryptocurrency world, Nasdaq-listed AgriFORCE (AGRI) is making waves with its audacious plan to rebrand as AVAX One. This strategic pivot marks a significant first: a publicly traded company on a major exchange explicitly dedicating its core strategy to investing in Avalanche (AVAX) reserves. For anyone tracking the evolving landscape of digital assets, the emergence of AVAX One signals a bold new chapter. What Does the AVAX One Rebrand Mean for Investors? The decision by AgriFORCE to transform into AVAX One is far more than just a name change; it’s a complete strategic overhaul. The company will now focus intensely on accumulating and managing Avalanche (AVAX) reserves. This commitment positions AVAX One as a unique player in the public market, offering traditional investors a direct avenue to exposure in a prominent layer-1 blockchain. Pioneering Public Exposure: AVAX One is set to become the first Nasdaq-listed entity to center its operations around a specific cryptocurrency, offering a new model for institutional crypto adoption. Significant Capital Commitment: The firm has already secured a substantial $300 million through a private investment in public equity (PIPE) deal. This initial capital infusion demonstrates strong investor confidence in the new direction. Targeting Growth: The ambition doesn’t stop there. AVAX One intends to raise an additional $250 million, aiming for a total of $550 million dedicated to building its AVAX reserves. This aggressive strategy underscores the company’s belief in Avalanche’s long-term potential. Powering Up AVAX One: The Role of Key Advisors To navigate this innovative venture, AVAX One is bringing in some heavy hitters from both traditional finance and the crypto industry. The caliber of these individuals speaks volumes about the serious intent behind this rebranding. The company has announced that two highly respected figures are expected to join its advisory board: Anthony Scaramucci: Founder of SkyBridge Capital, a global investment firm. Scaramucci is well-known for his insights into financial markets and his increasing involvement in the crypto space. His presence lends significant credibility and strategic guidance to AVAX One. Brett Tejpaul: Head of Coinbase Institutional. Tejpaul brings extensive experience from one of the leading cryptocurrency exchanges, offering invaluable expertise in digital asset markets, custody, and institutional trading strategies. These appointments suggest a robust framework for governance and strategic direction, blending deep financial acumen with specialized cryptocurrency knowledge. Their collective wisdom will be crucial in guiding AVAX One‘s investment decisions and market positioning. The Ambitious $550M Target for AVAX One Reserves – A Bold Move? The ambitious target for AVAX One‘s Avalanche reserves, aiming for a total of $550 million, is a testament to the company’s conviction in the Avalanche ecosystem. This substantial capital allocation positions AVAX One to potentially become a major holder of AVAX, with significant implications for both the company and the broader Avalanche network. Investing directly in a digital asset like AVAX comes with both opportunities and considerations: Potential for Appreciation: If Avalanche continues to grow and gain adoption, the value of AVAX One‘s reserves could appreciate significantly, benefiting shareholders. Ecosystem Participation: Holding substantial AVAX could allow AVAX One to participate in Avalanche’s governance, staking, and decentralized finance (DeFi) activities, potentially generating additional yield. Market Volatility: Like all cryptocurrencies, AVAX is subject to market volatility. AVAX One‘s strategy will need to account for these fluctuations and manage risk effectively. This strategic shift highlights a growing trend where traditional companies are seeking direct exposure to the crypto market, recognizing its potential for innovation and financial growth. In conclusion, AgriFORCE’s transformation into AVAX One is a landmark event, showcasing a Nasdaq-listed company’s full embrace of the digital asset economy. With substantial funding already secured, an ambitious reserve target, and a stellar advisory board, AVAX One is poised to be a significant player in the Avalanche ecosystem and a bellwether for institutional crypto adoption. This bold move will undoubtedly be watched closely by investors and the crypto community alike, as it charts new territory for public companies in the digital age. Frequently Asked Questions (FAQs) What is AVAX One? AVAX One is the new name for AgriFORCE (AGRI), a Nasdaq-listed company that is rebranding to focus its core business strategy on investing in and holding Avalanche (AVAX) cryptocurrency reserves. Why is AgriFORCE rebranding to AVAX One? AgriFORCE is rebranding to AVAX One to pivot its business model entirely towards the digital asset space, specifically focusing on Avalanche (AVAX) as its primary investment vehicle. This strategic shift aims to capitalize on the growth potential of the cryptocurrency market. Who are the key advisors for AVAX One? The advisory board for AVAX One is expected to include high-profile figures such as Anthony Scaramucci, founder of SkyBridge Capital, and Brett Tejpaul, head of Coinbase Institutional. Their expertise will guide the company’s new direction. What is Avalanche (AVAX)? Avalanche (AVAX) is a high-performance blockchain platform designed for decentralized applications (dApps) and custom blockchain networks. It is known for its speed, security, and scalability, making it a prominent player in the layer-1 blockchain space. What does the $550M target for AVAX One reserves mean? The $550 million target signifies the total amount of capital AVAX One aims to raise and dedicate to acquiring and holding Avalanche (AVAX) tokens. This includes $300 million already raised and an additional $250 million targeted for future fundraising. Did you find this article insightful? Share it with your network and help spread the word about this pioneering move in the crypto investment landscape! To learn more about the latest crypto market trends, explore our article on key developments shaping Avalanche price action. This post AVAX One Unveils Ambitious $550M Avalanche Reserve Plan first appeared on BitcoinWorld.
Share
Coinstats2025/09/22 19:40