Skip to content

Commit

Permalink
Fix unminting process
Browse files Browse the repository at this point in the history
There was a bug where after doing an unmint the dApp did not redirect to the
UnmintingDetails page. It behaved like that because it wrongly builded a
redemption key - we passed already prefixed `walletPublicKey` and
`redeemerOutputScript`, and the it was prefixed again in sdk lib. I've fixed it
by extracting only the code that was necessary from that funcion (so I'm acually
reverting the changes).

Besides that I've made it possible to pass additional argument to success
function in `useSendTransactionFromFn`. It was neede because
`findWalletForRedemption` is not a publi function anymore in our SDK and the
wallet is found during a redemption request (and the method from SDK return tx
hash of the redemption request and wallet public key hash). We return both of
them in our TBTC class (in threshold-ts lib) and the pass it ot `onSuccess`
callback in the hook I mentioned earlier. This way we could also get rid of
`findWalletForRedemption` method and all things related to that.

I've also managed to remove unnecessary imports. It looks like there are no
import from tbtc-v2 or tbtc-v2.ts in our threshold-ts/tbtc/index.ts file (at
least not direct ones :D).
  • Loading branch information
michalsmiarowski committed Nov 10, 2023
1 parent ca5af68 commit bb90941
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 204 deletions.
33 changes: 14 additions & 19 deletions src/components/Modal/tBTC/InitiateUnminting.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,7 @@ import {
useRedemptionEstimatedFees,
useRequestRedemption,
} from "../../../hooks/tbtc"
import {
BaseModalProps,
UnspentTransactionOutputPlainObject,
} from "../../../types"
import { BaseModalProps } from "../../../types"
import shortenAddress from "../../../utils/shortenAddress"
import { buildRedemptionDetailsLink } from "../../../utils/tBTC"
import { OnSuccessCallback } from "../../../web3/hooks"
Expand All @@ -38,41 +35,39 @@ import withBaseModal from "../withBaseModal"
type InitiateUnmintingProps = {
unmintAmount: string
btcAddress: string
wallet: {
walletPublicKey: string
mainUtxo: UnspentTransactionOutputPlainObject
}
} & BaseModalProps

