From e90d9bf9330d0a17cf6c0c215981f377ddadd740 Mon Sep 17 00:00:00 2001 From: Jimmy Nsenga <8685602+nsejim@users.noreply.github.com> Date: Tue, 22 Nov 2022 19:03:13 +0100 Subject: [PATCH] Example of programmatic update of JS Smart Contract (#287) * Example of programmatic update of JS Smart Contract Co-authored-by: Serhii Volovyk --- .github/workflows/examples.yml | 2 + README.md | 1 + .../__tests__/test-programmatic-update.ava.js | 49 +++++++++++++++++++ examples/package.json | 2 + examples/src/programmatic-update-after.ts | 30 ++++++++++++ examples/src/programmatic-update-before.ts | 30 ++++++++++++ 6 files changed, 114 insertions(+) create mode 100644 examples/__tests__/test-programmatic-update.ava.js create mode 100644 examples/src/programmatic-update-after.ts create mode 100644 examples/src/programmatic-update-before.ts diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index 9f80f0bf9..2f1b61d99 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -42,3 +42,5 @@ jobs: run: cd examples && yarn build:parking-lot && yarn test:parking-lot - name: standard nft run: cd examples && yarn build-nft && yarn test:nft + - name: programmatic-update + run: cd examples && yarn build:programmatic-update && yarn test:programmatic-update diff --git a/README.md b/README.md index 7463d4fb0..c17bfb38a 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ There are a couple of contract examples in the project: - [Non fungible token receiver contract](https://github.com/near/near-sdk-js/tree/develop/examples/src/non-fungible-token-receiver.js) - [Status message board](https://github.com/near/near-sdk-js/tree/develop/examples/src/status-message.js) - [Status message board with unique messages](https://github.com/near/near-sdk-js/tree/develop/examples/src/status-message-collections.js) +- [Programmatic Update After Locking The Contract](https://github.com/near/near-sdk-js/tree/develop/examples/src/programmatic-update.js) To build all examples, run `yarn build` in `examples/`. To test all examples, run `yarn test`. You can also build and test one specific example with `yarn build:` and `yarn test:`, see `examples/package.json`. diff --git a/examples/__tests__/test-programmatic-update.ava.js b/examples/__tests__/test-programmatic-update.ava.js new file mode 100644 index 000000000..84a48b0c9 --- /dev/null +++ b/examples/__tests__/test-programmatic-update.ava.js @@ -0,0 +1,49 @@ +import { Worker } from "near-workspaces"; +import test from "ava"; +import * as fs from "fs"; +import * as path from "path"; + +test.beforeEach(async (t) => { + const worker = await Worker.init(); + const root = worker.rootAccount; + + const ali = await root.createSubAccount("ali"); + + const contract = await root.devDeploy( + "build/programmatic-update-before.wasm" + ); + + await contract.call(contract, "init", { manager: ali.accountId }); + + t.context.worker = worker; + t.context.accounts = { root, contract, ali }; +}); + +test.afterEach.always(async (t) => { + await t.context.worker.tearDown().catch((error) => { + console.log("Failed to tear down the worker:", error); + }); +}); + +test("the contract can be programmatically updated", async (t) => { + const { ali, contract } = t.context.accounts; + + // ASSERT BEFORE CODE UPDATE + const codeBefore = await contract.viewCodeRaw(); + const beforeDefaultGreeting = await contract.view("get_greeting", {}); + t.is(beforeDefaultGreeting, "Hello"); + + // ACT (UPDATE CODE) + const code = fs.readFileSync( + path.resolve("./build/programmatic-update-after.wasm") + ); + await ali.call(contract, "updateContract", code, { + gas: "300" + "0".repeat(12), // 300 Tgas + }); + + // ASSERT AFTER CODE UPDATE + const codeAfter = await contract.viewCodeRaw(); + const afterDefaultGreeting = await contract.view("view_greeting", {}); + t.not(codeBefore, codeAfter, "code should be different after update"); + t.is(afterDefaultGreeting, "Hi"); +}); diff --git a/examples/package.json b/examples/package.json index 32948a4b6..a43f10d97 100644 --- a/examples/package.json +++ b/examples/package.json @@ -18,6 +18,7 @@ "build:non-fungible-token": "near-sdk-js build src/non-fungible-token-receiver.js build/non-fungible-token-receiver.wasm && near-sdk-js build src/non-fungible-token.js build/non-fungible-token.wasm", "build:status-message-collections": "near-sdk-js build src/status-message-collections.js build/status-message-collections.wasm", "build:parking-lot": "near-sdk-js build src/parking-lot.ts build/parking-lot.wasm", + "build:programmatic-update": "near-sdk-js build src/programmatic-update-before.ts build/programmatic-update-before.wasm && near-sdk-js build src/programmatic-update-after.ts build/programmatic-update-after.wasm", "build:nested-collections": "near-sdk-js build src/nested-collections.ts build/nested-collections.wasm", "build-nft": "run-s build:nft-*", "build:nft-contract": "near-sdk-js build src/standard-nft/my-nft.ts build/my-nft.wasm", @@ -36,6 +37,7 @@ "test:non-fungible-token": "ava __tests__/test-non-fungible-token.ava.js", "test:status-message-collections": "ava __tests__/test-status-message-collections.ava.js", "test:parking-lot": "ava __tests__/test-parking-lot.ava.js", + "test:programmatic-update": "ava __tests__/test-programmatic-update.ava.js", "test:nested-collections": "ava __tests__/test-nested-collections.ava.js" }, "author": "Near Inc ", diff --git a/examples/src/programmatic-update-after.ts b/examples/src/programmatic-update-after.ts new file mode 100644 index 000000000..9fa00e7a0 --- /dev/null +++ b/examples/src/programmatic-update-after.ts @@ -0,0 +1,30 @@ +import { NearBindgen, near, initialize, assert, view } from "near-sdk-js"; + +@NearBindgen({ requireInit: true }) +export class ProgrammaticUpdateAfter { + greeting = "Hello"; + + @initialize({ privateFunction: true }) + init({ manager }: { manager: string }) { + near.log(`Setting manager to be ${manager}`); + near.storageWrite("MANAGER", manager); + } + + @view({}) // Method renamed and return "Hi" when greeting is "Hello" + view_greeting(): string { + return this.greeting.replace("Hello", "Hi"); + } +} + +export function updateContract() { + const manager = near.storageRead("MANAGER"); + assert( + near.predecessorAccountId() === manager, + "Only the manager can update the code" + ); + + const promiseId = near.promiseBatchCreate(near.currentAccountId()); + near.promiseBatchActionDeployContract(promiseId, near.input()); + + return near.promiseReturn(promiseId); +} diff --git a/examples/src/programmatic-update-before.ts b/examples/src/programmatic-update-before.ts new file mode 100644 index 000000000..cae836077 --- /dev/null +++ b/examples/src/programmatic-update-before.ts @@ -0,0 +1,30 @@ +import { NearBindgen, near, initialize, assert, view } from "near-sdk-js"; + +@NearBindgen({ requireInit: true }) +export class ProgrammaticUpdateBefore { + greeting = "Hello"; + + @initialize({ privateFunction: true }) + init({ manager }: { manager: string }) { + near.log(`Setting manager to be ${manager}`); + near.storageWrite("MANAGER", manager); + } + + @view({}) // This method will be renamed after update and will return "Hi" if greeting is "Hello" + get_greeting(): string { + return this.greeting; + } +} + +export function updateContract() { + const manager = near.storageRead("MANAGER"); + assert( + near.predecessorAccountId() === manager, + "Only the manager can update the code" + ); + + const promiseId = near.promiseBatchCreate(near.currentAccountId()); + near.promiseBatchActionDeployContract(promiseId, near.input()); + + return near.promiseReturn(promiseId); +}