Get started developing on Secret Network using the public testnet and secret.js
In the previous section, we learned how to upload, execute, and query a Secret Network smart contract using SecretCLI and LocalSecret. Now we are going to repeat this process, but we will be uploading, executing, and querying our smart contract on a public testnet using Secret.js.
Secret.js is a JavaScript SDK for writing applications that interact with the Secret Network blockchain.
Key features include:
Written in TypeScript and provided with type definitions.
Provides simple abstractions over core data structures.
Supports every possible message and transaction type.
Exposes every possible query type.
Handles input/output encryption/decryption for Secret Contracts.
Works in Node.js, modern web browsers and React Native.
By the end of this tutorial, you will learn how to:
Add the Secret Testnet to your keplr wallet (and fund your wallet with testnet tokens)
Optimize and compile your Secret Network smart contract
Upload and Instantiate your contract using Secret.js
Execute and Query your contract using Secret.js
Let's get started!
To follow along with the guide, we will be using npm,
git,
make,
rust,
and docker
. Follow the Setting Up Your Environment guide here if you need any assistance.
Additionally, you will need to have the Secret Testnet configured with your keplr wallet and also fund it with testnet tokens. Learn how to configure and fund your keplr wallet here.
We will be working with a basic counter contract, which allows users to increment a counter variable by 1 and also reset the counter. If you've never worked with smart contracts written in Rust before that is perfectly fine.
The first thing you need to do is clone the counter contract from the Secret Network github repo. Secret Network developed this counter contract template so that developers have a simple structure to work with when developing new smart contracts, but we're going to use the contract exactly as it is for learning purposes.
Go to the folder in which you want to save your counter smart contract and run:
When you run the above code, it will name your contract folder directory "my-counter-contract". But you can change the name by altering the text that follows the --name
flag.
Start by opening the my-counter-contract
project folder in your text editor. If you navigate to my-counter-contract/src
you will seecontract.rs, msg.rs, lib.rs, and state.rs
—these are the files that make up our counter smart contract. If you've never worked with a Rust smart contract before, perhaps take some time to familiarize yourself with the code, although in this tutorial we will not be going into detail discussing the contract logic.
In your root project folder, ie my-counter-contract
, create a new folder. For this tutorial I am choosing to call the folder node
.
In your my-counter-contract/node
folder, create a new javascript file––I chose to name mineindex.js
.
Run npm init -y
to create a package.json file.
Add "type" : "module"
to your package.json file.
Install secret.js and dotenv: npm i secretjs dotenv
Create a .env
file in your node
folder, and add the variable MNEMONIC
along with your wallet address seed phrase, like so:
Never use a wallet with actual funds when working with the testnet. If your seed phrase were pushed to github you could lose all of your funds. Create a new wallet that you use solely for working with the testnet.
Congrats! 🎉 You now have your environment configured to develop with secret.js.
Since we are not making any changes to the contract code, we are going to compile it exactly as it is. To compile the code, run make build
in your terminal. This will take our Rust code and build a Web Assembly file that we can deploy to Secret Network. Basically, it just takes the smart contract that we've written and translates the code into a language that the blockchain can understand.
Run make build
from the terminal, or just GUI it up -
This will create a contract.wasm
and contract.wasm.gz
file in the root directory.
While we could upload this contract wasm file to the blockchain exactly as it is, instead we are going to follow best practices and optimize the wasm file. This just means we are going to reduce the size of the file so that it costs less gas to upload, which is critical when you eventually upload contracts to mainnet. Make sure you have docker installed and then run the following code:
Optimize compiled wasm
You should now have an optimized contract.wasm.gz
file in your root directory, which is ready to be uploaded to the blockchain! Also note that the optimizer should have removed the contract.wasm
file from your root directory 👍
Now that we have a working contract and an optimized wasm file, we can upload it to the blockchain and see it in action. This is called storing the contract code. We are using a public testnet environment, but the same commands apply no matter which network you want to use - local, public testnet, or mainnet.
Start by configuring your index.js
file like so:
You now have secret.js imported, a wallet
variable that points to your wallet address, and a contract_wasm
variable that points to the smart contract wasm file that we are going to upload to the testnet. The next step is to configure your Secret Network Client, which is used to broadcast transactions, send queries and receive chain information.
Note the chainId and the url that we are using. This chainId and url are for the Secret Network testnet. If you want to upload to LocalSecret or Mainnet instead, all you would need to do is swap out the chainId and url. A list of alternate API endpoints can be found here.
When connecting to a local node, you must indicate a port, which is 1317
Now console.log(secretjs)
and run node index.js
in the terminal of your my-counter-contract/node
folder to see that you have successfully connected to the Secret Network Client:
To upload a compiled contract to Secret Network, you can use the following code:
Run node index.js
in your terminal to call the upload_contract()
function. Upon successful upload, a codeId and a contractCodeHash will be logged in your terminal:
Be sure to save the codeId and contractCodeHash as variables so you can access them in additional function calls.
In the previous step, we stored the contract code on the blockchain. To actually use it, we need to instantiate a new instance of it. Comment out upload_contract()
and then add instantiate_contract()
.
Note that there is an initMsg
which contains count:0
. You can make the starting count whatever you'd like (as well as the contract label
).
Run node index.js
in your terminal to call the instantiate_contract()
function. Upon successful instantiation, a contractAddress will be logged in your terminal:
Be sure to save the contractAddress as a variable so you can access it in additional function calls.
Congrats 🎉! You just finished uploading and instantiating your first contract on a public Secret Network testnet! Now it's time to see it in action!
The way you interact with contracts on a blockchain is by sending contract messages. A message contains the JSON description of a specific action that should be taken on behalf of the sender, and in most Rust smart contracts they are defined in the msg.rs
file.
In our msg.rs
file, there are two enums: ExecuteMsg
, and QueryMsg
. They are enums because each variant of them represents a different message which can be sent. For example, the ExecuteMsg::Increment
corresponds to the try_increment
message in our contract.rs
file.
In the previous section, we compiled, uploaded and instantiated our counter smart contract. Now we are going to query the contract and also execute messages to update the contract state. Let's start by querying the counter contract we instantiated.
A Query Message is used to request information from a contract; unlike execute messages
, query messages do not change contract state, and are used just like database queries. You can think of queries as questions you can ask a smart contract.
Let's query our counter smart contract to return the current count. It should be 0, because that was the count we instantiated in the previous section. We query the count by calling the Query Message get_count {}
, which is defined in our msg.rs file. Comment out instantiate_contract()
and then add try_query_count()
.
The query returns:
Great! Now that we have queried the contract's starting count, let's execute an increment{}
message to modify the contract state.
An Execute Message is used for handling messages which modify contract state. They are used to perform contract actions.
Did you know? Messages aren't free! Each transaction costs a small fee, which represents how many resources were required to complete it. This cost is measured in gas units.
The counter contract consists of two execute messages: increment{}
, which increments the count by 1, and reset{}
, which resets the count to any i32
you want. The current count is 0, let's call the Execute Message increment{}
to increase the contract count by 1.
Nice work! Now we can query the contract once again to see if the contract state was successfully incremented by 1. The query returns:
Way to go! You have now successfully interacted with a Secret Network smart contract on the public testnet!
Congratulations! You completed the tutorial and successfully compiled, uploaded, deployed and executed a Secret Contract! The contract is the business logic that powers a blockchain application, but a full application contains other components as well. If you want to learn more about Secret Contracts, or explore what you just did more in depth, feel free to explore these awesome resources:
Secret University counter contract breakdown - explains the counter contract in depth
Millionaire's problem breakdown - explains how a Secret Contract works
CosmWasm Documentation - everything you want to know about CosmWasm
Secret.JS - Building a web UI for a Secret Contract