import { Buffer } from '@stacks/common' ; import { sha256 , sha512 } from 'sha.js' ; import { ClarityValue , serializeCV } from './clarity' ; import RIPEMD160 from 'ripemd160-min' ; import randombytes from 'randombytes' ; import { deserializeCV } from './clarity' ; import fetch from 'cross-fetch' ; import { c32addressDecode } from 'c32check' ; import lodashCloneDeep from 'lodash/cloneDeep' ; import { with0x } from '@stacks/common' ; export { randombytes as randomBytes } ; export class BufferArray { _value : Buffer [ ] = [ ] ; get value ( ) { return this . _value ; } appendHexString ( hexString ) { this . value . push ( Buffer . from ( hexString , 'hex' ) ) ; } push ( buffer ) { return this . _value . push ( buffer ) ; } appendByte ( octet ) { if ( ! Number . isInteger ( octet ) || octet < 0 || octet > 255 ) { throw new Error ( `Value ${ octet } is not a valid byte` ) ; } this . value . push ( Buffer . from ( [ octet ] ) ) ; } concatBuffer ( ) { return Buffer . concat ( this . value ) ; } } export const leftPadHex = ( hexString ) : string => hexString . length % 2 == 0 ? hexString : `0${ hexString }` ; export const leftPadHexToLength = ( hexString , length ) : string => hexString . padStart ( length , '0' ) ; export const rightPadHexToLength = ( hexString , length ) : string => hexString . padEnd ( length , '0' ) ; export const intToHexString = ( integer , lengthBytes = 8 ) : string => integer . toString ( 16 ) . padStart ( lengthBytes * 2 , '0' ) ; export const hexStringToInt = ( hexString ) : number => parseInt ( hexString , 16 ) ; export const exceedsMaxLengthBytes = ( string , maxLengthBytes ) : boolean => string ? Buffer . from ( string ) . length > maxLengthBytes : false ; export function cloneDeep < T > ( obj ) { return lodashCloneDeep ( obj ) ; } export function omit < T , K extends keyof any > ( obj , prop ) { const clone = cloneDeep ( obj ) ; delete clone [ prop ] ; return clone ; } export class sha512_256 extends sha512 { constructor ( ) { super ( ) ; Object . assign ( this , { _ah : 0x22312194 , _al : 0xfc2bf72c , _bh : 0x9f555fa3 , _bl : 0xc84c64c2 , _ch : 0x2393b86b , _cl : 0x6f53b151 , _dh : 0x96387719 , _dl : 0x5940eabd , _eh : 0x96283ee2 , _el : 0xa88effe3 , _fh : 0xbe5e1e25 , _fl : 0x53863992 , _gh : 0x2b0199fc , _gl : 0x2c85b8aa , _hh : 0x0eb72ddc , _hl : 0x81c52ca2 , } ) ; } digest ( ) ; digest ( encoding ) ; digest ( encoding ? ) { const buff = super . digest ( ) . slice ( 0 , 32 ) ; return encoding ? buff . toString ( encoding ) : buff ; } } export const txidFromData = ( data ) : string => new sha512_256 ( ) . update ( data ) . digest ( 'hex' ) ; export const hash160 = ( input ) : Buffer => { const sha256Result = new sha256 ( ) . update ( input ) . digest ( ) ; return Buffer . from ( new RIPEMD160 ( ) . update ( sha256Result ) . digest ( ) ) ; } ; export const hashP2PKH = ( input ) : string => { return hash160 ( input ) . toString ( 'hex' ) ; } ; export const hashP2WPKH = ( input ) : string => { const keyHash = hash160 ( input ) ; const bufferArray = new BufferArray ( ) ; bufferArray . appendByte ( 0 ) ; bufferArray . appendByte ( keyHash . length ) ; bufferArray . push ( keyHash ) ; const redeemScript = bufferArray . concatBuffer ( ) ; const redeemScriptHash = hash160 ( redeemScript ) ; return redeemScriptHash . toString ( 'hex' ) ; } ; export const hashP2SH = ( numSigs , pubKeys ) : string => { if ( numSigs > 15 || pubKeys . length > 15 ) { throw Error ( 'P2SH multisig address can only contain up to 15 public keys' ) ; } const bufferArray = new BufferArray ( ) ; bufferArray . appendByte ( 80 + numSigs ) ; pubKeys . forEach ( pubKey => { bufferArray . appendByte ( pubKey . length ) ; bufferArray . push ( pubKey ) ; } ) ; bufferArray . appendByte ( 80 + pubKeys . length ) ; bufferArray . appendByte ( 174 ) ; const redeemScript = bufferArray . concatBuffer ( ) ; const redeemScriptHash = hash160 ( redeemScript ) ; return redeemScriptHash . toString ( 'hex' ) ; } ; export const hashP2WSH = ( numSigs , pubKeys ) : string => { if ( numSigs > 15 || pubKeys . length > 15 ) { throw Error ( 'P2WSH multisig address can only contain up to 15 public keys' ) ; } const scriptArray = new BufferArray ( ) ; scriptArray . appendByte ( 80 + numSigs ) ; pubKeys . forEach ( pubKey => { scriptArray . appendByte ( pubKey . length ) ; scriptArray . push ( pubKey ) ; } ) ; scriptArray . appendByte ( 80 + pubKeys . length ) ; scriptArray . appendByte ( 174 ) ; const script = scriptArray . concatBuffer ( ) ; const digest = new sha256 ( ) . update ( script ) . digest ( ) ; const bufferArray = new BufferArray ( ) ; bufferArray . appendByte ( 0 ) ; bufferArray . appendByte ( digest . length ) ; bufferArray . push ( digest ) ; const redeemScript = bufferArray . concatBuffer ( ) ; const redeemScriptHash = hash160 ( redeemScript ) ; return redeemScriptHash . toString ( 'hex' ) ; } ; export function isClarityName ( name ) { const regex = /^[a-zA-Z]([a-zA-Z0-9]|[-_!?+<>=/*])*$|^[-+=/*]$|^[<>]=?$/ ; return regex . test ( name ) && name . length < 128 ; } ignore export async function fetchPrivate ( input , init ? ) { const defaultFetchOpts = { referrer : 'no-referrer' , referrerPolicy : 'no-referrer' , } ; const fetchOpts = Object . assign ( defaultFetchOpts , init ) ; const fetchResult = await fetch ( input , fetchOpts ) ; return fetchResult ; } param { ClarityValue } cv export function cvToHex ( cv ) { const serialized = serializeCV ( cv ) ; return `0x${ serialized . toString ( 'hex' ) }` ; } param { string } hex export function hexToCV ( hex ) { return deserializeCV ( hex ) ; } param { Boolean } okay param { string } result export interface ReadOnlyFunctionSuccessResponse { okay : true ; result : string ; } export interface ReadOnlyFunctionErrorResponse { okay : false ; cause : string ; } export type ReadOnlyFunctionResponse = ReadOnlyFunctionSuccessResponse | ReadOnlyFunctionErrorResponse ; param param export const parseReadOnlyResponse = ( response ) : ClarityValue => { if ( response . okay ) { return hexToCV ( response . result ) ; } else { throw new Error ( response . cause ) ; } } ; export const validateStacksAddress = ( stacksAddress ) : boolean => { try { c32addressDecode ( stacksAddress ) ; return true ; } catch ( e ) { return false ; } } ; export const validateTxId = ( txid ) : boolean => { if ( txid === 'success' ) return true ; const value = with0x ( txid ) . toLowerCase ( ) ; if ( value . length !== 66 ) return false ; return with0x ( BigInt ( value ) . toString ( 16 ) . padStart ( 64 , '0' ) ) === value ; } ;