Skip to content

Commit

Permalink
feat: add sendpsbt function
Browse files Browse the repository at this point in the history
  • Loading branch information
Polybius93 committed Feb 5, 2024
1 parent 96769c7 commit 0dbd736
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 94 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { useContext, useEffect, useState } from 'react';
import { useDispatch } from 'react-redux';

import { Button, VStack } from '@chakra-ui/react';
import { Button, Checkbox, Fade, HStack, Stack, Text, VStack, useToast } from '@chakra-ui/react';
import { VaultCard } from '@components/vault/vault-card';
import { useSignPSBT } from '@hooks/use-sign-psbt';
import { useVaults } from '@hooks/use-vaults';
import { BitcoinError } from '@models/error-types';
import { Vault } from '@models/vault';
import { BlockchainContext } from '@providers/blockchain-context-provider';
import { mintUnmintActions } from '@store/slices/mintunmint/mintunmint.actions';
Expand All @@ -15,10 +17,13 @@ interface LockScreenProps {
}

export function LockScreen({ currentStep }: LockScreenProps): React.JSX.Element {
const toast = useToast();
const dispatch = useDispatch();
const { readyVaults } = useVaults();
const blockchainContext = useContext(BlockchainContext);
const bitcoin = blockchainContext?.bitcoin;
const { handleSignTransaction, fundingTransactionSigned, closingTransactionSigned } =
useSignPSBT(bitcoin);
const ethereum = blockchainContext?.ethereum;

const [isSubmitting, setIsSubmitting] = useState(false);
Expand All @@ -39,10 +44,17 @@ export function LockScreen({ currentStep }: LockScreenProps): React.JSX.Element

try {
setIsSubmitting(true);
await bitcoin?.fetchBitcoinContractOfferAndSendToUserWallet(currentVault);
await handleSignTransaction(currentVault.collateral, currentVault.uuid);
setIsSubmitting(false);
} catch (error) {
setIsSubmitting(false);
throw new Error('Error locking vault');
toast({
title: 'Failed to sign transaction',
description: error instanceof BitcoinError ? error.message : '',
status: 'error',
duration: 9000,
isClosable: true,
});
}
}

Expand All @@ -68,6 +80,32 @@ export function LockScreen({ currentStep }: LockScreenProps): React.JSX.Element
>
Cancel
</Button>
<Stack w={'100%'}>
<Fade in={isSubmitting || (fundingTransactionSigned && !closingTransactionSigned)}>
<VStack w={'100%'} spacing={'10px'} p={'15px'} bgColor={'white.03'} borderRadius={'md'}>
<HStack w={'100%'} justifyContent={'space-between'}>
<Checkbox
iconColor={'accent.orange.01'}
isChecked={fundingTransactionSigned}
isDisabled
/>
<Text color={'white.01'} fontSize={'sm'} fontWeight={800}>
Funding Transaction
</Text>
</HStack>
<HStack w={'100%'} justifyContent={'space-between'}>
<Checkbox
iconColor={'accent.orange.01'}
isChecked={closingTransactionSigned}
isDisabled
/>
<Text color={'white.01'} fontSize={'sm'} fontWeight={800}>
Closing Transaction
</Text>
</HStack>
</VStack>
</Fade>
</Stack>
</VStack>
);
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,8 @@
import { useContext, useState } from 'react';

import {
Button,
Checkbox,
Fade,
FormControl,
FormErrorMessage,
HStack,
Stack,
Text,
VStack,
useToast,
} from '@chakra-ui/react';
import { Button, FormControl, FormErrorMessage, Text, VStack, useToast } from '@chakra-ui/react';
import { customShiftValue } from '@common/utilities';
import { useSignPSBT } from '@hooks/use-sign-psbt';
import { BitcoinError } from '@models/error-types';
import { EthereumError } from '@models/error-types';
import { BlockchainContext } from '@providers/blockchain-context-provider';
import { Form, Formik } from 'formik';

