Merge pull request #12 from Permissionless-Software-Foundation/dh-sign-view

feat(sign): Ported Sign view
This commit is contained in:
Chris Troutner
2025-03-25 17:07:56 -07:00
committed by GitHub
8 changed files with 142 additions and 10 deletions
+3 -2
View File
@@ -14,7 +14,8 @@ function GetBalance (props) {
const [textInput, setTextInput] = useState('')
// Button click handler
const handleGetBalance = async (event) => {
const handleGetBalance = async (e) => {
e.preventDefault()
try {
// Exit on invalid input
if (!textInput) return
@@ -43,7 +44,7 @@ function GetBalance (props) {
<Container>
<Row>
<Col className='text-break' style={{ textAlign: 'center' }}>
<Form>
<Form onSubmit={handleGetBalance}>
<Form.Group className='mb-3' controlId='formBasicEmail'>
<Form.Label>Enter any BCH address to query its balance on the blockchain.</Form.Label>
<Form.Control type='text' placeholder='bitcoincash:qqlrzp23w08434twmvr4fxw672whkjy0py26r63g3d' onChange={e => setTextInput(e.target.value)} />
@@ -259,7 +259,7 @@ function SendCard (props) {
<Row>
<Col xs={12} style={{ textAlign: 'center' }}>
<Form>
<Form onSubmit={(e) => e.preventDefault()}>
<Form.Group controlId='formBasicEmail' style={{ display: 'flex', alignItems: 'center' }}>
<Form.Control
style={{ marginRight: '1rem' }}
@@ -289,7 +289,7 @@ function SendCard (props) {
<Row>
<Col xs={12}>
<Form style={{ paddingBottom: '10px' }}>
<Form style={{ paddingBottom: '10px' }} onSubmit={(e) => { e.preventDefault(); handleSendBch({ sendCardData, appData }) }}>
<Form.Group controlId='formBasicEmail' style={{ textAlign: 'center' }}>
<Form.Control
type='number'
@@ -77,7 +77,7 @@ const WalletImport = (props) => {
<Container>
<Row>
<Col xs={12} className='text-break' style={{ textAlign: 'center' }}>
<Form>
<Form onSubmit={(e) => e.preventDefault()}>
<Form.Group className='mb-3' controlId='formImportWallet' style={{ display: 'flex', alignItems: 'center' }}>
<Form.Control
style={{ margin: '1rem' }}
+2
View File
@@ -20,6 +20,7 @@ import SelectServerButton from './servers/select-server-button'
import BchSend from './bch-send'
import SlpTokens from './slp-tokens'
import SweepWif from './sweep/index.js'
import SignMessage from './sign/index.js'
function AppBody (props) {
// Dependency injection through props
@@ -37,6 +38,7 @@ function AppBody (props) {
<Route path='/placeholder3' element={<Placeholder3 />} />
<Route path='/servers' element={<ServerSelectView appData={appData} />} />
<Route path='/sweep' element={<SweepWif appData={appData} />} />
<Route path='/sign' element={<SignMessage appData={appData} />} />
</Routes>
{/** Show in all paths except the servers view */}
{appData.currentPath !== '/servers' && <SelectServerButton linkTo='/servers' appData={appData} />}
+120
View File
@@ -0,0 +1,120 @@
/*
Component for signing a message with a WIF private key.
*/
// Global npm libraries
import React, { useState } from 'react'
import { Container, Row, Col, Form, Button } from 'react-bootstrap'
import { faCopy } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
function SignMessage (props) {
// Convert class state to hooks
const { wallet, appUtil } = props.appData
const [sign, setSign] = useState('')
const [msg, setMsg] = useState('')
const [bchAddr] = useState(wallet.walletInfo.cashAddress)
const [slpAddr] = useState(wallet.walletInfo.slpAddress)
const [err, setErr] = useState('')
const [copied, setCopied] = useState(false)
const handleSignMessage = (event) => {
try {
event.preventDefault()
if (!msg) throw new Error('Enter a message to sign.')
const bchjs = wallet.bchjs
const wif = props.appData.wallet.walletInfo.privateKey
const sig = bchjs.BitcoinCash.signMessageWithPrivKey(wif, msg)
setSign(sig)
setErr('')
} catch (err) {
console.log('Error in handleSignMessage(): ', err)
setErr(err.message)
setSign('')
}
}
// Function to copy the value to the clipboard.
const handleCopyToClipboard = async (value) => {
appUtil.copyToClipboard(value)
// show the copied message
setCopied(true)
// hide copied message after 1 second
setTimeout(function () {
setCopied(false)
}, 1000)
}
const copyIcon = (value) => {
return <FontAwesomeIcon icon={faCopy} size='lg' onClick={() => handleCopyToClipboard(value)} style={{ cursor: 'pointer', marginLeft: '10px' }} />
}
return (
<>
<Container>
<Row>
<Col>
<p>
This view allows you cryptographically sign a message with your
wallet. These signatures are used in a wide range of applications,
such as gaining access to
the <a href='https://t.me/psf_vip' target='_blank' rel='noreferrer'>PSF VIP Telegram channel</a>.
</p>
<p>
Enter any message into the form below and click the button. This
view will generate a cryptographic signature.
</p>
</Col>
</Row>
<Row>
<Col className='text-break'>
<Form onSubmit={handleSignMessage}>
<Form.Group className='mb-3' controlId='message'>
<Form.Label><b>Enter a message to sign.</b></Form.Label>
<Form.Control type='text' placeholder='' onChange={e => setMsg(e.target.value)} />
</Form.Group>
{err && <p style={{ color: 'red', marginBottom: '10px' }}>{`Error: ${err}`}</p>}
<Button variant='primary' onClick={handleSignMessage}>
Sign Message
</Button>
</Form>
</Col>
</Row>
<br />
{sign && (
<div style={{ textAlign: 'center' }}>
<Row>
<Col>
<p>
<b>Signature:</b> {sign} {copyIcon(sign)}
</p>
<p>
<b>BCH Address:</b> {bchAddr} {copyIcon(bchAddr)}
</p>
<p>
<b>SLP Address:</b> {slpAddr} {copyIcon(slpAddr)}
</p>
</Col>
</Row>
{copied && (
<span style={{ color: 'green' }}>
Copied!
</span>
)}
</div>
)}
</Container>
</>
)
}
export default SignMessage
@@ -51,7 +51,8 @@ function SendTokenButton ({ token, appData, refreshTokens }) {
// Click handler that fires when the user clicks the 'Send' button.
const handleSendTokens = async () => {
const handleSendTokens = async (e) => {
e.preventDefault()
try {
setStatusMsg('Preparing to send tokens...')
setHideSpinner(false)
@@ -131,7 +132,7 @@ function SendTokenButton ({ token, appData, refreshTokens }) {
<Row>
<Col xs={10}>
<Form>
<Form onSubmit={(e) => e.preventDefault()}>
<Form.Group controlId='formBasicEmail' style={{ textAlign: 'center' }}>
<Form.Control
type='text'
@@ -161,7 +162,7 @@ function SendTokenButton ({ token, appData, refreshTokens }) {
<Row>
<Col xs={10}>
<Form style={{ paddingBottom: '10px' }}>
<Form style={{ paddingBottom: '10px' }} onSubmit={handleSendTokens}>
<Form.Group controlId='formBasicEmail' style={{ textAlign: 'center' }}>
<Form.Control
type='text'
+3 -2
View File
@@ -40,7 +40,8 @@ const SweepWif = (props) => {
}
// Handle sweep function
const handleSweep = async () => {
const handleSweep = async (e) => {
e.preventDefault()
try {
console.log(`Sweeping this WIF: ${wifToSweep}`)
@@ -157,7 +158,7 @@ const SweepWif = (props) => {
<Row>
<Col>
<Form>
<Form onSubmit={handleSweep}>
<Form.Group controlId='formWif' style={{ textAlign: 'center' }}>
<Form.Control
type='text'
+7
View File
@@ -77,6 +77,13 @@ function NavMenu (props) {
>
Sweep
</NavLink>
<NavLink
className={(currentPath === '/sign') ? 'nav-link-active' : 'nav-link-inactive'}
to='/sign'
onClick={handleClickEvent}
>
Sign
</NavLink>
</Nav>
</Navbar.Collapse>
</Navbar>