abiDecodeString function
Decodes an ABI-encoded dynamic string (offset + length + data).
Returns an empty string when hexData is too short, malformed, or
declares a length that exceeds the actual payload. Callers can rely on
this function never throwing for untrusted on-chain data.
Implementation
String abiDecodeString(String hexData) {
final clean = hexData.startsWith('0x') ? hexData.substring(2) : hexData;
if (clean.length < 128) return '';
// Word 0: offset (should be 0x20 = 32)
// Word 1: length
final lengthHex = clean.substring(64, 128);
final int length;
try {
length = int.parse(lengthHex, radix: 16);
} on FormatException catch (_) {
return '';
}
if (length <= 0) return '';
// Data starts at hex char offset 128. Guard against contracts that declare
// a longer string than they actually pack — without this check,
// `substring(128, 128 + length * 2)` would throw RangeError for truncated
// payloads and escape through callers that assume ABI decode is total.
final int dataEnd = 128 + length * 2;
if (clean.length < dataEnd) return '';
final dataHex = clean.substring(128, dataEnd);
final bytes = <int>[];
for (int i = 0; i < dataHex.length; i += 2) {
try {
bytes.add(int.parse(dataHex.substring(i, i + 2), radix: 16));
} on FormatException catch (_) {
return '';
}
}
return String.fromCharCodes(bytes);
}