Step-by-step guide on how to execute Secret Network smart contracts that communicate with each other
In previous sections, we explored the creation of isolated smart contracts within the Secret Network ecosystem. To construct more intricate systems using smart contracts, it is essential to establish effective communication between them. This documentation will delve into the implementation of secure and efficient communication between secret contracts, allowing you to build powerful, interconnected applications on Secret Network.
In this tutorial, we will be designing a simple smart contract that can execute a counter smart contract that is already deployed to the Secret Testnet. Thus, we are working with two smart contracts:
Manager Contract - which executes the Counter Contract
Counter Contract - which is executed by the Manager Contract
By the end of this tutorial, you will understand how to implement the Increment()
function on a Manager smart contract, which increments a counter smart contract by one every time the Increment function is called.
To follow along with this tutorial step-by-step, clone down the Secret Network counter template repo here 😊
We will be designing a Manager Smart Contract which can execute a Counter Contract that is deployed to the Secret testnet. Let's start by creating the message that we want to execute on the counter smart contract. In the src directory (which currently contains contract.rs
, lib.rs
, msg.rs
and state.rs
), create a counter.rs
file and add the following:
CounterExecuteMsg
contains a single function Increment{}.
This is the function we will call to increment the counter contract once we have completed designing our Manager smart contract.
Now, navigate to the msg.rs
file. Replace the existing code with the following:
Here we have an empty InstantiateMsg
, as well as an enum ExecuteMsg
, with a single variant, IncrementCounter
, which contains two strings: contract
and code_hash.
This is the contract address and code hash of the counter contract, which we will be calling from the Manager contract.
What is the code hash?
Unlike other Cosmos chains, Secret Network requires the hash of the smart contract in addition to the address when executing calls to smart contracts.
Contract hashes are what binds a transaction to the specific contract code being called. Otherwise, it would be possible to perform a replay attack in a forked chain with a modified contract that decrypts and prints the input message.
Now, navigate to the contract.rs
file. Replace the execute entry point with the following:
When the IncrementCounter
variant is matched, the function calls the try_increment
function, which contains two fields, contract
and code_hash
. The contract
field is the address of the contract to be incremented, and the code_hash
field is the code hash of the contract. This function contains the logic for incrementing the counter in our counter contract.
Remember the counter.rs file that we created earlier with the CounterExecuteMsg
enum containing Increment{}
? 🤔 Now, in the try_increment()
function, we are going to use a WasmMsg to call Increment{}
in order to increment the counter contract.
In CosmWasm, a WasmMsg
is an enum used to perform actions such as instantiating a new contract, executing a function on an existing contract, or sending tokens to a contract. Here are the main variants of WasmMsg
:
Instantiate
: This variant is used to create a new instance of a Wasm smart contract. It contains the following fields:
code_id
: The ID of the Wasm code to instantiate.
msg
: The message to initialize the contract (usually a JSON object).
funds
: The amount of tokens to send to the contract upon instantiation.
label
: A human-readable label for the new contract instance.
admin
: An optional address that, if provided, will have administrative privileges over the contract instance (e.g., for migration or update purposes).
Execute
: This variant is used to execute a function on an existing Wasm smart contract (and is the variant that we are calling in our contract) It contains the following fields:
contract_addr
: The address of the contract to execute the function on.
msg
: The message to be processed by the contract (usually a JSON object).
funds
: The amount of tokens to send to the contract along with the execution message.
Migrate
: This variant is used to migrate an existing Wasm smart contract to a new code version. It contains the following fields:
contract_addr
: The address of the contract to migrate.
new_code_id
: The ID of the new Wasm code to migrate the contract to.
msg
: The message to initialize the new code version (usually a JSON object).
In the counter.rs file, comment out or delete the existing execute functions and add the following:
The try_increment
function creates a WasmMsg::Execute
message to increment the counter in the specified smart contract and returns a Response
object containing the message and an attribute. When the Response
object is returned by the smart contract's execute
function, the blockchain will execute the WasmMsg::Execute
message, effectively incrementing the counter in the target contract.
Here's a breakdown of the code inside the function:
let exec_msg = CounterExecuteMsg::Increment {};
: This line creates a new CounterExecuteMsg
enum instance with the Increment
variant, which represents a message to increment the counter.
let cosmos_msg = WasmMsg::Execute { ... };
: This block creates a WasmMsg
enum instance with the Execute
variant. The Execute
variant is used to execute our Counter smart contract. The contract_addr
field is set to the contract
parameter, the code_hash
field is set to the code_hash
parameter, the msg
field is set to the binary representation of exec_msg
, and the funds
field is set to an empty vector, indicating no funds are sent with this message.
Ok(Response::new() ... )
: This block constructs a new Response
object with the following:
.add_message(cosmos_msg)
: The cosmos_msg
(a WasmMsg::Execute
instance) is added to the response as a message to be executed after the current contract execution finishes.
.add_attribute("action", "increment")
: An attribute with key "action" and value "increment" is added to the response. Attributes are used to store additional information about the operation and can be useful for indexing and querying transactions.
You can view the completed code repo here which contains the upload, instantiation, and execution functions using secret.js.
Let's focus on how to write the increment function as seen below:
The secret.js function increase_count
sends an increment_counter
message (which is the message we defined in msg.rs
) to our Counter smart contract. This message increments the counter contract via the WasmMsg that we defined in the increase_count
function.
You can call this function by commenting out the other functions in index.js and running node index.js
in the terminal (make sure you are in the secret-messages/manager/node
directory).
Now, in your terminal, navigate to the secret/counter/node
directory. Call the query_count
function to see the current count of the counter contract. After using the manager contract to increment, it should now be incremented by one!
Congratulations, you have now created a Manager smart contract that interacts with a Counter smart contract on Secret Network! By designing a Multi-Contract System, you can enable secure and efficient communication between secret contracts, allowing for more intricate and interconnected decentralized applications.