fix(moveTokens): Moved from order use-case to wallet adapter

This commit is contained in:
Chris Troutner
2022-03-21 14:03:40 -07:00
parent 8ed1dbcffe
commit 810d386dbc
8 changed files with 177 additions and 104 deletions
+30
View File
@@ -303,6 +303,36 @@ class WalletAdapter {
// return true
}
// Move tokens to an address controlled by the HD wallet, to generate a
// segregated UTXO.
async moveTokens (inObj = {}) {
try {
const { tokenId, qty } = inObj
const keyPair = await this.getKeyPair()
console.log('keyPair: ', keyPair)
const receiver = {
address: keyPair.cashAddress,
tokenId,
qty
}
const txid = await this.bchWallet.sendTokens(receiver, 3)
const utxoInfo = {
txid,
vout: 1,
hdIndex: keyPair.hdIndex
}
return utxoInfo
} catch (err) {
console.error('Error in wallet.js/moveTokens()')
throw err
}
}
}
module.exports = WalletAdapter
+33 -23
View File
@@ -119,28 +119,30 @@ class OfferUseCases {
// 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.adapters.wallet.generatePartialTx(offerInfo)
// return partialTxHex
// const partialTxHex = await this.adapters.wallet.generatePartialTx(offerInfo)
// // return partialTxHex
//
// // Create valid Offer object
// const takenOfferInfo = Object.assign({}, offerInfo)
// takenOfferInfo.patialTxHex = partialTxHex
// delete takenOfferInfo.p2wdbHash
// delete takenOfferInfo._id
// takenOfferInfo.offerHash = offerInfo.p2wdbHash
//
// // Write offer info to the P2WDB
// // TODO: This will trigger the webhook. Find some way of triggering the
// // webhook on new offers, but not on counteroffers
// const p2wdbObj = {
// wif: this.adapters.wallet.bchWallet.walletInfo.privateKey,
// data: takenOfferInfo,
// appId: this.config.p2wdbAppId
// }
// const hash = await this.adapters.p2wdb.write(p2wdbObj)
//
// // Return the P2WDB CID
// return hash
// Create valid Offer object
const takenOfferInfo = Object.assign({}, offerInfo)
takenOfferInfo.patialTxHex = partialTxHex
delete takenOfferInfo.p2wdbHash
delete takenOfferInfo._id
takenOfferInfo.offerHash = offerInfo.p2wdbHash
// Write offer info to the P2WDB
// TODO: This will trigger the webhook. Find some way of triggering the
// webhook on new offers, but not on counteroffers
const p2wdbObj = {
wif: this.adapters.wallet.bchWallet.walletInfo.privateKey,
data: takenOfferInfo,
appId: this.config.p2wdbAppId
}
const hash = await this.adapters.p2wdb.write(p2wdbObj)
// Return the P2WDB CID
return hash
return 'fake-hash'
} catch (err) {
console.error('Error in use-cases/offer/takeOffer(): ', err)
throw err
@@ -155,6 +157,7 @@ class OfferUseCases {
// console.log(`walletInfo: ${JSON.stringify(this.adapters.wallet.bchWallet.walletInfo, null, 2)}`)
await this.adapters.wallet.bchWallet.walletInfoPromise
// console.log(`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
@@ -164,12 +167,19 @@ class OfferUseCases {
if (offerEntity.buyOrSell.includes('sell')) {
// Sell Offer
// Calculate the sats needed
const satsNeeded = offerEntity.numTokens * parseInt(offerEntity.rateInBaseUnit)
if (isNaN(satsNeeded)) {
throw new Error('Could not calculate sats needed!')
}
// Ensure the app wallet controlls enough BCH to pay for the tokens.
const satsNeeded = offerEntity.numTokens * parseInt(offerEntity.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.') }
if (satsNeeded + SATS_MARGIN > balance) {
throw new Error('App wallet does not control enough BCH to purchase the tokens.')
}
//
} else {
+30 -26
View File
@@ -37,7 +37,11 @@ class OrderLib {
await this.ensureFunds(orderEntity)
// Move the tokens to holding address.
const utxoInfo = await this.moveTokens(orderEntity)
const moveObj = {
tokenId: orderEntity.tokenId,
qty: orderEntity.numTokens
}
const utxoInfo = await this.adapters.wallet.moveTokens(moveObj)
console.log('utxoInfo: ', utxoInfo)
// Update the UTXO store for the wallet.
@@ -76,31 +80,31 @@ class OrderLib {
// Move the tokens indicated in the order to a temporary holding address.
// This will generate the UTXO used in the Signal message. This function
// moves the funds and returns the UTXO information.
async moveTokens (orderEntity) {
try {
const keyPair = await this.adapters.wallet.getKeyPair()
console.log('keyPair: ', keyPair)
const receiver = {
address: keyPair.cashAddress,
tokenId: orderEntity.tokenId,
qty: orderEntity.numTokens
}
const txid = await this.adapters.wallet.bchWallet.sendTokens(receiver, 3)
const utxoInfo = {
txid,
vout: 1,
hdIndex: keyPair.hdIndex
}
return utxoInfo
} catch (err) {
console.error('Error in moveTokens(): ', err)
throw err
}
}
// async moveTokens (orderEntity) {
// try {
// const keyPair = await this.adapters.wallet.getKeyPair()
// console.log('keyPair: ', keyPair)
//
// const receiver = {
// address: keyPair.cashAddress,
// tokenId: orderEntity.tokenId,
// qty: orderEntity.numTokens
// }
//
// const txid = await this.adapters.wallet.bchWallet.sendTokens(receiver, 3)
//
// const utxoInfo = {
// txid,
// vout: 1,
// hdIndex: keyPair.hdIndex
// }
//
// return utxoInfo
// } catch (err) {
// console.error('Error in moveTokens(): ', err)
// throw err
// }
// }
// Ensure that the wallet has enough BCH and tokens to complete the requested
// trade.
@@ -38,4 +38,27 @@ describe('#wallet', () => {
assert.equal(walletInstance.ar.interface, 'rest-api')
})
})
describe('#moveTokens', () => {
it('should move tokens to a new address in the HD wallet', async () => {
const walletData = await uut.openWallet()
// Force usage of FullStack.cash
uut.config.useFullStackCash = true
await uut.instanceWallet(walletData)
const inObj = {
tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
qty: 1
}
const result = await uut.moveTokens(inObj)
// console.log('result: ', result)
assert.property(result, 'txid')
assert.property(result, 'vout')
assert.property(result, 'hdIndex')
})
})
})
@@ -24,27 +24,6 @@ describe('#offer-use-case.js', () => {
})
describe('#ensureFunds', () => {
it('should throw error if wallet does not have enough funds', async () => {
try {
const offerEntity = {
lokadId: 'SWP',
messageType: 1,
messageClass: 1,
tokenId: 'token-id',
buyOrSell: 'sell',
rateInSats: 1000,
minSatsToExchange: 1250,
numTokens: 1
}
await uut.ensureFunds(offerEntity)
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'App wallet does not have enough tokens to satisfy the SELL offer.')
}
})
it('should return true if wallet has enough funds', async () => {
const offerEntity = {
lokadId: 'SWP',
@@ -52,8 +31,8 @@ describe('#offer-use-case.js', () => {
messageClass: 1,
tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
buyOrSell: 'sell',
rateInSats: 1000,
minSatsToExchange: 1250,
rateInBaseUnit: 1000,
minUnitsToExchange: 1250,
numTokens: 1
}
+26
View File
@@ -253,6 +253,32 @@ describe('#wallet', () => {
}
})
})
describe('#moveTokens', () => {
it('should move tokens to a new address in the HD wallet', async () => {
// Mock dependencies
sandbox.stub(uut, 'getKeyPair').resolves({
cashAddress: 'bitcoincash:qqsj63493jk4p05zzdgqzc29k5unqtet9vv8l4x0yt',
wif: 'L4qKTMCwjH9jHnYtNh9Vsrxj7Hg6zmoN8E2v7N47UKvNVEjw7FU8',
hdIndex: 6
})
uut.bchWallet = {
sendTokens: async () => 'fake-txid'
}
const inObj = {
tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
qty: 1
}
const result = await uut.moveTokens(inObj)
// console.log('result: ', result)
assert.property(result, 'txid')
assert.property(result, 'vout')
assert.property(result, 'hdIndex')
})
})
})
const deleteFile = (filepath) => {
+2 -1
View File
@@ -126,7 +126,8 @@ const wallet = {
getKeyPair: async () => {
return { cashAddress: 'fakeAddr', wif: 'fakeWif', hdIndex: 1 }
},
bchWallet: new MockBchWallet()
bchWallet: new MockBchWallet(),
moveTokens: async () => {}
}
const p2wdb = {
+31 -31
View File
@@ -65,34 +65,34 @@ describe('#order-use-case', () => {
})
})
describe('#moveTokens', () => {
it('should move tokens to the holding address', async () => {
// Mock dependencies
// sandbox
// .stub(uut.adapters.wallet.bchWallet, 'sendTokens')
// .resolves('fakeTxid')
const orderEntity = {
lokadId: 'SWP',
messageType: 1,
messageClass: 1,
tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
buyOrSell: 'sell',
rateInSats: 1000,
minSatsToExchange: 0,
numTokens: 1
}
const result = await uut.moveTokens(orderEntity)
// console.log('result: ', result)
assert.property(result, 'txid')
assert.property(result, 'vout')
assert.equal(result.txid, 'fakeTxid')
assert.equal(result.vout, 1)
})
})
// describe('#moveTokens', () => {
// it('should move tokens to the holding address', async () => {
// // Mock dependencies
// // sandbox
// // .stub(uut.adapters.wallet.bchWallet, 'sendTokens')
// // .resolves('fakeTxid')
//
// const orderEntity = {
// lokadId: 'SWP',
// messageType: 1,
// messageClass: 1,
// tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
// buyOrSell: 'sell',
// rateInBaseUnit: 1000,
// minUnitsToExchange: 0,
// numTokens: 1
// }
//
// const result = await uut.moveTokens(orderEntity)
// // console.log('result: ', result)
//
// assert.property(result, 'txid')
// assert.property(result, 'vout')
//
// assert.equal(result.txid, 'fakeTxid')
// assert.equal(result.vout, 1)
// })
// })
describe('#createOrder', () => {
it('should create an order and return the hash', async () => {
@@ -102,8 +102,8 @@ describe('#order-use-case', () => {
messageClass: 1,
tokenId: 'token-id',
buyOrSell: 'sell',
rateInSats: 1000,
minSatsToExchange: 1250,
rateInBaseUnit: 1000,
minUnitsToExchange: 1250,
numTokens: 1
}
@@ -111,7 +111,7 @@ describe('#order-use-case', () => {
// sandbox.stub(uut.adapters.wallet, 'burnPsf').resolves('fakeTxid')
sandbox.stub(uut.orderEntity, 'validate').returns(entryObj)
sandbox.stub(uut, 'ensureFunds').resolves()
sandbox.stub(uut, 'moveTokens').resolves({ txid: 'fakeTxid', vout: 0, hdIndex: 1 })
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, 'generateSignature')