trufflesuite/truffle

View on GitHub
packages/codec/lib/format/utils/exception.ts

Summary

Maintainability
A
0 mins
Test Coverage
/**
 * @protected
 *
 * @packageDocumentation
 */
import debugModule from "debug";
const debug = debugModule("codec:format:utils:exception");

import * as Format from "@truffle/codec/format/common";
import * as AstUtils from "@truffle/codec/ast/utils";
import type * as Storage from "@truffle/codec/storage/types";

//this function gives an error message
//for those errors that are meant to possibly
//be wrapped in a DecodingError and thrown
export function message(error: Format.Errors.ErrorForThrowing): string {
  switch (error.kind) {
    case "UserDefinedTypeNotFoundError":
      let typeName = Format.Types.isContractDefinedType(error.type)
        ? error.type.definingContractName + "." + error.type.typeName
        : error.type.typeName;
      return `Unknown ${error.type.typeClass} type ${typeName} of id ${error.type.id}`;
    case "UnsupportedConstantError":
      return `Unsupported constant type ${AstUtils.typeClass(
        error.definition
      )}`;
    case "UnusedImmutableError":
      return "Cannot read unused immutable";
    case "ReadErrorStack":
      return `Can't read stack from position ${error.from} to ${error.to}`;
    case "ReadErrorBytes":
      return `Can't read ${error.length} bytes from ${error.location} starting at ${error.start}`;
    case "ReadErrorStorage":
      if (error.range.length) {
        return `Can't read ${
          error.range.length
        } bytes from storage starting at index ${
          error.range.from.index
        } in ${slotAddressPrintout(error.range.from.slot)}`;
      } else {
        return `Can't read storage from index ${
          error.range.from.index
        } in ${slotAddressPrintout(error.range.from.slot)} to index ${
          error.range.to.index
        } in ${slotAddressPrintout(error.range.to.slot)}`;
      }
    case "StorageNotSuppliedError":
      return `Unknown storage at slot ${error.slot.toString()}`; //note: not actually used at present
    case "CodeNotSuppliedError":
      return `Unknown code for address ${error.address}`; //note: not actually used at present
  }
}

function slotAddressPrintout(slot: Storage.Slot): string {
  if (slot.key !== undefined && slot.path !== undefined) {
    // mapping reference
    let { type: keyEncoding, value: keyValue } = keyInfoForPrinting(slot.key);
    return (
      "keccak(" +
      keyValue +
      " as " +
      keyEncoding +
      ", " +
      slotAddressPrintout(slot.path) +
      ") + " +
      slot.offset.toString()
    );
  } else if (slot.path !== undefined) {
    const pathAddressPrintout = slotAddressPrintout(slot.path);
    return slot.hashPath
      ? "keccak(" + pathAddressPrintout + ")" + slot.offset.toString()
      : pathAddressPrintout + slot.offset.toString();
  } else {
    return slot.offset.toString();
  }
}

//this is like the old toSoliditySha3Input, but for debugging purposes ONLY
//it will NOT produce correct input to soliditySha3
//please use mappingKeyAsHex instead if you wish to encode a mapping key.
function keyInfoForPrinting(input: Format.Values.ElementaryValue): {
  type: string;
  value: string;
} {
  switch (input.type.typeClass) {
    case "uint":
      return {
        type: "uint",
        value: (<Format.Values.UintValue>input).value.asBN.toString()
      };
    case "int":
      return {
        type: "int",
        value: (<Format.Values.IntValue>input).value.asBN.toString()
      };
    case "fixed":
      return {
        type: `fixed256x${input.type.places}`,
        value: (<Format.Values.FixedValue>input).value.asBig.toString()
      };
    case "ufixed":
      return {
        type: `ufixed256x${input.type.places}`,
        value: (<Format.Values.UfixedValue>input).value.asBig.toString()
      };
    case "bool":
      //this is the case that won't work as valid input to soliditySha3 :)
      return {
        type: "uint",
        value: (<Format.Values.BoolValue>input).value.asBoolean.toString()
      };
    case "bytes":
      switch (input.type.kind) {
        case "static":
          return {
            type: "bytes32",
            value: (<Format.Values.BytesValue>input).value.asHex
          };
        case "dynamic":
          return {
            type: "bytes",
            value: (<Format.Values.BytesValue>input).value.asHex
          };
      }
    case "address":
      return {
        type: "address",
        value: (<Format.Values.AddressValue>input).value.asAddress
      };
    case "string":
      let coercedInput: Format.Values.StringValue = <Format.Values.StringValue>(
        input
      );
      switch (coercedInput.value.kind) {
        case "valid":
          return {
            type: "string",
            value: coercedInput.value.asString
          };
        case "malformed":
          return {
            type: "bytes",
            value: coercedInput.value.asHex
          };
      }
    //fixed and ufixed are skipped for now
  }
}