Skip to content

Commit

Permalink
Create basic Redeem page
Browse files Browse the repository at this point in the history
This commit introduces the structure for the redeem page where users
will be able to retrieve their deposit from the course.

Included are a number of new contract functions used to check the status
of a user's stake, the block in which they registered, the course length
and finally send a transaction to retrieve their deposit.

Still needed is the ability for a user to select whether or not they'd
like to retrieve their deposit or mint $LEARN instead.
  • Loading branch information
Anthony M committed May 27, 2022
1 parent de6f46a commit c6611bd
Show file tree
Hide file tree
Showing 10 changed files with 360 additions and 30 deletions.
77 changes: 77 additions & 0 deletions src/course/contracts.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,83 @@ export const isRegistered = async (learner, provider) => {
return res
}

export const hasStakeInCourse = async (provider) => {
const { chainId } = await provider.getNetwork()

const deSchoolContract = new Contract(
addresses(chainId).deSchool,
abis.deSchool,
provider
)
let res = false
try {
res = !!(await deSchoolContract.isDeployed(KERNEL_COURSE_ID))
} catch (err) {
// throws an error if either the learner is not registered or if the courseId does not exist
/** */
}
return res
}

export const getBlockRegistered = async (learner, provider) => {
const { chainId } = await provider.getNetwork()

const deSchoolContract = new Contract(
addresses(chainId).deSchool,
abis.deSchool,
provider
)

let res
try {
res = await deSchoolContract.getBlockRegistered(learner, KERNEL_COURSE_ID)
} catch (err) {
// throws an error if either the learner is not registered or if the courseId does not exist
/** */
}
return res
}

export const getCourseLength = async (provider) => {
const { chainId } = await provider.getNetwork()

const deSchoolContract = new Contract(
addresses(chainId).deSchool,
abis.deSchool,
provider
)

let res
try {
res = await deSchoolContract.courses(KERNEL_COURSE_ID)[1]
} catch (err) {
// throws an error if either the learner is not registered or if the courseId does not exist
/** */
}
return res
}

export const redeemDeposit = async (signer) => {
const chainId = await signer.getChainId()

const deSchoolContract = new Contract(
addresses(chainId).deSchool,
abis.deSchool,
signer
)

let res

try {
res = !!(await deSchoolContract.redeem(KERNEL_COURSE_ID))
} catch (err) {
// throws an error if either the learner is not registered or if the courseId does not exist
/** */
}

return res
}

