on ef3340a help sign in
bridge/src/views/Bitcoin.js
155 lines 4.8 kB history raw
1import React, { useState } from 'react';
2import { Grid, Text } from 'indigo-react';
3import { useWallet } from 'store/wallet';
5import { useLocalRouter } from 'lib/LocalRouter';
7import ViewHeader from 'components/ViewHeader';
8import View from 'components/View';
9import WarningBox from 'components/WarningBox';
11import CopiableAddressWrap from 'components/CopiableAddressWrap';
12import Highlighted from 'components/Highlighted';
14import { PsbtInput } from 'form/Inputs';
15import BridgeForm from 'form/BridgeForm';
17import * as need from 'lib/need';
18import * as bitcoin from 'bitcoinjs-lib';
19import * as bs58check from 'bs58check';
20import { Buffer } from 'buffer';
21import { composeValidator, buildPsbtValidator } from 'form/validators';
23function xpubToZpub(xpub) {
24 var data = bs58check.decode(xpub);
25 data = data.slice(4);
26 data = Buffer.concat([Buffer.from('04b24746', 'hex'), data]);
27 return bs58check.encode(data);
30export default function Bitcoin() {
31 const { pop } = useLocalRouter();
32 const { urbitWallet } = useWallet();
35 const empty = new bitcoin.Psbt();
37 const [signedTx, setSignedTx] = useState('');
38 const [psbt, setPsbt] = useState(empty);
40 const privKey = need.urbitWallet(urbitWallet).bitcoin.keys.private;
41 const pubKey = need.urbitWallet(urbitWallet).bitcoin.keys.public;
42 const chain = need.urbitWallet(urbitWallet).bitcoin.keys.chain;
44 const btcWallet = bitcoin.bip32.fromPrivateKey(
45 Buffer.from(privKey, 'hex'),
46 Buffer.from(chain, 'hex'),
47 bitcoin.networks.bitcoin
48 );
50 const zPub = xpubToZpub(
51 bitcoin.bip32
52 .fromPublicKey(
53 Buffer.from(pubKey, 'hex'),
54 Buffer.from(chain, 'hex'),
55 bitcoin.networks.bitcoin
56 )
57 .toBase58()
58 );
60 const validate = composeValidator({
61 unsignedTransaction: buildPsbtValidator(btcWallet),
62 });
64 const onValues = ({ valid, values, form }) => {
65 if (valid) {
66 const newPsbt = bitcoin.Psbt.fromBase64(values.unsignedTransaction);
67 const hex = newPsbt.data.inputs
68 .reduce((psbt, input, idx) => {
69 // removing already derived part, eg m/84'/0'/0'/0/0 becomes 0/0
70 const path = input.bip32Derivation[0].path
71 .split("m/84'/0'/0'/")
72 .join('');
73 const prv = btcWallet.derivePath(path).privateKey;
74 try {
75 return psbt.signInput(idx, bitcoin.ECPair.fromPrivateKey(prv));
76 } catch (e) {
77 return psbt;
78 }
79 }, newPsbt)
80 .finalizeAllInputs()
81 .extractTransaction()
82 .toHex();
84 if (psbt.toHex() !== newPsbt.toHex()) {
85 setPsbt(newPsbt);
86 }
87 setSignedTx(hex);
88 } else {
89 setSignedTx('');
90 }
91 };
93 return (
94 <View pop={pop} inset>
95 <Grid className="mt4">
96 <Grid.Item full as={ViewHeader}>
97 Bitcoin
98 </Grid.Item>
99 <Grid.Item full as={WarningBox}>
100 We do not recommend using this feature for significant amounts of
101 money, since your private keys are exposed to the browser.
102 </Grid.Item>
103 <Grid.Item full className="mt4" as={Text}>
104 Below is the bitcoin extended public key derived from your master
105 ticket. You can paste it to Landscape to generate an unsigned
106 transaction.
107 </Grid.Item>
108 <Grid.Item full className="mt4" as={Text}>
109 <Highlighted>
110 <CopiableAddressWrap>{zPub}</CopiableAddressWrap>
111 </Highlighted>
112 </Grid.Item>
113 <Grid.Item full className="mt4" as={Text}>
114 Next, paste the unsigned transaction below to sign it with your
115 private key.
116 </Grid.Item>
117 <BridgeForm validate={validate} onValues={onValues}>
118 {() => (
119 <Grid.Item
120 full
121 as={PsbtInput}
122 name="unsignedTransaction"
123 label="Unsigned transaction"
124 className="mt4"
125 />
126 )}
127 </BridgeForm>
128 {signedTx && (
129 <Grid.Item full className="mt4" as={Text}>
130 Transaction outputs:
131 </Grid.Item>
132 )}
133 {signedTx &&
134 psbt.txOutputs.map(e => (
135 <Grid.Item full className="mt4" key={e.address}>
136 <span>{e.address}</span>
137 <div className="mt1 mono black f6">{e.value} Satoshis</div>
138 </Grid.Item>
139 ))}
140 {signedTx && (
141 <>
142 <Grid.Item full className="mt4" as={ViewHeader}>
143 Signed transaction:
144 </Grid.Item>
145 <Grid.Item full as={Text}>
146 <Highlighted>
147 <CopiableAddressWrap>{signedTx}</CopiableAddressWrap>
148 </Highlighted>
149 </Grid.Item>
150 </>
151 )}
152 </Grid>
153 </View>
154 );