Using Truffle, Web3, Solidity, Firebase, and Vue/Vuex/Vuetify

In my previous tutorial, I went through building a fully decentralized user profile application on Ethereum and IPFS. That’s great if you’re looking to build something like Peepeth (for example).

For other type of projects, a fully decentralized architecture may not be the best choice. On this tutorial, we’ll build a similar project yet sacrifice some degree of decentralization in order to leverage Google Firebase. You could use other services too (e.g.: Amazon Lambda, S3, …), or go further and add your own custom backend — the pattern is the same as long as said backend doesn’t need to talk to the blockchain.

In addition, we’ll also use Vue, Vuex, and Vuetify for the front-end instead of the Bootstrap 4 and jQuery combo we used before. This will bring some variety to our learning journey while providing a great framework to build a more sophisticated User Interface at a later time.

Show me the Code

Here’s the full code for part 1 of this tutorial:

https://github.com/sbrocher/truffle-vuex-firebase/tree/part-1

Getting Started

While there’re a few community Truffle boxes that already include Vue, we’ll build our project from the official webpack box so we can configure things more granularly and learn a bit more along the way.

mkdir truffle-vuex-firebase

cd truffle-vuex-firebase

truffle unbox webpack

npm install

Let’s remove some unneeded files:

rm contracts/MetaCoin.sol contracts/ConvertLib.sol

rm migrations/2_deploy_contracts.js

rm test/metacoin.js test/TestMetacoin.sol

rm box-img-lg.png box-img-sm.png

Then open app/javascripts/app.js and replace its contents with the following for the time being:

// Import the page’s CSS. Webpack will know what to do with it.

import “../stylesheets/app.css”; // Import libraries we need.

import { default as Web3 } from ‘web3’;

import { default as contract } from ‘truffle-contract’; window.App = {

start: function() {

var self = this; console.log(‘App started!’);

}, }; window.addEventListener(‘load’, function() {

// Checking if Web3 has been injected by the browser (Mist/MetaMask)

if (typeof web3 !== ‘undefined’) {

console.warn(“Using web3 detected from external source.”);

// Use Mist/MetaMask’s provider

window.web3 = new Web3(web3.currentProvider);

} else {

console.warn(“No web3 detected.”);

} App.start();

});

Let’s also update app/index.html to a simpler skeleton:



<html>

<head>

<title>Ethereum Firebase User Profile</title>

<link href=’

<script src=”./app.js”></script>

</head>

<body>

<h1>Ethereum Firebase User Profile</h1>

<h2>Example Dapp</h2>

</body>

</html> Ethereum Firebase User Profile https://fonts.googleapis.com/css?family=Open+Sans:400,700' rel=’stylesheet’ type=’text/css’> Ethereum Firebase User Profile Example Dapp

Now run:

npm run dev

Open your browser. You should see something like this:

Then check that you have the ‘App Started!’ message on your JavaScript console. We now have a stripped-down Truffle Webpack project skeleton.

Adding Vue and Vuetify

In this section we’ll get Vue and Vuetify added to our project. The VuetifyJS quick start documentation shows an easy way to add these to an existing project. That technique, however, is not enough for our purposes as we want a more sophisticated configuration. So, we’ll use the command line tools to create a separate project with their full webpack template, and then inspect it and merge the important pieces into our existing project.

First, let’s install the VueJS command line tools:

npm install -g @vue/cli

npm install -g @vue/cli-init

Now, run the following commands to create the skeleton:

mkdir ../vue-skeleton

cd ../vue-skeleton

vue init vuetifyjs/webpack . ? Generate project in current directory? Yes

? Project name vue-skeleton

? Project description A Vue.js project

? Author Seb Brocher <seb@noctual.com>

? Vue build runtime

? Install vue-router? Yes

? Use ESLint to lint your code? Yes

? Pick an ESLint preset Standard

? Set up unit tests No

? Setup e2e tests with Nightwatch? No

? Use a-la-carte components? No

? Use custom theme? No

? Should we run `npm install` for you after the project has been created? (recommended) npm

Now that we have the skeleton ready, we can inspect it and copy over the important pieces. Open up your package.json file and replace your current “scripts” block with these five blocks from vue-skeleton:

"scripts": {

"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",

"start": "npm run dev",

"lint": "eslint --ext .js,.vue src",

"build": "node build/build.js"

},

"dependencies": {

"vue": "^2.5.2",

"vue-router": "^3.0.1",

"vuetify": "^1.0.0"

},

"engines": {

"node": ">= 6.0.0",

"npm": ">= 3.0.0"

},

"browserslist": [

"> 1%",

"last 2 versions",

"not ie <= 8"

]

