diff --git a/src/adapters/localdb/models/order.js b/src/adapters/localdb/models/order.js index 8e72d8b..4f31e2a 100644 --- a/src/adapters/localdb/models/order.js +++ b/src/adapters/localdb/models/order.js @@ -10,20 +10,20 @@ import mongoose from 'mongoose' const Order = new mongoose.Schema({ // Token data - tokenId: { type: String }, - utxoTxid: { type: String }, - utxoVout: { type: Number }, - ticker: { type: String }, - tokenType: { type: Number }, + tokenId: { type: String, required: true }, + utxoTxid: { type: String, required: true }, + utxoVout: { type: Number, required: true }, + ticker: { type: String, required: true }, + tokenType: { type: Number, required: true }, // Trade data - buyOrSell: { type: String }, - numTokens: { type: Number }, - rateInBaseUnit: { type: String }, + buyOrSell: { type: String, required: true }, + numTokens: { type: Number, required: true }, + rateInBaseUnit: { type: String, required: true }, minUnitsToExchange: { type: String }, p2wdbTxid: { type: String }, p2wdbHash: { type: String }, - makerAddr: { type: String }, + makerAddr: { type: String, required: true }, // Authentication data signature: { type: String }, @@ -32,13 +32,16 @@ const Order = new mongoose.Schema({ offerPubKey: { type: String }, // Wallet Data - hdIndex: { type: Number }, // HD index address holding the UTXO for this offer. + hdIndex: { type: Number, required: true }, // HD index address holding the UTXO for this offer. // SWaP Protocol Properties lokadId: { type: String }, messageType: { type: Number }, messageClass: { type: Number }, - nostrEventId: { type: String } // Nostr Event Id. + nostrEventId: { type: String, required: true }, // Nostr Event Id. + + // Additional properties found in createOrder + dataType: { type: String, required: true } }) export default mongoose.model('order', Order) diff --git a/src/adapters/wallet.js b/src/adapters/wallet.js index b1aba82..fbb7dd5 100644 --- a/src/adapters/wallet.js +++ b/src/adapters/wallet.js @@ -362,7 +362,7 @@ class WalletAdapter { throw err } - // return true + // return true } // Move tokens to an address controlled by the HD wallet, to generate a @@ -467,6 +467,13 @@ class WalletAdapter { // then broadcasting the transaction to the network. async completeTx (hex, hdIndex) { try { + // Input validation + if (!hex || typeof hex !== 'string') { + throw new Error('hex must be a string!') + } + if (typeof hdIndex !== 'number' || hdIndex < 0) { + throw new Error('hdIndex must be a non-negative number!') + } // console.log('hex: ', hex) console.log('completeTx() hdIndex: ', hdIndex) @@ -583,6 +590,16 @@ class WalletAdapter { async moveTokensFromCustomWallet (inObj = {}) { try { const { tokenId, qty, wallet } = inObj + // Input validation + if (!tokenId || typeof tokenId !== 'string') { + throw new Error('tokenId must be a string!') + } + if (!qty) { + throw new Error('qty must be a number!') + } + if (!wallet) { + throw new Error('wallet is required!') + } const keyPair = await this.getKeyPair() console.log('keyPair: ', keyPair) @@ -607,17 +624,16 @@ class WalletAdapter { ) const txid = await wallet.sendTokens(receiver, 3) - console.log('txid: ', txid) const utxoInfo = { txid, vout: 1, - hdIndex: wallet.walletInfo.hdIndex, + hdIndex: keyPair.hdIndex, tokenType: tokenUtxos[0].tokenType } return utxoInfo } catch (err) { - console.error('Error in wallet.js/moveTokens()') + console.error('Error in wallet.js/moveTokensFromCustomWallet()') throw err } } diff --git a/test/unit/adapters/order-pagination.unit.js b/test/unit/adapters/order-pagination.unit.js index 4248722..2c7596c 100644 --- a/test/unit/adapters/order-pagination.unit.js +++ b/test/unit/adapters/order-pagination.unit.js @@ -47,7 +47,15 @@ describe('#OrderPagination', () => { timestamp: 'timestamp', localTimestamp: 'localTimestamp', txid: 'txid', - p2wdbHash: 'p2wdbHash' + p2wdbHash: 'p2wdbHash', + hdIndex: 0, + nostrEventId: 'nostrEventId', + dataType: 'dataType', + makerAddr: 'makerAddr', + rateInBaseUnit: 'rateInBaseUnit', + minUnitsToExchange: 'minUnitsToExchange', + ticker: 'ticker', + tokenType: 1 }) await order.save() }) diff --git a/test/unit/adapters/wallet.unit.js b/test/unit/adapters/wallet.unit.js new file mode 100644 index 0000000..c9e6760 --- /dev/null +++ b/test/unit/adapters/wallet.unit.js @@ -0,0 +1,138 @@ + +/* + Unit tests for the adapter/wallet.js library. +*/ + +// Global npm libraries +import { assert } from 'chai' +import sinon from 'sinon' + +import WalletAdapter from '../../../src/adapters/wallet.js' +import { MockBchWallet } from '../mocks/adapters/wallet.js' + +describe('#wallet', () => { + let uut + let sandbox + + beforeEach(() => { + uut = new WalletAdapter() + uut.bchWallet = new MockBchWallet() + uut.bitcoinJs = { + Transaction: { + fromHex: () => { return { } }, + SIGHASH_ALL: () => { return { } } + }, + TransactionBuilder: { + fromTransaction: () => { return { sign: () => { return { } }, build: () => { return { toHex: () => { return 'hex' } } } } } + }, + ECPair: { + fromWIF: () => { return { } } + } + } + sandbox = sinon.createSandbox() + }) + + afterEach(() => sandbox.restore()) + + describe('#moveTokensFromCustomWallet', () => { + it('should move tokens from a custom wallet', async () => { + sandbox.stub(uut, 'getKeyPair').resolves({ + cashAddress: 'cashAddress', + wif: 'wif', + hdIndex: 11 + }) + + const customWallet = new MockBchWallet() + customWallet.sendTokens = () => { return 'move token tx id result' } + customWallet.utxos.utxoStore.slpUtxos.type1.tokens = [ + { + tokenId: 'tokenId', + tokenType: 1, + qty: 1 + } + ] + const inObj = { + qty: 1, + wallet: customWallet, + tokenId: 'tokenId' + } + const result = await uut.moveTokensFromCustomWallet(inObj) + assert.equal(result.txid, 'move token tx id result') + assert.equal(result.hdIndex, 11) + assert.equal(result.tokenType, 1) + assert.equal(result.vout, 1) + }) + it('should throw an error if the wallet is not provided', async () => { + try { + const inObj = { + qty: 1, + tokenId: 'tokenId' + } + await uut.moveTokensFromCustomWallet(inObj) + assert.fail('Unexpected code path') + } catch (error) { + assert.equal(error.message, 'wallet is required!') + } + }) + it('should throw an error if the tokenId is not provided', async () => { + try { + const inObj = { + qty: 1, + wallet: new MockBchWallet() + } + await uut.moveTokensFromCustomWallet(inObj) + assert.fail('Unexpected code path') + } catch (error) { + assert.equal(error.message, 'tokenId must be a string!') + } + }) + it('should throw an error if the qty is not provided', async () => { + try { + const inObj = { + tokenId: 'tokenId', + wallet: new MockBchWallet() + } + await uut.moveTokensFromCustomWallet(inObj) + assert.fail('Unexpected code path') + } catch (error) { + assert.equal(error.message, 'qty must be a number!') + } + }) + }) + describe('#completeTx', () => { + it('should complete a transaction', async () => { + sandbox.stub(uut, 'getKeyPair').resolves({ + cashAddress: 'bitcoincash:qzl0d3gcqeypv4cy7gh8rgdszxa9vvm2acv7fqtd00', + wif: 'L5D2UAam8tvo3uii5kpgaGyjvVMimdrXu8nWGQSQjuuAix6ji1YQ', + hdIndex: 11 + }) + sandbox.stub(uut, 'deseralizeTx').resolves({ + txid: 'complete tx id result' + }) + sandbox.stub(uut.retryQueue, 'addToQueue').resolves('complete tx id result') + + const hex = 'hex' + const hdIndex = 11 + const txid = await uut.completeTx(hex, hdIndex) + assert.equal(txid, 'complete tx id result') + }) + it('should throw an error if the hex is not provided', async () => { + try { + const hdIndex = 11 + await uut.completeTx(null, hdIndex) + assert.fail('Unexpected code path') + } catch (error) { + assert.equal(error.message, 'hex must be a string!') + } + }) + it('should throw an error if the hdIndex is not provided', async () => { + try { + const hex = 'hex' + await uut.completeTx(hex, null) + assert.fail('Unexpected code path') + } catch (error) { + assert.equal(error.message, 'hdIndex must be a non-negative number!') + } + }) + }) +})