|
| 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> |
0 commit comments