diff --git a/README.md b/README.md
index 572a4ab4..b972c951 100644
--- a/README.md
+++ b/README.md
@@ -257,7 +257,7 @@ pool.open().then(function() {
wallet.on('balance', function(balance) {
console.log('Balance updated.');
- console.log(bcoin.utils.btc(balance.unconfirmed));
+ console.log(bcoin.amount.btc(balance.unconfirmed));
});
});
```
diff --git a/bin/cli b/bin/cli
index c49e936d..d339fd10 100755
--- a/bin/cli
+++ b/bin/cli
@@ -7,6 +7,7 @@ var utils = require('../lib/utils/utils');
var co = require('../lib/utils/co');
var Client = require('../lib/http/client');
var Wallet = require('../lib/http/wallet');
+var Amount = require('../lib/utils/amount');
var main;
function CLI() {
@@ -286,10 +287,10 @@ CLI.prototype.sendTX = co(function* sendTX() {
if (this.config.script) {
output.script = this.config.script;
- output.value = utils.satoshi(this.config.value || this.argv[0]);
+ output.value = Amount.value(this.config.value || this.argv[0]);
} else {
output.address = this.config.address || this.argv[0];
- output.value = utils.satoshi(this.config.value || this.argv[1]);
+ output.value = Amount.value(this.config.value || this.argv[1]);
}
options = {
@@ -309,10 +310,10 @@ CLI.prototype.createTX = co(function* createTX() {
if (this.config.script) {
output.script = this.config.script;
- output.value = utils.satoshi(this.config.value || this.argv[0]);
+ output.value = Amount.value(this.config.value || this.argv[0]);
} else {
output.address = this.config.address || this.argv[0];
- output.value = utils.satoshi(this.config.value || this.argv[1]);
+ output.value = Amount.value(this.config.value || this.argv[1]);
}
options = {
diff --git a/browser/index.js b/browser/index.js
index 9e1b6b0c..78c59901 100644
--- a/browser/index.js
+++ b/browser/index.js
@@ -82,7 +82,7 @@ send.onsubmit = function(ev) {
options = {
outputs: [{
address: address,
- value: utils.satoshi(value)
+ value: bcoin.amount.value(value)
}]
};
@@ -148,7 +148,7 @@ function addItem(tx) {
chainState.innerHTML = ''
+ 'tx=' + node.chain.db.state.tx
+ ' coin=' + node.chain.db.state.coin
- + ' value=' + utils.btc(node.chain.db.state.value);
+ + ' value=' + bcoin.amount.btc(node.chain.db.state.value);
}
function setMouseup(el, obj) {
@@ -182,11 +182,11 @@ function formatWallet(wallet) {
wallet.getBalance().then(function(balance) {
html += 'Confirmed Balance: '
- + utils.btc(balance.confirmed)
+ + bcoin.amount.btc(balance.confirmed)
+ '
';
html += 'Unconfirmed Balance: '
- + utils.btc(balance.unconfirmed)
+ + bcoin.amount.btc(balance.unconfirmed)
+ '
';
return wallet.getHistory();
diff --git a/lib/chain/chaindb.js b/lib/chain/chaindb.js
index 08886c95..f027005d 100644
--- a/lib/chain/chaindb.js
+++ b/lib/chain/chaindb.js
@@ -13,6 +13,7 @@ var utils = require('../utils/utils');
var assert = require('assert');
var BufferWriter = require('../utils/writer');
var BufferReader = require('../utils/reader');
+var Amount = require('../utils/amount');
var encoding = require('../utils/encoding');
var co = require('../utils/co');
var Network = require('../protocol/network');
@@ -145,7 +146,7 @@ ChainDB.prototype._open = co(function* open() {
this.state.rhash,
this.state.tx,
this.state.coin,
- utils.btc(this.state.value));
+ Amount.btc(this.state.value));
});
/**
diff --git a/lib/http/client.js b/lib/http/client.js
index fdc1a97b..17020196 100644
--- a/lib/http/client.js
+++ b/lib/http/client.js
@@ -10,6 +10,7 @@
var Network = require('../protocol/network');
var AsyncObject = require('../utils/async');
var RPCClient = require('./rpcclient');
+var Amount = require('../utils/amount');
var utils = require('../utils/utils');
var co = require('../utils/co');
var request = require('./request').promise;
@@ -620,11 +621,11 @@ HTTPClient.prototype.send = function send(id, options) {
options.outputs = options.outputs || [];
if (options.rate)
- options.rate = utils.btc(options.rate);
+ options.rate = Amount.btc(options.rate);
options.outputs = options.outputs.map(function(output) {
return {
- value: utils.btc(output.value),
+ value: Amount.btc(output.value),
address: output.address,
script: toHex(output.script)
};
@@ -668,11 +669,11 @@ HTTPClient.prototype.createTX = function createTX(id, options) {
options = utils.merge({}, options);
if (options.rate)
- options.rate = utils.btc(options.rate);
+ options.rate = Amount.btc(options.rate);
options.outputs = options.outputs.map(function(output) {
return {
- value: utils.btc(output.value),
+ value: Amount.btc(output.value),
address: output.address,
script: toHex(output.script)
};
diff --git a/lib/http/rpc.js b/lib/http/rpc.js
index bf4f10ff..2560daa4 100644
--- a/lib/http/rpc.js
+++ b/lib/http/rpc.js
@@ -13,6 +13,7 @@ var assert = require('assert');
var constants = require('../protocol/constants');
var ec = require('../crypto/ec');
var time = require('../net/time');
+var Amount = require('../utils/amount');
var NetworkAddress = require('../primitives/netaddress');
var Script = require('../script/script');
var Address = require('../primitives/address');
@@ -322,7 +323,7 @@ RPC.prototype.getinfo = co(function* getinfo(args) {
version: constants.USER_VERSION,
protocolversion: constants.VERSION,
walletversion: 0,
- balance: +utils.btc(balance.unconfirmed),
+ balance: Amount.btc(balance.unconfirmed, true),
blocks: this.chain.height,
timeoffset: time.offset,
connections: this.pool.peers.all.length,
@@ -332,8 +333,8 @@ RPC.prototype.getinfo = co(function* getinfo(args) {
keypoololdest: 0,
keypoolsize: 0,
unlocked_until: this.wallet.master.until,
- paytxfee: +utils.btc(this.network.feeRate),
- relayfee: +utils.btc(this.network.minRelay),
+ paytxfee: Amount.btc(this.network.feeRate, true),
+ relayfee: Amount.btc(this.network.minRelay, true),
errors: ''
};
});
@@ -379,7 +380,7 @@ RPC.prototype.getnetworkinfo = function getnetworkinfo(args) {
timeoffset: time.offset,
connections: this.pool.peers.all.length,
networks: [],
- relayfee: +utils.btc(this.network.getMinRelay()),
+ relayfee: Amount.btc(this.network.getMinRelay(), true),
localaddresses: [],
warnings: ''
});
@@ -794,7 +795,7 @@ RPC.prototype._txToJSON = function _txToJSON(tx) {
}),
vout: tx.outputs.map(function(output, i) {
return {
- value: +utils.btc(output.value),
+ value: Amount.btc(output.value, true),
n: i,
scriptPubKey: self._scriptToJSON(output.script, true)
};
@@ -984,7 +985,7 @@ RPC.prototype.getmempoolinfo = function getmempoolinfo(args) {
bytes: this.mempool.getSize(),
usage: this.mempool.getSize(),
maxmempool: constants.mempool.MAX_MEMPOOL_SIZE,
- mempoolminfee: +utils.btc(this.mempool.minRelay)
+ mempoolminfee: Amount.btc(this.mempool.minRelay, true)
});
};
@@ -1123,18 +1124,18 @@ RPC.prototype._entryToJSON = function _entryToJSON(entry) {
var tx = entry.tx;
return {
size: entry.size,
- fee: +utils.btc(entry.fee),
- modifiedfee: +utils.btc(entry.fees),
+ fee: Amount.btc(entry.fee, true),
+ modifiedfee: Amount.btc(entry.fees, true),
time: entry.ts,
height: entry.height,
startingpriority: entry.priority,
currentpriority: entry.getPriority(this.chain.height),
descendantcount: this.mempool.countDescendants(tx),
descendantsize: entry.sizes,
- descendantfees: +utils.btc(entry.fees),
+ descendantfees: Amount.btc(entry.fees, true),
ancestorcount: this.mempool.countAncestors(tx),
ancestorsize: entry.sizes,
- ancestorfees: +utils.btc(entry.fees),
+ ancestorfees: Amount.btc(entry.fees, true),
depends: this.mempool.getDepends(tx).map(utils.revHex)
};
};
@@ -1172,7 +1173,7 @@ RPC.prototype.gettxout = co(function* gettxout(args) {
return {
bestblock: this.chain.tip.rhash,
confirmations: coin.getConfirmations(this.chain.height),
- value: +utils.btc(coin.value),
+ value: Amount.btc(coin.value, true),
scriptPubKey: this._scriptToJSON(coin.script, true),
version: coin.version,
coinbase: coin.coinbase
@@ -1283,7 +1284,7 @@ RPC.prototype.gettxoutsetinfo = function gettxoutsetinfo(args) {
txouts: this.chain.db.state.coin,
bytes_serialized: 0,
hash_serialized: 0,
- total_amount: +utils.btc(this.chain.db.state.value)
+ total_amount: Amount.btc(this.chain.db.state.value, true)
});
};
@@ -2333,7 +2334,7 @@ RPC.prototype.fundrawtransaction = co(function* fundrawtransaction(args) {
return {
hex: tx.toRaw().toString('hex'),
changepos: tx.changeIndex,
- fee: +utils.btc(tx.getFee())
+ fee: Amount.btc(tx.getFee(), true)
};
});
@@ -2545,7 +2546,7 @@ RPC.prototype.estimatefee = function estimatefee(args) {
if (fee === 0)
fee = -1;
else
- fee = +utils.btc(fee);
+ fee = Amount.btc(fee, true);
return Promise.resolve(fee);
};
@@ -2588,7 +2589,7 @@ RPC.prototype.estimatesmartfee = function estimatesmartfee(args) {
if (fee === 0)
fee = -1;
else
- fee = +utils.btc(fee);
+ fee = Amount.btc(fee, true);
return Promise.resolve({
fee: fee,
@@ -2901,7 +2902,7 @@ RPC.prototype.getbalance = co(function* getbalance(args) {
else
value = balance.unconfirmed;
- return +utils.btc(value);
+ return Amount.btc(value, true);
});
RPC.prototype.getnewaddress = co(function* getnewaddress(args) {
@@ -2982,7 +2983,7 @@ RPC.prototype.getreceivedbyaccount = co(function* getreceivedbyaccount(args) {
}
}
- return +utils.btc(total);
+ return Amount.btc(total, true);
});
RPC.prototype.getreceivedbyaddress = co(function* getreceivedbyaddress(args) {
@@ -3019,7 +3020,7 @@ RPC.prototype.getreceivedbyaddress = co(function* getreceivedbyaddress(args) {
}
}
- return +utils.btc(total);
+ return Amount.btc(total, true);
});
RPC.prototype._toWalletTX = co(function* _toWalletTX(tx) {
@@ -3054,7 +3055,7 @@ RPC.prototype._toWalletTX = co(function* _toWalletTX(tx) {
account: member.path.name,
address: member.address.toBase58(this.network),
category: 'receive',
- amount: +utils.btc(member.value),
+ amount: Amount.btc(member.value, true),
label: member.path.name,
vout: i
});
@@ -3073,8 +3074,8 @@ RPC.prototype._toWalletTX = co(function* _toWalletTX(tx) {
? member.address.toBase58(this.network)
: null,
category: 'send',
- amount: -(+utils.btc(member.value)),
- fee: -(+utils.btc(details.fee)),
+ amount: -(Amount.btc(member.value, true)),
+ fee: -(Amount.btc(details.fee, true)),
vout: i
});
@@ -3082,7 +3083,7 @@ RPC.prototype._toWalletTX = co(function* _toWalletTX(tx) {
}
json = {
- amount: +utils.btc(receive ? received : -sent),
+ amount: Amount.btc(receive ? received : -sent, true),
confirmations: details.confirmations,
blockhash: details.block ? utils.revHex(details.block) : null,
blockindex: details.index,
@@ -3145,7 +3146,7 @@ RPC.prototype.getunconfirmedbalance = co(function* getunconfirmedbalance(args) {
balance = yield this.wallet.getBalance();
- return +utils.btc(balance.unconfirmed);
+ return Amount.btc(balance.unconfirmed, true);
});
RPC.prototype.getwalletinfo = co(function* getwalletinfo(args) {
@@ -3159,15 +3160,15 @@ RPC.prototype.getwalletinfo = co(function* getwalletinfo(args) {
return {
walletid: this.wallet.id,
walletversion: 6,
- balance: +utils.btc(balance.unconfirmed),
- unconfirmed_balance: +utils.btc(balance.unconfirmed),
+ balance: Amount.btc(balance.unconfirmed, true),
+ unconfirmed_balance: Amount.btc(balance.unconfirmed, true),
txcount: this.wallet.state.tx,
keypoololdest: 0,
keypoolsize: 0,
unlocked_until: this.wallet.master.until,
paytxfee: this.feeRate != null
- ? +utils.btc(this.feeRate)
- : +utils.btc(0)
+ ? Amount.btc(this.feeRate, true)
+ : 0
};
});
@@ -3312,7 +3313,7 @@ RPC.prototype.listaccounts = co(function* listaccounts(args) {
for (i = 0; i < accounts.length; i++) {
account = accounts[i];
balance = yield this.wallet.getBalance(account);
- map[account] = +utils.btc(balance.unconfirmed);
+ map[account] = Amount.btc(balance.unconfirmed, true);
}
return map;
@@ -3465,7 +3466,7 @@ RPC.prototype._listReceived = co(function* _listReceived(minconf, empty, account
continue;
if (entry.confirmations === -1)
entry.confirmations = 0;
- entry.amount = +utils.btc(entry.amount);
+ entry.amount = Amount.btc(entry.amount, true);
result.push(entry);
}
@@ -3587,7 +3588,7 @@ RPC.prototype._toListTX = co(function* _toListTX(tx) {
? member.address.toBase58(this.network)
: null,
category: receive ? 'receive' : 'send',
- amount: +utils.btc(receive ? received : -sent),
+ amount: Amount.btc(receive ? received : -sent, true),
label: member.path ? member.path.name : undefined,
vout: index,
confirmations: details.confirmations,
@@ -3714,7 +3715,7 @@ RPC.prototype.listunspent = co(function* listunspent(args) {
? ring.script.toJSON()
: undefined,
scriptPubKey: coin.script.toJSON(),
- amount: +utils.btc(coin.value),
+ amount: Amount.btc(coin.value, true),
confirmations: depth,
spendable: !this.wallet.isLocked(coin),
solvable: true
@@ -4179,7 +4180,7 @@ function isHash(obj) {
function toSatoshi(obj) {
if (typeof obj !== 'number')
throw new RPCError('Bad BTC amount.');
- return utils.satoshi(obj + '');
+ return Amount.value(obj, true);
}
function reverseEndian(data) {
diff --git a/lib/http/server.js b/lib/http/server.js
index 3de68b17..e9f213ba 100644
--- a/lib/http/server.js
+++ b/lib/http/server.js
@@ -17,6 +17,7 @@ var HTTPBase = require('./base');
var utils = require('../utils/utils');
var co = require('../utils/co');
var base58 = require('../utils/base58');
+var Amount = require('../utils/amount');
var Address = require('../primitives/address');
var Bloom = require('../utils/bloom');
var TX = require('../primitives/tx');
@@ -304,16 +305,16 @@ HTTPServer.prototype._init = function _init() {
}
if (params.fee)
- options.fee = utils.satoshi(params.fee);
+ options.fee = Amount.value(params.fee);
if (params.hardFee)
- options.hardFee = utils.satoshi(params.hardFee);
+ options.hardFee = Amount.value(params.hardFee);
if (params.maxFee)
- options.maxFee = utils.satoshi(params.maxFee);
+ options.maxFee = Amount.value(params.maxFee);
if (params.rate)
- options.rate = utils.satoshi(params.rate);
+ options.rate = Amount.value(params.rate);
if (params.m != null) {
options.m = Number(params.m);
@@ -382,7 +383,7 @@ HTTPServer.prototype._init = function _init() {
script: output.script
? Script.fromRaw(output.script, 'hex')
: null,
- value: utils.satoshi(output.value)
+ value: Amount.value(output.value)
});
}
}
@@ -732,11 +733,11 @@ HTTPServer.prototype._init = function _init() {
var fee;
if (!this.fees)
- return send(200, { rate: utils.btc(this.network.feeRate) });
+ return send(200, { rate: Amount.btc(this.network.feeRate) });
fee = this.fees.estimateFee(req.options.blocks);
- send(200, { rate: utils.btc(fee) });
+ send(200, { rate: Amount.btc(fee) });
});
// Reset chain
@@ -1358,12 +1359,12 @@ HTTPServer.prototype._initIO = function _initIO() {
if (!self.fees) {
rate = self.network.feeRate;
- rate = utils.btc(rate);
+ rate = Amount.btc(rate);
return callback(null, rate);
}
rate = self.fees.estimateFee(blocks);
- rate = utils.btc(rate);
+ rate = Amount.btc(rate);
return callback(null, rate);
});
diff --git a/lib/primitives/coin.js b/lib/primitives/coin.js
index 9378fdae..2f7cad3f 100644
--- a/lib/primitives/coin.js
+++ b/lib/primitives/coin.js
@@ -7,10 +7,11 @@
'use strict';
+var assert = require('assert');
var utils = require('../utils/utils');
var constants = require('../protocol/constants');
var Network = require('../protocol/network');
-var assert = require('assert');
+var Amount = require('../utils/amount');
var Output = require('./output');
var Script = require('../script/script');
var Network = require('../protocol/network');
@@ -122,7 +123,7 @@ Coin.prototype.inspect = function inspect() {
type: this.getType(),
version: this.version,
height: this.height,
- value: utils.btc(this.value),
+ value: Amount.btc(this.value),
script: this.script,
coinbase: this.coinbase,
hash: this.hash ? utils.revHex(this.hash) : null,
@@ -150,7 +151,7 @@ Coin.prototype.toJSON = function toJSON(network) {
return {
version: this.version,
height: this.height,
- value: utils.btc(this.value),
+ value: Amount.btc(this.value),
script: this.script.toJSON(),
address: address,
coinbase: this.coinbase,
@@ -186,7 +187,7 @@ Coin.prototype.fromJSON = function fromJSON(json) {
this.version = json.version;
this.height = json.height;
- this.value = utils.satoshi(json.value);
+ this.value = Amount.value(json.value);
this.script.fromJSON(json.script);
this.coinbase = json.coinbase;
this.hash = json.hash ? utils.revHex(json.hash) : null;
diff --git a/lib/primitives/mtx.js b/lib/primitives/mtx.js
index 4e72d261..00ff5f77 100644
--- a/lib/primitives/mtx.js
+++ b/lib/primitives/mtx.js
@@ -175,8 +175,8 @@ MTX.prototype.addInput = function addInput(options, index) {
* Add an output.
* @example
* tx.addOutput({ address: ..., value: 100000 });
- * tx.addOutput({ address: ..., value: utils.satoshi('0.1') });
- * tx.addOutput(receivingWallet, utils.satoshi('0.1'));
+ * tx.addOutput({ address: ..., value: Amount.value('0.1') });
+ * tx.addOutput(receivingWallet, Amount.value('0.1'));
* @param {Wallet|KeyRing|Object} obj - Wallet, Address,
* or options (see {@link Script.createOutputScript} for options).
* @param {Amount?} value - Only needs to be present for non-options.
diff --git a/lib/primitives/output.js b/lib/primitives/output.js
index 7856043c..915a2f96 100644
--- a/lib/primitives/output.js
+++ b/lib/primitives/output.js
@@ -9,6 +9,7 @@
var utils = require('../utils/utils');
var constants = require('../protocol/constants');
+var Amount = require('../utils/amount');
var Network = require('../protocol/network');
var Script = require('../script/script');
var BufferWriter = require('../utils/writer');
@@ -117,7 +118,7 @@ Output.prototype.getHash = function getHash(enc) {
Output.prototype.inspect = function inspect() {
return {
type: this.getType(),
- value: utils.btc(this.value),
+ value: Amount.btc(this.value),
script: this.script,
address: this.getAddress()
};
@@ -138,7 +139,7 @@ Output.prototype.toJSON = function toJSON(network) {
address = address.toBase58(network);
return {
- value: utils.btc(this.value),
+ value: Amount.btc(this.value),
script: this.script.toJSON(),
address: address
};
@@ -205,7 +206,7 @@ Output.prototype.isDust = function isDust(rate) {
Output.prototype.fromJSON = function fromJSON(json) {
assert(typeof json.value === 'string');
- this.value = utils.satoshi(json.value);
+ this.value = Amount.value(json.value);
this.script.fromJSON(json.script);
return this;
};
diff --git a/lib/primitives/tx.js b/lib/primitives/tx.js
index 0d4154ee..343a773a 100644
--- a/lib/primitives/tx.js
+++ b/lib/primitives/tx.js
@@ -11,6 +11,7 @@ var assert = require('assert');
var utils = require('../utils/utils');
var crypto = require('../crypto/crypto');
var btcutils = require('../utils/btcutils');
+var Amount = require('../utils/amount');
var constants = require('../protocol/constants');
var Network = require('../protocol/network');
var Script = require('../script/script');
@@ -2041,10 +2042,10 @@ TX.prototype.inspect = function inspect() {
size: this.getSize(),
virtualSize: this.maxSize(),
height: this.height,
- value: utils.btc(this.getOutputValue()),
- fee: utils.btc(this.getFee()),
- minFee: utils.btc(this.getMinFee()),
- rate: utils.btc(rate),
+ value: Amount.btc(this.getOutputValue()),
+ fee: Amount.btc(this.getFee()),
+ minFee: Amount.btc(this.getMinFee()),
+ rate: Amount.btc(rate),
date: utils.date(this.ts || this.ps),
block: this.block ? utils.revHex(this.block) : null,
ts: this.ts,
@@ -2084,8 +2085,8 @@ TX.prototype.toJSON = function toJSON(network) {
ps: this.ps,
date: utils.date(this.ts || this.ps),
index: this.index,
- fee: utils.btc(this.getFee()),
- rate: utils.btc(rate),
+ fee: Amount.btc(this.getFee()),
+ rate: Amount.btc(rate),
version: this.version,
flag: this.flag,
inputs: this.inputs.map(function(input) {
diff --git a/lib/utils/amount.js b/lib/utils/amount.js
index 6457938a..362cbc27 100644
--- a/lib/utils/amount.js
+++ b/lib/utils/amount.js
@@ -1,5 +1,19 @@
+/*!
+ * amount.js - amount object for bcoin
+ * Copyright (c) 2014-2016, Christopher Jeffrey (MIT License).
+ * https://github.com/bcoin-org/bcoin
+ */
+
+'use strict';
+
+var assert = require('assert');
var utils = require('./utils');
+/**
+ * Amount
+ * @constructor
+ */
+
function Amount(value, unit, num) {
if (!(this instanceof Amount))
return new Amount(value, unit, num);
@@ -47,6 +61,7 @@ Amount.prototype.to = function to(unit, num) {
switch (unit) {
case 'sat':
return this.toSatoshis(num);
+ case 'ubtc':
case 'bits':
return this.toBits(num);
case 'mbtc':
@@ -91,6 +106,7 @@ Amount.prototype.from = function from(unit, value, num) {
switch (unit) {
case 'sat':
return this.fromSatoshis(value, num);
+ case 'ubtc':
case 'bits':
return this.fromBits(value, num);
case 'mbtc':
@@ -101,7 +117,7 @@ Amount.prototype.from = function from(unit, value, num) {
throw new Error('Unknown unit "' + unit + '".');
};
-Amount.fromOptions = function fromOptions(value) {
+Amount.fromOptions = function fromOptions(value, unit, num) {
return new Amount().fromOptions(value);
};
@@ -141,19 +157,21 @@ Amount.prototype.inspect = function inspect() {
* @returns {String} BTC string.
*/
-Amount.btc = function btc(value) {
+Amount.btc = function btc(value, num) {
if (utils.isFloat(value))
return value;
- return Amount.serialize(value, 8, false);
+ return Amount.serialize(value, 8, num);
};
/**
* Safely convert satoshis to a BTC string.
* This function explicitly avoids any
* floating point arithmetic.
- * @param {Amount} value - Satoshis.
- * @returns {String} BTC string.
+ * @param {Amount} value
+ * @param {Number} dec - Number of decimals.
+ * @param {Boolean} num - Return a number.
+ * @returns {String}
*/
Amount.serialize = function serialize(value, dec, num) {
@@ -167,11 +185,9 @@ Amount.serialize = function serialize(value, dec, num) {
negative = true;
}
- assert(value <= utils.MAX_SAFE_INTEGER, 'Number exceeds 2^53-1.');
-
value = value.toString(10);
- assert(value.length <= 8 + dec, 'Number exceeds 2^53-1.');
+ assert(value.length <= 16, 'Number exceeds 2^53-1.');
while (value.length < dec + 1)
value = '0' + value;
@@ -195,22 +211,44 @@ Amount.serialize = function serialize(value, dec, num) {
return result;
};
+/**
+ * Unsafely convert satoshis to a BTC string.
+ * @param {Amount} value
+ * @param {Number} dec - Number of decimals.
+ * @param {Boolean} num - Return a number.
+ * @returns {String}
+ */
+
+Amount.serializeUnsafe = function serializeUnsafe(value, dec, num) {
+ assert(utils.isInt(value), 'Non-satoshi value for conversion.');
+
+ value /= pow10(dec);
+ value = value.toFixed(dec);
+
+ if (num)
+ return +value;
+
+ if (dec !== 0) {
+ value = value.replace(/0+$/, '');
+ if (value[value.length - 1] === '.')
+ value += '0';
+ }
+
+ return value;
+};
+
/**
* Safely convert a BTC string to satoshis.
- * This function explicitly avoids any
- * floating point arithmetic. It also does
- * extra validation to ensure the resulting
- * Number will be 53 bits or less.
* @param {String} value - BTC
* @returns {Amount} Satoshis.
* @throws on parse error
*/
-Amount.satoshi = function satoshi(value) {
+Amount.value = function value(value, num) {
if (utils.isInt(value))
return value;
- return Amount.parse(value, 8, false);
+ return Amount.parse(value, 8, num);
};
/**
@@ -220,15 +258,17 @@ Amount.satoshi = function satoshi(value) {
* extra validation to ensure the resulting
* Number will be 53 bits or less.
* @param {String} value - BTC
+ * @param {Number} dec - Number of decimals.
+ * @param {Boolean} num - Allow numbers.
* @returns {Amount} Satoshis.
* @throws on parse error
*/
Amount.parse = function parse(value, dec, num) {
var negative = false;
- var mult = Math.pow(10, dec);
- var maxLo = utils.MAX_SAFE_INTEGER % mult;
- var maxHi = (utils.MAX_SAFE_INTEGER - maxLo) / mult;
+ var mult = pow10(dec);
+ var maxLo = modSafe(mult);
+ var maxHi = divSafe(mult);
var parts, hi, lo, result;
if (num && typeof value === 'number') {
@@ -276,6 +316,110 @@ Amount.parse = function parse(value, dec, num) {
return result;
};
+/**
+ * Unsafely convert a BTC string to satoshis.
+ * @param {String} value - BTC
+ * @param {Number} dec - Number of decimals.
+ * @param {Boolean} num - Allow numbers.
+ * @returns {Amount} Satoshis.
+ * @throws on parse error
+ */
+
+Amount.parseUnsafe = function parseUnsafe(value, dec, num) {
+ if (typeof value === 'string') {
+ assert(utils.isFloat(value), 'Non-BTC value for conversion.');
+ value = parseFloat(value, 10);
+ } else {
+ assert(utils.isNumber(value), 'Non-BTC value for conversion.');
+ assert(num, 'Cannot parse number.');
+ }
+
+ value *= pow10(dec);
+
+ assert(value % 1 === 0, 'Too many decimal places.');
+
+ return value;
+};
+
+/*
+ * Helpers
+ */
+
+function pow10(exp) {
+ switch (exp) {
+ case 0:
+ return 1;
+ case 1:
+ return 10;
+ case 2:
+ return 100;
+ case 3:
+ return 1000;
+ case 4:
+ return 10000;
+ case 5:
+ return 100000;
+ case 6:
+ return 1000000;
+ case 7:
+ return 10000000;
+ case 8:
+ return 100000000;
+ default:
+ assert(false);
+ }
+}
+
+function modSafe(mod) {
+ switch (mod) {
+ case 1:
+ return 0;
+ case 10:
+ return 1;
+ case 100:
+ return 91;
+ case 1000:
+ return 991;
+ case 10000:
+ return 991;
+ case 100000:
+ return 40991;
+ case 1000000:
+ return 740991;
+ case 10000000:
+ return 4740991;
+ case 100000000:
+ return 54740991;
+ default:
+ assert(false);
+ }
+}
+
+function divSafe(div) {
+ switch (div) {
+ case 1:
+ return 9007199254740991;
+ case 10:
+ return 900719925474099;
+ case 100:
+ return 90071992547409;
+ case 1000:
+ return 9007199254740;
+ case 10000:
+ return 900719925474;
+ case 100000:
+ return 90071992547;
+ case 1000000:
+ return 9007199254;
+ case 10000000:
+ return 900719925;
+ case 100000000:
+ return 90071992;
+ default:
+ assert(false);
+ }
+}
+
/*
* Expose
*/
diff --git a/lib/utils/btcutils.js b/lib/utils/btcutils.js
index dab5711e..85745aa2 100644
--- a/lib/utils/btcutils.js
+++ b/lib/utils/btcutils.js
@@ -11,6 +11,7 @@ var assert = require('assert');
var BN = require('bn.js');
var constants = require('../protocol/constants');
var utils = require('./utils');
+var Amount = require('./amount');
var btcutils = exports;
/**
@@ -194,42 +195,9 @@ btcutils.getRate = function getRate(size, fee) {
*/
btcutils.btc = function btc(value) {
- var negative = false;
- var hi, lo, result;
-
if (utils.isFloat(value))
return value;
-
- assert(utils.isInt(value), 'Non-satoshi value for conversion.');
-
- if (value < 0) {
- value = -value;
- negative = true;
- }
-
- assert(value <= utils.MAX_SAFE_INTEGER, 'Number exceeds 2^53-1.');
-
- value = value.toString(10);
-
- assert(value.length <= 16, 'Number exceeds 2^53-1.');
-
- while (value.length < 9)
- value = '0' + value;
-
- hi = value.slice(0, -8);
- lo = value.slice(-8);
-
- lo = lo.replace(/0+$/, '');
-
- if (lo.length === 0)
- lo += '0';
-
- result = hi + '.' + lo;
-
- if (negative)
- result = '-' + result;
-
- return result;
+ return Amount.fromValue(value).toBTC();
};
/**
@@ -244,50 +212,9 @@ btcutils.btc = function btc(value) {
*/
btcutils.satoshi = function satoshi(value) {
- var negative = false;
- var parts, hi, lo, result;
-
if (utils.isInt(value))
return value;
-
- assert(utils.isFloat(value), 'Non-BTC value for conversion.');
-
- if (value[0] === '-') {
- negative = true;
- value = value.substring(1);
- }
-
- parts = value.split('.');
-
- assert(parts.length <= 2, 'Bad decimal point.');
-
- hi = parts[0] || '0';
- lo = parts[1] || '0';
-
- hi = hi.replace(/^0+/, '');
- lo = lo.replace(/0+$/, '');
-
- assert(hi.length <= 8, 'Number exceeds 2^53-1.');
- assert(lo.length <= 8, 'Too many decimal places.');
-
- if (hi.length === 0)
- hi = '0';
-
- while (lo.length < 8)
- lo += '0';
-
- hi = parseInt(hi, 10);
- lo = parseInt(lo, 10);
-
- assert(hi < 90071992 || (hi === 90071992 && lo <= 54740991),
- 'Number exceeds 2^53-1.');
-
- result = hi * 100000000 + lo;
-
- if (negative)
- result = -result;
-
- return result;
+ return Amount.fromBTC(value).toValue();
};
/**
@@ -297,11 +224,8 @@ btcutils.satoshi = function satoshi(value) {
*/
btcutils.isSatoshi = function isSatoshi(value) {
- if (typeof value !== 'number')
- return false;
-
try {
- utils.satoshi(value);
+ Amount.fromValue(value);
return true;
} catch (e) {
return false;
@@ -315,11 +239,8 @@ btcutils.isSatoshi = function isSatoshi(value) {
*/
btcutils.isBTC = function isBTC(value) {
- if (typeof value !== 'string')
- return false;
-
try {
- utils.btc(value);
+ Amount.fromBTC(value);
return true;
} catch (e) {
return false;
diff --git a/lib/utils/errors.js b/lib/utils/errors.js
index 1e5c01cb..ad4575bd 100644
--- a/lib/utils/errors.js
+++ b/lib/utils/errors.js
@@ -9,6 +9,7 @@
var utils = require('../utils/utils');
var constants = require('../protocol/constants');
+var Amount = require('./amount');
/**
* An error thrown during verification. Can be either
@@ -140,8 +141,8 @@ function FundingError(msg, available, required) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, FundingError);
- msg += ' (available=' + utils.btc(available) + ',';
- msg += ' required=' + utils.btc(required) + ')';
+ msg += ' (available=' + Amount.btc(available) + ',';
+ msg += ' required=' + Amount.btc(required) + ')';
this.type = 'FundingError';
this.message = msg;
diff --git a/lib/utils/uri.js b/lib/utils/uri.js
index 0459c79a..32737d57 100644
--- a/lib/utils/uri.js
+++ b/lib/utils/uri.js
@@ -6,8 +6,9 @@
'use strict';
-var utils = require('../utils/utils');
+var utils = require('./utils');
var Address = require('../primitives/address');
+var Amount = require('./amount');
var assert = require('assert');
function URI(options) {
@@ -88,7 +89,7 @@ URI.prototype.fromString = function fromString(str) {
query = parsePairs(query);
if (query.amount)
- this.amount = utils.satoshi(query.amount);
+ this.amount = Amount.value(query.amount);
if (query.label)
this.label = query.label;
@@ -113,7 +114,7 @@ URI.prototype.toString = function toString() {
str += this.address.toBase58();
if (this.amount !== -1)
- query.push('amount=' + utils.btc(this.amount));
+ query.push('amount=' + Amount.btc(this.amount));
if (this.label)
query.push('label=' + escape(this.label));
diff --git a/lib/utils/utils.js b/lib/utils/utils.js
index e21a5e7f..9650d119 100644
--- a/lib/utils/utils.js
+++ b/lib/utils/utils.js
@@ -9,20 +9,13 @@
/* global gc */
-/**
- * @exports utils
- */
-
-var utils = exports;
-
var assert = require('assert');
var util = require('util');
var fs = require('fs');
var os = require('os');
var BN = require('bn.js');
-var base58 = require('./base58');
+var utils = exports;
var Number, Math, Date;
-var lazy;
/**
* Reference to the global object.
@@ -47,6 +40,10 @@ utils.global = (function() {
assert(false, 'No global defined.');
})();
+/*
+ * Globals
+ */
+
Number = utils.global.Number;
Math = utils.global.Math;
Date = utils.global.Date;
@@ -264,111 +261,6 @@ utils.merge = function merge(target) {
if (Object.assign)
utils.merge = Object.assign;
-/**
- * Safely convert satoshis to a BTC string.
- * This function explicitly avoids any
- * floating point arithmetic.
- * @param {Amount} value - Satoshis.
- * @returns {String} BTC string.
- */
-
-utils.btc = function btc(value) {
- var negative = false;
- var hi, lo, result;
-
- if (utils.isFloat(value))
- return value;
-
- assert(utils.isInt(value), 'Non-satoshi value for conversion.');
-
- if (value < 0) {
- value = -value;
- negative = true;
- }
-
- assert(value <= utils.MAX_SAFE_INTEGER, 'Number exceeds 2^53-1.');
-
- value = value.toString(10);
-
- assert(value.length <= 16, 'Number exceeds 2^53-1.');
-
- while (value.length < 9)
- value = '0' + value;
-
- hi = value.slice(0, -8);
- lo = value.slice(-8);
-
- lo = lo.replace(/0+$/, '');
-
- if (lo.length === 0)
- lo += '0';
-
- result = hi + '.' + lo;
-
- if (negative)
- result = '-' + result;
-
- return result;
-};
-
-/**
- * Safely convert a BTC string to satoshis.
- * This function explicitly avoids any
- * floating point arithmetic. It also does
- * extra validation to ensure the resulting
- * Number will be 53 bits or less.
- * @param {String} value - BTC
- * @returns {Amount} Satoshis.
- * @throws on parse error
- */
-
-utils.satoshi = function satoshi(value) {
- var negative = false;
- var parts, hi, lo, result;
-
- if (utils.isInt(value))
- return value;
-
- assert(utils.isFloat(value), 'Non-BTC value for conversion.');
-
- if (value[0] === '-') {
- negative = true;
- value = value.substring(1);
- }
-
- parts = value.split('.');
-
- assert(parts.length <= 2, 'Bad decimal point.');
-
- hi = parts[0] || '0';
- lo = parts[1] || '0';
-
- hi = hi.replace(/^0+/, '');
- lo = lo.replace(/0+$/, '');
-
- assert(hi.length <= 8, 'Number exceeds 2^53-1.');
- assert(lo.length <= 8, 'Too many decimal places.');
-
- if (hi.length === 0)
- hi = '0';
-
- while (lo.length < 8)
- lo += '0';
-
- hi = parseInt(hi, 10);
- lo = parseInt(lo, 10);
-
- assert(hi < 90071992 || (hi === 90071992 && lo <= 54740991),
- 'Number exceeds 2^53-1.');
-
- result = hi * 100000000 + lo;
-
- if (negative)
- result = -result;
-
- return result;
-};
-
/**
* Max safe integer (53 bits).
* @const {Number}
diff --git a/lib/wallet/txdb.js b/lib/wallet/txdb.js
index 93a229d8..d7512336 100644
--- a/lib/wallet/txdb.js
+++ b/lib/wallet/txdb.js
@@ -15,6 +15,7 @@ var constants = require('../protocol/constants');
var BufferReader = require('../utils/reader');
var BufferWriter = require('../utils/writer');
var btcutils = require('../utils/btcutils');
+var Amount = require('../utils/amount');
var TX = require('../primitives/tx');
var Coin = require('../primitives/coin');
var Outpoint = require('../primitives/outpoint');
@@ -81,8 +82,8 @@ TXDB.prototype.open = co(function* open() {
this.logger.info(
'Balance: unconfirmed=%s confirmed=%s.',
- utils.btc(this.state.unconfirmed),
- utils.btc(this.state.confirmed));
+ Amount.btc(this.state.unconfirmed),
+ Amount.btc(this.state.confirmed));
});
/**
@@ -2808,8 +2809,8 @@ Balance.prototype.toJSON = function toJSON(minimal) {
wid: !minimal ? this.wid : undefined,
id: !minimal ? this.id : undefined,
account: !minimal ? this.account : undefined,
- unconfirmed: utils.btc(this.unconfirmed),
- confirmed: utils.btc(this.confirmed)
+ unconfirmed: Amount.btc(this.unconfirmed),
+ confirmed: Amount.btc(this.confirmed)
};
};
@@ -2820,8 +2821,8 @@ Balance.prototype.toJSON = function toJSON(minimal) {
Balance.prototype.toString = function toString() {
return '';
};
@@ -2944,8 +2945,8 @@ TXDBState.prototype.toJSON = function toJSON(minimal) {
id: !minimal ? this.id : undefined,
tx: this.tx,
coin: this.coin,
- unconfirmed: utils.btc(this.unconfirmed),
- confirmed: utils.btc(this.confirmed)
+ unconfirmed: Amount.btc(this.unconfirmed),
+ confirmed: Amount.btc(this.confirmed)
};
};
@@ -3221,8 +3222,8 @@ Details.prototype.toJSON = function toJSON() {
index: this.index,
size: this.size,
virtualSize: this.vsize,
- fee: utils.btc(fee),
- rate: utils.btc(rate),
+ fee: Amount.btc(fee),
+ rate: Amount.btc(rate),
confirmations: this.getConfirmations(),
inputs: this.inputs.map(function(input) {
return input.toJSON(self.network);
@@ -3259,7 +3260,7 @@ function DetailsMember() {
DetailsMember.prototype.toJSON = function toJSON(network) {
return {
- value: utils.btc(this.value),
+ value: Amount.btc(this.value),
address: this.address
? this.address.toBase58(network)
: null,
diff --git a/test/http-test.js b/test/http-test.js
index d9c618fb..820701a7 100644
--- a/test/http-test.js
+++ b/test/http-test.js
@@ -9,6 +9,7 @@ var crypto = require('../lib/crypto/crypto');
var assert = require('assert');
var scriptTypes = constants.scriptTypes;
var co = require('../lib/utils/co');
+var Amount = require('../lib/utils/amount');
var cob = co.cob;
var dummyInput = {
@@ -108,16 +109,16 @@ describe('HTTP', function() {
assert.equal(receive.type, 'pubkeyhash');
assert.equal(receive.branch, 0);
assert(balance);
- assert.equal(utils.satoshi(balance.confirmed), 0);
- assert.equal(utils.satoshi(balance.unconfirmed), 201840);
+ assert.equal(Amount.value(balance.confirmed), 0);
+ assert.equal(Amount.value(balance.unconfirmed), 201840);
assert(details);
assert.equal(details.hash, t1.rhash);
}));
it('should get balance', cob(function* () {
var balance = yield wallet.getBalance();
- assert.equal(utils.satoshi(balance.confirmed), 0);
- assert.equal(utils.satoshi(balance.unconfirmed), 201840);
+ assert.equal(Amount.value(balance.confirmed), 0);
+ assert.equal(Amount.value(balance.unconfirmed), 201840);
}));
it('should send a tx', cob(function* () {
@@ -138,8 +139,8 @@ describe('HTTP', function() {
assert.equal(tx.inputs.length, 1);
assert.equal(tx.outputs.length, 2);
- value += utils.satoshi(tx.outputs[0].value);
- value += utils.satoshi(tx.outputs[1].value);
+ value += Amount.value(tx.outputs[0].value);
+ value += Amount.value(tx.outputs[1].value);
assert.equal(value, 48190);
hash = tx.hash;
@@ -160,7 +161,7 @@ describe('HTTP', function() {
it('should get balance', cob(function* () {
var balance = yield wallet.getBalance();
- assert.equal(utils.satoshi(balance.unconfirmed), 199570);
+ assert.equal(Amount.value(balance.unconfirmed), 199570);
}));
it('should execute an rpc call', cob(function* () {
diff --git a/test/utils-test.js b/test/utils-test.js
index 26772fd3..72308421 100644
--- a/test/utils-test.js
+++ b/test/utils-test.js
@@ -9,6 +9,7 @@ var base58 = require('../lib/utils/base58');
var encoding = require('../lib/utils/encoding');
var crypto = require('../lib/crypto/crypto');
var schnorr = require('../lib/crypto/schnorr');
+var Amount = require('../lib/utils/amount');
describe('Utils', function() {
var vectors = [
@@ -49,42 +50,42 @@ describe('Utils', function() {
});
it('should convert satoshi to btc', function() {
- var btc = btcutils.btc(5460);
+ var btc = Amount.btc(5460);
assert.equal(btc, '0.0000546');
- btc = btcutils.btc(54678 * 1000000);
+ btc = Amount.btc(54678 * 1000000);
assert.equal(btc, '546.78');
- btc = btcutils.btc(5460 * 10000000);
+ btc = Amount.btc(5460 * 10000000);
assert.equal(btc, '546.0');
});
it('should convert btc to satoshi', function() {
- var btc = btcutils.satoshi('0.0000546');
+ var btc = Amount.value('0.0000546');
assert(btc === 5460);
- btc = btcutils.satoshi('546.78');
+ btc = Amount.value('546.78');
assert(btc === 54678 * 1000000);
- btc = btcutils.satoshi('546');
+ btc = Amount.value('546');
assert(btc === 5460 * 10000000);
- btc = btcutils.satoshi('546.0');
+ btc = Amount.value('546.0');
assert(btc === 5460 * 10000000);
- btc = btcutils.satoshi('546.0000');
+ btc = Amount.value('546.0000');
assert(btc === 5460 * 10000000);
assert.doesNotThrow(function() {
- btcutils.satoshi('546.00000000000000000');
+ Amount.value('546.00000000000000000');
});
assert.throws(function() {
- btcutils.satoshi('546.00000000000000001');
+ Amount.value('546.00000000000000001');
});
assert.doesNotThrow(function() {
- btcutils.satoshi('90071992.54740991');
+ Amount.value('90071992.54740991');
});
assert.doesNotThrow(function() {
- btcutils.satoshi('090071992.547409910');
+ Amount.value('090071992.547409910');
});
assert.throws(function() {
- btcutils.satoshi('90071992.54740992');
+ Amount.value('90071992.54740992');
});
assert.throws(function() {
- btcutils.satoshi('190071992.54740991');
+ Amount.value('190071992.54740991');
});
});