From 3d2b05585b8239c752c942b199e8068df540c861 Mon Sep 17 00:00:00 2001 From: julienrbrt Date: Wed, 15 May 2024 14:05:02 +0000 Subject: [PATCH] chore(docs): deploy version v28.4.0 --- .../version-v28/02-guide/04-blog.md | 403 +++++++++++++++ .../02-guide/04-blog/00-express.md | 309 ----------- .../version-v28/02-guide/04-blog/01-intro.md | 17 - .../02-guide/04-blog/02-scaffolding.md | 124 ----- .../version-v28/02-guide/04-blog/03-create.md | 323 ------------ .../version-v28/02-guide/04-blog/04-update.md | 130 ----- .../version-v28/02-guide/04-blog/05-delete.md | 76 --- .../version-v28/02-guide/04-blog/06-show.md | 81 --- .../version-v28/02-guide/04-blog/07-list.md | 94 ---- .../version-v28/02-guide/04-blog/08-play.md | 97 ---- .../02-guide/04-blog/09-summary.md | 22 - .../02-guide/04-blog/_category_.json | 4 - .../version-v28/02-guide/05-loan.md | 483 ++++++++++++++++++ .../version-v28/02-guide/05-loan/00-intro.md | 86 ---- .../version-v28/02-guide/05-loan/01-init.md | 72 --- .../version-v28/02-guide/05-loan/02-bank.md | 32 -- .../02-guide/05-loan/03-request.md | 118 ----- .../02-guide/05-loan/04-approve.md | 98 ---- .../version-v28/02-guide/05-loan/05-repay.md | 98 ---- .../02-guide/05-loan/06-liquidate.md | 90 ---- .../version-v28/02-guide/05-loan/07-cancel.md | 74 --- .../version-v28/02-guide/05-loan/08-play.md | 318 ------------ .../02-guide/05-loan/_category_.json | 4 - .../06-tokenfactory/01-tokenfactory.md | 2 +- .../version-v28/04-network/04-validator.md | 2 +- .../version-v28/06-migration/v0.28.0.md | 17 - .../version-v28/06-migration/v28.0.0.md | 124 +++++ .../version-v28/08-references/01-cli.md | 180 +------ .../version-v28/08-references/02-config.md | 20 + .../version-v28/apps/02-developing-apps.md | 4 +- docs/versions.json | 2 +- 31 files changed, 1044 insertions(+), 2460 deletions(-) create mode 100644 docs/versioned_docs/version-v28/02-guide/04-blog.md delete mode 100644 docs/versioned_docs/version-v28/02-guide/04-blog/00-express.md delete mode 100644 docs/versioned_docs/version-v28/02-guide/04-blog/01-intro.md delete mode 100644 docs/versioned_docs/version-v28/02-guide/04-blog/02-scaffolding.md delete mode 100644 docs/versioned_docs/version-v28/02-guide/04-blog/03-create.md delete mode 100644 docs/versioned_docs/version-v28/02-guide/04-blog/04-update.md delete mode 100644 docs/versioned_docs/version-v28/02-guide/04-blog/05-delete.md delete mode 100644 docs/versioned_docs/version-v28/02-guide/04-blog/06-show.md delete mode 100644 docs/versioned_docs/version-v28/02-guide/04-blog/07-list.md delete mode 100644 docs/versioned_docs/version-v28/02-guide/04-blog/08-play.md delete mode 100644 docs/versioned_docs/version-v28/02-guide/04-blog/09-summary.md delete mode 100644 docs/versioned_docs/version-v28/02-guide/04-blog/_category_.json create mode 100644 docs/versioned_docs/version-v28/02-guide/05-loan.md delete mode 100644 docs/versioned_docs/version-v28/02-guide/05-loan/00-intro.md delete mode 100644 docs/versioned_docs/version-v28/02-guide/05-loan/01-init.md delete mode 100644 docs/versioned_docs/version-v28/02-guide/05-loan/02-bank.md delete mode 100644 docs/versioned_docs/version-v28/02-guide/05-loan/03-request.md delete mode 100644 docs/versioned_docs/version-v28/02-guide/05-loan/04-approve.md delete mode 100644 docs/versioned_docs/version-v28/02-guide/05-loan/05-repay.md delete mode 100644 docs/versioned_docs/version-v28/02-guide/05-loan/06-liquidate.md delete mode 100644 docs/versioned_docs/version-v28/02-guide/05-loan/07-cancel.md delete mode 100644 docs/versioned_docs/version-v28/02-guide/05-loan/08-play.md delete mode 100644 docs/versioned_docs/version-v28/02-guide/05-loan/_category_.json delete mode 100644 docs/versioned_docs/version-v28/06-migration/v0.28.0.md create mode 100644 docs/versioned_docs/version-v28/06-migration/v28.0.0.md diff --git a/docs/versioned_docs/version-v28/02-guide/04-blog.md b/docs/versioned_docs/version-v28/02-guide/04-blog.md new file mode 100644 index 0000000000..df8b1e6522 --- /dev/null +++ b/docs/versioned_docs/version-v28/02-guide/04-blog.md @@ -0,0 +1,403 @@ +--- +description: Explore the essentials of module development while creating a dynamic blogging platform on your blockchain, where users can seamlessly submit and access blog posts, gaining practical experience in decentralized application functionalities. +title: Blog tutorial +--- + +# Build a Blog on a Blockchain with Ignite CLI + +## Introduction + +This tutorial guides you through creating a blog application as a Cosmos SDK blockchain using Ignite CLI. You'll learn how to set up types, messages, queries, and write logic for creating, reading, updating, and deleting blog posts. + +## Creating the Blog Blockchain + +1. **Initialize the Blockchain:** + +```bash +ignite scaffold chain blog +cd blog +``` + +2. **Define the Post Type:** + +```bash +ignite scaffold type post title body creator id:uint +``` +This step creates a Post type with title (string), body (string), creator (string), and id (unsigned integer) fields. + +## Implementing CRUD operations + +**Creating Posts** + +1. **Scaffold Create Message** + +```bash +ignite scaffold message create-post title body --response id:uint +``` + +This message allows users to create posts with a title and body. + +2. **Append Posts to the Store:** + +Create the file `x/blog/keeper/post.go`. + +Implement `AppendPost` and the following functions in `x/blog/keeper/post.go` to add posts to the store. + +```go title="x/blog/keeper/post.go" +package keeper + +import ( + "encoding/binary" + + "cosmossdk.io/store/prefix" + "github.com/cosmos/cosmos-sdk/runtime" + sdk "github.com/cosmos/cosmos-sdk/types" + + "blog/x/blog/types" +) + +func (k Keeper) AppendPost(ctx sdk.Context, post types.Post) uint64 { + count := k.GetPostCount(ctx) + post.Id = count + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) + store := prefix.NewStore(storeAdapter, types.KeyPrefix(types.PostKey)) + appendedValue := k.cdc.MustMarshal(&post) + store.Set(GetPostIDBytes(post.Id), appendedValue) + k.SetPostCount(ctx, count+1) + return count +} + +func (k Keeper) GetPostCount(ctx sdk.Context) uint64 { + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) + store := prefix.NewStore(storeAdapter, []byte{}) + byteKey := types.KeyPrefix(types.PostCountKey) + bz := store.Get(byteKey) + if bz == nil { + return 0 + } + return binary.BigEndian.Uint64(bz) +} + +func GetPostIDBytes(id uint64) []byte { + bz := make([]byte, 8) + binary.BigEndian.PutUint64(bz, id) + return bz +} + +func (k Keeper) SetPostCount(ctx sdk.Context, count uint64) { + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) + store := prefix.NewStore(storeAdapter, []byte{}) + byteKey := types.KeyPrefix(types.PostCountKey) + bz := make([]byte, 8) + binary.BigEndian.PutUint64(bz, count) + store.Set(byteKey, bz) +} + +func (k Keeper) GetPost(ctx sdk.Context, id uint64) (val types.Post, found bool) { + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) + store := prefix.NewStore(storeAdapter, types.KeyPrefix(types.PostKey)) + b := store.Get(GetPostIDBytes(id)) + if b == nil { + return val, false + } + k.cdc.MustUnmarshal(b, &val) + return val, true +} +``` + +3. **Add Post key prefix:** + +Add the `PostKey` and `PostCountKey` functions to the `x/blog/types/keys.go` file: + +```go title="x/blog/types/keys.go" + // PostKey is used to uniquely identify posts within the system. + // It will be used as the beginning of the key for each post, followed bei their unique ID + PostKey = "Post/value/" + + // This key will be used to keep track of the ID of the latest post added to the store. + PostCountKey = "Post/count/" +``` + +4. **Update Create Post:** + +Update the `x/blog/keeper/msg_server_create_post.go` file with the `CreatePost` function: + +```go title="x/blog/keeper/msg_server_create_post.go" +package keeper + +import ( + "context" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "blog/x/blog/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 +} +``` + +**Updating Posts** + +1. **Scaffold Update Message:** + +```bash +ignite scaffold message update-post title body id:uint +``` + +This command allows for updating existing posts specified by their ID. + +2. **Update Logic** + +Implement `SetPost` in `x/blog/keeper/post.go` for updating posts in the store. + +```go title="x/blog/keeper/post.go" +func (k Keeper) SetPost(ctx sdk.Context, post types.Post) { + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) + store := prefix.NewStore(storeAdapter, types.KeyPrefix(types.PostKey)) + b := k.cdc.MustMarshal(&post) + store.Set(GetPostIDBytes(post.Id), b) +} +``` + +Refine the `UpdatePost` function in `x/blog/keeper/msg_server_update_post.go`. + +```go title="x/blog/keeper/msg_server_update_post.go" +package keeper + +import ( + "context" + "fmt" + + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + "blog/x/blog/types" +) + +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, errorsmod.Wrap(sdkerrors.ErrKeyNotFound, fmt.Sprintf("key %d doesn't exist", msg.Id)) + } + if msg.Creator != val.Creator { + return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "incorrect owner") + } + k.SetPost(ctx, post) + return &types.MsgUpdatePostResponse{}, nil +} +``` + +**Deleting Posts** + +1. **Scaffold Delete Message:** + +```bash +ignite scaffold message delete-post id:uint +``` + +This command enables the deletion of posts by their ID. + +2. **Delete Logic:** + +Implement RemovePost in `x/blog/keeper/post.go` to delete posts from the store. + +```go title="x/blog/keeper/post.go" +func (k Keeper) RemovePost(ctx sdk.Context, id uint64) { + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) + store := prefix.NewStore(storeAdapter, types.KeyPrefix(types.PostKey)) + store.Delete(GetPostIDBytes(id)) +} +``` + +Add the according logic to `x/blog/keeper/msg_server_delete_post`. + +```go title="x/blog/keeper/msg_server_delete_post.go" +package keeper + +import ( + "context" + "fmt" + + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + "blog/x/blog/types" +) + +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, errorsmod.Wrap(sdkerrors.ErrKeyNotFound, fmt.Sprintf("key %d doesn't exist", msg.Id)) + } + if msg.Creator != val.Creator { + return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "incorrect owner") + } + k.RemovePost(ctx, msg.Id) + return &types.MsgDeletePostResponse{}, nil +} +``` + +**Reading Posts** + +1. **Scaffold Query Messages:** + +```bash title="proto/blog/blog/query.proto" +ignite scaffold query show-post id:uint --response post:Post +ignite scaffold query list-post --response post:Post --paginated +``` + +These queries allow for retrieving a single post by ID and listing all posts with pagination. + +2. **Query Implementation:** + +Implement `ShowPost` in `x/blog/keeper/query_show_post.go`. + +```go title="x/blog/keeper/query_show_post.go" +package keeper + +import ( + "context" + + 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" + + "blog/x/blog/types" +) + +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 +} +``` + +Implement `ListPost` in `x/blog/keeper/query_list_post.go`. + +```go title="x/blog/keeper/query_list_post.go" +package keeper + +import ( + "context" + + "cosmossdk.io/store/prefix" + "github.com/cosmos/cosmos-sdk/runtime" + "github.com/cosmos/cosmos-sdk/types/query" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "blog/x/blog/types" +) + +func (k Keeper) ListPost(ctx context.Context, req *types.QueryListPostRequest) (*types.QueryListPostResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) + store := prefix.NewStore(storeAdapter, types.KeyPrefix(types.PostKey)) + + var posts []types.Post + pageRes, err := query.Paginate(store, 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 +} +``` + +3. **Proto Implementation:** + +Add a `repeated` keyword to return a list of posts in `QueryListPostResponse` and include the option +`[(gogoproto.nullable) = false]` in `QueryShowPostResponse` and `QueryListPostResponse` to generate the field without a pointer. + +```proto title="proto/blog/blog/query.proto" +message QueryShowPostResponse { + Post post = 1 [(gogoproto.nullable) = false]; +} + +message QueryListPostResponse { + // highlight-next-line + repeated Post post = 1 [(gogoproto.nullable) = false]; + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} +``` + +**Interacting with the Blog** + +1. **Create a Post:** + +```bash +blogd tx blog create-post hello world --from alice --chain-id blog +``` + +2. **View a Post:** + +```bash +blogd q blog show-post 0 +``` + +3. **List All Posts:** + +```bash +blogd q blog list-post +``` + +4. **Update a Post:** + +```bash +blogd tx blog update-post "Hello" "Cosmos" 0 --from alice --chain-id blog +``` + +5. **Delete a Post:** + +```bash +blogd tx blog delete-post 0 --from alice --chain-id blog +``` + +## Conclusion + +Congratulations on completing the Blog tutorial! You've successfully built a functional blockchain application using Ignite and Cosmos SDK. This tutorial equipped you with the skills to generate code for key blockchain operations and implement business-specific logic in a blockchain context. Continue developing your skills and expanding your blockchain applications with the next tutorials. diff --git a/docs/versioned_docs/version-v28/02-guide/04-blog/00-express.md b/docs/versioned_docs/version-v28/02-guide/04-blog/00-express.md deleted file mode 100644 index c7932c17b2..0000000000 --- a/docs/versioned_docs/version-v28/02-guide/04-blog/00-express.md +++ /dev/null @@ -1,309 +0,0 @@ ---- -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 --chain-id blog -``` - -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 --chain-id blog -``` - -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 --chain-id blog - -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 --chain-id blog -``` - -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! diff --git a/docs/versioned_docs/version-v28/02-guide/04-blog/01-intro.md b/docs/versioned_docs/version-v28/02-guide/04-blog/01-intro.md deleted file mode 100644 index c3d1807902..0000000000 --- a/docs/versioned_docs/version-v28/02-guide/04-blog/01-intro.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -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-v28/02-guide/04-blog/02-scaffolding.md b/docs/versioned_docs/version-v28/02-guide/04-blog/02-scaffolding.md deleted file mode 100644 index a5f07f02bb..0000000000 --- a/docs/versioned_docs/version-v28/02-guide/04-blog/02-scaffolding.md +++ /dev/null @@ -1,124 +0,0 @@ -# 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-v28/02-guide/04-blog/03-create.md b/docs/versioned_docs/version-v28/02-guide/04-blog/03-create.md deleted file mode 100644 index f5ef40183e..0000000000 --- a/docs/versioned_docs/version-v28/02-guide/04-blog/03-create.md +++ /dev/null @@ -1,323 +0,0 @@ -# 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" - - "cosmossdk.io/store/prefix" - "github.com/cosmos/cosmos-sdk/runtime" - 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 - storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) - store := prefix.NewStore(storeAdapter, 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 { - storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) - store := prefix.NewStore(storeAdapter, []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) { - storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) - store := prefix.NewStore(storeAdapter, []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. diff --git a/docs/versioned_docs/version-v28/02-guide/04-blog/04-update.md b/docs/versioned_docs/version-v28/02-guide/04-blog/04-update.md deleted file mode 100644 index 462837b81d..0000000000 --- a/docs/versioned_docs/version-v28/02-guide/04-blog/04-update.md +++ /dev/null @@ -1,130 +0,0 @@ -# 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) { - storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) - store := prefix.NewStore(storeAdapter, 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) { - storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) - store := prefix.NewStore(storeAdapter, 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" - - errorsmod "cosmossdk.io/errors" - 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, errorsmod.Wrap(sdkerrors.ErrKeyNotFound, fmt.Sprintf("key %d doesn't exist", msg.Id)) - } - if msg.Creator != val.Creator { - return nil, errorsmod.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. diff --git a/docs/versioned_docs/version-v28/02-guide/04-blog/05-delete.md b/docs/versioned_docs/version-v28/02-guide/04-blog/05-delete.md deleted file mode 100644 index 1fe784ec2a..0000000000 --- a/docs/versioned_docs/version-v28/02-guide/04-blog/05-delete.md +++ /dev/null @@ -1,76 +0,0 @@ -# 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) { - storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) - store := prefix.NewStore(storeAdapter, 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" - - errorsmod "cosmossdk.io/errors" - 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, errorsmod.Wrap(sdkerrors.ErrKeyNotFound, fmt.Sprintf("key %d doesn't exist", msg.Id)) - } - if msg.Creator != val.Creator { - return nil, errorsmod.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 `errorsmod.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. diff --git a/docs/versioned_docs/version-v28/02-guide/04-blog/06-show.md b/docs/versioned_docs/version-v28/02-guide/04-blog/06-show.md deleted file mode 100644 index 5101ee9a48..0000000000 --- a/docs/versioned_docs/version-v28/02-guide/04-blog/06-show.md +++ /dev/null @@ -1,81 +0,0 @@ -# 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 -``` diff --git a/docs/versioned_docs/version-v28/02-guide/04-blog/07-list.md b/docs/versioned_docs/version-v28/02-guide/04-blog/07-list.md deleted file mode 100644 index 25d1d9aa19..0000000000 --- a/docs/versioned_docs/version-v28/02-guide/04-blog/07-list.md +++ /dev/null @@ -1,94 +0,0 @@ -# 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" - - "cosmossdk.io/store/prefix" - "github.com/cosmos/cosmos-sdk/runtime" - 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(ctx context.Context, req *types.QueryListPostRequest) (*types.QueryListPostResponse, error) { - if req == nil { - return nil, status.Error(codes.InvalidArgument, "invalid request") - } - - storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) - store := prefix.NewStore(storeAdapter, types.KeyPrefix(types.PostKey)) - - var posts []types.Post - pageRes, err := query.Paginate(store, 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 creates a new store using a prefix of the `PostKey` and 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 -``` diff --git a/docs/versioned_docs/version-v28/02-guide/04-blog/08-play.md b/docs/versioned_docs/version-v28/02-guide/04-blog/08-play.md deleted file mode 100644 index e41f02678e..0000000000 --- a/docs/versioned_docs/version-v28/02-guide/04-blog/08-play.md +++ /dev/null @@ -1,97 +0,0 @@ -# Play - -## Create a blog post by Alice - -``` -blogd tx blog create-post hello world --from alice --chain-id blog -``` - -## 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 --chain-id blog -``` - -## 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 --chain-id blog -``` - -``` -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 --chain-id blog -``` - -``` -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 --chain-id blog -``` - -```yml -raw_log: 'failed to execute message; message index: 0: incorrect owner: unauthorized' -``` diff --git a/docs/versioned_docs/version-v28/02-guide/04-blog/09-summary.md b/docs/versioned_docs/version-v28/02-guide/04-blog/09-summary.md deleted file mode 100644 index c841606652..0000000000 --- a/docs/versioned_docs/version-v28/02-guide/04-blog/09-summary.md +++ /dev/null @@ -1,22 +0,0 @@ -# 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-v28/02-guide/04-blog/_category_.json b/docs/versioned_docs/version-v28/02-guide/04-blog/_category_.json deleted file mode 100644 index bfc03e31cc..0000000000 --- a/docs/versioned_docs/version-v28/02-guide/04-blog/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "Module basics: Blog", - "link": null -} \ No newline at end of file diff --git a/docs/versioned_docs/version-v28/02-guide/05-loan.md b/docs/versioned_docs/version-v28/02-guide/05-loan.md new file mode 100644 index 0000000000..b2a65f5d06 --- /dev/null +++ b/docs/versioned_docs/version-v28/02-guide/05-loan.md @@ -0,0 +1,483 @@ +# DeFi Loan + +## Introduction + +Decentralized finance (DeFi) is a rapidly growing sector that is transforming the way we think about financial instruments and provides an array of inventive financial products and services. These include lending, borrowing, spot trading, margin trading, and flash loans, all of which are available to anyone possessing an internet connection. + +A DeFi loan represents a financial contract where the borrower is granted a certain asset, like currency or digital tokens. +In return, the borrower agrees to pay an additional fee and repay the loan within a set period of time. +To secure a loan, the borrower provides collateral that the lender can claim in the event of default. + +## You Will Learn + +In this tutorial you will learn how to: + +- **Scaffold a DeFi Module:** Learn how to use Ignite CLI to scaffold the basic structure of a DeFi module tailored for loan services. +- **Implement Loan Transactions:** Walk through coding the logic for initiating, managing, and closing loans. +- **Create Custom Tokens:** Understand how to create and manage custom tokens within your DeFi ecosystem, vital for lending and borrowing mechanisms. +- **Integrate Interest Rate Models:** Dive into implementing interest rate models to calculate loan interests dynamically. +- **Ensure Security and Compliance:** Focus on security, ensure your DeFi module is resistant to common vulnerabilities by validating inputs. +- **Test and Debug:** Learn effective strategies for testing your DeFi module and debugging issues that arise during development. + +## Setup and Scaffold + +1. **Create a New Blockchain:** + +```bash +ignite scaffold chain loan --no-module && cd loan +``` + +Notice the `--no-module` flag, in the next step we make sure the `bank` dependency is included with scaffolding the module. + +2. **Create a Module:** + +Create a new "loan" module that is based on the standard Cosmos SDK `bank` module. + +```bash +ignite scaffold module loan --dep bank +``` + +3. **Define the loan Module:** + +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. + +```bash +ignite scaffold list loan amount fee collateral deadline state borrower lender --no-message +``` + +4. **Scaffold the Messages:** + +Scaffold the code for handling the messages for requesting, approving, repaying, liquidating, and cancelling loans. + +- Handling Loan Requests + +```bash +ignite scaffold message request-loan amount fee collateral deadline +``` + +- Approving and Canceling Loans + +```bash +ignite scaffold message approve-loan id:uint +``` + +```bash +ignite scaffold message cancel-loan id:uint +``` + +- Repaying and Liquidating Loans + +```bash +ignite scaffold message repay-loan id:uint +``` + +```bash +ignite scaffold message liquidate-loan id:uint +``` + +## Additional Features + +- **Extend the BankKeeper:** + +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`. +Remove the `SpendableCoins` function from the `BankKeeper`. + +Add these to the `Bankkeeper` interface. + +```go title="x/loan/types/expected_keepers.go" +package types + +import ( + "context" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// AccountKeeper defines the expected interface for the Account module. +type AccountKeeper interface { + GetAccount(context.Context, sdk.AccAddress) sdk.AccountI // only used for simulation + // Methods imported from account should be defined here +} + +// BankKeeper defines the expected interface for the Bank module. +type BankKeeper interface { + // SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins + SendCoins(ctx context.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error + SendCoinsFromAccountToModule(ctx context.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error + SendCoinsFromModuleToAccount(ctx context.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error +} + +// ParamSubspace defines the expected Subspace interface for parameters. +type ParamSubspace interface { + Get(context.Context, []byte, interface{}) + Set(context.Context, []byte, interface{}) +} +``` + +- **Implement basic Validation to ensure proper loan requests:** + +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. + +The `ValidateBasic` function plays a crucial role in maintaining the security and compliance of loan input parameters. By implementing comprehensive input validations, you enhance the security of your application. It's important to rigorously verify all user inputs to ensure they align with the established standards and rules of your platform. + +```go title="x/loan/types/message_request_loan.go" +import ( + "strconv" + + errorsmod "cosmossdk.io/errors" + 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 errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid creator address (%s)", err) + } + amount, _ := sdk.ParseCoinsNormalized(msg.Amount) + if !amount.IsValid() { + return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "amount is not a valid Coins object") + } + if amount.Empty() { + return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "amount is empty") + } + fee, _ := sdk.ParseCoinsNormalized(msg.Fee) + if !fee.IsValid() { + return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "fee is not a valid Coins object") + } + deadline, err := strconv.ParseInt(msg.Deadline, 10, 64) + if err != nil { + return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "deadline is not an integer") + } + if deadline <= 0 { + return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "deadline should be a positive integer") + } + collateral, _ := sdk.ParseCoinsNormalized(msg.Collateral) + if !collateral.IsValid() { + return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "collateral is not a valid Coins object") + } + if collateral.Empty() { + return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "collateral is empty") + } + return nil +} +``` + +## Using the Platform + +1. **As a Borrower:** + +Implement `RequestLoan` keeper method that will be called whenever a user requests a loan. `RequestLoan` creates a new loan; Set terms like amount, fee, collateral, and repayment deadline. The collateral from the borrower's account is sent to a module account, and adds the loan to the blockchain's store. + +Replace your scaffolded templates with the following code. + +```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 +} +``` + +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". + +```go title="x/loan/keeper/msg_server_cancel_loan.go" +package keeper + +import ( + "context" + + errorsmod "cosmossdk.io/errors" + 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, errorsmod.Wrapf(sdkerrors.ErrKeyNotFound, "key %d doesn't exist", msg.Id) + } + if loan.Borrower != msg.Creator { + return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "Cannot cancel: not the borrower") + } + if loan.State != "requested" { + return nil, errorsmod.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 +} +``` + +2. **As a Lender:** + +Approve loan requests and liquidate loans if borrowers fail to repay. + +```go title="x/loan/keeper/msg_server_approve_loan.go" +package keeper + +import ( + "context" + + errorsmod "cosmossdk.io/errors" + 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, errorsmod.Wrapf(sdkerrors.ErrKeyNotFound, "key %d doesn't exist", msg.Id) + } + if loan.State != "requested" { + return nil, errorsmod.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, errorsmod.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 +} +``` + +```go title="x/loan/keeper/msg_server_repay_loan.go" +package keeper + +import ( + "context" + + errorsmod "cosmossdk.io/errors" + 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, errorsmod.Wrapf(sdkerrors.ErrKeyNotFound, "key %d doesn't exist", msg.Id) + } + if loan.State != "approved" { + return nil, errorsmod.Wrapf(types.ErrWrongLoanState, "%v", loan.State) + } + lender, _ := sdk.AccAddressFromBech32(loan.Lender) + borrower, _ := sdk.AccAddressFromBech32(loan.Borrower) + if msg.Creator != loan.Borrower { + return nil, errorsmod.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 +} +``` + +```go title="x/loan/keeper/msg_server_liquidate_loan.go" +package keeper + +import ( + "context" + "strconv" + + errorsmod "cosmossdk.io/errors" + 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, errorsmod.Wrapf(sdkerrors.ErrKeyNotFound, "key %d doesn't exist", msg.Id) + } + if loan.Lender != msg.Creator { + return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "Cannot liquidate: not the lender") + } + if loan.State != "approved" { + return nil, errorsmod.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, errorsmod.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 +} +``` + +Add the custom errors `ErrWrongLoanState` and `ErrDeadline`: + +```go title="x/loan/types/errors.go" +package types + +import ( + sdkerrors "cosmossdk.io/errors" +) + +var ( + ErrWrongLoanState = sdkerrors.Register(ModuleName, 2, "wrong loan state") + ErrDeadline = sdkerrors.Register(ModuleName, 3, "deadline") +) +``` + +## Testing the Application + +- **Add Test Tokens:** + +Configure config.yml to add tokens (e.g., 10000foocoin) to test accounts. + +```bash title="config.yml" +version: 1 +accounts: + - name: alice + coins: + - 20000token + - 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 the Blockchain:** + +```bash +ignite chain serve +``` + +If everything works successful, you should see the `Blockchain is running` message in the Terminal. + +- **Request a loan:** + +In a new terminal window, 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: + +```bash +loand tx loan request-loan 1000token 100token 1000foocoin 500 --from alice --chain-id loan +``` + +- **Approve the loan:** + +```bash +loand tx loan approve-loan 0 --from bob --chain-id loan +``` + +- **Repay a loan:** + +```bash +loand tx loan repay-loan 0 --from alice --chain-id loan +``` + +- **Liquidate a loan:** + +```bash +loand tx loan request-loan 1000token 100token 1000foocoin 20 --from alice --chain-id loan -y +loand tx loan approve-loan 1 --from bob --chain-id loan -y +loand tx loan liquidate-loan 1 --from bob --chain-id loan -y +``` + +At any state in the process, use `q list loan` to see the active state of all loans. + +```bash +loand q loan list-loan +``` + +Query the blockchain for balances to confirm they have changed according to your transactions. + +```bash +loand q bank balances $(loand keys show alice -a) +``` + +## Conclusion + +This tutorial outlines the process of setting up a decentralized platform for digital asset loans using blockchain technology. By following these steps, you can create a DeFi platform that allows users to engage in secure and transparent lending and borrowing activities. diff --git a/docs/versioned_docs/version-v28/02-guide/05-loan/00-intro.md b/docs/versioned_docs/version-v28/02-guide/05-loan/00-intro.md deleted file mode 100644 index 9c3380bcd9..0000000000 --- a/docs/versioned_docs/version-v28/02-guide/05-loan/00-intro.md +++ /dev/null @@ -1,86 +0,0 @@ -# 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-v28/02-guide/05-loan/01-init.md b/docs/versioned_docs/version-v28/02-guide/05-loan/01-init.md deleted file mode 100644 index 9a979da953..0000000000 --- a/docs/versioned_docs/version-v28/02-guide/05-loan/01-init.md +++ /dev/null @@ -1,72 +0,0 @@ -# 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-v28/02-guide/05-loan/02-bank.md b/docs/versioned_docs/version-v28/02-guide/05-loan/02-bank.md deleted file mode 100644 index bdc2c726ca..0000000000 --- a/docs/versioned_docs/version-v28/02-guide/05-loan/02-bank.md +++ /dev/null @@ -1,32 +0,0 @@ -# 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 context.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error - SendCoinsFromAccountToModule(ctx context.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error - SendCoinsFromModuleToAccount(ctx context.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error - // highlight-end -} -``` diff --git a/docs/versioned_docs/version-v28/02-guide/05-loan/03-request.md b/docs/versioned_docs/version-v28/02-guide/05-loan/03-request.md deleted file mode 100644 index d540ab557c..0000000000 --- a/docs/versioned_docs/version-v28/02-guide/05-loan/03-request.md +++ /dev/null @@ -1,118 +0,0 @@ -# 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" - - errorsmod "cosmossdk.io/errors" - 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 errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid creator address (%s)", err) - } - // highlight-start - amount, _ := sdk.ParseCoinsNormalized(msg.Amount) - if !amount.IsValid() { - return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "amount is not a valid Coins object") - } - if amount.Empty() { - return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "amount is empty") - } - fee, _ := sdk.ParseCoinsNormalized(msg.Fee) - if !fee.IsValid() { - return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "fee is not a valid Coins object") - } - deadline, err := strconv.ParseInt(msg.Deadline, 10, 64) - if err != nil { - return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "deadline is not an integer") - } - if deadline <= 0 { - return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "deadline should be a positive integer") - } - collateral, _ := sdk.ParseCoinsNormalized(msg.Collateral) - if !collateral.IsValid() { - return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "collateral is not a valid Coins object") - } - if collateral.Empty() { - return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "collateral is empty") - } - // highlight-end - return nil -} -``` diff --git a/docs/versioned_docs/version-v28/02-guide/05-loan/04-approve.md b/docs/versioned_docs/version-v28/02-guide/05-loan/04-approve.md deleted file mode 100644 index d1ff0d5d5d..0000000000 --- a/docs/versioned_docs/version-v28/02-guide/05-loan/04-approve.md +++ /dev/null @@ -1,98 +0,0 @@ -# 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" - - errorsmod "cosmossdk.io/errors" - 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, errorsmod.Wrapf(sdkerrors.ErrKeyNotFound, "key %d doesn't exist", msg.Id) - } - if loan.State != "requested" { - return nil, errorsmod.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, errorsmod.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") -) -``` diff --git a/docs/versioned_docs/version-v28/02-guide/05-loan/05-repay.md b/docs/versioned_docs/version-v28/02-guide/05-loan/05-repay.md deleted file mode 100644 index 232327d349..0000000000 --- a/docs/versioned_docs/version-v28/02-guide/05-loan/05-repay.md +++ /dev/null @@ -1,98 +0,0 @@ -# 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" - - errorsmod "cosmossdk.io/errors" - 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, errorsmod.Wrapf(sdkerrors.ErrKeyNotFound, "key %d doesn't exist", msg.Id) - } - if loan.State != "approved" { - return nil, errorsmod.Wrapf(types.ErrWrongLoanState, "%v", loan.State) - } - lender, _ := sdk.AccAddressFromBech32(loan.Lender) - borrower, _ := sdk.AccAddressFromBech32(loan.Borrower) - if msg.Creator != loan.Borrower { - return nil, errorsmod.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-v28/02-guide/05-loan/06-liquidate.md b/docs/versioned_docs/version-v28/02-guide/05-loan/06-liquidate.md deleted file mode 100644 index 419cc740b1..0000000000 --- a/docs/versioned_docs/version-v28/02-guide/05-loan/06-liquidate.md +++ /dev/null @@ -1,90 +0,0 @@ -# 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" - - errorsmod "cosmossdk.io/errors" - 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, errorsmod.Wrapf(sdkerrors.ErrKeyNotFound, "key %d doesn't exist", msg.Id) - } - if loan.Lender != msg.Creator { - return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "Cannot liquidate: not the lender") - } - if loan.State != "approved" { - return nil, errorsmod.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, errorsmod.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 `errorsmod.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 `errorsmod.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 `errorsmod.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 `errorsmod.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-v28/02-guide/05-loan/07-cancel.md b/docs/versioned_docs/version-v28/02-guide/05-loan/07-cancel.md deleted file mode 100644 index 9415fe6ada..0000000000 --- a/docs/versioned_docs/version-v28/02-guide/05-loan/07-cancel.md +++ /dev/null @@ -1,74 +0,0 @@ -# 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" - - errorsmod "cosmossdk.io/errors" - 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, errorsmod.Wrapf(sdkerrors.ErrKeyNotFound, "key %d doesn't exist", msg.Id) - } - if loan.Borrower != msg.Creator { - return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "Cannot cancel: not the borrower") - } - if loan.State != "requested" { - return nil, errorsmod.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 `errorsmod.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 -`errorsmod.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 `errorsmod.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. diff --git a/docs/versioned_docs/version-v28/02-guide/05-loan/08-play.md b/docs/versioned_docs/version-v28/02-guide/05-loan/08-play.md deleted file mode 100644 index 40ab5aabf7..0000000000 --- a/docs/versioned_docs/version-v28/02-guide/05-loan/08-play.md +++ /dev/null @@ -1,318 +0,0 @@ -# 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 --chain-id loan -``` - -``` -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 --chain-id loan -``` - -``` -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 --chain-id loan -``` - -``` -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 --chain-id loan -``` - -``` -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 --chain-id loan -``` - -Liquidate the loan from Bob's account: - -``` -loand tx loan liquidate-loan 1 --from bob --chain-id loan -``` - -``` -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-v28/02-guide/05-loan/_category_.json b/docs/versioned_docs/version-v28/02-guide/05-loan/_category_.json deleted file mode 100644 index 0a76ef93ff..0000000000 --- a/docs/versioned_docs/version-v28/02-guide/05-loan/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "Advanced Module: DeFi Loan", - "link": null -} \ No newline at end of file diff --git a/docs/versioned_docs/version-v28/02-guide/06-tokenfactory/01-tokenfactory.md b/docs/versioned_docs/version-v28/02-guide/06-tokenfactory/01-tokenfactory.md index 70c1865bf9..046076e761 100644 --- a/docs/versioned_docs/version-v28/02-guide/06-tokenfactory/01-tokenfactory.md +++ b/docs/versioned_docs/version-v28/02-guide/06-tokenfactory/01-tokenfactory.md @@ -335,7 +335,7 @@ type BankKeeper interface { } ``` -### Commiting Your Changes +### Committing Your Changes Regular commits are vital for tracking progress and ensuring a stable rollback point if needed. After implementing these changes, use the following commands to commit: diff --git a/docs/versioned_docs/version-v28/04-network/04-validator.md b/docs/versioned_docs/version-v28/04-network/04-validator.md index 738dfb2777..4d3199bdc6 100644 --- a/docs/versioned_docs/version-v28/04-network/04-validator.md +++ b/docs/versioned_docs/version-v28/04-network/04-validator.md @@ -28,7 +28,7 @@ Launch Id Chain Id Source Phase ``` - `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 +- `Chain ID` represents the identifier 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: diff --git a/docs/versioned_docs/version-v28/06-migration/v0.28.0.md b/docs/versioned_docs/version-v28/06-migration/v0.28.0.md deleted file mode 100644 index 5484d9d482..0000000000 --- a/docs/versioned_docs/version-v28/06-migration/v0.28.0.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -sidebar_position: 990 -title: v0.28.0 -description: For chains that were scaffolded with Ignite CLI versions lower than v0.28.0. changes are required to use Ignite CLI v0.28.0. ---- - -## Upgrading legacy plugins configuration files - -Ignite `v0.28.0` changes the plugin system which is now called Ignite Apps. This version includes changes -to the CLI command names and the plugin configuration file. - -The plugins configuration file is now called `igniteapps.yml` and the "plugins" section is now called "apps". - -The global plugins directory is now `$HOME/.ignite/apps` instead `$HOME/.ignite/plugins`. - -Updates can be automatically applied by running `ignite doctor` in your blockchain application directory. -Running the command outside your blockchain application directory will only update the global plugins. diff --git a/docs/versioned_docs/version-v28/06-migration/v28.0.0.md b/docs/versioned_docs/version-v28/06-migration/v28.0.0.md new file mode 100644 index 0000000000..9c618fefd8 --- /dev/null +++ b/docs/versioned_docs/version-v28/06-migration/v28.0.0.md @@ -0,0 +1,124 @@ +--- +sidebar_position: 990 +title: v28.0.0 +description: For chains that were scaffolded with Ignite CLI versions lower than v28.0.0 changes are required to use Ignite CLI v28.0.0 +--- + +## **Upgrade to v28.0.0 - New Versioning Scheme in Ignite** + +With the latest update, Ignite has transitioned its versioning format from a leading-zero release system to a full number release system. This change marks a significant shift in how we communicate updates and stability in our software. Where the previous version was denoted as v0.27.0, it will now be upgraded to v28.0.0. + +This new versioning approach enhances our version control by clearly indicating major, minor, and patch releases. +From now on first number indicates a major release with breaking API changes, second number indicates minor release that might include new features while the last number is typically focused on bug fixes and minor improvements. +[Learn more about semantic versioning](https://semver.org/). + +## **Plugins are now called Apps. Upgrade Configuration Files** + +Ignite `v28.0.0` changes the plugin system which is now called Ignite Apps. This version includes changes +to the CLI command names and the plugin configuration file. + +The plugins configuration file is now called `igniteapps.yml` and "plugins" are now called "apps". + +The plugins configuration home directory is now `$HOME/.ignite/apps` instead `$HOME/.ignite/plugins`. + +Updates can be automatically applied by running `ignite doctor` in your blockchain application directory. +Running the command outside your blockchain application directory will only update the global plugins. + +## **Ignite and Cosmos SDK Upgrade Guide: From Ignite v0.27.0 to v28.0.0 and Cosmos SDK v0.47 to v0.50** + +### **Introduction** + +This guide provides a step-by-step process for developers to upgrade their applications from Ignite version 0.27.0 to 28.0.0, along with an upgrade in the Cosmos SDK from version 0.47 to v0.50. It covers essential changes, new features, and adjustments required for a smooth transition. + +### **Prerequisites** + +- Backup your current project. +- Ensure you have Ignite v0.27.0 and Cosmos SDK v0.47 installed. +- Basic familiarity with command line operations and the existing project structure. + +### **Step 1: Update Ignite CLI to Version 28.0.0** + +- **Command**: Run **`curl https://get.ignite.com/cli@v28.0.0 | bash`** in your terminal. +- **Note**: This command updates the Ignite CLI to the latest version. Ensure you have the necessary permissions to execute it. + +### **Step 2: Update Scaffold Chain Command** + +- **Old Command**: **`ignite scaffold chain github.com/alice/blog`** +- **New Command**: **`ignite scaffold chain blog`** +- **Explanation**: The command format has been simplified in the new version for ease of use. + +### **Step 3: Docker Version Upgrades** + +- **Action**: Upgrade the Ignite version for the Docker container to match the CLI version. +- **Note**: Ensure Docker compatibility with the new Ignite CLI version. + +### **Step 4: Change in Module Path** + +- **Old Path**: **`x/blog/module.go`** +- **New Path**: **`x/blog/module/module.go`** +- **Explanation**: The module path structure has been updated for better organization. + +### **Step 5: Frontend Scaffolding Options** + +- **Action**: Choose between Vue, React, Go, or TypeScript for frontend scaffolding. +- **Commands**: + - **`ignite scaffold react`** + - **`ignite scaffold vue`** +- **Note**: Vue is no longer the default option for frontend scaffolding. + +### **Step 6: Update Scaffold Message for CreatePost Command** + +- **Action**: Review and update the output for the scaffolded createPost command as per the new format. + +### **Step 7: AutoCLI Path Change** + +- **Old Path**: **`x/blog/client/cli/tx_create_post.go`** +- **New Path**: **`x/blog/module/autocli.go`** +- **Explanation**: AutoCLI is now integrated at a different path to streamline command-line interactions. + +### **Step 8: Adjustment in Stored Game** + +- **Old Code**: + + ```go + store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.PostKey)) + + ``` + +- **New Code**: + + ```go + storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) + store := prefix.NewStore(storeAdapter, types.KeyPrefix(types.PostKey)) + + ``` + +- **Explanation**: The way the KVStore is accessed has changed, requiring an update in the code for stored games. + +### **Step 9: Chain-ID Requirements in CLI Transaction Commands** + +- **Action**: Add **`-chain-id`** flag to CLI transaction commands. +- **Example**: + - **Old Command**: **`blogd tx blog create-post 'Hello, World!' 'This is a blog post' --from alice`** + - **New Command**: **`blogd tx blog create-post 'Hello, World!' 'This is a blog post' --from alice --chain-id blog`** +- **Explanation**: The **`chain-id`** flag is now required for transaction commands for identification purposes. + +### **Troubleshooting Common Issues** + +- **Dependency Conflicts**: Ensure compatibility of all dependencies with Ignite v28.0.0 and Cosmos SDK v0.50. +- **Docker Image Compatibility**: Align Docker image versions with the CLI for seamless operations. +- **Frontend Scaffolding**: For older projects, ensure correct scaffolding as per the new commands. +- **AutoCLI Integration**: Address discrepancies due to the new AutoCLI integration path. + +### **Additional Resources** + +- [Ignite Documentation](https://docs.ignite.com/) +- [Cosmos SDK Release Notes](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.50.1) + +### **Feedback** + +We value your feedback on this guide. Please share your experiences and suggestions for improvements. + +### **Updates Log** + +- **[01/15/24]**: Guide created for Ignite v28.0.0 and Cosmos SDK v0.50.1 \ No newline at end of file diff --git a/docs/versioned_docs/version-v28/08-references/01-cli.md b/docs/versioned_docs/version-v28/08-references/01-cli.md index 56c10341a4..7e5ab3e21a 100644 --- a/docs/versioned_docs/version-v28/08-references/01-cli.md +++ b/docs/versioned_docs/version-v28/08-references/01-cli.md @@ -31,7 +31,7 @@ To get started, create a blockchain: * [ignite account](#ignite-account) - Create, delete, and show Ignite accounts * [ignite app](#ignite-app) - Create and manage Ignite Apps * [ignite chain](#ignite-chain) - Build, init and start a blockchain node -* [ignite completion](#ignite-completion) - Generate the autocompletion script for the specified shell +* [ignite completion](#ignite-completion) - Generates shell completion script. * [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 @@ -578,7 +578,7 @@ ignite chain build [flags] **Options** ``` - --build.tags strings parameters to build the chain binary (default [app_v1]) + --build.tags strings parameters to build the chain binary --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 @@ -766,7 +766,7 @@ ignite chain init [flags] **Options** ``` - --build.tags strings parameters to build the chain binary (default [app_v1]) + --build.tags strings parameters to build the chain binary --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 @@ -835,7 +835,7 @@ ignite chain serve [flags] **Options** ``` - --build.tags strings parameters to build the chain binary (default [app_v1]) + --build.tags strings parameters to build the chain binary --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 @@ -909,184 +909,21 @@ ignite chain simulate [flags] ## 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) - -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. - +Generates shell completion script. ``` -ignite completion zsh [flags] +ignite completion [bash|zsh|fish|powershell] [flags] ``` **Options** ``` - -h, --help help for zsh - --no-descriptions disable completion descriptions + -h, --help help for completion ``` **SEE ALSO** -* [ignite completion](#ignite-completion) - Generate the autocompletion script for the specified shell +* [ignite](#ignite) - Ignite CLI offers everything you need to scaffold, test, build, and launch your blockchain ## ignite docs @@ -3225,6 +3062,7 @@ ignite scaffold chain [name] [flags] --address-prefix string account address prefix (default "cosmos") --clear-cache clear the build cache (advanced) -h, --help help for chain + --minimal create a minimal blockchain (with the minimum required Cosmos SDK modules) --no-module create a project without a default module --params strings add default module parameters -p, --path string create a project in a specific path diff --git a/docs/versioned_docs/version-v28/08-references/02-config.md b/docs/versioned_docs/version-v28/08-references/02-config.md index 1eb4aa7f8f..34347b9565 100644 --- a/docs/versioned_docs/version-v28/08-references/02-config.md +++ b/docs/versioned_docs/version-v28/08-references/02-config.md @@ -12,6 +12,26 @@ 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. +## Validation + +Ignite uses the `validation` field to determine the kind of validation +of your blockchain. There are currently two supported kinds of validation: + +- `sovereign` which is the standard kind of validation where your blockchain + has its own validator set. This is the default value when this field is not + in the config file. +- `consumer` indicates your blockchain is a consumer chain, in the sense of + Replicated Security. That means it doesn't have a validator set, but + inherits the one of a provider chain. + +While the `sovereign` chain is the default validation when you run the `ignite scaffold +chain`, to scaffold a consumer chain, you have to run `ignite scaffold chain +--consumer`. + +This field is, at this time of writing, only used by Ignite at the genesis +generation step, because the genesis of a sovereign chain and a consumer chain +are different. + ## Accounts A list of user accounts created during genesis of the blockchain. diff --git a/docs/versioned_docs/version-v28/apps/02-developing-apps.md b/docs/versioned_docs/version-v28/apps/02-developing-apps.md index 57d1fdd58b..935eddaaf8 100644 --- a/docs/versioned_docs/version-v28/apps/02-developing-apps.md +++ b/docs/versioned_docs/version-v28/apps/02-developing-apps.md @@ -97,7 +97,7 @@ message Manifest { // Enables sharing a single app server across all running instances of an Ignite App. // Useful if an app adds or extends long running commands. // - // Example: if an app defines a hook on `ignite chain serve`, a server is instanciated + // Example: if an app defines a hook on `ignite chain serve`, a server is instantiated // when the command is run. Now if you want to interact with that instance // from commands defined in that app, you need to enable shared host, or else the // commands will just instantiate separate app servers. @@ -143,7 +143,7 @@ func (app) Manifest(context.Context) (*plugin.Manifest, error) { Use: "oracle [name]", Short: "Scaffold an oracle module", Long: "Long description goes here...", - // Optionnal flags is required + // Optional flags is required Flags: []*plugin.Flag{ {Name: "source", Type: plugin.FlagTypeString, Usage: "the oracle source"}, }, diff --git a/docs/versions.json b/docs/versions.json index ed76f948be..89cbd829e3 100644 --- a/docs/versions.json +++ b/docs/versions.json @@ -3,4 +3,4 @@ "v0.27", "v0.26", "v0.25" -] \ No newline at end of file +]