diff --git a/src/networks.js b/src/networks.js index eedb66a..8e3ea4d 100644 --- a/src/networks.js +++ b/src/networks.js @@ -1,10 +1,6 @@ // https://en.bitcoin.it/wiki/List_of_address_prefixes // Dogecoin BIP32 is a proposed standard: https://bitcointalk.org/index.php?topic=409731 -function bitcoinEstimateFee(txByteSize) { - return networks.bitcoin.feePerKb * Math.ceil(txByteSize / 1000) -} - var networks = { bitcoin: { magicPrefix: '\x18Bitcoin Signed Message:\n', @@ -15,9 +11,9 @@ var networks = { pubKeyHash: 0x00, scriptHash: 0x05, wif: 0x80, - dustThreshold: 546, // https://github.com/bitcoin/bitcoin/blob/529047fcd18acd1b64dc95d6eb69edeaad75d405/src/core.h#L176-L188 - feePerKb: 10000, // https://github.com/bitcoin/bitcoin/blob/3f39b9d4551d729c3a2e4decd810ac6887cfaeb3/src/main.cpp#L52 - estimateFee: bitcoinEstimateFee + dustThreshold: 546, + feePerKb: 10000, + estimateFee: estimateFee('bitcoin') }, dogecoin: { magicPrefix: '\x19Dogecoin Signed Message:\n', @@ -29,7 +25,9 @@ var networks = { scriptHash: 0x16, wif: 0x9e, dustThreshold: 0, - feePerKb: 100000000 + dustSoftThreshold: 100000000, + feePerKb: 100000000, + estimateFee: estimateFee('dogecoin') }, litecoin: { magicPrefix: '\x19Litecoin Signed Message:\n', @@ -41,7 +39,9 @@ var networks = { scriptHash: 0x05, wif: 0xb0, dustThreshold: 0, - feePerKb: 100000 + dustSoftThreshold: 100000, + feePerKb: 100000, + estimateFee: estimateFee('litecoin') }, testnet: { magicPrefix: '\x18Bitcoin Signed Message:\n', @@ -54,7 +54,27 @@ var networks = { wif: 0xef, dustThreshold: 546, feePerKb: 10000, - estimateFee: bitcoinEstimateFee + estimateFee: estimateFee('bitcoin') } } + +function estimateFee(type) { + return function(tx) { + var network = networks[type] + var baseFee = network.feePerKb + var byteSize = tx.toBuffer().length + + var fee = baseFee * Math.ceil(byteSize / 1000) + if(network.dustSoftThreshold == undefined) return fee + + tx.outs.forEach(function(e){ + if(e.value < network.dustSoftThreshold) { + fee += baseFee + } + }) + + return fee + } +} + module.exports = networks diff --git a/src/wallet.js b/src/wallet.js index b15bcfd..a77cd02 100644 --- a/src/wallet.js +++ b/src/wallet.js @@ -236,8 +236,7 @@ function Wallet(seed, network) { var tmpTx = tx.clone() tmpTx.addOutput(getChangeAddress(), 0) - var byteSize = tmpTx.toBuffer().length - return network.estimateFee(byteSize) + return network.estimateFee(tmpTx) } function getChangeAddress() { diff --git a/test/network.js b/test/network.js index ee0d6d4..9e178b2 100644 --- a/test/network.js +++ b/test/network.js @@ -6,18 +6,84 @@ var fixtureTxes = require('./fixtures/mainnet_tx') var fixtureTx1Hex = fixtureTxes.prevTx var fixtureTxBigHex = fixtureTxes.bigTx -describe('bitcoin', function() { - describe('estimateFee', function() { - var estimateFee = networks.bitcoin.estimateFee +describe('networks', function() { + describe('bitcoin', function() { + describe('estimateFee', function() { + var estimateFee = networks.bitcoin.estimateFee - it('works for fixture tx 1', function() { - var tx = Transaction.fromHex(fixtureTx1Hex) - assert.equal(estimateFee(tx.toBuffer().length), 10000) + it('works for fixture tx 1', function() { + var tx = Transaction.fromHex(fixtureTx1Hex) + assert.equal(estimateFee(tx), 10000) + }) + + it('works for fixture big tx', function() { + var tx = Transaction.fromHex(fixtureTxBigHex) + assert.equal(estimateFee(tx), 30000) + }) }) + }) + + describe('dogecoin', function() { + describe('estimateFee', function() { + var estimateFee = networks.dogecoin.estimateFee + + it('regular fee per kb applies when every output has value no less than DUST_SOFT_LIMIT', function() { + var tx = Transaction.fromHex(fixtureTx1Hex) + tx.outs.forEach(function(e){ + e.value = 100000000 + }) + + assert.equal(estimateFee(tx), 100000000) + }) + + it('applies additional fee on every output with value below DUST_SOFT_LIMIT', function() { + var tx = Transaction.fromHex(fixtureTx1Hex) + tx.outs.forEach(function(e){ + e.value = 99999999 + }) + + assert.equal(estimateFee(tx), 4 * 100000000) // 3 outs in total + }) + + it('works for fixture big tx', function() { + var tx = Transaction.fromHex(fixtureTxBigHex) + tx.outs.forEach(function(e){ + e.value = 100000000 + }) + assert.equal(estimateFee(tx), 300000000) + }) + }) + }) + + describe('litecoin', function() { + describe('estimateFee', function() { + var estimateFee = networks.litecoin.estimateFee + + it('regular fee per kb applies when every output has value no less than DUST_SOFT_LIMIT', function() { + var tx = Transaction.fromHex(fixtureTx1Hex) + tx.outs.forEach(function(e){ + e.value = 100000 + }) + + assert.equal(estimateFee(tx), 100000) + }) + + it('applies additional fee on every output with value below DUST_SOFT_LIMIT', function() { + var tx = Transaction.fromHex(fixtureTx1Hex) + tx.outs.forEach(function(e){ + e.value = 99999 + }) + + assert.equal(estimateFee(tx), 4 * 100000) // 3 outs in total + }) - it('works for fixture big tx', function() { - var tx = Transaction.fromHex(fixtureTxBigHex) - assert.equal(estimateFee(tx.toBuffer().length), 30000) + it('works for fixture big tx', function() { + var tx = Transaction.fromHex(fixtureTxBigHex) + tx.outs.forEach(function(e){ + e.value = 100000 + }) + assert.equal(estimateFee(tx), 300000) + }) }) }) })