3 let Dash = module.exports;
5 const DUFFS = 100000000;
9 let Dashcore = require("@dashevo/dashcore-lib");
10 let Transaction = Dashcore.Transaction;
12 Dash.create = function ({
19 * Instant Balance is accurate with Instant Send
20 * @param {String} address
21 * @returns {Promise<InstantBalance>}
23 dashApi.getInstantBalance = async function (address) {
24 let body = await insightApi.getUtxos(address);
25 let utxos = await getUtxos(body);
26 let balance = utxos.reduce(function (total, utxo) {
27 return total + utxo.satoshis;
29 // because 0.1 + 0.2 = 0.30000000000000004,
30 // but we would only want 0.30000000
31 let floatBalance = parseFloat((balance / DUFFS).toFixed(8));
35 balance: floatBalance,
37 _utxoCount: utxos.length,
38 _utxoAmounts: utxos.map(function (utxo) {
46 * @param {String} privKey
49 dashApi.createBalanceTransfer = async function (privKey, pub) {
50 let pk = new Dashcore.PrivateKey(privKey);
51 let changeAddr = pk.toPublicKey().toAddress().toString();
53 let body = await insightApi.getUtxos(changeAddr);
54 let utxos = await getUtxos(body);
55 let balance = utxos.reduce(function (total, utxo) {
56 return total + utxo.satoshis;
59 //@ts-ignore - no input required, actually
60 let tmpTx = new Transaction()
61 //@ts-ignore - allows single value or array
63 tmpTx.to(pub, balance - 1000);
66 // TODO getsmartfeeestimate??
67 // fee = 1duff/byte (2 chars hex is 1 byte)
68 // +10 to be safe (the tmpTx may be a few bytes off)
69 let fee = 10 + tmpTx.toString().length / 2;
71 //@ts-ignore - no input required, actually
72 let tx = new Transaction()
73 //@ts-ignore - allows single value or array
75 tx.to(pub, balance - fee);
83 * Send with change back
84 * @param {String} privKey
85 * @param {(String|import('@dashevo/dashcore-lib').Address)} payAddr
86 * @param {Number} amount
87 * @param {(String|import('@dashevo/dashcore-lib').Address)} [changeAddr]
89 dashApi.createPayment = async function (
95 let pk = new Dashcore.PrivateKey(privKey);
96 let utxoAddr = pk.toPublicKey().toAddress().toString();
98 changeAddr = utxoAddr;
101 // TODO make more accurate?
102 let feePreEstimate = 1000;
103 let utxos = await getOptimalUtxos(utxoAddr, amount + feePreEstimate);
104 let balance = getBalance(utxos);
107 throw new Error(`not enough funds available in utxos for ${utxoAddr}`);
110 // (estimate) don't send dust back as change
111 if (balance - amount <= DUST + FEE) {
115 //@ts-ignore - no input required, actually
116 let tmpTx = new Transaction()
117 //@ts-ignore - allows single value or array
119 tmpTx.to(payAddr, amount);
120 //@ts-ignore - the JSDoc is wrong in dashcore-lib/lib/transaction/transaction.js
121 tmpTx.change(changeAddr);
124 // TODO getsmartfeeestimate??
125 // fee = 1duff/byte (2 chars hex is 1 byte)
126 // +10 to be safe (the tmpTx may be a few bytes off - probably only 4 -
127 // due to how small numbers are encoded)
128 let fee = 10 + tmpTx.toString().length / 2;
130 // (adjusted) don't send dust back as change
131 if (balance + -amount + -fee <= DUST) {
132 amount = balance - fee;
135 //@ts-ignore - no input required, actually
136 let tx = new Transaction()
137 //@ts-ignore - allows single value or array
139 tx.to(payAddr, amount);
141 //@ts-ignore - see above
142 tx.change(changeAddr);
148 // TODO make more optimal
150 * @param {String} utxoAddr
151 * @param {Number} fullAmount - including fee estimate
153 async function getOptimalUtxos(utxoAddr, fullAmount) {
154 // get smallest coin larger than transaction
155 // if that would create dust, donate it as tx fee
156 let body = await insightApi.getUtxos(utxoAddr);
157 let utxos = await getUtxos(body);
158 let balance = getBalance(utxos);
160 if (balance < fullAmount) {
164 // from largest to smallest
165 utxos.sort(function (a, b) {
166 return b.satoshis - a.satoshis;
169 /** @type Array<CoreUtxo> */
173 // try to get just one
174 utxos.every(function (utxo) {
175 if (utxo.satoshis > fullAmount) {
177 total = utxo.satoshis;
186 // try to use as few coins as possible
187 utxos.some(function (utxo) {
189 total += utxo.satoshis;
190 return total >= fullAmount;
196 * @param {Array<CoreUtxo>} utxos
198 function getBalance(utxos) {
199 return utxos.reduce(function (total, utxo) {
200 return total + utxo.satoshis;
205 * @param {Array<InsightUtxo>} body
207 async function getUtxos(body) {
208 /** @type Array<CoreUtxo> */
211 await body.reduce(async function (promise, utxo) {
214 let data = await insightApi.getTx(utxo.txid);
216 // TODO the ideal would be the smallest amount that is greater than the required amount
219 data.vout.some(function (vout, index) {
220 if (!vout.scriptPubKey?.addresses?.includes(utxo.address)) {
224 let satoshis = Math.round(parseFloat(vout.value) * DUFFS);
225 if (utxo.satoshis !== satoshis) {
235 outputIndex: utxoIndex,
236 address: utxo.address,
237 script: utxo.scriptPubKey,
238 satoshis: utxo.satoshis,
240 }, Promise.resolve());