Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
1w2w1qw committed Aug 13, 2023
1 parent f80077b commit 532d4d5
Show file tree
Hide file tree
Showing 10 changed files with 987 additions and 82 deletions.
598 changes: 593 additions & 5 deletions package-lock.json

Large diffs are not rendered by default.

22 changes: 15 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
{
"name": "voting",
"homepage": "https://github.com/JimHacker1980/voting",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.17.0",
"@emotion/react": "^11.10.6",
"@emotion/styled": "^11.10.6",
"@mui/material": "^5.11.16",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^27.5.2",
"@types/node": "^16.18.40",
"@types/react": "^18.2.20",
"@types/react-dom": "^18.2.7",
"@types/node": "^16.18.23",
"@types/react": "^18.0.34",
"@types/react-dom": "^18.0.11",
"gh-pages": "^5.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"scrypt-ts": "^1.3.2",
"scrypt-ts": "latest",
"typescript": "^4.9.5",
"web-vitals": "^2.1.4"

},
"scripts": {
"start": "react-app-rewired start",
Expand All @@ -26,7 +32,9 @@
"build:contract": "npx scrypt-cli compile",
"deploy:contract": "npx ts-node ./scripts/deploy.ts",
"verify:contract": "npx scrypt-cli verify $(cat .scriptHash) ./src/contracts/voting.ts",
"genprivkey": "npx ts-node ./scripts/privateKey.ts"
"genprivkey": "npx ts-node ./scripts/privateKey.ts",
"predeploy": "npm run build",
"deploy": "gh-pages -d build"
},
"eslintConfig": {
"extends": [
Expand All @@ -50,4 +58,4 @@
"node-polyfill-webpack-plugin": "^2.0.1",
"react-app-rewired": "^2.2.1"
}
}
}
Binary file added public/He.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/Yann.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
53 changes: 20 additions & 33 deletions scripts/deploy.ts
Original file line number Diff line number Diff line change
@@ -1,47 +1,34 @@
import { writeFileSync } from 'fs'
import { Voting } from '../src/contracts/voting'
import { privateKey } from './privateKey'
import { bsv, TestWallet, DefaultProvider, sha256 } from 'scrypt-ts'

function getScriptHash(scriptPubKeyHex: string) {
const res = sha256(scriptPubKeyHex).match(/.{2}/g)
if (!res) {
throw new Error('scriptPubKeyHex is not of even length')
}
return res.reverse().join('')
}
import { Name, Voting, N } from '../src/contracts/voting'
import { bsv, TestWallet, DefaultProvider, toByteString, FixedArray } from 'scrypt-ts'

import * as dotenv from 'dotenv'

dotenv.config()

const privateKey = bsv.PrivateKey.fromWIF(process.env.PRIVATE_KEY || '')


const signer = new TestWallet(privateKey, new DefaultProvider({
network: bsv.Networks.testnet
}))

async function main() {
await Voting.compile()

// Prepare signer.
// See https://scrypt.io/docs/how-to-deploy-and-call-a-contract/#prepare-a-signer-and-provider
const signer = new TestWallet(privateKey, new DefaultProvider({
network: bsv.Networks.testnet
}))

// TODO: Adjust the amount of satoshis locked in the smart contract:
const amount = 100
const candidateNames: FixedArray<Name, typeof N> = [
toByteString('Math', true),
toByteString('Physics', true)
]

const instance = new Voting(
// TODO: Pass constructor parameter values.
0n
candidateNames
)

// Connect to a signer.
await instance.connect(signer)

// Contract deployment.
const amount = 1
const deployTx = await instance.deploy(amount)

// Save deployed contracts script hash.
const scriptHash = getScriptHash(instance.lockingScript.toHex())
const shFile = `.scriptHash`;
writeFileSync(shFile, scriptHash);

console.log('Voting contract was successfully deployed!')
console.log(`TXID: ${deployTx.id}`)
console.log(`scriptHash: ${scriptHash}`)
console.log('Voting contract deployed: ', deployTx.id)
}

main()
269 changes: 254 additions & 15 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,263 @@
import React from 'react';
import logo from './logo.svg';
import './App.css';
import React, { useEffect, useRef, useState } from "react";
import "./App.css";
import {
TableContainer,
Table,
TableHead,
TableRow,
TableCell,
TableBody,
Paper,
Button,
Snackbar,
Alert,
Link,
Typography,
Box,
Divider
} from "@mui/material";
import {
Scrypt,
ScryptProvider,
SensiletSigner,
ContractCalledEvent,
ByteString,
} from "scrypt-ts";
import { Voting } from "./contracts/voting";
import Footer from "./Footer";

// `npm run deploycontract` to get deployment transaction id
const contract_id = {
/** The deployment transaction id */
txId: "bcf3b90a08e8b5c55f773db853bd8c23c955bb710c7d95aab1d10b384304b431",
/** The output index */
outputIndex: 0,
};

