diff --git a/src/app/components/mint-unmint/components/lock-screen/lock-screen.tsx b/src/app/components/mint-unmint/components/lock-screen/lock-screen.tsx
index 44c974ad..55b87e14 100644
--- a/src/app/components/mint-unmint/components/lock-screen/lock-screen.tsx
+++ b/src/app/components/mint-unmint/components/lock-screen/lock-screen.tsx
@@ -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';
@@ -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);
@@ -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,
+ });
}
}
@@ -68,6 +80,32 @@ export function LockScreen({ currentStep }: LockScreenProps): React.JSX.Element
>
Cancel
+
+
+
+
+
+
+ Funding Transaction
+
+
+
+
+
+ Closing Transaction
+
+
+
+
+
);
}
diff --git a/src/app/components/mint-unmint/components/transaction-form/transaction-form.tsx b/src/app/components/mint-unmint/components/transaction-form/transaction-form.tsx
index e06aa06b..fec9e1e1 100644
--- a/src/app/components/mint-unmint/components/transaction-form/transaction-form.tsx
+++ b/src/app/components/mint-unmint/components/transaction-form/transaction-form.tsx
@@ -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';
@@ -30,9 +18,7 @@ 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);
@@ -40,13 +26,12 @@ export function TransactionForm(): React.JSX.Element {
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,
@@ -78,42 +63,8 @@ export function TransactionForm(): React.JSX.Element {
type={'submit'}
isDisabled={Boolean(errors.amount)}
>
- {fundingTransactionSigned ? 'Sign Closing Transaction' : 'Lock Bitcoin'}
+ Create Vault
-
-
-
-
-
-
- Funding Transaction
-
-
-
-
-
- Closing Transaction
-
-
-
-
-
diff --git a/src/app/components/mint-unmint/components/walkthrough/walkthrough.tsx b/src/app/components/mint-unmint/components/walkthrough/walkthrough.tsx
index e52befff..7ab2a4d0 100644
--- a/src/app/components/mint-unmint/components/walkthrough/walkthrough.tsx
+++ b/src/app/components/mint-unmint/components/walkthrough/walkthrough.tsx
@@ -25,18 +25,17 @@ export function Walkthrough({ flow, currentStep }: WalkthroughProps): React.JSX.
- 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{' '}
- Bitcoin Wallet
+ Ethereum Wallet
.
diff --git a/src/app/hooks/use-bitcoin.ts b/src/app/hooks/use-bitcoin.ts
index 0f0e0187..8b653cc8 100644
--- a/src/app/hooks/use-bitcoin.ts
+++ b/src/app/hooks/use-bitcoin.ts
@@ -2,21 +2,19 @@ import { useEffect, useState } from 'react';
import { BitcoinNetwork, regtest } from '@models/bitcoin-network';
import { BitcoinError } from '@models/error-types';
-import { sha256 } from '@noble/hashes/sha256';
import { bytesToHex, hexToBytes } from '@noble/hashes/utils';
import { hex } from '@scure/base';
import * as btc from '@scure/btc-signer';
-import { concatBytes } from 'micro-packed';
const networkModes = ['mainnet', 'testnet'] as const;
-export type NetworkModes = (typeof networkModes)[number];
+type NetworkModes = (typeof networkModes)[number];
-type BitcoinTestnetModes = 'testnet' | 'regtest' | 'signet';
+// type BitcoinTestnetModes = 'testnet' | 'regtest' | 'signet';
-export type BitcoinNetworkModes = NetworkModes | BitcoinTestnetModes;
+// type BitcoinNetworkModes = NetworkModes | BitcoinTestnetModes;
-export declare enum SignatureHash {
+declare enum SignatureHash {
ALL = 1,
NONE = 2,
SINGLE = 3,
@@ -77,9 +75,7 @@ interface RpcResponse {
}
export interface UseBitcoinReturnType {
- signAndBroadcastFundingPSBT: (
- btcAmount: number
- ) => Promise<{
+ signAndBroadcastFundingPSBT: (btcAmount: number) => Promise<{
fundingTransactionID: string;
multisigTransaction: btc.P2TROut;
userNativeSegwitAddress: string;
@@ -88,18 +84,19 @@ export interface UseBitcoinReturnType {
signClosingPSBT: (
fundingTransactionID: string,
multisigTransaction: btc.P2TROut,
+ uuid: string,
userNativeSegwitAddress: string,
btcAmount: number
) => Promise;
bitcoinPrice: number;
}
-export interface SignAndBroadcastFundingPSBTResult {
- fundingTransactionID: string;
- multisigTransaction: btc.P2TROut;
- userNativeSegwitAddress: string;
- btcAmount: number;
-}
+// interface SignAndBroadcastFundingPSBTResult {
+// fundingTransactionID: string;
+// multisigTransaction: btc.P2TROut;
+// userNativeSegwitAddress: string;
+// btcAmount: number;
+// }
const ELECTRUM_API_URL = 'https://devnet.dlc.link/electrs';
@@ -149,6 +146,13 @@ export function useBitcoin(): UseBitcoinReturnType {
return utxos;
}
+ async function getAttestorPublicKey(): Promise {
+ const response = await fetch('http://localhost:3000/publickey');
+ const attestorPublicKey = await response.text();
+ console.log('attestorPublicKey', attestorPublicKey);
+ return attestorPublicKey;
+ }
+
function createMultisigTransactionAndAddress(
userPublicKey: Uint8Array,
attestorPublicKey: Uint8Array,
@@ -193,19 +197,37 @@ 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 {
+ setBTCNetwork(regtest);
+ 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);
+ } 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 {
const closingTransaction = new btc.Transaction();
const fundingInput = {
txid: hexToBytes(fundingTransactionID),
@@ -216,6 +238,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;
}
@@ -235,7 +258,7 @@ 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,
@@ -243,21 +266,40 @@ export function useBitcoin(): UseBitcoinReturnType {
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 {
- const closingTransaction = createClosingTransaction(
+ console.log('fundingTransactionID', fundingTransactionID);
+ console.log('multisigTransaction', multisigTransaction);
+ console.log('userAddress', userAddress);
+ console.log('uuid', uuid);
+ console.log('btcAmount', btcAmount);
+ console.log('btcNetwork', btcNetwork);
+
+ const closingTransaction = await createClosingTransaction(
fundingTransactionID,
multisigTransaction,
userAddress,
+ uuid,
btcAmount,
btcNetwork
);
@@ -265,9 +307,7 @@ export function useBitcoin(): UseBitcoinReturnType {
return closingTransactionHex;
}
- async function signAndBroadcastFundingPSBT(
- btcAmount: number
- ): Promise<{
+ async function signAndBroadcastFundingPSBT(btcAmount: number): Promise<{
fundingTransactionID: string;
multisigTransaction: btc.P2TROut;
userNativeSegwitAddress: string;
@@ -279,7 +319,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(
@@ -293,7 +333,7 @@ export function useBitcoin(): UseBitcoinReturnType {
btcNetwork
);
- const { fundingTransaction, fundingTransactionHex } = await handleFundingTransaction(
+ const { fundingTransactionHex, fundingTransactionID } = await handleFundingTransaction(
multisigAddress,
userNativeSegwitAddress,
userUTXOs,
@@ -302,14 +342,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 {
@@ -319,6 +358,7 @@ export function useBitcoin(): UseBitcoinReturnType {
fundingTransactionID,
multisigTransaction,
userNativeSegwitAddress,
+ uuid,
btcAmount,
btcNetwork
);
diff --git a/src/app/hooks/use-sign-psbt.ts b/src/app/hooks/use-sign-psbt.ts
index 3c1bf1da..a13f998e 100644
--- a/src/app/hooks/use-sign-psbt.ts
+++ b/src/app/hooks/use-sign-psbt.ts
@@ -1,12 +1,17 @@
import { useEffect, useState } from 'react';
+import { useDispatch, useSelector } from 'react-redux';
+import { customShiftValue } from '@common/utilities';
import { BitcoinError } from '@models/error-types';
import * as btc from '@scure/btc-signer';
+import { RootState } from '@store/index';
+import { mintUnmintActions } from '@store/slices/mintunmint/mintunmint.actions';
+import { vaultActions } from '@store/slices/vault/vault.actions';
import { UseBitcoinReturnType } from './use-bitcoin';
-export interface UseSignPSBTReturnType {
- handleSignTransaction: (btcAmount: number) => Promise;
+interface UseSignPSBTReturnType {
+ handleSignTransaction: (btcAmount: number, vaultUUID: string) => Promise;
fundingTransactionSigned: boolean;
closingTransactionSigned: boolean;
}
@@ -15,7 +20,10 @@ export function useSignPSBT(useBitcoin?: UseBitcoinReturnType): UseSignPSBTRetur
if (!useBitcoin) throw new Error('useBitcoin must be set before useSignPSBT can be used');
const { signAndBroadcastFundingPSBT, signClosingPSBT } = useBitcoin;
+ const { network } = useSelector((state: RootState) => state.account);
+ const dispatch = useDispatch();
const [btcAmount, setBTCAmount] = useState();
+ const [vaultUUID, setVaultUUID] = useState();
const [fundingTransactionSigned, setFundingTransactionSigned] = useState(false);
const [closingTransactionSigned, setClosingTransactionSigned] = useState(false);
const [fundingTransactionID, setFundingTransactionID] = useState();
@@ -23,24 +31,39 @@ export function useSignPSBT(useBitcoin?: UseBitcoinReturnType): UseSignPSBTRetur
const [userNativeSegwitAddress, setUserNativeSegwitAddress] = useState();
useEffect(() => {
- if (
- fundingTransactionSigned &&
- btcAmount &&
- fundingTransactionID &&
- multisigTransaction &&
- userNativeSegwitAddress
- ) {
- handleSignClosingTransaction();
- }
+ const signClosingTransaction = async () => {
+ if (
+ fundingTransactionSigned &&
+ btcAmount &&
+ vaultUUID &&
+ network &&
+ fundingTransactionID &&
+ multisigTransaction &&
+ userNativeSegwitAddress
+ ) {
+ await handleSignClosingTransaction();
+ dispatch(
+ vaultActions.setVaultToFunding({
+ vaultUUID,
+ fundingTX: fundingTransactionID,
+ networkID: network.id,
+ })
+ );
+ dispatch(mintUnmintActions.setMintStep([2, vaultUUID]));
+ }
+ };
+ signClosingTransaction();
}, [
fundingTransactionSigned,
btcAmount,
fundingTransactionID,
multisigTransaction,
userNativeSegwitAddress,
+ network,
+ vaultUUID,
]);
- async function handleSignFundingTransaction(btcAmount: number) {
+ async function handleSignFundingTransaction(btcAmount: number): Promise {
try {
const { fundingTransactionID, multisigTransaction, userNativeSegwitAddress } =
await signAndBroadcastFundingPSBT(btcAmount);
@@ -49,6 +72,7 @@ export function useSignPSBT(useBitcoin?: UseBitcoinReturnType): UseSignPSBTRetur
setMultisigTransaction(multisigTransaction);
setUserNativeSegwitAddress(userNativeSegwitAddress);
setFundingTransactionSigned(true);
+ return fundingTransactionID;
} catch (error) {
throw new BitcoinError(`Error signing funding transaction: ${error}`);
}
@@ -56,7 +80,13 @@ export function useSignPSBT(useBitcoin?: UseBitcoinReturnType): UseSignPSBTRetur
async function handleSignClosingTransaction() {
try {
- if (!fundingTransactionID || !multisigTransaction || !userNativeSegwitAddress || !btcAmount) {
+ if (
+ !fundingTransactionID ||
+ !multisigTransaction ||
+ !userNativeSegwitAddress ||
+ !btcAmount ||
+ !vaultUUID
+ ) {
throw new Error(
'Funding transaction must be signed before closing transaction can be signed'
);
@@ -64,6 +94,7 @@ export function useSignPSBT(useBitcoin?: UseBitcoinReturnType): UseSignPSBTRetur
await signClosingPSBT(
fundingTransactionID,
multisigTransaction,
+ vaultUUID,
userNativeSegwitAddress,
btcAmount
);
@@ -73,12 +104,24 @@ export function useSignPSBT(useBitcoin?: UseBitcoinReturnType): UseSignPSBTRetur
}
}
- async function handleSignTransaction(btcAmount: number) {
- setBTCAmount(btcAmount);
+ async function handleSignTransaction(btcAmount: number, vaultUUID: string) {
+ const shiftedBTCDepositAmount = customShiftValue(btcAmount, 8, false);
+ setBTCAmount(shiftedBTCDepositAmount);
+ setVaultUUID(vaultUUID);
if (!fundingTransactionSigned) {
- await handleSignFundingTransaction(btcAmount);
+ await handleSignFundingTransaction(shiftedBTCDepositAmount);
} else {
await handleSignClosingTransaction();
+ if (fundingTransactionID && network && vaultUUID) {
+ dispatch(
+ vaultActions.setVaultToFunding({
+ vaultUUID,
+ fundingTX: fundingTransactionID,
+ networkID: network.id,
+ })
+ );
+ dispatch(mintUnmintActions.setMintStep([2, vaultUUID]));
+ }
}
}
diff --git a/src/shared/models/bitcoin-network.ts b/src/shared/models/bitcoin-network.ts
index 8fbb54ed..9aa42b6e 100644
--- a/src/shared/models/bitcoin-network.ts
+++ b/src/shared/models/bitcoin-network.ts
@@ -14,19 +14,20 @@ export interface BitcoinNetwork {
versionBytes: number;
}
-export const bitcoin: BitcoinNetwork = {
- messagePrefix: '\x18Bitcoin Signed Message:\n',
- bech32: 'bc',
- bip32: {
- public: 0x0488b21e,
- private: 0x0488ade4,
- },
- pubKeyHash: 0x00,
- scriptHash: 0x05,
- wif: 0x80,
- bytes: 21,
- versionBytes: 1,
-};
+// export const bitcoin: BitcoinNetwork = {
+// messagePrefix: '\x18Bitcoin Signed Message:\n',
+// bech32: 'bc',
+// bip32: {
+// public: 0x0488b21e,
+// private: 0x0488ade4,
+// },
+// pubKeyHash: 0x00,
+// scriptHash: 0x05,
+// wif: 0x80,
+// bytes: 21,
+// versionBytes: 1,
+// };
+
export const regtest: BitcoinNetwork = {
messagePrefix: '\x18Bitcoin Signed Message:\n',
bech32: 'bcrt',
@@ -40,16 +41,16 @@ export const regtest: BitcoinNetwork = {
bytes: 21,
versionBytes: 1,
};
-export const testnet: BitcoinNetwork = {
- messagePrefix: '\x18Bitcoin Signed Message:\n',
- bech32: 'tb',
- bip32: {
- public: 0x043587cf,
- private: 0x04358394,
- },
- pubKeyHash: 0x6f,
- scriptHash: 0xc4,
- wif: 0xef,
- bytes: 21,
- versionBytes: 1,
-};
+// export const testnet: BitcoinNetwork = {
+// messagePrefix: '\x18Bitcoin Signed Message:\n',
+// bech32: 'tb',
+// bip32: {
+// public: 0x043587cf,
+// private: 0x04358394,
+// },
+// pubKeyHash: 0x6f,
+// scriptHash: 0xc4,
+// wif: 0xef,
+// bytes: 21,
+// versionBytes: 1,
+// };