Skip to content

Commit

Permalink
Merge pull request #346 from multiversx/feat/factories
Browse files Browse the repository at this point in the history
Merge feat/factories into feat/next
  • Loading branch information
popenta authored Oct 31, 2023
2 parents 1750529 + dd3271a commit b066716
Show file tree
Hide file tree
Showing 30 changed files with 2,891 additions and 79 deletions.
5 changes: 5 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,8 @@ export const ESDTNFT_TRANSFER_FUNCTION_NAME = "ESDTNFTTransfer";
export const MULTI_ESDTNFT_TRANSFER_FUNCTION_NAME = "MultiESDTNFTTransfer";
export const ESDT_TRANSFER_VALUE = "0";
export const ARGUMENTS_SEPARATOR = "@";
export const VM_TYPE_WASM_VM = new Uint8Array([0x05, 0x00]);
export const CONTRACT_DEPLOY_ADDRESS = "erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu";
export const DELEGATION_MANAGER_SC_ADDRESS = "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqylllslmq6y6";
export const DEFAULT_HRP = "erd";
export const ESDT_CONTRACT_ADDRESS = "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u";
23 changes: 23 additions & 0 deletions src/draftTransaction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { BigNumber } from "bignumber.js";

export class DraftTransaction {
public sender: string;
public receiver: string;
public gasLimit: BigNumber.Value;
public value: BigNumber.Value;
public data: Uint8Array;

public constructor(options: {
sender: string,
receiver: string,
gasLimit: BigNumber.Value,
value?: BigNumber.Value,
data?: Uint8Array
}) {
this.sender = options.sender;
this.receiver = options.receiver;
this.gasLimit = options.gasLimit;
this.value = options.value ?? 0;
this.data = options.data ?? new Uint8Array();
}
}
21 changes: 21 additions & 0 deletions src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -326,8 +326,29 @@ export class ErrGasLimitShouldBe0ForInnerTransaction extends Err {
}
}

/**
* Signals that the `isCompleted` property is missing on the transaction obect and is needed for the Transaction Watcher
*/
export class ErrIsCompletedFieldIsMissingOnTransaction extends Err {
public constructor() {
super("The transaction watcher requires the `isCompleted` property to be defined on the transaction object. Perhaps you've used the sdk-network-provider's `ProxyNetworkProvider.getTransaction()` and in that case you should also pass `withProcessStatus=true`.")
}
}

/**
* Signals that the provided token identifier is not valid
*/
export class ErrInvalidTokenIdentifier extends Err {
public constructor(message: string) {
super(message);
}
}

/**
* Signals a generic bad usage error
*/
export class ErrBadUsage extends Err {
public constructor(message: string) {
super(message);
}
}
17 changes: 17 additions & 0 deletions src/smartcontracts/codeMetadata.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { assert } from "chai";
import { CodeMetadata } from "./codeMetadata";

describe("test code metadata", function () {
it("should test code metadata from bytes", () => {
const bytes = new Uint8Array([1, 0]);
const codeMetadata = CodeMetadata.fromBytes(bytes);

assert.equal(codeMetadata.toString(), "0100");
assert.deepEqual(codeMetadata.toJSON(), {
upgradeable: true,
readable: false,
payable: false,
payableBySc: false
});
});
});
19 changes: 18 additions & 1 deletion src/smartcontracts/codeMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export class CodeMetadata {
private readable: boolean;
private payable: boolean;
private payableBySc: boolean;
private static readonly codeMetadataLength = 2;

/**
* Creates a metadata object. By default, set the `upgradeable` attribute, and uset all others.
Expand All @@ -22,6 +23,22 @@ export class CodeMetadata {
this.payableBySc = payableBySc
}

static fromBytes(bytes: Uint8Array): CodeMetadata {
if (bytes.length !== this.codeMetadataLength) {
return new CodeMetadata();
}

const byteZero = bytes[0];
const byteOne = bytes[1];

const upgradeable = (byteZero & ByteZero.Upgradeable) !== 0;
const readable = (byteZero & ByteZero.Readable) !== 0;
const payable = (byteOne & ByteOne.Payable) !== 0;
const payableBySc = (byteOne & ByteOne.PayableBySc) !== 0;

return new CodeMetadata(upgradeable, readable, payable, payableBySc);
}

/**
* Adjust the metadata (the `upgradeable` attribute), when preparing the deployment transaction.
*/
Expand Down Expand Up @@ -49,7 +66,7 @@ export class CodeMetadata {
togglePayableBySc(value: boolean) {
this.payableBySc = value;
}

/**
* Converts the metadata to the protocol-friendly representation.
*/
Expand Down
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
6 changes: 3 additions & 3 deletions src/smartcontracts/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export interface ISmartContract {
export interface DeployArguments {
code: ICode;
codeMetadata?: ICodeMetadata;
initArguments?: TypedValue[];
initArguments?: any[];
value?: ITransactionValue;
gasLimit: IGasLimit;
gasPrice?: IGasPrice;
Expand All @@ -42,7 +42,7 @@ export interface DeployArguments {
export interface UpgradeArguments {
code: ICode;
codeMetadata?: ICodeMetadata;
initArguments?: TypedValue[];
initArguments?: any[];
value?: ITransactionValue;
gasLimit: IGasLimit;
gasPrice?: IGasPrice;
Expand All @@ -52,7 +52,7 @@ export interface UpgradeArguments {

export interface CallArguments {
func: IContractFunction;
args?: TypedValue[];
args?: any[];
value?: ITransactionValue;
gasLimit: IGasLimit;
receiver?: IAddress;
Expand Down
39 changes: 39 additions & 0 deletions src/smartcontracts/smartContract.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,4 +125,43 @@ describe("test contract", () => {
assert.isTrue((await provider.getTransactionStatus(hashOne)).isExecuted());
assert.isTrue((await provider.getTransactionStatus(hashTwo)).isExecuted());
});

it("should upgrade", async () => {
setupUnitTestWatcherTimeouts();
let watcher = new TransactionWatcher(provider);

let contract = new SmartContract();
contract.setAddress(Address.fromBech32("erd1qqqqqqqqqqqqqpgq3ytm9m8dpeud35v3us20vsafp77smqghd8ss4jtm0q"))

let deployTransaction = contract.upgrade({
code: Code.fromBuffer(Buffer.from([1, 2, 3, 4])),
gasLimit: 1000000,
chainID: chainID,
caller: alice.address
});

provider.mockUpdateAccount(alice.address, account => {
account.nonce = 42;
});

await alice.sync(provider);
deployTransaction.setNonce(alice.account.nonce);

assert.equal(deployTransaction.getData().valueOf().toString(), "upgradeContract@01020304@0100");
assert.equal(deployTransaction.getGasLimit().valueOf(), 1000000);
assert.equal(deployTransaction.getNonce().valueOf(), 42);

// Sign the transaction
alice.signer.sign(deployTransaction);

// Now let's broadcast the deploy transaction, and wait for its execution.
let hash = await provider.sendTransaction(deployTransaction);

await Promise.all([
provider.mockTransactionTimeline(deployTransaction, [new Wait(40), new TransactionStatus("pending"), new Wait(40), new TransactionStatus("executed"), new MarkCompleted()]),
watcher.awaitCompleted(deployTransaction)
]);

assert.isTrue((await provider.getTransactionStatus(hash)).isExecuted());
});
});
Loading

0 comments on commit b066716

Please sign in to comment.