function byteString2utf8(b: ByteString) {
return Buffer.from(b, "hex").toString("utf8");
}

function App() {
const [votingContract, setContract] = useState<Voting>();
const signerRef = useRef<SensiletSigner>();
const [error, setError] = React.useState("");
const [success, setSuccess] = React.useState<{
txId: string;
candidate: string;
}>({
txId: "",
candidate: "",
});

async function fetchContract() {
try {
const instance = await Scrypt.contractApi.getLatestInstance(
Voting,
contract_id
);
setContract(instance);
} catch (error: any) {
console.error("fetchContract error: ", error);
setError(error.message);
}
}

useEffect(() => {
const provider = new ScryptProvider();
const signer = new SensiletSigner(provider);

signerRef.current = signer;

fetchContract();

const subscription = Scrypt.contractApi.subscribe(
{
clazz: Voting,
id: contract_id,
},
(event: ContractCalledEvent<Voting>) => {
setSuccess({
txId: event.tx.id,
candidate: event.args[0] as ByteString,
});
setContract(event.nexts[0]);
}
);

return () => {
subscription.unsubscribe();
};
}, []);

const handleClose = (
_event: React.SyntheticEvent | Event,
reason?: string
) => {
if (reason === "clickaway") {
return;
}
setError("");
};

const handleSuccessClose = (
_event: React.SyntheticEvent | Event,
reason?: string
) => {
if (reason === "clickaway") {
return;
}
setSuccess({
txId: "",
candidate: "",
});
};

async function voting(e: any) {
handleSuccessClose(e);
const signer = signerRef.current as SensiletSigner;

if (votingContract && signer) {
const { isAuthenticated, error } = await signer.requestAuth();
if (!isAuthenticated) {
throw new Error(error);
}

await votingContract.connect(signer);

// create the next instance from the current
const nextInstance = votingContract.next();

const candidateName = e.target.name;

// update state
nextInstance.increaseVotesReceived(candidateName);

// call the method of current instance to apply the updates on chain
votingContract.methods
.vote(candidateName, {
next: {
instance: nextInstance,
balance: votingContract.balance,
},
})
.then((result) => {
console.log(`Voting call tx: ${result.tx.id}`);
})
.catch((e) => {
setError(e.message);
fetchContract();
console.error("call error: ", e);
});
}
}

return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.tsx</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
<h2>What's your favorite professor in computer vision?</h2>
</header>
<TableContainer
component={Paper}
variant="outlined"
style={{ width: 1200, height: "80vh", margin: "auto" }}
>
<Table>
<TableHead>
<TableRow>
<TableCell align="center">Kaiming He</TableCell>
<TableCell align="center">Yann LeCun</TableCell>
</TableRow>
</TableHead>
<TableBody>
<TableRow>
<TableCell align="center">
<Box>
<Box
sx={{
height: 200,
}}
component="img"
alt={"Kaiming He"}
src={`${process.env.PUBLIC_URL}/${"He"}.jpg`}
/>
</Box>
</TableCell>
<TableCell align="center">
<Box>
<Box
sx={{
height: 200,
}}
component="img"
alt={"Yann LeCun"}
src={`${process.env.PUBLIC_URL}/${"Yann"}.jpg`}
/>
</Box>
</TableCell>
</TableRow>
<TableRow>
<TableCell align="center">
<Box>
<Typography variant={"h1"} >
{votingContract?.candidates[0].votesReceived.toString()}
</Typography>
<Button
variant="text"
onClick={voting}
name={votingContract?.candidates[0].name}
>
👍
</Button>
</Box>
</TableCell>

<TableCell align="center">
<Divider orientation="vertical" flexItem />
<Box>
<Typography variant={"h1"}>
{votingContract?.candidates[1].votesReceived.toString()}
</Typography>
<Button
variant="text"
onClick={voting}
name={votingContract?.candidates[1].name}
>
👍
</Button>
</Box>
</TableCell>
</TableRow>
</TableBody>
</Table>
</TableContainer>
<Footer />
<Snackbar
open={error !== ""}
autoHideDuration={6000}
onClose={handleClose}
>
<Alert severity="error">{error}</Alert>
</Snackbar>

<Snackbar
open={success.candidate !== "" && success.txId !== ""}
autoHideDuration={6000}
onClose={handleSuccessClose}
>
<Alert severity="success">
{" "}
<Link
href={`https://test.whatsonchain.com/tx/${success.txId}`}
target="_blank"
rel="noreferrer"
>
{`"${byteString2utf8(success.candidate)}" got one vote, tx: ${
success.txId
}`}
</Link>
</Alert>
</Snackbar>
</div>
);
}
Expand Down
Loading

0 comments on commit 532d4d5

Please sign in to comment.