Launch a dApp on Injective

Launch a dApp on Injective
Launch a dApp on Injective
     

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 state
  • execute(): called when a user wants to invoke a method on the smart contract
  • query(): 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 that execute() messages will interact by increasing or resetting it.
  • owner, the sender address of the MsgInstantiateContract, 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