github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/internal/state/handle_show.go (about)

     1  package statePkg
     2  
     3  import (
     4  	"errors"
     5  
     6  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base"
     7  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/identifiers"
     8  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/output"
     9  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/rpc"
    10  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/tslib"
    11  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/types"
    12  	"github.com/ethereum/go-ethereum"
    13  )
    14  
    15  func (opts *StateOptions) HandleShow(rCtx *output.RenderCtx) error {
    16  	chain := opts.Globals.Chain
    17  
    18  	previousBalance := make(map[base.Address]*base.Wei, len(opts.Addrs))
    19  	var filters rpc.StateFilters
    20  	if opts.Changes || opts.NoZero {
    21  		filters = rpc.StateFilters{
    22  			BalanceCheck: func(address base.Address, balance *base.Wei) bool {
    23  				if opts.Changes {
    24  					previous := previousBalance[address]
    25  					if balance.Text(10) == previous.Text(10) {
    26  						return false
    27  					}
    28  					previousBalance[address] = balance
    29  				}
    30  
    31  				if opts.NoZero {
    32  					return len(balance.Bytes()) > 0
    33  				}
    34  
    35  				return true
    36  			},
    37  		}
    38  	}
    39  
    40  	stateFields, outputFields, none := types.SliceToStateParts(opts.Parts)
    41  
    42  	cnt := 0
    43  	fetchData := func(modelChan chan types.Modeler, errorChan chan error) {
    44  		for _, addressStr := range opts.Addrs {
    45  			address := base.HexToAddress(addressStr)
    46  			currentBn := base.Blknum(0)
    47  			currentTs := base.Timestamp(0)
    48  			for _, br := range opts.BlockIds { // TODO: use the regular way to do this
    49  				blockNums, err := br.ResolveBlocks(chain)
    50  				if err != nil {
    51  					errorChan <- err
    52  					if errors.Is(err, ethereum.NotFound) {
    53  						continue
    54  					}
    55  					rCtx.Cancel()
    56  					return
    57  				}
    58  
    59  				for _, bn := range blockNums {
    60  					if none {
    61  						modelChan <- &types.State{
    62  							Address:     address,
    63  							BlockNumber: bn,
    64  						}
    65  						cnt++
    66  						return
    67  					}
    68  
    69  					if br.StartType == identifiers.BlockHash {
    70  						block, _ := opts.Conn.GetBlockHeaderByNumber(bn)
    71  						if base.HexToHash(br.Orig) != block.Hash {
    72  							errorChan <- errors.New("block hash " + br.Orig + " not found")
    73  							continue
    74  						}
    75  					}
    76  
    77  					state, err := opts.Conn.GetState(stateFields, address, bn, filters)
    78  					if err != nil {
    79  						errorChan <- err
    80  					}
    81  					// state may be nil if it was skipped by a filter for example
    82  					if state != nil {
    83  						if opts.Globals.Verbose {
    84  							if bn == 0 || bn != currentBn {
    85  								currentTs, _ = tslib.FromBnToTs(chain, bn)
    86  							}
    87  							state.Timestamp = currentTs
    88  							currentBn = bn
    89  						}
    90  						cnt++
    91  						modelChan <- state
    92  					}
    93  				}
    94  			}
    95  		}
    96  		if cnt == 0 {
    97  			errorChan <- errors.New("no state results were reported")
    98  		}
    99  	}
   100  
   101  	extraOpts := map[string]any{
   102  		"fields": outputFields,
   103  	}
   104  
   105  	return output.StreamMany(rCtx, fetchData, opts.Globals.OutputOptsWithExtra(extraOpts))
   106  }