Skip to content

Commit

Permalink
feat: added signTransferWithScheduleAndMemo, ConfigureBaker
Browse files Browse the repository at this point in the history
  • Loading branch information
GuilaneDen committed Dec 2, 2024
1 parent aa91597 commit 7f0614a
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 22 deletions.
81 changes: 75 additions & 6 deletions src/Concordium.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {
serializeSimpleTransfer,
serializeSimpleTransferWithMemo,
serializeTransferWithSchedule,
serializeConfigureBaker,
serializeTransferWithScheduleAndMemo
} from "./serialization";
import BigNumber from "bignumber.js";
import { encodeInt32 } from "./utils";
Expand Down Expand Up @@ -34,7 +36,12 @@ const INS = {
GET_PUBLIC_KEY: 0x01,
GET_VERSION: 0x03,
GET_APP_NAME: 0x04,
SIGN_TX: 0x06,
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,
};

const concordium_path = "44'/919'/0'/0/0/0";
Expand Down Expand Up @@ -150,7 +157,7 @@ export default class Concordium {
* @example
* concordium.getPublicKey("1105'/0'/0'/0/0/0/0/", true, false)
*/
async getPublicKey(path: string, display?: boolean, signedKey?: boolean): Promise<{ publicKey: string }> {
async getPublicKey(path: string, display?: boolean, signedKey?: boolean): Promise<{ publicKey: string, signedPublicKey?: string }> {
const pathBuffer = pathToBuffer(path);

const publicKeyBuffer = await this.sendToDevice(
Expand All @@ -162,6 +169,13 @@ export default class Concordium {

const publicKeyLength = publicKeyBuffer[0];

if (signedKey) {
return {
publicKey: publicKeyBuffer.subarray(1, 1 + publicKeyLength).toString("hex"),
signedPublicKey: publicKeyBuffer.subarray(1 + publicKeyLength).toString("hex"),
};
}

return {
publicKey: publicKeyBuffer.subarray(1, 1 + publicKeyLength).toString("hex"),
};
Expand All @@ -185,7 +199,7 @@ export default class Concordium {
for (let i = 0; i < payloads.length; i++) {
const lastChunk = i === payloads.length - 1;
response = await this.sendToDevice(
INS.SIGN_TX,
INS.SIGN_TRANSFER,
P1_FIRST_CHUNK + i,
lastChunk ? P2_LAST : P2_MORE,
payloads[i]
Expand All @@ -212,7 +226,7 @@ export default class Concordium {
for (let i = 0; i < payloads.length; i++) {
const lastChunk = i === payloads.length - 1;
response = await this.sendToDevice(
INS.SIGN_TX,
INS.SIGN_TRANSFER_MEMO,
P1_FIRST_CHUNK + i,
lastChunk ? P2_LAST : P2_MORE,
payloads[i]
Expand All @@ -238,7 +252,34 @@ export default class Concordium {
for (let i = 0; i < payloads.length; i++) {
const lastChunk = i === payloads.length - 1;
response = await this.sendToDevice(
INS.SIGN_TX,
INS.SIGN_TRANSFER_SCHEDULE,
P1_FIRST_CHUNK + i,
lastChunk ? P2_LAST : P2_MORE,
payloads[i]
);
}

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 }> {


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_SCHEDULE_AND_MEMO,
P1_FIRST_CHUNK + i,
lastChunk ? P2_LAST : P2_MORE,
payloads[i]
Expand All @@ -265,7 +306,35 @@ export default class Concordium {
for (let i = 0; i < payloads.length; i++) {
const lastChunk = i === payloads.length - 1;
response = await this.sendToDevice(
INS.SIGN_TX,
INS.SIGN_CONFIGURE_DELEGATION,
P1_FIRST_CHUNK + i,
lastChunk ? P2_LAST : P2_MORE,
payloads[i]
);
}

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 signConfigureBaker(txn, path: string): Promise<{ signature: string[]; transaction }> {


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_CONFIGURE_BAKER,
P1_FIRST_CHUNK + i,
lastChunk ? P2_LAST : P2_MORE,
payloads[i]
Expand Down
18 changes: 15 additions & 3 deletions src/serialization.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import BigNumber from "bignumber.js";
import BIPPath from "bip32-path";
import { serializeAccountTransaction } from "./utils";
import { DataBlob } from "@concordium/common-sdk/lib/types/DataBlob";
import { Buffer as NodeBuffer } from 'buffer/';
import { Buffer as NodeBuffer } from 'buffer/index';
const MAX_CHUNK_SIZE = 255;

const serializePath = (path: number[]): Buffer => {
Expand Down Expand Up @@ -82,7 +81,7 @@ export const serializeSimpleTransferWithMemo = (txn: any, path: string): { paylo
const memo: string = txn.payload.memo;
const memoBuffer = NodeBuffer.from(memo, 'utf-8');
// Encode the buffer as a DataBlob
txn.memo = new DataBlob(memoBuffer);
txn.payload.memo = new DataBlob(memoBuffer);

return serializeTransaction(txn, path);
};
Expand All @@ -91,6 +90,19 @@ export const serializeConfigureDelegation = (txn: any, path: string): { payloads
return serializeTransaction(txn, path);
};

export const serializeConfigureBaker = (txn: any, path: string): { payloads: Buffer[] } => {
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
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);
};
62 changes: 49 additions & 13 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,6 @@ export function isAccountTransactionHandlerExists(transactionKind: AccountTransa
}
}

/**
* Encodes a 8 bit signed integer to a Buffer using big endian.
* @param value a 8 bit integer
* @returns big endian serialization of the input
*/
export function encodeInt8(value: number): Buffer {
if (value > 127 || value < -128 || !Number.isInteger(value)) {
throw new Error('The input has to be a 8 bit signed integer but it was: ' + value);
}
return Buffer.from(Buffer.of(value));
}

/**
* Encodes a 64 bit unsigned integer to a Buffer using big endian.
* @param value a 64 bit integer
Expand All @@ -60,7 +48,7 @@ export function encodeWord64(value, useLittleEndian = false) {
*/
export function encodeInt32(value, useLittleEndian = false) {
if (value < -2147483648 || value > 2147483647 || !Number.isInteger(value)) {
throw new Error('The input has to be a 32 bit signed integer but it was: ' + value);
throw new Error('The input has to be a 32 bit signed integer but it was: ' + value);
}
const arr = new ArrayBuffer(4);
const view = new DataView(arr);
Expand All @@ -83,6 +71,39 @@ export function encodeWord32(value, useLittleEndian = false) {
return Buffer.from(new Uint8Array(arr));
}

/**
* Encodes a 16 bit unsigned integer to a Buffer using big endian.
* @param value a 16 bit integer
* @param useLittleEndian a boolean value, if not given, the value is serialized in big endian.
* @returns big endian serialization of the input
*/
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);
}
const arr = new ArrayBuffer(2);
const view = new DataView(arr);
view.setUint16(0, value, useLittleEndian);
return Buffer.from(new Uint8Array(arr));
}

/**
* Encodes a 8 bit signed integer to a Buffer using big endian.
* @param value a 8 bit integer
* @returns big endian serialization of the input
*/
export function encodeInt8(value: number): Buffer {
if (value > 127 || value < -128 || !Number.isInteger(value)) {
throw new Error('The input has to be a 8 bit signed integer but it was: ' + value);
}
return Buffer.from(Buffer.of(value));
}

export function encodeDataBlob(blob) {
const length = encodeWord16(blob.data.length);
return Buffer.concat([length, blob.data]);
}

function serializeSchedule(payload: any) {
const toAddressBuffer = AccountAddress.toBuffer(payload.toAddress);
const scheduleLength = encodeInt8(payload.schedule.length);
Expand All @@ -95,6 +116,19 @@ function serializeSchedule(payload: any) {
return Buffer.concat([toAddressBuffer, scheduleLength, ...bufferArray]);
}

function serializeScheduleAndMemo(payload: any) {
const toAddressBuffer = AccountAddress.toBuffer(payload.toAddress);
const scheduleLength = encodeInt8(payload.schedule.length);
const bufferArray = payload.schedule.map((item: { timestamp: string, amount: string }) => {
const timestampBuffer = encodeWord64(item.timestamp);
const amountBuffer = encodeWord64(item.amount);
return Buffer.concat([timestampBuffer, amountBuffer]);
});
const serializedMemo = encodeDataBlob(payload.memo);

return Buffer.concat([toAddressBuffer, serializedMemo, scheduleLength, ...bufferArray]);
}

/**
* Serialization of an account transaction header. The payload size is a part of the header,
* but is factored out of the type as it always has to be derived from the serialized
Expand Down Expand Up @@ -135,6 +169,8 @@ export const serializeAccountTransaction = (accountTransaction) => {
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);
}

const serializedHeader = serializeAccountTransactionHeader(accountTransaction, serializedPayload.length + 1);
Expand Down

0 comments on commit 7f0614a

Please sign in to comment.