From 2fc88f86a70cd27ffe006364608855dccc01c922 Mon Sep 17 00:00:00 2001
From: 1marcghannam <1marc.ghannam@gmail.com>
Date: Tue, 23 May 2023 17:51:22 -0400
Subject: [PATCH 01/11] feat: FAQ and fixes
---
README.md | 5 +-
client/packages/ui/package.json | 2 +-
client/packages/ui/src/App.tsx | 4 +-
client/packages/ui/src/Routes.ts | 3 +-
.../ui/src/api/contracts/readFunctions.ts | 24 +++++++-
.../packages/ui/src/components/faq/index.tsx | 61 +++++++++++++++++++
client/packages/ui/src/components/index.ts | 1 +
.../ui/src/components/navigationBar/index.tsx | 13 ++++
.../raffleCreate/components/create.tsx | 8 ++-
.../raffleDetail/components/detail.tsx | 15 ++---
.../raffleDetail/components/stepManager.tsx | 5 +-
.../packages/ui/src/hooks/useAsyncManager.ts | 3 +-
client/packages/ui/tsconfig.json | 16 +----
13 files changed, 130 insertions(+), 30 deletions(-)
create mode 100644 client/packages/ui/src/components/faq/index.tsx
diff --git a/README.md b/README.md
index 6feb37f..ddfab7a 100644
--- a/README.md
+++ b/README.md
@@ -36,7 +36,8 @@ export ANVIL_PRIVATE_KEY="" # Get from anvil after running for the first time, s
# UI
export UI_RAFFLE_MANAGER_CONTRACT_ADDRESS= # Get from anvil after deploying contract
-export UI_LINK_TOKEN_CONTRACT_ADDRESS=
+export UI_LINK_TOKEN_CONTRACT_ADDRESS=
+export UI_KEEPER_REGISTRY_CONTRACT_ADDRESS=
```
### 4. Setup Wallet
@@ -62,7 +63,7 @@ $ docker compose up
### 1. Setup Foundry
-([Installation instructions](https://book.getfoundry.sh/getting-started/installation)
+[Installation instructions](https://book.getfoundry.sh/getting-started/installation)
```bash
# Download foundry
diff --git a/client/packages/ui/package.json b/client/packages/ui/package.json
index 4362b0e..ccf8e8b 100644
--- a/client/packages/ui/package.json
+++ b/client/packages/ui/package.json
@@ -65,7 +65,7 @@
"react-test-renderer": "^18.2.0",
"string.prototype.replaceall": "^1.0.7",
"ts-jest": "^29.0.5",
- "typescript": "4.9.5",
+ "typescript": "^5.0.4",
"vite": "^2.5.4",
"vite-plugin-react-md": "^1.0.1"
}
diff --git a/client/packages/ui/src/App.tsx b/client/packages/ui/src/App.tsx
index d59b1ea..8fd2690 100644
--- a/client/packages/ui/src/App.tsx
+++ b/client/packages/ui/src/App.tsx
@@ -5,7 +5,7 @@ import { Routes } from '@ui/Routes'
import { RaffleList } from '@ui/features/raffleList'
import { RaffleDetail } from '@ui/features/raffleDetail'
import { RaffleCreate } from '@ui/features/raffleCreate'
-import { AuthenticatedRoute, Hero } from '@ui/components'
+import { AuthenticatedRoute, Hero, FAQ } from '@ui/components'
export const App = () => (
<>
@@ -26,6 +26,8 @@ export const App = () => (
render={({ match }) => }
/>
+
+
diff --git a/client/packages/ui/src/Routes.ts b/client/packages/ui/src/Routes.ts
index 0d18050..82c4edc 100644
--- a/client/packages/ui/src/Routes.ts
+++ b/client/packages/ui/src/Routes.ts
@@ -1,7 +1,8 @@
export const Routes = {
RaffleList: '/',
RaffleDetail: '/raffle/:id',
- RaffleCreate: '/create'
+ RaffleCreate: '/create',
+ FAQ: '/faq'
}
export const createRoute = ({ route, id }) => route.replace(':id', id)
diff --git a/client/packages/ui/src/api/contracts/readFunctions.ts b/client/packages/ui/src/api/contracts/readFunctions.ts
index d32adae..5939786 100644
--- a/client/packages/ui/src/api/contracts/readFunctions.ts
+++ b/client/packages/ui/src/api/contracts/readFunctions.ts
@@ -1,5 +1,6 @@
import { readContract } from '@wagmi/core'
-
+import { useEffect } from 'react'
+import { useContractRead } from 'wagmi'
import { env } from '@ui/config'
import {
transformRaffleItem,
@@ -42,6 +43,27 @@ export const getRaffle = async (id: number): Promise => {
}
}
+export const getRaffleHook = (store, id) => {
+ const { data, isError } = useContractRead({
+ ...defaultOptions,
+ address: raffleManagerContractAddress,
+ functionName: 'getRaffle',
+ args: [id]
+ })
+
+ useEffect(() => {
+ const transformedData = transformRaffleItem(data)
+ if (
+ data &&
+ JSON.stringify(store.state.raffle) !== JSON.stringify(transformedData)
+ ) {
+ store.update({ raffle: transformedData })
+ }
+ }, [data, store])
+
+ return { data, isError }
+}
+
export const getClaimableLink = async (id: number): Promise => {
try {
const data = await readContract({
diff --git a/client/packages/ui/src/components/faq/index.tsx b/client/packages/ui/src/components/faq/index.tsx
new file mode 100644
index 0000000..69fb122
--- /dev/null
+++ b/client/packages/ui/src/components/faq/index.tsx
@@ -0,0 +1,61 @@
+import React from 'react'
+import {
+ Container,
+ Heading,
+ Text,
+ Divider,
+ Box,
+ VStack
+} from '@chakra-ui/react'
+
+const faqList = [
+ {
+ question: 'What is a Chainlink Raffle?',
+ answer:
+ "A Chainlink Raffle is a lottery system built on the blockchain. It uses Chainlink's Verifiable Random Function (VRF) to ensure that the selection of winners is provably fair and transparent."
+ },
+ {
+ question: 'How do I participate in a Chainlink Raffle?',
+ answer:
+ "To participate in a Chainlink Raffle, you first need to register or 'sign up' during the sign up period. Once the raffle event is committed on-chain, you become a participant and stand a chance to win prizes when the raffle is settled."
+ },
+ {
+ question: 'How are the winners selected?',
+ answer:
+ "Winners are selected using Chainlink's Verifiable Random Function (VRF). This is a trusted and secure source of randomness on the blockchain. It ensures that the winner selection process is fair, transparent, and tamper-proof."
+ },
+ {
+ question: 'What is the role of the raffle owner?',
+ answer:
+ 'The raffle owner is responsible for creating and managing the raffle. They commit the rules of the raffle on-chain, such as the number of winners, the prizes, and the sign up period.'
+ },
+ {
+ question: 'What is the difference between Dynamic and Static Raffles?',
+ answer:
+ 'Dynamic Raffles have a level of flexibility as they allow participants to enter at any point before the raffle ends, provided they meet the necessary conditions. This feature provides the potential for a larger participant pool over time. Static Raffles, however, have a predetermined set of participants at the outset of the raffle event. Once the raffle starts, no new entrants can participate. This creates a fixed and unchanging number of entries over time.'
+ }
+]
+
+export const FAQ = () => {
+ return (
+
+
+ What are Chainlink Raffles?
+
+
+ Frequently Asked Questions
+
+
+
+ {faqList.map((faq, index) => (
+
+
+ {faq.question}
+
+ {faq.answer}
+
+ ))}
+
+
+ )
+}
diff --git a/client/packages/ui/src/components/index.ts b/client/packages/ui/src/components/index.ts
index 52ba721..973fabf 100644
--- a/client/packages/ui/src/components/index.ts
+++ b/client/packages/ui/src/components/index.ts
@@ -10,3 +10,4 @@ export * from './pending'
export * from './success'
export * from './form'
export * from './icons'
+export * from './faq'
diff --git a/client/packages/ui/src/components/navigationBar/index.tsx b/client/packages/ui/src/components/navigationBar/index.tsx
index 0acb5bd..90be524 100644
--- a/client/packages/ui/src/components/navigationBar/index.tsx
+++ b/client/packages/ui/src/components/navigationBar/index.tsx
@@ -49,6 +49,19 @@ export const NavigationBar = () => {
href={Routes.RaffleList}>
Home
+
+ FAQ
+
{address && (
diff --git a/client/packages/ui/src/features/raffleCreate/components/create.tsx b/client/packages/ui/src/features/raffleCreate/components/create.tsx
index d20f953..6c18f6b 100644
--- a/client/packages/ui/src/features/raffleCreate/components/create.tsx
+++ b/client/packages/ui/src/features/raffleCreate/components/create.tsx
@@ -1,6 +1,7 @@
import React, { useState, useEffect } from 'react'
import { useHistory } from 'react-router-dom'
import { useAccount } from 'wagmi'
+import { Link as RouterLink } from 'react-router-dom'
import {
Container,
Heading,
@@ -10,9 +11,9 @@ import {
Input,
Grid,
GridItem,
- Button
+ Button,
+ Link
} from '@chakra-ui/react'
-
import { Routes } from '@ui/Routes'
import { Error, Control } from '@ui/components'
import { useAsyncManager, useStore } from '@ui/hooks'
@@ -132,6 +133,9 @@ export const RaffleCreate = () => {
Create dynamic or static raffle
+
+ Need help? Check out our FAQ page.
+
diff --git a/client/packages/ui/src/features/raffleDetail/components/detail.tsx b/client/packages/ui/src/features/raffleDetail/components/detail.tsx
index 286edd6..e25986a 100644
--- a/client/packages/ui/src/features/raffleDetail/components/detail.tsx
+++ b/client/packages/ui/src/features/raffleDetail/components/detail.tsx
@@ -30,7 +30,6 @@ import {
} from '@ui/models'
import {
StepManager,
- getRaffle,
JoinButton,
CheckStatusButton,
PickWinnersButton,
@@ -41,6 +40,7 @@ import {
} from '@ui/features/raffleDetail'
import { formatUnixTs, formatFinishDate, shortenAddress } from '@ui/utils'
import { UploadWinners } from '@ui/features/raffleDetail'
+import { getRaffleHook } from '@ui/api/contracts'
export const initialState = {
raffle: null,
@@ -76,16 +76,16 @@ export const RaffleDetail = ({ id }) => {
const asyncManager = useAsyncManager()
const { raffle } = store.state
- const componentDidMount = () => {
- if (id) getRaffle({ id, update: store.update, asyncManager })
- }
- useEffect(componentDidMount, [])
+ getRaffleHook(store, id)
+
+ console.log(raffle)
- const addressOrRafleDidChange = () => {
+ const addressOrRaffleDidChange = () => {
if (raffle?.type == RaffleType.DYNAMIC)
store.update({ identifier: address })
}
- useEffect(addressOrRafleDidChange, [address, raffle])
+ useEffect(addressOrRaffleDidChange, [address, raffle])
+
return (
raffle?.id && (
{
+
{
const asyncManager = useAsyncManager()
const { step } = store.state
- const reset = (_store) => _store.update({ step: null })
+ const reset = (_store) => {
+ _store.update({ step: null })
+ asyncManager.reset()
+ }
return (
reset(store)} isOpen={!!step}>
diff --git a/client/packages/ui/src/hooks/useAsyncManager.ts b/client/packages/ui/src/hooks/useAsyncManager.ts
index f05707d..72e4130 100644
--- a/client/packages/ui/src/hooks/useAsyncManager.ts
+++ b/client/packages/ui/src/hooks/useAsyncManager.ts
@@ -48,6 +48,7 @@ export const useAsyncManager = () => {
start,
waiting,
success,
- fail
+ fail,
+ reset
}
}
diff --git a/client/packages/ui/tsconfig.json b/client/packages/ui/tsconfig.json
index df0ab6e..7237159 100644
--- a/client/packages/ui/tsconfig.json
+++ b/client/packages/ui/tsconfig.json
@@ -3,11 +3,7 @@
"compilerOptions": {
"target": "es2018",
"lib": ["es2018", "dom", "esnext.asynciterable"],
- "types": [
- "@types/jest",
- "jest",
- "node"
- ],
+ "types": ["@types/jest", "jest", "node"],
"typeRoots": ["node_modules/@types"],
"allowSyntheticDefaultImports": true,
"experimentalDecorators": true,
@@ -27,12 +23,6 @@
"@ui/*": ["./src/*"]
}
},
- "include": [
- "./src/**/*.ts",
- "./src/**/*.tsx",
- ],
- "exclude": [
- "babel.config.js",
- "node_modules"
- ]
+ "include": ["./src/**/*.ts", "./src/**/*.tsx"],
+ "exclude": ["babel.config.js", "node_modules"]
}
From 067caee9adbafd56775a13e9481e2d39a3929a48 Mon Sep 17 00:00:00 2001
From: 1marcghannam <1marc.ghannam@gmail.com>
Date: Tue, 23 May 2023 19:49:47 -0400
Subject: [PATCH 02/11] feat: Fix getRaffleHook
---
client/packages/ui/src/api/contracts/readFunctions.ts | 8 ++++----
.../src/features/raffleDetail/components/detail.tsx | 11 ++++-------
2 files changed, 8 insertions(+), 11 deletions(-)
diff --git a/client/packages/ui/src/api/contracts/readFunctions.ts b/client/packages/ui/src/api/contracts/readFunctions.ts
index 5939786..689eb23 100644
--- a/client/packages/ui/src/api/contracts/readFunctions.ts
+++ b/client/packages/ui/src/api/contracts/readFunctions.ts
@@ -44,14 +44,14 @@ export const getRaffle = async (id: number): Promise => {
}
export const getRaffleHook = (store, id) => {
- const { data, isError } = useContractRead({
+ const { data, isError, isLoading, isSuccess } = useContractRead({
...defaultOptions,
address: raffleManagerContractAddress,
functionName: 'getRaffle',
args: [id]
})
-
useEffect(() => {
+ if (!id || !isSuccess) return
const transformedData = transformRaffleItem(data)
if (
data &&
@@ -59,9 +59,9 @@ export const getRaffleHook = (store, id) => {
) {
store.update({ raffle: transformedData })
}
- }, [data, store])
+ }, [data, store, id, isSuccess])
- return { data, isError }
+ return { data, isError, isLoading, isSuccess }
}
export const getClaimableLink = async (id: number): Promise => {
diff --git a/client/packages/ui/src/features/raffleDetail/components/detail.tsx b/client/packages/ui/src/features/raffleDetail/components/detail.tsx
index e25986a..cc80ede 100644
--- a/client/packages/ui/src/features/raffleDetail/components/detail.tsx
+++ b/client/packages/ui/src/features/raffleDetail/components/detail.tsx
@@ -40,7 +40,7 @@ import {
} from '@ui/features/raffleDetail'
import { formatUnixTs, formatFinishDate, shortenAddress } from '@ui/utils'
import { UploadWinners } from '@ui/features/raffleDetail'
-import { getRaffleHook } from '@ui/api/contracts'
+import { getRaffleHook } from '@ui/api/contracts/readFunctions'
export const initialState = {
raffle: null,
@@ -78,14 +78,11 @@ export const RaffleDetail = ({ id }) => {
const { raffle } = store.state
getRaffleHook(store, id)
- console.log(raffle)
-
- const addressOrRaffleDidChange = () => {
+ const addressOrRafleDidChange = () => {
if (raffle?.type == RaffleType.DYNAMIC)
store.update({ identifier: address })
}
- useEffect(addressOrRaffleDidChange, [address, raffle])
-
+ useEffect(addressOrRafleDidChange, [address, raffle])
return (
raffle?.id && (
{
-
+
Date: Tue, 23 May 2023 20:01:40 -0400
Subject: [PATCH 03/11] feat: Fix csv example
---
.../raffleCreate/components/formStatic.tsx | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/client/packages/ui/src/features/raffleCreate/components/formStatic.tsx b/client/packages/ui/src/features/raffleCreate/components/formStatic.tsx
index 37bcfc9..59855cd 100644
--- a/client/packages/ui/src/features/raffleCreate/components/formStatic.tsx
+++ b/client/packages/ui/src/features/raffleCreate/components/formStatic.tsx
@@ -1,7 +1,6 @@
import React from 'react'
import { ethers } from 'ethers'
import { GridItem } from '@chakra-ui/react'
-
import { CSVUpload, Control } from '@ui/components'
export const initialStaticState = {
@@ -18,6 +17,18 @@ export const FormStatic = ({ update, validation }) => {
update({ participants })
}
+ const downloadCSV = () => {
+ const exampleCSV = 'john\nsteve\nbrad'
+ const blob = new Blob([exampleCSV], { type: 'text/csv' })
+ const url = URL.createObjectURL(blob)
+ const link = document.createElement('a')
+ link.href = url
+ link.download = 'example.csv'
+ document.body.appendChild(link)
+ link.click()
+ document.body.removeChild(link)
+ }
+
return (
{
errorMessage={validation['participants']}
helper={
<>
- CSV file example
+ CSV
+ file example
>
}>
Date: Thu, 25 May 2023 08:24:07 -0600
Subject: [PATCH 04/11] feat: Fixed pickwinners rerender
---
.../raffleDetail/components/detail.tsx | 108 ++++++++----------
.../features/raffleDetail/methods/contract.ts | 2 -
2 files changed, 50 insertions(+), 60 deletions(-)
diff --git a/client/packages/ui/src/features/raffleDetail/components/detail.tsx b/client/packages/ui/src/features/raffleDetail/components/detail.tsx
index cc80ede..6cc75eb 100644
--- a/client/packages/ui/src/features/raffleDetail/components/detail.tsx
+++ b/client/packages/ui/src/features/raffleDetail/components/detail.tsx
@@ -70,19 +70,13 @@ export const RaffleDetail = ({ id }) => {
const [isLargerThanMd] = useMediaQuery('(min-width: 48em)')
const { address } = useAccount()
const store = useStore({
- ...initialState,
- identifier: address ? address : initialState.identifier
+ ...initialState
})
const asyncManager = useAsyncManager()
const { raffle } = store.state
getRaffleHook(store, id)
- const addressOrRafleDidChange = () => {
- if (raffle?.type == RaffleType.DYNAMIC)
- store.update({ identifier: address })
- }
- useEffect(addressOrRafleDidChange, [address, raffle])
return (
raffle?.id && (
{
value={isLargerThanMd ? raffle.owner : shortenAddress(raffle.owner)}
/>
- {raffle.status !== RaffleStatus.RESOLVING && (
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
- )}
+
+
+
+
+
+
+
{
asyncManager.success()
- await getRaffle({ id, asyncManager, update })
-
success(true)
return true
} catch (error) {
From ebd20c791cd55e2be18fa175368715a07c96d311 Mon Sep 17 00:00:00 2001
From: 1marcghannam <1marc.ghannam@gmail.com>
Date: Thu, 25 May 2023 09:35:43 -0600
Subject: [PATCH 05/11] feat: Add tx link
---
.../raffleCreate/components/create.tsx | 6 ++++-
.../raffleDetail/components/detail.tsx | 2 +-
.../raffleDetail/components/pickWinners.tsx | 23 +++++++++++++++----
.../features/raffleDetail/methods/contract.ts | 6 +++--
4 files changed, 28 insertions(+), 9 deletions(-)
diff --git a/client/packages/ui/src/features/raffleCreate/components/create.tsx b/client/packages/ui/src/features/raffleCreate/components/create.tsx
index 6c18f6b..6ddfe12 100644
--- a/client/packages/ui/src/features/raffleCreate/components/create.tsx
+++ b/client/packages/ui/src/features/raffleCreate/components/create.tsx
@@ -133,7 +133,11 @@ export const RaffleCreate = () => {
Create dynamic or static raffle
-
+
Need help? Check out our FAQ page.
diff --git a/client/packages/ui/src/features/raffleDetail/components/detail.tsx b/client/packages/ui/src/features/raffleDetail/components/detail.tsx
index 6cc75eb..2597a1b 100644
--- a/client/packages/ui/src/features/raffleDetail/components/detail.tsx
+++ b/client/packages/ui/src/features/raffleDetail/components/detail.tsx
@@ -1,4 +1,4 @@
-import React, { useEffect } from 'react'
+import React from 'react'
import { useAccount } from 'wagmi'
import {
Container,
diff --git a/client/packages/ui/src/features/raffleDetail/components/pickWinners.tsx b/client/packages/ui/src/features/raffleDetail/components/pickWinners.tsx
index 6f25a81..45922b2 100644
--- a/client/packages/ui/src/features/raffleDetail/components/pickWinners.tsx
+++ b/client/packages/ui/src/features/raffleDetail/components/pickWinners.tsx
@@ -1,20 +1,22 @@
import React, { useState, useEffect } from 'react'
-import { Button, Text, Flex, Heading } from '@chakra-ui/react'
+import { Button, Text, Flex, Heading, Link } from '@chakra-ui/react'
+import { useNetwork } from 'wagmi'
import { pickWinners } from '@ui/features/raffleDetail'
export const PickWinners = ({ id, reset, asyncManager, store }) => {
const [success, setSuccess] = useState(false)
+ const [txHash, setTxHash] = useState(null)
+ const { chain } = useNetwork()
- const componentDidMount = () => {
+ useEffect(() => {
pickWinners({
id,
asyncManager,
success: setSuccess,
- update: store.update
+ txHash: setTxHash
})
- }
- useEffect(componentDidMount, [])
+ }, [])
return (
success && (
@@ -23,6 +25,17 @@ export const PickWinners = ({ id, reset, asyncManager, store }) => {
Pick Winners
Successfully picked winners for raffle id `{id}`.
+ {txHash && (
+
+
+ View VRF transaction
+
+
+ )}