Skip to content

Commit

Permalink
feat: write initial version of frontend
Browse files Browse the repository at this point in the history
  • Loading branch information
xtyrrell committed Apr 25, 2021
1 parent a1842de commit c335b0e
Show file tree
Hide file tree
Showing 14 changed files with 428 additions and 22 deletions.
File renamed without changes.
File renamed without changes.
File renamed without changes.
6 changes: 6 additions & 0 deletions smart-contracts/hardhat.config.js → hardhat.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,10 @@ require('@nomiclabs/hardhat-waffle')
*/
module.exports = {
solidity: "0.7.3",
networks: {
hardhat: {
// https://hardhat.org/metamask-issue.html
chainId: 1337
},
}
};
4 changes: 2 additions & 2 deletions smart-contracts/package-lock.json → package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

39 changes: 39 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"name": "ethereum-box",
"version": "1.0.0",
"description": "A simple but test-complete example of an end-to-end Etherum dApp implementation.",
"scripts": {
"test": "hardhat test",
"build": "snowpack build",
"start": "snowpack dev"
},
"repository": {
"type": "git",
"url": "git+ssh://[email protected]/xtyrrell/ethereum-box.git"
},
"keywords": [
"solidity",
"ethereum",
"smart-contract",
"dapp"
],
"author": "xtyrrell",
"license": "MIT",
"bugs": {
"url": "https://github.com/xtyrrell/ethereum-box/issues"
},
"homepage": "https://github.com/xtyrrell/ethereum-box#readme",
"devDependencies": {
"@snowpack/plugin-sass": "^1.4.0",
"snowpack": "^3.3.5",
"@nomiclabs/hardhat-ethers": "^2.0.2",
"@nomiclabs/hardhat-waffle": "^2.0.1",
"@openzeppelin/contracts": "^3.0.1",
"chai": "^4.3.4",
"ethereum-waffle": "^3.3.0",
"hardhat": "^2.2.1"
},
"dependencies": {
"ethers": "^5.1.3"
}
}
File renamed without changes.
20 changes: 20 additions & 0 deletions scripts/set-values.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// This is an example of interacting with the Box contract through ethers
async function main() {
// You'll need to change this to the deployed contract address, which is output
// when running scripts/deploy.js
const CONTRACT_ADDRESS = "0x5fbdb2315678afecb367f032d93f642f64180aa3"

const Box = await ethers.getContractFactory("Box")

const box = await Box.attach(CONTRACT_ADDRESS)

await box.storePublic(10)
await box.storeRestricted(99)
}

main()
.then(() => process.exit(0))
.catch(error => {
console.error(error)
process.exit(1)
})
20 changes: 0 additions & 20 deletions smart-contracts/package.json

This file was deleted.

21 changes: 21 additions & 0 deletions snowpack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Snowpack Configuration File
// See all supported options: https://www.snowpack.dev/reference/configuration

/** @type {import("snowpack").SnowpackUserConfig } */
module.exports = {
mount: {
"src": "/",
},
plugins: [
"@snowpack/plugin-sass",
],
packageOptions: {
/* ... */
},
devOptions: {
/* ... */
},
buildOptions: {
/* ... */
},
};
55 changes: 55 additions & 0 deletions src/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css" />
<title>The Ethereum Box dApp</title>
</head>
<body>
<h1>The Ethereum Box dApp 📦</h1>
<p>We're interacting live with a contract on the Ethereum blockchain. ⚡🛡️🔗</p>
<p>The contract we're connected to has address <span class="value contract-address"></span> on network <span class="value network"></span>.</p>

<div class="reading-values">
<h2>Reading values <button class="reload-values">Reload values</button></h2>
<p>In the Ethereum Box <code>Box.sol</code> smart contract, there are two <code>uint256</code> state variables:</p>

<p>The value of <code>publicValue</code> is <span class="readPublicValue value"></span>.</p>
<p>The value of <code>restrictedValue</code> is <span class="readRestrictedValue value"></span>.</p>
</div>

<div class="writing-values">
<h2>Writing values</h2>
<p>Now, let's try set these values.</p>

<form class="writePublicValue">
<p>The <code>storePublic(uint256 newValue) public</code> function is public and has no access control, so can use any account to set this value.</p>
<p>Set value of <code>publicValue</code>:
<input type="text" class="value-input" placeholder="New value" />
<input type="submit" value="Set value" />
</p>
</form>

<form class="writeRestrictedValue">
<p>The <code>storeRestricted(uint256 newValue) public onlyOwner</code> function is access-restricted, so you'll need to be the contract owner to set this value.</p>
<p>Set value of <code>restrictedValue</code>:
<input type="text" class="value-input" placeholder="New value" />
<input type="submit" value="Set value" />
</p>
</form>

<p class="notice">When you've confirmed the transaction, click Reload Values above to see your changes take effect.</p>
</div>

<footer>
<hr />

