1"use strict";(globalThis.webpackChunkmy_website=globalThis.webpackChunkmy_website||[]).push([[2135],{15680(e,n,r){r.d(n,{xA:()=>d,yg:()=>g});var t=r(96540);function i(e,n,r){return n in e?Object.defineProperty(e,n,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[n]=r,e}function o(e,n){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var t=Object.getOwnPropertySymbols(e);n&&(t=t.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),r.push.apply(r,t)}return r}function a(e){for(var n=1;n<arguments.length;n++){var r=null!=arguments[n]?arguments[n]:{};n%2?o(Object(r),!0).forEach((function(n){i(e,n,r[n])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):o(Object(r)).forEach((function(n){Object.defineProperty(e,n,Object.getOwnPropertyDescriptor(r,n))}))}return e}function l(e,n){if(null==e)return{};var r,t,i=function(e,n){if(null==e)return{};var r,t,i={},o=Object.keys(e);for(t=0;t<o.length;t++)r=o[t],n.indexOf(r)>=0||(i[r]=e[r]);return i}(e,n);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(t=0;t<o.length;t++)r=o[t],n.indexOf(r)>=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(i[r]=e[r])}return i}var s=t.createContext({}),c=function(e){var n=t.useContext(s),r=n;return e&&(r="function"==typeof e?e(n):a(a({},n),e)),r},d=function(e){var n=c(e.components);return t.createElement(s.Provider,{value:n},e.children)},u={inlineCode:"code",wrapper:function(e){var n=e.children;return t.createElement(t.Fragment,{},n)}},p=t.forwardRef((function(e,n){var r=e.components,i=e.mdxType,o=e.originalType,s=e.parentName,d=l(e,["components","mdxType","originalType","parentName"]),p=c(r),g=i,m=p["".concat(s,".").concat(g)]||p[g]||u[g]||o;return r?t.createElement(m,a(a({ref:n},d),{},{components:r})):t.createElement(m,a({ref:n},d))}));function g(e,n){var r=arguments,i=n&&n.mdxType;if("string"==typeof e||i){var o=r.length,a=new Array(o);a[0]=p;var l={};for(var s in n)hasOwnProperty.call(n,s)&&(l[s]=n[s]);l.originalType=e,l.mdxType="string"==typeof e?e:i,a[1]=l;for(var c=2;c<o;c++)a[c]=r[c];return t.createElement.apply(null,a)}return t.createElement.apply(null,r)}p.displayName="MDXCreateElement"},88985(e,n,r){r.r(n),r.d(n,{assets:()=>s,contentTitle:()=>a,default:()=>u,frontMatter:()=>o,metadata:()=>l,toc:()=>c});var t=r(58168),i=(r(96540),r(15680));const o={description:"Learn how Cosmos SDK modules manage state with collections",title:"State Management"},a="State Management in Modules",l={unversionedId:"guide/state",id:"version-v29/guide/state",title:"State Management",description:"Learn how Cosmos SDK modules manage state with collections",source:"@site/versioned_docs/version-v29/02-guide/08-state.md",sourceDirName:"02-guide",slug:"/guide/state",permalink:"/v29/guide/state",draft:!1,tags:[],version:"v29",sidebarPosition:8,frontMatter:{description:"Learn how Cosmos SDK modules manage state with collections",title:"State Management"},sidebar:"tutorialSidebar",previous:{title:"Inter-Blockchain Communication: Basics",permalink:"/v29/guide/ibc"},next:{title:"Chain simulation",permalink:"/v29/guide/simapp"}},s={},c=[{value:"Collections Package",id:"collections-package",level:2},{value:"Key Features of Collections",id:"key-features-of-collections",level:3},{value:"Understand keeper field",id:"understand-keeper-field",level:2},{value:"Common State Operations",id:"common-state-operations",level:2},{value:"Reading State",id:"reading-state",level:3},{value:"Writing State",id:"writing-state",level:3},{value:"Checking Existence",id:"checking-existence",level:3},{value:"Removing State",id:"removing-state",level:3},{value:"Implementing Business Logic in Messages",id:"implementing-business-logic-in-messages",level:2},{value:"Implementing Queries",id:"implementing-queries",level:2},{value:"Error Handling with Collections",id:"error-handling-with-collections",level:2},{value:"Iterating Over Collections",id:"iterating-over-collections",level:2},{value:"Conclusion",id:"conclusion",level:2}],d={toc:c};
1function u(e){let{components:n,...r}=e;return(0,i.yg)("wrapper",(0,t.A)({},d,r,{components:n,mdxType:"MDXLayout"}),(0,i.yg)("h1",{id:"state-management-in-modules"},"State Management in Modules"),(0,i.yg)("p",null,"In blockchain applications, state refers to the current data stored on the blockchain at a specific point in time. Handling state is usually the core of any blockchain application. The Cosmos SDK provides powerful tools for state management, with the ",(0,i.yg)("inlineCode",{parentName:"p"},"collections")," package being the recommended approach for modern applications."),(0,i.yg)("h2",{id:"collections-package"},"Collections Package"),(0,i.yg)("p",null,"IGNITE\xae scaffolds using the ",(0,i.yg)("a",{parentName:"p",href:"https://pkg.go.dev/cosmossdk.io/collections"},(0,i.yg)("inlineCode",{parentName:"a"},"collections"))," package for module code. This package provides a type-safe and efficient way to set and query values from the module store."),(0,i.yg)("h3",{id:"key-features-of-collections"},"Key Features of Collections"),(0,i.yg)("ul",null,(0,i.yg)("li",{parentName:"ul"},(0,i.yg)("strong",{parentName:"li"},"Type Safety"),": Collections are type-safe, reducing the risk of runtime errors."),(0,i.yg)("li",{parentName:"ul"},(0,i.yg)("strong",{parentName:"li"},"Simplified API"),": Easy-to-use methods for common operations like Get, Set, and Has."),(0,i.yg)("li",{parentName:"ul"},(0,i.yg)("strong",{parentName:"li"},"Performance"),": Optimized for performance with minimal overhead."),(0,i.yg)("li",{parentName:"ul"},(0,i.yg)("strong",{parentName:"li"},"Integration"),": Seamlessly integrates with the Cosmos SDK ecosystem.")),(0,i.yg)("h2",{id:"understand-keeper-field"},"Understand keeper field"),(0,i.yg)("p",null,"IGNITE\xae creates all the necessary boilerplate for collections in the ",(0,i.yg)("inlineCode",{parentName:"p"},"x/<module>/keeper/keeper.go")," file. The ",(0,i.yg)("inlineCode",{parentName:"p"},"Keeper")," struct contains fields for each collection you define in your module. Each field is an instance of a collection type, such as ",(0,i.yg)("inlineCode",{parentName:"p"},"collections.Map"),", ",(0,i.yg)("inlineCode",{parentName:"p"},"collections.Item"),", or ",(0,i.yg)("inlineCode",{parentName:"p"},"collections.List"),"."),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-go"},"type Keeper struct {\n // ...\n\n Params collections.Item[Params]\n Counters collections.Map[string, uint64]\n Profiles collections.Map[sdk.AccAddress, Profile]\n}\n")),(0,i.yg)("h2",{id:"common-state-operations"},"Common State Operations"),(0,i.yg)("h3",{id:"reading-state"},"Reading State"),(0,i.yg)("p",null,"To read values from state, use the ",(0,i.yg)("inlineCode",{parentName:"p"},"Get")," method:"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-go"},'// getting a single item\nparams, err := k.Params.Get(ctx)\nif err != nil {\n // handle error\n // collections.ErrNotFound is returned when an item doesn\'t exist\n}\n\n// getting a map entry\ncounter, err := k.Counters.Get(ctx, "my-counter")\nif err != nil {\n // handle error\n}\n')),(0,i.yg)("h3",{id:"writing-state"},"Writing State"),(0,i.yg)("p",null,"To write values to state, use the ",(0,i.yg)("inlineCode",{parentName:"p"},"Set")," method:"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-go"},'// setting a single item\nerr := k.Params.Set(ctx, params)\nif err != nil {\n // handle error\n}\n\n// setting a map entry\nerr = k.Counters.Set(ctx, "my-counter", 42)\nif err != nil {\n // handle error\n}\n')),(0,i.yg)("h3",{id:"checking-existence"},"Checking Existence"),(0,i.yg)("p",null,"Use the ",(0,i.yg)("inlineCode",{parentName:"p"},"Has")," method to check if a value exists without retrieving it:"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-go"},'exists, err := k.Counters.Has(ctx, "my-counter")\nif err != nil {\n // handle error\n}\nif exists {\n // value exists\n}\n')),(0,i.yg)("h3",{id:"removing-state"},"Removing State"),(0,i.yg)("p",null,"To remove values from state, use the ",(0,i.yg)("inlineCode",{parentName:"p"},"Remove")," method:"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-go"},'err := k.Counters.Remove(ctx, "my-counter")\nif err != nil {\n // handle error\n}\n')),(0,i.yg)("h2",{id:"implementing-business-logic-in-messages"},"Implementing Business Logic in Messages"),(0,i.yg)("p",null,"Messages in Cosmos SDK modules modify state based on user transactions. Here's how to implement business logic in a message handler using collections:"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-go"},'func (k msgServer) CreateProfile(ctx context.Context, msg *types.MsgCreateProfile) (*types.MsgCreateProfileResponse, error) {\n // validate message\n if err := msg.ValidateBasic(); err != nil {\n return nil, err\n }\n\n // parse sender address\n senderBz, err := k.addressCodec.StringToBytes(msg.Creator)\n if err != nil {\n return nil, err\n }\n sender := sdk.AccAddress(senderBz)\n\n // check if profile already exists\n exists, err := k.Profiles.Has(ctx, sender)\n if err != nil {\n return nil, err\n }\n if exists {\n return nil, sdkerrors.Wrap(types.ErrProfileExists, "profile already exists")\n }\n\n // create new profile\n sdkCtx := sdk.UnwrapSDKContext(ctx)\n profile := types.Profile{\n Name: msg.Name,\n Bio: msg.Bio,\n CreatedAt: sdkCtx.BlockTime().Unix(),\n }\n\n // store the profile\n err = k.Profiles.Set(ctx, sender, profile)\n if err != nil {\n return nil, err\n }\n\n // increment profile counter\n counter, err := k.Counters.Get(ctx, "profiles")\n if err != nil && !errors.Is(err, collections.ErrNotFound) {\n return nil, err\n }\n // set the counter (adding 1)\n err = k.Counters.Set(ctx, "profiles", counter+1)\n if err != nil {\n return nil, err\n }\n\n return &types.MsgCreateProfileResponse{}, nil\n}\n')),(0,i.yg)("h2",{id:"implementing-queries"},"Implementing Queries"),(0,i.yg)("p",null,"Queries allow users to read state without modifying it. Here's how to implement a query handler using collections:"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-go"},'func (q queryServer) GetProfile(ctx context.Context, req *types.QueryGetProfileRequest) (*types.QueryGetProfileResponse, error) {\n if req == nil {\n return nil, status.Error(codes.InvalidArgument, "invalid request")\n }\n\n // parse address\n addressBz, err := k.addressCodec.StringToBytes(req.Address)\n if err != nil {\n return nil, status.Error(codes.InvalidArgument, "invalid address")\n }
1\n address := sdk.AccAddress(addressBz)\n\n // get profile\n profile, err := q.k.Profiles.Get(ctx, address)\n if err != nil {\n if errors.Is(err, collections.ErrNotFound) {\n return nil, status.Error(codes.NotFound, "profile not found")\n }\n return nil, status.Error(codes.Internal, "internal error")\n }\n\n return &types.QueryGetProfileResponse{Profile: profile}, nil\n}\n')),(0,i.yg)("h2",{id:"error-handling-with-collections"},"Error Handling with Collections"),(0,i.yg)("p",null,"When working with collections, proper error handling is essential:"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-go"},'// example from a query function\nparams, err := q.k.Params.Get(ctx)\nif err != nil && !errors.Is(err, collections.ErrNotFound) {\n return nil, status.Error(codes.Internal, "internal error")\n}\n')),(0,i.yg)("p",null,"In the snippet above, it uses the ",(0,i.yg)("inlineCode",{parentName:"p"},"Get")," method to get a collection item. A ",(0,i.yg)("inlineCode",{parentName:"p"},"collections.ErrNotFound")," can be a valid error when the collection is empty, whereas any other error is considered an internal error that should be handled appropriately."),(0,i.yg)("h2",{id:"iterating-over-collections"},"Iterating Over Collections"),(0,i.yg)("p",null,"Collections also support iteration:"),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre",className:"language-go"},'// iterate over all profiles\nerr := k.Profiles.Walk(ctx, nil, func(key sdk.AccAddress, value types.Profile) (bool, error) {\n // process each profile\n // return true to stop iteration, false to continue\n return false, nil\n})\nif err != nil {\n // handle error\n}\n\n// iterate over a range of counters\nstartKey := "a"\nendKey := "z"\nerr = k.Counters.Walk(ctx, collections.NewPrefixedPairRange[string, uint64](startKey, endKey), func(key string, value uint64) (bool, error) {\n // process each counter in the range\n return false, nil\n})\nif err != nil {\n // handle error\n}\n')),(0,i.yg)("h2",{id:"conclusion"},"Conclusion"),(0,i.yg)("p",null,"The ",(0,i.yg)("inlineCode",{parentName:"p"},"collections")," package provides a powerful and type-safe way to manage state in Cosmos SDK modules. By understanding how to use collections effectively, you can build robust and efficient blockchain applications that handle state transitions reliably."),(0,i.yg)("p",null,"When developing with IGNITE\xae CLI, you are already taking advantage of collections which significantly simplify the state management code and reduce the potential for errors."))}u.isMDXComponent=!0}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.