diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8fcc93c --- /dev/null +++ b/.gitignore @@ -0,0 +1,31 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. +# Developer note: near.gitignore will be renamed to .gitignore upon project creation +# dependencies +/node_modules +/.pnp +.pnp.js +/out + +#keys +/neardev + + +# testing +/coverage + +# production +/build + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* +/.cache +/yarn.lock +/dist \ No newline at end of file diff --git a/.gitpod.yml b/.gitpod.yml new file mode 100644 index 0000000..cc33841 --- /dev/null +++ b/.gitpod.yml @@ -0,0 +1,6 @@ +tasks: + - init: yarn + command: yarn dev +ports: + - port: 1234 + onOpen: open-browser diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..b405e9a --- /dev/null +++ b/.npmignore @@ -0,0 +1,28 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +package-lock.json +/node_modules +/.pnp +.pnp.js + +# build +/out + +# testing +/coverage + +# production +/dist +/.cache + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/README.md b/README.md new file mode 100644 index 0000000..8c6be2a --- /dev/null +++ b/README.md @@ -0,0 +1,102 @@ +crossword +================== + +This [React] app was initialized with [create-near-app] + + +Quick Start +=========== + +To run this project locally: + +1. Prerequisites: Make sure you've installed [Node.js] ≥ 12 +2. Install dependencies: `yarn install` +3. Run the local development server: `yarn dev` (see `package.json` for a + full list of `scripts` you can run with `yarn`) + +Now you'll have a local development environment backed by the NEAR TestNet! + +Go ahead and play with the app and the code. As you make code changes, the app will automatically reload. + + +Exploring The Code +================== + +1. The "backend" code lives in the `/contract` folder. See the README there for + more info. +2. The frontend code lives in the `/src` folder. `/src/index.html` is a great + place to start exploring. Note that it loads in `/src/index.js`, where you + can learn how the frontend connects to the NEAR blockchain. +3. Tests: there are different kinds of tests for the frontend and the smart + contract. See `contract/README` for info about how it's tested. The frontend + code gets tested with [jest]. You can run both of these at once with `yarn + run test`. + + +Deploy +====== + +Every smart contract in NEAR has its [own associated account][NEAR accounts]. When you run `yarn dev`, your smart contract gets deployed to the live NEAR TestNet with a throwaway account. When you're ready to make it permanent, here's how. + + +Step 0: Install near-cli (optional) +------------------------------------- + +[near-cli] is a command line interface (CLI) for interacting with the NEAR blockchain. It was installed to the local `node_modules` folder when you ran `yarn install`, but for best ergonomics you may want to install it globally: + + yarn install --global near-cli + +Or, if you'd rather use the locally-installed version, you can prefix all `near` commands with `npx` + +Ensure that it's installed with `near --version` (or `npx near --version`) + + +Step 1: Create an account for the contract +------------------------------------------ + +Each account on NEAR can have at most one contract deployed to it. If you've already created an account such as `your-name.testnet`, you can deploy your contract to `crossword.your-name.testnet`. Assuming you've already created an account on [NEAR Wallet], here's how to create `crossword.your-name.testnet`: + +1. Authorize NEAR CLI, following the commands it gives you: + + near login + +2. Create a subaccount (replace `YOUR-NAME` below with your actual account name): + + near create-account crossword.YOUR-NAME.testnet --masterAccount YOUR-NAME.testnet + + +Step 2: set contract name in code +--------------------------------- + +Modify the line in `src/config.js` that sets the account name of the contract. Set it to the account id you used above. + + const CONTRACT_NAME = process.env.CONTRACT_NAME || 'crossword.YOUR-NAME.testnet' + + +Step 3: deploy! +--------------- + +One command: + + yarn deploy + +As you can see in `package.json`, this does two things: + +1. builds & deploys smart contract to NEAR TestNet +2. builds & deploys frontend code to GitHub using [gh-pages]. This will only work if the project already has a repository set up on GitHub. Feel free to modify the `deploy` script in `package.json` to deploy elsewhere. + + +Troubleshooting +=============== + +On Windows, if you're seeing an error containing `EPERM` it may be related to spaces in your path. Please see [this issue](https://github.com/zkat/npx/issues/209) for more details. + + + [React]: https://reactjs.org/ + [create-near-app]: https://github.com/near/create-near-app + [Node.js]: https://nodejs.org/en/download/package-manager/ + [jest]: https://jestjs.io/ + [NEAR accounts]: https://docs.near.org/docs/concepts/account + [NEAR Wallet]: https://wallet.testnet.near.org/ + [near-cli]: https://github.com/near/near-cli + [gh-pages]: https://github.com/tschaub/gh-pages diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 0000000..c57c4cc --- /dev/null +++ b/babel.config.js @@ -0,0 +1,3 @@ +module.exports = { + presets: ['@babel/preset-env', '@babel/preset-react'], +} diff --git a/contract/.cargo/config b/contract/.cargo/config new file mode 100644 index 0000000..58910a4 --- /dev/null +++ b/contract/.cargo/config @@ -0,0 +1,2 @@ +[build] +rustflags = ["-C", "link-args=-s"] diff --git a/contract/.gitignore b/contract/.gitignore new file mode 100644 index 0000000..1de5659 --- /dev/null +++ b/contract/.gitignore @@ -0,0 +1 @@ +target \ No newline at end of file diff --git a/contract/.npmignore b/contract/.npmignore new file mode 100644 index 0000000..eb5a316 --- /dev/null +++ b/contract/.npmignore @@ -0,0 +1 @@ +target diff --git a/contract/Cargo.lock b/contract/Cargo.lock new file mode 100644 index 0000000..de71e13 --- /dev/null +++ b/contract/Cargo.lock @@ -0,0 +1,659 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" + +[[package]] +name = "ahash" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e" + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "autocfg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding", + "byte-tools", + "byteorder", + "generic-array 0.12.3", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array 0.14.4", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", +] + +[[package]] +name = "borsh" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09a7111f797cc721407885a323fb071636aee57f750b1a4ddc27397eba168a74" +dependencies = [ + "borsh-derive", + "hashbrown 0.9.1", +] + +[[package]] +name = "borsh-derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "307f3740906bac2c118a8122fe22681232b244f1369273e45f1156b45c43d2dd" +dependencies = [ + "borsh-derive-internal", + "borsh-schema-derive-internal", + "proc-macro-crate", + "proc-macro2", + "syn", +] + +[[package]] +name = "borsh-derive-internal" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2104c73179359431cc98e016998f2f23bc7a05bc53e79741bcba705f30047bc" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "borsh-schema-derive-internal" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae29eb8418fcd46f723f8691a2ac06857d31179d33d2f2d91eb13967de97c728" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "bs58" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + +[[package]] +name = "byteorder" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "cpufeatures" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed00c67cb5d0a7d64a44f6ad2668db7e7530311dd53ea79bcd4fb022c64911c8" +dependencies = [ + "libc", +] + +[[package]] +name = "crossword" +version = "0.1.0" +dependencies = [ + "near-sdk", +] + +[[package]] +name = "derive_more" +version = "0.99.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc7b9cef1e351660e5443924e4f43ab25fbbed3e9a5f052df3677deb4d6b320" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array 0.12.3", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array 0.14.4", +] + +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + +[[package]] +name = "generic-array" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +dependencies = [ + "typenum", +] + +[[package]] +name = "generic-array" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "hashbrown" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34f595585f103464d8d2f6e9864682d74c1601fed5e07d62b1c9058dba8246fb" +dependencies = [ + "autocfg", +] + +[[package]] +name = "hashbrown" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" +dependencies = [ + "ahash", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "indexmap" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b88cd59ee5f71fea89a62248fc8f387d44400cefe05ef548466d61ced9029a7" +dependencies = [ + "autocfg", + "hashbrown 0.8.1", +] + +[[package]] +name = "itoa" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" + +[[package]] +name = "keccak" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd7d4bd64732af4bf3a67f367c27df8520ad7e230c5817b8ff485864d80242b9" + +[[package]] +name = "memchr" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" + +[[package]] +name = "memory_units" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" + +[[package]] +name = "near-primitives-core" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2b3fb5acf3a494aed4e848446ef2d6ebb47dbe91c681105d4d1786c2ee63e52" +dependencies = [ + "base64", + "borsh", + "bs58", + "derive_more", + "hex", + "lazy_static", + "num-rational", + "serde", + "serde_json", + "sha2 0.9.5", +] + +[[package]] +name = "near-rpc-error-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffa8dbf8437a28ac40fcb85859ab0d0b8385013935b000c7a51ae79631dd74d9" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn", +] + +[[package]] +name = "near-rpc-error-macro" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6111d713e90c7c551dee937f4a06cb9ea2672243455a4454cc7566387ba2d9" +dependencies = [ + "near-rpc-error-core", + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn", +] + +[[package]] +name = "near-runtime-utils" +version = "4.0.0-pre.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a48d80c4ca1d4cf99bc16490e1e3d49826c150dfc4410ac498918e45c7d98e07" +dependencies = [ + "lazy_static", + "regex", +] + +[[package]] +name = "near-sdk" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7383e242d3e07bf0951e8589d6eebd7f18bb1c1fc5fbec3fad796041a6aebd1" +dependencies = [ + "base64", + "borsh", + "bs58", + "near-primitives-core", + "near-sdk-macros", + "near-vm-logic", + "serde", + "serde_json", + "wee_alloc", +] + +[[package]] +name = "near-sdk-core" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "284a78d9eb8eda58330462fa0023a6d7014c941df1f0387095e7dfd1dc0f2bce" +dependencies = [ + "Inflector", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "near-sdk-macros" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2037337438f97d1ce5f7c896cf229dc56dacd5c01142d1ef95a7d778cde6ce7d" +dependencies = [ + "near-sdk-core", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "near-vm-errors" +version = "4.0.0-pre.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e281d8730ed8cb0e3e69fb689acee6b93cdb43824cd69a8ffd7e1bfcbd1177d7" +dependencies = [ + "borsh", + "hex", + "near-rpc-error-macro", + "serde", +] + +[[package]] +name = "near-vm-logic" +version = "4.0.0-pre.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e11cb28a2d07f37680efdaf860f4c9802828c44fc50c08009e7884de75d982c5" +dependencies = [ + "base64", + "borsh", + "bs58", + "byteorder", + "near-primitives-core", + "near-runtime-utils", + "near-vm-errors", + "serde", + "sha2 0.8.2", + "sha3", +] + +[[package]] +name = "num-bigint" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d0a3d5e207573f948a9e5376662aa743a2ea13f7c50a554d7af443a73fbfeba" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", + "serde", +] + +[[package]] +name = "num-traits" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" +dependencies = [ + "autocfg", +] + +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml", +] + +[[package]] +name = "proc-macro2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "serde" +version = "1.0.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5317f7588f0a5078ee60ef675ef96735a1442132dc645eb1d12c018620ed8cd3" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0be94b04690fbaed37cddffc5c134bf537c8e3329d53e982fe04c374978f8e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3433e879a558dde8b5e8feb2a04899cf34fdde1fafb894687e52105fc1162ac3" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" +dependencies = [ + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug 0.2.3", +] + +[[package]] +name = "sha2" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b362ae5752fd2137731f9fa25fd4d9058af34666ca1966fb969119cc35719f12" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.9.0", + "opaque-debug 0.3.0", +] + +[[package]] +name = "sha3" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd26bc0e7a2e3a7c959bc494caf58b72ee0c71d67704e9520f736ca7e4853ecf" +dependencies = [ + "block-buffer 0.7.3", + "byte-tools", + "digest 0.8.1", + "keccak", + "opaque-debug 0.2.3", +] + +[[package]] +name = "syn" +version = "1.0.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4211ce9909eb971f111059df92c45640aad50a619cf55cd76476be803c4c68e6" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + +[[package]] +name = "typenum" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "version_check" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" + +[[package]] +name = "wee_alloc" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "memory_units", + "winapi", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/contract/Cargo.toml b/contract/Cargo.toml new file mode 100644 index 0000000..4e9d1ae --- /dev/null +++ b/contract/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "crossword" +version = "0.1.0" +authors = ["Near Inc "] +edition = "2018" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +near-sdk = "3.1.0" + +[profile.release] +codegen-units = 1 +# Tell `rustc` to optimize for small code size. +opt-level = "z" +lto = true +debug = false +panic = "abort" +# Opt into extra safety checks on arithmetic operations https://stackoverflow.com/a/64136471/249801 +overflow-checks = true + +[workspace] +members = [] diff --git a/contract/README.md b/contract/README.md new file mode 100644 index 0000000..b657c8d --- /dev/null +++ b/contract/README.md @@ -0,0 +1,27 @@ +crossword Smart Contract +================== + +A [smart contract] written in [Rust] for an app initialized with [create-near-app] + + +Quick Start +=========== + +Before you compile this code, you will need to install Rust with [correct target] + + +Exploring The Code +================== + +1. The main smart contract code lives in `src/lib.rs`. You can compile it with + the `./compile` script. +2. Tests: You can run smart contract tests with the `./test` script. This runs + standard Rust tests using [cargo] with a `--nocapture` flag so that you + can see any debug info you print to the console. + + + [smart contract]: https://docs.near.org/docs/roles/developer/contracts/intro + [Rust]: https://www.rust-lang.org/ + [create-near-app]: https://github.com/near/create-near-app + [correct target]: https://github.com/near/near-sdk-rs#pre-requisites + [cargo]: https://doc.rust-lang.org/book/ch01-03-hello-cargo.html diff --git a/contract/compile.js b/contract/compile.js new file mode 100644 index 0000000..b9785cf --- /dev/null +++ b/contract/compile.js @@ -0,0 +1,55 @@ +// This file does two things: +// +// 1. Compile the Rust contract using cargo (see buildCmd below). This will +// create a wasm file in the 'build' folder. +// 2. Create a symbolic link (symlink) to the generated wasm file in the root +// project's `out` folder, for easy use with near-cli. +// +// First, import some helper libraries. `shelljs` is included in the +// devDependencies of the root project, which is why it's available here. It +// makes it easy to use *NIX-style scripting (which works on Linux distros, +// macOS, and Unix systems) on Windows as well. +const sh = require('shelljs') +const path = require('path') + +// Figure out which directory the user called this script from, which we'll use +// later to set up the symlink. +const calledFromDir = sh.pwd().toString() + +// For the duration of this script, we want to operate from within the +// Rust project's folder. Let's change into that directory. +sh.cd(__dirname) + +// You can call this script with `node compile.js` or `node compile.js +// --debug`. Let's set a variable to track whether `--debug` was used. +const debug = process.argv.pop() === '--debug' + +// You can call this script with `node compile.js` or `node compile.js --debug`. +// Let's set a variable to track whether `--debug` was used. +// Note: see other flags in ./cargo/config. Unfortunately, you cannot set the +// `--target option` in Cargo.toml. +const buildCmd = debug + ? 'cargo build --target wasm32-unknown-unknown' + : 'cargo build --target wasm32-unknown-unknown --release' + +// Execute the build command, storing exit code for later use +const { code } = sh.exec(buildCmd) + +// Assuming this is compiled from the root project directory, link the compiled +// contract to the `out` folder – +// When running commands like `near deploy`, near-cli looks for a contract at +// /out/main.wasm +if (code === 0 && calledFromDir !== __dirname) { + const linkDir = `${calledFromDir}/out` + const link = `${calledFromDir}/out/main.wasm` + const packageName = require('fs').readFileSync(`${__dirname}/Cargo.toml`).toString().match(/name = "([^"]+)"/)[1] + const outFile = `./target/wasm32-unknown-unknown/${debug ? 'debug' : 'release'}/${packageName}.wasm` + sh.mkdir('-p', linkDir) + sh.rm('-f', link) + const linkPath = path.relative(linkDir, outFile) + + sh.ln('-s', linkPath, link) +} + +// exit script with the same code as the build command +process.exit(code) diff --git a/contract/src/lib.rs b/contract/src/lib.rs new file mode 100644 index 0000000..419bbdb --- /dev/null +++ b/contract/src/lib.rs @@ -0,0 +1,113 @@ +use near_sdk::{ + borsh::{self, BorshDeserialize, BorshSerialize}, + log, Balance, Promise, +}; +use near_sdk::{env, near_bindgen, PublicKey}; +use near_sdk::{json_types::Base58PublicKey, AccountId}; +use std::collections::HashMap; + +near_sdk::setup_alloc!(); + +#[derive(BorshDeserialize, BorshSerialize)] +pub enum PuzzleStatus { + Unsolved, + Solved { solver: PublicKey }, + Claimed { memo: String }, +} + +#[derive(BorshDeserialize, BorshSerialize)] +pub struct Puzzle { + status: PuzzleStatus, + value: Balance, + creator: AccountId, +} + +#[near_bindgen] +#[derive(Default, BorshDeserialize, BorshSerialize)] +pub struct Crossword { + puzzles: HashMap, +} + +#[near_bindgen] +impl Crossword { + pub fn submit_solution(&mut self, new_public_key: Base58PublicKey) { + let solver = env::signer_account_pk(); + // check to see if the env::public key from signer is in the puzzles + // see if it's already solved… + let entry = self + .puzzles + .get_mut(&solver) + .expect("Not a correct public key to solve puzzle"); + + // batch action of removing that public key and adding the user's public key + entry.status = match entry.status { + PuzzleStatus::Unsolved => PuzzleStatus::Solved { + solver: new_public_key.clone().into(), + }, + _ => { + env::panic(b"puzzle is already solved"); + } + }; + + log!( + "Puzzle solved, new public key: {}", + String::from(&new_public_key) + ); + } + + // TODO claim reward functionality + + // Puzzle creator provides `key` that's the answer + #[payable] + pub fn new_puzzle(&mut self, key: Base58PublicKey) { + let value_transfered = env::attached_deposit(); + let creator = env::predecessor_account_id(); + let key = PublicKey::from(key); + let existing = self.puzzles.insert( + key.clone(), + Puzzle { + status: PuzzleStatus::Unsolved, + value: value_transfered, + creator, + }, + ); + + assert!(existing.is_none(), "Puzzle with that key already exists"); + Promise::new(env::current_account_id()).add_access_key( + key, + 250000000000000000000000, + env::current_account_id(), + // * Strange API for it to be cs names + b"submit_solution".to_vec(), + ); + } +} + +#[cfg(test)] +mod tests { + // use super::*; + // use near_sdk::MockedBlockchain; + // use near_sdk::{testing_env, VMContext}; + + // // mock the context for testing, notice "signer_account_id" that was accessed above from env:: + // fn get_context(input: Vec, is_view: bool) -> VMContext { + // VMContext { + // current_account_id: "alice_near".to_string(), + // signer_account_id: "bob_near".to_string(), + // signer_account_pk: vec![0, 1, 2], + // predecessor_account_id: "carol_near".to_string(), + // input, + // block_index: 0, + // block_timestamp: 0, + // account_balance: 0, + // account_locked_balance: 0, + // storage_usage: 0, + // attached_deposit: 0, + // prepaid_gas: 10u64.pow(18), + // random_seed: vec![0, 1, 2], + // is_view, + // output_data_receivers: vec![], + // epoch_height: 19, + // } + // } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..f52eea8 --- /dev/null +++ b/package.json @@ -0,0 +1,71 @@ +{ + "name": "crossword", + "version": "0.1.0", + "license": "UNLICENSED", + "scripts": { + "build": "npm run build:contract && npm run build:web", + "build:contract": "node contract/compile.js", + "build:contract:debug": "node contract/compile.js --debug", + "build:web": "parcel build src/index.html --public-url ./", + "dev:deploy:contract": "near dev-deploy", + "deploy:contract": "near deploy", + "deploy:pages": "gh-pages -d dist/", + "deploy": "npm run build && npm run deploy:contract && npm run deploy:pages", + "prestart" + : "npm run build:contract:debug && npm run dev:deploy:contract", + "start": "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", + "dev": "nodemon --watch contract/src -e rs --exec \"npm run start\"", + "test": "npm run build:contract:debug && cd contract && cargo test -- --nocapture && cd .. && jest test --runInBand" + }, + "devDependencies": { + "@babel/core": "~7.13.1", + "@babel/preset-env": "~7.13.5", + "@babel/preset-react": "~7.12.5", + "babel-jest": "~26.6.2", + "env-cmd": "~10.1.0", + "gh-pages": "~3.1.0", + "jest": "~26.6.2", + "jest-environment-node": "~26.6.2", + "near-cli": "~1.5.3", + "nodemon": "~2.0.3", + "parcel-bundler": "~1.12.4", + "react-test-renderer": "~17.0.1", + "shelljs": "~0.8.4" + }, + "dependencies": { + "near-api-js": "~0.36.3", + "react": "~17.0.1", + "react-dom": "~17.0.1", + "regenerator-runtime": "~0.13.5" + }, + "resolutions": { + "@babel/preset-env": "7.13.8" + }, + "jest": { + "moduleNameMapper": { + "\\.(jpg|ico|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/src/__mocks__/fileMock.js", + "\\.(css|less)$": "/src/__mocks__/fileMock.js" + }, + "setupFiles": [ + "/src/jest.init.js" + ], + "testEnvironment": "near-cli/test_environment", + "testPathIgnorePatterns": [ + "/contract/", + "/node_modules/" + ] + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + + "last 1 firefox version", + "last 1 safari version" + ] + } +} diff --git a/src/App.js b/src/App.js new file mode 100644 index 0000000..323eb47 --- /dev/null +++ b/src/App.js @@ -0,0 +1,197 @@ +import 'regenerator-runtime/runtime' +import React from 'react' +import { login, logout } from './utils' +import './global.css' + +import getConfig from './config' +const { networkId } = getConfig(process.env.NODE_ENV || 'development') + +export default function App() { + // use React Hooks to store greeting in component state + const [greeting, set_greeting] = React.useState() + + // when the user has not yet interacted with the form, disable the button + const [buttonDisabled, setButtonDisabled] = React.useState(true) + + // after submitting the form, we want to show Notification + const [showNotification, setShowNotification] = React.useState(false) + + // The useEffect hook can be used to fire side-effects during render + // Learn more: https://reactjs.org/docs/hooks-intro.html + React.useEffect( + () => { + // in this case, we only care to query the contract when signed in + if (window.walletConnection.isSignedIn()) { + + // window.contract is set by initContract in index.js + window.contract.get_greeting({ account_id: window.accountId }) + .then(greetingFromContract => { + set_greeting(greetingFromContract) + }) + } + }, + + // The second argument to useEffect tells React when to re-run the effect + // Use an empty array to specify "only run on first render" + // This works because signing into NEAR Wallet reloads the page + [] + ) + + // if not signed in, return early with sign-in prompt + if (!window.walletConnection.isSignedIn()) { + return ( +
+

