mirror of
https://github.com/Permissionless-Software-Foundation/bch-dex-taker-v2.git
synced 2026-09-21 16:52:01 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
313df177e9 | ||
|
|
2c34ea5c2e | ||
|
|
aa2d92bfaf | ||
|
|
e9e9982de0 | ||
|
|
5a3010ffce | ||
|
|
40467014f0 | ||
|
|
d94b3fc853 | ||
|
|
51ed5601fa |
Generated
+1654
-6441
File diff suppressed because it is too large
Load Diff
+13
-2
@@ -9,7 +9,7 @@
|
|||||||
"@fortawesome/react-fontawesome": "0.2.2",
|
"@fortawesome/react-fontawesome": "0.2.2",
|
||||||
"@noble/hashes": "1.8.0",
|
"@noble/hashes": "1.8.0",
|
||||||
"axios": "0.27.2",
|
"axios": "0.27.2",
|
||||||
"bch-dex-lib": "2.2.0",
|
"bch-dex-lib": "2.3.1",
|
||||||
"bch-message-lib": "2.2.1",
|
"bch-message-lib": "2.2.1",
|
||||||
"bch-nostr": "1.3.4",
|
"bch-nostr": "1.3.4",
|
||||||
"bch-token-sweep": "2.2.1",
|
"bch-token-sweep": "2.2.1",
|
||||||
@@ -40,7 +40,8 @@
|
|||||||
"eject": "react-app-rewired eject",
|
"eject": "react-app-rewired eject",
|
||||||
"lint": "standard --env mocha --fix",
|
"lint": "standard --env mocha --fix",
|
||||||
"pub": "node deploy/publish-main.js",
|
"pub": "node deploy/publish-main.js",
|
||||||
"pub:ghp": "./deploy/publish-gh-pages.sh"
|
"pub:ghp": "./deploy/publish-gh-pages.sh",
|
||||||
|
"postinstall": "rm -rf node_modules/fork-ts-checker-webpack-plugin/node_modules 2>/dev/null || true && (cd node_modules/@eslint/eslintrc && npm install ajv@^6.12.6 --no-save 2>/dev/null || true) && (cd node_modules/eslint && npm install ajv@^6.12.6 --no-save 2>/dev/null || true)"
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"extends": "react-app"
|
"extends": "react-app"
|
||||||
@@ -66,6 +67,16 @@
|
|||||||
"standard": "17.0.0",
|
"standard": "17.0.0",
|
||||||
"web3.storage": "4.3.0"
|
"web3.storage": "4.3.0"
|
||||||
},
|
},
|
||||||
|
"overrides": {
|
||||||
|
"ajv": "^8.17.1",
|
||||||
|
"ajv-keywords": "^5.1.0",
|
||||||
|
"ajv-formats": "^2.1.1",
|
||||||
|
"babel-loader": {
|
||||||
|
"schema-utils": "^2.7.1",
|
||||||
|
"ajv-keywords": "^3.5.2",
|
||||||
|
"ajv": "^6.12.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"release": {
|
"release": {
|
||||||
"publish": [
|
"publish": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
This Card component displays a counter offer with token icon, name, and price.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Global npm libraries
|
||||||
|
import React, { useState } from 'react'
|
||||||
|
import { Container, Row, Col, Card, Button } from 'react-bootstrap'
|
||||||
|
import Jdenticon from '@chris.troutner/react-jdenticon'
|
||||||
|
|
||||||
|
function CounterOfferCard (props) {
|
||||||
|
const { offer } = props
|
||||||
|
const [icon, setIcon] = useState(offer.tokenIcon)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Col xs={12} sm={6} lg={4} style={{ padding: '25px' }}>
|
||||||
|
<Card className='shadow-sm'>
|
||||||
|
<Card.Body style={{ textAlign: 'center', padding: '10px' }}>
|
||||||
|
{/** If the icon is loaded, display it */}
|
||||||
|
{icon && (
|
||||||
|
<Card.Img
|
||||||
|
src={icon}
|
||||||
|
style={{ height: '100px', width: 'auto', margin: '0 auto' }}
|
||||||
|
onError={(e) => {
|
||||||
|
setIcon(null) // Set the icon to null if it fails to load the image url.
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/** If the icon is not loaded, display the Jdenticon */}
|
||||||
|
{!icon && (
|
||||||
|
<div style={{ display: 'flex', justifyContent: 'center', marginBottom: '10px' }}>
|
||||||
|
<Jdenticon size='100' value={offer.tokenId} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Card.Title style={{ textAlign: 'center', marginTop: '10px' }}>
|
||||||
|
<h4>{offer.ticker}</h4>
|
||||||
|
</Card.Title>
|
||||||
|
|
||||||
|
<Container>
|
||||||
|
<Row>
|
||||||
|
<Col>
|
||||||
|
{/* <strong>{offer.tokenName}</strong> */}
|
||||||
|
<strong>Counter Offer UTXO</strong>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<Row>
|
||||||
|
{/* <Col>Price:</Col> */}
|
||||||
|
<Col><strong>{offer.price}</strong></Col>
|
||||||
|
</Row>
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<Row className='text-center'>
|
||||||
|
<Col>
|
||||||
|
<Button disabled variant='danger' size='sm'>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Container>
|
||||||
|
</Card.Body>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CounterOfferCard
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
Shows Counter Offers created by the user.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Global npm libraries
|
||||||
|
import React, { useState, useEffect } from 'react'
|
||||||
|
import { Container, Row, Col, Spinner } from 'react-bootstrap'
|
||||||
|
|
||||||
|
// Local libraries
|
||||||
|
import CounterOfferCard from './counter-offer-card'
|
||||||
|
import AsyncLoad from '../../../services/async-load'
|
||||||
|
|
||||||
|
function CounterOffers (props) {
|
||||||
|
const appData = props.appData
|
||||||
|
|
||||||
|
const [counterOffers, setCounterOffers] = useState([])
|
||||||
|
const [isLoading, setIsLoading] = useState(true)
|
||||||
|
|
||||||
|
// Generate counter offer cards
|
||||||
|
const generateCards = () => {
|
||||||
|
return counterOffers.map((offer) => (
|
||||||
|
<CounterOfferCard
|
||||||
|
key={offer.id}
|
||||||
|
offer={offer}
|
||||||
|
appData={appData}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const loadWallet = async () => {
|
||||||
|
try {
|
||||||
|
setIsLoading(true)
|
||||||
|
const { bchWalletState, serverUrl } = appData
|
||||||
|
const asyncLoad = new AsyncLoad()
|
||||||
|
await asyncLoad.loadWalletLib()
|
||||||
|
const counterOfferWallet = await asyncLoad.getDerivatedWallet(serverUrl, bchWalletState.mnemonic, "m/44'/245'/0'/0/1")
|
||||||
|
|
||||||
|
const utxoStore = counterOfferWallet.utxos.utxoStore
|
||||||
|
const bchUtxos = utxoStore.bchUtxos
|
||||||
|
setCounterOffers(bchUtxos)
|
||||||
|
console.log('counterOffers', bchUtxos)
|
||||||
|
setIsLoading(false)
|
||||||
|
} catch (error) {
|
||||||
|
setIsLoading(false)
|
||||||
|
console.log('error', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loadWallet()
|
||||||
|
}, [appData])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<Row>
|
||||||
|
<Col>
|
||||||
|
<h1>Counter Offers</h1>
|
||||||
|
<p className='text-muted'>Your pending counter offers</p>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
{isLoading
|
||||||
|
? (
|
||||||
|
<Row className='text-center' style={{ padding: '50px' }}>
|
||||||
|
<Col>
|
||||||
|
<Spinner animation='border' role='status'>
|
||||||
|
<span className='visually-hidden'>Loading...</span>
|
||||||
|
</Spinner>
|
||||||
|
<p className='mt-3 text-muted'>Loading counter offers...</p>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
)
|
||||||
|
: (
|
||||||
|
<>
|
||||||
|
<Row>
|
||||||
|
{generateCards()}
|
||||||
|
</Row>
|
||||||
|
{counterOffers.length === 0 && (
|
||||||
|
<Row className='text-center'>
|
||||||
|
<Col>
|
||||||
|
<p>No counter offers found.</p>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Container>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CounterOffers
|
||||||
@@ -30,6 +30,7 @@ import ContentCreators from './nostr/content-creators/index.js'
|
|||||||
import UserDataReview from './user-data-review'
|
import UserDataReview from './user-data-review'
|
||||||
import Offers from './offers'
|
import Offers from './offers'
|
||||||
import NostrChat from './nostr-chat'
|
import NostrChat from './nostr-chat'
|
||||||
|
import CounterOffers from './counter-offers'
|
||||||
function AppBody (props) {
|
function AppBody (props) {
|
||||||
// Dependency injection through props
|
// Dependency injection through props
|
||||||
const appData = props.appData
|
const appData = props.appData
|
||||||
@@ -55,6 +56,7 @@ function AppBody (props) {
|
|||||||
<Route path='/content-creators' element={<ContentCreators appData={appData} />} />
|
<Route path='/content-creators' element={<ContentCreators appData={appData} />} />
|
||||||
<Route path='/user-data/:tokenId' element={<UserDataReview appData={appData} />} />
|
<Route path='/user-data/:tokenId' element={<UserDataReview appData={appData} />} />
|
||||||
<Route path='/offers' element={<Offers appData={appData} />} />
|
<Route path='/offers' element={<Offers appData={appData} />} />
|
||||||
|
<Route path='/counter-offers' element={<CounterOffers appData={appData} />} />
|
||||||
<Route path='/nostr-chat' element={<NostrChat appData={appData} />} />
|
<Route path='/nostr-chat' element={<NostrChat appData={appData} />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
{/** Show in all paths except the servers view */}
|
{/** Show in all paths except the servers view */}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
// Global npm libraries
|
// Global npm libraries
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { Button, Modal, Container, Row, Col, Spinner } from 'react-bootstrap'
|
import { Button, Modal, Container, Row, Col, Spinner } from 'react-bootstrap'
|
||||||
|
import AsyncLoad from '../../../services/async-load'
|
||||||
|
|
||||||
function BuyButton (props) {
|
function BuyButton (props) {
|
||||||
const { token, appData, onSuccess } = props
|
const { token, appData, onSuccess } = props
|
||||||
@@ -45,10 +46,19 @@ function BuyButton (props) {
|
|||||||
|
|
||||||
// Generate a counter offer.
|
// Generate a counter offer.
|
||||||
const bchDexLib = appData.dexLib
|
const bchDexLib = appData.dexLib
|
||||||
const { offerData, partialHex } = await bchDexLib.take.takeOffer(
|
const { offerData, partialHex, counterOfferUtxo } = await bchDexLib.take.takeOffer(
|
||||||
targetOffer
|
targetOffer
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Get counter offer data
|
||||||
|
const asyncLoad = new AsyncLoad()
|
||||||
|
const { takerAddr, takerNpub, counterOfferAddr } = await asyncLoad.getCounterOfferMetadata(appData)
|
||||||
|
|
||||||
|
offerData.takerAddr = takerAddr
|
||||||
|
offerData.takerNpub = takerNpub
|
||||||
|
offerData.counterOfferAddr = counterOfferAddr
|
||||||
|
offerData.counterOfferUtxo = counterOfferUtxo.txid
|
||||||
|
|
||||||
progress.push(<p key='progress-msg2'>Uploading counter offer to Nostr...</p>)
|
progress.push(<p key='progress-msg2'>Uploading counter offer to Nostr...</p>)
|
||||||
setProgressMsg(progress)
|
setProgressMsg(progress)
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import React, { useState, useEffect, useCallback } from 'react'
|
|||||||
import { Container, Row, Col, Table, Button, Spinner } from 'react-bootstrap'
|
import { Container, Row, Col, Table, Button, Spinner } from 'react-bootstrap'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { DatatableWrapper, TableBody, TableHeader } from 'react-bs-datatable'
|
import { DatatableWrapper, TableBody, TableHeader } from 'react-bs-datatable'
|
||||||
|
import AsyncLoad from '../../../services/async-load'
|
||||||
// Local libraries
|
// Local libraries
|
||||||
import config from '../../../config'
|
import config from '../../../config'
|
||||||
import WaitingModal from '../../waiting-modal'
|
import WaitingModal from '../../waiting-modal'
|
||||||
@@ -100,10 +100,19 @@ function Offers (props) {
|
|||||||
|
|
||||||
// Generate a counter offer.
|
// Generate a counter offer.
|
||||||
const bchDexLib = appData.dexLib
|
const bchDexLib = appData.dexLib
|
||||||
const { offerData, partialHex } = await bchDexLib.take.takeOffer(
|
const { offerData, partialHex, counterOfferUtxo } = await bchDexLib.take.takeOffer(
|
||||||
targetOfferEventId
|
targetOfferEventId
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Get counter offer data
|
||||||
|
const asyncLoad = new AsyncLoad()
|
||||||
|
const { takerAddr, takerNpub, counterOfferAddr } = await asyncLoad.getCounterOfferMetadata(appData)
|
||||||
|
|
||||||
|
offerData.takerAddr = takerAddr
|
||||||
|
offerData.takerNpub = takerNpub
|
||||||
|
offerData.counterOfferAddr = counterOfferAddr
|
||||||
|
offerData.counterOfferUtxo = counterOfferUtxo.txid
|
||||||
|
|
||||||
// Upload the counter offer to Nostr.
|
// Upload the counter offer to Nostr.
|
||||||
const nostr = appData.nostr
|
const nostr = appData.nostr
|
||||||
const { eventId, noteId } = await nostr.testNostrUpload({
|
const { eventId, noteId } = await nostr.testNostrUpload({
|
||||||
|
|||||||
@@ -58,6 +58,14 @@ function NavMenu (props) {
|
|||||||
Fungible Tokens
|
Fungible Tokens
|
||||||
</NavLink>
|
</NavLink>
|
||||||
|
|
||||||
|
<NavLink
|
||||||
|
className={currentPath === '/counter-offers' ? 'nav-link-active' : 'nav-link-inactive'}
|
||||||
|
to='/counter-offers'
|
||||||
|
onClick={handleClickEvent}
|
||||||
|
>
|
||||||
|
Counter Offers
|
||||||
|
</NavLink>
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<NavLink
|
<NavLink
|
||||||
|
|||||||
@@ -333,6 +333,59 @@ class AsyncLoad {
|
|||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getDerivatedWallet (restURL, mnemonic, hdPath = "m/44'/245'/0'/0/0", initialize = true) {
|
||||||
|
try {
|
||||||
|
const options = {
|
||||||
|
interface: 'consumer-api',
|
||||||
|
restURL,
|
||||||
|
noUpdate: true,
|
||||||
|
hdPath
|
||||||
|
}
|
||||||
|
|
||||||
|
const wallet = new this.BchWallet(mnemonic, options)
|
||||||
|
|
||||||
|
// Wait for wallet to initialize.
|
||||||
|
await wallet.walletInfoPromise
|
||||||
|
if (initialize) {
|
||||||
|
await wallet.initialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
return wallet
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error initStarterWallet: ', error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get buyer and counter offer data.
|
||||||
|
async getCounterOfferMetadata (appData) {
|
||||||
|
try {
|
||||||
|
const { bchWalletState, serverUrl } = appData
|
||||||
|
// Start async load lib
|
||||||
|
const asyncLoad = new AsyncLoad()
|
||||||
|
await asyncLoad.loadWalletLib()
|
||||||
|
|
||||||
|
// Buyer wallet data
|
||||||
|
const buyerWallet = await asyncLoad.getDerivatedWallet(serverUrl, bchWalletState.mnemonic, "m/44'/245'/0'/0/0", false)
|
||||||
|
const buyerAddr = buyerWallet.walletInfo.cashAddress
|
||||||
|
const buyerKeyPair = asyncLoad.nostrKeyPairFromWIF(buyerWallet.walletInfo.privateKey)
|
||||||
|
const buyerNpub = buyerKeyPair.npub
|
||||||
|
|
||||||
|
// Counter offer wallet data
|
||||||
|
const counterOfferWallet = await asyncLoad.getDerivatedWallet(serverUrl, bchWalletState.mnemonic, "m/44'/245'/0'/0/1", false)
|
||||||
|
const counterOfferAddr = counterOfferWallet.walletInfo.cashAddress
|
||||||
|
|
||||||
|
return {
|
||||||
|
takerAddr: buyerAddr,
|
||||||
|
takerNpub: buyerNpub,
|
||||||
|
counterOfferAddr
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error getCounterOfferMetadata()', error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function sleep (ms) {
|
function sleep (ms) {
|
||||||
|
|||||||
Reference in New Issue
Block a user