Skip to content

Commit

Permalink
Read the min amount of BTC deposit from the SDK (#303)
Browse files Browse the repository at this point in the history
Closes #234
Closes #336

This PR reads the minimum amount of BTC deposit from the SDK and saves
it in the redux store.

### What has been done: 

- Fetch the minimum amount of BTC deposit from the SDK
- Create a special hook `useFetchSdkData` to load all needed data from
the SDK
- Set desired decimals for Bitcoin currency manually. Previously, we
determined the number of desired places based on the minimum stake
amount. However, now we are not able to do it this way because the data
from the SDK is loaded after connecting the accounts.
- Standardization of types in the form. Use a `bigint` for
`tokenBalnce`.
- Extend `CurrencyBalance` to pass the amount in `bigint`



One note: the contracts have not been deployed yet. Therefore, an error
will appear in the console `TypeError: this.instance.minDepositAmount is
not a function` However, When the contracts are re-deployed I will
update the code and the error will disappear.
  • Loading branch information
r-czajkowski authored Apr 16, 2024
2 parents d265ef5 + b836867 commit 1a2dbab
Show file tree
Hide file tree
Showing 31 changed files with 164 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ function StakeDetails({
maxTokenAmount,
}: {
currency: CurrencyType
minTokenAmount: string
maxTokenAmount: string
minTokenAmount: bigint
maxTokenAmount: bigint
}) {
const value = useTokenAmountFormValue() ?? 0n
const isMaximumValueExceeded = value > BigInt(maxTokenAmount)
const isMinimumValueFulfilled = value >= BigInt(minTokenAmount)
const isMaximumValueExceeded = value > maxTokenAmount
const isMinimumValueFulfilled = value >= minTokenAmount
// Let's not calculate the details of the transaction when the value is not valid.
const amount = !isMaximumValueExceeded && isMinimumValueFulfilled ? value : 0n
const details = useTransactionDetails(amount)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import React from "react"
import { BITCOIN_MIN_AMOUNT } from "#/constants"
import TokenAmountForm from "#/components/shared/TokenAmountForm"
import { TokenAmountFormValues } from "#/components/shared/TokenAmountForm/TokenAmountFormBase"
import { useWalletContext } from "#/hooks"
import { useMinDepositAmount, useWalletContext } from "#/hooks"
import { FormSubmitButton } from "#/components/shared/Form"
import StakeDetails from "./StakeDetails"

Expand All @@ -11,20 +10,21 @@ function StakeFormModal({
}: {
onSubmitForm: (values: TokenAmountFormValues) => void
}) {
const minDepositAmount = useMinDepositAmount()
const { btcAccount } = useWalletContext()
const tokenBalance = btcAccount?.balance.toString() ?? "0"
const tokenBalance = BigInt(btcAccount?.balance.toString() ?? "0")

return (
<TokenAmountForm
tokenBalanceInputPlaceholder="BTC"
currency="bitcoin"
tokenBalance={tokenBalance}
minTokenAmount={BITCOIN_MIN_AMOUNT}
minTokenAmount={minDepositAmount}
onSubmitForm={onSubmitForm}
>
<StakeDetails
currency="bitcoin"
minTokenAmount={BITCOIN_MIN_AMOUNT}
minTokenAmount={minDepositAmount}
maxTokenAmount={tokenBalance}
/>
<FormSubmitButton mt={4}>Stake</FormSubmitButton>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,29 @@
import React from "react"
import { Card, CardBody, Flex, HStack } from "@chakra-ui/react"
import { BITCOIN_MIN_AMOUNT } from "#/constants"
import TokenAmountForm from "#/components/shared/TokenAmountForm"
import { TokenAmountFormValues } from "#/components/shared/TokenAmountForm/TokenAmountFormBase"
import { TextMd, TextSm } from "#/components/shared/Typography"
import Spinner from "#/components/shared/Spinner"
import { FormSubmitButton } from "#/components/shared/Form"
import { useMinDepositAmount } from "#/hooks"
import UnstakeDetails from "./UnstakeDetails"

// TODO: Use a position amount
const MOCK_POSITION_AMOUNT = "2398567898"
const MOCK_POSITION_AMOUNT = BigInt("2398567898")

function UnstakeFormModal({
onSubmitForm,
}: {
onSubmitForm: (values: TokenAmountFormValues) => void
}) {
const minDepositAmount = useMinDepositAmount()

return (
<TokenAmountForm
tokenBalanceInputPlaceholder="BTC"
currency="bitcoin"
tokenBalance={MOCK_POSITION_AMOUNT}
minTokenAmount={BITCOIN_MIN_AMOUNT}
minTokenAmount={minDepositAmount}
onSubmitForm={onSubmitForm}
>
<Flex flexDirection="column" gap={10}>
Expand Down
4 changes: 2 additions & 2 deletions dapp/src/components/shared/CurrencyBalance/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { CurrencyType } from "#/types"

export type CurrencyBalanceProps = {
currency: CurrencyType
amount?: string | number
amount?: string | number | bigint
shouldBeFormatted?: boolean
desiredDecimals?: number
size?: string
Expand Down Expand Up @@ -40,7 +40,7 @@ export function CurrencyBalance({

const balance = useMemo(() => {
const value = amount ?? 0
if (shouldBeFormatted)
if (shouldBeFormatted || typeof value === "bigint")
return formatTokenAmount(value, decimals, desiredDecimals)

return numberToLocaleString(value, desiredDecimals)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const useTokenAmountFormValue = () => {

export type TokenAmountFormBaseProps = {
formId?: string
tokenBalance: string
tokenBalance: bigint
tokenBalanceInputPlaceholder: string
currency: CurrencyType
children?: React.ReactNode
Expand Down
2 changes: 1 addition & 1 deletion dapp/src/components/shared/TokenAmountForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import TokenAmountFormBase, {

type TokenAmountFormProps = {
onSubmitForm: (values: TokenAmountFormValues) => void
minTokenAmount: string
minTokenAmount: bigint
} & TokenAmountFormBaseProps

const TokenAmountForm = withFormik<TokenAmountFormProps, TokenAmountFormValues>(
Expand Down
8 changes: 3 additions & 5 deletions dapp/src/components/shared/TokenBalanceInput/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ function FiatCurrencyBalance({
export type TokenBalanceInputProps = {
amount?: bigint
currency: CurrencyType
tokenBalance: string | number
tokenBalance: bigint
placeholder?: string
size?: "lg" | "md"
setAmount: (value?: bigint) => void
Expand Down Expand Up @@ -143,9 +143,7 @@ export default function TokenBalanceInput({
placeholder={placeholder}
{...inputProps}
value={
amount
? fixedPointNumberToString(BigInt(amount), decimals)
: undefined
amount ? fixedPointNumberToString(amount, decimals) : undefined
}
onValueChange={(values: NumberFormatInputValues) =>
handleValueChange(values.value)
Expand All @@ -155,7 +153,7 @@ export default function TokenBalanceInput({
}}
/>
<InputRightElement>
<Button h="70%" onClick={() => setAmount(BigInt(tokenBalance))}>
<Button h="70%" onClick={() => setAmount(tokenBalance)}>
Max
</Button>
</InputRightElement>
Expand Down
4 changes: 2 additions & 2 deletions dapp/src/constants/currency.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { Currency, CurrencyType } from "#/types"
import { EthereumNetwork } from "@acre-btc/sdk"
import { ETHEREUM_NETWORK } from "./chains"
import { BITCOIN_DESIRED_DECIMALS } from "./staking"

export const BITCOIN: Currency = {
name: "Bitcoin",
symbol: "BTC",
decimals: 8,
desiredDecimals: BITCOIN_DESIRED_DECIMALS,
// TODO: Change when min amount of BTC will be updated
desiredDecimals: 5,
}

export const STBTC: Currency = {
Expand Down
10 changes: 0 additions & 10 deletions dapp/src/constants/staking.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1 @@
import { getDesiredDecimals } from "#/utils/numbers"

// TODO: Read the value from the SDK, once we expose it
export const BITCOIN_MIN_AMOUNT = String(1e4) // 0.0001 BTC

export const BITCOIN_DESIRED_DECIMALS = getDesiredDecimals(
BITCOIN_MIN_AMOUNT,
8,
)

export const REFERRAL = import.meta.env.VITE_REFERRAL
3 changes: 1 addition & 2 deletions dapp/src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from "./store"
export * from "./sdk"
export * from "./useDetectThemeMode"
export * from "./useRequestBitcoinAccount"
export * from "./useRequestEthereumAccount"
Expand All @@ -9,7 +10,6 @@ export * from "./useModalFlowContext"
export * from "./useTransactionContext"
export * from "./useTransactionDetails"
export * from "./useDepositBTCTransaction"
export * from "./useInitializeAcreSdk"
export * from "./useTransactionHistoryTable"
export * from "./useExecuteFunction"
export * from "./useStakeFlowContext"
Expand All @@ -19,5 +19,4 @@ export * from "./useDepositTelemetry"
export * from "./useFetchBTCPriceUSD"
export * from "./useCountdown"
export * from "./useActivities"
export * from "./useFetchBTCBalance"
export * from "./useSize"
4 changes: 4 additions & 0 deletions dapp/src/hooks/sdk/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from "./useInitializeAcreSdk"
export * from "./useFetchMinDepositAmount"
export * from "./useInitDataFromSdk"
export * from "./useFetchBTCBalance"
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { EthereumAddress } from "@acre-btc/sdk"
import { useAcreContext } from "#/acre-react/hooks"
import { logPromiseFailure } from "#/utils"
import { setEstimatedBtcBalance, setSharesBalance } from "#/store/btc"
import { useWalletContext } from "./useWalletContext"
import { useAppDispatch } from "./store"
import { useWalletContext } from "../useWalletContext"
import { useAppDispatch } from "../store/useAppDispatch"

export function useFetchBTCBalance() {
const { acre, isInitialized } = useAcreContext()
Expand Down
22 changes: 22 additions & 0 deletions dapp/src/hooks/sdk/useFetchMinDepositAmount.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { useEffect } from "react"
import { setMinDepositAmount } from "#/store/btc"
import { logPromiseFailure } from "#/utils"
import { useAcreContext } from "#/acre-react/hooks"
import { useAppDispatch } from "../store/useAppDispatch"

export function useFetchMinDepositAmount() {
const { acre, isInitialized } = useAcreContext()
const dispatch = useAppDispatch()

useEffect(() => {
if (!isInitialized || !acre) return

const fetchMinDepositAmount = async () => {
const minDepositAmount = await acre.staking.minDepositAmount()

dispatch(setMinDepositAmount(minDepositAmount))
}

logPromiseFailure(fetchMinDepositAmount())
}, [acre, dispatch, isInitialized])
}
7 changes: 7 additions & 0 deletions dapp/src/hooks/sdk/useInitDataFromSdk.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { useFetchBTCBalance } from "./useFetchBTCBalance"
import { useFetchMinDepositAmount } from "./useFetchMinDepositAmount"

export function useInitDataFromSdk() {
useFetchBTCBalance()
useFetchMinDepositAmount()
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useEffect } from "react"
import { ETHEREUM_NETWORK } from "#/constants"
import { logPromiseFailure } from "#/utils"
import { useAcreContext } from "#/acre-react/hooks"
import { useWalletContext } from "./useWalletContext"
import { useWalletContext } from "../useWalletContext"

export function useInitializeAcreSdk() {
const { ethAccount } = useWalletContext()
Expand Down
1 change: 1 addition & 0 deletions dapp/src/hooks/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from "./useAppDispatch"
export * from "./useAppSelector"
export * from "./useEstimatedBTCBalance"
export * from "./useSharesBalance"
export * from "./useMinDepositAmount"
6 changes: 6 additions & 0 deletions dapp/src/hooks/store/useMinDepositAmount.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { selectMinDepositAmount } from "#/store/btc"
import { useAppSelector } from "./useAppSelector"

export function useMinDepositAmount() {
return useAppSelector(selectMinDepositAmount)
}
2 changes: 1 addition & 1 deletion dapp/src/hooks/useCurrencyConversion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { useAppSelector } from "./store"

type CurrencyConversionType = {
currency: CurrencyType
amount?: number | string
amount?: number | string | bigint
}

// TODO: should be updated to handle another currencies
Expand Down
5 changes: 2 additions & 3 deletions dapp/src/hooks/useInitApp.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { useInitDataFromSdk, useInitializeAcreSdk } from "./sdk"
import { useSentry } from "./sentry"
import { useInitializeAcreSdk } from "./useInitializeAcreSdk"
import { useFetchBTCPriceUSD } from "./useFetchBTCPriceUSD"
import { useFetchBTCBalance } from "./useFetchBTCBalance"

export function useInitApp() {
// TODO: Let's uncomment when dark mode is ready
// useDetectThemeMode()
useSentry()
useInitializeAcreSdk()
useInitDataFromSdk()
useFetchBTCPriceUSD()
useFetchBTCBalance()
}
5 changes: 5 additions & 0 deletions dapp/src/store/btc/btcSelector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ import { RootState } from ".."

export const selectEstimatedBtcBalance = (state: RootState): bigint =>
state.btc.estimatedBtcBalance

export const selectSharesBalance = (state: RootState): bigint =>
state.btc.sharesBalance

export const selectBtcUsdPrice = (state: RootState): number =>
state.btc.usdPrice

export const selectMinDepositAmount = (state: RootState) =>
state.btc.minDepositAmount
8 changes: 7 additions & 1 deletion dapp/src/store/btc/btcSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ type BtcState = {
sharesBalance: bigint
isLoadingPriceUSD: boolean
usdPrice: number
minDepositAmount: bigint
}

const initialState: BtcState = {
estimatedBtcBalance: 0n,
sharesBalance: 0n,
isLoadingPriceUSD: false,
usdPrice: 0,
minDepositAmount: 0n,
}

// Store Bitcoin data such as balance, balance in usd and other related data to Bitcoin chain.
Expand All @@ -26,6 +28,9 @@ export const btcSlice = createSlice({
setEstimatedBtcBalance(state, action: PayloadAction<bigint>) {
state.estimatedBtcBalance = action.payload
},
setMinDepositAmount(state, action: PayloadAction<bigint>) {
state.minDepositAmount = action.payload
},
},
extraReducers: (builder) => {
builder.addCase(fetchBTCPriceUSD.pending, (state) => {
Expand All @@ -44,4 +49,5 @@ export const btcSlice = createSlice({
},
})

export const { setSharesBalance, setEstimatedBtcBalance } = btcSlice.actions
export const { setSharesBalance, setEstimatedBtcBalance, setMinDepositAmount } =
btcSlice.actions
13 changes: 5 additions & 8 deletions dapp/src/utils/forms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,21 @@ export function getErrorsObj<T>(errors: { [key in keyof T]: string }) {

export function validateTokenAmount(
value: bigint | undefined,
maxValue: string,
minValue: string,
maxValue: bigint,
minValue: bigint,
currency: CurrencyType,
): string | undefined {
if (value === undefined) return ERRORS.REQUIRED

const { decimals } = getCurrencyByType(currency)

const maxValueInBI = BigInt(maxValue)
const minValueInBI = BigInt(minValue)

const isMaximumValueExceeded = value > maxValueInBI
const isMinimumValueFulfilled = value >= minValueInBI
const isMaximumValueExceeded = value > maxValue
const isMinimumValueFulfilled = value >= minValue

if (isMaximumValueExceeded) return ERRORS.EXCEEDED_VALUE
if (!isMinimumValueFulfilled)
return ERRORS.INSUFFICIENT_VALUE(
fixedPointNumberToString(BigInt(minValue), decimals),
fixedPointNumberToString(minValue, decimals),
)

return undefined
Expand Down
8 changes: 1 addition & 7 deletions dapp/src/utils/numbers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export function bigIntToUserAmount(
*
*/
export const formatTokenAmount = (
amount: number | string,
amount: number | string | bigint,
decimals = 18,
desiredDecimals = 2,
) => {
Expand Down Expand Up @@ -201,12 +201,6 @@ export function userAmountToBigInt(
export const randomInteger = (min: number, max: number) =>
Math.floor(Math.random() * (max - min + 1)) + min

export function getDesiredDecimals(amount: string | number, decimals: number) {
const undecimaledAmount = amount.toString()
const desiredDecimals = decimals - undecimaledAmount.length + 1
return desiredDecimals > 0 ? desiredDecimals : 2
}

export const addLeadingZero = (num: number): string =>
num >= 0 && num <= 9 ? `0${num}` : `${num}`

Expand Down
5 changes: 5 additions & 0 deletions sdk/src/lib/contracts/bitcoin-depositor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,9 @@ export interface BitcoinDepositor extends DepositorProxy {
* @param extraData Encoded extra data.
*/
decodeExtraData(extraData: string): DecodedExtraData

/**
* @returns Minimum deposit amount.
*/
minDepositAmount(): Promise<bigint>
}
Loading

0 comments on commit 1a2dbab

Please sign in to comment.