Welcome to NEAR!

+

+ To make use of the NEAR blockchain, you need to sign in. The button + below will sign you in using NEAR Wallet. +

+

+ By default, when your app runs in "development" mode, it connects + to a test network ("testnet") wallet. This works just like the main + network ("mainnet") wallet, but the NEAR Tokens on testnet aren't + convertible to other currencies – they're just for testing! +

+

+ Go ahead and click the button below to try it out: +

+

+ +

+
+ ) + } + + return ( + // use React Fragment, <>, to avoid wrapping elements in unnecessary divs + <> + +
+

+ + {' '/* React trims whitespace around tags; insert literal space character when needed */} + {window.accountId}! +

+
{ + event.preventDefault() + + // get elements from the form using their id attribute + const { fieldset, greeting } = event.target.elements + + // hold onto new user-entered value from React's SynthenticEvent for use after `await` call + const newGreeting = greeting.value + + // disable the form while the value gets updated on-chain + fieldset.disabled = true + + try { + // make an update call to the smart contract + await window.contract.set_greeting({ + // pass the value that the user entered in the greeting field + message: newGreeting + }) + } catch (e) { + alert( + 'Something went wrong! ' + + 'Maybe you need to sign out and back in? ' + + 'Check your browser console for more info.' + ) + throw e + } finally { + // re-enable the form, whether the call succeeded or failed + fieldset.disabled = false + } + + // update local `greeting` variable to match persisted value + set_greeting(newGreeting) + + // show Notification + setShowNotification(true) + + // remove Notification again after css animation completes + // this allows it to be shown again next time the form is submitted + setTimeout(() => { + setShowNotification(false) + }, 11000) + }}> +
+ +
+ setButtonDisabled(e.target.value === greeting)} + style={{ flex: 1 }} + /> + +
+
+
+

+ Look at that! A Hello World app! This greeting is stored on the NEAR blockchain. Check it out: +

+
    +
  1. + Look in src/App.js and src/utils.js – you'll see get_greeting and set_greeting being called on contract. What's this? +
  2. +
  3. + Ultimately, this contract code is defined in assembly/main.ts – this is the source code for your smart contract.
  4. +
  5. + When you run yarn dev, the code in assembly/main.ts gets deployed to the NEAR testnet. You can see how this happens by looking in package.json at the scripts section to find the dev command.
  6. +
+
+

+ To keep learning, check out the NEAR docs or look through some example apps. +

+
+ {showNotification && } + + ) +} + +// this component gets rendered by App after the form is submitted +function Notification() { + const urlPrefix = `https://explorer.${networkId}.near.org/accounts` + return ( + + ) +} diff --git a/src/__mocks__/fileMock.js b/src/__mocks__/fileMock.js new file mode 100644 index 0000000..5defdcc --- /dev/null +++ b/src/__mocks__/fileMock.js @@ -0,0 +1,3 @@ +// NOTE: This is used to mock resource imports in JSX for tests +module.exports = '' + diff --git a/src/assets/favicon.ico b/src/assets/favicon.ico new file mode 100644 index 0000000..405779a Binary files /dev/null and b/src/assets/favicon.ico differ diff --git a/src/assets/logo-black.svg b/src/assets/logo-black.svg new file mode 100644 index 0000000..93e333a --- /dev/null +++ b/src/assets/logo-black.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/logo-white.svg b/src/assets/logo-white.svg new file mode 100644 index 0000000..47cb783 --- /dev/null +++ b/src/assets/logo-white.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/config.js b/src/config.js new file mode 100644 index 0000000..98df247 --- /dev/null +++ b/src/config.js @@ -0,0 +1,63 @@ +const CONTRACT_NAME = process.env.CONTRACT_NAME ||'crossword' + +function getConfig(env) { + switch (env) { + + case 'production': + case 'mainnet': + return { + networkId: 'mainnet', + nodeUrl: 'https://rpc.mainnet.near.org', + contractName: CONTRACT_NAME, + walletUrl: 'https://wallet.near.org', + helperUrl: 'https://helper.mainnet.near.org', + explorerUrl: 'https://explorer.mainnet.near.org', + } + case 'development': + case 'testnet': + return { + networkId: 'testnet', + nodeUrl: 'https://rpc.testnet.near.org', + contractName: CONTRACT_NAME, + walletUrl: 'https://wallet.testnet.near.org', + helperUrl: 'https://helper.testnet.near.org', + explorerUrl: 'https://explorer.testnet.near.org', + } + case 'betanet': + return { + networkId: 'betanet', + nodeUrl: 'https://rpc.betanet.near.org', + contractName: CONTRACT_NAME, + walletUrl: 'https://wallet.betanet.near.org', + helperUrl: 'https://helper.betanet.near.org', + explorerUrl: 'https://explorer.betanet.near.org', + } + 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, + } + case 'test': + case 'ci': + return { + networkId: 'shared-test', + nodeUrl: 'https://rpc.ci-testnet.near.org', + contractName: CONTRACT_NAME, + masterAccount: 'test.near', + } + case 'ci-betanet': + return { + networkId: 'shared-test-staging', + nodeUrl: 'https://rpc.ci-betanet.near.org', + contractName: CONTRACT_NAME, + masterAccount: 'test.near', + } + default: + throw Error(`Unconfigured environment '${env}'. Can be configured in src/config.js.`) + } +} + +module.exports = getConfig diff --git a/src/global.css b/src/global.css new file mode 100644 index 0000000..adfee0e --- /dev/null +++ b/src/global.css @@ -0,0 +1,188 @@ +* { + box-sizing: border-box; +} + +html { + --bg: #efefef; + --fg: #1e1e1e; + --gray: #555; + --light-gray: #ccc; + --shadow: #e6e6e6; + --success: rgb(90, 206, 132); + --primary: #FF585D; + --secondary: #0072CE; + + background-color: var(--bg); + color: var(--fg); + font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif; + font-size: calc(0.9em + 0.5vw); + line-height: 1.3; +} + +body { + margin: 0; + padding: 1em; +} + +main { + margin: 0 auto; + max-width: 25em; +} + +h1 { + background-image: url(assets/logo-black.svg); + background-position: center 1em; + background-repeat: no-repeat; + background-size: auto 1.5em; + margin-top: 0; + padding: 3.5em 0 0.5em; + text-align: center; +} + +a, +.link { + color: var(--primary); + text-decoration: none; +} +a:hover, +a:focus, +.link:hover, +.link:focus { + text-decoration: underline; +} +a:active, +.link:active { + color: var(--secondary); +} + +button, input { + font: inherit; + outline: none; +} + +button { + background-color: var(--secondary); + border-radius: 5px; + border: none; + color: #efefef; + cursor: pointer; + padding: 0.3em 0.75em; + transition: transform 30ms; +} +button:hover, button:focus { + box-shadow: 0 0 10em rgba(255, 255, 255, 0.2) inset; +} +button:active { + box-shadow: 0 0 10em rgba(0, 0, 0, 0.1) inset; +} +button.link { + background: none; + border: none; + box-shadow: none; + display: inline; +} +[disabled] button, button[disabled] { + box-shadow: none; + background-color: var(--light-gray); + color: gray; + cursor: not-allowed; + transform: none; +} +[disabled] button { + text-indent: -900em; + width: 2em; + position: relative; +} +[disabled] button:after { + content: " "; + display: block; + width: 0.8em; + height: 0.8em; + border-radius: 50%; + border: 2px solid #fff; + border-color: var(--fg) transparent var(--fg) transparent; + animation: loader 1.2s linear infinite; + position: absolute; + top: 0.45em; + right: 0.5em; +} +@keyframes loader { + 0% { transform: rotate(0deg) } + 100% { transform: rotate(360deg) } +} + +fieldset { + border: none; + padding: 2em 0; +} + +input { + background-color: var(--shadow); + border: none; + border-radius: 5px 0 0 5px; + caret-color: var(--primary); + color: inherit; + padding: 0.25em 1em; +} +input::selection { + background-color: var(--secondary); + color: #efefef; +} +input:focus { + box-shadow: 0 0 10em rgba(0, 0, 0, 0.02) inset; +} + +code { + color: var(--gray); +} + +li { + padding-bottom: 1em; +} + +aside { + animation: notify ease-in-out 10s; + background-color: var(--shadow); + border-radius: 5px; + bottom: 0; + font-size: 0.8em; + margin: 1em; + padding: 1em; + position: fixed; + transform: translateY(10em); + right: 0; +} +aside footer { + display: flex; + font-size: 0.9em; + justify-content: space-between; + margin-top: 0.5em; +} +aside footer *:first-child { + color: var(--success); +} +aside footer *:last-child { + color: var(--gray); +} +@keyframes notify { + 0% { transform: translateY(10em) } + 5% { transform: translateY(0) } + 95% { transform: translateY(0) } + 100% { transform: translateY(10em) } +} + +@media (prefers-color-scheme: dark) { + html { + --bg: #1e1e1e; + --fg: #efefef; + --gray: #aaa; + --shadow: #2a2a2a; + --light-gray: #444; + } + h1 { + background-image: url(assets/logo-white.svg); + } + input:focus { + box-shadow: 0 0 10em rgba(255, 255, 255, 0.02) inset; + } +} diff --git a/src/index.html b/src/index.html new file mode 100644 index 0000000..4cc6f37 --- /dev/null +++ b/src/index.html @@ -0,0 +1,25 @@ + + + + + + + + Welcome to NEAR with React + + + +
+ + + + diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..97a9730 --- /dev/null +++ b/src/index.js @@ -0,0 +1,13 @@ +import React from 'react' +import ReactDOM from 'react-dom' +import App from './App' +import { initContract } from './utils' + +window.nearInitPromise = initContract() + .then(() => { + ReactDOM.render( + , + document.querySelector('#root') + ) + }) + .catch(console.error) diff --git a/src/jest.init.js b/src/jest.init.js new file mode 100644 index 0000000..abf6292 --- /dev/null +++ b/src/jest.init.js @@ -0,0 +1 @@ +import 'regenerator-runtime/runtime' diff --git a/src/main.test.js b/src/main.test.js new file mode 100644 index 0000000..ab9394f --- /dev/null +++ b/src/main.test.js @@ -0,0 +1,28 @@ +beforeAll(async function () { + // NOTE: nearlib and nearConfig are made available by near-cli/test_environment + const near = await nearlib.connect(nearConfig) + window.accountId = nearConfig.contractName + window.contract = await near.loadContract(nearConfig.contractName, { + viewMethods: ['get_greeting'], + changeMethods: [], + sender: window.accountId + }) + + window.walletConnection = { + requestSignIn() { + }, + signOut() { + }, + isSignedIn() { + return true + }, + getAccountId() { + return window.accountId + } + } +}) + +test('get_greeting', async () => { + const message = await window.contract.get_greeting({ account_id: window.accountId }) + expect(message).toEqual('Hello') +}) diff --git a/src/utils.js b/src/utils.js new file mode 100644 index 0000000..28a7d7c --- /dev/null +++ b/src/utils.js @@ -0,0 +1,39 @@ +import { connect, Contract, keyStores, WalletConnection } from 'near-api-js' +import getConfig from './config' + +const nearConfig = getConfig(process.env.NODE_ENV || 'development') + +// Initialize contract & set global variables +export async function initContract() { + // Initialize connection to the NEAR testnet + const near = await connect(Object.assign({ deps: { keyStore: new keyStores.BrowserLocalStorageKeyStore() } }, nearConfig)) + + // Initializing Wallet based Account. It can work with NEAR testnet wallet that + // is hosted at https://wallet.testnet.near.org + window.walletConnection = new WalletConnection(near) + + // Getting the Account ID. If still unauthorized, it's just empty string + window.accountId = window.walletConnection.getAccountId() + + // Initializing our contract APIs by contract name and configuration + window.contract = await new Contract(window.walletConnection.account(), nearConfig.contractName, { + // View methods are read only. They don't modify the state, but usually return some value. + viewMethods: ['get_greeting'], + // Change methods can modify the state. But you don't receive the returned value when called. + changeMethods: ['set_greeting'], + }) +} + +export function logout() { + window.walletConnection.signOut() + // reload page + window.location.replace(window.location.origin + window.location.pathname) +} + +export function login() { + // Allow the current app to make calls to the specified contract on the + // user's behalf. + // This works by creating a new access key for the user's account and storing + // the private key in localStorage. + window.walletConnection.requestSignIn(nearConfig.contractName) +} diff --git a/src/wallet/login/index.html b/src/wallet/login/index.html new file mode 100644 index 0000000..1349ba4 --- /dev/null +++ b/src/wallet/login/index.html @@ -0,0 +1,28 @@ + + + + + + + +
For local account login, Please run the following command in NEAR CLI, then enter account id here. +
+
+ +
+ + + + + \ No newline at end of file