From 15c0403cbc455b5afefb87f0c0750522791cd3ef Mon Sep 17 00:00:00 2001 From: CarlosFaria94 Date: Sat, 18 Aug 2018 20:56:03 +0100 Subject: [PATCH] Init --- .angular-cli.json | 52 +++++++ .gitignore | 46 ++++++ README.md | 89 +++++++++++ ci/build.sh | 19 +++ ci/install-deps.sh | 6 + contracts/EntryStorage.sol | 92 ++++++++++++ contracts/Migrations.sol | 23 +++ e2e/app.e2e-spec.ts | 14 ++ e2e/app.po.ts | 45 ++++++ e2e/meta-sender.e2e-spec.ts | 31 ++++ e2e/tsconfig.e2e.json | 14 ++ karma.conf.js | 45 ++++++ migrations/1_initial_migration.js | 5 + migrations/2_initial_migration.js | 5 + package.json | 57 +++++++ protractor.conf.js | 34 +++++ src/app/app.component.css | 0 src/app/app.component.html | 2 + src/app/app.component.ts | 10 ++ src/app/app.module.ts | 34 +++++ .../meta-sender/meta-sender.component.css | 4 + .../meta-sender/meta-sender.component.html | 27 ++++ .../meta/meta-sender/meta-sender.component.ts | 114 ++++++++++++++ src/app/meta/meta.module.ts | 34 +++++ src/app/util/util.module.ts | 10 ++ src/app/util/web3.service.spec.ts | 40 +++++ src/app/util/web3.service.ts | 78 ++++++++++ src/assets/.gitkeep | 0 src/environments/environment.prod.ts | 3 + src/environments/environment.ts | 8 + src/favicon.ico | Bin 0 -> 5430 bytes src/index.html | 14 ++ src/main.ts | 12 ++ src/polyfills.ts | 72 +++++++++ src/styles.css | 2 + src/test.ts | 32 ++++ src/tsconfig.app.json | 13 ++ src/tsconfig.spec.json | 20 +++ src/typings.d.ts | 10 ++ truffle-box.json | 18 +++ truffle-config.js | 18 +++ truffle.js | 25 +++ tsconfig.json | 20 +++ tslint.json | 142 ++++++++++++++++++ 44 files changed, 1339 insertions(+) create mode 100644 .angular-cli.json create mode 100644 .gitignore create mode 100755 README.md create mode 100755 ci/build.sh create mode 100755 ci/install-deps.sh create mode 100644 contracts/EntryStorage.sol create mode 100755 contracts/Migrations.sol create mode 100755 e2e/app.e2e-spec.ts create mode 100755 e2e/app.po.ts create mode 100755 e2e/meta-sender.e2e-spec.ts create mode 100755 e2e/tsconfig.e2e.json create mode 100755 karma.conf.js create mode 100755 migrations/1_initial_migration.js create mode 100755 migrations/2_initial_migration.js create mode 100755 package.json create mode 100755 protractor.conf.js create mode 100755 src/app/app.component.css create mode 100755 src/app/app.component.html create mode 100755 src/app/app.component.ts create mode 100755 src/app/app.module.ts create mode 100755 src/app/meta/meta-sender/meta-sender.component.css create mode 100755 src/app/meta/meta-sender/meta-sender.component.html create mode 100755 src/app/meta/meta-sender/meta-sender.component.ts create mode 100755 src/app/meta/meta.module.ts create mode 100755 src/app/util/util.module.ts create mode 100755 src/app/util/web3.service.spec.ts create mode 100755 src/app/util/web3.service.ts create mode 100755 src/assets/.gitkeep create mode 100755 src/environments/environment.prod.ts create mode 100755 src/environments/environment.ts create mode 100755 src/favicon.ico create mode 100755 src/index.html create mode 100755 src/main.ts create mode 100755 src/polyfills.ts create mode 100755 src/styles.css create mode 100755 src/test.ts create mode 100755 src/tsconfig.app.json create mode 100755 src/tsconfig.spec.json create mode 100755 src/typings.d.ts create mode 100755 truffle-box.json create mode 100755 truffle-config.js create mode 100755 truffle.js create mode 100755 tsconfig.json create mode 100755 tslint.json diff --git a/.angular-cli.json b/.angular-cli.json new file mode 100644 index 0000000..9e23b1f --- /dev/null +++ b/.angular-cli.json @@ -0,0 +1,52 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "project": { + "name": "angular-truffle-box" + }, + "apps": [ + { + "root": "src", + "outDir": "dist", + "assets": ["assets", "favicon.ico"], + "index": "index.html", + "main": "main.ts", + "polyfills": "polyfills.ts", + "test": "test.ts", + "tsconfig": "tsconfig.app.json", + "testTsconfig": "tsconfig.spec.json", + "prefix": "app", + "styles": ["styles.css"], + "scripts": [], + "environmentSource": "environments/environment.ts", + "environments": { + "dev": "environments/environment.ts", + "prod": "environments/environment.prod.ts" + } + } + ], + "e2e": { + "protractor": { + "config": "./protractor.conf.js" + } + }, + "lint": [ + { + "project": "src/tsconfig.app.json" + }, + { + "project": "src/tsconfig.spec.json" + }, + { + "project": "e2e/tsconfig.e2e.json" + } + ], + "test": { + "karma": { + "config": "./karma.conf.js" + } + }, + "defaults": { + "styleExt": "css", + "component": {} + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7fdf3d8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,46 @@ +# dependencies +node_modules + +# Solidity +config/development/contracts.json +config/development/*.sol.js +config/test/contracts.json +build + +## compiled output +/dist +/tmp +/out-tsc + +## e2e +/e2e/*.js +/e2e/*.map + +# testing +coverage + +# production +build_webpack + +# misc +/.sass-cache +/connect.lock +/libpeerconnection.log +testem.log +/typings +/shippable +.DS_Store +.env +npm-debug.log +.truffle-solidity-loader + +# Logs +logs +*.log + +# VS Code +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json \ No newline at end of file diff --git a/README.md b/README.md new file mode 100755 index 0000000..4c72a9b --- /dev/null +++ b/README.md @@ -0,0 +1,89 @@ +# Truffle Box for Angular + +This Truffle Box provides a base for working with the Truffle Framework and Angular. +It provides a basic working example of the MetaCoin contracts with Angular components. +This project is generated with [Angular CLI](https://cli.angular.io/). + +## Prerequisites + +In order to run the Truffle box, you will need [Node.js](https://nodejs.org) (tested with version 8.9.x). This will include `npm`, needed +to install dependencies. In order install these dependencies, you will also need [Python](https://www.python.org) (version 2.7.x) and +[git](https://git-scm.com/downloads). You will also need the [MetaMask](https://metamask.io/) plugin for Chrome. + +## Building + +1. Install truffle, Angular CLI and an Ethereum client. If you don't have a test environment, we recommend ganache-cli + ```bash + npm install -g truffle + npm install -g @angular/cli + npm install -g ganache-cli + ``` + +2. Download the box. + ```bash + truffle unbox Quintor/angular-truffle-box + ``` + +3. Run your Ethereum client. For Ganache CLI: + ```bash + ganache-cli + ``` +Note the mnemonic 12-word phrase printed on startup, you will need it later. + +4. Compile and migrate your contracts. + ```bash + truffle compile && truffle migrate + ``` + +## Configuration +1. In order to connect with the Ethereum network, you will need to configure MetaMask +2. Log into the `ganache-cli` test accounts in MetaMask, using the 12-word phrase printed earlier. + 1. A detailed explaination of how to do this can be found [here](http://truffleframework.com/docs/advanced/truffle-with-metamask#using-the-browser-extension) + 1. Normally, the available test accounts will change whenever you restart `ganache-cli`. + 2. In order to receive the same test accounts every time you start `ganache-cli`, start it with a seed like this: `ganache-cli --seed 0` or `ganache-cli -m "put your mnemonic phrase here needs twelve words to work with MetaMask"` +3. Point MetaMask to `ganache-cli` by connecting to the network `localhost:8545` + + +## Running + +1. Run the app using Angular CLI: + ```bash + npm start + ``` +The app is now served on localhost:4200 + +2. Making sure you have configured MetaMask, visit http://localhost:4200 in your browser. + +3. Send MetaCoins! + +## Testing + +1. Running the Angular component tests: + ```bash + ng test + ``` + +2. Running the Truffle tests: + ```bash + truffle test + ``` + +3. Running Protactor end-to-end tests + + ```bash + ng e2e + ``` +## Releasing +Using the Angular CLI you can build a distributable of your app. Will be placed in `dist/` + + ```bash + ng build + ``` + +## FAQ + +* __Where can I find more documentation?__ + +This Truffle box is a union of [Truffle](http://truffleframework.com/) and an Angular setup created with [Angular CLI](https://cli.angular.io/). +For solidity compilation and Ethereum related issues, try the [Truffle documentation](http://truffleframework.com/docs/). +For Angular CLI and typescript issues, refer to the [Angular CLI documentation](https://github.com/angular/angular-cli/wiki) diff --git a/ci/build.sh b/ci/build.sh new file mode 100755 index 0000000..8c20bd5 --- /dev/null +++ b/ci/build.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env sh +set -x +set -e +ganache-cli -m "candy maple cake sugar pudding cream honey rich smooth crumble sweet treat" > /dev/null & +GANACHE_PID=$! +trap "kill $GANACHE_PID" EXIT INT TERM + +truffle compile +truffle migrate +truffle test + +npm test +ng e2e +ng lint + + +#(0) 0x627306090abab3a6e1400e9345bc60c78a8bef57 +#(1) 0xf17f52151ebef6c7334fad080c5704d77216b732 +#(2) 0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef diff --git a/ci/install-deps.sh b/ci/install-deps.sh new file mode 100755 index 0000000..f2b2f30 --- /dev/null +++ b/ci/install-deps.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env sh +set -x +npm install -g @angular/cli +npm install -g truffle +npm install -g ganache-cli +npm install diff --git a/contracts/EntryStorage.sol b/contracts/EntryStorage.sol new file mode 100644 index 0000000..7c93b0b --- /dev/null +++ b/contracts/EntryStorage.sol @@ -0,0 +1,92 @@ +pragma solidity ^0.4.23; + +contract EntryStorage { + mapping (uint => Entry) public entries; + uint public entryCount; + + struct Entry { + uint id; + address owner; + uint bounty; + // Multihash directoryHash; + uint unsafeCreatedTimestamp; + uint submissionCount; + mapping (uint => Submission) submissions; + Submission acceptedSubmission; + uint state; + } + + enum State { Open, Submitted, Done, Canceled } + + struct Submission { + uint id; + address owner; + // Multihash directoryHash; + uint created; + } + + struct Multihash { + bytes32 digest; + uint8 hashFunction; + uint8 size; + } + + constructor() public { + entryCount = 0; + } + + function addEntry( + // bytes32 _directoryDigest, + // uint8 _directoryHashFunction, + // uint8 _directorySize + ) public payable { + entryCount = entryCount + 1; + + // Multihash memory _directoryHash = Multihash( + // _directoryDigest, _directoryHashFunction, _directorySize); + + entries[entryCount].id = entryCount; + entries[entryCount].owner = msg.sender; + entries[entryCount].bounty = msg.value; + // entries[entryCount].directoryHash = _directoryHash; + // This timestamp will not be used for critical contract logic, only as reference + entries[entryCount].unsafeCreatedTimestamp = block.timestamp; + entries[entryCount].submissionCount = 0; + entries[entryCount].state = uint(State.Open); + } + + function cancelEntry(uint _entryId) public { + entries[_entryId].state = uint(State.Canceled); + entries[_entryId].owner.transfer(entries[_entryId].bounty); + } + + function submit( + uint _entryId + // bytes32 _directoryDigest, + // uint8 _directoryHashFunction, + // uint8 _directorySize + ) public { + Entry storage e = entries[_entryId]; + e.submissionCount = e.submissionCount + 1; + + // Multihash memory _directoryHash = Multihash( + // _directoryDigest, _directoryHashFunction, _directorySize); + + Submission memory newSubmission = Submission( + e.submissionCount, + msg.sender, + // _directoryHash, + block.timestamp + ); + + e.state = uint(State.Submitted); + e.submissions[e.submissionCount] = newSubmission; + } + + function acceptSubmission(uint _entryId, uint _submissionId) public { + Entry storage e = entries[_entryId]; + e.state = uint(State.Done); + e.acceptedSubmission = e.submissions[_submissionId]; + } + +} \ No newline at end of file diff --git a/contracts/Migrations.sol b/contracts/Migrations.sol new file mode 100755 index 0000000..c4efb65 --- /dev/null +++ b/contracts/Migrations.sol @@ -0,0 +1,23 @@ +pragma solidity ^0.4.23; + +contract Migrations { + address public owner; + uint public last_completed_migration; + + constructor() public { + owner = msg.sender; + } + + modifier restricted() { + if (msg.sender == owner) _; + } + + function setCompleted(uint completed) public restricted { + last_completed_migration = completed; + } + + function upgrade(address new_address) public restricted { + Migrations upgraded = Migrations(new_address); + upgraded.setCompleted(last_completed_migration); + } +} diff --git a/e2e/app.e2e-spec.ts b/e2e/app.e2e-spec.ts new file mode 100755 index 0000000..ea97f5c --- /dev/null +++ b/e2e/app.e2e-spec.ts @@ -0,0 +1,14 @@ +import { AppPage } from './app.po'; + +describe('angular-truffle-box App', () => { + let page: AppPage; + + beforeEach(() => { + page = new AppPage(); + }); + + it('should display welcome message', () => { + page.navigateTo(); + expect(page.getHeader()).toContain('Angular Truffle Box'); + }); +}); diff --git a/e2e/app.po.ts b/e2e/app.po.ts new file mode 100755 index 0000000..7444a57 --- /dev/null +++ b/e2e/app.po.ts @@ -0,0 +1,45 @@ +import { browser, by, element } from 'protractor'; + +export class AppPage { + navigateTo() { + browser.waitForAngularEnabled(false); + browser.get('/'); + return browser.driver.sleep(1000); + } + + getHeader() { + return element(by.css('mat-toolbar')).getText(); + } + + getButton() { + return element(by.id('send')); + } + + getBalance() { + return element(by.id('balance')).getText(); + } + + clickSelect() { + return element(by.id('address-selector')).click(); + } + + setAddress(index: number) { + return element.all(by.tagName('mat-option')).filter(( + (el, i) => { + return index === i; + } + )).click(); + } + + setToAddress(address: string) { + return element(by.id('receiver')).sendKeys(address); + } + + setAmount(amount: string) { + return element(by.id('amount')).sendKeys(amount); + } + + clickSend() { + return this.getButton().click(); + } +} diff --git a/e2e/meta-sender.e2e-spec.ts b/e2e/meta-sender.e2e-spec.ts new file mode 100755 index 0000000..72eccb4 --- /dev/null +++ b/e2e/meta-sender.e2e-spec.ts @@ -0,0 +1,31 @@ +import { AppPage } from './app.po'; +import { browser } from 'protractor'; + +describe('angular-truffle-box meta-sender', () => { + let page: AppPage; + + beforeEach(() => { + page = new AppPage(); + }); + + it('should transfer money', () => { + page.navigateTo(); + page.setAddress(0); + browser.driver.sleep(100); + + expect(page.getBalance()).toBe('10000'); + + page.setAmount('50'); + page.setToAddress('0xf17f52151ebef6c7334fad080c5704d77216b732'); + page.clickSend(); + + browser.driver.sleep(1000); + page.navigateTo(); + + expect(page.getBalance()).toBe('9950'); + page.clickSelect(); + page.setAddress(1); + browser.driver.sleep(100); + expect(page.getBalance()).toBe('50'); + }); +}); diff --git a/e2e/tsconfig.e2e.json b/e2e/tsconfig.e2e.json new file mode 100755 index 0000000..1d9e5ed --- /dev/null +++ b/e2e/tsconfig.e2e.json @@ -0,0 +1,14 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "../out-tsc/e2e", + "baseUrl": "./", + "module": "commonjs", + "target": "es5", + "types": [ + "jasmine", + "jasminewd2", + "node" + ] + } +} diff --git a/karma.conf.js b/karma.conf.js new file mode 100755 index 0000000..c0f6187 --- /dev/null +++ b/karma.conf.js @@ -0,0 +1,45 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular/cli'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage-istanbul-reporter'), + require('@angular/cli/plugins/karma') + ], + client:{ + clearContext: false // leave Jasmine Spec Runner output visible in browser + }, + coverageIstanbulReporter: { + reports: [ 'html', 'lcovonly' ], + fixWebpackSourcePaths: true + }, + angularCli: { + environment: 'dev' + }, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['ChromeHeadless'], + singleRun: false, + customLaunchers: { + ChromeHeadless: { + base: 'Chrome', + flags: [ + '--headless', + '--disable-gpu', + '--no-sandbox', + // Without a remote debugging port, Google Chrome exits immediately. + '--remote-debugging-port=9222', + ], + } + } + }); +}; diff --git a/migrations/1_initial_migration.js b/migrations/1_initial_migration.js new file mode 100755 index 0000000..4d5f3f9 --- /dev/null +++ b/migrations/1_initial_migration.js @@ -0,0 +1,5 @@ +var Migrations = artifacts.require("./Migrations.sol"); + +module.exports = function(deployer) { + deployer.deploy(Migrations); +}; diff --git a/migrations/2_initial_migration.js b/migrations/2_initial_migration.js new file mode 100755 index 0000000..6186399 --- /dev/null +++ b/migrations/2_initial_migration.js @@ -0,0 +1,5 @@ +var EntryStorage = artifacts.require('./EntryStorage.sol'); + +module.exports = function(deployer) { + deployer.deploy(EntryStorage); +}; diff --git a/package.json b/package.json new file mode 100755 index 0000000..3836103 --- /dev/null +++ b/package.json @@ -0,0 +1,57 @@ +{ + "name": "angular-truffle-box", + "version": "0.0.0", + "license": "MIT", + "scripts": { + "ng": "ng", + "start": "ng serve", + "build": "ng build", + "test": "ng test --watch=false", + "lint": "ng lint", + "e2e": "ng e2e" + }, + "private": true, + "dependencies": { + "@angular/animations": "^5.2.9", + "@angular/cdk": "^5.2.5", + "@angular/common": "^5.2.9", + "@angular/compiler": "^5.2.9", + "@angular/core": "^5.2.9", + "@angular/forms": "^5.2.9", + "@angular/http": "^5.2.9", + "@angular/material": "^5.2.5", + "@angular/platform-browser": "^5.2.9", + "@angular/platform-browser-dynamic": "^5.2.9", + "@angular/platform-server": "^5.2.9", + "@angular/router": "^5.2.9", + "core-js": "^2.5.4", + "ipfs-api": "^24.0.0", + "rxjs": "^5.5.8", + "web3": "1.0.0-beta.34", + "zone.js": "^0.8.23" + }, + "devDependencies": { + "@angular-devkit/core": "^0.4.8", + "@angular/cli": "^1.7.3", + "@angular/compiler-cli": "^5.2.9", + "@angular/language-service": "^4.4.6", + "@types/jasmine": "^2.8.6", + "@types/jasminewd2": "^2.0.3", + "@types/node": "^6.0.103", + "codelyzer": "^3.2.2", + "jasmine-core": "^2.99.1", + "jasmine-spec-reporter": "^4.2.1", + "karma": "^1.7.1", + "karma-chrome-launcher": "^2.2.0", + "karma-cli": "^1.0.1", + "karma-coverage-istanbul-reporter": "^1.4.2", + "karma-jasmine": "^1.1.1", + "karma-jasmine-html-reporter": "^0.2.2", + "protractor": "^5.3.0", + "protractor-console-plugin": "^0.1.1", + "truffle-contract": "^3.0.4", + "ts-node": "^3.3.0", + "tslint": "^5.9.1", + "typescript": "~2.4.0" + } +} diff --git a/protractor.conf.js b/protractor.conf.js new file mode 100755 index 0000000..6988d19 --- /dev/null +++ b/protractor.conf.js @@ -0,0 +1,34 @@ +// Protractor configuration file, see link for more information +// https://github.com/angular/protractor/blob/master/lib/config.ts + +const { SpecReporter } = require('jasmine-spec-reporter'); + +exports.config = { + allScriptsTimeout: 11000, + specs: [ + './e2e/**/*.e2e-spec.ts' + ], + capabilities: { + 'browserName': 'chrome', + chromeOptions: { + args: [ "--headless", "--no-sandbox", "--disable-gpu", "--window-size=800,600" ] + } + }, + directConnect: true, + baseUrl: 'http://localhost:4200/', + framework: 'jasmine', + jasmineNodeOpts: { + showColors: true, + defaultTimeoutInterval: 30000, + print: function() {} + }, + plugins: [{ + package: 'protractor-console-plugin' + }], + onPrepare() { + require('ts-node').register({ + project: 'e2e/tsconfig.e2e.json' + }); + jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); + } +}; diff --git a/src/app/app.component.css b/src/app/app.component.css new file mode 100755 index 0000000..e69de29 diff --git a/src/app/app.component.html b/src/app/app.component.html new file mode 100755 index 0000000..df828ce --- /dev/null +++ b/src/app/app.component.html @@ -0,0 +1,2 @@ +Angular Truffle Box + diff --git a/src/app/app.component.ts b/src/app/app.component.ts new file mode 100755 index 0000000..ff63e05 --- /dev/null +++ b/src/app/app.component.ts @@ -0,0 +1,10 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-root', + templateUrl: './app.component.html', + styleUrls: ['./app.component.css'] +}) +export class AppComponent { + title = 'app works!'; +} diff --git a/src/app/app.module.ts b/src/app/app.module.ts new file mode 100755 index 0000000..3a5e76f --- /dev/null +++ b/src/app/app.module.ts @@ -0,0 +1,34 @@ +import { BrowserModule } from '@angular/platform-browser'; +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { HttpModule } from '@angular/http'; + +import { AppComponent } from './app.component'; +import { CommonModule } from '@angular/common'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { + MatButtonModule, + MatCardModule, + MatFormFieldModule, + MatInputModule, + MatToolbarModule +} from '@angular/material'; + +@NgModule({ + declarations: [AppComponent], + imports: [ + BrowserAnimationsModule, + CommonModule, + MatButtonModule, + MatCardModule, + MatFormFieldModule, + MatInputModule, + MatToolbarModule, + BrowserModule, + FormsModule, + HttpModule + ], + providers: [], + bootstrap: [AppComponent] +}) +export class AppModule {} diff --git a/src/app/meta/meta-sender/meta-sender.component.css b/src/app/meta/meta-sender/meta-sender.component.css new file mode 100755 index 0000000..e860e7a --- /dev/null +++ b/src/app/meta/meta-sender/meta-sender.component.css @@ -0,0 +1,4 @@ +.address-field { + width: 400px; +} + diff --git a/src/app/meta/meta-sender/meta-sender.component.html b/src/app/meta/meta-sender/meta-sender.component.html new file mode 100755 index 0000000..aa5ae88 --- /dev/null +++ b/src/app/meta/meta-sender/meta-sender.component.html @@ -0,0 +1,27 @@ + + Balance + + + + {{account}} + + + +

You have {{model.balance}} META

+
+
+ + + Send MetaCoin + + + + + + + + + + + + diff --git a/src/app/meta/meta-sender/meta-sender.component.ts b/src/app/meta/meta-sender/meta-sender.component.ts new file mode 100755 index 0000000..321d00f --- /dev/null +++ b/src/app/meta/meta-sender/meta-sender.component.ts @@ -0,0 +1,114 @@ +import { Component, OnInit } from '@angular/core'; +import { Web3Service } from '../../util/web3.service'; +import { MatSnackBar } from '@angular/material'; + +declare let require: any; +const metacoin_artifacts = require('../../../../build/contracts/MetaCoin.json'); + +@Component({ + selector: 'app-meta-sender', + templateUrl: './meta-sender.component.html', + styleUrls: ['./meta-sender.component.css'] +}) +export class MetaSenderComponent implements OnInit { + accounts: string[]; + MetaCoin: any; + + model = { + amount: 5, + receiver: '', + balance: 0, + account: '' + }; + + status = ''; + + constructor( + private web3Service: Web3Service, + private matSnackBar: MatSnackBar + ) { + console.log('Constructor: ' + web3Service); + } + + ngOnInit(): void { + console.log('OnInit: ' + this.web3Service); + console.log(this); + this.watchAccount(); + this.web3Service + .artifactsToContract(metacoin_artifacts) + .then(MetaCoinAbstraction => { + this.MetaCoin = MetaCoinAbstraction; + }); + } + + watchAccount() { + this.web3Service.accountsObservable.subscribe(accounts => { + this.accounts = accounts; + this.model.account = accounts[0]; + this.refreshBalance(); + }); + } + + setStatus(status) { + this.matSnackBar.open(status, null, { duration: 3000 }); + } + + async sendCoin() { + if (!this.MetaCoin) { + this.setStatus('Metacoin is not loaded, unable to send transaction'); + return; + } + + const amount = this.model.amount; + const receiver = this.model.receiver; + + console.log('Sending coins' + amount + ' to ' + receiver); + + this.setStatus('Initiating transaction... (please wait)'); + try { + const deployedMetaCoin = await this.MetaCoin.deployed(); + const transaction = await deployedMetaCoin.sendCoin.sendTransaction( + receiver, + amount, + { from: this.model.account } + ); + + if (!transaction) { + this.setStatus('Transaction failed!'); + } else { + this.setStatus('Transaction complete!'); + } + } catch (e) { + console.log(e); + this.setStatus('Error sending coin; see log.'); + } + } + + async refreshBalance() { + console.log('Refreshing balance'); + + try { + const deployedMetaCoin = await this.MetaCoin.deployed(); + console.log(deployedMetaCoin); + console.log('Account', this.model.account); + const metaCoinBalance = await deployedMetaCoin.getBalance.call( + this.model.account + ); + console.log('Found balance: ' + metaCoinBalance); + this.model.balance = metaCoinBalance; + } catch (e) { + console.log(e); + this.setStatus('Error getting balance; see log.'); + } + } + + setAmount(e) { + console.log('Setting amount: ' + e.target.value); + this.model.amount = e.target.value; + } + + setReceiver(e) { + console.log('Setting receiver: ' + e.target.value); + this.model.receiver = e.target.value; + } +} diff --git a/src/app/meta/meta.module.ts b/src/app/meta/meta.module.ts new file mode 100755 index 0000000..bcf74a6 --- /dev/null +++ b/src/app/meta/meta.module.ts @@ -0,0 +1,34 @@ +import {NgModule} from '@angular/core'; +import {CommonModule} from '@angular/common'; +import {MetaSenderComponent} from './meta-sender/meta-sender.component'; +import {UtilModule} from '../util/util.module'; +import {RouterModule} from '@angular/router'; +import { + MatButtonModule, + MatCardModule, + MatFormFieldModule, + MatInputModule, + MatOptionModule, + MatSelectModule, MatSnackBarModule +} from '@angular/material'; +import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; + +@NgModule({ + imports: [ + BrowserAnimationsModule, + CommonModule, + MatButtonModule, + MatCardModule, + MatFormFieldModule, + MatInputModule, + MatOptionModule, + MatSelectModule, + MatSnackBarModule, + RouterModule, + UtilModule + ], + declarations: [MetaSenderComponent], + exports: [MetaSenderComponent] +}) +export class MetaModule { +} diff --git a/src/app/util/util.module.ts b/src/app/util/util.module.ts new file mode 100755 index 0000000..b37aa58 --- /dev/null +++ b/src/app/util/util.module.ts @@ -0,0 +1,10 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { Web3Service } from './web3.service'; + +@NgModule({ + imports: [CommonModule], + providers: [Web3Service], + declarations: [] +}) +export class UtilModule {} diff --git a/src/app/util/web3.service.spec.ts b/src/app/util/web3.service.spec.ts new file mode 100755 index 0000000..f5cfcf4 --- /dev/null +++ b/src/app/util/web3.service.spec.ts @@ -0,0 +1,40 @@ +import {TestBed, inject} from '@angular/core/testing'; +import Web3 from 'web3'; + +import {Web3Service} from './web3.service'; + +import metacoin_artifacts from '../../../build/contracts/MetaCoin.json'; + +declare let window: any; + +describe('Web3Service', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [Web3Service] + }); + }); + + it('should be created', inject([Web3Service], (service: Web3Service) => { + expect(service).toBeTruthy(); + })); + + it('should inject a default web3 on a contract', inject([Web3Service], (service: Web3Service) => { + service.bootstrapWeb3(); + + return service.artifactsToContract(metacoin_artifacts).then((abstraction) => { + expect(abstraction.currentProvider.host).toBe('http://localhost:8545'); + }); + })); + + it('should inject a the window web3 on a contract', inject([Web3Service], (service: Web3Service) => { + window.web3 = { + currentProvider: new Web3.providers.HttpProvider('http://localhost:1337') + }; + + service.bootstrapWeb3(); + + return service.artifactsToContract(metacoin_artifacts).then((abstraction) => { + expect(abstraction.currentProvider.host).toBe('http://localhost:1337'); + }); + })); +}); diff --git a/src/app/util/web3.service.ts b/src/app/util/web3.service.ts new file mode 100755 index 0000000..6fcd0ee --- /dev/null +++ b/src/app/util/web3.service.ts @@ -0,0 +1,78 @@ +import {Injectable} from '@angular/core'; +import * as contract from 'truffle-contract'; +import {Subject} from 'rxjs/Rx'; +declare let require: any; +const Web3 = require('web3'); + + +declare let window: any; + +@Injectable() +export class Web3Service { + private web3: any; + private accounts: string[]; + public ready = false; + public MetaCoin: any; + public accountsObservable = new Subject(); + + constructor() { + window.addEventListener('load', (event) => { + this.bootstrapWeb3(); + }); + } + + public bootstrapWeb3() { + // Checking if Web3 has been injected by the browser (Mist/MetaMask) + if (typeof window.web3 !== 'undefined') { + // Use Mist/MetaMask's provider + this.web3 = new Web3(window.web3.currentProvider); + } else { + console.log('No web3? You should consider trying MetaMask!'); + + // Hack to provide backwards compatibility for Truffle, which uses web3js 0.20.x + Web3.providers.HttpProvider.prototype.sendAsync = Web3.providers.HttpProvider.prototype.send; + // fallback - use your fallback strategy (local node / hosted node + in-dapp id mgmt / fail) + this.web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545')); + } + + setInterval(() => this.refreshAccounts(), 100); + } + + public async artifactsToContract(artifacts) { + if (!this.web3) { + const delay = new Promise(resolve => setTimeout(resolve, 100)); + await delay; + return await this.artifactsToContract(artifacts); + } + + const contractAbstraction = contract(artifacts); + contractAbstraction.setProvider(this.web3.currentProvider); + return contractAbstraction; + + } + + private refreshAccounts() { + this.web3.eth.getAccounts((err, accs) => { + console.log('Refreshing accounts'); + if (err != null) { + console.warn('There was an error fetching your accounts.'); + return; + } + + // Get the initial account balance so it can be displayed. + if (accs.length === 0) { + console.warn('Couldn\'t get any accounts! Make sure your Ethereum client is configured correctly.'); + return; + } + + if (!this.accounts || this.accounts.length !== accs.length || this.accounts[0] !== accs[0]) { + console.log('Observed new accounts'); + + this.accountsObservable.next(accs); + this.accounts = accs; + } + + this.ready = true; + }); + } +} diff --git a/src/assets/.gitkeep b/src/assets/.gitkeep new file mode 100755 index 0000000..e69de29 diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts new file mode 100755 index 0000000..3612073 --- /dev/null +++ b/src/environments/environment.prod.ts @@ -0,0 +1,3 @@ +export const environment = { + production: true +}; diff --git a/src/environments/environment.ts b/src/environments/environment.ts new file mode 100755 index 0000000..b7f639a --- /dev/null +++ b/src/environments/environment.ts @@ -0,0 +1,8 @@ +// The file contents for the current environment will overwrite these during build. +// The build system defaults to the dev environment which uses `environment.ts`, but if you do +// `ng build --env=prod` then `environment.prod.ts` will be used instead. +// The list of which env maps to which file can be found in `.angular-cli.json`. + +export const environment = { + production: false +}; diff --git a/src/favicon.ico b/src/favicon.ico new file mode 100755 index 0000000000000000000000000000000000000000..8081c7ceaf2be08bf59010158c586170d9d2d517 GIT binary patch literal 5430 zcmc(je{54#6vvCoAI3i*G5%$U7!sA3wtMZ$fH6V9C`=eXGJb@R1%(I_{vnZtpD{6n z5Pl{DmxzBDbrB>}`90e12m8T*36WoeDLA&SD_hw{H^wM!cl_RWcVA!I+x87ee975; z@4kD^=bYPn&pmG@(+JZ`rqQEKxW<}RzhW}I!|ulN=fmjVi@x{p$cC`)5$a!)X&U+blKNvN5tg=uLvuLnuqRM;Yc*swiexsoh#XPNu{9F#c`G zQLe{yWA(Y6(;>y|-efAy11k<09(@Oo1B2@0`PtZSkqK&${ zgEY}`W@t{%?9u5rF?}Y7OL{338l*JY#P!%MVQY@oqnItpZ}?s z!r?*kwuR{A@jg2Chlf0^{q*>8n5Ir~YWf*wmsh7B5&EpHfd5@xVaj&gqsdui^spyL zB|kUoblGoO7G(MuKTfa9?pGH0@QP^b#!lM1yHWLh*2iq#`C1TdrnO-d#?Oh@XV2HK zKA{`eo{--^K&MW66Lgsktfvn#cCAc*(}qsfhrvOjMGLE?`dHVipu1J3Kgr%g?cNa8 z)pkmC8DGH~fG+dlrp(5^-QBeEvkOvv#q7MBVLtm2oD^$lJZx--_=K&Ttd=-krx(Bb zcEoKJda@S!%%@`P-##$>*u%T*mh+QjV@)Qa=Mk1?#zLk+M4tIt%}wagT{5J%!tXAE;r{@=bb%nNVxvI+C+$t?!VJ@0d@HIyMJTI{vEw0Ul ze(ha!e&qANbTL1ZneNl45t=#Ot??C0MHjjgY8%*mGisN|S6%g3;Hlx#fMNcL<87MW zZ>6moo1YD?P!fJ#Jb(4)_cc50X5n0KoDYfdPoL^iV`k&o{LPyaoqMqk92wVM#_O0l z09$(A-D+gVIlq4TA&{1T@BsUH`Bm=r#l$Z51J-U&F32+hfUP-iLo=jg7Xmy+WLq6_tWv&`wDlz#`&)Jp~iQf zZP)tu>}pIIJKuw+$&t}GQuqMd%Z>0?t%&BM&Wo^4P^Y z)c6h^f2R>X8*}q|bblAF?@;%?2>$y+cMQbN{X$)^R>vtNq_5AB|0N5U*d^T?X9{xQnJYeU{ zoZL#obI;~Pp95f1`%X3D$Mh*4^?O?IT~7HqlWguezmg?Ybq|7>qQ(@pPHbE9V?f|( z+0xo!#m@Np9PljsyxBY-UA*{U*la#8Wz2sO|48_-5t8%_!n?S$zlGe+NA%?vmxjS- zHE5O3ZarU=X}$7>;Okp(UWXJxI%G_J-@IH;%5#Rt$(WUX?6*Ux!IRd$dLP6+SmPn= z8zjm4jGjN772R{FGkXwcNv8GBcZI#@Y2m{RNF_w8(Z%^A*!bS*!}s6sh*NnURytky humW;*g7R+&|Ledvc- + + + + AngularTruffleBox + + + + + + + + + diff --git a/src/main.ts b/src/main.ts new file mode 100755 index 0000000..91ec6da --- /dev/null +++ b/src/main.ts @@ -0,0 +1,12 @@ +import { enableProdMode } from '@angular/core'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; + +import { AppModule } from './app/app.module'; +import { environment } from './environments/environment'; + +if (environment.production) { + enableProdMode(); +} + +platformBrowserDynamic().bootstrapModule(AppModule) + .catch(err => console.log(err)); diff --git a/src/polyfills.ts b/src/polyfills.ts new file mode 100755 index 0000000..98fd841 --- /dev/null +++ b/src/polyfills.ts @@ -0,0 +1,72 @@ +/** + * This file includes polyfills needed by Angular and is loaded before the app. + * You can add your own extra polyfills to this file. + * + * This file is divided into 2 sections: + * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. + * 2. Application imports. Files imported after ZoneJS that should be loaded before your main + * file. + * + * The current setup is for so-called "evergreen" browsers; the last versions of browsers that + * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), + * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. + * + * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html + */ + +/*************************************************************************************************** + * BROWSER POLYFILLS + */ + +/** IE9, IE10 and IE11 requires all of the following polyfills. **/ +import 'core-js/es6/symbol'; +import 'core-js/es6/object'; +import 'core-js/es6/function'; +import 'core-js/es6/parse-int'; +import 'core-js/es6/parse-float'; +import 'core-js/es6/number'; +import 'core-js/es6/math'; +import 'core-js/es6/string'; +import 'core-js/es6/date'; +import 'core-js/es6/array'; +import 'core-js/es6/regexp'; +import 'core-js/es6/map'; +import 'core-js/es6/weak-map'; +import 'core-js/es6/set'; + +/** IE10 and IE11 requires the following for NgClass support on SVG elements */ +// import 'classlist.js'; // Run `npm install --save classlist.js`. + +/** Evergreen browsers require these. **/ +import 'core-js/es6/reflect'; +import 'core-js/es7/reflect'; + + +/** + * Required to support Web Animations `@angular/animation`. + * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation + **/ +// import 'web-animations-js'; // Run `npm install --save web-animations-js`. + + + +/*************************************************************************************************** + * Zone JS is required by Angular itself. + */ +import 'zone.js/dist/zone'; // Included with Angular CLI. + + + +/*************************************************************************************************** + * APPLICATION IMPORTS + */ + +/** + * Date, currency, decimal and percent pipes. + * Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10 + */ +// import 'intl'; // Run `npm install --save intl`. +/** + * Need to import at least one locale-data with intl. + */ +// import 'intl/locale-data/jsonp/en'; diff --git a/src/styles.css b/src/styles.css new file mode 100755 index 0000000..5f69d4f --- /dev/null +++ b/src/styles.css @@ -0,0 +1,2 @@ +/* You can add global styles to this file, and also import other style files */ +@import "~@angular/material/prebuilt-themes/indigo-pink.css"; diff --git a/src/test.ts b/src/test.ts new file mode 100755 index 0000000..cd612ee --- /dev/null +++ b/src/test.ts @@ -0,0 +1,32 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import 'zone.js/dist/long-stack-trace-zone'; +import 'zone.js/dist/proxy.js'; +import 'zone.js/dist/sync-test'; +import 'zone.js/dist/jasmine-patch'; +import 'zone.js/dist/async-test'; +import 'zone.js/dist/fake-async-test'; +import { getTestBed } from '@angular/core/testing'; +import { + BrowserDynamicTestingModule, + platformBrowserDynamicTesting +} from '@angular/platform-browser-dynamic/testing'; + +// Unfortunately there's no typing for the `__karma__` variable. Just declare it as any. +declare const __karma__: any; +declare const require: any; + +// Prevent Karma from running prematurely. +__karma__.loaded = function () {}; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment( + BrowserDynamicTestingModule, + platformBrowserDynamicTesting() +); +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().map(context); +// Finally, start Karma to run the tests. +__karma__.start(); diff --git a/src/tsconfig.app.json b/src/tsconfig.app.json new file mode 100755 index 0000000..39ba8db --- /dev/null +++ b/src/tsconfig.app.json @@ -0,0 +1,13 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "../out-tsc/app", + "baseUrl": "./", + "module": "es2015", + "types": [] + }, + "exclude": [ + "test.ts", + "**/*.spec.ts" + ] +} diff --git a/src/tsconfig.spec.json b/src/tsconfig.spec.json new file mode 100755 index 0000000..c697928 --- /dev/null +++ b/src/tsconfig.spec.json @@ -0,0 +1,20 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "../out-tsc/spec", + "baseUrl": "./", + "module": "commonjs", + "target": "es2016", + "types": [ + "jasmine", + "node" + ] + }, + "files": [ + "test.ts" + ], + "include": [ + "**/*.spec.ts", + "**/*.d.ts" + ] +} diff --git a/src/typings.d.ts b/src/typings.d.ts new file mode 100755 index 0000000..1f6c9de --- /dev/null +++ b/src/typings.d.ts @@ -0,0 +1,10 @@ +/* SystemJS module definition */ +declare var module: NodeModule; +interface NodeModule { + id: string; +} + +declare module "*.json" { + const value: any; + export default value; +} diff --git a/truffle-box.json b/truffle-box.json new file mode 100755 index 0000000..63ec58a --- /dev/null +++ b/truffle-box.json @@ -0,0 +1,18 @@ +{ + "ignore": [ + "README.md", + ".gitignore" + ], + "commands": { + "Compile": "truffle compile", + "Migrate": "truffle migrate", + "Test contracts": "truffle test", + "Test Angular components": "ng test", + "Test end-to-end": "ng e2e", + "Run dev server": "ng serve", + "Build for production": "ng build" + }, + "hooks": { + "post-unpack": "npm install" + } +} diff --git a/truffle-config.js b/truffle-config.js new file mode 100755 index 0000000..0855df1 --- /dev/null +++ b/truffle-config.js @@ -0,0 +1,18 @@ +/* + * NB: since truffle-hdwallet-provider 0.0.5 you must wrap HDWallet providers in a + * function when declaring them. Failure to do so will cause commands to hang. ex: + * ``` + * mainnet: { + * provider: function() { + * return new HDWalletProvider(mnemonic, 'https://mainnet.infura.io/') + * }, + * network_id: '1', + * gas: 4500000, + * gasPrice: 10000000000, + * }, + */ + +module.exports = { + // See + // to customize your Truffle configuration! +}; diff --git a/truffle.js b/truffle.js new file mode 100755 index 0000000..7a898c9 --- /dev/null +++ b/truffle.js @@ -0,0 +1,25 @@ +/* + * NB: since truffle-hdwallet-provider 0.0.5 you must wrap HDWallet providers in a + * function when declaring them. Failure to do so will cause commands to hang. ex: + * ``` + * mainnet: { + * provider: function() { + * return new HDWalletProvider(mnemonic, 'https://mainnet.infura.io/') + * }, + * network_id: '1', + * gas: 4500000, + * gasPrice: 10000000000, + * }, + */ + +module.exports = { + // See + // to customize your Truffle configuration! + networks: { + development: { + host: '127.0.0.1', + port: '8545', + network_id: '*' + } + } +}; diff --git a/tsconfig.json b/tsconfig.json new file mode 100755 index 0000000..012512e --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compileOnSave": false, + "compilerOptions": { + "outDir": "./dist/out-tsc", + "sourceMap": true, + "declaration": false, + "moduleResolution": "node", + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "allowSyntheticDefaultImports": true, + "target": "es5", + "typeRoots": [ + "node_modules/@types" + ], + "lib": [ + "es2017", + "dom" + ] + } +} diff --git a/tslint.json b/tslint.json new file mode 100755 index 0000000..0db5751 --- /dev/null +++ b/tslint.json @@ -0,0 +1,142 @@ +{ + "rulesDirectory": [ + "node_modules/codelyzer" + ], + "rules": { + "arrow-return-shorthand": true, + "callable-types": true, + "class-name": true, + "comment-format": [ + true, + "check-space" + ], + "curly": true, + "eofline": true, + "forin": true, + "import-blacklist": [ + true, + "rxjs" + ], + "import-spacing": true, + "indent": [ + true, + "spaces" + ], + "interface-over-type-literal": true, + "label-position": true, + "max-line-length": [ + true, + 140 + ], + "member-access": false, + "member-ordering": [ + true, + { + "order": [ + "static-field", + "instance-field", + "static-method", + "instance-method" + ] + } + ], + "no-arg": true, + "no-bitwise": true, + "no-console": [ + true, + "debug", + "info", + "time", + "timeEnd", + "trace" + ], + "no-construct": true, + "no-debugger": true, + "no-duplicate-super": true, + "no-empty": false, + "no-empty-interface": true, + "no-eval": true, + "no-inferrable-types": [ + true, + "ignore-params" + ], + "no-misused-new": true, + "no-non-null-assertion": true, + "no-shadowed-variable": true, + "no-string-literal": false, + "no-string-throw": true, + "no-switch-case-fall-through": true, + "no-trailing-whitespace": true, + "no-unnecessary-initializer": true, + "no-unused-expression": true, + "no-use-before-declare": true, + "no-var-keyword": true, + "object-literal-sort-keys": false, + "one-line": [ + true, + "check-open-brace", + "check-catch", + "check-else", + "check-whitespace" + ], + "prefer-const": true, + "quotemark": [ + true, + "single" + ], + "radix": true, + "semicolon": [ + true, + "always" + ], + "triple-equals": [ + true, + "allow-null-check" + ], + "typedef-whitespace": [ + true, + { + "call-signature": "nospace", + "index-signature": "nospace", + "parameter": "nospace", + "property-declaration": "nospace", + "variable-declaration": "nospace" + } + ], + "typeof-compare": true, + "unified-signatures": true, + "variable-name": false, + "whitespace": [ + true, + "check-branch", + "check-decl", + "check-operator", + "check-separator", + "check-type" + ], + "directive-selector": [ + true, + "attribute", + "app", + "camelCase" + ], + "component-selector": [ + true, + "element", + "app", + "kebab-case" + ], + "use-input-property-decorator": true, + "use-output-property-decorator": true, + "use-host-property-decorator": true, + "no-input-rename": true, + "no-output-rename": true, + "use-life-cycle-interface": true, + "use-pipe-transform-interface": true, + "component-class-suffix": true, + "directive-class-suffix": true, + "no-access-missing-member": true, + "templates-use-public": true, + "invoke-injectable": true + } +}