All files / src/clarity serialize.ts

98.63% Statements 72/73
100% Branches 19/19
100% Functions 16/16
98.61% Lines 71/72

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177                    21x   21x                   21x       242x 242x 242x 242x 242x       64x       13x 7x   6x         28x 28x   28x 28x       14x 14x 14x       16x 16x 16x       81x       8x             12x       10x   10x   10x   10x 15x 15x     10x       28x   28x 28x 28x   28x   28x 147x 147x 147x     28x 123x 123x   123x 123x     28x       39x 39x 39x 39x 39x   39x   39x 39x 39x       33x       6x     21x       64x     13x   28x   14x   16x   81x   8x     12x   10x   28x   33x   6x          
import {
  asciiToBytes,
  BufferArray,
  compare,
  hexToBytes,
  intToHexString,
  toTwos,
  utf8ToBytes,
  writeUInt32BE,
  concatByteArrays,
} from 'micro-stacks/common';
 
import { ClarityType } from './common/constants';
import { BooleanCV } from './types/booleanCV';
import { IntCV, UIntCV } from './types/intCV';
import { BufferCV } from './types/bufferCV';
import { OptionalCV } from './types/optionalCV';
import { ResponseCV } from './types/responseCV';
import { ContractPrincipalCV, StandardPrincipalCV } from './types/principalCV';
import { ListCV } from './types/listCV';
import { TupleCV } from './types/tupleCV';
import { StringAsciiCV, StringUtf8CV } from './types/stringCV';
import { createLPString, serializeAddress, serializeLPString } from './common/utils';
import { ClarityValue } from './clarity-value/types';
 
function bufferWithTypeID(typeId: ClarityType, buffer: Uint8Array): Uint8Array {
  const buffers = new BufferArray();
  const id = Uint8Array.from([typeId]);
  buffers.push(id);
  buffers.push(buffer);
  return buffers.concatBuffer();
}
 
function serializeBoolCV(value: BooleanCV): Uint8Array {
  return Uint8Array.from([value.type]);
}
 
function serializeOptionalCV(cv: OptionalCV<ClarityValue>): Uint8Array {
  if (cv.type === ClarityType.OptionalNone) {
    return new Uint8Array([cv.type]);
  } else {
    return bufferWithTypeID(cv.type, serializeCV(cv.value));
  }
}
 
function serializeBufferCV(cv: BufferCV): Uint8Array {
  const length = new Uint8Array(4);
  const view = new DataView(length.buffer, length.byteOffset, length.byteLength);
 
  view.setUint32(length.byteOffset, cv.buffer.length);
  return bufferWithTypeID(cv.type, concatByteArrays([length, Uint8Array.from(cv.buffer)]));
}
 
function serializeIntCV(cv: IntCV): Uint8Array {
  const hex = intToHexString(toTwos(cv.value), 16);
  const buffer = hexToBytes(hex);
  return bufferWithTypeID(cv.type, buffer);
}
 
function serializeUIntCV(cv: UIntCV): Uint8Array {
  const hex = intToHexString(cv.value, 16);
  const buffer = hexToBytes(hex);
  return bufferWithTypeID(cv.type, buffer);
}
 
function serializeStandardPrincipalCV(cv: StandardPrincipalCV): Uint8Array {
  return bufferWithTypeID(cv.type, serializeAddress(cv.address));
}
 
function serializeContractPrincipalCV(cv: ContractPrincipalCV): Uint8Array {
  return bufferWithTypeID(
    cv.type,
    concatByteArrays([serializeAddress(cv.address), serializeLPString(cv.contractName)])
  );
}
 
function serializeResponseCV(cv: ResponseCV) {
  return bufferWithTypeID(cv.type, serializeCV(cv.value));
}
 
function serializeListCV(cv: ListCV) {
  const buffers = new BufferArray();
 
  const length = new Uint8Array(4);
  writeUInt32BE(length, cv.list.length, 0);
  buffers.push(length);
 
  for (const value of cv.list) {
    const serializedValue = serializeCV(value);
    buffers.push(serializedValue);
  }
 
  return bufferWithTypeID(cv.type, buffers.concatBuffer());
}
 
function serializeTupleCV(cv: TupleCV) {
  const buffers = [];
 
  const length = new Uint8Array(4);
  const view = new DataView(length.buffer, length.byteOffset, length.byteLength);
  view.setUint32(length.byteOffset, Object.keys(cv.data).length);
 
  buffers.push(length);
 
  const lexicographicOrder = Object.keys(cv.data).sort((a, b) => {
    const bufA = utf8ToBytes(a);
    const bufB = utf8ToBytes(b);
    return compare(bufA, bufB);
  });
 
  for (const key of lexicographicOrder) {
    const nameWithLength = createLPString(key);
    buffers.push(serializeLPString(nameWithLength));
 
    const serializedValue = serializeCV(cv.data[key]);
    buffers.push(serializedValue);
  }
 
  return bufferWithTypeID(cv.type, concatByteArrays(buffers));
}
 
function serializeStringCV(cv: StringAsciiCV | StringUtf8CV, encoding: 'ascii' | 'utf8') {
  const buffers = new BufferArray();
  const toBytes = encoding === 'ascii' ? asciiToBytes : utf8ToBytes;
  const str = toBytes(cv.data);
  const len = new Uint8Array(4);
  const view = new DataView(len.buffer, len.byteOffset, len.byteLength);
 
  view.setUint32(len.byteOffset, str.length);
 
  buffers.push(len);
  buffers.push(str);
  return bufferWithTypeID(cv.type, buffers.concatBuffer());
}
 
function serializeStringAsciiCV(cv: StringAsciiCV) {
  return serializeStringCV(cv, 'ascii');
}
 
function serializeStringUtf8CV(cv: StringUtf8CV) {
  return serializeStringCV(cv, 'utf8');
}
 
export function serializeCV(value: ClarityValue): Uint8Array {
  switch (value.type) {
    case ClarityType.BoolTrue:
    case ClarityType.BoolFalse:
      return serializeBoolCV(value);
    case ClarityType.OptionalNone:
    case ClarityType.OptionalSome:
      return serializeOptionalCV(value);
    case ClarityType.Buffer:
      return serializeBufferCV(value);
    case ClarityType.Int:
      return serializeIntCV(value);
    case ClarityType.UInt:
      return serializeUIntCV(value);
    case ClarityType.PrincipalStandard:
      return serializeStandardPrincipalCV(value);
    case ClarityType.PrincipalContract:
      return serializeContractPrincipalCV(value);
    case ClarityType.ResponseOk:
    case ClarityType.ResponseErr:
      return serializeResponseCV(value);
    case ClarityType.List:
      return serializeListCV(value);
    case ClarityType.Tuple:
      return serializeTupleCV(value);
    case ClarityType.StringASCII:
      return serializeStringAsciiCV(value);
    case ClarityType.StringUTF8:
      return serializeStringUtf8CV(value);
    default:
      throw new Error('Unable to serialize. Invalid Clarity Value.');
  }
}