And replace your devDependencies block with this:

"devDependencies": {

"autoprefixer": "^7.1.2",

"babel-core": "^6.22.1",

"babel-eslint": "^7.1.1",

"babel-helper-vue-jsx-merge-props": "^2.0.3",

"babel-loader": "^7.1.1",

"babel-plugin-syntax-jsx": "^6.18.0",

"babel-plugin-transform-runtime": "^6.22.0",

"babel-plugin-transform-vue-jsx": "^3.5.0",

"babel-preset-env": "^1.3.2",

"babel-preset-stage-2": "^6.22.0",

"chalk": "^2.0.1",

"copy-webpack-plugin": "^4.0.1",

"css-loader": "^0.28.0",

"eslint": "^3.19.0",

"eslint-config-standard": "^10.2.1",

"eslint-friendly-formatter": "^3.0.0",

"eslint-loader": "^1.7.1",

"eslint-plugin-html": "^3.0.0",

"eslint-plugin-import": "^2.7.0",

"eslint-plugin-node": "^5.2.0",

"eslint-plugin-promise": "^3.4.0",

"eslint-plugin-standard": "^3.0.1",

"extract-text-webpack-plugin": "^3.0.0",

"file-loader": "^1.1.4",

"friendly-errors-webpack-plugin": "^1.6.1",

"html-webpack-plugin": "^2.30.1",

"node-notifier": "^5.1.2",

"optimize-css-assets-webpack-plugin": "^3.2.0",

"ora": "^1.2.0",

"portfinder": "^1.0.13",

"postcss-import": "^11.0.0",

"postcss-loader": "^2.0.8",

"postcss-url": "^7.2.1",

"rimraf": "^2.6.0",

"semver": "^5.3.0",

"shelljs": "^0.7.6",

"uglifyjs-webpack-plugin": "^1.1.1",

"url-loader": "^0.5.8",

"vue-loader": "^13.3.0",

"vue-style-loader": "^3.0.1",

"vue-template-compiler": "^2.5.2",

"webpack": "^3.6.0",

"webpack-bundle-analyzer": "^2.9.0",

"webpack-dev-server": "^2.9.1",

"webpack-merge": "^4.1.0",

"json-loader": "^0.5.4",

"truffle-contract": "^1.1.11",

"web3": "^0.20.0"

},

Then run the following commands:

cp -r ../vue-skeleton/build .

cp -r ../vue-skeleton/config .

cp -r ../vue-skeleton/src .

cp -r ../vue-skeleton/static .

cp ../vue-skeleton/.babelrc .

cp ../vue-skeleton/.editorconfig .

cat ../vue-skeleton/.eslintignore >> .eslintignore

rm .eslintrc

cp ../vue-skeleton/.eslintrc.js .

cp ../vue-skeleton/.gitignore .

cp ../vue-skeleton/.postcssrc.js .

cp ../vue-skeleton/index.html .

Now we have everything available within our project, but we’re not done merging it. To start with, there’re some conflicts going on:

The truffle box uses the /build folder as the output of the webpack build, contract json files, etc. The vue skeleton uses it for build scripts, and uses /dist folder instead for the webpack build.

The truffle box puts the app source code in the /app folder. The vue skeleton puts it in the /src folder.

Let’s solve the issues above and complete merging the whole enchilada:

We’ll keep Truffle’s default for the output and use /build folder (instead of /dist). And we’ll put the build scripts into a build-scripts folder.

1. mv build build-scripts

2. edit .eslintignore file and: change '/build/' to /build-scripts/

remove '/dist/' line 3. edit package.json file and change both lines in the scripts block to use "build-scripts" folder: "dev": "webpack-dev-server --inline --progress --config build-scripts/webpack.dev.conf.js", "build": "node build-scripts/build.js" 4. add this line to your build-scripts/webpack.base.conf.js file (within rules block): { test: /\.json$/, use: 'json-loader' }, 5. rm /webpack.config.js 6. open .gitignore, remove the /dist/ line and add instead: /build/static

