├── .gitattributes
├── .github
└── stale.yml
├── .gitignore
├── .prettierrc.json
├── LICENSE
├── README.md
├── client
├── README.md
├── package-lock.json
├── package.json
├── public
│ ├── favicon.ico
│ └── index.html
├── src
│ ├── App.jsx
│ ├── components
│ │ ├── Demo
│ │ │ ├── Contract.jsx
│ │ │ ├── ContractBtns.jsx
│ │ │ ├── Cta.jsx
│ │ │ ├── Desc.jsx
│ │ │ ├── NoticeNoArtifact.jsx
│ │ │ ├── NoticeWrongNetwork.jsx
│ │ │ ├── Title.jsx
│ │ │ └── index.jsx
│ │ ├── Footer.jsx
│ │ ├── Intro
│ │ │ ├── Desc.jsx
│ │ │ ├── Tree.jsx
│ │ │ ├── Welcome.jsx
│ │ │ └── index.jsx
│ │ └── Setup.jsx
│ ├── contexts
│ │ └── EthContext
│ │ │ ├── EthContext.js
│ │ │ ├── EthProvider.jsx
│ │ │ ├── index.js
│ │ │ ├── state.js
│ │ │ └── useEth.js
│ ├── index.jsx
│ └── styles.css
└── webpack.config.js
├── truffle-box.json
└── truffle
├── contracts
└── SimpleStorage.sol
├── migrations
└── 1_deploy_simple_storage.js
├── package-lock.json
├── package.json
├── scripts
└── increment.js
├── test
├── SimpleStorageTest.sol
└── simplestorage.js
└── truffle-config.js
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.sol linguist-language=Solidity
2 |
--------------------------------------------------------------------------------
/.github/stale.yml:
--------------------------------------------------------------------------------
1 | # Number of days of inactivity before an issue becomes stale
2 | daysUntilStale: 60
3 | # Number of days of inactivity before a stale issue is closed
4 | daysUntilClose: 7
5 | # Issues with these labels will never be considered stale
6 | exemptLabels:
7 | - pinned
8 | - security
9 | # Label to use when marking an issue as stale
10 | staleLabel: wontfix
11 | # Comment to post when marking an issue as stale. Set to `false` to disable
12 | markComment: >
13 | This issue has been automatically marked as stale because it has not had
14 | recent activity. It will be closed if no further activity occurs. Thank you
15 | for your contributions.
16 | # Comment to post when closing a stale issue. Set to `false` to disable
17 | closeComment: >
18 | This issue has been closed, but can be re-opened if further comments
19 | indicate that the problem persists. Feel free to tag maintainers if there
20 | is no reply to further comments.
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # Dependencies
4 | node_modules
5 | .pnp
6 | .pnp.js
7 |
8 | # Production
9 | build
10 | client/src/contracts
11 | client/*/src/contracts
12 |
13 | # Testing
14 | coverage
15 |
16 | # Env
17 | .env
18 | .env.local
19 | .env.development.local
20 | .env.test.local
21 | .env.production.local
22 |
23 | # Editor
24 | .vscode
25 |
26 | # Misc.
27 | .DS_Store
28 |
29 | npm-debug.log*
30 | yarn-debug.log*
31 | yarn-error.log*
32 |
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "tabWidth": 2
3 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2018 Truffle
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React Truffle Box
2 |
3 | This box comes with everything you need to start using Truffle to write, compile, test, and deploy smart contracts, and interact with them from a React app.
4 |
5 | ## Installation
6 |
7 | First ensure you are in an empty directory.
8 |
9 | Run the `unbox` command using 1 of 2 ways.
10 |
11 | ```sh
12 | # Install Truffle globally and run `truffle unbox`
13 | $ npm install -g truffle
14 | $ truffle unbox react
15 | ```
16 |
17 | ```sh
18 | # Alternatively, run `truffle unbox` via npx
19 | $ npx truffle unbox react
20 | ```
21 |
22 | Start the react dev server.
23 |
24 | ```sh
25 | $ cd client
26 | $ npm start
27 | ```
28 |
29 | From there, follow the instructions on the hosted React app. It will walk you through using Truffle and Ganache to deploy the `SimpleStorage` contract, making calls to it, and sending transactions to change the contract's state.
30 |
31 | ## FAQ
32 |
33 | - __How do I use this with Ganache (or any other network)?__
34 |
35 | The Truffle project is set to deploy to Ganache by default. If you'd like to change this, it's as easy as modifying the Truffle config file! Check out [our documentation on adding network configurations](https://trufflesuite.com/docs/truffle/reference/configuration/#networks). From there, you can run `truffle migrate` pointed to another network, restart the React dev server, and see the change take place.
36 |
37 | - __Where can I find more resources?__
38 |
39 | This Box is a sweet combo of [Truffle](https://trufflesuite.com) and [Webpack](https://webpack.js.org). Either one would be a great place to start!
40 |
--------------------------------------------------------------------------------
/client/README.md:
--------------------------------------------------------------------------------
1 | # React client
2 |
3 | This react project is unopinionated with only `web3.js` as an added dependency, so nothing stands in your way.
4 |
5 | ## Getting started
6 |
7 | Run `npm start` to start the dev server, and `npm build` to create a production build.
8 |
--------------------------------------------------------------------------------
/client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "truffle-client",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "build": "webpack",
7 | "start": "webpack serve"
8 | },
9 | "dependencies": {
10 | "react": "^18.2.0",
11 | "react-dom": "^18.2.0",
12 | "web3": "^1.8.2"
13 | },
14 | "devDependencies": {
15 | "@babel/preset-react": "^7.18.6",
16 | "babel-loader": "^9.1.2",
17 | "css-loader": "^6.7.3",
18 | "eslint": "^8.34.0",
19 | "eslint-config-react-app": "^7.0.1",
20 | "eslint-webpack-plugin": "^4.0.0",
21 | "html-webpack-plugin": "^5.5.0",
22 | "style-loader": "^3.3.1",
23 | "webpack": "^5.75.0",
24 | "webpack-cli": "^5.0.1",
25 | "webpack-dev-server": "^4.11.1"
26 | },
27 | "eslintConfig": {
28 | "extends": [
29 | "react-app"
30 | ]
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/client/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/truffle-box/react-box/bcfeb00e8acb9ec0a213c9a8f031725ab0f7c817/client/public/favicon.ico
--------------------------------------------------------------------------------
/client/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | React Truffle Box
8 |
9 |
10 |
11 | JavaScript is disabled in your browser.
12 | Please consider enabling it to run this app.
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/client/src/App.jsx:
--------------------------------------------------------------------------------
1 | import { EthProvider } from "./contexts/EthContext";
2 | import Intro from "./components/Intro/";
3 | import Setup from "./components/Setup";
4 | import Demo from "./components/Demo";
5 | import Footer from "./components/Footer";
6 |
7 | function App() {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | );
23 | }
24 |
25 | export default App;
26 |
--------------------------------------------------------------------------------
/client/src/components/Demo/Contract.jsx:
--------------------------------------------------------------------------------
1 | import { useRef, useEffect } from "react";
2 |
3 | function Contract({ value }) {
4 | const spanEle = useRef(null);
5 |
6 | useEffect(() => {
7 | spanEle.current.classList.add("flash");
8 | const flash = setTimeout(() => {
9 | spanEle.current.classList.remove("flash");
10 | }, 300);
11 | return () => {
12 | clearTimeout(flash);
13 | };
14 | }, [value]);
15 |
16 | return (
17 |
18 | {`contract SimpleStorage {
19 | uint256 value = `}
20 |
21 |
22 | {value}
23 |
24 |
25 | {`;
26 |
27 | function read() public view returns (uint256) {
28 | return value;
29 | }
30 |
31 | function write(uint256 newValue) public {
32 | value = newValue;
33 | }
34 | }`}
35 |
36 | );
37 | }
38 |
39 | export default Contract;
40 |
--------------------------------------------------------------------------------
/client/src/components/Demo/ContractBtns.jsx:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import useEth from "../../contexts/EthContext/useEth";
3 |
4 | function ContractBtns({ setValue }) {
5 | const { state: { contract, accounts } } = useEth();
6 | const [inputValue, setInputValue] = useState("");
7 |
8 | const handleInputChange = e => {
9 | if (/^\d+$|^$/.test(e.target.value)) {
10 | setInputValue(e.target.value);
11 | }
12 | };
13 |
14 | const read = async () => {
15 | const value = await contract.methods.read().call({ from: accounts[0] });
16 | setValue(value);
17 | };
18 |
19 | const write = async e => {
20 | if (e.target.tagName === "INPUT") {
21 | return;
22 | }
23 | if (inputValue === "") {
24 | alert("Please enter a value to write.");
25 | return;
26 | }
27 | const newValue = parseInt(inputValue);
28 | await contract.methods.write(newValue).send({ from: accounts[0] });
29 | };
30 |
31 | return (
32 |
33 |
34 |
35 | read()
36 |
37 |
38 |
39 | write( )
45 |
46 |
47 |
48 | );
49 | }
50 |
51 | export default ContractBtns;
52 |
--------------------------------------------------------------------------------
/client/src/components/Demo/Cta.jsx:
--------------------------------------------------------------------------------
1 | function Cta() {
2 | return (
3 |
4 | Try changing
5 | value
6 | in
7 | SimpleStorage .
8 |
9 | );
10 | }
11 |
12 | export default Cta;
13 |
--------------------------------------------------------------------------------
/client/src/components/Demo/Desc.jsx:
--------------------------------------------------------------------------------
1 | function Desc() {
2 | return (
3 | <>
4 |
5 | Take a look at client/src/contexts/EthContext .
6 | This context maintains a global state and provides web3.js functionalities
7 | to the rest of the app.
8 |
9 |
10 | Feel free to remove any component or styling that you don't need, and
11 | extend EthContext to your dapp's needs.
12 |
13 |
14 | Happy hacking!
15 |
16 | >
17 | );
18 | }
19 |
20 | export default Desc;
21 |
--------------------------------------------------------------------------------
/client/src/components/Demo/NoticeNoArtifact.jsx:
--------------------------------------------------------------------------------
1 | function NoticeNoArtifact() {
2 | return (
3 |
4 | ⚠️ Cannot find SimpleStorage contract artifact.
5 | Please complete the above preparation first, then restart the react dev server.
6 |
7 | );
8 | }
9 |
10 | export default NoticeNoArtifact;
11 |
--------------------------------------------------------------------------------
/client/src/components/Demo/NoticeWrongNetwork.jsx:
--------------------------------------------------------------------------------
1 | function NoticeWrongNetwork() {
2 | return (
3 |
4 | ⚠️ MetaMask is not connected to the same network as the one you deployed to.
5 |
6 | );
7 | }
8 |
9 | export default NoticeWrongNetwork;
10 |
--------------------------------------------------------------------------------
/client/src/components/Demo/Title.jsx:
--------------------------------------------------------------------------------
1 | function Title() {
2 | return See it in action ;
3 | }
4 |
5 | export default Title;
6 |
--------------------------------------------------------------------------------
/client/src/components/Demo/index.jsx:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import useEth from "../../contexts/EthContext/useEth";
3 | import Title from "./Title";
4 | import Cta from "./Cta";
5 | import Contract from "./Contract";
6 | import ContractBtns from "./ContractBtns";
7 | import Desc from "./Desc";
8 | import NoticeNoArtifact from "./NoticeNoArtifact";
9 | import NoticeWrongNetwork from "./NoticeWrongNetwork";
10 |
11 | function Demo() {
12 | const { state } = useEth();
13 | const [value, setValue] = useState("?");
14 |
15 | const demo =
16 | <>
17 |
18 |
19 |
20 |
21 |
22 |
23 | >;
24 |
25 | return (
26 |
27 |
28 | {
29 | !state.artifact ? :
30 | !state.contract ? :
31 | demo
32 | }
33 |
34 | );
35 | }
36 |
37 | export default Demo;
38 |
--------------------------------------------------------------------------------
/client/src/components/Footer.jsx:
--------------------------------------------------------------------------------
1 | function Link({ uri, text }) {
2 | return {text} ;
3 | }
4 |
5 | function Footer() {
6 | return (
7 |
8 | More resources
9 |
10 |
11 |
12 |
13 |
14 | );
15 | }
16 |
17 | export default Footer;
18 |
--------------------------------------------------------------------------------
/client/src/components/Intro/Desc.jsx:
--------------------------------------------------------------------------------
1 | function Desc() {
2 | return (
3 |
4 | This particular Box uses
5 |
6 | web3.js
7 |
8 | , a popular Ethereum library.
9 |
10 | );
11 | }
12 |
13 | export default Desc;
14 |
--------------------------------------------------------------------------------
/client/src/components/Intro/Tree.jsx:
--------------------------------------------------------------------------------
1 | function Tree() {
2 | return (
3 |
4 | {`.\n`}
5 | {`├── client`}
6 |
7 | {` # React project (create-react-app)\n`}
8 |
9 | {`└── truffle`}
10 |
11 | {` # Truffle project`}
12 |
13 |
14 | );
15 | }
16 |
17 | export default Tree;
18 |
--------------------------------------------------------------------------------
/client/src/components/Intro/Welcome.jsx:
--------------------------------------------------------------------------------
1 | function Welcome() {
2 | return (
3 |
4 |
👋 Welcome to the Truffle + React Box!
5 |
6 | This is everything you need to start using Truffle to write,
7 | compile, test, and deploy smart contracts, and interact with
8 | them from a React app.
9 |
10 |
11 | );
12 | }
13 |
14 | export default Welcome;
15 |
--------------------------------------------------------------------------------
/client/src/components/Intro/index.jsx:
--------------------------------------------------------------------------------
1 | import Welcome from "./Welcome";
2 | import Tree from "./Tree";
3 | import Desc from "./Desc";
4 |
5 | function Intro() {
6 | return (
7 | <>
8 |
9 |
10 |
11 | >
12 | );
13 | }
14 |
15 | export default Intro;
16 |
--------------------------------------------------------------------------------
/client/src/components/Setup.jsx:
--------------------------------------------------------------------------------
1 | function Setup() {
2 |
3 | return (
4 | <>
5 | Preparation
6 |
7 |
8 | Install
9 | Install Truffle and Ganache globally.
10 | $ npm install -g truffle ganache
11 |
12 |
13 |
14 | Ganache and MetaMask
15 |
16 | Open a terminal and run Ganache, a simulated Ethereum blockchain on your machine.
17 |
18 | $ ganache
19 | From the list of generated private keys, import the first one to MetaMask.
20 |
21 |
22 |
23 | Truffle
24 |
25 | Keep Ganache running and open another terminal. Let's compile and deploy our
26 | contracts to Ganache.
27 |
28 |
29 | {`$ cd truffle\n`}
30 | {`$ truffle migrate --network development\n`}
31 |
32 | # The `development` network points to Ganache, it's configured in
33 | truffle/truffle-config.js on line 45.
34 |
35 |
36 |
37 | >
38 | );
39 | }
40 |
41 | export default Setup;
42 |
--------------------------------------------------------------------------------
/client/src/contexts/EthContext/EthContext.js:
--------------------------------------------------------------------------------
1 | import { createContext } from "react";
2 |
3 | const EthContext = createContext();
4 |
5 | export default EthContext;
6 |
--------------------------------------------------------------------------------
/client/src/contexts/EthContext/EthProvider.jsx:
--------------------------------------------------------------------------------
1 | import React, { useReducer, useCallback, useEffect } from "react";
2 | import Web3 from "web3";
3 | import EthContext from "./EthContext";
4 | import { reducer, actions, initialState } from "./state";
5 |
6 | function EthProvider({ children }) {
7 | const [state, dispatch] = useReducer(reducer, initialState);
8 |
9 | const init = useCallback(
10 | async artifact => {
11 | if (artifact) {
12 | const web3 = new Web3(Web3.givenProvider || "ws://localhost:8545");
13 | const accounts = await web3.eth.requestAccounts();
14 | const networkID = await web3.eth.net.getId();
15 | const { abi } = artifact;
16 | let address, contract;
17 | try {
18 | address = artifact.networks[networkID].address;
19 | contract = new web3.eth.Contract(abi, address);
20 | } catch (err) {
21 | console.error(err);
22 | }
23 | dispatch({
24 | type: actions.init,
25 | data: { artifact, web3, accounts, networkID, contract }
26 | });
27 | }
28 | }, []);
29 |
30 | useEffect(() => {
31 | const tryInit = async () => {
32 | try {
33 | const artifact = require("../../contracts/SimpleStorage.json");
34 | init(artifact);
35 | } catch (err) {
36 | console.error(err);
37 | }
38 | };
39 |
40 | tryInit();
41 | }, [init]);
42 |
43 | useEffect(() => {
44 | const events = ["chainChanged", "accountsChanged"];
45 | const handleChange = () => {
46 | init(state.artifact);
47 | };
48 |
49 | events.forEach(e => window.ethereum.on(e, handleChange));
50 | return () => {
51 | events.forEach(e => window.ethereum.removeListener(e, handleChange));
52 | };
53 | }, [init, state.artifact]);
54 |
55 | return (
56 |
60 | {children}
61 |
62 | );
63 | }
64 |
65 | export default EthProvider;
66 |
--------------------------------------------------------------------------------
/client/src/contexts/EthContext/index.js:
--------------------------------------------------------------------------------
1 | export { default as EthContext } from "./EthContext";
2 | export { default as EthProvider } from "./EthProvider";
3 | export { default as useEth } from "./useEth";
4 | export * from "./state";
5 |
--------------------------------------------------------------------------------
/client/src/contexts/EthContext/state.js:
--------------------------------------------------------------------------------
1 | const actions = {
2 | init: "INIT"
3 | };
4 |
5 | const initialState = {
6 | artifact: null,
7 | web3: null,
8 | accounts: null,
9 | networkID: null,
10 | contract: null
11 | };
12 |
13 | const reducer = (state, action) => {
14 | const { type, data } = action;
15 | switch (type) {
16 | case actions.init:
17 | return { ...state, ...data };
18 | default:
19 | throw new Error("Undefined reducer action type");
20 | }
21 | };
22 |
23 | export { actions, initialState, reducer };
24 |
--------------------------------------------------------------------------------
/client/src/contexts/EthContext/useEth.js:
--------------------------------------------------------------------------------
1 | import { useContext } from "react";
2 | import EthContext from "./EthContext";
3 |
4 | const useEth = () => useContext(EthContext);
5 |
6 | export default useEth;
7 |
--------------------------------------------------------------------------------
/client/src/index.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import App from "./App";
4 | import "./styles.css";
5 |
6 | const root = ReactDOM.createRoot(document.getElementById("root"));
7 | root.render(
8 |
9 |
10 |
11 | );
12 |
--------------------------------------------------------------------------------
/client/src/styles.css:
--------------------------------------------------------------------------------
1 | html {
2 | font-size: 10px;
3 | font-family: sans-serif;
4 | }
5 |
6 | * {
7 | margin: 0;
8 | padding: 0;
9 | }
10 |
11 | #App {
12 | width: 100%;
13 | height: 100%;
14 | display: flex;
15 | align-items: center;
16 | justify-content: center;
17 | font-size: 1.6rem;
18 | --primary-color: #1266cc;
19 | --secondary-color: #f96400;
20 | --flash-color: rgb(89, 255, 0);
21 | --gray: #999;
22 | --light-gray: #e0e0e0;
23 | --dark-gray: #777;
24 | }
25 |
26 | #App .container {
27 | width: 40%;
28 | min-width: 60rem;
29 | max-width: 75rem;
30 | height: 100%;
31 | }
32 |
33 | code {
34 | display: block;
35 | white-space: pre-wrap;
36 | padding: 1.2rem 2rem;
37 | border-radius: 1rem;
38 | background-color: var(--light-gray);
39 | font-size: 1.4rem;
40 | }
41 |
42 | code span {
43 | transition: all 0.3s;
44 | }
45 |
46 | code span.primary-color {
47 | color: var(--primary-color);
48 | }
49 |
50 | code span.secondary-color {
51 | color: var(--secondary-color);
52 | }
53 |
54 | code span.dim-color {
55 | color: var(--gray);
56 | }
57 |
58 | code span.flash {
59 | background-color: var(--flash-color);
60 | }
61 |
62 | details {
63 | margin: 1.6rem 0;
64 | padding: 0.5rem 2rem 0.5rem 0;
65 | border: 1px solid var(--light-gray);
66 | border-radius: 1rem;
67 | }
68 |
69 | details :not(summary, span) {
70 | margin-left: 2.55rem;
71 | }
72 |
73 | details summary {
74 | padding: 1.2rem;
75 | cursor: pointer;
76 | }
77 |
78 | p,
79 | details code {
80 | margin-top: 1rem;
81 | margin-bottom: 1.4rem;
82 | }
83 |
84 | p {
85 | line-height: 2.6rem;
86 | }
87 |
88 | p span.code {
89 | font-family: monospace;
90 | color: var(--gray);
91 | }
92 |
93 | hr {
94 | margin: 4rem 0;
95 | }
96 |
97 | #App .container .welcome h1 {
98 | padding-top: 7.5rem;
99 | font-size: 2.6rem;
100 | }
101 |
102 | #App .container .demo .contract-container {
103 | display: flex;
104 | }
105 |
106 | #App .container .demo .contract-container code {
107 | min-width: 42rem;
108 | border-radius: 1rem 0 0 1rem;
109 | }
110 |
111 | #App .container .demo .contract-container .btns {
112 | width: 100%;
113 | display: flex;
114 | flex-direction: column;
115 | align-items: center;
116 | justify-content: space-evenly;
117 | padding: 0 1rem;
118 | background-color: var(--gray);
119 | border-radius: 0 1rem 1rem 0;
120 | }
121 |
122 | #App .container .demo .contract-container .btns > * {
123 | width: 12rem;
124 | display: flex;
125 | align-items: center;
126 | justify-content: center;
127 | padding: 1rem 0;
128 | background-color: var(--light-gray);
129 | border: none;
130 | font-size: 1.2rem;
131 | font-family: monospace;
132 | cursor: pointer;
133 | }
134 |
135 | #App .container .demo .contract-container .btns > *:hover {
136 | background-color: var(--dark-gray);
137 | }
138 |
139 | #App .container .demo .contract-container .btns .input-btn input {
140 | width: 4rem;
141 | text-align: center;
142 | font-family: monospace;
143 | font-size: 1.2rem;
144 | }
145 |
146 | #App .container footer {
147 | padding-bottom: 7.5rem;
148 | }
149 |
150 | #App .container footer a {
151 | display: inline-block;
152 | margin: 2rem 1rem 0 0;
153 | }
154 |
--------------------------------------------------------------------------------
/client/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const HtmlWebpackPlugin = require("html-webpack-plugin");
3 | const EslintWebpackPlugin = require("eslint-webpack-plugin");
4 |
5 | const extensions = [".js", ".jsx"];
6 |
7 | module.exports = {
8 | mode: process.env.NODE_ENV === "production" ? "production" : "development",
9 | entry: "./src/index.jsx",
10 | output: {
11 | path: path.resolve(__dirname, "build"),
12 | },
13 | resolve: { extensions },
14 | devServer: {
15 | client: {
16 | overlay: false,
17 | },
18 | },
19 | module: {
20 | rules: [
21 | {
22 | test: /\.jsx?$/i,
23 | use: [
24 | {
25 | loader: "babel-loader",
26 | options: {
27 | presets: [["@babel/preset-react", { runtime: "automatic" }]],
28 | },
29 | },
30 | ],
31 | exclude: /node_modules/,
32 | },
33 | {
34 | test: /\.css$/i,
35 | use: ["style-loader", "css-loader"],
36 | },
37 | ],
38 | },
39 | plugins: [
40 | new EslintWebpackPlugin({ extensions }),
41 | new HtmlWebpackPlugin({
42 | template: "./public/index.html",
43 | favicon: "./public/favicon.ico",
44 | }),
45 | ],
46 | stats: "minimal",
47 | };
48 |
--------------------------------------------------------------------------------
/truffle-box.json:
--------------------------------------------------------------------------------
1 | {
2 | "ignore": [
3 | ".github",
4 | ".prettierrc.json"
5 | ],
6 | "commands": {
7 | "Contracts: Compile": "cd truffle && truffle compile",
8 | "Contracts: Test": "cd truffle && truffle test",
9 | "Contracts: Migrate": "cd truffle && truffle migrate",
10 | "Dapp: Run dev server": "cd client && npm start",
11 | "Dapp: Test": "cd client && npm test",
12 | "Dapp: Build for production": "cd client && npm run build"
13 | },
14 | "hooks": {
15 | "post-unpack": "cd client && npm ci"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/truffle/contracts/SimpleStorage.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity >=0.4.22 <0.9.0;
3 |
4 | contract SimpleStorage {
5 | uint256 value;
6 |
7 | function read() public view returns (uint256) {
8 | return value;
9 | }
10 |
11 | function write(uint256 newValue) public {
12 | value = newValue;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/truffle/migrations/1_deploy_simple_storage.js:
--------------------------------------------------------------------------------
1 | const SimpleStorage = artifacts.require("SimpleStorage");
2 |
3 | module.exports = function (deployer) {
4 | deployer.deploy(SimpleStorage);
5 | };
6 |
--------------------------------------------------------------------------------
/truffle/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "truffle-project",
3 | "version": "0.1.0",
4 | "description": "Truffle project with SimpleStorage contract",
5 | "scripts": {
6 | "test": "truffle test"
7 | },
8 | "dependencies": {
9 | "@truffle/hdwallet-provider": "^2.1.7"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/truffle/scripts/increment.js:
--------------------------------------------------------------------------------
1 | /*
2 | Try `truffle exec scripts/increment.js`, you should `truffle migrate` first.
3 |
4 | Learn more about Truffle external scripts:
5 | https://trufflesuite.com/docs/truffle/getting-started/writing-external-scripts
6 | */
7 |
8 | const SimpleStorage = artifacts.require("SimpleStorage");
9 |
10 | module.exports = async function (callback) {
11 | const deployed = await SimpleStorage.deployed();
12 |
13 | const currentValue = (await deployed.read()).toNumber();
14 | console.log(`Current SimpleStorage value: ${currentValue}`);
15 |
16 | const { tx } = await deployed.write(currentValue + 1);
17 | console.log(`Confirmed transaction ${tx}`);
18 |
19 | const updatedValue = (await deployed.read()).toNumber();
20 | console.log(`Updated SimpleStorage value: ${updatedValue}`);
21 |
22 | callback();
23 | };
24 |
--------------------------------------------------------------------------------
/truffle/test/SimpleStorageTest.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity >=0.4.22 <0.9.0;
3 |
4 | import "../contracts/SimpleStorage.sol";
5 | // These files are dynamically created at test time
6 | import "truffle/Assert.sol";
7 | import "truffle/DeployedAddresses.sol";
8 |
9 | contract SimpleStorageTest {
10 |
11 | function testWriteValue() public {
12 | SimpleStorage simpleStorage = SimpleStorage(DeployedAddresses.SimpleStorage());
13 |
14 | Assert.equal(simpleStorage.read(), 0, "Contract should have 0 stored");
15 | simpleStorage.write(1);
16 | Assert.equal(simpleStorage.read(), 1, "Contract should have 1 stored");
17 | simpleStorage.write(2);
18 | Assert.equal(simpleStorage.read(), 2, "Contract should have 2 stored");
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/truffle/test/simplestorage.js:
--------------------------------------------------------------------------------
1 | const SimpleStorage = artifacts.require("SimpleStorage");
2 |
3 | contract('SimpleStorage', () => {
4 | it('should read newly written values', async() => {
5 | const simpleStorageInstance = await SimpleStorage.deployed();
6 | var value = (await simpleStorageInstance.read()).toNumber();
7 |
8 | assert.equal(value, 0, "0 wasn't the initial value");
9 |
10 | await simpleStorageInstance.write(1);
11 | value = (await simpleStorageInstance.read()).toNumber();
12 | assert.equal(value, 1, "1 was not written");
13 |
14 | await simpleStorageInstance.write(2);
15 | value = (await simpleStorageInstance.read()).toNumber();
16 | assert.equal(value, 2, "2 was not written");
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/truffle/truffle-config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Use this file to configure your truffle project. It's seeded with some
3 | * common settings for different networks and features like migrations,
4 | * compilation, and testing. Uncomment the ones you need or modify
5 | * them to suit your project as necessary.
6 | *
7 | * More information about configuration can be found at:
8 | *
9 | * https://trufflesuite.com/docs/truffle/reference/configuration
10 | *
11 | * Hands-off deployment with Infura
12 | * --------------------------------
13 | *
14 | * Do you have a complex application that requires lots of transactions to deploy?
15 | * Use this approach to make deployment a breeze 🏖️:
16 | *
17 | * Infura deployment needs a wallet provider (like @truffle/hdwallet-provider)
18 | * to sign transactions before they're sent to a remote public node.
19 | * Infura accounts are available for free at 🔍: https://infura.io/register
20 | *
21 | * You'll need a mnemonic - the twelve word phrase the wallet uses to generate
22 | * public/private key pairs. You can store your secrets 🤐 in a .env file.
23 | * In your project root, run `$ npm install dotenv`.
24 | * Create .env (which should be .gitignored) and declare your MNEMONIC
25 | * and Infura PROJECT_ID variables inside.
26 | * For example, your .env file will have the following structure:
27 | *
28 | * MNEMONIC =
29 | * PROJECT_ID =
30 | *
31 | * Deployment with Truffle Dashboard (Recommended for best security practice)
32 | * --------------------------------------------------------------------------
33 | *
34 | * Are you concerned about security and minimizing rekt status 🤔?
35 | * Use this method for best security:
36 | *
37 | * Truffle Dashboard lets you review transactions in detail, and leverages
38 | * MetaMask for signing, so there's no need to copy-paste your mnemonic.
39 | * More details can be found at 🔎:
40 | *
41 | * https://trufflesuite.com/docs/truffle/getting-started/using-the-truffle-dashboard/
42 | */
43 |
44 | // require('dotenv').config();
45 | // const { MNEMONIC, PROJECT_ID } = process.env;
46 |
47 | // const HDWalletProvider = require('@truffle/hdwallet-provider');
48 |
49 | module.exports = {
50 | /**
51 | * Networks define how you connect to your ethereum client and let you set the
52 | * defaults web3 uses to send transactions. If you don't specify one truffle
53 | * will spin up a managed Ganache instance for you on port 9545 when you
54 | * run `develop` or `test`. You can ask a truffle command to use a specific
55 | * network from the command line, e.g
56 | *
57 | * $ truffle test --network
58 | */
59 |
60 | contracts_build_directory: "../client/src/contracts",
61 | networks: {
62 | // Useful for testing. The `development` name is special - truffle uses it by default
63 | // if it's defined here and no other network is specified at the command line.
64 | // You should run a client (like ganache, geth, or parity) in a separate terminal
65 | // tab if you use this network and you must also set the `host`, `port` and `network_id`
66 | // options below to some value.
67 | //
68 | // development: {
69 | // host: "127.0.0.1", // Localhost (default: none)
70 | // port: 8545, // Standard Ethereum port (default: none)
71 | // network_id: "*", // Any network (default: none)
72 | // },
73 | //
74 | // An additional network, but with some advanced options…
75 | // advanced: {
76 | // port: 8777, // Custom port
77 | // network_id: 1342, // Custom network
78 | // gas: 8500000, // Gas sent with each transaction (default: ~6700000)
79 | // gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei)
80 | // from: , // Account to send transactions from (default: accounts[0])
81 | // websocket: true // Enable EventEmitter interface for web3 (default: false)
82 | // },
83 | //
84 | // Useful for deploying to a public network.
85 | // Note: It's important to wrap the provider as a function to ensure truffle uses a new provider every time.
86 | // goerli: {
87 | // provider: () => new HDWalletProvider(MNEMONIC, `https://goerli.infura.io/v3/${PROJECT_ID}`),
88 | // network_id: 5, // Goerli's id
89 | // confirmations: 2, // # of confirmations to wait between deployments. (default: 0)
90 | // timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50)
91 | // skipDryRun: true // Skip dry run before migrations? (default: false for public nets )
92 | // },
93 | //
94 | // Useful for private networks
95 | // private: {
96 | // provider: () => new HDWalletProvider(MNEMONIC, `https://network.io`),
97 | // network_id: 2111, // This network is yours, in the cloud.
98 | // production: true // Treats this network as if it was a public net. (default: false)
99 | // }
100 | },
101 |
102 | // Set default mocha options here, use special reporters, etc.
103 | mocha: {
104 | // timeout: 100000
105 | },
106 |
107 | // Configure your compilers
108 | compilers: {
109 | solc: {
110 | version: "0.8.18", // Fetch exact version from solc-bin (default: truffle's version)
111 | // docker: true, // Use "0.5.1" you've installed locally with docker (default: false)
112 | // settings: { // See the solidity docs for advice about optimization and evmVersion
113 | // optimizer: {
114 | // enabled: false,
115 | // runs: 200
116 | // },
117 | // evmVersion: "byzantium"
118 | // }
119 | }
120 | },
121 |
122 | // Truffle DB is currently disabled by default; to enable it, change enabled:
123 | // false to enabled: true. The default storage location can also be
124 | // overridden by specifying the adapter settings, as shown in the commented code below.
125 | //
126 | // NOTE: It is not possible to migrate your contracts to truffle DB and you should
127 | // make a backup of your artifacts to a safe location before enabling this feature.
128 | //
129 | // After you backed up your artifacts you can utilize db by running migrate as follows:
130 | // $ truffle migrate --reset --compile-all
131 | //
132 | // db: {
133 | // enabled: false,
134 | // host: "127.0.0.1",
135 | // adapter: {
136 | // name: "indexeddb",
137 | // settings: {
138 | // directory: ".db"
139 | // }
140 | // }
141 | // }
142 | };
143 |
--------------------------------------------------------------------------------