Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add watchGas CLI command #8

Open
wants to merge 9 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ See more in the [Anatomy of a Contract](https://docs.near.org/build/smart-contra

## Documentation

- [Learn how to use](https://github.com/near/create-near-app/tree/master/templates/contracts/ts)
- [Learn by example with AgorApp](https://agorapp.dev/catalog/all?difficulty=&chains=near)
- [Learn by example with NEAR Docs](https://docs.near.org/build/smart-contracts/quickstart)
- Check our [detailed examples and tutorials](https://docs.near.org/tutorials/welcome)
Expand Down
151 changes: 151 additions & 0 deletions benchmark/__tests__/test-collections-performance.ava.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import { Worker } from "near-workspaces";
import test from "ava";
import { logTotalGas, randomInt } from "./util.js";

const COLLECTION_SIZE = 20;

test.before(async (t) => {
// Init the worker and start a Sandbox server
const worker = await Worker.init();

// Prepare sandbox for tests, create accounts, deploy contracts, etx.
const root = worker.rootAccount;

// Deploy the test contracts.
const lookupMapContract = await root.devDeploy("build/lookup-map.wasm");
const lookupSetContract = await root.devDeploy("build/lookup-set.wasm");
const unorderedMapContract = await root.devDeploy("build/unordered-map.wasm");
const unorderedSetContract = await root.devDeploy("build/unordered-set.wasm");
const vectorContract = await root.devDeploy("build/vector.wasm");

// Test users
const ali = await root.createSubAccount("ali");

// Save state for test runs
t.context.worker = worker;
t.context.accounts = {
root,
lookupMapContract,
lookupSetContract,
unorderedMapContract,
unorderedSetContract,
vectorContract,
ali,
};
});

test.after.always(async (t) => {
await t.context.worker.tearDown().catch((error) => {
console.log("Failed to tear down the worker:", error);
});
});

test("JS lookup map contract operations", async (t) => {
const { ali, lookupMapContract } = t.context.accounts;

let rAdd;
for (let i = 0; i < COLLECTION_SIZE; i++) {
rAdd = await ali.callRaw(lookupMapContract, "addElement", { key: i, value: i });
}
t.is(rAdd.result.status.SuccessValue, "");
logTotalGas("Add element", rAdd, t);

const val = randomInt(COLLECTION_SIZE);
const rGet = await ali.callRaw(lookupMapContract, "getElement", { key: val });
t.is(JSON.parse(Buffer.from(rGet.result.status.SuccessValue, "base64")), val);
logTotalGas("Get element", rGet, t);

const rRem = await ali.callRaw(lookupMapContract, "removeElement", { key: randomInt(COLLECTION_SIZE) });
t.is(rRem.result.status.SuccessValue, "");
logTotalGas("Remove element", rRem, t);
});

test("JS lookup set contract operations", async (t) => {
const { ali, lookupSetContract } = t.context.accounts;

let rAdd;
for (let i = 0; i < COLLECTION_SIZE; i++) {
rAdd = await ali.callRaw(lookupSetContract, "addElement", { value: i });
}
t.is(rAdd.result.status.SuccessValue, "");
logTotalGas("Add element", rAdd, t);

const rGet = await ali.callRaw(lookupSetContract, "containsElement", { value: randomInt(COLLECTION_SIZE) });
t.is(JSON.parse(Buffer.from(rGet.result.status.SuccessValue, "base64")), true);
logTotalGas("Get element", rGet, t);

const rRem = await ali.callRaw(lookupSetContract, "removeElement", { value: randomInt(COLLECTION_SIZE) });
t.is(rRem.result.status.SuccessValue, "");
logTotalGas("Remove element", rRem, t);
});

test("JS unordered map contract operations", async (t) => {
const { ali, unorderedMapContract } = t.context.accounts;

let rAdd;
for (let i = 0; i < COLLECTION_SIZE; i++) {
rAdd = await ali.callRaw(unorderedMapContract, "addElement", { key: i, value: i });
}
t.is(rAdd.result.status.SuccessValue, "");
logTotalGas("Add element", rAdd, t);

const val = randomInt(COLLECTION_SIZE);
const rGet = await ali.callRaw(unorderedMapContract, "getElement", { key: val });
t.is(JSON.parse(Buffer.from(rGet.result.status.SuccessValue, "base64")), val);
logTotalGas("Get element", rGet, t);

const rIt = await ali.callRaw(unorderedMapContract, "iterate", {});
t.is(rIt.result.status.SuccessValue, "");
logTotalGas("Iterate collection", rIt, t);

const rRem = await ali.callRaw(unorderedMapContract, "removeElement", { key: randomInt(COLLECTION_SIZE) });
t.is(rRem.result.status.SuccessValue, "");
logTotalGas("Remove element", rRem, t);
});

test("JS unordered set contract operations", async (t) => {
const { ali, unorderedSetContract } = t.context.accounts;

let rAdd;
for (let i = 0; i < COLLECTION_SIZE; i++) {
rAdd = await ali.callRaw(unorderedSetContract, "addElement", { value: i });
}
t.is(rAdd.result.status.SuccessValue, "");
logTotalGas("Add element", rAdd, t);

const rGet = await ali.callRaw(unorderedSetContract, "containsElement", { value: randomInt(COLLECTION_SIZE) });
t.is(JSON.parse(Buffer.from(rGet.result.status.SuccessValue, "base64")), true);
logTotalGas ("Get element", rGet, t);

const rIt = await ali.callRaw(unorderedSetContract, "iterate", {});
t.is(rIt.result.status.SuccessValue, "");
logTotalGas("Iterate collection", rIt, t);

const rRem = await ali.callRaw(unorderedSetContract, "removeElement", { value: randomInt(COLLECTION_SIZE) });
t.is(rRem.result.status.SuccessValue, "");
logTotalGas("Remove element", rRem, t);
});

test("JS vector contract operations", async (t) => {
const { ali, vectorContract } = t.context.accounts;

let rAdd;
for (let i = 0; i < COLLECTION_SIZE; i++) {
rAdd = await ali.callRaw(vectorContract, "addElement", { value: i });
}
t.is(rAdd.result.status.SuccessValue, "");
logTotalGas("Add element", rAdd, t);

const val = randomInt(COLLECTION_SIZE);
const rGet = await ali.callRaw(vectorContract, "getElement", { index: val });
t.is(JSON.parse(Buffer.from(rGet.result.status.SuccessValue, "base64")), val);
logTotalGas("Get element", rGet, t);

const rIt = await ali.callRaw(vectorContract, "iterate", {});
t.is(rIt.result.status.SuccessValue, "");
logTotalGas("Iterate collection", rIt, t);

const rRem = await ali.callRaw(vectorContract, "removeElement", { index: randomInt(COLLECTION_SIZE) });
t.is(rRem.result.status.SuccessValue, "");
logTotalGas("Remove element", rRem, t);
});
17 changes: 17 additions & 0 deletions benchmark/__tests__/util.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// Functions consumed by the benchmark contracts tests

export function formatGas(gas) {
if (gas < 10 ** 12) {
let tGas = gas / 10 ** 12;
Expand Down Expand Up @@ -49,3 +51,18 @@ export function logGasDetail(r, t) {
)
);
}

export function logTotalGas(prefix = '', r, t) {
t.log(
prefix + ' - Total gas used: ',
formatGas(
r.result.transaction_outcome.outcome.gas_burnt +
r.result.receipts_outcome[0].outcome.gas_burnt +
(r.result.receipts_outcome[1]?.outcome.gas_burnt || 0)
)
);
}

export function randomInt(max) {
return Math.floor(Math.random() * max);
}
8 changes: 7 additions & 1 deletion benchmark/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,19 @@
"build:highlevel-collection": "near-sdk-js build src/highlevel-collection.js build/highlevel-collection.wasm",
"build:expensive-calc": "near-sdk-js build src/expensive-calc.js build/expensive-calc.wasm",
"build:deploy-contract": "near-sdk-js build src/deploy-contract.js build/deploy-contract.wasm",
"build:lookup-map": "near-sdk-js build src/lookup-map.js build/lookup-map.wasm",
"build:lookup-set": "near-sdk-js build src/lookup-set.js build/lookup-set.wasm",
"build:unordered-map": "near-sdk-js build src/unordered-map.js build/unordered-map.wasm",
"build:unordered-set": "near-sdk-js build src/unordered-set.js build/unordered-set.wasm",
"build:vector": "near-sdk-js build src/vector.js build/vector.wasm",
"test": "ava",
"test:lowlevel-minimal": "ava __tests__/test-lowlevel-minimal.ava.js",
"test:highlevel-minimal": "ava __tests__/test-highlevel-minimal.ava.js",
"test:lowlevel-api": "ava __tests__/test-lowlevel-api.ava.js",
"test:highlevel-collection": "ava __tests__/test-highlevel-collection.ava.js",
"test:expensive-calc": "ava __tests__/test-expensive-calc.ava.js",
"test:deploy-contract": "ava __tests__/test-deploy-contract.ava.js"
"test:deploy-contract": "ava __tests__/test-deploy-contract.ava.js",
"test:collections": "ava __tests__/test-collections-performance.ava.js"
},
"author": "Near Inc <[email protected]>",
"license": "Apache-2.0",
Expand Down
5 changes: 5 additions & 0 deletions benchmark/src/deploy-contract.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { near } from "near-sdk-js";

/**
* Used for contract deployment. More information for that
* can be found in the README.md
* - Deploy and cross contract call
*/
export function deploy_contract() {
let promiseId = near.promiseBatchCreate("a.caller.test.near");
near.promiseBatchActionCreateAccount(promiseId);
Expand Down
5 changes: 5 additions & 0 deletions benchmark/src/expensive-calc.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { NearBindgen, call, near } from "near-sdk-js";

/**
* ExpensiveCalc is connected to the expensive contract. More information for that
* can be found in the README.md
* - Computational expensive contract
*/
@NearBindgen({})
export class ExpensiveCalc {
@call({})
Expand Down
4 changes: 4 additions & 0 deletions benchmark/src/highlevel-collection.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { NearBindgen, call, UnorderedMap } from "near-sdk-js";

/**
* More information for that can be found in the README.md
* - Highlevel collection
*/
@NearBindgen({})
export class HighlevelCollection {
constructor() {
Expand Down
4 changes: 4 additions & 0 deletions benchmark/src/highlevel-minimal.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { NearBindgen, call } from "near-sdk-js";

/**
* More information for that can be found in the README.md
* - A highlevel minimal contract (using nearbindgen)
*/
@NearBindgen({})
export class HighlevelMinimal {
@call({})
Expand Down
23 changes: 23 additions & 0 deletions benchmark/src/lookup-map.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { NearBindgen, call, LookupMap, view } from "near-sdk-js";

@NearBindgen({})
export class LookupMapContract {
constructor() {
this.lookupMap = new LookupMap("LM");
}

@call({})
addElement({ key, value }) {
this.lookupMap.set(key, value);
}

@call({})
removeElement({ key }) {
this.lookupMap.remove(key);
}

@view({})
getElement({ key }) {
return this.lookupMap.get(key);
}
}
23 changes: 23 additions & 0 deletions benchmark/src/lookup-set.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { NearBindgen, call, LookupSet, view } from "near-sdk-js";

@NearBindgen({})
export class LookupSetContract {
constructor() {
this.lookupSet = new LookupSet("LS");
}

@call({})
addElement({ value }) {
this.lookupSet.set(value);
}

@call({})
removeElement({ value }) {
this.lookupSet.remove(value);
}

@view({})
containsElement({ value }) {
return this.lookupSet.contains(value);
}
}
8 changes: 8 additions & 0 deletions benchmark/src/lowlevel-api.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
import { near } from "near-sdk-js";

/**
* Helper method for the low level api. More information for that can be found in the README.md
* - Low level API
*/
export function lowlevel_storage_write() {
let data = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
near.storageWriteRaw(data, data);
}

/**
* Helper method for the low level api. More information for that can be found in the README.md
* - Low level API
*/
export function lowlevel_storage_write_many() {
let data = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
near.storageWriteRaw(data, data);
Expand Down
4 changes: 4 additions & 0 deletions benchmark/src/lowlevel-minimal.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import { near } from "near-sdk-js";

/**
* More information for that can be found in the README.md
* - A minimal contract
*/
export function empty() {}
31 changes: 31 additions & 0 deletions benchmark/src/unordered-map.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { NearBindgen, call, UnorderedMap, view } from "near-sdk-js";

@NearBindgen({})
export class UnorderedMapContract {
constructor() {
this.unorderedMap = new UnorderedMap("UM");
}

@call({})
addElement({ key, value }) {
this.unorderedMap.set(key, value);
}

@call({})
removeElement({ key }) {
this.unorderedMap.remove(key);
}

@view({})
getElement({ key }) {
return this.unorderedMap.get(key);
}

@view({})
iterate() {
const size = this.unorderedMap.length;
for (let i = 0; i < size; i++) {
this.unorderedMap.get(i);
}
}
}
31 changes: 31 additions & 0 deletions benchmark/src/unordered-set.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { NearBindgen, call, UnorderedSet, view } from "near-sdk-js";

@NearBindgen({})
export class UnorderedSetContract {
constructor() {
this.unorderedSet = new UnorderedSet("US");
}

@call({})
addElement({ value }) {
this.unorderedSet.set(value);
}

@call({})
removeElement({ value }) {
this.unorderedSet.remove(value);
}

@view({})
containsElement({ value }) {
return this.unorderedSet.contains(value);
}

@view({})
iterate() {
const size = this.unorderedSet.length;
for (let i = 0; i < size; i++) {
this.unorderedSet.contains(i);
}
}
}
Loading
Loading