<p>Debug information:</p>
<pre id="debug"></pre>
</footer>

<script type="module" src="script.js"></script>
</body>
</html>
142 changes: 142 additions & 0 deletions src/script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import { ethers } from "ethers";

// TODO: Pull this from artifacts. don't hardcode it
const CONTRACT_ADDRESS = "0x5FbDB2315678afecb367f032d93F642f64180aa3";
const NETWORK = "http://127.0.0.1:8545";

// ---
// utilities
// ---

const debugDiv = document.getElementById("debug")
const log = (...args) => {
debugDiv.innerText += args.map(x => `> ${typeof x === "string" ? x : JSON.stringify(x)}\n`).join("")
console.log(...args)
}

log("using ethers:", ethers);
log("using window.ethereum:", window.ethereum)

// ---
// the main action
// ---

async function setContractInfoInDom() {
document.querySelectorAll(".value.network").forEach(node => node.textContent = NETWORK)
document.querySelectorAll(".value.contract-address").forEach(node => node.textContent = CONTRACT_ADDRESS)
}

async function readValuesIntoDom(readOnlyContract) {
// first, clear the values if they already exist
document.querySelectorAll(".readPublicValue").forEach(node => node.textContent = "")
document.querySelectorAll(".readRestrictedValue").forEach(node => node.textContent = "")

const retrievedPublicValue = (await readOnlyContract.retrievePublic()).toString()
log("retrieved public retrievedPublicValue", retrievedPublicValue)

const retrievedRestrictedValue = (await readOnlyContract.retrieveRestricted()).toString()
log("retrieved public retrievedRestrictedValue", retrievedRestrictedValue)

document.querySelectorAll(".readPublicValue").forEach(node => node.textContent = retrievedPublicValue)
document.querySelectorAll(".readRestrictedValue").forEach(node => node.textContent = retrievedRestrictedValue)
}

async function setPublicValue(contract) {
const newPublicValue = document.querySelector(".writePublicValue .value-input").value

log("new public value in form", newPublicValue)
log("write public value response", await contract.storePublic(newPublicValue))

document.querySelector(".writePublicValue .value-input").value = ""
}

async function setRestrictedValue(contract) {
const newRestrictedValue = document.querySelector(".writeRestrictedValue .value-input").value

log("new restricted value in form", newRestrictedValue)

try {
log("write restricted value response", await contract.storeRestricted(newRestrictedValue))
} catch (e) {
const notAuthorised = e?.data?.message?.includes("Ownable: caller is not the owner")

if (notAuthorised) {
alert("Your attempt to change this value was rolled back because you didn't pass the access control check. Only the account that deployed this contract can change `restrictedValue`.")
} else {
alert("Oops! There was an issue running this transaction: " + e.data.message)
}
}

document.querySelector(".writeRestrictedValue .value-input").value = ""
}

async function requestSignerAccounts() {
// await window.ethereum.enable()
await ethereum.send('eth_requestAccounts')

// A Web3Provider wraps a standard Web3 provider, which is
// what Metamask injects as window.ethereum into each page
const provider = new ethers.providers.Web3Provider(window.ethereum)

// The Metamask plugin also allows signing transactions to
// send ether and pay to change state within the blockchain.
// For this, you need the account signer...
const signer = provider.getSigner()

return signer
}

// get a provider/signer
// connect to contract by giving it an ABI description, address and provider/signer
// then we can run the contract methods
async function main() {
const directProvider = new ethers.providers.JsonRpcProvider(NETWORK)

// TODO: Symlink an artifacts to `smart-contracts/artifacts` and use the generated ABI rather than this
// hardcoded interface and contract address

const contractInterface = [
'function storePublic(uint256 newValue) public',
'function storeRestricted(uint256 newValue) public',
'function retrievePublic() public view returns (uint256)',
'function retrieveRestricted() public view returns (uint256)'
]

await setContractInfoInDom()

const readOnlyContract = new ethers.Contract(CONTRACT_ADDRESS, contractInterface, directProvider)
readValuesIntoDom(readOnlyContract)

document.querySelectorAll('.reload-values').forEach(node => node.addEventListener('click', () => {
readValuesIntoDom(readOnlyContract)
}))


// ---

const signer = await requestSignerAccounts()
const contract = new ethers.Contract(CONTRACT_ADDRESS, contractInterface, signer)

document.querySelector("form.writePublicValue").addEventListener('submit', event => {
event.preventDefault()
setPublicValue(contract)
})

document.querySelector("form.writeRestrictedValue").addEventListener('submit', event => {
event.preventDefault()
setRestrictedValue(contract)
})

// try {
// await contract.storePublic(9999)
// log('Successfully ran `contract.storePublic(value)`')
// } catch (e) {
// log('Error trying to run `contract.storePublic(value)`')
// }


// await contract.storeRestricted(1010101)
}


main()
Loading

0 comments on commit c335b0e

Please sign in to comment.