Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ignite
GitHub Repository: ignite/cli
Path: blob/main/ignite/cmd/scaffold_module.go
1005 views
1
package ignitecmd
2
3
import (
4
"bytes"
5
"fmt"
6
"regexp"
7
"strings"
8
9
"github.com/spf13/cobra"
10
11
"github.com/ignite/cli/v29/ignite/pkg/cliui"
12
"github.com/ignite/cli/v29/ignite/pkg/errors"
13
"github.com/ignite/cli/v29/ignite/pkg/validation"
14
"github.com/ignite/cli/v29/ignite/pkg/xgenny"
15
"github.com/ignite/cli/v29/ignite/services/scaffolder"
16
modulecreate "github.com/ignite/cli/v29/ignite/templates/module/create"
17
)
18
19
// moduleNameKeeperAlias is a map of well known module names that have a different keeper name than the usual <module-name>Keeper.
20
var moduleNameKeeperAlias = map[string]string{}
21
22
const (
23
flagDep = "dep"
24
flagIBC = "ibc"
25
flagParams = "params"
26
flagModuleConfigs = "module-configs"
27
flagIBCOrdering = "ordering"
28
flagRequireRegistration = "require-registration"
29
)
30
31
// NewScaffoldModule returns the command to scaffold a Cosmos SDK module.
32
func NewScaffoldModule() *cobra.Command {
33
c := &cobra.Command{
34
Use: "module [name]",
35
Short: "Custom Cosmos SDK module",
36
Long: `Scaffold a new Cosmos SDK module.
37
38
Cosmos SDK is a modular framework and each independent piece of functionality is
39
implemented in a separate module. By default your blockchain imports a set of
40
standard Cosmos SDK modules. To implement custom functionality of your
41
blockchain, scaffold a module and implement the logic of your application.
42
43
This command does the following:
44
45
* Creates a directory with module's protocol buffer files in "proto/"
46
* Creates a directory with module's boilerplate Go code in "x/"
47
* Imports the newly created module by modifying "app/app.go"
48
49
This command will proceed with module scaffolding even if "app/app.go" doesn't
50
have the required default placeholders. If the placeholders are missing, you
51
will need to modify "app/app.go" manually to import the module. If you want the
52
command to fail if it can't import the module, use the "--require-registration"
53
flag.
54
55
To scaffold an IBC-enabled module use the "--ibc" flag. An IBC-enabled module is
56
like a regular module with the addition of IBC-specific logic and placeholders
57
to scaffold IBC packets with "ignite scaffold packet".
58
59
A module can depend on one or more other modules and import their keeper
60
methods. To scaffold a module with a dependency use the "--dep" flag
61
62
For example, your new custom module "foo" might have functionality that requires
63
sending tokens between accounts. The method for sending tokens is a defined in
64
the "bank"'s module keeper. You can scaffold a "foo" module with the dependency
65
on "bank" with the following command:
66
67
ignite scaffold module foo --dep bank
68
69
You can then define which methods you want to import from the "bank" keeper in
70
"expected_keepers.go".
71
72
You can also scaffold a module with a list of dependencies that can include both
73
standard and custom modules (provided they exist):
74
75
ignite scaffold module bar --dep foo,mint,account,FeeGrant
76
77
Note: the "--dep" flag doesn't install third-party modules into your
78
application, it just generates extra code that specifies which existing modules
79
your new custom module depends on.
80
81
A Cosmos SDK module can have parameters (or "params"). Params are values that
82
can be set at the genesis of the blockchain and can be modified while the
83
blockchain is running. An example of a param is "Inflation rate change" of the
84
"mint" module. A module can be scaffolded with params using the "--params" flag
85
that accepts a list of param names. By default params are of type "string", but
86
you can specify a type for each param. For example:
87
88
ignite scaffold module foo --params baz:uint,bar:bool
89
90
Refer to Cosmos SDK documentation to learn more about modules, dependencies and
91
params.
92
`,
93
Args: cobra.ExactArgs(1),
94
PreRunE: migrationPreRunHandler,
95
RunE: scaffoldModuleHandler,
96
}
97
98
flagSetPath(c)
99
flagSetClearCache(c)
100
101
c.Flags().AddFlagSet(flagSetYes())
102
c.Flags().StringSlice(flagDep, []string{}, "add a dependency on another module")
103
c.Flags().Bool(flagIBC, false, "add IBC functionality")
104
c.Flags().String(flagIBCOrdering, "none", "channel ordering of the IBC module [none|ordered|unordered]")
105
c.Flags().Bool(flagRequireRegistration, false, "fail if module can't be registered")
106
c.Flags().StringSlice(flagParams, []string{}, "add module parameters")
107
c.Flags().StringSlice(flagModuleConfigs, []string{}, "add module configs")
108
109
return c
110
}
111
112
func scaffoldModuleHandler(cmd *cobra.Command, args []string) error {
113
var (
114
name = args[0]
115
appPath = flagGetPath(cmd)
116
)
117
118
session := cliui.New(
119
cliui.StartSpinnerWithText(statusScaffolding),
120
cliui.WithoutUserInteraction(getYes(cmd)),
121
)
122
defer session.End()
123
124
cfg, _, err := getChainConfig(cmd)
125
if err != nil {
126
return err
127
}
128
129
ibcModule, _ := cmd.Flags().GetBool(flagIBC)
130
ibcOrdering, _ := cmd.Flags().GetString(flagIBCOrdering)
131
requireRegistration, _ := cmd.Flags().GetBool(flagRequireRegistration)
132
params, _ := cmd.Flags().GetStringSlice(flagParams)
133
134
moduleConfigs, err := cmd.Flags().GetStringSlice(flagModuleConfigs)
135
if err != nil {
136
return err
137
}
138
139
cacheStorage, err := newCache(cmd)
140
if err != nil {
141
return err
142
}
143
144
options := []scaffolder.ModuleCreationOption{
145
scaffolder.WithParams(params),
146
scaffolder.WithModuleConfigs(moduleConfigs),
147
}
148
149
// Check if the module must be an IBC module
150
if ibcModule {
151
options = append(options, scaffolder.WithIBCChannelOrdering(ibcOrdering), scaffolder.WithIBC())
152
}
153
154
// Get module dependencies
155
dependencies, _ := cmd.Flags().GetStringSlice(flagDep)
156
if len(dependencies) > 0 {
157
var deps []modulecreate.Dependency
158
159
isValid := regexp.MustCompile(`^[a-zA-Z]+$`).MatchString
160
161
for _, name := range dependencies {
162
if !isValid(name) {
163
return errors.Errorf("invalid module dependency name format '%s'", name)
164
}
165
166
if alias, ok := moduleNameKeeperAlias[strings.ToLower(name)]; ok {
167
name = alias
168
}
169
170
deps = append(deps, modulecreate.NewDependency(name))
171
}
172
173
options = append(options, scaffolder.WithDependencies(deps))
174
}
175
176
var msg bytes.Buffer
177
fmt.Fprintf(&msg, "\nšŸŽ‰ Module created %s.\n\n", name)
178
179
sc, err := scaffolder.New(cmd.Context(), appPath, cfg.Build.Proto.Path)
180
if err != nil {
181
return err
182
}
183
184
if err := sc.CreateModule(name, options...); err != nil {
185
var validationErr validation.Error
186
if !requireRegistration && errors.As(err, &validationErr) {
187
fmt.Fprintf(&msg, "Can't register module '%s'.\n", name)
188
fmt.Fprintln(&msg, validationErr.ValidationInfo())
189
} else {
190
return err
191
}
192
}
193
194
sm, err := sc.ApplyModifications(xgenny.ApplyPreRun(scaffolder.AskOverwriteFiles(session)))
195
if err != nil {
196
return err
197
}
198
199
if err := sc.PostScaffold(cmd.Context(), cacheStorage, false); err != nil {
200
return err
201
}
202
203
modificationsStr, err := sm.String()
204
if err != nil {
205
return err
206
}
207
208
session.Println(modificationsStr)
209
210
return session.Print(msg.String())
211
}
212
213