Compare commits

...
8 Commits
10 changed files with 5816 additions and 39 deletions
File diff suppressed because it is too large Load Diff
@@ -3,7 +3,7 @@
*/
// Global npm libraries
import React, { useCallback, useEffect, useState } from 'react'
import React, { useState } from 'react'
import { Container, Row, Col, Card } from 'react-bootstrap'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faWallet, faEye, faEyeSlash } from '@fortawesome/free-solid-svg-icons'
@@ -12,9 +12,6 @@ import { faWallet, faEye, faEyeSlash } from '@fortawesome/free-solid-svg-icons'
// Local libraries
import './wallet-summary.css'
import CopyOnClick from './copy-on-click'
import { getPublicKey } from 'nostr-tools/pure'
import { base58_to_binary as base58ToBinary } from 'base58-js'
import { bytesToHex } from '@noble/hashes/utils' // already an installed dependency
function WalletSummary (props) {
// Props
@@ -28,10 +25,7 @@ function WalletSummary (props) {
const [blurredPrivateKey, setBlurredPrivateKey] = useState(true)
const [blurredNostrPrivKey, setBlurredNostrPrivKey] = useState(true)
const [nostrKeyPair, setNostrKeyPair] = useState({
privHex: '',
pubHex: ''
})
const [nostrKeyPair] = useState(bchWalletState.nostrKeyPair)
// Encapsulate component state into an object that can be passed to child functions
const walletSummaryData = {
@@ -82,30 +76,6 @@ function WalletSummary (props) {
}
}
const nostrKeyPairFromWIF = useCallback((WIF) => {
if (!WIF) return
// Extract the privaty key from the WIF, using this guide:
// https://learnmeabitcoin.com/technical/keys/private-key/wif/
const wifBuf = base58ToBinary(WIF)
const privBuf = wifBuf.slice(1, 33)
// console.log('privBuf: ', privBuf)
const privHex = bytesToHex(privBuf)
console.log('BCH & Nostr private key (HEX format): ', privHex)
const pubHex = getPublicKey(privBuf)
console.log('nostrPubKey: ', pubHex)
return {
privHex,
pubHex
}
}, [])
useEffect(() => {
setNostrKeyPair(nostrKeyPairFromWIF(bchWalletState.privateKey))
}, [nostrKeyPairFromWIF, bchWalletState])
return (
<>
<Container>
+5
View File
@@ -23,6 +23,8 @@ import SlpTokens from './slp-tokens'
import SweepWif from './sweep/index.js'
import SignMessage from './sign/index.js'
import ServerSelectView from './configuration/select-server-view'
import NostrPost from './nostr/nostr-post.js'
import NostrRead from './nostr/nostr-read.js'
function AppBody (props) {
// Dependency injection through props
@@ -43,6 +45,9 @@ function AppBody (props) {
<Route path='/sweep' element={<SweepWif appData={appData} />} />
<Route path='/sign' element={<SignMessage appData={appData} />} />
<Route path='/configuration' element={<ServerSelectView appData={appData} />} />
<Route path='/nostr-post' element={<NostrPost appData={appData} />} />
<Route path='/nostr-read' element={<NostrRead appData={appData} />} />
</Routes>
{/** Show in all paths except the servers view */}
{/* {appData.currentPath !== '/servers' && <SelectServerButton linkTo='/servers' appData={appData} />} */}
+158
View File
@@ -0,0 +1,158 @@
/*
Component for posting nostr information.
*/
// Global npm libraries
import React, { useState } from 'react'
import { Container, Form, Button, Spinner } from 'react-bootstrap'
import Accordion from 'react-bootstrap/Accordion'
import { finalizeEvent } from 'nostr-tools/pure'
import { Relay } from 'nostr-tools/relay'
import { hexToBytes } from '@noble/hashes/utils' // already an installed dependency
function NostrPost (props) {
const [accordionKey, setAccordionKey] = useState('1')
const [onFetch, setOnFetch] = useState(false)
const { bchWalletState } = props.appData
const [formData, setFormData] = useState({
name: '',
about: ''
})
const [errorMsg, setErrorMsg] = useState('')
const [successMsg, setSuccessMsg] = useState('')
const handleInputChange = (e) => {
const { name, value } = e.target
setFormData({
...formData,
[name]: value
})
setSuccessMsg('')
setErrorMsg('')
}
// Post on nostr network
const handleSubmit = async (e) => {
e.preventDefault()
try {
setErrorMsg('')
setSuccessMsg('')
setOnFetch(true)
const { nostrKeyPair } = bchWalletState
// Convert private key to binary
const privateKeyBin = hexToBytes(nostrKeyPair.privHex)
// Relay list
const psf = 'wss://nostr-relay.psfoundation.info'
const formDataString = JSON.stringify(formData)
// Generate a post.
const eventTemplate = {
kind: 0,
created_at: Math.floor(Date.now() / 1000),
tags: [],
content: formDataString
}
console.log(`eventTemplate: ${JSON.stringify(eventTemplate, null, 2)}`)
// Sign the post
const signedEvent = finalizeEvent(eventTemplate, privateKeyBin)
console.log('signedEvent: ', signedEvent)
// Connect to a relay.
const relay = await Relay.connect(psf)
console.log(`connected to ${relay.url}`)
// Publish the message to the relay.
const result = await relay.publish(signedEvent)
console.log('result: ', result)
// Close the connection to the relay.
relay.close()
resetForm()
setSuccessMsg('Post successfully published!')
setOnFetch(false)
} catch (error) {
console.warn(error)
setErrorMsg(error.message || 'An error occurred while posting')
setOnFetch(false)
}
}
const handleAccordionChange = (key) => {
setAccordionKey(key)
}
const resetForm = () => {
setFormData({
name: '',
about: ''
})
}
return (
<>
<Container>
<Accordion activeKey={accordionKey} onSelect={handleAccordionChange}>
<Accordion.Item eventKey='1'>
<Accordion.Header>Post</Accordion.Header>
<Accordion.Body>
{errorMsg && (
<div className='alert alert-danger' role='alert'>
{errorMsg}
</div>
)}
{successMsg && (
<div className='alert alert-success' role='alert'>
{successMsg}
</div>
)}
<Form onSubmit={handleSubmit}>
<Form.Group className='mb-3'>
<Form.Label>Name</Form.Label>
<Form.Control
type='text'
placeholder='Enter your name'
name='name'
value={formData.name}
onChange={handleInputChange}
required
/>
</Form.Group>
<Form.Group className='mb-3'>
<Form.Label>About</Form.Label>
<Form.Control
as='textarea'
rows={3}
placeholder='Tell us about yourself'
name='about'
value={formData.about}
onChange={handleInputChange}
required
/>
</Form.Group>
<div className='d-flex justify-content-center'>
{!onFetch && (
<Button variant='primary' type='submit' disabled={onFetch}>
Post
</Button>
)}
{onFetch && <Spinner animation='border' variant='primary' />}
</div>
</Form>
</Accordion.Body>
</Accordion.Item>
</Accordion>
</Container>
</>
)
}
export default NostrPost
@@ -0,0 +1,67 @@
// Global npm libraries
import React, { useEffect, useState } from 'react'
import { Container } from 'react-bootstrap'
import Accordion from 'react-bootstrap/Accordion'
import { RelayPool } from 'nostr'
function NostrRead (props) {
const { bchWalletState } = props.appData
const [posts, setPosts] = useState([])
const [accordionKey, setAccordionKey] = useState('0')
const [loaded, setLoaded] = useState(false)
useEffect(() => {
// Get Last post from a author
const start = () => {
const { nostrKeyPair } = bchWalletState
const psf = 'wss://nostr-relay.psfoundation.info'
const pool = RelayPool([psf])
pool.on('open', relay => {
relay.subscribe('subid', { limit: 10, kinds: [0], authors: [nostrKeyPair.pubHex] })
})
pool.on('eose', relay => {
console.log('Closing Relay')
relay.close()
})
pool.on('event', (relay, subId, ev) => {
console.log('Received event:', ev)
setPosts(currentPosts => [...currentPosts, ev])
})
setLoaded(true)
}
if (!loaded) {
start()
}
}, [bchWalletState, loaded])
const handleAccordionChange = (key) => {
setAccordionKey(key)
}
return (
<Container>
<Accordion activeKey={accordionKey} onSelect={handleAccordionChange}>
<Accordion.Item eventKey='0'>
<Accordion.Header>Read ( {posts.length} post found )</Accordion.Header>
<Accordion.Body>
{posts.map((post, index) => (
<div key={index}>
<span>{post.content}</span>
</div>
))}
</Accordion.Body>
</Accordion.Item>
</Accordion>
</Container>
)
}
export default NostrRead
+4 -1
View File
@@ -41,7 +41,10 @@ const SweepWif = (props) => {
// Handle sweep function
const handleSweep = async (e) => {
e.preventDefault()
if (e) {
e.preventDefault()
}
try {
console.log(`Sweeping this WIF: ${wifToSweep}`)
+14
View File
@@ -100,6 +100,20 @@ function NavMenu (props) {
>
Configuration
</NavLink>
<NavLink
className={currentPath === '/nostr-post' ? 'nav-link-active' : 'nav-link-inactive'}
to='/nostr-post'
onClick={handleClickEvent}
>
Nostr Post
</NavLink>
<NavLink
className={currentPath === '/nostr-read' ? 'nav-link-active' : 'nav-link-inactive'}
to='/nostr-read'
onClick={handleClickEvent}
>
Nostr Profile
</NavLink>
</Nav>
</Navbar.Collapse>
</Navbar>
+4 -1
View File
@@ -14,7 +14,10 @@ const config = {
ghRepo: 'https://github.com/Permissionless-Software-Foundation/bch-dex-taker-v2',
radicleUrl: 'https://app.radicle.network/seeds/maple.radicle.garden/rad:git:hnrkd5cjwwb5tzx37hq9uqm5ubon7ee468xcy/remotes/hyyycncbn9qzqmobnhjq9rry6t4mbjiadzjoyhaknzxjcz3cxkpfpc',
dexServer: 'https://dex-api.fullstack.cash'
dexServer: 'https://dex-api.fullstack.cash',
nostrTopic: 'bch-dex-test-topic-02',
nostrRelay: 'wss://nostr-relay.psfoundation.info'
}
+31 -1
View File
@@ -10,6 +10,9 @@ import BchDexLib from 'bch-dex-lib'
import GistServers from './gist-servers'
import Nostr from './nostr'
import P2WDB from 'p2wdb'
import { base58_to_binary as base58ToBinary } from 'base58-js'
import { bytesToHex } from '@noble/hashes/utils' // already an installed dependency
import { getPublicKey } from 'nostr-tools/pure'
class AsyncLoad {
constructor () {
@@ -59,8 +62,14 @@ class AsyncLoad {
await wallet.walletInfoPromise
await wallet.initialize()
console.log('starting to update wallet state.')
// Get Nostr key pair from WIF
const nostrKeyPair = this.nostrKeyPairFromWIF(wallet.walletInfo.privateKey)
const walletInfo = wallet.walletInfo
walletInfo.nostrKeyPair = nostrKeyPair
// Update the state of the wallet.
appData.updateBchWalletState({ walletObj: wallet.walletInfo, appData })
appData.updateBchWalletState({ walletObj: walletInfo, appData })
console.log('finished updating wallet state.')
// Save the mnemonic to local storage.
if (!mnemonic) {
@@ -246,6 +255,27 @@ class AsyncLoad {
throw error
}
}
// Get Nostr key pair from WIF
nostrKeyPairFromWIF (WIF) {
if (!WIF) return
// Extract the privaty key from the WIF, using this guide:
// https://learnmeabitcoin.com/technical/keys/private-key/wif/
const wifBuf = base58ToBinary(WIF)
const privBuf = wifBuf.slice(1, 33)
// console.log('privBuf: ', privBuf)
const privHex = bytesToHex(privBuf)
console.log('BCH & Nostr private key (HEX format): ', privHex)
const pubHex = getPublicKey(privBuf)
return {
privHex,
pubHex
}
}
}
function sleep (ms) {
+5 -4
View File
@@ -13,6 +13,7 @@ import { finalizeEvent } from 'nostr-tools/pure'
import { Relay } from 'nostr-tools/relay'
import BchNostr from 'bch-nostr'
import * as nip19 from 'nostr-tools/nip19'
import config from '../config/index.js'
class NostrBrowser {
constructor (localConfig = {}) {
@@ -23,8 +24,8 @@ class NostrBrowser {
this.bchWallet = localConfig.bchWallet
this.bchNostr = new BchNostr({
relayWs: 'wss://nostr-relay.psfoundation.info',
topic: 'bch-dex-test-topic-01'
relayWs: config.nostrRelay,
topic: config.nostrTopic
})
}
@@ -68,11 +69,11 @@ class NostrBrowser {
// tags: [['t', 'bch-dex-test-topic-01']]
// }
const relayWs = 'wss://nostr-relay.psfoundation.info'
const relayWs = config.nostrRelay
const eventTemplate = {
kind: 867,
created_at: Math.floor(Date.now() / 1000),
tags: [['t', 'bch-dex-test-topic-01']],
tags: [['t', config.nostrTopic]],
content: msg
}