Introduction
Hi and welcome to Abstract, the interchain CosmWasm development platform!
Abstract’s CosmWasm framework is your gateway to taking amazing ideas from concept to reality. Whether you’re a coding genius or just getting started, Abstract has got your back! By building composable modules you’ll be crafting scalable masterpieces in no time.
Prefer to watch a video? Check out our video library!
Abstract in a Nutshell
Abstract is your one-stop solution for streamlined CosmWasm smart-contract development. We provide an integrated smart-contract framework, continuous deployment tools, robust data management solutions for both on-chain and off-chain needs, and top-tier infrastructure-as-a-service (IaaS) offerings.
Our products are designed to be composable, allowing you to re-use the components you need to build your applications. While Abstract aims to simplify the development experience, it functions as a powerful tool, enabling you to innovate with less effort.
Coming from 👾EVM👾 ? Be sure to read up on CosmWasm and its differences from EVM in the CosmWasm section.
Why Build with Abstract?
Building decentralized applications is hard! We know this because we’ve been there. We’ve spent countless hours building applications on the Cosmos ecosystem, and we’ve created Abstract with all the lessons we learned along the way to make it easier and faster to build for you.
-
Based on CosmWasm 🌟: The Abstract SDK is built on top of the CosmWasm smart-contract framework, which has been battle-tested and proven to be secure and reliable. This also means that you can leverage the existing tooling and community to accelerate your development process.
-
Chain-Agnostic 🌐: The Abstract platform can support any CosmWasm-based network, even outside the Cosmos ecosystem, giving you the flexibility to choose the best-suited platform for your dApp.
-
Modular Design 🧩: Abstract’s modular architecture allows you to choose from a curated library of smart-contract or develop your own using the Abstract SDK, enabling rapid development and customization. Think of Abstract as a lego set for building decentralized applications, connect the pieces you need and voilà!.
-
Custom Governance 🗳️: All Abstract Accounts can be governed by any entity tailored to your application’s specific requirements. Currently, the Console supports soverign, cw3-flex-multisig, and DaoDao-governed Accounts.
-
Development Tooling 🛠: Abstract offers a vast integration testing library, enabling you to quickly increase test coverage and ensure the reliability of your dApps.
-
Version Management 🔄: Abstract simplifies the process of managing smart-contract versioning, ensuring you can quickly release and access new features and bug-fixes.
-
Ecosystem Integrations 🌱: Abstract is designed to work seamlessly with protocols, projects, and services in the ever-expanding IBC ecosystem; from DEXes, order-books, NFT-marketplaces, to wallets, we got it all!. If you want to know more about our integrations, check out the Integrations section.
Abstract Products
-
CW-Orchestrator: A scripting tool crafted to simplify interactions with CosmWasm smart contracts. It offers macros that generate type-safe interfaces, promoting code readability, and reducing testing and deployment overhead.
-
Abstract JS: A comprehensive JavaScript library designed for easy integration with on-chain Abstract functions. It caters to both developers aiming to embed blockchain functionalities in their apps and enthusiasts exploring the Abstract framework.
-
Abstract App Template: A foundational template for developing apps within the Abstract framework. It provides the essentials for building and integrating new Abstract Apps, facilitating both frontend and smart contract templates.
-
Abstract Testing: A testing utility within the Abstract framework. It focuses on providing mock data creation, querying, and robust unit testing functionalities to ensure the correctness of the framework’s components.
These products collectively support developers in building, testing, and deploying CosmWasm-based applications with enhanced efficiency and security. For more information about Abstract products, please refer to the individual pages of each product.
If you have any questions or ideas you want to discuss about our products, please contact us on Discord.
How to Navigate the Docs
You can read the documentation in the order it is presented, or you can jump to the section that interests you the most! We also have a section with code tutorials that will help you get started with the Abstract SDK if you are more of a hands-on.
Here are some useful links to get you started:
Use Cases
How can you use Abstract? The limit is your imagination, we can’t wait to see what you build! From DeFi applications, to NFT marketplaces, to DAOs, Abstract is the perfect tool to build your next big idea.
See how others are using Abstract in the Use Cases section.
Help and Support
If you feel lost or have any doubts along the way, please reach out to us! We are here to help you!
Want to make Abstract better?
We are always looking for ways to improve Abstract and welcome everybody to contribute to the project. Look at the Contributing & Community section if you want to get involved.
Links
Prerequisites
In this section, we’ll cover the prerequisites for using the Abstract Platform and its products.
Rust
The Abstract SDK is built on top of the CosmWasm smart-contract framework, which is written in Rust. Therefore, it’s important to have a basic understanding of Rust.
Rust is a modern and user-friendly programming language that shines in both performance and safety. Its innovative features allow developers to write reliable and efficient code, making it a great choice for smart contracts.
Learn more about Rust here, or learn by doing the rustlings.
Already a CosmWasm expert? Jump to the next page to learn about the Abstract SDK!
CosmWasm
Some of the products such us the Abstract SDK and CW-Orchestrator are built on top of the CosmWasm smart-contract framework. Abstract simplifies complexity within CosmWasm, but it’s still important to understand the basics.
If you are already familiar with CosmWasm, you can skip this section.
What’s CosmWasm?
CosmWasm is a smart contract platform focusing on security, performance, and interoperability. It is the only smart contracting platform for public blockchains with heavy adoption outside the EVM world.
Key Points
- Deeply integrated in many Cosmos blockchains
- Type-safe development and powerful testing with Rust
- Native supports for powerful IBC communication
- Compatible with many Rust libraries (i.g. decimal math)
- Uses highly optimized Web Assembly runtime
- Welcoming (and diverse) dev community
The Actor Model
The actor model is a computational model used in the design and implementation of the CosmWasm framework. It provides a deterministic execution environment by employing message-based communication between individual actors. In this model, each actor is an autonomous entity capable of managing its internal state, creating other actors (other smart-contracts), and send and receive structured messages.
The actor model plays a crucial role in designing the architecture of a smart-contract application within the CosmWasm framework.
Benefits of the Actor Model
The application of the actor model in the CosmWasm framework provides the following benefits:
-
Encapsulation and Isolation: Each smart contract operates as an isolated actor, processing messages and state independently. This isolation prevents interference and unintended side effects between contracts, enhancing security and reducing the risk of vulnerabilities like re-entrancy attacks.
-
Sequential Message Processing: Contracts handle only one message at a time, ensuring deterministic execution and eliminating the need for call stacks. This approach prevents complex control flow issues and helps maintain the predictability of the contract’s state. It also simplifies the mental model required to understand and reason about execution order in multi-contract interactions.
-
Controlled Interactions: When a contract wants to invoke another contract, it sends a message containing the necessary information. This controlled interaction allows for well-defined communication patterns, promoting modularity, and ensuring that contracts can safely collaborate without exposing their internal states.
-
Error Handling and Atomic Transactions: A transaction in CosmWasm can represent a state-transition that involves multiple sub-messages and spans multiple actors. If any sub-message on any actor encounters an error, the entire transaction is rolled back, reverting all state changes made within that transaction. This ensures atomicity and prevents inconsistent states in the contract.
Coming from EVM?
There are a few key differences between the EVM and CosmWasm that you should be aware of. The most important one is that instances of contracts and the code that they run against are two different concepts in CosmWasm. This means that you can have multiple instances of the same contract code running at the same time, each with their own state. This is not possible in EVM, where the contract code and the contract instance are the same thing.
This is an important difference to be aware of when we talk about migrations further in our documentation.
Migrations are a key feature of CosmWasm. They allow you to upgrade a contract’s code while retaining the state of the contract.
A migration doesn’t delete the code that was previously running for a contract. Code (a WebAssembly binary) is referred to by code-ids and contracts run against a specific code-id and get their own address space (and state) when they are instantiated. Hence migrations just update the code-id that a contract uses to run. I.e. The contract keeps its address and state but now runs on a different code-id (binary).
If you’re looking for a more in-depth comparison go read this article by the creator of CosmWasm.
To learn more about CosmWasm, check out its official documentation.
Javascript
You need to have a basic understanding of Javascript to use out front-end library Abstract.js, which is used to interact with the on-chain Abstract framework.
You can learn about Javascript here.
Abstract SDK
At the heart of the Abstract development platform lies the Abstract SDK, a modular smart-contract framework intended to streamline and expedite the creation of CosmWasm dApps. It achieves this by emphasizing reusability and composability via an account-abstraction oriented architecture. The SDK is constructed atop the battle-tested CosmWasm: a WASM-based smart-contract framework developed in Rust.
From a high-level perspective, smart-contracts built with the Abstract SDK can use on-chain dependencies (other smart-contracts) to isolate specific functionalities. In this way, a smart-contract built with the Abstract SDK can explicitly define its dependencies and use them to perform complex multi-contract interactions with very minimal code. This, in turn, allows you to focus on the novel functionality of your application without inheriting the complexity of the underlying infrastructure.
Why build with Abstract?
The Abstract SDK is designed to solve the following problems:
-
Simplified Development ♻️: CosmWasm development has been a start-from-scratch process up until now. Abstract simplifies the development process by providing a modular architecture and a comprehensive set of tools both for the frontend and backend, allowing you to reuse components across multiple dApps reducing development time, increasing productivity, iteration speed and shortening the go-to-market of your ideas.
-
Security 🔐: The Abstract SDK is built on top of the CosmWasm smart-contract framework, which is a battle-tested and highly secure platform. Because of its modular design, it’s also easier to audit and test. Audited components can be reused across multiple dApps, reducing the risk of bugs and vulnerabilities. Building with the Abstract SDK automatically makes it more secure, with an “off-by-default“permission system, fully configurable by you.
-
Complexity 🧠: The Abstract SDK simplifies the development process by providing a modular framework that allows you to focus on the core functionality of your dApp.
-
Compatibility 🔄: The Abstract SDK is designed to work seamlessly with popular on-chain services in the Cosmos ecosystem, decentralized exchanges, money markets, oracles, etc.
-
Interoperability 🌐: The Abstract SDK is chain-agnostic, allowing you to build dApps that can interact with multiple blockchains within the Cosmos ecosystem.
-
Scalability 📈: The Abstract SDK is designed to scale with your needs, allowing you to easily deploy to new networks, iterate on your product and achieve product market fit. Through its novel on-chain application store it also allows for personalized dApps that can be customized by the developers to meet their specific needs, making the Abstract SDK the first decentralized software distribution platform.
-
Continuous Funding 📈: Developers on the Abstract platform can earn ongoing revenue for their contributions by leveraging the platform’s community-driven incentives. As a developer you can publish your smart contract modules on the marketplace and receive proceeds directly from sales to other teams via subscription or one-time fee. Read more about monetization on the Abstract platform here
-
Continuous Innovation and Collaboration 👥: The Abstract SDK enables you to collaborate on the creation of dApps through its composable architecture and its ability to easily publish testing infrastructure for mock environment construction.
-
Developer-Centric Approach 👥: Abstract is built by developers for developers. We understand the challenges and complexities that developers face in the blockchain space, and we’re committed to providing the necessary tools, resources, and support to make your journey smooth and rewarding. Whether you’re an experienced blockchain developer or new to the space, Abstract is designed to empower you and unlock your full potential.
In essence, the Abstract SDK offers a structured yet flexible approach to smart-contract development, promoting reusability and efficiency. With its robust technical capabilities, streamlined development process, rock-solid security infrastructure, and effortless integration with financial services, Abstract empowers developers to unleash their creativity, craft cutting-edge financial applications, and make valuable contributions to the flourishing Cosmos ecosystem.
Key Components
The Abstract SDK comprises the following key concepts:
Features
At the heart of the Abstract SDK are “features” - Rust traits that can be seen as building blocks you can combine in various ways. Each feature provides a specific capability or function. By composing these features it is possible to write advanced APIs that are automatically implemented on objects that support its required features.
Abstract APIs
The Abstract APIs are objects that can only be retrieved if a contract or feature-object implements the required features/api traits. If the trait constraints for the API is met it is automatically implemented on the object and allows you to retrieve the API object.
Abstract Base
Think of this as the foundation or starting point for building applications using the Abstract SDK. There are different types of bases available, each tailored for specific needs and functionalities.
There are three kinds of bases:
We will go into the technical details of these elements on the Build With Abstract section.
Example: Autocompounder
Let’s take a look at how an Autocompounder
app built with the Abstract SDK would look like. This Autocompounder
has
a dependency on two contracts, a Dex
and Staking
contract. The dependency tree would look like this:
flowchart LR subgraph Autocompounder Application direction BT Autocompounder -.-> Dex Autocompounder -.-> Staking Staking --> Account Autocompounder --> Account Dex --> Account end User[fa:fa-users Users] ==> Autocompounder style User fill: #161b25 style Autocompounder fill: #161b25 style Staking fill: #161b25 style Dex fill: #161b25 style Account fill: #161b25
Remember we said the Abstract SDK uses account-abstraction?
Each solid-line arrow represents execution permissions of the autocompounder contract on the account. These permissions allow the autocompounder contract to move funds, interact with other contracts through the account, and perform other actions. It does this by sending messages to the account, which then executes them on behalf of the autocompounder contract. This is the basic idea behind account abstraction and is further elaborated in on the account abstraction page.
Each dotted arrow indicates a dependency between the contracts. These dependencies are explicitly defined in the autocompounder contract and are asserted at contract instantiation. In this example the autocompounder contract is able to access specific functionality (like swapping or staking assets) from its dependencies (the dex and staking contract). Through this mechanism, a major reduction in the amount of code and its complexity is achieved, as otherwise every dex or yield provider would have to be integrated with the autocompounder contract itself.
From a developer ecosystem standpoint, the modularity provided by the Abstract SDK encourages collaboration and cross-team code re-use, a practice that has been proven to accelerate development and increase developers’ productivity. As the saying goes, a rising tide lifts all boats.
Account Abstraction
Account abstraction is a new concept that is making headlines on blockchain and smart-contract platforms. It’s a popular subject because it is designed to streamline how users interact with decentralized applications (dApps). The fundamental idea is to abstract away the complexities of blockchain interactions and provide a user-friendly, secure interface for using and managing applications.
In traditional blockchain interactions, a transaction is typically initiated by a users directly signing some data with their private key and transmitting that to the blockchain for validation. Account abstraction simplifies this process by making the transaction initiation and validation programmable. Essentially, it allows the transaction logic to be customized within a smart-contract, vastly extending the scope of UX possibilities.
The Abstract SDK provides what we call an Abstract Account, an extensible smart-contract wallet capable of holding tokens and interacting with other smart contracts. The ownership structure of the Abstract Account is customizable to fit the needs of the developers and their applications. It is designed to be highly programmable, allowing developers to build and distribute complex applications on top of it.
See EIP-4337 to read about account abstraction in the Ethereum ecosystem.
This concept of account abstraction, when implemented correctly, can provide numerous benefits:
-
Improved User Experience: Users can interact with smart contracts more seamlessly, without worrying about the underlying blockchain complexities. The verification model can be tailored to feel like familiar web2 experiences.
-
Enhanced Security: By shifting validation logic to smart contracts, a variety of security checks can be implemented to guard against unauthorized transactions. This could include multi-factor authentication, whitelisting, and more.
-
Reliable Fee Payment: Account abstraction can enable smart contracts to pay for gas, thereby relieving end-users from managing volatile gas prices or even paying for gas at all.
In the following sections, we’ll discuss how Abstract utilizes the concept of account abstraction, ensuring modularity, security, and scalability in applications built using the Abstract SDK.
Abstract Apps
Abstract Apps are smart-contracts that add functionality to an Abstract Account. Here’s a small snippet of code to give you an idea of how an App is created with the Abstract SDK:
#![allow(unused)] fn main() { pub const COUNTER_APP: CounterApp = CounterApp::new(COUNTER_ID, APP_VERSION, None) .with_instantiate(handlers::instantiate) .with_execute(handlers::execute) .with_query(handlers::query) .with_sudo(handlers::sudo) .with_receive(handlers::receive) .with_replies(&[(1u64, handlers::reply)]) .with_migrate(handlers::migrate); }
The code above defines an Abstract App. This app can be installed on any Abstract Account through the Abstract App store, allowing developers to monetize their code.
The customizable handlers that are used in the builder are functions similar to the native CosmWasm entry-point
functions. They expose an additional App object which, via the abstract-sdk
, empowers you to execute intricate
multi-contract transactions with minimum code. Importantly, this simplification does not limit the contract’s
programmability. Instead, it provides a balance of efficient coding and comprehensive control over inter-contract
interactions.
In the upcoming section we will explore the architecture of Abstract Accounts, providing insights into its design.
Abstract Account Architecture
Introduction
Abstract’s account architecture is skillfully designed, merging modularity, scalability, and security. This architectural design is anchored by the ideas of account abstraction. For a detailed exploration of account abstraction, read the preceding chapter. In the upcoming sections, we will delve deeper into the architecture of Abstract Accounts, providing insights into its design principles and components.
Abstract Account
The Abstract SDK provides users with a sovereign smart-contract wallet. We call this smart-contract wallet
an Abstract Account
. The account’s architecture has two primary components (smart-contracts): the Manager contract
and the Proxy contract.
flowchart LR subgraph Abstr[Abstract Account] direction TB Manager --> Proxy end Owner -.-> Manager style Owner fill: #161b25 style Manager fill: #161b25 style Proxy fill: #161b25
The owner of the account, can configure the Abstract Account by sending messages to the manager contract. We don’t make any assumptions about the nature of this owner, it can be a wallet, multi-sig or any other ownership structure, allowing you to customize your Abstract Account’s ownership to fit your needs.
You can read up on the different ownership structures in our Ownership section.
The account’s architecture centers around configurable programmability. In other words, how can one configure the account (install applications, set permissions, etc.) to enable users and developers to easily customize it to do what they want?
Let’s dive deeper into the two components of the Abstract Account.
Manager Contract
The Manager is responsible for the account’s configuration and security, serving as the controller of the Abstract Account. It is responsible for various important operations, including:
-
Owner Authentication 🔐: Authenticating privileged calls and ensuring only approved entities can interact with the account.
-
Application Management 📦: Managing and storing information about the applications installed on the account, their inter-dependencies, permissions and configurations.
-
Account Details 📄: Storing the account’s details, such as its name, description, and other relevant information.
Proxy Contract
The Proxy is responsible for the account’s programmability and assets management, serving as the asset vault of the Abstract Account, taking care of:
-
Asset Management & Pricing 💰: Holding the account’s assets, including tokens, NFTs, and other fungible and non-fungible assets as well as allows for pricing assets based on decentralized exchange or oracle prices.
-
Transaction Forwarding (Proxying) 🔀: Routing approved transactions from the Manager or other connected smart-contracts to other actors.
Why are these two contracts instead of one?
-
Separation of concerns: By separating the contracts the proxy’s functionality (and attack surface) is as small as possible. The separation also allows for simple permission management as we want to separate the admin calls (verified by the manager) from module calls.
-
Minimizing WASM size: Whenever a contract is loaded for execution the whole WASM binary needs to be loaded into memory. Because all the apps proxy their messages through the Proxy contract it would be smart to have this contract be as small as possible to make it cheap to load. While CosmWasm currently has a fixed cost for loading a contract irrespective of its size. We think that might change in the future.
Account Interactions
The diagram below depicts an Owner interacting with its Abstract Account through the Manager, and proxying a call to an external contract through the Proxy.
sequenceDiagram actor Owner participant Manager participant Proxy participant External Contract Owner ->> Manager: Account Action Manager ->> Proxy: Forward to Proxy Proxy ->> External Contract: Execute
Enabling IBC on your Abstract Account
Enabling the IBC functionality on your Abstract Account is done via the Manager contract with the UpdateSettings message. By doing so the IBC client will be registered to your account, enabling your modules to execute cross-chain commands.
sequenceDiagram autonumber actor U as Owner participant M as Manager participant VC as Version Control participant P as Proxy U ->> M: UpdateSettings Note right of U: ibc_enabled M -->>+ VC: Query IBC Client reference VC -->>- M: Return IBC Client address M ->> M: Register IBC Client M ->> P: Add IBC client to allowlist
Modularity in Abstract
Modularity is a fundamental design principle of the Abstract platform, contributing significantly to its flexibility and extensibility. A module in Abstract is a self-contained unit of functionality that can be added to an Abstract Account to extend its capabilities. This modular approach promotes the construction of bespoke applications, enhances the scalability of the system, and improves overall maintainability.
For application developers, modules simplify the development process by offering pre-built functionalities. Instead of building every aspect from scratch, you can leverage modules either from Abstract’s extensive library or those crafted by other developers available in the module marketplace. Additionally, you have the freedom to create custom modules using the Abstract SDK, tailoring them to specific application needs and even contributing back to the community.
Skip to Modules to learn what the kinds of modules are and why they are relevant to your Abstract Account.
How Modules Work
In Abstract, the process of integrating these modules is managed through the Manager contract within an Abstract Account. As described in a previous section, the Manager keeps track of all installed modules, managing their permissions and interactions. This system facilitates the customization of individual Abstract Accounts, permitting the installation or removal of modules according to your needs. In doing so, it effectively adapts the Account’s functionality.
From the perspective of a developer, the Abstract framework sets conventions and standards that allow leveraging existing modules during the development of new ones. This layer of abstraction saves considerable time and effort while promoting consistent design across different modules.
Security
Security is a priority at Abstract, especially when it comes to the modules that extend the capabilities of an Abstract Account. Every module listed on the mainnet marketplaces must undergo a thorough auditing process before it’s made available to developers. This process scrutinizes the module’s code, checking for potential vulnerabilities, and ensuring that it adheres to best security practices.
While no system can guarantee absolute security, this rigorous vetting process, coupled with the inherent security measures in Abstract and CosmWasm’s architecture, mitigates potential risks to a considerable extent.
The Abstract platform also maintains a Version Control for all the modules, allowing developers to track changes, understand the evolution of a module, and choose versions that have passed security audits.
Module Types
Within the Abstract SDK, a module is a contract that adds functionality to your Abstract Account. You can explore all the available modules on the modules tab of your Account through the web-app.
In the previous sections we referred to these modules as “applications”. We did this to simplify the mental framework of the Abstract SDK. However, the term “application” is not entirely accurate, instead we should refer to them as “modules”. These modules come in different types, each with their own purpose. The most common of which is the “App” module, or “Abstract App”.
Modules are classified in the following categories:
- App: modules that add a functionality, exposing new entry-points for you or your users.
- Adapter: modules that act as a standard interface between your Account and external services.
- Standalone: modules not built within Abstract, but registered to your Account so that the manager can execute commands on them.
Apps
An App module adds additional functionality to your Abstract Account, exposing new entry-points for you or your users. This could range from adding advanced financial logic, data management features, or complex computation capabilities, depending on your application’s needs.
Each App module is exclusive to a single Abstract Account, meaning the instance is owned by the Account owner, ensuring the owner has full control over the module’s functionality and lifecycle. This level of control extends to the management of upgrades, maintenance, and any customization that might be required for the specific use case of the application.
Because each Account has its own instance, App modules can be tightly integrated with the Account’s existing infrastructure. This includes the ability to interact directly with other modules within the same account, enabling powerful synergies and cross-module functionality.
The
abstract:etf
module is an app that exposes entry-points allowing external users to buy and sell “shares” in your Account, representing a portion of the Accounts’ value.
Adapters
Adapters serve as standard interfaces that facilitate communication between your Abstract Account and various external services. They act like bridges, enabling your account to interact with different smart contracts and blockchain services, thereby enhancing the interoperability of your applications.
The key function of an Adapter is to generalize functionality. Regardless of the underlying blockchain or smart contract protocol, the Adapter provides a standard interface that maintains consistency and simplifies the interaction process. As such, Adapters significantly simplify the developer experience and reduce the time required to integrate with various external systems.
Unlike other modules specific to each Abstract Account, Adapters are “global” in nature. This means that they are shared between multiple accounts. Due to this, Adapter modules are not migratable. This design choice is aimed at preventing supply-chain attacks that could potentially compromise the security of the Abstract ecosystem.
While individual Abstract Account owners can decide which Adapters and versions they wish to utilize, the overall control and maintenance of Adapters are handled at a platform level. This approach ensures that Adapters remain reliable, secure, and consistent across all Accounts.
The
abstract:dex
module allows Accounts to access standard functions on dexes with the same interface, regardless of whether they’re local to the chain or across IBC.
Example
flowchart LR subgraph Accounts direction BT subgraph Acc1["Account 1"] App1["abstract:etf"] end subgraph Acc2["Account 2"] App2["abstract:etf"] end end subgraph Adapters Acc1 --> Adapter1{{"abstract:dex"}} Acc2 --> Adapter1 end Adapter1 --> dex1([Osmosis]) Adapter1 --> dex2([Wyndex]) Adapter1 --> dex3([Astroport]) style App1 fill: #161b25 style App2 fill: #161b25 style Adapter1 fill: #161b25 style dex1 fill: #161b25 style dex2 fill: #161b25 style dex3 fill: #161b25
abstract:etf
app module installed, using the abstract:dex
adapter to interact with multiple dexesInstalling and Uninstalling Modules
The following are sequence diagrams of the process of installing and uninstalling a module on an Abstract Account. As you can see, the process happens via the Manager, and it can be done by the Account owner through the web-app.
sequenceDiagram autonumber actor U as Owner participant M as Manager participant MF as Module Factory participant VC as Version Control participant P as Proxy U ->> M: InstallModule M ->> MF: InstallModule MF -->>+ VC: Query reference alt adapter VC -->>+ MF: Return address else app / standalone VC -->>- MF: Return code_id MF -> MF: Instantiate module end MF ->> M: Register module address M ->> P: Update module allowlist
sequenceDiagram autonumber actor U as Owner participant M as Manager participant P as Proxy U ->> M: UninstallModule M --> M: Check dependencies M -> M: Deregister module M ->> P: Update module allowlist
How to Use Modules in Your Project
Leveraging modules in your project can significantly simplify the development process, allowing you to deploy projects faster and more cost-effectively than building traditionally. By using pre-built, tested, and community-reviewed modules, you can focus on your project’s unique features while reducing development time and minimizing potential bugs.
Here’s an example of how modules can be leveraged for a decentralized finance (DeFi) project:
Imagine you want to create a DeFi application with the following features:
- A fungible token for your platform
- Staking and delegation functionality
- A governance system for community-driven decision-making
- Integration with an oracle for fetching off-chain data
Instead of building each feature from scratch, you can leverage Abstract’s off-the-shelf modules to implement these functionalities with ease. This not only saves time and resources but also ensures that your project benefits from the best practices established by the Abstract community.
Step 1
Choose the module of your choice on the Abstract SDK. You can see the available modules on our repository.
Step 2
Import the chosen modules into your project and configure them according to your requirements. This can include setting custom parameters, such as token supply, staking rewards, or voting thresholds.
Step 3
Integrate the modules with your existing codebase, ensuring they work seamlessly with your project’s unique features. This can involve calling module functions, implementing hooks, or extending your data structures.
Step 4
Test your dApp thoroughly to ensure the modules function as intended and do not introduce any unexpected behavior.
By leveraging Abstract’s modules in this way, you can rapidly build and deploy your DeFi project while benefiting from the robustness and flexibility of the Abstract ecosystem.
Execute on Modules
The following are sequence diagrams of the process of executing a module on an Abstract Account. There are three types of execution: Owner Execution, Adapter Execution, and Module Execution.
Let’s explore each of them.
Owner Execution
To execute a message on a specific module, the Owner can call the ExecOnModule
function on the Manager, providing
the module id. This diagram depicts how the Manager interacts with any module installed on the account.
sequenceDiagram autonumber actor U as Owner participant M as Manager participant Md as Module ("addr123") U ->> M: ExecOnModule Note right of U: ModuleMsg {"module_id": "xyz"} M -->> M: Load module address M ->> Md: Execute Note right of M: ModuleMsg {"module_addr": "addr123"}
Adapter Execution
In the following example, the abstract:dex
module is installed on an Account, and the owner requests a swap on a dex.
sequenceDiagram autonumber actor U as Owner participant M as Manager participant D as abstract:dex participant VC as Version Control participant A as ANS participant P as Proxy participant T as Dex Pool U ->> M: ExecOnModule Note right of U: Dex::Swap M --> M: Load module address M ->> D: Call module Note right of M: Adapter Msg D -->+ VC: Load proxy address for Account VC -->- D: Address D -->>+ A: Resolve asset names A -->> D: Asset infos D --> A: Resolve dex pool A -->>- D: Pool metadata D --> D: Build swap msg for target dex D ->> P: Forward execution Note over VC, A: DexMsg P ->> T: Execute Note right of P: DexMsg
Module Execution
In this example, we use Equilibrium’s Rebalance
function as an example. Modules
with dependencies (equilibrium:balancer
is dependent on abstract:etf
and abstract:dex
) have their addresses
dynamically resolved when called.
sequenceDiagram autonumber actor U as User participant B as equilibrium:balancer participant P as Proxy participant M as Manager participant D as abstract:dex participant T as Target Dex U ->> B: Rebalance B -->>+ P: Query Allocations P -->>- B: Allocations B --> B: Calculate rebalancing requirements B -->>+ M: Query abstract:dex address M -->>- B: Address B ->> D: Call SwapRouter on dex D --> D: Build swap msg for target dex D --> D: Load proxy address D ->> P: Forward execution Note over M: DexMsg P ->> T: Execute Note over D, M: DexMsg
Module Upgradability
Smart-contract migrations are a highly-debated feature in smart-contract development. Nonetheless Abstract believes it to be a powerful feature that allows for fast product iteration. In the spirit of crypto we’ve designed a system that allows for permissionless software upgrades while maintaining trustlessness.
Module version storage
Permissionless software upgradeability is provided by a module version storage in the version control contract. The mapping allows your Account to:
- Instantiate a module of the latest versions.
- Upgrade a module to a new version as soon as it’s available.
- Provide custom modules to other developers.
- Do all this without any third-party permissions.
There are two types of possible migration paths, although they appear the same to you as a developer.
Migration Update
Most module updates will perform a contract migration. The migration can be evoked by the owner and is executed by the manager contract. You can learn more about contract migrations in the CosmWasm documentation.
Move Updates
Some modules will not perform a regular contract migration, and this has to do with our module classification system. Adapter modules for instance should not be migratable because it would remove the trustlessness of the system.
Therefore, if we still want to allow for upgradeable Adapters we need instantiate each Adapter version on a different address. When you as a developer decide to upgrade an Adapter module, the abstract infrastructure moves your Adapter configuration to the new addresses and removes the permissions of the old Adapter contract.
However, all other modules that depend on the upgraded Adapter module don’t have to change any stored addresses as module address resolution is performed through the manager contract, similar to how DNS works!
Upgrading Modules
One of the key strengths of Abstract is that it is designed to minimize your maintenance workload while maximizing the control you have over your infrastructure.
Abstract manages module upgrades for you, ensuring your infrastructure remains intact and your workflows continue to function smoothly through every upgrade. This process is carried out in a manner that consistently maintains the integrity and security of your system.
The process for upgrading modules is shown in the following diagram:
sequenceDiagram autonumber actor U as Owner participant M as Manager participant VC as Version Control participant P as Proxy U ->> M: Upgrade loop for each module M -->> VC: Query reference alt adapter VC -->> M: Return address M ->> M: Update module address M ->>+ P: Remove old adapter from allowlist M ->> P: Add new adapter to allowlist deactivate P else app / standalone VC -->> M: Return code_id M ->> M: Migrate module to new code_id end end alt M -> M: Migrate self end M -> M: Update dependencies M --> M: Check dependencies
An important aspect to point out of this process is how the integrity of the modules is ensured while performing the upgrades. Proposed module upgrades are performed sequentially and keeping track of the changes in each module upgrade. Additionally, version requirements and dependency checks are performed at the end of all the migrations to ensure module compatibility.
Account Ownership
Governance structures are a wildly under-developed field in the realm of smart contract technology. The Abstract Platform allows for any custom governance type to be used with its chain-agnostic framework. While most developers appreciate an easy-to-use interface to control their dApps, Abstract opts to provide two fully integrated governance choices ( token-based and DaoDao integration coming soon) that ensure a seamless user experience.
When setting up governance for your dApp, you will be prompted to choose between supported governance types, Monarchy and Multi-signature.
Not interested in account ownership? Skip to our section on Framework Components.
Monarchy
In a monarchy, a single wallet has full control over the dApp. If you’re connected with a wallet, your address will be automatically inserted as the owner.
graph TD A[Single Account] -->|Controls| B(Abstract Account) style A fill:#161b25 style B fill:#161b25
Multi-signature
Multi-signature (“multisig”) governance is a governance structure that requires a subset of its members to approve an action before it can be executed. Though multiple multisig contract implementations exist, Abstract provides this functionality using the cw-3 standard with the goal of providing the most flexible solution to users.
Here are a few terms you need to know about when configuring your multisig:
-
Voter weight 🏋️♂️: The weight that the voter has when voting on a proposal.
-
Threshold 📊: The minimal % of the total weight that needs to vote YES on a proposal for it to pass.
graph TD subgraph Voters V1[Voter 1] V2[Voter 2] V3[Voter 3] end V1 --> A[Multisig Wallet] V2 --> A V3 --> A A -->|Controls| B(Abstract Account) B[Abstract Account] style A fill:#2c313d style B fill:#161b25 style V1 fill:#161b25 style V2 fill:#161b25 style V3 fill:#161b25
Let’s look at an example to make it clear how this works.
Example
Suppose you are building a DeFi platform using Abstract and want to implement multisig governance. You have five stakeholders, and you want at least 60% of the total voting weight to approve a proposal for it to pass.
-
Set up the multisig module in your dApp.
-
Assign voter weights to each of the five stakeholders. For instance, A: 30%, B: 20%, C: 20%, D: 15%, and E: 15%.
-
Configure the multisig module with a 60% threshold.
With this configuration, any proposal will require approval from stakeholders with a combined voting weight of at least 60% to be executed. This ensures a more democratic decision-making process and reduces the risk of a single stakeholder making unilateral decisions.
Sub-accounts
A Sub-Account is an Abstract Account that is owned by another Abstract Account. They are easily created by calling CreateSubAccount
on any account. By creating a sub-account for each app it separates the access to funds between different apps. This system allows users to easily experiment with different apps without the concern of those apps accessing funds from their main account or other apps. The diagram below shows how sub-accounts can be owned by the main Account
or other sub-accounts.
flowchart TB Account SubAccount-A SubAccount-B SubAccount-C Owner --> Account Account --> SubAccount-A Account --> SubAccount-B SubAccount-A --> SubAccount-C
Now accessing or configuring these accounts could be hard. To make this easier we allow calling any sub-account or any app on a sub-account directly without requiring the message to be proxied through the top-level account. The diagram below shows how an account owner can configure the sub-accounts and apps directly that are part of his main account.
flowchart TB direction TB subgraph AbstrA[Sub-Account A] direction TB ManagerA[Manager] --> ProxyA[Proxy] AppA[App] end subgraph AbstrB[Sub-Account B] direction TB ManagerB[Manager] --> ProxyB[Proxy] end subgraph AbstrC[Sub-Account C] direction TB ManagerC[Manager] --> ProxyC[Proxy] App end subgraph Abstr[Account] direction TB Manager --> Proxy end Owner --> Manager Manager --> ManagerA Manager ---> ManagerB ManagerB --> ManagerC Owner -.Configure App.....-> AppA Owner -.Configure Account....-> ManagerC
As a result of this structure, complex multi-account systems can easily be transferred to between governance systems by simply changing the owner of the top-level account.
Interchain Abstract Accounts
The cosmos is going interchain. The arrival of Inter-Blockchain Communication (IBC) launched us into a new paradigm in application development. New use-cases and solutions to existing UX problems are now possible. As the interchain application platform, Abstract attempts to make IBC accessible to developers and users alike. In this section we’ll delve into interchain application development, what makes it so hard and how Abstract can help you create your first #Interchain application.
What is The Interchain?
The interchain, sometimes called the internet of blockchains, is the concept of a network of blockchains that can communicate with each other. This is made possible by the Inter-Blockchain Communication (IBC) protocol, which is a standard for communication between blockchains. IBC is a protocol that allows for the transfer of arbitrary data between blockchains. This means that users can send data (like tokens) from one blockchain to another without third-party trust assumptions. This is a huge step forward for the blockchain industry, as it allows for the creation of a network of blockchains that can independently communicate with each other.
If you’re interested in a visual representation of the interchain, check out the map of zones.
Building on The Interchain
Developers quickly started developing applications with IBC after its release. But they quickly ran into a major
problem. IBC is not developer friendly. It’s a low-level protocol that requires extensive knowledge about its inner
workings. These problems were quickly recognized by the CosmWasm community. In response DOA-DAO
built Polytone
, an Interchain Account (ICA) solution for CosmWasm
smart-contracts. This was a great step forward for interchain CosmWasm development, but it introduced its own
complexities and knowledge requirements.
As a response, Abstract has created its own ICA solution called Interchain Abstract Account (ICAA). The goal of ICAA is multifold. First, it aims to make IBC accessible to developers by removing any knowledge requirements about the underlying technology. Using IBC is as simple as enabling it on an Abstract Account and interacting with it through our SDK. Second, it aims to make IBC a backend technology, unbeknown to users. Users should not have to know about IBC. They should only have to know about the application they are using.
Interchain Abstract Accounts (ICAA)
An Interchain Abstract Account is an Abstract Account that is owned by another Abstract Account which is located on a different chain. This relationship is showcased in the diagram below.
flowchart LR subgraph Juno direction BT Account end subgraph Osmosis direction BT Account -- Polytone --> ICAAOsmo[ICAA] end direction TB Owner[fa:fa-user Owner] ==> Account
When a user creates an Abstract Account they do so locally on whichever chain they prefer. In this scenario the user opted to create his account on the Juno network.
After account creation the user (or his application) opted to create an ICAA on Osmosis. The ICAA creation is handled by the Account Factory in a similar way to regular account creation. After successfully creating the ICAA, the user (and his/her applications) can interact with the ICAA as if it were a regular account. Applications can send tokens to the ICAA, execute smart-contract calls, request queries and more. With the help of Polytone, the ICAA returns the result of these actions to the application.
The account creation process is covered in detail in the Account Factory section.
ICAAs can be chained together, creating a network of accounts that can interact with each other. This allows for the creation of complex applications that span multiple chains. The diagram below showcases this relationship.
flowchart LR subgraph Juno direction BT Account([Account]) end subgraph Osmosis direction LR Account --> ICAAOsmo([ICAA]) ICAAOsmo2([ICAA 2]) end subgraph Terra direction RL Account --> ICAATerra([ICAA]) end subgraph Archway direction RL ICAATerra --> ICAAArch([ICAA]) ICAAArch --> ICAAOsmo2 end Owner[fa:fa-user Owner] ==> Account
Each of these accounts has a unique AccountId
defined by the account’s origin chain and the path over which it was
created. This allows the account’s owner to decide how messages should be routed.
flowchart LR subgraph Juno direction BT Account([local:1]) end subgraph Osmosis direction LR Account --> ICAAOsmo([juno:1]) ICAAOsmo2([juno>terra>archway:1]) end subgraph Terra direction RL Account --> ICAATerra([juno:1]) end subgraph Archway direction RL ICAATerra --> ICAAArch([juno>terra:1]) ICAAArch --> ICAAOsmo2 end direction TB Owner[fa:fa-user Owner] ==> Account
Interchain Applications
We’re now able to easily create interchain applications. I.e. a single smart-contract that we can deploy to multiple blockchains that uses IBC to communicate about its state. Any chain-specific logic can be handled by the application’s dependencies, like the dex or staking adapter.
Need a refresher on adapters? Check out the Adapters section.
Framework Components
In the previous sections, we covered different aspects of the Abstract framework on a higher level. In this section, we will explore the different components under the hood of the Abstract framework that makes it powerful and unique, enabling it to work the way it does.
Here’s a peek into the key elements that form the foundation of the Abstract framework:
-
Abstract Name Service (ANS): Enables chain-agnostic action execution by storing crucial address space-related data on the deployed blockchain which can easily be referenced.
-
Version Control: Acts as a comprehensive on-chain registry for accounts and software. It exposes namespace claiming, module registrations, and seamlessly querying modules by namespace, name, and version.
-
Account Factory: Allows for the creation (Interchain) Abstract Accounts, which can be interacted with via scripts or the Account Console web interface.
-
Account Console: A web-based interface designed for optimal interaction with your Abstract Accounts. It’s a powerful tool that contains all the features you need not only to manage your accounts but also help you develop your application.
-
Module Factory: Allows you to install and manage Abstract Modules via the Account Manager. You can install modules by interacting with the Account Manager directly, i.e. via CLI/scripts, or by using the Account Console.
-
Monetization: Developers have the potential to monetize their modules by setting an installation fee for others to use their modules. By introducing monetization strategies, Abstract offers developers incentives to build and share valuable modules with the community.
-
Value Oracle: An integrated way to get the value of your account’s assets on-chain.
In the following pages we will explore each of these components in detail. If you have any questions, please don’t hesitate to reach out to us on Discord.
Abstract Name Service
The Abstract Name Service (or ANS in short) is an on-chain store of the most important address space related data of the blockchain it is deployed on. It allows for chain-agnostic action execution and dynamic address resolution. These features enable both users and developers to engage with the blockchain in a more intuitive manner.
ANS Architecture
The ANS is a smart contract that stores the following data:
- Assets: The most relevant assets on the local blockchain.
- Contracts: Contracts related to certain protocols or applications that could be dynamically resolved. This could be used to store the address for an asset-pair for a dex. For example, “osmosis/juno,osmo” could be resolved to the address of the osmosis pool that allows you to swap osmo for juno.
- Channels: IBC channel data to map a protocol + destination chain to a channel id. This allows for dynamic IBC transfers without having to know the channel id beforehand.
The ANS contract state layout is defined here. It consists of key-value mappings for the different entries.
#![allow(unused)] fn main() { /// Stores name and address of tokens and pairs /// LP token pairs are stored alphabetically pub const ASSET_ADDRESSES: Map<&AssetEntry, AssetInfo> = Map::new("assets"); pub const REV_ASSET_ADDRESSES: Map<&AssetInfo, AssetEntry> = Map::new("rev_assets"); /// Stores contract addresses pub const CONTRACT_ADDRESSES: Map<&ContractEntry, Addr> = Map::new("contracts"); /// stores channel-ids pub const CHANNELS: Map<&ChannelEntry, String> = Map::new("channels"); /// Stores the registered dex names pub const REGISTERED_DEXES: Item<Vec<DexName>> = Item::new("registered_dexes"); /// Stores the asset pairing entries to their pool ids /// (asset1, asset2, dex_name) -> {id: uniqueId, pool_id: poolId} pub const ASSET_PAIRINGS: Map<&DexAssetPairing, Vec<PoolReference>> = Map::new("pool_ids"); /// Stores the metadata for the pools using the unique pool id as the key pub const POOL_METADATA: Map<UniquePoolId, PoolMetadata> = Map::new("pools"); }
You can find the full source code for the ANS contract here.
Resolving Entries
The information provided by the ANS is great to have. However, directly calling CosmWasm smart queries on the ANS contract can make your code messy and significantly raise gas usage. For this reason, we offer three methods to efficiently and dependably execute low-gas queries on the ANS contract.
Resolving your asset/contract name to its matching value is much like resolving a domain name like abstract.money to its IP address (172.67.163.181).
There are three ways to resolve your entry into its matching value.
AbstractNameService
Trait (Recommended)
Both App and Adapter objects implement the AbstractNameService
trait which allows you to resolve entries.
let juno_name = AssetEntry::new("juno");
let juno_asset_info = module.name_service(deps).query(&juno_name)?;
Resolve
Trait
Entries that are resolvable by the Abstract Name Service implement the Resolve
trait which gives them the ability to
be resolved by ANS explicitly.
let ans_host = module.ans_host(deps)?;
let juno_name = AssetEntry::new("juno");
let juno_asset_info = juno_name.resolve(&deps.querier, &ans_host)?;
AnsHost Object
You can also load or create an AnsHost
struct. This struct is a simple wrapper around an Addr and implements methods
that perform raw queries on the wrapped address.
let ans_host = AnsHost {address: "juno1...."};
let juno_name = AssetEntry::new("juno");
let juno_asset_info = ans_host.query_asset(deps, &juno_name)?;
Version Control
The Version Control contract acts as the registry for all modules and accounts within the Abstract platform. Abstract Accounts can use it to claim namespaces and register their modules. The Version Control contract allows modules to be queried by its namespace, name, and version, returning its reference which may be a code id or address.
Propose Modules
Developers that wish to publish modules to the Abstract platform need to call ProposeModules
on the Version Control
contract. The modules will subsequently be reviewed by the Abstract platform for registration.
For documentation on how to register modules, see Module Deployment
Modules cannot be registered without their namespaces being claimed by an Account. This is to prevent malicious actors from registering modules under trusted namespaces.
Below details the assertion process.
sequenceDiagram autonumber actor U as Owner participant VC as Version Control participant Man as Manager of Namespace participant M as Adapter Instance U ->> VC: ProposeModules loop VC --> VC: Load Account ID for namespace VC --> VC: Load Account Manager address VC -->>+ Man: Query Account owner Man -->>- VC: Address opt adapter VC -->> M: Assert no sudo admin activate M VC -->> M: Assert same cw2 data deactivate M end end VC ->> VC: Save modules
Account Factory
The Account Factory is a contract that allows you to create and manage Abstract Accounts, which can be interacted with via the contract or the Account Console.
To recap from that chapter, an Abstract Account is composed of a Manager and a Proxy contract. Those contracts will be created for you by the Account Factory using the latest versions of these contracts, which are store on the Version Control contract.
Flow Diagram
When a developer requests the creation of an account, the following internal process is initiated:
sequenceDiagram autonumber actor U as User participant F as Account Factory participant VC as Version Control participant M as New Manager participant P as New Proxy U ->> F: CreateAccount F -->>+ VC: Query for Manager reference VC -->>- F: Manager code_id F-x+M: Instantiate Manager Note over VC: Reply M-->>-F: Manager Address F->F: Store manager address F-->>+VC: Query for Proxy reference VC-->>-F: Proxy code_id F-x+P: Instantiate Proxy Note over VC: Reply P-->>-F: Proxy Address F->>VC: Register Account F->>M: Register proxy address F->>P: Allowlist Manager address F->>P: Set owner to Manager F->>M: Set migrate admin to Self
If you want to see in details how this is accomplished, please refer to our Github repository.
Account Console
The Account Console is a web-based interface that allows you to interact with your Abstract Accounts. It is a powerful tool containing all the features you need not only to manage your accounts but also help you develop your application, including:
- Account Management: Create, update, and delete accounts.
- Module Management: Install, update, and delete modules.
- Name Service: Register and manage human-readable names for your accounts.
- Dev Tools: Contract message builder given the json schema, tools for contract interactions, signing transactions, add assets to your ANS and deployment configurations.
- Delegations: Delegate your voting power to a validator of your choice.
Accessing the Account Console
You can access the Account Console where you can create an account, claim namespaces and more by visiting app.abstract.money. You will be able to select the network you want to connect to, and then proceed to create your Abstract Account.
Account Management
Create Account
Creating an account is straight forward process. Once in the Account Console, click “Create Account”. You will be able to select the network you want to connect to, and then proceed to create your Abstract Account.
Are you having trouble creating an account? Please contact us on Discord and we’ll help you out.
Once the account is created, you can see the overview of the account, including the manager and the proxy contracts, among other details.
Claim a Namespace
Now that you have your account you can proceed to claim your namespace. The namespace will be exclusively linked to your Abstract Account and will prefix your module names to form a unique module identifier.
For example, if your namespace is
myapp
and your module name ismymodule
then your module identifier will bemyapp:mymodule
.
You can easily claim your namespace by going to your Account on our website and click the “Claim Namespace” button on the account page. You will be asked to pay a small fee to claim your namespace. This fee is used to prevent namespace squatting and to help us maintain the Abstract ecosystem.
Module Factory
The Module Factory is a contract that allows you to install and manage Abstract Modules via the Account Manager. You can install modules by interacting with the Account Manager directly, i.e. via CLI, or by using the Account Console.
To recap from that chapter, there are three types of modules: App, Adapter, and Standalone.
Flow Diagrams
Install Module
When a developer requests the installation of a module, the following internal process is initiated:
sequenceDiagram autonumber actor U as Owner participant M as Manager participant MF as Module Factory participant VC as Version Control participant P as Proxy U ->> M: InstallModule M ->> MF: InstallModule MF -->>+ VC: Query reference alt adapter VC -->>+ MF: Return address else app / standalone VC -->>- MF: Return code_id MF -> MF: Instantiate module end MF ->> M: Register module address M ->> P: Update module allowlist
Execute on Module
Once the module is installed, there are essentially three ways to interact with it depending on the type of module:
Owner Execution
sequenceDiagram autonumber actor U as Owner participant M as Manager participant Md as Module U ->> M: ExecOnModule Note right of U: ModuleMsg M -->> M: Load module address M ->> Md: Execute Note right of M: ModuleMsg
Adapter Execution
In the following example, the abstract:dex
module is installed on an Account, and the user requests a swap on a dex.
sequenceDiagram autonumber actor U as Owner participant M as Manager participant D as abstract:dex participant VC as Version Control participant A as ANS participant P as Proxy participant T as Dex Pool U ->> M: ExecOnModule Note right of U: Dex::Swap M --> M: Load module address M ->> D: Call module Note right of M: Adapter Msg D -->+ VC: Load proxy address for Account VC -->- D: Address D -->>+ A: Resolve asset names A -->> D: Asset infos D --> A: Resolve dex pool A -->>- D: Pool metadata D --> D: Build swap msg for target dex D ->> P: Forward execution Note over VC, A: DexMsg P ->> T: Execute Note right of P: DexMsg
User Execution
In this example, we use Equilibrium’s Rebalance
permissionless function as an example. Modules with
dependencies (equilibrium:balancer
is dependent on abstract:etf
and abstract:dex
) have their addresses dynamically
resolved when called.
sequenceDiagram autonumber actor U as User participant B as equilibrium:balancer participant P as Proxy participant M as Manager participant D as abstract:dex participant T as Target Dex U ->> B: Rebalance B -->>+ P: Query Allocations P -->>- B: Allocations B --> B: Calculate rebalancing requirements B -->>+ M: Query abstract:dex address M -->>- B: Address B ->> D: Call SwapRouter on dex D --> D: Build swap msg for target dex D --> D: Load proxy address D ->> P: Forward execution Note over M: DexMsg P ->> T: Execute Note over D, M: DexMsg
Monetization
In the Abstract framework, developers have the capability to monetize their modules by setting an installation fee for others to use their modules. By introducing monetization strategies, Abstract offers developers incentives to build and share valuable modules with the community.
Here’s a concise breakdown of how this works:
- Modules can be installed into abstract accounts.
- Each module can be configured with a Monetization strategy, primarily:
- InstallFee: A fee set by the developer which must be paid by other users to install the module. This fee is then transferred to the namespace owner’s account, which is fetched from the version control registry.
- None: No monetization strategy is applied for the module.
All module monetization details are stored in the version control but are verified and enforced by the module factory.
To assist users in budgeting, the module factory provides the SimulateInstallModules
query, which returns the total
sum of funds required to install a specified set of modules, including monetization and initialization funds.
Subscriptions
In addition to one-time installation fees, the Abstract framework empowers developers to introduce subscription-based monetization strategies for their modules. This model facilitates a steady stream of revenue, enhancing the sustainability and continuous development of the modules.
Subscriptions are being worked on and will be available soon, stay tuned!.
Account Value Oracle
Have you ever wished to know the value of your portfolio? I’m guessing you have. Luckily Abstract has an integrated system to retrieve the value of your assets, on-chain!
Value is relative
The value of something is always relative to some other thing. A bitcoin isn’t valued at 20,000, it’s valued at 20,000 USD. Likewise the first decision that’s required to work with Abstract’s oracle is: In what currency do you want your Account’s assets to be valued?
We’ll call this currency the base asset. There can never be more than one base asset and every asset will be valued in terms of this base asset.
With a base asset selected you can set up a value-reference for each asset that is held in your Account. A value-reference is a configuration that references some data that tells the contract how it should determine the value for that asset. Consequently your base asset won’t have an associated value-reference as everything is valued relative to it! Don’t worry, we’ll show some examples after covering the basics.
Types of Value-References
To ensure that you configure and use the oracle correctly you’ll need to understand, on a high level, how the value-reference system works and what its limitations are. Your app’s security might depend on it!
Remember: Every asset, apart from the base asset, has an associated value-reference and that value-reference allows your Account to determine the value of the asset in terms of the base asset.
Currently there are four value-reference types that can be applied to an asset. Lets go over them.
1. Reference Pool
This is the most common value-reference type. It points to a dex trading pair where one of the two assets of the pair is the asset on which this value-reference is applied. The Account takes this information and does three things.
- Query how much X the Account holds.
- Determine the price of asset X, defined by the pool X/Y.
- Calculate the value of asset X in terms of asset Y given the price. This gives us the value of asset X in terms of asset Y.
Your Account has 10 $JUNO and 50 $USDC. You’d like to be shown the value of your assets in terms of USD.
- You identify that you want every asset denominated in US dollars. Therefore you choose $USDC as your base asset.
- You identify the easiest route to swap your $JUNO for $USDC which is a trading pair on Junoswap. Therefore you add $JUNO to your Account with a Pool value-reference.
- The ratio of $JUNO/$USDC in the pool is 1/10 so 1 $JUNO = 10 $USDC.
The Account now knows that if you’d swap your $JUNO to $USDC in that pool, you’d end up getting 10 $USDC. Therefore the total value of your assets is 60 $USDC.
2. Liquidity Token
A liquidity token is nothing more than a claim on a set of assets in a liquidity pool. Therefore the value of each liquidity token is simply the composition of asset held within that pool.
3. Value As
You might want to set the value of some asset in terms of another asset. For example, you could argue that every stablecoin is equal in value, irrespective of small fluctuations between them.
4. External
Some assets/positions are more complex. These include, but are not limited to: staked tokens, locked tokens and most third-party vault-like products. The Account needs to interact with Adapter modules that interact with these services to find out how it should value the asset/position.
Use With Caution
As we’ve outlined, each asset is valued relative to an asset or multiple assets. By recursively calling this value-reference function on each asset we can determine the value of any asset relative to our base asset.
As each asset’s value is referenced to some other asset through a price relation it exposes assets with a weak link to the base asset to a lot more volatility and attack surface. Therefore we recommend that you select a highly-liquid base asset with the highest liquidity trading pairs when configuring your assets.
While this way of determining an asset’s value is very intuitive, it doesn’t account for bad actors. Manipulation of asset prices to trigger smart-contract actions isn’t uncommon. Therefore we don’t recommend this version of the Account for creating high-value automated-trading products. No worries, an implementation based on oracle prices is in the works!
Any questions regarding the oracle and its configuration can be asked in our Discord server!
Abstract Products
At Abstract, we are dedicated to expanding the horizons of blockchain development through our innovative and ever-growing suite of products. Our products aim to simplify and enhance the development process, allowing creators to bring their visions to life with efficiency and security.
Our Product Lineup
CW-Orchestrator
- Scripting Power: Simplify your interactions with CosmWasm contracts.
- Macros for Efficiency: Generate type-safe interfaces to streamline your workflow.
- Code Reusability: Use the same logic for testing and deployment.
Abstract JS
- Seamless Interactions: Engage with the blockchain from your web application effortlessly.
- Type Declarations: Develop with type safety in mind.
- Comprehensive: From queries to transactions, we’ve got you covered.
Abstract App Template
- Quick Start: Jumpstart your app development with our robust template.
- Integration Friendly: Easy to integrate with existing systems.
- TypeScript Support: Build with confidence using TypeScript.
Abstract Testing
- Mocking Tools: Simulate scenarios with ease.
- Query Testing: Verify your queries and logic reliably.
- ANS Integration: Test with mock assets in our Abstract Naming Service.
A Growing Ecosystem
Our ecosystem is constantly evolving, with new products and updates regularly introduced. Stay tuned for the latest additions and enhancements that will take your development experience to the next level.
CW-Orchestrator
The cw-orchestrator package is a scripting tool designed to simplify interactions with CosmWasm smart contracts. By providing a set of macros that generate type-safe interfaces for your contracts, it not only enhances the code’s readability and maintainability but also reduces testing and deployment overhead. These interfaces can be seamlessly combined into a single object, fostering easier integration and collaboration.
Furthermore, cw-orchestrator allows for code reusability between testing and deployments, establishing itself as our primary tool in making Abstract’s infrastructure highly available.
Usage
Here’s a snippet that sets up the complete Abstract SDK framework on a cw-multi-test environment, and deploys the Counter App to the framework.
#![allow(unused)] fn main() { // Create a sender and instantiate the mock environment let sender = Addr::unchecked("sender"); let mock = Mock::new(&sender); // Construct the counter interface (a wrapper around the contract's entry points) let contract = CounterApp::new(COUNTER_ID, mock.clone()); // Deploy Abstract to the mock let abstr_deployment = Abstract::deploy_on(mock, sender.to_string())?; // Create a new account to install the app onto let account = abstr_deployment .account_factory .create_default_account(GovernanceDetails::Monarchy { monarch: sender.to_string(), })?; // Claim the namespace so app can be deployed abstr_deployment .version_control .claim_namespace(1, "my-namespace".to_string())?; // Deploy the app! contract.deploy(APP_VERSION.parse()?)?; }
For more details on how to use cw-orchestrator, please refer to the cw-orchestrator Documentation, where you can find a quick start and a detailed guide on how to use the tool with your smart contracts, supported chains and more. Also, check out the cw-orchestrator Github Repo for more details about the tool’s code.
Abstract JS
Abstract.js is a comprehensive JavaScript library designed to facilitate seamless interactions with the on-chain Abstract framework. Whether you’re a developer looking to integrate blockchain functionalities into your application or a blockchain enthusiast aiming to explore the Abstract framework, Abstract.js provides the essential tools to make the process straightforward.
Features
- Easy Integration 🛠️ : Designed with developers in mind, Abstract.js ensures a hassle-free integration process with existing JavaScript applications.
- Type Declarations 🔍: For TypeScript enthusiasts, Abstract.js comes with type declarations, ensuring type safety and enhancing the development experience.
- Comprehensive Methods 🌐: From querying blockchain data to sending transactions, Abstract.js covers a wide range of functionalities required for on-chain operations.
- React Support ⚛︎️: With the @abstract-money/abstract.js-react package, you can easily integrate and manage the Abstract framework in their React applications.
Installation
To install the main library:
npm i @abstract-money/abstract.js
For React-specific functionalities:
npm i @abstract-money/abstract.js-react
To read more about the available types and methods, please refer to the Abstract.js Documentation.
Abstract App Template
The Abstract App Module Template is a starting point for developing apps that enable features or transform Abstract Accounts into standalone products. An app is instantiated for each Account individually and it’s migratable. Apps are allowed to perform actions on the Account and may also integrate with other apps and Adapters installed on the Account. For a deeper understanding of Abstract Accounts, please refer to the abstract accounts documentation. If you’re interested in learning about apps, consult the app module documentation.
The primary focus of the Abstract App Template as of now is to provide a template for building a new Abstract App, as well as support for generating TypeScript client code for the contract, which can then be imported into a frontend application.
In the future, the Abstract App Template will also provide a basic template for the frontend application.
Getting Started
To get started, please go to the Abstract App Template Github Repo and follow the instructions in the README.
In there you can find instructions on how to generate a new Abstract App, how to test it, deploy it, and generate TypeScript client code.
Abstract-Testing
The abstract-testing
package is Abstract framework’s testing utility, focusing on mocking and querying
functionalities. These utilities are essential for simulating various scenarios, ensuring the correctness of the
framework’s functionalities, and facilitating robust unit testing.
Features
- ️Mock Data Creation 🛠: Easily create mock data with predefined data for assets, contracts, accounts and more.
- Abstract Naming Service Integration 🌐: Add mock assets into the Abstract Naming Service (ANS) for testing purposes.
- Flexible Configuration 🔧: Adjust version control addresses, set up mock ANS hosts, and more.
- Assertion Tools ✅: Assert the existence of accounts, assets, map entries and more.
- Predefined Test Scenarios 📝: Run through common test scenarios with ease.
- Build & Execute 🔄: Construct mock queries and execute them to test various functionalities.
Usage
Add the abstract-testnet
dependency to your Cargo.toml file:
[dependencies]
abstract-testing = "0.18.0"
For more information about the available types and methods, please refer to the Abstract-Testing Documentation.
You can find the latest version of the package on crates.io.
Example
#![allow(unused)] fn main() { use abstract_testing::MockQuerierBuilder; use abstract_testing::prelude::*; #[test] fn returns_account_owner() -> VersionControlTestResult { let mut deps = mock_dependencies(); // Set up mock querier with the account deps.querier = AbstractMockQuerierBuilder::default() .account(TEST_MANAGER, TEST_PROXY, 0) .build(); mock_init_with_account(deps.as_mut(), true)?; let account_owner = query_account_owner(&deps.as_ref().querier, &Addr::unchecked(TEST_MANAGER), 0)?; assert_that!(account_owner).is_equal_to(Addr::unchecked(OWNER)); Ok(()) } }
Expanding The Smart Contract Design Space
If you are well-versed with smart contracts, you will find that the Abstract SDK is a tool that gives you more leverage. Enabling you to get things done faster and better. It does this by giving you an extensive design space that transcends traditional smart contract capabilities, introducing superior code reusability and an unmatched software distribution process.
Designing with Abstract
The Abstract SDK broadens your design space beyond traditional smart contract application architectures. While applications built with stand-alone smart contracts can also be crafted using the SDK, the Abstract SDK promotes a level of code reusability that goes beyond stand-alone smart contract development. It is through this code reusability that novel applications can be constructed with little effort in record time.
Design Spaces Explained
Traditional: Hosted Applications
Traditionally applications have been created by composing “stand-alone” smart contracts. With each smart contract designed to fulfill a different role in the application’s logic. We call these applications hosted applications since they’re deployed and controlled by the code maintainers, and to use them, users transfer funds to the application’s smart contract. Dexes, lending markets, yield aggregators are all examples of hosted applications.
flowchart LR subgraph Developer Team [fas:fa-users-cog Developer Team] %% subgraph Application direction BT A[Application] %% end end User[fa:fa-users Users] ==> A style User fill: #161b25 style A fill: #161b25
Building a Hosted Auto-Compounder
Hosted applications can be built more efficiently with the Abstract SDK because of it’s modular design. As an example, let’s consider an auto-compounder application. The auto-compounder provides liquidity to DEX trading pairs and re-invests the received rewards into the pairs. The application’s logic can be split into three modules:
- DEX Adapter: Provides an interface to perform DEX operations. (e.g., swap tokens, provide liquidity, etc.)
- Staking Adapter: Provides an interface to perform staking operations. (e.g., claim rewards, stake, unstake, etc.)
- Auto-Compounder: Orchestrates the DEX and staking adapters to perform the auto-compounding logic.
If we visualize this application, we can see that the DEX and staking adapters are reusable components that can be used in other applications. The auto-compounder, in this approach, is a unique application that can be installed on an account and used by users to deposit into and withdraw from the auto-compounder application. The account essentially acts as a vault that holds all the users’ funds.
flowchart LR subgraph Autocompounder Application direction BT Autocompounder -.-> Dex Autocompounder -.-> Staking Staking --> Account Autocompounder --> Account Dex --> Account end User[fa:fa-users Users] ==> Autocompounder style User fill: #161b25 style Autocompounder fill: #161b25 style Staking fill: #161b25 style Dex fill: #161b25 style Account fill: #161b25
This approach offers two significant benefits:
- Code Reusability: Developers can reuse the DEX and staking adapters in other applications. Furthermore, Abstract already provides a library of adapters for the most popular protocols. This saves you both time and money as you don’t need to write the integrations yourself.
- Security: The auto-compounder application’s logic is reduced to it’s bare minimum, making it easier to audit and maintain. Furthermore, the DEX and staking adapters are battle-tested smart contracts, which further reduces the attack surface.
Innovative: Self-Hosted Applications
Self-hosted applications, on the other hand, represent a novel concept only achievable with the Abstract SDK. Here, _ users own their applications_ and don’t need to transfer funds to the application’s smart contract. Instead, they deploy the smart contract to their account, which grants the application rights to access those funds. Each application is a new instantiation of a smart contract that is owned and configurable by the user. The user can thus update the application’s code, parameters, and permissions at any time, without relying on the application’s maintainers.
flowchart LR subgraph Developers [fas:fa-users-cog Developers] direction RL A[App] end subgraph Acc1 [fas:fa-user User's Account] direction TB Ap1[App] --> A1[Account] end subgraph Acc2 [fas:fa-user User's Account] direction TB Ap2[App] --> A2[Account] end subgraph Acc3 [fas:fa-user User's Account] direction TB Ap3[App] --> A3[Account] end Store -.-> Ap1 Store -.-> Ap2 Store -.-> Ap3 A ==> Store[fa:fa-store App Store] style A fill: #161b25 style Store fill: #161b25 style Ap1 fill: #161b25 style Ap2 fill: #161b25 style Ap3 fill: #161b25 style A1 fill: #161b25 style A2 fill: #161b25 style A3 fill: #161b25
This approach offers two significant benefits:
- Sovereignty: Users have more control over their funds as they don’t need to trust application maintainers.
- Customizability: Users can tailor their application, leading to novel customization options unavailable with hosted applications.
Let’s see how this applies to the auto-compounder application from before:
Building a Self-Hosted Auto-Compounder
The auto-compounder application can easily be converted into a self-hosted application. Again, by self-hosted we mean that instead of users moving their funds to an externally owned account, they deploy the auto-compounder application to their own account. The auto-compounder application is now owned by the user and can be configured to their liking.
flowchart BT subgraph Alex[Alex's Account] direction TB A1[Autocompounder] -.-> D1[Dex] A1[Autocompounder] -.-> S1[Staking] S1[Staking] --> C1[Account] A1[Autocompounder] --> C1[Account] D1[Dex] --> C1[Account] end subgraph Sarah[Sarah's Account] direction TB A2[Autocompounder] -.-> D2[Dex] A2[Autocompounder] -.-> S2[Staking] S2[Staking] --> C2[Account] A2[Autocompounder] --> C2[Account] D2[Dex] --> C2[Account] end AppStore[fa:fa-store App Store] ==> A1 AppStore[fa:fa-store App Store] ==> A2
With this setup Alex and Sarah can both use the auto-compounder application, but they can configure it to their liking. For example, Alex can configure the auto-compounder to compound his rewards every 24 hours, while Sarah can configure the auto-compounder to compound her rewards every 12 hours. This approach allows for a very customizable and personalized experience.
Abstract SDK - How to get started
This tutorial will walk you through the process of setting up your development environment, creating an app module, and deploying your first app to our on-chain store with cw-orchestator.
Before diving into coding, you might find it beneficial to explore the About Abstract section in our documentation for more insights.
Prerequisites
-
A minimal understanding of Rust is expected. If you are new to Rust, you can find a great introduction to the language in the The Rust Book.
-
The Abstract SDK is built using the CosmWasm smart-contract framework. If you are new to CosmWasm, you can find a great introduction to the framework in the CosmWasm Book.
-
Abstract also makes extensive use of cw-orchestrator, our CosmWasm scripting library. You can read its documentation here.
Setting up the environment
Before you get started with the Abstract SDK, you will need to set up your development environment. This guide will walk you through the process of setting up your environment and creating your first Abstract app module.
Experienced with CosmWasm? Skip to the Using The Template section.
Rust
To work with the SDK you will need the Rust programming language installed on your machine. If you don’t have it installed, you can find installation instructions on the official Rust website.
WASM
Additionally, you will need the Wasm compiler installed to build WASM binaries. You will need rustup
, which you got
when installing Rust on the previous step. To install it the Wasm compiler, run:
$ rustup target add wasm32-unknown-unknown
> installing wasm32-unknown-unknown
Git
You will also need git
installed to clone our template repository. You can find instructions for installing git
on
your operative system here.
IDE
You will need an IDE to write code. Here are a few options we recommend:
Recommended tools
- Rust Analyzer: Rust Analyzer is a language server that provides IDE support for Rust. If you use VS-Code it’s highly recommended.
- Intellij Rust Plugin: open-source Rust plugin compatible with all IntelliJ-based IDEs. You are going to need it if you are using the Intellij IDEA Community Edition IDE, however it’s not needed for the Rust Rover.
- Docker: Our testing infrastructure uses Docker to run the tests. If you want to run the tests locally, you will need to install Docker.
- Just: Just is a command runner that we use to improve the development flow. You can install it by following the instructions on the Github repository.
Using the Template
Time to start building! We’ll get you set up with the Abstract App template which contains:
- A scaffold app module with:
- A basic contract
- cw-orchestrator interface and deployment script
- Integration tests
- A scaffold front-end with:
- A basic UI
- A basic API client
- A set of just commands that will boost your productivity.
Go to our App Template on Github and click on the “Use this template” button to create a new repository based on the template. You can name the repository whatever you want, but we recommend using the name of your module.
Go ahead and read through the readme of the template repository to learn how it is structured. It contains instructions
on how to set up your
development environment, useful commands you can perform using just
, how to test and deploy your app, and more.
Contract layout
The template contains a scaffold contract that you can use as a starting point for your own contract. The contract is
located in the src
directory and is structured as follows:
contract.rs
: This file is the top-level file for your module. It contains the type definition of you module and the const builder that constructs your contract. It also contains a macro that exports your contract’s entry points. You can also specify the contract’s dependencies here.error.rs
: This file contains the error types that your contract can return.msg.rs
: This file contains the custom message types that your contract can receive. These messages also havecw-orchestrator
macros attached to them which comes in useful when you are writing your integration tests.state.rs
: This file contains the state types that your contract will use to store state to the blockchain.interface.rs
: This file contains the interface that your contract will use to interact with thecw-orchestrator
library.replies/
: This directory contains the reply handlers that your contract will use to handle replies.handlers/
: This directory contains the message handlers that your contract will use to handle the different messages it can receive.
If there’s anything you don’t understand about the template please don’t hesitate to reach out to us on our Discord server.
Front-end layout
To generate the front-end scaffold, you can run just ts-codegen
, which will run a code generation script for you found
in the typescript/scripts
folder. The code generation script will generate TypeScript code based on the specifications
defined in the contract schemas.
Once the script is complete, you will find the newly generated code under typescript/src
, and it’s structured as
follows:
index.ts
: The index file bundles and exports all the functionalities from the generated files (Template.types, Template.client, Template.message-composer, Template.msg-builder) under a single namespace called contracts, making it easier to access the various functionalities encapsulated in the other files.Template.client.ts
: The client file contains classes for creating client instances to query and interact with a blockchain module. These client instances can be used to retrieve module configurations or to connect a signing client for transaction functionalities.Template.message-composer.ts
: This file aids in crafting messages necessary for interacting with a specific blockchain contract, particularly for updating configurations. It contains a class that generates message objects that can be sent to the blockchain for execution.Template.msg-builder.ts
: In this file, abstract classes provide static methods to create message objects for querying or executing actions on the blockchain. It essentially facilitates the building of structured messages for blockchain interactions.Template.types.ts
: The types file defines various data structures and types that represent the structure of messages and responses within the blockchain module, aiding in maintaining consistency across different operations in the module.
Tools used in the template
The following Rust tools are used extensively in our template to improve your productivity.
- Taplo: The CI shipped with the
template will perform formatting checks. To ensure you pass the checks, you can install Taplo and use
the
just format
command to format your code and toml files. - Nextest: A better cargo test runner.
- Cargo Limit: Prioritizes errors over warnings in compile output as well as some other small improvements.
- Cargo Watch: Allows you to automatically re-run compilation when files change. This is useful when you are working on the contracts and want to fix compiler errors one by one.
You can install them by running just install-tools
. All the tools are built from the source by Cargo.
Module Builder
Abstract provides multiple module bases, as detailed in our section on modules. These base implementation provide you with the minimal state and configuration required to start building your modular application. After setting up your module base from our template you’ll probably want to customize it. Our module builder pattern allows you to do just that. It also gives you a great overview on all the entry points to you module, and those that others have built.
Overview
The builder pattern employed in building an Abstract module is a slight variation of the actual design pattern. Instead,
the module builder lets you set custom entry point handlers at compile time, meaning you end up with a const
value
that is heavily optimized by the compiler. This ensures that the overhead of using Abstract has a negligible effect on
both the code’s runtime and WASM binary size.
The code-snippets in this example can be found in the counter app example.
In this tutorial we will be working with an App
Module.
App Type
To set up your App module, start by integrating your custom messages. These messages are inserted in the top-level entry point message types, which will be discussed in more detail later. Here’s an example:
pub type CounterApp = AppContract<
CounterError,
CounterInitMsg,
CounterExecMsg,
CounterQueryMsg,
CounterMigrateMsg,
CounterReceiveMsg,
CounterSudoMsg,
>;
All of these messages can be customized and will be used to type-check the rest of your implementation.
Build The App
Now that you have defined your type, you can begin using the builder. To initiate this, first create the base version of the app:
pub const COUNTER_APP: CounterApp = CounterApp::new(COUNTER_ID, APP_VERSION, None)
The constructor takes three variables:
contract_id
: The contract ID is a string that will be saved to acw2
storage item. It’s an important security measure as this ensures that the contract can not be migrated to a different contract with a different function and also acts as an informational tag for off-chain processes.contract_version
: The contract version should be the version of the crate, it is also stored in thecw2
item and is checked when performing migrations and on-chain dependency resolution.metadata
: An optional URL that can be used to retrieve data off-chain. Can be used with the Abstract Metadata Standard to automatically generate interactive front-end components for the module.
All these fields are used in a custom ModuleData
store as well, along with the module’s dependencies, which we will
come back to later. Here’s the definition of the ModuleData
field:
pub const MODULE: Item<ModuleData> = Item::new("module_data");
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct ModuleData {
/// The name of the module, which should be composed of
/// the publisher's namespace and module id. eg. `cw-plus:cw20-base`
pub module: String,
/// Semantic version of the module's crate on release.
/// Is used for migration assertions
pub version: String,
/// List of modules that this module depends on
/// along with its version requirements.
pub dependencies: Vec<Dependency>,
/// URL to data that follows the Abstract metadata standard for
/// resolving off-chain module information.
pub metadata: Option<String>,
}
Handlers
The app can then be customized by adding whatever handler functions you need. These functions are executed whenever a specific endpoint is called on the module. A special feature about the functions is that we insert the instance of your module into the function’s attributes. This enables you to access the module struct in your code. You will learn why this is such a powerful feature in the next section on the Abstract SDK.
Here’s an example of a module with some handlers set:
pub const COUNTER_APP: CounterApp = CounterApp::new(COUNTER_ID, APP_VERSION, None)
.with_instantiate(handlers::instantiate)
.with_execute(handlers::execute)
.with_query(handlers::query)
.with_sudo(handlers::sudo)
.with_receive(handlers::receive)
.with_replies(&[(1u64, handlers::reply)])
.with_migrate(handlers::migrate);
These handlers are functions that allow you to customize the smart contract’s behavior. For example, here’s a
custom execute
handler that updates the contract’s config state.
#![allow(unused)] fn main() { pub fn execute( deps: DepsMut, _env: Env, info: MessageInfo, app: CounterApp, // <-- Notice how the `CounterApp` is available here msg: CounterExecMsg, ) -> CounterResult { match msg { CounterExecMsg::UpdateConfig {} => update_config(deps, info, app), } } /// Update the configuration of the app fn update_config(deps: DepsMut, msg_info: MessageInfo, app: CounterApp) -> CounterResult { // Only the admin should be able to call this app.admin.assert_admin(deps.as_ref(), &msg_info.sender)?; Ok(app .tag_response(Response::default(), "update_config") .set_data("counter_exec".as_bytes())) } }
You can find more application code to read in our 💥 Awesome Abstract repository 💥.
The available handlers are:
with_execute
: Called when the App’sExecuteMsg
is called on the instantiate entry point.with_instantiate
: Called when the App’sInstantiateMsg
is called on the instantiate entry point.with_query
: Called when the App’sQueryMsg::Module
is called on the query entry point.with_migrate
: Called when the App’sMigrateMsg
is called on the migrate entry point.with_replies
: Called when the App’s reply entry point is called. Matches the function’s associated reply-id.with_sudo
: Called when the App’sSudoMsg
is called on the sudo entry point.with_receive
: Called when the App’sExecuteMsg::Receive
variant is called on the execute entry point.with_ibc_callbacks
: Called when the App’sExecuteMsg::IbcCallback
is called on the execute entry point. Matches the callback’s callback ID to its associated function.
In the case of adapters, the handlers are the same, except for with_migrate
and with_sudo
that are missing for
reasons we explain in the adapter section.
For a full overview of the list of handlers available, please refer to the respective module type documentation:
Below, we examine each handler in greater detail. The base
fields and variants mentioned in the messages below are
defined
by the base module type that you chose to use, an App
in this case.
Instantiate
The instantiate entry point is a mutable entry point of the contract that can only be called on contract instantiation. Instantiation of a contract is essentially the association of a public address to a contract’s state.
Function Signature
Expected function signature for the custom instantiate handler:
/// Function signature for an instantiate handler.
pub type InstantiateHandlerFn<Module, CustomInitMsg, Error> =
fn(DepsMut, Env, MessageInfo, Module, CustomInitMsg) -> Result<Response, Error>;
Message
In order to instantiate an Abstract Module, you need to provide an InstantiateMsg with the following structure:
#[cosmwasm_schema::cw_serde]
pub struct InstantiateMsg<BaseMsg, CustomInitMsg = Empty> {
/// base instantiate information
pub base: BaseMsg,
/// custom instantiate msg
pub module: CustomInitMsg,
}
When the module’s instantiate function is called the struct’s module
field is passed to your custom instantiation
handler for you to perform any custom logic.
Execute
The execute entry point is a mutable entry point of the contract. Logic in this function can update the contract’s state and trigger state changes in other contracts by calling them. It is where the majority of your contract’s logic will reside.
Function Signature
Expected function signature for the custom execute handler:
/// Function signature for an execute handler.
pub type ExecuteHandlerFn<Module, CustomExecMsg, Error> =
fn(DepsMut, Env, MessageInfo, Module, CustomExecMsg) -> Result<Response, Error>;
Message
Called when the App’s ExecuteMsg::Module
variant is called on the execute entry point.
/// Wrapper around all possible messages that can be sent to the module.
#[cosmwasm_schema::cw_serde]
pub enum ExecuteMsg<BaseMsg, CustomExecMsg, ReceiveMsg = Empty> {
/// A configuration message, defined by the base.
Base(BaseMsg),
/// An app request defined by a base consumer.
Module(CustomExecMsg),
/// IbcReceive to process IBC callbacks
/// In order to trust this, the apps and adapters verify this comes from the ibc-client contract.
IbcCallback(IbcResponseMsg),
/// Receive endpoint for CW20 / external service integrations
Receive(ReceiveMsg),
}
The content of the Module
variant is passed to your custom execute handler.
Query
The query entry point is the non-mutable entry point of the contract. Like its name implies it it used to retrieve data from the contract’s state. This state retrieval can have a computation component but it can not alter the contract’s or any other state.
Function Signature
Expected function signature for the custom query handler:
/// Function signature for a query handler.
pub type QueryHandlerFn<Module, CustomQueryMsg, Error> =
fn(Deps, Env, &Module, CustomQueryMsg) -> Result<Binary, Error>;
Message
Called when the App’s QueryMsg::Module
variant is called on the query entry point.
#[cosmwasm_schema::cw_serde]
#[derive(QueryResponses)]
#[query_responses(nested)]
pub enum QueryMsg<BaseMsg, CustomQueryMsg = Empty> {
/// A query to the base.
Base(BaseMsg),
/// Custom query
Module(CustomQueryMsg),
}
The content of the Module
variant is passed to your custom query handler.
Migrate
The migrate entry point is a mutable entry point that is called after a code_id change is applied to the contract. A migration in CosmWasm essentially swaps out the code that’s executed at the contract’s address while keeping the state as-is. The implementation of this function is often used to change the format of the contract’s state by loading the data as the original format and overwriting it with a new format, in case it changed. All adapter base implementations already perform version assertions that make it impossible to migrate to a contract with a different ID or with a version that is lesser or equal to the old version.
Function Signature
Expected function signature for the custom migrate handler:
/// Function signature for a migrate handler.
pub type MigrateHandlerFn<Module, CustomMigrateMsg, Error> =
fn(DepsMut, Env, Module, CustomMigrateMsg) -> Result<Response, Error>;
Message
Called when the App’s migrate entry point is called. Uses the struct’s module
field to customize the migration. Only
this field is passed to the handler function.
#[cosmwasm_schema::cw_serde]
pub struct MigrateMsg<BaseMsg = Empty, CustomMigrateMsg = Empty> {
/// base migrate information
pub base: BaseMsg,
/// custom migrate msg
pub module: CustomMigrateMsg,
}
Reply
The reply entry point is a mutable entry point that is optionally called after a previous mutable action. It is
often used by factory contracts to retrieve the contract of a newly instantiated contract. It essentially provides the
ability perform callbacks on actions. A reply can be requested using CosmWasm’s SubMsg
type and requires a
unique ReplyId
which is a u64
. The customizable handler takes an array of (ReplyId, ReplyFn)
tuples and matches
any incoming reply on the correct ReplyId
for you.
Function Signature
Expected function signature for the custom reply handler:
/// Function signature for a reply handler.
pub type ReplyHandlerFn<Module, Error> = fn(DepsMut, Env, Module, Reply) -> Result<Response, Error>;
Message
There is no customizable message associated with this entry point.
Sudo
The sudo entry point is a mutable entry point that can only be called by the chain’s governance module. I.e. any calls made to this contract should have been required to have gone through the chain’s governance process. This can vary from chain to chain.
Function Signature
Expected function signature for the custom sudo handler:
/// Function signature for a sudo handler.
pub type SudoHandlerFn<Module, CustomSudoMsg, Error> =
fn(DepsMut, Env, Module, CustomSudoMsg) -> Result<Response, Error>;
Message
There is no base message for this entry point. Your message will be the message that the endpoint accepts.
Receive
The receive handler is a mutable entry point of the contract. It is similar to the execute
handler but is specifically
geared towards handling messages that expect a Receive
variant in the ExecuteMsg
. Examples of this include but are
not limited to:
- Cw20 send messages
- Nois Network random number feed
Function Signature
Expected function signature for the custom receive handler:
/// Function signature for a receive handler.
pub type ReceiveHandlerFn<Module, ReceiveMsg, Error> =
fn(DepsMut, Env, MessageInfo, Module, ReceiveMsg) -> Result<Response, Error>;
Message
Called when the App’s ExecuteMsg::Receive
variant is called on the execute entry point.
/// Wrapper around all possible messages that can be sent to the module.
#[cosmwasm_schema::cw_serde]
pub enum ExecuteMsg<BaseMsg, CustomExecMsg, ReceiveMsg = Empty> {
/// A configuration message, defined by the base.
Base(BaseMsg),
/// An app request defined by a base consumer.
Module(CustomExecMsg),
/// IbcReceive to process IBC callbacks
/// In order to trust this, the apps and adapters verify this comes from the ibc-client contract.
IbcCallback(IbcResponseMsg),
/// Receive endpoint for CW20 / external service integrations
Receive(ReceiveMsg),
}
Ibc Callback
The ibc callback handler is a mutable entry point of the contract. It is similar to the execute
handler but is
specifically geared towards handling callbacks from IBC actions. Since interacting with IBC is an asynchronous process
we aim to provide you with the means to easily work with IBC. Our SDK helps you send IBC messages while this handler
helps you execute logic whenever the IBC action succeeds or fails. Our framework does this by optionally allowing you to
add callback information to any IBC action. A callback requires a unique CallbackId
which is a String
. The callback
handler takes an array of (CallbackId, IbcCallbackFn)
tuples and matches any incoming callback on the
correct CallbackId
for you. Every call to this handler is verified by asserting that the caller is the framework’s
IBC-Client contract.
Function Signature
/// Function signature for an IBC callback handler.
pub type IbcCallbackHandlerFn<Module, Error> = fn(
DepsMut,
Env,
MessageInfo,
Module,
CallbackId,
CallbackMessage,
Callback,
) -> Result<Response, Error>;
Message
Called when the App’s ExecuteMsg::IbcCallback
variant is called on the execute entry point. The receiving type is not
customizable but contains the IBC action acknowledgment.
/// Wrapper around all possible messages that can be sent to the module.
#[cosmwasm_schema::cw_serde]
pub enum ExecuteMsg<BaseMsg, CustomExecMsg, ReceiveMsg = Empty> {
/// A configuration message, defined by the base.
Base(BaseMsg),
/// An app request defined by a base consumer.
Module(CustomExecMsg),
/// IbcReceive to process IBC callbacks
/// In order to trust this, the apps and adapters verify this comes from the ibc-client contract.
IbcCallback(IbcResponseMsg),
/// Receive endpoint for CW20 / external service integrations
Receive(ReceiveMsg),
}
Dependencies
There is another method accessible on the module builder, which is the with_dependencies
function. As it states it
allows you to specify any smart contract dependencies that your module might require. This is a key requirement for
building truly composable and secure applications. We’ll cover dependencies further
the dependencies section.
Summary
The Abstract SDK allows you to easily construct modules by using our low-overhead smart contract builder. By employing this pattern you re-use the base contract’s code, allowing you to focus on the ideas that make your product unique.
In the next section we’ll cover how you can use the module object that we make available in the function handlers to write highly functional smart contract code.
Ever wanted to swap on any cosmos DEX with only one line of code? Look no further!
SDK
Now that you’ve got your module set up you’re ready for our hot sauce. While you can create any regular smart contract in a module, it’s where our software shines. Instead, we’ve created an account abstraction programming toolbox that allows you to easily control an Abstract Account’s interactions, as well as create your own APIs that can be used by other developers to interact with your unique application. Composability galore!
How it works
The abstract-sdk
crate is a toolbox for developers to create composable smart contract APIs. It allows you to use
composed functionality with a few keystrokes through its combination of supertraits and blanket implementations.
Supertraits are Rust traits that have one or multiple trait bounds while a blanket implementation is a Rust
implementation that is automatically implemented for every object that meets the trait bounds. The Abstract SDK uses
both to achieve its modular design.
For more information about traits, supertraits and blanket implementations, check out the Rust documentation:
APIs
Abstract API objects are Rust structs that expose some smart contract functionality. Such an API can only be retrieved if a contract (or feature-object) implements the required features/api traits. Access to an API is automatically provided if the trait constraints for the API are met by the contract.
Most of the APIs either return a CosmosMsg
or an AccountAction
. The CosmosMsg
is a message that should be added
as-is to the Response
to perform some action.
CosmosMsg
Example
This example sends coins from the local contract (module) to the account that the application is installed on which does not require the account itself to execute the action.
// Get bank API struct from the app
let bank: Bank<'_, MockModule> = app.bank(deps.as_ref());
// Create coins to deposit
let coins: Vec<Coin> = coins(100u128, "asset");
// Construct messages for deposit (transfer from this contract to the account)
let deposit_msgs: Vec<CosmosMsg> = bank.deposit(coins.clone()).unwrap();
// Add to response
let response: Response = Response::new().add_messages(deposit_msgs);
Alternatively AccountAction
structs can also be returned by an API. An AccountAction
is supposed to be forwarded to
the Abstract Account to let the account perform action. AccountAction
s can be executed with
the Executor
API. The returned CosmosMsg
should be added to the action’s Response
.
AccountAction
Example
This example sends coins from the account to another address which requires the account itself to execute the action.
let recipient: Addr = Addr::unchecked("recipient");
let bank: Bank<'_, MockModule> = app.bank(deps.as_ref());
let coins: Vec<Coin> = coins(100u128, "asset");
let bank_transfer: AccountAction = bank.transfer(coins.clone(), &recipient).unwrap();
let executor: Executor<'_, MockModule> = app.executor(deps.as_ref());
let account_message: ExecutorMsg = executor.execute(vec![bank_transfer]).unwrap();
let response: Response = Response::new().add_message(account_message);
Creating your own API
The Bank
API allows
developers to transfer assets
from and to the Account. We now want to use this API to create a Splitter
API that splits the transfer of some amount
of funds between a set of receivers.
The code behind this example is available here.
// Trait to retrieve the Splitter object
// Depends on the ability to transfer funds
pub trait SplitterInterface: TransferInterface + ModuleIdentification {
fn splitter<'a>(&'a self, deps: Deps<'a>) -> Splitter<Self> {
Splitter { base: self, deps }
}
}
// Implement for every object that can transfer funds
impl<T> SplitterInterface for T where T: TransferInterface + ModuleIdentification {}
impl<'a, T: SplitterInterface> AbstractApi<T> for Splitter<'a, T> {
fn base(&self) -> &T {
self.base
}
fn deps(&self) -> Deps {
self.deps
}
}
impl<'a, T: SplitterInterface> ApiIdentification for Splitter<'a, T> {
fn api_id() -> String {
"Splitter".to_owned()
}
}
#[derive(Clone)]
pub struct Splitter<'a, T: SplitterInterface> {
base: &'a T,
deps: Deps<'a>,
}
impl<'a, T: SplitterInterface> Splitter<'a, T> {
/// Split an asset to multiple users
pub fn split(&self, asset: AnsAsset, receivers: &[Addr]) -> AbstractSdkResult<AccountAction> {
// split the asset between all receivers
let receives_each = AnsAsset {
amount: asset
.amount
.multiply_ratio(Uint128::one(), Uint128::from(receivers.len() as u128)),
..asset
};
// Retrieve the bank API
let bank = self.base.bank(self.deps);
receivers
.iter()
.map(|receiver| {
// Construct the transfer message
bank.transfer(vec![&receives_each], receiver)
})
.try_fold(AccountAction::new(), |mut acc, v| match v {
Ok(action) => {
// Merge two AccountAction objects
acc.merge(action);
Ok(acc)
}
Err(e) => Err(e),
})
}
}
These APIs can then be used by any contract that implements its required traits, in this case the TransferInterface
.
let asset = AnsAsset {
amount: Uint128::from(100u128),
name: "usd".into(),
};
let receivers = vec![
Addr::unchecked("receiver1"),
Addr::unchecked("receiver2"),
Addr::unchecked("receiver3"),
];
let split_funds = module.splitter(deps.as_ref()).split(asset, &receivers)?;
assert_eq!(split_funds.messages().len(), 3);
let msg: ExecutorMsg = module.executor(deps.as_ref()).execute(vec![split_funds])?;
Ok(Response::new().add_message(msg))
Available API Objects
The following API objects are available in the Abstract SDK:
Other projects have also started building APIs. Here are some examples:
Cron Cats
- More coming soon…
Features
Features are the lowest-level traits that are contained within the SDK and they don’t have any trait bounds. They generally act as data accessor traits. I.e. if a struct implements a feature it means that it has some way to get the information required by that feature.
Here’s an example of such a feature:
#![allow(unused)] fn main() { use crate::{ans_resolve::Resolve, cw_helpers::ApiQuery, AbstractSdkResult}; use abstract_core::{ ans_host::{AssetPairingFilter, AssetPairingMapEntry, PoolAddressListResponse, QueryMsg}, objects::{ans_host::AnsHost, DexAssetPairing}, }; use cosmwasm_std::Deps; use super::ModuleIdentification; use crate::apis::{AbstractApi, ApiIdentification}; /// Accessor to the Abstract Name Service. pub trait AbstractNameService: Sized { /// Get the ANS host address. fn ans_host(&self, deps: Deps) -> AbstractSdkResult<AnsHost>; /// Construct the name service client. fn name_service<'a>(&'a self, deps: Deps<'a>) -> AbstractNameServiceClient<Self> { AbstractNameServiceClient { _base: self, deps, host: self.ans_host(deps).unwrap(), } } } }
Any structure that implements this trait has access to the Abstract Name Service, and thus has a way to resolve ANS entries. By composing these features it is possible to write advanced APIs that are automatically implemented on objects that support its required features.
Now instead of letting you implement these traits yourself, we’ve already gone ahead and implemented them for the App and Adapter structs. Here’s the implementation for the App:
#![allow(unused)] fn main() { impl< Error: ContractError, CustomInitMsg, CustomExecMsg, CustomQueryMsg, CustomMigrateMsg, ReceiveMsg, SudoMsg, > AbstractNameService for AppContract< Error, CustomInitMsg, CustomExecMsg, CustomQueryMsg, CustomMigrateMsg, ReceiveMsg, SudoMsg, > { fn ans_host(&self, deps: Deps) -> AbstractSdkResult<AnsHost> { // Retrieve the ANS host address from the base state. Ok(self.base_state.load(deps.storage)?.ans_host) } } }
So when you’re building your application the module struct already has the features and data required to do the basic abstract operations. With this in place we can start creating more advanced functionality.
Usage
Add abstract-sdk
to your Cargo.toml
by running:
cargo add abstract-sdk
Then import the prelude in your contract. This will ensure that you have access to all the traits which should help your IDE with auto-completion.
use abstract_sdk::prelude::*;
Account Creation
We know you’re really excited to start playing with your module but there’s one more thing you need to do first. Abstract cares about your efforts, and we believe you should have the ability to monetize your modules in any way that you see fit. To help us provide that feature we created the concept of module namespaces. A module namespace is your (or your team’s) publishing domain for Abstract modules. Through this design you can monetize your product through a namespace or per-modules basis as explained in more detail in the monetization section.
Each namespace must be associated with an Abstract Account, hence you will need to create one.
To create an Abstract Account and claim your Namespace, please follow the instructions in this section.
With the Abstract Account and Namespace setup out of the way let’s get to the fun part: building your module!
Testing Your Module
Testing your smart contracts is a crucial step in its development. Without proper testing you risk compromising the accounts of your users and with it the funds that they hold. For that reason we expect modules to be thoroughly tested before they are allowed on our platform.
This section of the documentation outlines the different testing methods. Each method is accompanied by an Abstract helper. These helpers assist you in setting up your testing environment.
Unit-testing
The lowest level of testing is unit testing. Unit tests allow you to easily test complex, self-contained logic. Because unit tests should be self-contained, any queries made to other contracts need to be mocked. These mocks act as “query catchers”, allowing you to specify a response for a specific query.
Sadly constructing these mock queries is time-consuming and involves a lot of boilerplate. Additionally, there are
queries that your module should always support as they are part of its base implementation. For those reasons we created
an abstract-testing
package.
The abstract-testing
provides you with some small abstractions that allow you to mock Smart and Raw queries with ease.
What’s the difference between a Smart and a Raw query?
-
Smart Queries: A smart query is a query that contains a message in its request. It commonly involves computation on the queried contract. After this optional computation and state loading, the contract responds with a ResponseMsg. Mocking this type of query involves matching the serialized query request message (
Binary
) to a specific message type and returning a serialized response. Any expected computation needs to be mocked as well. -
Raw Queries: A raw query is a simple database key-value lookup. To mock this type of query you need to provide a mapping of the raw key to a raw value. The returned value then needs to be interpreted correctly according to the store’s type definitions.
Mock Querier
The abstract-testing
package contains
a MockQuerierBuilder
.
It uses the common builder pattern to allow for efficient mock construction. Let’s see how!
Mocking Smart Queries
Mocking a smart-query with
the MockQuerierBuilder
is easy! You do it by calling the with_smart_handler
function.
Example
#![allow(unused)] fn main() { /// let querier = MockQuerierBuilder::default().with_smart_handler("contract_address", |msg| { /// // handle the message /// let res = match from_json::<MockModuleQueryMsg>(msg).unwrap() { /// // handle the message /// MockModuleQueryMsg => /// return to_json_binary(&MockModuleQueryResponse {}).map_err(|e| e.to_string()) /// }; /// }).build(); }
Mocking Raw Queries
Instead of manually mapping the key-value relation and it’s types, we can use the available contract storage types. Using the storage types ensures that the mock and its data operations are the same as in the actual implementation. It also saves us a lot of work related to key serialization.
This approach allow you to easily map Item
and Map
datastores.
Example
#![allow(unused)] fn main() { /// let querier = MockQuerierBuilder::default().with_raw_handler("contract1", |key: &str| { /// // Example: Let's say, in the raw storage, the key "the key" maps to the value "the value" /// match key { /// "the key" => to_json_binary("the value").map_err(|e| e.to_string()), /// _ => to_json_binary("").map_err(|e| e.to_string()) /// } }
Abstract Querier
The easiest and best way to start using the querier is to use
the AbstractMockQuerierBuilder::mocked_account_querier_builder()
method. This method sets up a mock querier with an
initial Abstract Account.
Integration Testing
Integration testing your contract involves deploying your contract and any of its dependencies to a mock environment.
Abstract uses cw-orchestrator’s Mock
struct that is backed by a cw-multi-test::App
which you might be familiar with.
The Mock
struct provides a simulation of the CosmWasm environment, enabling testing of contract functionalities.
cw-orchestrator
is a CosmWasm scripting tool that we developed to improve the speed at which developers can test and deploy their applications. We recommend reading the cw-orchestrator documentation if you are not yet familiar with it.
Example
The Mock
encapsulates:
- A default sender for transactions.
- A state to map contract_id to its details.
- An emulation of the CosmWasm backend with app.
In this example, we use a setup functino to initialize our test environment. The setup function is utilized to:
- Initialize the contract you want to test within the mock environment, the counter contract in this case.
- Upload and instantiate the contract.
- Retrieve essential details like code_id and contract address for further interactions.
This provides you with a streamlined approach to test and validate smart contract operations in a controlled setting.
#![allow(unused)] fn main() { /// Instantiate the contract in any CosmWasm environment fn setup<Chain: CwEnv>(chain: Chain) -> CounterContract<Chain> { // Construct the counter interface let contract = CounterContract::new(CONTRACT_NAME, chain.clone()); let admin = Addr::unchecked(ADMIN); // Upload the contract let upload_resp = contract.upload().unwrap(); // Get the code-id from the response. let code_id = upload_resp.uploaded_code_id().unwrap(); // or get it from the interface. assert_eq!(code_id, contract.code_id().unwrap()); // Instantiate the contract let msg = InstantiateMsg { count: 1i32 }; let init_resp = contract.instantiate(&msg, Some(&admin), None).unwrap(); // Get the address from the response let contract_addr = init_resp.instantiated_contract_address().unwrap(); // or get it from the interface. assert_eq!(contract_addr, contract.address().unwrap()); // Return the interface contract } #[test] fn count() { // Create a sender let sender = Addr::unchecked(ADMIN); // Create a user let user = Addr::unchecked(USER); // Create the mock let mock = Mock::new(&sender); // Set up the contract let contract = setup(mock.clone()); // Increment the count of the contract contract // Set the caller to user .call_as(&user) // Call the increment function (auto-generated function provided by CounterExecuteMsgFns) .increment() .unwrap(); // Get the count. use counter_contract::CounterQueryMsgFns; let count1 = contract.get_count().unwrap(); // or query it manually let count2: GetCountResponse = contract.query(&QueryMsg::GetCount {}).unwrap(); assert_eq!(count1, count2); // Check the count assert_eq!(count1.count, 2); // Reset use counter_contract::CounterExecuteMsgFns; contract.reset(0).unwrap(); let count = contract.get_count().unwrap(); assert_eq!(count.count, 0); // Check negative case let exec_res = contract.call_as(&user).reset(0); let expected_err = ContractError::Unauthorized {}; assert_eq!( exec_res.unwrap_err().downcast::<ContractError>().unwrap(), expected_err ); } }
Local Daemon Testing
Once you have confirmed that your module works as expected you can spin up a local node and deploy Abstract + your app onto the chain. You can do this by running the test-local example, which uses a locally running juno daemon to deploy to. At this point you can also test your front-end with the contracts.
Locally testing your Abstract deployment is difficult if it depends on other protocols, and those protocols don’t make use of cw-orchestrator.
Testing
You can test the module using the different provided methods.
- Integration testing:
- Local Daemon:
Module Deployment
Deploying your module is an easy 3-step process: Module Uploading, Registration and Schema Linking. Let’s go over each step in detail.
This doc assumes you’re using the module app template, if you’re not we recommend looking at the relevant files in the template to set up your own deployment process.
Module Uploading
Uploading your module involves first compiling your module as a WASM binary and then uploading it to the network(s) you want your module to be available on. This will yield you a code_id that is a unique identifier for your module on the network.
Compiling your module
You can compile your module by running the following command:
$ just wasm
> Compiling to WASM...
The WASM optimizer uses a docker container to compile your module. If you don’t have docker installed you can install it from here.
This should result in an artifacts
directory being created in your project root. Inside you will find
a my_module.wasm
file that is your module’s binary.
Now you can go ahead and deploy the module to the network(s) you want to make it available on. You can do this by running the following command:
$ just deploy uni-1
> Deploying module...
This will use the module’s examples/deploy.rs
script to deploy the module to the uni-1
network. The resulting
code-id of your contract should now be in the state.json
file created for you. The script will also attempt to
register the module on the Abstract Version Control, hence the mnemonic used in the script should be the same as the one
you used to create the account and register the namespace.
JSON Schema Linking
To improve the user-experience for developers using your module we recommend linking your module’s JSON schema to the Abstract Version Control. This will allow developers (and you) to use the Abstract web app to interact with your module.
To link your module’s schema you can run the following command:
$ just publish-schemas <namespace> <name> <version>
> Publishing schemas...
Where you fill the <namespace>
, <name>
and <version>
with the same values you used to register your module on the
Abstract Version Control.
Module Installation
To install your module, go to the Abstract Account Dashboard,
go to your Account (or a new one) and click on the Modules
tab. Here you will find a list of all the modules you have
registered on the Abstract Version Control. Click on the Install
button next to your module and select the network you
want to install it on. This will open a modal with the following fields:
Module Dependencies
In the Abstract SDK, modules have conditions that must be met before they can be installed. These conditions largely revolve around module dependencies and version requirements. When installing a module, the system will check its dependencies and ensure that they are installed and meet the version requirements specified by the module.
Here’s how the process of installing a module and checking module dependencies looks:
sequenceDiagram autonumber actor U as Owner participant M as Manager participant F as Module Factory participant Mo as Module participant P as Proxy U ->> M: Install Module M -->> F: Install Module opt App instantiate F -->> Mo: Instantiate Module end M -->> Mo: Query Module Dependencies M -->> M: Assert Dependency Requirements M -->>+ M: Add Module as Dependent on its Dependencies M -->>+ P: Allowlist Module
Use Cases
Welcome to the Use Cases section of the Abstract documentation. This part of our guide is designed to show you the breadth and depth of possibilities that Abstract unlocks in the world of blockchain development.
As you navigate through this section, you will discover a variety of applications where Abstract’s unique approach to modular development and perpetual rewarding system can truly shine. We will explore real-life scenarios across different domains, such as Decentralized Application Development, Open Source Contribution, Decentralized Finance, and Educational Use. For each application, we’ll present concrete examples to illustrate how Abstract’s principles and technology have been used to drive value and innovation.
The journey through these use cases will provide you with a deeper understanding of Abstract’s potential and how its distinctive approach can revolutionize the way you develop on the blockchain. By the end of this section, we hope you’ll be inspired to consider new ways in which you could leverage Abstract in your own projects.
Decentralized Application Development
Abstract’s modular design allows developers to leverage pre-built functionalities, minimizing redundant work and accelerating the creation process. With Abstract, developers simply choose the modules they need—user authentication, data storage, payment processing—and integrate them effortlessly. Meanwhile, the platform’s blockchain nature enhances security, providing users with a safer, transparent experience. Plus, Abstract’s usage-based rewards mean that every use of a module generates income for its creator, promoting a cycle of continuous improvement and fair compensation.
Case Study
Open Source Contribution
Open source contribution is no longer a thankless job with Abstract. The platform has revolutionized the way open source developers are compensated, ensuring they are rewarded every time their code is used. Abstract’s unique model, powered by blockchain and tokenomics, ensures perpetual rewards based on the usage of their work.
Rather than the traditional one-off donations or sponsorships, Abstract brings a sustainable, fair, and motivating environment. The more your module is used, the more you earn. This directly ties your effort with your reward and incentivizes the production of quality work. It’s an open-source world where every contribution counts, and every use of your module is a vote of confidence and a token of appreciation.
Case Study
CronCat, built as an Abstract SDK module, is a decentralized scheduling system for blockchain transactions, designed to automate and schedule tasks for any contract within a blockchain environment. It provides a general-purpose, fully autonomous network enabling scheduled function calls for blockchain contract execution.
Decentralized Finance
Abstract simplifies the creation of DeFi applications, empowering developers to extend financial services to anyone with internet access. With its modular architecture, Abstract allows developers to create, share, and reuse DeFi modules, reducing development time, effort, and cost.
In the Abstract ecosystem, you can seamlessly integrate pre-existing DeFi modules into your applications, streamlining the process and boosting your development speed. Need a lending protocol or an AMM (Automated Market Maker) feature? Simply find a module, plug it into your application, and let Abstract do the heavy lifting.
Moreover, as every module is openly available on the platform, developers across the globe are continuously contributing to and refining the DeFi tools at your disposal. It’s never been easier to take part in the DeFi revolution and bring financial services to the unbanked and underbanked, all thanks to Abstract.
Case Study
- Equilibrium: Auto-rebalancing protocol for weighted portfolios of Cosmos assets.
- 4t2 Finance: Yield Aggregator for IBC-enabled Chains.
Educational Use
As an open, modular blockchain platform, Abstract is not just a tool for development but also a fantastic learning resource for budding developers keen to delve into the world of blockchain, modular architecture, and decentralized governance.
For blockchain enthusiasts, Abstract offers a real-world application of blockchain technology. By interacting with Abstract’s tokenomics, developers can understand how blockchain can be used to create secure, transparent, and decentralized systems, from DeFi applications to governance protocols.
The modular architecture of Abstract allows developers to explore how complex applications can be built from reusable, interchangeable modules. By experimenting with the platform’s modules, users can understand how to design, implement, and integrate modules into larger systems effectively.
Moreover, Abstract’s decentralized governance model offers invaluable insights into how decentralized systems can be managed and maintained. Through participating in governance with the ACT token, developers can learn about consensus mechanisms, voting systems, and the challenges and solutions involved in decentralized decision-making.
In essence, Abstract provides an all-in-one educational platform for any developer seeking to deepen their understanding of these critical areas in today’s tech landscape.
Case Study
There’s no better way to grasp the power and potential of Abstract than by diving in and exploring it firsthand. Whether you’re a seasoned developer, an open-source contributor, a DeFi enthusiast, or a curious learner, Abstract opens up a world of possibilities for you to discover. Start creating modules, contributing to the open-source community, building DeFi applications, or simply learning about the fascinating domains of blockchain, modular architecture, and decentralized governance. The journey with Abstract is certain to enrich your development skills, broaden your understanding, and potentially pave the way for you to create lasting value in the tech world.
Equilibrium
Equilibrium, dubbed as “The Cosmos Rebalancer” and awarded at HackWasm 2022, operates an auto-rebalancing protocol to facilitate creation of weighted portfolios of Cosmos assets, with portfolio allocations auto-adjusted per market fluctuations. The protocol is built using the Abstract SDK as a foundation as well as the CronCat protocol, and aims to boost liquidity within the Atom Economic Zone. Equilibrium’s contracts are highly composable, audited for security, and enable innovative financial products on the Neutron blockchain. This initiative seeks to align with Neutron’s core values and enhance its ecosystem by attracting investments, liquidity, and maximizing Miner Extractable Value (MEV) opportunities.
For more details about Equilibrium, please visit the official website.
4t2 Finance
FortyTwo, winner of the Cosmoverse 2023 pitch contest, aims to simplify access to the Cosmos ecosystem, acting as an entry point and yield aggregator for all IBC-enabled chains. It addresses Cosmos’ complexity by providing a unified user interface, optimized liquidity, and portfolio tracking, among other features. Through FortyTwo, users can interact with cross-chain DeFi and NFTs, manage their portfolios, and find optimal places to swap or earn on their crypto assets.
FortyTwo is built on the Abstract SDK.
For more details about FortyTwo, please visit the official website.
Frequently Asked Questions (FAQ)
- What is Abstract?
Abstract is a CosmWasm development platform designed to empower developers to craft secure and dynamic applications effortlessly. It boasts a modular architecture and provides an exhaustive range of tools, streamlining the development process and catalyzing the innovation of solutions in the blockchain space.
- Who can use Abstract?
There are two faces to Abstract:
- For developers who want to build any CosmWasm-based application quickly and securely, leveraging the security and modularity of the Abstract framework.
- For project ideators who want to setup their applications quickly and get their MVP out the door ASAP. Create an Abstract Account and install modules available in the marketplace to expose application functionality.
- How does Abstract differ from other blockchain platforms?
While there are other blockchain frameworks available, Abstract stands out due to its unique combination of a modular architecture, CosmWasm integration, and comprehensive developer tools, all aimed at simplifying and enhancing the development experience.
- How can I get started with Abstract?
To get started with Abstract, check out getting started! You will find comprehensive guides, tutorials, and resources to help you understand the platform’s features and functionality. Additionally, you can join our developer community on Discord to connect with like-minded developers and seek assistance if needed.
- Can I contribute to the Abstract ecosystem?
Absolutely! Abstract values community contributions and welcomes developers to contribute to the growth of CosmWasm. The best ways for you to contribute are by creating modules (see the getting started docs), sharing your insights and knowledge, participating in discussions, and collaborating on some of our open-source projects. Check out the Contributing page to learn more about how you can get involved.
- What are the costs associated with using Abstract?
Abstract offers various tiers, free and paid depending on your needs. For a detailed breakdown of costs, it’s best to visit our plans page or contact the Abstract team for specific pricing details.
- How does Abstract ensure the security of financial applications?
The marketplace on which all modules are registered and installed requires that each module be audited and conform to our security standards. Additionally, the modular architecture allows developers to leverage pre-built functionalities and best practices, reducing the risk of vulnerabilities. We are partnering with Oak Security to ensure every module is up to spec.
- How can I stay updated with Abstract’s latest developments?
Follow us on X @AbstractSDK to stay in the loop with our latest advancements!
- What about cw-orchestrator?
cw-orchestrator is a CosmWasm scripting, testing, and deployment tool designed to simplify interactions with CosmWasm smart contracts. By providing a set of macros that generate type-safe interfaces for your contracts, it not only enhances the code’s readability and maintainability but also reduces testing and deployment overhead. Check it out!
- Where can I seek support if I face issues?
The Abstract community is active and welcoming. If you’re encountering issues or have questions, you can join our developer community on Discord or browse the platform’s documentation for detailed guides and answers.
Abstract In 5 Minutes
Adair
A pitch of the Abstract Money CosmWasm application framework. A quick overview of the advanced features of the developer components and their relevance in an interchain ecosystem.
An Overview of the Abstract Platform - You Don’t Need To Know
Howard
Explore the transformative potential of abstraction, focusing on account and chain abstraction within the Web3 and Cosmos ecosystems. We’ll delve into how these concepts simplify blockchain interactions and enhance interoperability, while also touching on the importance of Web2 powered auth systems in bridging the authentication gap.
Why and How to Build on Abstract
Howard
Learn about the different components of Abstract and what it means to build applications on a chain-agnostic framework.
When All You Have is Cosmos, Everything Looks Like a Chain - The Fat Interface Thesis
Adair
An overview of the migration from the Fat Protocol Thesis to the Fat Application Thesis, and finally the Fat Interface Thesis (same as FPT). Identifies the key components in realizing true interchain applications without the users being aware of the technology behind them.
Abstract Glossary
These are some definitions used in our documentation:
Abstract
A framework designed to simplify the development of decentralized applications in the Cosmos ecosystem. It offers tools and infrastructure for composable smart-contract applications.
Abstract Account
A unique entity within the Abstract framework that can have modules installed onto it, enabling various functionalities. It consists of a Manager and a Proxy contract.
Abstract Account Console
A web-based interface that provides functionalities like account management, module management, name service, dev tools, and delegations.
Abstract APIs
Interfaces provided by Abstract to facilitate interactions between the frontend and the on-chain framework.
Abstract Base
The foundational layer of the Abstract framework, upon which other functionalities and modules are built.
Abstract Modules
Pre-built functionalities that can be installed onto an Abstract Account. They come in three types: App, Adapter, and Standalone.
Abstract Name Service (ANS)
An on-chain store that provides chain-agnostic action execution and dynamic address resolution.
Abstract SDK
A toolbox for developers to create composable smart-contract APIs in the Abstract ecosystem. It provides a set of tools and utilities to facilitate the creation and interaction of smart contracts.
Abstract-Testing
A package that provides testing utilities for CosmWasm contracts, focusing on mocking and querying functionalities.
Abstract.js
A JavaScript library designed to facilitate interactions with the on-chain Abstract framework.
Account Abstraction
A concept where the Abstract Account acts as a layer abstracting the complexities of blockchain interactions, allowing for a more user-friendly experience.
Account Factory
A contract that facilitates the creation and management of Abstract Accounts.
Account Ownership
The concept that defines who has control and access rights over an Abstract Account. This can be a single entity ( Monarchy) or multiple entities (Multisig).
Adapter
A type of Abstract Module that acts as an intermediary, translating and routing messages between Apps and external services or protocols.
API Objects
Rust structs in the Abstract SDK that expose specific smart-contract functionalities. They can be used if a contract implements the required features/api traits.
App
A type of Abstract Module designed to enable specific features or transform Abstract Accounts into standalone products.
Cosmos
A decentralized network of independent, scalable, and interoperable blockchains. The Cosmos ecosystem is built on a set of modular, adaptable, and interchangeable tools, with the Cosmos SDK being its foundational framework. Cosmos aims to create an “Internet of Blockchains” where different blockchains can communicate and transact with each other seamlessly through the Inter-Blockchain Communication (IBC) protocol.
CosmWasm
A smart contract platform built for the Cosmos ecosystem. Within the Abstract framework, CosmWasm serves as the underlying smart contract platform that powers the modular and composable functionalities of Abstract Modules. It allows developers to write secure and interoperable smart contracts in Rust, which can then be integrated into the Abstract ecosystem. By leveraging CosmWasm, Abstract ensures that its modules and applications are both scalable and compatible with the broader Cosmos ecosystem.
CW-Orchestrator
CW-Orchestrator is a scripting tool specifically designed to streamline interactions with, testing and deployment of CosmWasm smart contracts.
IBC-Host
A module that facilitates Inter-Blockchain Communication (IBC) within the Abstract framework, allowing for cross-chain interactions.
Integration Testing
Testing that involves deploying the contract and its dependencies to a mock environment to ensure they work together correctly.
JSON Schema Linking
Linking a module’s JSON schema to the Abstract Version Control to improve user experience for developers using the module.
Manager Contract
A contract within an Abstract Account responsible for managing the account’s modules and permissions.
Migration Update
A process within the Abstract framework that allows for the updating or upgrading of modules without compromising the state or data.
Mock Querier
A tool provided by the abstract-testing package to mock Smart and Raw queries for unit testing.
Module Factory
A contract that allows the installation and management of Abstract Modules via the Account Manager.
Module Installation
The process of adding a module to an Abstract Account, specifying its parameters, and initializing it on a specific network.
Module Uploading
The process of compiling a module as a WASM binary and then uploading it to the desired network(s).
Monarchy
A type of account ownership where a single entity has full control over an account.
Move Update
A process that allows for the migration of an Abstract Account from one blockchain to another within the Cosmos ecosystem.
Multisig
A type of account ownership where multiple entities have control over an account, and a predefined number of them must agree on actions taken.
Namespace
A unique publishing domain for Abstract modules, associated with an Abstract Account. It’s used to uniquely identify and monetize modules.
Proxy Contract
A contract within an Abstract Account that handles interactions with external contracts and services.
Raw Queries
Simple database key-value lookups without the computational aspect of smart queries.
Rust
A systems programming language that focuses on performance, reliability, and productivity. Rust offers memory safety guarantees by using a borrow checker to validate references. It’s known for its “zero-cost abstractions,” meaning developers can write high-level code without sacrificing performance. Rust has gained popularity for blockchain and smart contract development due to its safety features and efficient performance.
Smart Queries
Queries that contain a message in their request and often involve computation on the queried contract.
Version Control
A contract that acts as a registry for all modules and accounts within the Abstract platform.
Contributing to Abstract SDK
Thank you for considering to contribute to the Abstract SDK project! We appreciate your support and welcome contributions to help improve this multi-environment CosmWasm smart-contract scripting library. This document provides guidelines and instructions on how to contribute to the project effectively.
Table of Contents
Getting Started
To get started with contributing to the Abstract SDK project, you should first familiarize yourself with the repository structure and the codebase. Please read the project’s README to understand the purpose, features, and usage of the Abstract SDK library as well as its documentation.
How to Contribute
There are multiple ways to contribute to the Abstract SDK project, including reporting bugs, suggesting enhancements, and submitting code contributions.
Reporting Bugs
If you encounter any bugs or issues while using the Abstract SDK library, please report them by creating a new issue in the issue tracker. When reporting a bug, please provide the following information:
- A clear and descriptive title
- A detailed description of the issue, including steps to reproduce it
- Any relevant logs, error messages, or screenshots
- Information about your environment, such as the OS, software versions, and hardware specifications
Suggesting Enhancements
We welcome suggestions for new features or improvements to the existing functionality of the Abstract SDK library. To suggest an enhancement, create a new issue in the issue tracker with the following information:
- A clear and descriptive title
- A detailed explanation of the proposed enhancement, including its benefits and potential use cases
- If applicable, any examples or mockups of the proposed feature
Code Contributions
To contribute code to the Abstract SDK project, please follow these steps:
- Fork the repository to your own GitHub account.
- Clone your fork to your local machine.
- Create a new branch for your changes using the
git checkout -b feature/your-feature-name
command. - Make your changes and commit them with a clear and concise commit message.
- Push your branch to your fork on GitHub.
- Create a new pull request against the main branch of the Abstract SDK repository.
Pull Requests
When submitting a pull request, please make sure that your code follows the Style Guide and that all tests pass. Please provide a detailed description of your changes, including the motivation for the changes and any potential impact on the project. This will help maintainers review your pull request more effectively.
Style Guide
The Abstract SDK project follows the Rust coding style and conventions. Please ensure that your code adheres to these guidelines to maintain consistency and readability throughout the codebase.
- Use proper indentation (4 spaces) and consistent formatting (
cargo fmt
). - Write descriptive variable and function names.
- Use comments to explain complex or non-obvious code.
- Follow the Rust API Guidelines for API design.
- Add documentation for public functions, types, and modules.
- Write doc tests for public functions and methods.
Community
To join the Abstract SDK community, please join the
Abstract Discord server and
the #Abstract SDK
channel. You can also follow the project on
X and GitHub.