Expand All @@ -30,23 +18,20 @@ const initialValues: TransactionFormValues = { amount: 0.001 };
export function TransactionForm(): React.JSX.Element {
const toast = useToast();
const blockchainContext = useContext(BlockchainContext);
const { handleSignTransaction, fundingTransactionSigned, closingTransactionSigned } = useSignPSBT(
blockchainContext?.bitcoin
);
const ethereum = blockchainContext?.ethereum;
const bitcoinPrice = blockchainContext?.bitcoin.bitcoinPrice;
const [isSubmitting, setIsSubmitting] = useState(false);

async function handleSetup(btcDepositAmount: number) {
try {
setIsSubmitting(true);
const shiftedBTCDepositAmount = customShiftValue(btcDepositAmount, 8, false);
await handleSignTransaction(shiftedBTCDepositAmount);
setIsSubmitting(false);
await ethereum?.setupVault(shiftedBTCDepositAmount);
} catch (error) {
setIsSubmitting(false);
toast({
title: 'Failed to sign transaction',
description: error instanceof BitcoinError ? error.message : '',
title: 'Failed to create vault',
description: error instanceof EthereumError ? error.message : '',
status: 'error',
duration: 9000,
isClosable: true,
Expand Down Expand Up @@ -78,42 +63,8 @@ export function TransactionForm(): React.JSX.Element {
type={'submit'}
isDisabled={Boolean(errors.amount)}
>
{fundingTransactionSigned ? 'Sign Closing Transaction' : 'Lock Bitcoin'}
Create Vault
</Button>
<Stack w={'100%'}>
<Fade
in={isSubmitting || (fundingTransactionSigned && !closingTransactionSigned)}
>
<VStack
w={'100%'}
spacing={'10px'}
p={'15px'}
bgColor={'white.03'}
borderRadius={'md'}
>
<HStack w={'100%'} justifyContent={'space-between'}>
<Checkbox
iconColor={'accent.orange.01'}
isChecked={fundingTransactionSigned}
isDisabled
/>
<Text color={'white.01'} fontSize={'sm'} fontWeight={800}>
Funding Transaction
</Text>
</HStack>
<HStack w={'100%'} justifyContent={'space-between'}>
<Checkbox
iconColor={'accent.orange.01'}
isChecked={closingTransactionSigned}
isDisabled
/>
<Text color={'white.01'} fontSize={'sm'} fontWeight={800}>
Closing Transaction
</Text>
</HStack>
</VStack>
</Fade>
</Stack>
</VStack>
</FormControl>
</Form>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,17 @@ export function Walkthrough({ flow, currentStep }: WalkthroughProps): React.JSX.
<WalkthroughHeader
currentStep={currentStep}
title={'Create Vault'}
blockchain={'bitcoin'}
blockchain={'ethereum'}
/>
<Text color={'white.01'} fontSize={'md'}>
Select an amount of dlcBTC you would like to mint and sign the required transactions
in your{' '}
Select an amount of dlcBTC you would like to mint and confirm it in your{' '}
<Link
color={'accent.cyan.01'}
href="https://leather.io/"
href="https://metamask.io/"
isExternal
textDecoration={'underline'}
>
Bitcoin Wallet
Ethereum Wallet
</Link>
.
</Text>
Expand Down
71 changes: 56 additions & 15 deletions src/app/hooks/use-bitcoin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export interface UseBitcoinReturnType {
signClosingPSBT: (
fundingTransactionID: string,
multisigTransaction: btc.P2TROut,
uuid: string,
userNativeSegwitAddress: string,
btcAmount: number
) => Promise<void>;
Expand Down Expand Up @@ -149,6 +150,13 @@ export function useBitcoin(): UseBitcoinReturnType {
return utxos;
}

async function getAttestorPublicKey(): Promise<string> {
const response = await fetch('http://localhost:3000/publickey');
const attestorPublicKey = await response.text();
console.log('attestorPublicKey', attestorPublicKey)

Check warning on line 156 in src/app/hooks/use-bitcoin.ts

View workflow job for this annotation

GitHub Actions / lint-eslint

Unexpected console statement
return attestorPublicKey;
}

function createMultisigTransactionAndAddress(
userPublicKey: Uint8Array,
attestorPublicKey: Uint8Array,
Expand Down Expand Up @@ -193,19 +201,32 @@ export function useBitcoin(): UseBitcoinReturnType {
return fundingPSBT;
}

function getFundingTransactionID(fundingTransaction: Uint8Array): string {
const sha256x2 = (...msgs: Uint8Array[]) => sha256(sha256(concatBytes(...msgs)));
const fundingTransactionID = hex.encode(sha256x2(fundingTransaction).reverse());
return fundingTransactionID;
}
// function getFundingTransactionID(fundingTransaction: Uint8Array): string {
// const sha256x2 = (...msgs: Uint8Array[]) => sha256(sha256(concatBytes(...msgs)));
// const fundingTransactionID = hex.encode(sha256x2(fundingTransaction).reverse());
// return fundingTransactionID;
// }

function createClosingTransaction(
async function sendPSBT(closingPSBT: string, uuid: string, userNativeSegwitAddress: string): Promise<void> {
try {
const response = await fetch('http://localhost:3000/create-psbt-event', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*'},
body: JSON.stringify({ closingPSBT, uuid, userNativeSegwitAddress }),
});
console.log('response', response)

Check warning on line 217 in src/app/hooks/use-bitcoin.ts

View workflow job for this annotation

GitHub Actions / lint-eslint

Unexpected console statement
} catch (error) {
throw new BitcoinError(`Error sending PSBT: ${error}`);
}
}
async function createClosingTransaction(
fundingTransactionID: string,
multisigTransaction: any,
userNativeSegwitAddress: string,
uuid: string,
btcAmount: number,
btcNetwork: BitcoinNetwork
): Uint8Array {
): Promise<Uint8Array> {
const closingTransaction = new btc.Transaction();
const fundingInput = {
txid: hexToBytes(fundingTransactionID),
Expand All @@ -216,6 +237,7 @@ export function useBitcoin(): UseBitcoinReturnType {
closingTransaction.addInput(fundingInput);
closingTransaction.addOutputAddress(userNativeSegwitAddress, BigInt(btcAmount), btcNetwork);
const closingPSBT = closingTransaction.toPSBT();
await sendPSBT(bytesToHex(closingPSBT), uuid, userNativeSegwitAddress);
return closingPSBT;
}

Expand All @@ -235,29 +257,48 @@ export function useBitcoin(): UseBitcoinReturnType {
utxos: any[],
btcAmount: number,
btcNetwork: BitcoinNetwork
): Promise<{ fundingTransaction: Uint8Array; fundingTransactionHex: string }> {
): Promise<{ fundingTransactionHex: string; fundingTransactionID: string }> {
const fundingTransaction = createFundingTransaction(
multisigAddress,
userChangeAddress,
utxos,
btcAmount,
btcNetwork
);
const fundingTransactionHex = await signPSBT(fundingTransaction, true);
return { fundingTransaction, fundingTransactionHex };
const fundingTransactionHex = await signPSBT(fundingTransaction, false);
const transaction = btc.Transaction.fromPSBT(hexToBytes(fundingTransactionHex));
transaction.finalize();

let fundingTransactionID = '';
await fetch(`${ELECTRUM_API_URL}/tx`, {
method: 'POST',
body: bytesToHex(transaction.extract()),
}).then(async (response) => {
fundingTransactionID = await response.text();
});
return { fundingTransactionHex, fundingTransactionID };
}

async function handleClosingTransaction(
fundingTransactionID: string,
multisigTransaction: btc.P2TROut,
userAddress: string,
uuid: string,
btcAmount: number,
btcNetwork: BitcoinNetwork
): Promise<string> {
const closingTransaction = createClosingTransaction(
console.log('fundingTransactionID', fundingTransactionID)

Check warning on line 290 in src/app/hooks/use-bitcoin.ts

View workflow job for this annotation

GitHub Actions / lint-eslint

Unexpected console statement
console.log('multisigTransaction', multisigTransaction)

Check warning on line 291 in src/app/hooks/use-bitcoin.ts

View workflow job for this annotation

GitHub Actions / lint-eslint

Unexpected console statement
console.log('userAddress', userAddress)

Check warning on line 292 in src/app/hooks/use-bitcoin.ts

View workflow job for this annotation

GitHub Actions / lint-eslint

Unexpected console statement
console.log('uuid', uuid)

Check warning on line 293 in src/app/hooks/use-bitcoin.ts

View workflow job for this annotation

GitHub Actions / lint-eslint

Unexpected console statement
console.log('btcAmount', btcAmount)

Check warning on line 294 in src/app/hooks/use-bitcoin.ts

View workflow job for this annotation

GitHub Actions / lint-eslint

Unexpected console statement
console.log('btcNetwork', btcNetwork)

const closingTransaction = await createClosingTransaction(
fundingTransactionID,
multisigTransaction,
userAddress,
uuid,
btcAmount,
btcNetwork
);
Expand All @@ -279,7 +320,7 @@ export function useBitcoin(): UseBitcoinReturnType {
const userTaprootAddress = userAddresses[1] as BitcoinTaprootAddress;
const userPublicKey = userTaprootAddress.tweakedPublicKey;

const attestorPublicKey = 'a27d8d7e1976c7ffaea08ead4aec592da663bcdda75d49ff4bf92dfcb508476e';
const attestorPublicKey = await getAttestorPublicKey();

// const preImage = hexToBytes('107661134f21fc7c02223d50ab9eb3600bc3ffc3712423a1e47bb1f9a9dbf55f');
// const preImageHash = hexToBytes(
Expand All @@ -293,7 +334,7 @@ export function useBitcoin(): UseBitcoinReturnType {
btcNetwork
);

const { fundingTransaction, fundingTransactionHex } = await handleFundingTransaction(
const { fundingTransactionHex, fundingTransactionID } = await handleFundingTransaction(
multisigAddress,
userNativeSegwitAddress,
userUTXOs,
Expand All @@ -302,14 +343,13 @@ export function useBitcoin(): UseBitcoinReturnType {
);
console.log('fundingTransactionHex', fundingTransactionHex);

const fundingTransactionID = getFundingTransactionID(fundingTransaction);

return { fundingTransactionID, multisigTransaction, userNativeSegwitAddress, btcAmount };
}

async function signClosingPSBT(
fundingTransactionID: string,
multisigTransaction: btc.P2TROut,
uuid: string,
userNativeSegwitAddress: string,
btcAmount: number
): Promise<void> {
Expand All @@ -319,6 +359,7 @@ export function useBitcoin(): UseBitcoinReturnType {
fundingTransactionID,
multisigTransaction,
userNativeSegwitAddress,
uuid,
btcAmount,
btcNetwork
);
Expand Down
Loading

0 comments on commit 0dbd736

Please sign in to comment.