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