mirror of
https://github.com/Permissionless-Software-Foundation/bch-dex.git
synced 2026-09-21 16:52:00 -07:00
Working on take-offer logic
This commit is contained in:
+13
-57
@@ -178,63 +178,19 @@ class WalletAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
// Burn enough PSF to generate a valide proof-of-burn for writing to the P2WDB.
|
||||
// async burnPsf () {
|
||||
// try {
|
||||
// // TODO: Throw error if this.bchWallet has not been instantiated.
|
||||
//
|
||||
// // console.log('walletData: ', walletData)
|
||||
// // console.log(
|
||||
// // `walletData.utxos.utxoStore.slpUtxos: ${JSON.stringify(
|
||||
// // walletData.utxos.utxoStore.slpUtxos,
|
||||
// // null,
|
||||
// // 2,
|
||||
// // )}`,
|
||||
// // )
|
||||
//
|
||||
// // Get token UTXOs held by the wallet.
|
||||
// const tokenUtxos = this.bchWallet.utxos.utxoStore.slpUtxos.type1.tokens
|
||||
// // console.log(`tokenUtxos: ${JSON.stringify(tokenUtxos, null, 2)}`)
|
||||
//
|
||||
// // Find a token UTXO that contains PSF with a quantity higher than needed
|
||||
// // to generate a proof-of-burn.
|
||||
// let tokenUtxo = {}
|
||||
// for (let i = 0; i < tokenUtxos.length; i++) {
|
||||
// const thisUtxo = tokenUtxos[i]
|
||||
//
|
||||
// // If token ID matches.
|
||||
// if (thisUtxo.tokenId === P2WDB_TOKEN_ID) {
|
||||
// if (parseFloat(thisUtxo.qtyStr) >= PROOF_OF_BURN_QTY) {
|
||||
// tokenUtxo = thisUtxo
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (tokenUtxo.tokenId !== P2WDB_TOKEN_ID) {
|
||||
// throw new Error(
|
||||
// `Token UTXO of with ID of ${P2WDB_TOKEN_ID} and quantity greater than ${PROOF_OF_BURN_QTY} could not be found in wallet.`
|
||||
// )
|
||||
// }
|
||||
// // console.log(`tokenUtxo: ${JSON.stringify(tokenUtxo, null, 2)}`)
|
||||
//
|
||||
// const result = await this.bchWallet.burnTokens(
|
||||
// PROOF_OF_BURN_QTY,
|
||||
// P2WDB_TOKEN_ID
|
||||
// )
|
||||
// // console.log('walletData.burnTokens() result: ', result)
|
||||
//
|
||||
// return result
|
||||
//
|
||||
// // return {
|
||||
// // success: true,
|
||||
// // txid: 'fakeTxid',
|
||||
// // }
|
||||
// } catch (err) {
|
||||
// console.error('Error in burnPsf(): ', err)
|
||||
// throw err
|
||||
// }
|
||||
// }
|
||||
// Generate a partial transcation to *take* a 'sell' order.
|
||||
async generatePartialTx (orderInfo) {
|
||||
// try {
|
||||
// // const bchjs = this.bchWallet.bchjs
|
||||
//
|
||||
// return true
|
||||
// } catch (err) {
|
||||
// console.error('Error in wallet.js/generatePartialTx()')
|
||||
// throw err
|
||||
// }
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = WalletAdapter
|
||||
|
||||
@@ -61,6 +61,27 @@ class OrderRESTControllerLib {
|
||||
}
|
||||
}
|
||||
|
||||
// Currently only supports 'sell' orders, and will only buy the 'numTokens'
|
||||
// listed in the order.
|
||||
async takeOrder (ctx) {
|
||||
try {
|
||||
console.log('body: ', ctx.request.body)
|
||||
|
||||
const orderCid = ctx.request.body.orderCid
|
||||
|
||||
// Find the Order.
|
||||
// const orderEntity = await _this.useCases.order.findOrder(orderId)
|
||||
|
||||
// 'Take' the Order.
|
||||
const hash = await _this.useCases.order.takeOrder(orderCid)
|
||||
|
||||
ctx.body = { hash }
|
||||
} catch (err) {
|
||||
console.log('Error in takeOrder REST API handler.')
|
||||
_this.handleError(ctx, err)
|
||||
}
|
||||
}
|
||||
|
||||
// DRY error handler
|
||||
handleError (ctx, err) {
|
||||
console.log('err', err.message)
|
||||
|
||||
@@ -51,6 +51,7 @@ class OrderRouter {
|
||||
// Define the routes and attach the controller.
|
||||
this.router.post('/', _this.orderRESTController.createOrder)
|
||||
this.router.get('/list', _this.orderRESTController.listOrders)
|
||||
this.router.post('/take', _this.orderRESTController.takeOrder)
|
||||
|
||||
// Attach the Controller routes to the Koa app.
|
||||
app.use(_this.router.routes())
|
||||
|
||||
@@ -90,7 +90,7 @@ class OfferLib {
|
||||
|
||||
const utxoInfo = {
|
||||
txid,
|
||||
vout: 0,
|
||||
vout: 1,
|
||||
hdIndex: keyPair.hdIndex
|
||||
}
|
||||
|
||||
|
||||
@@ -68,6 +68,112 @@ class OrderUseCases {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// Generate phase 2 of 3 - take the other side of an Order.
|
||||
// Based on this example:
|
||||
// https://github.com/Permissionless-Software-Foundation/bch-js-examples/blob/master/bch/applications/collaborate/sell-slp/e2e-exchange/step2-purchase-tx.js
|
||||
async takeOrder (orderCid) {
|
||||
try {
|
||||
console.log('orderCid: ', orderCid)
|
||||
|
||||
// Get the Order information
|
||||
const orderInfo = await this.findOrderByHash(orderCid)
|
||||
console.log(`orderInfo: ${JSON.stringify(orderInfo, null, 2)}`)
|
||||
|
||||
// Ensure the order is in a 'posted' state and not already 'taken'
|
||||
if (orderInfo.orderStatus && orderInfo.orderStatus !== 'posted') {
|
||||
throw new Error('order already taken')
|
||||
}
|
||||
|
||||
// Verify that UTXO for sale is unspent. Abort if it's been spent.
|
||||
const txid = orderInfo.utxoTxid
|
||||
const vout = orderInfo.utxoVout
|
||||
const utxoStatus = await this.adapters.bchjs.Blockchain.getTxOut(
|
||||
txid,
|
||||
vout
|
||||
)
|
||||
console.log('utxoStatus: ', utxoStatus)
|
||||
if (utxoStatus === null) {
|
||||
console.log(`utxo txid: ${txid}, vout: ${vout}`)
|
||||
throw new Error('UTXO does not exist. Aborting.')
|
||||
}
|
||||
|
||||
// Ensure the app has enough funds to complete the trade.
|
||||
await this.ensureFunds(orderInfo)
|
||||
|
||||
// Get UTXOs.
|
||||
const utxos = this.adapters.wallet.bchWallet.utxos.utxoStore
|
||||
console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
|
||||
|
||||
// Create a partially signed transaction.
|
||||
// https://github.com/Permissionless-Software-Foundation/bch-js-examples/blob/master/bch/applications/collaborate/sell-slp/e2e-exchange/step2-purchase-tx.js#L59
|
||||
// const partialTxHex = await this.adapaters.wallet.generatePartialTx(orderInfo)
|
||||
|
||||
// return partialTxHex
|
||||
|
||||
return true
|
||||
} catch (err) {
|
||||
console.error('Error in use-cases/order/takeOrder()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that the wallet has enough BCH and tokens to complete the requested
|
||||
// trade. Will return true if it does. Will throw an error if it doesn't.
|
||||
async ensureFunds (orderEntity) {
|
||||
try {
|
||||
// console.log('this.adapters.wallet: ', this.adapters.wallet.bchWallet)
|
||||
// console.log(`walletInfo: ${JSON.stringify(this.adapters.wallet.bchWallet.walletInfo, null, 2)}`)
|
||||
|
||||
// Ensure the app wallet has enough funds to write to the P2WDB.
|
||||
const wif = this.adapters.wallet.bchWallet.walletInfo.privateKey
|
||||
const canWriteToP2WDB = await this.adapters.p2wdb.checkForSufficientFunds(wif)
|
||||
if (!canWriteToP2WDB) throw new Error('App wallet does not have funds for writing to the P2WDB.')
|
||||
|
||||
if (orderEntity.buyOrSell.includes('sell')) {
|
||||
// Sell Offer
|
||||
|
||||
// Ensure the app wallet controlls enough BCH to pay for the tokens.
|
||||
const satsNeeded = orderEntity.numTokens * parseInt(orderEntity.rateInSats)
|
||||
const balance = await this.adapters.wallet.bchWallet.getBalance()
|
||||
console.log(`wallet balance: ${balance}, sats needed: ${satsNeeded}`)
|
||||
const SATS_MARGIN = 5000
|
||||
if (satsNeeded + SATS_MARGIN > balance) { throw new Error('App wallet does not control enough BCH to purchase the tokens.') }
|
||||
|
||||
//
|
||||
} else {
|
||||
// Buy Offer
|
||||
throw new Error('Buy orders are not supported yet.')
|
||||
}
|
||||
|
||||
return true
|
||||
} catch (err) {
|
||||
console.error('Error in ensureFunds()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
async findOrderByHash (p2wdbHash) {
|
||||
try {
|
||||
if (typeof p2wdbHash !== 'string' || !p2wdbHash) {
|
||||
throw new Error('p2wdbHash must be a string')
|
||||
}
|
||||
|
||||
const order = await this.OrderModel.findOne({ p2wdbHash })
|
||||
|
||||
if (!order) {
|
||||
throw new Error('order not found')
|
||||
}
|
||||
|
||||
const orderObject = order.toObject()
|
||||
// return this.orderEntity.validateFromModel(orderObject)
|
||||
|
||||
return orderObject
|
||||
} catch (err) {
|
||||
console.error('Error in findOrder(): ', err)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = OrderUseCases
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
Part 2 of 3: Take an order
|
||||
*/
|
||||
|
||||
const axios = require('axios')
|
||||
|
||||
const LOCALHOST = 'http://localhost:5700'
|
||||
|
||||
async function start () {
|
||||
try {
|
||||
const options = {
|
||||
method: 'post',
|
||||
url: `${LOCALHOST}/order/take`,
|
||||
data: {
|
||||
orderCid: 'zdpuAkp98gTuivaNzGP31jTQi3ADXrFA6uANrceQcrQkTXy2j'
|
||||
}
|
||||
}
|
||||
|
||||
const result = await axios(options)
|
||||
console.log('result.data: ', result.data)
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
}
|
||||
start()
|
||||
Reference in New Issue
Block a user