├── .gitignore
├── README.md
├── api.js
├── app.js
├── blockchain.js
├── contracts
└── nft_simple.wasm
├── examples
└── nft_deploy
│ ├── README.MD
│ ├── deploy_tokens.js
│ └── token_types.js
├── near-api-server.config.json
├── near-api-ui
├── README.md
├── package.json
├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
└── src
│ ├── App.css
│ ├── App.js
│ ├── App.test.js
│ ├── assets
│ ├── android-chrome-192x192.png
│ ├── android-chrome-384x384.png
│ ├── apple-touch-icon.png
│ ├── explorer-bg.svg
│ ├── favicon-16x16.png
│ ├── favicon-32x32.png
│ ├── favicon.ico
│ ├── icon-network-right.svg
│ ├── logo-black.svg
│ ├── logo-white.svg
│ ├── logo.svg
│ ├── mstile-150x150.png
│ └── site.webmanifest
│ ├── config.js
│ ├── fonts
│ ├── 389947_6_0.eot
│ ├── 389947_6_0.ttf
│ ├── 389947_6_0.woff
│ └── 389947_6_0.woff2
│ ├── index.css
│ ├── index.html
│ ├── index.js
│ ├── logo.svg
│ ├── reportWebVitals.js
│ ├── setupTests.js
│ └── utils.js
├── package.json
├── token.js
└── user.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | node_modules/
3 | package-lock.json
4 | near-api-server.config.json
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # NEAR REST API SERVER
2 |
3 | > Interact with the NEAR blockchain using a simple REST API.
4 |
5 | ###### Live Demo:
6 | * [REST API Endpoint for NEAR Testnet](https://rest.nearspace.info)
7 | * [Web Console for `view`/`call` requests](https://api.nearspace.info)
8 |
9 | ---
10 |
11 | ## Overview
12 |
13 | _Click on a route for more information and examples_
14 |
15 | | Route | Method | Description |
16 | | ------------------------------------------ | ------ | --------------------------------------------------------------------------------------------------------------------------- |
17 | | **CONTRACTS** | | |
18 | | [`/deploy`](#deploy) | POST | Deploys a smart contract on NEAR. |
19 | | [`/view`](#view) | POST | Performs a smart contract **view** call with no gas burnt. |
20 | | [`/call`](#call) | POST | Performs a smart contract **change** call that burns gas. |
21 | | | | |
22 | | **UTILS** | | |
23 | | [`/init`](#init) | POST | Initializes the master account and updates `near-api-server-config.json` |
24 | | [`/create_user`](#create_user) | POST | Creates a NEAR [sub-account](https://docs.near.org/docs/concepts/account#subaccounts) and stores credentials in `/storage`. |
25 | | [`/parse_seed_phrase`](#parse_seed_phrase) | POST | Displays public and private key pair from a given seed phrase. |
26 | | [`/balance`](#balance) | GET | Displays account balance. |
27 | | [`/keypair`](#keypair) | GET | Generates Ed25519 key pair. |
28 | | [`/explorer`](#explorer) | POST | Run SELECT query in NEAR explorer database. |
29 | | | | |
30 | | **NFT EXAMPLE** | | |
31 | | [`/mint_nft`](#mint_nft) | POST | Mints an NFT for a given contract. |
32 | | [`/transfer_nft`](#transfer_nft) | POST | Transfers NFT ownership to a specified account. |
33 | | [`/view_nft`](#view_nft) | POST | Returns owner, metadata, and approved account IDs for a given token ID. |
34 |
35 | ---
36 |
37 | ## Requirements
38 |
39 | - [NEAR Account](https://docs.near.org/concepts/basics/account) _(with access to private key or seed phrase)_
40 | - [Node.js](https://nodejs.org/en/download/package-manager/)
41 | - [npm](https://www.npmjs.com/get-npm) or [Yarn](https://yarnpkg.com/getting-started/install)
42 | - API request tool such as [Postman](https://www.postman.com/downloads/)
43 |
44 | ---
45 |
46 | ## Setup
47 |
48 | 1. Clone repository
49 |
50 | ```bash
51 | git clone git@github.com:near-examples/near-api-server.git
52 | ```
53 |
54 | 2. Install dependencies
55 |
56 | ```bash
57 | npm install
58 | ```
59 |
60 | 3. Configure `near-api-server.config.json`
61 |
62 | Default settings:
63 |
64 | ```json
65 | {
66 | "server_host": "localhost",
67 | "server_port": 3000,
68 | "rpc_node": "https://rpc.testnet.near.org",
69 | "init_disabled": true
70 | }
71 | ```
72 |
73 | _**Note:** `init_disabled` determines if params can be changed via `/init` route._
74 |
75 | 4. Start server
76 |
77 | ```bash
78 | node app
79 | ```
80 |
81 | ---
82 |
83 | # Contracts
84 |
85 | ## `/deploy`
86 |
87 | > _Deploys a smart contract to the NEAR blockchain based on the wasm file located in `/contracts` folder._
88 |
89 | **Method:** **`POST`**
90 |
91 | | Param | Description |
92 | | -------------------------------- | ------------------------------------------------------------------------------------ |
93 | | `account_id` | _Account id that you will be deploying the contract to._ |
94 | | `seed_phrase` _OR_ `private_key` | _Seed phrase OR private key of the account id above._ |
95 | | `contract` | _wasm file of compiled contract located in the `/contracts` folder of this project._ |
96 |
97 | _**Note:** Use [`near login`](https://docs.near.org/tools/near-cli#near-login) to save your key pair to your local machine._
98 |
99 | Example:
100 |
101 | ```json
102 | {
103 | "account_id": "example.testnet",
104 | "seed_phrase": "witch collapse practice feed shame open despair creek road again ice least",
105 | "contract": "nft_simple.wasm"
106 | }
107 | ```
108 |
109 |
110 | Example Response:
111 |
112 |
113 | ```json
114 | {
115 | "status": {
116 | "SuccessValue": ""
117 | },
118 | "transaction": {
119 | "signer_id": "example.testnet",
120 | "public_key": "ed25519:Cgg4i7ciid8uG4K5Vnjzy5N4PXLst5aeH9ApRAUA3y8U",
121 | "nonce": 5,
122 | "receiver_id": "example.testnet",
123 | "actions": [
124 | {
125 | "DeployContract": {
126 | "code": "hT9saWV3aok50F8JundSIWAW+lxOcBOns1zenB2fB4E="
127 | }
128 | }
129 | ],
130 | "signature": "ed25519:3VrppDV8zMMRXErdBJVU9MMbbKZ4SK1pBZqXoyw3oSSiXTeyR2W7upNhhZPdFJ1tNBr9h9SnsTVeBm5W9Bhaemis",
131 | "hash": "HbokHoCGcjGQZrz8yU8QDqBeAm5BN8iPjaSMXu7Yp2KY"
132 | },
133 | "transaction_outcome": {
134 | "proof": [
135 | {
136 | "hash": "Dfjn2ro1dXrPqgzd5zU7eJpCMKnATm295ceocX73Qiqn",
137 | "direction": "Right"
138 | },
139 | {
140 | "hash": "9raAgMrEmLpL6uiynMAi9rykJrXPEZN4WSxLJUJXbipY",
141 | "direction": "Right"
142 | }
143 | ],
144 | "block_hash": "B64cQPDNkwiCcN3SGXU2U5Jz5M9EKF1hC6uDi4S15Fb3",
145 | "id": "HbokHoCGcjGQZrz8yU8QDqBeAm5BN8iPjaSMXu7Yp2KY",
146 | "outcome": {
147 | "logs": [],
148 | "receipt_ids": ["D94GcZVXE2WgPGuaJPJq8MdeEUidrN1FPkuU75NXWm7X"],
149 | "gas_burnt": 1733951676474,
150 | "tokens_burnt": "173395167647400000000",
151 | "executor_id": "example.testnet",
152 | "status": {
153 | "SuccessReceiptId": "D94GcZVXE2WgPGuaJPJq8MdeEUidrN1FPkuU75NXWm7X"
154 | }
155 | }
156 | },
157 | "receipts_outcome": [
158 | {
159 | "proof": [
160 | {
161 | "hash": "3HLkv7KrQ9LPptX658QiwkFagv8NwjcxF6ti15Een4uh",
162 | "direction": "Left"
163 | },
164 | {
165 | "hash": "9raAgMrEmLpL6uiynMAi9rykJrXPEZN4WSxLJUJXbipY",
166 | "direction": "Right"
167 | }
168 | ],
169 | "block_hash": "B64cQPDNkwiCcN3SGXU2U5Jz5M9EKF1hC6uDi4S15Fb3",
170 | "id": "D94GcZVXE2WgPGuaJPJq8MdeEUidrN1FPkuU75NXWm7X",
171 | "outcome": {
172 | "logs": [],
173 | "receipt_ids": [],
174 | "gas_burnt": 1733951676474,
175 | "tokens_burnt": "173395167647400000000",
176 | "executor_id": "example.testnet",
177 | "status": {
178 | "SuccessValue": ""
179 | }
180 | }
181 | }
182 | ]
183 | }
184 | ```
185 |
186 |
187 |
188 |
189 | ---
190 |
191 | ## `/view`
192 |
193 | > _Performs a smart contract view call that is free of charge (no gas burnt)._
194 |
195 | **Method:** **`POST`**
196 |
197 | | Param | Description |
198 | | ---------- | ----------------------------------------------------------------------------------------- |
199 | | `contract` | _Account id of the smart contract you are calling._ |
200 | | `method` | _Name of the public method on the contract you are calling._ |
201 | | `params` | _Arguments the method of the contract takes. Pass an empty object if no args are needed._ |
202 |
203 | Example:
204 |
205 | ```json
206 | {
207 | "contract": "inotel.pool.f863973.m0",
208 | "method": "get_accounts",
209 | "params": { "from_index": 0, "limit": 5 }
210 | }
211 | ```
212 |
213 |
214 | Example Response:
215 |
216 |
217 | ```json
218 | [
219 | {
220 | "account_id": "ino.lockup.m0",
221 | "unstaked_balance": "0",
222 | "staked_balance": "2719843984800963837328608365424",
223 | "can_withdraw": true
224 | },
225 | {
226 | "account_id": "ino.testnet",
227 | "unstaked_balance": "2",
228 | "staked_balance": "3044983795632859169857527919579",
229 | "can_withdraw": true
230 | },
231 | {
232 | "account_id": "ino.stakewars.testnet",
233 | "unstaked_balance": "2",
234 | "staked_balance": "21704174266817478470830456026",
235 | "can_withdraw": true
236 | },
237 | {
238 | "account_id": "ds4.testnet",
239 | "unstaked_balance": "3",
240 | "staked_balance": "10891355794195012441764921",
241 | "can_withdraw": true
242 | },
243 | {
244 | "account_id": "32oijafsiodjfas.testnet",
245 | "unstaked_balance": "3",
246 | "staked_balance": "383757424103247547511904666",
247 | "can_withdraw": true
248 | }
249 | ]
250 | ```
251 |
252 |
253 |
254 |
255 | ---
256 |
257 | ## `/call`
258 |
259 | > _Performs a smart contract call that changes state and burns gas._
260 |
261 | **Method:** **`POST`**
262 |
263 | | Param | Description |
264 | | -------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
265 | | `account_id` | _Account id that will be performing the call and will be charged for gas and attached tokens / deposit._ |
266 | | `seed_phrase` _OR_ `private_key` | _Seed phrase OR private key of the account id above._ |
267 | | `contract` | _Account id of the smart contract you will be calling._ |
268 | | `method` | _Public method on the smart contract that you will be calling._ |
269 | | `params` | _Arguments the method of the contract takes. Pass an empty object if no args are needed._ |
270 | | `attached_gas` | _Amount of gas you will be attaching to the call in [TGas](https://docs.near.org/docs/concepts/gas#thinking-in-gas)._ |
271 | | `attached_tokens` | _Amount of tokens to be sent to the contract you are calling in yoctoNEAR (10^-24 NEAR)._ |
272 |
273 | _**Note:** Use [`near login`](https://docs.near.org/docs/tools/near-cli#near-login) to save your key pair to your local machine._
274 |
275 | Example:
276 |
277 | ```json
278 | {
279 | "account_id": "example.testnet",
280 | "private_key": "2Kh6PJjxH5PTTsVnYqtgnnwXHeafvVGczDXoCb33ws8reyq8J4oBYix1KP2ugRQ7q9NQUyPcVFTtbSG3ARVKETfK",
281 | "contract": "guest-book.testnet",
282 | "method": "addMessage",
283 | "params": { "text": "Hello World" },
284 | "attached_gas": "100000000000000",
285 | "attached_tokens": "0"
286 | }
287 | ```
288 |
289 |
290 |
291 | Example Response:
292 |
293 |
294 | ```json
295 | {
296 | "status": {
297 | "SuccessValue": ""
298 | },
299 | "transaction": {
300 | "signer_id": "example.testnet",
301 | "public_key": "ed25519:ASZEids5Qa8XMHX2S7LRL4bQRczi4YuMWXSM7S1HE5b",
302 | "nonce": 4,
303 | "receiver_id": "guest-book.testnet",
304 | "actions": [
305 | {
306 | "FunctionCall": {
307 | "method_name": "addMessage",
308 | "args": "eyJ0ZXh0IjoiSGVsbG8gV29ybGQifQ==",
309 | "gas": 100000000000000,
310 | "deposit": "0"
311 | }
312 | }
313 | ],
314 | "signature": "ed25519:4T9FqsjYBxcitjd5GgHrv3i3hcdcJSNcwwG3jBUgs7zZCZ3uShAK44Hi3oYFefhr8e5UW3LLD49ofRpGXKwGqqot",
315 | "hash": "CniHtfQVzcyVWJaUrQibJyGdhLi5axsjsoSRvvFbJ1jv"
316 | },
317 | "transaction_outcome": {
318 | "proof": [
319 | {
320 | "hash": "EkzDGbbBHSAuJcCPmhKSqbnBKyLrMgXkrTEZZZQudHeH",
321 | "direction": "Right"
322 | },
323 | {
324 | "hash": "36j4PK6fsLChiVTBQnXS1ywVSgJgHo7FtWzd5y5jkK1B",
325 | "direction": "Right"
326 | }
327 | ],
328 | "block_hash": "CUAu2deED8UX4vkerCbsTMR7YkeKt8RQXknYMNrVvM7C",
329 | "id": "CniHtfQVzcyVWJaUrQibJyGdhLi5axsjsoSRvvFbJ1jv",
330 | "outcome": {
331 | "logs": [],
332 | "receipt_ids": ["B7xAYoga5vrKERK7wY7EHa2Z74LaRJwqPsh4esLrKeQF"],
333 | "gas_burnt": 2427992549888,
334 | "tokens_burnt": "242799254988800000000",
335 | "executor_id": "example.testnet",
336 | "status": {
337 | "SuccessReceiptId": "B7xAYoga5vrKERK7wY7EHa2Z74LaRJwqPsh4esLrKeQF"
338 | }
339 | }
340 | },
341 | "receipts_outcome": [
342 | {
343 | "proof": [
344 | {
345 | "hash": "6Uo6BajpAxiraJEv69RwhjYnC86u56cw29vRDB1SV4dv",
346 | "direction": "Right"
347 | }
348 | ],
349 | "block_hash": "Ecq6pK74uiJFKxPTaasYuQcsEznnQjdzMAfsyrBpDo2u",
350 | "id": "B7xAYoga5vrKERK7wY7EHa2Z74LaRJwqPsh4esLrKeQF",
351 | "outcome": {
352 | "logs": [],
353 | "receipt_ids": ["6S6m1TYuVPYovLu9FHGV5oLRnDXeNQ8NhXxYjcr91xAN"],
354 | "gas_burnt": 3766420707221,
355 | "tokens_burnt": "376642070722100000000",
356 | "executor_id": "guest-book.testnet",
357 | "status": {
358 | "SuccessValue": ""
359 | }
360 | }
361 | },
362 | {
363 | "proof": [
364 | {
365 | "hash": "2za2YKUhyMfWbeEL7UKZxZcQbAqEmSPgPoYh9QDdeJQi",
366 | "direction": "Left"
367 | },
368 | {
369 | "hash": "61aHEiTBBbPU8UEXgSQh42TujFkHXQQMSuTh13PLPwbG",
370 | "direction": "Right"
371 | }
372 | ],
373 | "block_hash": "6LfpzvCBkqq7h5uG9VjAHMwSpC3HMMBSAGNGhbrAJzKP",
374 | "id": "6S6m1TYuVPYovLu9FHGV5oLRnDXeNQ8NhXxYjcr91xAN",
375 | "outcome": {
376 | "logs": [],
377 | "receipt_ids": [],
378 | "gas_burnt": 0,
379 | "tokens_burnt": "0",
380 | "executor_id": "example.testnet",
381 | "status": {
382 | "SuccessValue": ""
383 | }
384 | }
385 | }
386 | ]
387 | }
388 | ```
389 |
390 |
391 |
392 |
393 | ---
394 |
395 | # Utils
396 |
397 | ---
398 |
399 | ## `/init`
400 |
401 | > _Configures `near-api-server.config.json` and creates a master account that stores credentials in this file. This allows for "simple methods" to be called where you won't have to pass as many parameters, primarily the master account id and private key or seed phrase._
402 |
403 | **ATTN: SERVER MUST BE RESTARTED AFTER CALLING THIS ENDPOINT**
404 |
405 | **Method:** **`POST`**
406 |
407 | | Param | Description |
408 | | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
409 | | `master_account_id` | _Master account that has full access to the NFT contract below_ |
410 | | `seed_phrase` _OR_ `private_key` | _Seed phrase OR private key of the account id above._ |
411 | | `nft_contract` | _Contract account that has NFT contract deployed to it_ |
412 | | `server_host` | _Public IP address for your API server (localhost is default)_ |
413 | | `server_port` | _(Port your API server will listen on)_ |
414 | | `rpc_node` | _[Network](https://docs.near.org/docs/concepts/networks) your server will be running on (testnet, mainnet, or betanet)_ |
415 |
416 | _**Note:** Use [`near login`](https://docs.near.org/docs/tools/near-cli#near-login) to save your key pair to your local machine._
417 |
418 | Example:
419 |
420 | ```json
421 | {
422 | "master_account_id": "example.testnet",
423 | "seed_phrase": "seed phrase for master_account_id goes here",
424 | "nft_contract": "nft-contract.example.testnet",
425 | "server_host": "localhost",
426 | "server_port": 3000,
427 | "rpc_node": "https://rpc.testnet.near.org"
428 | }
429 | ```
430 |
431 | Example Response:
432 |
433 | ```json
434 | {
435 | "text": "Settings updated."
436 | }
437 | ```
438 |
439 | ---
440 |
441 | ## `/sign_url`
442 |
443 | > _Generates a link to NEAR Wallet with provided transaction details. May be used to redirect user to the wallet and perform a transaction without generation application-specific keys and granting access._
444 |
445 | **Method:** **`POST`**
446 |
447 | | Param | Description |
448 | | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
449 | | `account_id` | _Signer Account_ |
450 | | `receiver_id` | _Recipient contract account, may be dApp contract or personal account_ |
451 | | `method` | _Contract method to call. Use `!transfer` to transfer NEAR tokens_ |
452 | | `params` | _Transaction arguments_ |
453 | | `deposit` | _Attached deposit in NEAR_ |
454 | | `gas` | _Attached gas in yoctoNEAR_ |
455 | | `meta` | _Transaction meta. May be empty_ |
456 | | `callback_url` | _URL to redirect user after the transaction. May be empty_ |
457 | | `network` | _Your network: mainnet/testnet_ |
458 |
459 | Example:
460 |
461 | ```
462 | {
463 | "account_id": "zavodil.testnet",
464 | "receiver_id": "inotel.pool.f863973.m0",
465 | "method": "ping",
466 | "params": {},
467 | "deposit": 0,
468 | "gas": 30000000000000,
469 | "meta": "",
470 | "callback_url": "",
471 | "network": "testnet"
472 | }
473 | ```
474 |
475 | Example Response:
476 |
477 | ```
478 | https://wallet.testnet.near.org/sign?transactions=DwAAAHphdm9kaWwudGVzdG5ldADKei8CC%2BlhIM9GNPitr87eHXpqdnQsCdLD%2B0ADdTJbqwEAAAAAAAAAFgAAAGlub3RlbC5wb29sLmY4NjM5NzMubTCfZPsioMcZCQRg4Uy7rOu4ERg10QV9c415FuXE0VDRRAEAAAACBAAAAHBpbmcCAAAAe30A4FfrSBsAAAAAAAAAAAAAAAAAAAAAAAA%3D&callbackUrl=
479 | ```
480 |
481 | Approving this url performed a transaction [143c9MNaqXFXuiobjUaQ8FPSBR2ukYbCMzGdPe6HqXEq](https://explorer.testnet.near.org/transactions/143c9MNaqXFXuiobjUaQ8FPSBR2ukYbCMzGdPe6HqXEq)
482 |
483 |
484 | ## `/create_user`
485 |
486 | > _Creates a NEAR [sub-account](https://docs.near.org/docs/concepts/account#subaccounts) using initialized master account and saves credentials to `/storage` directory. Requires [`/init`](#init) configuration with master account._
487 |
488 | **Note:** _Only letters, digits, and - or \_ separators are allowed._
489 |
490 | **Method:** **`POST`**
491 |
492 | Example:
493 |
494 | ```
495 | {
496 | "name" : "satoshi"
497 | }
498 | ```
499 |
500 | Example Response:
501 |
502 | ```json
503 | {
504 | "text": "Account satoshi.example.testnet created. Public key: ed25519:HW4koiHqLi5WdVHWy9fqBWHbLRrzfmvCiRAUVhMa14T2"
505 | }
506 | ```
507 |
508 | ---
509 |
510 | ## `/parse_seed_phrase`
511 |
512 | > _Converts seed phrase into public / private key pair._
513 |
514 | **Method:** **`POST`**
515 |
516 | Example:
517 |
518 | ```
519 | {
520 | "seed_phrase" : "witch collapse practice feed shame open despair creek road again ice least"
521 | }
522 | ```
523 |
524 | Example Response:
525 |
526 | ```
527 | {
528 | "seedPhrase": "witch collapse practice feed shame open despair creek road again ice least",
529 | "secretKey": "ed25519:41oHMLtYygTsgwDzaMdjWRq48Sy9xJsitJGmMxgA9A7nvd65aT8vQwAvRdHi1nruPP47B6pNhW5T5TK8SsqCZmjn",
530 | "publicKey": "ed25519:Cgg4i7ciid8uG4K5Vnjzy5N4PXLst5aeH9ApRAUA3y8U"
531 | }
532 | ```
533 |
534 | ---
535 |
536 | ## `/balance`
537 |
538 | > _Displays account balance in yoctoNEAR (10^-24 NEAR)._
539 |
540 | **Method:** **`GET`**
541 |
542 | Example:
543 |
544 | ```
545 | http://localhost:3000/balance/name.testnet
546 | ```
547 |
548 | Example Response:
549 |
550 | ```
551 | 199999959035075000000000000
552 | ```
553 |
554 | ---
555 |
556 | ## `/keypair`
557 |
558 | > _Generates Ed25519 key pair._
559 |
560 | **Method:** **`GET`**
561 |
562 | Example:
563 |
564 | ```
565 | http://localhost:3000/keypair
566 | ```
567 |
568 | Example Response:
569 |
570 | ```
571 | {
572 | "public_key": "ed25519:3pNJK3fwP14UEbPjQqgDASwWR4XmbAEQBeNsyThhtNKY",
573 | "private_key": "3s9nVrCU4MER3w9cxXcJM58RGRzFNJnLzo9vgQiNrkuGW3Xp7Up6cYnY4JKQZ7Qp3GhmXckrApRyDPAfzo2oCm8a"
574 | }
575 | ```
576 |
577 | ## `/explorer`
578 |
579 | > _Run SELECT query in NEAR explorer database._
580 |
581 | **Method:** **`POST`**
582 |
583 | | Param | Description |
584 | | -------------------------------- | ----------------------------------------------------------- |
585 | | `user` | _Public account, `public_readonly`_ |
586 | | `host` | _NEAR indexer host, `testnet.db.explorer.indexer.near.dev`_ |
587 | | `database` | _Name of the database, `testnet_explorer`_ |
588 | | `password` | _Password, `nearprotocol`_ |
589 | | `port` | _Port, `5432`_ |
590 | | `parameters` | _Array of query parameters, `[]`_ |
591 | | `query` | _Query without tabs, linebreaks and special characters_ |
592 |
593 | Check indexer server credentials on a [github](https://github.com/near/near-indexer-for-explorer/#shared-public-access).
594 |
595 | Example:
596 |
597 | ```json
598 | {
599 | "user": "public_readonly",
600 | "host": "35.184.214.98",
601 | "database": "testnet_explorer",
602 | "password": "nearprotocol",
603 | "port": 5432,
604 | "parameters": ["testnet", 1],
605 | "query": "SELECT * FROM action_receipt_actions WHERE receipt_receiver_account_id = $1 LIMIT $2"
606 | }
607 | ```
608 |
609 |
610 | Example Response:
611 |
612 |
613 | ```json
614 | [
615 | {
616 | "receipt_id": "GZMyzjDWPJLjrCuQG82uHj3xRVHwdDnWHH1gCnSBejkR",
617 | "index_in_action_receipt": 0,
618 | "action_kind": "TRANSFER",
619 | "args": {
620 | "deposit": "1273665187500000000"
621 | },
622 | "receipt_predecessor_account_id": "system",
623 | "receipt_receiver_account_id": "testnet",
624 | "receipt_included_in_block_timestamp": "1619207391172257749"
625 | }
626 | ]
627 | ```
628 |
629 |
630 |
631 |
632 | ---
633 |
634 | # NFTs
635 |
636 | ---
637 |
638 | ## `/mint_nft`
639 |
640 | > _Mints a new NFT on a specified contract._
641 |
642 | **Method:** **`POST`**
643 |
644 | ### Standard NFT Minting
645 |
646 | | Param | Description |
647 | | -------------------------------- | ------------------------------------------------------ |
648 | | `token_id` | _ID for new token you are minting_ |
649 | | `metadata` | _Metadata for the new token as a string._ |
650 | | `account_id` | _Account ID for the new token owner._ |
651 | | `seed_phrase` _OR_ `private_key` | _Seed phrase OR private key for the NFT contract._ |
652 | | `nft_contract` | _Account ID for the NFT contract your are minting on._ |
653 |
654 | _**Note:** Use [`near login`](https://docs.near.org/docs/tools/near-cli#near-login) to save your key pair to your local machine._
655 |
656 | Example:
657 |
658 | ```
659 | {
660 | "token_id": "EXAMPLE-TOKEN",
661 | "metadata": "https://ipfs.io/ipfs/Qme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pYrDKEoiu",
662 | "account_id": "example.testnet",
663 | "private_key": "41oHMLtYygTsgwDzaMdjWRq48Sy9xJsitJGmMxgA9A7nvd65aT8vQwAvRdHi1nruPP47B6pNhW5T5TK8SsqCZmjn",
664 | "contract": "nft.example.near",
665 | }
666 | ```
667 |
668 | ### Simple NFT Minting
669 |
670 | _Requires [`/init`](#init) configuration with master account._
671 |
672 | Example:
673 |
674 | ```json
675 | {
676 | "token_id": "EXAMPLE_TOKEN",
677 | "metadata": "https://ipfs.io/ipfs/Qme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pYrDKEoiu"
678 | }
679 | ```
680 |
681 |
682 | Example Response:
683 |
684 |
685 | ```json
686 | [
687 | {
688 | "token": {
689 | "owner_id": "example.testnet",
690 | "metadata": "https://ipfs.io/ipfs/Qme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pYrDKEoiu",
691 | "approved_account_ids": [],
692 | "token_id": "EXAMPLE_TOKEN"
693 | },
694 | "tx": "Be7tV1h2dvhg53S2rartojeSUbNfQTB7ypuprmb6xRhw"
695 | }
696 | ]
697 | ```
698 |
699 |
700 |
701 |
702 | _(`tx` is the transaction hash that can be queried in [NEAR Explorer](http://explorer.testnet.near.org))_
703 |
704 | ---
705 |
706 | ### Batch NFT minting (simple)
707 |
708 | _Requires [`/init`](#init) configuration with master account._
709 |
710 | Example:
711 |
712 | ```json
713 | {
714 | "token_id": "EXAMPLE_TOKEN_{inc}",
715 | "metadata": "https://ipfs.io/ipfs/Qme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pYrDKEoiu",
716 | "min": 31,
717 | "max": 33
718 | }
719 | ```
720 |
721 | _(This creates `EXAMPLE_TOKEN_31`, `EXAMPLE_TOKEN_32`, & `EXAMPLE_TOKEN_33`)_
722 |
723 |
724 | Example Response:
725 |
726 |
727 | ```json
728 | [
729 | {
730 | "tx": "mAL92gb6g6hhubZBRewJk5vSwmmzm2SXmwdAfYqfWcG"
731 | },
732 | {
733 | "tx": "Dv9h8nWJLujkKpmw58ZR4QwAgPVprb4j5QarDUumoGEX"
734 | },
735 | {
736 | "tx": "J48F3vALJBbbUguKXp6e16g5vKVwzC2LtVBpsfEVFpYa"
737 | }
738 | ]
739 | ```
740 |
741 |
742 |
743 |
744 | _(Above response are transaction hashes that can be queried in [NEAR Explorer](http://explorer.testnet.near.org))_
745 |
746 | ---
747 |
748 | ## `/transfer_nft`
749 |
750 | > _Transfers ownership of NFT from specified contract on behalf of provided `enforce_owner_id` signed with `owner_private_key`._
751 |
752 | **Method:** **`POST`**
753 |
754 | ### Standard Transfer NFT
755 |
756 | | Param | Description |
757 | | ------------------- | --------------------------------------------------------- |
758 | | `token_id` | _Token ID of the token being transferred_ |
759 | | `receiver_id` | _Account ID taking ownership of the NFT_ |
760 | | `enforce_owner_id` | _Account ID for the account that currently owns the NFT._ |
761 | | `memo` | _Optional message to the new token holder._ |
762 | | `owner_private_key` | _Private key of the `enforce_owner_id`._ |
763 | | `nft_contract` | _NFT contract that the token being transferred is on._ |
764 |
765 | _**Note:** Use [`near login`](https://docs.near.org/docs/tools/near-cli#near-login) to save your key pair to your local machine._
766 |
767 | Example:
768 |
769 | ```json
770 | {
771 | "token_id": "EXAMPLE-TOKEN",
772 | "receiver_id": "receiver.testnet",
773 | "enforce_owner_id": "example.testnet",
774 | "memo": "Here's a token I thought you might like! :)",
775 | "owner_private_key": "YOUR_PRIVATE_KEY",
776 | "contract": "nft.example.near"
777 | }
778 | ```
779 |
780 | Example Response:
781 |
782 | ```json
783 | {
784 | "owner_id": "receiver.testnet",
785 | "metadata": "https://ipfs.io/ipfs/Qme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pYrDKEoiu",
786 | "approved_account_ids": [],
787 | "tx": "5WdNgmNeA5UNpSMDRXemwJc95MB6J22LcvAaimuN5YzF"
788 | }
789 | ```
790 |
791 | _(`tx` is the transaction hash that can be queried in [NEAR Explorer](http://explorer.testnet.near.org))_
792 |
793 | ---
794 |
795 | ### Simple Transfer NFTs
796 |
797 | > _Requires [`/init`](#init) configuration with master account._
798 |
799 | | Param | Description |
800 | | ------------------ | --------------------------------------------------------- |
801 | | `token_id` | _Token ID of the token being transferred_ |
802 | | `receiver_id` | _Account ID taking ownership of the NFT_ |
803 | | `enforce_owner_id` | _Account ID for the account that currently owns the NFT._ |
804 | | `memo` | _Optional message to new token holder._ |
805 |
806 | Example:
807 |
808 | ```json
809 | {
810 | "token_id": "EXAMPLE-TOKEN",
811 | "receiver_id": "receiver.testnet",
812 | "enforce_owner_id": "example.testnet",
813 | "memo": "Here's a token I thought you might like! :)"
814 | }
815 | ```
816 |
817 | Example Response:
818 |
819 | ```json
820 | {
821 | "owner_id": "receiver.testnet",
822 | "metadata": "https://ipfs.io/ipfs/Qme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pYrDKEoiu",
823 | "approved_account_ids": [],
824 | "tx": "5WdNgmNeA5UNpSMDRXemwJc95MB6J22LcvAaimuN5YzF"
825 | }
826 | ```
827 |
828 | _(`tx` is the transaction hash that can be queried in [NEAR Explorer](http://explorer.testnet.near.org))_
829 |
830 | ---
831 |
832 | ## `view_nft`
833 |
834 | ### Standard View NFT
835 |
836 | > _Returns owner, metadata, and approved account IDs for a given token ID._
837 |
838 | **Method:** **`POST`**
839 |
840 | Example:
841 |
842 | ```json
843 | {
844 | "token_id": "EXAMPLE-TOKEN",
845 | "contract": "nft.example.testnet"
846 | }
847 | ```
848 |
849 | Example response:
850 |
851 | ```json
852 | {
853 | "owner_id": "example.testnet",
854 | "metadata": "https://ipfs.io/ipfs/Qme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pYrDKEoiu",
855 | "approved_account_ids": []
856 | }
857 | ```
858 |
859 | ---
860 |
861 | ### Simple View NFT
862 |
863 | > _Receive detailed information about NFT using URL params. Requires [`/init`](#init) configuration with master account._
864 |
865 | **Method:** **`GET`**
866 |
867 | Example:
868 |
869 | `http://localhost:3000/view_nft/EXAMPLE-TOKEN`
870 |
871 | Example Response:
872 |
873 | ```json
874 | {
875 | "owner_id": "example.testnet",
876 | "metadata": "https://ipfs.io/ipfs/Qme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pYrDKEoiu",
877 | "approved_account_ids": []
878 | }
879 | ```
880 |
881 | ---
882 |
883 | ## Faker data
884 |
885 | > Use the following tags below to use random data for testing purposes.
886 |
887 | - `{username}`
888 | - `{number}`
889 | - `{word}`
890 | - `{words}`
891 | - `{image}`
892 | - `{color}`
893 |
894 | ## Video Presentation
895 |
896 | [](https://youtu.be/d71OscmH4cA)
897 |
--------------------------------------------------------------------------------
/api.js:
--------------------------------------------------------------------------------
1 | const CONFIG_PATH = './near-api-server.config.json';
2 |
3 | module.exports = {
4 | CONFIG_PATH,
5 |
6 | reject: (err) => {
7 | return {error: typeof err === "string" ? err : JSON.stringify(err)};
8 | },
9 | notify: (message) => {
10 | return {text: message};
11 | },
12 | getNetworkFromRpcNode(rpc_node){
13 | return rpc_node.replace("https://rpc.", "").replace(".near.org", "");
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/app.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | 'use strict';
4 | const user = require('./user');
5 | const token = require('./token');
6 | const blockchain = require('./blockchain');
7 | const api = require('./api');
8 | const faker = require('faker');
9 | const crypto = require('crypto');
10 | const CatboxMemory = require('@hapi/catbox-memory');
11 | const Hapi = require('@hapi/hapi');
12 | const fs = require('fs');
13 | const {Client} = require('pg');
14 | Client.poolSize = 100;
15 |
16 | const settings = JSON.parse(fs.readFileSync(api.CONFIG_PATH, 'utf8'));
17 | const ViewCacheExpirationInSeconds = 10;
18 | const ViewGenerateTimeoutInSeconds = 30;
19 |
20 | const init = async () => {
21 | const server = Hapi.server({
22 | port: settings.server_port,
23 | host: settings.server_host,
24 | cache: [
25 | {
26 | name: 'near-api-cache',
27 | provider: {
28 | constructor: CatboxMemory
29 | }
30 | }
31 | ]
32 | });
33 |
34 | function processRequest(request) {
35 | Object.keys(request.payload).map((key) => {
36 | switch (request.payload[key]) {
37 | case '{username}':
38 | request.payload[key] = faker.internet
39 | .userName()
40 | .replace(/[^0-9a-z]/gi, '');
41 | break;
42 | case '{color}':
43 | request.payload[key] = faker.internet.color();
44 | break;
45 | case '{number}':
46 | request.payload[key] = faker.random.number();
47 | break;
48 | case '{word}':
49 | request.payload[key] = faker.random.word();
50 | break;
51 | case '{words}':
52 | request.payload[key] = faker.random.words();
53 | break;
54 | case '{image}':
55 | request.payload[key] = faker.random.image();
56 | break;
57 | }
58 | });
59 |
60 | return request;
61 | }
62 |
63 | server.route({
64 | method: 'GET',
65 | path: '/',
66 | handler: () => {
67 | return api.notify(
68 | 'Welcome to NEAR REST API SERVER (https://github.com/near-examples/near-api-rest-server)! ' +
69 | (!settings.master_account_id
70 | ? 'Please initialize your NEAR account in order to use simple nft mint/transfer methods'
71 | : `Master Account: ${settings.master_account_id}`)
72 | );
73 | },
74 | });
75 |
76 | server.route({
77 | method: 'POST',
78 | path: '/view',
79 | handler: async (request, h) => {
80 | request = processRequest(request);
81 |
82 | if (request.payload.disabled_cache) {
83 | return blockchain.View(
84 | request.payload.contract,
85 | request.payload.method,
86 | request.payload.params,
87 | request.payload.rpc_node,
88 | request.payload.headers
89 | );
90 | } else {
91 | request.payload.request_name = "view";
92 | return replyCachedValue(h, await server.methods.view(request.payload));
93 | }
94 | }
95 | });
96 |
97 | server.method(
98 | 'view',
99 | async (params) => blockchain.View(
100 | params.contract,
101 | params.method,
102 | params.params,
103 | params.rpc_node,
104 | params.headers
105 | ),
106 | getServerMethodParams());
107 |
108 | server.route({
109 | method: 'POST',
110 | path: '/call',
111 | handler: async (request) => {
112 | request = processRequest(request);
113 | let {
114 | account_id,
115 | private_key,
116 | attached_tokens,
117 | attached_gas,
118 | contract,
119 | method,
120 | params,
121 | network,
122 | rpc_node,
123 | headers
124 | } = request.payload;
125 | return await blockchain.Call(
126 | account_id,
127 | private_key,
128 | attached_tokens,
129 | attached_gas,
130 | contract,
131 | method,
132 | params,
133 | network,
134 | rpc_node,
135 | headers
136 | );
137 | },
138 | });
139 |
140 | server.route({
141 | method: 'POST',
142 | path: '/init',
143 | handler: async (request) => {
144 | if (settings.init_disabled) {
145 | return api.reject('Method now allowed');
146 | }
147 |
148 | request = processRequest(request);
149 | let {
150 | master_account_id,
151 | seed_phrase,
152 | private_key,
153 | nft_contract,
154 | server_host,
155 | server_port,
156 | rpc_node,
157 | } = request.payload;
158 |
159 | if (seed_phrase)
160 | private_key = (await user.GetKeysFromSeedPhrase(seed_phrase)).secretKey;
161 |
162 | let response = await blockchain.Init(
163 | master_account_id,
164 | private_key,
165 | nft_contract,
166 | server_host,
167 | server_port,
168 | rpc_node
169 | );
170 | if (!response.error) {
171 | process.on('SIGINT', function () {
172 | console.log('Stopping server...');
173 | server.stop({timeout: 1000}).then(async function () {
174 | await server.start();
175 | });
176 | });
177 | }
178 |
179 | return response;
180 | },
181 | });
182 |
183 | server.route({
184 | method: 'POST',
185 | path: '/deploy',
186 | handler: async (request) => {
187 | request = processRequest(request);
188 | let {account_id, private_key, seed_phrase, contract} = request.payload;
189 |
190 | if (seed_phrase)
191 | private_key = (await user.GetKeysFromSeedPhrase(seed_phrase)).secretKey;
192 |
193 | return await blockchain.DeployContract(account_id, private_key, contract);
194 | },
195 | });
196 |
197 | server.route({
198 | method: 'GET',
199 | path: '/view_nft/{token_id}',
200 | handler: async (request, h) => {
201 | request.params.request_name = "view_nft";
202 | return replyCachedValue(h, await server.methods.viewNFT(request.params));
203 | },
204 | });
205 |
206 | server.method(
207 | 'viewNFT',
208 | async (params) => await token.ViewNFT(params.token_id),
209 | getServerMethodParams());
210 |
211 | server.route({
212 | method: 'POST',
213 | path: '/view_nft',
214 | handler: async (request) => {
215 | return await token.ViewNFT(
216 | request.payload.token_id,
217 | request.payload.contract
218 | );
219 | },
220 | });
221 |
222 | server.route({
223 | method: 'POST',
224 | path: '/create_user',
225 | handler: async (request) => {
226 | request = processRequest(request);
227 |
228 | const name = (
229 | request.payload.name +
230 | '.' +
231 | settings.master_account_id
232 | ).toLowerCase();
233 | let account = await user.CreateKeyPair(name);
234 |
235 | let status = await user.CreateAccount(account);
236 |
237 | if (status)
238 | return {
239 | text: `Account ${name} created. Public key: ${account.public_key}`,
240 | };
241 | else return {text: 'Error'};
242 | },
243 | });
244 |
245 | server.route({
246 | method: 'POST',
247 | path: '/parse_seed_phrase',
248 | handler: async (request) => {
249 | request = processRequest(request);
250 |
251 | return await user.GetKeysFromSeedPhrase(request.payload.seed_phrase);
252 | },
253 | });
254 |
255 | server.route({
256 | method: 'GET',
257 | path: '/balance/{account_id}',
258 | handler: async (request) => {
259 | return await blockchain.GetBalance(request.params.account_id);
260 | }
261 | });
262 |
263 | server.route({
264 | method: 'GET',
265 | path: '/keypair',
266 | handler: async () => {
267 | return await user.GenerateKeyPair();
268 | }
269 | });
270 |
271 | server.route({
272 | method: 'POST',
273 | path: '/mint_nft',
274 | handler: async (request) => {
275 | let {min, max} = request.payload;
276 |
277 | if (!min || !max) min = max = 0;
278 | let response = [];
279 |
280 | request = processRequest(request);
281 | for (let i = min; i <= max; i++) {
282 | const tokenId = request.payload.token_id.replace('{inc}', i);
283 |
284 | let {account_id, private_key, metadata, contract} = request.payload;
285 |
286 | const tx = await token.MintNFT(
287 | tokenId,
288 | metadata,
289 | contract,
290 | account_id,
291 | private_key
292 | );
293 |
294 | if (tx) {
295 | if (min === max) {
296 | let create_token = await token.ViewNFT(tokenId, contract);
297 | create_token.token_id = tokenId;
298 | response.push({token: create_token, tx: tx});
299 | } else {
300 | response.push({tx: tx});
301 | }
302 | } else {
303 | response.push({text: 'Error. Check backend logs.'});
304 | }
305 | }
306 |
307 | return response;
308 | },
309 | });
310 |
311 | server.route({
312 | method: 'POST',
313 | path: '/transfer_nft',
314 | handler: async (request) => {
315 | request = processRequest(request);
316 |
317 | let {
318 | token_id,
319 | receiver_id,
320 | enforce_owner_id,
321 | memo,
322 | contract,
323 | owner_private_key,
324 | } = request.payload;
325 |
326 | const txStatus = await token.TransferNFT(
327 | token_id,
328 | receiver_id,
329 | enforce_owner_id,
330 | memo,
331 | contract,
332 | owner_private_key
333 | );
334 |
335 | if (txStatus.error) {
336 | return txStatus;
337 | } else if (txStatus.status.Failure) {
338 | return {
339 | error:
340 | 'Because of some reason transaction was not applied as expected',
341 | };
342 | } else {
343 | const new_token = await token.ViewNFT(token_id, contract);
344 | if (!new_token) return api.reject('Token not found');
345 |
346 | new_token.tx = txStatus.transaction.hash;
347 | return new_token;
348 | }
349 | },
350 | });
351 |
352 | server.route({
353 | method: 'GET',
354 | path: '/about',
355 | handler: async () => {
356 | const json = require('./package.json');
357 | return "NEAR REST API SERVER Ver. " + json.version;
358 | }
359 | });
360 |
361 | server.route({
362 | method: 'POST',
363 | path: '/explorer',
364 | handler: async (request) => {
365 | let {
366 | user,
367 | host,
368 | database,
369 | password,
370 | port,
371 | query,
372 | parameters
373 | } = request.payload;
374 |
375 | const client = new Client({
376 | user,
377 | host,
378 | database,
379 | password,
380 | port,
381 | });
382 |
383 | if (["104.199.89.51", "35.184.214.98"].includes(host)) {
384 | return api.reject('Please run explorer function only on your own NEAR REST API SERVER instance, https://github.com/near-examples/near-api-rest-server');
385 | }
386 |
387 | try {
388 | client.connect();
389 | let response = await client.query(query, parameters);
390 | return response.rows;
391 | } catch (ex) {
392 | return api.reject('Error. ' + ex.message);
393 | }
394 | },
395 | });
396 |
397 | server.route({
398 | method: 'POST',
399 | path: '/sign_url',
400 | handler: async (request) => {
401 | let {
402 | account_id,
403 | method,
404 | params,
405 | deposit,
406 | gas,
407 | receiver_id,
408 | meta,
409 | callback_url,
410 | network
411 | } = request.payload;
412 |
413 | return blockchain.GetSignUrl(
414 | account_id,
415 | method,
416 | params,
417 | deposit,
418 | gas,
419 | receiver_id,
420 | meta,
421 | callback_url,
422 | network
423 | );
424 | },
425 | });
426 |
427 | await server.start();
428 | console.log('Server running on %s', server.info.uri);
429 | };
430 |
431 | process.on('unhandledRejection', (err) => {
432 | console.log(err);
433 | process.exit(1);
434 | });
435 |
436 | const getServerMethodParams = () => {
437 | return {
438 | generateKey: (params) => {
439 | let hash = crypto.createHash('sha1');
440 | hash.update(JSON.stringify(params));
441 | return hash.digest('base64');
442 | },
443 | cache: {
444 | cache: 'near-api-cache',
445 | expiresIn: ViewCacheExpirationInSeconds * 1000,
446 | generateTimeout: ViewGenerateTimeoutInSeconds * 1000,
447 | getDecoratedValue: true
448 | }
449 | }
450 | };
451 |
452 | const replyCachedValue = (h, {value, cached}) => {
453 | const lastModified = cached ? new Date(cached.stored) : new Date();
454 | return h.response(value).header('Last-Modified', lastModified.toUTCString());
455 | };
456 |
457 | init();
458 |
--------------------------------------------------------------------------------
/blockchain.js:
--------------------------------------------------------------------------------
1 | const nearApi = require('near-api-js');
2 | const api = require('./api');
3 | const fs = require('fs');
4 | const fetch = require('node-fetch');
5 | const {getNetworkFromRpcNode} = require("./api");
6 |
7 | const settings = JSON.parse(fs.readFileSync(api.CONFIG_PATH, 'utf8'));
8 |
9 | module.exports = {
10 | /**
11 | * @return {string}
12 | */
13 | GetSignUrl: async function (account_id, method, params, deposit, gas, receiver_id, meta, callback_url, network) {
14 | try {
15 | if(!network)
16 | network = "mainnet";
17 | const deposit_value = typeof deposit == 'string' ? deposit : nearApi.utils.format.parseNearAmount('' + deposit);
18 | const actions = [method === '!transfer' ? nearApi.transactions.transfer(deposit_value) : nearApi.transactions.functionCall(method, Buffer.from(JSON.stringify(params)), gas, deposit_value)];
19 | const keypair = nearApi.utils.KeyPair.fromRandom('ed25519');
20 | const provider = new nearApi.providers.JsonRpcProvider({url: 'https://rpc.' + network + '.near.org'});
21 | const block = await provider.block({finality: 'final'});
22 | const txs = [nearApi.transactions.createTransaction(account_id, keypair.publicKey, receiver_id, 1, actions, nearApi.utils.serialize.base_decode(block.header.hash))];
23 | const newUrl = new URL('sign', 'https://wallet.' + network + '.near.org/');
24 | newUrl.searchParams.set('transactions', txs.map(transaction => nearApi.utils.serialize.serialize(nearApi.transactions.SCHEMA, transaction)).map(serialized => Buffer.from(serialized).toString('base64')).join(','));
25 | newUrl.searchParams.set('callbackUrl', callback_url);
26 | if (meta)
27 | newUrl.searchParams.set('meta', meta);
28 | return newUrl.href;
29 | } catch (e) {
30 | return api.reject(e);
31 | }
32 | },
33 |
34 | /**
35 | * @return {string}
36 | */
37 | View: async function (recipient, method, params, rpc_node, headers) {
38 | try {
39 | let rpc = rpc_node || settings.rpc_node;
40 | const nearRpc = new nearApi.providers.JsonRpcProvider({url: rpc});
41 |
42 | const account = new nearApi.Account({
43 | provider: nearRpc,
44 | networkId: getNetworkFromRpcNode(rpc),
45 | signer: recipient,
46 | headers: (typeof headers !== undefined) ? headers : {}
47 | },
48 | recipient);
49 | return await account.viewFunction(
50 | recipient,
51 | method,
52 | params
53 | );
54 | } catch (e) {
55 | return api.reject(e);
56 | }
57 | },
58 |
59 | Init: async function (master_account_id, master_key, nft_contract, server_host, server_port, rpc_node) {
60 | try {
61 | const new_settings = settings;
62 | if (master_account_id) new_settings.master_account_id = master_account_id;
63 | if (master_key) new_settings.master_key = master_key;
64 | if (nft_contract) new_settings.nft_contract = nft_contract;
65 | if (server_host) new_settings.server_host = server_host;
66 | if (server_port) new_settings.server_port = server_port;
67 | if (rpc_node) new_settings.rpc_node = rpc_node;
68 |
69 | await fs.promises.writeFile(api.CONFIG_PATH, JSON.stringify({
70 | ...new_settings
71 | }));
72 |
73 | return api.notify("Settings updated.");
74 | } catch (e) {
75 | return api.reject(e);
76 | }
77 | },
78 |
79 | GetBalance: async function (account_id) {
80 | try {
81 | const body = {
82 | jsonrpc: '2.0',
83 | id: "dontcare",
84 | method: "query",
85 | params: {
86 | request_type: "view_account",
87 | finality: "final",
88 | account_id: account_id
89 | }
90 | };
91 |
92 | return fetch(settings.rpc_node, {
93 | method: 'post',
94 | body: JSON.stringify(body),
95 | headers: {'Content-Type': 'application/json'}
96 | })
97 | .then(res => res.json())
98 | .then(json => {
99 | if (json.error)
100 | return api.reject(json.error.data);
101 |
102 | return json.result.amount
103 | });
104 | } catch (e) {
105 | return api.reject(e);
106 | }
107 | },
108 |
109 | DeployContract: async function (account_id, private_key, contract_file) {
110 | try {
111 | const path = `contracts/${contract_file}`;
112 | if (!fs.existsSync(path))
113 | return api.reject("Contract not found");
114 |
115 | const account = await this.GetAccountByKey(account_id, private_key);
116 |
117 | const data = [...fs.readFileSync(path)];
118 | const txs = [nearApi.transactions.deployContract(data)];
119 |
120 | let res = await account.signAndSendTransaction(account_id, txs);
121 |
122 | if (contract_file === "nft_simple.wasm")
123 | await this.Call(account_id, private_key, 0, "100000000000000",
124 | account_id, "new", {"owner_id": account_id});
125 |
126 | return res;
127 | } catch (e) {
128 | return api.reject(e);
129 | }
130 | },
131 |
132 | Call: async function (account_id, private_key, attached_tokens, attached_gas, recipient, method, params, network, rpc_node, headers) {
133 | try {
134 | const account = await this.GetAccountByKey(account_id, private_key, network, rpc_node, headers);
135 |
136 | return await account.functionCall({
137 | contractId: recipient,
138 | methodName: method,
139 | args: params,
140 | gas: attached_gas,
141 | attachedDeposit: attached_tokens
142 | });
143 | } catch (e) {
144 | return api.reject(e);
145 | }
146 | },
147 |
148 | GetMasterAccount: async function () {
149 | try {
150 | const keyPair = nearApi.utils.KeyPair.fromString(settings.master_key);
151 | const keyStore = new nearApi.keyStores.InMemoryKeyStore();
152 | keyStore.setKey("testnet", settings.master_account_id, keyPair);
153 |
154 | const near = await nearApi.connect({
155 | networkId: "testnet",
156 | deps: {keyStore},
157 | masterAccount: settings.master_account_id,
158 | nodeUrl: settings.rpc_node
159 | });
160 |
161 | return await near.account(settings.master_account_id);
162 | } catch (e) {
163 | return api.reject(e);
164 | }
165 | },
166 |
167 | GetUserAccount: async function (accountId) {
168 | try {
169 | const user = require('./user');
170 |
171 | const account_raw = await user.GetAccount(accountId);
172 | const account = JSON.parse(account_raw);
173 |
174 | const keyPair = nearApi.utils.KeyPair.fromString(account.private_key);
175 | const keyStore = new nearApi.keyStores.InMemoryKeyStore();
176 | keyStore.setKey("testnet", account.account_id, keyPair);
177 |
178 | const near = await nearApi.connect({
179 | networkId: "testnet",
180 | deps: {keyStore},
181 | masterAccount: account.account_id,
182 | nodeUrl: settings.rpc_node
183 | });
184 |
185 | return await near.account(account.account_id);
186 | } catch (e) {
187 | return api.reject(e);
188 | }
189 | },
190 |
191 | GetAccountByKey: async function (account_id, private_key, network, rpc_node, headers) {
192 | try {
193 | network = network || "testnet";
194 | rpc_node = rpc_node || settings.rpc_node;
195 |
196 | private_key = private_key.replace('"', '');
197 |
198 | const keyPair = nearApi.utils.KeyPair.fromString(private_key);
199 | const keyStore = new nearApi.keyStores.InMemoryKeyStore();
200 | keyStore.setKey(network, account_id, keyPair);
201 |
202 | const near = await nearApi.connect({
203 | networkId: network,
204 | deps: {keyStore},
205 | masterAccount: account_id,
206 | nodeUrl: rpc_node,
207 | headers: (typeof headers !== undefined) ? headers : {}
208 | });
209 |
210 | return await near.account(account_id);
211 | } catch (e) {
212 | return api.reject(e);
213 | }
214 | }
215 | };
216 |
--------------------------------------------------------------------------------
/contracts/nft_simple.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/near-examples/near-api-rest-server/0d3a18e0bf161306223140b19a034bf39c576ead/contracts/nft_simple.wasm
--------------------------------------------------------------------------------
/examples/nft_deploy/README.MD:
--------------------------------------------------------------------------------
1 | **INITIALIZE** you NFT contract for multi token types and batch **MINT** each type of token with perpetual royalties.
2 |
3 | Contract used: [NFT-Simple](https://github.com/near-apps/nft-market/tree/main/contracts).
4 |
5 | [Init & mint example](https://explorer.testnet.near.org/accounts/dev-1621541447792-38210652756946).
--------------------------------------------------------------------------------
/examples/nft_deploy/deploy_tokens.js:
--------------------------------------------------------------------------------
1 | const {data} = require('./token_types.js');
2 | const homedir = require("os").homedir();
3 | const path = require("path");
4 | const fs = require("fs");
5 | const fetch = require("node-fetch");
6 |
7 | const API_SERVER_URL = "http://rest.nearapi.org";
8 | const CONTRACT_ID = "dev-1621541447792-38210652756946";
9 | const ACCOUNT_ID = "zavodil.testnet";
10 | const CREDENTIALS_DIR = ".near-credentials/testnet/";
11 |
12 | const run = async () => {
13 | const tokens = data.map(({token_type, metadata}, i) => {
14 | return {
15 | token_type,
16 | token_id: token_type + '_1',
17 | metadata: {
18 | ...metadata,
19 | issued_at: Date.now().toString(),
20 | },
21 | perpetual_royalties: {
22 | ['escrow-' + (i + 1) + '.nft.near']: 1000, //10%
23 | "account-2.near": 500, // 5%
24 | "account-3.near": 100 // 1%
25 | }
26 | }
27 | });
28 |
29 | // initial add_token_types: 100 tokens of each type
30 | const supply_cap_by_type = tokens.map(({token_type}) => ({
31 | [token_type]: '100'
32 | })).reduce((a, c) => ({...a, ...c}), {});
33 |
34 | let tx = await init(CONTRACT_ID, supply_cap_by_type);
35 | console.log(tx);
36 |
37 | // initial mint 1 token of every type
38 | for (let i = 0; i < tokens.length; i++) {
39 | const token = tokens[i];
40 | tx = await mint(CONTRACT_ID, token);
41 | console.log(tx)
42 | }
43 | return "Finish"
44 | };
45 |
46 | const init = async function (contract, supply_cap_by_type) {
47 | const body = {
48 | method: 'new',
49 | contract: contract,
50 |
51 | params: {
52 | owner_id: ACCOUNT_ID,
53 | metadata: {
54 | "spec": "nft-1",
55 | "name": "NAME",
56 | "symbol": "NFT"
57 | },
58 | unlocked: true,
59 | supply_cap_by_type: supply_cap_by_type,
60 | },
61 |
62 | account_id: ACCOUNT_ID,
63 | private_key: await getPrivateKey(ACCOUNT_ID),
64 | attached_gas: "300000000000000",
65 | attached_tokens: ""
66 | };
67 |
68 | return await PostResponse("call", body);
69 | };
70 |
71 | const mint = async function (contract, token) {
72 | const body = {
73 | method: 'nft_mint',
74 | contract: contract,
75 |
76 | params: {
77 | token_id: token.token_id,
78 | metadata: token.metadata,
79 | perpetual_royalties: token.perpetual_royalties,
80 | token_type: token.token_type,
81 | },
82 |
83 | account_id: ACCOUNT_ID,
84 | private_key: await getPrivateKey(ACCOUNT_ID),
85 | attached_gas: "100000000000000",
86 | attached_tokens: "20000000000000000000000"
87 | };
88 |
89 | return await PostResponse("call", body);
90 | };
91 |
92 | const PostResponse = async (operation, body, options) => {
93 | const response = fetch(`${API_SERVER_URL}/${operation}`, {
94 | method: 'POST',
95 | body: JSON.stringify(body),
96 | headers: {
97 | 'Content-type': 'application/json; charset=UTF-8'
98 | }
99 | })
100 | .then(res => {
101 | return res.text().then(response => {
102 | if (options && options.convertToNear) {
103 | return module.exports.RoundFloat(module.exports.ConvertYoctoNear(response, config.FRACTION_DIGITS));
104 | } else {
105 | try {
106 | const json = JSON.parse(response);
107 | try {
108 | if (json.error)
109 | return (JSON.parse(json.error));
110 | else {
111 | return (json);
112 | }
113 | } catch (e) {
114 | throw new Error("PostResponse error for " + operation + " request " + JSON.stringify(body) + ". Error: " + e.message);
115 | }
116 | } catch {
117 | return response;
118 | }
119 | }
120 | });
121 |
122 | });
123 | return response;
124 | };
125 |
126 | const getPrivateKey = async (accountId) => {
127 | const credentialsPath = path.join(homedir, CREDENTIALS_DIR);
128 | const keyPath = credentialsPath + accountId + '.json';
129 | try {
130 | const credentials = JSON.parse(fs.readFileSync(keyPath));
131 | return (credentials.private_key);
132 | } catch (e) {
133 | throw new Error("Key not found for account " + keyPath + ". Error: " + e.message);
134 | }
135 | };
136 |
137 | run().then(r => console.log(r));
--------------------------------------------------------------------------------
/examples/nft_deploy/token_types.js:
--------------------------------------------------------------------------------
1 | const prefix = "test";
2 | module.exports = {
3 | data: [
4 | {
5 | token_type: prefix + '_name_1',
6 | metadata: {media: 'hash_1'}
7 | },
8 | {
9 | token_type: prefix + '_name_2',
10 | metadata: {media: 'hash_2'}
11 | },
12 | {
13 | token_type: prefix + '_name_3',
14 | metadata: {media: 'hash_3'}
15 | },
16 | ]
17 | };
--------------------------------------------------------------------------------
/near-api-server.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "server_host": "localhost",
3 | "server_port": 3000,
4 | "rpc_node": "https://rpc.testnet.near.org",
5 | "init_disabled": true
6 | }
--------------------------------------------------------------------------------
/near-api-ui/README.md:
--------------------------------------------------------------------------------
1 | # Getting Started with Create React App
2 |
3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
4 |
5 | ## Available Scripts
6 |
7 | In the project directory, you can run:
8 |
9 | ### `yarn start`
10 |
11 | Runs the app in the development mode.\
12 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
13 |
14 | The page will reload if you make edits.\
15 | You will also see any lint errors in the console.
16 |
17 | ### `yarn test`
18 |
19 | Launches the test runner in the interactive watch mode.\
20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
21 |
22 | ### `yarn build`
23 |
24 | Builds the app for production to the `build` folder.\
25 | It correctly bundles React in production mode and optimizes the build for the best performance.
26 |
27 | The build is minified and the filenames include the hashes.\
28 | Your app is ready to be deployed!
29 |
30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
31 |
32 | ### `yarn eject`
33 |
34 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!**
35 |
36 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
37 |
38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
39 |
40 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
41 |
42 | ## Learn More
43 |
44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
45 |
46 | To learn React, check out the [React documentation](https://reactjs.org/).
47 |
48 | ### Code Splitting
49 |
50 | This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
51 |
52 | ### Analyzing the Bundle Size
53 |
54 | This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
55 |
56 | ### Making a Progressive Web App
57 |
58 | This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
59 |
60 | ### Advanced Configuration
61 |
62 | This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
63 |
64 | ### Deployment
65 |
66 | This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
67 |
68 | ### `yarn build` fails to minify
69 |
70 | This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
71 |
--------------------------------------------------------------------------------
/near-api-ui/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "near-api-ui",
3 | "version": "0.1.0",
4 | "license": "UNLICENSED",
5 | "scripts": {
6 | "build": "npm run build:contract && npm run build:web",
7 | "build:contract": "node contract/compile.js",
8 | "build:contract:debug": "node contract/compile.js --debug",
9 | "build:web": "export NODE_ENV=mainnet && rm -r mainnet && parcel build src/index.html --public-url ./ && mv dist mainnet",
10 | "dev:deploy:contract": "near dev-deploy",
11 | "deploy:contract": "near deploy",
12 | "deploy:pages": "gh-pages -d dist/",
13 | "deploy": "npm run build && npm run deploy:contract && npm run deploy:pages",
14 | "prestart": "npm run build:contract:debug && npm run dev:deploy:contract",
15 | "start": "export PORT=39106 && echo The app is starting! It will automatically open in your browser when ready && env-cmd -f ./neardev/dev-account.env parcel src/index.html --open",
16 | "dev": "export PORT=39106 && nodemon --watch contract/src -e rs --exec \"npm run start\"",
17 | "test": "npm run build:contract:debug && cd contract && cargo test -- --nocapture && cd .. && jest test --runInBand",
18 | "build:web:testnet": "export NODE_ENV=testnet && rm -rf testnet && parcel build src/index.html --public-url ./ && mv dist testnet",
19 | "build:contract:testnet": "export NODE_ENV=testnet && node contract/compile.js",
20 | "deploy:contract:testnet": "export NODE_ENV=testnet && near deploy",
21 | "build:web:all": "export NODE_ENV=mainnet && rm -r mainnet && parcel build src/index.html --public-url ./ && mv dist mainnet && export NODE_ENV=testnet && rm -r testnet && parcel build src/index.html --public-url ./ && mv dist testnet",
22 | "dev:clear": "rm -r neardev"
23 | },
24 | "devDependencies": {
25 | "@babel/core": "~7.12.3",
26 | "@babel/preset-env": "~7.12.1",
27 | "@babel/preset-react": "~7.12.5",
28 | "babel-jest": "~26.6.2",
29 | "bn.js": "^5.1.1",
30 | "env-cmd": "~10.1.0",
31 | "gh-pages": "~3.1.0",
32 | "jest": "~26.6.2",
33 | "jest-environment-node": "~26.6.2",
34 | "near-api-js": "~0.36.2",
35 | "near-cli": "~2.0.0",
36 | "nodemon": "~2.0.3",
37 | "parcel-bundler": "~1.12.4",
38 | "react-file-reader": "~1.1.4",
39 | "react-test-renderer": "~17.0.1",
40 | "react-tooltip": "~4.2.11",
41 | "shelljs": "~0.8.4"
42 | },
43 | "dependencies": {
44 | "json-url": "^3.0.0",
45 | "query-string": "^4.3.4",
46 | "react": "~17.0.1",
47 | "react-dom": "~17.0.1",
48 | "react-json-view": "~1.21.3",
49 | "regenerator-runtime": "~0.13.5"
50 | },
51 | "jest": {
52 | "moduleNameMapper": {
53 | "\\.(jpg|ico|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/src/__mocks__/fileMock.js",
54 | "\\.(css|less)$": "/src/__mocks__/fileMock.js"
55 | },
56 | "setupFiles": [
57 | "/src/jest.init.js"
58 | ],
59 | "testEnvironment": "near-cli/test_environment",
60 | "testPathIgnorePatterns": [
61 | "/contract/",
62 | "/node_modules/"
63 | ]
64 | },
65 | "browserslist": {
66 | "production": [
67 | ">0.2%",
68 | "not dead",
69 | "not op_mini all"
70 | ],
71 | "development": [
72 | "last 1 chrome version",
73 | "last 1 firefox version",
74 | "last 1 safari version"
75 | ]
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/near-api-ui/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/near-examples/near-api-rest-server/0d3a18e0bf161306223140b19a034bf39c576ead/near-api-ui/public/favicon.ico
--------------------------------------------------------------------------------
/near-api-ui/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 | You need to enable JavaScript to run this app.
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/near-api-ui/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/near-examples/near-api-rest-server/0d3a18e0bf161306223140b19a034bf39c576ead/near-api-ui/public/logo192.png
--------------------------------------------------------------------------------
/near-api-ui/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/near-examples/near-api-rest-server/0d3a18e0bf161306223140b19a034bf39c576ead/near-api-ui/public/logo512.png
--------------------------------------------------------------------------------
/near-api-ui/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/near-api-ui/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/near-api-ui/src/App.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: BwSeidoRound, -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
5 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
6 | sans-serif;
7 | }
8 |
9 | .App {
10 |
11 | }
12 |
13 | .hidden{
14 | display: none;
15 | }
16 |
17 | .option-buttons{
18 | padding-top: 10px;
19 | }
20 |
21 | .option-buttons input{
22 | margin: 0 5px;
23 | }
24 |
25 | .error{
26 | color: red;
27 | }
28 |
29 | .error, .processed{
30 | padding-bottom: 10px;
31 | }
32 |
33 | @keyframes App-logo-spin {
34 | from {
35 | transform: rotate(0deg);
36 | }
37 | to {
38 | transform: rotate(360deg);
39 | }
40 | }
41 |
42 | .input-query{
43 | width: 700px;
44 | font-size: larger;
45 | }
46 |
47 | .query-options{
48 | display: block;
49 | width: 700px
50 | }
51 |
52 | .button-query{
53 | font-size: larger;
54 | }
55 |
56 | h1{
57 | margin: 5px 0;
58 | }
59 |
60 | h1 a, h1 a:hover, h1 a:visited{
61 | text-decoration: none;
62 | color: #000;
63 | }
64 |
65 | .github-hint{
66 | padding: 10px 0 0 20px;
67 | color: rgba(0, 0, 0, 0.8);
68 | }
69 |
70 | .json-response{
71 | display: inline-block;
72 | padding-left: 20px;
73 | }
74 |
75 | @font-face {
76 | font-family: BwSeidoRound;
77 | src: url(./fonts/389947_6_0.eot);
78 | src: url(./fonts/389947_6_0.eot?#iefix) format("embedded-opentype"), url(./fonts/389947_6_0.woff2) format("woff2"), url(./fonts/389947_6_0.woff) format("woff"), url(./fonts/389947_6_0.ttf) format("truetype");
79 | font-weight: 500;
80 | font-style: normal
81 | }
82 |
--------------------------------------------------------------------------------
/near-api-ui/src/App.js:
--------------------------------------------------------------------------------
1 | import 'regenerator-runtime/runtime'
2 | import React, {useState} from 'react';
3 | import {login, logout} from './utils'
4 | import './App.css';
5 | import * as nearAPI from 'near-api-js'
6 | import ReactJson from 'react-json-view'
7 |
8 | const codec = require('json-url')('lzw');
9 | const queryString = require('query-string');
10 |
11 | import {API_SERVER_URL, MAINNET_RPC} from './config'
12 |
13 | function App() {
14 | const [processing, setProcessing] = useState(false);
15 | const [processedTime, setProcessedTime] = useState(0);
16 | const [errorFlag, setErrorFlag] = useState(false);
17 | const [signedIn, setSignedIn] = useState(false);
18 | const [gasAttached, setGasAttached] = useState("100000000000000");
19 | const [tokensAttached, setTokensAttached] = useState("1000000000000000000000000");
20 | const [showCallOptions, setShowCallOptions] = useState(false);
21 | const [request, setRequest] = useState("near view lunanova.pool.f863973.m0 get_accounts '{\"from_index\": 0, \"limit\": 100}'");
22 | const [response, setResponse] = useState({});
23 | const [viewNetworkTestnet, setViewNetworkTestnet] = useState(true);
24 | const [viewNetworkDisabled, setViewNetworkDisabled] = useState(false);
25 |
26 | let firstLoad = true;
27 |
28 | const _handleKeyDown = function (e) {
29 | if (e.key === 'Enter') {
30 | _sendForm()
31 | }
32 | }
33 |
34 | const _sendForm = async () => {
35 | try {
36 |
37 | const call = request.toLowerCase().split(/('.*?'|".*?"|\S+)/g);
38 |
39 | if (
40 | !(call[1] || call[3] || call[5]) ||
41 | call[1] !== 'near' ||
42 | !['view', 'call'].includes(call[3])) {
43 | setResponse({error: "Illegal command"});
44 | } else if (call[3] === 'call' && !signedIn) {
45 | setResponse({error: "Sign In to send call requests"});
46 | } else {
47 | if (!call[9])
48 | call[9] = "{}";
49 |
50 | let params;
51 | try {
52 | params = JSON.parse(call[9].replaceAll("'", ""));
53 | } catch (e) {
54 | console.error("Invalid Params");
55 | params = "{}";
56 | }
57 |
58 | let body = {
59 | contract: call[5],
60 | method: call[7],
61 | params: params
62 | };
63 |
64 | if (call[3] === 'call') {
65 | const private_key = window.walletConnection._keyStore.localStorage[`near-api-js:keystore:${window.accountId}:testnet`];
66 | if (!private_key) {
67 | setResponse({error: "Key wasn't found to sign call request"});
68 | return;
69 | }
70 |
71 | body = {
72 | ...body,
73 | account_id: window.accountId,
74 | private_key: private_key,
75 | attached_gas: gasAttached,
76 | attached_tokens: tokensAttached,
77 | }
78 | } else {
79 | if (!viewNetworkTestnet)
80 | body = {
81 | ...body,
82 | rpc_node: MAINNET_RPC
83 | }
84 |
85 | SetViewQueryUrl(body);
86 | }
87 |
88 | await GetResponseFromNear(call[3], body);
89 |
90 | }
91 | } catch (err) {
92 | setResponse({error: "Illegal query"});
93 | console.log(err);
94 | }
95 | }
96 |
97 | const GetResponseFromNear = async (method, body) => {
98 | console.log("GetResponseFromNear")
99 | const t0 = performance.now();
100 | setProcessing(true);
101 |
102 | return await fetch(`${API_SERVER_URL}/${method}`, {
103 | method: 'POST',
104 | body: JSON.stringify(body),
105 | headers: {
106 | 'Content-type': 'application/json; charset=UTF-8'
107 | }
108 | })
109 | .then(res => res.json())
110 | .then(res => {
111 | if (res.error)
112 | setResponse(JSON.parse(res.error));
113 | else
114 | setResponse(res);
115 |
116 | setProcessing(false)
117 | setErrorFlag(!!res.error);
118 | setProcessedTime(Math.round((performance.now() - t0) / 10) / 100);
119 | })
120 | };
121 |
122 | React.useEffect(
123 | async () => {
124 | if (window.walletConnection.isSignedIn()) {
125 | setSignedIn(true)
126 | }
127 |
128 | if (firstLoad && location.search) {
129 | const query = JSON.parse(JSON.stringify(queryString.parse(location.search)));
130 | if (query && query.hasOwnProperty("q")) {
131 | codec.decompress(query.q).then(async (json) => {
132 | console.log("Loading url query...");
133 | console.log(json);
134 | console.log(`near view ${json.contract} ${json.method} '${JSON.stringify(json.params)}'`);
135 | setRequest(`near view ${json.contract} ${json.method} '${JSON.stringify(json.params)}'`);
136 | setViewNetworkTestnet(json.rpc_node !== MAINNET_RPC);
137 | await GetResponseFromNear("view", json);
138 | });
139 | }
140 | }
141 |
142 | firstLoad = false;
143 | },
144 | []
145 | );
146 |
147 | React.useEffect(() => {
148 | const timeOutId = setTimeout(() => UpdateQuery(request), 500);
149 | return () => clearTimeout(timeOutId);
150 | }, [request]);
151 |
152 | const SetViewQueryUrl = (request) => {
153 | codec.compress(request).then(compressed_string => {
154 | const url = location.protocol + '//' + location.host + location.pathname + '?q=' + compressed_string;
155 | window.history.replaceState({}, document.title, url);
156 | console.log(url)
157 | });
158 | }
159 |
160 | const SignButton = () => {
161 | return (signedIn ?
162 | {window.accountId}
163 | Sign out
164 |
165 | : Sign in
166 |
167 | )
168 | }
169 |
170 | const JsonOutput = () => {
171 | if(!response || (IsObject(response) && !Object.keys(response).length))
172 | return null;
173 |
174 | return IsObject(response)
175 | ?
176 | : {response} ;
177 | };
178 |
179 | const IsObject = (obj) => {
180 | return obj !== undefined && obj !== null && typeof obj == 'object';
181 | }
182 |
183 | const UpdateQuery = (query) => {
184 | query = query.toLowerCase();
185 | const isCall = query.startsWith("near call");
186 | setShowCallOptions(isCall);
187 | setViewNetworkDisabled(isCall);
188 | if (isCall)
189 | setViewNetworkTestnet(true);
190 | };
191 |
192 | return (
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
204 |
205 |
Query
206 |
Network:
207 |
208 | {
211 | setViewNetworkTestnet(e.target.checked)
212 | }}/>
213 | Testnet
214 |
215 |
216 |
217 |
218 | setRequest(e.target.value)}
221 | />
222 |
223 | {processing ? "Processing" : "Send"}
224 |
225 |
226 |
227 |
228 |
229 | Gas Attached
230 | setGasAttached(e.target.value)}
232 | />
233 |
234 | Tokens Attached
235 | setTokensAttached(e.target.value)}
237 | />
238 |
239 |
240 |
241 |
242 |
243 | {errorFlag
244 | ?
ERROR!
245 | : (processedTime
246 | ?
Process time: {processedTime} seconds
247 | : null)
248 | }
249 |
250 |
251 |
252 |
Interact with the NEAR blockchain using a simple REST API.
253 |
Github
254 |
255 |
256 |
257 |
258 | );
259 | }
260 |
261 | export default App;
262 |
--------------------------------------------------------------------------------
/near-api-ui/src/App.test.js:
--------------------------------------------------------------------------------
1 | import { render, screen } from '@testing-library/react';
2 | import App from './App';
3 |
4 | test('renders learn react link', () => {
5 | render( );
6 | const linkElement = screen.getByText(/learn react/i);
7 | expect(linkElement).toBeInTheDocument();
8 | });
9 |
--------------------------------------------------------------------------------
/near-api-ui/src/assets/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/near-examples/near-api-rest-server/0d3a18e0bf161306223140b19a034bf39c576ead/near-api-ui/src/assets/android-chrome-192x192.png
--------------------------------------------------------------------------------
/near-api-ui/src/assets/android-chrome-384x384.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/near-examples/near-api-rest-server/0d3a18e0bf161306223140b19a034bf39c576ead/near-api-ui/src/assets/android-chrome-384x384.png
--------------------------------------------------------------------------------
/near-api-ui/src/assets/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/near-examples/near-api-rest-server/0d3a18e0bf161306223140b19a034bf39c576ead/near-api-ui/src/assets/apple-touch-icon.png
--------------------------------------------------------------------------------
/near-api-ui/src/assets/explorer-bg.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/near-api-ui/src/assets/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/near-examples/near-api-rest-server/0d3a18e0bf161306223140b19a034bf39c576ead/near-api-ui/src/assets/favicon-16x16.png
--------------------------------------------------------------------------------
/near-api-ui/src/assets/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/near-examples/near-api-rest-server/0d3a18e0bf161306223140b19a034bf39c576ead/near-api-ui/src/assets/favicon-32x32.png
--------------------------------------------------------------------------------
/near-api-ui/src/assets/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/near-examples/near-api-rest-server/0d3a18e0bf161306223140b19a034bf39c576ead/near-api-ui/src/assets/favicon.ico
--------------------------------------------------------------------------------
/near-api-ui/src/assets/icon-network-right.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/near-api-ui/src/assets/logo-black.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/near-api-ui/src/assets/logo-white.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/near-api-ui/src/assets/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/near-api-ui/src/assets/mstile-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/near-examples/near-api-rest-server/0d3a18e0bf161306223140b19a034bf39c576ead/near-api-ui/src/assets/mstile-150x150.png
--------------------------------------------------------------------------------
/near-api-ui/src/assets/site.webmanifest:
--------------------------------------------------------------------------------
1 | {
2 | "name": "",
3 | "short_name": "",
4 | "icons": [
5 | {
6 | "src": "/assets/android-chrome-192x192.png",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | },
10 | {
11 | "src": "/assets/android-chrome-384x384.png",
12 | "sizes": "384x384",
13 | "type": "image/png"
14 | }
15 | ],
16 | "theme_color": "#ffffff",
17 | "background_color": "#ffffff",
18 | "display": "standalone"
19 | }
20 |
--------------------------------------------------------------------------------
/near-api-ui/src/config.js:
--------------------------------------------------------------------------------
1 | const CONTRACT_NAME = process.env.CONTRACT_NAME || "null_address.testnet";
2 |
3 | module.exports = {
4 | API_SERVER_URL: "http://rest.nearspace.info",
5 | MAINNET_RPC: "https://rpc.mainnet.near.org",
6 | getConfig: (env) => {
7 | switch (env) {
8 |
9 | case 'production':
10 | case 'mainnet':
11 | return {
12 | networkId: 'mainnet',
13 | nodeUrl: 'https://rpc.mainnet.near.org',
14 | contractName: CONTRACT_NAME || "null_address.near",
15 | walletUrl: 'https://wallet.near.org',
16 | helperUrl: 'https://helper.mainnet.near.org',
17 | explorerUrl: 'https://explorer.mainnet.near.org',
18 | }
19 | case 'development':
20 | case 'testnet':
21 | return {
22 | networkId: 'testnet',
23 | nodeUrl: 'https://rpc.testnet.near.org',
24 | contractName: CONTRACT_NAME || "null_address.testnet",
25 | walletUrl: 'https://wallet.testnet.near.org',
26 | helperUrl: 'https://helper.testnet.near.org',
27 | explorerUrl: 'https://explorer.testnet.near.org',
28 | }
29 | case 'betanet':
30 | return {
31 | networkId: 'betanet',
32 | nodeUrl: 'https://rpc.betanet.near.org',
33 | contractName: CONTRACT_NAME,
34 | walletUrl: 'https://wallet.betanet.near.org',
35 | helperUrl: 'https://helper.betanet.near.org',
36 | explorerUrl: 'https://explorer.betanet.near.org',
37 | }
38 | case 'local':
39 | return {
40 | networkId: 'local',
41 | nodeUrl: 'http://localhost:3030',
42 | keyPath: `${process.env.HOME}/.near/validator_key.json`,
43 | walletUrl: 'http://localhost:4000/wallet',
44 | contractName: CONTRACT_NAME,
45 | }
46 | case 'test':
47 | case 'ci':
48 | return {
49 | networkId: 'shared-test',
50 | nodeUrl: 'https://rpc.ci-testnet.near.org',
51 | contractName: CONTRACT_NAME,
52 | masterAccount: 'test.near',
53 | }
54 | case 'ci-betanet':
55 | return {
56 | networkId: 'shared-test-staging',
57 | nodeUrl: 'https://rpc.ci-betanet.near.org',
58 | contractName: CONTRACT_NAME,
59 | masterAccount: 'test.near',
60 | }
61 | default:
62 | throw Error(`Unconfigured environment '${env}'. Can be configured in src/config.js.`)
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/near-api-ui/src/fonts/389947_6_0.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/near-examples/near-api-rest-server/0d3a18e0bf161306223140b19a034bf39c576ead/near-api-ui/src/fonts/389947_6_0.eot
--------------------------------------------------------------------------------
/near-api-ui/src/fonts/389947_6_0.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/near-examples/near-api-rest-server/0d3a18e0bf161306223140b19a034bf39c576ead/near-api-ui/src/fonts/389947_6_0.ttf
--------------------------------------------------------------------------------
/near-api-ui/src/fonts/389947_6_0.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/near-examples/near-api-rest-server/0d3a18e0bf161306223140b19a034bf39c576ead/near-api-ui/src/fonts/389947_6_0.woff
--------------------------------------------------------------------------------
/near-api-ui/src/fonts/389947_6_0.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/near-examples/near-api-rest-server/0d3a18e0bf161306223140b19a034bf39c576ead/near-api-ui/src/fonts/389947_6_0.woff2
--------------------------------------------------------------------------------
/near-api-ui/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/near-api-ui/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | NEAR REST API
13 |
14 |
15 | You need to enable JavaScript to run this app.
16 |
17 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/near-api-ui/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 | import App from './App'
4 | import { initContract } from './utils'
5 |
6 | window.nearInitPromise = initContract()
7 | .then(() => {
8 | ReactDOM.render(
9 | ,
10 | document.querySelector('#root')
11 | )
12 | })
13 | .catch(console.error)
14 |
--------------------------------------------------------------------------------
/near-api-ui/src/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/near-api-ui/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
7 | getLCP(onPerfEntry);
8 | getTTFB(onPerfEntry);
9 | });
10 | }
11 | };
12 |
13 | export default reportWebVitals;
14 |
--------------------------------------------------------------------------------
/near-api-ui/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/near-api-ui/src/utils.js:
--------------------------------------------------------------------------------
1 | import {connect, Contract, keyStores, WalletConnection} from 'near-api-js'
2 | import {getConfig} from './config'
3 |
4 | const nearConfig = getConfig(process.env.NODE_ENV || 'development')
5 |
6 | // Initialize contract & set global variables
7 | export async function initContract() {
8 | // Initialize connection to the NEAR testnet
9 | const near = await connect(Object.assign({deps: {keyStore: new keyStores.BrowserLocalStorageKeyStore()}}, nearConfig))
10 |
11 | // Initializing Wallet based Account. It can work with NEAR testnet wallet that
12 | // is hosted at https://wallet.testnet.near.org
13 | window.walletConnection = new WalletConnection(near)
14 |
15 | // Getting the Account ID. If still unauthorized, it's just empty string
16 | window.accountId = window.walletConnection.getAccountId()
17 |
18 | // Initializing our contract APIs by contract name and configuration
19 | window.contract = await new Contract(window.walletConnection.account(), nearConfig.contractName, {
20 | // View methods are read only. They don't modify the state, but usually return some value.
21 | viewMethods: ['get_deposit'],
22 | // Change methods can modify the state. But you don't receive the returned value when called.
23 | changeMethods: ['deposit', 'multisend_from_balance', 'multisend_attached_tokens'],
24 | })
25 | }
26 |
27 | export function logout() {
28 | window.walletConnection.signOut()
29 | // reload page
30 | window.location.replace(window.location.origin + window.location.pathname)
31 | }
32 |
33 | export function login() {
34 | // Allow the current app to make calls to the specified contract on the
35 | // user's behalf.
36 | // This works by creating a new access key for the user's account and storing
37 | // the private key in localStorage.
38 | window.walletConnection.requestSignIn("", "NEAR REST API")
39 | }
40 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "near-api-server",
3 | "version": "1.1.0",
4 | "description": "",
5 | "main": "app.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "Vadim Ilin",
10 | "license": "ISC",
11 | "dependencies": {
12 | "@hapi/catbox-memory": "^5.0.1",
13 | "@hapi/hapi": "^18.1.0",
14 | "body-parser": "~1.19.0",
15 | "faker": "~5.4.0",
16 | "fs": "*",
17 | "near-api-js": "~0.44.1",
18 | "near-cli": "^2.2.0",
19 | "near-seed-phrase": "^0.1.0",
20 | "node-fetch": "^2.6.1",
21 | "path": "^0.12.7",
22 | "pg": "^8.6.0"
23 | },
24 | "bin": {
25 | "near-api-server": "./app.js"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/token.js:
--------------------------------------------------------------------------------
1 | const blockchain = require('./blockchain');
2 | const api = require('./api');
3 |
4 | const fs = require('fs');
5 | const settings = JSON.parse(fs.readFileSync(api.CONFIG_PATH, 'utf8'));
6 |
7 | module.exports = {
8 |
9 | /**
10 | * @return {string}
11 | */
12 | ViewNFT: async function (tokenId, contract) {
13 | try {
14 | const nftContract = contract ? contract : settings.nft_contract;
15 | return await blockchain.View(
16 | nftContract,
17 | "nft_token",
18 | {token_id: tokenId}
19 | );
20 | } catch (e) {
21 | return api.reject(e);
22 | }
23 | },
24 |
25 | /**
26 | * @return {string}
27 | */
28 | MintNFT: async function (tokenId, metadata, contractAccountId, account_id, private_key) {
29 | const nftContract = contractAccountId ? contractAccountId : settings.nft_contract;
30 |
31 | let account = !(account_id && private_key)
32 | ? await blockchain.GetMasterAccount()
33 | : await blockchain.GetAccountByKey(account_id, private_key);
34 |
35 | try {
36 | const tx = await account.functionCall(
37 | nftContract,
38 | "nft_mint",
39 | {
40 | "token_id": tokenId,
41 | "metadata": metadata
42 | },
43 | '100000000000000',
44 | '10000000000000000000000');
45 |
46 | if (!tx.status.Failure)
47 | return tx.transaction.hash
48 | } catch (e) {
49 | return api.reject(e);
50 | }
51 | },
52 |
53 | TransferNFT: async function (tokenId, receiverId, enforceOwnerId, memo, contractAccountId, owner_private_key) {
54 | try {
55 | const nftContract = contractAccountId ? contractAccountId : settings.nft_contract;
56 | let account;
57 |
58 | account = !(enforceOwnerId && owner_private_key)
59 | ? ((enforceOwnerId === settings.master_account_id)
60 | ? await blockchain.GetMasterAccount()
61 | : await blockchain.GetUserAccount(enforceOwnerId))
62 | : await blockchain.GetAccountByKey(enforceOwnerId, owner_private_key);
63 |
64 | return await account.functionCall(
65 | nftContract,
66 | "nft_transfer",
67 | {
68 | "token_id": tokenId,
69 | "receiver_id": receiverId,
70 | "enforce_owner_id": enforceOwnerId,
71 | "memo": memo
72 | },
73 | '100000000000000',
74 | '1');
75 | } catch (e) {
76 | return api.reject(e);
77 | }
78 | }
79 | };
80 |
--------------------------------------------------------------------------------
/user.js:
--------------------------------------------------------------------------------
1 | const nearApi = require('near-api-js');
2 | const blockchain = require('./blockchain');
3 | const nearSeedPhrase = require('near-seed-phrase');
4 | const fs = require('fs');
5 |
6 | const storageFolder = "storage";
7 |
8 | module.exports = {
9 | GenerateKeyPair: async function () {
10 | const keypair = nearApi.utils.KeyPair.fromRandom('ed25519');
11 |
12 | return {
13 | public_key: keypair.publicKey.toString(),
14 | private_key: keypair.secretKey
15 | };
16 | },
17 |
18 | CreateKeyPair: async function (name) {
19 | const keypair = nearApi.utils.KeyPair.fromRandom('ed25519');
20 |
21 | const account =
22 | {
23 | account_id: name,
24 | public_key: keypair.publicKey.toString(),
25 | private_key: keypair.secretKey
26 | };
27 |
28 | return account;
29 | },
30 |
31 | /**
32 | * @return {string}
33 | */
34 | GetFileName: function (account_id) {
35 | return `${storageFolder}/${account_id}.json`;
36 | },
37 |
38 | SaveKeyPair: async function (account) {
39 | if (!fs.existsSync(storageFolder))
40 | fs.mkdirSync(storageFolder);
41 |
42 | const filename = this.GetFileName(account.account_id);
43 | account.private_key = "ed25519:" + account.private_key;
44 |
45 | await fs.promises.writeFile(filename, JSON.stringify(account));
46 | },
47 |
48 | /**
49 | * @return {boolean}
50 | */
51 | CreateAccount: async function (new_account) {
52 | const account = await blockchain.GetMasterAccount();
53 |
54 | const res = await account.createAccount(new_account.account_id, new_account.public_key, '200000000000000000000000');
55 |
56 | try {
57 | if (res['status'].hasOwnProperty('SuccessValue')) {
58 | await this.SaveKeyPair(new_account);
59 | return true
60 | }
61 | } catch (e) {
62 | console.log(e);
63 | }
64 | return false;
65 | },
66 |
67 | GetAccount: async function (account_id) {
68 | const filename = this.GetFileName(account_id);
69 | return await fs.promises.readFile(filename, 'utf8');
70 | },
71 |
72 | GetKeysFromSeedPhrase: async function (seedPhrase) {
73 | return nearSeedPhrase.parseSeedPhrase(seedPhrase);
74 | }
75 | };
76 |
77 |
78 |
--------------------------------------------------------------------------------