ABI String Type
Description
Section titled βDescriptionβThis example demonstrates how to encode and decode dynamic strings using ABIStringType:
- ABIStringType encodes strings with a 2-byte length prefix followed by UTF-8 content
- Shows encoding of empty strings, ASCII text, and Unicode characters
- Demonstrates that strings are dynamic types (variable length)
- Displays byte breakdown: length prefix vs content bytes
Prerequisites
Section titled βPrerequisitesβ- No LocalNet required
Run This Example
Section titled βRun This ExampleβFrom the repository root:
cd examplesnpm run example abi/04-string-type.ts/** * Example: ABI String Type * * This example demonstrates how to encode and decode dynamic strings using ABIStringType: * - ABIStringType encodes strings with a 2-byte length prefix followed by UTF-8 content * - Shows encoding of empty strings, ASCII text, and Unicode characters * - Demonstrates that strings are dynamic types (variable length) * - Displays byte breakdown: length prefix vs content bytes * * Prerequisites: * - No LocalNet required */
import { ABIStringType } from '@algorandfoundation/algokit-utils/abi';import { formatHex, printHeader, printInfo, printStep, printSuccess } from '../shared/utils.js';
function main() { printHeader('ABI String Type Example');
// Step 1: ABIStringType basics printStep(1, 'ABIStringType - Basic Properties');
const stringType = new ABIStringType();
printInfo(`type: ${stringType.toString()}`); printInfo(`isDynamic: ${stringType.isDynamic()}`); printInfo('Note: Strings are dynamic types - their encoded length varies with content');
// Step 2: Encoding an empty string printStep(2, 'Encoding Empty String');
const emptyString = ''; const emptyEncoded = stringType.encode(emptyString); const emptyDecoded = stringType.decode(emptyEncoded);
printInfo(`string value: "" (empty)`); printInfo(` encoded: ${formatHex(emptyEncoded)}`); printInfo(` total bytes: ${emptyEncoded.length}`); printInfo( ` length prefix (2 bytes): ${formatHex(emptyEncoded.slice(0, 2))} = ${(emptyEncoded[0] << 8) | emptyEncoded[1]}`, ); printInfo(` content bytes: ${emptyEncoded.length - 2}`); printInfo(` decoded: "${emptyDecoded}"`); printInfo(` round-trip verified: ${emptyDecoded === emptyString}`);
// Step 3: Encoding a short ASCII string printStep(3, 'Encoding Short ASCII String');
const helloString = 'Hello'; const helloEncoded = stringType.encode(helloString); const helloDecoded = stringType.decode(helloEncoded);
printInfo(`string value: "${helloString}"`); printInfo(` encoded: ${formatHex(helloEncoded)}`); printInfo(` total bytes: ${helloEncoded.length}`); printInfo( ` length prefix (2 bytes): ${formatHex(helloEncoded.slice(0, 2))} = ${(helloEncoded[0] << 8) | helloEncoded[1]}`, ); printInfo(` content bytes: ${helloEncoded.length - 2}`);
// Show individual character encoding printInfo('\n Byte breakdown:'); printInfo( ` [0-1] Length prefix: ${formatHex(helloEncoded.slice(0, 2))} (${helloString.length})`, ); for (let i = 0; i < helloString.length; i++) { const charByte = helloEncoded[i + 2]; printInfo( ` [${i + 2}] '${helloString[i]}' -> 0x${charByte.toString(16).padStart(2, '0')} (${charByte})`, ); }
printInfo(`\n decoded: "${helloDecoded}"`); printInfo(` round-trip verified: ${helloDecoded === helloString}`);
// Step 4: Encoding a longer ASCII string printStep(4, 'Encoding Longer ASCII String');
const loremString = 'The quick brown fox jumps over the lazy dog.'; const loremEncoded = stringType.encode(loremString); const loremDecoded = stringType.decode(loremEncoded);
printInfo(`string value: "${loremString}"`); printInfo(` encoded: ${formatHex(loremEncoded)}`); printInfo(` total bytes: ${loremEncoded.length}`); printInfo( ` length prefix (2 bytes): ${formatHex(loremEncoded.slice(0, 2))} = ${(loremEncoded[0] << 8) | loremEncoded[1]}`, ); printInfo(` content bytes: ${loremEncoded.length - 2}`); printInfo(` decoded: "${loremDecoded}"`); printInfo(` round-trip verified: ${loremDecoded === loremString}`);
// Step 5: Encoding Unicode characters printStep(5, 'Encoding Unicode Characters');
const unicodeString = 'Hello, δΈη! π'; const unicodeEncoded = stringType.encode(unicodeString); const unicodeDecoded = stringType.decode(unicodeEncoded);
printInfo(`string value: "${unicodeString}"`); printInfo(` encoded: ${formatHex(unicodeEncoded)}`); printInfo(` total bytes: ${unicodeEncoded.length}`); printInfo( ` length prefix (2 bytes): ${formatHex(unicodeEncoded.slice(0, 2))} = ${(unicodeEncoded[0] << 8) | unicodeEncoded[1]}`, ); printInfo(` content bytes: ${unicodeEncoded.length - 2}`); printInfo(` JS string length: ${unicodeString.length} (characters)`); printInfo(` UTF-8 byte length: ${unicodeEncoded.length - 2} (bytes)`); printInfo(' Note: Unicode characters may use multiple bytes in UTF-8 encoding'); printInfo(` decoded: "${unicodeDecoded}"`); printInfo(` round-trip verified: ${unicodeDecoded === unicodeString}`);
// Step 6: Encoding emoji-only string printStep(6, 'Encoding Emoji String');
const emojiString = 'πππ»'; const emojiEncoded = stringType.encode(emojiString); const emojiDecoded = stringType.decode(emojiEncoded);
printInfo(`string value: "${emojiString}"`); printInfo(` encoded: ${formatHex(emojiEncoded)}`); printInfo(` total bytes: ${emojiEncoded.length}`); printInfo( ` length prefix (2 bytes): ${formatHex(emojiEncoded.slice(0, 2))} = ${(emojiEncoded[0] << 8) | emojiEncoded[1]}`, ); printInfo(` content bytes: ${emojiEncoded.length - 2}`); printInfo(` JS string length: ${emojiString.length} (UTF-16 code units, emojis use 2 each)`); printInfo(` UTF-8 byte length: ${emojiEncoded.length - 2} (bytes, emojis use 4 bytes each)`); printInfo(` decoded: "${emojiDecoded}"`); printInfo(` round-trip verified: ${emojiDecoded === emojiString}`);
// Step 7: Maximum length demonstration printStep(7, 'String Length Limits');
printInfo('String encoding uses a 2-byte (uint16) length prefix:'); printInfo(' Maximum string length: 65535 bytes (2^16 - 1)'); printInfo(' Length prefix is big-endian encoded');
// Demonstrate a string that would have a length > 255 (requiring both bytes) const longString = 'A'.repeat(300); const longEncoded = stringType.encode(longString);
printInfo(`\nExample with 300-character string:`); printInfo(` length prefix: ${formatHex(longEncoded.slice(0, 2))}`); printInfo(` high byte: 0x${longEncoded[0].toString(16).padStart(2, '0')} = ${longEncoded[0]}`); printInfo(` low byte: 0x${longEncoded[1].toString(16).padStart(2, '0')} = ${longEncoded[1]}`); printInfo( ` decoded length: (${longEncoded[0]} << 8) | ${longEncoded[1]} = ${(longEncoded[0] << 8) | longEncoded[1]}`, );
// Step 8: Summary printStep(8, 'Summary - Dynamic Type Behavior');
printInfo('Key points about ABIStringType:'); printInfo(' - Strings are dynamic types (isDynamic() returns true)'); printInfo(' - Encoding format: 2-byte length prefix + UTF-8 content'); printInfo(' - Length prefix is big-endian (most significant byte first)'); printInfo(' - UTF-8 encoding means characters may use 1-4 bytes'); printInfo(' - Maximum string length: 65535 bytes');
printSuccess('ABI String Type example completed successfully!');}
main();