Browse Source

ADD: send MAX (for BIP84 & BIP49)

keystore
Overtorment 5 years ago
parent
commit
d40ac66bf1
  1. 7
      BlueComponents.js
  2. 4
      class/abstract-wallet.js
  3. 4
      class/hd-segwit-bech32-wallet.js
  4. 22
      class/hd-segwit-p2sh-wallet.js
  5. 1
      models/bitcoinUnits.js
  6. 22
      screen/send/details.js
  7. 30
      tests/integration/HDWallet.test.js

7
BlueComponents.js

@ -1923,6 +1923,7 @@ export class BlueBitcoinAmount extends Component {
} else {
localCurrency = loc.formatBalanceWithoutSuffix(amount.toString(), BitcoinUnit.LOCAL_CURRENCY, false);
}
if (amount === BitcoinUnit.MAX) localCurrency = ''; // we dont want to display NaN
return (
<TouchableWithoutFeedback disabled={this.props.pointerEvents === 'none'} onPress={() => this.textInput.focus()}>
<View>
@ -1939,6 +1940,12 @@ export class BlueBitcoinAmount extends Component {
}
this.props.onChangeText(text);
}}
onBlur={() => {
if (this.props.onBlur) this.props.onBlur();
}}
onFocus={() => {
if (this.props.onFocus) this.props.onFocus();
}}
placeholder="0"
maxLength={10}
ref={textInput => (this.textInput = textInput)}

4
class/abstract-wallet.js

@ -68,6 +68,10 @@ export class AbstractWallet {
return true;
}
allowSendMax(): boolean {
return false;
}
allowRBF() {
return false;
}

4
class/hd-segwit-bech32-wallet.js

@ -36,6 +36,10 @@ export class HDSegwitBech32Wallet extends AbstractHDWallet {
return true;
}
allowSendMax(): boolean {
return true;
}
/**
* @inheritDoc
*/

22
class/hd-segwit-p2sh-wallet.js

@ -5,6 +5,7 @@ import bip39 from 'bip39';
import BigNumber from 'bignumber.js';
import b58 from 'bs58check';
import signer from '../models/signer';
import { BitcoinUnit } from '../models/bitcoinUnits';
const bitcoin = require('bitcoinjs-lib');
const bitcoin5 = require('bitcoinjs5');
const HDNode = require('bip32');
@ -49,6 +50,10 @@ export class HDSegwitP2SHWallet extends AbstractHDWallet {
return true;
}
allowSendMax(): boolean {
return true;
}
async generate() {
let that = this;
return new Promise(function(resolve) {
@ -255,12 +260,29 @@ export class HDSegwitP2SHWallet extends AbstractHDWallet {
}
}
/**
*
* @param utxos
* @param amount Either float (BTC) or string 'MAX' (BitcoinUnit.MAX) to send all
* @param fee
* @param address
* @returns {string}
*/
createTx(utxos, amount, fee, address) {
for (let utxo of utxos) {
utxo.wif = this._getWifForAddress(utxo.address);
}
let amountPlusFee = parseFloat(new BigNumber(amount).plus(fee).toString(10));
if (amount === BitcoinUnit.MAX) {
amountPlusFee = new BigNumber(0);
for (let utxo of utxos) {
amountPlusFee = amountPlusFee.plus(utxo.amount);
}
amountPlusFee = amountPlusFee.dividedBy(100000000).toString(10);
}
return signer.createHDSegwitTransaction(
utxos,
address,

1
models/bitcoinUnits.js

@ -2,6 +2,7 @@ export const BitcoinUnit = Object.freeze({
BTC: 'BTC',
SATS: 'sats',
LOCAL_CURRENCY: 'local_currency',
MAX: 'MAX',
});
export const Chain = Object.freeze({

22
screen/send/details.js

@ -22,6 +22,7 @@ import {
BlueAddressInput,
BlueDismissKeyboardInputAccessory,
BlueLoading,
BlueButtonLink,
} from '../../BlueComponents';
import Slider from '@react-native-community/slider';
import PropTypes from 'prop-types';
@ -72,6 +73,7 @@ export default class SendDetails extends Component {
}
this.state = {
isLoading: true,
showSendMax: false,
isFeeSelectionModalVisible: false,
fromAddress,
fromWallet,
@ -466,6 +468,9 @@ export default class SendDetails extends Component {
let targets = [];
targets.push({ address: this.state.address, value: satoshis });
if (this.state.amount === BitcoinUnit.MAX) {
targets = [{ address: this.state.address }];
}
let { tx, fee } = wallet.createTransaction(wallet.getUtxo(), targets, requestedSatPerByte, changeAddress);
@ -629,8 +634,25 @@ export default class SendDetails extends Component {
isLoading={this.state.isLoading}
amount={this.state.amount.toString()}
onChangeText={text => this.setState({ amount: text })}
onBlur={() => {
this.setState({ showSendMax: false });
}}
onFocus={() => {
this.setState({ showSendMax: true });
}}
inputAccessoryViewID={BlueDismissKeyboardInputAccessory.InputAccessoryViewID}
/>
{this.state.showSendMax && this.state.fromWallet.allowSendMax() && (
<View>
<BlueButtonLink
title={'send MAX'}
onPress={() => {
console.log('state was set');
this.setState({ amount: 'MAX' });
}}
/>
</View>
)}
<BlueAddressInput
onChangeText={text => {
if (!this.processBIP70Invoice(text)) {

30
tests/integration/HDWallet.test.js

@ -1,5 +1,6 @@
/* global it, jasmine, afterAll, beforeAll */
import { SegwitP2SHWallet, SegwitBech32Wallet, HDSegwitP2SHWallet, HDLegacyBreadwalletWallet, HDLegacyP2PKHWallet } from '../../class';
import {BitcoinUnit} from "../../models/bitcoinUnits";
global.crypto = require('crypto'); // shall be used by tests under nodejs CLI, but not in RN environment
let assert = require('assert');
let bitcoin = require('bitcoinjs-lib');
@ -177,6 +178,35 @@ it('HD (BIP49) can create TX', async () => {
chunksIn = bitcoin.script.decompile(tx.outs[0].script);
toAddress = bitcoin.address.fromOutputScript(chunksIn);
assert.strictEqual('3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK', toAddress);
// testing sendMAX
hd.utxo = [
{
txid: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
vout: 0,
amount: 26000,
address: '3GgPyzKfWXiaXMbJ9LeEVGetvEXdrX9Ecj',
wif: 'L3fg5Jb6tJDVMvoG2boP4u3CxjX1Er3e7Z4zDALQdGgVLLE8zVUr',
},
{
txid: 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',
vout: 0,
amount: 26000,
address: '3GgPyzKfWXiaXMbJ9LeEVGetvEXdrX9Ecj',
wif: 'L3fg5Jb6tJDVMvoG2boP4u3CxjX1Er3e7Z4zDALQdGgVLLE8zVUr',
},
{
txid: 'cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc',
vout: 0,
amount: 26000,
address: '3GgPyzKfWXiaXMbJ9LeEVGetvEXdrX9Ecj',
wif: 'L3fg5Jb6tJDVMvoG2boP4u3CxjX1Er3e7Z4zDALQdGgVLLE8zVUr',
},
];
txhex = hd.createTx(hd.utxo, BitcoinUnit.MAX, 0.00003, '3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK');
tx = bitcoin.Transaction.fromHex(txhex);
assert.strictEqual(tx.outs.length, 1);
assert.strictEqual(tx.outs[0].value, 75000);
});
it('Segwit HD (BIP49) can fetch UTXO', async function() {

Loading…
Cancel
Save