├── contract ├── as-pect.config.js ├── asconfig.json ├── assembly │ ├── as_types.d.ts │ ├── __tests__ │ │ ├── as-pect.d.ts │ │ └── main.spec.ts │ ├── tsconfig.json │ └── index.ts ├── package.json └── README.md ├── out └── .gitignore ├── .github ├── dependabot.yml └── workflows │ └── tests.yml ├── neardev └── shared-test │ └── test.near.json ├── .gitpod.Dockerfile ├── README.md ├── .travis.yml ├── ava.config.cjs ├── integration-tests ├── rs │ ├── Cargo.toml │ └── src │ │ └── tests.rs └── ts │ └── main.ava.ts ├── .gitignore ├── LICENSE ├── .gitpod.yml ├── README-Gitpod.md ├── package.json ├── frontend ├── assets │ ├── js │ │ ├── near │ │ │ ├── config.js │ │ │ └── utils.js │ │ └── main.js │ └── css │ │ └── global.css └── index.html └── LICENSE-APACHE /contract/as-pect.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('near-sdk-as/imports') 2 | -------------------------------------------------------------------------------- /contract/asconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "near-sdk-as/asconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /contract/assembly/as_types.d.ts: -------------------------------------------------------------------------------- 1 | /// -------------------------------------------------------------------------------- /contract/assembly/__tests__/as-pect.d.ts: -------------------------------------------------------------------------------- 1 | /// -------------------------------------------------------------------------------- /out/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | -------------------------------------------------------------------------------- /contract/assembly/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "assemblyscript/std/assembly.json", 3 | "include": [ 4 | "./**/*.ts" 5 | ] 6 | } -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | -------------------------------------------------------------------------------- /neardev/shared-test/test.near.json: -------------------------------------------------------------------------------- 1 | {"account_id":"test.near","private_key":"ed25519:2wyRcSwSuHtRVmkMCGjPwnzZmQLeXLzLLyED1NDMt4BjnKgQL6tF85yBx6Jr26D2dUNeC716RBoTxntVHsegogYw"} 2 | -------------------------------------------------------------------------------- /.gitpod.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gitpod/workspace-full 2 | 3 | RUN bash -c ". .nvm/nvm.sh \ 4 | && nvm install v12 && nvm alias default v12 \ 5 | && nvm use default && npm i -g yarn && alias near='yarn near'" \ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deprecated 2 | 3 | > The NEAR community is not actively supporting Assemblyscript anymore 4 | 5 | This repository is not being acively maintained anymore, please use the [typescript version instead](https://github.com/near-examples/counter-js). 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | os: 3 | - linux 4 | - osx 5 | - windows 6 | node_js: 7 | - 12 8 | env: 9 | - NODE_ENV=ci YARN_GPG=no 10 | - NODE_ENV=ci-betanet YARN_GPG=no 11 | cache: yarn 12 | script: 13 | - yarn build 14 | - yarn test 15 | -------------------------------------------------------------------------------- /ava.config.cjs: -------------------------------------------------------------------------------- 1 | require("util").inspect.defaultOptions.depth = 5; // Increase AVA's printing depth 2 | 3 | module.exports = { 4 | timeout: "300000", 5 | files: ["**/*.ava.ts"], 6 | failWithoutAssertions: false, 7 | extensions: ["ts"], 8 | require: ["ts-node/register"], 9 | }; 10 | -------------------------------------------------------------------------------- /contract/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "counter_contract", 3 | "version": "1.0.0", 4 | "license": "UNLICENSED", 5 | "private": true, 6 | "scripts": { 7 | "build": "asb", 8 | "test": "asp --nologo" 9 | }, 10 | "dependencies": { 11 | "near-sdk-as": "^3.2.3" 12 | }, 13 | "devDependencies": {} 14 | } 15 | -------------------------------------------------------------------------------- /integration-tests/rs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "integration-tests" 3 | version = "1.0.0" 4 | publish = false 5 | edition = "2018" 6 | 7 | [dev-dependencies] 8 | anyhow = "1.0" 9 | borsh = "0.9" 10 | maplit = "1.0" 11 | near-units = "0.2.0" 12 | # arbitrary_precision enabled for u128 types that workspaces requires for Balance types 13 | serde_json = { version = "1.0", features = ["arbitrary_precision"] } 14 | tokio = { version = "1.18.1", features = ["full"] } 15 | tracing = "0.1" 16 | tracing-subscriber = { version = "0.3.11", features = ["env-filter"] } 17 | workspaces = "0.4.0" 18 | pkg-config = "0.3.1" 19 | 20 | [[example]] 21 | name = "integration-tests" 22 | path = "src/tests.rs" 23 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | on: 3 | repository_dispatch: 4 | types: [tests-report] 5 | push: 6 | jobs: 7 | tests: 8 | strategy: 9 | matrix: 10 | platform: [ubuntu-latest] # mac-os in development 11 | runs-on: ${{ matrix.platform }} 12 | steps: 13 | - name: Checkout branch 14 | uses: actions/checkout@v2 15 | - name: Install Node 16 | uses: actions/setup-node@v2 17 | - name: Install Rust 18 | uses: actions-rs/toolchain@v1 19 | with: 20 | toolchain: stable 21 | target: wasm32-unknown-unknown 22 | - name: Install modules 23 | run: yarn 24 | - name: Run tests 25 | run: yarn test -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ########################################## 2 | # this file is useful with create-near-app 3 | ########################################## 4 | 5 | # ignore Mac OS noise 6 | .DS_Store 7 | 8 | # ignore large, hairy, autogenerated folders 9 | node_modules/ 10 | default/ 11 | target/ 12 | 13 | # ignore accounts generated by tests 14 | neardev/shared-test/* 15 | # but not the original test.near.json account 16 | !neardev/shared-test/test.near.json 17 | 18 | # ignore any default network private keys and dev-account 19 | neardev/default/* 20 | neardev/dev-account 21 | 22 | # ignore dev .cache 23 | .cache/ 24 | 25 | # ignore dist folder generated by build process 26 | dist/ 27 | out/ 28 | build/ 29 | 30 | # IDE files 31 | .idea 32 | 33 | # lock files 34 | *.lock 35 | *-lock.json 36 | 37 | yarn-error.log 38 | -------------------------------------------------------------------------------- /contract/README.md: -------------------------------------------------------------------------------- 1 | new-awesome-project Smart Contract 2 | ================== 3 | 4 | A [smart contract] written in [AssemblyScript] for an app initialized with [create-near-app] 5 | 6 | 7 | Quick Start 8 | =========== 9 | 10 | Before you compile this code, you will need to install [Node.js] ≥ 12 11 | 12 | 13 | Exploring The Code 14 | ================== 15 | 16 | 1. The main smart contract code lives in `assembly/index.ts`. You can compile 17 | it with the `./compile` script. 18 | 2. Tests: You can run smart contract tests with the `./test` script. This runs 19 | standard AssemblyScript tests using [as-pect]. 20 | 21 | 22 | [smart contract]: https://docs.near.org/develop/welcome 23 | [AssemblyScript]: https://www.assemblyscript.org/ 24 | [create-near-app]: https://github.com/near/create-near-app 25 | [Node.js]: https://nodejs.org/en/download/package-manager/ 26 | [as-pect]: https://www.npmjs.com/package/@as-pect/cli 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2020 NEAR Inc 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | image: 2 | file: .gitpod.Dockerfile 3 | # Options to prebuild the image after github events and set notifications/badges 4 | github: 5 | prebuilds: 6 | # enable for the master/default branch (defaults to true) 7 | master: true 8 | # enable for pull requests coming from this repo (defaults to true) 9 | pullRequests: true 10 | # enable for pull requests coming from forks (defaults to false) 11 | pullRequestsFromForks: true 12 | # add a check to pull requests (defaults to true) 13 | addCheck: true 14 | # add a "Review in Gitpod" button as a comment to pull requests (defaults to false) 15 | addComment: true 16 | # add a "Review in Gitpod" button to the pull request's description (defaults to false) 17 | addBadge: false 18 | # add a label once the prebuild is ready to pull requests (defaults to false) 19 | addLabel: false 20 | 21 | # List the start up tasks. You can start them in parallel in multiple terminals. See https://www.gitpod.io/docs/config-start-tasks/ 22 | tasks: 23 | - before: nvm use default 24 | init: yarn 25 | command: gp open README-Gitpod.md && yarn dev 26 | 27 | ports: 28 | - port: 1234 29 | onOpen: open-browser 30 | -------------------------------------------------------------------------------- /contract/assembly/index.ts: -------------------------------------------------------------------------------- 1 | import { storage, logging } from "near-sdk-as"; 2 | 3 | // Public method - Returns the counter value 4 | export function get_num(): i8 { 5 | return storage.getPrimitive("counter", 0); 6 | } 7 | 8 | // Public method - Increment the counter 9 | export function increment(): void { 10 | safeguard_overflow() 11 | const new_value = get_num() + 1; 12 | storage.set("counter", new_value); 13 | logging.log("Increased number to " + new_value.toString()); 14 | } 15 | 16 | // Public method - Decrement the counter 17 | export function decrement(): void { 18 | safeguard_underflow() 19 | const new_value = get_num() - 1; 20 | storage.set("counter", new_value); 21 | logging.log("Decreased number to " + new_value.toString()); 22 | } 23 | 24 | // Public method - Reset to zero 25 | export function reset(): void { 26 | storage.set("counter", 0); 27 | logging.log("Reset counter to zero"); 28 | } 29 | 30 | // Private method - Safeguard against overflow 31 | function safeguard_overflow(): void{ 32 | const value = get_num() 33 | assert(value < 127, "Counter is at maximum") 34 | } 35 | 36 | // Private method - Safeguard against underflow 37 | function safeguard_underflow(): void{ 38 | const value = get_num() 39 | assert(value > -128, "Counter is at minimum") 40 | } -------------------------------------------------------------------------------- /README-Gitpod.md: -------------------------------------------------------------------------------- 1 | Counter example in AssemblyScript - Gitpod version 2 | ================================================== 3 | 4 | This README is specific to Gitpod and this example. For local development, please see [README.md](README.md). 5 | 6 | ## Description 7 | 8 | In Gitpod, the counter will start automatically. Please look in the terminal for a link to follow. 9 | 10 | This contract implements simple counter backed by storage on blockchain. 11 | Contract in `assembly/main.ts` provides methods to increment / decrement counter and get it's current value or reset. 12 | 13 | Plus and minus buttons increase and decrease value correspondingly. When button L is toggled, counter will add or minus 10 a time. RS button is for reset. LE and RE buttons to let the robot wink to you. 14 | 15 | ## To Test 16 | 17 | ``` 18 | yarn asp # as-pect tests 19 | NODE_ENV=ci yarn jest # jest tests 20 | NODE_ENV=ci yarn test # both 21 | ``` 22 | 23 | ## To Explore 24 | 25 | - `assembly/main.ts` for the contract code 26 | - `src/index.html` for the front-end HTML 27 | - `src/main.js` for the JavaScript front-end code and how to integrate contracts 28 | - `src/test.js` for the JS tests for the contract 29 | 30 | 31 | ## Data collection 32 | 33 | By using Gitpod in this project, you agree to opt-in to basic, anonymous analytics. No personal information is transmitted. Instead, these usage statistics aid in discovering potential bugs and user flow information. 34 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "counter", 3 | "description": "An Assemblyscript counter that lives in the NEAR blockchain", 4 | "version": "1.1.0", 5 | "scripts": { 6 | "build": "cd contract && npm run build && mkdir -p ../out && rm -f ./out/counter.wasm && cp ./build/release/counter_contract.wasm ../out/main.wasm", 7 | "deploy": "npm run build && near dev-deploy", 8 | "start": "npm run deploy && echo The app is starting! && env-cmd -f ./neardev/dev-account.env parcel frontend/index.html --open", 9 | "dev": "nodemon --watch contract -e ts --exec \"npm run start\"", 10 | "test": "npm run build && npm run test:unit && npm run test:integration", 11 | "test:unit": "cd contract && npm i && npm run test", 12 | "test:integration": "npm run test:integration:ts && npm run test:integration:rs", 13 | "test:integration:ts": "ava --verbose", 14 | "test:integration:rs": "cd integration-tests/rs && cargo run --example integration-tests" 15 | }, 16 | "devDependencies": { 17 | "near-sdk-as": "3.2.3", 18 | "near-cli": "^3.2.0", 19 | "nodemon": "~2.0.15", 20 | "parcel-bundler": "~1.12.5", 21 | "ava": "^4.2.0", 22 | "near-workspaces": "^3.1.0", 23 | "typescript": "^4.6.4", 24 | "ts-node": "^10.7.0", 25 | "env-cmd": "^10.1.0" 26 | }, 27 | "dependencies": { 28 | "near-api-js": "^0.44.2", 29 | "regenerator-runtime": "^0.13.9" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /contract/assembly/__tests__/main.spec.ts: -------------------------------------------------------------------------------- 1 | import { get_num, reset, increment, decrement } from '../index'; 2 | import { context, storage } from 'near-sdk-as'; 3 | 4 | describe("Counter ", () => { 5 | it("should increment by one", () => { 6 | increment(); 7 | expect(get_num()).toBe(1, "counter should be one after a single increment."); 8 | }); 9 | 10 | it("get_num is the same as reading from storage", () => { 11 | expect(storage.getPrimitive("counter", 0)).toBe(get_num(), "storage.getPrimitive(\"counter\", 0) = get_num()"); 12 | }); 13 | 14 | it("should decrement by one", () => { 15 | increment(); 16 | decrement(); 17 | expect(get_num()).toBe(0, "counter should be zero after a single decrement."); 18 | }); 19 | 20 | it("should be resettable", () => { 21 | increment(); 22 | increment(); 23 | reset(); // reset to zero 24 | expect(get_num()).toBe(0, "counter should be zero after it is reset."); 25 | }); 26 | 27 | it("should increment multiple times and decrement back to zero", () => { 28 | increment(); 29 | expect(get_num()).toBe(1, "increment failed"); 30 | increment(); 31 | expect(get_num()).toBe(2, "increment failed"); 32 | decrement(); 33 | expect(get_num()).toBe(1, "increment failed"); 34 | }); 35 | 36 | it("should not overflow", () => { 37 | storage.set("counter", 127) 38 | expect(() => {increment()}).toThrow(); 39 | }); 40 | 41 | it("should not underflow", () => { 42 | storage.set("counter", -128) 43 | expect(() => {decrement()}).toThrow(); 44 | }); 45 | }); -------------------------------------------------------------------------------- /frontend/assets/js/near/config.js: -------------------------------------------------------------------------------- 1 | // Once you deploy the contract to its final account, make sure to specify it here 2 | const CONTRACT_NAME = process.env.CONTRACT_NAME || 'change_this_account'; /* TODO: Change this to the deployed account */ 3 | 4 | function getConfig (env) { 5 | switch (env) { 6 | case 'production': 7 | case 'mainnet': 8 | return { 9 | networkId: 'mainnet', 10 | nodeUrl: 'https://rpc.mainnet.near.org', 11 | contractName: CONTRACT_NAME, 12 | walletUrl: 'https://wallet.near.org', 13 | helperUrl: 'https://helper.mainnet.near.org' 14 | } 15 | case 'development': 16 | case 'testnet': 17 | return { 18 | networkId: 'testnet', 19 | nodeUrl: 'https://rpc.testnet.near.org', 20 | contractName: CONTRACT_NAME, 21 | walletUrl: 'https://wallet.testnet.near.org', 22 | helperUrl: 'https://helper.testnet.near.org' 23 | } 24 | case 'betanet': 25 | return { 26 | networkId: 'betanet', 27 | nodeUrl: 'https://rpc.betanet.near.org', 28 | contractName: CONTRACT_NAME, 29 | walletUrl: 'https://wallet.betanet.near.org', 30 | helperUrl: 'https://helper.betanet.near.org' 31 | } 32 | case 'local': 33 | return { 34 | networkId: 'local', 35 | nodeUrl: 'http://localhost:3030', 36 | keyPath: `${process.env.HOME}/.near/validator_key.json`, 37 | walletUrl: 'http://localhost:4000/wallet', 38 | contractName: CONTRACT_NAME 39 | } 40 | case 'test': 41 | case 'ci': 42 | return { 43 | networkId: 'shared-test', 44 | nodeUrl: 'https://rpc.ci-testnet.near.org', 45 | contractName: CONTRACT_NAME, 46 | masterAccount: 'test.near' 47 | } 48 | case 'ci-betanet': 49 | return { 50 | networkId: 'shared-test-staging', 51 | nodeUrl: 'https://rpc.ci-betanet.near.org', 52 | contractName: CONTRACT_NAME, 53 | masterAccount: 'test.near' 54 | } 55 | default: 56 | throw Error(`Unconfigured environment '${env}'. Can be configured in src/config.js.`) 57 | } 58 | } 59 | 60 | module.exports = getConfig 61 | -------------------------------------------------------------------------------- /integration-tests/ts/main.ava.ts: -------------------------------------------------------------------------------- 1 | import { Worker, NEAR, NearAccount } from "near-workspaces"; 2 | import anyTest, { TestFn } from "ava"; 3 | 4 | const test = anyTest as TestFn<{ 5 | worker: Worker; 6 | accounts: Record; 7 | }>; 8 | 9 | test.beforeEach(async (t) => { 10 | // Init the worker and start a Sandbox server 11 | const worker = await Worker.init(); 12 | 13 | // deploy contract 14 | const root = worker.rootAccount; 15 | const contract = await root.devDeploy( 16 | "./out/main.wasm", 17 | { initialBalance: NEAR.parse("30 N").toJSON() } 18 | ); 19 | 20 | // some test accounts 21 | const alice = await root.createSubAccount("alice", { 22 | initialBalance: NEAR.parse("30 N").toJSON(), 23 | }); 24 | const bob = await root.createSubAccount("bob", { 25 | initialBalance: NEAR.parse("30 N").toJSON(), 26 | }); 27 | const charlie = await root.createSubAccount("charlie", { 28 | initialBalance: NEAR.parse("30 N").toJSON(), 29 | }); 30 | 31 | // Save state for test runs, it is unique for each test 32 | t.context.worker = worker; 33 | t.context.accounts = { root, contract, alice, bob, charlie }; 34 | }); 35 | 36 | test.afterEach(async (t) => { 37 | // Stop Sandbox server 38 | await t.context.worker.tearDown().catch((error) => { 39 | console.log("Failed to stop the Sandbox:", error); 40 | }); 41 | }); 42 | 43 | test("can be incremented", async (t) => { 44 | const { root, contract } = t.context.accounts; 45 | const startCounter: number = await contract.view("get_num", {}); 46 | await root.call(contract, "increment", {}); 47 | const endCounter = await contract.view("get_num", {}); 48 | t.is(endCounter, startCounter + 1); 49 | }); 50 | 51 | test("can be decremented", async (t) => { 52 | const { root, contract } = t.context.accounts; 53 | await root.call(contract, "increment", {}); 54 | const startCounter: number = await contract.view("get_num", {}); 55 | await root.call(contract, "decrement", {}); 56 | const endCounter = await contract.view("get_num", {}); 57 | t.is(endCounter, startCounter - 1); 58 | }); 59 | 60 | test("can be reset", async (t) => { 61 | const { root, contract } = t.context.accounts; 62 | await root.call(contract, "increment", {}); 63 | await root.call(contract, "increment", {}); 64 | await root.call(contract, "reset", {}); 65 | const endCounter = await contract.view("get_num", {}); 66 | t.is(endCounter, 0); 67 | }); 68 | -------------------------------------------------------------------------------- /frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 |

This counter lives in the NEAR blockchain!

13 | 14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | 29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | 45 | 49 |
50 |
51 |
52 | 53 | 54 | 55 | 56 |
57 |
58 |
59 |
60 |
61 |
62 | 65 | 66 | 70 | 71 |
72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /frontend/assets/js/main.js: -------------------------------------------------------------------------------- 1 | import "regenerator-runtime/runtime"; 2 | import { initContract, login, logout, getCounter, counterIncrement, 3 | counterDecrement, counterReset } from './near/utils' 4 | 5 | function resetUI(){ 6 | document.querySelector('#show').classList.replace('number','loader'); 7 | document.querySelector('#show').innerText = ''; 8 | } 9 | 10 | // Animations 11 | document.querySelector('#c').addEventListener('click', () => { 12 | document.querySelector('#left').classList.toggle('eye'); 13 | }); 14 | document.querySelector('#b').addEventListener('click', () => { 15 | document.querySelector('#right').classList.toggle('eye'); 16 | }); 17 | document.querySelector('#d').addEventListener('click', () => { 18 | document.querySelector('.dot').classList.toggle('on'); 19 | }); 20 | 21 | // Buttons - Interact with the Smart Contract 22 | document.querySelector('#plus').addEventListener('click', async () => { 23 | resetUI(); 24 | await counterIncrement(); 25 | await updateUI(); 26 | }); 27 | 28 | document.querySelector('#minus').addEventListener('click', async () => { 29 | resetUI(); 30 | await counterDecrement(); 31 | await updateUI(); 32 | }); 33 | document.querySelector('#a').addEventListener('click', async () => { 34 | resetUI(); 35 | await counterReset(); 36 | await updateUI(); 37 | }); 38 | 39 | // Log in and log out users using NEAR Wallet 40 | document.querySelector('.sign-in .btn').onclick = login; 41 | document.querySelector('.sign-out .btn').onclick = logout; 42 | 43 | // `nearInitPromise` gets called on page load 44 | window.nearInitPromise = initContract() 45 | .then(flow) 46 | .catch(console.error) 47 | 48 | function flow(){ 49 | if (window.walletConnection.isSignedIn()){ 50 | signedInFlow() 51 | }else{ 52 | signedOutFlow() 53 | } 54 | updateUI() 55 | } 56 | 57 | // Display the signed-out-flow container 58 | function signedOutFlow() { 59 | document.querySelector('.sign-in').style.display = 'block'; 60 | document.querySelectorAll('.interact').forEach(button => button.disabled = true) 61 | } 62 | 63 | // Displaying the signed in flow container and display counter 64 | async function signedInFlow() { 65 | document.querySelector('.sign-out').style.display = 'block'; 66 | document.querySelectorAll('.interact').forEach(button => button.disabled = false) 67 | } 68 | 69 | async function updateUI(){ 70 | let count = await getCounter(); 71 | 72 | document.querySelector('#show').classList.replace('loader','number'); 73 | document.querySelector('#show').innerText = count === undefined ? 'calculating...' : count; 74 | document.querySelector('#left').classList.toggle('eye'); 75 | 76 | if (count >= 0) { 77 | document.querySelector('.mouth').classList.replace('cry','smile'); 78 | } else { 79 | document.querySelector('.mouth').classList.replace('smile','cry'); 80 | } 81 | 82 | if (count > 20 || count < -20) { 83 | document.querySelector('.tongue').style.display = 'block'; 84 | } else { 85 | document.querySelector('.tongue').style.display = 'none'; 86 | } 87 | } -------------------------------------------------------------------------------- /frontend/assets/js/near/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_num'], 22 | // Change methods can modify the state. But you don't receive the returned value when called. 23 | changeMethods: ['increment', 'decrement', 'reset'], 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(nearConfig.contractName) 39 | } 40 | 41 | export async function getCounter(){ 42 | let count = await window.contract.get_num({args:{}}) 43 | .catch(err => errorHelper(err)) 44 | return count; 45 | } 46 | 47 | export async function counterIncrement(){ 48 | await window.contract.increment({args:{}}) 49 | } 50 | 51 | export async function counterDecrement(){ 52 | await window.contract.decrement({args:{}}) 53 | } 54 | 55 | export async function counterReset(){ 56 | await window.contract.reset({args:{}}) 57 | } 58 | 59 | function errorHelper(err) { 60 | // if there's a cryptic error, provide more helpful feedback and instructions here 61 | // TODO: as soon as we get the error codes propagating back, use those 62 | if (err.message.includes('Cannot deserialize the contract state')) { 63 | console.warn('NEAR Warning: the contract/account seems to have state that is not (or no longer) compatible.\n' + 64 | 'This may require deleting and recreating the NEAR account as shown here:\n' + 65 | 'https://stackoverflow.com/a/60767144/711863'); 66 | } 67 | if (err.message.includes('Cannot deserialize the contract state')) { 68 | console.warn('NEAR Warning: the contract/account seems to have state that is not (or no longer) compatible.\n' + 69 | 'This may require deleting and recreating the NEAR account as shown here:\n' + 70 | 'https://stackoverflow.com/a/60767144/711863'); 71 | } 72 | console.error(err); 73 | } 74 | -------------------------------------------------------------------------------- /integration-tests/rs/src/tests.rs: -------------------------------------------------------------------------------- 1 | use serde_json::json; 2 | use near_units::parse_near; 3 | use workspaces::prelude::*; 4 | use workspaces::{network::Sandbox, Account, Contract, Worker}; 5 | 6 | const WASM_FILEPATH: &str = "../../out/main.wasm"; 7 | 8 | #[tokio::main] 9 | async fn main() -> anyhow::Result<()> { 10 | let worker = workspaces::sandbox().await?; 11 | let wasm = std::fs::read(WASM_FILEPATH)?; 12 | let contract = worker.dev_deploy(&wasm).await?; 13 | 14 | // create accounts 15 | let owner = worker.root_account().unwrap(); 16 | let alice = owner 17 | .create_subaccount(&worker, "alice") 18 | .initial_balance(parse_near!("30 N")) 19 | .transact() 20 | .await? 21 | .into_result()?; 22 | 23 | // begin tests 24 | test_increment(&alice, &contract, &worker).await?; 25 | test_decrement(&alice, &contract, &worker).await?; 26 | test_reset(&alice, &contract, &worker).await?; 27 | Ok(()) 28 | } 29 | 30 | async fn test_increment( 31 | user: &Account, 32 | contract: &Contract, 33 | worker: &Worker, 34 | ) -> anyhow::Result<()> { 35 | let start_counter: u64 = user 36 | .call(&worker, contract.id(), "get_num") 37 | .args_json(json!({}))? 38 | .transact() 39 | .await? 40 | .json()?; 41 | 42 | user 43 | .call(&worker, contract.id(), "increment") 44 | .args_json(json!({}))? 45 | .transact() 46 | .await?; 47 | 48 | let end_counter: u64 = user 49 | .call(&worker, contract.id(), "get_num") 50 | .args_json(json!({}))? 51 | .transact() 52 | .await? 53 | .json()?; 54 | 55 | assert_eq!(end_counter, start_counter + 1); 56 | println!(" Passed ✅ can be incremented"); 57 | Ok(()) 58 | } 59 | 60 | async fn test_decrement( 61 | user: &Account, 62 | contract: &Contract, 63 | worker: &Worker, 64 | ) -> anyhow::Result<()> { 65 | user 66 | .call(&worker, contract.id(), "increment") 67 | .args_json(json!({}))? 68 | .transact() 69 | .await?; 70 | 71 | let start_counter: u64 = user 72 | .call(&worker, contract.id(), "get_num") 73 | .args_json(json!({}))? 74 | .transact() 75 | .await? 76 | .json()?; 77 | 78 | user 79 | .call(&worker, contract.id(), "decrement") 80 | .args_json(json!({}))? 81 | .transact() 82 | .await?; 83 | 84 | let end_counter: u64 = user 85 | .call(&worker, contract.id(), "get_num") 86 | .args_json(json!({}))? 87 | .transact() 88 | .await? 89 | .json()?; 90 | 91 | assert_eq!(end_counter, start_counter - 1); 92 | println!(" Passed ✅ can be decremented"); 93 | Ok(()) 94 | } 95 | 96 | async fn test_reset( 97 | user: &Account, 98 | contract: &Contract, 99 | worker: &Worker, 100 | ) -> anyhow::Result<()> { 101 | user 102 | .call(&worker, contract.id(), "increment") 103 | .args_json(json!({}))? 104 | .transact() 105 | .await?; 106 | 107 | user 108 | .call(&worker, contract.id(), "increment") 109 | .args_json(json!({}))? 110 | .transact() 111 | .await?; 112 | 113 | user 114 | .call(&worker, contract.id(), "reset") 115 | .args_json(json!({}))? 116 | .transact() 117 | .await?; 118 | 119 | let end_counter: u64 = user 120 | .call(&worker, contract.id(), "get_num") 121 | .args_json(json!({}))? 122 | .transact() 123 | .await? 124 | .json()?; 125 | 126 | assert_eq!(end_counter, 0); 127 | println!(" Passed ✅ can be reset"); 128 | Ok(()) 129 | } -------------------------------------------------------------------------------- /frontend/assets/css/global.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | margin-top: 1em !important; 3 | margin-bottom: 1em !important; 4 | text-align: center; 5 | } 6 | 7 | .scene { 8 | height: 350px; 9 | } 10 | .face { 11 | margin: auto; 12 | width: 66%; 13 | height: 72%; 14 | background-color: #fffaf2; 15 | } 16 | .number { 17 | text-align: center; 18 | margin:auto; 19 | width: 66%; 20 | height: 28%; 21 | background-color: #ffd9f8; 22 | border-top: 5px dotted #82174d; 23 | font-size:1.5rem; 24 | } 25 | .eyes-row { 26 | display: flex; 27 | justify-content: space-around; 28 | margin-top: 24%; 29 | padding: 0 8px; 30 | } 31 | .closed { 32 | width: 22px; 33 | height: 13px; 34 | border-top-left-radius: 100px; 35 | border-top-right-radius: 100px; 36 | border-left: 2px solid #ffa4b1; 37 | border-right: 2px solid #ffa4b1; 38 | border-top: 2px solid #ffa4b1; 39 | margin-bottom: 12px; 40 | } 41 | .eye { 42 | width: 26px; 43 | height: 26px; 44 | border-radius: 100px; 45 | background-color: #ffa4b1; 46 | display: flex; 47 | justify-content: center; 48 | align-items: center; 49 | margin-bottom: 1px; 50 | } 51 | .eye .pupil { 52 | position: relative; 53 | width: 18px; 54 | height: 18px; 55 | border-radius: 100px; 56 | background-color: #82174d; 57 | } 58 | .eye .pupil:before { 59 | content: ""; 60 | width: 6px; 61 | height: 6px; 62 | position: absolute; 63 | background-color: #fff; 64 | top: 4px; 65 | left: 3px; 66 | border-radius: 100px; 67 | } 68 | .eye .pupil:after { 69 | content: ""; 70 | width: 3px; 71 | height: 3px; 72 | position: absolute; 73 | background-color: #fff; 74 | top: 9px; 75 | left: 12px; 76 | border-radius: 100px; 77 | } 78 | .mouth-row { 79 | display: flex; 80 | flex-direction: column; 81 | justify-content: space-around; 82 | align-items: center; 83 | } 84 | .mouth-row .mouth { 85 | margin-top: 3%; 86 | } 87 | .mouth-row .tongue { 88 | position:absolute; 89 | width: 10px; 90 | height: 10px; 91 | background-color: #FF585D; 92 | border-radius: 50% 50% 30%; 93 | margin: 5px 0; 94 | } 95 | .mouth-row .mouth.smile { 96 | height: 16px; 97 | width: 32px; 98 | border-bottom-left-radius: 32px; 99 | border-bottom-right-radius: 32px; 100 | background-color: #82174d; 101 | top: 120px; 102 | } 103 | .mouth-row .mouth.cry { 104 | height: 16px; 105 | width: 32px; 106 | border-top-left-radius: 32px; 107 | border-top-right-radius: 32px; 108 | background-color: #82174d; 109 | top: 120px; 110 | } 111 | .mouth-row .mouth:before { 112 | content: ""; 113 | left: 43px; 114 | margin-top: 1%; 115 | border-radius: 100%; 116 | height: 5%; 117 | width: 11.5%; 118 | background-color: #f56f79; 119 | position: absolute; 120 | } 121 | .mouth-row .mouth:after { 122 | content: ""; 123 | left: 106px; 124 | margin-top: 1%; 125 | border-radius: 100%; 126 | height: 5%; 127 | width: 11.5%; 128 | background-color: #f56f79; 129 | position: absolute; 130 | } 131 | .gameboy { 132 | position: relative; 133 | width: 400px; 134 | margin: auto; 135 | } 136 | .body-shape { 137 | position: absolute; 138 | height: 300px; 139 | width: 205px; 140 | border-radius: 15px; 141 | } 142 | .body-shape .shadow { 143 | height: 290px; 144 | } 145 | .body-shape .screen { 146 | margin: 20px; 147 | position: relative; 148 | width: 168px; 149 | height: 134px; 150 | background-color: #82174d; 151 | border-radius: 4px; 152 | display: flex; 153 | flex-direction: column; 154 | justify-items: center; 155 | } 156 | .body-shape .dot { 157 | content: ""; 158 | width: 8px; 159 | height: 8px; 160 | position: absolute; 161 | background-color: #ffa4b1; 162 | top: 40px; 163 | left: 10px; 164 | border-radius: 100px; 165 | } 166 | .body-shape .on { 167 | background-color: #F0EC74; 168 | } 169 | .body-shape .buttons { 170 | display: flex; 171 | flex-direction: column; 172 | justify-content: space-around; 173 | width: 100%; 174 | } 175 | .body-shape .buttons .row { 176 | display: flex; 177 | } 178 | .body-shape .buttons .selects { 179 | margin-top: 10px; 180 | padding-left: 140px; 181 | } 182 | .body-shape .buttons .arrows { 183 | position: relative; 184 | height: 40px; 185 | width: 40px; 186 | border-radius: 100px; 187 | background-color: #ffa4b1; 188 | margin-left: 30px; 189 | padding: 0; 190 | cursor: grab; 191 | } 192 | .body-shape .buttons .arrows:disabled { 193 | opacity: 0.8; 194 | background-color: #d18892; 195 | } 196 | .body-shape .buttons .arrows .updown { 197 | position: relative; 198 | height: 35px; 199 | width: 10px; 200 | background-color: #82174d; 201 | border-radius: 2px; 202 | top: -8px; 203 | left: 14px; 204 | } 205 | .body-shape .buttons .arrows .left { 206 | position: relative; 207 | width: 35px; 208 | height: 10px; 209 | background-color: #82174d; 210 | border-radius: 2px; 211 | top: 14px; 212 | left: 2px; 213 | } 214 | .body-shape .buttons .arrows .right { 215 | position: relative; 216 | width: 35px; 217 | height: 10px; 218 | background-color: #82174d; 219 | border-radius: 2px; 220 | left: 2px; 221 | } 222 | .body-shape .buttons .ab { 223 | width: 64px; 224 | height: 64px; 225 | background-color: #ffa4b1; 226 | border-radius: 10px; 227 | transform: rotate(45deg); 228 | font-size: 0.6rem; 229 | text-align: center; 230 | } 231 | .body-shape .buttons .ab .r { 232 | width: 24px; 233 | height: 24px; 234 | border-radius: 100%; 235 | background-color: #82174d; 236 | position: absolute; 237 | transform: rotate(-45deg); 238 | padding: 5px; 239 | color: #ffa4b1; 240 | cursor:pointer; 241 | box-shadow:-1px 0 1px #4B4F54; 242 | } 243 | .body-shape .buttons .ab .r:hover { 244 | opacity: 0.8; 245 | } 246 | .body-shape .buttons .ab .r:active { 247 | background-color: #F0EC74; 248 | opacity: 1; 249 | } 250 | .body-shape .buttons .ab .a { 251 | top: 4px; 252 | left: 4px; 253 | } 254 | .body-shape .buttons .ab .b { 255 | top: 4px; 256 | left: 36px; 257 | } 258 | .body-shape .buttons .ab .c { 259 | top: 36px; 260 | left: 4px; 261 | } 262 | .body-shape .buttons .ab .d { 263 | top: 36px; 264 | left: 36px; 265 | } 266 | .shadow { 267 | background-color: #888ad0; 268 | z-index: 0; 269 | left: -17px; 270 | top: 26px; 271 | } 272 | .side { 273 | background-color: #82174d; 274 | z-index: 1; 275 | } 276 | .front { 277 | background-color: #f56f79; 278 | left: 26px; 279 | z-index: 2; 280 | } 281 | .loader, 282 | .loader:before, 283 | .loader:after { 284 | background: #f56f79; 285 | -webkit-animation: load1 1s infinite ease-in-out; 286 | animation: load1 1s infinite ease-in-out; 287 | width: 0.4rem; 288 | height: 4em; 289 | } 290 | .loader { 291 | color: #f56f79; 292 | margin: 2px auto; 293 | font-size: 0.2rem; 294 | -webkit-transform: translateZ(0); 295 | -ms-transform: translateZ(0); 296 | transform: translateZ(0); 297 | -webkit-animation-delay: -0.16s; 298 | animation-delay: -0.16s; 299 | } 300 | .loader:before, 301 | .loader:after { 302 | position: absolute; 303 | top: 0; 304 | content: ''; 305 | } 306 | .loader:before { 307 | left: -1.5em; 308 | -webkit-animation-delay: -0.32s; 309 | animation-delay: -0.32s; 310 | } 311 | .loader:after { 312 | left: 1.5em; 313 | } 314 | @-webkit-keyframes load1 { 315 | 0%, 316 | 80%, 317 | 100% { 318 | box-shadow: 0 0; 319 | height: 2em; 320 | } 321 | 40% { 322 | box-shadow: 0 -2em; 323 | height: 3em; 324 | } 325 | } 326 | @keyframes load1 { 327 | 0%, 328 | 80%, 329 | 100% { 330 | box-shadow: 0 0; 331 | height: 2em; 332 | } 333 | 40% { 334 | box-shadow: 0 -2em; 335 | height: 3em; 336 | } 337 | } -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | --------------------------------------------------------------------------------