EOS Contracts — Deploying and Using Contracts without On-Chain ABI

Jonathan LEI
Coinmonks
3 min readAug 24, 2018

--

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!

Get Best Software Deals Directly In Your Inbox

--

--

Jonathan LEI
Coinmonks

Blockchain protocol engineer, blockchain smart contract reverse engineer