Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial implementation of stake form #93

Merged
merged 28 commits into from
Jan 10, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
7269487
Form initialization for stake/unstake action
kkosiorowska Dec 27, 2023
2fdfae3
Add function to convert user amount to `bigint`
kkosiorowska Dec 28, 2023
e4ccfbe
Add a validation for stake form
kkosiorowska Dec 28, 2023
4c67f66
Handling the submission of the stake form
kkosiorowska Dec 28, 2023
062cadf
Merge branch 'main' into stake-form
kkosiorowska Dec 28, 2023
1b1b9ab
Improvements for forms
kkosiorowska Dec 29, 2023
4b845b9
Define custom data from the form level
kkosiorowska Dec 29, 2023
a1bdfc7
Merge branch 'main' of github.com:thesis/acre into stake-form
kkosiorowska Jan 2, 2024
303f3b2
Make a `validate` function more general
kkosiorowska Jan 2, 2024
c697ec8
Create a utils function to find for currency by type
kkosiorowska Jan 2, 2024
9984076
Make the token amount form component clearer
kkosiorowska Jan 2, 2024
4399b2c
Simplify the ActionForm component
kkosiorowska Jan 3, 2024
8c8d2a1
Store the amount as bigint for the token amount form
kkosiorowska Jan 3, 2024
acda15a
Merge branch 'main' of github.com:thesis/acre into stake-form
kkosiorowska Jan 4, 2024
9eba84c
Update the submit form function for staking
kkosiorowska Jan 8, 2024
a0c4b71
Use a more precise name for custom form data
kkosiorowska Jan 8, 2024
443c881
Refactor the display of transaction details in the form
kkosiorowska Jan 9, 2024
d49a39c
Use `Currency` instead `CurrencyType` for TokenAmountForm
kkosiorowska Jan 9, 2024
99bcdfe
Move conversion logic to `CurrencyBalanceWithConversion` component
kkosiorowska Jan 9, 2024
a28a07f
Refactor for `useTransactionDetails`
kkosiorowska Jan 9, 2024
8f397e2
Fix the positioning bug for `TransactionDetailsAmountItem`
kkosiorowska Jan 9, 2024
720b567
Merge branch 'main' of github.com:thesis/acre into stake-form
kkosiorowska Jan 10, 2024
47c0ca1
Save the token amount field name for the form as const
kkosiorowska Jan 10, 2024
96e87fc
Make a formId optional for the the token amount form
kkosiorowska Jan 10, 2024
cf1eabb
Render the submit button inside form
kkosiorowska Jan 10, 2024
c6b6c7b
Use always `CurrencyType`
kkosiorowska Jan 10, 2024
39cfbbc
Fix typo from `seTokenAmount` to `setTokenAmount`
kkosiorowska Jan 10, 2024
597a890
Create a special hook for the token amount value from form
kkosiorowska Jan 10, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions dapp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"@emotion/styled": "^11.11.0",
"@ledgerhq/wallet-api-client": "^1.2.1",
"@ledgerhq/wallet-api-client-react": "^1.1.2",
"formik": "^2.4.5",
"framer-motion": "^10.16.5",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand Down
45 changes: 45 additions & 0 deletions dapp/src/components/Modals/ActionForm/index.tsx
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure about the name of this component because at first sight, it may indicate we can render any form but actually we are rendering stake/unstake form.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it's true this name isn't perfect. But I don't have a better idea. You are right, but on the other hand, we are just displaying the form there. The type of action restricts us on the types of form. If you just have any better idea let me know.

Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React from "react"
import {
ModalBody,
Tabs,
TabList,
Tab,
TabPanels,
TabPanel,
} from "@chakra-ui/react"
import { ModalStep } from "../../../contexts"
import StakeForm from "../Staking/StakeForm"

const TABS = ["stake", "unstake"] as const

type FormType = (typeof TABS)[number]
r-czajkowski marked this conversation as resolved.
Show resolved Hide resolved

type ActionFormProps = { defaultForm: FormType } & ModalStep
r-czajkowski marked this conversation as resolved.
Show resolved Hide resolved

function ActionForm({ defaultForm, goNext }: ActionFormProps) {
return (
<ModalBody>
<Tabs
w="100%"
variant="underline"
defaultIndex={TABS.indexOf(defaultForm)}
>
<TabList>
{TABS.map((tab) => (
<Tab key={tab} w="50%">
{tab}
</Tab>
))}
</TabList>
<TabPanels>
<TabPanel>
<StakeForm goNext={goNext} />
</TabPanel>
<TabPanel>{/* TODO: Add form for unstake */}</TabPanel>
</TabPanels>
</Tabs>
</ModalBody>
)
}

