UNPKG

digibyte-js

Version:

A pure and powerful JavaScript DigiByte library.

250 lines (214 loc) 7.79 kB
const Transaction = require('../transaction/transaction'); const Precision = require('../encoding/precision'); const Address = require('../address'); const Price = require('../price'); const Vote = require('./vote'); /** * @param {Rules} rules * @param {Price} price */ function Transferor(rules, price) { this._rules = rules || {}; this._price = price || {}; this._inputs = []; this._gas = []; this._outputs = []; this._assetChange = null; this._toBurn = 0; this._burnExtra = false; } Transferor.prototype = { get assetsIn() { var assets = {}; var amountIn = 0; for (var input of this._inputs) { assets[input.assets[0].assetId] = true; amountIn += input.assets[0].count; } if (Object.keys(assets) > 1) throw "This library can't handle different asset inputs"; return amountIn; }, get assetsOut() { var amountOut = 0; for (var output of this._outputs) amountOut += output.count; return amountOut; }, get assetsBurned() { if (this._burnExtra) return this.assetsIn - this.assetsOut; var deflation = 0; if (this._rules.deflation) deflation = this.isConsolidation ? 0 : this._rules.deflation; return this._toBurn + deflation; }, get assetsChange() { if (this._burnExtra) return 0; return this.assetsIn - this.assetsOut - this.assetsBurned; }, get changes() { var changes = {}; for (var input of this._inputs) changes[input.address] = (changes[input.address] || 0) - input.assets[0].count; for (var output of this._outputs) changes[output.address] = (changes[output.address] || 0) + output.count; this._rules = { rules: this._rules }; var change = this.assetsChange; this._rules = this._rules.rules; if (change > 0) { if (!Address.isValid(this._assetChange)) throw "Define change address or burn extra assets"; changes[this._assetChange] = (changes[this._assetChange] || 0) + change; } return changes; }, get isConsolidation() { for (var change of Object.values(this.changes)) if (change > 0) return false; return true; }, get inputs() { return this._inputs.concat(this._gas); }, get outputs() { var result = []; for (var output of this._outputs) result.push({ address: output.address, satoshis: 600 }); if (!this.isConsolidation) if ((this._rules.voting || {}).restricted) for (var address of Object.keys(this.changes)) { if (this.changes[address] <= 0) continue; if (!Vote.isValidAddress(address)) throw "This asset can't be send to a non voting address"; } if (this.assetsChange > 0) { if (Address.isValid(this._assetChange)) result.push({ address: this._assetChange, satoshis: 600 }); else throw "Define change address or burn extra assets"; } else if (this.assetsChange < 0) throw "More assets output than input"; return result; }, get instructions() { var data = new Precision(); if (this.assetsBurned > 0) data.addHex("44410325"); else data.addHex("44410315"); var n = 0; for (var output of this._outputs) { data.addInteger(0, 3); data.addInteger(n++, 5); data.addPrecision(output.count); } if (this.assetsChange > 0) { if (Address.isValid(this._assetChange)) { data.addInteger(0, 3); data.addInteger(n++, 5); data.addPrecision(this.assetsChange); } else throw "Define change address or burn extra assets"; } else if (this.assetsChange < 0) throw "More assets output than input"; if (n >= 32) throw "To many asset outputs max. 31"; if (this.assetsBurned > 0) { data.addInteger(0, 3); data.addInteger(31, 5); data.addPrecision(this.assetsBurned); } var result = data.toBuffer(); if (result.length > 80) throw "To many asset outputs"; return result; }, get royalty() { var result = []; if (this.isConsolidation) return []; if (this._rules.royalty === undefined) return []; var exchange = 100000000; if (this._rules.royalty.units !== undefined) { if (!this._price) throw "Must provide a price object"; if (typeof this._rules.royalty.units == "string" && this._rules.royalty.units.name != "DGB") this._rules.royalty.units = { ...Price.publishers.find(x => x.name === this._rules.royalty.units) }; if (typeof this._rules.royalty.units == "object") exchange = Math.round((this._price.find(x => x.address === this._rules.royalty.units.address && x.index === this._rules.royalty.units.index) || {}).value); } if (typeof exchange != "number" || isNaN(exchange)) throw "Corrupted price array"; var addresses = Object.keys(this._rules.royalty.addresses); for (var address of addresses) { var satoshis = Price.Multiply(this._rules.royalty.addresses[address], exchange); result.push({ address, satoshis }); } return result; } } /** * * @param {Object} utxo * @param {string} utxo.txid - Previus Transaction ID * @param {int} utxo.vout - Output vout * @param {string} utxo.address - Output Script * @param {int} utxo.satoshis - Output amount * * @param { { assetId, cid, count }[] } utxo.assets - Asset Array */ Transferor.prototype.addInputs = function (utxos) { if (typeof utxos.length == 'undefined') utxos = [utxos]; for (var utxo of utxos) { if (!utxo.address) utxo.address = Address.fromScript(utxo.script || utxo.scriptPubKey).toString(); if (utxo.assets) { if (utxo.assets.length == 1) this._inputs.push(utxo); else if (utxo.assets.length == 0) this._gas.push(utxo); else throw "This library can't handle multi-asset inputs"; } else this._gas.push(utxo); } return this; } /** * * @param {string} address * @param {int} amount */ Transferor.prototype.addOutput = function (address, count) { this._outputs.push({ address, count }); return this; } /** * * @param {string} address */ Transferor.prototype.setAssetChange = function (address) { this._assetChange = address; return this; } /** * Burn certain amount of assets * @param {number} amount */ Transferor.prototype.burnAssets = function (amount) { this._toBurn += amount; return this; } /** * Burn assets not assigned * @param {boolean} burn */ Transferor.prototype.burnExtra = function (burn) { this._burnExtra = burn; return this; } /** * Build a Transaction object without setting fees or change address * @returns {Transaction} */ Transferor.prototype.toTransaction = function () { var transaction = new Transaction() .from(this.inputs) .to(this.outputs) .addData(this.instructions) .to(this.royalty); return transaction; } module.exports = Transferor;