Getting Started with Abstract — How to create a Modular App (outdated)
Note: this article is outdated and will be updated shortly.
This guide will walk you through the steps to create a CosmWasm smart-contract module using Abstract. You will be able to leverage other modules, interact with OSes, and deploy your contract all from one repository.
Overview
Scaffold Generation
Open a terminal window and run the following command to generate the scaffold app on your machine:
If you don’t yet have it installed you can install it with
cargo install cargo-generate
cargo generate gh:Abstract-OS/app-module-template \
--branch template \
--name <module-name>
The above will clone the template repository into <module-name> which will be the name of your Abstract module.
All message structs will be prefixed with this module name as well. Ex:
ModuleNameInstantiateMsg
. This is to differentiate from the actualInstantiateMsg
(the true entry point) imported fromabstract_os
.
This name will also be the base name for most module-specific variables in the app. From here-on-out, the examples in this guide will be using “balancer”. This name will be the base name for most app-specific variables in the app as mentioned above.
Project Structure
Open the module-name folder, where you will find the following folders:
contracts
: your CosmWasm smart contractsinterfaces
: Rust deployment interfaces for your smart contractsscripts
: scripts for deploying your module to Abstract Version Control and for writing tests
Now that you have a fresh template, you are ready to start writing your module.
cargo fmt # cleanup any template residuals
cargo build
Contract Structure
All the following files are referenced from
contracts/src
directory. If you’ve ever written a CosmWasm smart contract before, this should be pretty standard aside from the contract.rs entry point.
msg.rs
Message declarations for your module. Here you will find the entry-point msg declarations as well as their responses.
error.rs
Human-readable error declarations for the module.
state.rs
The actual state stored on-chain for your smart-contract. Item
and Map
are both from the cw-storage-plus library, which provides powerful storage abstractions.
Commonly-used state like the
Admin
andContract Version
are already provided natively by the Abstract-SDK and exposed to your handlers (details below 👀)
contract.rs
This file serves as the overall entry point into your module. Abstract provides a wrapper around the CosmWasm entry points, and exposes their configuration to you through the AppContract
type. Your module-specific types for error, messages, and receive are then all encapsulated into a single new type: <ModuleName>App.
pub type BalancerApp = AppContract<
BalancerError,
BalancerExecuteMsg,
BalancerInstantiateMsg,
BalancerQueryMsg,
BalancerMigrateMsg,
>;
Further down, we can see that our APP
is an instance of this <ModuleName>App
which declares each of the handlers for instantiate
, query
, execute
, migrate
, and replies
.
const APP: BalancerApp = BalApp::new(MODULE_NAME, MODULE_VERSION)
.with_instantiate(handlers::instantiate_handler)
.with_query(handlers::query_handler)
.with_execute(handlers::execute_handler)
.with_migrate(handlers::migrate_handler)
.with_replies(&[(EXAMPLE_REPLY_ID, handlers::example_reply_handler)])
.with_dependencies(&[EXCHANGE]);
The dependencies
specify which modules your users need to have installed in their OS to interact with the module. By default, the template declares the abstract:dex
module as a dependency.
Finally, a Rust macro takes the type and const declaration of your app to export the actual CosmWasm entry points from your contract.
export_endpoints!(APP, BalancerApp);
handlers
All of the handlers for the messages sent to your contract (declared in msg.rs
) are held within the handlers
directory, and the appropriate messages can be handled via match
statement.
Below is the ExecuteMsg
handler for Equilibrium’s balancer
module.
pub fn execute_handler(
deps: DepsMut,
_env: Env,
info: MessageInfo,
balancer: BalancerApp,
msg: BalancerExecuteMsg,
) -> BalancerResult {
match msg {
BalancerExecuteMsg::Rebalance {} => rebalance(deps, info, balancer),
BalancerExecuteMsg::UpdateAssetWeights { to_add, to_remove } => {
update_asset_weights(deps, info, balancer, to_add, to_remove)
}
BalancerExecuteMsg::UpdateConfig { deviation, dex } => {
update_config(deps, info, balancer, deviation, dex)
}
}
The <ModuleName>App
acts as an adapter between your contract and the Abstract SDK, exposing Abstract Name System, other module functionality, asset value calculation, and much more. Read more in the Abstract docs.
Deployment
Interfaces
Within interfaces
is a template.rs
file that declares the deployment interface for your module. By default, the new
function should be enough to deploy the contract but you can add additional functions to perform more advanced actions. See abstract-boot for more examples.
Scripts
To make your module available for other OSes to install on a local, Testnet, or Mainnet network, you can deploy your module directly from the template repo.
1. Configure Deployment Env
Rename the example env
# in the root
mv .env.example .env
Configure the variables (this example uses uni-5 Juno testnet)
# human-readable name of the chain
CHAIN="juno"
# name of the deployment to be exported
DEPLOYMENT="v0.0.1"
# "local" | "testnet" | "mainnet"
NETWORK="testnet"
# Mnemonic of the account that will be used to deploy the contracts (admin)
TEST_MNEMONIC=""
# Abstract version control address for app deployment
VERSION_CONTROL_ADDRESS=junoxxx
The Abstract version control address can be found on https://beta.abstract.money/deployments (check our discord for when it is available)
2. Configure the Deployment Network
In src/bin/deploy_app.rs
, configure the network to instantiate the daemon to be UNI_5
for Juno testnet or JUNO_1
for Juno mainnet. Other available networks are exported from boot_core
(docs).
use boot_core::{instantiate_daemon_env, networks::juno::{JUNO_DAEMON, UNI_5, JUNO_1}};
pub fn deploy_app() -> anyhow::Result<()> {
let network = UNI_5;
//...
}
3. Deploy to Version Control
Run the following command:
cargo deploy
…and your module will be registered to Abstract Version Control. You can verify this by going to https://beta.abstract.money/ans (soon) and checking the registered modules.
Need help?
Connect with us on Discord! We’d be happy to help you with any questions, feedback, or suggestions.