on a98284f help sign in
bridge/src/views/Bitcoin/SignTransaction.js
132 lines 3.9 kB history raw
1import React, { useState } from 'react';
2import { Grid, Text } from 'indigo-react';
4import { useLocalRouter } from 'lib/LocalRouter';
5import { COMMANDS, useFlowCommand } from 'lib/flowCommand';
7import View from 'components/View';
8import Highlighted from 'components/Highlighted';
9import CopiableAddressWrap from 'components/CopiableAddressWrap';
10import ViewHeader from 'components/ViewHeader';
11import SubmitButton from 'form/SubmitButton';
13import { useWallet } from 'store/wallet';
15import * as need from 'lib/need';
16import * as bitcoin from 'bitcoinjs-lib';
18import BridgeForm from 'form/BridgeForm';
19import { PsbtInput } from 'form/Inputs';
21import { Buffer } from 'buffer';
22import { composeValidator, buildPsbtValidator } from 'form/validators';
24export default function SignTransaction() {
25 const { pop } = useLocalRouter();
26 const { urbitWallet } = useWallet();
27 const flow = useFlowCommand();
29 const [signedTx, setSignedTx] = useState('');
30 const [psbt, setPsbt] = useState({});
32 const { private: privKey, chain } = need.urbitWallet(
33 urbitWallet
34 ).bitcoin.keys;
36 const btcWallet = bitcoin.bip32.fromPrivateKey(
37 Buffer.from(privKey, 'hex'),
38 Buffer.from(chain, 'hex'),
39 bitcoin.networks.bitcoin
40 );
42 const validate = composeValidator({
43 unsignedTransaction: buildPsbtValidator(btcWallet),
44 });
46 const onSubmit = values => {
47 const newPsbt = bitcoin.Psbt.fromBase64(values.unsignedTransaction);
48 const hex = newPsbt.data.inputs
49 .reduce((psbt, input, idx) => {
50 // removing already derived part, eg m/84'/0'/0'/0/0 becomes 0/0
51 const path = input.bip32Derivation[0].path
52 .split("m/84'/0'/0'/")
53 .join('');
54 const prv = btcWallet.derivePath(path).privateKey;
55 try {
56 return psbt.signInput(idx, bitcoin.ECPair.fromPrivateKey(prv));
57 } catch (e) {
58 return psbt;
59 }
60 }, newPsbt)
61 .finalizeAllInputs()
62 .extractTransaction()
63 .toHex();
65 setPsbt(newPsbt);
66 setSignedTx(hex);
67 };
69 const initialValues = {};
70 if (flow && COMMANDS.BITCOIN === flow.kind) {
71 initialValues.unsignedTransaction = flow.utx;
72 }
74 return (
75 <View pop={pop} inset>
76 <Grid className="mt4">
77 <Grid.Item full className="mt4" as={Text}>
78 Paste unsigned transactions from Landscape here
79 </Grid.Item>
80 <BridgeForm
81 validate={validate}
82 initialValues={initialValues}
83 onSubmit={onSubmit}>
84 {({ handleSubmit }) => (
85 <>
86 <Grid.Item
87 full
88 as={PsbtInput}
89 name="unsignedTransaction"
90 label="Unsigned transaction"
91 className="mt4"
92 />
93 <Grid.Item
94 full
95 solid
96 className="mt2"
97 accessory=""
98 handleSubmit={handleSubmit}
99 as={SubmitButton}>
100 Sign Transaction
101 </Grid.Item>
102 </>
103 )}
104 </BridgeForm>
105 {signedTx && (
106 <Grid.Item full className="mt4" as={Text}>
107 Transaction outputs:
108 </Grid.Item>
109 )}
110 {signedTx &&
111 psbt.txOutputs.map(e => (
112 <Grid.Item full className="mt4" key={e.address}>
113 <span>{e.address}</span>
114 <div className="mt1 mono black f6">{e.value} sats</div>
115 </Grid.Item>
116 ))}
117 {signedTx && (
118 <>
119 <Grid.Item full className="mt4" as={ViewHeader}>
120 Signed transaction:
121 </Grid.Item>
122 <Grid.Item full as={Text}>
123 <Highlighted>
124 <CopiableAddressWrap>{signedTx}</CopiableAddressWrap>
125 </Highlighted>
126 </Grid.Item>
127 </>
128 )}
129 </Grid>
130 </View>
131 );