Skip to content

Commit

Permalink
chifra state --call <calldata> converted to chifra state --call --cal…
Browse files Browse the repository at this point in the history
…ldata <calldata>
  • Loading branch information
tjayrush committed Dec 17, 2024
1 parent d3f686a commit cfb5e6e
Show file tree
Hide file tree
Showing 29 changed files with 204 additions and 527 deletions.
2 changes: 1 addition & 1 deletion docs
2 changes: 1 addition & 1 deletion sdk
19 changes: 14 additions & 5 deletions src/apps/chifra/cmd/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,13 @@ Notes:
- If the queried node does not store historical state, the results are undefined.
- Special blocks are detailed under chifra when --list.
- Balance is the default mode. To select a single mode use none first, followed by that mode.
- Valid parameters for --call include Solidity-like syntax: balanceOf(0x316b...183d), a four-byte followed by parameters: 0x70a08231(0x316b...183d), or encoded input data.
- Valid parameters for --calldata include Solidity-like syntax: balanceOf(0x316b...), a four-byte followed by parameters: 0x70a08231(0x316b...), or encoded input data.
- You may specify multiple parts on a single line.
- In the --call string, you may separate multiple calls with a colon.`
- In the --call string, you may separate multiple calls with a colon.
- Your use of the unaudited --send option legally absolves TrueBlocks, LLC or any associated parties from liability or loss related to such use.
- The --send option does not validate its input before sending your transaction to the network. If you provide invalid data, you may lose your funds. Be warned.
- As of version 4.0.0, use --call --calldata <cmd> to provide your command.
- --calldata may be one or more colon-seperated solidity calls, four-byte plus parameters, or encoded call data strings.`

