diff --git a/package.json b/package.json index 22c7047..bacdcd5 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "test:all": "export BCH_DEX=test && nyc --reporter=text mocha --exit --timeout 15000 --recursive test/unit test/e2e/automated/", "test:unit": "export BCH_DEX=test && mocha --exit --timeout 15000 --recursive test/unit/", "test:e2e:auto": "export BCH_DEX=test && mocha --exit --timeout 15000 test/e2e/automated/", - "test:integration": "export BCH_DEX=test && mocha --exit --timeout 30000 --recursive test/integration", + "test:integration": "export BCH_DEX=test && mocha --exit --timeout 45000 --recursive test/integration", "test:temp": "export BCH_DEX=test && mocha --exit --timeout 15000 -g '#rate-limit' test/unit/json-rpc/", "lint": "standard --env mocha --fix", "docs": "./node_modules/.bin/apidoc -i src/ -o docs", diff --git a/src/use-cases/offer.js b/src/use-cases/offer.js index 0b26410..70042ac 100644 --- a/src/use-cases/offer.js +++ b/src/use-cases/offer.js @@ -112,6 +112,12 @@ class OfferLib { async ensureFunds (offerEntity) { try { // console.log('this.adapters.wallet: ', this.adapters.wallet.bchWallet) + // console.log(`walletInfo: ${JSON.stringify(this.adapters.wallet.bchWallet.walletInfo, null, 2)}`) + + // 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) + if (!canWriteToP2WDB) throw new Error('App wallet does not have funds for writing to the P2WDB.') // Get UTXOs. const utxos = this.adapters.wallet.bchWallet.utxos.utxoStore @@ -129,16 +135,18 @@ class OfferLib { // Get the total amount of tokens in the wallet that match the token // in the offer. let totalTokenBalance = 0 - tokenUtxos.map(x => (totalTokenBalance += parseFloat(x.tokenQty))) + tokenUtxos.map(x => (totalTokenBalance += parseFloat(x.qtyStr))) // console.log('totalTokenBalance: ', totalTokenBalance) // If there are fewer tokens in the wallet than what's in the offer, // throw an error. - if (totalTokenBalance <= offerEntity.numTokens) { + if (totalTokenBalance <= offerEntity.numTokens || isNaN(totalTokenBalance)) { throw new Error( 'App wallet does not have enough tokens to satisfy the SELL offer.' ) } + + // } else { // Buy Offer throw new Error('Buy orders are not supported yet.') diff --git a/test/integration/use-cases/offer-use-case-integration.js b/test/integration/use-cases/offer-use-case-integration.js new file mode 100644 index 0000000..f85ba0c --- /dev/null +++ b/test/integration/use-cases/offer-use-case-integration.js @@ -0,0 +1,65 @@ +/* + Integration tests for the offer-use-case.js library + + For these tests to pass, the app wallet must have Trout test tokens with + a token ID of a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2 +*/ + +// Public npm libraries +const assert = require('chai').assert + +// Local npm libraries +const Adapters = require('../../../src/adapters') +const Offer = require('../../../src/use-cases/offer') + +describe('#offer-use-case.js', () => { + let uut + + before(async () => { + const adapters = new Adapters() + adapters.config.getJwtAtStartup = false + await adapters.start() + + uut = new Offer({ adapters }) + }) + + describe('#ensureFunds', () => { + it('should throw error if wallet does not have enough funds', async () => { + try { + const offerEntity = { + lokadId: 'SWP', + messageType: 1, + messageClass: 1, + tokenId: 'token-id', + buyOrSell: 'sell', + rateInSats: 1000, + minSatsToExchange: 1250, + numTokens: 1 + } + + await uut.ensureFunds(offerEntity) + + assert.fail('Unexpected code path') + } catch (err) { + assert.include(err.message, 'App wallet does not have enough tokens to satisfy the SELL offer.') + } + }) + + it('should return true if wallet has enough funds', async () => { + const offerEntity = { + lokadId: 'SWP', + messageType: 1, + messageClass: 1, + tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2', + buyOrSell: 'sell', + rateInSats: 1000, + minSatsToExchange: 1250, + numTokens: 1 + } + + const result = await uut.ensureFunds(offerEntity) + + assert.equal(result, true) + }) + }) +}) diff --git a/test/unit/mocks/adapters/index.js b/test/unit/mocks/adapters/index.js index 908af32..8b0e5c6 100644 --- a/test/unit/mocks/adapters/index.js +++ b/test/unit/mocks/adapters/index.js @@ -13,7 +13,8 @@ const ipfs = { ipfsCoord: { useCases: { peer: { - sendPrivateMessage: () => {} + sendPrivateMessage: () => { + } } } } @@ -106,8 +107,10 @@ const bch = { // } const { MockBchWallet } = require('./wallet') const wallet = { - burnPsf: async () => {}, - generateSignature: async () => {}, + burnPsf: async () => { + }, + generateSignature: async () => { + }, getKeyPair: async () => { return { cashAddress: 'fakeAddr', wif: 'fakeWif', hdIndex: 1 } }, @@ -115,7 +118,9 @@ const wallet = { } const p2wdb = { - write: async () => {} + write: async () => { + }, + checkForSufficientFunds: async () => true } -module.exports = { ipfs, localdb, bch, wallet, p2wdb, bchjs } +module.exports = { ipfs, localdb, bch, wallet, p2wdb, bchjs} diff --git a/test/unit/use-cases/offer.use-case.unit.js b/test/unit/use-cases/offer.use-case.unit.js index 0d1f8de..5a66e27 100644 --- a/test/unit/use-cases/offer.use-case.unit.js +++ b/test/unit/use-cases/offer.use-case.unit.js @@ -46,6 +46,54 @@ describe('#offer-use-case', () => { }) }) + describe('#ensureFunds', () => { + it('should return true if wallet has enough funds for a sell order', async () => { + const offerEntity = { + lokadId: 'SWP', + messageType: 1, + messageClass: 1, + tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2', + buyOrSell: 'sell', + rateInSats: 1000, + minSatsToExchange: 0, + numTokens: 1 + } + + const result = await uut.ensureFunds(offerEntity) + + assert.equal(result, true) + }) + }) + + describe('#moveTokens', () => { + it('should move tokens to the holding address', async () => { + // Mock dependencies + // sandbox + // .stub(uut.adapters.wallet.bchWallet, 'sendTokens') + // .resolves('fakeTxid') + + const offerEntity = { + lokadId: 'SWP', + messageType: 1, + messageClass: 1, + tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2', + buyOrSell: 'sell', + rateInSats: 1000, + minSatsToExchange: 0, + numTokens: 1 + } + + const result = await uut.moveTokens(offerEntity) + // console.log('result: ', result) + + assert.property(result, 'txid') + assert.property(result, 'vout') + + assert.equal(result.txid, 'fakeTxid') + assert.equal(result.vout, 0) + }) + }) + describe('#createOffer', () => { it('should create an offer and return the hash', async () => { const entryObj = { @@ -91,54 +139,4 @@ describe('#offer-use-case', () => { } }) }) - - describe('#ensureFunds', () => { - it('should return true if wallet has enough funds for a sell order', async () => { - const offerEntity = { - lokadId: 'SWP', - messageType: 1, - messageClass: 1, - tokenId: - 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2', - buyOrSell: 'sell', - rateInSats: 1000, - minSatsToExchange: 0, - numTokens: 1 - } - - const result = await uut.ensureFunds(offerEntity) - - assert.equal(result, true) - }) - }) - - describe('#moveTokens', () => { - it('should move tokens to the holding address', async () => { - // Mock dependencies - // sandbox - // .stub(uut.adapters.wallet.bchWallet, 'sendTokens') - // .resolves('fakeTxid') - - const offerEntity = { - lokadId: 'SWP', - messageType: 1, - messageClass: 1, - tokenId: - 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2', - buyOrSell: 'sell', - rateInSats: 1000, - minSatsToExchange: 0, - numTokens: 1 - } - - const result = await uut.moveTokens(offerEntity) - // console.log('result: ', result) - - assert.property(result, 'txid') - assert.property(result, 'vout') - - assert.equal(result.txid, 'fakeTxid') - assert.equal(result.vout, 0) - }) - }) })