Skip to content

Commit dd19a98

Browse files
authored
Merge pull request #1073 from near/ben/js-quickstart
Javascript Enclave Quickstart Guide
2 parents fe79c26 + 66c7ee9 commit dd19a98

File tree

2 files changed

+350
-6
lines changed

2 files changed

+350
-6
lines changed
Lines changed: 335 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
---
2+
id: enclave-quickstart
3+
title: JavaScript Enclave
4+
sidebar_label: Enclave Quickstart
5+
---
6+
7+
The NEAR platform has historically supported writing contracts in Rust and AssemblyScript. This document aims to introduce developers to a new way of writing smart contracts by using JavaScript.
8+
9+
JavaScript is a widely-used programming language that is most well known for its Webpage scripting usages. See the [official JavaScript documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript) for more details.
10+
11+
:::warning Heads up
12+
JavaScript smart contract development is not recommended for financial use cases as it is still very new to the NEAR ecosystem.
13+
:::
14+
15+
## Overview {#overview}
16+
17+
The JavaScript Enclave, or JavaScript Virtual Machine (jsvm) provides an isolated environment where users can learn the basics of how to write smart contracts on NEAR. This isolated environment is run on a virtual machine, similar to how [Aurora](https://doc.aurora.dev/getting-started/aurora-engine) operates. Standard smart contracts that are built using Rust or AssemblyScript compile to [WebAssembly](https://webassembly.org/) or simply WASM. With the jsvm, contracts are encoded to base64 and are deployed to a virtual machine.
18+
19+
There are several pros and cons when comparing the enclave approach to the regular WASM approach. The key differences are outlined below.
20+
21+
| Areas | WebAssembly | Enclave |
22+
|---------------------------|--------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------|
23+
| Can interact with any smart contract on NEAR |||
24+
| Synchronous Cross-Contract Calls |||
25+
| Standards Support ||Not in v1.0|
26+
| Function Call Access Key Support ||Not in v1.0|
27+
28+
The JavaScript Enclave is a very powerful tool to help you kickstart your smart contract programming journey. Writing contracts in JavaScript is much easier than learning Rust, and thanks to the jsvm's ability to handle cross-contract calls synchronously can greatly help with people's understanding of how contracts can work.
29+
30+
## Quickstart {#quickstart}
31+
32+
In this quick-start guide, you'll learn the basics of setting up a new JavaScript smart contract on the enclave that stores and retrieves a greeting message. You'll then create a simple web-based frontend that displays the greeting and allows you to change it.
33+
34+
### Prerequisites
35+
36+
In order to successfully complete this quickstart guide, you'll need to have a few things installed:
37+
- [Node.js](https://nodejs.org/en/about/) and [npm](https://www.npmjs.com/):
38+
```bash
39+
curl -sL https://deb.nodesource.com/setup_17.x | sudo -E bash -
40+
sudo apt install build-essential nodejs
41+
PATH="$PATH"
42+
```
43+
Ensure that they are both installed by running a version check:
44+
```
45+
node -v
46+
npm -v
47+
```
48+
49+
It's important to have the **newest** version of the [NEAR-CLI](https://docs.near.org/docs/tools/near-cli) installed such that you can make use of the JavaScript features. To install or update, run:
50+
51+
```
52+
npm install -g near-cli
53+
```
54+
55+
### Creating a project
56+
57+
Now that you have Node, npm, and the NEAR-CLI installed, create a new directory to initialize the project in.
58+
59+
```bash
60+
mkdir javascript-enclave-quickstart && cd javascript-enclave-quickstart
61+
```
62+
63+
Once you're in the directory, initialize a new default npm project.
64+
65+
```bash
66+
npm init -y
67+
```
68+
69+
This will create a `package.json` file with contents similar to:
70+
71+
```js
72+
{
73+
"name": "javascript-enclave-quickstart",
74+
"version": "1.0.0",
75+
"description": "",
76+
"main": "index.js",
77+
"scripts": {
78+
"test": "echo \"Error: no test specified\" && exit 1"
79+
},
80+
"keywords": [],
81+
"author": "",
82+
"license": "ISC"
83+
}
84+
```
85+
86+
The next step is to install the `near-sdk-js` package and add it as a dependency for your project. This will allow for convenient ways of building and interacting with your smart contract.
87+
88+
```bash
89+
npm install --save near-sdk-js
90+
```
91+
92+
Once the package has successfully been installed, you can create a convenient script in your `package.json` for building your contract. Add the following line to your `package.json` under the `scripts` section:
93+
94+
```diff
95+
{
96+
"name": "javascript-enclave-quickstart",
97+
"version": "1.0.0",
98+
"description": "",
99+
"main": "index.js",
100+
"scripts": {
101+
+ "build": "near-sdk build",
102+
"test": "echo \"Error: no test specified\" && exit 1"
103+
},
104+
"keywords": [],
105+
"author": "",
106+
"license": "ISC",
107+
"dependencies": {
108+
"near-sdk-js": "^0.1.1"
109+
}
110+
}
111+
```
112+
113+
> **Note:** This is optional and you can simply run `near-sdk build` instead.
114+
115+
You'll now want to create the `src` directory and initialize a new JS file `index.js` where your contract logic will live.
116+
117+
```bash
118+
mkdir src && cd src && touch index.js && cd ..
119+
```
120+
121+
The last step is to create a new file called `babel.config.json` which allows you to configure how the contract is built. In the project's root folder, create a new file and add the following content.
122+
123+
```bash
124+
touch babel.config.json
125+
```
126+
```js
127+
{
128+
"plugins": [
129+
"near-sdk-js/src/build-tools/near-bindgen-exporter",
130+
["@babel/plugin-proposal-decorators", {"version": "legacy"}]
131+
]
132+
}
133+
```
134+
135+
At this point, you just need to install all the packages and you will be ready to build your smart contract!
136+
137+
```bash
138+
npm install
139+
```
140+
141+
Your file structure should now look as follows:
142+
143+
```
144+
javascript-enclave-quickstart
145+
├── node_modules
146+
├── package-lock.json
147+
├── babel.config.json
148+
├── package.json
149+
└── src
150+
└── index.js
151+
```
152+
153+
### Writing your first contract
154+
155+
Now that you have the basic structure outlined for your project, it's time to start writing your first contract. You'll create a simple contract for setting and getting a greeting message on-chain.
156+
157+
The contract presents 2 methods: `set_greeting` and `get_greeting`:
158+
- `set_greeting` stores a String in the contract's parameter message,
159+
- while `get_greeting` retrieves it.
160+
161+
By default, the contract returns the message "Hello".
162+
163+
Start by opening the `src/index.js` file as this is where your logic will go. You'll then want to add some imports that will help when writing the contract:
164+
165+
```js
166+
import {NearContract, NearBindgen, call, view, near} from 'near-sdk-js'
167+
```
168+
169+
Let's break down these imports to help you understand why they're necessary.
170+
171+
- `NearContract`: allows the contract to inherit functionalities for changing and reading the contract's state. The state can be thought of as the data stored on-chain.
172+
- `NearBindgen`: allows your contract to compile down to something that is NEAR compatible.
173+
- `call`, `view`: allows your methods to be view-only functions or mutable (change) functions.
174+
- `near`: allows you to access important information within your functions such as the signer, predecessor, attached deposit, etc.
175+
176+
Now that you've imported everything from the SDK, create a new class that extends the `NearContract`. This class will contain the core logic of your smart contract. You can also use this opportunity to create a default message variable. Below the `import` add:
177+
178+
```js
179+
// Define the default message
180+
const DEFAULT_MESSAGE = "Hello";
181+
182+
@NearBindgen
183+
class StatusMessage extends NearContract {
184+
// Define the constructor, which initializes the contract with a default message
185+
constructor() {
186+
// Used to give access to methods and properties of the parent or sibling class
187+
super()
188+
// Default the status records to
189+
this.message = DEFAULT_MESSAGE
190+
}
191+
}
192+
```
193+
Running the constructor will default the contract's `message` state variable with the `DEFAULT_MESSAGE`. Since there's no way to get the current greeting, within the class you can add the following `view` function:
194+
195+
```js
196+
// Public method - returns the greeting saved, defaulting to DEFAULT_MESSAGE
197+
@view
198+
get_greeting() {
199+
env.log(`current greeting is ${this.message}`)
200+
return this.message
201+
}
202+
```
203+
You now have a way to initialize the contract and get the current greeting. The next step is to create a setter which will take a message as a parameter and set the contract's `message` variable equal to the passed in string.
204+
205+
```js
206+
// Public method - accepts a greeting, such as "howdy", and records it
207+
@call
208+
set_greeting(message) {
209+
let account_id = near.signerAccountId()
210+
env.log(`Saving greeting ${message}`)
211+
this.message = message
212+
}
213+
```
214+
215+
At this point, your contract is finished and should look as follows:
216+
217+
```js
218+
import {NearContract, NearBindgen, call, view, near} from 'near-sdk-js'
219+
220+
// Define the default message
221+
const DEFAULT_MESSAGE = "Hello";
222+
223+
@NearBindgen
224+
class StatusMessage extends NearContract {
225+
// Define the constructor, which initializes the contract with a default message
226+
constructor() {
227+
// Used to give access to methods and properties of the parent or sibling class
228+
super()
229+
// Default the status records to
230+
this.message = DEFAULT_MESSAGE
231+
}
232+
233+
// Public method - returns the greeting saved, defaulting to DEFAULT_MESSAGE
234+
@view
235+
get_greeting() {
236+
env.log(`current greeting is ${this.message}`)
237+
return this.message
238+
}
239+
240+
// Public method - accepts a greeting, such as "howdy", and records it
241+
@call
242+
set_greeting(message) {
243+
let account_id = near.signerAccountId()
244+
env.log(`Saving greeting ${message}`)
245+
this.message = message
246+
}
247+
}
248+
```
249+
:::note Heads up
250+
You might see a warning from your JavaScript linter because the NEAR SDK uses a custom decorator which is an experimental feature. This will be addressed in a future release of the JS SDK. It can be ignored for now.
251+
:::
252+
253+
254+
### Building
255+
256+
Now that your contract is finished, it's time to build and deploy it. Run the following command to build your JS code and get the `build/contract.base64` contract file.
257+
258+
```
259+
yarn build
260+
```
261+
262+
You can now deploy the contract and start interacting with it!
263+
264+
### Deploying
265+
266+
Start by deploying the contract using the following command. This will create a [dev account](/docs/concepts/account#dev-accounts) and deploy the contract to it.
267+
268+
```
269+
near js dev-deploy --base64File build/contract.base64 --deposit 0.1
270+
```
271+
Alternatively, if you have an account already, you can specify the account you want to deploy to:
272+
273+
```
274+
near js deploy --accountId <YOUR_ACCOUNT_ID> --base64File build/contract.base64 --deposit 0.1
275+
```
276+
277+
> **Note**: When deploying the smart contract using the enclave approach, it will live on top of a virtual machine smart contract that is deployed to `jsvm.testnet`. This will act as a "middleman" and to interact with your contract, you'll need to go through the `jsvm` contract.
278+
279+
### Interacting
280+
281+
The return from the deploy should include an account address in the first line after Account id:
282+
283+
```bash
284+
Starting deployment. Account id: <someAccountID>, node: https://rpc.testnet.near.org, helper: https://helper.testnet.near.org, file: build/contract.base64, JSVM: jsvm.testnet
285+
Transaction Id EvSt3A4auSkBWKUvRo2JtbP7UdwimLmRh7fyn89RZ1d4
286+
To see the transaction in the transaction explorer, please open this url in your browser
287+
https://explorer.testnet.near.org/transactions/EvSt3A4auSkBWKUvRo2JtbP7UdwimLmRh7fyn89RZ1d4
288+
```
289+
Now that your contract is deployed, you can start interacting with it. The first thing to do is initialize the contract. For simplicity, export the account ID that the contract is deployed to into an environment variable.
290+
291+
```bash
292+
export JS_CONTRACT="dev-1653584404106-63749024395789"
293+
```
294+
You'll now initialize the contract such that the default greeting is set. If you try to interact with the contract before it's initialized, you'll get an error saying "Contract state is empty".
295+
296+
```bash
297+
near js call $JS_CONTRACT init --accountId $JS_CONTRACT --deposit 0.1
298+
```
299+
300+
Once the contract is initialized, you can view the current greeting by performing a `view` call:
301+
302+
```bash
303+
near js view $JS_CONTRACT get_greeting
304+
```
305+
306+
This should return something similar to:
307+
308+
```bash
309+
View call in JSVM[jsvm.testnet]: dev-1653584404106-63749024395789.get_greeting()
310+
Log [jsvm.testnet]: current greeting is Hello
311+
'Hello'
312+
```
313+
314+
Now that you know how to get the current greeting, you can go ahead and call the setter `set_greeting`:
315+
316+
```bash
317+
near js call $JS_CONTRACT set_greeting '["GO TEAM!"]' --accountId $JS_CONTRACT --deposit 0.1
318+
```
319+
320+
This should return something similar to:
321+
322+
```bash
323+
Scheduling a call in JSVM[jsvm.testnet]: dev-1653584404106-63749024395789.set_greeting(["GO TEAM!"]) with attached 0.1 NEAR
324+
Receipts: 8w9tNKgqtAnJd9UW5WMCFuGsTXHo83vvPHD5Ph36yWJM, E9CJED2cpLC7uaTb6s67i3KKbinDzcjUQXspK7Jj7CdF
325+
Log [jsvm.testnet]: Saving greeting GO TEAM!
326+
Transaction Id 8gr8gtWDvCGzwS9HQ9GerKxBqDbnbwaWr5bBjdDpDBYg
327+
To see the transaction in the transaction explorer, please open this url in your browser
328+
https://explorer.testnet.near.org/transactions/8gr8gtWDvCGzwS9HQ9GerKxBqDbnbwaWr5bBjdDpDBYg
329+
''
330+
```
331+
332+
Your contract is now finished and you've learned how to interact with it using the NEAR-CLI!
333+
334+
> Got a question?
335+
> <a href="https://stackoverflow.com/questions/tagged/nearprotocol"> > <h8>Ask it on StackOverflow!</h8></a>

website/sidebars.json

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,13 @@
8080
"develop/contracts/rust/near-sdk-rs",
8181
"develop/contracts/rust/testing-rust-contracts"
8282
]
83+
},
84+
{
85+
"type": "category",
86+
"label": "Javascript",
87+
"items": [
88+
"develop/contracts/js/enclave-quickstart"
89+
]
8390
}
8491
],
8592
"Front-end": [
@@ -89,11 +96,13 @@
8996
"api/naj-cookbook",
9097
"faq/naj-faq"
9198
],
92-
"Run a Node": [{
93-
"type": "link",
94-
"label": "Node Documentation",
95-
"href": "https://near-nodes.io/"
96-
}],
99+
"Run a Node": [
100+
{
101+
"type": "link",
102+
"label": "Node Documentation",
103+
"href": "https://near-nodes.io/"
104+
}
105+
],
97106
"NEAR <> ETH": [
98107
"develop/eth/evm",
99108
"develop/eth/rainbow-bridge"
@@ -259,4 +268,4 @@
259268
"community/contribute/contribute-faq"
260269
]
261270
}
262-
}
271+
}

0 commit comments

Comments
 (0)