From 56e0f7e8dff12d7e60ee5982b3a4c3c5c302d103 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sun, 11 Sep 2022 11:55:07 -0700 Subject: [PATCH 1/3] fix(p2wdb): Updating p2wdb to v2.2.3 --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index e1aeffc..31db52d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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" @@ -29379,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" diff --git a/package.json b/package.json index 76ee9ed..bcb1676 100644 --- a/package.json +++ b/package.json @@ -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", From 0896adffbb1af2ddf25b85cdf17b44eccc2b561e Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sun, 11 Sep 2022 14:36:40 -0700 Subject: [PATCH 2/3] feat(categorizeToken): New field to Offer model, for UI display --- src/adapters/localdb/models/offer.js | 1 + src/use-cases/offer/index.js | 38 +++++- test/unit/mocks/adapters/wallet.js | 1 + test/unit/mocks/use-cases/offer-mock-data.js | 136 +++++++++++++++++++ test/unit/use-cases/offer.use-case.unit.js | 37 +++++ 5 files changed, 206 insertions(+), 7 deletions(-) create mode 100644 test/unit/mocks/use-cases/offer-mock-data.js diff --git a/src/adapters/localdb/models/offer.js b/src/adapters/localdb/models/offer.js index f1a120a..45b2107 100644 --- a/src/adapters/localdb/models/offer.js +++ b/src/adapters/localdb/models/offer.js @@ -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 }, diff --git a/src/use-cases/offer/index.js b/src/use-cases/offer/index.js index 3134d9c..6425edf 100644 --- a/src/use-cases/offer/index.js +++ b/src/use-cases/offer/index.js @@ -80,8 +80,11 @@ class OfferUseCases { const offerEntity = this.offerEntity.validate(offerObj) console.log('offerEntity: ', offerEntity) - // const displayCategory = await this.categorizeToken(offerEntity) - // console.log('displayCategory: ', displayCategory) + // 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) @@ -105,14 +108,35 @@ class OfferUseCases { // 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)}`) + 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)}`) - return 'placeholder' + 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 () { diff --git a/test/unit/mocks/adapters/wallet.js b/test/unit/mocks/adapters/wallet.js index 19ce10b..2fadec3 100644 --- a/test/unit/mocks/adapters/wallet.js +++ b/test/unit/mocks/adapters/wallet.js @@ -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) { diff --git a/test/unit/mocks/use-cases/offer-mock-data.js b/test/unit/mocks/use-cases/offer-mock-data.js new file mode 100644 index 0000000..46831a1 --- /dev/null +++ b/test/unit/mocks/use-cases/offer-mock-data.js @@ -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 +} diff --git a/test/unit/use-cases/offer.use-case.unit.js b/test/unit/use-cases/offer.use-case.unit.js index c7b8d06..1bd405e 100644 --- a/test/unit/use-cases/offer.use-case.unit.js +++ b/test/unit/use-cases/offer.use-case.unit.js @@ -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') + }) + }) }) From 1c1169dd930637c6170828d8343204379bf12b0b Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sun, 11 Sep 2022 14:41:56 -0700 Subject: [PATCH 3/3] linting --- production/rpi-docker/docker-compose.yml | 8 ++++---- production/scripts/sweep-nfts.js | 4 ++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/production/rpi-docker/docker-compose.yml b/production/rpi-docker/docker-compose.yml index 8a5fca5..dfe77f4 100644 --- a/production/rpi-docker/docker-compose.yml +++ b/production/rpi-docker/docker-compose.yml @@ -67,10 +67,10 @@ services: restart: always bch-dex: - #build: - # context: ./bch-dex/ - # dockerfile: Dockerfile - image: christroutner/bch-dex-rpi:v1.9.6 + 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' diff --git a/production/scripts/sweep-nfts.js b/production/scripts/sweep-nfts.js index 547e7b4..86d34c7 100644 --- a/production/scripts/sweep-nfts.js +++ b/production/scripts/sweep-nfts.js @@ -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,