From 54d38d68010b314069486d070daec85dc32fee89 Mon Sep 17 00:00:00 2001 From: GuilaneDen <83951892+GuilaneDen@users.noreply.github.com> Date: Fri, 6 Dec 2024 18:52:23 +0100 Subject: [PATCH] feat: updated registerData, transferToPublic, signTransferWithMemo, signConfigureDelegation --- src/Concordium.ts | 242 +++++++++++++++++++++++++++++++++---------- src/serialization.ts | 147 +++++++++++++++++++++++--- src/utils.ts | 35 ++++++- 3 files changed, 352 insertions(+), 72 deletions(-) diff --git a/src/Concordium.ts b/src/Concordium.ts index 904ce56..3acdbed 100644 --- a/src/Concordium.ts +++ b/src/Concordium.ts @@ -7,7 +7,11 @@ import { serializeSimpleTransferWithMemo, serializeTransferWithSchedule, serializeConfigureBaker, - serializeTransferWithScheduleAndMemo + serializeTransferWithScheduleAndMemo, + serializeRegisterData, + serializeTransferToPublic, + serializeDeployModule, + serializeInitContract } from "./serialization"; import BigNumber from "bignumber.js"; import { encodeInt32 } from "./utils"; @@ -28,25 +32,34 @@ const P2_SIGNED_KEY = 0x01; // FOR SIGN TRANSACTION const P1_FIRST_CHUNK = 0x00; +const P1_INITIAL_WITH_MEMO = 0x01; +const P1_REMAINING_AMOUNT = 0x01; +const P1_DATA = 0x01; +const P1_PROOF = 0x02; +const P1_MEMO = 0x02; +const P1_AMOUNT = 0x03; const P2_MORE = 0x80; const P2_LAST = 0x00; +const P1_INITIAL_PACKET = 0x00; +const P1_SCHEDULED_TRANSFER_PAIRS = 0x01; const INS = { + // GET_VERSION: 0x03, VERIFY_ADDRESS: 0x00, GET_PUBLIC_KEY: 0x01, - GET_VERSION: 0x03, - GET_APP_NAME: 0x04, - SIGN_TRANSFER: 0x06, - SIGN_TRANSFER_MEMO: 0x06, - SIGN_TRANSFER_SCHEDULE: 0x06, - SIGN_TRANSFER_SCHEDULE_AND_MEMO: 0x06, - SIGN_CONFIGURE_DELEGATION: 0x06, - SIGN_CONFIGURE_BAKER: 0x06, + SIGN_TRANSFER: 0x02, + SIGN_TRANSFER_SCHEDULE: 0x03, + SIGN_TRANSFER_TO_PUBLIC: 0x12, + SIGN_CONFIGURE_DELEGATION: 0x17, + SIGN_CONFIGURE_BAKER: 0x18, + GET_APP_NAME: 0x21, + SIGN_TRANSFER_MEMO: 0x32, + SIGN_TRANSFER_SCHEDULE_AND_MEMO: 0x34, + SIGN_REGISTER_DATA: 0x35, + SIGN_DEPLOY_MODULE: 0x06, + SIGN_INIT_CONTRACT: 0x06, }; -const concordium_path = "44'/919'/0'/0/0/0"; -const concordium_legacy_path = "1105'/0'/0'/0/"; - /** * Concordium API * @@ -77,25 +90,25 @@ export default class Concordium { ); } - /** - * Get application version. - * - * @returns version object - * - * @example - * concordium.getVersion().then(r => r.version) - */ - async getVersion(): Promise<{ version: string }> { - const [major, minor, patch] = await this.sendToDevice( - INS.GET_VERSION, - NONE, - NONE, - Buffer.from([]) - ); - return { - version: `${major}.${minor}.${patch}`, - }; - } + // /** + // * Get application version. + // * + // * @returns version object + // * + // * @example + // * concordium.getVersion().then(r => r.version) + // */ + // async getVersion(): Promise<{ version: string }> { + // const [major, minor, patch] = await this.sendToDevice( + // INS.GET_VERSION, + // NONE, + // NONE, + // Buffer.from([]) + // ); + // return { + // version: `${major}.${minor}.${patch}`, + // }; + // } /** * Legacy Verify address. @@ -162,7 +175,7 @@ export default class Concordium { const publicKeyBuffer = await this.sendToDevice( INS.GET_PUBLIC_KEY, - display ? P1_CONFIRM : P1_NON_CONFIRM, + display ? P1_NON_CONFIRM : P1_CONFIRM, signedKey ? P2_SIGNED_KEY : NONE, pathBuffer ); @@ -216,17 +229,80 @@ export default class Concordium { }; } - async signTransferWithMemo(txn, path: string): Promise<{ signature: string[]; transaction }> { + async signTransferWithMemo(txn, path: string): Promise<{ signature: string[] }> { + + + const { payloadHeaderAddressMemoLength, payloadsMemo, payloadsAmount } = serializeSimpleTransferWithMemo(txn, path); + + let response; + response = await this.sendToDevice( + INS.SIGN_TRANSFER_MEMO, + P1_INITIAL_WITH_MEMO, + NONE, + payloadHeaderAddressMemoLength[0] + ); + response = await this.sendToDevice( + INS.SIGN_TRANSFER_MEMO, + P1_MEMO, + NONE, + payloadsMemo[0] + ); + response = await this.sendToDevice( + INS.SIGN_TRANSFER_MEMO, + P1_AMOUNT, + NONE, + payloadsAmount[0] + ); + + if (response.length === 1) throw new Error("User has declined."); + + return { + signature: response.toString("hex"), + }; + } + + async signTransferWithSchedule(txn, path: string): Promise<{ signature: string[] }> { - const { payloads } = serializeSimpleTransferWithMemo(txn, path); + const { payloadHeaderAddressScheduleLength, payloadsSchedule } = serializeTransferWithSchedule(txn, path); + + let response; + + response = await this.sendToDevice( + INS.SIGN_TRANSFER_SCHEDULE, + P1_INITIAL_PACKET, + NONE, + payloadHeaderAddressScheduleLength[0] + ); + + for (let i = 0; i < payloadsSchedule.length; i++) { + const lastChunk = i === payloadsSchedule.length - 1; + response = await this.sendToDevice( + INS.SIGN_TRANSFER_SCHEDULE, + P1_SCHEDULED_TRANSFER_PAIRS, + NONE, + payloadsSchedule[i] + ); + } + + if (response.length === 1) throw new Error("User has declined."); + + return { + signature: response.toString("hex"), + }; + } + + async signTransferWithScheduleAndMemo(txn, path: string): Promise<{ signature: string[]; transaction }> { + + + const { payloads } = serializeTransferWithScheduleAndMemo(txn, path); let response; for (let i = 0; i < payloads.length; i++) { const lastChunk = i === payloads.length - 1; response = await this.sendToDevice( - INS.SIGN_TRANSFER_MEMO, + INS.SIGN_TRANSFER_SCHEDULE_AND_MEMO, P1_FIRST_CHUNK + i, lastChunk ? P2_LAST : P2_MORE, payloads[i] @@ -234,6 +310,7 @@ export default class Concordium { } if (response.length === 1) throw new Error("User has declined."); + const transaction = payloads.slice(1); return { @@ -242,17 +319,17 @@ export default class Concordium { }; } - async signTransferWithSchedule(txn, path: string): Promise<{ signature: string[]; transaction }> { + async signConfigureDelegation(txn, path: string): Promise<{ signature: string[] }> { - const { payloads } = serializeTransferWithSchedule(txn, path); + const { payloads } = serializeConfigureDelegation(txn, path); let response; for (let i = 0; i < payloads.length; i++) { const lastChunk = i === payloads.length - 1; response = await this.sendToDevice( - INS.SIGN_TRANSFER_SCHEDULE, + INS.SIGN_CONFIGURE_DELEGATION, P1_FIRST_CHUNK + i, lastChunk ? P2_LAST : P2_MORE, payloads[i] @@ -261,25 +338,21 @@ export default class Concordium { if (response.length === 1) throw new Error("User has declined."); - const transaction = payloads.slice(1); - return { signature: response.toString("hex"), - transaction: Buffer.concat(transaction).toString("hex"), }; } - async signTransferWithScheduleAndMemo(txn, path: string): Promise<{ signature: string[]; transaction }> { - + async signConfigureBaker(txn, path: string): Promise<{ signature: string[]; transaction }> { - const { payloads } = serializeTransferWithScheduleAndMemo(txn, path); + const { payloads } = serializeConfigureBaker(txn, path); let response; for (let i = 0; i < payloads.length; i++) { const lastChunk = i === payloads.length - 1; response = await this.sendToDevice( - INS.SIGN_TRANSFER_SCHEDULE_AND_MEMO, + INS.SIGN_CONFIGURE_BAKER, P1_FIRST_CHUNK + i, lastChunk ? P2_LAST : P2_MORE, payloads[i] @@ -290,23 +363,87 @@ export default class Concordium { const transaction = payloads.slice(1); + return { signature: response.toString("hex"), transaction: Buffer.concat(transaction).toString("hex"), }; } - async signConfigureDelegation(txn, path: string): Promise<{ signature: string[]; transaction }> { + async signRegisterData(txn, path: string): Promise<{ signature: string[] }> { + const { payloadHeader, payloadsData } = serializeRegisterData(txn, path); - const { payloads } = serializeConfigureDelegation(txn, path); + let response; + response = await this.sendToDevice( + INS.SIGN_REGISTER_DATA, + P1_INITIAL_PACKET, + NONE, + payloadHeader[0] + ); + + for (let i = 0; i < payloadsData.length; i++) { + response = await this.sendToDevice( + INS.SIGN_REGISTER_DATA, + P1_DATA, + NONE, + payloadsData[i] + ); + } + + if (response.length === 1) throw new Error("User has declined."); + + return { + signature: response.toString("hex"), + }; + } + + async signTransferToPublic(txn, path: string): Promise<{ signature: string[] }> { + + const { payloadHeader, payloadsAmountAndProofsLength, payloadsProofs } = serializeTransferToPublic(txn, path); + + let response; + + response = await this.sendToDevice( + INS.SIGN_TRANSFER_TO_PUBLIC, + P1_INITIAL_PACKET, + NONE, + payloadHeader[0] + ); + + response = await this.sendToDevice( + INS.SIGN_TRANSFER_TO_PUBLIC, + P1_REMAINING_AMOUNT, + NONE, + payloadsAmountAndProofsLength[0] + ); + + for (let i = 0; i < payloadsProofs.length; i++) { + response = await this.sendToDevice( + INS.SIGN_TRANSFER_TO_PUBLIC, + P1_PROOF, + NONE, + payloadsProofs[i] + ); + } + + if (response.length === 1) throw new Error("User has declined."); + + return { + signature: response.toString("hex"), + }; + } + + async signDeployModule(txn, path: string): Promise<{ signature: string[]; transaction }> { + + const { payloads } = serializeDeployModule(txn, path); let response; for (let i = 0; i < payloads.length; i++) { const lastChunk = i === payloads.length - 1; response = await this.sendToDevice( - INS.SIGN_CONFIGURE_DELEGATION, + INS.SIGN_DEPLOY_MODULE, P1_FIRST_CHUNK + i, lastChunk ? P2_LAST : P2_MORE, payloads[i] @@ -317,24 +454,22 @@ export default class Concordium { const transaction = payloads.slice(1); - return { signature: response.toString("hex"), transaction: Buffer.concat(transaction).toString("hex"), }; } - async signConfigureBaker(txn, path: string): Promise<{ signature: string[]; transaction }> { + async signInitContract(txn, path: string): Promise<{ signature: string[]; transaction }> { - - const { payloads } = serializeConfigureBaker(txn, path); + const { payloads } = serializeInitContract(txn, path); let response; for (let i = 0; i < payloads.length; i++) { const lastChunk = i === payloads.length - 1; response = await this.sendToDevice( - INS.SIGN_CONFIGURE_BAKER, + INS.SIGN_INIT_CONTRACT, P1_FIRST_CHUNK + i, lastChunk ? P2_LAST : P2_MORE, payloads[i] @@ -345,7 +480,6 @@ export default class Concordium { const transaction = payloads.slice(1); - return { signature: response.toString("hex"), transaction: Buffer.concat(transaction).toString("hex"), diff --git a/src/serialization.ts b/src/serialization.ts index d26b0ca..cca6bf3 100644 --- a/src/serialization.ts +++ b/src/serialization.ts @@ -1,8 +1,10 @@ import BIPPath from "bip32-path"; -import { serializeAccountTransaction } from "./utils"; +import { encodeDataBlob, encodeInt8, encodeWord16, encodeWord64, serializeAccountTransaction, serializeAccountTransactionHeader } from "./utils"; import { DataBlob } from "@concordium/common-sdk/lib/types/DataBlob"; import { Buffer as NodeBuffer } from 'buffer/index'; +import { AccountAddress } from "@concordium/web-sdk"; const MAX_CHUNK_SIZE = 255; +const MAX_SCHEDULE_CHUNK_SIZE = 15; const serializePath = (path: number[]): Buffer => { const buf = Buffer.alloc(1 + path.length * 4); @@ -35,18 +37,44 @@ export const pathToBuffer = (originalPath: string): Buffer => { return serializePath(pathNums); }; -const serializeTransactionPayloads = ( path: string, rawTx: Buffer): Buffer[] => { +const serializeTransactionPayloadsWithDerivationPath = (path: string, rawTx: Buffer): Buffer[] => { const paths = splitPath(path); let offset = 0; const payloads: Buffer[] = []; - let buffer = Buffer.alloc( - 1 + paths.length * 4 - ); - buffer[0] = paths.length; + let pathBuffer = Buffer.alloc(1 + paths.length * 4); + pathBuffer[0] = paths.length; paths.forEach((element, index) => { - buffer.writeUInt32BE(element, 1 + 4 * index); + pathBuffer.writeUInt32BE(element, 1 + 4 * index); }); - payloads.push(buffer); + + while (offset !== rawTx.length) { + const first = offset === 0; + let chunkSize = + offset + MAX_CHUNK_SIZE > rawTx.length + ? rawTx.length - offset + : MAX_CHUNK_SIZE; + + // Allocate buffer for the first chunk with pathBuffer size + const buffer = Buffer.alloc(first ? pathBuffer.length + chunkSize : chunkSize); + + if (first) { + // Copy pathBuffer to the beginning of the first chunk + pathBuffer.copy(buffer, 0); + rawTx.copy(buffer, pathBuffer.length, offset, offset + chunkSize); + } else { + rawTx.copy(buffer, 0, offset, offset + chunkSize); + } + + payloads.push(buffer); + offset += chunkSize; + } + return payloads; +}; + + +const serializeTransactionPayloads = (rawTx: Buffer): Buffer[] => { + let offset = 0; + const payloads: Buffer[] = []; while (offset !== rawTx.length) { const first = offset === 0; let chunkSize = @@ -66,9 +94,10 @@ const serializeTransactionPayloads = ( path: string, rawTx: Buffer): Buffer[] => return payloads; }; + export const serializeTransaction = (txn: any, path: string): { payloads: Buffer[] } => { const txSerialized = serializeAccountTransaction(txn); - const payloads = serializeTransactionPayloads(path, txSerialized); + const payloads = serializeTransactionPayloadsWithDerivationPath(path, txSerialized); return { payloads }; } @@ -76,14 +105,59 @@ export const serializeSimpleTransfer = (txn: any, path: string): { payloads: Buf return serializeTransaction(txn, path); }; -export const serializeSimpleTransferWithMemo = (txn: any, path: string): { payloads: Buffer[] } => { +export const serializeSimpleTransferWithMemo = (txn: any, path: string): { payloadHeaderAddressMemoLength: Buffer[], payloadsMemo: Buffer[], payloadsAmount: Buffer[] } => { // Convert the string to a buffer const memo: string = txn.payload.memo; const memoBuffer = NodeBuffer.from(memo, 'utf-8'); // Encode the buffer as a DataBlob txn.payload.memo = new DataBlob(memoBuffer); - return serializeTransaction(txn, path); + const serializedType = Buffer.from(Uint8Array.of(txn.transactionKind)); + const serializedToAddress = AccountAddress.toBuffer(txn.payload.toAddress); + const serializedAmount = encodeWord64(txn.payload.amount.microCcdAmount); + const serializedMemo = encodeDataBlob(txn.payload.memo); + const memoLength = serializedMemo.subarray(0, 2); + + const payloadSize = serializedType.length + serializedMemo.length + serializedAmount.length + serializedToAddress.length; + const serializedHeader = serializeAccountTransactionHeader(txn, payloadSize); + const serializedHeaderAddressMemoLength = Buffer.concat([serializedHeader, serializedType, serializedToAddress, memoLength]); + + const payloadHeaderAddressMemoLength = serializeTransactionPayloadsWithDerivationPath(path, serializedHeaderAddressMemoLength); + const payloadsMemo = serializeTransactionPayloads(serializedMemo.subarray(2)); + const payloadsAmount = serializeTransactionPayloads(serializedAmount); + + + + return { payloadHeaderAddressMemoLength, payloadsMemo, payloadsAmount }; +}; + +export const serializeTransferWithSchedule = (txn: any, path: string): { payloadHeaderAddressScheduleLength: Buffer[], payloadsSchedule: Buffer[] } => { + const serializedType = Buffer.from(Uint8Array.of(txn.transactionKind)); + const toAddressBuffer = AccountAddress.toBuffer(txn.payload.toAddress); + const scheduleLength = encodeInt8(txn.payload.schedule.length); + const scheduleBuffer = txn.payload.schedule.map((item: { timestamp: string, amount: string }) => { + const timestampBuffer = encodeWord64(item.timestamp); + const amountBuffer = encodeWord64(item.amount); + return Buffer.concat([timestampBuffer, amountBuffer]); + }); + const serializedSchedule = Buffer.concat([...scheduleBuffer]); + + + const payloadSize = serializedType.length + scheduleLength.length + serializedSchedule.length + toAddressBuffer.length; + const serializedHeader = serializeAccountTransactionHeader(txn, payloadSize); + const serializedHeaderAddressScheduleLength = Buffer.concat([serializedHeader, serializedType, toAddressBuffer, scheduleLength]); + + const payloadHeaderAddressScheduleLength = serializeTransactionPayloadsWithDerivationPath(path, serializedHeaderAddressScheduleLength); + const payloadsSchedule: Buffer[] = []; + + let remainingPairs = txn.payload.schedule.length + for (let i = 0; i < scheduleBuffer.length; i += MAX_SCHEDULE_CHUNK_SIZE) { + const offset = remainingPairs > MAX_SCHEDULE_CHUNK_SIZE ? MAX_SCHEDULE_CHUNK_SIZE : remainingPairs + const scheduleChunk = serializeTransactionPayloads(serializedSchedule.subarray(i * 16, (i + offset) * 16)); + payloadsSchedule.push(...scheduleChunk); + remainingPairs = txn.payload.schedule.length - MAX_SCHEDULE_CHUNK_SIZE + } + return { payloadHeaderAddressScheduleLength, payloadsSchedule }; }; export const serializeConfigureDelegation = (txn: any, path: string): { payloads: Buffer[] } => { @@ -94,9 +168,6 @@ export const serializeConfigureBaker = (txn: any, path: string): { payloads: Buf return serializeTransaction(txn, path); }; -export const serializeTransferWithSchedule = (txn: any, path: string): { payloads: Buffer[] } => { - return serializeTransaction(txn, path); -}; export const serializeTransferWithScheduleAndMemo = (txn: any, path: string): { payloads: Buffer[] } => { // Convert the string to a buffer @@ -106,3 +177,51 @@ export const serializeTransferWithScheduleAndMemo = (txn: any, path: string): { txn.payload.memo = new DataBlob(memoBuffer); return serializeTransaction(txn, path); }; + +export const serializeRegisterData = (txn: any, path: string): { payloadHeader: Buffer[], payloadsData: Buffer[] } => { + // Convert the string to a buffer + const data: string = txn.payload.data; + const dataBuffer = NodeBuffer.from(data, 'utf-8'); + // Encode the buffer as a DataBlob + txn.payload.data = new DataBlob(dataBuffer); + + const serializedData = encodeDataBlob(txn.payload.data); + const serializedType = Buffer.from(Uint8Array.of(txn.transactionKind)); + + const payloadSize = serializedType.length + serializedData.length; + const serializedHeader = serializeAccountTransactionHeader(txn, payloadSize); + const serializedHeaderAndKind = Buffer.concat([serializedHeader, serializedType, serializedData.subarray(0, 2)]); + + const payloadHeader = serializeTransactionPayloadsWithDerivationPath(path, serializedHeaderAndKind); + const payloadsData = serializeTransactionPayloads(serializedData.subarray(2)); + + return { payloadHeader, payloadsData }; +}; + +export const serializeTransferToPublic = (txn: any, path: string): { payloadHeader: Buffer[], payloadsAmountAndProofsLength: Buffer[], payloadsProofs: Buffer[] } => { + const remainingAmount = Buffer.from(txn.payload.remainingAmount, 'hex'); + const transferAmount = encodeWord64(txn.payload.transferAmount.microCcdAmount); + const index = encodeWord64(txn.payload.index); + const proofs = Buffer.from(txn.payload.proofs, 'hex'); + const proofsLength = encodeWord16(proofs.length); + + const serializedType = Buffer.from(Uint8Array.of(txn.transactionKind)); + const payloadSize = remainingAmount.length + transferAmount.length + index.length + proofsLength.length + proofs.length + serializedType.length; + const serializedHeader = serializeAccountTransactionHeader(txn, payloadSize); + const serializedHeaderAndKind = Buffer.concat([serializedHeader, serializedType]); + const serializedAmountAndProofsLength = Buffer.concat([remainingAmount, transferAmount, index, proofsLength]); + + const payloadHeader = serializeTransactionPayloadsWithDerivationPath(path, serializedHeaderAndKind); + const payloadsAmountAndProofsLength = serializeTransactionPayloads(serializedAmountAndProofsLength); + const payloadsProofs = serializeTransactionPayloads(proofs); + + return { payloadHeader, payloadsAmountAndProofsLength, payloadsProofs }; +}; + +export const serializeDeployModule = (txn: any, path: string): { payloads: Buffer[] } => { + return serializeTransaction(txn, path); +}; + +export const serializeInitContract = (txn: any, path: string): { payloads: Buffer[] } => { + return serializeTransaction(txn, path); +}; \ No newline at end of file diff --git a/src/utils.ts b/src/utils.ts index 35100d9..191f534 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -79,7 +79,7 @@ export function encodeWord32(value, useLittleEndian = false) { */ export function encodeWord16(value, useLittleEndian = false) { if (value > 65535 || value < 0 || !Number.isInteger(value)) { - throw new Error('The input has to be a 16 bit unsigned integer but it was: ' + value); + throw new Error('The input has to be a 16 bit unsigned integer but it was: ' + value); } const arr = new ArrayBuffer(2); const view = new DataView(arr); @@ -126,7 +126,30 @@ function serializeScheduleAndMemo(payload: any) { }); const serializedMemo = encodeDataBlob(payload.memo); - return Buffer.concat([toAddressBuffer, serializedMemo, scheduleLength, ...bufferArray]); + return { + addressAndMemo: Buffer.concat([toAddressBuffer, serializedMemo]), + schedule: Buffer.concat([scheduleLength, ...bufferArray]), + }; +} + +function serializeTransferWithMemo(payload: any) { + const serializedToAddress = AccountAddress.toBuffer(payload.toAddress); + const serializedMemo = encodeDataBlob(payload.memo); + const serializedAmount = encodeWord64(payload.amount.microCcdAmount); + + return { + addressAndMemo: Buffer.concat([serializedToAddress, serializedMemo]), + amount: serializedAmount, + }; +} + +function serializeTransferToPublic(payload: any) { + const remainingAmount = Buffer.from(payload.remainingAmount, 'hex'); + const transferAmount = encodeWord64(payload.transferAmount.microCcdAmount); + const index = encodeWord64(payload.index); + const proofs = Buffer.from(payload.proofs, 'hex'); + const proofsLength = encodeWord16(proofs.length); + return Buffer.concat([remainingAmount, transferAmount, index, proofsLength, proofs]); } /** @@ -137,7 +160,7 @@ function serializeScheduleAndMemo(payload: any) { * @param payloadSize the byte size of the serialized payload * @returns the serialized account transaction header */ -const serializeAccountTransactionHeader = (accountTransaction, payloadSize) => { +export const serializeAccountTransactionHeader = (accountTransaction, payloadSize) => { const serializedSender = AccountAddress.toBuffer(accountTransaction.sender); const serializedNonce = encodeWord64(accountTransaction.nonce); const serializedEnergyAmount = encodeWord64(accountTransaction.energyAmount); @@ -164,13 +187,17 @@ export const serializeAccountTransaction = (accountTransaction) => { const serializedType = Buffer.from(Uint8Array.of(accountTransaction.transactionKind)); let serializedPayload; - if (isAccountTransactionHandlerExists(accountTransaction.transactionKind)) { + if (isAccountTransactionHandlerExists(accountTransaction.transactionKind) && accountTransaction.transactionKind !== AccountTransactionType.TransferWithMemo) { const accountTransactionHandler = getAccountTransactionHandler(accountTransaction.transactionKind); serializedPayload = accountTransactionHandler.serialize(accountTransaction.payload); } else if (accountTransaction.transactionKind === AccountTransactionType.TransferWithSchedule) { serializedPayload = serializeSchedule(accountTransaction.payload); } else if (accountTransaction.transactionKind === AccountTransactionType.TransferWithScheduleAndMemo) { serializedPayload = serializeScheduleAndMemo(accountTransaction.payload); + } else if (accountTransaction.transactionKind === AccountTransactionType.TransferToPublic) { + serializedPayload = serializeTransferToPublic(accountTransaction.payload); + } else if (accountTransaction.transactionKind === AccountTransactionType.TransferWithMemo) { + serializedPayload = serializeTransferWithMemo(accountTransaction.payload); } const serializedHeader = serializeAccountTransactionHeader(accountTransaction, serializedPayload.length + 1);