/build/index.html 7. edit config/index.js file and change both lines to use build/ instead of dist/: build: {

// Template for index.html

index: path.resolve(__dirname, '../build/index.html'), // Paths

assetsRoot: path.resolve(__dirname, '../build'),

...

We’ll keep the Vue skeleton default for the app code and use /src for it (instead of /app)

1. open src/main.js and add these lines: import { default as Web3 } from 'web3'

// eslint-disable-next-line

import { default as contract } from 'truffle-contract' window.addEventListener('load', () => {

// Checking if Web3 has been injected by the browser (Mist/MetaMask)

if (typeof web3 !== 'undefined') {

console.warn('Using web3 detected from external source.')

// Use Mist/MetaMask's provider

// eslint-disable-next-line

window.web3 = new Web3(web3.currentProvider)

// eslint-disable-next-line

web3.eth.getAccounts(function(err, accs) {

if (err != null) {

console.warn('There was an error fetching your accounts.')

} else {

console.log('Got the accounts', accs)

}

})

} else {

console.warn('No web3 detected.')

}

}) 2. remove the app/ folder and its contents

Note that we’ll move some of the code above out of the main.js file later on, but this works for now.

Go ahead and run ‘npm install’, then start your dev server with ‘npm run dev’. If all goes well, your browser should show something like this:

At this time, it’d be a good idea to make sure web3 is also working properly. Go ahead and open Ganache, and have MetaMask connect to it (check out my first tutorial for more details on this). Refresh and check your JavaScript console: you should see your Ethereum address displayed.

Let’s also check that Truffle is able to compile our contracts: run ‘truffle compile’ and you should see a successful output (plus a new /build/contracts/Migrations.json file).

Finally, let’s check that the production build also works. Go ahead and run ‘npm run build’. Webpack should show a “building for production…” message and, after a little while, produce a minified build/index.html file that loads minified css, js, and assets from the build/static folder. These files, together with the compiled/migrated contract json files are all you need to deploy your application to production.

Adding Vuex

With your dev server stopped, install vuex and create the folder we’ll use for the store:

npm install --save vuex

mkdir src/store

Then create your initial store file at src/store/index.js with the following content:

import Vue from 'vue'

import Vuex from 'vuex' Vue.use(Vuex) export const store = new Vuex.Store({

state: {

appTitle: 'Ethereum Firebase Example App'

},

mutations: {},

actions: {},

getters: {}

})

The Getting Started guide from Vuex can come in handy at this time if you’re new to it. Now let’s add this to our src/main.js file:

import { store } from './store'

...

/* eslint-disable no-new */

new Vue({

el: '#app',

router,

store,

render: h => h(App)

})

Finally, let’s make sure it works by using the appTitle variable we defined in the store from the src/App.vue component:

<script>

export default {

data () {

return {

clipped: false,

drawer: true,

fixed: false,

items: [{

icon: 'bubble_chart',

title: 'Inspire'

}],

miniVariant: false,

right: true,

rightDrawer: false,

title: this.$store.state.appTitle

}

},

name: 'App'

}

</script>

On your browser, the navbar should now show the new app title ‘Ethereum Firebase Example App’.

Tip: install the Vue.js chrome extension to simplify your Vue development experience. Here’s a quick screenshot showing how you can inspect the Vuex store we just setup:

Adding Firebase

If you got all the way here, congrats, this is really great progress! We’ll soon get to the most fun part of implementing some features and their corresponding UX. But, before we do that, let’s add pour some 🔥 base to our stack.

Stop your dev server and add the package:

npm install --save firebase

Next, open src.main.js, include, and initialize Firebase:

import firebase from 'firebase' firebase.initializeApp({

apiKey: 'YOUR_API_KEY',

authDomain: 'YOUR_AUTH_DOMAIN',

databaseURL: 'YOUR_DATABASE_URL',

projectId: 'YOUR_PROJECT_ID'

})

To get your credentials for above, https://firebase.google.com/:

Click on ‘Get Started’, click on ‘Add Project’, enter a ‘Project Name’, select a Country, and click ‘Create Project’. You should then see something like this:

Click on ‘Add Firebase to your web app’, and copy/paste the config hash portion only into your initializeApp call above, making sure to replace any double quotes to single quotes so our eslint config doesn’t bring up an error. Start your dev server with ‘npm run dev’ to make sure it all works fine.

That’s it! We’ve got the full stack configured and we’ll look into building some features on Part 2 of this tutorial. Before we wrap up though, let’s have some fun and deploy our app to production!

Hosting your app with Firebase

In addition to providing a real-time database, authentication services, and many other features, Firebase can actually host your app, too. It’s really easy, fast, and convenient:

npm install -g firebase-tools

firebase login (we'll take you through a quick login / oauth through your browser)

firebase init (you can select Hosting only for now, then select the project you created above, select build for your public directory, yes to single-page app, and No to override build/index.html.

Now let’s build and deploy the app:

npm run build

firebase deploy

That’s it! The output from ‘firebase deploy’ should show a URL to your live app. Mine is here. Open it on your browser and check it out! Notice how the html, javascript, and css is all minimized. Beautiful!

Edit: Here’s the link to part 2, enjoy!

Update: If you enjoyed this tutorial, checkout my post on “Pre-Launching CryptoArte”, the project leverages and uses a lot of the techniques covered here!