github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/pkg/rpc/get_state.go (about) 1 package rpc 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 8 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base" 9 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/rpc/query" 10 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/types" 11 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/walk" 12 ) 13 14 type StateFilters struct { 15 BalanceCheck func(address base.Address, balance *base.Wei) bool 16 } 17 18 // GetState returns account state (search: FromRpc) 19 func (conn *Connection) GetState(fieldBits types.StatePart, address base.Address, blockNumber base.Blknum, filters StateFilters) (*types.State, error) { 20 blockTs := base.Timestamp(0) 21 if conn.StoreReadable() { 22 // walk.Cache_State 23 state := &types.State{ 24 BlockNumber: blockNumber, 25 Address: address, 26 } 27 if err := conn.Store.Read(state, nil); err == nil { 28 if state.Parts&fieldBits == fieldBits { 29 // we have what we need 30 return state, nil 31 } 32 } 33 fieldBits |= state.Parts // preserve what's there 34 blockTs = conn.GetBlockTimestamp(blockNumber) 35 } 36 37 // We always ask for balance even if we dont' need it. Not sure why. 38 rpcPayload := []query.BatchPayload{ 39 { 40 Key: "balance", 41 Payload: &query.Payload{ 42 Method: "eth_getBalance", 43 Params: query.Params{ 44 address, 45 fmt.Sprintf("0x%x", blockNumber), 46 }, 47 }, 48 }, 49 } 50 51 if (fieldBits & types.Nonce) != 0 { 52 rpcPayload = append(rpcPayload, query.BatchPayload{ 53 Key: "nonce", 54 Payload: &query.Payload{ 55 Method: "eth_getTransactionCount", 56 Params: query.Params{ 57 address, 58 fmt.Sprintf("0x%x", blockNumber), 59 }, 60 }, 61 }) 62 } 63 64 if (fieldBits & types.Code) != 0 { 65 rpcPayload = append(rpcPayload, query.BatchPayload{ 66 Key: "code", 67 Payload: &query.Payload{ 68 Method: "eth_getCode", 69 Params: query.Params{ 70 address, 71 fmt.Sprintf("0x%x", blockNumber), 72 }, 73 }, 74 }) 75 } 76 77 queryResults, err := query.QueryBatch[string](conn.Chain, rpcPayload) 78 if err != nil { 79 return nil, err 80 } 81 82 value := queryResults["balance"] 83 balance := base.NewWei(0) 84 balance.SetString(*value, 0) 85 86 state := &types.State{ 87 Address: address, 88 BlockNumber: blockNumber, 89 Deployed: base.NOPOSN, 90 Timestamp: blockTs, 91 Parts: fieldBits, 92 } 93 94 if (fieldBits & types.Balance) != 0 { 95 state.Balance = *balance 96 } 97 98 if (fieldBits & types.Nonce) != 0 { 99 if value, ok := queryResults["nonce"]; ok { 100 state.Nonce = base.MustParseValue(*value) 101 } 102 } 103 104 if (fieldBits & types.Code) != 0 { 105 if value, ok := queryResults["code"]; ok { 106 code := *value 107 if code != "0x" { 108 state.Code = code 109 } 110 } 111 } 112 113 if (fieldBits & types.Deployed) != 0 { 114 block, err := conn.GetContractDeployBlock(address) 115 if err != nil && !errors.Is(err, ErrNotAContract) { 116 return nil, err 117 } 118 // If err is ErrNotAContract, then we'll use the default value 119 if err == nil { 120 state.Deployed = block 121 } 122 } 123 124 var proxy base.Address 125 126 if (fieldBits&types.Proxy) != 0 || (fieldBits&types.Type) != 0 { 127 proxy, err = conn.GetContractProxyAt(address, blockNumber) 128 if err != nil { 129 return nil, err 130 } 131 if (fieldBits & types.Proxy) != 0 { 132 state.Proxy = proxy 133 } 134 } 135 136 if (fieldBits & types.Type) != 0 { 137 if !proxy.IsZero() { 138 state.AccountType = "Proxy" 139 } else { 140 state.AccountType = conn.getTypeNonProxy(address, blockNumber) 141 } 142 } 143 144 isFinal := base.IsFinal(conn.LatestBlockTimestamp, blockTs) 145 if isFinal && conn.StoreWritable() && conn.EnabledMap[walk.Cache_State] { 146 _ = conn.Store.Write(state, nil) 147 } 148 149 if filters.BalanceCheck != nil { 150 if !filters.BalanceCheck(address, &state.Balance) { 151 return nil, nil 152 } 153 } 154 155 return state, nil 156 } 157 158 // GetBalanceAt returns a balance for an address at a block 159 func (conn *Connection) GetBalanceAt(addr base.Address, bn base.Blknum) (*base.Wei, error) { 160 if ec, err := conn.getClient(); err != nil { 161 var zero base.Wei 162 return &zero, err 163 } else { 164 defer ec.Close() 165 ret, err := ec.BalanceAt(context.Background(), addr.Common(), base.BiFromBn(bn)) 166 return (*base.Wei)(ret), err 167 } 168 } 169 170 func (conn *Connection) getTypeNonProxy(address base.Address, bn base.Blknum) string { 171 isContractErr := conn.IsContractAt(address, bn) 172 if errors.Is(isContractErr, ErrNotAContract) { 173 return "EOA" 174 } 175 return "Contract" 176 }