-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: write initial version of frontend
- Loading branch information
Showing
14 changed files
with
428 additions
and
22 deletions.
There are no files selected for viewing
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
}) |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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: { | ||
/* ... */ | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
Oops, something went wrong.