feat(nostr): Generate Nostr nsec and npub

This commit is contained in:
Daniel Gonzalez
2025-05-21 14:59:10 -04:00
parent 55b87d4e9d
commit a77147d077
3 changed files with 136 additions and 4 deletions
+65 -2
View File
@@ -13,6 +13,7 @@
"@fortawesome/free-regular-svg-icons": "6.7.2",
"@fortawesome/free-solid-svg-icons": "6.7.2",
"@fortawesome/react-fontawesome": "0.2.2",
"@noble/hashes": "^1.8.0",
"axios": "0.27.2",
"bch-dex-lib": "2.0.2",
"bch-message-lib": "2.2.1",
@@ -1929,6 +1930,18 @@
"nostr-wasm": "0.1.0"
}
},
"node_modules/@chris.troutner/nostr-tools/node_modules/@noble/hashes": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz",
"integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==",
"license": "MIT",
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@chris.troutner/react-jdenticon": {
"version": "1.0.1",
"license": "MIT",
@@ -2976,10 +2989,12 @@
"license": "MIT"
},
"node_modules/@noble/hashes": {
"version": "1.3.1",
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz",
"integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==",
"license": "MIT",
"engines": {
"node": ">= 16"
"node": "^14.21.3 || >=16"
},
"funding": {
"url": "https://paulmillr.com/funding/"
@@ -3657,6 +3672,30 @@
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@scure/bip32/node_modules/@noble/curves/node_modules/@noble/hashes": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz",
"integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==",
"license": "MIT",
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@scure/bip32/node_modules/@noble/hashes": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz",
"integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==",
"license": "MIT",
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@scure/bip39": {
"version": "1.2.1",
"license": "MIT",
@@ -3668,6 +3707,18 @@
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@scure/bip39/node_modules/@noble/hashes": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz",
"integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==",
"license": "MIT",
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@sec-ant/readable-stream": {
"version": "0.4.1",
"resolved": "http://94.130.170.209:4873/@sec-ant%2freadable-stream/-/readable-stream-0.4.1.tgz",
@@ -15150,6 +15201,18 @@
}
}
},
"node_modules/nostr-tools/node_modules/@noble/hashes": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz",
"integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==",
"license": "MIT",
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/nostr-wasm": {
"version": "0.1.0",
"license": "MIT",
+1
View File
@@ -7,6 +7,7 @@
"@fortawesome/free-regular-svg-icons": "6.7.2",
"@fortawesome/free-solid-svg-icons": "6.7.2",
"@fortawesome/react-fontawesome": "0.2.2",
"@noble/hashes": "^1.8.0",
"axios": "0.27.2",
"bch-dex-lib": "2.0.2",
"bch-message-lib": "2.2.1",
@@ -3,7 +3,7 @@
*/
// Global npm libraries
import React, { useState } from 'react'
import React, { useCallback, useEffect, 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,17 +12,26 @@ 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
const appData = props.appData
const bchWalletState = appData.bchWalletState
console.log('wallet summary state: ', bchWalletState)
// State
const [blurredMnemonic, setBlurredMnemonic] = useState(true)
const [blurredPrivateKey, setBlurredPrivateKey] = useState(true)
const [blurredNostrPrivKey, setBlurredNostrPrivKey] = useState(true)
const [nostrKeyPair, setNostrKeyPair] = useState({
privHex: '',
pubHex: ''
})
// Encapsulate component state into an object that can be passed to child functions
const walletSummaryData = {
@@ -64,6 +73,39 @@ function WalletSummary (props) {
}
}
// Toggle the state of blurring for the private key
const toggleNostrPrivateKeyBlur = (inObj = {}) => {
try {
setBlurredNostrPrivKey(!blurredNostrPrivKey)
} catch (error) {
console.error('Error toggling private key blur: ', error)
}
}
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>
@@ -151,6 +193,32 @@ function WalletSummary (props) {
<CopyOnClick walletProp='hdPath' appData={appData} value={bchWalletState.hdPath} />
</Col>
</Row>
<Row style={{ padding: '25px', backgroundColor: '#eee' }}>
<Col xs={10} sm={10} lg={8} style={{ padding: '10px' }}>
<b>Nostr Priv Key:</b> <span className={blurredNostrPrivKey ? 'blurred' : null}>{nostrKeyPair.privHex}</span>
</Col>
<Col xs={6} sm={1} lg={2} style={{ textAlign: 'center' }}>
<FontAwesomeIcon
style={{ cursor: 'pointer' }}
icon={eyeIcon.privateKey}
size='lg'
onClick={() => toggleNostrPrivateKeyBlur(nostrKeyPair.privHex)}
/>
</Col>
<Col xs={6} sm={1} lg={2} style={{ textAlign: 'center' }}>
<CopyOnClick appData={appData} value={nostrKeyPair.privHex} />
</Col>
</Row>
<Row style={{ padding: '25px', backgroundColor: '#eee' }}>
<Col xs={10} sm={10} lg={8} style={{ padding: '10px' }}>
<b>Nostr Pub Key:</b> {nostrKeyPair.pubHex}
</Col>
<Col xs={6} sm={1} lg={2} style={{ textAlign: 'center' }} />
<Col xs={6} sm={1} lg={2} style={{ textAlign: 'center' }}>
<CopyOnClick appData={appData} value={nostrKeyPair.pubHex} />
</Col>
</Row>
</Container>
</Card.Body>
</Card>