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 first step is to export the reference so that it is visible outside of the 'callee' contract's module. You don’t need to declare this manually; Ink!’s macro system handles it for you. The reference will be named with your contract’s name followed by a Ref suffix. For example, if your contract is named MyContract, the reference will be MyContractRef.

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://testnet.phronscan.io 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