mirror of
https://github.com/Permissionless-Software-Foundation/bch-dex.git
synced 2026-09-22 09:11:59 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e8104a8a12 | ||
|
|
2c43793bf2 | ||
|
|
b0eda392a6 | ||
|
|
f81d6853fc | ||
|
|
02a889385d | ||
|
|
f17b21bee8 | ||
|
|
78b9e4fcb1 | ||
|
|
eb1bee1b61 | ||
|
|
611c2d1631 | ||
|
|
cb9f5af62e | ||
|
|
2fca908c97 | ||
|
|
3aba1e061b | ||
|
|
c398942c95 | ||
|
|
c2bffd8c4e | ||
|
|
ac97831721 | ||
|
|
02eab8686c | ||
|
|
1c1169dd93 | ||
|
|
0896adffbb | ||
|
|
56e0f7e8df | ||
|
|
34841f53db | ||
|
|
c27209cceb |
Generated
+11
-8
@@ -38,7 +38,7 @@
|
||||
"mongoose": "5.13.13",
|
||||
"node-fetch": "npm:@achingbrain/node-fetch@2.6.7",
|
||||
"nodemailer": "6.7.5",
|
||||
"p2wdb": "2.2.1",
|
||||
"p2wdb": "2.2.3",
|
||||
"passport-local": "1.0.0",
|
||||
"public-ip": "^4.0.4",
|
||||
"winston": "3.3.3",
|
||||
@@ -14738,9 +14738,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/p2wdb": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/p2wdb/-/p2wdb-2.2.1.tgz",
|
||||
"integrity": "sha512-ft/mGRRLXqWfcodiL7d/403QfImOflpw8BJBpULM0F6rGt/8sTY6i5W3NCyUQp3Mx39xIEw/wTgdb32Hn5Uomg==",
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/p2wdb/-/p2wdb-2.2.3.tgz",
|
||||
"integrity": "sha512-eZQaE3iVudOJV6Iji/fBO20tYhs23EmtcxLZbXK7ZjY+c0NysBzhS0mGCkEUWrU/qBthjIEZ2O5detuAHsmHCw==",
|
||||
"dependencies": {
|
||||
"apidoc": "0.52.0",
|
||||
"axios": "0.24.0"
|
||||
@@ -14788,7 +14788,8 @@
|
||||
},
|
||||
"node_modules/p2wdb/node_modules/axios": {
|
||||
"version": "0.24.0",
|
||||
"license": "MIT",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz",
|
||||
"integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.14.4"
|
||||
}
|
||||
@@ -29378,9 +29379,9 @@
|
||||
"version": "1.0.1"
|
||||
},
|
||||
"p2wdb": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/p2wdb/-/p2wdb-2.2.1.tgz",
|
||||
"integrity": "sha512-ft/mGRRLXqWfcodiL7d/403QfImOflpw8BJBpULM0F6rGt/8sTY6i5W3NCyUQp3Mx39xIEw/wTgdb32Hn5Uomg==",
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/p2wdb/-/p2wdb-2.2.3.tgz",
|
||||
"integrity": "sha512-eZQaE3iVudOJV6Iji/fBO20tYhs23EmtcxLZbXK7ZjY+c0NysBzhS0mGCkEUWrU/qBthjIEZ2O5detuAHsmHCw==",
|
||||
"requires": {
|
||||
"apidoc": "0.52.0",
|
||||
"axios": "0.24.0"
|
||||
@@ -29415,6 +29416,8 @@
|
||||
},
|
||||
"axios": {
|
||||
"version": "0.24.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz",
|
||||
"integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==",
|
||||
"requires": {
|
||||
"follow-redirects": "^1.14.4"
|
||||
}
|
||||
|
||||
+1
-1
@@ -56,7 +56,7 @@
|
||||
"mongoose": "5.13.13",
|
||||
"node-fetch": "npm:@achingbrain/node-fetch@2.6.7",
|
||||
"nodemailer": "6.7.5",
|
||||
"p2wdb": "2.2.1",
|
||||
"p2wdb": "2.2.3",
|
||||
"passport-local": "1.0.0",
|
||||
"public-ip": "^4.0.4",
|
||||
"winston": "3.3.3",
|
||||
|
||||
@@ -46,7 +46,7 @@ services:
|
||||
#build:
|
||||
# context: ./p2wdb/
|
||||
# dockerfile: Dockerfile
|
||||
image: christroutner/p2wdb:v3.0.9
|
||||
image: christroutner/p2wdb:v3.1.5
|
||||
container_name: p2wdb
|
||||
environment:
|
||||
CONSUMER_URL: 'https://free-bch.fullstack.cash'
|
||||
@@ -73,7 +73,7 @@ services:
|
||||
#build:
|
||||
# context: ./bch-dex/
|
||||
# dockerfile: Dockerfile
|
||||
image: christroutner/bch-dex:v1.9.6
|
||||
image: christroutner/bch-dex:v1.13.0
|
||||
container_name: bch-dex
|
||||
environment:
|
||||
CONSUMER_URL: 'https://free-bch.fullstack.cash'
|
||||
@@ -97,7 +97,7 @@ services:
|
||||
#build:
|
||||
# context: ./bch-dex-ui/
|
||||
# dockerfile: Dockerfile
|
||||
image: christroutner/bch-dex-ui:v1.6.6
|
||||
image: christroutner/bch-dex-ui:v1.12.0
|
||||
container_name: dex-ui
|
||||
environment:
|
||||
SERVER: 'http://192.168.2.3'
|
||||
|
||||
@@ -43,10 +43,10 @@ services:
|
||||
#build:
|
||||
# context: ./p2wdb/
|
||||
# dockerfile: Dockerfile
|
||||
image: christroutner/p2wdb-rpi:v3.0.10
|
||||
image: christroutner/p2wdb-rpi:v3.1.5
|
||||
container_name: p2wdb
|
||||
environment:
|
||||
CONSUMER_URL: 'https://free-bch.fullstack.cash'
|
||||
CONSUMER_URL: 'https://wa-usa-bch-consumer.fullstackcash.nl'
|
||||
DEBUG_LEVEL: 1
|
||||
logging:
|
||||
driver: 'json-file'
|
||||
@@ -69,11 +69,12 @@ services:
|
||||
bch-dex:
|
||||
#build:
|
||||
# context: ./bch-dex/
|
||||
# dockerfile: Dockerfile
|
||||
image: christroutner/bch-dex-rpi:v1.9.6
|
||||
# dockerfile: Dockerfile
|
||||
image: christroutner/bch-dex-rpi:v1.13.0
|
||||
container_name: bch-dex
|
||||
environment:
|
||||
CONSUMER_URL: 'https://free-bch.fullstack.cash'
|
||||
#CONSUMER_URL: 'https://free-bch.fullstack.cash'
|
||||
CONSUMER_URL: 'https://wa-usa-bch-consumer.fullstackcash.nl'
|
||||
logging:
|
||||
driver: 'json-file'
|
||||
options:
|
||||
@@ -94,7 +95,7 @@ services:
|
||||
#build:
|
||||
# context: ./bch-dex-ui/
|
||||
# dockerfile: Dockerfile
|
||||
image: christroutner/bch-dex-ui-rpi:v1.7.5
|
||||
image: christroutner/bch-dex-ui-rpi:v1.13.0
|
||||
container_name: dex-ui
|
||||
logging:
|
||||
driver: 'json-file'
|
||||
|
||||
@@ -64,6 +64,8 @@ async function sweepNfts () {
|
||||
if (thisToken.tokenType === 65) {
|
||||
console.log(`NFT ${thisToken.ticker} (${thisToken.name}) found. Sweeping to ${rootAddr}`)
|
||||
|
||||
await walletRoot.initialize()
|
||||
|
||||
// Send BCH to the child address, to pay for transaction fees.
|
||||
const receiver1 = [{
|
||||
address: cashAddress,
|
||||
@@ -76,6 +78,8 @@ async function sweepNfts () {
|
||||
console.log('Waiting for indexers to update...')
|
||||
await bchjs.Util.sleep(120000)
|
||||
|
||||
await walletChild.initialize()
|
||||
|
||||
// Send NFT to root address.
|
||||
const receiver2 = [{
|
||||
address: rootAddr,
|
||||
|
||||
@@ -71,6 +71,10 @@ class Adapters {
|
||||
|
||||
// Wait until a webhook is established with the P2WDB
|
||||
await this.webhook.waitUntilSuccess(this.config.webhookTarget)
|
||||
|
||||
// Overwrite instances of wallet used by P2WDB lib.
|
||||
this.p2wdb.bchWallet = this.wallet.bchWallet
|
||||
this.p2wdb.bchjs = this.wallet.bchWallet.bchjs
|
||||
}
|
||||
|
||||
console.log('Async Adapters have been started.')
|
||||
|
||||
@@ -27,6 +27,9 @@ const Offer = new mongoose.Schema({
|
||||
timestamp: { type: Number },
|
||||
globalTimestamp: { type: String },
|
||||
localTimestamp: { type: String },
|
||||
displayCategory: { type: String },
|
||||
nsfw: { type: Boolean, default: false },
|
||||
flags: { type: Array },
|
||||
|
||||
// SWaP Protocol Properties
|
||||
lokadId: { type: String },
|
||||
|
||||
@@ -49,14 +49,47 @@ class OfferRESTControllerLib {
|
||||
}
|
||||
}
|
||||
|
||||
// curl -X GET http://localhost:5700/offer/list
|
||||
// curl -X GET http://localhost:5700/offer/list/all/0
|
||||
async listOffers (ctx) {
|
||||
try {
|
||||
const offers = await _this.useCases.offer.listOffers()
|
||||
let page = ctx.params.page
|
||||
if (!page) page = 0
|
||||
|
||||
const offers = await _this.useCases.offer.listOffers(page)
|
||||
|
||||
ctx.body = offers
|
||||
} catch (err) {
|
||||
console.log('Error in listOffers REST API handler.')
|
||||
console.log('Error in listOffers REST API handler: ', err)
|
||||
_this.handleError(ctx, err)
|
||||
}
|
||||
}
|
||||
|
||||
// curl -X GET http://localhost:5700/offer/list/nft/0
|
||||
async listNftOffers (ctx) {
|
||||
try {
|
||||
let page = ctx.params.page
|
||||
if (!page) page = 0
|
||||
|
||||
const offers = await _this.useCases.offer.listNftOffers(page)
|
||||
|
||||
ctx.body = offers
|
||||
} catch (err) {
|
||||
console.log('Error in listNftOffers REST API handler: ', err)
|
||||
_this.handleError(ctx, err)
|
||||
}
|
||||
}
|
||||
|
||||
// curl -X GET http://localhost:5700/offer/list/fungible/0
|
||||
async listFungibleOffers (ctx) {
|
||||
try {
|
||||
let page = ctx.params.page
|
||||
if (!page) page = 0
|
||||
|
||||
const offers = await _this.useCases.offer.listFungibleOffers(page)
|
||||
|
||||
ctx.body = offers
|
||||
} catch (err) {
|
||||
console.log('Error in Fungible REST API handler: ', err)
|
||||
_this.handleError(ctx, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,8 +50,10 @@ class OfferRouter {
|
||||
|
||||
// Define the routes and attach the controller.
|
||||
this.router.post('/', _this.offerRESTController.createOffer)
|
||||
this.router.get('/list', _this.offerRESTController.listOffers)
|
||||
this.router.post('/take', _this.offerRESTController.takeOffer)
|
||||
this.router.get('/list/all/:page', _this.offerRESTController.listOffers)
|
||||
this.router.get('/list/nft/:page', _this.offerRESTController.listNftOffers)
|
||||
this.router.get('/list/fungible/:page', _this.offerRESTController.listFungibleOffers)
|
||||
|
||||
// Attach the Controller routes to the Koa app.
|
||||
app.use(_this.router.routes())
|
||||
|
||||
@@ -45,12 +45,23 @@ class P2WDBRESTControllerLib {
|
||||
const counterOffer = ctx.request.body
|
||||
|
||||
await _this.useCases.offer.acceptCounterOffer(counterOffer)
|
||||
|
||||
//
|
||||
} else if (dataType.includes('offer')) {
|
||||
// Detect and handle new Offers
|
||||
console.log('offer data detected')
|
||||
|
||||
const offerObj = ctx.request.body
|
||||
await _this.useCases.offer.createOffer(offerObj)
|
||||
|
||||
//
|
||||
} else if (dataType.includes('flag')) {
|
||||
// Detect and handle data generated by users flagging NSFW Offers.
|
||||
const flagData = ctx.request.body
|
||||
console.log('Flag type data received: ', flagData)
|
||||
|
||||
// const p2wdbHash = flagData.p2wdbHash
|
||||
await _this.useCases.offer.flagOffer(flagData)
|
||||
} else {
|
||||
console.log('Could not route P2WDB webhook data.')
|
||||
}
|
||||
|
||||
+185
-18
@@ -13,10 +13,17 @@
|
||||
a partially signed transaction.
|
||||
*/
|
||||
|
||||
// Global npm libraries
|
||||
const axios = require('axios')
|
||||
|
||||
// Local libraries
|
||||
const OfferEntity = require('../../entities/offer')
|
||||
const config = require('../../../config')
|
||||
|
||||
const DEFAULT_ENTRIES_PER_PAGE = 20
|
||||
const NFT_ENTRIES_PER_PAGE = 6
|
||||
const FUNGIBLE_ENTRIES_PER_PAGE = 20
|
||||
|
||||
class OfferUseCases {
|
||||
constructor (localConfig = {}) {
|
||||
// console.log('User localConfig: ', localConfig)
|
||||
@@ -35,6 +42,7 @@ class OfferUseCases {
|
||||
|
||||
// Encapsulate dependencies
|
||||
this.config = config
|
||||
this.axios = axios
|
||||
|
||||
this.offerEntity = new OfferEntity()
|
||||
this.OfferModel = this.adapters.localdb.Offer
|
||||
@@ -80,8 +88,21 @@ class OfferUseCases {
|
||||
const offerEntity = this.offerEntity.validate(offerObj)
|
||||
console.log('offerEntity: ', offerEntity)
|
||||
|
||||
// const displayCategory = await this.categorizeToken(offerEntity)
|
||||
// console.log('displayCategory: ', displayCategory)
|
||||
// Get data about the token.
|
||||
const tokenId = offerEntity.tokenId
|
||||
const tokenData = await this.adapters.wallet.bchWallet.getTokenData(tokenId)
|
||||
console.log(`tokenData: ${JSON.stringify(tokenData, null, 2)}`)
|
||||
|
||||
// Generate a 'display category' for the token. This will allow the
|
||||
// front end UI to figure out how to display the token.
|
||||
const displayCategory = await this.categorizeToken(offerEntity, tokenData)
|
||||
console.log('displayCategory: ', displayCategory)
|
||||
offerEntity.displayCategory = displayCategory
|
||||
|
||||
// Detect if user set the NSFW flag.
|
||||
let nsfw = false
|
||||
nsfw = await this.detectNsfw(tokenData)
|
||||
offerEntity.nsfw = nsfw
|
||||
|
||||
// Add offer to the local database.
|
||||
const offerModel = new this.OfferModel(offerEntity)
|
||||
@@ -94,6 +115,45 @@ class OfferUseCases {
|
||||
}
|
||||
}
|
||||
|
||||
// By default this function returns false, to indicate the NFT is safe for work.
|
||||
// If the user who created the token sets the nsfw property in the mutable data,
|
||||
// this function will return true.
|
||||
async detectNsfw (tokenData) {
|
||||
try {
|
||||
let nsfw = false
|
||||
|
||||
const mutableCid = tokenData.mutableData
|
||||
|
||||
// If there is no mutable IPFS CID, then skip this token.
|
||||
if (!mutableCid.includes('ipfs://')) return nsfw
|
||||
|
||||
// Revove the ipfs:// prefix.
|
||||
const cid = mutableCid.substring(7)
|
||||
|
||||
// Retrieve the mutable data from Filecoin/IPFS.
|
||||
const url = `https://${cid}.ipfs.w3s.link/data.json`
|
||||
const result = await axios.get(url)
|
||||
const mutableData = result.data
|
||||
console.log(`mutableData: ${JSON.stringify(mutableData, null, 2)}`)
|
||||
|
||||
// Logical tests
|
||||
const hasNsfw = !!mutableData.nsfw
|
||||
const nsfwSetTrue = mutableData.nsfw === true
|
||||
const nsfwStringTrue = mutableData.nsfw === 'true'
|
||||
const nsfwDetected = hasNsfw && (nsfwSetTrue || nsfwStringTrue)
|
||||
|
||||
if (nsfwDetected) {
|
||||
console.log('NSFW flag set as true')
|
||||
nsfw = true
|
||||
}
|
||||
|
||||
return nsfw
|
||||
} catch (err) {
|
||||
console.error('Error in detectNsfw(): ', err)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Categorize the token for display purposes. This will categorize a token
|
||||
// into one of these categories:
|
||||
// - nft
|
||||
@@ -104,26 +164,96 @@ class OfferUseCases {
|
||||
// The first three are easy to categorize. The simple-nft is a fungible token
|
||||
// with a quantity of 1, decimals of 0, and no minting baton. Categorizing this
|
||||
// type of token is the main reason why this function exists.
|
||||
async categorizeToken (offerData) {
|
||||
console.log(`categorizeToken(): ${JSON.stringify(offerData, null, 2)}`)
|
||||
async categorizeToken (offerData, tokenData) {
|
||||
try {
|
||||
// console.log(`categorizeToken(): ${JSON.stringify(offerData, null, 2)}`)
|
||||
|
||||
const tokenId = offerData.tokenId
|
||||
// const tokenId = offerData.tokenId
|
||||
//
|
||||
// const tokenData = await this.adapters.wallet.bchWallet.getTokenData(tokenId)
|
||||
// console.log(`tokenData: ${JSON.stringify(tokenData, null, 2)}`)
|
||||
|
||||
const tokenData = await this.adapters.wallet.bchWallet.getTokenData(tokenId)
|
||||
console.log(`tokenData: ${JSON.stringify(tokenData, null, 2)}`)
|
||||
if (tokenData.genesisData.type === 65) {
|
||||
return 'nft'
|
||||
}
|
||||
|
||||
return 'placeholder'
|
||||
// Create a set of checks to detect a simple NFT
|
||||
const isType1 = tokenData.genesisData.type === 1
|
||||
const hasNoMintingBaton = !tokenData.genesisData.mintBatonIsActive
|
||||
const hasNoDecimals = !tokenData.genesisData.decimals
|
||||
const hasQtyOfOne = parseInt(tokenData.genesisData.tokensInCirculationStr) === 1
|
||||
|
||||
if (isType1 && hasNoMintingBaton && hasNoDecimals && hasQtyOfOne) {
|
||||
return 'simple-nft'
|
||||
}
|
||||
|
||||
if (isType1) return 'fungible'
|
||||
|
||||
throw new Error(`Unknown token type: ${tokenData.genesisData.type}`)
|
||||
} catch (err) {
|
||||
console.error('Error in use-cases/offer/index.js categorizeToken(): ', err)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
async listOffers () {
|
||||
async listOffers (page = 0) {
|
||||
try {
|
||||
return this.OfferModel.find({}).sort('-timestamp')
|
||||
const data = await this.OfferModel.find({})
|
||||
// Sort entries so newest entries show first.
|
||||
.sort('-timestamp')
|
||||
// Skip to the start of the selected page.
|
||||
.skip(page * DEFAULT_ENTRIES_PER_PAGE)
|
||||
// Only return 20 results.
|
||||
.limit(DEFAULT_ENTRIES_PER_PAGE)
|
||||
|
||||
return data
|
||||
} catch (error) {
|
||||
console.error('Error in use-cases/offer/listOffers()')
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
async listNftOffers (page = 0, nsfw = false) {
|
||||
try {
|
||||
const data = await this.OfferModel.find({
|
||||
displayCategory: { $ne: 'fungible' },
|
||||
nsfw
|
||||
})
|
||||
// Sort entries so newest entries show first.
|
||||
.sort('-timestamp')
|
||||
// Skip to the start of the selected page.
|
||||
.skip(page * NFT_ENTRIES_PER_PAGE)
|
||||
// Only return 20 results.
|
||||
.limit(NFT_ENTRIES_PER_PAGE)
|
||||
|
||||
// console.log('listNftOffers() returning this data: ', data)
|
||||
|
||||
return data
|
||||
} catch (error) {
|
||||
console.error('Error in use-cases/offer/listNftOffers()')
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
async listFungibleOffers (page = 0) {
|
||||
try {
|
||||
const data = await this.OfferModel.find({ displayCategory: 'fungible' })
|
||||
// Sort entries so newest entries show first.
|
||||
.sort('-timestamp')
|
||||
// Skip to the start of the selected page.
|
||||
.skip(page * FUNGIBLE_ENTRIES_PER_PAGE)
|
||||
// Only return 20 results.
|
||||
.limit(FUNGIBLE_ENTRIES_PER_PAGE)
|
||||
|
||||
// console.log('listFungibleOffers() returning this data: ', data)
|
||||
|
||||
return data
|
||||
} catch (error) {
|
||||
console.error('Error in use-cases/offer/listFungibleOffers()')
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// Generate phase 2 of 3 - take the other side of an Offer.
|
||||
// Based on this example:
|
||||
// https://github.com/Permission=less-Software-Foundation/bch-js-examples/blob/master/bch/applications/collaborate/sell-slp/e2e-exchange/step2-purchase-tx.js
|
||||
@@ -146,15 +276,21 @@ class OfferUseCases {
|
||||
}
|
||||
|
||||
// Verify that UTXO for sale is unspent. Abort if it's been spent.
|
||||
const txid = offerInfo.utxoTxid
|
||||
const vout = offerInfo.utxoVout
|
||||
const utxoStatus = await this.adapters.bchjs.Blockchain.getTxOut(
|
||||
txid,
|
||||
vout
|
||||
)
|
||||
// const txid = offerInfo.utxoTxid
|
||||
// const vout = offerInfo.utxoVout
|
||||
|
||||
const utxo = {
|
||||
tx_hash: offerInfo.utxoTxid,
|
||||
tx_pos: offerInfo.utxoVout
|
||||
}
|
||||
const utxoStatus = await this.adapters.wallet.bchWallet.utxoIsValid(utxo)
|
||||
// const utxoStatus = await this.adapters.bchjs.Blockchain.getTxOut(
|
||||
// txid,
|
||||
// vout
|
||||
// )
|
||||
console.log('utxoStatus: ', utxoStatus)
|
||||
if (utxoStatus === null) {
|
||||
console.log(`utxo txid: ${txid}, vout: ${vout}`)
|
||||
if (!utxoStatus) {
|
||||
console.log(`utxo txid: ${offerInfo.utxoTxid}, vout: ${offerInfo.utxoVout}`)
|
||||
|
||||
// TODO: Mark this Offer as 'dead'
|
||||
|
||||
@@ -236,6 +372,9 @@ class OfferUseCases {
|
||||
await this.adapters.wallet.bchWallet.walletInfoPromise
|
||||
// console.log(`utxos: ${JSON.stringify(this.adapters.wallet.bchWallet.utxos.utxoStore, null, 2)}`)
|
||||
|
||||
// Refresh the wallet UTXOs
|
||||
await this.adapters.wallet.bchWallet.initialize()
|
||||
|
||||
// Ensure the app wallet has enough funds to write to the P2WDB.
|
||||
const wif = this.adapters.wallet.bchWallet.walletInfo.privateKey
|
||||
const canWriteToP2WDB = await this.adapters.p2wdb.checkForSufficientFunds(wif)
|
||||
@@ -424,6 +563,34 @@ class OfferUseCases {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
async flagOffer (flagData) {
|
||||
console.log(`flagData: ${JSON.stringify(flagData, null, 2)}`)
|
||||
|
||||
const p2wdbHash = flagData.data.p2wdbHash
|
||||
|
||||
// Get the offer from the database.
|
||||
const offer = await this.findOfferByHash(p2wdbHash)
|
||||
console.log(`Flagging this offer: ${JSON.stringify(offer, null, 2)}`)
|
||||
|
||||
if (!offer) {
|
||||
throw new Error(`Offer ${p2wdbHash} not found in the database.`)
|
||||
}
|
||||
|
||||
// Add the raw flag data to the database model.
|
||||
offer.flags.push(flagData)
|
||||
|
||||
// If flag count is 3 or more, mark the Offer as NSFW
|
||||
const flagCnt = offer.flags.length
|
||||
if (flagCnt >= 3) {
|
||||
offer.nsfw = true
|
||||
}
|
||||
|
||||
// Save the updated offer data to the database.
|
||||
await offer.save()
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = OfferUseCases
|
||||
|
||||
@@ -34,6 +34,7 @@ class MockBchWallet {
|
||||
tokenTicker: 'TROUT'
|
||||
}]
|
||||
}
|
||||
this.getTokenData = async () => {}
|
||||
|
||||
// Environment variable is used by wallet-balance.unit.js to force an error.
|
||||
if (process.env.NO_UTXO) {
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
Mocking data for Offer Use Case unit tests.
|
||||
*/
|
||||
|
||||
const nftOffer01 = {
|
||||
"messageType": 1,
|
||||
"messageClass": 1,
|
||||
"tokenId": "eb93f05553ff088bffb0ec687519e83c59e5108c160f7c25a4b6c45109d7e40b",
|
||||
"buyOrSell": "sell",
|
||||
"rateInBaseUnit": 7672536,
|
||||
"minUnitsToExchange": 7672536,
|
||||
"numTokens": 1,
|
||||
"utxoTxid": "c736d73e274df20e9b069b4990d1a264fb80aa98d67cc6c7a39e42bff48e7c04",
|
||||
"utxoVout": 1,
|
||||
"timestamp": 1662930309998,
|
||||
"globaltimestamp": "2022-09-11T21:05:09.998Z",
|
||||
"localTimestamp": "9/11/2022, 9:05:09 PM",
|
||||
"txid": "4266862b8358664996038d8c29d49dc0f3d3058fd1ef3d567b084e6b16ceb5b2",
|
||||
"p2wdbHash": "zdpuArq7rCuVCGPyTnWpLjy9AYsh8yrbwjGdKwg2GDDALkM8t",
|
||||
"offerStatus": "posted",
|
||||
"makerAddr": "bitcoincash:qrqlz63cwmu0hcmsrfnd8jemn3atkpaqds6tf4ksrr",
|
||||
"ticker": "TV001",
|
||||
"tokenType": 65
|
||||
}
|
||||
|
||||
const nftTokenData01 = {
|
||||
"genesisData": {
|
||||
"type": 65,
|
||||
"ticker": "TV001",
|
||||
"name": "Introduction to NFTs on BCH",
|
||||
"tokenId": "eb93f05553ff088bffb0ec687519e83c59e5108c160f7c25a4b6c45109d7e40b",
|
||||
"documentUri": "ipfs://bafybeibmgilu4lk3uivwyzhtm7jorl5zbsluewqmcsxs6otzrphh33okk4",
|
||||
"documentHash": "c1731268f4873f1928438abdaf6ffc546d86a1817dc6f3c6bc73fbdfb4664f10",
|
||||
"decimals": 0,
|
||||
"mintBatonIsActive": false,
|
||||
"tokensInCirculationBN": "1",
|
||||
"tokensInCirculationStr": "1",
|
||||
"blockCreated": 740395,
|
||||
"totalBurned": "0",
|
||||
"totalMinted": "1",
|
||||
"parentGroupId": "030563ddd65772d8e9b79b825529ed53c7d27037507b57c528788612b4911107"
|
||||
},
|
||||
"immutableData": "ipfs://bafybeibmgilu4lk3uivwyzhtm7jorl5zbsluewqmcsxs6otzrphh33okk4",
|
||||
"mutableData": "ipfs://bafybeifzhunfpodsztsj5x4ypopngkxuapbxwaxaxkahaybdktk6joqtlq"
|
||||
}
|
||||
|
||||
const simpleNftOffer01 = {
|
||||
"messageType": 1,
|
||||
"messageClass": 1,
|
||||
"tokenId": "a80c89d2f9f7ad7584743587c4950a0f28514ba8054e8cec4e871747cf9c162c",
|
||||
"buyOrSell": "sell",
|
||||
"rateInBaseUnit": 76725,
|
||||
"minUnitsToExchange": 76725,
|
||||
"numTokens": 1,
|
||||
"utxoTxid": "dd51a8bf96c8ab16a2935b09e51650e1769a1fa75cce9f0455abdbf72071d3c2",
|
||||
"utxoVout": 1,
|
||||
"timestamp": 1662930102814,
|
||||
"globaltimestamp": "2022-09-11T21:01:42.814Z",
|
||||
"localTimestamp": "9/11/2022, 9:01:42 PM",
|
||||
"txid": "4013177602ee75bbc5c4a67ea8d6d80e4f3d12b9c46ae2a6643911f48257b578",
|
||||
"p2wdbHash": "zdpuAzyLPFBwFWbNi8XQuxgAx9aBNGWZEGqib7KE6Hp3AEEhq",
|
||||
"offerStatus": "posted",
|
||||
"makerAddr": "bitcoincash:qrqlz63cwmu0hcmsrfnd8jemn3atkpaqds6tf4ksrr",
|
||||
"ticker": "BAA",
|
||||
"tokenType": 1
|
||||
}
|
||||
|
||||
const simpleNftTokenData01 = {
|
||||
"genesisData": {
|
||||
"type": 1,
|
||||
"ticker": "BAA",
|
||||
"name": "Bitcoin AI Art 001",
|
||||
"tokenId": "a80c89d2f9f7ad7584743587c4950a0f28514ba8054e8cec4e871747cf9c162c",
|
||||
"documentUri": "ipfs://bafybeifevvtnap3m3fsjbwnvpknvzhqihl2ouuypio33f2vtwqejipgpkq",
|
||||
"documentHash": "d6213e5629d01fd2cd714cde709e11b888c0ba441e05bb1cc649f8b784df5a6c",
|
||||
"decimals": 0,
|
||||
"mintBatonIsActive": false,
|
||||
"tokensInCirculationBN": "1",
|
||||
"tokensInCirculationStr": "1",
|
||||
"blockCreated": 757238,
|
||||
"totalBurned": "0",
|
||||
"totalMinted": "1"
|
||||
},
|
||||
"immutableData": "ipfs://bafybeifevvtnap3m3fsjbwnvpknvzhqihl2ouuypio33f2vtwqejipgpkq",
|
||||
"mutableData": "ipfs://bafybeibqnsmmh6bkf2wwextetki4tly65z4r4qkrrpl5xwgvzdzjley6wm"
|
||||
}
|
||||
|
||||
const fungibleOffer01 = {
|
||||
"messageType": 1,
|
||||
"messageClass": 1,
|
||||
"tokenId": "a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2",
|
||||
"buyOrSell": "sell",
|
||||
"rateInBaseUnit": 7672,
|
||||
"minUnitsToExchange": 7672,
|
||||
"numTokens": 1,
|
||||
"utxoTxid": "822dc4c765cc68314479c7707bba28028eacab6013bb683b1680dbcc9c95b286",
|
||||
"utxoVout": 1,
|
||||
"timestamp": 1662930501091,
|
||||
"globaltimestamp": "2022-09-11T21:08:21.091Z",
|
||||
"localTimestamp": "9/11/2022, 9:08:21 PM",
|
||||
"txid": "7ee0ec32e116b068ff43c5516fa500058d14f53037ba5cc729e78eb51d1f1ffd",
|
||||
"p2wdbHash": "zdpuB2gHDvmByfvPFLJgEAaRyUi1cwnD45Qt66y4f5tnMXFo7",
|
||||
"offerStatus": "posted",
|
||||
"makerAddr": "bitcoincash:qrqlz63cwmu0hcmsrfnd8jemn3atkpaqds6tf4ksrr",
|
||||
"ticker": "TROUT",
|
||||
"tokenType": 1
|
||||
}
|
||||
|
||||
const fungibleTokenData01 = {
|
||||
"genesisData": {
|
||||
"type": 1,
|
||||
"ticker": "TROUT",
|
||||
"name": "Trout's test token",
|
||||
"tokenId": "a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2",
|
||||
"documentUri": "troutsblog.com",
|
||||
"documentHash": "",
|
||||
"decimals": 2,
|
||||
"mintBatonIsActive": true,
|
||||
"tokensInCirculationBN": "100097954686",
|
||||
"tokensInCirculationStr": "100097954686",
|
||||
"blockCreated": 622414,
|
||||
"totalBurned": "2045314",
|
||||
"totalMinted": "100100000000"
|
||||
},
|
||||
"immutableData": "troutsblog.com",
|
||||
"mutableData": ""
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
nftOffer01,
|
||||
nftTokenData01,
|
||||
simpleNftOffer01,
|
||||
simpleNftTokenData01,
|
||||
fungibleOffer01,
|
||||
fungibleTokenData01
|
||||
}
|
||||
@@ -13,6 +13,7 @@ const sinon = require('sinon')
|
||||
const OfferLib = require('../../../src/use-cases/offer')
|
||||
const OrderUseCase = require('../../../src/use-cases/order')
|
||||
const adapters = require('../mocks/adapters')
|
||||
const mockData = require('../mocks/use-cases/offer-mock-data.js')
|
||||
|
||||
describe('#offer-use-case', () => {
|
||||
let uut
|
||||
@@ -73,6 +74,9 @@ describe('#offer-use-case', () => {
|
||||
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut.adapters.bchjs.Blockchain, 'getTxOut').resolves(null)
|
||||
sandbox.stub(uut.adapters.wallet.bchWallet, 'getTokenData').resolves({})
|
||||
sandbox.stub(uut, 'categorizeToken').resolves('nft')
|
||||
sandbox.stub(uut, 'detectNsfw').resolves(false)
|
||||
|
||||
const result = await uut.createOffer(offerObj)
|
||||
// console.log('result: ', result)
|
||||
@@ -118,6 +122,9 @@ describe('#offer-use-case', () => {
|
||||
},
|
||||
coinbase: false
|
||||
})
|
||||
sandbox.stub(uut, 'categorizeToken').resolves('fungible')
|
||||
sandbox.stub(uut.adapters.wallet.bchWallet, 'getTokenData').resolves({})
|
||||
sandbox.stub(uut, 'detectNsfw').resolves(false)
|
||||
|
||||
const result = await uut.createOffer(offerObj)
|
||||
// console.log('result: ', result)
|
||||
@@ -125,4 +132,42 @@ describe('#offer-use-case', () => {
|
||||
assert.equal(result, true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#categorizeToken', () => {
|
||||
it('should categorize an NFT', async () => {
|
||||
// Mock dependencies
|
||||
// sandbox.stub(uut.adapters.wallet.bchWallet, 'getTokenData').resolves(mockData.nftTokenData01)
|
||||
|
||||
const offerData = mockData.nftOffer01
|
||||
const tokenData = mockData.nftTokenData01
|
||||
|
||||
const result = await uut.categorizeToken(offerData, tokenData)
|
||||
|
||||
assert.equal(result, 'nft')
|
||||
})
|
||||
|
||||
it('should categorize a simple NFT', async () => {
|
||||
// Mock dependencies
|
||||
// sandbox.stub(uut.adapters.wallet.bchWallet, 'getTokenData').resolves(mockData.simpleNftTokenData01)
|
||||
|
||||
const offerData = mockData.simpleNftOffer01
|
||||
const tokenData = mockData.simpleNftTokenData01
|
||||
|
||||
const result = await uut.categorizeToken(offerData, tokenData)
|
||||
|
||||
assert.equal(result, 'simple-nft')
|
||||
})
|
||||
|
||||
it('should categorize a fungible token', async () => {
|
||||
// Mock dependencies
|
||||
// sandbox.stub(uut.adapters.wallet.bchWallet, 'getTokenData').resolves(mockData.fungibleTokenData01)
|
||||
|
||||
const offerData = mockData.fungibleOffer01
|
||||
const tokenData = mockData.fungibleTokenData01
|
||||
|
||||
const result = await uut.categorizeToken(offerData, tokenData)
|
||||
|
||||
assert.equal(result, 'fungible')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user