/**
   * Determines if the address is valid.
   *
   * @return true if the address is valid.
   */
  public static boolean isValid(@Nullable final String value) {
    if (value == null) {
      return false;
    }
    // this check should prevent leading and trailing whitespace
    if (NUM_DECODED_BYTES_LENGTH != value.length()) {
      return false;
    }

    final byte[] encodedBytes;

    try {
      encodedBytes = Base32Encoder.getBytes(value);
    } catch (final IllegalArgumentException e) {
      return false;
    }
    if (NUM_ENCODED_BYTES_LENGTH != encodedBytes.length) {
      return false;
    }

    if (AppConstants.NETWORK_VERSION.get() != encodedBytes[0]) {
      return false;
    }

    final int checksumStartIndex = NUM_ENCODED_BYTES_LENGTH - NUM_CHECKSUM_BYTES;
    final byte[] versionPrefixedHash = Arrays.copyOfRange(encodedBytes, 0, checksumStartIndex);
    final byte[] addressChecksum =
        Arrays.copyOfRange(
            encodedBytes, checksumStartIndex, checksumStartIndex + NUM_CHECKSUM_BYTES);
    final byte[] calculatedChecksum = generateChecksum(versionPrefixedHash);
    return Arrays.equals(addressChecksum, calculatedChecksum);
  }
  private static String generateEncoded(final byte version, final byte[] publicKey) {
    // step 1: sha3 hash of the public key
    final byte[] sha3PublicKeyHash = Hashes.sha3_256(publicKey);

    // step 2: ripemd160 hash of (1)
    final byte[] ripemd160StepOneHash = Hashes.ripemd160(sha3PublicKeyHash);

    // step 3: store version byte in front of (2)
    final byte[] versionPrefixedRipemd160Hash =
        ArrayUtils.concat(new byte[] {version}, ripemd160StepOneHash);

    // step 4: get the checksum of (3)
    final byte[] stepThreeChecksum = generateChecksum(versionPrefixedRipemd160Hash);

    // step 5: concatenate (3) and (4)
    final byte[] concatStepThreeAndStepSix =
        ArrayUtils.concat(versionPrefixedRipemd160Hash, stepThreeChecksum);

    // step 6: base32 encode (5)
    return Base32Encoder.getString(concatStepThreeAndStepSix);
  }