trufflesuite/truffle

View on GitHub
packages/codec/lib/storage/utils.ts

Summary

Maintainability
A
0 mins
Test Coverage
import debugModule from "debug";
const debug = debugModule("codec:storage:utils");
import type BN from "bn.js";
import * as Evm from "@truffle/codec/evm";
import * as MappingKey from "@truffle/codec/mapping-key";

import type { StorageLength, Slot } from "./types";

export function isWordsLength(size: StorageLength): size is { words: number } {
  return (<{ words: number }>size).words !== undefined;
}

export function storageLengthToBytes(size: StorageLength): number {
  if (isWordsLength(size)) {
    debug("size.words %d", size.words);
    return size.words * Evm.Utils.WORD_SIZE;
  } else {
    return size.bytes;
  }
}

/**
 * convert a slot to a word corresponding to actual storage address
 *
 * if `slot` is an array, return hash of array values.
 * if `slot` array is nested, recurse on sub-arrays
 *
 * @param slot - number or possibly-nested array of numbers
 */
export function slotAddress(slot: Slot): BN {
  if (slot.key !== undefined && slot.path !== undefined) {
    // mapping reference
    return Evm.Utils.keccak256(
      MappingKey.Encode.mappingKeyAsHex(slot.key),
      slotAddress(slot.path)
    ).add(slot.offset);
  } else if (slot.path !== undefined) {
    const pathAddress = slotAddress(slot.path);
    const path: BN = slot.hashPath
      ? Evm.Utils.keccak256(pathAddress)
      : pathAddress;
    return path.add(slot.offset);
  } else {
    return slot.offset;
  }
}

//note: this function compares slots mostly by structure,
//rather than by their numerical value
export function equalSlots(
  slot1: Slot | undefined,
  slot2: Slot | undefined
): boolean {
  if (!slot1 || !slot2) {
    return !slot1 && !slot2; //if either is undefined, it's true only if both are
  }
  if (!slot1.offset.eq(slot2.offset)) {
    return false;
  }
  if (slot1.hashPath !== slot2.hashPath) {
    return false;
  }
  if (!equalSlots(slot1.path, slot2.path)) {
    return false;
  }
  //to compare keys, we'll just compare their hex encodings
  //(yes, that leaves some wiggle room, as it could consider different
  //*types* of keys to be equal, but if keys are the only difference then
  //that should determine those types, so it shouldn't be a problem)
  if (!slot1.key || !slot2.key) {
    //first, though, they likely don't *have* keys
    return !slot1.key && !slot2.key;
  }
  //if they do have keys, though...
  return Evm.Utils.equalData(
    MappingKey.Encode.encodeMappingKey(slot1.key),
    MappingKey.Encode.encodeMappingKey(slot2.key)
  );
}