From af3a92a0ad18f07e7df5d0e658efc23388ce2965 Mon Sep 17 00:00:00 2001 From: Dmytro Vynnyk Date: Thu, 12 Dec 2024 13:26:59 +0100 Subject: [PATCH] Improve Transaction creation from JSON --- README.md | 8 ++-- src/rpc/client.ts | 4 +- src/rpc/request.ts | 4 +- src/rpc/rpc_client.ts | 11 +++--- src/types/Transaction.ts | 85 +++++++++++++++++++++++++++++++++++++++- 5 files changed, 98 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index e87834763..b111be09c 100644 --- a/README.md +++ b/README.md @@ -176,12 +176,14 @@ const transactionPayload = TransactionV1Payload.build({ pricingMode }); -const transaction = TransactionV1.makeTransactionV1( +const transactionV1 = TransactionV1.makeTransactionV1( transactionPayload ); -await transaction.sign(privateKey); +await transactionV1.sign(privateKey); -const result = await rpcClient.putTransactionV1(transaction); +const tx = Transaction.fromTransactionV1(transactionV1); + +const result = await rpcClient.putTransaction(tx); console.log(`Transaction Hash: ${result.transactionHash}`); ``` diff --git a/src/rpc/client.ts b/src/rpc/client.ts index 3ada7b28b..c4a4b9e2d 100644 --- a/src/rpc/client.ts +++ b/src/rpc/client.ts @@ -35,7 +35,7 @@ import { PurseIdentifier, RpcRequest } from './request'; -import { TransactionV1, Deploy, PublicKey } from '../types'; +import { Deploy, PublicKey, Transaction } from '../types'; export interface ClientPOS { getLatestAuctionInfo(): Promise; @@ -226,7 +226,7 @@ export interface ClientInformational { export interface ClientTransactional { putDeploy(deploy: Deploy): Promise; - putTransactionV1(transaction: TransactionV1): Promise; + putTransaction(transaction: Transaction): Promise; } export interface IClient diff --git a/src/rpc/request.ts b/src/rpc/request.ts index 380e7be3b..a55e0d2ff 100644 --- a/src/rpc/request.ts +++ b/src/rpc/request.ts @@ -385,10 +385,10 @@ export class PutDeployRequest { @jsonObject export class PutTransactionRequest { @jsonMember({ constructor: TransactionWrapper }) - transaction: TransactionWrapper; + transactionWrapper: TransactionWrapper; constructor(transaction: TransactionWrapper) { - this.transaction = transaction; + this.transactionWrapper = transaction; } } diff --git a/src/rpc/rpc_client.ts b/src/rpc/rpc_client.ts index 2cb38a37c..1b3e87425 100644 --- a/src/rpc/rpc_client.ts +++ b/src/rpc/rpc_client.ts @@ -58,11 +58,10 @@ import { import { IDValue } from './id_value'; import { TransactionHash, - TransactionV1, - TransactionWrapper, Deploy, PublicKey, - Hash + Hash, + Transaction } from '../types'; export class RpcClient implements IClient { @@ -903,12 +902,12 @@ export class RpcClient implements IClient { return result; } - async putTransactionV1( - transaction: TransactionV1 + async putTransaction( + transaction: Transaction ): Promise { const serializer = new TypedJSON(PutTransactionRequest); const transactionRequestParam = new PutTransactionRequest( - new TransactionWrapper(undefined, transaction) + transaction.getTransactionWrapper() ); const resp = await this.processRequest( diff --git a/src/types/Transaction.ts b/src/types/Transaction.ts index 601d3c4e2..a8d62f0e5 100644 --- a/src/types/Transaction.ts +++ b/src/types/Transaction.ts @@ -404,7 +404,6 @@ export class Transaction { * @param entryPoint The entry point of the transaction. * @param scheduling The scheduling information for the transaction. * @param approvals The list of approvals for this transaction. - * @param category The category of the transaction, indicating its type (e.g., minting, auction). * @param originTransactionV1 The original TransactionV1, if applicable. * @param originDeployV1 The original deploy, if applicable. */ @@ -437,6 +436,18 @@ export class Transaction { this.originDeployV1 = originDeployV1; this.originTransactionV1 = originTransactionV1; + + if (!(this.originDeployV1 || this.originTransactionV1)) { + throw new Error( + 'Incorrect Transaction instance. Missing originTransactionV1 or originDeploy' + ); + } + + if (this.originDeployV1 && this.originTransactionV1) { + throw new Error( + 'Incorrect Transaction instance. Should be only one of originTransactionV1 or originDeploy' + ); + } } /** @@ -455,6 +466,53 @@ export class Transaction { return this.originTransactionV1; } + public getTransactionWrapper(): TransactionWrapper { + return new TransactionWrapper(this.originDeployV1, this.originTransactionV1); + } + + /** + * Validates the transaction by checking the transaction hash and the approval signatures. + * @throws {TransactionError} Throws errors if validation fails. + */ + public validate(): boolean { + if (this.originTransactionV1) { + return this.originTransactionV1.validate(); + } else if (this.originDeployV1) { + return this.originDeployV1.validate(); + } + + throw new Error('Incorrect Transaction instance. Missing origin value'); + } + + /** + * Signs the transaction using the provided private key. + * @param key The private key to sign the transaction. + */ + async sign(key: PrivateKey): Promise { + const signatureBytes = await key.sign(this.hash.toBytes()); + this.setSignature(signatureBytes, key.publicKey); + } + + /** + * Sets an already generated signature to the transaction. + * @param signature The Ed25519 or Secp256K1 signature. + * @param publicKey The public key used to generate the signature. + */ + setSignature(signature: Uint8Array, publicKey: PublicKey) { + const hex = new HexBytes(signature); + const approval = new Approval(publicKey, hex); + + this.approvals.push(approval); + + if (this.originTransactionV1) { + this.originTransactionV1.approvals.push(approval); + } else if (this.originDeployV1) { + this.originDeployV1.approvals.push(approval); + } else { + throw new Error('Incorrect Transaction instance. Missing origin value'); + } + } + /** * Converts a `TransactionV1` to a `Transaction` object. * @param v1 The `TransactionV1` to convert. @@ -477,6 +535,31 @@ export class Transaction { undefined // originDeployV1 is not applicable for this method ); } + + /** + * Converts a `TransactionV1` to a `Transaction` object. + * @param deploy The `Deploy` to convert. + * @returns A new `Transaction` instance created from the given `Deploy`. + */ + static fromDeploy(deploy: Deploy): Transaction { + return Deploy.newTransactionFromDeploy(deploy); + } + + static fromJson(json: any): Transaction { + try { + const txV1 = TransactionV1.fromJSON(json); + + return Transaction.fromTransactionV1(txV1); + } catch (e) {} + + try { + const deploy = Deploy.fromJSON(json); + + return Transaction.fromDeploy(deploy); + } catch (e) {} + + throw new Error("The JSON can't be parsed as a Transaction."); + } } /**