This repository is no longer supported. For an up-to-date guide to set up your local Secret Developer environment and deploy your first contract, use the Secret docs instead.
This repository can be used to master Secret Contract development! In this document you'll find information on setting up a local Secret Network developer testnet (secretdev), learning secret contract basics and shortcuts to improve your development experience and finally build and deploy secret contracts of varying complexity with UIs to create your very own Secret Apps.
To learn more about secret contracts, please visit our documentation page.
Topics covered on this page
- Setup the Local Developer Testnet
- Setup Secret Contracts
- Create Initial Smart Contract
- Deploy Smart Contract to local Testnet
- Instantiate the Smart Contract
- Migrating to Testnet
- Secret Contracts
- SecretJS
- Keplr integration
- Other resources
The developer blockchain is configured to run inside a docker container. Install Docker for your environment (Mac, Windows, Linux).
Open a terminal window and change to your project directory. Then start SecretNetwork, labelled secretdev from here on:
docker run -it --rm \
-p 26657:26657 -p 26656:26656 -p 1337:1337 \
--name secretdev enigmampc/secret-network-sw-dev
NOTE: The secretdev docker container can be stopped by CTRL+C
At this point you're running a local SecretNetwork full-node. Let's connect to the container so we can view and manage the secret keys:
NOTE: In a new terminal
docker exec -it secretdev /bin/bash
The local blockchain has a couple of keys setup for you (similar to accounts if you're familiar with Truffle Ganache). The keys are stored in the test
keyring backend, which makes it easier for local development and testing.
secretcli keys list --keyring-backend test
exit
when you are done
In order to setup Secret Contracts on your development environment, you will need to:
- install Rust (you don't need to be a Rust expert to build Secret Contracts and you can check out the Rust book, rustlings course, examples to learn more at https://www.rust-lang.org/learn)
- install the Rust dependencies
- create your first project
The Rust dependencies include the Rust compiler, cargo (package manager), toolchain and a package to generate projects (you can check out the Rust book, rustlings course, examples and more at https://www.rust-lang.org/learn).
- Install Rust
More information about installing Rust can be found here: https://www.rust-lang.org/tools/install.
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env
- Add rustup target wasm32 for both stable and nightly
rustup default stable
rustup target list --installed
rustup target add wasm32-unknown-unknown
rustup install nightly
rustup target add wasm32-unknown-unknown --toolchain nightly
- If using linux, install the standard build tools:
apt install build-essential
- Run cargo install cargo-generate
Cargo generate is the tool you'll use to create a smart contract project (https://doc.rust-lang.org/cargo).
cargo install cargo-generate --features vendored-openssl
To create the smart contract you'll:
- generate the initial project
- compile the smart contract
- run unit tests
- optimize the wasm contract bytecode to prepare for deployment
- deploy the smart contract to your local SecretNetwork
- instantiate it with contract parameters
cargo generate --git https://github.com/enigmampc/secret-template --name mysimplecounter
The git project above is a cosmwasm smart contract template that implements a simple counter. The contract is created with a parameter for the initial count and allows subsequent incrementing.
Change directory to the project you created and view the structure and files that were created.
cd mysimplecounter
The generate creates a directory with the project name and has this structure:
Cargo.lock Developing.md LICENSE Publishing.md examples schema tests
Cargo.toml Importing.md NOTICE README.md rustfmt.toml src
Use the following command to compile the smart contract which produces the wasm contract file.
cargo wasm
RUST_BACKTRACE=1 cargo unit-test
The integration tests are under the tests/
directory and run as:
cargo integration-test
We can also generate JSON Schemas that serve as a guide for anyone trying to use the contract, to specify which arguments they need.
Auto-generate msg schemas (when changed):
cargo schema
Before deploying or storing the contract on a testnet, you need to run the secret contract optimizer.
docker run --rm -v "$(pwd)":/contract \
--mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \
--mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \
enigmampc/secret-contract-optimizer
The contract wasm needs to be optimized to get a smaller footprint. Cosmwasm notes state the contract would be too large for the blockchain unless optimized. This example contract.wasm is 1.8M before optimizing, 90K after.
This creates a zip of two files:
- contract.wasm
- hash.txt
# First lets start it up again, this time mounting our project's code inside the container.
docker run -it --rm \
-p 26657:26657 -p 26656:26656 -p 1337:1337 \
-v $(pwd):/root/code \
--name secretdev enigmampc/secret-network-sw-dev
Upload the optimized contract.wasm.gz:
docker exec -it secretdev /bin/bash
cd code
secretcli tx compute store contract.wasm.gz --from a --gas 1000000 -y --keyring-backend test
List current smart contract code
secretcli query compute list-code
[
{
"id": 1,
"creator": "secret1zy80x04d4jh4nvcqmamgjqe7whus5tcw406sna",
"data_hash": "D98F0CA3E8568B6B59772257E07CAC2ED31DD89466BFFAA35B09564B39484D92",
"source": "",
"builder": ""
}
]
At this point the contract's been uploaded and stored on the testnet, but there's no "instance."
This is like discovery migrate
which handles both the deploying and creation of the contract instance, except in Cosmos the deploy-execute process consists of 3 steps rather than 2 in Ethereum. You can read more about the logic behind this decision, and other comparisons to Solidity, in the cosmwasm documentation. These steps are:
- Upload Code - Upload some optimized wasm code, no state nor contract address (example Standard ERC20 contract)
- Instantiate Contract - Instantiate a code reference with some initial state, creates new address (example set token name, max issuance, etc for my ERC20 token)
- Execute Contract - This may support many different calls, but they are all unprivileged usage of a previously instantiated contract, depends on the contract design (example: Send ERC20 token, grant approval to other contract)
To create an instance of this project we must also provide some JSON input data, a starting count.
INIT='{"count": 100000000}'
CODE_ID=1
secretcli tx compute instantiate $CODE_ID "$INIT" --from a --label "my counter" -y --keyring-backend test
With the contract now initialized, we can find its address
secretcli query compute list-contract-by-code 1
Our instance is secret18vd8fpwxzck93qlwghaj6arh4p7c5n8978vsyg
We can query the contract state
CONTRACT=secret18vd8fpwxzck93qlwghaj6arh4p7c5n8978vsyg
secretcli query compute query $CONTRACT '{"get_count": {}}'
And we can increment our counter
secretcli tx compute execute $CONTRACT '{"increment": {}}' --from a --keyring-backend test
Holodeck is the official Secret Network testnet. To deploy your contract to the testnet follow these steps:
- Install and configure the Secret Network Light Client
- Get some SCRT from the faucet
- Store the Secret Contract on Holodeck
- Instantiate your Secret Contract
If you don't have the latest secretcli
, using these steps to download the
CLI and add its location to your PATH.
Before deploying your contract make sure it's configured to point to an existing RPC node. You can also use the testnet bootstrap node. Set the chain-id
to
holodeck-2
. Below we've also got a config setting to point to the test
keyring backend which allows you to interact with the testnet and your contract
without providing an account password each time.
secretcli config node http://bootstrap.secrettestnet.io:26657
secretcli config chain-id holodeck-2
secretcli config trust-node true
secretcli config keyring-backend test
NOTE: To reset your keyring-backend
, use secretcli config keyring-backend os
.
Create a key for the Holodeck testnet that you'll use to get SCRT from the faucet, store and instantiate the contract, and other testnet transactions.
secretcli keys add <your account alias>
This will output your address, a 45 character-string starting with secret1...
. Copy/paste it to get some testnet SCRT from
the faucet.
Continue when you have confirmed your account has some SCRT in it.
Next upload the compiled, optimized contract to the testnet.
secretcli tx compute store contract.wasm.gz --from <your account alias> -y --gas 1000000 --gas-prices=1.0uscrt
The result is a transaction hash (txhash). Query it to see the code_id
in the logs, which you'll use to create an instance of the contract.
secretcli query tx <txhash>
To create an instance of your contract on Holodeck set the CODE_ID
value below to the code_id
you got by querying the txhash.
INIT='{"count": 100000000}'
CODE_ID=<code_id>
secretcli tx compute instantiate $CODE_ID "$INIT" --from <your account alias> --label "my counter" -y
You can use the testnet explorer Transactions tab to view the contract instantiation.
The source directory (src/
) has these files:
contract.rs lib.rs msg.rs state.rs
The developer modifies contract.rs
for contract logic, contract entry points are init
, handle
and query
functions.
init
in the Counter contract initializes the storage, specifically the current count and the signer/owner of the instance being initialized.
We also define handle
, a generic handler for all functions writing to storage, the counter can be incremented and reset. These functions are provided the storage and the environment, the latter's used by the reset
function to compare the signer with the contract owner.
Finally we have query
for all functions reading state, we only have query_count
, returning the counter state.
The rest of the contract file is unit tests so you can confidently change the contract logic.
The state.rs
file defines the State struct, used for storing the contract data, the only information persisted between multiple contract calls.
The msg.rs
file is where the InitMsg parameters are specified (like a constructor), the types of Query (GetCount) and Handle[r] (Increment) messages, and any custom structs for each query response.
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct InitMsg {
pub count: i32,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "lowercase")]
pub enum HandleMsg {
Increment {},
Reset { count: i32 },
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "lowercase")]
pub enum QueryMsg {
// GetCount returns the current count as a json-encoded number
GetCount {},
}
// We define a custom struct for each query response
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct CountResponse {
pub count: i32,
}
Use this link to a see a sample voting contract and a line by line description of everything you need to know
Unit tests are coded in the contract.rs
file itself:
#[cfg(test)]
mod tests {
use super::*;
use cosmwasm::errors::Error;
use cosmwasm::mock::{dependencies, mock_env};
use cosmwasm::serde::from_slice;
use cosmwasm::types::coin;
#[test]
fn proper_initialization() {
let mut deps = dependencies(20);
let msg = InitMsg { count: 17 };
let env = mock_env(&deps.api, "creator", &coin("1000", "earth"), &[]);
// we can just call .unwrap() to assert this was a success
let res = init(&mut deps, env, msg).unwrap();
assert_eq!(0, res.messages.len());
// it worked, let's query the state
let res = query(&deps, QueryMsg::GetCount {}).unwrap();
let value: CountResponse = from_slice(&res).unwrap();
assert_eq!(17, value.count);
}
Secret Toolkit is a collection of Rust packages that contain common tools used in development of Secret Contracts running on the Secret Network.
Secret Toolkit contains very helpful tools that can be used to call other contracts from your own. Here is a guide on how to call other contracts from your own using the InitCallback
, HandleCallback
, and Query
traits defined in the utils package.
If you are specifically wanting to call Handle functions or Queries of SNIP-20 token contracts, there are individually named functions you can use to make it even simpler than using the generic traits. These are located in the SNIP-20 package.
Use this link for a sealed-bid (secret) auction contract that makes use of SNIP-20 and a walkthrough of the contract
Now that we have mastered secret contract development, let's see how to connect a secret contract to a front-end using secret.js and how to add wallet functionality using Keplr.
Secret.js is how users interact with Secret Network. Secret.js is mostly based on CosmWasm.js. While secret.js documents are being updated, you can refer to CosmWasm.js documents
Keplr is the web wallet users need to interact with Secret Applications. Please use this link for sample implementations of Keplr and Secret.js
Secret Contracts are based on CosmWasm v0.10, but they have additional privacy properties that can only be found on Secret Network.Secret Contract developers must always consider the trade-off between privacy, user experience, performance and gas usage. Please use this link to learn more about the privacy model of secret contracts.
Visit this link for all tutorials about Secret Network
Smart Contracts in the Secret Network based based on CosmWasm. Therefore, for troubleshooting and additional context, CosmWasm documentation may be very useful. Here are some of the links we relied on in putting together this guide: