EOS Contracts — Deploying and Using Contracts without On-Chain ABI
If you’re a smart contract developer, then you must have at least heard of ABI. ABI stands for Application Binary Interface, which is used for converting human-readable data formats (i.e. JSON) into plain binary format for maximum efficiency.
Back in Ethereum, ABIs were published and distributed off-chain. For instance, Etherscan provides ABIs for verified contracts so that users can easily interact with them.
Recognizing the importance of ABI, EOS has decided to put it on-chain. For example, the following is an extract of the ABI for system contract eosio.token
:
We can learn from the ABI that the action transfer
uses a parameter type of transfer
, which contains 4 fields: from
, to
, quantity
and memo
. This way, when users invoke the contract interface, the front-end software can serialize the parameters provided by the user given the information contained in the ABI.
In fact, whenever you use eosjs
for contract interaction, it always first issue a get_abi
request followed by the actual push_transaction
request.
However, while it’s convenient to have the ABI on-chain, such a mechanism also brings along un-desired side-effects. For private contracts, we want as least information about them being published as possible, and publishing the ABI is definitely not the best way to do it.
Note that you can still decipher a compiled contract without using the ABI (i.e. decompilation), but having the ABI published makes this a lot easier.
Thus, I’m introducing a way to publish the contract code only, and to still be able to interact with it.
Deploying the Contract
By default, publishing contracts with the command cleos set contract
would publish the ABI file together with the contract. There’s no way to change this behavior as shown by the cleos
source code:
That’s why we’re using eosjs
, the official JS library for EOS.
For basic instructions on using the library, please refer to the official GitHub documentation for help. I’m assuming you’ve successfully created a working eos
object.
First, read the compiled contract files:
Then, only call setcode
when deploying the code:
As you shall see, the contract is deployed successfully without uploading the ABI.
Invoking the Contract
However, the contract is not immediately usable right away. An error would be thrown when we use eos.transaction
to interact with the contract, as there’s no ABI information available. We can load the ABI locally by using:
Yet the transaction would still fail! This is because everything before a transaction is made, eos
automatically fires a get_abi
request and overwrites the local version. To effectively cache ABI, we need an eos
object that’s not capable of accessing the blockchain.
But then here comes another problem. There’s a header in every EOS transaction, which can only be automatically obtained from the blockchain. If we just create a disconnected eos
object, it won’t even be able to construct the transaction.
Fortunately, we can first obtain the header from the normal eos
object, pass it to a new object coldEos
, cache the ABI in coldEos
, use coldEos
for transaction generation and signature, and finally, use eos
again for pushing the signed transaction onto the network.
We’d first need to define a function for fetching transaction header:
Then, we can create a coldEos
object and use it for signing the transaction. We’re invoking a contract interface called myaction
with a string as parameter:
Finally, we broadcast the signed transaction with the eos
object:
The entire process has been a little bit complicated, but we finally managed to deploy a contract without ABI and to successfully interact with it.
Please follow me on Medium to learn more about blockchain and smart contracts!