mirror of
https://github.com/Permissionless-Software-Foundation/bch-dex.git
synced 2026-09-22 09:11:59 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
abd4e872bf | ||
|
|
acd7154d41 | ||
|
|
63a1bea60d | ||
|
|
b0f42d613b | ||
|
|
8e5b87f142 | ||
|
|
92483ee5e1 | ||
|
|
a80b82da7a | ||
|
|
41d2aaa16d | ||
|
|
b9aeb8faa0 | ||
|
|
a4e75cdccc | ||
|
|
f74c94ef50 | ||
|
|
bfb51fdcc6 | ||
|
|
25aaf4146f | ||
|
|
8ee10f2f8c | ||
|
|
bf96821c2c | ||
|
|
9787806427 | ||
|
|
9043aab432 | ||
|
|
58989a81dc | ||
|
|
82acd623d8 | ||
|
|
e4fe8cc492 | ||
|
|
66b8e6b263 | ||
|
|
cabff83222 | ||
|
|
d94b527780 | ||
|
|
f0e8a05f08 | ||
|
|
278b4127e1 | ||
|
|
87b3d7519c | ||
|
|
ed4e75c9ad | ||
|
|
b0cc5a6e0b | ||
|
|
6b4139a29e | ||
|
|
925242262a | ||
|
|
74fe77009c | ||
|
|
51f7905dad | ||
|
|
0b1d3e63f6 | ||
|
|
549fd4fd5a | ||
|
|
846ee21c48 | ||
|
|
6654836440 | ||
|
|
a25a13673e | ||
|
|
30eed1ffb1 | ||
|
|
7884cffdc6 | ||
|
|
c1a8210fa7 | ||
|
|
9f3b482f57 | ||
|
|
9487027852 | ||
|
|
9065900608 | ||
|
|
2da319f896 | ||
|
|
449a98d80c | ||
|
|
2343120fcf | ||
|
|
2d4fa523cd | ||
|
|
c8cc3adbc5 | ||
|
|
169e0ad3d2 | ||
|
|
e20a0a5d27 | ||
|
|
6dda29bb14 | ||
|
|
dd662e9fe9 | ||
|
|
9b775c5bf6 | ||
|
|
2047673862 | ||
|
|
5b545aebeb | ||
|
|
1e75816125 | ||
|
|
455fd807a4 | ||
|
|
8b75ac0f32 | ||
|
|
f1a5ae70e2 | ||
|
|
46bd532eeb | ||
|
|
6f99b215cd | ||
|
|
3482540fc8 | ||
|
|
fb999c7bd4 | ||
|
|
a09e5a5ba7 | ||
|
|
ba14b97258 | ||
|
|
79ac3729c9 | ||
|
|
bd77754686 | ||
|
|
b3b5322226 | ||
|
|
ec1f950ffb | ||
|
|
b18bc51885 | ||
|
|
e8c35dd1f3 | ||
|
|
8be0accdca | ||
|
|
94c56a06b7 | ||
|
|
42f64e3304 | ||
|
|
cc9bbc3c5d | ||
|
|
0c6696875b | ||
|
|
37b2e426a5 | ||
|
|
f3713985ed | ||
|
|
4ed9994414 | ||
|
|
140707ce6b | ||
|
|
c289e82b0e | ||
|
|
84210d07c4 | ||
|
|
6d285c77cd | ||
|
|
7cefbd71ef | ||
|
|
85d6aa7d46 | ||
|
|
aaee25389f | ||
|
|
ce83ddcadf |
@@ -69,5 +69,6 @@ ipfs-service-provider.sh
|
||||
run-dev.sh
|
||||
wallet.json
|
||||
data/
|
||||
*.zip
|
||||
|
||||
!README.md
|
||||
|
||||
Vendored
+8
-1
@@ -47,6 +47,7 @@ export default {
|
||||
|
||||
// P2WDB URL that will accept API calls from the p2wdb npm library.
|
||||
p2wdbUrl: process.env.P2WDB_URL ? process.env.P2WDB_URL : 'https://p2wdb.fullstack.cash',
|
||||
// p2wdbUrl: process.env.P2WDB_URL ? process.env.P2WDB_URL : 'http://localhost:5010',
|
||||
|
||||
// FullStack.cash account information, used for automatic JWT handling.
|
||||
getJwtAtStartup: process.env.GET_JWT_AT_STARTUP ? true : false,
|
||||
@@ -62,6 +63,9 @@ export default {
|
||||
fullstackPassword: process.env.FULLSTACKPASS
|
||||
? process.env.FULLSTACKPASS
|
||||
: 'demo',
|
||||
authPass: process.env.FULLSTACK_AUTH_PASS
|
||||
? process.env.FULLSTACK_AUTH_PASS
|
||||
: '',
|
||||
|
||||
// IPFS settings.
|
||||
isCircuitRelay: process.env.ENABLE_CIRCUIT_RELAY ? true : false,
|
||||
@@ -113,5 +117,8 @@ export default {
|
||||
? parseInt(process.env.IPFS_API_PORT)
|
||||
: 5001,
|
||||
|
||||
chatPubSubChan: 'psf-ipfs-chat-001'
|
||||
chatPubSubChan: 'psf-ipfs-chat-001',
|
||||
|
||||
// IPFS gateway
|
||||
ipfsGateway: process.env.IPFS_GATEWAY ? process.env.IPFS_GATEWAY : 'https://p2wdb-gateway-678.fullstack.cash/ipfs/'
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
Generated
+671
-221
File diff suppressed because it is too large
Load Diff
+9
-7
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "bch-dex",
|
||||
"version": "1.0.0",
|
||||
"version": "2.0.2",
|
||||
"description": "A DEX on BCH for trading SLP tokens.",
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
@@ -28,12 +28,15 @@
|
||||
},
|
||||
"repository": "Permissionless-Software-Foundation/bch-dex",
|
||||
"dependencies": {
|
||||
"@chris.troutner/retry-queue": "1.0.8",
|
||||
"@psf/bch-js": "6.7.3",
|
||||
"axios": "0.27.2",
|
||||
"bch-message-lib": "2.2.1",
|
||||
"bch-token-sweep": "2.2.1",
|
||||
"bcryptjs": "2.4.3",
|
||||
"bitcoincashjs-lib": "3.3.3",
|
||||
"glob": "7.1.6",
|
||||
"ipfs-coord-esm": "9.1.3",
|
||||
"ipfs-coord-esm": "9.1.7",
|
||||
"ipfs-http-client": "58.0.0",
|
||||
"jsonrpc-lite": "2.2.0",
|
||||
"jsonwebtoken": "8.5.1",
|
||||
@@ -49,21 +52,20 @@
|
||||
"koa-router": "10.0.0",
|
||||
"koa-static": "5.0.0",
|
||||
"koa2-ratelimit": "0.9.1",
|
||||
"libp2p": "^0.36.2",
|
||||
"libp2p": "0.36.2",
|
||||
"line-reader": "0.4.0",
|
||||
"minimal-slp-wallet": "5.1.0",
|
||||
"minimal-slp-wallet": "5.8.9",
|
||||
"mongoose": "5.13.14",
|
||||
"node-fetch": "npm:@achingbrain/node-fetch@2.6.7",
|
||||
"nodemailer": "6.7.5",
|
||||
"p2wdb": "2.2.3",
|
||||
"p2wdb-esm": "2.2.9",
|
||||
"passport-local": "1.0.0",
|
||||
"public-ip": "^4.0.4",
|
||||
"public-ip": "4.0.4",
|
||||
"winston": "3.3.3",
|
||||
"winston-daily-rotate-file": "4.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"apidoc": "0.51.1",
|
||||
"bch-token-sweep": "2.1.2",
|
||||
"c8": "7.12.0",
|
||||
"chai": "4.3.0",
|
||||
"coveralls": "2.11.4",
|
||||
|
||||
@@ -72,7 +72,10 @@ RUN npm run docs
|
||||
#EXPOSE 5010
|
||||
|
||||
# Start the application.
|
||||
#COPY start-production.sh start-production.sh
|
||||
CMD ["./start-production.sh"]
|
||||
|
||||
#CMD ["npm", "start"]
|
||||
|
||||
# Used for debugging
|
||||
#COPY dummy-app.js dummy-app.js
|
||||
#CMD ["node", "dummy-app.js"]
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
setInterval(function () {
|
||||
const now = new Date()
|
||||
console.log(`timestamp: ${now.toLocaleString()}`)
|
||||
}, 5000)
|
||||
@@ -24,9 +24,11 @@ export DBURL=mongodb://172.17.0.1:5666/bch-swap-service-prod
|
||||
export PORT=5700
|
||||
|
||||
# bch-dex specific env vars
|
||||
export BCH_DEX=production
|
||||
export BCH_DEX=prod
|
||||
export WEBHOOKSERVICE=http://172.17.0.1:5667/webhook
|
||||
export WEBHOOKTARGET=http://172.17.0.1:5700/p2wdb
|
||||
export APP_ID=bch-dex-001
|
||||
|
||||
#export P2WDB_URL=http://172.17.0.1:5667
|
||||
|
||||
npm start
|
||||
|
||||
@@ -46,10 +46,11 @@ services:
|
||||
#build:
|
||||
# context: ./p2wdb/
|
||||
# dockerfile: Dockerfile
|
||||
image: christroutner/p2wdb:v3.1.8
|
||||
image: christroutner/p2wdb:v4.1.3
|
||||
container_name: p2wdb
|
||||
environment:
|
||||
CONSUMER_URL: 'https://free-bch.fullstack.cash'
|
||||
#ENABLE_BCH_PAYMENT: 1
|
||||
DEBUG_LEVEL: 1
|
||||
logging:
|
||||
driver: 'json-file'
|
||||
@@ -67,13 +68,14 @@ services:
|
||||
volumes:
|
||||
- ../data/ipfsdata/p2wdb:/home/safeuser/ipfs-p2wdb-service/.ipfsdata/p2wdb
|
||||
- ./p2wdb/start-production.sh:/home/safeuser/ipfs-p2wdb-service/start-production.sh
|
||||
#- ../scripts/wallet.json:/home/safeuser/ipfs-p2wdb-service/wallet.json
|
||||
restart: always
|
||||
|
||||
bch-dex:
|
||||
#build:
|
||||
# context: ./bch-dex/
|
||||
# dockerfile: Dockerfile
|
||||
image: christroutner/bch-dex:v1.13.0
|
||||
image: christroutner/bch-dex:v2.2.4
|
||||
container_name: bch-dex
|
||||
environment:
|
||||
CONSUMER_URL: 'https://free-bch.fullstack.cash'
|
||||
@@ -97,7 +99,7 @@ services:
|
||||
#build:
|
||||
# context: ./bch-dex-ui/
|
||||
# dockerfile: Dockerfile
|
||||
image: christroutner/bch-dex-ui:v1.12.0
|
||||
image: christroutner/bch-dex-ui:v1.2.4
|
||||
container_name: dex-ui
|
||||
environment:
|
||||
SERVER: 'http://192.168.2.3'
|
||||
|
||||
@@ -24,7 +24,7 @@ export DBURL=mongodb://172.17.0.1:5666/bch-swap-service-prod
|
||||
export PORT=5700
|
||||
|
||||
# bch-dex specific env vars
|
||||
export BCH_DEX=production
|
||||
export BCH_DEX=prod
|
||||
export WEBHOOKSERVICE=http://172.17.0.1:5667/webhook
|
||||
export WEBHOOKTARGET=http://172.17.0.1:5700/p2wdb
|
||||
export APP_ID=bch-dex-001
|
||||
|
||||
@@ -43,7 +43,7 @@ services:
|
||||
#build:
|
||||
# context: ./p2wdb/
|
||||
# dockerfile: Dockerfile
|
||||
image: christroutner/p2wdb-rpi:v3.1.5
|
||||
image: christroutner/p2wdb-rpi:v3.1.9
|
||||
container_name: p2wdb
|
||||
environment:
|
||||
CONSUMER_URL: 'https://wa-usa-bch-consumer.fullstackcash.nl'
|
||||
@@ -70,7 +70,7 @@ services:
|
||||
#build:
|
||||
# context: ./bch-dex/
|
||||
# dockerfile: Dockerfile
|
||||
image: christroutner/bch-dex-rpi:v1.13.0
|
||||
image: christroutner/bch-dex-rpi:v2.0.8
|
||||
container_name: bch-dex
|
||||
environment:
|
||||
#CONSUMER_URL: 'https://free-bch.fullstack.cash'
|
||||
@@ -95,7 +95,7 @@ services:
|
||||
#build:
|
||||
# context: ./bch-dex-ui/
|
||||
# dockerfile: Dockerfile
|
||||
image: christroutner/bch-dex-ui-rpi:v1.2.2
|
||||
image: christroutner/bch-dex-ui-rpi:v1.2.4
|
||||
container_name: dex-ui
|
||||
logging:
|
||||
driver: 'json-file'
|
||||
|
||||
@@ -43,6 +43,6 @@ export IPFS_TCP_PORT=4001
|
||||
export IPFS_WS_PORT=4003
|
||||
|
||||
# P2WDB specific env vars
|
||||
export ORBITDB_NAME=/orbitdb/zdpuAqNiwLiJBfbRK7uihV2hAbNSXj78ufzv5VyQb8GuvRwDh/psf-bch-p2wdb-keyvalue-v3.0.0-0001
|
||||
#export ORBITDB_NAME=/orbitdb/zdpuAqNiwLiJBfbRK7uihV2hAbNSXj78ufzv5VyQb8GuvRwDh/psf-bch-p2wdb-keyvalue-v3.0.0-0001
|
||||
|
||||
npm start
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
This script is similar ot sweep-wallet.js, but it can do a much faster (bulk)
|
||||
scanning of addresses. However, this requires a subscription to a FullStack.cash
|
||||
or access to a web2 Cash Stack.
|
||||
|
||||
This script will scan 20 addresses at a time. If a balance is found in any
|
||||
of the addresses, it will sweep the funds from that address.
|
||||
*/
|
||||
/*
|
||||
// Public npm libraries
|
||||
import BchTokenSweep from 'bch-token-sweep/index.js'
|
||||
|
||||
// Local libraries
|
||||
import WalletAdapter from '../../src/adapters/wallet.js'
|
||||
import BCHJS from '@psf/bch-js'
|
||||
import JwtLib from 'jwt-bch-lib'
|
||||
|
||||
// Constants
|
||||
// const EMTPY_ADDR_CUTOFF = 100
|
||||
const LAST_ADDR_INDEX = 100
|
||||
|
||||
if (!process.env.BCHJSTOKEN) {
|
||||
console.log('You will need a JWT token from FullStack.cash to execute this script. Export it to the BCHJSTOKEN environment variable and try again.')
|
||||
exit(0)
|
||||
}
|
||||
|
||||
async function sweepFunds () {
|
||||
try {
|
||||
// Configure the wallet library to use a FullStack.cash or a local Cash Stack
|
||||
const walletLib = new WalletAdapter()
|
||||
walletLib.config.useFullStackCash = true
|
||||
walletLib.config.apiServer = 'http://192.168.2.129:3000/v5/'
|
||||
|
||||
// const walletInfo = await walletLib.openWallet()
|
||||
const walletInfo = await openWallet(walletLib)
|
||||
const wallet = await walletLib.instanceWallet(walletInfo)
|
||||
console.log('walletInfo: ', walletInfo)
|
||||
|
||||
const rootAddr = walletInfo.cashAddress
|
||||
const rootWif = walletInfo.privateKey
|
||||
console.log(`Sweeping all funds into root address ${rootAddr}...`)
|
||||
|
||||
// Instantiate bch-js, and use the JWT token saved to environment variable.
|
||||
|
||||
// Generate an HD tree
|
||||
// const bchjs = wallet.bchjs
|
||||
const bchjs = new BCHJS()
|
||||
const rootSeed = await bchjs.Mnemonic.toSeed(walletInfo.mnemonic)
|
||||
const masterHDNode = bchjs.HDNode.fromSeed(rootSeed)
|
||||
|
||||
const emptyAddrCnt = 0
|
||||
const hdIndex = 1
|
||||
|
||||
// Divide all the addresses by 20, since that's how many will be checked at once.
|
||||
const numOfLoops = Math.ceil(LAST_ADDR_INDEX / 20)
|
||||
|
||||
for (let i = 1; i < numOfLoops; i++) {
|
||||
const startIndex = i
|
||||
const stopIndex = i * 20
|
||||
console.log(`startIndex: ${startIndex}, stopIndex: ${stopIndex}`)
|
||||
|
||||
// Generate the next block of 20 key pairs to scan.
|
||||
const addrsToScan = []
|
||||
const wifsToScan = []
|
||||
for (let j = startIndex; j < stopIndex; j++) {
|
||||
// Generate a keypair from the HD wallet.
|
||||
const childNode = masterHDNode.derivePath(`m/44'/245'/0'/0/${j}`)
|
||||
const cashAddress = bchjs.HDNode.toCashAddress(childNode)
|
||||
const wifToSweep = bchjs.HDNode.toWIF(childNode)
|
||||
|
||||
addrsToScan.push(cashAddress)
|
||||
wifsToScan.push(wifToSweep)
|
||||
}
|
||||
console.log(`Scanning these addresses for a balance: ${JSON.stringify(addrsToScan, null, 2)}`)
|
||||
|
||||
// Scan a block of 20 addresses for a balance.
|
||||
const balances = await bchjs.Electrumx.balance(addrsToScan)
|
||||
console.log(`balances: ${JSON.stringify(balances, null, 2)}`)
|
||||
|
||||
// Scan through the results and look for a balance.
|
||||
for (let j = 0; j < balances.balances.length; j++) {
|
||||
const thisBalance = balances.balances[j]
|
||||
|
||||
// const combinedBal = thisBalance.balance.confirmed + thisBalance.balance.
|
||||
|
||||
// if(thisBalance.)
|
||||
}
|
||||
}
|
||||
|
||||
// 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 ${cashAddress} with private key ${wifToSweep}`)
|
||||
//
|
||||
// try {
|
||||
// // Sweep tokens from address
|
||||
// const sweeper = new BchTokenSweep(
|
||||
// wifToSweep,
|
||||
// rootWif,
|
||||
// wallet,
|
||||
// 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}`)
|
||||
//
|
||||
// emptyAddrCnt = 0
|
||||
//
|
||||
// // Wait between loop iterations.
|
||||
// await bchjs.Util.sleep(3000)
|
||||
// } catch (err) {
|
||||
// console.log(`error message with index ${hdIndex}: ${err.message}`)
|
||||
// // console.log(err)
|
||||
// emptyAddrCnt++
|
||||
// }
|
||||
//
|
||||
// hdIndex++
|
||||
// } while (emptyAddrCnt < EMTPY_ADDR_CUTOFF)
|
||||
//
|
||||
// console.log(`${EMTPY_ADDR_CUTOFF} 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()
|
||||
|
||||
// Open the wallet file, or create one if the file doesn't exist.
|
||||
// Does not instance the wallet. The output of this function is expected to
|
||||
// be passed to instanceWallet().
|
||||
async function openWallet (walletLib) {
|
||||
try {
|
||||
// console.log('this.WALLET_FILE: ', this.WALLET_FILE)
|
||||
const walletData = await walletLib.jsonFiles.readJSON('./wallet.json')
|
||||
|
||||
return walletData
|
||||
} catch (err) {
|
||||
console.error('Error in openWallet()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -6,7 +6,7 @@
|
||||
the DEX app wallet in order to be acceped.
|
||||
*/
|
||||
|
||||
const axios = require('axios')
|
||||
import axios from 'axios'
|
||||
|
||||
const LOCALHOST = 'http://localhost:5700'
|
||||
// const LOCALHOST = 'http://172.17.0.1:5700'
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
*/
|
||||
|
||||
// Public NPM libraries
|
||||
const BchWallet = require('minimal-slp-wallet/index')
|
||||
const fs = require('fs').promises
|
||||
import BchWallet from 'minimal-slp-wallet'
|
||||
|
||||
import { promises as fs } from 'fs'
|
||||
|
||||
async function createWallet () {
|
||||
try {
|
||||
@@ -15,9 +16,9 @@ async function createWallet () {
|
||||
}
|
||||
|
||||
// Wait for the wallet to be created.
|
||||
this.bchWallet = new BchWallet(undefined, advancedConfig)
|
||||
await this.bchWallet.walletInfoPromise
|
||||
const walletData = this.bchWallet.walletInfo
|
||||
const bchWallet = new BchWallet(undefined, advancedConfig)
|
||||
await bchWallet.walletInfoPromise
|
||||
const walletData = bchWallet.walletInfo
|
||||
walletData.nextAddress = 1
|
||||
|
||||
// Save the wallet file to disk.
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
This script is the same as sweep-wallet.js, but it requires a FullStack.cash
|
||||
account, which makes the scanning much faster.
|
||||
*/
|
||||
|
||||
// Public npm libraries
|
||||
// const BCHJS = require('@psf/bch-js')
|
||||
import BchTokenSweep from 'bch-token-sweep/index.js'
|
||||
|
||||
// Local libraries
|
||||
import WalletAdapter from '../../src/adapters/wallet.js'
|
||||
|
||||
// Constants
|
||||
// const EMTPY_ADDR_CUTOFF = 10
|
||||
const START_INDEX = 1
|
||||
const LAST_ADDR_INDEX = 100
|
||||
|
||||
if (!process.env.BCHJSTOKEN) {
|
||||
console.log('You will need a JWT token from FullStack.cash to execute this script. Export it to the BCHJSTOKEN environment variable and try again.')
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
async function sweepFunds () {
|
||||
try {
|
||||
// Configure the wallet library to use a FullStack.cash or a local Cash Stack
|
||||
const walletLib = new WalletAdapter()
|
||||
walletLib.config.useFullStackCash = true
|
||||
// walletLib.config.apiServer = 'http://192.168.2.129:3000/v5/'
|
||||
walletLib.config.apiServer = 'https://api.fullstack.cash/v5/'
|
||||
|
||||
// Open the wallet files.
|
||||
const walletInfo = await openWallet(walletLib)
|
||||
const wallet = await walletLib.instanceWallet(walletInfo)
|
||||
console.log('walletInfo: ', walletInfo)
|
||||
|
||||
const rootAddr = walletInfo.cashAddress
|
||||
const rootWif = walletInfo.privateKey
|
||||
console.log(`Sweeping all funds into root address ${rootAddr}...`)
|
||||
|
||||
// Generate an HD tree
|
||||
const bchjs = wallet.bchjs
|
||||
const rootSeed = await bchjs.Mnemonic.toSeed(walletInfo.mnemonic)
|
||||
const masterHDNode = bchjs.HDNode.fromSeed(rootSeed)
|
||||
|
||||
let emptyAddrCnt = 0
|
||||
let hdIndex = START_INDEX
|
||||
|
||||
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 ${cashAddress} with private key ${wifToSweep}`)
|
||||
|
||||
try {
|
||||
// Sweep tokens from address
|
||||
const sweeper = new BchTokenSweep(
|
||||
wifToSweep,
|
||||
rootWif,
|
||||
wallet,
|
||||
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}`)
|
||||
|
||||
emptyAddrCnt = 0
|
||||
|
||||
// Wait between loop iterations.
|
||||
await bchjs.Util.sleep(3000)
|
||||
} catch (err) {
|
||||
console.log(`error message with index ${hdIndex}: ${err.message}`)
|
||||
// console.log(err)
|
||||
emptyAddrCnt++
|
||||
}
|
||||
|
||||
hdIndex++
|
||||
// } while (emptyAddrCnt < EMTPY_ADDR_CUTOFF)
|
||||
// console.log(`${EMTPY_ADDR_CUTOFF} empty addresses detected. Exiting.`)
|
||||
} while (hdIndex < LAST_ADDR_INDEX)
|
||||
console.log(`Last index of ${LAST_ADDR_INDEX} reached. emptyAddrCnt: ${emptyAddrCnt}`)
|
||||
|
||||
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()
|
||||
|
||||
// Open the wallet file, or create one if the file doesn't exist.
|
||||
// Does not instance the wallet. The output of this function is expected to
|
||||
// be passed to instanceWallet().
|
||||
async function openWallet (walletLib) {
|
||||
try {
|
||||
// console.log('this.WALLET_FILE: ', this.WALLET_FILE)
|
||||
const walletData = await walletLib.jsonFiles.readJSON('./wallet.json')
|
||||
|
||||
return walletData
|
||||
} catch (err) {
|
||||
console.error('Error in openWallet()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
This script is the same as sweep-wallet.js, but instead of sweeping the keys,
|
||||
it simply lists out the addresses in the HD keychain. This is hany when trying
|
||||
to figure out which address a token was sent to.
|
||||
*/
|
||||
|
||||
// Public npm libraries
|
||||
|
||||
// Local libraries
|
||||
import WalletAdapter from '../../src/adapters/wallet.js'
|
||||
|
||||
// Constants
|
||||
const START_INDEX = 1
|
||||
const LAST_ADDR_INDEX = 100
|
||||
|
||||
async function listAddrs () {
|
||||
try {
|
||||
// Configure the wallet library to use a FullStack.cash or a local Cash Stack
|
||||
const walletLib = new WalletAdapter()
|
||||
|
||||
// Open the wallet files.
|
||||
const walletInfo = await openWallet(walletLib)
|
||||
const wallet = await walletLib.instanceWallet(walletInfo)
|
||||
console.log('walletInfo: ', walletInfo)
|
||||
|
||||
const rootAddr = walletInfo.cashAddress
|
||||
// const rootWif = walletInfo.privateKey
|
||||
console.log(`Sweeping all funds into root address ${rootAddr}...`)
|
||||
|
||||
// Generate an HD tree
|
||||
const bchjs = wallet.bchjs
|
||||
const rootSeed = await bchjs.Mnemonic.toSeed(walletInfo.mnemonic)
|
||||
const masterHDNode = bchjs.HDNode.fromSeed(rootSeed)
|
||||
|
||||
let hdIndex = START_INDEX
|
||||
|
||||
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(`HD Index ${hdIndex}: ${cashAddress}`)
|
||||
|
||||
hdIndex++
|
||||
} while (hdIndex < LAST_ADDR_INDEX)
|
||||
console.log(`Last index of ${LAST_ADDR_INDEX} reached.`)
|
||||
} catch (err) {
|
||||
console.error('Error in sweepFunds(): ', err)
|
||||
}
|
||||
}
|
||||
listAddrs()
|
||||
|
||||
// Open the wallet file, or create one if the file doesn't exist.
|
||||
// Does not instance the wallet. The output of this function is expected to
|
||||
// be passed to instanceWallet().
|
||||
async function openWallet (walletLib) {
|
||||
try {
|
||||
// console.log('this.WALLET_FILE: ', this.WALLET_FILE)
|
||||
const walletData = await walletLib.jsonFiles.readJSON('./wallet.json')
|
||||
|
||||
return walletData
|
||||
} catch (err) {
|
||||
console.error('Error in openWallet()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -5,10 +5,10 @@
|
||||
*/
|
||||
|
||||
// Global npm libraries
|
||||
const BchWallet = require('minimal-slp-wallet/index')
|
||||
import BchWallet from 'minimal-slp-wallet'
|
||||
|
||||
// Local libraries
|
||||
const WalletAdapter = require('../../src/adapters/wallet')
|
||||
import WalletAdapter from '../../src/adapters/wallet.js'
|
||||
|
||||
// Constants
|
||||
const EMTPY_ADDR_CUTOFF = 10
|
||||
|
||||
@@ -2,16 +2,14 @@
|
||||
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 script will sweep BCH and fungible (Type 1) tokens, but not NFTs.
|
||||
*/
|
||||
|
||||
// Public npm libraries
|
||||
// const BCHJS = require('@psf/bch-js')
|
||||
const BchTokenSweep = require('bch-token-sweep/index')
|
||||
import BchTokenSweep from 'bch-token-sweep/index.js'
|
||||
|
||||
// Local libraries
|
||||
const WalletAdapter = require('../../src/adapters/wallet')
|
||||
import WalletAdapter from '../../src/adapters/wallet.js'
|
||||
|
||||
// Constants
|
||||
const EMTPY_ADDR_CUTOFF = 10
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
import BCHJS from '@psf/bch-js'
|
||||
|
||||
import MsgLib from 'bch-message-lib'
|
||||
import BchWallet from 'minimal-slp-wallet/index.js'
|
||||
import BchWallet from 'minimal-slp-wallet'
|
||||
|
||||
class Bch {
|
||||
constructor () {
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import IpfsCoord from 'ipfs-coord-esm'
|
||||
|
||||
// import BCHJS from '@psf/bch-js';
|
||||
import SlpWallet from 'minimal-slp-wallet/index.js'
|
||||
import SlpWallet from 'minimal-slp-wallet'
|
||||
import publicIp from 'public-ip'
|
||||
|
||||
// Local libraries
|
||||
|
||||
@@ -6,7 +6,7 @@ const Offer = new mongoose.Schema({
|
||||
tokenId: { type: String },
|
||||
utxoTxid: { type: String },
|
||||
utxoVout: { type: Number },
|
||||
ticker: { type: String },
|
||||
ticker: { type: String, default: '' },
|
||||
tokenType: { type: Number },
|
||||
|
||||
// Trade data
|
||||
|
||||
@@ -5,10 +5,11 @@
|
||||
// Public npm libraries.
|
||||
import axios from 'axios'
|
||||
|
||||
import { Write, Read } from 'p2wdb/index.js'
|
||||
import P2WDB from 'p2wdb-esm'
|
||||
|
||||
// Local libraries
|
||||
import config from '../../config/index.js'
|
||||
const { Write, Read } = P2WDB
|
||||
|
||||
class P2wdbAdapter {
|
||||
constructor (localConfig = {}) {
|
||||
@@ -27,6 +28,10 @@ class P2wdbAdapter {
|
||||
|
||||
// Allow the localConfig to overwrite the config file values.
|
||||
this.p2wdbURL = localConfig.p2wdbURL || config.p2wdbUrl
|
||||
|
||||
// Bind the 'this' object to all functions
|
||||
this.write = this.write.bind(this)
|
||||
this.checkForSufficientFunds = this.checkForSufficientFunds.bind(this)
|
||||
}
|
||||
|
||||
// Write some data to the P2WDB
|
||||
|
||||
+52
-3
@@ -3,7 +3,8 @@
|
||||
*/
|
||||
|
||||
// Public npm libraries
|
||||
import BchWallet from 'minimal-slp-wallet/index.js'
|
||||
import BchWallet from 'minimal-slp-wallet'
|
||||
import BchTokenSweep from 'bch-token-sweep/index.js'
|
||||
|
||||
import bitcoinJs from 'bitcoincashjs-lib'
|
||||
|
||||
@@ -22,6 +23,8 @@ const WALLET_FILE = `${__dirname.toString()}/../../wallet.json`
|
||||
// const P2WDB_TOKEN_ID =
|
||||
// '38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0'
|
||||
|
||||
const advancedConfig = {}
|
||||
|
||||
class WalletAdapter {
|
||||
constructor (localConfig = {}) {
|
||||
// Encapsulate dependencies
|
||||
@@ -31,6 +34,10 @@ class WalletAdapter {
|
||||
this.bitcoinJs = bitcoinJs
|
||||
this.BchWallet = BchWallet
|
||||
this.bchWallet = {} // Will be replaced when initialized.
|
||||
// this.advancedConfig = localConfig.advancedConfig
|
||||
|
||||
// Bind the 'this' object
|
||||
this.moveTokens = this.moveTokens.bind(this)
|
||||
}
|
||||
|
||||
// Open the wallet file, or create one if the file doesn't exist.
|
||||
@@ -84,7 +91,8 @@ class WalletAdapter {
|
||||
throw new Error('Wallet data is not formatted correctly. Can not read mnemonic in wallet file!')
|
||||
}
|
||||
|
||||
const advancedConfig = {}
|
||||
// const advancedConfig = {}
|
||||
console.log(`Using FullStack.cash: ${this.config.useFullStackCash}`)
|
||||
if (this.config.useFullStackCash) {
|
||||
advancedConfig.interface = 'rest-api'
|
||||
advancedConfig.restURL = this.config.apiServer
|
||||
@@ -356,7 +364,7 @@ class WalletAdapter {
|
||||
}
|
||||
|
||||
// Update the UTXO store of the wallet.
|
||||
await this.bchWallet.getUtxos()
|
||||
await this.bchWallet.initialize()
|
||||
|
||||
// Get the token type of the token being moved.
|
||||
// Combine Fungible and NFT token UTXOs.
|
||||
@@ -508,6 +516,47 @@ class WalletAdapter {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// Given an Order database object, this function sends the tokens from the
|
||||
// HD index where they are stored, back to the root address of the wallet.
|
||||
async reclaimTokens (orderData) {
|
||||
try {
|
||||
// Get the key pair from the Order.
|
||||
const keyPair = await this.getKeyPair(orderData.hdIndex)
|
||||
console.log('keyPair: ', keyPair)
|
||||
|
||||
// Instantiate the wallet using the private key of the Order.
|
||||
// const tempWallet = new this.BchWallet(keyPair.wif, advancedConfig)
|
||||
// await tempWallet.initialize()
|
||||
|
||||
// Send the tokens from the Order to the root address.
|
||||
// const receiver = {
|
||||
// address: this.bchWallet.walletInfo.address,
|
||||
// tokenId: orderData.tokenId,
|
||||
// qty: orderData.numTokens
|
||||
// }
|
||||
// console.log('receiver: ', receiver)
|
||||
//
|
||||
// const txid = await tempWallet.sendTokens(receiver)
|
||||
|
||||
// Sweep the tokens to the root address.
|
||||
const sweeper = new BchTokenSweep(
|
||||
keyPair.wif,
|
||||
this.bchWallet.walletInfo.privateKey,
|
||||
this.bchWallet,
|
||||
550,
|
||||
this.bchWallet.walletInfo.cashAddress
|
||||
)
|
||||
await sweeper.populateObjectFromNetwork()
|
||||
const hex = await sweeper.sweepTo(this.bchWallet.walletInfo.cashAddress)
|
||||
const txid = await this.bchWallet.ar.sendTx(hex)
|
||||
|
||||
return txid
|
||||
} catch (err) {
|
||||
console.error('Error in wallet.js/reclaimTokens()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default WalletAdapter
|
||||
|
||||
@@ -35,6 +35,7 @@ class OrderRESTControllerLib {
|
||||
// console.log('body: ', ctx.request.body)
|
||||
|
||||
const orderObj = ctx.request.body.order
|
||||
console.log('orderObj: ', orderObj)
|
||||
|
||||
const hash = await _this.useCases.order.createOrder(orderObj)
|
||||
|
||||
@@ -47,6 +48,41 @@ class OrderRESTControllerLib {
|
||||
}
|
||||
}
|
||||
|
||||
// curl -X GET http://localhost:5700/order/list/all/0
|
||||
async listOrders (ctx) {
|
||||
try {
|
||||
let page = ctx.params.page
|
||||
if (!page) page = 0
|
||||
|
||||
const offers = await _this.useCases.order.listOrders(page)
|
||||
|
||||
ctx.body = offers
|
||||
} catch (err) {
|
||||
console.log('Error in listOrders REST API handler: ', err)
|
||||
_this.handleError(ctx, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Delete an existing order by returning the token to the root address of
|
||||
// the DEX wallet.
|
||||
async deleteOrder (ctx) {
|
||||
try {
|
||||
// console.log('body: ', ctx.request.body)
|
||||
|
||||
const p2wdbHash = ctx.request.body.p2wdbHash
|
||||
// console.log('p2wdbHash: ', p2wdbHash)
|
||||
|
||||
const txid = await _this.useCases.order.deleteOrder(p2wdbHash)
|
||||
|
||||
ctx.body = { txid }
|
||||
} catch (err) {
|
||||
// console.log(`err.message: ${err.message}`)
|
||||
// console.log('err: ', err)
|
||||
// ctx.throw(422, err.message)
|
||||
_this.handleError(ctx, err)
|
||||
}
|
||||
}
|
||||
|
||||
// DRY error handler
|
||||
handleError (ctx, err) {
|
||||
console.log('err', err)
|
||||
|
||||
@@ -50,6 +50,8 @@ class OrderRouter {
|
||||
|
||||
// Define the routes and attach the controller.
|
||||
this.router.post('/', _this.orderRESTController.createOrder)
|
||||
this.router.get('/list/all/:page', _this.orderRESTController.listOrders)
|
||||
this.router.post('/delete', _this.orderRESTController.deleteOrder)
|
||||
|
||||
// Attach the Controller routes to the Koa app.
|
||||
app.use(_this.router.routes())
|
||||
|
||||
@@ -33,6 +33,7 @@ class TimerControllers {
|
||||
startTimers () {
|
||||
setInterval(this.gcOrders, 60000 * 5)
|
||||
setInterval(this.gcOffers, 60000 * 5)
|
||||
setInterval(this.checkDupOffers, 60000 * 4.5)
|
||||
}
|
||||
|
||||
// Garbage Collect the Orders.
|
||||
@@ -54,6 +55,16 @@ class TimerControllers {
|
||||
console.log('Error in timer-controllers.js/gcOffers(): ', err)
|
||||
}
|
||||
}
|
||||
|
||||
// Remove duplicate Offers
|
||||
checkDupOffers () {
|
||||
try {
|
||||
_this.useCases.offer.removeDuplicateOffers()
|
||||
} catch (err) {
|
||||
// Do not throw an error. This is a top-level function.
|
||||
console.log('Error in timer-controllers.js/checkDupOffers(): ', err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default TimerControllers
|
||||
|
||||
@@ -67,9 +67,9 @@ class OfferEntity {
|
||||
if (!makerAddr || typeof makerAddr !== 'string') {
|
||||
throw new Error("Property 'makerAddr' must be a string.")
|
||||
}
|
||||
if (!ticker || typeof ticker !== 'string') {
|
||||
throw new Error("Property 'ticker' must be a string.")
|
||||
}
|
||||
// if (!ticker || typeof ticker !== 'string') {
|
||||
// throw new Error("Property 'ticker' must be a string.")
|
||||
// }
|
||||
if (!tokenType || typeof tokenType !== 'number') {
|
||||
throw new Error("Property 'tokenType' must be a number.")
|
||||
}
|
||||
|
||||
+10
-8
@@ -7,7 +7,10 @@
|
||||
The Order tracks the hdIndex address used to hold tokens or BCH for sale.
|
||||
*/
|
||||
class Order {
|
||||
validate (data) {
|
||||
// TODO: Create a fullValidate() function that validates a fully-hydrated
|
||||
// order model.
|
||||
|
||||
inputValidate (data) {
|
||||
const {
|
||||
messageType,
|
||||
messageClass,
|
||||
@@ -16,8 +19,7 @@ class Order {
|
||||
rateInBaseUnit,
|
||||
minUnitsToExchange,
|
||||
numTokens,
|
||||
makerAddr,
|
||||
ticker
|
||||
makerAddr
|
||||
} = data
|
||||
|
||||
// Input Validation
|
||||
@@ -45,9 +47,9 @@ class Order {
|
||||
if (!makerAddr || typeof makerAddr !== 'string') {
|
||||
throw new Error("Property 'makerAddr' must be a string.")
|
||||
}
|
||||
if (!ticker || typeof ticker !== 'string') {
|
||||
throw new Error("Property 'ticker' must be a string.")
|
||||
}
|
||||
// if (!ticker || typeof ticker !== 'string') {
|
||||
// throw new Error("Property 'ticker' must be a string.")
|
||||
// }
|
||||
|
||||
const offerData = {
|
||||
messageType,
|
||||
@@ -57,8 +59,8 @@ class Order {
|
||||
rateInBaseUnit,
|
||||
minUnitsToExchange,
|
||||
numTokens,
|
||||
makerAddr,
|
||||
ticker
|
||||
makerAddr
|
||||
// ticker
|
||||
}
|
||||
|
||||
return offerData
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
|
||||
// Global npm libraries
|
||||
import axios from 'axios'
|
||||
import RetryQueue from '@chris.troutner/retry-queue'
|
||||
|
||||
// Local libraries
|
||||
import OfferEntity from '../../entities/offer.js'
|
||||
|
||||
import config from '../../../config/index.js'
|
||||
|
||||
const DEFAULT_ENTRIES_PER_PAGE = 20
|
||||
@@ -44,9 +44,12 @@ class OfferUseCases {
|
||||
// Encapsulate dependencies
|
||||
this.config = config
|
||||
this.axios = axios
|
||||
|
||||
this.offerEntity = new OfferEntity()
|
||||
this.OfferModel = this.adapters.localdb.Offer
|
||||
this.retryQueue = new RetryQueue({ retryPeriod: 1000, attempts: 3 })
|
||||
|
||||
// Bind 'this' object to functions
|
||||
this.detectNsfw = this.detectNsfw.bind(this)
|
||||
}
|
||||
|
||||
// This method is called by the POST /offer REST API controller, which is
|
||||
@@ -74,14 +77,15 @@ class OfferUseCases {
|
||||
|
||||
// Verify that UTXO in offer is unspent. If it is spent, then ignore the
|
||||
// offer.
|
||||
const txid = offerObj.data.utxoTxid
|
||||
const vout = offerObj.data.utxoVout
|
||||
const utxoStatus = await this.adapters.bchjs.Blockchain.getTxOut(
|
||||
txid,
|
||||
vout
|
||||
)
|
||||
const utxo = {
|
||||
tx_hash: offerObj.data.utxoTxid,
|
||||
tx_pos: offerObj.data.utxoVout
|
||||
}
|
||||
// const utxoStatus = await this.adapters.wallet.bchWallet.utxoIsValid(utxo)
|
||||
const utxoStatus = await this.retryQueue.addToQueue(this.adapters.wallet.bchWallet.utxoIsValid, utxo)
|
||||
console.log('utxoStatus: ', utxoStatus)
|
||||
if (utxoStatus === null) return false
|
||||
// if (utxoStatus === null) return false
|
||||
if (!utxoStatus) return false
|
||||
|
||||
// A new offer gets a status of 'posted'
|
||||
offerObj.data.offerStatus = 'posted'
|
||||
@@ -91,18 +95,20 @@ class OfferUseCases {
|
||||
|
||||
// Get data about the token.
|
||||
const tokenId = offerEntity.tokenId
|
||||
const tokenData = await this.adapters.wallet.bchWallet.getTokenData(tokenId)
|
||||
// const tokenData = await this.adapters.wallet.bchWallet.getTokenData(tokenId)
|
||||
const tokenData = await this.retryQueue.addToQueue(this.adapters.wallet.bchWallet.getTokenData, tokenId)
|
||||
console.log(`tokenData: ${JSON.stringify(tokenData, null, 2)}`)
|
||||
|
||||
// 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, tokenData)
|
||||
const displayCategory = this.categorizeToken(offerEntity, tokenData)
|
||||
console.log('displayCategory: ', displayCategory)
|
||||
offerEntity.displayCategory = displayCategory
|
||||
|
||||
// Detect if user set the NSFW flag.
|
||||
let nsfw = false
|
||||
nsfw = await this.detectNsfw(tokenData)
|
||||
// nsfw = await this.detectNsfw(tokenData)
|
||||
nsfw = await this.retryQueue.addToQueue(this.detectNsfw, tokenData)
|
||||
offerEntity.nsfw = nsfw
|
||||
|
||||
// Add offer to the local database.
|
||||
@@ -132,7 +138,8 @@ class OfferUseCases {
|
||||
const cid = mutableCid.substring(7)
|
||||
|
||||
// Retrieve the mutable data from Filecoin/IPFS.
|
||||
const url = `https://${cid}.ipfs.w3s.link/data.json`
|
||||
// const url = `https://${cid}.ipfs.w3s.link/data.json`
|
||||
const url = `${this.config.ipfsGateway}${cid}/data.json`
|
||||
const result = await axios.get(url)
|
||||
const mutableData = result.data
|
||||
console.log(`mutableData: ${JSON.stringify(mutableData, null, 2)}`)
|
||||
@@ -165,7 +172,7 @@ class OfferUseCases {
|
||||
// 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, tokenData) {
|
||||
categorizeToken (offerData, tokenData) {
|
||||
try {
|
||||
// console.log(`categorizeToken(): ${JSON.stringify(offerData, null, 2)}`)
|
||||
|
||||
@@ -277,18 +284,11 @@ class OfferUseCases {
|
||||
}
|
||||
|
||||
// Verify that UTXO for sale is unspent. Abort if it's been spent.
|
||||
// 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) {
|
||||
console.log(`utxo txid: ${offerInfo.utxoTxid}, vout: ${offerInfo.utxoVout}`)
|
||||
@@ -306,7 +306,7 @@ class OfferUseCases {
|
||||
console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
|
||||
|
||||
// Calculate amount of sats to generate a counter offer.
|
||||
let satsToMove = offerInfo.numTokens * parseInt(offerInfo.rateInBaseUnit)
|
||||
let satsToMove = Math.ceil(offerInfo.numTokens * parseInt(offerInfo.rateInBaseUnit))
|
||||
if (isNaN(satsToMove)) {
|
||||
throw new Error('Could not calculate the amount of BCH to generate counter offer')
|
||||
}
|
||||
@@ -385,7 +385,7 @@ class OfferUseCases {
|
||||
// Sell Offer
|
||||
|
||||
// Calculate the sats needed
|
||||
const satsNeeded = offerEntity.numTokens * parseInt(offerEntity.rateInBaseUnit)
|
||||
const satsNeeded = Math.ceil(offerEntity.numTokens * parseInt(offerEntity.rateInBaseUnit))
|
||||
if (isNaN(satsNeeded)) {
|
||||
throw new Error('Could not calculate sats needed!')
|
||||
}
|
||||
@@ -467,7 +467,7 @@ class OfferUseCases {
|
||||
console.log(`txObj: ${JSON.stringify(txObj, null, 2)}`)
|
||||
|
||||
// Ensure the 3rd output (vout=2) contains the required amount of BCH.
|
||||
const satsToReceive = orderData.numTokens * parseInt(orderData.rateInBaseUnit)
|
||||
const satsToReceive = Math.ceil(orderData.numTokens * parseInt(orderData.rateInBaseUnit))
|
||||
if (isNaN(satsToReceive)) {
|
||||
throw new Error('Could not calculate the amount of BCH offered in the Counter Offer')
|
||||
}
|
||||
@@ -497,6 +497,45 @@ class OfferUseCases {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Write unit tests for this function, then add it to the Timer Controllers
|
||||
// Looks for duplicate offers and removes the duplicate.
|
||||
// Duplicates are checked for when an offer is created, and so should not
|
||||
// exist. But emperical testing shows that they do. This function is called
|
||||
// periodically by a timer, to clean up any duplicates that slipped through
|
||||
// the cracks.
|
||||
async removeDuplicateOffers () {
|
||||
try {
|
||||
const now = new Date()
|
||||
console.log(`Starting removeDuplicateOffers() at ${now.toLocaleString()}`)
|
||||
|
||||
let duplicateFound = false
|
||||
|
||||
// Get all Offers in the database.
|
||||
const offers = await this.OfferModel.find({})
|
||||
// console.log('offers: ', offers)
|
||||
|
||||
const offerZcids = []
|
||||
|
||||
for (let i = 0; i < offers.length; i++) {
|
||||
const thisOffer = offers[i]
|
||||
|
||||
if (offerZcids.includes(thisOffer.p2wdbHash)) {
|
||||
await thisOffer.remove()
|
||||
duplicateFound = true
|
||||
continue
|
||||
}
|
||||
|
||||
// Add the zcid to the array.
|
||||
offerZcids.push(thisOffer.p2wdbHash)
|
||||
}
|
||||
|
||||
return duplicateFound
|
||||
} catch (err) {
|
||||
console.error('Error in removeDuplicateOffers()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// This function is called by the garbage collection timer controller. It
|
||||
// checks the UTXO associated with each Offer in the database. If the UTXO
|
||||
// has been spent, the Offer is deleted from the database.
|
||||
@@ -515,12 +554,6 @@ 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
|
||||
// )
|
||||
|
||||
// Get the status of the UTXO associate with this Offer.
|
||||
const utxo = {
|
||||
tx_hash: thisOffer.utxoTxid,
|
||||
@@ -528,7 +561,8 @@ class OfferUseCases {
|
||||
}
|
||||
// console.log(`Checking this UTXO: ${JSON.stringify(utxo, null, 2)}`)
|
||||
|
||||
utxoStatus = await this.adapters.wallet.bchWallet.utxoIsValid(utxo)
|
||||
// utxoStatus = await this.adapters.wallet.bchWallet.utxoIsValid(utxo)
|
||||
utxoStatus = await this.retryQueue.addToQueue(this.adapters.wallet.bchWallet.utxoIsValid, utxo)
|
||||
// console.log('utxoStatus: ', utxoStatus)
|
||||
} catch (err) {
|
||||
// Handle corner case of bad-data in the Offer model.
|
||||
@@ -545,7 +579,8 @@ class OfferUseCases {
|
||||
}
|
||||
|
||||
// If the Offer UTXO is spent, delete the Offer model.
|
||||
if (!utxoStatus) {
|
||||
if (utxoStatus === false) {
|
||||
console.log('utxoStatus: ', utxoStatus)
|
||||
console.log(`Spent UTXO detected. Deleting this Offer: ${JSON.stringify(thisOffer, null, 2)}`)
|
||||
await thisOffer.remove()
|
||||
}
|
||||
|
||||
+71
-18
@@ -2,12 +2,16 @@
|
||||
Order use-case library.
|
||||
*/
|
||||
|
||||
// Global npm libraries
|
||||
import RetryQueue from '@chris.troutner/retry-queue'
|
||||
|
||||
// Local libraries
|
||||
import wlogger from '../adapters/wlogger.js'
|
||||
|
||||
import OrderEntity from '../entities/order.js'
|
||||
import config from '../../config/index.js'
|
||||
|
||||
const DEFAULT_ENTRIES_PER_PAGE = 20
|
||||
|
||||
class OrderLib {
|
||||
constructor (localConfig = {}) {
|
||||
// console.log('User localConfig: ', localConfig)
|
||||
@@ -23,6 +27,10 @@ class OrderLib {
|
||||
this.OrderModel = this.adapters.localdb.Order
|
||||
this.bch = this.adapters.bch
|
||||
this.config = config
|
||||
this.retryQueue = new RetryQueue({ retryPeriod: 1000, attempts: 3 })
|
||||
|
||||
// Bind subfunctions to the 'this' object.
|
||||
this.ensureFunds = this.ensureFunds.bind(this)
|
||||
}
|
||||
|
||||
// Create a new order model and add it to the Mongo database.
|
||||
@@ -37,33 +45,37 @@ class OrderLib {
|
||||
entryObj.makerAddr = this.adapters.wallet.bchWallet.walletInfo.cashAddress
|
||||
console.log('entryObj.makerAddr: ', entryObj.makerAddr)
|
||||
|
||||
// Get Ticker for token ID.
|
||||
// TODO: Move this below the orderEntity.validate() call.
|
||||
const tokenData = await this.adapters.wallet.bchWallet.getTxData([entryObj.tokenId])
|
||||
// console.log(`tokenData: ${JSON.stringify(tokenData, null, 2)}`)
|
||||
entryObj.ticker = tokenData[0].tokenTicker
|
||||
|
||||
// Input Validation
|
||||
// TODO: Remove ticker from validation.
|
||||
// TODO: Rename validate() to inputValidate(). Create fullValidate() that
|
||||
// validates a fully hydrated Order entity.
|
||||
const orderEntity = this.orderEntity.validate(entryObj)
|
||||
const orderEntity = this.orderEntity.inputValidate(entryObj)
|
||||
console.log('orderEntity: ', orderEntity)
|
||||
|
||||
// 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, {})
|
||||
|
||||
// Ensure sufficient tokens exist to create the order.
|
||||
await this.ensureFunds(orderEntity)
|
||||
// await this.ensureFunds(orderEntity)
|
||||
await this.retryQueue.addToQueue(this.ensureFunds, orderEntity)
|
||||
|
||||
// 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])
|
||||
// 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
|
||||
}
|
||||
const utxoInfo = await this.adapters.wallet.moveTokens(moveObj)
|
||||
// const utxoInfo = await this.adapters.wallet.moveTokens(moveObj)
|
||||
const utxoInfo = await this.retryQueue.addToQueue(this.adapters.wallet.moveTokens, moveObj)
|
||||
console.log('utxoInfo: ', utxoInfo)
|
||||
|
||||
// Update the UTXO store for the wallet.
|
||||
await this.adapters.wallet.bchWallet.bchjs.Util.sleep(3000)
|
||||
await this.adapters.wallet.bchWallet.getUtxos()
|
||||
// await this.adapters.wallet.bchWallet.getUtxos()
|
||||
await this.retryQueue.addToQueue(this.adapters.wallet.bchWallet.initialize, {})
|
||||
|
||||
// Update the order with the new UTXO information.
|
||||
orderEntity.utxoTxid = utxoInfo.txid
|
||||
@@ -79,7 +91,8 @@ class OrderLib {
|
||||
data: orderEntity,
|
||||
appId: this.config.p2wdbAppId
|
||||
}
|
||||
const hash = await this.adapters.p2wdb.write(p2wdbObj)
|
||||
// const hash = await this.adapters.p2wdb.write(p2wdbObj)
|
||||
const hash = await this.retryQueue.addToQueue(this.adapters.p2wdb.write, p2wdbObj)
|
||||
// console.log('hash: ', hash)
|
||||
|
||||
// Create a MongoDB model to hold the Order
|
||||
@@ -108,6 +121,7 @@ class OrderLib {
|
||||
|
||||
// Update the wallet balance
|
||||
await this.adapters.wallet.bchWallet.getUtxos()
|
||||
// console.log(`wallet UTXOs: ${JSON.stringify(this.adapters.wallet.bchWallet.utxos.utxoStore, null, 2)}`)
|
||||
|
||||
// Ensure the app wallet has enough funds to write to the P2WDB.
|
||||
const wif = this.adapters.wallet.bchWallet.walletInfo.privateKey
|
||||
@@ -116,7 +130,7 @@ class OrderLib {
|
||||
|
||||
// Get UTXOs.
|
||||
const utxos = this.adapters.wallet.bchWallet.utxos.utxoStore
|
||||
console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
|
||||
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
|
||||
|
||||
if (orderEntity.buyOrSell.includes('sell')) {
|
||||
// Sell Order
|
||||
@@ -209,7 +223,8 @@ class OrderLib {
|
||||
tx_hash: thisOrder.utxoTxid,
|
||||
tx_pos: thisOrder.utxoVout
|
||||
}
|
||||
utxoStatus = await this.adapters.wallet.bchWallet.utxoIsValid(utxo)
|
||||
// utxoStatus = await this.adapters.wallet.bchWallet.utxoIsValid(utxo)
|
||||
utxoStatus = await this.retryQueue.addToQueue(this.adapters.wallet.bchWallet.utxoIsValid, utxo)
|
||||
// console.log('utxoStatus: ', utxoStatus)
|
||||
} catch (err) {
|
||||
// Handle corner case of bad-data in the Order model.
|
||||
@@ -226,7 +241,8 @@ class OrderLib {
|
||||
}
|
||||
|
||||
// If the Order UTXO is spent, delete the Order model.
|
||||
if (!utxoStatus) {
|
||||
if (utxoStatus === false) {
|
||||
console.log('utxoStatus: ', utxoStatus)
|
||||
console.log(`Spent UTXO detected. Deleting this Order: ${JSON.stringify(thisOrder, null, 2)}`)
|
||||
await thisOrder.remove()
|
||||
}
|
||||
@@ -236,6 +252,43 @@ class OrderLib {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
async listOrders (page = 0) {
|
||||
try {
|
||||
const data = await this.OrderModel.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/order/listOrders()')
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// Delete an Order model by sending the token back to the root address. This
|
||||
// will allow the garbage collectors to delete the Order and the Offer.
|
||||
async deleteOrder (p2wdbHash) {
|
||||
try {
|
||||
console.log('p2wdbHash: ', p2wdbHash)
|
||||
|
||||
// Find the order by the given hash.
|
||||
const order = await this.findOrderByHash(p2wdbHash)
|
||||
console.log('order: ', order)
|
||||
|
||||
// Reclaim the tokens
|
||||
const txid = await this.adapters.wallet.reclaimTokens(order)
|
||||
|
||||
return txid
|
||||
} catch (err) {
|
||||
console.error('Error in use-cases/order/deleteOrder()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default OrderLib
|
||||
|
||||
@@ -8,7 +8,7 @@ import { assert } from 'chai'
|
||||
import sinon from 'sinon'
|
||||
|
||||
// const BCHJS = require('@psf/bch-js')
|
||||
import BchWallet from 'minimal-slp-wallet/index.js'
|
||||
import BchWallet from 'minimal-slp-wallet'
|
||||
|
||||
// Local libraries.
|
||||
import P2wdbAdapter from '../../../src/adapters/p2wdb-adapter.js'
|
||||
|
||||
@@ -21,10 +21,10 @@ describe('#Order-Entity', () => {
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('#validate', () => {
|
||||
describe('#inputValidate', () => {
|
||||
it('should throw an error if data is not provided', () => {
|
||||
try {
|
||||
uut.validate()
|
||||
uut.inputValidate()
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.include(err.message, 'Cannot destructure property')
|
||||
@@ -34,7 +34,7 @@ describe('#Order-Entity', () => {
|
||||
it('should throw an error if messageType is not included', () => {
|
||||
try {
|
||||
const data = {}
|
||||
uut.validate(data)
|
||||
uut.inputValidate(data)
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.include(
|
||||
@@ -47,7 +47,7 @@ describe('#Order-Entity', () => {
|
||||
it('should throw an error if messageClass is not included', () => {
|
||||
try {
|
||||
const data = { messageType: 1 }
|
||||
uut.validate(data)
|
||||
uut.inputValidate(data)
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.include(
|
||||
@@ -60,7 +60,7 @@ describe('#Order-Entity', () => {
|
||||
it('should throw an error if tokenId is not included', () => {
|
||||
try {
|
||||
const data = { messageType: 1, messageClass: 1 }
|
||||
uut.validate(data)
|
||||
uut.inputValidate(data)
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.include(err.message, "Property 'tokenId' must be a string.")
|
||||
@@ -70,7 +70,7 @@ describe('#Order-Entity', () => {
|
||||
it('should throw an error if buyOrSell is not included', () => {
|
||||
try {
|
||||
const data = { messageType: 1, messageClass: 1, tokenId: 'fakeId' }
|
||||
uut.validate(data)
|
||||
uut.inputValidate(data)
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.include(err.message, "Property 'buyOrSell' must be a string.")
|
||||
@@ -85,7 +85,7 @@ describe('#Order-Entity', () => {
|
||||
tokenId: 'fakeId',
|
||||
buyOrSell: 'buy'
|
||||
}
|
||||
uut.validate(data)
|
||||
uut.inputValidate(data)
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.include(
|
||||
@@ -104,7 +104,7 @@ describe('#Order-Entity', () => {
|
||||
buyOrSell: 'buy',
|
||||
rateInBaseUnit: 1000
|
||||
}
|
||||
uut.validate(data)
|
||||
uut.inputValidate(data)
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.include(
|
||||
@@ -124,7 +124,7 @@ describe('#Order-Entity', () => {
|
||||
rateInBaseUnit: 1000,
|
||||
minUnitsToExchange: 350
|
||||
}
|
||||
uut.validate(data)
|
||||
uut.inputValidate(data)
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.include(err.message, "Property 'numTokens' must be a number.")
|
||||
|
||||
@@ -35,6 +35,9 @@ class MockBchWallet {
|
||||
}]
|
||||
}
|
||||
this.getTokenData = async () => {}
|
||||
this.initialize = async () => {}
|
||||
this.utxoIsValid = async () => {}
|
||||
this.optimize = async () => {}
|
||||
|
||||
// Environment variable is used by wallet-balance.unit.js to force an error.
|
||||
if (process.env.NO_UTXO) {
|
||||
|
||||
@@ -74,7 +74,7 @@ describe('#offer-use-case', () => {
|
||||
}
|
||||
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut.adapters.bchjs.Blockchain, 'getTxOut').resolves(null)
|
||||
sandbox.stub(uut.adapters.wallet.bchWallet, 'utxoIsValid').resolves(false)
|
||||
sandbox.stub(uut.adapters.wallet.bchWallet, 'getTokenData').resolves({})
|
||||
sandbox.stub(uut, 'categorizeToken').resolves('nft')
|
||||
sandbox.stub(uut, 'detectNsfw').resolves(false)
|
||||
@@ -111,18 +111,7 @@ describe('#offer-use-case', () => {
|
||||
}
|
||||
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut.adapters.bchjs.Blockchain, 'getTxOut').resolves({
|
||||
bestblock:
|
||||
'000000000000000000d2060b83f90f8187b92fcccb4a42aaa19ce5a305fe0ae3',
|
||||
confirmations: 0,
|
||||
value: 0,
|
||||
scriptPubKey: {
|
||||
asm: 'OP_RETURN 5262419 1 1145980243 38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0 00000000001e8480 0000000009a7ec80',
|
||||
hex: '6a04534c500001010453454e442038e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b00800000000001e8480080000000009a7ec80',
|
||||
type: 'nulldata'
|
||||
},
|
||||
coinbase: false
|
||||
})
|
||||
sandbox.stub(uut.adapters.wallet.bchWallet, 'utxoIsValid').resolves(true)
|
||||
sandbox.stub(uut, 'categorizeToken').resolves('fungible')
|
||||
sandbox.stub(uut.adapters.wallet.bchWallet, 'getTokenData').resolves({})
|
||||
sandbox.stub(uut, 'detectNsfw').resolves(false)
|
||||
@@ -171,4 +160,35 @@ describe('#offer-use-case', () => {
|
||||
assert.equal(result, 'fungible')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#removeDuplicateOffers', () => {
|
||||
it('should remove duplicate entries and return true', async () => {
|
||||
// Mock dependencies and force desired code path.
|
||||
sandbox.stub(uut.OfferModel, 'find').resolves([
|
||||
{ p2wdbHash: 'a', remove: async () => {} },
|
||||
{ p2wdbHash: 'a', remove: async () => {} },
|
||||
{ p2wdbHash: 'b', remove: async () => {} }
|
||||
])
|
||||
// sandbox.stub(uut.OfferModel, 'remove').resolves()
|
||||
|
||||
const result = await uut.removeDuplicateOffers()
|
||||
console.log('result: ', result)
|
||||
|
||||
assert.equal(result, true)
|
||||
})
|
||||
|
||||
it('should return false if there are no duplicate entries', async () => {
|
||||
// Mock dependencies and force desired code path.
|
||||
sandbox.stub(uut.OfferModel, 'find').resolves([
|
||||
{ p2wdbHash: 'a', remove: async () => {} },
|
||||
{ p2wdbHash: 'b', remove: async () => {} }
|
||||
])
|
||||
// sandbox.stub(uut.OfferModel, 'remove').resolves()
|
||||
|
||||
const result = await uut.removeDuplicateOffers()
|
||||
console.log('result: ', result)
|
||||
|
||||
assert.equal(result, false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -103,17 +103,16 @@ describe('#order-use-case', () => {
|
||||
buyOrSell: 'sell',
|
||||
rateInBaseUnit: 1000,
|
||||
minUnitsToExchange: 1250,
|
||||
numTokens: 1
|
||||
numTokens: 1,
|
||||
ticker: 'TEST'
|
||||
}
|
||||
|
||||
// Mock dependencies
|
||||
// sandbox.stub(uut.adapters.wallet, 'burnPsf').resolves('fakeTxid')
|
||||
// sandbox.stub(uut.adapters.wallet.bchWallet, 'getTxData').resolves({ tokenTicker: 'TROUT' })
|
||||
sandbox.stub(uut.orderEntity, 'validate').returns(entryObj)
|
||||
// Mock dependencies and force expected code path
|
||||
sandbox.stub(uut.orderEntity, 'inputValidate').returns(entryObj)
|
||||
sandbox.stub(uut, 'ensureFunds').resolves()
|
||||
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.bchWallet, 'getUtxos').resolves()
|
||||
sandbox.stub(uut.adapters.wallet.bchWallet, 'initialize').resolves()
|
||||
sandbox.stub(uut.adapters.p2wdb, 'write').resolves('fakeHash')
|
||||
|
||||
const result = await uut.createOrder(entryObj)
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
This script has not been customized for offers yet.
|
||||
*/
|
||||
|
||||
const mongoose = require('mongoose')
|
||||
import mongoose from 'mongoose'
|
||||
|
||||
// Force test environment
|
||||
// make sure environment variable is set before this file gets called.
|
||||
// see test script in package.json.
|
||||
// process.env.KOA_ENV = 'test'
|
||||
const config = require('../../config')
|
||||
import config from '../../config'
|
||||
|
||||
const User = require('../../src/models/users')
|
||||
import User from '../../src/models/users'
|
||||
|
||||
async function deleteUsers () {
|
||||
// Connect to the Mongo Database.
|
||||
|
||||
@@ -2,11 +2,10 @@
|
||||
Get all Offers in the database.
|
||||
*/
|
||||
|
||||
const mongoose = require('mongoose')
|
||||
import mongoose from 'mongoose'
|
||||
|
||||
const config = require('../../config')
|
||||
|
||||
const Offer = require('../../src/adapters/localdb/models/offer')
|
||||
import config from '../../config/index.js'
|
||||
import Offer from '../../src/adapters/localdb/models/offer.js'
|
||||
|
||||
async function getOffers () {
|
||||
// Connect to the Mongo Database.
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
const mongoose = require('mongoose')
|
||||
import mongoose from 'mongoose'
|
||||
|
||||
// Force test environment
|
||||
// make sure environment variable is set before this file gets called.
|
||||
// see test script in package.json.
|
||||
// process.env.KOA_ENV = 'test'
|
||||
const config = require('../../config')
|
||||
import config from '../../config'
|
||||
|
||||
const User = require('../../src/models/users')
|
||||
import User from '../../src/models/users'
|
||||
|
||||
async function deleteUsers () {
|
||||
// Connect to the Mongo Database.
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
const mongoose = require('mongoose')
|
||||
|
||||
const config = require('../../config')
|
||||
|
||||
const Order = require('../../src/adapters/localdb/models/order')
|
||||
import mongoose from 'mongoose'
|
||||
import config from '../../config/index.js'
|
||||
import Order from '../../src/adapters/localdb/models/order.js'
|
||||
|
||||
async function getOrder () {
|
||||
// Connect to the Mongo Database.
|
||||
|
||||
@@ -7,11 +7,12 @@
|
||||
*/
|
||||
|
||||
// Public npm libraries
|
||||
const BCHJS = require('@psf/bch-js')
|
||||
const BchTokenSweep = require('bch-token-sweep/index')
|
||||
import BCHJS from '@psf/bch-js'
|
||||
|
||||
import BchTokenSweep from 'bch-token-sweep/index'
|
||||
|
||||
// Local libraries
|
||||
const WalletAdapter = require('../../src/adapters/wallet')
|
||||
import WalletAdapter from '../../src/adapters/wallet'
|
||||
|
||||
// Constants
|
||||
const EMTPY_ADDR_CUTOFF = 15
|
||||
|
||||
Reference in New Issue
Block a user