export const getDaiNonce = async (learner, provider) => {
const { chainId } = await provider.getNetwork()

Expand Down
34 changes: 5 additions & 29 deletions src/modules/flashcard/card.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,11 @@
/** @jsx jsx */
import { Children, Fragment, useState, useEffect } from 'react'
import { jsx, Flex, Text, Box } from 'theme-ui'
import { jsx, Flex } from 'theme-ui'
import { useConnect, useAccount, useProvider } from 'wagmi'
import { Button } from '@modules/ui'
import { motion } from 'framer-motion'
import { Connector } from '@src/course/connect'
import { isRegistered } from '@src/course/contracts'
import Web3Modal from '../web3/modal'

const Web3Control = ({
children,
onClickButton,
buttonText,
descriptionText,
isDisabled,
}) => {
return (
<div>
<Box sx={{ padding: '0.5rem' }}>
<Text sx={styles.connectText}>{descriptionText}</Text>
</Box>
<Button
sx={{ marginX: 'auto' }}
disabled={isDisabled}
onClick={onClickButton}>
{buttonText}
</Button>
{children}
</div>
)
}
import { Modal as Web3Modal, Button as Web3Button } from '@src/modules/web3'

const Card = ({
children,
Expand Down Expand Up @@ -149,15 +125,15 @@ const Card = ({
</Flex>
)}
{isConnected && !isUserRegistered && (
<Web3Control
<Web3Button
descriptionText="Register to reveal"
buttonText="Register"
isDisabled={!connector.ready}
onClickButton={() => setIsModalVisible(true)}
/>
)}
{!isConnected && (
<Web3Control
<Web3Button
descriptionText="Connect wallet to reveal"
buttonText="Metamask"
isDisabled={!connector.ready}
Expand All @@ -167,7 +143,7 @@ const Card = ({
{error?.message && (
<div sx={styles.connectError}>Failed to connect</div>
)}
</Web3Control>
</Web3Button>
)}
</motion.div>
{isConnected && isUserRegistered && (
Expand Down
28 changes: 28 additions & 0 deletions src/modules/redemptionPage/ConnectButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/** @jsx jsx */

import { Flex, jsx } from 'theme-ui'
import { useConnect } from 'wagmi'
import { useState } from 'react'

import { Button as Web3Button } from '@src/modules/web3'
import { Connector } from '@src/course/connect'

const ConnectButton = () => {
const { connect, connectors } = useConnect()
const [connector] = useState(connectors[Connector.INJECTED])

return (
<Flex>
<Web3Button
descriptionText="Connect your wallet to continue"
buttonText="Metamask"
isDisabled={!connector.ready}
onClickButton={() => {
connect(connector)
}}
/>
</Flex>
)
}

export default ConnectButton
37 changes: 37 additions & 0 deletions src/modules/redemptionPage/RedemptionConnector.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/** @jsx jsx */

import { jsx } from 'theme-ui'
import { useAccount, useProvider } from 'wagmi'
import { useEffect, useState } from 'react'

import { hasStakeInCourse } from '@src/course/contracts'
import RegisterButton from './RegisterButton'
import RedemptionDetails from './RedemptionDetails'

const RedemptionConnector = () => {
const { data: accountData } = useAccount()
const provider = useProvider()
const [hasStakeStakeToRedeem, setHasStakeToRedeem] = useState(false)

useEffect(() => {
const retrieveAndSetStakeState = async () => {
const _hasStake = await hasStakeInCourse(provider)
setHasStakeToRedeem(_hasStake)
}

if (accountData?.address && provider) {
retrieveAndSetStakeState()
}
}, [accountData?.address, provider])

return (
<div>
{hasStakeStakeToRedeem && (
<RedemptionDetails address={accountData?.address} />
)}
{!hasStakeStakeToRedeem && <RegisterButton />}
</div>
)
}

export default RedemptionConnector
110 changes: 110 additions & 0 deletions src/modules/redemptionPage/RedemptionDetails.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/** @jsx jsx */

import { Flex, jsx } from 'theme-ui'
import { useConnect, useProvider, useBlockNumber, useSigner } from 'wagmi'
import { useEffect, useState } from 'react'

import { Button as Web3Button } from '@src/modules/web3'
import { Connector } from '@src/course/connect'
import {
getBlockRegistered,
getCourseLength,
redeemDeposit,
} from '@src/course/contracts'

const isRedemptionTime = (currentBlockNumber, redemptionBlockNumber) => {
return currentBlockNumber >= redemptionBlockNumber
}

const getTimeUntilRedemptionText = (
currentBlockNumber,
redemptionBlockNumber
) => {
if (isRedemptionTime(currentBlockNumber, redemptionBlockNumber)) {
return 'You can redeem your deposit now'
} else {
const averageBlockTimeInDays = 14 / 60 / 60 / 24
const daysUntilRedemption =
(redemptionBlockNumber - currentBlockNumber) * averageBlockTimeInDays

if (daysUntilRedemption <= 1) {
return `You can redeem your deposit today at block number ${redemptionBlockNumber}`
} else {
const roundedDays = Math.round(daysUntilRedemption)
const dayText = roundedDays == 1 ? 'day' : 'days'
return `You can redeem your deposit in ${roundedDays} ${dayText} at block number ${redemptionBlockNumber}`
}
}
}

const RedemptionDetails = ({ address }) => {
const provider = useProvider()
const { data: signer } = useSigner()
const { data: currentBlockNumber } = useBlockNumber({ watch: true })
const { connectors } = useConnect()

const [connector] = useState(connectors[Connector.INJECTED])
const [blockRegistered, setBlockRegistered] = useState(0)
const [courseLength, setCourseLength] = useState(0)
const [canRedeemDeposit, setCanRedeemDeposit] = useState(false)

const redemptionBlockNumber = blockRegistered + courseLength
const timeUntilRedemptionText = getTimeUntilRedemptionText(
currentBlockNumber,
redemptionBlockNumber
)

useEffect(() => {
const retrieveBlockRegistered = async () => {
const blockNumber = await getBlockRegistered(address, provider)
setBlockRegistered(blockNumber || 0)
}

const retrieveCourseLength = async () => {
const lengthOfCourse = await getCourseLength(provider)
setCourseLength(lengthOfCourse || 0)
}

if (address && provider) {
retrieveBlockRegistered()
retrieveCourseLength()
}
}, [address, provider])

useEffect(() => {
if (currentBlockNumber && blockRegistered) {
setCanRedeemDeposit(
isRedemptionTime(currentBlockNumber, redemptionBlockNumber)
)
}
}, [blockRegistered, currentBlockNumber])

const handleOnPressRedeem = async () => {
await redeemDeposit(signer)
}

return (
<Flex sx={styles.container}>
<p sx={styles.blockNumberText}>Current block: {currentBlockNumber}</p>
<Web3Button
descriptionText={timeUntilRedemptionText}
buttonText="Redeem"
isDisabled={!connector.ready || !canRedeemDeposit}
onClickButton={handleOnPressRedeem}
/>
</Flex>
)
}

const styles = {
container: {
flexDirection: 'column',
},
blockNumberText: {
textAlign: 'center',
fontWeight: 'bold',
marginX: 'auto',
},
}

export default RedemptionDetails
28 changes: 28 additions & 0 deletions src/modules/redemptionPage/RegisterButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/** @jsx jsx */

import { Flex, jsx } from 'theme-ui'
import { useConnect } from 'wagmi'
import { useState } from 'react'

import { Button as Web3Button, Modal as Web3Modal } from '@src/modules/web3'
import { Connector } from '@src/course/connect'

const RegisterButton = () => {
const { connectors } = useConnect()
const [connector] = useState(connectors[Connector.INJECTED])
const [isModalVisible, setIsModalVisible] = useState(false)

return (
<Flex>
{isModalVisible && <Web3Modal setIsVisible={setIsModalVisible} />}
<Web3Button
descriptionText="You don't have any deposits in the course"
buttonText="Register"
isDisabled={!connector.ready}
onClickButton={() => setIsModalVisible(true)}
/>
</Flex>
)
}

export default RegisterButton
4 changes: 4 additions & 0 deletions src/modules/redemptionPage/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import ConnectButton from './ConnectButton'
import RedemptionConnector from './RedemptionConnector'

export { ConnectButton, RedemptionConnector }
37 changes: 37 additions & 0 deletions src/modules/web3/button.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/** @jsx jsx */

import { jsx, Text, Box } from 'theme-ui'
import { Button } from '@modules/ui'

const Web3Button = ({
children,
onClickButton,
buttonText,
descriptionText,
isDisabled,
}) => {
return (
<div>
<Box sx={{ padding: '0.5rem' }}>
<Text sx={styles.connectText}>{descriptionText}</Text>
</Box>
<Button
sx={{ marginX: 'auto' }}
disabled={isDisabled}
onClick={onClickButton}>
{buttonText}
</Button>
{children}
</div>
)
}

const styles = {
connectText: {
textAlign: 'center',
fontWeight: 'bold',
marginX: 'auto',
},
}

export default Web3Button
Loading

0 comments on commit c6611bd

Please sign in to comment.