mirror of
https://github.com/Permissionless-Software-Foundation/bch-dex.git
synced 2026-09-22 01:02:00 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2bffd8c4e | ||
|
|
ac97831721 | ||
|
|
02eab8686c | ||
|
|
1c1169dd93 | ||
|
|
0896adffbb | ||
|
|
56e0f7e8df | ||
|
|
34841f53db | ||
|
|
c27209cceb | ||
|
|
5a0fc31c7a | ||
|
|
4701f245b9 | ||
|
|
daa06a57ed | ||
|
|
b3157a04ed | ||
|
|
3753db46f6 | ||
|
|
b924449d02 | ||
|
|
dd8d992b8a | ||
|
|
4ed657ae28 | ||
|
|
e14633db79 | ||
|
|
6a4e749218 | ||
|
|
7a5036e3aa | ||
|
|
cca10452c0 | ||
|
|
309ea0774d | ||
|
|
c2b68a0787 | ||
|
|
2bb4cd7cae |
Generated
+1102
-1528
File diff suppressed because it is too large
Load Diff
+4
-11
@@ -29,7 +29,7 @@
|
||||
"dependencies": {
|
||||
"@psf/bch-js": "6.4.5",
|
||||
"axios": "0.27.2",
|
||||
"bch-message-lib": "2.2.0",
|
||||
"bch-message-lib": "2.2.1",
|
||||
"bcryptjs": "2.4.3",
|
||||
"bitcoincashjs-lib": "3.3.3",
|
||||
"glob": "7.1.6",
|
||||
@@ -52,11 +52,11 @@
|
||||
"koa2-ratelimit": "0.9.1",
|
||||
"libp2p": "^0.36.2",
|
||||
"line-reader": "0.4.0",
|
||||
"minimal-slp-wallet": "4.6.5",
|
||||
"minimal-slp-wallet": "5.1.0",
|
||||
"mongoose": "5.13.13",
|
||||
"node-fetch": "npm:@achingbrain/node-fetch@2.6.7",
|
||||
"nodemailer": "6.7.5",
|
||||
"p2wdb": "1.4.0",
|
||||
"p2wdb": "2.2.3",
|
||||
"passport-local": "1.0.0",
|
||||
"public-ip": "^4.0.4",
|
||||
"winston": "3.3.3",
|
||||
@@ -67,19 +67,12 @@
|
||||
"bch-token-sweep": "2.1.2",
|
||||
"chai": "4.3.0",
|
||||
"coveralls": "2.11.4",
|
||||
"eslint": "7.19.0",
|
||||
"eslint-config-prettier": "7.2.0",
|
||||
"eslint-config-standard": "16.0.2",
|
||||
"eslint-plugin-node": "11.1.0",
|
||||
"eslint-plugin-prettier": "3.3.1",
|
||||
"eslint-plugin-promise": "^4.3.1",
|
||||
"eslint-plugin-standard": "4.0.0",
|
||||
"husky": "4.3.8",
|
||||
"mocha": "10.0.0",
|
||||
"nyc": "15.1.0",
|
||||
"semantic-release": "19.0.3",
|
||||
"sinon": "9.2.4",
|
||||
"standard": "16.0.4",
|
||||
"standard": "17.0.0",
|
||||
"uuid": "8.3.2"
|
||||
},
|
||||
"release": {
|
||||
|
||||
@@ -37,7 +37,7 @@ WORKDIR /home/safeuser
|
||||
RUN npm install -g npm
|
||||
|
||||
# npm mirror to prevent direct dependency on npm.
|
||||
RUN npm set registry http://94.130.170.209:4873/
|
||||
#RUN npm set registry http://94.130.170.209:4873/
|
||||
|
||||
# Switch to user account.
|
||||
#USER safeuser
|
||||
@@ -46,11 +46,11 @@ RUN npm set registry http://94.130.170.209:4873/
|
||||
|
||||
# Clone the rest.bitcoin.com repository
|
||||
WORKDIR /home/safeuser
|
||||
RUN git clone https://github.com/Permissionless-Software-Foundation/bch-dex-ui
|
||||
RUN git clone https://github.com/Permissionless-Software-Foundation/bch-dex-ui-v2
|
||||
|
||||
# Switch to the desired branch. `master` is usually stable,
|
||||
# and `stage` has the most up-to-date changes.
|
||||
WORKDIR /home/safeuser/bch-dex-ui
|
||||
WORKDIR /home/safeuser/bch-dex-ui-v2
|
||||
|
||||
# For development: switch to unstable branch
|
||||
RUN git checkout ct-unstable
|
||||
@@ -58,7 +58,7 @@ RUN git checkout ct-unstable
|
||||
# Install dependencies
|
||||
#RUN mkdir .ipfsdata
|
||||
#RUN npm install -g @mapbox/node-pre-gyp
|
||||
RUN npm install --force
|
||||
RUN npm install
|
||||
|
||||
# Build the site
|
||||
RUN CI=true npm run build
|
||||
@@ -68,6 +68,6 @@ FROM nginx
|
||||
EXPOSE 80
|
||||
|
||||
# Copy the files built in the first container to the new NGINX container.
|
||||
COPY --from=builder /home/safeuser/bch-dex-ui/public /usr/share/nginx/html
|
||||
COPY --from=builder /home/safeuser/bch-dex-ui-v2/build /usr/share/nginx/html
|
||||
|
||||
#USER safeuser
|
||||
|
||||
@@ -73,7 +73,7 @@ services:
|
||||
#build:
|
||||
# context: ./bch-dex/
|
||||
# dockerfile: Dockerfile
|
||||
image: christroutner/bch-dex:v1.9.1
|
||||
image: christroutner/bch-dex:v1.9.6
|
||||
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.5
|
||||
image: christroutner/bch-dex-ui:v1.6.6
|
||||
container_name: dex-ui
|
||||
environment:
|
||||
SERVER: 'http://192.168.2.3'
|
||||
|
||||
@@ -46,11 +46,11 @@ RUN npm set registry http://94.130.170.209:4873/
|
||||
|
||||
# Clone the rest.bitcoin.com repository
|
||||
WORKDIR /home/safeuser
|
||||
RUN git clone https://github.com/Permissionless-Software-Foundation/bch-dex-ui
|
||||
RUN git clone https://github.com/Permissionless-Software-Foundation/bch-dex-ui-v2
|
||||
|
||||
# Switch to the desired branch. `master` is usually stable,
|
||||
# and `stage` has the most up-to-date changes.
|
||||
WORKDIR /home/safeuser/bch-dex-ui
|
||||
WORKDIR /home/safeuser/bch-dex-ui-v2
|
||||
|
||||
# For development: switch to unstable branch
|
||||
RUN git checkout ct-unstable
|
||||
@@ -58,7 +58,7 @@ RUN git checkout ct-unstable
|
||||
# Install dependencies
|
||||
#RUN mkdir .ipfsdata
|
||||
#RUN npm install -g @mapbox/node-pre-gyp
|
||||
RUN npm install --force
|
||||
RUN npm install
|
||||
|
||||
# Build the site
|
||||
RUN CI=true npm run build
|
||||
@@ -68,6 +68,6 @@ FROM nginx
|
||||
EXPOSE 80
|
||||
|
||||
# Copy the files built in the first container to the new NGINX container.
|
||||
COPY --from=builder /home/safeuser/bch-dex-ui/public /usr/share/nginx/html
|
||||
COPY --from=builder /home/safeuser/bch-dex-ui-v2/build /usr/share/nginx/html
|
||||
|
||||
#USER safeuser
|
||||
|
||||
@@ -14,40 +14,36 @@ services:
|
||||
restart: always
|
||||
|
||||
ipfs:
|
||||
#image: ipfs/go-ipfs:v0.13.0
|
||||
image: odanado/go-ipfs:v0.11.0
|
||||
image: christroutner/trickle-ipfs-rpi:v1.0.1
|
||||
#image: odanado/go-ipfs:v0.11.0
|
||||
container_name: ipfs
|
||||
environment:
|
||||
MY_ENV_VAR: 'placeholder'
|
||||
IPFS_DAEMON_ARGUMENTS: '--enable-pubsub-experiment --migrate=true --agent-version-suffix=docker --routing=dhtclient'
|
||||
UPLOAD_KBPS: '25'
|
||||
DOWNLOAD_KBPS: '25'
|
||||
logging:
|
||||
driver: 'json-file'
|
||||
options:
|
||||
max-size: '10m'
|
||||
max-file: '10'
|
||||
mem_limit: 2000mb
|
||||
mem_limit: 1000mb
|
||||
ports:
|
||||
- 4001:4001
|
||||
- 4003:4003
|
||||
- 172.17.0.1:5001:5001
|
||||
- 172.17.0.1:8080:8080
|
||||
volumes:
|
||||
- ../data/go-ipfs/data:/data/ipfs
|
||||
# - ../../swarm.key:/data/ipfs/swarm.key
|
||||
# https://docs.docker.com/compose/compose-file/compose-file-v3/#command
|
||||
# https://github.com/ipfs/go-ipfs/blob/91c52657166bcf86f2476926e4fe56694dc26562/Dockerfile#L115
|
||||
command:
|
||||
[
|
||||
'daemon',
|
||||
'--migrate=true',
|
||||
'--agent-version-suffix=docker',
|
||||
'--enable-pubsub-experiment'
|
||||
]
|
||||
- ./data/go-ipfs:/root/.ipfs
|
||||
command: [
|
||||
'./start-ipfs.sh'
|
||||
]
|
||||
restart: always
|
||||
|
||||
p2wdb:
|
||||
#build:
|
||||
# context: ./p2wdb/
|
||||
# dockerfile: Dockerfile
|
||||
image: christroutner/p2wdb-rpi-v3.0.6
|
||||
image: christroutner/p2wdb-rpi:v3.0.10
|
||||
container_name: p2wdb
|
||||
environment:
|
||||
CONSUMER_URL: 'https://free-bch.fullstack.cash'
|
||||
@@ -71,10 +67,10 @@ services:
|
||||
restart: always
|
||||
|
||||
bch-dex:
|
||||
#build:
|
||||
# context: ./bch-dex/
|
||||
# dockerfile: Dockerfile
|
||||
image: christroutner/bch-dex-rpi:v1.8.2
|
||||
build:
|
||||
context: ./bch-dex/
|
||||
dockerfile: Dockerfile
|
||||
#image: christroutner/bch-dex-rpi:v1.9.8
|
||||
container_name: bch-dex
|
||||
environment:
|
||||
CONSUMER_URL: 'https://free-bch.fullstack.cash'
|
||||
@@ -98,7 +94,7 @@ services:
|
||||
#build:
|
||||
# context: ./bch-dex-ui/
|
||||
# dockerfile: Dockerfile
|
||||
image: christroutner/bch-dex-ui-rpi:v1.7.4
|
||||
image: christroutner/bch-dex-ui-rpi:v1.7.5
|
||||
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,
|
||||
|
||||
@@ -37,6 +37,7 @@ class Adapters {
|
||||
this.bch = new BCHAdapter()
|
||||
this.config = config
|
||||
this.wallet = new WalletAdapter()
|
||||
localConfig.bchWallet = this.wallet.bchWallet
|
||||
this.p2wdb = new P2wdbAdapter(localConfig)
|
||||
this.webhook = new Webhook()
|
||||
|
||||
@@ -70,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,7 @@ const Offer = new mongoose.Schema({
|
||||
timestamp: { type: Number },
|
||||
globalTimestamp: { type: String },
|
||||
localTimestamp: { type: String },
|
||||
displayCategory: { type: String },
|
||||
|
||||
// SWaP Protocol Properties
|
||||
lokadId: { type: String },
|
||||
|
||||
@@ -12,9 +12,9 @@ const config = require('../../config')
|
||||
class P2wdbAdapter {
|
||||
constructor (localConfig = {}) {
|
||||
// Dependency injection
|
||||
this.bchjs = localConfig.bchjs
|
||||
if (!this.bchjs) {
|
||||
throw new Error('Must pass an instance of bch-js when instantiating p2wdb.js adapter.')
|
||||
this.bchWallet = localConfig.bchWallet
|
||||
if (!this.bchWallet) {
|
||||
throw new Error('Must pass an instance of minimal-slp-wallet as bchWallet when instantiating p2wdb.js adapter.')
|
||||
}
|
||||
|
||||
// Encapsulate dependencies
|
||||
@@ -22,6 +22,7 @@ class P2wdbAdapter {
|
||||
this.config = config
|
||||
this.Write = Write
|
||||
this.Read = Read
|
||||
this.bchjs = this.bchWallet.bchjs
|
||||
|
||||
// Allow the localConfig to overwrite the config file values.
|
||||
this.p2wdbURL = localConfig.p2wdbURL || config.p2wdbUrl
|
||||
@@ -51,6 +52,10 @@ class P2wdbAdapter {
|
||||
// settings.
|
||||
instantiateWriteLib (wif) {
|
||||
try {
|
||||
if (!wif) {
|
||||
throw new Error('WIF input required when calling instantiateWriteLib()')
|
||||
}
|
||||
|
||||
// Object used to configure the Write library.
|
||||
let configObj = {}
|
||||
|
||||
@@ -62,7 +67,8 @@ class P2wdbAdapter {
|
||||
wif,
|
||||
serverURL: this.p2wdbURL,
|
||||
restURL: this.bchjs.restURL,
|
||||
apiToken: this.bchjs.apiToken
|
||||
apiToken: this.bchjs.apiToken,
|
||||
bchWallet: this.bchWallet
|
||||
}
|
||||
} else {
|
||||
// Use web 3 infrastructure.
|
||||
@@ -72,7 +78,8 @@ class P2wdbAdapter {
|
||||
wif,
|
||||
serverURL: this.p2wdbURL,
|
||||
interface: 'consumer-api',
|
||||
restURL: this.config.consumerUrl
|
||||
restURL: this.config.consumerUrl,
|
||||
bchWallet: this.bchWallet
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,9 +20,10 @@ class WalletAdapter {
|
||||
// Encapsulate dependencies
|
||||
this.jsonFiles = new JsonFiles()
|
||||
this.WALLET_FILE = WALLET_FILE
|
||||
this.BchWallet = BchWallet
|
||||
this.config = config
|
||||
this.bitcoinJs = bitcoinJs
|
||||
this.BchWallet = BchWallet
|
||||
this.bchWallet = {} // Will be replaced when initialized.
|
||||
}
|
||||
|
||||
// Open the wallet file, or create one if the file doesn't exist.
|
||||
@@ -91,6 +92,7 @@ class WalletAdapter {
|
||||
|
||||
// Wait for wallet to initialize.
|
||||
await this.bchWallet.walletInfoPromise
|
||||
await this.bchWallet.initialize()
|
||||
console.log(`BCH wallet initialized. Wallet address: ${this.bchWallet.walletInfo.cashAddress}`)
|
||||
// console.log(`this.bchWallet.walletInfo: ${JSON.stringify(this.bchWallet.walletInfo, null, 2)}`)
|
||||
|
||||
|
||||
@@ -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())
|
||||
|
||||
+132
-16
@@ -17,6 +17,10 @@
|
||||
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)
|
||||
@@ -80,6 +84,12 @@ class OfferUseCases {
|
||||
const offerEntity = this.offerEntity.validate(offerObj)
|
||||
console.log('offerEntity: ', offerEntity)
|
||||
|
||||
// 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)
|
||||
console.log('displayCategory: ', displayCategory)
|
||||
offerEntity.displayCategory = displayCategory
|
||||
|
||||
// Add offer to the local database.
|
||||
const offerModel = new this.OfferModel(offerEntity)
|
||||
await offerModel.save()
|
||||
@@ -91,15 +101,103 @@ class OfferUseCases {
|
||||
}
|
||||
}
|
||||
|
||||
async listOffers () {
|
||||
// Categorize the token for display purposes. This will categorize a token
|
||||
// into one of these categories:
|
||||
// - nft
|
||||
// - group
|
||||
// - fungible
|
||||
// - simple-nft
|
||||
//
|
||||
// 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) {
|
||||
try {
|
||||
return this.OfferModel.find({}).sort('-timestamp')
|
||||
console.log(`categorizeToken(): ${JSON.stringify(offerData, null, 2)}`)
|
||||
|
||||
const tokenId = offerData.tokenId
|
||||
|
||||
const tokenData = await this.adapters.wallet.bchWallet.getTokenData(tokenId)
|
||||
console.log(`tokenData: ${JSON.stringify(tokenData, null, 2)}`)
|
||||
|
||||
if (tokenData.genesisData.type === 65) {
|
||||
return 'nft'
|
||||
}
|
||||
|
||||
// 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 (page = 0) {
|
||||
try {
|
||||
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) {
|
||||
try {
|
||||
const data = await this.OfferModel.find({ displayCategory: { $ne: 'fungible' } })
|
||||
// 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
|
||||
@@ -122,15 +220,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'
|
||||
|
||||
@@ -212,6 +316,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)
|
||||
@@ -352,10 +459,19 @@ class OfferUseCases {
|
||||
let utxoStatus = null
|
||||
try {
|
||||
// Get the status of the UTXO associate with this Offer.
|
||||
utxoStatus = await this.adapters.bchjs.Blockchain.getTxOut(
|
||||
thisOffer.utxoTxid,
|
||||
thisOffer.utxoVout
|
||||
)
|
||||
// utxoStatus = await this.adapters.bchjs.Blockchain.getTxOut(
|
||||
// thisOffer.utxoTxid,
|
||||
// thisOffer.utxoVout
|
||||
// )
|
||||
|
||||
// Get the status of the UTXO associate with this Offer.
|
||||
const utxo = {
|
||||
tx_hash: thisOffer.utxoTxid,
|
||||
tx_pos: thisOffer.utxoVout
|
||||
}
|
||||
// console.log(`Checking this UTXO: ${JSON.stringify(utxo, null, 2)}`)
|
||||
|
||||
utxoStatus = await this.adapters.wallet.bchWallet.utxoIsValid(utxo)
|
||||
// console.log('utxoStatus: ', utxoStatus)
|
||||
} catch (err) {
|
||||
// Handle corner case of bad-data in the Offer model.
|
||||
@@ -364,7 +480,7 @@ class OfferUseCases {
|
||||
await thisOffer.remove()
|
||||
continue
|
||||
} else if (err.isAxiosError) {
|
||||
console.log('Error trying to contact api.fullstack.cash')
|
||||
console.log('Error trying to contact wallet service: ', err)
|
||||
continue
|
||||
} else {
|
||||
throw err
|
||||
@@ -372,7 +488,7 @@ class OfferUseCases {
|
||||
}
|
||||
|
||||
// If the Offer UTXO is spent, delete the Offer model.
|
||||
if (utxoStatus === null) {
|
||||
if (!utxoStatus) {
|
||||
console.log(`Spent UTXO detected. Deleting this Offer: ${JSON.stringify(thisOffer, null, 2)}`)
|
||||
await thisOffer.remove()
|
||||
}
|
||||
|
||||
+13
-6
@@ -198,10 +198,17 @@ class OrderLib {
|
||||
let utxoStatus = null
|
||||
try {
|
||||
// Get the status of the UTXO associate with this Order.
|
||||
utxoStatus = await this.adapters.bchjs.Blockchain.getTxOut(
|
||||
thisOrder.utxoTxid,
|
||||
thisOrder.utxoVout
|
||||
)
|
||||
// utxoStatus = await this.adapters.bchjs.Blockchain.getTxOut(
|
||||
// thisOrder.utxoTxid,
|
||||
// thisOrder.utxoVout
|
||||
// )
|
||||
|
||||
// Get the status of the UTXO associate with this Order.
|
||||
const utxo = {
|
||||
tx_hash: thisOrder.utxoTxid,
|
||||
tx_pos: thisOrder.utxoVout
|
||||
}
|
||||
utxoStatus = await this.adapters.wallet.bchWallet.utxoIsValid(utxo)
|
||||
// console.log('utxoStatus: ', utxoStatus)
|
||||
} catch (err) {
|
||||
// Handle corner case of bad-data in the Order model.
|
||||
@@ -210,7 +217,7 @@ class OrderLib {
|
||||
await thisOrder.remove()
|
||||
continue
|
||||
} else if (err.isAxiosError) {
|
||||
console.log('Error trying to contact api.fullstack.cash')
|
||||
console.log('Error trying to contact wallet service: ', err)
|
||||
continue
|
||||
} else {
|
||||
throw err
|
||||
@@ -218,7 +225,7 @@ class OrderLib {
|
||||
}
|
||||
|
||||
// If the Order UTXO is spent, delete the Order model.
|
||||
if (utxoStatus === null) {
|
||||
if (!utxoStatus) {
|
||||
console.log(`Spent UTXO detected. Deleting this Order: ${JSON.stringify(thisOrder, null, 2)}`)
|
||||
await thisOrder.remove()
|
||||
}
|
||||
|
||||
@@ -5,31 +5,34 @@
|
||||
// Public npm libraries.
|
||||
const assert = require('chai').assert
|
||||
const sinon = require('sinon')
|
||||
const BCHJS = require('@psf/bch-js')
|
||||
// const BCHJS = require('@psf/bch-js')
|
||||
const BchWallet = require('minimal-slp-wallet/index.js')
|
||||
|
||||
// Local libraries.
|
||||
const P2wdbAdapter = require('../../../src/adapters/p2wdb-adapter')
|
||||
|
||||
describe('#P2wdbAdapter', () => {
|
||||
let uut, sandbox
|
||||
let uut, sandbox, bchWallet
|
||||
|
||||
beforeEach(() => {
|
||||
const bchjs = new BCHJS()
|
||||
beforeEach(async () => {
|
||||
// const bchjs = new BCHJS()
|
||||
bchWallet = new BchWallet()
|
||||
await bchWallet.walletInfoPromise
|
||||
|
||||
uut = new P2wdbAdapter({ bchjs })
|
||||
uut = new P2wdbAdapter({ bchWallet })
|
||||
sandbox = sinon.createSandbox()
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('#constructor', () => {
|
||||
it('should throw error if bch-js is not passed in', () => {
|
||||
it('should throw error if bchWallet is not passed in', () => {
|
||||
try {
|
||||
uut = new P2wdbAdapter()
|
||||
|
||||
assert.fail('Unexpected coded path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Must pass an instance of bch-js when instantiating p2wdb.js adapter.')
|
||||
assert.include(err.message, 'Must pass an instance of minimal-slp-wallet as bchWallet when instantiating p2wdb.js adapter.')
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -82,7 +85,7 @@ describe('#P2wdbAdapter', () => {
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.include(err.message, 'WIF private key required')
|
||||
assert.include(err.message, 'WIF input required when calling instantiateWriteLib()')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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
|
||||
@@ -118,6 +119,7 @@ describe('#offer-use-case', () => {
|
||||
},
|
||||
coinbase: false
|
||||
})
|
||||
sandbox.stub(uut, 'categorizeToken').resolves('fungible')
|
||||
|
||||
const result = await uut.createOffer(offerObj)
|
||||
// console.log('result: ', result)
|
||||
@@ -125,4 +127,39 @@ 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 result = await uut.categorizeToken(offerData)
|
||||
|
||||
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 result = await uut.categorizeToken(offerData)
|
||||
|
||||
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 result = await uut.categorizeToken(offerData)
|
||||
|
||||
assert.equal(result, 'fungible')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user