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 | |
|---|---|---|---|
|
|
ddce40bb52 | ||
|
|
746f9a3488 | ||
|
|
a611580f48 | ||
|
|
07c671ede4 | ||
|
|
7c8d73810d | ||
|
|
9829e8581c | ||
|
|
a12aa5609d | ||
|
|
abf834c5a8 | ||
|
|
7ae557d143 | ||
|
|
5f4b0c8da3 | ||
|
|
6ef197dae3 | ||
|
|
fafa7eecd9 |
@@ -46,11 +46,11 @@ WORKDIR /home/safeuser
|
||||
|
||||
# Clone the rest.bitcoin.com repository
|
||||
WORKDIR /home/safeuser
|
||||
RUN git clone https://github.com/Permissionless-Software-Foundation/bch-dex-ui-v2
|
||||
RUN git clone https://github.com/Permissionless-Software-Foundation/bch-dex-ui-v3
|
||||
|
||||
# Switch to the desired branch. `master` is usually stable,
|
||||
# and `stage` has the most up-to-date changes.
|
||||
WORKDIR /home/safeuser/bch-dex-ui-v2
|
||||
WORKDIR /home/safeuser/bch-dex-ui-v3
|
||||
|
||||
# For development: switch to unstable branch
|
||||
RUN git checkout ct-unstable
|
||||
@@ -61,13 +61,13 @@ RUN git checkout ct-unstable
|
||||
RUN npm install
|
||||
|
||||
# Build the site
|
||||
RUN CI=true npm run build
|
||||
RUN npm run build
|
||||
|
||||
# Load the NGINX image.
|
||||
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-v2/build /usr/share/nginx/html
|
||||
COPY --from=builder /home/safeuser/bch-dex-ui-v3/build /usr/share/nginx/html
|
||||
|
||||
#USER safeuser
|
||||
|
||||
@@ -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)
|
||||
|
||||
+63
-1
@@ -54,6 +54,7 @@ class WalletAdapter {
|
||||
this.deseralizeTx = this.deseralizeTx.bind(this)
|
||||
this.completeTx = this.completeTx.bind(this)
|
||||
this.reclaimTokens = this.reclaimTokens.bind(this)
|
||||
this.moveTokensFromCustomWallet = this.moveTokensFromCustomWallet.bind(this)
|
||||
}
|
||||
|
||||
// Open the wallet file, or create one if the file doesn't exist.
|
||||
@@ -361,7 +362,7 @@ class WalletAdapter {
|
||||
throw err
|
||||
}
|
||||
|
||||
// return true
|
||||
// return true
|
||||
}
|
||||
|
||||
// Move tokens to an address controlled by the HD wallet, to generate a
|
||||
@@ -466,7 +467,15 @@ 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)
|
||||
|
||||
const bchjs = this.bchWallet.bchjs
|
||||
|
||||
@@ -491,6 +500,8 @@ class WalletAdapter {
|
||||
// 'mainnet'
|
||||
// )
|
||||
|
||||
console.log('completeTx() this.walletInfo: ', this.walletInfo)
|
||||
|
||||
// Get the keypair for the address used in the Order
|
||||
const keyPair = await this.getKeyPair(hdIndex)
|
||||
console.log(`maker keyPair: ${JSON.stringify(keyPair, null, 2)}`)
|
||||
@@ -575,6 +586,57 @@ class WalletAdapter {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
const receiver = {
|
||||
address: keyPair.cashAddress,
|
||||
tokenId,
|
||||
qty
|
||||
}
|
||||
console.log('receiver: ', receiver)
|
||||
|
||||
// Update the UTXO store of the wallet.
|
||||
await wallet.initialize()
|
||||
|
||||
// Get the token type of the token being moved.
|
||||
// Combine Fungible and NFT token UTXOs.
|
||||
let tokenUtxos = wallet.utxos.utxoStore.slpUtxos.type1.tokens.concat(
|
||||
wallet.utxos.utxoStore.slpUtxos.nft.tokens)
|
||||
// Get token UTXOs that match the token in the order.
|
||||
tokenUtxos = tokenUtxos.filter(
|
||||
x => x.tokenId === tokenId
|
||||
)
|
||||
|
||||
const txid = await wallet.sendTokens(receiver, 3)
|
||||
const utxoInfo = {
|
||||
txid,
|
||||
vout: 1,
|
||||
hdIndex: keyPair.hdIndex,
|
||||
tokenType: tokenUtxos[0].tokenType
|
||||
}
|
||||
|
||||
return utxoInfo
|
||||
} catch (err) {
|
||||
console.error('Error in wallet.js/moveTokensFromCustomWallet()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default WalletAdapter
|
||||
|
||||
@@ -35,6 +35,8 @@ class OrderRESTControllerLib {
|
||||
// console.log('body: ', ctx.request.body)
|
||||
|
||||
const orderObj = ctx.request.body.order
|
||||
const user = ctx.state.user
|
||||
orderObj.userId = user._id
|
||||
console.log('orderObj: ', orderObj)
|
||||
|
||||
const { eventId, noteId } = await _this.useCases.order.createOrder(orderObj)
|
||||
|
||||
@@ -7,6 +7,7 @@ import Router from 'koa-router'
|
||||
|
||||
// Local libraries.
|
||||
import OrderRESTControllerLib from './controller.js'
|
||||
import Validators from '../middleware/validators.js'
|
||||
|
||||
let _this
|
||||
|
||||
@@ -32,12 +33,14 @@ class OrderRouter {
|
||||
}
|
||||
|
||||
// Encapsulate dependencies.
|
||||
this.validators = new Validators()
|
||||
this.orderRESTController = new OrderRESTControllerLib(dependencies)
|
||||
|
||||
// Instantiate the router and set the base route.
|
||||
const baseUrl = '/order'
|
||||
this.router = new Router({ prefix: baseUrl })
|
||||
|
||||
this.createOrder = this.createOrder.bind(this)
|
||||
_this = this
|
||||
}
|
||||
|
||||
@@ -49,7 +52,7 @@ class OrderRouter {
|
||||
}
|
||||
|
||||
// Define the routes and attach the controller.
|
||||
this.router.post('/', _this.orderRESTController.createOrder)
|
||||
this.router.post('/', this.createOrder)
|
||||
this.router.get('/list/all/:page', _this.orderRESTController.listOrders)
|
||||
this.router.post('/delete', _this.orderRESTController.deleteOrder)
|
||||
|
||||
@@ -57,6 +60,11 @@ class OrderRouter {
|
||||
app.use(_this.router.routes())
|
||||
app.use(_this.router.allowedMethods())
|
||||
}
|
||||
|
||||
async createOrder (ctx, next) {
|
||||
await _this.validators.ensureUser(ctx, next)
|
||||
await _this.orderRESTController.createOrder(ctx, next)
|
||||
}
|
||||
}
|
||||
|
||||
export default OrderRouter
|
||||
|
||||
@@ -514,7 +514,7 @@ class OfferUseCases {
|
||||
|
||||
// This function is called by the P2WDB webhook REST API handler. When a
|
||||
// Counter Offer is passed to bch-dex by the P2WDB, the data is then passed
|
||||
// to this function. It does due dilligence on the Counter Offer, then signs
|
||||
// to this function. It does due diligence on the Counter Offer, then signs
|
||||
// and broadcasts the transaction to accept the Counter Offer.
|
||||
async acceptCounterOffer (offerData) {
|
||||
try {
|
||||
@@ -535,7 +535,7 @@ class OfferUseCases {
|
||||
let orderData = {}
|
||||
try {
|
||||
orderData = await this.orderUseCase.findOrderByUtxo(offerData)
|
||||
// console.log(`orderData: ${JSON.stringify(orderData, null, 2)}`)
|
||||
console.log(`orderData: ${JSON.stringify(orderData, null, 2)}`)
|
||||
} catch (err) {
|
||||
console.log('Order matching this Counter Offer is not managed by this instance of bch-dex. Skipping.')
|
||||
|
||||
|
||||
+40
-12
@@ -25,6 +25,7 @@ class OrderLib {
|
||||
// Encapsulate dependencies
|
||||
this.orderEntity = new OrderEntity()
|
||||
this.OrderModel = this.adapters.localdb.Order
|
||||
this.UserModel = this.adapters.localdb.Users
|
||||
this.bch = this.adapters.bch
|
||||
this.config = config
|
||||
this.retryQueue = new RetryQueue({ retryPeriod: 1000, attempts: 3 })
|
||||
@@ -43,8 +44,32 @@ class OrderLib {
|
||||
|
||||
if (!entryObj.tokenId) throw new Error('entry does not contain required properties')
|
||||
|
||||
const user = await this.UserModel.findById(entryObj.userId)
|
||||
if (!user) throw new Error('user not found')
|
||||
|
||||
console.log(`Using FullStack.cash: ${this.config.useFullStackCash}`)
|
||||
const advancedConfig = {}
|
||||
if (this.config.useFullStackCash) {
|
||||
advancedConfig.interface = 'rest-api'
|
||||
advancedConfig.restURL = this.config.apiServer
|
||||
advancedConfig.apiToken = this.config.apiToken
|
||||
} else {
|
||||
advancedConfig.interface = 'consumer-api'
|
||||
advancedConfig.restURL = this.config.consumerUrl
|
||||
}
|
||||
|
||||
// Instantiate minimal-slp-wallet with the user's mnemonic.
|
||||
const BchWallet = this.adapters.wallet.BchWallet
|
||||
const userWallet = new BchWallet(user.mnemonic, advancedConfig)
|
||||
|
||||
// Wait for wallet to initialize.
|
||||
await userWallet.walletInfoPromise
|
||||
await userWallet.initialize()
|
||||
|
||||
// Specify the address to send payment.
|
||||
entryObj.makerAddr = this.adapters.wallet.bchWallet.walletInfo.cashAddress
|
||||
console.log('userWallet.walletInfo: ', userWallet.walletInfo)
|
||||
entryObj.makerAddr = userWallet.walletInfo.cashAddress
|
||||
console.log('entryObj.makerAddr: ', entryObj.makerAddr)
|
||||
// console.log('entryObj.makerAddr: ', entryObj.makerAddr)
|
||||
|
||||
// Input Validation
|
||||
@@ -53,7 +78,8 @@ class OrderLib {
|
||||
|
||||
// Optimize the wallet to speed up working with it.
|
||||
console.log('Optimizing wallet before creating new order.')
|
||||
await this.retryQueue.addToQueue(this.adapters.wallet.bchWallet.optimize, {})
|
||||
// await this.retryQueue.addToQueue(this.adapters.wallet.bchWallet.optimize, {})
|
||||
await userWallet.optimize()
|
||||
|
||||
// Ensure sufficient tokens exist to create the order.
|
||||
// await this.ensureFunds(orderEntity)
|
||||
@@ -61,24 +87,26 @@ class OrderLib {
|
||||
|
||||
// Get Ticker for token ID.
|
||||
// const tokenData = await this.adapters.wallet.bchWallet.getTxData([entryObj.tokenId])
|
||||
const tokenData = await this.retryQueue.addToQueue(this.adapters.wallet.bchWallet.getTxData, [entryObj.tokenId])
|
||||
// const tokenData = await this.retryQueue.addToQueue(this.adapters.wallet.bchWallet.getTxData, [entryObj.tokenId])
|
||||
const tokenData = await userWallet.getTxData([entryObj.tokenId])
|
||||
// console.log(`tokenData: ${JSON.stringify(tokenData, null, 2)}`)
|
||||
orderEntity.ticker = tokenData[0].tokenTicker
|
||||
|
||||
// Move the tokens to holding address.
|
||||
const moveObj = {
|
||||
tokenId: orderEntity.tokenId,
|
||||
qty: orderEntity.numTokens
|
||||
qty: orderEntity.numTokens,
|
||||
wallet: userWallet
|
||||
}
|
||||
// const utxoInfo = await this.adapters.wallet.moveTokens(moveObj)
|
||||
const utxoInfo = await this.retryQueue.addToQueue(this.adapters.wallet.moveTokens, moveObj)
|
||||
const utxoInfo = await this.retryQueue.addToQueue(this.adapters.wallet.moveTokensFromCustomWallet, moveObj)
|
||||
// console.log('utxoInfo: ', utxoInfo)
|
||||
|
||||
// Update the UTXO store for the wallet.
|
||||
await this.adapters.wallet.bchWallet.bchjs.Util.sleep(3000)
|
||||
await userWallet.bchjs.Util.sleep(3000)
|
||||
// await this.adapters.wallet.bchWallet.getUtxos()
|
||||
await this.retryQueue.addToQueue(this.adapters.wallet.bchWallet.initialize, {})
|
||||
|
||||
// await this.retryQueue.addToQueue(this.adapters.wallet.bchWallet.initialize, {})
|
||||
await userWallet.initialize()
|
||||
// Update the order with the new UTXO information.
|
||||
orderEntity.utxoTxid = utxoInfo.txid
|
||||
orderEntity.utxoVout = utxoInfo.vout
|
||||
@@ -159,7 +187,7 @@ class OrderLib {
|
||||
)
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
} else {
|
||||
// Buy Order
|
||||
throw new Error('Buy orders are not supported yet.')
|
||||
@@ -286,11 +314,11 @@ class OrderLib {
|
||||
try {
|
||||
const data = await this.OrderModel.find({})
|
||||
|
||||
// Sort entries so newest entries show first.
|
||||
// Sort entries so newest entries show first.
|
||||
.sort('-timestamp')
|
||||
// Skip to the start of the selected page.
|
||||
// Skip to the start of the selected page.
|
||||
.skip(page * DEFAULT_ENTRIES_PER_PAGE)
|
||||
// Only return 20 results.
|
||||
// Only return 20 results.
|
||||
.limit(DEFAULT_ENTRIES_PER_PAGE)
|
||||
|
||||
return data
|
||||
|
||||
@@ -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()
|
||||
})
|
||||
|
||||
@@ -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!')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -65,6 +65,9 @@ describe('#Order-REST-Router', () => {
|
||||
ctx.request.body = {
|
||||
order: {}
|
||||
}
|
||||
ctx.state.user = {
|
||||
id: 'testUserId'
|
||||
}
|
||||
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut.useCases.order, 'createOrder').resolves({ eventId: 'testEventId', noteId: 'testNoteId' })
|
||||
@@ -80,6 +83,9 @@ describe('#Order-REST-Router', () => {
|
||||
ctx.request.body = {
|
||||
order: {}
|
||||
}
|
||||
ctx.state.user = {
|
||||
id: 'testUserId'
|
||||
}
|
||||
|
||||
// Force an error
|
||||
sandbox
|
||||
|
||||
@@ -152,6 +152,7 @@ const wallet = {
|
||||
bchWallet: new MockBchWallet(),
|
||||
BchWallet: MockBchWallet,
|
||||
moveTokens: async () => {},
|
||||
moveTokensFromCustomWallet: async () => {},
|
||||
moveBch: async () => {},
|
||||
reclaimTokens: async ()=>{},
|
||||
generatePartialTx: async ()=>{},
|
||||
|
||||
@@ -173,8 +173,9 @@ describe('#order-use-case', () => {
|
||||
// Mock dependencies and force expected code path
|
||||
sandbox.stub(uut.orderEntity, 'inputValidate').returns(entryObj)
|
||||
sandbox.stub(uut, 'ensureFunds').resolves()
|
||||
sandbox.stub(uut.UserModel, 'findById').resolves({ mnemonic: 'testMnemonic' })
|
||||
sandbox.stub(uut.adapters.wallet.bchWallet.bchjs.Util, 'sleep').resolves()
|
||||
sandbox.stub(uut.adapters.wallet, 'moveTokens').resolves({ txid: 'fakeTxid', vout: 0, hdIndex: 1 })
|
||||
sandbox.stub(uut.adapters.wallet, 'moveTokensFromCustomWallet').resolves({ txid: 'fakeTxid', vout: 0, hdIndex: 1 })
|
||||
sandbox.stub(uut.adapters.wallet.bchWallet, 'initialize').resolves()
|
||||
sandbox.stub(uut.adapters.nostr, 'post').resolves('fakeEvenetId')
|
||||
|
||||
@@ -184,6 +185,55 @@ describe('#order-use-case', () => {
|
||||
assert.property(result, 'eventId')
|
||||
assert.property(result, 'noteId')
|
||||
})
|
||||
it('should create an order with consumer-api', async () => {
|
||||
uut.config.useFullStackCash = false
|
||||
const entryObj = {
|
||||
lokadId: 'SWP',
|
||||
messageType: 1,
|
||||
messageClass: 1,
|
||||
tokenId: 'token-id',
|
||||
buyOrSell: 'sell',
|
||||
rateInBaseUnit: 1000,
|
||||
minUnitsToExchange: 1250,
|
||||
numTokens: 1,
|
||||
ticker: 'TEST'
|
||||
}
|
||||
|
||||
// Mock dependencies and force expected code path
|
||||
sandbox.stub(uut.orderEntity, 'inputValidate').returns(entryObj)
|
||||
sandbox.stub(uut, 'ensureFunds').resolves()
|
||||
sandbox.stub(uut.UserModel, 'findById').resolves({ mnemonic: 'testMnemonic' })
|
||||
sandbox.stub(uut.adapters.wallet.bchWallet.bchjs.Util, 'sleep').resolves()
|
||||
sandbox.stub(uut.adapters.wallet, 'moveTokensFromCustomWallet').resolves({ txid: 'fakeTxid', vout: 0, hdIndex: 1 })
|
||||
sandbox.stub(uut.adapters.wallet.bchWallet, 'initialize').resolves()
|
||||
sandbox.stub(uut.adapters.nostr, 'post').resolves('fakeEvenetId')
|
||||
|
||||
const result = await uut.createOrder(entryObj)
|
||||
console.log('result: ', result)
|
||||
|
||||
assert.property(result, 'eventId')
|
||||
assert.property(result, 'noteId')
|
||||
})
|
||||
it('should throw error if user is not found', async () => {
|
||||
try {
|
||||
const entryObj = {
|
||||
lokadId: 'SWP',
|
||||
messageType: 1,
|
||||
messageClass: 1,
|
||||
tokenId: 'token-id',
|
||||
buyOrSell: 'sell',
|
||||
rateInBaseUnit: 1000,
|
||||
minUnitsToExchange: 1250,
|
||||
numTokens: 1,
|
||||
ticker: 'TEST'
|
||||
}
|
||||
await uut.createOrder(entryObj)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'user not found')
|
||||
}
|
||||
})
|
||||
|
||||
it('should catch and throw an error', async () => {
|
||||
try {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import mongoose from 'mongoose'
|
||||
import config from '../../config/index.js'
|
||||
import User from '../../src/models/users.js'
|
||||
import User from '../../src/adapters/localdb/models/users.js'
|
||||
|
||||
async function getUsers () {
|
||||
// Connect to the Mongo Database.
|
||||
@@ -0,0 +1,21 @@
|
||||
import mongoose from 'mongoose'
|
||||
// import config from '../../config/index.js'
|
||||
import User from '../../../src/adapters/localdb/models/users.js'
|
||||
|
||||
const mongooseConnectStr = 'mongodb://172.17.0.1:5666/bch-swap-service-prod'
|
||||
|
||||
async function getUsers () {
|
||||
// Connect to the Mongo Database.
|
||||
mongoose.Promise = global.Promise
|
||||
mongoose.set('useCreateIndex', true) // Stop deprecation warning.
|
||||
await mongoose.connect(
|
||||
mongooseConnectStr,
|
||||
{ useNewUrlParser: true, useUnifiedTopology: true }
|
||||
)
|
||||
|
||||
const users = await User.find({}, '-password')
|
||||
console.log(`users: ${JSON.stringify(users, null, 2)}`)
|
||||
|
||||
mongoose.connection.close()
|
||||
}
|
||||
getUsers()
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
This script will travers the HD wallet and sweep funds and tokens back
|
||||
into the root address (index 0). That root address needs to have funds
|
||||
to pay for the transactions.
|
||||
|
||||
This version will traverse the HD index of the wallet until it reaches
|
||||
the value in the nextAddress property. This ensures that all UTXOs
|
||||
that could be used by the wallet have been swept.
|
||||
*/
|
||||
|
||||
// Public npm libraries
|
||||
import BCHJS from '@psf/bch-js'
|
||||
|
||||
import BchTokenSweep from 'bch-token-sweep'
|
||||
|
||||
// Local libraries
|
||||
import WalletAdapter from '../../src/adapters/wallet.js'
|
||||
|
||||
async function sweepFunds () {
|
||||
try {
|
||||
// Open the wallet files.
|
||||
const wallet = new WalletAdapter()
|
||||
const walletInfo = await wallet.openWallet()
|
||||
const bchWallet = await wallet.instanceWallet(walletInfo)
|
||||
console.log('walletInfo: ', walletInfo)
|
||||
|
||||
const lastIndex = walletInfo.nextAddress
|
||||
|
||||
const rootAddr = walletInfo.cashAddress
|
||||
const rootWif = walletInfo.privateKey
|
||||
console.log(`Sweeping all funds into root address ${rootAddr}...`)
|
||||
|
||||
// Generate an HD tree
|
||||
const bchjs = new BCHJS()
|
||||
const rootSeed = await bchjs.Mnemonic.toSeed(walletInfo.mnemonic)
|
||||
const masterHDNode = bchjs.HDNode.fromSeed(rootSeed)
|
||||
|
||||
let hdIndex = 1
|
||||
|
||||
do {
|
||||
// Generate a keypair from the HD wallet.
|
||||
const childNode = masterHDNode.derivePath(`m/44'/245'/0'/0/${hdIndex}`)
|
||||
const cashAddress = bchjs.HDNode.toCashAddress(childNode)
|
||||
const wifToSweep = bchjs.HDNode.toWIF(childNode)
|
||||
|
||||
console.log(`\nSweeping HD Index ${hdIndex} with address ${cashAddress}`)
|
||||
|
||||
try {
|
||||
// Sweep tokens from address
|
||||
const sweeper = new BchTokenSweep(
|
||||
wifToSweep,
|
||||
rootWif,
|
||||
bchWallet,
|
||||
550,
|
||||
rootAddr
|
||||
)
|
||||
await sweeper.populateObjectFromNetwork()
|
||||
|
||||
const hex = await sweeper.sweepTo(rootAddr)
|
||||
// console.log(`hex: ${hex}`)
|
||||
|
||||
const txid = await sweeper.blockchain.broadcast(hex)
|
||||
|
||||
// console.log('Transaction ID', txid)
|
||||
console.log(`Swept HD index ${hdIndex}. TXID: ${txid}`)
|
||||
|
||||
// Wait between loop iterations.
|
||||
await bchjs.Util.sleep(3000)
|
||||
} catch (err) {
|
||||
console.log(`error message with index ${hdIndex}: ${err}`)
|
||||
}
|
||||
|
||||
hdIndex++
|
||||
} while (hdIndex <= lastIndex)
|
||||
|
||||
console.log(`${lastIndex} empty addresses detected. Exiting.`)
|
||||
|
||||
console.log('\n\nDo not forget to reset the nextAddress property in the wallet.json file!\n\n')
|
||||
} catch (err) {
|
||||
console.error('Error in sweepFunds(): ', err)
|
||||
}
|
||||
}
|
||||
sweepFunds()
|
||||
Reference in New Issue
Block a user