├── client ├── .env ├── public │ ├── favicon.ico │ ├── manifest.json │ └── index.html ├── scss │ ├── _content.scss │ ├── _main.scss │ ├── _navbar.scss │ ├── _deployment.scss │ ├── _splash.scss │ ├── index.scss │ ├── _sidebar.scss │ └── _code.scss ├── src │ ├── index.js │ ├── utilities │ │ ├── web3Config.js │ │ ├── localStorageHelper.js │ │ ├── codeRunnerUtil.js │ │ ├── generalUtil.js │ │ ├── transactionPoll.js │ │ ├── PollWeb3.js │ │ └── solidityMode.js │ ├── components │ │ ├── navbar.jsx │ │ ├── tutorial.jsx │ │ ├── stage_button.jsx │ │ ├── App.js │ │ ├── content.jsx │ │ ├── splash.jsx │ │ ├── main.jsx │ │ ├── code_display.js │ │ ├── code_tabs.jsx │ │ ├── deployment.jsx │ │ ├── code_buttons.jsx │ │ ├── completion.js │ │ ├── code_editor.js │ │ ├── sidebar.jsx │ │ ├── metamask.jsx │ │ └── code.jsx │ ├── actions │ │ ├── module_actions.js │ │ └── ui_actions.js │ ├── store.js │ ├── modules │ │ └── spaceman │ │ │ ├── deploy.js │ │ │ └── config.json │ ├── reducers │ │ ├── ui_reducer.js │ │ └── modules_reducer.js │ ├── registerServiceWorker.js │ └── scss_dist │ │ └── index.css ├── gulpfile.js └── package.json ├── .gitignore ├── modules └── spaceman │ ├── reactapp │ ├── .env │ ├── README.md │ ├── public │ │ ├── favicon.ico │ │ ├── manifest.json │ │ └── index.html │ ├── src │ │ ├── images │ │ │ └── outerspace.jpg │ │ ├── web3Api │ │ │ ├── web3Config.js │ │ │ ├── deploy.js │ │ │ ├── astronautApi.js │ │ │ └── Migrations.json │ │ ├── App.test.js │ │ ├── index.js │ │ ├── App.css │ │ ├── utilities │ │ │ └── transactionPoll.js │ │ ├── App.js │ │ ├── component │ │ │ ├── shipped.jsx │ │ │ ├── approve.jsx │ │ │ ├── withdraw.jsx │ │ │ ├── userInfo.jsx │ │ │ ├── fund.jsx │ │ │ └── deploy.jsx │ │ ├── logo.svg │ │ ├── styles │ │ │ ├── reset.css │ │ │ └── index.css │ │ └── registerServiceWorker.js │ └── package.json │ └── truffle │ ├── migrations │ ├── 2_Astronaut.sol │ └── 1_initial_migration.js │ ├── truffle-config.js │ └── contracts │ ├── Migrations.sol │ └── astronaut.sol ├── server ├── package.json ├── index.js └── package-lock.json └── README.md /client/.env: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /modules/spaceman/reactapp/.env: -------------------------------------------------------------------------------- 1 | PORT=3030 2 | -------------------------------------------------------------------------------- /modules/spaceman/reactapp/README.md: -------------------------------------------------------------------------------- 1 | 1) Run Ganache-Cli to populate 2 | -------------------------------------------------------------------------------- /client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockstreetboys/blockstreet/HEAD/client/public/favicon.ico -------------------------------------------------------------------------------- /modules/spaceman/reactapp/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockstreetboys/blockstreet/HEAD/modules/spaceman/reactapp/public/favicon.ico -------------------------------------------------------------------------------- /client/scss/_content.scss: -------------------------------------------------------------------------------- 1 | .dev-content { 2 | background-color: $accent-background-color; 3 | a { 4 | text-decoration: underline; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /modules/spaceman/reactapp/src/images/outerspace.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockstreetboys/blockstreet/HEAD/modules/spaceman/reactapp/src/images/outerspace.jpg -------------------------------------------------------------------------------- /modules/spaceman/truffle/migrations/2_Astronaut.sol: -------------------------------------------------------------------------------- 1 | const Astronaut = artifacts.require('Astronaut'); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Adoption); 5 | }; 6 | -------------------------------------------------------------------------------- /modules/spaceman/truffle/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | var Migrations = artifacts.require("./Migrations.sol"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /modules/spaceman/truffle/truffle-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // See 3 | // to customize your Truffle configuration! 4 | contracts_build_directory: '../reactapp/src/web3Api' 5 | }; 6 | -------------------------------------------------------------------------------- /client/scss/_main.scss: -------------------------------------------------------------------------------- 1 | 2 | .dev-main { 3 | z-index: 10; 4 | display: flex; 5 | width: 100%; 6 | position: relative; 7 | // width: calc(100% - 200px); 8 | } 9 | 10 | .dev-content, .dev-code { 11 | margin: 40px; 12 | // width: 50%; 13 | } 14 | -------------------------------------------------------------------------------- /client/scss/_navbar.scss: -------------------------------------------------------------------------------- 1 | $navbar-height: 45px; 2 | 3 | .navbar { 4 | height: $navbar-height; 5 | padding: 18px; 6 | border-bottom: 1px solid $main-line-color; 7 | 8 | h1 { 9 | font-family: 'Bungee Shade', cursive; 10 | font-size: 40px; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /client/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './components/App'; 4 | import registerServiceWorker from './registerServiceWorker'; 5 | 6 | ReactDOM.render(, document.getElementById('root')); 7 | registerServiceWorker(); 8 | -------------------------------------------------------------------------------- /client/src/utilities/web3Config.js: -------------------------------------------------------------------------------- 1 | const Web3 = require('web3'); 2 | 3 | const provider = (typeof window.web3 !== 'undefined') ? 4 | window.web3.currentProvider : 5 | new Web3.providers.HttpProvider("http://localhost:8545"); 6 | 7 | const web3 = new Web3(provider); 8 | 9 | export default web3; 10 | -------------------------------------------------------------------------------- /client/src/components/navbar.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | class NavBar extends Component { 4 | 5 | render() { 6 | return ( 7 | 10 | ); 11 | } 12 | } 13 | 14 | export default NavBar; 15 | -------------------------------------------------------------------------------- /modules/spaceman/reactapp/src/web3Api/web3Config.js: -------------------------------------------------------------------------------- 1 | const Web3 = require('web3'); 2 | 3 | const provider = (typeof window.web3 !== 'undefined') ? 4 | window.web3.currentProvider : 5 | new Web3.providers.HttpProvider("http://localhost:8545"); 6 | 7 | const web3 = new Web3(provider); 8 | 9 | export default web3; 10 | -------------------------------------------------------------------------------- /modules/spaceman/reactapp/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | ReactDOM.unmountComponentAtNode(div); 9 | }); 10 | -------------------------------------------------------------------------------- /modules/spaceman/reactapp/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import'./styles/reset.css'; 4 | import './styles/index.css'; 5 | import App from './App'; 6 | import registerServiceWorker from './registerServiceWorker'; 7 | 8 | ReactDOM.render(, document.getElementById('root')); 9 | registerServiceWorker(); 10 | -------------------------------------------------------------------------------- /client/src/components/tutorial.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Sidebar from './sidebar'; 3 | import Main from './main'; 4 | 5 | class Tutorial extends Component { 6 | render () { 7 | return ( 8 |
9 | 10 |
11 |
12 | ); 13 | } 14 | } 15 | 16 | export default Tutorial; 17 | -------------------------------------------------------------------------------- /client/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": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /client/src/actions/module_actions.js: -------------------------------------------------------------------------------- 1 | export const UPDATE_CODE = "UPDATE_CODE"; 2 | export const SHOW_SOLUTION = 'SHOW_SOLUTION'; 3 | 4 | export const updateCode = (code, activeStage) => { 5 | return ({ 6 | type: UPDATE_CODE, 7 | code, 8 | activeStage 9 | }); 10 | }; 11 | 12 | export const showSolution = (activeStage) => { 13 | return({ 14 | type: SHOW_SOLUTION, 15 | activeStage 16 | }); 17 | }; 18 | -------------------------------------------------------------------------------- /modules/spaceman/reactapp/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": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node index.js", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "archiver": "^2.1.1", 14 | "body-parser": "^1.18.2", 15 | "express": "^4.16.2", 16 | "fs-extra": "^5.0.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /client/scss/_deployment.scss: -------------------------------------------------------------------------------- 1 | .metamask-outer-container { 2 | text-align: center; 3 | .instructions { 4 | font-size: 1.5em; 5 | } 6 | } 7 | 8 | .deployment { 9 | display: flex; 10 | flex-direction: column; 11 | align-items: center; 12 | justify-content: center; 13 | } 14 | 15 | .completion { 16 | display: flex; 17 | flex-direction: column; 18 | align-items: center; 19 | justify-content: center; 20 | .dev-button { 21 | margin: 10px; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /modules/spaceman/reactapp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactapp", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "ganache-cli": "^6.0.3", 7 | "react": "^16.2.0", 8 | "react-dom": "^16.2.0", 9 | "react-scripts": "1.1.1", 10 | "web3": "0.20.0" 11 | }, 12 | "scripts": { 13 | "start": "react-scripts start", 14 | "build": "react-scripts build", 15 | "test": "react-scripts test --env=jsdom", 16 | "eject": "react-scripts eject" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /client/src/utilities/localStorageHelper.js: -------------------------------------------------------------------------------- 1 | export const loadState = () => { 2 | try { 3 | const serializedState = localStorage.getItem('state'); 4 | if(serializedState === null) { 5 | return undefined; 6 | } 7 | return JSON.parse(serializedState); 8 | } catch(err) { 9 | return undefined; 10 | } 11 | } 12 | 13 | export const saveState = (state) => { 14 | try { 15 | const serializedState = JSON.stringify(state); 16 | localStorage.setItem('state', serializedState); 17 | } catch(err) { 18 | // ignore 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /modules/spaceman/reactapp/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | } 16 | 17 | .App-title { 18 | font-size: 1.5em; 19 | } 20 | 21 | .App-intro { 22 | font-size: large; 23 | } 24 | 25 | @keyframes App-logo-spin { 26 | from { transform: rotate(0deg); } 27 | to { transform: rotate(360deg); } 28 | } 29 | -------------------------------------------------------------------------------- /client/src/utilities/codeRunnerUtil.js: -------------------------------------------------------------------------------- 1 | const crUrl = 'https://codecast.qualified.io/relay'; 2 | 3 | export const runCode = ({ preloaded, code, testCases, language, languageVersion, testFramework }, ref) => { 4 | ref.contentWindow.postMessage({ 5 | method: 'run', 6 | data: { 7 | language, 8 | languageVersion, 9 | testFramework, 10 | fixture: testCases, 11 | code, 12 | privilegeMode: "full", 13 | successMode: "specs", 14 | services: [], 15 | setup: preloaded || "", 16 | ably: true, 17 | batch: true 18 | } 19 | }, crUrl); 20 | }; 21 | -------------------------------------------------------------------------------- /modules/spaceman/truffle/contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.17; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | modifier restricted() { 8 | if (msg.sender == owner) _; 9 | } 10 | 11 | function Migrations() public { 12 | owner = msg.sender; 13 | } 14 | 15 | function setCompleted(uint completed) public restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) public restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /client/gulpfile.js: -------------------------------------------------------------------------------- 1 | const sass = require("gulp-sass"); 2 | const gulp = require("gulp"); 3 | const run = require('gulp-run'); 4 | const multiProcess = require('gulp-multi-process'); 5 | 6 | gulp.task("scss", function () { 7 | return gulp.src("./scss/*.scss") 8 | .pipe(sass().on('error', sass.logError)) 9 | .pipe(gulp.dest("./src/scss_dist")) 10 | }); 11 | 12 | gulp.task('react', function() { 13 | return run('npm start').exec(); 14 | }) 15 | 16 | gulp.task('watch', function() { 17 | return gulp.watch("./scss/*.scss", ['scss']); 18 | }) 19 | 20 | gulp.task('default', ['scss'], function (cb) { 21 | return multiProcess(['react', 'watch'], cb); 22 | }); 23 | -------------------------------------------------------------------------------- /client/src/actions/ui_actions.js: -------------------------------------------------------------------------------- 1 | export const SHOW_DEV_SIDEBAR = "SHOW_DEV_SIDEBAR"; 2 | export const HIDE_DEV_SIDEBAR = "HIDE_DEV_SIDEBAR"; 3 | export const SWITCH_TAB = "SWITCH_TAB"; 4 | export const CHANGE_STAGE = "CHANGE_STAGE"; 5 | 6 | export const showDevSidebar = () => { 7 | return { 8 | type: SHOW_DEV_SIDEBAR 9 | }; 10 | }; 11 | 12 | export const hideDevSidebar = () => { 13 | return { 14 | type: HIDE_DEV_SIDEBAR 15 | }; 16 | }; 17 | 18 | export const changeStage = (stageNumber) => { 19 | return { 20 | type: CHANGE_STAGE, 21 | stageNumber 22 | }; 23 | }; 24 | 25 | export const switchTab = (tabName) => { 26 | return ({ 27 | type: SWITCH_TAB, 28 | tabName 29 | }); 30 | }; 31 | -------------------------------------------------------------------------------- /client/scss/_splash.scss: -------------------------------------------------------------------------------- 1 | 2 | .splash { 3 | display: flex; 4 | justify-content: center; 5 | align-items: center; 6 | height: 100%; 7 | 8 | content { 9 | width: 700px; 10 | height: 400px; 11 | } 12 | 13 | p, h1 { 14 | color: $lighter-gray-text-color; 15 | } 16 | 17 | h1 { 18 | font-size: 42px; 19 | } 20 | 21 | p { 22 | font-size: 20px; 23 | } 24 | span { 25 | color: white; 26 | font-family: 'Bungee Shade', cursive; 27 | font-size: 75px; 28 | letter-spacing: 5px; 29 | } 30 | } 31 | 32 | .get-started { 33 | margin-top: 20px; 34 | height: 80px; 35 | display: flex; 36 | flex-direction: column; 37 | justify-content: space-between; 38 | align-items: center; 39 | } 40 | -------------------------------------------------------------------------------- /client/src/components/stage_button.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | class StageButton extends Component { 4 | constructor(props) { 5 | super(props); 6 | this.handleClick = this.handleClick.bind(this); 7 | } 8 | 9 | handleClick() { 10 | this.props.changeStage(this.props.idx); 11 | } 12 | 13 | render() { 14 | const stageClasses = ['dev-stage-button']; 15 | if (this.props.activeStage === this.props.idx) { 16 | stageClasses.push('dev-stage-active'); 17 | } 18 | 19 | return ( 20 |
21 | {this.props.stage.title} 22 |
23 | 24 | ); 25 | } 26 | } 27 | 28 | export default StageButton; 29 | -------------------------------------------------------------------------------- /client/src/utilities/generalUtil.js: -------------------------------------------------------------------------------- 1 | // Returns a function, that, as long as it continues to be invoked, will not 2 | // be triggered. The function will be called after it stops being called for 3 | // N milliseconds. If `immediate` is passed, trigger the function on the 4 | // leading edge, instead of the trailing. 5 | export const debounce = (func, wait, immediate) => { 6 | var timeout; 7 | return function() { 8 | var context = this, args = arguments; 9 | var later = function() { 10 | timeout = null; 11 | if (!immediate) func.apply(context, args); 12 | }; 13 | var callNow = immediate && !timeout; 14 | clearTimeout(timeout); 15 | timeout = setTimeout(later, wait); 16 | if (callNow) func.apply(context, args); 17 | }; 18 | }; 19 | -------------------------------------------------------------------------------- /client/src/store.js: -------------------------------------------------------------------------------- 1 | import { createStore, combineReducers, applyMiddleware } from 'redux'; 2 | import { logger } from 'redux-logger'; 3 | import thunk from 'redux-thunk'; 4 | import { saveState, loadState } from './utilities/localStorageHelper'; 5 | import uiReducer from './reducers/ui_reducer'; 6 | import modulesReducer from './reducers/modules_reducer'; 7 | 8 | const reducer = combineReducers({ 9 | ui: uiReducer, 10 | modules: modulesReducer 11 | }); 12 | 13 | const persistedState = loadState(); 14 | 15 | const store = createStore(reducer, 16 | persistedState, 17 | applyMiddleware( thunk, logger)); 18 | 19 | store.subscribe(() => { 20 | // determines what is persisted in localstorage 21 | 22 | saveState(); 23 | }); 24 | 25 | export default store; 26 | -------------------------------------------------------------------------------- /client/src/modules/spaceman/deploy.js: -------------------------------------------------------------------------------- 1 | import web3 from '../../utilities/web3Config'; 2 | const data = require('./AstronautContract'); 3 | const {abi, bytecode} = data; 4 | const AstronautContract = web3.eth.contract(abi); 5 | 6 | const deploy = (arbiterAddress, shipperAddress, astronautAddress, callback) => { 7 | const contractData = AstronautContract.new.getData(arbiterAddress, 8 | shipperAddress, {data: bytecode}); 9 | web3.eth.estimateGas({data: contractData}, (err, gas) => { 10 | if (err) { 11 | console.log(err); 12 | return; 13 | } 14 | console.log(`GAS: ${gas}`); 15 | AstronautContract.new(arbiterAddress, shipperAddress, { 16 | data: bytecode, 17 | from: astronautAddress, 18 | gas: gas 19 | }, callback); 20 | }); 21 | 22 | }; 23 | 24 | export default deploy; 25 | -------------------------------------------------------------------------------- /modules/spaceman/reactapp/src/web3Api/deploy.js: -------------------------------------------------------------------------------- 1 | import web3 from './web3Config'; 2 | const data = require('./AstronautContract'); 3 | const {abi, bytecode} = data; 4 | const AstronautContract = web3.eth.contract(abi); 5 | 6 | const deploy = (arbiterAddress, shipperAddress, astronautAddress, callback) => { 7 | const contractData = AstronautContract.new.getData(arbiterAddress, 8 | shipperAddress, {data: bytecode}); 9 | web3.eth.estimateGas({data: contractData}, (err, gas) => { 10 | if (err) { 11 | console.log(err); 12 | return; 13 | } 14 | console.log(`GAS: ${gas}`); 15 | AstronautContract.new(arbiterAddress, shipperAddress, { 16 | data: bytecode, 17 | from: astronautAddress, 18 | gas: gas 19 | }, callback); 20 | }); 21 | 22 | }; 23 | 24 | export default deploy; 25 | -------------------------------------------------------------------------------- /client/src/components/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Provider } from 'react-redux'; 3 | import { Route, Link, Switch, HashRouter } from 'react-router-dom'; 4 | import store from '../store'; 5 | import '../scss_dist/index.css'; 6 | import Tutorial from './tutorial'; 7 | import SplashPage from './splash'; 8 | import NavBar from './navbar'; 9 | 10 | 11 | class App extends Component { 12 | render() { 13 | return ( 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 |
23 |
24 |
25 | ); 26 | } 27 | } 28 | 29 | export default App; 30 | -------------------------------------------------------------------------------- /client/src/reducers/ui_reducer.js: -------------------------------------------------------------------------------- 1 | import { 2 | HIDE_DEV_SIDEBAR, 3 | SHOW_DEV_SIDEBAR, 4 | CHANGE_STAGE, 5 | SWITCH_TAB } from '../actions/ui_actions'; 6 | 7 | const defaultState = { 8 | showDevSidebar: true, 9 | activeStage: 0, 10 | activeTab: "code", 11 | solutionBoolean: false 12 | }; 13 | 14 | const uiReducer = (state = defaultState, action) => { 15 | switch (action.type) { 16 | case HIDE_DEV_SIDEBAR: 17 | return Object.assign({}, state, {showDevSidebar: false}); 18 | case SHOW_DEV_SIDEBAR: 19 | return Object.assign({}, state, {showDevSidebar: true}); 20 | case CHANGE_STAGE: 21 | return Object.assign({}, state, {activeStage: action.stageNumber}); 22 | case SWITCH_TAB: 23 | return Object.assign({}, state, {activeTab: action.tabName}); 24 | default: 25 | return state; 26 | } 27 | }; 28 | 29 | export default uiReducer; 30 | -------------------------------------------------------------------------------- /client/src/components/content.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import ReactMarkdown from 'react-markdown'; 3 | import { connect } from 'react-redux'; 4 | 5 | const mapStateToProps = (state) => { 6 | const activeStage = state.ui.activeStage; 7 | let content = ""; 8 | 9 | if (state.modules.spaceman.stages[activeStage]) { 10 | content = state.modules.spaceman.stages[activeStage].instructions; 11 | } 12 | 13 | return ({ 14 | content 15 | }); 16 | }; 17 | 18 | const mapDispatchToProps = (dispatch) => { 19 | return(null); 20 | }; 21 | 22 | 23 | class Content extends Component { 24 | 25 | render() { 26 | const input = this.props.content; 27 | 28 | return ( 29 |
30 | 31 |
32 | 33 | ); 34 | } 35 | } 36 | 37 | export default connect(mapStateToProps, null)(Content); 38 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "blockstreet", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "axios": "^0.17.1", 7 | "codemirror": "^5.34.0", 8 | "gulp": "^3.9.1", 9 | "gulp-multi-process": "^1.3.1", 10 | "gulp-run": "^1.7.1", 11 | "gulp-sass": "^3.1.0", 12 | "lodash": "^4.17.5", 13 | "metamask-logo": "^2.1.3", 14 | "react": "^16.2.0", 15 | "react-dom": "^16.2.0", 16 | "react-markdown": "^3.2.0", 17 | "react-redux": "^5.0.7", 18 | "react-router-dom": "^4.2.2", 19 | "react-scripts": "1.1.1", 20 | "redux": "^3.7.2", 21 | "redux-logger": "^3.0.6", 22 | "redux-thunk": "^2.2.0", 23 | "web3": "0.20.0" 24 | }, 25 | "scripts": { 26 | "start": "react-scripts start", 27 | "build": "react-scripts build", 28 | "test": "react-scripts test --env=jsdom", 29 | "eject": "react-scripts eject" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /modules/spaceman/reactapp/src/web3Api/astronautApi.js: -------------------------------------------------------------------------------- 1 | import { abi, bytecode } from './AstronautContract'; 2 | import web3 from './web3Config'; 3 | const AstronautContract = web3.eth.contract(abi); 4 | 5 | class AstronautApi { 6 | constructor(address) { 7 | this.address = address; 8 | this.instance = AstronautContract.at(address); 9 | } 10 | 11 | pay(astronautAddress, value, callback) { 12 | value = web3.toWei(value, 'ether'); 13 | this.instance.pay.sendTransaction({ 14 | from: astronautAddress, 15 | value: value 16 | }, 17 | callback); 18 | } 19 | 20 | receive(arbiterAddress, callback) { 21 | this.instance.receive.sendTransaction({ 22 | from: arbiterAddress 23 | }, 24 | callback); 25 | } 26 | 27 | withdraw(shipperAddress, callback) { 28 | this.instance.withdraw.sendTransaction({ 29 | from: shipperAddress 30 | }, 31 | callback); 32 | } 33 | } 34 | 35 | export default AstronautApi; 36 | -------------------------------------------------------------------------------- /client/src/components/splash.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | class SplashPage extends Component { 4 | constructor(props) { 5 | super(props); 6 | this.launchSpaceman = this.launchSpaceman.bind(this); 7 | } 8 | 9 | launchSpaceman(e) { 10 | this.props.history.push("/tutorial"); 11 | e.stopPropagation(); 12 | } 13 | 14 | render() { 15 | return ( 16 |
17 | 18 |

Welcome to BLOCKSTREET

19 |

20 | Blockstreet is an interactive environment for end to end 21 | development and testing of decentralized applications. 22 |

23 |
24 |
New to blockchain development?
25 | 26 |
27 |
28 |
29 | ); 30 | } 31 | } 32 | 33 | export default SplashPage; 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # blockstreet 2 | The De Facto, End-to-End Ethereum Dapp Launching Tutorial 3 | 4 | 5 | If you would like to participate in our Alpha testing, please email us at ethblockstreet2018@gmail.com 6 | 7 | Thank you! 8 | 9 | ## Running 10 | 11 | ### Start Client 12 | 13 | ```bash 14 | cd .../blockstreet/client 15 | npm install 16 | export PATH=$PWD/node_modules/.bin:$PATH 17 | which react-scripts 1>/dev/null || echo "Could not find react-scripts. Fix this before continuing." 18 | npm start 19 | ``` 20 | 21 | A browser tab should be opened at http://localhost:3000/. 22 | 23 | ### Tutorial 24 | 25 | Next, run through the tutorial. 26 | 27 | Before clicking any download links on the "Blastoff!" page, start the server. 28 | 29 | ### Start Server 30 | 31 | ```bash 32 | cd .../blockstreet/server 33 | npm install 34 | npm start 35 | ``` 36 | 37 | ## Troubleshooting Client 38 | 39 | * If you get `Error: watch .../blockstreet/client/public ENOSPC` then run: 40 | 41 | ```bash 42 | sudo bash -c 'echo 524288 >/proc/sys/fs/inotify/max_user_watches' 43 | ``` 44 | -------------------------------------------------------------------------------- /client/src/utilities/transactionPoll.js: -------------------------------------------------------------------------------- 1 | import web3 from './web3'; 2 | 3 | function _transactionTimeout(...args) { 4 | const [ hash, resolve, reject ] = args 5 | setTimeout(() => { 6 | web3.eth.getTransactionReceipt(hash, (err, receipt) => { 7 | if(err) { 8 | console.log('Failed to lookup receipt', err); 9 | _transactionTimeout(...args); 10 | } 11 | else if(!receipt) { 12 | // transaction receipt is not available for pending transactions 13 | _transactionTimeout(...args); 14 | } 15 | else { 16 | const { status } = receipt; 17 | if(+status === 0) { 18 | reject(receipt); 19 | } 20 | else { 21 | resolve(receipt); 22 | } 23 | } 24 | }) 25 | }, 1000) 26 | } 27 | 28 | const hashes = {} 29 | 30 | export const watchTransaction = (hash) => { 31 | if(hashes[hash]) return hashes[hash]; 32 | hashes[hash] = new Promise((resolve, reject) => _transactionTimeout(hash, resolve, reject)); 33 | return hashes[hash]; 34 | } 35 | -------------------------------------------------------------------------------- /client/src/components/main.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Content from './content'; 3 | import Code from './code'; 4 | import Deployment from './deployment'; 5 | import Completion from './completion'; 6 | import { connect } from 'react-redux'; 7 | 8 | const mapStateToProps = state => { 9 | return ({ 10 | currentModule: state.modules.spaceman, 11 | activeStage: state.ui.activeStage 12 | }); 13 | }; 14 | 15 | class Main extends Component { 16 | renderRightSection() { 17 | const { currentModule, activeStage } = this.props; 18 | const stage = currentModule.stages[activeStage]; 19 | if (!stage) return null; 20 | if(stage.type === 'code') return 21 | if(stage.type === 'deployment') return 22 | if(stage.type === 'completion') return 23 | return null; 24 | } 25 | render() { 26 | return ( 27 |
28 | 29 | { this.renderRightSection() } 30 |
31 | ); 32 | } 33 | } 34 | 35 | export default connect(mapStateToProps)(Main); 36 | -------------------------------------------------------------------------------- /client/scss/index.scss: -------------------------------------------------------------------------------- 1 | $accent-text-color: #51586a; 2 | $lighter-gray-text-color: #9d9d9e; 3 | $main-text-color: #cfd2da; 4 | $main-background-color: #252830; 5 | $light-blue-color: #1997c6; 6 | $main-line-color: #434857; 7 | $accent-background-color: #2c2d33; 8 | $dark-blue-color: #0f7dbd; 9 | 10 | @import 'navbar'; 11 | @import 'main'; 12 | @import 'code'; 13 | @import 'sidebar'; 14 | @import 'content'; 15 | @import 'splash'; 16 | @import 'deployment'; 17 | 18 | * { 19 | background-color: transparent; 20 | border: none; 21 | color: $main-text-color; 22 | text-decoration: none; 23 | } 24 | 25 | html { 26 | height: 100%; 27 | background: $main-background-color; 28 | color: $main-text-color; 29 | min-height: 800px; 30 | } 31 | 32 | 33 | #root { 34 | height: 100%; 35 | 36 | main { 37 | height: 100%; 38 | } 39 | } 40 | 41 | body { 42 | height: 100%; 43 | margin: 0; 44 | padding: 0; 45 | font-family: 'Roboto', sans-serif; 46 | 47 | h1 { 48 | -webkit-margin-before: 0; 49 | -webkit-margin-after: 0; 50 | } 51 | } 52 | 53 | .dev-body { 54 | display: flex; 55 | height: 85%; 56 | } 57 | -------------------------------------------------------------------------------- /modules/spaceman/reactapp/src/utilities/transactionPoll.js: -------------------------------------------------------------------------------- 1 | import web3 from '../web3Api/web3Config'; 2 | 3 | function _transactionTimeout(...args) { 4 | const [ hash, resolve, reject ] = args 5 | setTimeout(() => { 6 | web3.eth.getTransactionReceipt(hash, (err, receipt) => { 7 | if(err) { 8 | console.log('Failed to lookup receipt', err); 9 | _transactionTimeout(...args); 10 | } 11 | else if(!receipt) { 12 | // transaction receipt is not available for pending transactions 13 | _transactionTimeout(...args); 14 | } 15 | else { 16 | const { status } = receipt; 17 | if(+status === 0) { 18 | reject(receipt); 19 | } 20 | else { 21 | resolve(receipt); 22 | } 23 | } 24 | }) 25 | }, 1000) 26 | } 27 | 28 | const hashes = {} 29 | 30 | export const watchTransaction = (hash) => { 31 | if(hashes[hash]) return hashes[hash]; 32 | hashes[hash] = new Promise((resolve, reject) => _transactionTimeout(hash, resolve, reject)); 33 | return hashes[hash]; 34 | } 35 | -------------------------------------------------------------------------------- /client/src/components/code_display.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | const crUrl = 'https://codecast.qualified.io/relay'; 3 | 4 | 5 | class CodeDisplay extends Component { 6 | constructor(props) { 7 | super(props); 8 | this.handleMessage = this.handleMessage.bind(this); 9 | } 10 | 11 | 12 | handleMessage (e) { 13 | const response = e.data; 14 | if (response && response.method) { 15 | const { method } = response; 16 | 17 | if (method === 'notifyResponse') { 18 | const { data } = response; 19 | console.log(data); 20 | if(data.completed) { 21 | // we win! 22 | } 23 | else { 24 | // we don't 25 | } 26 | } 27 | } 28 | } 29 | 30 | componentDidMount() { 31 | window.addEventListener('message', this.handleMessage); 32 | this.props.updateRef(this.refs.frame); 33 | } 34 | 35 | render() { 36 | return ( 37 | 38 | ); 39 | } 40 | } 41 | 42 | export default CodeDisplay; 43 | -------------------------------------------------------------------------------- /modules/spaceman/truffle/contracts/astronaut.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.13; 2 | 3 | contract AstronautContract { 4 | address public astronaut; 5 | address public shipper; 6 | address public arbiter; 7 | 8 | enum State { 9 | DEPLOYED, 10 | PAID, 11 | RECEIVED, 12 | WITHDRAWN 13 | } 14 | State public state; 15 | 16 | event Paid(uint256 value); 17 | event Received(uint256 time); 18 | event Withdraw(uint256 value); 19 | 20 | function AstronautContract(address _arbiter, address _shipper) public { 21 | state = State.DEPLOYED; 22 | arbiter = _arbiter; 23 | shipper = _shipper; 24 | astronaut = msg.sender; 25 | } 26 | 27 | function pay() public payable { 28 | require(msg.sender == astronaut); 29 | state = State.PAID; 30 | Paid(msg.value); 31 | } 32 | 33 | function receive() public { 34 | require(msg.sender == arbiter); 35 | require(state == State.PAID); 36 | state = State.RECEIVED; 37 | Received(now); 38 | } 39 | 40 | function withdraw() public { 41 | require(msg.sender == shipper); 42 | require(state == State.RECEIVED); 43 | state = State.WITHDRAWN; 44 | Withdraw(this.balance); 45 | shipper.transfer(this.balance); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /client/src/components/code_tabs.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { switchTab } from '../actions/ui_actions'; 4 | 5 | const mapStateToProps = state => { 6 | return ({ 7 | activeTab: state.ui.activeTab 8 | }); 9 | }; 10 | 11 | const mapDispatchToProps = dispatch => { 12 | return ({ 13 | switchTab: (tabName) => dispatch(switchTab(tabName)) 14 | }); 15 | }; 16 | 17 | class CodeTabs extends Component { 18 | constructor(props) { 19 | super(props); 20 | 21 | this.handleSwitch = this.handleSwitch.bind(this); 22 | } 23 | 24 | handleSwitch(tabName) { 25 | return () => { 26 | if (this.props.activeTab !== tabName) { 27 | this.props.switchTab(tabName); 28 | } 29 | }; 30 | } 31 | 32 | render() { 33 | return ( 34 |
35 |
Script
36 |
Tests
37 |
38 | 39 | ); 40 | } 41 | } 42 | 43 | export default connect(mapStateToProps, mapDispatchToProps)(CodeTabs); 44 | -------------------------------------------------------------------------------- /client/src/utilities/PollWeb3.js: -------------------------------------------------------------------------------- 1 | import web3 from './web3Config'; 2 | import { Component } from 'react'; 3 | 4 | export const ERRORS = { 5 | MISSING_METAMASK: 0, 6 | NEED_TO_LOGIN: 1, 7 | SWITCH_NETWORK: 2 8 | } 9 | 10 | class PollWeb3 extends Component { 11 | constructor() { 12 | super(); 13 | this.state = { 14 | error: null, 15 | polling: true, 16 | } 17 | } 18 | componentDidMount() { 19 | this.checkStepLoop(); 20 | } 21 | checkStepLoop() { 22 | if(this.state.polling) { 23 | this.checkStep(); 24 | setTimeout(() => this.checkStepLoop(), 1000); 25 | } 26 | } 27 | checkStep() { 28 | if(!window.web3) { 29 | this.setState({ error: ERRORS.MISSING_METAMASK }); 30 | return; 31 | } 32 | 33 | web3.eth.getAccounts((err, accounts) => { 34 | if(accounts.length === 0) { 35 | this.setState({ error: ERRORS.NEED_TO_LOGIN }); 36 | } 37 | else if(web3.version.network === '1') { 38 | this.setState({ error: ERRORS.SWITCH_NETWORK }); 39 | } 40 | else if(this.state.polling) { 41 | this.setState({ error: null, polling: false }); 42 | if(this.donePolling) this.donePolling(); 43 | } 44 | }) 45 | } 46 | } 47 | 48 | export default PollWeb3; 49 | -------------------------------------------------------------------------------- /client/src/components/deployment.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import MetaMask from './metamask'; 3 | import { connect } from 'react-redux'; 4 | import deploy from '../modules/spaceman/deploy'; 5 | import web3 from '../utilities/web3Config'; 6 | 7 | const mapStateToProps = state => { 8 | const { activeStage } = state.ui; 9 | const { stages } = state.modules.spaceman; 10 | return ({ 11 | lastSolidityStage: stages[3], // TODO: hardcoded for now! 12 | currentStage: stages[activeStage], 13 | }); 14 | }; 15 | 16 | class Deployment extends Component { 17 | constructor(props) { 18 | super(props); 19 | this.state = { 20 | deploy: false 21 | } 22 | this.deploy = this.deploy.bind(this); 23 | } 24 | deploy() { 25 | deploy(web3.eth.accounts[0], web3.eth.accounts[0], web3.eth.accounts[0], () => {}); 26 | } 27 | render() { 28 | if(!this.state.deploy) return this.setState({ deploy: true })}/> 29 | return ( 30 |
31 |

You can deploy your contract to the test network

32 |
33 | Deploy my Contract 34 |
35 |
36 | ) 37 | } 38 | } 39 | 40 | export default connect(mapStateToProps)(Deployment); 41 | -------------------------------------------------------------------------------- /client/src/reducers/modules_reducer.js: -------------------------------------------------------------------------------- 1 | import { UPDATE_CODE, SHOW_SOLUTION } from '../actions/module_actions'; 2 | import { merge } from 'lodash'; 3 | import spacemanData from '../modules/spaceman/config'; 4 | 5 | const defaultState = { 6 | spaceman: spacemanData 7 | }; 8 | 9 | const modulesReducer = (state = defaultState, action) => { 10 | const { stages } = state.spaceman; 11 | const currentStage = stages[action.activeStage]; 12 | switch (action.type) { 13 | case SHOW_SOLUTION: 14 | return { 15 | ...state, 16 | spaceman: { 17 | ...state.spaceman, 18 | stages: [ 19 | ...stages.slice(0, action.activeStage), 20 | { ...currentStage, code: currentStage.referenceSolution }, 21 | ...stages.slice(action.activeStage + 1) 22 | ] 23 | } 24 | } 25 | case UPDATE_CODE: 26 | return { 27 | ...state, 28 | spaceman: { 29 | ...state.spaceman, 30 | stages: [ 31 | ...stages.slice(0, action.activeStage), 32 | { ...currentStage, code: action.code }, 33 | ...stages.slice(action.activeStage + 1) 34 | ], 35 | } 36 | } 37 | default: 38 | return state; 39 | } 40 | }; 41 | 42 | export default modulesReducer; 43 | -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const app = express(); 3 | const bodyParser = require('body-parser'); 4 | const port = process.env.PORT || 4049; 5 | const Archiver = require('archiver'); 6 | 7 | const routesPrefix = '/api'; 8 | 9 | app.use(bodyParser.json()); 10 | app.use(bodyParser.urlencoded({ extended: true })); 11 | 12 | app.get(`${routesPrefix}/zip/`, function(request, response) { 13 | const { contract, testCases } = request.query; 14 | 15 | response.writeHead(200, { 16 | 'Content-Type': 'application/zip', 17 | 'Content-disposition': 'attachment; filename=astronaut.zip' 18 | }); 19 | 20 | let zip = Archiver('zip'); 21 | 22 | zip.pipe(response); 23 | 24 | zip.directory('../modules/spaceman/reactapp/', 'reactapp'); 25 | zip.directory('../modules/spaceman/truffle/migrations', 'reactapp/truffle/migrations'); 26 | zip.directory('../modules/spaceman/truffle/tests', 'reactapp/truffle/tests'); 27 | 28 | zip.file('../modules/spaceman/truffle/contracts/Migrations.sol', { name: 'reactapp/truffle/contracts/Migrations.sol' }); 29 | 30 | zip.append(contract || "", { name: 'reactapp/truffle/contracts/astronaut.sol' }) 31 | zip.append(testCases || "", { name: 'reactapp/truffle/tests/astronaut.js' }) 32 | 33 | zip.finalize(); 34 | }); 35 | 36 | app.listen(port, () => console.log(`I'm alive on port ${port}! Links on the Blastoff page should now work.`)) 37 | -------------------------------------------------------------------------------- /modules/spaceman/reactapp/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Deploy from './component/deploy'; 3 | import Fund from './component/fund'; 4 | import Shipped from './component/shipped'; 5 | import Approve from './component/approve'; 6 | import Withdraw from './component/withdraw'; 7 | import UserInfo from './component/userInfo'; 8 | import web3 from './web3Api/web3Config'; 9 | 10 | 11 | class App extends Component { 12 | constructor (props) { 13 | super(props); 14 | this.state = { 15 | currentStage: "STAGE-0", 16 | contract: {}, 17 | balance: 0, 18 | arbiterAddress: "", 19 | shipperAddress: "", 20 | astronautAddress: "" 21 | }; 22 | this.setGlobalState = this.setGlobalState.bind(this); 23 | } 24 | 25 | setGlobalState(key, value) { 26 | this.setState( {[key]: value}); 27 | } 28 | 29 | render() { 30 | const globalProps = { 31 | state: this.state, 32 | setGlobalState: this.setGlobalState, 33 | }; 34 | 35 | return ( 36 |
37 |
38 | 39 | 40 | 41 | 42 | 43 |
44 | 45 |
46 | ); 47 | } 48 | } 49 | 50 | export default App; 51 | -------------------------------------------------------------------------------- /client/scss/_sidebar.scss: -------------------------------------------------------------------------------- 1 | $size-sidebar-knob: 45px; 2 | 3 | .dev-sidebar { 4 | z-index: 20; 5 | height: 100%; 6 | display: flex; 7 | background-color: $accent-background-color; 8 | border-top: 7px solid #141417; 9 | border-bottom: 7px solid #141417; 10 | 11 | h1 { 12 | padding: 10px 0; 13 | text-align: center; 14 | font-size: 25px; 15 | border-bottom: 1px solid $main-line-color; 16 | } 17 | } 18 | 19 | .dev-sidebar-handle { 20 | width: 8px; 21 | height: inherit; 22 | position: relative; 23 | cursor: pointer; 24 | background-color: #141417; 25 | } 26 | 27 | 28 | 29 | 30 | #sidebar-knob { 31 | display: block; 32 | position: absolute; 33 | top: 50%; 34 | width: $size-sidebar-knob - 18px; 35 | height: $size-sidebar-knob; 36 | background-color: #141417; 37 | cursor: pointer; 38 | border-bottom-right-radius: $size-sidebar-knob * 2.5; 39 | border-top-right-radius: $size-sidebar-knob * 2.5; 40 | } 41 | 42 | 43 | .hide-sidebar { 44 | display: none; 45 | } 46 | 47 | .hidden-sidebar:hover { 48 | .dev-sidebar-handle { 49 | width: 10px; 50 | } 51 | #sidebar-knob { 52 | width: $size-sidebar-knob - 16px; 53 | } 54 | } 55 | 56 | 57 | .dev-stage-button { 58 | height: 18px; 59 | width: 180px; 60 | padding: 15px 0; 61 | font-size: 20px; 62 | text-align: center; 63 | cursor: pointer; 64 | } 65 | 66 | .dev-stage-button:hover { 67 | background-color: $light-blue-color; 68 | color: white; 69 | } 70 | 71 | .dev-stage-active { 72 | background-color: $dark-blue-color; 73 | color: white; 74 | } 75 | -------------------------------------------------------------------------------- /client/src/components/code_buttons.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { showSolution, updateCode } from '../actions/module_actions'; 3 | import { connect } from 'react-redux'; 4 | 5 | const mapDispatchToProps = dispatch => { 6 | return { 7 | showSolution: (activeStage) => dispatch(showSolution(activeStage)), 8 | updateCode: (code, activeStage) => dispatch(updateCode(code, activeStage)) 9 | }; 10 | }; 11 | 12 | class CodeButtons extends Component { 13 | constructor(props) { 14 | super(props); 15 | } 16 | 17 | render() { 18 | const { showSolution, activeStage, updateCode } = this.props; 19 | return ( 20 |
21 | 22 | 23 | 24 |
25 | ); 26 | } 27 | } 28 | 29 | class RunTestButton extends Component { 30 | constructor(props) { 31 | super(props); 32 | this.handleClick = this.handleClick.bind(this); 33 | } 34 | 35 | handleClick(e) { 36 | this.props.runTest(e); 37 | } 38 | 39 | render() { 40 | return ( 41 | 42 | ); 43 | } 44 | } 45 | 46 | class ContinueButton extends Component { 47 | render() { 48 | return ( 49 | 50 | ); 51 | } 52 | } 53 | 54 | export default connect(null, mapDispatchToProps)(CodeButtons); 55 | -------------------------------------------------------------------------------- /client/scss/_code.scss: -------------------------------------------------------------------------------- 1 | .dev-code { 2 | display: flex; 3 | flex-direction: column; 4 | 5 | .CodeMirror { 6 | flex-grow: 2.5; 7 | margin: 0 0 20px 0; 8 | } 9 | .code-display { 10 | background-color: #131414; 11 | flex-grow: 1; 12 | } 13 | } 14 | 15 | 16 | .dev-content { 17 | padding: 15px; 18 | width: 515px; 19 | overflow-y: scroll; 20 | } 21 | 22 | .dev-code { 23 | flex-grow: 1.75; 24 | } 25 | 26 | .dev-code-button { 27 | display: flex; 28 | } 29 | 30 | .dev-code-buttons { 31 | position: absolute; 32 | display: flex; 33 | bottom: 0; 34 | } 35 | 36 | .dev-button { 37 | margin-left: 2rem; 38 | } 39 | 40 | #run-test-button, .dev-button { 41 | cursor: pointer; 42 | padding: 7px 17px; 43 | text-align: center; 44 | border-radius: 6px; 45 | border: 1px solid $light-blue-color; 46 | color: $light-blue-color; 47 | font-size: 14px; 48 | &.disabled { 49 | color: #869094; 50 | border: 1px solid #869094; 51 | } 52 | } 53 | 54 | #run-test-button:hover, .dev-button:hover{ 55 | color: white; 56 | background-color: $light-blue-color; 57 | font-size: 14px; 58 | } 59 | 60 | .hidden-editor { 61 | height: 0; 62 | overflow: hidden; 63 | } 64 | 65 | 66 | .dev-code-tabs { 67 | display: flex; 68 | justify-content: flex-start; 69 | 70 | div { 71 | height: 20px; 72 | width: 70px; 73 | background-color: #0c1020; 74 | cursor: pointer; 75 | padding: 10px; 76 | border-top-right-radius: 5px; 77 | border-top-left-radius: 5px; 78 | text-align: center; 79 | margin-left: 5px; 80 | } 81 | 82 | .inactive-tab { 83 | background-color: #1B223E; 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /modules/spaceman/reactapp/src/component/shipped.jsx: -------------------------------------------------------------------------------- 1 | import React , { Component } from 'react'; 2 | 3 | class Shipped extends Component { 4 | constructor (props) { 5 | super(props); 6 | this.state = props.globalProps.state; 7 | this.setGlobalState = props.globalProps.setGlobalState; 8 | this.shipGoods = this.shipGoods.bind(this); 9 | } 10 | 11 | 12 | componentWillReceiveProps(nextProps) { 13 | this.setState(nextProps.globalProps.state); 14 | } 15 | 16 | componentDidUpdate() { 17 | if (this.state.currentStage === "STAGE-2") { 18 | document.getElementById('shipped').classList.add('active_stage'); 19 | } 20 | } 21 | 22 | shipGoods() { 23 | if (this.state.currentStage === "STAGE-2") { 24 | console.log("GOODS SHIPPED!"); 25 | document.getElementById('shipped').classList.remove('active_stage'); 26 | this.setGlobalState('currentStage', "STAGE-3"); 27 | } 28 | } 29 | 30 | render() { 31 | const coverClass = this.state.currentStage === "STAGE-2" ? 32 | "closed" : "open"; 33 | 34 | return ( 35 |
36 | 37 |
38 |
39 | 40 |
41 |
42 | 46 |
47 | 48 |
49 |
50 | 51 |
52 |
53 |
54 | 55 |
56 | ); 57 | } 58 | } 59 | 60 | export default Shipped; 61 | -------------------------------------------------------------------------------- /client/src/components/completion.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { connect } from 'react-redux'; 3 | 4 | const mapStateToProps = state => { 5 | const { stages } = state.modules.spaceman; 6 | return ({ 7 | lastSolidityStage: stages[3], // TODO: hardcoded for now! 8 | }); 9 | }; 10 | 11 | // from https://stackoverflow.com/questions/1714786/query-string-encoding-of-a-javascript-object 12 | function serialize(obj) { 13 | var str = []; 14 | for(var p in obj) 15 | if (obj.hasOwnProperty(p)) { 16 | str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p])); 17 | } 18 | return str.join("&"); 19 | } 20 | 21 | class Completion extends Component { 22 | constructor(props) { 23 | super(props); 24 | this.deploy = this.deploy.bind(this); 25 | } 26 | deploy() { 27 | const { lastSolidityStage } = this.props; 28 | let query = serialize({ 29 | contract: lastSolidityStage.code, 30 | testCases: lastSolidityStage.testCases 31 | }) 32 | window.open(`http://localhost:4049/api/zip?${query}`) 33 | } 34 | render() { 35 | return ( 36 |
37 |

You can download your application!

38 |
39 | Download Source Code in React Application 40 |
41 |
42 | Download Source Code in Angular Application 43 |
44 |
45 | Download Source Code in Vue Application 46 |
47 |
48 | ) 49 | } 50 | } 51 | 52 | export default connect(mapStateToProps)(Completion); 53 | -------------------------------------------------------------------------------- /modules/spaceman/reactapp/src/component/approve.jsx: -------------------------------------------------------------------------------- 1 | import React , { Component } from 'react'; 2 | 3 | class Approve extends Component { 4 | constructor (props) { 5 | super(props); 6 | this.state = props.globalProps.state; 7 | this.setGlobalState = props.globalProps.setGlobalState; 8 | this.approveTransaction = this.approveTransaction.bind(this); 9 | } 10 | 11 | componentWillReceiveProps(nextProps) { 12 | this.setState(nextProps.globalProps.state); 13 | } 14 | 15 | componentDidUpdate() { 16 | if (this.state.currentStage === "STAGE-3") { 17 | document.getElementById('approve').classList.add('active_stage'); 18 | } 19 | } 20 | 21 | approveTransaction() { 22 | if (this.state.currentStage === "STAGE-3") { 23 | this.state.contract.receive(this.state.arbiterAddress, (err, res) => { 24 | if (err) { 25 | console.log(err); 26 | return; 27 | } 28 | console.log(`TRANSACTION APPROVED`); 29 | console.log(res); 30 | document.getElementById('approve').classList.remove('active_stage'); 31 | this.setGlobalState('currentStage', "STAGE-4"); 32 | }); 33 | } 34 | } 35 | 36 | render() { 37 | const coverClass = this.state.currentStage === "STAGE-3" ? 38 | "closed" : "open"; 39 | 40 | return ( 41 |
42 | 43 |
44 |
45 | 46 |
47 |
48 | 53 |
54 | 55 |
56 |
57 | 58 |
59 |
60 |
61 | 62 |
63 | ); 64 | } 65 | } 66 | 67 | export default Approve; 68 | -------------------------------------------------------------------------------- /modules/spaceman/reactapp/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 14 | 15 | 16 | 25 | Astronaut Smart Contract 26 | 27 | 28 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /client/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 15 | 16 | 17 | 26 | Blockstreet 27 | 28 | 29 | 32 |
33 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /modules/spaceman/reactapp/src/component/withdraw.jsx: -------------------------------------------------------------------------------- 1 | import React , { Component } from 'react'; 2 | 3 | class Withdraw extends Component { 4 | constructor (props) { 5 | super(props); 6 | this.state = props.globalProps.state; 7 | this.setGlobalState = props.globalProps.setGlobalState; 8 | this.withdrawFunds = this.withdrawFunds.bind(this); 9 | } 10 | 11 | componentWillReceiveProps(nextProps) { 12 | this.setState(nextProps.globalProps.state); 13 | } 14 | 15 | componentDidUpdate() { 16 | if (this.state.currentStage === "STAGE-4") { 17 | document.getElementById('withdraw').classList.add('active_stage'); 18 | } 19 | } 20 | 21 | withdrawFunds() { 22 | if (this.state.currentStage === "STAGE-4") { 23 | this.state.contract.withdraw(this.state.shipperAddress, (err, res) => { 24 | if (err) { 25 | console.log(err); 26 | return; 27 | } 28 | console.log(`FUNDS WITHDRAWN`); 29 | console.log(res); 30 | document.getElementById('withdraw').classList.remove('active_stage'); 31 | this.setGlobalState('currentStage', "COMPLETE"); 32 | this.setGlobalState('balance', 0); 33 | }); 34 | } 35 | } 36 | 37 | render() { 38 | const coverClass = this.state.currentStage === "STAGE-4" ? 39 | "closed" : "open"; 40 | 41 | return ( 42 |
43 | 44 |
45 |
46 | 47 |
48 |
49 | 53 |
54 | 55 |
56 |
57 | 58 |
59 | 63 |
64 |
65 | 66 |
67 | ); 68 | } 69 | } 70 | 71 | export default Withdraw; 72 | -------------------------------------------------------------------------------- /client/src/components/code_editor.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import CodeMirror from 'codemirror'; 3 | import 'codemirror/lib/codemirror.css'; 4 | import 'codemirror/theme/blackboard.css'; 5 | import 'codemirror/mode/javascript/javascript.js'; 6 | import '../utilities/solidityMode'; 7 | import { debounce } from '../utilities/generalUtil'; 8 | import { connect } from 'react-redux'; 9 | 10 | class CodeEditor extends Component { 11 | constructor(props) { 12 | super(props); 13 | 14 | this.handleChange = this.handleChange.bind(this); 15 | this.updateStoreCode = this.updateStoreCode.bind(this); 16 | } 17 | 18 | handleChange(type) { 19 | return (editor) => { 20 | this.props.updateCompCodeState({[type]: editor.getValue()}); 21 | }; 22 | } 23 | 24 | updateStoreCode(type) { 25 | if(type !== "testCases") { 26 | return debounce((editor)=>{ 27 | this.props.updateCode(editor.getValue(), this.props.activeStage); 28 | }, 1000).bind(this); 29 | } 30 | return () => {} 31 | } 32 | 33 | componentDidMount() { 34 | let readOnly = false; 35 | 36 | const { type } = this.props; 37 | 38 | if (type === "testCases") { 39 | readOnly = true; 40 | } 41 | 42 | this.codeMirror = CodeMirror.fromTextArea(this.refs.editor, { 43 | mode: this.props.mode, 44 | theme: 'blackboard', 45 | lineNumbers: true, 46 | readOnly: readOnly, 47 | lineWrapping: true 48 | }); 49 | 50 | this.codeMirror.on('changes', this.handleChange(type)); 51 | this.codeMirror.on('changes', this.updateStoreCode(type)); 52 | this.props.updateCompCodeState({[type]: this.codeMirror.getValue()}); 53 | 54 | this.codeMirror.setValue(this.props.currentModule[type] || ""); 55 | } 56 | 57 | componentWillReceiveProps (nextProps) { 58 | const { type } = this.props; 59 | if(this.props.currentModule[type] !== nextProps.currentModule[type]) { 60 | this.codeMirror.setValue(nextProps.currentModule[type] || ""); 61 | } 62 | } 63 | 64 | render() { 65 | return ( 66 |
68 |