diff --git a/.github/.husky/commitlint.json b/.github/.husky/commitlint.json index 827b75b72..94e31eb5c 100644 --- a/.github/.husky/commitlint.json +++ b/.github/.husky/commitlint.json @@ -8,6 +8,8 @@ "header-max-length": [0, "always"], "scope-max-length": [0, "always"], "subject-max-length": [0, "always"], - "type-max-length": [0, "always"] + "type-max-length": [0, "always"], + + "subject-case": [0, "always", "sentence-case"] } } diff --git a/.github/MIGRATION.md b/.github/MIGRATION.md index 7a8c2471a..cc8e62adb 100644 --- a/.github/MIGRATION.md +++ b/.github/MIGRATION.md @@ -6,6 +6,8 @@ - [StacksNodeApi](#stacksnodeapi) - [StacksNetwork to StacksNodeApi](#stacksnetwork-to-stacksnodeapi) - [Clarity Representation](#clarity-representation) + - [`serialize` methods](#serialize-methods) + - [Asset Helper Methods](#asset-helper-methods) - [Stacks.js (\<=4.x.x) → (5.x.x)](#stacksjs-4xx--5xx) - [Breaking Changes](#breaking-changes-1) - [Buffer to Uint8Array](#buffer-to-uint8array) @@ -26,6 +28,8 @@ - The `@stacks/network` `new StacksNetwork()` objects were removed. Instead `@stacks/network` now exports the objects `STACKS_MAINNET`, `STACKS_TESNET`, and `STACKS_DEVNET`, which are static (and shouldn't be changed for most use-cases). [Read more...](#stacks-network) - The `ClarityType` enum was replaced by a readable version. The previous (wire format compatible) enum is still available as `ClarityWireType`. [Read more...](#clarity-representation) +- The `serializeXyz` methods were changed to return `string` (hex-encoded) instead of `Uint8Array`. Compatible `serializeXzyBytes` methods were added to ease the migration. [Read more...](#serialize-methods) +- The `AssetInfo` type was renamed to `Asset` for accuracy. The `Asset` helper methods were also renamed to to remove the `Info` suffix. [Read more...](#asset-helper-methods) ### Stacks Network @@ -144,6 +148,42 @@ For `bigint` values, the type of the `value` property is a now `string`, for bet } ``` +### `serialize` methods + +Existing methods now use hex-encoded strings instead of `Uint8Array`s. +For easiery migrating, renaming the following methods is possible to keep the previous behavior: + +- `serializeCV` → `serializeCVBytes` +- `serializeAddress` → `serializeAddressBytes` +- `deserializeAddress` → `deserializeAddressBytes` +- `serializeLPList` → `serializeLPListBytes` +- `deserializeLPList` → `deserializeLPListBytes` +- `serializeLPString` → `serializeLPStringBytes` +- `deserializeLPString` → `deserializeLPStringBytes` +- `serializePayload` → `serializePayloadBytes` +- `deserializePayload` → `deserializePayloadBytes` +- `serializePublicKey` → `serializePublicKeyBytes` +- `deserializePublicKey` → `deserializePublicKeyBytes` +- `serializeStacksMessage` → `serializeStacksMessageBytes` +- `deserializeStacksMessage` → `deserializeStacksMessageBytes` +- `serializeMemoString` → `serializeMemoStringBytes` +- `deserializeMemoString` → `deserializeMemoStringBytes` +- `serializeTransactionAuthField` → `serializeTransactionAuthFieldBytes` +- `deserializeTransactionAuthField` → `deserializeTransactionAuthFieldBytes` +- `serializeMessageSignature` → `serializeMessageSignatureBytes` +- `deserializeMessageSignature` → `deserializeMessageSignatureBytes` +- `serializePostCondition` → `serializePostConditionBytes` +- `deserializePostCondition` → `deserializePostConditionBytes` + +### Asset Helper Methods + +The following interfaces and methods were renamed: + +- `AssetInfo` → `Asset` +- `StacksMessageType.AssetInfo` → `StacksMessageType.Asset` +- `createAssetInfo` → `createAsset` +- `parseAssetInfoString` → `parseAssetString` + ## Stacks.js (<=4.x.x) → (5.x.x) ### Breaking Changes diff --git a/packages/bns/src/index.ts b/packages/bns/src/index.ts index c44a52476..5079f58a2 100644 --- a/packages/bns/src/index.ts +++ b/packages/bns/src/index.ts @@ -23,7 +23,7 @@ import { makeRandomPrivKey, makeUnsignedContractCall, noneCV, - parseAssetInfoString, + parseAssetString, publicKeyToAddress, someCV, standardPrincipalCV, @@ -705,7 +705,7 @@ export async function buildTransferNameTx({ const postConditionSender = createNonFungiblePostCondition( publicKeyToAddress(getAddressVersion(network), publicKey), NonFungibleConditionCode.Sends, - parseAssetInfoString(`${getBnsContractAddress(network)}.bns::names`), + parseAssetString(`${getBnsContractAddress(network)}.bns::names`), tupleCV({ name: bufferCVFromString(name), namespace: bufferCVFromString(namespace), @@ -714,7 +714,7 @@ export async function buildTransferNameTx({ const postConditionReceiver = createNonFungiblePostCondition( newOwnerAddress, NonFungibleConditionCode.DoesNotSend, - parseAssetInfoString(`${getBnsContractAddress(network)}.bns::names`), + parseAssetString(`${getBnsContractAddress(network)}.bns::names`), tupleCV({ name: bufferCVFromString(name), namespace: bufferCVFromString(namespace), diff --git a/packages/bns/tests/bns.test.ts b/packages/bns/tests/bns.test.ts index 4531d8941..351b00572 100644 --- a/packages/bns/tests/bns.test.ts +++ b/packages/bns/tests/bns.test.ts @@ -12,7 +12,7 @@ import { falseCV, hash160, noneCV, - parseAssetInfoString, + parseAssetString, publicKeyToAddress, responseErrorCV, responseOkCV, @@ -704,7 +704,7 @@ test('transferName', async () => { const nameTransferPostConditionOne = createNonFungiblePostCondition( publicKeyToAddress(getAddressVersion(network), publicKey), NonFungibleConditionCode.Sends, - parseAssetInfoString(`${getBnsContractAddress(network)}.bns::names`), + parseAssetString(`${getBnsContractAddress(network)}.bns::names`), tupleCV({ name: bufferCVFromString(name), namespace: bufferCVFromString(namespace), @@ -713,7 +713,7 @@ test('transferName', async () => { const nameTransferPostConditionTwo = createNonFungiblePostCondition( newOwnerAddress, NonFungibleConditionCode.DoesNotSend, - parseAssetInfoString(`${getBnsContractAddress(network)}.bns::names`), + parseAssetString(`${getBnsContractAddress(network)}.bns::names`), tupleCV({ name: bufferCVFromString(name), namespace: bufferCVFromString(namespace), @@ -772,7 +772,7 @@ test('transferName optionalArguments', async () => { const nameTransferPostConditionOne = createNonFungiblePostCondition( publicKeyToAddress(getAddressVersion(network), publicKey), NonFungibleConditionCode.Sends, - parseAssetInfoString(`${getBnsContractAddress(network)}.bns::names`), + parseAssetString(`${getBnsContractAddress(network)}.bns::names`), tupleCV({ name: bufferCVFromString(name), namespace: bufferCVFromString(namespace), @@ -781,7 +781,7 @@ test('transferName optionalArguments', async () => { const nameTransferPostConditionTwo = createNonFungiblePostCondition( newOwnerAddress, NonFungibleConditionCode.DoesNotSend, - parseAssetInfoString(`${getBnsContractAddress(network)}.bns::names`), + parseAssetString(`${getBnsContractAddress(network)}.bns::names`), tupleCV({ name: bufferCVFromString(name), namespace: bufferCVFromString(namespace), diff --git a/packages/cli/src/cli.ts b/packages/cli/src/cli.ts index 8a39fc39b..a04a713c7 100644 --- a/packages/cli/src/cli.ts +++ b/packages/cli/src/cli.ts @@ -783,7 +783,7 @@ async function contractDeploy(network: CLINetworkAdapter, args: string[]): Promi if (estimateOnly) { return estimateTransaction({ - payload: bytesToHex(serializePayload(tx.payload)), + payload: serializePayload(tx.payload), estimatedLength: estimateTransactionByteLength(tx), }).then(costs => costs[1].fee.toString(10)); } @@ -870,7 +870,7 @@ async function contractFunctionCall(network: CLINetworkAdapter, args: string[]): if (estimateOnly) { return estimateTransaction({ - payload: bytesToHex(serializePayload(tx.payload)), + payload: serializePayload(tx.payload), estimatedLength: estimateTransactionByteLength(tx), }).then(costs => costs[1].fee.toString(10)); } diff --git a/packages/transactions/README.md b/packages/transactions/README.md index c2cfa64d4..159d53dee 100644 --- a/packages/transactions/README.md +++ b/packages/transactions/README.md @@ -425,7 +425,7 @@ const contractSTXPostCondition = makeContractSTXPostCondition( ```typescript import { FungibleConditionCode, - createAssetInfo, + createAsset, makeStandardFungiblePostCondition, } from '@stacks/transactions'; @@ -436,13 +436,13 @@ const postConditionAmount = 12345n; const assetAddress = 'SP62M8MEFH32WGSB7XSF9WJZD7TQB48VQB5ANWSJ'; const assetContractName = 'test-asset-contract'; const assetName = 'test-token'; -const fungibleAssetInfo = createAssetInfo(assetAddress, assetContractName, assetName); +const fungibleAsset = createAsset(assetAddress, assetContractName, assetName); const standardFungiblePostCondition = makeStandardFungiblePostCondition( postConditionAddress, postConditionCode, postConditionAmount, - fungibleAssetInfo + fungibleAsset ); // With a contract principal @@ -451,14 +451,14 @@ const contractName = 'test-contract'; const assetAddress = 'SP62M8MEFH32WGSB7XSF9WJZD7TQB48VQB5ANWSJ'; const assetContractName = 'test-asset-contract'; const assetName = 'test-token'; -const fungibleAssetInfo = createAssetInfo(assetAddress, assetContractName, assetName); +const fungibleAsset = createAsset(assetAddress, assetContractName, assetName); const contractFungiblePostCondition = makeContractFungiblePostCondition( contractAddress, contractName, postConditionCode, postConditionAmount, - fungibleAssetInfo + fungibleAsset ); ``` @@ -473,7 +473,7 @@ const contractFungiblePostCondition = makeContractFungiblePostCondition( ```typescript import { NonFungibleConditionCode, - createAssetInfo, + createAsset, makeStandardNonFungiblePostCondition, makeContractNonFungiblePostCondition, bufferCVFromString, @@ -486,12 +486,12 @@ const assetAddress = 'SP62M8MEFH32WGSB7XSF9WJZD7TQB48VQB5ANWSJ'; const assetContractName = 'test-asset-contract'; const assetName = 'test-asset'; const assetId = bufferCVFromString('test-token-asset-id'); -const nonFungibleAssetInfo = createAssetInfo(assetAddress, assetContractName, assetName); +const nonFungibleAsset = createAsset(assetAddress, assetContractName, assetName); const standardNonFungiblePostCondition = makeStandardNonFungiblePostCondition( postConditionAddress, postConditionCode, - nonFungibleAssetInfo, + nonFungibleAsset, assetId ); @@ -503,7 +503,7 @@ const contractNonFungiblePostCondition = makeContractNonFungiblePostCondition( contractAddress, contractName, postConditionCode, - nonFungibleAssetInfo, + nonFungibleAsset, assetId ); ``` diff --git a/packages/transactions/src/authorization.ts b/packages/transactions/src/authorization.ts index b5d2e30f1..77f13054c 100644 --- a/packages/transactions/src/authorization.ts +++ b/packages/transactions/src/authorization.ts @@ -31,15 +31,15 @@ import { } from './keys'; import { deserializeMessageSignature, - serializeMessageSignature, + serializeMessageSignatureBytes, TransactionAuthField, } from './signature'; import { addressFromPublicKeys, createEmptyAddress, createLPList, - deserializeLPList, - serializeLPList, + deserializeLPListBytes, + serializeLPListBytes, } from './types'; import { cloneDeep, leftPadHex, txidFromData } from './utils'; @@ -205,7 +205,7 @@ export function serializeSingleSigSpendingCondition( intToBytes(condition.nonce, false, 8), intToBytes(condition.fee, false, 8), condition.keyEncoding as number, - serializeMessageSignature(condition.signature), + serializeMessageSignatureBytes(condition.signature), ]; return concatArray(bytesArray); } @@ -221,7 +221,7 @@ export function serializeMultiSigSpendingCondition( ]; const fields = createLPList(condition.fields); - bytesArray.push(serializeLPList(fields)); + bytesArray.push(serializeLPListBytes(fields)); const numSigs = new Uint8Array(2); writeUInt16BE(numSigs, condition.signaturesRequired, 0); @@ -265,7 +265,7 @@ export function deserializeMultiSigSpendingCondition( const nonce = BigInt('0x' + bytesToHex(bytesReader.readBytes(8))); const fee = BigInt('0x' + bytesToHex(bytesReader.readBytes(8))); - const fields = deserializeLPList(bytesReader, StacksMessageType.TransactionAuthField) + const fields = deserializeLPListBytes(bytesReader, StacksMessageType.TransactionAuthField) .values as TransactionAuthField[]; let haveUncompressed = false; diff --git a/packages/transactions/src/builders.ts b/packages/transactions/src/builders.ts index 3af24b958..ad002b444 100644 --- a/packages/transactions/src/builders.ts +++ b/packages/transactions/src/builders.ts @@ -42,7 +42,8 @@ import { createSTXPostCondition, } from './postcondition'; import { - AssetInfo, + Asset, + AssetString, FungiblePostCondition, NonFungiblePostCondition, PostCondition, @@ -595,19 +596,19 @@ export function makeContractSTXPostCondition( * @param address - the c32check address * @param conditionCode - the condition code * @param amount - the amount of fungible tokens (in their respective base unit) - * @param assetInfo - asset info describing the fungible token + * @param asset - asset info describing the fungible token */ export function makeStandardFungiblePostCondition( address: string, conditionCode: FungibleConditionCode, amount: IntegerType, - assetInfo: string | AssetInfo + asset: AssetString | Asset ): FungiblePostCondition { return createFungiblePostCondition( createStandardPrincipal(address), conditionCode, amount, - assetInfo + asset ); } @@ -620,20 +621,20 @@ export function makeStandardFungiblePostCondition( * @param contractName - the name of the contract * @param conditionCode - the condition code * @param amount - the amount of fungible tokens (in their respective base unit) - * @param assetInfo - asset info describing the fungible token + * @param asset - asset info describing the fungible token */ export function makeContractFungiblePostCondition( address: string, contractName: string, conditionCode: FungibleConditionCode, amount: IntegerType, - assetInfo: string | AssetInfo + asset: AssetString | Asset ): FungiblePostCondition { return createFungiblePostCondition( createContractPrincipal(address, contractName), conditionCode, amount, - assetInfo + asset ); } @@ -644,7 +645,7 @@ export function makeContractFungiblePostCondition( * * @param {String} address - the c32check address * @param {FungibleConditionCode} conditionCode - the condition code - * @param {AssetInfo} assetInfo - asset info describing the non-fungible token + * @param {Asset} asset - asset info describing the non-fungible token * @param {ClarityValue} assetId - asset identifier of the nft instance (typically a uint/buffer/string) * * @return {NonFungiblePostCondition} @@ -652,13 +653,13 @@ export function makeContractFungiblePostCondition( export function makeStandardNonFungiblePostCondition( address: string, conditionCode: NonFungibleConditionCode, - assetInfo: string | AssetInfo, + asset: AssetString | Asset, assetId: ClarityValue ): NonFungiblePostCondition { return createNonFungiblePostCondition( createStandardPrincipal(address), conditionCode, - assetInfo, + asset, assetId ); } @@ -671,7 +672,7 @@ export function makeStandardNonFungiblePostCondition( * @param {String} address - the c32check address * @param {String} contractName - the name of the contract * @param {FungibleConditionCode} conditionCode - the condition code - * @param {AssetInfo} assetInfo - asset info describing the non-fungible token + * @param {Asset} asset - asset info describing the non-fungible token * @param {ClarityValue} assetId - asset identifier of the nft instance (typically a uint/buffer/string) * * @return {NonFungiblePostCondition} @@ -680,13 +681,13 @@ export function makeContractNonFungiblePostCondition( address: string, contractName: string, conditionCode: NonFungibleConditionCode, - assetInfo: string | AssetInfo, + asset: AssetString | Asset, assetId: ClarityValue ): NonFungiblePostCondition { return createNonFungiblePostCondition( createContractPrincipal(address, contractName), conditionCode, - assetInfo, + asset, assetId ); } diff --git a/packages/transactions/src/cl.ts b/packages/transactions/src/cl.ts index fe54bfa87..c326f9cee 100644 --- a/packages/transactions/src/cl.ts +++ b/packages/transactions/src/cl.ts @@ -9,7 +9,7 @@ import { noneCV, responseErrorCV, responseOkCV, - serializeCV, + serializeCVBytes, someCV, standardPrincipalCV, stringAsciiCV, @@ -269,7 +269,7 @@ export const tuple = tupleCV; /** * `Cl.serialize` — Serializes a Clarity JS object to the equivalent hex-encoded representation * - * Alias for {@link serializeCV} + * Alias for {@link serializeCVBytes} * @example * ``` * import { Cl } from '@stacks/transactions'; @@ -277,7 +277,7 @@ export const tuple = tupleCV; * ``` * @see {@link deserialize} */ -export const serialize = serializeCV; +export const serialize = serializeCVBytes; /** * `Cl.deserialize` — Deserializes a hex string to the equivalent Clarity JS object * diff --git a/packages/transactions/src/clarity/deserialize.ts b/packages/transactions/src/clarity/deserialize.ts index 26392015b..4d3720380 100644 --- a/packages/transactions/src/clarity/deserialize.ts +++ b/packages/transactions/src/clarity/deserialize.ts @@ -16,18 +16,13 @@ import { tupleCV, } from '.'; import { BytesReader as BytesReader } from '../bytesReader'; -import { deserializeAddress, deserializeLPString } from '../types'; +import { deserializeAddressBytes, deserializeLPStringBytes } from '../types'; import { DeserializationError } from '../errors'; import { stringAsciiCV, stringUtf8CV } from './types/stringCV'; import { bytesToAscii, bytesToUtf8, hexToBytes } from '@stacks/common'; /** * Deserializes clarity value to clarity type - * - * @param {value} Uint8Array | string value to be converted to clarity type - ** - * @returns {ClarityType} returns the clarity type instance - * * @example * ``` * import { intCV, serializeCV, deserializeCV } from '@stacks/transactions'; @@ -79,12 +74,12 @@ export function deserializeCV( return falseCV() as T; case ClarityWireType.address: - const sAddress = deserializeAddress(bytesReader); + const sAddress = deserializeAddressBytes(bytesReader); return standardPrincipalCVFromAddress(sAddress) as T; case ClarityWireType.contract: - const cAddress = deserializeAddress(bytesReader); - const contractName = deserializeLPString(bytesReader); + const cAddress = deserializeAddressBytes(bytesReader); + const contractName = deserializeLPStringBytes(bytesReader); return contractPrincipalCVFromAddress(cAddress, contractName) as T; case ClarityWireType.ok: @@ -111,7 +106,7 @@ export function deserializeCV( const tupleLength = bytesReader.readUInt32BE(); const tupleContents: { [key: string]: ClarityValue } = {}; for (let i = 0; i < tupleLength; i++) { - const clarityName = deserializeLPString(bytesReader).content; + const clarityName = deserializeLPStringBytes(bytesReader).content; if (clarityName === undefined) { throw new DeserializationError('"content" is undefined'); } diff --git a/packages/transactions/src/clarity/index.ts b/packages/transactions/src/clarity/index.ts index eea2290ba..48c7eb9a0 100644 --- a/packages/transactions/src/clarity/index.ts +++ b/packages/transactions/src/clarity/index.ts @@ -44,5 +44,6 @@ export { stringAsciiCV, stringCV, } from './types/stringCV'; -export { serializeCV } from './serialize'; -export { deserializeCV } from './deserialize'; + +export * from './serialize'; +export * from './deserialize'; diff --git a/packages/transactions/src/clarity/serialize.ts b/packages/transactions/src/clarity/serialize.ts index 617f6681b..26759a59e 100644 --- a/packages/transactions/src/clarity/serialize.ts +++ b/packages/transactions/src/clarity/serialize.ts @@ -6,8 +6,9 @@ import { writeUInt32BE, utf8ToBytes, asciiToBytes, + bytesToHex, } from '@stacks/common'; -import { serializeAddress, serializeLPString } from '../types'; +import { serializeAddressBytes, serializeLPStringBytes } from '../types'; import { createLPString } from '../postcondition-types'; import { BooleanCV, @@ -40,7 +41,7 @@ function serializeOptionalCV(cv: OptionalCV): Uint8Array { if (cv.type === ClarityType.OptionalNone) { return new Uint8Array([clarityTypeToByte(cv.type)]); } else { - return bytesWithTypeID(cv.type, serializeCV(cv.value)); + return bytesWithTypeID(cv.type, serializeCVBytes(cv.value)); } } @@ -61,18 +62,18 @@ function serializeUIntCV(cv: UIntCV): Uint8Array { } function serializeStandardPrincipalCV(cv: StandardPrincipalCV): Uint8Array { - return bytesWithTypeID(cv.type, serializeAddress(cv.address)); + return bytesWithTypeID(cv.type, serializeAddressBytes(cv.address)); } function serializeContractPrincipalCV(cv: ContractPrincipalCV): Uint8Array { return bytesWithTypeID( cv.type, - concatBytes(serializeAddress(cv.address), serializeLPString(cv.contractName)) + concatBytes(serializeAddressBytes(cv.address), serializeLPStringBytes(cv.contractName)) ); } function serializeResponseCV(cv: ResponseCV) { - return bytesWithTypeID(cv.type, serializeCV(cv.value)); + return bytesWithTypeID(cv.type, serializeCVBytes(cv.value)); } function serializeListCV(cv: ListCV) { @@ -83,7 +84,7 @@ function serializeListCV(cv: ListCV) { bytesArray.push(length); for (const value of cv.list) { - const serializedValue = serializeCV(value); + const serializedValue = serializeCVBytes(value); bytesArray.push(serializedValue); } @@ -101,9 +102,9 @@ function serializeTupleCV(cv: TupleCV) { for (const key of lexicographicOrder) { const nameWithLength = createLPString(key); - bytesArray.push(serializeLPString(nameWithLength)); + bytesArray.push(serializeLPStringBytes(nameWithLength)); - const serializedValue = serializeCV(cv.data[key]); + const serializedValue = serializeCVBytes(cv.data[key]); bytesArray.push(serializedValue); } @@ -132,25 +133,23 @@ function serializeStringUtf8CV(cv: StringUtf8CV) { } /** - * Serializes clarity value to Uint8Array - * - * @param {ClarityValue} value to be converted to bytes - ** - * @returns {Uint8Array} returns the bytes - * + * Serializes clarity value to hex * @example * ``` * import { intCV, serializeCV } from '@stacks/transactions'; * * const serialized = serializeCV(intCV(100)); // Similarly works for other clarity types as well like listCV, booleanCV ... - * - * // + * // '0000000000000000000000000000000064' * ``` * * @see * {@link https://github.com/hirosystems/stacks.js/blob/main/packages/transactions/tests/clarity.test.ts | clarity test cases for more examples} */ -export function serializeCV(value: ClarityValue): Uint8Array { +export function serializeCV(value: ClarityValue): string { + return bytesToHex(serializeCVBytes(value)); +} +/** @ignore */ +export function serializeCVBytes(value: ClarityValue): Uint8Array { switch (value.type) { case ClarityType.BoolTrue: case ClarityType.BoolFalse: diff --git a/packages/transactions/src/constants.ts b/packages/transactions/src/constants.ts index 1ebc01acd..dbc41f752 100644 --- a/packages/transactions/src/constants.ts +++ b/packages/transactions/src/constants.ts @@ -20,7 +20,7 @@ export enum StacksMessageType { Principal, LengthPrefixedString, MemoString, - AssetInfo, + Asset, PostCondition, PublicKey, LengthPrefixedList, diff --git a/packages/transactions/src/fetch.ts b/packages/transactions/src/fetch.ts index fdb8f4361..636a6a3bc 100644 --- a/packages/transactions/src/fetch.ts +++ b/packages/transactions/src/fetch.ts @@ -6,15 +6,16 @@ import { validateHash256, with0x, } from '@stacks/common'; +import { deriveDefaultUrl } from '@stacks/network'; import { ClarityValue, NoneCV, deserializeCV, serializeCV } from './clarity'; +import { ClarityAbi } from './contract-abi'; import { NoEstimateAvailableError } from './errors'; -import { serializePayload } from './payload'; +import { serializePayloadBytes } from './payload'; import { StacksTransaction, deriveNetworkFromTx, estimateTransactionByteLength, } from './transaction'; -import { cvToHex, defaultApiFromNetwork, parseReadOnlyResponse } from './utils'; import { FeeEstimateResponse, FeeEstimation, @@ -22,8 +23,7 @@ import { TxBroadcastResultOk, TxBroadcastResultRejected, } from './types'; -import { deriveDefaultUrl } from '@stacks/network'; -import { ClarityAbi } from './contract-abi'; +import { cvToHex, defaultApiFromNetwork, parseReadOnlyResponse } from './utils'; export const BROADCAST_PATH = '/v2/transactions'; export const TRANSFER_FEE_ESTIMATE_PATH = '/v2/fees/transfer'; @@ -227,7 +227,7 @@ export async function estimateFee({ const estimatedLength = estimateTransactionByteLength(txOpt); return ( await estimateTransaction({ - payload: bytesToHex(serializePayload(txOpt.payload)), + payload: bytesToHex(serializePayloadBytes(txOpt.payload)), estimatedLength, api, }) @@ -344,7 +344,7 @@ export async function getContractMapEntry mapName: string; mapKey: ClarityValue; } & ApiParam): Promise { - const keyHex = with0x(bytesToHex(serializeCV(mapKey))); + const keyHex = with0x(serializeCV(mapKey)); const options = { method: 'POST', diff --git a/packages/transactions/src/index.ts b/packages/transactions/src/index.ts index 87eafc127..f01dbd141 100644 --- a/packages/transactions/src/index.ts +++ b/packages/transactions/src/index.ts @@ -71,6 +71,7 @@ export { isSmartContractPayload, isTokenTransferPayload, serializePayload, + serializePayloadBytes, } from './payload'; /** * ### `Pc.` Post Condition Builder diff --git a/packages/transactions/src/keys.ts b/packages/transactions/src/keys.ts index 847ae8e1f..d29a52268 100644 --- a/packages/transactions/src/keys.ts +++ b/packages/transactions/src/keys.ts @@ -13,6 +13,7 @@ import { hexToBigInt, hexToBytes, intToHex, + isInstance, parseRecoverableSignatureVrs, PRIVATE_KEY_COMPRESSED_LENGTH, privateKeyToBytes, @@ -128,7 +129,11 @@ export function publicKeyIsCompressed(publicKey: PublicKey): boolean { return !publicKeyToHex(publicKey).startsWith('04'); } -export function serializePublicKey(key: StacksPublicKey): Uint8Array { +export function serializePublicKey(key: StacksPublicKey): string { + return bytesToHex(serializePublicKeyBytes(key)); +} +/** @ignore */ +export function serializePublicKeyBytes(key: StacksPublicKey): Uint8Array { return key.data.slice(); } @@ -147,7 +152,14 @@ export function compressPublicKey(publicKey: PublicKey): string { return Point.fromHex(publicKeyToHex(publicKey)).toHex(true); } -export function deserializePublicKey(bytesReader: BytesReader): StacksPublicKey { +export function deserializePublicKey(serialized: string): StacksPublicKey { + return deserializePublicKeyBytes(hexToBytes(serialized)); +} +/** @ignore */ +export function deserializePublicKeyBytes(serialized: Uint8Array | BytesReader): StacksPublicKey { + const bytesReader = isInstance(serialized, BytesReader) + ? serialized + : new BytesReader(serialized); const fieldId = bytesReader.readUInt8(); const keyLength = fieldId === 4 ? UNCOMPRESSED_PUBKEY_LENGTH_BYTES : COMPRESSED_PUBKEY_LENGTH_BYTES; diff --git a/packages/transactions/src/payload.ts b/packages/transactions/src/payload.ts index 2b44d4f01..9229cf002 100644 --- a/packages/transactions/src/payload.ts +++ b/packages/transactions/src/payload.ts @@ -1,19 +1,28 @@ -import { concatArray, IntegerType, intToBigInt, intToBytes, writeUInt32BE } from '@stacks/common'; +import { + bytesToHex, + concatArray, + hexToBytes, + IntegerType, + intToBigInt, + intToBytes, + isInstance, + writeUInt32BE, +} from '@stacks/common'; import { ClarityVersion, COINBASE_BYTES_LENGTH, PayloadType, StacksMessageType } from './constants'; import { BytesReader } from './bytesReader'; -import { ClarityValue, deserializeCV, serializeCV } from './clarity/'; +import { ClarityValue, deserializeCV, serializeCVBytes } from './clarity/'; import { PrincipalCV, principalCV } from './clarity/types/principalCV'; import { Address } from './common'; import { createAddress, createLPString, LengthPrefixedString } from './postcondition-types'; import { codeBodyString, createMemoString, - deserializeAddress, - deserializeLPString, - deserializeMemoString, + deserializeAddressBytes, + deserializeLPStringBytes, + deserializeMemoStringBytes, MemoString, - serializeStacksMessage, + serializeStacksMessageBytes, } from './types'; export type Payload = @@ -202,36 +211,39 @@ export function createCoinbasePayload( coinbaseBytes, }; } - -export function serializePayload(payload: PayloadInput): Uint8Array { +export function serializePayload(payload: PayloadInput): string { + return bytesToHex(serializePayloadBytes(payload)); +} +/** @ignore */ +export function serializePayloadBytes(payload: PayloadInput): Uint8Array { const bytesArray = []; bytesArray.push(payload.payloadType); switch (payload.payloadType) { case PayloadType.TokenTransfer: - bytesArray.push(serializeCV(payload.recipient)); + bytesArray.push(serializeCVBytes(payload.recipient)); bytesArray.push(intToBytes(payload.amount, false, 8)); - bytesArray.push(serializeStacksMessage(payload.memo)); + bytesArray.push(serializeStacksMessageBytes(payload.memo)); break; case PayloadType.ContractCall: - bytesArray.push(serializeStacksMessage(payload.contractAddress)); - bytesArray.push(serializeStacksMessage(payload.contractName)); - bytesArray.push(serializeStacksMessage(payload.functionName)); + bytesArray.push(serializeStacksMessageBytes(payload.contractAddress)); + bytesArray.push(serializeStacksMessageBytes(payload.contractName)); + bytesArray.push(serializeStacksMessageBytes(payload.functionName)); const numArgs = new Uint8Array(4); writeUInt32BE(numArgs, payload.functionArgs.length, 0); bytesArray.push(numArgs); payload.functionArgs.forEach(arg => { - bytesArray.push(serializeCV(arg)); + bytesArray.push(serializeCVBytes(arg)); }); break; case PayloadType.SmartContract: - bytesArray.push(serializeStacksMessage(payload.contractName)); - bytesArray.push(serializeStacksMessage(payload.codeBody)); + bytesArray.push(serializeStacksMessageBytes(payload.contractName)); + bytesArray.push(serializeStacksMessageBytes(payload.codeBody)); break; case PayloadType.VersionedSmartContract: bytesArray.push(payload.clarityVersion); - bytesArray.push(serializeStacksMessage(payload.contractName)); - bytesArray.push(serializeStacksMessage(payload.codeBody)); + bytesArray.push(serializeStacksMessageBytes(payload.contractName)); + bytesArray.push(serializeStacksMessageBytes(payload.codeBody)); break; case PayloadType.PoisonMicroblock: // TODO: implement @@ -241,14 +253,21 @@ export function serializePayload(payload: PayloadInput): Uint8Array { break; case PayloadType.CoinbaseToAltRecipient: bytesArray.push(payload.coinbaseBytes); - bytesArray.push(serializeCV(payload.recipient)); + bytesArray.push(serializeCVBytes(payload.recipient)); break; } return concatArray(bytesArray); } -export function deserializePayload(bytesReader: BytesReader): Payload { +export function deserializePayload(serialized: string): Payload { + return deserializePayloadBytes(hexToBytes(serialized)); +} +/** @ignore */ +export function deserializePayloadBytes(serialized: Uint8Array | BytesReader): Payload { + const bytesReader = isInstance(serialized, BytesReader) + ? serialized + : new BytesReader(serialized); const payloadType = bytesReader.readUInt8Enum(PayloadType, n => { throw new Error(`Cannot recognize PayloadType: ${n}`); }); @@ -257,12 +276,12 @@ export function deserializePayload(bytesReader: BytesReader): Payload { case PayloadType.TokenTransfer: const recipient = deserializeCV(bytesReader) as PrincipalCV; const amount = intToBigInt(bytesReader.readBytes(8), false); - const memo = deserializeMemoString(bytesReader); + const memo = deserializeMemoStringBytes(bytesReader); return createTokenTransferPayload(recipient, amount, memo); case PayloadType.ContractCall: - const contractAddress = deserializeAddress(bytesReader); - const contractCallName = deserializeLPString(bytesReader); - const functionName = deserializeLPString(bytesReader); + const contractAddress = deserializeAddressBytes(bytesReader); + const contractCallName = deserializeLPStringBytes(bytesReader); + const functionName = deserializeLPStringBytes(bytesReader); const functionArgs: ClarityValue[] = []; const numberOfArgs = bytesReader.readUInt32BE(); for (let i = 0; i < numberOfArgs; i++) { @@ -276,16 +295,16 @@ export function deserializePayload(bytesReader: BytesReader): Payload { functionArgs ); case PayloadType.SmartContract: - const smartContractName = deserializeLPString(bytesReader); - const codeBody = deserializeLPString(bytesReader, 4, 100_000); + const smartContractName = deserializeLPStringBytes(bytesReader); + const codeBody = deserializeLPStringBytes(bytesReader, 4, 100_000); return createSmartContractPayload(smartContractName, codeBody); case PayloadType.VersionedSmartContract: { const clarityVersion = bytesReader.readUInt8Enum(ClarityVersion, n => { throw new Error(`Cannot recognize ClarityVersion: ${n}`); }); - const smartContractName = deserializeLPString(bytesReader); - const codeBody = deserializeLPString(bytesReader, 4, 100_000); + const smartContractName = deserializeLPStringBytes(bytesReader); + const codeBody = deserializeLPStringBytes(bytesReader, 4, 100_000); return createSmartContractPayload(smartContractName, codeBody, clarityVersion); } case PayloadType.PoisonMicroblock: diff --git a/packages/transactions/src/pc.ts b/packages/transactions/src/pc.ts index 0e60f6dca..0125afca3 100644 --- a/packages/transactions/src/pc.ts +++ b/packages/transactions/src/pc.ts @@ -9,7 +9,13 @@ import { } from './builders'; import { ClarityValue } from './clarity'; import { FungibleConditionCode, NonFungibleConditionCode } from './constants'; -import { createAssetInfo, NonFungiblePostCondition } from './postcondition-types'; +import { + AddressString, + AssetString, + ContractIdString, + createAsset, + NonFungiblePostCondition, +} from './postcondition-types'; /// `Pc.` Post Condition Builder // @@ -19,21 +25,6 @@ import { createAssetInfo, NonFungiblePostCondition } from './postcondition-types // PRINCIPAL -> [AMOUNT] -> CODE -> ASSET // -/** - * An address string encoded as c32check - */ -type AddressString = string; - -/** - * A contract identifier string given as `
.` - */ -type ContractIdString = `${string}.${string}`; - -/** - * An asset name string given as `::` aka `.::` - */ -type NftString = `${ContractIdString}::${string}`; - /** * ### `Pc.` Post Condition Builder * @beta Interface may be subject to change in future releases. @@ -234,14 +225,14 @@ class PartialPcFtWithCode { this.contractName, this.code, this.amount, - createAssetInfo(address, name, tokenName) + createAsset(address, name, tokenName) ); } return makeStandardFungiblePostCondition( this.address, this.code, this.amount, - createAssetInfo(address, name, tokenName) + createAsset(address, name, tokenName) ); } } @@ -261,7 +252,7 @@ class PartialPcNftWithCode { * @param assetName - The name of the NFT asset. Formatted as `.::`. * @param assetId - The asset identifier of the NFT. A Clarity value defining the single NFT instance. */ - nft(assetName: NftString, assetId: ClarityValue): NonFungiblePostCondition; + nft(assetName: AssetString, assetId: ClarityValue): NonFungiblePostCondition; /** * ### Non-Fungible Token Post Condition * @param contractId - The contract identifier of the NFT. Formatted as `.`. @@ -283,7 +274,7 @@ class PartialPcNftWithCode { this.principal, this.contractName, this.code, - createAssetInfo(contractAddress, contractName, tokenName), + createAsset(contractAddress, contractName, tokenName), assetId ); } @@ -291,7 +282,7 @@ class PartialPcNftWithCode { return makeStandardNonFungiblePostCondition( this.principal, this.code, - createAssetInfo(contractAddress, contractName, tokenName), + createAsset(contractAddress, contractName, tokenName), assetId ); } @@ -305,7 +296,7 @@ function parseContractId(contractId: ContractIdString) { } /** @internal */ -function parseNft(nftAssetName: NftString) { +function parseNft(nftAssetName: AssetString) { const [principal, tokenName] = nftAssetName.split('::') as [ContractIdString, string]; if (!principal || !tokenName) throw new Error(`Invalid fully-qualified nft asset name: ${nftAssetName}`); @@ -324,7 +315,7 @@ function isContractIdString(value: AddressString | ContractIdString): value is C * @internal */ function getNftArgs( - assetName: NftString, + asset: AssetString, assetId: ClarityValue ): { contractAddress: string; contractName: string; tokenName: string; assetId: ClarityValue }; function getNftArgs( diff --git a/packages/transactions/src/postcondition-types.ts b/packages/transactions/src/postcondition-types.ts index ab24d6701..9421d7323 100644 --- a/packages/transactions/src/postcondition-types.ts +++ b/packages/transactions/src/postcondition-types.ts @@ -11,6 +11,21 @@ import { Address } from './common'; import { ClarityValue } from './clarity'; import { exceedsMaxLengthBytes } from './utils'; +/** + * An address string encoded as c32check + */ +export type AddressString = string; + +/** + * A contract identifier string given as `
.` + */ +export type ContractIdString = `${string}.${string}`; + +/** + * An asset name string given as `::` aka `.::` + */ +export type AssetString = `${ContractIdString}::${string}`; + export interface StandardPrincipal { readonly type: StacksMessageType.Principal; readonly prefix: PostConditionPrincipalId.Standard; @@ -31,8 +46,8 @@ export interface LengthPrefixedString { readonly maxLengthBytes: number; } -export interface AssetInfo { - readonly type: StacksMessageType.AssetInfo; +export interface Asset { + readonly type: StacksMessageType.Asset; readonly address: Address; readonly contractName: LengthPrefixedString; readonly assetName: LengthPrefixedString; @@ -52,7 +67,7 @@ export interface FungiblePostCondition { readonly principal: PostConditionPrincipal; readonly conditionCode: FungibleConditionCode; readonly amount: bigint; - readonly assetInfo: AssetInfo; + readonly asset: Asset; } export interface NonFungiblePostCondition { @@ -61,15 +76,15 @@ export interface NonFungiblePostCondition { readonly principal: PostConditionPrincipal; readonly conditionCode: NonFungibleConditionCode; /** Structure that identifies the token type. */ - readonly assetInfo: AssetInfo; + readonly asset: Asset; /** The Clarity value that names the token instance. */ readonly assetName: ClarityValue; } -export function parseAssetInfoString(id: string): AssetInfo { +export function parseAssetString(id: AssetString): Asset { const [assetAddress, assetContractName, assetTokenName] = id.split(/\.|::/); - const assetInfo = createAssetInfo(assetAddress, assetContractName, assetTokenName); - return assetInfo; + const asset = createAsset(assetAddress, assetContractName, assetTokenName); + return asset; } export function createLPString(content: string): LengthPrefixedString; @@ -97,13 +112,9 @@ export function createLPString( }; } -export function createAssetInfo( - addressString: string, - contractName: string, - assetName: string -): AssetInfo { +export function createAsset(addressString: string, contractName: string, assetName: string): Asset { return { - type: StacksMessageType.AssetInfo, + type: StacksMessageType.Asset, address: createAddress(addressString), contractName: createLPString(contractName), assetName: createLPString(assetName), diff --git a/packages/transactions/src/postcondition.ts b/packages/transactions/src/postcondition.ts index caf448764..d38fdf33a 100644 --- a/packages/transactions/src/postcondition.ts +++ b/packages/transactions/src/postcondition.ts @@ -7,12 +7,13 @@ import { StacksMessageType, } from './constants'; import { - AssetInfo, + Asset, + AssetString, FungiblePostCondition, NonFungiblePostCondition, PostConditionPrincipal, STXPostCondition, - parseAssetInfoString, + parseAssetString, parsePrincipalString, } from './postcondition-types'; @@ -38,13 +39,13 @@ export function createFungiblePostCondition( principal: string | PostConditionPrincipal, conditionCode: FungibleConditionCode, amount: IntegerType, - assetInfo: string | AssetInfo + asset: AssetString | Asset ): FungiblePostCondition { if (typeof principal === 'string') { principal = parsePrincipalString(principal); } - if (typeof assetInfo === 'string') { - assetInfo = parseAssetInfoString(assetInfo); + if (typeof asset === 'string') { + asset = parseAssetString(asset); } return { @@ -53,21 +54,21 @@ export function createFungiblePostCondition( principal, conditionCode, amount: intToBigInt(amount, false), - assetInfo, + asset: asset, }; } export function createNonFungiblePostCondition( principal: string | PostConditionPrincipal, conditionCode: NonFungibleConditionCode, - assetInfo: string | AssetInfo, + asset: AssetString | Asset, assetName: ClarityValue ): NonFungiblePostCondition { if (typeof principal === 'string') { principal = parsePrincipalString(principal); } - if (typeof assetInfo === 'string') { - assetInfo = parseAssetInfoString(assetInfo); + if (typeof asset === 'string') { + asset = parseAssetString(asset); } return { @@ -75,7 +76,7 @@ export function createNonFungiblePostCondition( conditionType: PostConditionType.NonFungible, principal, conditionCode, - assetInfo, + asset: asset, assetName, }; } diff --git a/packages/transactions/src/signature.ts b/packages/transactions/src/signature.ts index 4b62465b4..1b5d7024b 100644 --- a/packages/transactions/src/signature.ts +++ b/packages/transactions/src/signature.ts @@ -3,15 +3,15 @@ import { DeserializationError } from './errors'; import { PubKeyEncoding, RECOVERABLE_ECDSA_SIG_LENGTH_BYTES, StacksMessageType } from './constants'; import { compressPublicKey, - deserializePublicKey, - serializePublicKey, + deserializePublicKeyBytes, + serializePublicKeyBytes, StacksPublicKey, } from './keys'; import { createMessageSignature, MessageSignature } from './common'; // @ts-ignore -import { bytesToHex, concatArray, hexToBytes } from '@stacks/common'; +import { bytesToHex, concatArray, hexToBytes, isInstance } from '@stacks/common'; export enum AuthFieldType { PublicKeyCompressed = 0x00, @@ -51,7 +51,16 @@ export function createTransactionAuthField( }; } -export function deserializeTransactionAuthField(bytesReader: BytesReader): TransactionAuthField { +export function deserializeTransactionAuthField(serialized: string): TransactionAuthField { + return deserializeTransactionAuthFieldBytes(hexToBytes(serialized)); +} +/** @ignore */ +export function deserializeTransactionAuthFieldBytes( + serialized: Uint8Array | BytesReader +): TransactionAuthField { + const bytesReader = isInstance(serialized, BytesReader) + ? serialized + : new BytesReader(serialized); const authFieldType = bytesReader.readUInt8Enum(AuthFieldType, n => { throw new DeserializationError(`Could not read ${n} as AuthFieldType`); }); @@ -60,12 +69,12 @@ export function deserializeTransactionAuthField(bytesReader: BytesReader): Trans case AuthFieldType.PublicKeyCompressed: return createTransactionAuthField( PubKeyEncoding.Compressed, - deserializePublicKey(bytesReader) + deserializePublicKeyBytes(bytesReader) ); case AuthFieldType.PublicKeyUncompressed: return createTransactionAuthField( PubKeyEncoding.Uncompressed, - deserializePublicKey(bytesReader) + deserializePublicKeyBytes(bytesReader) ); case AuthFieldType.SignatureCompressed: return createTransactionAuthField( @@ -82,18 +91,26 @@ export function deserializeTransactionAuthField(bytesReader: BytesReader): Trans } } -export function serializeMessageSignature(messageSignature: MessageSignature): Uint8Array { +export function serializeMessageSignature(messageSignature: MessageSignature): string { + return bytesToHex(serializeMessageSignatureBytes(messageSignature)); +} +/** @ignore */ +export function serializeMessageSignatureBytes(messageSignature: MessageSignature): Uint8Array { return hexToBytes(messageSignature.data); } -export function serializeTransactionAuthField(field: TransactionAuthField): Uint8Array { +export function serializeTransactionAuthField(field: TransactionAuthField): string { + return bytesToHex(serializeTransactionAuthFieldBytes(field)); +} +/** @ignore */ +export function serializeTransactionAuthFieldBytes(field: TransactionAuthField): Uint8Array { const bytesArray = []; switch (field.contents.type) { case StacksMessageType.PublicKey: if (field.pubKeyEncoding == PubKeyEncoding.Compressed) { bytesArray.push(AuthFieldType.PublicKeyCompressed); - bytesArray.push(serializePublicKey(field.contents)); + bytesArray.push(serializePublicKeyBytes(field.contents)); } else { bytesArray.push(AuthFieldType.PublicKeyUncompressed); bytesArray.push(hexToBytes(compressPublicKey(field.contents.data))); @@ -105,7 +122,7 @@ export function serializeTransactionAuthField(field: TransactionAuthField): Uint } else { bytesArray.push(AuthFieldType.SignatureUncompressed); } - bytesArray.push(serializeMessageSignature(field.contents)); + bytesArray.push(serializeMessageSignatureBytes(field.contents)); break; } diff --git a/packages/transactions/src/structuredDataSignature.ts b/packages/transactions/src/structuredDataSignature.ts index 391db4213..998b3b54a 100644 --- a/packages/transactions/src/structuredDataSignature.ts +++ b/packages/transactions/src/structuredDataSignature.ts @@ -1,6 +1,6 @@ import { sha256 } from '@noble/hashes/sha256'; import { bytesToHex, concatBytes, utf8ToBytes } from '@stacks/common'; -import { ClarityType, ClarityValue, serializeCV } from './clarity'; +import { ClarityType, ClarityValue, serializeCVBytes } from './clarity'; import { StacksMessageType } from './constants'; import { StructuredDataSignature } from './message-types'; import { PrivateKey, signMessageHashRsv } from './keys'; @@ -10,7 +10,7 @@ import { PrivateKey, signMessageHashRsv } from './keys'; export const STRUCTURED_DATA_PREFIX = new Uint8Array([0x53, 0x49, 0x50, 0x30, 0x31, 0x38]); export function hashStructuredData(structuredData: ClarityValue): Uint8Array { - return sha256(serializeCV(structuredData)); + return sha256(serializeCVBytes(structuredData)); } const hash256BytesLength = 32; diff --git a/packages/transactions/src/transaction.ts b/packages/transactions/src/transaction.ts index 439dcf7ed..44da13be8 100644 --- a/packages/transactions/src/transaction.ts +++ b/packages/transactions/src/transaction.ts @@ -31,9 +31,14 @@ import { createTransactionAuthField } from './signature'; import { cloneDeep, txidFromData } from './utils'; -import { deserializePayload, Payload, PayloadInput, serializePayload } from './payload'; +import { deserializePayloadBytes, Payload, PayloadInput, serializePayloadBytes } from './payload'; -import { createLPList, deserializeLPList, LengthPrefixedList, serializeLPList } from './types'; +import { + createLPList, + deserializeLPListBytes, + LengthPrefixedList, + serializeLPListBytes, +} from './types'; import { PrivateKey, privateKeyIsCompressed, publicKeyIsCompressed, StacksPublicKey } from './keys'; @@ -254,8 +259,8 @@ export class StacksTransaction { bytesArray.push(serializeAuthorization(this.auth)); bytesArray.push(this.anchorMode); bytesArray.push(this.postConditionMode); - bytesArray.push(serializeLPList(this.postConditions)); - bytesArray.push(serializePayload(this.payload)); + bytesArray.push(serializeLPListBytes(this.postConditions)); + bytesArray.push(serializePayloadBytes(this.payload)); return concatArray(bytesArray); } @@ -288,8 +293,8 @@ export function deserializeTransaction(tx: string | Uint8Array | BytesReader) { const postConditionMode = bytesReader.readUInt8Enum(PostConditionMode, n => { throw new Error(`Could not parse ${n} as PostConditionMode`); }); - const postConditions = deserializeLPList(bytesReader, StacksMessageType.PostCondition); - const payload = deserializePayload(bytesReader); + const postConditions = deserializeLPListBytes(bytesReader, StacksMessageType.PostCondition); + const payload = deserializePayloadBytes(bytesReader); return new StacksTransaction( version, diff --git a/packages/transactions/src/types.ts b/packages/transactions/src/types.ts index 48d7f2c5d..ced488a1a 100644 --- a/packages/transactions/src/types.ts +++ b/packages/transactions/src/types.ts @@ -6,11 +6,12 @@ import { hexToInt, intToBytes, intToHex, + isInstance, utf8ToBytes, } from '@stacks/common'; import { StacksNetwork, StacksNetworkName, TransactionVersion } from '@stacks/network'; import { BytesReader } from './bytesReader'; -import { ClarityValue, deserializeCV, serializeCV } from './clarity'; +import { ClarityValue, deserializeCV, serializeCVBytes } from './clarity'; import { Address, MessageSignature, @@ -30,13 +31,13 @@ import { import { DeserializationError, SerializationError } from './errors'; import { StacksPublicKey, - deserializePublicKey, + deserializePublicKeyBytes, publicKeyIsCompressed, - serializePublicKey, + serializePublicKeyBytes, } from './keys'; -import { Payload, deserializePayload, serializePayload } from './payload'; +import { Payload, deserializePayloadBytes, serializePayloadBytes } from './payload'; import { - AssetInfo, + Asset as Asset, ContractPrincipal, LengthPrefixedString, PostCondition, @@ -47,9 +48,9 @@ import { import { TransactionAuthField, deserializeMessageSignature, - deserializeTransactionAuthField, - serializeMessageSignature, - serializeTransactionAuthField, + deserializeTransactionAuthFieldBytes, + serializeMessageSignatureBytes, + serializeTransactionAuthFieldBytes, } from './signature'; import { exceedsMaxLengthBytes, @@ -67,36 +68,40 @@ export type StacksMessage = | LengthPrefixedList | Payload | MemoString - | AssetInfo + | Asset | PostCondition | StacksPublicKey | TransactionAuthField | MessageSignature; -export function serializeStacksMessage(message: StacksMessage): Uint8Array { +export function serializeStacksMessage(message: StacksMessage): string { + return bytesToHex(serializeStacksMessageBytes(message)); +} +/** @ignore */ +export function serializeStacksMessageBytes(message: StacksMessage): Uint8Array { switch (message.type) { case StacksMessageType.Address: - return serializeAddress(message); + return serializeAddressBytes(message); case StacksMessageType.Principal: - return serializePrincipal(message); + return serializePrincipalBytes(message); case StacksMessageType.LengthPrefixedString: - return serializeLPString(message); + return serializeLPStringBytes(message); case StacksMessageType.MemoString: - return serializeMemoString(message); - case StacksMessageType.AssetInfo: - return serializeAssetInfo(message); + return serializeMemoStringBytes(message); + case StacksMessageType.Asset: + return serializeAssetBytes(message); case StacksMessageType.PostCondition: - return serializePostCondition(message); + return serializePostConditionBytes(message); case StacksMessageType.PublicKey: - return serializePublicKey(message); + return serializePublicKeyBytes(message); case StacksMessageType.LengthPrefixedList: - return serializeLPList(message); + return serializeLPListBytes(message); case StacksMessageType.Payload: - return serializePayload(message); + return serializePayloadBytes(message); case StacksMessageType.TransactionAuthField: - return serializeTransactionAuthField(message); + return serializeTransactionAuthFieldBytes(message); case StacksMessageType.MessageSignature: - return serializeMessageSignature(message); + return serializeMessageSignatureBytes(message); } } @@ -107,26 +112,26 @@ export function deserializeStacksMessage( ): StacksMessage { switch (type) { case StacksMessageType.Address: - return deserializeAddress(bytesReader); + return deserializeAddressBytes(bytesReader); case StacksMessageType.Principal: - return deserializePrincipal(bytesReader); + return deserializePrincipalBytes(bytesReader); case StacksMessageType.LengthPrefixedString: - return deserializeLPString(bytesReader); + return deserializeLPStringBytes(bytesReader); case StacksMessageType.MemoString: - return deserializeMemoString(bytesReader); - case StacksMessageType.AssetInfo: - return deserializeAssetInfo(bytesReader); + return deserializeMemoStringBytes(bytesReader); + case StacksMessageType.Asset: + return deserializeAssetBytes(bytesReader); case StacksMessageType.PostCondition: - return deserializePostCondition(bytesReader); + return deserializePostConditionBytes(bytesReader); case StacksMessageType.PublicKey: - return deserializePublicKey(bytesReader); + return deserializePublicKeyBytes(bytesReader); case StacksMessageType.Payload: - return deserializePayload(bytesReader); + return deserializePayloadBytes(bytesReader); case StacksMessageType.LengthPrefixedList: if (!listType) { throw new DeserializationError('No List Type specified'); } - return deserializeLPList(bytesReader, listType); + return deserializeLPListBytes(bytesReader, listType); case StacksMessageType.MessageSignature: return deserializeMessageSignature(bytesReader); default: @@ -181,48 +186,75 @@ export function addressFromPublicKeys( case AddressHashMode.SerializeP2WPKH: return addressFromVersionHash(version, hashP2WPKH(publicKeys[0].data)); case AddressHashMode.SerializeP2SH: - return addressFromVersionHash(version, hashP2SH(numSigs, publicKeys.map(serializePublicKey))); + return addressFromVersionHash( + version, + hashP2SH(numSigs, publicKeys.map(serializePublicKeyBytes)) + ); case AddressHashMode.SerializeP2WSH: return addressFromVersionHash( version, - hashP2WSH(numSigs, publicKeys.map(serializePublicKey)) + hashP2WSH(numSigs, publicKeys.map(serializePublicKeyBytes)) ); } } -export function serializeAddress(address: Address): Uint8Array { +export function serializeAddress(address: Address): string { + return bytesToHex(serializeAddressBytes(address)); +} +/** @ignore */ +export function serializeAddressBytes(address: Address): Uint8Array { const bytesArray = []; bytesArray.push(hexToBytes(intToHex(address.version, 1))); bytesArray.push(hexToBytes(address.hash160)); return concatArray(bytesArray); } -export function deserializeAddress(bytesReader: BytesReader): Address { +export function deserializeAddress(serialized: string): Address { + return deserializeAddressBytes(hexToBytes(serialized)); +} +/** @ignore */ +export function deserializeAddressBytes(serialized: Uint8Array | BytesReader): Address { + const bytesReader = isInstance(serialized, BytesReader) + ? serialized + : new BytesReader(serialized); const version = hexToInt(bytesToHex(bytesReader.readBytes(1))); const data = bytesToHex(bytesReader.readBytes(20)); return { type: StacksMessageType.Address, version, hash160: data }; } -export function serializePrincipal(principal: PostConditionPrincipal): Uint8Array { +export function serializePrincipal(principal: PostConditionPrincipal): string { + return bytesToHex(serializePrincipalBytes(principal)); +} +/** @ignore */ +export function serializePrincipalBytes(principal: PostConditionPrincipal): Uint8Array { const bytesArray = []; bytesArray.push(principal.prefix); - bytesArray.push(serializeAddress(principal.address)); + bytesArray.push(serializeAddressBytes(principal.address)); if (principal.prefix === PostConditionPrincipalId.Contract) { - bytesArray.push(serializeLPString(principal.contractName)); + bytesArray.push(serializeLPStringBytes(principal.contractName)); } return concatArray(bytesArray); } -export function deserializePrincipal(bytesReader: BytesReader): PostConditionPrincipal { +export function deserializePrincipal(serialized: string): PostConditionPrincipal { + return deserializePrincipalBytes(hexToBytes(serialized)); +} +/** @ignore */ +export function deserializePrincipalBytes( + serialized: Uint8Array | BytesReader +): PostConditionPrincipal { + const bytesReader = isInstance(serialized, BytesReader) + ? serialized + : new BytesReader(serialized); const prefix = bytesReader.readUInt8Enum(PostConditionPrincipalId, n => { throw new DeserializationError(`Unexpected Principal payload type: ${n}`); }); - const address = deserializeAddress(bytesReader); + const address = deserializeAddressBytes(bytesReader); if (prefix === PostConditionPrincipalId.Standard) { return { type: StacksMessageType.Principal, prefix, address } as StandardPrincipal; } - const contractName = deserializeLPString(bytesReader); + const contractName = deserializeLPStringBytes(bytesReader); return { type: StacksMessageType.Principal, prefix, @@ -231,7 +263,11 @@ export function deserializePrincipal(bytesReader: BytesReader): PostConditionPri } as ContractPrincipal; } -export function serializeLPString(lps: LengthPrefixedString) { +export function serializeLPString(lps: LengthPrefixedString): string { + return bytesToHex(serializeLPStringBytes(lps)); +} +/** @ignore */ +export function serializeLPStringBytes(lps: LengthPrefixedString): Uint8Array { const bytesArray = []; const contentBytes = utf8ToBytes(lps.content); const length = contentBytes.byteLength; @@ -241,11 +277,22 @@ export function serializeLPString(lps: LengthPrefixedString) { } export function deserializeLPString( - bytesReader: BytesReader, + serialized: string, + prefixBytes?: number, + maxLength?: number +): LengthPrefixedString { + return deserializeLPStringBytes(hexToBytes(serialized), prefixBytes, maxLength); +} +/** @ignore */ +export function deserializeLPStringBytes( + serialized: Uint8Array | BytesReader, prefixBytes?: number, maxLength?: number ): LengthPrefixedString { prefixBytes = prefixBytes ? prefixBytes : 1; + const bytesReader = isInstance(serialized, BytesReader) + ? serialized + : new BytesReader(serialized); const length = hexToInt(bytesToHex(bytesReader.readBytes(prefixBytes))); const content = bytesToUtf8(bytesReader.readBytes(length)); return createLPString(content, prefixBytes, maxLength ?? 128); @@ -267,7 +314,11 @@ export function createMemoString(content: string): MemoString { return { type: StacksMessageType.MemoString, content }; } -export function serializeMemoString(memoString: MemoString): Uint8Array { +export function serializeMemoString(memoString: MemoString): string { + return bytesToHex(serializeMemoStringBytes(memoString)); +} +/** @ignore */ +export function serializeMemoStringBytes(memoString: MemoString): Uint8Array { const bytesArray = []; const contentBytes = utf8ToBytes(memoString.content); const paddedContent = rightPadHexToLength(bytesToHex(contentBytes), MEMO_MAX_LENGTH_BYTES * 2); @@ -275,26 +326,44 @@ export function serializeMemoString(memoString: MemoString): Uint8Array { return concatArray(bytesArray); } -export function deserializeMemoString(bytesReader: BytesReader): MemoString { +export function deserializeMemoString(serialized: string): MemoString { + return deserializeMemoStringBytes(hexToBytes(serialized)); +} +/** @ignore */ +export function deserializeMemoStringBytes(serialized: Uint8Array | BytesReader): MemoString { + const bytesReader = isInstance(serialized, BytesReader) + ? serialized + : new BytesReader(serialized); let content = bytesToUtf8(bytesReader.readBytes(MEMO_MAX_LENGTH_BYTES)); content = content.replace(/\u0000*$/, ''); // remove all trailing unicode null characters return { type: StacksMessageType.MemoString, content }; } -export function serializeAssetInfo(info: AssetInfo): Uint8Array { +export function serializeAsset(info: Asset): string { + return bytesToHex(serializeAssetBytes(info)); +} +/** @ignore */ +export function serializeAssetBytes(info: Asset): Uint8Array { const bytesArray = []; - bytesArray.push(serializeAddress(info.address)); - bytesArray.push(serializeLPString(info.contractName)); - bytesArray.push(serializeLPString(info.assetName)); + bytesArray.push(serializeAddressBytes(info.address)); + bytesArray.push(serializeLPStringBytes(info.contractName)); + bytesArray.push(serializeLPStringBytes(info.assetName)); return concatArray(bytesArray); } -export function deserializeAssetInfo(bytesReader: BytesReader): AssetInfo { +export function deserializeAsset(serialized: string): Asset { + return deserializeAssetBytes(hexToBytes(serialized)); +} +/** @ignore */ +export function deserializeAssetBytes(serialized: Uint8Array | BytesReader): Asset { + const bytesReader = isInstance(serialized, BytesReader) + ? serialized + : new BytesReader(serialized); return { - type: StacksMessageType.AssetInfo, - address: deserializeAddress(bytesReader), - contractName: deserializeLPString(bytesReader), - assetName: deserializeLPString(bytesReader), + type: StacksMessageType.Asset, + address: deserializeAddressBytes(bytesReader), + contractName: deserializeLPStringBytes(bytesReader), + assetName: deserializeLPStringBytes(bytesReader), }; } @@ -315,66 +384,85 @@ export function createLPList( }; } -export function serializeLPList(lpList: LengthPrefixedList): Uint8Array { +export function serializeLPList(lpList: LengthPrefixedList): string { + return bytesToHex(serializeLPListBytes(lpList)); +} +/** @ignore */ +export function serializeLPListBytes(lpList: LengthPrefixedList): Uint8Array { const list = lpList.values; const bytesArray = []; bytesArray.push(hexToBytes(intToHex(list.length, lpList.lengthPrefixBytes))); for (const l of list) { - bytesArray.push(serializeStacksMessage(l)); + bytesArray.push(serializeStacksMessageBytes(l)); } return concatArray(bytesArray); } export function deserializeLPList( - bytesReader: BytesReader, + serialized: string, type: StacksMessageType, lengthPrefixBytes?: number ): LengthPrefixedList { + return deserializeLPListBytes(hexToBytes(serialized), type, lengthPrefixBytes); +} +/** @ignore */ +export function deserializeLPListBytes( + serialized: Uint8Array | BytesReader, + type: StacksMessageType, + lengthPrefixBytes?: number +): LengthPrefixedList { + const bytesReader = isInstance(serialized, BytesReader) + ? serialized + : new BytesReader(serialized); const length = hexToInt(bytesToHex(bytesReader.readBytes(lengthPrefixBytes || 4))); const l: StacksMessage[] = []; for (let index = 0; index < length; index++) { switch (type) { case StacksMessageType.Address: - l.push(deserializeAddress(bytesReader)); + l.push(deserializeAddressBytes(bytesReader)); break; case StacksMessageType.LengthPrefixedString: - l.push(deserializeLPString(bytesReader)); + l.push(deserializeLPStringBytes(bytesReader)); break; case StacksMessageType.MemoString: - l.push(deserializeMemoString(bytesReader)); + l.push(deserializeMemoStringBytes(bytesReader)); break; - case StacksMessageType.AssetInfo: - l.push(deserializeAssetInfo(bytesReader)); + case StacksMessageType.Asset: + l.push(deserializeAssetBytes(bytesReader)); break; case StacksMessageType.PostCondition: - l.push(deserializePostCondition(bytesReader)); + l.push(deserializePostConditionBytes(bytesReader)); break; case StacksMessageType.PublicKey: - l.push(deserializePublicKey(bytesReader)); + l.push(deserializePublicKeyBytes(bytesReader)); break; case StacksMessageType.TransactionAuthField: - l.push(deserializeTransactionAuthField(bytesReader)); + l.push(deserializeTransactionAuthFieldBytes(bytesReader)); break; } } return createLPList(l, lengthPrefixBytes); } -export function serializePostCondition(postCondition: PostCondition): Uint8Array { +export function serializePostCondition(postCondition: PostCondition): string { + return bytesToHex(serializePostConditionBytes(postCondition)); +} +/** @ignore */ +export function serializePostConditionBytes(postCondition: PostCondition): Uint8Array { const bytesArray = []; bytesArray.push(postCondition.conditionType); - bytesArray.push(serializePrincipal(postCondition.principal)); + bytesArray.push(serializePrincipalBytes(postCondition.principal)); if ( postCondition.conditionType === PostConditionType.Fungible || postCondition.conditionType === PostConditionType.NonFungible ) { - bytesArray.push(serializeAssetInfo(postCondition.assetInfo)); + bytesArray.push(serializeAssetBytes(postCondition.asset)); } if (postCondition.conditionType === PostConditionType.NonFungible) { - bytesArray.push(serializeCV(postCondition.assetName)); + bytesArray.push(serializeCVBytes(postCondition.assetName)); } bytesArray.push(postCondition.conditionCode); @@ -392,15 +480,22 @@ export function serializePostCondition(postCondition: PostCondition): Uint8Array return concatArray(bytesArray); } -export function deserializePostCondition(bytesReader: BytesReader): PostCondition { +export function deserializePostCondition(serialized: string): PostCondition { + return deserializePostConditionBytes(hexToBytes(serialized)); +} +/** @ignore */ +export function deserializePostConditionBytes(serialized: Uint8Array | BytesReader): PostCondition { + const bytesReader = isInstance(serialized, BytesReader) + ? serialized + : new BytesReader(serialized); const postConditionType = bytesReader.readUInt8Enum(PostConditionType, n => { throw new DeserializationError(`Could not read ${n} as PostConditionType`); }); - const principal = deserializePrincipal(bytesReader); + const principal = deserializePrincipalBytes(bytesReader); let conditionCode; - let assetInfo; + let asset; let amount: bigint; switch (postConditionType) { case PostConditionType.STX: @@ -416,7 +511,7 @@ export function deserializePostCondition(bytesReader: BytesReader): PostConditio amount, }; case PostConditionType.Fungible: - assetInfo = deserializeAssetInfo(bytesReader); + asset = deserializeAssetBytes(bytesReader); conditionCode = bytesReader.readUInt8Enum(FungibleConditionCode, n => { throw new DeserializationError(`Could not read ${n} as FungibleConditionCode`); }); @@ -427,10 +522,10 @@ export function deserializePostCondition(bytesReader: BytesReader): PostConditio principal, conditionCode, amount, - assetInfo, + asset: asset, }; case PostConditionType.NonFungible: - assetInfo = deserializeAssetInfo(bytesReader); + asset = deserializeAssetBytes(bytesReader); const assetName = deserializeCV(bytesReader); conditionCode = bytesReader.readUInt8Enum(NonFungibleConditionCode, n => { throw new DeserializationError(`Could not read ${n} as FungibleConditionCode`); @@ -440,7 +535,7 @@ export function deserializePostCondition(bytesReader: BytesReader): PostConditio conditionType: PostConditionType.NonFungible, principal, conditionCode, - assetInfo, + asset: asset, assetName, }; } diff --git a/packages/transactions/src/utils.ts b/packages/transactions/src/utils.ts index d6130ec9b..2e623d858 100644 --- a/packages/transactions/src/utils.ts +++ b/packages/transactions/src/utils.ts @@ -153,7 +153,7 @@ export function isClarityName(name: string) { */ export function cvToHex(cv: ClarityValue) { const serialized = serializeCV(cv); - return `0x${bytesToHex(serialized)}`; + return `0x${serialized}`; } /** diff --git a/packages/transactions/tests/builder.test.ts b/packages/transactions/tests/builder.test.ts index d2bbd2561..0d0e79cba 100644 --- a/packages/transactions/tests/builder.test.ts +++ b/packages/transactions/tests/builder.test.ts @@ -34,8 +34,8 @@ import { privateKeyToPublic, publicKeyIsCompressed, publicKeyToHex, - serializePostCondition, - serializePublicKey, + serializePostConditionBytes, + serializePublicKeyBytes, } from '../src'; import { MultiSigSpendingCondition, @@ -72,6 +72,7 @@ import { bufferCVFromString, noneCV, serializeCV, + serializeCVBytes, standardPrincipalCV, uintCV, } from '../src/clarity'; @@ -90,8 +91,12 @@ import { PubKeyEncoding, TxRejectedReason, } from '../src/constants'; -import { TokenTransferPayload, createTokenTransferPayload, serializePayload } from '../src/payload'; -import { createAssetInfo } from '../src/postcondition-types'; +import { + TokenTransferPayload, + createTokenTransferPayload, + serializePayloadBytes, +} from '../src/payload'; +import { createAsset } from '../src/postcondition-types'; import { createTransactionAuthField } from '../src/signature'; import { TransactionSigner } from '../src/signer'; import { @@ -395,7 +400,7 @@ test('Make Multi-Sig STX token transfer', async () => { ]; const pubKeys = privKeys.map(privateKeyToPublic).map(createStacksPublicKey); - const pubKeyStrings = pubKeys.map(serializePublicKey).map(publicKeyToHex); + const pubKeyStrings = pubKeys.map(serializePublicKeyBytes).map(publicKeyToHex); const transaction = await makeUnsignedSTXTokenTransfer({ recipient, @@ -470,7 +475,7 @@ test('Should deserialize partially signed multi-Sig STX token transfer', async ( ]; const pubKeys = privKeys.map(privateKeyToPublic).map(createStacksPublicKey); - const pubKeyStrings = pubKeys.map(serializePublicKey).map(publicKeyToHex); + const pubKeyStrings = pubKeys.map(serializePublicKeyBytes).map(publicKeyToHex); const transaction = await makeUnsignedSTXTokenTransfer({ recipient, @@ -547,7 +552,7 @@ test('Should throw error if multisig transaction is oversigned', async () => { ]; const pubKeys = privKeys.map(privateKeyToPublic).map(createStacksPublicKey); - const pubKeyStrings = pubKeys.map(serializePublicKey).map(publicKeyToHex); + const pubKeyStrings = pubKeys.map(serializePublicKeyBytes).map(publicKeyToHex); const transaction = await makeUnsignedSTXTokenTransfer({ recipient, @@ -591,7 +596,7 @@ test('Make Multi-Sig STX token transfer with two transaction signers', async () ]; const pubKeys = privKeys.map(privateKeyToPublic).map(createStacksPublicKey); - const pubKeyStrings = pubKeys.map(serializePublicKey).map(publicKeyToHex); + const pubKeyStrings = pubKeys.map(serializePublicKeyBytes).map(publicKeyToHex); const transaction = await makeUnsignedSTXTokenTransfer({ recipient, @@ -837,7 +842,7 @@ test('make a multi-sig contract deploy', async () => { ]; const pubKeys = privKeys.map(privateKeyToPublic).map(createStacksPublicKey); - const pubKeyStrings = pubKeys.map(serializePublicKey).map(publicKeyToHex); + const pubKeyStrings = pubKeys.map(serializePublicKeyBytes).map(publicKeyToHex); const transaction = await makeContractDeploy({ codeBody, @@ -955,7 +960,7 @@ test('Make contract-call with post conditions', async () => { const assetAddress = 'ST34RKEJKQES7MXQFBT29KSJZD73QK3YNT5N56C6X'; const assetContractName = 'test-asset-contract'; const assetName = 'test-asset-name'; - const info = createAssetInfo(assetAddress, assetContractName, assetName); + const info = createAsset(assetAddress, assetContractName, assetName); const tokenAssetName = 'token-asset-name'; const fee = 0; @@ -1111,7 +1116,7 @@ test('make a multi-sig contract call', async () => { ]; const pubKeys = privKeys.map(privateKeyToPublic).map(createStacksPublicKey); - const pubKeyStrings = pubKeys.map(serializePublicKey).map(publicKeyToHex); + const pubKeyStrings = pubKeys.map(serializePublicKeyBytes).map(publicKeyToHex); const transaction = await makeContractCall({ contractAddress, @@ -1186,7 +1191,7 @@ test('Estimate transaction transfer fee', async () => { const mainnet = {}; // default const resultEstimateFee = await estimateTransaction({ - payload: bytesToHex(serializePayload(transaction.payload)), + payload: bytesToHex(serializePayloadBytes(transaction.payload)), estimatedLength: transactionByteLength, api: mainnet, }); @@ -1195,7 +1200,7 @@ test('Estimate transaction transfer fee', async () => { const testnet = { url: HIRO_TESTNET_URL }; const resultEstimateFee2 = await estimateTransaction({ - payload: bytesToHex(serializePayload(transaction.payload)), + payload: bytesToHex(serializePayloadBytes(transaction.payload)), estimatedLength: transactionByteLength, api: testnet, }); @@ -1204,14 +1209,14 @@ test('Estimate transaction transfer fee', async () => { expect(fetchMock.mock.calls[0][0]).toEqual(`${HIRO_MAINNET_URL}${TRANSACTION_FEE_ESTIMATE_PATH}`); expect(fetchMock.mock.calls[0][1]?.body).toEqual( JSON.stringify({ - transaction_payload: bytesToHex(serializePayload(transaction.payload)), + transaction_payload: bytesToHex(serializePayloadBytes(transaction.payload)), estimated_len: transactionByteLength, }) ); expect(fetchMock.mock.calls[1][0]).toEqual(`${HIRO_TESTNET_URL}${TRANSACTION_FEE_ESTIMATE_PATH}`); expect(fetchMock.mock.calls[1][1]?.body).toEqual( JSON.stringify({ - transaction_payload: bytesToHex(serializePayload(transaction.payload)), + transaction_payload: bytesToHex(serializePayloadBytes(transaction.payload)), estimated_len: transactionByteLength, }) ); @@ -1336,7 +1341,7 @@ test('Multi-sig transaction byte length must include the required signatures', a ]; const pubKeys = privKeys.map(privateKeyToPublic).map(createStacksPublicKey); - const pubKeyStrings = pubKeys.map(serializePublicKey).map(publicKeyToHex); + const pubKeyStrings = pubKeys.map(serializePublicKeyBytes).map(publicKeyToHex); // Create a unsigned multi-sig transaction const transaction = await makeUnsignedSTXTokenTransfer({ @@ -2098,7 +2103,7 @@ test('Call read-only function', async () => { }; const apiUrl = `${api.url}${READONLY_FUNCTION_CALL_PATH}/${contractAddress}/kv-store/get-value%3F`; // uri encoded - fetchMock.mockOnce(`{"okay": true, "result": "0x${bytesToHex(serializeCV(mockResult))}"}`); + fetchMock.mockOnce(`{"okay": true, "result": "0x${serializeCV(mockResult)}"}`); const result = await callReadOnlyFunction(options); @@ -2110,7 +2115,7 @@ test('Call read-only function', async () => { test('Get contract map entry - success', async () => { const mockValue = 60n; const mockResult = uintCV(mockValue); - fetchMock.mockOnce(`{"data": "0x${bytesToHex(serializeCV(mockResult))}"}`); + fetchMock.mockOnce(`{"data": "0x${serializeCV(mockResult)}"}`); const result = await getContractMapEntry({ contractAddress: 'SPSCWDV3RKV5ZRN1FQD84YE1NQFEDJ9R1F4DYQ11', @@ -2129,7 +2134,7 @@ test('Get contract map entry - success', async () => { test('Get contract map entry - no match', async () => { const mockResult = noneCV(); - fetchMock.mockOnce(`{"data": "0x${bytesToHex(serializeCV(mockResult))}"}`); + fetchMock.mockOnce(`{"data": "0x${serializeCVBytes(mockResult)}"}`); const result = await getContractMapEntry({ contractAddress: 'SPSCWDV3RKV5ZRN1FQD84YE1NQFEDJ9R1F4DYQ11', @@ -2160,11 +2165,11 @@ test('Post-conditions with amount larger than 8 bytes throw an error', () => { ); expect(() => { - serializePostCondition(stxPc); + serializePostConditionBytes(stxPc); }).toThrowError('The post-condition amount may not be larger than 8 bytes'); expect(() => { - serializePostCondition(fungiblePc); + serializePostConditionBytes(fungiblePc); }).toThrowError('The post-condition amount may not be larger than 8 bytes'); }); diff --git a/packages/transactions/tests/clarity.test.ts b/packages/transactions/tests/clarity.test.ts index 96321b45a..d4a89b3f2 100644 --- a/packages/transactions/tests/clarity.test.ts +++ b/packages/transactions/tests/clarity.test.ts @@ -1,48 +1,43 @@ -import { - asciiToBytes, - bytesToHex, - bytesToUtf8, - concatBytes, - hexToBytes, - utf8ToBytes, -} from '@stacks/common'; +import { asciiToBytes, bytesToUtf8, concatBytes, hexToBytes, utf8ToBytes } from '@stacks/common'; +import assert from 'assert'; +import { Cl } from '../src'; import { BytesReader } from '../src/bytesReader'; import { - bufferCV, BufferCV, - clarityByteToType, ClarityType, - clarityTypeToByte, ClarityValue, ClarityWireType, + IntCV, + ListCV, + SomeCV, + StandardPrincipalCV, + StringAsciiCV, + StringUtf8CV, + TupleCV, + UIntCV, + bufferCV, + clarityByteToType, + clarityTypeToByte, contractPrincipalCV, contractPrincipalCVFromStandard, deserializeCV, falseCV, - IntCV, intCV, listCV, - ListCV, noneCV, responseErrorCV, responseOkCV, serializeCV, + serializeCVBytes, someCV, - SomeCV, standardPrincipalCV, - StandardPrincipalCV, standardPrincipalCVFromAddress, stringAsciiCV, - StringAsciiCV, stringUtf8CV, - StringUtf8CV, trueCV, tupleCV, - TupleCV, uintCV, - UIntCV, } from '../src/clarity'; -import { Cl } from '../src'; import { cvToJSON, cvToString, @@ -51,13 +46,12 @@ import { isClarityType, } from '../src/clarity/clarityValue'; import { addressToString } from '../src/common'; -import { deserializeAddress } from '../src/types'; -import assert from 'assert'; +import { deserializeAddressBytes } from '../src/types'; const ADDRESS = 'SP2JXKMSH007NPYAQHKJPQMAQYAD90NQGTVJVQ02B'; function serializeDeserialize(value: T): T { - const serializedDeserialized: Uint8Array = serializeCV(value); + const serializedDeserialized: Uint8Array = serializeCVBytes(value); const bytesReader = new BytesReader(serializedDeserialized); return deserializeCV(bytesReader) as T; } @@ -271,7 +265,7 @@ describe('Clarity Types', () => { const numCV = intCV(num); expect(numCV.value.toString()).toBe(expectedInt); const serialized = serializeCV(numCV); - expect('0x' + bytesToHex(serialized.slice(1))).toBe(expectedHex); + expect('0x' + serialized.slice(2)).toBe(expectedHex); expect(cvToString(deserializeCV(serialized))).toBe(expectedInt); }); @@ -280,7 +274,7 @@ describe('Clarity Types', () => { const maxSigned128 = intCV(2n ** 127n - 1n); expect(maxSigned128.value.toString()).toBe('170141183460469231731687303715884105727'); const serializedMax = serializeCV(maxSigned128); - expect('0x' + bytesToHex(serializedMax.slice(1))).toBe('0x7fffffffffffffffffffffffffffffff'); + expect('0x' + serializedMax.slice(2)).toBe('0x7fffffffffffffffffffffffffffffff'); const serializedDeserializedMax = serializeDeserialize(maxSigned128) as IntCV; expect(cvToString(serializedDeserializedMax)).toBe(cvToString(maxSigned128)); @@ -288,7 +282,7 @@ describe('Clarity Types', () => { const minSigned128 = intCV((-2n) ** 127n); expect(minSigned128.value.toString()).toBe('-170141183460469231731687303715884105728'); const serializedMin = serializeCV(minSigned128); - expect('0x' + bytesToHex(serializedMin.slice(1))).toBe('0x80000000000000000000000000000000'); + expect('0x' + serializedMin.slice(2)).toBe('0x80000000000000000000000000000000'); const serializedDeserializedMin = serializeDeserialize(minSigned128) as IntCV; expect(cvToString(serializedDeserializedMin)).toBe(cvToString(minSigned128)); @@ -304,13 +298,13 @@ describe('Clarity Types', () => { const uint = uintCV(10); expect(uint.value.toString()).toBe('10'); const serialized1 = serializeCV(uint); - expect('0x' + bytesToHex(serialized1.slice(1))).toBe('0x0000000000000000000000000000000a'); + expect('0x' + serialized1.slice(2)).toBe('0x0000000000000000000000000000000a'); const serializedDeserialized = serializeDeserialize(uint) as UIntCV; expect(cvToString(serializedDeserialized)).toBe(cvToString(uint)); const uint2 = uintCV('0x0a'); const serialized2 = serializeCV(uint2); - expect('0x' + bytesToHex(serialized2.slice(1))).toBe('0x0000000000000000000000000000000a'); + expect('0x' + serialized2.slice(2)).toBe('0x0000000000000000000000000000000a'); expect(cvToString(serializeDeserialize(uint2))).toBe(cvToString(uint)); expect(() => uintCV(10000000000000000000000000000000)).toThrowError(RangeError); @@ -321,7 +315,7 @@ describe('Clarity Types', () => { const max128 = uintCV(2n ** 128n - 1n); expect(max128.value.toString()).toBe('340282366920938463463374607431768211455'); const serializedMax = serializeCV(max128); - expect('0x' + bytesToHex(serializedMax.slice(1))).toBe('0xffffffffffffffffffffffffffffffff'); + expect('0x' + serializedMax.slice(2)).toBe('0xffffffffffffffffffffffffffffffff'); const serializedDeserializedMax = serializeDeserialize(max128); expect(cvToString(serializedDeserializedMax)).toBe(cvToString(max128)); @@ -329,7 +323,7 @@ describe('Clarity Types', () => { const min128 = uintCV(0); expect(min128.value.toString()).toBe('0'); const serializedMin = serializeCV(min128); - expect('0x' + bytesToHex(serializedMin.slice(1))).toBe('0x00000000000000000000000000000000'); + expect('0x' + serializedMin.slice(2)).toBe('0x00000000000000000000000000000000'); const serializedDeserializedMin = serializeDeserialize(min128); expect(cvToString(serializedDeserializedMin)).toBe(cvToString(min128)); @@ -354,7 +348,7 @@ describe('Clarity Types', () => { const numCV = uintCV(num); expect(numCV.value.toString()).toBe(expectedInt); const serialized = serializeCV(numCV); - expect('0x' + bytesToHex(serialized.slice(1))).toBe(expectedHex); + expect('0x' + serialized.slice(2)).toBe(expectedHex); expect(cvToString(deserializeCV(serialized))).toBe('u' + expectedInt); }); @@ -458,37 +452,37 @@ describe('Clarity Types', () => { describe('Serialization Test Vectors', () => { test('Int 1 Vector', () => { const int = intCV(1); - const serialized = bytesToHex(serializeCV(int)); + const serialized = serializeCV(int); expect(serialized).toEqual('0000000000000000000000000000000001'); }); test('Int -1 Vector', () => { const int = intCV(-1); - const serialized = bytesToHex(serializeCV(int)); + const serialized = serializeCV(int); expect(serialized).toEqual('00ffffffffffffffffffffffffffffffff'); }); test('UInt 1 Vector', () => { const uint = uintCV(1); - const serialized = bytesToHex(serializeCV(uint)); + const serialized = serializeCV(uint); expect(serialized).toEqual('0100000000000000000000000000000001'); }); test('Buffer Vector', () => { const buffer = bufferCV(new Uint8Array([0xde, 0xad, 0xbe, 0xef])); - const serialized = bytesToHex(serializeCV(buffer)); + const serialized = serializeCV(buffer); expect(serialized).toEqual('0200000004deadbeef'); }); test('True Vector', () => { const t = trueCV(); - const serialized = bytesToHex(serializeCV(t)); + const serialized = serializeCV(t); expect(serialized).toEqual('03'); }); test('False Vector', () => { const f = falseCV(); - const serialized = bytesToHex(serializeCV(f)); + const serialized = serializeCV(f); expect(serialized).toEqual('04'); }); @@ -498,8 +492,10 @@ describe('Clarity Types', () => { 0x11, 0xab, 0xab, 0xff, 0xff, ]); const bytesReader = new BytesReader(concatBytes(new Uint8Array([0x00]), addressBuffer)); - const standardPrincipal = standardPrincipalCVFromAddress(deserializeAddress(bytesReader)); - const serialized = bytesToHex(serializeCV(standardPrincipal)); + const standardPrincipal = standardPrincipalCVFromAddress( + deserializeAddressBytes(bytesReader) + ); + const serialized = serializeCV(standardPrincipal); expect(serialized).toEqual('050011deadbeef11ababffff11deadbeef11ababffff'); }); @@ -510,39 +506,41 @@ describe('Clarity Types', () => { ]); const contractName = 'abcd'; const bytesReader = new BytesReader(concatBytes(new Uint8Array([0x00]), addressBuffer)); - const standardPrincipal = standardPrincipalCVFromAddress(deserializeAddress(bytesReader)); + const standardPrincipal = standardPrincipalCVFromAddress( + deserializeAddressBytes(bytesReader) + ); const contractPrincipal = contractPrincipalCVFromStandard(standardPrincipal, contractName); - const serialized = bytesToHex(serializeCV(contractPrincipal)); + const serialized = serializeCV(contractPrincipal); expect(serialized).toEqual('060011deadbeef11ababffff11deadbeef11ababffff0461626364'); }); test('Response Ok Vector', () => { const ok = responseOkCV(intCV(-1)); - const serialized = bytesToHex(serializeCV(ok)); + const serialized = serializeCV(ok); expect(serialized).toEqual('0700ffffffffffffffffffffffffffffffff'); }); test('Response Err Vector', () => { const err = responseErrorCV(intCV(-1)); - const serialized = bytesToHex(serializeCV(err)); + const serialized = serializeCV(err); expect(serialized).toEqual('0800ffffffffffffffffffffffffffffffff'); }); test('None Vector', () => { const none = noneCV(); - const serialized = bytesToHex(serializeCV(none)); + const serialized = serializeCV(none); expect(serialized).toEqual('09'); }); test('Some Vector', () => { const some = someCV(intCV(-1)); - const serialized = bytesToHex(serializeCV(some)); + const serialized = serializeCV(some); expect(serialized).toEqual('0a00ffffffffffffffffffffffffffffffff'); }); test('List Vector', () => { const list = listCV([intCV(1), intCV(2), intCV(3), intCV(-4)]); - const serialized = bytesToHex(serializeCV(list)); + const serialized = serializeCV(list); expect(serialized).toEqual( '0b0000000400000000000000000000000000000000010000000000000000000000000000000002000000000000000000000000000000000300fffffffffffffffffffffffffffffffc' ); @@ -553,13 +551,13 @@ describe('Clarity Types', () => { baz: noneCV(), foobar: trueCV(), }); - const serialized = bytesToHex(serializeCV(tuple)); + const serialized = serializeCV(tuple); expect(serialized).toEqual('0c000000020362617a0906666f6f62617203'); }); test('Ascii String Vector', () => { const str = stringAsciiCV('hello world'); - const serialized = bytesToHex(serializeCV(str)); + const serialized = serializeCV(str); expect(serialized).toEqual('0d0000000b68656c6c6f20776f726c64'); }); @@ -572,7 +570,7 @@ describe('Clarity Types', () => { stringAsciiCV('\r'), stringAsciiCV('\0'), ]; - const serialized = strings.map(serializeCV); + const serialized = strings.map(serializeCVBytes); serialized.forEach(ser => { const reader = new BytesReader(ser); const serializedStringLenByte = reader.readBytes(5)[4]; @@ -583,7 +581,7 @@ describe('Clarity Types', () => { test('Utf8 String Vector', () => { const str = stringUtf8CV('hello world'); - const serialized = bytesToHex(serializeCV(str)); + const serialized = serializeCV(str); expect(serialized).toEqual('0e0000000b68656c6c6f20776f726c64'); }); }); diff --git a/packages/transactions/tests/macros.ts b/packages/transactions/tests/macros.ts index 247d70c9a..e99c48659 100644 --- a/packages/transactions/tests/macros.ts +++ b/packages/transactions/tests/macros.ts @@ -1,4 +1,4 @@ -import { StacksMessage, serializeStacksMessage, deserializeStacksMessage } from '../src/types'; +import { StacksMessage, serializeStacksMessageBytes, deserializeStacksMessage } from '../src/types'; import { BytesReader } from '../src/bytesReader'; import { StacksMessageType } from '../src/constants'; @@ -6,7 +6,7 @@ export function serializeDeserialize { STANDARD_ADDRESS, FungibleConditionCode.Equal, 100, - createAssetInfo(FT_CONTRACT_ADDRESS, FT_CONTRACT_NAME, FT_ASSET_NAME) + createAsset(FT_CONTRACT_ADDRESS, FT_CONTRACT_NAME, FT_ASSET_NAME) ); expect(pc).toEqual(postCondition); }); @@ -165,7 +165,7 @@ describe('pc -- post condition builder', () => { STANDARD_ADDRESS, FungibleConditionCode.Less, 100, - createAssetInfo(FT_CONTRACT_ADDRESS, FT_CONTRACT_NAME, FT_ASSET_NAME) + createAsset(FT_CONTRACT_ADDRESS, FT_CONTRACT_NAME, FT_ASSET_NAME) ); expect(pc).toEqual(postCondition); }); @@ -178,7 +178,7 @@ describe('pc -- post condition builder', () => { STANDARD_ADDRESS, FungibleConditionCode.Less, 100, - createAssetInfo(FT_CONTRACT_ADDRESS, FT_CONTRACT_NAME, FT_ASSET_NAME) + createAsset(FT_CONTRACT_ADDRESS, FT_CONTRACT_NAME, FT_ASSET_NAME) ); expect(pc).toEqual(postCondition); }); @@ -191,7 +191,7 @@ describe('pc -- post condition builder', () => { STANDARD_ADDRESS, FungibleConditionCode.GreaterEqual, 100, - createAssetInfo(FT_CONTRACT_ADDRESS, FT_CONTRACT_NAME, FT_ASSET_NAME) + createAsset(FT_CONTRACT_ADDRESS, FT_CONTRACT_NAME, FT_ASSET_NAME) ); expect(pc).toEqual(postCondition); }); @@ -204,7 +204,7 @@ describe('pc -- post condition builder', () => { STANDARD_ADDRESS, FungibleConditionCode.Greater, 100, - createAssetInfo(FT_CONTRACT_ADDRESS, FT_CONTRACT_NAME, FT_ASSET_NAME) + createAsset(FT_CONTRACT_ADDRESS, FT_CONTRACT_NAME, FT_ASSET_NAME) ); expect(pc).toEqual(postCondition); }); @@ -218,7 +218,7 @@ describe('pc -- post condition builder', () => { const postCondition = makeStandardNonFungiblePostCondition( STANDARD_ADDRESS, NonFungibleConditionCode.Sends, - createAssetInfo(NFT_CONTRACT_ADDRESS, NFT_CONTRACT_NAME, NFT_TOKEN_NAME), + createAsset(NFT_CONTRACT_ADDRESS, NFT_CONTRACT_NAME, NFT_TOKEN_NAME), NFT_ASSET_ID ); expect(pc).toEqual(postCondition); @@ -231,7 +231,7 @@ describe('pc -- post condition builder', () => { const postCondition = makeStandardNonFungiblePostCondition( STANDARD_ADDRESS, NonFungibleConditionCode.DoesNotSend, - createAssetInfo(NFT_CONTRACT_ADDRESS, NFT_CONTRACT_NAME, NFT_TOKEN_NAME), + createAsset(NFT_CONTRACT_ADDRESS, NFT_CONTRACT_NAME, NFT_TOKEN_NAME), NFT_ASSET_ID ); expect(pc).toEqual(postCondition); @@ -307,7 +307,7 @@ describe('pc -- post condition builder', () => { CONTRACT_NAME, FungibleConditionCode.Equal, 100, - createAssetInfo(FT_CONTRACT_ADDRESS, FT_CONTRACT_NAME, FT_ASSET_NAME) + createAsset(FT_CONTRACT_ADDRESS, FT_CONTRACT_NAME, FT_ASSET_NAME) ); expect(pc).toEqual(postCondition); }); @@ -321,7 +321,7 @@ describe('pc -- post condition builder', () => { CONTRACT_NAME, FungibleConditionCode.Less, 100, - createAssetInfo(FT_CONTRACT_ADDRESS, FT_CONTRACT_NAME, FT_ASSET_NAME) + createAsset(FT_CONTRACT_ADDRESS, FT_CONTRACT_NAME, FT_ASSET_NAME) ); expect(pc).toEqual(postCondition); }); @@ -335,7 +335,7 @@ describe('pc -- post condition builder', () => { CONTRACT_NAME, FungibleConditionCode.Less, 100, - createAssetInfo(FT_CONTRACT_ADDRESS, FT_CONTRACT_NAME, FT_ASSET_NAME) + createAsset(FT_CONTRACT_ADDRESS, FT_CONTRACT_NAME, FT_ASSET_NAME) ); expect(pc).toEqual(postCondition); }); @@ -349,7 +349,7 @@ describe('pc -- post condition builder', () => { CONTRACT_NAME, FungibleConditionCode.GreaterEqual, 100, - createAssetInfo(FT_CONTRACT_ADDRESS, FT_CONTRACT_NAME, FT_ASSET_NAME) + createAsset(FT_CONTRACT_ADDRESS, FT_CONTRACT_NAME, FT_ASSET_NAME) ); expect(pc).toEqual(postCondition); }); @@ -363,7 +363,7 @@ describe('pc -- post condition builder', () => { CONTRACT_NAME, FungibleConditionCode.Greater, 100, - createAssetInfo(FT_CONTRACT_ADDRESS, FT_CONTRACT_NAME, FT_ASSET_NAME) + createAsset(FT_CONTRACT_ADDRESS, FT_CONTRACT_NAME, FT_ASSET_NAME) ); expect(pc).toEqual(postCondition); }); @@ -378,7 +378,7 @@ describe('pc -- post condition builder', () => { CONTRACT_ADDRESS, CONTRACT_NAME, NonFungibleConditionCode.Sends, - createAssetInfo(NFT_CONTRACT_ADDRESS, NFT_CONTRACT_NAME, NFT_TOKEN_NAME), + createAsset(NFT_CONTRACT_ADDRESS, NFT_CONTRACT_NAME, NFT_TOKEN_NAME), NFT_ASSET_ID ); expect(pc).toEqual(postCondition); @@ -392,7 +392,7 @@ describe('pc -- post condition builder', () => { CONTRACT_ADDRESS, CONTRACT_NAME, NonFungibleConditionCode.DoesNotSend, - createAssetInfo(NFT_CONTRACT_ADDRESS, NFT_CONTRACT_NAME, NFT_TOKEN_NAME), + createAsset(NFT_CONTRACT_ADDRESS, NFT_CONTRACT_NAME, NFT_TOKEN_NAME), NFT_ASSET_ID ); expect(pc).toEqual(postCondition); diff --git a/packages/transactions/tests/postcondition.test.ts b/packages/transactions/tests/postcondition.test.ts index 45354c077..562933bac 100644 --- a/packages/transactions/tests/postcondition.test.ts +++ b/packages/transactions/tests/postcondition.test.ts @@ -8,7 +8,7 @@ import { STXPostCondition, FungiblePostCondition, NonFungiblePostCondition, - createAssetInfo, + createAsset, createStandardPrincipal, createContractPrincipal, ContractPrincipal, @@ -62,7 +62,7 @@ test('Fungible post condition serialization and deserialization', () => { const assetAddress = 'SP2ZP4GJDZJ1FDHTQ963F0292PE9J9752TZJ68F21'; const assetContractName = 'contract_name'; const assetName = 'asset_name'; - const info = createAssetInfo(assetAddress, assetContractName, assetName); + const info = createAsset(assetAddress, assetContractName, assetName); const postCondition = createFungiblePostCondition(principal, conditionCode, amount, info); @@ -75,9 +75,9 @@ test('Fungible post condition serialization and deserialization', () => { expect(addressToString(deserialized.principal.address)).toBe(address); expect(deserialized.conditionCode).toBe(conditionCode); expect(deserialized.amount.toString()).toBe(amount.toString()); - expect(addressToString(deserialized.assetInfo.address)).toBe(assetAddress); - expect(deserialized.assetInfo.contractName.content).toBe(assetContractName); - expect(deserialized.assetInfo.assetName.content).toBe(assetName); + expect(addressToString(deserialized.asset.address)).toBe(assetAddress); + expect(deserialized.asset.contractName.content).toBe(assetContractName); + expect(deserialized.asset.assetName.content).toBe(assetName); }); test('Non-fungible post condition serialization and deserialization', () => { @@ -92,7 +92,7 @@ test('Non-fungible post condition serialization and deserialization', () => { const assetAddress = 'SP2ZP4GJDZJ1FDHTQ963F0292PE9J9752TZJ68F21'; const assetContractName = 'contract_name'; const assetName = 'asset_name'; - const info = createAssetInfo(assetAddress, assetContractName, assetName); + const info = createAsset(assetAddress, assetContractName, assetName); const nftAssetName = 'nft_asset_name'; @@ -112,9 +112,9 @@ test('Non-fungible post condition serialization and deserialization', () => { expect(addressToString(deserialized.principal.address)).toBe(address); expect((deserialized.principal as ContractPrincipal).contractName.content).toBe(contractName); expect(deserialized.conditionCode).toBe(conditionCode); - expect(addressToString(deserialized.assetInfo.address)).toBe(assetAddress); - expect(deserialized.assetInfo.contractName.content).toBe(assetContractName); - expect(deserialized.assetInfo.assetName.content).toBe(assetName); + expect(addressToString(deserialized.asset.address)).toBe(assetAddress); + expect(deserialized.asset.contractName.content).toBe(assetContractName); + expect(deserialized.asset.assetName.content).toBe(assetName); expect(bytesToUtf8((deserialized.assetName as BufferCV).buffer)).toEqual(nftAssetName); }); @@ -148,8 +148,8 @@ test('Non-fungible post condition with string IDs serialization and deserializat expect(addressToString(deserialized.principal.address)).toBe(address); expect((deserialized.principal as ContractPrincipal).contractName.content).toBe(contractName); expect(deserialized.conditionCode).toBe(conditionCode); - expect(addressToString(deserialized.assetInfo.address)).toBe(assetAddress); - expect(deserialized.assetInfo.contractName.content).toBe(assetContractName); - expect(deserialized.assetInfo.assetName.content).toBe(assetName); + expect(addressToString(deserialized.asset.address)).toBe(assetAddress); + expect(deserialized.asset.contractName.content).toBe(assetContractName); + expect(deserialized.asset.assetName.content).toBe(assetName); expect(bytesToUtf8((deserialized.assetName as BufferCV).buffer)).toEqual(nftAssetName); }); diff --git a/packages/transactions/tests/transaction.test.ts b/packages/transactions/tests/transaction.test.ts index 19eb5d65d..e06cd88c5 100644 --- a/packages/transactions/tests/transaction.test.ts +++ b/packages/transactions/tests/transaction.test.ts @@ -23,7 +23,7 @@ import { createStacksPublicKey, privateKeyToPublic, publicKeyToHex, - serializePublicKey, + serializePublicKeyBytes, } from '../src/keys'; import { CoinbasePayloadToAltRecipient, @@ -204,7 +204,7 @@ test('STX token transfer transaction multi-sig serialization and deserialization ]; const pubKeys = privKeys.map(privateKeyToPublic).map(createStacksPublicKey); - const pubKeyStrings = pubKeys.map(serializePublicKey).map(publicKeyToHex); + const pubKeyStrings = pubKeys.map(serializePublicKeyBytes).map(publicKeyToHex); const spendingCondition = createMultiSigSpendingCondition( addressHashMode, @@ -273,7 +273,7 @@ test('STX token transfer transaction multi-sig uncompressed keys serialization a ]; const pubKeys = privKeys.map(privateKeyToPublic).map(createStacksPublicKey); - const pubKeyStrings = pubKeys.map(serializePublicKey).map(publicKeyToHex); + const pubKeyStrings = pubKeys.map(serializePublicKeyBytes).map(publicKeyToHex); const spendingCondition = createMultiSigSpendingCondition( addressHashMode, diff --git a/packages/transactions/tests/types.test.ts b/packages/transactions/tests/types.test.ts index 0d2246f10..ce9eeae58 100644 --- a/packages/transactions/tests/types.test.ts +++ b/packages/transactions/tests/types.test.ts @@ -1,17 +1,17 @@ import { createLPList, - serializeStacksMessage, - deserializeLPList, + serializeStacksMessageBytes, + deserializeLPListBytes, addressFromHashMode, LengthPrefixedList, addressFromPublicKeys, } from '../src/types'; import { LengthPrefixedString, - AssetInfo, + Asset, createLPString, createAddress, - createAssetInfo, + createAsset, } from '../src/postcondition-types'; import { Address, addressToString } from '../src/common'; import { AddressHashMode, StacksMessageType } from '../src/constants'; @@ -48,10 +48,10 @@ test('Length prefixed list serialization and deserialization', () => { l.push(addressList[index]); } const lpList: LengthPrefixedList = createLPList(l); - const serialized = serializeStacksMessage(lpList); + const serialized = serializeStacksMessageBytes(lpList); const bytesReader = new BytesReader(serialized); - const deserialized = deserializeLPList(bytesReader, StacksMessageType.Address); + const deserialized = deserializeLPListBytes(bytesReader, StacksMessageType.Address); expect(deserialized.values.length).toBe(addressList.length); @@ -143,8 +143,8 @@ test('Asset info serialization and deserialization', () => { const assetAddress = 'SP2ZP4GJDZJ1FDHTQ963F0292PE9J9752TZJ68F21'; const assetContractName = 'contract_name'; const assetName = 'asset_name'; - const info = createAssetInfo(assetAddress, assetContractName, assetName); - const deserialized = serializeDeserialize(info, StacksMessageType.AssetInfo) as AssetInfo; + const info = createAsset(assetAddress, assetContractName, assetName); + const deserialized = serializeDeserialize(info, StacksMessageType.Asset) as Asset; expect(addressToString(deserialized.address)).toBe(assetAddress); expect(deserialized.contractName.content).toBe(assetContractName); expect(deserialized.assetName.content).toBe(assetName);