Encoding & decoding
Convert between Clarity values and JavaScript types
Overview
Stacks uses Clarity values (CVs) to represent data in smart contracts. When building applications, you'll need to encode JavaScript values into CVs for contract calls and decode CVs back to JavaScript for display. This guide covers all CV types and conversion patterns.
Basic type conversions
Integers
Convert between JavaScript numbers and Clarity integers:
import {intCV,uintCV,cvToValue,cvToJSON} from '@stacks/transactions';// Encodingconst positiveInt = uintCV(42); // u42const negativeInt = intCV(-100); // -100const largeUint = uintCV(1000000); // u1000000// Decodingconst jsValue = cvToValue(positiveInt); // 42const jsonValue = cvToJSON(positiveInt); // { type: 'uint', value: '42' }// Working with BigInt for large numbersconst bigNumber = uintCV(BigInt('123456789012345678901234567890'));const decoded = cvToValue(bigNumber); // '123456789012345678901234567890'
Booleans
Simple true/false values:
import { trueCV, falseCV, boolCV } from '@stacks/transactions';// Encodingconst clarityTrue = trueCV(); // trueconst clarityFalse = falseCV(); // falseconst fromBoolean = boolCV(true); // true// Decodingconst jsBoolean = cvToValue(clarityTrue); // true
Strings
Handle ASCII and UTF-8 strings:
import {stringAsciiCV,stringUtf8CV,cvToString} from '@stacks/transactions';// ASCII strings (limited character set)const asciiString = stringAsciiCV('Hello World');const asciiDecoded = cvToValue(asciiString); // 'Hello World'// UTF-8 strings (full Unicode support)const utf8String = stringUtf8CV('Hello 世界! 🌍');const utf8Decoded = cvToValue(utf8String); // 'Hello 世界! 🌍'// Direct string extractionconst directString = cvToString(utf8String); // 'Hello 世界! 🌍'
Principals
Encode Stacks addresses and contract principals:
import {standardPrincipalCV,contractPrincipalCV,cvToValue} from '@stacks/transactions';// Standard principal (user address)const userPrincipal = standardPrincipalCV('SP2J6Y09JMFWWZCT4VJX0BA5W7A9HZP5EX96Y6VZY');// Contract principalconst contractPrincipal = contractPrincipalCV('SP2J6Y09JMFWWZCT4VJX0BA5W7A9HZP5EX96Y6VZY','my-contract');// Decodingconst address = cvToValue(userPrincipal);// 'SP2J6Y09JMFWWZCT4VJX0BA5W7A9HZP5EX96Y6VZY'const contract = cvToValue(contractPrincipal);// 'SP2J6Y09JMFWWZCT4VJX0BA5W7A9HZP5EX96Y6VZY.my-contract'
Complex type handling
Buffers
Work with binary data:
import {bufferCV,bufferCVFromString,cvToValue} from '@stacks/transactions';// From hex stringconst hexBuffer = bufferCV(Buffer.from('deadbeef', 'hex'));// From UTF-8 stringconst stringBuffer = bufferCVFromString('Hello Buffer');// From byte arrayconst byteBuffer = bufferCV(new Uint8Array([1, 2, 3, 4]));// Decoding returns hex stringconst decoded = cvToValue(hexBuffer); // '0xdeadbeef'// Get raw bufferconst rawBuffer = hexBuffer.buffer; // Buffer instance
Optional values
Handle Clarity's optional type:
import {someCV,noneCV,cvToValue} from '@stacks/transactions';// Some value (contains a value)const someValue = someCV(uintCV(42)); // (some u42)const someString = someCV(stringUtf8CV('hi')); // (some u"hi")// None value (no value)const noneValue = noneCV(); // none// Decodingconst decodedSome = cvToValue(someValue); // 42const decodedNone = cvToValue(noneValue); // null// Check optional typeif (someValue.type === ClarityType.OptionalSome) {const innerValue = cvToValue(someValue.value);}
Response values
Handle contract response types:
import {responseOkCV,responseErrorCV,cvToValue} from '@stacks/transactions';// Success responseconst okResponse = responseOkCV(uintCV(100)); // (ok u100)const okString = responseOkCV(stringUtf8CV('Success')); // (ok u"Success")// Error responseconst errorResponse = responseErrorCV(uintCV(404)); // (err u404)const errorMsg = responseErrorCV(stringUtf8CV('Not found')); // (err u"Not found")// Decoding and checkingconst result = okResponse;if (result.type === ClarityType.ResponseOk) {console.log('Success:', cvToValue(result.value));} else {console.log('Error:', cvToValue(result.value));}
Tuples
Create and decode structured data:
import {tupleCV,cvToValue,cvToJSON} from '@stacks/transactions';// Create tupleconst userInfo = tupleCV({id: uintCV(1),name: stringUtf8CV('Alice'),balance: uintCV(1000000),active: trueCV(),metadata: tupleCV({created: uintCV(Date.now()),tags: listCV([stringAsciiCV('user'), stringAsciiCV('premium')])})});// Decode to JavaScript objectconst decoded = cvToValue(userInfo);// {// id: 1,// name: 'Alice',// balance: 1000000,// active: true,// metadata: {// created: 1234567890,// tags: ['user', 'premium']// }// }// Access tuple fieldsconst nameCV = userInfo.data.name;const name = cvToValue(nameCV); // 'Alice'
Lists
Work with arrays of values:
import {listCV,cvToValue} from '@stacks/transactions';// List of same typeconst numbers = listCV([uintCV(1), uintCV(2), uintCV(3)]);const strings = listCV([stringUtf8CV('apple'),stringUtf8CV('banana'),stringUtf8CV('cherry')]);// List of tuples (common pattern)const users = listCV([tupleCV({ id: uintCV(1), name: stringUtf8CV('Alice') }),tupleCV({ id: uintCV(2), name: stringUtf8CV('Bob') }),]);// Decodingconst decodedNumbers = cvToValue(numbers); // [1, 2, 3]const decodedUsers = cvToValue(users);// [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]// Iterate over listnumbers.list.forEach((cv, index) => {console.log(`Item ${index}:`, cvToValue(cv));});
Advanced encoding patterns
Dynamic type encoding
Build encoders for runtime values:
function encodeValue(value: any): ClarityValue {if (typeof value === 'number') {return value >= 0 ? uintCV(value) : intCV(value);} else if (typeof value === 'string') {// Check if valid ASCIIif (/^[\x00-\x7F]*$/.test(value)) {return stringAsciiCV(value);}return stringUtf8CV(value);} else if (typeof value === 'boolean') {return boolCV(value);} else if (value === null || value === undefined) {return noneCV();} else if (Array.isArray(value)) {return listCV(value.map(encodeValue));} else if (typeof value === 'object') {const tupleData: { [key: string]: ClarityValue } = {};for (const [key, val] of Object.entries(value)) {tupleData[key] = encodeValue(val);}return tupleCV(tupleData);}throw new Error(`Cannot encode value: ${value}`);}// Usageconst encoded = encodeValue({name: 'Alice',age: 30,tags: ['user', 'admin'],active: true});
Type-safe decoding
Create decoders with type validation:
interface UserData {id: number;name: string;balance: number;active: boolean;}function decodeUser(cv: ClarityValue): UserData {if (cv.type !== ClarityType.Tuple) {throw new Error('Expected tuple');}const data = cv.data;// Validate and extract each fieldif (!data.id || data.id.type !== ClarityType.UInt) {throw new Error('Invalid id field');}if (!data.name || (data.name.type !== ClarityType.StringASCII &&data.name.type !== ClarityType.StringUTF8)) {throw new Error('Invalid name field');}return {id: Number(cvToValue(data.id)),name: cvToString(data.name),balance: Number(cvToValue(data.balance)),active: cvToValue(data.active) as boolean,};}
Batch encoding utilities
Encode multiple values efficiently:
class ClarityEncoder {static encodeArray<T>(items: T[],encoder: (item: T) => ClarityValue): ClarityValue {return listCV(items.map(encoder));}static encodeTuple<T extends Record<string, any>>(obj: T,schema: { [K in keyof T]: (value: T[K]) => ClarityValue }): TupleCV {const tupleData: { [key: string]: ClarityValue } = {};for (const [key, encoder] of Object.entries(schema)) {tupleData[key] = encoder(obj[key as keyof T]);}return tupleCV(tupleData);}static encodeOptional<T>(value: T | null | undefined,encoder: (value: T) => ClarityValue): OptionalCV {if (value === null || value === undefined) {return noneCV();}return someCV(encoder(value));}}// Usageconst users = [{ id: 1, name: 'Alice', balance: 1000 },{ id: 2, name: 'Bob', balance: 2000 },];const encoded = ClarityEncoder.encodeArray(users, user =>ClarityEncoder.encodeTuple(user, {id: (id) => uintCV(id),name: (name) => stringUtf8CV(name),balance: (balance) => uintCV(balance),}));
Serialization and deserialization
Work with serialized Clarity values:
import {serializeCV,deserializeCV,cvToHex,hexToCV} from '@stacks/transactions';// Serialize to bufferconst cv = tupleCV({ amount: uintCV(1000), memo: stringUtf8CV('Payment') });const serialized = serializeCV(cv); // Buffer// Convert to hex for storage/transportconst hex = cvToHex(cv); // '0x0c00000002046d656d6f...'// Deserialize from hexconst deserialized = hexToCV(hex);const value = cvToValue(deserialized); // { amount: 1000, memo: 'Payment' }// Work with raw buffersconst buffer = Buffer.from(hex, 'hex');const fromBuffer = deserializeCV(buffer);
Common conversion patterns
Contract call arguments
Prepare arguments for contract calls:
function prepareTransferArgs(recipient: string,amount: number,memo?: string): ClarityValue[] {const args = [standardPrincipalCV(recipient),uintCV(amount),];if (memo) {args.push(someCV(stringUtf8CV(memo)));} else {args.push(noneCV());}return args;}// Usage in contract callconst functionArgs = prepareTransferArgs('SP2J6Y09JMFWWZCT4VJX0BA5W7A9HZP5EX96Y6VZY',1000000,'Monthly payment');
Response handling
Process contract responses:
function handleContractResponse(response: ClarityValue): {success: boolean;data: any;error?: string;} {if (response.type === ClarityType.ResponseOk) {return {success: true,data: cvToValue(response.value),};} else if (response.type === ClarityType.ResponseErr) {const errorValue = cvToValue(response.value);return {success: false,data: null,error: typeof errorValue === 'string' ? errorValue : `Error: ${errorValue}`,};}throw new Error('Invalid response type');}
Best practices
- Validate types: Always check CV types before decoding
- Handle edge cases: Consider null, undefined, and empty values
- Use appropriate string types: ASCII for simple text, UTF-8 for international
- Preserve precision: Use BigInt for large numbers
- Type narrowing: Use TypeScript type guards for safety
Common mistakes
String type confusion
// Bad: Using ASCII for Unicodeconst bad = stringAsciiCV('Hello 世界'); // Will throw error// Good: Use UTF-8 for Unicodeconst good = stringUtf8CV('Hello 世界');
Number overflow
// Bad: JavaScript number too largeconst bad = uintCV(Number.MAX_SAFE_INTEGER + 1); // Precision loss// Good: Use BigIntconst good = uintCV(BigInt('9007199254740992'));