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
================================================
<h1 align="center">Truffle and Next.js</h1> <br>
<p align="center">
<img alt="comet" src="https://user-images.githubusercontent.com/943555/33169670-574322ee-cffa-11e7-9150-7b720ee0ee24.png" width="120">
</p>
<p align="center">Rapid Ethereum Dapp Development</p>
<p align="center">
<img alt="made for ethereum" src="https://img.shields.io/badge/made_for-ethereum-771ea5.svg">
<img alt="to the moon" src="https://img.shields.io/badge/to_the-moon-fab127.svg">
<img alt="MIT license" src="https://img.shields.io/badge/license-MIT-blue.svg">
</p>
---
# 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 }) => (
<div>
<h1>My Accounts</h1>
<pre>{JSON.stringify(accounts, null, 4)}</pre>
<div><Link href='/dapp'><a>My Dapp</a></Link></div>
<div><Link href='/'><a>Home</a></Link></div>
</div>
)
export default () => (
<Web3Container
renderLoading={() => <div>Loading Accounts Page...</div>}
render={({ accounts }) => <Accounts accounts={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 (
<div>
<h1>My Dapp</h1>
<button onClick={this.storeValue}>Store 5 into account balance</button>
<button onClick={this.getValue}>Get account balance</button>
<button onClick={this.getEthBalance}>Get ether balance</button>
<div>Account Balance: {balance}</div>
<div>Ether Balance: {ethBalance}</div>
<div>
<Link href='/accounts'>
<a>My Accounts</a>
</Link>
</div>
<div>
<Link href='/'>
<a>Home</a>
</Link>
</div>
</div>
)
}
}
export default () => (
<Web3Container
renderLoading={() => <div>Loading Dapp Page...</div>}
render={({ web3, accounts, contract }) => (
<Dapp accounts={accounts} contract={contract} web3={web3} />
)}
/>
)
================================================
FILE: client/pages/index.js
================================================
import React from 'react'
import Link from 'next/link'
export default () =>
<div>
<h1>Home</h1>
<p>Note that Web3 is not loaded for this page.</p>
<div><Link href='/dapp'><a>My Dapp</a></Link></div>
<div><Link href='/accounts'><a>My Accounts</a></Link></div>
</div>
================================================
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
}
}
}
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
SYMBOL INDEX (5 symbols across 2 files)
FILE: client/lib/Web3Container.js
class Web3Container (line 6) | class Web3Container extends React.Component {
method componentDidMount (line 9) | async componentDidMount () {
method render (line 23) | render () {
FILE: client/pages/dapp.js
class Dapp (line 5) | class Dapp extends React.Component {
method render (line 29) | render () {
Condensed preview — 21 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (16K chars).
[
{
"path": ".gitignore",
"chars": 40,
"preview": "build\nclient/lib/contracts\nnode_modules\n"
},
{
"path": "README.md",
"chars": 3300,
"preview": "<h1 align=\"center\">Truffle and Next.js</h1> <br>\n<p align=\"center\">\n <img alt=\"comet\" src=\"https://user-images.githubus"
},
{
"path": "client/.gitignore",
"chars": 19,
"preview": "node_modules\n.next\n"
},
{
"path": "client/README.md",
"chars": 2195,
"preview": "# Next.js client\n\nThis is the frontend client for our dapp. It is built with Next.js and uses a render-prop pattern (via"
},
{
"path": "client/lib/Web3Container.js",
"chars": 879,
"preview": "import React from 'react'\nimport getWeb3 from './getWeb3'\nimport getContract from './getContract'\nimport contractDefinit"
},
{
"path": "client/lib/getContract.js",
"chars": 407,
"preview": "const getContractInstance = async (web3, contractDefinition) => {\n // get network ID and the deployed address\n const n"
},
{
"path": "client/lib/getWeb3.js",
"chars": 884,
"preview": "import Web3 from 'web3'\n\nconst resolveWeb3 = (resolve) => {\n let { web3 } = window\n const alreadyInjected = typeof web"
},
{
"path": "client/next.config.js",
"chars": 187,
"preview": "module.exports = {\n webpack: (config, { buildId, dev }) => {\n // This allows the app to refer to files through our s"
},
{
"path": "client/package.json",
"chars": 380,
"preview": "{\n \"name\": \"client\",\n \"version\": \"1.0.0\",\n \"main\": \"index.js\",\n \"license\": \"MIT\",\n \"scripts\": {\n \"dev\": \"next\",\n"
},
{
"path": "client/pages/accounts.js",
"chars": 516,
"preview": "import React from 'react'\nimport Link from 'next/link'\nimport Web3Container from '../lib/Web3Container'\n\nconst Accounts "
},
{
"path": "client/pages/dapp.js",
"chars": 1689,
"preview": "import React from 'react'\nimport Link from 'next/link'\nimport Web3Container from '../lib/Web3Container'\n\nclass Dapp exte"
},
{
"path": "client/pages/index.js",
"chars": 287,
"preview": "import React from 'react'\nimport Link from 'next/link'\n\nexport default () =>\n <div>\n <h1>Home</h1>\n <p>Note that "
},
{
"path": "contracts/Migrations.sol",
"chars": 507,
"preview": "pragma solidity ^0.4.24;\n\ncontract Migrations {\n address public owner;\n uint public last_completed_migration;\n\n modif"
},
{
"path": "contracts/SimpleStorage.sol",
"chars": 204,
"preview": "\npragma solidity ^0.4.24;\n\ncontract SimpleStorage {\n uint storedData;\n\n function set(uint x) public {\n storedData ="
},
{
"path": "migrations/1_initial_migration.js",
"chars": 129,
"preview": "const Migrations = artifacts.require('./Migrations.sol')\n\nmodule.exports = function (deployer) {\n deployer.deploy(Migra"
},
{
"path": "migrations/2_deploy_contracts.js",
"chars": 138,
"preview": "const SimpleStorage = artifacts.require('./SimpleStorage.sol')\n\nmodule.exports = function (deployer) {\n deployer.deploy"
},
{
"path": "package.json",
"chars": 376,
"preview": "{\n \"name\": \"truffle-next\",\n \"version\": \"1.0.0\",\n \"license\": \"MIT\",\n \"scripts\": {\n \"lint\": \"standard\"\n },\n \"stan"
},
{
"path": "test/TestSimpleStorage.sol",
"chars": 435,
"preview": "pragma solidity ^0.4.18;\n\nimport \"truffle/Assert.sol\";\nimport \"truffle/DeployedAddresses.sol\";\nimport \"../contracts/Simp"
},
{
"path": "test/simplestorage.js",
"chars": 469,
"preview": "const SimpleStorage = artifacts.require('./SimpleStorage.sol')\n\ncontract('SimpleStorage', (accounts) => {\n it('...shoul"
},
{
"path": "truffle-box.json",
"chars": 381,
"preview": "{\n \"ignore\": [\n \"README.md\",\n \".gitignore\",\n \"package.json\",\n \"box-img-lg.png\",\n \"box-img-sm.png\",\n \""
},
{
"path": "truffle.js",
"chars": 566,
"preview": "module.exports = {\n networks: {\n development: {\n host: 'localhost',\n port: 8545,\n network_id: '*' // "
}
]
About this extraction
This page contains the full source code of the adrianmcli/truffle-next GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 21 files (13.7 KB), approximately 4.1k tokens, and a symbol index with 5 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.