docs: function encodeSingle (type, arg) {
    var size, num, ret, i
  
    if (type === 'address') {
      return encodeSingle('uint160', parseNumber(arg))
    } else if (type === 'bool') {
      return encodeSingle('uint8', arg ? 1 : 0)
    } else if (type === 'string') {
      return encodeSingle('bytes', Buffer.from(arg, 'utf8'))
    } else if (isArray(type)) {
      // this part handles fixed-length ([2]) and variable length ([]) arrays
      // NOTE: we catch here all calls to arrays, that simplifies the rest
      if (typeof arg.length === 'undefined') {
        throw new Error('Not an array?')
      }
      size = parseTypeArray(type)
      if (size !== 'dynamic' && size !== 0 && arg.length > size) {
        throw new Error('Elements exceed array size: ' + size)
      }
      ret = []
      type = type.slice(0, type.lastIndexOf('['))
      if (typeof arg === 'string') {
        arg = JSON.parse(arg)
      }
      for (i in arg) {
        ret.push(encodeSingle(type, arg[i]))
      }
      if (size === 'dynamic') {
        var length = encodeSingle('uint256', arg.length)
        ret.unshift(length)
      }
      return Buffer.concat(ret)
    } else if (type === 'bytes') {
      arg = Buffer.from(arg)
  
      ret = Buffer.concat([ encodeSingle('uint256', arg.length), arg ])
  
      if ((arg.length % 32) !== 0) {
        ret = Buffer.concat([ ret, utils.zeros(32 - (arg.length % 32)) ])
      }
  
      return ret
    } else if (type.startsWith('bytes')) {
      size = parseTypeN(type)
      if (size < 1 || size > 32) {
        throw new Error('Invalid bytes<N> width: ' + size)
      }
  
      return utils.setLengthRight(arg, 32)
    } else if (type.startsWith('uint')) {
      size = parseTypeN(type)
      if ((size % 8) || (size < 8) || (size > 256)) {
        throw new Error('Invalid uint<N> width: ' + size)
      }
  
      num = parseNumber(arg)
      if (num.bitLength() > size) {
        throw new Error('Supplied uint exceeds width: ' + size + ' vs ' + num.bitLength())
      }
  
      if (num < 0) {
        throw new Error('Supplied uint is negative')
      }
  
      return num.toArrayLike(Buffer, 'be', 32)
    } else if (type.startsWith('int')) {
      size = parseTypeN(type)
      if ((size % 8) || (size < 8) || (size > 256)) {
        throw new Error('Invalid int<N> width: ' + size)
      }
  
      num = parseNumber(arg)
      if (num.bitLength() > size) {
        throw new Error('Supplied int exceeds width: ' + size + ' vs ' + num.bitLength())
      }
  
      return num.toTwos(256).toArrayLike(Buffer, 'be', 32)
    } else if (type.startsWith('ufixed')) {
      size = parseTypeNxM(type)
  
      num = parseNumber(arg)
  
      if (num < 0) {
        throw new Error('Supplied ufixed is negative')
      }
  
      return encodeSingle('uint256', num.mul(new BN(2).pow(new BN(size[1]))))
    } else if (type.startsWith('fixed')) {
      size = parseTypeNxM(type)
  
      return encodeSingle('int256', parseNumber(arg).mul(new BN(2).pow(new BN(size[1]))))
    }
  
    throw new Error('Unsupported or invalid type: ' + type)
  }