Skip to content

Commit

Permalink
Merge pull request #19 from Permissionless-Software-Foundation/ct-uns…
Browse files Browse the repository at this point in the history
…table

feat(sweep): Adding sweep feature
  • Loading branch information
christroutner authored Aug 26, 2022
2 parents fbf4373 + 0e3cd81 commit 59efca5
Show file tree
Hide file tree
Showing 10 changed files with 268 additions and 5 deletions.
Binary file modified android/apk/bch-web3-wallet.apk
Binary file not shown.
1 change: 1 addition & 0 deletions public/bch-token-sweep.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion public/minimal-slp-wallet.min.js

Large diffs are not rendered by default.

14 changes: 11 additions & 3 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,10 @@ class App extends React.Component {
bchBalance: 0,
slpTokens: [],
bchUsdPrice: 150
}
},

// Will be replaced by Sweep library class once the library loads.
Sweep: null
}

this.cnt = 0
Expand All @@ -84,9 +87,12 @@ class App extends React.Component {
async componentDidMount () {
try {
this.addToModal('Loading minimal-slp-wallet')

await this.asyncLoad.loadWalletLib()

this.addToModal('Loading bch-sweep-lib')
const Sweep = await this.asyncLoad.loadSweepLib()
this.setState({ Sweep })

// Update the list of potential back end servers.
this.addToModal('Getting alternative servers')
const servers = await this.asyncLoad.getServers()
Expand Down Expand Up @@ -152,7 +158,9 @@ class App extends React.Component {
setMnemonic: this.setMnemonic,
delMnemonic: this.delMnemonic,

servers: this.state.servers // Alternative back end servers
servers: this.state.servers, // Alternative back end servers

Sweep: this.state.Sweep // Sweep library
}

return (
Expand Down
3 changes: 3 additions & 0 deletions src/components/app-body/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import BchWallet from '../bch-wallet'
import BchSend from '../bch-send'
import SlpTokens from '../slp-tokens'
import ServerSelectView from '../servers/select-server-view'
import Sweep from '../sweep'

// let _this

Expand Down Expand Up @@ -57,6 +58,8 @@ class AppBody extends React.Component {
case 1:
return (<SlpTokens appData={this.state.appData} />)
case 2:
return (<Sweep appData={this.state.appData} />)
case 3:
return (
<BchWallet
appData={this.state.appData}
Expand Down
4 changes: 4 additions & 0 deletions src/components/bch-wallet/warning.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ class WebWalletWarning extends React.Component {
<br />
<b>Do not store large amounts of money on a web wallet.
</b>
<br /><br />
Note: Scammers frequently copy this open source code to build
apps for stealing people's money. Be sure you trust the source
serving you this app.
</Card.Text>
</Card.Body>
</Card>
Expand Down
1 change: 1 addition & 0 deletions src/components/load-scripts.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ function LoadScripts () {

// Load the libraries from the local directory.
useScript(`${process.env.PUBLIC_URL}/minimal-slp-wallet.min.js`)
useScript(`${process.env.PUBLIC_URL}/bch-token-sweep.min.js`)

return true
}
Expand Down
3 changes: 2 additions & 1 deletion src/components/nav-menu/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ class NavMenu extends React.Component {
<Nav className='mr-auto' style={{ padding: '25px' }}>
<Nav.Link href='#' onClick={() => this.handleClickEvent(0)}>BCH</Nav.Link>
<Nav.Link href='#' onClick={() => this.handleClickEvent(1)}>Tokens</Nav.Link>
<Nav.Link href='#' onClick={() => this.handleClickEvent(2)}>Wallet</Nav.Link>
<Nav.Link href='#' onClick={() => this.handleClickEvent(2)}>Sweep</Nav.Link>
<Nav.Link href='#' onClick={() => this.handleClickEvent(3)}>Wallet</Nav.Link>
</Nav>
</Navbar.Collapse>
</Navbar>
Expand Down
227 changes: 227 additions & 0 deletions src/components/sweep/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
/*
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'

// let _this

class Sweep extends React.Component {
constructor (props) {
super()

this.state = {
appData: props.appData,
wifToSweep: '',

// Modal control
showModal: false,
statusMsg: '',
hideSpinner: false,
shouldRefreshOnModalClose: false
}

// Bind this to event handlers
this.handleSweep = this.handleSweep.bind(this)
this.updateWalletState = this.updateWalletState.bind(this)

// _this = this
}

render () {
// Generate the JSX for the modal.
const modal = this.getModal()

return (
<>
<Container>
<Row>
<Col>
<p>
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 <a href='https://paperwallet.fullstack.cash/' target='_blank' rel='noreferrer'>generate paper wallets here</a>.
</p>
<p>
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'.
</p>
</Col>
</Row>

<Row>
<Col>
<Form>
<Form.Group controlId='formWif' style={{ textAlign: 'center' }}>
<Form.Control
type='text'
placeholder='KzJqZxi5XSo36woCy7MFVNRPDpfp8x8FpkhRvrErKBBrDXRVY9Ft'
onChange={e => this.setState({ wifToSweep: e.target.value })}
value={this.state.wifToSweep}
/>
</Form.Group>
</Form>
</Col>
</Row>
<br />

<Row style={{ textAlign: 'center' }}>
<Col>
<Button variant='info' onClick={(e) => this.handleSweep()}>Sweep</Button>
</Col>
</Row>
</Container>

{
this.state.showModal
? modal
: null
}
</>
)
}

async handleSweep (event) {
try {
const wif = this.state.wifToSweep
console.log(`Sweeping this WIF: ${wif}`)

// Set the modal to its initial state.
this.setState({
showModal: true,
hideSpinner: false,
statusMsg: ''
})

// Input validation
const isWIF = this.validateWIF(wif)
if (!isWIF) {
// throw new Error('Not a WIF key')
this.setState({
hideSpinner: true,
statusMsg: 'Input is not a WIF private key.'
})
return
}

const Sweep = this.state.appData.Sweep
const walletWif = this.state.appData.bchWallet.walletInfo.privateKey
// const bchjs = this.state.appData.bchWallet.bchjs
const toAddr = this.state.appData.bchWallet.slpAddress

// Instance the Sweep library
const sweep = new Sweep(wif, walletWif, this.state.appData.bchWallet)
await sweep.populateObjectFromNetwork()

// Constructing the sweep transaction
const hex = await sweep.sweepTo(toAddr)

// return transactionHex

// Broadcast the transaction to the network.
// const txId = await sweeperLib.blockchain.broadcast(transactionHex)
const txid = await this.state.appData.bchWallet.ar.sendTx(hex)

// Generate an HTML status message with links to block explorers.
const statusMsg = (
<>
<p>
Sweep succeeded!
</p>
<p>
Transaction ID: {txid}
</p>
<p>
<a href={`https://blockchair.com/bitcoin-cash/transaction/${txid}`} target='_blank' rel='noreferrer'>TX on Blockchair BCH Block Explorer</a>
</p>
<p>
<a href={`https://token.fullstack.cash/transactions/?txid=${txid}`} target='_blank' rel='noreferrer'>TX on token explorer</a>
</p>
</>
)

this.setState({
hideSpinner: true,
statusMsg
})

await this.updateWalletState()
} catch (err) {
console.error('Error in handleSweep(): ', err)
}
}

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
}

// Generate the info modal that is displayed when the button is clicked.
getModal () {
// const token = this.state.token
// console.log(`token: ${JSON.stringify(token, null, 2)}`)

return (
<Modal show={this.state.showModal} size='lg' onHide={(e) => this.handleCloseModal(this)}>
<Modal.Header closeButton>
<Modal.Title>Sweeping...</Modal.Title>
</Modal.Header>
<Modal.Body>
<Container>
<Row>
<Col style={{ textAlign: 'center' }}>
Sweeping private key... {
this.state.hideSpinner ? null : <Spinner animation='border' />
}
</Col>
</Row>
<br />

<Row>
<Col style={{ textAlign: 'center' }}>
{this.state.statusMsg}
</Col>
</Row>
</Container>
</Modal.Body>
<Modal.Footer />
</Modal>
)
}

// This handler function is called when the modal is closed.
async handleCloseModal () {
this.setState({
showModal: false
})
}

// Update the wallet state. This probably needs to be refined.
async updateWalletState () {
const wallet = this.state.appData.bchWallet

const bchBalance = await wallet.getBalance(wallet.walletInfo.cashAddress)
await wallet.initialize()
const slpTokens = await wallet.listTokens(wallet.walletInfo.cashAddress)

this.state.appData.updateBchWalletState({ bchBalance, slpTokens })
}
}

export default Sweep
18 changes: 18 additions & 0 deletions src/services/async-load.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import GistServers from './gist-servers'
class AsyncLoad {
constructor () {
this.BchWallet = false
this.Sweep = false
}

// Load the minimal-slp-wallet which comes in as a <script> file and is
Expand All @@ -30,6 +31,22 @@ class AsyncLoad {
} while (!this.BchWallet)
}

// Load the bch-sweep-lib which comes in a <script> file and is attached to
// the global 'window' object.
async loadSweepLib () {
do {
if (typeof window !== 'undefined' && window.Sweep) {
this.Sweep = window.Sweep

return this.Sweep
} else {
console.log('Waiting for sweep library to load...')
}

await sleep(1000)
} while (!this.Sweep)
}

// Initialize the BCH wallet
async initWallet (restURL, mnemonic, setMnemonic, updateBchWalletState) {
const options = {
Expand All @@ -49,6 +66,7 @@ class AsyncLoad {

// Wait for wallet to initialize.
await wallet.walletInfoPromise
await wallet.initialize()

// Update the state of the wallet.
updateBchWalletState(wallet.walletInfo)
Expand Down

0 comments on commit 59efca5

Please sign in to comment.