const InitiateUnmintingBase: FC<InitiateUnmintingProps> = ({
closeModal,
unmintAmount,
btcAddress,
wallet,
}) => {
const navigate = useNavigate()
const { account } = useWeb3React()
const { estimatedBTCAmount, thresholdNetworkFee } =
useRedemptionEstimatedFees(unmintAmount)
const threshold = useThreshold()

const onSuccess: OnSuccessCallback = (receipt) => {
navigate(
buildRedemptionDetailsLink(
receipt.transactionHash,
account!,
wallet.walletPublicKey,
btcAddress,
threshold.tbtc.bitcoinNetwork
const onSuccess: OnSuccessCallback = (receipt, additionalParams) => {
//@ts-ignore
const { walletPublicKey } = additionalParams
if (walletPublicKey) {
navigate(
buildRedemptionDetailsLink(
receipt.transactionHash,
account!,
walletPublicKey,
btcAddress,
threshold.tbtc.bitcoinNetwork
)
)
)
}
closeModal()
}

const { sendTransaction } = useRequestRedemption(onSuccess)

const initiateUnminting = async () => {
const { walletPublicKey, mainUtxo } = wallet
await sendTransaction(btcAddress, unmintAmount)
}

Expand Down
78 changes: 35 additions & 43 deletions src/hooks/tbtc/useFetchRedemptionDetails.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { BigNumber } from "ethers"
import { useEffect, useState } from "react"
import { Hex } from "tbtc-sdk-v2"
import {
useIsSdkInitializing,
useThreshold,
Expand Down Expand Up @@ -48,11 +46,7 @@ export const useFetchRedemptionDetails = (
const { isSdkInitializing, isSdkInitialized } = useIsSdkInitializing()

useEffect(() => {
// TODO: Check if it works properly with new SDK
if (!isSdkInitializing || !isSdkInitialized) {
setError("Sdk is not initialized!")
return
}
if (!isSdkInitialized) return

if (!redeemer || isEmptyOrZeroAddress(redeemer)) {
setError("Invalid redeemer value.")
Expand Down Expand Up @@ -90,37 +84,38 @@ export const useFetchRedemptionDetails = (
// reduce the number of records - any user can request redemption for
// the same wallet.
const redemptionRequestedEvent = (
await threshold.tbtc.sdk!.tbtcContracts.bridge.getRedemptionRequestedEvents()
).find(
(event) =>
// It's not possible that the redemption request with the same
// redemption key can be created in the same transaction - it means
// that redemption key is unique and can be used for only one
// pending request at the same time. We also need to find an event
// by transaction hash because it's possible that there can be
// multiple `RedemptionRequest` events with the same redemption key
// but created at different times eg:
// - redemption X requested,
// - redemption X was handled successfully and the redemption X was
// removed from `pendingRedemptions` map,
// - the same wallet is still in `live` state and can handle
// redemption request with the same `walletPubKeyHash` and
// `redeemerOutputScript` pair,
// - now 2 `RedemptionRequested` events exist with the same
// redemption key(the same `walletPubKeyHash` and
// `redeemerOutputScript` pair).
//
// In that case we must know exactly which redemption request we
// want to fetch.

event.transactionHash.toPrefixedString() ===
redemptionRequestedTxHash &&
await threshold.tbtc.getRedemptionRequestedEvents({
walletPublicKeyHash,
redeemer,
})
).find((event) => {
// It's not possible that the redemption request with the same
// redemption key can be created in the same transaction - it means
// that redemption key is unique and can be used for only one
// pending request at the same time. We also need to find an event
// by transaction hash because it's possible that there can be
// multiple `RedemptionRequest` events with the same redemption key
// but created at different times eg:
// - redemption X requested,
// - redemption X was handled successfully and the redemption X was
// removed from `pendingRedemptions` map,
// - the same wallet is still in `live` state and can handle
// redemption request with the same `walletPubKeyHash` and
// `redeemerOutputScript` pair,
// - now 2 `RedemptionRequested` events exist with the same
// redemption key(the same `walletPubKeyHash` and
// `redeemerOutputScript` pair).
//
// In that case we must know exactly which redemption request we
// want to fetch.
return (
event.txHash === redemptionRequestedTxHash &&
threshold.tbtc.buildRedemptionKey(
event.walletPublicKeyHash.toString(),
event.redeemerOutputScript.toString()
event.walletPublicKeyHash,
event.redeemerOutputScript
) === redemptionKey
)

)
})
if (!redemptionRequestedEvent) {
throw new Error("Redemption not found...")
}
Expand Down Expand Up @@ -166,10 +161,9 @@ export const useFetchRedemptionDetails = (
) {
setRedemptionData({
requestedAmount: fromSatoshiToTokenPrecision(
redemptionRequestedEvent.requestedAmount
redemptionRequestedEvent.amount
).toString(),
redemptionRequestedTxHash:
redemptionRequestedEvent.transactionHash.toString(),
redemptionRequestedTxHash: redemptionRequestedEvent.txHash,
redemptionCompletedTxHash: undefined,
requestedAt: requestedAt,
redemptionTimedOutTxHash: timedOutTxHash,
Expand All @@ -180,7 +174,6 @@ export const useFetchRedemptionDetails = (
})
return
}

// If we are here it means that the redemption request was handled
// successfully and we need to find all `RedemptionCompleted` events
// that happened after `redemptionRequest` block and filter by
Expand Down Expand Up @@ -213,11 +206,10 @@ export const useFetchRedemptionDetails = (

setRedemptionData({
requestedAmount: fromSatoshiToTokenPrecision(
redemptionRequestedEvent.requestedAmount
redemptionRequestedEvent.amount
).toString(),
receivedAmount,
redemptionRequestedTxHash:
redemptionRequestedEvent.transactionHash.toString(),
redemptionRequestedTxHash: redemptionRequestedEvent.txHash,
redemptionCompletedTxHash: {
chain: txHash,
bitcoin: redemptionBitcoinTxHash,
Expand Down
40 changes: 2 additions & 38 deletions src/pages/tBTC/Bridge/Unmint.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,6 @@ import { useModal } from "../../../hooks/useModal"
import { UnmintDetails } from "./UnmintDetails"
import { UnmintingCard } from "./UnmintingCard"
import { featureFlags } from "../../../constants"
import { RedemptionWalletData } from "../../../threshold-ts/tbtc"
import { UnspentTransactionOutputPlainObject } from "../../../types/tbtc"
import { BridgeProcessEmptyState } from "./components/BridgeProcessEmptyState"

const UnmintFormPage: PageComponent = ({}) => {
Expand All @@ -69,25 +67,9 @@ const UnmintFormPage: PageComponent = ({}) => {
const threshold = useThreshold()

const onSubmitForm = (values: UnmintFormValues) => {
const { wallet } = values

const walletData: {
walletPublicKey: string
mainUtxo: UnspentTransactionOutputPlainObject
} = {
...wallet,
mainUtxo: {
...wallet.mainUtxo,
transactionHash:
values.wallet.mainUtxo.transactionHash.toPrefixedString(),
value: wallet.mainUtxo.value.toString(),
outputIndex: wallet.mainUtxo.outputIndex.toString(),
},
}
openModal(ModalType.InitiateUnminting, {
unmintAmount: values.amount,
btcAddress: values.btcAddress,
wallet: walletData,
})
}

Expand All @@ -108,7 +90,6 @@ const UnmintFormPage: PageComponent = ({}) => {
maxTokenAmount={balance.toString()}
onSubmitForm={onSubmitForm}
bitcoinNetwork={threshold.tbtc.bitcoinNetwork}
findRedemptionWallet={threshold.tbtc.findWalletForRedemption}
/>
<Box as="p" textAlign="center" mt="4">
<BridgeContractLink />
Expand Down Expand Up @@ -225,22 +206,16 @@ const UnmintFormBase: FC<UnmintFormBaseProps> = ({
type UnmintFormValues = {
amount: string
btcAddress: string
wallet: RedemptionWalletData
}

type UnmintFormProps = {
onSubmitForm: (values: UnmintFormValues) => void
findRedemptionWallet: (
amount: string,
redeemerOutputScript: string
) => Promise<RedemptionWalletData>
} & UnmintFormBaseProps

const UnmintForm = withFormik<UnmintFormProps, UnmintFormValues>({
mapPropsToValues: () => ({
amount: "",
btcAddress: "",
wallet: {} as RedemptionWalletData,
}),
validate: async (values, props) => {
const errors: FormikErrors<UnmintFormValues> = {}
Expand All @@ -254,25 +229,14 @@ const UnmintForm = withFormik<UnmintFormProps, UnmintFormValues>({
props.maxTokenAmount,
UNMINT_MIN_AMOUNT
)
errors.wallet = undefined

// @ts-ignore
return getErrorsObj(errors)
},
handleSubmit: async (
values,
{ props, setFieldValue, setFieldError, setSubmitting }
) => {
handleSubmit: async (values, { props, setFieldError, setSubmitting }) => {
try {
setSubmitting(true)

const wallet = await props.findRedemptionWallet(
values.amount,
values.btcAddress
)
setFieldValue("wallet", wallet, false)

props.onSubmitForm({ ...values, wallet })
props.onSubmitForm({ ...values })
} catch (error) {
setFieldError("wallet", (error as Error).message)
} finally {
Expand Down
Loading

0 comments on commit bb90941

Please sign in to comment.