Repository: adrianmcli/truffle-next Branch: master Commit: 5b4cf28ca528 Files: 21 Total size: 13.7 KB Directory structure: gitextract_scp6vz3s/ ├── .gitignore ├── README.md ├── client/ │ ├── .gitignore │ ├── README.md │ ├── lib/ │ │ ├── Web3Container.js │ │ ├── getContract.js │ │ └── getWeb3.js │ ├── next.config.js │ ├── package.json │ └── pages/ │ ├── accounts.js │ ├── dapp.js │ └── index.js ├── contracts/ │ ├── Migrations.sol │ └── SimpleStorage.sol ├── migrations/ │ ├── 1_initial_migration.js │ └── 2_deploy_contracts.js ├── package.json ├── test/ │ ├── TestSimpleStorage.sol │ └── simplestorage.js ├── truffle-box.json └── truffle.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ build client/lib/contracts node_modules ================================================ FILE: README.md ================================================

Truffle and Next.js


comet

Rapid Ethereum Dapp Development

made for ethereum to the moon MIT license

--- # 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 }) => (

My Accounts

{JSON.stringify(accounts, null, 4)}
My Dapp
Home
) export default () => (
Loading Accounts Page...
} render={({ accounts }) => } /> ) ================================================ FILE: client/pages/dapp.js ================================================ import React from 'react' import Link from 'next/link' import Web3Container from '../lib/Web3Container' class Dapp extends React.Component { state = { balance: undefined, ethBalance: undefined }; storeValue = async () => { const { accounts, contract } = this.props await contract.methods.set(5).send({ from: accounts[0] }) alert('Stored 5 into account') }; getValue = async () => { const { accounts, contract } = this.props const response = await contract.methods.get().call({ from: accounts[0] }) this.setState({ balance: response }) }; getEthBalance = async () => { const { web3, accounts } = this.props const balanceInWei = await web3.eth.getBalance(accounts[0]) this.setState({ ethBalance: balanceInWei / 1e18 }) }; render () { const { balance = 'N/A', ethBalance = 'N/A' } = this.state return (

My Dapp

Account Balance: {balance}
Ether Balance: {ethBalance}
My Accounts
Home
) } } export default () => (
Loading Dapp Page...
} render={({ web3, accounts, contract }) => ( )} /> ) ================================================ FILE: client/pages/index.js ================================================ import React from 'react' import Link from 'next/link' export default () =>

Home

Note that Web3 is not loaded for this page.

================================================ FILE: contracts/Migrations.sol ================================================ pragma solidity ^0.4.24; contract Migrations { address public owner; uint public last_completed_migration; modifier restricted() { if (msg.sender == owner) _; } constructor() public { owner = msg.sender; } 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); } } ================================================ FILE: contracts/SimpleStorage.sol ================================================ pragma solidity ^0.4.24; contract SimpleStorage { uint storedData; function set(uint x) public { storedData = x; } function get() public view returns (uint) { return storedData; } } ================================================ FILE: migrations/1_initial_migration.js ================================================ const Migrations = artifacts.require('./Migrations.sol') module.exports = function (deployer) { deployer.deploy(Migrations) } ================================================ FILE: migrations/2_deploy_contracts.js ================================================ const SimpleStorage = artifacts.require('./SimpleStorage.sol') module.exports = function (deployer) { deployer.deploy(SimpleStorage) } ================================================ FILE: package.json ================================================ { "name": "truffle-next", "version": "1.0.0", "license": "MIT", "scripts": { "lint": "standard" }, "standard": { "parser": "babel-eslint", "envs": [ "mocha", "browser" ], "globals": [ "artifacts", "contract", "assert" ] }, "devDependencies": { "babel-eslint": "^8.0.2", "standard": "^10.0.3" } } ================================================ FILE: test/TestSimpleStorage.sol ================================================ pragma solidity ^0.4.18; import "truffle/Assert.sol"; import "truffle/DeployedAddresses.sol"; import "../contracts/SimpleStorage.sol"; contract TestSimpleStorage { function testItStoresAValue() public { SimpleStorage simpleStorage = SimpleStorage(DeployedAddresses.SimpleStorage()); simpleStorage.set(89); uint expected = 89; Assert.equal(simpleStorage.get(), expected, "It should store the value 89."); } } ================================================ FILE: test/simplestorage.js ================================================ const SimpleStorage = artifacts.require('./SimpleStorage.sol') contract('SimpleStorage', (accounts) => { it('...should store the value 89.', async () => { const simpleStorageInstance = await SimpleStorage.deployed() // Set value of 89 await simpleStorageInstance.set(89, {from: accounts[0]}) // Get stored value const storedData = await simpleStorageInstance.get.call() assert.equal(storedData, 89, 'The value 89 was not stored.') }) }) ================================================ FILE: truffle-box.json ================================================ { "ignore": [ "README.md", ".gitignore", "package.json", "box-img-lg.png", "box-img-sm.png", "package-lock.json" ], "commands": { "Compile contracts": "truffle compile", "Migrate contracts": "truffle migrate", "Test contracts": "truffle test" }, "hooks": { "post-unpack": "cd client && npm install && npm run link-contracts" } } ================================================ FILE: truffle.js ================================================ module.exports = { networks: { development: { host: 'localhost', port: 8545, network_id: '*' // Match any network id } }, solc: { // Turns on the Solidity optimizer. For development the optimizer's // quite helpful, just remember to be careful, and potentially turn it // off, for live deployment and/or audit time. For more information, // see the Truffle 4.0.0 release notes. // // https://github.com/trufflesuite/truffle/releases/tag/v4.0.0 optimizer: { enabled: true, runs: 200 } } }