The eosio::binary_extension type

Let's fully explain what the eosio::binary_extension type is, what it does, and why we need it for contract upgrades in certain situations.

You can find the implementation of eosio::binary_extension in the eosio.cdt repository in file: eosio.cdt/libraries/eosiolib/core/eosio/binary_extension.hpp.

Our primary concern when using this type is when we are adding a new field to a smart contract's data structure that is currently utilized in an eosio::multi_index type (AKA a table), or when adding a new parameter to an action declaration.

By wrapping the new field in an eosio::binary_extension, you are enabling your contract to be backwards compatible for future use. Note that this new field/parameter MUST be appended at the end of a data structure (this is due to implementation details in eosio::multi_index, which relies on the boost::multi_index type), or at the end of the parameter list in an action declaration.

If you don't wrap the new field in an eosio::binary_extension, the eosio::multi_index table will be reformatted in such a way that disallows reads to the former datum; or in an action's case, the function will be uncallable.

But let's see how the eosio::binary_extension type works with a good example.

Take a moment to study this smart contract and its corresponding .abi.

This contract not only serves as a good example to the eosio::binary_extension type, but can also be used as a gateway for developing smart contracts on the eosio protocol.

{"____comment":"This file was generated with eosio-abigen. DO NOT EDIT ","version":"eosio::abi/1.1","types":[],"structs":[{"name":"modifyp","base":"","fields":[{"name":"primary_key","type":"name"},{"name":"n","type":"name"}]},{"name":"modifys","base":"","fields":[{"name":"primary_key","type":"name"},{"name":"n","type":"name"}]},{"name":"printbyp","base":"","fields":[{"name":"primary_key","type":"name"}]},{"name":"printbys","base":"","fields":[{"name":"secondary_key","type":"name"}]},{"name":"regpkey","base":"","fields":[{"name":"primary_key","type":"name"}]},{"name":"structure","base":"","fields":[{"name":"_primary_key","type":"name"},{"name":"_secondary_key","type":"name"}]}],"actions":[{"name":"modifyp","type":"modifyp","ricardian_contract":""},{"name":"modifys","type":"modifys","ricardian_contract":""},{"name":"printbyp","type":"printbyp","ricardian_contract":""},{"name":"printbys","type":"printbys","ricardian_contract":""},{"name":"regpkey","type":"regpkey","ricardian_contract":""}],"tables":[{"name":"table","type":"structure","index_type":"i64","key_names":[],"key_types":[]}],"ricardian_clauses":[],"variants":[]}

Take note of the action regpkey, and the struct structure in con.hpp and con.cpp; the parts of the contract we will be upgrading.

Now, let's upgrade the smart contract by adding a new field to the table and a new parameter to an action while NOT wrapping the new field/parameter in an eosio::binary_extension type and see what happens: