Home Reference Source

lib/data/decode/storage.js

import debugModule from "debug";
const debug = debugModule("debugger:data:decode:storage");

import { WORD_SIZE } from "./utils";
import * as utils from "./utils";


/**
 * read slot from storage
 *
 * @param slot - big number or array of regular numbers
 * @param offset - for array, offset from the keccak determined location
 */
export function read(storage, slot, offset = 0) {
  if (slot instanceof Array) {
    slot = utils.keccak256(...slot.map(utils.toBigNumber));
  }

  slot = utils.toBigNumber(slot).plus(offset);

  debug("reading slot: %o", utils.toHexString(slot));

  let word = storage[utils.toHexString(slot, WORD_SIZE)] ||
    new Uint8Array(WORD_SIZE);

  debug("word %o", word);
  return word
}

/**
 * read all bytes in some range.
 *
 * parameters `from` and `to` are objects with the following properties:
 *
 *   slot - (required) either a bignumber or a "path" array of integer offsets
 *
 *     path array values get converted into keccak256 hash as per solidity
 *     storage allocation method
 *
 *     ref: https://solidity.readthedocs.io/en/v0.4.23/miscellaneous.html#layout-of-state-variables-in-storage
 *     (search "concatenation")
 *
 *  offset - (default: 0) slot offset
 *
 *  index - (default: 0) byte index in word
 *
 * @param from - location (see ^)
 * @param to - location (see ^). inclusive.
 * @param length - instead of `to`, number of bytes after `from`
 */
export function readRange(storage, {from, to, length}) {
  if (!length && !to || length && to) {
    throw new Error("must specify exactly one `to`|`length`");
  }

  from = {
    ...from,
    offset: from.offset || 0
  };

  if (length) {
    to = {
      slot: from.slot,
      offset: from.offset + Math.floor((from.index + length - 1) / WORD_SIZE),
      index: (from.index + length - 1) % WORD_SIZE
    };
  } else {
    to = {
      ...to,
      offset: to.offset || 0
    }
  }

  debug("readRange %o", {from,to});

  const totalWords = to.offset - from.offset + 1;
  debug("totalWords %o", totalWords);

  let data = new Uint8Array(totalWords * WORD_SIZE);

  for (let i = 0; i < totalWords; i++) {
    let offset = i + from.offset;
    data.set(read(storage, from.slot, offset), i * WORD_SIZE);
  }
  debug("words %o", data);

  data = data.slice(from.index, (totalWords - 1) * WORD_SIZE + to.index + 1);

  debug("data: %o", data);

  return data;
}