Using references

The Bulletin Board example uses a reference to the HighlightedPosts contract to delete highlights. In this section, we will take a look at three basic building blocks of cross-contract calls.

Exporting a reference

The very first thing we need to do is to export the reference to make it visible outside of the 'callee' contract's module. Note that you don't have to declare it: this is already done, courtesy of Ink!'s macro system. The reference's name will be your contract's name with a Ref suffix, so in our example this will be:

highlighted_posts/lib.rs
pub use highlighted_posts::HighlightedPostsRef;

Please remember that the export above needs to be placed at the top level and not inside the highlighted_posts module.

So far so good, let's switch the files and start using the reference!

Initializing the reference

A very important thing to note is that the reference needs to be initialized with a code hash of a specific contract. That is: for this to work, the HighlightedPosts contract needs to be already deployed on the chain and you need to know its code hash!

A natural question would be: how to get the said code hash?

The easiest method is to write it down during contract deployment. cargo contract upload will output the hash once it's done. Similarly, the Contracts UI will show it to you during the upload.

However, if your contract is already on the chain and you know its address (account), you can go to https://test.azero.dev/#/chainstate (for Testnet, or https://azero.dev/#/chainstate for Mainnet) and run the contracts::contractInfoOf method, supplying the account as the argument. The code hash will be one of the fields of the resulting response.

Assuming we were able to get ourselves a code hash, we will need to use it to initialize the reference. In our example, it will look like this:

bulletin_board/lib.rs
let highlighted_posts_board_ref = HighlightedPostsRef::new()
                .code_hash(highlighted_posts_board_hash)
                .salt_bytes([version.to_le_bytes().as_ref(), Self::env().caller().as_ref()].concat())
                .endowment(0)
                .instantiate();

The salt_bytes can be pretty much anything you want: here we set it to the concatenation of the version and the caller's account. The endowment is a value transferred along with the call and, counter-intuitively, is required by the API (even though we don't actually transfer anything).

After constructing the reference, we will need to save it in the contract's storage struct. The next paragraph will diverge slightly from the Bulletin Board example for the sake of simplicity (the Bulletin Board converts the ref to an account id because it uses it for multiple things).

Calling methods on the reference

Let's assume we have the reference saved to our storage struct as highlighted_posts_board. Now, in order to call the method, we use the familiar syntax:

bulletin_board/lib.rs
self.highlighted_posts_board.delete_by_author(some_account);

Just like that! Of course, remember to handle the Result of this call. If you want to check if the compiler truly type-checks this call, you can make a typo in the method or pass a param that doesn't make sense, like the number 42.

Last updated