@@ -0,0 +1,203 @@
1+import React, { useMemo, useState } from 'react';
2+import { Grid, Text } from 'indigo-react';
3+import { Box, Button, Row, Icon } from '@tlon/indigo-react';
5+import { useWallet } from 'store/wallet';
7+import * as bitcoin from 'bitcoinjs-lib';
8+import { useLocalRouter } from 'lib/LocalRouter';
9+import { COMMANDS, useFlowCommand } from 'lib/flowCommand';
10+import * as need from 'lib/need';
11+import useCopiable from 'lib/useCopiable';
13+import View from 'components/View';
14+import L2BackHeader from 'components/L2/Headers/L2BackHeader';
15+import Window from 'components/L2/Window/Window';
16+import HeaderPane from 'components/L2/Window/HeaderPane';
17+import BodyPane from 'components/L2/Window/BodyPane';
18+import Highlighted from 'components/Highlighted';
19+import CopiableAddressWrap from 'components/copiable/CopiableAddressWrap';
20+import ViewHeader from 'components/ViewHeader';
21+import SubmitButton from 'form/SubmitButton';
22+import BridgeForm from 'form/BridgeForm';
23+import { PsbtInput } from 'form/Inputs';
24+import { composeValidator, buildPsbtValidator } from 'form/validators';
26+import './Bitcoin.scss';
28+const BITCOIN_MAINNET_INFO = {
29+ messagePrefix: '\x18Bitcoin Signed Message:\n',
40+const BITCOIN_TESTNET_INFO = {
41+ messagePrefix: '\x18Bitcoin Signed Message:\n',
52+export default function Bitcoin() {
53+ const { pop }: any = useLocalRouter();
54+ const { urbitWallet }: any = useWallet();
55+ const flow = useFlowCommand();
57+ const [signedTx, setSignedTx] = useState('');
58+ const [psbt, setPsbt] = useState({});
60+ // set to false for BTC testnet
61+ const isMainnet = true;
62+ const { xpub: zpub } = need.urbitWallet(urbitWallet).bitcoinMainnet.keys;
63+ const { xpub: vpub } = need.urbitWallet(urbitWallet).bitcoinTestnet.keys;
65+ const { xprv: zprv } = need.urbitWallet(urbitWallet).bitcoinMainnet.keys;
66+ const { xprv: vprv } = need.urbitWallet(urbitWallet).bitcoinTestnet.keys;
68+ const validate = composeValidator({
69+ unsignedTransaction: buildPsbtValidator(),
72+ const xpub = useMemo(() => (isMainnet ? zpub : vpub), [
78+ const [doCopy, didCopy] = useCopiable(xpub);
80+ const onSubmit = values => {
81+ const newPsbt = bitcoin.Psbt.fromBase64(values.unsignedTransaction);
83+ const isTestnet = newPsbt.data.inputs[0].bip32Derivation[0].path.startsWith(
87+ const derivationPrefix = isTestnet ? "m/84'/1'/0'/" : "m/84'/0'/0'/";
89+ const btcWallet = isTestnet
90+ ? bitcoin.bip32.fromBase58(vprv, BITCOIN_TESTNET_INFO)
91+ : bitcoin.bip32.fromBase58(zprv, BITCOIN_MAINNET_INFO);
94+ const hex = newPsbt.data.inputs
95+ .reduce((psbt, input, idx) => {
96+ // removing already derived part, eg m/84'/0'/0'/0/0 becomes 0/0
97+ const path = input.bip32Derivation[0].path
98+ .split(derivationPrefix)
100+ const prv = btcWallet.derivePath(path).privateKey;
101+ return psbt.signInput(idx, bitcoin.ECPair.fromPrivateKey(prv));
103+ .finalizeAllInputs()
104+ .extractTransaction()
110+ unsignedTransaction: 'Cannot sign transaction, mismatching keys.',
115+ const initialValues = {};
116+ if (flow && COMMANDS.BITCOIN === flow.kind) {
117+ initialValues.unsignedTransaction = flow.utx;
125+ header={<L2BackHeader />}
127+ <Window className="os-home">
129+ <Row className="header-row">
131+ <Button onClick={doCopy} className="copy-button">
136+ <Icon icon="Copy" color="#fff" />
144+ <Grid className="w-full h-full">
147+ initialValues={initialValues}
148+ onSubmit={onSubmit}>
149+ {({ handleSubmit }) => (
150+ <Grid.Item full className="h-full flex-col justify-between">
151+ <Box className="full">
152+ <Box className="label full">Unsigned Transaction</Box>
157+ name="unsignedTransaction"
167+ handleSubmit={handleSubmit}
169+ Sign Bitcoin Transaction
175+ <Grid.Item full className="mt4" as={Text}>
176+ Transaction outputs:
180+ psbt.txOutputs.map(e => (
181+ <Grid.Item full className="mt4" key={e.address}>
182+ <span>{e.address}</span>
183+ <div className="mt1 mono black f6">{e.value} sats</div>
188+ <Grid.Item full className="mt4" as={ViewHeader}>
191+ <Grid.Item full as={Text}>
193+ <CopiableAddressWrap>{signedTx}</CopiableAddressWrap>