export default ActionForm
59 changes: 49 additions & 10 deletions dapp/src/components/Modals/Staking/StakeForm.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,54 @@
import React from "react"
import { Button, ModalBody } from "@chakra-ui/react"
import React, { useCallback } from "react"
import { FormikErrors, withFormik } from "formik"
import { ModalStep } from "../../../contexts"
import { TextMd } from "../../shared/Typography"
import FormBase, { FormBaseProps, FormValues } from "../../shared/FormBase"
import { useTransactionContext, useWalletContext } from "../../../hooks"
import { getErrorsObj, validateTokenAmount } from "../../../utils"

const CUSTOM_DATA = {
buttonText: "Stake",
btcAmountText: "Amount to be staked",
estimatedAmountText: "Approximately staked tokens",
}

r-czajkowski marked this conversation as resolved.
Show resolved Hide resolved
const StakeFormik = withFormik<
{ onSubmitForm: (values: FormValues) => void } & FormBaseProps,
FormValues
r-czajkowski marked this conversation as resolved.
Show resolved Hide resolved
>({
mapPropsToValues: () => ({
amount: "",
}),
r-czajkowski marked this conversation as resolved.
Show resolved Hide resolved
validate: async ({ amount }, { tokenBalance }) => {
const errors: FormikErrors<FormValues> = {}

errors.amount = validateTokenAmount(amount, tokenBalance)

return getErrorsObj(errors)
},
handleSubmit: (values, { props }) => {
props.onSubmitForm(values)
},
})(FormBase)

