diff --git a/package-lock.json b/package-lock.json index ed5f2bc..b3aa1e7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@fortawesome/react-fontawesome": "0.2.2", "axios": "0.27.2", "bch-message-lib": "2.2.1", + "bch-token-sweep": "^2.2.1", "bootstrap": "5.2.0", "qrcode.react": "4.2.0", "query-string": "7.1.1", @@ -5126,6 +5127,19 @@ "minimal-slp-wallet": ">= 4" } }, + "node_modules/bch-token-sweep": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/bch-token-sweep/-/bch-token-sweep-2.2.1.tgz", + "integrity": "sha512-f4UGEpPXzea7HQMRSEb6h/amXiwrlsMBAjl1dVYg5//26IDWeG+e3qVNtMJhm00NMZ0BS+LasUa2xlz0UVziZA==", + "license": "MIT", + "dependencies": { + "bch-donation": "1.1.2", + "bignumber.js": "9.0.0" + }, + "peerDependencies": { + "minimal-slp-wallet": ">= 4" + } + }, "node_modules/bchaddrjs-slp": { "version": "0.2.5", "license": "MIT", @@ -13302,6 +13316,8 @@ "node_modules/node-fetch": { "name": "@achingbrain/node-fetch", "version": "2.6.7", + "resolved": "https://registry.npmjs.org/@achingbrain/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-iTASGs+HTFK5E4ZqcMsHmeJ4zodyq8L38lZV33jwqcBJYoUt3HjN4+ot+O9/0b+ke8ddE7UgOtVuZN/OkV19/g==", "dev": true, "license": "MIT", "engines": { @@ -22429,6 +22445,15 @@ "version": "2.2.1", "requires": {} }, + "bch-token-sweep": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/bch-token-sweep/-/bch-token-sweep-2.2.1.tgz", + "integrity": "sha512-f4UGEpPXzea7HQMRSEb6h/amXiwrlsMBAjl1dVYg5//26IDWeG+e3qVNtMJhm00NMZ0BS+LasUa2xlz0UVziZA==", + "requires": { + "bch-donation": "1.1.2", + "bignumber.js": "9.0.0" + } + }, "bchaddrjs-slp": { "version": "0.2.5", "requires": { @@ -27457,7 +27482,8 @@ "version": "2.0.2" }, "node-fetch": { - "version": "npm:@achingbrain/node-fetch@2.6.7", + "version": "https://registry.npmjs.org/@achingbrain/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-iTASGs+HTFK5E4ZqcMsHmeJ4zodyq8L38lZV33jwqcBJYoUt3HjN4+ot+O9/0b+ke8ddE7UgOtVuZN/OkV19/g==", "dev": true }, "node-forge": { diff --git a/package.json b/package.json index 08727ea..18cad8a 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "@fortawesome/react-fontawesome": "0.2.2", "axios": "0.27.2", "bch-message-lib": "2.2.1", + "bch-token-sweep": "^2.2.1", "bootstrap": "5.2.0", "qrcode.react": "4.2.0", "query-string": "7.1.1", diff --git a/src/components/app-body/index.js b/src/components/app-body/index.js index 8e1565b..f28d7d6 100644 --- a/src/components/app-body/index.js +++ b/src/components/app-body/index.js @@ -19,6 +19,7 @@ import ServerSelectView from './servers/select-server-view' import SelectServerButton from './servers/select-server-button' import BchSend from './bch-send' import SlpTokens from './slp-tokens' +import SweepWif from './sweep/index.js' function AppBody (props) { // Dependency injection through props @@ -35,6 +36,7 @@ function AppBody (props) { } /> } /> } /> + } /> {/** Show in all paths except the servers view */} {appData.currentPath !== '/servers' && } diff --git a/src/components/app-body/sweep/index.js b/src/components/app-body/sweep/index.js new file mode 100644 index 0000000..29c781e --- /dev/null +++ b/src/components/app-body/sweep/index.js @@ -0,0 +1,187 @@ +/* + This Sweep component allows users to sweep a private key and transfer any + BCH or SLP tokens into their wallet. +*/ + +// Global npm libraries +import React from 'react' +import { Container, Row, Col, Form, Button, Modal, Spinner } from 'react-bootstrap' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faCircleQuestion } from '@fortawesome/free-solid-svg-icons' +import Sweeper from 'bch-token-sweep' + +// let _this + +const SweepWif = (props) => { + const { appData } = props + console.log('appData', appData) + const [wifToSweep, setWifToSweep] = React.useState('') + const [showModal, setShowModal] = React.useState(false) + const [statusMsg, setStatusMsg] = React.useState('') + const [hideSpinner, setHideSpinner] = React.useState(false) + + // shouldRefreshOnModalClose: false + + // Helper function to validate WIF + const validateWIF = (WIF) => { + if (typeof WIF !== 'string') return false + if (WIF.length !== 52) return false + if (WIF[0] !== 'L' && WIF[0] !== 'K') return false + return true + } + + // Update wallet state function + const updateWalletState = async () => { + const wallet = appData.wallet + const bchBalance = await wallet.getBalance({ bchAddress: wallet.walletInfo.cashAddress }) + await wallet.initialize() + const slpTokens = await wallet.listTokens(wallet.walletInfo.cashAddress) + appData.updateBchWalletState({ walletObj: { bchBalance, slpTokens }, appData }) + } + + // Handle sweep function + const handleSweep = async () => { + try { + console.log(`Sweeping this WIF: ${wifToSweep}`) + + // Set modal initial state + setShowModal(true) + setHideSpinner(false) + setStatusMsg('') + + // Input validation + const isWIF = validateWIF(wifToSweep) + if (!isWIF) { + setHideSpinner(true) + setStatusMsg(Input is not a WIF private key.) + return + } + + try { + const walletWif = appData.wallet.walletInfo.privateKey + const toAddr = appData.wallet.slpAddress + + // Instance the Sweep library + const sweep = new Sweeper(wifToSweep, walletWif, appData.wallet) + await sweep.populateObjectFromNetwork() + + // Constructing the sweep transaction + const hex = await sweep.sweepTo(toAddr) + const txid = await appData.wallet.ar.sendTx(hex) + + // Generate status message + const newStatusMsg = ( + <> +

Sweep succeeded!

+

Transaction ID: {txid}

+

+ + TX on Blockchair BCH Block Explorer + +

+

+ + TX on token explorer + +

+ + ) + + setHideSpinner(true) + setStatusMsg(newStatusMsg) + setWifToSweep('') + + await updateWalletState() + } catch (err) { + setHideSpinner(true) + setStatusMsg({`Error: ${err.message}`}) + } + } catch (err) { + console.error('Error in handleSweep(): ', err) + } + } + + // Modal component + const getModal = () => ( + setShowModal(false)}> + + Sweeping... + + + + + {!hideSpinner && ( + + Sweeping private key... + + )} + +
+ {statusMsg && ( + + {statusMsg} + + )} +
+
+ +
+ ) + + return ( + <> + + + + + + + + + + + +

+ This View is used to 'sweep' a private key. This will transfer + any BCH or SLP tokens from a paper wallet to your web wallet. + Paper wallets are used to store BCH and tokens. You + can generate paper wallets here. +

+

+ Paste the private key of a paper wallet below and click the button + to sweep the funds. The private key must be in WIF format. It will + start with the letter 'K' or 'L'. +

+ +
+ + + +
+ + setWifToSweep(e.target.value)} + value={wifToSweep} + /> + +
+ +
+
+ + + + + + +
+ {showModal && getModal()} + + ) +} + +export default SweepWif diff --git a/src/components/nav-menu/index.js b/src/components/nav-menu/index.js index d4319c3..a623d8a 100644 --- a/src/components/nav-menu/index.js +++ b/src/components/nav-menu/index.js @@ -70,6 +70,13 @@ function NavMenu (props) { > Check Balance + + Sweep +