Developers are at the heart of the Injective ecosystem. With a diverse range of dApps spanning Web3 finance, Injective offers something for everyone. If you’ve been following the Injective ecosystem and want to learn more about creating your first dApp on Injective, this guide is for you.
In this guide, we will cover downloading the injectived
binaries, creating your first smart contract, interacting with it via the command line interface (CLI), developing a frontend, and deploying it on testnet.
Installing injectived
injectived
is the CLI tool used to interact with the Injective network. You can use the injectived
CLI to compile, deploy, and interact with Injective smart contracts, run your own node, and more. Before installing it, ensure you have rustup
along with recent versions of rustc
and cargo
installed. Currently, testing can be done with Rust v1.58.1+.
Additionally, you need to install the wasm32-unknown-unknown
target and the cargo-generate
Rust crate.
You can check versions via the following commands:
rustc --version
cargo --version
rustup target list --installed
If wasm32
is not listed, run this command:
rustup target add wasm32-unknown-unknown
To install cargo-generate
, run:
cargo install cargo-generate
To install injectived
on your machine, you have two options: install it from the binary or from source. For complete instructions on both methods, see Install Injectived.
Your First Smart Contract
A smart contract can be considered an instance of a singleton object whose internal state is persisted on the blockchain. Users can trigger state changes or query the contract's state by sending JSON messages. These JSON messages are different from Injective messages such as MsgSend
and MsgExecuteContract
.
As a smart contract developer, your job is to define three functions that compose your smart contract's interface:
instantiate()
: a constructor called during contract instantiation to provide the initial stateexecute()
: called when a user wants to invoke a method on the smart contractquery()
: called when a user wants to retrieve data from the smart contract
In our sample counter contract, we will implement one instantiate
, one query
, and two execute
methods.
Counter Template
In your working directory, launch your smart contract with the recommended folder structure and build options by running the following commands:
cargo generate --git https://github.com/CosmWasm/cw-template.git --branch 1.0 --name my-first-contract
cd my-first-contract
This template will provide you with the basic boilerplate and structure for a smart contract. In the src/contract.rs file you will find that the standard CosmWasm entry points instantiate(), execute(), and query() are properly exposed and connected.
State
State handles the database where smart contract data is stored and accessed.
The starting template has the following basic state, a singleton struct State
containing:
count
, a 32-bit integer thatexecute()
messages will interact by increasing or resetting it.owner
, the sender address of theMsgInstantiateContract
, which determines if certain execution messages are permitted.
InstantiateMsg
InstantiateMsg
is provided to the contract when a user instantiates a contract on the blockchain through a MsgInstantiateContract
. This message supplies the contract with its configuration as well as its initial state.
On Injective, uploading a contract's code and instantiating a contract are separate events, unlike on Ethereum. This separation allows a small set of vetted contract archetypes to exist as multiple instances sharing the same base code, but be configured with different parameters (imagine one canonical ERC20, and multiple tokens that use its code).
ExecuteMsg
ExecuteMsg
is a JSON message passed to the execute()
function through a MsgExecuteContract
. Unlike InstantiateMsg
, ExecuteMsg
can exist as several different types of messages to account for the various types of functions that a smart contract can expose to users. The execute()
function demultiplexes these messages to the appropriate message handler logic.
We have two ExecuteMsgs
: Increment
and Reset
.
Increment
has no input parameter and increases the value of count by 1.Reset
takes a 32-bit integer as a parameter and resets the value of count to the input parameter.
QueryMsg
The GetCount
query message has no parameters and returns the count value.
Unit test
Unit tests should be run as the first line of assurance before deploying the code on-chain. They are quick to execute and can provide helpful backtraces on failures with the RUST_BACKTRACE=1
flag:
cargo unit-test // run this with RUST_BACKTRACE=1 for helpful backtraces
You can find the unit test implementation at src/contract.rs
Building the Contract
Now that we understand and have tested the contract, we can run the following command to build the contract. This will check for any preliminary errors before we optimize the contract in the next step.
cargo wasm
Next, we must optimize the contract in order to ready the code for upload to the chain.
CosmWasm has rust-optimizer, an optimizing compiler that produces a small and consistent build output. The easiest method to use the tool is through a published Docker image—check here for the latest x86 version, or here for the latest ARM version. With Docker running, use the following command to mount the contract code to /code
and optimize the output (you can use an absolute path instead of $(pwd)
if you don't want to cd to the directory first):
For x86:
docker run --rm -v "$(pwd)":/code \
--mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \
--mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \
cosmwasm/rust-optimizer:0.12.12
For ARM64:
docker run --rm -v "$(pwd)":/code \
--mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \
--mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \
cosmwasm/rust-optimizer-arm64:0.12.12
This produces an artifacts
directory with a PROJECT_NAME.wasm
and a checksums.txt
file containing the SHA256
hash of the Wasm file. The Wasm file is compiled deterministically, meaning anyone else running the same Docker image on the same Git commit should get an identical file with the same SHA256
hash.
Upload the Wasm Contract
As mentioned above, Wasm smart contracts are uploaded via the injectived
CLI. To proceed, initialize and fund an account with test funds (see here for instructions). Once that is done, enter the following command to upload the contract:
# inside the "injective-core-staging" container, or from the contract directory if running injectived locally
yes 12345678 | injectived tx wasm store artifacts/my_first_contract.wasm \
--from=$(echo $INJ_ADDRESS) \
--chain-id="injective-888" \
--yes --fees=1000000000000000inj --gas=2000000 \
--node=https://k8s.testnet.tm.injective.network:443
Next, enter your address on the Injective Testnet Explorer and look for a transaction with the txhash
returned from storing the code on-chain. The transaction type should be MsgStoreCode
.
And voilà! You have successfully created and uploaded your first smart contract on Injective. For more information and context around each step, see here.
Next, let us build a front end to interact with this contract from a UI.
Build a Front End
While it is possible to interact with your contract through the Injective CLI, this is not ideal for most dApp users. A web UI can provide a much better experience. A web UI can provide a much better experience. Rather than sending transaction messages through injectived
, we can abstract away the complexity and provide the user with two buttons—one to increment the count and one to reset the count.
For example, see the counter website example. A high level guide on developing the front end using Vue and the Injective TypeScript SDK can be found in injective-simple-sc-counter/nuxt. For a React implementation, see injective-simple-sc-counter-ui/next.
Now, interacting with the contract is as simple as clicking a button and signing with a wallet such as MetaMask (ensure the account is set to Ethereum Goerli Testnet to avoid a chain ID mismatch error).
And just like that, you have built your first dApp on Injective!
For further reading, visit the Injective docs site for additional information, best practices, and the latest updates on Injective. If you have any questions, join the Injective developer Telegram group. Happy building ninjas!
About Injective
Injective is a lightning fast interoperable layer one blockchain optimized for building the premier Web3 finance applications. Injective provides developers with powerful plug-and-play modules for creating unmatched dApps. INJ is the native asset that powers Injective and its rapidly growing ecosystem. Injective is incubated by Binance and is backed by prominent investors such as Jump Crypto, Pantera and Mark Cuban.
Website | Telegram | Discord | Blog | Twitter | Learn | Youtube | Facebook|LinkedIn | Reddit |Instagram | Orbit Newsletter