Skip to content

Commit

Permalink
Merge pull request #327 from multiversx/sc-intents-factories
Browse files Browse the repository at this point in the history
Smart Contract intents factories
  • Loading branch information
popenta authored Sep 8, 2023
2 parents 2c93aef + 08dd376 commit 3807fa5
Show file tree
Hide file tree
Showing 10 changed files with 485 additions and 5 deletions.
3 changes: 3 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@ 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"

62 changes: 62 additions & 0 deletions src/testdata/adder.abi.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{
"buildInfo": {
"rustc": {
"version": "1.71.0-nightly",
"commitHash": "7f94b314cead7059a71a265a8b64905ef2511796",
"commitDate": "2023-04-23",
"channel": "Nightly",
"short": "rustc 1.71.0-nightly (7f94b314c 2023-04-23)"
},
"contractCrate": {
"name": "adder",
"version": "0.0.0"
},
"framework": {
"name": "multiversx-sc",
"version": "0.41.3"
}
},
"docs": [
"One of the simplest smart contracts possible,",
"it holds a single variable in storage, which anyone can increment."
],
"name": "Adder",
"constructor": {
"inputs": [
{
"name": "initial_value",
"type": "BigUint"
}
],
"outputs": []
},
"endpoints": [
{
"name": "getSum",
"mutability": "readonly",
"inputs": [],
"outputs": [
{
"type": "BigUint"
}
]
},
{
"docs": [
"Add desired amount to the storage variable."
],
"name": "add",
"mutability": "mutable",
"inputs": [
{
"name": "value",
"type": "BigUint"
}
],
"outputs": []
}
],
"events": [],
"hasCallback": false,
"types": {}
}
Binary file added src/testdata/adder.wasm
Binary file not shown.
5 changes: 1 addition & 4 deletions src/tokenOperations/codec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,7 @@ export function bigIntToHex(value: BigNumber.Value): string {
return contractsCodecUtils.getHexMagnitudeOfBigInt(value);
}

export function utf8ToHex(value: string) {
const hex = Buffer.from(value).toString("hex");
return codecUtils.zeroPadStringIfOddLength(hex);
}
export { utf8ToHex } from "../utils.codec";