r-czajkowski marked this conversation as resolved.
Show resolved Hide resolved
function StakeForm({ goNext }: ModalStep) {
const { btcAccount } = useWalletContext()
const { setAmount } = useTransactionContext()

const handleSubmitForm = useCallback(
(values: FormValues) => {
setAmount(values.amount)
goNext()
},
[goNext, setAmount],
)

export default function StakeModal({ goNext }: ModalStep) {
return (
<ModalBody>
<TextMd>Stake modal</TextMd>
<Button width="100%" onClick={goNext}>
Stake
</Button>
</ModalBody>
<StakeFormik
customData={CUSTOM_DATA}
tokenBalance={btcAccount?.balance.toString() ?? "0"}
onSubmitForm={handleSubmitForm}
/>
)
}

export default StakeForm
6 changes: 3 additions & 3 deletions dapp/src/components/Modals/Staking/index.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import React from "react"
import { useModalFlowContext } from "../../../hooks"
import StakeForm from "./StakeForm"
import Overview from "./Overview"
import ModalBase from "../../shared/ModalBase"
import ActionForm from "../ActionForm"

function StakingSteps() {
const { activeStep, goNext } = useModalFlowContext()

switch (activeStep) {
case 1:
return <Overview goNext={goNext} />
return <ActionForm defaultForm="stake" goNext={goNext} />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to figure out how to display the correct steps based on the form type. In this case, if a user changes tab to unstake, fills a form, and goes to the next step this component will display the next step for staking not unstaking. Just flagging, we probably should address it in a separate PR during the unstaking flow work.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great catch! Let's do it as you say in a separate PR. We will have to think about it so I will also create a task to not lose it. #127

case 2:
return <StakeForm goNext={goNext} />
return <Overview goNext={goNext} />
default:
return null
}
Expand Down
4 changes: 4 additions & 0 deletions dapp/src/components/shared/Form/Form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { chakra } from "@chakra-ui/react"
import { Form as FormikForm } from "formik"

export const Form = chakra(FormikForm)
24 changes: 24 additions & 0 deletions dapp/src/components/shared/Form/FormTokenBalanceInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from "react"
import { useField } from "formik"
import TokenBalanceInput, { TokenBalanceInputProps } from "../TokenBalanceInput"

export type FormTokenBalanceInputProps = {
name: string
} & Omit<TokenBalanceInputProps, "setAmount">
export function FormTokenBalanceInput({
name,
...restProps
}: FormTokenBalanceInputProps) {
const [field, meta, helpers] = useField(name)

return (
<TokenBalanceInput
{...restProps}
{...field}
amount={meta.value}
setAmount={helpers.setValue}
hasError={Boolean(meta.touched && meta.error)}
errorMsgText={meta.error}
/>
)
}
2 changes: 2 additions & 0 deletions dapp/src/components/shared/Form/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./Form"
export * from "./FormTokenBalanceInput"
99 changes: 99 additions & 0 deletions dapp/src/components/shared/FormBase/TransactionDetails.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import React from "react"
import { Box, Flex, VStack } from "@chakra-ui/react"
import { useField } from "formik"
import { CurrencyBalanceWithConversion } from "../CurrencyBalanceWithConversion"
import { TextMd } from "../Typography"

// TODO: Use data from the SDK
function getTransactionDetails(value: string):
| {
btcAmount: string
protocolFee: string
stakedAmount: string
}
| undefined {
const btcAmount = value ?? 0n
const btcAmountInBI = BigInt(btcAmount)

if (btcAmountInBI <= 0n) return undefined

const protocolFee = btcAmountInBI / 10000n
const stakedAmount = btcAmountInBI - protocolFee

return {
btcAmount,
protocolFee: protocolFee.toString(),
stakedAmount: stakedAmount.toString(),
}
}

function TransactionDetailsItem({
text,
btcAmount,
usdAmount,
}: {
text: string
btcAmount?: string | number
usdAmount: string
}) {
return (
<Flex justifyContent="space-between" w="100%">
<TextMd fontWeight="semibold" color="grey.700">
{text}
</TextMd>
<Box textAlign="end">
<CurrencyBalanceWithConversion
from={{
currencyType: "bitcoin",
amount: btcAmount,
}}
to={{
currencyType: "usd",
amount: usdAmount,
shouldBeFormatted: false,
size: "sm",
fontWeight: "medium",
color: "grey.500",
}}
/>
</Box>
</Flex>
)
}

function TransactionDetails({
fieldName,
btcAmountText,
estimatedAmountText,
}: {
fieldName: string
btcAmountText: string
estimatedAmountText: string
}) {
const [, { value }] = useField(fieldName)

const details = getTransactionDetails(value)

return (
<VStack gap={3} mt={10}>
<TransactionDetailsItem
text={btcAmountText}
btcAmount={details?.btcAmount}
usdAmount="45.725,91"
/>
<TransactionDetailsItem
// TODO: Use protocol fee from the SDK
text="Protocol fee (0.01%)"
btcAmount={details?.protocolFee}
usdAmount="0.024,91"
/>
<TransactionDetailsItem
text={estimatedAmountText}
btcAmount={details?.stakedAmount}
usdAmount="44.762,21"
/>
</VStack>
)
}

export default TransactionDetails
55 changes: 55 additions & 0 deletions dapp/src/components/shared/FormBase/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import React from "react"
import { Button } from "@chakra-ui/react"
import { FormikProps } from "formik"
import { Form, FormTokenBalanceInput } from "../Form"
import { CurrencyType } from "../../../types"
import TransactionDetails from "./TransactionDetails"

export type FormValues = {
amount: string
}

export type FormBaseProps = {
customData: {
buttonText: string
btcAmountText: string
estimatedAmountText: string
}
tokenBalance: string
tokenBalanceInputPlaceholder?: string
currencyType?: CurrencyType
fieldName?: string
children?: React.ReactNode
}

function FormBase({
customData,
tokenBalance,
tokenBalanceInputPlaceholder = "BTC",
currencyType = "bitcoin",
fieldName = "amount",
children,
...formikProps
}: FormBaseProps & FormikProps<FormValues>) {
return (
<Form onSubmit={formikProps.handleSubmit}>
<FormTokenBalanceInput
name={fieldName}
tokenBalance={tokenBalance}
placeholder={tokenBalanceInputPlaceholder}
currencyType={currencyType}
/>
<TransactionDetails
fieldName={fieldName}
btcAmountText={customData.btcAmountText}
estimatedAmountText={customData.estimatedAmountText}
/>
{children}
<Button type="submit" size="lg" width="100%" mt={4}>
{customData.buttonText}
</Button>
</Form>
)
}

export default FormBase
26 changes: 16 additions & 10 deletions dapp/src/components/shared/ModalBase/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import {
ModalContent,
ModalOverlay,
} from "@chakra-ui/react"
import { ModalFlowContext, ModalFlowContextValue } from "../../../contexts"
import {
ModalFlowContext,
ModalFlowContextValue,
TransactionContextProvider,
} from "../../../contexts"
import { useSidebar } from "../../../hooks"
import SupportWrapper from "../../Modals/Support"

Expand Down Expand Up @@ -66,14 +70,16 @@ export default function ModalBase({
)

return (
<ModalFlowContext.Provider value={contextValue}>
<Modal size="lg" isOpen={isOpen} onClose={handleClose}>
<ModalOverlay mt="header_height" />
<ModalContent mt="modal_shift">
<ModalCloseButton />
<SupportWrapper>{children}</SupportWrapper>
</ModalContent>
</Modal>
</ModalFlowContext.Provider>
<TransactionContextProvider>
<ModalFlowContext.Provider value={contextValue}>
<Modal size="lg" isOpen={isOpen} onClose={handleClose}>
<ModalOverlay mt="header_height" />
<ModalContent mt="modal_shift">
<ModalCloseButton />
<SupportWrapper>{children}</SupportWrapper>
</ModalContent>
</Modal>
</ModalFlowContext.Provider>
</TransactionContextProvider>
)
}
Loading
Loading