on c6f53ef help sign in
bridge/src/views/Bitcoin/SignTransaction.js
160 lines 4.6 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/copiable/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 { composeValidator, buildPsbtValidator } from 'form/validators';
23const BITCOIN_MAINNET_INFO = {
24 messagePrefix: '\x18Bitcoin Signed Message:\n',
25 bech32: 'bc',
26 bip32: {
27 public: 0x04b24746,
28 private: 0x04b2430c,
29 },
30 pubKeyHash: 0x00,
31 scriptHash: 0x05,
32 wif: 0x80,
33};
35const BITCOIN_TESTNET_INFO = {
36 messagePrefix: '\x18Bitcoin Signed Message:\n',
37 bech32: 'tb',
38 bip32: {
39 public: 0x045f1cf6,
40 private: 0x045f18bc,
41 },
42 pubKeyHash: 0x6f,
43 scriptHash: 0xc4,
44 wif: 0xef,
45};
47export default function SignTransaction() {
48 const { pop } = useLocalRouter();
49 const { urbitWallet } = useWallet();
50 const flow = useFlowCommand();
52 const [signedTx, setSignedTx] = useState('');
53 const [psbt, setPsbt] = useState({});
55 const { xprv: zprv } = need.urbitWallet(urbitWallet).bitcoinMainnet.keys;
56 const { xprv: vprv } = need.urbitWallet(urbitWallet).bitcoinTestnet.keys;
58 const validate = composeValidator({
59 unsignedTransaction: buildPsbtValidator(),
60 });
62 const onSubmit = values => {
63 const newPsbt = bitcoin.Psbt.fromBase64(values.unsignedTransaction);
65 const isTestnet = newPsbt.data.inputs[0].bip32Derivation[0].path.startsWith(
66 "m/84'/1'/0'/"
67 );
69 const derivationPrefix = isTestnet ? "m/84'/1'/0'/" : "m/84'/0'/0'/";
71 const btcWallet = isTestnet
72 ? bitcoin.bip32.fromBase58(vprv, BITCOIN_TESTNET_INFO)
73 : bitcoin.bip32.fromBase58(zprv, BITCOIN_MAINNET_INFO);
75 try {
76 const hex = newPsbt.data.inputs
77 .reduce((psbt, input, idx) => {
78 // removing already derived part, eg m/84'/0'/0'/0/0 becomes 0/0
79 const path = input.bip32Derivation[0].path
80 .split(derivationPrefix)
81 .join('');
82 const prv = btcWallet.derivePath(path).privateKey;
83 return psbt.signInput(idx, bitcoin.ECPair.fromPrivateKey(prv));
84 }, newPsbt)
85 .finalizeAllInputs()
86 .extractTransaction()
87 .toHex();
88 setPsbt(newPsbt);
89 setSignedTx(hex);
90 } catch (error) {
91 return {
92 unsignedTransaction: 'Cannot sign transaction, mismatching keys.',
93 };
94 }
95 };
97 const initialValues = {};
98 if (flow && COMMANDS.BITCOIN === flow.kind) {
99 initialValues.unsignedTransaction = flow.utx;
100 }
102 return (
103 <View pop={pop} inset>
104 <Grid className="mt4">
105 <Grid.Item full className="mt4" as={Text}>
106 Paste unsigned transactions from Landscape here
107 </Grid.Item>
108 <BridgeForm
109 validate={validate}
110 initialValues={initialValues}
111 onSubmit={onSubmit}>
112 {({ handleSubmit }) => (
113 <>
114 <Grid.Item
115 full
116 as={PsbtInput}
117 name="unsignedTransaction"
118 label="Unsigned transaction"
119 className="mt4"
120 />
121 <Grid.Item
122 full
123 solid
124 className="mt2"
125 accessory=""
126 handleSubmit={handleSubmit}
127 as={SubmitButton}>
128 Sign Transaction
129 </Grid.Item>
130 </>
131 )}
132 </BridgeForm>
133 {signedTx && (
134 <Grid.Item full className="mt4" as={Text}>
135 Transaction outputs:
136 </Grid.Item>
137 )}
138 {signedTx &&
139 psbt.txOutputs.map(e => (
140 <Grid.Item full className="mt4" key={e.address}>
141 <span>{e.address}</span>
142 <div className="mt1 mono black f6">{e.value} sats</div>
143 </Grid.Item>
144 ))}
145 {signedTx && (
146 <>
147 <Grid.Item full className="mt4" as={ViewHeader}>
148 Signed transaction:
149 </Grid.Item>
150 <Grid.Item full as={Text}>
151 <Highlighted>
152 <CopiableAddressWrap>{signedTx}</CopiableAddressWrap>
153 </Highlighted>
154 </Grid.Item>
155 </>
156 )}
157 </Grid>
158 </View>
159 );