github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/internal/slurp/options.go (about) 1 // Copyright 2016, 2024 The TrueBlocks Authors. All rights reserved. 2 // Use of this source code is governed by a license that can 3 // be found in the LICENSE file. 4 /* 5 * Parts of this file were auto generated. Edit only those parts of 6 * the code inside of 'EXISTING_CODE' tags. 7 */ 8 9 package slurpPkg 10 11 import ( 12 // EXISTING_CODE 13 "encoding/json" 14 "io" 15 "net/http" 16 "net/url" 17 "strings" 18 19 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/internal/globals" 20 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base" 21 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/caps" 22 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/identifiers" 23 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger" 24 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/rpc" 25 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/rpc/provider" 26 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/validate" 27 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/walk" 28 // EXISTING_CODE 29 ) 30 31 // SlurpOptions provides all command options for the chifra slurp command. 32 type SlurpOptions struct { 33 Addrs []string `json:"addrs,omitempty"` // One or more addresses to slurp from Etherscan 34 Blocks []string `json:"blocks,omitempty"` // An optional range of blocks to slurp 35 BlockIds []identifiers.Identifier `json:"blockIds,omitempty"` // Block identifiers 36 Parts []string `json:"parts,omitempty"` // Which types of transactions to request 37 Appearances bool `json:"appearances,omitempty"` // Show only the blocknumber.tx_id appearances of the exported transactions 38 Articulate bool `json:"articulate,omitempty"` // Articulate the retrieved data if ABIs can be found 39 Source string `json:"source,omitempty"` // The source of the slurped data 40 Count bool `json:"count,omitempty"` // For --appearances mode only, display only the count of records 41 Page uint64 `json:"page,omitempty"` // The page to retrieve (page number) 42 PageId string `json:"pageId,omitempty"` // The page to retrieve (page ID) 43 PerPage uint64 `json:"perPage,omitempty"` // The number of records to request on each page 44 Sleep float64 `json:"sleep,omitempty"` // Seconds to sleep between requests 45 Types []string `json:"types,omitempty"` // Deprecated, use --parts instead 46 Globals globals.GlobalOptions `json:"globals,omitempty"` // The global options 47 Conn *rpc.Connection `json:"conn,omitempty"` // The connection to the RPC server 48 BadFlag error `json:"badFlag,omitempty"` // An error flag if needed 49 // EXISTING_CODE 50 // EXISTING_CODE 51 } 52 53 var defaultSlurpOptions = SlurpOptions{ 54 Source: "etherscan", 55 PerPage: 1000, 56 Sleep: .25, 57 } 58 59 // testLog is used only during testing to export the options for this test case. 60 func (opts *SlurpOptions) testLog() { 61 logger.TestLog(len(opts.Addrs) > 0, "Addrs: ", opts.Addrs) 62 logger.TestLog(len(opts.Blocks) > 0, "Blocks: ", opts.Blocks) 63 logger.TestLog(len(opts.Parts) > 0, "Parts: ", opts.Parts) 64 logger.TestLog(opts.Appearances, "Appearances: ", opts.Appearances) 65 logger.TestLog(opts.Articulate, "Articulate: ", opts.Articulate) 66 logger.TestLog(len(opts.Source) > 0 && opts.Source != "etherscan", "Source: ", opts.Source) 67 logger.TestLog(opts.Count, "Count: ", opts.Count) 68 logger.TestLog(opts.Page != 0, "Page: ", opts.Page) 69 logger.TestLog(len(opts.PageId) > 0, "PageId: ", opts.PageId) 70 logger.TestLog(opts.PerPage != 1000, "PerPage: ", opts.PerPage) 71 logger.TestLog(opts.Sleep != float64(.25), "Sleep: ", opts.Sleep) 72 opts.Conn.TestLog(opts.getCaches()) 73 opts.Globals.TestLog() 74 } 75 76 // String implements the Stringer interface 77 func (opts *SlurpOptions) String() string { 78 b, _ := json.MarshalIndent(opts, "", " ") 79 return string(b) 80 } 81 82 // slurpFinishParseApi finishes the parsing for server invocations. Returns a new SlurpOptions. 83 func slurpFinishParseApi(w http.ResponseWriter, r *http.Request) *SlurpOptions { 84 values := r.URL.Query() 85 if r.Header.Get("User-Agent") == "testRunner" { 86 values.Set("testRunner", "true") 87 } 88 return SlurpFinishParseInternal(w, values) 89 } 90 91 func SlurpFinishParseInternal(w io.Writer, values url.Values) *SlurpOptions { 92 copy := defaultSlurpOptions 93 copy.Globals.Caps = getCaps() 94 opts := © 95 opts.Source = "etherscan" 96 opts.PerPage = 1000 97 opts.Sleep = .25 98 for key, value := range values { 99 switch key { 100 case "addrs": 101 for _, val := range value { 102 s := strings.Split(val, " ") // may contain space separated items 103 opts.Addrs = append(opts.Addrs, s...) 104 } 105 case "blocks": 106 for _, val := range value { 107 s := strings.Split(val, " ") // may contain space separated items 108 opts.Blocks = append(opts.Blocks, s...) 109 } 110 case "parts": 111 for _, val := range value { 112 s := strings.Split(val, " ") // may contain space separated items 113 opts.Parts = append(opts.Parts, s...) 114 } 115 case "appearances": 116 opts.Appearances = true 117 case "articulate": 118 opts.Articulate = true 119 case "source": 120 opts.Source = value[0] 121 case "count": 122 opts.Count = true 123 case "page": 124 opts.Page = base.MustParseUint64(value[0]) 125 case "pageId": 126 opts.PageId = value[0] 127 case "perPage": 128 opts.PerPage = base.MustParseUint64(value[0]) 129 case "sleep": 130 opts.Sleep = base.MustParseFloat64(value[0]) 131 case "types": 132 for _, val := range value { 133 s := strings.Split(val, " ") // may contain space separated items 134 opts.Types = append(opts.Types, s...) 135 } 136 default: 137 if !copy.Globals.Caps.HasKey(key) { 138 err := validate.Usage("Invalid key ({0}) in {1} route.", key, "slurp") 139 if opts.BadFlag == nil || opts.BadFlag.Error() > err.Error() { 140 opts.BadFlag = err 141 } 142 } 143 } 144 } 145 opts.Conn = opts.Globals.FinishParseApi(w, values, opts.getCaches()) 146 147 // Deprecated... 148 if len(opts.Types) > 0 && len(opts.Parts) == 0 { 149 logger.Warn("The --types flag is deprecated. Please use --parts instead.") 150 opts.Parts = opts.Types 151 opts.Types = []string{} 152 } 153 154 // EXISTING_CODE 155 for _, t := range opts.Parts { 156 if t == "all" { 157 opts.Parts = []string{"ext", "int", "token", "nfts", "1155", "miner", "uncles", "withdrawals"} 158 break 159 } 160 } 161 if len(opts.Parts) == 0 { 162 opts.Parts = []string{"ext"} 163 } 164 // EXISTING_CODE 165 opts.Addrs, _ = opts.Conn.GetEnsAddresses(opts.Addrs) 166 167 return opts 168 } 169 170 // slurpFinishParse finishes the parsing for command line invocations. Returns a new SlurpOptions. 171 func slurpFinishParse(args []string) *SlurpOptions { 172 // remove duplicates from args if any (not needed in api mode because the server does it). 173 dedup := map[string]int{} 174 if len(args) > 0 { 175 tmp := []string{} 176 for _, arg := range args { 177 if value := dedup[arg]; value == 0 { 178 tmp = append(tmp, arg) 179 } 180 dedup[arg]++ 181 } 182 args = tmp 183 } 184 185 defFmt := "txt" 186 opts := GetOptions() 187 opts.Conn = opts.Globals.FinishParse(args, opts.getCaches()) 188 189 // Deprecated... 190 if len(opts.Types) > 0 && len(opts.Parts) == 0 { 191 logger.Warn("The --types flag is deprecated. Please use --parts instead.") 192 opts.Parts = opts.Types 193 opts.Types = []string{} 194 } 195 196 // EXISTING_CODE 197 for _, arg := range args { 198 if base.IsValidAddress(arg) { 199 opts.Addrs = append(opts.Addrs, arg) 200 } else { 201 opts.Blocks = append(opts.Blocks, arg) 202 } 203 } 204 for _, t := range opts.Parts { 205 if t == "all" { 206 opts.Parts = []string{"ext", "int", "token", "nfts", "1155", "miner", "uncles", "withdrawals"} 207 break 208 } 209 } 210 if len(opts.Parts) == 0 { 211 opts.Parts = []string{"ext"} 212 } 213 // EXISTING_CODE 214 opts.Addrs, _ = opts.Conn.GetEnsAddresses(opts.Addrs) 215 if len(opts.Globals.Format) == 0 || opts.Globals.Format == "none" { 216 opts.Globals.Format = defFmt 217 } 218 219 return opts 220 } 221 222 func GetOptions() *SlurpOptions { 223 // EXISTING_CODE 224 // EXISTING_CODE 225 return &defaultSlurpOptions 226 } 227 228 func getCaps() caps.Capability { 229 var capabilities caps.Capability // capabilities for chifra slurp 230 capabilities = capabilities.Add(caps.Default) 231 capabilities = capabilities.Add(caps.Caching) 232 capabilities = capabilities.Add(caps.Ether) 233 capabilities = capabilities.Add(caps.Names) 234 // EXISTING_CODE 235 // EXISTING_CODE 236 return capabilities 237 } 238 239 func ResetOptions(testMode bool) { 240 // We want to keep writer between command file calls 241 w := GetOptions().Globals.Writer 242 opts := SlurpOptions{} 243 globals.SetDefaults(&opts.Globals) 244 opts.Globals.TestMode = testMode 245 opts.Globals.Writer = w 246 opts.Globals.Caps = getCaps() 247 opts.Source = "etherscan" 248 opts.PerPage = 1000 249 opts.Sleep = .25 250 defaultSlurpOptions = opts 251 } 252 253 func (opts *SlurpOptions) getCaches() (caches map[walk.CacheType]bool) { 254 // EXISTING_CODE 255 caches = map[walk.CacheType]bool{ 256 walk.Cache_Transactions: true, 257 } 258 // EXISTING_CODE 259 return 260 } 261 262 // EXISTING_CODE 263 func (opts *SlurpOptions) Addresses() []base.Address { 264 addresses := make([]base.Address, 0, len(opts.Addrs)) 265 for _, addr := range opts.Addrs { 266 addresses = append(addresses, base.HexToAddress(addr)) 267 } 268 return addresses 269 } 270 271 func (opts *SlurpOptions) Query() *provider.Query { 272 return &provider.Query{ 273 Addresses: opts.Addresses(), 274 Resources: opts.Parts, 275 PerPage: uint(opts.PerPage), 276 StartPage: uint(opts.Page), 277 StartPageId: opts.PageId, 278 BlockRange: opts.BlockIds, 279 } 280 } 281 282 // Provider returns 3rd party RPC provider based on --source 283 func (opts *SlurpOptions) Provider() (provider.Provider, error) { 284 switch opts.Source { 285 case "key": 286 return provider.NewKeyProvider(opts.Conn, opts.Globals.Chain) 287 case "covalent": 288 return provider.NewCovalentProvider(opts.Conn, opts.Globals.Chain) 289 case "alchemy": 290 return provider.NewAlchemyProvider(opts.Conn, opts.Globals.Chain) 291 case "etherscan": 292 fallthrough 293 default: 294 return provider.NewEtherscanProvider(opts.Conn) 295 } 296 } 297 298 // EXISTING_CODE