diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 5fbe7db694..34daa9d589 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1928,6 +1928,61 @@ func NewDebugAPI(b Backend) *DebugAPI { return &DebugAPI{b: b} } +// BatchGetStorageAt returns multiple storage slots for a single account at the given block. +// Params: address, [hexKey...], blockNrOrHash +// Returns values in the same order as input keys. +func (api *DebugAPI) BatchGetStorageAt(ctx context.Context, address common.Address, hexKeys []string, blockNrOrHash rpc.BlockNumberOrHash) ([]hexutil.Bytes, error) { + state, _, err := api.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) + if state == nil || err != nil { + return nil, err + } + // Preallocate results + res := make([]hexutil.Bytes, len(hexKeys)) + for i, k := range hexKeys { + key, _, err := decodeHash(k) + if err != nil { + return nil, fmt.Errorf("unable to decode storage key at index %d: %w", i, err) + } + v := state.GetState(address, key) + vv := make([]byte, len(v)) + copy(vv, v[:]) + res[i] = vv + } + if err := state.Error(); err != nil { + return nil, err + } + return res, nil +} + +// BatchGetStorage returns multiple storage slots for multiple accounts at the given block. +// Params: {address: [hexKey, ...], ...}, blockNrOrHash +// Returns a map {address: [value,...]} preserving the order within each key list. +func (api *DebugAPI) BatchGetStorage(ctx context.Context, req map[common.Address][]string, blockNrOrHash rpc.BlockNumberOrHash) (map[common.Address][]hexutil.Bytes, error) { + state, _, err := api.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) + if state == nil || err != nil { + return nil, err + } + out := make(map[common.Address][]hexutil.Bytes, len(req)) + for addr, keys := range req { + vals := make([]hexutil.Bytes, len(keys)) + for i, k := range keys { + key, _, err := decodeHash(k) + if err != nil { + return nil, fmt.Errorf("unable to decode storage key for %s at index %d: %w", addr.Hex(), i, err) + } + v := state.GetState(addr, key) + vv := make([]byte, len(v)) + copy(vv, v[:]) + vals[i] = vv + } + out[addr] = vals + } + if err := state.Error(); err != nil { + return nil, err + } + return out, nil +} + // GetRawHeader retrieves the RLP encoding for a single header. func (api *DebugAPI) GetRawHeader(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (hexutil.Bytes, error) { var hash common.Hash