diff --git a/src/helpers/wallet.js b/src/helpers/wallet.js index c701a55..7a1a2ea 100644 --- a/src/helpers/wallet.js +++ b/src/helpers/wallet.js @@ -1,6 +1,8 @@ import { + DashTx, DashHd, DashSight, + DashSocket, Cryptic, } from '../imports.js' import { DatabaseSetup } from './db.js' @@ -12,6 +14,34 @@ let dashsight = DashSight.create({ // baseUrl: 'https://dashsight.dashincubator.dev', }); +let defaultSocketEvents = { + onClose: async (e) => console.log('onClose', e), + onError: async (e) => console.log('onError', e), + onMessage: async (e, data) => console.log('onMessage', e, data), +} + +export async function initDashSocket( + events = {} +) { + // @ts-ignore + let dashsocket = DashSocket.create({ + dashsocketBaseUrl: 'https://insight.dash.org/socket.io', + cookieStore: null, + debug: true, + ...defaultSocketEvents, + ...events, + }) + + await dashsocket.init() + .catch((e) => console.log('dashsocket catch err', e)); + + setTimeout(() => { + dashsocket.close() + }, 15*60*1000); + + return dashsocket +} + export const store = await DatabaseSetup() export async function loadWallets() { @@ -335,3 +365,102 @@ export async function batchAddressGenerate( finalAddressIndex: addressIndex, } } + +// await forceInsightUpdate('XsiasfIJBjifeifasdijbuUt3') +export async function forceInsightUpdateForAddress(addr) { + let currentAddr = await store.addresses.getItem( + addr + ) + await store.addresses.setItem( + addr, + { + ...currentAddr, + insight: { + ...currentAddr.insight, + updated_at: 0 + } + } + ) +} + +export async function sendTx( + fromWallet, recipient, amount, +) { + const MIN_FEE = 191; + const DUST = 2000; + + console.log(DashTx, fromWallet) + let dashTx = DashTx.create({ + // @ts-ignore + version: 3, + }); + + let privateKeys = { + [fromWallet.address]: fromWallet.addressKey.privateKey, + }; + + let coreUtxos = await dashsight.getCoreUtxos(fromWallet.address); + + console.log('coreUtxos', coreUtxos); + + let payments = [ + { + address: recipient?.address || recipient, + satoshis: DashTx.toSats(amount), + }, + ]; + + let spendableDuffs = coreUtxos.reduce(function (total, utxo) { + return total + utxo.satoshis; + }, 0); + let spentDuffs = payments.reduce(function (total, output) { + return total + output.satoshis; + }, 0); + let unspentDuffs = spendableDuffs - spentDuffs; + + let txInfo = { + inputs: coreUtxos, + outputs: payments, + }; + + let sizes = DashTx.appraise(txInfo); + let midFee = sizes.mid; + + if (unspentDuffs < MIN_FEE) { + throw new Error( + `overspend: inputs total '${spendableDuffs}', but outputs total '${spentDuffs}', which leaves no way to pay the fee of '${sizes.mid}'`, + ); + } + + txInfo.inputs.sort(DashTx.sortInputs) + + let outputs = txInfo.outputs.slice(0); + let change; + + change = unspentDuffs - (midFee + DashTx.OUTPUT_SIZE); + if (change < DUST) { + change = 0; + } + if (change) { + txInfo.outputs = outputs.slice(0); + txInfo.outputs.push({ + address: fromWallet.address, + satoshis: change, + }); + } + + let keys = coreUtxos.map(utxo => privateKeys[utxo.address]); + + let tx = await dashTx.hashAndSignAll(txInfo, keys); + + let txHex = tx.transaction; + + console.log('tx', tx); + console.log('txHex', [txHex]); + + let result = await dashsight.instantSend(txHex); + + console.log('instantSend result', result); + + return result +} \ No newline at end of file diff --git a/src/main.js b/src/main.js index 54b94e4..99c9fa9 100644 --- a/src/main.js +++ b/src/main.js @@ -9,15 +9,18 @@ import { } from './helpers/utils.js' import { + DUFFS, OIDC_CLAIMS, } from './helpers/constants.js' import { + initDashSocket, batchAddressGenerate, updateAllFunds, decryptWallet, loadWalletsForAlias, store, + sendTx, } from './helpers/wallet.js' import setupNav from './components/nav.js' @@ -57,15 +60,15 @@ let appState = envoy( aliasInfo: {}, contacts: [], }, - (state, oldState) => { - if (state.contacts !== oldState.contacts) { - console.log( - 'state.contacts !== oldState.contacts on push', - oldState.contacts, - state.contacts, - ) - } - } + // (state, oldState) => { + // if (state.contacts !== oldState.contacts) { + // console.log( + // 'state.contacts !== oldState.contacts on push', + // oldState.contacts, + // state.contacts, + // ) + // } + // } ) // rigs @@ -320,7 +323,7 @@ async function main() { appDialogs.sendConfirm = sendConfirmRig({ mainApp, setupDialog, appDialogs, - wallet, deriveWalletData, + wallet, deriveWalletData, sendTx, }) svgSprite.render() @@ -336,6 +339,7 @@ async function main() { event.stopPropagation() appDialogs.sendOrRequest.render({ + wallet, userInfo, contacts: appState.contacts }) @@ -539,14 +543,102 @@ async function main() { updateAllFunds(wallet) .then(funds => { - dashBalance?.restate({ - wallet, - walletFunds: { - balance: funds - } - }) + walletFunds.balance = funds + // dashBalance?.restate({ + // wallet, + // walletFunds: { + // balance: funds + // } + // }) }) .catch(err => console.error('catch updateAllFunds', err, wallet)) + + let addr = wallet?.address + + initDashSocket({ + onMessage: async function (evname, data) { + // console.log('onMessage check for', addr, evname, data) + // let result; + // try { + // result = await find(evname, data); + // } catch (e) { + // reject(e); + // return; + // } + + // if (result) { + // resolve(result); + // } + + if (![ + // "tx", + "txlock" + ].includes(evname) + ) { + return; + } + + let now = Date.now(); + // if (mempoolTx?.timestamp) { + // // don't wait longer than 3s for a txlock + // if (now - mempoolTx.timestamp > maxTxLockWait) { + // return mempoolTx; + // } + // } + + let result = data.vout.some(function (vout) { + if (!(addr in vout)) { + return false; + } + + let duffs = vout[addr]; + // if (amount && duffs !== amount) { + // return false; + // } + + let newTx = { + address: addr, + timestamp: now, + txid: data.txid, + satoshis: duffs, + dash: (duffs / DUFFS), + txlock: data.txlock, + }; + + walletFunds.balance = walletFunds?.balance + newTx.dash + + // dashBalance?.restate({ + // wallet, + // walletFunds: { + // balance: walletFunds?.balance || 0 + // } + // }) + + // if ("txlock" !== evname) { + // if (!mempoolTx) { + // mempoolTx = newTx; + // } + // return false; + // } + + // result = newTx; + console.log( + 'found main address', + addr, + newTx, + ) + + return newTx; + }); + + if (result) { + console.log( + 'socket found main address', + addr, + ) + } + }, + }) } main() diff --git a/src/rigs/send-confirm.js b/src/rigs/send-confirm.js index 8d1dae3..76305cf 100644 --- a/src/rigs/send-confirm.js +++ b/src/rigs/send-confirm.js @@ -8,7 +8,7 @@ export let sendConfirmRig = (function (globals) { let { mainApp, setupDialog, appDialogs, - wallet, deriveWalletData, + wallet, deriveWalletData, sendTx, } = globals let sendConfirm = setupDialog( @@ -116,7 +116,8 @@ export let sendConfirmRig = (function (globals) { // return; // } - let outWallet, address, sendWallet = {} + let outWallet, address, txRes + let sendWallet = {} if (state.contact) { outWallet = Object.values(state.contact?.outgoing)?.[0] @@ -134,22 +135,31 @@ export let sendConfirmRig = (function (globals) { address = state.to } + if (state.amount > 0) { + txRes = await sendTx( + state.wallet, + address, + state.amount, + ) + } + console.log( `${fde.intent} TO ${address}`, `Ð ${state.amount || 0}`, state.contact, + txRes, // { // xkeyId: outWallet?.xkeyId, // addressKeyId: outWallet?.addressKeyId, // addressIndex: outWallet?.addressIndex, // address: outWallet?.address, // }, - { - xkeyId: sendWallet?.xkeyId, - addressKeyId: sendWallet?.addressKeyId, - addressIndex: sendWallet?.addressIndex, - address, - }, + // { + // xkeyId: sendWallet?.xkeyId, + // addressKeyId: sendWallet?.addressKeyId, + // addressIndex: sendWallet?.addressIndex, + // address, + // }, ) } diff --git a/src/rigs/send-or-request.js b/src/rigs/send-or-request.js index 6a50a38..d59feab 100644 --- a/src/rigs/send-or-request.js +++ b/src/rigs/send-or-request.js @@ -138,7 +138,7 @@ export let sendOrRequestRig = (function (globals) { if (fde?.intent === 'scan_new_contact') { appDialogs.scanContact.render( { - wallet, + wallet: state.wallet, }, 'afterend', ) @@ -182,7 +182,7 @@ export let sendOrRequestRig = (function (globals) { appDialogs.sendConfirm.render( { - wallet, + wallet: state.wallet, contact, to, amount: Number(fde.amount), diff --git a/types.js b/types.js index 2c758e7..e1083dc 100644 --- a/types.js +++ b/types.js @@ -6,6 +6,8 @@ * @typedef {import("dashhd").HDFromXKeyOptions} HDFromXKeyOptions * @typedef {import("dashhd").HDToPublic} HDToPublic * @typedef {import("dashhd").HDKey} HDKey + * @typedef {import("dashkeys").PublicKeyToPubKeyHash} PublicKeyToPubKeyHash + * @typedef {import("dashkeys").AddressToPubKeyHash} AddressToPubKeyHash * * @typedef {{ * encPrivKey?: HTMLElement & { passphrase?: HTMLInputElement };