text: "use strict";
  /**
   * @protected
   *
   * @packageDocumentation
   */
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
      if (k2 === undefined) k2 = k;
      Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
  }) : (function(o, m, k, k2) {
      if (k2 === undefined) k2 = k;
      o[k2] = m[k];
  }));
  var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
      Object.defineProperty(o, "default", { enumerable: true, value: v });
  }) : function(o, v) {
      o["default"] = v;
  });
  var __importStar = (this && this.__importStar) || function (mod) {
      if (mod && mod.__esModule) return mod;
      var result = {};
      if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
      __setModuleDefault(result, mod);
      return result;
  };
  var __importDefault = (this && this.__importDefault) || function (mod) {
      return (mod && mod.__esModule) ? mod : { "default": mod };
  };
  Object.defineProperty(exports, "__esModule", { value: true });
  exports.decodeAbiReferenceStatic = exports.decodeAbiReferenceByAddress = exports.decodeAbi = void 0;
  const debug_1 = __importDefault(require("debug"));
  const debug = debug_1.default("codec:abi-data:decode");
  const read_1 = __importDefault(require("../../read"));
  const Conversion = __importStar(require("../../conversion"));
  const Basic = __importStar(require("../../basic"));
  const Bytes = __importStar(require("../../bytes"));
  const Format = __importStar(require("../../format"));
  const Evm = __importStar(require("../../evm"));
  const allocate_1 = require("../allocate");
  const errors_1 = require("../../errors");
  function* decodeAbi(dataType, pointer, info, options = {}) {
      if (Format.Types.isReferenceType(dataType) ||
          dataType.typeClass === "tuple") {
          //I don't want tuples to be considered a reference type, but it makes sense
          //to group them for this purpose
          let dynamic;
          try {
              dynamic = allocate_1.abiSizeInfo(dataType, info.allocations.abi).dynamic;
          }
          catch (error) {
              return errors_1.handleDecodingError(dataType, error, options.strictAbiMode);
          }
          if (dynamic) {
              return yield* decodeAbiReferenceByAddress(dataType, pointer, info, options);
          }
          else {
              return yield* decodeAbiReferenceStatic(dataType, pointer, info, options);
          }
      }
      else {
          debug("pointer %o", pointer);
          return yield* Basic.Decode.decodeBasic(dataType, pointer, info, options);
      }
  }
  exports.decodeAbi = decodeAbi;
  function* decodeAbiReferenceByAddress(dataType, pointer, info, options = {}) {
      let { strictAbiMode: strict, abiPointerBase: base, lengthOverride } = options;
      base = base || 0; //in case base was undefined
      const { allocations: { abi: allocations }, state } = info;
      debug("pointer %o", pointer);
      //this variable holds the location we should look to *next*
      //stack pointers point to calldata; other pointers point to same location
      const location = pointer.location === "stack" || pointer.location === "stackliteral"
          ? "calldata"
          : pointer.location;
      if (pointer.location !== "stack" && pointer.location !== "stackliteral") {
          //length overrides are only applicable when you're decoding a pointer
          //from the stack!  otherwise they must be ignored!
          lengthOverride = undefined;
      }
      let rawValue;
      try {
          rawValue = yield* read_1.default(pointer, state);
      }
      catch (error) {
          return errors_1.handleDecodingError(dataType, error, strict);
      }
      let rawValueAsBN = Conversion.toBN(rawValue);
      debug("rawValue: %O", rawValue);
      debug("rawValueAsBN: %O", rawValueAsBN);
      let rawValueAsNumber;
      try {
          rawValueAsNumber = rawValueAsBN.toNumber();
      }
      catch (_a) {
          let error = {
              kind: "OverlargePointersNotImplementedError",
              pointerAsBN: rawValueAsBN
          };
          if (strict) {
              throw new errors_1.StopDecodingError(error);
          }
          return {
              //again with the TS failures...
              type: dataType,
              kind: "error",
              error
          };
      }
      let startPosition = rawValueAsNumber + base;
      debug("startPosition %d", startPosition);
      let dynamic;
      let size;
      try {
          ({ dynamic, size } = allocate_1.abiSizeInfo(dataType, allocations));
      }
      catch (error) {
          return errors_1.handleDecodingError(dataType, error, strict);
      }
      if (!dynamic) {
          //this will only come up when called from stack.ts
          let staticPointer = {
              location,
              start: startPosition,
              length: size
          };
          return yield* decodeAbiReferenceStatic(dataType, staticPointer, info, options);
      }
      let length;
      let lengthAsBN;
      let rawLength;
      switch (dataType.typeClass) {
          case "bytes":
          case "string":
              //initial word contains length (unless an override was given)
              if (lengthOverride !== undefined) {
                  lengthAsBN = lengthOverride;
                  //note in this case we do *not* increment start position;
                  //if a length override is given, that means the given start
                  //position skips over the length word!
              }
              else {
                  try {
                      rawLength = yield* read_1.default({
                          location,
                          start: startPosition,
                          length: Evm.Utils.WORD_SIZE
                      }, state);
                  }
                  catch (error) {
                      return errors_1.handleDecodingError(dataType, error, strict);
                  }
                  lengthAsBN = Conversion.toBN(rawLength);
                  startPosition += Evm.Utils.WORD_SIZE; //increment start position after reading length
                  //so it'll be set up to read the data
              }
              if (strict && lengthAsBN.gtn(state[location].length)) {
                  //you may notice that the comparison is a bit crude; that's OK, this is
                  //just to prevent huge numbers from DOSing us, other errors will still
                  //be caught regardless
                  throw new errors_1.StopDecodingError({
                      kind: "OverlongArrayOrStringStrictModeError",
                      lengthAsBN,
                      dataLength: state[location].length
                  });
              }
              try {
                  length = lengthAsBN.toNumber();
              }
              catch (_b) {
                  //note: if we're in this situation, we can assume we're not in strict mode,
                  //as the strict case was handled above
                  return {
                      //again with the TS failures...
                      type: dataType,
                      kind: "error",
                      error: {
                          kind: "OverlongArraysAndStringsNotImplementedError",
                          lengthAsBN
                      }
                  };
              }
              let childPointer = {
                  location,
                  start: startPosition,
                  length
              };
              return yield* Bytes.Decode.decodeBytes(dataType, childPointer, info, options);
          case "array":
              if (dataType.kind === "static") {
                  //static-length array
                  lengthAsBN = dataType.length;
                  //note we don't increment start position; static arrays don't
                  //include a length word!
              }
              else if (lengthOverride !== undefined) {
                  debug("override: %o", lengthOverride);
                  //dynamic-length array, but with length override
                  lengthAsBN = lengthOverride;
                  //we don't increment start position; if a length override was
                  //given, that means the pointer skipped the length word!
              }
              else {
                  //dynamic-length array, read length from data
                  //initial word contains array length
                  try {
                      rawLength = yield* read_1.default({
                          location,
                          start: startPosition,
                          length: Evm.Utils.WORD_SIZE
                      }, state);
                  }
                  catch (error) {
                      return errors_1.handleDecodingError(dataType, error, strict);
                  }
                  lengthAsBN = Conversion.toBN(rawLength);
                  startPosition += Evm.Utils.WORD_SIZE; //increment startPosition
                  //to next word, as first word was used for length
              }
              if (strict && lengthAsBN.gtn(state[location].length)) {
                  //you may notice that the comparison is a bit crude; that's OK, this is
                  //just to prevent huge numbers from DOSing us, other errors will still
                  //be caught regardless
                  throw new errors_1.StopDecodingError({
                      kind: "OverlongArraysAndStringsNotImplementedError",
                      lengthAsBN,
                      dataLength: state[location].length
                  });
              }
              try {
                  length = lengthAsBN.toNumber();
              }
              catch (_c) {
                  //again, if we get here, we can assume we're not in strict mode
                  return {
                      type: dataType,
                      kind: "error",
                      error: {
                          kind: "OverlongArraysAndStringsNotImplementedError",
                          lengthAsBN
                      }
                  };
              }
              //note: I've written this fairly generically, but it is worth noting that
              //since this array is of dynamic type, we know that if it's static length
              //then size must be EVM.WORD_SIZE
              let baseSize;
              try {
                  baseSize = allocate_1.abiSizeInfo(dataType.baseType, allocations).size;
              }
              catch (error) {
                  return errors_1.handleDecodingError(dataType, error, strict);
              }
              let decodedChildren = [];
              for (let index = 0; index < length; index++) {
                  decodedChildren.push(yield* decodeAbi(dataType.baseType, {
                      location,
                      start: startPosition + index * baseSize,
                      length: baseSize
                  }, info, Object.assign(Object.assign({}, options), { abiPointerBase: startPosition }))); //pointer base is always start of list, never the length
              }
              return {
                  type: dataType,
                  kind: "value",
                  value: decodedChildren
              };
          case "struct":
              return yield* decodeAbiStructByPosition(dataType, location, startPosition, info, options);
          case "tuple":
              return yield* decodeAbiTupleByPosition(dataType, location, startPosition, info, options);
      }
  }
  exports.decodeAbiReferenceByAddress = decodeAbiReferenceByAddress;
  function* decodeAbiReferenceStatic(dataType, pointer, info, options = {}) {
      debug("static");
      debug("pointer %o", pointer);
      const location = pointer.location;
      switch (dataType.typeClass) {
          case "array":
              //we're in the static case, so we know the array must be statically sized
              const lengthAsBN = dataType.length;
              let length;
              try {
                  length = lengthAsBN.toNumber();
              }
              catch (_a) {
                  //note: since this is the static case, we don't bother including the stronger
                  //strict-mode guard against getting DOSed by large array sizes, since in this
                  //case we're not reading the size from the input; if there's a huge static size
                  //array, well, we'll just have to deal with it
                  let error = {
                      kind: "OverlongArraysAndStringsNotImplementedError",
                      lengthAsBN
                  };
                  if (options.strictAbiMode) {
                      throw new errors_1.StopDecodingError(error);
                  }
                  return {
                      type: dataType,
                      kind: "error",
                      error
                  };
              }
              let baseSize;
              try {
                  baseSize = allocate_1.abiSizeInfo(dataType.baseType, info.allocations.abi).size;
              }
              catch (error) {
                  return errors_1.handleDecodingError(dataType, error, options.strictAbiMode);
              }
              let decodedChildren = [];
              for (let index = 0; index < length; index++) {
                  decodedChildren.push(yield* decodeAbi(dataType.baseType, {
                      location,
                      start: pointer.start + index * baseSize,
                      length: baseSize
                  }, info, options));
              }
              return {
                  type: dataType,
                  kind: "value",
                  value: decodedChildren
              };
          case "struct":
              return yield* decodeAbiStructByPosition(dataType, location, pointer.start, info, options);
          case "tuple":
              return yield* decodeAbiTupleByPosition(dataType, location, pointer.start, info, options);
      }
  }
  exports.decodeAbiReferenceStatic = decodeAbiReferenceStatic;
  //note that this function takes the start position as a *number*; it does not take a pointer
  function* decodeAbiStructByPosition(dataType, location, startPosition, info, options = {}) {
      const { allocations: { abi: allocations } } = info;
      const typeLocation = location === "calldata" ? "calldata" : null; //other abi locations are not valid type locations
      const typeId = dataType.id;
      const structAllocation = allocations[typeId];
      if (!structAllocation) {
          let error = {
              kind: "UserDefinedTypeNotFoundError",
              type: dataType
          };
          if (options.strictAbiMode || options.allowRetry) {
              throw new errors_1.StopDecodingError(error, true);
              //note that we allow a retry if we couldn't locate the allocation!
          }
          return {
              type: dataType,
              kind: "error",
              error
          };
      }
      let decodedMembers = [];
      for (let index = 0; index < structAllocation.members.length; index++) {
          const memberAllocation = structAllocation.members[index];
          const memberPointer = memberAllocation.pointer;
          const childPointer = {
              location,
              start: startPosition + memberPointer.start,
              length: memberPointer.length
          };
          let memberName = memberAllocation.name;
          let memberType = Format.Types.specifyLocation(memberAllocation.type, typeLocation);
          decodedMembers.push({
              name: memberName,
              value: yield* decodeAbi(memberType, childPointer, info, Object.assign(Object.assign({}, options), { abiPointerBase: startPosition }))
              //note that the base option is only needed in the dynamic case, but we're being indiscriminate
          });
      }
      return {
          type: dataType,
          kind: "value",
          value: decodedMembers
      };
  }
  //note that this function takes the start position as a *number*; it does not take a pointer
  function* decodeAbiTupleByPosition(dataType, location, startPosition, info, options = {}) {
      //WARNING: This case is written in a way that involves a bunch of unnecessary recomputation!
      //I'm writing it this way anyway for simplicity, to avoid rewriting the decoder
      //However it may be worth revisiting this in the future if performance turns out to be a problem
      //(changing this may be pretty hard though)
      let decodedMembers = [];
      let position = startPosition;
      for (const { name, type: memberType } of dataType.memberTypes) {
          const memberSize = allocate_1.abiSizeInfo(memberType, info.allocations.abi).size;
          const childPointer = {
              location,
              start: position,
              length: memberSize
          };
          decodedMembers.push({
              name,
              value: yield* decodeAbi(memberType, childPointer, info, Object.assign(Object.assign({}, options), { abiPointerBase: startPosition }))
              //note that the base option is only needed in the dynamic case, but we're being indiscriminate
          });
          position += memberSize;
      }
      return {
          type: dataType,
          kind: "value",
          value: decodedMembers
      };
  }
  //# sourceMappingURL=index.js.map