├── .editorconfig ├── .gitignore ├── .prettierignore ├── .prettierrc ├── LICENSE ├── README.md ├── buildspec.yaml ├── makefile ├── package.json ├── public ├── favicon.png ├── index.html ├── manifest.json ├── robots.txt └── vs │ ├── base │ └── worker │ │ └── workerMain.js │ └── loader.js ├── src ├── assets │ ├── fonts │ │ └── NotoSans-Regular.ttf │ └── images │ │ ├── asset_a.png │ │ ├── asset_b.png │ │ ├── asset_c.png │ │ ├── asset_d.png │ │ ├── asset_e.png │ │ ├── asset_f.png │ │ └── zilliqa.png ├── components │ ├── chapter-complete-card │ │ ├── __snapshots__ │ │ │ └── index.test.tsx.snap │ │ ├── index.test.tsx │ │ ├── index.tsx │ │ └── style.css │ ├── chapter-list │ │ ├── index.tsx │ │ └── style.css │ ├── cheat-sheet-modal │ │ ├── __snapshots__ │ │ │ └── index.test.tsx.snap │ │ ├── index.test.tsx │ │ ├── index.tsx │ │ └── style.css │ ├── editor-control-panel │ │ └── index.tsx │ ├── editor-diff-viewer │ │ ├── index.tsx │ │ └── style.css │ ├── editor-input │ │ ├── index.tsx │ │ └── style.css │ ├── editor │ │ └── index.tsx │ ├── footer │ │ ├── __snapshots__ │ │ │ └── index.test.tsx.snap │ │ ├── index.test.tsx │ │ ├── index.tsx │ │ └── style.module.css │ ├── header │ │ └── index.tsx │ ├── instruction-viewer │ │ ├── index.tsx │ │ └── style.css │ ├── lesson-navigator │ │ ├── __snapshots__ │ │ │ └── index.test.tsx.snap │ │ ├── index.test.tsx │ │ └── index.tsx │ └── lesson-progressbar │ │ ├── __snapshots__ │ │ └── index.test.tsx.snap │ │ ├── index.test.tsx │ │ ├── index.tsx │ │ └── style.css ├── containers │ ├── chapter-list-container │ │ ├── index.tsx │ │ └── style.css │ ├── home-container │ │ └── index.tsx │ └── lesson-container │ │ └── index.tsx ├── course-codes │ ├── chapter1 │ │ └── index.ts │ ├── chapter2 │ │ └── index.ts │ ├── chapter3 │ │ └── index.ts │ └── index.ts ├── fn-components │ ├── button │ │ ├── __snapshots__ │ │ │ └── index.test.tsx.snap │ │ ├── index.test.tsx │ │ └── index.tsx │ ├── colors.ts │ ├── index.ts │ ├── modal │ │ ├── __snapshots__ │ │ │ └── index.test.tsx.snap │ │ ├── index.test.tsx │ │ └── index.tsx │ └── spinner │ │ ├── __snapshots__ │ │ └── index.test.tsx.snap │ │ ├── index.test.tsx │ │ └── index.tsx ├── index.css ├── index.tsx ├── locales │ └── instructions │ │ ├── en │ │ ├── chapter1 │ │ │ └── index.ts │ │ ├── chapter2 │ │ │ └── index.ts │ │ ├── chapter3 │ │ │ └── index.ts │ │ └── index.ts │ │ └── index.ts ├── react-app-env.d.ts ├── routes.tsx ├── scilla.ts ├── serviceWorker.ts ├── setupTests.ts └── typings.d.ts ├── template.yaml ├── tsconfig.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | indent_size = 2 9 | indent_style = space 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [*.md] 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | functions/node_modules 6 | 7 | /.pnp 8 | .pnp.js 9 | 10 | # testing 11 | /coverage 12 | 13 | # production 14 | /build 15 | 16 | # misc 17 | .DS_Store 18 | .env.local 19 | .env.development.local 20 | .env.test.local 21 | .env.production.local 22 | 23 | yarn-debug.log* 24 | yarn-error.log* 25 | 26 | # firebase 27 | .firebase 28 | firebase-debug.log* 29 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Build folder and files # 2 | ########################## 3 | build/ 4 | 5 | # Development folders and files # 6 | ################################# 7 | .tmp/ 8 | dist/ 9 | node_modules/ 10 | *.compiled.* 11 | coverage 12 | public 13 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "typescript", 3 | "printWidth": 100, 4 | "singleQuote": true, 5 | "arrowParens": "always", 6 | "overrides": [ 7 | { 8 | "files": "*.css", 9 | "options": { 10 | "parser": "css" 11 | } 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![License][license-svg]][license-url] [![Discord chat][discord-svg]][discord-url] 2 | 3 | # learn-scilla 4 | 5 | An interactive tutorial for people to learn Scilla, Zilliqa’s smart contract language, through a gamification process. 6 | 7 | ## About Scilla 8 | 9 |

