diff --git a/react/src/actions/actions/tools.js b/react/src/actions/actions/tools.js index ad94988..17d1977 100644 --- a/react/src/actions/actions/tools.js +++ b/react/src/actions/actions/tools.js @@ -166,4 +166,60 @@ export function shepherdToolsPushTx(network, rawtx) { resolve(!json.result ? 'error' : json); }); }); +} + +export function shepherdToolsWifToKP(coin, wif) { + return new Promise((resolve, reject) => { + fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/electrum/wiftopub?coin=${coin}&wif=${wif}&token=${Config.token}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }) + .catch((error) => { + console.log(error); + Store.dispatch( + triggerToaster( + 'shepherdToolsWifToKP', + 'Error', + 'error' + ) + ); + }) + .then(response => response.json()) + .then(json => { + resolve(!json.result ? 'error' : json); + }); + }); +} + +export function shepherdToolsSeedToWif(seed, network, iguana) { + return new Promise((resolve, reject) => { + fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/electrum/seedtowif`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + seed, + network, + iguana, + token: Config.token, + }), + }) + .catch((error) => { + console.log(error); + Store.dispatch( + triggerToaster( + 'shepherdToolsSeedToWif', + 'Error', + 'error' + ) + ); + }) + .then(response => response.json()) + .then(json => { + resolve(!json.result ? 'error' : json); + }); + }); } \ No newline at end of file diff --git a/react/src/components/dashboard/tools/tools.js b/react/src/components/dashboard/tools/tools.js index 84ed680..670913e 100644 --- a/react/src/components/dashboard/tools/tools.js +++ b/react/src/components/dashboard/tools/tools.js @@ -8,6 +8,9 @@ import { shepherdToolsBalance, shepherdToolsBuildUnsigned, shepherdToolsPushTx, + shepherdToolsSeedToWif, + shepherdToolsWifToKP, + shepherdElectrumListunspent, } from '../../../actions/actionCreators'; import Store from '../../../store'; import QRCode from 'qrcode.react'; @@ -27,6 +30,19 @@ class Tools extends React.Component { rawTx2Push: null, txPushResult: null, string2qr: null, + s2wSeed: '', + s2wCoin: '', + s2wisIguana: true, + s2wResult: null, + w2wWif: '', + w2wCoin: '', + w2wResult: null, + utxoAddr: '', + utxoCoin: '', + utxoResult: null, + balanceAddr: '', + balanceCoin: '', + balanceResult: null, }; this.updateInput = this.updateInput.bind(this); this.updateSelectedCoin = this.updateSelectedCoin.bind(this); @@ -34,16 +50,114 @@ class Tools extends React.Component { 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); + } + + 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); + // console.warn(_txData); shepherdToolsPushTx(_txData[0], _txData[1]) .then((res) => { - console.warn(res); + // console.warn(res); this.setState({ txPushResult: res.result, @@ -78,7 +192,7 @@ class Tools extends React.Component { shepherdToolsBuildUnsigned(_coin[0], this.state.amount * 100000000, this.state.sendTo, this.state.sendFrom) .then((res) => { - console.warn(res); + // console.warn(res); if (res.msg === 'success') { let tx2qr = 'agtx:'; @@ -90,8 +204,8 @@ class Tools extends React.Component { 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); + // console.warn(tx2qr); + // console.warn('txqr length', tx2qr.length); // max 350 chars @@ -130,13 +244,12 @@ class Tools extends React.Component { ); } - updateSelectedCoin(e, index) { + updateSelectedCoin(e, propName) { if (e && e.value && e.value.indexOf('|')) { this.setState({ - selectedCoin: e.value, - balance: null, + [propName]: e.value, }); } } @@ -147,6 +260,71 @@ class Tools extends React.Component { }); } + setActiveSection(section) { + this.setState({ + activeSection: section, + }); + } + + toggleS2wIsIguana() { + this.setState({ + s2wisIguana: !this.state.s2wisIguana, + }); + } + + 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 ( + + + + + + + { _coin[0] === 'KMD' && + + } + + + + + { _items } + + + + + + + { _coin[0] === 'KMD' && + + } + + + +
AmountConfirmationsVoutLocktimeTxID
AmountConfirmationsVoutLocktimeTxID
+ ); + } + render() { return (
@@ -154,180 +332,433 @@ class Tools extends React.Component {

Tools

-

Offline Transaction Signing

-
- - -
-
- - { this.state.balance && - - } -
-
- - -
-
- - -
-
- + + + + + +
* Electrum
- { this.state.tx2qr && -
- - +
+ { 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.tx2qr && -
- - { this.state.utxo.length > 3 && -
cant encode a qr tx larger than 3 utxos!
+ { 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.tx2qr && - this.state.utxo.length < 4 && -
-
-
- + { this.state.activeSection === 'string2qr' && +
+
+

String to QR

+
+
+ +
+ { this.state.string2qr && +
+ +
+ }
- -
-
- } -
-
-
-

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.activeSection === 'balance' && +
+
+ + +
+ + +
+
+ +
+ { this.state.balanceResult && +
+
+ Balance (confirmed): { this.state.balanceResult.balance } +
+
+ Balance (unconfirmed): { this.state.balanceResult.unconfirmed } +
+
+ } +
+ } + { this.state.activeSection === 'utxo' && +
+
+ + +
+
+ +
+ { this.state.utxoResult && +
+ { this.renderUTXOResponse() } +
+ } +
+ } + { this.state.activeSection === 'wif2wif' && +
+
+ + +
+
+ +
+ { this.state.w2wResult && +
+
+ WIF: { this.state.w2wResult.keys.priv } +
+
+ Pub: { this.state.w2wResult.keys.pub } +
+
+ } +
+ } + { this.state.activeSection === 'seed2kp' && +
+
+ + +
+
+ +
+ Iguana Core compatible +
+
+
+ +
+ { this.state.s2wResult && +
+
+ WIF: { this.state.s2wResult.keys.priv } +
+
+ Pub: { this.state.s2wResult.keys.pub } +
+
+ } +
+ }
- { this.state.string2qr && -
- -
- }
diff --git a/react/src/components/dashboard/tools/tools.scss b/react/src/components/dashboard/tools/tools.scss index fb3fa58..519e9f5 100644 --- a/react/src/components/dashboard/tools/tools.scss +++ b/react/src/components/dashboard/tools/tools.scss @@ -30,4 +30,10 @@ right: 10px; } } + + .iguana-core-toggle { + position: relative; + top: -6px; + left: 18px; + } } \ No newline at end of file diff --git a/react/src/components/login/login.js b/react/src/components/login/login.js index 71db8b7..4825913 100644 --- a/react/src/components/login/login.js +++ b/react/src/components/login/login.js @@ -142,7 +142,7 @@ class Login extends React.Component { toggleShouldEncryptSeed() { this.setState({ - shouldEncryptSeed: !this.state.shouldEncryptSeed + shouldEncryptSeed: !this.state.shouldEncryptSeed, }); } diff --git a/react/src/components/login/login.scss b/react/src/components/login/login.scss index b3239ad..81fa5da 100644 --- a/react/src/components/login/login.scss +++ b/react/src/components/login/login.scss @@ -247,6 +247,10 @@ option.login-option { } .qr-modal-login-block { + position: absolute; + right: 0; + top: -50px; + .btn { background: transparent; color: #fff;