diff --git a/assets/rpc.proto b/assets/rpc.proto index 663961d08..b41557975 100644 --- a/assets/rpc.proto +++ b/assets/rpc.proto @@ -191,6 +191,16 @@ service Lightning { }; } + /** lncli: `estimatefee` + EstimateFee asks the chain backend to estimate the fee rate and total fees + for a transaction that pays to multiple specified outputs. + */ + rpc EstimateFee (EstimateFeeRequest) returns (EstimateFeeResponse) { + option (google.api.http) = { + get: "/v1/transactions/fee" + }; + } + /** lncli: `sendcoins` SendCoins executes a request to send coins to a particular address. Unlike SendMany, this RPC call only allows creating a single output at a time. If @@ -654,6 +664,27 @@ message LightningAddress { string host = 2 [json_name = "host"]; } +message FeeEstimate { + /// The total fee in satoshis. + int64 fee_sat = 1; + + /// The fee rate in satoshi/byte. + int64 feerate_sat_per_byte = 2; +} + +message EstimateFeeRequest { + /// The map from addresses to amounts for the transaction. + map AddrToAmount = 1; + + /// The target number of blocks that this transaction should be confirmed by. + int32 target_conf = 2; +} + +message EstimateFeeResponse { + /// A fee estimate for the specified confirmation target. + FeeEstimate estimate = 1; +} + message SendManyRequest { /// The map from addresses to amounts map AddrToAmount = 1; diff --git a/src/action/payment.js b/src/action/payment.js index 7a5766424..62f8d01fd 100644 --- a/src/action/payment.js +++ b/src/action/payment.js @@ -53,6 +53,20 @@ class PaymentAction { } } + async estimateFee() { + try { + const { payment, settings } = this._store; + const AddrToAmount = {}; + AddrToAmount[payment.address] = toSatoshis(payment.amount, settings.unit); + const { estimate } = await this._grpc.sendCommand('estimateFee', { + AddrToAmount, + }); + payment.fee = toAmount(parseSat(estimate.fee_sat), settings.unit); + } catch (err) { + this._notification.display({ msg: 'Estimating fee failed!', err }); + } + } + async payBitcoin() { try { const { payment, settings } = this._store; diff --git a/test/integration/action/action-integration.spec.js b/test/integration/action/action-integration.spec.js index 96994fb49..6fa103952 100644 --- a/test/integration/action/action-integration.spec.js +++ b/test/integration/action/action-integration.spec.js @@ -245,9 +245,14 @@ describe('Action Integration Tests', function() { transactions2.subscribeTransactions(); }); - it('should send some on-chain funds to node2', async () => { + it('should estimate on-chain fees', async () => { store1.payment.address = store2.walletAddress; store1.payment.amount = '10'; + await payments1.estimateFee(); + expect(store1.payment.fee, 'to be ok'); + }); + + it('should send some on-chain funds to node2', async () => { await payments1.payBitcoin(); }); diff --git a/test/unit/action/payment.spec.js b/test/unit/action/payment.spec.js index 6f28cf15f..428b3fd74 100644 --- a/test/unit/action/payment.spec.js +++ b/test/unit/action/payment.spec.js @@ -39,6 +39,7 @@ describe('Action Payments Unit Tests', () => { payment.init(); expect(store.payment.address, 'to equal', ''); expect(store.payment.amount, 'to equal', ''); + expect(store.payment.fee, 'to equal', ''); expect(store.payment.note, 'to equal', ''); expect(nav.goPay, 'was called once'); }); @@ -80,6 +81,31 @@ describe('Action Payments Unit Tests', () => { }); }); + describe('estimateFee()', () => { + it('should get fee estimate to display', async () => { + store.payment.amount = '0.00001'; + store.payment.address = 'some-address'; + grpc.sendCommand.withArgs('estimateFee').resolves({ + estimate: { + fee_sat: '8250', + feerate_sat_per_byte: '50', + }, + }); + await payment.estimateFee(); + expect(grpc.sendCommand, 'was called with', 'estimateFee', { + AddrToAmount: { 'some-address': 1000 }, + }); + expect(store.payment.fee, 'to equal', '0.0000825'); + }); + + it('should display notification on error', async () => { + grpc.sendCommand.withArgs('estimateFee').rejects(); + await payment.estimateFee(); + expect(notification.display, 'was called once'); + expect(store.payment.fee, 'to equal', ''); + }); + }); + describe('payBitcoin()', () => { it('should send on-chain transaction', async () => { store.payment.amount = '0.00001';