1import React, { useMemo, useState } from 'react';
2import { Grid, Text } from 'indigo-react';
3import { Box, Button, Row, Icon } from '@tlon/indigo-react';
5import { useWallet } from 'store/wallet';
7import * as bitcoin from 'bitcoinjs-lib';
8import { useLocalRouter } from 'lib/LocalRouter';
9import { COMMANDS, FlowType, useFlowCommand } from 'lib/flowCommand';
10import * as need from 'lib/need';
11import useCopiable from 'lib/useCopiable';
13import View from 'components/View';
14import L2BackHeader from 'components/L2/Headers/L2BackHeader';
15import Window from 'components/L2/Window/Window';
16import HeaderPane from 'components/L2/Window/HeaderPane';
17import BodyPane from 'components/L2/Window/BodyPane';
18import Highlighted from 'components/Highlighted';
19import CopiableAddressWrap from 'components/copiable/CopiableAddressWrap';
20import ViewHeader from 'components/ViewHeader';
21import SubmitButton from 'form/SubmitButton';
22import BridgeForm from 'form/BridgeForm';
23import { PsbtInput } from 'form/Inputs';
24import { composeValidator, buildPsbtValidator } from 'form/validators';
26import './Bitcoin.scss';
28const BITCOIN_MAINNET_INFO = {
29 messagePrefix: '\x18Bitcoin Signed Message:\n',
40const BITCOIN_TESTNET_INFO = {
41 messagePrefix: '\x18Bitcoin Signed Message:\n',
52export default function Bitcoin() {
53 const { pop }: any = useLocalRouter();
54 const { urbitWallet }: any = useWallet();
55 const flow: FlowType = useFlowCommand();
57 const [signedTx, setSignedTx] = useState('');
58 const [psbt, setPsbt] = useState<ReturnType<
59 typeof bitcoin.Psbt.fromBase64
62 // set to false for BTC testnet
63 const isMainnet = true;
64 const { xpub: zpub } = need.urbitWallet(urbitWallet).bitcoinMainnet.keys;
65 const { xpub: vpub } = need.urbitWallet(urbitWallet).bitcoinTestnet.keys;
67 const { xprv: zprv } = need.urbitWallet(urbitWallet).bitcoinMainnet.keys;
68 const { xprv: vprv } = need.urbitWallet(urbitWallet).bitcoinTestnet.keys;
70 const validate = composeValidator({
71 unsignedTransaction: buildPsbtValidator(),
74 const xpub = useMemo(() => (isMainnet ? zpub : vpub), [
80 const [doCopy, didCopy] = useCopiable(xpub);
82 const onSubmit = (values: { unsignedTransaction: string }) => {
83 const newPsbt = bitcoin.Psbt.fromBase64(values.unsignedTransaction);
84 const derivations = newPsbt.data.inputs[0]?.bip32Derivation;
85 const firstDerivation =
86 derivations && derivations.length > 0 ? derivations[0] : null;
87 const firstDerivationPath = firstDerivation ? firstDerivation.path : null;
89 firstDerivationPath && firstDerivationPath.startsWith("m/84'/1'/0'/");
90 const derivationPrefix = isTestnet ? "m/84'/1'/0'/" : "m/84'/0'/0'/";
92 const btcWallet = isTestnet
93 ? bitcoin.bip32.fromBase58(vprv, BITCOIN_TESTNET_INFO)
94 : bitcoin.bip32.fromBase58(zprv, BITCOIN_MAINNET_INFO);
97 if (!firstDerivationPath) {
98 throw new Error('No derivation path available');
100 const hex = newPsbt.data.inputs
101 .reduce((psbt, input, idx) => {
102 // removing already derived part, eg m/84'/0'/0'/0/0 becomes 0/0
103 const path = firstDerivationPath.split(derivationPrefix).join('');
104 const prv = btcWallet.derivePath(path).privateKey;
106 throw new Error('Private key unavailable');
108 return psbt.signInput(idx, bitcoin.ECPair.fromPrivateKey(prv));
111 .extractTransaction()
117 unsignedTransaction: 'Cannot sign transaction, mismatching keys.',
122 let initialValues = {
123 unsignedTransaction: '',
125 if (flow && COMMANDS.BITCOIN === flow.kind) {
126 initialValues.unsignedTransaction = flow.utx as string;
134 header={<L2BackHeader hideBalance />}
136 <Window className="os-home">
138 <Row className="header-row">
140 <Button onClick={doCopy} className="copy-button">
145 <Icon icon="Copy" color="#fff" />
153 <Grid className="w-full h-full">
156 initialValues={initialValues}
158 {({ handleSubmit }: any) => (
159 <Grid.Item full className="h-full flex-col justify-between">
160 <Box className="full">
161 <Box className="label full">Unsigned Transaction</Box>
166 name="unsignedTransaction"
176 handleSubmit={handleSubmit}
178 Sign Bitcoin Transaction
184 <Grid.Item full className="mt4" as={Text}>
190 psbt.txOutputs.map(e => (
191 <Grid.Item full className="mt4" key={e.address}>
192 <span>{e.address}</span>
193 <div className="mt1 mono black f6">{e.value} sats</div>
198 <Grid.Item full className="mt4" as={ViewHeader}>
201 <Grid.Item full as={Text}>
203 <CopiableAddressWrap>{signedTx}</CopiableAddressWrap>
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list