10 | 11 | [Scilla](https://scilla-lang.org/) is an intermediate-level smart contract language being developed for [Zilliqa](https://zilliqa.com/). Scilla has been designed as a principled language with smart contract safety in mind. Scilla imposes a structure on smart contracts that will make applications less vulnerable to attacks by eliminating certain known vulnerabilities directly at the language-level. 12 | 13 | ## Installation and Usage 14 | 15 | ### `yarn` 16 | 17 | Installs the dependencies. 18 | 19 | ### `yarn start` 20 | 21 | Runs the app in development mode. 22 | Open `http://localhost:3000` to view it in the browser. 23 | 24 | ### `yarn test` 25 | 26 | Runs the test watcher in an interactive mode. 27 | We use [Jest](https://jestjs.io/) for testing. 28 | 29 | ## License 30 | 31 | This project is open source software licensed as [GPL-3.0](https://github.com/neutiyoo/learn-scilla/blob/develop/LICENSE). 32 | 33 | [license-svg]: https://img.shields.io/badge/License-GPLv3-blue.svg 34 | [license-url]: https://github.com/neutiyoo/learn-scilla/blob/master/LICENSE 35 | [discord-svg]: https://img.shields.io/discord/308323056592486420.svg 36 | [discord-url]: https://discord.gg/mWp9HdR 37 | -------------------------------------------------------------------------------- /buildspec.yaml: -------------------------------------------------------------------------------- 1 | version: 0.2 2 | 3 | phases: 4 | install: 5 | runtime-versions: 6 | nodejs: 10 7 | commands: 8 | - echo Installing source NPM dependencies... 9 | - npm i npm@latest -g 10 | - npm install -g yarn 11 | - pip install --upgrade pip 12 | - pip install --upgrade awscli 13 | pre_build: 14 | commands: 15 | - yarn install 16 | - yarn validate 17 | build: 18 | commands: 19 | - yarn build 20 | post_build: 21 | commands: 22 | - echo Entered the post_build phase... 23 | - aws cloudfront create-invalidation --distribution-id $CLOUDFRONT_DIST_ID --paths "/*" 24 | 25 | artifacts: 26 | files: 27 | - '**/*' 28 | base-directory: build -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | stack: 2 | aws cloudformation deploy --stack-name learn-scilla-prod --template-file template.yaml --region ap-southeast-1 -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "learn-scilla", 3 | "keywords": [ 4 | "scilla", 5 | "smart contract", 6 | "zilliqa", 7 | "blockchain" 8 | ], 9 | "version": "0.1.0", 10 | "scripts": { 11 | "start": "react-scripts start", 12 | "build": "react-scripts build", 13 | "test": "react-scripts test", 14 | "test:cov": "react-scripts test --coverage", 15 | "test:ci": "CI=true react-scripts test --coverage", 16 | "eject": "react-scripts eject", 17 | "prettier": "prettier \"**/*.+(ts|tsx|css)\"", 18 | "format": "npm run prettier -- --write", 19 | "validate": "npm run prettier -- --list-different && yarn test:ci && tsc --noEmit" 20 | }, 21 | "dependencies": { 22 | "@testing-library/react": "^10.0.1", 23 | "@types/jest": "^24.0.22", 24 | "@types/node": "^12.12.6", 25 | "@types/react": "^16.9.11", 26 | "@types/react-dom": "^16.9.4", 27 | "bootstrap": "^4.3.1", 28 | "classnames": "^2.2.6", 29 | "enzyme": "^3.9.0", 30 | "enzyme-adapter-react-16": "^1.15.1", 31 | "husky": "^3.0.9", 32 | "jest-styled-components": "^6.3.1", 33 | "prettier": "^1.18.2", 34 | "react": "^16.11.0", 35 | "react-dom": "^16.11.0", 36 | "react-ga": "^2.6.0", 37 | "react-helmet": "^5.2.1", 38 | "react-icons": "^3.8.0", 39 | "react-markdown": "^4.2.2", 40 | "react-monaco-editor": "0.25.1", 41 | "react-router": "^5.1.2", 42 | "react-router-dom": "^5.1.2", 43 | "react-scripts": "^3.2.0", 44 | "react-test-renderer": "^16.11.0", 45 | "styled-components": "^4.3.2", 46 | "typescript": "^3.4.5" 47 | }, 48 | "jest": { 49 | "collectCoverageFrom": [ 50 | "src/components/**/*.{ts,tsx}" 51 | ] 52 | }, 53 | "husky": { 54 | "hooks": { 55 | "pre-commit": "yarn validate && yarn test:ci" 56 | } 57 | }, 58 | "license": "GPL-3.0", 59 | "browserslist": [ 60 | ">0.2%", 61 | "not dead", 62 | "not ie <= 11", 63 | "not op_mini all" 64 | ] 65 | } 66 | -------------------------------------------------------------------------------- /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zilliqa/learn-scilla/0838df6c3d3b8c3a2e5d002b7f7456a35d931b2c/public/favicon.png -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | LearnScilla - An interactive tutorial for people to learn Scilla 8 | 9 | 11 | 13 | 14 | 15 | 16 | 20 | 21 | 30 | 31 | 32 | 37 | 45 | 46 | 47 | 53 | 54 | 55 | 56 | 59 | 60 |
61 |
62 |
 
 
 
63 |
 
64 |
65 |
66 | 67 | 68 | 69 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | user-agent: * 2 | disallow: /account/ 3 | -------------------------------------------------------------------------------- /src/assets/fonts/NotoSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zilliqa/learn-scilla/0838df6c3d3b8c3a2e5d002b7f7456a35d931b2c/src/assets/fonts/NotoSans-Regular.ttf -------------------------------------------------------------------------------- /src/assets/images/asset_a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zilliqa/learn-scilla/0838df6c3d3b8c3a2e5d002b7f7456a35d931b2c/src/assets/images/asset_a.png -------------------------------------------------------------------------------- /src/assets/images/asset_b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zilliqa/learn-scilla/0838df6c3d3b8c3a2e5d002b7f7456a35d931b2c/src/assets/images/asset_b.png -------------------------------------------------------------------------------- /src/assets/images/asset_c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zilliqa/learn-scilla/0838df6c3d3b8c3a2e5d002b7f7456a35d931b2c/src/assets/images/asset_c.png -------------------------------------------------------------------------------- /src/assets/images/asset_d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zilliqa/learn-scilla/0838df6c3d3b8c3a2e5d002b7f7456a35d931b2c/src/assets/images/asset_d.png -------------------------------------------------------------------------------- /src/assets/images/asset_e.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zilliqa/learn-scilla/0838df6c3d3b8c3a2e5d002b7f7456a35d931b2c/src/assets/images/asset_e.png -------------------------------------------------------------------------------- /src/assets/images/asset_f.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zilliqa/learn-scilla/0838df6c3d3b8c3a2e5d002b7f7456a35d931b2c/src/assets/images/asset_f.png -------------------------------------------------------------------------------- /src/assets/images/zilliqa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zilliqa/learn-scilla/0838df6c3d3b8c3a2e5d002b7f7456a35d931b2c/src/assets/images/zilliqa.png -------------------------------------------------------------------------------- /src/components/chapter-complete-card/__snapshots__/index.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Chapter Complete Card tests basic tests matches the snapshot 1`] = ` 4 | .c0 { 5 | outline: none; 6 | margin: 0; 7 | font-family: inherit; 8 | overflow: visible; 9 | text-transform: none; 10 | display: inline-block; 11 | font-weight: 400; 12 | text-align: center; 13 | vertical-align: middle; 14 | -webkit-user-select: none; 15 | -moz-user-select: none; 16 | -ms-user-select: none; 17 | user-select: none; 18 | background-color: transparent; 19 | border: 1px solid transparent; 20 | -webkit-transition: color 0.15s ease-in-out,background-color 0.15s ease-in-out, border-color 0.15s ease-in-out,box-shadow 0.15s ease-in-out; 21 | transition: color 0.15s ease-in-out,background-color 0.15s ease-in-out, border-color 0.15s ease-in-out,box-shadow 0.15s ease-in-out; 22 | cursor: pointer; 23 | } 24 | 25 | .c0::-webkit-file-upload-button { 26 | font: inherit; 27 | -webkit-appearance: button; 28 | } 29 | 30 | .c0 [type='button'], 31 | .c0 [type='reset'], 32 | .c0 [type='submit'] { 33 | -webkit-appearance: button; 34 | } 35 | 36 | .c0::-moz-focus-inner, 37 | .c0 [type='button']::-moz-focus-inner, 38 | .c0 [type='reset']::-moz-focus-inner, 39 | .c0 [type='submit']::-moz-focus-inner { 40 | padding: 0; 41 | border-style: none; 42 | } 43 | 44 | .c0 { 45 | padding: 0.375rem 0.75rem; 46 | font-size: 1rem; 47 | line-height: 1.5; 48 | border-radius: 0.25rem; 49 | } 50 | 51 | .c0 { 52 | background-color: #0000cc; 53 | color: #ffffff; 54 | border-color: transparent; 55 | opacity: 0.85; 56 | } 57 | 58 | .c0:hover, 59 | .c0:active, 60 | .c0:focus { 61 | opacity: 1; 62 | } 63 | 64 |
68 |
71 |

72 | Good Job 73 | ! 74 |

75 |

76 | You have completed this chapter. 77 |

78 |
79 |
80 | 91 |
92 |
93 | 98 | Table of Contents 99 | 100 |
101 |
102 | `; 103 | -------------------------------------------------------------------------------- /src/components/chapter-complete-card/index.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import * as renderer from 'react-test-renderer'; 4 | import { MemoryRouter } from 'react-router-dom'; 5 | import ChapterCompleteCard from '.'; 6 | 7 | const t = (s: string) => s; 8 | const navigate = (chapterNum, lessonNum) => console.log('navigate', chapterNum, lessonNum); 9 | 10 | describe('Chapter Complete Card tests', () => { 11 | const baseComponent = (props) => ( 12 | 13 | 14 | 15 | ); 16 | 17 | describe('basic tests', () => { 18 | it('matches the snapshot', () => { 19 | const tree = renderer.create(baseComponent({ total: 2, chapter: 1 })).toJSON(); 20 | expect(tree).toMatchSnapshot(); 21 | }); 22 | 23 | it('renders without crashing while loading', () => { 24 | const div = document.createElement('div'); 25 | ReactDOM.render(baseComponent({ total: 2, chapter: 1 }), div); 26 | ReactDOM.unmountComponentAtNode(div); 27 | }); 28 | 29 | it('renders without crashing after loaded', () => { 30 | const div = document.createElement('div'); 31 | ReactDOM.render(baseComponent({ total: 2, chapter: 1 }), div); 32 | ReactDOM.unmountComponentAtNode(div); 33 | }); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /src/components/chapter-complete-card/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { FunctionComponent } from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import { paths } from '../../routes'; 4 | import './style.css'; 5 | import { Button } from '../../fn-components'; 6 | 7 | interface IProps { 8 | total: number; 9 | chapter: number; 10 | navigate: (chapterNum: number, lessonNum: number) => void; 11 | } 12 | 13 | const ChapterCompleteCard: FunctionComponent = ({ total, chapter, navigate }) => { 14 | // Check if the current chapter is the last one. 15 | const isLast: boolean = chapter === total; 16 | 17 | return ( 18 |
22 |
23 |

{`Good Job`}!

24 |

{`You have completed this chapter.`}

25 |
26 |
27 | {isLast ? null : ( 28 |
36 |
37 | 38 | {`Table of Contents`} 39 | 40 |
41 |
42 | ); 43 | }; 44 | 45 | export default ChapterCompleteCard; 46 | -------------------------------------------------------------------------------- /src/components/chapter-complete-card/style.css: -------------------------------------------------------------------------------- 1 | .chapter-complete-card { 2 | height: 550px; 3 | } 4 | -------------------------------------------------------------------------------- /src/components/chapter-list/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { CourseInstructionType } from '../../typings'; 3 | import './style.css'; 4 | import { Button } from '../../fn-components'; 5 | 6 | interface IProps { 7 | chapterList: CourseInstructionType; 8 | navigate: (chapterNum, lessonNum) => void; 9 | } 10 | 11 | const ChapterList: React.SFC = (props) => { 12 | const { chapterList, navigate } = props; 13 | const list = chapterList || []; 14 | 15 | const result = list.map((item, index) => { 16 | const chapterNum: number = index + 1; 17 | 18 | const chapterProgressNum = Number(localStorage.getItem(`chapter${chapterNum}`)) || 0; 19 | 20 | const lessons: string[] = item.lessons || []; 21 | const totalNum: number = lessons.length; 22 | const lessonNum = totalNum <= chapterProgressNum ? totalNum : chapterProgressNum + 1; 23 | const progressText = `(${lessonNum}/${totalNum})`; 24 | 25 | return ( 26 |
27 |
37 | ); 38 | }); 39 | 40 | return ( 41 |
42 | {result} 43 |
44 | ); 45 | }; 46 | 47 | export default ChapterList; 48 | -------------------------------------------------------------------------------- /src/components/chapter-list/style.css: -------------------------------------------------------------------------------- 1 | .chapter-list { 2 | max-width: 400px; 3 | } 4 | -------------------------------------------------------------------------------- /src/components/cheat-sheet-modal/__snapshots__/index.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Cheat Sheet Modal tests basic tests matches the snapshot 1`] = ` 4 | .c0 { 5 | outline: none; 6 | margin: 0; 7 | font-family: inherit; 8 | overflow: visible; 9 | text-transform: none; 10 | display: inline-block; 11 | font-weight: 400; 12 | text-align: center; 13 | vertical-align: middle; 14 | -webkit-user-select: none; 15 | -moz-user-select: none; 16 | -ms-user-select: none; 17 | user-select: none; 18 | background-color: transparent; 19 | border: 1px solid transparent; 20 | -webkit-transition: color 0.15s ease-in-out,background-color 0.15s ease-in-out, border-color 0.15s ease-in-out,box-shadow 0.15s ease-in-out; 21 | transition: color 0.15s ease-in-out,background-color 0.15s ease-in-out, border-color 0.15s ease-in-out,box-shadow 0.15s ease-in-out; 22 | cursor: pointer; 23 | } 24 | 25 | .c0::-webkit-file-upload-button { 26 | font: inherit; 27 | -webkit-appearance: button; 28 | } 29 | 30 | .c0 [type='button'], 31 | .c0 [type='reset'], 32 | .c0 [type='submit'] { 33 | -webkit-appearance: button; 34 | } 35 | 36 | .c0::-moz-focus-inner, 37 | .c0 [type='button']::-moz-focus-inner, 38 | .c0 [type='reset']::-moz-focus-inner, 39 | .c0 [type='submit']::-moz-focus-inner { 40 | padding: 0; 41 | border-style: none; 42 | } 43 | 44 | .c0 { 45 | padding: 0.25rem 0.5rem; 46 | font-size: 0.875rem; 47 | line-height: 1.5; 48 | border-radius: 0.2rem; 49 | } 50 | 51 | .c0 { 52 | background-color: transparent; 53 | color: #000000; 54 | border-color: #000000; 55 | opacity: 0.85; 56 | } 57 | 58 | .c0:hover, 59 | .c0:active, 60 | .c0:focus { 61 | opacity: 1; 62 | } 63 | 64 | 67 | 97 | 98 | `; 99 | -------------------------------------------------------------------------------- /src/components/cheat-sheet-modal/index.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import * as renderer from 'react-test-renderer'; 3 | import CheatSheetModal from '.'; 4 | import { shallow } from 'enzyme'; 5 | 6 | const t = (s: string) => s; 7 | 8 | describe('Cheat Sheet Modal tests', () => { 9 | const baseComponent = () => ; 10 | 11 | describe('basic tests', () => { 12 | it('matches the snapshot', () => { 13 | const tree = renderer.create(baseComponent()).toJSON(); 14 | expect(tree).toMatchSnapshot(); 15 | }); 16 | 17 | it('renders the component', () => { 18 | const wrapper = shallow(baseComponent()); 19 | const assertion = wrapper.find('[data-testid="cheat-sheet-modal"]').length; 20 | expect(assertion).toBe(1); 21 | }); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /src/components/cheat-sheet-modal/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { Button, Modal } from '../../fn-components'; 3 | import { FaCode } from 'react-icons/fa'; 4 | import './style.css'; 5 | 6 | const CheatSheetModal = () => { 7 | const [isOpen, setIsOpen] = useState(false); 8 | return ( 9 | 10 | 51 | 52 |
56 |
    57 |
  • 58 | 63 | {'Home'} 64 | 65 |
  • 66 | 67 |
  • 68 | 73 | {'Tutorial'} 74 | 75 |
  • 76 |
77 |
78 | 79 | ); 80 | }; 81 | 82 | export default Header; 83 | -------------------------------------------------------------------------------- /src/components/instruction-viewer/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactMarkdown from 'react-markdown'; 3 | import MonacoEditor from 'react-monaco-editor'; 4 | import { language, configuration } from '../../scilla'; 5 | import './style.css'; 6 | 7 | interface IProps { 8 | instruction: any; 9 | } 10 | 11 | class InstructionViewer extends React.Component { 12 | public editor; 13 | private myRef = React.createRef(); 14 | public componentWillReceiveProps(nextProps) { 15 | const node = this.myRef.current; 16 | if (node) { 17 | node.scrollTop = 0; 18 | } 19 | } 20 | 21 | public render() { 22 | return ( 23 |
24 | 25 |
26 | ); 27 | } 28 | 29 | private editorWillMount = (monaco) => { 30 | this.editor = monaco; 31 | if (!monaco.languages.getLanguages().some(({ id }) => id === 'scilla')) { 32 | // Register a new language 33 | monaco.languages.register({ id: 'scilla' }); 34 | // Register a tokens provider for the language 35 | monaco.languages.setMonarchTokensProvider('scilla', language); 36 | // Set the editing configuration for the language 37 | monaco.languages.setLanguageConfiguration('scilla', configuration); 38 | } 39 | }; 40 | 41 | private renderCode = (props) => { 42 | const options: any = { 43 | readOnly: true, 44 | lineNumbers: 'off', 45 | overviewRulerBorder: false, 46 | hideCursorInOverviewRuler: true, 47 | occurrencesHighlight: false, 48 | scrollBeyondLastLine: false, 49 | minimap: { 50 | enabled: false 51 | } 52 | }; 53 | 54 | // This className decide code visibility 55 | 56 | return ( 57 |
58 | 65 |
66 | ); 67 | }; 68 | } 69 | export default InstructionViewer; 70 | -------------------------------------------------------------------------------- /src/components/instruction-viewer/style.css: -------------------------------------------------------------------------------- 1 | .instruction-viewer-container .monaco-editor .margin { 2 | background-color: var(--white) !important; 3 | } 4 | 5 | .instruction-viewer-container { 6 | height: 550px; 7 | overflow-y: scroll; 8 | overflow-x: auto; 9 | padding-top: 10px; 10 | padding-right: 10px; 11 | border: 1px solid var(--gray300); 12 | border-left: none; 13 | } 14 | 15 | div.instruction-viewer-container::-webkit-scrollbar { 16 | width: 3px; 17 | height: 3px; 18 | } 19 | 20 | div.instruction-viewer-container::-webkit-scrollbar-track { 21 | box-shadow: inset 0 0 15px rgba(0, 0, 0, 0.3); 22 | } 23 | 24 | div.instruction-viewer-container::-webkit-scrollbar-thumb { 25 | box-shadow: inset 0 0 15px rgba(0, 0, 0, 0.5); 26 | } 27 | 28 | .instruction-code-block { 29 | margin-bottom: 1rem; 30 | } 31 | -------------------------------------------------------------------------------- /src/components/lesson-navigator/__snapshots__/index.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Lesson Navigator tests basic tests matches the snapshot 1`] = ` 4 | .c0 { 5 | outline: none; 6 | margin: 0; 7 | font-family: inherit; 8 | overflow: visible; 9 | text-transform: none; 10 | display: inline-block; 11 | font-weight: 400; 12 | text-align: center; 13 | vertical-align: middle; 14 | -webkit-user-select: none; 15 | -moz-user-select: none; 16 | -ms-user-select: none; 17 | user-select: none; 18 | background-color: transparent; 19 | border: 1px solid transparent; 20 | -webkit-transition: color 0.15s ease-in-out,background-color 0.15s ease-in-out, border-color 0.15s ease-in-out,box-shadow 0.15s ease-in-out; 21 | transition: color 0.15s ease-in-out,background-color 0.15s ease-in-out, border-color 0.15s ease-in-out,box-shadow 0.15s ease-in-out; 22 | cursor: pointer; 23 | } 24 | 25 | .c0::-webkit-file-upload-button { 26 | font: inherit; 27 | -webkit-appearance: button; 28 | } 29 | 30 | .c0 [type='button'], 31 | .c0 [type='reset'], 32 | .c0 [type='submit'] { 33 | -webkit-appearance: button; 34 | } 35 | 36 | .c0::-moz-focus-inner, 37 | .c0 [type='button']::-moz-focus-inner, 38 | .c0 [type='reset']::-moz-focus-inner, 39 | .c0 [type='submit']::-moz-focus-inner { 40 | padding: 0; 41 | border-style: none; 42 | } 43 | 44 | .c0 { 45 | padding: 0.25rem 0.5rem; 46 | font-size: 0.875rem; 47 | line-height: 1.5; 48 | border-radius: 0.2rem; 49 | } 50 | 51 | .c0 { 52 | background-color: transparent; 53 | color: #000000; 54 | border-color: #000000; 55 | opacity: 0.85; 56 | } 57 | 58 | .c0:hover, 59 | .c0:active, 60 | .c0:focus { 61 | opacity: 1; 62 | } 63 | 64 |
69 | 99 | 129 |
130 | `; 131 | -------------------------------------------------------------------------------- /src/components/lesson-navigator/index.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import * as renderer from 'react-test-renderer'; 3 | import { shallow } from 'enzyme'; 4 | import LessonNavigator from '.'; 5 | 6 | const goNext = () => console.log('goNext'); 7 | const goBack = () => console.log('goBack'); 8 | const t = (s: string) => s; 9 | 10 | describe('Lesson Navigator tests', () => { 11 | const baseComponent = () => ( 12 | 13 | ); 14 | 15 | describe('basic tests', () => { 16 | it('matches the snapshot', () => { 17 | const tree = renderer.create(baseComponent()).toJSON(); 18 | expect(tree).toMatchSnapshot(); 19 | }); 20 | 21 | it('renders the component', () => { 22 | const wrapper = shallow(baseComponent()); 23 | const assertion = wrapper.find('[data-testid="lesson-navigator"]').length; 24 | expect(assertion).toBe(1); 25 | }); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /src/components/lesson-navigator/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { FaArrowLeft, FaArrowRight } from 'react-icons/fa'; 3 | import { Button } from '../../fn-components'; 4 | 5 | interface IProps { 6 | goNext: () => void; 7 | goBack: () => void; 8 | lessonNumber: number; 9 | total: number; 10 | } 11 | 12 | const LessonNavigator: React.SFC = (props) => { 13 | const { goNext, goBack, lessonNumber, total } = props; 14 | 15 | const isBackButtonDisabled = lessonNumber <= 1; 16 | const isProceedButtonDisabled = lessonNumber >= total; 17 | 18 | return ( 19 |
20 |
40 | ); 41 | }; 42 | 43 | export default LessonNavigator; 44 | -------------------------------------------------------------------------------- /src/components/lesson-progressbar/__snapshots__/index.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Lesson Progressbar tests basic tests matches the snapshot 1`] = ` 4 |
7 |
10 | 11 | Chapter 1 12 | 13 |
14 |
18 |
31 |
44 |
57 |
70 |
83 |
96 |
109 |
122 |
135 |
148 |
149 |
150 | `; 151 | -------------------------------------------------------------------------------- /src/components/lesson-progressbar/index.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import * as renderer from 'react-test-renderer'; 3 | import { shallow } from 'enzyme'; 4 | import LessonProgressbar from '.'; 5 | 6 | const t = (s) => s; 7 | const navigate = jest.fn(); 8 | const chapterNumber = 1; 9 | describe('Lesson Progressbar tests', () => { 10 | const baseComponent = () => ( 11 | 17 | ); 18 | 19 | describe('basic tests', () => { 20 | it('matches the snapshot', () => { 21 | const tree = renderer.create(baseComponent()).toJSON(); 22 | expect(tree).toMatchSnapshot(); 23 | }); 24 | 25 | it('renders the component', () => { 26 | const wrapper = shallow(baseComponent()); 27 | const assertion = wrapper.find('[data-testid="lesson-progressbar"]').length; 28 | expect(assertion).toBe(1); 29 | }); 30 | }); 31 | 32 | describe('component behavior', () => { 33 | it('google login', () => { 34 | const index = 1; 35 | const wrapper = shallow(baseComponent()); 36 | wrapper.find(`[data-testid="lesson-progressbar-block${index}"]`).simulate('click'); 37 | expect(navigate).toHaveBeenCalled(); 38 | 39 | expect(navigate.mock.calls[0][0]).toBe(chapterNumber); 40 | expect(navigate.mock.calls[0][1]).toBe(index + 1); 41 | expect(navigate.mock.calls.length).toBe(1); 42 | }); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /src/components/lesson-progressbar/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import './style.css'; 4 | import classNames from 'classnames'; 5 | 6 | interface IProps { 7 | lessonNumber: number; 8 | chapterNumber: number; 9 | total: number; 10 | navigate: (chapterNum: number, lessonNum: number) => void; 11 | } 12 | 13 | // Renders step progressbar dynamically 14 | const LessonProgressbar: React.SFC = ({ navigate, chapterNumber, lessonNumber, total }) => { 15 | // initialized array with the given total number 16 | const list = Array.from({ length: total }); 17 | const percent = (1 / total) * 100; 18 | const currentChapterText = `Chapter ${chapterNumber}`; 19 | return ( 20 |
21 |
22 | {currentChapterText} 23 |
24 | 29 | {list.map((item, index) => { 30 | const isLocked = lessonNumber <= index; 31 | const color = isLocked ? 'secondary' : 'primary'; 32 | return ( 33 | navigate(chapterNumber, index + 1)} 37 | barClassName={`lesson-progress-bar cursor-pointer`} 38 | bar={true} 39 | color={color} 40 | value={percent} 41 | max={total} 42 | /> 43 | ); 44 | })} 45 | 46 |
47 | ); 48 | }; 49 | 50 | export default LessonProgressbar; 51 | 52 | interface IProgressProps { 53 | onClick?: () => void; 54 | children?: any; 55 | bar?: boolean; 56 | multi?: boolean; 57 | value?: number; 58 | max?: number; 59 | animated?: boolean; 60 | striped?: boolean; 61 | color?: string; 62 | className?: string; 63 | barClassName?: string; 64 | } 65 | 66 | const Progress = (props: IProgressProps) => { 67 | const { 68 | children, 69 | className, 70 | barClassName, 71 | value = 0, 72 | max = 100, 73 | animated, 74 | striped, 75 | color, 76 | bar, 77 | multi, 78 | onClick, 79 | ...attributes 80 | } = props; 81 | 82 | const percent = (value / max) * 100; 83 | const progressClasses = classNames(className, 'progress'); 84 | 85 | const progressBarClasses = classNames( 86 | 'progress-bar', 87 | bar ? className || barClassName : barClassName, 88 | animated ? 'progress-bar-animated' : null, 89 | color ? `bg-${color}` : null, 90 | striped || animated ? 'progress-bar-striped' : null 91 | ); 92 | 93 | const ProgressBar = multi ? ( 94 | children 95 | ) : ( 96 |
106 | ); 107 | 108 | if (bar) { 109 | return ProgressBar; 110 | } 111 | 112 | return
; 113 | }; 114 | -------------------------------------------------------------------------------- /src/components/lesson-progressbar/style.css: -------------------------------------------------------------------------------- 1 | .lesson-progress-bar-container { 2 | max-width: 720px; 3 | margin: auto; 4 | height: 5px; 5 | } 6 | 7 | .lesson-progress-bar { 8 | margin-left: 1px; 9 | margin-right: 1px; 10 | border-radius: 3px; 11 | } 12 | -------------------------------------------------------------------------------- /src/containers/chapter-list-container/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Helmet } from 'react-helmet'; 3 | import * as H from 'history'; 4 | 5 | import courseInstructions from '../../locales/instructions'; 6 | 7 | import ChapterList from '../../components/chapter-list'; 8 | import { Spinner } from '../../fn-components'; 9 | import Header from '../../components/header'; 10 | import Footer from '../../components/footer'; 11 | import './style.css'; 12 | 13 | interface IProps { 14 | history: H.History; 15 | location: H.Location; 16 | } 17 | 18 | class ChapterContainer extends React.Component { 19 | public render(): React.ReactNode { 20 | const lang: string = 'en'; 21 | if (courseInstructions === undefined || courseInstructions[lang] === undefined) { 22 | return ( 23 |
24 | 25 |
26 | ); 27 | } 28 | 29 | const intructionsLocalized = courseInstructions[lang]; 30 | const documentTitle = `LearnScilla - An interactive tutorial for people to learn Scilla`; 31 | 32 | return ( 33 |
34 |
35 | 36 | {documentTitle} 37 | 38 |
39 |
40 |
41 |
42 |

{'Scilla Tutorial'}

43 |
44 | 45 |
46 |
47 |
48 |
49 |
50 |
51 | ); 52 | } 53 | 54 | private navigate = (chapterNum, lessonNum) => { 55 | const { history } = this.props; 56 | const startingChapterPath = `/chapter/${chapterNum}/lesson/${lessonNum}`; 57 | return history.push(startingChapterPath); 58 | }; 59 | } 60 | 61 | export default ChapterContainer; 62 | -------------------------------------------------------------------------------- /src/containers/chapter-list-container/style.css: -------------------------------------------------------------------------------- 1 | .chapter-list-container { 2 | padding-top: 80px; 3 | padding-bottom: 200px; 4 | } 5 | -------------------------------------------------------------------------------- /src/containers/home-container/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Helmet } from 'react-helmet'; 3 | import Header from '../../components/header'; 4 | import Footer from '../../components/footer'; 5 | import { Button } from '../../fn-components'; 6 | import { paths } from '../../routes'; 7 | import A from '../../assets/images/asset_a.png'; 8 | import B from '../../assets/images/asset_b.png'; 9 | import C from '../../assets/images/asset_c.png'; 10 | import D from '../../assets/images/asset_d.png'; 11 | import E from '../../assets/images/asset_e.png'; 12 | 13 | const Home = (props) => { 14 | const { history } = props; 15 | const documentTitle = `LearnScilla - Home`; 16 | return ( 17 |
18 |
19 | 20 | {documentTitle} 21 | 22 | 23 |
31 |
32 |
33 |
34 |
35 |

36 | LEARN TO SAFELY CODE ON BLOCKCHAIN 37 |

38 | 39 |

Step by step Interactive Tutorial

40 |
48 |
49 |
50 |
51 | asset a 57 |
58 |
59 |
60 |
61 |
62 | 63 |
64 |
65 |
66 |
67 |

What is Blockchain?

68 |
69 |

70 | A blockchain is a specific type of database that’s not maintained by any one 71 | particular entity, where you can’t change the previously stored data and all new 72 | data is updated one block of entries or transactions at a time under public view and 73 | consensus. In a public blockchain, anybody can choose to maintain the database 74 | correctly as it grows, and are often rewarded to do so by the internal mechanisms of 75 | the established blockchain protocol. This reward is usually in the form of the 76 | digital currency whose balance is maintained in the blockchain database. 77 |

78 |

79 | The first digital currency that used blockchain technology to maintain a correct 80 | database of ownership is bitcoin. 81 |

82 |
83 |
84 | asset d 85 |
86 |
87 |
88 |
89 | 90 |
93 |
94 |
95 |
96 | asset e 97 |
98 |
99 |
100 |

What is a Smart Contract

101 |
102 |

103 | After the advent of the digital currency ‘bitcoin’, the next step was to have a 104 | programmable asset which could be transferred based on predetermined conditions, 105 | rather than human intervention. 106 |

107 |

108 | The set of those predetermined conditions and the way to interact with those set of 109 | conditions come together in the form of smart contract. 110 |

111 |

112 | More succinctly, in the words of Nick Szabo - who coined the term smart contract - a 113 | smart contract is a set of promises, specified in digital form, including protocols 114 | within which the parties perform on these promises. 115 |

116 |

117 | Smart contracts can help mitigate the counterparty risk i.e. helps us transact with 118 | each other without needing to rely on mutual trust or any arbitration parties such 119 | as courts if that trust is perceived to be breached. 120 |

121 |
122 |
123 |
124 |
125 | 126 |
127 |
128 |
129 |
130 |

What is Scilla

131 |
132 |

Scilla is a language for writing smart contracts that are safe by design.

133 |

134 | Since smart contracts have to deal with digital assets, they are often targeted by 135 | hackers for any flaws in the programming which might allow the hackers to exploit 136 | them. 137 |

138 |

139 | Scilla is based on functional programming languages such as OCaml which makes it 140 | more friendly towards static checks and formal verification which can help 141 | programmers make their smart contracts much more secure. 142 |

143 |
144 | 145 |
146 | asset b 147 |
148 |
149 |
150 |
151 | 152 |
155 |
156 |
157 |
158 | asset c 159 |
160 |
161 |
162 |

So, are you ready to learn how to code a smart contract in Scilla?

163 |
164 |

165 | We’ll be there with you helping you through a friendly interactive tutorial and a 166 | link to our chat where you can always discuss your doubts. 167 |
168 | Additionally, building with Scilla language can also help you be eligible for a 169 | grant from Scilla. 170 |
171 |
172 | Learn more about it here. 173 |
174 |
175 | Take the first step to become a programmer on blockchain. 176 |

177 |
178 |
186 |
187 |
188 |
189 |
190 | 191 |
192 |
193 |
194 | ); 195 | }; 196 | 197 | export default Home; 198 | -------------------------------------------------------------------------------- /src/containers/lesson-container/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { Helmet } from 'react-helmet'; 4 | import Header from '../../components/header'; 5 | import Footer from '../../components/footer'; 6 | import * as H from 'history'; 7 | import ChapterCompleteCard from '../../components/chapter-complete-card'; 8 | import LessonProgressbar from '../../components/lesson-progressbar'; 9 | import Editor from '../../components/editor'; 10 | import InstructionViewer from '../../components/instruction-viewer'; 11 | import LessonNavigator from '../../components/lesson-navigator'; 12 | import { IMatch, CourseInstructionType } from '../../typings'; 13 | import CheatSheetModal from '../../components/cheat-sheet-modal'; 14 | import courseInstructions from '../../locales/instructions'; 15 | import courseCodes from '../../course-codes'; 16 | 17 | interface IProps { 18 | history: H.History; 19 | location: H.Location; 20 | match: IMatch; 21 | courseInstructions: CourseInstructionType; 22 | } 23 | interface IState { 24 | code: string; 25 | codeForDiff: string; 26 | showAnswer: boolean; 27 | } 28 | 29 | class LessonContainer extends React.Component { 30 | public render(): React.ReactNode { 31 | const { location } = this.props; 32 | 33 | const chapterNumber: number = this.getChapterNumber(); 34 | const chapterIndex: number = chapterNumber - 1; 35 | const lessonNumber: number = this.getChatperNumber(); 36 | const lessonIndex: number = lessonNumber - 1; 37 | const instruction = this.getInstruction(); 38 | 39 | const codeLessonList = courseCodes[chapterIndex] || []; 40 | 41 | const numOfTotalChapter: number = courseCodes.length; 42 | 43 | const numOfTotalLesson: number = codeLessonList.length || 0; 44 | 45 | // Check if the current lesson is the end of this chapter. 46 | const isLastLesson: boolean = lessonNumber === numOfTotalLesson; 47 | 48 | const code = codeLessonList[lessonIndex] || { initialCode: undefined, answerCode: undefined }; 49 | const { initialCode, answerCode } = code; 50 | 51 | const currentChapterText = `Chapter ${chapterNumber}`; 52 | const currentLessonText = `Lesson ${lessonNumber}`; 53 | const documentTitle: string = `LearnScilla - ${currentChapterText} ${currentLessonText}`; 54 | 55 | const { pathname } = location; 56 | return ( 57 |
58 |
59 | 60 | {documentTitle} 61 | 62 |
63 | 69 |
70 | 71 |
72 |
73 | 74 |
75 |
76 | {isLastLesson ? ( 77 | 82 | ) : ( 83 | 89 | )} 90 |
91 |
92 |
93 |
94 | 95 | 101 |
102 |
103 |
104 |
105 |
106 |
107 | ); 108 | } 109 | 110 | public goNext = (): void => { 111 | const chapterNumber: number = this.getChapterNumber(); 112 | const lessonNumber: number = this.getChatperNumber(); 113 | 114 | this.updateProgress(chapterNumber, lessonNumber); 115 | this.navigateToNextLesson(chapterNumber, lessonNumber); 116 | }; 117 | 118 | private goBack = (): void => { 119 | const { history } = this.props; 120 | const chapterNumber: number = this.getChapterNumber(); 121 | const lessonNumber: number = this.getChatperNumber(); 122 | const previousLessonPath = `/chapter/${chapterNumber}/lesson/${lessonNumber - 1}`; 123 | history.push(previousLessonPath); 124 | }; 125 | 126 | private updateProgress = (currentChapter: number, currentLesson: number) => { 127 | localStorage.setItem(`chapter${currentChapter}`, currentLesson.toString()); 128 | }; 129 | 130 | private navigateToNextLesson = (currentChapter: number, currentLesson: number) => { 131 | this.navigate(currentChapter, currentLesson + 1); 132 | }; 133 | 134 | private navigate = (chapterNum, lessonNum) => { 135 | const { history } = this.props; 136 | history.push(`/chapter/${chapterNum}/lesson/${lessonNum}`); 137 | }; 138 | 139 | private getInstruction = () => { 140 | const chapterNumber = this.getChapterNumber(); 141 | const chapterIndex = chapterNumber - 1; 142 | const lessonNumber = this.getChatperNumber(); 143 | const lessonIndex = lessonNumber - 1; 144 | 145 | const intructionsLocalized = courseInstructions['en']; 146 | const chapter = intructionsLocalized[chapterIndex] || {}; 147 | 148 | const instructionLessonList = chapter.lessons || []; 149 | const instruction = instructionLessonList[lessonIndex] || {}; 150 | 151 | return instruction; 152 | }; 153 | 154 | private getChapterNumber = (): number => { 155 | const { match } = this.props; 156 | const routeParams = match.params; 157 | return parseInt(routeParams.chapter, 10); 158 | }; 159 | 160 | private getChatperNumber = (): number => { 161 | const { match } = this.props; 162 | const routeParams = match.params; 163 | return parseInt(routeParams.lesson, 10); 164 | }; 165 | } 166 | 167 | export default LessonContainer; 168 | -------------------------------------------------------------------------------- /src/course-codes/chapter1/index.ts: -------------------------------------------------------------------------------- 1 | import { ILessonCode, ChapterCodeType } from '../../typings'; 2 | 3 | const l1: ILessonCode = { 4 | initialCode: `(* Start typing from the line below. *)`, 5 | answerCode: `(* Start typing from the line below. *) 6 | scilla_version 0 7 | 8 | contract SocialMediaPayment` 9 | }; 10 | 11 | const l2: ILessonCode = { 12 | initialCode: `scilla_version 0 13 | 14 | (* Insert the immutable variable declaration in parantheses after the contract name below *) 15 | contract SocialMediaPayment 16 | 17 | `, 18 | answerCode: `scilla_version 0 19 | (* Insert the immutable variable declaration in parantheses after the contract name below *) 20 | contract SocialMediaPayment (owner: ByStr20)` 21 | }; 22 | 23 | const l3: ILessonCode = { 24 | initialCode: `scilla_version 0 25 | 26 | contract SocialMediaPayment (owner: ByStr20) 27 | (* Start typing from the line below. *) 28 | `, 29 | answerCode: `scilla_version 0 30 | 31 | contract SocialMediaPayment (owner: ByStr20) 32 | (* Start typing from the line below. *) 33 | field username : String = "Alice"` 34 | }; 35 | 36 | const l4: ILessonCode = { 37 | initialCode: `scilla_version 0 38 | 39 | contract SocialMediaPayment (owner: ByStr20) 40 | field username : String = "Alice" 41 | 42 | (* Start typing from the line below *) 43 | `, 44 | answerCode: `scilla_version 0 45 | 46 | contract SocialMediaPayment (owner: ByStr20) 47 | field username : String = "Alice" 48 | 49 | (* Start typing from the line below *) 50 | transition changeName() 51 | end` 52 | }; 53 | 54 | const l5: ILessonCode = { 55 | initialCode: `scilla_version 0 56 | 57 | contract SocialMediaPayment (owner: ByStr20) 58 | field username : String = "Alice" 59 | 60 | transition changeName() 61 | (* Start typing from the line below *) 62 | end`, 63 | answerCode: `scilla_version 0 64 | 65 | contract SocialMediaPayment (owner: ByStr20) 66 | field username : String = "Alice" 67 | 68 | transition changeName() 69 | (* Start typing from the line below *) 70 | newname = "Bob" 71 | end` 72 | }; 73 | 74 | const l6: ILessonCode = { 75 | initialCode: `scilla_version 0 76 | 77 | contract SocialMediaPayment (owner: ByStr20) 78 | field username : String = "Alice" 79 | 80 | transition changeName() 81 | newname = "Bob" (*Now that we’ll be having another line, don’t forget to include a semicolon at the end of of the previous line, i.e. after "Bob" *) 82 | (* Start typing from the line below *) 83 | end`, 84 | answerCode: `scilla_version 0 85 | 86 | contract SocialMediaPayment (owner: ByStr20) 87 | field username : String = "Alice" 88 | 89 | transition changeName() 90 | newname = "Bob"; (*Now that we’ll be having another line, don’t forget to include a semicolon at the end of the previous line, i.e. after "Bob" *) 91 | (* Start typing from the line below *) 92 | username := newname 93 | end` 94 | }; 95 | 96 | const l7: ILessonCode = { 97 | initialCode: `scilla_version 0 98 | 99 | contract SocialMediaPayment (owner: ByStr20) 100 | field username : String = "Alice" 101 | 102 | 103 | (* Start typing in the parentheses below *) 104 | transition changeName() 105 | username := newname 106 | end 107 | `, 108 | answerCode: `scilla_version 0 109 | 110 | contract SocialMediaPayment (owner: ByStr20) 111 | field username : String = "Alice" 112 | 113 | (* Start typing in the parentheses below *) 114 | transition changeName(newname: String) 115 | username := newname 116 | end` 117 | }; 118 | 119 | const l8: ILessonCode = { 120 | initialCode: `scilla_version 0 121 | 122 | contract SocialMediaPayment (owner: ByStr20) 123 | field username : String = "Alice" 124 | (*Declare the two new mutable variables below. You don’t need to use any semicolons to separate the lines outside the transitions*) 125 | 126 | transition changeName(newname: String) 127 | username := newname 128 | (*Use ‘accept’ command in the line below which will accept the amount sent to this transition.*) 129 | 130 | (*Assign the value of the implicit variables to the new mutable variables in the lines below. You’ll need to use the semicolons to separate the lines in the transition*) 131 | 132 | end 133 | `, 134 | answerCode: `scilla_version 0 135 | 136 | contract SocialMediaPayment (owner: ByStr20) 137 | field username : String = "Alice" 138 | field user_address : ByStr20 = 0x1234567890123456789012345678901234567890 139 | field user_tokens: Uint128 = Uint128 0 140 | 141 | transition changeName(newname: String) 142 | username := newname; 143 | (*Use ‘accept’ command in the line below which will accept the amount sent to this transition.*) 144 | accept; 145 | (*Assign the value of the implicit variables to the new mutable variables in the lines below. You’ll need to use the semicolons to separate the lines in the transition*) 146 | user_address := _sender; 147 | user_tokens := _amount 148 | 149 | end` 150 | }; 151 | 152 | const l9: ILessonCode = { 153 | initialCode: `contract Zealgame 154 | (owner: ByStr20) 155 | field player_name : String = "Alice" 156 | field player_address : ByStr20 = 0x1234567890123456789012345678901234567890 157 | field player_zeal: Uint128 = Uint128 0 158 | 159 | Transition changeName(newname: String) 160 | player_name := newname; 161 | player_address := _sender; 162 | player_zeal := _amount 163 | end 164 | `, 165 | answerCode: `contract Zealgame 166 | (owner: ByStr20) 167 | field player_name : String = "Alice" 168 | field player_address : ByStr20 = 0x1234567890123456789012345678901234567890 169 | field player_zeal: Uint128 = Uint128 0 170 | 171 | Transition changeName(newname: String) 172 | player_name := newname; 173 | player_address := _sender; 174 | player_zeal := _amount 175 | end` 176 | }; 177 | 178 | const chapter1: ChapterCodeType = [l1, l2, l3, l4, l5, l6, l7, l8, l9]; 179 | 180 | export default chapter1; 181 | -------------------------------------------------------------------------------- /src/course-codes/chapter2/index.ts: -------------------------------------------------------------------------------- 1 | import { ILessonCode, ChapterCodeType } from '../../typings'; 2 | 3 | const l1: ILessonCode = { 4 | initialCode: `scilla_version 0 5 | 6 | contract SocialMediaPayment (owner: ByStr20) 7 | (*Start typing from the line below*) 8 | `, 9 | answerCode: `scilla_version 0 10 | 11 | contract SocialMediaPayment (owner: ByStr20) 12 | (*Start typing from the line below*) 13 | 14 | transition deposit() 15 | 16 | end 17 | ` 18 | }; 19 | 20 | const l2: ILessonCode = { 21 | initialCode: `scilla_version 0 22 | 23 | (*Start typing from the line below*) 24 | 25 | 26 | contract SocialMediaPayment(owner: ByStr20) 27 | 28 | transition deposit() 29 | 30 | end 31 | `, 32 | answerCode: `scilla_version 0 33 | 34 | (*Start typing from the line below*) 35 | import BoolUtils 36 | 37 | contract SocialMediaPayment(owner: ByStr20) 38 | 39 | transition deposit() 40 | 41 | end` 42 | }; 43 | 44 | const l3: ILessonCode = { 45 | initialCode: `scilla_version 0 46 | import BoolUtils 47 | 48 | contract SocialMediaPayment(owner: ByStr20) 49 | 50 | transition deposit() 51 | (*Start typing from the line below. The answer will be a single line within the transition so you won’t need to use semicolon at the end*) 52 | 53 | end 54 | `, 55 | answerCode: `scilla_version 0 56 | import BoolUtils 57 | 58 | contract SocialMediaPayment(owner: ByStr20) 59 | 60 | transition deposit() 61 | (*Start typing from the line below. The answer will be a single line within the transition so you won’t need to use semicolon at the end*) 62 | 63 | sender_is_owner = builtin eq _sender owner 64 | 65 | 66 | end` 67 | }; 68 | 69 | const l4: ILessonCode = { 70 | initialCode: `scilla_version 0 71 | import BoolUtils 72 | 73 | contract SocialMediaPayment(owner: ByStr20) 74 | 75 | transition deposit() 76 | sender_is_owner = builtin eq _sender owner 77 | (*Start typing from the line below. *) 78 | 79 | end 80 | `, 81 | answerCode: `scilla_version 0 82 | import BoolUtils 83 | 84 | contract SocialMediaPayment(owner: ByStr20) 85 | 86 | transition deposit() 87 | sender_is_owner = builtin eq _sender owner; 88 | (*Start typing from the line below. *) 89 | match sender_is_owner with 90 | | False => 91 | 92 | | True => 93 | 94 | end 95 | end` 96 | }; 97 | 98 | const l5: ILessonCode = { 99 | initialCode: `scilla_version 0 100 | import BoolUtils 101 | (*Start typing from the line below. First define the library*) 102 | 103 | (*Now define the variable zero for Uint128 0*) 104 | 105 | 106 | (*Now, in the line below, define the variable not_owner_code of type Uint32 and value 1*) 107 | 108 | 109 | contract SocialMediaPayment(owner: ByStr20) 110 | 111 | transition deposit() 112 | sender_is_owner = builtin eq _sender owner; 113 | match sender_is_owner with 114 | | False => 115 | 116 | | True => 117 | 118 | end 119 | end 120 | `, 121 | answerCode: `scilla_version 0 122 | import BoolUtils 123 | (*Start typing from the line below. First define the library*) 124 | library SocialMediaPayment 125 | 126 | (*Now define the variable zero for Uint128 0*) 127 | let zero = Uint128 0 128 | 129 | 130 | (*Now, in the line below, define the variable not_owner_code of type Uint32 and value 1*) 131 | let not_owner_code = Uint32 1 132 | 133 | contract SocialMediaPayment(owner: ByStr20) 134 | 135 | transition deposit() 136 | sender_is_owner = builtin eq _sender owner; 137 | match sender_is_owner with 138 | | False => 139 | 140 | | True => 141 | 142 | end 143 | end` 144 | }; 145 | 146 | const l6: ILessonCode = { 147 | initialCode: `scilla_version 0 148 | import BoolUtils 149 | 150 | library SocialMediaPayment 151 | (*Start typing from the line below. Copy paste the code given in the first task below to define the variable one_msg*) 152 | 153 | 154 | let zero = Uint128 0 155 | let not_owner_code = Uint32 1 156 | 157 | contract SocialMediaPayment(owner: ByStr20) 158 | 159 | transition deposit() 160 | sender_is_owner = builtin eq _sender owner; 161 | match sender_is_owner with 162 | | False => 163 | (*Start typing from the line below. Define the variable msg below with the values given in the second point of the task*) 164 | 165 | 166 | (*Start typing from the line below. Copy the two lines given in the third point of the task below. *) 167 | 168 | 169 | | True => 170 | 171 | end 172 | end 173 | `, 174 | answerCode: `scilla_version 0 175 | import BoolUtils 176 | 177 | library SocialMediaPayment 178 | (*Start typing from the line below. Copy paste the code given in the first task below to define the variable one_msg*) 179 | let one_msg = 180 | fun (msg : Message) => 181 | let nil_msg = Nil {Message} in 182 | Cons {Message} msg nil_msg 183 | 184 | let zero = Uint128 0 185 | let not_owner_code = Uint32 1 186 | 187 | contract SocialMediaPayment(owner: ByStr20) 188 | 189 | transition deposit() 190 | sender_is_owner = builtin eq _sender owner; 191 | match sender_is_owner with 192 | | False => 193 | (*Start typing from the line below. Define the variable msg below with the values given in the second point of the task*) 194 | 195 | msg = {_tag: ""; 196 | _recipient: _sender; 197 | _amount: zero; 198 | code: not_owner_code}; 199 | (*Start typing from the line below. Copy the two lines given in the third point of the task below. *) 200 | msgs = one_msg msg; 201 | send msgs 202 | 203 | | True => 204 | 205 | end 206 | end 207 | ` 208 | }; 209 | 210 | const l7: ILessonCode = { 211 | initialCode: `scilla_version 0 212 | import BoolUtils 213 | 214 | library SocialMediaPayment 215 | 216 | let one_msg = 217 | fun (msg : Message) => 218 | let nil_msg = Nil {Message} in 219 | Cons {Message} msg nil_msg 220 | 221 | let zero = Uint128 0 222 | let not_owner_code = Uint32 1 223 | (*Start typing from the line below. declare a Uint32 type of variable with variable name “accepted_code” and value “0” *) 224 | 225 | 226 | 227 | contract SocialMediaPayment(owner: ByStr20) 228 | 229 | transition deposit() 230 | sender_is_owner = builtin eq _sender owner; 231 | match sender_is_owner with 232 | | False => 233 | 234 | msg = {_tag: ""; 235 | _recipient: _sender; 236 | _amount: zero; 237 | code: not_owner_code}; 238 | msgs = one_msg msg; 239 | send msgs 240 | 241 | | True => 242 | (*Start typing from the line below for task 2, 3 and 4*) 243 | 244 | 245 | end 246 | end 247 | `, 248 | answerCode: `scilla_version 0 249 | import BoolUtils 250 | 251 | library SocialMediaPayment 252 | 253 | let one_msg = 254 | fun (msg : Message) => 255 | let nil_msg = Nil {Message} in 256 | Cons {Message} msg nil_msg 257 | 258 | let zero = Uint128 0 259 | let not_owner_code = Uint32 1 260 | (*Start typing from the line below. declare a Uint32 type of variable with variable name “accepted_code” and value “0” *) 261 | let accepted_code = Uint32 0 262 | 263 | 264 | contract SocialMediaPayment(owner: ByStr20) 265 | 266 | transition deposit() 267 | sender_is_owner = builtin eq _sender owner; 268 | match sender_is_owner with 269 | | False => 270 | 271 | msg = {_tag: ""; 272 | _recipient: _sender; 273 | _amount: zero; 274 | code: not_owner_code}; 275 | msgs = one_msg msg; 276 | send msgs 277 | 278 | | True => 279 | (*Start typing from the line below for task 2, 3 and 4*) 280 | accept; 281 | msg = {_tag: ""; 282 | _recipient: _sender; 283 | _amount: zero; 284 | code: accepted_code}; 285 | msgs = one_msg msg; 286 | send msgs 287 | 288 | end 289 | end 290 | ` 291 | }; 292 | 293 | const l8: ILessonCode = { 294 | initialCode: `scilla_version 0 295 | import BoolUtils 296 | 297 | library SocialMediaPayment 298 | 299 | let one_msg = 300 | fun (msg : Message) => 301 | let nil_msg = Nil {Message} in 302 | Cons {Message} msg nil_msg 303 | 304 | let zero = Uint128 0 305 | let not_owner_code = Uint32 1 306 | (*Start typing from the line below. declare a Uint32 type of variable with variable name “accepted_code” and value “0” *) 307 | 308 | 309 | 310 | contract SocialMediaPayment(owner: ByStr20) 311 | 312 | transition deposit() 313 | sender_is_owner = builtin eq _sender owner; 314 | match sender_is_owner with 315 | | False => 316 | 317 | msg = {_tag: ""; 318 | _recipient: _sender; 319 | _amount: zero; 320 | code: not_owner_code}; 321 | msgs = one_msg msg; 322 | send msgs 323 | 324 | | True => 325 | (*Start typing from the line below for task 2, 3 and 4*) 326 | 327 | 328 | end 329 | end 330 | `, 331 | answerCode: `scilla_version 0 332 | import BoolUtils 333 | 334 | library SocialMediaPayment 335 | 336 | let one_msg = 337 | fun (msg : Message) => 338 | let nil_msg = Nil {Message} in 339 | Cons {Message} msg nil_msg 340 | 341 | let zero = Uint128 0 342 | let not_owner_code = Uint32 1 343 | (*Start typing from the line below. declare a Uint32 type of variable with variable name “accepted_code” and value “0” *) 344 | let accepted_code = Uint32 0 345 | 346 | 347 | contract SocialMediaPayment(owner: ByStr20) 348 | 349 | transition deposit() 350 | sender_is_owner = builtin eq _sender owner; 351 | match sender_is_owner with 352 | | False => 353 | 354 | msg = {_tag: ""; 355 | _recipient: _sender; 356 | _amount: zero; 357 | code: not_owner_code}; 358 | msgs = one_msg msg; 359 | send msgs 360 | 361 | | True => 362 | (*Start typing from the line below for task 2, 3 and 4*) 363 | accept; 364 | msg = {_tag: ""; 365 | _recipient: _sender; 366 | _amount: zero; 367 | code: accepted_code}; 368 | msgs = one_msg msg; 369 | send msgs 370 | 371 | end 372 | end 373 | ` 374 | }; 375 | 376 | const chapter2: ChapterCodeType = [l1, l2, l3, l4, l5, l6, l7, l8]; 377 | 378 | export default chapter2; 379 | -------------------------------------------------------------------------------- /src/course-codes/index.ts: -------------------------------------------------------------------------------- 1 | import { CourseCodeType } from '../typings'; 2 | import chapter1 from './chapter1'; 3 | import chapter2 from './chapter2'; 4 | import chapter3 from './chapter3'; 5 | 6 | const course: CourseCodeType = [chapter1, chapter2, chapter3]; 7 | 8 | export default course; 9 | -------------------------------------------------------------------------------- /src/fn-components/button/index.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render, cleanup } from '@testing-library/react'; 3 | import Button from '.'; 4 | import 'jest-styled-components'; 5 | 6 | // automatically unmount and cleanup DOM after the test is finished. 7 | afterEach(cleanup); 8 | 9 | const themeArray = ['light', 'dark']; 10 | const levelArray = ['primary', 'secondary', 'tertiary']; 11 | const sizeArray = ['small', 'medium', 'large']; 12 | const disabledArray = [true, false]; 13 | const snapshot = (theme) => (level) => (size) => (disabled) => { 14 | test('matches the snapshot', () => { 15 | const onClick = jest.fn(); 16 | const { container } = render( 17 |