-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4 from chain-notes-brussels/frontend
Frontend
- Loading branch information
Showing
10 changed files
with
482 additions
and
50 deletions.
There are no files selected for viewing
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 |
---|---|---|
@@ -1,80 +1,95 @@ | ||
# 🏗 Scaffold-ETH 2 | ||
# 🏗 Chain Notes | ||
|
||
<h4 align="center"> | ||
<a href="https://docs.scaffoldeth.io">Documentation</a> | | ||
<a href="https://scaffoldeth.io">Website</a> | ||
<a href="https://github.com/chain-notes-brussels/chain-notes-snap">Repo</a> | | ||
<a href="https://chain-notes.vercel.app/">Website</a> | ||
</h4> | ||
|
||
🧪 An open-source, up-to-date toolkit for building decentralized applications (dapps) on the Ethereum blockchain. It's designed to make it easier for developers to create and deploy smart contracts and build user interfaces that interact with those contracts. | ||
🫰 Always falling for rugs and scams? Afraid to claim airdrops because you're unsure if the contract is safe? Chain Notes provides context about a contract, before a transaction. The context can show if a contract is malicious or safe, or provide other insightful information. What is shown is based on notes that are linked to an address, and voted on by the community. The voting logic is inspired on Twitter/X's implementation of Community Notes and [Vitalik's blog post](https://vitalik.eth.limo/general/2023/08/16/communitynotes.html). | ||
|
||
⚙️ Built using NextJS, RainbowKit, Foundry, Wagmi, Viem, and Typescript. | ||
⚙️ Built using NextJS, Foundry, Metamask Snap, WorldCoin and The Graph. | ||
|
||
- ✅ **Contract Hot Reload**: Your frontend auto-adapts to your smart contract as you edit it. | ||
- 🪝 **[Custom hooks](https://docs.scaffoldeth.io/hooks/)**: Collection of React hooks wrapper around [wagmi](https://wagmi.sh/) to simplify interactions with smart contracts with typescript autocompletion. | ||
- 🧱 [**Components**](https://docs.scaffoldeth.io/components/): Collection of common web3 components to quickly build your frontend. | ||
- 🔥 **Burner Wallet & Local Faucet**: Quickly test your application with a burner wallet and local faucet. | ||
- 🔐 **Integration with Wallet Providers**: Connect to different wallet providers and interact with the Ethereum network. | ||
- ✅ **User friendly UX**: Our Metamask Snap shows the most useful note/context before the user approves a transaction, to ensure the user does not interact with any malicious contracts. | ||
- 🧱 **Sybil resistant**: Using Worldcoin WorldID ensures that every person can only vote once on a note. | ||
- ⛓️ **Available on all chains**: Chain Notes is available on all EVM chains! | ||
|
||
![Debug Contracts tab](https://github.com/scaffold-eth/scaffold-eth-2/assets/55535804/b237af0c-5027-4849-a5c1-2e31495cccb1) | ||
|
||
## Requirements | ||
## Diagram | ||
|
||
Before you begin, you need to install the following tools: | ||
![dashboard 1](assets/diagram.png) | ||
|
||
- [Node (>= v18.17)](https://nodejs.org/en/download/) | ||
- Yarn ([v1](https://classic.yarnpkg.com/en/docs/install/) or [v2+](https://yarnpkg.com/getting-started/install)) | ||
- [Git](https://git-scm.com/downloads) | ||
## Screenshots | ||
|
||
## Quickstart | ||
| Landing page | Metamask Snap | | ||
| --------------------------------- | --------------------------------- | | ||
| ![dashboard 1](screenshots/1.png) | ![dashboard 2](screenshots/2.png) | | ||
|
||
To get started with Scaffold-ETH 2, follow the steps below: | ||
| Create Note | View note | | ||
| --------------------------------- | --------------------------------- | | ||
| ![dashboard 3](screenshots/3.png) | ![dashboard 4](screenshots/4.png) | | ||
|
||
1. Install dependencies if it was skipped in CLI: | ||
|
||
``` | ||
cd my-dapp-example | ||
yarn install | ||
``` | ||
|
||
2. Run a local network in the first terminal: | ||
## Bounties | ||
|
||
``` | ||
yarn chain | ||
``` | ||
### Worldcoin | ||
|
||
This command starts a local Ethereum network using Foundry. The network runs on your local machine and can be used for testing and development. You can customize the network configuration in `packages/foundry/foundry.toml`. | ||
### Base - Best Consumer App UX | ||
|
||
3. On a second terminal, deploy the test contract: | ||
Our dApp is deployed on Base testnet. Our dApp provides context to contracts before approving a transaction, which can be used to warn users about malicious contracts. We think this is a great improvement to UX, since it's not intrusive and keeps users informed and safe. | ||
|
||
``` | ||
yarn deploy | ||
``` | ||
### Arbitrum - Best General Project Built on Arbitrum Technology | ||
|
||
This command deploys a test smart contract to the local network. The contract is located in `packages/foundry/contracts` and can be modified to suit your needs. The `yarn deploy` command uses the deploy script located in `packages/foundry/script` to deploy the contract to the network. You can also customize the deploy script. | ||
Deployed our project on Arbitrum testnet. | ||
|
||
4. On a third terminal, start your NextJS app: | ||
### Metamask Linea | ||
|
||
``` | ||
yarn start | ||
``` | ||
We created a metamask snap ([in a seperate repo](https://github.com/chain-notes-brussels/snap)) and deployed to Linea testnet. | ||
|
||
Visit your app on: `http://localhost:3000`. You can interact with your smart contract using the `Debug Contracts` page. You can tweak the app config in `packages/nextjs/scaffold.config.ts`. | ||
### Morph | ||
|
||
Run smart contract test with `yarn foundry:test` | ||
We deployed our contracts on Morph Holesky and manually added the config for the frontend, since this is not available yet in viem and scaffold. Bridging tokens to Morph and deploying was very straightforward. Only hickup we encountered was that we had to manually add network config on the frontend. | ||
|
||
- Edit your smart contract `YourContract.sol` in `packages/foundry/contracts` | ||
- Edit your frontend homepage at `packages/nextjs/app/page.tsx`. For guidance on [routing](https://nextjs.org/docs/app/building-your-application/routing/defining-routes) and configuring [pages/layouts](https://nextjs.org/docs/app/building-your-application/routing/pages-and-layouts) checkout the Next.js documentation. | ||
- Edit your deployment scripts in `packages/foundry/script` | ||
- [Manual network config](https://github.com/chain-notes-brussels/chain-notes-snap/blob/d71b19303b4d260dab5b9d66f40d89af665c750b/packages/nextjs/utils/scaffold-eth/morechains.ts) | ||
|
||
### Scroll - Best Project on Scroll | ||
|
||
## Documentation | ||
### Neon EVM - Best Use Case of Neon EVM | ||
|
||
Visit our [docs](https://docs.scaffoldeth.io) to learn how to start building with Scaffold-ETH 2. | ||
We deployed our contracts to the Neon EVM. | ||
|
||
To know more about its features, check out our [website](https://scaffoldeth.io). | ||
### ApeCoin - Best Dapp | ||
|
||
## Contributing to Scaffold-ETH 2 | ||
We deployed our contracts to the ApeChain. Also we added custom config for the network on the frontend, since it was not available in viem and scaffold by default. | ||
- [Manual network config](https://github.com/chain-notes-brussels/chain-notes-snap/blob/d71b19303b4d260dab5b9d66f40d89af665c750b/packages/nextjs/utils/scaffold-eth/morechains.ts) | ||
|
||
We welcome contributions to Scaffold-ETH 2! | ||
|
||
Please see [CONTRIBUTING.MD](https://github.com/scaffold-eth/scaffold-eth-2/blob/main/CONTRIBUTING.md) for more information and guidelines for contributing to Scaffold-ETH 2. | ||
### Zerion - Best build on ZERϴ Network | ||
|
||
We deployed our contracts on Zerio testnet. Also we added custom config for the network on the frontend, since it was not available in viem and scaffold by default. | ||
- [Manual network config](https://github.com/chain-notes-brussels/chain-notes-snap/blob/d71b19303b4d260dab5b9d66f40d89af665c750b/packages/nextjs/utils/scaffold-eth/morechains.ts) | ||
|
||
### Zircuit - Best Project on Zircuit | ||
|
||
We deployed our contracts on Zircuit and manually added the config for the frontend, since this is not available yet in viem and scaffold. Getting testnet tokens and deploying on Zircuit was very smooth. Only hickup we encountered was that we had to manually add network config on the frontend. | ||
|
||
- [Zircuit manual network config](https://github.com/chain-notes-brussels/chain-notes-snap/blob/d71b19303b4d260dab5b9d66f40d89af665c750b/packages/nextjs/utils/scaffold-eth/morechains.ts) | ||
|
||
|
||
## Links | ||
|
||
- [Live dApp on Vercel](https://chain-notes.vercel.app/) | ||
- [Github: Solidity contract and frontend dashboard](https://github.com/chain-notes-brussels/chain-notes-snap) | ||
- [Github: Metamask Snap](https://github.com/chain-notes-brussels/snap) | ||
|
||
### Deployed contracts | ||
- [Notes.sol on Sepolia](https://sepolia.etherscan.io/address/0x62a4d5b0f16d8eb9065310afbb7f2622d981f124) | ||
|
||
### References | ||
- [Vitalik blog - What do I think about Community Notes?](https://vitalik.eth.limo/general/2023/08/16/communitynotes.html) | ||
|
||
## Team | ||
|
||
- [arjanjohan](https://x.com/arjanjohan/) | ||
- [0xjsi.eth](https://twitter.com/0xjsieth) | ||
- [Aleksandre Tsetskhladze](https://twitter.com/atsetsoffc) |
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,83 @@ | ||
"use client"; | ||
|
||
import { useState } from "react"; | ||
import type { NextPage } from "next"; | ||
import { useAccount } from "wagmi"; | ||
import { AddressInput } from "~~/components/scaffold-eth"; | ||
import { useDeployedContractInfo, useScaffoldWriteContract } from "~~/hooks/scaffold-eth"; | ||
|
||
const CreateNote: NextPage = () => { | ||
const [address, setAddress] = useState(""); | ||
const [noteContent, setNoteContent] = useState(""); | ||
const [isDanger, setIsDanger] = useState(false); | ||
const { address: connectedAccount } = useAccount(); | ||
const { data: NotesContractInfo } = useDeployedContractInfo("Notes"); | ||
|
||
const { writeContractAsync: writeNotesContractAsync } = useScaffoldWriteContract("Notes"); | ||
|
||
return ( | ||
<div className="container mx-auto p-8"> | ||
<h1 className="text-center mb-4 mt-5"> | ||
<span className="block text-4xl font-bold">Create a Note</span> | ||
</h1> | ||
<div className="flex flex-col items-center"> | ||
<div className="w-full max-w-lg bg-base-100 shadow-lg shadow-secondary border-8 border-secondary rounded-xl p-8 m-8"> | ||
<div className="flex flex-col text-center mb-4"> | ||
<span className="text-2xl font-semibold">Create Note</span> | ||
|
||
</div> | ||
<div className="flex flex-col space-y-4"> | ||
<div className="flex flex-col items-center"> | ||
<span className="w-full"> | ||
Address{" "} | ||
<AddressInput value={address} onChange={value => setAddress(value)} placeholder="Address" /> | ||
</span> | ||
</div> | ||
<div className="flex flex-col items-center"> | ||
<span className="w-full"> | ||
Note Content{" "} | ||
<textarea | ||
className="textarea textarea-bordered w-full" | ||
value={noteContent} | ||
onChange={e => setNoteContent(e.target.value)} | ||
placeholder="Note Content" | ||
/> | ||
</span> | ||
</div> | ||
<div className="flex flex-col items-center"> | ||
<label className="label cursor-pointer"> | ||
<span className="label-text">Danger/Warning </span> | ||
|
||
<input | ||
type="checkbox" | ||
className="toggle toggle-error" | ||
checked={isDanger} | ||
onChange={e => setIsDanger(e.target.checked)} | ||
/> | ||
</label> | ||
</div> | ||
<div className="flex flex-col items-center mt-4"> | ||
<button | ||
className="btn btn-primary" | ||
onClick={async () => { | ||
try { | ||
await writeNotesContractAsync({ | ||
functionName: "publishNote", | ||
args: [address, noteContent, isDanger], | ||
}); | ||
} catch (err) { | ||
console.error("Error calling create function", err); | ||
} | ||
}} | ||
> | ||
Create Note | ||
</button> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
export default CreateNote; |
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,124 @@ | ||
"use client"; | ||
|
||
import { useEffect, useState } from "react"; | ||
import { useParams } from "next/navigation"; | ||
import type { NextPage } from "next"; | ||
import { useScaffoldReadContract, useScaffoldWriteContract } from "~~/hooks/scaffold-eth"; | ||
import { Address } from "~~/components/scaffold-eth"; | ||
import { isAddress } from "viem"; | ||
|
||
const ViewNote: NextPage = () => { | ||
const { address } = useParams(); | ||
const [notes, setNotes] = useState<any[]>([]); | ||
const [validAddress, setValidAddress] = useState(false); | ||
const [ratings, setRatings] = useState<number[]>([]); // Array to store selected ratings | ||
|
||
const { data: notesData, refetch } = useScaffoldReadContract({ | ||
contractName: "Notes", | ||
functionName: "retrieveContractNotes", | ||
args: [address], | ||
}); | ||
|
||
const { writeContractAsync: writeNotesContract, isPending } = useScaffoldWriteContract("Notes"); | ||
|
||
useEffect(() => { | ||
if (address && isAddress(address as string)) { | ||
setValidAddress(true); | ||
refetch(); | ||
} else { | ||
setValidAddress(false); | ||
} | ||
}, [address, refetch]); | ||
|
||
useEffect(() => { | ||
if (notesData) { | ||
setNotes(notesData); | ||
setRatings(new Array(notesData.length).fill(0)); // Initialize ratings array with default value 0 (HELPFUL) | ||
} | ||
}, [notesData]); | ||
|
||
const handleRatingChange = (index: number, rating: number) => { | ||
const newRatings = [...ratings]; | ||
newRatings[index] = rating; | ||
setRatings(newRatings); | ||
}; | ||
|
||
const handleVote = async (noteIndex: number, rating: number) => { | ||
const noteAddress = address; // Assuming the address is linked to the note | ||
try { | ||
console.log("Submitting vote..."); | ||
await writeNotesContract({ | ||
functionName: "vote", | ||
args: [rating, BigInt(noteIndex), noteAddress], | ||
}); | ||
console.log("Vote submitted successfully"); | ||
} catch (error) { | ||
console.error("Error submitting vote:", error); | ||
} | ||
}; | ||
|
||
return ( | ||
<div className="container mx-auto p-8"> | ||
<h1 className="text-center mb-4 mt-5"> | ||
<span className="block text-4xl font-bold">View Note</span> | ||
|
||
<div className="flex justify-center items-center space-x-2 flex-col sm:flex-row"> | ||
<p className="my-2 font-medium">Contract Address:</p> | ||
<Address address={address} /> | ||
</div> | ||
</h1> | ||
{validAddress ? ( | ||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4"> | ||
{notes | ||
.sort((a, b) => b.score - a.score) // Sort notes, highest score first | ||
.map((note, index) => ( | ||
<div key={index} className="bg-base-100 shadow-lg shadow-secondary border-8 border-secondary rounded-xl p-8"> | ||
<div className="flex flex-col mb-4"> | ||
<p className="font-medium my-0 break-words mb-2">{note.sentiment ? "✅ Positive note" : "❌ Negative note"}</p> | ||
<div className="bg-secondary rounded-3xl text-sm px-4 py-1.5 break-words overflow-auto"> | ||
<pre className="whitespace-pre-wrap break-words">{note.uri}</pre> | ||
</div> | ||
</div> | ||
<div className="flex flex-col mb-4"> | ||
<span className="block text-xl font-semibold mb-2">Score</span> | ||
<p>{note.score}</p> | ||
</div> | ||
<div className="flex flex-col mb-4"> | ||
<span className="block text-xl font-semibold mb-2">Rate this note</span> | ||
<div className="flex"> | ||
<button | ||
onClick={() => handleVote(index, 0)} | ||
className="btn flex-1 rounded-none first:rounded-l-lg last:rounded-r-lg" | ||
disabled={isPending} | ||
> | ||
HELPFUL | ||
</button> | ||
<button | ||
onClick={() => handleVote(index, 2)} | ||
className="btn flex-1 rounded-none first:rounded-l-lg last:rounded-r-lg" | ||
disabled={isPending} | ||
> | ||
SOMEWHAT_HELPFUL | ||
</button> | ||
<button | ||
onClick={() => handleVote(index, 1)} | ||
className="btn flex-1 rounded-none first:rounded-l-lg last:rounded-r-lg" | ||
disabled={isPending} | ||
> | ||
NOT_HELPFUL | ||
</button> | ||
</div> | ||
</div> | ||
</div> | ||
))} | ||
</div> | ||
) : ( | ||
<div className="text-center"> | ||
<p className="text-red-500">Invalid address. Please check the URL.</p> | ||
</div> | ||
)} | ||
</div> | ||
); | ||
}; | ||
|
||
export default ViewNote; |
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,44 @@ | ||
"use client"; | ||
|
||
import { useState } from "react"; | ||
import Link from "next/link"; | ||
import { isAddress } from "viem"; | ||
import { AddressInput } from "~~/components/scaffold-eth"; | ||
|
||
const ViewPage = () => { | ||
const [inputAddress, setInputAddress] = useState(""); | ||
const [validAddress, setValidAddress] = useState(false); | ||
|
||
const handleAddressChange = (value: string) => { | ||
setInputAddress(value); | ||
setValidAddress(isAddress(value)); | ||
}; | ||
|
||
return ( | ||
<div className="container mx-auto p-8"> | ||
<h1 className="text-center mb-4 mt-5"> | ||
<span className="block text-4xl font-bold">View Notes</span> | ||
</h1> | ||
<div className="flex flex-col items-center"> | ||
<div className="w-full max-w-lg bg-base-100 shadow-lg shadow-secondary border-8 border-secondary rounded-xl p-8 m-8"> | ||
<div className="flex flex-col items-center mb-4"> | ||
<span className="block text-xl font-semibold mb-2">Enter Address</span> | ||
<AddressInput value={inputAddress} onChange={handleAddressChange} placeholder="Address" /> | ||
</div> | ||
<div className="flex flex-col items-center mt-4"> | ||
<Link href={validAddress ? `/view/${inputAddress}` : "#"} passHref> | ||
<button | ||
className={`btn btn-primary ${!validAddress && 'btn-disabled'}`} | ||
disabled={!validAddress} | ||
> | ||
View Note | ||
</button> | ||
</Link> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
export default ViewPage; |
Oops, something went wrong.