Skip to content

Commit

Permalink
Merge pull request #403 from multiversx/tx-factories-constructors-ref…
Browse files Browse the repository at this point in the history
…actoring

Refactored the constructors for the transactions factories
  • Loading branch information
popenta authored Mar 15, 2024
2 parents ea1499a + 6df2983 commit 52fdbf7
Show file tree
Hide file tree
Showing 17 changed files with 121 additions and 127 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@multiversx/sdk-core",
"version": "13.0.0-beta.5",
"version": "13.0.0-beta.6",
"description": "MultiversX SDK for JavaScript and TypeScript",
"main": "out/index.js",
"types": "out/index.d.js",
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ export * from "./transactionPayload";
export * from "./transactionWatcher";
export * from "./transferTransactionsFactory";
export * from "./utils";
export * from "./transactionComputer";
2 changes: 1 addition & 1 deletion src/smartcontracts/smartContract.local.net.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { TransactionsFactoryConfig } from "../transactionsFactories/transactions
import { SmartContractTransactionsFactory } from "../transactionsFactories/smartContractTransactionsFactory";
import { TokenComputer } from "../tokens";
import { promises } from "fs";
import { TransactionComputer } from "../transaction";
import { TransactionComputer } from "../transactionComputer";

describe("test on local testnet", function () {
let alice: TestWallet, bob: TestWallet, carol: TestWallet;
Expand Down
5 changes: 3 additions & 2 deletions src/transaction.local.net.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ import { Logger } from "./logger";
import { loadTestWallets, TestWallet } from "./testutils";
import { createLocalnetProvider } from "./testutils/networkProviders";
import { TokenTransfer } from "./tokenTransfer";
import { Transaction, TransactionComputer } from "./transaction";
import { Transaction } from "./transaction";
import { TransactionPayload } from "./transactionPayload";
import { TransactionWatcher } from "./transactionWatcher";
import { TransactionsFactoryConfig } from "./transactionsFactories/transactionsFactoryConfig";
import { NextTransferTransactionsFactory } from "./transactionsFactories/transferTransactionsFactory";
import { TokenComputer } from "./tokens";
import { TransactionComputer } from "./transactionComputer";

describe("test transaction", function () {
let alice: TestWallet, bob: TestWallet;
Expand Down Expand Up @@ -152,7 +153,7 @@ describe("test transaction", function () {
const network = await provider.getNetworkConfig();

const config = new TransactionsFactoryConfig({ chainID: network.ChainID });
const factory = new NextTransferTransactionsFactory(config, new TokenComputer());
const factory = new NextTransferTransactionsFactory({ config: config, tokenComputer: new TokenComputer() });

await alice.sync(provider);
await bob.sync(provider);
Expand Down
3 changes: 2 additions & 1 deletion src/transaction.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import { TransactionOptions, TransactionVersion } from "./networkParams";
import { ProtoSerializer } from "./proto";
import { TestWallet, loadTestWallets } from "./testutils";
import { TokenTransfer } from "./tokenTransfer";
import { Transaction, TransactionComputer } from "./transaction";
import { Transaction } from "./transaction";
import { TransactionPayload } from "./transactionPayload";
import { TransactionComputer } from "./transactionComputer";

describe("test transaction", async () => {
let wallets: Record<string, TestWallet>;
Expand Down
81 changes: 1 addition & 80 deletions src/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { BigNumber } from "bignumber.js";
import { Address } from "./address";
import { TRANSACTION_MIN_GAS_PRICE, TRANSACTION_OPTIONS_DEFAULT, TRANSACTION_VERSION_DEFAULT } from "./constants";
import { TransactionsConverter } from "./converters/transactionsConverter";
import * as errors from "./errors";
import { Hash } from "./hash";
import {
IAddress,
Expand All @@ -12,20 +11,16 @@ import {
INonce,
IPlainTransactionObject,
ISignature,
ITransaction,
ITransactionOptions,
ITransactionPayload,
ITransactionValue,
ITransactionVersion,
} from "./interface";
import { INetworkConfig } from "./interfaceOfNetwork";
import { TransactionOptions, TransactionVersion } from "./networkParams";
import { ProtoSerializer } from "./proto";
import { interpretSignatureAsBuffer } from "./signature";
import { TransactionPayload } from "./transactionPayload";

const createTransactionHasher = require("blake2b");
const TRANSACTION_HASH_LENGTH = 32;
import { TransactionComputer } from "./transactionComputer";

/**
* An abstraction for creating, signing and broadcasting transactions.
Expand Down Expand Up @@ -446,77 +441,3 @@ export class TransactionHash extends Hash {
return new TransactionHash(Buffer.from(hash).toString("hex"));
}
}

/**
* An utilitary class meant to work together with the {@link Transaction} class.
*/
export class TransactionComputer {
constructor() {}

computeTransactionFee(
transaction: { gasPrice: bigint; gasLimit: bigint; data: Uint8Array },
networkConfig: INetworkConfig,
): bigint {
const moveBalanceGas = BigInt(
networkConfig.MinGasLimit + transaction.data.length * networkConfig.GasPerDataByte,
);
if (moveBalanceGas > transaction.gasLimit) {
throw new errors.ErrNotEnoughGas(parseInt(transaction.gasLimit.toString(), 10));
}

const gasPrice = transaction.gasPrice;
const feeForMove = moveBalanceGas * gasPrice;
if (moveBalanceGas === transaction.gasLimit) {
return feeForMove;
}

const diff = transaction.gasLimit - moveBalanceGas;
const modifiedGasPrice = BigInt(
new BigNumber(gasPrice.toString()).multipliedBy(new BigNumber(networkConfig.GasPriceModifier)).toFixed(0),
);
const processingFee = diff * modifiedGasPrice;

return feeForMove + processingFee;
}

computeBytesForSigning(transaction: ITransaction): Uint8Array {
// TODO: do some checks for the transaction e.g. sender, chain ID etc.
// TODO: for appropriate tx.version, interpret tx.options accordingly and sign using the content / data hash

const plainTransaction = this.toPlainObjectForSigning(transaction);
const serialized = JSON.stringify(plainTransaction);
return new Uint8Array(Buffer.from(serialized));
}

computeTransactionHash(transaction: ITransaction): Uint8Array {
const serializer = new ProtoSerializer();
const buffer = serializer.serializeTransaction(new Transaction(transaction));
const hash = createTransactionHasher(TRANSACTION_HASH_LENGTH).update(buffer).digest("hex");

return Buffer.from(hash, "hex");
}

private toPlainObjectForSigning(transaction: ITransaction) {
return {
nonce: Number(transaction.nonce),
value: transaction.value.toString(),
receiver: transaction.receiver,
sender: transaction.sender,
senderUsername: this.toBase64OrUndefined(transaction.senderUsername),
receiverUsername: this.toBase64OrUndefined(transaction.receiverUsername),
gasPrice: Number(transaction.gasPrice),
gasLimit: Number(transaction.gasLimit),
data: this.toBase64OrUndefined(transaction.data),
chainID: transaction.chainID,
version: transaction.version,
options: transaction.options ? transaction.options : undefined,
guardian: transaction.guardian ? transaction.guardian : undefined,
};
}

private toBase64OrUndefined(value?: string | Uint8Array) {
return value && value.length ? Buffer.from(value).toString("base64") : undefined;
}

// TODO: add missing functions wrt. specs
}
83 changes: 83 additions & 0 deletions src/transactionComputer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { INetworkConfig } from "./interfaceOfNetwork";
import * as errors from "./errors";
import BigNumber from "bignumber.js";
import { ITransaction } from "./interface";
import { ProtoSerializer } from "./proto";
import { Transaction } from "./transaction";

const createTransactionHasher = require("blake2b");
const TRANSACTION_HASH_LENGTH = 32;

/**
* An utilitary class meant to work together with the {@link Transaction} class.
*/
export class TransactionComputer {
constructor() {}

computeTransactionFee(
transaction: { gasPrice: bigint; gasLimit: bigint; data: Uint8Array },
networkConfig: INetworkConfig,
): bigint {
const moveBalanceGas = BigInt(
networkConfig.MinGasLimit + transaction.data.length * networkConfig.GasPerDataByte,
);
if (moveBalanceGas > transaction.gasLimit) {
throw new errors.ErrNotEnoughGas(parseInt(transaction.gasLimit.toString(), 10));
}

const gasPrice = transaction.gasPrice;
const feeForMove = moveBalanceGas * gasPrice;
if (moveBalanceGas === transaction.gasLimit) {
return feeForMove;
}

const diff = transaction.gasLimit - moveBalanceGas;
const modifiedGasPrice = BigInt(
new BigNumber(gasPrice.toString()).multipliedBy(new BigNumber(networkConfig.GasPriceModifier)).toFixed(0),
);
const processingFee = diff * modifiedGasPrice;

return feeForMove + processingFee;
}

computeBytesForSigning(transaction: ITransaction): Uint8Array {
// TODO: do some checks for the transaction e.g. sender, chain ID etc.
// TODO: for appropriate tx.version, interpret tx.options accordingly and sign using the content / data hash

const plainTransaction = this.toPlainObjectForSigning(transaction);
const serialized = JSON.stringify(plainTransaction);
return new Uint8Array(Buffer.from(serialized));
}

computeTransactionHash(transaction: ITransaction): Uint8Array {
const serializer = new ProtoSerializer();
const buffer = serializer.serializeTransaction(new Transaction(transaction));
const hash = createTransactionHasher(TRANSACTION_HASH_LENGTH).update(buffer).digest("hex");

return Buffer.from(hash, "hex");
}

private toPlainObjectForSigning(transaction: ITransaction) {
return {
nonce: Number(transaction.nonce),
value: transaction.value.toString(),
receiver: transaction.receiver,
sender: transaction.sender,
senderUsername: this.toBase64OrUndefined(transaction.senderUsername),
receiverUsername: this.toBase64OrUndefined(transaction.receiverUsername),
gasPrice: Number(transaction.gasPrice),
gasLimit: Number(transaction.gasLimit),
data: this.toBase64OrUndefined(transaction.data),
chainID: transaction.chainID,
version: transaction.version,
options: transaction.options ? transaction.options : undefined,
guardian: transaction.guardian ? transaction.guardian : undefined,
};
}

private toBase64OrUndefined(value?: string | Uint8Array) {
return value && value.length ? Buffer.from(value).toString("base64") : undefined;
}

// TODO: add missing functions wrt. specs
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { TransactionsFactoryConfig } from "./transactionsFactoryConfig";

describe("test delegation transactions factory", function () {
const config = new TransactionsFactoryConfig({ chainID: "D" });
const delegationFactory = new DelegationTransactionsFactory(config);
const delegationFactory = new DelegationTransactionsFactory({ config: config });

it("should create 'Transaction' for new delegation contract", async function () {
const sender = Address.fromBech32("erd18s6a06ktr2v6fgxv4ffhauxvptssnaqlds45qgsrucemlwc8rawq553rt2");
Expand Down
4 changes: 2 additions & 2 deletions src/transactionsFactories/delegationTransactionsFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ interface IValidatorPublicKey {
export class DelegationTransactionsFactory {
private readonly config: Config;

constructor(config: Config) {
this.config = config;
constructor(options: { config: Config }) {
this.config = options.config;
}

createTransactionForNewDelegationContract(options: {
Expand Down
5 changes: 3 additions & 2 deletions src/transactionsFactories/relayedTransactionsFactory.spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { assert } from "chai";
import { TestWallet, loadTestWallets } from "../testutils";
import { Transaction, TransactionComputer } from "../transaction";
import { Transaction } from "../transaction";
import { RelayedTransactionsFactory } from "./relayedTransactionsFactory";
import { TransactionsFactoryConfig } from "./transactionsFactoryConfig";
import { TransactionComputer } from "../transactionComputer";

describe("test relayed v1 transaction builder", function () {
const config = new TransactionsFactoryConfig({ chainID: "T" });
const factory = new RelayedTransactionsFactory(config);
const factory = new RelayedTransactionsFactory({ config: config });
const transactionComputer = new TransactionComputer();
let alice: TestWallet, bob: TestWallet, carol: TestWallet, grace: TestWallet, frank: TestWallet;

Expand Down
9 changes: 3 additions & 6 deletions src/transactionsFactories/relayedTransactionsFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,11 @@ interface IConfig {
export class RelayedTransactionsFactory {
private readonly config: IConfig;

constructor(config: IConfig) {
this.config = config;
constructor(options: { config: IConfig }) {
this.config = options.config;
}

createRelayedV1Transaction(options: {
innerTransaction: ITransaction;
relayerAddress: IAddress;
}): Transaction {
createRelayedV1Transaction(options: { innerTransaction: ITransaction; relayerAddress: IAddress }): Transaction {
if (!options.innerTransaction.gasLimit) {
throw new ErrInvalidInnerTransaction("The gas limit is not set for the inner transaction");
}
Expand Down
8 changes: 4 additions & 4 deletions src/transactionsFactories/smartContractTransactionsFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ export class SmartContractTransactionsFactory {
private readonly tokenComputer: TokenComputer;
private readonly dataArgsBuilder: TokenTransfersDataBuilder;

constructor({ config, abi, tokenComputer }: { config: Config; abi?: IAbi; tokenComputer: TokenComputer }) {
this.config = config;
this.abi = abi;
this.tokenComputer = tokenComputer;
constructor(options: { config: Config; abi?: IAbi; tokenComputer: TokenComputer }) {
this.config = options.config;
this.abi = options.abi;
this.tokenComputer = options.tokenComputer;
this.dataArgsBuilder = new TokenTransfersDataBuilder();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ describe("test token management transactions factory", () => {
before(async function () {
({ frank, grace } = await loadTestWallets());
config = new TransactionsFactoryConfig({ chainID: "T" });
tokenManagementFactory = new TokenManagementTransactionsFactory(config);
tokenManagementFactory = new TokenManagementTransactionsFactory({ config: config });
});

it("should create 'Transaction' for registering and setting roles", () => {
Expand Down
Loading

0 comments on commit 52fdbf7

Please sign in to comment.