diff --git a/react/src/actions/actions/tools.js b/react/src/actions/actions/tools.js
index 17d1977..3295601 100644
--- a/react/src/actions/actions/tools.js
+++ b/react/src/actions/actions/tools.js
@@ -1,8 +1,6 @@
import { translate } from '../../translate/translate';
import Config from '../../config';
-import {
- triggerToaster,
-} from '../actionCreators';
+import { triggerToaster } from '../actionCreators';
import Store from '../../store';
export function shepherdToolsSeedKeys(seed) {
diff --git a/react/src/components/dashboard/tools/tools.js b/react/src/components/dashboard/tools/tools.js
index b22e26e..d6d2128 100644
--- a/react/src/components/dashboard/tools/tools.js
+++ b/react/src/components/dashboard/tools/tools.js
@@ -1,631 +1,22 @@
import React from 'react';
import { translate } from '../../../translate/translate';
-import addCoinOptionsCrypto from '../../addcoin/addcoinOptionsCrypto';
-import addCoinOptionsAC from '../../addcoin/addcoinOptionsAC';
-import Select from 'react-select';
-import {
- triggerToaster,
- shepherdToolsBalance,
- shepherdToolsBuildUnsigned,
- shepherdToolsPushTx,
- shepherdToolsSeedToWif,
- shepherdToolsWifToKP,
- shepherdElectrumListunspent,
- shepherdCliPromise,
- shepherdElectrumSplitUtxoPromise,
-} from '../../../actions/actionCreators';
-import Store from '../../../store';
-import QRCode from 'qrcode.react';
-import QRModal from '../qrModal/qrModal';
-import { isKomodoCoin } from '../../../util/coinHelper';
+
+import ToolsGetBalance from './toolsGetBalance';
+import ToolsGetUtxos from './toolsGetUtxos';
+import ToolsOfflineSigCreate from './toolsOfflineSigCreate';
+import ToolsOfflineSigScan from './toolsOfflineSigScan';
+import ToolsSeedToWif from './toolsSeedToWif';
+import ToolsWifToWif from './toolsWifToWif';
+import ToolsStringToQr from './toolsStringToQr';
+import ToolsMergeUTXO from './toolsMergeUtxo';
+import ToolsSplitUTXO from './toolsSplitUtxo';
class Tools extends React.Component {
constructor() {
super();
this.state = {
- sendFrom: '',
- sendTo: '',
- amount: 0,
- selectedCoin: '',
- balance: null,
- tx2qr: null,
- utxo: null,
- rawTx2Push: null,
- txPushResult: null,
- string2qr: null,
- // seed 2 wif
- s2wSeed: '',
- s2wCoin: '',
- s2wisIguana: true,
- s2wResult: null,
- // wif 2 wif
- w2wWif: '',
- w2wCoin: '',
- w2wResult: null,
- // utxo list
- utxoAddr: '',
- utxoCoin: '',
- utxoResult: null,
- // balance
- balanceAddr: '',
- balanceCoin: '',
- balanceResult: null,
- // utxo split
- utxoSplitLargestUtxo: null,
- utxoSplitAddress: null,
- utxoSplitWif: null,
- utxoSplitSeed: '',
- utxoSplitCoin: '',
- utxoSplitList: null,
- utxoSplitPairsCount: 1,
- utxoSplitPairs: '10,0.002',
- utxoSplitRawtx: null,
- utxoSplitPushResult: null,
- utxoSplitShowUtxoList: false,
- // utxo merge
- utxoMergeAddress: null,
- utxoMergeWif: null,
- utxoMergeSeed: '',
- utxoMergeCoin: '',
- utxoMergeUtxoNum: 10,
- utxoMergeRawtx: null,
- utxoMergeList: null,
- utxoMergePushResult: null,
- utxoMergeShowUtxoList: false,
};
- this.updateInput = this.updateInput.bind(this);
- this.updateSelectedCoin = this.updateSelectedCoin.bind(this);
- this.getBalance = this.getBalance.bind(this);
- this.getUnsignedTx = this.getUnsignedTx.bind(this);
- this.sendTx = this.sendTx.bind(this);
- this.closeQr = this.closeQr.bind(this);
this.setActiveSection = this.setActiveSection.bind(this);
- this.seed2Wif = this.seed2Wif.bind(this);
- this.wif2wif = this.wif2wif.bind(this);
- this.getBalanceAlt = this.getBalanceAlt.bind(this);
- this.getUtxos = this.getUtxos.bind(this);
- this.toggleS2wIsIguana = this.toggleS2wIsIguana.bind(this);
- this.getUtxoSplit = this.getUtxoSplit.bind(this);
- this.splitUtxo = this.splitUtxo.bind(this);
- this.getUtxoMerge = this.getUtxoMerge.bind(this);
- this.mergeUtxo = this.mergeUtxo.bind(this);
- this.toggleMergeUtxoList = this.toggleMergeUtxoList.bind(this);
- this.toggleSplitUtxoList = this.toggleSplitUtxoList.bind(this);
- }
-
- toggleMergeUtxoList() {
- this.setState({
- utxoMergeShowUtxoList: !this.state.utxoMergeShowUtxoList,
- });
- }
-
- toggleSplitUtxoList() {
- this.setState({
- utxoSplitShowUtxoList: !this.state.utxoSplitShowUtxoList,
- });
- }
-
- mergeUtxo() {
- const wif = this.state.utxoMergeWif;
- const address = this.state.utxoMergeAddress;
- const utxoNum = this.state.utxoMergeUtxoNum;
- let totalOutSize = 0;
- let _utxos = [];
- let _interest = 0;
-
- for (let i = 0; i < utxoNum; i++) {
- console.warn(`vout ${i} ${this.state.utxoMergeList[i].amount}`);
- _utxos.push(JSON.parse(JSON.stringify(this.state.utxoMergeList[i])));
- totalOutSize += Number(this.state.utxoMergeList[i].amount);
- }
-
- for (let i = 0; i < _utxos.length; i++) {
- _utxos[i].amount = Number(_utxos[i].amount) * 100000000;
- _utxos[i].interest = Number(_utxos[i].interest) * 100000000;
- _interest += _utxos[i].interest;
- }
-
- console.warn(`total out size ${totalOutSize}`);
- console.warn(`interest ${_interest}`);
-
- const payload = {
- wif,
- network: 'komodo',
- targets: [Math.floor(totalOutSize * 100000000) - 10000 + _interest],
- utxo: _utxos,
- changeAddress: address,
- outputAddress: address,
- change: 0,
- };
-
- console.log(payload);
-
- shepherdElectrumSplitUtxoPromise(payload)
- .then((res) => {
- console.warn(res);
-
- if (res.msg === 'success') {
- const _coin = this.state.utxoMergeCoin.split('|');
-
- shepherdCliPromise(
- null,
- _coin[0],
- 'sendrawtransaction',
- [res.result]
- )
- .then((res) => {
- console.warn(res);
-
- if (!res.error) {
- this.setState({
- utxoMergePushResult: res.result,
- });
- Store.dispatch(
- triggerToaster(
- 'Merge success',
- 'UTXO',
- 'success'
- )
- );
- } else {
- Store.dispatch(
- triggerToaster(
- res.result,
- 'Split UTXO error',
- 'error'
- )
- );
- }
- });
- } else {
- Store.dispatch(
- triggerToaster(
- res.result,
- 'Split UTXO error',
- 'error'
- )
- );
- }
- });
- }
-
- splitUtxo() {
- let largestUTXO = { amount: 0 };
-
- for (let i = 0; i < this.state.utxoSplitList.length; i++) {
- if (Number(this.state.utxoSplitList[i].amount) > Number(largestUTXO.amount)) {
- largestUTXO = this.state.utxoSplitList[i];
- }
- }
-
- console.warn(`largest utxo ${largestUTXO.amount}`);
- console.warn(`largest utxo ${largestUTXO.amount}`);
-
- const utxoSize = largestUTXO.amount;
- const targetSizes = this.state.utxoSplitPairs.split(',');
- const wif = this.state.utxoSplitWif;
- const address = this.state.utxoSplitAddress;
- const pairsCount = this.state.utxoSplitPairsCount;
- let totalOutSize = 0;
- let _targets = [];
-
- console.warn(`total utxos ${pairsCount * targetSizes.length}`);
- console.warn(`total pairs ${pairsCount}`);
- console.warn(`utxo size ${utxoSize}`);
- console.warn(`utxo sizes`);
- console.warn(targetSizes);
-
- for (let i = 0; i < pairsCount; i++) {
- for (let j = 0; j < targetSizes.length; j++) {
- console.warn(`vout ${_targets.length} ${targetSizes[j]}`);
- _targets.push(Number(targetSizes[j]) * 100000000);
- totalOutSize += Number(targetSizes[j]);
- }
- }
-
- console.warn(`total out size ${totalOutSize}`);
- console.warn(`change ${Math.floor(Number(utxoSize - totalOutSize)) - 0.0001 + (largestUTXO.interest)}`);
-
- const payload = {
- wif,
- network: 'komodo',
- targets: _targets,
- utxo: [largestUTXO],
- changeAddress: address,
- outputAddress: address,
- change: Math.floor(Number(utxoSize - totalOutSize) * 100000000) - 10000 + (largestUTXO.interest * 100000000), // 10k sat fee
- };
-
- console.warn(payload);
- console.warn(largestUTXO);
-
- shepherdElectrumSplitUtxoPromise(payload)
- .then((res) => {
- console.warn(res);
-
- if (res.msg === 'success') {
- const _coin = this.state.utxoSplitCoin.split('|');
-
- shepherdCliPromise(
- null,
- _coin[0],
- 'sendrawtransaction',
- [res.result]
- )
- .then((res) => {
- console.warn(res);
-
- if (!res.error) {
- this.setState({
- utxoSplitPushResult: res.result,
- });
- Store.dispatch(
- triggerToaster(
- 'Split success',
- 'UTXO',
- 'success'
- )
- );
- } else {
- Store.dispatch(
- triggerToaster(
- res.result,
- 'Split UTXO error',
- 'error'
- )
- );
- }
- });
- } else {
- Store.dispatch(
- triggerToaster(
- res.result,
- 'Split UTXO error',
- 'error'
- )
- );
- }
- });
- }
-
- getUtxoSplit() {
- const _coin = this.state.utxoSplitCoin.split('|');
-
- shepherdToolsSeedToWif(
- this.state.utxoSplitSeed,
- 'KMD',
- true
- )
- .then((seed2kpRes) => {
- if (seed2kpRes.msg === 'success') {
- shepherdCliPromise(null, _coin[0], 'listunspent')
- .then((res) => {
- // console.warn(res);
-
- if (!res.error) {
- const _utxoList = res.result;
- let largestUTXO = 0;
-
- if (_utxoList &&
- _utxoList.length) {
- let _mineUtxo = [];
-
- for (let i = 0; i < _utxoList.length; i++) {
- if (_utxoList[i].spendable &&
- seed2kpRes.result.keys.pub === _utxoList[i].address) {
- _mineUtxo.push(_utxoList[i]);
- }
- }
-
- for (let i = 0; i < _mineUtxo.length; i++) {
- if (Number(_mineUtxo[i].amount) > Number(largestUTXO)) {
- largestUTXO = _mineUtxo[i].amount;
- }
- }
-
- this.setState({
- utxoSplitList: _mineUtxo,
- utxoSplitLargestUtxo: largestUTXO,
- utxoSplitAddress: seed2kpRes.result.keys.pub,
- utxoSplitWif: seed2kpRes.result.keys.priv,
- });
- } else {
- Store.dispatch(
- triggerToaster(
- res.result,
- 'Split UTXO error',
- 'error'
- )
- );
- }
- } else {
- Store.dispatch(
- triggerToaster(
- res.result,
- 'Get UTXO error',
- 'error'
- )
- );
- }
- });
- } else {
- Store.dispatch(
- triggerToaster(
- seed2kpRes.result,
- 'Seed to wif error',
- 'error'
- )
- );
- }
- });
- }
-
- getUtxoMerge() {
- const _coin = this.state.utxoMergeCoin.split('|');
-
- shepherdToolsSeedToWif(
- this.state.utxoMergeSeed,
- 'KMD',
- true
- )
- .then((seed2kpRes) => {
- if (seed2kpRes.msg === 'success') {
- shepherdCliPromise(null, _coin[0], 'listunspent')
- .then((res) => {
- // console.warn(res);
-
- if (!res.error) {
- const _utxoList = res.result;
- let largestUTXO = 0;
-
- if (_utxoList &&
- _utxoList.length) {
- let _mineUtxo = [];
-
- for (let i = 0; i < _utxoList.length; i++) {
- if (_utxoList[i].spendable &&
- seed2kpRes.result.keys.pub === _utxoList[i].address) {
- _mineUtxo.push(_utxoList[i]);
- }
- }
-
- this.setState({
- utxoMergeList: _mineUtxo,
- utxoMergeAddress: seed2kpRes.result.keys.pub,
- utxoMergeWif: seed2kpRes.result.keys.priv,
- utxoMergeUtxoNum: _mineUtxo.length,
- });
- } else {
- Store.dispatch(
- triggerToaster(
- 'Utxo Merge Error',
- 'No valid UTXO',
- 'error'
- )
- );
- }
- } else {
- Store.dispatch(
- triggerToaster(
- res.result,
- 'Get UTXO error',
- 'error'
- )
- );
- }
- });
- } else {
- Store.dispatch(
- triggerToaster(
- seed2kpRes.result,
- 'Seed to wif error',
- 'error'
- )
- );
- }
- });
- }
-
- getUtxos() {
- const _coin = this.state.utxoCoin.split('|');
-
- shepherdElectrumListunspent(_coin[0], this.state.utxoAddr)
- .then((res) => {
- if (res.msg === 'success') {
- this.setState({
- utxoResult: res.result,
- });
- } else {
- Store.dispatch(
- triggerToaster(
- res.result,
- 'Get UTXO error',
- 'error'
- )
- );
- }
- });
- }
-
- getBalanceAlt() {
- const _coin = this.state.balanceCoin.split('|');
-
- shepherdToolsBalance(_coin[0], this.state.balanceAddr)
- .then((res) => {
- if (res.msg === 'success') {
- this.setState({
- balanceResult: res.result,
- });
- } else {
- Store.dispatch(
- triggerToaster(
- res.result,
- 'Get balance error',
- 'error'
- )
- );
- }
- });
- }
-
- wif2wif() {
- const _coin = this.state.w2wCoin.split('|');
-
- shepherdToolsWifToKP(_coin[0], this.state.w2wWif)
- .then((res) => {
- // console.warn(res);
-
- if (res.msg === 'success') {
- this.setState({
- w2wResult: res.result,
- });
- } else {
- Store.dispatch(
- triggerToaster(
- res.result,
- 'Seed to wif error',
- 'error'
- )
- );
- }
- });
- }
-
- seed2Wif() {
- const _coin = this.state.s2wCoin.split('|');
-
- shepherdToolsSeedToWif(
- this.state.s2wSeed,
- _coin[0],
- this.state.s2wisIguana
- )
- .then((res) => {
- // console.warn(res);
-
- if (res.msg === 'success') {
- this.setState({
- s2wResult: res.result,
- });
- } else {
- Store.dispatch(
- triggerToaster(
- res.result,
- 'Seed to wif error',
- 'error'
- )
- );
- }
- });
- }
-
- sendTx(rawTx2Push) {
- let _txData = rawTx2Push.split(':');
-
- // console.warn(_txData);
-
- shepherdToolsPushTx(_txData[0], _txData[1])
- .then((res) => {
- // console.warn(res);
-
- this.setState({
- txPushResult: res.result,
- rawTx2Push,
- });
- });
- }
-
- getBalance() {
- const _coin = this.state.selectedCoin.split('|');
-
- shepherdToolsBalance(_coin[0], this.state.sendFrom)
- .then((res) => {
- if (res.msg === 'success') {
- this.setState({
- balance: res.result,
- });
- } else {
- Store.dispatch(
- triggerToaster(
- res.result,
- 'Offline tx signing',
- 'error'
- )
- );
- }
- });
- }
-
- getUnsignedTx() {
- const _coin = this.state.selectedCoin.split('|');
-
- shepherdToolsBuildUnsigned(_coin[0], this.state.amount * 100000000, this.state.sendTo, this.state.sendFrom)
- .then((res) => {
- // console.warn(res);
-
- if (res.msg === 'success') {
- let tx2qr = 'agtx:';
- res = res.result;
-
- tx2qr += (res.network === 'komodo' ? 'KMD' : res.network) + ':' + res.outputAddress + ':' + res.changeAddress + ':' + res.value + ':' + res.change + ':u:';
-
- for (let i = 0; i < res.utxoSet.length; i++) {
- tx2qr += res.utxoSet[i].txid + ':' + res.utxoSet[i].value + ':' + res.utxoSet[i].vout + (i === res.utxoSet.length -1 ? '' : '-');
- }
-
- // console.warn(tx2qr);
- // console.warn('txqr length', tx2qr.length);
-
- // max 350 chars
-
- this.setState({
- tx2qr,
- utxo: res.utxoSet,
- });
- } else {
- Store.dispatch(
- triggerToaster(
- res.result,
- 'Offline tx signing',
- 'error'
- )
- );
- }
- });
- }
-
- closeQr() {
- this.setState({
- tx2qr: null,
- });
- }
-
- renderCoinOption(option) {
- return (
-
-
-
{ option.label }
-
- );
- }
-
- updateSelectedCoin(e, propName) {
- if (e &&
- e.value &&
- e.value.indexOf('|')) {
- this.setState({
- [propName]: e.value,
- });
- }
- }
-
- updateInput(e) {
- this.setState({
- [e.target.name]: e.target.value,
- });
}
setActiveSection(section) {
@@ -634,134 +25,6 @@ class Tools extends React.Component {
});
}
- toggleS2wIsIguana() {
- this.setState({
- s2wisIguana: !this.state.s2wisIguana,
- });
- }
-
- openExplorerWindow(txid, coin) {
- const url = `http://${coin}.explorer.supernet.org/tx/${txid}`;
- const remote = window.require('electron').remote;
- const BrowserWindow = remote.BrowserWindow;
-
- const externalWindow = new BrowserWindow({
- width: 1280,
- height: 800,
- title: `${translate('INDEX.LOADING')}...`,
- icon: remote.getCurrentWindow().iguanaIcon,
- webPreferences: {
- nodeIntegration: false,
- },
- });
-
- externalWindow.loadURL(url);
- externalWindow.webContents.on('did-finish-load', () => {
- setTimeout(() => {
- externalWindow.show();
- }, 40);
- });
- }
-
- renderUTXOResponse() {
- const _utxos = this.state.utxoResult;
- const _coin = this.state.utxoCoin.split('|');
- let _items = [];
-
- if (_utxos &&
- _utxos.length) {
- for (let i = 0; i < _utxos.length; i++) {
- _items.push(
-
- { _utxos[i].amount } |
- { _utxos[i].confirmations } |
- { _utxos[i].vout } |
- { _coin[0] === 'KMD' &&
- { _utxos[i].locktime } |
- }
- { _utxos[i].txid } |
-
- );
- }
- }
-
- return (
-
-
-
- Amount |
- Confirmations |
- Vout |
- { _coin[0] === 'KMD' &&
- Locktime |
- }
- TxID |
-
-
-
- { _items }
-
-
-
- Amount |
- Confirmations |
- Vout |
- { _coin[0] === 'KMD' &&
- Locktime |
- }
- TxID |
-
-
-
- );
- }
-
- renderUTXOSplitMergeResponse(type) {
- const _utxos = type === 'merge' ? this.state.utxoMergeList : this.state.utxoSplitList;
- let _items = [];
-
- if (_utxos &&
- _utxos.length) {
- for (let i = 0; i < _utxos.length; i++) {
- _items.push(
-
- { _utxos[i].amount } |
- { _utxos[i].address } |
- { _utxos[i].confirmations } |
- { _utxos[i].vout } |
- { _utxos[i].txid } |
-
- );
- }
- }
-
- return (
-
-
-
- Amount |
- Address |
- Confirmations |
- Vout |
- TxID |
-
-
-
- { _items }
-
-
-
- Amount |
- Address |
- Confirmations |
- Vout |
- TxID |
-
-
-
- );
- }
-
render() {
return (
@@ -828,680 +91,31 @@ class Tools extends React.Component {
{ this.state.activeSection === 'offlinesig-create' &&
-
-
-
Offline Transaction Signing
-
-
-
-
-
-
-
-
-
-
- { this.state.balance &&
-
- }
-
-
-
-
-
-
-
-
-
-
-
-
- { this.state.tx2qr &&
-
-
-
-
- }
- { this.state.tx2qr &&
-
-
- { this.state.utxo.length > 3 &&
-
cant encode a qr tx larger than 3 utxos!
- }
-
- }
- { this.state.tx2qr &&
- this.state.utxo.length < 4 &&
-
- }
-
+
}
{ this.state.activeSection === 'offlinesig-scan' &&
-
-
-
Push QR transaction
-
-
-
-
- { this.state.rawTx2Push &&
-
-
-
- }
- { this.state.txPushResult &&
-
- { this.state.txPushResult.length === 64 &&
-
-
- { this.state.rawTx2Push.split(':')[0].toUpperCase() } transaction pushed!
-
-
TxID { this.state.txPushResult }
-
- }
- { this.state.txPushResult.length !== 64 &&
-
Error: { this.state.txPushResult }
- }
-
- }
-
+
}
{ this.state.activeSection === 'string2qr' &&
-
-
-
String to QR
-
-
-
-
- { this.state.string2qr &&
-
-
-
- }
-
+
}
{ this.state.activeSection === 'balance' &&
-
-
-
Get balance
-
-
-
-
-
-
-
-
-
-
-
- { this.state.balanceResult &&
-
-
- Balance (confirmed): { this.state.balanceResult.balance }
-
-
- Balance (unconfirmed): { this.state.balanceResult.unconfirmed }
-
-
- }
-
+
}
{ this.state.activeSection === 'utxo' &&
-
-
-
Get UTXO list
-
-
-
-
-
-
-
-
-
-
-
- { this.state.utxoResult &&
-
- { this.renderUTXOResponse() }
-
- }
-
+
}
{ this.state.activeSection === 'wif2wif' &&
-
-
-
WIF to key pair (wif, pub)
-
-
-
-
-
-
-
-
-
-
-
- { this.state.w2wResult &&
-
-
- WIF: { this.state.w2wResult.keys.priv }
-
-
- Pub: { this.state.w2wResult.keys.pub }
-
-
- }
-
+
}
{ this.state.activeSection === 'seed2kp' &&
-
-
-
Seed to key pair (wif, pub)
-
-
-
-
-
-
-
-
-
-
-
- Iguana Core compatible
-
-
-
-
-
- { this.state.s2wResult &&
-
-
- WIF: { this.state.s2wResult.keys.priv }
-
-
- Pub: { this.state.s2wResult.keys.pub }
-
-
- }
-
+
}
{ this.state.activeSection === 'utxo-split' &&
-
-
-
Split UTXO
-
-
-
-
-
-
-
-
- { this.state.utxoSplitAddress &&
-
- Pub: { this.state.utxoSplitAddress }
-
- }
- { this.state.utxoSplitAddress &&
-
- WIF: { this.state.utxoSplitWif }
-
- }
-
-
-
- { this.state.utxoSplitList &&
-
- { /*this.renderUTXOSplitResponse()*/ }
-
Total UTXO: { this.state.utxoSplitList.length }
-
Largest UTXO: { this.state.utxoSplitLargestUtxo }
-
- }
-
-
-
- Show UTXO list
-
-
- { this.state.utxoSplitShowUtxoList &&
-
- { this.renderUTXOSplitMergeResponse('split') }
-
- }
-
-
-
-
-
-
-
-
-
-
-
-
- { this.state.splitUtxoApproximateVal &&
-
- Total out size: { this.state.splitUtxoApproximateVal }
-
- }
- {
- /*this.state.utxoSplitRawtx &&
-
- Rawtx:
{ this.state.utxoSplitRawtx }
-
*/
- }
- { this.state.utxoSplitPushResult &&
-
- TXID:
{ this.state.utxoSplitPushResult }
- { isKomodoCoin(this.state.utxoSplitCoin.split('|')[0]) &&
-
-
-
- }
-
- }
-
+
}
{ this.state.activeSection === 'utxo-merge' &&
-
-
-
Merge UTXO
-
-
-
-
-
-
-
-
- { this.state.utxoMergeAddress &&
-
- Pub: { this.state.utxoMergeAddress }
-
- }
- { this.state.utxoMergeAddress &&
-
- WIF: { this.state.utxoMergeWif }
-
- }
-
-
-
- { this.state.utxoMergeList &&
-
-
Total UTXO: { this.state.utxoMergeList.length }
-
- }
-
-
-
- Show UTXO list
-
-
- { this.state.utxoMergeShowUtxoList &&
-
- { this.renderUTXOSplitMergeResponse('merge') }
-
- }
-
-
-
-
-
-
-
- { /*this.state.utxoMergeRawtx &&
-
- Rawtx:
{ this.state.utxoMergeRawtx }
-
*/
- }
- { this.state.utxoMergePushResult &&
-
- TXID:
{ this.state.utxoMergePushResult }
- { isKomodoCoin(this.state.utxoMergeCoin.split('|')[0]) &&
-
-
-
- }
-
- }
-
+
}
diff --git a/react/src/components/dashboard/tools/toolsGetBalance.js b/react/src/components/dashboard/tools/toolsGetBalance.js
new file mode 100644
index 0000000..359cc09
--- /dev/null
+++ b/react/src/components/dashboard/tools/toolsGetBalance.js
@@ -0,0 +1,138 @@
+import React from 'react';
+import { translate } from '../../../translate/translate';
+import addCoinOptionsCrypto from '../../addcoin/addcoinOptionsCrypto';
+import addCoinOptionsAC from '../../addcoin/addcoinOptionsAC';
+import Select from 'react-select';
+import {
+ triggerToaster,
+ shepherdToolsBalance,
+ shepherdToolsBuildUnsigned,
+ shepherdToolsPushTx,
+ shepherdToolsSeedToWif,
+ shepherdToolsWifToKP,
+ shepherdElectrumListunspent,
+ shepherdCliPromise,
+ shepherdElectrumSplitUtxoPromise,
+} from '../../../actions/actionCreators';
+import Store from '../../../store';
+
+class ToolsGetBalance extends React.Component {
+ constructor() {
+ super();
+ this.state = {
+ balanceAddr: '',
+ balanceCoin: '',
+ balanceResult: null,
+ };
+ this.updateInput = this.updateInput.bind(this);
+ this.updateSelectedCoin = this.updateSelectedCoin.bind(this);
+ this.getBalanceAlt = this.getBalanceAlt.bind(this);
+ }
+
+ getBalanceAlt() {
+ const _coin = this.state.balanceCoin.split('|');
+
+ shepherdToolsBalance(_coin[0], this.state.balanceAddr)
+ .then((res) => {
+ if (res.msg === 'success') {
+ this.setState({
+ balanceResult: res.result,
+ });
+ } else {
+ Store.dispatch(
+ triggerToaster(
+ res.result,
+ 'Get balance error',
+ 'error'
+ )
+ );
+ }
+ });
+ }
+
+ renderCoinOption(option) {
+ return (
+
+
+
{ option.label }
+
+ );
+ }
+
+ updateSelectedCoin(e, propName) {
+ if (e &&
+ e.value &&
+ e.value.indexOf('|')) {
+ this.setState({
+ [propName]: e.value,
+ });
+ }
+ }
+
+ updateInput(e) {
+ this.setState({
+ [e.target.name]: e.target.value,
+ });
+ }
+
+ render() {
+ return (
+
+
+
Get balance
+
+
+
+
+
+
+
+
+
+
+
+ { this.state.balanceResult &&
+
+
+ Balance (confirmed): { this.state.balanceResult.balance }
+
+
+ Balance (unconfirmed): { this.state.balanceResult.unconfirmed }
+
+
+ }
+
+ );
+ }
+}
+
+export default ToolsGetBalance;
\ No newline at end of file
diff --git a/react/src/components/dashboard/tools/toolsGetUtxos.js b/react/src/components/dashboard/tools/toolsGetUtxos.js
new file mode 100644
index 0000000..f065815
--- /dev/null
+++ b/react/src/components/dashboard/tools/toolsGetUtxos.js
@@ -0,0 +1,186 @@
+import React from 'react';
+import { translate } from '../../../translate/translate';
+import addCoinOptionsCrypto from '../../addcoin/addcoinOptionsCrypto';
+import addCoinOptionsAC from '../../addcoin/addcoinOptionsAC';
+import Select from 'react-select';
+import {
+ triggerToaster,
+ shepherdToolsBalance,
+ shepherdToolsBuildUnsigned,
+ shepherdToolsPushTx,
+ shepherdToolsSeedToWif,
+ shepherdToolsWifToKP,
+ shepherdElectrumListunspent,
+ shepherdCliPromise,
+ shepherdElectrumSplitUtxoPromise,
+} from '../../../actions/actionCreators';
+import Store from '../../../store';
+
+class ToolsGetUtxos extends React.Component {
+ constructor() {
+ super();
+ this.state = {
+ utxoAddr: '',
+ utxoCoin: '',
+ utxoResult: null,
+ };
+ this.updateInput = this.updateInput.bind(this);
+ this.updateSelectedCoin = this.updateSelectedCoin.bind(this);
+ this.getUtxos = this.getUtxos.bind(this);
+ }
+
+ getUtxos() {
+ const _coin = this.state.utxoCoin.split('|');
+
+ shepherdElectrumListunspent(_coin[0], this.state.utxoAddr)
+ .then((res) => {
+ if (res.msg === 'success') {
+ this.setState({
+ utxoResult: res.result,
+ });
+ } else {
+ Store.dispatch(
+ triggerToaster(
+ res.result,
+ 'Get UTXO error',
+ 'error'
+ )
+ );
+ }
+ });
+ }
+
+ renderUTXOResponse() {
+ const _utxos = this.state.utxoResult;
+ const _coin = this.state.utxoCoin.split('|');
+ let _items = [];
+
+ if (_utxos &&
+ _utxos.length) {
+ for (let i = 0; i < _utxos.length; i++) {
+ _items.push(
+
+ { _utxos[i].amount } |
+ { _utxos[i].confirmations } |
+ { _utxos[i].vout } |
+ { _coin[0] === 'KMD' &&
+ { _utxos[i].locktime } |
+ }
+ { _utxos[i].txid } |
+
+ );
+ }
+ }
+
+ return (
+
+
+
+ Amount |
+ Confirmations |
+ Vout |
+ { _coin[0] === 'KMD' &&
+ Locktime |
+ }
+ TxID |
+
+
+
+ { _items }
+
+
+
+ Amount |
+ Confirmations |
+ Vout |
+ { _coin[0] === 'KMD' &&
+ Locktime |
+ }
+ TxID |
+
+
+
+ );
+ }
+
+ renderCoinOption(option) {
+ return (
+
+
+
{ option.label }
+
+ );
+ }
+
+ updateSelectedCoin(e, propName) {
+ if (e &&
+ e.value &&
+ e.value.indexOf('|')) {
+ this.setState({
+ [propName]: e.value,
+ });
+ }
+ }
+
+ updateInput(e) {
+ this.setState({
+ [e.target.name]: e.target.value,
+ });
+ }
+
+ render() {
+ return (
+
+
+
Get UTXO list
+
+
+
+
+
+
+
+
+
+
+
+ { this.state.utxoResult &&
+
+ { this.renderUTXOResponse() }
+
+ }
+
+ );
+ }
+}
+
+export default ToolsGetUtxos;
\ No newline at end of file
diff --git a/react/src/components/dashboard/tools/toolsMergeUtxo.js b/react/src/components/dashboard/tools/toolsMergeUtxo.js
new file mode 100644
index 0000000..92f0c85
--- /dev/null
+++ b/react/src/components/dashboard/tools/toolsMergeUtxo.js
@@ -0,0 +1,424 @@
+import React from 'react';
+import { translate } from '../../../translate/translate';
+import addCoinOptionsCrypto from '../../addcoin/addcoinOptionsCrypto';
+import addCoinOptionsAC from '../../addcoin/addcoinOptionsAC';
+import Select from 'react-select';
+import {
+ triggerToaster,
+ shepherdToolsBalance,
+ shepherdToolsBuildUnsigned,
+ shepherdToolsPushTx,
+ shepherdToolsSeedToWif,
+ shepherdToolsWifToKP,
+ shepherdElectrumListunspent,
+ shepherdCliPromise,
+ shepherdElectrumSplitUtxoPromise,
+} from '../../../actions/actionCreators';
+import Store from '../../../store';
+import { isKomodoCoin } from '../../../util/coinHelper';
+import devlog from '../../../util/devlog';
+
+class ToolsMergeUTXO extends React.Component {
+ constructor() {
+ super();
+ this.state = {
+ utxoMergeAddress: null,
+ utxoMergeWif: null,
+ utxoMergeSeed: '',
+ utxoMergeCoin: '',
+ utxoMergeUtxoNum: 10,
+ utxoMergeRawtx: null,
+ utxoMergeList: null,
+ utxoMergePushResult: null,
+ utxoMergeShowUtxoList: false,
+ };
+ this.updateInput = this.updateInput.bind(this);
+ this.updateSelectedCoin = this.updateSelectedCoin.bind(this);
+ this.getUtxoMerge = this.getUtxoMerge.bind(this);
+ this.mergeUtxo = this.mergeUtxo.bind(this);
+ this.toggleMergeUtxoList = this.toggleMergeUtxoList.bind(this);
+ }
+
+ toggleMergeUtxoList() {
+ this.setState({
+ utxoMergeShowUtxoList: !this.state.utxoMergeShowUtxoList,
+ });
+ }
+
+ mergeUtxo() {
+ const wif = this.state.utxoMergeWif;
+ const address = this.state.utxoMergeAddress;
+ const utxoNum = this.state.utxoMergeUtxoNum;
+ let totalOutSize = 0;
+ let _utxos = [];
+ let _interest = 0;
+
+ for (let i = 0; i < utxoNum; i++) {
+ devlog(`vout ${i} ${this.state.utxoMergeList[i].amount}`);
+ _utxos.push(JSON.parse(JSON.stringify(this.state.utxoMergeList[i])));
+ totalOutSize += Number(this.state.utxoMergeList[i].amount);
+ }
+
+ for (let i = 0; i < _utxos.length; i++) {
+ _utxos[i].amount = Number(_utxos[i].amount) * 100000000;
+ _utxos[i].interest = Number(_utxos[i].interest) * 100000000;
+ _interest += (_utxos[i].interest ? _utxos[i].interest : 0);
+ }
+
+ devlog(`total out size ${totalOutSize}`);
+ devlog(`interest ${_interest}`);
+
+ const payload = {
+ wif,
+ network: 'komodo',
+ targets: [Math.floor(totalOutSize * 100000000) - 10000 + _interest],
+ utxo: _utxos,
+ changeAddress: address,
+ outputAddress: address,
+ change: 0,
+ };
+
+ console.log(payload);
+
+ shepherdElectrumSplitUtxoPromise(payload)
+ .then((res) => {
+ devlog(res);
+
+ if (res.msg === 'success') {
+ const _coin = this.state.utxoMergeCoin.split('|');
+
+ shepherdCliPromise(
+ null,
+ _coin[0],
+ 'sendrawtransaction',
+ [res.result]
+ )
+ .then((res) => {
+ devlog(res);
+
+ if (!res.error) {
+ this.setState({
+ utxoMergePushResult: res.result,
+ });
+ Store.dispatch(
+ triggerToaster(
+ 'Merge success',
+ 'UTXO',
+ 'success'
+ )
+ );
+ } else {
+ Store.dispatch(
+ triggerToaster(
+ res.result,
+ 'Merge UTXO error',
+ 'error'
+ )
+ );
+ }
+ });
+ } else {
+ Store.dispatch(
+ triggerToaster(
+ res.result,
+ 'Merge UTXO error',
+ 'error'
+ )
+ );
+ }
+ });
+ }
+
+ getUtxoMerge() {
+ const _coin = this.state.utxoMergeCoin.split('|');
+
+ shepherdToolsSeedToWif(
+ this.state.utxoMergeSeed,
+ 'KMD',
+ true
+ )
+ .then((seed2kpRes) => {
+ if (seed2kpRes.msg === 'success') {
+ shepherdCliPromise(null, _coin[0], 'listunspent')
+ .then((res) => {
+ // devlog(res);
+
+ if (!res.error) {
+ const _utxoList = res.result;
+ let largestUTXO = 0;
+
+ if (_utxoList &&
+ _utxoList.length) {
+ let _mineUtxo = [];
+
+ for (let i = 0; i < _utxoList.length; i++) {
+ if (_utxoList[i].spendable &&
+ seed2kpRes.result.keys.pub === _utxoList[i].address) {
+ _mineUtxo.push(_utxoList[i]);
+ }
+ }
+
+ this.setState({
+ utxoMergeList: _mineUtxo,
+ utxoMergeAddress: seed2kpRes.result.keys.pub,
+ utxoMergeWif: seed2kpRes.result.keys.priv,
+ utxoMergeUtxoNum: _mineUtxo.length,
+ });
+ } else {
+ Store.dispatch(
+ triggerToaster(
+ 'UTXO merge Error',
+ 'No valid UTXO',
+ 'error'
+ )
+ );
+ }
+ } else {
+ Store.dispatch(
+ triggerToaster(
+ res.result,
+ 'Get UTXO error',
+ 'error'
+ )
+ );
+ }
+ });
+ } else {
+ Store.dispatch(
+ triggerToaster(
+ seed2kpRes.result,
+ 'Seed to wif error',
+ 'error'
+ )
+ );
+ }
+ });
+ }
+
+ renderCoinOption(option) {
+ return (
+
+
+
{ option.label }
+
+ );
+ }
+
+ updateSelectedCoin(e, propName) {
+ if (e &&
+ e.value &&
+ e.value.indexOf('|')) {
+ this.setState({
+ [propName]: e.value,
+ });
+ }
+ }
+
+ updateInput(e) {
+ this.setState({
+ [e.target.name]: e.target.value,
+ });
+ }
+
+ openExplorerWindow(txid, coin) {
+ const url = `http://${coin}.explorer.supernet.org/tx/${txid}`;
+ const remote = window.require('electron').remote;
+ const BrowserWindow = remote.BrowserWindow;
+
+ const externalWindow = new BrowserWindow({
+ width: 1280,
+ height: 800,
+ title: `${translate('INDEX.LOADING')}...`,
+ icon: remote.getCurrentWindow().iguanaIcon,
+ webPreferences: {
+ nodeIntegration: false,
+ },
+ });
+
+ externalWindow.loadURL(url);
+ externalWindow.webContents.on('did-finish-load', () => {
+ setTimeout(() => {
+ externalWindow.show();
+ }, 40);
+ });
+ }
+
+ renderUTXOSplitMergeResponse(type) {
+ const _utxos = type === 'merge' ? this.state.utxoMergeList : this.state.utxoSplitList;
+ let _items = [];
+
+ if (_utxos &&
+ _utxos.length) {
+ for (let i = 0; i < _utxos.length; i++) {
+ _items.push(
+
+ { _utxos[i].amount } |
+ { _utxos[i].address } |
+ { _utxos[i].confirmations } |
+ { _utxos[i].vout } |
+ { _utxos[i].txid } |
+
+ );
+ }
+ }
+
+ return (
+
+
+
+ Amount |
+ Address |
+ Confirmations |
+ Vout |
+ TxID |
+
+
+
+ { _items }
+
+
+
+ Amount |
+ Address |
+ Confirmations |
+ Vout |
+ TxID |
+
+
+
+ );
+ }
+
+ render() {
+ return (
+
+
+
Merge UTXO
+
+
+
+
+
+
+
+
+ { this.state.utxoMergeAddress &&
+
+ Pub: { this.state.utxoMergeAddress }
+
+ }
+ { this.state.utxoMergeAddress &&
+
+ WIF: { this.state.utxoMergeWif }
+
+ }
+
+
+
+ { this.state.utxoMergeList &&
+
+
Total UTXO: { this.state.utxoMergeList.length }
+
+ }
+
+
+
+ Show UTXO list
+
+
+ { this.state.utxoMergeShowUtxoList &&
+
+ { this.renderUTXOSplitMergeResponse('merge') }
+
+ }
+
+
+
+
+
+
+
+ { /*this.state.utxoMergeRawtx &&
+
+ Rawtx:
{ this.state.utxoMergeRawtx }
+
*/
+ }
+ { this.state.utxoMergePushResult &&
+
+ TXID:
{ this.state.utxoMergePushResult }
+ { isKomodoCoin(this.state.utxoMergeCoin.split('|')[0]) &&
+
+
+
+ }
+
+ }
+
+ );
+ }
+}
+
+export default ToolsMergeUTXO;
\ No newline at end of file
diff --git a/react/src/components/dashboard/tools/toolsOfflineSigCreate.js b/react/src/components/dashboard/tools/toolsOfflineSigCreate.js
new file mode 100644
index 0000000..2796566
--- /dev/null
+++ b/react/src/components/dashboard/tools/toolsOfflineSigCreate.js
@@ -0,0 +1,263 @@
+import React from 'react';
+import { translate } from '../../../translate/translate';
+import addCoinOptionsCrypto from '../../addcoin/addcoinOptionsCrypto';
+import addCoinOptionsAC from '../../addcoin/addcoinOptionsAC';
+import Select from 'react-select';
+import {
+ triggerToaster,
+ shepherdToolsBalance,
+ shepherdToolsBuildUnsigned,
+ shepherdToolsPushTx,
+ shepherdToolsSeedToWif,
+ shepherdToolsWifToKP,
+ shepherdElectrumListunspent,
+ shepherdCliPromise,
+ shepherdElectrumSplitUtxoPromise,
+} from '../../../actions/actionCreators';
+import Store from '../../../store';
+import QRCode from 'qrcode.react';
+import QRModal from '../qrModal/qrModal';
+
+class ToolsOfflineSigCreate extends React.Component {
+ constructor() {
+ super();
+ this.state = {
+ sendFrom: '',
+ sendTo: '',
+ amount: 0,
+ selectedCoin: '',
+ balance: null,
+ tx2qr: null,
+ utxo: null,
+ rawTx2Push: null,
+ txPushResult: null,
+ };
+ this.updateInput = this.updateInput.bind(this);
+ this.updateSelectedCoin = this.updateSelectedCoin.bind(this);
+ this.getBalance = this.getBalance.bind(this);
+ this.getUnsignedTx = this.getUnsignedTx.bind(this);
+ this.closeQr = this.closeQr.bind(this);
+ }
+
+ getBalance() {
+ const _coin = this.state.selectedCoin.split('|');
+
+ shepherdToolsBalance(_coin[0], this.state.sendFrom)
+ .then((res) => {
+ if (res.msg === 'success') {
+ this.setState({
+ balance: res.result,
+ });
+ } else {
+ Store.dispatch(
+ triggerToaster(
+ res.result,
+ 'Offline tx signing',
+ 'error'
+ )
+ );
+ }
+ });
+ }
+
+ getUnsignedTx() {
+ const _coin = this.state.selectedCoin.split('|');
+
+ shepherdToolsBuildUnsigned(_coin[0], this.state.amount * 100000000, this.state.sendTo, this.state.sendFrom)
+ .then((res) => {
+ // console.warn(res);
+
+ if (res.msg === 'success') {
+ let tx2qr = 'agtx:';
+ res = res.result;
+
+ tx2qr += (res.network === 'komodo' ? 'KMD' : res.network) + ':' + res.outputAddress + ':' + res.changeAddress + ':' + res.value + ':' + res.change + ':u:';
+
+ for (let i = 0; i < res.utxoSet.length; i++) {
+ tx2qr += res.utxoSet[i].txid + ':' + res.utxoSet[i].value + ':' + res.utxoSet[i].vout + (i === res.utxoSet.length -1 ? '' : '-');
+ }
+
+ // console.warn(tx2qr);
+ // console.warn('txqr length', tx2qr.length);
+
+ // max 350 chars
+
+ this.setState({
+ tx2qr,
+ utxo: res.utxoSet,
+ });
+ } else {
+ Store.dispatch(
+ triggerToaster(
+ res.result,
+ 'Offline tx signing',
+ 'error'
+ )
+ );
+ }
+ });
+ }
+
+ closeQr() {
+ this.setState({
+ tx2qr: null,
+ });
+ }
+
+ renderCoinOption(option) {
+ return (
+
+
+
{ option.label }
+
+ );
+ }
+
+ updateSelectedCoin(e, propName) {
+ if (e &&
+ e.value &&
+ e.value.indexOf('|')) {
+ this.setState({
+ [propName]: e.value,
+ });
+ }
+ }
+
+ updateInput(e) {
+ this.setState({
+ [e.target.name]: e.target.value,
+ });
+ }
+
+ render() {
+ return (
+
+
+
Offline Transaction Signing
+
+
+
+
+
+
+
+
+
+
+ { this.state.balance &&
+
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+ { this.state.tx2qr &&
+
+
+
+
+ }
+ { this.state.tx2qr &&
+
+
+ { this.state.utxo.length > 3 &&
+
cant encode a qr tx larger than 3 utxos!
+ }
+
+ }
+ { this.state.tx2qr &&
+ this.state.utxo.length < 4 &&
+
+ }
+
+ );
+ }
+}
+
+export default ToolsOfflineSigCreate;
\ No newline at end of file
diff --git a/react/src/components/dashboard/tools/toolsOfflineSigScan.js b/react/src/components/dashboard/tools/toolsOfflineSigScan.js
new file mode 100644
index 0000000..5e998f8
--- /dev/null
+++ b/react/src/components/dashboard/tools/toolsOfflineSigScan.js
@@ -0,0 +1,125 @@
+import React from 'react';
+import { translate } from '../../../translate/translate';
+import addCoinOptionsCrypto from '../../addcoin/addcoinOptionsCrypto';
+import addCoinOptionsAC from '../../addcoin/addcoinOptionsAC';
+import Select from 'react-select';
+import {
+ triggerToaster,
+ shepherdToolsBalance,
+ shepherdToolsBuildUnsigned,
+ shepherdToolsPushTx,
+ shepherdToolsSeedToWif,
+ shepherdToolsWifToKP,
+ shepherdElectrumListunspent,
+ shepherdCliPromise,
+ shepherdElectrumSplitUtxoPromise,
+} from '../../../actions/actionCreators';
+import Store from '../../../store';
+import QRCode from 'qrcode.react';
+import QRModal from '../qrModal/qrModal';
+
+class ToolsOfflineSigScan extends React.Component {
+ constructor() {
+ super();
+ this.state = {
+ sendFrom: '',
+ sendTo: '',
+ amount: 0,
+ selectedCoin: '',
+ balance: null,
+ tx2qr: null,
+ utxo: null,
+ rawTx2Push: null,
+ txPushResult: null,
+ };
+ this.updateInput = this.updateInput.bind(this);
+ this.updateSelectedCoin = this.updateSelectedCoin.bind(this);
+ this.sendTx = this.sendTx.bind(this);
+ }
+
+ sendTx(rawTx2Push) {
+ let _txData = rawTx2Push.split(':');
+
+ // console.warn(_txData);
+
+ shepherdToolsPushTx(_txData[0], _txData[1])
+ .then((res) => {
+ // console.warn(res);
+
+ this.setState({
+ txPushResult: res.result,
+ rawTx2Push,
+ });
+ });
+ }
+
+ renderCoinOption(option) {
+ return (
+
+
+
{ option.label }
+
+ );
+ }
+
+ updateSelectedCoin(e, propName) {
+ if (e &&
+ e.value &&
+ e.value.indexOf('|')) {
+ this.setState({
+ [propName]: e.value,
+ });
+ }
+ }
+
+ updateInput(e) {
+ this.setState({
+ [e.target.name]: e.target.value,
+ });
+ }
+
+ render() {
+ return (
+
+
+
Push QR transaction
+
+
+
+
+ { this.state.rawTx2Push &&
+
+
+
+ }
+ { this.state.txPushResult &&
+
+ { this.state.txPushResult.length === 64 &&
+
+
+ { this.state.rawTx2Push.split(':')[0].toUpperCase() } transaction pushed!
+
+
TxID { this.state.txPushResult }
+
+ }
+ { this.state.txPushResult.length !== 64 &&
+
Error: { this.state.txPushResult }
+ }
+
+ }
+
+ );
+ }
+}
+
+export default ToolsOfflineSigScan;
\ No newline at end of file
diff --git a/react/src/components/dashboard/tools/toolsSeedToWif.js b/react/src/components/dashboard/tools/toolsSeedToWif.js
new file mode 100644
index 0000000..b6ce43b
--- /dev/null
+++ b/react/src/components/dashboard/tools/toolsSeedToWif.js
@@ -0,0 +1,167 @@
+import React from 'react';
+import { translate } from '../../../translate/translate';
+import addCoinOptionsCrypto from '../../addcoin/addcoinOptionsCrypto';
+import addCoinOptionsAC from '../../addcoin/addcoinOptionsAC';
+import Select from 'react-select';
+import {
+ triggerToaster,
+ shepherdToolsBalance,
+ shepherdToolsBuildUnsigned,
+ shepherdToolsPushTx,
+ shepherdToolsSeedToWif,
+ shepherdToolsWifToKP,
+ shepherdElectrumListunspent,
+ shepherdCliPromise,
+ shepherdElectrumSplitUtxoPromise,
+} from '../../../actions/actionCreators';
+import Store from '../../../store';
+
+class ToolsSeedToWif extends React.Component {
+ constructor() {
+ super();
+ this.state = {
+ s2wSeed: '',
+ s2wCoin: '',
+ s2wisIguana: true,
+ s2wResult: null,
+ };
+ this.updateInput = this.updateInput.bind(this);
+ this.updateSelectedCoin = this.updateSelectedCoin.bind(this);
+ this.seed2Wif = this.seed2Wif.bind(this);
+ this.toggleS2wIsIguana = this.toggleS2wIsIguana.bind(this);
+ }
+
+ seed2Wif() {
+ const _coin = this.state.s2wCoin.split('|');
+
+ shepherdToolsSeedToWif(
+ this.state.s2wSeed,
+ _coin[0],
+ this.state.s2wisIguana
+ )
+ .then((res) => {
+ // console.warn(res);
+
+ if (res.msg === 'success') {
+ this.setState({
+ s2wResult: res.result,
+ });
+ } else {
+ Store.dispatch(
+ triggerToaster(
+ res.result,
+ 'Seed to wif error',
+ 'error'
+ )
+ );
+ }
+ });
+ }
+
+ toggleS2wIsIguana() {
+ this.setState({
+ s2wisIguana: !this.state.s2wisIguana,
+ });
+ }
+
+ renderCoinOption(option) {
+ return (
+
+
+
{ option.label }
+
+ );
+ }
+
+ updateSelectedCoin(e, propName) {
+ if (e &&
+ e.value &&
+ e.value.indexOf('|')) {
+ this.setState({
+ [propName]: e.value,
+ });
+ }
+ }
+
+ updateInput(e) {
+ this.setState({
+ [e.target.name]: e.target.value,
+ });
+ }
+
+ render() {
+ return (
+
+
+
Seed to key pair (wif, pub)
+
+
+
+
+
+
+
+
+
+
+
+ Iguana Core compatible
+
+
+
+
+
+ { this.state.s2wResult &&
+
+
+ WIF: { this.state.s2wResult.keys.priv }
+
+
+ Pub: { this.state.s2wResult.keys.pub }
+
+
+ }
+
+ );
+ }
+}
+
+export default ToolsSeedToWif;
\ No newline at end of file
diff --git a/react/src/components/dashboard/tools/toolsSplitUtxo.js b/react/src/components/dashboard/tools/toolsSplitUtxo.js
new file mode 100644
index 0000000..4f77925
--- /dev/null
+++ b/react/src/components/dashboard/tools/toolsSplitUtxo.js
@@ -0,0 +1,520 @@
+import React from 'react';
+import { translate } from '../../../translate/translate';
+import addCoinOptionsCrypto from '../../addcoin/addcoinOptionsCrypto';
+import addCoinOptionsAC from '../../addcoin/addcoinOptionsAC';
+import Select from 'react-select';
+import {
+ triggerToaster,
+ shepherdToolsBalance,
+ shepherdToolsBuildUnsigned,
+ shepherdToolsPushTx,
+ shepherdToolsSeedToWif,
+ shepherdToolsWifToKP,
+ shepherdElectrumListunspent,
+ shepherdCliPromise,
+ shepherdElectrumSplitUtxoPromise,
+} from '../../../actions/actionCreators';
+import Store from '../../../store';
+import { isKomodoCoin } from '../../../util/coinHelper';
+import devlog from '../../../util/devlog';
+
+class ToolsSplitUTXO extends React.Component {
+ constructor() {
+ super();
+ this.state = {
+ utxoSplitLargestUtxo: null,
+ utxoSplitAddress: null,
+ utxoSplitWif: null,
+ utxoSplitSeed: '',
+ utxoSplitCoin: '',
+ utxoSplitList: null,
+ utxoSplitPairsCount: 1,
+ utxoSplitPairs: '10,0.002',
+ utxoSplitRawtx: null,
+ utxoSplitPushResult: null,
+ utxoSplitShowUtxoList: false,
+ splitUtxoApproximateVal: null,
+ };
+ this.updateInput = this.updateInput.bind(this);
+ this.updateSelectedCoin = this.updateSelectedCoin.bind(this);
+ this.getUtxoSplit = this.getUtxoSplit.bind(this);
+ this.splitUtxo = this.splitUtxo.bind(this);
+ this.toggleSplitUtxoList = this.toggleSplitUtxoList.bind(this);
+ this.splitUtxoApproximate = this.splitUtxoApproximate.bind(this);
+ }
+
+ toggleSplitUtxoList() {
+ this.setState({
+ utxoSplitShowUtxoList: !this.state.utxoSplitShowUtxoList,
+ });
+ }
+
+ splitUtxoApproximate() {
+ let largestUTXO = { amount: 0 };
+
+ for (let i = 0; i < this.state.utxoSplitList.length; i++) {
+ if (Number(this.state.utxoSplitList[i].amount) > Number(largestUTXO.amount)) {
+ largestUTXO = JSON.parse(JSON.stringify(this.state.utxoSplitList[i]));
+ }
+ }
+
+ devlog(`largest utxo ${largestUTXO.amount}`);
+ devlog(`largest utxo ${largestUTXO.amount}`);
+
+ const utxoSize = largestUTXO.amount;
+ const targetSizes = this.state.utxoSplitPairs.split(',');
+ const wif = this.state.utxoSplitWif;
+ const address = this.state.utxoSplitAddress;
+ const pairsCount = this.state.utxoSplitPairsCount;
+ let totalOutSize = 0;
+ let _targets = [];
+
+ devlog(`total utxos ${pairsCount * targetSizes.length}`);
+ devlog(`total pairs ${pairsCount}`);
+ devlog(`utxo size ${utxoSize}`);
+ devlog(`utxo sizes`);
+ devlog(targetSizes);
+
+ for (let i = 0; i < pairsCount; i++) {
+ for (let j = 0; j < targetSizes.length; j++) {
+ devlog(`vout ${_targets.length} ${targetSizes[j]}`);
+ _targets.push(Number(targetSizes[j]) * 100000000);
+ totalOutSize += Number(targetSizes[j]);
+ }
+ }
+
+ devlog(`total out size ${totalOutSize}`);
+ devlog(`largest utxo size ${largestUTXO.amount}`);
+ devlog(`change ${Number(largestUTXO.amount - totalOutSize) - 0.0001 + (largestUTXO.interest ? largestUTXO.interest : 0)}`);
+
+ this.setState({
+ splitUtxoApproximateVal: largestUTXO.amount - totalOutSize > 0 ? totalOutSize : 'no op, output is bigger than utxo size!',
+ });
+ }
+
+ splitUtxo() {
+ let largestUTXO = { amount: 0 };
+
+ for (let i = 0; i < this.state.utxoSplitList.length; i++) {
+ if (Number(this.state.utxoSplitList[i].amount) > Number(largestUTXO.amount)) {
+ largestUTXO = JSON.parse(JSON.stringify(this.state.utxoSplitList[i]));
+ }
+ }
+
+ devlog(`largest utxo ${largestUTXO.amount}`);
+ devlog(`largest utxo ${largestUTXO.amount}`);
+
+ const utxoSize = largestUTXO.amount;
+ const targetSizes = this.state.utxoSplitPairs.split(',');
+ const wif = this.state.utxoSplitWif;
+ const address = this.state.utxoSplitAddress;
+ const pairsCount = this.state.utxoSplitPairsCount;
+ let totalOutSize = 0;
+ let _targets = [];
+
+ devlog(`total utxos ${pairsCount * targetSizes.length}`);
+ devlog(`total pairs ${pairsCount}`);
+ devlog(`utxo size ${utxoSize}`);
+ devlog(`utxo sizes`);
+ devlog(targetSizes);
+
+ for (let i = 0; i < pairsCount; i++) {
+ for (let j = 0; j < targetSizes.length; j++) {
+ devlog(`vout ${_targets.length} ${targetSizes[j]}`);
+ _targets.push(Number(targetSizes[j]) * 100000000);
+ totalOutSize += Number(targetSizes[j]);
+ }
+ }
+
+ devlog(`total out size ${totalOutSize}`);
+ devlog(`largest utxo size ${largestUTXO.amount}`);
+ devlog(`change ${Number(largestUTXO.amount - totalOutSize) - 0.0001 + (largestUTXO.interest ? largestUTXO.interest : 0)}`);
+
+ const payload = {
+ wif,
+ network: 'komodo',
+ targets: _targets,
+ utxo: [largestUTXO],
+ changeAddress: address,
+ outputAddress: address,
+ change: Math.floor(Number(largestUTXO.amount - totalOutSize) * 100000000 - 10000 + ((largestUTXO.interest ? largestUTXO.interest : 0) * 100000000)), // 10k sat fee
+ };
+
+ devlog(payload);
+ devlog(largestUTXO);
+
+ shepherdElectrumSplitUtxoPromise(payload)
+ .then((res) => {
+ devlog(res);
+
+ if (res.msg === 'success') {
+ const _coin = this.state.utxoSplitCoin.split('|');
+
+ shepherdCliPromise(
+ null,
+ _coin[0],
+ 'sendrawtransaction',
+ [res.result]
+ )
+ .then((res) => {
+ devlog(res);
+
+ if (!res.error) {
+ this.setState({
+ utxoSplitPushResult: res.result,
+ });
+ Store.dispatch(
+ triggerToaster(
+ 'Split success',
+ 'UTXO',
+ 'success'
+ )
+ );
+ } else {
+ Store.dispatch(
+ triggerToaster(
+ res.result,
+ 'Split UTXO error',
+ 'error'
+ )
+ );
+ }
+ });
+ } else {
+ Store.dispatch(
+ triggerToaster(
+ res.result,
+ 'Split UTXO error',
+ 'error'
+ )
+ );
+ }
+ });
+ }
+
+ getUtxoSplit() {
+ const _coin = this.state.utxoSplitCoin.split('|');
+
+ shepherdToolsSeedToWif(
+ this.state.utxoSplitSeed,
+ 'KMD',
+ true
+ )
+ .then((seed2kpRes) => {
+ if (seed2kpRes.msg === 'success') {
+ shepherdCliPromise(null, _coin[0], 'listunspent')
+ .then((res) => {
+ // devlog(res);
+
+ if (!res.error) {
+ const _utxoList = res.result;
+ let largestUTXO = 0;
+
+ if (_utxoList &&
+ _utxoList.length) {
+ let _mineUtxo = [];
+
+ for (let i = 0; i < _utxoList.length; i++) {
+ if (_utxoList[i].spendable &&
+ seed2kpRes.result.keys.pub === _utxoList[i].address) {
+ _mineUtxo.push(_utxoList[i]);
+ }
+ }
+
+ for (let i = 0; i < _mineUtxo.length; i++) {
+ if (Number(_mineUtxo[i].amount) > Number(largestUTXO)) {
+ largestUTXO = _mineUtxo[i].amount;
+ }
+ }
+
+ this.setState({
+ utxoSplitList: _mineUtxo,
+ utxoSplitLargestUtxo: largestUTXO,
+ utxoSplitAddress: seed2kpRes.result.keys.pub,
+ utxoSplitWif: seed2kpRes.result.keys.priv,
+ });
+ } else {
+ Store.dispatch(
+ triggerToaster(
+ res.result,
+ 'Split UTXO error',
+ 'error'
+ )
+ );
+ }
+ } else {
+ Store.dispatch(
+ triggerToaster(
+ res.result,
+ 'Get UTXO error',
+ 'error'
+ )
+ );
+ }
+ });
+ } else {
+ Store.dispatch(
+ triggerToaster(
+ seed2kpRes.result,
+ 'Seed to wif error',
+ 'error'
+ )
+ );
+ }
+ });
+ }
+
+ renderCoinOption(option) {
+ return (
+
+
+
{ option.label }
+
+ );
+ }
+
+ updateSelectedCoin(e, propName) {
+ if (e &&
+ e.value &&
+ e.value.indexOf('|')) {
+ this.setState({
+ [propName]: e.value,
+ });
+ }
+ }
+
+ updateInput(e) {
+ this.setState({
+ [e.target.name]: e.target.value,
+ });
+ }
+
+ openExplorerWindow(txid, coin) {
+ const url = `http://${coin}.explorer.supernet.org/tx/${txid}`;
+ const remote = window.require('electron').remote;
+ const BrowserWindow = remote.BrowserWindow;
+
+ const externalWindow = new BrowserWindow({
+ width: 1280,
+ height: 800,
+ title: `${translate('INDEX.LOADING')}...`,
+ icon: remote.getCurrentWindow().iguanaIcon,
+ webPreferences: {
+ nodeIntegration: false,
+ },
+ });
+
+ externalWindow.loadURL(url);
+ externalWindow.webContents.on('did-finish-load', () => {
+ setTimeout(() => {
+ externalWindow.show();
+ }, 40);
+ });
+ }
+
+ renderUTXOSplitMergeResponse(type) {
+ const _utxos = type === 'merge' ? this.state.utxoMergeList : this.state.utxoSplitList;
+ let _items = [];
+
+ if (_utxos &&
+ _utxos.length) {
+ for (let i = 0; i < _utxos.length; i++) {
+ _items.push(
+
+ { _utxos[i].amount } |
+ { _utxos[i].address } |
+ { _utxos[i].confirmations } |
+ { _utxos[i].vout } |
+ { _utxos[i].txid } |
+
+ );
+ }
+ }
+
+ return (
+
+
+
+ Amount |
+ Address |
+ Confirmations |
+ Vout |
+ TxID |
+
+
+
+ { _items }
+
+
+
+ Amount |
+ Address |
+ Confirmations |
+ Vout |
+ TxID |
+
+
+
+ );
+ }
+
+ render() {
+ return (
+
+
+
Split UTXO
+
+
+
+
+
+
+
+
+ { this.state.utxoSplitAddress &&
+
+ Pub: { this.state.utxoSplitAddress }
+
+ }
+ { this.state.utxoSplitAddress &&
+
+ WIF: { this.state.utxoSplitWif }
+
+ }
+
+
+
+ { this.state.utxoSplitList &&
+
+ { /*this.renderUTXOSplitResponse()*/ }
+
Total UTXO: { this.state.utxoSplitList.length }
+
Largest UTXO: { this.state.utxoSplitLargestUtxo }
+
+ }
+
+
+
+ Show UTXO list
+
+
+ { this.state.utxoSplitShowUtxoList &&
+
+ { this.renderUTXOSplitMergeResponse('split') }
+
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+ { this.state.splitUtxoApproximateVal &&
+
+ Total out size: { this.state.splitUtxoApproximateVal }
+
+ }
+ {
+ /*this.state.utxoSplitRawtx &&
+
+ Rawtx:
{ this.state.utxoSplitRawtx }
+
*/
+ }
+ { this.state.utxoSplitPushResult &&
+
+ TXID:
{ this.state.utxoSplitPushResult }
+ { isKomodoCoin(this.state.utxoSplitCoin.split('|')[0]) &&
+
+
+
+ }
+
+ }
+
+ );
+ }
+}
+
+export default ToolsSplitUTXO;
\ No newline at end of file
diff --git a/react/src/components/dashboard/tools/toolsStringToQr.js b/react/src/components/dashboard/tools/toolsStringToQr.js
new file mode 100644
index 0000000..5974467
--- /dev/null
+++ b/react/src/components/dashboard/tools/toolsStringToQr.js
@@ -0,0 +1,61 @@
+import React from 'react';
+import { translate } from '../../../translate/translate';
+import addCoinOptionsCrypto from '../../addcoin/addcoinOptionsCrypto';
+import addCoinOptionsAC from '../../addcoin/addcoinOptionsAC';
+import Select from 'react-select';
+import {
+ triggerToaster,
+ shepherdToolsBalance,
+ shepherdToolsBuildUnsigned,
+ shepherdToolsPushTx,
+ shepherdToolsSeedToWif,
+ shepherdToolsWifToKP,
+} from '../../../actions/actionCreators';
+import Store from '../../../store';
+import QRCode from 'qrcode.react';
+import QRModal from '../qrModal/qrModal';
+
+class ToolsStringToQr extends React.Component {
+ constructor() {
+ super();
+ this.state = {
+ string2qr: '',
+ };
+ this.updateInput = this.updateInput.bind(this);
+ }
+
+ updateInput(e) {
+ this.setState({
+ [e.target.name]: e.target.value,
+ });
+ }
+
+ render() {
+ return (
+
+
+
String to QR
+
+
+
+
+ { this.state.string2qr &&
+
+
+
+ }
+
+ );
+ }
+}
+
+export default ToolsStringToQr;
\ No newline at end of file
diff --git a/react/src/components/dashboard/tools/toolsWifToWif.js b/react/src/components/dashboard/tools/toolsWifToWif.js
new file mode 100644
index 0000000..f989bf7
--- /dev/null
+++ b/react/src/components/dashboard/tools/toolsWifToWif.js
@@ -0,0 +1,140 @@
+import React from 'react';
+import { translate } from '../../../translate/translate';
+import addCoinOptionsCrypto from '../../addcoin/addcoinOptionsCrypto';
+import addCoinOptionsAC from '../../addcoin/addcoinOptionsAC';
+import Select from 'react-select';
+import {
+ triggerToaster,
+ shepherdToolsBalance,
+ shepherdToolsBuildUnsigned,
+ shepherdToolsPushTx,
+ shepherdToolsSeedToWif,
+ shepherdToolsWifToKP,
+ shepherdElectrumListunspent,
+ shepherdCliPromise,
+ shepherdElectrumSplitUtxoPromise,
+} from '../../../actions/actionCreators';
+import Store from '../../../store';
+
+class ToolsWifToWif extends React.Component {
+ constructor() {
+ super();
+ this.state = {
+ w2wWif: '',
+ w2wCoin: '',
+ w2wResult: null,
+ };
+ this.updateInput = this.updateInput.bind(this);
+ this.updateSelectedCoin = this.updateSelectedCoin.bind(this);
+ this.wif2wif = this.wif2wif.bind(this);
+ }
+
+ wif2wif() {
+ const _coin = this.state.w2wCoin.split('|');
+
+ shepherdToolsWifToKP(_coin[0], this.state.w2wWif)
+ .then((res) => {
+ // console.warn(res);
+
+ if (res.msg === 'success') {
+ this.setState({
+ w2wResult: res.result,
+ });
+ } else {
+ Store.dispatch(
+ triggerToaster(
+ res.result,
+ 'Seed to wif error',
+ 'error'
+ )
+ );
+ }
+ });
+ }
+
+ renderCoinOption(option) {
+ return (
+
+
+
{ option.label }
+
+ );
+ }
+
+ updateSelectedCoin(e, propName) {
+ if (e &&
+ e.value &&
+ e.value.indexOf('|')) {
+ this.setState({
+ [propName]: e.value,
+ });
+ }
+ }
+
+ updateInput(e) {
+ this.setState({
+ [e.target.name]: e.target.value,
+ });
+ }
+
+ render() {
+ return (
+
+
+
WIF to key pair (wif, pub)
+
+
+
+
+
+
+
+
+
+
+
+ { this.state.w2wResult &&
+
+
+ WIF: { this.state.w2wResult.keys.priv }
+
+
+ Pub: { this.state.w2wResult.keys.pub }
+
+
+ }
+
+ );
+ }
+}
+
+export default ToolsWifToWif;
\ No newline at end of file
diff --git a/react/src/util/devlog.js b/react/src/util/devlog.js
new file mode 100644
index 0000000..8d6857a
--- /dev/null
+++ b/react/src/util/devlog.js
@@ -0,0 +1,9 @@
+const devlog = (msg) => {
+ const mainWindow = window.require('electron').remote.getCurrentWindow();
+
+ if (mainWindow.appConfig.dev) {
+ console.warn(msg);
+ }
+}
+
+export default devlog;
\ No newline at end of file