|
| 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 docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript) for more details. |
| 10 | + |
| 11 | +<blockquote class="warning"> |
| 12 | +<strong>heads up</strong><br /><br /> |
| 13 | + |
| 14 | +Javascript smart contract development is not recommended for financial use cases as it is still very new to the NEAR ecosystem. |
| 15 | + |
| 16 | +</blockquote> |
| 17 | + |
| 18 | +## Overview {#overview} |
| 19 | + |
| 20 | +The Javascript Enclave, or jsvm for short, 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. |
| 21 | + |
| 22 | +There are several pros and cons when comparing the enclave approach to the regular WASM approach. The key differences are outlined below. |
| 23 | + |
| 24 | +| Areas | WebAssembly | Enclave | |
| 25 | +|---------------------------|--------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------| |
| 26 | +| Can interact with any smart contract on NEAR |✅|❌| |
| 27 | +| Synchronous Cross-Contract Calls |❌|✅| |
| 28 | +| Standards Support |✅|❌| |
| 29 | +| Function Call Access Key Support |✅|❌| |
| 30 | + |
| 31 | +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 the ability for cross-contract calls to be synchronous can greatly help with people's understanding of how contracts can work. |
| 32 | + |
| 33 | +## Quickstart {#quickstart} |
| 34 | + |
| 35 | +In this quickstart 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 go through and create a simple web-based frontend that displays the greeting and allows you to change it. |
| 36 | + |
| 37 | +### Prerequisites |
| 38 | + |
| 39 | +In order to successfully complete this quickstart guide, you'll need to have a few things installed: |
| 40 | +- [Node.js](https://nodejs.org/en/about/) and [npm](https://www.npmjs.com/): |
| 41 | +```bash |
| 42 | +curl -sL https://deb.nodesource.com/setup_17.x | sudo -E bash - |
| 43 | +sudo apt install build-essential nodejs |
| 44 | +PATH="$PATH" |
| 45 | +``` |
| 46 | +Ensure that they are both installed by running a version check: |
| 47 | +``` |
| 48 | +node -v |
| 49 | +npm -v |
| 50 | +``` |
| 51 | + |
| 52 | +It's important to have the **newest** version of the NEAR-CLI installed such that you can make use of the javascript features. To install or update, run: |
| 53 | + |
| 54 | +``` |
| 55 | +npm install -g near-cli |
| 56 | +``` |
| 57 | + |
| 58 | +### Creating a project |
| 59 | + |
| 60 | +Now that you have Node and npm installed, create a new directory to initialize the project in. |
| 61 | + |
| 62 | +```bash |
| 63 | +mkdir javascript-enclave-quickstart && cd javascript-enclave-quickstart |
| 64 | +``` |
| 65 | + |
| 66 | +Once you're in the directory, initialize a new default npm project. |
| 67 | + |
| 68 | +```bash |
| 69 | +npm init -y |
| 70 | +``` |
| 71 | + |
| 72 | +This will create a `package.json` file with contents similar to: |
| 73 | + |
| 74 | +```js |
| 75 | +{ |
| 76 | + "name": "javascript-enclave-quickstart", |
| 77 | + "version": "1.0.0", |
| 78 | + "description": "", |
| 79 | + "main": "index.js", |
| 80 | + "scripts": { |
| 81 | + "test": "echo \"Error: no test specified\" && exit 1" |
| 82 | + }, |
| 83 | + "keywords": [], |
| 84 | + "author": "", |
| 85 | + "license": "ISC" |
| 86 | +} |
| 87 | +``` |
| 88 | + |
| 89 | +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. |
| 90 | + |
| 91 | +```bash |
| 92 | +npm install --save near-sdk-js |
| 93 | +``` |
| 94 | + |
| 95 | +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: |
| 96 | + |
| 97 | +```diff |
| 98 | +{ |
| 99 | + "name": "javascript-enclave-quickstart", |
| 100 | + "version": "1.0.0", |
| 101 | + "description": "", |
| 102 | + "main": "index.js", |
| 103 | + "scripts": { |
| 104 | ++ "build": "near-sdk build", |
| 105 | + "test": "echo \"Error: no test specified\" && exit 1" |
| 106 | + }, |
| 107 | + "keywords": [], |
| 108 | + "author": "", |
| 109 | + "license": "ISC", |
| 110 | + "dependencies": { |
| 111 | + "near-sdk-js": "^0.1.1" |
| 112 | + } |
| 113 | +} |
| 114 | +``` |
| 115 | + |
| 116 | +> **Note:** This is optional and you can simply run `near-sdk build` instead. |
| 117 | +
|
| 118 | +You'll now want to create the `src` directory and initialize a new javascript file `index.js` where your contract logic will live. |
| 119 | + |
| 120 | +```bash |
| 121 | +mkdir src && cd src && touch index.js && cd .. |
| 122 | +``` |
| 123 | + |
| 124 | +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 root, create a new file and add the following content. |
| 125 | + |
| 126 | +```bash |
| 127 | +touch babel.config.json |
| 128 | +``` |
| 129 | +```js |
| 130 | +{ |
| 131 | + "plugins": [ |
| 132 | + "near-sdk-js/src/build-tools/near-bindgen-exporter", |
| 133 | + ["@babel/plugin-proposal-decorators", {"version": "legacy"}] |
| 134 | + ] |
| 135 | +} |
| 136 | +``` |
| 137 | + |
| 138 | +At this point, you just need to install all the packages and you will be ready to build your smart contract! |
| 139 | + |
| 140 | +```bash |
| 141 | +npm install |
| 142 | +``` |
| 143 | + |
| 144 | +Your file structure should now look as follows: |
| 145 | + |
| 146 | +``` |
| 147 | +javascript-enclave-quickstart |
| 148 | +├── node_modules |
| 149 | +├── package-lock.json |
| 150 | +├── babel.config.json |
| 151 | +├── package.json |
| 152 | +└── src |
| 153 | + └── index.js |
| 154 | +``` |
| 155 | + |
| 156 | +### Writing your first contract |
| 157 | + |
| 158 | +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. |
| 159 | + |
| 160 | +The contract presents 2 methods: set_greeting and get_greeting. The first one stores a String in the contract's parameter message, while the second one retrieves it. By default, the contract returns the message "Hello". |
| 161 | + |
| 162 | +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: |
| 163 | + |
| 164 | +```js |
| 165 | +import {NearContract, NearBindgen, call, view, near} from 'near-sdk-js' |
| 166 | +``` |
| 167 | +Let's break down these imports to help you understand why they're necessary. |
| 168 | +- `NearContract`: allows our contract to inherit important functionalities for changing and reading the contract's state. |
| 169 | +- `NearBindgen`: allows your contract to compile down to something that is NEAR compatible. |
| 170 | +- `call, view`: allows your methods to be view functions or change functions. |
| 171 | +- `near`: allows you to access important information within your functions such as the signer, predecessor, attached deposit etc.. |
| 172 | + |
| 173 | +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: |
| 174 | + |
| 175 | +```js |
| 176 | +// Define the default message |
| 177 | +const DEFAULT_MESSAGE = "Hello"; |
| 178 | + |
| 179 | +@NearBindgen |
| 180 | +class StatusMessage extends NearContract { |
| 181 | + // Define the constructor, which initializes the contract with a default message |
| 182 | + constructor() { |
| 183 | + // Used to give access to methods and properties of the parent or sibling class |
| 184 | + super() |
| 185 | + // Default the status records to |
| 186 | + this.message = DEFAULT_MESSAGE |
| 187 | + } |
| 188 | +} |
| 189 | +``` |
| 190 | +Running the constructor will default the contract's `message` state variable with the `DEFAULT_MESSAGE`. There's no way to get the current greeting, however. Within the class, add the following function. |
| 191 | + |
| 192 | +```js |
| 193 | +// Public method - returns the greeting saved, defaulting to DEFAULT_MESSAGE |
| 194 | +@view |
| 195 | +get_greeting() { |
| 196 | + env.log(`current greeting is ${this.message}`) |
| 197 | + return this.message |
| 198 | +} |
| 199 | +``` |
| 200 | +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. |
| 201 | + |
| 202 | +```js |
| 203 | +// Public method - accepts a greeting, such as "howdy", and records it |
| 204 | +@call |
| 205 | +set_greeting(message) { |
| 206 | + let account_id = near.signerAccountId() |
| 207 | + env.log(`Saving greeting ${message}`) |
| 208 | + this.message = message |
| 209 | +} |
| 210 | +``` |
| 211 | + |
| 212 | +At this point, your contract is finished and should look as follows: |
| 213 | + |
| 214 | +```js |
| 215 | +import {NearContract, NearBindgen, call, view, near} from 'near-sdk-js' |
| 216 | + |
| 217 | +// Define the default message |
| 218 | +const DEFAULT_MESSAGE = "Hello"; |
| 219 | + |
| 220 | +@NearBindgen |
| 221 | +class StatusMessage extends NearContract { |
| 222 | + // Define the constructor, which initializes the contract with a default message |
| 223 | + constructor() { |
| 224 | + // Used to give access to methods and properties of the parent or sibling class |
| 225 | + super() |
| 226 | + // Default the status records to |
| 227 | + this.message = DEFAULT_MESSAGE |
| 228 | + } |
| 229 | + |
| 230 | + // Public method - returns the greeting saved, defaulting to DEFAULT_MESSAGE |
| 231 | + @view |
| 232 | + get_greeting() { |
| 233 | + env.log(`current greeting is ${this.message}`) |
| 234 | + return this.message |
| 235 | + } |
| 236 | + |
| 237 | + // Public method - accepts a greeting, such as "howdy", and records it |
| 238 | + @call |
| 239 | + set_greeting(message) { |
| 240 | + let account_id = near.signerAccountId() |
| 241 | + env.log(`Saving greeting ${message}`) |
| 242 | + this.message = message |
| 243 | + } |
| 244 | +} |
| 245 | +``` |
| 246 | + |
| 247 | +### Building |
| 248 | + |
| 249 | +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/contact.base64` contract file. |
| 250 | + |
| 251 | +``` |
| 252 | +yarn build |
| 253 | +``` |
| 254 | + |
| 255 | +You can now deploy the contract and start interacting with it! |
| 256 | + |
| 257 | +### Deploying |
| 258 | + |
| 259 | +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. |
| 260 | + |
| 261 | +``` |
| 262 | +near js dev-deploy --base64File <build/contract.base64> --deposit 0.1 |
| 263 | +``` |
| 264 | +Alternatively, if you have an account already, you can specify the account you want to deploy to: |
| 265 | + |
| 266 | +``` |
| 267 | +near js deploy --accountId <YOUR_ACCOUNT_ID> --base64File <build/contract.base64> --deposit 0.1 |
| 268 | +``` |
| 269 | + |
| 270 | +> **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. |
| 271 | +
|
| 272 | +### Interacting |
| 273 | + |
| 274 | +Now that your contract is deployed, |
| 275 | + |
| 276 | + |
| 277 | + |
| 278 | + |
| 279 | + |
| 280 | + |
| 281 | +> Got a question? |
| 282 | +> <a href="https://stackoverflow.com/questions/tagged/nearprotocol"> > <h8>Ask it on StackOverflow!</h8></a> |
0 commit comments