func init() {
var capabilities caps.Capability // capabilities for chifra state
Expand All @@ -69,9 +73,14 @@ func init() {
One or more of [ balance | nonce | code | proxy | deployed | accttype | some | all ]`)
stateCmd.Flags().BoolVarP(&statePkg.GetOptions().Changes, "changes", "c", false, `only report a balance when it changes from one block to the next`)
stateCmd.Flags().BoolVarP(&statePkg.GetOptions().NoZero, "no_zero", "z", false, `suppress the display of zero balance accounts`)
stateCmd.Flags().StringVarP(&statePkg.GetOptions().Call, "call", "l", "", `call a smart contract with one or more solidity calls, four-byte plus parameters, or encoded call data strings`)
stateCmd.Flags().BoolVarP(&statePkg.GetOptions().Articulate, "articulate", "a", false, `for the --call option only, articulate the retrieved data if ABIs can be found`)
stateCmd.Flags().StringVarP(&statePkg.GetOptions().ProxyFor, "proxy_for", "r", "", `for the --call option only, redirects calls to this implementation`)
stateCmd.Flags().BoolVarP(&statePkg.GetOptions().Call, "call", "l", false, `write-only call (a query) to a smart contract`)
stateCmd.Flags().BoolVarP(&statePkg.GetOptions().Send, "send", "s", false, `writes a transaction to an address (see docs for more information) (hidden)`)
stateCmd.Flags().StringVarP(&statePkg.GetOptions().Calldata, "calldata", "d", "", `for commands (--call or --send), provides the call data (in various forms) for the command (may be empty for --send)`)
stateCmd.Flags().BoolVarP(&statePkg.GetOptions().Articulate, "articulate", "a", false, `for commands only, articulate the retrieved data if ABIs can be found`)
stateCmd.Flags().StringVarP(&statePkg.GetOptions().ProxyFor, "proxy_for", "r", "", `for commands only, redirects calls to this implementation`)
if os.Getenv("TEST_MODE") != "true" {
_ = stateCmd.Flags().MarkHidden("send")
}
globals.InitGlobals("state", stateCmd, &statePkg.GetOptions().Globals, capabilities)

stateCmd.SetUsageTemplate(UsageWithNotes(notesState))
Expand Down
13 changes: 9 additions & 4 deletions src/apps/chifra/internal/state/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ Flags:
One or more of [ balance | nonce | code | proxy | deployed | accttype | some | all ]
-c, --changes only report a balance when it changes from one block to the next
-z, --no_zero suppress the display of zero balance accounts
-l, --call string call a smart contract with one or more solidity calls, four-byte plus parameters, or encoded call data strings
-a, --articulate for the --call option only, articulate the retrieved data if ABIs can be found
-r, --proxy_for string for the --call option only, redirects calls to this implementation
-l, --call write-only call (a query) to a smart contract
-d, --calldata string for commands (--call or --send), provides the call data (in various forms) for the command (may be empty for --send)
-a, --articulate for commands only, articulate the retrieved data if ABIs can be found
-r, --proxy_for string for commands only, redirects calls to this implementation
-H, --ether specify value in ether
-o, --cache force the results of the query into the cache
-D, --decache removes related items from the cache
Expand All @@ -39,9 +40,13 @@ Notes:
- If the queried node does not store historical state, the results are undefined.
- Special blocks are detailed under chifra when --list.
- Balance is the default mode. To select a single mode use none first, followed by that mode.
- Valid parameters for --call include Solidity-like syntax: balanceOf(0x316b...183d), a four-byte followed by parameters: 0x70a08231(0x316b...183d), or encoded input data.
- Valid parameters for --calldata include Solidity-like syntax: balanceOf(0x316b...), a four-byte followed by parameters: 0x70a08231(0x316b...), or encoded input data.
- You may specify multiple parts on a single line.
- In the --call string, you may separate multiple calls with a colon.
- Your use of the unaudited --send option legally absolves TrueBlocks, LLC or any associated parties from liability or loss related to such use.
- The --send option does not validate its input before sending your transaction to the network. If you provide invalid data, you may lose your funds. Be warned.
- As of version 4.0.0, use --call --calldata <cmd> to provide your command.
- --calldata may be one or more colon-seperated solidity calls, four-byte plus parameters, or encoded call data strings.
```

Data models produced by this tool:
Expand Down
9 changes: 4 additions & 5 deletions src/apps/chifra/internal/state/handle_call.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func (opts *StateOptions) HandleCall(rCtx *output.RenderCtx) error {
return articulate.ArticulateFunction(function, "", str[2:])
}

callAddress := opts.GetCallAddress()
callAddress := opts.GetAddressOrProxy()
fetchData := func(modelChan chan types.Modeler, errorChan chan error) {
apps, _, err := identifiers.IdsToApps(chain, opts.BlockIds)
if err != nil {
Expand Down Expand Up @@ -65,7 +65,7 @@ func (opts *StateOptions) HandleCall(rCtx *output.RenderCtx) error {
for _, c := range opts.Calls {
if contractCall, _, err := call.NewContractCall(opts.Conn, callAddress, c); err != nil {
delete(thisMap, app)
return fmt.Errorf("the --call value provided (%s) was not found: %s", c, err)
return fmt.Errorf("the --calldata value provided (%s) was not found: %s", c, err)

} else {
contractCall.BlockNumber = bn
Expand Down Expand Up @@ -117,9 +117,8 @@ func (opts *StateOptions) HandleCall(rCtx *output.RenderCtx) error {
return output.StreamMany(rCtx, fetchData, opts.Globals.OutputOptsWithExtra(extraOpts))
}

func (opts *StateOptions) GetCallAddress() base.Address {
// Note that the validator precludes the possibility of having more than one address
// if the call option is present.
func (opts *StateOptions) GetAddressOrProxy() base.Address {
// Note that validtor makes sure there's exactly one address for --call or --send.
callAddress := base.HexToAddress(opts.Addrs[0])
proxy := base.HexToAddress(opts.ProxyFor)
if !proxy.IsZero() {
Expand Down
4 changes: 2 additions & 2 deletions src/apps/chifra/internal/state/handle_decache.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ func (opts *StateOptions) getItemsToRemove() ([]cache.Locator, error) {

for _, c := range opts.Calls {
if len(c) > 0 {
callAddress := opts.GetCallAddress()
callAddress := opts.GetAddressOrProxy()
if contractCall, _, err := call.NewContractCall(opts.Conn, callAddress, c); err != nil {
wrapped := fmt.Errorf("the --call value provided (%s) was not found: %s", c, err)
wrapped := fmt.Errorf("the --calldata value provided (%s) was not found: %s", c, err)
return []cache.Locator{}, wrapped

} else {
Expand Down
20 changes: 20 additions & 0 deletions src/apps/chifra/internal/state/handle_send.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright 2021 The TrueBlocks Authors. All rights reserved.
// Use of this source code is governed by a license that can
// be found in the LICENSE file.

package statePkg

import (
"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/output"
"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/types"
)

func (opts *StateOptions) HandleSend(rCtx *output.RenderCtx) error {
fetchData := func(modelChan chan types.Modeler, errorChan chan error) {
modelChan <- &types.Result{
Name: "I am not right",
}
}

return output.StreamMany(rCtx, fetchData, opts.Globals.OutputOpts())
}
28 changes: 18 additions & 10 deletions src/apps/chifra/internal/state/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,11 @@ type StateOptions struct {
Parts []string `json:"parts,omitempty"` // Control which state to export
Changes bool `json:"changes,omitempty"` // Only report a balance when it changes from one block to the next
NoZero bool `json:"noZero,omitempty"` // Suppress the display of zero balance accounts
Call string `json:"call,omitempty"` // Call a smart contract with one or more solidity calls, four-byte plus parameters, or encoded call data strings
Articulate bool `json:"articulate,omitempty"` // For the --call option only, articulate the retrieved data if ABIs can be found
ProxyFor string `json:"proxyFor,omitempty"` // For the --call option only, redirects calls to this implementation
Call bool `json:"call,omitempty"` // Write-only call (a query) to a smart contract
Send bool `json:"send,omitempty"` // Writes a transaction to an address (see docs for more information)
Calldata string `json:"calldata,omitempty"` // For commands (--call or --send), provides the call data (in various forms) for the command (may be empty for --send)
Articulate bool `json:"articulate,omitempty"` // For commands only, articulate the retrieved data if ABIs can be found
ProxyFor string `json:"proxyFor,omitempty"` // For commands only, redirects calls to this implementation
Globals globals.GlobalOptions `json:"globals,omitempty"` // The global options
Conn *rpc.Connection `json:"conn,omitempty"` // The connection to the RPC server
BadFlag error `json:"badFlag,omitempty"` // An error flag if needed
Expand All @@ -55,7 +57,9 @@ func (opts *StateOptions) testLog() {
logger.TestLog(len(opts.Parts) > 0, "Parts: ", opts.Parts)
logger.TestLog(opts.Changes, "Changes: ", opts.Changes)
logger.TestLog(opts.NoZero, "NoZero: ", opts.NoZero)
logger.TestLog(len(opts.Call) > 0, "Call: ", opts.Call)
logger.TestLog(opts.Call, "Call: ", opts.Call)
logger.TestLog(opts.Send, "Send: ", opts.Send)
logger.TestLog(len(opts.Calldata) > 0, "Calldata: ", opts.Calldata)
logger.TestLog(opts.Articulate, "Articulate: ", opts.Articulate)
logger.TestLog(len(opts.ProxyFor) > 0, "ProxyFor: ", opts.ProxyFor)
opts.Conn.TestLog(opts.getCaches())
Expand Down Expand Up @@ -103,7 +107,11 @@ func StateFinishParseInternal(w io.Writer, values url.Values) *StateOptions {
case "noZero":
opts.NoZero = true
case "call":
opts.Call = value[0]
opts.Call = true
case "send":
opts.Send = true
case "calldata":
opts.Calldata = value[0]
case "articulate":
opts.Articulate = true
case "proxyFor":
Expand All @@ -120,8 +128,8 @@ func StateFinishParseInternal(w io.Writer, values url.Values) *StateOptions {
opts.Conn = opts.Globals.FinishParseApi(w, values, opts.getCaches())

// EXISTING_CODE
opts.Call = strings.Replace(strings.Trim(opts.Call, "'"), "'", "\"", -1)
opts.Calls = strings.Split(opts.Call, ":")
opts.Calldata = strings.Replace(strings.Trim(opts.Calldata, "'"), "'", "\"", -1)
opts.Calls = strings.Split(opts.Calldata, ":")
if len(opts.Blocks) == 0 {
if opts.Globals.TestMode {
opts.Blocks = []string{"17000000"}
Expand Down Expand Up @@ -163,8 +171,8 @@ func stateFinishParse(args []string) *StateOptions {
opts.Blocks = append(opts.Blocks, arg)
}
}
opts.Call = strings.Replace(strings.Trim(opts.Call, "'"), "'", "\"", -1)
opts.Calls = strings.Split(opts.Call, ":")
opts.Calldata = strings.Replace(strings.Trim(opts.Calldata, "'"), "'", "\"", -1)
opts.Calls = strings.Split(opts.Calldata, ":")
if len(opts.Blocks) == 0 {
if opts.Globals.TestMode {
opts.Blocks = []string{"17000000"}
Expand Down Expand Up @@ -214,7 +222,7 @@ func (opts *StateOptions) getCaches() (caches map[walk.CacheType]bool) {
// EXISTING_CODE
caches = map[walk.CacheType]bool{
walk.Cache_State: true,
walk.Cache_Results: len(opts.Call) > 0,
walk.Cache_Results: opts.Call || opts.Send,
}
// EXISTING_CODE
return
Expand Down
4 changes: 3 additions & 1 deletion src/apps/chifra/internal/state/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,10 @@ func (opts *StateOptions) StateInternal(rCtx *output.RenderCtx) error {
// EXISTING_CODE
if opts.Globals.Decache {
err = opts.HandleDecache(rCtx)
} else if len(opts.Call) > 0 {
} else if opts.Call {
err = opts.HandleCall(rCtx)
} else if opts.Send {
err = opts.HandleSend(rCtx)
} else {
err = opts.HandleShow(rCtx)
}
Expand Down
93 changes: 64 additions & 29 deletions src/apps/chifra/internal/state/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,59 +37,70 @@ func (opts *StateOptions) validateState() error {
// do nothing for now

} else {
if len(opts.Call) > 0 {
if opts.Call && opts.Send {
return validate.Usage("The {0} and {1} options are mutually exclusive.", "--call", "--send")
}

if opts.Call || opts.Send {
if len(opts.Calldata) == 0 {
return validate.Usage("The {0} option requires at least one {1} command.", "--call", "--calldata")
}

if len(opts.Parts) > 0 {
return validate.Usage("The {0} option is not available{1}.", "--parts", " with the --call option")
return validate.Usage("The {0} option is not available{1}.", "--parts", " with the --call or --send option")
}

if opts.Changes {
return validate.Usage("The {0} option is not available{1}.", "--changes", " with the --call option")
return validate.Usage("The {0} option is not available{1}.", "--changes", " with the --call or --send option")
}

if opts.NoZero {
return validate.Usage("The {0} option is not available{1}.", "--no_zero", " with the --call option")
return validate.Usage("The {0} option is not available{1}.", "--no_zero", " with the --call or --send option")
}

if len(opts.Addrs) != 1 {
return validate.Usage("Exactly one address is required for the {0} option.", "--call")
return validate.Usage("Exactly one address is required for the {0} option.", "--call or --send")
}

callAddress := opts.GetCallAddress()
err := opts.Conn.IsContractAtLatest(base.HexToAddress(callAddress.Hex()))
if err != nil {
if errors.Is(err, rpc.ErrNotAContract) {
return validate.Usage("The address for the --call option must be a smart contract.")
}
return err
callAddress := opts.GetAddressOrProxy()
isContract := false
if err = opts.Conn.IsContractAtLatest(base.HexToAddress(callAddress.Hex())); err == nil {
isContract = true
}

// Before we do anythinng, let's just make sure we have a valid four-byte
for _, c := range opts.Calls {
if _, suggestions, err := call.NewContractCall(opts.Conn, callAddress, c); err != nil {
message := fmt.Sprintf("the --call value provided (%s) was not found: %s", c, err)
if len(suggestions) > 0 {
if len(suggestions) > 0 {
message += " Suggestions: "
for index, suggestion := range suggestions {
if index > 0 {
message += " "
}
message += fmt.Sprintf("%d: %s.", index+1, suggestion)
}
}
if opts.Send {
// Sends with no call data are okay to either smart contracts or regular accounts.
if isContract {
if err := opts.checkCommands(callAddress, opts.Send); err != nil {
return err
}
}

} else if opts.Call {
if isContract {
if err := opts.checkCommands(callAddress, opts.Send); err != nil {
return err
}
} else {
if errors.Is(err, rpc.ErrNotAContract) {
return validate.Usage("The address for the --call option must be a smart contract.")
}
return errors.New(message)
return err
}
}

} else {
if len(opts.Calldata) > 0 {
return validate.Usage("The {0} option is only available with the {1} option.", "--calldata", "--call or --send")
}

if opts.Articulate {
return validate.Usage("The {0} option is only available with the {1} option.", "--articulate", "--call")
return validate.Usage("The {0} option is only available with the {1} option.", "--articulate", "--call or --send")
}

proxy := base.HexToAddress(opts.ProxyFor)
if !proxy.IsZero() {
return validate.Usage("The {0} option is only available with the {1} option.", "--proxy_for", "--call")
return validate.Usage("The {0} option is only available with the {1} option.", "--proxy_for", "--call or --send")
}

err := validate.ValidateAtLeastOneAddr(opts.Addrs)
Expand Down Expand Up @@ -134,3 +145,27 @@ func (opts *StateOptions) validateState() error {

return opts.Globals.Validate()
}

func (opts *StateOptions) checkCommands(callAddress base.Address, isSend bool) error {
if len(opts.Calldata) == 0 && !isSend {
return validate.Usage("The {0} option requires at least one command.", "--call")
}

// an empty list of commands is okay...
for _, cmd := range opts.Calls {
if _, suggestions, err := call.NewContractCall(opts.Conn, callAddress, cmd); err != nil {
message := fmt.Sprintf("the --calldata value provided (%s) was not found: %s", cmd, err)
if len(suggestions) > 0 {
message += " Suggestions: "
for index, suggestion := range suggestions {
if index > 0 {
message += " "
}
message += fmt.Sprintf("%d: %s.", index+1, suggestion)
}
}
return errors.New(message)
}
}
return nil
}
4 changes: 2 additions & 2 deletions src/apps/chifra/pkg/call/call.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ type ContractCall struct {
func NewContractCallWithAbi(conn *rpc.Connection, callAddress base.Address, theCall string, abiMap *abi.SelectorSyncMap) (*ContractCall, []string, error) {
parsed, err := parser.ParseCall(theCall)
if err != nil {
err = fmt.Errorf("the value provided --call (%s) is invalid", theCall)
err = fmt.Errorf("the value provided (%s) is invalid", theCall)
return nil, []string{}, err
}

Expand Down Expand Up @@ -100,7 +100,7 @@ func NewContractCall(conn *rpc.Connection, callAddress base.Address, theCall str

parsed, err := parser.ParseCall(theCall)
if err != nil {
err = fmt.Errorf("the value provided --call (%s) is invalid", theCall)
err = fmt.Errorf("the value provided (%s) is invalid", theCall)
return nil, []string{}, err
}

Expand Down
2 changes: 1 addition & 1 deletion src/apps/chifra/pkg/configtypes/chain_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ type ChainGroup struct {
IpfsGateway string `json:"ipfsGateway" toml:"ipfsGateway,omitempty"`
KeyEndpoint string `json:"keyEndpoint" toml:"keyEndpoint,omitempty"`
LocalExplorer string `json:"localExplorer" toml:"localExplorer,omitempty"`
RemoteExplorer string `json:"removeExplorer" toml:"remoteExplorer,omitempty"`
RemoteExplorer string `json:"remoteExplorer" toml:"remoteExplorer,omitempty"`
RpcProvider string `json:"rpcProvider" toml:"rpcProvider"`
Symbol string `json:"symbol" toml:"symbol"`
Scrape ScrapeSettings `json:"scrape" toml:"scrape"`
Expand Down
4 changes: 0 additions & 4 deletions src/apps/chifra/pkg/configtypes/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@ func (s *Config) String() string {
return string(bytes)
}

func (s *Config) ShallowCopy() Config {
return *s
}

func NewConfig(cachePath, indexPath, defaultIpfs string) Config {
ret := defaultConfig
ret.Settings.CachePath = cachePath
Expand Down
4 changes: 3 additions & 1 deletion src/apps/chifra/pkg/index/checkVersion.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ func IsInitialized(chain, required string) error {
(and allow it to complete) or 'chifra scrape' before using this command.
Error: %w
Chain: %s
Path: %s
`
return fmt.Errorf(indexNotInitialized, ErrNotInitialized)
return fmt.Errorf(indexNotInitialized, ErrNotInitialized, chain, fileName)
}

var err error
Expand Down
Loading

0 comments on commit cfb5e6e

Please sign in to comment.