docs: function decodeSingle (parsedType, data, offset) {
    if (typeof parsedType === 'string') {
      parsedType = parseType(parsedType)
    }
    var size, num, ret, i
  
    if (parsedType.name === 'address') {
      return decodeSingle(parsedType.rawType, data, offset).toArrayLike(Buffer, 'be', 20).toString('hex')
    } else if (parsedType.name === 'bool') {
      return decodeSingle(parsedType.rawType, data, offset).toString() === new BN(1).toString()
    } else if (parsedType.name === 'string') {
      var bytes = decodeSingle(parsedType.rawType, data, offset)
      return Buffer.from(bytes, 'utf8').toString()
    } else if (parsedType.isArray) {
      // this part handles fixed-length arrays ([2]) and variable length ([]) arrays
      // NOTE: we catch here all calls to arrays, that simplifies the rest
      ret = []
      size = parsedType.size
  
      if (parsedType.size === 'dynamic') {
        offset = decodeSingle('uint256', data, offset).toNumber()
        size = decodeSingle('uint256', data, offset).toNumber()
        offset = offset + 32
      }
      for (i = 0; i < size; i++) {
        var decoded = decodeSingle(parsedType.subArray, data, offset)
        ret.push(decoded)
        offset += parsedType.subArray.memoryUsage
      }
      return ret
    } else if (parsedType.name === 'bytes') {
      offset = decodeSingle('uint256', data, offset).toNumber()
      size = decodeSingle('uint256', data, offset).toNumber()
      return data.slice(offset + 32, offset + 32 + size)
    } else if (parsedType.name.startsWith('bytes')) {
      return data.slice(offset, offset + parsedType.size)
    } else if (parsedType.name.startsWith('uint')) {
      num = new BN(data.slice(offset, offset + 32), 16, 'be')
      if (num.bitLength() > parsedType.size) {
        throw new Error('Decoded int exceeds width: ' + parsedType.size + ' vs ' + num.bitLength())
      }
      return num
    } else if (parsedType.name.startsWith('int')) {
      num = new BN(data.slice(offset, offset + 32), 16, 'be').fromTwos(256)
      if (num.bitLength() > parsedType.size) {
        throw new Error('Decoded uint exceeds width: ' + parsedType.size + ' vs ' + num.bitLength())
      }
  
      return num
    } else if (parsedType.name.startsWith('ufixed')) {
      size = new BN(2).pow(new BN(parsedType.size[1]))
      num = decodeSingle('uint256', data, offset)
      if (!num.mod(size).isZero()) {
        throw new Error('Decimals not supported yet')
      }
      return num.div(size)
    } else if (parsedType.name.startsWith('fixed')) {
      size = new BN(2).pow(new BN(parsedType.size[1]))
      num = decodeSingle('int256', data, offset)
      if (!num.mod(size).isZero()) {
        throw new Error('Decimals not supported yet')
      }
      return num.div(size)
    }
    throw new Error('Unsupported or invalid type: ' + parsedType.name)
  }