Ether Binder
Set of utilities for interacting with Ethereum and it's smart contracts with PHP, with generating ABI bindings and Ethereum-related types
|
One of main features of EtherBinder is bindings (hence the name). It is alternative of geth's abigen for PHP.
In essence, it creates typed classes that allow you to interact with smart contract on EVM chain. The binding handles for you underlying complexity, mostly abi en/de-coding.
To create binding you need abi json file. If you want to be able to deploy contract, you also need compiled byte code in hex format. The combined format is not supported at the second.
Following examples will use this example ERC20 implementation:
Assuming that you have built above contract and stored it's abi in ./erc20.abi.json
and bytecode in ./erc20.bin.hex
(note: bytecode must be in hex format, not binary blob); and assuming that you have ./src as your project root and want your binding to reside in \Contracts
namespace, you can call:
The used parameters are:
--abi
- path to abi json file--bin
- optional path to bytecode file--fqcn
- fully qualified class name of root class of the binding. Events and tuples will derive their names from this name too. If it points to global namespace, warning will be emitted. Also bear in mind that shel will treat single \
as escape character, so to pass namespace, you need to escape \
with another \
--out
- path to directory (if it doesn't exist, it will be created). Any file that will be generated will be overwritten, but directory itself will not be cleared.Bear in mind that you need to take into account your autoloader setup when writing namespace and directory.
This should result in bindings being generated into ./src/contracts
with main class ./src/contracts/ERC20.php
Currently, event allows parsing event data into appropriate object (to Event
classes). There is a rare case of having events that emit indexed dynamic data such as strings, arrays or tuples. Solidity in such case returns keccak256 hash of such data, not the data itself, making the data itself unrecoverable. If the ABIGen stumbles upon such event, while generating bindings, it will throw NotSupportedException. Such events are not supported. In pinch, it's OK to remove the event from ABI JSON manually, but of course, such events will not be parsed.
If this happens you get exception with explainer and exactly which event and it's type causes the problem, so you can adjust ABI or contract.
Usage code samples will assume you have defined private key and RPC
To deploy contract call static function deployNewCLASS_NAME
. First 2 parameters are always AbstractRpc
and private Key
. Rest of parameters depend on given contract as these are input for it.
In most basic form, the instantiating requires only RPC and contract address:
You can also supply private key and/or fallback address while instantiating. If you supply key, it's address will be used ass fallback, regardless if you set fallback address, or not. Supplying private keys allows binding to sign and send transactions, while fallback address is used for estimations and calls (from field). Not supplying any of these will set this field to null address (0x000...). Depending on contract logic, this may produce invalid estimations or throw exceptions that wouldn't be thrown otherwise (for example reverting due to "insufficient balance")
To supply private key:
Or when you already have contract instantiated:
To remove private key:
To supply fallback address, and get unsigned transactions:
To add fallback on already instantiated contract:
To remove fallback on already instantiated contract:
To read contract, you just need to call function of bound contract object with same name, and pass same params as in solidity.
Tuples are supported and typed
Some functions return multiple variables. To show this, the example solidity code has returnsMultiple
test function.
Write functions work similarly to read functions, but instead return transactions in different states. The state depends on binding state, and you can read more about it in instantiation section. Transaction kind is dynamically determined based on underlying chain (Legacy or London).
If function is payable, first param accepts value wrapped in OOGmp (Ether Binder's big number object), and function params start from second param.
Ether Binder has 3 ways to parse events.
One way is to parse a specific event from Log
Second way is to parse all events from Receipt
Third way is to use filter binding. NOTE: After getting constructed, it does not install filter on RPC, and therefore the events listening didn't start yet. At this point you can additionally configure the object with setFromBlock
and setToBlock
methods to set filter's start and end. To install filter either start fetching with parseFetchNext
or call installFilter
. Note that after doing so, the setFromBlock
and setToBlock
methods will throw exceptions. To set new from / to values, instantiate new filter.
The binding for parameters accepts rpc and contract address, and then the indexed params from event. Event dependent params can be null (to accept any event) or array (to set up OR filter for this variable). If array is provided, all items must be of same type as single item typing, otherwise an exception will be thrown.
fetchNext()
will return next known event, or null if no more events were found
Binding has static variable AbstractContract::$transactionFeesPercentageBump
.
It is flat percentage bump, defaults to 0. For legacy transactions it influences gas price, and for post-London transactions it influences both base fee cap and tip fee. If set to for example 10, it means the gas price will be 110% of what it would be at default.
Calculation is simple: