diff --git a/docs/versioned_docs/version-v0.27.1/01-welcome/01-index.md b/docs/versioned_docs/version-v0.27.1/01-welcome/01-index.md new file mode 100644 index 0000000000..23d3ef1924 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/01-welcome/01-index.md @@ -0,0 +1,63 @@ +--- +slug: / +--- + +import ProjectsTable from '@site/src/components/ProjectsTable'; + +# Introduction to Ignite + +[Ignite CLI](https://github.com/ignite/cli) offers everything you need to build, test, and launch your blockchain with a +decentralized worldwide community. Ignite CLI is built on top of [Cosmos SDK](https://docs.cosmos.network), the world’s +most popular blockchain framework. Ignite CLI accelerates chain development by scaffolding everything you need so you +can focus on business logic. + +## What is Ignite CLI? + +Ignite CLI is an easy-to-use CLI tool for creating and maintaining sovereign application-specific blockchains. +Blockchains created with Ignite CLI use Cosmos SDK and Tendermint. Ignite CLI and the Cosmos SDK modules are written in +the Go programming language. The scaffolded blockchain that is created with Ignite CLI includes a command line interface +that lets you manage keys, create validators, and send tokens. + +With just a few commands, you can use Ignite CLI to: + +- Create a modular blockchain written in Go +- Scaffold modules, messages, types with CRUD operations, IBC packets, and more +- Start a blockchain node in development with live reloading +- Connect to other blockchains with a built-in IBC relayer +- Use generated TypeScript/Vuex clients to interact with your blockchain +- Use the Vue.js web app template with a set of components and Vuex modules + +## Install Ignite CLI + +To install the `ignite` binary in `/usr/local/bin` run the following command: + +``` +curl https://get.ignite.com/cli | bash +``` + +## Projects using Tendermint and Cosmos SDK + +Many projects already showcase the Tendermint BFT consensus engine and the Cosmos SDK. Explore +the [Cosmos ecosystem](https://cosmos.network/ecosystem/apps) to discover a wide variety of apps, blockchains, wallets, +and explorers that are built in the Cosmos ecosystem. + +## Projects building with Ignite CLI + + \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.27.1/01-welcome/02-install.md b/docs/versioned_docs/version-v0.27.1/01-welcome/02-install.md new file mode 100644 index 0000000000..87fe81fe61 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/01-welcome/02-install.md @@ -0,0 +1,114 @@ +--- +sidebar_position: 1 +description: Steps to install Ignite CLI on your local computer. +--- + +# Install Ignite CLI + +You can run [Ignite CLI](https://github.com/ignite/cli) in a web-based Gitpod IDE or you can install Ignite CLI on your +local computer. + +## Prerequisites + +Be sure you have met the prerequisites before you install and use Ignite CLI. + +### Operating systems + +Ignite CLI is supported for the following operating systems: + +- GNU/Linux +- macOS +- Windows Subsystem for Linux (WSL) + +### Go + +Ignite CLI is written in the Go programming language. To use Ignite CLI on a local system: + +- Install [Go](https://golang.org/doc/install) (**version 1.19** or higher) +- Ensure the Go environment variables are [set properly](https://golang.org/doc/gopath_code#GOPATH) on your system + +## Verify your Ignite CLI version + +To verify the version of Ignite CLI you have installed, run the following command: + +```bash +ignite version +``` + +## Installing Ignite CLI + +To install the latest version of the `ignite` binary use the following command. + +```bash +curl https://get.ignite.com/cli! | bash +``` + +This command invokes `curl` to download the installation script and pipes the output to `bash` to perform the +installation. The `ignite` binary is installed in `/usr/local/bin`. + +To learn more or customize the installation process, see the [installer docs](https://github.com/ignite/installer) on +GitHub. + +### Write permission + +Ignite CLI installation requires write permission to the `/usr/local/bin/` directory. If the installation fails because +you do not have write permission to `/usr/local/bin/`, run the following command: + +```bash +curl https://get.ignite.com/cli | bash +``` + +Then run this command to move the `ignite` executable to `/usr/local/bin/`: + +```bash +sudo mv ignite /usr/local/bin/ +``` + +On some machines, a permissions error occurs: + +```bash +mv: rename ./ignite to /usr/local/bin/ignite: Permission denied +============ +Error: mv failed +``` + +In this case, use sudo before `curl` and before `bash`: + +```bash +sudo curl https://get.ignite.com/cli | sudo bash +``` + +## Upgrading your Ignite CLI installation + +Before you install a new version of Ignite CLI, remove all existing Ignite CLI installations. + +To remove the current Ignite CLI installation: + +1. On your terminal window, press `Ctrl+C` to stop the chain that you started with `ignite chain serve`. +2. Remove the Ignite CLI binary with `rm $(which ignite)`. + Depending on your user permissions, run the command with or without `sudo`. +3. Repeat this step until all `ignite` installations are removed from your system. + +After all existing Ignite CLI installations are removed, follow the [Installing Ignite CLI](#installing-ignite-cli) +instructions. + +For details on version features and changes, see +the [changelog.md](https://github.com/ignite/cli/blob/main/changelog.md) +in the repo. + +## Build from source + +To experiment with the source code, you can build from source: + +```bash +git clone https://github.com/ignite/cli --depth=1 +cd cli && make install +``` + +## Summary + +- Verify the prerequisites. +- To set up a local development environment, install Ignite CLI locally on your computer. +- Install Ignite CLI by fetching the binary using cURL or by building from source. +- The latest version is installed by default. You can install previous versions of the precompiled `ignite` binary. +- Stop the chain and remove existing versions before installing a new version. diff --git a/docs/versioned_docs/version-v0.27.1/01-welcome/_category_.json b/docs/versioned_docs/version-v0.27.1/01-welcome/_category_.json new file mode 100644 index 0000000000..5437d69109 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/01-welcome/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Welcome", + "link": null +} \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/00-introduction.md b/docs/versioned_docs/version-v0.27.1/02-guide/00-introduction.md new file mode 100644 index 0000000000..1af3f532f5 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/00-introduction.md @@ -0,0 +1,36 @@ +--- +sidebar_position: 0 +title: Introduction +slug: /guide +--- + +# Introduction + +Developer tutorials provide step-by-step instructions to help you build blockchain developer skills. + +By following these developer tutorials you will learn how to: + +* Install Ignite CLI on your local machine +* Create a new blockchain and start a node locally for development +* Make your blockchain say "Hello, World!" + * Scaffold a Cosmos SDK query + * Modify a keeper method to return a static string + * Use the blockchain CLI to make a query +* Write and read blog posts to your chain in the Blog tutorial + * Scaffold a Cosmos SDK message + * Define new types in protocol buffer files + * Write keeper methods to write data to the store + * Read data from the store and return it as a result a query + * Use the blockchain CLI to broadcast transactions +* Build a blockchain for buying and selling names in the Nameservice tutorial + * Scaffold CRUD logic with `map` + * Use other module methods in your custom module + * Send tokens between addresses +* Build a guessing game with rewards + * Use an escrow account to store tokens +* Use the Inter-Blockchain Communication (IBC) protocol + * Scaffold an IBC-enabled module + * Send and receive IBC packets + * Configure and run a built-in IBC relayer +* Build a decentralized order-book token exchange + * Build an advanced IBC-enabled module diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/02-getting-started.md b/docs/versioned_docs/version-v0.27.1/02-guide/02-getting-started.md new file mode 100644 index 0000000000..d1d8c7c161 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/02-getting-started.md @@ -0,0 +1,151 @@ +--- +sidebar_position: 2 +--- + +# Getting started + +In this tutorial, we will be using Ignite CLI to create a new blockchain. Ignite +CLI is a command line interface that allows users to quickly and easily create +blockchain networks. By using Ignite CLI, we can quickly create a new blockchain +without having to manually set up all the necessary components. + +Once we have created our blockchain with Ignite CLI, we will take a look at the +directory structure and files that were created. This will give us an +understanding of how the blockchain is organized and how the different +components of the blockchain interact with each other. + +By the end of this tutorial, you will have a basic understanding of how to use +Ignite CLI to create a new blockchain, and you will have a high-level +understanding of the directory structure and files that make up a blockchain. +This knowledge will be useful as you continue to explore the world of blockchain +development. + +## Creating a new blockchain + +To create a new blockchain project with Ignite, you will need to run the +following command: + +``` +ignite scaffold chain example +``` + +The [`ignite scaffold chain`](../references/cli#ignite-scaffold-chain) command will create a +new blockchain in a new directory `example`. + +The new blockchain is built using the Cosmos SDK framework and imports several +standard modules to provide a range of functionality. These modules include +`staking`, which enables a delegated Proof-of-Stake consensus mechanism, `bank` +for facilitating fungible token transfers between accounts, and `gov` for +on-chain governance. In addition to these modules, the blockchain also imports +other modules from the Cosmos SDK framework. + +The `example` directory contains the generated files and directories that make +up the structure of a Cosmos SDK blockchain. This directory includes files for +the chain's configuration, application logic, and tests, among others. It +provides a starting point for developers to quickly set up a new Cosmos SDK +blockchain and build their desired functionality on top of it. + +By default, Ignite creates a new empty custom module with the same name as the +blockchain being created (in this case, `example`) in the `x/` directory. This +module doesn't have any functionality by itself, but can serve as a starting +point for building out the features of your application. If you don't want to +create this module, you can use the `--no-module` flag to skip it. + +## Directory structure + +In order to understand what the Ignite CLI has generated for your project, you +can inspect the contents of the `example/` directory. + +The `app/` directory contains the files that connect the different parts of the +blockchain together. The most important file in this directory is `app.go`, +which includes the type definition of the blockchain and functions for creating +and initializing it. This file is responsible for wiring together the various +components of the blockchain and defining how they will interact with each +other. + +The `cmd/` directory contains the main package responsible for the command-line +interface (CLI) of the compiled binary. This package defines the commands that +can be run from the CLI and how they should be executed. It is an important part +of the blockchain project as it provides a way for developers and users to +interact with the blockchain and perform various tasks, such as querying the +blockchain state or sending transactions. + +The `docs/` directory is used for storing project documentation. By default, +this directory includes an OpenAPI specification file, which is a +machine-readable format for defining the API of a software project. The OpenAPI +specification can be used to automatically generate human-readable documentation +for the project, as well as provide a way for other tools and services to +interact with the API. The `docs/` directory can be used to store any additional +documentation that is relevant to the project. + +The `proto/` directory contains protocol buffer files, which are used to +describe the data structure of the blockchain. Protocol buffers are a language- +and platform-neutral mechanism for serializing structured data, and are often +used in the development of distributed systems, such as blockchain networks. The +protocol buffer files in the `proto/` directory define the data structures and +messages that are used by the blockchain, and are used to generate code for +various programming languages that can be used to interact with the blockchain. +In the context of the Cosmos SDK, protocol buffer files are used to define the +specific types of data that can be sent and received by the blockchain, as well +as the specific RPC endpoints that can be used to access the blockchain's +functionality. + +The `testutil/` directory contains helper functions that are used for testing. +These functions provide a convenient way to perform common tasks that are needed +when writing tests for the blockchain, such as creating test accounts, +generating transactions, and checking the state of the blockchain. By using the +helper functions in the `testutil/` directory, developers can write tests more +quickly and efficiently, and can ensure that their tests are comprehensive and +effective. + +The `x/` directory contains custom Cosmos SDK modules that have been added to +the blockchain. Standard Cosmos SDK modules are pre-built components that +provide common functionality for Cosmos SDK-based blockchains, such as support +for staking and governance. Custom modules, on the other hand, are modules that +have been developed specifically for the blockchain project and provide +project-specific functionality. + +The `config.yml` file is a configuration file that can be used to customize the +blockchain during development. This file includes settings that control various +aspects of the blockchain, such as the network's ID, account balances, and the +node parameters. + +The `.github` directory contains a GitHub Actions workflow that can be used to +automatically build and release a blockchain binary. GitHub Actions is a tool +that allows developers to automate their software development workflows, +including building, testing, and deploying their projects. The workflow in the +`.github` directory is used to automate the process of building the blockchain +binary and releasing it, which can save time and effort for developers. + +The `readme.md` file is a readme file that provides an overview of the +blockchain project. This file typically includes information such as the +project's name and purpose, as well as instructions on how to build and run the +blockchain. By reading the `readme.md` file, developers and users can quickly +understand the purpose and capabilities of the blockchain project and get +started using it. + +## Starting a blockchain node + +To start a blockchain node in development, you can run the following command: + +``` +ignite chain serve +``` + +The [`ignite chain serve`](../references/cli#ignite-scaffold-chain) command is used to start +a blockchain node in development mode. It first compiles and installs the binary +using the `ignite chain build` command, then initializes the blockchain's data +directory for a single validator using the `ignite chain init` command. After +that, it starts the node locally and enables automatic code reloading so that +changes to the code can be reflected in the running blockchain without having to +restart the node. This allows for faster development and testing of the +blockchain. + +Congratulations! 🥳 You have successfully created a brand-new Cosmos blockchain +using the Ignite CLI. This blockchain uses the delegated proof of stake (DPoS) +consensus algorithm, and comes with a set of standard modules for token +transfers, governance, and inflation. Now that you have a basic understanding of +your Cosmos blockchain, it's time to start building custom functionality. In the +following tutorials, you will learn how to build custom modules and add new +features to your blockchain, allowing you to create a unique and powerful +decentralized application. diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/03-hello/00-express.md b/docs/versioned_docs/version-v0.27.1/02-guide/03-hello/00-express.md new file mode 100644 index 0000000000..89e76bcfb4 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/03-hello/00-express.md @@ -0,0 +1,141 @@ +--- +description: Step-by-step guidance to build your first blockchain and your first Cosmos SDK module. +title: Express tutorial +--- + +# "Hello, World!" in 5 minutes + +In this tutorial, you will create a simple blockchain with a custom query that +responds with `"Hello, %s!"`, where `%s` is a name provided in the query. To do +this, you will use the Ignite CLI to generate most of the code, and then modify +the query to return the desired response. After completing the tutorial, you +will have a better understanding of how to create custom queries in a +blockchain. + +First, create a new `hello` blockchain with Ignite CLI: + +``` +ignite scaffold chain hello +``` + +Let's add a query to the blockchain we just created. + +In the Cosmos SDK, a query is a request for information from the blockchain. +Queries are used to retrieve data from the blockchain, such as the current state +of the ledger or the details of a specific transaction. The Cosmos SDK provides +a number of built-in query methods that can be used to retrieve data from the +blockchain, and developers can also create custom queries to access specific +data or perform complex operations. Queries are processed by the blockchain's +nodes and the results are returned to the querying client. + +## Create a query with Ignite + +To add a query, run the following command inside the `hello` directory: + +``` +ignite scaffold query say-hello name --response name +``` + +The `ignite scaffold query` command is a tool used to quickly create new +queries. When you run this command, it makes changes to your source code to add +the new query and make it available in your API. This command accepts a query +name (`"say-hello"`) and a list of request fields (in our case only `name`). The +optional `--response` flag specifies the return values of the query. + +This command made the following changes to the source code. + +The `proto/hello/hello/query.proto` file was modified to define the request and +response for a query, as well as to add the `SayHello` query in the `Query` +service. + +The `x/hello/client/cli/query_say_hello.go` file was created and added to the +project. This file contains a CLI command `CmdSayHello` that allows users to +submit a "say hello" query to the blockchain. This command allows users to +interact with the blockchain in a more user-friendly way, allowing them to +easily submit queries and receive responses from the blockchain. + +The `x/hello/client/cli/query.go` was modified to add the `CmdSayHello` command +to the CLI of the blockchain. + +The `x/hello/keeper/query_say_hello.go` file was created with a keeper method +called `SayHello`. This method is responsible for handling the "say hello" +query, which can be called by a client using the command-line interface (CLI) or +an API. When the "say hello" query is executed, the `SayHello` method is called +to perform the necessary actions and return a response to the client. The +`SayHello` method may retrieve data from the application's database, process the +data, and return a result to the client in a specific format, such as a string +of text or a data structure. + +To change the source code so that the query returns the `"Hello, %s!"` string, +modify the return statement in `query_say_hello.go` to return +`fmt.Sprintf("hello %s", req.Name)`. + +```go title="x/hello/keeper/query_say_hello.go" +func (k Keeper) SayHello(goCtx context.Context, req *types.QuerySayHelloRequest) (*types.QuerySayHelloResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + ctx := sdk.UnwrapSDKContext(goCtx) + + // TODO: Process the query + _ = ctx + // highlight-next-line + return &types.QuerySayHelloResponse{Name: fmt.Sprintf("Hello, %s!", req.Name)}, nil +} +``` + +The function now returns a `QuerySayHelloResponse` struct with the `Name` field +set to the string `"Hello, %s!"` with `req.Name` as the value for the `%s` +placeholder. It also returns a nil error to indicate success. + +Now that you have added a query your blockchain and modified it return the value +you want, you can start your blockchain with Ignite: + +``` +ignite chain serve +``` + +After starting your blockchain, you can use its command-line interface (CLI) to +interact with it and perform various actions such as querying the blockchain's +state, sending transactions, and more. + +You can use the `hellod` binary to run the `say-hello` query: + +``` +hellod q hello say-hello bob +``` + +Once you run this command, the `hellod` binary will send a `say-hello` query to +your blockchain with the argument `bob`. The blockchain will process the query +and return the result, which will be printed by the `hellod` binary. In this +case, the expected result is a string containing the message `Hello, bob!`. + +``` +name: Hello, bob! +``` + +Congratulations! 🎉 You have successfully created a new Cosmos SDK module called +`hello` with a custom query functionality. This allows users to query the +blockchain and receive a response with a personalized greeting. This tutorial +demonstrated how to use Ignite CLI to create a custom query in a blockchain. + +Ignite is an incredibly convenient tool for developers because it automatically +generates much of the code required for a project. This saves developers time +and effort by reducing the amount of code they need to write manually. With +Ignite, developers can quickly and easily set up the basic structure of their +project, allowing them to focus on the more complex and unique aspects of their +work. + +However, it is also important for developers to understand how the code +generated by Ignite works under the hood. One way to do this is to implement the +same functionality manually, without using Ignite. For example, in this tutorial +Ignite was used to generate query functionality, now could try implementing the +same functionality manually to see how it works and gain a deeper understanding +of the code. + +Implementing the same functionality manually can be time-consuming and +challenging, but it can also be a valuable learning experience. By seeing how +the code works at a low level, developers can gain a better understanding of how +different components of their project fit together and how they can be +customized and optimized. diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/03-hello/01-scaffolding.md b/docs/versioned_docs/version-v0.27.1/02-guide/03-hello/01-scaffolding.md new file mode 100644 index 0000000000..130552636e --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/03-hello/01-scaffolding.md @@ -0,0 +1,236 @@ +--- +title: In-depth tutorial +--- + +# In-depth "Hello, World!" tutorial + +In this tutorial you will implement "Hello, World!" functionality from +scratch. The functionality of the application you will be building will be +identical to what the one you created in the "Express tutorial" section, but +here you will be doing it manually in order to gain a deeper understanding of +the process. + +To begin, let's start with a fresh `hello` blockchain. You can either roll back +the changes you made in the previous section or create a new blockchain using +Ignite. Either way, you will have a blank blockchain that is ready for you to +work with. + +``` +ignite scaffold chain hello +``` + +## `SayHello` RPC + +In Cosmos SDK blockchains, queries are defined as remote procedure calls (RPCs) +in a `Query` service in protocol buffer files. To add a new query, you can add +the following code to the `query.proto` file of your module: + +```protobuf title="proto/hello/hello/query.proto" +service Query { + // highlight-start + rpc SayHello(QuerySayHelloRequest) returns (QuerySayHelloResponse) { + option (google.api.http).get = "/hello/hello/say_hello/{name}"; + } + // highlight-end +} +``` + +The RPC accepts a request argument of type `QuerySayHelloRequest` and returns a +value of type `QuerySayHelloResponse`. To define these types, you can add the +following code to the `query.proto` file: + +```protobuf title="proto/hello/hello/query.proto" +message QuerySayHelloRequest { + string name = 1; +} + +message QuerySayHelloResponse { + string name = 1; +} +``` + +To use the types defined in `query.proto`, you must transpile the protocol +buffer files into Go source code. This can be done by running `ignite chain +serve`, which will build and initialize the blockchain and automatically +generate the Go source code from the protocol buffer files. Alternatively, you +can run `ignite generate proto-go` to only generate the Go source code from the +protocol buffer files, without building and initializing the blockchain. + +## `SayHello` keeper method + +After defining the query, request, and response types in the `query.proto` file, +you will need to implement the logic for the query in your code. This typically +involves writing a function that processes the request and returns the +appropriate response. Create a new file `query_say_hello.go` with the following +contents: + +```go title="x/hello/keeper/query_say_hello.go" +package keeper + +import ( + "context" + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "hello/x/hello/types" +) + +func (k Keeper) SayHello(goCtx context.Context, req *types.QuerySayHelloRequest) (*types.QuerySayHelloResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + ctx := sdk.UnwrapSDKContext(goCtx) + // TODO: Process the query + _ = ctx + return &types.QuerySayHelloResponse{Name: fmt.Sprintf("hello %s", req.Name)}, nil +} +``` + +This code defines a `SayHello` function that accepts a request of type +`QuerySayHelloRequest` and returns a value of type `QuerySayHelloResponse`. The +function first checks if the request is valid, and then processes the query by +returning the response message with the provided name as the value for the `%s` +placeholder. You can add additional logic to the function as needed, such as +retrieving data from the blockchain or performing complex operations, to handle +the query and return the appropriate response. + +## `CmdSayHello` command + +After implementing the query logic, you will need to make the query available to +clients so that they can call it and receive the response. This typically +involves adding the query to the blockchain's application programming interface +(API) and providing a command-line interface (CLI) command that allows users to +easily submit the query and receive the response. + +To provide a CLI command for the query, you can create the `query_say_hello.go` +file and implement a `CmdSayHello` command that calls the `SayHello` function +and prints the response to the console. + +```go title="x/hello/client/cli/query_say_hello.go" +package cli + +import ( + "strconv" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/spf13/cobra" + + "hello/x/hello/types" +) + +var _ = strconv.Itoa(0) + +func CmdSayHello() *cobra.Command { + cmd := &cobra.Command{ + Use: "say-hello [name]", + Short: "Query say-hello", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) (err error) { + reqName := args[0] + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + params := &types.QuerySayHelloRequest{ + Name: reqName, + } + res, err := queryClient.SayHello(cmd.Context(), params) + if err != nil { + return err + } + return clientCtx.PrintProto(res) + }, + } + flags.AddQueryFlagsToCmd(cmd) + return cmd +} +``` + +The code defines a `CmdSayHello` command. The command is defined using the +`cobra` library, which is a popular framework for building command-line +applications in Go. The command accepts a `name` as an argument and uses it to +create a `QuerySayHelloRequest` struct that is passed to the `SayHello` function +from the `types.QueryClient`. The `SayHello` function is used to send the +`say-hello` query to the blockchain, and the response is stored in the `res` +variable. + +The `QuerySayHelloRequest` struct is defined in the `query.proto` file, which is +a Protocol Buffer file that defines the request and response types for the +query. The `QuerySayHelloRequest` struct includes a `Name` field of type +`string`, which is used to provide the name to be included in the response +message. + +After the query has been sent and the response has been received, the code uses +the `clientCtx.PrintProto` function to print the response to the console. The +`clientCtx` variable is obtained using the `client.GetClientQueryContext` +function, which provides access to the client context, including the client's +configuration and connection information. The `PrintProto` function is used to +print the response using the Protocol Buffer format, which allows for efficient +serialization and deserialization of the data. + +The `flags.AddQueryFlagsToCmd` function is used to add query-related flags to +the command. This allows users to specify additional options when calling the +command, such as the node URL and other query parameters. These flags are used +to configure the query and provide the necessary information to the `SayHello` +function, allowing it to connect to the blockchain and send the query. + +To make the `CmdSayHello` command available to users, you will need to add it to +the chain's binary. This is typically done by modifying the +`x/hello/client/cli/query.go` file and adding the +`cmd.AddCommand(CmdSayHello())` statement. This adds the `CmdSayHello` command +to the list of available commands, allowing users to call it from the +command-line interface (CLI). + +```go title="x/hello/client/cli/query.go" +func GetQueryCmd(queryRoute string) *cobra.Command { + cmd := &cobra.Command{ + Use: types.ModuleName, + Short: fmt.Sprintf("Querying commands for the %s module", types.ModuleName), + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + cmd.AddCommand(CmdQueryParams()) + // highlight-next-line + cmd.AddCommand(CmdSayHello()) + return cmd +} +``` + +Once you have provided a CLI command, users will be able to call the `say-hello` +query and receive the appropriate response. + +Save all the changes you made to the source code of your project and run the +following command to start a blockchain node: + +``` +ignite chain serve +``` + +Use the following command to submit the query and receive the response: + +``` +hellod q hello say-hello bob +``` + +This command will send a "say-hello" query to the blockchain with the name "bob" +and print the response of "Hello, bob!" to the console. You can modify the query +and response as needed to suit your specific requirements and provide the +desired functionality. + +Congratulations on completing the "Hello, World!" tutorial! In this tutorial, +you learned how to define a new query in a protocol buffer file, implement the +logic for the query in your code, and make the query available to clients +through the blockchain's API and CLI. By following the steps outlined in the +tutorial, you were able to create a functional query that can be used to +retrieve data from your blockchain or perform other operations as needed. + +Now that you have completed the tutorial, you can continue to build on your +knowledge of the Cosmos SDK and explore the many features and capabilities it +offers. You may want to try implementing more complex queries or experiment with +other features of the SDK to see what you can create. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/03-hello/_category_.json b/docs/versioned_docs/version-v0.27.1/02-guide/03-hello/_category_.json new file mode 100644 index 0000000000..871a74e160 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/03-hello/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Hello, World!", + "link": null +} \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/04-blog/00-express.md b/docs/versioned_docs/version-v0.27.1/02-guide/04-blog/00-express.md new file mode 100644 index 0000000000..e2cb2fc142 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/04-blog/00-express.md @@ -0,0 +1,309 @@ +--- +description: Learn module basics by writing and reading blog posts to your chain. +title: Express tutorial +--- + +# "Build a blog" in 5 minutes + +In this tutorial, we will create a blockchain with a module that allows us to +write and read data from the blockchain. This module will implement the ability +to create and read blog posts, similar to a blogging application. The end user +will be able to submit new blog posts and view a list of existing posts on the +blockchain. This tutorial will guide you through the process of creating and +using this module to interact with the blockchain. + +The goal of this tutorial is to provide step-by-step instructions for creating a +feedback loop that allows you to submit data to the blockchain and read that +data back from the blockchain. By the end of this tutorial, you will have +implemented a complete feedback loop and will be able to use it to interact with +the blockchain. + +First, create a new `blog` blockchain with Ignite CLI: + +``` +ignite scaffold chain blog +``` + +In order to create a blog application that uses a blockchain, we need to define +the requirements for our application. We want the application to store objects +of type `Post` on the blockchain. These objects should have two properties: a +`title` and a `body`. + +In addition to storing posts on the blockchain, we also want to provide users +with the ability to perform CRUD (create, read, update, and delete) operations +on these posts. This will allow users to create new posts, read existing posts, +update the contents of existing posts, and delete posts that are no longer +needed. + +One of the features of the Ignite CLI is the ability to generate code that +implements basic CRUD functionality. This is accomplished through the use of +scaffolding commands, which can be used to quickly generate the necessary code +for creating, reading, updating, and deleting data in your application. + +The Ignite CLI is capable of generating code for data that is stored in +different types of data structures. This includes lists, which are collections +of data indexed by an incrementing integer, maps, which are collections indexed +by a custom key, and singles, which are single instances of data. By using these +different data structures, you can customize your application to fit your +specific needs. For example, if you are building a blog application, you may +want to use a list to store all posts, with each post indexed by an integer. +Alternatively, you could use a map to index each post by its unique title, or a +single to store a single post. The choice of data structure will depend on the +specific requirements of your application. + +In addition to the data structure you choose, the Ignite CLI also requires you +to provide the name of the type of data that it will generate code for, as well +as fields that describe the type of data. For example, if you are creating a +blog application, you may want to create a type called "Post" that has fields +for the "title" and "body" of the post. The Ignite CLI will use this information +to generate the necessary code for creating, reading, updating, and deleting +data of this type in your application. + +Switch to the `blog` directory and run the `ignite scaffold list` command: + +``` +cd blog +ignite scaffold list post title body +``` + +Now that you have used the Ignite CLI to generate code for your application, +let's review what it has created. The Ignite CLI will have generated code for +the data structure and data type that you specified, as well as code for the +basic CRUD operations that are needed to manipulate this data. This code will +provide a solid foundation for your application, and you can customize it +further to fit your specific needs. By reviewing the code generated by the +ignite CLI, you can ensure that it meets your requirements and get a better +understanding of how to build your application using this tool. + +The Ignite CLI has generated several files and modifications in the +`proto/blog/blog` directory. These include: + +* `post.proto`: This is a protocol buffer file that defines the `Post` type, + with fields for the `title`, `body`, `id`, and `creator`. +* `tx.proto`: This file has been modified to include three RPCs (remote + procedure calls): `CreatePost`, `UpdatePost`, and `DeletePost`. Each of these + RPCs corresponds to a Cosmos SDK message that can be used to perform the + corresponding CRUD operation on a post. +* `query.proto`: This file has been modified to include two queries: `Post` and + `PostAll`. The `Post` query can be used to retrieve a single post by its ID, + while the `PostAll` query can be used to retrieve a paginated list of posts. +* `genesis.proto`: This file has been modified to include posts in the genesis + state of the module, which defines the initial state of the blockchain when it + is first started. + +The Ignite CLI has also generated several new files in the `x/blog/keeper` +directory that implement the CRUD-specific logic for your application. These +include: + +* `msg_server_post.go`: This file implements keeper methods for the + `CreatePost`, `UpdatePost`, and `DeletePost` messages. These methods are + called when a corresponding message is processed by the module, and they + handle the specific logic for each of the CRUD operations. +* `query_post.go`: This file implements the `Post` and `PostAll` queries, which + are used to retrieve individual posts by ID or a paginated list of posts, + respectively. +* `post.go`: This file implements the underlying functions that the keeper + methods depend on. These functions include appending (adding) posts to the + store, getting individual posts, getting the post count, and other operations + that are needed to manage the posts in the application. + +Overall, these files provide the necessary implementation for the CRUD +functionality of your blog application. They handle the specific logic for each +of the CRUD operations, as well as the underlying functions that these +operations depend on. + +Files were created and modified in the `x/blog/types` directory. + +* `messages_post.go`: This new file contains Cosmos SDK message constructors and + associated methods such as `Route()`, `Type()`, `GetSigners()`, + `GetSignBytes()`, and `ValidateBasic()`. +* `keys.go`: This file was modified to include key prefixes for storing blog + posts. By using key prefixes, we can ensure that the data for our blog posts + is kept separate from other types of data in the database, and that it can be + easily accessed when needed. +* `genesis.go`: This file was modified to define the initial (genesis) state of + the blog module, as well as the `Validate()` function for validating this + initial state. This is an important step in setting up our blockchain, as it + defines the initial data and ensures that it is valid according to the rules + of our application. +* `codec.go`: This file was modified to register our message types with the + encoder, allowing them to be properly serialized and deserialized when + transmitted over the network. + +Additionally, `*.pb.go` files were generated from `*.proto` files, and they +contain type definitions for messages, RPCs, and queries used by our +application. These files are automatically generated from the `*.proto` files +using the Protocol Buffers (protobuf) tool, which allows us to define the +structure of our data in a language-agnostic way. + +The Ignite CLI has added functionality to the `x/blog/client/cli` directory by +creating and modifying several files. +* `tx_post.go`: This file was created to implement CLI commands for broadcasting + transactions containing messages for the blog module. These commands allow + users to easily send messages to the blockchain using the Ignite CLI. +* `query_post.go`: This file was created to implement CLI commands for querying + the blog module. These commands allow users to retrieve information from the + blockchain, such as a list of blog posts. +* `tx.go`: This file was modified to add the CLI commands for broadcasting + transactions to the chain's binary. +* `query.go`: This file was also modified to add the CLI commands for querying + the chain to the chain's binary. + +As you can see, the `ignite scaffold list` command has generated and modified a +number of source code files. These files define the types of messages, logic +that gets executed when a message is processed, and the wiring that connects +everything together. This includes the logic for creating, updating, and +deleting blog posts, as well as the queries needed to retrieve this information. + +To see the generated code in action, we will need to start the blockchain. We +can do this by using the `ignite chain serve` command, which will build, +initialize, and start the blockchain for us: + +``` +ignite chain serve +``` + +Once the blockchain is running, we can use the binary to interact with it and +see how the code handles creating, updating, and deleting blog posts. We can +also see how it processes and responds to queries. This will give us a better +understanding of how our application works and allow us to test its +functionality. + +While `ignite chain serve` is running in one terminal window, open another +terminal and use the chain's binary to create a new blog post on the blockchain: + +``` +blogd tx blog create-post 'Hello, World!' 'This is a blog post' --from alice +``` + +When using the `--from` flag to specify the account that will be used to sign a +transaction, it's important to ensure that the specified account is available +for use. In a development environment, you can see a list of available accounts +in the output of the `ignite chain serve` command, or in the `config.yml` file. + +It's also worth noting that the `--from` flag is required when broadcasting +transactions. This flag specifies the account that will be used to sign the +transaction, which is a crucial step in the transaction process. Without a valid +signature, the transaction will not be accepted by the blockchain. Therefore, +it's important to ensure that the account specified with the `--from` flag is +available. + +After the transaction has been broadcasted successfully, you can query the +blockchain for the list of blog posts. To do this, you can use the `blogd q blog +list-post` command, which will return a paginated list of all the blog posts +that have been added to the blockchain. + +``` +blogd q blog list-post + +Post: +- body: This is a blog post + creator: cosmos1xz770h6g55rrj8vc9ll9krv6mr964tzhqmsu2v + id: "0" + title: Hello, World! +pagination: + next_key: null + total: "0" +``` + +By querying the blockchain, you can verify that your transaction was processed +successfully and that the blog post has been added to the chain. Additionally, +you can use other query commands to retrieve information about other data on the +blockchain, such as accounts, balances, and governance proposals. + +Let's modify the blog post that we just created by changing the `body` content. +To do this, we can use the `blogd tx blog update-post` command, which allows us +to update an existing blog post on the blockchain. When running this command, we +will need to specify the ID of the blog post that we want to modify, as well as +the new body content that we want to use. After running this command, the +transaction will be broadcasted to the blockchain and the blog post will be +updated with the new body content. + +``` +blogd tx blog update-post 0 'Hello, World!' 'This is a blog post from Alice' --from alice +``` + +Now that we have updated the blog post with new content, let's query the +blockchain again to see the changes. To do this, we can use the `blogd q blog +list-post` command, which will return a list of all the blog posts on the +blockchain. By running this command again, we can see the updated blog post in +the list, and we can verify that the changes we made have been successfully +applied to the blockchain. + + +``` +blogd q blog list-post + +Post: +- body: This is a blog post from Alice + creator: cosmos1xz770h6g55rrj8vc9ll9krv6mr964tzhqmsu2v + id: "0" + title: Hello, World! +pagination: + next_key: null + total: "0" +``` + +Let's try to delete one of the blog posts using Bob's account. However, since +the blog post was created using Alice's account, we can expect the blockchain to +check whether the user is authorized to delete the post. In this case, since Bob +is not the author of the post, his transaction should be rejected by the +blockchain. + +To delete a blog post, we can use the `blogd tx blog delete-post` command, which +allows us to delete an existing blog post on the blockchain. When running this +command, we will need to specify the ID of the blog post that we want to delete, +as well as the account that we want to use for signing the transaction. In this +case, we will use Bob's account to sign the transaction. + +After running this command, the transaction will be broadcasted to the +blockchain. However, since Bob is not the author of the post, the blockchain +should reject his transaction and the blog post will not be deleted. This is an +example of how the blockchain can enforce rules and permissions, and it shows +that only authorized users are able to make changes to the blockchain. + +``` +blogd tx blog delete-post 0 --from bob + +raw_log: 'failed to execute message; message index: 0: incorrect owner: unauthorized' +``` + +Now, let's try to delete the blog post again, but this time using Alice's +account. Since Alice is the author of the blog post, she should be authorized to +delete it. + +``` +blogd tx blog delete-post 0 --from alice +``` + +To check whether the blog post has been successfully deleted by Alice, we can +query the blockchain for a list of posts again. + +``` +blogd q blog list-post + +Post: [] +pagination: + next_key: null + total: "0" +``` + +Congratulations on successfully completing the tutorial on building a blog with +Ignite CLI! By following the instructions, you have learned how to create a new +blockchain, generate code for a "post" type with CRUD functionality, start a +local blockchain, and test out the functionality of your blog. + +Now that you have a working example of a simple application, you can experiment +with the code generated by Ignite and see how changes affect the behavior of the +application. This is a valuable skill to have, as it will allow you to customize +your application to fit your specific needs and improve the functionality of +your application. You can try making changes to the data structure or data type, +or add additional fields or functionality to the code. + +In the following tutorials, we will take a closer look at the code that Ignite +generates in order to better understand how to build blockchains. By writing +some of the code ourselves, we can gain a deeper understanding of how Ignite +works and how it can be used to create applications on a blockchain. This will +help us learn more about the capabilities of Ignite CLI and how it can be used +to build robust and powerful applications. Keep an eye out for these tutorials +and get ready to dive deeper into the world of blockchains with Ignite! \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/04-blog/01-intro.md b/docs/versioned_docs/version-v0.27.1/02-guide/04-blog/01-intro.md new file mode 100644 index 0000000000..c3d1807902 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/04-blog/01-intro.md @@ -0,0 +1,17 @@ +--- +title: In-depth tutorial +--- + +# In-depth blog tutorial + +In this tutorial, you will learn how to create a blog application as a Cosmos +SDK blockchain using the Ignite CLI by building it from scratch. This means that +you will be responsible for setting up the necessary types, messages, and +queries and writing the logic to create, read, update, and delete blog posts on +the blockchain. + +The functionality of the application you will be building will be identical to +what is generated by the Ignite CLI command `ignite scaffold list post title +body`, but you will be doing it manually in order to gain a deeper understanding +of the process. Through this tutorial, you will learn how to build a blog +application on a Cosmos SDK blockchain using the Ignite CLI in a hands-on way. diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/04-blog/02-scaffolding.md b/docs/versioned_docs/version-v0.27.1/02-guide/04-blog/02-scaffolding.md new file mode 100644 index 0000000000..a5f07f02bb --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/04-blog/02-scaffolding.md @@ -0,0 +1,124 @@ +# Creating the structure + +Create a new blockchain with the following command: + +``` +ignite scaffold chain blog +``` + +This will create a new directory called `blog/` containing the necessary files +and directories for your [blockchain +application](https://docs.cosmos.network/main/basics/app-anatomy). Next, +navigate to the newly created directory by running: + +``` +cd blog +``` + +Since your app will be storing and operating with blog posts, you will need to +create a `Post` type to represent these posts. You can do this using the +following Ignite CLI command: + +``` +ignite scaffold type post title body creator id:uint +``` + +This will create a `Post` type with four fields: `title`, `body`, `creator`, all +of type `string`, and `id` of type `uint`. + +It is a good practice to commit your changes to a version control system like +Git after using Ignite's code scaffolding commands. This will allow you to +differentiate between changes made automatically by Ignite and changes made +manually by developers, and also allow you to roll back changes if necessary. +You can commit your changes to Git with the following commands: + +``` +git add . +git commit -am "ignite scaffold type post title body" +``` + +### Creating messages + +Next, you will be implementing CRUD (create, read, update, and delete) +operations for your blog posts. Since create, update, and delete operations +change the state of the application, they are considered write operations. In +Cosmos SDK blockchains, state is changed by broadcasting +[transactions](https://docs.cosmos.network/main/basics/tx-lifecycle) that +contain messages that trigger state transitions. To create the logic for +broadcasting and handling transactions with a "create post" message, you can use +the following Ignite CLI command: + +``` +ignite scaffold message create-post title body --response id:uint +``` + +This will create a "create post" message with two fields: `title` and `body`, +both of which are of type `string`. Posts will be stored in the key-value store +in a list-like data structure, where they are indexed by an incrementing integer +ID. When a new post is created, it will be assigned an ID integer. The +`--response` flag is used to return `id` of type `uint` as a response to the +"create post" message. + +To update a specific blog post in your application, you will need to create a +message called "update post" that accepts three arguments: `title`, `body`, and +`id`. The `id` argument of type `uint` is necessary to specify which blog post +you want to update. You can create this message using the Ignite CLI command: + +``` +ignite scaffold message update-post title body id:uint +``` + +To delete a specific blog post in your application, you will need to create a +message called "delete post" that accepts only the `id` of the post to be +deleted. You can create this message using the Ignite CLI command: + +``` +ignite scaffold message delete-post id:uint +``` + +### Creating queries + +[Queries](https://docs.cosmos.network/main/basics/query-lifecycle) allow users +to retrieve information from the blockchain state. In your application, you will +have two queries: "show post" and "list post". The "show post" query will allow +users to retrieve a specific post by its ID, while the "list post" query will +return a paginated list of all stored posts. + +To create the "show post" query, you can use the following Ignite CLI command: + +``` +ignite scaffold query show-post id:uint --response post:Post +``` + +This query will accept `id` of type `uint` as an argument, and will return a +`post` of type `Post` as a response. + +To create the "list post" query, you can use the following Ignite CLI command: + +``` +ignite scaffold query list-post --response post:Post --paginated +``` + +This query will return a post of type Post in a paginated output. The +`--paginated` flag indicates that the query should return its results in a +paginated format, allowing users to retrieve a specific page of results at a +time. + +## Summary + +Congratulations on completing the initial setup of your blockchain application! +You have successfully created a "post" data type and generated the necessary +code for handling three types of messages (create, update, and delete) and two +types of queries (list and show posts). + +However, at this point, the messages you have created will not trigger any state +transitions, and the queries you have created will not return any results. This +is because Ignite only generates the boilerplate code for these features, and it +is up to you to implement the necessary logic to make them functional. + +In the next chapters of the tutorial, you will learn how to implement the +message handling and query logic to complete your blockchain application. This +will involve writing code to process the messages and queries you have created +and use them to modify or retrieve data from the blockchain's state. By the end +of this process, you will have a fully functional blog application on a Cosmos +SDK blockchain. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/04-blog/03-create.md b/docs/versioned_docs/version-v0.27.1/02-guide/04-blog/03-create.md new file mode 100644 index 0000000000..5e0ea9209e --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/04-blog/03-create.md @@ -0,0 +1,319 @@ +# Creating posts + +In this chapter, we will be focusing on the process of handling a "create post" +message. This involves the use of a special type of function known as a keeper +method. [Keeper](https://docs.cosmos.network/main/building-modules/keeper) +methods are responsible for interacting with the blockchain and modifying its +state based on the instructions provided in a message. + +When a "create post" message is received, the corresponding keeper method will +be called and passed the message as an argument. The keeper method can then use +the various getter and setter functions provided by the store object to retrieve +and modify the current state of the blockchain. This allows the keeper method to +effectively process the "create post" message and make the necessary updates to +the blockchain. + +In order to keep the code for accessing and modifying the store object clean and +separate from the logic implemented in the keeper methods, we will create a new +file called `post.go`. This file will contain functions that are specifically +designed to handle operations related to creating and managing posts within the +blockchain. + +## Appending posts to the store + +```go title="x/blog/keeper/post.go" +package keeper + +import ( + "encoding/binary" + + "blog/x/blog/types" + + "github.com/cosmos/cosmos-sdk/store/prefix" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func (k Keeper) AppendPost(ctx sdk.Context, post types.Post) uint64 { + count := k.GetPostCount(ctx) + post.Id = count + store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.PostKey)) + appendedValue := k.cdc.MustMarshal(&post) + store.Set(GetPostIDBytes(post.Id), appendedValue) + k.SetPostCount(ctx, count+1) + return count +} +``` + +This code defines a function called `AppendPost` which belongs to a `Keeper` +type. The `Keeper` type is responsible for interacting with the blockchain and +modifying its state in response to various messages. + +The `AppendPost` function takes in two arguments: a `Context` object and a +`Post` object. The [`Context`](https://docs.cosmos.network/main/core/context) +object is a standard parameter in many functions in the Cosmos SDK and is used +to provide contextual information about the current state of the blockchain, +such as the current block height. The `Post` object represents a post that will +be added to the blockchain. + +The function begins by retrieving the current post count using the +`GetPostCount` method. You will implement this method in the next step as it has +not been implemented yet. This method is called on the `Keeper` object and takes +in a `Context` object as an argument. It returns the current number of posts +that have been added to the blockchain. + +Next, the function sets the ID of the new post to be the current post count, so +that each post has a unique identifier. It does this by assigning the value of +count to the `Id` field of the `Post` object. + +The function then creates a new +[store](https://docs.cosmos.network/main/core/store) object using the +`prefix.NewStore` function. The `prefix.NewStore` function takes in two +arguments: the `KVStore` associated with the provided context and a key prefix +for the `Post` objects. The `KVStore` is a key-value store that is used to +persist data on the blockchain, and the key prefix is used to differentiate the +`Post` objects from other types of objects that may be stored in the same +`KVStore`. + +The function then serializes the `Post` object using the `cdc.MustMarshal` +function and stores it in the blockchain using the `Set` method of the store +object. The `cdc.MustMarshal` function is part of the Cosmos SDK's +[encoding/decoding](https://docs.cosmos.network/main/core/encoding) library and +is used to convert the `Post` object into a byte slice that can be stored in the +`KVStore`. The `Set` method is called on the store object and takes in two +arguments: a key and a value. In this case, the key is a byte slice generated by +the `GetPostIDBytes` function and the value is the serialized `Post` object. You +will implement this method in the next step as it has not been implemented yet. + +Finally, the function increments the post count by one and updates the +blockchain state using the `SetPostCount` method. You will implement this method +in the next step as it has not been implemented yet. This method is called on +the Keeper object and takes in a `Context` object and a new post count as +arguments. It updates the current post count in the blockchain to be the new +post count provided. + +The function then returns the ID of the newly created post, which is the current +post count before it was incremented. This allows the caller of the function to +know the ID of the post that was just added to the blockchain. + +To complete the implementation of `AppendPost`, the following tasks need to be +performed: + +* Define `PostKey`, which will be used to store and retrieve posts from the + database. +* Implement `GetPostCount`, which will retrieve the current number of posts + stored in the database. +* Implement `GetPostIDBytes`, which will convert a post ID to a byte array. +* Implement `SetPostCount`, which will update the post count stored in the + database. + +### Post key prefix + +In the file `keys.go`, let's define the `PostKey` prefix as follows: + +```go title="x/blog/types/keys.go" +const ( + PostKey = "Post/value/" +) +``` + +This prefix will be used to uniquely identify a post within the system. It will +be used as the beginning of the key for each post, followed by the ID of the +post to create a unique key for each post. + +### Getting the post count + +In the file `post.go`, let's define the `GetPostCount` function as follows: + +```go title="x/blog/keeper/post.go" +func (k Keeper) GetPostCount(ctx sdk.Context) uint64 { + store := prefix.NewStore(ctx.KVStore(k.storeKey), []byte{}) + byteKey := types.KeyPrefix(types.PostCountKey) + bz := store.Get(byteKey) + if bz == nil { + return 0 + } + return binary.BigEndian.Uint64(bz) +} +``` + +This code defines a function named `GetPostCount` that belongs to the `Keeper` +struct. The function takes in a single argument, a context object `ctx` of type +`sdk.Context`, and returns a value of type `uint64`. + +The function begins by creating a new store using the key-value store in the +context and an empty byte slice as the prefix. It then defines a byte slice +`byteKey` using the `KeyPrefix` function from the `types` package, which takes +in the `PostCountKey`. You will define `PostCountKey` in the next step. + +The function then retrieves the value at the key `byteKey` in the store using +the `Get` method and stores it in a variable `bz`. + +Next, the function checks if the value at `byteKey` is `nil` using an if +statement. If it is `nil`, meaning that the key does not exist in the store, the +function returns 0. This indicates that there are no elements or posts +associated with the key. + +If the value at `byteKey` is not nil, the function uses the `binary` package's +`BigEndian` type to parse the bytes in `bz` and returns the resulting `uint64` +value. The `BigEndian` type is used to interpret the bytes in `bz` as a +big-endian encoded unsigned 64-bit integer. The `Uint64` method converts the +bytes to a `uint64` value and returns it. + +`GetPostCount` function is used to retrieve the total number of posts stored in +the key-value store, represented as a `uint64` value. + +In the file `keys.go`, let's define the `PostCountKey` as follows: + +```go title="x/blog/types/keys.go" +const ( + PostCountKey = "Post/count/" +) +``` + +This key will be used to keep track of the ID of the latest post added to the +store. + +### Converting post ID to bytes + +Now, let's implement `GetPostIDBytes`, which will convert a post ID to a byte +array. + +```go title="x/blog/keeper/post.go" +func GetPostIDBytes(id uint64) []byte { + bz := make([]byte, 8) + binary.BigEndian.PutUint64(bz, id) + return bz +} +``` + +`GetPostIDBytes` takes in a value `id` of type `uint64` and returns a value of +type `[]byte`. + +The function starts by creating a new byte slice `bz` with a length of 8 using +the `make` built-in function. It then uses the `binary` package's `BigEndian` +type to encode the value of `id` as a big-endian encoded unsigned integer and +store the result in `bz` using the `PutUint64` method. Finally, the function +returns the resulting byte slice `bz`. + +This function can be used to convert a post ID, represented as a `uint64`, to a +byte slice that can be used as a key in a key-value store. The +`binary.BigEndian.PutUint64` function encodes the `uint64` value of `id` as a +big-endian encoded unsigned integer and stores the resulting bytes in the +`[]byte` slice `bz`. The resulting byte slice can then be used as a key in the +store. + +### Updating the post count + +Implement `SetPostCount` in `post.go`, which will update the post count stored +in the database. + +```go title="x/blog/keeper/post.go" +func (k Keeper) SetPostCount(ctx sdk.Context, count uint64) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), []byte{}) + byteKey := types.KeyPrefix(types.PostCountKey) + bz := make([]byte, 8) + binary.BigEndian.PutUint64(bz, count) + store.Set(byteKey, bz) +} +``` + +This code defines a function `SetPostCount` in the `Keeper` struct. The function +takes in a context `ctx` of type `sdk.Context` and a value `count` of type +`uint64`, and does not return a value. + +The function first creates a new store by calling the `NewStore` function from +the prefix package and passing in the key-value store from the context and an +empty byte slice as the prefix. It stores the resulting store in a variable +named `store`. + +Next, the function defines a byte slice `byteKey` using the `KeyPrefix` function +from the `types` package and passing in the `PostCountKey`. The `KeyPrefix` +function returns a byte slice with the given key as a prefix. + +The function then creates a new byte slice `bz` with a length of 8 using the +`make` built-in function. It then uses the `binary` package's `BigEndian` type +to encode the value of count as a big-endian encoded unsigned integer and store +the result in `bz` using the `PutUint64` method. + +Finally, the function calls the `Set` method on the `store` variable, passing in +`byteKey` and `bz` as arguments. This sets the value at the key `byteKey` in the +store to the value `bz`. + +This function can be used to update the count of posts stored in the database. +It does this by converting the `uint64` value of count to a byte slice using the +`binary.BigEndian.PutUint64` function, and then storing the resulting byte slice +at the key `byteKey` in the store using the `Set` method. + +Now that you have implemented the code for creating blog posts, you can proceed +to implement the keeper method that is invoked when the "create post" message is +processed. + +## Handling the "create post" message + +```go title="x/blog/keeper/msg_server_create_post.go" +package keeper + +import ( + "context" + + "blog/x/blog/types" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func (k msgServer) CreatePost(goCtx context.Context, msg *types.MsgCreatePost) (*types.MsgCreatePostResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + var post = types.Post{ + Creator: msg.Creator, + Title: msg.Title, + Body: msg.Body, + } + id := k.AppendPost( + ctx, + post, + ) + return &types.MsgCreatePostResponse{ + Id: id, + }, nil +} +``` + +The `CreatePost` function is a message handler for the `MsgCreatePost` message +type. It is responsible for creating a new post on the blockchain based on the +information provided in the `MsgCreatePost` message. + +The function first retrieves the Cosmos SDK context from the Go context using +the `sdk.UnwrapSDKContext` function. It then creates a new `Post` object using +the `Creator`, `Title`, and `Body` fields from the MsgCreatePost message. + +Next, the function calls the `AppendPost` method on the `msgServer` object +(which is of the Keeper type) and passes in the Cosmos SDK context and the new +`Post` object as arguments. The `AppendPost` method is responsible for adding +the new post to the blockchain and returning the ID of the new post. + +Finally, the function returns a `MsgCreatePostResponse` object that contains the +ID of the new post. It also returns a nil error, indicating that the operation +was successful. + +## Summary + +Great job! You have successfully implemented the logic for writing blog posts to +the blockchain store and the keeper method that will be called when a "create +post" message is processed. + +The `AppendPost` keeper method retrieves the current post count, sets the ID of +the new post to be the current post count, serializes the post object, and +stores it in the blockchain using the `Set` method of the `store` object. The +key for the post in the store is a byte slice generated by the `GetPostIDBytes` +function and the value is the serialized post object. The function then +increments the post count by one and updates the blockchain state using the +`SetPostCount` method. + +The `CreatePost` handler method receives a `MsgCreatePost` message containing +the data for the new post, creates a new `Post` object using this data, and +passes it to the `AppendPost` keeper method to be added to the blockchain. It +then returns a `MsgCreatePostResponse` object containing the ID of the newly +created post. + +By implementing these methods, you have successfully implemented the necessary +logic for handling "create post" messages and adding posts to the blockchain. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/04-blog/04-update.md b/docs/versioned_docs/version-v0.27.1/02-guide/04-blog/04-update.md new file mode 100644 index 0000000000..7be1ca80bc --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/04-blog/04-update.md @@ -0,0 +1,127 @@ +# Updating posts + +In this chapter, we will be focusing on the process of handling an "update post" +message. + +To update a post, you need to retrieve the specific post from the store using +the "Get" operation, modify the values, and then write the updated post back to +the store using the "Set" operation. + +Let's first implement a getter and a setter logic. + +## Getting posts + +Implement the `GetPost` keeper method in `post.go`: + +```go title="x/blog/keeper/post.go" +func (k Keeper) GetPost(ctx sdk.Context, id uint64) (val types.Post, found bool) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.PostKey)) + b := store.Get(GetPostIDBytes(id)) + if b == nil { + return val, false + } + k.cdc.MustUnmarshal(b, &val) + return val, true +} +``` + +`GetPost` takes in two arguments: a context `ctx` and an `id` of type `uint64` +representing the ID of the post to be retrieved. It returns a `types.Post` +struct containing the values of the post, and a boolean value indicating whether +the post was found in the database. + +The function first creates a `store` using the `prefix.NewStore` method, passing +in the key-value store from the context and the `types.KeyPrefix` function +applied to the `types.PostKey` constant as arguments. It then attempts to +retrieve the post from the store using the `store.Get` method, passing in the ID +of the post as a byte slice. If the post is not found in the store, it returns +an empty `types.Post` struct and a boolean value of false. + +If the post is found in the store, the function unmarshals the retrieved byte +slice into a `types.Post` struct using the `cdc.MustUnmarshal` method, passing +in a pointer to the val variable as an argument. It then returns the val struct +and a boolean value of true to indicate that the post was found in the database. + +## Setting posts + +Implement the `SetPost` keeper method in `post.go`: + +```go title="x/blog/keeper/post.go" +func (k Keeper) SetPost(ctx sdk.Context, post types.Post) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.PostKey)) + b := k.cdc.MustMarshal(&post) + store.Set(GetPostIDBytes(post.Id), b) +} +``` + +`SetPost` takes in two arguments: a context `ctx` and a `types.Post` struct +containing the updated values for the post. The function does not return +anything. + +The function first creates a store using the `prefix.NewStore` method, passing +in the key-value store from the context and the `types.KeyPrefix` function +applied to the `types.PostKey` constant as arguments. It then marshals the +updated post struct into a byte slice using the `cdc.MustMarshal` method, +passing in a pointer to the post struct as an argument. Finally, it updates the +post in the store using the `store.Set` method, passing in the ID of the post as +a byte slice and the marshaled post struct as arguments. + + +## Update posts + +```go title="x/blog/keeper/msg_server_update_post.go" +package keeper + +import ( + "context" + "fmt" + + "blog/x/blog/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +func (k msgServer) UpdatePost(goCtx context.Context, msg *types.MsgUpdatePost) (*types.MsgUpdatePostResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + var post = types.Post{ + Creator: msg.Creator, + Id: msg.Id, + Title: msg.Title, + Body: msg.Body, + } + val, found := k.GetPost(ctx, msg.Id) + if !found { + return nil, sdkerrors.Wrap(sdkerrors.ErrKeyNotFound, fmt.Sprintf("key %d doesn't exist", msg.Id)) + } + if msg.Creator != val.Creator { + return nil, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "incorrect owner") + } + k.SetPost(ctx, post) + return &types.MsgUpdatePostResponse{}, nil +} +``` + +`UpdatePost` takes in a context and a message `MsgUpdatePost` as input, and +returns a response `MsgUpdatePostResponse` and an `error`. The function first +retrieves the current values of the post from the database using the provided +`msg.Id`, and checks if the post exists and if the `msg.Creator` is the same as +the current owner of the post. If either of these checks fail, it returns an +error. If both checks pass, it updates the post in the database with the new +values provided in `msg`, and returns a response without an error. + +## Summary + +Well done! You have successfully implemented a number of important methods for +managing posts within a store. + +The `GetPost` method allows you to retrieve a specific post from the store based +on its unique identification number, or post ID. This can be useful for +displaying a specific post to a user, or for updating it. + +The `SetPost` method enables you to update an existing post in the store. This +can be useful for correcting mistakes or updating the content of a post as new +information becomes available. + +Finally, you implemented the `UpdatePost` method, which is called whenever the +blockchain processes a message requesting an update to a post. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/04-blog/05-delete.md b/docs/versioned_docs/version-v0.27.1/02-guide/04-blog/05-delete.md new file mode 100644 index 0000000000..4bef35b8de --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/04-blog/05-delete.md @@ -0,0 +1,74 @@ +# Deleting posts + +In this chapter, we will be focusing on the process of handling a "delete post" +message. + +## Removing posts + +```go title="x/blog/keeper/post.go" +func (k Keeper) RemovePost(ctx sdk.Context, id uint64) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.PostKey)) + store.Delete(GetPostIDBytes(id)) +} +``` + +`RemovePost` function takes in two arguments: a context object `ctx` and an +unsigned integer `id`. The function removes a post from a key-value store by +deleting the key-value pair associated with the given `id`. The key-value store +is accessed using the `store` variable, which is created by using the `prefix` +package to create a new store using the context's key-value store and a prefix +based on the `PostKey` constant. The `Delete` method is then called on the +`store` object, using the `GetPostIDBytes` function to convert the `id` to a +byte slice as the key to delete. + +## Deleting posts + +```go title="x/blog/keeper/msg_server_delete_post.go" +package keeper + +import ( + "context" + "fmt" + + "blog/x/blog/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +func (k msgServer) DeletePost(goCtx context.Context, msg *types.MsgDeletePost) (*types.MsgDeletePostResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + val, found := k.GetPost(ctx, msg.Id) + if !found { + return nil, sdkerrors.Wrap(sdkerrors.ErrKeyNotFound, fmt.Sprintf("key %d doesn't exist", msg.Id)) + } + if msg.Creator != val.Creator { + return nil, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "incorrect owner") + } + k.RemovePost(ctx, msg.Id) + return &types.MsgDeletePostResponse{}, nil +} +``` + +`DeletePost` takes in two arguments: a context `goCtx` of type `context.Context` +and a pointer to a message of type `*types.MsgDeletePost`. The function returns +a pointer to a message of type `*types.MsgDeletePostResponse` and an `error`. + +Inside the function, the context is unwrapped using the `sdk.UnwrapSDKContext` +function and the value of the post with the ID specified in the message is +retrieved using the `GetPost` function. If the post is not found, an error is +returned using the `sdkerrors.Wrap` function. If the creator of the message does +not match the creator of the post, another error is returned. If both of these +checks pass, the `RemovePost` function is called with the context and the ID of +the post to delete the post. Finally, the function returns a response message +with no data and a `nil` error. + +In short, `DeletePost` handles a request to delete a post, ensuring that the +requester is the creator of the post before deleting it. + +## Summary + +Congratulations on completing the implementation of the `RemovePost` and +`DeletePost` methods in the keeper package! These methods provide functionality +for removing a post from a store and handling a request to delete a post, +respectively. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/04-blog/06-show.md b/docs/versioned_docs/version-v0.27.1/02-guide/04-blog/06-show.md new file mode 100644 index 0000000000..b7e60f3813 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/04-blog/06-show.md @@ -0,0 +1,81 @@ +# Show a post + +In this chapter, you will implement a feature in your blogging application that +enables users to retrieve individual blog posts by their unique ID. This ID is +assigned to each blog post when it is created and stored on the blockchain. By +adding this querying functionality, users will be able to easily retrieve +specific blog posts by specifying their ID. + +## Show post + +Let's implement the `ShowPost` keeper method that will be called when a user +makes a query to the blockchain application, specifying the ID of the desired +post. + +```go title="x/blog/keeper/query_show_post.go" +package keeper + +import ( + "context" + + "blog/x/blog/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +func (k Keeper) ShowPost(goCtx context.Context, req *types.QueryShowPostRequest) (*types.QueryShowPostResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + ctx := sdk.UnwrapSDKContext(goCtx) + post, found := k.GetPost(ctx, req.Id) + if !found { + return nil, sdkerrors.ErrKeyNotFound + } + + return &types.QueryShowPostResponse{Post: post}, nil +} +``` + +`ShowPost` is a function for retrieving a single post object from the +blockchain's state. It takes in two arguments: a `context.Context` object called +`goCtx` and a pointer to a `types.QueryShowPostRequest` object called `req`. It +returns a pointer to a `types.QueryShowPostResponse` object and an `error`. + +The function first checks if the `req` argument is `nil`. If it is, it returns +an `error` with the code `InvalidArgument` and the message "invalid request" +using the `status.Error` function from the `google.golang.org/grpc/status` +package. + +If the `req` argument is not `nil`, the function unwraps the `sdk.Context` +object from the `context.Context` object using the `sdk.UnwrapSDKContext` +function. It then retrieves a post object with the specified `Id` from the +blockchain's state using the `GetPost` function, and checks if the post was +found by checking the value of the `found` boolean variable. If the post was not +found, it returns an error with the type `sdkerrors.ErrKeyNotFound`. + +If the post was found, the function creates a new `types.QueryShowPostResponse` +object with the retrieved post object as a field, and returns a pointer to this +object and a `nil` error. + +## Modify `QueryShowPostResponse` + +Include the option `[(gogoproto.nullable) = false]` in the `post` field in the +`QueryShowPostResponse` message to generate the field without a pointer. + +```proto title="proto/blog/blog/query.proto" +message QueryShowPostResponse { + // highlight-next-line + Post post = 1 [(gogoproto.nullable) = false]; +} +``` + +Run the command to generate Go files from proto: + +``` +ignite generate proto-go +``` \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/04-blog/07-list.md b/docs/versioned_docs/version-v0.27.1/02-guide/04-blog/07-list.md new file mode 100644 index 0000000000..268a3349a5 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/04-blog/07-list.md @@ -0,0 +1,97 @@ +# List posts + +In this chapter, you will develop a feature that enables users to retrieve all +of the blog posts stored on your blockchain application. The feature will allow +users to perform a query and receive a paginated response, which means that the +output will be divided into smaller chunks or "pages" of data. This will allow +users to more easily navigate and browse through the list of posts, as they will +be able to view a specific number of posts at a time rather than having to +scroll through a potentially lengthy list all at once. + +## List posts + +Let's implement the `ListPost` keeper method that will be called when a user +makes a query to the blockchain application, requesting a paginated list of all +the posts stored on chain. + +```go title="x/blog/keeper/query_list_post.go" +package keeper + +import ( + "context" + + "blog/x/blog/types" + + "github.com/cosmos/cosmos-sdk/store/prefix" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/query" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +func (k Keeper) ListPost(goCtx context.Context, req *types.QueryListPostRequest) (*types.QueryListPostResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + var posts []types.Post + ctx := sdk.UnwrapSDKContext(goCtx) + + store := ctx.KVStore(k.storeKey) + postStore := prefix.NewStore(store, types.KeyPrefix(types.PostKey)) + + pageRes, err := query.Paginate(postStore, req.Pagination, func(key []byte, value []byte) error { + var post types.Post + if err := k.cdc.Unmarshal(value, &post); err != nil { + return err + } + + posts = append(posts, post) + return nil + }) + + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + return &types.QueryListPostResponse{Post: posts, Pagination: pageRes}, nil +} +``` + +`ListPost` takes in two arguments: a context object and a request object of type +`QueryListPostRequest`. It returns a response object of type +`QueryListPostResponse` and an error. + +The function first checks if the request object is `nil` and returns an error +with a `InvalidArgument` code if it is. It then initializes an empty slice of +`Post` objects and unwraps the context object. + +It retrieves a key-value store from the context using the `storeKey` field of +the keeper struct and creates a new store using a prefix of the `PostKey`. It +then calls the `Paginate` function from the `query` package on the store and the +pagination information in the request object. The function passed as an argument +to Paginate iterates over the key-value pairs in the store and unmarshals the +values into `Post` objects, which are then appended to the `posts` slice. + +If an error occurs during pagination, the function returns an `Internal error` +with the error message. Otherwise, it returns a `QueryListPostResponse` object +with the list of posts and pagination information. + +## Modify `QueryListPostResponse` + +Add a `repeated` keyword to return a list of posts and include the option +`[(gogoproto.nullable) = false]` to generate the field without a pointer. + +```proto title="proto/blog/blog/query.proto" +message QueryListPostResponse { + // highlight-next-line + repeated Post post = 1 [(gogoproto.nullable) = false]; + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} +``` + +Run the command to generate Go files from proto: + +``` +ignite generate proto-go +``` \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/04-blog/08-play.md b/docs/versioned_docs/version-v0.27.1/02-guide/04-blog/08-play.md new file mode 100644 index 0000000000..abda90113f --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/04-blog/08-play.md @@ -0,0 +1,97 @@ +# Play + +## Create a blog post by Alice + +``` +blogd tx blog create-post hello world --from alice +``` + +## Show a blog post + +``` +blogd q blog show-post 0 +``` + +```yml +post: + body: world + creator: cosmos1x33ummgkjdd6h2frlugt3tft7vnc0nxyfxnx9h + id: "0" + title: hello +``` + +## Create a blog post by Bob + +``` +blogd tx blog create-post foo bar --from bob +``` + +## List all blog posts with pagination + +``` +blogd q blog list-post +``` + +```yml +pagination: + next_key: null + total: "2" +post: +- body: world + creator: cosmos1x33ummgkjdd6h2frlugt3tft7vnc0nxyfxnx9h + id: "0" + title: hello +- body: bar + creator: cosmos1ysl9ws3fdamrrj4fs9ytzrrzw6ul3veddk7gz3 + id: "1" + title: foo +``` + +## Update a blog post + +``` +blogd tx blog update-post hello cosmos 0 --from alice +``` + +``` +blogd q blog show-post 0 +``` + +```yml +post: + body: cosmos + creator: cosmos1x33ummgkjdd6h2frlugt3tft7vnc0nxyfxnx9h + id: "0" + title: hello +``` + +## Delete a blog post + +``` +blogd tx blog delete-post 0 --from alice +``` + +``` +blogd q blog list-post +``` + +```yml +pagination: + next_key: null + total: "1" +post: +- body: bar + creator: cosmos1ysl9ws3fdamrrj4fs9ytzrrzw6ul3veddk7gz3 + id: "1" + title: foo +``` + +## Delete a blog post unsuccessfully + +``` +blogd tx blog delete-post 1 --from alice +``` + +```yml +raw_log: 'failed to execute message; message index: 0: incorrect owner: unauthorized' +``` \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/04-blog/09-summary.md b/docs/versioned_docs/version-v0.27.1/02-guide/04-blog/09-summary.md new file mode 100644 index 0000000000..c841606652 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/04-blog/09-summary.md @@ -0,0 +1,22 @@ +# Summary + +Congratulations on completing the Blog tutorial and building your first +functional application-specific blockchain using Ignite and Cosmos SDK! This is +a significant accomplishment, and you should be proud of the hard work and +dedication you put into it. + +One of the great things about using Ignite is that it allows you to quickly +generate most of the code for your app with just a few commands. This not only +saves you time, but also provides a solid structure for you to build upon as you +develop your app further. In this tutorial, you were able to create code for +handling four types of messages and two types of queries, which are important +building blocks for any blockchain application. + +You also tackled the task of implementing business-specific logic for creating, +updating, and deleting blog posts, as well as fetching individual blog posts by +ID and paginated lists of posts. You should now have a good understanding of how +to implement this sort of functionality in a blockchain context. + +Overall, completing this tutorial is a major accomplishment, and you should feel +confident in your ability to continue developing and expanding upon your app. +Keep up the great work, and keep learning and growing as a developer! \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/04-blog/_category_.json b/docs/versioned_docs/version-v0.27.1/02-guide/04-blog/_category_.json new file mode 100644 index 0000000000..bfc03e31cc --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/04-blog/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Module basics: Blog", + "link": null +} \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/05-loan/00-intro.md b/docs/versioned_docs/version-v0.27.1/02-guide/05-loan/00-intro.md new file mode 100644 index 0000000000..9c3380bcd9 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/05-loan/00-intro.md @@ -0,0 +1,86 @@ +# DeFi Loan + +Decentralized finance (DeFi) is a rapidly growing sector of the blockchain +ecosystem that is transforming the way we think about financial instruments and +services. DeFi offers a wide range of innovative financial products and +services, including lending, borrowing, spot trading, margin trading, and flash +loans, that are accessible to anyone with an internet connection and a digital +wallet. + +One of the key benefits of DeFi is that it allows end users to access financial +instruments and services quickly and easily, without the need for complex +onboarding processes or the submission of personal documents such as passports +or background checks. This makes DeFi an attractive alternative to traditional +banking systems, which can be slow, costly, and inconvenient. + +In this tutorial, you will learn how to create a DeFi platform that enables +users to lend and borrow digital assets from each other. The platform you will +build will be powered by a blockchain, which provides a decentralized and +immutable record of all transactions. This ensures that the platform is +transparent, secure, and resistant to fraud. + +A loan is a financial transaction in which one party, the borrower, receives a +certain amount of assets, such as money or digital tokens, and agrees to pay +back the loan amount plus a fee to the lender by a predetermined deadline. To +secure the loan, the borrower provides collateral, which may be seized by the +lender if the borrower fails to pay back the loan as agreed. + +A loan has several properties that define its terms and conditions. + +The `id` is a unique identifier that is used to identify the loan on a +blockchain. + +The `amount` is the amount of assets that are being lent to the borrower. + +The `fee` is the cost that the borrower must pay to the lender for the loan. + +The `collateral` is the asset or assets that the borrower provides to the lender +as security for the loan. + +The `deadline` is the date by which the borrower must pay back the loan. If the +borrower fails to pay back the loan by the deadline, the lender may choose to +liquidate the loan and seize the collateral. + +The `state` of a loan describes the current status of the loan and can take on +several values, such as `requested`, `approved`, `paid`, `cancelled`, or +`liquidated`. A loan is in the `requested` state when the borrower first submits +a request for the loan. If the lender approves the request, the loan moves to +the `approved` state. When the borrower repays the loan, the loan moves to the +`paid` state. If the borrower cancels the loan before it is approved, the loan +moves to the `cancelled` state. If the borrower is unable to pay back the loan +by the deadline, the lender may choose to liquidate the loan and seize the +collateral. In this case, the loan moves to the `liquidated` state. + +In a loan transaction, there are two parties involved: the borrower and the +lender. The borrower is the party that requests the loan and agrees to pay back +the loan amount plus a fee to the lender by a predetermined deadline. The lender +is the party that approves the loan request and provides the borrower with the +loan amount. + +As a borrower, you should be able to perform several actions on the loan +platform. These actions may include: + +* requesting a loan, +* canceling a loan, +* repaying a loan. + +Requesting a loan allows you to specify the terms and conditions of the loan, +including the amount, the fee, the collateral, and the deadline for repayment. +If you cancel a loan, you can withdraw your request for the loan before it is +approved or funded. Repaying a loan allows you to pay back the loan amount plus +the fee to the lender in accordance with the loan terms. + +As a lender, you should be able to perform two actions on the platform: + +* approving a loan +* liquidating a loan. + +Approving a loan allows you to accept the terms and conditions of the loan and +send the loan amount to the borrower. Liquidating a loan allows the lender to +seize the collateral if you are unable to pay back the loan by the deadline. + +By performing these actions, lenders and borrowers can interact with each other +and facilitate the lending and borrowing of digital assets on the platform. The +platform enables users to access financial instruments and services that allow +them to manage their assets and achieve their financial goals in a secure and +transparent manner. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/05-loan/01-init.md b/docs/versioned_docs/version-v0.27.1/02-guide/05-loan/01-init.md new file mode 100644 index 0000000000..9a979da953 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/05-loan/01-init.md @@ -0,0 +1,72 @@ +# Creating a structure of the application + +To create a structure for a blockchain application that enables users to lend +and borrow digital assets from each other, use the Ignite CLI to generate the +necessary code. + +First, create a new blockchain called `loan` by running the following command: + +``` +ignite scaffold chain loan --no-module +``` + +The `--no-module` flag tells Ignite not to create a default module. Instead, you +will create the module yourself in the next step. + +Next, change the directory to `loan/`: + +``` +cd loan +``` + +Create a module with a dependency on the standard Cosmos SDK `bank` module by +running the following command: + +``` +ignite scaffold module loan --dep bank +``` + +Create a `loan` model with a list of properties. + +``` +ignite scaffold list loan amount fee collateral deadline state borrower lender --no-message +``` + +The `--no-message` flag tells Ignite not to generate Cosmos SDK messages for +creating, updating, and deleting loans. Instead, you will generate the code for +custom messages. + + +To generate the code for handling the messages for requesting, approving, +repaying, liquidating, and cancelling loans, run the following commands: + +``` +ignite scaffold message request-loan amount fee collateral deadline +``` + +``` +ignite scaffold message approve-loan id:uint +``` + +``` +ignite scaffold message repay-loan id:uint +``` + +``` +ignite scaffold message liquidate-loan id:uint +``` + +``` +ignite scaffold message cancel-loan id:uint +``` + +Great job! By using a few simple commands with Ignite CLI, you have successfully +set up the foundation for your blockchain application. You have created a loan +model and included keeper methods to allow interaction with the store. In +addition, you have also implemented message handlers for five custom messages. + +Now that the basic structure is in place, it's time to move on to the next phase +of development. In the coming sections, you will be focusing on implementing the +business logic within the message handlers you have created. This will involve +writing code to define the specific actions and processes that should be carried +out when each message is received. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/05-loan/02-bank.md b/docs/versioned_docs/version-v0.27.1/02-guide/05-loan/02-bank.md new file mode 100644 index 0000000000..440c806e79 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/05-loan/02-bank.md @@ -0,0 +1,32 @@ +# Importing methods from the Bank keeper + +In the previous step you have created the `loan` module with `ignite scaffold +module` using `--dep bank`. This command created a new module and added the +`bank` keeper to the `loan` module, which allows you to add and use bank's +keeper methods in loan's keeper methods. + +To see the changes made by `--dep bank`, review the following files: +`x/loan/keeper/keeper.go` and `x/loan/module.go`. + +Ignite takes care of adding the `bank` keeper, but you still need to tell the +`loan` module which `bank` methods you will be using. You will be using three +methods: `SendCoins`, `SendCoinsFromAccountToModule`, and +`SendCoinsFromModuleToAccount`. You can do that by adding method signatures to +the `BankKeeper` interface: + +```go title="x/loan/types/expected_keepers.go" +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +type BankKeeper interface { + SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins + // highlight-start + SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error + SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error + SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error + // highlight-end +} +``` \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/05-loan/03-request.md b/docs/versioned_docs/version-v0.27.1/02-guide/05-loan/03-request.md new file mode 100644 index 0000000000..a68aed6845 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/05-loan/03-request.md @@ -0,0 +1,117 @@ +# Request a loan + +Implement `RequestLoan` keeper method that will be called whenever a user +requests a loan. `RequestLoan` creates a new loan with the provided data, sends +the collateral from the borrower's account to a module account, and adds the +loan to the blockchain's store. + +## Keeper method + +```go title="x/loan/keeper/msg_server_request_loan.go" +package keeper + +import ( + "context" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "loan/x/loan/types" +) + +func (k msgServer) RequestLoan(goCtx context.Context, msg *types.MsgRequestLoan) (*types.MsgRequestLoanResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + var loan = types.Loan{ + Amount: msg.Amount, + Fee: msg.Fee, + Collateral: msg.Collateral, + Deadline: msg.Deadline, + State: "requested", + Borrower: msg.Creator, + } + borrower, err := sdk.AccAddressFromBech32(msg.Creator) + if err != nil { + panic(err) + } + collateral, err := sdk.ParseCoinsNormalized(loan.Collateral) + if err != nil { + panic(err) + } + sdkError := k.bankKeeper.SendCoinsFromAccountToModule(ctx, borrower, types.ModuleName, collateral) + if sdkError != nil { + return nil, sdkError + } + k.AppendLoan(ctx, loan) + return &types.MsgRequestLoanResponse{}, nil +} +``` + +The function takes in two arguments: a `context.Context` object and a pointer to +a `types.MsgRequestLoan` struct. It returns a pointer to a +`types.MsgRequestLoanResponse` struct and an `error` object. + +The first thing the function does is create a new `types.Loan` struct with the +data from the input `types.MsgRequestLoan` struct. It sets the `State` field of +`the types.Loan` struct to "requested". + +Next, the function gets the borrower's address from the `msg.Creator` field of +the input `types.MsgRequestLoan` struct. It then parses the `loan.Collateral` +field (which is a string) into `sdk.Coins` using the `sdk.ParseCoinsNormalized` +function. + +The function then sends the collateral from the borrower's account to a module +account using the `k.bankKeeper.SendCoinsFromAccountToModule` function. Finally, +it adds the new loan to a keeper using the `k.AppendLoan` function. The function +returns a `types.MsgRequestLoanResponse` struct and a `nil` error if all goes +well. + +## Basic message validation + +When a loan is created, a certain message input validation is required. You want +to throw error messages in case the end user tries impossible inputs. + +```go title="x/loan/types/message_request_loan.go" +package types + +import ( + // highlight-next-line + "strconv" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +func (msg *MsgRequestLoan) ValidateBasic() error { + _, err := sdk.AccAddressFromBech32(msg.Creator) + if err != nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid creator address (%s)", err) + } + // highlight-start + amount, _ := sdk.ParseCoinsNormalized(msg.Amount) + if !amount.IsValid() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "amount is not a valid Coins object") + } + if amount.Empty() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "amount is empty") + } + fee, _ := sdk.ParseCoinsNormalized(msg.Fee) + if !fee.IsValid() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "fee is not a valid Coins object") + } + deadline, err := strconv.ParseInt(msg.Deadline, 10, 64) + if err != nil { + return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "deadline is not an integer") + } + if deadline <= 0 { + return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "deadline should be a positive integer") + } + collateral, _ := sdk.ParseCoinsNormalized(msg.Collateral) + if !collateral.IsValid() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "collateral is not a valid Coins object") + } + if collateral.Empty() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "collateral is empty") + } + // highlight-end + return nil +} +``` diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/05-loan/04-approve.md b/docs/versioned_docs/version-v0.27.1/02-guide/05-loan/04-approve.md new file mode 100644 index 0000000000..4fb75acf66 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/05-loan/04-approve.md @@ -0,0 +1,97 @@ +# Approve a loan + +After a loan request has been made, it is possible for another account to +approve the loan and accept the terms proposed by the borrower. This process +involves the transfer of the requested funds from the lender to the borrower. + +To be eligible for approval, a loan must have a status of "requested." This +means that the borrower has made a request for a loan and is waiting for a +lender to agree to the terms and provide the funds. Once a lender has decided to +approve the loan, they can initiate the transfer of the funds to the borrower. + +Upon loan approval, the status of the loan is changed to "approved." This +signifies that the funds have been successfully transferred and that the loan +agreement is now in effect. + +## Keeper method + +```go title="x/loan/keeper/msg_server_approve_loan.go" +package keeper + +import ( + "context" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + "loan/x/loan/types" +) + +func (k msgServer) ApproveLoan(goCtx context.Context, msg *types.MsgApproveLoan) (*types.MsgApproveLoanResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + loan, found := k.GetLoan(ctx, msg.Id) + if !found { + return nil, sdkerrors.Wrapf(sdkerrors.ErrKeyNotFound, "key %d doesn't exist", msg.Id) + } + if loan.State != "requested" { + return nil, sdkerrors.Wrapf(types.ErrWrongLoanState, "%v", loan.State) + } + lender, _ := sdk.AccAddressFromBech32(msg.Creator) + borrower, _ := sdk.AccAddressFromBech32(loan.Borrower) + amount, err := sdk.ParseCoinsNormalized(loan.Amount) + if err != nil { + return nil, sdkerrors.Wrap(types.ErrWrongLoanState, "Cannot parse coins in loan amount") + } + err = k.bankKeeper.SendCoins(ctx, lender, borrower, amount) + if err != nil { + return nil, err + } + loan.Lender = msg.Creator + loan.State = "approved" + k.SetLoan(ctx, loan) + return &types.MsgApproveLoanResponse{}, nil +} +``` + +`ApproveLoan` takes a context and a message of type `*types.MsgApproveLoan` as +input, and returns a pointer to a `types.MsgApproveLoanResponse` and an `error`. + +The function first retrieves a loan object by calling `k.GetLoan(ctx, msg.Id)`, +where `ctx` is a context object, `k` is the `msgServer` object, `GetLoan` is a +method on `k`, and `msg.Id` is a field of the msg object passed as an argument. +If the loan is not found, it returns `nil` and an error wrapped with +`sdkerrors.ErrKeyNotFound`. + +Next, the function checks if the loan's state is `"requested"`. If it is not, it +returns `nil` and an error wrapped with `types.ErrWrongLoanState`. + +If the loan's state is `"requested"`, the function parses the addresses of the +lender and borrower from bech32 strings, and then parses the `amount` of the +loan from a string. If there is an error parsing the coins in the loan amount, +it returns `nil` and an error wrapped with `types.ErrWrongLoanState`. + +Otherwise, the function calls the `SendCoins` method on the `k.bankKeeper` +object, passing it the context, the lender and borrower addresses, and the +amount of the loan. It then updates the lender field of the loan object and sets +its state to `"approved"`. Finally, it stores the updated loan object by calling +`k.SetLoan(ctx, loan)`. + +At the end, the function returns a `types.MsgApproveLoanResponse` object and +`nil` for the error. + +## Register a custom error + +To register the custom error `ErrWrongLoanState` that is used in the +`ApproveLoan` function, modify the "errors.go" file: + +```go title="x/loan/types/errors.go" +package types + +import ( + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +var ( + ErrWrongLoanState = sdkerrors.Register(ModuleName, 2, "wrong loan state") +) +``` \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/05-loan/05-repay.md b/docs/versioned_docs/version-v0.27.1/02-guide/05-loan/05-repay.md new file mode 100644 index 0000000000..72150f695f --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/05-loan/05-repay.md @@ -0,0 +1,97 @@ +# Repay a loan + +The `RepayLoan` method is responsible for handling the repayment of a loan. This +involves transferring the borrowed funds, along with any agreed upon fees, from +the borrower to the lender. In addition, the collateral that was provided as +part of the loan agreement will be released from the escrow account and returned +to the borrower. + +It is important to note that the `RepayLoan` method can only be called under +certain conditions. Firstly, the transaction must be signed by the borrower of +the loan. This ensures that only the borrower has the ability to initiate the +repayment process. Secondly, the loan must be in an approved status. This means +that the loan has received approval and is ready to be repaid. + +To implement the `RepayLoan` method, we must ensure that these conditions are +met before proceeding with the repayment process. Once the necessary checks have +been performed, the method can then handle the transfer of funds and the release +of the collateral from the escrow account. + +## Keeper method + +```go title="x/loan/keeper/msg_server_repay_loan.go" +package keeper + +import ( + "context" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + "loan/x/loan/types" +) + +func (k msgServer) RepayLoan(goCtx context.Context, msg *types.MsgRepayLoan) (*types.MsgRepayLoanResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + loan, found := k.GetLoan(ctx, msg.Id) + if !found { + return nil, sdkerrors.Wrapf(sdkerrors.ErrKeyNotFound, "key %d doesn't exist", msg.Id) + } + if loan.State != "approved" { + return nil, sdkerrors.Wrapf(types.ErrWrongLoanState, "%v", loan.State) + } + lender, _ := sdk.AccAddressFromBech32(loan.Lender) + borrower, _ := sdk.AccAddressFromBech32(loan.Borrower) + if msg.Creator != loan.Borrower { + return nil, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "Cannot repay: not the borrower") + } + amount, _ := sdk.ParseCoinsNormalized(loan.Amount) + fee, _ := sdk.ParseCoinsNormalized(loan.Fee) + collateral, _ := sdk.ParseCoinsNormalized(loan.Collateral) + err := k.bankKeeper.SendCoins(ctx, borrower, lender, amount) + if err != nil { + return nil, err + } + err = k.bankKeeper.SendCoins(ctx, borrower, lender, fee) + if err != nil { + return nil, err + } + err = k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, borrower, collateral) + if err != nil { + return nil, err + } + loan.State = "repayed" + k.SetLoan(ctx, loan) + return &types.MsgRepayLoanResponse{}, nil +} +``` + +`RepayLoan` takes in two arguments: a context and a pointer to a +`types.MsgRepayLoan` type. It returns a pointer to a +`types.MsgRepayLoanResponse` type and an `error`. + +The method first retrieves a loan from storage by passing the provided loan ID +to the `k.GetLoan` method. If the loan cannot be found, the method returns an +error wrapped in a `sdkerrors.ErrKeyNotFound` error. + +The method then checks that the state of the loan is "approved". If it is not, +the method returns an error wrapped in a `types.ErrWrongLoanState` error. + +Next, the method converts the lender and borrower addresses stored in the loan +struct to `sdk.AccAddress` types using the `sdk.AccAddressFromBech32` function. +It then checks that the transaction is signed by the borrower of the loan by +comparing the `msg.Creator` field to the borrower address stored in the loan +struct. If these do not match, the method returns an error wrapped in a +`sdkerrors.ErrUnauthorized` error. + +The method then parses the loan amount, fee, and collateral stored in the loan +struct as `sdk.Coins` using the `sdk.ParseCoinsNormalized` function. It then +uses the `k.bankKeeper.SendCoins` function to transfer the loan amount and fee +from the borrower to the lender. It then uses the +`k.bankKeeper.SendCoinsFromModuleToAccount` function to transfer the collateral +from the escrow account to the borrower. + +Finally, the method updates the state of the loan to "repayed" and stores the +updated loan in storage using the `k.SetLoan` method. The method returns a +`types.MsgRepayLoanResponse` and a `nil` error to indicate that the repayment +process was successful. diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/05-loan/06-liquidate.md b/docs/versioned_docs/version-v0.27.1/02-guide/05-loan/06-liquidate.md new file mode 100644 index 0000000000..8d49058b76 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/05-loan/06-liquidate.md @@ -0,0 +1,89 @@ +# Liquidate loan + +The `LiquidateLoan` method is a function that allows the lender to sell off the +collateral belonging to the borrower in the event that the borrower has failed +to repay the loan by the specified deadline. This process is known as +"liquidation" and is typically carried out as a way for the lender to recoup +their losses in the event that the borrower is unable to fulfill their repayment +obligations. + +During the liquidation process, the collateral tokens that have been pledged by +the borrower as security for the loan are transferred from the borrower's +account to the lender's account. This transfer is initiated by the lender and is +typically triggered when the borrower fails to repay the loan by the agreed upon +deadline. Once the collateral has been transferred, the lender can then sell it +off in order to recoup their losses and compensate for the unpaid loan. + +## Keeper method + +```go title="x/loan/keeper/msg_server_liquidate_loan.go" +package keeper + +import ( + "context" + "strconv" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + "loan/x/loan/types" +) + +func (k msgServer) LiquidateLoan(goCtx context.Context, msg *types.MsgLiquidateLoan) (*types.MsgLiquidateLoanResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + loan, found := k.GetLoan(ctx, msg.Id) + if !found { + return nil, sdkerrors.Wrapf(sdkerrors.ErrKeyNotFound, "key %d doesn't exist", msg.Id) + } + if loan.Lender != msg.Creator { + return nil, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "Cannot liquidate: not the lender") + } + if loan.State != "approved" { + return nil, sdkerrors.Wrapf(types.ErrWrongLoanState, "%v", loan.State) + } + lender, _ := sdk.AccAddressFromBech32(loan.Lender) + collateral, _ := sdk.ParseCoinsNormalized(loan.Collateral) + deadline, err := strconv.ParseInt(loan.Deadline, 10, 64) + if err != nil { + panic(err) + } + if ctx.BlockHeight() < deadline { + return nil, sdkerrors.Wrap(types.ErrDeadline, "Cannot liquidate before deadline") + } + err = k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, lender, collateral) + if err != nil { + return nil, err + } + loan.State = "liquidated" + k.SetLoan(ctx, loan) + return &types.MsgLiquidateLoanResponse{}, nil +} +``` + +`LiquidateLoan` takes in a context and a `types.MsgLiquidateLoan` message as input and returns a types.MsgLiquidateLoanResponse message and an error as output. + +The function first retrieves a loan using the `GetLoan` method and the `Id` field of the input message. If the loan is not found, it returns an error using the `sdkerrors.Wrap` function and the `sdkerrors.ErrKeyNotFound` error code. + +Next, the function checks that the `Creator` field of the input message is the same as the `Lender` field of the loan. If they are not the same, it returns an error using the `sdkerrors.Wrap` function and the `sdkerrors.ErrUnauthorized` error code. + +The function then checks that the State field of the loan is equal to "approved". If it is not, it returns an error using the `sdkerrors.Wrapf` function and the `types.ErrWrongLoanState` error code. + +The function then converts the Lender field of the loan to an address using the `sdk.AccAddressFromBech32` function and the `Collateral` field to coins using the `sdk.ParseCoinsNormalized` function. It also converts the `Deadline` field to an integer using the `strconv.ParseInt` function. If this function returns an error, it panics. + +Finally, the function checks that the current block height is greater than or equal to the deadline. If it is not, it returns an error using the `sdkerrors.Wrap` function and the `types.ErrDeadline` error code. If all checks pass, the function uses the `bankKeeper.SendCoinsFromModuleToAccount` method to transfer the collateral from the module account to the lender's account and updates the `State` field of the loan to `"liquidated"`. It then stores the updated loan using the `SetLoan` method and returns a `types.MsgLiquidateLoanResponse` message with no error. + +## Register a custom error + +```go title="x/loan/types/errors.go" +package types + +import ( + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +var ( + ErrWrongLoanState = sdkerrors.Register(ModuleName, 2, "wrong loan state") + // highlight-next-line + ErrDeadline = sdkerrors.Register(ModuleName, 3, "deadline") +) +``` diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/05-loan/07-cancel.md b/docs/versioned_docs/version-v0.27.1/02-guide/05-loan/07-cancel.md new file mode 100644 index 0000000000..3997072a92 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/05-loan/07-cancel.md @@ -0,0 +1,73 @@ +# Cancel a loan + +As a borrower, you have the option to cancel a loan you have created if you no +longer want to proceed with it. However, this action is only possible if the +loan's current status is marked as "requested". + +If you decide to cancel the loan, the collateral tokens that were being held as +security for the loan will be transferred back to your account from the module +account. This means that you will regain possession of the collateral tokens you +had originally put up for the loan. + +```go title="x/loan/keeper/msg_server_cancel_loan.go" +package keeper + +import ( + "context" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + "loan/x/loan/types" +) + +func (k msgServer) CancelLoan(goCtx context.Context, msg *types.MsgCancelLoan) (*types.MsgCancelLoanResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + loan, found := k.GetLoan(ctx, msg.Id) + if !found { + return nil, sdkerrors.Wrapf(sdkerrors.ErrKeyNotFound, "key %d doesn't exist", msg.Id) + } + if loan.Borrower != msg.Creator { + return nil, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "Cannot cancel: not the borrower") + } + if loan.State != "requested" { + return nil, sdkerrors.Wrapf(types.ErrWrongLoanState, "%v", loan.State) + } + borrower, _ := sdk.AccAddressFromBech32(loan.Borrower) + collateral, _ := sdk.ParseCoinsNormalized(loan.Collateral) + err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, borrower, collateral) + if err != nil { + return nil, err + } + loan.State = "cancelled" + k.SetLoan(ctx, loan) + return &types.MsgCancelLoanResponse{}, nil +} +``` + +`CancelLoan` takes in two arguments: a `context.Context` named `goCtx` and a +pointer to a `types.MsgCancelLoan` named `msg`. It returns a pointer to a +`types.MsgCancelLoanResponse` and an error. + +The function begins by using the `sdk.UnwrapSDKContext` method to get the +`sdk.Context` from the `context.Context` object. It then uses the `GetLoan` +method of the `msgServer` type to retrieve a loan identified by the `Id` field +of the `msg` argument. If the loan is not found, the function returns an error +using the `sdk.ErrKeyNotFound` error wrapped with the `sdk.Wrap` method. + +Next, the function checks if the `Creator` field of the msg argument is the same +as the `Borrower` field of the loan. If they are not the same, the function +returns an error using the `sdk.ErrUnauthorized` error wrapped with the +`sdk.Wrap` method. + +The function then checks if the `State` field of the loan is equal to the string +`"requested"`. If it is not, the function returns an error using the +types.`ErrWrongLoanState` error wrapped with the `sdk.Wrapf` method. + +If the loan has the correct state and the creator of the message is the borrower +of the loan, the function proceeds to send the collateral coins held in the +`Collateral` field of the loan back to the borrower's account using the +`SendCoinsFromModuleToAccount` method of the `bankKeeper`. The function then +updates the State field of the loan to the string "cancelled" and sets the +updated loan using the `SetLoan` method. Finally, the function returns a +`types.MsgCancelLoanResponse` object and a nil error. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/05-loan/08-play.md b/docs/versioned_docs/version-v0.27.1/02-guide/05-loan/08-play.md new file mode 100644 index 0000000000..eb715b0512 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/05-loan/08-play.md @@ -0,0 +1,318 @@ +# Play + +Add `10000foocoin` to Alice's account. These tokens will be used as a loan +collateral. + +```yml title="config.yml" +version: 1 +accounts: + - name: alice + coins: + - 20000token + # highlight-next-line + - 10000foocoin + - 200000000stake + - name: bob + coins: + - 10000token + - 100000000stake +client: + openapi: + path: docs/static/openapi.yml +faucet: + name: bob + coins: + - 5token + - 100000stake +validators: + - name: alice + bonded: 100000000stake +``` + +Start a blockchain node: + +``` +ignite chain serve +``` + +## Repaying a loan + +Request a loan of `1000token` with `100token` as a fee and `1000foocoin` as a +collateral from Alice's account. The deadline is set to `500` blocks: + +``` +loand tx loan request-loan 1000token 100token 1000foocoin 500 --from alice +``` + +``` +loand q loan list-loan +``` + +```yml +Loan: +- amount: 1000token + borrower: cosmos153dk8qh56v4yg6e4uzrvvqjueu6d36fptlr2kw + collateral: 1000foocoin + deadline: "500" + fee: 100token + id: "0" + lender: "" + state: requested +``` + +Please be aware that the addresses displayed in your terminal window (such as those in the `borrower` field) will not match the ones provided in this tutorial. This is because Ignite generates new accounts each time a chain is started, unless an account has a mnemonic specified in the `config.yml` file. + +Approve the loan from Bob's account: + +``` +loand tx loan approve-loan 0 --from bob +``` + +``` +loand q loan list-loan +``` + +The `lender` field has been updated to Bob's address and the `state` field has +been updated to `approved`: + +```yml +Loan: +- amount: 1000token + borrower: cosmos153dk8qh56v4yg6e4uzrvvqjueu6d36fptlr2kw + collateral: 1000foocoin + deadline: "500" + fee: 100token + id: "0" + # highlight-start + lender: cosmos1qfzpxfhsu2qfy2exkukuanrkzrrexh9yeg2pr4 + state: approved + # highlight-end +``` + +``` +loand q bank balances $(loand keys show alice -a) +``` + +The `foocoin` balance has been updated to `9000`, because `1000foocoin` has been +transferred as collateral to the module account. The `token` balance has been +updated to `21000`, because `1000token` has been transferred from Bob's account +to Alice's account as a loan: + +```yml +balances: + # highlight-start +- amount: "9000" + denom: foocoin + # highlight-end +- amount: "100000000" + denom: stake + # highlight-start +- amount: "21000" + denom: token + # highlight-end +``` + +``` +loand q bank balances $(loand keys show bob -a) +``` + +The `token` balance has been updated to `9000`, because `1000token` has been +transferred from Bob's account to Alice's account as a loan: + +```yml +balances: +- amount: "100000000" + denom: stake + # highlight-start +- amount: "9000" + denom: token + # highlight-end +``` + +Repay the loan from Alice's account: + +``` +loand tx loan repay-loan 0 --from alice +``` + +``` +loand q loan list-loan +``` + +The `state` field has been updated to `repayed`: + +```yml +Loan: +- amount: 1000token + borrower: cosmos153dk8qh56v4yg6e4uzrvvqjueu6d36fptlr2kw + collateral: 1000foocoin + deadline: "500" + fee: 100token + id: "0" + lender: cosmos1qfzpxfhsu2qfy2exkukuanrkzrrexh9yeg2pr4 + # highlight-next-line + state: repayed +``` + +``` +loand q bank balances $(loand keys show alice -a) +``` + +The `foocoin` balance has been updated to `10000`, because `1000foocoin` has +been transferred from the module account to Alice's account. The `token` balance +has been updated to `19900`, because `1000token` has been transferred from +Alice's account to Bob's account as a repayment and `100token` has been +transferred from Alice's account to Bob's account as a fee: + +```yml +balances: + # highlight-start +- amount: "10000" + denom: foocoin + # highlight-end +- amount: "100000000" + denom: stake + # highlight-start +- amount: "19900" + denom: token + # highlight-end +``` + +``` +loand q bank balances $(loand keys show bob -a) +``` + +The `token` balance has been updated to `10100`, because `1000token` has been +transferred from Alice's account to Bob's account as a repayment and `100token` +has been transferred from Alice's account to Bob's account as a fee: + +```yml +balances: +- amount: "100000000" + denom: stake + # highlight-start +- amount: "10100" + denom: token + # highlight-end +``` + +## Liquidating a loan + +Request a loan of `1000token` with `100token` as a fee and `1000foocoin` as a +collateral from Alice's account. The deadline is set to `20` blocks. The +deadline is set to a very small value, so that the loan can be quickly +liquidated in the next step: + +``` +loand tx loan request-loan 1000token 100token 1000foocoin 20 --from alice +``` + +``` +loand q loan list-loan +``` + +A loan has been added to the list: + +```yml +Loan: +- amount: 1000token + borrower: cosmos153dk8qh56v4yg6e4uzrvvqjueu6d36fptlr2kw + collateral: 1000foocoin + deadline: "500" + fee: 100token + id: "0" + lender: cosmos1qfzpxfhsu2qfy2exkukuanrkzrrexh9yeg2pr4 + state: repayed + # highlight-start +- amount: 1000token + borrower: cosmos153dk8qh56v4yg6e4uzrvvqjueu6d36fptlr2kw + collateral: 1000foocoin + deadline: "20" + fee: 100token + id: "1" + lender: "" + state: requested + # highlight-end +``` + +Approve the loan from Bob's account: + +``` +loand tx loan approve-loan 1 --from bob +``` + +Liquidate the loan from Bob's account: + +``` +loand tx loan liquidate-loan 1 --from bob +``` + +``` +loand q loan list-loan +``` + +The loan has been liquidated: + +```yml +Loan: +- amount: 1000token + borrower: cosmos153dk8qh56v4yg6e4uzrvvqjueu6d36fptlr2kw + collateral: 1000foocoin + deadline: "500" + fee: 100token + id: "0" + lender: cosmos1qfzpxfhsu2qfy2exkukuanrkzrrexh9yeg2pr4 + state: repayed + # highlight-start +- amount: 1000token + borrower: cosmos153dk8qh56v4yg6e4uzrvvqjueu6d36fptlr2kw + collateral: 1000foocoin + deadline: "20" + fee: 100token + id: "1" + lender: cosmos1qfzpxfhsu2qfy2exkukuanrkzrrexh9yeg2pr4 + state: liquidated + # highlight-end +``` + +``` +loand q bank balances $(loand keys show alice -a) +``` + +The `foocoin` balance has been updated to `9000`, because `1000foocoin` has been +transferred from Alice's account to the module account as a collateral. Alice +has lost her collateral, but she has kept the loan amount: + +```yml +balances: + # highlight-start +- amount: "9000" + denom: foocoin + # highlight-end +- amount: "100000000" + denom: stake + # highlight-start +- amount: "20900" + denom: token + # highlight-end +``` + +``` +loand q bank balances $(loand keys show bob -a) +``` + +The `foocoin` balance has been updated to `1000`, because `1000foocoin` has been +transferred from the module account to Bob's account as a collateral. Bob has +gained the collateral, but he has lost the loan amount: + +```yml +balances: + # highlight-start +- amount: "1000" + denom: foocoin + # highlight-end +- amount: "100000000" + denom: stake +- amount: "9100" + denom: token +``` diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/05-loan/_category_.json b/docs/versioned_docs/version-v0.27.1/02-guide/05-loan/_category_.json new file mode 100644 index 0000000000..0a76ef93ff --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/05-loan/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Advanced Module: DeFi Loan", + "link": null +} \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/06-ibc.md b/docs/versioned_docs/version-v0.27.1/02-guide/06-ibc.md new file mode 100644 index 0000000000..83efb4f760 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/06-ibc.md @@ -0,0 +1,710 @@ +--- +sidebar_position: 7 +description: Build an understanding of how to create and send packets across blockchains and navigate between blockchains. +title: "Inter-Blockchain Communication: Basics" +--- + +# Inter-Blockchain Communication: Basics + +The Inter-Blockchain Communication protocol (IBC) is an important part of the +Cosmos SDK ecosystem. The Hello World tutorial is a time-honored tradition in +computer programming. This tutorial builds an understanding of how to create and +send packets across blockchain. This foundational knowledge helps you navigate +between blockchains with the Cosmos SDK. + +**You will learn how to** + +- Use IBC to create and send packets between blockchains. +- Navigate between blockchains using the Cosmos SDK and the Ignite CLI Relayer. +- Create a basic blog post and save the post on another blockchain. + +## What is IBC? + +The Inter-Blockchain Communication protocol (IBC) allows blockchains to talk to +each other. IBC handles transport across different sovereign blockchains. This +end-to-end, connection-oriented, stateful protocol provides reliable, ordered, +and authenticated communication between heterogeneous blockchains. + +The [IBC protocol in the Cosmos +SDK](https://ibc.cosmos.network/main/ibc/overview.html) is the standard for the +interaction between two blockchains. The IBCmodule interface defines how packets +and messages are constructed to be interpreted by the sending and the receiving +blockchain. + +The IBC relayer lets you connect between sets of IBC-enabled chains. This +tutorial teaches you how to create two blockchains and then start and use the +relayer with Ignite CLI to connect two blockchains. + +This tutorial covers essentials like modules, IBC packets, relayer, and the +lifecycle of packets routed through IBC. + +## Create a blockchain + +Create a blockchain app with a blog module to write posts on other blockchains +that contain the Hello World message. For this tutorial, you can write posts for +the Cosmos SDK universe that contain Hello Mars, Hello Cosmos, and Hello Earth +messages. + +For this simple example, create an app that contains a blog module that has a +post transaction with title and text. + +After you define the logic, run two blockchains that have this module installed. + +- The chains can send posts between each other using IBC. + +- On the sending chain, save the `acknowledged` and `timed out` posts. + +After the transaction is acknowledged by the receiving chain, you know that the +post is saved on both blockchains. + +- The sending chain has the additional data `postID`. + +- Sent posts that are acknowledged and timed out contain the title and the + target chain of the post. These identifiers +- are visible on the parameter `chain`. The following chart shows the lifecycle + of a packet that travels through IBC. + +![The Lifecycle of an IBC packet](./images/packet_sendpost.png) + +## Build your blockchain app + +Use Ignite CLI to scaffold the blockchain app and the blog module. + +### Build a new blockchain + +To scaffold a new blockchain named `planet`: + +```bash +ignite scaffold chain planet --no-module +cd planet +``` + +A new directory named `planet` is created in your home directory. The `planet` +directory contains a working blockchain app. + +### Scaffold the blog module inside your blockchain + +Next, use Ignite CLI to scaffold a blog module with IBC capabilities. The blog +module contains the logic for creating blog posts and routing them through IBC +to the second blockchain. + +To scaffold a module named `blog`: + +```bash +ignite scaffold module blog --ibc +``` + +A new directory with the code for an IBC module is created in `planet/x/blog`. +Modules scaffolded with the `--ibc` flag include all the logic for the +scaffolded IBC module. + +### Generate CRUD actions for types + +Next, create the CRUD actions for the blog module types. + +Use the `ignite scaffold list` command to scaffold the boilerplate code for the +create, read, update, and delete (CRUD) actions. + +These `ignite scaffold list` commands create CRUD code for the following +transactions: + +- Creating blog posts + + ```bash + ignite scaffold list post title content creator --no-message --module blog + ``` + +- Processing acknowledgments for sent posts + + ```bash + ignite scaffold list sentPost postID title chain creator --no-message --module blog + ``` + +- Managing post timeouts + + ```bash + ignite scaffold list timedoutPost title chain creator --no-message --module blog + ``` + +The scaffolded code includes proto files for defining data structures, messages, +messages handlers, keepers for modifying the state, and CLI commands. + +### Ignite CLI Scaffold List Command Overview + +``` +ignite scaffold list [typeName] [field1] [field2] ... [flags] +``` + +The first argument of the `ignite scaffold list [typeName]` command specifies +the name of the type being created. For the blog app, you created `post`, +`sentPost`, and `timedoutPost` types. + +The next arguments define the fields that are associated with the type. For the +blog app, you created `title`, `content`, `postID`, and `chain` fields. + +The `--module` flag defines which module the new transaction type is added to. +This optional flag lets you manage multiple modules within your Ignite CLI app. +When the flag is not present, the type is scaffolded in the module that matches +the name of the repo. + +When a new type is scaffolded, the default behavior is to scaffold messages that +can be sent by users for CRUD operations. The `--no-message` flag disables this +feature. Disable the messages option for the app since you want the posts to be +created upon reception of IBC packets and not directly created from a user's +messages. + +### Scaffold a sendable and interpretable IBC packet + +You must generate code for a packet that contains the title and the content of +the blog post. + +The `ignite packet` command creates the logic for an IBC packet that can be sent +to another blockchain. + +- The `title` and `content` are stored on the target chain. + +- The `postID` is acknowledged on the sending chain. + +To scaffold a sendable and interpretable IBC packet: + +```bash +ignite scaffold packet ibcPost title content --ack postID --module blog +``` + +Notice the fields in the `ibcPost` packet match the fields in the `post` type +that you created earlier. + +- The `--ack` flag defines which identifier is returned to the sending + blockchain. + +- The `--module` flag specifies to create the packet in a particular IBC module. + +The `ignite packet` command also scaffolds the CLI command that is capable of +sending an IBC packet: + +```bash +planetd tx blog send-ibcPost [portID] [channelID] [title] [content] +``` + +## Modify the source code + +After you create the types and transactions, you must manually insert the logic +to manage updates in the database. Modify the source code to save the data as +specified earlier in this tutorial. + +### Add creator to the blog post packet + +Start with the proto file that defines the structure of the IBC packet. + +To identify the creator of the post in the receiving blockchain, add the +`creator` field inside the packet. This field was not specified directly in the +command because it would automatically become a parameter in the `SendIbcPost` +CLI command. + +```protobuf title="proto/planet/blog/packet.proto" +message IbcPostPacketData { + string title = 1; + string content = 2; + // highlight-next-line + string creator = 3; +} +``` + +To make sure the receiving chain has content on the creator of a blog post, add +the `msg.Creator` value to the IBC `packet`. + +- The content of the `sender` of the message is automatically included in + `SendIbcPost` message. +- The sender is verified as the signer of the message, so you can add the + `msg.Sender` as the creator to the new packet +- before it is sent over IBC. + +```go title="x/blog/keeper/msg_server_ibc_post.go" +package keeper + +import ( + // ... + "planet/x/blog/types" +) + +func (k msgServer) SendIbcPost(goCtx context.Context, msg *types.MsgSendIbcPost) (*types.MsgSendIbcPostResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + // TODO: logic before transmitting the packet + + // Construct the packet + var packet types.IbcPostPacketData + + packet.Title = msg.Title + packet.Content = msg.Content + // highlight-next-line + packet.Creator = msg.Creator + + // Transmit the packet + err := k.TransmitIbcPostPacket( + ctx, + packet, + msg.Port, + msg.ChannelID, + clienttypes.ZeroHeight(), + msg.TimeoutTimestamp, + ) + if err != nil { + return nil, err + } + + return &types.MsgSendIbcPostResponse{}, nil +} +``` + +### Receive the post + +The methods for primary transaction logic are in the `x/blog/keeper/ibc_post.go` +file. Use these methods to manage IBC packets: + +- `TransmitIbcPostPacket` is called manually to send the packet over IBC. This + method also defines the logic before the packet is sent over IBC to another + blockchain app. +- `OnRecvIbcPostPacket` hook is automatically called when a packet is received + on the chain. This method defines the packet reception logic. +- `OnAcknowledgementIbcPostPacket` hook is called when a sent packet is + acknowledged on the source chain. This method defines the logic when the + packet has been received. +- `OnTimeoutIbcPostPacket` hook is called when a sent packet times out. This + method defines the logic when the packet is not received on the target chain + +You must modify the source code to add the logic inside those functions so that +the data tables are modified accordingly. + +On reception of the post message, create a new post with the title and the +content on the receiving chain. + +To identify the blockchain app that a message is originating from and who +created the message, use an identifier in the following format: + +`--` + +Finally, the Ignite CLI-generated AppendPost function returns the ID of the new +appended post. You can return this value to the source chain through +acknowledgment. + +Append the type instance as `PostID` on receiving the packet: + +- The context `ctx` is an [immutable data + structure](https://docs.cosmos.network/main/core/context.html#go-context-package) + that has header data from the transaction. See [how the context is + initiated](https://github.com/cosmos/cosmos-sdk/blob/main/types/context.go#L71) +- The identifier format that you defined earlier +- The `title` is the Title of the blog post +- The `content` is the Content of the blog post + +In the `x/blog/keeper/ibc_post.go` file, make sure to import `"strconv"` below +`"errors"`: + +```go title="x/blog/keeper/ibc_post.go" +import ( + //... + + "strconv" + +// ... +) +``` + +Then modify the `OnRecvIbcPostPacket` keeper function with the following code: + +```go +package keeper + +// ... + +func (k Keeper) OnRecvIbcPostPacket(ctx sdk.Context, packet channeltypes.Packet, data types.IbcPostPacketData) (packetAck types.IbcPostPacketAck, err error) { + // validate packet data upon receiving + if err := data.ValidateBasic(); err != nil { + return packetAck, err + } + + id := k.AppendPost( + ctx, + types.Post{ + Creator: packet.SourcePort + "-" + packet.SourceChannel + "-" + data.Creator, + Title: data.Title, + Content: data.Content, + }, + ) + + packetAck.PostID = strconv.FormatUint(id, 10) + + return packetAck, nil +} +``` + +### Receive the post acknowledgement + +On the sending blockchain, store a `sentPost` so you know that the post has been +received on the target chain. + +Store the title and the target to identify the post. + +When a packet is scaffolded, the default type for the received acknowledgment +data is a type that identifies if the packet treatment has failed. The +`Acknowledgement_Error` type is set if `OnRecvIbcPostPacket` returns an error +from the packet. + +```go title="x/blog/keeper/ibc_post.go" +package keeper + +// ... + +// x/blog/keeper/ibc_post.go +func (k Keeper) OnAcknowledgementIbcPostPacket(ctx sdk.Context, packet channeltypes.Packet, data types.IbcPostPacketData, ack channeltypes.Acknowledgement) error { + switch dispatchedAck := ack.Response.(type) { + case *channeltypes.Acknowledgement_Error: + // We will not treat acknowledgment error in this tutorial + return nil + case *channeltypes.Acknowledgement_Result: + // Decode the packet acknowledgment + var packetAck types.IbcPostPacketAck + + if err := types.ModuleCdc.UnmarshalJSON(dispatchedAck.Result, &packetAck); err != nil { + // The counter-party module doesn't implement the correct acknowledgment format + return errors.New("cannot unmarshal acknowledgment") + } + + k.AppendSentPost( + ctx, + types.SentPost{ + Creator: data.Creator, + PostID: packetAck.PostID, + Title: data.Title, + Chain: packet.DestinationPort + "-" + packet.DestinationChannel, + }, + ) + + return nil + default: + return errors.New("the counter-party module does not implement the correct acknowledgment format") + } +} +``` + +### Store information about the timed-out packet + +Store posts that have not been received by target chains in `timedoutPost` +posts. This logic follows the same format as `sentPost`. + +```go title="x/blog/keeper/ibc_post.go" +func (k Keeper) OnTimeoutIbcPostPacket(ctx sdk.Context, packet channeltypes.Packet, data types.IbcPostPacketData) error { + k.AppendTimedoutPost( + ctx, + types.TimedoutPost{ + Creator: data.Creator, + Title: data.Title, + Chain: packet.DestinationPort + "-" + packet.DestinationChannel, + }, + ) + + return nil +} + +``` + +This last step completes the basic `blog` module setup. The blockchain is now +ready! + +## Use the IBC modules + +You can now spin up the blockchain and send a blog post from one blockchain app +to the other. Multiple terminal windows are required to complete these next +steps. + +### Test the IBC modules + +To test the IBC module, start two blockchain networks on the same machine. Both +blockchains use the same source code. Each blockchain has a unique chain ID. + +One blockchain is named `earth` and the other blockchain is named `mars`. + +The `earth.yml` and `mars.yml` files are required in the project directory: + +```yaml title="earth.yml" +version: 1 +build: + proto: + path: proto + third_party_paths: + - third_party/proto + - proto_vendor +accounts: +- name: alice + coins: + - 1000token + - 100000000stake +- name: bob + coins: + - 500token + - 100000000stake +faucet: + name: bob + coins: + - 5token + - 100000stake + host: 0.0.0.0:4500 +genesis: + chain_id: earth +validators: +- name: alice + bonded: 100000000stake + home: $HOME/.earth +``` + +```yaml title="mars.yml" +version: 1 +build: + proto: + path: proto + third_party_paths: + - third_party/proto + - proto_vendor +accounts: +- name: alice + coins: + - 1000token + - 1000000000stake +- name: bob + coins: + - 500token + - 100000000stake +faucet: + name: bob + coins: + - 5token + - 100000stake + host: :4501 +genesis: + chain_id: mars +validators: +- name: alice + bonded: 100000000stake + app: + api: + address: :1318 + grpc: + address: :9092 + grpc-web: + address: :9093 + config: + p2p: + laddr: :26658 + rpc: + laddr: :26659 + pprof_laddr: :6061 + home: $HOME/.mars +``` + +Open a terminal window and run the following command to start the `earth` +blockchain: + +```bash +ignite chain serve -c earth.yml +``` + +Open a different terminal window and run the following command to start the +`mars` blockchain: + +```bash +ignite chain serve -c mars.yml +``` + +### Remove Existing Relayer and Ignite CLI Configurations + +If you previously used the relayer, follow these steps to remove exiting relayer +and Ignite CLI configurations: + +- Stop your blockchains and delete previous configuration files: + + ```bash + rm -rf ~/.ignite/relayer + ``` + +If existing relayer configurations do not exist, the command returns `no matches +found` and no action is taken. + +### Configure and start the relayer + +First, configure the relayer. Use the Ignite CLI `configure` command with the +`--advanced` option: + +```bash +ignite relayer configure -a \ + --source-rpc "http://0.0.0.0:26657" \ + --source-faucet "http://0.0.0.0:4500" \ + --source-port "blog" \ + --source-version "blog-1" \ + --source-gasprice "0.0000025stake" \ + --source-prefix "cosmos" \ + --source-gaslimit 300000 \ + --target-rpc "http://0.0.0.0:26659" \ + --target-faucet "http://0.0.0.0:4501" \ + --target-port "blog" \ + --target-version "blog-1" \ + --target-gasprice "0.0000025stake" \ + --target-prefix "cosmos" \ + --target-gaslimit 300000 +``` + +When prompted, press Enter to accept the default values for `Source Account` and +`Target Account`. + +The output looks like: + +``` +--------------------------------------------- +Setting up chains +--------------------------------------------- + +🔐 Account on "source" is "cosmos1xcxgzq75yrxzd0tu2kwmwajv7j550dkj7m00za" + + |· received coins from a faucet + |· (balance: 100000stake,5token) + +🔐 Account on "target" is "cosmos1nxg8e4mfp5v7sea6ez23a65rvy0j59kayqr8cx" + + |· received coins from a faucet + |· (balance: 100000stake,5token) + +⛓ Configured chains: earth-mars +``` + +In a new terminal window, start the relayer process: + +```bash +ignite relayer connect +``` + +Results: + +``` +------ +Paths +------ + +earth-mars: + earth > (port: blog) (channel: channel-0) + mars > (port: blog) (channel: channel-0) + +------ +Listening and relaying packets between chains... +------ +``` + +### Send packets + +You can now send packets and verify the received posts: + +```bash +planetd tx blog send-ibc-post blog channel-0 "Hello" "Hello Mars, I'm Alice from Earth" --from alice --chain-id earth --home ~/.earth +``` + +To verify that the post has been received on Mars: + +```bash +planetd q blog list-post --node tcp://localhost:26659 +``` + +The packet has been received: + +```yaml +Post: + - content: Hello Mars, I'm Alice from Earth + creator: blog-channel-0-cosmos1aew8dk9cs3uzzgeldatgzvm5ca2k4m98xhy20x + id: "0" + title: Hello +pagination: + next_key: null + total: "1" +``` + +To check if the packet has been acknowledged on Earth: + +```bash +planetd q blog list-sent-post +``` + +Output: + +```yaml +SentPost: + - chain: blog-channel-0 + creator: cosmos1aew8dk9cs3uzzgeldatgzvm5ca2k4m98xhy20x + id: "0" + postID: "0" + title: Hello +pagination: + next_key: null + total: "1" +``` + +To test timeout, set the timeout time of a packet to 1 nanosecond, verify that +the packet is timed out, and check the timed-out posts: + +```bash +planetd tx blog send-ibc-post blog channel-0 "Sorry" "Sorry Mars, you will never see this post" --from alice --chain-id earth --home ~/.earth --packet-timeout-timestamp 1 +``` + +Check the timed-out posts: + +```bash +planetd q blog list-timedout-post +``` + +Results: + +```yaml +TimedoutPost: + - chain: blog-channel-0 + creator: cosmos1fhpcsxn0g8uask73xpcgwxlfxtuunn3ey5ptjv + id: "0" + title: Sorry +pagination: + next_key: null + total: "2" +``` + +You can also send a post from Mars: + +```bash +planetd tx blog send-ibc-post blog channel-0 "Hello" "Hello Earth, I'm Alice from Mars" --from alice --chain-id mars --home ~/.mars --node tcp://localhost:26659 +``` + +List post on Earth: + +```bash +planetd q blog list-post +``` + +Results: + +```yaml +Post: + - content: Hello Earth, I'm Alice from Mars + creator: blog-channel-0-cosmos1xtpx43l826348s59au24p22pxg6q248638q2tf + id: "0" + title: Hello +pagination: + next_key: null + total: "1" +``` + +## Congratulations 🎉 + +By completing this tutorial, you've learned to use the Inter-Blockchain +Communication protocol (IBC). + +Here's what you accomplished in this tutorial: + +- Built two Hello blockchain apps as IBC modules +- Modified the generated code to add CRUD action logic +- Configured and used the Ignite CLI relayer to connect two blockchains with + each other +- Transferred IBC packets from one blockchain to another diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/07-interchange/00-introduction.md b/docs/versioned_docs/version-v0.27.1/02-guide/07-interchange/00-introduction.md new file mode 100644 index 0000000000..3b553768e9 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/07-interchange/00-introduction.md @@ -0,0 +1,49 @@ +--- +sidebar_position: 0 +slug: /guide/interchange +--- + +# Introduction + +The Interchain Exchange is a module to create buy and sell orders between blockchains. + +In this tutorial, you learn how to create a Cosmos SDK module that can create order pairs, buy orders, and sell orders. +You create order books and buy and sell orders across blockchains, which in turn enables you to swap token from one +blockchain to another. + +**Note:** The code in this tutorial is written specifically for this tutorial and is intended only for educational +purposes. This tutorial code is not intended to be used in production. + +If you want to see the end result, see the example implementation in +the [interchange repo](https://github.com/tendermint/interchange). + +**You will learn how to:** + +- Create a blockchain with Ignite CLI +- Create a Cosmos SDK IBC module +- Create an order book that hosts buy and sell orders with a module +- Send IBC packets from one blockchain to another +- Deal with timeouts and acknowledgements of IBC packets + +## How the Interchange Exchange Module Works + +To build an exchange that works with two or more blockchains, follow the steps in this tutorial to create a Cosmos SDK +module called `dex`. + +The new `dex` module allows you to open an exchange order book for a pair of token: a token from one blockchain and a token +on another blockchain. The blockchains are required to have the `dex` module available. + +Token can be bought or sold with limit orders on a simple order book. In this tutorial, there is no notion of a +liquidity pool or automated market maker (AMM). + +The market is unidirectional: + +- The token sold on the source chain cannot be bought back as it is +- The token bought from the target chain cannot be sold back using the same pair. + +If a token on a source chain is sold, it can only be bought back by creating a new pair on the order book. +This workflow is due to the nature of the Inter-Blockchain Communication protocol (IBC) which creates a `voucher` +token on the target blockchain. There is a difference of a native blockchain token and a `voucher` token that is minted +on another blockchain. You must create a second order book pair in order to receive the native token back. + +In the next chapter, you learn details about the design of the interblockchain exchange. diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/07-interchange/01-design.md b/docs/versioned_docs/version-v0.27.1/02-guide/07-interchange/01-design.md new file mode 100644 index 0000000000..87727c39aa --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/07-interchange/01-design.md @@ -0,0 +1,108 @@ +--- +sidebar_position: 1 +description: Learn about the interchain exchange module design. +--- + +# App Design + +In this chapter, you learn how the interchain exchange module is designed. The module has order books, buy orders, and +sell orders. + +- First, create an order book for a pair of token. +- After an order book exists, you can create buy and sell orders for this pair of token. + +The module uses the Inter-Blockchain Communication +protocol [IBC](https://github.com/cosmos/ibc/blob/old/ibc/2_IBC_ARCHITECTURE.md). +By using IBC, the module can create order books so that multiple blockchains can interact and exchange their token. + +You create an order book pair with a token from one blockchain and another token from another blockchain. In this +tutorial, call the module you create the `dex` module. + +> When a user exchanges a token with the `dex` module, a `voucher` of that token is received on the other blockchain. +> This voucher is similar to how an `ibc-transfer` is constructed. Since a blockchain module does not have the rights +> to mint new token of a blockchain into existence, the token on the target chain is locked up, and the buyer receives +> a `voucher` of that token. + +This process can be reversed when the `voucher` gets burned to unlock the original token. This exchange process is +explained in more detail throughout the tutorial. + +## Assumption of the Design + +An order book can be created for the exchange of any tokens between any pair of chains. + +- Both blockchains require the `dex` module to be installed and running. +- There can only be one order book for a pair of token at the same time. + + + +A specific chain cannot mint new coins of its native token. + + + +This module is inspired by the [`ibc transfer`](https://github.com/cosmos/ibc-go/tree/main/modules/apps/transfer) +module on the Cosmos SDK. The `dex` module you create in this tutorial has similarities, like the `voucher` creation. + +However, the new `dex` module you are creating is more complex because it supports creation of: + +- Several types of packets to send +- Several types of acknowledgments to treat +- More complex logic on how to treat a packet on receipt, on timeout, and more + +## Interchain Exchange Overview + +Assume you have two blockchains: Venus and Mars. + +- The native token on Venus is `venuscoin`. +- The native token on Mars is `marscoin`. + +When a token is exchanged from Mars to Venus: + +- The Venus blockchain has an IBC `voucher` token with a denom that looks like `ibc/B5CB286...A7B21307F`. +- The long string of characters after `ibc/` is a denom trace hash of a token that was transferred using IBC. + +Using the blockchain's API you can get a denom trace from that hash. The denom trace consists of a `base_denom` and a +`path`. In our example: + +- The `base_denom` is `marscoin`. +- The `path` contains pairs of ports and channels through which the token has been transferred. + +For a single-hop transfer, the `path` is identified by `transfer/channel-0`. + +Learn more about token paths +in [ICS 20 Fungible Token Transfer](https://github.com/cosmos/ibc/tree/main/spec/app/ics-020-fungible-token-transfer). + +**Note:** This token `ibc/Venus/marscoin` cannot be sold back using the same order book. If you want to "reverse" the +exchange and receive the Mars token back, you must create and use a new order book for the `ibc/Venus/marscoin` to +`marscoin` transfer. + +## The Design of the Order Books + +As a typical exchange, a new pair implies the creation of an order book with orders to sell `marscoin` or orders to buy +`venuscoin`. Here, you have two chains and this data structure must be split between Mars and Venus. + +- Users from chain Mars sell `marscoin`. +- Users from chain Venus buy `marscoin`. + +Therefore, we represent: + +- All orders to sell `marscoin` on chain Mars. +- All orders to buy `marscoin` on chain Venus. + +In this example, blockchain Mars holds the sell orders and blockchain Venus holds the buy orders. + +## Exchanging Tokens Back + +Like `ibc-transfer`, each blockchain keeps a trace of the token voucher that was created on the other blockchain. + +If blockchain Mars sells `marscoin` to chain Venus and `ibc/Venus/marscoin` is minted on Venus then, if +`ibc/Venus/marscoin` is sold back to Mars, the token is unlocked and the token that is received is `marscoin`. + +## Features + +The features supported by the interchain exchange module are: + +- Create an exchange order book for a token pair between two chains +- Send sell orders on source chain +- Send buy orders on target chain +- Cancel sell or buy orders diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/07-interchange/02-init.md b/docs/versioned_docs/version-v0.27.1/02-guide/07-interchange/02-init.md new file mode 100644 index 0000000000..a8c96755f2 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/07-interchange/02-init.md @@ -0,0 +1,219 @@ +--- +sidebar_position: 2 +description: Create the blockchain for the interchain exchange app. +--- + +# App Init + +## Initialize the Blockchain + +In this chapter, you create the basic blockchain module for the interchain exchange app. You scaffold the blockchain, +the module, the transaction, the IBC packets, and messages. In later chapters, you integrate more code into each of the +transaction handlers. + +## Create the Blockchain + +Scaffold a new blockchain called `interchange`: + +```bash +ignite scaffold chain interchange --no-module +``` + +A new directory named `interchange` is created. + +Change into this directory where you can scaffold modules, types, and maps: + +```bash +cd interchange +``` + +The `interchange` directory contains a working blockchain app. + +A local GitHub repository has been created for you with the initial scaffold. + +Next, create a new IBC module. + +## Create the dex Module + +Scaffold a module inside your blockchain named `dex` with IBC capabilities. + +The dex module contains the logic to create and maintain order books and route them through IBC to the second +blockchain. + +```bash +ignite scaffold module dex --ibc --ordering unordered --dep bank +``` + +## Create CRUD logic for Buy and Sell Order Books + +Scaffold two types with create, read, update, and delete (CRUD) actions. + +Run the following Ignite CLI `type` commands to create `sellOrderBook` and `buyOrderBook` types: + +```bash +ignite scaffold map sell-order-book amountDenom priceDenom --no-message --module dex +ignite scaffold map buy-order-book amountDenom priceDenom --no-message --module dex +``` + +The values are: + +- `amountDenom`: the token to be sold and in which quantity +- `priceDenom`: the token selling price + +The `--no-message` flag specifies to skip the message creation. Custom messages will be created in the next steps. + +The `--module dex` flag specifies to scaffold the type in the `dex` module. + +## Create the IBC Packets + +Create three packets for IBC: + +- An order book pair `createPair` +- A sell order `sellOrder` +- A buy order `buyOrder` + +```bash +ignite scaffold packet create-pair sourceDenom targetDenom --module dex +ignite scaffold packet sell-order amountDenom amount:int priceDenom price:int --ack remainingAmount:int,gain:int --module dex +ignite scaffold packet buy-order amountDenom amount:int priceDenom price:int --ack remainingAmount:int,purchase:int --module dex +``` + +The optional `--ack` flag defines field names and types of the acknowledgment returned after the packet has been +received by the target chain. The value of the `--ack` flag is a comma-separated list of names (no spaces). Append +optional types after a colon (`:`). + +## Cancel messages + +Cancelling orders is done locally in the network, there is no packet to send. + +Use the `message` command to create a message to cancel a sell or buy order: + +```bash +ignite scaffold message cancel-sell-order port channel amountDenom priceDenom orderID:int --desc "Cancel a sell order" --module dex +ignite scaffold message cancel-buy-order port channel amountDenom priceDenom orderID:int --desc "Cancel a buy order" --module dex +``` + +Use the optional `--desc` flag to define a description of the CLI command that is used to broadcast a transaction with +the message. + +## Trace the Denom + +The token denoms must have the same behavior as described in the `ibc-transfer` module: + +- An external token received from a chain has a unique `denom`, referred to as `voucher`. +- When a token is sent to a blockchain and then sent back and received, the chain can resolve the voucher and convert + it back to the original token denomination. + +`Voucher` tokens are represented as hashes, therefore you must store which original denomination is related to a +voucher. +You can do this with an indexed type. + +For a `voucher` you store, define the source port ID, source channel ID, and the original denom: + +```bash +ignite scaffold map denom-trace port channel origin --no-message --module dex +``` + +## Create the Configuration for Two Blockchains + +Add two config files `mars.yml` and `venus.yml` to test two blockchain networks with specific token for each. + +Add the config files in the `interchange` folder. + +The native denoms for Mars are `marscoin`, and for Venus `venuscoin`. + +Create the `mars.yml` file with your content: + +```yaml title="mars.yml" +version: 1 +build: + proto: + path: proto + third_party_paths: + - third_party/proto + - proto_vendor +accounts: +- name: alice + coins: + - 1000token + - 100000000stake + - 1000marscoin +- name: bob + coins: + - 500token + - 1000marscoin + - 100000000stake +faucet: + name: bob + coins: + - 5token + - 100000stake + host: 0.0.0.0:4500 +genesis: + chain_id: mars +validators: +- name: alice + bonded: 100000000stake + home: $HOME/.mars +``` + +Create the `venus.yml` file with your content: + +```yaml title="venus.yml" +version: 1 +build: + proto: + path: proto + third_party_paths: + - third_party/proto + - proto_vendor +accounts: +- name: alice + coins: + - 1000token + - 1000000000stake + - 1000venuscoin +- name: bob + coins: + - 500token + - 1000venuscoin + - 100000000stake +faucet: + name: bob + coins: + - 5token + - 100000stake + host: :4501 +genesis: + chain_id: venus +validators: +- name: alice + bonded: 100000000stake + app: + api: + address: :1318 + grpc: + address: :9092 + grpc-web: + address: :9093 + config: + p2p: + laddr: :26658 + rpc: + laddr: :26659 + pprof_laddr: :6061 + home: $HOME/.venus +``` + +In order to run two blockchains side by side on a single machine, you need to +start them on different ports. `venus.yml` has a validators configuration that +stars services HTTP API, gRPC, P2P and RPC services on custom ports. + +After scaffolding, now is a good time to make a commit to the local GitHub repository that was created for you. + +```bash +git add . +git commit -m "Scaffold module, maps, packages and messages for the dex" +``` + +Implement the code for the order book in the next chapter. diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/07-interchange/03-walkthrough.md b/docs/versioned_docs/version-v0.27.1/02-guide/07-interchange/03-walkthrough.md new file mode 100644 index 0000000000..131dbba605 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/07-interchange/03-walkthrough.md @@ -0,0 +1,687 @@ +--- +sidebar_position: 3 +description: Walkthrough of commands to use the interchain exchange module. +--- + +# Use the Interchain Exchange + +In this chapter, you will learn about the exchange and how it will function once +it is implemented. This will give you a better understanding of what you will be +building in the coming chapters. + +To achieve this, we will perform the following tasks: + +* Start two local blockchains +* Set up an IBC relayer between the two chains +* Create an exchange order book for a token pair on the two chains +* Submit sell orders on the Mars chain +* Submit buy orders on the Venus chain +* Cancel sell or buy orders + +Starting the two local blockchains and setting up the IBC relayer will allow us +to create an exchange order book between the two chains. This order book will +allow us to submit sell and buy orders, as well as cancel any orders that we no +longer want to maintain. + +It is important to note that the commands in this chapter will only work +properly if you have completed all the following chapters in this tutorial. By +the end of this chapter, you should have a good understanding of how the +exchange will operate. + +## Start blockchain nodes + +To start using the interchain exchange, you will need to start two separate +blockchains. This can be done by running the `ignite chain serve` command, +followed by the `-c` flag and the path to the configuration file for each +blockchain. For example, to start the `mars` blockchain, you would run: + +``` +ignite chain serve -c mars.yml +``` + +To start the `venus` blockchain, you would run a similar command, but with the +path to the `venus.yml` configuration file: + +``` +ignite chain serve -c venus.yml +``` + +Once both blockchains are running, you can proceed with configuring the relayer +to enable interchain exchange between the two chains. + +## Relayer + +Next, let's set up an IBC relayer between two chains. If you have used a relayer +in the past, reset the relayer configuration directory: + +``` +rm -rf ~/.ignite/relayer +``` + +Now you can use the `ignite relayer configure` command. This command allows you +to specify the source and target chains, along with their respective RPC +endpoints, faucet URLs, port numbers, versions, gas prices, and gas limits. + +``` +ignite relayer configure -a --source-rpc "http://0.0.0.0:26657" --source-faucet "http://0.0.0.0:4500" --source-port "dex" --source-version "dex-1" --source-gasprice "0.0000025stake" --source-prefix "cosmos" --source-gaslimit 300000 --target-rpc "http://0.0.0.0:26659" --target-faucet "http://0.0.0.0:4501" --target-port "dex" --target-version "dex-1" --target-gasprice "0.0000025stake" --target-prefix "cosmos" --target-gaslimit 300000 +``` + +To create a connection between the two chains, you can use the ignite relayer +connect command. This command will establish a connection between the source and +target chains, allowing you to transfer data and assets between them. + +``` +ignite relayer connect +``` + +Now that we have two separate blockchain networks up and running, and a relayer +connection established to facilitate communication between them, we are ready to +begin using the interchain exchange binary to interact with these networks. This +will allow us to create order books and buy/sell orders, enabling us to trade +assets between the two chains. + +## Order Book + +To create an order book for a pair of tokens, you can use the following command: + +``` +interchanged tx dex send-create-pair dex channel-0 marscoin venuscoin --from alice --chain-id mars --home ~/.mars +``` + +This command will create an order book for the pair of tokens `marscoin` and +`venuscoin`. The command will be executed by the user `alice` on the Mars +blockchain. The `--home` parameter specifies the location of the configuration +directory for the Mars blockchain. + +Creating an order book affects state on the Mars blockchain to which the +transaction was broadcast and the Venus blockchain. + +On the Mars blockchain, the `send-create-pair` command creates an empty sell +order book. + +``` +interchanged q dex list-sell-order-book +``` + +```yml +sellOrderBook: +- amountDenom: marscoin + book: + idCount: 0 + orders: [] + index: dex-channel-0-marscoin-venuscoin + priceDenom: venuscoin +``` + +On the Venus blockchain, the same `send-createPair` command creates a buy order +book: + +``` +interchanged q dex list-buy-order-book --node tcp://localhost:26659 +``` + +```yml +buyOrderBook: +- amountDenom: marscoin + book: + idCount: 0 + orders: [] + index: dex-channel-0-marscoin-venuscoin + priceDenom: venuscoin +``` + +In the `create-pair` command on the Mars blockchain, an IBC packet is sent to +the Venus chain. This packet contains information that is used to create a buy +order book on the Venus chain. + +When the Venus chain receives the IBC packet, it processes the information +contained in the packet and creates a buy order book. The Venus chain then sends +an acknowledgement back to the Mars chain to confirm that the buy order book has +been successfully created. + +Upon receiving the acknowledgement from the Venus chain, the Mars chain creates +a sell order book. This sell order book is associated with the buy order book on +the Venus chain, allowing users to trade assets between the two chains. + +## Sell Order + +After creating an order book, the next step is to create a sell order. This can +be done using the `send-sell-order` command, which is used to broadcast a +transaction with a message that locks a specified amount of tokens and creates a +sell order on the Mars blockchain. + +``` +interchanged tx dex send-sell-order dex channel-0 marscoin 10 venuscoin 15 --from alice --chain-id mars --home ~/.mars +``` + +In the example provided, the `send-sell-order` command is used to create a sell +order for 10 `marscoin` token and 15 `venuscoin` token. This sell order will be +added to the order book on the Mars blockchain. + +``` +interchanged q bank balances $(interchanged keys show -a alice --home ~/.mars) +``` + +```yml +balances: +- amount: "990" # decreased from 1000 + denom: marscoin +- amount: "1000" + denom: token +``` + +``` +interchanged q dex list-sell-order-book +``` + +```yml +sellOrderBook: +- amountDenom: marscoin + book: + idCount: 1 + orders: # a new sell order is created + - amount: 10 + creator: cosmos14ntyzr6d2dx4ppds9tvenx53fn0xl5jcakrtm4 + id: 0 + price: 15 + index: dex-channel-0-marscoin-venuscoin + priceDenom: venuscoin +``` + +## Buy order + +After creating a sell order, the next step in the trading process is typically +to create a buy order. This can be done using the `send-buy-order` command, +which is used to lock a specified amount of tokens and create a buy order on the +Venus blockchain + +``` +interchanged tx dex send-buy-order dex channel-0 marscoin 10 venuscoin 5 --from alice --chain-id venus --home ~/.venus --node tcp://localhost:26659 +``` + +In the example provided, the `send-buy-order` command is used to create a buy +order for 10 `marscoin` token and 5 `venuscoin` token. This buy order will be +added to the order book on the Venus blockchain. + +``` +interchanged q bank balances $(interchanged keys show -a alice --home ~/.venus) --node tcp://localhost:26659 +``` + +```yml +balances: +- amount: "900000000" + denom: stake +- amount: "1000" + denom: token +- amount: "950" # decreased from 1000 + denom: venuscoin +``` + +``` +interchanged q dex list-buy-order-book --node tcp://localhost:26659 +``` + +```yml +buyOrderBook: +- amountDenom: marscoin + book: + idCount: 1 + orders: # a new buy order is created + - amount: 10 + creator: cosmos1mrrttwtdcp47pl4hq6sar3mwqpmtc7pcl9e6ss + id: 0 + price: 5 + index: dex-channel-0-marscoin-venuscoin + priceDenom: venuscoin +``` + +## Perform an Exchange with a Sell Order + +You currently have two open orders for `marscoin`: + +* A sell order on the Mars chain, where you are offering to sell 10 `marscoin` + for 15 `venuscoin`. +* A buy order on the Venus chain, where you are willing to buy 5 `marscoin` for + 5 `venuscoin`. + +To perform an exchange, you can send a sell order to the Mars chain using the +following command: + +``` +interchanged tx dex send-sell-order dex channel-0 marscoin 5 venuscoin 3 --from alice --home ~/.mars +``` + +This sell order, offering to sell 5 `marscoin` for 3 `venuscoin`, will be filled +on the Venus chain by the existing buy order. This will result in the amount of +the buy order on the Venus chain being reduced by 5 `marscoin`. + +``` +interchanged q dex list-buy-order-book --node tcp://localhost:26659 +``` + +```yml +buyOrderBook: +- amountDenom: marscoin + book: + idCount: 1 + orders: + - amount: 5 # decreased from 10 + creator: cosmos1mrrttwtdcp47pl4hq6sar3mwqpmtc7pcl9e6ss + id: 0 + price: 5 + index: dex-channel-0-marscoin-venuscoin + priceDenom: venuscoin +``` + +The sender of the filled sell order traded 5 `marscoin` for 25 `venuscoin` +tokens. This means that the amount of the sell order (5 `marscoin`) was +multiplied by the price of the buy order (5 `venuscoin`) to determine the value +of the exchange. In this case, the value of the exchange was 25 `venuscoin` +vouchers. + +``` +interchanged q bank balances $(interchanged keys show -a alice --home ~/.mars) +``` + +```yml +balances: +- amount: "25" # increased from 0 + denom: ibc/BB38C24E9877 +- amount: "985" # decreased from 990 + denom: marscoin +- amount: "1000" + denom: token +``` + +The counterparty, or the sender of the buy `marscoin` order, will receive 5 +`marscoin` as a result of the exchange. + +``` +interchanged q bank balances $(interchanged keys show -a alice --home ~/.venus) --node tcp://localhost:26659 +``` + +```yml +balances: +- amount: "5" # increased from 0 + denom: ibc/745B473BFE24 # marscoin voucher +- amount: "900000000" + denom: stake +- amount: "1000" + denom: token +- amount: "950" + denom: venuscoin +``` + +The `venuscoin` balance has remained unchanged because the appropriate amount of +`venuscoin` (50) was already locked at the time the buy order was created in the +previous step. + + +## Perform an Exchange with a Buy Order + +To perform an exchange with a buy order, send a transaction to the decentralized +exchange to buy 5 `marscoin` for 15 `venuscoin`. This is done by running the +following command: + +``` +interchanged tx dex send-buy-order dex channel-0 marscoin 5 venuscoin 15 --from alice --home ~/.venus --node tcp://localhost:26659 +``` + +This buy order will be immediately filled on the Mars chain, and the creator of +the sell order will receive 75 `venuscoin` vouchers as payment. + +``` +interchanged q bank balances $(interchanged keys show -a alice --home ~/.mars) +``` + +```yml +balances: +- amount: "100" # increased from 25 + denom: ibc/BB38C24E9877 # venuscoin voucher +- amount: "985" + denom: marscoin +- amount: "1000" + denom: token +``` + +The amount of the sell order will be decreased by the amount of the filled buy +order, so in this case it will be decreased by 5 `marscoin`. + +``` +interchanged q dex list-sell-order-book +``` + +```yml +sellOrderBook: +- amountDenom: marscoin + book: + idCount: 1 + orders: + - amount: 5 # decreased from 10 + creator: cosmos14ntyzr6d2dx4ppds9tvenx53fn0xl5jcakrtm4 + id: 0 + price: 15 + index: dex-channel-0-marscoin-venuscoin + priceDenom: venuscoin +``` + +The creator of the buy order receives 5 marscoin vouchers for 75 venuscoin +(5marscoin * 15venuscoin): + +``` +interchanged q bank balances $(interchanged keys show -a alice --home ~/.venus) --node tcp://localhost:26659 +``` + +```yml +balances: +- amount: "10" # increased from 5 + denom: ibc/745B473BFE24 # marscoin vouchers +- amount: "900000000" + denom: stake +- amount: "1000" + denom: token +- amount: "875" # decreased from 950 + denom: venuscoin +``` + +## Complete Exchange with a Partially Filled Sell Order + +To complete the exchange with a partially filled sell order, send a transaction +to the decentralized exchange to sell 10 `marscoin` for 3 `venuscoin`. This is +done by running the following command: + +``` +interchanged tx dex send-sell-order dex channel-0 marscoin 10 venuscoin 3 --from alice --home ~/.mars +``` + +In this scenario, the sell amount is 10 `marscoin`, but there is an existing buy +order for only 5 `marscoin`. The buy order will be filled completely and removed +from the order book. The author of the previously created buy order will receive +10 `marscoin` vouchers from the exchange. + +To check the balances, she can run the following command: + +``` +interchanged q bank balances $(interchanged keys show -a alice --home ~/.venus) --node tcp://localhost:26659 +``` + +```yml +balances: +- amount: "15" # increased from 5 + denom: ibc/745B473BFE24 # marscoin voucher +- amount: "900000000" + denom: stake +- amount: "1000" + denom: token +- amount: "875" + denom: venuscoin +``` + +``` +interchanged q dex list-buy-order-book --node tcp://localhost:26659 +``` + +```yml +buyOrderBook: +- amountDenom: marscoin + book: + idCount: 1 + orders: [] # buy order with amount 5marscoin has been closed + index: dex-channel-0-marscoin-venuscoin + priceDenom: venuscoin +``` + +``` +interchanged q bank balances $(interchanged keys show -a alice --home ~/.mars) +``` + +The author of the sell order successfuly exchanged 5 marscoin and received 25 +venuscoin vouchers. The other 5marscoin created a sell order: + +```yml +balances: +- amount: "125" # increased from 100 + denom: ibc/BB38C24E9877 # venuscoin vouchers +- amount: "975" # decreased from 985 + denom: marscoin +- amount: "1000" + denom: token +``` + +``` +interchanged q dex list-sell-order-book +``` + +```yml +sellOrderBook: +- amountDenom: marscoin + book: + idCount: 2 + orders: + - amount: 5 # hasn't changed + creator: cosmos14ntyzr6d2dx4ppds9tvenx53fn0xl5jcakrtm4 + id: 0 + price: 15 + - amount: 5 # new order is created + creator: cosmos14ntyzr6d2dx4ppds9tvenx53fn0xl5jcakrtm4 + id: 1 + price: 3 + index: dex-channel-0-marscoin-venuscoin + priceDenom: venuscoin +``` + +## Complete Exchange with a Partially Filled Buy Order + +To complete the exchange with a partially filled buy order, send a transaction +to the decentralized exchange to buy 10 `marscoin` for 5 `venuscoin`. This is +done by running the following command: + +``` +interchanged tx dex send-buy-order dex channel-0 marscoin 10 venuscoin 5 --from alice --home ~/.venus --node tcp://localhost:26659 +``` + +In this scenario, the buy order is only partially filled for 5 `marscoin`. There +is an existing sell order for 5 `marscoin` (with a price of 3 `venuscoin`) on +the Mars chain, which is completely filled and removed from the order book. The +author of the closed sell order will receive 15 `venuscoin` vouchers as payment, +which is the product of 5 `marscoin` and 3 `venuscoin`. + +``` +interchanged q bank balances $(interchanged keys show -a alice --home ~/.mars) +``` + +```yml +balances: +- amount: "140" # increased from 125 + denom: ibc/BB38C24E9877 # venuscoin vouchers +- amount: "975" + denom: marscoin +- amount: "1000" + denom: token +``` + +``` +interchanged q dex list-sell-order-book +``` + +```yml +sellOrderBook: +- amountDenom: marscoin + book: + idCount: 2 + orders: + - amount: 5 # order hasn't changed + creator: cosmos14ntyzr6d2dx4ppds9tvenx53fn0xl5jcakrtm4 + id: 0 + price: 15 + # a sell order for 5 marscoin has been closed + index: dex-channel-0-marscoin-venuscoin + priceDenom: venuscoin +``` + +In this scenario, the author of the buy order will receive 5 `marscoin` vouchers +as payment, which locks up 50 `venuscoin` of their token. The remaining 5 +`marscoin` that is not filled by the sell order will create a new buy order on +the Venus chain. This means that the author of the buy order is still interested +in purchasing 5 `marscoin`, and is willing to pay the specified price for it. +The new buy order will remain on the order book until it is filled by another +sell order, or it is cancelled by the buyer. + +``` +interchanged q bank balances $(interchanged keys show -a alice --home ~/.venus) --node tcp://localhost:26659 +``` + +```yml +balances: +- amount: "20" # increased from 15 + denom: ibc/745B473BFE24 # marscoin vouchers +- amount: "900000000" + denom: stake +- amount: "1000" + denom: token +- amount: "825" # decreased from 875 + denom: venuscoin +``` + +``` +interchanged q dex list-buy-order-book --node tcp://localhost:26659 +``` + +```yml +buyOrderBook: +- amountDenom: marscoin + book: + idCount: 2 + orders: + - amount: 5 # new buy order is created + creator: cosmos1mrrttwtdcp47pl4hq6sar3mwqpmtc7pcl9e6ss + id: 1 + price: 5 + index: dex-channel-0-marscoin-venuscoin + priceDenom: venuscoin +``` + +## Cancel an Order + +After the exchanges described, there are still two open orders: a sell order on +the Mars chain (5 `marscoin` for 15 `venuscoin`), and a buy order on the Venus +chain (5 `marscoin` for 5 `venuscoin`). + +To cancel an order on a blockchain, you can use the `cancel-sell-order` or +`cancel-buy-order` command, depending on the type of order you want to cancel. +The command takes several arguments, including the `channel-id` of the IBC +connection, the `amount-denom` and `price-denom` of the order, and the +`order-id` of the order you want to cancel. + +To cancel a sell order on the Mars chain, you would run the following command: + +``` +interchanged tx dex cancel-sell-order dex channel-0 marscoin venuscoin 0 --from alice --home ~/.mars +``` + +This will cancel the sell order and remove it from the order book. The balance +of Alice's `marscoin` will be increased by the amount of the cancelled sell +order. + +To check Alice's balances, including her updated `marscoin` balance, run the +following command: + +``` +interchanged q bank balances $(interchanged keys show -a alice --home ~/.mars) +``` + +This will return a list of Alice's balances, including her updated `marscoin` +balance. + +```yml +balances: +- amount: "140" + denom: ibc/BB38C24E9877 +- amount: "980" # increased from 975 + denom: marscoin +- amount: "1000" + denom: token +``` + +After the sell order on the Mars chain has been cancelled, the sell order book +on that blockchain will be empty. This means that there are no longer any active +sell orders on the Mars chain, and anyone interested in purchasing `marscoin` +will need to create a new buy order. The sell order book will remain empty until +a new sell order is created and added to it. + +``` +interchanged q dex list-sell-order-book +``` + +```yml +sellOrderBook: +- amountDenom: marscoin + book: + idCount: 2 + orders: [] + index: dex-channel-0-marscoin-venuscoin + priceDenom: venuscoin +``` + +To cancel a buy order on the `Venus` chain, you can run the following command: + +``` +interchanged tx dex cancel-buy-order dex channel-0 marscoin venuscoin 1 --from alice --home ~/.venus --node tcp://localhost:26659 +``` + +This will cancel the buy order and remove it from the order book. The balance of +Alice's `venuscoin` will be increased by the amount of the cancelled buy order. + +To check Alice's balances, including her updated `venuscoin` balance, you can +run the following command: + +``` +interchanged q bank balances $(interchanged keys show -a alice --home ~/.venus) --node tcp://localhost:26659 +``` + +The amount of `venuscoin` is increased: + +```yml +balances: +- amount: "20" + denom: ibc/745B473BFE24 +- amount: "900000000" + denom: stake +- amount: "1000" + denom: token +- amount: "850" # increased from 825 + denom: venuscoin +``` + +This will return a list of Alice's balances, including her updated `venuscoin` +balance. + +After canceling a buy order, the buy order book on the Venus blockchain will be +empty. This means that there are no longer any active buy orders on the chain, +and anyone interested in selling `marscoin` will need to create a new sell +order. The buy order book will remain empty until a new buy order is created and +added to it. + +``` +interchanged q dex list-buy-order-book --node tcp://localhost:26659 +``` + +```yml +buyOrderBook: +- amountDenom: marscoin + book: + idCount: 2 + orders: [] + index: dex-channel-0-marscoin-venuscoin + priceDenom: venuscoin +``` + +In this walkthrough, we demonstrated how to set up an interchain exchange for +trading tokens between two different blockchain networks. This involved creating +an exchange order book for a specific token pair and establishing a fixed +exchange rate between the two. + +Once the exchange was set up, users could send sell orders on the Mars chain and +buy orders on the Venus chain. This allowed them to offer their tokens for sale +or purchase tokens from the exchange. In addition, users could also cancel their +orders if needed. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/07-interchange/04-creating-order-books.md b/docs/versioned_docs/version-v0.27.1/02-guide/07-interchange/04-creating-order-books.md new file mode 100644 index 0000000000..d95c7580db --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/07-interchange/04-creating-order-books.md @@ -0,0 +1,480 @@ +--- +sidebar_position: 4 +description: Implement logic to create order books. +--- + +# Implement the Order Books + +In this chapter, you implement the logic to create order books. + +In the Cosmos SDK, the state is stored in a key-value store. Each order book is stored under a unique key that is +composed of four values: + +- Port ID +- Channel ID +- Source denom +- Target denom + +For example, an order book for marscoin and venuscoin could be stored under `dex-channel-4-marscoin-venuscoin`. + +First, define a function that returns an order book store key: + +```go +// x/dex/types/keys.go +package types + +import "fmt" + +// ... +func OrderBookIndex(portID string, channelID string, sourceDenom string, targetDenom string) string { + return fmt.Sprintf("%s-%s-%s-%s", portID, channelID, sourceDenom, targetDenom) +} +``` + +The `send-create-pair` command is used to create order books. This command: + +- Creates and broadcasts a transaction with a message of type `SendCreatePair`. +- The message gets routed to the `dex` module. +- Finally, a `SendCreatePair` keeper method is called. + +You need the `send-create-pair` command to do the following: + +- When processing `SendCreatePair` message on the source chain: + - Check that an order book with the given pair of denoms does not yet exist. + - Transmit an IBC packet with information about port, channel, source denoms, and target denoms. +- After the packet is received on the target chain: + - Check that an order book with the given pair of denoms does not yet exist on the target chain. + - Create a new order book for buy orders. + - Transmit an IBC acknowledgement back to the source chain. +- After the acknowledgement is received on the source chain: + - Create a new order book for sell orders. + +## Message Handling in SendCreatePair + +The `SendCreatePair` function was created during the IBC packet scaffolding. The function creates an IBC packet, +populates it with source and target denoms, and transmits this packet over IBC. + +Now, add the logic to check for an existing order book for a particular pair of denoms: + +```go +// x/dex/keeper/msg_server_create_pair.go + +package keeper + +import ( + "errors" + // ... +) + +func (k msgServer) SendCreatePair(goCtx context.Context, msg *types.MsgSendCreatePair) (*types.MsgSendCreatePairResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + // Get an order book index + pairIndex := types.OrderBookIndex(msg.Port, msg.ChannelID, msg.SourceDenom, msg.TargetDenom) + + // If an order book is found, return an error + _, found := k.GetSellOrderBook(ctx, pairIndex) + if found { + return &types.MsgSendCreatePairResponse{}, errors.New("the pair already exist") + } + + // Construct the packet + var packet types.CreatePairPacketData + + packet.SourceDenom = msg.SourceDenom + packet.TargetDenom = msg.TargetDenom + + // Transmit the packet + _, err := k.TransmitCreatePairPacket( + ctx, + packet, + msg.Port, + msg.ChannelID, + clienttypes.ZeroHeight(), + msg.TimeoutTimestamp, + ) + if err != nil { + return nil, err + } + + return &types.MsgSendCreatePairResponse{}, nil +} +``` + +## Lifecycle of an IBC Packet + +During a successful transmission, an IBC packet goes through these stages: + +1. Message processing before packet transmission on the source chain +2. Reception of a packet on the target chain +3. Acknowledgment of a packet on the source chain +4. Timeout of a packet on the source chain + +In the following section, implement the packet reception logic in the `OnRecvCreatePairPacket` function and the packet +acknowledgement logic in the `OnAcknowledgementCreatePairPacket` function. + +Leave the Timeout function empty. + +## Receive an IBC packet + +The protocol buffer definition defines the data that an order book contains. + +Add the `OrderBook` and `Order` messages to the `order.proto` file. + +First, add the proto buffer files to build the Go code files. You can modify these files for the purpose of your app. + +Create a new `order.proto` file in the `proto/interchange/dex` directory and add the content: + +```protobuf +// proto/interchange/dex/order.proto + +syntax = "proto3"; + +package interchange.dex; + +option go_package = "interchange/x/dex/types"; + +message OrderBook { + int32 idCount = 1; + repeated Order orders = 2; +} + +message Order { + int32 id = 1; + string creator = 2; + int32 amount = 3; + int32 price = 4; +} +``` + +Modify the `buy_order_book.proto` file to have the fields for creating a buy order on the order book. +Don't forget to add the import as well. + +**Tip:** Don't forget to add the import as well. + +```protobuf +// proto/interchange/dex/buy_order_book.proto + +// ... + +import "interchange/dex/order.proto"; + +message BuyOrderBook { + // ... + OrderBook book = 4; +} +``` + +Modify the `sell_order_book.proto` file to add the order book into the buy order book. + +The proto definition for the `SellOrderBook` looks like: + +```protobuf +// proto/interchange/dex/sell_order_book.proto + +// ... +import "interchange/dex/order.proto"; + +message SellOrderBook { + // ... + OrderBook book = 4; +} +``` + +Now, use Ignite CLI to build the proto files for the `send-create-pair` command: + +```bash +ignite generate proto-go --yes +``` + +Start enhancing the functions for the IBC packets. + +Create a new file `x/dex/types/order_book.go`. + +Add the new order book function to the corresponding Go file: + +```go +// x/dex/types/order_book.go + +package types + +func NewOrderBook() OrderBook { + return OrderBook{ + IdCount: 0, + } +} +``` + +To create a new buy order book type, define `NewBuyOrderBook` in a new file `x/dex/types/buy_order_book.go` : + +```go +// x/dex/types/buy_order_book.go + +package types + +func NewBuyOrderBook(AmountDenom string, PriceDenom string) BuyOrderBook { + book := NewOrderBook() + return BuyOrderBook{ + AmountDenom: AmountDenom, + PriceDenom: PriceDenom, + Book: &book, + } +} +``` + +When an IBC packet is received on the target chain, the module must check whether a book already exists. If not, then +create a buy order book for the specified denoms. + +```go +// x/dex/keeper/create_pair.go + +package keeper + +// ... + +func (k Keeper) OnRecvCreatePairPacket(ctx sdk.Context, packet channeltypes.Packet, data types.CreatePairPacketData) (packetAck types.CreatePairPacketAck, err error) { + // ... + + // Get an order book index + pairIndex := types.OrderBookIndex(packet.SourcePort, packet.SourceChannel, data.SourceDenom, data.TargetDenom) + + // If an order book is found, return an error + _, found := k.GetBuyOrderBook(ctx, pairIndex) + if found { + return packetAck, errors.New("the pair already exist") + } + + // Create a new buy order book for source and target denoms + book := types.NewBuyOrderBook(data.SourceDenom, data.TargetDenom) + + // Assign order book index + book.Index = pairIndex + + // Save the order book to the store + k.SetBuyOrderBook(ctx, book) + return packetAck, nil +} +``` + +## Receive an IBC Acknowledgement + +When an IBC acknowledgement is received on the source chain, the module must check whether a book already exists. If +not, +create a sell order book for the specified denoms. + +Create a new file `x/dex/types/sell_order_book.go`. +Insert the `NewSellOrderBook` function which creates a new sell order book. + +```go +// x/dex/types/sell_order_book.go + +package types + +func NewSellOrderBook(AmountDenom string, PriceDenom string) SellOrderBook { + book := NewOrderBook() + return SellOrderBook{ + AmountDenom: AmountDenom, + PriceDenom: PriceDenom, + Book: &book, + } +} +``` + +Modify the Acknowledgement function in the `x/dex/keeper/create_pair.go` file: + +```go +// x/dex/keeper/create_pair.go + +package keeper + +// ... + +func (k Keeper) OnAcknowledgementCreatePairPacket(ctx sdk.Context, packet channeltypes.Packet, data types.CreatePairPacketData, ack channeltypes.Acknowledgement) error { + switch dispatchedAck := ack.Response.(type) { + case *channeltypes.Acknowledgement_Error: + return nil + case *channeltypes.Acknowledgement_Result: + // Decode the packet acknowledgment + var packetAck types.CreatePairPacketAck + if err := types.ModuleCdc.UnmarshalJSON(dispatchedAck.Result, &packetAck); err != nil { + // The counter-party module doesn't implement the correct acknowledgment format + return errors.New("cannot unmarshal acknowledgment") + } + + // Set the sell order book + pairIndex := types.OrderBookIndex(packet.SourcePort, packet.SourceChannel, data.SourceDenom, data.TargetDenom) + book := types.NewSellOrderBook(data.SourceDenom, data.TargetDenom) + book.Index = pairIndex + k.SetSellOrderBook(ctx, book) + + return nil + default: + // The counter-party module doesn't implement the correct acknowledgment format + return errors.New("invalid acknowledgment format") + } +} +``` + +In this section, you implemented the logic behind the new `send-create-pair` command: + +- When an IBC packet is received on the target chain, `send-create-pair` command creates a buy order book. +- When an IBC acknowledgement is received on the source chain, the `send-create-pair` command creates a sell order book. + +### Implement the appendOrder Function to Add Orders to the Order Book + +```go +// x/dex/types/order_book.go + +package types + +import ( + "errors" + "sort" +) + +func NewOrderBook() OrderBook { + return OrderBook{ + IdCount: 0, + } +} + +const ( + MaxAmount = int32(100000) + MaxPrice = int32(100000) +) + +type Ordering int + +const ( + Increasing Ordering = iota + Decreasing +) + +var ( + ErrMaxAmount = errors.New("max amount reached") + ErrMaxPrice = errors.New("max price reached") + ErrZeroAmount = errors.New("amount is zero") + ErrZeroPrice = errors.New("price is zero") + ErrOrderNotFound = errors.New("order not found") +) +``` + +The `AppendOrder` function initializes and appends a new order to an order book from the order information: + +```go +// x/dex/types/order_book.go + +func (book *OrderBook) appendOrder(creator string, amount int32, price int32, ordering Ordering) (int32, error) { + if err := checkAmountAndPrice(amount, price); err != nil { + return 0, err + } + + // Initialize the order + var order Order + order.Id = book.GetNextOrderID() + order.Creator = creator + order.Amount = amount + order.Price = price + + // Increment ID tracker + book.IncrementNextOrderID() + + // Insert the order + book.insertOrder(order, ordering) + return order.Id, nil +} +``` + +#### Implement the checkAmountAndPrice Function For an Order + +The `checkAmountAndPrice` function checks for the correct amount or price: + +```go +// x/dex/types/order_book.go + +func checkAmountAndPrice(amount int32, price int32) error { + if amount == int32(0) { + return ErrZeroAmount + } + if amount > MaxAmount { + return ErrMaxAmount + } + + if price == int32(0) { + return ErrZeroPrice + } + if price > MaxPrice { + return ErrMaxPrice + } + + return nil +} +``` + +#### Implement the GetNextOrderID Function + +The `GetNextOrderID` function gets the ID of the next order to append: + +```go +// x/dex/types/order_book.go + +func (book OrderBook) GetNextOrderID() int32 { + return book.IdCount +} +``` + +#### Implement the IncrementNextOrderID Function + +The `IncrementNextOrderID` function updates the ID count for orders: + +```go +// x/dex/types/order_book.go + +func (book *OrderBook) IncrementNextOrderID() { + // Even numbers to have different ID than buy orders + book.IdCount++ +} +``` + +#### Implement the insertOrder Function + +The `insertOrder` function inserts the order in the book with the provided order: + +```go +// x/dex/types/order_book.go + +func (book *OrderBook) insertOrder(order Order, ordering Ordering) { + if len(book.Orders) > 0 { + var i int + + // get the index of the new order depending on the provided ordering + if ordering == Increasing { + i = sort.Search(len(book.Orders), func(i int) bool { return book.Orders[i].Price > order.Price }) + } else { + i = sort.Search(len(book.Orders), func(i int) bool { return book.Orders[i].Price < order.Price }) + } + + // insert order + orders := append(book.Orders, &order) + copy(orders[i+1:], orders[i:]) + orders[i] = &order + book.Orders = orders + } else { + book.Orders = append(book.Orders, &order) + } +} +``` + +This completes the order book setup. + +Now is a good time to save the state of your implementation. +Because your project is in a local repository, you can use git. Saving your current state lets you jump back and forth +in case you introduce errors or need a break. + +```bash +git add . +git commit -m "Create Order Books" +``` + +In the next chapter, you learn how to deal with vouchers by minting and burning vouchers and locking and unlocking +native blockchain token in your app. diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/07-interchange/05-mint-and-burn-voucher.md b/docs/versioned_docs/version-v0.27.1/02-guide/07-interchange/05-mint-and-burn-voucher.md new file mode 100644 index 0000000000..fed19764ef --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/07-interchange/05-mint-and-burn-voucher.md @@ -0,0 +1,351 @@ +--- +order: 5 +description: Mint vouchers and lock and unlock native token from a blockchain. +--- + +# Mint and Burn Vouchers + +In this chapter, you learn about vouchers. The `dex` module implementation mints vouchers and locks and unlocks native +token from a blockchain. + +There is a lot to learn from this `dex` module implementation: + +- You work with the `bank` keeper and use several methods it offers. +- You interact with another module and use the module account to lock tokens. + +This implementation can teach you how to use various interactions with module accounts or minting, locking or burning +tokens. + +## Create the SafeBurn Function to Burn Vouchers or Lock Tokens + +The `SafeBurn` function burns tokens if they are IBC vouchers (have an `ibc/` prefix) and locks tokens if they are +native to the chain. + +Create a new `x/dex/keeper/mint.go` file: + +```go +// x/dex/keeper/mint.go + +package keeper + +import ( + "fmt" + "strings" + + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + ibctransfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types" + + "interchange/x/dex/types" +) + +// isIBCToken checks if the token came from the IBC module +// Each IBC token starts with an ibc/ denom, the check is rather simple +func isIBCToken(denom string) bool { + return strings.HasPrefix(denom, "ibc/") +} + +func (k Keeper) SafeBurn(ctx sdk.Context, port string, channel string, sender sdk.AccAddress, denom string, amount int32) error { + if isIBCToken(denom) { + // Burn the tokens + if err := k.BurnTokens(ctx, sender, sdk.NewCoin(denom, sdkmath.NewInt(int64(amount)))); err != nil { + return err + } + } else { + // Lock the tokens + if err := k.LockTokens(ctx, port, channel, sender, sdk.NewCoin(denom, sdkmath.NewInt(int64(amount)))); err != nil { + return err + } + } + + return nil +} +``` + +If the token comes from another blockchain as an IBC token, the burning method actually burns those IBC tokens on one +chain and unlocks them on the other chain. The native token are locked away. + +Now, implement the `BurnTokens` keeper method as used in the previous function. The `bankKeeper` has a useful function +for this: + +```go +// x/dex/keeper/mint.go + +package keeper + +// ... + +func (k Keeper) BurnTokens(ctx sdk.Context, sender sdk.AccAddress, tokens sdk.Coin) error { + // transfer the coins to the module account and burn them + if err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, sender, types.ModuleName, sdk.NewCoins(tokens)); err != nil { + return err + } + + if err := k.bankKeeper.BurnCoins( + ctx, types.ModuleName, sdk.NewCoins(tokens), + ); err != nil { + // NOTE: should not happen as the module account was + // retrieved on the step above and it has enough balance + // to burn. + panic(fmt.Sprintf("cannot burn coins after a successful send to a module account: %v", err)) + } + + return nil +} +``` + +Implement the `LockTokens` keeper method. + +To lock token from a native chain, you can send the native token to the Escrow Address: + +```go +// x/dex/keeper/mint.go + +package keeper + +// ... + +func (k Keeper) LockTokens(ctx sdk.Context, sourcePort string, sourceChannel string, sender sdk.AccAddress, tokens sdk.Coin) error { + // create the escrow address for the tokens + escrowAddress := ibctransfertypes.GetEscrowAddress(sourcePort, sourceChannel) + + // escrow source tokens. It fails if balance insufficient + if err := k.bankKeeper.SendCoins( + ctx, sender, escrowAddress, sdk.NewCoins(tokens), + ); err != nil { + return err + } + + return nil +} +``` + +`BurnTokens` and `LockTokens` use `SendCoinsFromAccountToModule`, `BurnCoins`, and `SendCoins` keeper methods of the +`bank` module. + +To start using these function from the `dex` module, first add them to the `BankKeeper` interface in the +`x/dex/types/expected_keepers.go` file. + +```go +// x/dex/types/expected_keepers.go + +package types + +import sdk "github.com/cosmos/cosmos-sdk/types" + +// BankKeeper defines the expected bank keeper +type BankKeeper interface { + //... + SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error + BurnCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error + SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error +} +``` + +## SaveVoucherDenom + +The `SaveVoucherDenom` function saves the voucher denom to be able to convert it back later. + +Create a new `x/dex/keeper/denom.go` file: + +```go +// x/dex/keeper/denom.go + +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + ibctransfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types" + + "interchange/x/dex/types" +) + +func (k Keeper) SaveVoucherDenom(ctx sdk.Context, port string, channel string, denom string) { + voucher := VoucherDenom(port, channel, denom) + + // Store the origin denom + _, saved := k.GetDenomTrace(ctx, voucher) + if !saved { + k.SetDenomTrace(ctx, types.DenomTrace{ + Index: voucher, + Port: port, + Channel: channel, + Origin: denom, + }) + } +} +``` + +Finally, the last function to implement is the `VoucherDenom` function that returns the voucher of the denom from the +port ID and channel ID: + +```go +// x/dex/keeper/denom.go + +package keeper + +// ... + +func VoucherDenom(port string, channel string, denom string) string { + // since SendPacket did not prefix the denomination, we must prefix denomination here + sourcePrefix := ibctransfertypes.GetDenomPrefix(port, channel) + + // NOTE: sourcePrefix contains the trailing "/" + prefixedDenom := sourcePrefix + denom + + // construct the denomination trace from the full raw denomination + denomTrace := ibctransfertypes.ParseDenomTrace(prefixedDenom) + voucher := denomTrace.IBCDenom() + return voucher[:16] +} +``` + +### Implement an OriginalDenom Function + +The `OriginalDenom` function returns back the original denom of the voucher. + +False is returned if the port ID and channel ID provided are not the origins of the voucher: + +```go +// x/dex/keeper/denom.go + +package keeper + +// ... + +func (k Keeper) OriginalDenom(ctx sdk.Context, port string, channel string, voucher string) (string, bool) { + trace, exist := k.GetDenomTrace(ctx, voucher) + if exist { + // Check if original port and channel + if trace.Port == port && trace.Channel == channel { + return trace.Origin, true + } + } + + // Not the original chain + return "", false +} +``` + +### Implement a SafeMint Function + +If a token is an IBC token (has an `ibc/` prefix), the `SafeMint` function mints IBC token with `MintTokens`. +Otherwise, it unlocks native token with `UnlockTokens`. + +Go back to the `x/dex/keeper/mint.go` file and add the following code: + +```go +// x/dex/keeper/mint.go + +package keeper + +// ... + +func (k Keeper) SafeMint(ctx sdk.Context, port string, channel string, receiver sdk.AccAddress, denom string, amount int32) error { + if isIBCToken(denom) { + // Mint IBC tokens + if err := k.MintTokens(ctx, receiver, sdk.NewCoin(denom, sdkmath.NewInt(int64(amount)))); err != nil { + return err + } + } else { + // Unlock native tokens + if err := k.UnlockTokens( + ctx, + port, + channel, + receiver, + sdk.NewCoin(denom, sdkmath.NewInt(int64(amount))), + ); err != nil { + return err + } + } + + return nil +} +``` + +#### Implement a `MintTokens` Function + +You can use the `bankKeeper` function again to MintCoins. These token will then be sent to the receiver account: + +```go +// x/dex/keeper/mint.go + +package keeper + +// ... + +func (k Keeper) MintTokens(ctx sdk.Context, receiver sdk.AccAddress, tokens sdk.Coin) error { + // mint new tokens if the source of the transfer is the same chain + if err := k.bankKeeper.MintCoins( + ctx, types.ModuleName, sdk.NewCoins(tokens), + ); err != nil { + return err + } + + // send to receiver + if err := k.bankKeeper.SendCoinsFromModuleToAccount( + ctx, types.ModuleName, receiver, sdk.NewCoins(tokens), + ); err != nil { + panic(fmt.Sprintf("unable to send coins from module to account despite previously minting coins to module account: %v", err)) + } + + return nil +} +``` + +Finally, add the function to unlock token after they are sent back to the native blockchain: + +```go +// x/dex/keeper/mint.go + +package keeper + +// ... + +func (k Keeper) UnlockTokens(ctx sdk.Context, sourcePort string, sourceChannel string, receiver sdk.AccAddress, tokens sdk.Coin) error { + // create the escrow address for the tokens + escrowAddress := ibctransfertypes.GetEscrowAddress(sourcePort, sourceChannel) + + // escrow source tokens. It fails if balance insufficient + if err := k.bankKeeper.SendCoins( + ctx, escrowAddress, receiver, sdk.NewCoins(tokens), + ); err != nil { + return err + } + + return nil +} +``` + +The `MintTokens` function uses two keeper methods from the `bank` module: `MintCoins` and `SendCoinsFromModuleToAccount` +. +To import these methods, add their signatures to the `BankKeeper` interface in the `x/dex/types/expected_keepers.go` +file: + +```go +// x/dex/types/expected_keepers.go + +package types + +// ... + +type BankKeeper interface { + // ... + MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error + SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error +} +``` + +## Summary + +You finished the mint and burn voucher logic. + +It is a good time to make another git commit to save the state of your work: + +```bash +git add . +git commit -m "Add Mint and Burn Voucher" +``` + +In the next chapter, you look into creating sell orders. diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/07-interchange/06-creating-sell-orders.md b/docs/versioned_docs/version-v0.27.1/02-guide/07-interchange/06-creating-sell-orders.md new file mode 100644 index 0000000000..99b3925da5 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/07-interchange/06-creating-sell-orders.md @@ -0,0 +1,402 @@ +--- +sidebar_position: 6 +description: Implement logic to create sell orders. +--- + +# Create Sell Orders + +In this chapter, you implement the logic for creating sell orders. + +The packet proto file for a sell order is already generated. Add the seller information: + +```protobuf +// proto/dex/packet.proto + +message SellOrderPacketData { + // ... + string seller = 5; +} +``` + +Now, use Ignite CLI to build the proto files for the `send-sell-order` command. You used this command in a previous +chapter. + +```bash +ignite generate proto-go --yes +``` + +## Message Handling in SendSellOrder + +Sell orders are created using the `send-sell-order` command. This command creates a transaction with a `SendSellOrder` +message that triggers the `SendSellOrder` keeper method. + +The `SendSellOrder` command: + +* Checks that an order book for a specified denom pair exists. +* Safely burns or locks token. + * If the token is an IBC token, burn the token. + * If the token is a native token, lock the token. +* Saves the voucher that is received on the target chain to later resolve a denom. +* Transmits an IBC packet to the target chain. + +```go +// x/dex/keeper/msg_server_sell_order.go + +package keeper + +import ( + "context" + "errors" + + sdk "github.com/cosmos/cosmos-sdk/types" + clienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types" + + "interchange/x/dex/types" +) + +func (k msgServer) SendSellOrder(goCtx context.Context, msg *types.MsgSendSellOrder) (*types.MsgSendSellOrderResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + // If an order book doesn't exist, throw an error + pairIndex := types.OrderBookIndex(msg.Port, msg.ChannelID, msg.AmountDenom, msg.PriceDenom) + _, found := k.GetSellOrderBook(ctx, pairIndex) + if !found { + return &types.MsgSendSellOrderResponse{}, errors.New("the pair doesn't exist") + } + + // Get sender's address + sender, err := sdk.AccAddressFromBech32(msg.Creator) + if err != nil { + return &types.MsgSendSellOrderResponse{}, err + } + + // Use SafeBurn to ensure no new native tokens are minted + if err := k.SafeBurn(ctx, msg.Port, msg.ChannelID, sender, msg.AmountDenom, msg.Amount); err != nil { + return &types.MsgSendSellOrderResponse{}, err + } + + // Save the voucher received on the other chain, to have the ability to resolve it into the original denom + k.SaveVoucherDenom(ctx, msg.Port, msg.ChannelID, msg.AmountDenom) + + var packet types.SellOrderPacketData + packet.Seller = msg.Creator + packet.AmountDenom = msg.AmountDenom + packet.Amount = msg.Amount + packet.PriceDenom = msg.PriceDenom + packet.Price = msg.Price + + // Transmit the packet + err = k.TransmitSellOrderPacket(ctx, packet, msg.Port, msg.ChannelID, clienttypes.ZeroHeight(), msg.TimeoutTimestamp) + if err != nil { + return nil, err + } + + return &types.MsgSendSellOrderResponse{}, nil +} +``` + +## On Receiving a Sell Order + +When a "sell order" packet is received on the target chain, you want the module to: + +* Update the sell order book +* Distribute sold token to the buyer +* Send the sell order to chain A after the fill attempt + +```go +// x/dex/keeper/sell_order.go + +package keeper + +// ... + +func (k Keeper) OnRecvSellOrderPacket(ctx sdk.Context, packet channeltypes.Packet, data types.SellOrderPacketData) (packetAck types.SellOrderPacketAck, err error) { + if err := data.ValidateBasic(); err != nil { + return packetAck, err + } + + pairIndex := types.OrderBookIndex(packet.SourcePort, packet.SourceChannel, data.AmountDenom, data.PriceDenom) + book, found := k.GetBuyOrderBook(ctx, pairIndex) + if !found { + return packetAck, errors.New("the pair doesn't exist") + } + + // Fill sell order + remaining, liquidated, gain, _ := book.FillSellOrder(types.Order{ + Amount: data.Amount, + Price: data.Price, + }) + + // Return remaining amount and gains + packetAck.RemainingAmount = remaining.Amount + packetAck.Gain = gain + + // Before distributing sales, we resolve the denom + // First we check if the denom received comes from this chain originally + finalAmountDenom, saved := k.OriginalDenom(ctx, packet.DestinationPort, packet.DestinationChannel, data.AmountDenom) + if !saved { + // If it was not from this chain we use voucher as denom + finalAmountDenom = VoucherDenom(packet.SourcePort, packet.SourceChannel, data.AmountDenom) + } + + // Dispatch liquidated buy orders + for _, liquidation := range liquidated { + liquidation := liquidation + addr, err := sdk.AccAddressFromBech32(liquidation.Creator) + if err != nil { + return packetAck, err + } + + if err := k.SafeMint(ctx, packet.DestinationPort, packet.DestinationChannel, addr, finalAmountDenom, liquidation.Amount); err != nil { + return packetAck, err + } + } + + // Save the new order book + k.SetBuyOrderBook(ctx, book) + + return packetAck, nil +} +``` + +### Implement the FillSellOrder Function + +The `FillSellOrder` function tries to fill the buy order with the order book and returns all the side effects: + +```go +// x/dex/types/buy_order_book.go + +package types + +// ... + +func (b *BuyOrderBook) FillSellOrder(order Order) ( + remainingSellOrder Order, + liquidated []Order, + gain int32, + filled bool, +) { + var liquidatedList []Order + totalGain := int32(0) + remainingSellOrder = order + + // Liquidate as long as there is match + for { + var match bool + var liquidation Order + remainingSellOrder, liquidation, gain, match, filled = b.LiquidateFromSellOrder( + remainingSellOrder, + ) + if !match { + break + } + + // Update gains + totalGain += gain + + // Update liquidated + liquidatedList = append(liquidatedList, liquidation) + + if filled { + break + } + } + + return remainingSellOrder, liquidatedList, totalGain, filled +} +``` + +### Implement The LiquidateFromSellOrder Function + +The `LiquidateFromSellOrder` function liquidates the first sell order of the book from the buy order. If no match is +found, return false for match: + +```go +// x/dex/types/buy_order_book.go + +package types + +// ... + +func (b *BuyOrderBook) LiquidateFromSellOrder(order Order) ( + remainingSellOrder Order, + liquidatedBuyOrder Order, + gain int32, + match bool, + filled bool, +) { + remainingSellOrder = order + + // No match if no order + orderCount := len(b.Book.Orders) + if orderCount == 0 { + return order, liquidatedBuyOrder, gain, false, false + } + + // Check if match + highestBid := b.Book.Orders[orderCount-1] + if order.Price > highestBid.Price { + return order, liquidatedBuyOrder, gain, false, false + } + + liquidatedBuyOrder = *highestBid + + // Check if sell order can be entirely filled + if highestBid.Amount >= order.Amount { + remainingSellOrder.Amount = 0 + liquidatedBuyOrder.Amount = order.Amount + gain = order.Amount * highestBid.Price + + // Remove the highest bid if it has been entirely liquidated + highestBid.Amount -= order.Amount + if highestBid.Amount == 0 { + b.Book.Orders = b.Book.Orders[:orderCount-1] + } else { + b.Book.Orders[orderCount-1] = highestBid + } + + return remainingSellOrder, liquidatedBuyOrder, gain, true, true + } + + // Not entirely filled + gain = highestBid.Amount * highestBid.Price + b.Book.Orders = b.Book.Orders[:orderCount-1] + remainingSellOrder.Amount -= highestBid.Amount + + return remainingSellOrder, liquidatedBuyOrder, gain, true, false +} +``` + +### Implement the OnAcknowledgement Function for Sell Order Packets + +After an IBC packet is processed on the target chain, an acknowledgement is returned to the source chain and processed +by the `OnAcknowledgementSellOrderPacket` function. + +The dex module on the source chain: + +* Stores the remaining sell order in the sell order book. +* Distributes sold tokens to the buyers. +* Distributes the price of the amount sold to the seller. +* On error, mints the burned tokens. + +```go +// x/dex/keeper/sell_order.go + +package keeper + +// ... + +func (k Keeper) OnAcknowledgementSellOrderPacket(ctx sdk.Context, packet channeltypes.Packet, data types.SellOrderPacketData, ack channeltypes.Acknowledgement) error { + switch dispatchedAck := ack.Response.(type) { + case *channeltypes.Acknowledgement_Error: + // In case of error we mint back the native token + receiver, err := sdk.AccAddressFromBech32(data.Seller) + if err != nil { + return err + } + + if err := k.SafeMint(ctx, packet.SourcePort, packet.SourceChannel, receiver, data.AmountDenom, data.Amount); err != nil { + return err + } + + return nil + case *channeltypes.Acknowledgement_Result: + // Decode the packet acknowledgment + var packetAck types.SellOrderPacketAck + if err := types.ModuleCdc.UnmarshalJSON(dispatchedAck.Result, &packetAck); err != nil { + // The counter-party module doesn't implement the correct acknowledgment format + return errors.New("cannot unmarshal acknowledgment") + } + + // Get the sell order book + pairIndex := types.OrderBookIndex(packet.SourcePort, packet.SourceChannel, data.AmountDenom, data.PriceDenom) + book, found := k.GetSellOrderBook(ctx, pairIndex) + if !found { + panic("sell order book must exist") + } + + // Append the remaining amount of the order + if packetAck.RemainingAmount > 0 { + _, err := book.AppendOrder(data.Seller, packetAck.RemainingAmount, data.Price) + if err != nil { + return err + } + + // Save the new order book + k.SetSellOrderBook(ctx, book) + } + + // Mint the gains + if packetAck.Gain > 0 { + receiver, err := sdk.AccAddressFromBech32(data.Seller) + if err != nil { + return err + } + + finalPriceDenom, saved := k.OriginalDenom(ctx, packet.SourcePort, packet.SourceChannel, data.PriceDenom) + if !saved { + // If it was not from this chain we use voucher as denom + finalPriceDenom = VoucherDenom(packet.DestinationPort, packet.DestinationChannel, data.PriceDenom) + } + + if err := k.SafeMint(ctx, packet.SourcePort, packet.SourceChannel, receiver, finalPriceDenom, packetAck.Gain); err != nil { + return err + } + } + + return nil + default: + // The counter-party module doesn't implement the correct acknowledgment format + return errors.New("invalid acknowledgment format") + } +} +``` + +```go +// x/dex/types/sell_order_book.go + +package types + +// ... + +func (s *SellOrderBook) AppendOrder(creator string, amount int32, price int32) (int32, error) { + return s.Book.appendOrder(creator, amount, price, Decreasing) +} +``` + +### Add the OnTimeout of a Sell Order Packet Function + +If a timeout occurs, mint back the native token: + +```go +// x/dex/keeper/sell_order.go + +package keeper + +// ... + +func (k Keeper) OnTimeoutSellOrderPacket(ctx sdk.Context, packet channeltypes.Packet, data types.SellOrderPacketData) error { + // In case of error we mint back the native token + receiver, err := sdk.AccAddressFromBech32(data.Seller) + if err != nil { + return err + } + + if err := k.SafeMint(ctx, packet.SourcePort, packet.SourceChannel, receiver, data.AmountDenom, data.Amount); err != nil { + return err + } + + return nil +} +``` + +## Summary + +Great, you have completed the sell order logic. + +It is a good time to make another git commit again to save the state of your work: + +```bash +git add . +git commit -m "Add Sell Orders" +``` diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/07-interchange/07-creating-buy-orders.md b/docs/versioned_docs/version-v0.27.1/02-guide/07-interchange/07-creating-buy-orders.md new file mode 100644 index 0000000000..05fb6471a6 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/07-interchange/07-creating-buy-orders.md @@ -0,0 +1,440 @@ +--- +sidebar_position: 7 +description: Implement the buy order logic. +--- + +# Creating Buy Orders + +In this chapter, you implement the creation of buy orders. The logic is very similar to the sell order logic you +implemented in the previous chapter. + +## Modify the Proto Definition + +Add the buyer to the proto file definition: + +```protobuf +// proto/interchange/dex/packet.proto + +message BuyOrderPacketData { + // ... + string buyer = 5; +} +``` + +Now, use Ignite CLI to build the proto files for the `send-buy-order` command. You used this command in previous +chapters. + +```bash +ignite generate proto-go --yes +``` + +## IBC Message Handling in SendBuyOrder + +* Check if the pair exists on the order book +* If the token is an IBC token, burn the tokens +* If the token is a native token, lock the tokens +* Save the voucher received on the target chain to later resolve a denom + +```go +// x/dex/keeper/msg_server_buy_order.go + +package keeper + +import ( + "context" + "errors" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "interchange/x/dex/types" +) + +func (k msgServer) SendBuyOrder(goCtx context.Context, msg *types.MsgSendBuyOrder) (*types.MsgSendBuyOrderResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + // Cannot send a order if the pair doesn't exist + pairIndex := types.OrderBookIndex(msg.Port, msg.ChannelID, msg.AmountDenom, msg.PriceDenom) + _, found := k.GetBuyOrderBook(ctx, pairIndex) + if !found { + return &types.MsgSendBuyOrderResponse{}, errors.New("the pair doesn't exist") + } + + // Lock the token to send + sender, err := sdk.AccAddressFromBech32(msg.Creator) + if err != nil { + return &types.MsgSendBuyOrderResponse{}, err + } + + // Use SafeBurn to ensure no new native tokens are minted + if err := k.SafeBurn(ctx, msg.Port, msg.ChannelID, sender, msg.PriceDenom, msg.Amount*msg.Price); err != nil { + return &types.MsgSendBuyOrderResponse{}, err + } + + // Save the voucher received on the other chain, to have the ability to resolve it into the original denom + k.SaveVoucherDenom(ctx, msg.Port, msg.ChannelID, msg.PriceDenom) + + // Construct the packet + var packet types.BuyOrderPacketData + + packet.Buyer = msg.Creator + packet.AmountDenom = msg.AmountDenom + packet.Amount = msg.Amount + packet.PriceDenom = msg.PriceDenom + packet.Price = msg.Price + + // Transmit the packet + err = k.TransmitBuyOrderPacket( + ctx, + packet, + msg.Port, + msg.ChannelID, + clienttypes.ZeroHeight(), + msg.TimeoutTimestamp, + ) + if err != nil { + return nil, err + } + + // Transmit an IBC packet... + return &types.MsgSendBuyOrderResponse{}, nil +} +``` + +## On Receiving a Buy Order + +* Update the buy order book +* Distribute sold token to the buyer +* Send the sell order to chain A after the fill attempt + +```go +// x/dex/keeper/buy_order.go + +package keeper + +// ... + +func (k Keeper) OnRecvBuyOrderPacket(ctx sdk.Context, packet channeltypes.Packet, data types.BuyOrderPacketData) (packetAck types.BuyOrderPacketAck, err error) { + // validate packet data upon receiving + if err := data.ValidateBasic(); err != nil { + return packetAck, err + } + + // Check if the sell order book exists + pairIndex := types.OrderBookIndex(packet.SourcePort, packet.SourceChannel, data.AmountDenom, data.PriceDenom) + book, found := k.GetSellOrderBook(ctx, pairIndex) + if !found { + return packetAck, errors.New("the pair doesn't exist") + } + + // Fill buy order + remaining, liquidated, purchase, _ := book.FillBuyOrder(types.Order{ + Amount: data.Amount, + Price: data.Price, + }) + + // Return remaining amount and gains + packetAck.RemainingAmount = remaining.Amount + packetAck.Purchase = purchase + + // Before distributing gains, we resolve the denom + // First we check if the denom received comes from this chain originally + finalPriceDenom, saved := k.OriginalDenom(ctx, packet.DestinationPort, packet.DestinationChannel, data.PriceDenom) + if !saved { + // If it was not from this chain we use voucher as denom + finalPriceDenom = VoucherDenom(packet.SourcePort, packet.SourceChannel, data.PriceDenom) + } + + // Dispatch liquidated buy order + for _, liquidation := range liquidated { + liquidation := liquidation + addr, err := sdk.AccAddressFromBech32(liquidation.Creator) + if err != nil { + return packetAck, err + } + + if err := k.SafeMint( + ctx, + packet.DestinationPort, + packet.DestinationChannel, + addr, + finalPriceDenom, + liquidation.Amount*liquidation.Price, + ); err != nil { + return packetAck, err + } + } + + // Save the new order book + k.SetSellOrderBook(ctx, book) + + return packetAck, nil +} +``` + +### Implement a FillBuyOrder Function + +The `FillBuyOrder` function tries to fill the sell order with the order book and returns all the side effects: + +```go +// x/dex/types/sell_order_book.go + +package types + +// ... + +func (s *SellOrderBook) FillBuyOrder(order Order) ( + remainingBuyOrder Order, + liquidated []Order, + purchase int32, + filled bool, +) { + var liquidatedList []Order + totalPurchase := int32(0) + remainingBuyOrder = order + + // Liquidate as long as there is match + for { + var match bool + var liquidation Order + remainingBuyOrder, liquidation, purchase, match, filled = s.LiquidateFromBuyOrder( + remainingBuyOrder, + ) + if !match { + break + } + + // Update gains + totalPurchase += purchase + + // Update liquidated + liquidatedList = append(liquidatedList, liquidation) + + if filled { + break + } + } + + return remainingBuyOrder, liquidatedList, totalPurchase, filled +} +``` + +### Implement a LiquidateFromBuyOrder Function + +The `LiquidateFromBuyOrder` function liquidates the first buy order of the book from the sell order. If no match is +found, return false for match: + +```go +// x/dex/types/sell_order_book.go + +package types + +// ... + +func (s *SellOrderBook) LiquidateFromBuyOrder(order Order) ( + remainingBuyOrder Order, + liquidatedSellOrder Order, + purchase int32, + match bool, + filled bool, +) { + remainingBuyOrder = order + + // No match if no order + orderCount := len(s.Book.Orders) + if orderCount == 0 { + return order, liquidatedSellOrder, purchase, false, false + } + + // Check if match + lowestAsk := s.Book.Orders[orderCount-1] + if order.Price < lowestAsk.Price { + return order, liquidatedSellOrder, purchase, false, false + } + + liquidatedSellOrder = *lowestAsk + + // Check if buy order can be entirely filled + if lowestAsk.Amount >= order.Amount { + remainingBuyOrder.Amount = 0 + liquidatedSellOrder.Amount = order.Amount + purchase = order.Amount + + // Remove lowest ask if it has been entirely liquidated + lowestAsk.Amount -= order.Amount + if lowestAsk.Amount == 0 { + s.Book.Orders = s.Book.Orders[:orderCount-1] + } else { + s.Book.Orders[orderCount-1] = lowestAsk + } + + return remainingBuyOrder, liquidatedSellOrder, purchase, true, true + } + + // Not entirely filled + purchase = lowestAsk.Amount + s.Book.Orders = s.Book.Orders[:orderCount-1] + remainingBuyOrder.Amount -= lowestAsk.Amount + + return remainingBuyOrder, liquidatedSellOrder, purchase, true, false +} +``` + +## Receiving a Buy Order Acknowledgment + +After a buy order acknowledgement is received, chain `Mars`: + +* Stores the remaining sell order in the sell order book. +* Distributes sold `marscoin` to the buyers. +* Distributes to the seller the price of the amount sold. +* On error, mints back the burned tokens. + +```go +// x/dex/keeper/buy_order.go + +package keeper + +// ... + +func (k Keeper) OnAcknowledgementBuyOrderPacket(ctx sdk.Context, packet channeltypes.Packet, data types.BuyOrderPacketData, ack channeltypes.Acknowledgement) error { + switch dispatchedAck := ack.Response.(type) { + case *channeltypes.Acknowledgement_Error: + // In case of error we mint back the native token + receiver, err := sdk.AccAddressFromBech32(data.Buyer) + if err != nil { + return err + } + + if err := k.SafeMint( + ctx, + packet.SourcePort, + packet.SourceChannel, + receiver, + data.PriceDenom, + data.Amount*data.Price, + ); err != nil { + return err + } + + return nil + case *channeltypes.Acknowledgement_Result: + // Decode the packet acknowledgment + var packetAck types.BuyOrderPacketAck + + if err := types.ModuleCdc.UnmarshalJSON(dispatchedAck.Result, &packetAck); err != nil { + // The counter-party module doesn't implement the correct acknowledgment format + return errors.New("cannot unmarshal acknowledgment") + } + + // Get the sell order book + pairIndex := types.OrderBookIndex(packet.SourcePort, packet.SourceChannel, data.AmountDenom, data.PriceDenom) + book, found := k.GetBuyOrderBook(ctx, pairIndex) + if !found { + panic("buy order book must exist") + } + + // Append the remaining amount of the order + if packetAck.RemainingAmount > 0 { + _, err := book.AppendOrder( + data.Buyer, + packetAck.RemainingAmount, + data.Price, + ) + if err != nil { + return err + } + + // Save the new order book + k.SetBuyOrderBook(ctx, book) + } + + // Mint the purchase + if packetAck.Purchase > 0 { + receiver, err := sdk.AccAddressFromBech32(data.Buyer) + if err != nil { + return err + } + + finalAmountDenom, saved := k.OriginalDenom(ctx, packet.SourcePort, packet.SourceChannel, data.AmountDenom) + if !saved { + // If it was not from this chain we use voucher as denom + finalAmountDenom = VoucherDenom(packet.DestinationPort, packet.DestinationChannel, data.AmountDenom) + } + + if err := k.SafeMint( + ctx, + packet.SourcePort, + packet.SourceChannel, + receiver, + finalAmountDenom, + packetAck.Purchase, + ); err != nil { + return err + } + } + + return nil + default: + // The counter-party module doesn't implement the correct acknowledgment format + return errors.New("invalid acknowledgment format") + } +} +``` + +`AppendOrder` appends an order in the buy order book. +Add the following function to the `x/dex/types/buy_order_book.go` file in the `types` directory. + +```go +// x/dex/types/buy_order_book.go + +package types + +// ... + +func (b *BuyOrderBook) AppendOrder(creator string, amount int32, price int32) (int32, error) { + return b.Book.appendOrder(creator, amount, price, Increasing) +} +``` + +## OnTimeout of a Buy Order Packet + +If a timeout occurs, mint back the native token: + +```go +// x/dex/keeper/buy_order.go + +package keeper + +// ... + +func (k Keeper) OnTimeoutBuyOrderPacket(ctx sdk.Context, packet channeltypes.Packet, data types.BuyOrderPacketData) error { + // In case of error we mint back the native token + receiver, err := sdk.AccAddressFromBech32(data.Buyer) + if err != nil { + return err + } + + if err := k.SafeMint( + ctx, + packet.SourcePort, + packet.SourceChannel, + receiver, + data.PriceDenom, + data.Amount*data.Price, + ); err != nil { + return err + } + + return nil +} +``` + +## Summary + +Congratulations, you implemented the buy order logic. + +Again, it's a good time to save your current state to your local GitHub repository: + +```bash +git add . +git commit -m "Add Buy Orders" +``` diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/07-interchange/08-cancelling-orders.md b/docs/versioned_docs/version-v0.27.1/02-guide/07-interchange/08-cancelling-orders.md new file mode 100644 index 0000000000..103ff5ffbd --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/07-interchange/08-cancelling-orders.md @@ -0,0 +1,200 @@ +--- +sidebar_position: 8 +description: Enable cancelling of buy and sell orders. +--- + +# Cancelling Orders + +You have implemented order books, buy and sell orders. In this chapter, you enable cancelling of buy and sell orders. + +## Cancel a Sell Order + +To cancel a sell order, you have to get the ID of the specific sell order. Then you can use the function +`RemoveOrderFromID` to remove the specific order from the order book and update the keeper accordingly. + +Move to the keeper directory and edit the `x/dex/keeper/msg_server_cancel_sell_order.go` file: + +```go +// x/dex/keeper/msg_server_cancel_sell_order.go + +package keeper + +import ( + "context" + "errors" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "interchange/x/dex/types" +) + +func (k msgServer) CancelSellOrder(goCtx context.Context, msg *types.MsgCancelSellOrder) (*types.MsgCancelSellOrderResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + // Retrieve the book + pairIndex := types.OrderBookIndex(msg.Port, msg.Channel, msg.AmountDenom, msg.PriceDenom) + s, found := k.GetSellOrderBook(ctx, pairIndex) + if !found { + return &types.MsgCancelSellOrderResponse{}, errors.New("the pair doesn't exist") + } + + // Check order creator + order, err := s.Book.GetOrderFromID(msg.OrderID) + if err != nil { + return &types.MsgCancelSellOrderResponse{}, err + } + + if order.Creator != msg.Creator { + return &types.MsgCancelSellOrderResponse{}, errors.New("canceller must be creator") + } + + // Remove order + if err := s.Book.RemoveOrderFromID(msg.OrderID); err != nil { + return &types.MsgCancelSellOrderResponse{}, err + } + + k.SetSellOrderBook(ctx, s) + + // Refund seller with remaining amount + seller, err := sdk.AccAddressFromBech32(order.Creator) + if err != nil { + return &types.MsgCancelSellOrderResponse{}, err + } + + if err := k.SafeMint(ctx, msg.Port, msg.Channel, seller, msg.AmountDenom, order.Amount); err != nil { + return &types.MsgCancelSellOrderResponse{}, err + } + + return &types.MsgCancelSellOrderResponse{}, nil +} +``` + +### Implement the GetOrderFromID Function + +The `GetOrderFromID` function gets an order from the book from its ID. + +Add this function to the `x/dex/types/order_book.go` function in the `types` directory: + +```go +// x/dex/types/order_book.go + +func (book OrderBook) GetOrderFromID(id int32) (Order, error) { + for _, order := range book.Orders { + if order.Id == id { + return *order, nil + } + } + + return Order{}, ErrOrderNotFound +} +``` + +### Implement the RemoveOrderFromID Function + +The `RemoveOrderFromID` function removes an order from the book and keeps it ordered: + +```go +// x/dex/types/order_book.go + +package types + +// ... + +func (book *OrderBook) RemoveOrderFromID(id int32) error { + for i, order := range book.Orders { + if order.Id == id { + book.Orders = append(book.Orders[:i], book.Orders[i+1:]...) + return nil + } + } + + return ErrOrderNotFound +} +``` + +## Cancel a Buy Order + +To cancel a buy order, you have to get the ID of the specific buy order. Then you can use the function +`RemoveOrderFromID` to remove the specific order from the order book and update the keeper accordingly: + +```go +// x/dex/keeper/msg_server_cancel_buy_order.go + +package keeper + +import ( + "context" + "errors" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "interchange/x/dex/types" +) + +func (k msgServer) CancelBuyOrder(goCtx context.Context, msg *types.MsgCancelBuyOrder) (*types.MsgCancelBuyOrderResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + // Retrieve the book + pairIndex := types.OrderBookIndex(msg.Port, msg.Channel, msg.AmountDenom, msg.PriceDenom) + b, found := k.GetBuyOrderBook(ctx, pairIndex) + if !found { + return &types.MsgCancelBuyOrderResponse{}, errors.New("the pair doesn't exist") + } + + // Check order creator + order, err := b.Book.GetOrderFromID(msg.OrderID) + if err != nil { + return &types.MsgCancelBuyOrderResponse{}, err + } + + if order.Creator != msg.Creator { + return &types.MsgCancelBuyOrderResponse{}, errors.New("canceller must be creator") + } + + // Remove order + if err := b.Book.RemoveOrderFromID(msg.OrderID); err != nil { + return &types.MsgCancelBuyOrderResponse{}, err + } + + k.SetBuyOrderBook(ctx, b) + + // Refund buyer with remaining price amount + buyer, err := sdk.AccAddressFromBech32(order.Creator) + if err != nil { + return &types.MsgCancelBuyOrderResponse{}, err + } + + if err := k.SafeMint( + ctx, + msg.Port, + msg.Channel, + buyer, + msg.PriceDenom, + order.Amount*order.Price, + ); err != nil { + return &types.MsgCancelBuyOrderResponse{}, err + } + + return &types.MsgCancelBuyOrderResponse{}, nil +} +``` + +## Summary + +You have completed implementing the functions that are required for the `dex` module. In this chapter, you have +implemented the design for cancelling specific buy or sell orders. + +To test if your Ignite CLI blockchain builds correctly, use the `chain build` command: + +```bash +ignite chain build +``` + +Again, it is a good time (a great time!) to add your state to the local GitHub repository: + +```bash +git add . +git commit -m "Add Cancelling Orders" +``` + +Finally, it's now time to write test files. diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/07-interchange/09-tests.md b/docs/versioned_docs/version-v0.27.1/02-guide/07-interchange/09-tests.md new file mode 100644 index 0000000000..24981931ea --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/07-interchange/09-tests.md @@ -0,0 +1,729 @@ +--- +sidebar_position: 9 +description: Add test files. +--- + +# Write Test Files + +To test your application, add the test files to your code. + +After you add the test files, change into the `interchange` directory with your terminal, then run: + +```bash +go test -timeout 30s ./x/dex/types +``` + +## Order Book Tests + +Create a new `x/dex/types/order_book_test.go` file in the `types` directory. + +Add the following testsuite: + +```go +// x/dex/types/order_book_test.go + +package types_test + +import ( + "math/rand" + "testing" + + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + + "interchange/x/dex/types" +) + +func GenString(n int) string { + alpha := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + + buf := make([]rune, n) + for i := range buf { + buf[i] = alpha[rand.Intn(len(alpha))] + } + + return string(buf) +} + +func GenAddress() string { + pk := ed25519.GenPrivKey().PubKey() + addr := pk.Address() + return sdk.AccAddress(addr).String() +} + +func GenAmount() int32 { + return int32(rand.Intn(int(types.MaxAmount)) + 1) +} + +func GenPrice() int32 { + return int32(rand.Intn(int(types.MaxPrice)) + 1) +} + +func GenPair() (string, string) { + return GenString(10), GenString(10) +} + +func GenOrder() (string, int32, int32) { + return GenLocalAccount(), GenAmount(), GenPrice() +} + +func GenLocalAccount() string { + return GenAddress() +} + +func MockAccount(str string) string { + return str +} + +func OrderListToOrderBook(list []types.Order) types.OrderBook { + listCopy := make([]*types.Order, len(list)) + for i, order := range list { + order := order + listCopy[i] = &order + } + + return types.OrderBook{ + IdCount: 0, + Orders: listCopy, + } +} + +func TestRemoveOrderFromID(t *testing.T) { + inputList := []types.Order{ + {Id: 3, Creator: MockAccount("3"), Amount: 2, Price: 10}, + {Id: 2, Creator: MockAccount("2"), Amount: 30, Price: 15}, + {Id: 1, Creator: MockAccount("1"), Amount: 200, Price: 20}, + {Id: 0, Creator: MockAccount("0"), Amount: 50, Price: 25}, + } + + book := OrderListToOrderBook(inputList) + expectedList := []types.Order{ + {Id: 3, Creator: MockAccount("3"), Amount: 2, Price: 10}, + {Id: 1, Creator: MockAccount("1"), Amount: 200, Price: 20}, + {Id: 0, Creator: MockAccount("0"), Amount: 50, Price: 25}, + } + expectedBook := OrderListToOrderBook(expectedList) + err := book.RemoveOrderFromID(2) + require.NoError(t, err) + require.Equal(t, expectedBook, book) + + book = OrderListToOrderBook(inputList) + expectedList = []types.Order{ + {Id: 3, Creator: MockAccount("3"), Amount: 2, Price: 10}, + {Id: 2, Creator: MockAccount("2"), Amount: 30, Price: 15}, + {Id: 1, Creator: MockAccount("1"), Amount: 200, Price: 20}, + } + expectedBook = OrderListToOrderBook(expectedList) + err = book.RemoveOrderFromID(0) + require.NoError(t, err) + require.Equal(t, expectedBook, book) + + book = OrderListToOrderBook(inputList) + expectedList = []types.Order{ + {Id: 2, Creator: MockAccount("2"), Amount: 30, Price: 15}, + {Id: 1, Creator: MockAccount("1"), Amount: 200, Price: 20}, + {Id: 0, Creator: MockAccount("0"), Amount: 50, Price: 25}, + } + expectedBook = OrderListToOrderBook(expectedList) + err = book.RemoveOrderFromID(3) + require.NoError(t, err) + require.Equal(t, expectedBook, book) + + book = OrderListToOrderBook(inputList) + err = book.RemoveOrderFromID(4) + require.ErrorIs(t, err, types.ErrOrderNotFound) +} +``` + +## Buy Order Tests + +Create a new `x/dex/types/buy_order_book_test.go` file in the `types` directory to add the tests for the Buy Order Book: + +```go +// x/dex/types/buy_order_book_test.go + +package types_test + +import ( + "sort" + "testing" + + "github.com/stretchr/testify/require" + + "interchange/x/dex/types" +) + +func OrderListToBuyOrderBook(list []types.Order) types.BuyOrderBook { + listCopy := make([]*types.Order, len(list)) + for i, order := range list { + order := order + listCopy[i] = &order + } + + book := types.BuyOrderBook{ + AmountDenom: "foo", + PriceDenom: "bar", + Book: &types.OrderBook{ + IdCount: 0, + Orders: listCopy, + }, + } + return book +} + +func TestAppendOrder(t *testing.T) { + buyBook := types.NewBuyOrderBook(GenPair()) + + // Prevent zero amount + seller, amount, price := GenOrder() + _, err := buyBook.AppendOrder(seller, 0, price) + require.ErrorIs(t, err, types.ErrZeroAmount) + + // Prevent big amount + _, err = buyBook.AppendOrder(seller, types.MaxAmount+1, price) + require.ErrorIs(t, err, types.ErrMaxAmount) + + // Prevent zero price + _, err = buyBook.AppendOrder(seller, amount, 0) + require.ErrorIs(t, err, types.ErrZeroPrice) + + // Prevent big price + _, err = buyBook.AppendOrder(seller, amount, types.MaxPrice+1) + require.ErrorIs(t, err, types.ErrMaxPrice) + + // Can append buy orders + for i := 0; i < 20; i++ { + // Append a new order + creator, amount, price := GenOrder() + newOrder := types.Order{ + Id: buyBook.Book.IdCount, + Creator: creator, + Amount: amount, + Price: price, + } + orderID, err := buyBook.AppendOrder(creator, amount, price) + + // Checks + require.NoError(t, err) + require.Contains(t, buyBook.Book.Orders, &newOrder) + require.Equal(t, newOrder.Id, orderID) + } + + require.Len(t, buyBook.Book.Orders, 20) + require.True(t, sort.SliceIsSorted(buyBook.Book.Orders, func(i, j int) bool { + return buyBook.Book.Orders[i].Price < buyBook.Book.Orders[j].Price + })) +} + +type liquidateSellRes struct { + Book []types.Order + Remaining types.Order + Liquidated types.Order + Gain int32 + Match bool + Filled bool +} + +func simulateLiquidateFromSellOrder( + t *testing.T, + inputList []types.Order, + inputOrder types.Order, + expected liquidateSellRes, +) { + book := OrderListToBuyOrderBook(inputList) + expectedBook := OrderListToBuyOrderBook(expected.Book) + + require.True(t, sort.SliceIsSorted(book.Book.Orders, func(i, j int) bool { + return book.Book.Orders[i].Price < book.Book.Orders[j].Price + })) + require.True(t, sort.SliceIsSorted(expectedBook.Book.Orders, func(i, j int) bool { + return expectedBook.Book.Orders[i].Price < expectedBook.Book.Orders[j].Price + })) + + remaining, liquidated, gain, match, filled := book.LiquidateFromSellOrder(inputOrder) + + require.Equal(t, expectedBook, book) + require.Equal(t, expected.Remaining, remaining) + require.Equal(t, expected.Liquidated, liquidated) + require.Equal(t, expected.Gain, gain) + require.Equal(t, expected.Match, match) + require.Equal(t, expected.Filled, filled) +} + +func TestLiquidateFromSellOrder(t *testing.T) { + // No match for empty book + inputOrder := types.Order{Id: 10, Creator: MockAccount("1"), Amount: 100, Price: 30} + book := OrderListToBuyOrderBook([]types.Order{}) + _, _, _, match, _ := book.LiquidateFromSellOrder(inputOrder) + require.False(t, match) + + // Buy book + inputBook := []types.Order{ + {Id: 2, Creator: MockAccount("2"), Amount: 30, Price: 15}, + {Id: 1, Creator: MockAccount("1"), Amount: 200, Price: 20}, + {Id: 0, Creator: MockAccount("0"), Amount: 50, Price: 25}, + } + + // Test no match if highest bid too low (25 < 30) + book = OrderListToBuyOrderBook(inputBook) + _, _, _, match, _ = book.LiquidateFromSellOrder(inputOrder) + require.False(t, match) + + // Entirely filled (30 < 50) + inputOrder = types.Order{Id: 10, Creator: MockAccount("1"), Amount: 30, Price: 22} + expected := liquidateSellRes{ + Book: []types.Order{ + {Id: 2, Creator: MockAccount("2"), Amount: 30, Price: 15}, + {Id: 1, Creator: MockAccount("1"), Amount: 200, Price: 20}, + {Id: 0, Creator: MockAccount("0"), Amount: 20, Price: 25}, + }, + Remaining: types.Order{Id: 10, Creator: MockAccount("1"), Amount: 0, Price: 22}, + Liquidated: types.Order{Id: 0, Creator: MockAccount("0"), Amount: 30, Price: 25}, + Gain: int32(30 * 25), + Match: true, + Filled: true, + } + simulateLiquidateFromSellOrder(t, inputBook, inputOrder, expected) + + // Entirely filled and liquidated ( 50 = 50) + inputOrder = types.Order{Id: 10, Creator: MockAccount("1"), Amount: 50, Price: 15} + expected = liquidateSellRes{ + Book: []types.Order{ + {Id: 2, Creator: MockAccount("2"), Amount: 30, Price: 15}, + {Id: 1, Creator: MockAccount("1"), Amount: 200, Price: 20}, + }, + Remaining: types.Order{Id: 10, Creator: MockAccount("1"), Amount: 0, Price: 15}, + Liquidated: types.Order{Id: 0, Creator: MockAccount("0"), Amount: 50, Price: 25}, + Gain: int32(50 * 25), + Match: true, + Filled: true, + } + simulateLiquidateFromSellOrder(t, inputBook, inputOrder, expected) + + // Not filled and entirely liquidated (60 > 50) + inputOrder = types.Order{Id: 10, Creator: MockAccount("1"), Amount: 60, Price: 10} + expected = liquidateSellRes{ + Book: []types.Order{ + {Id: 2, Creator: MockAccount("2"), Amount: 30, Price: 15}, + {Id: 1, Creator: MockAccount("1"), Amount: 200, Price: 20}, + }, + Remaining: types.Order{Id: 10, Creator: MockAccount("1"), Amount: 10, Price: 10}, + Liquidated: types.Order{Id: 0, Creator: MockAccount("0"), Amount: 50, Price: 25}, + Gain: int32(50 * 25), + Match: true, + Filled: false, + } + simulateLiquidateFromSellOrder(t, inputBook, inputOrder, expected) +} + +type fillSellRes struct { + Book []types.Order + Remaining types.Order + Liquidated []types.Order + Gain int32 + Filled bool +} + +func simulateFillSellOrder( + t *testing.T, + inputList []types.Order, + inputOrder types.Order, + expected fillSellRes, +) { + book := OrderListToBuyOrderBook(inputList) + expectedBook := OrderListToBuyOrderBook(expected.Book) + + require.True(t, sort.SliceIsSorted(book.Book.Orders, func(i, j int) bool { + return book.Book.Orders[i].Price < book.Book.Orders[j].Price + })) + require.True(t, sort.SliceIsSorted(expectedBook.Book.Orders, func(i, j int) bool { + return expectedBook.Book.Orders[i].Price < expectedBook.Book.Orders[j].Price + })) + + remaining, liquidated, gain, filled := book.FillSellOrder(inputOrder) + + require.Equal(t, expectedBook, book) + require.Equal(t, expected.Remaining, remaining) + require.Equal(t, expected.Liquidated, liquidated) + require.Equal(t, expected.Gain, gain) + require.Equal(t, expected.Filled, filled) +} + +func TestFillSellOrder(t *testing.T) { + var inputBook []types.Order + + // Empty book + inputOrder := types.Order{Id: 10, Creator: MockAccount("1"), Amount: 30, Price: 30} + expected := fillSellRes{ + Book: []types.Order{}, + Remaining: inputOrder, + Liquidated: []types.Order(nil), + Gain: int32(0), + Filled: false, + } + simulateFillSellOrder(t, inputBook, inputOrder, expected) + + // No match + inputBook = []types.Order{ + {Id: 2, Creator: MockAccount("2"), Amount: 30, Price: 15}, + {Id: 1, Creator: MockAccount("1"), Amount: 200, Price: 20}, + {Id: 0, Creator: MockAccount("0"), Amount: 50, Price: 25}, + } + expected = fillSellRes{ + Book: inputBook, + Remaining: inputOrder, + Liquidated: []types.Order(nil), + Gain: int32(0), + Filled: false, + } + simulateFillSellOrder(t, inputBook, inputOrder, expected) + + // First order liquidated, not filled + inputOrder = types.Order{Id: 10, Creator: MockAccount("1"), Amount: 60, Price: 22} + expected = fillSellRes{ + Book: []types.Order{ + {Id: 2, Creator: MockAccount("2"), Amount: 30, Price: 15}, + {Id: 1, Creator: MockAccount("1"), Amount: 200, Price: 20}, + }, + Remaining: types.Order{Id: 10, Creator: MockAccount("1"), Amount: 10, Price: 22}, + Liquidated: []types.Order{ + {Id: 0, Creator: MockAccount("0"), Amount: 50, Price: 25}, + }, + Gain: int32(50 * 25), + Filled: false, + } + simulateFillSellOrder(t, inputBook, inputOrder, expected) + + // Filled with two order + inputOrder = types.Order{Id: 10, Creator: MockAccount("1"), Amount: 60, Price: 18} + expected = fillSellRes{ + Book: []types.Order{ + {Id: 2, Creator: MockAccount("2"), Amount: 30, Price: 15}, + {Id: 1, Creator: MockAccount("1"), Amount: 190, Price: 20}, + }, + Remaining: types.Order{Id: 10, Creator: MockAccount("1"), Amount: 0, Price: 18}, + Liquidated: []types.Order{ + {Id: 0, Creator: MockAccount("0"), Amount: 50, Price: 25}, + {Id: 1, Creator: MockAccount("1"), Amount: 10, Price: 20}, + }, + Gain: int32(50*25 + 10*20), + Filled: true, + } + simulateFillSellOrder(t, inputBook, inputOrder, expected) + + // Not filled, buy order book liquidated + inputOrder = types.Order{Id: 10, Creator: MockAccount("1"), Amount: 300, Price: 10} + expected = fillSellRes{ + Book: []types.Order{}, + Remaining: types.Order{Id: 10, Creator: MockAccount("1"), Amount: 20, Price: 10}, + Liquidated: []types.Order{ + {Id: 0, Creator: MockAccount("0"), Amount: 50, Price: 25}, + {Id: 1, Creator: MockAccount("1"), Amount: 200, Price: 20}, + {Id: 2, Creator: MockAccount("2"), Amount: 30, Price: 15}, + }, + Gain: int32(50*25 + 200*20 + 30*15), + Filled: false, + } + simulateFillSellOrder(t, inputBook, inputOrder, expected) +} +``` + +## Sell Order Tests + +Create a new testsuite for Sell Orders in a new file `x/dex/types/sell_order_book_test.go`: + +```go +// x/dex/types/sell_order_book_test.go + +package types_test + +import ( + "sort" + "testing" + + "github.com/stretchr/testify/require" + + "interchange/x/dex/types" +) + +func OrderListToSellOrderBook(list []types.Order) types.SellOrderBook { + listCopy := make([]*types.Order, len(list)) + for i, order := range list { + order := order + listCopy[i] = &order + } + + book := types.SellOrderBook{ + AmountDenom: "foo", + PriceDenom: "bar", + Book: &types.OrderBook{ + IdCount: 0, + Orders: listCopy, + }, + } + return book +} + +func TestSellOrderBook_AppendOrder(t *testing.T) { + sellBook := types.NewSellOrderBook(GenPair()) + + // Prevent zero amount + seller, amount, price := GenOrder() + _, err := sellBook.AppendOrder(seller, 0, price) + require.ErrorIs(t, err, types.ErrZeroAmount) + + // Prevent big amount + _, err = sellBook.AppendOrder(seller, types.MaxAmount+1, price) + require.ErrorIs(t, err, types.ErrMaxAmount) + + // Prevent zero price + _, err = sellBook.AppendOrder(seller, amount, 0) + require.ErrorIs(t, err, types.ErrZeroPrice) + + // Prevent big price + _, err = sellBook.AppendOrder(seller, amount, types.MaxPrice+1) + require.ErrorIs(t, err, types.ErrMaxPrice) + + // Can append sell orders + for i := 0; i < 20; i++ { + // Append a new order + creator, amount, price := GenOrder() + newOrder := types.Order{ + Id: sellBook.Book.IdCount, + Creator: creator, + Amount: amount, + Price: price, + } + orderID, err := sellBook.AppendOrder(creator, amount, price) + + // Checks + require.NoError(t, err) + require.Contains(t, sellBook.Book.Orders, &newOrder) + require.Equal(t, newOrder.Id, orderID) + } + require.Len(t, sellBook.Book.Orders, 20) + require.True(t, sort.SliceIsSorted(sellBook.Book.Orders, func(i, j int) bool { + return sellBook.Book.Orders[i].Price > sellBook.Book.Orders[j].Price + })) +} + +type liquidateBuyRes struct { + Book []types.Order + Remaining types.Order + Liquidated types.Order + Purchase int32 + Match bool + Filled bool +} + +func simulateLiquidateFromBuyOrder( + t *testing.T, + inputList []types.Order, + inputOrder types.Order, + expected liquidateBuyRes, +) { + book := OrderListToSellOrderBook(inputList) + expectedBook := OrderListToSellOrderBook(expected.Book) + require.True(t, sort.SliceIsSorted(book.Book.Orders, func(i, j int) bool { + return book.Book.Orders[i].Price > book.Book.Orders[j].Price + })) + require.True(t, sort.SliceIsSorted(expectedBook.Book.Orders, func(i, j int) bool { + return expectedBook.Book.Orders[i].Price > expectedBook.Book.Orders[j].Price + })) + + remaining, liquidated, purchase, match, filled := book.LiquidateFromBuyOrder(inputOrder) + + require.Equal(t, expectedBook, book) + require.Equal(t, expected.Remaining, remaining) + require.Equal(t, expected.Liquidated, liquidated) + require.Equal(t, expected.Purchase, purchase) + require.Equal(t, expected.Match, match) + require.Equal(t, expected.Filled, filled) +} + +func TestLiquidateFromBuyOrder(t *testing.T) { + // No match for empty book + inputOrder := types.Order{Id: 10, Creator: MockAccount("1"), Amount: 100, Price: 10} + book := OrderListToSellOrderBook([]types.Order{}) + _, _, _, match, _ := book.LiquidateFromBuyOrder(inputOrder) + require.False(t, match) + + // Sell book + inputBook := []types.Order{ + {Id: 0, Creator: MockAccount("0"), Amount: 50, Price: 25}, + {Id: 1, Creator: MockAccount("1"), Amount: 200, Price: 20}, + {Id: 2, Creator: MockAccount("2"), Amount: 30, Price: 15}, + } + + // Test no match if lowest ask too high (25 < 30) + book = OrderListToSellOrderBook(inputBook) + _, _, _, match, _ = book.LiquidateFromBuyOrder(inputOrder) + require.False(t, match) + + // Entirely filled (30 > 15) + inputOrder = types.Order{Id: 10, Creator: MockAccount("1"), Amount: 20, Price: 30} + expected := liquidateBuyRes{ + Book: []types.Order{ + {Id: 0, Creator: MockAccount("0"), Amount: 50, Price: 25}, + {Id: 1, Creator: MockAccount("1"), Amount: 200, Price: 20}, + {Id: 2, Creator: MockAccount("2"), Amount: 10, Price: 15}, + }, + Remaining: types.Order{Id: 10, Creator: MockAccount("1"), Amount: 0, Price: 30}, + Liquidated: types.Order{Id: 2, Creator: MockAccount("2"), Amount: 20, Price: 15}, + Purchase: int32(20), + Match: true, + Filled: true, + } + simulateLiquidateFromBuyOrder(t, inputBook, inputOrder, expected) + + // Entirely filled (30 = 30) + inputOrder = types.Order{Id: 10, Creator: MockAccount("1"), Amount: 30, Price: 30} + expected = liquidateBuyRes{ + Book: []types.Order{ + {Id: 0, Creator: MockAccount("0"), Amount: 50, Price: 25}, + {Id: 1, Creator: MockAccount("1"), Amount: 200, Price: 20}, + }, + Remaining: types.Order{Id: 10, Creator: MockAccount("1"), Amount: 0, Price: 30}, + Liquidated: types.Order{Id: 2, Creator: MockAccount("2"), Amount: 30, Price: 15}, + Purchase: int32(30), + Match: true, + Filled: true, + } + simulateLiquidateFromBuyOrder(t, inputBook, inputOrder, expected) + + // Not filled and entirely liquidated (60 > 30) + inputOrder = types.Order{Id: 10, Creator: MockAccount("1"), Amount: 60, Price: 30} + expected = liquidateBuyRes{ + Book: []types.Order{ + {Id: 0, Creator: MockAccount("0"), Amount: 50, Price: 25}, + {Id: 1, Creator: MockAccount("1"), Amount: 200, Price: 20}, + }, + Remaining: types.Order{Id: 10, Creator: MockAccount("1"), Amount: 30, Price: 30}, + Liquidated: types.Order{Id: 2, Creator: MockAccount("2"), Amount: 30, Price: 15}, + Purchase: int32(30), + Match: true, + Filled: false, + } + simulateLiquidateFromBuyOrder(t, inputBook, inputOrder, expected) +} + +type fillBuyRes struct { + Book []types.Order + Remaining types.Order + Liquidated []types.Order + Purchase int32 + Filled bool +} + +func simulateFillBuyOrder( + t *testing.T, + inputList []types.Order, + inputOrder types.Order, + expected fillBuyRes, +) { + book := OrderListToSellOrderBook(inputList) + expectedBook := OrderListToSellOrderBook(expected.Book) + + require.True(t, sort.SliceIsSorted(book.Book.Orders, func(i, j int) bool { + return book.Book.Orders[i].Price > book.Book.Orders[j].Price + })) + require.True(t, sort.SliceIsSorted(expectedBook.Book.Orders, func(i, j int) bool { + return expectedBook.Book.Orders[i].Price > expectedBook.Book.Orders[j].Price + })) + + remaining, liquidated, purchase, filled := book.FillBuyOrder(inputOrder) + + require.Equal(t, expectedBook, book) + require.Equal(t, expected.Remaining, remaining) + require.Equal(t, expected.Liquidated, liquidated) + require.Equal(t, expected.Purchase, purchase) + require.Equal(t, expected.Filled, filled) +} + +func TestFillBuyOrder(t *testing.T) { + var inputBook []types.Order + + // Empty book + inputOrder := types.Order{Id: 10, Creator: MockAccount("1"), Amount: 30, Price: 10} + expected := fillBuyRes{ + Book: []types.Order{}, + Remaining: inputOrder, + Liquidated: []types.Order(nil), + Purchase: int32(0), + Filled: false, + } + simulateFillBuyOrder(t, inputBook, inputOrder, expected) + + // No match + inputBook = []types.Order{ + {Id: 0, Creator: MockAccount("0"), Amount: 50, Price: 25}, + {Id: 1, Creator: MockAccount("1"), Amount: 200, Price: 20}, + {Id: 2, Creator: MockAccount("2"), Amount: 30, Price: 15}, + } + expected = fillBuyRes{ + Book: inputBook, + Remaining: inputOrder, + Liquidated: []types.Order(nil), + Purchase: int32(0), + Filled: false, + } + simulateFillBuyOrder(t, inputBook, inputOrder, expected) + + // First order liquidated, not filled + inputOrder = types.Order{Id: 10, Creator: MockAccount("1"), Amount: 60, Price: 18} + expected = fillBuyRes{ + Book: []types.Order{ + {Id: 0, Creator: MockAccount("0"), Amount: 50, Price: 25}, + {Id: 1, Creator: MockAccount("1"), Amount: 200, Price: 20}, + }, + Remaining: types.Order{Id: 10, Creator: MockAccount("1"), Amount: 30, Price: 18}, + Liquidated: []types.Order{ + {Id: 2, Creator: MockAccount("2"), Amount: 30, Price: 15}, + }, + Purchase: int32(30), + Filled: false, + } + simulateFillBuyOrder(t, inputBook, inputOrder, expected) + + // Filled with two order + inputOrder = types.Order{Id: 10, Creator: MockAccount("1"), Amount: 60, Price: 22} + expected = fillBuyRes{ + Book: []types.Order{ + {Id: 0, Creator: MockAccount("0"), Amount: 50, Price: 25}, + {Id: 1, Creator: MockAccount("1"), Amount: 170, Price: 20}, + }, + Remaining: types.Order{Id: 10, Creator: MockAccount("1"), Amount: 0, Price: 22}, + Liquidated: []types.Order{ + {Id: 2, Creator: MockAccount("2"), Amount: 30, Price: 15}, + {Id: 1, Creator: MockAccount("1"), Amount: 30, Price: 20}, + }, + Purchase: int32(30 + 30), + Filled: true, + } + simulateFillBuyOrder(t, inputBook, inputOrder, expected) + + // Not filled, sell order book liquidated + inputOrder = types.Order{Id: 10, Creator: MockAccount("1"), Amount: 300, Price: 30} + expected = fillBuyRes{ + Book: []types.Order{}, + Remaining: types.Order{Id: 10, Creator: MockAccount("1"), Amount: 20, Price: 30}, + Liquidated: []types.Order{ + {Id: 2, Creator: MockAccount("2"), Amount: 30, Price: 15}, + {Id: 1, Creator: MockAccount("1"), Amount: 200, Price: 20}, + {Id: 0, Creator: MockAccount("0"), Amount: 50, Price: 25}, + }, + Purchase: int32(30 + 200 + 50), + Filled: false, + } + simulateFillBuyOrder(t, inputBook, inputOrder, expected) +} +``` + +## Successful Test Output + +When the tests are successful, your output is: + +``` +ok interchange/x/dex/types 0.550s +``` diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/07-interchange/_category_.json b/docs/versioned_docs/version-v0.27.1/02-guide/07-interchange/_category_.json new file mode 100644 index 0000000000..aab4fa0969 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/07-interchange/_category_.json @@ -0,0 +1,5 @@ +{ + "label": "Advanced Module: Interchange", + "position": 8, + "link": null + } \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/08-debug.md b/docs/versioned_docs/version-v0.27.1/02-guide/08-debug.md new file mode 100644 index 0000000000..e1b578a9fe --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/08-debug.md @@ -0,0 +1,209 @@ +--- +description: Debugging your Cosmos SDK blockchain +--- + +# Debugging a chain + +Ignite chain debug command can help you find issues during development. It uses +[Delve](https://github.com/go-delve/delve) debugger which enables you to +interact with your blockchain app by controlling the execution of the process, +evaluating variables, and providing information of thread / goroutine state, CPU +register state and more. + +## Debug Command + +The debug command requires that the blockchain app binary is build with +debugging support by removing optimizations and inlining. A debug binary is +built by default by the `ignite chain serve` command or can optionally be +created using the `--debug` flag when running `ignite chain init` or `ignite +chain build` sub-commands. + +To start a debugging session in the terminal run: + +``` +ignite chain debug +``` + +The command runs your blockchan app in the background, attaches to it and +launches a terminal debugger shell: + +``` +Type 'help' for list of commands. +(dlv) +``` + +At this point the blockchain app blocks execution, so you can set one or more +breakpoints before continuing execution. + +Use the +[break](https://github.com/go-delve/delve/blob/master/Documentation/cli/README.md#break) +(alias `b`) command to set any number of breakpoints using, for example the +`:` notation: + +``` +(dlv) break x/hello/client/cli/query_say_hello.go:14 +``` + +This command adds a breakpoint to the `x/hello/client/cli/query_say_hello.go` +file at line 14. + +Once all breakpoints are set resume blockchain execution using the +[continue](https://github.com/go-delve/delve/blob/master/Documentation/cli/README.md#continue) +(alias `c`) command: + +``` +(dlv) continue +``` + +The debugger will launch the shell and stop blockchain execution again when a +breakpoint is triggered. + +Within the debugger shell use the `quit` (alias `q`) or `exit` commands to stop +the blockchain app and exit the debugger. + +## Debug Server + +A debug server can optionally be started in cases where the default terminal +client is not desirable. When the server starts it first runs the blockchain +app, attaches to it and finally waits for a client connection. The default +server address is *tcp://127.0.0.1:30500* and it accepts both JSON-RPC or DAP +client connections. + +To start a debug server use the following flag: + +``` +ignite chain debug --server +``` + +To start a debug server with a custom address use the following flags: + +``` +ignite chain debug --server --server-address 127.0.0.1:30500 +``` + +The debug server stops automatically when the client connection is closed. + +## Debugging Clients + +### Gdlv: Multiplatform Delve UI + +[Gdlv](https://github.com/aarzilli/gdlv) is a graphical frontend to Delve for +Linux, Windows and macOS. + +Using it as debugging client is straightforward as it doesn't require any +configuration. Once the debug server is running and listening for client +requests connect to it by running: + +``` +gdlv connect 127.0.0.1:30500 +``` + +Setting breakpoints and continuing execution is done in the same way as Delve, +by using the `break` and `continue` commands. + +### Visual Studio Code + +Using [Visual Studio Code](https://code.visualstudio.com/) as debugging client +requires an initial configuration to allow it to connect to the debug server. + +Make sure that the [Go](https://code.visualstudio.com/docs/languages/go) +extension is installed. + +VS Code debugging is configured using the `launch.json` file which is usually +located inside the `.vscode` folder in your workspace. + +You can use the following launch configuration to set up VS Code as debugging +client: + +```json title=launch.json +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Connect to Debug Server", + "type": "go", + "request": "attach", + "mode": "remote", + "remotePath": "${workspaceFolder}", + "port": 30500, + "host": "127.0.0.1" + } + ] +} +``` + +Alternatively it's possible to create a custom `launch.json` file from the "Run +and Debug" panel. When prompted choose the Go debugger option labeled "Go: +Connect to Server" and enter the debug host address and then the port number. + +## Example: Debugging a Blockchain App + +In this short example we will be using Ignite CLI to create a new blockchain and +a query to be able to trigger a debugging breakpoint when the query is called. + +Create a new blockchain: + +``` +ignite scaffold chain hello +``` + +Scaffold a new query in the `hello` directory: + +``` +ignite scaffold query say-hello name --response name +``` + +The next step initializes the blockchain's data directory and compiles a debug +binary: + +``` +ignite chain init --debug +``` + +Once the initialization finishes launch the debugger shell: + +``` +ignite chain debug +``` + +Within the debugger shell create a breakpoint that will be triggered when the +`SayHello` function is called and then continue execution: + +``` +(dlv) break x/hello/keeper/query_say_hello.go:12 +(dlv) continue +``` + +From a different terminal use the `hellod` binary to call the query: + +``` +hellod query hello say-hello bob +``` + +A debugger shell will be launched when the breakpoint is triggered: + +``` + 7: "google.golang.org/grpc/codes" + 8: "google.golang.org/grpc/status" + 9: "hello/x/hello/types" + 10: ) + 11: +=> 12: func (k Keeper) SayHello(goCtx context.Context, req *types.QuerySayHelloRequest) (*types.QuerySayHelloResponse, error) { + 13: if req == nil { + 14: return nil, status.Error(codes.InvalidArgument, "invalid request") + 15: } + 16: + 17: ctx := sdk.UnwrapSDKContext(goCtx) +``` + +From then on you can use Delve commands like `next` (alias `n`) or `print` +(alias `p`) to control execution and print values. For example, to print the +*name* argument value use the `print` command followed by "req.Name": + +``` +(dlv) print req.Name +"bob" +``` + +Finally, use `quit` (alias `q`) to stop the blockchain app and finish the +debugging session. diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/09-docker.md b/docs/versioned_docs/version-v0.27.1/02-guide/09-docker.md new file mode 100644 index 0000000000..77edaa31ab --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/09-docker.md @@ -0,0 +1,142 @@ +--- +description: Run Ignite CLI using a Docker container. +--- + +# Running inside a Docker container + +You can run Ignite CLI inside a Docker container without installing the Ignite +CLI binary directly on your machine. + +Running Ignite CLI in Docker can be useful for various reasons; isolating your +test environment, running Ignite CLI on an unsupported operating system, or +experimenting with a different version of Ignite CLI without installing it. + +Docker containers are like virtual machines because they provide an isolated +environment to programs that runs inside them. In this case, you can run Ignite +CLI in an isolated environment. + +Experimentation and file system impact is limited to the Docker instance. The +host machine is not impacted by changes to the container. + +## Prerequisites + +Docker must be installed. See [Get Started with +Docker](https://www.docker.com/get-started). + +## Ignite CLI Commands in Docker + +After you scaffold and start a chain in your Docker container, all Ignite CLI +commands are available. Just type the commands after `docker run -ti +ignite/cli`. For example: + +```bash +docker run -ti ignitehq/cli -h +docker run -ti ignitehq/cli scaffold chain planet +docker run -ti ignitehq/cli chain serve +``` + +## Scaffolding a chain + +When Docker is installed, you can build a blockchain with a single command. + +Ignite CLI, and the chains you serve with Ignite CLI, persist some files. When +using the CLI binary directly, those files are located in `$HOME/.ignite` and +`$HOME/.cache`, but in the context of Docker it's better to use a directory +different from `$HOME`, so we use `$HOME/sdh`. This folder should be created +manually prior to the docker commands below, or else Docker creates it with the +root user. + +```bash +mkdir $HOME/sdh +``` + +To scaffold a blockchain `planet` in the `/apps` directory in the container, run +this command in a terminal window: + +```bash +docker run -ti -v $HOME/sdh:/home/tendermint -v $PWD:/apps ignitehq/cli:0.25.2 scaffold chain planet +``` + +Be patient, this command takes a minute or two to run because it does everything +for you: + +- Creates a container that runs from the `ignitehq/cli:0.25.2` image. +- Executes the Ignite CLI binary inside the image. +- `-v $HOME/sdh:/home/tendermint` maps the `$HOME/sdh` directory in your local + computer (the host machine) to the home directory `/home/tendermint` inside + the container. +- `-v $PWD:/apps` maps the current directory in the terminal window on the host + machine to the `/apps` directory in the container. You can optionally specify + an absolute path instead of `$PWD`. + + Using `-w` and `-v` together provides file persistence on the host machine. + The application source code on the Docker container is mirrored to the file + system of the host machine. + + **Note:** The directory name for the `-w` and `-v` flags can be a name other + than `/app`, but the same directory must be specified for both flags. If you + omit `-w` and `-v`, the changes are made in the container only and are lost + when that container is shut down. + +## Starting a blockchain + +To start the blockchain node in the Docker container you just created, run this +command: + +```bash +docker run -ti -v $HOME/sdh:/home/tendermint -v $PWD:/apps -p 1317:1317 -p 26657:26657 ignitehq/cli:0.25.2 chain serve -p planet +``` + +This command does the following: + +- `-v $HOME/sdh:/home/tendermint` maps the `$HOME/sdh` directory in your local + computer (the host machine) to the home directory `/home/tendermint` inside + the container. +- `-v $PWD:/apps` persists the scaffolded app in the container to the host + machine at current working directory. +- `serve -p planet` specifies to use the `planet` directory that contains the + source code of the blockchain. +- `-p 1317:1317` maps the API server port (cosmos-sdk) to the host machine to + forward port 1317 listening inside the container to port 1317 on the host + machine. +- `-p 26657:26657` maps RPC server port 26657 (tendermint) on the host machine + to port 26657 in Docker. +- After the blockchain is started, open `http://localhost:26657` to see the + Tendermint API. +- The `-v` flag specifies for the container to access the application's source + code from the host machine, so it can build and run it. + +## Versioning + +You can specify which version of Ignite CLI to install and run in your Docker +container. + +### Latest version + +- By default, `ignite/cli` resolves to `ignite/cli:latest`. +- The `latest` image tag is always the latest stable [Ignite CLI + release](https://github.com/ignite/cli/releases). + +For example, if latest release is +[v0.25.2](https://github.com/ignite/cli/releases/tag/v0.25.2), the `latest` tag +points to the `0.25.2` tag. + +### Specific version + +You can specify to use a specific version of Ignite CLI. All available tags are +in the [ignite/cli +image](https://hub.docker.com/r/ignite/cli/tags?page=1&ordering=last_updated) on +Docker Hub. + +For example: + +- Use `ignitehq/cli:0.25.2` (without the `v` prefix) to use version `0.25.2`. +- Use `ignitehq/cli` to use the latest version. +- Use `ignitehq/cli:main` to use the `main` branch, so you can experiment with + the upcoming version. + +To get the latest image, run `docker pull`. + +```bash +docker pull ignitehq/cli:main +``` diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/10-simapp.md b/docs/versioned_docs/version-v0.27.1/02-guide/10-simapp.md new file mode 100644 index 0000000000..fd2dcea96f --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/10-simapp.md @@ -0,0 +1,164 @@ +--- +sidebar_position: 10 +description: Test different scenarios for your chain. +--- + +# Chain simulation + +The Ignite CLI chain simulator can help you to run your chain based in +randomized inputs for you can make fuzz testing and also benchmark test for your +chain, simulating the messages, blocks, and accounts. You can scaffold a +template to perform simulation testing in each module along with a boilerplate +simulation methods for each scaffolded message. + +## Module simulation + +Every new module that is scaffolded with Ignite CLI implements the Cosmos SDK +[Module +Simulation](https://docs.cosmos.network/main/building-modules/simulator.html). + +- Each new message creates a file with the simulation methods required for the + tests. +- Scaffolding a `CRUD` type like a `list` or `map` creates a simulation file + with `create`, `update`, and `delete` simulation methods in the + `x//simulation` folder and registers these methods in + `x//module_simulation.go`. +- Scaffolding a single message creates an empty simulation method to be + implemented by the user. + +We recommend that you maintain the simulation methods for each new modification +into the message keeper methods. + +Every simulation is weighted because the sender of the operation is assigned +randomly. The weight defines how much the simulation calls the message. + +For better randomizations, you can define a random seed. The simulation with the +same random seed is deterministic with the same output. + +## Scaffold a simulation + +To create a new chain: + +``` +ignite scaffold chain mars +``` + +Review the empty `x/mars/simulation` folder and the +`x/mars/module_simulation.go` file to see that a simulation is not registered. + +Now, scaffold a new message: + +``` +ignite scaffold list user address balance:uint state +``` + +A new file `x/mars/simulation/user.go` is created and is registered with the +weight in the `x/mars/module_simulation.go` file. + +Be sure to define the proper simulation weight with a minimum weight of 0 and a +maximum weight of 100. + +For this example, change the `defaultWeightMsgDeleteUser` to 30 and the +`defaultWeightMsgUpdateUser` to 50. + +Run the `BenchmarkSimulation` method into `app/simulation_test.go` to run +simulation tests for all modules: + +``` +ignite chain simulate +``` + +You can also define flags that are provided by the simulation. Flags are defined +by the method `simapp.GetSimulatorFlags()`: + +``` +ignite chain simulate -v --numBlocks 200 --blockSize 50 --seed 33 +``` + +Wait for the entire simulation to finish and check the result of the messages. + +The default `go test` command works to run the simulation: + +``` +go test -v -benchmem -run=^$ -bench ^BenchmarkSimulation -cpuprofile cpu.out ./app -Commit=true +``` + +### Skip message + +Use logic to avoid sending a message without returning an error. Return only +`simtypes.NoOpMsg(...)` into the simulation message handler. + +## Params + +Scaffolding a module with params automatically adds the module in the +`module_simulaton.go` file: + +``` +ignite s module earth --params channel:string,minLaunch:uint,maxLaunch:int +``` + +After the parameters are scaffolded, change the +`x//module_simulation.go` file to set the random parameters into the +`RandomizedParams` method. The simulation will change the params randomly +according to call the function. + +## Invariants + +Simulating a chain can help you prevent [chain invariants +errors](https://docs.cosmos.network/main/building-modules/invariants.html). An +invariant is a function called by the chain to check if something broke, +invalidating the chain data. To create a new invariant and check the chain +integrity, you must create a method to validate the invariants and register all +invariants. + + +For example, in `x/earth/keeper/invariants.go`: + +```go title="x/earth/keeper/invariants.go" +package keeper + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/tendermint/spn/x/launch/types" +) + +const zeroLaunchTimestampRoute = "zero-launch-timestamp" + +// RegisterInvariants registers all module invariants +func RegisterInvariants(ir sdk.InvariantRegistry, k Keeper) { + ir.RegisterRoute(types.ModuleName, zeroLaunchTimestampRoute, + ZeroLaunchTimestampInvariant(k)) +} + +// ZeroLaunchTimestampInvariant invariant that checks if the +// `LaunchTimestamp is zero +func ZeroLaunchTimestampInvariant(k Keeper) sdk.Invariant { + return func(ctx sdk.Context) (string, bool) { + all := k.GetAllChain(ctx) + for _, chain := range all { + if chain.LaunchTimestamp == 0 { + return sdk.FormatInvariant( + types.ModuleName, zeroLaunchTimestampRoute, + "LaunchTimestamp is not set while LaunchTriggered is set", + ), true + } + } + return "", false + } +} +``` + +Now, register the keeper invariants into the `x/earth/module.go` file: + +```go +package earth + +// ... + +// RegisterInvariants registers the capability module's invariants. +func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { + keeper.RegisterInvariants(ir, am.keeper) +} +``` diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/_category_.json b/docs/versioned_docs/version-v0.27.1/02-guide/_category_.json new file mode 100644 index 0000000000..4de109a727 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/02-guide/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Develop a chain", + "link": null +} \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/images/api.png b/docs/versioned_docs/version-v0.27.1/02-guide/images/api.png new file mode 100644 index 0000000000..2034f2bdac Binary files /dev/null and b/docs/versioned_docs/version-v0.27.1/02-guide/images/api.png differ diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/images/packet_sendpost.png b/docs/versioned_docs/version-v0.27.1/02-guide/images/packet_sendpost.png new file mode 100644 index 0000000000..c96cdcdd3e Binary files /dev/null and b/docs/versioned_docs/version-v0.27.1/02-guide/images/packet_sendpost.png differ diff --git a/docs/versioned_docs/version-v0.27.1/03-clients/01-go-client.md b/docs/versioned_docs/version-v0.27.1/03-clients/01-go-client.md new file mode 100644 index 0000000000..6f01677c41 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/03-clients/01-go-client.md @@ -0,0 +1,300 @@ +--- +description: Blockchain client in Go +title: Go client +--- + +# A client in the Go programming language + +In this tutorial, we will show you how to create a standalone Go program that +serves as a client for a blockchain. We will use the Ignite CLI to set up a +standard blockchain. To communicate with the blockchain, we will utilize the +`cosmosclient` package, which provides an easy-to-use interface for interacting +with the blockchain. You will learn how to use the `cosmosclient` package to +send transactions and query the blockchain. By the end of this tutorial, you +will have a good understanding of how to build a client for a blockchain using +Go and the `cosmosclient` package. + +## Create a blockchain + +To create a blockchain using the Ignite CLI, use the following command: + +``` +ignite scaffold chain blog +``` + +This will create a new Cosmos SDK blockchain called "blog". + +Once the blockchain has been created, you can generate code for a "blog" model +that will enable you to perform create, read, update, and delete (CRUD) +operations on blog posts. To do this, you can use the following command: + +``` +cd blog +ignite scaffold list post title body +``` + +This will generate the necessary code for the "blog" model, including functions +for creating, reading, updating, and deleting blog posts. With this code in +place, you can now use your blockchain to perform CRUD operations on blog posts. +You can use the generated code to create new blog posts, retrieve existing ones, +update their content, and delete them as needed. This will give you a fully +functional Cosmos SDK blockchain with the ability to manage blog posts. + +Start your blockchain node with the following command: + +``` +ignite chain serve +``` + +## Creating a blockchain client + +Create a new directory called `blogclient` on the same level as `blog` +directory. As the name suggests, `blogclient` will contain a standalone Go +program that acts as a client to your `blog` blockchain. + +```bash +mkdir blogclient +``` + +This command will create a new directory called `blogclient` in your current +location. If you type `ls` in your terminal window, you should see both the +`blog` and `blogclient` directories listed. + +To initialize a new Go package inside the `blogclient` directory, you can use +the following command: + +``` +cd blogclient +go mod init blogclient +``` + +This will create a `go.mod` file in the `blogclient` directory, which contains +information about the package and the Go version being used. + +To import dependencies for your package, you can add the following code to the +`go.mod` file: + +```text title="blogclient/go.mod" +module blogclient + +go 1.19 + +require ( + blog v0.0.0-00010101000000-000000000000 + github.com/ignite/cli v0.25.2 +) + +replace blog => ../blog +replace github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 +``` + +Your package will import two dependencies: + +* `blog`, which contains `types` of messages and a query client +* `ignite` for the `cosmosclient` package + +The `replace` directive uses the package from the local `blog` directory and is +specified as a relative path to the `blogclient` directory. + +Cosmos SDK uses a custom version of the `protobuf` package, so use the `replace` +directive to specify the correct dependency. + +Finally, install dependencies for your `blogclient`: + +```bash +go mod tidy +``` + +### Main logic of the client in `main.go` + +Create a `main.go` file inside the `blogclient` directory and add the following +code: + +```go title="blogclient/main.go" +package main + +import ( + "context" + "fmt" + "log" + + // Importing the general purpose Cosmos blockchain client + "github.com/ignite/cli/ignite/pkg/cosmosclient" + + // Importing the types package of your blog blockchain + "blog/x/blog/types" +) + +func main() { + ctx := context.Background() + addressPrefix := "cosmos" + + // Create a Cosmos client instance + client, err := cosmosclient.New(ctx, cosmosclient.WithAddressPrefix(addressPrefix)) + if err != nil { + log.Fatal(err) + } + + // Account `alice` was initialized during `ignite chain serve` + accountName := "alice" + + // Get account from the keyring + account, err := client.Account(accountName) + if err != nil { + log.Fatal(err) + } + + addr, err := account.Address(addressPrefix) + if err != nil { + log.Fatal(err) + } + + // Define a message to create a post + msg := &types.MsgCreatePost{ + Creator: addr, + Title: "Hello!", + Body: "This is the first post", + } + + // Broadcast a transaction from account `alice` with the message + // to create a post store response in txResp + txResp, err := client.BroadcastTx(ctx, account, msg) + if err != nil { + log.Fatal(err) + } + + // Print response from broadcasting a transaction + fmt.Print("MsgCreatePost:\n\n") + fmt.Println(txResp) + + // Instantiate a query client for your `blog` blockchain + queryClient := types.NewQueryClient(client.Context()) + + // Query the blockchain using the client's `PostAll` method + // to get all posts store all posts in queryResp + queryResp, err := queryClient.PostAll(ctx, &types.QueryAllPostRequest{}) + if err != nil { + log.Fatal(err) + } + + // Print response from querying all the posts + fmt.Print("\n\nAll posts:\n\n") + fmt.Println(queryResp) +} +``` + +The code above creates a standalone Go program that acts as a client to the +`blog` blockchain. It begins by importing the required packages, including the +general purpose Cosmos blockchain client and the `types` package of the `blog` +blockchain. + +In the `main` function, the code creates a Cosmos client instance and sets the +address prefix to "cosmos". It then retrieves an account named `"alice"` from +the keyring and gets the address of the account using the address prefix. + +Next, the code defines a message to create a blog post with the title "Hello!" +and body "This is the first post". It then broadcasts a transaction from the +account "alice" with the message to create the post, and stores the response in +the variable `txResp`. + +The code then instantiates a query client for the blog blockchain and uses it to +query the blockchain to retrieve all the posts. It stores the response in the +variable `queryResp` and prints it to the console. + +Finally, the code prints the response from broadcasting the transaction to the +console. This allows the user to see the results of creating and querying a blog +post on the `blog` blockchain using the client. + +To find out more about the `cosmosclient` package, you can refer to the Go +package documentation for +[`cosmosclient`](https://pkg.go.dev/github.com/ignite/cli/ignite/pkg/cosmosclient). +This documentation provides information on how to use the `Client` type with +`Options` and `KeyringBackend`. + +## Run the blockchain and the client + +Make sure your blog blockchain is still running with `ignite chain serve`. + +Run the blockchain client: + +```bash +go run main.go +``` + +If the command is successful, the results of running the command will be printed +to the terminal. The output may include some warnings, which can be ignored. + +```yml +MsgCreatePost: + +code: 0 +codespace: "" +data: 12220A202F626C6F672E626C6F672E4D7367437265617465506F7374526573706F6E7365 +events: +- attributes: + - index: true + key: ZmVl + value: null + - index: true + key: ZmVlX3BheWVy + value: Y29zbW9zMWR6ZW13NzZ3enQ3cDBnajd3MzQyN2E0eHg3MjRkejAzd3hnOGhk + type: tx +- attributes: + - index: true + key: YWNjX3NlcQ== + value: Y29zbW9zMWR6ZW13NzZ3enQ3cDBnajd3MzQyN2E0eHg3MjRkejAzd3hnOGhkLzE= + type: tx +- attributes: + - index: true + key: c2lnbmF0dXJl + value: UWZncUJCUFQvaWxWVzJwNUJNTngzcDlvRzVpSXp0elhXdE9yMHcwVE00OEtlSkRqR0FEdU9VNjJiY1ZRNVkxTHdEbXNuYUlsTmc3VE9uMnJ2ZWRHSlE9PQ== + type: tx +- attributes: + - index: true + key: YWN0aW9u + value: L2Jsb2cuYmxvZy5Nc2dDcmVhdGVQb3N0 + type: message +gas_used: "52085" +gas_wanted: "300000" +height: "20" +info: "" +logs: +- events: + - attributes: + - key: action + value: /blog.blog.MsgCreatePost + type: message + log: "" + msg_index: 0 +raw_log: '[{"msg_index":0,"events":[{"type":"message","attributes":[{"key":"action","value":"/blog.blog.MsgCreatePost"}]}]}]' +timestamp: "" +tx: null +txhash: 4F53B75C18254F96EF159821DDD665E965DBB576A5AC2B94CE863EB62E33156A + +All posts: + +Post: pagination: +``` + +As you can see the client has successfully broadcasted a transaction and queried +the chain for blog posts. + +Please note, that some values in the output on your terminal (like transaction +hash and block height) might be different from the output above. + +You can confirm the new post with using the `blogd q blog list-post` command: + +```yaml +Post: +- body: This is the first post + creator: cosmos1dzemw76wzt7p0gj7w3427a4xx724dz03wxg8hd + id: "0" + title: Hello! +pagination: + next_key: null + total: "0" +``` + +Great job! You have successfully completed the process of creating a Go client +for your Cosmos SDK blockchain, submitting a transaction, and querying the +chain. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.27.1/03-clients/02-typescript.md b/docs/versioned_docs/version-v0.27.1/03-clients/02-typescript.md new file mode 100644 index 0000000000..3c8d29e81d --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/03-clients/02-typescript.md @@ -0,0 +1,431 @@ +--- +description: Information about the generated TypeScript client code. +--- + +# TypeScript frontend + +Ignite offers powerful functionality for generating client-side code for your +blockchain. Think of this as a one-click client SDK generation tailored +specifically for your blockchain. + +See [`ignite generate ts-client --help`](../references/cli#ignite-generate-ts-client) learn +more on how to use TypeScript code generation. + +## Starting a node + +Create a new blockchain with `ignite scaffold chain`. You can use an existing +blockchain project if you have one, instead. + +``` +ignite scaffold chain example +``` + +For testing purposes add a new account to `config.yml` with a mnemonic: + +```yml title="config.yml" +accounts: + - name: frank + coins: ["1000token", "100000000stake"] + mnemonic: play butter frown city voyage pupil rabbit wheat thrive mind skate turkey helmet thrive door either differ gate exhibit impose city swallow goat faint +``` + +Run a command to generate TypeScript clients for both standard and custom Cosmos +SDK modules: + +``` +ignite generate ts-client --clear-cache +``` + +Run a command to start your blockchain node: + +``` +ignite chain serve -r +``` + +## Setting up a TypeScript frontend client + +The best way to get started building with the TypeScript client is by using +[Vite](https://vitejs.dev). Vite provides boilerplate code for +vanilla TS projects as well as React, Vue, Lit, Svelte and Preact frameworks. +You can find additional information at the [Vite Getting Started +guide](https://vitejs.dev/guide). + +You will also need to [polyfill](https://developer.mozilla.org/en-US/docs/Glossary/Polyfill) the client's dependencies. The following is an +example of setting up a vanilla TS project with the necessary polyfills: + +```bash +npm create vite@latest my-frontend-app -- --template vanilla-ts +cd my-frontend-app +npm install --save-dev @esbuild-plugins/node-globals-polyfill @rollup/plugin-node-resolve +``` + +You must then create the necessary `vite.config.ts` file. + +```typescript title="my-frontend-app/vite.config.ts" +import { nodeResolve } from "@rollup/plugin-node-resolve"; +import { NodeGlobalsPolyfillPlugin } from "@esbuild-plugins/node-globals-polyfill"; +import { defineConfig } from "vite"; + +export default defineConfig({ + plugins: [nodeResolve()], + + optimizeDeps: { + esbuildOptions: { + define: { + global: "globalThis", + }, + plugins: [ + NodeGlobalsPolyfillPlugin({ + buffer: true, + }), + ], + }, + }, +}); +``` + +You are then ready to use the generated client code inside this project directly +or by publishing the client and installing it like any other `npm` package. + +After the chain starts, you will see Frank's address is +`cosmos13xkhcx2dquhqdml0k37sr7yndquwteuvt2cml7`. We'll be using Frank's account +for querying data and broadcasting transactions in the next section. + +## Querying + +The code generated in `ts-client` comes with a `package.json` file ready to +publish which you can modify to suit your needs. To use`ts-client` install the +required dependencies: + +``` +cd ts-client +npm install +``` + +The client is based on a modular architecture where you can configure a client +class to support the modules you need and instantiate it. + +By default, the generated client exports a client class that includes all the +Cosmos SDK, custom and 3rd party modules in use in your project. + +To instantiate the client you need to provide environment information (endpoints +and chain prefix). For querying that's all you need: + +```typescript title="my-frontend-app/src/main.ts" +import { Client } from "../../ts-client"; + +const client = new Client( + { + apiURL: "http://localhost:1317", + rpcURL: "http://localhost:26657", + prefix: "cosmos", + } +); +``` + +The example above uses `ts-client` from a local directory. If you have published +your `ts-client` on `npm` replace `../../ts-client` with a package name. + +The resulting client instance contains namespaces for each module, each with a +`query` and `tx` namespace containing the module's relevant querying and +transacting methods with full type and auto-completion support. + +To query for a balance of an address: + +```typescript +const balances = await client.CosmosBankV1Beta1.query.queryAllBalances( + 'cosmos13xkhcx2dquhqdml0k37sr7yndquwteuvt2cml7' +); +``` + +## Broadcasting a transaction + +Add signing capabilities to the client by creating a wallet from a mnemonic +(we're using the Frank's mnemonic added to `config.yml` earlier) and passing it +as an optional argument to `Client()`. The wallet implements the CosmJS +OfflineSigner` interface. + +```typescript title="my-frontend-app/src/main.ts" +import { Client } from "../../ts-client"; +// highlight-start +import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing"; + +const mnemonic = + "play butter frown city voyage pupil rabbit wheat thrive mind skate turkey helmet thrive door either differ gate exhibit impose city swallow goat faint"; +const wallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic); +// highlight-end + +const client = new Client( + { + apiURL: "http://localhost:1317", + rpcURL: "http://localhost:26657", + prefix: "cosmos", + }, + // highlight-next-line + wallet +); +``` + +Broadcasting a transaction: + +```typescript title="my-frontend-app/src/main.ts" +const tx_result = await client.CosmosBankV1Beta1.tx.sendMsgSend({ + value: { + amount: [ + { + amount: '200', + denom: 'token', + }, + ], + fromAddress: 'cosmos13xkhcx2dquhqdml0k37sr7yndquwteuvt2cml7', + toAddress: 'cosmos15uw6qpxqs6zqh0zp3ty2ac29cvnnzd3qwjntnc', + }, + fee: { + amount: [{ amount: '0', denom: 'stake' }], + gas: '200000', + }, + memo: '', +}) +``` + +## Broadcasting a transaction with a custom message + +If your chain already has custom messages defined, you can use those. If not, +we'll be using Ignite's scaffolded code as an example. Create a post with CRUD +messages: + +``` +ignite scaffold list post title body +``` + +After adding messages to your chain you may need to re-generate the TypeScript +client: + +``` +ignite generate ts-client --clear-cache +``` + +Broadcast a transaction containing the custom `MsgCreatePost`: + +```typescript title="my-frontend-app/src/main.ts" +import { Client } from "../../ts-client"; +import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing"; + +const mnemonic = + "play butter frown city voyage pupil rabbit wheat thrive mind skate turkey helmet thrive door either differ gate exhibit impose city swallow goat faint"; +const wallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic); + +const client = new Client( + { + apiURL: "http://localhost:1317", + rpcURL: "http://localhost:26657", + prefix: "cosmos", + }, + wallet +); +// highlight-start +const tx_result = await client.ExampleExample.tx.sendMsgCreatePost({ + value: { + title: 'foo', + body: 'bar', + creator: 'cosmos13xkhcx2dquhqdml0k37sr7yndquwteuvt2cml7', + }, + fee: { + amount: [{ amount: '0', denom: 'stake' }], + gas: '200000', + }, + memo: '', +}) +// highlight-end +``` + +## Lightweight client + +If you prefer, you can construct a lighter client using only the modules you are +interested in by importing the generic client class and expanding it with the +modules you need: + +```typescript title="my-frontend-app/src/main.ts" +// highlight-start +import { IgniteClient } from '../../ts-client/client' +import { Module as CosmosBankV1Beta1 } from '../../ts-client/cosmos.bank.v1beta1' +import { Module as CosmosStakingV1Beta1 } from '../../ts-client/cosmos.staking.v1beta1' +// highlight-end +import { DirectSecp256k1HdWallet } from '@cosmjs/proto-signing' + +const mnemonic = + 'play butter frown city voyage pupil rabbit wheat thrive mind skate turkey helmet thrive door either differ gate exhibit impose city swallow goat faint' +const wallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic) +// highlight-next-line +const Client = IgniteClient.plugin([CosmosBankV1Beta1, CosmosStakingV1Beta1]) + +const client = new Client( + { + apiURL: 'http://localhost:1317', + rpcURL: 'http://localhost:26657', + prefix: 'cosmos', + }, + wallet, +) +``` + +## Broadcasting a multi-message transaction + +You can also construct TX messages separately and send them in a single TX using +a global signing client like so: + +```typescript title="my-frontend-app/src/main.ts" +const msg1 = await client.CosmosBankV1Beta1.tx.msgSend({ + value: { + amount: [ + { + amount: '200', + denom: 'token', + }, + ], + fromAddress: 'cosmos13xkhcx2dquhqdml0k37sr7yndquwteuvt2cml7', + toAddress: 'cosmos15uw6qpxqs6zqh0zp3ty2ac29cvnnzd3qwjntnc', + }, +}) + +const msg2 = await client.CosmosBankV1Beta1.tx.msgSend({ + value: { + amount: [ + { + amount: '200', + denom: 'token', + }, + ], + fromAddress: 'cosmos13xkhcx2dquhqdml0k37sr7yndquwteuvt2cml7', + toAddress: 'cosmos15uw6qpxqs6zqh0zp3ty2ac29cvnnzd3qwjntnc', + }, +}) + +const tx_result = await client.signAndBroadcast( + [msg1, msg2], + { + amount: [{ amount: '0', denom: 'stake' }], + gas: '200000', + }, + '', +) +``` + +Finally, for additional ease-of-use, apart from the modular client mentioned +above, each generated module is usable on its own in a stripped-down way by +exposing a separate txClient and queryClient. + +```typescript title="my-frontend-app/src/main.ts" +import { txClient } from '../../ts-client/cosmos.bank.v1beta1' +import { DirectSecp256k1HdWallet } from '@cosmjs/proto-signing' + +const mnemonic = + 'play butter frown city voyage pupil rabbit wheat thrive mind skate turkey helmet thrive door either differ gate exhibit impose city swallow goat faint' +const wallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic) + +const client = txClient({ + signer: wallet, + prefix: 'cosmos', + addr: 'http://localhost:26657', +}) + +const tx_result = await client.sendMsgSend({ + value: { + amount: [ + { + amount: '200', + denom: 'token', + }, + ], + fromAddress: 'cosmos13xkhcx2dquhqdml0k37sr7yndquwteuvt2cml7', + toAddress: 'cosmos15uw6qpxqs6zqh0zp3ty2ac29cvnnzd3qwjntnc', + }, + fee: { + amount: [{ amount: '0', denom: 'stake' }], + gas: '200000', + }, + memo: '', +}) +``` + +## Usage with Keplr + +Normally, Keplr provides a wallet object implementing the `OfflineSigner` +interface, so you can simply replace the `wallet` argument in client +instantiation with `window.keplr.getOfflineSigner(chainId)`. However, Keplr +requires information about your chain, like chain ID, denoms, fees, etc. +[`experimentalSuggestChain()`](https://docs.keplr.app/api/suggest-chain.html) is +a method Keplr provides to pass this information to the Keplr extension. + +The generated client makes this easier by offering a `useKeplr()` method that +automatically discovers the chain information and sets it up for you. Thus, you +can instantiate the client without a wallet and then call `useKeplr()` to enable +transacting via Keplr like so: + +```typescript title="my-frontend-app/src/main.ts" +import { Client } from '../../ts-client'; + +const client = new Client({ + apiURL: "http://localhost:1317", + rpcURL: "http://localhost:26657", + prefix: "cosmos" + } +); +await client.useKeplr(); +``` + +`useKeplr()` optionally accepts an object argument that contains one or more of +the same keys as the `ChainInfo` type argument of `experimentalSuggestChain()` +allowing you to override the auto-discovered values. + +For example, the default chain name and token precision (which are not recorded +on-chain) are set to ` Network` and `0` while the ticker for the denom +is set to the denom name in uppercase. If you want to override these, you can do +something like: + +```typescript title="my-frontend-app/src/main.ts" +import { Client } from '../../ts-client'; + +const client = new Client({ + apiURL: "http://localhost:1317", + rpcURL: "http://localhost:26657", + prefix: "cosmos" + } +); +await client.useKeplr({ + chainName: 'My Great Chain', + stakeCurrency: { + coinDenom: 'TOKEN', + coinMinimalDenom: 'utoken', + coinDecimals: '6', + }, +}) +``` + +## Wallet switching + +The client also allows you to switch out the wallet for a different one on an +already instantiated client like so: + +```typescript +import { Client } from '../../ts-client'; +import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing"; + +const mnemonic = + 'play butter frown city voyage pupil rabbit wheat thrive mind skate turkey helmet thrive door either differ gate exhibit impose city swallow goat faint' +const wallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic); + +const client = new Client({ + apiURL: "http://localhost:1317", + rpcURL: "http://localhost:26657", + prefix: "cosmos" + } +); +await client.useKeplr(); + +// broadcast transactions using the Keplr wallet + +client.useSigner(wallet); + +// broadcast transactions using the CosmJS wallet +``` diff --git a/docs/versioned_docs/version-v0.27.1/03-clients/03-vue.md b/docs/versioned_docs/version-v0.27.1/03-clients/03-vue.md new file mode 100644 index 0000000000..f6684f023a --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/03-clients/03-vue.md @@ -0,0 +1,174 @@ +# Vue frontend + +Welcome to this tutorial on using Ignite to develop a web application for your +blockchain with Vue 3. Ignite is a tool that simplifies the process of building +a blockchain application by providing a set of templates and generators that can +be used to get up and running quickly. + +One of the features of Ignite is its support for [Vue 3](https://vuejs.org/), a +popular JavaScript framework for building user interfaces. In this tutorial, you +will learn how to use Ignite to create a new blockchain and scaffold a Vue +frontend template. This will give you a basic foundation for your web +application and make it easier to get started building out the rest of your +application. + +Once you have your blockchain and Vue template set up, the next step is to +generate an API client. This will allow you to easily interact with your +blockchain from your web application, enabling you to retrieve data and make +transactions. By the end of this tutorial, you will have a fully functional web +application that is connected to your own blockchain. + +Prerequisites: + +* [Node.js](https://nodejs.org/en/) +* [Keplr](https://www.keplr.app/) Chrome extension + +## Create a blockchain and a Vue app + +Create a new blockchain project: + +``` +ignite scaffold chain example +``` + +To create a Vue frontend template, go to the `example` directory and run the +following command: + +``` +ignite scaffold vue +``` + +This will create a new Vue project in the `vue` directory. This project can be +used with any blockchain, but it depends on an API client to interact with the +blockchain. To generate an API client, run the following command in the +`example` directory: + +``` +ignite generate composables +``` + +This command generates two directories: + +* `ts-client`: a framework-agnostic TypeScript client that can be used to + interact with your blockchain. You can learn more about how to use this client + in the [TypeScript client tutorial](/clients/typescript). +* `vue/src/composables`: a collection of Vue 3 + [composables](https://vuejs.org/guide/reusability/composables.html) that wrap + the TypeScript client and make it easier to interact with your blockchain from + your Vue application. + +## Set up Keplr and an account + +Open your browser with the Keplr wallet extension installed. Follow [the +instructions](https://keplr.crunch.help/en/getting-started/creating-a-new-keplr-account) +to create a new account or use an existing one. Make sure to save the mnemonic +phrase as you will need it in the next step. + +Do not use a mnemonic phrase that is associated with an account that holds +assets you care about. If you do, you risk losing those assets. It's a good +practice to create a new account for development purposes. + +Add the account you're using in Keplr to your blockchain's `config.yml` file: + +```yml +accounts: + - name: alice + coins: [20000token, 200000000stake] + - name: bob + coins: [10000token, 100000000stake] + # highlight-start + - name: frank + coins: [10000token, 100000000stake] + mnemonic: struggle since inmate safe logic kite tag web win stay security wonder + # highlight-end +``` + +Replace the `struggle since...` mnemonic with the one you saved in the previous +step. + +Adding an account with a mnemonic to the config file will tell Ignite CLI to add +the account to the blockchain when you start it. This is useful for development +purposes, but you should not do this in production. + +## Start a blockchain and a Vue app + +In the `example` directory run the following command to start your blockchain: + +``` +ignite chain serve +``` + +To start your Vue application, go to the `vue` directory and run the following +command in a separate terminal window: + +``` +npm install && npm run dev +``` + +It is recommended to run `npm install` before starting your app with `npm run +dev` to ensure that all dependencies are installed (including the ones that the +API client has, see `vue/postinstall.js`). + +Open your browser and navigate to +[http://localhost:5173/](http://localhost:5173/). + +![Web app](/img/web-1.png) + +Press "Connect wallet", enter your password into Keplr and press "Approve" to +add your blockchain to Keplr. + + + +Make sure to select the account you're using for development purposes and the +"Example Network" in Keplr's blockchain dropdown. You should see a list of +assets in your Vue app. + +![Web app](/img/web-5.png) + +Congratulations! You have successfully created a client-side Vue application and +connected it to your blockchain. You can modify the source code of your Vue +application to build out the rest of your project. + +## Setting the address prefix + +It is necessary to set the correct address prefix in order for the Vue app to +properly interact with a Cosmos chain. The address prefix is used to identify +the chain that the app is connected to, and must match the prefix used by the +chain. + +By default, Ignite creates a chain with the the `cosmos` prefix. If you have +created your chain with `ignite scaffold chain ... --adddress-prefix foo` or +manually changed the prefix in the source code of the chain, you need to set the +prefix in the Vue app. + +There are two ways to set the address prefix in a Vue app. + +### Using an environment variable + +You can set the `VITE_ADDRESS_PREFIX` environment variable to the correct +address prefix for your chain. This will override the default prefix used by the +app. + +To set the `VITE_ADDRESS_PREFIX` environment variable, you can use the following +command: + +```bash +export VITE_ADDRESS_PREFIX=your-prefix +``` + +Replace `your-prefix` with the actual address prefix for your chain. + +### Setting address prefix in the code + +Alternatively, you can manually set the correct address prefix by replacing the +fallback value of the `prefix` variable in the file `./vue/src/env.ts`. + +To do this, open the file `./vue/src/env.ts` and find the following line: + +```ts title="./vue/src/env.ts" +const prefix = process.env.VITE_ADDRESS_PREFIX || 'your-prefix'; +``` + +Replace `your-prefix` with the actual address prefix for your chain. + +Save the file and restart the Vue app to apply the changes. diff --git a/docs/versioned_docs/version-v0.27.1/03-clients/04-react.md b/docs/versioned_docs/version-v0.27.1/03-clients/04-react.md new file mode 100644 index 0000000000..b9e7cff6f7 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/03-clients/04-react.md @@ -0,0 +1,130 @@ +# React frontend + +Welcome to this tutorial on using Ignite to develop a web application for your +blockchain with React. Ignite is a tool that simplifies the process of building +a blockchain application by providing a set of templates and generators that can +be used to get up and running quickly. + +One of the features of Ignite is its support for [React](https://reactjs.org/), a +popular JavaScript framework for building user interfaces. In this tutorial, you +will learn how to use Ignite to create a new blockchain and scaffold a React +frontend template. This will give you a basic foundation for your web +application and make it easier to get started building out the rest of your +application. + +Once you have your blockchain and React template set up, the next step is to +generate an API client. This will allow you to easily interact with your +blockchain from your web application, enabling you to retrieve data and make +transactions. By the end of this tutorial, you will have a fully functional web +application that is connected to your own blockchain. + +Prerequisites: + +* [Node.js](https://nodejs.org/en/) +* [Keplr](https://www.keplr.app/) Chrome extension + +## Create a blockchain and a React app + +Create a new blockchain project: + +``` +ignite scaffold chain example +``` + +To create a React frontend template, go to the `example` directory and run the +following command: + +``` +ignite scaffold react +``` + +This will create a new React project in the `react` directory. This project can be +used with any blockchain, but it depends on an API client to interact with the +blockchain. To generate an API client, run the following command in the +`example` directory: + +``` +ignite generate hooks +``` + +This command generates two directories: + +* `ts-client`: a framework-agnostic TypeScript client that can be used to + interact with your blockchain. You can learn more about how to use this client + in the [TypeScript client tutorial](/clients/typescript). +* `react/src/hooks`: a collection of + [React Hooks](https://reactjs.org/docs/hooks-intro.html) that wrap + the TypeScript client and make it easier to interact with your blockchain from + your React application. + +## Set up Keplr and an account + +Open your browser with the Keplr wallet extension installed. Follow [the +instructions](https://keplr.crunch.help/en/getting-started/creating-a-new-keplr-account) +to create a new account or use an existing one. Make sure to save the mnemonic +phrase as you will need it in the next step. + +Do not use a mnemonic phrase that is associated with an account that holds +assets you care about. If you do, you risk losing those assets. It's a good +practice to create a new account for development purposes. + +Add the account you're using in Keplr to your blockchain's `config.yml` file: + +```yml +accounts: + - name: alice + coins: [20000token, 200000000stake] + - name: bob + coins: [10000token, 100000000stake] + # highlight-start + - name: frank + coins: [10000token, 100000000stake] + mnemonic: struggle since inmate safe logic kite tag web win stay security wonder + # highlight-end +``` + +Replace the `struggle since...` mnemonic with the one you saved in the previous +step. + +Adding an account with a mnemonic to the config file will tell Ignite CLI to add +the account to the blockchain when you start it. This is useful for development +purposes, but you should not do this in production. + +## Start a blockchain and a React app + +In the `example` directory run the following command to start your blockchain: + +``` +ignite chain serve +``` + +To start your React application, go to the `react` directory and run the following +command in a separate terminal window: + +``` +npm install && npm run dev +``` + +It is recommended to run `npm install` before starting your app with `npm run +dev` to ensure that all dependencies are installed (including the ones that the +API client has, see `react/postinstall.js`). + +Open your browser and navigate to +[http://localhost:5173/](http://localhost:5173/). + +![Web app](/img/web-1.png) + +Press "Connect wallet", enter your password into Keplr and press "Approve" to +add your blockchain to Keplr. + + + +Make sure to select the account you're using for development purposes and the +"Example Network" in Keplr's blockchain dropdown. You should see a list of +assets in your React app. + +![Web app](/img/web-5.png) + +Congratulations! You have successfully created a client-side React application and +connected it to your blockchain. You can modify the source code of your React +application to build out the rest of your project. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.27.1/03-clients/_category_.json b/docs/versioned_docs/version-v0.27.1/03-clients/_category_.json new file mode 100644 index 0000000000..7cb006e423 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/03-clients/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Develop a client app", + "link": null +} \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.27.1/04-network/01-chain.md b/docs/versioned_docs/version-v0.27.1/04-network/01-chain.md new file mode 100644 index 0000000000..4edf0a91cb --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/04-network/01-chain.md @@ -0,0 +1,237 @@ +--- +sidebar_position: 1 +description: Ignite Chain. +--- + +# Ignite Chain + +## Introduction + +_Ignite is a blockchain to help launch Cosmos SDK-based blockchains._ + +Using Cosmos SDK and Ignite CLI, developers can quickly create a crypto application that is decentralized, economical for usage, and scalable. The Cosmos SDK framework allows developers to create sovereign application-specific blockchains that become part of the wider [Cosmos ecosystem](https://v1.cosmos.network/ecosystem/apps). Blockchains created with Cosmos SDK use a Proof-of-Stake (PoS) consensus protocol that requires validators to secure the chain. + +Even though tools like Ignite CLI simplify the development of a Cosmos SDK blockchain, launching a new chain is a highly complex process. One of the major challenges of developing and launching your own sovereign blockchain is ensuring the security of the underlying consensus. Since Cosmos SDK chains are based on the PoS consensus, each blockchain requires initial coin allocations and validators before they can be launched, which presents developers with significant challenges, such as determining their chain's tokenomics or coordinating a robust validator set. + +The initial coin allocations and validators are described in a JSON-formatted genesis file that is shared among all initial nodes in the network. This genesis file defines the initial state of the application. Based on PoS, secure chains require the initial allocation of coins to be well distributed so that no single validator holds more than 1/3 of all tokens and receives a disproportionate amount of voting power. + +Along with ensuring the security of the underlying consensus, another highly difficult task in launching a new blockchain is attracting a diverse set of validators for the genesis file. Many promising projects fail to capture the attention of a sufficient number of trustworthy validators to secure their chains due to a lack of resources or experience. + +The Ignite Chain has, therefore, been conceived to facilitate the launch of Cosmos SDK blockchains by helping developers to navigate the complexities of launching a blockchain and coordinate the genesis of a new chain. Using the decentralized nature of blockchain, Ignite's coordination features help blockchain builders connect with validators and investors, speeding up the time to market of their projects and chances of success. + +Commands to interact with Ignite Chain are integrated into Ignite CLI and allow launching chains from it. Integration with Ignite Chain allows the CLI to support the developer in the entire lifecycle of realizing a Cosmos project, from the development and experimentation of the blockchain to the launch of its mainnet. + +## What is Ignite Chain + +Ignite Chain is a secure platform that simplifies the launch of Cosmos SDK-based chains, lending vital resources and support at the coordination, preparation, and launch stages. Ignite provides the tools that blockchain projects need to overcome the complexities of launching their chain, from validator coordination and token issuance to fundraising and community building. + +Ignite facilitates the launch of new chains with an overall launch process during three phases: + +- Coordination +- Preparation +- Launch + +To reduce friction at each phase, Ignite provides an immutable and universal database for validator coordination. + +In the future, Ignite will also offer: + +- Token issuance: Ignite allows the issuance of tokens (called vouchers) that represent a share + allocation of a future mainnet network +- A fundraising platform for selling vouchers +- A permissionless framework to reward validator activities on a launched testnet network + +## Validator coordination + +To launch a chain in the Cosmos ecosystem, the validators must start nodes that connect to each other to create the new blockchain network. A node must be started from a file called the genesis file. The genesis file must be identical on all validator nodes before the new chain can be started. + +![genesis](./assets/genesis.png) + +The JSON-formatted genesis file contains information on the initial state of the chain, including coin allocations, the list of validators, various parameters for the chain like the maximum number of validators actively signing blocks, and the specific launch time. Because each validator has the same genesis file, the blockchain network starts automatically when the genesis time is reached. + +![launch](./assets/launch.png) + +### Ignite as a coordination source of truth + +Ignite Chain acts as a source of truth for new chains to coordinate a validator set and for validators to generate the genesis for a chain launch. The blockchain doesn’t directly store the final genesis file in its own ledger but rather stores information that allows generating the genesis file in a deterministic manner. + +The information stored on Ignite that supports deterministic generation of the genesis file for a specific chain launch is referred to as the _launch information_. When creating a new chain on Ignite, the coordinator provides the initial launch information. Then, through on-chain coordination, this launch information is updated by interacting with the blockchain by sending messages. When the chain is ready to be launched, the genesis file is generated by calling a genesis generation algorithm that uses the launch information. + +**GenesisGenerate(LaunchInformation) => genesis.json** + +The genesis generation algorithm is officially and formally specified. The official implementation of the genesis generation algorithm is developed in Go using Ignite CLI. However, any project is free to develop its own implementation of the algorithm as long as it complies with the specification of the algorithm. + +The genesis generation algorithm is not part of the on-chain protocol. In order to successfully launch a new chain, all validators must use the algorithm to generate their genesis using the launch information. The algorithm deterministically generates the genesis from the launch information that is stored on the Ignite chain. + +If any element of the launch information is censored, for example, removing an account balance, the launched chain reputation is negatively impacted and implies that the majority of validators agree on not using: + +- The tamper-proof launch information +- The official genesis generation algorithm + +Outside of the genesis generation, the genesis generation algorithm specification gives guidance on how to set up your network configuration. For example, the launch information can contain the addresses of the persistent peers of the blockchain network. + +![generation](./assets/generation.png) + +## Launch information + +Launch information can be created or updated in three different ways: + +1. Defined during chain creation but updatable by the coordinator after creation +2. Determined through coordination +3. Determined through specific on-chain logic not related to coordination + +### 1 - Launch information determined during chain creation: + +- `GenesisChainID`: The identifier for the network +- `SourceURL`: The URL of the git repository of the source code for building the blockchain + node binary +- `SourceHash`: The specific hash that identifies the release of the source code +- `InitialGenesis`: A multiformat structure that specifies the initial genesis for the chain + launch before running the genesis generation algorithm + +### 2 - Launch information determined through coordination: + +- `GenesisAccounts`: A list of genesis accounts for the chain, comprised of addresses with associated balances +- `VestingAccounts`: A list of genesis accounts with vesting options +- `GenesisValidators`: A list of the initial validators at chain launch +- `ParamChanges`: A list of module param changes in the genesis state + +### 3 - Launch information determined through on-chain logic: + +- `GenesisTime`: The timestamp for the network start, also referred to as LaunchTime + +### Initial genesis + +The launch information contains the initial genesis structure. This structure provides the information for generating the initial genesis before running the genesis generation algorithm and finalizing the genesis file. + +The initial genesis structure can be: + +- `DefaultGenesis`: the default genesis file is generated by the chain binary init command +- `GenesisURL`: the initial genesis for a chain launch is an existing genesis file that is + fetched from a URL and then modified with the required algorithm - this initial genesis type should be used when the initial genesis state is extensive, + containing a lot of accounts for token distribution, containing records for an + airdrop +- `GenesisConfig`: the initial genesis for a chain launch is generated from an Ignite CLI + config that contains genesis accounts and module parameters - this initial genesis type should be used when the coordinator doesn’t have extensive state for the initial genesis but some module parameters must be customized. For example, the staking bond denom for the staking token + +## Coordination process + +The coordination process starts immediately after the chain is created and ends when the coordinator triggers the launch of the chain. + +The launch information is updated during the coordination process. + +During the coordination process, any entity can send requests to the network. A request is an object whose content specifies updates to the launch information. + +The chain coordinator approves or rejects the requests: + +- If a request is approved, the content is applied to the launch information +- If the request is rejected, no change is made to the launch information + +The request creator can also directly reject or cancel the request. + +Each chain contains a request pool that contains all requests. Each request has a status: + +- _PENDING_: Waiting for the approval of the coordinator +- _APPROVED_: Approved by the coordinator, its content has been applied to the launch + information +- _REJECTED_: Rejected by the coordinator or the request creator + +Approving or rejecting a request is irreversible. The only possible status transitions are: + +- _PENDING_ to _APPROVED_ +- _PENDING_ to _REJECTED_ + +To revert the effect on launch information from a request, a user must send the eventual opposite request (example: AddAccount → RemoveAccount). + +Since the coordinator is the sole approver for requests, each request created by the coordinator is immediately set to APPROVED and its content is applied to the launch information. + +![requests](./assets/requests.png) + +## Available requests + +Six types of requests can be sent to the Ignite chain: + +- `AddGenesisAccount` +- `AddVestingAccount` +- `AddGenesisValidator` +- `RemoveAccount` +- `RemoveValidator` +- `ChangeParam` + +**`AddGenesisAccount`** requests a new account for the chain genesis with a coin balance. This request content is composed of two fields: + +- Account address, must be unique in launch information +- Account balance + +The request automatically fails to be applied if a genesis account or a vesting account with an identical address is already specified in the launch information. + +**`AddVestingAccount`** requests a new account for the chain genesis with a coin balance and vesting options. This request content is composed of two fields: + +- Address of the account +- Vesting options of the account + +The currently supported vesting option is delayed vesting where the total balance of the account is specified and a number of tokens of the total balance of the account are vested only after an end time is reached. + +The request automatically fails to be applied if a genesis account or a vesting account with an identical address is already specified in the launch information. + +**`AddGenesisValidator`** requests a new genesis validator for the chain. A genesis validator in a Cosmos SDK blockchain represents an account with an existing balance in the genesis that self-delegates part of its balance during genesis initialization to become a bonded validator when the network starts. In most cases, the validator must first request an account with `AddGenesisAccount` before requesting to be a validator, unless they already have an account with a balance in the initial genesis of the chain. + +Self-delegation during genesis initialization is performed with a [Cosmos SDK module named genutils](https://pkg.go.dev/github.com/cosmos/cosmos-sdk/x/genutil). In the genesis, the _genutils_ module contains objects called gentx that represent transactions that were executed before the network launch. To be a validator when the network starts, a future validator must provide a gentx that contains the transaction for the self-delegation from their account. + +The request content is composed of five fields: + +- The gentx for the validator self-delegation +- The address of the validator +- The consensus public key of the validator node +- The self-delegation +- The peer information for the validator node + +The request automatically fails to be applied if a validator with the same address already exists in the launch information. + +**`RemoveAccount`** requests the removal of a genesis or vesting account from the launch information. The request content contains the address of the account to be removed. The request automatically fails to be applied if no genesis or vesting account with the specified address exists in the launch information. + +**`RemoveValidator`** requests the removal of a genesis validator from the launch information. The request content contains the address of the validator to be removed. The request automatically fails to be applied if no validator account with the specified address exists in the launch information. + +**`ChangeParam`** requests the modification of a module parameter in the genesis. Modules in a Cosmos SDK blockchain can have parameters that will configure the logic of the blockchain. The parameters can be changed through governance once the blockchain network is live. During the launch process, the initial parameters of the chain are set in the genesis. + +This request content is composed of three fields: + +- The name of the module +- The name of the parameter +- The value of the parameter represented as generic data + +### Request validity + +Some checks are verified on-chain when applying a request. For example, a genesis account can’t be added twice. However, some other validity properties can’t be checked on-chain. For example, because a gentx is represented through a generic byte array in the blockchain, an on-chain check is not possible to verify that the gentx is correctly signed or that the provided consensus public key that is stored on-chain corresponds to the consensus public key in the gentx. This gentx verification is the responsibility of the client interacting with the blockchain to ensure the requests have a valid format and allow for the start of the chain. Some validity checks are specified in the genesis generation algorithm. + +## Launch process + +The overall launch process of a chain through Ignite is composed of three phases: + +- Coordination phase +- Preparation phase +- Launch phase + +After the coordinator creates the chain on Ignite and provides the initial launch information, the launch process enters the coordination phase where users can send requests for the chain genesis. After the coordinator deems the chain as ready to be launched, they trigger the launch of the chain. During this operation, the coordinator provides the launch time, or genesis, time for the chain. + +Once the launch is triggered and before the launch time is reached, the chain launch process enters the preparation phase. During the preparation phase, requests can no longer be sent and the launch information of the chain is finalized. The validators run the genesis generation algorithm to get the final genesis of the chain and prepare their node. The remaining time must provide enough time for the validators to prepare their nodes. This launch time is set by the coordinator, although a specific range for the remaining time is imposed. + +Once the launch time is reached, the chain network is started and the chain launch process enters the launch phase. At this point, since the chain is live, no further action is required from the coordinator. However, under some circumstances, the chain might have failed to start. For example, a chain does not start if every validator in the genesis does not start their node. + +The coordinator has the ability to revert the chain launch. Reverting the chain launch sets the launch process back to the coordination phase where requests can be sent again to allow addressing the issue related to the launch failure. Reverting the launch has an effect only on Ignite. If the new chain is effectively launched, reverting the launch on Ignite has no effect on the chain liveness. Reverting the launch of the chain can be performed only by the coordinator after the launch time plus a delay called the revert delay. + +![process](./assets/process.png) + +## Genesis generation + +To ensure determinism, genesis generation rules must be rigorously specified depending on the launch information of the chain. + +The general steps for the genesis generation are: + +- Building the blockchain node binary from source +- Generating the initial genesis +- Setting the chain ID +- Setting the genesis time +- Adding genesis accounts +- Adding genesis accounts with vesting options +- Adding gentxs for genesis validators +- Changing module params from param changes diff --git a/docs/versioned_docs/version-v0.27.1/04-network/02-introduction.md b/docs/versioned_docs/version-v0.27.1/04-network/02-introduction.md new file mode 100644 index 0000000000..dc4209c086 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/04-network/02-introduction.md @@ -0,0 +1,75 @@ +--- +sidebar_position: 2 +description: Introduction to Ignite Network commands. +--- + +# Ignite Network commands + +The `ignite network` commands allow to coordinate the launch of sovereign Cosmos blockchains by interacting with the +Ignite Chain. + +To launch a Cosmos blockchain you need someone to be a coordinator and others to be validators. These are just roles, +anyone can be a coordinator or a validator. + +- A coordinator publishes information about a chain to be launched on the Ignite blockchain, approves validator requests + and coordinates the launch. +- Validators send requests to join a chain and start their nodes when a blockchain is ready for launch. + +## Launching a chain on Ignite + +Launching with the CLI can be as simple as a few short commands with the the CLI using `ignite network` command +namespace. + +> **NOTE:** `ignite n` can also be used as a shortcut for `ignite network`. + +To publish the information about your chain as a coordinator, run the following command (the URL should point to a +repository with a Cosmos SDK chain): + +``` +ignite network chain publish github.com/ignite/example +``` + +This command will return the launch identifier you will be using in the following +commands. Let's say this identifier is 42. +Next, ask validators to initialize their nodes and request to join the network. +For a testnet you can use the default values suggested by the +CLI. + +``` +ignite network chain init 42 +ignite network chain join 42 --amount 95000000stake +``` + +As a coordinator, list all validator requests: + +``` +ignite network request list 42 +``` + +Approve validator requests: + +``` +ignite network request approve 42 1,2 +``` + +Once you've approved all validators you need in the validator set, announce that +the chain is ready for launch: + +``` +ignite network chain launch 42 +``` + +Validators can now prepare their nodes for launch: + +``` +ignite network chain prepare 42 +``` + +The output of this command will show a command that a validator would use to +launch their node, for example `exampled --home ~/.example`. After enough +validators launch their nodes, a blockchain will be live. + +--- + +The next two sections provide more information on the process of coordinating a chain launch from a coordinator and +participating in a chain launch as a validator. diff --git a/docs/versioned_docs/version-v0.27.1/04-network/03-coordinator.md b/docs/versioned_docs/version-v0.27.1/04-network/03-coordinator.md new file mode 100644 index 0000000000..5001c35348 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/04-network/03-coordinator.md @@ -0,0 +1,146 @@ +--- +sidebar_position: 3 +description: Ignite Network commands for coordinators. +--- + +# Coordinator Guide + +Coordinators organize and launch new chains on Ignite Chain. + +--- + +## Publish a chain + +The first step in the process of a chain launch is for the coordinator to publish the intention of launching a chain. +The `publish` command publishes the intention of launching a chain on Ignite from a project git repository. + +```shell +ignite n chain publish https://github.com/ignite/example +``` + +**Output** + +``` +✔ Source code fetched +✔ Blockchain set up +✔ Chain's binary built +✔ Blockchain initialized +✔ Genesis initialized +✔ Network published +⋆ Launch ID: 3 +``` + +`LaunchID` identifies the published blockchain on Ignite blockchain. + +### Specify a initial genesis + +During coordination, new genesis accounts and genesis validators are added into the chain genesis. +The initial genesis where these accounts are added is by default the default genesis generated by the chain binary. + +The coordinator can specify a custom initial genesis for the chain launch with the `--genesis` flag. This custom initial +genesis can contain additional default genesis accounts and custom params for the chain modules. + +A URL must be provided for the `--genesis-url` flag. This can either directly point to a JSON genesis file or a tarball +containing a genesis file. + +```shell +ignite n chain publish https://github.com/ignite/example --genesis-url https://raw.githubusercontent.com/ignite/example/master/genesis/gen.json +``` + +## Approve validator requests + +When coordinating for a chain launch, validators send requests. These represent requests to be part of the genesis as a +validator for the chain. + +The coordinator can list these requests: + +``` +ignite n request list 3 +``` + +> **NOTE:** here "3" is specifying the `LaunchID`. + +**Output** + +``` +Id Status Type Content +1 APPROVED Add Genesis Account spn1daefnhnupn85e8vv0yc5epmnkcr5epkqncn2le, 100000000stake +2 APPROVED Add Genesis Validator e3d3ca59d8214206839985712282967aaeddfb01@84.118.211.157:26656, spn1daefnhnupn85e8vv0yc5epmnkcr5epkqncn2le, 95000000stake +3 PENDING Add Genesis Account spn1daefnhnupn85e8vv0yc5epmnkcr5epkqncn2le, 95000000stake +4 PENDING Add Genesis Validator b10f3857133907a14dca5541a14df9e8e3389875@84.118.211.157:26656, spn1daefnhnupn85e8vv0yc5epmnkcr5epkqncn2le, 95000000stake +``` + +The coordinator can either approve or reject these requests. + +To approve the requests: + +``` +ignite n request approve 3 3,4 +``` + +> **NOTE:** when selecting a list of requests, both syntaxes can be used: `1,2,3,4` and `1-3,4`. + +**Output** + +``` +✔ Source code fetched +✔ Blockchain set up +✔ Requests format verified +✔ Blockchain initialized +✔ Genesis initialized +✔ Genesis built +✔ The network can be started +✔ Request(s) #3, #4 verified +✔ Request(s) #3, #4 approved +``` + +Ignite CLI automatically verifies that the requests can be applied for the genesis, the approved requests don't generate +an invalid genesis. + +To reject the requests: + +``` +ignite n request reject 3 3,4 +``` + +**Output** + +``` +✔ Request(s) #3, #4 rejected +``` + +--- + +## Initiate the launch of a chain + +When enough validators are approved for the genesis and the coordinator deems the chain ready to be launched, the +coordinator can initiate the launch of the chain. + +This action will finalize the genesis of chain, meaning that no new requests can be approved for the chain. + +This action also sets the launch time (or genesis time) for the chain, the time when the blockchain network will go +live. + +``` +ignite n chain launch 3 +``` + +**Output** + +``` +✔ Chain 3 will be launched on 2022-10-01 09:00:00.000000 +0200 CEST +``` + +This example output shows the launch time of the chain on the network. + +### Set a custom launch time + +By default, the launch time will be set to the earliest date possible. In practice, the validators should have time to +prepare their node for the network launch. If a validator fails to be online, they can get jailed for inactivity in the +validator set. + +The coordinator can specify a custom time with the `--launch-time` flag. + +``` +ignite n chain launch --launch-time 2022-01-01T00:00:00Z +``` diff --git a/docs/versioned_docs/version-v0.27.1/04-network/04-validator.md b/docs/versioned_docs/version-v0.27.1/04-network/04-validator.md new file mode 100644 index 0000000000..738dfb2777 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/04-network/04-validator.md @@ -0,0 +1,161 @@ +--- +sidebar_position: 4 +description: Ignite Network commands for validators. +--- + +# Validator Guide + +Validators join as genesis validators for chain launches on Ignite Chain. + +--- + +## List all published chains + +Validators can list and explore published chains to be launched on Ignite. + +``` +ignite n chain list +``` + +**Output** + +``` +Launch Id Chain Id Source Phase + +3 example-1 https://github.com/ignite/example coordinating +2 spn-10 https://github.com/tendermint/spn launched +1 example-20 https://github.com/tendermint/spn launching +``` + +- `Launch ID` is the unique identifier of the chain on Ignite. This is the ID used to interact with the chain launch. +- `Chain ID` represents the identifer of the chain network once it will be launched. It should be a unique identifier in + practice but doesn't need to be unique on Ignite. +- `Source` is the repository URL of the project. +- `Phase` is the current phase of the chain launch. A chain can have 3 different phases: + - `coordinating`: means the chain is open to receive requests from validators + - `launching`: means the chain no longer receives requests but it hasn't been launched yet + - `launched`: means the chain network has been launched + +--- + +## Request network participation + +When the chain is in the coordination phase, validators can request to be a genesis validator for the chain. +Ignite CLI supports an automatic workflow that can setup a node for the validator and a workflow for advanced users with +a specific setup for their node. + +### Simple Flow + +`ignite` can handle validator setup automatically. Initialize the node and generate a gentx file with default values: + +``` +ignite n chain init 3 +``` + +**Output** + +``` +✔ Source code fetched +✔ Blockchain set up +✔ Blockchain initialized +✔ Genesis initialized +? Staking amount 95000000stake +? Commission rate 0.10 +? Commission max rate 0.20 +? Commission max change rate 0.01 +⋆ Gentx generated: /Users/lucas/spn/3/config/gentx/gentx.json +``` + +Now, create and broadcast a request to join a chain as a validator: + +``` +ignite n chain join 3 --amount 100000000stake +``` + +The join command accepts a `--amount` flag with a comma-separated list of tokens. If the flag is provided, the +command will broadcast a request to add the validator’s address as an account to the genesis with the specific amount. + +**Output** + +``` +? Peer's address 192.168.0.1:26656 +✔ Source code fetched +✔ Blockchain set up +✔ Account added to the network by the coordinator! +✔ Validator added to the network by the coordinator! +``` + +--- + +### Advanced Flow + +Using a more advanced setup (e.g. custom `gentx`), validators must provide an additional flag to their command +to point to the custom file: + +``` +ignite n chain join 3 --amount 100000000stake --gentx ~/chain/config/gentx/gentx.json +``` + +--- + +## Launch the network + +### Simple Flow + +Generate the final genesis and config of the node: + +``` +ignite n chain prepare 3 +``` + +**Output** + +``` +✔ Source code fetched +✔ Blockchain set up +✔ Chain's binary built +✔ Genesis initialized +✔ Genesis built +✔ Chain is prepared for launch +``` + +Next, start the node: + +``` +exampled start --home ~/spn/3 +``` + +--- + +### Advanced Flow + +Fetch the final genesis for the chain: + +``` +ignite n chain show genesis 3 +``` + +**Output** + +``` +✔ Source code fetched +✔ Blockchain set up +✔ Blockchain initialized +✔ Genesis initialized +✔ Genesis built +⋆ Genesis generated: ./genesis.json +``` + +Next, fetch the persistent peer list: + +``` +ignite n chain show peers 3 +``` + +**Output** + +``` +⋆ Peer list generated: ./peers.txt +``` + +The fetched genesis file and peer list can be used for a manual node setup. diff --git a/docs/versioned_docs/version-v0.27.1/04-network/05-coordination.md b/docs/versioned_docs/version-v0.27.1/04-network/05-coordination.md new file mode 100644 index 0000000000..8f8dd34f80 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/04-network/05-coordination.md @@ -0,0 +1,72 @@ +--- +sidebar_position: 5 +description: Other commands for coordination. +--- + +# Other commands for coordination + +Ignite CLI offers various other commands to coordinate chain launches that can be used by coordinators, validators, or other participants. + +The requests follow the same logic as the request for validator participation; they must be approved by the chain coordinator to be effective in the genesis. + +--- + +## Request a genesis account + +Any participant can request a genesis account with an associated balance for the chain. +The participant must provide an address with a comma-separated list of token balances. + +Any prefix can be used for the Bech32 address, it is automatically converted into `spn` on the Ignite Chain. + +``` +ignite n request add-account 3 spn1pe5h2gelhu8aukmrnj0clmec56aspxzuxcy99y 1000stake +``` + +**Output** + +``` +Source code fetched +Blockchain set up +⋆ Request 10 to add account to the network has been submitted! +``` +--- + +## Request to remove a genesis account + +Any participant can request to remove a genesis account from the chain genesis. +It might be the case if, for example, a user suggests an account balance that is so high it could harm the network. +The participant must provide the address of the account. + +Any prefix can be used for the Bech32 address, it is automatically converted into `spn` on the Ignite Chain. + +``` +ignite n request remove-account 3 spn1pe5h2gelhu8aukmrnj0clmec56aspxzuxcy99y +``` + +**Output** + +``` +Request 11 to remove account from the network has been submitted! +``` +--- + +## Request to remove a genesis validator + +Any participant can request to remove a genesis validator (gentx) from the chain genesis. +It might be the case if, for example, a chain failed to launch because of some validators, and they must be removed from genesis. +The participant must provide the address of the validator account (same format as genesis account). + +Any prefix can be used for the Bech32 address, it is automatically converted into `spn` on the Ignite Chain. + +The request removes only the gentx from the genesis but not the associated account balance. + +``` +ignite n request remove-validator 429 spn1pe5h2gelhu8aukmrnj0clmec56aspxzuxcy99y +``` + +**Output** + +``` +Request 12 to remove validator from the network has been submitted! +``` +--- \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.27.1/04-network/_category_.json b/docs/versioned_docs/version-v0.27.1/04-network/_category_.json new file mode 100644 index 0000000000..9764095e85 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/04-network/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Launch a chain", + "link": null +} \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.27.1/04-network/assets/generation.png b/docs/versioned_docs/version-v0.27.1/04-network/assets/generation.png new file mode 100644 index 0000000000..cb978e2dbf Binary files /dev/null and b/docs/versioned_docs/version-v0.27.1/04-network/assets/generation.png differ diff --git a/docs/versioned_docs/version-v0.27.1/04-network/assets/genesis.png b/docs/versioned_docs/version-v0.27.1/04-network/assets/genesis.png new file mode 100644 index 0000000000..a0ecad4c44 Binary files /dev/null and b/docs/versioned_docs/version-v0.27.1/04-network/assets/genesis.png differ diff --git a/docs/versioned_docs/version-v0.27.1/04-network/assets/launch.png b/docs/versioned_docs/version-v0.27.1/04-network/assets/launch.png new file mode 100644 index 0000000000..0fa5398b22 Binary files /dev/null and b/docs/versioned_docs/version-v0.27.1/04-network/assets/launch.png differ diff --git a/docs/versioned_docs/version-v0.27.1/04-network/assets/process.png b/docs/versioned_docs/version-v0.27.1/04-network/assets/process.png new file mode 100644 index 0000000000..6449114340 Binary files /dev/null and b/docs/versioned_docs/version-v0.27.1/04-network/assets/process.png differ diff --git a/docs/versioned_docs/version-v0.27.1/04-network/assets/requests.png b/docs/versioned_docs/version-v0.27.1/04-network/assets/requests.png new file mode 100644 index 0000000000..38a62ea985 Binary files /dev/null and b/docs/versioned_docs/version-v0.27.1/04-network/assets/requests.png differ diff --git a/docs/versioned_docs/version-v0.27.1/05-contributing/01-docs.md b/docs/versioned_docs/version-v0.27.1/05-contributing/01-docs.md new file mode 100644 index 0000000000..de8ff3e12a --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/05-contributing/01-docs.md @@ -0,0 +1,105 @@ +--- +sidebar_position: 1 +slug: /contributing +--- + +# Improving documentation + +Thank you for visiting our repository and considering making contributions. We +appreciate your interest in helping us to create and maintain awesome tutorials +and documentation. + +## Using this repo + +Review existing [Ignite CLI issues](https://github.com/ignite/cli/issues) to see +if your question has already been asked and answered. + +- To provide feedback, file an issue and provide generous details to help us + understand how we can make it better. +- To provide a fix, make a direct contribution. If you're not a member or + maintainer, fork the repo and then submit a pull request (PR) from your forked + repo to the `main` branch. +- Start by creating a draft pull request. Create your draft PR early, even if + your work is just beginning or incomplete. Your draft PR indicates to the + community that you're working on something and provides a space for + conversations early in the development process. Merging is blocked for `Draft` + PRs, so they provide a safe place to experiment and invite comments. + +## Reviewing technical content PRs + +Some of the best content contributions come during the PR review cycles. Follow +best practices for technical content PR reviews just like you do for code +reviews. + +- For in-line suggestions, use the [GitHub suggesting + feature](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/commenting-on-a-pull-request) + . +- The PR owner can merge in your suggested commits one at a time or in batch + (preferred). +- When you are providing a more granular extensive review that results in more + than 20 in-line suggestions, go ahead and check out the branch and make the + changes yourself. + +## Writing and contributing + +We welcome contributions to the docs and tutorials. + +Our technical content follows the [Google developer documentation style +guide](https://developers.google.com/style). Highlights to help you get started: + +- [Highlights](https://developers.google.com/style/highlights) +- [Word list](https://developers.google.com/style/word-list) +- [Style and tone](https://developers.google.com/style/tone) +- [Writing for a global + audience](https://developers.google.com/style/translation) +- [Cross-references](https://developers.google.com/style/cross-references) +- [Present tense](https://developers.google.com/style/tense) + +The Google guidelines include more material than is listed here and are used as +a guide that enables easy decision-making about proposed content changes. + +Other useful resources: + +- [Google Technical Writing Courses](https://developers.google.com/tech-writing) +- [GitHub Guides Mastering + Markdown](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax) + +## Where can I find the tutorials and docs? + +Technical content includes knowledge base articles and interactive tutorials. + +- The Ignite CLI Developer Tutorials content is in the `docs/guide` folder. +- The Knowledge Base content is in the `docs/kb` folder. +- Upgrade information is in the `docs/migration` folder. + +Note: The CLI docs are auto-generated and do not support doc updates. + +Locations and folders for other content can vary. Explore the self-describing +folders for the content that you are interested in. Some articles and tutorials +reside in a single Markdown file while sub-folders might be present for other +tutorials. + +As always, work-in-progress content might be happening in other locations and +repos. + +## Who works on the tutorials? + +The Ignite product team developers are focused on building Ignite CLI and +improving the developer experience. The Ignite Ecosystem Development team owns +the technical content and tutorials and manages developer onboarding. + +Meet the [people behind Ignite CLI and our +contributors](https://github.com/ignite/cli/graphs/contributors). + +## Viewing docs builds + +Use a preview to see what your changes will look like in production before the +updated pages are published. + +- While a PR is in draft mode, you can rely on using the preview feature in + Markdown. +- After the PR moves from **Draft** to **Ready for review**, the CI status + checks generate a deployment preview. This preview stays up to date as you + continue to work and commit new changes to the same branch. A `Docs Deploy + Preview / build_and_deploy (pull_request)` preview on a GitHub actions URL is + unique for that PR. diff --git a/docs/versioned_docs/version-v0.27.1/05-contributing/_category_.json b/docs/versioned_docs/version-v0.27.1/05-contributing/_category_.json new file mode 100644 index 0000000000..e88b1ff8fe --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/05-contributing/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Contribute to Ignite", + "link": null +} \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.27.1/06-migration/_category_.json b/docs/versioned_docs/version-v0.27.1/06-migration/_category_.json new file mode 100644 index 0000000000..9243d2011d --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/06-migration/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Migration", + "link": null +} \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.27.1/06-migration/readme.md b/docs/versioned_docs/version-v0.27.1/06-migration/readme.md new file mode 100644 index 0000000000..08dd1101d3 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/06-migration/readme.md @@ -0,0 +1,14 @@ +--- +sidebar_position: 0 +--- + +# Migration Guides + +Welcome to the section on upgrading to a newer version of Ignite CLI! If you're +looking to update to the latest version, you'll want to start by checking the +documentation to see if there are any special considerations or instructions you +need to follow. + +If there is no documentation for the latest version of Ignite CLI, it's +generally safe to assume that there were no breaking changes, and you can +proceed with using the latest version with your project. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.27.1/06-migration/v0.18.md b/docs/versioned_docs/version-v0.27.1/06-migration/v0.18.md new file mode 100644 index 0000000000..d7ce7ef20e --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/06-migration/v0.18.md @@ -0,0 +1,458 @@ +--- +sidebar_position: 999 +title: v0.18.0 +description: For chains that were scaffolded with Ignite CLI versions lower than v0.18, changes are required to use Ignite CLI v0.18. +--- + +# Upgrading a Blockchain to use Ignite CLI v0.18 + +Ignite CLI v0.18 comes with Cosmos SDK v0.44. This version of Cosmos SDK introduced changes that are not compatible with +chains that were scaffolded with Ignite CLI versions lower than v0.18. + +**Important:** After upgrading from Ignite CLI v0.17.3 to Ignite CLI v0.18, you must update the default blockchain +template to use blockchains that were scaffolded with earlier versions. + +These instructions are written for a blockchain that was scaffolded with the following command: + +``` +ignite scaffold chain github.com/username/mars +``` + +If you used a different module path, replace `username` and `mars` with the correct values for your blockchain. + +## Blockchain + +For each file listed, make the required changes to the source code of the blockchain template. + +### go.mod + +``` +module github.com/username/mars + +go 1.16 + +require ( + github.com/cosmos/cosmos-sdk v0.44.0 + github.com/cosmos/ibc-go v1.2.0 + github.com/gogo/protobuf v1.3.3 + github.com/google/go-cmp v0.5.6 // indirect + github.com/gorilla/mux v1.8.0 + github.com/grpc-ecosystem/grpc-gateway v1.16.0 + github.com/spf13/cast v1.3.1 + github.com/spf13/cobra v1.1.3 + github.com/stretchr/testify v1.7.0 + github.com/tendermint/spm v0.1.6 + github.com/tendermint/tendermint v0.34.13 + github.com/tendermint/tm-db v0.6.4 + google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83 + google.golang.org/grpc v1.40.0 +) + +replace ( + github.com/99designs/keyring => github.com/cosmos/keyring v1.1.7-0.20210622111912-ef00f8ac3d76 + github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 + google.golang.org/grpc => google.golang.org/grpc v1.33.2 +) +``` + +### app/app.go + +```go +package app + +import ( + //... + // Add the following packages: + "github.com/cosmos/cosmos-sdk/x/feegrant" + feegrantkeeper "github.com/cosmos/cosmos-sdk/x/feegrant/keeper" + feegrantmodule "github.com/cosmos/cosmos-sdk/x/feegrant/module" + + "github.com/cosmos/ibc-go/modules/apps/transfer" + ibctransferkeeper "github.com/cosmos/ibc-go/modules/apps/transfer/keeper" + ibctransfertypes "github.com/cosmos/ibc-go/modules/apps/transfer/types" + ibc "github.com/cosmos/ibc-go/modules/core" + ibcclient "github.com/cosmos/ibc-go/modules/core/02-client" + ibcporttypes "github.com/cosmos/ibc-go/modules/core/05-port/types" + ibchost "github.com/cosmos/ibc-go/modules/core/24-host" + ibckeeper "github.com/cosmos/ibc-go/modules/core/keeper" + // Remove the following packages: + // transfer "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer" + // ibctransferkeeper "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/keeper" + // ibctransfertypes "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/types" + // ibc "github.com/cosmos/cosmos-sdk/x/ibc/core" + // ibcclient "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client" + // porttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/05-port/types" + // ibchost "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + // ibckeeper "github.com/cosmos/cosmos-sdk/x/ibc/core/keeper" +) + +var ( + //... + ModuleBasics = module.NewBasicManager( + //... + slashing.AppModuleBasic{}, + // Add feegrantmodule.AppModuleBasic{}, + feegrantmodule.AppModuleBasic{}, // <-- + ibc.AppModuleBasic{}, + //... + ) + //... +) + +type App struct { + //... + // Replace codec.Marshaler with codec.Codec + appCodec codec.Codec // <-- + // Add FeeGrantKeeper + FeeGrantKeeper feegrantkeeper.Keeper // <-- +} + +func New( /*...*/ ) { + //bApp.SetAppVersion(version.Version) + bApp.SetVersion(version.Version) // <-- + + keys := sdk.NewKVStoreKeys( + //... + upgradetypes.StoreKey, + // Add feegrant.StoreKey + feegrant.StoreKey, // <-- + evidencetypes.StoreKey, + //... + ) + + app.FeeGrantKeeper = feegrantkeeper.NewKeeper(appCodec, keys[feegrant.StoreKey], app.AccountKeeper) // <-- + // Add app.BaseApp as the last argument to upgradekeeper.NewKeeper + app.UpgradeKeeper = upgradekeeper.NewKeeper(skipUpgradeHeights, keys[upgradetypes.StoreKey], appCodec, homePath, app.BaseApp) + + app.IBCKeeper = ibckeeper.NewKeeper( + // Add app.UpgradeKeeper + appCodec, keys[ibchost.StoreKey], app.GetSubspace(ibchost.ModuleName), app.StakingKeeper, app.UpgradeKeeper, scopedIBCKeeper, + ) + + govRouter.AddRoute(govtypes.RouterKey, govtypes.ProposalHandler). + //... + // Replace NewClientUpdateProposalHandler with NewClientProposalHandler + AddRoute(ibchost.RouterKey, ibcclient.NewClientProposalHandler(app.IBCKeeper.ClientKeeper)) + + // Replace porttypes with ibcporttypes + ibcRouter := ibcporttypes.NewRouter() + + app.mm.SetOrderBeginBlockers( + upgradetypes.ModuleName, + // Add capabilitytypes.ModuleName, + capabilitytypes.ModuleName, + minttypes.ModuleName, + //... + // Add feegrant.ModuleName, + feegrant.ModuleName, + ) + + // Add app.appCodec as an argument to module.NewConfigurator: + app.mm.RegisterServices(module.NewConfigurator(app.appCodec, app.MsgServiceRouter(), app.GRPCQueryRouter())) + + // Replace: + // app.SetAnteHandler( + // ante.NewAnteHandler( + // app.AccountKeeper, app.BankKeeper, ante.DefaultSigVerificationGasConsumer, + // encodingConfig.TxConfig.SignModeHandler(), + // ), + // ) + + // With the following: + anteHandler, err := ante.NewAnteHandler( + ante.HandlerOptions{ + AccountKeeper: app.AccountKeeper, + BankKeeper: app.BankKeeper, + SignModeHandler: encodingConfig.TxConfig.SignModeHandler(), + FeegrantKeeper: app.FeeGrantKeeper, + SigGasConsumer: ante.DefaultSigVerificationGasConsumer, + }, + ) + if err != nil { + panic(err) + } + app.SetAnteHandler(anteHandler) + + // Remove the following: + // ctx := app.BaseApp.NewUncachedContext(true, tmproto.Header{}) + // app.CapabilityKeeper.InitializeAndSeal(ctx) +} + +func (app *App) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { + var genesisState GenesisState + if err := tmjson.Unmarshal(req.AppStateBytes, &genesisState); err != nil { + panic(err) + } + // Add the following: + app.UpgradeKeeper.SetModuleVersionMap(ctx, app.mm.GetVersionMap()) + return app.mm.InitGenesis(ctx, app.appCodec, genesisState) +} + +// Replace Marshaler with Codec +func (app *App) AppCodec() codec.Codec { + return app.appCodec +} + +// Replace BinaryMarshaler with BinaryCodec +func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino, key, tkey sdk.StoreKey) paramskeeper.Keeper { + //... +} +``` + +### app/genesis.go + +```go +// Replace codec.JSONMarshaler with codec.JSONCodec +func NewDefaultGenesisState(cdc codec.JSONCodec) GenesisState { + // ... +} +``` + +### testutil/keeper/mars.go + +Add the following code: + +```go +package keeper + +import ( + "testing" + + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/store" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/libs/log" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + tmdb "github.com/tendermint/tm-db" + "github.com/username/mars/x/mars/keeper" + "github.com/username/mars/x/mars/types" +) + +func MarsKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) { + storeKey := sdk.NewKVStoreKey(types.StoreKey) + memStoreKey := storetypes.NewMemoryStoreKey(types.MemStoreKey) + + db := tmdb.NewMemDB() + stateStore := store.NewCommitMultiStore(db) + stateStore.MountStoreWithDB(storeKey, sdk.StoreTypeIAVL, db) + stateStore.MountStoreWithDB(memStoreKey, sdk.StoreTypeMemory, nil) + require.NoError(t, stateStore.LoadLatestVersion()) + + registry := codectypes.NewInterfaceRegistry() + k := keeper.NewKeeper( + codec.NewProtoCodec(registry), + storeKey, + memStoreKey, + ) + + ctx := sdk.NewContext(stateStore, tmproto.Header{}, false, log.NewNopLogger()) + return k, ctx +} +``` + +If `mars` is an IBC-enabled module, add the following code, instead: + +```go +package keeper + +import ( + "testing" + + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/store" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" + typesparams "github.com/cosmos/cosmos-sdk/x/params/types" + ibckeeper "github.com/cosmos/ibc-go/modules/core/keeper" + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/libs/log" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + tmdb "github.com/tendermint/tm-db" + "github.com/username/test/x/mars/keeper" + "github.com/username/test/x/mars/types" +) + +func MarsKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) { + logger := log.NewNopLogger() + + storeKey := sdk.NewKVStoreKey(types.StoreKey) + memStoreKey := storetypes.NewMemoryStoreKey(types.MemStoreKey) + + db := tmdb.NewMemDB() + stateStore := store.NewCommitMultiStore(db) + stateStore.MountStoreWithDB(storeKey, sdk.StoreTypeIAVL, db) + stateStore.MountStoreWithDB(memStoreKey, sdk.StoreTypeMemory, nil) + require.NoError(t, stateStore.LoadLatestVersion()) + + registry := codectypes.NewInterfaceRegistry() + appCodec := codec.NewProtoCodec(registry) + capabilityKeeper := capabilitykeeper.NewKeeper(appCodec, storeKey, memStoreKey) + + amino := codec.NewLegacyAmino() + ss := typesparams.NewSubspace(appCodec, + amino, + storeKey, + memStoreKey, + "MarsSubSpace", + ) + IBCKeeper := ibckeeper.NewKeeper( + appCodec, + storeKey, + ss, + nil, + nil, + capabilityKeeper.ScopeToModule("MarsIBCKeeper"), + ) + + k := keeper.NewKeeper( + codec.NewProtoCodec(registry), + storeKey, + memStoreKey, + IBCKeeper.ChannelKeeper, + &IBCKeeper.PortKeeper, + capabilityKeeper.ScopeToModule("MarsScopedKeeper"), + ) + + ctx := sdk.NewContext(stateStore, tmproto.Header{}, false, logger) + return k, ctx +} +``` + +### testutil/network/network.go + +```go +func DefaultConfig() network.Config { + // ... + return network.Config{ + // ... + // Add sdk.DefaultPowerReduction + AccountTokens: sdk.TokensFromConsensusPower(1000, sdk.DefaultPowerReduction), + StakingTokens: sdk.TokensFromConsensusPower(500, sdk.DefaultPowerReduction), + BondedTokens: sdk.TokensFromConsensusPower(100, sdk.DefaultPowerReduction), + // ... + } +} +``` + +### testutil/sample/sample.go + +Add the following code: + +```go +package sample + +import ( + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// AccAddress returns a sample account address +func AccAddress() string { + pk := ed25519.GenPrivKey().PubKey() + addr := pk.Address() + return sdk.AccAddress(addr).String() +} +``` + +### BandChain Support + +If your module includes integration with BandChain, added manually or scaffolded with `ignite scaffold band`, upgrade +the `github.com/bandprotocol/bandchain-packet` package to `v0.0.2` in `go.mod`. + +## Module + +### x/mars/keeper/keeper.go + +```go +package keeper + +// ... + +type ( + Keeper struct { + // Replace Marshaler with BinaryCodec + cdc codec.BinaryCodec + //... + } +) + +func NewKeeper( + // Replace Marshaler with BinaryCodec + cdc codec.BinaryCodec, + // ... +) *Keeper { + // ... +} +``` + +### x/mars/keeper/msg_server_test.go + +```go +package keeper_test + +import ( + //... + // Add the following: + keepertest "github.com/username/mars/testutil/keeper" + "github.com/username/mars/x/mars/keeper" +) + +func setupMsgServer(t testing.TB) (types.MsgServer, context.Context) { + // Replace + // keeper, ctx := setupKeeper(t) + // return NewMsgServerImpl(*keeper), sdk.WrapSDKContext(ctx) + + // With the following: + k, ctx := keepertest.MarsKeeper(t) + return keeper.NewMsgServerImpl(*k), sdk.WrapSDKContext(ctx) +} +``` + +### x/mars/module.go + +```go +package mars + +type AppModuleBasic struct { + // Replace Marshaler with BinaryCodec + cdc codec.BinaryCodec +} + +// Replace Marshaler with BinaryCodec +func NewAppModuleBasic(cdc codec.BinaryCodec) AppModuleBasic { + return AppModuleBasic{cdc: cdc} +} + +// Replace JSONMarshaler with JSONCodec +func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { + return cdc.MustMarshalJSON(types.DefaultGenesis()) +} + +// Replace JSONMarshaler with JSONCodec +func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, config client.TxEncodingConfig, bz json.RawMessage) error { + //... +} + +// Replace codec.Marshaller with codec.Codec +func NewAppModule(cdc codec.Codec, keeper keeper.Keeper) AppModule { + //... +} + +// Replace JSONMarshaler with JSONCodec +func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, gs json.RawMessage) []abci.ValidatorUpdate { + //... +} + +// Replace JSONMarshaler with JSONCodec +func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage { + //... +} + +// Add the following +func (AppModule) ConsensusVersion() uint64 { return 2 } +``` diff --git a/docs/versioned_docs/version-v0.27.1/06-migration/v0.19.2.md b/docs/versioned_docs/version-v0.27.1/06-migration/v0.19.2.md new file mode 100644 index 0000000000..5bd63a73c1 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/06-migration/v0.19.2.md @@ -0,0 +1,26 @@ +--- +sidebar_position: 998 +title: v0.19.2 +description: For chains that were scaffolded with Ignite CLI versions lower than v0.19.2, changes are required to use Ignite CLI v0.19.2. +--- + +# Upgrading a blockchain to use Ignite CLI v0.19.2 + +Ignite CLI v0.19.2 comes with IBC v2.0.2. + +With Ignite CLI v0.19.2, the contents of the deprecated Ignite CLI Modules `tendermint/spm` repo are moved to the +official Ignite CLI repo which introduces breaking changes. + +To migrate your chain that was scaffolded with Ignite CLI versions lower than v0.19.2: + +1. IBC upgrade: Use + the [IBC migration documents](https://github.com/cosmos/ibc-go/blob/main/docs/migrations/v1-to-v2.md) + +2. In your chain's `go.mod` file, remove `tendermint/spm` and add the v0.19.2 version of `tendermint/starport`. If your + chain uses these packages, change the import paths as shown: + + - `github.com/tendermint/spm/ibckeeper` moved to `github.com/tendermint/starport/starport/pkg/cosmosibckeeper` + - `github.com/tendermint/spm/cosmoscmd` moved to `github.com/tendermint/starport/starport/pkg/cosmoscmd` + - `github.com/tendermint/spm/openapiconsole` moved to `github.com/tendermint/starport/starport/pkg/openapiconsole` + - `github.com/tendermint/spm/testutil/sample` moved + to `github.com/tendermint/starport/starport/pkg/cosmostestutil/sample` diff --git a/docs/versioned_docs/version-v0.27.1/06-migration/v0.20.0.md b/docs/versioned_docs/version-v0.27.1/06-migration/v0.20.0.md new file mode 100644 index 0000000000..df23fa9bf8 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/06-migration/v0.20.0.md @@ -0,0 +1,12 @@ +--- +sidebar_position: 997 +title: v0.20.0 +description: For chains that were scaffolded with Ignite CLI versions lower than v0.20.0, changes are required to use Ignite CLI v0.20.0. +--- + +# Upgrading a blockchain to use Ignite CLI v0.20.2 + +1. Upgrade your Cosmos SDK version to [v0.45.3](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.45.3). + +2. Update your `SetOrderBeginBlockers` and `SetOrderEndBlockers` in your `app/app.go` to explicitly add entries for all + the modules you use in your chain. diff --git a/docs/versioned_docs/version-v0.27.1/06-migration/v0.22.0.md b/docs/versioned_docs/version-v0.27.1/06-migration/v0.22.0.md new file mode 100644 index 0000000000..54ea27ebde --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/06-migration/v0.22.0.md @@ -0,0 +1,36 @@ +--- +sidebar_position: 996 +title: v0.22.0 +description: For chains that were scaffolded with Ignite CLI versions lower than v0.22.0, changes are required to use Ignite CLI v0.22.0. +--- + +# Upgrading a blockchain to use Ignite CLI v0.22.0 + +Ignite CLI v0.22.2 changed the GitHub username from "ignite-hq" to "ignite", which means the imports must be fixed to +reflect this change. + +1. In your `go.mod` file find the require line for Ignite CLI that starts with `github.com/ignite-hq/cli` and is + followed by a version. + It looks something like `github.com/ignite-hq/cli v0.22.0`, and replace it by `github.com/ignite/cli v0.22.2`. + +2. Make a bulk find and replace in the import statements for `github.com/ignite-hq/cli` to be replaced + by `github.com/ignite/cli`. + +3. Finally, run `go mod tidy` and ensure there's no mention if `ignite-hq/cli` in your `go.sum` file. + +This update includes an upgrade to the `ibc-go` packages. Please make the according changes: + +1. Upgrade your IBC version to [v3](https://github.com/cosmos/ibc-go/releases/tag/v3.0.0). + + 1. Search for `github.com/cosmos/ibc-go/v2` in the import statements of your `.go` files and replace `v2` in the end + with `v3` + + 1. Open your `app.go`, + + - Update your transfer keeper by adding another `app.IBCKeeper.ChannelKeeper` as an argument + after `app.IBCKeeper.ChannelKeeper` + + - Define `var transferIBCModule = transfer.NewIBCModule(app.TransferKeeper)` in your `New()` func, and update + your existent IBC router to use it: `ibcRouter.AddRoute(ibctransfertypes.ModuleName, transferIBCModule)` + + 3. Open your `go.mod` and change the IBC line with `github.com/cosmos/ibc-go/v3 v3.0.0` diff --git a/docs/versioned_docs/version-v0.27.1/06-migration/v0.24.0.md b/docs/versioned_docs/version-v0.27.1/06-migration/v0.24.0.md new file mode 100644 index 0000000000..bcf1b85185 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/06-migration/v0.24.0.md @@ -0,0 +1,330 @@ +--- +sidebar_position: 995 +title: v0.24.0 +description: For chains that were scaffolded with Ignite CLI versions lower than v0.24, changes are required to use Ignite CLI v0.24.0. +--- + +## Cosmos SDK v0.46 upgrade notes + +### Update dependencies + +Cosmos SDK v0.46 is compatible with the latest version of IBC Go v5. If you have a chain that is using an older version, +update the dependencies in your project. + +Throughout the code you might see the following dependencies: + +```go +package pkg_name + +import ( + "github.com/cosmos/ibc-go/v3/..." +) +``` + +Where `v3` is the version of IBC Go and `...` are different IBC Go packages. + +To upgrade the version to `v5`, a global find-and-replace should work. Replace `cosmos/ibc-go/v3` (or whicherver version +you're using) with `cosmos/ibc-go/v5` only in `*.go` files (to exclude unwated changes to files like `go,sum`). + +### Module keeper + +Add an import: + +```go +// x/{moduleName}/keeper/keeper.go + +package keeper + +// ... + +import ( + //... + storetypes "github.com/cosmos/cosmos-sdk/store/types" +) +``` + +In the `Keeper` struct replace `sdk.StoreKey` with `storetypes.StoreKey`: + +```go +// x/{moduleName}/keeper/keeper.go + +package keeper + +// ... + +type ( + Keeper struct { + cdc codec.BinaryCodec + storeKey storetypes.StoreKey + memKey storetypes.StoreKey + paramstore paramtypes.Subspace + } +) +``` + +In the argument list of the `NewKeeper` function definition: + +```go +package keeper + +// ... + +// x/{moduleName}/keeper/keeper.go + +func NewKeeper( + //... + memKey storetypes.StoreKey, +) +``` + +Store type aliases have been removed from the Cosmos SDK `types` package and now have to be imported from `store/types`, +instead. + +In the `testutil/keeper/{moduleName}.go` replace `types.StoreKey` with `storetypes.StoreKey` and `types.MemStoreKey` +with `storetypes.MemStoreKey`. + +```go +// testutil/keeper/{moduleName}.go + +package keeper + +// ... + +func {moduleName}Keeper(t testing.TB) (*keeper.Keeper, sdk.Context) { + storeKey := sdk.NewKVStoreKey(storetypes.StoreKey) + memStoreKey := storetypes.NewMemoryStoreKey(storetypes.MemStoreKey) + //... +} +``` + +### Testutil network package + +Add the `require` package for testing and `pruningtypes` and remove `storetypes`: + +```go +// testutil/network/network.go + +package network + +// ... + +import ( + pruningtypes "github.com/cosmos/cosmos-sdk/pruning/types" + "github.com/stretchr/testify/require" + // storetypes "github.com/cosmos/cosmos-sdk/store/types" <-- remove this line +) +``` + +In the `DefaultConfig` function replace `storetypes.NewPruningOptionsFromString` +with `pruningtypes.NewPruningOptionsFromString` + +```go +// testutil/network/network.go + +package network + +// ... + +func DefaultConfig() network.Config { + //... + return network.Config{ + AppConstructor: func(val network.Validator) servertypes.Application { + return app.New( + baseapp.SetPruning(pruningtypes.NewPruningOptionsFromString(val.AppConfig.Pruning)), + //... + ) + }, + //... + } +} +``` + +The `New` function in the Cosmos SDK `testutil/network` package now +accepts [three arguments](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0/testutil/network/network.go#L206) instead of +two. + +In the `New` function add `t.TempDir()` as the second argument to `network.New()` and test that no error is thrown +with `require.NoError(t, err)`: + +```go +// testutil/network/network.go + +package network + +// ... + +func New(t *testing.T, configs ...network.Config) *network.Network { + //... + net, err := network.New(t, t.TempDir(), cfg) + require.NoError(t, err) + //... +} +``` + +### Testutil keeper package + +In the `{moduleName}Keeper` function make the following replacements: + +- `storetypes.StoreKey` → `types.StoreKey` +- `storetypes.MemStoreKey` → `types.MemStoreKey` +- `sdk.StoreTypeIAVL` → `storetypes.StoreTypeIAVL` +- `sdk.StoreTypeMemory` → `storetypes.StoreTypeMemory` + +```go +// testutil/keeper/{moduleName}.go + +package keeper + +// ... + +func {moduleName}Keeper(t testing.TB) (*keeper.Keeper, sdk.Context) { + storeKey := sdk.NewKVStoreKey(types.StoreKey) + memStoreKey := storetypes.NewMemoryStoreKey(types.MemStoreKey) + //... + stateStore.MountStoreWithDB(storeKey, storetypes.StoreTypeIAVL, db) + stateStore.MountStoreWithDB(memStoreKey, storetypes.StoreTypeMemory, nil) + //... +} +``` + +### IBC modules + +If you have IBC-enabled modules (for example, added with `ignite scaffold module ... --ibc` or created manually), make +the following changes to the source code. + +Cosmos SDK expects IBC modules +to [implement the `IBCModule` interface](https://ibc.cosmos.network/main/ibc/apps/ibcmodule.html). Create a `IBCModule` +type that embeds the module's keeper and a method that returns a new `IBCModule`. Methods in this file will be defined +on this type. + +```go +// x/{moduleName}/module_ibc.go + +package module_name + +// ... + +type IBCModule struct { + keeper keeper.Keeper +} + +func NewIBCModule(k keeper.Keeper) IBCModule { + return IBCModule{ + keeper: k, + } +} +``` + +Replace receivers for all methods in this file from `(am AppModule)` to `(im IBCModule)`. Replace all instances of `am.` +with `im.` to fix the errors. + +`OnChanOpenInit` now returns to values: a `string` and an `error`: + +```go +// x/{moduleName}/module_ibc.go + +package module_name + +// ... + +func (im IBCModule) OnChanOpenInit( /*...*/ ) (string, error) +``` + +Ensure that all return statements (five, in the default template) in `OnChanOpenInit` return two values. For example: + +```go +// x/{moduleName}/module_ibc.go + +package module_name + +// ... + +func (im IBCModule) OnChanOpenInit( /*...*/ ) (string, error) { + //... + return "", sdkerrors.Wrapf(porttypes.ErrInvalidPort, "invalid port: %s, expected %s", portID, boundPort) + //... +} +``` + +Error acknowledgments returned from Transfer `OnRecvPacket` now include a deterministic ABCI code and error message. +Remove the `.Error()` call: + +```go +// x/{moduleName}/module_ibc.go + +package module_name + +// ... + +func (im IBCModule) OnRecvPacket( /*...*/ ) { + //... + if err := modulePacketData.Unmarshal(modulePacket.GetData()); err != nil { + // return channeltypes.NewErrorAcknowledgement(sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "cannot unmarshal packet data: %s", err.Error()).Error()) + return channeltypes.NewErrorAcknowledgement(sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "cannot unmarshal packet data: %s", err.Error())) + } + + // ... + + // Dispatch packet + switch packet := modulePacketData.Packet.(type) { + // ... + default: + // errMsg := fmt.Sprintf("unrecognized %s packet type: %T", types.ModuleName, packet) + // return channeltypes.NewErrorAcknowledgement(errMsg) + err := fmt.Errorf("unrecognized %s packet type: %T", types.ModuleName, packet) + return channeltypes.NewErrorAcknowledgement(err) + } +} +``` + +After switching to using both `AppModule` and `IBCModule`, modifying the following line: + +```go +// x/{moduleName}/module.go + +package module_name + +// ... + +var ( + //... + _ porttypes.IBCModule = IBCModule{} // instead of "= AppModule{}" +) +``` + +### Main + +The `Execute` function in Cosmos SDK `server/cmd` package now +accepts [three arguments](https://github.com/cosmos/cosmos-sdk/blob/v0.46.0/server/cmd/execute.go#L20) instead of two. + +```go +// cmd/{{projectName}}d/main.go + +package projectNamed + +// ... + +func main() { + //... + if err := svrcmd.Execute(rootCmd, "", app.DefaultNodeHome); err != nil { + os.Exit(1) + } +} +``` + +### Handler + +Cosmos SDK v0.46 no longer needs a `NewHandler` function that was used to handle messages and call appropriate keeper +methods based on message types. Feel free to remove `x/{moduleName}/handler.go` file. + +Since there is no `NewHandler` now, modify the deprecated `Route` function to return `sdk.Route{}`: + +```go +// x/{moduleName}/module.go + +package module_name + +// ... + +func (am AppModule) Route() sdk.Route { return sdk.Route{} } +``` diff --git a/docs/versioned_docs/version-v0.27.1/06-migration/v0.25.0.md b/docs/versioned_docs/version-v0.27.1/06-migration/v0.25.0.md new file mode 100644 index 0000000000..35db7963f2 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/06-migration/v0.25.0.md @@ -0,0 +1,1187 @@ +--- +sidebar_position: 994 +title: v0.25.0 +description: For chains that were scaffolded with Ignite CLI versions lower than v0.25.0. changes are required to use Ignite CLI v0.25.0. +--- + +## Protobuf directory migration + +`v0.25.0` changes the location of scaffolded `.proto` files. Previously, `.proto` files were located in `./proto/{moduleName}/`, +where `moduleName` is the same name of the Cosmos SDK module found in `./x/{moduleName}/`. This new version of `ignite` +modifies the scaffolded protobuf files so that they are now generated in `./proto/{appName}/{moduleName}`. + +The only change that is needed to be made is to create an `{appName}` folder in the `proto` directory, and then place the +sub-directories within it. An example below demonstrates this change: + +### Previous Directory Structure + +This example shows a chain that was generated using `ignite` with `v0.24.0` using the following command: + +```bash +ignite s chain github.com/cosmos/planet --no-module +ignite s module mars +``` + +```bash +├── app +├── cmd +├── docs +├── proto +│ ├── mars +├── x +│ ├── mars +├── README.md +├── config.yml +├── go.mod +├── go.sum +└── .gitignore +``` + +### `v0.25.0` Directory Structure + +This example shows a chain that was generated using `ignite` with `v0.25.0` using the following command: + +```bash +ignite s chain github.com/cosmos/planet --no-module +ignite s module mars +``` + +```bash +├── app +├── cmd +├── docs +├── proto +│ ├── planet +│ │ ├── mars +├── x +│ ├── mars +├── README.md +├── config.yml +├── go.mod +├── go.sum +└── .gitignore +``` + +The only difference is the additional directory `planet` which is the name of the application. The name of the app can +be verified by checking the package in the `go.mod` file. In this example, the package is `github.com/cosmos/planet` +where `planet` is the app name. + + --- + +## Removing `cosmoscmd` + +`v0.25.0` removes the `cosmoscmd` package from scaffolded chains. This package provided utility for creating +commands and starting up their application. The `cosmoscmd` package is now deprecated, and it is suggested that chains +implement this functionality in their codebase so they can be more easily upgraded and customized. + +The main functionality of `cosmoscmd` will be moved to the `app` package of your chain. Some imports in these +examples contain the sample string, `{ModulePath}`. Replace this string with the Go module path of your blockchain. +For example, if your blockchain module path is `github.com/planet/mars`, `{ModulePath}/app/params` would be become +`github.com/planet/mars/app/params`. + +#### Migration in `app` package + +To begin, create a new file, `./app/params/encoding.go`, containing the following code: + +```go +package params + +import ( + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" +) + +// EncodingConfig specifies the concrete encoding types to use for a given app. +// This is provided for compatibility between protobuf and amino implementations. +type EncodingConfig struct { + InterfaceRegistry types.InterfaceRegistry + Marshaler codec.Codec + TxConfig client.TxConfig + Amino *codec.LegacyAmino +} +``` + +Next, create a new file, `./app/encoding.go`, containing the following code: + +```go +package app + +import ( + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/std" + "github.com/cosmos/cosmos-sdk/x/auth/tx" + + "{ModulePath}/app/params" +) + +// makeEncodingConfig creates an EncodingConfig for an amino based test configuration. +func makeEncodingConfig() params.EncodingConfig { + amino := codec.NewLegacyAmino() + interfaceRegistry := types.NewInterfaceRegistry() + marshaler := codec.NewProtoCodec(interfaceRegistry) + txCfg := tx.NewTxConfig(marshaler, tx.DefaultSignModes) + + return params.EncodingConfig{ + InterfaceRegistry: interfaceRegistry, + Marshaler: marshaler, + TxConfig: txCfg, + Amino: amino, + } +} + +// MakeEncodingConfig creates an EncodingConfig for testing +func MakeEncodingConfig() params.EncodingConfig { + encodingConfig := makeEncodingConfig() + std.RegisterLegacyAminoCodec(encodingConfig.Amino) + std.RegisterInterfaces(encodingConfig.InterfaceRegistry) + ModuleBasics.RegisterLegacyAminoCodec(encodingConfig.Amino) + ModuleBasics.RegisterInterfaces(encodingConfig.InterfaceRegistry) + return encodingConfig +} +``` + +Next, modify `./app/simulation_test.go` so that it looks like the following: + +```go +package app_test + +import ( + "os" + "testing" + "time" + + "github.com/cosmos/cosmos-sdk/simapp" + simulationtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/simulation" + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + tmtypes "github.com/tendermint/tendermint/types" + // remove-next-line + "github.com/ignite/cli/ignite/pkg/cosmoscmd" + + // highlight-next-line + "{ModulePath}/app" +) + +// remove-start +type SimApp interface { + cosmoscmd.App + GetBaseApp() *baseapp.BaseApp + AppCodec() codec.Codec + SimulationManager() *module.SimulationManager + ModuleAccountAddrs() map[string]bool + Name() string + LegacyAmino() *codec.LegacyAmino + BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) + abci.ResponseBeginBlock + EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) + abci.ResponseEndBlock + InitChainer(ctx sdk.Context, req abci.RequestInitChain) + abci.ResponseInitChain +} + +// remove-end + +// ... + +// BenchmarkSimulation run the chain simulation +// Running using starport command: +// `starport chain simulate -v --numBlocks 200 --blockSize 50` +// Running as go benchmark test: +// `go test -benchmem -run=^$ -bench ^BenchmarkSimulation ./app -NumBlocks=200 -BlockSize 50 -Commit=true -Verbose=true -Enabled=true` +func BenchmarkSimulation(b *testing.B) { + + // ... + + // remove-next-line + encoding := cosmoscmd.MakeEncodingConfig(app.ModuleBasics) + // highlight-next-line + encoding := app.MakeEncodingConfig() + + app := app.New( + logger, + db, + nil, + true, + map[int64]bool{}, + app.DefaultNodeHome, + 0, + encoding, + simapp.EmptyAppOptions{}, + ) + + // remove-start + simApp, ok := app.(SimApp) + require.True(b, ok, "can't use simapp") + // remove-end + + // Run randomized simulations + _, simParams, simErr := simulation.SimulateFromSeed( + b, + os.Stdout, + // highlight-next-line + app.BaseApp, + // highlight-next-line + simapp.AppStateFn(app.AppCodec(), app.SimulationManager()), + simulationtypes.RandomAccounts, + // highlight-next-line + simapp.SimulationOperations(app, app.AppCodec(), config), + // highlight-next-line + app.ModuleAccountAddrs(), + config, + // highlight-next-line + app.AppCodec(), + ) + + // export state and simParams before the simulation error is checked + // highlight-next-line + err = simapp.CheckExportSimulation(app, config, simParams) + require.NoError(b, err) + require.NoError(b, simErr) + + // ... +} +``` + +The main changes here are that the `SimApp` interface has been removed and is being replaced with `app`. + +The final modification in the `app` package is in `app/app.go`: + +```go +package app + +import ( + // ... + + // this line is used by starport scaffolding # stargate/app/moduleImport + + // remove-next-line + "github.com/ignite/cli/ignite/pkg/cosmoscmd" + + // highlight-start + appparams "{ModulePath}/app/params" + "{ModulePath}/docs" + // highlight-end +) + +// ... + +var ( + // remove-next-line + _ cosmoscmd.App = (*App)(nil) + _ servertypes.Application = (*App)(nil) + _ simapp.App = (*App)(nil) +) + +// ... + +// New returns a reference to an initialized blockchain app +func New( + logger log.Logger, + db dbm.DB, + traceStore io.Writer, + loadLatest bool, + skipUpgradeHeights map[int64]bool, + homePath string, + invCheckPeriod uint, + // highlight-next-line + encodingConfig appparams.EncodingConfig, + appOpts servertypes.AppOptions, + baseAppOptions ...func(*baseapp.BaseApp), + // highlight-next-line +) *App { + appCodec := encodingConfig.Marshaler + cdc := encodingConfig.Amino + interfaceRegistry := encodingConfig.InterfaceRegistry + + bApp := baseapp.NewBaseApp( + Name, + logger, + db, + encodingConfig.TxConfig.TxDecoder(), + baseAppOptions..., + ) + + // ... + +} + +// ... + +// Name returns the name of the App +func (app *App) Name() string { return app.BaseApp.Name() } + +// remove-start +// GetBaseApp returns the base app of the application +func (app App) GetBaseApp() *baseapp.BaseApp { return app.BaseApp } + +// remove-end + +// BeginBlocker application updates every begin block +func (app *App) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock { + return app.mm.BeginBlock(ctx, req) +} + +// ... +``` + +Again, here we are removing the use of `cosmoscmd` and replacing it with `app`. + +#### Migration in `cmd` package + +Some imports in these +examples contain the sample string, `{binaryNamePrefix}d`. Replace this string with the binary name of your blockchain. +For example, if your blockchain module path is `github.com/planet/mars`, `./cmd/{binaryNamePrefix}d/cmd/` would be +become `./cmd/marsd/cmd/`. + +First, create the new file `./cmd/{binaryNamePrefix}d/cmd/config.go` with the following code: + +```go +package cmd + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "{ModulePath}/app" +) + +func initSDKConfig() { + // Set prefixes + accountPubKeyPrefix := app.AccountAddressPrefix + "pub" + validatorAddressPrefix := app.AccountAddressPrefix + "valoper" + validatorPubKeyPrefix := app.AccountAddressPrefix + "valoperpub" + consNodeAddressPrefix := app.AccountAddressPrefix + "valcons" + consNodePubKeyPrefix := app.AccountAddressPrefix + "valconspub" + + // Set and seal config + config := sdk.GetConfig() + config.SetBech32PrefixForAccount(app.AccountAddressPrefix, accountPubKeyPrefix) + config.SetBech32PrefixForValidator(validatorAddressPrefix, validatorPubKeyPrefix) + config.SetBech32PrefixForConsensusNode(consNodeAddressPrefix, consNodePubKeyPrefix) + config.Seal() +} +``` + +Next, create the new file `./cmd/{binaryNamePrefix}d/cmd/genaccounts.go` with the following code: + +```go +package cmd + +import ( + "bufio" + "encoding/json" + "errors" + "fmt" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/server" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + authvesting "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/x/genutil" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + "github.com/spf13/cobra" +) + +const ( + flagVestingStart = "vesting-start-time" + flagVestingEnd = "vesting-end-time" + flagVestingAmt = "vesting-amount" +) + +// AddGenesisAccountCmd returns add-genesis-account cobra Command. +func AddGenesisAccountCmd(defaultNodeHome string) *cobra.Command { + cmd := &cobra.Command{ + Use: "add-genesis-account [address_or_key_name] [coin][,[coin]]", + Short: "Add a genesis account to genesis.json", + Long: `Add a genesis account to genesis.json. The provided account must specify +the account address or key name and a list of initial coins. If a key name is given, +the address will be looked up in the local Keybase. The list of initial tokens must +contain valid denominations. Accounts may optionally be supplied with vesting parameters. +`, + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx := client.GetClientContextFromCmd(cmd) + cdc := clientCtx.Codec + + serverCtx := server.GetServerContextFromCmd(cmd) + config := serverCtx.Config + + config.SetRoot(clientCtx.HomeDir) + + coins, err := sdk.ParseCoinsNormalized(args[1]) + if err != nil { + return fmt.Errorf("failed to parse coins: %w", err) + } + + addr, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + inBuf := bufio.NewReader(cmd.InOrStdin()) + keyringBackend, err := cmd.Flags().GetString(flags.FlagKeyringBackend) + if err != nil { + return err + } + + // attempt to lookup address from Keybase if no address was provided + kb, err := keyring.New(sdk.KeyringServiceName(), keyringBackend, clientCtx.HomeDir, inBuf, cdc) + if err != nil { + return err + } + + info, err := kb.Key(args[0]) + if err != nil { + return fmt.Errorf("failed to get address from Keybase: %w", err) + } + + addr, err = info.GetAddress() + if err != nil { + return fmt.Errorf("failed to get address from Keybase: %w", err) + } + } + + vestingStart, err := cmd.Flags().GetInt64(flagVestingStart) + if err != nil { + return err + } + vestingEnd, err := cmd.Flags().GetInt64(flagVestingEnd) + if err != nil { + return err + } + vestingAmtStr, err := cmd.Flags().GetString(flagVestingAmt) + if err != nil { + return err + } + + vestingAmt, err := sdk.ParseCoinsNormalized(vestingAmtStr) + if err != nil { + return fmt.Errorf("failed to parse vesting amount: %w", err) + } + + // create concrete account type based on input parameters + var genAccount authtypes.GenesisAccount + + balances := banktypes.Balance{Address: addr.String(), Coins: coins.Sort()} + baseAccount := authtypes.NewBaseAccount(addr, nil, 0, 0) + + if !vestingAmt.IsZero() { + baseVestingAccount := authvesting.NewBaseVestingAccount(baseAccount, vestingAmt.Sort(), vestingEnd) + + if (balances.Coins.IsZero() && !baseVestingAccount.OriginalVesting.IsZero()) || + baseVestingAccount.OriginalVesting.IsAnyGT(balances.Coins) { + return errors.New("vesting amount cannot be greater than total amount") + } + + switch { + case vestingStart != 0 && vestingEnd != 0: + genAccount = authvesting.NewContinuousVestingAccountRaw(baseVestingAccount, vestingStart) + + case vestingEnd != 0: + genAccount = authvesting.NewDelayedVestingAccountRaw(baseVestingAccount) + + default: + return errors.New("invalid vesting parameters; must supply start and end time or end time") + } + } else { + genAccount = baseAccount + } + + if err := genAccount.Validate(); err != nil { + return fmt.Errorf("failed to validate new genesis account: %w", err) + } + + genFile := config.GenesisFile() + appState, genDoc, err := genutiltypes.GenesisStateFromGenFile(genFile) + if err != nil { + return fmt.Errorf("failed to unmarshal genesis state: %w", err) + } + + authGenState := authtypes.GetGenesisStateFromAppState(cdc, appState) + + accs, err := authtypes.UnpackAccounts(authGenState.Accounts) + if err != nil { + return fmt.Errorf("failed to get accounts from any: %w", err) + } + + if accs.Contains(addr) { + return fmt.Errorf("cannot add account at existing address %s", addr) + } + + // Add the new account to the set of genesis accounts and sanitize the + // accounts afterwards. + accs = append(accs, genAccount) + accs = authtypes.SanitizeGenesisAccounts(accs) + + genAccs, err := authtypes.PackAccounts(accs) + if err != nil { + return fmt.Errorf("failed to convert accounts into any's: %w", err) + } + authGenState.Accounts = genAccs + + authGenStateBz, err := cdc.MarshalJSON(&authGenState) + if err != nil { + return fmt.Errorf("failed to marshal auth genesis state: %w", err) + } + + appState[authtypes.ModuleName] = authGenStateBz + + bankGenState := banktypes.GetGenesisStateFromAppState(cdc, appState) + bankGenState.Balances = append(bankGenState.Balances, balances) + bankGenState.Balances = banktypes.SanitizeGenesisBalances(bankGenState.Balances) + + bankGenStateBz, err := cdc.MarshalJSON(bankGenState) + if err != nil { + return fmt.Errorf("failed to marshal bank genesis state: %w", err) + } + + appState[banktypes.ModuleName] = bankGenStateBz + + appStateJSON, err := json.Marshal(appState) + if err != nil { + return fmt.Errorf("failed to marshal application genesis state: %w", err) + } + + genDoc.AppState = appStateJSON + return genutil.ExportGenesisFile(genDoc, genFile) + }, + } + + cmd.Flags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|kwallet|pass|test)") + cmd.Flags().String(flags.FlagHome, defaultNodeHome, "The application home directory") + cmd.Flags().String(flagVestingAmt, "", "amount of coins for vesting accounts") + cmd.Flags().Int64(flagVestingStart, 0, "schedule start time (unix epoch) for vesting accounts") + cmd.Flags().Int64(flagVestingEnd, 0, "schedule end time (unix epoch) for vesting accounts") + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} +``` + +This command allows one to generate new accounts: `appd add-genesis-account`. + +Next, create the new file `./cmd/{binaryNamePrefix}d/cmd/root.go` with the following code: + +```go +package cmd + +import ( + "errors" + "io" + "os" + "path/filepath" + "strings" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/config" + "github.com/cosmos/cosmos-sdk/client/debug" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/keys" + "github.com/cosmos/cosmos-sdk/client/rpc" + "github.com/cosmos/cosmos-sdk/server" + serverconfig "github.com/cosmos/cosmos-sdk/server/config" + servertypes "github.com/cosmos/cosmos-sdk/server/types" + "github.com/cosmos/cosmos-sdk/snapshots" + snapshottypes "github.com/cosmos/cosmos-sdk/snapshots/types" + "github.com/cosmos/cosmos-sdk/store" + sdk "github.com/cosmos/cosmos-sdk/types" + authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" + "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/x/crisis" + genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" + "github.com/ignite/cli/ignite/services/network" + "github.com/spf13/cast" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + tmcfg "github.com/tendermint/tendermint/config" + tmcli "github.com/tendermint/tendermint/libs/cli" + "github.com/tendermint/tendermint/libs/log" + dbm "github.com/tendermint/tm-db" + // this line is used by starport scaffolding # root/moduleImport + + "{ModulePath}/app" + appparams "{ModulePath}/app/params" +) + +// NewRootCmd creates a new root command for a Cosmos SDK application +func NewRootCmd() (*cobra.Command, appparams.EncodingConfig) { + encodingConfig := app.MakeEncodingConfig() + initClientCtx := client.Context{}. + WithCodec(encodingConfig.Marshaler). + WithInterfaceRegistry(encodingConfig.InterfaceRegistry). + WithTxConfig(encodingConfig.TxConfig). + WithLegacyAmino(encodingConfig.Amino). + WithInput(os.Stdin). + WithAccountRetriever(types.AccountRetriever{}). + WithHomeDir(app.DefaultNodeHome). + WithViper("") + + rootCmd := &cobra.Command{ + Use: app.Name + "d", + Short: "Stargate CosmosHub App", + PersistentPreRunE: func(cmd *cobra.Command, _ []string) error { + // set the default command outputs + cmd.SetOut(cmd.OutOrStdout()) + cmd.SetErr(cmd.ErrOrStderr()) + initClientCtx, err := client.ReadPersistentCommandFlags(initClientCtx, cmd.Flags()) + if err != nil { + return err + } + initClientCtx, err = config.ReadFromClientConfig(initClientCtx) + if err != nil { + return err + } + + if err := client.SetCmdClientContextHandler(initClientCtx, cmd); err != nil { + return err + } + + customAppTemplate, customAppConfig := initAppConfig() + customTMConfig := initTendermintConfig() + return server.InterceptConfigsPreRunHandler( + cmd, customAppTemplate, customAppConfig, customTMConfig, + ) + }, + } + + initRootCmd(rootCmd, encodingConfig) + overwriteFlagDefaults(rootCmd, map[string]string{ + flags.FlagChainID: strings.ReplaceAll(app.Name, "-", ""), + flags.FlagKeyringBackend: "test", + }) + + return rootCmd, encodingConfig +} + +// initTendermintConfig helps to override default Tendermint Config values. +// return tmcfg.DefaultConfig if no custom configuration is required for the application. +func initTendermintConfig() *tmcfg.Config { + cfg := tmcfg.DefaultConfig() + return cfg +} + +func initRootCmd( + rootCmd *cobra.Command, + encodingConfig appparams.EncodingConfig, +) { + // Set config + initSDKConfig() + + rootCmd.AddCommand( + genutilcli.InitCmd(app.ModuleBasics, app.DefaultNodeHome), + genutilcli.CollectGenTxsCmd(banktypes.GenesisBalancesIterator{}, app.DefaultNodeHome), + genutilcli.MigrateGenesisCmd(), + genutilcli.GenTxCmd( + app.ModuleBasics, + encodingConfig.TxConfig, + banktypes.GenesisBalancesIterator{}, + app.DefaultNodeHome, + ), + genutilcli.ValidateGenesisCmd(app.ModuleBasics), + AddGenesisAccountCmd(app.DefaultNodeHome), + tmcli.NewCompletionCmd(rootCmd, true), + debug.Cmd(), + config.Cmd(), + // this line is used by starport scaffolding # root/commands + ) + + a := appCreator{ + encodingConfig, + } + + // add server commands + server.AddCommands( + rootCmd, + app.DefaultNodeHome, + a.newApp, + a.appExport, + addModuleInitFlags, + ) + + // add keybase, auxiliary RPC, query, and tx child commands + rootCmd.AddCommand( + rpc.StatusCommand(), + queryCommand(), + txCommand(), + keys.Commands(app.DefaultNodeHome), + ) +} + +// queryCommand returns the sub-command to send queries to the app +func queryCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "query", + Aliases: []string{"q"}, + Short: "Querying subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + cmd.AddCommand( + authcmd.GetAccountCmd(), + rpc.ValidatorCommand(), + rpc.BlockCommand(), + authcmd.QueryTxsByEventsCmd(), + authcmd.QueryTxCmd(), + ) + + app.ModuleBasics.AddQueryCommands(cmd) + cmd.PersistentFlags().String(flags.FlagChainID, "", "The network chain ID") + + return cmd +} + +// txCommand returns the sub-command to send transactions to the app +func txCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "tx", + Short: "Transactions subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + cmd.AddCommand( + authcmd.GetSignCommand(), + authcmd.GetSignBatchCommand(), + authcmd.GetMultiSignCommand(), + authcmd.GetValidateSignaturesCommand(), + flags.LineBreak, + authcmd.GetBroadcastCommand(), + authcmd.GetEncodeCommand(), + authcmd.GetDecodeCommand(), + ) + + app.ModuleBasics.AddTxCommands(cmd) + cmd.PersistentFlags().String(flags.FlagChainID, "", "The network chain ID") + + return cmd +} + +func addModuleInitFlags(startCmd *cobra.Command) { + crisis.AddModuleInitFlags(startCmd) + // this line is used by starport scaffolding # root/arguments +} + +func overwriteFlagDefaults(c *cobra.Command, defaults map[string]string) { + set := func(s *pflag.FlagSet, key, val string) { + if f := s.Lookup(key); f != nil { + f.DefValue = val + f.Value.Set(val) + } + } + for key, val := range defaults { + set(c.Flags(), key, val) + set(c.PersistentFlags(), key, val) + } + for _, c := range c.Commands() { + overwriteFlagDefaults(c, defaults) + } +} + +type appCreator struct { + encodingConfig appparams.EncodingConfig +} + +// newApp creates a new Cosmos SDK app +func (a appCreator) newApp( + logger log.Logger, + db dbm.DB, + traceStore io.Writer, + appOpts servertypes.AppOptions, +) servertypes.Application { + var cache sdk.MultiStorePersistentCache + + if cast.ToBool(appOpts.Get(server.FlagInterBlockCache)) { + cache = store.NewCommitKVStoreCacheManager() + } + + skipUpgradeHeights := make(map[int64]bool) + for _, h := range cast.ToIntSlice(appOpts.Get(server.FlagUnsafeSkipUpgrades)) { + skipUpgradeHeights[int64(h)] = true + } + + pruningOpts, err := server.GetPruningOptionsFromFlags(appOpts) + if err != nil { + panic(err) + } + + snapshotDir := filepath.Join(cast.ToString(appOpts.Get(flags.FlagHome)), "data", "snapshots") + snapshotDB, err := dbm.NewDB("metadata", dbm.GoLevelDBBackend, snapshotDir) + if err != nil { + panic(err) + } + snapshotStore, err := snapshots.NewStore(snapshotDB, snapshotDir) + if err != nil { + panic(err) + } + + snapshotOptions := snapshottypes.NewSnapshotOptions( + cast.ToUint64(appOpts.Get(server.FlagStateSyncSnapshotInterval)), + cast.ToUint32(appOpts.Get(server.FlagStateSyncSnapshotKeepRecent)), + ) + + return app.New( + logger, + db, + traceStore, + true, + skipUpgradeHeights, + cast.ToString(appOpts.Get(flags.FlagHome)), + cast.ToUint(appOpts.Get(server.FlagInvCheckPeriod)), + a.encodingConfig, + appOpts, + baseapp.SetPruning(pruningOpts), + baseapp.SetMinGasPrices(cast.ToString(appOpts.Get(server.FlagMinGasPrices))), + baseapp.SetMinRetainBlocks(cast.ToUint64(appOpts.Get(server.FlagMinRetainBlocks))), + baseapp.SetHaltHeight(cast.ToUint64(appOpts.Get(server.FlagHaltHeight))), + baseapp.SetHaltTime(cast.ToUint64(appOpts.Get(server.FlagHaltTime))), + baseapp.SetInterBlockCache(cache), + baseapp.SetTrace(cast.ToBool(appOpts.Get(server.FlagTrace))), + baseapp.SetIndexEvents(cast.ToStringSlice(appOpts.Get(server.FlagIndexEvents))), + baseapp.SetSnapshot(snapshotStore, snapshotOptions), + ) +} + +// appExport creates a new simapp (optionally at a given height) +func (a appCreator) appExport( + logger log.Logger, + db dbm.DB, + traceStore io.Writer, + height int64, + forZeroHeight bool, + jailAllowedAddrs []string, + appOpts servertypes.AppOptions, +) (servertypes.ExportedApp, error) { + homePath, ok := appOpts.Get(flags.FlagHome).(string) + if !ok || homePath == "" { + return servertypes.ExportedApp{}, errors.New("application home not set") + } + + app := app.New( + logger, + db, + traceStore, + height == -1, // -1: no height provided + map[int64]bool{}, + homePath, + uint(1), + a.encodingConfig, + appOpts, + ) + + if height != -1 { + if err := app.LoadHeight(height); err != nil { + return servertypes.ExportedApp{}, err + } + } + + return app.ExportAppStateAndValidators(forZeroHeight, jailAllowedAddrs) +} + +// initAppConfig helps to override default appConfig template and configs. +// return "", nil if no custom configuration is required for the application. +func initAppConfig() (string, interface{}) { + // The following code snippet is just for reference. + + // WASMConfig defines configuration for the wasm module. + type WASMConfig struct { + // This is the maximum sdk gas (wasm and storage) that we allow for any x/wasm "smart" queries + QueryGasLimit uint64 `mapstructure:"query_gas_limit"` + + // Address defines the gRPC-web server to listen on + LruSize uint64 `mapstructure:"lru_size"` + } + + type CustomAppConfig struct { + serverconfig.Config + + WASM WASMConfig `mapstructure:"wasm"` + } + + // Optionally allow the chain developer to overwrite the SDK's default + // server config. + srvCfg := serverconfig.DefaultConfig() + // The SDK's default minimum gas price is set to "" (empty value) inside + // app.toml. If left empty by validators, the node will halt on startup. + // However, the chain developer can set a default app.toml value for their + // validators here. + // + // In summary: + // - if you leave srvCfg.MinGasPrices = "", all validators MUST tweak their + // own app.toml config, + // - if you set srvCfg.MinGasPrices non-empty, validators CAN tweak their + // own app.toml to override, or use this default value. + // + // In simapp, we set the min gas prices to 0. + srvCfg.MinGasPrices = "0stake" + + customAppConfig := CustomAppConfig{ + Config: *srvCfg, + WASM: WASMConfig{ + LruSize: 1, + QueryGasLimit: 300000, + }, + } + + customAppTemplate := serverconfig.DefaultConfigTemplate + ` +[wasm] +# This is the maximum sdk gas (wasm and storage) that we allow for any x/wasm "smart" queries +query_gas_limit = 300000 +# This is the number of wasm vm instances we keep cached in memory for speed-up +# Warning: this is currently unstable and may lead to crashes, best to keep for 0 unless testing locally +lru_size = 0` + + return customAppTemplate, customAppConfig +} +``` + +Finally, modify `./cmd/{binaryNamePrefix}d/main.go` to include the new changes: + +```go +package main + +import ( + "os" + + "github.com/cosmos/cosmos-sdk/server" + svrcmd "github.com/cosmos/cosmos-sdk/server/cmd" + // remove-next-line + "github.com/ignite/cli/ignite/pkg/cosmoscmd" + + "{ModulePath}/app" + "{ModulePath}/cmd/{BinaryNamePrefix}d/cmd" +) + +func main() { + // highlight-start + rootCmd, _ := cmd.NewRootCmd() + if err := svrcmd.Execute(rootCmd, "", app.DefaultNodeHome); err != nil { + switch e := err.(type) { + case server.ErrorCode: + os.Exit(e.Code) + + default: + os.Exit(1) + } + } + // highlight-end +} +``` + +#### Migration in `testutil` package + +Modify `./testutil/network/network.go` to include the new changes: + + +```go +package network + +import ( + "fmt" + "testing" + "time" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + pruningtypes "github.com/cosmos/cosmos-sdk/pruning/types" + servertypes "github.com/cosmos/cosmos-sdk/server/types" + "github.com/cosmos/cosmos-sdk/simapp" + "github.com/cosmos/cosmos-sdk/testutil/network" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/stretchr/testify/require" + tmrand "github.com/tendermint/tendermint/libs/rand" + tmdb "github.com/tendermint/tm-db" + + // highlight-next-line + "{ModulePath}/app" + + // remove-next-line + "github.com/ignite/cli/ignite/pkg/cosmoscmd" +) + +// ... + +// DefaultConfig will initialize config for the network with custom application, +// genesis and single validator. All other parameters are inherited from cosmos-sdk/testutil/network.DefaultConfig +func DefaultConfig() network.Config { + // highlight-next-line + encoding := app.MakeEncodingConfig() + // remove-next-line + encoding := cosmoscmd.MakeEncodingConfig(app.ModuleBasics) + return network.Config{ + Codec: encoding.Marshaler, + TxConfig: encoding.TxConfig, + LegacyAmino: encoding.Amino, + InterfaceRegistry: encoding.InterfaceRegistry, + AccountRetriever: authtypes.AccountRetriever{}, + AppConstructor: func(val network.Validator) servertypes.Application { + return app.New( + val.Ctx.Logger, tmdb.NewMemDB(), nil, true, map[int64]bool{}, val.Ctx.Config.RootDir, 0, + encoding, + simapp.EmptyAppOptions{}, + baseapp.SetPruning(pruningtypes.NewPruningOptionsFromString(val.AppConfig.Pruning)), + baseapp.SetMinGasPrices(val.AppConfig.MinGasPrices), + ) + }, + GenesisState: app.ModuleBasics.DefaultGenesis(encoding.Marshaler), + TimeoutCommit: 2 * time.Second, + ChainID: "chain-" + tmrand.NewRand().Str(6), + NumValidators: 1, + BondDenom: sdk.DefaultBondDenom, + MinGasPrices: fmt.Sprintf("0.000006%s", sdk.DefaultBondDenom), + AccountTokens: sdk.TokensFromConsensusPower(1000, sdk.DefaultPowerReduction), + StakingTokens: sdk.TokensFromConsensusPower(500, sdk.DefaultPowerReduction), + BondedTokens: sdk.TokensFromConsensusPower(100, sdk.DefaultPowerReduction), + PruningStrategy: pruningtypes.PruningOptionNothing, + CleanupDir: true, + SigningAlgo: string(hd.Secp256k1Type), + KeyringOptions: []keyring.Option{}, + } +} +``` + + --- + +## Fix ICA controller keeper wiring + +Related issue: https://github.com/ignite/cli/issues/2867 + +Apply the following changes to `app/app.go` file : + +```go +package app + +import ( + + // highlight-start + icacontrollerkeeper "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/controller/keeper" + icacontrollertypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/controller/types" + // highlight-end + // ... +) + +// New returns a reference to an initialized blockchain app +func New( + logger log.Logger, + db dbm.DB, + traceStore io.Writer, + loadLatest bool, + skipUpgradeHeights map[int64]bool, + homePath string, + invCheckPeriod uint, + encodingConfig appparams.EncodingConfig, + appOpts servertypes.AppOptions, + baseAppOptions ...func(*baseapp.BaseApp), +) *App { + + // ... + + keys := sdk.NewKVStoreKeys( + authtypes.StoreKey, authz.ModuleName, banktypes.StoreKey, + stakingtypes.StoreKey, + minttypes.StoreKey, distrtypes.StoreKey, slashingtypes.StoreKey, + govtypes.StoreKey, + paramstypes.StoreKey, ibchost.StoreKey, upgradetypes.StoreKey, + feegrant.StoreKey, evidencetypes.StoreKey, + ibctransfertypes.StoreKey, icahosttypes.StoreKey, + capabilitytypes.StoreKey, group.StoreKey, + // highlight-next-line + icacontrollertypes.StoreKey, + yourchainmoduletypes.StoreKey, + // this line is used by starport scaffolding # stargate/app/storeKey + ) + + // ... + + // remove-next-line + icaModule := ica.NewAppModule(nil, &app.ICAHostKeeper) + // highlight-start + icaControllerKeeper := icacontrollerkeeper.NewKeeper( + appCodec, keys[icacontrollertypes.StoreKey], + app.GetSubspace(icacontrollertypes.SubModuleName), + app.IBCKeeper.ChannelKeeper, // may be replaced with middleware such as ics29 fee + app.IBCKeeper.ChannelKeeper, &app.IBCKeeper.PortKeeper, + scopedICAControllerKeeper, app.MsgServiceRouter(), + ) + icaModule := ica.NewAppModule(&icaControllerKeeper, &app.ICAHostKeeper) + // highlight-end + icaHostIBCModule := icahost.NewIBCModule(app.ICAHostKeeper) + + // ... +} + +// ... + +// initParamsKeeper init params keeper and its subspaces +func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino, key, tkey storetypes.StoreKey) paramskeeper.Keeper { + paramsKeeper := paramskeeper.NewKeeper(appCodec, legacyAmino, key, tkey) + + paramsKeeper.Subspace(authtypes.ModuleName) + paramsKeeper.Subspace(banktypes.ModuleName) + paramsKeeper.Subspace(stakingtypes.ModuleName) + paramsKeeper.Subspace(minttypes.ModuleName) + paramsKeeper.Subspace(distrtypes.ModuleName) + paramsKeeper.Subspace(slashingtypes.ModuleName) + paramsKeeper.Subspace(govtypes.ModuleName).WithKeyTable(govv1.ParamKeyTable()) + paramsKeeper.Subspace(crisistypes.ModuleName) + paramsKeeper.Subspace(ibctransfertypes.ModuleName) + paramsKeeper.Subspace(ibchost.ModuleName) + // highlight-next-line + paramsKeeper.Subspace(icacontrollertypes.SubModuleName) + paramsKeeper.Subspace(icahosttypes.SubModuleName) + paramsKeeper.Subspace(mychainmoduletypes.ModuleName) + // this line is used by starport scaffolding # stargate/app/paramSubspace + + return paramsKeeper +} +``` + + --- + +## Fix capability keeper not sealed + +Related issue: https://github.com/ignite/cli/issues/1921 + +Apply the following change to `app/app.go` file : + +```go +package app + +// New returns a reference to an initialized blockchain app +func New( + logger log.Logger, + db dbm.DB, + traceStore io.Writer, + loadLatest bool, + skipUpgradeHeights map[int64]bool, + homePath string, + invCheckPeriod uint, + encodingConfig appparams.EncodingConfig, + appOpts servertypes.AppOptions, + baseAppOptions ...func(*baseapp.BaseApp), +) *App { + + // ... + + // this line is used by starport scaffolding # stargate/app/keeperDefinition + + // highlight-start + // Sealing prevents other modules from creating scoped sub-keepers + app.CapabilityKeeper.Seal() + // highlight-end + + // Create static IBC router, add transfer route, then set and seal it + + // ... +} +``` diff --git a/docs/versioned_docs/version-v0.27.1/06-migration/v0.25.1.md b/docs/versioned_docs/version-v0.27.1/06-migration/v0.25.1.md new file mode 100644 index 0000000000..42fe7f783c --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/06-migration/v0.25.1.md @@ -0,0 +1,67 @@ +--- +sidebar_position: 993 +title: v0.25.1 +description: For chains that were scaffolded with Ignite CLI versions lower than v0.25.1. changes are required to use Ignite CLI v0.25.1. +--- + +## Drabonberry fix + +`v0.25.1` contains the Dragonberry fix, update your `go.mod` as : + +```sh +require ( + // remove-next-line + github.com/ignite/cli v0.24.0 + // highlight-next-line + github.com/ignite/cli v0.25.1 +) + +// highlight-next-line +replace github.com/confio/ics23/go => github.com/cosmos/cosmos-sdk/ics23/go v0.8.0 +``` + +Then run: + +``` +$ go mod tidy +``` + +As a result, you should see `cosmos-sdk` and `ibc-go` upgraded as well. + +Finally, apply the following change to `app/app.go`: + +```go +package app + +// New returns a reference to an initialized blockchain app +func New( + logger log.Logger, + db dbm.DB, + traceStore io.Writer, + loadLatest bool, + skipUpgradeHeights map[int64]bool, + homePath string, + invCheckPeriod uint, + encodingConfig appparams.EncodingConfig, + appOpts servertypes.AppOptions, + baseAppOptions ...func(*baseapp.BaseApp), +) *App { + + // ... + + app.ICAHostKeeper = icahostkeeper.NewKeeper( + appCodec, keys[icahosttypes.StoreKey], + app.GetSubspace(icahosttypes.SubModuleName), + app.IBCKeeper.ChannelKeeper, + // highlight-next-line + app.IBCKeeper.ChannelKeeper, + &app.IBCKeeper.PortKeeper, + app.AccountKeeper, + scopedICAHostKeeper, + app.MsgServiceRouter(), + ) + + // ... + +} +``` diff --git a/docs/versioned_docs/version-v0.27.1/06-migration/v0.26.0.md b/docs/versioned_docs/version-v0.27.1/06-migration/v0.26.0.md new file mode 100644 index 0000000000..f8895aabf1 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/06-migration/v0.26.0.md @@ -0,0 +1,263 @@ +--- +sidebar_position: 992 +title: v0.26.0 +description: For chains that were scaffolded with Ignite CLI versions lower than v0.26.0. changes are required to use Ignite CLI v0.26.0. +--- + +Ignite CLI `v0.26.0` is fully compatible with chains that are compatible with `v0.25.1`. Please follow the existing +migration guides if your chain is not upgraded to `v0.25.1` support. + +## Go Version + +Chains that are newly scaffolded with Ignite CLI `v0.26.0` now require `go 1.19` in their `go.mod` files. It is +recommended that chains scaffolded with an older version of Ignite CLI also bump their required `go` version and update +their tooling to the latest version. + +## ibc-go v6 + +Chains that are newly scaffolded with Ignite CLI `v0.26.0` now use `ibc-go/v6` for ibc functionality. It is not +necessary, but recommended to upgrade to the newest version of `ibc-go`. Most migrations can be done by following the +`ibc-go` [migration guide](https://github.com/cosmos/ibc-go/blob/main/docs/migrations/v5-to-v6.md), but there are some +specific changes that will need to be followed for Ignite scaffolded chains. + +### Removing `cosmosibckeeper` + +Ignite CLI `v0.26.0` has deprecated [pkg/cosmosibckeeper](https://github.com/ignite/cli/tree/main/ignite/pkg/cosmosibckeeper). +This package contained interfaces for ibc-related keepers. Newly scaffolded chains now include the interface files in their +`./x/{moduleName}/types` directory in a new `expected_ibc_keeper.go` file. To migrate, create the following file for +each module: + +```go title="x/{moduleName}/types/expected_ibc_keeper.go" +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + clienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v6/modules/core/04-channel/types" +) + +// ChannelKeeper defines the expected IBC channel keeper. +type ChannelKeeper interface { + GetChannel(ctx sdk.Context, portID, channelID string) (channeltypes.Channel, bool) + GetNextSequenceSend(ctx sdk.Context, portID, channelID string) (uint64, bool) + SendPacket( + ctx sdk.Context, + channelCap *capabilitytypes.Capability, + sourcePort string, + sourceChannel string, + timeoutHeight clienttypes.Height, + timeoutTimestamp uint64, + data []byte, + ) (uint64, error) + ChanCloseInit(ctx sdk.Context, portID, channelID string, chanCap *capabilitytypes.Capability) error +} + +// PortKeeper defines the expected IBC port keeper. +type PortKeeper interface { + BindPort(ctx sdk.Context, portID string) *capabilitytypes.Capability +} + +// ScopedKeeper defines the expected IBC scoped keeper. +type ScopedKeeper interface { + GetCapability(ctx sdk.Context, name string) (*capabilitytypes.Capability, bool) + AuthenticateCapability(ctx sdk.Context, cap *capabilitytypes.Capability, name string) bool + ClaimCapability(ctx sdk.Context, cap *capabilitytypes.Capability, name string) error +} +``` + +Next, make the following updates to each `x/{moduleName}/keeper/keeper.go` file for each ibc-enabled +module in your project: + +```go title="x/{moduleName}/keeper/keeper.go" +package keeper + +import ( + "fmt" + + // remove-start + "blogibc/x/testibc/types" + "github.com/cosmos/cosmos-sdk/codec" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" + "github.com/ignite/cli/ignite/pkg/cosmosibckeeper" + "github.com/tendermint/tendermint/libs/log" + // remove-end + // highlight-start + "github.com/cosmos/cosmos-sdk/codec" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" + channeltypes "github.com/cosmos/ibc-go/v6/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v6/modules/core/24-host" + "github.com/cosmos/ibc-go/v6/modules/core/exported" + "github.com/tendermint/tendermint/libs/log" + + "{appName}/x/{moduleName}/types" + // highlight-end +) + +type ( + Keeper struct { + // remove-line-next + *cosmosibckeeper.Keeper + cdc codec.BinaryCodec + storeKey storetypes.StoreKey + memKey storetypes.StoreKey + paramstore paramtypes.Subspace + + // highlight-start + channelKeeper types.ChannelKeeper + portKeeper types.PortKeeper + scopedKeeper exported.ScopedKeeper + // highlight-end + } +) + +func NewKeeper( + cdc codec.BinaryCodec, + storeKey, + memKey storetypes.StoreKey, + ps paramtypes.Subspace, + // highlight-start + channelKeeper types.ChannelKeeper, + portKeeper types.PortKeeper, + scopedKeeper types.ScopedKeeper, + // highlight-end +) *Keeper { + // set KeyTable if it has not already been set + if !ps.HasKeyTable() { + ps = ps.WithKeyTable(types.ParamKeyTable()) + } + + return &Keeper{ + // remove-start + Keeper: cosmosibckeeper.NewKeeper( + types.PortKey, + storeKey, + channelKeeper, + portKeeper, + scopedKeeper, + ), + // remove-end + cdc: cdc, + storeKey: storeKey, + memKey: memKey, + paramstore: ps, + // highlight-start + channelKeeper: channelKeeper, + portKeeper: portKeeper, + scopedKeeper: scopedKeeper, + // highlight-end + } +} + +// highlight-start +// ---------------------------------------------------------------------------- +// IBC Keeper Logic +// ---------------------------------------------------------------------------- + +// ChanCloseInit defines a wrapper function for the channel Keeper's function. +func (k Keeper) ChanCloseInit(ctx sdk.Context, portID, channelID string) error { + capName := host.ChannelCapabilityPath(portID, channelID) + chanCap, ok := k.scopedKeeper.GetCapability(ctx, capName) + if !ok { + return sdkerrors.Wrapf(channeltypes.ErrChannelCapabilityNotFound, "could not retrieve channel capability at: %s", capName) + } + return k.channelKeeper.ChanCloseInit(ctx, portID, channelID, chanCap) +} + +// IsBound checks if the IBC app module is already bound to the desired port +func (k Keeper) IsBound(ctx sdk.Context, portID string) bool { + _, ok := k.scopedKeeper.GetCapability(ctx, host.PortPath(portID)) + return ok +} + +// BindPort defines a wrapper function for the port Keeper's function in +// order to expose it to module's InitGenesis function +func (k Keeper) BindPort(ctx sdk.Context, portID string) error { + cap := k.portKeeper.BindPort(ctx, portID) + return k.ClaimCapability(ctx, cap, host.PortPath(portID)) +} + +// GetPort returns the portID for the IBC app module. Used in ExportGenesis +func (k Keeper) GetPort(ctx sdk.Context) string { + store := ctx.KVStore(k.storeKey) + return string(store.Get(types.PortKey)) +} + +// SetPort sets the portID for the IBC app module. Used in InitGenesis +func (k Keeper) SetPort(ctx sdk.Context, portID string) { + store := ctx.KVStore(k.storeKey) + store.Set(types.PortKey, []byte(portID)) +} + +// AuthenticateCapability wraps the scopedKeeper's AuthenticateCapability function +func (k Keeper) AuthenticateCapability(ctx sdk.Context, cap *capabilitytypes.Capability, name string) bool { + return k.scopedKeeper.AuthenticateCapability(ctx, cap, name) +} + +// ClaimCapability allows the IBC app module to claim a capability that core IBC +// passes to it +func (k Keeper) ClaimCapability(ctx sdk.Context, cap *capabilitytypes.Capability, name string) error { + return k.scopedKeeper.ClaimCapability(ctx, cap, name) +} + +//highlight-end + +func (k Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) +} +``` + +### Remaining migration + +After all uses of `cosmosibckeeper` have been removed, you can follow any remaining steps in the`ibc-go`[migration guide](https://github.com/cosmos/ibc-go/blob/main/docs/migrations/v5-to-v6.md). + +## Scaffolded Release Workflow + +The develop branch of the CLI has been deprecated. To continue using the release workflow that uses the CLI to +automatically build and release your chain's binaries, replace develop with main in the following lines: + +```yaml title=".github/workflows/release.yml" +... + +jobs: + might_release: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Prepare Release Variables + id: vars + // highlight-next-line + uses: ignite/cli/actions/release/vars@main + - name: Issue Release Assets + // highlight-next-line + uses: ignite/cli/actions/cli@main + if: ${{ steps.vars.outputs.should_release == 'true' }} + with: + args: chain build --release --release.prefix ${{ steps.vars.outputs.tarball_prefix }} -t linux:amd64 -t darwin:amd64 -t darwin:arm64 + - name: Delete the "latest" Release + uses: dev-drprasad/delete-tag-and-release@v0.2.0 + if: ${{ steps.vars.outputs.is_release_type_latest == 'true' }} + with: + tag_name: ${{ steps.vars.outputs.tag_name }} + delete_release: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Publish the Release + uses: softprops/action-gh-release@v1 + if: ${{ steps.vars.outputs.should_release == 'true' }} + with: + tag_name: ${{ steps.vars.outputs.tag_name }} + files: release/* + prerelease: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} +``` diff --git a/docs/versioned_docs/version-v0.27.1/07-packages/_category_.json b/docs/versioned_docs/version-v0.27.1/07-packages/_category_.json new file mode 100644 index 0000000000..28c5e6b357 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/07-packages/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Packages", + "link": null +} \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.27.1/07-packages/cosmostxcollector.md b/docs/versioned_docs/version-v0.27.1/07-packages/cosmostxcollector.md new file mode 100644 index 0000000000..c38fc2d3d5 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/07-packages/cosmostxcollector.md @@ -0,0 +1,200 @@ +--- +sidebar_position: 0 +title: cosmostxcollector +slug: /packages/cosmostxcollector +--- + +# cosmostxcollector + +The package implements support for collecting transactions and events from Cosmos blockchains +into a data backend and it also adds support for querying the collected data. + +## Transaction and event data collecting + +Transactions and events can be collected using the `cosmostxcollector.Collector` type. This +type uses a `cosmosclient.Client` instance to fetch the data from each block and a data backend +adapter to save the data. + +### Data backend adapters + +Data backend adapters are used to query and save the collected data into different types of data +backends and must implement the `cosmostxcollector.adapter.Adapter` interface. + +An adapter for PostgreSQL is already implemented in `cosmostxcollector.adapter.postgres.Adapter`. +This is the one used in the examples. + +### Example: Data collection + +The data collection example assumes that there is a PostgreSQL database running in the local +environment containing an empty database named "cosmos". + +The required database tables will be created automatically by the collector the first time it is run. + +When the application is run it will fetch all the transactions and events starting from one of the +recent blocks until the current block height and populate the database: + +```go +package main + +import ( + "context" + "log" + + "github.com/ignite/cli/ignite/pkg/clictx" + "github.com/ignite/cli/ignite/pkg/cosmosclient" + "github.com/ignite/cli/ignite/pkg/cosmostxcollector" + "github.com/ignite/cli/ignite/pkg/cosmostxcollector/adapter/postgres" +) + +const ( + // Name of a local PostgreSQL database + dbName = "cosmos" + + // Cosmos RPC address + rpcAddr = "https://rpc.cosmos.network:443" +) + +func collect(ctx context.Context, db postgres.Adapter) error { + // Make sure that the data backend schema is up to date + if err := db.Init(ctx); err != nil { + return err + } + + // Init the Cosmos client + client, err := cosmosclient.New(ctx, cosmosclient.WithNodeAddress(rpcAddr)) + if err != nil { + return err + } + + // Get the latest block height + latestHeight, err := client.LatestBlockHeight(ctx) + if err != nil { + return err + } + + // Collect transactions and events starting from a block height. + // The collector stops at the latest height available at the time of the call. + collector := cosmostxcollector.New(db, client) + if err := collector.Collect(ctx, latestHeight-50); err != nil { + return err + } + + return nil +} + +func main() { + ctx := clictx.From(context.Background()) + + // Init an adapter for a local PostgreSQL database running with the default values + params := map[string]string{"sslmode": "disable"} + db, err := postgres.NewAdapter(dbName, postgres.WithParams(params)) + if err != nil { + log.Fatal(err) + } + + if err := collect(ctx, db); err != nil { + log.Fatal(err) + } +} +``` + +## Queries + +Collected data can be queried through the data backend adapters using event queries or +cursor-based queries. + +Queries support sorting, paging and filtering by using different options during creation. +The cursor-based ones also support the selection of specific fields or properties and also +passing arguments in cases where the query is a function. + +By default no sorting, filtering nor paging is applied to the queries. + +### Event queries + +The event queries return events and their attributes as `[]cosmostxcollector.query.Event`. + +### Example: Query events + +The example reads transfer events from Cosmos' bank module and paginates the results. + +```go +import ( + "context" + + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/ignite/cli/ignite/pkg/cosmostxcollector/adapter/postgres" + "github.com/ignite/cli/ignite/pkg/cosmostxcollector/query" +) + +func queryBankTransferEvents(ctx context.Context, db postgres.Adapter) ([]query.Event, error) { + // Create an event query that returns events of type "transfer" + qry := query.NewEventQuery( + query.WithFilters( + // Filter transfer events from Cosmos' bank module + postgres.FilterByEventType(banktypes.EventTypeTransfer), + ), + query.WithPageSize(10), + query.AtPage(1), + ) + + // Execute the query + return db.QueryEvents(ctx, qry) +} +``` + +### Cursor-based queries + +This type of queries is meant to be used in contexts where the Event queries are not +useful. + +Cursor-based queries can query a single "entity" which can be a table, view or function +in relational databases or a collection or function in non relational data backends. + +The result of these types of queries is a cursor that implements the `cosmostxcollector.query.Cursor` +interface. + +### Example: Query events using cursors + +```go +import ( + "context" + + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/ignite/cli/ignite/pkg/cosmostxcollector/adapter/postgres" + "github.com/ignite/cli/ignite/pkg/cosmostxcollector/query" +) + +func queryBankTransferEventIDs(ctx context.Context, db postgres.Adapter) (ids []int64, err error) { + // Create a query that returns the IDs for events of type "transfer" + qry := query.New( + "event", + query.Fields("id"), + query.WithFilters( + // Filter transfer events from Cosmos' bank module + postgres.NewFilter("type", banktypes.EventTypeTransfer), + ), + query.WithPageSize(10), + query.AtPage(1), + query.SortByFields(query.SortOrderAsc, "id"), + ) + + // Execute the query + cr, err := db.Query(ctx, qry) + if err != nil { + return nil, err + } + + // Read the results + for cr.Next() { + var eventID int64 + + if err := cr.Scan(&eventID); err != nil { + return nil, err + } + + ids = append(ids, eventID) + } + + return ids, nil +} +``` diff --git a/docs/versioned_docs/version-v0.27.1/08-references/01-cli.md b/docs/versioned_docs/version-v0.27.1/08-references/01-cli.md new file mode 100644 index 0000000000..0865265c21 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/08-references/01-cli.md @@ -0,0 +1,4146 @@ +--- +description: Ignite CLI docs. +--- + +# CLI commands + +Documentation for Ignite CLI. +## ignite + +Ignite CLI offers everything you need to scaffold, test, build, and launch your blockchain + +**Synopsis** + +Ignite CLI is a tool for creating sovereign blockchains built with Cosmos SDK, the world’s +most popular modular blockchain framework. Ignite CLI offers everything you need to scaffold, +test, build, and launch your blockchain. + +To get started, create a blockchain: + + ignite scaffold chain example + + +**Options** + +``` + -h, --help help for ignite +``` + +**SEE ALSO** + +* [ignite account](#ignite-account) - Create, delete, and show Ignite accounts +* [ignite chain](#ignite-chain) - Build, init and start a blockchain node +* [ignite completion](#ignite-completion) - Generate the autocompletion script for the specified shell +* [ignite docs](#ignite-docs) - Show Ignite CLI docs +* [ignite generate](#ignite-generate) - Generate clients, API docs from source code +* [ignite network](#ignite-network) - Launch a blockchain in production +* [ignite node](#ignite-node) - Make requests to a live blockchain node +* [ignite plugin](#ignite-plugin) - Handle plugins +* [ignite relayer](#ignite-relayer) - Connect blockchains with an IBC relayer +* [ignite scaffold](#ignite-scaffold) - Create a new blockchain, module, message, query, and more +* [ignite tools](#ignite-tools) - Tools for advanced users +* [ignite version](#ignite-version) - Print the current build information + + +## ignite account + +Create, delete, and show Ignite accounts + +**Synopsis** + +Commands for managing Ignite accounts. An Ignite account is a private/public +keypair stored in a keyring. Currently Ignite accounts are used when interacting +with Ignite relayer commands and when using "ignite network" commands. + +Note: Ignite account commands are not for managing your chain's keys and accounts. Use +you chain's binary to manage accounts from "config.yml". For example, if your +blockchain is called "mychain", use "mychaind keys" to manage keys for the +chain. + + +**Options** + +``` + -h, --help help for account + --keyring-backend string keyring backend to store your account keys (default "test") + --keyring-dir string accounts keyring directory (default "/home/runner/.ignite/accounts") +``` + +**SEE ALSO** + +* [ignite](#ignite) - Ignite CLI offers everything you need to scaffold, test, build, and launch your blockchain +* [ignite account create](#ignite-account-create) - Create a new account +* [ignite account delete](#ignite-account-delete) - Delete an account by name +* [ignite account export](#ignite-account-export) - Export an account as a private key +* [ignite account import](#ignite-account-import) - Import an account by using a mnemonic or a private key +* [ignite account list](#ignite-account-list) - Show a list of all accounts +* [ignite account show](#ignite-account-show) - Show detailed information about a particular account + + +## ignite account create + +Create a new account + +``` +ignite account create [name] [flags] +``` + +**Options** + +``` + -h, --help help for create +``` + +**Options inherited from parent commands** + +``` + --keyring-backend string keyring backend to store your account keys (default "test") + --keyring-dir string accounts keyring directory (default "/home/runner/.ignite/accounts") +``` + +**SEE ALSO** + +* [ignite account](#ignite-account) - Create, delete, and show Ignite accounts + + +## ignite account delete + +Delete an account by name + +``` +ignite account delete [name] [flags] +``` + +**Options** + +``` + -h, --help help for delete +``` + +**Options inherited from parent commands** + +``` + --keyring-backend string keyring backend to store your account keys (default "test") + --keyring-dir string accounts keyring directory (default "/home/runner/.ignite/accounts") +``` + +**SEE ALSO** + +* [ignite account](#ignite-account) - Create, delete, and show Ignite accounts + + +## ignite account export + +Export an account as a private key + +``` +ignite account export [name] [flags] +``` + +**Options** + +``` + -h, --help help for export + --non-interactive do not enter into interactive mode + --passphrase string passphrase to encrypt the exported key + --path string path to export private key. default: ./key_[name] +``` + +**Options inherited from parent commands** + +``` + --keyring-backend string keyring backend to store your account keys (default "test") + --keyring-dir string accounts keyring directory (default "/home/runner/.ignite/accounts") +``` + +**SEE ALSO** + +* [ignite account](#ignite-account) - Create, delete, and show Ignite accounts + + +## ignite account import + +Import an account by using a mnemonic or a private key + +``` +ignite account import [name] [flags] +``` + +**Options** + +``` + -h, --help help for import + --non-interactive do not enter into interactive mode + --passphrase string passphrase to decrypt the imported key (ignored when secret is a mnemonic) + --secret string Your mnemonic or path to your private key (use interactive mode instead to securely pass your mnemonic) +``` + +**Options inherited from parent commands** + +``` + --keyring-backend string keyring backend to store your account keys (default "test") + --keyring-dir string accounts keyring directory (default "/home/runner/.ignite/accounts") +``` + +**SEE ALSO** + +* [ignite account](#ignite-account) - Create, delete, and show Ignite accounts + + +## ignite account list + +Show a list of all accounts + +``` +ignite account list [flags] +``` + +**Options** + +``` + --address-prefix string account address prefix (default "cosmos") + -h, --help help for list +``` + +**Options inherited from parent commands** + +``` + --keyring-backend string keyring backend to store your account keys (default "test") + --keyring-dir string accounts keyring directory (default "/home/runner/.ignite/accounts") +``` + +**SEE ALSO** + +* [ignite account](#ignite-account) - Create, delete, and show Ignite accounts + + +## ignite account show + +Show detailed information about a particular account + +``` +ignite account show [name] [flags] +``` + +**Options** + +``` + --address-prefix string account address prefix (default "cosmos") + -h, --help help for show +``` + +**Options inherited from parent commands** + +``` + --keyring-backend string keyring backend to store your account keys (default "test") + --keyring-dir string accounts keyring directory (default "/home/runner/.ignite/accounts") +``` + +**SEE ALSO** + +* [ignite account](#ignite-account) - Create, delete, and show Ignite accounts + + +## ignite chain + +Build, init and start a blockchain node + +**Synopsis** + +Commands in this namespace let you to build, initialize, and start your +blockchain node locally for development purposes. + +To run these commands you should be inside the project's directory so that +Ignite can find the source code. To ensure that you are, run "ls", you should +see the following files in the output: "go.mod", "x", "proto", "app", etc. + +By default the "build" command will identify the "main" package of the project, +install dependencies if necessary, set build flags, compile the project into a +binary and install the binary. The "build" command is useful if you just want +the compiled binary, for example, to initialize and start the chain manually. It +can also be used to release your chain's binaries automatically as part of +continuous integration workflow. + +The "init" command will build the chain's binary and use it to initialize a +local validator node. By default the validator node will be initialized in your +$HOME directory in a hidden directory that matches the name of your project. +This directory is called a data directory and contains a chain's genesis file +and a validator key. This command is useful if you want to quickly build and +initialize the data directory and use the chain's binary to manually start the +blockchain. The "init" command is meant only for development purposes, not +production. + +The "serve" command builds, initializes, and starts your blockchain locally with +a single validator node for development purposes. "serve" also watches the +source code directory for file changes and intelligently +re-builds/initializes/starts the chain, essentially providing "code-reloading". +The "serve" command is meant only for development purposes, not production. + +To distinguish between production and development consider the following. + +In production, blockchains often run the same software on many validator nodes +that are run by different people and entities. To launch a blockchain in +production, the validator entities coordinate the launch process to start their +nodes simultaneously. + +During development, a blockchain can be started locally on a single validator +node. This convenient process lets you restart a chain quickly and iterate +faster. Starting a chain on a single node in development is similar to starting +a traditional web application on a local server. + +The "faucet" command lets you send tokens to an address from the "faucet" +account defined in "config.yml". Alternatively, you can use the chain's binary +to send token from any other account that exists on chain. + +The "simulate" command helps you start a simulation testing process for your +chain. + + +**Options** + +``` + -c, --config string path to Ignite config file (default: ./config.yml) + -h, --help help for chain + -y, --yes answers interactive yes/no questions with yes +``` + +**SEE ALSO** + +* [ignite](#ignite) - Ignite CLI offers everything you need to scaffold, test, build, and launch your blockchain +* [ignite chain build](#ignite-chain-build) - Build a node binary +* [ignite chain debug](#ignite-chain-debug) - Launch a debugger for a blockchain app +* [ignite chain faucet](#ignite-chain-faucet) - Send coins to an account +* [ignite chain init](#ignite-chain-init) - Initialize your chain +* [ignite chain serve](#ignite-chain-serve) - Start a blockchain node in development +* [ignite chain simulate](#ignite-chain-simulate) - Run simulation testing for the blockchain + + +## ignite chain build + +Build a node binary + +**Synopsis** + + +The build command compiles the source code of the project into a binary and +installs the binary in the $(go env GOPATH)/bin directory. + +You can customize the output directory for the binary using a flag: + + ignite chain build --output dist + +To compile the binary Ignite first compiles protocol buffer (proto) files into +Go source code. Proto files contain required type and services definitions. If +you're using another program to compile proto files, you can use a flag to tell +Ignite to skip the proto compilation step: + + ignite chain build --skip-proto + +Afterwards, Ignite install dependencies specified in the go.mod file. By default +Ignite doesn't check that dependencies of the main module stored in the module +cache have not been modified since they were downloaded. To enforce dependency +checking (essentially, running "go mod verify") use a flag: + + ignite chain build --check-dependencies + +Next, Ignite identifies the "main" package of the project. By default the "main" +package is located in "cmd/{app}d" directory, where "{app}" is the name of the +scaffolded project and "d" stands for daemon. If your project contains more +than one "main" package, specify the path to the one that Ignite should compile +in config.yml: + + build: + main: custom/path/to/main + +By default the binary name will match the top-level module name (specified in +go.mod) with a suffix "d". This can be customized in config.yml: + + build: + binary: mychaind + +You can also specify custom linker flags: + + build: + ldflags: + - "-X main.Version=development" + - "-X main.Date=01/05/2022T19:54" + +To build binaries for a release, use the --release flag. The binaries for one or +more specified release targets are built in a "release/" directory in the +project's source directory. Specify the release targets with GOOS:GOARCH build +tags. If the optional --release.targets is not specified, a binary is created +for your current environment. + + ignite chain build --release -t linux:amd64 -t darwin:amd64 -t darwin:arm64 + + +``` +ignite chain build [flags] +``` + +**Options** + +``` + --check-dependencies verify that cached dependencies have not been modified since they were downloaded + --clear-cache clear the build cache (advanced) + --debug build a debug binary + -h, --help help for build + -o, --output string binary output path + -p, --path string path of the app (default ".") + --release build for a release + --release.prefix string tarball prefix for each release target. Available only with --release flag + -t, --release.targets strings release targets. Available only with --release flag + --skip-proto skip file generation from proto + -v, --verbose verbose output +``` + +**Options inherited from parent commands** + +``` + -c, --config string path to Ignite config file (default: ./config.yml) + -y, --yes answers interactive yes/no questions with yes +``` + +**SEE ALSO** + +* [ignite chain](#ignite-chain) - Build, init and start a blockchain node + + +## ignite chain debug + +Launch a debugger for a blockchain app + +**Synopsis** + +The debug command starts a debug server and launches a debugger. + +Ignite uses the Delve debugger by default. Delve enables you to interact with +your program by controlling the execution of the process, evaluating variables, +and providing information of thread / goroutine state, CPU register state and +more. + +A debug server can optionally be started in cases where default terminal client +is not desirable. When the server starts it first runs the blockchain app, +attaches to it and finally waits for a client connection. It accepts both +JSON-RPC or DAP client connections. + +To start a debug server use the following flag: + + ignite chain debug --server + +To start a debug server with a custom address use the following flags: + + ignite chain debug --server --server-address 127.0.0.1:30500 + +The debug server stops automatically when the client connection is closed. + + +``` +ignite chain debug [flags] +``` + +**Options** + +``` + -h, --help help for debug + -p, --path string path of the app (default ".") + --server start a debug server + --server-address string debug server address (default "127.0.0.1:30500") +``` + +**Options inherited from parent commands** + +``` + -c, --config string path to Ignite config file (default: ./config.yml) + -y, --yes answers interactive yes/no questions with yes +``` + +**SEE ALSO** + +* [ignite chain](#ignite-chain) - Build, init and start a blockchain node + + +## ignite chain faucet + +Send coins to an account + +``` +ignite chain faucet [address] [coin<,...>] [flags] +``` + +**Options** + +``` + -h, --help help for faucet + --home string directory where the blockchain node is initialized + -p, --path string path of the app (default ".") + -v, --verbose verbose output +``` + +**Options inherited from parent commands** + +``` + -c, --config string path to Ignite config file (default: ./config.yml) + -y, --yes answers interactive yes/no questions with yes +``` + +**SEE ALSO** + +* [ignite chain](#ignite-chain) - Build, init and start a blockchain node + + +## ignite chain init + +Initialize your chain + +**Synopsis** + +The init command compiles and installs the binary (like "ignite chain build") +and uses that binary to initialize the blockchain's data directory for one +validator. To learn how the build process works, refer to "ignite chain build +--help". + +By default, the data directory will be initialized in $HOME/.mychain, where +"mychain" is the name of the project. To set a custom data directory use the +--home flag or set the value in config.yml: + + validators: + - name: alice + bonded: '100000000stake' + home: "~/.customdir" + +The data directory contains three files in the "config" directory: app.toml, +config.toml, client.toml. These files let you customize the behavior of your +blockchain node and the client executable. When a chain is re-initialized the +data directory can be reset. To make some values in these files persistent, set +them in config.yml: + + validators: + - name: alice + bonded: '100000000stake' + app: + minimum-gas-prices: "0.025stake" + config: + consensus: + timeout_commit: "5s" + timeout_propose: "5s" + client: + output: "json" + +The configuration above changes the minimum gas price of the validator (by +default the gas price is set to 0 to allow "free" transactions), sets the block +time to 5s, and changes the output format to JSON. To see what kind of values +this configuration accepts see the generated TOML files in the data directory. + +As part of the initialization process Ignite creates on-chain accounts with +token balances. By default, config.yml has two accounts in the top-level +"accounts" property. You can add more accounts and change their token balances. +Refer to config.yml guide to see which values you can set. + +One of these accounts is a validator account and the amount of self-delegated +tokens can be set in the top-level "validator" property. + +One of the most important components of an initialized chain is the genesis +file, the 0th block of the chain. The genesis file is stored in the data +directory "config" subdirectory and contains the initial state of the chain, +including consensus and module parameters. You can customize the values of the +genesis in config.yml: + + genesis: + app_state: + staking: + params: + bond_denom: "foo" + +The example above changes the staking token to "foo". If you change the staking +denom, make sure the validator account has the right tokens. + +The init command is meant to be used ONLY FOR DEVELOPMENT PURPOSES. Under the +hood it runs commands like "appd init", "appd add-genesis-account", "appd +gentx", and "appd collect-gentx". For production, you may want to run these +commands manually to ensure a production-level node initialization. + + +``` +ignite chain init [flags] +``` + +**Options** + +``` + --check-dependencies verify that cached dependencies have not been modified since they were downloaded + --clear-cache clear the build cache (advanced) + --debug build a debug binary + -h, --help help for init + --home string directory where the blockchain node is initialized + -p, --path string path of the app (default ".") + --skip-proto skip file generation from proto +``` + +**Options inherited from parent commands** + +``` + -c, --config string path to Ignite config file (default: ./config.yml) + -y, --yes answers interactive yes/no questions with yes +``` + +**SEE ALSO** + +* [ignite chain](#ignite-chain) - Build, init and start a blockchain node + + +## ignite chain serve + +Start a blockchain node in development + +**Synopsis** + +The serve command compiles and installs the binary (like "ignite chain build"), +uses that binary to initialize the blockchain's data directory for one validator +(like "ignite chain init"), and starts the node locally for development purposes +with automatic code reloading. + +Automatic code reloading means Ignite starts watching the project directory. +Whenever a file change is detected, Ignite automatically rebuilds, reinitializes +and restarts the node. + +Whenever possible Ignite will try to keep the current state of the chain by +exporting and importing the genesis file. + +To force Ignite to start from a clean slate even if a genesis file exists, use +the following flag: + + ignite chain serve --reset-once + +To force Ignite to reset the state every time the source code is modified, use +the following flag: + + ignite chain serve --force-reset + +With Ignite it's possible to start more than one blockchain from the same source +code using different config files. This is handy if you're building +inter-blockchain functionality and, for example, want to try sending packets +from one blockchain to another. To start a node using a specific config file: + + ignite chain serve --config mars.yml + +The serve command is meant to be used ONLY FOR DEVELOPMENT PURPOSES. Under the +hood, it runs "appd start", where "appd" is the name of your chain's binary. For +production, you may want to run "appd start" manually. + + +``` +ignite chain serve [flags] +``` + +**Options** + +``` + --check-dependencies verify that cached dependencies have not been modified since they were downloaded + --clear-cache clear the build cache (advanced) + -f, --force-reset force reset of the app state on start and every source change + --generate-clients generate code for the configured clients on reset or source code change + -h, --help help for serve + --home string directory where the blockchain node is initialized + -p, --path string path of the app (default ".") + --quit-on-fail quit program if the app fails to start + -r, --reset-once reset the app state once on init + --skip-proto skip file generation from proto + -v, --verbose verbose output +``` + +**Options inherited from parent commands** + +``` + -c, --config string path to Ignite config file (default: ./config.yml) + -y, --yes answers interactive yes/no questions with yes +``` + +**SEE ALSO** + +* [ignite chain](#ignite-chain) - Build, init and start a blockchain node + + +## ignite chain simulate + +Run simulation testing for the blockchain + +**Synopsis** + +Run simulation testing for the blockchain. It sends many randomized-input messages of each module to a simulated node and checks if invariants break + +``` +ignite chain simulate [flags] +``` + +**Options** + +``` + --blockSize int operations per block (default 30) + --exportParamsHeight int height to which export the randomly generated params + --exportParamsPath string custom file path to save the exported params JSON + --exportStatePath string custom file path to save the exported app state JSON + --exportStatsPath string custom file path to save the exported simulation statistics JSON + --genesis string custom simulation genesis file; cannot be used with params file + --genesisTime int override genesis UNIX time instead of using a random UNIX time + -h, --help help for simulate + --initialBlockHeight int initial block to start the simulation (default 1) + --lean lean simulation log output + --numBlocks int number of new blocks to simulate from the initial block height (default 200) + --params string custom simulation params file which overrides any random params; cannot be used with genesis + --period uint run slow invariants only once every period assertions + --printAllInvariants print all invariants if a broken invariant is found + --seed int simulation random seed (default 42) + --simulateEveryOperation run slow invariants every operation + -v, --verbose verbose log output +``` + +**Options inherited from parent commands** + +``` + -c, --config string path to Ignite config file (default: ./config.yml) + -y, --yes answers interactive yes/no questions with yes +``` + +**SEE ALSO** + +* [ignite chain](#ignite-chain) - Build, init and start a blockchain node + + +## ignite completion + +Generate the autocompletion script for the specified shell + +**Synopsis** + +Generate the autocompletion script for ignite for the specified shell. +See each sub-command's help for details on how to use the generated script. + + +**Options** + +``` + -h, --help help for completion +``` + +**SEE ALSO** + +* [ignite](#ignite) - Ignite CLI offers everything you need to scaffold, test, build, and launch your blockchain +* [ignite completion bash](#ignite-completion-bash) - Generate the autocompletion script for bash +* [ignite completion fish](#ignite-completion-fish) - Generate the autocompletion script for fish +* [ignite completion powershell](#ignite-completion-powershell) - Generate the autocompletion script for powershell +* [ignite completion zsh](#ignite-completion-zsh) - Generate the autocompletion script for zsh + + +## ignite completion bash + +Generate the autocompletion script for bash + +**Synopsis** + +Generate the autocompletion script for the bash shell. + +This script depends on the 'bash-completion' package. +If it is not installed already, you can install it via your OS's package manager. + +To load completions in your current shell session: + + source <(ignite completion bash) + +To load completions for every new session, execute once: + +**#### Linux:** + + ignite completion bash > /etc/bash_completion.d/ignite + +**#### macOS:** + + ignite completion bash > $(brew --prefix)/etc/bash_completion.d/ignite + +You will need to start a new shell for this setup to take effect. + + +``` +ignite completion bash +``` + +**Options** + +``` + -h, --help help for bash + --no-descriptions disable completion descriptions +``` + +**SEE ALSO** + +* [ignite completion](#ignite-completion) - Generate the autocompletion script for the specified shell + + +## ignite completion fish + +Generate the autocompletion script for fish + +**Synopsis** + +Generate the autocompletion script for the fish shell. + +To load completions in your current shell session: + + ignite completion fish | source + +To load completions for every new session, execute once: + + ignite completion fish > ~/.config/fish/completions/ignite.fish + +You will need to start a new shell for this setup to take effect. + + +``` +ignite completion fish [flags] +``` + +**Options** + +``` + -h, --help help for fish + --no-descriptions disable completion descriptions +``` + +**SEE ALSO** + +* [ignite completion](#ignite-completion) - Generate the autocompletion script for the specified shell + + +## ignite completion powershell + +Generate the autocompletion script for powershell + +**Synopsis** + +Generate the autocompletion script for powershell. + +To load completions in your current shell session: + + ignite completion powershell | Out-String | Invoke-Expression + +To load completions for every new session, add the output of the above command +to your powershell profile. + + +``` +ignite completion powershell [flags] +``` + +**Options** + +``` + -h, --help help for powershell + --no-descriptions disable completion descriptions +``` + +**SEE ALSO** + +* [ignite completion](#ignite-completion) - Generate the autocompletion script for the specified shell + + +## ignite completion zsh + +Generate the autocompletion script for zsh + +**Synopsis** + +Generate the autocompletion script for the zsh shell. + +If shell completion is not already enabled in your environment you will need +to enable it. You can execute the following once: + + echo "autoload -U compinit; compinit" >> ~/.zshrc + +To load completions in your current shell session: + + source <(ignite completion zsh); compdef _ignite ignite + +To load completions for every new session, execute once: + +**#### Linux:** + + ignite completion zsh > "${fpath[1]}/_ignite" + +**#### macOS:** + + ignite completion zsh > $(brew --prefix)/share/zsh/site-functions/_ignite + +You will need to start a new shell for this setup to take effect. + + +``` +ignite completion zsh [flags] +``` + +**Options** + +``` + -h, --help help for zsh + --no-descriptions disable completion descriptions +``` + +**SEE ALSO** + +* [ignite completion](#ignite-completion) - Generate the autocompletion script for the specified shell + + +## ignite docs + +Show Ignite CLI docs + +``` +ignite docs [flags] +``` + +**Options** + +``` + -h, --help help for docs +``` + +**SEE ALSO** + +* [ignite](#ignite) - Ignite CLI offers everything you need to scaffold, test, build, and launch your blockchain + + +## ignite generate + +Generate clients, API docs from source code + +**Synopsis** + +Generate clients, API docs from source code. + +Such as compiling protocol buffer files into Go or implement particular +functionality, for example, generating an OpenAPI spec. + +Produced source code can be regenerated by running a command again and is not +meant to be edited by hand. + + +**Options** + +``` + --clear-cache clear the build cache (advanced) + -h, --help help for generate + -p, --path string path of the app (default ".") +``` + +**SEE ALSO** + +* [ignite](#ignite) - Ignite CLI offers everything you need to scaffold, test, build, and launch your blockchain +* [ignite generate composables](#ignite-generate-composables) - TypeScript frontend client and Vue 3 composables +* [ignite generate hooks](#ignite-generate-hooks) - TypeScript frontend client and React hooks +* [ignite generate openapi](#ignite-generate-openapi) - OpenAPI spec for your chain +* [ignite generate proto-go](#ignite-generate-proto-go) - Compile protocol buffer files to Go source code required by Cosmos SDK +* [ignite generate ts-client](#ignite-generate-ts-client) - TypeScript frontend client +* [ignite generate vuex](#ignite-generate-vuex) - *DEPRECATED* TypeScript frontend client and Vuex stores + + +## ignite generate composables + +TypeScript frontend client and Vue 3 composables + +``` +ignite generate composables [flags] +``` + +**Options** + +``` + -h, --help help for composables + -o, --output string Vue 3 composables output path + -y, --yes answers interactive yes/no questions with yes +``` + +**Options inherited from parent commands** + +``` + --clear-cache clear the build cache (advanced) + -p, --path string path of the app (default ".") +``` + +**SEE ALSO** + +* [ignite generate](#ignite-generate) - Generate clients, API docs from source code + + +## ignite generate hooks + +TypeScript frontend client and React hooks + +``` +ignite generate hooks [flags] +``` + +**Options** + +``` + -h, --help help for hooks + -o, --output string React hooks output path + -y, --yes answers interactive yes/no questions with yes +``` + +**Options inherited from parent commands** + +``` + --clear-cache clear the build cache (advanced) + -p, --path string path of the app (default ".") +``` + +**SEE ALSO** + +* [ignite generate](#ignite-generate) - Generate clients, API docs from source code + + +## ignite generate openapi + +OpenAPI spec for your chain + +``` +ignite generate openapi [flags] +``` + +**Options** + +``` + -h, --help help for openapi + -y, --yes answers interactive yes/no questions with yes +``` + +**Options inherited from parent commands** + +``` + --clear-cache clear the build cache (advanced) + -p, --path string path of the app (default ".") +``` + +**SEE ALSO** + +* [ignite generate](#ignite-generate) - Generate clients, API docs from source code + + +## ignite generate proto-go + +Compile protocol buffer files to Go source code required by Cosmos SDK + +``` +ignite generate proto-go [flags] +``` + +**Options** + +``` + -h, --help help for proto-go + -y, --yes answers interactive yes/no questions with yes +``` + +**Options inherited from parent commands** + +``` + --clear-cache clear the build cache (advanced) + -p, --path string path of the app (default ".") +``` + +**SEE ALSO** + +* [ignite generate](#ignite-generate) - Generate clients, API docs from source code + + +## ignite generate ts-client + +TypeScript frontend client + +**Synopsis** + +Generate a framework agnostic TypeScript client for your blockchain project. + +By default the TypeScript client is generated in the "ts-client/" directory. You +can customize the output directory in config.yml: + + client: + typescript: + path: new-path + +Output can also be customized by using a flag: + + ignite generate ts-client --output new-path + +TypeScript client code can be automatically regenerated on reset or source code +changes when the blockchain is started with a flag: + + ignite chain serve --generate-clients + + +``` +ignite generate ts-client [flags] +``` + +**Options** + +``` + -h, --help help for ts-client + -o, --output string TypeScript client output path + --use-cache use build cache to speed-up generation + -y, --yes answers interactive yes/no questions with yes +``` + +**Options inherited from parent commands** + +``` + --clear-cache clear the build cache (advanced) + -p, --path string path of the app (default ".") +``` + +**SEE ALSO** + +* [ignite generate](#ignite-generate) - Generate clients, API docs from source code + + +## ignite generate vuex + +*DEPRECATED* TypeScript frontend client and Vuex stores + +``` +ignite generate vuex [flags] +``` + +**Options** + +``` + -h, --help help for vuex + -o, --output string Vuex store output path + -y, --yes answers interactive yes/no questions with yes +``` + +**Options inherited from parent commands** + +``` + --clear-cache clear the build cache (advanced) + -p, --path string path of the app (default ".") +``` + +**SEE ALSO** + +* [ignite generate](#ignite-generate) - Generate clients, API docs from source code + + +## ignite network + +Launch a blockchain in production + +**Synopsis** + + +Ignite Network commands allow to coordinate the launch of sovereign Cosmos blockchains. + +To launch a Cosmos blockchain you need someone to be a coordinator and others to +be validators. These are just roles, anyone can be a coordinator or a validator. +A coordinator publishes information about a chain to be launched on the Ignite +blockchain, approves validator requests and coordinates the launch. Validators +send requests to join a chain and start their nodes when a blockchain is ready +for launch. + +To publish the information about your chain as a coordinator run the following +command (the URL should point to a repository with a Cosmos SDK chain): + + ignite network chain publish github.com/ignite/example + +This command will return a launch identifier you will be using in the following +commands. Let's say this identifier is 42. + +Next, ask validators to initialize their nodes and request to join the network +as validators. For a testnet you can use the default values suggested by the +CLI. + + ignite network chain init 42 + + ignite network chain join 42 --amount 95000000stake + +As a coordinator list all validator requests: + + ignite network request list 42 + +Approve validator requests: + + ignite network request approve 42 1,2 + +Once you've approved all validators you need in the validator set, announce that +the chain is ready for launch: + + ignite network chain launch 42 + +Validators can now prepare their nodes for launch: + + ignite network chain prepare 42 + +The output of this command will show a command that a validator would use to +launch their node, for example “exampled --home ~/.example”. After enough +validators launch their nodes, a blockchain will be live. + + +**Options** + +``` + -h, --help help for network + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite](#ignite) - Ignite CLI offers everything you need to scaffold, test, build, and launch your blockchain +* [ignite network chain](#ignite-network-chain) - Publish a chain, join as a validator and prepare node for launch +* [ignite network coordinator](#ignite-network-coordinator) - Show and update a coordinator profile +* [ignite network profile](#ignite-network-profile) - Show the address profile info +* [ignite network project](#ignite-network-project) - Handle projects +* [ignite network request](#ignite-network-request) - Create, show, reject and approve requests +* [ignite network reward](#ignite-network-reward) - Manage network rewards +* [ignite network tool](#ignite-network-tool) - Commands to run subsidiary tools +* [ignite network validator](#ignite-network-validator) - Show and update a validator profile +* [ignite network version](#ignite-network-version) - Version of the plugin + + +## ignite network chain + +Publish a chain, join as a validator and prepare node for launch + +**Synopsis** + +The "chain" namespace features the most commonly used commands for launching +blockchains with Ignite. + +As a coordinator you "publish" your blockchain to Ignite. When enough validators +are approved for the genesis and no changes are excepted to be made to the +genesis, a coordinator announces that the chain is ready for launch with the +"launch" command. In the case of an unsuccessful launch, the coordinator can revert it +using the "revert-launch" command. + +As a validator, you "init" your node and apply to become a validator for a +blockchain with the "join" command. After the launch of the chain is announced, +validators can generate the finalized genesis and download the list of peers with the +"prepare" command. + +The "install" command can be used to download, compile the source code and +install the chain's binary locally. The binary can be used, for example, to +initialize a validator node or to interact with the chain after it has been +launched. + +All chains published to Ignite can be listed by using the "list" command. + + +**Options** + +``` + -h, --help help for chain +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network](#ignite-network) - Launch a blockchain in production +* [ignite network chain init](#ignite-network-chain-init) - Initialize a chain from a published chain ID +* [ignite network chain install](#ignite-network-chain-install) - Install chain binary for a launch +* [ignite network chain join](#ignite-network-chain-join) - Request to join a network as a validator +* [ignite network chain launch](#ignite-network-chain-launch) - Trigger the launch of a chain +* [ignite network chain list](#ignite-network-chain-list) - List published chains +* [ignite network chain prepare](#ignite-network-chain-prepare) - Prepare the chain for launch +* [ignite network chain publish](#ignite-network-chain-publish) - Publish a new chain to start a new network +* [ignite network chain revert-launch](#ignite-network-chain-revert-launch) - Revert launch of a network as a coordinator +* [ignite network chain show](#ignite-network-chain-show) - Show details of a chain + + +## ignite network chain init + +Initialize a chain from a published chain ID + +**Synopsis** + +Ignite network chain init is a command used by validators to initialize a +validator node for a blockchain from the information stored on the Ignite chain. + + ignite network chain init 42 + +This command fetches the information about a chain with launch ID 42. The source +code of the chain is cloned in a temporary directory, and the node's binary is +compiled from the source. The binary is then used to initialize the node. By +default, Ignite uses "~/spn/[launch-id]/" as the home directory for the blockchain. + +An important part of initializing a validator node is creation of the gentx (a +transaction that adds a validator at the genesis of the chain). + +The "init" command will prompt for values like self-delegation and commission. +These values will be used in the validator's gentx. You can use flags to provide +the values in non-interactive mode. + +Use the "--home" flag to choose a different path for the home directory of the +blockchain: + + ignite network chain init 42 --home ~/mychain + +The end result of the "init" command is a validator home directory with a +genesis validator transaction (gentx) file. + +``` +ignite network chain init [launch-id] [flags] +``` + +**Options** + +``` + --check-dependencies verify that cached dependencies have not been modified since they were downloaded + --clear-cache clear the build cache (advanced) + --from string account name to use for sending transactions to SPN (default "default") + -h, --help help for init + --home string home directory used for blockchains + --keyring-backend string keyring backend to store your account keys (default "test") + --keyring-dir string accounts keyring directory (default "/home/runner/.ignite/accounts") + --validator-account string account for the chain validator (default "default") + --validator-details string details about the validator + --validator-gas-price string validator gas price + --validator-identity string validator identity signature (ex. UPort or Keybase) + --validator-moniker string custom validator moniker + --validator-security-contact string validator security contact email + --validator-self-delegation string validator minimum self delegation + --validator-website string associate a website with the validator + -y, --yes answers interactive yes/no questions with yes +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network chain](#ignite-network-chain) - Publish a chain, join as a validator and prepare node for launch + + +## ignite network chain install + +Install chain binary for a launch + +``` +ignite network chain install [launch-id] [flags] +``` + +**Options** + +``` + --check-dependencies verify that cached dependencies have not been modified since they were downloaded + --clear-cache clear the build cache (advanced) + --from string account name to use for sending transactions to SPN (default "default") + -h, --help help for install +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network chain](#ignite-network-chain) - Publish a chain, join as a validator and prepare node for launch + + +## ignite network chain join + +Request to join a network as a validator + +**Synopsis** + +The "join" command is used by validators to send a request to join a blockchain. +The required argument is a launch ID of a blockchain. The "join" command expects +that the validator has already setup a home directory for the blockchain and has +a gentx either by running "ignite network chain init" or initializing the data +directory manually with the chain's binary. + +By default the "join" command just sends the request to join as a validator. +However, often a validator also needs to request an genesis account with a token +balance to afford self-delegation. + +The following command will send a request to join blockchain with launch ID 42 +as a validator and request to be added as an account with a token balance of +95000000 STAKE. + + ignite network chain join 42 --amount 95000000stake + +A request to join as a validator contains a gentx file. Ignite looks for gentx +in a home directory used by "ignite network chain init" by default. To use a +different directory, use the "--home" flag or pass a gentx file directly with +the "--gentx" flag. + +To join a chain as a validator, you must provide the IP address of your node so +that other validators can connect to it. The join command will ask you for the +IP address and will attempt to automatically detect and fill in the value. If +you want to manually specify the IP address, you can use the "--peer-address" +flag: + + ignite network chain join 42 --peer-address 0.0.0.0 + +Since "join" broadcasts a transaction to the Ignite blockchain, you will need an +account on the Ignite blockchain. During the testnet phase, however, Ignite +automatically requests tokens from a faucet. + + +``` +ignite network chain join [launch-id] [flags] +``` + +**Options** + +``` + --amount string amount of coins for account request (ignored if coordinator has fixed the account balances or if --no-acount flag is set) + --check-dependencies verify that cached dependencies have not been modified since they were downloaded + --from string account name to use for sending transactions to SPN (default "default") + --gentx string path to a gentx json file + -h, --help help for join + --home string home directory used for blockchains + --keyring-backend string keyring backend to store your account keys (default "test") + --keyring-dir string accounts keyring directory (default "/home/runner/.ignite/accounts") + --no-account prevent sending a request for a genesis account + --peer-address string peer's address + -y, --yes answers interactive yes/no questions with yes +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network chain](#ignite-network-chain) - Publish a chain, join as a validator and prepare node for launch + + +## ignite network chain launch + +Trigger the launch of a chain + +**Synopsis** + +The launch command communicates to the world that the chain is ready to be +launched. + +Only the coordinator of the chain can execute the launch command. + + ignite network chain launch 42 + +After the launch command is executed no changes to the genesis are accepted. For +example, validators will no longer be able to successfully execute the "ignite +network chain join" command to apply as a validator. + +The launch command sets the date and time after which the chain will start. By +default, the current time is set. To give validators more time to prepare for +the launch, set the time with the "--launch-time" flag: + + ignite network chain launch 42 --launch-time 2023-01-01T00:00:00Z + +After the launch command is executed, validators can generate the finalized +genesis and prepare their nodes for the launch. For example, validators can run +"ignite network chain prepare" to generate the genesis and populate the peer +list. + +If you want to change the launch time or open up the genesis file for changes +you can use "ignite network chain revert-launch" to make it possible, for +example, to accept new validators and add accounts. + + +``` +ignite network chain launch [launch-id] [flags] +``` + +**Options** + +``` + --from string account name to use for sending transactions to SPN (default "default") + -h, --help help for launch + --keyring-backend string keyring backend to store your account keys (default "test") + --keyring-dir string accounts keyring directory (default "/home/runner/.ignite/accounts") + --launch-time string timestamp the chain is effectively launched (example "2022-01-01T00:00:00Z") +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network chain](#ignite-network-chain) - Publish a chain, join as a validator and prepare node for launch + + +## ignite network chain list + +List published chains + +``` +ignite network chain list [flags] +``` + +**Options** + +``` + --advanced show advanced information about the chains + -h, --help help for list + --limit uint limit of results per page (default 100) + --page uint page for chain list result (default 1) +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network chain](#ignite-network-chain) - Publish a chain, join as a validator and prepare node for launch + + +## ignite network chain prepare + +Prepare the chain for launch + +**Synopsis** + +The prepare command prepares a validator node for the chain launch by generating +the final genesis and adding IP addresses of peers to the validator's +configuration file. + + ignite network chain prepare 42 + +By default, Ignite uses "$HOME/spn/LAUNCH_ID" as the data directory. If you used +a different data directory when initializing the node, use the "--home" flag and +set the correct path to the data directory. + +Ignite generates the genesis file in "config/genesis.json" and adds peer IPs by +modifying "config/config.toml". + +The prepare command should be executed after the coordinator has triggered the +chain launch and finalized the genesis with "ignite network chain launch". You +can force Ignite to run the prepare command without checking if the launch has +been triggered with the "--force" flag (this is not recommended). + +After the prepare command is executed the node is ready to be started. + + +``` +ignite network chain prepare [launch-id] [flags] +``` + +**Options** + +``` + --check-dependencies verify that cached dependencies have not been modified since they were downloaded + --clear-cache clear the build cache (advanced) + -f, --force force the prepare command to run even if the chain is not launched + --from string account name to use for sending transactions to SPN (default "default") + -h, --help help for prepare + --home string home directory used for blockchains + --keyring-backend string keyring backend to store your account keys (default "test") + --keyring-dir string accounts keyring directory (default "/home/runner/.ignite/accounts") +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network chain](#ignite-network-chain) - Publish a chain, join as a validator and prepare node for launch + + +## ignite network chain publish + +Publish a new chain to start a new network + +**Synopsis** + +To begin the process of launching a blockchain with Ignite, a coordinator needs +to publish the information about a blockchain. The only required bit of +information is the URL of the source code of the blockchain. + +The following command publishes the information about an example blockchain: + + ignite network chain publish github.com/ignite/example + +This command fetches the source code of the blockchain, compiles the binary, +verifies that a blockchain can be started with the binary, and publishes the +information about the blockchain to Ignite. Currently, only public repositories +are supported. The command returns an integer number that acts as an identifier +of the chain on Ignite. + +By publishing a blockchain on Ignite you become the "coordinator" of this +blockchain. A coordinator is an account that has the authority to approve and +reject validator requests, set parameters of the blockchain and trigger the +launch of the chain. + +The default Git branch is used when publishing a chain. If you want to use a +specific branch, tag or a commit hash, use "--branch", "--tag", or "--hash" +flags respectively. + +The repository name is used as the default chain ID. Ignite does not ensure that +chain IDs are unique, but they have to have a valid format: [string]-[integer]. +To set a custom chain ID use the "--chain-id" flag. + + ignite network chain publish github.com/ignite/example --chain-id foo-1 + +Once the chain is published users can request accounts with coin balances to be +added to the chain's genesis. By default, users are free to request any number +of tokens. If you want all users requesting tokens to get the same amount, use +the "--account-balance" flag with a list of coins. + + ignite network chain publish github.com/ignite/example --account-balance 2000foocoin + + +``` +ignite network chain publish [source-url] [flags] +``` + +**Options** + +``` + --account-balance string balance for each approved genesis account for the chain + --amount string amount of coins for account request + --branch string Git branch to use for the repo + --chain-id string chain ID to use for this network + --check-dependencies verify that cached dependencies have not been modified since they were downloaded + --clear-cache clear the build cache (advanced) + --from string account name to use for sending transactions to SPN (default "default") + --genesis-config string name of an Ignite config file in the repo for custom Genesis + --genesis-url string URL to a custom Genesis + --hash string Git hash to use for the repo + -h, --help help for publish + --home string home directory used for blockchains + --keyring-backend string keyring backend to store your account keys (default "test") + --keyring-dir string accounts keyring directory (default "/home/runner/.ignite/accounts") + --mainnet initialize a mainnet project + --metadata string add chain metadata + --no-check skip verifying chain's integrity + --project uint project ID to use for this network + --reward.coins string reward coins + --reward.height int last reward height + --shares string add shares for the project + --tag string Git tag to use for the repo + --total-supply string add a total of the mainnet of a project + -y, --yes answers interactive yes/no questions with yes +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network chain](#ignite-network-chain) - Publish a chain, join as a validator and prepare node for launch + + +## ignite network chain revert-launch + +Revert launch of a network as a coordinator + +**Synopsis** + +The revert launch command reverts the previously scheduled launch of a chain. + +Only the coordinator of the chain can execute the launch command. + + ignite network chain revert-launch 42 + +After the revert launch command is executed, changes to the genesis of the chain +are allowed again. For example, validators will be able to request to join the +chain. Revert launch also resets the launch time. + + +``` +ignite network chain revert-launch [launch-id] [flags] +``` + +**Options** + +``` + --from string account name to use for sending transactions to SPN (default "default") + -h, --help help for revert-launch + --keyring-backend string keyring backend to store your account keys (default "test") + --keyring-dir string accounts keyring directory (default "/home/runner/.ignite/accounts") +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network chain](#ignite-network-chain) - Publish a chain, join as a validator and prepare node for launch + + +## ignite network chain show + +Show details of a chain + +**Options** + +``` + -h, --help help for show +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network chain](#ignite-network-chain) - Publish a chain, join as a validator and prepare node for launch +* [ignite network chain show accounts](#ignite-network-chain-show-accounts) - Show all vesting and genesis accounts of the chain +* [ignite network chain show genesis](#ignite-network-chain-show-genesis) - Show the chain genesis file +* [ignite network chain show info](#ignite-network-chain-show-info) - Show info details of the chain +* [ignite network chain show peers](#ignite-network-chain-show-peers) - Show peers list of the chain +* [ignite network chain show validators](#ignite-network-chain-show-validators) - Show all validators of the chain + + +## ignite network chain show accounts + +Show all vesting and genesis accounts of the chain + +``` +ignite network chain show accounts [launch-id] [flags] +``` + +**Options** + +``` + -h, --help help for accounts + --prefix string account address prefix (default "spn") +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network chain show](#ignite-network-chain-show) - Show details of a chain + + +## ignite network chain show genesis + +Show the chain genesis file + +``` +ignite network chain show genesis [launch-id] [flags] +``` + +**Options** + +``` + --clear-cache clear the build cache (advanced) + -h, --help help for genesis + --out string path to output Genesis file (default "./genesis.json") +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network chain show](#ignite-network-chain-show) - Show details of a chain + + +## ignite network chain show info + +Show info details of the chain + +``` +ignite network chain show info [launch-id] [flags] +``` + +**Options** + +``` + -h, --help help for info +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network chain show](#ignite-network-chain-show) - Show details of a chain + + +## ignite network chain show peers + +Show peers list of the chain + +``` +ignite network chain show peers [launch-id] [flags] +``` + +**Options** + +``` + -h, --help help for peers + --out string path to output peers list (default "./peers.txt") +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network chain show](#ignite-network-chain-show) - Show details of a chain + + +## ignite network chain show validators + +Show all validators of the chain + +``` +ignite network chain show validators [launch-id] [flags] +``` + +**Options** + +``` + -h, --help help for validators + --prefix string account address prefix (default "spn") +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network chain show](#ignite-network-chain-show) - Show details of a chain + + +## ignite network coordinator + +Show and update a coordinator profile + +**Options** + +``` + -h, --help help for coordinator +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network](#ignite-network) - Launch a blockchain in production +* [ignite network coordinator set](#ignite-network-coordinator-set) - Set an information in a coordinator profile +* [ignite network coordinator show](#ignite-network-coordinator-show) - Show a coordinator profile + + +## ignite network coordinator set + +Set an information in a coordinator profile + +**Synopsis** + +Coordinators on Ignite can set a profile containing a description for the coordinator. +The coordinator set command allows to set information for the coordinator. +The following information can be set: +- details: general information about the coordinator. +- identity: a piece of information to verify the identity of the coordinator with a system like Keybase or Veramo. +- website: website of the coordinator. + + +``` +ignite network coordinator set details|identity|website [value] [flags] +``` + +**Options** + +``` + --from string account name to use for sending transactions to SPN (default "default") + -h, --help help for set + --home string home directory used for blockchains + --keyring-backend string keyring backend to store your account keys (default "test") + --keyring-dir string accounts keyring directory (default "/home/runner/.ignite/accounts") +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network coordinator](#ignite-network-coordinator) - Show and update a coordinator profile + + +## ignite network coordinator show + +Show a coordinator profile + +``` +ignite network coordinator show [address] [flags] +``` + +**Options** + +``` + -h, --help help for show +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network coordinator](#ignite-network-coordinator) - Show and update a coordinator profile + + +## ignite network profile + +Show the address profile info + +``` +ignite network profile [project-id] [flags] +``` + +**Options** + +``` + --from string account name to use for sending transactions to SPN (default "default") + -h, --help help for profile + --home string home directory used for blockchains + --keyring-backend string keyring backend to store your account keys (default "test") +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network](#ignite-network) - Launch a blockchain in production + + +## ignite network project + +Handle projects + +**Options** + +``` + -h, --help help for project +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network](#ignite-network) - Launch a blockchain in production +* [ignite network project account](#ignite-network-project-account) - Handle project accounts +* [ignite network project create](#ignite-network-project-create) - Create a project +* [ignite network project list](#ignite-network-project-list) - List published projects +* [ignite network project show](#ignite-network-project-show) - Show published project +* [ignite network project update](#ignite-network-project-update) - Update details fo the project of the project + + +## ignite network project account + +Handle project accounts + +**Options** + +``` + -h, --help help for account +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network project](#ignite-network-project) - Handle projects +* [ignite network project account list](#ignite-network-project-account-list) - Show all mainnet and mainnet vesting of the project + + +## ignite network project account list + +Show all mainnet and mainnet vesting of the project + +``` +ignite network project account list [project-id] [flags] +``` + +**Options** + +``` + -h, --help help for list +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network project account](#ignite-network-project-account) - Handle project accounts + + +## ignite network project create + +Create a project + +``` +ignite network project create [name] [total-supply] [flags] +``` + +**Options** + +``` + --from string account name to use for sending transactions to SPN (default "default") + -h, --help help for create + --home string home directory used for blockchains + --keyring-backend string keyring backend to store your account keys (default "test") + --keyring-dir string accounts keyring directory (default "/home/runner/.ignite/accounts") + --metadata string Add a metadata to the chain +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network project](#ignite-network-project) - Handle projects + + +## ignite network project list + +List published projects + +``` +ignite network project list [flags] +``` + +**Options** + +``` + -h, --help help for list +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network project](#ignite-network-project) - Handle projects + + +## ignite network project show + +Show published project + +``` +ignite network project show [project-id] [flags] +``` + +**Options** + +``` + -h, --help help for show +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network project](#ignite-network-project) - Handle projects + + +## ignite network project update + +Update details fo the project of the project + +``` +ignite network project update [project-id] [flags] +``` + +**Options** + +``` + --from string account name to use for sending transactions to SPN (default "default") + -h, --help help for update + --keyring-backend string keyring backend to store your account keys (default "test") + --keyring-dir string accounts keyring directory (default "/home/runner/.ignite/accounts") + --metadata string update the project metadata + --name string update the project name + --total-supply string update the total of the mainnet of a project +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network project](#ignite-network-project) - Handle projects + + +## ignite network request + +Create, show, reject and approve requests + +**Synopsis** + +The "request" namespace contains commands for creating, showing, approving, and +rejecting requests. + +A request is mechanism in Ignite that allows changes to be made to the genesis +file like adding accounts with token balances and validators. Anyone can submit +a request, but only the coordinator of a chain can approve or reject a request. + +Each request has a status: + +* Pending: waiting for the approval of the coordinator +* Approved: approved by the coordinator, its content has been applied to the + launch information +* Rejected: rejected by the coordinator or the request creator + + +**Options** + +``` + -h, --help help for request +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network](#ignite-network) - Launch a blockchain in production +* [ignite network request add-account](#ignite-network-request-add-account) - Send request to add account +* [ignite network request approve](#ignite-network-request-approve) - Approve requests +* [ignite network request change-param](#ignite-network-request-change-param) - Send request to change a module param +* [ignite network request list](#ignite-network-request-list) - List all requests for a chain +* [ignite network request reject](#ignite-network-request-reject) - Reject requests +* [ignite network request remove-account](#ignite-network-request-remove-account) - Send request to remove a genesis account +* [ignite network request remove-validator](#ignite-network-request-remove-validator) - Send request to remove a validator +* [ignite network request show](#ignite-network-request-show) - Show detailed information about a request +* [ignite network request verify](#ignite-network-request-verify) - Verify the request and simulate the chain genesis from them + + +## ignite network request add-account + +Send request to add account + +**Synopsis** + +The "add account" command creates a new request to add an account with a given +address and a specified coin balance to the genesis of the chain. + +The request automatically fails to be applied if a genesis account or a vesting +account with an identical address is already specified in the launch +information. + +If a coordinator has specified that all genesis accounts on a chain should have +the same balance (useful for testnets, for example), the "add account" expects +only an address as an argument. Attempt to provide a token balance will result +in an error. + + +``` +ignite network request add-account [launch-id] [address] [coins] [flags] +``` + +**Options** + +``` + --clear-cache clear the build cache (advanced) + --from string account name to use for sending transactions to SPN (default "default") + -h, --help help for add-account + --home string home directory used for blockchains + --keyring-backend string keyring backend to store your account keys (default "test") + --keyring-dir string accounts keyring directory (default "/home/runner/.ignite/accounts") +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network request](#ignite-network-request) - Create, show, reject and approve requests + + +## ignite network request approve + +Approve requests + +**Synopsis** + +The "approve" command is used by a chain's coordinator to approve requests. +Multiple requests can be approved using a comma-separated list and/or using a +dash syntax. + + ignite network request approve 42 1,2,3-6,7,8 + +The command above approves requests with IDs from 1 to 8 included on a chain +with a launch ID 42. + +When requests are approved Ignite applies the requested changes and simulates +initializing and launching the chain locally. If the chain starts successfully, +requests are considered to be "verified" and are approved. If one or more +requested changes stop the chain from launching locally, the verification +process fails and the approval of all requests is canceled. To skip the +verification process use the "--no-verification" flag. + +Note that Ignite will try to approve requests in the same order as request IDs +are submitted to the "approve" command. + +``` +ignite network request approve [launch-id] [number<,...>] [flags] +``` + +**Options** + +``` + --clear-cache clear the build cache (advanced) + --from string account name to use for sending transactions to SPN (default "default") + -h, --help help for approve + --home string home directory used for blockchains + --keyring-backend string keyring backend to store your account keys (default "test") + --keyring-dir string accounts keyring directory (default "/home/runner/.ignite/accounts") + --no-verification approve the requests without verifying them +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network request](#ignite-network-request) - Create, show, reject and approve requests + + +## ignite network request change-param + +Send request to change a module param + +``` +ignite network request change-param [launch-id] [module-name] [param-name] [value (json, string, number)] [flags] +``` + +**Options** + +``` + --clear-cache clear the build cache (advanced) + --from string account name to use for sending transactions to SPN (default "default") + -h, --help help for change-param + --home string home directory used for blockchains + --keyring-backend string keyring backend to store your account keys (default "test") + --keyring-dir string accounts keyring directory (default "/home/runner/.ignite/accounts") +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network request](#ignite-network-request) - Create, show, reject and approve requests + + +## ignite network request list + +List all requests for a chain + +``` +ignite network request list [launch-id] [flags] +``` + +**Options** + +``` + -h, --help help for list + --prefix string account address prefix (default "spn") +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network request](#ignite-network-request) - Create, show, reject and approve requests + + +## ignite network request reject + +Reject requests + +**Synopsis** + +The "reject" command is used by a chain's coordinator to reject requests. + + ignite network request reject 42 1,2,3-6,7,8 + +The syntax of the "reject" command is similar to that of the "approve" command. + + +``` +ignite network request reject [launch-id] [number<,...>] [flags] +``` + +**Options** + +``` + --from string account name to use for sending transactions to SPN (default "default") + -h, --help help for reject + --home string home directory used for blockchains + --keyring-backend string keyring backend to store your account keys (default "test") + --keyring-dir string accounts keyring directory (default "/home/runner/.ignite/accounts") +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network request](#ignite-network-request) - Create, show, reject and approve requests + + +## ignite network request remove-account + +Send request to remove a genesis account + +``` +ignite network request remove-account [launch-id] [address] [flags] +``` + +**Options** + +``` + --clear-cache clear the build cache (advanced) + --from string account name to use for sending transactions to SPN (default "default") + -h, --help help for remove-account + --home string home directory used for blockchains + --keyring-backend string keyring backend to store your account keys (default "test") + --keyring-dir string accounts keyring directory (default "/home/runner/.ignite/accounts") +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network request](#ignite-network-request) - Create, show, reject and approve requests + + +## ignite network request remove-validator + +Send request to remove a validator + +``` +ignite network request remove-validator [launch-id] [address] [flags] +``` + +**Options** + +``` + --clear-cache clear the build cache (advanced) + --from string account name to use for sending transactions to SPN (default "default") + -h, --help help for remove-validator + --home string home directory used for blockchains + --keyring-backend string keyring backend to store your account keys (default "test") + --keyring-dir string accounts keyring directory (default "/home/runner/.ignite/accounts") +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network request](#ignite-network-request) - Create, show, reject and approve requests + + +## ignite network request show + +Show detailed information about a request + +``` +ignite network request show [launch-id] [request-id] [flags] +``` + +**Options** + +``` + -h, --help help for show +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network request](#ignite-network-request) - Create, show, reject and approve requests + + +## ignite network request verify + +Verify the request and simulate the chain genesis from them + +**Synopsis** + +The "verify" command applies selected requests to the genesis of a chain locally +to verify that approving these requests will result in a valid genesis that +allows a chain to launch without issues. This command does not approve requests, +only checks them. + + +``` +ignite network request verify [launch-id] [number<,...>] [flags] +``` + +**Options** + +``` + --clear-cache clear the build cache (advanced) + --from string account name to use for sending transactions to SPN (default "default") + -h, --help help for verify + --home string home directory used for blockchains + --keyring-backend string keyring backend to store your account keys (default "test") + --keyring-dir string accounts keyring directory (default "/home/runner/.ignite/accounts") +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network request](#ignite-network-request) - Create, show, reject and approve requests + + +## ignite network reward + +Manage network rewards + +**Options** + +``` + -h, --help help for reward +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network](#ignite-network) - Launch a blockchain in production +* [ignite network reward release](#ignite-network-reward-release) - Connect the monitoring modules of launched chains with SPN +* [ignite network reward set](#ignite-network-reward-set) - set a network chain reward + + +## ignite network reward release + +Connect the monitoring modules of launched chains with SPN + +``` +ignite network reward release [launch-id] [chain-rpc] [flags] +``` + +**Options** + +``` + --create-client-only only create the network client id + --from string account name to use for sending transactions to SPN (default "default") + -h, --help help for release + --keyring-backend string keyring backend to store your account keys (default "test") + --spn-gaslimit int gas limit used for transactions on SPN (default 400000) + --spn-gasprice string gas price used for transactions on SPN (default "0.0000025uspn") + --testnet-account string testnet chain account (default "default") + --testnet-faucet string faucet address of the testnet chain + --testnet-gaslimit int gas limit used for transactions on testnet chain (default 400000) + --testnet-gasprice string gas price used for transactions on testnet chain (default "0.0000025stake") + --testnet-prefix string address prefix of the testnet chain (default "cosmos") +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network reward](#ignite-network-reward) - Manage network rewards + + +## ignite network reward set + +set a network chain reward + +``` +ignite network reward set [launch-id] [last-reward-height] [coins] [flags] +``` + +**Options** + +``` + --from string account name to use for sending transactions to SPN (default "default") + -h, --help help for set + --home string home directory used for blockchains + --keyring-backend string keyring backend to store your account keys (default "test") + --keyring-dir string accounts keyring directory (default "/home/runner/.ignite/accounts") +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network reward](#ignite-network-reward) - Manage network rewards + + +## ignite network tool + +Commands to run subsidiary tools + +**Options** + +``` + -h, --help help for tool +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network](#ignite-network) - Launch a blockchain in production +* [ignite network tool proxy-tunnel](#ignite-network-tool-proxy-tunnel) - Setup a proxy tunnel via HTTP + + +## ignite network tool proxy-tunnel + +Setup a proxy tunnel via HTTP + +**Synopsis** + +Starts an HTTP proxy server and HTTP proxy clients for each node that +needs HTTP tunneling. + +HTTP tunneling is activated **ONLY** if SPN_CONFIG_FILE has "tunneled_peers" +field inside with a list of tunneled peers/nodes. + +If you're using SPN as coordinator and do not want to allow HTTP tunneling +feature at all, you can prevent "spn.yml" file to being generated by not +approving validator requests that has HTTP tunneling enabled instead of plain +TCP connections. + +``` +ignite network tool proxy-tunnel SPN_CONFIG_FILE [flags] +``` + +**Options** + +``` + -h, --help help for proxy-tunnel +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network tool](#ignite-network-tool) - Commands to run subsidiary tools + + +## ignite network validator + +Show and update a validator profile + +**Options** + +``` + -h, --help help for validator +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network](#ignite-network) - Launch a blockchain in production +* [ignite network validator set](#ignite-network-validator-set) - Set an information in a validator profile +* [ignite network validator show](#ignite-network-validator-show) - Show a validator profile + + +## ignite network validator set + +Set an information in a validator profile + +**Synopsis** + +Validators on Ignite can set a profile containing a description for the validator. +The validator set command allows to set information for the validator. +The following information can be set: +- details: general information about the validator. +- identity: piece of information to verify identity of the validator with a system like Keybase of Veramo. +- website: website of the validator. +- security: security contact for the validator. + + +``` +ignite network validator set details|identity|website|security [value] [flags] +``` + +**Options** + +``` + --from string account name to use for sending transactions to SPN (default "default") + -h, --help help for set + --home string home directory used for blockchains + --keyring-backend string keyring backend to store your account keys (default "test") + --keyring-dir string accounts keyring directory (default "/home/runner/.ignite/accounts") +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network validator](#ignite-network-validator) - Show and update a validator profile + + +## ignite network validator show + +Show a validator profile + +``` +ignite network validator show [address] [flags] +``` + +**Options** + +``` + -h, --help help for show +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network validator](#ignite-network-validator) - Show and update a validator profile + + +## ignite network version + +Version of the plugin + +**Synopsis** + +The version of the plugin to use to interact with a chain might be specified by the coordinator. + + +``` +ignite network version [flags] +``` + +**Options** + +``` + -h, --help help for version +``` + +**Options inherited from parent commands** + +``` + --local Use local SPN network + --nightly Use nightly SPN network + --spn-faucet-address string SPN faucet address (default "https://faucet.devnet.ignite.com:443") + --spn-node-address string SPN node address (default "https://rpc.devnet.ignite.com:443") +``` + +**SEE ALSO** + +* [ignite network](#ignite-network) - Launch a blockchain in production + + +## ignite node + +Make requests to a live blockchain node + +**Options** + +``` + -h, --help help for node + --node string : to tendermint rpc interface for this chain (default "https://rpc.cosmos.network:443") +``` + +**SEE ALSO** + +* [ignite](#ignite) - Ignite CLI offers everything you need to scaffold, test, build, and launch your blockchain +* [ignite node query](#ignite-node-query) - Querying subcommands +* [ignite node tx](#ignite-node-tx) - Transactions subcommands + + +## ignite node query + +Querying subcommands + +**Options** + +``` + -h, --help help for query +``` + +**Options inherited from parent commands** + +``` + --node string : to tendermint rpc interface for this chain (default "https://rpc.cosmos.network:443") +``` + +**SEE ALSO** + +* [ignite node](#ignite-node) - Make requests to a live blockchain node +* [ignite node query bank](#ignite-node-query-bank) - Querying commands for the bank module +* [ignite node query tx](#ignite-node-query-tx) - Query for transaction by hash + + +## ignite node query bank + +Querying commands for the bank module + +**Options** + +``` + -h, --help help for bank +``` + +**Options inherited from parent commands** + +``` + --node string : to tendermint rpc interface for this chain (default "https://rpc.cosmos.network:443") +``` + +**SEE ALSO** + +* [ignite node query](#ignite-node-query) - Querying subcommands +* [ignite node query bank balances](#ignite-node-query-bank-balances) - Query for account balances by account name or address + + +## ignite node query bank balances + +Query for account balances by account name or address + +``` +ignite node query bank balances [from_account_or_address] [flags] +``` + +**Options** + +``` + --address-prefix string account address prefix (default "cosmos") + --count-total count total number of records in all balances to query for + -h, --help help for balances + --home string directory where the blockchain node is initialized + --keyring-backend string keyring backend to store your account keys (default "test") + --keyring-dir string accounts keyring directory (default "/home/runner/.ignite/accounts") + --limit uint pagination limit of all balances to query for (default 100) + --offset uint pagination offset of all balances to query for + --page uint pagination page of all balances to query for. This sets offset to a multiple of limit (default 1) + --page-key string pagination page-key of all balances to query for + --reverse results are sorted in descending order +``` + +**Options inherited from parent commands** + +``` + --node string : to tendermint rpc interface for this chain (default "https://rpc.cosmos.network:443") +``` + +**SEE ALSO** + +* [ignite node query bank](#ignite-node-query-bank) - Querying commands for the bank module + + +## ignite node query tx + +Query for transaction by hash + +``` +ignite node query tx [hash] [flags] +``` + +**Options** + +``` + -h, --help help for tx +``` + +**Options inherited from parent commands** + +``` + --node string : to tendermint rpc interface for this chain (default "https://rpc.cosmos.network:443") +``` + +**SEE ALSO** + +* [ignite node query](#ignite-node-query) - Querying subcommands + + +## ignite node tx + +Transactions subcommands + +**Options** + +``` + --address-prefix string account address prefix (default "cosmos") + --fees string fees to pay along with transaction; eg: 10uatom + --gas string gas limit to set per-transaction; set to "auto" to calculate sufficient gas automatically (default "auto") + --gas-prices string gas prices in decimal format to determine the transaction fee (e.g. 0.1uatom) + --generate-only build an unsigned transaction and write it to STDOUT + -h, --help help for tx + --home string directory where the blockchain node is initialized + --keyring-backend string keyring backend to store your account keys (default "test") + --keyring-dir string accounts keyring directory (default "/home/runner/.ignite/accounts") +``` + +**Options inherited from parent commands** + +``` + --node string : to tendermint rpc interface for this chain (default "https://rpc.cosmos.network:443") +``` + +**SEE ALSO** + +* [ignite node](#ignite-node) - Make requests to a live blockchain node +* [ignite node tx bank](#ignite-node-tx-bank) - Bank transaction subcommands + + +## ignite node tx bank + +Bank transaction subcommands + +**Options** + +``` + -h, --help help for bank +``` + +**Options inherited from parent commands** + +``` + --address-prefix string account address prefix (default "cosmos") + --fees string fees to pay along with transaction; eg: 10uatom + --gas string gas limit to set per-transaction; set to "auto" to calculate sufficient gas automatically (default "auto") + --gas-prices string gas prices in decimal format to determine the transaction fee (e.g. 0.1uatom) + --generate-only build an unsigned transaction and write it to STDOUT + --home string directory where the blockchain node is initialized + --keyring-backend string keyring backend to store your account keys (default "test") + --keyring-dir string accounts keyring directory (default "/home/runner/.ignite/accounts") + --node string : to tendermint rpc interface for this chain (default "https://rpc.cosmos.network:443") +``` + +**SEE ALSO** + +* [ignite node tx](#ignite-node-tx) - Transactions subcommands +* [ignite node tx bank send](#ignite-node-tx-bank-send) - Send funds from one account to another. + + +## ignite node tx bank send + +Send funds from one account to another. + +``` +ignite node tx bank send [from_account_or_address] [to_account_or_address] [amount] [flags] +``` + +**Options** + +``` + -h, --help help for send +``` + +**Options inherited from parent commands** + +``` + --address-prefix string account address prefix (default "cosmos") + --fees string fees to pay along with transaction; eg: 10uatom + --gas string gas limit to set per-transaction; set to "auto" to calculate sufficient gas automatically (default "auto") + --gas-prices string gas prices in decimal format to determine the transaction fee (e.g. 0.1uatom) + --generate-only build an unsigned transaction and write it to STDOUT + --home string directory where the blockchain node is initialized + --keyring-backend string keyring backend to store your account keys (default "test") + --keyring-dir string accounts keyring directory (default "/home/runner/.ignite/accounts") + --node string : to tendermint rpc interface for this chain (default "https://rpc.cosmos.network:443") +``` + +**SEE ALSO** + +* [ignite node tx bank](#ignite-node-tx-bank) - Bank transaction subcommands + + +## ignite plugin + +Handle plugins + +**Options** + +``` + -h, --help help for plugin +``` + +**SEE ALSO** + +* [ignite](#ignite) - Ignite CLI offers everything you need to scaffold, test, build, and launch your blockchain +* [ignite plugin add](#ignite-plugin-add) - Adds a plugin declaration to a plugin configuration +* [ignite plugin describe](#ignite-plugin-describe) - Output information about the a registered plugin +* [ignite plugin list](#ignite-plugin-list) - List declared plugins and status +* [ignite plugin remove](#ignite-plugin-remove) - Removes a plugin declaration from a chain's plugin configuration +* [ignite plugin scaffold](#ignite-plugin-scaffold) - Scaffold a new plugin +* [ignite plugin update](#ignite-plugin-update) - Update plugins + + +## ignite plugin add + +Adds a plugin declaration to a plugin configuration + +**Synopsis** + +Adds a plugin declaration to a plugin configuration. +Respects key value pairs declared after the plugin path to be added to the +generated configuration definition. +Example: + ignite plugin add github.com/org/my-plugin/ foo=bar baz=qux + +``` +ignite plugin add [path] [key=value]... [flags] +``` + +**Options** + +``` + -g, --global use global plugins configuration ($HOME/.ignite/plugins/plugins.yml) + -h, --help help for add +``` + +**SEE ALSO** + +* [ignite plugin](#ignite-plugin) - Handle plugins + + +## ignite plugin describe + +Output information about the a registered plugin + +**Synopsis** + +Output information about a registered plugins commands and hooks. + +``` +ignite plugin describe [path] [flags] +``` + +**Options** + +``` + -h, --help help for describe +``` + +**SEE ALSO** + +* [ignite plugin](#ignite-plugin) - Handle plugins + + +## ignite plugin list + +List declared plugins and status + +**Synopsis** + +Prints status and information of declared plugins + +``` +ignite plugin list [flags] +``` + +**Options** + +``` + -h, --help help for list +``` + +**SEE ALSO** + +* [ignite plugin](#ignite-plugin) - Handle plugins + + +## ignite plugin remove + +Removes a plugin declaration from a chain's plugin configuration + +``` +ignite plugin remove [path] [flags] +``` + +**Options** + +``` + -g, --global use global plugins configuration ($HOME/.ignite/plugins/plugins.yml) + -h, --help help for remove +``` + +**SEE ALSO** + +* [ignite plugin](#ignite-plugin) - Handle plugins + + +## ignite plugin scaffold + +Scaffold a new plugin + +**Synopsis** + +Scaffolds a new plugin in the current directory with the given repository path configured. A git repository will be created with the given module name, unless the current directory is already a git repository. + +``` +ignite plugin scaffold [github.com/org/repo] [flags] +``` + +**Options** + +``` + -h, --help help for scaffold +``` + +**SEE ALSO** + +* [ignite plugin](#ignite-plugin) - Handle plugins + + +## ignite plugin update + +Update plugins + +**Synopsis** + +Updates a plugin specified by path. If no path is specified all declared plugins are updated + +``` +ignite plugin update [path] [flags] +``` + +**Options** + +``` + -h, --help help for update +``` + +**SEE ALSO** + +* [ignite plugin](#ignite-plugin) - Handle plugins + + +## ignite relayer + +Connect blockchains with an IBC relayer + +**Options** + +``` + -h, --help help for relayer +``` + +**SEE ALSO** + +* [ignite](#ignite) - Ignite CLI offers everything you need to scaffold, test, build, and launch your blockchain +* [ignite relayer configure](#ignite-relayer-configure) - Configure source and target chains for relaying +* [ignite relayer connect](#ignite-relayer-connect) - Link chains associated with paths and start relaying tx packets in between + + +## ignite relayer configure + +Configure source and target chains for relaying + +``` +ignite relayer configure [flags] +``` + +**Options** + +``` + -a, --advanced advanced configuration options for custom IBC modules + -h, --help help for configure + --keyring-backend string keyring backend to store your account keys (default "test") + --keyring-dir string accounts keyring directory (default "/home/runner/.ignite/accounts") + --ordered set the channel as ordered + -r, --reset reset the relayer config + --source-account string source Account + --source-client-id string use a custom client id for source + --source-faucet string faucet address of the source chain + --source-gaslimit int gas limit used for transactions on source chain + --source-gasprice string gas price used for transactions on source chain + --source-port string IBC port ID on the source chain + --source-prefix string address prefix of the source chain + --source-rpc string RPC address of the source chain + --source-version string module version on the source chain + --target-account string target Account + --target-client-id string use a custom client id for target + --target-faucet string faucet address of the target chain + --target-gaslimit int gas limit used for transactions on target chain + --target-gasprice string gas price used for transactions on target chain + --target-port string IBC port ID on the target chain + --target-prefix string address prefix of the target chain + --target-rpc string RPC address of the target chain + --target-version string module version on the target chain +``` + +**SEE ALSO** + +* [ignite relayer](#ignite-relayer) - Connect blockchains with an IBC relayer + + +## ignite relayer connect + +Link chains associated with paths and start relaying tx packets in between + +``` +ignite relayer connect [,...] [flags] +``` + +**Options** + +``` + -h, --help help for connect + --keyring-backend string keyring backend to store your account keys (default "test") + --keyring-dir string accounts keyring directory (default "/home/runner/.ignite/accounts") +``` + +**SEE ALSO** + +* [ignite relayer](#ignite-relayer) - Connect blockchains with an IBC relayer + + +## ignite scaffold + +Create a new blockchain, module, message, query, and more + +**Synopsis** + +Scaffolding is a quick way to generate code for major pieces of your +application. + +For details on each scaffolding target (chain, module, message, etc.) run the +corresponding command with a "--help" flag, for example, "ignite scaffold chain +--help". + +The Ignite team strongly recommends committing the code to a version control +system before running scaffolding commands. This will make it easier to see the +changes to the source code as well as undo the command if you've decided to roll +back the changes. + +This blockchain you create with the chain scaffolding command uses the modular +Cosmos SDK framework and imports many standard modules for functionality like +proof of stake, token transfer, inter-blockchain connectivity, governance, and +more. Custom functionality is implemented in modules located by convention in +the "x/" directory. By default, your blockchain comes with an empty custom +module. Use the module scaffolding command to create an additional module. + +An empty custom module doesn't do much, it's basically a container for logic +that is responsible for processing transactions and changing the application +state. Cosmos SDK blockchains work by processing user-submitted signed +transactions, which contain one or more messages. A message contains data that +describes a state transition. A module can be responsible for handling any +number of messages. + +A message scaffolding command will generate the code for handling a new type of +Cosmos SDK message. Message fields describe the state transition that the +message is intended to produce if processed without errors. + +Scaffolding messages is useful to create individual "actions" that your module +can perform. Sometimes, however, you want your blockchain to have the +functionality to create, read, update and delete (CRUD) instances of a +particular type. Depending on how you want to store the data there are three +commands that scaffold CRUD functionality for a type: list, map, and single. +These commands create four messages (one for each CRUD action), and the logic to +add, delete, and fetch the data from the store. If you want to scaffold only the +logic, for example, you've decided to scaffold messages separately, you can do +that as well with the "--no-message" flag. + +Reading data from a blockchain happens with a help of queries. Similar to how +you can scaffold messages to write data, you can scaffold queries to read the +data back from your blockchain application. + +You can also scaffold a type, which just produces a new protocol buffer file +with a proto message description. Note that proto messages produce (and +correspond with) Go types whereas Cosmos SDK messages correspond to proto "rpc" +in the "Msg" service. + +If you're building an application with custom IBC logic, you might need to +scaffold IBC packets. An IBC packet represents the data sent from one blockchain +to another. You can only scaffold IBC packets in IBC-enabled modules scaffolded +with an "--ibc" flag. Note that the default module is not IBC-enabled. + + +**Options** + +``` + -h, --help help for scaffold +``` + +**SEE ALSO** + +* [ignite](#ignite) - Ignite CLI offers everything you need to scaffold, test, build, and launch your blockchain +* [ignite scaffold chain](#ignite-scaffold-chain) - New Cosmos SDK blockchain +* [ignite scaffold list](#ignite-scaffold-list) - CRUD for data stored as an array +* [ignite scaffold map](#ignite-scaffold-map) - CRUD for data stored as key-value pairs +* [ignite scaffold message](#ignite-scaffold-message) - Message to perform state transition on the blockchain +* [ignite scaffold module](#ignite-scaffold-module) - Custom Cosmos SDK module +* [ignite scaffold packet](#ignite-scaffold-packet) - Message for sending an IBC packet +* [ignite scaffold query](#ignite-scaffold-query) - Query for fetching data from a blockchain +* [ignite scaffold react](#ignite-scaffold-react) - React web app template +* [ignite scaffold single](#ignite-scaffold-single) - CRUD for data stored in a single location +* [ignite scaffold type](#ignite-scaffold-type) - Type definition +* [ignite scaffold vue](#ignite-scaffold-vue) - Vue 3 web app template + + +## ignite scaffold chain + +New Cosmos SDK blockchain + +**Synopsis** + +Create a new application-specific Cosmos SDK blockchain. + +For example, the following command will create a blockchain called "hello" in +the "hello/" directory: + + ignite scaffold chain hello + +A project name can be a simple name or a URL. The name will be used as the Go +module path for the project. Examples of project names: + + ignite scaffold chain foo + ignite scaffold chain foo/bar + ignite scaffold chain example.org/foo + ignite scaffold chain github.com/username/foo + +A new directory with source code files will be created in the current directory. +To use a different path use the "--path" flag. + +Most of the logic of your blockchain is written in custom modules. Each module +effectively encapsulates an independent piece of functionality. Following the +Cosmos SDK convention, custom modules are stored inside the "x/" directory. By +default, Ignite creates a module with a name that matches the name of the +project. To create a blockchain without a default module use the "--no-module" +flag. Additional modules can be added after a project is created with "ignite +scaffold module" command. + +Account addresses on Cosmos SDK-based blockchains have string prefixes. For +example, the Cosmos Hub blockchain uses the default "cosmos" prefix, so that +addresses look like this: "cosmos12fjzdtqfrrve7zyg9sv8j25azw2ua6tvu07ypf". To +use a custom address prefix use the "--address-prefix" flag. For example: + + ignite scaffold chain foo --address-prefix bar + +By default when compiling a blockchain's source code Ignite creates a cache to +speed up the build process. To clear the cache when building a blockchain use +the "--clear-cache" flag. It is very unlikely you will ever need to use this +flag. + +The blockchain is using the Cosmos SDK modular blockchain framework. Learn more +about Cosmos SDK on https://docs.cosmos.network + + +``` +ignite scaffold chain [name] [flags] +``` + +**Options** + +``` + --address-prefix string account address prefix (default "cosmos") + --clear-cache clear the build cache (advanced) + -h, --help help for chain + --no-module create a project without a default module + -p, --path string create a project in a specific path + --skip-git skip Git repository initialization +``` + +**SEE ALSO** + +* [ignite scaffold](#ignite-scaffold) - Create a new blockchain, module, message, query, and more + + +## ignite scaffold list + +CRUD for data stored as an array + +**Synopsis** + +The "list" scaffolding command is used to generate files that implement the +logic for storing and interacting with data stored as a list in the blockchain +state. + +The command accepts a NAME argument that will be used as the name of a new type +of data. It also accepts a list of FIELDs that describe the type. + +The interaction with the data follows the create, read, updated, and delete +(CRUD) pattern. For each type three Cosmos SDK messages are defined for writing +data to the blockchain: MsgCreate{Name}, MsgUpdate{Name}, MsgDelete{Name}. For +reading data two queries are defined: {Name} and {Name}All. The type, messages, +and queries are defined in the "proto/" directory as protocol buffer messages. +Messages and queries are mounted in the "Msg" and "Query" services respectively. + +When messages are handled, the appropriate keeper methods are called. By +convention, the methods are defined in +"x/{moduleName}/keeper/msg_server_{name}.go". Helpful methods for getting, +setting, removing, and appending are defined in the same "keeper" package in +"{name}.go". + +The "list" command essentially allows you to define a new type of data and +provides the logic to create, read, update, and delete instances of the type. +For example, let's review a command that generates the code to handle a list of +posts and each post has "title" and "body" fields: + + ignite scaffold list post title body + +This provides you with a "Post" type, MsgCreatePost, MsgUpdatePost, +MsgDeletePost and two queries: Post and PostAll. The compiled CLI, let's say the +binary is "blogd" and the module is "blog", has commands to query the chain (see +"blogd q blog") and broadcast transactions with the messages above (see "blogd +tx blog"). + +The code generated with the list command is meant to be edited and tailored to +your application needs. Consider the code to be a "skeleton" for the actual +business logic you will implement next. + +By default, all fields are assumed to be strings. If you want a field of a +different type, you can specify it after a colon ":". The following types are +supported: string, bool, int, uint, coin, array.string, array.int, array.uint, +array.coin. An example of using field types: + + ignite scaffold list pool amount:coin tags:array.string height:int + +Supported types: + +| Type | Alias | Index | Code Type | Description | +|--------------|---------|-------|-----------|---------------------------------| +| string | - | yes | string | Text type | +| array.string | strings | no | []string | List of text type | +| bool | - | yes | bool | Boolean type | +| int | - | yes | int32 | Integer type | +| array.int | ints | no | []int32 | List of integers types | +| uint | - | yes | uint64 | Unsigned integer type | +| array.uint | uints | no | []uint64 | List of unsigned integers types | +| coin | - | no | sdk.Coin | Cosmos SDK coin type | +| array.coin | coins | no | sdk.Coins | List of Cosmos SDK coin types | + +"Index" indicates whether the type can be used as an index in +"ignite scaffold map". + +Ignite also supports custom types: + + ignite scaffold list product-details name desc + ignite scaffold list product price:coin details:ProductDetails + +In the example above the "ProductDetails" type was defined first, and then used +as a custom type for the "details" field. Ignite doesn't support arrays of +custom types yet. + +Your chain will accept custom types in JSON-notation: + + exampled tx example create-product 100coin '{"name": "x", "desc": "y"}' --from alice + +By default the code will be scaffolded in the module that matches your project's +name. If you have several modules in your project, you might want to specify a +different module: + + ignite scaffold list post title body --module blog + +By default, each message comes with a "creator" field that represents the +address of the transaction signer. You can customize the name of this field with +a flag: + + ignite scaffold list post title body --signer author + +It's possible to scaffold just the getter/setter logic without the CRUD +messages. This is useful when you want the methods to handle a type, but would +like to scaffold messages manually. Use a flag to skip message scaffolding: + + ignite scaffold list post title body --no-message + +The "creator" field is not generated if a list is scaffolded with the +"--no-message" flag. + + +``` +ignite scaffold list NAME [field]... [flags] +``` + +**Options** + +``` + --clear-cache clear the build cache (advanced) + -h, --help help for list + --module string specify which module to generate code in + --no-message skip generating message handling logic + --no-simulation skip simulation logic + -p, --path string path of the app (default ".") + --signer string label for the message signer (default: creator) + -y, --yes answers interactive yes/no questions with yes +``` + +**SEE ALSO** + +* [ignite scaffold](#ignite-scaffold) - Create a new blockchain, module, message, query, and more + + +## ignite scaffold map + +CRUD for data stored as key-value pairs + +**Synopsis** + +The "map" scaffolding command is used to generate files that implement the logic +for storing and interacting with data stored as key-value pairs (or a +dictionary) in the blockchain state. + +The "map" command is very similar to "ignite scaffold list" with the main +difference in how values are indexed. With "list" values are indexed by an +incrementing integer, whereas "list" values are indexed by a user-provided value +(or multiple values). + +Let's use the same blog post example: + + ignite scaffold map post title body + +This command scaffolds a "Post" type and CRUD functionality to create, read, +updated, and delete posts. However, when creating a new post with your chain's +binary (or by submitting a transaction through the chain's API) you will be +required to provide an "index": + + blogd tx blog create-post [index] [title] [body] + blogd tx blog create-post hello "My first post" "This is the body" + +This command will create a post and store it in the blockchain's state under the +"hello" index. You will be able to fetch back the value of the post by querying +for the "hello" key. + + blogd q blog show-post hello + +To customize the index, use the "--index" flag. Multiple indices can be +provided, which simplifies querying values. For example: + + ignite scaffold map product price desc --index category,guid + +With this command, you would get a "Product" value indexed by both a category +and a GUID (globally unique ID). This will let you programmatically fetch +product values that have the same category but are using different GUIDs. + +Since the behavior of "list" and "map" scaffolding is very similar, you can use +the "--no-message", "--module", "--signer" flags as well as the colon syntax for +custom types. + + +``` +ignite scaffold map NAME [field]... [flags] +``` + +**Options** + +``` + --clear-cache clear the build cache (advanced) + -h, --help help for map + --index strings fields that index the value (default [index]) + --module string specify which module to generate code in + --no-message skip generating message handling logic + --no-simulation skip simulation logic + -p, --path string path of the app (default ".") + --signer string label for the message signer (default: creator) + -y, --yes answers interactive yes/no questions with yes +``` + +**SEE ALSO** + +* [ignite scaffold](#ignite-scaffold) - Create a new blockchain, module, message, query, and more + + +## ignite scaffold message + +Message to perform state transition on the blockchain + +**Synopsis** + +Message scaffolding is useful for quickly adding functionality to your +blockchain to handle specific Cosmos SDK messages. + +Messages are objects whose end goal is to trigger state transitions on the +blockchain. A message is a container for fields of data that affect how the +blockchain's state will change. You can think of messages as "actions" that a +user can perform. + +For example, the bank module has a "Send" message for token transfers between +accounts. The send message has three fields: from address (sender), to address +(recipient), and a token amount. When this message is successfully processed, +the token amount will be deducted from the sender's account and added to the +recipient's account. + +Ignite's message scaffolding lets you create new types of messages and add them +to your chain. For example: + + ignite scaffold message add-pool amount:coins denom active:bool --module dex + +The command above will create a new message MsgAddPool with three fields: amount +(in tokens), denom (a string), and active (a boolean). The message will be added +to the "dex" module. + +By default, the message is defined as a proto message in the +"proto/{app}/{module}/tx.proto" and registered in the "Msg" service. A CLI command to +create and broadcast a transaction with MsgAddPool is created in the module's +"cli" package. Additionally, Ignite scaffolds a message constructor and the code +to satisfy the sdk.Msg interface and register the message in the module. + +Most importantly in the "keeper" package Ignite scaffolds an "AddPool" function. +Inside this function, you can implement message handling logic. + +When successfully processed a message can return data. Use the —response flag to +specify response fields and their types. For example + + ignite scaffold message create-post title body --response id:int,title + +The command above will scaffold MsgCreatePost which returns both an ID (an +integer) and a title (a string). + +Message scaffolding follows the rules as "ignite scaffold list/map/single" and +supports fields with standard and custom types. See "ignite scaffold list —help" +for details. + + +``` +ignite scaffold message [name] [field1] [field2] ... [flags] +``` + +**Options** + +``` + --clear-cache clear the build cache (advanced) + -d, --desc string description of the command + -h, --help help for message + --module string module to add the message into. Default: app's main module + --no-simulation disable CRUD simulation scaffolding + -p, --path string path of the app (default ".") + -r, --response strings response fields + --signer string label for the message signer (default: creator) + -y, --yes answers interactive yes/no questions with yes +``` + +**SEE ALSO** + +* [ignite scaffold](#ignite-scaffold) - Create a new blockchain, module, message, query, and more + + +## ignite scaffold module + +Custom Cosmos SDK module + +**Synopsis** + +Scaffold a new Cosmos SDK module. + +Cosmos SDK is a modular framework and each independent piece of functionality is +implemented in a separate module. By default your blockchain imports a set of +standard Cosmos SDK modules. To implement custom functionality of your +blockchain, scaffold a module and implement the logic of your application. + +This command does the following: + +* Creates a directory with module's protocol buffer files in "proto/" +* Creates a directory with module's boilerplate Go code in "x/" +* Imports the newly created module by modifying "app/app.go" +* Creates a file in "testutil/keeper/" that contains logic to create a keeper + for testing purposes + +This command will proceed with module scaffolding even if "app/app.go" doesn't +have the required default placeholders. If the placeholders are missing, you +will need to modify "app/app.go" manually to import the module. If you want the +command to fail if it can't import the module, use the "--require-registration" +flag. + +To scaffold an IBC-enabled module use the "--ibc" flag. An IBC-enabled module is +like a regular module with the addition of IBC-specific logic and placeholders +to scaffold IBC packets with "ignite scaffold packet". + +A module can depend on one or more other modules and import their keeper +methods. To scaffold a module with a dependency use the "--dep" flag + +For example, your new custom module "foo" might have functionality that requires +sending tokens between accounts. The method for sending tokens is a defined in +the "bank"'s module keeper. You can scaffold a "foo" module with the dependency +on "bank" with the following command: + + ignite scaffold module foo --dep bank + +You can then define which methods you want to import from the "bank" keeper in +"expected_keepers.go". + +You can also scaffold a module with a list of dependencies that can include both +standard and custom modules (provided they exist): + + ignite scaffold module bar --dep foo,mint,account,FeeGrant + +Note: the "--dep" flag doesn't install third-party modules into your +application, it just generates extra code that specifies which existing modules +your new custom module depends on. + +A Cosmos SDK module can have parameters (or "params"). Params are values that +can be set at the genesis of the blockchain and can be modified while the +blockchain is running. An example of a param is "Inflation rate change" of the +"mint" module. A module can be scaffolded with params using the "--params" flag +that accepts a list of param names. By default params are of type "string", but +you can specify a type for each param. For example: + + ignite scaffold module foo --params baz:uint,bar:bool + +Refer to Cosmos SDK documentation to learn more about modules, dependencies and +params. + + +``` +ignite scaffold module [name] [flags] +``` + +**Options** + +``` + --clear-cache clear the build cache (advanced) + --dep strings add a dependency on another module + -h, --help help for module + --ibc add IBC functionality + --ordering string channel ordering of the IBC module [none|ordered|unordered] (default "none") + --params strings add module parameters + -p, --path string path of the app (default ".") + --require-registration fail if module can't be registered + -y, --yes answers interactive yes/no questions with yes +``` + +**SEE ALSO** + +* [ignite scaffold](#ignite-scaffold) - Create a new blockchain, module, message, query, and more + + +## ignite scaffold packet + +Message for sending an IBC packet + +**Synopsis** + +Scaffold an IBC packet in a specific IBC-enabled Cosmos SDK module + +``` +ignite scaffold packet [packetName] [field1] [field2] ... --module [moduleName] [flags] +``` + +**Options** + +``` + --ack strings custom acknowledgment type (field1,field2,...) + --clear-cache clear the build cache (advanced) + -h, --help help for packet + --module string IBC Module to add the packet into + --no-message disable send message scaffolding + -p, --path string path of the app (default ".") + --signer string label for the message signer (default: creator) + -y, --yes answers interactive yes/no questions with yes +``` + +**SEE ALSO** + +* [ignite scaffold](#ignite-scaffold) - Create a new blockchain, module, message, query, and more + + +## ignite scaffold query + +Query for fetching data from a blockchain + +``` +ignite scaffold query [name] [request_field1] [request_field2] ... [flags] +``` + +**Options** + +``` + --clear-cache clear the build cache (advanced) + -d, --desc string description of the CLI to broadcast a tx with the message + -h, --help help for query + --module string module to add the query into. Default: app's main module + --paginated define if the request can be paginated + -p, --path string path of the app (default ".") + -r, --response strings response fields + -y, --yes answers interactive yes/no questions with yes +``` + +**SEE ALSO** + +* [ignite scaffold](#ignite-scaffold) - Create a new blockchain, module, message, query, and more + + +## ignite scaffold react + +React web app template + +``` +ignite scaffold react [flags] +``` + +**Options** + +``` + -h, --help help for react + -p, --path string path to scaffold content of the React app (default "./react") + -y, --yes answers interactive yes/no questions with yes +``` + +**SEE ALSO** + +* [ignite scaffold](#ignite-scaffold) - Create a new blockchain, module, message, query, and more + + +## ignite scaffold single + +CRUD for data stored in a single location + +``` +ignite scaffold single NAME [field]... [flags] +``` + +**Options** + +``` + --clear-cache clear the build cache (advanced) + -h, --help help for single + --module string specify which module to generate code in + --no-message skip generating message handling logic + --no-simulation skip simulation logic + -p, --path string path of the app (default ".") + --signer string label for the message signer (default: creator) + -y, --yes answers interactive yes/no questions with yes +``` + +**SEE ALSO** + +* [ignite scaffold](#ignite-scaffold) - Create a new blockchain, module, message, query, and more + + +## ignite scaffold type + +Type definition + +``` +ignite scaffold type NAME [field]... [flags] +``` + +**Options** + +``` + --clear-cache clear the build cache (advanced) + -h, --help help for type + --module string specify which module to generate code in + --no-message skip generating message handling logic + --no-simulation skip simulation logic + -p, --path string path of the app (default ".") + --signer string label for the message signer (default: creator) + -y, --yes answers interactive yes/no questions with yes +``` + +**SEE ALSO** + +* [ignite scaffold](#ignite-scaffold) - Create a new blockchain, module, message, query, and more + + +## ignite scaffold vue + +Vue 3 web app template + +``` +ignite scaffold vue [flags] +``` + +**Options** + +``` + -h, --help help for vue + -p, --path string path to scaffold content of the Vue.js app (default "./vue") + -y, --yes answers interactive yes/no questions with yes +``` + +**SEE ALSO** + +* [ignite scaffold](#ignite-scaffold) - Create a new blockchain, module, message, query, and more + + +## ignite tools + +Tools for advanced users + +**Options** + +``` + -h, --help help for tools +``` + +**SEE ALSO** + +* [ignite](#ignite) - Ignite CLI offers everything you need to scaffold, test, build, and launch your blockchain +* [ignite tools ibc-relayer](#ignite-tools-ibc-relayer) - TypeScript implementation of an IBC relayer +* [ignite tools ibc-setup](#ignite-tools-ibc-setup) - Collection of commands to quickly setup a relayer +* [ignite tools protoc](#ignite-tools-protoc) - Execute the protoc command + + +## ignite tools ibc-relayer + +TypeScript implementation of an IBC relayer + +``` +ignite tools ibc-relayer [--] [...] [flags] +``` + +**Examples** + +``` +ignite tools ibc-relayer -- -h +``` + +**Options** + +``` + -h, --help help for ibc-relayer +``` + +**SEE ALSO** + +* [ignite tools](#ignite-tools) - Tools for advanced users + + +## ignite tools ibc-setup + +Collection of commands to quickly setup a relayer + +``` +ignite tools ibc-setup [--] [...] [flags] +``` + +**Examples** + +``` +ignite tools ibc-setup -- -h +ignite tools ibc-setup -- init --src relayer_test_1 --dest relayer_test_2 +``` + +**Options** + +``` + -h, --help help for ibc-setup +``` + +**SEE ALSO** + +* [ignite tools](#ignite-tools) - Tools for advanced users + + +## ignite tools protoc + +Execute the protoc command + +**Synopsis** + +The protoc command. You don't need to setup the global protoc include folder with -I, it's automatically handled + +``` +ignite tools protoc [--] [...] [flags] +``` + +**Examples** + +``` +ignite tools protoc -- --version +``` + +**Options** + +``` + -h, --help help for protoc +``` + +**SEE ALSO** + +* [ignite tools](#ignite-tools) - Tools for advanced users + + +## ignite version + +Print the current build information + +``` +ignite version [flags] +``` + +**Options** + +``` + -h, --help help for version +``` + +**SEE ALSO** + +* [ignite](#ignite) - Ignite CLI offers everything you need to scaffold, test, build, and launch your blockchain + diff --git a/docs/versioned_docs/version-v0.27.1/08-references/02-config.md b/docs/versioned_docs/version-v0.27.1/08-references/02-config.md new file mode 100644 index 0000000000..1eb4aa7f8f --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/08-references/02-config.md @@ -0,0 +1,263 @@ +--- +sidebar_position: 3 +description: Primary configuration file to describe the development environment for your blockchain. +title: Configuration file +--- + +# Configuration file reference + +The `config.yml` file generated in your blockchain folder uses key-value pairs +to describe the development environment for your blockchain. + +Only a default set of parameters is provided. If more nuanced configuration is +required, you can add these parameters to the `config.yml` file. + +## Accounts + +A list of user accounts created during genesis of the blockchain. + +```yml +accounts: + - name: alice + coins: ['20000token', '200000000stake'] + - name: bob + coins: ['10000token', '100000000stake'] +``` + +Ignite uses information from `accounts` when initializing the chain with `ignite +chain init` and `ignite chain start`. In the example above Ignite will add two +accounts to the `genesis.json` file of the chain. + +`name` is a local name of a key pair associated with an account. Once the chain +is initialized and started, you will be able to use `name` when signing +transactions. With the configuration above, you'd be able to sign transactions +both with Alice's and Bob's accounts like so `exampled tx bank send ... --from +alice`. + +`coins` is a list of token balances for the account. If a token denomination is +in this list, it will exist in the genesis balance and will be a valid token. +When initialized with the config file above, a chain will only have two accounts +at genesis (Alice and Bob) and two native tokens (with denominations `token` and +`stake`). + +By default, every time a chain is re-initialized, Ignite will create a new key +pair for each account. So even though the account name can remain the same +(`bob`), every chain reinitialize it will have a different mnemonic and address. + +If you want an account to have a specific address, provide the `address` field +with a valid bech32 address. The prefix (by default, `cosmos`) should match the +one expected by your chain. When an account is provided with an `address` a key +pair will not be generated, because it's impossible to derive a key from an +address. An account with a given address will be added to the genesis file (with +an associated token balance), but because there is no key pair, you will not be +able to broadcast transactions from that address. This is useful when you have +generated a key pair outside of Ignite (for example, using your chain's CLI or +in an extension wallet) and want to have a token balance associated with the +address of this key pair. + +```yml +accounts: + - name: bob + coins: ['20000token', '200000000stake'] + address: cosmos1s39200s6v4c96ml2xzuh389yxpd0guk2mzn3mz +``` + +If you want an account to be initialized from a specific mnemonic, provide the +`mnemonic` field with a valid mnemonic. A private key, a public key and an +address will be derived from a mnemonic. + +```yml +accounts: + - name: bob + coins: ['20000token', '200000000stake'] + mnemonic: cargo ramp supreme review change various throw air figure humble soft steel slam pole betray inhale already dentist enough away office apple sample glue +``` + +You cannot have both `address` and `mnemonic` defined for a single account. + +Some accounts are used as validator accounts (see `validators` section). +Validator accounts cannot have an `address` field, because Ignite needs to be +able to derive a private key (either from a random mnemonic or from a specific +one provided in the `mnemonic` field). Validator accounts should have enough +tokens of the staking denomination for self-delegation. + +By default, the `alice` account is used as a validator account, its key is +derived from a mnemonic generated randomly at genesis, the staking denomination +is `stake`, and this account has enough `stake` for self-delegation. + +If your chain is using its own +[cointype](https://github.com/satoshilabs/slips/blob/master/slip-0044.md), you +can use the `cointype` field to provide the integer value + +```yml +accounts: + - name: bob + coins: ['20000token', '200000000stake'] + cointype: 7777777 +``` + +## Validators + +Commands like `ignite chain init` and `ignite chain serve` initialize and launch +a validator node for development purposes. + +```yml +validators: + - name: alice + bonded: '100000000stake' +``` + +`name` refers to key name in the `accounts` list. + +`bonded` is the self-delegation amount of a validator. The `bonded` amount +should not be lower than `1000000` nor higher than the account's +balance in the `account` list. + +Validators store their node configuration files in the data directory. By +default, Ignite uses the name of the project as the name of the data directory, +for example, `$HOME/.example/`. To use a different path for the data directory +you can customize the `home` property. + +Configuration in the data directory is reset frequently by Ignite. To persist +some changes to configuration files you can use `app`, `config` and `client` +properties that correspond to `$HOME/.example/config/app.toml`, +`$HOME/.example/config/config.toml` and `$HOME/.example/config/client.toml`. + +```yml +validators: + - name: alice + bonded: '100000000stake' + home: "~/.mychain" + app: + pruning: "nothing" + config: + moniker: "mychain" + client: + output: "json" +``` + +To see which properties are available for `config.toml`, `app.toml` and +`client.toml`, initialize a chain with `ignite chain init` and open the file you +want to know more about. + +Currently, Ignite starts only one validator node, so the first item in the +`validators` list is used (the rest is ignored). Support for multiple validators +is in progress. + +## Build + +The `build` property lets you customize how Ignite builds your chain's binary. + +By default, Ignite builds the `main` package from `cmd/PROJECT_NAME/main.go`. If +you more than one `main` package in your project, or you have renamed the +directory, use the `main` property to provide the path to the `main` Go package: + +```yml +build: + main: cmd/hello/cmd +``` + +Ignite compiles your project into a binary and uses the project's name with a +`d` suffix as name for the binary. To customize the binary name use the `binary` +property: + +```yml +build: + binary: "helloworldd" +``` + +To customize the linker flags used in the build process: + +```yml +build: + ldflags: [ "-X main.Version=development", "-X main.Date=01/05/2022T19:54" ] +``` + +By default, custom protocol buffer (proto) files are located in the `proto` +directory. If your project keeps proto files in a different directory, you +should tell Ignite about this: + +```yml +build: + proto: + path: "myproto" +``` + +Ignite comes with required third-party proto out of the box. Ignite also looks +into `third_party/proto` and `proto_vendor` directories for extra proto files. +If your project keeps third-party proto files in a different directory, you +should tell Ignite about this: + +```yml +build: + proto: + third_party_paths: ["my_third_party/proto"] +``` + +## Faucet + +The faucet service sends tokens to addresses. + +```yml +faucet: + name: bob + coins: ["5token", "100000stake"] +``` + +`name` refers to a key name in the `accounts` list. This is a required property. + +`coins` is the amount of tokens that will be sent to a user by the faucet. This +is a required property. + +`coins_max` is a maximum amount of tokens that can be sent to a single address. +To reset the token limit use the `rate_limit_window` property (in seconds). + +The default the faucet works on port `4500`. To use a different port number use +the `port` property. + +```yml +faucet: + name: faucet + coins: [ "100token", "5foo" ] + coins_max: [ "2000token", "1000foo" ] + port: 4500 + rate_limit_window: 3600 +``` + +## Genesis + +Genesis file is the initial block in the blockchain. It is required to launch a +blockchain, because it contains important information like token balances, and +modules' state. Genesis is stored in `$DATA_DIR/config/genesis.json`. + +Since the genesis file is reinitialized frequently during development, you can +set persistent options in the `genesis` property: + +```yml +genesis: + app_state: + staking: + params: + bond_denom: "denom" +``` + +To know which properties a genesis file supports, initialize a chain and look up +the genesis file in the data directory. + +## Client code generation + +Ignite can generate client-side code for interacting with your chain with the +`ignite generate` set of commands. Use the following properties to customize the +paths where the client-side code is generated. + +```yml +client: + openapi: + path: "docs/static/openapi.yml" + typescript: + path: "ts-client" + composables: + path: "vue/src/composables" + hooks: + path: "react/src/hooks" +``` diff --git a/docs/versioned_docs/version-v0.27.1/08-references/_category_.json b/docs/versioned_docs/version-v0.27.1/08-references/_category_.json new file mode 100644 index 0000000000..22c96cc0a0 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/08-references/_category_.json @@ -0,0 +1,5 @@ +{ + "label": "References", + "link": null, + "collapsed": false +} \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.27.1/plugins/01-using-plugins.md b/docs/versioned_docs/version-v0.27.1/plugins/01-using-plugins.md new file mode 100644 index 0000000000..9b830a2ef6 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/plugins/01-using-plugins.md @@ -0,0 +1,43 @@ +--- +description: Using and Developing plugins +--- + +# Using Plugins + +Ignite plugins offer a way to extend the functionality of the Ignite CLI. There +are two core concepts within plugins : `Commands` and `Hooks`. Where `Commands` +extend the cli's functionality, and `Hooks` extend existing command +functionality. + +Plugins are registered in an Ignite scaffolded Blockchain project through the +`plugins.yml`, or globally through `$HOME/.ignite/plugins/plugins.yml`. + +To use a plugin within your project, execute the following command inside the +project directory: + +```sh +ignite plugin add github.com/project/cli-plugin +``` + +The plugin will be available only when running `ignite` inside the project +directory. + +To use a plugin globally on the other hand, execute the following command: + +```sh +ignite plugin add -g github.com/project/cli-plugin +``` + +The command will compile the plugin and make it immediately available to the +`ignite` command lists. + +## Listing installed plugins + +When in an ignite scaffolded blockchain you can use the command `ignite plugin +list` to list all plugins and there statuses. + +## Updating plugins + +When a plugin in a remote repository releases updates, running `ignite plugin +update ` will update a specific plugin declared in your +project's `config.yml`. diff --git a/docs/versioned_docs/version-v0.27.1/plugins/02-dev-plugins.md b/docs/versioned_docs/version-v0.27.1/plugins/02-dev-plugins.md new file mode 100644 index 0000000000..6cbc1b6426 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/plugins/02-dev-plugins.md @@ -0,0 +1,244 @@ +--- +description: Using and Developing plugins +--- + +# Developing Plugins + +It's easy to create a plugin and use it immediately in your project. First +choose a directory outside your project and run : + +```sh +$ ignite plugin scaffold my-plugin +``` + +This will create a new directory `my-plugin` that contains the plugin's code, +and will output some instructions about how to use your plugin with the +`ignite` command. Indeed, a plugin path can be a local directory, which has +several benefits: + +- you don't need to use a git repository during the development of your plugin. +- the plugin is recompiled each time you run the `ignite` binary in your +project, if the source files are older than the plugin binary. + +Thus, the plugin development workflow is as simple as : + +1. scaffold a plugin with `ignite plugin scaffold my-plugin` +2. add it to your config via `ignite plugin add -g /path/to/my-plugin` +3. update plugin code +4. run `ignite my-plugin` binary to compile and run the plugin. +5. go back to 3. + +Once your plugin is ready, you can publish it to a git repository, and the +community can use it by calling `ignite plugin add github.com/foo/my-plugin`. + +Now let's detail how to update your plugin's code. + +## The plugin interface + +The `ignite` plugin system uses `github.com/hashicorp/go-plugin` under the hood, +which implies to implement a predefined interface: + +```go title=ignite/services/plugin/interface.go +// An ignite plugin must implements the Plugin interface. +type Interface interface { + // Manifest declares the plugin's Command(s) and Hook(s). + Manifest() (Manifest, error) + + // Execute will be invoked by ignite when a plugin Command is executed. + // It is global for all commands declared in Manifest, if you have declared + // multiple commands, use cmd.Path to distinguish them. + Execute(cmd ExecutedCommand) error + + // ExecuteHookPre is invoked by ignite when a command specified by the Hook + // path is invoked. + // It is global for all hooks declared in Manifest, if you have declared + // multiple hooks, use hook.Name to distinguish them. + ExecuteHookPre(hook ExecutedHook) error + + // ExecuteHookPost is invoked by ignite when a command specified by the hook + // path is invoked. + // It is global for all hooks declared in Manifest, if you have declared + // multiple hooks, use hook.Name to distinguish them. + ExecuteHookPost(hook ExecutedHook) error + + // ExecuteHookCleanUp is invoked by ignite when a command specified by the + // hook path is invoked. Unlike ExecuteHookPost, it is invoked regardless of + // execution status of the command and hooks. + // It is global for all hooks declared in Manifest, if you have declared + // multiple hooks, use hook.Name to distinguish them. + ExecuteHookCleanUp(hook ExecutedHook) error +} +``` + +The code scaffolded already implements this interface, you just need to update +the methods' body. + + +## Defining plugin's manifest + +Here is the `Manifest` struct : + +```go title=ignite/services/plugin/interface.go +type Manifest struct { + Name string + // Commands contains the commands that will be added to the list of ignite + // commands. Each commands are independent, for nested commands use the + // inner Commands field. + Commands []Command + // Hooks contains the hooks that will be attached to the existing ignite + // commands. + Hooks []Hook + // SharedHost enables sharing a single plugin server across all running instances + // of a plugin. Useful if a plugin adds or extends long running commands + // + // Example: if a plugin defines a hook on `ignite chain serve`, a plugin server is instanciated + // when the command is run. Now if you want to interact with that instance from commands + // defined in that plugin, you need to enable `SharedHost`, or else the commands will just + // instantiate separate plugin servers. + // + // When enabled, all plugins of the same `Path` loaded from the same configuration will + // attach it's rpc client to a an existing rpc server. + // + // If a plugin instance has no other running plugin servers, it will create one and it will be the host. + SharedHost bool `yaml:"shared_host"` +} +``` + +In your plugin's code, the `Manifest` method already returns a predefined +`Manifest` struct as an example. Adapt it according to your need. + +If your plugin adds one or more new commands to `ignite`, feeds the `Commands` +field. + +If your plugin adds features to existing commands, feeds the `Hooks` field. + +Of course a plugin can declare `Commands` *and* `Hooks`. + +A plugin may also share a host process by setting `SharedHost` to `true`. +`SharedHost` is desirable if a plugin hooks into, or declares long running commands. +Commands executed from the same plugin context interact with the same plugin server. +Allowing all executing commands to share the same server instance, giving shared execution context. + +## Adding new command + +Plugin commands are custom commands added to the ignite cli by a registered +plugin. Commands can be of any path not defined already by ignite. All plugin +commands will extend of the command root `ignite`. + +For instance, let's say your plugin adds a new `oracle` command to `ignite +scaffold`, the `Manifest()` method will look like : + +```go +func (p) Manifest() (plugin.Manifest, error) { + return plugin.Manifest{ + Name: "oracle", + Commands: []plugin.Command{ + { + Use: "oracle [name]", + Short: "Scaffold an oracle module", + Long: "Long description goes here...", + // Optionnal flags is required + Flags: []plugin.Flag{ + {Name: "source", Type: plugin.FlagTypeString, Usage: "the oracle source"}, + }, + // Attach the command to `scaffold` + PlaceCommandUnder: "ignite scaffold", + }, + }, + }, nil +} +``` + +To update the plugin execution, you have to change the plugin `Execute` command, +for instance : + +```go +func (p) Execute(cmd plugin.ExecutedCommand) error { + if len(cmd.Args) == 0 { + return fmt.Errorf("oracle name missing") + } + var ( + name = cmd.Args[0] + source, _ = cmd.Flags().GetString("source") + ) + // Read chain information + c, err := getChain(cmd) + if err != nil { + return err + } + + //... +} +``` + +Then, run `ignite scaffold oracle` to execute the plugin. + +## Adding hooks + +Plugin `Hooks` allow existing ignite commands to be extended with new +functionality. Hooks are useful when you want to streamline functionality +without needing to run custom scripts after or before a command has been run. +this can streamline processes that where once error prone or forgotten all +together. + +The following are hooks defined which will run on a registered `ignite` commands + +| Name | Description | +| -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | +| Pre | Runs before a commands main functionality is invoked in the `PreRun` scope | +| Post | Runs after a commands main functionality is invoked in the `PostRun` scope | +| Clean Up | Runs after a commands main functionality is invoked. if the command returns an error it will run before the error is returned to guarantee execution. | + +*Note*: If a hook causes an error in the pre step the command will not run +resulting in `post` and `clean up` not executing. + +The following is an example of a `hook` definition. + +```go +func (p) Manifest() (plugin.Manifest, error) { + return plugin.Manifest{ + Name: "oracle", + Hooks: []plugin.Hook{ + { + Name: "my-hook", + PlaceHookOn: "ignite chain build", + }, + }, + }, nil +} + +func (p) ExecuteHookPre(hook plugin.ExecutedHook) error { + switch hook.Name { + case "my-hook": + fmt.Println("I'm executed before ignite chain build") + default: + return fmt.Errorf("hook not defined") + } + return nil +} + +func (p) ExecuteHookPost(hook plugin.ExecutedHook) error { + switch hook.Name { + case "my-hook": + fmt.Println("I'm executed after ignite chain build (if no error)") + default: + return fmt.Errorf("hook not defined") + } + return nil +} + +func (p) ExecuteHookCleanUp(hook plugin.ExecutedHook) error { + switch hook.Name { + case "my-hook": + fmt.Println("I'm executed after ignite chain build (regardless errors)") + default: + return fmt.Errorf("hook not defined") + } + return nil +} +``` + +Above we can see a similar definition to `Command` where a hook has a `Name` and +a `PlaceHookOn`. You'll notice that the `Execute*` methods map directly to each +life cycle of the hook. All hooks defined within the plugin will invoke these +methods. diff --git a/docs/versioned_docs/version-v0.27.1/plugins/_category_.json b/docs/versioned_docs/version-v0.27.1/plugins/_category_.json new file mode 100644 index 0000000000..27114924ce --- /dev/null +++ b/docs/versioned_docs/version-v0.27.1/plugins/_category_.json @@ -0,0 +1,5 @@ +{ + "label": "Plugins", + "position": 7, + "link": null +} diff --git a/docs/versioned_sidebars/version-v0.27.1-sidebars.json b/docs/versioned_sidebars/version-v0.27.1-sidebars.json new file mode 100644 index 0000000000..f3e9bb7a5b --- /dev/null +++ b/docs/versioned_sidebars/version-v0.27.1-sidebars.json @@ -0,0 +1,25 @@ +{ + "tutorialSidebar": [ + { + "type": "autogenerated", + "dirName": "." + }, + { + "type": "category", + "label": "Resources", + "collapsed": true, + "items": [ + { + "type": "link", + "label": "Ignite CLI on Github", + "href": "https://github.com/ignite/cli" + }, + { + "type": "link", + "label": "Cosmos SDK Docs", + "href": "https://docs.cosmos.network/" + } + ] + } + ] +} diff --git a/docs/versions.json b/docs/versions.json index 4f8191fdff..efb566a16c 100644 --- a/docs/versions.json +++ b/docs/versions.json @@ -1,4 +1,5 @@ [ + "v0.27.1", "v0.26.1", "v0.25.2" ] diff --git a/docs/yarn.lock b/docs/yarn.lock index 9a70b4f37b..2aa51b1117 100644 --- a/docs/yarn.lock +++ b/docs/yarn.lock @@ -2357,11 +2357,6 @@ resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== -abbrev@1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" - integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== - accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: version "1.3.8" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" @@ -2518,11 +2513,6 @@ anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" -"aproba@^1.0.3 || ^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" - integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== - arg@^5.0.0, arg@^5.0.2: version "5.0.2" resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" @@ -2565,13 +2555,6 @@ at-least-node@^1.0.0: resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== -autocomplete.js@^0.37.0: - version "0.37.1" - resolved "https://registry.yarnpkg.com/autocomplete.js/-/autocomplete.js-0.37.1.tgz#a29a048d827e7d2bf8f7df8b831766e5cc97df01" - integrity sha512-PgSe9fHYhZEsm/9jggbjtVsGXJkPLvd+9mC7gZJ662vVL5CRWEtm/mIrrzCx0MrNxHVwxD5d00UOn6NsmL2LUQ== - dependencies: - immediate "^3.2.3" - autoprefixer@^10.4.12, autoprefixer@^10.4.14, autoprefixer@^10.4.7: version "10.4.14" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.14.tgz#e28d49902f8e759dd25b153264e862df2705f79d" @@ -2683,11 +2666,6 @@ batch@0.6.1: resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw== -bcp-47-match@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/bcp-47-match/-/bcp-47-match-1.0.3.tgz#cb8d03071389a10aff2062b862d6575ffd7cd7ef" - integrity sha512-LggQ4YTdjWQSKELZF5JwchnBa1u0pIQSZf5lSdOHEdbVP55h0qICA/FUp3+W99q0xqxYa1ZQizTUH87gecII5w== - big.js@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" @@ -2955,11 +2933,6 @@ ci-info@^3.2.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.8.0.tgz#81408265a5380c929f0bc665d62256628ce9ef91" integrity sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw== -classnames@^2.2.6: - version "2.3.2" - resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924" - integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw== - clean-css@^5.2.2, clean-css@^5.3.0: version "5.3.2" resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-5.3.2.tgz#70ecc7d4d4114921f5d298349ff86a31a9975224" @@ -3041,11 +3014,6 @@ color-name@^1.1.4, color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -color-support@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" - integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== - colord@^2.9.1: version "2.9.3" resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43" @@ -3143,11 +3111,6 @@ consola@^2.15.3: resolved "https://registry.yarnpkg.com/consola/-/consola-2.15.3.tgz#2e11f98d6a4be71ff72e0bdf07bd23e12cb61550" integrity sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw== -console-control-strings@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" - integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== - content-disposition@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" @@ -3330,11 +3293,6 @@ css-select@^5.1.0: domutils "^3.0.1" nth-check "^2.0.1" -css-selector-parser@^1.0.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/css-selector-parser/-/css-selector-parser-1.4.1.tgz#03f9cb8a81c3e5ab2c51684557d5aaf6d2569759" - integrity sha512-HYPSb7y/Z7BNDCOrakL4raGO2zltZkbeXyAd6Tg9obzix6QhzxCotdBl6VT0Dv4vZfJGVz3WL/xaEI9Ly3ul0g== - css-to-react-native@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/css-to-react-native/-/css-to-react-native-3.2.0.tgz#cdd8099f71024e149e4f6fe17a7d46ecd55f1e32" @@ -3560,11 +3518,6 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" -direction@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/direction/-/direction-1.0.4.tgz#2b86fb686967e987088caf8b89059370d4837442" - integrity sha512-GYqKi1aH7PJXxdhTeZBFrg8vUBeKXi+cNprXsC1kpJcbcVnV9wBsrOu1cQEdG0WeQwlfHiy3XvnKfIrJ2R0NzQ== - dlv@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79" @@ -3582,26 +3535,6 @@ dns-packet@^5.2.2: dependencies: "@leichtgewicht/ip-codec" "^2.0.1" -docusaurus-lunr-search@^2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/docusaurus-lunr-search/-/docusaurus-lunr-search-2.3.2.tgz#9991ef51addb0bf09ac80a06cb5729f28939332d" - integrity sha512-Ngvm2kXwliWThqAThXI1912rOKHlFL7BjIc+OVNUfzkjpk5ar4TFEh+EUaaMOLw4V0BBko3CW0Ym7prqqm3jLQ== - dependencies: - autocomplete.js "^0.37.0" - classnames "^2.2.6" - gauge "^3.0.0" - hast-util-select "^4.0.0" - hast-util-to-text "^2.0.0" - hogan.js "^3.0.2" - lunr "^2.3.8" - lunr-languages "^1.4.0" - minimatch "^3.0.4" - object-assign "^4.1.1" - rehype-parse "^7.0.1" - to-vfile "^6.1.0" - unified "^9.0.0" - unist-util-is "^4.0.2" - docusaurus-plugin-image-zoom@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/docusaurus-plugin-image-zoom/-/docusaurus-plugin-image-zoom-0.1.1.tgz#f5e16ae568f7b74e8a357ee67ea7922521f64539" @@ -4150,21 +4083,6 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== -gauge@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395" - integrity sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q== - dependencies: - aproba "^1.0.3 || ^2.0.0" - color-support "^1.1.2" - console-control-strings "^1.0.0" - has-unicode "^2.0.1" - object-assign "^4.1.1" - signal-exit "^3.0.0" - string-width "^4.2.3" - strip-ansi "^6.0.1" - wide-align "^1.1.2" - gensync@^1.0.0-beta.1, gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -4368,11 +4286,6 @@ has-symbols@^1.0.3: resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== -has-unicode@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" - integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== - has-yarn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" @@ -4410,16 +4323,6 @@ hast-util-from-parse5@^6.0.0: vfile-location "^3.2.0" web-namespaces "^1.0.0" -hast-util-has-property@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/hast-util-has-property/-/hast-util-has-property-1.0.4.tgz#9f137565fad6082524b382c1e7d7d33ca5059f36" - integrity sha512-ghHup2voGfgFoHMGnaLHOjbYFACKrRh9KFttdCzMCbFoBMJXiNi2+XTrPP8+q6cDJM/RSqlCfVWrjp1H201rZg== - -hast-util-is-element@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/hast-util-is-element/-/hast-util-is-element-1.1.0.tgz#3b3ed5159a2707c6137b48637fbfe068e175a425" - integrity sha512-oUmNua0bFbdrD/ELDSSEadRVtWZOf3iF6Lbv81naqsIV99RnSCieTbWuWCY8BAeEfKJTKl0gRdokv+dELutHGQ== - hast-util-parse-selector@^2.0.0: version "2.2.5" resolved "https://registry.yarnpkg.com/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz#d57c23f4da16ae3c63b3b6ca4616683313499c3a" @@ -4441,26 +4344,6 @@ hast-util-raw@6.0.1: xtend "^4.0.0" zwitch "^1.0.0" -hast-util-select@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/hast-util-select/-/hast-util-select-4.0.2.tgz#ae3ef2860e02cda2ad3a2e72b47c1f5e8f44e9e7" - integrity sha512-8EEG2//bN5rrzboPWD2HdS3ugLijNioS1pqOTIolXNf67xxShYw4SQEmVXd3imiBG+U2bC2nVTySr/iRAA7Cjg== - dependencies: - bcp-47-match "^1.0.0" - comma-separated-tokens "^1.0.0" - css-selector-parser "^1.0.0" - direction "^1.0.0" - hast-util-has-property "^1.0.0" - hast-util-is-element "^1.0.0" - hast-util-to-string "^1.0.0" - hast-util-whitespace "^1.0.0" - not "^0.1.0" - nth-check "^2.0.0" - property-information "^5.0.0" - space-separated-tokens "^1.0.0" - unist-util-visit "^2.0.0" - zwitch "^1.0.0" - hast-util-to-parse5@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/hast-util-to-parse5/-/hast-util-to-parse5-6.0.0.tgz#1ec44650b631d72952066cea9b1445df699f8479" @@ -4472,25 +4355,6 @@ hast-util-to-parse5@^6.0.0: xtend "^4.0.0" zwitch "^1.0.0" -hast-util-to-string@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/hast-util-to-string/-/hast-util-to-string-1.0.4.tgz#9b24c114866bdb9478927d7e9c36a485ac728378" - integrity sha512-eK0MxRX47AV2eZ+Lyr18DCpQgodvaS3fAQO2+b9Two9F5HEoRPhiUMNzoXArMJfZi2yieFzUBMRl3HNJ3Jus3w== - -hast-util-to-text@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/hast-util-to-text/-/hast-util-to-text-2.0.1.tgz#04f2e065642a0edb08341976084aa217624a0f8b" - integrity sha512-8nsgCARfs6VkwH2jJU9b8LNTuR4700na+0h3PqCaEk4MAnMDeu5P0tP8mjk9LLNGxIeQRLbiDbZVw6rku+pYsQ== - dependencies: - hast-util-is-element "^1.0.0" - repeat-string "^1.0.0" - unist-util-find-after "^3.0.0" - -hast-util-whitespace@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/hast-util-whitespace/-/hast-util-whitespace-1.0.4.tgz#e4fe77c4a9ae1cb2e6c25e02df0043d0164f6e41" - integrity sha512-I5GTdSfhYfAPNztx2xJRQpG8cuDSNt599/7YUn7Gx/WxNMsG+a835k97TDkFgk123cwjfwINaZknkKkphx/f2A== - hastscript@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-6.0.0.tgz#e8768d7eac56c3fdeac8a92830d58e811e5bf640" @@ -4519,14 +4383,6 @@ history@^4.9.0: tiny-warning "^1.0.0" value-equal "^1.0.1" -hogan.js@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/hogan.js/-/hogan.js-3.0.2.tgz#4cd9e1abd4294146e7679e41d7898732b02c7bfd" - integrity sha512-RqGs4wavGYJWE07t35JQccByczmNUXQT0E12ZYV1VKYu5UiAU9lsos/yBAcf840+zrUQQxgVduCR5/B8nNtibg== - dependencies: - mkdirp "0.3.0" - nopt "1.0.10" - hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.1.0: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" @@ -4688,11 +4544,6 @@ image-size@^1.0.1: dependencies: queue "6.0.2" -immediate@^3.2.3: - version "3.3.0" - resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.3.0.tgz#1aef225517836bcdf7f2a2de2600c79ff0269266" - integrity sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q== - immer@^9.0.7: version "9.0.21" resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.21.tgz#1e025ea31a40f24fb064f1fef23e931496330176" @@ -5251,16 +5102,6 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -lunr-languages@^1.4.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/lunr-languages/-/lunr-languages-1.10.0.tgz#2afe9fff47b435d9bc74bd372fb923dbf8ee1990" - integrity sha512-BBjKKcwrieJlzwwc9M5H/MRXGJ2qyOSDx/NXYiwkuKjiLOOoouh0WsDzeqcLoUWcX31y7i8sb8IgsZKObdUCkw== - -lunr@^2.3.8: - version "2.3.9" - resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1" - integrity sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow== - make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" @@ -5424,11 +5265,6 @@ minimist@^1.2.0, minimist@^1.2.5: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== -mkdirp@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.0.tgz#1bbf5ab1ba827af23575143490426455f481fe1e" - integrity sha512-OHsdUcVAQ6pOtg5JYWpCBo9W/GySVuwvP9hueRMW7UqshC0tbfzLv8wjySTPm3tfUZ/21CE9E1pJagOA91Pxew== - mrmime@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-1.0.1.tgz#5f90c825fad4bdd41dc914eff5d1a8cfdaf24f27" @@ -5513,13 +5349,6 @@ node-releases@^2.0.8: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.10.tgz#c311ebae3b6a148c89b1813fd7c4d3c024ef537f" integrity sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w== -nopt@1.0.10: - version "1.0.10" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" - integrity sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg== - dependencies: - abbrev "1" - normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" @@ -5540,11 +5369,6 @@ normalize-url@^6.0.1: resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== -not@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/not/-/not-0.1.0.tgz#c9691c1746c55dcfbe54cbd8bd4ff041bc2b519d" - integrity sha512-5PDmaAsVfnWUgTUbJ3ERwn7u79Z0dYxN9ErxCpVJJqe2RK0PJ3z+iFUxuqjwtlDDegXvtWoxD/3Fzxox7tFGWA== - npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" @@ -5557,7 +5381,7 @@ nprogress@^0.2.0: resolved "https://registry.yarnpkg.com/nprogress/-/nprogress-0.2.0.tgz#cb8f34c53213d895723fcbab907e9422adbcafb1" integrity sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA== -nth-check@^2.0.0, nth-check@^2.0.1: +nth-check@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== @@ -6624,14 +6448,6 @@ regjsparser@^0.9.1: dependencies: jsesc "~0.5.0" -rehype-parse@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/rehype-parse/-/rehype-parse-7.0.1.tgz#58900f6702b56767814afc2a9efa2d42b1c90c57" - integrity sha512-fOiR9a9xH+Le19i4fGzIEowAbwG7idy2Jzs4mOrFWBSJ0sNUgy0ev871dwWnbOo371SjgjG4pwzrbgSVrKxecw== - dependencies: - hast-util-from-parse5 "^6.0.0" - parse5 "^6.0.0" - relateurl@^0.2.7: version "0.2.7" resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" @@ -6705,7 +6521,7 @@ renderkid@^3.0.0: lodash "^4.17.21" strip-ansi "^6.0.1" -repeat-string@^1.0.0, repeat-string@^1.5.4: +repeat-string@^1.5.4: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w== @@ -7031,7 +6847,7 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" -signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: +signal-exit@^3.0.2, signal-exit@^3.0.3: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== @@ -7165,7 +6981,7 @@ std-env@^3.0.1: resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.3.2.tgz#af27343b001616015534292178327b202b9ee955" integrity sha512-uUZI65yrV2Qva5gqE0+A7uVAvO40iPo6jGhs7s8keRfHCmtg+uB2X6EiLGCI9IgL1J17xGhvoOqSz79lzICPTA== -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: +string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -7439,14 +7255,6 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -to-vfile@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/to-vfile/-/to-vfile-6.1.0.tgz#5f7a3f65813c2c4e34ee1f7643a5646344627699" - integrity sha512-BxX8EkCxOAZe+D/ToHdDsJcVI4HqQfmw0tCkp31zf3dNP/XWIAjU4CmeuSwsSoOzOTqHPOL0KUzyZqJplkD0Qw== - dependencies: - is-buffer "^2.0.0" - vfile "^4.0.0" - toidentifier@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" @@ -7560,7 +7368,7 @@ unified@9.2.0: trough "^1.0.0" vfile "^4.0.0" -unified@^9.0.0, unified@^9.2.2: +unified@^9.2.2: version "9.2.2" resolved "https://registry.yarnpkg.com/unified/-/unified-9.2.2.tgz#67649a1abfc3ab85d2969502902775eb03146975" integrity sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ== @@ -7584,19 +7392,12 @@ unist-builder@2.0.3, unist-builder@^2.0.0: resolved "https://registry.yarnpkg.com/unist-builder/-/unist-builder-2.0.3.tgz#77648711b5d86af0942f334397a33c5e91516436" integrity sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw== -unist-util-find-after@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/unist-util-find-after/-/unist-util-find-after-3.0.0.tgz#5c65fcebf64d4f8f496db46fa8fd0fbf354b43e6" - integrity sha512-ojlBqfsBftYXExNu3+hHLfJQ/X1jYY/9vdm4yZWjIbf0VuWF6CRufci1ZyoD/wV2TYMKxXUoNuoqwy+CkgzAiQ== - dependencies: - unist-util-is "^4.0.0" - unist-util-generated@^1.0.0: version "1.1.6" resolved "https://registry.yarnpkg.com/unist-util-generated/-/unist-util-generated-1.1.6.tgz#5ab51f689e2992a472beb1b35f2ce7ff2f324d4b" integrity sha512-cln2Mm1/CZzN5ttGK7vkoGw+RZ8VcUH6BtGbq98DDtRGquAAOXig1mrBQYelOwMXYS8rK+vZDyyojSjp7JX+Lg== -unist-util-is@^4.0.0, unist-util-is@^4.0.2: +unist-util-is@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-4.1.0.tgz#976e5f462a7a5de73d94b706bac1b90671b57797" integrity sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg== @@ -7973,13 +7774,6 @@ which@^2.0.1: dependencies: isexe "^2.0.0" -wide-align@^1.1.2: - version "1.1.5" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" - integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== - dependencies: - string-width "^1.0.2 || 2 || 3 || 4" - widest-line@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca"