diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..52587d54 --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +tmp-project diff --git a/.eslintrc.yml b/.eslintrc.yml new file mode 100644 index 00000000..1132fadf --- /dev/null +++ b/.eslintrc.yml @@ -0,0 +1,36 @@ +env: + es6: true + node: true + jest: true +extends: 'eslint:recommended' +parserOptions: + ecmaVersion: 2018 +rules: + indent: + - error + - 4 + linebreak-style: + - error + - unix + quotes: + - error + - single + semi: + - error + - always + no-console: off +globals: + jasmine: true + window: true + fetch: true + Headers: true + document: true + Cookies: true + nearConfig: true + nearlib: true + near: true + walletAccount: true + contract: true + testSettings: true + + diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0afe363a..a1688767 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -9,6 +9,7 @@ run_tests: - gcloud script: - yarn + - yarn lint - NODE_ENV=ci yarn test run_tests_staging: diff --git a/blank_project/gulpfile.js b/blank_project/gulpfile.js index b13b1342..e327a313 100644 --- a/blank_project/gulpfile.js +++ b/blank_project/gulpfile.js @@ -1,9 +1,9 @@ -const gulp = require("gulp"); -const nearUtils = require("near-shell/gulp-utils"); +const gulp = require('gulp'); +const nearUtils = require('near-shell/gulp-utils'); function build_wasm(done){ - nearUtils.compile("./assembly/main.ts", "./out/main.wasm", done); -}; + nearUtils.compile('./assembly/main.ts', './out/main.wasm', done); +} const build = gulp.series(build_wasm); diff --git a/blank_project/src/config.js b/blank_project/src/config.js index 0064308c..c1baf945 100644 --- a/blank_project/src/config.js +++ b/blank_project/src/config.js @@ -5,59 +5,59 @@ function getConfig(env) { switch (env) { - case 'production': - case 'development': - return { - networkId: 'default', - nodeUrl: 'https://rpc.nearprotocol.com', - contractName: CONTRACT_NAME, - walletUrl: 'https://wallet.nearprotocol.com', - initialBalance: 100000000, - }; - case 'staging': - return { - networkId: 'staging', - nodeUrl: 'https://staging-rpc.nearprotocol.com/', - contractName: CONTRACT_NAME, - walletUrl: 'https://near-wallet-staging.onrender.com', - initialBalance: 100000000, - }; - case 'local': - return { - networkId: 'local', - nodeUrl: 'http://localhost:3030', - keyPath: `${process.env.HOME}/.near/validator_key.json`, - walletUrl: 'http://localhost:4000/wallet', - contractName: CONTRACT_NAME, - initialBalance: 100000000, - }; - case 'test': - return { - networkId: 'local', - nodeUrl: 'http://localhost:3030', - contractName: CONTRACT_NAME, - masterAccount: 'test.near', - initialBalance: 100000000, - }; - case 'test-remote': - case 'ci': - return { - networkId: 'shared-test', - nodeUrl: 'http://shared-test.nearprotocol.com:3030', - contractName: CONTRACT_NAME, - masterAccount: 'test.near', - initialBalance: 100000000, - }; - case 'ci-staging': - return { - networkId: 'shared-test-staging', - nodeUrl: 'http://staging-shared-test.nearprotocol.com:3030', - contractName: CONTRACT_NAME, - masterAccount: 'test.near', - initialBalance: 100000000, - }; - default: - throw Error(`Unconfigured environment '${env}'. Can be configured in src/config.js.`); + case 'production': + case 'development': + return { + networkId: 'default', + nodeUrl: 'https://rpc.nearprotocol.com', + contractName: CONTRACT_NAME, + walletUrl: 'https://wallet.nearprotocol.com', + initialBalance: 100000000, + }; + case 'staging': + return { + networkId: 'staging', + nodeUrl: 'https://staging-rpc.nearprotocol.com/', + contractName: CONTRACT_NAME, + walletUrl: 'https://near-wallet-staging.onrender.com', + initialBalance: 100000000, + }; + case 'local': + return { + networkId: 'local', + nodeUrl: 'http://localhost:3030', + keyPath: `${process.env.HOME}/.near/validator_key.json`, + walletUrl: 'http://localhost:4000/wallet', + contractName: CONTRACT_NAME, + initialBalance: 100000000, + }; + case 'test': + return { + networkId: 'local', + nodeUrl: 'http://localhost:3030', + contractName: CONTRACT_NAME, + masterAccount: 'test.near', + initialBalance: 100000000, + }; + case 'test-remote': + case 'ci': + return { + networkId: 'shared-test', + nodeUrl: 'http://shared-test.nearprotocol.com:3030', + contractName: CONTRACT_NAME, + masterAccount: 'test.near', + initialBalance: 100000000, + }; + case 'ci-staging': + return { + networkId: 'shared-test-staging', + nodeUrl: 'http://staging-shared-test.nearprotocol.com:3030', + contractName: CONTRACT_NAME, + masterAccount: 'test.near', + initialBalance: 100000000, + }; + default: + throw Error(`Unconfigured environment '${env}'. Can be configured in src/config.js.`); } } diff --git a/blank_project/src/main.js b/blank_project/src/main.js index 59b8b332..aca24b59 100755 --- a/blank_project/src/main.js +++ b/blank_project/src/main.js @@ -1,94 +1,94 @@ // Initializing contract async function initContract() { - console.log("nearConfig", nearConfig); + console.log('nearConfig', nearConfig); - // Initializing connection to the NEAR DevNet. - window.near = await nearlib.connect(Object.assign({ deps: { keyStore: new nearlib.keyStores.BrowserLocalStorageKeyStore() } }, nearConfig)); + // Initializing connection to the NEAR DevNet. + window.near = await nearlib.connect(Object.assign({ deps: { keyStore: new nearlib.keyStores.BrowserLocalStorageKeyStore() } }, nearConfig)); - // Initializing Wallet based Account. It can work with NEAR DevNet wallet that - // is hosted at https://wallet.nearprotocol.com - window.walletAccount = new nearlib.WalletAccount(window.near); + // Initializing Wallet based Account. It can work with NEAR DevNet wallet that + // is hosted at https://wallet.nearprotocol.com + window.walletAccount = new nearlib.WalletAccount(window.near); - // Getting the Account ID. If unauthorized yet, it's just empty string. - window.accountId = window.walletAccount.getAccountId(); + // Getting the Account ID. If unauthorized yet, it's just empty string. + window.accountId = window.walletAccount.getAccountId(); - // Initializing our contract APIs by contract name and configuration. - window.contract = await near.loadContract(nearConfig.contractName, { - // NOTE: This configuration only needed while NEAR is still in development - // View methods are read only. They don't modify the state, but usually return some value. - viewMethods: ["whoSaidHi"], - // Change methods can modify the state. But you don't receive the returned value when called. - changeMethods: ["sayHi"], - // Sender is the account ID to initialize transactions. - sender: window.accountId, - }); + // Initializing our contract APIs by contract name and configuration. + window.contract = await near.loadContract(nearConfig.contractName, { // eslint-disable-line require-atomic-updates + // NOTE: This configuration only needed while NEAR is still in development + // View methods are read only. They don't modify the state, but usually return some value. + viewMethods: ['whoSaidHi'], + // Change methods can modify the state. But you don't receive the returned value when called. + changeMethods: ['sayHi'], + // Sender is the account ID to initialize transactions. + sender: window.accountId, + }); } // Using initialized contract async function doWork() { - // Setting up refresh button - document.getElementById('refresh-button').addEventListener('click', updateWhoSaidHi); + // Setting up refresh button + document.getElementById('refresh-button').addEventListener('click', updateWhoSaidHi); - // Based on whether you've authorized, checking which flow we should go. - if (!window.walletAccount.isSignedIn()) { - signedOutFlow(); - } else { - signedInFlow(); - } + // Based on whether you've authorized, checking which flow we should go. + if (!window.walletAccount.isSignedIn()) { + signedOutFlow(); + } else { + signedInFlow(); + } } // Function that initializes the signIn button using WalletAccount function signedOutFlow() { - // Displaying the signed out flow container. - document.getElementById('signed-out-flow').classList.remove('d-none'); - // Adding an event to a sing-in button. - document.getElementById('sign-in-button').addEventListener('click', () => { - window.walletAccount.requestSignIn( - // The contract name that would be authorized to be called by the user's account. - window.nearConfig.contractName, - // This is the app name. It can be anything. - 'Who was the last person to say "Hi!"?', - // We can also provide URLs to redirect on success and failure. - // The current URL is used by default. - ); - }); + // Displaying the signed out flow container. + document.getElementById('signed-out-flow').classList.remove('d-none'); + // Adding an event to a sing-in button. + document.getElementById('sign-in-button').addEventListener('click', () => { + window.walletAccount.requestSignIn( + // The contract name that would be authorized to be called by the user's account. + window.nearConfig.contractName, + // This is the app name. It can be anything. + 'Who was the last person to say "Hi!"?', + // We can also provide URLs to redirect on success and failure. + // The current URL is used by default. + ); + }); } // Main function for the signed-in flow (already authorized by the wallet). function signedInFlow() { - // Displaying the signed in flow container. - document.getElementById('signed-in-flow').classList.remove('d-none'); + // Displaying the signed in flow container. + document.getElementById('signed-in-flow').classList.remove('d-none'); - // Displaying current account name. - document.getElementById('account-id').innerText = window.accountId; + // Displaying current account name. + document.getElementById('account-id').innerText = window.accountId; - // Adding an event to a say-hi button. - document.getElementById('say-hi').addEventListener('click', () => { + // Adding an event to a say-hi button. + document.getElementById('say-hi').addEventListener('click', () => { // We call say Hi and then update who said Hi last. - window.contract.sayHi().then(updateWhoSaidHi); - }); + window.contract.sayHi().then(updateWhoSaidHi); + }); - // Adding an event to a sing-out button. - document.getElementById('sign-out-button').addEventListener('click', () => { - walletAccount.signOut(); - // Forcing redirect. - window.location.replace(window.location.origin + window.location.pathname); - }); + // Adding an event to a sing-out button. + document.getElementById('sign-out-button').addEventListener('click', () => { + walletAccount.signOut(); + // Forcing redirect. + window.location.replace(window.location.origin + window.location.pathname); + }); } // Function to update who said hi function updateWhoSaidHi() { - // JavaScript tip: - // This is another example of how to use promises. Since this function is not async, - // we can't await for `contract.whoSaidHi()`, instead we attaching a callback function - // usin `.then()`. - contract.whoSaidHi().then((who) => { + // JavaScript tip: + // This is another example of how to use promises. Since this function is not async, + // we can't await for `contract.whoSaidHi()`, instead we attaching a callback function + // usin `.then()`. + contract.whoSaidHi().then((who) => { // If the result doesn't have a value we fallback to the text - document.getElementById('who').innerText = who || "Nobody (but you can be the first)"; - }); + document.getElementById('who').innerText = who || 'Nobody (but you can be the first)'; + }); } // Loads nearlib and this contract into window scope. window.nearInitPromise = initContract() - .then(doWork) - .catch(console.error); \ No newline at end of file + .then(doWork) + .catch(console.error); \ No newline at end of file diff --git a/blank_project/src/test.js b/blank_project/src/test.js index 26ab3135..9c8fdeb0 100755 --- a/blank_project/src/test.js +++ b/blank_project/src/test.js @@ -1,5 +1,5 @@ // >> tests-snippet -describe("Greeter", function() { +describe('Greeter', function() { let near; let contract; let accountId; @@ -8,34 +8,34 @@ describe("Greeter", function() { // Common setup below beforeAll(async function() { - if (window.testSettings === undefined) { - window.testSettings = {}; - } - near = await nearlib.connect(testSettings); - accountId = testSettings.accountId; - const contractName = testSettings.contractName ? - testSettings.contractName : - (new URL(window.location.href)).searchParams.get("contractName"); - contract = await near.loadContract(contractName, { + if (window.testSettings === undefined) { + window.testSettings = {}; + } + near = await nearlib.connect(testSettings); + accountId = testSettings.accountId; + const contractName = testSettings.contractName ? + testSettings.contractName : + (new URL(window.location.href)).searchParams.get('contractName'); + contract = await near.loadContract(contractName, { // NOTE: This configuration only needed while NEAR is still in development // View methods are read only. They don't modify the state, but usually return some value. - viewMethods: ["whoSaidHi"], - // Change methods can modify the state. But you don't receive the returned value when called. - changeMethods: [], - sender: accountId - }); + viewMethods: ['whoSaidHi'], + // Change methods can modify the state. But you don't receive the returned value when called. + changeMethods: [], + sender: accountId + }); }); // Multiple tests can be described below. Search Jasmine JS for documentation. - describe("simple", function() { - beforeAll(async function() { + describe('simple', function() { + beforeAll(async function() { // There can be some common setup for each test. - }); + }); - it("get hello message", async function() { - const result = await contract.whoSaidHi(); - expect(result).toBeNull(); - }); - }); + it('get hello message', async function() { + const result = await contract.whoSaidHi(); + expect(result).toBeNull(); + }); + }); }); // << tests-snippet diff --git a/get-config.js b/get-config.js index 526492ee..5934a2ff 100644 --- a/get-config.js +++ b/get-config.js @@ -13,4 +13,4 @@ module.exports = function getConfig() { } throw e; } -} +}; diff --git a/gulp-utils.js b/gulp-utils.js index 458823b3..96d6eaed 100644 --- a/gulp-utils.js +++ b/gulp-utils.js @@ -8,59 +8,58 @@ // "--measure" // ], callback); // } -var path = require("path"); function compile(inputFile, outputFile, callback) { - const asc = getAsc(); - asc.main([ - inputFile, - // TODO: Optimiziation is very slow, enable it only conditionally for "prod" builds? - "-O1", - "--baseDir", process.cwd(), - "--binaryFile", outputFile, - "--textFile",outputFile.substring(0,outputFile.lastIndexOf("."))+ ".wat", - "--measure", - "--runtime", "stub" - ], callback); + const asc = getAsc(); + asc.main([ + inputFile, + // TODO: Optimiziation is very slow, enable it only conditionally for "prod" builds? + '-O1', + '--baseDir', process.cwd(), + '--binaryFile', outputFile, + '--textFile',outputFile.substring(0,outputFile.lastIndexOf('.'))+ '.wat', + '--measure', + '--runtime', 'stub' + ], callback); } let asc; function getAsc() { - if (asc) { - return asc; - } - - asc = require("assemblyscript/bin/asc"); - - const fs = require("fs"); - const pathModule = require("path"); - asc.main = (main => (args, options, fn) => { - if (typeof options === "function") { - fn = options; - options = undefined; + if (asc) { + return asc; } - const logLn = process.browser ? window.logLn : console.log; - return main(args, options || { - stdout: process.stdout || asc.createMemoryStream(logLn), - stderr: process.stderr || asc.createMemoryStream(logLn), - readFile: (filename, baseDir) => { - baseDir = pathModule.relative(process.cwd(), baseDir); - let path = pathModule.join(baseDir, filename); - if (!fs.existsSync(path)) { - return null; + asc = require('assemblyscript/bin/asc'); + + const fs = require('fs'); + const pathModule = require('path'); + asc.main = (main => (args, options, fn) => { + if (typeof options === 'function') { + fn = options; + options = undefined; } - return fs.readFileSync(path).toString("utf8"); - }, - writeFile: (filename, contents) => { - const name = filename.startsWith("../") ? filename.substring(3) : filename; - fs.writeFileSync(name, contents); - }, - listFiles: () => [] - }, fn); - })(asc.main); - return asc; + const logLn = process.browser ? window.logLn : console.log; + return main(args, options || { + stdout: process.stdout || asc.createMemoryStream(logLn), + stderr: process.stderr || asc.createMemoryStream(logLn), + readFile: (filename, baseDir) => { + baseDir = pathModule.relative(process.cwd(), baseDir); + let path = pathModule.join(baseDir, filename); + if (!fs.existsSync(path)) { + return null; + } + + return fs.readFileSync(path).toString('utf8'); + }, + writeFile: (filename, contents) => { + const name = filename.startsWith('../') ? filename.substring(3) : filename; + fs.writeFileSync(name, contents); + }, + listFiles: () => [] + }, fn); + })(asc.main); + return asc; } module.exports = { compile }; diff --git a/index.js b/index.js index 52e5c6ed..def87094 100644 --- a/index.js +++ b/index.js @@ -2,7 +2,7 @@ const nearjs = require('nearlib'); const { KeyPair, keyStores } = require('nearlib'); const UnencryptedFileSystemKeyStore = keyStores.UnencryptedFileSystemKeyStore; const fs = require('fs'); -const util = require('util') +const util = require('util'); const yargs = require('yargs'); const bs58 = require('bs58'); const ncp = require('ncp').ncp; @@ -19,25 +19,25 @@ const inspectResponse = (response) => { // TODO: Fix promisified wrappers to handle error properly exports.newProject = async function(options) { - // Need to wait for the copy to finish, otherwise next tasks do not find files. - const projectDir = options.projectDir; - const sourceDir = __dirname + "/blank_project"; - console.log(`Copying files to new project directory (${projectDir}) from template source (${sourceDir}).`); - const copyDirFn = () => { - return new Promise(resolve => { - ncp (sourceDir, options.projectDir, response => resolve(response)); - })}; - await copyDirFn(); - console.log('Copying project files complete.') + // Need to wait for the copy to finish, otherwise next tasks do not find files. + const projectDir = options.projectDir; + const sourceDir = __dirname + '/blank_project'; + console.log(`Copying files to new project directory (${projectDir}) from template source (${sourceDir}).`); + const copyDirFn = () => { + return new Promise(resolve => { + ncp (sourceDir, options.projectDir, response => resolve(response)); + });}; + await copyDirFn(); + console.log('Copying project files complete.'); }; exports.clean = async function() { - const rmDirFn = () => { - return new Promise(resolve => { - rimraf(yargs.argv.outDir, response => resolve(response)); - })}; - await rmDirFn(); - console.log("Clean complete."); + const rmDirFn = () => { + return new Promise(resolve => { + rimraf(yargs.argv.outDir, response => resolve(response)); + });}; + await rmDirFn(); + console.log('Clean complete.'); }; async function connect(options) { @@ -64,7 +64,7 @@ exports.createAccount = async function(options) { await near.connection.signer.keyStore.setKey(options.networkId, options.accountId, keyPair); } console.log(`Account ${options.accountId} for network "${options.networkId}" was created.`); -} +}; exports.viewAccount = async function(options) { let near = await connect(options); @@ -72,7 +72,7 @@ exports.viewAccount = async function(options) { let state = await account.state(); console.log(`Account ${options.accountId}`); console.log(inspectResponse(state)); -} +}; exports.deleteAccount = async function(options) { console.log( @@ -89,14 +89,14 @@ exports.keys = async function(options) { let accessKeys = await account.getAccessKeys(); console.log(`Keys for account ${options.accountId}`); console.log(inspectResponse(accessKeys)); -} +}; exports.txStatus = async function(options) { let near = await connect(options); let status = await near.connection.provider.txStatus(bs58.decode(options.hash)); console.log(`Transaction ${options.hash}`); console.log(inspectResponse(status)); -} +}; exports.deploy = async function(options) { console.log( @@ -136,15 +136,15 @@ exports.stake = async function(options) { console.log(`Staking ${options.amount} on ${options.accountId} with public key = ${options.publicKey}.`); const near = await connect(options); const account = await near.account(options.accountId); - const result = await account.stake(options.publicKey, BigInt(options.amount)); + const result = await account.stake(options.publicKey, options.amount); console.log(inspectResponse(result)); -} +}; exports.login = async function(options) { if (!options.walletUrl) { - console.log("Log in is not needed on this environment. Please use appropriate master account for shell operations.") + console.log('Log in is not needed on this environment. Please use appropriate master account for shell operations.'); } else { - const newUrl = new URL(options.walletUrl + "/login/"); + const newUrl = new URL(options.walletUrl + '/login/'); const title = 'NEAR Shell'; newUrl.searchParams.set('title', title); const keyPair = await KeyPair.fromRandom('ed25519'); @@ -167,12 +167,12 @@ exports.login = async function(options) { keyStore.setKey(options.networkId, accountId, keyPair); console.log(`Logged in with ${accountId}`); } else { - console.log('Log in did not succeed. Please try again.') + console.log('Log in did not succeed. Please try again.'); } } catch (e) { - console.log(e) + console.log(e); } rl.close(); }); } -} +}; diff --git a/package.json b/package.json index 244a48b2..c161e3cf 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,9 @@ "description": "Command line utilities to interact with near blockchain", "main": "index.js", "scripts": { - "build": "echo \"Error: not implemented\" && exit 1", - "deploy": "echo \"Error: not implemented\" && exit 1", - "test": "./test/index.sh" + "test": "./test/index.sh", + "lint": "eslint .", + "fix": "eslint . --fix" }, "repository": { "type": "git", @@ -22,6 +22,7 @@ "near": "./bin/near" }, "devDependencies": { + "eslint": "^6.4.0", "strip-ansi-cli": "^2.0.0" }, "dependencies": { diff --git a/test_environment.js b/test_environment.js index 84bbfb54..59928ace 100644 --- a/test_environment.js +++ b/test_environment.js @@ -16,8 +16,8 @@ class LocalTestEnvironment extends NodeEnvironment { let config = require('./get-config')(); this.global.testSettings = this.global.nearConfig = config; config = Object.assign(config, { - contractName: "test" + Date.now(), - accountId: "test" + Date.now() + contractName: 'test' + Date.now(), + accountId: 'test' + Date.now() }); const keyStore = new nearlib.keyStores.UnencryptedFileSystemKeyStore('./neardev'); config.deps = Object.assign(config.deps || {}, { @@ -59,7 +59,7 @@ class LocalTestEnvironment extends NodeEnvironment { delete store[key]; } }; - }; + } } -module.exports = LocalTestEnvironment +module.exports = LocalTestEnvironment;