diff --git a/src/course/contracts.js b/src/course/contracts.js
index 25adb7d58a..d6b8156f9a 100644
--- a/src/course/contracts.js
+++ b/src/course/contracts.js
@@ -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()
diff --git a/src/modules/flashcard/card.js b/src/modules/flashcard/card.js
index 50761bccec..bcc86ab41c 100644
--- a/src/modules/flashcard/card.js
+++ b/src/modules/flashcard/card.js
@@ -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 (
-
-
- {descriptionText}
-
-
- {children}
-
- )
-}
+import { Modal as Web3Modal, Button as Web3Button } from '@src/modules/web3'
const Card = ({
children,
@@ -149,7 +125,7 @@ const Card = ({
)}
{isConnected && !isUserRegistered && (
-
)}
{!isConnected && (
- Failed to connect
)}
-
+
)}
{isConnected && isUserRegistered && (
diff --git a/src/modules/redemptionPage/ConnectButton.js b/src/modules/redemptionPage/ConnectButton.js
new file mode 100644
index 0000000000..172158dcf4
--- /dev/null
+++ b/src/modules/redemptionPage/ConnectButton.js
@@ -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 (
+
+ {
+ connect(connector)
+ }}
+ />
+
+ )
+}
+
+export default ConnectButton
diff --git a/src/modules/redemptionPage/RedemptionConnector.js b/src/modules/redemptionPage/RedemptionConnector.js
new file mode 100644
index 0000000000..7ad91157f3
--- /dev/null
+++ b/src/modules/redemptionPage/RedemptionConnector.js
@@ -0,0 +1,36 @@
+/** @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(true)
+
+ useEffect(() => {
+ const retrieveAndSetStakeState = async () => {
+ const _hasStake = await hasStakeInCourse(provider)
+ setHasStakeToRedeem(_hasStake)
+ }
+ if (accountData?.address && provider) {
+ retrieveAndSetStakeState()
+ }
+ }, [accountData?.address, provider])
+
+ return (
+
+ {hasStakeStakeToRedeem && (
+
+ )}
+ {!hasStakeStakeToRedeem && }
+
+ )
+}
+
+export default RedemptionConnector
diff --git a/src/modules/redemptionPage/RedemptionDetails.js b/src/modules/redemptionPage/RedemptionDetails.js
new file mode 100644
index 0000000000..8847dae925
--- /dev/null
+++ b/src/modules/redemptionPage/RedemptionDetails.js
@@ -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 / 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 || 28556533)
+ }
+
+ const retrieveCourseLength = async () => {
+ const lengthOfCourse = await getCourseLength(provider)
+ setCourseLength(lengthOfCourse || 1)
+ }
+
+ if (address && provider) {
+ retrieveBlockRegistered()
+ retrieveCourseLength()
+ }
+ }, [address, provider])
+
+ useEffect(() => {
+ if (currentBlockNumber && blockRegistered) {
+ setCanRedeemDeposit(
+ isRedemptionTime(currentBlockNumber, redemptionBlockNumber)
+ )
+ }
+ }, [blockRegistered, currentBlockNumber])
+
+ const handleOnPressRedeem = async () => {
+ await redeemDeposit(signer)
+ }
+
+ return (
+
+ Current block: {currentBlockNumber}
+
+
+ )
+}
+
+const styles = {
+ container: {
+ flexDirection: 'column',
+ },
+ blockNumberText: {
+ textAlign: 'center',
+ fontWeight: 'bold',
+ marginX: 'auto',
+ },
+}
+
+export default RedemptionDetails
diff --git a/src/modules/redemptionPage/RegisterButton.js b/src/modules/redemptionPage/RegisterButton.js
new file mode 100644
index 0000000000..2ec6ae6832
--- /dev/null
+++ b/src/modules/redemptionPage/RegisterButton.js
@@ -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 (
+
+ {isModalVisible && }
+ setIsModalVisible(true)}
+ />
+
+ )
+}
+
+export default RegisterButton
diff --git a/src/modules/redemptionPage/index.js b/src/modules/redemptionPage/index.js
new file mode 100644
index 0000000000..df39e0a1c1
--- /dev/null
+++ b/src/modules/redemptionPage/index.js
@@ -0,0 +1,4 @@
+import ConnectButton from './ConnectButton'
+import RedemptionConnector from './RedemptionConnector'
+
+export { ConnectButton, RedemptionConnector }
diff --git a/src/modules/web3/button.js b/src/modules/web3/button.js
new file mode 100644
index 0000000000..20b0fe665d
--- /dev/null
+++ b/src/modules/web3/button.js
@@ -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 (
+
+
+ {descriptionText}
+
+
+ {children}
+
+ )
+}
+
+const styles = {
+ connectText: {
+ textAlign: 'center',
+ fontWeight: 'bold',
+ marginX: 'auto',
+ },
+}
+
+export default Web3Button
diff --git a/src/modules/web3/index.js b/src/modules/web3/index.js
index e5a9f47b88..bcf5b1081d 100644
--- a/src/modules/web3/index.js
+++ b/src/modules/web3/index.js
@@ -1 +1,4 @@
-export { default as Web3 } from './modal'
+import Modal from './modal'
+import Button from './button'
+
+export { Modal, Button }
diff --git a/src/pages/en/redeem.js b/src/pages/en/redeem.js
new file mode 100644
index 0000000000..7781d6a727
--- /dev/null
+++ b/src/pages/en/redeem.js
@@ -0,0 +1,30 @@
+/** @jsx jsx */
+
+import { Flex, jsx } from 'theme-ui'
+import { useConnect } from 'wagmi'
+
+import { Heading } from '@modules/ui/heading'
+import { ConnectButton, RedemptionConnector } from '@src/modules/redemptionPage'
+
+const RedeemPage = () => {
+ const { isConnected } = useConnect()
+
+ return (
+
+ Redeem
+ {isConnected && }
+ {!isConnected && }
+
+ )
+}
+
+const styles = {
+ container: {
+ width: '100%',
+ justifyContent: 'center',
+ alignItems: 'center',
+ flexDirection: 'column',
+ },
+}
+
+export default RedeemPage