export function bufferToHex(value: Buffer) {
const hex = value.toString("hex");
Expand Down
23 changes: 23 additions & 0 deletions src/transactionIntent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { BigNumber } from "bignumber.js";

export class TransactionIntent {
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;
this.data = options.data;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
import { assert, expect } from "chai";
import { SmartContractTransactionIntentsFactory } from "./smartContractTransactionIntentsFactory";
import { Address } from "../address";
import { Code } from "../smartcontracts/code";
import { AbiRegistry } from "../smartcontracts/typesystem/abiRegistry";
import { U32Value } from "../smartcontracts";
import { CONTRACT_DEPLOY_ADDRESS } from "../constants";
import { loadContractCode, loadAbiRegistry } from "../testutils/utils";
import { Err } from "../errors";

describe("test smart contract intents factory", function () {
let factory: SmartContractTransactionIntentsFactory;
let abiAwareFactory: SmartContractTransactionIntentsFactory;
let adderByteCode: Code;
let abiRegistry: AbiRegistry;

before(async function () {
factory = new SmartContractTransactionIntentsFactory({
config:
{
chainID: "D",
minGasLimit: 50000,
gasLimitPerByte: 1500
}
});

adderByteCode = await loadContractCode("src/testdata/adder.wasm");
abiRegistry = await loadAbiRegistry("src/testdata/adder.abi.json");

abiAwareFactory = new SmartContractTransactionIntentsFactory({
config:
{
chainID: "D",
minGasLimit: 50000,
gasLimitPerByte: 1500
},
abi: abiRegistry
},
);
});

it("should throw error when args are not of type 'TypedValue'", async function () {
const sender = Address.fromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th");
const gasLimit = 6000000;
const args = [0];

assert.throws(() => factory.createTransactionIntentForDeploy({
sender: sender,
bytecode: adderByteCode.valueOf(),
gasLimit: gasLimit,
args: args
}), Err, "Can't convert args to TypedValues");
});

it("should build intent for deploy", async function () {
const sender = Address.fromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th");
const gasLimit = 6000000;
const args = [new U32Value(0)];

const deployIntent = factory.createTransactionIntentForDeploy({
sender: sender,
bytecode: adderByteCode.valueOf(),
gasLimit: gasLimit,
args: args
});
const abiDeployIntent = abiAwareFactory.createTransactionIntentForDeploy({
sender: sender,
bytecode: adderByteCode.valueOf(),
gasLimit: gasLimit,
args: args
});

assert.equal(deployIntent.sender, "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th");
assert.equal(deployIntent.receiver, CONTRACT_DEPLOY_ADDRESS);
assert.isDefined(deployIntent.data);
expect(deployIntent.data!.length).to.be.greaterThan(0);

const expectedGasLimit = 6000000 + 50000 + 1500 * deployIntent.data!.length;
assert.equal(deployIntent.gasLimit.valueOf(), expectedGasLimit);
assert.equal(deployIntent.value, 0);

assert.deepEqual(deployIntent, abiDeployIntent);
});

it("should build intent for execute", async function () {
const sender = Address.fromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th");
const contract = Address.fromBech32("erd1qqqqqqqqqqqqqpgqhy6nl6zq07rnzry8uyh6rtyq0uzgtk3e69fqgtz9l4");
const func = "add";
const gasLimit = 6000000;
const args = [new U32Value(7)];

const deployIntent = factory.createTransactionIntentForExecute({
sender: sender,
contractAddress: contract,
func: func,
gasLimit: gasLimit,
args: args
});
const abiDeployIntent = abiAwareFactory.createTransactionIntentForExecute({
sender: sender,
contractAddress: contract,
func: func,
gasLimit: gasLimit,
args: args
});

assert.equal(deployIntent.sender, "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th");
assert.equal(deployIntent.receiver, "erd1qqqqqqqqqqqqqpgqhy6nl6zq07rnzry8uyh6rtyq0uzgtk3e69fqgtz9l4");

assert.isDefined(deployIntent.data);
assert.deepEqual(deployIntent.data, Buffer.from("add@07"));

assert.equal(deployIntent.gasLimit.valueOf(), 6059000);
assert.equal(deployIntent.value, 0);

assert.deepEqual(deployIntent, abiDeployIntent);
});

it("should build intent for upgrade", async function () {
const sender = Address.fromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th");
const contract = Address.fromBech32("erd1qqqqqqqqqqqqqpgqhy6nl6zq07rnzry8uyh6rtyq0uzgtk3e69fqgtz9l4");
const gasLimit = 6000000;
const args = [new U32Value(0)];

const deployIntent = factory.createTransactionIntentForUpgrade({
sender: sender,
contract: contract,
bytecode: adderByteCode.valueOf(),
gasLimit: gasLimit,
args: args
});
const abiDeployIntent = abiAwareFactory.createTransactionIntentForUpgrade({
sender: sender,
contract: contract,
bytecode: adderByteCode.valueOf(),
gasLimit: gasLimit,
args: args
});

assert.equal(deployIntent.sender, "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th");
assert.equal(deployIntent.receiver, "erd1qqqqqqqqqqqqqpgqhy6nl6zq07rnzry8uyh6rtyq0uzgtk3e69fqgtz9l4");
assert.isDefined(deployIntent.data);
assert(checkIfByteArrayStartsWith(deployIntent.data!, "upgradeContract@"));

const expectedGasLimit = 6000000 + 50000 + 1500 * deployIntent.data!.length;
assert.equal(deployIntent.gasLimit.valueOf(), expectedGasLimit);
assert.equal(deployIntent.value, 0);

assert.deepEqual(deployIntent, abiDeployIntent);
});

function checkIfByteArrayStartsWith(array: Uint8Array, sequence: string) {
const sequenceBytes = Buffer.from(sequence);

for (let i = 0; i < sequenceBytes.length; i++) {
if (sequenceBytes[i] !== array[i]) {
return false;
}
}
return true;
}
});
Loading

0 comments on commit 3807fa5

Please sign in to comment.