---
# A Minimal Smart Contract Development Boilerplate
[Truffle](https://github.com/trufflesuite/truffle) is great for developing Solidity smart contracts, but building a React frontend for a smart contract is often a chore. [Next.js](https://github.com/zeit/next.js) is one of the easiest ways to build such a frontend and the integration between Truffle and Next.js is what this boilerplate is trying to demonstrate.
There are two major features:
- A plain `truffle init` project is used as the base (along with a SimpleStorage example contract).
- A Next.js project resides in the `client` directory with a symlink to the output folder of the contract ABI definitions. The Next.js app also provides a simple skeleton for connecting to and interacting with the smart contract on a network.
For more information on how the frontend works, go read the [README.md](https://github.com/adrianmcli/truffle-next/blob/master/client/README.md) located in the `client` directory.
## Installation
1. Install Truffle globally.
```bash
npm install -g truffle
```
2. Download the box. This also takes care of installing the necessary dependencies.
```bash
truffle unbox adrianmcli/truffle-next
```
3. Run the development console.
```bash
truffle develop
```
4. Compile and migrate the smart contracts. Note inside the development console we don't preface commands with `truffle`.
```bash
compile
migrate
```
5. Run the next.js server for the front-end. Smart contract changes must be manually recompiled and migrated.
```bash
// Change directory to the front-end folder
cd client
// Serves the front-end on http://localhost:3000
npm run dev
```
6. Truffle can run tests written in Solidity or JavaScript against your smart contracts. Note the command varies slightly if you're in or outside of the development console.
```bash
// If inside the development console.
test
// If outside the development console..
truffle test
```
## Running with MetaMask
Since `truffle develop` exposes the blockchain onto port `9545`, you'll need to add a Custom RPC network of `http://localhost:9545` in your MetaMask to make it work.
## Running with TestRPC
We highly recommend using `truffle develop` over `testrpc`, but if you want to use `testrpc`, there are a couple things you need to do:
- Change Line 6 of `client/lib/getWeb3.js` to use `localhost:8545` instead of `localhost:9545` so we refer to `testrpc` instead of `truffle develop`.
- Run your `testrpc` with the following command (because [reasons](https://github.com/trufflesuite/truffle/issues/660#issuecomment-343066784)):
```
testrpc --gasLimit 6721975 --gasPrice 100000000000
```
================================================
FILE: client/.gitignore
================================================
node_modules
.next
================================================
FILE: client/README.md
================================================
# Next.js client
This is the frontend client for our dapp. It is built with Next.js and uses a render-prop pattern (via `lib/Web3Container.js`) so we can easily inject blockchain functionality (i.e. web3, accounts, and the contract instance) into each page.
## Pages
There are three pages:
- `index.js` — This is a barebones Next.js page. It links to other pages which are web3-enabled. In your dapp, this can be a landing page.
- `accounts.js` — This is a page listing the accounts returned from Web3. This file demonstrates the basic use of the `Web3Container` component.
- `dapp.js` — This is a barebones demonstration dapp that utilizes the `Web3Container` component, but also makes calls to the contract. More specifically, it stores a value and gets a value.
## The `lib` folder
### `contracts`
A symlink to the `build/contracts` located in the Truffle project is placed here so that the Next.js app can refer to the build artifacts from the parent Truffle project.
### `Web3Container.js`
This is a component that utilizes the render-prop pattern to inject `web3`, `accounts`, and `contract` instance objects into a given function. When these objects are loading, it will render a loading function that is expected to return a React component.
For an example of how to use it, please see the `accounts` and `dapp` pages.
You may want to modify this for your own purposes. For example, you can require multiple contracts if your dapp requires it.
### `getWeb3.js`
This is a function for actually getting the Web3 object. Unfortunately, this file is not as straight-forward as I would have liked it. Your best bet at understanding this is to read the comments I have written in the file. You probably don't need to change anything in this file.
### `getContract.js`
This function requires `web3` to be passed in. It uses `truffle-contract` to initialize and return a contract instance. This function is used by `Web3Container.js`. You probably don't need to change anything in this file.
### `getAccounts.js`
This simply wraps `web3.eth.getAccounts` into a Promise so we can use it cleanly inside `Web3Container.js`. You probably don't need to change anything in this file.
================================================
FILE: client/lib/Web3Container.js
================================================
import React from 'react'
import getWeb3 from './getWeb3'
import getContract from './getContract'
import contractDefinition from './contracts/SimpleStorage.json'
export default class Web3Container extends React.Component {
state = { web3: null, accounts: null, contract: null };
async componentDidMount () {
try {
const web3 = await getWeb3()
const accounts = await web3.eth.getAccounts()
const contract = await getContract(web3, contractDefinition)
this.setState({ web3, accounts, contract })
} catch (error) {
alert(
`Failed to load web3, accounts, or contract. Check console for details.`
)
console.log(error)
}
}
render () {
const { web3, accounts, contract } = this.state
return web3 && accounts
? this.props.render({ web3, accounts, contract })
: this.props.renderLoading()
}
}
================================================
FILE: client/lib/getContract.js
================================================
const getContractInstance = async (web3, contractDefinition) => {
// get network ID and the deployed address
const networkId = await web3.eth.net.getId()
const deployedAddress = contractDefinition.networks[networkId].address
// create the instance
const instance = new web3.eth.Contract(
contractDefinition.abi,
deployedAddress
)
return instance
}
export default getContractInstance
================================================
FILE: client/lib/getWeb3.js
================================================
import Web3 from 'web3'
const resolveWeb3 = (resolve) => {
let { web3 } = window
const alreadyInjected = typeof web3 !== 'undefined' // i.e. Mist/Metamask
const localProvider = `http://localhost:9545`
if (alreadyInjected) {
console.log(`Injected web3 detected.`)
web3 = new Web3(web3.currentProvider)
} else {
console.log(`No web3 instance injected, using Local web3.`)
const provider = new Web3.providers.HttpProvider(localProvider)
web3 = new Web3(provider)
}
resolve(web3)
}
export default () =>
new Promise((resolve) => {
// Wait for loading completion to avoid race conditions with web3 injection timing.
window.addEventListener(`load`, () => {
resolveWeb3(resolve)
})
// If document has loaded already, try to get Web3 immediately.
if (document.readyState === `complete`) {
resolveWeb3(resolve)
}
})
================================================
FILE: client/next.config.js
================================================
module.exports = {
webpack: (config, { buildId, dev }) => {
// This allows the app to refer to files through our symlink
config.resolve.symlinks = false
return config
}
}
================================================
FILE: client/package.json
================================================
{
"name": "client",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start",
"link-contracts": "cd lib && ln -s ../../build/contracts contracts"
},
"dependencies": {
"next": "^6.1.1",
"react": "^16.4.1",
"react-dom": "^16.4.1",
"web3": "^1.0.0-beta.34"
}
}
================================================
FILE: client/pages/accounts.js
================================================
import React from 'react'
import Link from 'next/link'
import Web3Container from '../lib/Web3Container'
const Accounts = ({ accounts }) => (