@@ -0,0 +1,132 @@
1+import React, { useState } from 'react';
2+import { Grid, Text } from 'indigo-react';
3+
4+import { useLocalRouter } from 'lib/LocalRouter';
5+import { COMMANDS, useFlowCommand } from 'lib/flowCommand';
6+
7+import View from 'components/View';
8+import Highlighted from 'components/Highlighted';
9+import CopiableAddressWrap from 'components/CopiableAddressWrap';
10+import ViewHeader from 'components/ViewHeader';
11+import SubmitButton from 'form/SubmitButton';
12+
13+import { useWallet } from 'store/wallet';
14+
15+import * as need from 'lib/need';
16+import * as bitcoin from 'bitcoinjs-lib';
17+
18+import BridgeForm from 'form/BridgeForm';
19+import { PsbtInput } from 'form/Inputs';
20+
21+import { Buffer } from 'buffer';
22+import { composeValidator, buildPsbtValidator } from 'form/validators';
23+
24+export default function SignTransaction() {
25+ const { pop } = useLocalRouter();
26+ const { urbitWallet } = useWallet();
27+ const flow = useFlowCommand();
28+
29+ const [signedTx, setSignedTx] = useState('');
30+ const [psbt, setPsbt] = useState({});
31+
32+ const { private: privKey, chain } = need.urbitWallet(
33+ urbitWallet
34+ ).bitcoin.keys;
35+
36+ const btcWallet = bitcoin.bip32.fromPrivateKey(
37+ Buffer.from(privKey, 'hex'),
38+ Buffer.from(chain, 'hex'),
39+ bitcoin.networks.bitcoin
40+ );
41+
42+ const validate = composeValidator({
43+ unsignedTransaction: buildPsbtValidator(btcWallet),
44+ });
45+
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();
64+
65+ setPsbt(newPsbt);
66+ setSignedTx(hex);
67+ };
68+
69+ const initialValues = {};
70+ if (flow && COMMANDS.BITCOIN === flow.kind) {
71+ initialValues.unsignedTransaction = flow.utx;
72+ }
73+
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+ );
132+}