Skip to content

Commit

Permalink
Merge pull request #339 from multiversx/update-transaction-factories
Browse files Browse the repository at this point in the history
Update transaction factories to match the sdk-specs
  • Loading branch information
popenta authored Oct 23, 2023
2 parents 17e4312 + 41538ac commit b2c0025
Show file tree
Hide file tree
Showing 15 changed files with 722 additions and 683 deletions.
6 changes: 3 additions & 3 deletions src/transactionIntent.ts → src/draftTransaction.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { BigNumber } from "bignumber.js";

export class TransactionIntent {
export class DraftTransaction {
public sender: string;
public receiver: string;
public gasLimit: BigNumber.Value;
Expand All @@ -17,7 +17,7 @@ export class TransactionIntent {
this.sender = options.sender;
this.receiver = options.receiver;
this.gasLimit = options.gasLimit;
this.value = options.value;
this.data = options.data;
this.value = options.value ?? 0;
this.data = options.data ?? new Uint8Array();
}
}
14 changes: 8 additions & 6 deletions src/smartcontracts/interaction.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ describe("test smart contract interactor", function () {
let interaction = new Interaction(contract, dummyFunction, []);

let transaction = interaction
.withSender(alice.address)
.withNonce(7)
.withValue(TokenTransfer.egldFromAmount(1))
.withGasLimit(20000000)
Expand Down Expand Up @@ -63,6 +64,7 @@ describe("test smart contract interactor", function () {

// ESDT, single
let transaction = new Interaction(contract, dummyFunction, [])
.withSender(alice)
.withSingleESDTTransfer(TokenFoo(10))
.buildTransaction();

Expand Down Expand Up @@ -180,7 +182,7 @@ describe("test smart contract interactor", function () {
assert.isTrue(queryCode.equals(ReturnCode.Ok));

// Execute, do not wait for execution
let transaction = interaction.withNonce(0).buildTransaction();
let transaction = interaction.withSender(alice.address).withNonce(0).buildTransaction();
transaction.setSender(alice.address);
await alice.signer.sign(transaction);
await provider.sendTransaction(transaction);
Expand Down Expand Up @@ -235,15 +237,15 @@ describe("test smart contract interactor", function () {

assert.deepEqual(counterValue!.valueOf(), new BigNumber(7));

let incrementTransaction = incrementInteraction.withNonce(14).buildTransaction();
let incrementTransaction = incrementInteraction.withSender(alice.address).withNonce(14).buildTransaction();
await alice.signer.sign(incrementTransaction);
provider.mockGetTransactionWithAnyHashAsNotarizedWithOneResult("@6f6b@08");
let { bundle: { firstValue: valueAfterIncrement } } = await controller.execute(incrementInteraction, incrementTransaction);
assert.deepEqual(valueAfterIncrement!.valueOf(), new BigNumber(8));

// Decrement three times (simulate three parallel broadcasts). Wait for execution of the latter (third transaction). Return fake "5".
// Decrement #1
let decrementTransaction = decrementInteraction.withNonce(15).buildTransaction();
let decrementTransaction = decrementInteraction.withSender(alice.address).withNonce(15).buildTransaction();
await alice.signer.sign(decrementTransaction);
await provider.sendTransaction(decrementTransaction);
// Decrement #2
Expand Down Expand Up @@ -292,7 +294,7 @@ describe("test smart contract interactor", function () {
);

// start()
let startTransaction = startInteraction.withNonce(14).buildTransaction();
let startTransaction = startInteraction.withSender(alice.address).withNonce(14).buildTransaction();
await alice.signer.sign(startTransaction);
provider.mockGetTransactionWithAnyHashAsNotarizedWithOneResult("@6f6b");
let { bundle: { returnCode: startReturnCode, values: startReturnValues } } = await controller.execute(startInteraction, startTransaction);
Expand All @@ -302,7 +304,7 @@ describe("test smart contract interactor", function () {
assert.lengthOf(startReturnValues, 0);

// status() (this is a view function, but for the sake of the test, we'll execute it)
let statusTransaction = statusInteraction.withNonce(15).buildTransaction();
let statusTransaction = statusInteraction.withSender(alice.address).withNonce(15).buildTransaction();
await alice.signer.sign(statusTransaction);
provider.mockGetTransactionWithAnyHashAsNotarizedWithOneResult("@6f6b@01");
let { bundle: { returnCode: statusReturnCode, values: statusReturnValues, firstValue: statusFirstValue } } = await controller.execute(statusInteraction, statusTransaction);
Expand All @@ -313,7 +315,7 @@ describe("test smart contract interactor", function () {
assert.deepEqual(statusFirstValue!.valueOf(), { name: "Running", fields: [] });

// lotteryInfo() (this is a view function, but for the sake of the test, we'll execute it)
let getLotteryInfoTransaction = getLotteryInfoInteraction.withNonce(15).buildTransaction();
let getLotteryInfoTransaction = getLotteryInfoInteraction.withSender(alice.address).withNonce(15).buildTransaction();
await alice.signer.sign(getLotteryInfoTransaction);
provider.mockGetTransactionWithAnyHashAsNotarizedWithOneResult("@6f6b@0000000b6c75636b792d746f6b656e000000010100000000000000005fc2b9dbffffffff00000001640000000a140ec80fa7ee88000000");
let { bundle: { returnCode: infoReturnCode, values: infoReturnValues, firstValue: infoFirstValue } } = await controller.execute(getLotteryInfoInteraction, getLotteryInfoTransaction);
Expand Down
68 changes: 30 additions & 38 deletions src/smartcontracts/smartContract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ import { NativeSerializer } from "./nativeSerializer";
import { Query } from "./query";
import { ArwenVirtualMachine, ContractCallPayloadBuilder, ContractUpgradePayloadBuilder } from "./transactionPayloadBuilders";
import { EndpointDefinition, TypedValue } from "./typesystem";
import { SmartContractTransactionIntentsFactory } from "../transactionIntentsFactories/smartContractTransactionIntentsFactory";
import { TransactionIntentsFactoryConfig } from "../transactionIntentsFactories/transactionIntentsFactoryConfig";
import { SmartContractTransactionsFactory } from "../transactionsFactories/smartContractTransactionsFactory";
import { TransactionsFactoryConfig } from "../transactionsFactories/transactionsFactoryConfig";
import { TransactionPayload } from "../transactionPayload";
import { TRANSACTION_MIN_GAS_PRICE } from "../constants";
const createKeccakHash = require("keccak");

interface IAbi {
Expand Down Expand Up @@ -115,16 +116,16 @@ export class SmartContract implements ISmartContract {
deploy({ deployer, code, codeMetadata, initArguments, value, gasLimit, gasPrice, chainID }: DeployArguments): Transaction {
Compatibility.guardAddressIsSetAndNonZero(deployer, "'deployer' of SmartContract.deploy()", "pass the actual address to deploy()");

const config = new TransactionIntentsFactoryConfig(chainID.valueOf());
const scIntentFactory = new SmartContractTransactionIntentsFactory({
const config = new TransactionsFactoryConfig(chainID.valueOf());
const scDraftTransactionFactory = new SmartContractTransactionsFactory({
config: config,
abi: this.abi
});

const bytecode = Buffer.from(code.toString(), 'hex');
const metadataAsJson = this.getMetadataPropertiesAsObject(codeMetadata);

const intent = scIntentFactory.createTransactionIntentForDeploy({
const draftTx = scDraftTransactionFactory.createTransactionForDeploy({
sender: deployer,
bytecode: bytecode,
gasLimit: gasLimit.valueOf(),
Expand All @@ -135,15 +136,12 @@ export class SmartContract implements ISmartContract {
isPayableBySmartContract: metadataAsJson.payableBySc
});

return new Transaction({
receiver: Address.fromBech32(intent.receiver),
sender: Address.fromBech32(intent.sender),
value: value,
gasLimit: new BigNumber(intent.gasLimit).toNumber(),
gasPrice: gasPrice,
data: new TransactionPayload(Buffer.from(intent.data!)),
chainID: chainID
});
let transaction = Transaction.fromDraft(draftTx);
transaction.setChainID(chainID);
transaction.setValue(value ?? 0);
transaction.setGasPrice(gasPrice ?? TRANSACTION_MIN_GAS_PRICE)

return transaction;
}

private getMetadataPropertiesAsObject(codeMetadata?: ICodeMetadata): {
Expand Down Expand Up @@ -177,16 +175,16 @@ export class SmartContract implements ISmartContract {

this.ensureHasAddress();

const config = new TransactionIntentsFactoryConfig(chainID.valueOf());
const scIntentFactory = new SmartContractTransactionIntentsFactory({
const config = new TransactionsFactoryConfig(chainID.valueOf());
const scDraftTransactionFactory = new SmartContractTransactionsFactory({
config: config,
abi: this.abi
});

const bytecode = Uint8Array.from(Buffer.from(code.toString(), 'hex'));
const metadataAsJson = this.getMetadataPropertiesAsObject(codeMetadata);

const intent = scIntentFactory.createTransactionIntentForUpgrade({
const draftTx = scDraftTransactionFactory.createTransactionForUpgrade({
sender: caller,
contract: this.getAddress(),
bytecode: bytecode,
Expand All @@ -198,15 +196,12 @@ export class SmartContract implements ISmartContract {
isPayableBySmartContract: metadataAsJson.payableBySc
})

return new Transaction({
sender: Address.fromBech32(intent.sender),
receiver: Address.fromBech32(intent.receiver),
value: value,
gasLimit: new BigNumber(intent.gasLimit).toNumber(),
gasPrice: gasPrice,
data: new TransactionPayload(Buffer.from(intent.data!)),
chainID: chainID
});
let transaction = Transaction.fromDraft(draftTx);
transaction.setChainID(chainID);
transaction.setValue(value ?? 0);
transaction.setGasPrice(gasPrice ?? TRANSACTION_MIN_GAS_PRICE)

return transaction;
}

/**
Expand All @@ -217,32 +212,29 @@ export class SmartContract implements ISmartContract {

this.ensureHasAddress();

const config = new TransactionIntentsFactoryConfig(chainID.valueOf());
const scIntentFactory = new SmartContractTransactionIntentsFactory({
const config = new TransactionsFactoryConfig(chainID.valueOf());
const scDraftTransactionFactory = new SmartContractTransactionsFactory({
config: config,
abi: this.abi
});

args = args || [];
value = value || 0;

const intent = scIntentFactory.createTransactionIntentForExecute({
const draftTx = scDraftTransactionFactory.createTransactionForExecute({
sender: caller,
contractAddress: receiver ? receiver : this.getAddress(),
functionName: func.toString(),
gasLimit: gasLimit.valueOf(),
args: args
})

return new Transaction({
sender: caller,
receiver: Address.fromBech32(intent.receiver),
value: value,
gasLimit: new BigNumber(intent.gasLimit).toNumber(),
gasPrice: gasPrice,
data: new TransactionPayload(Buffer.from(intent.data!)),
chainID: chainID
});
let transaction = Transaction.fromDraft(draftTx);
transaction.setChainID(chainID);
transaction.setValue(value);
transaction.setGasPrice(gasPrice ?? TRANSACTION_MIN_GAS_PRICE)

return transaction;
}

createQuery({ func, args, value, caller }: QueryArguments): Query {
Expand Down
19 changes: 19 additions & 0 deletions src/transaction.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { TestWallet, loadTestWallets } from "./testutils";
import { TokenTransfer } from "./tokenTransfer";
import { Transaction } from "./transaction";
import { TransactionPayload } from "./transactionPayload";
import { DraftTransaction } from "./draftTransaction";


describe("test transaction construction", async () => {
Expand All @@ -17,6 +18,24 @@ describe("test transaction construction", async () => {
wallets = await loadTestWallets();
});

it("create transaction from draft transaction", async () => {
const draftTransaction = new DraftTransaction({
sender: "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th",
receiver: "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx",
gasLimit: 56000,
value: "1000000000000000000",
data: Buffer.from("test")
});

const transaction = Transaction.fromDraft(draftTransaction);
assert.deepEqual(transaction.getSender(), Address.fromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"));
assert.deepEqual(transaction.getReceiver(), Address.fromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"));
assert.equal(transaction.getGasLimit().valueOf(), 56000);
assert.equal(transaction.getValue().toString(), "1000000000000000000");
assert.equal(transaction.getData().toString(), "test");
assert.equal(transaction.getChainID().valueOf(), "");
});

it("with no data, no value", async () => {
let transaction = new Transaction({
nonce: 89,
Expand Down
15 changes: 15 additions & 0 deletions src/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { ProtoSerializer } from "./proto";
import { Signature } from "./signature";
import { TransactionPayload } from "./transactionPayload";
import { guardNotEmpty } from "./utils";
import { DraftTransaction } from "./draftTransaction";

const createTransactionHasher = require("blake2b");
const TRANSACTION_HASH_LENGTH = 32;
Expand Down Expand Up @@ -152,6 +153,20 @@ export class Transaction {
this.hash = TransactionHash.empty();
}

/**
* Creates a new Transaction object from a DraftTransaction.
*/
static fromDraft(draft: DraftTransaction): Transaction {
return new Transaction({
sender: Address.fromBech32(draft.sender),
receiver: Address.fromBech32(draft.receiver),
gasLimit: new BigNumber(draft.gasLimit).toNumber(),
chainID: "",
value: draft.value ?? 0,
data: draft.data ? new TransactionPayload(Buffer.from(draft.data)) : new TransactionPayload()
})
}

getNonce(): INonce {
return this.nonce;
}
Expand Down
Loading

0 comments on commit b2c0025

Please sign in to comment.