Skip to content

Commit 4b1c25a

Browse files
committed
support interactive mode for network add cmd
1 parent cf56b39 commit 4b1c25a

File tree

1 file changed

+153
-0
lines changed

1 file changed

+153
-0
lines changed

Diff for: cmd/network.go

+153
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,15 @@ package cmd
33
import (
44
"fmt"
55
"io"
6+
"net/url"
67
"os"
8+
"strconv"
79
"strings"
810

11+
"github.com/ethereum/go-ethereum/common"
912
"github.com/spf13/cobra"
1013

14+
cmdutil "github.com/tranvictor/jarvis/cmd/util"
1115
"github.com/tranvictor/jarvis/networks"
1216
"github.com/tranvictor/jarvis/util"
1317
)
@@ -17,6 +21,154 @@ var (
1721
NetworkForce bool
1822
)
1923

24+
// TODO: in this version, we only support adding a new network that works with etherscan
25+
// in next version, we will support adding a new network that works with altlayer or doesn't have a block explorer
26+
func PromptNetwork() networks.Network {
27+
name := cmdutil.PromptInputWithValidation("Please enter the name of the network", func(name string) error {
28+
if name == "" {
29+
return fmt.Errorf("name cannot be empty")
30+
}
31+
32+
if NetworkForce {
33+
return nil
34+
}
35+
36+
if _, err := networks.GetNetwork(name); err == nil {
37+
return fmt.Errorf("network with name %s already exists. If you want to replace it, use flag --force", name)
38+
}
39+
return nil
40+
})
41+
42+
alternativeNamesStr := cmdutil.PromptInput("Please enter the alternative names of the network (comma separated, don't wrap with quotes)")
43+
alternativeNames := strings.Split(alternativeNamesStr, ",")
44+
for i, name := range alternativeNames {
45+
alternativeNames[i] = strings.TrimSpace(name)
46+
}
47+
48+
chainIDStr := cmdutil.PromptInputWithValidation("Please enter the chain ID of the network", func(chainIDstr string) error {
49+
if chainIDstr == "" {
50+
return fmt.Errorf("chain ID cannot be empty")
51+
}
52+
_, err := strconv.Atoi(chainIDstr)
53+
if err != nil {
54+
return fmt.Errorf("chain ID must be a number")
55+
}
56+
return nil
57+
})
58+
chainID, _ := strconv.Atoi(chainIDStr)
59+
60+
nativeTokenSymbol := cmdutil.PromptInputWithValidation("Please enter the native token symbol of the network", func(nativeTokenSymbol string) error {
61+
if nativeTokenSymbol == "" {
62+
return fmt.Errorf("native token symbol cannot be empty")
63+
}
64+
return nil
65+
})
66+
67+
nativeTokenDecimalStr := cmdutil.PromptInputWithValidation("Please enter the native token decimal of the network", func(nativeTokenDecimal string) error {
68+
if nativeTokenDecimal == "" {
69+
return fmt.Errorf("native token decimal cannot be empty")
70+
}
71+
_, err := strconv.Atoi(nativeTokenDecimal)
72+
if err != nil {
73+
return fmt.Errorf("native token decimal must be a number")
74+
}
75+
return nil
76+
})
77+
nativeTokenDecimal, _ := strconv.Atoi(nativeTokenDecimalStr)
78+
79+
blockTimeStr := cmdutil.PromptInputWithValidation("Please enter the block time of the network in seconds", func(blockTime string) error {
80+
if blockTime == "" {
81+
return fmt.Errorf("block time cannot be empty")
82+
}
83+
_, err := strconv.Atoi(blockTime)
84+
if err != nil {
85+
return fmt.Errorf("block time must be a number")
86+
}
87+
return nil
88+
})
89+
blockTime, _ := strconv.Atoi(blockTimeStr)
90+
91+
nodeVariableName := cmdutil.PromptInputWithValidation("Please enter the node variable name of the network", func(nodeVariableName string) error {
92+
if nodeVariableName == "" {
93+
return fmt.Errorf("node variable name cannot be empty")
94+
}
95+
96+
// the input has to be in capital letters
97+
if strings.ToUpper(nodeVariableName) != nodeVariableName {
98+
return fmt.Errorf("node variable name must be in capital letters")
99+
}
100+
return nil
101+
})
102+
103+
defaultNodesStr := cmdutil.PromptInputWithValidation("Please enter the default node urls of the network (comma separated, no wrapping with quotes)", func(defaultNodes string) error {
104+
if defaultNodes == "" {
105+
return fmt.Errorf("default node urls cannot be empty")
106+
}
107+
108+
nodes := strings.Split(defaultNodes, ",")
109+
for _, node := range nodes {
110+
// check if the node is a valid url
111+
_, err := url.Parse(node)
112+
if err != nil {
113+
return fmt.Errorf("default node url %s is not a valid url", node)
114+
}
115+
}
116+
return nil
117+
})
118+
defaultNodes := make(map[string]string)
119+
for _, node := range strings.Split(defaultNodesStr, ",") {
120+
// name of the node is the domain of the url
121+
nodeURL, _ := url.Parse(strings.TrimSpace(node))
122+
defaultNodes[nodeURL.Host] = strings.TrimSpace(node)
123+
}
124+
125+
blockExplorerAPIKeyVariableName := cmdutil.PromptInputWithValidation("Please enter the block explorer API key variable name of the network", func(blockExplorerAPIKeyVariableName string) error {
126+
if blockExplorerAPIKeyVariableName == "" {
127+
return fmt.Errorf("block explorer API key variable name cannot be empty")
128+
}
129+
return nil
130+
})
131+
132+
blockExplorerAPIURL := cmdutil.PromptInputWithValidation("Please enter the block explorer API URL of the network", func(blockExplorerAPIURL string) error {
133+
if blockExplorerAPIURL == "" {
134+
return fmt.Errorf("block explorer API URL cannot be empty")
135+
}
136+
_, err := url.Parse(blockExplorerAPIURL)
137+
if err != nil {
138+
return fmt.Errorf("block explorer API URL %s is not a valid url", blockExplorerAPIURL)
139+
}
140+
return nil
141+
})
142+
143+
multiCallContractAddress := cmdutil.PromptInputWithValidation("Please enter the multi call contract address of the network", func(multiCallContractAddress string) error {
144+
if multiCallContractAddress == "" {
145+
return fmt.Errorf("multi call contract address cannot be empty")
146+
}
147+
148+
// check if the address is an ethereum address
149+
if !common.IsHexAddress(multiCallContractAddress) {
150+
return fmt.Errorf("multi call contract address %s is not a valid address", multiCallContractAddress)
151+
}
152+
return nil
153+
})
154+
155+
networkConfig := networks.GenericEtherscanNetworkConfig{
156+
Name: name,
157+
AlternativeNames: alternativeNames,
158+
ChainID: uint64(chainID),
159+
NativeTokenSymbol: nativeTokenSymbol,
160+
NativeTokenDecimal: uint64(nativeTokenDecimal),
161+
BlockTime: uint64(blockTime),
162+
NodeVariableName: nodeVariableName,
163+
DefaultNodes: defaultNodes,
164+
BlockExplorerAPIKeyVariableName: blockExplorerAPIKeyVariableName,
165+
BlockExplorerAPIURL: blockExplorerAPIURL,
166+
MultiCallContractAddress: common.HexToAddress(multiCallContractAddress),
167+
}
168+
169+
return networks.NewGenericEtherscanNetwork(networkConfig)
170+
}
171+
20172
var addNetworkCmd = &cobra.Command{
21173
Use: "add",
22174
Short: "Add a new network to the supported networks list locally",
@@ -74,6 +226,7 @@ var addNetworkCmd = &cobra.Command{
74226
}
75227
} else {
76228
// in this case, user didn't provide any config, we need to prompt user to input the network config
229+
newNetwork = PromptNetwork()
77230
}
78231

79232
allNames := []string{newNetwork.GetName()}

0 commit comments

Comments
 (0)