CID.fromString constructor

CID.fromString(
  1. String s
)

Decodes a CID from its string representation.

Supports:

  • CIDv0: Base58btc encoded multihashes (e.g., "Qm...").
  • CIDv1: Multibase-prefixed strings. Currently, it specifically handles:
    • Base32 (prefix 'b', e.g., "bafy...") - canonical form.
    • Base58btc (prefix 'z', e.g., "zdj7...") - common alternative.

Throws FormatException if the string is empty, has an unsupported multibase prefix, or is otherwise malformed.

  • s: The CID string to parse.

Example:

final cidV0 = CID.fromString('QmWvQxTqbG2Z9HPJgG57jjwR154cKhbtJenbyYTWkjgF3e');
final cidV1 = CID.fromString('bafybeid7qoywk77r7rj3slobqfekdvs57qwuwh5d2z3sqsw52iabe3mqne');

Implementation

factory CID.fromString(String s) {
  if (s.isEmpty) {
    throw FormatException('Cannot parse CID from empty string');
  }

  // CIDv0: Starts with "Qm", is Base58btc encoded multihash.
  // Length of a sha2-256 multihash (32-byte hash + 2-byte prefix) is 34 bytes.
  // Base58 encoded, it's typically 46 characters long (e.g. Qm...).
  if (s.length == 46 && s.startsWith('Qm')) {
    try {
      final multihashBytes = mb.base58.decode(s);
      // Validate it's a sha2-256 multihash
      final decodedMh = mh.Multihash.decode(multihashBytes);
      // Assuming 'digest' is the property holding the hash bytes, and its length is what we need.
      if (decodedMh.code == codecNameToCode['sha2-256']! && decodedMh.digest.length == 32) {
        return CID(V0, codecNameToCode['dag-pb']!, multihashBytes);
      } else {
        throw FormatException('CIDv0 string is not a valid sha2-256 multihash');
      }
    } catch (e) {
      throw FormatException('Failed to parse CIDv0 string "$s": $e');
    }
  }

  // CIDv1: Starts with a multibase prefix. For canonical form, this is 'b' (base32).
  if (s.length > 1) {
    final prefix = s[0];
    final rest = s.substring(1);

    // For now, we only support base32 ('b') for CIDv1 string parsing
    // and base58btc ('z') as a common alternative for CIDv1.
    // A full multibase implementation would be more robust.
    if (prefix == 'b') { // base32
      try {
        // The base32 package might require uppercase and padding for decoding.
        // The input 'rest' should be base32 encoded data.
        // Standard Base32 alphabet: ABCDEFGHIJKLMNOPQRSTUVWXYZ234567
        // We need to handle potential lowercase input by converting to uppercase.
        // Padding might also need to be re-added if the decoder expects it.
        String toDecode = rest.toUpperCase();
        // Add padding if necessary for the decoder
        final missingPadding = (8 - (toDecode.length % 8)) % 8;
        toDecode += '=' * missingPadding;

        final decodedBytes = b32.base32.decode(toDecode);
        return CID.fromBytes(decodedBytes);
      } catch (e) {
         throw FormatException('Failed to parse CIDv1 base32 string "$s": $e');
      }
    } else if (prefix == 'z') { // base58btc (often used for CIDv1 too)
       try {
        final decodedBytes = mb.base58.decode(rest);
        return CID.fromBytes(decodedBytes);
      } catch (e) {
         throw FormatException('Failed to parse CIDv1 base58btc string "$s": $e');
      }
    }
    // Add other multibase prefixes if needed
  }
  throw FormatException('Invalid CID string format: "$s"');
}