All files / src/clarity/common utils.ts

100% Statements 52/52
93.75% Branches 15/16
100% Functions 10/10
100% Lines 41/41

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 14321x                   21x 21x   21x 185x 185x     21x                                       21x 94x 94x             21x 23x                   21x 282x                 21x         256x 256x 256x 1x   255x               21x 148x 148x 148x   148x     21x 206x 206x 206x 206x 206x 206x     21x         55x 55x 55x 55x     21x 50x 50x   50x                                                
import { c32address, c32addressDecode, StacksNetworkVersion } from 'micro-stacks/crypto';
import {
  BufferArray,
  BufferReader,
  bytesToHex,
  hexStringToInt,
  hexToBytes,
  intToHexString,
  utf8ToBytes,
  bytesToUtf8,
} from 'micro-stacks/common';
import { MAX_STRING_LENGTH_BYTES, PostConditionPrincipalID } from './constants';
 
export function isClarityName(name: string) {
  const regex = /^[a-zA-Z]([a-zA-Z0-9]|[-_!?+<>=/*])*$|^[-+=/*]$|^[<>]=?$/;
  return regex.test(name) && name.length < 128;
}
 
export enum StacksMessageType {
  Address,
  Principal,
  LengthPrefixedString,
  MemoString,
  AssetInfo,
  PostCondition,
  PublicKey,
  LengthPrefixedList,
  Payload,
  MessageSignature,
  TransactionAuthField,
}
 
export interface Address {
  readonly type: StacksMessageType.Address;
  readonly version: StacksNetworkVersion;
  readonly hash160: string;
}
 
export function createAddress(c32AddressString: string): Address {
  const addressData = c32addressDecode(c32AddressString);
  return {
    type: StacksMessageType.Address,
    version: addressData[0],
    hash160: bytesToHex(addressData[1]),
  };
}
 
export function addressToString(address: Address): string {
  return c32address(address.version, hexToBytes(address.hash160));
}
 
export interface LengthPrefixedString {
  readonly type: StacksMessageType.LengthPrefixedString;
  readonly content: string;
  readonly lengthPrefixBytes: number;
  readonly maxLengthBytes: number;
}
 
export const exceedsMaxLengthBytes = (string: string, maxLengthBytes: number): boolean =>
  string ? utf8ToBytes(string).length > maxLengthBytes : false;
 
export function createLPString(content: string): LengthPrefixedString;
export function createLPString(content: string, lengthPrefixBytes: number): LengthPrefixedString;
export function createLPString(
  content: string,
  lengthPrefixBytes: number,
  maxLengthBytes: number
): LengthPrefixedString;
export function createLPString(
  content: string,
  lengthPrefixBytes?: number,
  maxLengthBytes?: number
): LengthPrefixedString {
  const prefixLength = lengthPrefixBytes || 1;
  const maxLength = maxLengthBytes || MAX_STRING_LENGTH_BYTES;
  if (exceedsMaxLengthBytes(content, maxLength)) {
    throw new Error(`String length exceeds maximum bytes ${maxLength.toString()}`);
  }
  return {
    type: StacksMessageType.LengthPrefixedString,
    content,
    lengthPrefixBytes: prefixLength,
    maxLengthBytes: maxLength,
  };
}
 
export function serializeAddress(address: Address): Uint8Array {
  const bufferArray: BufferArray = new BufferArray();
  bufferArray.appendHexString(intToHexString(address.version, 1));
  bufferArray.appendHexString(address.hash160);
 
  return bufferArray.concatBuffer();
}
 
export function serializeLPString(lps: LengthPrefixedString) {
  const bufferArray: BufferArray = new BufferArray();
  const contentBuffer = utf8ToBytes(lps.content); // is this hex?
  const length = contentBuffer.byteLength;
  bufferArray.appendHexString(intToHexString(length, lps.lengthPrefixBytes));
  bufferArray.push(contentBuffer);
  return bufferArray.concatBuffer();
}
 
export function deserializeLPString(
  bufferReader: BufferReader,
  prefixBytes?: number,
  maxLength?: number
): LengthPrefixedString {
  prefixBytes = prefixBytes ? prefixBytes : 1;
  const length = hexStringToInt(bytesToHex(bufferReader.readBuffer(prefixBytes)));
  const content = bytesToUtf8(bufferReader.readBuffer(length));
  return createLPString(content, prefixBytes, maxLength ?? 128);
}
 
export function deserializeAddress(bufferReader: BufferReader): Address {
  const version = hexStringToInt(bytesToHex(bufferReader.readBuffer(1)));
  const data = bytesToHex(bufferReader.readBuffer(20));
 
  return { type: StacksMessageType.Address, version, hash160: data };
}
 
export interface StandardPrincipal {
  readonly type: StacksMessageType.Principal;
  readonly prefix: PostConditionPrincipalID.Standard;
  readonly address: Address;
}
 
export interface ContractPrincipal {
  readonly type: StacksMessageType.Principal;
  readonly prefix: PostConditionPrincipalID.Contract;
  readonly address: Address;
  readonly contractName: LengthPrefixedString;
}
 
export type PostConditionPrincipal = StandardPrincipal | ContractPrincipal;
 
export interface AssetInfo {
  readonly type: StacksMessageType.AssetInfo;
  readonly address: Address;
  readonly contractName: LengthPrefixedString;
  readonly assetName: LengthPrefixedString;
}