amashukov / abi-encoder-php
Solidity ABI calldata encoder in pure PHP — function selectors plus static (address/uint/int/bytesN) and dynamic (string/bytes) types, byte-for-byte equivalent to ethers.js v6.
Requires
- php: >=8.3
- ext-gmp: *
- amashukov/keccak-php: ^0.1.2
Requires (Dev)
- amashukov/rector-php-rules: ^0.1.0
- friendsofphp/php-cs-fixer: ^3.50
- phpstan/phpstan: ^2.0
- phpunit/phpunit: ^11.0
- rector/rector: ^2.0
README
Solidity ABI calldata encoder in pure PHP — byte-for-byte parity with ethers.js v6.
Encode Ethereum / EVM Solidity ABI calldata in pure PHP: function selectors (methodId), static head types (address, uint8 / uint24 / uint256) and top-level dynamic types (bytes, string). Output is byte-for-byte equivalent to ethers.js v6 Interface.encodeFunctionData, so calldata built in PHP matches what your JavaScript tooling and Solidity contracts expect.
Features
- Function selector (
methodId) from a canonical signature via keccak-256. - Full call encoding (selector + head + dynamic tail) in one call.
- Component encoders:
encodeAddress,encodeUint,encodeBytes. - Bigint-safe
uint256viaext-gmp— accepts hex or decimal strings, handles max uint256. - Byte-for-byte parity with ethers.js v6 (cross-validated in tests).
- PHPStan level 9 clean,
strict_types.
Why amashukov/abi-encoder-php
Most PHP web3 stacks pull in web3p/web3.php (and its web3p/ethereum-abi) for ABI encoding, which is a much larger surface than a contract call usually needs. This package is intentionally narrow and dependency-light: it covers the head-only static types and top-level dynamic bytes / string that real EVM contract calls use, with explicit ethers.js v6 parity as the correctness bar. If you only need to build calldata for eth_sendRawTransaction, this is the focused tool.
Installation
composer require amashukov/abi-encoder-php
Usage
Function selector
use Amashukov\AbiEncoder\AbiEncoder; AbiEncoder::methodId('withdraw(address,uint256)'); // 'f3fef3a3'
The selector is the first 4 bytes of the keccak-256 hash of the canonical signature, returned as 8 hex chars (no 0x).
Encode a full call
$calldata = AbiEncoder::encodeCall('withdraw(address,uint256)', [ ['address', '0x1234567890123456789012345678901234567890'], ['uint256', '500000000000000000'], // decimal string; bigints supported via ext-gmp ]); // 0xf3fef3a3000000…0006f05b59d3b20000
Each argument is a [type, value] pair. Supported types: address, uint8, uint24, uint256, bytes, string. Dynamic types (bytes, string) are placed in the tail with the correct head offset, exactly as the ABI spec + ethers.js produce.
Encode individual components
AbiEncoder::encodeAddress('0xABCDEF…'); // left-padded to 32 bytes, lowercased AbiEncoder::encodeUint('0xFF'); // accepts hex or decimal; bigint-safe AbiEncoder::encodeUint('115792089237316195423570985008687907853269984665640564039457584007913129639935'); // max uint256 AbiEncoder::encodeBytes('0xdeadbeef'); // length word + right-padded data AbiEncoder::encodeBytes('v1', isString: true); // UTF-8 string as dynamic bytes
Scope
Intentionally narrow — covers the head-only static types and top-level dynamic bytes / string that real EVM contract calls use. Nested dynamic types (bytes[], string[], dynamic tuples) are out of scope.
Requirements
- PHP 8.3+
ext-gmp- amashukov/keccak-php (for the selector hash)
Related packages
| Package | Tier | Purpose |
|---|---|---|
| amashukov/keccak-php | leaf | keccak-256 hashing (selector hash) |
| amashukov/eip1559-tx-signer-php | composite | EIP-1559 raw tx assembly + signing |
| amashukov/rlp-php | leaf | RLP encoding |
| amashukov/secp256k1-php | leaf | ECDSA sign / pubkey derivation |
| amashukov/eth-rpc-client-php | RPC | Ethereum JSON-RPC client |
| amashukov/eth-php | meta | EVM umbrella package |
Quality
- PHPStan level 9.
- php-cs-fixer with the
@PER-CSruleset. - GitHub Actions CI on every push.
- Byte-for-byte parity tests against ethers.js v6
Interface.encodeFunctionData.
License
MIT.